IDE & Editor Setup

Use CalcMark in VS Code, Zed, Neovim, or Emacs with full language intelligence.

On this page

CalcMark ships an LSP server (cm lsp) that provides diagnostics, autocomplete, hover, go-to-definition, document symbols, semantic highlighting, and quick fixes in any editor that supports the Language Server Protocol.

VS Code #

Install the CalcMark extension from the VS Code Marketplace, or from the editors/vscode-calcmark/ directory in the repository.

The extension automatically finds the cm binary in your PATH. To use a specific binary:

{
  "calcmark.binaryPath": "/path/to/cm"
}
Dev Setup (Generic LSP Client)

If you’re building CalcMark from source or want to try the LSP before the official extension ships, you can use a generic LSP client extension to connect VS Code to cm lsp.

Prerequisites: VS Code 1.82+ and CalcMark built from source (task build) or installed via Homebrew.

Step 1: Install Generic LSP Client (v2) by zsol — search zsol.vscode-glspc in Extensions.

Step 2: Open your VS Code settings JSON (Ctrl+Shift+P -> “Preferences: Open User Settings (JSON)”) and add:

"glspc.server.command": "/path/to/cm",
"glspc.server.commandArguments": ["lsp"],
"glspc.server.languageId": ["calcmark"]

Replace /path/to/cm with the actual path to your cm binary. If cm is on your PATH (e.g., installed via Homebrew), you can use just "cm".

You also need to tell VS Code that .cm files are the calcmark language:

"files.associations": {
    "*.cm": "calcmark",
    "*.calcmark": "calcmark"
}

Step 3: Open a .cm file. You should see diagnostics, autocomplete (Ctrl+Space), hover info, and go-to-definition (Ctrl+click).

Limitations: The generic LSP client does not provide syntax highlighting (files appear as plain text), live preview, or auto-install of the cm binary. The official extension will include all of these.

Troubleshooting #

First step for any issue: Open the Output panel (Ctrl+Shift+U) and select “CalcMark LSP” from the dropdown on the right. This shows the server’s communication log and any errors.

No diagnostics or completions:

  • Check the CalcMark LSP output channel for errors
  • Verify cm lsp starts correctly: run cm lsp in a terminal – it should hang waiting for input (that’s normal)
  • Check that the file extension is .cm or .calcmark

“Command not found” error:

  • The cm binary path in settings must be correct. Use an absolute path if in doubt.
  • If you built from source, make sure you ran task build first

Completions don’t auto-popup:

  • Press Ctrl+Space to manually trigger completions
  • VS Code should auto-popup for LSP completions by default, but check that "editor.quickSuggestions" is enabled in your settings

Server crashes on startup (error -32097):

  • The cm binary on your PATH may be an older version without lsp support
  • Set calcmark.binaryPath in settings to the full path of the correct cm binary
  • Check the CalcMark LSP output channel – it will show what command failed

AI autocomplete blocking CalcMark suggestions:

  • GitHub Copilot or similar extensions can intercept completions. Disable for CalcMark files:
    "github.copilot.enable": { "calcmark": false }
    

Binary version mismatch:

  • If you’re developing CalcMark, rebuild with task build after pulling changes to keep the LSP server up to date

Zed #

Install the CalcMark extension from Zed’s extension marketplace.

Dev install (local development) #

Install the extension from your local checkout:

  1. Open Zed
  2. Open the command palette: Cmd+Shift+P
  3. Run zed: install dev extension
  4. Select the editors/zed-calcmark/ directory

The extension uses tree-sitter for syntax highlighting and cm lsp for language intelligence.

Uninstall dev extension #

  1. Open the command palette: Cmd+Shift+P
  2. Run zed: uninstall dev extension
  3. Select calcmark

Or remove it manually:

rm -rf ~/.local/share/zed/extensions/installed/calcmark

Then restart Zed.

Enable semantic highlighting #

Zed disables LSP semantic tokens by default. Add this to your Zed settings (Cmd+,) to enable context-aware highlighting for CalcMark:

{
  "languages": {
    "CalcMark": {
      "semantic_tokens": "full"
    }
  }
}

Useful keybindings in Zed #

These are Zed’s built-in LSP keybindings – they work automatically with the CalcMark extension:

ShortcutAction
Cmd+.Code actions (quick fixes for undefined variables)
Cmd+Shift+ODocument symbols (variables and headings outline)
Cmd+click or F12Go to definition (jump to variable assignment)
Cmd+Shift+MOpen diagnostics panel (errors and warnings)
hoverHover info (variable values, function signatures)
typeAutocomplete (functions, units, variables appear automatically)
Cmd+Shift+SpaceSignature help (parameter hints inside function calls)

Live preview from Zed #

Zed doesn’t support webview panels, so use cm watch for live preview.

Add a task to .zed/tasks.json in your project root:

[
  {
    "label": "CalcMark: Preview",
    "command": "cm watch \"$ZED_FILE\"",
    "reveal": "always",
    "hide": "on_success"
  }
]

Run it via Cmd+Shift+P -> task: spawn -> CalcMark: Preview, or bind it to a key in ~/.config/zed/keymap.json:

[
  {
    "context": "Workspace",
    "bindings": {
      "cmd-shift-r": ["task::Spawn", { "task_name": "CalcMark: Preview" }]
    }
  }
]

Now Cmd+Shift+R on any .cm file launches a live preview in your browser that auto-reloads on every save.

Neovim #

Neovim 0.11+ has built-in LSP support.

Add to your init.lua:

