Language Extension API¶
For supported formats and a custom-language registration example, see Language Support.
Registry¶
- contextcraft.default_registry() LanguageRegistry¶
Returns a new registry containing the built-in language classes. Register your
own language before passing it to ContextCraft(registry=registry).
- class contextcraft.LanguageRegistry¶
Register language metadata and resolve paths to ordered parser candidates.
Multiple languages may share an extension or filename. Registration order determines parser preference; duplicate IDs and aliases are rejected.
- detect_candidates(path: str) list[type[Language]]¶
Prefer exact filenames over case-insensitive extensions; return a new list.
- property ignore_patterns: tuple[str, ...]¶
register()accepts aLanguagesubclass, not an instance. Duplicate IDs or aliases raiseValueError; multiple languages may share an extension.get()resolves an ID or alias, returningNonefor an unknown name.detect_candidates()prefers an exact, case-sensitive filename match over an extension match. Extensions are case-insensitive. Candidates retain registration order; an unknown path returns an empty list.languagesis a tuple of registered classes.ignore_patternscombines their ignore patterns without duplicates.
Language definition¶
The following types live in contextcraft.languages.base.
- class contextcraft.languages.base.LanguageSpec(id: 'str', extensions: 'frozenset[str]' = frozenset(), filenames: 'frozenset[str]' = frozenset(), ignore_patterns: 'tuple[str, ...]' = (), parser_id: 'str | None' = None, aliases: 'tuple[str, ...]' = ())¶
Field |
Meaning |
|---|---|
|
Unique registry ID |
|
Filename suffixes including the leading dot, such as |
|
Exact filenames such as |
|
Language-specific scanner exclusions |
|
Grammar name passed to tree-sitter-language-pack, or |
|
Additional names accepted by registry lookup |
- class contextcraft.languages.base.Language¶
- classmethod build_scopes(root: Node, source: bytes) ScopeGraph¶
A subclass supplies four class attributes: spec, extractor,
skeleton_builder, and signatures_builder. The extractor and builders are
classes, not instances. parse() and build_scopes() delegate to a fresh
extractor instance.
Extractors¶
- class contextcraft.languages.base.ASTExtractor(*args, **kwargs)¶
- build_scopes(root: Node, source: bytes) ScopeGraph¶
This protocol accepts a tree-sitter root node and the original UTF-8 bytes.
extract() returns a ParsedAst; build_scopes() returns a ScopeGraph.
- class contextcraft.languages.base.BaseExtractor¶
- build_scopes(root: Node, source: bytes) ScopeGraph¶
BaseExtractor.extract() creates a ParsedAst using build_scopes() and the
root’s error status. Override build_scopes() at minimum; override extract()
to add symbols and imports.
This helper loads the configured grammar and calls the language extractor.
With parser_id=None, it returns a source-only AST. Grammar-loading failures
return a source-only AST with has_errors=True.
Builders¶
- class contextcraft.languages.base.SkeletonBuilder(spec: LanguageSpec)¶
- class contextcraft.languages.base.SignaturesBuilder(spec: LanguageSpec)¶
Both builders receive a LanguageSpec at construction. Implement
build(parsed) to return text; token counting and tier fallback are handled by
Elider. Builders should use the supplied AST rather than reparsing the source.
SignaturesBuilder.build_selected(parsed, symbols) renders a subset of declarations.
Its default implementation includes their ancestors and calls build() with a
filtered symbol list, preserving the original AST. Override it if a custom
builder needs additional context.
Language.build_selected() uses the signature builder, falling back to a
source-preserving skeleton when needed.
Built-in signature builders trim known unused named imports using their grammar.
This covers Python, JavaScript/TypeScript/TSX, Rust, Go, Java, C#, Kotlin, Scala,
Swift, and PHP where the declaration identifies a binding. Trimming preserves
whole grouped declarations if any binding is needed. Runtime loads such as
Ruby require, R library, shell source, C/C++ headers, namespace imports,
wildcards, and other unclassifiable forms remain intact. Imports and source
ranges on the original AST are never changed.
Scope data¶
These types live in contextcraft.ast.scope.
- class contextcraft.ast.scope.ScopeGraph(scopes: 'list[Scope]' = <factory>, bindings: 'list[Binding]' = <factory>, references: 'list[Reference]' = <factory>)¶
scopes, bindings, and references hold the extracted lexical scope graph.
- class contextcraft.ast.scope.Scope(start_byte: 'int', end_byte: 'int', parent: 'Scope | None' = None, kind: 'str' = 'block', bindings: 'dict[str, Binding]'=<factory>, references: 'list[Reference]' = <factory>)¶
resolve(name) searches the current scope and then its parents, returning the
nearest Binding or None.
- class contextcraft.ast.scope.Binding(name: 'str', start_byte: 'int', end_byte: 'int', scope: 'Scope')¶
- class contextcraft.ast.scope.Reference(name: 'str', start_byte: 'int', end_byte: 'int', scope: 'Scope')¶
Bindings and references carry a name, half-open byte offsets, and their scope.