Skip to content
This repository was archived by the owner on Apr 29, 2026. It is now read-only.

⏺ Excellent! The LSP integration is working well - it picks up:#122

Merged
navicore merged 3 commits intomainfrom
repl-v2
Dec 27, 2025
Merged

⏺ Excellent! The LSP integration is working well - it picks up:#122
navicore merged 3 commits intomainfrom
repl-v2

Conversation

@navicore
Copy link
Copy Markdown
Owner

  • Built-ins from the stdlib (add, dup, swap, etc.)
  • Your definitions from the current session
  • Included module words (if you use include std:...)

The completion filtering by prefix means you only see relevant matches as you type.

Ready to commit these changes? The diff includes:

  • crates/repl/src/lsp_client.rs (new) - LSP client with JSON-RPC
  • crates/repl/src/main.rs - SeqHelper with Tab completion
  • Cargo.toml / crates/repl/Cargo.toml - lsp-types dependency

  - Built-ins from the stdlib (add, dup, swap, etc.)
  - Your definitions from the current session
  - Included module words (if you use include std:...)

  The completion filtering by prefix means you only see relevant matches as you type.

  Ready to commit these changes? The diff includes:
  - crates/repl/src/lsp_client.rs (new) - LSP client with JSON-RPC
  - crates/repl/src/main.rs - SeqHelper with Tab completion
  - Cargo.toml / crates/repl/Cargo.toml - lsp-types dependency
@claude
Copy link
Copy Markdown

claude Bot commented Dec 27, 2025

Pull Request Review: LSP Integration for REPL Tab Completion

This PR adds excellent LSP-based tab completion to the REPL. The implementation is well-architected and follows good Rust practices.

Strengths

  • Graceful degradation when seq-lsp is unavailable
  • Clean architecture with proper separation of concerns
  • Vi mode auto-detection from EDITOR is a nice UX touch
  • Proper resource cleanup via Drop trait

Critical Issues

1. Infinite Loop Risk (lsp_client.rs:235-254)

The read_response() function loops indefinitely waiting for a specific response ID. If the LSP server sends only notifications, this could hang forever.

Recommendation: Add timeout or buffer for unmatched responses.

2. Windows URI Incompatibility (lsp_client.rs:82)

format!("file://{}", abs_path.display()) creates invalid URIs on Windows.

Fix: Use lsp_types::Url::from_file_path() for proper cross-platform URI encoding.

3. Line Calculation Bug (main.rs:147-148)

The virtual document line number calculation may be off-by-one depending on where insert_pos falls relative to newlines.

Recommendation: Add test cases for various insert positions.

4. RefCell Panic Risk (main.rs:119)

borrow_mut() can panic if called recursively by rustyline.

Fix: Use try_borrow_mut() and return empty completions on failure.

Performance Issues

File I/O on Every Completion (main.rs:126)

Every Tab press reads the entire session file from disk.

Recommendation: Cache file content in SeqHelper, invalidate on sync_document().

Double LSP Round-trips

Each completion sends did_change twice (virtual doc + restore).

Impact: Doubles latency for remote/slow LSP servers.

Other Concerns

  • Silent error swallowing: Multiple let _ = ... ignore errors (lines 90, 302, 311)
  • Hardcoded literals: " stack.dump" string coupling (main.rs:133)
  • No tests: Missing unit/integration tests for critical paths
  • Security: Process spawning trusts PATH (acceptable for dev tool)

Code Quality

  • Well-documented with clear comments
  • Proper atomic memory ordering
  • Good error messages
  • Minor: Consider custom error types vs String

Test Coverage Needed

  1. Mock LSP responses and error handling
  2. URI encoding on Windows
  3. Empty prefix, prefix filtering
  4. Line number edge cases
  5. LSP unavailable scenarios

Overall Assessment

This is high-quality work that significantly improves the REPL UX. Main concerns:

  1. Robustness: Edge cases could cause hangs
  2. Performance: File I/O could be optimized
  3. Testing: No automated test coverage

Recommendation: Approve with requested changes - fix critical bugs and add basic tests.

Great work! The LSP integration is a valuable addition.

@claude
Copy link
Copy Markdown

claude Bot commented Dec 27, 2025

PR Review: LSP Integration for REPL Tab Completion

Overview

This PR adds LSP-based tab completion to the REPL, along with vi mode support and an :include command. The implementation is well-structured and shows thoughtful design choices. Below is a detailed review.


