Contributing to CodeMap
January 13, 2026 ยท View on GitHub
First off, thank you for considering contributing to CodeMap! ๐
This document provides guidelines and instructions for contributing. Following these guidelines helps communicate that you respect the time of the developers managing this project.
Table of Contents
- Code of Conduct
- How Can I Contribute?
- Development Setup
- Project Structure
- Adding a New Language Parser
- Code Style
- Testing
- Pull Request Process
- Help Wanted
Code of Conduct
This project follows a simple code of conduct:
- Be respectful โ Treat everyone with respect and kindness
- Be constructive โ Provide helpful feedback, not criticism
- Be patient โ Maintainers review PRs in their spare time
- Be inclusive โ Welcome newcomers and help them get started
How Can I Contribute?
Reporting Bugs
Before creating a bug report, please check existing issues to avoid duplicates.
When creating a bug report, include:
**Environment:**
- OS: [e.g., macOS 14.0, Ubuntu 22.04, Windows 11]
- Python version: [e.g., 3.11.5]
- CodeMap version: [e.g., 1.0.0]
**Describe the bug:**
A clear description of what the bug is.
**To reproduce:**
1. Run `codemap init ...`
2. Then run `codemap find ...`
3. See error
**Expected behavior:**
What you expected to happen.
**Actual behavior:**
What actually happened.
**Error output:**
Paste any error messages here
**Additional context:**
Any other relevant information.
Suggesting Features
Feature requests are welcome! Please include:
- Use case โ Why do you need this feature?
- Proposed solution โ How do you envision it working?
- Alternatives considered โ Other ways to solve the problem
- Additional context โ Screenshots, mockups, examples
Contributing Code
- Check open issues for something to work on
- Comment on the issue to let others know you're working on it
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
Development Setup
Prerequisites
- Python 3.10 or higher
- Git
- (Optional) tree-sitter for TypeScript/JavaScript support
Setup Steps
# 1. Fork and clone the repository
git clone https://github.com/YOUR_USERNAME/codemap.git
cd codemap
# 2. Create a virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# 3. Install in development mode with all dependencies
pip install -e ".[all]"
# 4. Verify installation
codemap --version
pytest
Running the CLI Locally
# Run from source
python -m codemap.cli --help
# Or use the installed command
codemap --help
Project Structure
codemap/
โโโ cli.py # CLI entry point (Click commands)
โโโ core/
โ โโโ __init__.py
โ โโโ indexer.py # Main indexing orchestrator
โ โโโ hasher.py # SHA256 file hashing
โ โโโ map_store.py # JSON storage (distributed indexes)
โ โโโ watcher.py # File system watcher
โโโ parsers/
โ โโโ __init__.py
โ โโโ base.py # Abstract Parser class
โ โโโ python_parser.py # Python AST parser
โ โโโ typescript_parser.py # TypeScript tree-sitter parser
โ โโโ javascript_parser.py # JavaScript tree-sitter parser
โโโ hooks/
โ โโโ __init__.py
โ โโโ installer.py # Git hook installation
โโโ utils/
โ โโโ __init__.py
โ โโโ config.py # Configuration management
โ โโโ file_utils.py # File discovery utilities
โโโ tests/
โโโ __init__.py
โโโ test_indexer.py
โโโ test_parsers.py
โโโ test_cli.py
โโโ fixtures/ # Sample files for testing
Key Files
| File | Purpose |
|---|---|
cli.py | All CLI commands (init, find, show, etc.) |
core/indexer.py | Orchestrates file discovery and parsing |
core/map_store.py | Reads/writes .codemap/ JSON files |
parsers/base.py | Parser abstract class and Symbol dataclass |
Adding a New Language Parser
This is one of the most valuable contributions! Here's how to add support for a new language.
Step 1: Create the Parser File
Create codemap/parsers/{language}_parser.py:
"""Parser for {Language} files."""
from typing import List
from .base import Parser, Symbol
class {Language}Parser(Parser):
"""{Language} parser using {method}."""
# File extensions this parser handles
extensions = [".ext1", ".ext2"]
def parse(self, source: str) -> List[Symbol]:
"""Parse {Language} source and extract symbols.
Args:
source: Source code as string
Returns:
List of top-level symbols
"""
symbols = []
# Your parsing logic here
# Extract classes, functions, methods, etc.
return symbols
Step 2: Implement Symbol Extraction
The Symbol dataclass (from base.py):
@dataclass
class Symbol:
name: str # Symbol name (e.g., "UserService")
type: str # One of: class, function, method,
# async_function, async_method,
# interface, type, enum
lines: tuple[int, int] # (start_line, end_line), 1-indexed
signature: Optional[str] = None # Function signature
docstring: Optional[str] = None # First 150 chars of docstring
children: List["Symbol"] = None # Nested symbols (methods in class)
Step 3: Choose a Parsing Strategy
Option A: AST-based (preferred for languages with Python bindings)
# Example: Using a hypothetical Go AST library
import go_ast
class GoParser(Parser):
extensions = [".go"]
def parse(self, source: str) -> List[Symbol]:
tree = go_ast.parse(source)
return self._extract_symbols(tree)
def _extract_symbols(self, tree) -> List[Symbol]:
symbols = []
for node in tree.declarations:
if node.type == "function":
symbols.append(Symbol(
name=node.name,
type="function",
lines=(node.start_line, node.end_line),
signature=self._get_signature(node),
))
return symbols
Option B: Tree-sitter (recommended for most languages)
# Example: Rust parser using tree-sitter
try:
import tree_sitter_rust as ts_rust
from tree_sitter import Language, Parser as TSParser
TREE_SITTER_AVAILABLE = True
except ImportError:
TREE_SITTER_AVAILABLE = False
class RustParser(Parser):
extensions = [".rs"]
def __init__(self):
if not TREE_SITTER_AVAILABLE:
raise ImportError(
"tree-sitter-rust required. "
"Install with: pip install tree-sitter tree-sitter-rust"
)
self._parser = TSParser(Language(ts_rust.language()))
def parse(self, source: str) -> List[Symbol]:
tree = self._parser.parse(bytes(source, "utf-8"))
return self._extract_symbols(tree.root_node, source)
def _extract_symbols(self, node, source) -> List[Symbol]:
symbols = []
for child in node.children:
if child.type == "function_item":
symbols.append(self._parse_function(child, source))
elif child.type == "struct_item":
symbols.append(self._parse_struct(child, source))
elif child.type == "impl_item":
symbols.extend(self._parse_impl(child, source))
return symbols
Option C: Regex-based (fallback, not recommended)
Only use if no AST/tree-sitter option exists. Regex parsing is fragile.
Step 4: Register the Parser
Edit codemap/parsers/__init__.py:
from .base import Parser, Symbol
from .python_parser import PythonParser
from .typescript_parser import TypeScriptParser
from .javascript_parser import JavaScriptParser
from .{language}_parser import {Language}Parser # Add this
__all__ = [
"Parser",
"Symbol",
"PythonParser",
"TypeScriptParser",
"JavaScriptParser",
"{Language}Parser", # Add this
]
Edit codemap/core/indexer.py to include the new parser:
def _init_parsers(self) -> dict[str, Parser]:
parsers = {}
if "python" in self.languages:
parsers["python"] = PythonParser()
# Add your language
if "{language}" in self.languages:
try:
from ..parsers.{language}_parser import {Language}Parser
parsers["{language}"] = {Language}Parser()
except ImportError:
pass # Optional dependency not installed
return parsers
Step 5: Add Tests
Create tests/test_{language}_parser.py:
import pytest
from codemap.parsers.{language}_parser import {Language}Parser
@pytest.fixture
def parser():
return {Language}Parser()
class Test{Language}Parser:
def test_parse_function(self, parser):
source = '''
// Your language's function syntax
func hello(name string) string {
return "Hello, " + name
}
'''
symbols = parser.parse(source)
assert len(symbols) == 1
assert symbols[0].name == "hello"
assert symbols[0].type == "function"
assert symbols[0].lines[0] > 0 # Valid line number
def test_parse_class_or_struct(self, parser):
# Test class/struct/type parsing
pass
def test_parse_empty_file(self, parser):
symbols = parser.parse("")
assert symbols == []
def test_parse_syntax_error(self, parser):
# Should not raise, return empty or partial results
symbols = parser.parse("invalid {{{ syntax")
assert isinstance(symbols, list)
Add test fixtures in tests/fixtures/sample.{ext}.
Step 6: Update Documentation
- Add language to the table in
README.md - Add installation instructions if extra dependencies needed
- Update
pyproject.tomlwith optional dependencies
Parser Checklist
- Parser class inherits from
Parser -
extensionsattribute lists all file extensions -
parse()returnsList[Symbol] - Line numbers are 1-indexed
- Handles empty files (returns
[]) - Handles syntax errors gracefully (no exceptions)
- Extracts: classes, functions, methods at minimum
- Nested symbols use
childrenattribute - Signatures are truncated to 100 chars
- Docstrings are truncated to 150 chars
- Unit tests cover basic cases
- Documented in README
Code Style
Python Style
We use Black for formatting and Ruff for linting.
# Format code
black codemap
# Check linting
ruff check codemap
# Fix auto-fixable issues
ruff check --fix codemap
Guidelines
- Type hints โ Use them everywhere
- Docstrings โ Google style for public functions
- Line length โ 100 characters max (Black default)
- Imports โ Use absolute imports, sorted by isort
# Good
from codemap.parsers.base import Parser, Symbol
def get_user(user_id: int) -> Optional[User]:
"""Fetch a user by ID.
Args:
user_id: The user's unique identifier.
Returns:
User object if found, None otherwise.
"""
pass
Testing
Running Tests
# Run all tests
pytest
# Run with coverage
pytest --cov=codemap
# Run specific test file
pytest tests/test_python_parser.py
# Run specific test
pytest tests/test_python_parser.py::TestPythonParser::test_parse_class
# Verbose output
pytest -v
Writing Tests
- Place tests in
tests/directory - Name files
test_*.py - Use pytest fixtures for common setup
- Test both success and failure cases
- Add fixtures to
tests/fixtures/for sample files
Test Coverage Goals
- Parsers: 90%+ coverage
- Core: 80%+ coverage
- CLI: 70%+ coverage (integration tests)
Pull Request Process
Before Submitting
- โ
Code is formatted (
black codemap) - โ
Linting passes (
ruff check codemap) - โ
All tests pass (
pytest) - โ New code has tests
- โ Documentation updated if needed
PR Title Format
type: short description
Examples:
feat: add Go language parser
fix: handle empty files in Python parser
docs: update installation instructions
test: add edge case tests for indexer
refactor: simplify map_store logic
PR Description Template
## What
Brief description of changes.
## Why
Why is this change needed?
## How
How does this change address the issue?
## Testing
How was this tested?
## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] Code formatted with Black
- [ ] Linting passes
Review Process
- Submit PR against
mainbranch - CI runs tests automatically
- Maintainer reviews code
- Address feedback if any
- Maintainer merges when approved
Help Wanted
Looking for something to work on? Here are high-impact areas:
๐ข Good First Issues
- Add more test cases for existing parsers
- Improve error messages
- Documentation improvements
- Add examples to README
๐ก Medium Difficulty
- New language parsers:
- Go
- Rust
- Java
- C#
- Ruby
- PHP
- CLI improvements:
- JSON output format (
--jsonflag) - Fuzzy symbol search
- Better progress indicators
- JSON output format (
๐ด Advanced
- MCP server mode โ Expose CodeMap as Model Context Protocol server
- VSCode extension โ GUI for non-CLI users
- Performance optimization โ Parallel indexing for large repos
- Incremental updates โ Only re-parse changed portions of files
Questions?
- ๐ฌ Open a GitHub Discussion
- ๐ File an Issue
Thank you for contributing! ๐