[BUG] LSP plugin extensionToLanguage does not match compound file extensions
LSP plugin support for compound file extensions
Summary
The LSP plugin system's extensionToLanguage configuration does not support compound file extensions like .tfcomponent.hcl, .tfdeploy.hcl, or .tftest.hcl. The extension matching algorithm only extracts the final segment after the last ., causing these files to match .hcl instead of their specific compound extension.
Evidence
terraform-ls Works Correctly (Direct Test)
We tested terraform-ls directly using the LSP protocol, bypassing Claude Code:
=== example.tfcomponent.hcl (languageId: terraform-stack) ===
Symbols: 2
- component "vpc"
- component "database"
=== example.tfdeploy.hcl (languageId: terraform-deploy) ===
Symbols: 4
- deployment "development"
- deployment "staging"
- deployment "production"
- orchestrate "rolling"
=== test.tftest.hcl (languageId: terraform-test) ===
Symbols: 2
- run "valid_pet_name"
- run "create_server"
terraform-ls fully supports all compound extensions when the correct languageId is provided.
Claude Code Fails (Extension Matching Issue)
Using the same files through Claude Code's LSP tool:
LSP operation: documentSymbol
File: example.tfcomponent.hcl
Result: "No LSP server available for file type: .hcl"
LSP operation: documentSymbol
File: example.tfdeploy.hcl
Result: "No LSP server available for file type: .hcl"
LSP operation: documentSymbol
File: test.tftest.hcl
Result: "No LSP server available for file type: .hcl"
Plugin Configuration
The plugin configuration is correct:
{
"lspServers": {
"terraform": {
"command": "terraform-ls",
"args": ["serve"],
"extensionToLanguage": {
".tf": "terraform",
".tfvars": "terraform-vars",
".tfstack.hcl": "terraform-stack",
".tfcomponent.hcl": "terraform-stack",
".tfdeploy.hcl": "terraform-deploy",
".tftest.hcl": "terraform-test",
".hcl": "terraform-stack"
}
}
}
}
Root Cause
Evidence from error message: When LSP fails, Claude Code reports "No LSP server available for file type: .hcl" - confirming it extracts only the final extension segment.
Likely implementation: Claude Code appears to use Node.js path.extname() or similar, which for example.tfcomponent.hcl returns only .hcl.
What happens:
- Takes filename
example.tfcomponent.hcl - Extracts extension as
.hcl(confirmed by error message) - Looks up
.hclinextensionToLanguage- doesn't find the intended.tfcomponent.hclmapping - Returns error:
"No LSP server available for file type: .hcl"
Expected Behavior
The extension matching should try the longest/most-specific match first:
- Take filename
example.tfcomponent.hcl - Try
.tfcomponent.hcl→ MATCH → returnterraform-stack - Send
languageId: "terraform-stack"to terraform-ls - LSP operations work correctly
Use Case
HashiCorp Terraform uses compound extensions for different file types:
| Extension | Purpose | Language ID |
|-----------|---------|-------------|
| .tf | Terraform configuration | terraform |
| .tfvars | Variable definitions | terraform-vars |
| .tftest.hcl | Terraform test files | terraform-test |
| .tfcomponent.hcl | Stacks component definitions | terraform-stack |
| .tfdeploy.hcl | Stacks deployment configs | terraform-deploy |
| .tfstack.hcl | Stacks definitions | terraform-stack |
This pattern is also used by other ecosystems:
- TypeScript:
.spec.ts,.test.ts,.d.ts - Angular:
.component.ts,.service.ts,.module.ts - Vue:
.vue.ts
Proposed Solution
Longest Match Wins: Sort extension keys by length (descending) and return the first match:
def get_language_id(filename, extension_map):
# Sort by length descending
sorted_extensions = sorted(extension_map.keys(), key=len, reverse=True)
for ext in sorted_extensions:
if filename.endswith(ext):
return extension_map[ext]
return None
Example:
filename: "example.tfcomponent.hcl"
sorted_extensions: [".tfcomponent.hcl", ".tfstack.hcl", ".tfdeploy.hcl", ".tftest.hcl", ".tfvars", ".hcl", ".tf"]
check: "example.tfcomponent.hcl".endswith(".tfcomponent.hcl") → True
return: "terraform-stack"
Environment
- Claude Code CLI: Latest
- OS: macOS Darwin 25.1.0
- terraform-ls: 0.38.3
Test Script
To reproduce the terraform-ls direct test:
# Full test script available - sends LSP protocol messages directly to terraform-ls
# Confirms the language server works correctly with all compound extensions
# Issue is isolated to Claude Code's extension matching
Related Issues
- #14803 - LSP plugins not recognized (race condition - fixed)
- #15619 - Enable LSP tool to connect to installed language servers
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