uf0.2.0

Type to search every page and heading. Page titles only, while the full index loads.

All pages

Editors

Emacs

Eglot and lsp-mode both give JavaScript buffers typescript-language-server by default, and it reads a Flow file as TypeScript. uf.el makes uf the server for a uf project's buffers and leaves every other project with the server it would have had.

What you will be able to do: edit a uf project in Emacs with uf's diagnostics, hover, definitions, completion, code actions and formatting.

What you need first: uf installed, a project with a uf.config.js, and Emacs 29 or later (Eglot is built in), or lsp-mode.

1. Load uf.el

uf editor install emacs writes uf.el to ~/.local/share/uf/editors/emacs and prints the load-path line. uf.el is also in a uf checkout:

(add-to-list 'load-path "/path/to/uf/editors/emacs")
(require 'uf)
(setq uf-format-on-save t)                     ; optional
(add-hook 'js-mode-hook #'uf-eglot-ensure)     ; Eglot: start it in uf projects
(add-hook 'js-ts-mode-hook #'uf-eglot-ensure)

uf.el makes a directory with uf.config.js a project.el project. That project root is where Eglot starts the server, and the command names it again with --cwd.

2. How the TypeScript server stays off

  • Eglot. uf.el puts its entry first in eglot-server-programs. Its contact function answers uf lsp in a uf project. Anywhere else it answers with whatever the rest of eglot-server-programs gives the buffer's mode: typescript-language-server on Emacs 30, and on Emacs 31 the rass ts alternative it tries first. A TypeScript project therefore keeps its server.
  • lsp-mode. uf.el registers a client, uf, at priority 1, whose activation function accepts a buffer only in a uf project. lsp-mode starts the highest-priority client that accepts a buffer, and ts-ls is -2, so a uf project gets uf and every other project is unchanged.

To say it per project for lsp-mode as well, commit a .dir-locals.el (uf editor setup emacs writes it when the project has none). lsp-disabled-clients is marked safe as a directory-local variable, so Emacs applies it without asking:

((js-mode . ((lsp-disabled-clients . (ts-ls jsts-ls tsgo deno-ls))))
 (js-ts-mode . ((lsp-disabled-clients . (ts-ls jsts-ls tsgo deno-ls)))))

Setting eglot-server-programs from .dir-locals.el also works, but it is not marked safe, so Emacs asks every time. uf.el's contact function makes that unnecessary.

Check that it worked

M-x eglot-events-buffer (or M-x lsp-describe-session) shows uf for a Flow buffer. M-x flymake-show-buffer-diagnostics lists uf and flow entries. M-x eglot-format-buffer, then uf fmt --check in a terminal: they agree when the server read the project's uf.config.js. After editing uf.config.js, M-x eglot-reconnect (or M-x lsp-workspace-restart).

Checked against

Eglot's defaults and lsp-mode's client ids, priorities and lsp-disabled-clients were read from eglot.el (emacs-30 and master) and lsp-mode's source in September 2026. The Editors workflow byte-compiles uf.el with warnings as errors and runs its ERT tests in batch Emacs. Those tests cover the project root, the command, the fallback to another server outside a uf project, and lsp-mode's activation. Eglot and lsp-mode themselves are not started by the tests.

Edit this pagedocs/app/guide/editors/emacs/$page.mdx