Skip to main content

Language Server Protocol Integration

Qwen Code includes first-class support for the Language Server Protocol (LSP), enabling precise code navigation, intelligent symbol lookup, and advanced refactoring capabilities.

Overview

LSP provides Qwen Code with:
  • Go to Definition: Navigate to where symbols are defined
  • Find References: Locate all usages of a symbol
  • Hover Information: Get documentation and type information
  • Document Symbols: List all symbols in a file
  • Workspace Symbols: Search for symbols across the entire codebase
  • Go to Implementation: Find implementations of interfaces
  • Call Hierarchy: Analyze function call relationships
  • Diagnostics: Access compiler/linter errors and warnings
  • Code Actions: Get available quick fixes and refactorings

LSP Tool Usage

The unified lsp tool supports all LSP operations through a single interface:
// Tool signature from packages/core/src/tools/lsp.ts:49
export interface LspToolParams {
  operation: LspOperation;
  filePath?: string;
  line?: number;              // 1-based
  character?: number;         // 1-based
  endLine?: number;
  endCharacter?: number;
  includeDeclaration?: boolean;
  query?: string;
  callHierarchyItem?: LspCallHierarchyItem;
  serverName?: string;
  limit?: number;
  diagnostics?: LspDiagnostic[];
  codeActionKinds?: LspCodeActionKind[];
}

Supported Operations

type LspOperation =
  | 'goToDefinition'
  | 'findReferences'
  | 'hover'
  | 'documentSymbol'
  | 'workspaceSymbol'
  | 'goToImplementation'
  | 'prepareCallHierarchy'
  | 'incomingCalls'
  | 'outgoingCalls'
  | 'diagnostics'
  | 'workspaceDiagnostics'
  | 'codeActions';

Operation Examples

Go to Definition

Find where a symbol is defined:
{
  operation: "goToDefinition",
  filePath: "src/index.ts",
  line: 42,
  character: 15,
  limit: 20
}
Response format (from lsp.ts:208):
Definitions for src/index.ts:42:15:
1. src/utils/helper.ts:10:9 [typescript]
2. types/index.d.ts:5:14 [typescript]

Find References

Locate all usages of a symbol:
{
  operation: "findReferences",
  filePath: "src/api.ts",
  line: 10,
  character: 8,
  includeDeclaration: false,  // Exclude the definition itself
  limit: 50
}
Response (from lsp.ts:290):
References for src/api.ts:10:8:
1. src/handlers/user.ts:15:3 [typescript]
2. src/routes.ts:42:20 [typescript]
3. tests/api.test.ts:8:12 [typescript]
...

Hover Information

Get documentation and type info:
{
  operation: "hover",
  filePath: "src/models/user.ts",
  line: 25,
  character: 10
}
Response (from lsp.ts:332):
Hover for src/models/user.ts:25:10:

```typescript
function getUserById(id: string): Promise<User | null>
Fetches a user by their unique identifier. @param id - The user’s unique ID @returns A promise resolving to the User object or null if not found

### Document Symbols

List all symbols in a file:

```typescript
{
  operation: "documentSymbol",
  filePath: "src/service.ts",
  limit: 50
}
Response (from lsp.ts:370):
Document symbols for src/service.ts:
1. UserService (Class) - src/service.ts:5:7 [typescript]
2. constructor (Constructor) in UserService - src/service.ts:8:3 [typescript]
3. getUser (Method) in UserService - src/service.ts:12:9 [typescript]
4. createUser (Method) in UserService - src/service.ts:20:9 [typescript]
...
Search for symbols across the workspace:
{
  operation: "workspaceSymbol",
  query: "handleRequest",
  limit: 20
}
Response (from lsp.ts:415):
Found 20 of 45 symbols for query "handleRequest":
1. handleRequest (Function) - src/api.ts:10:9 [typescript]
2. handleRequestError (Function) - src/errors.ts:5:9 [typescript]
3. handleRequestMiddleware (Function) - src/middleware.ts:15:9 [typescript]
...

References for top match (handleRequest):
1. src/routes.ts:25:3 [typescript]
2. src/app.ts:40:8 [typescript]
...

Go to Implementation

Find implementations of an interface or abstract method:
{
  operation: "goToImplementation",
  filePath: "src/interfaces/storage.ts",
  line: 5,
  character: 18,
  limit: 20
}
Response (from lsp.ts:249):
Implementations for src/interfaces/storage.ts:5:18:
1. src/storage/local.ts:10:7 [typescript]
2. src/storage/s3.ts:8:7 [typescript]
3. src/storage/memory.ts:5:7 [typescript]

