#
tokens: 48120/50000 6/440 files (page 12/23)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 12 of 23. Use http://codebase.md/shashankss1205/codegraphcontext?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .cgcignore
├── .dockerignore
├── .env.example
├── .github
│   ├── FUNDING.yml
│   └── workflows
│       ├── docker-publish.yml
│       ├── e2e-tests.yml
│       ├── post_discord_invite.yml
│       ├── test.yml
│       └── update-contributors.yml
├── .gitignore
├── CLI_Commands.md
├── CONTRIBUTING.md
├── contributors.md
├── deploy-production.sh
├── DEPLOYMENT_CHECKLIST.md
├── DOCKER_COMPLETE_GUIDE.md
├── DOCKER_DEPLOYMENT.md
├── DOCKER_README.md
├── docker-compose.template.yml
├── docker-quickstart.sh
├── Dockerfile
├── 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
│   │   └── watching.md
│   ├── mkdocs.yml
│   └── site
│       ├── 404.html
│       ├── architecture
│       │   └── index.html
│       ├── assets
│       │   ├── images
│       │   │   └── favicon.png
│       │   ├── javascripts
│       │   │   ├── bundle.79ae519e.min.js
│       │   │   ├── bundle.79ae519e.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.2c215733.min.js
│       │   │       └── search.2c215733.min.js.map
│       │   └── stylesheets
│       │       ├── main.484c7ddc.min.css
│       │       ├── main.484c7ddc.min.css.map
│       │       ├── palette.ab4e12ef.min.css
│       │       └── palette.ab4e12ef.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
│       └── watching
│           └── index.html
├── funding.json
├── HOSTING_COMPARISON.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
├── k8s
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── neo4j-deployment.yaml
│   ├── pvc.yaml
│   ├── README.md
│   └── service.yaml
├── LICENSE
├── MANIFEST.in
├── organizer
│   ├── CONTRIBUTING_LANGUAGES.md
│   ├── cookbook.md
│   ├── docs.md
│   ├── language_specific_nodes.md
│   ├── Tools_Exploration.md
│   └── troubleshoot.md
├── pyproject.toml
├── README.md
├── scripts
│   ├── generate_lang_contributors.py
│   ├── post_install_fix.sh
│   ├── test_all_parsers.py
│   └── update_language_parsers.py
├── SECURITY.md
├── src
│   └── codegraphcontext
│       ├── __init__.py
│       ├── __main__.py
│       ├── cli
│       │   ├── __init__.py
│       │   ├── cli_helpers.py
│       │   ├── config_manager.py
│       │   ├── main.py
│       │   ├── setup_macos.py
│       │   └── setup_wizard.py
│       ├── core
│       │   ├── __init__.py
│       │   ├── database_falkordb.py
│       │   ├── database.py
│       │   ├── falkor_worker.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
│       │   │   ├── csharp.py
│       │   │   ├── go.py
│       │   │   ├── java.py
│       │   │   ├── javascript.py
│       │   │   ├── kotlin.py
│       │   │   ├── php.py
│       │   │   ├── python.py
│       │   │   ├── ruby.py
│       │   │   ├── rust.py
│       │   │   ├── scala.py
│       │   │   ├── swift.py
│       │   │   ├── typescript.py
│       │   │   └── typescriptjsx.py
│       │   ├── package_resolver.py
│       │   ├── query_tool_languages
│       │   │   ├── c_toolkit.py
│       │   │   ├── cpp_toolkit.py
│       │   │   ├── csharp_toolkit.py
│       │   │   ├── go_toolkit.py
│       │   │   ├── java_toolkit.py
│       │   │   ├── javascript_toolkit.py
│       │   │   ├── python_toolkit.py
│       │   │   ├── ruby_toolkit.py
│       │   │   ├── rust_toolkit.py
│       │   │   ├── scala_toolkit.py
│       │   │   ├── swift_toolkit.py
│       │   │   └── typescript_toolkit.py
│       │   └── system.py
│       └── utils
│           ├── debug_log.py
│           └── tree_sitter_manager.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_csharp
│   │   ├── README.md
│   │   └── src
│   │       └── Example.App
│   │           ├── Attributes
│   │           │   └── CustomAttributes.cs
│   │           ├── Example.App.csproj
│   │           ├── Models
│   │           │   ├── Person.cs
│   │           │   ├── Point.cs
│   │           │   ├── Role.cs
│   │           │   └── User.cs
│   │           ├── OuterClass.cs
│   │           ├── Program.cs
│   │           ├── Services
│   │           │   ├── GreetingService.cs
│   │           │   ├── IGreetingService.cs
│   │           │   └── LegacyService.cs
│   │           └── Utils
│   │               ├── CollectionHelper.cs
│   │               └── FileHelper.cs
│   ├── 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_kotlin
│   │   ├── AdvancedClasses.kt
│   │   ├── Annotations.kt
│   │   ├── Coroutines.kt
│   │   ├── EdgeCases.kt
│   │   ├── Functions.kt
│   │   ├── Main.kt
│   │   ├── Properties.kt
│   │   └── User.kt
│   ├── 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_scala
│   │   ├── Animals.scala
│   │   ├── Complex.scala
│   │   ├── Functional.scala
│   │   ├── Geometry.scala
│   │   ├── Main.scala
│   │   ├── PackageObject.scala
│   │   ├── Script.sc
│   │   ├── Services.scala
│   │   ├── Shapes.scala
│   │   ├── Utils.scala
│   │   └── Variables.scala
│   ├── sample_project_swift
│   │   ├── Generics.swift
│   │   ├── Main.swift
│   │   ├── README.md
│   │   ├── Shapes.swift
│   │   ├── User.swift
│   │   └── Vehicles.swift
│   ├── sample_project_typescript
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── sample_tsx.tsx
│   │   ├── 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_kotlin_parser.py
│   ├── test_swift_parser.py
│   ├── test_tree_sitter
│   │   ├── __init__.py
│   │   ├── class_instantiation.py
│   │   ├── complex_classes.py
│   │   └── test_file.py
│   ├── test_tree_sitter_manager.py
│   └── test_typescript_parser.py
├── visualize_graph.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
│   │   │   ├── SocialMentionsTimeline.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
└── windows_setup_guide.md
```

# Files

--------------------------------------------------------------------------------
/src/codegraphcontext/tools/languages/ruby.py:
--------------------------------------------------------------------------------

```python
  1 | from pathlib import Path
  2 | from typing import Any, Dict, Optional, Tuple
  3 | from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger, debug_logger
  4 | from codegraphcontext.utils.tree_sitter_manager import execute_query
  5 | 
  6 | RUBY_QUERIES = {
  7 |     "functions": """
  8 |         (method
  9 |             name: (identifier) @name
 10 |         ) @function_node
 11 |     """,
 12 |     "classes": """
 13 |         (class
 14 |             name: (constant) @name
 15 |         ) @class
 16 |     """,
 17 |     "modules": """
 18 |         (module
 19 |             name: (constant) @name
 20 |         ) @module_node
 21 |     """,
 22 |     "imports": """
 23 |         (call
 24 |             method: (identifier) @method_name
 25 |             arguments: (argument_list
 26 |                 (string) @path
 27 |             )
 28 |         ) @import
 29 |     """,
 30 |     "calls": """
 31 |         (call
 32 |             receiver: (_)? @receiver
 33 |             method: (identifier) @name
 34 |             arguments: (argument_list)? @args
 35 |         ) @call_node
 36 |     """,
 37 |     "variables": """
 38 |         (assignment
 39 |             left: (identifier) @name
 40 |             right: (_) @value
 41 |         )
 42 |         (assignment
 43 |             left: (instance_variable) @name
 44 |             right: (_) @value
 45 |         )
 46 |     """,
 47 |     "comments": """
 48 |         (comment) @comment
 49 |     """,
 50 |     "module_includes": """
 51 |         (call
 52 |           method: (identifier) @method
 53 |           arguments: (argument_list (constant) @module)
 54 |         ) @include_call
 55 |     """,
 56 | }
 57 | 
 58 | 
 59 | class RubyTreeSitterParser:
 60 |     """A Ruby-specific parser using tree-sitter."""
 61 | 
 62 |     def __init__(self, generic_parser_wrapper: Any):
 63 |         self.generic_parser_wrapper = generic_parser_wrapper
 64 |         self.language_name = "ruby"
 65 |         self.language = generic_parser_wrapper.language
 66 |         self.parser = generic_parser_wrapper.parser
 67 | 
 68 |     def _get_node_text(self, node: Any) -> str:
 69 |         return node.text.decode("utf-8")
 70 |     
 71 |     def _enclosing_class_name(self, node: Any) -> Optional[str]:
 72 |         name, typ, _ = self._get_parent_context(node, ('class',))
 73 |         return name
 74 |     
 75 |     def _find_modules(self, root_node: Any) -> list[Dict[str, Any]]:
 76 |         modules = []
 77 |         query_str = RUBY_QUERIES["modules"]
 78 |         # name via captures
 79 |         captures = list(execute_query(self.language, query_str, root_node))
 80 |         for node, cap in captures:
 81 |             if cap == "module_node":
 82 |                 name = None
 83 |                 for n, c in captures:
 84 |                     if c == "name":
 85 |                         if n.start_byte >= node.start_byte and n.end_byte <= node.end_byte:
 86 |                             name = self._get_node_text(n)
 87 |                             break
 88 |                 if name:
 89 |                     modules.append({
 90 |                         "name": name,
 91 |                         "line_number": node.start_point[0] + 1,
 92 |                         "end_line": node.end_point[0] + 1,
 93 |                         "source": self._get_node_text(node),
 94 | 
 95 |                         "lang": self.language_name,
 96 |                         "is_dependency": False,
 97 |                     })
 98 |         return modules
 99 | 
