Skip to content

Commit

Permalink
Fix #124: Move to LSP char positions exactly according to spec
Browse files Browse the repository at this point in the history
* eglot-tests.el (issue-124): New test.

* eglot.el (eglot--lsp-char-to-column): New helper.
(eglot--lsp-position-to-point): Use eglot--lsp-char-to-column.

* fixtures/utf16.cpp: New test fixture
  • Loading branch information
joaotavora committed Nov 7, 2018
1 parent f2ccb77 commit 1999245
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 64 deletions.
9 changes: 9 additions & 0 deletions eglot-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,15 @@ Pass TIMEOUT to `eglot--with-timeout'."
(should-not (eglot--server-capable :foobarbaz))
(should-not (eglot--server-capable :textDocumentSync :foobarbaz))))


(ert-deftest issue-124 ()
"Test LSP's UTF-16 quirks."
(with-current-buffer (find-file-no- "fixtures/utf16.cpp")
(should (eql (eglot--lsp-position-to-point '(:character 8 :line 2)) 22))
(should (eql (eglot--lsp-position-to-point '(:character 19 :line 2)) 33))
(should (eql (eglot--lsp-position-to-point '(:character 50 :line 2)) 63))
(should (eql (eglot--lsp-position-to-point '(:character 60 :line 2)) 73))))

(provide 'eglot-tests)
;;; eglot-tests.el ends here

Expand Down
132 changes: 68 additions & 64 deletions eglot.el
Original file line number Diff line number Diff line change
Expand Up @@ -700,16 +700,30 @@ CONNECT-ARGS are passed as additional arguments to
:character (- (goto-char (or pos (point)))
(line-beginning-position)))))

(defun eglot--lsp-char-to-column (lsp-charpos)
"Helper for `eglot--lsp-position-to-point'."
(let ((line (buffer-substring-no-properties (point) (line-end-position))))
(with-temp-buffer
(save-excursion (insert line))
(cl-loop with start-filepos =
(bufferpos-to-filepos (point) 'exact 'utf-16-unix)
for current-pos = start-filepos
then (bufferpos-to-filepos (point) 'exact 'utf-16-unix)
while (and (not (eolp))
(< (/ (- current-pos start-filepos) 2)
lsp-charpos))
do (forward-char)
finally do (cl-return (1- (point)))))))

(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)))
(forward-char (eglot--lsp-char-to-column (plist-get pos-plist :character)))
(if marker (copy-marker (point-marker)) (point))))

(defun eglot--path-to-uri (path)
"URIfy PATH."
Expand Down Expand Up @@ -1943,63 +1957,53 @@ If SKIP-SIGNATURE, don't try to send textDocument/signatureHelp."

(defun eglot--eclipse-jdt-contact (interactive)
"Return a contact for connecting to eclipse.jdt.ls server, as a cons cell."
(cl-labels
((is-the-jar
(path)
(and (string-match-p
"org\\.eclipse\\.equinox\\.launcher_.*\\.jar$"
(file-name-nondirectory path))
(file-exists-p path))))
(let* ((classpath (or (getenv "CLASSPATH") ":"))
(cp-jar (cl-find-if #'is-the-jar (split-string classpath ":")))
(jar cp-jar)
(dir
(cond
(jar (file-name-as-directory
(expand-file-name ".." (file-name-directory jar))))
(interactive
(expand-file-name
(read-directory-name
(concat "Path to eclipse.jdt.ls directory (could not"
" find it in CLASSPATH): ")
nil nil t)))
(t (error "Could not find eclipse.jdt.ls jar in CLASSPATH"))))
(repodir
(concat dir
"org.eclipse.jdt.ls.product/target/repository/"))
(repodir (if (file-directory-p repodir) repodir dir))
(config
(concat
repodir
(cond
((string= system-type "darwin") "config_mac")
((string= system-type "windows-nt") "config_win")
(t "config_linux"))))
(project (or (project-current) `(transient . ,default-directory)))
(workspace
(expand-file-name (md5 (car (project-roots project)))
(concat user-emacs-directory
"eglot-eclipse-jdt-cache"))))
(unless jar
(setq jar
(cl-find-if #'is-the-jar
(directory-files (concat repodir "plugins") t))))
(unless (and jar (file-exists-p jar) (file-directory-p config))
(error "Could not find required eclipse.jdt.ls files (build required?)"))
(when (and interactive (not cp-jar)
(y-or-n-p (concat "Add path to the server program "
"to CLASSPATH environment variable?")))
(setenv "CLASSPATH" (concat (getenv "CLASSPATH") ":" jar)))
(unless (file-directory-p workspace)
(make-directory workspace t))
(cons 'eglot-eclipse-jdt
(list (executable-find "java")
"-Declipse.application=org.eclipse.jdt.ls.core.id1"
"-Dosgi.bundles.defaultStartLevel=4"
"-Declipse.product=org.eclipse.jdt.ls.core.product"
"-jar" jar
"-configuration" config
"-data" workspace)))))


(if-let ((jar (cl-find "org\\.eclipse\\.equinox\\.launcher_.*\\.jar$"
(split-string (getenv "CLASSPATH") ":")
:test #'string-match-p
:key #'file-name-nondirectory)))
(setq dir (expand-file-name "../" (file-name-directory jar)))

)

(let (config
(jar
(or
(interactive
(setq dir (read-directory-name
(concat "Path to eclipse.jdt.ls directory (could not"
" find it in CLASSPATH): ")
nil nil t))
(setq repodir
(find #'file-directory-p
(expand-file-name "org.eclipse.jdt.ls.product/target/repository/" dir)
dir))
(setq config
(expand-file-name
(cond
((string= system-type "darwin") "config_mac")
((string= system-type "windows-nt") "config_win")
(t "config_linux"))
repodir)))))


(unless (and jar (file-exists-p jar) (file-directory-p config))
(error "Could not find required eclipse.jdt.ls files (build required?)"))
(when (and interactive (not cp-jar)
(y-or-n-p (concat "Add path to the server program "
"to CLASSPATH environment variable?")))
(setenv "CLASSPATH" (concat (getenv "CLASSPATH") ":" jar)))
(unless (file-directory-p workspace)
(make-directory workspace t))
(cons 'eglot-eclipse-jdt
(list (executable-find "java")
"-Declipse.application=org.eclipse.jdt.ls.core.id1"
"-Dosgi.bundles.defaultStartLevel=4"
"-Declipse.product=org.eclipse.jdt.ls.core.product"
"-jar" jar
"-configuration" config
"-data" workspace)))))

(cl-defmethod eglot-execute-command
((_server eglot-eclipse-jdt) (_cmd (eql java.apply.workspaceEdit)) arguments)
Expand Down
Binary file added fixtures/utf16.cpp
Binary file not shown.

0 comments on commit 1999245

Please sign in to comment.