Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@tommcd
Copy link
Owner

@tommcd tommcd commented Nov 18, 2025

No description provided.

Implement Task 1 from the document-outliner-with-lsp Kiro spec.

Created:
- Directory structure for VS Code extension (extensions/doctk-outliner/)
- Directory structure for language server (src/doctk/lsp/)
- TypeScript interfaces for tree nodes and operations (types.ts)
- Python protocols for document operations (protocols.py)
- Build configuration files (tsconfig.json, package.json)
- Language configuration for doctk DSL

This establishes the foundational structure for the outliner extension
and LSP server, satisfying Requirements 15 and 20.

Task: .kiro/specs/document-outliner-with-lsp/tasks.md#1
Requirements: 15, 20
Implement Task 2 from the document-outliner-with-lsp Kiro spec.

Created comprehensive document structure operations:
- StructureOperations class with all operations
- DocumentTreeBuilder for node mapping and lookup
- promote() and demote() to adjust heading levels (1-6)
- move_up() and move_down() to reorder siblings
- nest() to move sections under new parents
- unnest() to move sections up one level
- Validation methods for all operations

Key features:
- Immutable operations (returns new Document instances)
- Node ID-based addressing (h1-0, h2-1, etc.)
- Edge case handling (level limits, boundary checks)
- Error handling with descriptive messages

Testing:
- 34 comprehensive unit tests
- Test coverage: 77.78% for operations.py
- Tests for all operations, edge cases, and immutability
- Complex scenario tests with mixed document types

All tests pass successfully.

Task: .kiro/specs/document-outliner-with-lsp/tasks.md#2
Requirements: 2.2, 2.3, 3.2, 3.3, 3.4, 3.5, 20
Implement Task 3 from the document-outliner-with-lsp Kiro spec.

Python side (JSON-RPC bridge):
- ExtensionBridge class with JSON-RPC 2.0 protocol
- Handles all document operations via stdin/stdout
- Method routing for promote, demote, move_up, move_down, nest, unnest
- Validation methods for all operations
- Comprehensive error handling and serialization
- Main entry point for standalone execution

TypeScript side (PythonBridge client):
- PythonBridge class that spawns Python process via uv
- JSON-RPC request/response handling over stdio
- Promise-based async API for all operations
- Process lifecycle management (start, stop, restart)
- Automatic restart on crash (up to 3 attempts)
- Request timeout handling (default 10s)
- Buffered line-based communication
- Convenience methods for all operations

Testing:
- 18 comprehensive integration tests
- Test coverage: 67.96% for bridge.py
- Tests for all operations (promote, demote, move_up, move_down, nest, unnest)
- Tests for validation operations
- Error handling tests (invalid JSON-RPC, unknown methods, missing params)
- Complex scenario tests (operation chains, roundtrips, reordering)
- Error recovery tests

Key features:
- Standard JSON-RPC 2.0 protocol
- Clean separation of concerns
- Type-safe TypeScript interface
- Robust error handling
- Automatic process management

All 18 tests pass successfully.

Task: .kiro/specs/document-outliner-with-lsp/tasks.md#3
Requirements: 18, 20
Implement Task 4 from the document-outliner-with-lsp Kiro spec.

VS Code Extension Components:

1. DocumentOutlineProvider (outlineProvider.ts - 230 lines):
   - Implements TreeDataProvider<OutlineNode> interface
   - Parses Markdown documents to extract heading hierarchy
   - Builds OutlineNode tree with unique IDs (h1-0, h2-1, etc.)
   - Tracks node ranges for navigation
   - Provides getTreeItem(), getChildren(), getParent() methods
   - Icons based on heading level (h1-h6)
   - Tooltips with node metadata
   - Debounced refresh mechanism (300ms)
   - Document update synchronization

2. Extension Entry Point (extension.ts - 210 lines):
   - Activates extension for Markdown files
   - Registers tree view in Explorer sidebar
   - Initializes PythonBridge for backend operations
   - Registers all commands (promote, demote, move_up, move_down, etc.)
   - Navigate to node command for clicking tree items
   - Listens for editor changes and document updates
   - Executes operations via Python bridge
   - Applies workspace edits with undo support
   - Error handling and user notifications

Testing (test_outline_integration.py - 400+ lines):
- 17 comprehensive integration tests
- All tests passing

