From: João Távora Date: Mon, 12 Nov 2018 22:29:38 +0000 (+0000) Subject: Add ability to move to lsp-precise columns X-Git-Tag: emacs-29.0.90~1616^2~524^2~4^2~403 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=9f44e74ca9d2b254fbf9fd1d5ccfde64e2b652f1;p=emacs.git Add ability to move to lsp-precise columns Also close https://github.com/joaotavora/eglot/issues/125. Idea and much of design contributed by Michał Krzywkowski This introduces the variable eglot-move-to-column-function. According to the standard, LSP column/character offsets are based on a count of UTF-16 code units, not actual visual columns. So when LSP says position 3 of a line containing just \"aXbc\", where X is a multi-byte character, it actually means `b', not `c'. This is what the function `eglot-move-to-lsp-abiding-column' does. However, many servers don't follow the spec this closely, and thus this variable should be set to `move-to-column' in buffers managed by those servers. * eglot.el (eglot-move-to-column-function): New variable. (eglot-move-to-lsp-abiding-column): New function. (eglot--lsp-position-to-point): Use eglot-move-to-column-function. GitHub-reference: fix https://github.com/joaotavora/eglot/issues/124 --- diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 576d7f39d31..f7b9c869743 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -728,16 +728,43 @@ CONNECT-ARGS are passed as additional arguments to :character (- (goto-char (or pos (point))) (line-beginning-position))))) +(defvar eglot-move-to-column-function #'move-to-column + "How to move to a column reported by the LSP server. + +According to the standard, LSP column/character offsets are based +on a count of UTF-16 code units, not actual visual columns. So +when LSP says position 3 of a line containing just \"aXbc\", +where X is a multi-byte character, it actually means `b', not +`c'. This is what the function +`eglot-move-to-lsp-abiding-column' does. + +However, many servers don't follow the spec this closely, and +thus this variable should be set to `move-to-column' in buffers +managed by those servers.") + +(defun eglot-move-to-lsp-abiding-column (column) + "Move to COLUMN abiding by the LSP spec." + (cl-loop + initially (move-to-column column) + with lbp = (line-beginning-position) + for diff = (- column + (/ (- (length (encode-coding-region lbp (point) 'utf-16 t)) + 2) + 2)) + until (zerop diff) + for offset = (max 1 (abs (/ diff 2))) + do (if (> diff 0) (forward-char offset) (backward-char offset)))) + (defun eglot--lsp-position-to-point (pos-plist &optional marker) "Convert LSP position POS-PLIST to Emacs point. If optional MARKER, return a marker instead" - (save-excursion (goto-char (point-min)) - (forward-line (min most-positive-fixnum - (plist-get pos-plist :line))) - (forward-char (min (plist-get pos-plist :character) - (- (line-end-position) - (line-beginning-position)))) - (if marker (copy-marker (point-marker)) (point)))) + (save-excursion + (goto-char (point-min)) + (forward-line (min most-positive-fixnum + (plist-get pos-plist :line))) + (unless (eobp) ;; if line was excessive leave point at eob + (funcall eglot-move-to-column-function (plist-get pos-plist :character))) + (if marker (copy-marker (point-marker)) (point)))) (defun eglot--path-to-uri (path) "URIfy PATH." @@ -1040,7 +1067,7 @@ Uses THING, FACE, DEFS and PREPEND." (priority . ,(+ 50 i)) (keymap . ,(let ((map (make-sparse-keymap))) (define-key map [mouse-1] - (eglot--mouse-call 'eglot-code-actions)) + (eglot--mouse-call 'eglot-code-actions)) map)))))