This is page 4 of 14. Use http://codebase.md/shashankss1205/codegraphcontext?page={x} to view the full context.
# Directory Structure
```
├── .github
│ └── workflows
│ ├── e2e-tests.yml
│ ├── post_discord_invite.yml
│ ├── test.yml
│ └── update-contributors.yml
├── .gitignore
├── CONTRIBUTING.md
├── contributors.md
├── docs
│ ├── docs
│ │ ├── architecture.md
│ │ ├── cli.md
│ │ ├── contributing_languages.md
│ │ ├── contributing.md
│ │ ├── cookbook.md
│ │ ├── core.md
│ │ ├── future_work.md
│ │ ├── images
│ │ │ ├── 1.png
│ │ │ ├── 11.png
│ │ │ ├── 12.png
│ │ │ ├── 13.png
│ │ │ ├── 14.png
│ │ │ ├── 16.png
│ │ │ ├── 19.png
│ │ │ ├── 2.png
│ │ │ ├── 20.png
│ │ │ ├── 21.png
│ │ │ ├── 22.png
│ │ │ ├── 23.png
│ │ │ ├── 24.png
│ │ │ ├── 26.png
│ │ │ ├── 28.png
│ │ │ ├── 29.png
│ │ │ ├── 3.png
│ │ │ ├── 30.png
│ │ │ ├── 31.png
│ │ │ ├── 32.png
│ │ │ ├── 33.png
│ │ │ ├── 34.png
│ │ │ ├── 35.png
│ │ │ ├── 36.png
│ │ │ ├── 38.png
│ │ │ ├── 39.png
│ │ │ ├── 4.png
│ │ │ ├── 40.png
│ │ │ ├── 41.png
│ │ │ ├── 42.png
│ │ │ ├── 43.png
│ │ │ ├── 44.png
│ │ │ ├── 5.png
│ │ │ ├── 6.png
│ │ │ ├── 7.png
│ │ │ ├── 8.png
│ │ │ ├── 9.png
│ │ │ ├── Indexing.gif
│ │ │ ├── tool_images
│ │ │ │ ├── 1.png
│ │ │ │ ├── 2.png
│ │ │ │ └── 3.png
│ │ │ └── Usecase.gif
│ │ ├── index.md
│ │ ├── installation.md
│ │ ├── license.md
│ │ ├── server.md
│ │ ├── tools.md
│ │ ├── troubleshooting.md
│ │ └── use_cases.md
│ ├── mkdocs.yml
│ └── site
│ ├── 404.html
│ ├── architecture
│ │ └── index.html
│ ├── assets
│ │ ├── images
│ │ │ └── favicon.png
│ │ ├── javascripts
│ │ │ ├── bundle.f55a23d4.min.js
│ │ │ ├── bundle.f55a23d4.min.js.map
│ │ │ ├── lunr
│ │ │ │ ├── min
│ │ │ │ │ ├── lunr.ar.min.js
│ │ │ │ │ ├── lunr.da.min.js
│ │ │ │ │ ├── lunr.de.min.js
│ │ │ │ │ ├── lunr.du.min.js
│ │ │ │ │ ├── lunr.el.min.js
│ │ │ │ │ ├── lunr.es.min.js
│ │ │ │ │ ├── lunr.fi.min.js
│ │ │ │ │ ├── lunr.fr.min.js
│ │ │ │ │ ├── lunr.he.min.js
│ │ │ │ │ ├── lunr.hi.min.js
│ │ │ │ │ ├── lunr.hu.min.js
│ │ │ │ │ ├── lunr.hy.min.js
│ │ │ │ │ ├── lunr.it.min.js
│ │ │ │ │ ├── lunr.ja.min.js
│ │ │ │ │ ├── lunr.jp.min.js
│ │ │ │ │ ├── lunr.kn.min.js
│ │ │ │ │ ├── lunr.ko.min.js
│ │ │ │ │ ├── lunr.multi.min.js
│ │ │ │ │ ├── lunr.nl.min.js
│ │ │ │ │ ├── lunr.no.min.js
│ │ │ │ │ ├── lunr.pt.min.js
│ │ │ │ │ ├── lunr.ro.min.js
│ │ │ │ │ ├── lunr.ru.min.js
│ │ │ │ │ ├── lunr.sa.min.js
│ │ │ │ │ ├── lunr.stemmer.support.min.js
│ │ │ │ │ ├── lunr.sv.min.js
│ │ │ │ │ ├── lunr.ta.min.js
│ │ │ │ │ ├── lunr.te.min.js
│ │ │ │ │ ├── lunr.th.min.js
│ │ │ │ │ ├── lunr.tr.min.js
│ │ │ │ │ ├── lunr.vi.min.js
│ │ │ │ │ └── lunr.zh.min.js
│ │ │ │ ├── tinyseg.js
│ │ │ │ └── wordcut.js
│ │ │ └── workers
│ │ │ ├── search.973d3a69.min.js
│ │ │ └── search.973d3a69.min.js.map
│ │ └── stylesheets
│ │ ├── main.2a3383ac.min.css
│ │ ├── main.2a3383ac.min.css.map
│ │ ├── palette.06af60db.min.css
│ │ └── palette.06af60db.min.css.map
│ ├── cli
│ │ └── index.html
│ ├── contributing
│ │ └── index.html
│ ├── contributing_languages
│ │ └── index.html
│ ├── cookbook
│ │ └── index.html
│ ├── core
│ │ └── index.html
│ ├── future_work
│ │ └── index.html
│ ├── images
│ │ ├── 1.png
│ │ ├── 11.png
│ │ ├── 12.png
│ │ ├── 13.png
│ │ ├── 14.png
│ │ ├── 16.png
│ │ ├── 19.png
│ │ ├── 2.png
│ │ ├── 20.png
│ │ ├── 21.png
│ │ ├── 22.png
│ │ ├── 23.png
│ │ ├── 24.png
│ │ ├── 26.png
│ │ ├── 28.png
│ │ ├── 29.png
│ │ ├── 3.png
│ │ ├── 30.png
│ │ ├── 31.png
│ │ ├── 32.png
│ │ ├── 33.png
│ │ ├── 34.png
│ │ ├── 35.png
│ │ ├── 36.png
│ │ ├── 38.png
│ │ ├── 39.png
│ │ ├── 4.png
│ │ ├── 40.png
│ │ ├── 41.png
│ │ ├── 42.png
│ │ ├── 43.png
│ │ ├── 44.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ ├── 7.png
│ │ ├── 8.png
│ │ ├── 9.png
│ │ ├── Indexing.gif
│ │ ├── tool_images
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ └── 3.png
│ │ └── Usecase.gif
│ ├── index.html
│ ├── installation
│ │ └── index.html
│ ├── license
│ │ └── index.html
│ ├── search
│ │ └── search_index.json
│ ├── server
│ │ └── index.html
│ ├── sitemap.xml
│ ├── sitemap.xml.gz
│ ├── tools
│ │ └── index.html
│ ├── troubleshooting
│ │ └── index.html
│ └── use_cases
│ └── index.html
├── images
│ ├── 1.png
│ ├── 11.png
│ ├── 12.png
│ ├── 13.png
│ ├── 14.png
│ ├── 16.png
│ ├── 19.png
│ ├── 2.png
│ ├── 20.png
│ ├── 21.png
│ ├── 22.png
│ ├── 23.png
│ ├── 24.png
│ ├── 26.png
│ ├── 28.png
│ ├── 29.png
│ ├── 3.png
│ ├── 30.png
│ ├── 31.png
│ ├── 32.png
│ ├── 33.png
│ ├── 34.png
│ ├── 35.png
│ ├── 36.png
│ ├── 38.png
│ ├── 39.png
│ ├── 4.png
│ ├── 40.png
│ ├── 41.png
│ ├── 42.png
│ ├── 43.png
│ ├── 44.png
│ ├── 5.png
│ ├── 6.png
│ ├── 7.png
│ ├── 8.png
│ ├── 9.png
│ ├── Indexing.gif
│ ├── tool_images
│ │ ├── 1.png
│ │ ├── 2.png
│ │ └── 3.png
│ └── Usecase.gif
├── LICENSE
├── MANIFEST.in
├── organizer
│ ├── CONTRIBUTING_LANGUAGES.md
│ ├── cookbook.md
│ ├── docs.md
│ ├── language_specific_nodes.md
│ ├── Tools_Exploration.md
│ └── troubleshoot.md
├── package-lock.json
├── pyproject.toml
├── README.md
├── scripts
│ ├── generate_lang_contributors.py
│ └── post_install_fix.sh
├── SECURITY.md
├── src
│ └── codegraphcontext
│ ├── __init__.py
│ ├── __main__.py
│ ├── cli
│ │ ├── __init__.py
│ │ ├── cli_helpers.py
│ │ ├── main.py
│ │ ├── setup_macos.py
│ │ └── setup_wizard.py
│ ├── core
│ │ ├── __init__.py
│ │ ├── database.py
│ │ ├── jobs.py
│ │ └── watcher.py
│ ├── prompts.py
│ ├── server.py
│ ├── tools
│ │ ├── __init__.py
│ │ ├── advanced_language_query_tool.py
│ │ ├── code_finder.py
│ │ ├── graph_builder.py
│ │ ├── languages
│ │ │ ├── c.py
│ │ │ ├── cpp.py
│ │ │ ├── go.py
│ │ │ ├── java.py
│ │ │ ├── javascript.py
│ │ │ ├── python.py
│ │ │ ├── ruby.py
│ │ │ ├── rust.py
│ │ │ └── typescript.py
│ │ ├── package_resolver.py
│ │ ├── query_tool_languages
│ │ │ ├── c_toolkit.py
│ │ │ ├── cpp_toolkit.py
│ │ │ ├── go_toolkit.py
│ │ │ ├── java_toolkit.py
│ │ │ ├── javascript_toolkit.py
│ │ │ ├── python_toolkit.py
│ │ │ ├── ruby_toolkit.py
│ │ │ ├── rust_toolkit.py
│ │ │ └── typescript_toolkit.py
│ │ └── system.py
│ └── utils
│ └── debug_log.py
├── tests
│ ├── __init__.py
│ ├── conftest.py
│ ├── sample_project
│ │ ├── advanced_calls.py
│ │ ├── advanced_classes.py
│ │ ├── advanced_classes2.py
│ │ ├── advanced_functions.py
│ │ ├── advanced_imports.py
│ │ ├── async_features.py
│ │ ├── callbacks_decorators.py
│ │ ├── circular1.py
│ │ ├── circular2.py
│ │ ├── class_instantiation.py
│ │ ├── cli_and_dunder.py
│ │ ├── complex_classes.py
│ │ ├── comprehensions_generators.py
│ │ ├── context_managers.py
│ │ ├── control_flow.py
│ │ ├── datatypes.py
│ │ ├── dynamic_dispatch.py
│ │ ├── dynamic_imports.py
│ │ ├── edge_cases
│ │ │ ├── comments_only.py
│ │ │ ├── docstring_only.py
│ │ │ ├── empty.py
│ │ │ ├── hardcoded_secrets.py
│ │ │ ├── long_functions.py
│ │ │ └── syntax_error.py
│ │ ├── function_chains.py
│ │ ├── generators.py
│ │ ├── import_reexports.py
│ │ ├── mapping_calls.py
│ │ ├── module_a.py
│ │ ├── module_b.py
│ │ ├── module_c
│ │ │ ├── __init__.py
│ │ │ ├── submodule1.py
│ │ │ └── submodule2.py
│ │ ├── namespace_pkg
│ │ │ └── ns_module.py
│ │ ├── pattern_matching.py
│ │ └── typing_examples.py
│ ├── sample_project_c
│ │ ├── cgc_sample
│ │ ├── include
│ │ │ ├── config.h
│ │ │ ├── math
│ │ │ │ └── vec.h
│ │ │ ├── module.h
│ │ │ ├── platform.h
│ │ │ └── util.h
│ │ ├── Makefile
│ │ ├── README.md
│ │ └── src
│ │ ├── main.c
│ │ ├── math
│ │ │ └── vec.c
│ │ ├── module.c
│ │ └── util.c
│ ├── sample_project_cpp
│ │ ├── class_features.cpp
│ │ ├── classes.cpp
│ │ ├── control_flow.cpp
│ │ ├── edge_cases.cpp
│ │ ├── enum_struct_union.cpp
│ │ ├── exceptions.cpp
│ │ ├── file_io.cpp
│ │ ├── function_chain.cpp
│ │ ├── function_chain.h
│ │ ├── function_types.cpp
│ │ ├── main.cpp
│ │ ├── main.exe
│ │ ├── namespaces.cpp
│ │ ├── raii_example.cpp
│ │ ├── README.md
│ │ ├── sample_project.exe
│ │ ├── stl_usage.cpp
│ │ ├── templates.cpp
│ │ └── types_variable_assignments.cpp
│ ├── sample_project_go
│ │ ├── advanced_types.go
│ │ ├── basic_functions.go
│ │ ├── embedded_composition.go
│ │ ├── error_handling.go
│ │ ├── generics.go
│ │ ├── go.mod
│ │ ├── goroutines_channels.go
│ │ ├── interfaces.go
│ │ ├── packages_imports.go
│ │ ├── README.md
│ │ ├── structs_methods.go
│ │ └── util
│ │ └── helpers.go
│ ├── sample_project_java
│ │ ├── out
│ │ │ └── com
│ │ │ └── example
│ │ │ └── app
│ │ │ ├── annotations
│ │ │ │ └── Logged.class
│ │ │ ├── Main.class
│ │ │ ├── misc
│ │ │ │ ├── Outer.class
│ │ │ │ └── Outer$Inner.class
│ │ │ ├── model
│ │ │ │ ├── Role.class
│ │ │ │ └── User.class
│ │ │ ├── service
│ │ │ │ ├── AbstractGreeter.class
│ │ │ │ ├── GreetingService.class
│ │ │ │ └── impl
│ │ │ │ └── GreetingServiceImpl.class
│ │ │ └── util
│ │ │ ├── CollectionUtils.class
│ │ │ └── IOHelper.class
│ │ ├── README.md
│ │ ├── sources.txt
│ │ └── src
│ │ └── com
│ │ └── example
│ │ └── app
│ │ ├── annotations
│ │ │ └── Logged.java
│ │ ├── Main.java
│ │ ├── misc
│ │ │ └── Outer.java
│ │ ├── model
│ │ │ ├── Role.java
│ │ │ └── User.java
│ │ ├── service
│ │ │ ├── AbstractGreeter.java
│ │ │ ├── GreetingService.java
│ │ │ └── impl
│ │ │ └── GreetingServiceImpl.java
│ │ └── util
│ │ ├── CollectionUtils.java
│ │ └── IOHelper.java
│ ├── sample_project_javascript
│ │ ├── arrays.js
│ │ ├── asyncAwait.js
│ │ ├── classes.js
│ │ ├── dom.js
│ │ ├── errorHandling.js
│ │ ├── events.js
│ │ ├── exporter.js
│ │ ├── fetchAPI.js
│ │ ├── fixtures
│ │ │ └── js
│ │ │ └── accessors.js
│ │ ├── functions.js
│ │ ├── importer.js
│ │ ├── objects.js
│ │ ├── promises.js
│ │ ├── README.md
│ │ └── variables.js
│ ├── sample_project_misc
│ │ ├── index.html
│ │ ├── README.md
│ │ ├── styles.css
│ │ ├── tables.css
│ │ └── tables.html
│ ├── sample_project_php
│ │ ├── classes_objects.php
│ │ ├── database.php
│ │ ├── edgecases.php
│ │ ├── error_handling.php
│ │ ├── file_handling.php
│ │ ├── functions.php
│ │ ├── generators_iterators.php
│ │ ├── globals_superglobals.php
│ │ ├── Inheritance.php
│ │ ├── interface_traits.php
│ │ └── README.md
│ ├── sample_project_ruby
│ │ ├── class_example.rb
│ │ ├── enumerables.rb
│ │ ├── error_handling.rb
│ │ ├── file_io.rb
│ │ ├── inheritance_example.rb
│ │ ├── main.rb
│ │ ├── metaprogramming.rb
│ │ ├── mixins_example.rb
│ │ ├── module_example.rb
│ │ └── tests
│ │ ├── test_mixins.py
│ │ └── test_sample.rb
│ ├── sample_project_rust
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src
│ │ ├── basic_functions.rs
│ │ ├── concurrency.rs
│ │ ├── error_handling.rs
│ │ ├── generics.rs
│ │ ├── iterators_closures.rs
│ │ ├── lib.rs
│ │ ├── lifetimes_references.rs
│ │ ├── modules.rs
│ │ ├── smart_pointers.rs
│ │ ├── structs_enums.rs
│ │ └── traits.rs
│ ├── sample_project_typescript
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── advanced-types.ts
│ │ │ ├── async-promises.ts
│ │ │ ├── classes-inheritance.ts
│ │ │ ├── decorators-metadata.ts
│ │ │ ├── error-validation.ts
│ │ │ ├── functions-generics.ts
│ │ │ ├── index.ts
│ │ │ ├── modules-namespaces.ts
│ │ │ ├── types-interfaces.ts
│ │ │ └── utilities-helpers.ts
│ │ └── tsconfig.json
│ ├── test_cpp_parser.py
│ ├── test_database_validation.py
│ ├── test_end_to_end.py
│ ├── test_graph_indexing_js.py
│ ├── test_graph_indexing.py
│ ├── test_tree_sitter
│ │ ├── __init__.py
│ │ ├── class_instantiation.py
│ │ ├── complex_classes.py
│ │ └── test_file.py
│ └── test_typescript_parser.py
└── website
├── .example.env
├── .gitignore
├── api
│ └── pypi.ts
├── bun.lockb
├── components.json
├── eslint.config.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ ├── favicon.ico
│ ├── placeholder.svg
│ └── robots.txt
├── README.md
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── assets
│ │ ├── function-calls.png
│ │ ├── graph-total.png
│ │ ├── hero-graph.jpg
│ │ └── hierarchy.png
│ ├── components
│ │ ├── ComparisonTable.tsx
│ │ ├── CookbookSection.tsx
│ │ ├── DemoSection.tsx
│ │ ├── ExamplesSection.tsx
│ │ ├── FeaturesSection.tsx
│ │ ├── Footer.tsx
│ │ ├── HeroSection.tsx
│ │ ├── InstallationSection.tsx
│ │ ├── MoveToTop.tsx
│ │ ├── ShowDownloads.tsx
│ │ ├── ShowStarGraph.tsx
│ │ ├── TestimonialSection.tsx
│ │ ├── ThemeProvider.tsx
│ │ ├── ThemeToggle.tsx
│ │ └── ui
│ │ ├── accordion.tsx
│ │ ├── alert-dialog.tsx
│ │ ├── alert.tsx
│ │ ├── aspect-ratio.tsx
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── breadcrumb.tsx
│ │ ├── button.tsx
│ │ ├── calendar.tsx
│ │ ├── card.tsx
│ │ ├── carousel.tsx
│ │ ├── chart.tsx
│ │ ├── checkbox.tsx
│ │ ├── collapsible.tsx
│ │ ├── command.tsx
│ │ ├── context-menu.tsx
│ │ ├── dialog.tsx
│ │ ├── drawer.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── form.tsx
│ │ ├── hover-card.tsx
│ │ ├── input-otp.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── menubar.tsx
│ │ ├── navigation-menu.tsx
│ │ ├── orbiting-circles.tsx
│ │ ├── pagination.tsx
│ │ ├── popover.tsx
│ │ ├── progress.tsx
│ │ ├── radio-group.tsx
│ │ ├── resizable.tsx
│ │ ├── scroll-area.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── sidebar.tsx
│ │ ├── skeleton.tsx
│ │ ├── slider.tsx
│ │ ├── sonner.tsx
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── textarea.tsx
│ │ ├── toast.tsx
│ │ ├── toaster.tsx
│ │ ├── toggle-group.tsx
│ │ ├── toggle.tsx
│ │ ├── tooltip.tsx
│ │ └── use-toast.ts
│ ├── hooks
│ │ ├── use-mobile.tsx
│ │ └── use-toast.ts
│ ├── index.css
│ ├── lib
│ │ └── utils.ts
│ ├── main.tsx
│ ├── pages
│ │ ├── Index.tsx
│ │ └── NotFound.tsx
│ └── vite-env.d.ts
├── tailwind.config.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
├── vercel.json
└── vite.config.ts
```
# Files
--------------------------------------------------------------------------------
/src/codegraphcontext/cli/cli_helpers.py:
--------------------------------------------------------------------------------
```python
# src/codegraphcontext/cli/cli_helpers.py
import asyncio
import json
import urllib.parse
from pathlib import Path
import time
from rich.console import Console
from rich.table import Table
from ..core.database import DatabaseManager
from ..core.jobs import JobManager
from ..tools.code_finder import CodeFinder
from ..tools.graph_builder import GraphBuilder
from ..tools.package_resolver import get_local_package_path
console = Console()
def _initialize_services():
"""Initializes and returns core service managers."""
console.print("[dim]Initializing services and database connection...[/dim]")
db_manager = DatabaseManager()
try:
db_manager.get_driver()
except ValueError as e:
console.print(f"[bold red]Database Connection Error:[/bold red] {e}")
console.print("Please ensure your Neo4j credentials are correct and the database is running.")
return None, None, None
# The GraphBuilder requires an event loop, even for synchronous-style execution
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
graph_builder = GraphBuilder(db_manager, JobManager(), loop)
code_finder = CodeFinder(db_manager)
console.print("[dim]Services initialized.[/dim]")
return db_manager, graph_builder, code_finder
def index_helper(path: str):
"""Synchronously indexes a repository."""
time_start = time.time()
services = _initialize_services()
if not all(services):
return
db_manager, graph_builder, code_finder = services
path_obj = Path(path).resolve()
if not path_obj.exists():
console.print(f"[red]Error: Path does not exist: {path_obj}[/red]")
db_manager.close_driver()
return
indexed_repos = code_finder.list_indexed_repositories()
if any(Path(repo["path"]).resolve() == path_obj for repo in indexed_repos):
console.print(f"[yellow]Repository '{path}' is already indexed. Skipping.[/yellow]")
db_manager.close_driver()
return
console.print(f"Starting indexing for: {path_obj}")
console.print("[yellow]This may take a few minutes for large repositories...[/yellow]")
async def do_index():
await graph_builder.build_graph_from_path_async(path_obj, is_dependency=False)
try:
asyncio.run(do_index())
time_end = time.time()
elapsed = time_end - time_start
console.print(f"[green]Successfully finished indexing: {path} in {elapsed:.2f} seconds[/green]")
except Exception as e:
console.print(f"[bold red]An error occurred during indexing:[/bold red] {e}")
finally:
db_manager.close_driver()
def add_package_helper(package_name: str, language: str):
"""Synchronously indexes a package."""
services = _initialize_services()
if not all(services):
return
db_manager, graph_builder, code_finder = services
package_path_str = get_local_package_path(package_name, language)
if not package_path_str:
console.print(f"[red]Error: Could not find package '{package_name}' for language '{language}'.[/red]")
db_manager.close_driver()
return
package_path = Path(package_path_str)
indexed_repos = code_finder.list_indexed_repositories()
if any(repo.get("name") == package_name for repo in indexed_repos if repo.get("is_dependency")):
console.print(f"[yellow]Package '{package_name}' is already indexed. Skipping.[/yellow]")
db_manager.close_driver()
return
console.print(f"Starting indexing for package '{package_name}' at: {package_path}")
console.print("[yellow]This may take a few minutes...[/yellow]")
async def do_index():
await graph_builder.build_graph_from_path_async(package_path, is_dependency=True)
try:
asyncio.run(do_index())
console.print(f"[green]Successfully finished indexing package: {package_name}[/green]")
except Exception as e:
console.print(f"[bold red]An error occurred during package indexing:[/bold red] {e}")
finally:
db_manager.close_driver()
def list_repos_helper():
"""Lists all indexed repositories."""
services = _initialize_services()
if not all(services):
return
db_manager, _, code_finder = services
try:
repos = code_finder.list_indexed_repositories()
if not repos:
console.print("[yellow]No repositories indexed yet.[/yellow]")
return
table = Table(show_header=True, header_style="bold magenta")
table.add_column("Name", style="dim")
table.add_column("Path")
table.add_column("Type")
for repo in repos:
repo_type = "Dependency" if repo.get("is_dependency") else "Project"
table.add_row(repo["name"], repo["path"], repo_type)
console.print(table)
except Exception as e:
console.print(f"[bold red]An error occurred:[/bold red] {e}")
finally:
db_manager.close_driver()
def delete_helper(repo_path: str):
"""Deletes a repository from the graph."""
services = _initialize_services()
if not all(services):
return
db_manager, graph_builder, _ = services
try:
graph_builder.delete_repository_from_graph(repo_path)
console.print(f"[green]Successfully deleted repository: {repo_path}[/green]")
except Exception as e:
console.print(f"[bold red]An error occurred:[/bold red] {e}")
finally:
db_manager.close_driver()
def cypher_helper(query: str):
"""Executes a read-only Cypher query."""
services = _initialize_services()
if not all(services):
return
db_manager, _, _ = services
# Replicating safety checks from MCPServer
forbidden_keywords = ['CREATE', 'MERGE', 'DELETE', 'SET', 'REMOVE', 'DROP', 'CALL apoc']
if any(keyword in query.upper() for keyword in forbidden_keywords):
console.print("[bold red]Error: This command only supports read-only queries.[/bold red]")
db_manager.close_driver()
return
try:
with db_manager.get_driver().session() as session:
result = session.run(query)
records = [record.data() for record in result]
console.print(json.dumps(records, indent=2))
except Exception as e:
console.print(f"[bold red]An error occurred while executing query:[/bold red] {e}")
finally:
db_manager.close_driver()
def visualize_helper(query: str):
"""Generates a URL to visualize a Cypher query."""
try:
encoded_query = urllib.parse.quote(query)
visualization_url = f"http://localhost:7474/browser/?cmd=edit&arg={encoded_query}"
console.print("[green]Graph visualization URL:[/green]")
console.print(visualization_url)
console.print("Open the URL in your browser to see the graph.")
except Exception as e:
console.print(f"[bold red]An error occurred while generating URL:[/bold red] {e}")
```
--------------------------------------------------------------------------------
/website/src/components/ShowStarGraph.tsx:
--------------------------------------------------------------------------------
```typescript
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { ExternalLink, Star, TrendingUp, RefreshCw } from "lucide-react";
import { useState, useCallback, useEffect } from "react";
export default function ShowStarGraph() {
const [imageLoaded, setImageLoaded] = useState(false);
const [imageError, setImageError] = useState(false);
const [refreshKey, setRefreshKey] = useState(0);
const [isRefreshing, setIsRefreshing] = useState(false);
const baseStarHistoryImageUrl =
"https://api.star-history.com/svg?repos=Shashankss1205/CodeGraphContext&type=Date";
// Add cache busting parameter to force updates
const starHistoryImageUrl = `${baseStarHistoryImageUrl}&t=${refreshKey}`;
const starHistoryDarkImageUrl = `${baseStarHistoryImageUrl}&theme=dark&t=${refreshKey}`;
const githubRepoUrl = "https://github.com/Shashankss1205/CodeGraphContext";
const starHistoryUrl =
"https://star-history.com/#Shashankss1205/CodeGraphContext&Date";
const handleImageLoad = () => {
setImageLoaded(true);
setImageError(false);
setIsRefreshing(false);
};
const handleImageError = () => {
setImageError(true);
setImageLoaded(false);
setIsRefreshing(false);
};
const handleRefresh = useCallback(() => {
setIsRefreshing(true);
setImageLoaded(false);
setImageError(false);
setRefreshKey((prev) => prev + 1);
}, []);
//Refreshed the graph everyday
useEffect(() => {
const today = new Date().toDateString();
const lastUpdated = localStorage.getItem("starHistoryLastUpdate");
if (lastUpdated !== today) {
handleRefresh();
localStorage.setItem("starHistoryLastUpdate", today);
}
}, [handleRefresh]);
return (
<>
<section className="px-4 bg-gradient-to-b from-secondary/10 to-background" data-aos="fade-up">
<div className="container mx-auto max-w-6xl">
<div className="text-center mb-12" data-aos="fade-down">
<div className="flex items-center justify-center gap-2 mb-4">
<Star className="h-6 w-6 text-yellow-500 fill-yellow-500" />
<h2 className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-primary via-primary to-accent bg-clip-text text-transparent py-2">
Star History
</h2>
<TrendingUp className="h-6 w-6 text-green-500" />
</div>
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
Track the growth and popularity of CodeGraphContext over time
</p>
</div>
<Card className="w-full shadow-2xl border-border/50 bg-card/50 backdrop-blur-sm" data-aos="zoom-in-up">
<CardHeader className="text-center">
<CardTitle className="flex items-center justify-center gap-2 text-2xl">
<Star className="h-5 w-5 text-yellow-500 fill-yellow-500" />
Repository Growth
<Badge variant="outline" className="ml-2">
Updated Daily
</Badge>
</CardTitle>
</CardHeader>
<CardContent className="p-6">
<div className="relative">
{(!imageLoaded && !imageError) ||
(isRefreshing && (
<div className="w-full h-64 bg-muted rounded-lg flex items-center justify-center">
<div className="text-center">
<div className="animate-spin h-8 w-8 border-4 border-primary border-t-transparent rounded-full mx-auto mb-2"></div>
<p className="text-muted-foreground">
{isRefreshing
? "Refreshing star history..."
: "Loading star history..."}
</p>
</div>
</div>
))}
{imageError && (
<div className="w-full h-64 bg-muted rounded-lg flex items-center justify-center border-2 border-dashed border-border">
<div className="text-center">
<Star className="h-12 w-12 text-muted-foreground mx-auto mb-2" />
<p className="text-muted-foreground mb-2">
Unable to load star history
</p>
<Button
variant="outline"
onClick={() => window.open(starHistoryUrl, "_blank")}
>
<ExternalLink className="h-4 w-4 mr-2" />
View on Star History
</Button>
</div>
</div>
)}
<div key={refreshKey}>
<img
src={starHistoryImageUrl}
alt="CodeGraphContext Star History"
className={`w-full h-auto rounded-lg transition-opacity duration-300 dark:hidden ${
imageLoaded && !isRefreshing
? "opacity-100"
: "opacity-0 absolute inset-0"
} ${imageError ? "hidden" : "block"}`}
onLoad={handleImageLoad}
onError={handleImageError}
/>
<img
src={starHistoryDarkImageUrl}
alt="CodeGraphContext Star History"
className={`w-full h-auto rounded-lg transition-opacity duration-300 hidden dark:block ${
imageLoaded && !isRefreshing
? "opacity-100"
: "opacity-0 absolute inset-0"
} ${imageError ? "hidden" : "block"}`}
onLoad={handleImageLoad}
onError={handleImageError}
/>
</div>
</div>
{imageLoaded && !isRefreshing && (
<div className="mt-6 flex flex-col sm:flex-row gap-4 justify-center items-center" data-aos="fade-up" data-aos-delay="200">
<Button
variant="outline"
onClick={() => window.open(githubRepoUrl, "_blank")}
className="flex items-center gap-2"
>
<Star className="h-4 w-4" />
Star on GitHub
<ExternalLink className="h-4 w-4" />
</Button>
<Button
variant="ghost"
onClick={() => window.open(starHistoryUrl, "_blank")}
className="flex items-center gap-2"
>
<TrendingUp className="h-4 w-4" />
View Full History
<ExternalLink className="h-4 w-4" />
</Button>
</div>
)}
</CardContent>
</Card>
</div>
</section>
<br />
<br />
</>
);
}
```
--------------------------------------------------------------------------------
/website/src/components/ui/context-menu.tsx:
--------------------------------------------------------------------------------
```typescript
import * as React from "react";
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
import { Check, ChevronRight, Circle } from "lucide-react";
import { cn } from "@/lib/utils";
const ContextMenu = ContextMenuPrimitive.Root;
const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
const ContextMenuGroup = ContextMenuPrimitive.Group;
const ContextMenuPortal = ContextMenuPrimitive.Portal;
const ContextMenuSub = ContextMenuPrimitive.Sub;
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
const ContextMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
<ContextMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[state=open]:bg-accent data-[state=open]:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
inset && "pl-8",
className,
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</ContextMenuPrimitive.SubTrigger>
));
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
const ContextMenuSubContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
));
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
const ContextMenuContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
</ContextMenuPrimitive.Portal>
));
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
const ContextMenuItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
inset && "pl-8",
className,
)}
{...props}
/>
));
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
const ContextMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<ContextMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</ContextMenuPrimitive.ItemIndicator>
</span>
{children}
</ContextMenuPrimitive.CheckboxItem>
));
ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
const ContextMenuRadioItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<ContextMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</ContextMenuPrimitive.ItemIndicator>
</span>
{children}
</ContextMenuPrimitive.RadioItem>
));
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
const ContextMenuLabel = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Label
ref={ref}
className={cn("px-2 py-1.5 text-sm font-semibold text-foreground", inset && "pl-8", className)}
{...props}
/>
));
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
const ContextMenuSeparator = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-border", className)} {...props} />
));
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
const ContextMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} {...props} />;
};
ContextMenuShortcut.displayName = "ContextMenuShortcut";
export {
ContextMenu,
ContextMenuTrigger,
ContextMenuContent,
ContextMenuItem,
ContextMenuCheckboxItem,
ContextMenuRadioItem,
ContextMenuLabel,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuGroup,
ContextMenuPortal,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuRadioGroup,
};
```
--------------------------------------------------------------------------------
/website/src/components/ui/dropdown-menu.tsx:
--------------------------------------------------------------------------------
```typescript
import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { Check, ChevronRight, Circle } from "lucide-react";
import { cn } from "@/lib/utils";
const DropdownMenu = DropdownMenuPrimitive.Root;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[state=open]:bg-accent focus:bg-accent",
inset && "pl-8",
className,
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
));
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
));
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
inset && "pl-8",
className,
)}
{...props}
/>
));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
));
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
));
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...props}
/>
));
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />;
};
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
};
```
--------------------------------------------------------------------------------
/organizer/language_specific_nodes.md:
--------------------------------------------------------------------------------
```markdown
# How to Add Language-Specific Features
This document outlines the standard pattern for extending the CodeGraphContext tool to support new, language-specific code constructs (like Go interfaces, Rust traits, C++ macros, etc.).
## Core Philosophy
The system is designed with a clear separation of concerns:
1. **Language-Specific Parsers:** Located in `src/codegraphcontext/tools/languages/`, these are responsible for understanding the syntax of a single language and extracting its constructs into a standardized Python dictionary.
2. **Generic Graph Builder:** The `GraphBuilder` in `src/codegraphcontext/tools/graph_builder.py` consumes these dictionaries and is responsible for creating nodes and relationships in the Neo4j database. It is language-agnostic.
Adding a new feature always involves these two steps: **(1) Specialize the Parser** and **(2) Generalize the Builder**.
---
## Step-by-Step Guide: Adding a New Node Type
We will walk through two examples:
1. Adding support for Go `interface` nodes.
2. Adding support for C/C++ `macro` nodes.
### Part 1: Modify the Language Parser
Your first goal is to teach the correct language parser to identify the new construct and return it under a unique key.
#### Example: Go Interfaces
**File to Edit:** `src/codegraphcontext/tools/languages/go.py`
**1. Add a Tree-sitter Query:**
Ensure a query exists in the `GO_QUERIES` dictionary to find the construct.
```python
GO_QUERIES = {
# ... existing queries
"interfaces": """
(type_declaration
(type_spec
name: (type_identifier) @name
type: (interface_type) @interface_body
)
) @interface_node
""",
}
```
**2. Create a Dedicated Parsing Method:**
Create a new method in the `GoTreeSitterParser` class to handle the results from your new query.
```python
# In GoTreeSitterParser class
def _find_interfaces(self, root_node):
interfaces = []
interface_query = self.queries['interfaces']
for node, capture_name in interface_query.captures(root_node):
if capture_name == 'name':
interface_node = self._find_type_declaration_for_name(node)
if interface_node:
name = self._get_node_text(node)
interfaces.append({
"name": name,
"line_number": interface_node.start_point[0] + 1,
"end_line": interface_node.end_point[0] + 1,
"source": self._get_node_text(interface_node),
})
return interfaces
```
**3. Update the Main `parse` Method:**
In the parser's main `parse` method, call your new function and add its results to the dictionary that gets returned. **The key you use here (e.g., `"interfaces"`) is what the Graph Builder will use.**
```python
# In GoTreeSitterParser.parse()
def parse(self, file_path: Path, is_dependency: bool = False) -> Dict:
# This comment explains the pattern for future developers.
# This method orchestrates the parsing of a single file.
# It calls specialized `_find_*` methods for each language construct.
# The returned dictionary should map a specific key (e.g., 'functions', 'interfaces')
# to a list of dictionaries, where each dictionary represents a single code construct.
# The GraphBuilder will then use these keys to create nodes with corresponding labels.
with open(file_path, "r", encoding="utf-8") as f:
source_code = f.read()
tree = self.parser.parse(bytes(source_code, "utf8"))
root_node = tree.root_node
functions = self._find_functions(root_node)
structs = self._find_structs(root_node)
interfaces = self._find_interfaces(root_node) # Call the new method
# ... find other constructs
return {
"file_path": str(file_path),
"functions": functions,
"classes": structs, # Structs are mapped to the generic :Class label
"interfaces": interfaces, # The new key-value pair
"variables": variables,
"imports": imports,
"function_calls": function_calls,
"is_dependency": is_dependency,
"lang": self.language_name,
}
```
---
### Part 2: Update the Generic Graph Builder
Now, teach the `GraphBuilder` how to handle the new key (e.g., `"interfaces"`) produced by the parser.
**File to Edit:** `src/codegraphcontext/tools/graph_builder.py`
**1. Add a Schema Constraint:**
In the `create_schema` method, add a uniqueness constraint for the new Neo4j node label you are introducing (e.g., `:Interface`, `:Macro`). This is crucial for data integrity.
```python
# In GraphBuilder.create_schema()
def create_schema(self):
"""Create constraints and indexes in Neo4j."""
# When adding a new node type with a unique key, add its constraint here.
with self.driver.session() as session:
try:
# ... existing constraints
session.run("CREATE CONSTRAINT class_unique IF NOT EXISTS FOR (c:Class) REQUIRE (c.name, c.file_path, c.line_number) IS UNIQUE")
# Add constraints for the new types
session.run("CREATE CONSTRAINT interface_unique IF NOT EXISTS FOR (i:Interface) REQUIRE (i.name, i.file_path, i.line_number) IS UNIQUE")
session.run("CREATE CONSTRAINT macro_unique IF NOT EXISTS FOR (m:Macro) REQUIRE (m.name, m.file_path, m.line_number) IS UNIQUE")
# ... other schema items
```
**2. Update the Node Creation Loop:**
In the `add_file_to_graph` method, there is a list called `item_mappings`. Add your new construct to this list. The builder will handle the rest automatically.
```python
# In GraphBuilder.add_file_to_graph()
# To add a new language-specific node type (e.g., 'Trait' for Rust):
# 1. Ensure your language-specific parser returns a list under a unique key (e.g., 'traits': [...] ).
# 2. Add a new constraint for the new label in the `create_schema` method.
# 3. Add a new entry to the `item_mappings` list below (e.g., (file_data.get('traits', []), 'Trait') ).
item_mappings = [
(file_data.get('functions', []), 'Function'),
(file_data.get('classes', []), 'Class'),
(file_data.get('variables', []), 'Variable'),
(file_data.get('interfaces', []), 'Interface'), # Added for Go
(file_data.get('macros', []), 'Macro') # Added for C/C++
]
for item_data, label in item_mappings:
for item in item_data:
# ... generic node creation logic
```
Using `file_data.get('macros', [])` ensures that the builder doesn't fail if a language parser (like Python's) doesn't produce a `macros` key.
---
## Advanced Topic: Scaling with Multi-Labeling
A valid concern is the proliferation of node labels. A more advanced pattern is to use multiple labels to capture both specific and general concepts.
For example:
- A Go interface node could have the labels: `[:Interface, :Contract, :Go]`
- A Rust trait node could have the labels: `[:Trait, :Contract, :Rust]`
This allows for powerful, cross-language queries (e.g., `MATCH (c:Contract)`) while retaining language-specific details.
This can be implemented in `add_file_to_graph` by dynamically constructing the label string based on the data provided by the parser, which already includes a `lang` key.
```
--------------------------------------------------------------------------------
/tests/sample_project_go/advanced_types.go:
--------------------------------------------------------------------------------
```go
// advanced_types.go - Demonstrates advanced Go types
package main
import (
"fmt"
"sort"
)
// CustomString is a custom type based on string
type CustomString string
// Length returns the length of the custom string
func (cs CustomString) Length() int {
return len(cs)
}
// ToUpper converts to uppercase
func (cs CustomString) ToUpper() CustomString {
return CustomString(fmt.Sprintf("%s", cs))
}
// CustomInt is a custom numeric type
type CustomInt int
// IsEven checks if the number is even
func (ci CustomInt) IsEven() bool {
return ci%2 == 0
}
// Double doubles the value
func (ci CustomInt) Double() CustomInt {
return ci * 2
}
// Status is an enum-like type
type Status int
const (
StatusPending Status = iota
StatusActive
StatusInactive
StatusDeleted
)
// String implements Stringer interface for Status
func (s Status) String() string {
return [...]string{"Pending", "Active", "Inactive", "Deleted"}[s]
}
// IsValid checks if status is valid
func (s Status) IsValid() bool {
return s >= StatusPending && s <= StatusDeleted
}
// Priority represents priority levels
type Priority string
const (
PriorityLow Priority = "low"
PriorityMedium Priority = "medium"
PriorityHigh Priority = "high"
)
// MapOperations demonstrates map operations
func MapOperations() map[string]int {
// Initialize map
scores := make(map[string]int)
// Add elements
scores["Alice"] = 95
scores["Bob"] = 87
scores["Charlie"] = 92
// Check existence
if val, ok := scores["Alice"]; ok {
fmt.Printf("Alice's score: %d\n", val)
}
// Delete element
delete(scores, "Bob")
return scores
}
// NestedMap demonstrates nested maps
func NestedMap() map[string]map[string]int {
users := make(map[string]map[string]int)
users["user1"] = make(map[string]int)
users["user1"]["age"] = 25
users["user1"]["score"] = 100
users["user2"] = map[string]int{
"age": 30,
"score": 95,
}
return users
}
// SliceOperations demonstrates slice operations
func SliceOperations() []int {
// Create slice
nums := []int{1, 2, 3, 4, 5}
// Append
nums = append(nums, 6, 7, 8)
// Slice operations
subset := nums[2:5]
fmt.Println(subset)
// Copy
copied := make([]int, len(nums))
copy(copied, nums)
return copied
}
// SliceOfSlices demonstrates 2D slices
func SliceOfSlices() [][]int {
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
return matrix
}
// ArrayOperations demonstrates array operations
func ArrayOperations() [5]int {
var arr [5]int
arr[0] = 10
arr[1] = 20
// Array initialization
arr2 := [5]int{1, 2, 3, 4, 5}
return arr2
}
// StructWithTags demonstrates struct tags
type User struct {
ID int `json:"id" db:"user_id"`
Username string `json:"username" db:"username" validate:"required"`
Email string `json:"email" db:"email" validate:"email"`
Age int `json:"age,omitempty" db:"age"`
IsActive bool `json:"is_active" db:"is_active"`
Role string `json:"role" db:"role" default:"user"`
}
// AnonymousStruct demonstrates anonymous structs
func AnonymousStruct() {
person := struct {
Name string
Age int
}{
Name: "John",
Age: 30,
}
fmt.Printf("%+v\n", person)
}
// FunctionType is a function type
type FunctionType func(int, int) int
// ApplyOperation applies a function operation
func ApplyOperation(a, b int, op FunctionType) int {
return op(a, b)
}
// MathOperations demonstrates function types
func MathOperations() {
add := func(a, b int) int { return a + b }
multiply := func(a, b int) int { return a * b }
fmt.Println(ApplyOperation(5, 3, add))
fmt.Println(ApplyOperation(5, 3, multiply))
}
// ChannelTypes demonstrates different channel types
func ChannelTypes() {
// Unbuffered channel
ch1 := make(chan int)
// Buffered channel
ch2 := make(chan string, 5)
// Send-only channel (in function signature)
go func(ch chan<- int) {
ch <- 42
}(ch1)
// Receive-only channel (in function signature)
go func(ch <-chan string) {
<-ch
}(ch2)
}
// SortableInts demonstrates custom sorting
type SortableInts []int
func (s SortableInts) Len() int { return len(s) }
func (s SortableInts) Less(i, j int) bool { return s[i] < s[j] }
func (s SortableInts) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// SortIntegers sorts integers using custom type
func SortIntegers(nums []int) []int {
sortable := SortableInts(nums)
sort.Sort(sortable)
return nums
}
// ByAge implements sort for Person by age
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// SortPeople sorts people by age
func SortPeople(people []Person) []Person {
sort.Sort(ByAge(people))
return people
}
// Result is a generic result type
type Result struct {
Success bool
Data interface{}
Error error
}
// NewSuccessResult creates a success result
func NewSuccessResult(data interface{}) Result {
return Result{
Success: true,
Data: data,
Error: nil,
}
}
// NewErrorResult creates an error result
func NewErrorResult(err error) Result {
return Result{
Success: false,
Data: nil,
Error: err,
}
}
// PointerTypes demonstrates pointer operations
func PointerTypes() {
x := 10
ptr := &x
fmt.Printf("Value: %d, Pointer: %p, Dereferenced: %d\n", x, ptr, *ptr)
*ptr = 20
fmt.Printf("Modified value: %d\n", x)
}
// PointerToStruct demonstrates pointer to struct
func PointerToStruct() *User {
user := &User{
ID: 1,
Username: "john_doe",
Email: "[email protected]",
Age: 30,
IsActive: true,
}
return user
}
// TypeAlias demonstrates type aliasing
type UserID = int
type Username = string
type EmailAddress = string
// CreateUserWithAliases uses type aliases
func CreateUserWithAliases(id UserID, name Username, email EmailAddress) User {
return User{
ID: id,
Username: name,
Email: email,
}
}
// InterfaceType demonstrates interface{} (any)
func InterfaceType(data interface{}) string {
switch v := data.(type) {
case int:
return fmt.Sprintf("Integer: %d", v)
case string:
return fmt.Sprintf("String: %s", v)
case []int:
return fmt.Sprintf("Slice of ints: %v", v)
case User:
return fmt.Sprintf("User: %s", v.Username)
default:
return fmt.Sprintf("Unknown type: %T", v)
}
}
// BitFlags demonstrates bit flags
type Permission uint8
const (
PermissionRead Permission = 1 << iota
PermissionWrite
PermissionExecute
PermissionDelete
)
// HasPermission checks if a permission is set
func HasPermission(perms Permission, perm Permission) bool {
return perms&perm != 0
}
// AddPermission adds a permission
func AddPermission(perms Permission, perm Permission) Permission {
return perms | perm
}
// RemovePermission removes a permission
func RemovePermission(perms Permission, perm Permission) Permission {
return perms &^ perm
}
func demonstrateAdvancedTypes() {
// Custom types
cs := CustomString("hello")
fmt.Println(cs.Length())
ci := CustomInt(10)
fmt.Println(ci.IsEven())
// Enums
status := StatusActive
fmt.Println(status.String())
// Maps
scores := MapOperations()
fmt.Println(scores)
// Slices
nums := SliceOperations()
fmt.Println(nums)
// Sorting
sorted := SortIntegers([]int{5, 2, 8, 1, 9})
fmt.Println(sorted)
}
```
--------------------------------------------------------------------------------
/contributors.md:
--------------------------------------------------------------------------------
```markdown
# Language Contributors
This file is auto-generated. Do not edit manually.
## C Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | PrakharGoyal28 | 1 | 295 | 0 | [c.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 2 | Harsh | 1 | 260 | 256 | [c.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 3 | [Shashank Shekhar Singh](https://github.com/Shashankss1205) | 1 | 256 | 260 | [c.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/c.py?author=Shashankss1205) |
| 4 | TanmayRanaware | 1 | 251 | 13 | [c.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 5 | Shashankss1205 | 1 | 2 | 5 | [c.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
## Cpp Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | Paras | 7 | 376 | 69 | [cpp.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 2 | Shashankss1205 | 6 | 206 | 470 | [cpp.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 3 | [TheSaturnian](https://github.com/GnautSpace) | 1 | 430 | 16 | [cpp.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/cpp.py?author=GnautSpace) |
## Go Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | Shashankss1205 | 2 | 19 | 14 | [go.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 2 | Harshdhall01 | 1 | 446 | 0 | [go.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
## Java Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | Aashish Jha | 1 | 304 | 0 | [java.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 2 | Sreeja Khan | 1 | 42 | 1 | [java.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 3 | Shashankss1205 | 1 | 8 | 10 | [java.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 4 | [Shashank Shekhar Singh](https://github.com/Shashankss1205) | 1 | 0 | 7 | [java.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/java.py?author=Shashankss1205) |
## Javascript Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | Shashankss1205 | 4 | 427 | 114 | [javascript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 2 | [Shashank Shekhar Singh](https://github.com/Shashankss1205) | 3 | 289 | 315 | [javascript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/javascript.py?author=Shashankss1205) |
| 3 | Akshit | 3 | 55 | 5 | [javascript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 4 | PrakharGoyal28 | 1 | 195 | 23 | [javascript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 5 | Priyal Duggal | 1 | 175 | 113 | [javascript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
## Python Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | Shashankss1205 | 4 | 488 | 34 | [python.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
## Ruby Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | TanmayRanaware | 1 | 426 | 0 | [ruby.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 2 | Peter Jusko | 1 | 71 | 3 | [ruby.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 3 | Shashankss1205 | 1 | 2 | 5 | [ruby.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
## Rust Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | Shashankss1205 | 3 | 148 | 5 | [rust.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 2 | Siddharth Baleja | 1 | 122 | 53 | [rust.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 3 | Syed Sohail | 1 | 28 | 8 | [rust.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
## Typescript Contributors
| Rank | Contributor | Commits | Lines Added | Lines Deleted | Link to Contributions |
|---|---|---|---|---|---|
| 1 | Shashankss1205 | 2 | 71 | 35 | [typescript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 2 | [Vasiliy Kiryanov](https://github.com/vasiliyk) | 1 | 412 | 0 | [typescript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/typescript.py?author=vasiliyk) |
| 3 | ANUKOOL | 1 | 54 | 0 | [typescript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/[email protected]) |
| 4 | [Shashank Shekhar Singh](https://github.com/Shashankss1205) | 1 | 1 | 2 | [typescript.py](https://github.com/Shashankss1205/CodeGraphContext/commits/main/src/codegraphcontext/tools/languages/typescript.py?author=Shashankss1205) |
```
--------------------------------------------------------------------------------
/tests/sample_project_rust/src/iterators_closures.rs:
--------------------------------------------------------------------------------
```rust
// iterators_closures.rs - Demonstrates Rust iterators and closures
use std::collections::HashMap;
// Closure examples
/// Function taking closure as parameter
pub fn apply<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(x)
}
/// Function returning closure
pub fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n
}
/// Closure capturing environment
pub fn closure_capture() {
let factor = 5;
let multiply = |x| x * factor;
println!("Result: {}", multiply(10));
}
/// Closure with move
pub fn closure_move() -> Box<dyn Fn(i32) -> i32> {
let factor = 5;
Box::new(move |x| x * factor)
}
/// FnOnce closure (consumes captured values)
pub fn call_once<F>(f: F)
where
F: FnOnce(),
{
f();
}
/// FnMut closure (mutates captured values)
pub fn call_mut<F>(mut f: F)
where
F: FnMut(),
{
f();
f();
}
// Iterator examples
/// Custom iterator
pub struct Counter {
count: u32,
max: u32,
}
impl Counter {
pub fn new(max: u32) -> Self {
Self { count: 0, max }
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.count < self.max {
self.count += 1;
Some(self.count)
} else {
None
}
}
}
/// Range iterator wrapper
pub struct Range {
start: i32,
end: i32,
current: i32,
}
impl Range {
pub fn new(start: i32, end: i32) -> Self {
Self {
start,
end,
current: start,
}
}
}
impl Iterator for Range {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
if self.current < self.end {
let result = self.current;
self.current += 1;
Some(result)
} else {
None
}
}
}
// Iterator adapter patterns
/// Map pattern
pub fn double_numbers(numbers: Vec<i32>) -> Vec<i32> {
numbers.iter().map(|&x| x * 2).collect()
}
/// Filter pattern
pub fn filter_evens(numbers: Vec<i32>) -> Vec<i32> {
numbers.into_iter().filter(|&x| x % 2 == 0).collect()
}
/// Filter and map combined
pub fn process_numbers(numbers: Vec<i32>) -> Vec<i32> {
numbers
.into_iter()
.filter(|&x| x > 0)
.map(|x| x * 2)
.collect()
}
/// Fold/reduce pattern
pub fn sum_numbers(numbers: &[i32]) -> i32 {
numbers.iter().fold(0, |acc, &x| acc + x)
}
/// Find pattern
pub fn find_first_even(numbers: &[i32]) -> Option<i32> {
numbers.iter().find(|&&x| x % 2 == 0).copied()
}
/// Any and all
pub fn has_negative(numbers: &[i32]) -> bool {
numbers.iter().any(|&x| x < 0)
}
pub fn all_positive(numbers: &[i32]) -> bool {
numbers.iter().all(|&x| x > 0)
}
/// Take and skip
pub fn take_first_n(numbers: Vec<i32>, n: usize) -> Vec<i32> {
numbers.into_iter().take(n).collect()
}
pub fn skip_first_n(numbers: Vec<i32>, n: usize) -> Vec<i32> {
numbers.into_iter().skip(n).collect()
}
/// Chain iterators
pub fn chain_vectors(v1: Vec<i32>, v2: Vec<i32>) -> Vec<i32> {
v1.into_iter().chain(v2.into_iter()).collect()
}
/// Zip iterators
pub fn zip_vectors(v1: Vec<i32>, v2: Vec<i32>) -> Vec<(i32, i32)> {
v1.into_iter().zip(v2.into_iter()).collect()
}
/// Enumerate
pub fn enumerate_items(items: Vec<String>) -> Vec<(usize, String)> {
items.into_iter().enumerate().collect()
}
/// Flat map
pub fn flat_map_example(nested: Vec<Vec<i32>>) -> Vec<i32> {
nested.into_iter().flat_map(|v| v.into_iter()).collect()
}
/// Partition
pub fn partition_numbers(numbers: Vec<i32>) -> (Vec<i32>, Vec<i32>) {
numbers.into_iter().partition(|&x| x % 2 == 0)
}
// Complex iterator chains
/// Multiple operations
pub fn complex_pipeline(numbers: Vec<i32>) -> i32 {
numbers
.into_iter()
.filter(|&x| x > 0)
.map(|x| x * 2)
.filter(|&x| x < 100)
.fold(0, |acc, x| acc + x)
}
/// Group by pattern (using HashMap)
pub fn group_by_parity(numbers: Vec<i32>) -> HashMap<bool, Vec<i32>> {
let mut map = HashMap::new();
for num in numbers {
let is_even = num % 2 == 0;
map.entry(is_even).or_insert_with(Vec::new).push(num);
}
map
}
/// Max and min
pub fn find_extremes(numbers: &[i32]) -> (Option<i32>, Option<i32>) {
let max = numbers.iter().max().copied();
let min = numbers.iter().min().copied();
(max, min)
}
/// Collect into different types
pub fn collect_examples(numbers: Vec<i32>) -> (Vec<i32>, String) {
let vec: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
let string: String = numbers
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(", ");
(vec, string)
}
// Custom iterator adapters
pub struct StepBy {
iter: std::vec::IntoIter<i32>,
step: usize,
}
impl StepBy {
pub fn new(vec: Vec<i32>, step: usize) -> Self {
Self {
iter: vec.into_iter(),
step,
}
}
}
impl Iterator for StepBy {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
let result = self.iter.next()?;
for _ in 0..self.step - 1 {
self.iter.next();
}
Some(result)
}
}
// Lazy evaluation demonstration
pub struct LazyMap<I, F> {
iter: I,
f: F,
}
impl<I, F, T, U> Iterator for LazyMap<I, F>
where
I: Iterator<Item = T>,
F: FnMut(T) -> U,
{
type Item = U;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|x| (self.f)(x))
}
}
// Infinite iterators
pub fn fibonacci_iterator() -> impl Iterator<Item = u64> {
let mut a = 0;
let mut b = 1;
std::iter::from_fn(move || {
let result = a;
let next = a + b;
a = b;
b = next;
Some(result)
})
}
/// Take from infinite iterator
pub fn first_n_fibonacci(n: usize) -> Vec<u64> {
fibonacci_iterator().take(n).collect()
}
// Peekable iterator
pub fn peek_example(numbers: Vec<i32>) -> Vec<i32> {
let mut iter = numbers.into_iter().peekable();
let mut result = Vec::new();
while let Some(&next) = iter.peek() {
if next % 2 == 0 {
result.push(iter.next().unwrap());
} else {
iter.next();
}
}
result
}
// Cycle iterator
pub fn cycle_example(items: Vec<i32>, count: usize) -> Vec<i32> {
items.into_iter().cycle().take(count).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_counter() {
let counter = Counter::new(5);
let result: Vec<u32> = counter.collect();
assert_eq!(result, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_double_numbers() {
let numbers = vec![1, 2, 3, 4, 5];
let doubled = double_numbers(numbers);
assert_eq!(doubled, vec![2, 4, 6, 8, 10]);
}
#[test]
fn test_filter_evens() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let evens = filter_evens(numbers);
assert_eq!(evens, vec![2, 4, 6]);
}
#[test]
fn test_sum_numbers() {
let numbers = vec![1, 2, 3, 4, 5];
assert_eq!(sum_numbers(&numbers), 15);
}
#[test]
fn test_make_adder() {
let add_five = make_adder(5);
assert_eq!(add_five(10), 15);
}
#[test]
fn test_fibonacci() {
let fibs = first_n_fibonacci(10);
assert_eq!(fibs, vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34]);
}
}
```
--------------------------------------------------------------------------------
/website/src/components/ui/menubar.tsx:
--------------------------------------------------------------------------------
```typescript
import * as React from "react";
import * as MenubarPrimitive from "@radix-ui/react-menubar";
import { Check, ChevronRight, Circle } from "lucide-react";
import { cn } from "@/lib/utils";
const MenubarMenu = MenubarPrimitive.Menu;
const MenubarGroup = MenubarPrimitive.Group;
const MenubarPortal = MenubarPrimitive.Portal;
const MenubarSub = MenubarPrimitive.Sub;
const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
const Menubar = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root>
>(({ className, ...props }, ref) => (
<MenubarPrimitive.Root
ref={ref}
className={cn("flex h-10 items-center space-x-1 rounded-md border bg-background p-1", className)}
{...props}
/>
));
Menubar.displayName = MenubarPrimitive.Root.displayName;
const MenubarTrigger = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<MenubarPrimitive.Trigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none data-[state=open]:bg-accent data-[state=open]:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
className,
)}
{...props}
/>
));
MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName;
const MenubarSubTrigger = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & {
inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
<MenubarPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[state=open]:bg-accent data-[state=open]:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
inset && "pl-8",
className,
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</MenubarPrimitive.SubTrigger>
));
MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName;
const MenubarSubContent = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<MenubarPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
));
MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName;
const MenubarContent = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content>
>(({ className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, ref) => (
<MenubarPrimitive.Portal>
<MenubarPrimitive.Content
ref={ref}
align={align}
alignOffset={alignOffset}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
</MenubarPrimitive.Portal>
));
MenubarContent.displayName = MenubarPrimitive.Content.displayName;
const MenubarItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<MenubarPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
inset && "pl-8",
className,
)}
{...props}
/>
));
MenubarItem.displayName = MenubarPrimitive.Item.displayName;
const MenubarCheckboxItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<MenubarPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</MenubarPrimitive.ItemIndicator>
</span>
{children}
</MenubarPrimitive.CheckboxItem>
));
MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName;
const MenubarRadioItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<MenubarPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</MenubarPrimitive.ItemIndicator>
</span>
{children}
</MenubarPrimitive.RadioItem>
));
MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName;
const MenubarLabel = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<MenubarPrimitive.Label
ref={ref}
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...props}
/>
));
MenubarLabel.displayName = MenubarPrimitive.Label.displayName;
const MenubarSeparator = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Separator>
>(({ className, ...props }, ref) => (
<MenubarPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
));
MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName;
const MenubarShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} {...props} />;
};
MenubarShortcut.displayname = "MenubarShortcut";
export {
Menubar,
MenubarMenu,
MenubarTrigger,
MenubarContent,
MenubarItem,
MenubarSeparator,
MenubarLabel,
MenubarCheckboxItem,
MenubarRadioGroup,
MenubarRadioItem,
MenubarPortal,
MenubarSubContent,
MenubarSubTrigger,
MenubarGroup,
MenubarSub,
MenubarShortcut,
};
```
--------------------------------------------------------------------------------
/tests/sample_project_rust/src/concurrency.rs:
--------------------------------------------------------------------------------
```rust
// concurrency.rs - Demonstrates Rust concurrency patterns
use std::sync::{Arc, Mutex, RwLock, mpsc};
use std::thread;
use std::time::Duration;
/// Simple thread spawning
pub fn spawn_simple_thread() {
let handle = thread::spawn(|| {
for i in 1..5 {
println!("Thread: {}", i);
thread::sleep(Duration::from_millis(10));
}
});
handle.join().unwrap();
}
/// Thread with move closure
pub fn thread_with_ownership() {
let data = vec![1, 2, 3, 4, 5];
let handle = thread::spawn(move || {
println!("Thread got data: {:?}", data);
data.iter().sum::<i32>()
});
let result = handle.join().unwrap();
println!("Sum: {}", result);
}
/// Multiple threads
pub fn spawn_multiple_threads(count: usize) -> Vec<i32> {
let mut handles = vec![];
for i in 0..count {
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(10));
i as i32 * 2
});
handles.push(handle);
}
handles.into_iter().map(|h| h.join().unwrap()).collect()
}
// Mutex for shared state
/// Shared counter with Mutex
pub fn shared_counter(thread_count: usize) -> i32 {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..thread_count {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..100 {
let mut num = counter.lock().unwrap();
*num += 1;
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let final_count = *counter.lock().unwrap();
final_count
}
/// Safe data structure with Mutex
#[derive(Debug)]
pub struct SafeCounter {
count: Arc<Mutex<i32>>,
}
impl SafeCounter {
pub fn new() -> Self {
Self {
count: Arc::new(Mutex::new(0)),
}
}
pub fn increment(&self) {
let mut count = self.count.lock().unwrap();
*count += 1;
}
pub fn get(&self) -> i32 {
*self.count.lock().unwrap()
}
pub fn clone_counter(&self) -> SafeCounter {
SafeCounter {
count: Arc::clone(&self.count),
}
}
}
// RwLock for read-heavy workloads
/// Shared data with RwLock
pub struct SharedData {
data: Arc<RwLock<Vec<i32>>>,
}
impl SharedData {
pub fn new() -> Self {
Self {
data: Arc::new(RwLock::new(Vec::new())),
}
}
pub fn add(&self, value: i32) {
let mut data = self.data.write().unwrap();
data.push(value);
}
pub fn get(&self, index: usize) -> Option<i32> {
let data = self.data.read().unwrap();
data.get(index).copied()
}
pub fn len(&self) -> usize {
let data = self.data.read().unwrap();
data.len()
}
pub fn clone_data(&self) -> SharedData {
SharedData {
data: Arc::clone(&self.data),
}
}
}
// Message passing with channels
/// Simple channel communication
pub fn simple_channel() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let messages = vec!["hello", "from", "thread"];
for msg in messages {
tx.send(msg).unwrap();
thread::sleep(Duration::from_millis(10));
}
});
for received in rx {
println!("Got: {}", received);
}
}
/// Multiple producers
pub fn multiple_producers() -> Vec<String> {
let (tx, rx) = mpsc::channel();
let mut results = Vec::new();
for i in 0..3 {
let tx_clone = tx.clone();
thread::spawn(move || {
let message = format!("Message from thread {}", i);
tx_clone.send(message).unwrap();
});
}
drop(tx); // Drop original sender
for received in rx {
results.push(received);
}
results
}
/// Worker pool pattern
pub struct ThreadPool {
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
type Job = Box<dyn FnOnce() + Send + 'static>;
struct Worker {
id: usize,
thread: Option<thread::JoinHandle<()>>,
}
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
let thread = thread::spawn(move || loop {
let job = receiver.lock().unwrap().recv();
match job {
Ok(job) => {
println!("Worker {} executing job", id);
job();
}
Err(_) => {
println!("Worker {} shutting down", id);
break;
}
}
});
Worker {
id,
thread: Some(thread),
}
}
}
impl ThreadPool {
pub fn new(size: usize) -> ThreadPool {
assert!(size > 0);
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender }
}
pub fn execute<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
impl Drop for ThreadPool {
fn drop(&mut self) {
for worker in &mut self.workers {
if let Some(thread) = worker.thread.take() {
thread.join().unwrap();
}
}
}
}
// Barrier synchronization
use std::sync::Barrier;
pub fn barrier_example() {
let barrier = Arc::new(Barrier::new(3));
let mut handles = vec![];
for i in 0..3 {
let barrier = Arc::clone(&barrier);
let handle = thread::spawn(move || {
println!("Thread {} before barrier", i);
barrier.wait();
println!("Thread {} after barrier", i);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
// Atomic operations
use std::sync::atomic::{AtomicUsize, Ordering};
pub struct AtomicCounter {
count: AtomicUsize,
}
impl AtomicCounter {
pub fn new() -> Self {
Self {
count: AtomicUsize::new(0),
}
}
pub fn increment(&self) {
self.count.fetch_add(1, Ordering::SeqCst);
}
pub fn get(&self) -> usize {
self.count.load(Ordering::SeqCst)
}
}
// Once initialization
use std::sync::Once;
static mut SINGLETON: Option<String> = None;
static INIT: Once = Once::new();
pub fn get_singleton() -> &'static String {
unsafe {
INIT.call_once(|| {
SINGLETON = Some(String::from("Singleton instance"));
});
SINGLETON.as_ref().unwrap()
}
}
// Scoped threads (Rust 1.63+)
pub fn scoped_threads() {
let mut data = vec![1, 2, 3, 4, 5];
thread::scope(|s| {
s.spawn(|| {
println!("Thread 1 can access data: {:?}", data);
});
s.spawn(|| {
println!("Thread 2 can also access data: {:?}", data);
});
});
data.push(6); // Can use data after scope
println!("Final data: {:?}", data);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shared_counter() {
let result = shared_counter(5);
assert_eq!(result, 500);
}
#[test]
fn test_safe_counter() {
let counter = SafeCounter::new();
counter.increment();
counter.increment();
assert_eq!(counter.get(), 2);
}
#[test]
fn test_shared_data() {
let data = SharedData::new();
data.add(1);
data.add(2);
data.add(3);
assert_eq!(data.len(), 3);
assert_eq!(data.get(1), Some(2));
}
#[test]
fn test_atomic_counter() {
let counter = AtomicCounter::new();
counter.increment();
counter.increment();
assert_eq!(counter.get(), 2);
}
}
```
--------------------------------------------------------------------------------
/tests/sample_project_rust/src/error_handling.rs:
--------------------------------------------------------------------------------
```rust
// error_handling.rs - Demonstrates Rust error handling patterns
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::{self, Read};
/// Custom error type
#[derive(Debug, Clone)]
pub struct CustomError {
pub code: u32,
pub message: String,
}
impl fmt::Display for CustomError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Error {}: {}", self.code, self.message)
}
}
impl Error for CustomError {}
impl CustomError {
pub fn new(code: u32, message: String) -> Self {
Self { code, message }
}
}
/// Validation error
#[derive(Debug)]
pub struct ValidationError {
pub field: String,
pub value: String,
pub reason: String,
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Validation failed for field '{}' with value '{}': {}",
self.field, self.value, self.reason
)
}
}
impl Error for ValidationError {}
/// Multiple error types enum
#[derive(Debug)]
pub enum AppError {
Io(io::Error),
Parse(std::num::ParseIntError),
Custom(CustomError),
Validation(ValidationError),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::Io(err) => write!(f, "IO error: {}", err),
AppError::Parse(err) => write!(f, "Parse error: {}", err),
AppError::Custom(err) => write!(f, "Custom error: {}", err),
AppError::Validation(err) => write!(f, "Validation error: {}", err),
}
}
}
impl Error for AppError {}
impl From<io::Error> for AppError {
fn from(err: io::Error) -> Self {
AppError::Io(err)
}
}
impl From<std::num::ParseIntError> for AppError {
fn from(err: std::num::ParseIntError) -> Self {
AppError::Parse(err)
}
}
impl From<CustomError> for AppError {
fn from(err: CustomError) -> Self {
AppError::Custom(err)
}
}
// Basic Result usage
/// Simple function returning Result
pub fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err("Division by zero".to_string())
} else {
Ok(a / b)
}
}
/// Function with multiple error conditions
pub fn validate_age(age: i32) -> Result<i32, String> {
if age < 0 {
return Err("Age cannot be negative".to_string());
}
if age > 150 {
return Err("Age is unrealistic".to_string());
}
Ok(age)
}
/// Using custom error type
pub fn validate_username(username: &str) -> Result<String, CustomError> {
if username.is_empty() {
return Err(CustomError::new(400, "Username cannot be empty".to_string()));
}
if username.len() < 3 {
return Err(CustomError::new(400, "Username too short".to_string()));
}
if username.len() > 20 {
return Err(CustomError::new(400, "Username too long".to_string()));
}
Ok(username.to_string())
}
/// Error propagation with ? operator
pub fn read_file_contents(filename: &str) -> Result<String, io::Error> {
let mut file = File::open(filename)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
/// Chaining operations with ?
pub fn parse_and_double(s: &str) -> Result<i32, std::num::ParseIntError> {
let num: i32 = s.parse()?;
Ok(num * 2)
}
/// Multiple error types with ?
pub fn read_and_parse(filename: &str) -> Result<i32, AppError> {
let mut file = File::open(filename)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let num: i32 = contents.trim().parse()?;
Ok(num)
}
/// Custom validation with ValidationError
pub fn validate_email(email: &str) -> Result<String, ValidationError> {
if !email.contains('@') {
return Err(ValidationError {
field: "email".to_string(),
value: email.to_string(),
reason: "Must contain @ symbol".to_string(),
});
}
if !email.contains('.') {
return Err(ValidationError {
field: "email".to_string(),
value: email.to_string(),
reason: "Must contain domain extension".to_string(),
});
}
Ok(email.to_string())
}
/// Using match for error handling
pub fn process_result(result: Result<i32, String>) -> i32 {
match result {
Ok(value) => value,
Err(e) => {
println!("Error occurred: {}", e);
0
}
}
}
/// Using unwrap_or
pub fn safe_divide(a: i32, b: i32) -> i32 {
divide(a, b).unwrap_or(0)
}
/// Using unwrap_or_else
pub fn safe_divide_with_default(a: i32, b: i32, default: i32) -> i32 {
divide(a, b).unwrap_or_else(|_| default)
}
/// Using map and and_then
pub fn parse_and_validate(s: &str) -> Result<i32, String> {
s.parse::<i32>()
.map_err(|e| format!("Parse error: {}", e))
.and_then(|num| {
if num >= 0 {
Ok(num)
} else {
Err("Number must be non-negative".to_string())
}
})
}
/// Error recovery pattern
pub fn try_parse_or_default(s: &str, default: i32) -> i32 {
s.parse().unwrap_or(default)
}
/// Multiple validation steps
pub fn validate_user(name: &str, age: i32, email: &str) -> Result<User, Vec<String>> {
let mut errors = Vec::new();
if name.is_empty() {
errors.push("Name cannot be empty".to_string());
}
if age < 18 {
errors.push("Must be 18 or older".to_string());
}
if !email.contains('@') {
errors.push("Invalid email".to_string());
}
if errors.is_empty() {
Ok(User {
name: name.to_string(),
age,
email: email.to_string(),
})
} else {
Err(errors)
}
}
#[derive(Debug)]
pub struct User {
name: String,
age: i32,
email: String,
}
/// Panic for unrecoverable errors
pub fn must_succeed(value: Option<i32>) -> i32 {
value.expect("This should never be None")
}
/// Option to Result conversion
pub fn find_user(id: u32) -> Option<String> {
if id > 0 {
Some(format!("User {}", id))
} else {
None
}
}
pub fn get_user_or_error(id: u32) -> Result<String, String> {
find_user(id).ok_or_else(|| "User not found".to_string())
}
/// Combining Results
pub fn validate_and_parse(s: &str) -> Result<i32, String> {
if s.is_empty() {
return Err("Input is empty".to_string());
}
s.parse::<i32>()
.map_err(|e| format!("Failed to parse: {}", e))
}
/// Early return pattern
pub fn process_input(input: &str) -> Result<i32, String> {
let trimmed = input.trim();
if trimmed.is_empty() {
return Err("Empty input".to_string());
}
let num: i32 = trimmed.parse().map_err(|_| "Invalid number".to_string())?;
if num < 0 {
return Err("Negative number".to_string());
}
Ok(num * 2)
}
/// Collecting Results
pub fn parse_numbers(strings: Vec<&str>) -> Result<Vec<i32>, String> {
strings
.iter()
.map(|s| s.parse::<i32>().map_err(|e| e.to_string()))
.collect()
}
/// Result with Box<dyn Error>
pub fn flexible_error_handling(input: &str) -> Result<i32, Box<dyn Error>> {
let num: i32 = input.parse()?;
if num < 0 {
return Err(Box::new(CustomError::new(400, "Negative number".to_string())));
}
Ok(num)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_divide_success() {
assert_eq!(divide(10, 2), Ok(5));
}
#[test]
fn test_divide_error() {
assert!(divide(10, 0).is_err());
}
#[test]
fn test_validate_age() {
assert!(validate_age(25).is_ok());
assert!(validate_age(-1).is_err());
assert!(validate_age(200).is_err());
}
#[test]
fn test_validate_username() {
assert!(validate_username("john_doe").is_ok());
assert!(validate_username("ab").is_err());
assert!(validate_username("").is_err());
}
}
```
--------------------------------------------------------------------------------
/tests/sample_project_rust/src/generics.rs:
--------------------------------------------------------------------------------
```rust
// generics.rs - Demonstrates Rust generics and type parameters
use std::cmp::PartialOrd;
use std::fmt::{Debug, Display};
use std::ops::Add;
/// Generic function with single type parameter
pub fn first<T>(list: &[T]) -> Option<&T> {
list.first()
}
/// Generic function with trait bound
pub fn largest<T: PartialOrd>(list: &[T]) -> Option<&T> {
if list.is_empty() {
return None;
}
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
Some(largest)
}
/// Generic function with multiple type parameters
pub fn pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
/// Generic function with multiple trait bounds
pub fn print_pair<T: Display, U: Display>(first: T, second: U) {
println!("First: {}, Second: {}", first, second);
}
/// Generic function with where clause
pub fn complex_function<T, U>(t: T, u: U) -> String
where
T: Display + Clone,
U: Display + Debug,
{
format!("T: {}, U: {:?}", t, u)
}
// Generic structs
/// Simple generic struct
#[derive(Debug, Clone)]
pub struct Point<T> {
pub x: T,
pub y: T,
}
impl<T> Point<T> {
pub fn new(x: T, y: T) -> Self {
Self { x, y }
}
pub fn x(&self) -> &T {
&self.x
}
pub fn y(&self) -> &T {
&self.y
}
}
impl<T: Add<Output = T> + Copy> Point<T> {
pub fn add(&self, other: &Point<T>) -> Point<T> {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
/// Generic struct with multiple type parameters
#[derive(Debug)]
pub struct Pair<T, U> {
pub first: T,
pub second: U,
}
impl<T, U> Pair<T, U> {
pub fn new(first: T, second: U) -> Self {
Self { first, second }
}
pub fn swap(self) -> Pair<U, T> {
Pair {
first: self.second,
second: self.first,
}
}
}
impl<T: Clone, U: Clone> Pair<T, U> {
pub fn clone_first(&self) -> T {
self.first.clone()
}
pub fn clone_second(&self) -> U {
self.second.clone()
}
}
// Generic collections
/// Generic stack
#[derive(Debug)]
pub struct Stack<T> {
items: Vec<T>,
}
impl<T> Stack<T> {
pub fn new() -> Self {
Self { items: Vec::new() }
}
pub fn push(&mut self, item: T) {
self.items.push(item);
}
pub fn pop(&mut self) -> Option<T> {
self.items.pop()
}
pub fn peek(&self) -> Option<&T> {
self.items.last()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn len(&self) -> usize {
self.items.len()
}
}
/// Generic queue
#[derive(Debug)]
pub struct Queue<T> {
items: Vec<T>,
}
impl<T> Queue<T> {
pub fn new() -> Self {
Self { items: Vec::new() }
}
pub fn enqueue(&mut self, item: T) {
self.items.push(item);
}
pub fn dequeue(&mut self) -> Option<T> {
if self.items.is_empty() {
None
} else {
Some(self.items.remove(0))
}
}
pub fn peek(&self) -> Option<&T> {
self.items.first()
}
pub fn len(&self) -> usize {
self.items.len()
}
}
/// Generic linked list node
#[derive(Debug)]
pub struct Node<T> {
pub value: T,
pub next: Option<Box<Node<T>>>,
}
impl<T> Node<T> {
pub fn new(value: T) -> Self {
Self { value, next: None }
}
}
/// Generic linked list
#[derive(Debug)]
pub struct LinkedList<T> {
head: Option<Box<Node<T>>>,
size: usize,
}
impl<T> LinkedList<T> {
pub fn new() -> Self {
Self {
head: None,
size: 0,
}
}
pub fn push(&mut self, value: T) {
let new_node = Box::new(Node {
value,
next: self.head.take(),
});
self.head = Some(new_node);
self.size += 1;
}
pub fn pop(&mut self) -> Option<T> {
self.head.take().map(|node| {
self.head = node.next;
self.size -= 1;
node.value
})
}
pub fn len(&self) -> usize {
self.size
}
pub fn is_empty(&self) -> bool {
self.size == 0
}
}
// Generic enums
/// Generic Option-like enum
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Maybe<T> {
Just(T),
Nothing,
}
impl<T> Maybe<T> {
pub fn is_just(&self) -> bool {
matches!(self, Maybe::Just(_))
}
pub fn unwrap(self) -> T {
match self {
Maybe::Just(value) => value,
Maybe::Nothing => panic!("Called unwrap on Nothing"),
}
}
pub fn unwrap_or(self, default: T) -> T {
match self {
Maybe::Just(value) => value,
Maybe::Nothing => default,
}
}
pub fn map<U, F>(self, f: F) -> Maybe<U>
where
F: FnOnce(T) -> U,
{
match self {
Maybe::Just(value) => Maybe::Just(f(value)),
Maybe::Nothing => Maybe::Nothing,
}
}
}
/// Generic Result-like enum
#[derive(Debug, Clone, PartialEq)]
pub enum Outcome<T, E> {
Success(T),
Failure(E),
}
impl<T, E> Outcome<T, E> {
pub fn is_success(&self) -> bool {
matches!(self, Outcome::Success(_))
}
pub fn map<U, F>(self, f: F) -> Outcome<U, E>
where
F: FnOnce(T) -> U,
{
match self {
Outcome::Success(value) => Outcome::Success(f(value)),
Outcome::Failure(err) => Outcome::Failure(err),
}
}
pub fn and_then<U, F>(self, f: F) -> Outcome<U, E>
where
F: FnOnce(T) -> Outcome<U, E>,
{
match self {
Outcome::Success(value) => f(value),
Outcome::Failure(err) => Outcome::Failure(err),
}
}
}
// Generic trait implementations
/// Generic wrapper type
#[derive(Debug, Clone)]
pub struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
pub fn new(value: T) -> Self {
Self { value }
}
pub fn into_inner(self) -> T {
self.value
}
pub fn get(&self) -> &T {
&self.value
}
pub fn get_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T: Display> Display for Wrapper<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Wrapper({})", self.value)
}
}
// Generic functions with closures
/// Map function for vectors
pub fn map<T, U, F>(vec: Vec<T>, f: F) -> Vec<U>
where
F: Fn(T) -> U,
{
vec.into_iter().map(f).collect()
}
/// Filter function for vectors
pub fn filter<T, F>(vec: Vec<T>, predicate: F) -> Vec<T>
where
F: Fn(&T) -> bool,
{
vec.into_iter().filter(|x| predicate(x)).collect()
}
/// Reduce/fold function
pub fn reduce<T, U, F>(vec: Vec<T>, initial: U, f: F) -> U
where
F: Fn(U, T) -> U,
{
vec.into_iter().fold(initial, f)
}
// Const generics (Rust 1.51+)
/// Array wrapper with const generic size
#[derive(Debug)]
pub struct FixedArray<T, const N: usize> {
data: [T; N],
}
impl<T: Default + Copy, const N: usize> FixedArray<T, N> {
pub fn new() -> Self {
Self {
data: [T::default(); N],
}
}
pub fn get(&self, index: usize) -> Option<&T> {
self.data.get(index)
}
pub fn set(&mut self, index: usize, value: T) -> Result<(), String> {
if index < N {
self.data[index] = value;
Ok(())
} else {
Err("Index out of bounds".to_string())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_largest() {
let numbers = vec![34, 50, 25, 100, 65];
assert_eq!(largest(&numbers), Some(&100));
}
#[test]
fn test_point_add() {
let p1 = Point::new(1, 2);
let p2 = Point::new(3, 4);
let p3 = p1.add(&p2);
assert_eq!(p3.x, 4);
assert_eq!(p3.y, 6);
}
#[test]
fn test_stack() {
let mut stack = Stack::new();
stack.push(1);
stack.push(2);
stack.push(3);
assert_eq!(stack.pop(), Some(3));
assert_eq!(stack.len(), 2);
}
#[test]
fn test_maybe() {
let just = Maybe::Just(42);
let nothing: Maybe<i32> = Maybe::Nothing;
assert!(just.is_just());
assert!(!nothing.is_just());
assert_eq!(just.unwrap(), 42);
}
}
```
--------------------------------------------------------------------------------
/tests/test_database_validation.py:
--------------------------------------------------------------------------------
```python
"""
Tests for database configuration validation and error handling.
"""
import pytest
import sys
from pathlib import Path
# Add src to path for imports (needed for direct test execution)
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
from codegraphcontext.core.database import DatabaseManager
class TestConfigValidation:
"""Tests for validate_config method"""
def test_validate_config_valid_neo4j_uri(self):
"""Test validation passes for valid neo4j:// URI"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7687", "neo4j", "password123"
)
assert is_valid is True
assert error is None
def test_validate_config_valid_bolt_uri(self):
"""Test validation passes for valid bolt:// URI"""
is_valid, error = DatabaseManager.validate_config(
"bolt://localhost:7687", "neo4j", "password123"
)
assert is_valid is True
assert error is None
def test_validate_config_valid_secure_uri(self):
"""Test validation passes for valid neo4j+s:// URI"""
is_valid, error = DatabaseManager.validate_config(
"neo4j+s://localhost:7687", "neo4j", "password123"
)
assert is_valid is True
assert error is None
def test_validate_config_invalid_uri_no_protocol(self):
"""Test validation fails for URI without protocol"""
is_valid, error = DatabaseManager.validate_config(
"localhost:7687", "neo4j", "password123"
)
assert is_valid is False
assert error is not None
assert "Invalid Neo4j URI format" in error
assert "neo4j://" in error or "bolt://" in error
def test_validate_config_invalid_uri_no_port(self):
"""Test validation fails for URI without port"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost", "neo4j", "password123"
)
assert is_valid is False
assert error is not None
assert "Invalid Neo4j URI format" in error
def test_validate_config_invalid_uri_wrong_protocol(self):
"""Test validation fails for invalid protocol"""
is_valid, error = DatabaseManager.validate_config(
"http://localhost:7687", "neo4j", "password123"
)
assert is_valid is False
assert error is not None
assert "Invalid Neo4j URI format" in error
def test_validate_config_empty_username(self):
"""Test validation fails for empty username"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7687", "", "password123"
)
assert is_valid is False
assert error is not None
assert "Username cannot be empty" in error
assert "neo4j" in error # Should mention default username
def test_validate_config_whitespace_username(self):
"""Test validation fails for whitespace-only username"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7687", " ", "password123"
)
assert is_valid is False
assert error is not None
assert "Username cannot be empty" in error
def test_validate_config_none_username(self):
"""Test validation fails for None username"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7687", None, "password123"
)
assert is_valid is False
assert error is not None
assert "Username cannot be empty" in error
def test_validate_config_empty_password(self):
"""Test validation fails for empty password"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7687", "neo4j", ""
)
assert is_valid is False
assert error is not None
assert "Password cannot be empty" in error
def test_validate_config_whitespace_password(self):
"""Test validation fails for whitespace-only password"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7687", "neo4j", " "
)
assert is_valid is False
assert error is not None
assert "Password cannot be empty" in error
def test_validate_config_none_password(self):
"""Test validation fails for None password"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7687", "neo4j", None
)
assert is_valid is False
assert error is not None
assert "Password cannot be empty" in error
def test_validate_config_valid_with_special_chars_in_password(self):
"""Test validation passes for password with special characters"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7687", "neo4j", "p@ssw0rd!#$%"
)
assert is_valid is True
assert error is None
def test_validate_config_valid_custom_port(self):
"""Test validation passes for custom port"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://localhost:7688", "neo4j", "password123"
)
assert is_valid is True
assert error is None
def test_validate_config_valid_remote_host(self):
"""Test validation passes for remote host"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://example.com:7687", "neo4j", "password123"
)
assert is_valid is True
assert error is None
def test_validate_config_valid_ip_address(self):
"""Test validation passes for IP address"""
is_valid, error = DatabaseManager.validate_config(
"neo4j://192.168.1.100:7687", "neo4j", "password123"
)
assert is_valid is True
assert error is None
class TestConnectionTest:
"""Tests for test_connection method"""
def test_connection_invalid_host_unreachable(self):
"""Test connection fails gracefully for unreachable host"""
is_connected, error = DatabaseManager.test_connection(
"neo4j://unreachable-host-12345.invalid:7687", "neo4j", "password123"
)
assert is_connected is False
assert error is not None
assert "Cannot reach Neo4j server" in error or "Connection failed" in error
def test_connection_invalid_port(self):
"""Test connection fails for invalid port"""
is_connected, error = DatabaseManager.test_connection(
"neo4j://localhost:9999", "neo4j", "password123"
)
assert is_connected is False
assert error is not None
# Should mention connectivity or unavailability
assert any(keyword in error for keyword in ["reach", "unavailable", "failed", "Connection"])
def test_connection_provides_troubleshooting_tips(self):
"""Test that error messages include troubleshooting tips"""
is_connected, error = DatabaseManager.test_connection(
"neo4j://localhost:9999", "neo4j", "password123"
)
assert is_connected is False
assert error is not None
# Should provide helpful troubleshooting
assert any(keyword in error.lower() for keyword in ["troubleshoot", "check", "docker", "try"])
class TestValidationIntegration:
"""Integration tests for validation in real-world scenarios"""
def test_validate_aura_style_uri(self):
"""Test validation works for Neo4j Aura-style URIs"""
is_valid, error = DatabaseManager.validate_config(
"neo4j+s://abc123.databases.neo4j.io:7687", "neo4j", "password123"
)
assert is_valid is True
assert error is None
def test_validate_bolt_ssc_uri(self):
"""Test validation works for bolt+ssc:// URIs"""
is_valid, error = DatabaseManager.validate_config(
"bolt+ssc://localhost:7687", "neo4j", "password123"
)
assert is_valid is True
assert error is None
def test_multiple_validation_errors_at_once(self):
"""Test validation catches the first error in priority order"""
# URI is invalid, username empty, password empty - should catch URI first
is_valid, error = DatabaseManager.validate_config(
"invalid-uri", "", ""
)
assert is_valid is False
assert "Invalid Neo4j URI format" in error
if __name__ == "__main__":
pytest.main([__file__, "-v"])
```
--------------------------------------------------------------------------------
/src/codegraphcontext/tools/languages/rust.py:
--------------------------------------------------------------------------------
```python
from pathlib import Path
from typing import Any, Dict, Optional, Tuple
import re
from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger, debug_logger
RUST_QUERIES = {
"functions": """
(function_item
name: (identifier) @name
parameters: (parameters) @params
) @function_node
""",
"classes": """
[
(struct_item name: (type_identifier) @name)
(enum_item name: (type_identifier) @name)
(trait_item name: (type_identifier) @name)
] @class
""",
"imports": """
(use_declaration) @import
""",
"calls": """
(call_expression
function: [
(identifier) @name
(field_expression field: (field_identifier) @name)
(scoped_identifier name: (identifier) @name)
]
)
""",
"traits": """
(trait_item name: (type_identifier) @name) @trait_node
""", # <-- Added trait query
}
class RustTreeSitterParser:
"""A Rust-specific parser using tree-sitter."""
def __init__(self, generic_parser_wrapper: Any):
self.generic_parser_wrapper = generic_parser_wrapper
self.language_name = "rust"
self.language = generic_parser_wrapper.language
self.parser = generic_parser_wrapper.parser
self.queries = {
name: self.language.query(query_str)
for name, query_str in RUST_QUERIES.items()
}
def _get_node_text(self, node: Any) -> str:
return node.text.decode("utf-8")
def parse(self, file_path: Path, is_dependency: bool = False) -> Dict[str, Any]:
"""Parses a Rust file and returns its structure."""
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
source_code = f.read()
tree = self.parser.parse(bytes(source_code, "utf8"))
root_node = tree.root_node
functions = self._find_functions(root_node)
classes = self._find_structs(root_node)
imports = self._find_imports(root_node)
function_calls = self._find_calls(root_node)
traits = self._find_traits(root_node) # <-- Added trait detection
return {
"file_path": str(file_path),
"functions": functions,
"classes": classes,
"traits": traits, # <-- Result for traits
"variables": [],
"imports": imports,
"function_calls": function_calls,
"is_dependency": is_dependency,
"lang": self.language_name,
}
def _parse_function_args(self, params_node: Any) -> list[Dict[str, Any]]:
"""Helper to parse function arguments from a (parameters) node."""
args = []
for param in params_node.named_children:
arg_info: Dict[str, Any] = {"name": "", "type": None}
if param.type == "parameter":
pattern_node = param.child_by_field_name("pattern")
type_node = param.child_by_field_name("type")
if pattern_node:
arg_info["name"] = self._get_node_text(pattern_node)
if type_node:
arg_info["type"] = self._get_node_text(type_node)
args.append(arg_info)
elif param.type == "self_parameter":
arg_info["name"] = self._get_node_text(param)
arg_info["type"] = "self"
args.append(arg_info)
return args
def _find_functions(self, root_node: Any) -> list[Dict[str, Any]]:
functions = []
query = self.queries["functions"]
for match in query.matches(root_node):
captures = {name: node for node, name in match.captures}
func_node = captures.get("function_node")
name_node = captures.get("name")
params_node = captures.get("params")
if func_node and name_node:
name = self._get_node_text(name_node)
args = self._parse_function_args(params_node) if params_node else []
functions.append(
{
"name": name,
"line_number": name_node.start_point[0] + 1,
"end_line": func_node.end_point[0] + 1,
"source_code": self._get_node_text(func_node),
"args": args,
}
)
return functions
def _find_structs(self, root_node: Any) -> list[Dict[str, Any]]:
structs = []
query = self.queries["classes"]
for match in query.matches(root_node):
captures = {name: node for node, name in match.captures}
class_node = captures.get("class")
name_node = captures.get("name")
if class_node and name_node:
name = self._get_node_text(name_node)
structs.append(
{
"name": name,
"line_number": name_node.start_point[0] + 1,
"end_line": class_node.end_point[0] + 1,
"source_code": self._get_node_text(class_node),
"bases": [],
}
)
return structs
def _find_traits(self, root_node: Any) -> list[Dict[str, Any]]:
traits = []
query = self.queries["traits"]
for match in query.matches(root_node):
captures = {name: node for node, name in match.captures}
trait_node = captures.get("trait_node")
name_node = captures.get("name")
if trait_node and name_node:
name = self._get_node_text(name_node)
traits.append(
{
"name": name,
"line_number": name_node.start_point[0] + 1,
"end_line": trait_node.end_point[0] + 1,
"source_code": self._get_node_text(trait_node),
}
)
return traits
def _find_imports(self, root_node: Any) -> list[Dict[str, Any]]:
imports = []
query = self.queries["imports"]
for node, _ in query.captures(root_node):
full_import_name = self._get_node_text(node)
alias = None
alias_match = re.search(r"as\s+(\w+)\s*;?$", full_import_name)
if alias_match:
alias = alias_match.group(1)
name = alias
else:
cleaned_path = re.sub(r";$", "", full_import_name).strip()
last_part = cleaned_path.split("::")[-1]
if last_part.strip() == "*":
name = "*"
else:
name_match = re.findall(r"(\w+)", last_part)
name = name_match[-1] if name_match else last_part
imports.append(
{
"name": name,
"full_import_name": full_import_name,
"line_number": node.start_point[0] + 1,
"alias": alias,
}
)
return imports
def _find_calls(self, root_node: Any) -> list[Dict[str, Any]]:
"""Finds all function and method calls."""
calls = []
query = self.queries["calls"]
for node, capture_name in query.captures(root_node):
if capture_name == "name":
call_name = self._get_node_text(node)
calls.append(
{
"name": call_name,
"line_number": node.start_point[0] + 1,
}
)
return calls
def pre_scan_rust(files: list[Path], parser_wrapper) -> dict:
"""Scans Rust files to create a map of function/struct/enum/trait names to their file paths."""
imports_map = {}
query_str = """
(function_item name: (identifier) @name)
(struct_item name: (type_identifier) @name)
(enum_item name: (type_identifier) @name)
(trait_item name: (type_identifier) @name)
"""
query = parser_wrapper.language.query(query_str)
for file_path in files:
try:
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
tree = parser_wrapper.parser.parse(bytes(f.read(), "utf8"))
for capture, _ in query.captures(tree.root_node):
name = capture.text.decode('utf-8')
if name not in imports_map:
imports_map[name] = []
imports_map[name].append(str(file_path.resolve()))
except Exception as e:
warning_logger(f"Tree-sitter pre-scan failed for {file_path}: {e}")
return imports_map
```
--------------------------------------------------------------------------------
/docs/site/assets/javascripts/lunr/min/lunr.fi.min.js:
--------------------------------------------------------------------------------
```javascript
/*!
* Lunr languages, `Finnish` language
* https://github.com/MihaiValentin/lunr-languages
*
* Copyright 2014, Mihai Valentin
* http://www.mozilla.org/MPL/
*/
/*!
* based on
* Snowball JavaScript Library v0.3
* http://code.google.com/p/urim/
* http://snowball.tartarus.org/
*
* Copyright 2010, Oleg Mazko
* http://www.mozilla.org/MPL/
*/
!function(i,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(i.lunr)}(this,function(){return function(i){if(void 0===i)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===i.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");i.fi=function(){this.pipeline.reset(),this.pipeline.add(i.fi.trimmer,i.fi.stopWordFilter,i.fi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(i.fi.stemmer))},i.fi.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",i.fi.trimmer=i.trimmerSupport.generateTrimmer(i.fi.wordCharacters),i.Pipeline.registerFunction(i.fi.trimmer,"trimmer-fi"),i.fi.stemmer=function(){var e=i.stemmerSupport.Among,r=i.stemmerSupport.SnowballProgram,n=new function(){function i(){f=A.limit,d=f,n()||(f=A.cursor,n()||(d=A.cursor))}function n(){for(var i;;){if(i=A.cursor,A.in_grouping(W,97,246))break;if(A.cursor=i,i>=A.limit)return!0;A.cursor++}for(A.cursor=i;!A.out_grouping(W,97,246);){if(A.cursor>=A.limit)return!0;A.cursor++}return!1}function t(){return d<=A.cursor}function s(){var i,e;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(h,10)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.in_grouping_b(x,97,246))return;break;case 2:if(!t())return}A.slice_del()}else A.limit_backward=e}function o(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(v,9))switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:r=A.limit-A.cursor,A.eq_s_b(1,"k")||(A.cursor=A.limit-r,A.slice_del());break;case 2:A.slice_del(),A.ket=A.cursor,A.eq_s_b(3,"kse")&&(A.bra=A.cursor,A.slice_from("ksi"));break;case 3:A.slice_del();break;case 4:A.find_among_b(p,6)&&A.slice_del();break;case 5:A.find_among_b(g,6)&&A.slice_del();break;case 6:A.find_among_b(j,2)&&A.slice_del()}else A.limit_backward=e}function l(){return A.find_among_b(q,7)}function a(){return A.eq_s_b(1,"i")&&A.in_grouping_b(L,97,246)}function u(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(C,30)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.eq_s_b(1,"a"))return;break;case 2:case 9:if(!A.eq_s_b(1,"e"))return;break;case 3:if(!A.eq_s_b(1,"i"))return;break;case 4:if(!A.eq_s_b(1,"o"))return;break;case 5:if(!A.eq_s_b(1,"ä"))return;break;case 6:if(!A.eq_s_b(1,"ö"))return;break;case 7:if(r=A.limit-A.cursor,!l()&&(A.cursor=A.limit-r,!A.eq_s_b(2,"ie"))){A.cursor=A.limit-r;break}if(A.cursor=A.limit-r,A.cursor<=A.limit_backward){A.cursor=A.limit-r;break}A.cursor--,A.bra=A.cursor;break;case 8:if(!A.in_grouping_b(W,97,246)||!A.out_grouping_b(W,97,246))return}A.slice_del(),k=!0}else A.limit_backward=e}function c(){var i,e,r;if(A.cursor>=d)if(e=A.limit_backward,A.limit_backward=d,A.ket=A.cursor,i=A.find_among_b(P,14)){if(A.bra=A.cursor,A.limit_backward=e,1==i){if(r=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-r}A.slice_del()}else A.limit_backward=e}function m(){var i;A.cursor>=f&&(i=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.find_among_b(F,2)?(A.bra=A.cursor,A.limit_backward=i,A.slice_del()):A.limit_backward=i)}function w(){var i,e,r,n,t,s;if(A.cursor>=f){if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.eq_s_b(1,"t")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.in_grouping_b(W,97,246)&&(A.cursor=A.limit-r,A.slice_del(),A.limit_backward=e,n=A.limit-A.cursor,A.cursor>=d&&(A.cursor=d,t=A.limit_backward,A.limit_backward=A.cursor,A.cursor=A.limit-n,A.ket=A.cursor,i=A.find_among_b(S,2))))){if(A.bra=A.cursor,A.limit_backward=t,1==i){if(s=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-s}return void A.slice_del()}A.limit_backward=e}}function _(){var i,e,r,n;if(A.cursor>=f){for(i=A.limit_backward,A.limit_backward=f,e=A.limit-A.cursor,l()&&(A.cursor=A.limit-e,A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.in_grouping_b(y,97,228)&&(A.bra=A.cursor,A.out_grouping_b(W,97,246)&&A.slice_del()),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"j")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.eq_s_b(1,"o")?A.slice_del():(A.cursor=A.limit-r,A.eq_s_b(1,"u")&&A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"o")&&(A.bra=A.cursor,A.eq_s_b(1,"j")&&A.slice_del()),A.cursor=A.limit-e,A.limit_backward=i;;){if(n=A.limit-A.cursor,A.out_grouping_b(W,97,246)){A.cursor=A.limit-n;break}if(A.cursor=A.limit-n,A.cursor<=A.limit_backward)return;A.cursor--}A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,b=A.slice_to(),A.eq_v_b(b)&&A.slice_del())}}var k,b,d,f,h=[new e("pa",-1,1),new e("sti",-1,2),new e("kaan",-1,1),new e("han",-1,1),new e("kin",-1,1),new e("hän",-1,1),new e("kään",-1,1),new e("ko",-1,1),new e("pä",-1,1),new e("kö",-1,1)],p=[new e("lla",-1,-1),new e("na",-1,-1),new e("ssa",-1,-1),new e("ta",-1,-1),new e("lta",3,-1),new e("sta",3,-1)],g=[new e("llä",-1,-1),new e("nä",-1,-1),new e("ssä",-1,-1),new e("tä",-1,-1),new e("ltä",3,-1),new e("stä",3,-1)],j=[new e("lle",-1,-1),new e("ine",-1,-1)],v=[new e("nsa",-1,3),new e("mme",-1,3),new e("nne",-1,3),new e("ni",-1,2),new e("si",-1,1),new e("an",-1,4),new e("en",-1,6),new e("än",-1,5),new e("nsä",-1,3)],q=[new e("aa",-1,-1),new e("ee",-1,-1),new e("ii",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1),new e("ää",-1,-1),new e("öö",-1,-1)],C=[new e("a",-1,8),new e("lla",0,-1),new e("na",0,-1),new e("ssa",0,-1),new e("ta",0,-1),new e("lta",4,-1),new e("sta",4,-1),new e("tta",4,9),new e("lle",-1,-1),new e("ine",-1,-1),new e("ksi",-1,-1),new e("n",-1,7),new e("han",11,1),new e("den",11,-1,a),new e("seen",11,-1,l),new e("hen",11,2),new e("tten",11,-1,a),new e("hin",11,3),new e("siin",11,-1,a),new e("hon",11,4),new e("hän",11,5),new e("hön",11,6),new e("ä",-1,8),new e("llä",22,-1),new e("nä",22,-1),new e("ssä",22,-1),new e("tä",22,-1),new e("ltä",26,-1),new e("stä",26,-1),new e("ttä",26,9)],P=[new e("eja",-1,-1),new e("mma",-1,1),new e("imma",1,-1),new e("mpa",-1,1),new e("impa",3,-1),new e("mmi",-1,1),new e("immi",5,-1),new e("mpi",-1,1),new e("impi",7,-1),new e("ejä",-1,-1),new e("mmä",-1,1),new e("immä",10,-1),new e("mpä",-1,1),new e("impä",12,-1)],F=[new e("i",-1,-1),new e("j",-1,-1)],S=[new e("mma",-1,1),new e("imma",0,-1)],y=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],W=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],L=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],x=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],A=new r;this.setCurrent=function(i){A.setCurrent(i)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){var e=A.cursor;return i(),k=!1,A.limit_backward=e,A.cursor=A.limit,s(),A.cursor=A.limit,o(),A.cursor=A.limit,u(),A.cursor=A.limit,c(),A.cursor=A.limit,k?(m(),A.cursor=A.limit):(A.cursor=A.limit,w(),A.cursor=A.limit),_(),!0}};return function(i){return"function"==typeof i.update?i.update(function(i){return n.setCurrent(i),n.stem(),n.getCurrent()}):(n.setCurrent(i),n.stem(),n.getCurrent())}}(),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}});
```
--------------------------------------------------------------------------------
/docs/docs/contributing_languages.md:
--------------------------------------------------------------------------------
```markdown
# Contributing New Language Support to CodeGraphContext
This document outlines the steps and best practices for adding support for a new programming language to CodeGraphContext. By following this guide, contributors can efficiently integrate new languages and leverage the Neo4j graph for verification.
## 1. Understanding the Architecture
CodeGraphContext uses a modular architecture for multi-language support:
* **Generic `TreeSitterParser` (in `graph_builder.py`):** This acts as a wrapper, dispatching parsing tasks to language-specific implementations.
* **Language-Specific Parser Modules (in `src/codegraphcontext/tools/languages/`):** Each language (e.g., Python, JavaScript) has its own module (e.g., `python.py`, `javascript.py`) containing:
* Tree-sitter queries (`<LANG>_QUERIES`).
* A `<Lang>TreeSitterParser` class that encapsulates language-specific parsing logic.
* A `pre_scan_<lang>` function for initial symbol mapping.
* **`GraphBuilder` (in `graph_builder.py`):** Manages the overall graph building process, including file discovery, pre-scanning, and dispatching to the correct language parser.
## 2. Steps to Add a New Language (e.g., TypeScript - `.ts`)
### Step 2.1: Create the Language Module File
1. Create a new file: `src/codegraphcontext/tools/languages/typescript.py`.
2. Add the necessary imports: `from pathlib import Path`, `from typing import Any, Dict, Optional, Tuple`, `import logging`, `import ast` (if needed for AST manipulation).
3. Define `TS_QUERIES` (Tree-sitter queries for TypeScript).
4. Create a `TypescriptTreeSitterParser` class.
5. Create a `pre_scan_typescript` function.
### Step 2.2: Define Tree-sitter Queries (`TS_QUERIES`)
This is the most critical and often iterative step. You'll need to define queries for:
* **`functions`**: Function declarations, arrow functions, methods.
* **`classes`**: Class declarations, class expressions.
* **`imports`**: ES6 imports (`import ... from ...`), CommonJS `require()`.
* **`calls`**: Function calls, method calls.
* **`variables`**: Variable declarations (`let`, `const`, `var`).
* **`docstrings`**: (Optional) How documentation comments are identified.
* **`lambda_assignments`**: (Optional, Python-specific) If the language has similar constructs.
**Tips for Query Writing:**
* **Consult Tree-sitter Grammars:** Find the `node-types.json` or grammar definition for your language (e.g., `tree-sitter-typescript`).
* **Use `tree-sitter parse`:** Use the `tree-sitter parse` command-line tool to inspect the AST of sample code snippets. This is invaluable for identifying correct node types and field names.
* **Start Simple:** Begin with basic queries and gradually add complexity.
* **Test Iteratively:** After each query, test it with sample code.
### Step 2.3: Implement `<Lang>TreeSitterParser` Class
This class (e.g., `TypescriptTreeSitterParser`) will encapsulate the language-specific logic.
1. **`__init__(self, generic_parser_wrapper)`**:
* Store `generic_parser_wrapper`, `language_name`, `language`, `parser` from the generic wrapper.
* Load `TS_QUERIES` using `self.language.query(query_str)`.
2. **Helper Methods**:
* `_get_node_text(self, node)`: Extracts text from a tree-sitter node.
* `_get_parent_context(self, node, types=...)`: (Language-specific node types for context).
* `_calculate_complexity(self, node)`: (Language-specific complexity nodes).
* `_get_docstring(self, body_node)`: (Language-specific docstring extraction).
3. **`parse(self, file_path: Path, is_dependency: bool = False) -> Dict`**:
* Reads the file, parses it with `self.parser`.
* Calls its own `_find_*` methods (`_find_functions`, `_find_classes`, etc.).
* Returns a standardized dictionary format (as seen in `python.py` and `javascript.py`).
4. **`_find_*` Methods**:
Implement these for each query type, extracting data from the AST and populating the standardized dictionary.
### Step 2.4: Implement `pre_scan_<lang>` Function
This function (e.g., `pre_scan_typescript`) will quickly scan files to build an initial `imports_map`.
1. It takes `files: list[Path]` and `parser_wrapper` (an instance of `TreeSitterParser`).
2. Uses a simplified query (e.g., for `class_declaration` and `function_declaration`) to quickly find definitions.
3. Returns a dictionary mapping symbol names to file paths.
### Step 2.5: Integrate into `graph_builder.py`
1. **`GraphBuilder.__init__`**:
* Add `'.ts': TreeSitterParser('typescript')` to `self.parsers`.
2. **`TreeSitterParser.__init__`**:
* Add an `elif self.language_name == 'typescript':` block to initialize `self.language_specific_parser` with `TypescriptTreeSitterParser(self)`.
3. **`GraphBuilder._pre_scan_for_imports`**:
* Add an `elif '.ts' in files_by_lang:` block to import `pre_scan_typescript` and call it.
## 3. Verification and Debugging using Neo4j
After implementing support for a new language, it's crucial to verify that the graph is being built correctly.
### Step 3.1: Prepare a Sample Project
Create a small sample project for your new language (e.g., `tests/sample_project_typescript/`) with:
* Function declarations.
* Class declarations (including inheritance).
* Various import types (if applicable).
* Function calls.
* Variable declarations.
### Step 3.2: Index the Sample Project
1. **Delete existing data (if any):**
```bash
# Replace with your sample project path
<tool_code>print(default_api.delete_repository(repo_path='/path/to/your/sample_project'))</tool_code>
2. **Index the project:**
```bash
# Replace with your sample project path
<tool_code>print(default_api.add_code_to_graph(path='/path/to/your/sample_project'))</tool_code>
3. **Monitor job status:**
```bash
# Use the job_id returned by add_code_to_graph
<tool_code>print(default_api.check_job_status(job_id='<your_job_id>'))</tool_code>
### Step 3.3: Query the Neo4j Graph
Use Cypher queries to inspect the generated graph.
* **Check for Files and Language Tags:**
```cypher
MATCH (f:File)
WHERE f.path STARTS WITH '/path/to/your/sample_project'
RETURN f.name, f.path, f.lang
```
*Expected:* All files from your sample project should be listed with the correct `lang` tag.
* **Check for Functions:**
```cypher
MATCH (f:File)-[:CONTAINS]->(fn:Function)
WHERE f.path STARTS WITH '/path/to/your/sample_project'
AND fn.lang = '<your_language_name>'
RETURN f.name AS FileName, fn.name AS FunctionName, fn.line_number AS Line
```
*Expected:* All functions from your sample project should be listed.
* **Check for Classes:**
```cypher
MATCH (f:File)-[:CONTAINS]->(c:Class)
WHERE f.path STARTS WITH '/path/to/your/sample_project'
AND c.lang = '<your_language_name>'
RETURN f.name AS FileName, c.name AS ClassName, c.line_number AS Line
```
*Expected:* All classes from your sample project should be listed.
* **Check for Imports (Module-level):**
```cypher
MATCH (f:File)-[:IMPORTS]->(m:Module)
WHERE f.path STARTS WITH '/path/to/your/sample_project'
AND f.lang = '<your_language_name>'
RETURN f.name AS FileName, m.name AS ImportedModule, m.full_import_name AS FullImportName
```
*Expected:* All module-level imports should be listed.
* **Check for Function Calls:**
```cypher
MATCH (caller:Function)-[:CALLS]->(callee:Function)
WHERE caller.file_path STARTS WITH '/path/to/your/sample_project'
AND caller.lang = '<your_language_name>'
RETURN caller.name AS Caller, callee.name AS Callee, caller.file_path AS CallerFile, callee.file_path AS CalleeFile
```
*Expected:* All function calls should be correctly linked.
* **Check for Class Inheritance:**
```cypher
MATCH (child:Class)-[:INHERITS]->(parent:Class)
WHERE child.file_path STARTS WITH '/path/to/your/sample_project'
AND child.lang = '<your_language_name>'
RETURN child.name AS ChildClass, parent.name AS ParentClass, child.file_path AS ChildFile, parent.file_path AS ParentFile
```
*Expected:* All inheritance relationships should be correctly linked.
### Step 3.4: Debugging Common Issues
* **`NameError: Invalid node type ...`**: Your tree-sitter query is using a node type that doesn't exist in the language's grammar. Use `tree-sitter parse` to inspect the AST.
* **Missing Relationships (e.g., `CALLS`, `IMPORTS`)**:
* **Check `_find_*` methods**: Ensure your `_find_*` methods are correctly extracting the necessary data.
* **Check `imports_map`**: Verify that the `pre_scan_<lang>` function is correctly populating the `imports_map`.
* **Check `local_imports` map**: Ensure the `local_imports` map (built in `_create_function_calls` and `_create_inheritance_links`) is correctly resolving symbols.
* **Incorrect `lang` tags**: Ensure `self.language_name` is correctly passed and stored.
By following these steps, contributors can effectively add and verify new language support.
```
--------------------------------------------------------------------------------
/organizer/CONTRIBUTING_LANGUAGES.md:
--------------------------------------------------------------------------------
```markdown
# Contributing New Language Support to CodeGraphContext
This document outlines the steps and best practices for adding support for a new programming language to CodeGraphContext. By following this guide, contributors can efficiently integrate new languages and leverage the Neo4j graph for verification.
## 1. Understanding the Architecture
CodeGraphContext uses a modular architecture for multi-language support:
* **Generic `TreeSitterParser` (in `graph_builder.py`):** This acts as a wrapper, dispatching parsing tasks to language-specific implementations.
* **Language-Specific Parser Modules (in `src/codegraphcontext/tools/languages/`):** Each language (e.g., Python, JavaScript) has its own module (e.g., `python.py`, `javascript.py`) containing:
* Tree-sitter queries (`<LANG>_QUERIES`).
* A `<Lang>TreeSitterParser` class that encapsulates language-specific parsing logic.
* A `pre_scan_<lang>` function for initial symbol mapping.
* **`GraphBuilder` (in `graph_builder.py`):** Manages the overall graph building process, including file discovery, pre-scanning, and dispatching to the correct language parser.
## 2. Steps to Add a New Language (e.g., TypeScript - `.ts`)
### Step 2.1: Create the Language Module File
1. Create a new file: `src/codegraphcontext/tools/languages/typescript.py`.
2. Add the necessary imports: `from pathlib import Path`, `from typing import Any, Dict, Optional, Tuple`, `import logging`, `import ast` (if needed for AST manipulation).
3. Define `TS_QUERIES` (Tree-sitter queries for TypeScript).
4. Create a `TypescriptTreeSitterParser` class.
5. Create a `pre_scan_typescript` function.
### Step 2.2: Define Tree-sitter Queries (`TS_QUERIES`)
This is the most critical and often iterative step. You'll need to define queries for:
* **`functions`**: Function declarations, arrow functions, methods.
* **`classes`**: Class declarations, class expressions.
* **`imports`**: ES6 imports (`import ... from ...`), CommonJS `require()`.
* **`calls`**: Function calls, method calls.
* **`variables`**: Variable declarations (`let`, `const`, `var`).
* **`docstrings`**: (Optional) How documentation comments are identified.
* **`lambda_assignments`**: (Optional, Python-specific) If the language has similar constructs.
**Tips for Query Writing:**
* **Consult Tree-sitter Grammars:** Find the `node-types.json` or grammar definition for your language (e.g., `tree-sitter-typescript`).
* **Use `tree-sitter parse`:** Use the `tree-sitter parse` command-line tool to inspect the AST of sample code snippets. This is invaluable for identifying correct node types and field names.
* **Start Simple:** Begin with basic queries and gradually add complexity.
* **Test Iteratively:** After each query, test it with sample code.
### Step 2.3: Implement `<Lang>TreeSitterParser` Class
This class (e.g., `TypescriptTreeSitterParser`) will encapsulate the language-specific logic.
1. **`__init__(self, generic_parser_wrapper)`**:
* Store `generic_parser_wrapper`, `language_name`, `language`, `parser` from the generic wrapper.
* Load `TS_QUERIES` using `self.language.query(query_str)`.
2. **Helper Methods**:
* `_get_node_text(self, node)`: Extracts text from a tree-sitter node.
* `_get_parent_context(self, node, types=...)`: (Language-specific node types for context).
* `_calculate_complexity(self, node)`: (Language-specific complexity nodes).
* `_get_docstring(self, body_node)`: (Language-specific docstring extraction).
3. **`parse(self, file_path: Path, is_dependency: bool = False) -> Dict`**:
* Reads the file, parses it with `self.parser`.
* Calls its own `_find_*` methods (`_find_functions`, `_find_classes`, etc.).
* Returns a standardized dictionary format (as seen in `python.py` and `javascript.py`).
4. **`_find_*` Methods**:
Implement these for each query type, extracting data from the AST and populating the standardized dictionary.
### Step 2.4: Implement `pre_scan_<lang>` Function
This function (e.g., `pre_scan_typescript`) will quickly scan files to build an initial `imports_map`.
1. It takes `files: list[Path]` and `parser_wrapper` (an instance of `TreeSitterParser`).
2. Uses a simplified query (e.g., for `class_declaration` and `function_declaration`) to quickly find definitions.
3. Returns a dictionary mapping symbol names to file paths.
### Step 2.5: Integrate into `graph_builder.py`
1. **`GraphBuilder.__init__`**:
* Add `'.ts': TreeSitterParser('typescript')` to `self.parsers`.
2. **`TreeSitterParser.__init__`**:
* Add an `elif self.language_name == 'typescript':` block to initialize `self.language_specific_parser` with `TypescriptTreeSitterParser(self)`.
3. **`GraphBuilder._pre_scan_for_imports`**:
* Add an `elif '.ts' in files_by_lang:` block to import `pre_scan_typescript` and call it.
## 3. Verification and Debugging using Neo4j
After implementing support for a new language, it's crucial to verify that the graph is being built correctly.
### Step 3.1: Prepare a Sample Project
Create a small sample project for your new language (e.g., `tests/sample_project_typescript/`) with:
* Function declarations.
* Class declarations (including inheritance).
* Various import types (if applicable).
* Function calls.
* Variable declarations.
### Step 3.2: Index the Sample Project
1. **Delete existing data (if any):**
```bash
# Replace with your sample project path
<tool_code>print(default_api.delete_repository(repo_path='/path/to/your/sample_project'))</tool_code>
2. **Index the project:**
```bash
# Replace with your sample project path
<tool_code>print(default_api.add_code_to_graph(path='/path/to/your/sample_project'))</tool_code>
3. **Monitor job status:**
```bash
# Use the job_id returned by add_code_to_graph
<tool_code>print(default_api.check_job_status(job_id='<your_job_id>'))</tool_code>
### Step 3.3: Query the Neo4j Graph
Use Cypher queries to inspect the generated graph.
* **Check for Files and Language Tags:**
```cypher
MATCH (f:File)
WHERE f.path STARTS WITH '/path/to/your/sample_project'
RETURN f.name, f.path, f.lang
```
*Expected:* All files from your sample project should be listed with the correct `lang` tag.
* **Check for Functions:**
```cypher
MATCH (f:File)-[:CONTAINS]->(fn:Function)
WHERE f.path STARTS WITH '/path/to/your/sample_project'
AND fn.lang = '<your_language_name>'
RETURN f.name AS FileName, fn.name AS FunctionName, fn.line_number AS Line
```
*Expected:* All functions from your sample project should be listed.
* **Check for Classes:**
```cypher
MATCH (f:File)-[:CONTAINS]->(c:Class)
WHERE f.path STARTS WITH '/path/to/your/sample_project'
AND c.lang = '<your_language_name>'
RETURN f.name AS FileName, c.name AS ClassName, c.line_number AS Line
```
*Expected:* All classes from your sample project should be listed.
* **Check for Imports (Module-level):**
```cypher
MATCH (f:File)-[:IMPORTS]->(m:Module)
WHERE f.path STARTS WITH '/path/to/your/sample_project'
AND f.lang = '<your_language_name>'
RETURN f.name AS FileName, m.name AS ImportedModule, m.full_import_name AS FullImportName
```
*Expected:* All module-level imports should be listed.
* **Check for Function Calls:**
```cypher
MATCH (caller:Function)-[:CALLS]->(callee:Function)
WHERE caller.file_path STARTS WITH '/path/to/your/sample_project'
AND caller.lang = '<your_language_name>'
RETURN caller.name AS Caller, callee.name AS Callee, caller.file_path AS CallerFile, callee.file_path AS CalleeFile
```
*Expected:* All function calls should be correctly linked.
* **Check for Class Inheritance:**
```cypher
MATCH (child:Class)-[:INHERITS]->(parent:Class)
WHERE child.file_path STARTS WITH '/path/to/your/sample_project'
AND child.lang = '<your_language_name>'
RETURN child.name AS ChildClass, parent.name AS ParentClass, child.file_path AS ChildFile, parent.file_path AS ParentFile
```
*Expected:* All inheritance relationships should be correctly linked.
### Step 3.4: Debugging Common Issues
* **`NameError: Invalid node type ...`**: Your tree-sitter query is using a node type that doesn't exist in the language's grammar. Use `tree-sitter parse` to inspect the AST.
* **Missing Relationships (e.g., `CALLS`, `IMPORTS`)**:
* **Check `_find_*` methods**: Ensure your `_find_*` methods are correctly extracting the necessary data.
* **Check `imports_map`**: Verify that the `pre_scan_<lang>` function is correctly populating the `imports_map`.
* **Check `local_imports` map**: Ensure the `local_imports` map (built in `_create_function_calls` and `_create_inheritance_links`) is correctly resolving symbols.
* **Incorrect `lang` tags**: Ensure `self.language_name` is correctly passed and stored.
By following these steps, contributors can effectively add and verify new language support.
```
--------------------------------------------------------------------------------
/docs/site/assets/javascripts/lunr/min/lunr.hu.min.js:
--------------------------------------------------------------------------------
```javascript
/*!
* Lunr languages, `Hungarian` language
* https://github.com/MihaiValentin/lunr-languages
*
* Copyright 2014, Mihai Valentin
* http://www.mozilla.org/MPL/
*/
/*!
* based on
* Snowball JavaScript Library v0.3
* http://code.google.com/p/urim/
* http://snowball.tartarus.org/
*
* Copyright 2010, Oleg Mazko
* http://www.mozilla.org/MPL/
*/
!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,n=L.cursor;if(d=L.limit,L.in_grouping(W,97,252))for(;;){if(e=L.cursor,L.out_grouping(W,97,252))return L.cursor=e,L.find_among(g,8)||(L.cursor=e,e<L.limit&&L.cursor++),void(d=L.cursor);if(L.cursor=e,e>=L.limit)return void(d=e);L.cursor++}if(L.cursor=n,L.out_grouping(W,97,252)){for(;!L.in_grouping(W,97,252);){if(L.cursor>=L.limit)return;L.cursor++}d=L.cursor}}function i(){return d<=L.cursor}function a(){var e;if(L.ket=L.cursor,(e=L.find_among_b(h,2))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e")}}function t(){var e=L.limit-L.cursor;return!!L.find_among_b(p,23)&&(L.cursor=L.limit-e,!0)}function s(){if(L.cursor>L.limit_backward){L.cursor--,L.ket=L.cursor;var e=L.cursor-1;L.limit_backward<=e&&e<=L.limit&&(L.cursor=e,L.bra=e,L.slice_del())}}function c(){var e;if(L.ket=L.cursor,(e=L.find_among_b(_,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function o(){L.ket=L.cursor,L.find_among_b(v,44)&&(L.bra=L.cursor,i()&&(L.slice_del(),a()))}function w(){var e;if(L.ket=L.cursor,(e=L.find_among_b(z,3))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("e");break;case 2:case 3:L.slice_from("a")}}function l(){var e;if(L.ket=L.cursor,(e=L.find_among_b(y,6))&&(L.bra=L.cursor,i()))switch(e){case 1:case 2:L.slice_del();break;case 3:L.slice_from("a");break;case 4:L.slice_from("e")}}function u(){var e;if(L.ket=L.cursor,(e=L.find_among_b(j,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function m(){var e;if(L.ket=L.cursor,(e=L.find_among_b(C,7))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:L.slice_del()}}function k(){var e;if(L.ket=L.cursor,(e=L.find_among_b(P,12))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 9:L.slice_del();break;case 2:case 5:case 8:L.slice_from("e");break;case 3:case 6:L.slice_from("a")}}function f(){var e;if(L.ket=L.cursor,(e=L.find_among_b(F,31))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:L.slice_del();break;case 2:case 5:case 10:case 14:case 19:L.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:L.slice_from("e")}}function b(){var e;if(L.ket=L.cursor,(e=L.find_among_b(S,42))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:L.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:L.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:L.slice_from("e")}}var d,g=[new n("cs",-1,-1),new n("dzs",-1,-1),new n("gy",-1,-1),new n("ly",-1,-1),new n("ny",-1,-1),new n("sz",-1,-1),new n("ty",-1,-1),new n("zs",-1,-1)],h=[new n("á",-1,1),new n("é",-1,2)],p=[new n("bb",-1,-1),new n("cc",-1,-1),new n("dd",-1,-1),new n("ff",-1,-1),new n("gg",-1,-1),new n("jj",-1,-1),new n("kk",-1,-1),new n("ll",-1,-1),new n("mm",-1,-1),new n("nn",-1,-1),new n("pp",-1,-1),new n("rr",-1,-1),new n("ccs",-1,-1),new n("ss",-1,-1),new n("zzs",-1,-1),new n("tt",-1,-1),new n("vv",-1,-1),new n("ggy",-1,-1),new n("lly",-1,-1),new n("nny",-1,-1),new n("tty",-1,-1),new n("ssz",-1,-1),new n("zz",-1,-1)],_=[new n("al",-1,1),new n("el",-1,2)],v=[new n("ba",-1,-1),new n("ra",-1,-1),new n("be",-1,-1),new n("re",-1,-1),new n("ig",-1,-1),new n("nak",-1,-1),new n("nek",-1,-1),new n("val",-1,-1),new n("vel",-1,-1),new n("ul",-1,-1),new n("nál",-1,-1),new n("nél",-1,-1),new n("ból",-1,-1),new n("ról",-1,-1),new n("tól",-1,-1),new n("bõl",-1,-1),new n("rõl",-1,-1),new n("tõl",-1,-1),new n("ül",-1,-1),new n("n",-1,-1),new n("an",19,-1),new n("ban",20,-1),new n("en",19,-1),new n("ben",22,-1),new n("képpen",22,-1),new n("on",19,-1),new n("ön",19,-1),new n("képp",-1,-1),new n("kor",-1,-1),new n("t",-1,-1),new n("at",29,-1),new n("et",29,-1),new n("ként",29,-1),new n("anként",32,-1),new n("enként",32,-1),new n("onként",32,-1),new n("ot",29,-1),new n("ért",29,-1),new n("öt",29,-1),new n("hez",-1,-1),new n("hoz",-1,-1),new n("höz",-1,-1),new n("vá",-1,-1),new n("vé",-1,-1)],z=[new n("án",-1,2),new n("én",-1,1),new n("ánként",-1,3)],y=[new n("stul",-1,2),new n("astul",0,1),new n("ástul",0,3),new n("stül",-1,2),new n("estül",3,1),new n("éstül",3,4)],j=[new n("á",-1,1),new n("é",-1,2)],C=[new n("k",-1,7),new n("ak",0,4),new n("ek",0,6),new n("ok",0,5),new n("ák",0,1),new n("ék",0,2),new n("ök",0,3)],P=[new n("éi",-1,7),new n("áéi",0,6),new n("ééi",0,5),new n("é",-1,9),new n("ké",3,4),new n("aké",4,1),new n("eké",4,1),new n("oké",4,1),new n("áké",4,3),new n("éké",4,2),new n("öké",4,1),new n("éé",3,8)],F=[new n("a",-1,18),new n("ja",0,17),new n("d",-1,16),new n("ad",2,13),new n("ed",2,13),new n("od",2,13),new n("ád",2,14),new n("éd",2,15),new n("öd",2,13),new n("e",-1,18),new n("je",9,17),new n("nk",-1,4),new n("unk",11,1),new n("ánk",11,2),new n("énk",11,3),new n("ünk",11,1),new n("uk",-1,8),new n("juk",16,7),new n("ájuk",17,5),new n("ük",-1,8),new n("jük",19,7),new n("éjük",20,6),new n("m",-1,12),new n("am",22,9),new n("em",22,9),new n("om",22,9),new n("ám",22,10),new n("ém",22,11),new n("o",-1,18),new n("á",-1,19),new n("é",-1,20)],S=[new n("id",-1,10),new n("aid",0,9),new n("jaid",1,6),new n("eid",0,9),new n("jeid",3,6),new n("áid",0,7),new n("éid",0,8),new n("i",-1,15),new n("ai",7,14),new n("jai",8,11),new n("ei",7,14),new n("jei",10,11),new n("ái",7,12),new n("éi",7,13),new n("itek",-1,24),new n("eitek",14,21),new n("jeitek",15,20),new n("éitek",14,23),new n("ik",-1,29),new n("aik",18,26),new n("jaik",19,25),new n("eik",18,26),new n("jeik",21,25),new n("áik",18,27),new n("éik",18,28),new n("ink",-1,20),new n("aink",25,17),new n("jaink",26,16),new n("eink",25,17),new n("jeink",28,16),new n("áink",25,18),new n("éink",25,19),new n("aitok",-1,21),new n("jaitok",32,20),new n("áitok",-1,22),new n("im",-1,5),new n("aim",35,4),new n("jaim",36,1),new n("eim",35,4),new n("jeim",38,1),new n("áim",35,2),new n("éim",35,3)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var n=L.cursor;return e(),L.limit_backward=n,L.cursor=L.limit,c(),L.cursor=L.limit,o(),L.cursor=L.limit,w(),L.cursor=L.limit,l(),L.cursor=L.limit,u(),L.cursor=L.limit,k(),L.cursor=L.limit,f(),L.cursor=L.limit,b(),L.cursor=L.limit,m(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}});
```
--------------------------------------------------------------------------------
/src/codegraphcontext/cli/main.py:
--------------------------------------------------------------------------------
```python
# src/codegraphcontext/cli/main.py
"""
This module defines the command-line interface (CLI) for the CodeGraphContext application.
It uses the Typer library to create a user-friendly and well-documented CLI.
Commands:
- setup: Runs an interactive wizard to configure the Neo4j database connection.
- start: Launches the main MCP server.
- help: Displays help information.
- version: Show the installed version.
"""
import typer
from rich.console import Console
from rich.table import Table
from typing import Optional
import asyncio
import logging
import json
import os
from pathlib import Path
from dotenv import load_dotenv, find_dotenv
from importlib.metadata import version as pkg_version, PackageNotFoundError
from codegraphcontext.server import MCPServer
from .setup_wizard import run_setup_wizard
# Import the new helper functions
from .cli_helpers import (
index_helper,
add_package_helper,
list_repos_helper,
delete_helper,
cypher_helper,
visualize_helper,
)
# Set the log level for the noisy neo4j and asyncio logger to WARNING to keep the output clean.
logging.getLogger("neo4j").setLevel(logging.WARNING)
logging.getLogger("asyncio").setLevel(logging.WARNING)
# Initialize the Typer app and Rich console for formatted output.
app = typer.Typer(
name="cgc",
help="CodeGraphContext: An MCP server for AI-powered code analysis.",
add_completion=True,
)
console = Console(stderr=True)
# Configure basic logging for the application.
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(name)s - %(message)s')
def get_version() -> str:
"""
Try to read version from the installed package metadata.
Fallback to a dev version if not installed.
"""
try:
return pkg_version("codegraphcontext") # must match [project].name in pyproject.toml
except PackageNotFoundError:
return "0.0.0 (dev)"
@app.command()
def setup():
"""
Runs the interactive setup wizard to configure the server and database connection.
This helps users set up a local Docker-based Neo4j instance or connect to a remote one.
"""
run_setup_wizard()
def _load_credentials():
"""
Loads Neo4j credentials from various sources into environment variables.
Priority order:
1. Local `mcp.json`
2. Global `~/.codegraphcontext/.env`
3. Any `.env` file found in the directory tree.
"""
# 1. Prefer loading from mcp.json
mcp_file_path = Path.cwd() / "mcp.json"
if mcp_file_path.exists():
try:
with open(mcp_file_path, "r") as f:
mcp_config = json.load(f)
server_env = mcp_config.get("mcpServers", {}).get("CodeGraphContext", {}).get("env", {})
for key, value in server_env.items():
os.environ[key] = value
console.print("[green]Loaded Neo4j credentials from local mcp.json.[/green]")
return
except Exception as e:
console.print(f"[bold red]Error loading mcp.json:[/bold red] {e}")
# 2. Try global .env file
global_env_path = Path.home() / ".codegraphcontext" / ".env"
if global_env_path.exists():
try:
load_dotenv(dotenv_path=global_env_path)
console.print(f"[green]Loaded Neo4j credentials from global .env file: {global_env_path}[/green]")
return
except Exception as e:
console.print(f"[bold red]Error loading global .env file from {global_env_path}:[/bold red] {e}")
# 3. Fallback to any discovered .env
try:
dotenv_path = find_dotenv(usecwd=True, raise_error_if_not_found=False)
if dotenv_path:
load_dotenv(dotenv_path)
console.print(f"[green]Loaded Neo4j credentials from discovered .env file: {dotenv_path}[/green]")
else:
console.print("[yellow]No local mcp.json or .env file found. Credentials may not be set.[/yellow]")
except Exception as e:
console.print(f"[bold red]Error loading .env file:[/bold red] {e}")
@app.command()
def start():
"""
Starts the CodeGraphContext MCP server, which listens for JSON-RPC requests from stdin.
"""
console.print("[bold green]Starting CodeGraphContext Server...[/bold green]")
_load_credentials()
server = None
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
# Initialize and run the main server.
server = MCPServer(loop=loop)
loop.run_until_complete(server.run())
except ValueError as e:
# This typically happens if credentials are still not found after all checks.
console.print(f"[bold red]Configuration Error:[/bold red] {e}")
console.print("Please run `cgc setup` to configure the server.")
except KeyboardInterrupt:
# Handle graceful shutdown on Ctrl+C.
console.print("\n[bold yellow]Server stopped by user.[/bold yellow]")
finally:
# Ensure server and event loop are properly closed.
if server:
server.shutdown()
loop.close()
@app.command()
def index(path: Optional[str] = typer.Argument(None, help="Path to the directory or file to index. Defaults to the current directory.")):
"""
Indexes a directory or file by adding it to the code graph.
If no path is provided, it indexes the current directory.
"""
_load_credentials() # Credentials must be loaded before helpers are called
if path is None:
path = str(Path.cwd())
index_helper(path)
@app.command()
def delete(path: str = typer.Argument(..., help="Path of the repository to delete from the code graph.")):
"""
Deletes a repository from the code graph.
"""
_load_credentials()
delete_helper(path)
@app.command()
def visualize(query: Optional[str] = typer.Argument(None, help="The Cypher query to visualize.")):
"""
Generates a URL to visualize a Cypher query in the Neo4j Browser.
If no query is provided, a default query will be used.
"""
if query is None:
query = "MATCH p=()-->() RETURN p"
visualize_helper(query)
@app.command(name="list-repos")
def list_repos():
"""
Lists all indexed repositories.
"""
_load_credentials()
list_repos_helper()
@app.command(name="add-package")
def add_package(package_name: str = typer.Argument(..., help="Name of the package to add."), language: str = typer.Argument(..., help="Language of the package." )):
"""
Adds a package to the code graph.
"""
_load_credentials()
add_package_helper(package_name, language)
@app.command()
def cypher(query: str = typer.Argument(..., help="The read-only Cypher query to execute.")):
"""
Executes a read-only Cypher query.
"""
_load_credentials()
cypher_helper(query)
@app.command(name="list-mcp-tools")
def list_mcp_tools():
"""
Lists all available tools and their descriptions.
"""
_load_credentials()
console.print("[bold green]Available Tools:[/bold green]")
try:
# Instantiate the server to access the tool definitions.
server = MCPServer()
tools = server.tools.values()
table = Table(show_header=True, header_style="bold magenta")
table.add_column("Tool Name", style="dim", width=30)
table.add_column("Description")
for tool in sorted(tools, key=lambda t: t['name']):
table.add_row(tool['name'], tool['description'])
console.print(table)
except ValueError as e:
console.print(f"[bold red]Error loading tools:[/bold red] {e}")
console.print("Please ensure your Neo4j credentials are set up correctly (`cgc setup`), as they are needed to initialize the server.")
except Exception as e:
console.print(f"[bold red]An unexpected error occurred:[/bold red] {e}")
@app.command()
def help(ctx: typer.Context):
"""Show the main help message and exit."""
root_ctx = ctx.parent or ctx
typer.echo(root_ctx.get_help())
@app.command("version")
def version_cmd():
"""Show the application version."""
console.print(f"CodeGraphContext [bold cyan]{get_version()}[/bold cyan]")
@app.callback(invoke_without_command=True)
def main(
ctx: typer.Context,
version_: bool = typer.Option(
None,
"--version",
"-v",
help="Show the application version and exit.",
is_eager=True,
),
):
"""
Main entry point for the cgc CLI application.
If no subcommand is provided, it displays a welcome message with instructions.
"""
if version_:
console.print(f"CodeGraphContext [bold cyan]{get_version()}[/bold cyan]")
raise typer.Exit()
if ctx.invoked_subcommand is None:
console.print("[bold green]👋 Welcome to CodeGraphContext (cgc)![/bold green]\n")
console.print("👉 Run [cyan]cgc setup[/cyan] to configure the server and database.")
console.print("👉 Run [cyan]cgc start[/cyan] to launch the server.")
console.print("👉 Run [cyan]cgc help[/cyan] to see all available commands.\n")
console.print("👉 Run [cyan]cgc --version[/cyan] to check the version.\n")
console.print("👉 Running [green]codegraphcontext [white]works the same as using [green]cgc")
```
--------------------------------------------------------------------------------
/tests/sample_project_typescript/src/classes-inheritance.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Classes and Inheritance
* Demonstrates TypeScript's OOP features including classes, inheritance,
* abstract classes, access modifiers, static members, and advanced patterns
*/
// ========== Basic Class Definition ==========
export class Person {
// Public property (default)
public name: string;
// Private property (only accessible within this class)
private _id: number;
// Protected property (accessible in this class and subclasses)
protected age: number;
// Readonly property
readonly birthDate: Date;
// Static property
static species: string = "Homo sapiens";
// Static method
static getSpecies(): string {
return Person.species;
}
constructor(name: string, age: number, id: number) {
this.name = name;
this.age = age;
this._id = id;
this.birthDate = new Date();
}
// Public method
public introduce(): string {
return `Hello, I'm ${this.name} and I'm ${this.age} years old.`;
}
// Private method
private validateAge(age: number): boolean {
return age >= 0 && age <= 150;
}
// Protected method
protected updateAge(newAge: number): void {
if (this.validateAge(newAge)) {
this.age = newAge;
}
}
// Getter
get id(): number {
return this._id;
}
// Setter
set id(value: number) {
if (value > 0) {
this._id = value;
}
}
// Method with overloads
greet(): string;
greet(formal: boolean): string;
greet(formal: boolean, title: string): string;
greet(formal?: boolean, title?: string): string {
const greeting = formal ? "Good day" : "Hi";
const nameWithTitle = title ? `${title} ${this.name}` : this.name;
return `${greeting}, ${nameWithTitle}!`;
}
}
// ========== Inheritance ==========
export class Employee extends Person {
private salary: number;
public department: string;
constructor(name: string, age: number, id: number, department: string, salary: number) {
super(name, age, id); // Call parent constructor
this.department = department;
this.salary = salary;
}
// Override parent method
public override introduce(): string {
return `${super.introduce()} I work in ${this.department}.`;
}
// New method specific to Employee
public work(): string {
return `${this.name} is working in the ${this.department} department.`;
}
// Access protected member from parent
public celebrateBirthday(): void {
this.updateAge(this.age + 1);
console.log(`Happy birthday! Now ${this.age} years old.`);
}
// Getter for private property
get monthlySalary(): number {
return this.salary;
}
// Setter with validation
set monthlySalary(value: number) {
if (value >= 0) {
this.salary = value;
} else {
throw new Error("Salary cannot be negative");
}
}
}
// ========== Abstract Classes ==========
export abstract class Animal {
protected name: string;
protected age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// Concrete method
public sleep(): string {
return `${this.name} is sleeping.`;
}
// Abstract method (must be implemented by subclasses)
abstract makeSound(): string;
// Abstract method with parameters
abstract move(distance: number): string;
}
// ========== Concrete Implementation of Abstract Class ==========
export class Dog extends Animal {
private breed: string;
constructor(name: string, age: number, breed: string) {
super(name, age);
this.breed = breed;
}
// Implementation of abstract method
public makeSound(): string {
return `${this.name} the ${this.breed} says: Woof! Woof!`;
}
// Implementation of abstract method
public move(distance: number): string {
return `${this.name} runs ${distance} meters.`;
}
// Additional method specific to Dog
public fetch(): string {
return `${this.name} fetches the ball!`;
}
}
export class Cat extends Animal {
private indoor: boolean;
constructor(name: string, age: number, indoor: boolean = true) {
super(name, age);
this.indoor = indoor;
}
public makeSound(): string {
return `${this.name} says: Meow!`;
}
public move(distance: number): string {
const verb = this.indoor ? "walks" : "prowls";
return `${this.name} ${verb} ${distance} meters.`;
}
public climb(): string {
return `${this.name} climbs up high.`;
}
}
// ========== Interface Implementation ==========
interface Flyable {
fly(height: number): string;
land(): string;
}
interface Swimmable {
swim(depth: number): string;
surface(): string;
}
export class Duck extends Animal implements Flyable, Swimmable {
constructor(name: string, age: number) {
super(name, age);
}
public makeSound(): string {
return `${this.name} says: Quack! Quack!`;
}
public move(distance: number): string {
return `${this.name} waddles ${distance} meters.`;
}
// Implement Flyable
public fly(height: number): string {
return `${this.name} flies at ${height} meters high.`;
}
public land(): string {
return `${this.name} lands gracefully.`;
}
// Implement Swimmable
public swim(depth: number): string {
return `${this.name} swims at ${depth} meters deep.`;
}
public surface(): string {
return `${this.name} surfaces from the water.`;
}
}
// ========== Generic Classes ==========
export class Container<T> {
private items: T[] = [];
public add(item: T): void {
this.items.push(item);
}
public remove(item: T): boolean {
const index = this.items.indexOf(item);
if (index > -1) {
this.items.splice(index, 1);
return true;
}
return false;
}
public getAll(): readonly T[] {
return [...this.items];
}
public size(): number {
return this.items.length;
}
public clear(): void {
this.items = [];
}
}
// Generic class with constraints
export class NumberContainer<T extends number> extends Container<T> {
public sum(): T {
return this.getAll().reduce((sum, item) => (sum + item) as T, 0 as T);
}
public average(): number {
const items = this.getAll();
return items.length > 0 ? this.sum() / items.length : 0;
}
}
// ========== Singleton Pattern ==========
export class Database {
private static instance: Database;
private connections: number = 0;
private constructor() {
// Private constructor prevents instantiation
}
public static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
public connect(): string {
this.connections++;
return `Connected to database. Total connections: ${this.connections}`;
}
public disconnect(): string {
if (this.connections > 0) {
this.connections--;
return `Disconnected from database. Remaining connections: ${this.connections}`;
}
return "No active connections to disconnect.";
}
}
// ========== Static Class Pattern ==========
export class MathUtils {
// Prevent instantiation
private constructor() {}
public static PI: number = Math.PI;
public static calculateCircleArea(radius: number): number {
return MathUtils.PI * radius * radius;
}
public static calculateDistance(x1: number, y1: number, x2: number, y2: number): number {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
public static clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}
}
// ========== Mixins Pattern ==========
type Constructor<T = {}> = new (...args: any[]) => T;
// Mixin function
export function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
getTimestamp(): number {
return this.timestamp;
}
};
}
export function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActive = false;
activate(): void {
this.isActive = true;
}
deactivate(): void {
this.isActive = false;
}
};
}
// Base class for mixins
export class BaseUser {
constructor(public name: string) {}
}
// Apply mixins
export const TimestampedUser = Timestamped(BaseUser);
export const ActivatableUser = Activatable(BaseUser);
export const EnhancedUser = Timestamped(Activatable(BaseUser));
// ========== Usage Examples ==========
export const classExamples = {
// Basic usage
person: new Person("John Doe", 30, 123),
employee: new Employee("Jane Smith", 28, 456, "Engineering", 75000),
// Animals
dog: new Dog("Buddy", 3, "Golden Retriever"),
cat: new Cat("Whiskers", 2, true),
duck: new Duck("Quackers", 1),
// Generic containers
stringContainer: new Container<string>(),
numberContainer: new NumberContainer<number>(),
// Singleton
database: Database.getInstance(),
// Mixins
timestampedUser: new TimestampedUser("Alice"),
enhancedUser: new EnhancedUser("Bob")
};
// Initialize some examples
classExamples.stringContainer.add("Hello");
classExamples.stringContainer.add("World");
classExamples.numberContainer.add(10);
classExamples.numberContainer.add(20);
classExamples.numberContainer.add(30);
```