### Install VSIX Extension Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Installs the packaged .vsix extension file into VS Code. ```bash # 6. Install the extension code --install-extension l-language-server-1.5.0.vsix ``` -------------------------------- ### initialize Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Declares all capabilities the server supports to the connecting editor client. Called once at the start of every LSP session. ```APIDOC ## `initialize` — Server Capability Declaration Declares all capabilities the server supports to the connecting editor client. Called once at the start of every LSP session. ```rust async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { server_info: None, offset_encoding: None, capabilities: ServerCapabilities { // Full-document sync: server receives the complete text on every change text_document_sync: Some(TextDocumentSyncCapability::Options( TextDocumentSyncOptions { open_close: Some(true), change: Some(TextDocumentSyncKind::FULL), save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions { include_text: Some(true), })), ..Default::default() }, )), hover_provider: Some(HoverProviderCapability::Simple(true)), definition_provider: Some(OneOf::Left(true)), references_provider: Some(OneOf::Left(true)), rename_provider: Some(OneOf::Left(true)), document_formatting_provider: Some(OneOf::Left(true)), inlay_hint_provider: Some(OneOf::Left(true)), completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![".".to_string()]) // fires on '.' ..Default::default() }), semantic_tokens_provider: Some( SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions( SemanticTokensRegistrationOptions { text_document_registration_options: TextDocumentRegistrationOptions { document_selector: Some(vec![DocumentFilter { language: Some("l".to_string()), scheme: Some("file".to_string()), pattern: None, }]), }, semantic_tokens_options: SemanticTokensOptions { legend: SemanticTokensLegend { // Index 0=FUNCTION 1=VARIABLE 2=PARAMETER 3=STRUCT 4=PROPERTY token_types: vec![ SemanticTokenType::FUNCTION, SemanticTokenType::VARIABLE, SemanticTokenType::PARAMETER, SemanticTokenType::STRUCT, SemanticTokenType::PROPERTY, ], token_modifiers: vec![], }, range: Some(true), full: Some(SemanticTokensFullOptions::Bool(true)), ..Default::default() }, ..Default::default() }, ), ), ..ServerCapabilities::default() }, }) } ``` ``` -------------------------------- ### Rust LSP Server Initialization and Runtime Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Sets up the logger, initializes the LSP service with the Backend state, and starts the server to handle stdio communication. Reads RUST_LOG env var for logging level. ```rust #[tokio::main] async fn main() { env_logger::init(); // reads RUST_LOG env var (e.g. RUST_LOG=debug) let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); let (service, socket) = LspService::build(|client| Backend { client, document_map: DashMap::new(), semanticast_map: DashMap::new(), }) .finish(); Server::new(stdin, stdout, socket).serve(service).await; } ``` -------------------------------- ### Install JS/TS Dependencies Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Installs JavaScript and TypeScript dependencies for the project, including running a postinstall script for the client directory. ```bash # 1. Install JS/TS dependencies (also runs `cd client && pnpm i` via postinstall) pnpm install ``` -------------------------------- ### Declare Server Capabilities with `initialize` Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt This method is called once at the start of every LSP session to declare supported capabilities. It configures features like full document sync, hover, definition, references, rename, formatting, inlay hints, and semantic tokens. ```rust async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { server_info: None, offset_encoding: None, capabilities: ServerCapabilities { // Full-document sync: server receives the complete text on every change text_document_sync: Some(TextDocumentSyncCapability::Options( TextDocumentSyncOptions { open_close: Some(true), change: Some(TextDocumentSyncKind::FULL), save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions { include_text: Some(true), })), ..Default::default() }, )), hover_provider: Some(HoverProviderCapability::Simple(true)), definition_provider: Some(OneOf::Left(true)), references_provider: Some(OneOf::Left(true)), rename_provider: Some(OneOf::Left(true)), document_formatting_provider: Some(OneOf::Left(true)), inlay_hint_provider: Some(OneOf::Left(true)), completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec!["_".to_string()]), // fires on '.' ..Default::default() }), semantic_tokens_provider: Some( SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions( SemanticTokensRegistrationOptions { text_document_registration_options: TextDocumentRegistrationOptions { document_selector: Some(vec![DocumentFilter { language: Some("l".to_string()), scheme: Some("file".to_string()), pattern: None, }]), }, semantic_tokens_options: SemanticTokensOptions { legend: SemanticTokensLegend { // Index 0=FUNCTION 1=VARIABLE 2=PARAMETER 3=STRUCT 4=PROPERTY token_types: vec![ SemanticTokenType::FUNCTION, SemanticTokenType::VARIABLE, SemanticTokenType::PARAMETER, SemanticTokenType::STRUCT, SemanticTokenType::PROPERTY, ], token_modifiers: vec![], }, range: Some(true), full: Some(SemanticTokensFullOptions::Bool(true)), ..Default::default() }, ..Default::default() }, ), ), ..ServerCapabilities::default() }, }) } ``` -------------------------------- ### Package VSIX Extension Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Packages the extension into a .vsix file for installation. ```bash # 5. Package into a .vsix file pnpm run package # produces l-language-server-1.5.0.vsix ``` -------------------------------- ### Example: LSP Coordinate Conversion Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Demonstrates converting an LSP Position to a byte offset and back using the provided utility functions. ```rust // Example: converting back and forth let rope = Rope::from("struct Point {\n x: int,\n}"); let pos = Position::new(1, 4); // line 1, col 4 → "x" let offset = position_to_offset(pos, &rope); // → Some(19) let back = offset_to_position(19, &rope); // → Some(Position { line: 1, character: 4 }) ``` -------------------------------- ### Launch VS Code Extension Development Host Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Opens the project in VS Code and launches the Extension Development Host for debugging. ```bash # 3. Open in VS Code and press F5 to launch the Extension Development Host code . ``` -------------------------------- ### Build Rust Language Server Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Builds the Rust language server binary. Uses the version specified in rust-toolchain.toml. ```bash # 2. Build the Rust language server binary (uses rust-toolchain.toml → 1.88.0) cargo build ``` -------------------------------- ### Implement Context-Aware Code Completion Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt This function provides code completion suggestions based on the AST node at the cursor's position. It differentiates between field access expressions (e.g., `foo.`) and general scope, returning relevant symbols accordingly. Ensure the LSP server has access to semantic analysis and document maps. ```rust async fn completion(&self, params: CompletionParams) -> Result> { Ok(self.get_completion(params).map(CompletionResponse::Array)) } ``` ```rust fn get_completion(&self, params: CompletionParams) -> Option> { let uri = params.text_document_position.text_document.uri.to_string(); let result = self.semanticast_map.get(&uri)?; let rope = self.document_map.get(&uri)?; let offset = position_to_offset(params.text_document_position.position, &rope)?; let mut items = Vec::new(); match find_node_at_offset(result.program.file(), offset as u32) { Some(AstNode::ExprField(field_expr)) => { // Dot-access: look up the struct and return its fields let struct_id = self.get_struct_id_from_field(field_expr, &result)?; let struct_def = result.semantic.structs.get(&struct_id)?; for field in &struct_def.fields { items.push(CompletionItem { label: field.name.clone(), kind: Some(CompletionItemKind::FIELD), detail: Some(format!(": {{}}", field.ty.format_literal_type(&result.semantic))), insert_text: Some(field.name.clone()), ..Default::default() }); } } _ => { // General: return every known symbol (variables, functions, structs) result.semantic.bindings.iter_enumerated().for_each(|(sym_id, type_info)| { let kind = result.semantic.get_symbol_kind(sym_id); let span = result.semantic.get_symbol_span(sym_id); let name = rope.byte_slice(span.start as usize..span.end as usize); if let Ok(name) = std::str::from_utf8(name.bytes().collect::>().as_slice()) { let (comp_kind, detail) = match kind { SymbolKind::Variable => (Some(CompletionItemKind::VARIABLE), Some(format!(": {{}}", type_info.ty.format_literal_type(&result.semantic)))), SymbolKind::Function => (Some(CompletionItemKind::FUNCTION), None), SymbolKind::Struct => (Some(CompletionItemKind::STRUCT), None), _ => (None, None), }; items.push(CompletionItem { label: name.to_string(), kind: comp_kind, detail, insert_text: Some(name.to_string()), ..Default::default() }); } }); } } Some(items) } ``` -------------------------------- ### Valid Program in l-lang Source: https://github.com/iwanabethatguy/tower-lsp-boilerplate/blob/main/README.md Demonstrates the syntax and structure of a valid program in the l-lang programming language, including struct definitions, function declarations, and variable assignments. ```rust struct Point { x: int, y: int, } struct Rectangle { top_left: Point, bottom_right: Point, } fn add_points(a: Point, b: Point) -> Point { return Point { x: a.x + b.x, y: a.y + b.y }; } fn main() { let p1 = Point { x: 10, y: 20 }; let p2 = Point { x: 5, y: 15 }; let result = add_points(p1, p2); let rect = Rectangle { top_left: Point { x: 0, y: 100 }, bottom_right: Point { x: 100, y: 0 }, }; return result; } ``` -------------------------------- ### Generate Inlay Hints for Variable Type Annotations Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt This function generates inlay hints for local variable type annotations. For struct types, it creates a clickable label that navigates to the struct definition. Ensure semantic analysis results and document ropes are available. ```rust // For: `let p1 = Point { x: 10, y: 20 };` // Renders after `p1`: `: Point` (clickable → jumps to Point definition) fn build_inlay_hints(&self, uri: &str) -> Option> { let semantic_result = self.semanticast_map.get(uri)?; let rope = self.document_map.get(uri)?; let bindings = &semantic_result.semantic.bindings; let hints = bindings .iter_enumerated() .filter_map(|(symbol_id, type_info)| { // Only annotate local variables, not parameters/fields/functions if semantic_result.semantic.get_symbol_kind(symbol_id) != SymbolKind::Variable { return None; } let span = semantic_result.semantic.get_symbol_span(symbol_id); let end = offset_to_position(span.end as usize, &rope)?; let label = match type_info.ty { Type::Struct(struct_id) => { // Build a label with a location link to the struct definition let struct_span = semantic_result.semantic.get_symbol_span(struct_id); let struct_start = offset_to_position(struct_span.start as usize, &rope)?; let struct_end = offset_to_position(struct_span.end as usize, &rope)?; let location = Location::new( Url::parse(uri).unwrap(), Range::new(struct_start, struct_end), ); InlayHintLabel::LabelParts(vec![ InlayHintLabelPart { value: ": ".to_string(), ..Default::default() }, InlayHintLabelPart { value: type_info.ty.format_literal_type(&semantic_result.semantic), location: Some(location), ..Default::default() }, ]) } _ => InlayHintLabel::String(format!( ": {{}}", type_info.ty.format_literal_type(&semantic_result.semantic) )), }; Some(InlayHint { position: Position::new(end.line, end.character), label, kind: Some(InlayHintKind::TYPE), padding_left: Some(true), padding_right: Some(false), ..Default::default() }) }) .collect(); Some(hints) } ``` -------------------------------- ### Format TypeScript/JavaScript Code Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Runs oxfmt to format the TypeScript and JavaScript code. ```bash pnpm run fmt # oxfmt ``` -------------------------------- ### Format Document (Rust) Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Formats the entire document using `l_lang::Formatter` with a configurable line-width. Returns a single `TextEdit` replacing the whole file content. ```rust // Input: unformatted l-lang source, params.text_document.uri // Output: Vec with one edit spanning the whole file async fn formatting(&self, params: DocumentFormattingParams) -> Result>> { Ok(self.format_text(params)) } fn format_text(&self, params: DocumentFormattingParams) -> Option> { let uri = params.text_document.uri.to_string(); let rope = self.document_map.get(&uri)?; let semantic_result = self.semanticast_map.get(&uri)?; let formatter = Formatter::new(80); // 80-column print width let formatted = formatter.format(semantic_result.program.file(), &rope.to_string()); let last_line = rope.line_len() as u32; let last_col = rope.line(rope.line_len() - 1).byte_len() as u32; Some(vec![TextEdit { range: Range::new(Position::new(0, 0), Position::new(last_line, last_col)), new_text: formatted, }]) } ``` -------------------------------- ### VS Code Client Activation Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Activates the language server client for *.l files in VS Code. The server command path is configurable via SERVER_PATH environment variable. ```typescript import { workspace, ExtensionContext, window } from "vscode"; import { Executable, LanguageClient, LanguageClientOptions, ServerOptions, } from "vscode-languageclient/node"; let client: LanguageClient; export async function activate(_context: ExtensionContext) { const traceOutputChannel = window.createOutputChannel("L Language Server trace"); // In VS Code launch.json, SERVER_PATH is set to ${workspaceRoot}/target/debug/l-language-server // In production VSIX, the binary must be on PATH as `l-language-server` const command = process.env.SERVER_PATH || "l-language-server"; const run: Executable = { command, options: { env: { ...process.env, RUST_LOG: "debug" }, // enable server-side debug logging }, }; const serverOptions: ServerOptions = { run, debug: run }; const clientOptions: LanguageClientOptions = { documentSelector: [{ scheme: "file", language: "l" }], // activates for *.l files synchronize: { fileEvents: workspace.createFileSystemWatcher("**/.clientrc"), }, traceOutputChannel, }; client = new LanguageClient( "l-language-server", "L language server", serverOptions, clientOptions, ); client.start(); } export function deactivate(): Thenable | undefined { return client?.stop(); } ``` -------------------------------- ### build_semantic_tokens — Full-Document Semantic Highlighting Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Converts the semantic symbol and reference tables into the LSP delta-encoded `SemanticToken` stream for the whole document. Token type indices follow the legend declared in `initialize`. ```APIDOC ## `build_semantic_tokens` — Full-Document Semantic Highlighting Converts the semantic symbol and reference tables into the LSP delta-encoded `SemanticToken` stream for the whole document. Token type indices follow the legend declared in `initialize`. ### Method Signature ```rust fn build_semantic_tokens(&self, uri: &str) -> Option> ``` ### Parameters - **uri** (&str) - The URI of the document to generate semantic tokens for. ### Returns - `Option>` - A vector of `SemanticToken` objects representing the highlighted tokens in the document, or `None` if token generation fails. ### Token Type Legend - 0 → FUNCTION - 1 → VARIABLE - 2 → PARAMETER - 3 → STRUCT - 4 → PROPERTY ``` -------------------------------- ### Compile Document and Publish Diagnostics Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt This method is invoked on `textDocument/didOpen` and `textDocument/didChange` events. It recompiles the document, gathers syntactic and semantic diagnostics, sends them to the client, and stores the compilation results. ```rust struct TextDocumentChange<'a> { uri: String, text: &'a str, } // Called from did_open and did_change handlers: async fn on_change(&self, item: TextDocumentChange<'_>) { let rope = Rope::from(item.text); let compile_result = compile(item.text); // l_lang::compile → CompileResult // --- Syntactic diagnostics (parse errors) --- let mut diagnostics: Vec = compile_result .diagnostics .iter() .flat_map(|d| { d.labels.iter().filter_map(|label| { let start = offset_to_position(label.range.start, &rope)?; let end = offset_to_position(label.range.end, &rope)?; Some(Diagnostic { range: Range::new(start, end), message: format!("{:?}", d.message), ..Default::default() }) }) }) .collect(); // --- Semantic diagnostics (type errors, undefined names, etc.) --- compile_result.semantic.errors.iter().for_each(|sem_err| { if let (Some(start), Some(end)) = ( offset_to_position(sem_err.span.start as usize, &rope), offset_to_position(sem_err.span.end as usize, &rope), ) { diagnostics.push(Diagnostic { range: Range::new(start, end), message: sem_err.message.to_string(), ..Default::default() }); } }); let uri = Url::parse(&item.uri) .unwrap_or_else(|_| Url::from_directory_path(&item.uri).unwrap()); self.client.publish_diagnostics(uri, diagnostics, None).await; self.semanticast_map.insert(item.uri.clone(), compile_result); self.document_map.insert(item.uri.clone(), rope); } ``` -------------------------------- ### Build Semantic Tokens (Rust) Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Converts semantic symbol and reference tables into the LSP delta-encoded `SemanticToken` stream for the whole document. Token type indices follow the legend declared in `initialize`. ```rust // Token type legend (index → SemanticTokenType): // 0 → FUNCTION 1 → VARIABLE 2 → PARAMETER 3 → STRUCT 4 → PROPERTY fn build_semantic_tokens(&self, uri: &str) -> Option> { let semantic_result = self.semanticast_map.get(uri)?; let rope = self.document_map.get(uri)?; let mut tokens: Vec<(usize, usize, u32)> = Vec::new(); // (byte_start, len, type_idx) // Collect symbol definitions for (sym_id, span) in semantic_result.semantic.symbol_spans.iter_enumerated() { let type_idx = match semantic_result.semantic.get_symbol_kind(sym_id) { SymbolKind::Function => 0, SymbolKind::Variable => 1, SymbolKind::Parameter => 2, SymbolKind::Struct => 3, SymbolKind::Field => 4, }; tokens.push((span.start as usize, (span.end - span.start) as usize, type_idx)); } // Collect references (inherit token type from the referenced symbol) for (ref_id, span) in semantic_result.semantic.reference_spans.iter_enumerated() { if let Some(sym_id) = semantic_result.semantic.references[ref_id] { let type_idx = match semantic_result.semantic.get_symbol_kind(sym_id) { SymbolKind::Function => 0, SymbolKind::Variable => 1, SymbolKind::Parameter => 2, SymbolKind::Struct => 3, SymbolKind::Field => 4, }; tokens.push((span.start as usize, (span.end - span.start) as usize, type_idx)); } } tokens.sort_by_key(|t| t.0); // Encode as LSP delta format (each token's line/char are relative to previous) let mut pre_line: u32 = 0; let mut pre_start: u32 = 0; Some(tokens.iter().map(|(start, length, token_type)| { let line = rope.line_of_byte(*start) as u32; let char_offset = (start - rope.byte_of_line(line as usize)) as u32; let delta_line = line - pre_line; let delta_start = if delta_line == 0 { char_offset - pre_start } else { char_offset }; pre_line = line; pre_start = char_offset; SemanticToken { delta_line, delta_start, length: *length as u32, token_type: *token_type, token_modifiers_bitset: 0 } }).collect()) } ``` -------------------------------- ### Enable Semantic Highlighting in VSCode Source: https://github.com/iwanabethatguy/tower-lsp-boilerplate/blob/main/README.md Configuration setting for VSCode to enable semantic highlighting. Ensure this is set to true in your editor's settings.json file for features like syntax highlighting based on semantic roles. ```json { "editor.semanticHighlighting.enabled": true } ``` -------------------------------- ### Rust Backend Struct for LSP Server State Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Defines the core server state, including the tower-lsp client handle and concurrent maps for document text and compilation results. Initialize the server with this struct. ```rust use crop::Rope; use dashmap::DashMap; use l_lang::CompileResult; use tower_lsp::{Client, LspService, Server}; #[derive(Debug)] struct Backend { client: Client, // tower-lsp client handle for push notifications document_map: DashMap, // URI → rope (efficient text representation) semanticast_map: DashMap, // URI → full compile result } ``` -------------------------------- ### VS Code Settings for L Language Server Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Configuration settings for the L language server in VS Code to enable its full feature set. ```json { "editor.semanticHighlighting.enabled": true, "l-language-server.trace.server": "verbose" } ``` -------------------------------- ### Find All Symbol References Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Use this function to retrieve all locations referencing a given symbol. The `include_declaration` flag determines whether the definition site itself should be included in the results. ```rust async fn references(&self, params: ReferenceParams) -> Result>> { let uri = params.text_document_position.text_document.uri.to_string(); let position = params.text_document_position.position; Ok(self.get_references(uri, position, params.context.include_declaration)) } fn get_references( &self, uri: String, position: Position, include_self: bool, ) -> Option> { let rope = self.document_map.get(&uri)?; let compilation_result = self.semanticast_map.get(&uri)?; let offset = position_to_offset(position, &rope)?; let symbol_id = compilation_result.semantic.get_symbol_at(offset)?; let uri_parsed = Url::parse(&uri).unwrap(); let mut references = Vec::new(); if include_self { let sym_span = compilation_result.semantic.get_symbol_span(symbol_id); let start = offset_to_position(sym_span.start as usize, &rope)?; let end = offset_to_position(sym_span.end as usize, &rope)?; references.push(Location::new(uri_parsed.clone(), Range::new(start, end))); } let ref_ids = compilation_result.semantic.get_symbol_references(symbol_id); references.extend(ref_ids.iter().filter_map(|ref_id| { let span = compilation_result.semantic.reference_spans[*ref_id]; let start = offset_to_position(span.start as usize, &rope)?; let end = offset_to_position(span.end as usize, &rope)?; Some(Location::new(uri_parsed.clone(), Range::new(start, end))) })); Some(references) } ``` -------------------------------- ### format_text — Document Formatting Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Formats the entire document using `l_lang::Formatter` with a configurable line-width. Returns a single `TextEdit` replacing the whole file content. ```APIDOC ## `format_text` — Document Formatting Formats the entire document using `l_lang::Formatter` with a configurable line-width. Returns a single `TextEdit` replacing the whole file content. ### Method Signature ```rust fn format_text(&self, params: DocumentFormattingParams) -> Option> ``` ### Parameters - **params** (DocumentFormattingParams) - Formatting parameters including the document URI. ### Returns - `Option>` - A vector containing a single `TextEdit` that spans the entire file, or `None` if formatting fails. ``` -------------------------------- ### Compile TypeScript for Extension Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Compiles the TypeScript code for the VS Code extension into `dist/extension.js` using rolldown. ```bash # 4. Compile TypeScript → dist/extension.js (uses rolldown) pnpm run compile ``` -------------------------------- ### Provide Symbol Type Information on Hover Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt This method returns a Markdown-formatted hover card displaying the kind and type of the symbol under the cursor. It requires the document rope and compilation results to be available. ```rust // Input: cursor at `result` in `let result = add_points(p1, p2);` // Output hover markdown: // ```l // let result: Point // ``` async fn hover(&self, params: HoverParams) -> Result> { let uri = params.text_document_position_params.text_document.uri.to_string(); let position = params.text_document_position_params.position; let rope = self.document_map.get(&uri); let compilation_result = self.semanticast_map.get(&uri); let hover = (|| -> Option { let rope = rope.as_deref()?; let compilation_result = compilation_result.as_deref()?; let offset = position_to_offset(position, rope)?; let symbol_id = compilation_result.semantic.get_symbol_at(offset)?; let symbol_kind = compilation_result.semantic.get_symbol_kind(symbol_id); let type_info = &compilation_result.semantic.bindings[symbol_id]; let span = compilation_result.semantic.get_symbol_span(symbol_id); let name = rope.byte_slice(span.start as usize..span.end as usize).to_string(); let content = match symbol_kind { SymbolKind::Function => format!("`l\nfn {{name}}\n```"), SymbolKind::Struct => format!("`l\nstruct {{name}}\n```"), SymbolKind::Variable => { let ty = type_info.ty.format_literal_type(&compilation_result.semantic); format!("`l\nlet {{name}}: {{ty}}\n```") } SymbolKind::Parameter => { let ty = type_info.ty.format_literal_type(&compilation_result.semantic); format!("`l\n{{name}}: {{ty}}\n```") } SymbolKind::Field => { let ty = type_info.ty.format_literal_type(&compilation_result.semantic); format!("`l\n{{name}}: {{ty}}\n```") } _ => return None, }; Some(Hover { contents: HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, value: content, }), range: None, }) })(); Ok(hover) } ``` -------------------------------- ### Format Rust Code Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Formats the Rust code across the entire workspace according to standard Rust formatting conventions. ```bash cargo fmt --all ``` -------------------------------- ### Lint TypeScript/JavaScript Code Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Runs oxlint to perform linting on the TypeScript and JavaScript code. ```bash pnpm run lint # oxlint ``` -------------------------------- ### Lint Rust Code Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Runs the Rust compiler with clippy to check for warnings and enforce linting rules across the workspace. ```bash # --- Linting & formatting --- cargo clippy --workspace --all-targets -- --deny warnings ``` -------------------------------- ### Convert Byte Offset to LSP Position Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Converts a byte offset within a Rope to an LSP Position (line, character). Handles out-of-bounds offsets by returning None. ```rust fn offset_to_position(offset: usize, rope: &Rope) -> Option { if offset > rope.byte_len() { return None; // guard against out-of-bounds } let line = rope.line_of_byte(offset); let line_start = rope.byte_of_line(line); let column = offset - line_start; Some(Position::new(line as u32, column as u32)) } ``` -------------------------------- ### Convert LSP Position to Byte Offset Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Converts an LSP Position (line, character) to a byte offset within a Rope. Handles invalid line numbers by returning None. ```rust fn position_to_offset(position: Position, rope: &Rope) -> Option { if position.line as usize >= rope.line_len() { return None; // guard against line beyond EOF } Some(rope.byte_of_line(position.line as usize) + position.character as usize) } ``` -------------------------------- ### Resolve Symbol Declaration Location Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Use this function to find the declaration site of a symbol under the cursor. It first checks if the cursor is directly on a definition, otherwise it resolves the reference to its bound symbol. ```rust async fn goto_definition( &self, params: GotoDefinitionParams, ) -> Result> { Ok(self.get_definition(params)) } fn get_definition(&self, params: GotoDefinitionParams) -> Option { let uri = params.text_document_position_params.text_document.uri.to_string(); let position = params.text_document_position_params.position; let rope = self.document_map.get(&uri)?; let compilation_result = self.semanticast_map.get(&uri)?; let offset = position_to_offset(position, &rope)?; // 1. Is the cursor directly on a symbol definition? if let Some(interval) = compilation_result.semantic.span_to_symbol .find(offset, offset + 1).next() { let start = offset_to_position(interval.start, &rope)?; let end = offset_to_position(interval.stop, &rope)?; return Some(GotoDefinitionResponse::Scalar(Location::new( params.text_document_position_params.text_document.uri, Range::new(start, end), ))); } // 2. Otherwise resolve via the reference → symbol mapping let ref_id = compilation_result.semantic.span_to_reference .find(offset, offset + 1).next()?.val; let symbol_id = compilation_result.semantic.references[ref_id]?; let sym_span = compilation_result.semantic.get_symbol_span(symbol_id); let start = offset_to_position(sym_span.start as usize, &rope)?; let end = offset_to_position(sym_span.end as usize, &rope)?; Some(GotoDefinitionResponse::Scalar(Location::new( params.text_document_position_params.text_document.uri, Range::new(start, end), ))) } ``` -------------------------------- ### Rename Symbol in Document (Rust) Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Renames a symbol across all its usages in the document. Reuses `get_references` and converts locations to `TextEdit`. ```rust // Input: cursor on `p1`, new_name = "point1" // Output: WorkspaceEdit replacing every occurrence of `p1` with `point1` async fn rename(&self, params: RenameParams) -> Result> { let uri = params.text_document_position.text_document.uri.to_string(); let position = params.text_document_position.position; Ok(self.get_rename_edit(uri, position, params.new_name)) } fn get_rename_edit( &self, uri: String, position: Position, new_name: String, ) -> Option { let all_references = self.get_references(uri.clone(), position, true)?; let edits: Vec = all_references .into_iter() .map(|loc| TextEdit { range: loc.range, new_text: new_name.clone() }) .collect(); let parsed_uri = Url::parse(&uri).unwrap(); let mut edit_map = std::collections::HashMap::new(); edit_map.insert(parsed_uri, edits); Some(WorkspaceEdit::new(edit_map)) } ``` -------------------------------- ### get_rename_edit — Rename Symbol Source: https://context7.com/iwanabethatguy/tower-lsp-boilerplate/llms.txt Renames a symbol across all its usages in the document by reusing `get_references` and converting each location into a `TextEdit`. ```APIDOC ## `get_rename_edit` — Rename Symbol Renames a symbol across all its usages in the document by reusing `get_references` and converting each location into a `TextEdit`. ### Method Signature ```rust fn get_rename_edit( &self, uri: String, position: Position, new_name: String, ) -> Option ``` ### Parameters - **uri** (String) - The URI of the document. - **position** (Position) - The cursor position in the document. - **new_name** (String) - The new name for the symbol. ### Returns - `Option` - A `WorkspaceEdit` containing the text edits for renaming, or `None` if the operation fails. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.