100 |     def _find_module_inclusions(self, root_node: Any) -> list[Dict[str, Any]]:
101 |         includes = []
102 |         query_str = RUBY_QUERIES["module_includes"]
103 |         for node, cap in execute_query(self.language, query_str, root_node):
104 |             if cap == "method":
105 |                 method_name = self._get_node_text(node)
106 |                 if method_name != "include":
107 |                     continue
108 |             if cap == "include_call":
109 |                 method = None
110 |                 module = None
111 |                 for n, c in execute_query(self.language, query_str, node):
112 |                     if c == "method":
113 |                         method = self._get_node_text(n)
114 |                     elif c == "module":
115 |                         module = self._get_node_text(n)
116 |                 if method == "include" and module:
117 |                     cls = self._enclosing_class_name(node)
118 |                     if cls:
119 |                         includes.append({
120 |                             "class": cls,
121 |                             "module": module,
122 |                             "line_number": node.start_point[0] + 1,
123 |                             "lang": self.language_name,
124 |                             "is_dependency": False,
125 |                         })
126 |         return includes
127 | 
128 | 
129 |     def _get_parent_context(self, node: Any, types: Tuple[str, ...] = ('class', 'module', 'method')):
130 |         """Find parent context for Ruby constructs."""
131 |         curr = node.parent
132 |         while curr:
133 |             if curr.type in types:
134 |                 name_node = curr.child_by_field_name('name')
135 |                 if name_node:
136 |                     return self._get_node_text(name_node), curr.type, curr.start_point[0] + 1
137 |             curr = curr.parent
138 |         return None, None, None
139 | 
140 |     def _calculate_complexity(self, node: Any) -> int:
141 |         """Calculate cyclomatic complexity for Ruby constructs."""
142 |         complexity_nodes = {
143 |             "if", "unless", "case", "when", "while", "until", "for", "rescue", "ensure",
144 |             "and", "or", "&&", "||", "?", "ternary"
145 |         }
146 |         count = 1
147 | 
148 |         def traverse(n):
149 |             nonlocal count
150 |             if n.type in complexity_nodes:
151 |                 count += 1
152 |             for child in n.children:
153 |                 traverse(child)
154 | 
155 |         traverse(node)
156 |         return count
157 | 
158 |     def _get_docstring(self, node: Any) -> Optional[str]:
159 |         """Extract comments as docstrings for Ruby constructs."""
160 |         # Look for comments before the node
161 |         prev_sibling = node.prev_sibling
162 |         while prev_sibling and prev_sibling.type in ('comment', '\n', ' '):
163 |             if prev_sibling.type == 'comment':
164 |                 comment_text = self._get_node_text(prev_sibling)
165 |                 if comment_text.startswith('#') and not comment_text.startswith('#!'):
166 |                     return comment_text.strip()
167 |             prev_sibling = prev_sibling.prev_sibling
168 |         return None
169 | 
170 |     def _parse_method_parameters(self, method_node: Any) -> list[str]:
171 |         """Parse method parameters from a method node."""
172 |         params = []
173 |         # Look for parameters in the method node
174 |         for child in method_node.children:
175 |             if child.type == 'identifier' and child != method_node.child_by_field_name('name'):
176 |                 # This is likely a parameter
177 |                 params.append(self._get_node_text(child))
178 |         return params
179 | 
180 |     def parse(self, file_path: Path, is_dependency: bool = False) -> Dict[str, Any]:
181 |         """Parses a Ruby file and returns its structure."""
182 |         with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
183 |             source_code = f.read()
184 | 
185 |         tree = self.parser.parse(bytes(source_code, "utf8"))
186 |         root_node = tree.root_node
187 | 
188 |         functions = self._find_functions(root_node)
189 |         classes = self._find_classes(root_node)
190 |         imports = self._find_imports(root_node)
191 |         function_calls = self._find_calls(root_node)
192 |         variables = self._find_variables(root_node)
193 |         modules = self._find_modules(root_node)
194 |         module_inclusions = self._find_module_inclusions(root_node)
195 | 
196 |         return {
197 |             "file_path": str(file_path),
198 |             "functions": functions,
199 |             "classes": classes,
200 |             "variables": variables,
201 |             "imports": imports,
202 |             "function_calls": function_calls,
203 |             "is_dependency": is_dependency,
204 |             "lang": self.language_name,
205 |             "modules": modules,
206 |             "module_inclusions": module_inclusions,
207 |         }
208 | 
209 |     def _find_functions(self, root_node: Any) -> list[Dict[str, Any]]:
210 |         """Find all function/method definitions."""
211 |         functions = []
212 |         query_str = RUBY_QUERIES["functions"]
213 |         
214 |         # Collect all captures first
215 |         all_captures = list(execute_query(self.language, query_str, root_node))
216 |         
217 |         # Group captures by function node using a different approach
218 |         captures_by_function = {}
219 |         for node, capture_name in all_captures:
220 |             if capture_name == 'function_node':
221 |                 captures_by_function[id(node)] = {'node': node, 'name': None}
222 |         
223 |         # Now find names for each function
224 |         for node, capture_name in all_captures:
225 |             if capture_name == 'name':
226 |                 # Find which function this name belongs to
227 |                 for func_id, func_data in captures_by_function.items():
228 |                     func_node = func_data['node']
229 |                     # Check if this name node is within the function node
230 |                     if (node.start_byte >= func_node.start_byte and 
231 |                         node.end_byte <= func_node.end_byte):
232 |                         captures_by_function[func_id]['name'] = self._get_node_text(node)
233 |                         break
234 | 
235 |         # Build function entries
236 |         for func_data in captures_by_function.values():
237 |             func_node = func_data['node']
238 |             name = func_data['name']
239 |             
240 |             if name:
241 |                 args = self._parse_method_parameters(func_node)
242 | 
243 |                 # Get context and docstring
244 |                 context, context_type, _ = self._get_parent_context(func_node)
245 |                 class_context = context if context_type in ('class', 'module') else None
246 |                 docstring = self._get_docstring(func_node)
247 | 
248 |                 functions.append({
249 |                     "name": name,
250 |                     "line_number": func_node.start_point[0] + 1,
251 |                     "end_line": func_node.end_point[0] + 1,
252 |                     "args": args,
253 |                     "source": self._get_node_text(func_node),
254 | 
255 |                     "docstring": docstring,
256 |                     "cyclomatic_complexity": self._calculate_complexity(func_node),
257 |                     "context": context,
258 |                     "context_type": context_type,
259 |                     "class_context": class_context,
260 |                     "decorators": [],
261 |                     "lang": self.language_name,
262 |                     "is_dependency": False,
263 |                 })
264 | 
265 |         return functions
266 | 
267 |     def _find_classes(self, root_node: Any) -> list[Dict[str, Any]]:
268 |         """Find all class and module definitions."""
269 |         classes = []
270 |         query_str = RUBY_QUERIES["classes"]
271 |         
272 |         # Collect all captures first
273 |         all_captures = list(execute_query(self.language, query_str, root_node))
274 |         
275 |         # Group captures by class node using a different approach
276 |         captures_by_class = {}
277 |         for node, capture_name in all_captures:
278 |             if capture_name == 'class':
279 |                 captures_by_class[id(node)] = {'node': node, 'name': None}
280 |         
281 |         # Now find names for each class
282 |         for node, capture_name in all_captures:
283 |             if capture_name == 'name':
284 |                 # Find which class this name belongs to
285 |                 for class_id, class_data in captures_by_class.items():
286 |                     class_node = class_data['node']
287 |                     # Check if this name node is within the class node
288 |                     if (node.start_byte >= class_node.start_byte and 
289 |                         node.end_byte <= class_node.end_byte):
290 |                         captures_by_class[class_id]['name'] = self._get_node_text(node)
291 |                         break
292 | 
293 |         # Build class entries
294 |         for class_data in captures_by_class.values():
295 |             class_node = class_data['node']
296 |             name = class_data['name']
297 |             
298 |             if name:
299 |                 # Get superclass for inheritance (simplified)
300 |                 bases = []
301 | 
302 |                 # Get docstring
303 |                 docstring = self._get_docstring(class_node)
304 | 
305 |                 classes.append({
306 |                     "name": name,
307 |                     "line_number": class_node.start_point[0] + 1,
308 |                     "end_line": class_node.end_point[0] + 1,
309 |                     "bases": bases,
310 |                     "source": self._get_node_text(class_node),
311 | 
312 |                     "docstring": docstring,
313 |                     "context": None,
314 |                     "decorators": [],
315 |                     "lang": self.language_name,
316 |                     "is_dependency": False,
317 |                 })
318 | 
319 |         return classes
320 | 
321 |     def _find_imports(self, root_node: Any) -> list[Dict[str, Any]]:
322 |         """Find all require/load statements."""
323 |         imports = []
324 |         query_str = RUBY_QUERIES["imports"]
325 |         
326 |         # Collect all captures first
327 |         all_captures = list(execute_query(self.language, query_str, root_node))
328 |         
329 |         # Group captures by import node using a different approach
330 |         captures_by_import = {}
331 |         for node, capture_name in all_captures:
332 |             if capture_name == 'import':
333 |                 captures_by_import[id(node)] = {'node': node, 'method_name': None, 'path': None}
334 |         
335 |         # Now find method names and paths for each import
336 |         for node, capture_name in all_captures:
337 |             if capture_name == 'method_name':
338 |                 # Find which import this method name belongs to
339 |                 for import_id, import_data in captures_by_import.items():
340 |                     import_node = import_data['node']
341 |                     # Check if this method name node is within the import node
342 |                     if (node.start_byte >= import_node.start_byte and 
343 |                         node.end_byte <= import_node.end_byte):
344 |                         captures_by_import[import_id]['method_name'] = self._get_node_text(node)
345 |                         break
346 |             elif capture_name == 'path':
347 |                 # Find which import this path belongs to
348 |                 for import_id, import_data in captures_by_import.items():
349 |                     import_node = import_data['node']
350 |                     # Check if this path node is within the import node
351 |                     if (node.start_byte >= import_node.start_byte and 
352 |                         node.end_byte <= import_node.end_byte):
353 |                         captures_by_import[import_id]['path'] = self._get_node_text(node)
354 |                         break
355 | 
356 |         # Build import entries
357 |         for import_data in captures_by_import.values():
358 |             import_node = import_data['node']
359 |             method_name = import_data['method_name']
360 |             path = import_data['path']
361 |             
362 |             if method_name and path:
363 |                 path = path.strip('\'"')
364 |                 
365 |                 # Only process require/load statements
366 |                 if method_name in ('require', 'require_relative', 'load'):
367 |                     imports.append({
368 |                         "name": path,
369 |                         "full_import_name": f"{method_name} '{path}'",
370 |                         "line_number": import_node.start_point[0] + 1,
371 |                         "alias": None,
372 |                         "lang": self.language_name,
373 |                         "is_dependency": False,
374 |                     })
375 | 
376 |         return imports
377 | 
378 |     def _find_calls(self, root_node: Any) -> list[Dict[str, Any]]:
379 |         """Find all function and method calls."""
380 |         calls = []
381 |         query_str = RUBY_QUERIES["calls"]
382 |         
383 |         # Collect all captures
384 |         all_captures = list(execute_query(self.language, query_str, root_node))
385 |         
386 |         # Group by call node
387 |         captures_by_call = {}
388 |         for node, capture_name in all_captures:
389 |             if capture_name == 'call_node':
390 |                 captures_by_call[id(node)] = {'node': node, 'name': None, 'receiver': None, 'args': []}
391 |         
392 |         for node, capture_name in all_captures:
393 |              for call_id, call_data in captures_by_call.items():
394 |                 call_node = call_data['node']
395 |                 if not (node.start_byte >= call_node.start_byte and node.end_byte <= call_node.end_byte):
396 |                     continue
397 | 
398 |                 if capture_name == 'name':
399 |                      # The identifier could be part of receiver or arguments too, be careful
400 |                      # But tree-sitter structure ensures method name is distinct
401 |                      # Check if node is child 'method' of call_node
402 |                      if node == call_node.child_by_field_name('method'):
403 |                         captures_by_call[call_id]['name'] = self._get_node_text(node)
404 |                 
405 |                 elif capture_name == 'receiver':
406 |                     captures_by_call[call_id]['receiver'] = self._get_node_text(node)
407 |                 
408 |                 elif capture_name == 'args':
409 |                      # Capture arguments
410 |                     args_text = self._get_node_text(node)
411 |                     # Simple heuristic: split by comma
412 |                     captures_by_call[call_id]['args'] = [a.strip() for a in args_text.strip("()").split(',') if a.strip()]
413 | 
414 |         for call_data in captures_by_call.values():
415 |             call_node = call_data['node']
416 |             name = call_data['name']
417 |             
418 |             if name:
419 |                 receiver = call_data['receiver']
420 |                 full_name = f"{receiver}.{name}" if receiver else name
421 |                 
422 |                 context_name, context_type, context_line = self._get_parent_context(call_node)
423 |                 class_context = context_name if context_type in ('class', 'module') else None
424 |                 if context_type == 'method':
425 |                      # If inside a method, try to find enclosing class too
426 |                      enclosing_class, _, _ = self._get_parent_context(call_node.parent, ('class', 'module'))
427 |                      class_context = enclosing_class
428 | 
429 | 
430 |                 calls.append({
431 |                     "name": name,
432 |                     "full_name": full_name,
433 |                     "line_number": call_node.start_point[0] + 1,
434 |                     "args": call_data['args'],
435 |                     "inferred_obj_type": None,
436 |                     "context": (context_name, context_type, context_line),
437 |                     "class_context": class_context,
438 |                     "lang": self.language_name,
439 |                     "is_dependency": False,
440 |                 })
441 | 
442 |         return calls
443 | 
444 |     def _find_variables(self, root_node: Any) -> list[Dict[str, Any]]:
445 |         """Find all variable assignments."""
446 |         variables = []
447 |         query_str = RUBY_QUERIES["variables"]
448 |         
449 |         # Group captures by assignment node
450 |         captures_by_assignment = {}
451 |         for node, capture_name in execute_query(self.language, query_str, root_node):
452 |             if capture_name == 'name':
453 |                 # Find the parent assignment node
454 |                 current = node.parent
455 |                 while current and current.type != 'assignment':
456 |                     current = current.parent
457 |                 if current:
458 |                     assignment_id = id(current)
459 |                     if assignment_id not in captures_by_assignment:
460 |                         captures_by_assignment[assignment_id] = {'node': current, 'name': None, 'value': None}
461 |                     captures_by_assignment[assignment_id]['name'] = self._get_node_text(node)
462 |             elif capture_name == 'value':
463 |                 # Find the parent assignment node
464 |                 current = node.parent
465 |                 while current and current.type != 'assignment':
466 |                     current = current.parent
467 |                 if current:
468 |                     assignment_id = id(current)
469 |                     if assignment_id not in captures_by_assignment:
470 |                         captures_by_assignment[assignment_id] = {'node': current, 'name': None, 'value': None}
471 |                     captures_by_assignment[assignment_id]['value'] = self._get_node_text(node)
472 | 
473 |         # Build variable entries
474 |         for var_data in captures_by_assignment.values():
475 |             name = var_data['name']
476 |             value = var_data['value']
477 |             
478 |             if name:
479 |                 # Determine variable type based on name prefix
480 |                 var_type = "local"
481 |                 if name.startswith("@"):
482 |                     var_type = "instance"
483 |                 elif name.startswith("@@"):
484 |                     var_type = "class"
485 |                 elif name.startswith("$"):
486 |                     var_type = "global"
487 | 
488 |                 variables.append({
489 |                     "name": name,
490 |                     "line_number": var_data['node'].start_point[0] + 1,
491 |                     "value": value,
492 |                     "type": var_type,
493 |                     "context": None,  # Placeholder
494 |                     "class_context": None,  # Placeholder
495 |                     "lang": self.language_name,
496 |                     "is_dependency": False,
497 |                 })
498 | 
499 |         return variables
500 | 
501 | 
502 | def pre_scan_ruby(files: list[Path], parser_wrapper) -> dict:
503 |     """Scans Ruby files to create a map of class/method names to their file paths."""
504 |     imports_map = {}
505 |     query_str = """
506 |         (class
507 |             name: (constant) @name
508 |         )
509 |         (module
510 |             name: (constant) @name
511 |         )
512 |         (method
513 |             name: (identifier) @name
514 |         )
515 |     """
516 |     
517 | 
518 |     for file_path in files:
519 |         try:
520 |             with open(file_path, "r", encoding="utf-8") as f:
521 |                 tree = parser_wrapper.parser.parse(bytes(f.read(), "utf8"))
522 | 
523 |             for capture, _ in execute_query(parser_wrapper.language, query_str, tree.root_node):
524 |                 name = capture.text.decode('utf-8')
525 |                 if name not in imports_map:
526 |                     imports_map[name] = []
527 |                 imports_map[name].append(str(file_path.resolve()))
528 |         except Exception as e:
529 |             warning_logger(f"Tree-sitter pre-scan failed for {file_path}: {e}")
530 |     
531 |     return imports_map
532 | 
```

--------------------------------------------------------------------------------
/src/codegraphcontext/tools/languages/c.py:
--------------------------------------------------------------------------------

```python
  1 | from pathlib import Path
  2 | from typing import Any, Dict, Optional, Tuple
  3 | from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger
  4 | from codegraphcontext.utils.tree_sitter_manager import execute_query
  5 | 
  6 | C_QUERIES = {
  7 |     "functions": """
  8 |         (function_definition
  9 |             declarator: (function_declarator
 10 |                 declarator: (identifier) @name
 11 |             )
 12 |         ) @function_node
 13 |         
 14 |         (function_definition
 15 |             declarator: (function_declarator
 16 |                 declarator: (pointer_declarator
 17 |                     declarator: (identifier) @name
 18 |                 )
 19 |             )
 20 |         ) @function_node
 21 |     """,
 22 |     "structs": """
 23 |         (struct_specifier
 24 |             name: (type_identifier) @name
 25 |         ) @struct
 26 |     """,
 27 |     "unions": """
 28 |         (union_specifier
 29 |             name: (type_identifier) @name
 30 |         ) @union
 31 |     """,
 32 |     "enums": """
 33 |         (enum_specifier
 34 |             name: (type_identifier) @name
 35 |         ) @enum
 36 |     """,
 37 |     "typedefs": """
 38 |         (type_definition
 39 |             declarator: (type_identifier) @name
 40 |         ) @typedef
 41 |     """,
 42 |     "imports": """
 43 |         (preproc_include
 44 |             path: [
 45 |                 (string_literal) @path
 46 |                 (system_lib_string) @path
 47 |             ]
 48 |         ) @import
 49 |     """,
 50 |     "calls": """
 51 |         (call_expression
 52 |             function: (identifier) @name
 53 |         )
 54 |     """,
 55 |     "variables": """
 56 |         (declaration
 57 |             declarator: (init_declarator
 58 |                 declarator: (identifier) @name
 59 |             )
 60 |         )
 61 |         
 62 |         (declaration
 63 |             declarator: (init_declarator
 64 |                 declarator: (pointer_declarator
 65 |                     declarator: (identifier) @name
 66 |                 )
 67 |             )
 68 |         )
 69 |         
 70 |         (declaration
 71 |             declarator: (identifier) @name
 72 |         )
 73 |         
 74 |         (declaration
 75 |             declarator: (pointer_declarator
 76 |                 declarator: (identifier) @name
 77 |             )
 78 |         )
 79 |     """,
 80 |     "macros": """
 81 |         (preproc_def
 82 |             name: (identifier) @name
 83 |         ) @macro
 84 |     """,
 85 | }
 86 | 
 87 | class CTreeSitterParser:
 88 |     """A C-specific parser using tree-sitter."""
 89 | 
 90 |     def __init__(self, generic_parser_wrapper: Any):
 91 |         self.generic_parser_wrapper = generic_parser_wrapper
 92 |         self.language_name = "c"
 93 |         self.language = generic_parser_wrapper.language
 94 |         self.parser = generic_parser_wrapper.parser
 95 | 
 96 |     def _get_node_text(self, node: Any) -> str:
 97 |         return node.text.decode("utf-8")
 98 | 
 99 |     def parse(self, file_path: Path, is_dependency: bool = False) -> Dict[str, Any]:
