Skip to content

Getting Started

Notation

Throughout these docs, keybindings use standard Emacs notation:

Notation Meaning
C- Hold Control
M- Hold Alt (or Option on macOS)
C-c C-d b Hold Control, press c, then hold Control and press d, then press b
C-u Universal prefix argument (modifies the next command)
M-x Open the command prompt (run any command by name)
RET Press Enter/Return

Prerequisites

To get the most out of fsharp-ts-mode, you'll want:

  • .NET SDK -- for dotnet fsi, dotnet build, etc.
  • FsAutoComplete -- the F# language server (auto-installed by fsharp-ts-eglot)

What Works Out of the Box

Once installed, opening any .fs, .fsx, or .fsi file activates fsharp-ts-mode (or fsharp-ts-signature-mode for .fsi). The following features work immediately:

  • Syntax highlighting via tree-sitter
  • Indentation via tree-sitter
  • Imenu (symbol index) with fully-qualified names
  • Navigation (C-M-a / C-M-e to move between definitions, C-M-f / C-M-b for balanced expressions)
  • Switch between .fs and .fsi files (C-c C-a)
  • Compile (C-c C-c) with error parsing for dotnet build
  • .NET API docs lookup at point (C-c C-d)
  • Shift region left/right (C-c > / C-c <)
  • Format the buffer with Fantomas (C-c C-f)
  • Clickable links for URLs and issue references in comments
  • project.el integration -- F# solutions and projects are recognized as project roots
  • Build directory awareness -- prompts to switch when visiting files under bin/ or obj/

Opt-in Features

Some features require loading additional libraries or enabling minor modes. Here's what's available and how to set it up:

F# Interactive (REPL)

Adds keybindings for sending code to dotnet fsi. See REPL Integration for details.

(add-hook 'fsharp-ts-mode-hook #'fsharp-ts-repl-minor-mode)

dotnet CLI commands

Adds keybindings for build, test, run, and other dotnet operations. See dotnet CLI for the full command list.

(add-hook 'fsharp-ts-mode-hook #'fsharp-ts-dotnet-mode)

Eglot/LSP with FsAutoComplete

Provides completions, diagnostics, type information, and many advanced LSP features. FsAutoComplete is downloaded automatically on first use.

(require 'fsharp-ts-eglot)
(add-hook 'fsharp-ts-mode-hook #'eglot-ensure)

Type signature overlays (LineLens)

Shows inferred type signatures as inline overlays after definitions. Requires an active Eglot connection. See LineLens.

(require 'fsharp-ts-lens)
(add-hook 'fsharp-ts-mode-hook #'fsharp-ts-lens-mode)

Documentation info panel

Auto-updating side window with rich type documentation. Requires an active Eglot connection. See Documentation Info Panel.

(require 'fsharp-ts-info)
(add-hook 'fsharp-ts-mode-hook #'fsharp-ts-info-mode)

Other opt-in features

;; Prettify symbols (-> to →, fun to λ, etc.)
(add-hook 'fsharp-ts-mode-hook #'prettify-symbols-mode)

;; Code folding (Emacs 30+)
(add-hook 'fsharp-ts-mode-hook #'outline-minor-mode)

;; Auto-detect indentation from file contents
(setq fsharp-ts-guess-indent-offset t)

;; Format with Fantomas on every save
(setq fsharp-ts-format-on-save t)

Formatting

C-c C-f (fsharp-ts-format-buffer) formats the whole buffer with Fantomas. Install it with dotnet tool install -g fantomas, or point fsharp-ts-fantomas-program at a local install. Fantomas reads the nearest .editorconfig, so your project's formatting settings are respected. Set fsharp-ts-format-on-save to t to format automatically before each save.

Here's a complete setup that enables all major features:

(use-package fsharp-ts-mode
  :ensure t
  :hook ((fsharp-ts-mode . fsharp-ts-repl-minor-mode)
         (fsharp-ts-mode . fsharp-ts-dotnet-mode)
         (fsharp-ts-mode . eglot-ensure)
         (fsharp-ts-mode . prettify-symbols-mode))
  :config
  (require 'fsharp-ts-eglot)
  (require 'fsharp-ts-lens)
  (require 'fsharp-ts-info)
  (add-hook 'fsharp-ts-mode-hook #'fsharp-ts-lens-mode)
  (add-hook 'fsharp-ts-mode-hook #'fsharp-ts-info-mode)
  (setq fsharp-ts-guess-indent-offset t))

Tip

You don't need everything at once. Start with the base mode, add Eglot when you want LSP features, and add the REPL and dotnet modes as you need them.

Typical Workflow

A typical F# editing session looks like this:

  1. Open a .fs file -- fsharp-ts-mode activates automatically
  2. Eglot connects to FsAutoComplete (if configured), giving you completions, diagnostics, and type info
  3. Edit code with tree-sitter-powered highlighting and indentation
  4. Use C-c C-z to send code to the REPL for interactive exploration
  5. Use C-c C-d b to build, C-c C-d t to run tests (requires fsharp-ts-dotnet-mode)

Keybinding overlap

When fsharp-ts-dotnet-mode is active, the C-c C-d prefix is used for dotnet commands, which shadows the base mode's C-c C-d binding for .NET API docs lookup. Use M-x fsharp-ts-mode-doc-at-point instead, or bind it to a different key.

Similarly, when fsharp-ts-repl-minor-mode is active, C-c C-c sends the definition at point to the REPL instead of running compile. Use M-x compile directly if needed.

Useful Commands

Base mode (always active)

Key Command Description
C-c C-a ff-find-other-file Switch between .fs and .fsi
C-c C-c compile Run compilation
C-c C-d fsharp-ts-mode-doc-at-point Look up symbol in .NET docs
C-c C-f fsharp-ts-format-buffer Format the buffer with Fantomas
C-c > fsharp-ts-mode-shift-region-right Indent region by one level
C-c < fsharp-ts-mode-shift-region-left Dedent region by one level

See also: Code Navigation for movement commands, REPL Integration for REPL commands, dotnet CLI for build commands.

Documentation lookup

Command Description
fsharp-ts-mode-doc-at-point Look up symbol at point in .NET API docs
fsharp-ts-mode-browse-fsharp-docs Open the F# documentation home page
fsharp-ts-mode-search-by-signature Search FSDN by type signature (e.g., string -> int)

Indentation helpers

Both shift commands accept a prefix argument to shift by multiple levels (e.g., C-u 2 C-c > shifts right by 2 levels).

M-x fsharp-ts-mode-guess-indent-offset scans the buffer and sets fsharp-ts-indent-offset to match the file's convention. Set fsharp-ts-guess-indent-offset to t to run this automatically on file open.

Debugging

fsharp-ts-mode registers F# buffers with dape's built-in netcoredbg configuration, so once dape and the netcoredbg adapter are installed, M-x dape offers the .NET debugger in F# buffers with no extra setup. The configuration builds and debugs the project's bin/Debug DLL, which works for F# projects just as it does for C# ones.

Registration happens when a buffer enters the mode, so load dape before opening your F# files (or revert the buffer afterwards).

Known Limitations

F# is an indentation-sensitive language -- the tree-sitter grammar needs correct whitespace to parse the code. See the FAQ for details on how this affects editing.

Companion Packages

These packages work well alongside fsharp-ts-mode:

  • eglot -- LSP client (built into Emacs 29+)
  • corfu or company -- completion UI
  • flymake -- inline diagnostics (works with eglot)
  • which-key -- keybinding discovery
  • expreg -- structural selection using tree-sitter
  • dape -- debugging via the Debug Adapter Protocol (see Debugging)