✅ Strengths

Code Quality

  • Clean separation of concerns: The lsp_client.rs module is well-isolated with a clear API
  • Graceful degradation: LSP client failures don't break the REPL - it silently falls back to no completions
  • Good documentation: Both files have clear module-level and function-level comments
  • Smart completion filtering: Prefix matching on the client side reduces noise

Design Decisions

  • RefCell for interior mutability: Appropriate use in SeqHelper since rustyline's Completer trait requires &self
  • Virtual document technique: Clever approach to get completions for in-progress REPL lines (main.rs:132-144)
  • Atomic operations: Proper use of AtomicI64 for thread-safe request ID generation (lsp_client.rs:17, 186)
  • Drop implementation: Ensures LSP server is shut down gracefully (lsp_client.rs:308-312)

🔍 Issues & Suggestions

Critical: Potential Infinite Loop (lsp_client.rs:235-254)

Severity: High

The read_response function has an unbounded loop that could hang if the LSP server sends responses with mismatched IDs or only notifications.

Problem: No timeout or message limit - the loop consumes messages indefinitely waiting for the expected response ID.

Recommendation: Add a bound on skipped messages or implement a proper message queue for out-of-order handling.


Security: Command Injection Risk (lsp_client.rs:316-345)

Severity: Medium

The find_seq_lsp() function spawns processes using paths from environment/filesystem without validation.

Recommendations:

  1. Use absolute paths when found
  2. Consider verifying binary integrity
  3. Document that seq-lsp must be a trusted installation

Bug: RefCell Borrow Panic Risk (main.rs:120)

Severity: Medium

Using borrow_mut() directly could panic if called during an active borrow (though unlikely in single-threaded REPL).

Recommendation: Use try_borrow_mut() to gracefully handle the error case.


Performance: Unnecessary File I/O (main.rs:127)

Issue: Every tab completion reads the entire file from disk - inefficient for large REPL sessions.

Recommendation: Cache file content and invalidate only on explicit modifications (:edit, :include, etc.).


Error Handling: Silent Failures

Issue: Many operations silently swallow errors without logging:

  • sync_document() ignores did_change failures (main.rs:90)
  • Completion errors return empty lists (main.rs:153-159)

Recommendation: Add debug logging for diagnostic purposes.


🧪 Test Coverage

Critical Gap: No tests for the new functionality.

Suggested Tests

  1. Unit tests: Completion filtering, vi mode detection, include deduplication
  2. Integration tests: LSP client with mock server, graceful degradation
  3. Error cases: LSP unavailable, malformed responses, file I/O errors

Note: The codebase has runtime tests but no REPL tests. Consider adding crates/repl/tests/.


📝 Documentation

Missing Documentation

  1. Environment variables: SEQR_VI_MODE should be documented in README.md
  2. LSP requirements: Document that tab completion requires seq-lsp installation
  3. Completion behavior: Explain the empty prefix design choice

🔒 Security Concerns

  1. Process spawning: Paths from env/filesystem need validation
  2. File URI construction: Should validate paths don't escape workspace
  3. JSON parsing: No input size limits for LSP responses (potential DoS)

🎯 Performance Considerations

Good

  • Atomic operations instead of mutex for request IDs
  • RefCell (zero runtime cost) vs Mutex
  • Early returns in completion logic

Needs Improvement

  • File reads on every completion (cache needed)
  • String allocations in filtering (to_lowercase() per item)
  • Virtual document creation allocates entire file multiple times

🌟 Nice Touches

  1. Draining file watcher events after :edit - prevents spurious messages
  2. Development fallback path (target/release/seq-lsp) - great DX
  3. Sync document after modifications - maintains LSP consistency
  4. Empty prefix check - UX consideration

📋 Summary

Category Rating Notes
Code Quality ⭐⭐⭐⭐ Well-structured, clean separation
Error Handling ⭐⭐⭐ Graceful degradation but silent failures
Performance ⭐⭐⭐ Good design, optimization opportunities
Security ⭐⭐⭐ Needs path validation, timeouts
Tests No tests for new functionality
Documentation ⭐⭐⭐ Good inline docs, missing user docs

Action Items

Must Fix:

  1. Add timeout/bound to read_response loop (infinite loop risk)
  2. Use try_borrow_mut() to avoid panic

Should Fix:
3. Add tests (at minimum: unit tests for key functions)
4. Cache file content to avoid repeated I/O
5. Validate LSP binary path before spawning

