---
title: Language Server
description: >
Panache includes a built-in language server protocol (LSP) implementation that
provides a rich set of editor features for Pandoc Markdown, Quarto, and R
Markdown files.
---
Panache includes a built-in language server protocol (LSP) implementation that
provides a rich set of editor features for Pandoc Markdown, Quarto, and R
Markdown files. The language server is designed to be editor-agnostic and
configurations for popular editors like Neovim, VS Code, Helix, Zed, and Emacs
are provided in [Editor Configuration](#editor-configuration).
## Features
In this section, we provide an overview of the key features supported by the
Panache LSP.
Document formatting
: Format entire documents or selected ranges (`textDocument/formatting`,
`textDocument/rangeFormatting`)
Go to definition
: Jump from references to definitions (`textDocument/definition`)
- Reference links: `[text][ref]` → `[ref]: url`
- Reference images: `![alt][ref]` → `[ref]: url`
- Footnote references: `[^id]` → `[^id]: content`
- Citation references: `[@key]` → `[@key]: content`
- Quarto crossrefs: `@fig-label` → chunk/equation/figure label definition
Find references
: Find all usages of a symbol (`textDocument/references`)
- Quarto crossrefs and chunk labels
- Citation keys (with optional bibliography/inline-reference declarations)
Document outline
: Hierarchical view of document structure (`textDocument/documentSymbol`)
- Headings (H1-H6) with proper nesting
- Tables (with captions when available)
- Figures (images with alt text)
Code folding
: Fold sections of your document (`textDocument/foldingRange`)
- Headings
- Code blocks
- Fenced divs
- YAML frontmatter
Diagnostics
: Real-time linting as you type (`textDocument/publishDiagnostics`)
- Heading hierarchy violations
- Duplicate references
- Citation validation
- Parser errors
- External linter integration (e.g., R code linting)
Code actions
: Quick fixes and refactorings (`textDocument/codeAction`)
- Auto-fix lint issues (e.g., heading hierarchy corrections)
- Convert list styles (loose ↔ compact)
- Convert footnote styles (inline ↔ reference)
Hover information
: Contextual information on hover (`textDocument/hover`)
- Footnote definitions
- Citation previews
Auto-completion
: Smart completions for Markdown syntax (`textDocument/completion`)
- Citation keys
- Reference labels
Symbol renaming
: Rename references and their definitions together (`textDocument/rename`)
- Citation keys
- Reference labels
- Quarto crossref labels (including executable chunk `label` options)
Document tracking
: Incremental synchronization for efficiency (`textDocument/didOpen`,
`textDocument/didChange`, `textDocument/didClose`)
Configuration discovery
: Automatic detection from workspace root
## Editor Configuration {#editor-configuration}
If you want to start the language server manually (for debugging), then you can
run `panache lsp` in your terminal. This will start the server and wait for
JSON-RPC input. But most users will want to set up their editor to start the
server automatically when editing supported files. See the editor integration
instructions below.
### Neovim
In Neovim +0.11, you can use the built-in LSP client:
```lua
-- .config/nvim/lsp/panache.lua
return {
cmd = { "panache", "lsp" },
filetypes = { "quarto", "markdown", "rmarkdown" },
root_markers = { ".panache.toml", "panache.toml", ".git" },
settings = {},
}
-- Enable it
vim.lsp.enable({"panache"})
```
For earlier Neovim releases, use `nvim-lspconfig`:
```lua
-- Add to your LSP config
local lspconfig = require("lspconfig")
local configs = require("lspconfig.configs")
-- Define panache LSP
if not configs.panache then
configs.panache = {
default_config = {
cmd = { "panache", "lsp" },
filetypes = { "quarto", "markdown", "rmarkdown" },
root_dir = lspconfig.util.root_pattern(
".panache.toml",
"panache.toml",
".git"
),
settings = {},
},
}
end
-- Enable it
lspconfig.panache.setup({})
```
Note that you need to have `panache` in your PATH for the above configurations
to work.
To format on save, add an autocmd:
```lua
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = { "*.qmd", "*.md", "*.rmd" },
callback = function()
vim.lsp.buf.format({ async = false })
end,
})
```
Format the current buffer with:
```vim
:lua vim.lsp.buf.format()
```
Or map it to a key:
```lua
vim.keymap.set("n", "<leader>f", vim.lsp.buf.format, { desc = "Format buffer" })
```
### VS Code
Install the [VS Code Marketplace
extension](https://marketplace.visualstudio.com/items?itemName=jolars.panache).
The extension starts the language server automatically for supported files.
To install through the command line, run:
```bash
code --install-extension jolars.panache
```
If you prefer to install from the VS Code UI, launch VS Code Quick Open
(`Ctrl+P`), then run:
```sh
ext install jolars.panache
```
The extension launches `panache lsp` automatically.
Optional extension settings:
```json
{
"panache.commandPath": "panache",
"panache.downloadBinary": true,
"panache.releaseTag": "latest",
"panache.serverArgs": [],
"panache.serverEnv": { "RUST_LOG": "info" },
"panache.trace.server": "off",
"panache.experimental.incrementalParsing": false
}
```
`panache.experimental.incrementalParsing` enables an experimental incremental
parse path for `textDocument/didChange`. It is disabled by default.
### Open VSX (Positron, Cursor, VSCodium, etc.)
Install the [Open VSX extension](https://open-vsx.org/extension/jolars/panache).
The extension starts the language server automatically for supported files.
The Open VSX extension is identical to the VS Code extension and uses the same
configuration settings, so see the [VS Code] section above for configuration
instructions.
### Helix
Add to `~/.config/helix/languages.toml`:
```toml
[[language]]
name = "markdown"
language-servers = ["panache"]
auto-format = true
[[language]]
name = "quarto"
language-servers = ["panache"]
auto-format = true
[[language]]
name = "rmarkdown"
language-servers = ["panache"]
auto-format = true
[language-server.panache]
command = "panache"
args = ["lsp"]
```
Format the current file with `:format` or `<space>f`.
### Emacs
Using `lsp-mode`:
```elisp
(require 'lsp-mode)
;; Define panache LSP client
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection '("panache" "lsp"))
:activation-fn (lsp-activate-on "quarto" "markdown" "rmarkdown")
:server-id 'panache))
;; Enable for specific modes
(add-hook 'markdown-mode-hook #'lsp-deferred)
(add-hook 'quarto-mode-hook #'lsp-deferred)
;; Format on save
(add-hook 'before-save-hook #'lsp-format-buffer nil t)
```
### Sublime Text
Using LSP package:
1. Install [LSP](https://packagecontrol.io/packages/LSP) package
2. Add to LSP settings:
```json
{
"clients": {
"panache": {
"enabled": true,
"command": ["panache", "lsp"],
"selector": "text.html.markdown | source.quarto"
}
}
}
```
### Kate
Kate supports LSP servers via its LSP client plugin:
1. Enable the LSP Client plugin
2. Add to LSP client configuration:
```json
{
"servers": {
"markdown": {
"command": ["panache", "lsp"],
"highlightingModeRegex": "^Markdown$"
}
}
}
```
## Configuration Discovery
The LSP automatically discovers configuration files from your workspace:
1. Searches for `.panache.toml` or `panache.toml` from workspace root
2. Falls back to `~/.config/panache/config.toml`
3. Uses built-in defaults if no config found
### Workspace Root Detection
The LSP determines the workspace root by looking for:
- `.panache.toml` or `panache.toml`
- `.git` directory
- Project-specific files (`.quarto.yml`, `_quarto.yml`, etc.)
## Capabilities
### Document Formatting
The LSP provides full document and range formatting via
`textDocument/formatting` and `textDocument/rangeFormatting` requests. This uses
the same formatting engine as the `panache format` CLI command.
Format entire documents or selected ranges:
```vim
" Neovim: format buffer
:lua vim.lsp.buf.format()
" Neovim: format selected range (visual mode)
:'<,'>lua vim.lsp.buf.format()
```
Format on save is supported by all major editors. See editor configuration
sections above for setup instructions.
### Go to Definition
Jump from link/footnote references to their definitions:
Reference links
: `[text][label]` → `[label]: url`
Shortcut reference links
: `[label]` → `[label]: url`
Reference images
: `![alt][label]` → `[label]: url`
Footnote references
: `[^id]` → `[^id]: content`
Quarto crossrefs
: `@fig-label` → `#| label: fig-label` (or other crossref label definitions)
Place your cursor on a reference and trigger "go to definition" (F12 in many
editors).
### Document Outline
The LSP provides a hierarchical document outline showing:
Headings
: H1-H6 with proper nesting levels
Tables
: With captions when available
Figures
: Image links with alt text
The outline appears in:
- **VSCode**: Outline view (sidebar) or breadcrumbs
- **Neovim**: Telescope symbols (`:Telescope lsp_document_symbols`) or Aerial
plugin
- **Helix**: Symbol picker (`:symbol-picker`)
The outline updates automatically as you edit.
### Code Actions
Quick fixes and refactorings available at the cursor position:
Auto-fix lint issues
: Fix heading hierarchy violations automatically
Convert list styles
: Toggle between loose (blank lines) and compact list formatting
Convert footnote styles
: Toggle between inline `^[text]` and reference `[^id]` footnotes
Trigger code actions:
```vim
" Neovim: show code actions at cursor
:lua vim.lsp.buf.code_action()
```
Most editors show a lightbulb icon when code actions are available.
### Folding Ranges
Fold sections of your document for easier navigation:
Headings
: Fold sections under headings
Code blocks
: Fold multi-line fenced code blocks
Fenced divs
: Fold `::: {.class}` content
YAML frontmatter
: Fold `---` delimited metadata
Most editors support folding with default key bindings (e.g., `za` in Neovim,
`Ctrl+Shift+[` in VSCode).
### Live Diagnostics
Linting errors and warnings appear in real-time as you type:
Built-in rules
: Heading hierarchy, duplicate references, citation validation, parser errors
External linters
: Code block linting (e.g., `jarl` for R when configured in `[linters]`)
Diagnostics appear as:
- Squiggly underlines in the editor
- Hover tooltips with error messages
- Problems/diagnostics panel
Quick fixes are available via code actions where applicable.
### Hover Information
Hover over elements to see contextual information (implementation varies by
element type).
### Auto-Completion
Smart completions for Markdown syntax (implementation varies by context).
### Symbol Renaming
Rename references and their definitions together. Place cursor on a reference
label or definition and trigger rename (F2 in many editors).
### Find References
Find all references to the symbol under cursor (`textDocument/references`),
including Quarto crossrefs, executable chunk labels, and citation keys.
## Troubleshooting
### LSP Not Starting
Check that panache is in your PATH:
```bash
which panache
```
Test the LSP manually:
```bash
panache lsp
# Should start and wait for JSON-RPC input
```
### Formatting Not Working
Enable LSP logging in your editor to see error messages:
#### Neovim
```lua
vim.lsp.set_log_level("debug")
-- View logs: :lua vim.cmd('e'..vim.lsp.get_log_path())
```
#### VS Code
Set `"panache.trace.server": "verbose"` in settings.
### Configuration Not Loading
Verify your config file is valid by testing with the CLI first:
```bash
panache format --config .panache.toml test.qmd
```
The LSP searches for config from the workspace root, not the file's directory.