Language Server (LSP)
BioLang includes a full Language Server Protocol implementation that provides
intelligent editor support. The LSP server (bl-lsp) supports
diagnostics, code completion, hover information, go-to-definition, symbol
renaming, and document formatting. It works with any LSP-compatible editor.
Features
- Diagnostics — Real-time error and warning underlines as you type. Catches undefined variables, type mismatches, unused bindings, and invalid sequence literals.
- Completion — Context-aware suggestions for variables, builtins, methods, file paths, and API function arguments. Includes documentation snippets and type signatures.
- Hover — Hover over any identifier to see its type, documentation, and signature. For bio functions, shows parameter descriptions and return types.
- Go to Definition — Jump to where a variable or function was defined, including across imported files.
- Rename Symbol — Rename a variable or function across all references in the file and its imports.
-
Document Formatting — Format the
entire file or a selection using the same formatter as
bl fmt.
VS Code Setup
Install the BioLang extension from the VS Code marketplace, or configure manually:
Option 1: Extension (Recommended)
# Install from the marketplace
code --install-extension bioras.biolang-vscode
The extension includes syntax highlighting, the LSP client, snippet support, and
a file icon for .bl files.
Option 2: Manual Configuration
Add to your VS Code settings.json:
{
"biolang.server.path": "bl-lsp",
"biolang.server.args": [],
"biolang.trace.server": "messages",
"[biolang]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "bioras.biolang-vscode"
}
}
Neovim Setup
Using nvim-lspconfig:
-- In your Neovim LSP configuration (e.g., init.lua)
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
-- Register the BioLang LSP
if not configs.biolang then
configs.biolang = {
default_config = {
cmd = { 'bl-lsp' },
filetypes = { 'biolang' },
root_dir = lspconfig.util.root_pattern('biolang.toml', '.git'),
settings = {},
},
}
end
lspconfig.biolang.setup({
on_attach = function(client, bufnr)
-- Enable completion triggered by <c-x><c-o>
vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
-- Keybindings
local opts = { noremap = true, silent = true, buffer = bufnr }
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
vim.keymap.set('n', '<leader>f', vim.lsp.buf.format, opts)
end,
})
For file type detection, add to ~/.config/nvim/ftdetect/biolang.lua:
vim.filetype.add({
extension = {
bl = 'biolang',
},
})
Emacs Setup
Using lsp-mode and eglot:
With lsp-mode
;; Add BioLang major mode (from MELPA or manual)
(require 'biolang-mode)
;; Register the LSP server
(with-eval-after-load 'lsp-mode
(add-to-list 'lsp-language-id-configuration '(biolang-mode . "biolang"))
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection '("bl-lsp"))
:activation-fn (lsp-activate-on "biolang")
:server-id 'biolang-ls)))
;; Enable LSP for BioLang files
(add-hook 'biolang-mode-hook #'lsp)
With eglot (Emacs 29+)
(require 'biolang-mode)
(add-to-list 'eglot-server-programs
'(biolang-mode "bl-lsp"))
(add-hook 'biolang-mode-hook #'eglot-ensure)
Helix Setup
Add to ~/.config/helix/languages.toml:
[[language]]
name = "biolang"
scope = "source.biolang"
file-types = ["bl"]
language-servers = ["bl-lsp"]
indent = { tab-width = 2, unit = " " }
comment-token = "#"
[language-server.bl-lsp]
command = "bl-lsp"
Completion Details
The LSP provides rich completion across several categories:
# After typing "seq |> ", completions show:
gc_content() Float — GC content as fraction 0.0-1.0
reverse_complement() DNA — Reverse complement of sequence
complement() DNA — Complement without reversing
transcribe() RNA — Transcribe DNA to RNA
len() Int — Sequence length
slice(start, end) DNA — Extract subsequence
validate() Bool — Validate sequence alphabet
kmer_count(k) Map — Count all k-mers
# After typing "ncbi_", completions show:
ncbi_search(db, term) — Search an NCBI database
ncbi_fetch(ids, db) — Fetch records by ID list
ncbi_summary(db, id) — Get document summary
ncbi_gene(term) — Quick gene lookup
ncbi_sequence(id) — Fetch sequence as FASTA text
ncbi_pubmed(term) — Search PubMed
Hover Information
Hovering over identifiers shows contextual information:
# Hovering over gc_content shows:
┌──────────────────────────────────────────────┐
│ gc_content() -> Float │
│ │
│ Calculate the GC content (fraction of G+C │
│ bases) of a DNA or RNA sequence. │
│ │
│ Returns: Float between 0.0 and 1.0 │
│ │
│ Example: │
│ dna"ATCGATCG" |> gc_content() # => 0.5 │
└──────────────────────────────────────────────┘
# Hovering over a variable shows its inferred type:
┌──────────────────────────────────────────────┐
│ seq: DNA │
│ Defined at line 3 │
│ Value: dna"ATCGATCG" │
└──────────────────────────────────────────────┘
Diagnostics
The LSP reports errors and warnings in real time:
# Undefined variable (red underline)
samplse |> print()
^^^^^^^
error: undefined variable 'samplse' (did you mean 'samples'?)
# Type mismatch (red underline)
dna"ATCG" |> translate()
^^^^^^^^^^
error: 'translate' expects RNA, got DNA (did you mean to call 'transcribe' first?)
# Unused variable (yellow underline)
let temp = compute_something()
^^^^
warning: unused variable 'temp'
# Invalid sequence literal (red underline)
let seq = dna"ATCXYZ"
^^^
error: invalid DNA characters 'XYZ' at position 3
Debug Logging
For troubleshooting LSP issues, enable debug logging:
# Start LSP with debug output
bl lsp --log-level debug --log-file /tmp/bl-lsp.log
# Watch the log
tail -f /tmp/bl-lsp.log