vim.filetype.add({ extension = { cm = 'calcmark', calcmark = 'calcmark' } })
vim.lsp.enable({ 'calcmark' })

Create ~/.config/nvim/lsp/calcmark.lua:

return {
  cmd = { 'cm', 'lsp' },
  filetypes = { 'calcmark' },
  root_markers = { '.git' },
}

That’s it. Open any .cm file and the language server attaches automatically.

If cm isn’t on your PATH, use the full path:

cmd = { '/path/to/cm', 'lsp' },

Verify It Works #

Open a .cm file and run:

:lua vim.print(vim.lsp.get_clients())

You should see a calcmark-lsp client. Try typing a = 1 / 0 – a diagnostic should appear.

Autocomplete #

Neovim’s built-in completion requires a manual trigger:

  • Ctrl+X Ctrl+O – trigger omni-completion (LSP completions)
  • Ctrl+N / Ctrl+P – cycle through results

For auto-popup completions as you type, install nvim-cmp (see below).

Auto-completions with nvim-cmp

The built-in Ctrl+X Ctrl+O workflow is functional but not ideal. nvim-cmp gives you automatic popup completions as you type.

Install with lazy.nvim #

Add to your lazy.nvim plugin spec:

{
  'hrsh7th/nvim-cmp',
  dependencies = {
    'hrsh7th/cmp-nvim-lsp',
  },
  config = function()
    local cmp = require('cmp')
    cmp.setup({
      sources = {
        { name = 'nvim_lsp' },
      },
      mapping = cmp.mapping.preset.insert({
        ['<C-Space>'] = cmp.mapping.complete(),
        ['<CR>'] = cmp.mapping.confirm({ select = true }),
        ['<C-n>'] = cmp.mapping.select_next_item(),
        ['<C-p>'] = cmp.mapping.select_prev_item(),
      }),
    })
  end,
},

Install with packer.nvim #

use {
  'hrsh7th/nvim-cmp',
  requires = { 'hrsh7th/cmp-nvim-lsp' },
  config = function()
    local cmp = require('cmp')
    cmp.setup({
      sources = { { name = 'nvim_lsp' } },
      mapping = cmp.mapping.preset.insert({
        ['<C-Space>'] = cmp.mapping.complete(),
        ['<CR>'] = cmp.mapping.confirm({ select = true }),
      }),
    })
  end,
}

After installing, restart Neovim. Completions will auto-popup as you type in .cm files – variables, functions (including natural-language syntax like average of), and units.

Troubleshooting #

LSP not attaching:

  • Check :lua vim.print(vim.lsp.get_clients()) – should show calcmark-lsp
  • Verify the filetype: :set ft? should show calcmark
  • Check the server can start: run cm lsp in a terminal (it should hang waiting for input – that’s correct)

No diagnostics appearing:

  • Ensure vim.diagnostic is enabled (it is by default in Neovim 0.11+)
  • Try :lua vim.diagnostic.get() to see if diagnostics exist but aren’t rendering

Completions not showing with nvim-cmp:

  • Verify cmp-nvim-lsp is installed: :lua print(require('cmp_nvim_lsp')) should not error
  • Check the LSP is providing completions: :lua vim.print(vim.lsp.get_clients()[1].server_capabilities.completionProvider)

Emacs #

With eglot (built-in since Emacs 29):

(add-to-list 'auto-mode-alist '("\\.cm\\'" . fundamental-mode))
(add-to-list 'auto-mode-alist '("\\.calcmark\\'" . fundamental-mode))
(add-to-list 'eglot-server-programs '((fundamental-mode :language-id "calcmark") "cm" "lsp"))
(add-hook 'fundamental-mode-hook #'eglot-ensure)

Live Preview #

For a browser-based live preview (any editor):

cm watch budget.cm

This starts a local HTTP server that watches the file and auto-reloads the browser on every save. The URL is printed to stderr – it includes a random session token for security.

Options:

cm watch --port 8080 budget.cm    # Custom port (default: 3141)

What the LSP Provides #

FeatureDescription
DiagnosticsErrors and warnings with precise source positions
AutocompleteFunctions, units, variables, NL syntax, date keywords
HoverVariable values, function signatures, unit descriptions
Go-to-definitionJump to variable assignment
Document symbolsVariables and headings in outline view
Semantic tokensContext-aware highlighting (calc vs Markdown lines)
Code actionsQuick fixes for undefined variables (“did you mean?”)
Signature helpParameter hints for both function-call and natural-language syntax

Autocomplete Details #

The LSP provides context-sensitive completions:

  • Functions — both call form (compound() and natural-language snippets (compound $1000 by 5% monthly over 10 years). Each item carries structured parameter metadata so editors can show type-aware placeholder suggestions.
  • Variables — in-scope variables filtered by type compatibility when inside a function argument. For example, inside accumulate(|), only rate-typed variables appear.
  • Enum values — for parameters like throughput(|), only valid network types (gigabit, wifi, etc.) are offered.
  • Units — after in or as keywords for unit conversion.
  • Date keywordstoday, next Friday, this quarter, etc.

Signature Help Details #

When your cursor is inside a function call, the LSP shows the full function signature with the active parameter highlighted, its type, and example values. This works for both syntaxes:

  • Function calls: compound($10000, |) highlights the rate parameter with type percentage and examples like 5%, 8.5%
  • Natural-language forms: compound $1000 by | shows the same parameter hints, tracking which argument the cursor is nearest to

Optional parameters (like period in compound or salvage in depreciate) are marked with ? in the signature and documented as optional in the parameter hints.