Language Server Protocol

Overview

Panache includes a built-in Language Server Protocol (LSP) implementation that provides document formatting capabilities for editors and IDEs.

Features

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
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
Live 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)
Auto-completion
Smart completions for Markdown syntax (textDocument/completion)
Symbol renaming
Rename references and their definitions together (textDocument/rename)
Document tracking
Incremental synchronization for efficiency (textDocument/didOpen, textDocument/didChange, textDocument/didClose)
Configuration discovery
Automatic detection from workspace root
Format on save
Automatic formatting when saving files

Starting the Server

Most users should install an editor extension and let the editor start the server automatically.

Manual start (for debugging):

panache lsp

The server communicates over stdin/stdout using the standard LSP JSON-RPC protocol.

Editor Configuration

Neovim

In Neovim +0.11, you can use the built-in LSP client:

-- .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:

-- 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:

vim.api.nvim_create_autocmd("BufWritePre", {
  pattern = { "*.qmd", "*.md", "*.rmd" },
  callback = function()
    vim.lsp.buf.format({ async = false })
  end,
})

Format the current buffer with:

:lua vim.lsp.buf.format()

Or map it to a key:

vim.keymap.set("n", "<leader>f", vim.lsp.buf.format, { desc = "Format buffer" })

VS Code

Panache is available as a VS Code extension with built-in LSP support:

To install through the command line, run:

# VS Code
code --install-extension jolars.panache

If you prefer to install from the VS Code UI, launch VS Code Quick Open (Ctrl+P), then run:

ext install jolars.panache

The extension launches panache lsp automatically.

Optional extension settings:

{
  "panache.commandPath": "panache",
  "panache.downloadBinary": true,
  "panache.releaseTag": "latest",
  "panache.serverArgs": [],
  "panache.serverEnv": { "RUST_LOG": "info" },
  "panache.trace.server": "off"
}

Cursor

Install the Panache extension from the marketplace:

ext install jolars.panache

Open a Markdown or Quarto file and the extension will automatically start the LSP server.

Helix

Add to ~/.config/helix/languages.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:

(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 package
  2. Add to LSP settings:
{
  "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:
{
  "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:

" 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

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:

" 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).

Document Synchronization

The LSP tracks document changes using incremental synchronization:

textDocument/didOpen
Document opened in editor
textDocument/didChange
Document content changed (incremental deltas sent)
textDocument/didClose
Document closed

This ensures the LSP always has the current document state while minimizing data transfer.

Troubleshooting

LSP Not Starting

Check that panache is in your PATH:

which panache

Test the LSP manually:

panache lsp
# Should start and wait for JSON-RPC input

Formatting Not Working

Enable LSP logging in your editor to see error messages:

Neovim

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:

panache format --config .panache.toml test.qmd

The LSP searches for config from the workspace root, not the file’s directory.