Language Server Protocol (LSP)
A modern approach to code intelligence
The Language Server Protocol (LSP) is a standardized communication protocol developed by Microsoft™ to enable real-time language analysis and intelligent code editing across different editors and IDE (Integrated Development Environment).
By decoupling language-specific logic from the editor itself, LSP allows developers to use advanced features such as autocompletion, diagnostics, code navigation and refactoring, regardless of the programming language or toolchain.
This protocol has revolutionized the way developers interact with code, making it possible to offer consistent, high-quality language support in any editor that implements LSP.
LSP in Neovim
Neovim includes a built-in LSP client that integrates with
language servers to give IDE features. The implementation maintains Neovim's
performance and extensibility while delivering code completion,
diagnostics and symbol navigation.
This native support ensures a consistent experience across languages and
workflows.
LSP in Rocksmarker
Rocksmarker leverages Neovim's native LSP client to enhance editing for
code-intensive documentation and multi-language projects. The integration
uses mason.nvim, nvim-lspconfig, and mason-lspconfig.nvim to
streamline language server management.
- This setup provides:
-
Simplified installation and configuration of LSP servers
-
Consistent access to code intelligence, diagnostics, and refactoring tools
-
Maintained editor responsiveness during language server operations
The result is a unified workflow for coding, debugging, and technical writing within the same environment.
Core Functions
client_supports_method(client, method, bufnr)
Utility function to verify if an LSP client supports a specific method (e.g.,
textDocument/completion). Ensures feature compatibility before enabling
client-specific functionalities.
--- Check if a client supports a specific LSP method
--- @param client table The LSP client
--- @param method string The LSP method to check (e.g., "textDocument/completion")
--- @param bufnr integer|nil The buffer number (optional)
--- @return boolean
local function client_supports_method(client, method, bufnr)
return client:supports_method(method, bufnr)
end
Parameters:
- client (table): The LSP client.
- method (string): The LSP method to check.
- bufnr (integer|nil): Optional buffer number.
Returns:
- boolean: true if the client supports the method, false otherwise.
on_attach(client, bufnr)
Global callback triggered when an LSP client attaches to a buffer. Configures buffer-specific features such as document highlighting and inlay hints.
Code:
--- Global on_attach function for LSP clients
--- @param client table The LSP client
--- @param bufnr integer The buffer number
local function on_attach(client, bufnr)
-- Document highlight on cursor hold
if client_supports_method(client, "textDocument/documentHighlight", bufnr) then
local highlight_augroup = vim.api.nvim_create_augroup("lsp-highlight", { clear = false })
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
buffer = bufnr,
group = highlight_augroup,
callback = vim.lsp.buf.document_highlight,
})
vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
buffer = bufnr,
group = highlight_augroup,
callback = vim.lsp.buf.clear_references,
})
vim.api.nvim_create_autocmd("LspDetach", {
group = vim.api.nvim_create_augroup("lsp-detach", { clear = true }),
callback = function(event)
vim.lsp.buf.clear_references()
vim.api.nvim_clear_autocmds({ group = "lsp-highlight", buffer = event.buf })
end,
})
end
-- Inlay hints: enable on attach, toggle keymap
if client_supports_method(client, "textDocument/inlayHint", bufnr) then
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
end
end
Behavior:
- Creates autocommand groups for:
- CursorHold / CursorHoldI: Triggers vim.lsp.buf.document_highlight.
- CursorMoved / CursorMovedI: Clears references.
- LspDetach: Cleans up autocommands and references.
- Enables inlay hints if the client supports textDocument/inlayHint.
get_lsp_capabilities()
Constructs and extends the default LSP client capabilities to support advanced features like Markdown documentation, snippets, and completion enhancements. Ensures compatibility with modern LSP servers and plugins like blink.cmp.
Code:
--- Get LSP capabilities with support for Markdown and other features.
--- @return table The LSP client capabilities.
local function get_lsp_capabilities()
-- Initialize with default client capabilities
local capabilities = vim.lsp.protocol.make_client_capabilities()
-- Try to extend capabilities with blink.cmp
local blink_cmp_ok, blink_cmp = pcall(require, "blink.cmp")
if blink_cmp_ok then
capabilities = blink_cmp.get_lsp_capabilities(capabilities)
else
vim.notify("blink.cmp not found. Using default capabilities.", vim.log.levels.WARN)
end
-- Ensure the structure exists
capabilities.textDocument = capabilities.textDocument or {}
capabilities.textDocument.completion = capabilities.textDocument.completion or {}
capabilities.textDocument.completion.completionItem = capabilities.textDocument.completion.completionItem or {}
-- Extend completion item capabilities with Markdown support
local new_capabilities = {
documentationFormat = { "markdown", "plaintext" },
snippetSupport = true,
preselectSupport = true,
insertReplaceSupport = true,
labelDetailsSupport = true,
deprecatedSupport = true,
commitCharactersSupport = true,
tagSupport = { valueSet = { 1 } },
resolveSupport = {
properties = {
"documentation",
"detail",
"additionalTextEdits",
},
},
}
-- Manually extend the table
for key, value in pairs(new_capabilities) do
capabilities.textDocument.completion.completionItem[key] = value
end
return capabilities
end
Integration:
- Attempts to extend capabilities using blink.cmp.get_lsp_capabilities(). Falls back to defaults if blink.cmp is unavailable.
setup_lsp_server(server_name, config)
Helper function to set up an LSP server with consistent settings, including on_attach and capabilities. Simplifies server configuration and ensures error handling for compatibility with Neovim 0.11+.
Code:
-- Helper function to setup an LSP server with error handling.
--- @param server_name string The name of the LSP server
--- @param config table|nil The configuration table for the server
local function setup_lsp_server(server_name, config)
-- Check if Neovim 0.11+ is available.
if not vim.lsp.config then
vim.notify("vim.lsp.config not available. Ensure you are using Neovim 0.11+.", vim.log.levels.ERROR)
return
end
-- Merge the provided config with on_attach and capabilities
local final_config = vim.tbl_deep_extend("force", {
on_attach = on_attach,
capabilities = get_lsp_capabilities(),
}, config or {})
-- Set up the LSP server with the provided configuration.
local success, err = pcall(function()
vim.lsp.config(server_name, final_config)
end)
if not success then
vim.notify("Failed to setup LSP server: " .. server_name .. ". Error: " .. err, vim.log.levels.ERROR)
end
end
Parameters:
- server_name (string): Name of the LSP server (e.g., "taplo", "marksman").
- config (table|nil): Optional server-specific configuration.
Behavior:
- Merges the provided config with default settings (on_attach, capabilities).
- Logs errors if the server setup fails.
LSP Server Configurations
1. Taplo (TOML Support)
Configures the Taplo LSP server for TOML file support, providing validation, formatting, and completion features. Useful for managing configuration files like rocks.toml.
Code:
-- Configure Taplo language server for TOML file support in Neovim.
setup_lsp_server("taplo", {
-- Settings specific to Taplo LSP.
settings = {
-- Formatting configuration: enable or disable automatic formatting.
format = {
enable = true,
},
-- Completion configuration: control how code completion works.
completion = {
enable = true,
-- Characters that trigger completion suggestions.
triggerCharacters = { ".", '"', "'" },
},
-- Diagnostics configuration: control how errors and warnings are displayed.
diagnostics = {
enable = true,
severity = "Error",
},
-- Schema support configuration: enable or disable JSON schema validation.
schema = {
enable = false,
},
},
-- Specify the filetypes for which Taplo should be activated.
filetypes = { "toml" },
})
Features:
- Auto-formatting: Enabled by default.
- Completion: Triggered by
.,", and'characters. - Diagnostics: Reports errors only.
- Schema validation: Turned off by default.
2. Vale LS (Prose Linting)
Configures the Vale LS server for linting prose in Markdown, plain text, TeX, and reStructuredText files. Ensures consistency and style adherence in documentation.
Code:
-- Configure vale_ls for prose linting in Neovim.
setup_lsp_server("vale_ls", {
-- Specify the filetypes for which vale_ls should be activated.
filetypes = { "markdown", "text", "tex", "rst" },
-- Enable single file support.
single_file_support = true,
})
Features:
- Single-file support: Works without requiring a project root directory.
- Customizable: Can be configured via .vale.ini.
3. Marksman (Markdown Support)
Configures the Marksman LSP server for Markdown files, providing features like diagnostics, completion, and hover previews. Enhances the editing experience for documentation and notes.
Code:
-- Configure Marksman for Markdown language support in Neovim.
setup_lsp_server("marksman", {
-- Use extended client capabilities for full LSP feature support.
capabilities = get_lsp_capabilities(),
-- Specify the filetypes for which Marksman should be activated.
filetypes = { "markdown" },
-- Enable single file support.
single_file_support = true,
-- Optional: Add any additional settings for Marksman.
settings = {
marksman = {
-- Enable or disable automatic formatting.
format = {
enable = false,
},
-- Enable or disable diagnostics (e.g., broken links).
diagnostics = {
enable = true,
},
-- Enable or disable completion for links, references, etc.
completion = {
enable = true,
},
-- Enable or disable hover information (e.g., link previews).
hover = {
enable = true,
},
},
},
})
Features:
- Diagnostics: Detects issues like broken links.
- Completion: Provides suggestions for links and references.
- Hover previews: Shows link previews on hover.
- Formatting: Disabled by default.
4. Harper LS (Grammar & Style Checking)
Configures the Harper LS server for grammar, style, and spell-checking in prose and Markdown files. Helps maintain high-quality writing by detecting errors and suggesting improvements.
Code:
-- Configure Harper language server for grammar and style checking in Neovim.
setup_lsp_server("harper_ls", {
settings = {
-- All Harper-specific settings must be nested under the harper-ls key.
["harper-ls"] = {
-- Linters configuration: enable or disable specific grammar and style checks.
linters = {
SpellCheck = true,
SpelledNumbers = false,
AnA = true,
SentenceCapitalization = true,
UnclosedQuotes = true,
WrongQuotes = false,
LongSentences = true,
RepeatedWords = true,
Spaces = true,
Matcher = true,
CorrectNumberSuffix = true,
UseTitleCase = false,
},
-- Code actions configuration: control how code actions are displayed.
codeActions = {
ForceStable = false,
},
-- Markdown-specific settings: control how Harper handles Markdown files.
markdown = {
IgnoreLinkTitle = false,
IgnoreCodeBlocks = true,
IgnoreInlineCode = true,
CheckLists = true,
CheckHeadings = true,
},
-- Set the severity level for diagnostics.
-- Options: "error", "warning", "information", "hint"
diagnosticSeverity = "hint",
-- Isolate English language checks to avoid false positives in mixed-language documents.
isolateEnglish = true,
},
},
})
Features:
- Spell-checking: Enabled by default.
- Markdown-aware: Ignores code blocks and checks headings/lists.
- Diagnostics: Displayed as hints to avoid intrusiveness.
- English isolation: Reduces false positives in mixed-language documents.
Mason Integration
Mason Setup
Initializes the Mason plugin, which manages the installation and updating of LSP servers, linters, and formatters. All dependencies are handled by rocks.nvim.
Code:
-- Setup Mason for managing LSP servers and related tools.
local mason_ok, mason = pcall(require, "mason")
if not mason_ok then
vim.notify("mason.nvim not installed.", vim.log.levels.ERROR)
return
end
mason.setup({}) -- Initialize Mason with default settings.
Mason-LSPConfig
Bridges Mason and nvim-lspconfig to automatically install and configure LSP servers listed in ensure_installed. Ensures that all required LSP servers are available and properly set up.
Code:
-- Setup Mason-LSPConfig to bridge Mason and nvim-lspconfig.
local mason_lspconfig_ok, mason_lspconfig = pcall(require, "mason-lspconfig")
if not mason_lspconfig_ok then
vim.notify("mason-lspconfig not installed.", vim.log.levels.ERROR)
return
end
mason_lspconfig.setup({
-- List of LSP servers to automatically install.
ensure_installed = {
"emmylua_ls",
"html",
"cssls",
"marksman",
"harper_ls",
"yamlls",
"bashls",
"taplo",
"jsonls",
"vimls",
"vale_ls",
},
-- Handler to automatically set up each installed LSP server.
handlers = {
function(server_name)
setup_lsp_server(server_name, {})
end,
},
})
Installed Servers:
| Server | Purpose |
|---|---|
| emmylua_ls | Lua language support |
| html | HTML language support |
| cssls | CSS language support |
| marksman | Markdown language support |
| harper_ls | Grammar and style checking |
| yamlls | YAML language support |
| bashls | Bash language support |
| taplo | TOML language support |
| jsonls | Json language support |
| vimls | Vim script language support |
| vale_ls | Prose linting |
Mason Tool Installer
Automatically installs formatting, linting, and utility tools to complement LSP servers. All dependencies are managed via rocks.nvim.
Code:
-- Mason tool installer for additional formatting, linting, and utility tools.
local mason_tool_installer_ok, mason_tool_installer = pcall(require, "mason-tool-installer")
if not mason_tool_installer_ok then
vim.notify("mason-tool-installer not installed.", vim.log.levels.ERROR)
return
end
mason_tool_installer.setup({
-- List of tools to automatically install for formatting, linting, and validation.
ensure_installed = {
"stylua",
"shfmt",
"yamlfmt",
"shellcheck",
"prettier",
"yamllint",
"jsonlint",
"vint",
},
auto_update = true,
run_on_start = true,
})
Tools:
| Tool | Purpose |
|---|---|
| stylua | Lua formatter |
| shfmt | Shell script formatter |
| yamlfmt | YAML formatter |
| shellcheck | Shell script linter |
| prettier | Multi-language formatter |
| yamllint | YAML linter |
| jsonlint | JSON linter |
| vint | Vim script linter |
Autocompletion with blink.cmp
Configures the blink.cmp plugin for autocompletion in Neovim. Provides a fast and customizable completion engine with fuzzy matching and keymap presets. Dependency managed by rocks.nvim.
Code:
-- Configure blink.cmp for autocompletion in Neovim.
local blink_cmp_ok, blink_cmp = pcall(require, "blink.cmp")
if not blink_cmp_ok then
vim.notify("blink.cmp not installed.", vim.log.levels.ERROR)
return
end
blink_cmp.setup({
-- Keymap configuration for completion behavior.
keymap = {
preset = "super-tab",
["<ESC>"] = { "cancel", "fallback" },
},
-- Command-line mode completion settings.
cmdline = {
keymap = {
preset = "default",
},
},
-- Fuzzy matching settings for completion results.
fuzzy = {
implementation = "lua",
},
})
Features:
- Super-Tab keymap: Uses Tab for navigation and selection.
- ESC key: Cancels completion or falls back to default behavior.
- Fuzzy matching: Uses Lua-based implementation for performance.
Inlay Hints Management
Manages inlay hints, which are inline annotations that display additional information (e.g., variable types) directly in the code. Prevents race conditions by disabling hints in Insert mode and re-enabling them afterward.
Code:
-- Inlay hints: disable during insert mode to prevent race conditions
vim.api.nvim_create_autocmd("InsertEnter", {
group = vim.api.nvim_create_augroup("lsp-inlay-hints-insert", { clear = true }),
callback = function(event)
vim.lsp.inlay_hint.enable(false, { bufnr = event.buf })
end,
})
vim.api.nvim_create_autocmd("InsertLeave", {
group = vim.api.nvim_create_augroup("lsp-inlay-hints-insert-leave", { clear = true }),
callback = function(event)
vim.schedule(function()
if vim.api.nvim_buf_is_valid(event.buf) then
vim.lsp.inlay_hint.enable(true, { bufnr = event.buf })
end
end)
end,
})
Integrated LSP management
These plugins combine to create a unified ecosystem for Language Server
Protocol (LSP) tools. They automate server configuration, dependency
management, and client communication, removing the need for manual setup.
The system manages initialization, error handling, and performance tuning
automatically.
This allows developers and technical writers to focus on coding, documentation, and productivity without distractions from underlying infrastructure. The outcome is a responsive, reliable, and maintenance-free environment for advanced language features.