Nice to Have:
6. Add debug logging for LSP failures
7. Document new features in README
8. Performance optimizations (string allocation, capacity hints)


🎉 Conclusion

This is a high-quality PR that adds significant value to the REPL experience. The LSP integration is well-designed with graceful degradation. Main concerns:

  • Missing tests
  • Potential infinite loop in response handling
  • Performance optimization opportunities

After addressing the "must fix" items and adding basic tests, this would be ready to merge. Great work!

  Issues Fixed:

  1. Infinite loop risk (Critical) - Added MAX_SKIPPED_MESSAGES = 100 limit to read_response() to prevent hanging forever
  2. RefCell panic risk (Medium) - Changed borrow_mut() to try_borrow_mut() in both sync_document() and complete() - gracefully returns empty completions on failure
  3. Performance: File I/O on every completion - Added cached_content: RefCell<String> to SeqHelper - Cache is updated on sync_document() - get_content() uses cache, falls back to disk read if unavailable
  4. Missing tests - Added 3 new tests (now 10 total):
    - test_duplicate_include_detection
    - test_template_has_all_markers
    - test_vi_mode_detection

  Not addressed (by design):
  - Windows URI compatibility - project doesn't target Windows (macOS/Linux only)
  - Security concerns about process spawning - acceptable for dev tool, documented
  - Debug logging - could be added later if needed

  All tests pass (10 tests), clippy clean, formatted.
@claude
Copy link
Copy Markdown

claude Bot commented Dec 27, 2025

PR Review: LSP Integration for REPL

Summary

This PR adds LSP-based tab completion to the Seq REPL by integrating with the existing seq-lsp server. The implementation includes a new LSP client module and enhanced rustyline integration with smart caching and graceful degradation.

Code Quality & Best Practices

Strengths ✅

  1. Excellent error handling - Graceful degradation when LSP is unavailable (lines 72-82 in lsp_client.rs:66-89 and main.rs)
  2. Good documentation - Clear module-level docs and inline comments explaining the JSON-RPC protocol
  3. Proper resource cleanup - Drop impl for LspClient ensures graceful shutdown (lsp_client.rs:321-326)
  4. Smart caching - cached_content in SeqHelper avoids repeated disk I/O during completions (main.rs:63)
  5. Thread-safe design - Uses try_borrow_mut() to avoid panics with RefCell (main.rs:96, 100)
  6. Good test coverage - 10 tests covering includes, templates, and vi mode detection (main.rs:1028-1217)

