README.org
October 19, 2024 ยท View on GitHub
- 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