Call Hierarchy

Prepare Call Hierarchy - Get callable items at a position:
{
  operation: "prepareCallHierarchy",
  filePath: "src/utils.ts",
  line: 30,
  character: 9,
  limit: 20
}
Response (from lsp.ts:505):
Call hierarchy items for src/utils.ts:30:9:
1. processData (Function) - src/utils.ts:30:9 [typescript]

Call hierarchy items (JSON):
[
  {
    "name": "processData",
    "kind": "function",
    "uri": "file:///workspace/src/utils.ts",
    "range": { "start": { "line": 29, "character": 0 }, "end": { "line": 45, "character": 1 } },
    "selectionRange": { "start": { "line": 29, "character": 9 }, "end": { "line": 29, "character": 20 } },
    "serverName": "typescript"
  }
]
Incoming Calls - Find callers of a function:
{
  operation: "incomingCalls",
  callHierarchyItem: { /* from prepareCallHierarchy */ },
  limit: 20
}
Response (from lsp.ts:548):
Incoming calls for processData at src/utils.ts:30:9:
1. handleData (Function) - src/handlers.ts:15:5 (calls at 18:3) [typescript]
2. main (Function) - src/index.ts:10:9 (calls at 25:5) [typescript]
Outgoing Calls - Find functions called by a function:
{
  operation: "outgoingCalls",
  callHierarchyItem: { /* from prepareCallHierarchy */ },
  limit: 20
}
Response (from lsp.ts:602):
Outgoing calls for processData at src/utils.ts:30:9:
1. validateInput (Function) - src/validators.ts:8:9 (calls at 32:3) [typescript]
2. transformData (Function) - src/transform.ts:12:9 (calls at 35:10) [typescript]
3. saveToDatabase (Function) - src/db.ts:20:9 (calls at 40:3) [typescript]

Diagnostics

File Diagnostics - Get errors/warnings for a file:
{
  operation: "diagnostics",
  filePath: "src/buggy.ts"
}
Response (from lsp.ts:660):
Diagnostics for src/buggy.ts (3 issues):
1. [ERROR] 15:5 (2322) [typescript]: Type 'string' is not assignable to type 'number'.
2. [WARNING] 22:10 (6133) [typescript]: 'unusedVar' is declared but its value is never read.
3. [INFO] 30:1 (80001) [typescript]: File is a CommonJS module; it may be converted to an ES module.
Workspace Diagnostics - Get all diagnostics:
{
  operation: "workspaceDiagnostics",
  limit: 50
}
Response (from lsp.ts:700):
Workspace diagnostics (15 issues in 5 files):

src/app.ts [typescript]:
  [ERROR] 10:3 (2304): Cannot find name 'proces'. Did you mean 'process'?
  [WARNING] 25:5 (6133): 'tempVar' is declared but its value is never read.

src/utils.ts [typescript]:
  [ERROR] 42:8 (2345): Argument of type 'string[]' is not assignable to parameter of type 'number[]'.
...

Code Actions

Get available quick fixes and refactorings:
{
  operation: "codeActions",
  filePath: "src/app.ts",
  line: 10,
  character: 3,
  endLine: 10,
  endCharacter: 20,
  diagnostics: [ /* errors at this location */ ],
  codeActionKinds: ["quickfix", "refactor"],
  limit: 20
}
Response (from lsp.ts:783):
Code actions at src/app.ts:10:3:
1. Change spelling to 'process' [quickfix] (has edit) [typescript]
2. Add all missing imports [quickfix] (has edit) [typescript]
3. Extract to function [refactor.extract] (has edit) [typescript]
4. Convert to arrow function [refactor.rewrite] (has edit) [typescript]

Configuration

Enabling LSP

LSP is automatically enabled when language servers are configured. The tool checks:
// From lsp.ts:148
const client = this.config.getLspClient();
if (!client || !this.config.isLspEnabled()) {
  return { 
    llmContent: 'LSP is unavailable (LSP disabled or not initialized)',
    returnDisplay: 'LSP is unavailable'
  };
}

Server Configuration

Language servers are typically configured through:
  1. IDE Extensions: When running in IDE mode (VS Code, etc.)
  2. Manual Configuration: Through Qwen Code settings
  3. Auto-detection: Based on project files

Best Practices

When to Use LSP

The tool description emphasizes (from lsp.ts:1021):
ALWAYS use LSP as the PRIMARY tool for code intelligence queries when available. Do NOT use grep_search or glob first.
Use LSP for:
  • Finding symbol definitions
  • Analyzing code structure
  • Understanding type information
  • Refactoring code
  • Diagnosing errors