100 |         """Parses a C file and returns its structure."""
101 |         with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
102 |             source_code = f.read()
103 | 
104 |         tree = self.parser.parse(bytes(source_code, "utf8"))
105 |         root_node = tree.root_node
106 | 
107 |         functions = self._find_functions(root_node)
108 |         classes = self._find_structs_unions_enums(root_node)
109 |         imports = self._find_imports(root_node)
110 |         function_calls = self._find_calls(root_node)
111 |         variables = self._find_variables(root_node)
112 |         macros = self._find_macros(root_node)
113 | 
114 |         return {
115 |             "file_path": str(file_path),
116 |             "functions": functions,
117 |             "classes": classes,
118 |             "variables": variables,
119 |             "imports": imports,
120 |             "function_calls": function_calls,
121 |             "macros": macros,
122 |             "is_dependency": is_dependency,
123 |             "lang": self.language_name,
124 |         }
125 | 
126 |     def _get_parent_context(self, node: Any, types: tuple = ('function_definition', 'struct_specifier', 'union_specifier', 'enum_specifier')) -> tuple:
127 |         """Get parent context for nested constructs."""
128 |         curr = node.parent
129 |         while curr:
130 |             if curr.type in types:
131 |                 if curr.type == 'function_definition':
132 |                     # Traverse declarator to find name and use its line number
133 |                     decl = curr.child_by_field_name('declarator')
134 |                     while decl:
135 |                         if decl.type == 'identifier':
136 |                              return self._get_node_text(decl), curr.type, decl.start_point[0] + 1
137 |                         
138 |                         # Handle recursive declarators (function, pointer, array, parenthesized)
139 |                         child = decl.child_by_field_name('declarator')
140 |                         if child:
141 |                             decl = child
142 |                         else:
143 |                             # Fallback if structure is different
144 |                             break
145 |                 else:
146 |                     name_node = curr.child_by_field_name('name')
147 |                     if name_node:
148 |                         return self._get_node_text(name_node), curr.type, name_node.start_point[0] + 1
149 |             curr = curr.parent
150 |         return None, None, None
151 | 
152 |     def _calculate_complexity(self, node: Any) -> int:
153 |         """Calculate cyclomatic complexity for C functions."""
154 |         complexity_nodes = {
155 |             "if_statement", "for_statement", "while_statement", "do_statement",
156 |             "switch_statement", "case_statement", "conditional_expression",
157 |             "logical_expression", "binary_expression", "goto_statement"
158 |         }
159 |         count = 1
160 |         
161 |         def traverse(n):
162 |             nonlocal count
163 |             if n.type in complexity_nodes:
164 |                 count += 1
165 |             for child in n.children:
166 |                 traverse(child)
167 |         
168 |         traverse(node)
169 |         return count
170 | 
171 |     def _get_docstring(self, node: Any) -> Optional[str]:
172 |         """Extract comments as documentation."""
173 |         # Look for comments before the node
174 |         if node.parent:
175 |             for child in node.parent.children:
176 |                 if child.type == 'comment' and child.start_point[0] < node.start_point[0]:
177 |                     return self._get_node_text(child)
178 |         return None
179 | 
180 |     def _parse_function_args(self, params_node: Any) -> list[Dict[str, Any]]:
181 |         """Enhanced helper to parse function arguments from a (parameter_list) node."""
182 |         args = []
183 |         if not params_node:
184 |             return args
185 |             
186 |         for param in params_node.named_children:
187 |             if param.type == "parameter_declaration":
188 |                 arg_info: Dict[str, Any] = {"name": "", "type": None, "is_pointer": False, "is_array": False}
189 |                 
190 |                 # Find the declarator (variable name)
191 |                 declarator = param.child_by_field_name("declarator")
192 |                 if declarator:
193 |                     if declarator.type == "identifier":
194 |                         arg_info["name"] = self._get_node_text(declarator)
195 |                     elif declarator.type == "pointer_declarator":
196 |                         arg_info["is_pointer"] = True
197 |                         inner_declarator = declarator.child_by_field_name("declarator")
198 |                         if inner_declarator and inner_declarator.type == "identifier":
199 |                             arg_info["name"] = self._get_node_text(inner_declarator)
200 |                     elif declarator.type == "array_declarator":
201 |                         arg_info["is_array"] = True
202 |                         inner_declarator = declarator.child_by_field_name("declarator")
203 |                         if inner_declarator and inner_declarator.type == "identifier":
204 |                             arg_info["name"] = self._get_node_text(inner_declarator)
205 |                 
206 |                 # Find the type
207 |                 type_node = param.child_by_field_name("type")
208 |                 if type_node:
209 |                     arg_info["type"] = self._get_node_text(type_node)
210 |                 
211 |                 # Handle variadic arguments
212 |                 if param.type == "variadic_parameter":
213 |                     arg_info["name"] = "..."
214 |                     arg_info["type"] = "variadic"
215 |                 
216 |                 args.append(arg_info)
217 |         return args
218 | 
219 |     def _find_functions(self, root_node: Any) -> list[Dict[str, Any]]:
220 |         functions = []
221 |         query_str = C_QUERIES["functions"]
222 |         for match in execute_query(self.language, query_str, root_node):
223 |             capture_name = match[1]
224 |             node = match[0]
225 |             if capture_name == 'name':
226 |                 func_node = node.parent.parent.parent
227 |                 name = self._get_node_text(node)
228 |                 
229 |                 # Find parameters
230 |                 params_node = None
231 |                 body_node = None
232 |                 for child in func_node.children:
233 |                     if child.type == "function_declarator":
234 |                         params_node = child.child_by_field_name("parameters")
235 |                     elif child.type == "compound_statement":
236 |                         body_node = child
237 |                 
238 |                 args = self._parse_function_args(params_node) if params_node else []
239 |                 context, context_type, _ = self._get_parent_context(func_node)
240 | 
241 |                 functions.append({
242 |                     "name": name,
243 |                     "line_number": node.start_point[0] + 1,
244 |                     "end_line": func_node.end_point[0] + 1,
245 |                     "args": [arg["name"] for arg in args if arg["name"]],  # Simplified args for compatibility
246 |                     "source": self._get_node_text(func_node),
247 | 
248 |                     "docstring": self._get_docstring(func_node),
249 |                     "cyclomatic_complexity": self._calculate_complexity(func_node),
250 |                     "context": context,
251 |                     "context_type": context_type,
252 |                     "class_context": None,
253 |                     "decorators": [],
254 |                     "lang": self.language_name,
255 |                     "is_dependency": False,
256 |                     "detailed_args": args,  # Keep detailed args for future use
257 |                 })
258 |         return functions
259 | 
260 |     def _find_structs_unions_enums(self, root_node: Any) -> list[Dict[str, Any]]:
261 |         """Find structs, unions, and enums (treated as classes in C)."""
262 |         classes = []
263 |         
264 |         # Find structs
265 |         query_str = C_QUERIES["structs"]
266 |         for match in execute_query(self.language, query_str, root_node):
267 |             capture_name = match[1]
268 |             node = match[0]
269 |             if capture_name == 'name':
270 |                 struct_node = node.parent
271 |                 name = self._get_node_text(node)
272 |                 context, context_type, _ = self._get_parent_context(struct_node)
273 |                 
274 |                 classes.append({
275 |                     "name": name,
276 |                     "line_number": node.start_point[0] + 1,
277 |                     "end_line": struct_node.end_point[0] + 1,
278 |                     "bases": [],  # C doesn't have inheritance
279 |                     "source": self._get_node_text(struct_node),
280 |                     "docstring": self._get_docstring(struct_node),
281 |                     "context": context,
282 |                     "decorators": [],
283 |                     "lang": self.language_name,
284 |                     "is_dependency": False,
285 |                     "type": "struct",
286 |                 })
287 | 
288 |         # Find unions
289 |         query_str = C_QUERIES["unions"]
290 |         for match in execute_query(self.language, query_str, root_node):
291 |             capture_name = match[1]
292 |             node = match[0]
293 |             if capture_name == 'name':
294 |                 union_node = node.parent
295 |                 name = self._get_node_text(node)
296 |                 context, context_type, _ = self._get_parent_context(union_node)
297 |                 
298 |                 classes.append({
299 |                     "name": name,
300 |                     "line_number": node.start_point[0] + 1,
301 |                     "end_line": union_node.end_point[0] + 1,
302 |                     "bases": [],
303 |                     "source": self._get_node_text(union_node),
304 |                     "docstring": self._get_docstring(union_node),
305 |                     "context": context,
306 |                     "decorators": [],
307 |                     "lang": self.language_name,
308 |                     "is_dependency": False,
309 |                     "type": "union",
310 |                 })
311 | 
312 |         # Find enums
313 |         query_str = C_QUERIES["enums"]
314 |         for match in execute_query(self.language, query_str, root_node):
315 |             capture_name = match[1]
316 |             node = match[0]
317 |             if capture_name == 'name':
318 |                 enum_node = node.parent
319 |                 name = self._get_node_text(node)
320 |                 context, context_type, _ = self._get_parent_context(enum_node)
321 |                 
322 |                 classes.append({
323 |                     "name": name,
324 |                     "line_number": node.start_point[0] + 1,
325 |                     "end_line": enum_node.end_point[0] + 1,
326 |                     "bases": [],
327 |                     "source": self._get_node_text(enum_node),
328 |                     "docstring": self._get_docstring(enum_node),
329 |                     "context": context,
330 |                     "decorators": [],
331 |                     "lang": self.language_name,
332 |                     "is_dependency": False,
333 |                     "type": "enum",
334 |                 })
335 | 
336 |         return classes
337 | 
338 |     def _find_imports(self, root_node: Any) -> list[Dict[str, Any]]:
339 |         imports = []
340 |         query_str = C_QUERIES["imports"]
341 |         for match in execute_query(self.language, query_str, root_node):
342 |             capture_name = match[1]
343 |             node = match[0]
344 |             if capture_name == 'path':
345 |                 path = self._get_node_text(node).strip('"<>')
346 |                 context, context_type, _ = self._get_parent_context(node)
347 |                 
348 |                 imports.append({
349 |                     "name": path,
350 |                     "full_import_name": path,
351 |                     "line_number": node.start_point[0] + 1,
352 |                     "alias": None,
353 |                     "context": context,
354 |                     "lang": self.language_name,
355 |                     "is_dependency": False,
356 |                 })
357 |         return imports
358 | 
359 |     def _find_calls(self, root_node: Any) -> list[Dict[str, Any]]:
360 |         """Enhanced function call detection."""
361 |         calls = []
362 |         query_str = C_QUERIES["calls"]
363 |         for match in execute_query(self.language, query_str, root_node):
364 |             capture_name = match[1]
365 |             node = match[0]
366 |             if capture_name == "name":
367 |                 call_node = node.parent if node.parent.type == "call_expression" else node.parent.parent
368 |                 call_name = self._get_node_text(node)
369 |                 
370 |                 # Extract arguments
371 |                 args = []
372 |                 args_node = call_node.child_by_field_name("arguments")
373 |                 if args_node:
374 |                     for child in args_node.children:
375 |                         if child.type not in ['(', ')', ',']:
376 |                             args.append(self._get_node_text(child))
377 |                 
378 |                 context_name, context_type, context_line = self._get_parent_context(call_node)
379 |                 
380 |                 # print(f"DEBUG_C_PARSER: Call {call_name} context: {context_name}, {context_type}, {context_line}")
381 | 
382 |                 calls.append({
383 |                     "name": call_name,
384 |                     "full_name": call_name,  # For C, function name is the same as full name
385 |                     "line_number": node.start_point[0] + 1,
386 |                     "args": args,
387 |                     "inferred_obj_type": None,
388 |                     "context": (context_name, context_type, context_line),
389 |                     "class_context": None,
390 |                     "lang": self.language_name,
391 |                     "is_dependency": False,
392 |                 })
393 |         return calls
394 | 
395 |     def _find_variables(self, root_node: Any) -> list[Dict[str, Any]]:
396 |         """Enhanced variable declaration detection."""
397 |         variables = []
398 |         query_str = C_QUERIES["variables"]
399 |         for match in execute_query(self.language, query_str, root_node):
400 |             capture_name = match[1]
401 |             node = match[0]
402 |             if capture_name == "name":
403 |                 var_name = self._get_node_text(node)
404 |                 
405 |                 # Find the declaration node
406 |                 decl_node = node.parent
407 |                 while decl_node and decl_node.type != "declaration":
408 |                     decl_node = decl_node.parent
409 |                 
410 |                 # Extract type information
411 |                 var_type = None
412 |                 is_pointer = False
413 |                 is_array = False
414 |                 value = None
415 |                 
416 |                 if decl_node:
417 |                     # Find type
418 |                     for child in decl_node.children:
419 |                         if child.type in ["primitive_type", "type_identifier", "sized_type_specifier"]:
420 |                             var_type = self._get_node_text(child)
421 |                         elif child.type == "init_declarator":
422 |                             # Check for pointer/array
423 |                             if child.child_by_field_name("declarator"):
424 |                                 declarator = child.child_by_field_name("declarator")
425 |                                 if declarator.type == "pointer_declarator":
426 |                                     is_pointer = True
427 |                                 elif declarator.type == "array_declarator":
428 |                                     is_array = True
429 |                             
430 |                             # Check for initial value
431 |                             if child.child_by_field_name("value"):
432 |                                 value = self._get_node_text(child.child_by_field_name("value"))
433 |                 
434 |                 context, context_type, _ = self._get_parent_context(node)
435 |                 class_context, _, _ = self._get_parent_context(node, types=('struct_specifier', 'union_specifier', 'enum_specifier'))
436 |                 
437 |                 variables.append({
438 |                     "name": var_name,
439 |                     "line_number": node.start_point[0] + 1,
440 |                     "value": value,
441 |                     "type": var_type,
442 |                     "context": context,
443 |                     "class_context": class_context,
444 |                     "lang": self.language_name,
445 |                     "is_dependency": False,
446 |                     "is_pointer": is_pointer,
447 |                     "is_array": is_array,
448 |                 })
449 |         return variables
450 | 
451 |     def _find_macros(self, root_node: Any) -> list[Dict[str, Any]]:
452 |         """Enhanced preprocessor macro detection."""
453 |         macros = []
454 |         query_str = C_QUERIES["macros"]
455 |         for match in execute_query(self.language, query_str, root_node):
456 |             capture_name = match[1]
457 |             node = match[0]
458 |             if capture_name == 'name':
459 |                 macro_node = node.parent
460 |                 name = self._get_node_text(node)
461 |                 
462 |                 # Extract macro value
463 |                 value = None
464 |                 if macro_node.child_by_field_name("value"):
465 |                     value = self._get_node_text(macro_node.child_by_field_name("value"))
466 |                 
467 |                 # Extract parameters for function-like macros
468 |                 params = []
469 |                 if macro_node.child_by_field_name("parameters"):
470 |                     params_node = macro_node.child_by_field_name("parameters")
471 |                     for child in params_node.children:
472 |                         if child.type == "identifier":
473 |                             params.append(self._get_node_text(child))
474 |                 
475 |                 context, context_type, _ = self._get_parent_context(macro_node)
476 |                 
477 |                 macros.append({
478 |                     "name": name,
479 |                     "line_number": node.start_point[0] + 1,
480 |                     "end_line": macro_node.end_point[0] + 1,
481 |                     "source": self._get_node_text(macro_node),
482 |                     "value": value,
483 |                     "params": params,
484 |                     "context": context,
485 |                     "lang": self.language_name,
486 |                     "is_dependency": False,
487 |                 })
488 |         return macros
489 | 
490 | 
491 | def pre_scan_c(files: list[Path], parser_wrapper) -> dict:
492 |     """Scans C files to create a map of function/struct/union/enum names to their file paths."""
493 |     imports_map = {}
494 |     query_str = """
495 |         (function_definition
496 |             declarator: (function_declarator
497 |                 declarator: (identifier) @name
498 |             )
499 |         )
500 |         
501 |         (function_definition
502 |             declarator: (function_declarator
503 |                 declarator: (pointer_declarator
504 |                     declarator: (identifier) @name
505 |                 )
506 |             )
507 |         )
508 |         
509 |         (struct_specifier
510 |             name: (type_identifier) @name
511 |         )
512 |         
513 |         (union_specifier
514 |             name: (type_identifier) @name
515 |         )
516 |         
517 |         (enum_specifier
518 |             name: (type_identifier) @name
519 |         )
520 |         
521 |         (type_definition
522 |             declarator: (type_identifier) @name
523 |         )
524 |         
525 |         (preproc_def
526 |             name: (identifier) @name
527 |         )
528 |     """
529 |     
530 |     
531 |     for file_path in files:
532 |         try:
533 |             with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
534 |                 tree = parser_wrapper.parser.parse(bytes(f.read(), "utf8"))
535 |             
536 |             for capture, _ in execute_query(parser_wrapper.language, query_str, tree.root_node):
537 |                 name = capture.text.decode('utf-8')
538 |                 if name not in imports_map:
539 |                     imports_map[name] = []
540 |                 imports_map[name].append(str(file_path.resolve()))
541 |         except Exception as e:
542 |             warning_logger(f"Tree-sitter pre-scan failed for {file_path}: {e}")
543 |     return imports_map
544 | 
```

--------------------------------------------------------------------------------
/src/codegraphcontext/tools/languages/php.py:
--------------------------------------------------------------------------------

```python
  1 | from pathlib import Path
  2 | from typing import Any, Dict, Optional, Tuple
  3 | import re
  4 | from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger
  5 | from codegraphcontext.utils.tree_sitter_manager import execute_query
  6 | 
  7 | # Reference: https://github.com/tree-sitter/tree-sitter-php/blob/master/queries/tags.scm
  8 | PHP_QUERIES = {
  9 |     "functions": """
 10 |         (function_definition
 11 |             name: (name) @name
 12 |             parameters: (formal_parameters) @params
 13 |         ) @function_node
 14 | 
 15 |         (method_declaration
 16 |             name: (name) @name
 17 |             parameters: (formal_parameters) @params
 18 |         ) @function_node
 19 |     """,
 20 |     "classes": """
 21 |         (class_declaration
 22 |             name: (name) @name
 23 |         ) @class
 24 |         
 25 |         (interface_declaration
 26 |             name: (name) @name
 27 |         ) @interface
 28 |         
 29 |         (trait_declaration
 30 |             name: (name) @name
 31 |         ) @trait
 32 |     """,
 33 |     "imports": """
 34 |         (use_declaration) @import
 35 |     """,
 36 |     "calls": """
 37 |         (function_call_expression
 38 |             function: [
 39 |                 (qualified_name) @name
 40 |                 (name) @name
 41 |             ]
 42 |         ) @call_node
 43 |         
 44 |         (member_call_expression
 45 |             name: (name) @name
 46 |         ) @call_node
 47 |         
 48 |         (scoped_call_expression
 49 |             name: (name) @name
 50 |         ) @call_node
 51 |         
 52 |         (object_creation_expression) @call_node
 53 |     """,
 54 |     "variables": """
 55 |         (variable_name) @variable
 56 |     """,
 57 | }
 58 | 
 59 | class PhpTreeSitterParser:
 60 |     def __init__(self, generic_parser_wrapper: Any):
 61 |         self.generic_parser_wrapper = generic_parser_wrapper
 62 |         self.language_name = "php"
 63 |         self.language = generic_parser_wrapper.language
 64 |         self.parser = generic_parser_wrapper.parser
 65 | 
 66 |     def parse(self, file_path: Path, is_dependency: bool = False) -> Dict[str, Any]:
 67 |         try:
 68 |             with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
 69 |                 source_code = f.read()
 70 | 
 71 |             if not source_code.strip():
 72 |                 warning_logger(f"Empty or whitespace-only file: {file_path}")
 73 |                 return {
 74 |                     "file_path": str(file_path),
 75 |                     "functions": [],
 76 |                     "classes": [],
 77 |                     "interfaces": [],
 78 |                     "traits": [],
 79 |                     "variables": [],
 80 |                     "imports": [],
 81 |                     "function_calls": [],
 82 |                     "is_dependency": is_dependency,
 83 |                     "lang": self.language_name,
 84 |                 }
 85 | 
 86 |             tree = self.parser.parse(bytes(source_code, "utf8"))
 87 | 
 88 |             parsed_functions = []
 89 |             parsed_classes = []
 90 |             parsed_interfaces = []
 91 |             parsed_traits = []
 92 |             parsed_variables = []
 93 |             parsed_imports = []
 94 |             parsed_calls = []
 95 | 
 96 |             for capture_name, query in PHP_QUERIES.items():
 97 |                 results = execute_query(self.language, query, tree.root_node)
 98 | 
 99 |                 if capture_name == "functions":
