Skip to content

Repository files navigation

Extra Small Markdown LSP (xs-md)

Another LSP server for Markdown note-taking.

Installation

If you're on MacOS or Linux:

curl -sSfL https://raw.githubusercontent.com/RaquerLabs/xsmd/main/install.sh | sh

If you're on windows:

iwr https://raw.githubusercontent.com/RaquerLabs/xsmd/main/install.ps1 | iex

Configuration

The LSP server looks for an xsmd.toml file at the root of your project. You can configure the following options in it:

# Enable verbose debug logs printed to xsmd.log
debug = false

# Folders to ignore during autocomplete. Paths must start from the project root directory.
# For example, "/journal" will ignore everything in "/journal/*"
ignore = []

The same settings can be provided per-editor through the LSP initializationOptions payload, which override xsmd.toml:

{ "debug": true, "ignore": ["/journal"] }

In Neovim this is passed via init_options:

local lsp_config = {
  name = "xsmd",
  cmd = { "xsmd" },
  filetypes = { "markdown" },
  init_options = { debug = false, ignore = {} },
}
vim.lsp.start(lsp_config)

Neovim Setup

To ensure Neovim launches a single xsmd process and correctly shares/reuses it across all open Markdown buffers, configure the server with a dynamically resolved root_dir, specify name = "xsmd", and disable single_file_support:

local lsp_config = {
  name = "xsmd",
  cmd = { "xsmd" },
  filetypes = { "markdown" },
  -- Dynamically resolve the workspace root per-buffer
  root_dir = function(filepath)
    return vim.fs.root(filepath, { "xsmd.toml", ".git" })
  end,
  -- Prevent spawning a process per file/buffer if no root is detected
  single_file_support = false,
  -- ... on_attach, capabilities, settings
}
vim.lsp.start(lsp_config)

Commands

The server provides a list of commands for debug:

  • xsmd.dumpState: Outputs a list of all current indexed document keys to xsmd.log. In Neovim, you can run this with:
    :XsmdDump
    

Features Implemented

  • Workspace Crawling: Scans your vault on boot, locates the project root via the anchor file xsmd.toml.
  • Workspace File Watching: Dynamically registers filesystem watchers for Markdown files (**/*.md, **/*.markdown) to automatically keep the in-memory database in sync when files are changed externally.
  • Go to Definition:
    • Links starting with / (e.g., [Link](/docs/file.md)) are resolved relative to the workspace root.
    • Links not starting with / (e.g., [Link](../file.md)) are resolved relative to the current file's folder.
  • Find References
  • Folding:
    • # Headings, ## Subheadings
    • nested lists (- or *)
  • Autocomplete:
    • Caches the primary # H1 Title of every note in the directory. Filtering out files that don't have # H1 Title headers.
    • Typing [ autocompletes with note names, adding the folder-relative [Title Text](../path/to/note.md) snippet.
    • Typing ( inside a link (e.g., [Label]() autocompletes with paths, also adds the folder-relative snippet.
  • Rename: Moves files and automatically updates all reference links across the workspace.

Todo

  • Anchor completion: complete #heading anchors — in-file headings for [](#, and target-file headings for links like [x](file.md# — backed by a per-document heading index.

How It Works Under the Hood

The server communicates with Neovim using standard input/output (stdin/stdout) over JSON-RPC.

       ┌───────────┐      JSON-RPC (stdio)      ┌─────────────┐
       │    IDE    │ ────────────────────────>  │   Go Core   │
       │ (Buffers) │ <────────────────────────  │ (LSP Server)│
       └───────────┘                            └─────────────┘
                                                       │
                                        ┌──────────────┴──────────────┐
                                        ▼                             ▼
                                ┌──────────────┐              ┌──────────────┐
                                │ In-Memory    │              │   Goldmark   │
                                │ State Index  │              │  AST Parser  │
                                └──────────────┘              └──────────────┘

Documentation

Developer Quick Start

Build and Run

Compile:

mise run build

Launch the LSP server:

./dist/xsmd

List indexed workspace files (ignoring configured directories):

./dist/xsmd list

List workspace files as JSON (path, title, has_h1), sorted by path:

./dist/xsmd list --json

Print the version:

./dist/xsmd --version

To install it globally:

mise run install

Run Tests

mise run test

How to Contribute

  1. Fork the repo and make your adjustments in the Go code.
  2. Format your files using mise run format.
  3. Assert that all unit tests pass with mise run test.
  4. Send a PR

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages