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 (textDocument/formatting)
  • Range formatting (textDocument/rangeFormatting)
  • Document outline (textDocument/documentSymbol) - hierarchical view of headings, tables, and figures
  • Live diagnostics (textDocument/publishDiagnostics) - linting errors and warnings
  • Code actions (textDocument/codeAction) - quick fixes for lint issues
  • Document tracking (textDocument/didOpen/didChange/didClose)
  • Configuration discovery from workspace root
  • Format on save support

Starting the Server

Start the LSP server:

panache lsp

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

Editor Configuration

Neovim

Using nvim-lspconfig:

-- Add to your LSP configuration file
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 the server
lspconfig.panache.setup{}

Format on Save

Add this to enable automatic formatting when saving:

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

Manual Formatting

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

Option 1: Custom LSP Extension

Install the Custom LSP extension, then configure in settings.json:

{
  "customLsp.servers": {
    "panache": {
      "command": "panache",
      "args": ["lsp"],
      "filetypes": ["quarto", "markdown", "rmarkdown"]
    }
  },
  "editor.formatOnSave": true
}

Option 2: Generic LSP Client

Install a generic LSP client extension, then configure:

{
  "languageServerExample.server": {
    "command": "panache",
    "args": ["lsp"],
    "filetypes": ["quarto", "markdown", "rmarkdown"]
  },
  "editor.formatOnSave": true,
  "[quarto]": {
    "editor.defaultFormatter": "languageServerExample"
  },
  "[markdown]": {
    "editor.defaultFormatter": "languageServerExample"
  }
}

Helix

Add to ~/.config/helix/languages.toml:

[[language]]
name = "markdown"
language-servers = ["panache-lsp"]
auto-format = true

[[language]]
name = "quarto"
language-servers = ["panache-lsp"]
auto-format = true

[language-server.panache-lsp]
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 formatting via the textDocument/formatting request. This is the same formatting as the panache format CLI command.

Format entire documents or selected ranges:

" Neovim: format buffer
:lua vim.lsp.buf.format()

" Neovim: format selected range
:'<,'>lua vim.lsp.buf.format()

Format on save is supported by all major editors (see configuration above).

Document Outline

The LSP provides a hierarchical document outline/symbol view showing:

  • Headings (H1-H6) with proper nesting
  • Tables (with captions when available)
  • Figures (image links with alt text)

This appears in:

  • VSCode: Outline view (sidebar) or breadcrumbs
  • Neovim: Telescope symbols (<leader>ss) or Aerial plugin
  • Helix: Symbol picker (:symbol-picker)

The outline is automatically updated as you edit the document.

Live Diagnostics

Linting errors and warnings are shown in real-time as you type:

  • Heading hierarchy violations
  • Future lint rules as they’re added

Quick fixes are available via code actions:

" Neovim: show code actions at cursor
:lua vim.lsp.buf.code_action()

Document Synchronization

The LSP tracks document changes using incremental synchronization mode:

  • textDocument/didOpen: Document opened in editor
  • textDocument/didChange: Document content changed (incremental sync)
  • textDocument/didClose: Document closed

This ensures the LSP always has the current document state for formatting.

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 "customLsp.trace.server": "verbose" in settings.

Configuration Not Loading

Verify your config file is valid:

# Test with CLI first
panache format --config panache.toml test.qmd

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

Performance Issues

For large documents:

  1. Reduce line_width in config
  2. Disable external formatters temporarily
  3. Use wrap = "preserve" instead of "reflow"

Comparison with CLI

Feature LSP CLI
Formatting ✅ Same engine ✅ Direct
Config discovery ✅ From workspace root ✅ From file directory
Format on save ✅ Automatic ❌ Manual
Multiple files ✅ Per-editor-tab ✅ Shell scripting
Error reporting ✅ In-editor diagnostics ✅ stderr output
External formatters ✅ Supported ✅ Supported

Best Practices

  1. Place config at project root: Put panache.toml at your project root for consistent behavior
  2. Use format on save: Enable format-on-save for automatic formatting
  3. Test CLI first: Verify formatting with CLI before relying on LSP
  4. Check logs: Enable LSP logging when debugging issues
  5. Restart after config changes: Restart LSP server after changing .panache.toml

See Also