100 |                     parsed_functions = self._parse_functions(results, source_code, file_path)
101 |                 elif capture_name == "classes":
102 |                     # We group classes, interfaces, traits here, but separating them is cleaner
103 |                     # Wait, my query combines them. I should verify results.
104 |                     # execute_query returns (node, capture_name)
105 |                     # I can filter inside _parse_classes
106 |                     parsed_classes, parsed_interfaces, parsed_traits = self._parse_types(results, source_code, file_path)
107 |                 elif capture_name == "imports":
108 |                     parsed_imports = self._parse_imports(results, source_code)
109 |                 elif capture_name == "calls":
110 |                     parsed_calls = self._parse_calls(results, source_code)
111 |                 elif capture_name == "variables":
112 |                     parsed_variables = self._parse_variables(results, source_code, file_path)
113 | 
114 |             return {
115 |                 "file_path": str(file_path),
116 |                 "functions": parsed_functions,
117 |                 "classes": parsed_classes,
118 |                 "interfaces": parsed_interfaces,
119 |                 "traits": parsed_traits,
120 |                 "variables": parsed_variables,
121 |                 "imports": parsed_imports,
122 |                 "function_calls": parsed_calls,
123 |                 "is_dependency": is_dependency,
124 |                 "lang": self.language_name,
125 |             }
126 | 
127 |         except Exception as e:
128 |             error_logger(f"Error parsing PHP file {file_path}: {e}")
129 |             return {
130 |                 "file_path": str(file_path),
131 |                 "functions": [],
132 |                 "classes": [],
133 |                 "interfaces": [],
134 |                 "traits": [],
135 |                 "variables": [],
136 |                 "imports": [],
137 |                 "function_calls": [],
138 |                 "is_dependency": is_dependency,
139 |                 "lang": self.language_name,
140 |             }
141 | 
142 |     def _get_parent_context(self, node: Any) -> Tuple[Optional[str], Optional[str], Optional[int]]:
143 |         curr = node.parent
144 |         while curr:
145 |             if curr.type in ("function_definition", "method_declaration", "class_declaration", "interface_declaration", "trait_declaration"):
146 |                 name_node = curr.child_by_field_name("name")
147 |                 return (
148 |                     self._get_node_text(name_node) if name_node else None,
149 |                     curr.type,
150 |                     curr.start_point[0] + 1,
151 |                 )
152 |             curr = curr.parent
153 |         return None, None, None
154 | 
155 |     def _get_node_text(self, node: Any) -> str:
156 |         if not node: return ""
157 |         return node.text.decode("utf-8")
158 | 
159 |     def _parse_functions(self, captures: list, source_code: str, file_path: Path) -> list[Dict[str, Any]]:
160 |         functions = []
161 |         seen_nodes = set()
162 | 
163 |         for node, capture_name in captures:
164 |             if capture_name == "function_node":
165 |                 node_id = (node.start_byte, node.end_byte, node.type)
166 |                 if node_id in seen_nodes:
167 |                     continue
168 |                 seen_nodes.add(node_id)
169 |                 
170 |                 try:
171 |                     start_line = node.start_point[0] + 1
172 |                     end_line = node.end_point[0] + 1
173 |                     
174 |                     name_node = node.child_by_field_name("name")
175 |                     if name_node:
176 |                         func_name = self._get_node_text(name_node)
177 |                         
178 |                         params_node = node.child_by_field_name("parameters")
179 |                         parameters = []
180 |                         if params_node:
181 |                             # PHP parameters: function($a, $b)
182 |                             for child in params_node.children:
183 |                                 if "variable_name" in child.type or "simple_parameter" in child.type:
184 |                                      # Extract variable name from simple_parameter
185 |                                      var_node = child if "variable_name" in child.type else child.child_by_field_name("name")
186 |                                      if var_node:
187 |                                          parameters.append(self._get_node_text(var_node))
188 | 
189 |                         source_text = self._get_node_text(node)
190 |                         
191 |                         # Get class context
192 |                         context_name, context_type, context_line = self._get_parent_context(node)
193 | 
194 |                         functions.append({
195 |                             "name": func_name,
196 |                             "parameters": parameters,
197 |                             "line_number": start_line,
198 |                             "end_line": end_line,
199 |                             "source": source_text,
200 |                             "file_path": str(file_path),
201 |                             "lang": self.language_name,
202 |                             "context": context_name,
203 |                             "context_type": context_type,
204 |                             "class_context": context_name if context_type and ("class" in context_type or "interface" in context_type or "trait" in context_type) else None
205 |                         })
206 |                         
207 |                 except Exception as e:
208 |                     error_logger(f"Error parsing function in {file_path}: {e}")
209 |                     continue
210 | 
211 |         return functions
212 | 
213 |     def _parse_types(self, captures: list, source_code: str, file_path: Path) -> Tuple[list, list, list]:
214 |         classes = []
215 |         interfaces = []
216 |         traits = []
217 |         seen_nodes = set()
218 | 
219 |         for node, capture_name in captures:
220 |             if capture_name in ("class", "interface", "trait"):
221 |                 node_id = (node.start_byte, node.end_byte, node.type)
222 |                 if node_id in seen_nodes:
223 |                     continue
224 |                 seen_nodes.add(node_id)
225 |                 
226 |                 try:
227 |                     start_line = node.start_point[0] + 1
228 |                     end_line = node.end_point[0] + 1
229 |                     
230 |                     name_node = node.child_by_field_name("name")
231 |                     if name_node:
232 |                         type_name = self._get_node_text(name_node)
233 |                         source_text = self._get_node_text(node)
234 |                         
235 |                         bases = []
236 |                         # Look for extends/implements
237 |                         base_clause_node = node.child_by_field_name('base_clause') # extends
238 |                         interfaces_clause_node = node.child_by_field_name('interfaces_clause') # implements
239 |                         
240 |                         if base_clause_node:
241 |                             # Children of base_clause contain identifiers
242 |                             for child in base_clause_node.children:
243 |                                 if child.type in ('name', 'qualified_name'):
244 |                                     bases.append(self._get_node_text(child))
245 | 
246 |                         if interfaces_clause_node:
247 |                             for child in interfaces_clause_node.children:
248 |                                 if child.type in ('name', 'qualified_name'):
249 |                                     bases.append(self._get_node_text(child))
250 | 
251 |                         type_data = {
252 |                             "name": type_name,
253 |                             "line_number": start_line,
254 |                             "end_line": end_line,
255 |                             "bases": bases,
256 |                             "source": source_text,
257 |                             "file_path": str(file_path),
258 |                             "lang": self.language_name,
259 |                         }
260 |                         
261 |                         if capture_name == "class":
262 |                             classes.append(type_data)
263 |                         elif capture_name == "interface":
264 |                             interfaces.append(type_data)
265 |                         elif capture_name == "trait":
266 |                             traits.append(type_data)
267 |                         
268 |                 except Exception as e:
269 |                     error_logger(f"Error parsing type in {file_path}: {e}")
270 |                     continue
271 | 
272 |         return classes, interfaces, traits
273 | 
274 |     def _parse_variables(self, captures: list, source_code: str, file_path: Path) -> list[Dict[str, Any]]:
275 |         variables = []
276 |         seen_vars = set()
277 |         
278 |         for node, capture_name in captures:
279 |             if capture_name == "variable":
280 |                 try:
281 |                      var_name = self._get_node_text(node)
282 |                      start_line = node.start_point[0] + 1
283 |                      
284 |                      start_byte = node.start_byte
285 |                      if start_byte in seen_vars:
286 |                          continue
287 |                      seen_vars.add(start_byte)
288 |                      
289 |                      ctx_name, ctx_type, ctx_line = self._get_parent_context(node)
290 | 
291 |                      # Infer type from assignment
292 |                      inferred_type = "mixed"
293 |                      parent = node.parent
294 |                      if parent and parent.type == 'assignment_expression':
295 |                          # $var = new Class();
296 |                          left = parent.child_by_field_name('left')
297 |                          right = parent.child_by_field_name('right')
298 |                          
299 |                          # Ensure we are looking at the left side variable
300 |                          if left == node and right and right.type == 'object_creation_expression':
301 |                              # Extract class name from right side
302 |                              for child in right.children:
303 |                                  if child.type in ('name', 'qualified_name'):
304 |                                      inferred_type = self._get_node_text(child)
305 |                                      break
306 |                                      
307 |                      variables.append({
308 |                         "name": var_name,
309 |                         "type": inferred_type,
310 |                         "line_number": start_line,
311 |                         "file_path": str(file_path),
312 |                         "lang": self.language_name,
313 |                         "context": ctx_name,
314 |                         "class_context": ctx_name if ctx_type and ("class" in ctx_type or "interface" in ctx_type or "trait" in ctx_type) else None
315 |                      })
316 |                 except Exception as e:
317 |                     continue
318 | 
319 |         return variables
320 | 
321 |     def _parse_imports(self, captures: list, source_code: str) -> list[dict]:
322 |         imports = []
323 |         
324 |         for node, capture_name in captures:
325 |             if capture_name == "import":
326 |                 try:
327 |                     import_text = self._get_node_text(node)
328 |                     # use Foo\Bar as Baz;
329 |                     # Node usually has children: name (qualified_name), optional alias
330 |                     
331 |                     name_node = None
332 |                     alias_node = None
333 |                     
334 |                     for child in node.children:
335 |                         if child.type == "qualified_name" or child.type == "name":
336 |                             name_node = child
337 |                         # Alias in PHP: use X as Y; The 'as' is usually implicit structure or explicit?
338 |                         # Tree sitter grammar: use_declaration -> use_clause -> (use_as_clause (qualified_name) (name))
339 |                     
340 |                     # Assuming simple handling for now, extracting string from text
341 |                     # Regex might be safer given tree complexity for `use`
342 |                     import_match = re.search(r'use\s+([\w\\]+)(?:\s+as\s+(\w+))?', import_text)
343 |                     if import_match:
344 |                         import_path = import_match.group(1).strip()
345 |                         alias = import_match.group(2).strip() if import_match.group(2) else None
346 |                         
347 |                         import_data = {
348 |                             "name": import_path,
349 |                             "full_import_name": import_text,
350 |                             "line_number": node.start_point[0] + 1,
351 |                             "alias": alias,
352 |                             "context": (None, None),
353 |                             "lang": self.language_name,
354 |                             "is_dependency": False,
355 |                         }
356 |                         imports.append(import_data)
357 |                 except Exception as e:
358 |                     error_logger(f"Error parsing import: {e}")
359 |                     continue
360 | 
361 |         return imports
362 | 
363 |     def _parse_calls(self, captures: list, source_code: str) -> list[dict]:
364 |         calls = []
365 |         seen_calls = set()
366 |         
367 |         for node, capture_name in captures:
368 |             # For object_creation_expression without @name capture on inner node, we catch the whole node as @call_node
369 |             # But the 'calls' query uses @name for function/method calls on the identifier, and @call_node for full expression.
370 |             # My query change: (object_creation_expression) @call_node
371 |             # This means for obj creation, I won't get a separate @name capture. I need to iterate @call_node captures too?
372 |             # actually execute_query returns (node, capture_name).
373 |             
374 |             # Let's handle 'name' capture which gives us the function name
375 |             if capture_name == "name":
376 |                 try:
377 |                     call_name = self._get_node_text(node)
378 |                     line_number = node.start_point[0] + 1
379 |                     
380 |                     # Ensure we identify the full call node
381 |                     call_node = node.parent
382 |                     while call_node and call_node.type not in ("function_call_expression", "member_call_expression", "scoped_call_expression"):
383 |                         call_node = call_node.parent
384 |                     
385 |                     if not call_node:
386 |                          continue # It might be a name inside object creation or something we handle otherwise
387 | 
388 |                     # Avoid duplicates
389 |                     call_key = f"{call_name}_{line_number}"
390 |                     if call_key in seen_calls:
391 |                         continue
392 |                     seen_calls.add(call_key)
393 |                     
394 |                     # Extract arguments
395 |                     args = []
396 |                     args_node = call_node.child_by_field_name('arguments')
397 |                     if args_node:
398 |                         for arg in args_node.children:
399 |                             if arg.type not in ('(', ')', ','):
400 |                                 args.append(self._get_node_text(arg))
401 | 
402 |                     full_name = call_name # Default
403 |                     if call_node.type == 'member_call_expression':
404 |                         # $obj->method()
405 |                         obj_node = call_node.child_by_field_name('object')
406 |                         if obj_node:
407 |                              receiver = self._get_node_text(obj_node)
408 |                              # Normalize -> to . for graph builder compatibility
409 |                              full_name = f"{receiver}.{call_name}"
410 |                     elif call_node.type == 'scoped_call_expression':
411 |                          # Class::method()
412 |                         scope_node = call_node.child_by_field_name('scope')
413 |                         if scope_node:
414 |                             receiver = self._get_node_text(scope_node)
415 |                             # Normalize :: to . for graph builder compatibility
416 |                             full_name = f"{receiver}.{call_name}"
417 | 
418 |                     ctx_name, ctx_type, ctx_line = self._get_parent_context(node)
419 |                     
420 |                     call_data = {
421 |                         "name": call_name,
422 |                         "full_name": full_name,
423 |                         "line_number": line_number,
424 |                         "args": args,
425 |                         "inferred_obj_type": None,
426 |                         "context": (ctx_name, ctx_type, ctx_line),
427 |                         "class_context": (ctx_name, ctx_line) if ctx_type and ("class" in ctx_type or "interface" in ctx_type or "trait" in ctx_type) else (None, None),
428 |                         "lang": self.language_name,
429 |                         "is_dependency": False,
430 |                     }
431 |                     calls.append(call_data)
432 |                 except Exception as e:
433 |                     error_logger(f"Error parsing call: {e}")
434 |                     continue
435 | 
436 |             # Handle object creation separately as capture is on the whole node
437 |             elif capture_name == "call_node" and node.type == "object_creation_expression":
438 |                  try:
439 |                     line_number = node.start_point[0] + 1
440 |                     
441 |                     # Find class name (child not named 'arguments')
442 |                     class_name = "Unknown"
443 |                     for child in node.children:
444 |                         if child.type in ('name', 'qualified_name'):
445 |                             class_name = self._get_node_text(child)
446 |                             break
447 |                         if child.type == "variable_name": # dynamic new $class()
448 |                              class_name = self._get_node_text(child)
449 |                              break
450 |                     
451 |                     call_key = f"new {class_name}_{line_number}"
452 |                     if call_key in seen_calls:
453 |                         continue
454 |                     seen_calls.add(call_key)
455 | 
456 |                     args = []
457 |                     args_node = node.child_by_field_name('arguments')
458 |                     if args_node:
459 |                          for arg in args_node.children:
460 |                             if arg.type not in ('(', ')', ','):
461 |                                 args.append(self._get_node_text(arg))
462 |                     
463 |                     full_name = class_name # For GraphBuilder to link to Class
464 |                     
465 |                     ctx_name, ctx_type, ctx_line = self._get_parent_context(node)
466 |                     
467 |                     call_data = {
468 |                         "name": class_name,
469 |                         "full_name": full_name, # Usually we want the class name here so GB can link to Class node
470 |                         "line_number": line_number,
471 |                         "args": args,
472 |                         "inferred_obj_type": None,
473 |                         "context": (ctx_name, ctx_type, ctx_line),
474 |                         "class_context": (ctx_name, ctx_line) if ctx_type and ("class" in ctx_type or "interface" in ctx_type or "trait" in ctx_type) else (None, None),
475 |                         "lang": self.language_name,
476 |                         "is_dependency": False,
477 |                     }
478 |                     calls.append(call_data)
479 |                  except Exception:
480 |                      continue
481 | 
482 |         return calls
483 | 
484 | def pre_scan_php(files: list[Path], parser_wrapper) -> dict:
485 |     name_to_files = {}
486 |     return name_to_files
487 | 
```

--------------------------------------------------------------------------------
/docs/site/core/index.html:
--------------------------------------------------------------------------------

```html
   1 | 
   2 | <!doctype html>
   3 | <html lang="en" class="no-js">
   4 |   <head>
   5 |     
   6 |       <meta charset="utf-8">
   7 |       <meta name="viewport" content="width=device-width,initial-scale=1">
   8 |       
   9 |       
  10 |       
  11 |       
  12 |         <link rel="prev" href="../server/">
  13 |       
  14 |       
  15 |         <link rel="next" href="../tools/">
  16 |       
  17 |       
  18 |         
  19 |       
  20 |       
  21 |       <link rel="icon" href="../assets/images/favicon.png">
  22 |       <meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
  23 |     
  24 |     
  25 |       
  26 |         <title>Core Concepts - CodeGraphContext</title>
  27 |       
  28 |     
  29 |     
  30 |       <link rel="stylesheet" href="../assets/stylesheets/main.484c7ddc.min.css">
  31 |       
  32 |       
  33 | 
  34 | 
  35 |     
  36 |     
  37 |       
  38 |     
  39 |     
  40 |       
  41 |         
  42 |         
  43 |         <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  44 |         <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
  45 |         <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
  46 |       
  47 |     
  48 |     
  49 |     <script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
  50 |     
  51 |       
  52 | 
  53 |     
  54 |     
  55 |   </head>
  56 |   
  57 |   
  58 |     <body dir="ltr">
  59 |   
  60 |     
  61 |     <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
  62 |     <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
  63 |     <label class="md-overlay" for="__drawer"></label>
  64 |     <div data-md-component="skip">
  65 |       
  66 |         
  67 |         <a href="#core-concepts" class="md-skip">
  68 |           Skip to content
  69 |         </a>
  70 |       
  71 |     </div>
  72 |     <div data-md-component="announce">
  73 |       
  74 |     </div>
  75 |     
  76 |     
  77 |       
  78 | 
  79 |   
  80 | 
  81 | <header class="md-header md-header--shadow" data-md-component="header">
  82 |   <nav class="md-header__inner md-grid" aria-label="Header">
  83 |     <a href=".." title="CodeGraphContext" class="md-header__button md-logo" aria-label="CodeGraphContext" data-md-component="logo">
  84 |       
  85 |   
  86 |   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
  87 | 
  88 |     </a>
  89 |     <label class="md-header__button md-icon" for="__drawer">
  90 |       
  91 |       <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
  92 |     </label>
  93 |     <div class="md-header__title" data-md-component="header-title">
  94 |       <div class="md-header__ellipsis">
  95 |         <div class="md-header__topic">
  96 |           <span class="md-ellipsis">
  97 |             CodeGraphContext
  98 |           </span>
  99 |         </div>
 100 |         <div class="md-header__topic" data-md-component="header-topic">
 101 |           <span class="md-ellipsis">
 102 |             
 103 |               Core Concepts
 104 |             
 105 |           </span>
 106 |         </div>
 107 |       </div>
 108 |     </div>
 109 |     
 110 |     
 111 |       <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
 112 |     
 113 |     
 114 |     
 115 |       
 116 |       
 117 |         <label class="md-header__button md-icon" for="__search">
 118 |           
 119 |           <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
 120 |         </label>
 121 |         <div class="md-search" data-md-component="search" role="dialog">
 122 |   <label class="md-search__overlay" for="__search"></label>
 123 |   <div class="md-search__inner" role="search">
 124 |     <form class="md-search__form" name="search">
 125 |       <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
 126 |       <label class="md-search__icon md-icon" for="__search">
 127 |         
 128 |         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
 129 |         
 130 |         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
 131 |       </label>
 132 |       <nav class="md-search__options" aria-label="Search">
 133 |         
 134 |         <button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
 135 |           
 136 |           <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
 137 |         </button>
 138 |       </nav>
 139 |       
 140 |     </form>
 141 |     <div class="md-search__output">
 142 |       <div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
 143 |         <div class="md-search-result" data-md-component="search-result">
 144 |           <div class="md-search-result__meta">
 145 |             Initializing search
 146 |           </div>
 147 |           <ol class="md-search-result__list" role="presentation"></ol>
 148 |         </div>
 149 |       </div>
 150 |     </div>
 151 |   </div>
 152 | </div>
 153 |       
 154 |     
 155 |     
 156 |   </nav>
 157 |   
 158 | </header>
 159 |     
 160 |     <div class="md-container" data-md-component="container">
 161 |       
 162 |       
 163 |         
 164 |           
 165 |         
 166 |       
 167 |       <main class="md-main" data-md-component="main">
 168 |         <div class="md-main__inner md-grid">
 169 |           
 170 |             
 171 |               
 172 |               <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
 173 |                 <div class="md-sidebar__scrollwrap">
 174 |                   <div class="md-sidebar__inner">
 175 |                     
 176 | 
 177 | 
 178 | 
 179 | <nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
 180 |   <label class="md-nav__title" for="__drawer">
 181 |     <a href=".." title="CodeGraphContext" class="md-nav__button md-logo" aria-label="CodeGraphContext" data-md-component="logo">
 182 |       
 183 |   
 184 |   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
 185 | 
 186 |     </a>
 187 |     CodeGraphContext
 188 |   </label>
 189 |   
 190 |   <ul class="md-nav__list" data-md-scrollfix>
 191 |     
 192 |       
 193 |       
 194 |   
 195 |   
 196 |   
 197 |   
 198 |     <li class="md-nav__item">
 199 |       <a href=".." class="md-nav__link">
 200 |         
 201 |   
 202 |   
 203 |   <span class="md-ellipsis">
 204 |     
 205 |   
 206 |     Home
 207 |   
 208 | 
 209 |     
 210 |   </span>
 211 |   
 212 |   
 213 | 
 214 |       </a>
 215 |     </li>
 216 |   
 217 | 
 218 |     
 219 |       
 220 |       
 221 |   
 222 |   
 223 |   
 224 |   
 225 |     <li class="md-nav__item">
 226 |       <a href="../installation/" class="md-nav__link">
 227 |         
 228 |   
 229 |   
 230 |   <span class="md-ellipsis">
 231 |     
 232 |   
 233 |     Installation
 234 |   
 235 | 
 236 |     
 237 |   </span>
 238 |   
 239 |   
 240 | 
 241 |       </a>
 242 |     </li>
 243 |   
 244 | 
 245 |     
 246 |       
 247 |       
 248 |   
 249 |   
 250 |   
 251 |   
 252 |     <li class="md-nav__item">
 253 |       <a href="../use_cases/" class="md-nav__link">
 254 |         
 255 |   
 256 |   
 257 |   <span class="md-ellipsis">
 258 |     
 259 |   
 260 |     Use Cases
 261 |   
 262 | 
 263 |     
 264 |   </span>
 265 |   
 266 |   
 267 | 
 268 |       </a>
 269 |     </li>
 270 |   
 271 | 
 272 |     
 273 |       
 274 |       
 275 |   
 276 |   
 277 |   
 278 |   
 279 |     <li class="md-nav__item">
 280 |       <a href="../architecture/" class="md-nav__link">
 281 |         
 282 |   
 283 |   
 284 |   <span class="md-ellipsis">
 285 |     
 286 |   
 287 |     Architecture
 288 |   
 289 | 
 290 |     
 291 |   </span>
 292 |   
 293 |   
 294 | 
 295 |       </a>
 296 |     </li>
 297 |   
 298 | 
 299 |     
 300 |       
 301 |       
 302 |   
 303 |   
 304 |   
 305 |   
 306 |     <li class="md-nav__item">
 307 |       <a href="../cli/" class="md-nav__link">
 308 |         
 309 |   
 310 |   
 311 |   <span class="md-ellipsis">
 312 |     
 313 |   
 314 |     CLI Reference
 315 |   
 316 | 
 317 |     
 318 |   </span>
 319 |   
 320 |   
 321 | 
 322 |       </a>
 323 |     </li>
 324 |   
 325 | 
 326 |     
 327 |       
 328 |       
 329 |   
 330 |   
 331 |   
 332 |   
 333 |     <li class="md-nav__item">
 334 |       <a href="../watching/" class="md-nav__link">
 335 |         
 336 |   
 337 |   
 338 |   <span class="md-ellipsis">
 339 |     
 340 |   
 341 |     Live Watching
 342 |   
 343 | 
 344 |     
 345 |   </span>
 346 |   
 347 |   
 348 | 
 349 |       </a>
 350 |     </li>
 351 |   
 352 | 
 353 |     
 354 |       
 355 |       
 356 |   
 357 |   
 358 |   
 359 |   
 360 |     <li class="md-nav__item">
 361 |       <a href="../server/" class="md-nav__link">
 362 |         
 363 |   
 364 |   
 365 |   <span class="md-ellipsis">
 366 |     
 367 |   
 368 |     Server
 369 |   
 370 | 
 371 |     
 372 |   </span>
 373 |   
 374 |   
 375 | 
 376 |       </a>
 377 |     </li>
 378 |   
 379 | 
 380 |     
 381 |       
 382 |       
 383 |   
 384 |   
 385 |     
 386 |   
 387 |   
 388 |   
 389 |     <li class="md-nav__item md-nav__item--active">
 390 |       
 391 |       <input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
 392 |       
 393 |       
 394 |         
 395 |       
 396 |       
 397 |         <label class="md-nav__link md-nav__link--active" for="__toc">
 398 |           
 399 |   
 400 |   
 401 |   <span class="md-ellipsis">
 402 |     
 403 |   
 404 |     Core Concepts
 405 |   
 406 | 
 407 |     
 408 |   </span>
 409 |   
 410 |   
 411 | 
 412 |           <span class="md-nav__icon md-icon"></span>
 413 |         </label>
 414 |       
 415 |       <a href="./" class="md-nav__link md-nav__link--active">
 416 |         
 417 |   
 418 |   
 419 |   <span class="md-ellipsis">
 420 |     
 421 |   
 422 |     Core Concepts
 423 |   
 424 | 
 425 |     
 426 |   </span>
 427 |   
 428 |   
 429 | 
 430 |       </a>
 431 |       
 432 |         
 433 | 
 434 | <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
 435 |   
 436 |   
 437 |   
 438 |     
 439 |   
 440 |   
 441 |     <label class="md-nav__title" for="__toc">
 442 |       <span class="md-nav__icon md-icon"></span>
 443 |       Table of contents
 444 |     </label>
 445 |     <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
 446 |       
 447 |         <li class="md-nav__item">
 448 |   <a href="#databasemanager" class="md-nav__link">
 449 |     <span class="md-ellipsis">
 450 |       
 451 |         DatabaseManager
 452 |       
 453 |     </span>
 454 |   </a>
 455 |   
 456 |     <nav class="md-nav" aria-label="DatabaseManager">
 457 |       <ul class="md-nav__list">
 458 |         
 459 |           <li class="md-nav__item">
 460 |   <a href="#key-methods" class="md-nav__link">
 461 |     <span class="md-ellipsis">
 462 |       
 463 |         Key Methods
 464 |       
 465 |     </span>
 466 |   </a>
 467 |   
 468 | </li>
 469 |         
 470 |       </ul>
 471 |     </nav>
 472 |   
 473 | </li>
 474 |       
 475 |         <li class="md-nav__item">
 476 |   <a href="#jobmanager" class="md-nav__link">
 477 |     <span class="md-ellipsis">
 478 |       
 479 |         JobManager
 480 |       
 481 |     </span>
 482 |   </a>
 483 |   
 484 |     <nav class="md-nav" aria-label="JobManager">
 485 |       <ul class="md-nav__list">
 486 |         
 487 |           <li class="md-nav__item">
 488 |   <a href="#jobstatus" class="md-nav__link">
 489 |     <span class="md-ellipsis">
 490 |       
 491 |         JobStatus
 492 |       
 493 |     </span>
 494 |   </a>
 495 |   
 496 | </li>
 497 |         
 498 |           <li class="md-nav__item">
 499 |   <a href="#jobinfo" class="md-nav__link">
 500 |     <span class="md-ellipsis">
 501 |       
 502 |         JobInfo
 503 |       
 504 |     </span>
 505 |   </a>
 506 |   
 507 | </li>
 508 |         
 509 |           <li class="md-nav__item">
 510 |   <a href="#key-methods_1" class="md-nav__link">
 511 |     <span class="md-ellipsis">
 512 |       
 513 |         Key Methods
 514 |       
 515 |     </span>
 516 |   </a>
 517 |   
 518 | </li>
 519 |         
 520 |       </ul>
 521 |     </nav>
 522 |   
 523 | </li>
 524 |       
 525 |         <li class="md-nav__item">
 526 |   <a href="#codewatcher" class="md-nav__link">
 527 |     <span class="md-ellipsis">
 528 |       
 529 |         CodeWatcher
 530 |       
 531 |     </span>
 532 |   </a>
 533 |   
 534 |     <nav class="md-nav" aria-label="CodeWatcher">
 535 |       <ul class="md-nav__list">
 536 |         
 537 |           <li class="md-nav__item">
 538 |   <a href="#repositoryeventhandler" class="md-nav__link">
 539 |     <span class="md-ellipsis">
 540 |       
 541 |         RepositoryEventHandler
 542 |       
 543 |     </span>
 544 |   </a>
 545 |   
 546 | </li>
 547 |         
 548 |           <li class="md-nav__item">
 549 |   <a href="#key-methods_2" class="md-nav__link">
 550 |     <span class="md-ellipsis">
 551 |       
 552 |         Key Methods
 553 |       
 554 |     </span>
 555 |   </a>
 556 |   
 557 | </li>
 558 |         
 559 |       </ul>
 560 |     </nav>
 561 |   
 562 | </li>
 563 |       
 564 |     </ul>
 565 |   
 566 | </nav>
 567 |       
 568 |     </li>
 569 |   
 570 | 
 571 |     
 572 |       
 573 |       
 574 |   
 575 |   
 576 |   
 577 |   
 578 |     <li class="md-nav__item">
 579 |       <a href="../tools/" class="md-nav__link">
 580 |         
 581 |   
 582 |   
 583 |   <span class="md-ellipsis">
 584 |     
 585 |   
 586 |     Tools
 587 |   
 588 | 
 589 |     
 590 |   </span>
 591 |   
 592 |   
 593 | 
 594 |       </a>
 595 |     </li>
 596 |   
 597 | 
 598 |     
 599 |       
 600 |       
 601 |   
 602 |   
 603 |   
 604 |   
 605 |     <li class="md-nav__item">
 606 |       <a href="../cookbook/" class="md-nav__link">
 607 |         
 608 |   
 609 |   
 610 |   <span class="md-ellipsis">
 611 |     
 612 |   
 613 |     Cookbook
 614 |   
 615 | 
 616 |     
 617 |   </span>
 618 |   
 619 |   
 620 | 
 621 |       </a>
 622 |     </li>
 623 |   
 624 | 
 625 |     
 626 |       
 627 |       
 628 |   
 629 |   
 630 |   
 631 |   
 632 |     
 633 |     
 634 |     
 635 |     
 636 |     
 637 |     <li class="md-nav__item md-nav__item--nested">
 638 |       
 639 |         
 640 |         
 641 |         <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_11" >
 642 |         
 643 |           
 644 |           <label class="md-nav__link" for="__nav_11" id="__nav_11_label" tabindex="0">
 645 |             
 646 |   
 647 |   
 648 |   <span class="md-ellipsis">
 649 |     
 650 |   
 651 |     Contributing
 652 |   
 653 | 
 654 |     
 655 |   </span>
 656 |   
 657 |   
 658 | 
 659 |             <span class="md-nav__icon md-icon"></span>
 660 |           </label>
 661 |         
 662 |         <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_11_label" aria-expanded="false">
 663 |           <label class="md-nav__title" for="__nav_11">
 664 |             <span class="md-nav__icon md-icon"></span>
 665 |             
 666 |   
 667 |     Contributing
 668 |   
 669 | 
 670 |           </label>
 671 |           <ul class="md-nav__list" data-md-scrollfix>
 672 |             
 673 |               
 674 |                 
 675 |   
 676 |   
 677 |   
 678 |   
 679 |     <li class="md-nav__item">
 680 |       <a href="../contributing/" class="md-nav__link">
 681 |         
 682 |   
 683 |   
 684 |   <span class="md-ellipsis">
 685 |     
 686 |   
 687 |     Overview
 688 |   
 689 | 
 690 |     
 691 |   </span>
 692 |   
 693 |   
 694 | 
 695 |       </a>
 696 |     </li>
 697 |   
 698 | 
 699 |               
 700 |             
 701 |               
 702 |                 
 703 |   
 704 |   
 705 |   
 706 |   
 707 |     <li class="md-nav__item">
 708 |       <a href="../contributing_languages/" class="md-nav__link">
 709 |         
 710 |   
 711 |   
 712 |   <span class="md-ellipsis">
 713 |     
 714 |   
 715 |     Adding New Languages
 716 |   
 717 | 
 718 |     
 719 |   </span>
 720 |   
 721 |   
 722 | 
 723 |       </a>
 724 |     </li>
 725 |   
 726 | 
 727 |               
 728 |             
 729 |           </ul>
 730 |         </nav>
 731 |       
 732 |     </li>
 733 |   
 734 | 
 735 |     
 736 |       
 737 |       
 738 |   
 739 |   
 740 |   
 741 |   
 742 |     <li class="md-nav__item">
 743 |       <a href="../troubleshooting/" class="md-nav__link">
 744 |         
 745 |   
 746 |   
 747 |   <span class="md-ellipsis">
 748 |     
 749 |   
 750 |     Troubleshooting
 751 |   
 752 | 
 753 |     
 754 |   </span>
 755 |   
 756 |   
 757 | 
 758 |       </a>
 759 |     </li>
 760 |   
 761 | 
 762 |     
 763 |       
 764 |       
 765 |   
 766 |   
 767 |   
 768 |   
 769 |     <li class="md-nav__item">
 770 |       <a href="../future_work/" class="md-nav__link">
 771 |         
 772 |   
 773 |   
 774 |   <span class="md-ellipsis">
 775 |     
 776 |   
 777 |     Future Work
 778 |   
 779 | 
 780 |     
 781 |   </span>
 782 |   
 783 |   
 784 | 
 785 |       </a>
 786 |     </li>
 787 |   
 788 | 
 789 |     
 790 |       
 791 |       
 792 |   
 793 |   
 794 |   
 795 |   
 796 |     <li class="md-nav__item">
 797 |       <a href="../license/" class="md-nav__link">
 798 |         
 799 |   
 800 |   
 801 |   <span class="md-ellipsis">
 802 |     
 803 |   
 804 |     License
 805 |   
 806 | 
 807 |     
 808 |   </span>
 809 |   
 810 |   
 811 | 
 812 |       </a>
 813 |     </li>
 814 |   
 815 | 
 816 |     
 817 |   </ul>
 818 | </nav>
 819 |                   </div>
 820 |                 </div>
 821 |               </div>
 822 |             
 823 |             
 824 |               
 825 |               <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
 826 |                 <div class="md-sidebar__scrollwrap">
 827 |                   <div class="md-sidebar__inner">
 828 |                     
 829 | 
 830 | <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
 831 |   
 832 |   
 833 |   
 834 |     
 835 |   
 836 |   
 837 |     <label class="md-nav__title" for="__toc">
 838 |       <span class="md-nav__icon md-icon"></span>
 839 |       Table of contents
 840 |     </label>
 841 |     <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
 842 |       
 843 |         <li class="md-nav__item">
 844 |   <a href="#databasemanager" class="md-nav__link">
 845 |     <span class="md-ellipsis">
 846 |       
 847 |         DatabaseManager
 848 |       
 849 |     </span>
 850 |   </a>
 851 |   
 852 |     <nav class="md-nav" aria-label="DatabaseManager">
 853 |       <ul class="md-nav__list">
 854 |         
 855 |           <li class="md-nav__item">
 856 |   <a href="#key-methods" class="md-nav__link">
 857 |     <span class="md-ellipsis">
 858 |       
 859 |         Key Methods
 860 |       
 861 |     </span>
 862 |   </a>
 863 |   
 864 | </li>
 865 |         
 866 |       </ul>
 867 |     </nav>
 868 |   
 869 | </li>
 870 |       
 871 |         <li class="md-nav__item">
 872 |   <a href="#jobmanager" class="md-nav__link">
 873 |     <span class="md-ellipsis">
 874 |       
 875 |         JobManager
 876 |       
 877 |     </span>
 878 |   </a>
 879 |   
 880 |     <nav class="md-nav" aria-label="JobManager">
 881 |       <ul class="md-nav__list">
 882 |         
 883 |           <li class="md-nav__item">
 884 |   <a href="#jobstatus" class="md-nav__link">
 885 |     <span class="md-ellipsis">
 886 |       
 887 |         JobStatus
 888 |       
 889 |     </span>
 890 |   </a>
 891 |   
 892 | </li>
 893 |         
 894 |           <li class="md-nav__item">
 895 |   <a href="#jobinfo" class="md-nav__link">
 896 |     <span class="md-ellipsis">
 897 |       
 898 |         JobInfo
 899 |       
 900 |     </span>
 901 |   </a>
 902 |   
 903 | </li>
 904 |         
 905 |           <li class="md-nav__item">
 906 |   <a href="#key-methods_1" class="md-nav__link">
 907 |     <span class="md-ellipsis">
 908 |       
 909 |         Key Methods
 910 |       
 911 |     </span>
 912 |   </a>
 913 |   
 914 | </li>
 915 |         
 916 |       </ul>
 917 |     </nav>
 918 |   
 919 | </li>
 920 |       
 921 |         <li class="md-nav__item">
 922 |   <a href="#codewatcher" class="md-nav__link">
 923 |     <span class="md-ellipsis">
 924 |       
 925 |         CodeWatcher
 926 |       
 927 |     </span>
 928 |   </a>
 929 |   
 930 |     <nav class="md-nav" aria-label="CodeWatcher">
 931 |       <ul class="md-nav__list">
 932 |         
 933 |           <li class="md-nav__item">
 934 |   <a href="#repositoryeventhandler" class="md-nav__link">
 935 |     <span class="md-ellipsis">
 936 |       
 937 |         RepositoryEventHandler
 938 |       
 939 |     </span>
 940 |   </a>
 941 |   
 942 | </li>
 943 |         
 944 |           <li class="md-nav__item">
 945 |   <a href="#key-methods_2" class="md-nav__link">
 946 |     <span class="md-ellipsis">
 947 |       
 948 |         Key Methods
 949 |       
 950 |     </span>
 951 |   </a>
 952 |   
 953 | </li>
 954 |         
 955 |       </ul>
 956 |     </nav>
 957 |   
 958 | </li>
 959 |       
 960 |     </ul>
 961 |   
 962 | </nav>
 963 |                   </div>
 964 |                 </div>
 965 |               </div>
 966 |             
 967 |           
 968 |           
 969 |             <div class="md-content" data-md-component="content">
 970 |               
 971 |               <article class="md-content__inner md-typeset">
 972 |                 
 973 |                   
 974 | 
 975 | 
 976 | 
 977 | <h1 id="core-concepts">Core Concepts</h1>
 978 | <p>The core of CodeGraphContext is built upon a few key components that manage the database connection, handle background tasks, and watch for file system changes.</p>
 979 | <h2 id="databasemanager"><code>DatabaseManager</code></h2>
 980 | <p>The <code>DatabaseManager</code> class in <code>database.py</code> is a thread-safe singleton responsible for managing the connection to the Neo4j database. This ensures that only one connection pool is created and shared across the application, which is crucial for performance and resource management.</p>
 981 | <h3 id="key-methods">Key Methods</h3>
 982 | <ul>
 983 | <li><code>get_driver()</code>: Returns the active Neo4j Driver instance, creating it if it doesn't exist.</li>
 984 | <li><code>close_driver()</code>: Closes the Neo4j driver connection.</li>
 985 | <li><code>is_connected()</code>: Checks if the database connection is currently active.</li>
 986 | </ul>
 987 | <h2 id="jobmanager"><code>JobManager</code></h2>
 988 | <p>The <code>JobManager</code> class in <code>jobs.py</code> handles long-running, background jobs, such as code indexing. It stores job information in memory and provides a thread-safe way to create, update, and retrieve information about these jobs.</p>
 989 | <h3 id="jobstatus"><code>JobStatus</code></h3>
 990 | <p>An enumeration for the possible statuses of a background job:
 991 | - <code>PENDING</code>
 992 | - <code>RUNNING</code>
 993 | - <code>COMPLETED</code>
 994 | - <code>FAILED</code>
 995 | - <code>CANCELLED</code></p>
 996 | <h3 id="jobinfo"><code>JobInfo</code></h3>
 997 | <p>A data class that holds all information about a single background job, including its ID, status, start/end times, progress, and any errors.</p>
 998 | <h3 id="key-methods_1">Key Methods</h3>
 999 | <ul>
