LSP: documentSymbol/hover return empty results (missing textDocument/didOpen)

Resolved 💬 3 comments Opened Mar 8, 2026 by ZodicSlanser Closed Apr 5, 2026

Description

The LSP integration does not return results for documentSymbol, hover, workspaceSymbol, or goToDefinition operations, despite the LSP server running correctly and producing diagnostics.

Environment

  • OS: Windows 10 (MSYS2/Git Bash)
  • Claude Code version: Latest (as of 2026-03-08)
  • Plugin: php-lsp@claude-plugins-official (Intelephense 1.16.5)
  • Node: v20.19.5 (via nvm4w)

Steps to Reproduce

  1. Install php-lsp plugin: /plugin install php-lsp@claude-plugins-official
  2. Install Intelephense globally: npm install -g intelephense
  3. Open a Laravel project
  4. Use the LSP tool:

```
LSP documentSymbol app/Model/User.php line:1 character:1
→ "No symbols found in document"

LSP hover app/Model/User.php line:13 character:40
→ "No hover information available"
```

Expected Behavior

documentSymbol should return class/method/property symbols. hover should return type information.

Actual Behavior

All interactive LSP operations return empty results. However, diagnostics DO work — Intelephense produces publishDiagnostics notifications (e.g., "Undefined type 'Illuminate\Foundation\Auth\User'") which Claude Code receives and displays.

Root Cause Analysis

I tested Intelephense directly by sending JSON-RPC messages via stdin/stdout and confirmed:

  1. The LSP server initializes correctly (13ms)
  2. Indexing starts and completes
  3. documentSymbol returns full results (namespace, class, properties, methods)
  4. Diagnostics are empty (types resolve correctly once indexed)

The critical difference: in my direct test, I sent a textDocument/didOpen notification before querying documentSymbol. The LSP spec requires this — servers only track documents that have been explicitly opened.

Hypothesis: Claude Code's LSP client is not sending textDocument/didOpen before performing queries like documentSymbol or hover, so the server has no knowledge of the file content and returns empty results.

Additional Windows Issues

Two additional issues encountered on Windows:

  1. uv_spawn ENOENT: The plugin config uses "command": "intelephense" which fails on Windows because Node's child_process.spawn can't resolve npm's shell script wrappers. It needs either intelephense.cmd or node <path>/intelephense.js. I worked around this by editing marketplace.json to use "command": "node", "args": ["<path>/intelephense.js", "--stdio"].
  1. Restart message persists: The "1 LSP server(s) provided by plugins require restart to activate" message appears on every /reload-plugins even after a full exit and restart. This message never clears.

Test Script

Node.js script that demonstrates Intelephense working correctly with proper didOpen:

const { spawn } = require('child_process');
const child = spawn('node', ['path/to/intelephense.js', '--stdio']);

function send(msg) {
  const json = JSON.stringify(msg);
  child.stdin.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
}

// 1. Initialize
send({ jsonrpc: '2.0', id: 1, method: 'initialize', params: {
  processId: process.pid,
  rootUri: 'file:///path/to/project',
  capabilities: { textDocument: { documentSymbol: { hierarchicalDocumentSymbolSupport: true } } }
}});

// 2. Initialized
setTimeout(() => send({ jsonrpc: '2.0', method: 'initialized', params: {} }), 1000);

// 3. Open document (REQUIRED for symbols/hover to work)
setTimeout(() => {
  const content = require('fs').readFileSync('path/to/File.php', 'utf8');
  send({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: {
    textDocument: { uri: 'file:///path/to/File.php', languageId: 'php', version: 1, text: content }
  }});
}, 3000);

// 4. Now documentSymbol works
setTimeout(() => send({ jsonrpc: '2.0', id: 2, method: 'textDocument/documentSymbol', params: {
  textDocument: { uri: 'file:///path/to/File.php' }
}}), 5000);

Summary

| Feature | Works? | Notes |
|---------|--------|-------|
| LSP server starts | Yes | After Windows .cmd workaround |
| Diagnostics | Yes | publishDiagnostics received by Claude Code |
| documentSymbol | No | Returns empty |
| hover | No | Returns empty |
| workspaceSymbol | No | Returns empty |
| goToDefinition | Not tested | Likely same issue |

The fix should be straightforward: send textDocument/didOpen with the file content before performing any document-level LSP operations.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