This is page 9 of 18. Use http://codebase.md/shashankss1205/codegraphcontext?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .github
│ └── workflows
│ ├── e2e-tests.yml
│ ├── post_discord_invite.yml
│ ├── test.yml
│ └── update-contributors.yml
├── .gitignore
├── CONTRIBUTING.md
├── contributors.md
├── docs
│ ├── docs
│ │ ├── architecture.md
│ │ ├── cli.md
│ │ ├── contributing_languages.md
│ │ ├── contributing.md
│ │ ├── cookbook.md
│ │ ├── core.md
│ │ ├── future_work.md
│ │ ├── images
│ │ │ ├── 1.png
│ │ │ ├── 11.png
│ │ │ ├── 12.png
│ │ │ ├── 13.png
│ │ │ ├── 14.png
│ │ │ ├── 16.png
│ │ │ ├── 19.png
│ │ │ ├── 2.png
│ │ │ ├── 20.png
│ │ │ ├── 21.png
│ │ │ ├── 22.png
│ │ │ ├── 23.png
│ │ │ ├── 24.png
│ │ │ ├── 26.png
│ │ │ ├── 28.png
│ │ │ ├── 29.png
│ │ │ ├── 3.png
│ │ │ ├── 30.png
│ │ │ ├── 31.png
│ │ │ ├── 32.png
│ │ │ ├── 33.png
│ │ │ ├── 34.png
│ │ │ ├── 35.png
│ │ │ ├── 36.png
│ │ │ ├── 38.png
│ │ │ ├── 39.png
│ │ │ ├── 4.png
│ │ │ ├── 40.png
│ │ │ ├── 41.png
│ │ │ ├── 42.png
│ │ │ ├── 43.png
│ │ │ ├── 44.png
│ │ │ ├── 5.png
│ │ │ ├── 6.png
│ │ │ ├── 7.png
│ │ │ ├── 8.png
│ │ │ ├── 9.png
│ │ │ ├── Indexing.gif
│ │ │ ├── tool_images
│ │ │ │ ├── 1.png
│ │ │ │ ├── 2.png
│ │ │ │ └── 3.png
│ │ │ └── Usecase.gif
│ │ ├── index.md
│ │ ├── installation.md
│ │ ├── license.md
│ │ ├── server.md
│ │ ├── tools.md
│ │ ├── troubleshooting.md
│ │ └── use_cases.md
│ ├── mkdocs.yml
│ └── site
│ ├── 404.html
│ ├── architecture
│ │ └── index.html
│ ├── assets
│ │ ├── images
│ │ │ └── favicon.png
│ │ ├── javascripts
│ │ │ ├── bundle.f55a23d4.min.js
│ │ │ ├── bundle.f55a23d4.min.js.map
│ │ │ ├── lunr
│ │ │ │ ├── min
│ │ │ │ │ ├── lunr.ar.min.js
│ │ │ │ │ ├── lunr.da.min.js
│ │ │ │ │ ├── lunr.de.min.js
│ │ │ │ │ ├── lunr.du.min.js
│ │ │ │ │ ├── lunr.el.min.js
│ │ │ │ │ ├── lunr.es.min.js
│ │ │ │ │ ├── lunr.fi.min.js
│ │ │ │ │ ├── lunr.fr.min.js
│ │ │ │ │ ├── lunr.he.min.js
│ │ │ │ │ ├── lunr.hi.min.js
│ │ │ │ │ ├── lunr.hu.min.js
│ │ │ │ │ ├── lunr.hy.min.js
│ │ │ │ │ ├── lunr.it.min.js
│ │ │ │ │ ├── lunr.ja.min.js
│ │ │ │ │ ├── lunr.jp.min.js
│ │ │ │ │ ├── lunr.kn.min.js
│ │ │ │ │ ├── lunr.ko.min.js
│ │ │ │ │ ├── lunr.multi.min.js
│ │ │ │ │ ├── lunr.nl.min.js
│ │ │ │ │ ├── lunr.no.min.js
│ │ │ │ │ ├── lunr.pt.min.js
│ │ │ │ │ ├── lunr.ro.min.js
│ │ │ │ │ ├── lunr.ru.min.js
│ │ │ │ │ ├── lunr.sa.min.js
│ │ │ │ │ ├── lunr.stemmer.support.min.js
│ │ │ │ │ ├── lunr.sv.min.js
│ │ │ │ │ ├── lunr.ta.min.js
│ │ │ │ │ ├── lunr.te.min.js
│ │ │ │ │ ├── lunr.th.min.js
│ │ │ │ │ ├── lunr.tr.min.js
│ │ │ │ │ ├── lunr.vi.min.js
│ │ │ │ │ └── lunr.zh.min.js
│ │ │ │ ├── tinyseg.js
│ │ │ │ └── wordcut.js
│ │ │ └── workers
│ │ │ ├── search.973d3a69.min.js
│ │ │ └── search.973d3a69.min.js.map
│ │ └── stylesheets
│ │ ├── main.2a3383ac.min.css
│ │ ├── main.2a3383ac.min.css.map
│ │ ├── palette.06af60db.min.css
│ │ └── palette.06af60db.min.css.map
│ ├── cli
│ │ └── index.html
│ ├── contributing
│ │ └── index.html
│ ├── contributing_languages
│ │ └── index.html
│ ├── cookbook
│ │ └── index.html
│ ├── core
│ │ └── index.html
│ ├── future_work
│ │ └── index.html
│ ├── images
│ │ ├── 1.png
│ │ ├── 11.png
│ │ ├── 12.png
│ │ ├── 13.png
│ │ ├── 14.png
│ │ ├── 16.png
│ │ ├── 19.png
│ │ ├── 2.png
│ │ ├── 20.png
│ │ ├── 21.png
│ │ ├── 22.png
│ │ ├── 23.png
│ │ ├── 24.png
│ │ ├── 26.png
│ │ ├── 28.png
│ │ ├── 29.png
│ │ ├── 3.png
│ │ ├── 30.png
│ │ ├── 31.png
│ │ ├── 32.png
│ │ ├── 33.png
│ │ ├── 34.png
│ │ ├── 35.png
│ │ ├── 36.png
│ │ ├── 38.png
│ │ ├── 39.png
│ │ ├── 4.png
│ │ ├── 40.png
│ │ ├── 41.png
│ │ ├── 42.png
│ │ ├── 43.png
│ │ ├── 44.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ ├── 7.png
│ │ ├── 8.png
│ │ ├── 9.png
│ │ ├── Indexing.gif
│ │ ├── tool_images
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ └── 3.png
│ │ └── Usecase.gif
│ ├── index.html
│ ├── installation
│ │ └── index.html
│ ├── license
│ │ └── index.html
│ ├── search
│ │ └── search_index.json
│ ├── server
│ │ └── index.html
│ ├── sitemap.xml
│ ├── sitemap.xml.gz
│ ├── tools
│ │ └── index.html
│ ├── troubleshooting
│ │ └── index.html
│ └── use_cases
│ └── index.html
├── images
│ ├── 1.png
│ ├── 11.png
│ ├── 12.png
│ ├── 13.png
│ ├── 14.png
│ ├── 16.png
│ ├── 19.png
│ ├── 2.png
│ ├── 20.png
│ ├── 21.png
│ ├── 22.png
│ ├── 23.png
│ ├── 24.png
│ ├── 26.png
│ ├── 28.png
│ ├── 29.png
│ ├── 3.png
│ ├── 30.png
│ ├── 31.png
│ ├── 32.png
│ ├── 33.png
│ ├── 34.png
│ ├── 35.png
│ ├── 36.png
│ ├── 38.png
│ ├── 39.png
│ ├── 4.png
│ ├── 40.png
│ ├── 41.png
│ ├── 42.png
│ ├── 43.png
│ ├── 44.png
│ ├── 5.png
│ ├── 6.png
│ ├── 7.png
│ ├── 8.png
│ ├── 9.png
│ ├── Indexing.gif
│ ├── tool_images
│ │ ├── 1.png
│ │ ├── 2.png
│ │ └── 3.png
│ └── Usecase.gif
├── LICENSE
├── MANIFEST.in
├── organizer
│ ├── CONTRIBUTING_LANGUAGES.md
│ ├── cookbook.md
│ ├── docs.md
│ ├── language_specific_nodes.md
│ ├── Tools_Exploration.md
│ └── troubleshoot.md
├── package-lock.json
├── pyproject.toml
├── README.md
├── scripts
│ ├── generate_lang_contributors.py
│ └── post_install_fix.sh
├── SECURITY.md
├── src
│ └── codegraphcontext
│ ├── __init__.py
│ ├── __main__.py
│ ├── cli
│ │ ├── __init__.py
│ │ ├── cli_helpers.py
│ │ ├── main.py
│ │ ├── setup_macos.py
│ │ └── setup_wizard.py
│ ├── core
│ │ ├── __init__.py
│ │ ├── database.py
│ │ ├── jobs.py
│ │ └── watcher.py
│ ├── prompts.py
│ ├── server.py
│ ├── tools
│ │ ├── __init__.py
│ │ ├── advanced_language_query_tool.py
│ │ ├── code_finder.py
│ │ ├── graph_builder.py
│ │ ├── languages
│ │ │ ├── c.py
│ │ │ ├── cpp.py
│ │ │ ├── go.py
│ │ │ ├── java.py
│ │ │ ├── javascript.py
│ │ │ ├── python.py
│ │ │ ├── ruby.py
│ │ │ ├── rust.py
│ │ │ └── typescript.py
│ │ ├── package_resolver.py
│ │ ├── query_tool_languages
│ │ │ ├── c_toolkit.py
│ │ │ ├── cpp_toolkit.py
│ │ │ ├── go_toolkit.py
│ │ │ ├── java_toolkit.py
│ │ │ ├── javascript_toolkit.py
│ │ │ ├── python_toolkit.py
│ │ │ ├── ruby_toolkit.py
│ │ │ ├── rust_toolkit.py
│ │ │ └── typescript_toolkit.py
│ │ └── system.py
│ └── utils
│ └── debug_log.py
├── tests
│ ├── __init__.py
│ ├── conftest.py
│ ├── sample_project
│ │ ├── advanced_calls.py
│ │ ├── advanced_classes.py
│ │ ├── advanced_classes2.py
│ │ ├── advanced_functions.py
│ │ ├── advanced_imports.py
│ │ ├── async_features.py
│ │ ├── callbacks_decorators.py
│ │ ├── circular1.py
│ │ ├── circular2.py
│ │ ├── class_instantiation.py
│ │ ├── cli_and_dunder.py
│ │ ├── complex_classes.py
│ │ ├── comprehensions_generators.py
│ │ ├── context_managers.py
│ │ ├── control_flow.py
│ │ ├── datatypes.py
│ │ ├── dynamic_dispatch.py
│ │ ├── dynamic_imports.py
│ │ ├── edge_cases
│ │ │ ├── comments_only.py
│ │ │ ├── docstring_only.py
│ │ │ ├── empty.py
│ │ │ ├── hardcoded_secrets.py
│ │ │ ├── long_functions.py
│ │ │ └── syntax_error.py
│ │ ├── function_chains.py
│ │ ├── generators.py
│ │ ├── import_reexports.py
│ │ ├── mapping_calls.py
│ │ ├── module_a.py
│ │ ├── module_b.py
│ │ ├── module_c
│ │ │ ├── __init__.py
│ │ │ ├── submodule1.py
│ │ │ └── submodule2.py
│ │ ├── namespace_pkg
│ │ │ └── ns_module.py
│ │ ├── pattern_matching.py
│ │ └── typing_examples.py
│ ├── sample_project_c
│ │ ├── cgc_sample
│ │ ├── include
│ │ │ ├── config.h
│ │ │ ├── math
│ │ │ │ └── vec.h
│ │ │ ├── module.h
│ │ │ ├── platform.h
│ │ │ └── util.h
│ │ ├── Makefile
│ │ ├── README.md
│ │ └── src
│ │ ├── main.c
│ │ ├── math
│ │ │ └── vec.c
│ │ ├── module.c
│ │ └── util.c
│ ├── sample_project_cpp
│ │ ├── class_features.cpp
│ │ ├── classes.cpp
│ │ ├── control_flow.cpp
│ │ ├── edge_cases.cpp
│ │ ├── enum_struct_union.cpp
│ │ ├── exceptions.cpp
│ │ ├── file_io.cpp
│ │ ├── function_chain.cpp
│ │ ├── function_chain.h
│ │ ├── function_types.cpp
│ │ ├── main.cpp
│ │ ├── main.exe
│ │ ├── namespaces.cpp
│ │ ├── raii_example.cpp
│ │ ├── README.md
│ │ ├── sample_project.exe
│ │ ├── stl_usage.cpp
│ │ ├── templates.cpp
│ │ └── types_variable_assignments.cpp
│ ├── sample_project_go
│ │ ├── advanced_types.go
│ │ ├── basic_functions.go
│ │ ├── embedded_composition.go
│ │ ├── error_handling.go
│ │ ├── generics.go
│ │ ├── go.mod
│ │ ├── goroutines_channels.go
│ │ ├── interfaces.go
│ │ ├── packages_imports.go
│ │ ├── README.md
│ │ ├── structs_methods.go
│ │ └── util
│ │ └── helpers.go
│ ├── sample_project_java
│ │ ├── out
│ │ │ └── com
│ │ │ └── example
│ │ │ └── app
│ │ │ ├── annotations
│ │ │ │ └── Logged.class
│ │ │ ├── Main.class
│ │ │ ├── misc
│ │ │ │ ├── Outer.class
│ │ │ │ └── Outer$Inner.class
│ │ │ ├── model
│ │ │ │ ├── Role.class
│ │ │ │ └── User.class
│ │ │ ├── service
│ │ │ │ ├── AbstractGreeter.class
│ │ │ │ ├── GreetingService.class
│ │ │ │ └── impl
│ │ │ │ └── GreetingServiceImpl.class
│ │ │ └── util
│ │ │ ├── CollectionUtils.class
│ │ │ └── IOHelper.class
│ │ ├── README.md
│ │ ├── sources.txt
│ │ └── src
│ │ └── com
│ │ └── example
│ │ └── app
│ │ ├── annotations
│ │ │ └── Logged.java
│ │ ├── Main.java
│ │ ├── misc
│ │ │ └── Outer.java
│ │ ├── model
│ │ │ ├── Role.java
│ │ │ └── User.java
│ │ ├── service
│ │ │ ├── AbstractGreeter.java
│ │ │ ├── GreetingService.java
│ │ │ └── impl
│ │ │ └── GreetingServiceImpl.java
│ │ └── util
│ │ ├── CollectionUtils.java
│ │ └── IOHelper.java
│ ├── sample_project_javascript
│ │ ├── arrays.js
│ │ ├── asyncAwait.js
│ │ ├── classes.js
│ │ ├── dom.js
│ │ ├── errorHandling.js
│ │ ├── events.js
│ │ ├── exporter.js
│ │ ├── fetchAPI.js
│ │ ├── fixtures
│ │ │ └── js
│ │ │ └── accessors.js
│ │ ├── functions.js
│ │ ├── importer.js
│ │ ├── objects.js
│ │ ├── promises.js
│ │ ├── README.md
│ │ └── variables.js
│ ├── sample_project_misc
│ │ ├── index.html
│ │ ├── README.md
│ │ ├── styles.css
│ │ ├── tables.css
│ │ └── tables.html
│ ├── sample_project_php
│ │ ├── classes_objects.php
│ │ ├── database.php
│ │ ├── edgecases.php
│ │ ├── error_handling.php
│ │ ├── file_handling.php
│ │ ├── functions.php
│ │ ├── generators_iterators.php
│ │ ├── globals_superglobals.php
│ │ ├── Inheritance.php
│ │ ├── interface_traits.php
│ │ └── README.md
│ ├── sample_project_ruby
│ │ ├── class_example.rb
│ │ ├── enumerables.rb
│ │ ├── error_handling.rb
│ │ ├── file_io.rb
│ │ ├── inheritance_example.rb
│ │ ├── main.rb
│ │ ├── metaprogramming.rb
│ │ ├── mixins_example.rb
│ │ ├── module_example.rb
│ │ └── tests
│ │ ├── test_mixins.py
│ │ └── test_sample.rb
│ ├── sample_project_rust
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src
│ │ ├── basic_functions.rs
│ │ ├── concurrency.rs
│ │ ├── error_handling.rs
│ │ ├── generics.rs
│ │ ├── iterators_closures.rs
│ │ ├── lib.rs
│ │ ├── lifetimes_references.rs
│ │ ├── modules.rs
│ │ ├── smart_pointers.rs
│ │ ├── structs_enums.rs
│ │ └── traits.rs
│ ├── sample_project_typescript
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── advanced-types.ts
│ │ │ ├── async-promises.ts
│ │ │ ├── classes-inheritance.ts
│ │ │ ├── decorators-metadata.ts
│ │ │ ├── error-validation.ts
│ │ │ ├── functions-generics.ts
│ │ │ ├── index.ts
│ │ │ ├── modules-namespaces.ts
│ │ │ ├── types-interfaces.ts
│ │ │ └── utilities-helpers.ts
│ │ └── tsconfig.json
│ ├── test_cpp_parser.py
│ ├── test_database_validation.py
│ ├── test_end_to_end.py
│ ├── test_graph_indexing_js.py
│ ├── test_graph_indexing.py
│ ├── test_tree_sitter
│ │ ├── __init__.py
│ │ ├── class_instantiation.py
│ │ ├── complex_classes.py
│ │ └── test_file.py
│ └── test_typescript_parser.py
└── website
├── .example.env
├── .gitignore
├── api
│ └── pypi.ts
├── bun.lockb
├── components.json
├── eslint.config.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ ├── favicon.ico
│ ├── placeholder.svg
│ └── robots.txt
├── README.md
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── assets
│ │ ├── function-calls.png
│ │ ├── graph-total.png
│ │ ├── hero-graph.jpg
│ │ └── hierarchy.png
│ ├── components
│ │ ├── ComparisonTable.tsx
│ │ ├── CookbookSection.tsx
│ │ ├── DemoSection.tsx
│ │ ├── ExamplesSection.tsx
│ │ ├── FeaturesSection.tsx
│ │ ├── Footer.tsx
│ │ ├── HeroSection.tsx
│ │ ├── InstallationSection.tsx
│ │ ├── MoveToTop.tsx
│ │ ├── ShowDownloads.tsx
│ │ ├── ShowStarGraph.tsx
│ │ ├── TestimonialSection.tsx
│ │ ├── ThemeProvider.tsx
│ │ ├── ThemeToggle.tsx
│ │ └── ui
│ │ ├── accordion.tsx
│ │ ├── alert-dialog.tsx
│ │ ├── alert.tsx
│ │ ├── aspect-ratio.tsx
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── breadcrumb.tsx
│ │ ├── button.tsx
│ │ ├── calendar.tsx
│ │ ├── card.tsx
│ │ ├── carousel.tsx
│ │ ├── chart.tsx
│ │ ├── checkbox.tsx
│ │ ├── collapsible.tsx
│ │ ├── command.tsx
│ │ ├── context-menu.tsx
│ │ ├── dialog.tsx
│ │ ├── drawer.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── form.tsx
│ │ ├── hover-card.tsx
│ │ ├── input-otp.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── menubar.tsx
│ │ ├── navigation-menu.tsx
│ │ ├── orbiting-circles.tsx
│ │ ├── pagination.tsx
│ │ ├── popover.tsx
│ │ ├── progress.tsx
│ │ ├── radio-group.tsx
│ │ ├── resizable.tsx
│ │ ├── scroll-area.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── sidebar.tsx
│ │ ├── skeleton.tsx
│ │ ├── slider.tsx
│ │ ├── sonner.tsx
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── textarea.tsx
│ │ ├── toast.tsx
│ │ ├── toaster.tsx
│ │ ├── toggle-group.tsx
│ │ ├── toggle.tsx
│ │ ├── tooltip.tsx
│ │ └── use-toast.ts
│ ├── hooks
│ │ ├── use-mobile.tsx
│ │ └── use-toast.ts
│ ├── index.css
│ ├── lib
│ │ └── utils.ts
│ ├── main.tsx
│ ├── pages
│ │ ├── Index.tsx
│ │ └── NotFound.tsx
│ └── vite-env.d.ts
├── tailwind.config.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
├── vercel.json
└── vite.config.ts
```
# Files
--------------------------------------------------------------------------------
/src/codegraphcontext/tools/languages/cpp.py:
--------------------------------------------------------------------------------
```python
1 |
2 | from pathlib import Path
3 | from typing import Any, Dict, Optional, Tuple
4 | from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger
5 |
6 | CPP_QUERIES = {
7 | "functions": """
8 | (function_definition
9 | declarator: (function_declarator
10 | declarator: (identifier) @name
11 | )
12 | ) @function_node
13 | """,
14 | "classes": """
15 | (class_specifier
16 | name: (type_identifier) @name
17 | ) @class
18 | """,
19 | "imports": """
20 | (preproc_include
21 | path: [
22 | (string_literal) @path
23 | (system_lib_string) @path
24 | ]
25 | ) @import
26 | """,
27 | "calls": """
28 | (call_expression
29 | function: [
30 | (identifier) @function_name
31 | (field_expression
32 | field: (field_identifier) @method_name
33 | )
34 | ]
35 | arguments: (argument_list) @args
36 | )
37 | """,
38 | "enums":"""
39 | (enum_specifier
40 | name: (type_identifier) @name
41 | body: (enumerator_list
42 | (enumerator
43 | name: (identifier) @value
44 | )*
45 | )? @body
46 | ) @enum
47 | """,
48 | "structs":"""
49 | (struct_specifier
50 | name: (type_identifier) @name
51 | body: (field_declaration_list)? @body
52 | ) @struct
53 | """,
54 | "unions": """
55 | (union_specifier
56 | name: (type_identifier)? @name
57 | body: (field_declaration_list
58 | (field_declaration
59 | declarator: [
60 | (field_identifier) @value
61 | (pointer_declarator (field_identifier) @value)
62 | (array_declarator (field_identifier) @value)
63 | ]
64 | )*
65 | )? @body
66 | ) @union
67 |
68 | """,
69 | "macros": """
70 | (preproc_def
71 | name: (identifier) @name
72 | ) @macro
73 | """,
74 | "variables": """
75 | (declaration
76 | declarator: (init_declarator
77 | declarator: (identifier) @name))
78 |
79 | (declaration
80 | declarator: (init_declarator
81 | declarator: (pointer_declarator
82 | declarator: (identifier) @name)))
83 | """,
84 | "lambda_assignments": """
85 | ; Match a lambda assigned to a variable
86 | (declaration
87 | declarator: (init_declarator
88 | declarator: (identifier) @name
89 | value: (lambda_expression) @lambda_node))
90 | """
91 |
92 | }
93 |
94 | class CppTreeSitterParser:
95 | """A C++-specific parser using tree-sitter."""
96 |
97 | def __init__(self, generic_parser_wrapper):
98 | self.generic_parser_wrapper = generic_parser_wrapper
99 | self.language_name = "cpp"
100 | self.language = generic_parser_wrapper.language
101 | self.parser = generic_parser_wrapper.parser
102 |
103 | self.queries = {
104 | name: self.language.query(query_str)
105 | for name, query_str in CPP_QUERIES.items()
106 | }
107 |
108 | def _get_node_text(self, node) -> str:
109 | return node.text.decode('utf-8')
110 |
111 | def parse(self, file_path: Path, is_dependency: bool = False, **kwargs) -> Dict:
112 | """Parses a C++ file and returns its structure."""
113 | with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
114 | source_code = f.read()
115 |
116 | tree = self.parser.parse(bytes(source_code, "utf8"))
117 | root_node = tree.root_node
118 |
119 | functions = self._find_functions(root_node)
120 | functions.extend(self._find_lambda_assignments(root_node))
121 | function_calls = self._find_calls(root_node)
122 | classes = self._find_classes(root_node)
123 | imports = self._find_imports(root_node)
124 | structs = self._find_structs(root_node)
125 | enums = self._find_enums(root_node)
126 | unions = self._find_unions(root_node)
127 | macros = self._find_macros(root_node)
128 | variables = self._find_variables(root_node)
129 |
130 | return {
131 | "file_path": str(file_path),
132 | "functions": functions,
133 | "classes": classes,
134 | "structs": structs,
135 | "enums": enums,
136 | "unions": unions,
137 | "macros": macros,
138 | "variables": variables,
139 | "declarations": [], # Placeholder
140 | "imports": imports,
141 | "function_calls": function_calls,
142 | "is_dependency": is_dependency,
143 | "lang": self.language_name,
144 | }
145 |
146 | def _find_functions(self, root_node):
147 | functions = []
148 | query = self.queries['functions']
149 | for match in query.captures(root_node):
150 | capture_name = match[1]
151 | node = match[0]
152 | if capture_name == 'name':
153 | func_node = node.parent.parent.parent
154 | name = self._get_node_text(node)
155 | functions.append({
156 | "name": name,
157 | "line_number": node.start_point[0] + 1,
158 | "end_line": func_node.end_point[0] + 1,
159 | "source_code": self._get_node_text(func_node),
160 | "args": [], # Placeholder
161 | })
162 | return functions
163 |
164 | def _find_classes(self, root_node):
165 | classes = []
166 | query = self.queries['classes']
167 | for match in query.captures(root_node):
168 | capture_name = match[1]
169 | node = match[0]
170 | if capture_name == 'name':
171 | class_node = node.parent
172 | name = self._get_node_text(node)
173 | classes.append({
174 | "name": name,
175 | "line_number": node.start_point[0] + 1,
176 | "end_line": class_node.end_point[0] + 1,
177 | "source_code": self._get_node_text(class_node),
178 | "bases": [], # Placeholder
179 | })
180 | return classes
181 |
182 | def _find_imports(self, root_node):
183 | imports = []
184 | query = self.queries['imports']
185 | for match in query.captures(root_node):
186 | capture_name = match[1]
187 | node = match[0]
188 | if capture_name == 'path':
189 | path = self._get_node_text(node).strip('<>')
190 | imports.append({
191 | "name": path,
192 | "full_import_name": path,
193 | "line_number": node.start_point[0] + 1,
194 | "alias": None,
195 | })
196 | return imports
197 |
198 | def _find_enums(self, root_node):
199 | enums = []
200 | query = self.queries['enums']
201 | for node, capture_name in query.captures(root_node):
202 | if capture_name == 'name':
203 | name = self._get_node_text(node)
204 | enum_node = node.parent
205 | enums.append({
206 | "name": name,
207 | "line_number": node.start_point[0] + 1,
208 | "end_line": enum_node.end_point[0] + 1,
209 | "source_code": self._get_node_text(enum_node),
210 | })
211 | return enums
212 |
213 | def _find_structs(self, root_node):
214 | structs = []
215 | query = self.queries['structs']
216 | for node, capture_name in query.captures(root_node):
217 | if capture_name == 'name':
218 | name = self._get_node_text(node)
219 | struct_node = node.parent
220 | structs.append({
221 | "name": name,
222 | "line_number": node.start_point[0] + 1,
223 | "end_line": struct_node.end_point[0] + 1,
224 | "source_code": self._get_node_text(struct_node),
225 | })
226 | return structs
227 |
228 | def _find_unions(self, root_node):
229 | unions = []
230 | query = self.queries['unions']
231 | for node, capture_name in query.captures(root_node):
232 | if capture_name == 'name':
233 | name = self._get_node_text(node)
234 | union_node = node.parent
235 | unions.append({
236 | "name": name,
237 | "line_number": node.start_point[0] + 1,
238 | "end_line": union_node.end_point[0] + 1,
239 | "source_code": self._get_node_text(union_node),
240 | })
241 | return unions
242 |
243 | def _find_macros(self, root_node):
244 | macros = []
245 | query = self.queries['macros']
246 | for match in query.captures(root_node):
247 | capture_name = match[1]
248 | node = match[0]
249 | if capture_name == 'name':
250 | macro_node = node.parent
251 | name = self._get_node_text(node)
252 | macros.append({
253 | "name": name,
254 | "line_number": node.start_point[0] + 1,
255 | "end_line": macro_node.end_point[0] + 1,
256 | "source_code": self._get_node_text(macro_node),
257 | })
258 | return macros
259 |
260 | def _find_lambda_assignments(self, root_node):
261 | functions = []
262 | query = self.queries.get('lambda_assignments')
263 | if not query: return []
264 |
265 | for match in query.captures(root_node):
266 | capture_name = match[1]
267 | node = match[0]
268 |
269 | if capture_name == 'name':
270 | assignment_node = node.parent
271 | lambda_node = assignment_node.child_by_field_name('value')
272 | if lambda_node is None or lambda_node.type != 'lambda_expression':
273 | continue
274 |
275 | params_node = lambda_node.child_by_field_name('declarator')
276 | if params_node:
277 | params_node = params_node.child_by_field_name('parameters')
278 | name = self._get_node_text(node)
279 | params_node = lambda_node.child_by_field_name('parameters')
280 |
281 | context, context_type, _ = self._get_parent_context(assignment_node)
282 | class_context, _, _ = self._get_parent_context(assignment_node, types=('class_definition',))
283 |
284 | func_data = {
285 | "name": name,
286 | "line_number": node.start_point[0] + 1,
287 | "end_line": assignment_node.end_point[0] + 1,
288 | "args": [p for p in [self._get_node_text(p) for p in params_node.children if p.type == 'identifier'] if p] if params_node else [],
289 | "source": self._get_node_text(assignment_node),
290 | "source_code": self._get_node_text(assignment_node),
291 | "docstring": None,
292 | "cyclomatic_complexity": 1,
293 | "context": context,
294 | "context_type": context_type,
295 | "class_context": class_context,
296 | "decorators": [],
297 | "lang": self.language_name,
298 | "is_dependency": False,
299 | }
300 | functions.append(func_data)
301 | return functions
302 |
303 | def _find_variables(self, root_node):
304 | variables = []
305 | query = self.queries['variables']
306 | for match in query.captures(root_node):
307 | capture_name = match[1]
308 | node = match[0]
309 |
310 | if capture_name == 'name':
311 | assignment_node = node.parent
312 |
313 | # Skip lambda assignments, they are handled by _find_lambda_assignments
314 | right_node = assignment_node.child_by_field_name('value')
315 | if right_node and right_node.type == 'lambda_expression':
316 | continue
317 |
318 | name = self._get_node_text(node)
319 | value = self._get_node_text(right_node) if right_node else None
320 |
321 | type_node = assignment_node.child_by_field_name('type')
322 | type_text = self._get_node_text(type_node) if type_node else None
323 |
324 | context, _, _ = self._get_parent_context(node)
325 | class_context, _, _ = self._get_parent_context(node, types=('class_definition',))
326 |
327 | variable_data = {
328 | "name": name,
329 | "line_number": node.start_point[0] + 1,
330 | "value": value,
331 | "type": type_text,
332 | "context": context,
333 | "class_context": class_context,
334 | "lang": self.language_name,
335 | "is_dependency": False,
336 | }
337 | variables.append(variable_data)
338 | return variables
339 |
340 | def _get_parent_context(self, node, types=('function_definition', 'class_definition')):
341 | curr = node.parent
342 | while curr:
343 | if curr.type in types:
344 | name_node = curr.child_by_field_name('name')
345 | return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
346 | curr = curr.parent
347 | return None, None, None
348 |
349 | def _find_calls(self, root_node):
350 | calls = []
351 | query = self.queries['calls']
352 | for node, capture_name in query.captures(root_node):
353 | if capture_name == "function_name":
354 | func_name = self._get_node_text(node)
355 | func_node = node.parent.parent # function_declarator -> function_definition
356 | full_name = self._get_full_name(func_node) or func_name
357 |
358 | # Find return type node (captured separately)
359 | return_type_node = None
360 | for n, cap in query.captures(func_node):
361 | if cap == "return_type":
362 | return_type_node = n
363 | break
364 | return_type = self._get_node_text(return_type_node) if return_type_node else None
365 |
366 | # Extract parameters
367 | args = []
368 | parameters_node = func_node.child_by_field_name("declarator")
369 | if parameters_node:
370 | param_list_node = parameters_node.child_by_field_name("parameters")
371 | if param_list_node:
372 | for param in param_list_node.children:
373 | if param.type == "parameter_declaration":
374 | type_node = param.child_by_field_name("type")
375 | name_node = param.child_by_field_name("declarator")
376 |
377 | param_type = self._get_node_text(type_node) if type_node else None
378 | param_name = self._get_node_text(name_node) if name_node else None
379 |
380 | args.append({
381 | "type": param_type,
382 | "name": param_name
383 | })
384 |
385 |
386 | # Get context info (function may be inside class)
387 | context, _, _ = self._get_parent_context(node)
388 | class_context, _, _ = self._get_parent_context(node, types=("class_definition",))
389 |
390 | call_data = {
391 | "name": func_name,
392 | "full_name": full_name,
393 | "line_number": node.start_point[0] + 1,
394 | "args": args,
395 | "inferred_obj_type": None,
396 | "context": context,
397 | "class_context": class_context,
398 | "lang": self.language_name,
399 | "is_dependency": False,
400 | }
401 | calls.append(call_data)
402 | return calls
403 |
404 | def _get_full_name(self, node):
405 | "Builds a fully qualified name for a function or call node."
406 |
407 | name_parts = []
408 |
409 | # Move upward and collect parent scopes
410 | curr = node
411 | while curr:
412 | if curr.type in ("function_definition", "function_declarator"):
413 | id_node = curr.child_by_field_name("declarator")
414 | if id_node and id_node.type == "identifier":
415 | name_parts.insert(0, id_node.text.decode("utf8"))
416 | elif curr.type == "class_specifier":
417 | name_node = curr.child_by_field_name("name")
418 | if name_node:
419 | name_parts.insert(0, name_node.text.decode("utf8"))
420 | elif curr.type == "namespace_definition":
421 | name_node = curr.child_by_field_name("name")
422 | if name_node:
423 | name_parts.insert(0, name_node.text.decode("utf8"))
424 | curr = curr.parent
425 |
426 | return "::".join(name_parts) if name_parts else None
427 |
428 |
429 | def pre_scan_cpp(files: list[Path], parser_wrapper) -> dict:
430 | """
431 | Quickly scans C++ files to build a map of top-level class, struct, and function names
432 | to their file paths.
433 | """
434 | imports_map = {}
435 |
436 | query_str = """
437 | (class_specifier name: (type_identifier) @name)
438 | (struct_specifier name: (type_identifier) @name)
439 | (function_definition declarator: (function_declarator declarator: (identifier) @name))
440 | """
441 | query = parser_wrapper.language.query(query_str)
442 |
443 | for file_path in files:
444 | try:
445 | with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
446 | source_bytes = f.read().encode("utf-8")
447 | tree = parser_wrapper.parser.parse(source_bytes)
448 |
449 | for node, capture_name in query.captures(tree.root_node):
450 | if capture_name == "name":
451 | name = node.text.decode("utf-8")
452 | imports_map.setdefault(name, []).append(str(file_path.resolve()))
453 | except Exception as e:
454 | warning_logger(f"Tree-sitter pre-scan failed for {file_path}: {e}")
455 |
456 | return imports_map
457 |
```
--------------------------------------------------------------------------------
/src/codegraphcontext/tools/package_resolver.py:
--------------------------------------------------------------------------------
```python
1 | # src/codegraphcontext/tools/package_resolver.py
2 | import importlib
3 | import stdlibs
4 | from pathlib import Path
5 | import subprocess
6 | from typing import Optional
7 |
8 | from ..utils.debug_log import debug_log
9 |
10 | def _get_python_package_path(package_name: str) -> Optional[str]:
11 | """
12 | Finds the local installation path of a Python package.
13 | """
14 | try:
15 | debug_log(f"Getting local path for Python package: {package_name}")
16 | module = importlib.import_module(package_name)
17 | if hasattr(module, '__file__') and module.__file__:
18 | module_file = Path(module.__file__)
19 | if module_file.name == '__init__.py':
20 | return str(module_file.parent)
21 | elif package_name in stdlibs.module_names:
22 | return str(module_file)
23 | else:
24 | return str(module_file.parent)
25 | elif hasattr(module, '__path__'):
26 | if isinstance(module.__path__, list) and module.__path__:
27 | return str(Path(module.__path__[0]))
28 | else:
29 | return str(Path(str(module.__path__)))
30 | return None
31 | except ImportError:
32 | return None
33 | except Exception as e:
34 | debug_log(f"Error getting local path for {package_name}: {e}")
35 | return None
36 |
37 | def _get_npm_package_path(package_name: str) -> Optional[str]:
38 | """
39 | Finds the local installation path of a Node.js package using `npm root`.
40 | """
41 | try:
42 | debug_log(f"Getting local path for npm package: {package_name}")
43 | local_path = Path(f"./node_modules/{package_name}")
44 | if local_path.exists():
45 | return str(local_path.resolve())
46 |
47 | result = subprocess.run(["npm", "root", "-g"], capture_output=True, text=True)
48 | if result.returncode == 0:
49 | global_root = result.stdout.strip()
50 | package_path = Path(global_root) / package_name
51 | if package_path.exists():
52 | return str(package_path.resolve())
53 | return None
54 | except Exception as e:
55 | debug_log(f"Error getting npm package path for {package_name}: {e}")
56 | return None
57 |
58 | def _get_typescript_package_path(package_name: str) -> Optional[str]:
59 | """
60 | Finds the local installation path of a TypeScript package.
61 | TypeScript packages are typically npm packages, so this uses the same logic as npm.
62 | """
63 | try:
64 | debug_log(f"Getting local path for TypeScript package: {package_name}")
65 |
66 | # Check local node_modules first
67 | local_path = Path(f"./node_modules/{package_name}")
68 | if local_path.exists():
69 | return str(local_path.resolve())
70 |
71 | # Check global npm packages
72 | result = subprocess.run(["npm", "root", "-g"], capture_output=True, text=True, timeout=5)
73 | if result.returncode == 0:
74 | global_root = result.stdout.strip()
75 | package_path = Path(global_root) / package_name
76 | if package_path.exists():
77 | return str(package_path.resolve())
78 |
79 | return None
80 | except subprocess.TimeoutExpired:
81 | debug_log(f"npm command timed out for {package_name}")
82 | return None
83 | except Exception as e:
84 | debug_log(f"Error getting TypeScript package path for {package_name}: {e}")
85 | return None
86 |
87 | def _get_java_package_path(package_name: str) -> Optional[str]:
88 | """
89 | Finds the local installation path of a Java package (JAR).
90 | Searches in Maven and Gradle cache directories.
91 |
92 | Args:
93 | package_name: Package name in format "groupId:artifactId" (e.g., "com.google.code.gson:gson")
94 | or just "artifactId" for simple search.
95 | """
96 | try:
97 | debug_log(f"Getting local path for Java package: {package_name}")
98 |
99 | # Parse package name - expect format "groupId:artifactId" or just "artifactId"
100 | if ':' in package_name:
101 | group_id, artifact_id = package_name.split(':', 1)
102 | # Convert group_id dots to path separators (e.g., com.google.gson -> com/google/gson)
103 | group_path = group_id.replace('.', '/')
104 | else:
105 | # If only artifact_id provided, search for it
106 | artifact_id = package_name
107 | group_path = None
108 |
109 | search_paths = []
110 |
111 | # Maven repository (~/.m2/repository)
112 | maven_repo = Path.home() / ".m2" / "repository"
113 | if maven_repo.exists():
114 | if group_path:
115 | # Search for specific group/artifact
116 | package_path = maven_repo / group_path / artifact_id
117 | if package_path.exists():
118 | # Find the latest version directory
119 | version_dirs = [d for d in package_path.iterdir() if d.is_dir()]
120 | if version_dirs:
121 | # Sort by name (assumes semantic versioning) and get the latest
122 | latest_version = sorted(version_dirs, key=lambda x: x.name)[-1]
123 | return str(latest_version.resolve())
124 | else:
125 | # Search for artifact_id in the entire Maven repo
126 | search_paths.append(maven_repo)
127 |
128 | # Gradle cache (~/.gradle/caches/modules-2/files-2.1)
129 | gradle_cache = Path.home() / ".gradle" / "caches" / "modules-2" / "files-2.1"
130 | if gradle_cache.exists():
131 | if group_path:
132 | group_id_full = group_id if ':' in package_name else None
133 | if group_id_full:
134 | package_path = gradle_cache / group_id_full / artifact_id
135 | if package_path.exists():
136 | # Find the latest version directory
137 | version_dirs = [d for d in package_path.iterdir() if d.is_dir()]
138 | if version_dirs:
139 | latest_version = sorted(version_dirs, key=lambda x: x.name)[-1]
140 | # Gradle stores files in hash subdirectories
141 | hash_dirs = [d for d in latest_version.iterdir() if d.is_dir()]
142 | if hash_dirs:
143 | return str(hash_dirs[0].resolve())
144 | else:
145 | search_paths.append(gradle_cache)
146 |
147 | # If group_path wasn't provided or not found, search in the cache directories
148 | if not group_path or search_paths:
149 | for base_path in search_paths:
150 | for jar_file in base_path.rglob(f"*{artifact_id}*.jar"):
151 | return str(jar_file.parent.resolve())
152 |
153 | # Check local lib directories
154 | local_lib_paths = [
155 | Path("./lib"),
156 | Path("./libs"),
157 | Path("/usr/local/lib/java"),
158 | Path("/opt/java/lib"),
159 | ]
160 |
161 | for lib_path in local_lib_paths:
162 | if not lib_path.exists():
163 | continue
164 |
165 | # Look for JAR files matching the artifact name
166 | for jar_file in lib_path.glob(f"*{artifact_id}*.jar"):
167 | return str(jar_file.resolve())
168 |
169 | return None
170 | except Exception as e:
171 | debug_log(f"Error getting Java package path for {package_name}: {e}")
172 | def _get_c_package_path(package_name: str) -> Optional[str]:
173 | """
174 | Finds the local installation path of a C package.
175 | """
176 | try:
177 | debug_log(f"Getting local path for C package: {package_name}")
178 |
179 | # Try using pkg-config to find the package
180 | try:
181 | result = subprocess.run(
182 | ["pkg-config", "--variable=includedir", package_name],
183 | capture_output=True,
184 | text=True,
185 | timeout=5
186 | )
187 | if result.returncode == 0 and result.stdout.strip():
188 | include_dir = Path(result.stdout.strip())
189 | package_path = include_dir / package_name
190 | if package_path.exists():
191 | return str(package_path.resolve())
192 | if include_dir.exists():
193 | return str(include_dir.resolve())
194 | except (subprocess.TimeoutExpired, FileNotFoundError):
195 | debug_log(f"pkg-config not available or timed out for {package_name}")
196 |
197 | # Search in standard system include directories
198 | common_include_paths = [
199 | "/usr/include",
200 | "/usr/local/include",
201 | "/opt/homebrew/include",
202 | "/opt/local/include",
203 | Path.home() / ".local" / "include",
204 | ]
205 |
206 | for base_path in common_include_paths:
207 | base_path = Path(base_path)
208 | if not base_path.exists():
209 | continue
210 |
211 | # Check if package exists as a directory
212 | package_dir = base_path / package_name
213 | if package_dir.exists() and package_dir.is_dir():
214 | return str(package_dir.resolve())
215 |
216 | # Check for header files with package name
217 | header_file = base_path / f"{package_name}.h"
218 | if header_file.exists():
219 | return str(header_file.resolve())
220 |
221 | # Check current directory for local installations
222 | local_package = Path(f"./{package_name}")
223 | if local_package.exists():
224 | return str(local_package.resolve())
225 |
226 | return None
227 | except Exception as e:
228 | debug_log(f"Error getting C package path for {package_name}: {e}")
229 | return None
230 |
231 | def _get_ruby_package_path(package_name: str) -> Optional[str]:
232 | """
233 | Finds the local installation path of a Ruby gem.
234 | """
235 | try:
236 | debug_log(f"Getting local path for Ruby gem: {package_name}")
237 | result = subprocess.run(
238 | ["gem", "which", package_name],
239 | capture_output=True,
240 | text=True,
241 | timeout=5
242 | )
243 | if result.returncode == 0 and result.stdout.strip():
244 | gem_path = Path(result.stdout.strip())
245 | if gem_path.exists():
246 | lib_dir = gem_path.parent if gem_path.is_file() else gem_path
247 | # If we are inside a gem (…/gems/foo-x.y.z/lib/foo.rb), prefer the lib/ dir:
248 | if (lib_dir.name == "lib") and lib_dir.is_dir():
249 | return str(lib_dir.resolve())
250 | # Try parent/lib in case `gem which` returned .../lib/foo.rb
251 | if (lib_dir / "lib").is_dir():
252 | return str((lib_dir / "lib").resolve())
253 | # Fallback: just return the directory containing the file (stdlib case like 'json')
254 | return str(lib_dir.resolve())
255 | return None
256 | except (subprocess.TimeoutExpired, FileNotFoundError):
257 | debug_log(f"gem command not available or timed out for {package_name}")
258 | return None
259 | except Exception as e:
260 | debug_log(f"Error getting Ruby gem path for {package_name}: {e}")
261 | return None
262 |
263 | def _get_go_package_path(package_name: str) -> Optional[str]:
264 | """
265 | Finds the local installation path of a Go package using `go list`.
266 | Tries multiple approaches in sequence to handle different package scenarios:
267 | 1) package dir: go list -f '{{.Dir}}' <pkg> (works for stdlib, GOPATH, or module subpackages)
268 | 2) module root: go list -m -f '{{.Dir}}' <module> (works for full module paths)
269 | 3) force mod: go list -mod=mod -f '{{.Dir}}' <pkg> (works when outside a module context)
270 | 4) GOROOT check: for standard library packages
271 | 5) GOPATH check: for packages in GOPATH
272 | """
273 |
274 | def _first_existing_dir(output: str) -> Optional[str]:
275 | for line in (l.strip().strip("'\"") for l in output.splitlines() if l.strip()):
276 | p = Path(line)
277 | if p.exists() and p.is_dir():
278 | return str(p.resolve())
279 | return None
280 |
281 | try:
282 | debug_log(f"Getting local path for Go package: {package_name}")
283 |
284 | # 1. Package directory (works for stdlib, GOPATH, or subpackages)
285 | cp = subprocess.run(
286 | ["go", "list", "-f", "{{.Dir}}", package_name],
287 | capture_output=True, text=True, timeout=15
288 | )
289 | if cp.returncode == 0:
290 | d = _first_existing_dir(cp.stdout)
291 | if d:
292 | return d
293 |
294 | # 2. Module root directory (where go.mod lives)
295 | cp2 = subprocess.run(
296 | ["go", "list", "-m", "-f", "{{.Dir}}", package_name],
297 | capture_output=True, text=True, timeout=15
298 | )
299 | if cp2.returncode == 0:
300 | d = _first_existing_dir(cp2.stdout)
301 | if d:
302 | debug_log(f"Found Go module {package_name} at {d}")
303 | return d
304 |
305 | # 3. Retry forcing module mode
306 | cp3 = subprocess.run(
307 | ["go", "list", "-mod=mod", "-f", "{{.Dir}}", package_name],
308 | capture_output=True, text=True, timeout=15
309 | )
310 | if cp3.returncode == 0:
311 | d = _first_existing_dir(cp3.stdout)
312 | if d:
313 | return d
314 |
315 | # 4. Check in GOROOT for standard library packages
316 | try:
317 | cp4 = subprocess.run(
318 | ["go", "env", "GOROOT"],
319 | capture_output=True, text=True, timeout=5
320 | )
321 | if cp4.returncode == 0:
322 | goroot = cp4.stdout.strip()
323 | if goroot:
324 | std_lib_path = Path(goroot) / "src" / package_name
325 | if std_lib_path.exists() and std_lib_path.is_dir():
326 | return str(std_lib_path.resolve())
327 | except Exception as e:
328 | debug_log(f"Error checking GOROOT for {package_name}: {e}")
329 |
330 | # 5. Check in GOPATH as fallback
331 | try:
332 | cp5 = subprocess.run(
333 | ["go", "env", "GOPATH"],
334 | capture_output=True, text=True, timeout=5
335 | )
336 | if cp5.returncode == 0:
337 | gopath = cp5.stdout.strip()
338 | if gopath:
339 | gopath_lib_path = Path(gopath) / "src" / package_name
340 | if gopath_lib_path.exists() and gopath_lib_path.is_dir():
341 | debug_log(f"Found Go package in GOPATH {package_name} at {gopath_lib_path}")
342 | return str(gopath_lib_path.resolve())
343 | except Exception as e:
344 | debug_log(f"Error checking GOPATH for {package_name}: {e}")
345 |
346 | debug_log(f"Could not find Go package: {package_name}")
347 | return None
348 |
349 | except (subprocess.TimeoutExpired, FileNotFoundError):
350 | debug_log(f"go command not available or timed out for {package_name}")
351 | return None
352 | except Exception as e:
353 | debug_log(f"Error getting Go package path for {package_name}: {e}")
354 | return None
355 |
356 | def _get_php_package_path(package_name: str) -> Optional[str]:
357 | try:
358 | debug_log(f"Getting local path for PHP package: {package_name}")
359 |
360 | local_vendor = Path("./vendor") / package_name
361 | if local_vendor.exists() and local_vendor.is_dir():
362 | return str(local_vendor.resolve())
363 |
364 | current_dir = Path.cwd()
365 | for parent in [current_dir] + list(current_dir.parents):
366 | vendor_path = parent / "vendor" / package_name
367 | if vendor_path.exists() and vendor_path.is_dir():
368 | return str(vendor_path.resolve())
369 |
370 | if (parent / "composer.json").exists():
371 | break
372 |
373 | composer_home = Path.home() / ".composer" / "vendor" / package_name
374 | if composer_home.exists() and composer_home.is_dir():
375 | return str(composer_home.resolve())
376 |
377 | composer_global = Path.home() / ".config" / "composer" / "vendor" / package_name
378 | if composer_global.exists() and composer_global.is_dir():
379 | return str(composer_global.resolve())
380 |
381 | return None
382 | except Exception as e:
383 | debug_log(f"Error getting PHP package path for {package_name}: {e}")
384 | return None
385 |
386 |
387 | def get_local_package_path(package_name: str, language: str) -> Optional[str]:
388 | """
389 | Dispatches to the correct package path finder based on the language.
390 | """
391 | finders = {
392 | "python": _get_python_package_path,
393 | "javascript": _get_npm_package_path,
394 | "typescript": _get_typescript_package_path,
395 | "java": _get_java_package_path,
396 | "c": _get_c_package_path,
397 | "go": _get_go_package_path,
398 | "ruby": _get_ruby_package_path,
399 | "php": _get_php_package_path,
400 | "cpp": _get_cpp_package_path,
401 |
402 | }
403 | finder = finders.get(language)
404 | if finder:
405 | return finder(package_name)
406 | return None
407 |
408 | def _get_cpp_package_path(package_name: str) -> Optional[str]:
409 | """
410 | C++ package ka local path find karta hai.
411 | Pehle pkg-config try karta hai, fir common system paths check karta hai.
412 | """
413 | import subprocess
414 | import os
415 |
416 | # Try pkg-config
417 | try:
418 | result = subprocess.run(
419 | ["pkg-config", "--variable=includedir", package_name],
420 | capture_output=True,
421 | text=True,
422 | check=False
423 | )
424 | path = result.stdout.strip()
425 | if path and os.path.exists(path):
426 | return path
427 | except FileNotFoundError:
428 | pass
429 |
430 | # Common system include/lib folders
431 | common_paths = [
432 | f"/usr/include/{package_name}",
433 | f"/usr/local/include/{package_name}",
434 | f"/usr/lib/{package_name}",
435 | f"/usr/local/lib/{package_name}",
436 | ]
437 | for path in common_paths:
438 | if os.path.exists(path):
439 | return path
440 |
441 | return None
442 |
443 |
```
--------------------------------------------------------------------------------
/src/codegraphcontext/tools/languages/go.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 |
5 |
6 | GO_QUERIES = {
7 | "functions": """
8 | (function_declaration
9 | name: (identifier) @name
10 | parameters: (parameter_list) @params
11 | ) @function_node
12 |
13 | (method_declaration
14 | receiver: (parameter_list) @receiver
15 | name: (field_identifier) @name
16 | parameters: (parameter_list) @params
17 | ) @function_node
18 | """,
19 | "structs": """
20 | (type_declaration
21 | (type_spec
22 | name: (type_identifier) @name
23 | type: (struct_type) @struct_body
24 | )
25 | ) @struct_node
26 | """,
27 | "interfaces": """
28 | (type_declaration
29 | (type_spec
30 | name: (type_identifier) @name
31 | type: (interface_type) @interface_body
32 | )
33 | ) @interface_node
34 | """,
35 | "imports": """
36 | (import_declaration
37 | (import_spec
38 | path: (interpreted_string_literal) @path
39 | )
40 | ) @import
41 |
42 | (import_declaration
43 | (import_spec
44 | name: (package_identifier) @alias
45 | path: (interpreted_string_literal) @path
46 | )
47 | ) @import_alias
48 | """,
49 | "calls": """
50 | (call_expression
51 | function: (identifier) @name
52 | )
53 | (call_expression
54 | function: (selector_expression
55 | field: (field_identifier) @name
56 | )
57 | )
58 | """,
59 | "variables": """
60 | (var_declaration
61 | (var_spec
62 | name: (identifier) @name
63 | )
64 | )
65 | (short_var_declaration
66 | left: (expression_list
67 | (identifier) @name
68 | )
69 | )
70 | """,
71 | }
72 |
73 | class GoTreeSitterParser:
74 | """A Go-specific parser using tree-sitter, encapsulating language-specific logic."""
75 |
76 | def __init__(self, generic_parser_wrapper):
77 | self.generic_parser_wrapper = generic_parser_wrapper
78 | self.language_name = generic_parser_wrapper.language_name
79 | self.language = generic_parser_wrapper.language
80 | self.parser = generic_parser_wrapper.parser
81 |
82 | self.queries = {
83 | name: self.language.query(query_str)
84 | for name, query_str in GO_QUERIES.items()
85 | }
86 |
87 | def _get_node_text(self, node) -> str:
88 | return node.text.decode('utf-8')
89 |
90 | def _get_parent_context(self, node, types=('function_declaration', 'method_declaration', 'type_declaration')):
91 | curr = node.parent
92 | while curr:
93 | if curr.type in types:
94 | if curr.type == 'type_declaration':
95 | type_spec = curr.child_by_field_name('type_spec')
96 | if type_spec:
97 | name_node = type_spec.child_by_field_name('name')
98 | return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
99 | else:
100 | name_node = curr.child_by_field_name('name')
101 | return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
102 | curr = curr.parent
103 | return None, None, None
104 |
105 | def _calculate_complexity(self, node):
106 | complexity_nodes = {
107 | "if_statement", "for_statement", "switch_statement", "case_clause",
108 | "expression_switch_statement", "type_switch_statement",
109 | "binary_expression", "call_expression"
110 | }
111 | count = 1
112 |
113 | def traverse(n):
114 | nonlocal count
115 | if n.type in complexity_nodes:
116 | count += 1
117 | for child in n.children:
118 | traverse(child)
119 |
120 | traverse(node)
121 | return count
122 |
123 | def _get_docstring(self, func_node):
124 | """Extract Go doc comment preceding the function."""
125 | prev_sibling = func_node.prev_sibling
126 | while prev_sibling and prev_sibling.type in ('comment', '\n', ' '):
127 | if prev_sibling.type == 'comment':
128 | comment_text = self._get_node_text(prev_sibling)
129 | if comment_text.startswith('//'):
130 | return comment_text.strip()
131 | prev_sibling = prev_sibling.prev_sibling
132 | return None
133 |
134 | def parse(self, file_path: Path, is_dependency: bool = False) -> Dict:
135 | """Parses a file and returns its structure in a standardized dictionary format."""
136 | # This method orchestrates the parsing of a single file.
137 | # It calls specialized `_find_*` methods for each language construct.
138 | # The returned dictionary should map a specific key (e.g., 'functions', 'interfaces')
139 | # to a list of dictionaries, where each dictionary represents a single code construct.
140 | # The GraphBuilder will then use these keys to create nodes with corresponding labels.
141 | with open(file_path, "r", encoding="utf-8") as f:
142 | source_code = f.read()
143 |
144 | tree = self.parser.parse(bytes(source_code, "utf8"))
145 | root_node = tree.root_node
146 |
147 | functions = self._find_functions(root_node)
148 | structs = self._find_structs(root_node)
149 | interfaces = self._find_interfaces(root_node)
150 | imports = self._find_imports(root_node)
151 | function_calls = self._find_calls(root_node)
152 | variables = self._find_variables(root_node)
153 |
154 | return {
155 | "file_path": str(file_path),
156 | "functions": functions,
157 | "classes": structs,
158 | "interfaces": interfaces,
159 | "variables": variables,
160 | "imports": imports,
161 | "function_calls": function_calls,
162 | "is_dependency": is_dependency,
163 | "lang": self.language_name,
164 | }
165 |
166 | def _find_functions(self, root_node):
167 | functions = []
168 | query = self.queries['functions']
169 |
170 | captures_by_function = {}
171 |
172 | for node, capture_name in query.captures(root_node):
173 | if capture_name == 'function_node':
174 | func_id = id(node)
175 | if func_id not in captures_by_function:
176 | captures_by_function[func_id] = {
177 | 'node': node,
178 | 'name': None,
179 | 'params': None,
180 | 'receiver': None
181 | }
182 | elif capture_name == 'name':
183 | func_node = self._find_function_node_for_name(node)
184 | if func_node:
185 | func_id = id(func_node)
186 | if func_id not in captures_by_function:
187 | captures_by_function[func_id] = {
188 | 'node': func_node,
189 | 'name': None,
190 | 'params': None,
191 | 'receiver': None
192 | }
193 | captures_by_function[func_id]['name'] = self._get_node_text(node)
194 | elif capture_name == 'params':
195 | func_node = self._find_function_node_for_params(node)
196 | if func_node:
197 | func_id = id(func_node)
198 | if func_id not in captures_by_function:
199 | captures_by_function[func_id] = {
200 | 'node': func_node,
201 | 'name': None,
202 | 'params': None,
203 | 'receiver': None
204 | }
205 | captures_by_function[func_id]['params'] = node
206 | elif capture_name == 'receiver':
207 | func_node = node.parent
208 | if func_node and func_node.type == 'method_declaration':
209 | func_id = id(func_node)
210 | if func_id not in captures_by_function:
211 | captures_by_function[func_id] = {
212 | 'node': func_node,
213 | 'name': None,
214 | 'params': None,
215 | 'receiver': None
216 | }
217 | captures_by_function[func_id]['receiver'] = node
218 |
219 | for func_id, data in captures_by_function.items():
220 | if data['name']:
221 | func_node = data['node']
222 | name = data['name']
223 |
224 | args = []
225 | if data['params']:
226 | args = self._extract_parameters(data['params'])
227 |
228 | receiver_type = None
229 | if data['receiver']:
230 | receiver_type = self._extract_receiver(data['receiver'])
231 |
232 | context, context_type, context_line = self._get_parent_context(func_node)
233 | class_context = receiver_type or (context if context_type == 'type_declaration' else None)
234 |
235 | docstring = self._get_docstring(func_node)
236 |
237 | func_data = {
238 | "name": name,
239 | "line_number": func_node.start_point[0] + 1,
240 | "end_line": func_node.end_point[0] + 1,
241 | "args": args,
242 | "source": self._get_node_text(func_node),
243 | "source_code": self._get_node_text(func_node),
244 | "docstring": docstring,
245 | "cyclomatic_complexity": self._calculate_complexity(func_node),
246 | "context": context,
247 | "context_type": context_type,
248 | "class_context": class_context,
249 | "decorators": [],
250 | "lang": self.language_name,
251 | "is_dependency": False,
252 | }
253 | functions.append(func_data)
254 |
255 | return functions
256 |
257 | def _find_function_node_for_name(self, name_node):
258 | current = name_node.parent
259 | while current:
260 | if current.type in ('function_declaration', 'method_declaration'):
261 | return current
262 | current = current.parent
263 | return None
264 |
265 | def _find_function_node_for_params(self, params_node):
266 | current = params_node.parent
267 | while current:
268 | if current.type in ('function_declaration', 'method_declaration'):
269 | return current
270 | current = current.parent
271 | return None
272 |
273 | def _extract_parameters(self, params_node):
274 | params = []
275 | if params_node.type == 'parameter_list':
276 | for child in params_node.children:
277 | if child.type == 'parameter_declaration':
278 | name_node = child.child_by_field_name('name')
279 | if name_node:
280 | params.append(self._get_node_text(name_node))
281 | elif child.type == 'variadic_parameter_declaration':
282 | name_node = child.child_by_field_name('name')
283 | if name_node:
284 | params.append(f"...{self._get_node_text(name_node)}")
285 | return params
286 |
287 | def _extract_receiver(self, receiver_node):
288 | if receiver_node.type == 'parameter_list' and receiver_node.named_child_count > 0:
289 | param = receiver_node.named_child(0)
290 | type_node = param.child_by_field_name('type')
291 | if type_node:
292 | type_text = self._get_node_text(type_node)
293 | return type_text.strip('*')
294 | return None
295 |
296 | def _find_structs(self, root_node):
297 | structs = []
298 | struct_query = self.queries['structs']
299 | for node, capture_name in struct_query.captures(root_node):
300 | if capture_name == 'name':
301 | struct_node = self._find_type_declaration_for_name(node)
302 | if struct_node:
303 | name = self._get_node_text(node)
304 | class_data = {
305 | "name": name,
306 | "line_number": struct_node.start_point[0] + 1,
307 | "end_line": struct_node.end_point[0] + 1,
308 | "bases": [],
309 | "source": self._get_node_text(struct_node),
310 | "docstring": self._get_docstring(struct_node),
311 | "context": None,
312 | "decorators": [],
313 | "lang": self.language_name,
314 | "is_dependency": False,
315 | }
316 | structs.append(class_data)
317 | return structs
318 |
319 | def _find_interfaces(self, root_node):
320 | interfaces = []
321 | interface_query = self.queries['interfaces']
322 | for node, capture_name in interface_query.captures(root_node):
323 | if capture_name == 'name':
324 | interface_node = self._find_type_declaration_for_name(node)
325 | if interface_node:
326 | name = self._get_node_text(node)
327 | class_data = {
328 | "name": name,
329 | "line_number": interface_node.start_point[0] + 1,
330 | "end_line": interface_node.end_point[0] + 1,
331 | "bases": [],
332 | "source": self._get_node_text(interface_node),
333 | "docstring": self._get_docstring(interface_node),
334 | "context": None,
335 | "decorators": [],
336 | "lang": self.language_name,
337 | "is_dependency": False,
338 | }
339 | interfaces.append(class_data)
340 | return interfaces
341 |
342 | def _find_type_declaration_for_name(self, name_node):
343 | current = name_node.parent
344 | while current:
345 | if current.type == 'type_declaration':
346 | return current
347 | current = current.parent
348 | return None
349 |
350 | def _find_imports(self, root_node):
351 | imports = []
352 | query = self.queries['imports']
353 |
354 | for node, capture_name in query.captures(root_node):
355 | line_number = node.start_point[0] + 1
356 |
357 | if capture_name == 'path':
358 | path_text = self._get_node_text(node).strip('"')
359 | package_name = path_text.split('/')[-1]
360 |
361 | alias = None
362 | import_spec = node.parent
363 | if import_spec and import_spec.type == 'import_spec':
364 | alias_node = import_spec.child_by_field_name('name')
365 | if alias_node:
366 | alias = self._get_node_text(alias_node)
367 |
368 | imports.append({
369 | 'name': package_name,
370 | 'source': path_text,
371 | 'alias': alias,
372 | 'line_number': line_number,
373 | 'lang': self.language_name
374 | })
375 |
376 | return imports
377 |
378 | def _find_calls(self, root_node):
379 | calls = []
380 | query = self.queries['calls']
381 |
382 | for node, capture_name in query.captures(root_node):
383 | if capture_name == 'name':
384 | call_node = node.parent
385 | while call_node and call_node.type != 'call_expression':
386 | call_node = call_node.parent
387 |
388 | if call_node:
389 | name = self._get_node_text(node)
390 |
391 | call_data = {
392 | "name": name,
393 | "full_name": self._get_node_text(call_node.child_by_field_name('function')) if call_node.child_by_field_name('function') else name,
394 | "line_number": node.start_point[0] + 1,
395 | "args": [],
396 | "inferred_obj_type": None,
397 | "context": None,
398 | "class_context": None,
399 | "lang": self.language_name,
400 | "is_dependency": False,
401 | }
402 | calls.append(call_data)
403 |
404 | return calls
405 |
406 | def _find_variables(self, root_node):
407 | variables = []
408 | query = self.queries['variables']
409 |
410 | for node, capture_name in query.captures(root_node):
411 | if capture_name == 'name':
412 | name = self._get_node_text(node)
413 |
414 | variable_data = {
415 | "name": name,
416 | "line_number": node.start_point[0] + 1,
417 | "value": None,
418 | "type": None,
419 | "context": None,
420 | "class_context": None,
421 | "lang": self.language_name,
422 | "is_dependency": False,
423 | }
424 | variables.append(variable_data)
425 |
426 | return variables
427 |
428 | def pre_scan_go(files: list[Path], parser_wrapper) -> dict:
429 | """Scans Go files to create a map of function/struct names to their file paths."""
430 | imports_map = {}
431 | query_str = """
432 | (function_declaration name: (identifier) @name)
433 | (method_declaration name: (field_identifier) @name)
434 | (type_declaration (type_spec name: (type_identifier) @name))
435 | """
436 | query = parser_wrapper.language.query(query_str)
437 |
438 | for file_path in files:
439 | try:
440 | with open(file_path, "r", encoding="utf-8") as f:
441 | tree = parser_wrapper.parser.parse(bytes(f.read(), "utf8"))
442 |
443 | for capture, _ in query.captures(tree.root_node):
444 | name = capture.text.decode('utf-8')
445 | if name not in imports_map:
446 | imports_map[name] = []
447 | imports_map[name].append(str(file_path.resolve()))
448 | except Exception as e:
449 | warning_logger(f"Tree-sitter pre-scan failed for {file_path}: {e}")
450 |
451 | return imports_map
```
--------------------------------------------------------------------------------
/tests/sample_project_typescript/src/utilities-helpers.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Utilities and Helpers
3 | * Demonstrates common utility functions, type helpers, and patterns
4 | * used in TypeScript projects for enhanced development experience
5 | */
6 |
7 | // ========== String Utilities ==========
8 | export const StringUtils = {
9 | /**
10 | * Capitalizes the first letter of a string
11 | */
12 | capitalize: (str: string): string =>
13 | str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(),
14 |
15 | /**
16 | * Converts string to camelCase
17 | */
18 | camelCase: (str: string): string =>
19 | str.replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : '')),
20 |
21 | /**
22 | * Converts string to kebab-case
23 | */
24 | kebabCase: (str: string): string =>
25 | str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`).replace(/^-/, ''),
26 |
27 | /**
28 | * Converts string to snake_case
29 | */
30 | snakeCase: (str: string): string =>
31 | str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`).replace(/^_/, ''),
32 |
33 | /**
34 | * Truncates a string to specified length with ellipsis
35 | */
36 | truncate: (str: string, length: number, suffix: string = '...'): string =>
37 | str.length <= length ? str : str.slice(0, length) + suffix,
38 |
39 | /**
40 | * Removes extra whitespace and trims
41 | */
42 | normalize: (str: string): string =>
43 | str.replace(/\s+/g, ' ').trim(),
44 |
45 | /**
46 | * Pluralizes a word (simple English rules)
47 | */
48 | pluralize: (word: string, count: number = 2): string => {
49 | if (count === 1) return word;
50 | if (word.endsWith('y')) return word.slice(0, -1) + 'ies';
51 | if (word.endsWith('s') || word.endsWith('x') || word.endsWith('ch') || word.endsWith('sh')) {
52 | return word + 'es';
53 | }
54 | return word + 's';
55 | }
56 | };
57 |
58 | // ========== Array Utilities ==========
59 | export const ArrayUtils = {
60 | /**
61 | * Chunks array into smaller arrays of specified size
62 | */
63 | chunk: <T>(array: T[], size: number): T[][] => {
64 | const chunks: T[][] = [];
65 | for (let i = 0; i < array.length; i += size) {
66 | chunks.push(array.slice(i, i + size));
67 | }
68 | return chunks;
69 | },
70 |
71 | /**
72 | * Removes duplicate values from array
73 | */
74 | unique: <T>(array: T[]): T[] => [...new Set(array)],
75 |
76 | /**
77 | * Groups array items by a key function
78 | */
79 | groupBy: <T, K extends string | number | symbol>(
80 | array: T[],
81 | keyFn: (item: T) => K
82 | ): Record<K, T[]> => {
83 | return array.reduce((groups, item) => {
84 | const key = keyFn(item);
85 | groups[key] = groups[key] || [];
86 | groups[key]!.push(item);
87 | return groups;
88 | }, {} as Record<K, T[]>);
89 | },
90 |
91 | /**
92 | * Flattens nested arrays by one level
93 | */
94 | flatten: <T>(array: (T | T[])[]): T[] => array.flat() as T[],
95 |
96 | /**
97 | * Deeply flattens nested arrays
98 | */
99 | flattenDeep: <T>(array: any[]): T[] => {
100 | return array.reduce((acc, val) =>
101 | Array.isArray(val) ? acc.concat(ArrayUtils.flattenDeep(val)) : acc.concat(val), []
102 | );
103 | },
104 |
105 | /**
106 | * Shuffles array elements randomly
107 | */
108 | shuffle: <T>(array: T[]): T[] => {
109 | const result = [...array];
110 | for (let i = result.length - 1; i > 0; i--) {
111 | const j = Math.floor(Math.random() * (i + 1));
112 | [result[i], result[j]] = [result[j]!, result[i]!];
113 | }
114 | return result;
115 | },
116 |
117 | /**
118 | * Returns random element from array
119 | */
120 | sample: <T>(array: T[]): T | undefined => {
121 | return array[Math.floor(Math.random() * array.length)];
122 | },
123 |
124 | /**
125 | * Returns multiple random elements from array
126 | */
127 | sampleSize: <T>(array: T[], n: number): T[] => {
128 | const shuffled = ArrayUtils.shuffle(array);
129 | return shuffled.slice(0, Math.min(n, array.length));
130 | }
131 | };
132 |
133 | // ========== Object Utilities ==========
134 | export const ObjectUtils = {
135 | /**
136 | * Deep clones an object
137 | */
138 | deepClone: <T>(obj: T): T => {
139 | if (obj === null || typeof obj !== 'object') return obj;
140 | if (obj instanceof Date) return new Date(obj.getTime()) as unknown as T;
141 | if (obj instanceof Array) return obj.map(item => ObjectUtils.deepClone(item)) as unknown as T;
142 | if (typeof obj === 'object') {
143 | const copy = {} as T;
144 | Object.keys(obj).forEach(key => {
145 | (copy as any)[key] = ObjectUtils.deepClone((obj as any)[key]);
146 | });
147 | return copy;
148 | }
149 | return obj;
150 | },
151 |
152 | /**
153 | * Merges objects deeply
154 | */
155 | deepMerge: <T extends Record<string, any>, U extends Record<string, any>>(
156 | target: T,
157 | source: U
158 | ): T & U => {
159 | const result = { ...target } as T & U;
160 |
161 | Object.keys(source).forEach(key => {
162 | if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
163 | if (result[key as keyof (T & U)] && typeof result[key as keyof (T & U)] === 'object') {
164 | (result as any)[key] = ObjectUtils.deepMerge(
165 | result[key as keyof (T & U)] as any,
166 | source[key]
167 | );
168 | } else {
169 | (result as any)[key] = ObjectUtils.deepClone(source[key]);
170 | }
171 | } else {
172 | (result as any)[key] = source[key];
173 | }
174 | });
175 |
176 | return result;
177 | },
178 |
179 | /**
180 | * Picks specified keys from object
181 | */
182 | pick: <T extends Record<string, any>, K extends keyof T>(
183 | obj: T,
184 | keys: K[]
185 | ): Pick<T, K> => {
186 | const result = {} as Pick<T, K>;
187 | keys.forEach(key => {
188 | if (key in obj) {
189 | result[key] = obj[key];
190 | }
191 | });
192 | return result;
193 | },
194 |
195 | /**
196 | * Omits specified keys from object
197 | */
198 | omit: <T extends Record<string, any>, K extends keyof T>(
199 | obj: T,
200 | keys: K[]
201 | ): Omit<T, K> => {
202 | const result = { ...obj };
203 | keys.forEach(key => {
204 | delete result[key];
205 | });
206 | return result as Omit<T, K>;
207 | },
208 |
209 | /**
210 | * Gets nested value from object using dot notation
211 | */
212 | get: <T>(obj: any, path: string, defaultValue?: T): T | undefined => {
213 | const keys = path.split('.');
214 | let result = obj;
215 |
216 | for (const key of keys) {
217 | if (result == null || typeof result !== 'object') {
218 | return defaultValue;
219 | }
220 | result = result[key];
221 | }
222 |
223 | return result !== undefined ? result : defaultValue;
224 | },
225 |
226 | /**
227 | * Sets nested value in object using dot notation
228 | */
229 | set: (obj: any, path: string, value: any): void => {
230 | const keys = path.split('.');
231 | const lastKey = keys.pop()!;
232 | let current = obj;
233 |
234 | for (const key of keys) {
235 | if (!(key in current) || typeof current[key] !== 'object') {
236 | current[key] = {};
237 | }
238 | current = current[key];
239 | }
240 |
241 | current[lastKey] = value;
242 | },
243 |
244 | /**
245 | * Checks if object has nested property
246 | */
247 | has: (obj: any, path: string): boolean => {
248 | const keys = path.split('.');
249 | let current = obj;
250 |
251 | for (const key of keys) {
252 | if (current == null || typeof current !== 'object' || !(key in current)) {
253 | return false;
254 | }
255 | current = current[key];
256 | }
257 |
258 | return true;
259 | }
260 | };
261 |
262 | // ========== Function Utilities ==========
263 | export const FunctionUtils = {
264 | /**
265 | * Debounces function execution
266 | */
267 | debounce: <T extends (...args: any[]) => any>(
268 | fn: T,
269 | delay: number
270 | ): ((...args: Parameters<T>) => void) => {
271 | let timeoutId: NodeJS.Timeout;
272 | return (...args: Parameters<T>) => {
273 | clearTimeout(timeoutId);
274 | timeoutId = setTimeout(() => fn(...args), delay);
275 | };
276 | },
277 |
278 | /**
279 | * Throttles function execution
280 | */
281 | throttle: <T extends (...args: any[]) => any>(
282 | fn: T,
283 | limit: number
284 | ): ((...args: Parameters<T>) => void) => {
285 | let inThrottle: boolean;
286 | return (...args: Parameters<T>) => {
287 | if (!inThrottle) {
288 | fn(...args);
289 | inThrottle = true;
290 | setTimeout(() => (inThrottle = false), limit);
291 | }
292 | };
293 | },
294 |
295 | /**
296 | * Memoizes function results
297 | */
298 | memoize: <T extends (...args: any[]) => any>(fn: T): T => {
299 | const cache = new Map();
300 | return ((...args: any[]) => {
301 | const key = JSON.stringify(args);
302 | if (cache.has(key)) {
303 | return cache.get(key);
304 | }
305 | const result = fn(...args);
306 | cache.set(key, result);
307 | return result;
308 | }) as T;
309 | },
310 |
311 | /**
312 | * Creates a curried version of function
313 | */
314 | curry: <T extends (...args: any[]) => any>(
315 | fn: T,
316 | arity: number = fn.length
317 | ): any => {
318 | return (...args: any[]) => {
319 | if (args.length >= arity) {
320 | return fn(...args);
321 | }
322 | return FunctionUtils.curry(fn.bind(null, ...args), arity - args.length);
323 | };
324 | },
325 |
326 | /**
327 | * Composes functions from right to left
328 | */
329 | compose: <T>(...fns: Function[]): (arg: T) => any => {
330 | return (arg: T) => fns.reduceRight((result, fn) => fn(result), arg);
331 | },
332 |
333 | /**
334 | * Pipes functions from left to right
335 | */
336 | pipe: <T>(...fns: Function[]): (arg: T) => any => {
337 | return (arg: T) => fns.reduce((result, fn) => fn(result), arg);
338 | }
339 | };
340 |
341 | // ========== Date Utilities ==========
342 | export const DateUtils = {
343 | /**
344 | * Formats date to ISO string with timezone
345 | */
346 | toISOString: (date: Date): string => date.toISOString(),
347 |
348 | /**
349 | * Formats date to readable string
350 | */
351 | format: (date: Date, locale: string = 'en-US'): string =>
352 | date.toLocaleDateString(locale),
353 |
354 | /**
355 | * Gets difference between dates in various units
356 | */
357 | diff: (date1: Date, date2: Date, unit: 'days' | 'hours' | 'minutes' | 'seconds' = 'days'): number => {
358 | const diffMs = Math.abs(date1.getTime() - date2.getTime());
359 | switch (unit) {
360 | case 'seconds': return Math.floor(diffMs / 1000);
361 | case 'minutes': return Math.floor(diffMs / (1000 * 60));
362 | case 'hours': return Math.floor(diffMs / (1000 * 60 * 60));
363 | case 'days': return Math.floor(diffMs / (1000 * 60 * 60 * 24));
364 | }
365 | },
366 |
367 | /**
368 | * Adds time to date
369 | */
370 | add: (date: Date, amount: number, unit: 'days' | 'hours' | 'minutes' | 'seconds'): Date => {
371 | const result = new Date(date);
372 | switch (unit) {
373 | case 'seconds': result.setSeconds(result.getSeconds() + amount); break;
374 | case 'minutes': result.setMinutes(result.getMinutes() + amount); break;
375 | case 'hours': result.setHours(result.getHours() + amount); break;
376 | case 'days': result.setDate(result.getDate() + amount); break;
377 | }
378 | return result;
379 | },
380 |
381 | /**
382 | * Checks if date is between two dates
383 | */
384 | isBetween: (date: Date, start: Date, end: Date): boolean =>
385 | date >= start && date <= end,
386 |
387 | /**
388 | * Gets start of day
389 | */
390 | startOfDay: (date: Date): Date => {
391 | const result = new Date(date);
392 | result.setHours(0, 0, 0, 0);
393 | return result;
394 | },
395 |
396 | /**
397 | * Gets end of day
398 | */
399 | endOfDay: (date: Date): Date => {
400 | const result = new Date(date);
401 | result.setHours(23, 59, 59, 999);
402 | return result;
403 | }
404 | };
405 |
406 | // ========== Number Utilities ==========
407 | export const NumberUtils = {
408 | /**
409 | * Clamps number between min and max
410 | */
411 | clamp: (value: number, min: number, max: number): number =>
412 | Math.min(Math.max(value, min), max),
413 |
414 | /**
415 | * Rounds to specified decimal places
416 | */
417 | round: (value: number, decimals: number = 0): number =>
418 | Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals),
419 |
420 | /**
421 | * Generates random number between min and max
422 | */
423 | random: (min: number, max: number): number =>
424 | Math.random() * (max - min) + min,
425 |
426 | /**
427 | * Generates random integer between min and max (inclusive)
428 | */
429 | randomInt: (min: number, max: number): number =>
430 | Math.floor(Math.random() * (max - min + 1)) + min,
431 |
432 | /**
433 | * Checks if number is in range
434 | */
435 | inRange: (value: number, min: number, max: number): boolean =>
436 | value >= min && value <= max,
437 |
438 | /**
439 | * Converts number to percentage string
440 | */
441 | toPercent: (value: number, decimals: number = 2): string =>
442 | `${NumberUtils.round(value * 100, decimals)}%`,
443 |
444 | /**
445 | * Formats number with thousands separators
446 | */
447 | format: (value: number, locale: string = 'en-US'): string =>
448 | value.toLocaleString(locale)
449 | };
450 |
451 | // ========== Type Utilities ==========
452 | export type DeepPartial<T> = {
453 | [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
454 | };
455 |
456 | export type DeepRequired<T> = {
457 | [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
458 | };
459 |
460 | export type KeysOfType<T, U> = {
461 | [K in keyof T]: T[K] extends U ? K : never;
462 | }[keyof T];
463 |
464 | export type Writeable<T> = {
465 | -readonly [P in keyof T]: T[P];
466 | };
467 |
468 | export type OptionalExcept<T, K extends keyof T> = Partial<T> & Pick<T, K>;
469 |
470 | export type RequiredExcept<T, K extends keyof T> = Required<Omit<T, K>> & Partial<Pick<T, K>>;
471 |
472 | // ========== Validation Helpers ==========
473 | export const ValidationHelpers = {
474 | /**
475 | * Email validation
476 | */
477 | isEmail: (value: string): boolean =>
478 | /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
479 |
480 | /**
481 | * URL validation
482 | */
483 | isUrl: (value: string): boolean => {
484 | try {
485 | new URL(value);
486 | return true;
487 | } catch {
488 | return false;
489 | }
490 | },
491 |
492 | /**
493 | * UUID validation
494 | */
495 | isUuid: (value: string): boolean =>
496 | /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value),
497 |
498 | /**
499 | * Credit card number validation (Luhn algorithm)
500 | */
501 | isCreditCard: (value: string): boolean => {
502 | const num = value.replace(/\D/g, '');
503 | if (num.length < 13 || num.length > 19) return false;
504 |
505 | let sum = 0;
506 | let isEven = false;
507 |
508 | for (let i = num.length - 1; i >= 0; i--) {
509 | let digit = parseInt(num[i]!);
510 |
511 | if (isEven) {
512 | digit *= 2;
513 | if (digit > 9) digit -= 9;
514 | }
515 |
516 | sum += digit;
517 | isEven = !isEven;
518 | }
519 |
520 | return sum % 10 === 0;
521 | },
522 |
523 | /**
524 | * Strong password validation
525 | */
526 | isStrongPassword: (value: string): boolean => {
527 | const minLength = value.length >= 8;
528 | const hasLower = /[a-z]/.test(value);
529 | const hasUpper = /[A-Z]/.test(value);
530 | const hasNumber = /\d/.test(value);
531 | const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(value);
532 |
533 | return minLength && hasLower && hasUpper && hasNumber && hasSpecial;
534 | }
535 | };
536 |
537 | // ========== Color Utilities ==========
538 | export const ColorUtils = {
539 | /**
540 | * Converts hex to RGB
541 | */
542 | hexToRgb: (hex: string): { r: number; g: number; b: number } | null => {
543 | const match = hex.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
544 | return match ? {
545 | r: parseInt(match[1]!, 16),
546 | g: parseInt(match[2]!, 16),
547 | b: parseInt(match[3]!, 16)
548 | } : null;
549 | },
550 |
551 | /**
552 | * Converts RGB to hex
553 | */
554 | rgbToHex: (r: number, g: number, b: number): string => {
555 | const componentToHex = (c: number) => {
556 | const hex = c.toString(16);
557 | return hex.length === 1 ? '0' + hex : hex;
558 | };
559 | return `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
560 | },
561 |
562 | /**
563 | * Lightens a color by percentage
564 | */
565 | lighten: (hex: string, percent: number): string => {
566 | const rgb = ColorUtils.hexToRgb(hex);
567 | if (!rgb) return hex;
568 |
569 | const factor = 1 + percent / 100;
570 | return ColorUtils.rgbToHex(
571 | Math.min(255, Math.round(rgb.r * factor)),
572 | Math.min(255, Math.round(rgb.g * factor)),
573 | Math.min(255, Math.round(rgb.b * factor))
574 | );
575 | }
576 | };
577 |
578 | // ========== Performance Utilities ==========
579 | export const PerformanceUtils = {
580 | /**
581 | * Measures execution time of a function
582 | */
583 | measure: async <T>(fn: () => T | Promise<T>): Promise<{ result: T; duration: number }> => {
584 | const start = performance.now();
585 | const result = await fn();
586 | const end = performance.now();
587 | return { result, duration: end - start };
588 | },
589 |
590 | /**
591 | * Creates a performance timer
592 | */
593 | timer: () => {
594 | const start = performance.now();
595 | return {
596 | stop: () => performance.now() - start,
597 | lap: () => {
598 | const current = performance.now();
599 | return current - start;
600 | }
601 | };
602 | },
603 |
604 | /**
605 | * Batches synchronous operations
606 | */
607 | batch: <T, R>(
608 | items: T[],
609 | processor: (batch: T[]) => R[],
610 | batchSize: number = 100
611 | ): R[] => {
612 | const results: R[] = [];
613 | for (let i = 0; i < items.length; i += batchSize) {
614 | const batch = items.slice(i, i + batchSize);
615 | results.push(...processor(batch));
616 | }
617 | return results;
618 | }
619 | };
620 |
621 | // ========== Usage Examples ==========
622 | export const utilityExamples = {
623 | // String utilities
624 | camelCased: StringUtils.camelCase("hello-world-example"),
625 | kebabCased: StringUtils.kebabCase("HelloWorldExample"),
626 | truncated: StringUtils.truncate("This is a very long string", 15),
627 |
628 | // Array utilities
629 | chunked: ArrayUtils.chunk([1, 2, 3, 4, 5, 6, 7], 3),
630 | unique: ArrayUtils.unique([1, 2, 2, 3, 3, 3, 4]),
631 | grouped: ArrayUtils.groupBy(
632 | [{ type: 'fruit', name: 'apple' }, { type: 'vegetable', name: 'carrot' }],
633 | item => item.type
634 | ),
635 |
636 | // Object utilities
637 | picked: ObjectUtils.pick({ a: 1, b: 2, c: 3 }, ['a', 'c']),
638 | deepValue: ObjectUtils.get({ user: { profile: { name: 'John' } } }, 'user.profile.name'),
639 |
640 | // Function utilities
641 | debouncedLog: FunctionUtils.debounce((message: string) => console.log(message), 1000),
642 | memoizedFib: FunctionUtils.memoize((n: number): number => {
643 | if (n <= 1) return n;
644 | return utilityExamples.memoizedFib(n - 1) + utilityExamples.memoizedFib(n - 2);
645 | }),
646 |
647 | // Date utilities
648 | futureDate: DateUtils.add(new Date(), 7, 'days'),
649 | daysDiff: DateUtils.diff(new Date('2023-12-31'), new Date('2023-01-01')),
650 |
651 | // Number utilities
652 | clamped: NumberUtils.clamp(150, 0, 100),
653 | rounded: NumberUtils.round(3.14159, 2),
654 | percentage: NumberUtils.toPercent(0.85),
655 |
656 | // Validation
657 | emailValid: ValidationHelpers.isEmail('[email protected]'),
658 | strongPassword: ValidationHelpers.isStrongPassword('StrongPass123!'),
659 |
660 | // Colors
661 | rgbColor: ColorUtils.hexToRgb('#ff0000'),
662 | lighterColor: ColorUtils.lighten('#ff0000', 20)
663 | };
664 |
665 | // Initialize some examples
666 | console.log('Utility examples initialized:', {
667 | camelCased: utilityExamples.camelCased,
668 | chunked: utilityExamples.chunked,
669 | deepValue: utilityExamples.deepValue
670 | });
```
--------------------------------------------------------------------------------
/docs/site/use_cases/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="../installation/">
13 |
14 |
15 | <link rel="next" href="../architecture/">
16 |
17 |
18 | <link rel="icon" href="../assets/images/favicon.png">
19 | <meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.21">
20 |
21 |
22 |
23 | <title>Use Cases - CodeGraphContext</title>
24 |
25 |
26 |
27 | <link rel="stylesheet" href="../assets/stylesheets/main.2a3383ac.min.css">
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
41 | <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">
42 | <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
43 |
44 |
45 |
46 | <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>
47 |
48 |
49 |
50 |
51 |
52 |
53 | </head>
54 |
55 |
56 | <body dir="ltr">
57 |
58 |
59 | <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
60 | <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
61 | <label class="md-overlay" for="__drawer"></label>
62 | <div data-md-component="skip">
63 |
64 |
65 | <a href="#real-world-use-cases" class="md-skip">
66 | Skip to content
67 | </a>
68 |
69 | </div>
70 | <div data-md-component="announce">
71 |
72 | </div>
73 |
74 |
75 |
76 |
77 |
78 |
79 | <header class="md-header md-header--shadow" data-md-component="header">
80 | <nav class="md-header__inner md-grid" aria-label="Header">
81 | <a href=".." title="CodeGraphContext" class="md-header__button md-logo" aria-label="CodeGraphContext" data-md-component="logo">
82 |
83 |
84 | <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>
85 |
86 | </a>
87 | <label class="md-header__button md-icon" for="__drawer">
88 |
89 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
90 | </label>
91 | <div class="md-header__title" data-md-component="header-title">
92 | <div class="md-header__ellipsis">
93 | <div class="md-header__topic">
94 | <span class="md-ellipsis">
95 | CodeGraphContext
96 | </span>
97 | </div>
98 | <div class="md-header__topic" data-md-component="header-topic">
99 | <span class="md-ellipsis">
100 |
101 | Use Cases
102 |
103 | </span>
104 | </div>
105 | </div>
106 | </div>
107 |
108 |
109 | <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>
110 |
111 |
112 |
113 |
114 |
115 | <label class="md-header__button md-icon" for="__search">
116 |
117 | <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>
118 | </label>
119 | <div class="md-search" data-md-component="search" role="dialog">
120 | <label class="md-search__overlay" for="__search"></label>
121 | <div class="md-search__inner" role="search">
122 | <form class="md-search__form" name="search">
123 | <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>
124 | <label class="md-search__icon md-icon" for="__search">
125 |
126 | <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>
127 |
128 | <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>
129 | </label>
130 | <nav class="md-search__options" aria-label="Search">
131 |
132 | <button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
133 |
134 | <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>
135 | </button>
136 | </nav>
137 |
138 | </form>
139 | <div class="md-search__output">
140 | <div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
141 | <div class="md-search-result" data-md-component="search-result">
142 | <div class="md-search-result__meta">
143 | Initializing search
144 | </div>
145 | <ol class="md-search-result__list" role="presentation"></ol>
146 | </div>
147 | </div>
148 | </div>
149 | </div>
150 | </div>
151 |
152 |
153 |
154 | </nav>
155 |
156 | </header>
157 |
158 | <div class="md-container" data-md-component="container">
159 |
160 |
161 |
162 |
163 |
164 |
165 | <main class="md-main" data-md-component="main">
166 | <div class="md-main__inner md-grid">
167 |
168 |
169 |
170 | <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
171 | <div class="md-sidebar__scrollwrap">
172 | <div class="md-sidebar__inner">
173 |
174 |
175 |
176 |
177 | <nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
178 | <label class="md-nav__title" for="__drawer">
179 | <a href=".." title="CodeGraphContext" class="md-nav__button md-logo" aria-label="CodeGraphContext" data-md-component="logo">
180 |
181 |
182 | <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>
183 |
184 | </a>
185 | CodeGraphContext
186 | </label>
187 |
188 | <ul class="md-nav__list" data-md-scrollfix>
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | <li class="md-nav__item">
197 | <a href=".." class="md-nav__link">
198 |
199 |
200 |
201 | <span class="md-ellipsis">
202 | Home
203 |
204 | </span>
205 |
206 |
207 | </a>
208 | </li>
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | <li class="md-nav__item">
219 | <a href="../installation/" class="md-nav__link">
220 |
221 |
222 |
223 | <span class="md-ellipsis">
224 | Installation
225 |
226 | </span>
227 |
228 |
229 | </a>
230 | </li>
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 | <li class="md-nav__item md-nav__item--active">
243 |
244 | <input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
245 |
246 |
247 |
248 |
249 |
250 | <a href="./" class="md-nav__link md-nav__link--active">
251 |
252 |
253 |
254 | <span class="md-ellipsis">
255 | Use Cases
256 |
257 | </span>
258 |
259 |
260 | </a>
261 |
262 | </li>
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 | <li class="md-nav__item">
273 | <a href="../architecture/" class="md-nav__link">
274 |
275 |
276 |
277 | <span class="md-ellipsis">
278 | Architecture
279 |
280 | </span>
281 |
282 |
283 | </a>
284 | </li>
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 | <li class="md-nav__item">
295 | <a href="../cli/" class="md-nav__link">
296 |
297 |
298 |
299 | <span class="md-ellipsis">
300 | CLI Reference
301 |
302 | </span>
303 |
304 |
305 | </a>
306 | </li>
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 | <li class="md-nav__item">
317 | <a href="../server/" class="md-nav__link">
318 |
319 |
320 |
321 | <span class="md-ellipsis">
322 | Server
323 |
324 | </span>
325 |
326 |
327 | </a>
328 | </li>
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 | <li class="md-nav__item">
339 | <a href="../core/" class="md-nav__link">
340 |
341 |
342 |
343 | <span class="md-ellipsis">
344 | Core Concepts
345 |
346 | </span>
347 |
348 |
349 | </a>
350 | </li>
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 | <li class="md-nav__item">
361 | <a href="../tools/" class="md-nav__link">
362 |
363 |
364 |
365 | <span class="md-ellipsis">
366 | Tools
367 |
368 | </span>
369 |
370 |
371 | </a>
372 | </li>
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 | <li class="md-nav__item">
383 | <a href="../cookbook/" class="md-nav__link">
384 |
385 |
386 |
387 | <span class="md-ellipsis">
388 | Cookbook
389 |
390 | </span>
391 |
392 |
393 | </a>
394 | </li>
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 | <li class="md-nav__item md-nav__item--nested">
410 |
411 |
412 |
413 | <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_10" >
414 |
415 |
416 | <label class="md-nav__link" for="__nav_10" id="__nav_10_label" tabindex="0">
417 |
418 |
419 |
420 | <span class="md-ellipsis">
421 | Contributing
422 |
423 | </span>
424 |
425 |
426 | <span class="md-nav__icon md-icon"></span>
427 | </label>
428 |
429 | <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_10_label" aria-expanded="false">
430 | <label class="md-nav__title" for="__nav_10">
431 | <span class="md-nav__icon md-icon"></span>
432 | Contributing
433 | </label>
434 | <ul class="md-nav__list" data-md-scrollfix>
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 | <li class="md-nav__item">
443 | <a href="../contributing/" class="md-nav__link">
444 |
445 |
446 |
447 | <span class="md-ellipsis">
448 | Overview
449 |
450 | </span>
451 |
452 |
453 | </a>
454 | </li>
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 | <li class="md-nav__item">
466 | <a href="../contributing_languages/" class="md-nav__link">
467 |
468 |
469 |
470 | <span class="md-ellipsis">
471 | Adding New Languages
472 |
473 | </span>
474 |
475 |
476 | </a>
477 | </li>
478 |
479 |
480 |
481 |
482 | </ul>
483 | </nav>
484 |
485 | </li>
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 | <li class="md-nav__item">
496 | <a href="../troubleshooting/" class="md-nav__link">
497 |
498 |
499 |
500 | <span class="md-ellipsis">
501 | Troubleshooting
502 |
503 | </span>
504 |
505 |
506 | </a>
507 | </li>
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 | <li class="md-nav__item">
518 | <a href="../future_work/" class="md-nav__link">
519 |
520 |
521 |
522 | <span class="md-ellipsis">
523 | Future Work
524 |
525 | </span>
526 |
527 |
528 | </a>
529 | </li>
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 | <li class="md-nav__item">
540 | <a href="../license/" class="md-nav__link">
541 |
542 |
543 |
544 | <span class="md-ellipsis">
545 | License
546 |
547 | </span>
548 |
549 |
550 | </a>
551 | </li>
552 |
553 |
554 |
555 | </ul>
556 | </nav>
557 | </div>
558 | </div>
559 | </div>
560 |
561 |
562 |
563 | <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
564 | <div class="md-sidebar__scrollwrap">
565 | <div class="md-sidebar__inner">
566 |
567 |
568 | <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
569 |
570 |
571 |
572 |
573 |
574 |
575 | </nav>
576 | </div>
577 | </div>
578 | </div>
579 |
580 |
581 |
582 | <div class="md-content" data-md-component="content">
583 | <article class="md-content__inner md-typeset">
584 |
585 |
586 |
587 |
588 |
589 | <h1 id="real-world-use-cases">Real World Use Cases</h1>
590 | <p>CodeGraphContext can be a powerful ally in your daily coding journey. Here are some real-world scenarios where it can significantly boost your productivity and understanding of a codebase:</p>
591 | <ol>
592 | <li>
593 | <p><strong>Onboarding New Developers:</strong> A new team member can quickly get up to speed by asking questions like:</p>
594 | <ul>
595 | <li>"Where is the authentication logic handled?"</li>
596 | <li>"Show me the main entry point of the application."</li>
597 | </ul>
598 | </li>
599 | <li>
600 | <p><strong>Impact Analysis:</strong> Before making a change, assess the potential ripple effects:</p>
601 | <ul>
602 | <li>"What other parts of the code will be affected if I change the <code>calculate_total</code> function?"</li>
603 | </ul>
604 | </li>
605 | <li>
606 | <p><strong>Code Review:</strong> Gain context on pull requests faster:</p>
607 | <ul>
608 | <li>"Show me the callers of this new function."</li>
609 | <li>"Does this change introduce any new dependencies?"</li>
610 | </ul>
611 | </li>
612 | <li>
613 | <p><strong>Debugging:</strong> Trace execution flows to pinpoint the source of a bug:</p>
614 | <ul>
615 | <li>"Show me the call chain from <code>handle_request</code> to <code>process_payment</code>."</li>
616 | </ul>
617 | </li>
618 | <li>
619 | <p><strong>Refactoring:</strong> Identify and plan large-scale code changes:</p>
620 | <ul>
621 | <li>"Find all instances of the deprecated <code>OldApiClass</code>."</li>
622 | <li>"List all functions that use the <code>urllib</code> library so I can replace them with <code>requests</code>."</li>
623 | </ul>
624 | </li>
625 | <li>
626 | <p><strong>Identifying Code Smells:</strong> Proactively find areas that need improvement:</p>
627 | <ul>
628 | <li>"Find the 10 most complex functions in the codebase."</li>
629 | <li>"Show me functions with more than 5 arguments."</li>
630 | </ul>
631 | </li>
632 | <li>
633 | <p><strong>Security Audits:</strong> Search for potentially vulnerable code patterns:</p>
634 | <ul>
635 | <li>"Find all functions that use the <code>eval</code> function."</li>
636 | <li>"Show me where raw SQL queries are being executed."</li>
637 | </ul>
638 | </li>
639 | <li>
640 | <p><strong>Automated Documentation:</strong> Use the tool's understanding of the code to generate documentation. (This is what this agent is doing!)</p>
641 | </li>
642 | <li>
643 | <p><strong>Dependency Management:</strong> Understand how your project uses its dependencies:</p>
644 | <ul>
645 | <li>"Which files import the <code>requests</code> library?"</li>
646 | <li>"Are there any circular dependencies between modules?"</li>
647 | </ul>
648 | </li>
649 | <li>
650 | <p><strong>Cleaning Up Unused Code:</strong> Keep your codebase lean and maintainable:</p>
651 | <ul>
652 | <li>"Is there any dead or unused code in this project?"</li>
653 | </ul>
654 | </li>
655 | <li>
656 | <p><strong>Exploring a Large Codebase:</strong> Navigate large, unfamiliar projects with ease:</p>
657 | <ul>
658 | <li>"List all the classes in the <code>core</code> module."</li>
659 | <li>"What are the top-level functions in the <code>utils</code> directory?"</li>
660 | </ul>
661 | </li>
662 | <li>
663 | <p><strong>Enforcing Coding Standards:</strong> Check for adherence to team conventions:</p>
664 | <ul>
665 | <li>"Find all functions that are not decorated with <code>@log_execution</code>."</li>
666 | <li>"Show me all public methods that don't have a docstring."</li>
667 | </ul>
668 | </li>
669 | <li>
670 | <p><strong>Discovering API Usage:</strong> Find examples of how to use internal or external APIs:</p>
671 | <ul>
672 | <li>"Show me how other parts of the code use the <code>UserService</code>."</li>
673 | </ul>
674 | </li>
675 | <li>
676 | <p><strong>Improving Test Coverage:</strong> Identify areas that may lack test coverage:</p>
677 | <ul>
678 | <li>"Find all functions that are not called by any test files."</li>
679 | </ul>
680 | </li>
681 | <li>
682 | <p><strong>Visualizing Code Structure:</strong> Get a bird's-eye view of your project's architecture:</p>
683 | <ul>
684 | <li>"Generate a graph visualization of the <code>auth</code> module and its dependencies."</li>
685 | </ul>
686 | </li>
687 | <li>
688 | <p><strong>Learning a New Framework:</strong> Understand how a new framework operates by exploring its source code.</p>
689 | </li>
690 | <li>
691 | <p><strong>Code Archeology:</strong> Investigate legacy code to understand its history and purpose.</p>
692 | </li>
693 | <li>
694 | <p><strong>Planning a Migration:</strong> Identify all points of contact when migrating a library or framework:</p>
695 | <ul>
696 | <li>"Find all the places where the <code>old_payment_gateway</code> is used."</li>
697 | </ul>
698 | </li>
699 | <li>
700 | <p><strong>Knowledge Sharing:</strong> Use the code graph as a centralized, always-up-to-date knowledge base for your team.</p>
701 | </li>
702 | <li>
703 | <p><strong>Automated Code Reviews:</strong> Integrate CodeGraphContext into your CI/CD pipeline to automatically flag potential issues in pull requests.</p>
704 | </li>
705 | </ol>
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 | </article>
720 | </div>
721 |
722 |
723 | <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
724 | </div>
725 |
726 | </main>
727 |
728 | <footer class="md-footer">
729 |
730 | <div class="md-footer-meta md-typeset">
731 | <div class="md-footer-meta__inner md-grid">
732 | <div class="md-copyright">
733 |
734 |
735 | Made with
736 | <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
737 | Material for MkDocs
738 | </a>
739 |
740 | </div>
741 |
742 | </div>
743 | </div>
744 | </footer>
745 |
746 | </div>
747 | <div class="md-dialog" data-md-component="dialog">
748 | <div class="md-dialog__inner md-typeset"></div>
749 | </div>
750 |
751 |
752 |
753 |
754 | <script id="__config" type="application/json">{"base": "..", "features": [], "search": "../assets/javascripts/workers/search.973d3a69.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>
755 |
756 |
757 | <script src="../assets/javascripts/bundle.f55a23d4.min.js"></script>
758 |
759 |
760 | </body>
761 | </html>
```
--------------------------------------------------------------------------------
/docs/site/contributing/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="../cookbook/">
13 |
14 |
15 | <link rel="next" href="../contributing_languages/">
16 |
17 |
18 | <link rel="icon" href="../assets/images/favicon.png">
19 | <meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.21">
20 |
21 |
22 |
23 | <title>Overview - CodeGraphContext</title>
24 |
25 |
26 |
27 | <link rel="stylesheet" href="../assets/stylesheets/main.2a3383ac.min.css">
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
41 | <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">
42 | <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
43 |
44 |
45 |
46 | <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>
47 |
48 |
49 |
50 |
51 |
52 |
53 | </head>
54 |
55 |
56 | <body dir="ltr">
57 |
58 |
59 | <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
60 | <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
61 | <label class="md-overlay" for="__drawer"></label>
62 | <div data-md-component="skip">
63 |
64 |
65 | <a href="#contributing-to-codegraphcontext" class="md-skip">
66 | Skip to content
67 | </a>
68 |
69 | </div>
70 | <div data-md-component="announce">
71 |
72 | </div>
73 |
74 |
75 |
76 |
77 |
78 |
79 | <header class="md-header md-header--shadow" data-md-component="header">
80 | <nav class="md-header__inner md-grid" aria-label="Header">
81 | <a href=".." title="CodeGraphContext" class="md-header__button md-logo" aria-label="CodeGraphContext" data-md-component="logo">
82 |
83 |
84 | <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>
85 |
86 | </a>
87 | <label class="md-header__button md-icon" for="__drawer">
88 |
89 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
90 | </label>
91 | <div class="md-header__title" data-md-component="header-title">
92 | <div class="md-header__ellipsis">
93 | <div class="md-header__topic">
94 | <span class="md-ellipsis">
95 | CodeGraphContext
96 | </span>
97 | </div>
98 | <div class="md-header__topic" data-md-component="header-topic">
99 | <span class="md-ellipsis">
100 |
101 | Overview
102 |
103 | </span>
104 | </div>
105 | </div>
106 | </div>
107 |
108 |
109 | <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>
110 |
111 |
112 |
113 |
114 |
115 | <label class="md-header__button md-icon" for="__search">
116 |
117 | <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>
118 | </label>
119 | <div class="md-search" data-md-component="search" role="dialog">
120 | <label class="md-search__overlay" for="__search"></label>
121 | <div class="md-search__inner" role="search">
122 | <form class="md-search__form" name="search">
123 | <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>
124 | <label class="md-search__icon md-icon" for="__search">
125 |
126 | <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>
127 |
128 | <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>
129 | </label>
130 | <nav class="md-search__options" aria-label="Search">
131 |
132 | <button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
133 |
134 | <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>
135 | </button>
136 | </nav>
137 |
138 | </form>
139 | <div class="md-search__output">
140 | <div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
141 | <div class="md-search-result" data-md-component="search-result">
142 | <div class="md-search-result__meta">
143 | Initializing search
144 | </div>
145 | <ol class="md-search-result__list" role="presentation"></ol>
146 | </div>
147 | </div>
148 | </div>
149 | </div>
150 | </div>
151 |
152 |
153 |
154 | </nav>
155 |
156 | </header>
157 |
158 | <div class="md-container" data-md-component="container">
159 |
160 |
161 |
162 |
163 |
164 |
165 | <main class="md-main" data-md-component="main">
166 | <div class="md-main__inner md-grid">
167 |
168 |
169 |
170 | <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
171 | <div class="md-sidebar__scrollwrap">
172 | <div class="md-sidebar__inner">
173 |
174 |
175 |
176 |
177 | <nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
178 | <label class="md-nav__title" for="__drawer">
179 | <a href=".." title="CodeGraphContext" class="md-nav__button md-logo" aria-label="CodeGraphContext" data-md-component="logo">
180 |
181 |
182 | <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>
183 |
184 | </a>
185 | CodeGraphContext
186 | </label>
187 |
188 | <ul class="md-nav__list" data-md-scrollfix>
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | <li class="md-nav__item">
197 | <a href=".." class="md-nav__link">
198 |
199 |
200 |
201 | <span class="md-ellipsis">
202 | Home
203 |
204 | </span>
205 |
206 |
207 | </a>
208 | </li>
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | <li class="md-nav__item">
219 | <a href="../installation/" class="md-nav__link">
220 |
221 |
222 |
223 | <span class="md-ellipsis">
224 | Installation
225 |
226 | </span>
227 |
228 |
229 | </a>
230 | </li>
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 | <li class="md-nav__item">
241 | <a href="../use_cases/" class="md-nav__link">
242 |
243 |
244 |
245 | <span class="md-ellipsis">
246 | Use Cases
247 |
248 | </span>
249 |
250 |
251 | </a>
252 | </li>
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 | <li class="md-nav__item">
263 | <a href="../architecture/" class="md-nav__link">
264 |
265 |
266 |
267 | <span class="md-ellipsis">
268 | Architecture
269 |
270 | </span>
271 |
272 |
273 | </a>
274 | </li>
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 | <li class="md-nav__item">
285 | <a href="../cli/" class="md-nav__link">
286 |
287 |
288 |
289 | <span class="md-ellipsis">
290 | CLI Reference
291 |
292 | </span>
293 |
294 |
295 | </a>
296 | </li>
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 | <li class="md-nav__item">
307 | <a href="../server/" class="md-nav__link">
308 |
309 |
310 |
311 | <span class="md-ellipsis">
312 | Server
313 |
314 | </span>
315 |
316 |
317 | </a>
318 | </li>
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 | <li class="md-nav__item">
329 | <a href="../core/" class="md-nav__link">
330 |
331 |
332 |
333 | <span class="md-ellipsis">
334 | Core Concepts
335 |
336 | </span>
337 |
338 |
339 | </a>
340 | </li>
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 | <li class="md-nav__item">
351 | <a href="../tools/" class="md-nav__link">
352 |
353 |
354 |
355 | <span class="md-ellipsis">
356 | Tools
357 |
358 | </span>
359 |
360 |
361 | </a>
362 | </li>
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 | <li class="md-nav__item">
373 | <a href="../cookbook/" class="md-nav__link">
374 |
375 |
376 |
377 | <span class="md-ellipsis">
378 | Cookbook
379 |
380 | </span>
381 |
382 |
383 | </a>
384 | </li>
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 | <li class="md-nav__item md-nav__item--active md-nav__item--nested">
402 |
403 |
404 |
405 | <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_10" checked>
406 |
407 |
408 | <label class="md-nav__link" for="__nav_10" id="__nav_10_label" tabindex="0">
409 |
410 |
411 |
412 | <span class="md-ellipsis">
413 | Contributing
414 |
415 | </span>
416 |
417 |
418 | <span class="md-nav__icon md-icon"></span>
419 | </label>
420 |
421 | <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_10_label" aria-expanded="true">
422 | <label class="md-nav__title" for="__nav_10">
423 | <span class="md-nav__icon md-icon"></span>
424 | Contributing
425 | </label>
426 | <ul class="md-nav__list" data-md-scrollfix>
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 | <li class="md-nav__item md-nav__item--active">
437 |
438 | <input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
439 |
440 |
441 |
442 |
443 |
444 | <label class="md-nav__link md-nav__link--active" for="__toc">
445 |
446 |
447 |
448 | <span class="md-ellipsis">
449 | Overview
450 |
451 | </span>
452 |
453 |
454 | <span class="md-nav__icon md-icon"></span>
455 | </label>
456 |
457 | <a href="./" class="md-nav__link md-nav__link--active">
458 |
459 |
460 |
461 | <span class="md-ellipsis">
462 | Overview
463 |
464 | </span>
465 |
466 |
467 | </a>
468 |
469 |
470 |
471 | <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
472 |
473 |
474 |
475 |
476 |
477 |
478 | <label class="md-nav__title" for="__toc">
479 | <span class="md-nav__icon md-icon"></span>
480 | Table of contents
481 | </label>
482 | <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
483 |
484 | <li class="md-nav__item">
485 | <a href="#general-guidelines" class="md-nav__link">
486 | <span class="md-ellipsis">
487 | General Guidelines
488 | </span>
489 | </a>
490 |
491 | </li>
492 |
493 | <li class="md-nav__item">
494 | <a href="#setting-up-your-development-environment" class="md-nav__link">
495 | <span class="md-ellipsis">
496 | Setting up Your Development Environment
497 | </span>
498 | </a>
499 |
500 | </li>
501 |
502 | <li class="md-nav__item">
503 | <a href="#debugging" class="md-nav__link">
504 | <span class="md-ellipsis">
505 | Debugging
506 | </span>
507 | </a>
508 |
509 | </li>
510 |
511 | <li class="md-nav__item">
512 | <a href="#running-tests" class="md-nav__link">
513 | <span class="md-ellipsis">
514 | Running Tests
515 | </span>
516 | </a>
517 |
518 | </li>
519 |
520 | <li class="md-nav__item">
521 | <a href="#submitting-changes" class="md-nav__link">
522 | <span class="md-ellipsis">
523 | Submitting Changes
524 | </span>
525 | </a>
526 |
527 | </li>
528 |
529 | </ul>
530 |
531 | </nav>
532 |
533 | </li>
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 | <li class="md-nav__item">
545 | <a href="../contributing_languages/" class="md-nav__link">
546 |
547 |
548 |
549 | <span class="md-ellipsis">
550 | Adding New Languages
551 |
552 | </span>
553 |
554 |
555 | </a>
556 | </li>
557 |
558 |
559 |
560 |
561 | </ul>
562 | </nav>
563 |
564 | </li>
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 | <li class="md-nav__item">
575 | <a href="../troubleshooting/" class="md-nav__link">
576 |
577 |
578 |
579 | <span class="md-ellipsis">
580 | Troubleshooting
581 |
582 | </span>
583 |
584 |
585 | </a>
586 | </li>
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 | <li class="md-nav__item">
597 | <a href="../future_work/" class="md-nav__link">
598 |
599 |
600 |
601 | <span class="md-ellipsis">
602 | Future Work
603 |
604 | </span>
605 |
606 |
607 | </a>
608 | </li>
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 | <li class="md-nav__item">
619 | <a href="../license/" class="md-nav__link">
620 |
621 |
622 |
623 | <span class="md-ellipsis">
624 | License
625 |
626 | </span>
627 |
628 |
629 | </a>
630 | </li>
631 |
632 |
633 |
634 | </ul>
635 | </nav>
636 | </div>
637 | </div>
638 | </div>
639 |
640 |
641 |
642 | <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
643 | <div class="md-sidebar__scrollwrap">
644 | <div class="md-sidebar__inner">
645 |
646 |
647 | <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
648 |
649 |
650 |
651 |
652 |
653 |
654 | <label class="md-nav__title" for="__toc">
655 | <span class="md-nav__icon md-icon"></span>
656 | Table of contents
657 | </label>
658 | <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
659 |
660 | <li class="md-nav__item">
661 | <a href="#general-guidelines" class="md-nav__link">
662 | <span class="md-ellipsis">
663 | General Guidelines
664 | </span>
665 | </a>
666 |
667 | </li>
668 |
669 | <li class="md-nav__item">
670 | <a href="#setting-up-your-development-environment" class="md-nav__link">
671 | <span class="md-ellipsis">
672 | Setting up Your Development Environment
673 | </span>
674 | </a>
675 |
676 | </li>
677 |
678 | <li class="md-nav__item">
679 | <a href="#debugging" class="md-nav__link">
680 | <span class="md-ellipsis">
681 | Debugging
682 | </span>
683 | </a>
684 |
685 | </li>
686 |
687 | <li class="md-nav__item">
688 | <a href="#running-tests" class="md-nav__link">
689 | <span class="md-ellipsis">
690 | Running Tests
691 | </span>
692 | </a>
693 |
694 | </li>
695 |
696 | <li class="md-nav__item">
697 | <a href="#submitting-changes" class="md-nav__link">
698 | <span class="md-ellipsis">
699 | Submitting Changes
700 | </span>
701 | </a>
702 |
703 | </li>
704 |
705 | </ul>
706 |
707 | </nav>
708 | </div>
709 | </div>
710 | </div>
711 |
712 |
713 |
714 | <div class="md-content" data-md-component="content">
715 | <article class="md-content__inner md-typeset">
716 |
717 |
718 |
719 |
720 |
721 | <h1 id="contributing-to-codegraphcontext">Contributing to CodeGraphContext</h1>
722 | <p>We welcome contributions! Please follow these steps:</p>
723 | <h2 id="general-guidelines">General Guidelines</h2>
724 | <ul>
725 | <li>Ensure your code adheres to the existing style and conventions of the project.</li>
726 | <li>Write clear, concise, and well-documented code.</li>
727 | <li>All new features or bug fixes should be accompanied by appropriate tests.</li>
728 | <li>Keep your pull requests focused on a single feature or bug fix.</li>
729 | </ul>
730 | <h2 id="setting-up-your-development-environment">Setting up Your Development Environment</h2>
731 | <ol>
732 | <li>Fork the repository.</li>
733 | <li>Set up your development environment: <code>pip install -e ".[dev]"</code></li>
734 | <li>Create a new branch for your feature or bugfix (e.g., <code>git checkout -b feature/my-new-feature</code>).</li>
735 | </ol>
736 | <h2 id="debugging">Debugging</h2>
737 | <p>To enable debug mode for detailed logging, locate the <code>debug_mode</code> variable in <code>src/codegraphcontext/tools/graph_builder.py</code> and set its value to <code>1</code>.</p>
738 | <pre><code class="language-python"># src/codegraphcontext/tools/graph_builder.py
739 | debug_mode = 1
740 | </code></pre>
741 | <h2 id="running-tests">Running Tests</h2>
742 | <p>Tests are located in the <code>tests/</code> directory and are run using <code>pytest</code>.</p>
743 | <ol>
744 | <li>Navigate to the root of the <code>CodeGraphContext</code> directory.</li>
745 | <li>Run all tests using the command: <code>pytest</code></li>
746 | <li>To run specific tests, you can provide the path to the test file, for example: <code>pytest tests/test_tools.py</code></li>
747 | <li><strong>Skipping Re-indexing:</strong> To speed up test runs, especially during development, you can set the <code>CGC_SKIP_REINDEX</code> environment variable to <code>true</code>. This will prevent the test suite from re-indexing the sample project if it's already indexed.
748 | <code>bash
749 | CGC_SKIP_REINDEX=true pytest</code></li>
750 | </ol>
751 | <h2 id="submitting-changes">Submitting Changes</h2>
752 | <ol>
753 | <li>Write your code and add corresponding tests in the <code>tests/</code> directory.</li>
754 | <li>Ensure all tests pass and your code lints without errors.</li>
755 | <li>Commit your changes with a descriptive commit message.</li>
756 | <li>Submit a pull request to the <code>main</code> branch.</li>
757 | </ol>
758 | <!-- "Failed to check job status: 'JobManager' object has no attribute 'JobStatus'" -->
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 | </article>
773 | </div>
774 |
775 |
776 | <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
777 | </div>
778 |
779 | </main>
780 |
781 | <footer class="md-footer">
782 |
783 | <div class="md-footer-meta md-typeset">
784 | <div class="md-footer-meta__inner md-grid">
785 | <div class="md-copyright">
786 |
787 |
788 | Made with
789 | <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
790 | Material for MkDocs
791 | </a>
792 |
793 | </div>
794 |
795 | </div>
796 | </div>
797 | </footer>
798 |
799 | </div>
800 | <div class="md-dialog" data-md-component="dialog">
801 | <div class="md-dialog__inner md-typeset"></div>
802 | </div>
803 |
804 |
805 |
806 |
807 | <script id="__config" type="application/json">{"base": "..", "features": [], "search": "../assets/javascripts/workers/search.973d3a69.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>
808 |
809 |
810 | <script src="../assets/javascripts/bundle.f55a23d4.min.js"></script>
811 |
812 |
813 | </body>
814 | </html>
```
--------------------------------------------------------------------------------
/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 |
5 | RUBY_QUERIES = {
6 | "functions": """
7 | (method
8 | name: (identifier) @name
9 | ) @function_node
10 | """,
11 | "classes": """
12 | (class
13 | name: (constant) @name
14 | ) @class
15 | """,
16 | "modules": """
17 | (module
18 | name: (constant) @name
19 | ) @module_node
20 | """,
21 | "imports": """
22 | (call
23 | method: (identifier) @method_name
24 | arguments: (argument_list
25 | (string) @path
26 | )
27 | ) @import
28 | """,
29 | "calls": """
30 | (call
31 | method: (identifier) @name
32 | )
33 | """,
34 | "variables": """
35 | (assignment
36 | left: (identifier) @name
37 | right: (_) @value
38 | )
39 | (assignment
40 | left: (instance_variable) @name
41 | right: (_) @value
42 | )
43 | """,
44 | "comments": """
45 | (comment) @comment
46 | """,
47 | "module_includes": """
48 | (call
49 | method: (identifier) @method
50 | arguments: (argument_list (constant) @module)
51 | ) @include_call
52 | """,
53 | }
54 |
55 |
56 | class RubyTreeSitterParser:
57 | """A Ruby-specific parser using tree-sitter."""
58 |
59 | def __init__(self, generic_parser_wrapper: Any):
60 | self.generic_parser_wrapper = generic_parser_wrapper
61 | self.language_name = "ruby"
62 | self.language = generic_parser_wrapper.language
63 | self.parser = generic_parser_wrapper.parser
64 |
65 | self.queries = {
66 | name: self.language.query(query_str)
67 | for name, query_str in RUBY_QUERIES.items()
68 | }
69 |
70 | def _get_node_text(self, node: Any) -> str:
71 | return node.text.decode("utf-8")
72 |
73 | def _enclosing_class_name(self, node: Any) -> Optional[str]:
74 | name, typ, _ = self._get_parent_context(node, ('class',))
75 | return name
76 |
77 | def _find_modules(self, root_node: Any) -> list[Dict[str, Any]]:
78 | modules = []
79 | query = self.queries["modules"]
80 | # name via captures
81 | captures = list(query.captures(root_node))
82 | for node, cap in captures:
83 | if cap == "module_node":
84 | name = None
85 | for n, c in captures:
86 | if c == "name":
87 | if n.start_byte >= node.start_byte and n.end_byte <= node.end_byte:
88 | name = self._get_node_text(n)
89 | break
90 | if name:
91 | modules.append({
92 | "name": name,
93 | "line_number": node.start_point[0] + 1,
94 | "end_line": node.end_point[0] + 1,
95 | "source": self._get_node_text(node),
96 | "source_code": self._get_node_text(node),
97 | "lang": self.language_name,
98 | "is_dependency": False,
99 | })
100 | return modules
101 |
102 | def _find_module_inclusions(self, root_node: Any) -> list[Dict[str, Any]]:
103 | includes = []
104 | query = self.queries["module_includes"]
105 | for node, cap in query.captures(root_node):
106 | if cap == "method":
107 | method_name = self._get_node_text(node)
108 | if method_name != "include":
109 | continue
110 | if cap == "include_call":
111 | method = None
112 | module = None
113 | for n, c in query.captures(node):
114 | if c == "method":
115 | method = self._get_node_text(n)
116 | elif c == "module":
117 | module = self._get_node_text(n)
118 | if method == "include" and module:
119 | cls = self._enclosing_class_name(node)
120 | if cls:
121 | includes.append({
122 | "class": cls,
123 | "module": module,
124 | "line_number": node.start_point[0] + 1,
125 | "lang": self.language_name,
126 | "is_dependency": False,
127 | })
128 | return includes
129 |
130 |
131 | def _get_parent_context(self, node: Any, types: Tuple[str, ...] = ('class', 'module', 'method')):
132 | """Find parent context for Ruby constructs."""
133 | curr = node.parent
134 | while curr:
135 | if curr.type in types:
136 | name_node = curr.child_by_field_name('name')
137 | if name_node:
138 | return self._get_node_text(name_node), curr.type, curr.start_point[0] + 1
139 | curr = curr.parent
140 | return None, None, None
141 |
142 | def _calculate_complexity(self, node: Any) -> int:
143 | """Calculate cyclomatic complexity for Ruby constructs."""
144 | complexity_nodes = {
145 | "if", "unless", "case", "when", "while", "until", "for", "rescue", "ensure",
146 | "and", "or", "&&", "||", "?", "ternary"
147 | }
148 | count = 1
149 |
150 | def traverse(n):
151 | nonlocal count
152 | if n.type in complexity_nodes:
153 | count += 1
154 | for child in n.children:
155 | traverse(child)
156 |
157 | traverse(node)
158 | return count
159 |
160 | def _get_docstring(self, node: Any) -> Optional[str]:
161 | """Extract comments as docstrings for Ruby constructs."""
162 | # Look for comments before the node
163 | prev_sibling = node.prev_sibling
164 | while prev_sibling and prev_sibling.type in ('comment', '\n', ' '):
165 | if prev_sibling.type == 'comment':
166 | comment_text = self._get_node_text(prev_sibling)
167 | if comment_text.startswith('#') and not comment_text.startswith('#!'):
168 | return comment_text.strip()
169 | prev_sibling = prev_sibling.prev_sibling
170 | return None
171 |
172 | def _parse_method_parameters(self, method_node: Any) -> list[str]:
173 | """Parse method parameters from a method node."""
174 | params = []
175 | # Look for parameters in the method node
176 | for child in method_node.children:
177 | if child.type == 'identifier' and child != method_node.child_by_field_name('name'):
178 | # This is likely a parameter
179 | params.append(self._get_node_text(child))
180 | return params
181 |
182 | def parse(self, file_path: Path, is_dependency: bool = False) -> Dict[str, Any]:
183 | """Parses a Ruby file and returns its structure."""
184 | with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
185 | source_code = f.read()
186 |
187 | tree = self.parser.parse(bytes(source_code, "utf8"))
188 | root_node = tree.root_node
189 |
190 | functions = self._find_functions(root_node)
191 | classes = self._find_classes(root_node)
192 | imports = self._find_imports(root_node)
193 | function_calls = self._find_calls(root_node)
194 | variables = self._find_variables(root_node)
195 | modules = self._find_modules(root_node)
196 | module_inclusions = self._find_module_inclusions(root_node)
197 |
198 | return {
199 | "file_path": str(file_path),
200 | "functions": functions,
201 | "classes": classes,
202 | "variables": variables,
203 | "imports": imports,
204 | "function_calls": function_calls,
205 | "is_dependency": is_dependency,
206 | "lang": self.language_name,
207 | "modules": modules,
208 | "module_inclusions": module_inclusions,
209 | }
210 |
211 | def _find_functions(self, root_node: Any) -> list[Dict[str, Any]]:
212 | """Find all function/method definitions."""
213 | functions = []
214 | query = self.queries["functions"]
215 |
216 | # Collect all captures first
217 | all_captures = list(query.captures(root_node))
218 |
219 | # Group captures by function node using a different approach
220 | captures_by_function = {}
221 | for node, capture_name in all_captures:
222 | if capture_name == 'function_node':
223 | captures_by_function[id(node)] = {'node': node, 'name': None}
224 |
225 | # Now find names for each function
226 | for node, capture_name in all_captures:
227 | if capture_name == 'name':
228 | # Find which function this name belongs to
229 | for func_id, func_data in captures_by_function.items():
230 | func_node = func_data['node']
231 | # Check if this name node is within the function node
232 | if (node.start_byte >= func_node.start_byte and
233 | node.end_byte <= func_node.end_byte):
234 | captures_by_function[func_id]['name'] = self._get_node_text(node)
235 | break
236 |
237 | # Build function entries
238 | for func_data in captures_by_function.values():
239 | func_node = func_data['node']
240 | name = func_data['name']
241 |
242 | if name:
243 | args = self._parse_method_parameters(func_node)
244 |
245 | # Get context and docstring
246 | context, context_type, _ = self._get_parent_context(func_node)
247 | class_context = context if context_type in ('class', 'module') else None
248 | docstring = self._get_docstring(func_node)
249 |
250 | functions.append({
251 | "name": name,
252 | "line_number": func_node.start_point[0] + 1,
253 | "end_line": func_node.end_point[0] + 1,
254 | "args": args,
255 | "source": self._get_node_text(func_node),
256 | "source_code": self._get_node_text(func_node),
257 | "docstring": docstring,
258 | "cyclomatic_complexity": self._calculate_complexity(func_node),
259 | "context": context,
260 | "context_type": context_type,
261 | "class_context": class_context,
262 | "decorators": [],
263 | "lang": self.language_name,
264 | "is_dependency": False,
265 | })
266 |
267 | return functions
268 |
269 | def _find_classes(self, root_node: Any) -> list[Dict[str, Any]]:
270 | """Find all class and module definitions."""
271 | classes = []
272 | query = self.queries["classes"]
273 |
274 | # Collect all captures first
275 | all_captures = list(query.captures(root_node))
276 |
277 | # Group captures by class node using a different approach
278 | captures_by_class = {}
279 | for node, capture_name in all_captures:
280 | if capture_name == 'class':
281 | captures_by_class[id(node)] = {'node': node, 'name': None}
282 |
283 | # Now find names for each class
284 | for node, capture_name in all_captures:
285 | if capture_name == 'name':
286 | # Find which class this name belongs to
287 | for class_id, class_data in captures_by_class.items():
288 | class_node = class_data['node']
289 | # Check if this name node is within the class node
290 | if (node.start_byte >= class_node.start_byte and
291 | node.end_byte <= class_node.end_byte):
292 | captures_by_class[class_id]['name'] = self._get_node_text(node)
293 | break
294 |
295 | # Build class entries
296 | for class_data in captures_by_class.values():
297 | class_node = class_data['node']
298 | name = class_data['name']
299 |
300 | if name:
301 | # Get superclass for inheritance (simplified)
302 | bases = []
303 |
304 | # Get docstring
305 | docstring = self._get_docstring(class_node)
306 |
307 | classes.append({
308 | "name": name,
309 | "line_number": class_node.start_point[0] + 1,
310 | "end_line": class_node.end_point[0] + 1,
311 | "bases": bases,
312 | "source": self._get_node_text(class_node),
313 | "source_code": self._get_node_text(class_node),
314 | "docstring": docstring,
315 | "context": None,
316 | "decorators": [],
317 | "lang": self.language_name,
318 | "is_dependency": False,
319 | })
320 |
321 | return classes
322 |
323 | def _find_imports(self, root_node: Any) -> list[Dict[str, Any]]:
324 | """Find all require/load statements."""
325 | imports = []
326 | query = self.queries["imports"]
327 |
328 | # Collect all captures first
329 | all_captures = list(query.captures(root_node))
330 |
331 | # Group captures by import node using a different approach
332 | captures_by_import = {}
333 | for node, capture_name in all_captures:
334 | if capture_name == 'import':
335 | captures_by_import[id(node)] = {'node': node, 'method_name': None, 'path': None}
336 |
337 | # Now find method names and paths for each import
338 | for node, capture_name in all_captures:
339 | if capture_name == 'method_name':
340 | # Find which import this method name belongs to
341 | for import_id, import_data in captures_by_import.items():
342 | import_node = import_data['node']
343 | # Check if this method name node is within the import node
344 | if (node.start_byte >= import_node.start_byte and
345 | node.end_byte <= import_node.end_byte):
346 | captures_by_import[import_id]['method_name'] = self._get_node_text(node)
347 | break
348 | elif capture_name == 'path':
349 | # Find which import this path belongs to
350 | for import_id, import_data in captures_by_import.items():
351 | import_node = import_data['node']
352 | # Check if this path node is within the import node
353 | if (node.start_byte >= import_node.start_byte and
354 | node.end_byte <= import_node.end_byte):
355 | captures_by_import[import_id]['path'] = self._get_node_text(node)
356 | break
357 |
358 | # Build import entries
359 | for import_data in captures_by_import.values():
360 | import_node = import_data['node']
361 | method_name = import_data['method_name']
362 | path = import_data['path']
363 |
364 | if method_name and path:
365 | path = path.strip('\'"')
366 |
367 | # Only process require/load statements
368 | if method_name in ('require', 'require_relative', 'load'):
369 | imports.append({
370 | "name": path,
371 | "full_import_name": f"{method_name} '{path}'",
372 | "line_number": import_node.start_point[0] + 1,
373 | "alias": None,
374 | "lang": self.language_name,
375 | "is_dependency": False,
376 | })
377 |
378 | return imports
379 |
380 | def _find_calls(self, root_node: Any) -> list[Dict[str, Any]]:
381 | """Find all function and method calls."""
382 | calls = []
383 | query = self.queries["calls"]
384 |
385 | for node, capture_name in query.captures(root_node):
386 | if capture_name == 'name':
387 | name = self._get_node_text(node)
388 | full_name = name
389 |
390 | calls.append({
391 | "name": name,
392 | "full_name": full_name,
393 | "line_number": node.start_point[0] + 1,
394 | "args": [], # Placeholder - could be enhanced to extract arguments
395 | "inferred_obj_type": None,
396 | "context": None, # Placeholder
397 | "class_context": None, # Placeholder
398 | "lang": self.language_name,
399 | "is_dependency": False,
400 | })
401 |
402 | return calls
403 |
404 | def _find_variables(self, root_node: Any) -> list[Dict[str, Any]]:
405 | """Find all variable assignments."""
406 | variables = []
407 | query = self.queries["variables"]
408 |
409 | # Group captures by assignment node
410 | captures_by_assignment = {}
411 | for node, capture_name in query.captures(root_node):
412 | if capture_name == 'name':
413 | # Find the parent assignment node
414 | current = node.parent
415 | while current and current.type != 'assignment':
416 | current = current.parent
417 | if current:
418 | assignment_id = id(current)
419 | if assignment_id not in captures_by_assignment:
420 | captures_by_assignment[assignment_id] = {'node': current, 'name': None, 'value': None}
421 | captures_by_assignment[assignment_id]['name'] = self._get_node_text(node)
422 | elif capture_name == 'value':
423 | # Find the parent assignment node
424 | current = node.parent
425 | while current and current.type != 'assignment':
426 | current = current.parent
427 | if current:
428 | assignment_id = id(current)
429 | if assignment_id not in captures_by_assignment:
430 | captures_by_assignment[assignment_id] = {'node': current, 'name': None, 'value': None}
431 | captures_by_assignment[assignment_id]['value'] = self._get_node_text(node)
432 |
433 | # Build variable entries
434 | for var_data in captures_by_assignment.values():
435 | name = var_data['name']
436 | value = var_data['value']
437 |
438 | if name:
439 | # Determine variable type based on name prefix
440 | var_type = "local"
441 | if name.startswith("@"):
442 | var_type = "instance"
443 | elif name.startswith("@@"):
444 | var_type = "class"
445 | elif name.startswith("$"):
446 | var_type = "global"
447 |
448 | variables.append({
449 | "name": name,
450 | "line_number": var_data['node'].start_point[0] + 1,
451 | "value": value,
452 | "type": var_type,
453 | "context": None, # Placeholder
454 | "class_context": None, # Placeholder
455 | "lang": self.language_name,
456 | "is_dependency": False,
457 | })
458 |
459 | return variables
460 |
461 |
462 | def pre_scan_ruby(files: list[Path], parser_wrapper) -> dict:
463 | """Scans Ruby files to create a map of class/method names to their file paths."""
464 | imports_map = {}
465 | query_str = """
466 | (class
467 | name: (constant) @name
468 | )
469 | (module
470 | name: (constant) @name
471 | )
472 | (method
473 | name: (identifier) @name
474 | )
475 | """
476 | query = parser_wrapper.language.query(query_str)
477 |
478 | for file_path in files:
479 | try:
480 | with open(file_path, "r", encoding="utf-8") as f:
481 | tree = parser_wrapper.parser.parse(bytes(f.read(), "utf8"))
482 |
483 | for capture, _ in query.captures(tree.root_node):
484 | name = capture.text.decode('utf-8')
485 | if name not in imports_map:
486 | imports_map[name] = []
487 | imports_map[name].append(str(file_path.resolve()))
488 | except Exception as e:
489 | warning_logger(f"Tree-sitter pre-scan failed for {file_path}: {e}")
490 |
491 | return imports_map
492 |
```