LSP and Code Intelligence
LSP and Code Intelligence
LSP (Language Server Protocol) gives LazyVim IDE-like features: autocomplete, go to definition, refactoring, inline errors, and more. Understanding how to use LSP effectively makes you 10× faster.
What is LSP?
LSP is a protocol that allows editors to communicate with language-specific servers that understand your code.
LazyVim (client) ←→ typescript-language-server (server)
←→ pyright (Python)
←→ lua-language-server (Lua)
What LSP provides:
- ✅ Autocomplete with type information
- ✅ Go to definition/references
- ✅ Inline error diagnostics
- ✅ Hover documentation
- ✅ Code actions (refactoring)
- ✅ Rename symbols across files
- ✅ Signature help (function parameters)
What LSP does NOT do:
- Run your code (that's a REPL/debugger)
- Format your code (that's a formatter like Prettier)
- Lint your code (that's a linter like ESLint - though some LSPs include it)
Essential LSP Keybindings
Go to Definition
gd
What it does: Jumps to where a function/variable is defined.
Example:
typescript// In file A: import { calculateTotal } from './utils'; const result = calculateTotal(100, 0.2); ↑ cursor here // Press gd → Jumps to file B: export function calculateTotal(price: number, tax: number) { return price + (price * tax); }
Return to previous location:
Ctrl+o # Jump back
Go to Type Definition
gy
What it does: Jumps to the type definition.
Example:
typescriptconst user: User = getUser(); ↑ cursor here // Press gy → Jumps to: interface User { name: string; email: string; }
Find References
gr
What it does: Shows everywhere a symbol is used.
Example:
typescriptfunction calculateTotal(a, b) { return a + b; } ↑ cursor here // Press gr → Shows list: 1. src/utils.ts:12 - function calculateTotal 2. src/App.tsx:45 - const total = calculateTotal(x, y) 3. src/Cart.tsx:23 - return calculateTotal(price, tax)
Hover Documentation
K (Shift+k)
What it does: Shows documentation for symbol under cursor.
Example:
typescriptconst result = Math.max(10, 20); ↑ cursor here // Press K → Shows popup: ┌─────────────────────────────────────┐ │ Math.max(...values: number[]): number │ │ │ │ Returns the larger of a set of │ │ supplied numeric expressions. │ └─────────────────────────────────────┘
Close popup: Press K again or Esc
Code Actions
Space + c + a
What it does: Shows available refactorings and quick fixes.
Examples:
Import missing symbol:
typescriptconst result = calculateTotal(10, 20); ↑ (red underline: not imported) Space + c + a → Shows: 1. Import calculateTotal from './utils' 2. Create function calculateTotal
Extract to constant:
typescriptreturn price * 0.2; ↑ select "price * 0.2" Space + c + a → Shows: 1. Extract to constant 2. Extract to function
Fix all in file:
Space + c + A # Capital A
Rename Symbol
Space + c + r
What it does: Renames a symbol everywhere in the project.
Example:
typescriptfunction calculateTotal(price, tax) { return price + tax; } const result = calculateTotal(100, 20); ↑ cursor on "calculateTotal" Space + c + r Type: "computeSum" → Renames in ALL files where it's used
This is extremely powerful:
- Refactor safely across entire codebase
- Renames in imports/exports too
- Updates all references
Signature Help
# Automatically shows when typing function call
# Or manually trigger:
Ctrl+k
Example:
typescriptMath.max( ↑ cursor here // Automatically shows: ┌──────────────────────────────────┐ │ max(value1: number, value2: number, ...values: number[]): number │ │ ^^^^^^ │ └──────────────────────────────────┘
Shows which parameter you're on and its type.
Diagnostics (Errors/Warnings)
LSP shows errors and warnings inline.
See Diagnostics
Visual indicators:
- Red underline = Error
- Yellow underline = Warning
- Blue underline = Info
- Gray text = Hint
See full diagnostic message:
# Hover over error
K
Navigate Diagnostics
]d # Next diagnostic
[d # Previous diagnostic
Show All Diagnostics
Space + x + x # Diagnostics in current file
Space + x + X # Diagnostics in all files
Opens list:
1. error: Cannot find name 'calculateTotal'
2. warning: Unused variable 'result'
3. info: Consider using const instead of let
Fix Diagnostic
# Cursor on error
Space + c + a # Shows available fixes
Autocomplete
LazyVim has intelligent autocomplete via nvim-cmp.
Trigger Autocomplete
Autocomplete triggers automatically as you type.
Manual trigger:
Ctrl+Space
Navigate Completions
Ctrl+n # Next item
Ctrl+p # Previous item
Ctrl+y # Accept (yes)
Ctrl+e # Cancel (exit)
Enter # Accept selected
Autocomplete Sources
LazyVim pulls suggestions from:
- LSP (functions, variables, types)
- Buffer (words in current file)
- Path (file paths)
- Snippets (code templates)
Example:
typescriptconst user = { name: "John", email: "[email protected]" }; user. ↑ Press Ctrl+Space → Shows: name: string email: string
Code Actions Examples
Import Missing Modules
typescriptconst result = calculateTotal(10, 20); ↑ Red underline Space + c + a → Import calculateTotal from './utils'
Add Missing Types
typescriptfunction greet(name) { ↑ Space + c + a → Infer type from usage → Add explicit type annotation
Extract to Function
typescriptconst total = price * (1 + taxRate); ↑ Select expression Space + c + a → Extract to function → Extract to constant
Organize Imports
Space + c + o
What it does:
- Removes unused imports
- Sorts imports
- Groups imports by type
Format Document
Space + c + f
Uses configured formatter:
- Prettier for JS/TS
- Black for Python
- stylua for Lua
LSP Server Management
Check LSP Status
:LspInfo
Shows:
- Active LSP servers
- Server capabilities
- Configuration details
Install Language Server
:Mason
Interface:
i- Install serveru- Update serverX- Uninstall server
Recommended servers:
typescript-language-server (JS/TS)
pyright (Python)
lua-language-server (Lua)
rust-analyzer (Rust)
gopls (Go)
Restart LSP
:LspRestart
Use when:
- Changed configuration
- Server is behaving strangely
- After installing new dependencies
Snippets
Snippets are code templates that expand.
Trigger Snippet
# Type trigger word
# Press Tab to expand
Examples:
TypeScript:
fun<Tab>
→ Expands to:
function name(params) {
}
React:
rfc<Tab>
→ Expands to:
import React from 'react';
export const ComponentName = () => {
return <div></div>;
};
Navigate Snippet Placeholders
Tab # Next placeholder
Shift+Tab # Previous placeholder
Example:
typescriptfun<Tab> → function |name|(params) { } ↑ cursor here Tab → function name(|params|) { } ↑ cursor here
Advanced Features
Workspace Symbols
Space + s + S # Capital S
Search for symbols across entire project:
Type: "User"
→ Shows:
interface User (types.ts)
class UserService (services.ts)
const userSchema (schemas.ts)
Peek Definition
gD # Capital D
Shows definition in popup without jumping:
typescriptconst result = calculateTotal(10, 20); ↑ gD → Shows popup with function definition → Close with Esc, don't lose your place
Show Call Hierarchy
Space + c + h
Shows:
- Where function is called (incoming calls)
- What function calls (outgoing calls)
Code Lens
Some LSP servers show inline information:
typescriptfunction test() { // ← 3 references // ... }
Click or Space + c + l to trigger code lens action.
Language-Specific Features
TypeScript
typescript// Organize imports Space + c + o // Auto-import on complete Ctrl+Space → Select → Auto-imports // Type checking :TSServer typeCoverage
Python
python# Auto-import Space + c + a → Import missing module # Rename Space + c + r → Renames across all files # Sort imports Space + c + o
Lua (Neovim config)
lua-- Hover for Neovim API docs K → Shows documentation for vim.opt, etc. -- Go to source gd → Jump to where function is defined
Common Workflows
Workflow 1: Explore Unfamiliar Code
1. Open file
2. Cursor on function name
3. K → Read documentation
4. gd → See implementation
5. gr → Find where it's used
6. Ctrl+o → Jump back
Workflow 2: Refactor Function Name
1. Cursor on function name
2. Space + c + r → Rename symbol
3. Type new name
4. Enter → Renames everywhere
Workflow 3: Fix Import Errors
1. See red underline
2. Space + c + a → Code actions
3. Select "Import from..."
4. Done
Workflow 4: Extract Repeated Code
1. Visual select code
2. Space + c + a
3. "Extract to function"
4. Name the function
5. LSP updates all call sites
Workflow 5: Add Types to Untyped Code
typescriptfunction greet(name) { return "Hello, " + name; } 1. Cursor on "name" 2. Space + c + a 3. "Add type annotation" 4. Select inferred type
Keybinding Reference
| Action | Keybinding | Description |
|---|---|---|
| Go to definition | gd | Jump to where symbol is defined |
| Go to type def | gy | Jump to type definition |
| Find references | gr | Show all uses of symbol |
| Hover docs | K | Show documentation |
| Code actions | Space c a | Show refactorings/fixes |
| Rename | Space c r | Rename symbol everywhere |
| Next diagnostic | ]d | Jump to next error/warning |
| Prev diagnostic | [d | Jump to previous error/warning |
| Signature help | Ctrl+k | Show function parameters |
| Format | Space c f | Format document |
| Organize imports | Space c o | Clean up imports |
Tips and Tricks
1. Learn gd and gr first
- These are the most-used LSP features
- Navigate code like a pro
2. Use K liberally
- Hover documentation saves trips to MDN/docs
- Learn APIs without leaving editor
3. Space c a fixes most issues
- Auto-import missing modules
- Convert to const/let
- Extract code
- Add types
4. Rename is safer than find-replace
Space c runderstands scope- Won't rename unrelated symbols
5. Organize imports frequently
Space c oafter adding imports- Keeps code clean
6. Use diagnostics to learn
- Red underlines teach best practices
- Read the full error message with
K
Common Mistakes
❌ Not installing language servers
→ Run :Mason and install servers for your languages
❌ Ignoring code actions
→ Space c a solves most issues automatically
❌ Using find-replace instead of rename
→ Space c r is scope-aware and safer
❌ Not reading hover docs
→ K shows types and documentation inline
❌ Manually fixing imports
→ Space c o organizes imports automatically
Troubleshooting
LSP not working:
vim:LspInfo # Check server status :Mason # Install missing server :LspRestart # Restart server
Autocomplete not showing:
vim:checkhealth nvim-cmp # Check completion health Ctrl+Space # Manually trigger
Diagnostics not appearing:
vim:LspInfo # Verify server is attached :checkhealth lsp # Check LSP health
Wrong language server:
lua-- In ~/.config/nvim/lua/config/lsp.lua -- Configure which server for which filetype
Related:
- File Navigation in LazyVim - Finding code
- Code Editing Workflows - Making changes
- Git Integration in LazyVim - Committing changes
- LazyVim Setup - Installing language servers