@node Language servers
@chapter Language servers (LSP)

@cindex LSP
@cindex Language Server Protocol
@cindex Metals
@cindex Scala
@cindex completion
@cindex go to definition
@cindex hover

Where the debugger (@pxref{Debugging}) lets you @emph{run} a program step by
step, a @dfn{language server} lets the editor @emph{understand} your code:
errors as the compiler sees them, completion with type signatures, the type of
a symbol under the cursor, and jump-to-definition.  xwpe speaks the
@dfn{Language Server Protocol} (LSP) -- the same protocol VS Code, Neovim and
Emacs use -- so it drives the very same servers those editors do.  The server
is an external tool; nothing of it ships inside xwpe, and the editor stays a
few megabytes.

The first wired language is @strong{Scala}, through @uref{https://scalameta.org/metals/,
Metals} -- so a Scala developer can use @command{wpe} in a terminal as a tiny,
zero-config IDE front-end, no XQuartz and no Electron.  @strong{C and C++} (through
@uref{https://clangd.llvm.org/, clangd}), @strong{Python} (through pyright or
pylsp), @strong{Go} (through gopls) and @strong{Rust} (through rust-analyzer) are
wired too -- the same keys, the same engine (@pxref{Language servers, , C and
C++}, @pxref{Language servers, , Python}, @pxref{Language servers, , Go},
@pxref{Language servers, , Rust}).

@cindex example, language server
A small, fully-commented demo project lives in
@file{docs/examples/scala-lsp/} -- open it with @kbd{wpe main.scala}; each line
carries an inline comment telling you which action to try right there.  Its
@file{actions.scala} is a dedicated playground for the code actions
(@kbd{Alt-Q A}): a direct-edit refactor, a server-command one (organize
imports), and an unresolved one (convert to named arguments) -- see ``Code
actions'' below.

@section Keys

@cindex Metals menu
@cindex status bar
While a language-server file is the active window, the bottom status bar shows an
@samp{Alt-Q ? @var{server}} entry, where @var{server} names the language server
backing the active file -- @samp{Alt-Q ? Metals} for a Scala file,
@samp{Alt-Q ? clangd} for a C/C++ one.  The highlighted @kbd{Alt-Q ?} tells you
how to open the action @strong{menu}.  This follows the Borland convention of the
@samp{Block} menu and its @kbd{Ctrl-K} commands: the menu is the discoverable
path, the prefix is the fast one.

@itemize @bullet
@item
@kbd{Alt-Q}@tie{}+@tie{}@var{letter} runs an action @strong{directly} -- no menu
is drawn, so there is no flicker (e.g.@: @kbd{Alt-Q@tie{}D} jumps to the
definition at once).
@item
@kbd{Alt-Q@tie{}?} (or @key{F1}, or a click on the bar entry) opens the drop-up
@strong{menu} -- the same widget as the @samp{File}/@samp{Edit} menus, unfolding
upward from the bar, listing every action with its letter.  Arrow keys move, the
letter or a click picks, @key{ESC} closes.  @kbd{Alt-Q} then @key{ESC} cancels.
@end itemize

@noindent
The entry appears only for files a server backs, and disappears when you switch
to an ordinary file.  (Note: @kbd{Alt-Q}, not @kbd{Alt-M}, drives the server --
@kbd{Alt-M} is @samp{Make}.)

All language-server actions live under the @kbd{Alt-Q} prefix (@dfn{Query the
language server}), each by its letter:

@table @kbd
@item Alt-Q E
Start the server for the current file, import the build, and show compiler
diagnostics (errors and warnings) in the Messages window, in the same
@code{file:line:col:} form as F9 build errors.  The problem ranges are also
marked @emph{inline} in the source: error cells turn red, warning cells amber,
updated live as you type (no save needed).  The marks clear when the problem
is fixed or the server is closed.
@item Alt-Q .
Next problem: jump the cursor to the next diagnostic in the file (wrapping
around), like an IDE's @key{F8}; its message shows in a tooltip at the cursor.
While the tooltip is up a bare @kbd{.} steps to the next problem and @kbd{,} to
the previous, so you walk the whole list without re-typing the prefix.
@item Alt-Q ,
Previous problem: the same, in reverse.
@item Alt-Q D
Go to the definition of the symbol under the cursor -- including @emph{into
library and standard-library sources}.  The server extracts those under
@file{.metals/readonly/} and they open @strong{read-only} -- marked by a padlock
at the left of the title bar and a dimmed name (you can read and copy, not edit).
Language-server actions are not offered inside such a source -- run them from your
project window.
@item Alt-Q I
Go to the @emph{implementation} of the symbol: the concrete override of an
abstract @code{def} or trait member (jumps past the declaration to the body).
@item Alt-Q T
Go to the @emph{type definition} of the symbol: the class or trait of a
value's type, rather than the value's own declaration.
@item Alt-Q H
Hover: show the type and documentation of the symbol under the cursor
(e.g.@: @code{var f: Long}) in a tooltip @emph{at the cursor}.  Any key closes
it (its bottom border says so).
@item Alt-Q C
Completion: offer candidates for the word under the cursor in a dropdown
@emph{at the cursor} (capped to fit the editor window), like a modern editor's
autocomplete.  Move with the arrow keys, press @key{RET} to insert the selected
candidate (it replaces the partial word), or @key{ESC} to cancel.
@item Alt-Q R
References: list every use of the symbol under the cursor (including its
declaration) in the Messages window.
@item Alt-Q B
Incoming calls (@dfn{called By}): list every caller of the method under the
cursor in the Messages window, each as @code{name  file:line:col}.  Put the
cursor on a @code{def} and ask ``who reaches this?'' to trace a call graph
upward without leaving xwpe.
@item Alt-Q G
Outgoing calls (@dfn{Goes to}): list every method the one under the cursor
calls, in the same @code{name  file:line:col} form -- the downward direction of
the call hierarchy.
@item Alt-Q K
Supertypes: list what the type (class/trait) under the cursor extends or
implements -- the inheritance graph @emph{upward}.  The @kbd{K} is the
vi @dfn{up} key, a reminder that supertypes are the parents.
@item Alt-Q J
Subtypes: list what extends or implements the type under the cursor -- the
inheritance graph @emph{downward} (@kbd{J} = vi @dfn{down} = the children).
@item Alt-Q V
Expand selection (@kbd{V} = @dfn{Visual}): grow the block mark to the smallest
syntactic range that encloses it -- with nothing selected it picks the token
under the cursor, then each press widens by structure (token -> expression ->
argument list -> call -> statement -> block), not by lines.  The result is an
ordinary block, so @kbd{Ctrl-K C} / @kbd{Ctrl-K Y} copy or delete it as usual.
@item Alt-Q U
Highlight @dfn{uses}: mark every occurrence of the symbol under the cursor
@emph{in the current file} (in the found-word colour) -- a search highlight that
follows meaning, not text.  Press it off any identifier to clear the highlight.
@item Alt-Q O
Outline: list the file's symbols (objects, defs, vals, ...) in a popup; pick
one with the arrows and @key{RET} to jump to it.
@item Alt-Q Y
@dfn{Inlay hints} (toggle): turn on an opt-in overlay that shows each line's
@emph{inferred types} -- the @samp{: @var{T}} the compiler deduced for a
@code{val}/@code{def} you did not annotate -- dimly at the @strong{end of the
line} (e.g.@: @code{val xs = List(1,2)} grows a faint @code{: List[Int]}).
Press @kbd{Alt-Q Y} again to turn it off.  Off by default.  Unlike a modern
GUI IDE the hint is shown at line-end, not woven mid-line: xwpe's cell grid
keeps screen column == buffer column (so the cursor, editing and scrolling stay
exact), and the hint conveys the same type without disturbing that.  The set
refreshes itself: when the server finishes indexing and the inferred types
change -- e.g.@: a cross-file type that was unknown the instant you toggled on
-- it asks xwpe to re-query, and the overlay updates on your next keystroke, so
a cold toggle fills in as the project settles (no need to re-toggle).
@item Alt-Q M
@dfn{Semantic colours} (toggle): server-driven syntax highlighting.  The plain
syntax colouring is a regular-expression lexer -- it cannot tell a type from a
variable, or a method call from a keyword, because it never sees the program's
meaning.  Metals does: it returns @dfn{semantic tokens} that classify every
identifier from the @emph{compiler's} point of view.  Toggling this on repaints
the file from those tokens, with a dedicated palette so the categories that the
regex lexer conflates become distinct: @strong{types/classes} in teal,
@strong{functions/methods} in orange, @strong{parameters} in violet,
@strong{variables} in green, @strong{keywords} in white, strings in their own
shade.  The win is the one the big IDEs sell: at a glance a @code{type} reads
differently from a @code{keyword}, a @code{function} call, and a
@code{parameter}, even where the regex colouring would paint them all the same.
The set re-queries itself when the server reclassifies (e.g.@: a cross-file type
resolves after indexing), updating on your next keystroke.  Press @kbd{Alt-Q M}
again to turn it off and return to the regex colouring.  Off by default.

The function/method @strong{orange} (@code{#FF8C00}) and the type/class
@strong{teal} (@code{#4EC9B0}) are real 24-bit colours the 16-colour palette
cannot express (its nearest teal washes out on the Borland-blue background).  In
X11 they always show.  In the terminal they show exactly when the terminal can
do 24-bit colour: a direct-colour terminfo (@env{TERM=xterm-direct}, kitty, or
tmux configured with the @code{RGB} capability) renders them precisely, a
256-colour terminal that allows palette redefinition gets them via @code{OSC 4},
and anything else falls back to the 16-colour approximation (red for methods,
yellow for types).  There is nothing to configure in xwpe -- it detects the
terminal at start-up.
@item Alt-Q L
Code @dfn{lenses}: the @samp{run}/@samp{debug}/@samp{test} and reference-count
annotations the server attaches to definitions (main methods, test suites), in
a popup as @samp{@var{label}  (line @var{N})}; selecting one jumps to that
definition.  To actually run or test a Scala main, start the debugger
(@kbd{Ctrl-G T}), which drives the same build-server path the @samp{run} lens
would use (@pxref{Debugging}).
@item Alt-Q W
Workspace symbol search: you are prompted for a query, then a popup lists
matching symbols from across the @emph{whole project} (each shown with its
file); @key{RET} jumps to the chosen one, opening its file if needed.  This is
the editor-wide ``Go to Symbol''.
@item Alt-Q A
Code actions: list the quick-fixes and refactors the server offers at the
cursor (organize imports, create a companion object, ...) in a picker
@emph{at the cursor} -- the fix targets the symbol there, so the list drops
there, like completion.  Applying an action rewrites the buffer.  An action
carrying a direct edit is
applied straight away; one that runs a server command is executed over
@code{workspace/executeCommand}, and the server's resulting edit
(@code{workspace/applyEdit}) is applied for you.  When an action also changes
@emph{other} files a note reports how many -- those are left untouched.  The
rewrite is a single @kbd{Ctrl-U} (Undo) away, and @kbd{Ctrl-R} redoes it, like
any other edit; the same applies to Rename (@kbd{Alt-Q N}) and Format
(@kbd{Alt-Q F}).
@item Alt-Q S
Signature: show the signature of the call the cursor is inside (e.g.
@code{println(x: Any): Unit}) in a tooltip @emph{at the cursor}; any key closes
it.
@item Alt-Q N
Rename: rename the symbol under the cursor everywhere (you are prompted for the
new name).  Edits in the current file are applied to the buffer; if the rename
also touches other files, xwpe tells you (open them and rename there).  (Rename
is interactive and works normally; it is just absent from the recorded demo
GIFs, because its text-input dialog does not capture reliably under the headless
screen recorder -- not an editor limitation.)
@item Alt-Q F
Format with the server's formatter (scalafmt for Scala; a
@file{.scalafmt.conf} in the project enables it).  Context-sensitive, like the
big IDEs: if a block is marked it reformats @emph{only that range}
(@code{textDocument/rangeFormatting}) and leaves the rest untouched; with no
selection it reformats the whole file.  Pairs with @kbd{Alt-Q V} -- widen the
selection to a method, then @kbd{Alt-Q F} to tidy just that method.
@end table

After a rename or format, the buffer is rebuilt with the result and marked
modified; save (@key{F2}) to keep the changes.  The new text is sent to the
server straight away, so the inline diagnostics re-publish for it -- and that
holds for @kbd{Ctrl-U} (Undo) and @kbd{Ctrl-R} (Redo) too: undo a quick-fix and
the error it removed reappears, marks and all.

@cindex cursor tooltips
@cindex chaining actions
The @emph{peeks} -- hover (@kbd{Alt-Q H}), signature (@kbd{Alt-Q S}) and the
problem messages (@kbd{Alt-Q .} / @kbd{Alt-Q ,}) -- and the contextual
@emph{lists} -- completion (@kbd{Alt-Q C}) and code actions (@kbd{Alt-Q A}) --
all appear @emph{at the cursor}, where you are working.  Only the project-wide
browse lists, Outline (@kbd{Alt-Q O}) and Workspace symbols (@kbd{Alt-Q W}),
stay an ordinary dialog, since the cursor does not pick those.  A tooltip
closes on @emph{any} key, and the peeks @emph{chain}: with one up, press
@kbd{Alt-Q} and another action letter to run it in one motion -- e.g.
@kbd{Alt-Q .} to land on a problem, then @kbd{Alt-Q A} to pull up its quick-fix
without dismissing first.

@cindex live diagnostics
Once the server is running, every edit is sent to it as you type (no save
needed), and a @samp{LSP: N error(s), M warning(s)} status is updated live in
the Messages window whenever the totals change.  @kbd{Alt-Q E} still lists the
full set on demand.

@cindex asynchronous start
@cindex cold start
@cindex eager start
@vindex XWPE_LSP_NO_EAGER
@emph{Opening} a file the server understands already starts it: when you open a
@file{.scala}/@file{.sc} (and Metals is installed) xwpe boots it in the
@emph{background} right away, so it is warming up while you read the code and is
usually ready by the time you ask for anything.  @samp{Starting language server
(the editor stays responsive)@dots{}} appears in Messages and the cold start --
JVM boot, build import and the first compile, a minute or more -- runs without
freezing the editor: you can keep typing, scrolling and using other menus.
Once Messages shows @samp{Language server ready} (or the live status below
settles) the actions work; they are instant thereafter.  If the server is not
up yet when you invoke an action, that action just kicks off the start (or
reports it is still starting) -- try again once it is ready.  To keep xwpe from
auto-starting a server on open (a slow machine, say), set the environment
variable @env{XWPE_LSP_NO_EAGER=1}; the first @kbd{Alt-Q} action then starts it
on demand instead.

@cindex server status
@cindex indexing
The server's own progress is surfaced too: while Metals imports the build and
indexes the project -- the slow part of the cold start -- it reports what it is
doing (@samp{LSP: Indexing}, @samp{LSP: Compiling}, @dots{}) in the Messages
window, so the wait is not silent.  Until that finishes the presentation
compiler has no type information yet, so a hover on a symbol can legitimately
come back empty; in that case @kbd{Alt-Q H} says @samp{Language server still
working -- try again in a moment} rather than the bare @samp{No hover
information}, so you know to retry once indexing settles.

@cindex worksheets
@cindex .worksheet.sc
Open a Scala @dfn{worksheet} -- a file whose name ends in @strong{@file{.worksheet.sc}}
(e.g.@: @file{demo.worksheet.sc}) -- and the server @emph{evaluates} it: the
result of each top-level expression appears dimly at the @strong{end of its line}
(e.g.@: @code{val a = 5 + 7} grows a faint @code{: Int = 12}), a REPL that updates
in place.  Unlike IntelliJ there is no @samp{Eval} button: Metals re-evaluates
automatically (on the first compile, and a moment after each edit) and delivers
the results as @dfn{inlay hints} -- the same mechanism as the @kbd{Alt-Q Y} type
hints -- so xwpe turns the overlay on @emph{automatically} (no @kbd{Alt-Q Y}
needed).  This is the lightweight way to try an API or check a calculation
without writing a @code{main}; it needs the same running Metals as everything
else (the first @kbd{Alt-Q} action, or just wait for the background start to
settle).

@cindex .sc scripts
@strong{The @file{.worksheet.sc} ending matters}: a plain @file{.sc} file (e.g.@:
@file{script.sc}) is treated by Metals as a @emph{scala-cli script}, not a
worksheet -- you still get the inferred-@emph{type} hints (@code{: Int}) and all
the other actions, but @emph{not} the @code{= 12} evaluation results.  Rename it
to @file{name.worksheet.sc} to have it evaluated.

@cindex cross-file navigation
@subsection Working across files
The server indexes the whole project (not just the open file), so the
navigation actions reach across files: @kbd{Alt-Q D} (definition), @kbd{Alt-Q
I} (implementation), @kbd{Alt-Q T} (type definition) and @kbd{Alt-Q W}
(workspace symbol search) all jump to the right file, @emph{opening it} in a new
editor window if it was not already open.  @kbd{Alt-Q R} (references) lists uses
from every file in the Messages window.

Two limitations to know about in this first release:

@itemize @bullet
@item @strong{One document at a time.}  The server is attached to a single open
file.  When you use an @kbd{Alt-Q} action in a @emph{different} file, xwpe
transparently re-points the server at it (a brief pause).  Diagnostics and the
inline marks track whichever file the server is currently attached to.
@item @strong{Rename is applied in the current file only.}  @kbd{Alt-Q N}
renames every occurrence in the file you are in; if the rename also touches
@emph{other} files, xwpe reports how many but does not edit them -- open each one
and rename there.  @kbd{Alt-Q U} (highlight uses) is single-file by design.
@end itemize

The server starts lazily on the first @kbd{Alt-Q} action and is reused for the
rest of the session.  The first start is slow: it boots a JVM language server
and imports the build (faster afterwards, once caches are warm).  Diagnostics
reflect the file as saved on disk, so save (@key{F2}) before @kbd{Alt-Q E} for
the freshest view.

@section Scala (Metals)

@cindex scala-cli
@cindex coursier
@cindex JAVA_HOME
@cindex JDK

Three things must be reachable (on your @env{PATH}, or pointed to by an
environment variable) before the @kbd{Alt-Q} keys do anything useful:

@enumerate
@item @strong{@command{metals}} -- the Scala language server.  Install it with
@uref{https://get-coursier.io/,coursier}:
@example
cs install metals
@end example

@item @strong{@command{scala-cli}} -- the build tool Metals drives
(@code{cs install scala-cli}).  xwpe runs @code{scala-cli setup-ide} for you, so
Metals connects to the build server automatically; it is the same tooling the
Scala debugger uses (@pxref{Debugging}).

@item @strong{A JDK the Scala@tie{}3 compiler supports -- and Metals must run on
it.}  This is the subtle one; see the next subsection.
@end enumerate

@subsection The JDK: two JVMs, and the one that matters

@cindex presentation compiler
@cindex asTerm called on not-a-Term
There are really @emph{two} JVMs in play, and they are easy to confuse:

@itemize @bullet
@item The @dfn{build} JVM compiles the project (scala-cli / Bloop).  You pin it
@emph{per project} with a one-line @file{project.scala}:
@example
//> using jvm temurin:21
@end example
@noindent
scala-cli downloads and caches that JDK.  This is enough for @kbd{F9} builds and
the debugger.

@item The JVM @strong{Metals itself} runs on.  Metals' @dfn{presentation
compiler} -- which powers hover, completion and go-to-definition -- runs in
@emph{Metals' own} process, @strong{not} the build JVM.  So @file{project.scala}
alone does @emph{not} cover it: if Metals is launched on a too-new JDK, the
Scala@tie{}3 presentation compiler crashes at start-up with @code{asTerm called
on not-a-Term}, and every navigation action silently comes back empty -- even
though diagnostics (which come from the build server) look fine.
@end itemize

@noindent
@strong{So the real requirement is that Metals run on a supported LTS JDK
(17 or 21).}  @strong{xwpe handles this for you}: when it starts Metals it checks
the default JDK, and if that is too new (>=@tie{}24) it looks for an
installed LTS JDK and points @env{JAVA_HOME} at it for the Metals process,
reporting what it picked in the Messages window
(@samp{Metals: pinned JAVA_HOME=@dots{}}).  You only need to set @env{JAVA_HOME}
yourself if no LTS JDK is installed (xwpe then tells you), or to override the
choice:

@example
export JAVA_HOME=/path/to/jdk-21
export PATH="$JAVA_HOME/bin:$PATH"
wpe Main.scala
@end example
@noindent
An explicit @env{JAVA_HOME} that already points at a supported JDK is respected
and never overridden.

@noindent
A JDK that is too @emph{old} fails the other way: the ancient distribution
@code{scala} (2.11) is below Metals' floor (it wants Scala 2.12.12+, 2.13 or
3.x).  An LTS@tie{}17 or@tie{}21 is the safe middle.

@subsubsection Debian / Ubuntu
@example
# metals and scala-cli are not in the archive -- install via coursier:
cs install metals scala-cli
# an LTS JDK (the default-jdk may be too new):
sudo apt install openjdk-21-jdk
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export PATH="$JAVA_HOME/bin:$PATH"
@end example

@subsubsection macOS
@example
brew install coursier/formulas/coursier && cs setup
cs install metals scala-cli
brew install openjdk@@21          # or a Temurin 21 from Adoptium
export JAVA_HOME="$(brew --prefix openjdk@@21)/libexec/openjdk.jdk/Contents/Home"
export PATH="$JAVA_HOME/bin:$PATH"
@end example

@subsection Using it

Open a @file{.scala} file and use the @kbd{Alt-Q} keys above.  Symbols come
back Scala-aware: completion shows full signatures
(@code{println(x: Any): Unit}), hover shows the inferred type, and
go-to-definition follows into library sources.

@cindex Metals Doctor
When Metals has something to report about the build (its @dfn{Doctor}), xwpe
shows it as text in a dedicated @samp{Metals Doctor} window (a one-line note in
@samp{Messages} says it updated; the editor file keeps focus).  It is @emph{not}
opened in an external web browser, so a headless or remote session stays
self-contained.  The Doctor is also the place to look when hover/completion act
up: if it reports a presentation-compiler crash
(@code{java.lang.AssertionError: asTerm called on not-a-Term}), pin a stable LTS
Scala -- @code{//> using scala 3.3.7} -- rather than a bleeding-edge default.

@section C and C++ (clangd)

@cindex clangd
@cindex C, language server
@cindex C++, language server
The second wired language is @strong{C and C++}, through
@uref{https://clangd.llvm.org/, clangd} -- xwpe's home turf.  Install it
(@code{apt install clangd} on Debian/Ubuntu) and open a @file{.c}, @file{.h},
@file{.cpp} (or @file{.cc}/@file{.cxx}/@file{.hpp}/@dots{}) file; the same
@kbd{Alt-Q} keys work, now backed by clangd instead of Metals.  Unlike Metals
there is no JVM and no build-server import, so the server is ready in seconds and
@kbd{Alt-Q D} (definition) follows straight into the system headers under
@file{/usr/include} -- which open @strong{read-only} (the padlock), since they
are not yours to edit.

@cindex compile_commands.json
@cindex compile_flags.txt
For a single file clangd uses sensible default flags.  For a real project, give
it the compile flags so its diagnostics and navigation match your build: either a
@file{compile_commands.json} (generated by @command{bear -- make}, or by CMake
with @code{-DCMAKE_EXPORT_COMPILE_COMMANDS=ON}) or a one-line
@file{compile_flags.txt} (e.g.@: @code{-I./include}) in the project root.  clangd
finds it by walking up from the file.

@section Python (pyright or pylsp)

@cindex pyright
@cindex pylsp
@cindex Python, language server
@strong{Python} is wired through @emph{two} servers, and xwpe uses whichever is
installed -- it prefers @uref{https://github.com/microsoft/pyright, pyright}
(Microsoft's type checker, what VS Code uses) and falls back to
@uref{https://github.com/python-lsp/python-lsp-server, pylsp}
(@command{python-lsp-server}).  Open a @file{.py} (or @file{.pyi}) file and the
@kbd{Alt-Q} keys work, the status bar naming the active one (@samp{Alt-Q ?
pyright} or @samp{Alt-Q ? pylsp}).

@table @asis
@item pyright
@example
pipx install pyright          # or: pip install --user pyright / npm i -g pyright
@end example
The richer choice: full type inference, so hover shows inferred types, completion
is type-aware, and diagnostics flag undefined names and type errors out of the
box.  Not in the Debian archive (it is a Node program) -- install it with pipx,
pip or npm; xwpe looks for @command{pyright-langserver} on @env{PATH}.

@item pylsp
@example
sudo apt install python3-pylsp          # Debian/Ubuntu
@end example
The Debian-native fallback (in the archive).  Navigation, hover, completion,
references and rename come from the bundled @dfn{jedi}; @strong{diagnostics need
@code{python3-pyflakes}} (and @code{python3-pycodestyle} for style) installed
alongside -- without them pylsp still navigates, just does not lint.
@end table

@noindent
With both installed, xwpe uses pyright; remove it (or install only pylsp) and it
uses pylsp -- no configuration, just what is on @env{PATH}.

@section Go (gopls)

@cindex gopls
@cindex Go, language server
@cindex go.mod
@strong{Go} is wired through @uref{https://pkg.go.dev/golang.org/x/tools/gopls,
gopls}, the official Go language server.  Install it and open a @file{.go} file:

@example
sudo apt install gopls          # Debian/Ubuntu (or: go install
                                #   golang.org/x/tools/gopls@@latest)
@end example

@noindent
The @kbd{Alt-Q} keys work as everywhere else (the bar shows @samp{Alt-Q ?
gopls}).  One Go-specific note: gopls wants a @strong{module} to give full
results, so work inside a directory with a @file{go.mod} (@code{go mod init
@var{name}} creates one).  A lone @file{.go} file with no module loads in a
degraded ad-hoc mode -- navigation may be limited until the module exists.
gopls is a type checker, so @kbd{Alt-Q E} flags undefined names and type errors
the way the compiler would.

@section Rust (rust-analyzer)

@cindex rust-analyzer
@cindex Rust, language server
@cindex Cargo
@strong{Rust} is wired through @uref{https://rust-analyzer.github.io/,
rust-analyzer}, the official server.  Install it and open a @file{.rs} file:

@example
sudo apt install rust-analyzer          # Debian/Ubuntu
                                        # (or: rustup component add rust-analyzer)
@end example

@noindent
The @kbd{Alt-Q} keys work as everywhere else.  On the bottom bar the entry is
shown concisely as @samp{Alt-Q ? rust} -- the full @samp{rust-analyzer} is too
wide to fit the 80-column button row without crowding out @samp{Alt-X Quit}, so
the short form is used (the menu and this manual keep the full name).  Two notes:

@itemize @bullet
@item Like gopls, rust-analyzer wants a @strong{Cargo workspace}: open files
inside a crate (a directory with a @file{Cargo.toml}; @code{cargo new}/@code{cargo
init} make one).  It also reads the standard library, so install @code{rust-src}
(@code{rustup component add rust-src}, or the @code{rust-src} package) for
go-to-definition into @code{std}.
@item rust-analyzer @strong{indexes the standard library on first start}, so the
cold start is the slowest of the wired servers -- the editor stays responsive
(the async start), and the actions sharpen as the index settles.
@end itemize

@cindex status bar, long label
@noindent
More generally, the bottom button row repacks itself to the width of its labels,
so a longer server name (e.g.@: @samp{pyright}) still leaves every button -- up
to and including @samp{Alt-X Quit} -- whole on screen.

@section Other languages

LSP is one protocol for every language: adding a server is a descriptor entry,
not new plumbing, because the transport (JSON-RPC over the same framing as the
DAP debugger) is shared.  Five languages are wired today -- Scala (Metals), C/C++
(clangd), Python (pyright/pylsp), Go (gopls) and Rust (rust-analyzer) -- and the
same one-row recipe adds any other LSP server (a descriptor row, the file
extension, the command); nothing else changes.

@section How it works

xwpe spawns the server as a child process and talks LSP over its
stdin/stdout.  The engine (@file{we_lsp.c}) is independent of the editor and is
integration-tested against real servers for @emph{each} wired language -- Metals
(Scala), clangd (C/C++), a Python server (pyright or pylsp), gopls (Go) and
rust-analyzer (Rust) -- the same code drives every one, only the spawned command
and the @code{languageId} differ; the editor side maps the @kbd{Alt-Q} keys onto
it.
Because the server is an external program, the heavy machinery (for Scala: a JVM,
the build server) lives outside xwpe -- the editor binary itself gains only a few
kilobytes of protocol code.  See @file{HACKING-LSP.md} in the source tree for the
architecture.
