An open API service indexing awesome lists of open source software.

https://github.com/nohzafk/emacs-devcontainer

Doom Emacs config works with LSP in Devcontainer
https://github.com/nohzafk/emacs-devcontainer

devcontainer-feature doom-emacs-configuration emacs lsp-client

Last synced: 8 months ago
JSON representation

Doom Emacs config works with LSP in Devcontainer

Awesome Lists containing this project

README

          

* Emacs with LSP in Devcontainer

This is a [[https://github.com/doomemacs/doomemacs][doom emacs]] configuration of how to use emacs with language server in devcontainer.

- use [[https://github.com/manateelazycat/lsp-bridge][lsp-bridge]] as the LSP client
- use [[https://github.com/nohzafk/devcontainer-feature-emacs-lsp-bridge][devcontainer-feature-emacs-lsp-bridge]] to install lsp-bridge and the language server inside the container
- creation of the devcontainer is delegated to VSCode with [[https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers][Dev Containers Extension]]

** status of devcontainer support

Currently I'm the main contributor of lsp-bridge's devcontainer support. Here I trace the features that I'd like to add, and what I've done for Emacs to work with devcontainer.

| status | scope | |
|--------+----------------------+----------------------------------------------------------------------------------------|
| DONE | devcontainer-feature | able to install lsp-bridge |
| DONE | devcontainer-feature | able to install any language server that lsp-bridge supports [1] |
| DONE | devcontainer-feature | daily auto update latest version of lsp-bridge and release |
| DONE | lsp-bridge | open container file using =find-file= /docker: |
| DONE | lsp-bridge | enable auto-completion |
| DONE | lsp-bridge | save file by =lsp-bridge-remote-save-buffer= |
| DONE | lsp-bridge | format buffer using =apheleia= |
| DONE | lsp-bridge | jump to definition and jump back |
| DONE | doom-emacs | ripgrep search in container |
| DONE | doom-emacs | open vTerm with =bash= in container |
| DONE | devcontainer-feature | instructions of how to setup with =Python= projects |
| TBD | devcontainer | manage container in Emacs with [[https://github.com/nohzafk/cli2eli][CLI2ELI]] |
| TBD | devcontainer | get rid of VSCode [2] |
| TBD | lsp-bridge | auto re-connect to the new container and re-open file if devcontainer has been rebuilt |

- [1] Consider it's done, while a few language servers are missing at Nixpkgs or require special setting, also language server which runs on Windows is not supported
- [2] Currently VSCode is needed as port forward is not implemented by =devcontainer CLI=. POC at [[https://github.com/nohzafk/devcontainer-cli-port-forwarder][devcontainer-cli-port-forwarder]]

* Remote Editing Configuration
** TRAMP
- Use =M-x tramp-cleanup-all-connections= to remove all cached connection history.
- Use =M-x tramp-cleanup-all-buffers= to close all remote buffers.

** install additional tools
To install additional tools like =ripgrep= in a devcontainer, you can use the =features/nix=.

Add this to your [[https://code.visualstudio.com/docs/devcontainers/create-dev-container][devcontainer.json]],

#+begin_src json :tangle no
{
"features": {
"ghcr.io/devcontainers/features/nix:1": {
"packages": "ripgrep"
}
}
}
#+end_src

use nix to install tools that you want to use in the container.

** tramp-remote-path
Ensure that =~/.nix-profile/bin= is included in the =PATH= when executing remote commands.

#+begin_src elisp :tangle config.el
(after! tramp
(add-to-list 'tramp-remote-path "~/.nix-profile/bin")
(add-to-list 'tramp-remote-path 'tramp-own-remote-path))
#+end_src

this will make search tools such as =ripgrep.el= and =projectile-ripgrep= works with remote files.

** lsp-bridge
#+begin_src elisp :tangle packages.el
(when (package! lsp-bridge
:recipe (:host github
:repo "manateelazycat/lsp-bridge"
:branch "master"
:files ("*.el" "*.py" "acm" "core" "langserver" "multiserver" "resources")
:build (:not compile)))
;; doom-emacs has mardown-mode
;; (package! markdown-mode)
(package! yasnippet)
(package! topsy)
(package! flymake-bridge
:recipe (:host github :repo "liuyinz/flymake-bridge" :branch "master")))
#+end_src
#+begin_src elisp :tangle config.el
(use-package! lsp-bridge
:config

;; for muscle memory to save buffer
(defun my/save-buffer ()
(interactive)
(if lsp-bridge-remote-file-flag
(call-interactively #'lsp-bridge-remote-save-buffer)
(call-interactively #'save-buffer)))

(map! "C-x C-s" #'my/save-buffer))

(use-package! flymake-bridge
:after lsp-bridge
:hook (lsp-bridge-mode . flymake-bridge-setup))

(map! :after flymake
"M-n" #'flymake-goto-next-error
"M-p" #'flymake-goto-prev-error)
#+end_src

** formatter support
Enable =format= feature in =init.el=, it will install the =apheleia= package.

use =SPC c f= to format the buffer.

#+begin_src elisp :tangle config.el
(use-package! apheleia
:after lsp-bridge
:config
;; don't mess up with lsp-mode
(setq +format-with-lsp nil)
(setq apheleia-remote-algorithm 'remote))
#+end_src

** remote file indicator

Add a sticky header to indicate editing remote file

#+begin_src elisp :tangle config.el
(use-package! topsy
:after lsp-bridge
:config
;; display a bar to remind editing remote file
(setcdr (assoc nil topsy-mode-functions)
(lambda ()
(when (lsp-bridge-is-remote-file) "[LBR] REMOTE FILE")))

;; do not activate when the current major mode is org-mode
(add-hook 'lsp-bridge-mode-hook (lambda ()
(unless (derived-mode-p 'org-mode)
(topsy-mode 1)))))
#+end_src

** vTerm
Enable =vterm= feature in =init.el=

use =/bin/bash= for vterm when editing container file, use =SPC o t= to open vTerm buffer

#+begin_src elisp :tangle config.el
(after! vterm
(defun my/set-vterm-shell ()
(when (string-prefix-p "/docker:" (file-remote-p default-directory))
(when (eq major-mode 'vterm-mode)
(let ((shell (if (string-prefix-p "/docker:" (file-remote-p default-directory))
"/bin/bash"
(or (getenv "SHELL") "/bin/bash"))))
(vterm-send-string (format "exec %s\n" shell))
(vterm-send-string "clear\n")))))

(add-hook 'vterm-mode-hook #'my/set-vterm-shell))
#+end_src

** how to setup language server for python project (using basedpyright)
It should be clearly stated to avoid any confusing that lsp-bridge itself runs
in a python process, and basedpyright-langserver uses another python process.

Normally you created a virtual environment using tools like *uv* or *pdm* at the
root directory, suppose the name of the virtual environment is ".venv".

You need to add a section to the *pyproject.toml*, and basedpyright will inspect
the virtual environment correctly, [[https://docs.basedpyright.com/latest/configuration/config-files/][more details]].

#+begin_src toml
[tool.basedpyright]
venvPath = "."
venv = ".venv"
#+end_src