Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support lsp-bridge in container correctly #1009

Merged
merged 2 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,13 @@ start the devcontainer and use `file-find` `/docker:user@container:/path/to/file
more detail please refer to [devcontainer-feature-emacs-lsp-bridge](https://github.com/nohzafk/devcontainer-feature-emacs-lsp-bridge).

If you use `apheleia` as formatter, `lsp-bridge` now support auto formatting file on devcontainer.
```elisp
;; setup PATH for remote command execution
(with-eval-after-load 'tramp
(add-to-list 'tramp-remote-path "~/.nix-profile/bin")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lsp-bridge user must be install Nix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

container support is done by using the devcontainer feature which use nix to install lsp-bridge and language server.

I'm planing to create a separate repo for instructions and setup of how to use lsp-bridge in container. Will update README to point to the new repo later.

(add-to-list 'tramp-remote-path 'tramp-own-remote-path))

```elsip
(use-package! apheleia
(use-package apheleia
:config
;; which formatter to use
(setf (alist-get 'python-mode apheleia-mode-alist) 'ruff)
Expand Down
2 changes: 1 addition & 1 deletion core/remote_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ def get_container_local_ip(container_name):
result = subprocess.check_output(command, shell=True, text=True).strip()
return result
except subprocess.CalledProcessError:
message_emacs(f"{traceback.format_exc()}")
message_emacs(f"get_container_local_ip error: {traceback.format_exc()}")
return None


Expand Down
87 changes: 41 additions & 46 deletions lsp-bridge.el
Original file line number Diff line number Diff line change
Expand Up @@ -843,9 +843,14 @@ you can customize `lsp-bridge-get-workspace-folder' to return workspace folder p

(defun lsp-bridge-find-file-hook-function ()
(when (and lsp-bridge-enable-with-tramp (file-remote-p (buffer-file-name)))
(lsp-bridge-sync-tramp-remote nil)
(when (string-prefix-p "/docker:" (buffer-file-name))
(lsp-bridge-call-async "open_remote_file" (buffer-file-name) (list :line 0 :character 0)))))
(lsp-bridge-sync-tramp-remote nil)))

(defun lsp-bridge--setup-tramp-docker-buffer (tramp-file-name)
"Setup the buffer of TRAMP-FILE-NAME as if it is a non-file buffer."
(with-current-buffer (get-file-buffer tramp-file-name)
(setq buffer-file-name nil)
(setq buffer-file-truename nil)
(message "set buffer %s buffer-file-name to nil" (buffer-name))))

(add-hook 'find-file-hook #'lsp-bridge-find-file-hook-function)

Expand Down Expand Up @@ -2058,40 +2063,36 @@ Then we need call `lsp-bridge--set-mark-ring-in-new-buffer' in new buffer after
(defun lsp-bridge--set-mark-ring-in-new-buffer ()
(setq-local lsp-bridge-mark-ring (append (list lsp-bridge-position-before-jump) mark-ring)))

(defun lsp-bridge-find-window-match-filename (filename)
(cl-dolist (window (window-list))
(when (string-equal filename (buffer-file-name (window-buffer window)))
(cl-return window))))

(defun lsp-bridge-define--jump (filename filehost position)
(let (lsp-bridge-position-before-jump)
(lsp-bridge--record-mark-ring)

(if (or (string-equal filehost "") lsp-bridge-enable-with-tramp)
(progn
;; filehost sent by lsp-bridge server running inside docker is 127.0.0.1
(when (and lsp-bridge-remote-file-host (string= filehost "127.0.0.1"))
(setq filehost lsp-bridge-remote-file-host))

(setq filename (concat (cdr (assoc filehost lsp-bridge-tramp-alias-alist)) filename))
(let ((match-window (lsp-bridge-find-window-match-filename filename)))
(if (and lsp-bridge-find-def-select-in-open-windows
match-window)
;; Try select to window if `lsp-bridge-find-def-select-in-open-windows' is non-nil.
(select-window match-window)
;; Jump to define.
;; Show define in other window if `lsp-bridge-jump-to-def-in-other-window' is non-nil.
(if lsp-bridge-jump-to-def-in-other-window
(find-file-other-window filename)
(find-file filename))
))
(if (and (not (string= filehost ""))
(not lsp-bridge-enable-with-tramp))
(lsp-bridge-call-async "open_remote_file" (format "%s:%s" filehost filename) position)
;; filehost is not empty or lsp-bridge-enable-with-tramp is t
(when (string= filehost "127.0.0.1")
(setq filehost lsp-bridge-remote-file-host))

(let ((match-window (lsp-bridge--with-file-buffer filename filehost (get-buffer-window))))
;; select the window to display definition
(if match-window
;; if match-window is found, avoid using find-file to open the file twice
(progn
(cond
(lsp-bridge-find-def-select-in-open-windows (select-window match-window))
(lsp-bridge-jump-to-def-in-other-window (select-window match-window))
(t (switch-to-buffer (window-buffer match-window)))))
;; match-window not found, we need to open the file
(let* ((tramp-file-name (concat (cdr (assoc filehost lsp-bridge-tramp-alias-alist)) filename)))
(if lsp-bridge-jump-to-def-in-other-window
(find-file-other-window tramp-file-name)
(find-file tramp-file-name))))

;; Init jump history in new buffer.
(lsp-bridge--set-mark-ring-in-new-buffer)

(lsp-bridge-define--jump-flash position))
(lsp-bridge-call-async "open_remote_file" (format "%s:%s" filehost filename) position))
))
(lsp-bridge-define--jump-flash position)))))

(defun lsp-bridge-define--jump-flash (position)
;; We need call `display' before `goto-char',
Expand Down Expand Up @@ -2813,6 +2814,7 @@ We need exclude `markdown-code-fontification:*' buffer in `lsp-bridge-monitor-be
(defvar-local lsp-bridge-remote-file-host nil)
(defvar-local lsp-bridge-remote-file-port nil)
(defvar-local lsp-bridge-remote-file-path nil)
;; TODO need to consider tramp docker file pattern
(defvar lsp-bridge-remote-file-pattern
(rx bos (? "/")
;; username
Expand Down Expand Up @@ -2851,9 +2853,11 @@ We need exclude `markdown-code-fontification:*' buffer in `lsp-bridge-monitor-be
"Conditionally execute BODY with the buffer associated with TRAMP-FILE-NAME.

If the buffer is created by TRAMP with TRAMP-FILE-NAME, BODY is executed within
the context of that buffer. If the buffer is created by
`lsp-bridge-open-remote-file--response', `lsp-bridge--with-file-buffer' is used
with PATH and HOST to find the buffer, then BODY is executed within that buffer."
the context of that buffer.

If the buffer is created by `lsp-bridge-open-remote-file--response',
`lsp-bridge--with-file-buffer' is used with PATH and HOST to find the buffer,
then BODY is executed within that buffer."

`(if (get-file-buffer ,tramp-file-name)
(with-current-buffer (get-file-buffer ,tramp-file-name)
Expand Down Expand Up @@ -2911,18 +2915,19 @@ I haven't idea how to make lsp-bridge works with `electric-indent-mode', PR are
(port (tramp-file-name-port tramp-vec))
(path (tramp-file-name-localname tramp-vec))
(tramp-connection-info (substring file-name 0 (+ 1 (string-match ":" file-name (+ 1 (string-match ":" file-name))))))
(ip-host (cdr (assoc tramp-connection-info lsp-bridge-tramp-connection-info))))
(connected-host (cdr (assoc tramp-connection-info lsp-bridge-tramp-connection-info))))

