F# Language Support
F# is a first-class citizen in SharpLsp β not a bolt-on. The same Rust LSP host that drives C# routes F# requests to a long-running F# sidecar built on the F# Compiler Service (FCS), Fantomas, and SharpLsp's own FCS-backed static analyzers.
The reference implementation for F# tooling is FsAutoComplete (FSAC) β the engine behind Ionide. SharpLsp targets full FSAC feature parity, then beyond, while staying editor-agnostic: one server, every editor, standard LSP wherever possible.
Feature Parity at a Glance
| FSAC / Ionide feature | SharpLsp | Notes |
|---|---|---|
| Completion + resolve | β | FCS GetDeclarationListInfo; unopened-namespace suggestions |
| Hover / tooltips | β | Markdown signature + XML/docstring |
| Signature help | β | Overload selection + per-parameter hints |
| Go to Definition | β | Cross-file via FCS symbol uses |
| Type Definition | β | |
| Implementation | β | |
| Find References | β project-wide | Exceeds FSAC's file-local default |
| Document Highlight | β | |
| Rename | β project-wide | Prepare + apply, multi-document edit |
| Document Symbols / Outline | β | FCS navigation items, nested hierarchy |
| Workspace Symbols | β | |
| Formatting (Fantomas) | β | Whole-document + range |
| Code Actions / Quick Fixes | β | See the matrix below |
| Code Lens (reference count) | β | Lens above every top-level definition |
| Inlay Hints | β | Type, parameter-name, and pipeline hints |
| Folding Range | β | tree-sitter, <10 ms |
| Selection Range | β | tree-sitter |
| Semantic Tokens | β | FCS symbol classification, delta-encoded |
| Diagnostics | β | FCS compiler errors + analyzers |
| Unused-code analysis | β project-wide | Dead-code analyzer; monorepo mode |
| Call Hierarchy | β | Incoming + outgoing (beyond FSAC) |
| Type Hierarchy | β | Supertypes + subtypes (beyond FSAC) |
| F# Interactive (FSI) | β | Send selection / file, generate signature |
Legend: β supported today. Roadmap items are listed under What's Next.
IntelliSense
Completion
Completions come from FCS GetDeclarationListInfo at the cursor. The list includes keywords and members from opened namespaces, plus symbols from unopened namespaces β the completion detail shows the open that would be required, matching FSAC's external-autocomplete behaviour.
Hover & Signature Help
Hover renders a Markdown tooltip with the symbol's signature and documentation. Signature help resolves overloads through FCS GetMethods, highlights the active parameter, and updates as you type arguments.
Inlay Hints
SharpLsp renders three hint kinds, configurable in sharplsp.toml:
| Hint | Example |
|---|---|
Type hints on let bindings |
let x : int |
| Parameter-name hints at call sites | add (x =1) (y =2) |
Pipeline type hints after |> |
the inferred type flowing through a pipeline |
Pipeline hints are a SharpLsp extension beyond the FSAC hint set.
Navigation
Go-to-definition, type-definition, and implementation resolve through FCS symbol uses and navigate across files in the loaded project.
Find References, Rename, Document Highlight, Call Hierarchy, and Type Hierarchy are project-wide. Where FSAC resolves references in the current file by default, SharpLsp uses ParseAndCheckProject + GetUsesOfSymbol so every occurrence across the project is found, renamed, or highlighted in a single operation.
Code Actions & Quick Fixes
The F# sidecar maps FCS compiler diagnostics to actionable fixes:
| Trigger | Quick Fix |
|---|---|
FS0039 undefined name |
Suggest open for the resolving namespace |
FS0001 type mismatch |
Insert the obvious conversion |
FS0020 implicitly ignored value |
ignore the value |
FS0025 incomplete match |
Generate the missing match cases |
FS0026 redundant case |
Remove the dead pattern |
FS1182 unused value |
Replace with _ |
| Union value | Generate union-case match arms |
| Record value | Generate missing record-field stubs |
These cover FSAC's "generate union pattern cases", "replace unused with _", "resolve namespace", "fix typo", and stub-generation fixes.
Diagnostics & Analyzers
Diagnostics are dual-sourced: FCS compiler errors plus SharpLsp's own analyzers, merged into a single workspace/diagnostics response.
| Analyzer | Code | FSAC / Ionide | SharpLsp |
|---|---|---|---|
Unused open detection |
SLSPF0102 |
β | β Hint, removable |
| Simplify name / redundant qualifier | SLSPF0103 |
β | β Hint |
| Unused-symbol / dead-code | SLSPF0101 |
β file-local | β project-wide |
- Project-wide dead-code analysis (
SLSPF0101) walks every symbol use in the project (GetAllUsesOfAllSymbols), so a declaration with no non-definition reference anywhere is flagged β not just within the open file. Private/internal dead code is reported as a warning even in ordinary repos, because it can never be reached from outside the assembly. - Monorepo mode (
[analyzers] monorepo = trueinsharplsp.toml) treats the repository as the entire world: an unused public symbol is then genuinely dead and is escalated from a warning to a hard error. No FSAC/Ionide rule offers a cross-project deadness gate β this is unique to SharpLsp. - The unused-
open(SLSPF0102) and simplify-name (SLSPF0103) analyzers run via FCSEditorServices(UnusedOpens,SimplifyNames) as always-onHintdiagnostics, matching FSAC's "remove unused open" and "simplify name".
The host reads the [analyzers] table and pushes the flags to the sidecar via analyzers/configure right after the workspace loads. See Diagnostics for the full pipeline and DIAGNOSTICS-STATIC-ANALYZERS-SPEC for the design.
Formatting (Fantomas)
Document and range formatting are powered by Fantomas.Core, the same formatter FSAC uses, so output matches the .editorconfig/fantomas-tool conventions your team already follows.
F# Interactive (FSI)
The VS Code extension drives a terminal-backed F# Interactive session:
| Command | Action |
|---|---|
SharpLsp: Send Selection to FSI |
Evaluate the selected expression |
SharpLsp: Send File to FSI |
Load the whole file |
SharpLsp: Start FSI |
Open an interactive session |
SharpLsp: Generate Signature |
Produce an .fsi signature from the implementation |
FSI launches through the same .NET 10 SDK SharpLsp acquired on activation (see Getting Started), so it works even when dotnet is not on your $PATH β exactly the case right after the extension installs the SDK for you. Pass custom flags to dotnet fsi (Ionide's fsiExtraParameters equivalent) with sharplsp.fsi.extraArgs in sharplsp.toml or your editor settings; the setting is honoured only in trusted workspaces.
Editor-Agnostic by Design
Ionide exposes F#-specific protocol extensions (fsharp/signature, fsharp/workspacePeek, fsharp/f1Help, β¦) that only Ionide-aware editors understand. SharpLsp prefers standard LSP so every editor β VS Code, Neovim, Helix, Emacs, Zed β gets the same experience without custom client code:
| Ionide custom endpoint | SharpLsp standard-LSP equivalent |
|---|---|
fsharp/signature, fsharp/signatureData |
textDocument/hover, textDocument/signatureHelp |
fsharp/lineLens |
textDocument/codeLens |
fsharp/workspaceLoad, fsharp/project |
Automatic solution/project loading on activation |
fsharp/documentation, fsharp/documentationSymbol |
Hover Markdown documentation |
What's Next
Tracked in docs/plans/FSHARP-FEATURES-PLAN.md:
- Unsaved-buffer fidelity β stream
didChangeto the F# sidecar so results reflect in-flight edits, not just on-disk content. - Completion auto-
openinsertion β apply the namespaceopenas an additional text edit on accept. - Multi-project workspaces β load every
.fsprojin a solution, not just the first. - FSDN / F1 help β signature search and documentation lookup.
- Cross-language hierarchy β F# β C# call/type edges via a unified symbol index.