← Back to articles

LSP and Code Intelligence

Path: Computer Tech/Development/Editors/Neovim/LazyVim Workflows/LSP and Code Intelligence.mdUpdated: 2/3/2026

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:

typescript
const 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:

typescript
function 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:

typescript
const 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:

typescript
const result = calculateTotal(10, 20);
               ↑ (red underline: not imported)

Space + c + a β†’ Shows:
1. Import calculateTotal from './utils'
2. Create function calculateTotal

Extract to constant:

typescript
return 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:

typescript
function 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:

typescript
Math.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:

typescript
const user = {
  name: "John",
  email: "[email protected]"
};

user.
    ↑ Press Ctrl+Space

β†’ Shows:
  name: string
  email: string

Code Actions Examples

Import Missing Modules

typescript
const result = calculateTotal(10, 20);
               ↑ Red underline

Space + c + a
β†’ Import calculateTotal from './utils'

Add Missing Types

typescript
function greet(name) {
              ↑
Space + c + a
β†’ Infer type from usage
β†’ Add explicit type annotation

Extract to Function

typescript
const 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 server
  • u - Update server
  • X - 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:

typescript
fun<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:

typescript
const 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:

typescript
function 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

typescript
function greet(name) {
  return "Hello, " + name;
}

1. Cursor on "name"
2. Space + c + a
3. "Add type annotation"
4. Select inferred type

Keybinding Reference

ActionKeybindingDescription
Go to definitiongdJump to where symbol is defined
Go to type defgyJump to type definition
Find referencesgrShow all uses of symbol
Hover docsKShow documentation
Code actionsSpace c aShow refactorings/fixes
RenameSpace c rRename symbol everywhere
Next diagnostic]dJump to next error/warning
Prev diagnostic[dJump to previous error/warning
Signature helpCtrl+kShow function parameters
FormatSpace c fFormat document
Organize importsSpace c oClean 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 r understands scope
  • Won't rename unrelated symbols

5. Organize imports frequently

  • Space c o after 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: