┌─────────────────────────────────────────────────────────────┐
│ VS Code Extension Host │
└──────────────────────────┬──────────────────────────────────┘
│
│ Document Events
│ (onChange, onSave)
│
┌───────────────▼───────────────┐
│ Extension Activator │
│ (extension.ts) │
│ │
│ • Register diagnostic │
│ collection │
│ • Initialize registries: │
│ - Language handlers │
│ - Editor handlers │
│ • Setup event handlers: │
│ - DocumentEventHandler │
│ - ConfigurationHandler │
│ • Initialize LLM service │
│ • Initialize StatusBar │
│ • Register Code Actions │
└───────────────┬───────────────┘
│
│ Get handler for language
│
┌───────────────▼───────────────┐
│ Language Handler Registry │
│ │
│ • register(languageId, ...) │
│ • get(languageId) │
│ • isSupported(languageId) │
│ • resetCache(languageId) │
│ │
│ Registered Handlers: │
│ └─► Python Handler. │
│ └─► TypeScript Handler │
│ └─► JavaScript Handler │
└───────────────┬───────────────┘
│
│ Returns LanguageHandler
│
┌───────────────▼───────────────┐
│ Language Handler │
│ (Python Example) │
│ │
│ • parser: IParser │
│ • docstringParsers: Map │
│ • analyzers: IAnalyzer[] │
│ • selectDocstringParser() │
│ • resetCache() │
└───────────────┬───────────────┘
│
┌──────────────┴──────────────┐
│ │
│ Parse │ Parse
▼ ▼
┌─────────────────┐ ┌─────────────────────┐
│ Parser Layer │ │ Docstring Parser │
│ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────────┐ │
│ │ Python │ │ │ │ Google Style │ │
│ │ Parser │ │ │ │ Parser │ │
│ │ (AST) │ │ │ └─────────────────┘ │
│ └─────────────┘ │ │ │
│ │ │ ┌─────────────────┐ │
│ • Extract │ │ │ Sphinx Style │ │
│ function │ │ │ Parser │ │
│ signatures │ │ └─────────────────┘ │
│ • Parameters │ │ │
│ • Returns │ │ • Args, Returns │
│ • Yields │ │ • Raises, Yields │
│ • Exceptions │ │ • Auto-detection │
│ • Side effects │ │ • Multi-line │
└─────────────────┘ └─────────────────────┘
│ │
└──────────────┬──────────────┘
│
│ Parsed data
│
┌───────────────▼───────────────┐
│ Analyzer Layer │
│ │
│ ┌─────────────────────────┐ │
│ │ Signature Analyzer │ │
│ │ • Parameter checks │ │
│ │ • Type normalization │ │
│ │ • Optional handling │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ Return Analyzer │ │
│ │ • Return type checks │ │
│ │ • Generator detection │ │
│ │ • Multiple returns │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ Exception Analyzer │ │
│ │ • Raised vs documented │ │
│ │ • Exception types │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ Side Effects Analyzer │ │
│ │ • I/O operations │ │
│ │ • Global modifications │ │
│ └─────────────────────────┘ │
└───────────────┬───────────────┘
│
│ Diagnostics
│
┌───────────────▼───────────────┐
│ Diagnostic Factory │
│ │
│ • Create VS Code diagnostics │
│ • Assign codes (DSV101-401) │
│ • Set severity levels │
│ • Format messages │
└───────────────┬───────────────┘
│
│ Display
│
┌───────────────▼───────────────┐
│ VS Code UI │
│ │
│ • Yellow/red squiggly lines │
│ • Problems panel │
│ • Hover tooltips │
│ • Status bar indicator │
│ (via StatusBarManager) │
└───────────────────────────────┘
User edits file.py
↓
Extension detects change
↓
Python Parser extracts:
- Function: calculate(x: int, y: int) -> int
- Returns: [int]
- Docstring exists
↓
Docstring Parser extracts:
- Args: y (int)
- Returns: str
↓
Signature Analyzer compares:
✓ Parameter 'y' matches
✗ Parameter 'x' missing in docstring → DSV102
↓
Return Analyzer compares:
✗ Return type int ≠ str → DSV201
↓
Diagnostic Factory creates:
- Diagnostic 1: "Parameter 'x' missing..."
- Diagnostic 2: "Return type mismatch..."
↓
VS Code displays:
- Yellow squiggly lines on function
- Warnings in Problems panel
To improve user experience, the extension normalizes common type aliases:
| Code Type | Docstring Aliases |
|---|---|
str |
string, str, STR |
int |
integer, int, INT |
bool |
boolean, bool |
dict |
dictionary, dict |
Handles multiple Optional syntaxes:
Optional[str]≡str | None≡Union[str, None]- Case-insensitive:
None,none,NONE
Preserves and compares complex types:
- Generics:
list[int],dict[str, Any] - Unions:
str | int | None - No deep type checking (by design)
Functions with yield or yield from statements are marked as generators:
isGenerator: trueflag setyieldStatements[]array populated
Language Handler Registry pattern makes it easy to add new languages.
Steps:
-
Create Language Handler Factory (
src/languages/{language}/factory.ts)export function createTypeScriptHandler(context: vscode.ExtensionContext): LanguageHandler { return { parser: new TypeScriptParser(), docstringParsers: new Map([['jsdoc', new JSDocParser()]]), analyzers: [ new TypeScriptSignatureAnalyzer(), new TypeScriptReturnAnalyzer(), new TypeScriptExceptionAnalyzer(), ], // Optional: selectDocstringParser() if multiple styles // Optional: resetCache() for cleanup }; }
-
Implement Language Parser (
src/parsers/{language}/)- Implement
IParserinterface - Extract function metadata to
FunctionDescriptor - Use language-specific AST (e.g., TypeScript Compiler API)
- Implement
-
Implement Docstring Parser (
src/docstring/{language}/)- Implement
IDocstringParserinterface - Parse documentation to
DocstringDescriptor - Handle language-specific formats (JSDoc, TSDoc, etc.)
- Implement
-
Adapt or Create Analyzers (
src/analyzers/{language}/)- Reuse existing analyzers if logic is universal
- Create language-specific versions for unique patterns
- Example: Exception handling differs (try/catch vs raise)
-
Register in extension.ts
languageRegistry.register('typescript', createTypeScriptHandler(context));
That's it! No need to modify core extension logic.
- Implement
IAnalyzerinterface - Add diagnostic codes to
DiagnosticCodeenum - Add analyzer to language handler's
analyzersarray - Create factory methods in
DiagnosticFactory
- Implement
IDocstringParserinterface - Parse sections into
DocstringDescriptor - Add format detection logic (optional)
- Register parser in language handler's
docstringParsersMap
Key Components:
-
LanguageHandler Interface (
src/languages/types.ts)- Defines contract for all language implementations
- Contains parser, docstring parsers, and analyzers
- Optional methods for language-specific behavior
-
LanguageHandlerRegistry (
src/languages/registry.ts)- Centralized registry for language handlers
- Methods:
register(),get(),isSupported(),resetCache() - Enables dynamic language registration
-
Language Factories (
src/languages/{language}/factory.ts)- Create and configure language-specific handlers
- Encapsulate initialization logic
- Example:
createPythonHandler(context)
Decision: Use Python subprocess instead of pure JavaScript parser
Rationale:
- Python's
astmodule is stdlib and battle-tested - Accurate type annotation extraction
- Easy exception tracking through AST
- No need to maintain custom Python parser
Trade-off: Startup latency (~50-100ms) vs parsing accuracy
Decision: Normalize common type aliases before comparison
Rationale:
- Users mix
str/stringin docstrings - Python typing evolved:
Optional[T]vsT | None - Better UX: fewer false positives
Trade-off: More complex comparison logic
Decision: Multiple return types as Information, not Warning
Rationale:
- Common pattern in Python (early returns, error handling)
- Not always incorrect (may need union type documentation)
- Helpful hint, not an error
User clicks Quick Fix (💡)
↓
Code Action Provider
- Filter diagnostics by source
- Route to appropriate fix provider
↓
Fix Provider (currently: ParameterFixProvider)
- Get diagnostic context
- Find function for diagnostic
↓
LLM Service (optional, if enabled)
- Generate parameter description
- Use GitHub Copilot Language Model API
- Cache results (LRU cache, 1000 items)
- Timeout: 5s (configurable)
- Fallback to "TODO" on failure
↓
Editor Handler Registry
- Get editor for language + style
- Create fresh editor instance (factory pattern)
↓
Docstring Editor (e.g., GoogleDocstringEditor)
- Load existing docstring
- Parse into editable structure
- Apply surgical edit:
* Add/remove parameter line
* Update type/optional marker
* Add/remove return section
* Add/remove exception entry
- Preserve all user descriptions
- Maintain formatting
↓
Create WorkspaceEdit
- Replace docstring range
- Apply to document
↓
Updated docstring in file
- Caching: Parse results cached per document version
- Debouncing: 500ms delay after typing stops
- Incremental: Only re-analyze changed functions
- Async: All operations non-blocking
- Parse: ~50-100ms for typical Python file
- Analyze: <10ms per function
- Display: Instant (VS Code built-in)
Target: <200ms total for typical file
- Covering all analyzers (signature, return, exception, side effects)
- Shared test utilities to eliminate duplication
- Both positive and negative cases
- Edge case coverage for type interactions
- StatusBarManager tests
- Editor and Code Actions tests
- For AST extractor
- Generator detection
- Async functions
- Multiple returns
- Error handling
Example files in examples/python/:
- Each diagnostic code has dedicated file
- Mix of incorrect (trigger) and correct (pass) examples
Centralized logging with:
- PII sanitization (file paths obfuscated)
- Configurable levels (ERROR/WARN/INFO/DEBUG/TRACE)
- Output channel in VS Code
Structured codes enable:
- Filtering in Problems panel
- User configuration (enable/disable rules)
- Analytics (which rules trigger most)
For implementation details and development history, see MVP.md.
(C) MIT, Andrey Krisanov 2025