Test categories:
- Tree building from various structures (simple, nested, complex)
- Node ID generation and uniqueness
- Mixed content with paragraphs
- Markdown parsing integration
- Operation integration (promote, demote, move)
- Edge cases (empty docs, skipped levels, reverse order)

Key features:
- ✅ Complete TreeDataProvider implementation
- ✅ Markdown parsing with heading extraction
- ✅ Hierarchical tree structure
- ✅ Unique node ID generation
- ✅ Range tracking for navigation
- ✅ Debounced refresh (300ms)
- ✅ Document synchronization
- ✅ Command integration
- ✅ Python bridge integration
- ✅ Workspace edit support
- ✅ Comprehensive testing

All 17 tests pass successfully.

Task: .kiro/specs/document-outliner-with-lsp/tasks.md#4
Requirements: 1.1, 1.2, 1.3, 1.4, 16.1, 16.2, 16.3
This commit addresses all issues identified in the Gemini Code Assist review
of PR #5 (#5 (review)).

CRITICAL FIXES:
- Fix move_up/move_down/nest operations to move entire sections (heading + content)
  instead of just heading nodes. Added get_section_range() helper method to
  DocumentTreeBuilder to identify complete sections. This prevents document corruption
  where content would end up under the wrong heading. (operations.py:70-100, 293-450)

HIGH PRIORITY FIXES:
- Implement handshake mechanism in waitForReady to avoid race conditions. Python bridge
  now prints "BRIDGE_READY" when initialized, and TypeScript waits for this signal
  before resolving the start promise. (bridge.py:313, pythonBridge.ts:348-366)

MEDIUM PRIORITY FIXES:
- Add proper TypeScript type annotations for operation methods. Created ValidationResult
  interface and updated all operation/validation methods to use OperationResult/
  ValidationResult instead of Promise<any>. Made call() method generic. (types.ts:89-97,
  pythonBridge.ts:7, 143, 183-235)

- Remove keybindings from DoctkConfiguration interface. Keybindings are contributions
  in package.json, not user-configurable settings from workspace.getConfiguration().
  (types.ts:104-115)

- Add traceback logging to error handling in bridge.py for easier debugging. Full
  traceback is now printed to stderr when exceptions occur. (bridge.py:7, 61)

- Simplify operation method signatures to return only OperationResult instead of
  redundant tuple[Document[Node], OperationResult]. Updated all callers in bridge.py
  to handle single return value. (operations.py:105, 157, 257, 348, 489, 589;
  bridge.py:107-173)

- Move local imports to top of file in operations.py. Converted TYPE_CHECKING imports
  to regular imports for better readability and to avoid hiding dependencies.
  (operations.py:3-8)

TEST UPDATES:
- Updated all LSP operation tests to handle new return signature (single OperationResult
  instead of tuple). All 34 tests now pass. (test_lsp_operations.py)

Verified with: uv run pytest tests/unit/test_lsp_operations.py -v
Result: 34 passed in 1.49s
- Remove unused imports (Optional, Node, OperationResult) from bridge.py
- Apply ruff auto-fixes for docstring formatting
- Update test_outline_integration.py tests to use new API (3 tests)

All 99 unit tests passing.
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @tommcd, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers the foundational implementation for the doctk Document Outliner VS Code extension, integrating a Language Server Protocol (LSP) backend. It establishes the complete project structure for the extension, defines its user interface components and commands, and sets up a robust communication bridge between the TypeScript frontend and Python backend. The core document manipulation functionalities, such as promoting, demoting, moving, nesting, and unnesting document sections, are now fully implemented and thoroughly tested, marking a significant milestone towards a functional document outlining tool.

Highlights

  • Feature Completion: All initial development tasks for the doctk Document Outliner feature, including project setup, core document manipulation API, TypeScript-Python communication bridge, and VS Code tree data provider, have been marked as completed.
  • VS Code Extension Implementation: A new VS Code extension (extensions/doctk-outliner/) has been added, defining its manifest (package.json), language configuration (language-configuration.json), main activation logic (extension.ts), outline tree view provider (outlineProvider.ts), and TypeScript type definitions (types.ts).
  • Python Backend Integration (LSP): The Python backend (src/doctk/lsp/) now includes an ExtensionBridge for JSON-RPC communication, StructureOperations for document manipulation (promote, demote, move, nest, unnest), and protocol definitions, enabling robust interaction with the VS Code frontend.
  • Comprehensive Testing: New unit tests have been added for both the Python ExtensionBridge and StructureOperations, as well as integration tests for the outline tree building, ensuring the reliability and correctness of the new LSP components.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant amount of new functionality for a VS Code extension, including the extension's frontend in TypeScript and a Python backend for document operations. The overall structure is well-organized, with clear separation of concerns between the UI, the Python bridge, and the core logic. However, there are several critical and high-severity issues that should be addressed. These include a major user experience problem with how document updates are applied, a non-functional command being exposed to users, and a fragile dependency on a non-standard Python tool. I've also included some medium-severity suggestions to improve type safety and maintainability.