(if (or force (not ip-host))
(if (or force (not connected-host))
(when (and (not (member tramp-method '("sudo" "sudoedit" "su" "doas")))
(not (member host lsp-bridge-tramp-blacklist)))
(read-only-mode 1)
(lsp-bridge-call-async "sync_tramp_remote" file-name tramp-method user host port path))
(lsp-bridge--conditional-update-tramp-file-info file-name path ip-host

(lsp-bridge--conditional-update-tramp-file-info file-name path connected-host
(setq-local lsp-bridge-remote-file-flag t)
(setq-local lsp-bridge-remote-file-tramp-method tramp-method)
(setq-local lsp-bridge-remote-file-user user)
(setq-local lsp-bridge-remote-file-host ip-host)
(setq-local lsp-bridge-remote-file-host connected-host)
(setq-local lsp-bridge-remote-file-path path)

(add-hook 'kill-buffer-hook 'lsp-bridge-remote-kill-buffer nil t)
Expand Down Expand Up @@ -2971,7 +2976,6 @@ SSH tramp file name is like /ssh:user@host#port:path"

(with-current-buffer (get-buffer-create buf-name)
(text-mode)

(read-only-mode -1)
(erase-buffer)
(insert (lsp-bridge-decode-base64 content))
Expand Down Expand Up @@ -3007,15 +3011,6 @@ SSH tramp file name is like /ssh:user@host#port:path"
;; Remote file can always edit and update content even some file haven't corresponding lsp server, such as *.txt
(lsp-bridge-mode 1))

(when (string-equal tramp-method "docker")
(let ((tramp-filename (lsp-bridge-construct-tramp-file-name lsp-bridge-remote-file-tramp-method
lsp-bridge-remote-file-user
lsp-bridge-remote-file-host
lsp-bridge-remote-file-port
lsp-bridge-remote-file-path)))
;; use `find-file' to open TRAMP docker file will open a tramp buffer, kill it
(kill-buffer (get-file-buffer tramp-filename))))

(when lsp-bridge-ref-open-remote-file-go-back-to-ref-window
(lsp-bridge-switch-to-ref-window)
(setq lsp-bridge-ref-open-remote-file-go-back-to-ref-window nil)))
Expand Down
8 changes: 4 additions & 4 deletions lsp_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,8 @@ def init_search_backends(self):

@threaded
def init_remote_file_server(self):
print("* Running lsp-bridge in remote server, "
"use command 'lsp-bridge-open-remote-file' to open remote file, "
"or 'find-file' /docker: to open file in running container.")
print("* Running lsp-bridge on remote server. "
"Access files with 'lsp-bridge-open-remote-file' or 'find-file /docker:...'")

# Build loop for remote files management.
self.file_server = FileSyncServer("0.0.0.0", REMOTE_FILE_SYNC_CHANNEL)
Expand Down Expand Up @@ -437,6 +436,7 @@ def sync_tramp_remote(self, tramp_file_name, tramp_method, server_username, serv
self.remote_sync(container_name, tramp_connection_info)
# container_name is used for emacs buffer local variable lsp-bridge-remote-file-host
eval_in_emacs("lsp-bridge-update-tramp-file-info", tramp_file_name, tramp_connection_info, tramp_method, container_user, container_name, path)
eval_in_emacs("lsp-bridge--setup-tramp-docker-buffer", tramp_file_name)
self.sync_tramp_remote_complete_event.set()
except Exception as e:
logger.exception(e)
Expand Down Expand Up @@ -510,7 +510,7 @@ def open_remote_file(self, path, jump_define_pos):
"jump_define_pos": epc_arg_transformer(jump_define_pos)
})
else:
message_emacs("Please input valid path match rule: 'ip:/path/file'.")
message_emacs(f"Unsupported remote path {path}")

@threaded
def update_remote_file(self, remote_file_host, remote_file_path, content):
Expand Down
Loading