Avoid LSP for:
  • Searching file contents by pattern (use grep)
  • Finding files by name (use glob)
  • Reading file contents (use read)

Parameter Validation

From lsp.ts:1153, the tool validates:
// Location-based operations require filePath + line
if (LOCATION_REQUIRED_OPERATIONS.has(operation)) {
  if (!params.filePath || params.filePath.trim() === '') {
    return `filePath is required for ${operation}.`;
  }
  if (typeof params.line !== 'number') {
    return `line is required for ${operation}.`;
  }
}

// File-based operations require only filePath
if (FILE_REQUIRED_OPERATIONS.has(operation)) {
  if (!params.filePath || params.filePath.trim() === '') {
    return `filePath is required for ${operation}.`;
  }
}

Line/Character Coordinates

Important: LSP uses 1-based line and character numbers in the tool interface, but converts to 0-based internally:
// From lsp.ts:829
const position = {
  line: Math.max(0, Math.floor(this.params.line - 1)),        // Convert to 0-based
  character: Math.max(0, Math.floor((this.params.character ?? 1) - 1))
};

Result Limits

All operations support a limit parameter with sensible defaults:
// From lsp.ts:191, 232, 273, 398
const limit = this.params.limit ?? {
  goToDefinition: 20,
  findReferences: 50,
  documentSymbol: 50,
  workspaceSymbol: 20,
  // ...
};

Troubleshooting

LSP Not Available

Problem: Tool returns “LSP is unavailable” Solution: Ensure language servers are configured:
# Check if LSP is enabled
qwen config get lsp.enabled

# Enable LSP
qwen config set lsp.enabled true

No Results Returned

Problem: Operations return empty results Possible causes:
  1. Incorrect file path (use workspace-relative or absolute paths)
  2. Wrong line/character position
  3. Language server not initialized for file type
  4. Symbol not indexed yet
Solution:
// Use workspaceSymbol to search first
{ operation: "workspaceSymbol", query: "MyClass" }

// Then use returned location for precise lookup
{ 
  operation: "goToDefinition",
  filePath: "src/myclass.ts",  // From search result
  line: 10,
  character: 7
}

Server-Specific Operations

Problem: Need to target specific language server Solution: Use the serverName parameter:
{
  operation: "goToDefinition",
  filePath: "src/index.ts",
  line: 10,
  character: 5,
  serverName: "typescript"  // Target TypeScript LSP specifically
}

Path Resolution

From lsp.ts:847, paths are resolved as:
private resolveUri(filePath: string, workspaceRoot: string): string | null {
  if (!filePath) return null;
  
  // Already a URI
  if (filePath.startsWith('file://') || filePath.includes('://')) {
    return filePath;
  }
  
  // Convert to absolute path
  const absolutePath = path.isAbsolute(filePath)
    ? filePath
    : path.resolve(workspaceRoot, filePath);
    
  // Convert to file:// URI
  return pathToFileURL(absolutePath).toString();
}

Integration Examples

Symbol Navigation Workflow

// 1. Search for symbol in workspace
{ operation: "workspaceSymbol", query: "UserService" }
// Returns: src/services/user.ts:10:7

// 2. Get documentation
{ 
  operation: "hover",
  filePath: "src/services/user.ts",
  line: 10,
  character: 7
}

// 3. Find all usages
{
  operation: "findReferences",
  filePath: "src/services/user.ts",
  line: 10,
  character: 7,
  includeDeclaration: false
}

Error Analysis Workflow

// 1. Get all workspace errors
{ operation: "workspaceDiagnostics", limit: 100 }

// 2. Get errors for specific file
{ operation: "diagnostics", filePath: "src/buggy.ts" }

// 3. Get available fixes
{
  operation: "codeActions",
  filePath: "src/buggy.ts",
  line: 15,
  character: 5,
  endLine: 15,
  endCharacter: 20,
  codeActionKinds: ["quickfix"]
}

Call Graph Analysis

// 1. Prepare call hierarchy for function
{
  operation: "prepareCallHierarchy",
  filePath: "src/api.ts",
  line: 50,
  character: 9
}
// Returns: callHierarchyItem

// 2. Find who calls this function
{
  operation: "incomingCalls",
  callHierarchyItem: { /* from step 1 */ },
  limit: 50
}

// 3. Find what this function calls
{
  operation: "outgoingCalls",
  callHierarchyItem: { /* from step 1 */ },
  limit: 50
}

Source Code References

  • LSP tool implementation: packages/core/src/tools/lsp.ts
  • LSP types: packages/core/src/lsp/types.ts
  • Configuration: packages/core/src/config/config.ts:64