Emacs Take Two (Eglot)
This post is my contribution to the Emacs Carnival June 2025 topic “Take Two” set by Christian Tietze. This is also a cross post for the IndieWeb Carnival: Take Two which was the original inspiration behind the Emacs Carnival. You can get more information about the Emacs Carnival on the wiki.
I started using Emacs about 20 years ago. From the start I was heavily involved in the Emacs IRC channel and the Org mode mailing list. I contributed the refresh of the Org mode logo to Dr. Carsten Dominik and the community as a small thank you for such a great package. All my notes were stored in Org mode files, along with my task lists. For development I used Elpy for Python and Webmode for anything HTML and Javascript. Elpy was good enough for me back then. Webmode was just ok but had a lot of problems with javascript. I jumped over to LSP mode pretty early and that solved a lot of problems with Python but it was a pain to configure and broke at the most inconvenient times. I was frustrated a lot of the time I was trying to work.
Around 2015 I was introduced to PyCharm by a client. The debugger was fantastic and hooked me quickly. It replaced a lot of what I was using Emacs for. I still used Emacs but jumped back and forth between the two (a lot of things are just easier in Emacs). Around the same time I started working with React and I could never get anything working well in Emacs, but PyCharm just worked. So I stayed with PyCharm for many years (mostly). All my notes were still stored in Org mode files (and most still are). I still have notes in org files from nearly 20 years ago. I can’t say that about any other notes apps.
When Eglot started gaining momentum I decided to see what it could do. I was pleasantly surprised but it did not fully bring me back. It was still easier to just use PyCharm. PyCharm is a great IDE but I find IDEs too opinionated. I always had the itch to return to Emacs for all my development needs. A year or two ago I decided to give Eglot another try. It had matured - or perhaps I had. I played around with the available options for LSP and found Pyright to be very good. I was able to get my Emacs config to provide me with most of what PyCharm was handling. When I finally upgraded Emacs from 29 to 30, I stopped using PyCharm. With Eglot, the current options for Language Servers, and Tree-sitter I now have a stable environment for Python/Django work as well as React. I’m now comfortable with my environment.
Currently, I’m using Eglot with Basedpyright Language Server and Ruff for linting. For React/Javascript I’m using the Typescript Language Server. Below is my current Eglot config. It has taken many iterations to get it where I’m happy with it. I still need to add JS linting to it but for now that’s mostly covered in my pre-commit hooks.
(use-package eglot
:straight t
:ensure t
:defer t
:custom
;; Optimize performance
(eglot-send-changes-idle-time 0.5)
(eglot-extend-to-xref t)
;; Configure language servers
(eglot-server-programs
'((python-ts-mode . ("basedpyright-langserver" "--stdio"
:initializationOptions (:basedpyright (:plugins (
:ruff (:enabled t
:lineLength 88
:exclude ["E501"] ; Disable line length warnings
:select ["E", "F", "I", "UP"]) ; Enable specific rule families
:pycodestyle (:enabled nil) ; Disable other linters since we're using ruff
:pyflakes (:enabled nil)
:pylint (:enabled nil)
:rope_completion (:enabled t)
:autopep8 (:enabled nil))))))
((js-ts-mode typescript-ts-mode tsx-ts-mode) .
("typescript-language-server" "--stdio"))))
:config
(setq-default
eglot-workspace-configuration
'(:basedpyright (
:typeCheckingMode "off"
)
:basedpyright.analysis (
:diagnosticSeverityOverrides (
:reportUnusedCallResult "none"
)
:inlayHints (
:callArgumentNames :json-false
)
)))
:bind (:map eglot-mode-map
("C-c l a" . eglot-code-actions)
("C-c l r" . eglot-rename)
("C-c l f" . eglot-format)
("C-c l d" . eldoc)
("C-c l o" . eglot-code-action-organize-imports)
("C-c l h" . eglot-inlay-hints-mode)
("C-c l q" . eglot-shutdown-all))
:hook ((python-ts-mode . eglot-ensure)
(js-ts-mode . eglot-ensure)
(typescript-ts-mode . eglot-ensure)
(tsx-ts-mode . eglot-ensure)
;; Python-specific settings
(python-ts-mode . (lambda ()
(setq-local indent-tabs-mode nil
tab-width 4
python-indent-offset 4)
(superword-mode 1)
(hs-minor-mode 1)
(set-fill-column 88)
(display-line-numbers-mode 1)))))