The JCL Language Server provides IDE features like diagnostics, autocomplete, and hover information.
Installation
Build the LSP server:
cargo build --release --bin jcl-lsp
The binary will be available at target/release/jcl-lsp.
Features
- Diagnostics: Real-time syntax checking, linting, and schema validation
- Parse errors
- Unused variables and functions
- Naming convention violations
- Type annotation suggestions
- Redundant operations
- Schema validation errors (if
.jcf-schema.jsonor.jcf-schema.yamlpresent)
- Autocomplete: Intelligent code completion for:
- All 70+ built-in functions
- Keywords (fn, if, else, for, etc.)
- Type names
- Constants (true, false, null)
- Schema-based property suggestions (when schema is present)
- Hover: Documentation on hover
- Basic hover information for all symbols
- Schema information: Type, constraints, and descriptions for schema-defined fields
-
Go to Definition: Jump to variable and function definitions
-
Find References: Find all usages of a symbol
- Rename Symbol: Rename variables and functions across files
Editor Configuration
VSCode
Add to your settings.json:
{
"jcl.languageServer": {
"enabled": true,
"path": "/path/to/jcl-lsp"
}
}
Or use the generic LSP client extension and configure:
{
"languageServerExample.trace.server": "verbose",
"languageServerExample.servers": {
"jcl": {
"command": "/path/to/jcl-lsp",
"filetypes": ["jcl"],
"rootPatterns": [".git/"]
}
}
}
Neovim (with nvim-lspconfig)
Add to your init.lua:
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
-- Define JCL LSP if not already defined
if not configs.jcf then
configs.jcf = {
default_config = {
cmd = {'/path/to/jcl-lsp'},
filetypes = {'jcl'},
root_dir = lspconfig.util.root_pattern('.git', 'jcl.toml'),
settings = {},
},
}
end
-- Setup JCL LSP
lspconfig.jcf.setup{}
Emacs (with lsp-mode)
Add to your init.el:
(require 'lsp-mode)
(add-to-list 'lsp-language-id-configuration '(jcl-mode . "jcl"))
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection "/path/to/jcl-lsp")
:major-modes '(jcl-mode)
:server-id 'jcl))
(add-hook 'jcl-mode-hook #'lsp)
Sublime Text (with LSP package)
Add to LSP.sublime-settings:
{
"clients": {
"jcl": {
"enabled": true,
"command": ["/path/to/jcl-lsp"],
"selector": "source.jcf"
}
}
}
Schema Validation
The LSP server automatically discovers and loads schema files from your workspace root, providing real-time schema validation alongside linting errors.
Automatic Schema Discovery
When the LSP server initializes, it searches for schema files in your workspace root in this order:
.jcf-schema.json.jcf-schema.yaml.jcf-schema.ymljcl-schema.jsonjcl-schema.yaml
The first file found is loaded and used for validation. Both JSON and YAML schema formats are supported.
Example: Using Schema Validation
Create a schema file .jcf-schema.json in your workspace root:
{
"version": "1.0",
"title": "Application Configuration",
"type": {
"kind": "map",
"required": ["name", "port"],
"properties": {
"name": {
"type": {"kind": "string", "min_length": 1}
},
"port": {
"type": {"kind": "number", "minimum": 1, "maximum": 65535, "integer_only": true}
}
}
}
}
Now edit a JCL file:
# config.jcf
name = "my-app"
port = "8080" # ← Error: Type mismatch: expected Int, found String (at config.port)
The LSP will show schema validation errors in real-time:
- Red squiggles under invalid values
- Error messages with field paths
- Suggestions for fixes (when available)
Schema Validation Diagnostics
Schema validation errors appear alongside linting errors with:
- Source:
jcl-schema(vsjclfor linter) - Code:
schema-{ErrorType}(e.g.,schema-TypeMismatch) - Message: Includes field path (e.g., “at config.database.port”)
- Precise positioning: Errors appear exactly at the problematic field, not at (0,0)
Hot-Reloading
The LSP server automatically watches the schema file for changes. When you edit and save your schema file, the LSP will:
- Automatically reload the schema
- Re-validate all open documents
- Send a notification to the editor (success or error)
No need to restart your editor or reload the workspace!
Testing the LSP Server
Create a test file test.jcf:
# This should show autocomplete for built-in functions
result = map(x => x * 2, [1, 2, 3])
# This should show a warning about unused variable
unusedVar = 42
# This should show naming convention warning
MyVariable = "test"
# This should show redundant operation warning
value = x + 0
Open the file in your configured editor and you should see:
- Diagnostics highlighting issues
- Autocomplete when typing function names
- Hover information on symbols
Advanced Configuration
Custom Lint Rules
The LSP uses the same linter as jcl lint. You can configure which rules to enable by setting environment variables before starting the LSP:
JCL_LINT_LEVEL=info jcl-lsp
Logging
The LSP server logs to stderr. To see debug logs:
RUST_LOG=debug jcl-lsp 2> lsp.log
Architecture
The JCL LSP implementation uses:
- tower-lsp: Async LSP framework for Rust
- tokio: Async runtime
- Integration with existing JCL parser and linter
The server maintains a document cache and re-parses/re-lints on every change, providing real-time feedback.
Future Enhancements
Planned features:
- Code actions (quick fixes)
- Document symbols
- Workspace symbols
- Semantic highlighting
- Inlay hints for inferred types
Troubleshooting
LSP not starting
- Check that
jcl-lspis executable:chmod +x /path/to/jcl-lsp - Verify it runs:
/path/to/jcl-lsp(should wait for stdin) - Check editor LSP logs for errors
No diagnostics appearing
- Ensure file has
.jcfextension - Check that file is syntactically valid
- Look at LSP logs for errors
Autocomplete not working
- Verify LSP is connected (check editor status bar)
- Try typing a known function name like
map - Check that completion is triggered on
.and(
Contributing
To add new LSP features:
- Modify
src/lsp.rs - Add capabilities to
initialize()method - Implement the corresponding trait method
- Test with multiple editors
- Update this documentation