Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

LSP Integration

Refactor DSL integrates with Language Server Protocol (LSP) servers to provide semantic refactoring capabilities that understand your code’s types, scopes, and references.

Why LSP?

While text-based and AST transforms are powerful, they lack semantic understanding:

FeatureText/ASTLSP
Find text patternsYesYes
Understand syntaxAST onlyYes
Understand typesNoYes
Cross-file referencesNoYes
Rename with importsNoYes
Find all usagesLimitedYes

Quick Start

#![allow(unused)]
fn main() {
use refactor::lsp::LspRename;

// Rename a symbol semantically
let result = LspRename::new("src/main.rs", 10, 4, "new_function_name")
    .auto_install()  // Download LSP server if needed
    .dry_run()       // Preview changes
    .execute()?;

println!("Would modify {} files:", result.file_count());
println!("{}", result.diff()?);
}

Supported Languages

Out of the box, Refactor DSL supports:

LanguageLSP ServerExtensions
Rustrust-analyzer.rs
TypeScript/JavaScripttypescript-language-server.ts, .tsx, .js, .jsx
Pythonpyright.py, .pyi
Gogopls.go
C/C++clangd.c, .h, .cpp, .hpp, .cc, .cxx

Components

LspRegistry

Manages LSP server configurations for different languages:

#![allow(unused)]
fn main() {
let registry = LspRegistry::new();  // Includes defaults
let config = registry.find_for_file(Path::new("src/main.rs"));
}

LspInstaller

Downloads and installs LSP servers from the Mason registry:

#![allow(unused)]
fn main() {
let installer = LspInstaller::new()?;
let binary = installer.install("rust-analyzer")?;
}

LspRename

Performs semantic rename operations:

#![allow(unused)]
fn main() {
LspRename::find_symbol("src/lib.rs", "old_name", "new_name")?
    .execute()?;
}

LspClient

Low-level client for LSP communication:

#![allow(unused)]
fn main() {
let mut client = LspClient::start(&config, &root_path)?;
client.initialize()?;
client.open_document(path)?;
let edit = client.rename(path, position, "new_name")?;
}

Architecture

┌─────────────────────────────────────────────────┐
│                  LspRename                       │
│  (High-level semantic rename API)               │
├─────────────────────────────────────────────────┤
│                                                  │
│  ┌─────────────┐  ┌─────────────┐               │
│  │ LspRegistry │  │ LspInstaller│               │
│  │             │  │             │               │
│  │ Find server │  │ Download    │               │
│  │ for file    │  │ from Mason  │               │
│  └─────────────┘  └─────────────┘               │
│                                                  │
├─────────────────────────────────────────────────┤
│                  LspClient                       │
│  (JSON-RPC communication with LSP server)       │
├─────────────────────────────────────────────────┤
│                                                  │
│  rust-analyzer │ tsserver │ pyright │ ...       │
│                                                  │
└─────────────────────────────────────────────────┘

Capabilities

Currently supported LSP operations:

  • Rename - Rename symbols across files with import updates
  • Find References - Locate all usages of a symbol
  • Go to Definition - Find symbol definitions

Future planned operations:

  • Extract function/method
  • Inline variable
  • Move to file
  • Organize imports

Error Handling

#![allow(unused)]
fn main() {
use refactor::error::RefactorError;

match LspRename::new("file.rs", 10, 4, "new_name").execute() {
    Ok(result) => println!("Renamed in {} files", result.file_count()),
    Err(RefactorError::UnsupportedLanguage(ext)) => {
        println!("No LSP server for .{} files", ext);
    }
    Err(RefactorError::TransformFailed { message }) => {
        println!("LSP error: {}", message);
    }
    Err(e) => return Err(e.into()),
}
}

See Also