1000 | <li><code>create_job()</code>: Creates a new job with a unique ID.</li>
1001 | <li><code>update_job()</code>: Updates the information for a specific job.</li>
1002 | <li><code>get_job()</code>: Retrieves the information for a single job.</li>
1003 | <li><code>list_jobs()</code>: Returns a list of all jobs.</li>
1004 | <li><code>cleanup_old_jobs()</code>: Removes old, completed jobs from memory.</li>
1005 | </ul>
1006 | <h2 id="codewatcher"><code>CodeWatcher</code></h2>
1007 | <p>The <code>CodeWatcher</code> class in <code>watcher.py</code> implements the live file-watching functionality using the <code>watchdog</code> library. It observes directories for changes and triggers updates to the code graph.</p>
1008 | <h3 id="repositoryeventhandler"><code>RepositoryEventHandler</code></h3>
1009 | <p>A dedicated event handler for a single repository. It performs an initial scan and then uses a debouncing mechanism to efficiently handle file changes, creations, or deletions.</p>
1010 | <h3 id="key-methods_2">Key Methods</h3>
1011 | <ul>
1012 | <li><code>watch_directory()</code>: Schedules a directory to be watched for changes.</li>
1013 | <li><code>unwatch_directory()</code>: Stops watching a directory.</li>
1014 | <li><code>list_watched_paths()</code>: Returns a list of all currently watched directory paths.</li>
1015 | <li><code>start()</code>: Starts the observer thread.</li>
1016 | <li><code>stop()</code>: Stops the observer thread gracefully.</li>
1017 | </ul>
1018 | 
1019 | 
1020 | 
1021 | 
1022 | 
1023 | 
1024 | 
1025 | 
1026 | 
1027 | 
1028 | 
1029 | 
1030 |                 
1031 |               </article>
1032 |             </div>
1033 |           
1034 |           
1035 | <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
1036 |         </div>
1037 |         
1038 |       </main>
1039 |       
1040 |         <footer class="md-footer">
1041 |   
1042 |   <div class="md-footer-meta md-typeset">
1043 |     <div class="md-footer-meta__inner md-grid">
1044 |       <div class="md-copyright">
1045 |   
1046 |   
1047 |     Made with
1048 |     <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
1049 |       Material for MkDocs
1050 |     </a>
1051 |   
1052 | </div>
1053 |       
1054 |     </div>
1055 |   </div>
1056 | </footer>
1057 |       
1058 |     </div>
1059 |     <div class="md-dialog" data-md-component="dialog">
1060 |       <div class="md-dialog__inner md-typeset"></div>
1061 |     </div>
1062 |     
1063 |     
1064 |     
1065 |       
1066 |       
1067 |       <script id="__config" type="application/json">{"annotate": null, "base": "..", "features": [], "search": "../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
1068 |     
1069 |     
1070 |       <script src="../assets/javascripts/bundle.79ae519e.min.js"></script>
1071 |       
1072 |     
1073 |   </body>
1074 | </html>
```

--------------------------------------------------------------------------------
/docs/site/architecture/index.html:
--------------------------------------------------------------------------------

```html
   1 | 
   2 | <!doctype html>
   3 | <html lang="en" class="no-js">
   4 |   <head>
   5 |     
   6 |       <meta charset="utf-8">
   7 |       <meta name="viewport" content="width=device-width,initial-scale=1">
   8 |       
   9 |       
  10 |       
  11 |       
  12 |         <link rel="prev" href="../use_cases/">
  13 |       
  14 |       
  15 |         <link rel="next" href="../cli/">
  16 |       
  17 |       
  18 |         
  19 |       
  20 |       
  21 |       <link rel="icon" href="../assets/images/favicon.png">
  22 |       <meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
  23 |     
  24 |     
  25 |       
  26 |         <title>Architecture - CodeGraphContext</title>
  27 |       
  28 |     
  29 |     
  30 |       <link rel="stylesheet" href="../assets/stylesheets/main.484c7ddc.min.css">
  31 |       
  32 |       
  33 | 
  34 | 
  35 |     
  36 |     
  37 |       
  38 |     
  39 |     
  40 |       
  41 |         
  42 |         
  43 |         <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  44 |         <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
  45 |         <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
  46 |       
  47 |     
  48 |     
  49 |     <script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
  50 |     
  51 |       
  52 | 
  53 |     
  54 |     
  55 |   </head>
  56 |   
  57 |   
  58 |     <body dir="ltr">
  59 |   
  60 |     
  61 |     <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
  62 |     <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
  63 |     <label class="md-overlay" for="__drawer"></label>
  64 |     <div data-md-component="skip">
  65 |       
  66 |         
  67 |         <a href="#architecture-documentation" class="md-skip">
  68 |           Skip to content
  69 |         </a>
  70 |       
  71 |     </div>
  72 |     <div data-md-component="announce">
  73 |       
  74 |     </div>
  75 |     
  76 |     
  77 |       
  78 | 
  79 |   
  80 | 
  81 | <header class="md-header md-header--shadow" data-md-component="header">
  82 |   <nav class="md-header__inner md-grid" aria-label="Header">
  83 |     <a href=".." title="CodeGraphContext" class="md-header__button md-logo" aria-label="CodeGraphContext" data-md-component="logo">
  84 |       
  85 |   
  86 |   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
  87 | 
  88 |     </a>
  89 |     <label class="md-header__button md-icon" for="__drawer">
  90 |       
  91 |       <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
  92 |     </label>
  93 |     <div class="md-header__title" data-md-component="header-title">
  94 |       <div class="md-header__ellipsis">
  95 |         <div class="md-header__topic">
  96 |           <span class="md-ellipsis">
  97 |             CodeGraphContext
  98 |           </span>
  99 |         </div>
 100 |         <div class="md-header__topic" data-md-component="header-topic">
 101 |           <span class="md-ellipsis">
 102 |             
 103 |               Architecture
 104 |             
 105 |           </span>
 106 |         </div>
 107 |       </div>
 108 |     </div>
 109 |     
 110 |     
 111 |       <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
 112 |     
 113 |     
 114 |     
 115 |       
 116 |       
 117 |         <label class="md-header__button md-icon" for="__search">
 118 |           
 119 |           <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
 120 |         </label>
 121 |         <div class="md-search" data-md-component="search" role="dialog">
 122 |   <label class="md-search__overlay" for="__search"></label>
 123 |   <div class="md-search__inner" role="search">
 124 |     <form class="md-search__form" name="search">
 125 |       <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
 126 |       <label class="md-search__icon md-icon" for="__search">
 127 |         
 128 |         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
 129 |         
 130 |         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
 131 |       </label>
 132 |       <nav class="md-search__options" aria-label="Search">
 133 |         
 134 |         <button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
 135 |           
 136 |           <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
 137 |         </button>
 138 |       </nav>
 139 |       
 140 |     </form>
 141 |     <div class="md-search__output">
 142 |       <div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
 143 |         <div class="md-search-result" data-md-component="search-result">
 144 |           <div class="md-search-result__meta">
 145 |             Initializing search
 146 |           </div>
 147 |           <ol class="md-search-result__list" role="presentation"></ol>
 148 |         </div>
 149 |       </div>
 150 |     </div>
 151 |   </div>
 152 | </div>
 153 |       
 154 |     
 155 |     
 156 |   </nav>
 157 |   
 158 | </header>
 159 |     
 160 |     <div class="md-container" data-md-component="container">
 161 |       
 162 |       
 163 |         
 164 |           
 165 |         
 166 |       
 167 |       <main class="md-main" data-md-component="main">
 168 |         <div class="md-main__inner md-grid">
 169 |           
 170 |             
 171 |               
 172 |               <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
 173 |                 <div class="md-sidebar__scrollwrap">
 174 |                   <div class="md-sidebar__inner">
 175 |                     
 176 | 
 177 | 
 178 | 
 179 | <nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
 180 |   <label class="md-nav__title" for="__drawer">
 181 |     <a href=".." title="CodeGraphContext" class="md-nav__button md-logo" aria-label="CodeGraphContext" data-md-component="logo">
 182 |       
 183 |   
 184 |   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
 185 | 
 186 |     </a>
 187 |     CodeGraphContext
 188 |   </label>
 189 |   
 190 |   <ul class="md-nav__list" data-md-scrollfix>
 191 |     
 192 |       
 193 |       
 194 |   
 195 |   
 196 |   
 197 |   
 198 |     <li class="md-nav__item">
 199 |       <a href=".." class="md-nav__link">
 200 |         
 201 |   
 202 |   
 203 |   <span class="md-ellipsis">
 204 |     
 205 |   
 206 |     Home
 207 |   
 208 | 
 209 |     
 210 |   </span>
 211 |   
 212 |   
 213 | 
 214 |       </a>
 215 |     </li>
 216 |   
 217 | 
 218 |     
 219 |       
 220 |       
 221 |   
 222 |   
 223 |   
 224 |   
 225 |     <li class="md-nav__item">
 226 |       <a href="../installation/" class="md-nav__link">
 227 |         
 228 |   
 229 |   
 230 |   <span class="md-ellipsis">
 231 |     
 232 |   
 233 |     Installation
 234 |   
 235 | 
 236 |     
 237 |   </span>
 238 |   
 239 |   
 240 | 
 241 |       </a>
 242 |     </li>
 243 |   
 244 | 
 245 |     
 246 |       
 247 |       
 248 |   
 249 |   
 250 |   
 251 |   
 252 |     <li class="md-nav__item">
 253 |       <a href="../use_cases/" class="md-nav__link">
 254 |         
 255 |   
 256 |   
 257 |   <span class="md-ellipsis">
 258 |     
 259 |   
 260 |     Use Cases
 261 |   
 262 | 
 263 |     
 264 |   </span>
 265 |   
 266 |   
 267 | 
 268 |       </a>
 269 |     </li>
 270 |   
 271 | 
 272 |     
 273 |       
 274 |       
 275 |   
 276 |   
 277 |     
 278 |   
 279 |   
 280 |   
 281 |     <li class="md-nav__item md-nav__item--active">
 282 |       
 283 |       <input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
 284 |       
 285 |       
 286 |         
 287 |       
 288 |       
 289 |         <label class="md-nav__link md-nav__link--active" for="__toc">
 290 |           
 291 |   
 292 |   
 293 |   <span class="md-ellipsis">
 294 |     
 295 |   
 296 |     Architecture
 297 |   
 298 | 
 299 |     
 300 |   </span>
 301 |   
 302 |   
 303 | 
 304 |           <span class="md-nav__icon md-icon"></span>
 305 |         </label>
 306 |       
 307 |       <a href="./" class="md-nav__link md-nav__link--active">
 308 |         
 309 |   
 310 |   
 311 |   <span class="md-ellipsis">
 312 |     
 313 |   
 314 |     Architecture
 315 |   
 316 | 
 317 |     
 318 |   </span>
 319 |   
 320 |   
 321 | 
 322 |       </a>
 323 |       
 324 |         
 325 | 
 326 | <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
 327 |   
 328 |   
 329 |   
 330 |     
 331 |   
 332 |   
 333 |     <label class="md-nav__title" for="__toc">
 334 |       <span class="md-nav__icon md-icon"></span>
 335 |       Table of contents
 336 |     </label>
 337 |     <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
 338 |       
 339 |         <li class="md-nav__item">
 340 |   <a href="#high-level-overview" class="md-nav__link">
 341 |     <span class="md-ellipsis">
 342 |       
 343 |         High-Level Overview
 344 |       
 345 |     </span>
 346 |   </a>
 347 |   
 348 | </li>
 349 |       
 350 |         <li class="md-nav__item">
 351 |   <a href="#backend-architecture" class="md-nav__link">
 352 |     <span class="md-ellipsis">
 353 |       
 354 |         Backend Architecture
 355 |       
 356 |     </span>
 357 |   </a>
 358 |   
 359 |     <nav class="md-nav" aria-label="Backend Architecture">
 360 |       <ul class="md-nav__list">
 361 |         
 362 |           <li class="md-nav__item">
 363 |   <a href="#core-components" class="md-nav__link">
 364 |     <span class="md-ellipsis">
 365 |       
 366 |         Core Components
 367 |       
 368 |     </span>
 369 |   </a>
 370 |   
 371 | </li>
 372 |         
 373 |           <li class="md-nav__item">
 374 |   <a href="#tools" class="md-nav__link">
 375 |     <span class="md-ellipsis">
 376 |       
 377 |         Tools
 378 |       
 379 |     </span>
 380 |   </a>
 381 |   
 382 | </li>
 383 |         
 384 |           <li class="md-nav__item">
 385 |   <a href="#server" class="md-nav__link">
 386 |     <span class="md-ellipsis">
 387 |       
 388 |         Server
 389 |       
 390 |     </span>
 391 |   </a>
 392 |   
 393 | </li>
 394 |         
 395 |           <li class="md-nav__item">
 396 |   <a href="#cli" class="md-nav__link">
 397 |     <span class="md-ellipsis">
 398 |       
 399 |         CLI
 400 |       
 401 |     </span>
 402 |   </a>
 403 |   
 404 | </li>
 405 |         
 406 |       </ul>
 407 |     </nav>
 408 |   
 409 | </li>
 410 |       
 411 |         <li class="md-nav__item">
 412 |   <a href="#frontend-architecture" class="md-nav__link">
 413 |     <span class="md-ellipsis">
 414 |       
 415 |         Frontend Architecture
 416 |       
 417 |     </span>
 418 |   </a>
 419 |   
 420 | </li>
 421 |       
 422 |         <li class="md-nav__item">
 423 |   <a href="#testing" class="md-nav__link">
 424 |     <span class="md-ellipsis">
 425 |       
 426 |         Testing
 427 |       
 428 |     </span>
 429 |   </a>
 430 |   
 431 | </li>
 432 |       
 433 |     </ul>
 434 |   
 435 | </nav>
 436 |       
 437 |     </li>
 438 |   
 439 | 
 440 |     
 441 |       
 442 |       
 443 |   
 444 |   
 445 |   
 446 |   
 447 |     <li class="md-nav__item">
 448 |       <a href="../cli/" class="md-nav__link">
 449 |         
 450 |   
 451 |   
 452 |   <span class="md-ellipsis">
 453 |     
 454 |   
 455 |     CLI Reference
 456 |   
 457 | 
 458 |     
 459 |   </span>
 460 |   
 461 |   
 462 | 
 463 |       </a>
 464 |     </li>
 465 |   
 466 | 
 467 |     
 468 |       
 469 |       
 470 |   
 471 |   
 472 |   
 473 |   
 474 |     <li class="md-nav__item">
 475 |       <a href="../watching/" class="md-nav__link">
 476 |         
 477 |   
 478 |   
 479 |   <span class="md-ellipsis">
 480 |     
 481 |   
 482 |     Live Watching
 483 |   
 484 | 
 485 |     
 486 |   </span>
 487 |   
 488 |   
 489 | 
 490 |       </a>
 491 |     </li>
 492 |   
 493 | 
 494 |     
 495 |       
 496 |       
 497 |   
 498 |   
 499 |   
 500 |   
 501 |     <li class="md-nav__item">
 502 |       <a href="../server/" class="md-nav__link">
 503 |         
 504 |   
 505 |   
 506 |   <span class="md-ellipsis">
 507 |     
 508 |   
 509 |     Server
 510 |   
 511 | 
 512 |     
 513 |   </span>
 514 |   
 515 |   
 516 | 
 517 |       </a>
 518 |     </li>
 519 |   
 520 | 
 521 |     
 522 |       
 523 |       
 524 |   
 525 |   
 526 |   
 527 |   
 528 |     <li class="md-nav__item">
 529 |       <a href="../core/" class="md-nav__link">
 530 |         
 531 |   
 532 |   
 533 |   <span class="md-ellipsis">
 534 |     
 535 |   
 536 |     Core Concepts
 537 |   
 538 | 
 539 |     
 540 |   </span>
 541 |   
 542 |   
 543 | 
 544 |       </a>
 545 |     </li>
 546 |   
 547 | 
 548 |     
 549 |       
 550 |       
 551 |   
 552 |   
 553 |   
 554 |   
 555 |     <li class="md-nav__item">
 556 |       <a href="../tools/" class="md-nav__link">
 557 |         
 558 |   
 559 |   
 560 |   <span class="md-ellipsis">
 561 |     
 562 |   
 563 |     Tools
 564 |   
 565 | 
 566 |     
 567 |   </span>
 568 |   
 569 |   
 570 | 
 571 |       </a>
 572 |     </li>
 573 |   
 574 | 
 575 |     
 576 |       
 577 |       
 578 |   
 579 |   
 580 |   
 581 |   
 582 |     <li class="md-nav__item">
 583 |       <a href="../cookbook/" class="md-nav__link">
 584 |         
 585 |   
 586 |   
 587 |   <span class="md-ellipsis">
 588 |     
 589 |   
 590 |     Cookbook
 591 |   
 592 | 
 593 |     
 594 |   </span>
 595 |   
 596 |   
 597 | 
 598 |       </a>
 599 |     </li>
 600 |   
 601 | 
 602 |     
 603 |       
 604 |       
 605 |   
 606 |   
 607 |   
 608 |   
 609 |     
 610 |     
 611 |     
 612 |     
 613 |     
 614 |     <li class="md-nav__item md-nav__item--nested">
 615 |       
 616 |         
 617 |         
 618 |         <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_11" >
 619 |         
 620 |           
 621 |           <label class="md-nav__link" for="__nav_11" id="__nav_11_label" tabindex="0">
 622 |             
 623 |   
 624 |   
 625 |   <span class="md-ellipsis">
 626 |     
 627 |   
 628 |     Contributing
 629 |   
 630 | 
 631 |     
 632 |   </span>
 633 |   
 634 |   
 635 | 
 636 |             <span class="md-nav__icon md-icon"></span>
 637 |           </label>
 638 |         
 639 |         <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_11_label" aria-expanded="false">
 640 |           <label class="md-nav__title" for="__nav_11">
 641 |             <span class="md-nav__icon md-icon"></span>
 642 |             
 643 |   
 644 |     Contributing
 645 |   
 646 | 
 647 |           </label>
 648 |           <ul class="md-nav__list" data-md-scrollfix>
 649 |             
 650 |               
 651 |                 
 652 |   
 653 |   
 654 |   
 655 |   
 656 |     <li class="md-nav__item">
 657 |       <a href="../contributing/" class="md-nav__link">
 658 |         
 659 |   
 660 |   
 661 |   <span class="md-ellipsis">
 662 |     
 663 |   
 664 |     Overview
 665 |   
 666 | 
 667 |     
 668 |   </span>
 669 |   
 670 |   
 671 | 
 672 |       </a>
 673 |     </li>
 674 |   
 675 | 
 676 |               
 677 |             
 678 |               
 679 |                 
 680 |   
 681 |   
 682 |   
 683 |   
 684 |     <li class="md-nav__item">
 685 |       <a href="../contributing_languages/" class="md-nav__link">
 686 |         
 687 |   
 688 |   
 689 |   <span class="md-ellipsis">
 690 |     
 691 |   
 692 |     Adding New Languages
 693 |   
 694 | 
 695 |     
 696 |   </span>
 697 |   
 698 |   
 699 | 
 700 |       </a>
 701 |     </li>
 702 |   
 703 | 
 704 |               
 705 |             
 706 |           </ul>
 707 |         </nav>
 708 |       
 709 |     </li>
 710 |   
 711 | 
 712 |     
 713 |       
 714 |       
 715 |   
 716 |   
 717 |   
 718 |   
 719 |     <li class="md-nav__item">
 720 |       <a href="../troubleshooting/" class="md-nav__link">
 721 |         
 722 |   
 723 |   
 724 |   <span class="md-ellipsis">
 725 |     
 726 |   
 727 |     Troubleshooting
 728 |   
 729 | 
 730 |     
 731 |   </span>
 732 |   
 733 |   
 734 | 
 735 |       </a>
 736 |     </li>
 737 |   
 738 | 
 739 |     
 740 |       
 741 |       
 742 |   
 743 |   
 744 |   
 745 |   
 746 |     <li class="md-nav__item">
 747 |       <a href="../future_work/" class="md-nav__link">
 748 |         
 749 |   
 750 |   
 751 |   <span class="md-ellipsis">
 752 |     
 753 |   
 754 |     Future Work
 755 |   
 756 | 
 757 |     
 758 |   </span>
 759 |   
 760 |   
 761 | 
 762 |       </a>
 763 |     </li>
 764 |   
 765 | 
 766 |     
 767 |       
 768 |       
 769 |   
 770 |   
 771 |   
 772 |   
 773 |     <li class="md-nav__item">
 774 |       <a href="../license/" class="md-nav__link">
 775 |         
 776 |   
 777 |   
 778 |   <span class="md-ellipsis">
 779 |     
 780 |   
 781 |     License
 782 |   
 783 | 
 784 |     
 785 |   </span>
 786 |   
 787 |   
 788 | 
 789 |       </a>
 790 |     </li>
 791 |   
 792 | 
 793 |     
 794 |   </ul>
 795 | </nav>
 796 |                   </div>
 797 |                 </div>
 798 |               </div>
 799 |             
 800 |             
 801 |               
 802 |               <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
 803 |                 <div class="md-sidebar__scrollwrap">
 804 |                   <div class="md-sidebar__inner">
 805 |                     
 806 | 
 807 | <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
 808 |   
 809 |   
 810 |   
 811 |     
 812 |   
 813 |   
 814 |     <label class="md-nav__title" for="__toc">
 815 |       <span class="md-nav__icon md-icon"></span>
 816 |       Table of contents
 817 |     </label>
 818 |     <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
 819 |       
 820 |         <li class="md-nav__item">
 821 |   <a href="#high-level-overview" class="md-nav__link">
 822 |     <span class="md-ellipsis">
 823 |       
 824 |         High-Level Overview
 825 |       
 826 |     </span>
 827 |   </a>
 828 |   
 829 | </li>
 830 |       
 831 |         <li class="md-nav__item">
 832 |   <a href="#backend-architecture" class="md-nav__link">
 833 |     <span class="md-ellipsis">
 834 |       
 835 |         Backend Architecture
 836 |       
 837 |     </span>
 838 |   </a>
 839 |   
 840 |     <nav class="md-nav" aria-label="Backend Architecture">
 841 |       <ul class="md-nav__list">
 842 |         
 843 |           <li class="md-nav__item">
 844 |   <a href="#core-components" class="md-nav__link">
 845 |     <span class="md-ellipsis">
 846 |       
 847 |         Core Components
 848 |       
 849 |     </span>
 850 |   </a>
 851 |   
 852 | </li>
 853 |         
 854 |           <li class="md-nav__item">
 855 |   <a href="#tools" class="md-nav__link">
 856 |     <span class="md-ellipsis">
 857 |       
 858 |         Tools
 859 |       
 860 |     </span>
 861 |   </a>
 862 |   
 863 | </li>
 864 |         
 865 |           <li class="md-nav__item">
 866 |   <a href="#server" class="md-nav__link">
 867 |     <span class="md-ellipsis">
 868 |       
 869 |         Server
 870 |       
 871 |     </span>
 872 |   </a>
 873 |   
 874 | </li>
 875 |         
 876 |           <li class="md-nav__item">
 877 |   <a href="#cli" class="md-nav__link">
 878 |     <span class="md-ellipsis">
 879 |       
 880 |         CLI
 881 |       
 882 |     </span>
 883 |   </a>
 884 |   
 885 | </li>
 886 |         
 887 |       </ul>
 888 |     </nav>
 889 |   
 890 | </li>
 891 |       
 892 |         <li class="md-nav__item">
 893 |   <a href="#frontend-architecture" class="md-nav__link">
 894 |     <span class="md-ellipsis">
 895 |       
 896 |         Frontend Architecture
 897 |       
 898 |     </span>
 899 |   </a>
 900 |   
 901 | </li>
 902 |       
 903 |         <li class="md-nav__item">
 904 |   <a href="#testing" class="md-nav__link">
 905 |     <span class="md-ellipsis">
 906 |       
 907 |         Testing
 908 |       
 909 |     </span>
 910 |   </a>
 911 |   
 912 | </li>
 913 |       
 914 |     </ul>
 915 |   
 916 | </nav>
 917 |                   </div>
 918 |                 </div>
 919 |               </div>
 920 |             
 921 |           
 922 |           
 923 |             <div class="md-content" data-md-component="content">
 924 |               
 925 |               <article class="md-content__inner md-typeset">
 926 |                 
 927 |                   
 928 | 
 929 | 
 930 | 
 931 | <h1 id="architecture-documentation">Architecture Documentation</h1>
 932 | <p>This document provides a detailed overview of the architecture of the CodeGraphContext project.</p>
 933 | <h2 id="high-level-overview">High-Level Overview</h2>
 934 | <p>The project is a client-server application designed to analyze and visualize codebases. It consists of:</p>
 935 | <ul>
 936 | <li><strong>A Python backend:</strong> This is the core of the application, responsible for parsing and analyzing code, building a graph representation of the codebase, and exposing this data through an API.</li>
 937 | <li><strong>A web-based frontend:</strong> A user interface for interacting with the backend, visualizing the code graph, and exploring the codebase.</li>
 938 | <li><strong>A command-line interface (CLI):</strong> For managing the backend and performing analysis from the terminal.</li>
 939 | </ul>
 940 | <h2 id="backend-architecture">Backend Architecture</h2>
 941 | <p>The backend is a Python application located in the <code>src/codegraphcontext</code> directory.</p>
 942 | <h3 id="core-components">Core Components</h3>
 943 | <p>The <code>src/codegraphcontext/core</code> directory contains the fundamental building blocks of the backend:</p>
 944 | <ul>
 945 | <li><strong>Database:</strong> A graph database is used to store the code graph. This allows for efficient querying of relationships between code elements (e.g., function calls, class inheritance).</li>
 946 | <li><strong>Jobs:</strong> Asynchronous jobs are used for long-running tasks like indexing a new codebase. This prevents the application from becoming unresponsive.</li>
 947 | <li><strong>Watcher:</strong> A file system watcher monitors the codebase for changes and triggers re-indexing, keeping the code graph up-to-date.</li>
 948 | </ul>
 949 | <h3 id="tools">Tools</h3>
 950 | <p>The <code>src/codegraphcontext/tools</code> directory contains the logic for code analysis:</p>
 951 | <ul>
 952 | <li><strong>Graph Builder:</strong> This component is responsible for parsing the code and building the graph representation that is stored in the database.</li>
 953 | <li><strong>Code Finder:</strong> Provides functionality to search for specific code elements within the indexed codebase.</li>
 954 | <li><strong>Import Extractor:</strong> This tool analyzes the import statements in the code to understand dependencies between modules.</li>
 955 | </ul>
 956 | <h3 id="server">Server</h3>
 957 | <p>The <code>src/codegraphcontext/server.py</code> file implements the API server. It exposes the functionality of the backend to the frontend through a JSON-RPC API.</p>
 958 | <h3 id="cli">CLI</h3>
 959 | <p>The <code>src/codegraphcontext/cli</code> directory contains the implementation of the command-line interface. It allows users to:</p>
 960 | <ul>
 961 | <li>Start and stop the backend server.</li>
 962 | <li>Index new projects.</li>
 963 | <li>Run analysis tools from the command line.</li>
 964 | </ul>
 965 | <h2 id="frontend-architecture">Frontend Architecture</h2>
 966 | <p>The frontend is a modern web application located in the <code>website/</code> directory.</p>
 967 | <ul>
 968 | <li><strong>Framework:</strong> It is built using React and TypeScript.</li>
 969 | <li><strong>Build Tool:</strong> Vite is used for fast development and building the application.</li>
 970 | <li><strong>Component-Based:</strong> The UI is organized into reusable components, located in <code>website/src/components</code>. This includes UI elements like buttons and dialogs, as well as higher-level components for different sections of the application.</li>
 971 | <li><strong>Styling:</strong> Tailwind CSS is used for styling the application.</li>
 972 | </ul>
 973 | <h2 id="testing">Testing</h2>
 974 | <p>The <code>tests/</code> directory contains the test suite for the project.</p>
 975 | <ul>
 976 | <li><strong>Integration Tests:</strong> <code>test_cgc_integration.py</code> contains tests that verify the interaction between different components of the backend.</li>
 977 | <li><strong>Unit Tests:</strong> Other files in this directory contain unit tests for specific modules and functions.</li>
 978 | <li><strong>Sample Project:</strong> The <code>tests/sample_project</code> directory contains a variety of Python files used as input for testing the code analysis tools.</li>
 979 | </ul>
 980 | 
 981 | 
 982 | 
 983 | 
 984 | 
 985 | 
 986 | 
 987 | 
 988 | 
 989 | 
 990 | 
 991 | 
 992 |                 
 993 |               </article>
 994 |             </div>
 995 |           
 996 |           
 997 | <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
 998 |         </div>
 999 |         
1000 |       </main>
1001 |       
1002 |         <footer class="md-footer">
1003 |   
1004 |   <div class="md-footer-meta md-typeset">
1005 |     <div class="md-footer-meta__inner md-grid">
1006 |       <div class="md-copyright">
1007 |   
1008 |   
1009 |     Made with
1010 |     <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
1011 |       Material for MkDocs
1012 |     </a>
1013 |   
1014 | </div>
1015 |       
1016 |     </div>
1017 |   </div>
1018 | </footer>
1019 |       
1020 |     </div>
1021 |     <div class="md-dialog" data-md-component="dialog">
1022 |       <div class="md-dialog__inner md-typeset"></div>
1023 |     </div>
1024 |     
1025 |     
1026 |     
1027 |       
1028 |       
1029 |       <script id="__config" type="application/json">{"annotate": null, "base": "..", "features": [], "search": "../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
1030 |     
1031 |     
1032 |       <script src="../assets/javascripts/bundle.79ae519e.min.js"></script>
1033 |       
1034 |     
1035 |   </body>
1036 | </html>
```

--------------------------------------------------------------------------------
/src/codegraphcontext/tools/languages/csharp.py:
--------------------------------------------------------------------------------

```python
  1 | from pathlib import Path
  2 | from typing import Any, Dict, Optional, Tuple
  3 | import re
  4 | from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger
  5 | from codegraphcontext.utils.tree_sitter_manager import execute_query
  6 | 
  7 | CSHARP_QUERIES = {
  8 |     "functions": """
  9 |         (method_declaration
 10 |             name: (identifier) @name
 11 |             parameters: (parameter_list) @params
 12 |         ) @function_node
 13 |         
 14 |         (constructor_declaration
 15 |             name: (identifier) @name
 16 |             parameters: (parameter_list) @params
 17 |         ) @function_node
 18 |         
 19 |         (local_function_statement
 20 |             name: (identifier) @name
 21 |             parameters: (parameter_list) @params
 22 |         ) @function_node
 23 |     """,
 24 |     "classes": """
 25 |         (class_declaration 
 26 |             name: (identifier) @name
 27 |             (base_list)? @bases
 28 |         ) @class
 29 |     """,
 30 |     "interfaces": """
 31 |         (interface_declaration 
 32 |             name: (identifier) @name
 33 |             (base_list)? @bases
 34 |         ) @interface
 35 |     """,
 36 |     "structs": """
 37 |         (struct_declaration 
 38 |             name: (identifier) @name
 39 |             (base_list)? @bases
 40 |         ) @struct
 41 |     """,
 42 |     "enums": """
 43 |         (enum_declaration 
 44 |             name: (identifier) @name
 45 |         ) @enum
 46 |     """,
 47 |     "records": """
 48 |         (record_declaration 
 49 |             name: (identifier) @name
 50 |             (base_list)? @bases
 51 |         ) @record
 52 |     """,
 53 |     "properties": """
 54 |         (property_declaration
 55 |             name: (identifier) @name
 56 |         ) @property
 57 |     """,
 58 |     "imports": """
 59 |         (using_directive) @import
 60 |     """,
 61 |     "calls": """
 62 |         (invocation_expression
 63 |             function: [
 64 |                 (identifier) @name
 65 |                 (member_access_expression
 66 |                     name: (identifier) @name
 67 |                 )
 68 |             ]
 69 |         )
 70 |         
 71 |         (object_creation_expression
 72 |             type: [
 73 |                 (identifier) @name
 74 |                 (qualified_name) @name
 75 |             ]
 76 |         )
 77 |     """,
 78 | }
 79 | 
 80 | class CSharpTreeSitterParser:
 81 |     def __init__(self, generic_parser_wrapper: Any):
 82 |         self.generic_parser_wrapper = generic_parser_wrapper
 83 |         self.language_name = "c_sharp"
 84 |         self.language = generic_parser_wrapper.language
 85 |         self.parser = generic_parser_wrapper.parser
 86 | 
 87 |     def parse(self, file_path: Path, is_dependency: bool = False) -> Dict[str, Any]:
 88 |         try:
 89 |             with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
 90 |                 source_code = f.read()
 91 | 
 92 |             if not source_code.strip():
 93 |                 warning_logger(f"Empty or whitespace-only file: {file_path}")
 94 |                 return {
 95 |                     "file_path": str(file_path),
 96 |                     "functions": [],
 97 |                     "classes": [],
 98 |                     "interfaces": [],
 99 |                     "structs": [],
100 |                     "enums": [],
101 |                     "records": [],
102 |                     "properties": [],
103 |                     "variables": [],
104 |                     "imports": [],
105 |                     "function_calls": [],
106 |                     "is_dependency": is_dependency,
107 |                     "lang": self.language_name,
108 |                 }
109 | 
110 |             tree = self.parser.parse(bytes(source_code, "utf8"))
111 | 
112 |             parsed_functions = []
113 |             parsed_classes = []
114 |             parsed_interfaces = []
115 |             parsed_structs = []
116 |             parsed_enums = []
117 |             parsed_records = []
118 |             parsed_properties = []
119 |             parsed_imports = []
120 |             parsed_calls = []
121 | 
122 |             for capture_name, query_str in CSHARP_QUERIES.items():
123 |                 captures = execute_query(self.language, query_str, tree.root_node)
124 | 
125 |                 if capture_name == "functions":
126 |                     parsed_functions = self._parse_functions(captures, source_code, file_path, tree.root_node)
127 |                 elif capture_name == "classes":
128 |                     parsed_classes = self._parse_type_declarations(captures, source_code, file_path, "Class")
129 |                 elif capture_name == "interfaces":
130 |                     parsed_interfaces = self._parse_type_declarations(captures, source_code, file_path, "Interface")
131 |                 elif capture_name == "structs":
132 |                     parsed_structs = self._parse_type_declarations(captures, source_code, file_path, "Struct")
133 |                 elif capture_name == "enums":
134 |                     parsed_enums = self._parse_type_declarations(captures, source_code, file_path, "Enum")
135 |                 elif capture_name == "records":
136 |                     parsed_records = self._parse_type_declarations(captures, source_code, file_path, "Record")
137 |                 elif capture_name == "properties":
138 |                     parsed_properties = self._parse_properties(captures, source_code, file_path, tree.root_node)
139 |                 elif capture_name == "imports":
140 |                     parsed_imports = self._parse_imports(captures, source_code)
141 |                 elif capture_name == "calls":
142 |                     parsed_calls = self._parse_calls(captures, source_code)
143 | 
144 |             return {
145 |                 "file_path": str(file_path),
146 |                 "functions": parsed_functions,
147 |                 "classes": parsed_classes,
148 |                 "interfaces": parsed_interfaces,
149 |                 "structs": parsed_structs,
150 |                 "enums": parsed_enums,
151 |                 "records": parsed_records,
152 |                 "properties": parsed_properties,
153 |                 "variables": [],
154 |                 "imports": parsed_imports,
155 |                 "function_calls": parsed_calls,
156 |                 "is_dependency": is_dependency,
157 |                 "lang": self.language_name,
158 |             }
159 | 
160 |         except Exception as e:
161 |             error_logger(f"Error parsing C# file {file_path}: {e}")
162 |             return {
163 |                 "file_path": str(file_path),
164 |                 "functions": [],
165 |                 "classes": [],
166 |                 "interfaces": [],
167 |                 "structs": [],
168 |                 "enums": [],
169 |                 "records": [],
170 |                 "properties": [],
171 |                 "variables": [],
172 |                 "imports": [],
173 |                 "function_calls": [],
174 |                 "is_dependency": is_dependency,
175 |                 "lang": self.language_name,
176 |             }
177 | 
178 |     def _parse_functions(self, captures: list, source_code: str, file_path: Path, root_node) -> list[Dict[str, Any]]:
179 |         functions = []
180 |         source_lines = source_code.splitlines()
181 | 
182 |         for node, capture_name in captures:
183 |             if capture_name == "function_node":
184 |                 try:
185 |                     start_line = node.start_point[0] + 1
186 |                     end_line = node.end_point[0] + 1
187 |                     
188 |                     name_captures = [
189 |                         (n, cn) for n, cn in captures 
190 |                         if cn == "name" and n.parent.id == node.id
191 |                     ]
192 |                     
193 |                     if name_captures:
194 |                         name_node = name_captures[0][0]
195 |                         func_name = source_code[name_node.start_byte:name_node.end_byte]
196 |                         
197 |                         params_captures = [
198 |                             (n, cn) for n, cn in captures 
199 |                             if cn == "params" and n.parent.id == node.id
200 |                         ]
201 |                         
202 |                         parameters = []
203 |                         if params_captures:
204 |                             params_node = params_captures[0][0]
205 |                             parameters = self._extract_parameters(params_node)
206 | 
207 |                         # Extract attributes applied to this function
208 |                         attributes = []
209 |                         if node.parent and node.parent.type == "attribute_list":
210 |                             attr_text = source_code[node.parent.start_byte:node.parent.end_byte]
211 |                             attributes.append(attr_text)
212 | 
213 |                         # Find containing class/struct/interface
214 |                         class_context = self._find_containing_type(node, source_code)
215 | 
216 |                         source_text = source_code[node.start_byte:node.end_byte]
217 |                         
218 |                         func_data = {
219 |                             "name": func_name,
220 |                             "args": parameters,
221 |                             "attributes": attributes,
222 |                             "line_number": start_line,
223 |                             "end_line": end_line,
224 |                             "source": source_text,
225 |                             "file_path": str(file_path),
226 |                             "lang": self.language_name,
227 |                         }
228 |                         
229 |                         # Add class context if found
230 |                         if class_context:
231 |                             func_data["class_context"] = class_context
232 |                         
233 |                         functions.append(func_data)
234 |                         
235 |                 except Exception as e:
236 |                     error_logger(f"Error parsing function in {file_path}: {e}")
237 |                     continue
238 | 
239 |         return functions
240 | 
241 |     def _parse_type_declarations(self, captures: list, source_code: str, file_path: Path, type_label: str) -> list[Dict[str, Any]]:
242 |         """Parse class, interface, struct, enum, or record declarations with inheritance info."""
243 |         types = []
244 |         
245 |         # Map capture names based on type
246 |         capture_map = {
247 |             "Class": "class",
248 |             "Interface": "interface",
249 |             "Struct": "struct",
250 |             "Enum": "enum",
251 |             "Record": "record"
252 |         }
253 |         expected_capture = capture_map.get(type_label, "class")
254 | 
255 |         for node, capture_name in captures:
256 |             if capture_name == expected_capture:
257 |                 try:
258 |                     start_line = node.start_point[0] + 1
259 |                     end_line = node.end_point[0] + 1
260 |                     
261 |                     name_captures = [
262 |                         (n, cn) for n, cn in captures 
263 |                         if cn == "name" and n.parent.id == node.id
264 |                     ]
265 |                     
266 |                     if name_captures:
267 |                         name_node = name_captures[0][0]
268 |                         type_name = source_code[name_node.start_byte:name_node.end_byte]
269 |                         
270 |                         # Extract base classes/interfaces
271 |                         bases = []
272 |                         bases_captures = [
273 |                             (n, cn) for n, cn in captures 
274 |                             if cn == "bases" and n.parent.id == node.id
275 |                         ]
276 |                         
277 |                         if bases_captures:
278 |                             bases_node = bases_captures[0][0]
279 |                             bases_text = source_code[bases_node.start_byte:bases_node.end_byte]
280 |                             # Parse base list: ": BaseClass, IInterface1, IInterface2"
281 |                             bases_text = bases_text.strip().lstrip(':').strip()
282 |                             if bases_text:
283 |                                 bases = [b.strip() for b in bases_text.split(',')]
284 |                         
285 |                         source_text = source_code[node.start_byte:node.end_byte]
286 |                         
287 |                         type_data = {
288 |                             "name": type_name,
289 |                             "line_number": start_line,
290 |                             "end_line": end_line,
291 |                             "source": source_text,
292 |                             "file_path": str(file_path),
293 |                             "lang": self.language_name,
294 |                         }
295 |                         
296 |                         # Add bases if found
297 |                         if bases:
298 |                             type_data["bases"] = bases
299 |                         
300 |                         types.append(type_data)
301 |                         
302 |                 except Exception as e:
303 |                     error_logger(f"Error parsing {type_label} in {file_path}: {e}")
304 |                     continue
305 | 
306 |         return types
307 | 
308 |     def _parse_imports(self, captures: list, source_code: str) -> list[dict]:
309 |         imports = []
310 |         
311 |         for node, capture_name in captures:
312 |             if capture_name == "import":
313 |                 try:
314 |                     import_text = source_code[node.start_byte:node.end_byte]
315 |                     # Match: using System.Collections.Generic; or using static System.Math;
316 |                     import_match = re.search(r'using\s+(?:static\s+)?([^;]+)', import_text)
317 |                     if import_match:
318 |                         import_path = import_match.group(1).strip()
319 |                         
320 |                         # Check for alias: using MyAlias = System.Collections.Generic.List<int>;
321 |                         alias = None
322 |                         if '=' in import_path:
323 |                             parts = import_path.split('=')
324 |                             alias = parts[0].strip()
325 |                             import_path = parts[1].strip()
326 |                         
327 |                         import_data = {
328 |                             "name": import_path,
329 |                             "full_import_name": import_path,
330 |                             "line_number": node.start_point[0] + 1,
331 |                             "alias": alias,
332 |                             "context": (None, None),
333 |                             "lang": self.language_name,
334 |                             "is_dependency": False,
335 |                         }
336 |                         imports.append(import_data)
337 |                 except Exception as e:
338 |                     error_logger(f"Error parsing import: {e}")
339 |                     continue
340 | 
341 |         return imports
342 | 
343 |     def _get_parent_context(self, node: Any, types: Tuple[str, ...] = ('class_declaration', 'struct_declaration', 'function_declaration', 'method_declaration')):
344 |         """Find parent context for C# constructs."""
345 |         curr = node.parent
346 |         while curr:
347 |             if curr.type in types:
348 |                 if curr.type in ('method_declaration', 'function_declaration'):
349 |                      name_node = curr.child_by_field_name('name')
350 |                      return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
351 |                 else: 
352 |                      # Classes, structs, etc.
353 |                      name_node = curr.child_by_field_name('name')
354 |                      return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
355 |             curr = curr.parent
356 |         return None, None, None
357 | 
358 |     def _get_node_text(self, node: Any) -> str:
359 |         if not node: return ""
360 |         return node.text.decode("utf-8")
361 | 
362 |     def _parse_calls(self, captures: list, source_code: str) -> list[dict]:
363 |         calls = []
364 |         seen_calls = set()
365 |         
366 |         for node, capture_name in captures:
367 |             if capture_name == "name":
368 |                 try:
369 |                     call_name = source_code[node.start_byte:node.end_byte]
370 |                     line_number = node.start_point[0] + 1
371 |                     
372 |                     # Avoid duplicates
373 |                     call_key = f"{call_name}_{line_number}"
374 |                     if call_key in seen_calls:
375 |                         continue
376 |                     seen_calls.add(call_key)
377 |                     
378 |                     # Get context
379 |                     context_name, context_type, context_line = self._get_parent_context(node)
380 |                     class_context = context_name if context_type and 'class' in context_type else None
381 | 
382 |                     call_data = {
383 |                         "name": call_name,
384 |                         "full_name": call_name,
385 |                         "line_number": line_number,
386 |                         "args": [],
387 |                         "inferred_obj_type": None,
388 |                         "context": (context_name, context_type, context_line),
389 |                         "class_context": class_context,
390 |                         "lang": self.language_name,
391 |                         "is_dependency": False,
392 |                     }
393 |                     calls.append(call_data)
394 |                 except Exception as e:
395 |                     error_logger(f"Error parsing call: {e}")
396 |                     continue
397 | 
398 |         return calls
399 |     
400 | 
401 |     def _extract_parameters(self, params_node) -> list[str]:
402 |         params = []
403 |         if not params_node:
404 |             return params
405 |             
406 |         # Iterate over parameter nodes in the parameter list
407 |         for child in params_node.children:
408 |             if child.type == "parameter":
409 |                 # find the identifier
410 |                 name_node = child.child_by_field_name("name")
411 |                 if name_node:
412 |                      params.append(self._get_node_text(name_node))
413 |                 else:
414 |                     # Fallback: scan children for identifier if field name not present in this grammar version
415 |                     for sub in child.children:
416 |                         if sub.type == "identifier":
417 |                             params.append(self._get_node_text(sub))
418 |                             break
419 |         return params
420 | 
421 |     def _find_containing_type(self, node, source_code):
422 |         """Find the containing class, struct, interface, or record for a given node."""
423 |         current = node.parent
424 |         while current:
425 |             if current.type in ['class_declaration', 'struct_declaration', 'interface_declaration', 'record_declaration']:
426 |                 # Find the name of this type
427 |                 for child in current.children:
428 |                     if child.type == 'identifier':
429 |                         return source_code[child.start_byte:child.end_byte]
430 |             current = current.parent
431 |         return None
432 | 
433 |     def _parse_properties(self, captures: list, source_code: str, file_path: Path, root_node) -> list[Dict[str, Any]]:
434 |         """Parse C# properties."""
435 |         properties = []
436 |         
437 |         for node, capture_name in captures:
438 |             if capture_name == "property":
439 |                 try:
440 |                     start_line = node.start_point[0] + 1
441 |                     end_line = node.end_point[0] + 1
442 |                     
443 |                     name_captures = [
444 |                         (n, cn) for n, cn in captures 
445 |                         if cn == "name" and n.parent == node
446 |                     ]
447 |                     
448 |                     if name_captures:
449 |                         name_node = name_captures[0][0]
450 |                         prop_name = source_code[name_node.start_byte:name_node.end_byte]
451 |                         
452 |                         # Get property type from node children
453 |                         prop_type = None
454 |                         for child in node.children:
455 |                             if child.type in ['predefined_type', 'identifier', 'generic_name', 'nullable_type', 'array_type']:
456 |                                 prop_type = source_code[child.start_byte:child.end_byte]
457 |                                 break
458 |                         
459 |                         
460 |                         # Find containing class/struct
461 |                         class_context = self._find_containing_type(node, source_code)
462 |                         
463 |                         source_text = source_code[node.start_byte:node.end_byte]
464 |                         
465 |                         prop_data = {
466 |                             "name": prop_name,
467 |                             "type": prop_type,
468 |                             "line_number": start_line,
469 |                             "end_line": end_line,
470 |                             "source": source_text,
471 |                             "file_path": str(file_path),
472 |                             "lang": self.language_name,
473 |                         }
474 |                         
475 |                         if class_context:
476 |                             prop_data["class_context"] = class_context
477 |                         
478 |                         properties.append(prop_data)
479 |                         
480 |                 except Exception as e:
481 |                     error_logger(f"Error parsing property in {file_path}: {e}")
482 |                     continue
483 |         
484 |         return properties
485 | 
486 | 
487 | 
488 | def pre_scan_csharp(files: list[Path], parser_wrapper) -> dict:
489 |     """Pre-scan C# files to build a name-to-files mapping."""
490 |     name_to_files = {}
491 |     
492 |     for file_path in files:
493 |         try:
494 |             with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
495 |                 content = f.read()
496 |             
497 |             # Match class declarations
498 |             class_matches = re.finditer(
499 |                 r'\b(?:public\s+|private\s+|protected\s+|internal\s+)?(?:static\s+)?(?:abstract\s+)?(?:sealed\s+)?(?:partial\s+)?class\s+(\w+)',
500 |                 content
501 |             )
502 |             for match in class_matches:
503 |                 class_name = match.group(1)
504 |                 if class_name not in name_to_files:
505 |                     name_to_files[class_name] = []
506 |                 name_to_files[class_name].append(str(file_path))
507 |             
508 |             # Match interface declarations
509 |             interface_matches = re.finditer(
510 |                 r'\b(?:public\s+|private\s+|protected\s+|internal\s+)?(?:partial\s+)?interface\s+(\w+)',
511 |                 content
512 |             )
513 |             for match in interface_matches:
514 |                 interface_name = match.group(1)
515 |                 if interface_name not in name_to_files:
516 |                     name_to_files[interface_name] = []
517 |                 name_to_files[interface_name].append(str(file_path))
518 |             
519 |             # Match struct declarations
520 |             struct_matches = re.finditer(
521 |                 r'\b(?:public\s+|private\s+|protected\s+|internal\s+)?(?:readonly\s+)?(?:partial\s+)?struct\s+(\w+)',
522 |                 content
523 |             )
524 |             for match in struct_matches:
525 |                 struct_name = match.group(1)
526 |                 if struct_name not in name_to_files:
527 |                     name_to_files[struct_name] = []
528 |                 name_to_files[struct_name].append(str(file_path))
529 |             
530 |             # Match record declarations
531 |             record_matches = re.finditer(
532 |                 r'\b(?:public\s+|private\s+|protected\s+|internal\s+)?(?:sealed\s+)?record\s+(?:class\s+)?(\w+)',
533 |                 content
534 |             )
535 |             for match in record_matches:
536 |                 record_name = match.group(1)
537 |                 if record_name not in name_to_files:
538 |                     name_to_files[record_name] = []
539 |                 name_to_files[record_name].append(str(file_path))
540 |                 
541 |         except Exception as e:
542 |             error_logger(f"Error pre-scanning C# file {file_path}: {e}")
543 |             
544 |     return name_to_files
545 | 
```
Page 12/23FirstPrevNextLast