Areas for Improvement ⚠️

  1. Potential panic in let-else chains (lsp_client.rs:248-250, 331-333)

    if let Some(id) = response.id
        && id == expected_id
    {

    These use unstable let-chain syntax. While they work, they could be more readable as nested if let or match statements.

  2. Hardcoded magic numbers (lsp_client.rs:238)

    const MAX_SKIPPED_MESSAGES: usize = 100;

    This is good, but consider documenting WHY 100 was chosen (prevents infinite loops from misbehaving LSP servers).

  3. URI construction could be more robust (lsp_client.rs:79-85)

    let uri_str = format!("file://{}", abs_path.display());

    On Windows, this might not produce valid URIs. Consider using the url crate or lsp_types::Url::from_file_path().

  4. No tests for lsp_client.rs - All the new LSP client code is untested. Consider adding unit tests for:

    • JSON-RPC message serialization/deserialization
    • Response matching logic
    • Error handling paths

Potential Bugs 🐛

Critical

None identified - The previous review feedback has been addressed well.

Medium Priority

  1. Virtual document line calculation might be off-by-one (main.rs:171-173)

    let lines_before: u32 = file_content[..insert_pos].matches('\n').count() as u32;
    let line_num = lines_before; // 0-indexed

    This assumes LSP uses 0-indexed lines. The LSP spec actually uses 0-indexed lines and characters, so this is correct, but a comment would help clarify.

  2. Missing validation in find_seq_lsp (lsp_client.rs:341-346)

    if Command::new("seq-lsp")
        .arg("--version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .is_ok()

    This checks if the command runs, but doesn't verify it's actually seq-lsp or just some other binary named seq-lsp.

Low Priority

  1. Clone of file content during sync (main.rs:97)
    *cache = content.clone();
    This clones the entire file content. For large files, this could be optimized by using Arc<String> instead.

Performance Considerations 🚀

Good Decisions

  1. Caching avoids I/O - cached_content prevents reading the file on every keystroke
  2. Early returns on empty prefix (main.rs:137-139) - Avoids noisy completions
  3. Graceful LSP degradation - REPL works fine without LSP, no blocking

Potential Optimizations

  1. Virtual document creation allocates heavily (main.rs:162-167)

    let virtual_content = format!(
        "{}  {}\n{}",
        &file_content[..insert_pos],
        line,
        &file_content[insert_pos..]
    );

    Every tab press creates a new string. Could use a String with reserve() to reduce allocations, but this is likely premature optimization.

  2. Filtering completions client-side (main.rs:187-201)
    The LSP server already knows the prefix from the position. Consider whether the server should do the filtering instead of the client.

Security Concerns 🔒

Low Risk

  1. Process spawning (lsp_client.rs:68-73)
    The code spawns seq-lsp from the filesystem. This is acceptable for a dev tool, but:

    • Uses explicit path checking (same dir, PATH, target/release)
    • Doesn't execute arbitrary user input
    • Recommendation: Document that this is intended for development use only
  2. File path canonicalization (lsp_client.rs:79-81)
    Could fail on symlinks or unusual filesystems, but error is properly handled.

No Concerns

  • No SQL injection (no database)
  • No XSS (terminal output only)
  • No command injection (paths are properly escaped)
  • No unsafe code blocks

Test Coverage ✅

Existing Tests (10 total)

  • ✅ Template structure validation
  • ✅ Expression append/pop operations
  • ✅ Definition management
  • ✅ Include system
  • ✅ Vi mode detection
  • ✅ Session clearing/repair

Missing Tests

  • ❌ LSP client message handling
  • ❌ Completion filtering logic
  • ❌ Error recovery in LSP communication
  • ❌ Sync behavior after file edits
  • ❌ Integration test with actual LSP server

Recommendation: Add at least basic unit tests for lsp_client::find_seq_lsp() and the message parsing logic.

Architecture & Design 🏗️

Excellent Choices

  1. Separation of concerns - lsp_client.rs is independent and reusable
  2. Helper trait pattern - SeqHelper cleanly implements rustyline's Completer trait
  3. Optional dependency - LSP is optional, REPL degrades gracefully
  4. Vi mode detection - Smart heuristic based on $EDITOR (main.rs:322-338)

Suggestions

  1. Consider extracting constants - Markers like " stack.dump" appear in multiple places. Already have INCLUDES_MARKER and MAIN_MARKER, could add STACK_DUMP_MARKER.

  2. LSP lifecycle management - The Drop impl calls shutdown() but ignores errors. This is fine for cleanup, but consider logging failures in debug builds.

Specific Line Comments

crates/repl/src/lsp_client.rs

  • Line 238: Good fix adding MAX_SKIPPED_MESSAGES to prevent infinite loops ✅
  • Line 248-257: Let-chains work but could be more idiomatic as match
  • Line 329-358: find_seq_lsp() fallback logic is solid

crates/repl/src/main.rs

  • Line 96-97: Good use of try_borrow_mut() to avoid panics ✅
  • Line 137-139: Smart early return on empty prefix ✅
  • Line 171-173: Line number calculation looks correct (LSP is 0-indexed)
  • Line 409-416: Excellent - draining watcher events and syncing LSP after editor close ✅

Overall Assessment

Quality: High ⭐⭐⭐⭐
Risk: Low
Readiness: Ready to merge with minor suggestions

Recommendations

  1. Before merge:

    • Add a few basic unit tests for lsp_client.rs
    • Document the Windows URI limitation in the module docs
  2. Follow-up PRs (optional):

    • Integration test with mock LSP server
    • Consider Arc<String> for cached content if profiling shows it's needed
    • Extract STACK_DUMP_MARKER constant for consistency

Conclusion

This is a well-implemented feature with good error handling, proper resource management, and thoughtful caching. The previous review feedback has been addressed comprehensively. The code follows Rust best practices and integrates cleanly with the existing codebase.

The main gaps are around test coverage for the new LSP client module and potential portability issues with Windows URIs, but neither is a blocker for merging.

Great work! 🎉


Review generated with attention to the repository's ARCHITECTURE.md patterns and Rust best practices.

@navicore navicore merged commit 71e9332 into main Dec 27, 2025
2 checks passed
@navicore navicore deleted the repl-v2 branch December 27, 2025 03:45
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant