Skip to content

fmguerreiro/unison-ts-mode

Repository files navigation

unison-ts-mode

Emacs major mode for Unison using tree-sitter.

Status: Ready for MELPA submission. Once accepted, install with M-x package-install RET unison-ts-mode.

Quick Start

  1. Install the package (see Installation)
  2. Open a .u file - you'll be prompted to install the tree-sitter grammar
  3. (Optional) Enable LSP: M-x eglot or M-x lsp

Features

  • Syntax highlighting with tree-sitter (4 customizable levels)
  • Automatic indentation for all Unison constructs
  • imenu support for navigation (functions, types, abilities)
  • LSP integration (eglot and lsp-mode)
  • UCM REPL integration with keybindings
  • Auto-install of tree-sitter grammar

Screenshots

Syntax highlighting example 1 Syntax highlighting example 2

Requirements

Installation

Package

use-package + straight:

(use-package unison-ts-mode
  :straight (:host github :repo "fmguerreiro/unison-ts-mode")
  :mode ("\\.u\\'" "\\.unison\\'"))

straight.el:

(straight-use-package
  '(unison-ts-mode :type git :host github :repo "fmguerreiro/unison-ts-mode"))

Doom Emacs:

;; packages.el
(package! unison-ts-mode :recipe (:host github :repo "fmguerreiro/unison-ts-mode"))

Grammar

The tree-sitter grammar installs automatically when you first open a .u file. Customize behavior with unison-ts-grammar-install:

  • 'prompt (default): Ask before installing
  • 'auto: Install automatically
  • nil: Never auto-install

Manual install: M-x unison-ts-install-grammar

Custom grammar source:

(setq unison-ts-grammar-repository "https://github.com/yourname/tree-sitter-unison")
(setq unison-ts-grammar-revision "your-branch")  ; Optional

Configuration

Font-lock Levels

Customize highlighting depth via treesit-font-lock-level:

  1. comment, doc, string, declaration, preprocessor, error
  2. keyword, type, constant
  3. function-call, variable
  4. bracket, operator, delimiter

Apply changes: M-x treesit-font-lock-recompute-features

imenu Navigation

Navigate to functions, types, and abilities:

  • M-x imenu - Jump to definition
  • M-x which-function-mode - Show current function in mode line
  • Works with helm-imenu, counsel-imenu, consult-imenu
;; Auto-enable which-function-mode
(add-hook 'unison-ts-mode-hook 'which-function-mode)

LSP Support

Requires UCM. UCM auto-starts in headless mode when you open a .u file.

Eglot (built-in Emacs 29+):

(with-eval-after-load 'unison-ts-mode
  (with-eval-after-load 'eglot
    (unison-ts-mode-setup-eglot)))
(add-hook 'unison-ts-mode-hook 'eglot-ensure)

lsp-mode:

(with-eval-after-load 'unison-ts-mode
  (with-eval-after-load 'lsp-mode
    (unison-ts-mode-setup-lsp)))
(add-hook 'unison-ts-mode-hook 'lsp-deferred)

Custom port:

export UNISON_LSP_PORT=5758

Windows: LSP is disabled by default. Enable it:

[System.Environment]::SetEnvironmentVariable('UNISON_LSP_ENABLED','true')

Manual UCM:

ucm headless

UCM Integration

Interact with UCM directly from Emacs. Open the REPL with C-c C-u r or use these commands:

Keybinding Command Description
C-c C-u r unison-ts-repl Open UCM REPL
C-c C-u a unison-ts-add Add definitions from current file
C-c C-u u unison-ts-update Update existing definitions
C-c C-u t unison-ts-test Run tests (prompts for pattern)
C-c C-u x unison-ts-run Run a term
C-c C-u w unison-ts-watch Watch current file for changes
C-c C-u l unison-ts-load Load current file

Troubleshooting

Grammar installation fails:

Install build tools:

# macOS
xcode-select --install

# Debian/Ubuntu
sudo apt-get install build-essential git

# Fedora/RHEL
sudo dnf install gcc git

ABI version mismatch:

tree-sitter generate --abi=13

Grammar not found:

Check ~/.emacs.d/tree-sitter/ or treesit-extra-load-path contains the compiled grammar.

LSP connection refused:

  • Verify ucm is in PATH: which ucm
  • Check port 5757: lsof -i :5757 (macOS/Linux) or netstat -an | findstr 5757 (Windows)
  • Start manually: ucm headless
  • Check logs: *EGLOT events* (eglot) or *lsp-log* (lsp-mode)

LSP features not working:

Ensure you're in a valid Unison codebase directory.

Advanced

Manual Grammar Build

If auto-install fails:

git clone https://github.com/fmguerreiro/tree-sitter-unison.git
cd tree-sitter-unison

# Determine shared library extension
if [ "$(uname)" = "Darwin" ]; then soext="dylib"
elif uname | grep -q "MINGW"; then soext="dll"
else soext="so"; fi

cd src
cc -fPIC -c -I. parser.c
cc -fPIC -c -I. scanner.c
cc -fPIC -shared *.o -o "libtree-sitter-unison.${soext}"

# Copy to Emacs tree-sitter directory
mkdir -p ~/.emacs.d/tree-sitter
cp "libtree-sitter-unison.${soext}" ~/.emacs.d/tree-sitter/

Syntax Tree Inspection

Use M-x treesit-explore-mode to inspect the syntax tree while developing or debugging.

Development

Set up the pre-commit hook to run MELPA-style checks:

ln -s ../../scripts/pre-commit .git/hooks/pre-commit

Contributing

Contributions welcome via GitHub pull requests.

License

GPL-3.0 License. See LICENSE for details.

Credits

Tree-sitter grammar by @kylegoetz.