Editor Setup

Norq includes a Language Server Protocol (LSP) server that provides editor intelligence for .md and .json template files inside notification directories.

Features

Feature Description
Diagnostics Real-time lint errors and warnings as you type
Completions Variable names from data.schema.yaml, directive names, pipe names
Hover Type info and descriptions from the schema on hover
Null safety Warnings when nullable fields are used without :::if guards
Code actions Quick fixes (e.g., wrap nullable access in :::if guard)

VS Code

Install the Norq extension from the VS Code marketplace. It bundles the LSP server and provides:

  • Syntax highlighting for .md template files
  • LSP features (diagnostics, completions, hover, code actions)
  • Inline preview panel
  • Channel switcher in the status bar

The extension automatically detects projects with norq.config.yaml and activates.

Neovim

vim.api.nvim_create_autocmd('FileType', {
  pattern = 'markdown',
  callback = function()
    vim.lsp.start({
      name = 'norq',
      cmd = { 'norq', 'lsp' },
      root_dir = vim.fs.root(0, 'norq.config.yaml'),
    })
  end,
})

With nvim-lspconfig

local lspconfig = require("lspconfig")
 
lspconfig.norq.setup({
  cmd = { "norq", "lsp" },
  filetypes = { "markdown" },
  root_dir = lspconfig.util.root_pattern("norq.config.yaml"),
})

With coc.nvim

Add to coc-settings.json:

{
  "languageserver": {
    "norq": {
      "command": "norq",
      "args": ["lsp"],
      "filetypes": ["markdown"],
      "rootPatterns": ["norq.config.yaml"]
    }
  }
}

Vim

Vim does not have a built-in LSP client. Use the vim-lsp plugin.

Add to your .vimrc:

if executable('norq')
  au User lsp_setup call lsp#register_server({
    \ 'name': 'norq',
    \ 'cmd': {server_info->['norq', 'lsp']},
    \ 'allowlist': ['markdown'],
    \ 'root_uri': {server_info->lsp#utils#path_to_uri(
    \   lsp#utils#find_nearest_parent_file_directory(
    \     lsp#utils#get_buffer_path(), 'norq.config.yaml'))},
    \ })
endif

Helix

Add to languages.toml:

[[language]]
name = "markdown"
language-servers = ["norq-lsp"]
 
[language-server.norq-lsp]
command = "norq"
args = ["lsp"]

Zed

Install the Norq extension from the Zed extension registry, or load the zed/ directory as a dev extension:

  1. Open Zed
  2. Go to Extensions > Install Dev Extension
  3. Select the zed/ directory in the norq repo

The extension automatically finds norq on your PATH and launches the LSP.

Sublime Text

Install the LSP package, then add to LSP settings:

{
  "clients": {
    "norq": {
      "command": ["norq", "lsp"],
      "selector": "text.html.markdown",
      "initializationOptions": {},
      "settings": {}
    }
  }
}

Emacs (lsp-mode)

(with-eval-after-load 'lsp-mode
  (add-to-list 'lsp-language-id-configuration '(markdown-mode . "markdown"))
  (lsp-register-client
   (make-lsp-client
    :new-connection (lsp-stdio-connection '("norq" "lsp"))
    :activation-fn (lsp-activate-on "markdown")
    :server-id 'norq-lsp)))

How the LSP works

The LSP server starts via norq lsp on stdin/stdout. It:

  1. Discovers the project root by finding norq.config.yaml
  2. Loads all data.schema.yaml files for completion and type info
  3. Watches for file changes to update diagnostics in real time
  4. Provides scoped completions based on the current context (directive names after :::, variable paths inside {{, pipe names after |)

The LSP only activates for files inside a notifications/ directory that is part of a Norq project.

Completions

The LSP provides context-aware completions:

  • After ::: -- directive names (header, footer, action, highlight, centered, table, etc.)
  • Inside {{ -- variable paths from data.schema.yaml with dot-path traversal
  • After | inside {{ -- pipe names (capitalize, uppercase, truncate, etc.)
  • Inside frontmatter -- known frontmatter keys for the current channel
  • After a space following a directive name -- directive parameters (center, left, right, compact, spacious, bg=, color=)

Completion trigger characters

The LSP registers the following characters as completion triggers: :, {, |, ., (space).

Space triggers directive-parameter completions. After typing ::: hero , the LSP suggests layout params (center, left, right) and style params (bg=, color=, compact, spacious) relevant to that directive.

Directive completions

The following directives appear in completions after ::::

Directive Description
header Branded page header
footer Footer with small text
action Call-to-action button(s)
callout Highlighted callout box
hero Full-width image or banner
fields Key-value data table
media Image, video, or file link
columns Multi-column layout
list Ordered or unordered list
highlight Dark card with brand color background
centered Center-aligned text block
table Iterable data table (rows from array variable)
if / else / for Control flow
raw Raw passthrough block

Directive parameter completions

After a space following a directive name, the LSP offers parameter completions:

  • Alignment: center, left, right
  • Density: compact, spacious
  • Style overrides: bg=, color=

Example: typing ::: highlight triggers suggestions for bg=, color=, compact, spacious.

Diagnostics

Diagnostics appear in real time as you edit:

  • Error: Parse failures, missing required frontmatter
  • Warning: Nullable access without :::if guard or | default pipe, raw expressions
  • Info: Native JSON mode suggestion, unused variables

Code Actions

The LSP offers quick fixes triggered by diagnostics.

Wrap in :::if guard

When the linter warns about nullable access (template/nullable-access), the LSP offers a one-click fix to wrap the expression in an :::if guard. You can also silence the warning by using a | default pipe instead.

Before:

Hello {{user.last_name}}

Diagnostic: Variable 'user.last_name' is nullable -- wrap in :::if guard or use | default "..."

Fix with code action (wraps in :::if guard):

:::if user.last_name
Hello {{user.last_name}}
:::

Fix with | default pipe:

Hello {{user.last_name | default "friend"}}

In VS Code, click the lightbulb or press Ctrl+. / Cmd+. on the warning to apply.

Config file support

The LSP provides intelligence for norq.config.yaml in addition to template files:

  • Diagnostics -- unknown keys, missing required fields, invalid channel names in routing
  • Completions -- top-level keys (notifications, providers, routing, theme, codegen), theme sub-keys, channel names in routing values
  • Hover -- descriptions and default values for each config key

The config schema is defined in crates/core/src/schema/config.rs. Adding a new config key means adding one ConfigField entry.