Skip to content

Adding a Language

Arham-Qureshi edited this page Jul 21, 2026 · 1 revision

Adding a New Language

codebase-vis can parse any language with a tree-sitter grammar. This guide walks you through adding support for a new language.

Overview

Adding a language requires changes in 4-5 files:

src/parser/{language}.js          # New parser module
src/parser/index.js               # Register in GRAMMAR_MAP
src/parser/languages.js           # Add language metadata
src/parser/parse-worker.js        # If grammar needs native bindings
test/parser/{language}.test.js    # Tests

Step 1: Install the tree-sitter Grammar

npm install tree-sitter-{language}

Check if the grammar is already listed in package.json dependencies.

Step 2: Create the Parser Module

Create src/parser/{language}.js. It must export:

  • grammar — the tree-sitter language grammar
  • extractDependencies(node) — returns string[] of import paths
  • extractEntities(node) — returns { classes: string[], functions: string[], methods: string[], docstrings: string[] }

Template

import { promises as fs } from 'node:fs'
import { resolve } from 'node:path'

const query = `
  ; Dependency query
  (import_statement
    (string (string_fragment) @import))

  ; Entity queries
  (class_declaration
    name: (identifier) @class_name)
  (function_declaration
    name: (identifier) @function_name)
`

const methodQuery = `
  (class_body
    (method_definition
      name: (property_identifier) @method_name))
`

const docstringQuery = `
  (comment) @docstring
`

export const grammar = await import('tree-sitter-{language}').then(m => m.default || m)

export function extractDependencies(node) {
  const deps = new Set()
  const matches = node.querySelectorAll(query)
  for (const match of matches) {
    if (match.captures.import) {
      deps.add(match.captures.import.text)
    }
  }
  return [...deps]
}

export function extractEntities(node) {
  const classes = []
  const functions = []
  const methods = []
  const docstrings = []

  // Extract using query captures
  // Deduplicate methods from functions

  return { classes, functions, methods, docstrings }
}

Key Patterns

Technique Implementation
Dependency capture S-expression query matching import statements
Entity deduplication Use AST position keys (startIndex-endIndex) with Set
Method exclusion Methods found inside class bodies → separate from top-level functions
Docstring association Docstrings immediately preceding entities are attached to them
Path stripping Strip quotes, @, ./, angle brackets from captured paths

Step 3: Register in GRAMMAR_MAP

In src/parser/index.js, add your language:

const GRAMMAR_MAP = {
  // ... existing entries ...
  '.ext': { grammar: yourLanguageGrammar, parser: '{language}' },
}

Add your extension to KNOWN_EXTENSIONS if not already covered.

Step 4: Add Language Metadata

In src/parser/languages.js:

export const LANGUAGES = [
  // ... existing entries ...
  {
    name: 'YourLanguage',
    extensions: ['.ext1', '.ext2'],
    parser: '{language}',
    color: '#yourColor',
    defaultIgnores: ['build/', '*.ext_obj'],
  },
]

If your language has stack markers for init detection, add to STACK_MARKERS:

export const STACK_MARKERS = [
  // ... existing entries ...
  { name: 'yourstack', markerFiles: ['your.config.js'], priority: 5 },
]

Step 5: Update Worker

If your grammar requires native bindings (most do), ensure src/parser/parse-worker.js imports it:

// Add to the worker's local GRAMMAR_MAP
const GRAMMAR_MAP = {
  // ... existing entries ...
}

Step 6: Write Tests

Create test/parser/{language}.test.js:

import { test } from 'node:test'
import assert from 'node:assert'
import { parseFile } from '../../src/parser/index.js'

test('parses imports', async () => {
  const result = await parseFile('path/to/test/file.ext')
  assert.ok(result.dependencies.includes('expected-dependency'))
})

test('extracts entities', async () => {
  const result = await parseFile('path/to/test/file.ext')
  assert.ok(result.entities.classes.includes('ExpectedClass'))
})

Existing Language Examples

Study these existing parsers for reference:

Language File Key Patterns
JavaScript src/parser/javascript.js ES imports, CommonJS, dynamic imports
Python src/parser/python.js Absolute/relative imports, decorator handling
C++ src/parser/cpp.js System vs local includes, angle bracket stripping
Go src/parser/go.js Simple import spec, quoted string stripping
Rust src/parser/rust.js Use declarations, extern crate

Clone this wiki locally