Comment on lines 169 to 175
const edit = new vscode.WorkspaceEdit();
const fullRange = new vscode.Range(
document.positionAt(0),
document.positionAt(documentText.length)
);
edit.replace(document.uri, fullRange, result.document);
await vscode.workspace.applyEdit(edit);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Replacing the entire document content on every operation is highly inefficient and leads to a poor user experience. It has several major drawbacks:

  • Clears Undo/Redo Stack: The user loses their entire undo/redo history for the file.
  • Loses Cursor Position and Selections: The user's cursor position and any text selections are lost.
  • Performance: For large documents, sending the entire content to the Python backend and then replacing it in the editor can be slow.

Instead of replacing the whole document, you should apply more granular changes. The backend should ideally return a set of edits (e.g., diffs or specific ranges to replace) that can be applied via vscode.WorkspaceEdit. The OperationResult type already includes a modifiedRanges property, which suggests this was considered. This approach needs to be fully implemented on both the backend and frontend.

Comment on lines 50 to 54
{
"command": "doctk.delete",
"title": "doctk: Delete Section",
"icon": "$(trash)"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The doctk.delete command is registered here, making it visible in the context menu and available via keybinding. However, the implementation in src/extension.ts is just a placeholder that shows an information message. Exposing non-functional commands to the user can be confusing. It's better to remove the command from the UI contributions until it is fully implemented.


constructor(options: PythonBridgeOptions = {}) {
this.options = {
pythonCommand: options.pythonCommand || 'uv',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Defaulting the pythonCommand to uv is not robust, as uv is a relatively new tool and is not guaranteed to be installed on a user's machine. This will cause the extension to fail for many users.

A safer default would be python3 or python. Ideally, this should also be a user-configurable setting in the extension's configuration (package.json) so users can specify a path to their preferred Python executable or virtual environment.

Suggested change
pythonCommand: options.pythonCommand || 'uv',
pythonCommand: options.pythonCommand || 'python3',

* @param operation - Operation name
* @param node - Target node
*/
async function executeOperation(operation: string, node: any): Promise<void> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The node parameter is typed as any. This weakens the type safety of the function. For better maintainability and to leverage TypeScript's static analysis, you should use the specific OutlineNode type defined in src/types.ts.

This also applies to the command handlers where node is received, for example on lines 64, 70, 76, 82, and 88.

Suggested change
async function executeOperation(operation: string, node: any): Promise<void> {
async function executeOperation(operation: string, node: OutlineNode): Promise<void> {

Comment on lines +157 to +160
// Generate unique ID
const count = headingCounters.get(level) || 0;
headingCounters.set(level, count + 1);
const id = `h${level}-${count}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for generating unique IDs for heading nodes is duplicated in the frontend (here) and the Python backend (src/doctk/lsp/operations.py in DocumentTreeBuilder). This creates a tight coupling and a maintainability issue, as any change to the ID generation scheme must be synchronized in both places.

Consider making the Python backend the single source of truth for the document structure and node IDs. The frontend could request the document tree from the backend, and then use the received IDs for subsequent operations. This would centralize the parsing and ID logic, reduce code duplication, and improve the overall architecture.

This commit addresses HIGH and MEDIUM priority issues from the Gemini Code
Assist review of PR #6.

HIGH PRIORITY FIXES (2):
- Remove non-functional delete command from UI
  - Removed from command contributions in package.json
  - Removed from context menus and keybindings
  - Removed placeholder command handler from extension.ts

- Change Python command default from 'uv' to 'python3'
  - Updated default in pythonBridge.ts constructor
  - Added configurable setting 'doctk.lsp.pythonCommand' in package.json
  - Extension now reads configuration on startup
  - More robust default that works on most systems

MEDIUM PRIORITY FIX (1):
- Add proper TypeScript typing for node parameters
  - Imported OutlineNode type from types.ts
  - Replaced all 'node: any' with 'node: OutlineNode' (6 occurrences)
  - Improves type safety and maintainability

REMAINING ISSUES (2):
- CRITICAL: Document replacement approach (requires significant refactoring)
- MEDIUM: ID generation duplication (requires architectural changes)

All 99 unit tests passing.
Add detailed design and implementation plan for 2 remaining PR #6 review issues:

1. CRITICAL: Granular document edits (Issue #1)
   - Added 'Optimized Edits (CRITICAL IMPROVEMENT)' section to design.md
   - Explains problem: full document replacement clears undo/redo, loses cursor
   - Solution: Backend computes ModifiedRange[], frontend applies granular edits
   - Added 5 subtasks (22.5.1-22.5.5) to tasks.md with implementation steps

2. MEDIUM: Centralize ID generation (Issue #5)
   - Added 'Centralized Node ID Generation' section to design.md
   - Explains problem: Duplicate ID logic in frontend and backend
   - Solution: Backend as single source of truth for tree structure & IDs
   - Added 4 subtasks (22.6.1-22.6.4) to tasks.md with implementation steps

Both sections include:
- Problem statement and impact
- Proposed solution with code examples
- Design rationale
- Backend and frontend changes
- Testing requirements

This provides clear roadmap for addressing the remaining architectural issues
identified in #6 (review)
Implement ModifiedRange-based edits to preserve cursor position and undo/redo
stack. This addresses the CRITICAL issue from PR #6 review.

Backend changes:
- Add ModifiedRange dataclass to protocols.py with line/column positions
- Implement DiffComputer class to compute changed text ranges
- Update all operations (promote, demote, move_up, move_down, nest, unnest)
  to return modified_ranges in addition to full document
- Update bridge.py to serialize ModifiedRange objects to JSON

Frontend changes:
- Add ModifiedRange interface to types.ts
- Update extension.ts executeOperation() to use granular edits when available
- Fall back to full document replacement if modified_ranges not provided
- This preserves cursor position and undo/redo stack

Design changes:
- Remove Approach 2 (Backend ID Service) from design.md
- Keep only Backend-Driven Tree Structure approach for centralized IDs

All 34 existing tests pass. Granular edits improve UX by:
- Preserving cursor position during operations
- Maintaining VS Code undo/redo stack
- Better performance for large documents

Related: PR #6 review issue #1 (CRITICAL)
@tommcd tommcd merged commit dc0d711 into master Nov 18, 2025
3 checks passed
tommcd pushed a commit that referenced this pull request Nov 18, 2025
Implement Task 9 from vscode-outliner-extension spec to centralize
node ID generation in the backend, ensuring consistency across all
operations and eliminating duplicate ID generation logic.

Changes:
- Added getDocumentTree() method to PythonBridge TypeScript class to
  request tree structure with backend-assigned IDs via JSON-RPC
- Added BackendTreeNode and DocumentTreeResponse type definitions to
  support backend tree serialization format
- Updated DocumentOutlineProvider to use backend tree when available:
  - Added pythonBridge constructor parameter
  - Implemented deserializeBackendTree() method to convert backend
    TreeNode structure to frontend OutlineNode format
  - Modified updateFromDocument() to call backend getDocumentTree()
    with fallback to local parsing if backend unavailable
- Updated extension.ts to initialize DocumentOutlineProvider with
  pythonBridge for centralized ID generation
- Kept local parseDocument() as robust fallback when backend offline
- Fixed Python code formatting in dsl/executor.py and dsl/lexer.py
  (pre-existing formatting issues found during quality checks)

Task completion:
- Task 9.1: Frontend now requests tree from backend ✓
- Task 9.2: Backend tests already comprehensive (25 tests) ✓
- Task 8.1: Granular edits already implemented ✓

Tests:
- All 168 Python tests pass (2 skipped)
- Backend has 13 tests for build_tree_with_ids
- Backend has 12 tests for get_document_tree RPC method
- Ruff linting and formatting checks pass

This addresses PR #6 Issue #5 (Medium priority) and ensures node IDs
are generated consistently by the backend, preventing ID mismatches
between tree view and operations.
@tommcd tommcd deleted the claude/address-pr-review-feedback-019YyryeLApHbc72HYuxLdry branch November 22, 2025 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants