commit c640dc9ef533c1fa68cf7b29f63a3fb32e4c3b06 (HEAD, refs/remotes/origin/master) Merge: 6c4abbab799 267fc6d00c4 Author: Stefan Kangas Date: Mon Feb 27 06:30:20 2023 +0100 Merge from origin/emacs-29 267fc6d00c4 ruby-smie-rules: Fix misindentation of a method call afte... 0fde314f6f6 * lib-src/etags.c (process_file_name): Free malloc'ed var... dde9d149af3 ; Improve documentation of loading *.eln files 7c552be89da ; Another doc fix in eglot.el 75c65fcc98e ; Fix last change a3d15c1f749 ; Fix last change ca79b138d42 Eglot: rename and redocument encoding-related functions (... 3e3e6d71be7 Eglot: support positionEncoding LSP capability (bug#61726) b0e87e930e8 Eglot: use faster strategy for moving to LSP positions (b... 5b174b96834 Fix mule-tests in UTF-8 locales 5256392a7ec Fix 'vertical-motion' when display strings are around 0db88d625a7 ; * src/treesit.c (treesit_predicate_match): Fix typo. commit 267fc6d00c4db6f20da3c2d63e48a71ab70505d3 Author: Dmitry Gutov Date: Mon Feb 27 02:05:56 2023 +0200 ruby-smie-rules: Fix misindentation of a method call after assignment * lisp/progmodes/ruby-mode.el (ruby-smie-rules): Fix indentation of a method call after assignment with ruby-after-operator-indent=nil (bug#61822). * test/lisp/progmodes/ruby-mode-resources/ruby-after-operator-indent.rb: Add corresponding example. diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index dba9ff0a846..559b62fef54 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -909,7 +909,9 @@ ruby-smie-rules "<<=" ">>=" "&&=" "||=" "and" "or")) (cond ((not ruby-after-operator-indent) - (ruby-smie--indent-to-stmt ruby-indent-level)) + (ruby-smie--indent-to-stmt (if (smie-indent--hanging-p) + ruby-indent-level + 0))) ((and (smie-rule-parent-p ";" nil) (smie-indent--hanging-p)) ruby-indent-level))) diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-after-operator-indent.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-after-operator-indent.rb index 25cd8736f97..e339d229d3e 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-after-operator-indent.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-after-operator-indent.rb @@ -10,6 +10,10 @@ foo = obj.bar { |m| tee(m) } + obj.qux { |m| hum(m) } +some_variable = abc + some_method( + some_argument +) + foo. bar .baz commit 0fde314f6f6e6664cddab1b2f0fe20629cd39d14 Author: Eli Zaretskii Date: Sun Feb 26 20:03:20 2023 +0200 * lib-src/etags.c (process_file_name): Free malloc'ed vars (bug#61819). diff --git a/lib-src/etags.c b/lib-src/etags.c index 9fb9e312a66..0b048748602 100644 --- a/lib-src/etags.c +++ b/lib-src/etags.c @@ -1732,6 +1732,8 @@ process_file_name (char *file, language *lang) char *cmd = xmalloc (buf_len); snprintf (cmd, buf_len, "%s %s > %s", compr->command, new_real_name, new_tmp_name); + free (new_real_name); + free (new_tmp_name); #endif inf = (system (cmd) == -1 ? NULL commit dde9d149af3281f7ed3d3b9d8a4fd7caa238d0f9 Author: Eli Zaretskii Date: Sun Feb 26 19:51:59 2023 +0200 ; Improve documentation of loading *.eln files * doc/lispref/loading.texi (How Programs Do Loading): * doc/emacs/building.texi (Lisp Libraries): Some additional details about what happens with natively-compiled files. diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi index 3f6a418de1a..f82b605598e 100644 --- a/doc/emacs/building.texi +++ b/doc/emacs/building.texi @@ -1556,18 +1556,26 @@ Lisp Libraries searches through each directory in the Emacs Lisp load path, trying to find a file matching that library name. If the library name is @samp{@var{foo}}, it tries looking for files named -@file{@var{foo}.elc}, @file{@var{foo}.el}, and @file{@var{foo}}. The -default behavior is to load the first file found. This command -prefers @file{.elc} files over @file{.el} files because compiled files -load and run faster. If it finds that @file{@var{lib}.el} is newer -than @file{@var{lib}.elc}, it issues a warning, in case someone made -changes to the @file{.el} file and forgot to recompile it, but loads -the @file{.elc} file anyway. (Due to this behavior, you can save -unfinished edits to Emacs Lisp source files, and not recompile until -your changes are ready for use.) If you set the option -@code{load-prefer-newer} to a non-@code{nil} value, however, then -rather than the procedure described above, Emacs loads whichever -version of the file is newest. +@file{@var{foo}.elc}, @file{@var{foo}.el}, and @file{@var{foo}}. (If +Emacs was built with native compilation enabled, @code{load-library} +looks for a @samp{.eln} file that corresponds to @file{@var{foo}.el} +and loads it instead of @file{@var{foo}.elc}.) The default behavior +is to load the first file found. This command prefers @file{.eln} +files over @file{.elc} files, and prefers @file{.elc} files over +@file{.el} files, because compiled files load and run faster. If it +finds that @file{@var{lib}.el} is newer than @file{@var{lib}.elc}, it +issues a warning, in case someone made changes to the @file{.el} file +and forgot to recompile it, but loads the @file{.elc} file anyway. +(Due to this behavior, you can save unfinished edits to Emacs Lisp +source files, and not recompile until your changes are ready for use.) +If you set the option @code{load-prefer-newer} to a non-@code{nil} +value, however, then rather than the procedure described above, Emacs +loads whichever version of the file is newest. If Emacs was built +with native compilation, and it cannot find the @samp{.eln} file +corresponding to @file{@var{lib}.el}, it will load a +@file{@var{lib}.elc} and start native compilation of +@file{@var{lib}.el} in the background, then load the @samp{.eln} file +when it finishes compilation. Emacs Lisp programs usually load Emacs Lisp files using the @code{load} function. This is similar to @code{load-library}, but is @@ -1604,6 +1612,11 @@ Lisp Libraries @code{load-path}, or in some subdirectory of @file{site-lisp}. This way, you don't need to modify the default value of @code{load-path}. +@vindex native-comp-eln-load-path + Similarly to @code{load-path}, the list of directories where Emacs +looks for @file{*.eln} files with natively-compiled Lisp code is +specified by the variable @code{native-comp-eln-load-path}. + @cindex autoload Some commands are @dfn{autoloaded}; when you run them, Emacs automatically loads the associated library first. For instance, the diff --git a/doc/lispref/loading.texi b/doc/lispref/loading.texi index dbbdc767738..5c84ba4b1eb 100644 --- a/doc/lispref/loading.texi +++ b/doc/lispref/loading.texi @@ -75,17 +75,20 @@ How Programs Do Loading (@pxref{Native Compilation}), @code{load} attempts to find a corresponding @samp{.eln} file, and if found, loads it instead of @file{@var{filename}.elc}. Otherwise, it loads -@file{@var{filename}.elc}. If there is no file by that name, then -@code{load} looks for a file named @file{@var{filename}.el}. If that -file exists, it is loaded. If Emacs was compiled with support for -dynamic modules (@pxref{Dynamic Modules}), @code{load} next looks for -a file named @file{@var{filename}.@var{ext}}, where @var{ext} is a -system-dependent file-name extension of shared libraries. Finally, if -neither of those names is found, @code{load} looks for a file named -@var{filename} with nothing appended, and loads it if it exists. (The -@code{load} function is not clever about looking at @var{filename}. -In the perverse case of a file named @file{foo.el.el}, evaluation of -@code{(load "foo.el")} will indeed find it.) +@file{@var{filename}.elc} (and starts a background native compilation +to produce the missing @samp{.eln} file, followed by loading that +file). If there is no @file{@var{filename}.elc}, then @code{load} +looks for a file named @file{@var{filename}.el}. If that file exists, +it is loaded. If Emacs was compiled with support for dynamic modules +(@pxref{Dynamic Modules}), @code{load} next looks for a file named +@file{@var{filename}.@var{ext}}, where @var{ext} is a system-dependent +file-name extension of shared libraries (@samp{.so} on GNU and Unix +systems). Finally, if neither of those names is found, @code{load} +looks for a file named @var{filename} with nothing appended, and loads +it if it exists. (The @code{load} function is not clever about +looking at @var{filename}. In the perverse case of a file named +@file{foo.el.el}, evaluation of @code{(load "foo.el")} will indeed +find it.) If Auto Compression mode is enabled, as it is by default, then if @code{load} can not find a file, it searches for a compressed version commit 7c552be89da02270993f0866035abedb8c44f890 Author: Eli Zaretskii Date: Sun Feb 26 16:48:07 2023 +0200 ; Another doc fix in eglot.el * lisp/progmodes/eglot.el (eglot-current-linepos-function): Another doc fix. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 3d511126d93..dd84f545ed4 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1451,12 +1451,13 @@ eglot--warn 'eglot-current-column-function 'eglot-current-linepos-function "29.1") (defvar eglot-current-linepos-function #'eglot-utf-16-linepos - "Function calculating number of code units from line beginning. + "Function calculating position relative to line beginning. -This is the inverse operation of -`eglot-move-to-linepos-function' (which see). It is a function of -no arguments returning the number of code units corresponding to -the current position of point relative to line beginning.") +This is the inverse of `eglot-move-to-linepos-function' (which see). +It is a function of no arguments returning the number of code units +or bytes or codepoints corresponding to the current position of point, +relative to line beginning, as expected by the function that is the +value of `eglot-move-to-linepos-function'.") (defun eglot-utf-8-linepos () "Calculate number of UTF-8 bytes from line beginning." commit 6c4abbab7999f55792a323e4bb1eb55ef5a7b990 Author: Michael Albinus Date: Sun Feb 26 15:40:30 2023 +0100 Tramp: Do not unlock when connection is broken * lisp/net/tramp.el (tramp-handle-unlock-file): Do not unlock when connection is broken. (Bug#61663) * test/lisp/net/tramp-tests.el (tramp-test39-make-lock-file-name): Extend test. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index baa9f966dd8..2110d815c95 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -4778,7 +4778,13 @@ tramp-handle-make-lock-file-name (defun tramp-handle-unlock-file (file) "Like `unlock-file' for Tramp files." - (when-let ((lockname (tramp-compat-make-lock-file-name file))) + ;; When there is no connection, we don't do it. Otherwise, + ;; functions like `kill-buffer' would try to reestablish the + ;; connection. See Bug#61663. + (when-let ((v (tramp-dissect-file-name file)) + (p (tramp-get-process v)) + ((process-live-p p)) + (lockname (tramp-compat-make-lock-file-name file))) (condition-case err (delete-file lockname) ;; `userlock--handle-unlock-error' exists since Emacs 28.1. diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index f19847b0103..69004bdbdf3 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -6535,11 +6535,33 @@ tramp-test39-make-lock-file-name (save-buffer) (should-not (buffer-modified-p))) (should-not (with-no-warnings (file-locked-p tmp-name1))) + + ;; `kill-buffer' removes the lock. (with-no-warnings (lock-file tmp-name1)) (should (eq (with-no-warnings (file-locked-p tmp-name1)) t)) + (with-temp-buffer + (set-visited-file-name tmp-name1) + (insert "foo") + (should (buffer-modified-p)) + (cl-letf (((symbol-function #'read-from-minibuffer) + (lambda (&rest _args) "yes"))) + (kill-buffer))) + (should-not (with-no-warnings (file-locked-p tmp-name1))) + ;; `kill-buffer' should not remove the lock when the + ;; connection is broken. See Bug#61663. + (with-no-warnings (lock-file tmp-name1)) + (should (eq (with-no-warnings (file-locked-p tmp-name1)) t)) + (with-temp-buffer + (set-visited-file-name tmp-name1) + (insert "foo") + (should (buffer-modified-p)) + (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) + (cl-letf (((symbol-function #'read-from-minibuffer) + (lambda (&rest _args) "yes"))) + (kill-buffer))) ;; A new connection changes process id, and also the - ;; lockname contents. + ;; lockname contents. But the lock file still exists. (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password) (should (stringp (with-no-warnings (file-locked-p tmp-name1)))) commit 75c65fcc98ef5bda8fe91aeb8ba25c03e3182b8e Author: João Távora Date: Sun Feb 26 14:05:07 2023 +0000 ; Fix last change bug#61726 * lisp/progmodes/eglot.el (eglot-current-linepos-function): Fix docstring. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index ccd9cf3ee44..3d511126d93 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1451,7 +1451,7 @@ eglot--warn 'eglot-current-column-function 'eglot-current-linepos-function "29.1") (defvar eglot-current-linepos-function #'eglot-utf-16-linepos - "Function calculating number of UTF-16 code units from line beginning. + "Function calculating number of code units from line beginning. This is the inverse operation of `eglot-move-to-linepos-function' (which see). It is a function of commit a3d15c1f74988b488fc4f924b303f8de620645a9 Author: Eli Zaretskii Date: Sun Feb 26 15:24:11 2023 +0200 ; Fix last change * lisp/progmodes/eglot.el (eglot-current-linepos-function) (eglot-utf-8-linepos, eglot-utf-16-linepos) (eglot-utf-32-linepos, eglot-move-to-linepos-function) (eglot-move-to-utf-8-linepos, eglot-move-to-utf-32-linepos): Doc fixes. (Bug#61726) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index e63dd563c44..ccd9cf3ee44 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1451,7 +1451,7 @@ eglot--warn 'eglot-current-column-function 'eglot-current-linepos-function "29.1") (defvar eglot-current-linepos-function #'eglot-utf-16-linepos - "Function calculating number of code units to line beginning. + "Function calculating number of UTF-16 code units from line beginning. This is the inverse operation of `eglot-move-to-linepos-function' (which see). It is a function of @@ -1459,12 +1459,12 @@ eglot-current-linepos-function the current position of point relative to line beginning.") (defun eglot-utf-8-linepos () - "Calculate number of code units to line beginning using UTF-8." + "Calculate number of UTF-8 bytes from line beginning." (length (encode-coding-region (line-beginning-position) (point) 'utf-8-unix t))) (defun eglot-utf-16-linepos (&optional lbp) - "Calculate number of code units to line beginning using UTF-16. + "Calculate number of UTF-16 code units from position given by LBP. LBP defaults to `line-beginning-position'." (/ (- (length (encode-coding-region (or lbp (line-beginning-position)) ;; Fix github#860 @@ -1473,7 +1473,7 @@ eglot-utf-16-linepos 2)) (defun eglot-utf-32-linepos () - "Calculate number of code units to line beginning using UTF-32." + "Calculate number of Unicode codepoints from line beginning." (- (point) (line-beginning-position))) (defun eglot--pos-to-lsp-position (&optional pos) @@ -1492,7 +1492,7 @@ eglot--pos-to-lsp-position 'eglot-move-to-column-function 'eglot-move-to-linepos-function "29.1") (defvar eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos - "Function to move to a column reported by the LSP server. + "Function to move to a position within a line reported by the LSP server. Per the LSP spec, character offsets in LSP Position objects count UTF-16 code units, not actual code points. So when LSP says @@ -1501,13 +1501,13 @@ eglot-move-to-linepos-function actually means `b', not `c'. The default value `eglot-move-to-utf-16-linepos' accounts for this. -This variable also be set to `eglot-move-to-utf-8-linepos' or +This variable can also be set to `eglot-move-to-utf-8-linepos' or `eglot-move-to-utf-32-linepos' for servers not closely following the spec. Also, since LSP 3.17 server and client may agree on an encoding and Eglot will set this variable automatically.") (defun eglot-move-to-utf-8-linepos (n) - "Move to line's Nth code unit as computed by LSP's UTF-8 criterion." + "Move to line's Nth byte as computed by LSP's UTF-8 criterion." (let* ((bol (line-beginning-position)) (goal-byte (+ (position-bytes bol) n)) (eol (line-end-position))) @@ -1529,7 +1529,7 @@ eglot-move-to-utf-16-linepos (forward-char 1)))) (defun eglot-move-to-utf-32-linepos (n) - "Move to line's Nth code unit as computed by LSP's UTF-32 criterion." + "Move to line's Nth codepoint as computed by LSP's UTF-32 criterion." ;; We cannot use `move-to-column' here, because it moves to *visual* ;; columns, which can be different from LSP characters in case of ;; `whitespace-mode', `prettify-symbols-mode', etc. (github#296, commit ca79b138d424766e3bfe8e4ca5d2b315b9ea4408 Author: João Távora Date: Sun Feb 26 12:50:42 2023 +0000 Eglot: rename and redocument encoding-related functions (bug#61726) * lisp/progmodes/eglot.el (eglot-current-column): Obsolete. (eglot-lsp-abiding-column): Obsolete. (eglot-current-column-function): Obsolete. (eglot-current-linepos-function): Rename from eglot-current-column-function. (eglot-utf-8-linepos): Rename from eglot-bytewise-column. (eglot-utf-16-linepos): Rename from eglot-lsp-abiding-column. (eglot-utf-32-linepos): Rename from eglot-current-column. (eglot-move-to-current-column): Obsolete. (eglot-move-to-lsp-abiding-column): Obsolete. (eglot-move-to-column-function): Obsolete. (eglot-move-to-linepos-function): Rename from eglot-move-to-column-function. (eglot-move-to-utf-8-linepos): Rename from eglot-move-to-bytewise-column. (eglot-move-to-utf-16-linepos): Rename from eglot-move-to-lsp-abiding-column. (eglot-move-to-utf-32-linepos): Rename from eglot-move-to-current-column. (eglot--managed-mode): Adjust. (eglot-client-capabilities): Trim whitespace. * test/lisp/progmodes/eglot-tests.el (eglot-test-lsp-abiding-column) (eglot-test-lsp-abiding-column-1): Use new function/variable names. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 9e5dd268a94..e63dd563c44 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -816,9 +816,7 @@ eglot-client-capabilities `(:valueSet [,@(mapcar #'car eglot--tag-faces)]))) - :general - (list - :positionEncodings ["utf-32" "utf-8" "utf-16"]) + :general (list :positionEncodings ["utf-32" "utf-8" "utf-16"]) :experimental eglot--{}))) (cl-defgeneric eglot-workspace-folders (server) @@ -1442,25 +1440,31 @@ eglot--warn (let ((warning-minimum-level :error)) (display-warning 'eglot (apply #'format format args) :warning))) -(defun eglot-current-column () (- (point) (line-beginning-position))) + +;;; Encoding fever +;;; +(define-obsolete-function-alias + 'eglot-lsp-abiding-column 'eglot-utf-16-linepos "29.1") +(define-obsolete-function-alias + 'eglot-current-column 'eglot-utf-32-linepos "29.1") +(define-obsolete-variable-alias + 'eglot-current-column-function 'eglot-current-linepos-function "29.1") + +(defvar eglot-current-linepos-function #'eglot-utf-16-linepos + "Function calculating number of code units to line beginning. + +This is the inverse operation of +`eglot-move-to-linepos-function' (which see). It is a function of +no arguments returning the number of code units corresponding to +the current position of point relative to line beginning.") -(defun eglot-bytewise-column () - "Calculate current column using the LSP `utf-8' criterion." +(defun eglot-utf-8-linepos () + "Calculate number of code units to line beginning using UTF-8." (length (encode-coding-region (line-beginning-position) (point) 'utf-8-unix t))) -(defvar eglot-current-column-function #'eglot-lsp-abiding-column - "Function to calculate the current column. - -This is the inverse operation of -`eglot-move-to-column-function' (which see). It is a function of -no arguments returning a column number. For buffers managed by -fully LSP-compliant servers, this should be set to -`eglot-lsp-abiding-column' (the default), and -`eglot-current-column' for all others.") - -(defun eglot-lsp-abiding-column (&optional lbp) - "Calculate current COLUMN as defined by the LSP spec. +(defun eglot-utf-16-linepos (&optional lbp) + "Calculate number of code units to line beginning using UTF-16. LBP defaults to `line-beginning-position'." (/ (- (length (encode-coding-region (or lbp (line-beginning-position)) ;; Fix github#860 @@ -1468,60 +1472,71 @@ eglot-lsp-abiding-column 2) 2)) +(defun eglot-utf-32-linepos () + "Calculate number of code units to line beginning using UTF-32." + (- (point) (line-beginning-position))) + (defun eglot--pos-to-lsp-position (&optional pos) "Convert point POS to LSP position." (eglot--widening ;; LSP line is zero-origin; emacs is one-origin. (list :line (1- (line-number-at-pos pos t)) :character (progn (when pos (goto-char pos)) - (funcall eglot-current-column-function))))) + (funcall eglot-current-linepos-function))))) -(defvar eglot-move-to-column-function #'eglot-move-to-lsp-abiding-column - "Function to move to a column reported by the LSP server. +(define-obsolete-function-alias + 'eglot-move-to-current-column 'eglot-move-to-utf-32-linepos "29.1") +(define-obsolete-function-alias + 'eglot-move-to-lsp-abiding-column 'eglot-move-to-utf-16-linepos "29.1") +(define-obsolete-variable-alias +'eglot-move-to-column-function 'eglot-move-to-linepos-function "29.1") -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'. However, many servers don't follow the spec this closely. +(defvar eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos + "Function to move to a column reported by the LSP server. -For buffers managed by fully LSP-compliant servers, this should -be set to `eglot-move-to-lsp-abiding-column' (the default), and -`eglot-move-to-column' for all others.") +Per the LSP spec, character offsets in LSP Position objects count +UTF-16 code units, not actual code points. So when LSP says +position 3 of a line containing just \"aXbc\", where X is a funny +looking character in the UTF-16 \"supplementary plane\", it +actually means `b', not `c'. The default value +`eglot-move-to-utf-16-linepos' accounts for this. -(defun eglot-move-to-column (column) - "Move to COLUMN without closely following the LSP spec." - ;; We cannot use `move-to-column' here, because it moves to *visual* - ;; columns, which can be different from LSP columns in case of - ;; `whitespace-mode', `prettify-symbols-mode', etc. (github#296, - ;; github#297) - (goto-char (min (+ (line-beginning-position) column) - (line-end-position)))) +This variable also be set to `eglot-move-to-utf-8-linepos' or +`eglot-move-to-utf-32-linepos' for servers not closely following +the spec. Also, since LSP 3.17 server and client may agree on an +encoding and Eglot will set this variable automatically.") -(defun eglot-move-to-lsp-abiding-column (column) - "Move to COLUMN as computed by LSP's UTF-16 criterion." +(defun eglot-move-to-utf-8-linepos (n) + "Move to line's Nth code unit as computed by LSP's UTF-8 criterion." (let* ((bol (line-beginning-position)) - (goal-char (+ bol column)) + (goal-byte (+ (position-bytes bol) n)) (eol (line-end-position))) (goto-char bol) - (while (and (< (point) goal-char) - (< (point) eol)) - (if (<= #x010000 (char-after) #x10ffff) - (setq goal-char (1- goal-char))) + (while (and (< (position-bytes (point)) goal-byte) (< (point) eol)) + ;; raw bytes take 2 bytes in the buffer + (when (>= (char-after) #x3fff80) (setq goal-byte (1+ goal-byte))) (forward-char 1)))) -(defun eglot-move-to-bytewise-column (column) - "Move to COLUMN as computed using the LSP `utf-8' criterion." +(defun eglot-move-to-utf-16-linepos (n) + "Move to line's Nth code unit as computed by LSP's UTF-16 criterion." (let* ((bol (line-beginning-position)) - (goal-byte (+ (position-bytes bol) column)) - (eol (line-end-position))) + (goal-char (+ bol n)) + (eol (line-end-position))) (goto-char bol) - (while (and (< (position-bytes (point)) goal-byte) - (< (point) eol)) - (if (>= (char-after) #x3fff80) ; raw bytes take 2 bytes in the buffer - (setq goal-byte (1+ goal-byte))) + (while (and (< (point) goal-char) (< (point) eol)) + ;; code points in the "supplementary place" use two code units + (when (<= #x010000 (char-after) #x10ffff) (setq goal-char (1- goal-char))) (forward-char 1)))) +(defun eglot-move-to-utf-32-linepos (n) + "Move to line's Nth code unit as computed by LSP's UTF-32 criterion." + ;; We cannot use `move-to-column' here, because it moves to *visual* + ;; columns, which can be different from LSP characters in case of + ;; `whitespace-mode', `prettify-symbols-mode', etc. (github#296, + ;; github#297) + (goto-char (min (+ (line-beginning-position) n) + (line-end-position)))) + (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" @@ -1532,16 +1547,17 @@ eglot--lsp-position-to-point (forward-line (min most-positive-fixnum (plist-get pos-plist :line))) (unless (eobp) ;; if line was excessive leave point at eob - (let ((tab-width 1) - (col (plist-get pos-plist :character))) + (let ((col (plist-get pos-plist :character))) (unless (wholenump col) (eglot--warn "Caution: LSP server sent invalid character position %s. Using 0 instead." col) (setq col 0)) - (funcall eglot-move-to-column-function col))) + (funcall eglot-move-to-linepos-function col))) (if marker (copy-marker (point-marker)) (point))))) + +;;; More helpers (defconst eglot--uri-path-allowed-chars (let ((vec (copy-sequence url-path-allowed-chars))) (aset vec ?: nil) ;; see github#639 @@ -1778,11 +1794,11 @@ eglot--managed-mode (pcase (plist-get (eglot--capabilities (eglot-current-server)) :positionEncoding) ("utf-32" - (eglot--setq-saving eglot-current-column-function #'eglot-current-column) - (eglot--setq-saving eglot-move-to-column-function #'eglot-move-to-column)) + (eglot--setq-saving eglot-current-linepos-function #'eglot-utf-32-linepos) + (eglot--setq-saving eglot-move-to-linepos-function #'eglot-move-to-utf-32-linepos)) ("utf-8" - (eglot--setq-saving eglot-current-column-function #'eglot-bytewise-column) - (eglot--setq-saving eglot-move-to-column-function #'eglot-move-to-bytewise-column))) + (eglot--setq-saving eglot-current-linepos-function #'eglot-utf-8-linepos) + (eglot--setq-saving eglot-move-to-linepos-function #'eglot-move-to-utf-8-linepos))) (add-hook 'after-change-functions 'eglot--after-change nil t) (add-hook 'before-change-functions 'eglot--before-change nil t) (add-hook 'kill-buffer-hook #'eglot--managed-mode-off nil t) @@ -2616,7 +2632,7 @@ eglot--xref-make-match (add-face-text-property hi-beg hi-end 'xref-match t substring) (list substring (line-number-at-pos (point) t) - (eglot-current-column) (- end beg)))))) + (eglot-utf-32-linepos) (- end beg)))))) (`(,summary ,line ,column ,length) (cond (visiting (with-current-buffer visiting (funcall collect))) diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index 4b6528351b2..5d5de59a19a 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -856,8 +856,8 @@ eglot-tests--lsp-abiding-column-1 '((c-mode . ("clangd"))))) (with-current-buffer (eglot--find-file-noselect "project/foo.c") - (setq-local eglot-move-to-column-function #'eglot-move-to-lsp-abiding-column) - (setq-local eglot-current-column-function #'eglot-lsp-abiding-column) + (setq-local eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos) + (setq-local eglot-current-linepos-function #'eglot-utf-16-linepos) (eglot--sniffing (:client-notifications c-notifs) (eglot--tests-connect) (end-of-line) @@ -866,12 +866,12 @@ eglot-tests--lsp-abiding-column-1 (eglot--wait-for (c-notifs 2) (&key params &allow-other-keys) (should (equal 71 (cadddr (cadadr (aref (cadddr params) 0)))))) (beginning-of-line) - (should (eq eglot-move-to-column-function #'eglot-move-to-lsp-abiding-column)) - (funcall eglot-move-to-column-function 71) + (should (eq eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos)) + (funcall eglot-move-to-linepos-function 71) (should (looking-at "p"))))))) (ert-deftest eglot-test-lsp-abiding-column () - "Test basic `eglot-lsp-abiding-column' and `eglot-move-to-lsp-abiding-column'." + "Test basic LSP character counting logic." (skip-unless (executable-find "clangd")) (eglot-tests--lsp-abiding-column-1)) commit 3e3e6d71be76b80beca946f9dd433d12aa450695 Author: Augusto Stoffel Date: Sun Feb 26 11:47:32 2023 +0000 Eglot: support positionEncoding LSP capability (bug#61726) * lisp/progmodes/eglot.el(eglot-client-capabilities): Announce the new capability. (eglot-bytewise-column, eglot-move-to-bytewise-column): New functions. (eglot--managed-mode): Set 'eglot-current-column-function' and 'eglot-move-to-bytewise-column' appropriately. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index b52b4975a89..9e5dd268a94 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -816,6 +816,9 @@ eglot-client-capabilities `(:valueSet [,@(mapcar #'car eglot--tag-faces)]))) + :general + (list + :positionEncodings ["utf-32" "utf-8" "utf-16"]) :experimental eglot--{}))) (cl-defgeneric eglot-workspace-folders (server) @@ -1441,6 +1444,11 @@ eglot--warn (defun eglot-current-column () (- (point) (line-beginning-position))) +(defun eglot-bytewise-column () + "Calculate current column using the LSP `utf-8' criterion." + (length (encode-coding-region (line-beginning-position) (point) + 'utf-8-unix t))) + (defvar eglot-current-column-function #'eglot-lsp-abiding-column "Function to calculate the current column. @@ -1502,6 +1510,18 @@ eglot-move-to-lsp-abiding-column (setq goal-char (1- goal-char))) (forward-char 1)))) +(defun eglot-move-to-bytewise-column (column) + "Move to COLUMN as computed using the LSP `utf-8' criterion." + (let* ((bol (line-beginning-position)) + (goal-byte (+ (position-bytes bol) column)) + (eol (line-end-position))) + (goto-char bol) + (while (and (< (position-bytes (point)) goal-byte) + (< (point) eol)) + (if (>= (char-after) #x3fff80) ; raw bytes take 2 bytes in the buffer + (setq goal-byte (1+ goal-byte))) + (forward-char 1)))) + (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" @@ -1755,6 +1775,14 @@ eglot--managed-mode :init-value nil :lighter nil :keymap eglot-mode-map (cond (eglot--managed-mode + (pcase (plist-get (eglot--capabilities (eglot-current-server)) + :positionEncoding) + ("utf-32" + (eglot--setq-saving eglot-current-column-function #'eglot-current-column) + (eglot--setq-saving eglot-move-to-column-function #'eglot-move-to-column)) + ("utf-8" + (eglot--setq-saving eglot-current-column-function #'eglot-bytewise-column) + (eglot--setq-saving eglot-move-to-column-function #'eglot-move-to-bytewise-column))) (add-hook 'after-change-functions 'eglot--after-change nil t) (add-hook 'before-change-functions 'eglot--before-change nil t) (add-hook 'kill-buffer-hook #'eglot--managed-mode-off nil t) commit b0e87e930e86d2e48659aed9cf85099c1fd2795c Author: Eli Zaretskii Date: Sun Feb 26 10:27:18 2023 +0000 Eglot: use faster strategy for moving to LSP positions (bug#61726) Turns out we don't need encode-coding-region after all. * lisp/progmodes/eglot.el (eglot-move-to-lsp-abiding-column): Rewrite. Co-authored-by: Augusto Stoffel diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 2b9d44f84e6..b52b4975a89 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1491,19 +1491,16 @@ eglot-move-to-column (line-end-position)))) (defun eglot-move-to-lsp-abiding-column (column) - "Move to COLUMN abiding by the LSP spec." - (save-restriction - (cl-loop - with lbp = (line-beginning-position) - initially - (narrow-to-region lbp (line-end-position)) - (move-to-column column) - for diff = (- column - (eglot-lsp-abiding-column lbp)) - until (zerop diff) - do (condition-case eob-err - (forward-char (/ (if (> diff 0) (1+ diff) (1- diff)) 2)) - (end-of-buffer (cl-return eob-err)))))) + "Move to COLUMN as computed by LSP's UTF-16 criterion." + (let* ((bol (line-beginning-position)) + (goal-char (+ bol column)) + (eol (line-end-position))) + (goto-char bol) + (while (and (< (point) goal-char) + (< (point) eol)) + (if (<= #x010000 (char-after) #x10ffff) + (setq goal-char (1- goal-char))) + (forward-char 1)))) (defun eglot--lsp-position-to-point (pos-plist &optional marker) "Convert LSP position POS-PLIST to Emacs point. commit 5b174b96834af7153f2aec1ac10d91caa0d03e52 Author: Eli Zaretskii Date: Sun Feb 26 11:46:20 2023 +0200 Fix mule-tests in UTF-8 locales * test/lisp/international/mule-tests.el (sgml-html-meta-no-post-less-than-10lines): Fix test condition. diff --git a/test/lisp/international/mule-tests.el b/test/lisp/international/mule-tests.el index 6b76e35ae22..3e0c5bf9f4b 100644 --- a/test/lisp/international/mule-tests.el +++ b/test/lisp/international/mule-tests.el @@ -119,9 +119,10 @@ sgml-html-meta-no-pre (ert-deftest sgml-html-meta-no-post-less-than-10lines () "No '', detect charset in the first 10 lines." (let ((sgml-html-meta-post "")) - (should (eq 'utf-8 (sgml-html-meta-run - (concat "\n\n\n\n\n\n\n\n\n" - "")))))) + (should (eq 'utf-8 (coding-system-base + (sgml-html-meta-run + (concat "\n\n\n\n\n\n\n\n\n" + ""))))))) (ert-deftest sgml-html-meta-no-post-10lines () "No '', do not detect charset after the first 10 lines." commit 5256392a7ec0639ba4ba684d608bb3aaf976c985 Author: Eli Zaretskii Date: Sun Feb 26 11:34:14 2023 +0200 Fix 'vertical-motion' when display strings are around * src/indent.c (Fvertical_motion): Correct bidi-related condition for character position, when we didn't move vertically. (Bug#61636) diff --git a/src/indent.c b/src/indent.c index 6de18d749ca..08d2bf5ea28 100644 --- a/src/indent.c +++ b/src/indent.c @@ -2401,7 +2401,7 @@ DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 3, 0, last line that it occupies. */ if (it_start < ZV) { - if ((it.bidi_it.scan_dir > 0) + if ((it.bidi_it.scan_dir >= 0 || it.vpos == vpos_init) ? IT_CHARPOS (it) < it_start : IT_CHARPOS (it) > it_start) { commit 0db88d625a7ca1468af51ef0a04c22577500e059 Author: Yuan Fu Date: Sun Feb 26 01:12:18 2023 -0800 ; * src/treesit.c (treesit_predicate_match): Fix typo. diff --git a/src/treesit.c b/src/treesit.c index ef0f2407840..5a4fe3e8803 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -2484,7 +2484,7 @@ treesit_predicate_match (Lisp_Object args, struct capture_range captures) { if (XFIXNUM (Flength (args)) != 2) xsignal2 (Qtreesit_query_error, - build_string ("Predicate `equal' requires two " + build_string ("Predicate `match' requires two " "arguments but only given"), Flength (args));