Emacs 29’s Eglot Boosts Scala & Kotlin
Emacs 29 Changed the JVM Editor Conversation: Eglot for Scala and Kotlin in 2026
Emacs 29 changed the JVM editor conversation because Eglot became part of Emacs itself, and that matters right now for Scala and Kotlin developers who want IDE-grade language features without living inside a heavier desktop IDE. The jointhefreeworld Scala and Kotlin Eglot guide frames the shift clearly: Eglot is lightweight, fast, and aligned with Emacs conventions, while language servers do Scala and Kotlin analysis work outside the editor.
The practical story is simple: Emacs edits text, Eglot speaks the Language Server Protocol, Metals handles Scala, and kotlin-language-server handles Kotlin. That split gives developers completion, diagnostics, go-to-definition, rename, and code actions through standard Emacs commands instead of a single IDE owning the whole workflow.

Key Takeaways
- Eglot is the built-in Emacs Language Server Protocol client in Emacs 29 and later.
- Scala support normally comes from Metals, while Kotlin support uses kotlin-language-server.
- The most reliable setup keeps Emacs configuration small and pushes language intelligence into the server process.
- lsp-mode still has a larger feature surface, but Eglot has a simpler operating model for many Emacs users.
- The main trade-offs are debugging depth, advanced refactoring polish, and occasional language server state issues.
Why Eglot Matters for Scala and Kotlin in 2026
Note: The following code is an illustrative example and has not been verified against official documentation. Please refer to the official docs for production-ready code.
;; Save as init-eglot-jvm.el, then run:
;; emacs --batch -l init-eglot-jvm.el
;;
;; Expected output includes:
;; Eglot JVM setup loaded
(require 'package)
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
(dolist (package-name '(scala-mode kotlin-mode))
(unless (package-installed-p package-name)
(package-install package-name)))
(require 'eglot)
(require 'scala-mode)
(require 'kotlin-mode)
(add-to-list 'eglot-server-programs '(scala-mode . ("metals")))
(add-to-list 'eglot-server-programs '(kotlin-mode . ("kotlin-language-server")))
(add-hook 'scala-mode-hook #'eglot-ensure)
(add-hook 'kotlin-mode-hook #'eglot-ensure)
(message "Eglot JVM setup loaded")
;; Note: prod use should pin package archives and manage server versions separately.
This is the common case: open a Scala file, let scala-mode activate, and let eglot-ensure start Metals. Open a Kotlin file, let kotlin-mode activate, and let the Kotlin server attach. The GNU Eglot quick start says starting Eglot in a project buffer begins the configured language server for that programming language and makes Eglot manage related buffers.

The important design choice is that Eglot uses existing Emacs interfaces rather than inventing a separate editor inside Emacs. Navigation goes through xref. Diagnostics go through Flymake. Completion goes through completion-at-point. Andrey Listopadov’s migration notes say both Eglot and lsp-mode provide completion and go-to-definition through standard Emacs APIs such as completion-at-point-fns and xref-backend-fns in his lsp-mode to Eglot migration write-up.
That matters for developers with one to five years of experience because the setup stays understandable. You do not need to learn a separate dashboard before you can jump to definition. You do not need a special completion framework before you can ask the server for symbols. Eglot fits into Emacs instead of asking Emacs users to adopt a new editor model.
A Working Emacs Eglot Setup for Scala and Kotlin in 2026
#!/usr/bin/env bash
# Save as check-eglot-jvm-tools.sh, then run:
# chmod +x check-eglot-jvm-tools.sh
# ./check-eglot-jvm-tools.sh
#
# Expected output on a configured workstation:
# found: emacs
# found: metals
# found: kotlin-language-server
# ready: Emacs can launch Scala and Kotlin language servers
set -euo pipefail
required_commands=("emacs" "metals" "kotlin-language-server")
for command_name in "${required_commands[@]}"; do
if command -v "$command_name" >/dev/null 2>&1; then
echo "found: $command_name"
else
echo "missing: $command_name" >&2
exit 1
fi
done
echo "ready: Emacs can launch Scala and Kotlin language servers"
# Note: prod use should also check project build files and Java runtime policy.
Before tuning Emacs Lisp, verify server binaries. Eglot can only start a language server that Emacs can find on exec-path. If metals or kotlin-language-server works in your terminal but not in Emacs, the usual cause is that GUI Emacs inherited a different environment from your shell.
The Eglot README says users can install Eglot with M-x package-install RET eglot RET in Emacs 26.3 and later, then run M-x eglot in a source file. That command path still matters for users on older Emacs builds, but the 2026 default for Emacs 29 users is simpler because Eglot ships with Emacs.
Scala projects usually have more moving parts than small Kotlin examples because Metals must understand the build. When Metals starts, it needs to infer project structure and compiler classpath. If it cannot find a build root, features degrade. You may still get syntax awareness, but type-aware completion and navigation will suffer.
Kotlin has a similar rule: the language server needs to run from a project that contains Kotlin files. If you open a single detached file outside a project root, Eglot can start the server, but project features will be weaker. Keep source files inside a normal repository layout and start Emacs from a location where the server can find the project.
How Eglot, Metals, and Kotlin Language Server Flow Works in 2026
;; Save as print-eglot-jvm-map.el, then run:
;; emacs --batch -l print-eglot-jvm-map.el
;;
;; Expected output:
;; Scala server command: metals
;; Kotlin server command: kotlin-language-server
(require 'eglot)
(setq eglot-server-programs
'((scala-mode . ("metals"))
(kotlin-mode . ("kotlin-language-server"))))
(let ((scala-command (cadr (assoc 'scala-mode eglot-server-programs)))
(kotlin-command (cadr (assoc 'kotlin-mode eglot-server-programs))))
(message "Scala server command: %s" (car scala-command))
(message "Kotlin server command: %s" (car kotlin-command)))
;; Note: prod use should keep per-project overrides in versioned editor config.
The mapping above is the heart of the setup. Eglot does not parse Scala itself. It launches Metals for buffers using scala-mode. It does not parse Kotlin itself. It launches kotlin-language-server for buffers using kotlin-mode. That is why failures tend to fall into predictable buckets: the server command is missing, the project root is wrong, the build import fails, or the language server process needs a restart.
The Eglot manual describes Eglot as language-agnostic: it can support any programming language that has both a language server and an Emacs major mode. Scala and Kotlin fit that model cleanly because the editor mode handles file syntax, while the server handles project-aware language features.
For Scala, Metals is the center of the experience. It reads the build, indexes symbols, reports diagnostics, and answers LSP requests from Eglot. For Kotlin, the Kotlin server plays an equivalent role. In both cases, Eglot is a protocol client, not a compiler and not a build tool.
This distinction helps when debugging. If completion works in one project and fails in another, Eglot is rarely the first suspect. Check whether the server process started, whether the project root is correct, and whether the build import succeeded. Use M-x eglot-events-buffer when you need the raw conversation between Emacs and the server.
Eglot vs lsp-mode for JVM Work in 2026
Note: The following code is an illustrative example and has not been verified against official documentation. Please refer to the official docs for production-ready code.
;; Save as eglot-jvm-keys.el, then evaluate in Emacs:
;; M-x load-file RET eglot-jvm-keys.el RET
;;
;; Expected result:
;; Keybindings are installed for buffers where eglot-mode is active.
(with-eval-after-load 'eglot
(define-key eglot-mode-map (kbd "M-.") #'xref-find-definitions)
(define-key eglot-mode-map (kbd "M-?") #'xref-find-references)
(define-key eglot-mode-map (kbd "C-c r") #'eglot-rename)
(define-key eglot-mode-map (kbd "C-c a") #'eglot-code-actions)
(define-key eglot-mode-map (kbd "C-c f") #'eglot-format))
(message "Eglot JVM keybindings loaded")
;; Note: prod use should avoid keybindings that conflict with team Emacs configs.
The comparison with lsp-mode should start with workflow, not ideology. Both clients speak LSP. Both can provide completion and navigation through standard Emacs APIs. The difference is the amount of client-side machinery you want around the server.
| 2026 decision point | Eglot | lsp-mode | Source |
|---|---|---|---|
| Startup behavior | Connects faster because it does less work on startup | Can add 1-3 seconds on first open while initializing UI components and workspace scanning | linkhub.dk Eglot vs lsp-mode comparison |
| Emacs API integration | Uses standard Emacs APIs for completion and go-to-definition | Uses standard Emacs APIs for completion and go-to-definition | Andrey Listopadov migration notes |
| Default client status | Built into Emacs 29 as default LSP client described by jointhefreeworld setup | Discussed as more feature-complete Emacs LSP implementation in practitioner comparison | jointhefreeworld JVM setup and Aleksandr Petrosyan on Emacs LSP clients |
The table points to a real trade-off. Eglot is a smaller layer. lsp-mode adds more client-side features. If your Scala or Kotlin workflow is mainly edit, compile, jump, rename, format, and inspect diagnostics, Eglot is easier to reason about. If you want heavier UI behavior inside Emacs, lsp-mode may be a better fit.
The practical reason developers leave IntelliJ is usually not that IntelliJ lacks power. It is that the editor becomes a project environment, build interface, navigation model, and source of truth for too many actions. Eglot moves the source of truth back into standard tools: the repository, the language server, the build, and Emacs commands.
For cost and control discussions, the same build-vs-buy discipline applies to developer tools that we use for infrastructure choices. If a team standardizes on a desktop IDE, it buys speed and a common interface. If it supports Emacs plus language servers, it buys control and portability. For a broader way to frame those trade-offs, see our 2026 guide to engineering finance and build-vs-buy infrastructure decisions.
Daily Scala and Kotlin Workflow in Emacs in 2026
A good Eglot workflow should feel boring. Open a project. Open a Scala or Kotlin file. Wait for the mode hook to start the server. Use M-. for definitions, M-? for references, C-c r for rename, and C-c a for code actions. If you need to inspect the server, open the Eglot events buffer.

Scala developers should pay special attention to build import state. Metals depends on the build definition to understand dependencies and compiler options. When a project changes build files, restart or reconnect the server if diagnostics look stale. This is faster than chasing phantom editor issues.
Kotlin developers should take the same approach with project roots. Open a repository, not an isolated file copied into a scratch directory. The Kotlin server needs project context to give useful answers. If completion returns only obvious symbols, check whether Eglot started the server from the expected root.
Diagnostics should remain part of the editor loop, but they should not replace builds and tests. LSP diagnostics are excellent for fast feedback. They are not a final release gate. Keep command-line builds in your workflow and treat Eglot as an interactive editor layer over a real project.

Production Pitfalls and Trade-Offs in 2026
The first pitfall is treating Eglot as an IDE replacement rather than an LSP client. It does not try to own your entire dev loop. It gives Emacs a way to ask a language server for semantic information. That narrower role is a strength, but it also means some IntelliJ workflows do not have one-click equivalents.
The second pitfall is ignoring server state. If Metals or kotlin-language-server gets confused, restarting Emacs is usually unnecessary. Use M-x eglot-reconnect first. If that fails, check the server executable and project root. Most problems are outside Emacs Lisp configuration after the initial setup is correct.
The third pitfall is over-configuring too early. Developers often copy a large Emacs setup before they understand which piece solves which problem. Start with hooks, server mappings, and a few keybindings. Add completion UI, diagnostics styling, and project helpers only after the base loop works.
The fourth pitfall is expecting the same refactoring depth as a dedicated JVM IDE. Metals supports language-aware actions through LSP, but IntelliJ has years of product work around refactoring flows, debugging screens, and project import UX. If your job depends on complex cross-file refactors every day, keep IntelliJ available while you test the Emacs path.
The fifth pitfall is confusing editor speed with build speed. Eglot can make the editor feel lighter, but Scala and Kotlin builds still run through their language and build tooling. If compile times are slow, changing LSP clients will not fix the build. It will only make editing, navigation, and diagnostics feel different.
The 2026 Takeaway for Developers Leaving IntelliJ
Emacs plus Eglot is a credible Scala and Kotlin development setup in 2026 because it uses the right division of labor. Emacs stays an editor. Eglot stays a protocol client. Metals and kotlin-language-server do language work. That separation is why the setup appeals to developers who want control without giving up modern IDE signals.
The strongest reason to switch is operational clarity. When something breaks, you can ask a specific question: did Emacs load the major mode, did Eglot start, did the server launch, did the project root resolve, and did the build import complete? That chain is easier to inspect than a large IDE status screen that hides several subsystems behind one progress bar.
The strongest reason to stay with IntelliJ is depth. If you need a richer debugger UX, broader JVM refactoring support, or a single packaged environment for a mixed-experience team, a dedicated IDE still has advantages. Eglot wins when you value a smaller toolchain, standard Emacs commands, and the ability to compose your own workflow.
Start with the code in this guide. Confirm emacs, metals, and kotlin-language-server are available. Load the Emacs Lisp mapping. Open a real project, not a scratch file. If navigation and diagnostics work, you have already crossed the biggest line: your editor is no longer a bottleneck between you and Scala or Kotlin code.
Related Reading
More in-depth coverage from this blog on closely related topics:
- A 10-K Can Change the Way You Read a Tech
- Kimi and Sota: Competitive Reasoning in AI
- Hugging Face in 2026: Security Challenges
- Gemini and Flash: AI Infrastructure ROI
- AIodysseybyyoroll: 2024 AI Models for 2026
Sources and References
Sources cited while researching and writing this article:
Rafael
Born with the collective knowledge of the internet and the writing style of nobody in particular. Still learning what "touching grass" means. I am Just Rafael...
