Merge remote-tracking branch 'refs/remotes/origin/master'
This commit is contained in:
commit
9e05453a8c
31 changed files with 491 additions and 287 deletions
|
@ -99,6 +99,7 @@ This manual documents how to configure, use, and customize Eglot.
|
|||
* Using Eglot:: Important Eglot commands and variables.
|
||||
* Customizing Eglot:: Eglot customization and advanced features.
|
||||
* Advanced server configuration:: Fine-tune a specific language server
|
||||
* Extending Eglot:: Writing Eglot extensions in Elisp
|
||||
* Troubleshooting Eglot:: Troubleshooting and reporting bugs.
|
||||
* GNU Free Documentation License:: The license for this manual.
|
||||
* Index::
|
||||
|
@ -1264,6 +1265,154 @@ is serialized by Eglot to the following JSON text:
|
|||
@}
|
||||
@end example
|
||||
|
||||
@node Extending Eglot
|
||||
@chapter Extending Eglot
|
||||
|
||||
Sometimes it may be useful to extend existing Eglot functionality
|
||||
using Elisp its public methods. A good example of when this need may
|
||||
arise is adding support for a custom LSP protocol extension only
|
||||
implemented by a specific server.
|
||||
|
||||
The best source of documentation for this is probably Eglot source
|
||||
code itself, particularly the section marked ``API''.
|
||||
|
||||
Most of the functionality is implemented with Common-Lisp style
|
||||
generic functions (@pxref{Generics,,,eieio,EIEIO}) that can be easily
|
||||
extended or overridden. The Eglot code itself is an example on how to
|
||||
do this.
|
||||
|
||||
The following is a relatively simple example that adds support for the
|
||||
@code{inactiveRegions} experimental feature introduced in version 17
|
||||
of the @command{clangd} C/C++ language server++.
|
||||
|
||||
Summarily, the feature works by first having the server detect the
|
||||
Eglot's advertisement of the @code{inactiveRegions} client capability
|
||||
during startup, whereupon the language server will report a list of
|
||||
regions of inactive code for each buffer. This is usually code
|
||||
surrounded by C/C++ @code{#ifdef} macros that the preprocessor removes
|
||||
based on compile-time information.
|
||||
|
||||
The language server reports the regions by periodically sending a
|
||||
@code{textDocument/inactiveRegions} notification for each managed
|
||||
buffer (@pxref{Eglot and Buffers}). Normally, unknown server
|
||||
notifications are ignored by Eglot, but we're going change that.
|
||||
|
||||
Both the announcement of the client capability and the handling of the
|
||||
new notification is done by adding methods to generic functions.
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
The first method extends @code{eglot-client-capabilities} using a
|
||||
simple heuristic to detect if current server is @command{clangd} and
|
||||
enables the @code{inactiveRegion} capability.
|
||||
|
||||
@lisp
|
||||
(cl-defmethod eglot-client-capabilities :around (server)
|
||||
(let ((base (cl-call-next-method)))
|
||||
(when (cl-find "clangd" (process-command
|
||||
(jsonrpc--process server))
|
||||
:test #'string-match)
|
||||
(setf (cl-getf (cl-getf base :textDocument)
|
||||
:inactiveRegionsCapabilities)
|
||||
'(:inactiveRegions t)))
|
||||
base))
|
||||
@end lisp
|
||||
|
||||
Notice we use an internal function of the @code{jsonrpc.el} library,
|
||||
and a regexp search to detect @command{clangd}. An alternative would
|
||||
be to define a new EIEIO subclass of @code{eglot-lsp-server}, maybe
|
||||
called @code{eglot-clangd}, so that the method would be simplified:
|
||||
|
||||
@lisp
|
||||
(cl-defmethod eglot-client-capabilities :around ((_s eglot-clangd))
|
||||
(let ((base (cl-call-next-method)))
|
||||
(setf (cl-getf (cl-getf base :textDocument)
|
||||
:inactiveRegionsCapabilities)
|
||||
'(:inactiveRegions t))))
|
||||
@end lisp
|
||||
|
||||
However, this would require that users tweak
|
||||
@code{eglot-server-program} to tell Eglot instantiate such sub-classes
|
||||
instead of the generic @code{eglot-lsp-server} (@pxref{Setting Up LSP
|
||||
Servers}). For the purposes of this particular demonstration, we're
|
||||
going to use the more hacky regexp route which doesn't require that.
|
||||
|
||||
Note, however, that detecting server versions before announcing new
|
||||
capabilities is generally not needed, as both server and client are
|
||||
required by LSP to ignore unknown capabilities advertised by their
|
||||
counterparts.
|
||||
|
||||
@item
|
||||
The second method implements @code{eglot-handle-notification} to
|
||||
process the server notification for the LSP method
|
||||
@code{textDocument/inactiveRegions}. For each region received it
|
||||
creates an overlay applying the @code{shadow} face to the region.
|
||||
Overlays are recreated every time a new notification of this kind is
|
||||
received.
|
||||
|
||||
To learn about how @command{clangd}'s special JSONRPC notification
|
||||
message is structured in detail you could consult that server's
|
||||
documentation. Another possibility is to evaluate the first
|
||||
capability-announcing method, reconnect to the server and peek in the
|
||||
events buffer (@pxref{Eglot Commands, eglot-events-buffer}). You
|
||||
could find something like:
|
||||
|
||||
@lisp
|
||||
[server-notification] Mon Sep 4 01:10:04 2023:
|
||||
(:jsonrpc "2.0" :method "textDocument/inactiveRegions" :params
|
||||
(:textDocument
|
||||
(:uri "file:///path/to/file.cpp")
|
||||
:regions
|
||||
[(:start (:character 0 :line 18)
|
||||
:end (:character 58 :line 19))
|
||||
(:start (:character 0 :line 36)
|
||||
:end (:character 1 :line 38))]))
|
||||
@end lisp
|
||||
|
||||
This reveals that the @code{textDocument/inactiveRegions} notification
|
||||
contains a @code{:textDocument} property to designate the managed
|
||||
buffer and an array of LSP regions under the @code{:regions} property.
|
||||
Notice how the message (originally in JSON format), is represented as
|
||||
Elisp plists (@pxref{JSONRPC objects in Elisp}).
|
||||
|
||||
The Eglot generic function machinery will automatically destructure
|
||||
the incoming message, so these two properties can simply be added to
|
||||
the new method's lambda list as @code{&key} arguments. Also, the
|
||||
@code{eglot-uri-to-path} and@code{eglot-range-region} may be used to
|
||||
easily parse the LSP @code{:uri} and @code{:start ... :end ...}
|
||||
objects to obtain Emacs objects for file names and positions.
|
||||
|
||||
The remainder of the implementation consists of standard Elisp
|
||||
techniques to loop over arrays, manage buffers and overlays.
|
||||
|
||||
@lisp
|
||||
(defvar-local eglot-clangd-inactive-region-overlays '())
|
||||
|
||||
(cl-defmethod eglot-handle-notification
|
||||
(_server (_method (eql textDocument/inactiveRegions))
|
||||
&key regions textDocument &allow-other-keys)
|
||||
(if-let* ((path (expand-file-name (eglot-uri-to-path
|
||||
(cl-getf textDocument :uri))))
|
||||
(buffer (find-buffer-visiting path)))
|
||||
(with-current-buffer buffer
|
||||
(mapc #'delete-overlay eglot-clangd-inactive-region-overlays)
|
||||
(cl-loop
|
||||
for r across regions
|
||||
for (beg . end) = (eglot-range-region r)
|
||||
for ov = (make-overlay beg end)
|
||||
do
|
||||
(overlay-put ov 'face 'shadow)
|
||||
(push ov eglot-clangd-inactive-region-overlays)))))
|
||||
@end lisp
|
||||
|
||||
@end itemize
|
||||
|
||||
After evaluating these two additions and reconnecting to the
|
||||
@command{clangd} language server (version 17), the result will be that
|
||||
all the inactive code in the buffer will be nicely grayed out using
|
||||
the LSP server knowledge about current compile time preprocessor
|
||||
defines.
|
||||
|
||||
@node Troubleshooting Eglot
|
||||
@chapter Troubleshooting Eglot
|
||||
@cindex troubleshooting Eglot
|
||||
|
|
|
@ -658,11 +658,30 @@ versions, specific architectures, etc.:
|
|||
@cindex skipping tests
|
||||
@cindex test preconditions
|
||||
@cindex preconditions of a test
|
||||
@findex skip-when
|
||||
@findex skip-unless
|
||||
Sometimes, it doesn't make sense to run a test due to missing
|
||||
preconditions. A required Emacs feature might not be compiled in, the
|
||||
function to be tested could call an external binary which might not be
|
||||
available on the test machine, you name it. In this case, the macro
|
||||
@code{skip-unless} could be used to skip the test:
|
||||
available on the test machine, you name it. In this case, the macros
|
||||
@code{skip-when} or @code{skip-unless} could be used to skip the
|
||||
test.@footnote{The @code{skip-when} macro was added in Emacs 30.1. If
|
||||
you need your tests to be compatible with older versions of Emacs, use
|
||||
@code{skip-unless} instead.}
|
||||
|
||||
@noindent
|
||||
For example, this test is skipped on MS-Windows and macOS:
|
||||
|
||||
@lisp
|
||||
(ert-deftest test-gnu-linux ()
|
||||
"A test that is not relevant on MS-Windows and macOS."
|
||||
(skip-when (memq system-type '(windows-nt ns))
|
||||
...))
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
This test is skipped if the feature @samp{dbusbind} is not present in
|
||||
the running Emacs:
|
||||
|
||||
@lisp
|
||||
(ert-deftest test-dbus ()
|
||||
|
|
7
etc/NEWS
7
etc/NEWS
|
@ -724,6 +724,13 @@ without specifying a file, like this:
|
|||
*** New user option 'image-dired-thumb-naming'.
|
||||
You can now configure how a thumbnail is named using this option.
|
||||
|
||||
** ERT
|
||||
|
||||
*** New macro `skip-when' to skip 'ert-deftest' tests.
|
||||
This can help avoid some awkward skip conditions. For example
|
||||
'(skip-unless (not noninteractive))' can be changed to the easier
|
||||
to read '(skip-when noninteractive)'.
|
||||
|
||||
** checkdoc
|
||||
|
||||
---
|
||||
|
|
|
@ -34,17 +34,18 @@
|
|||
;; `ert-run-tests-batch-and-exit' for non-interactive use.
|
||||
;;
|
||||
;; The body of `ert-deftest' forms resembles a function body, but the
|
||||
;; additional operators `should', `should-not', `should-error' and
|
||||
;; `skip-unless' are available. `should' is similar to cl's `assert',
|
||||
;; but signals a different error when its condition is violated that
|
||||
;; is caught and processed by ERT. In addition, it analyzes its
|
||||
;; argument form and records information that helps debugging
|
||||
;; (`cl-assert' tries to do something similar when its second argument
|
||||
;; SHOW-ARGS is true, but `should' is more sophisticated). For
|
||||
;; information on `should-not' and `should-error', see their
|
||||
;; docstrings. `skip-unless' skips the test immediately without
|
||||
;; processing further, this is useful for checking the test
|
||||
;; environment (like availability of features, external binaries, etc).
|
||||
;; additional operators `should', `should-not', `should-error',
|
||||
;; `skip-when' and `skip-unless' are available. `should' is similar
|
||||
;; to cl's `assert', but signals a different error when its condition
|
||||
;; is violated that is caught and processed by ERT. In addition, it
|
||||
;; analyzes its argument form and records information that helps
|
||||
;; debugging (`cl-assert' tries to do something similar when its
|
||||
;; second argument SHOW-ARGS is true, but `should' is more
|
||||
;; sophisticated). For information on `should-not' and
|
||||
;; `should-error', see their docstrings. The `skip-when' and
|
||||
;; `skip-unless' forms skip the test immediately, which is useful for
|
||||
;; checking the test environment (like availability of features,
|
||||
;; external binaries, etc).
|
||||
;;
|
||||
;; See ERT's Info manual `(ert) Top' as well as the docstrings for
|
||||
;; more details. To see some examples of tests written in ERT, see
|
||||
|
@ -194,8 +195,8 @@ and the body."
|
|||
BODY is evaluated as a `progn' when the test is run. It should
|
||||
signal a condition on failure or just return if the test passes.
|
||||
|
||||
`should', `should-not', `should-error' and `skip-unless' are
|
||||
useful for assertions in BODY.
|
||||
`should', `should-not', `should-error', `skip-when', and
|
||||
`skip-unless' are useful for assertions in BODY.
|
||||
|
||||
Use `ert' to run tests interactively.
|
||||
|
||||
|
@ -227,7 +228,8 @@ in batch mode, an error is signaled.
|
|||
(tags nil tags-supplied-p))
|
||||
body)
|
||||
(ert--parse-keys-and-body docstring-keys-and-body)
|
||||
`(cl-macrolet ((skip-unless (form) `(ert--skip-unless ,form)))
|
||||
`(cl-macrolet ((skip-when (form) `(ert--skip-when ,form))
|
||||
(skip-unless (form) `(ert--skip-unless ,form)))
|
||||
(ert-set-test ',name
|
||||
(make-ert-test
|
||||
:name ',name
|
||||
|
@ -464,6 +466,15 @@ failed."
|
|||
(list
|
||||
:fail-reason "did not signal an error")))))))))
|
||||
|
||||
(cl-defmacro ert--skip-when (form)
|
||||
"Evaluate FORM. If it returns t, skip the current test.
|
||||
Errors during evaluation are caught and handled like t."
|
||||
(declare (debug t))
|
||||
(ert--expand-should `(skip-when ,form) form
|
||||
(lambda (inner-form form-description-form _value-var)
|
||||
`(when (condition-case nil ,inner-form (t t))
|
||||
(ert-skip ,form-description-form)))))
|
||||
|
||||
(cl-defmacro ert--skip-unless (form)
|
||||
"Evaluate FORM. If it returns nil, skip the current test.
|
||||
Errors during evaluation are caught and handled like nil."
|
||||
|
|
|
@ -2617,11 +2617,12 @@ flags that control whether to collect or render objects."
|
|||
columns))
|
||||
|
||||
(defun shr-count (dom elem)
|
||||
;; This is faster than `seq-count', and shr can use it.
|
||||
(let ((i 0))
|
||||
(dolist (sub (dom-children dom))
|
||||
(when (and (not (stringp sub))
|
||||
(eq (dom-tag sub) elem))
|
||||
(setq i (1+ i))))
|
||||
(eq (dom-tag sub) elem))
|
||||
(setq i (1+ i))))
|
||||
i))
|
||||
|
||||
(defun shr-max-columns (dom)
|
||||
|
|
|
@ -131,6 +131,35 @@
|
|||
(defvar tramp-ssh-controlmaster-options)
|
||||
(defvar tramp-use-ssh-controlmaster-options)
|
||||
|
||||
|
||||
;;; Obsolete aliases
|
||||
;;;
|
||||
(make-obsolete-variable 'eglot--managed-mode-hook
|
||||
'eglot-managed-mode-hook "1.6")
|
||||
(define-obsolete-variable-alias 'eglot-confirm-server-initiated-edits
|
||||
'eglot-confirm-server-edits "1.16")
|
||||
(define-obsolete-function-alias 'eglot--uri-to-path 'eglot-uri-to-path "1.16")
|
||||
(define-obsolete-function-alias 'eglot--path-to-uri 'eglot-path-to-uri "1.16")
|
||||
(define-obsolete-function-alias 'eglot--range-region 'eglot-range-region "1.16")
|
||||
(define-obsolete-function-alias 'eglot--server-capable 'eglot-server-capable "1.16")
|
||||
(define-obsolete-function-alias 'eglot--server-capable-or-lose 'eglot-server-capable-or-lose "1.16")
|
||||
(define-obsolete-function-alias
|
||||
'eglot-lsp-abiding-column 'eglot-utf-16-linepos "1.12")
|
||||
(define-obsolete-function-alias
|
||||
'eglot-current-column 'eglot-utf-32-linepos "1.12")
|
||||
(define-obsolete-variable-alias
|
||||
'eglot-current-column-function 'eglot-current-linepos-function "1.12")
|
||||
(define-obsolete-function-alias
|
||||
'eglot-move-to-current-column 'eglot-move-to-utf-32-linepos "1.12")
|
||||
(define-obsolete-function-alias
|
||||
'eglot-move-to-lsp-abiding-column 'eglot-move-to-utf-16-linepos "1.12")
|
||||
(define-obsolete-variable-alias
|
||||
'eglot-move-to-column-function 'eglot-move-to-linepos-function "1.12")
|
||||
(define-obsolete-variable-alias 'eglot-ignored-server-capabilites
|
||||
'eglot-ignored-server-capabilities "1.8")
|
||||
;;;###autoload
|
||||
(define-obsolete-function-alias 'eglot-update 'eglot-upgrade-eglot "29.1")
|
||||
|
||||
|
||||
;;; User tweakable stuff
|
||||
(defgroup eglot nil
|
||||
|
@ -391,9 +420,6 @@ done by `eglot-reconnect'."
|
|||
:type '(choice (const :tag "No limit" nil)
|
||||
(integer :tag "Number of characters")))
|
||||
|
||||
(define-obsolete-variable-alias 'eglot-confirm-server-initiated-edits
|
||||
'eglot-confirm-server-edits "1.16")
|
||||
|
||||
(defcustom eglot-confirm-server-edits '((eglot-rename . nil)
|
||||
(t . maybe-summary))
|
||||
"Control if changes proposed by LSP should be confirmed with user.
|
||||
|
@ -444,6 +470,36 @@ mode line indicator."
|
|||
:type 'boolean
|
||||
:version "1.10")
|
||||
|
||||
(defcustom eglot-ignored-server-capabilities (list)
|
||||
"LSP server capabilities that Eglot could use, but won't.
|
||||
You could add, for instance, the symbol
|
||||
`:documentHighlightProvider' to prevent automatic highlighting
|
||||
under cursor."
|
||||
:type '(set
|
||||
:tag "Tick the ones you're not interested in"
|
||||
(const :tag "Documentation on hover" :hoverProvider)
|
||||
(const :tag "Code completion" :completionProvider)
|
||||
(const :tag "Function signature help" :signatureHelpProvider)
|
||||
(const :tag "Go to definition" :definitionProvider)
|
||||
(const :tag "Go to type definition" :typeDefinitionProvider)
|
||||
(const :tag "Go to implementation" :implementationProvider)
|
||||
(const :tag "Go to declaration" :declarationProvider)
|
||||
(const :tag "Find references" :referencesProvider)
|
||||
(const :tag "Highlight symbols automatically" :documentHighlightProvider)
|
||||
(const :tag "List symbols in buffer" :documentSymbolProvider)
|
||||
(const :tag "List symbols in workspace" :workspaceSymbolProvider)
|
||||
(const :tag "Execute code actions" :codeActionProvider)
|
||||
(const :tag "Code lens" :codeLensProvider)
|
||||
(const :tag "Format buffer" :documentFormattingProvider)
|
||||
(const :tag "Format portion of buffer" :documentRangeFormattingProvider)
|
||||
(const :tag "On-type formatting" :documentOnTypeFormattingProvider)
|
||||
(const :tag "Rename symbol" :renameProvider)
|
||||
(const :tag "Highlight links in document" :documentLinkProvider)
|
||||
(const :tag "Decorate color references" :colorProvider)
|
||||
(const :tag "Fold regions of buffer" :foldingRangeProvider)
|
||||
(const :tag "Execute custom commands" :executeCommandProvider)
|
||||
(const :tag "Inlay hints" :inlayHintProvider)))
|
||||
|
||||
(defvar eglot-withhold-process-id nil
|
||||
"If non-nil, Eglot will not send the Emacs process id to the language server.
|
||||
This can be useful when using docker to run a language server.")
|
||||
|
@ -488,6 +544,7 @@ It is nil if Eglot is not byte-complied.")
|
|||
(2 . eglot-diagnostic-tag-deprecated-face)))
|
||||
|
||||
(defvaralias 'eglot-{} 'eglot--{})
|
||||
|
||||
(defconst eglot--{} (make-hash-table :size 1) "The empty JSON object.")
|
||||
|
||||
(defun eglot--executable-find (command &optional remote)
|
||||
|
@ -499,6 +556,12 @@ It is nil if Eglot is not byte-complied.")
|
|||
(if (and (not eglot-prefer-plaintext) (fboundp 'gfm-view-mode))
|
||||
["markdown" "plaintext"] ["plaintext"]))
|
||||
|
||||
(defconst eglot--uri-path-allowed-chars
|
||||
(let ((vec (copy-sequence url-path-allowed-chars)))
|
||||
(aset vec ?: nil) ;; see github#639
|
||||
vec)
|
||||
"Like `url-path-allows-chars' but more restrictive.")
|
||||
|
||||
|
||||
;;; Message verification helpers
|
||||
;;;
|
||||
|
@ -692,7 +755,6 @@ Honor `eglot-strict-mode'."
|
|||
(cl-destructuring-bind (&key ,@vars &allow-other-keys) ,object-once
|
||||
(funcall ,fn-once ,@vars))))))))
|
||||
|
||||
|
||||
(cl-defmacro eglot--lambda (cl-lambda-list &body body)
|
||||
"Function of args CL-LAMBDA-LIST for processing INTERFACE objects.
|
||||
Honor `eglot-strict-mode'."
|
||||
|
@ -741,9 +803,6 @@ treated as in `eglot--dbind'."
|
|||
,obj-once
|
||||
',(mapcar #'car clauses)))))))
|
||||
|
||||
|
||||
;;; API (WORK-IN-PROGRESS!)
|
||||
;;;
|
||||
(cl-defmacro eglot--when-live-buffer (buf &rest body)
|
||||
"Check BUF live, then do BODY in it." (declare (indent 1) (debug t))
|
||||
(let ((b (cl-gensym)))
|
||||
|
@ -761,6 +820,9 @@ treated as in `eglot--dbind'."
|
|||
"Save excursion and restriction. Widen. Then run BODY." (declare (debug t))
|
||||
`(save-excursion (save-restriction (widen) ,@body)))
|
||||
|
||||
|
||||
;;; Public Elisp API
|
||||
;;;
|
||||
(cl-defgeneric eglot-handle-request (server method &rest params)
|
||||
"Handle SERVER's METHOD request with PARAMS.")
|
||||
|
||||
|
@ -783,7 +845,7 @@ ACTION is an LSP object of either `CodeAction' or `Command' type."
|
|||
(((Command)) (eglot--request server :workspace/executeCommand action))
|
||||
(((CodeAction) edit command data)
|
||||
(if (and (null edit) (null command) data
|
||||
(eglot--server-capable :codeActionProvider :resolveProvider))
|
||||
(eglot-server-capable :codeActionProvider :resolveProvider))
|
||||
(eglot-execute server (eglot--request server :codeAction/resolve action))
|
||||
(when edit (eglot--apply-workspace-edit edit this-command))
|
||||
(when command (eglot--request server :workspace/executeCommand command)))))))
|
||||
|
@ -903,7 +965,7 @@ ACTION is an LSP object of either `CodeAction' or `Command' type."
|
|||
(let ((project (eglot--project server)))
|
||||
(vconcat
|
||||
(mapcar (lambda (dir)
|
||||
(list :uri (eglot--path-to-uri dir)
|
||||
(list :uri (eglot-path-to-uri dir)
|
||||
:name (abbreviate-file-name dir)))
|
||||
`(,(project-root project) ,@(project-external-roots project))))))
|
||||
|
||||
|
@ -949,6 +1011,81 @@ ACTION is an LSP object of either `CodeAction' or `Command' type."
|
|||
:documentation
|
||||
"Represents a server. Wraps a process for LSP communication.")
|
||||
|
||||
(declare-function w32-long-file-name "w32proc.c" (fn))
|
||||
(defun eglot-uri-to-path (uri)
|
||||
"Convert URI to file path, helped by `eglot--current-server'."
|
||||
(when (keywordp uri) (setq uri (substring (symbol-name uri) 1)))
|
||||
(let* ((server (eglot-current-server))
|
||||
(remote-prefix (and server (eglot--trampish-p server)))
|
||||
(url (url-generic-parse-url uri)))
|
||||
;; Only parse file:// URIs, leave other URI untouched as
|
||||
;; `file-name-handler-alist' should know how to handle them
|
||||
;; (bug#58790).
|
||||
(if (string= "file" (url-type url))
|
||||
(let* ((retval (url-unhex-string (url-filename url)))
|
||||
;; Remove the leading "/" for local MS Windows-style paths.
|
||||
(normalized (if (and (not remote-prefix)
|
||||
(eq system-type 'windows-nt)
|
||||
(cl-plusp (length retval)))
|
||||
(w32-long-file-name (substring retval 1))
|
||||
retval)))
|
||||
(concat remote-prefix normalized))
|
||||
uri)))
|
||||
|
||||
(defun eglot-path-to-uri (path)
|
||||
"Convert PATH, a file name, to LSP URI string and return it."
|
||||
(let ((truepath (file-truename path)))
|
||||
(if (and (url-type (url-generic-parse-url path))
|
||||
;; It might be MS Windows path which includes a drive
|
||||
;; letter that looks like a URL scheme (bug#59338)
|
||||
(not (and (eq system-type 'windows-nt)
|
||||
(file-name-absolute-p truepath))))
|
||||
;; Path is already a URI, so forward it to the LSP server
|
||||
;; untouched. The server should be able to handle it, since
|
||||
;; it provided this URI to clients in the first place.
|
||||
path
|
||||
(concat "file://"
|
||||
;; Add a leading "/" for local MS Windows-style paths.
|
||||
(if (and (eq system-type 'windows-nt)
|
||||
(not (file-remote-p truepath)))
|
||||
"/")
|
||||
(url-hexify-string
|
||||
;; Again watch out for trampy paths.
|
||||
(directory-file-name (file-local-name truepath))
|
||||
eglot--uri-path-allowed-chars)))))
|
||||
|
||||
(defun eglot-range-region (range &optional markers)
|
||||
"Return a cons (BEG . END) of positions representing LSP RANGE.
|
||||
If optional MARKERS, make markers instead."
|
||||
(let* ((st (plist-get range :start))
|
||||
(beg (eglot--lsp-position-to-point st markers))
|
||||
(end (eglot--lsp-position-to-point (plist-get range :end) markers)))
|
||||
(cons beg end)))
|
||||
|
||||
(defun eglot-server-capable (&rest feats)
|
||||
"Determine if current server is capable of FEATS."
|
||||
(unless (cl-some (lambda (feat)
|
||||
(memq feat eglot-ignored-server-capabilities))
|
||||
feats)
|
||||
(cl-loop for caps = (eglot--capabilities (eglot--current-server-or-lose))
|
||||
then (cadr probe)
|
||||
for (feat . more) on feats
|
||||
for probe = (plist-member caps feat)
|
||||
if (not probe) do (cl-return nil)
|
||||
if (eq (cadr probe) :json-false) do (cl-return nil)
|
||||
if (not (listp (cadr probe))) do (cl-return (if more nil (cadr probe)))
|
||||
finally (cl-return (or (cadr probe) t)))))
|
||||
|
||||
(defun eglot-server-capable-or-lose (&rest feats)
|
||||
"Like `eglot-server-capable', but maybe error out."
|
||||
(let ((retval (apply #'eglot-server-capable feats)))
|
||||
(unless retval
|
||||
(eglot--error "Unsupported or ignored LSP capability `%s'"
|
||||
(mapconcat #'symbol-name feats " ")))
|
||||
retval))
|
||||
|
||||
|
||||
;;; Process/server management
|
||||
(defun eglot--major-modes (s) "Major modes server S is responsible for."
|
||||
(mapcar #'car (eglot--languages s)))
|
||||
|
||||
|
@ -958,8 +1095,6 @@ ACTION is an LSP object of either `CodeAction' or `Command' type."
|
|||
(cl-defmethod initialize-instance :before ((_server eglot-lsp-server) &optional args)
|
||||
(cl-remf args :initializationOptions))
|
||||
|
||||
|
||||
;;; Process management
|
||||
(defvar eglot--servers-by-project (make-hash-table :test #'equal)
|
||||
"Keys are projects. Values are lists of processes.")
|
||||
|
||||
|
@ -1401,7 +1536,7 @@ This docstring appeases checkdoc, that's all."
|
|||
;; into `/path/to/baz.py', so LSP groks it.
|
||||
:rootPath (file-local-name
|
||||
(expand-file-name default-directory))
|
||||
:rootUri (eglot--path-to-uri default-directory)
|
||||
:rootUri (eglot-path-to-uri default-directory)
|
||||
:initializationOptions (eglot-initialization-options
|
||||
server)
|
||||
:capabilities (eglot-client-capabilities server)
|
||||
|
@ -1556,13 +1691,6 @@ Unless IMMEDIATE, send pending changes before making request."
|
|||
|
||||
;;; Encoding fever
|
||||
;;;
|
||||
(define-obsolete-function-alias
|
||||
'eglot-lsp-abiding-column 'eglot-utf-16-linepos "1.12")
|
||||
(define-obsolete-function-alias
|
||||
'eglot-current-column 'eglot-utf-32-linepos "1.12")
|
||||
(define-obsolete-variable-alias
|
||||
'eglot-current-column-function 'eglot-current-linepos-function "1.12")
|
||||
|
||||
(defvar eglot-current-linepos-function #'eglot-utf-16-linepos
|
||||
"Function calculating position relative to line beginning.
|
||||
|
||||
|
@ -1601,13 +1729,6 @@ LBP defaults to `eglot--bol'."
|
|||
:character (progn (when pos (goto-char pos))
|
||||
(funcall eglot-current-linepos-function)))))
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'eglot-move-to-current-column 'eglot-move-to-utf-32-linepos "1.12")
|
||||
(define-obsolete-function-alias
|
||||
'eglot-move-to-lsp-abiding-column 'eglot-move-to-utf-16-linepos "1.12")
|
||||
(define-obsolete-variable-alias
|
||||
'eglot-move-to-column-function 'eglot-move-to-linepos-function "1.12")
|
||||
|
||||
(defvar eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos
|
||||
"Function to move to a position within a line reported by the LSP server.
|
||||
|
||||
|
@ -1674,55 +1795,6 @@ If optional MARKER, return a marker instead"
|
|||
|
||||
|
||||
;;; More helpers
|
||||
(defconst eglot--uri-path-allowed-chars
|
||||
(let ((vec (copy-sequence url-path-allowed-chars)))
|
||||
(aset vec ?: nil) ;; see github#639
|
||||
vec)
|
||||
"Like `url-path-allows-chars' but more restrictive.")
|
||||
|
||||
(defun eglot--path-to-uri (path)
|
||||
"URIfy PATH."
|
||||
(let ((truepath (file-truename path)))
|
||||
(if (and (url-type (url-generic-parse-url path))
|
||||
;; It might be MS Windows path which includes a drive
|
||||
;; letter that looks like a URL scheme (bug#59338)
|
||||
(not (and (eq system-type 'windows-nt)
|
||||
(file-name-absolute-p truepath))))
|
||||
;; Path is already a URI, so forward it to the LSP server
|
||||
;; untouched. The server should be able to handle it, since
|
||||
;; it provided this URI to clients in the first place.
|
||||
path
|
||||
(concat "file://"
|
||||
;; Add a leading "/" for local MS Windows-style paths.
|
||||
(if (and (eq system-type 'windows-nt)
|
||||
(not (file-remote-p truepath)))
|
||||
"/")
|
||||
(url-hexify-string
|
||||
;; Again watch out for trampy paths.
|
||||
(directory-file-name (file-local-name truepath))
|
||||
eglot--uri-path-allowed-chars)))))
|
||||
|
||||
(declare-function w32-long-file-name "w32proc.c" (fn))
|
||||
(defun eglot--uri-to-path (uri)
|
||||
"Convert URI to file path, helped by `eglot--current-server'."
|
||||
(when (keywordp uri) (setq uri (substring (symbol-name uri) 1)))
|
||||
(let* ((server (eglot-current-server))
|
||||
(remote-prefix (and server (eglot--trampish-p server)))
|
||||
(url (url-generic-parse-url uri)))
|
||||
;; Only parse file:// URIs, leave other URI untouched as
|
||||
;; `file-name-handler-alist' should know how to handle them
|
||||
;; (bug#58790).
|
||||
(if (string= "file" (url-type url))
|
||||
(let* ((retval (url-unhex-string (url-filename url)))
|
||||
;; Remove the leading "/" for local MS Windows-style paths.
|
||||
(normalized (if (and (not remote-prefix)
|
||||
(eq system-type 'windows-nt)
|
||||
(cl-plusp (length retval)))
|
||||
(w32-long-file-name (substring retval 1))
|
||||
retval)))
|
||||
(concat remote-prefix normalized))
|
||||
uri)))
|
||||
|
||||
(defun eglot--snippet-expansion-fn ()
|
||||
"Compute a function to expand snippets.
|
||||
Doubles as an indicator of snippet support."
|
||||
|
@ -1757,69 +1829,6 @@ Doubles as an indicator of snippet support."
|
|||
(prop-match-end match)))))
|
||||
(string-trim (buffer-string))))))
|
||||
|
||||
(define-obsolete-variable-alias 'eglot-ignored-server-capabilites
|
||||
'eglot-ignored-server-capabilities "1.8")
|
||||
|
||||
(defcustom eglot-ignored-server-capabilities (list)
|
||||
"LSP server capabilities that Eglot could use, but won't.
|
||||
You could add, for instance, the symbol
|
||||
`:documentHighlightProvider' to prevent automatic highlighting
|
||||
under cursor."
|
||||
:type '(set
|
||||
:tag "Tick the ones you're not interested in"
|
||||
(const :tag "Documentation on hover" :hoverProvider)
|
||||
(const :tag "Code completion" :completionProvider)
|
||||
(const :tag "Function signature help" :signatureHelpProvider)
|
||||
(const :tag "Go to definition" :definitionProvider)
|
||||
(const :tag "Go to type definition" :typeDefinitionProvider)
|
||||
(const :tag "Go to implementation" :implementationProvider)
|
||||
(const :tag "Go to declaration" :declarationProvider)
|
||||
(const :tag "Find references" :referencesProvider)
|
||||
(const :tag "Highlight symbols automatically" :documentHighlightProvider)
|
||||
(const :tag "List symbols in buffer" :documentSymbolProvider)
|
||||
(const :tag "List symbols in workspace" :workspaceSymbolProvider)
|
||||
(const :tag "Execute code actions" :codeActionProvider)
|
||||
(const :tag "Code lens" :codeLensProvider)
|
||||
(const :tag "Format buffer" :documentFormattingProvider)
|
||||
(const :tag "Format portion of buffer" :documentRangeFormattingProvider)
|
||||
(const :tag "On-type formatting" :documentOnTypeFormattingProvider)
|
||||
(const :tag "Rename symbol" :renameProvider)
|
||||
(const :tag "Highlight links in document" :documentLinkProvider)
|
||||
(const :tag "Decorate color references" :colorProvider)
|
||||
(const :tag "Fold regions of buffer" :foldingRangeProvider)
|
||||
(const :tag "Execute custom commands" :executeCommandProvider)
|
||||
(const :tag "Inlay hints" :inlayHintProvider)))
|
||||
|
||||
(defun eglot--server-capable (&rest feats)
|
||||
"Determine if current server is capable of FEATS."
|
||||
(unless (cl-some (lambda (feat)
|
||||
(memq feat eglot-ignored-server-capabilities))
|
||||
feats)
|
||||
(cl-loop for caps = (eglot--capabilities (eglot--current-server-or-lose))
|
||||
then (cadr probe)
|
||||
for (feat . more) on feats
|
||||
for probe = (plist-member caps feat)
|
||||
if (not probe) do (cl-return nil)
|
||||
if (eq (cadr probe) :json-false) do (cl-return nil)
|
||||
if (not (listp (cadr probe))) do (cl-return (if more nil (cadr probe)))
|
||||
finally (cl-return (or (cadr probe) t)))))
|
||||
|
||||
(defun eglot--server-capable-or-lose (&rest feats)
|
||||
"Like `eglot--server-capable', but maybe error out."
|
||||
(let ((retval (apply #'eglot--server-capable feats)))
|
||||
(unless retval
|
||||
(eglot--error "Unsupported or ignored LSP capability `%s'"
|
||||
(mapconcat #'symbol-name feats " ")))
|
||||
retval))
|
||||
|
||||
(defun eglot--range-region (range &optional markers)
|
||||
"Return region (BEG . END) that represents LSP RANGE.
|
||||
If optional MARKERS, make markers."
|
||||
(let* ((st (plist-get range :start))
|
||||
(beg (eglot--lsp-position-to-point st markers))
|
||||
(end (eglot--lsp-position-to-point (plist-get range :end) markers)))
|
||||
(cons beg end)))
|
||||
|
||||
(defun eglot--read-server (prompt &optional dont-if-just-the-one)
|
||||
"Read a running Eglot server from minibuffer using PROMPT.
|
||||
If DONT-IF-JUST-THE-ONE and there's only one server, don't prompt
|
||||
|
@ -2078,9 +2087,6 @@ If it is activated, also signal textDocument/didOpen."
|
|||
(package-delete existing t))
|
||||
(package-install (cadr (assoc 'eglot package-archive-contents)))))
|
||||
|
||||
;;;###autoload
|
||||
(define-obsolete-function-alias 'eglot-update 'eglot-upgrade-eglot "29.1")
|
||||
|
||||
(easy-menu-define eglot-menu nil "Eglot"
|
||||
`("Eglot"
|
||||
;; Commands for getting information and customization.
|
||||
|
@ -2089,47 +2095,47 @@ If it is activated, also signal textDocument/didOpen."
|
|||
;; xref like commands.
|
||||
["Find definitions" xref-find-definitions
|
||||
:help "Find definitions of identifier at point"
|
||||
:active (eglot--server-capable :definitionProvider)]
|
||||
:active (eglot-server-capable :definitionProvider)]
|
||||
["Find references" xref-find-references
|
||||
:help "Find references to identifier at point"
|
||||
:active (eglot--server-capable :referencesProvider)]
|
||||
:active (eglot-server-capable :referencesProvider)]
|
||||
["Find symbols in workspace (apropos)" xref-find-apropos
|
||||
:help "Find symbols matching a query"
|
||||
:active (eglot--server-capable :workspaceSymbolProvider)]
|
||||
:active (eglot-server-capable :workspaceSymbolProvider)]
|
||||
["Find declaration" eglot-find-declaration
|
||||
:help "Find declaration for identifier at point"
|
||||
:active (eglot--server-capable :declarationProvider)]
|
||||
:active (eglot-server-capable :declarationProvider)]
|
||||
["Find implementation" eglot-find-implementation
|
||||
:help "Find implementation for identifier at point"
|
||||
:active (eglot--server-capable :implementationProvider)]
|
||||
:active (eglot-server-capable :implementationProvider)]
|
||||
["Find type definition" eglot-find-typeDefinition
|
||||
:help "Find type definition for identifier at point"
|
||||
:active (eglot--server-capable :typeDefinitionProvider)]
|
||||
:active (eglot-server-capable :typeDefinitionProvider)]
|
||||
"--"
|
||||
;; LSP-related commands (mostly Eglot's own commands).
|
||||
["Rename symbol" eglot-rename
|
||||
:active (eglot--server-capable :renameProvider)]
|
||||
:active (eglot-server-capable :renameProvider)]
|
||||
["Format buffer" eglot-format-buffer
|
||||
:active (eglot--server-capable :documentFormattingProvider)]
|
||||
:active (eglot-server-capable :documentFormattingProvider)]
|
||||
["Format active region" eglot-format
|
||||
:active (and (region-active-p)
|
||||
(eglot--server-capable :documentRangeFormattingProvider))]
|
||||
(eglot-server-capable :documentRangeFormattingProvider))]
|
||||
["Show Flymake diagnostics for buffer" flymake-show-buffer-diagnostics]
|
||||
["Show Flymake diagnostics for project" flymake-show-project-diagnostics]
|
||||
["Show Eldoc documentation at point" eldoc-doc-buffer]
|
||||
"--"
|
||||
["All possible code actions" eglot-code-actions
|
||||
:active (eglot--server-capable :codeActionProvider)]
|
||||
:active (eglot-server-capable :codeActionProvider)]
|
||||
["Organize imports" eglot-code-action-organize-imports
|
||||
:visible (eglot--server-capable :codeActionProvider)]
|
||||
:visible (eglot-server-capable :codeActionProvider)]
|
||||
["Extract" eglot-code-action-extract
|
||||
:visible (eglot--server-capable :codeActionProvider)]
|
||||
:visible (eglot-server-capable :codeActionProvider)]
|
||||
["Inline" eglot-code-action-inline
|
||||
:visible (eglot--server-capable :codeActionProvider)]
|
||||
:visible (eglot-server-capable :codeActionProvider)]
|
||||
["Rewrite" eglot-code-action-rewrite
|
||||
:visible (eglot--server-capable :codeActionProvider)]
|
||||
:visible (eglot-server-capable :codeActionProvider)]
|
||||
["Quickfix" eglot-code-action-quickfix
|
||||
:visible (eglot--server-capable :codeActionProvider)]))
|
||||
:visible (eglot-server-capable :codeActionProvider)]))
|
||||
|
||||
(easy-menu-define eglot-server-menu nil "Monitor server communication"
|
||||
'("Debugging the server communication"
|
||||
|
@ -2323,7 +2329,7 @@ still unanswered LSP requests to the server\n")))
|
|||
(t 'eglot-note)))
|
||||
(mess (source code message)
|
||||
(concat source (and code (format " [%s]" code)) ": " message)))
|
||||
(if-let* ((path (expand-file-name (eglot--uri-to-path uri)))
|
||||
(if-let* ((path (expand-file-name (eglot-uri-to-path uri)))
|
||||
(buffer (find-buffer-visiting path)))
|
||||
(with-current-buffer buffer
|
||||
(cl-loop
|
||||
|
@ -2335,7 +2341,7 @@ still unanswered LSP requests to the server\n")))
|
|||
diag-spec
|
||||
(setq message (mess source code message))
|
||||
(pcase-let
|
||||
((`(,beg . ,end) (eglot--range-region range)))
|
||||
((`(,beg . ,end) (eglot-range-region range)))
|
||||
;; Fallback to `flymake-diag-region' if server
|
||||
;; botched the range
|
||||
(when (= beg end)
|
||||
|
@ -2427,7 +2433,7 @@ THINGS are either registrations or unregisterations (sic)."
|
|||
(filename))
|
||||
(cond
|
||||
((eq external t) (browse-url uri))
|
||||
((file-readable-p (setq filename (eglot--uri-to-path uri)))
|
||||
((file-readable-p (setq filename (eglot-uri-to-path uri)))
|
||||
;; Use run-with-timer to avoid nested client requests like the
|
||||
;; "synchronous imenu" floated in bug#62116 presumably caused by
|
||||
;; which-func-mode.
|
||||
|
@ -2440,7 +2446,7 @@ THINGS are either registrations or unregisterations (sic)."
|
|||
(select-frame-set-input-focus (selected-frame)))
|
||||
((display-buffer (current-buffer))))
|
||||
(when selection
|
||||
(pcase-let ((`(,beg . ,end) (eglot--range-region selection)))
|
||||
(pcase-let ((`(,beg . ,end) (eglot-range-region selection)))
|
||||
;; FIXME: it is very naughty to use someone else's `--'
|
||||
;; function, but `xref--goto-char' happens to have
|
||||
;; exactly the semantics we want vis-a-vis widening.
|
||||
|
@ -2451,7 +2457,7 @@ THINGS are either registrations or unregisterations (sic)."
|
|||
|
||||
(defun eglot--TextDocumentIdentifier ()
|
||||
"Compute TextDocumentIdentifier object for current buffer."
|
||||
`(:uri ,(eglot--path-to-uri (or buffer-file-name
|
||||
`(:uri ,(eglot-path-to-uri (or buffer-file-name
|
||||
(ignore-errors
|
||||
(buffer-file-name
|
||||
(buffer-base-buffer)))))))
|
||||
|
@ -2492,7 +2498,7 @@ buffer."
|
|||
(defun eglot--post-self-insert-hook ()
|
||||
"Set `eglot--last-inserted-char', maybe call on-type-formatting."
|
||||
(setq eglot--last-inserted-char last-command-event)
|
||||
(let ((ot-provider (eglot--server-capable :documentOnTypeFormattingProvider)))
|
||||
(let ((ot-provider (eglot-server-capable :documentOnTypeFormattingProvider)))
|
||||
(when (and ot-provider
|
||||
(ignore-errors ; github#906, some LS's send empty strings
|
||||
(or (eq eglot--last-inserted-char
|
||||
|
@ -2516,7 +2522,7 @@ buffer."
|
|||
`(:context
|
||||
,(if-let (trigger (and (characterp eglot--last-inserted-char)
|
||||
(cl-find eglot--last-inserted-char
|
||||
(eglot--server-capable :completionProvider
|
||||
(eglot-server-capable :completionProvider
|
||||
:triggerCharacters)
|
||||
:key (lambda (str) (aref str 0))
|
||||
:test #'char-equal)))
|
||||
|
@ -2677,7 +2683,7 @@ When called interactively, use the currently active server"
|
|||
(mapcar
|
||||
(eglot--lambda ((ConfigurationItem) scopeUri section)
|
||||
(cl-loop
|
||||
with scope-uri-path = (and scopeUri (eglot--uri-to-path scopeUri))
|
||||
with scope-uri-path = (and scopeUri (eglot-uri-to-path scopeUri))
|
||||
for (wsection o)
|
||||
on (eglot--workspace-configuration-plist server scope-uri-path)
|
||||
by #'cddr
|
||||
|
@ -2693,7 +2699,7 @@ When called interactively, use the currently active server"
|
|||
"Send textDocument/didChange to server."
|
||||
(when eglot--recent-changes
|
||||
(let* ((server (eglot--current-server-or-lose))
|
||||
(sync-capability (eglot--server-capable :textDocumentSync))
|
||||
(sync-capability (eglot-server-capable :textDocumentSync))
|
||||
(sync-kind (if (numberp sync-capability) sync-capability
|
||||
(plist-get sync-capability :change)))
|
||||
(full-sync-p (or (eq sync-kind 1)
|
||||
|
@ -2738,9 +2744,9 @@ When called interactively, use the currently active server"
|
|||
"Maybe send textDocument/willSave to server."
|
||||
(let ((server (eglot--current-server-or-lose))
|
||||
(params `(:reason 1 :textDocument ,(eglot--TextDocumentIdentifier))))
|
||||
(when (eglot--server-capable :textDocumentSync :willSave)
|
||||
(when (eglot-server-capable :textDocumentSync :willSave)
|
||||
(jsonrpc-notify server :textDocument/willSave params))
|
||||
(when (eglot--server-capable :textDocumentSync :willSaveWaitUntil)
|
||||
(when (eglot-server-capable :textDocumentSync :willSaveWaitUntil)
|
||||
(ignore-errors
|
||||
(eglot--apply-text-edits
|
||||
(eglot--request server :textDocument/willSaveWaitUntil params
|
||||
|
@ -2749,7 +2755,7 @@ When called interactively, use the currently active server"
|
|||
(defun eglot--signal-textDocument/didSave ()
|
||||
"Maybe send textDocument/didSave to server."
|
||||
(eglot--signal-textDocument/didChange)
|
||||
(when (eglot--server-capable :textDocumentSync :save)
|
||||
(when (eglot-server-capable :textDocumentSync :save)
|
||||
(jsonrpc-notify
|
||||
(eglot--current-server-or-lose)
|
||||
:textDocument/didSave
|
||||
|
@ -2808,12 +2814,12 @@ may be called multiple times (respecting the protocol of
|
|||
"Like `xref-make-match' but with LSP's NAME, URI and RANGE.
|
||||
Try to visit the target file for a richer summary line."
|
||||
(pcase-let*
|
||||
((file (eglot--uri-to-path uri))
|
||||
((file (eglot-uri-to-path uri))
|
||||
(visiting (or (find-buffer-visiting file)
|
||||
(gethash uri eglot--temp-location-buffers)))
|
||||
(collect (lambda ()
|
||||
(eglot--widening
|
||||
(pcase-let* ((`(,beg . ,end) (eglot--range-region range))
|
||||
(pcase-let* ((`(,beg . ,end) (eglot-range-region range))
|
||||
(bol (progn (goto-char beg) (eglot--bol)))
|
||||
(substring (buffer-substring bol (line-end-position)))
|
||||
(hi-beg (- beg bol))
|
||||
|
@ -2844,7 +2850,7 @@ Try to visit the target file for a richer summary line."
|
|||
"Ask for :workspace/symbol on PAT, return list of formatted strings.
|
||||
If BUFFER, switch to it before."
|
||||
(with-current-buffer (or buffer (current-buffer))
|
||||
(eglot--server-capable-or-lose :workspaceSymbolProvider)
|
||||
(eglot-server-capable-or-lose :workspaceSymbolProvider)
|
||||
(mapcar
|
||||
(lambda (wss)
|
||||
(eglot--dbind ((WorkspaceSymbol) name containerName kind) wss
|
||||
|
@ -2906,7 +2912,7 @@ If BUFFER, switch to it before."
|
|||
|
||||
(cl-defun eglot--lsp-xrefs-for-method (method &key extra-params capability)
|
||||
"Make `xref''s for METHOD, EXTRA-PARAMS, check CAPABILITY."
|
||||
(eglot--server-capable-or-lose
|
||||
(eglot-server-capable-or-lose
|
||||
(or capability
|
||||
(intern
|
||||
(format ":%sProvider"
|
||||
|
@ -2970,7 +2976,7 @@ If BUFFER, switch to it before."
|
|||
:textDocument/references :extra-params `(:context (:includeDeclaration t)))))
|
||||
|
||||
(cl-defmethod xref-backend-apropos ((_backend (eql eglot)) pattern)
|
||||
(when (eglot--server-capable :workspaceSymbolProvider)
|
||||
(when (eglot-server-capable :workspaceSymbolProvider)
|
||||
(eglot--collecting-xrefs (collect)
|
||||
(mapc
|
||||
(eglot--lambda ((SymbolInformation) name location)
|
||||
|
@ -3008,7 +3014,7 @@ for which LSP on-type-formatting should be requested."
|
|||
:end (eglot--pos-to-lsp-position end)))))
|
||||
(t
|
||||
'(:textDocument/formatting :documentFormattingProvider nil)))))
|
||||
(eglot--server-capable-or-lose cap)
|
||||
(eglot-server-capable-or-lose cap)
|
||||
(eglot--apply-text-edits
|
||||
(eglot--request
|
||||
(eglot--current-server-or-lose)
|
||||
|
@ -3033,7 +3039,7 @@ for which LSP on-type-formatting should be requested."
|
|||
(defun eglot-completion-at-point ()
|
||||
"Eglot's `completion-at-point' function."
|
||||
;; Commit logs for this function help understand what's going on.
|
||||
(when-let (completion-capability (eglot--server-capable :completionProvider))
|
||||
(when-let (completion-capability (eglot-server-capable :completionProvider))
|
||||
(let* ((server (eglot--current-server-or-lose))
|
||||
(sort-completions
|
||||
(lambda (completions)
|
||||
|
@ -3096,7 +3102,7 @@ for which LSP on-type-formatting should be requested."
|
|||
(lambda (lsp-comp)
|
||||
(or (gethash lsp-comp resolved)
|
||||
(setf (gethash lsp-comp resolved)
|
||||
(if (and (eglot--server-capable :completionProvider
|
||||
(if (and (eglot-server-capable :completionProvider
|
||||
:resolveProvider)
|
||||
(plist-get lsp-comp :data))
|
||||
(eglot--request server :completionItem/resolve
|
||||
|
@ -3224,7 +3230,7 @@ for which LSP on-type-formatting should be requested."
|
|||
(delete-region orig-pos (point))
|
||||
(eglot--dbind ((TextEdit) range newText) textEdit
|
||||
(pcase-let ((`(,beg . ,end)
|
||||
(eglot--range-region range)))
|
||||
(eglot-range-region range)))
|
||||
(delete-region beg end)
|
||||
(goto-char beg)
|
||||
(funcall (or snippet-fn #'insert) newText))))
|
||||
|
@ -3307,7 +3313,7 @@ for which LSP on-type-formatting should be requested."
|
|||
|
||||
(defun eglot-signature-eldoc-function (cb)
|
||||
"A member of `eldoc-documentation-functions', for signatures."
|
||||
(when (eglot--server-capable :signatureHelpProvider)
|
||||
(when (eglot-server-capable :signatureHelpProvider)
|
||||
(let ((buf (current-buffer)))
|
||||
(jsonrpc-async-request
|
||||
(eglot--current-server-or-lose)
|
||||
|
@ -3331,7 +3337,7 @@ for which LSP on-type-formatting should be requested."
|
|||
|
||||
(defun eglot-hover-eldoc-function (cb)
|
||||
"A member of `eldoc-documentation-functions', for hover."
|
||||
(when (eglot--server-capable :hoverProvider)
|
||||
(when (eglot-server-capable :hoverProvider)
|
||||
(let ((buf (current-buffer)))
|
||||
(jsonrpc-async-request
|
||||
(eglot--current-server-or-lose)
|
||||
|
@ -3353,7 +3359,7 @@ for which LSP on-type-formatting should be requested."
|
|||
;; FIXME: Obviously, this is just piggy backing on eldoc's calls for
|
||||
;; convenience, as shown by the fact that we just ignore cb.
|
||||
(let ((buf (current-buffer)))
|
||||
(when (eglot--server-capable :documentHighlightProvider)
|
||||
(when (eglot-server-capable :documentHighlightProvider)
|
||||
(jsonrpc-async-request
|
||||
(eglot--current-server-or-lose)
|
||||
:textDocument/documentHighlight (eglot--TextDocumentPositionParams)
|
||||
|
@ -3365,7 +3371,7 @@ for which LSP on-type-formatting should be requested."
|
|||
(mapcar
|
||||
(eglot--lambda ((DocumentHighlight) range)
|
||||
(pcase-let ((`(,beg . ,end)
|
||||
(eglot--range-region range)))
|
||||
(eglot-range-region range)))
|
||||
(let ((ov (make-overlay beg end)))
|
||||
(overlay-put ov 'face 'eglot-highlight-symbol-face)
|
||||
(overlay-put ov 'modification-hooks
|
||||
|
@ -3385,7 +3391,7 @@ for which LSP on-type-formatting should be requested."
|
|||
(pcase-lambda (`(,container . ,objs))
|
||||
(let ((elems (mapcar
|
||||
(eglot--lambda ((SymbolInformation) kind name location)
|
||||
(let ((reg (eglot--range-region
|
||||
(let ((reg (eglot-range-region
|
||||
(plist-get location :range)))
|
||||
(kind (alist-get kind eglot--symbol-kind-names)))
|
||||
(cons (propertize name
|
||||
|
@ -3401,7 +3407,7 @@ for which LSP on-type-formatting should be requested."
|
|||
(defun eglot--imenu-DocumentSymbol (res)
|
||||
"Compute `imenu--index-alist' for RES vector of DocumentSymbol."
|
||||
(cl-labels ((dfs (&key name children range kind &allow-other-keys)
|
||||
(let* ((reg (eglot--range-region range))
|
||||
(let* ((reg (eglot-range-region range))
|
||||
(kind (alist-get kind eglot--symbol-kind-names))
|
||||
(name (propertize name
|
||||
'breadcrumb-region reg
|
||||
|
@ -3415,7 +3421,7 @@ for which LSP on-type-formatting should be requested."
|
|||
(cl-defun eglot-imenu ()
|
||||
"Eglot's `imenu-create-index-function'.
|
||||
Returns a list as described in docstring of `imenu--index-alist'."
|
||||
(unless (eglot--server-capable :documentSymbolProvider)
|
||||
(unless (eglot-server-capable :documentSymbolProvider)
|
||||
(cl-return-from eglot-imenu))
|
||||
(let* ((res (eglot--request (eglot--current-server-or-lose)
|
||||
:textDocument/documentSymbol
|
||||
|
@ -3457,7 +3463,7 @@ If SILENT, don't echo progress in mode-line."
|
|||
(when reporter
|
||||
(eglot--reporter-update reporter (cl-incf done))))))))
|
||||
(mapcar (eglot--lambda ((TextEdit) range newText)
|
||||
(cons newText (eglot--range-region range 'markers)))
|
||||
(cons newText (eglot-range-region range 'markers)))
|
||||
(reverse edits)))
|
||||
(undo-amalgamate-change-group change-group)
|
||||
(when reporter
|
||||
|
@ -3521,14 +3527,14 @@ edit proposed by the server."
|
|||
(mapcar (eglot--lambda ((TextDocumentEdit) textDocument edits)
|
||||
(eglot--dbind ((VersionedTextDocumentIdentifier) uri version)
|
||||
textDocument
|
||||
(list (eglot--uri-to-path uri) edits version)))
|
||||
(list (eglot-uri-to-path uri) edits version)))
|
||||
documentChanges)))
|
||||
(unless (and changes documentChanges)
|
||||
;; We don't want double edits, and some servers send both
|
||||
;; changes and documentChanges. This unless ensures that we
|
||||
;; prefer documentChanges over changes.
|
||||
(cl-loop for (uri edits) on changes by #'cddr
|
||||
do (push (list (eglot--uri-to-path uri) edits) prepared)))
|
||||
do (push (list (eglot-uri-to-path uri) edits) prepared)))
|
||||
(cl-flet ((notevery-visited-p ()
|
||||
(cl-notevery #'find-buffer-visiting
|
||||
(mapcar #'car prepared)))
|
||||
|
@ -3566,7 +3572,7 @@ edit proposed by the server."
|
|||
"unknown symbol"))
|
||||
nil nil nil nil
|
||||
(symbol-name (symbol-at-point)))))
|
||||
(eglot--server-capable-or-lose :renameProvider)
|
||||
(eglot-server-capable-or-lose :renameProvider)
|
||||
(eglot--apply-workspace-edit
|
||||
(eglot--request (eglot--current-server-or-lose)
|
||||
:textDocument/rename `(,@(eglot--TextDocumentPositionParams)
|
||||
|
@ -3593,7 +3599,7 @@ at point. With prefix argument, prompt for ACTION-KIND."
|
|||
'("quickfix" "refactor.extract" "refactor.inline"
|
||||
"refactor.rewrite" "source.organizeImports")))
|
||||
t))
|
||||
(eglot--server-capable-or-lose :codeActionProvider)
|
||||
(eglot-server-capable-or-lose :codeActionProvider)
|
||||
(let* ((server (eglot--current-server-or-lose))
|
||||
(actions
|
||||
(eglot--request
|
||||
|
@ -3693,7 +3699,7 @@ at point. With prefix argument, prompt for ACTION-KIND."
|
|||
(funcall glob file))))
|
||||
(jsonrpc-notify
|
||||
server :workspace/didChangeWatchedFiles
|
||||
`(:changes ,(vector `(:uri ,(eglot--path-to-uri file)
|
||||
`(:changes ,(vector `(:uri ,(eglot-path-to-uri file)
|
||||
:type ,action-type))))
|
||||
(when (and (eq action 'created)
|
||||
(file-directory-p file))
|
||||
|
@ -3960,7 +3966,7 @@ If NOERROR, return predicate, else erroring function."
|
|||
"Minor mode for annotating buffers with LSP server's inlay hints."
|
||||
:global nil
|
||||
(cond (eglot-inlay-hints-mode
|
||||
(if (eglot--server-capable :inlayHintProvider)
|
||||
(if (eglot-server-capable :inlayHintProvider)
|
||||
(jit-lock-register #'eglot--update-hints 'contextual)
|
||||
(eglot-inlay-hints-mode -1)))
|
||||
(t
|
||||
|
@ -3987,11 +3993,7 @@ If NOERROR, return predicate, else erroring function."
|
|||
"https://github.com/joaotavora/eglot/issues/%s"
|
||||
"https://debbugs.gnu.org/%s")
|
||||
(match-string 3))))
|
||||
;;; Obsolete
|
||||
;;;
|
||||
|
||||
(make-obsolete-variable 'eglot--managed-mode-hook
|
||||
'eglot-managed-mode-hook "1.6")
|
||||
(provide 'eglot)
|
||||
|
||||
|
||||
|
|
|
@ -8750,11 +8750,12 @@ This expects NAME TYPE IDLWAVE-TWIN-CLASS to be bound to the right values."
|
|||
|
||||
(defun idlwave-count-eq (elt list)
|
||||
"How often is ELT in LIST?"
|
||||
(length (delq nil (mapcar (lambda (x) (eq x elt)) list))))
|
||||
(declare (obsolete nil "30.1"))
|
||||
(seq-count (lambda (x) (eq x elt)) list))
|
||||
|
||||
(defun idlwave-count-memq (elt alist)
|
||||
"How often is ELT a key in ALIST?"
|
||||
(length (delq nil (mapcar (lambda (x) (eq (car x) elt)) alist))))
|
||||
(seq-count (lambda (x) (eq (car x) elt)) alist))
|
||||
|
||||
(defun idlwave-syslib-p (file)
|
||||
"Non-nil if FILE is in the system library."
|
||||
|
|
|
@ -257,7 +257,7 @@ This expects `auto-revert--messages' to be bound by
|
|||
;; Repeated unpredictable failures, bug#32645.
|
||||
:tags '(:unstable)
|
||||
;; Unlikely to be hydra-specific?
|
||||
;; (skip-unless (not (getenv "EMACS_HYDRA_CI")))
|
||||
;; (skip-when (getenv "EMACS_HYDRA_CI"))
|
||||
(with-auto-revert-test
|
||||
(ert-with-temp-file tmpfile
|
||||
(let (;; Try to catch bug#32645.
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
(ert-deftest benchmark-tests ()
|
||||
;; Avoid fork failures on Cygwin. See bug#62450 and etc/PROBLEMS
|
||||
;; ("Fork failures in a build with native compilation").
|
||||
(skip-unless (not (and (eq system-type 'cygwin)
|
||||
(featurep 'native-compile))))
|
||||
(skip-when (and (eq system-type 'cygwin)
|
||||
(featurep 'native-compile)))
|
||||
(let (str t-long t-short m)
|
||||
(should (consp (benchmark-run nil (setq m (1+ 0)))))
|
||||
(should (consp (benchmark-run 1 (setq m (1+ 0)))))
|
||||
|
|
|
@ -304,6 +304,20 @@ failed or if there was a problem."
|
|||
(cl-macrolet ((test () (error "Foo")))
|
||||
(should-error (test))))
|
||||
|
||||
(ert-deftest ert-test-skip-when ()
|
||||
;; Don't skip.
|
||||
(let ((test (make-ert-test :body (lambda () (skip-when nil)))))
|
||||
(let ((result (ert-run-test test)))
|
||||
(should (ert-test-passed-p result))))
|
||||
;; Skip.
|
||||
(let ((test (make-ert-test :body (lambda () (skip-when t)))))
|
||||
(let ((result (ert-run-test test)))
|
||||
(should (ert-test-skipped-p result))))
|
||||
;; Skip in case of error.
|
||||
(let ((test (make-ert-test :body (lambda () (skip-when (error "Foo"))))))
|
||||
(let ((result (ert-run-test test)))
|
||||
(should (ert-test-skipped-p result)))))
|
||||
|
||||
(ert-deftest ert-test-skip-unless ()
|
||||
;; Don't skip.
|
||||
(let ((test (make-ert-test :body (lambda () (skip-unless t)))))
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
(ert-deftest find-func-tests--library-completion () ;bug#43393
|
||||
;; FIXME: How can we make this work in batch (see also
|
||||
;; `mule-cmds--test-universal-coding-system-argument')?
|
||||
;; (skip-unless (not noninteractive))
|
||||
;; (skip-when noninteractive)
|
||||
;; Check that `partial-completion' works when completing library names.
|
||||
(should (equal "org/org"
|
||||
(ert-simulate-keys
|
||||
|
|
|
@ -281,7 +281,7 @@ prompt. See bug#54136."
|
|||
(executable-find "sleep")))
|
||||
;; This test doesn't work on EMBA with AOT nativecomp, but works
|
||||
;; fine elsewhere.
|
||||
(skip-unless (not (getenv "EMACS_EMBA_CI")))
|
||||
(skip-when (getenv "EMACS_EMBA_CI"))
|
||||
(with-temp-eshell
|
||||
(eshell-insert-command
|
||||
(concat "sh -c 'while true; do echo y; sleep 1; done' | "
|
||||
|
|
|
@ -1583,7 +1583,7 @@ the file watch."
|
|||
:tags '(:expensive-test)
|
||||
(skip-unless (file-notify--test-local-enabled))
|
||||
;; This test does not work for kqueue (yet).
|
||||
(skip-unless (not (string-equal (file-notify--test-library) "kqueue")))
|
||||
(skip-when (string-equal (file-notify--test-library) "kqueue"))
|
||||
|
||||
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
|
||||
file-notify--test-tmpfile1 (file-notify--test-make-temp-name))
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
(ert-deftest ibuffer-0autoload () ; sort first
|
||||
"Tests to see whether ibuffer has been autoloaded"
|
||||
(skip-unless (not (featurep 'ibuf-ext)))
|
||||
(skip-when (featurep 'ibuf-ext))
|
||||
(should
|
||||
(fboundp 'ibuffer-mark-unsaved-buffers))
|
||||
(should
|
||||
|
|
|
@ -255,8 +255,8 @@ Must be called with `ucs-normalize-tests--norm-buf' as current buffer."
|
|||
|
||||
(ert-deftest ucs-normalize-part1 ()
|
||||
:tags '(:expensive-test)
|
||||
(skip-unless (not (or (getenv "EMACS_HYDRA_CI")
|
||||
(getenv "EMACS_EMBA_CI")))) ; SLOW ~ 1800s
|
||||
(skip-when (or (getenv "EMACS_HYDRA_CI")
|
||||
(getenv "EMACS_EMBA_CI"))) ; SLOW ~ 1800s
|
||||
;; This takes a long time, so make sure we're compiled.
|
||||
(dolist (fun '(ucs-normalize-tests--part1-rule2
|
||||
ucs-normalize-tests--rule1-failing-for-partX
|
||||
|
|
|
@ -236,7 +236,7 @@
|
|||
(while (and (eq (process-status proc) 'connect)
|
||||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(skip-unless (not (eq (process-status proc) 'connect)))
|
||||
(skip-when (eq (process-status proc) 'connect))
|
||||
(with-current-buffer (process-buffer proc)
|
||||
(process-send-string proc "echo foo")
|
||||
(sleep-for 0.1)
|
||||
|
@ -323,7 +323,7 @@
|
|||
(while (and (eq (process-status proc) 'connect)
|
||||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(skip-unless (not (eq (process-status proc) 'connect))))
|
||||
(skip-when (eq (process-status proc) 'connect)))
|
||||
(if (process-live-p server) (delete-process server)))
|
||||
(setq status (gnutls-peer-status proc))
|
||||
(should (consp status))
|
||||
|
@ -336,7 +336,7 @@
|
|||
(ert-deftest connect-to-tls-ipv6-nowait ()
|
||||
(skip-unless (executable-find "gnutls-serv"))
|
||||
(skip-unless (gnutls-available-p))
|
||||
(skip-unless (not (eq system-type 'windows-nt)))
|
||||
(skip-when (eq system-type 'windows-nt))
|
||||
(skip-unless (featurep 'make-network-process '(:family ipv6)))
|
||||
(let ((server (make-tls-server 44333))
|
||||
(times 0)
|
||||
|
@ -368,7 +368,7 @@
|
|||
(while (and (eq (process-status proc) 'connect)
|
||||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(skip-unless (not (eq (process-status proc) 'connect))))
|
||||
(skip-when (eq (process-status proc) 'connect)))
|
||||
(if (process-live-p server) (delete-process server)))
|
||||
(setq status (gnutls-peer-status proc))
|
||||
(should (consp status))
|
||||
|
@ -403,7 +403,7 @@
|
|||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(should proc)
|
||||
(skip-unless (not (eq (process-status proc) 'connect))))
|
||||
(skip-when (eq (process-status proc) 'connect)))
|
||||
(if (process-live-p server) (delete-process server)))
|
||||
(setq status (gnutls-peer-status proc))
|
||||
(should (consp status))
|
||||
|
@ -446,7 +446,7 @@
|
|||
(while (and (eq (process-status proc) 'connect)
|
||||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(skip-unless (not (eq (process-status proc) 'connect))))
|
||||
(skip-when (eq (process-status proc) 'connect)))
|
||||
(if (process-live-p server) (delete-process server)))
|
||||
(setq status (gnutls-peer-status proc))
|
||||
(should (consp status))
|
||||
|
@ -484,7 +484,7 @@
|
|||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(should proc)
|
||||
(skip-unless (not (eq (process-status proc) 'connect))))
|
||||
(skip-when (eq (process-status proc) 'connect)))
|
||||
(if (process-live-p server) (delete-process server)))
|
||||
(setq status (gnutls-peer-status proc))
|
||||
(should (consp status))
|
||||
|
@ -523,7 +523,7 @@
|
|||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(should proc)
|
||||
(skip-unless (not (eq (process-status proc) 'connect))))
|
||||
(skip-when (eq (process-status proc) 'connect)))
|
||||
(if (process-live-p server) (delete-process server)))
|
||||
(setq status (gnutls-peer-status proc))
|
||||
(should (consp status))
|
||||
|
@ -673,7 +673,7 @@
|
|||
(while (and (eq (process-status proc) 'connect)
|
||||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(skip-unless (not (eq (process-status proc) 'connect))))
|
||||
(skip-when (eq (process-status proc) 'connect)))
|
||||
(if (process-live-p server) (delete-process server)))
|
||||
(setq status (gnutls-peer-status proc))
|
||||
(should (consp status))
|
||||
|
@ -712,7 +712,7 @@
|
|||
(while (and (eq (process-status proc) 'connect)
|
||||
(< (setq times (1+ times)) 10))
|
||||
(sit-for 0.1))
|
||||
(skip-unless (not (eq (process-status proc) 'connect))))
|
||||
(skip-when (eq (process-status proc) 'connect)))
|
||||
(if (process-live-p server) (delete-process server)))
|
||||
(setq status (gnutls-peer-status proc))
|
||||
(should (consp status))
|
||||
|
|
|
@ -892,8 +892,8 @@ without a statement terminator on the same line does not loop
|
|||
forever. The test starts an asynchronous Emacs batch process
|
||||
under timeout control."
|
||||
:tags '(:expensive-test)
|
||||
(skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; FIXME times out
|
||||
(skip-unless (not (< emacs-major-version 28))) ; times out in older Emacsen
|
||||
(skip-when (getenv "EMACS_HYDRA_CI")) ; FIXME times out
|
||||
(skip-when (< emacs-major-version 28)) ; times out in older Emacsen
|
||||
(skip-unless (eq cperl-test-mode #'cperl-mode))
|
||||
(let* ((emacs (concat invocation-directory invocation-name))
|
||||
(test-function 'cperl-test--run-bug-10483)
|
||||
|
@ -1242,7 +1242,7 @@ however, must not happen when the keyword occurs in a variable
|
|||
\"$else\" or \"$continue\"."
|
||||
(skip-unless (eq cperl-test-mode #'cperl-mode))
|
||||
;; `self-insert-command' takes a second argument only since Emacs 27
|
||||
(skip-unless (not (< emacs-major-version 27)))
|
||||
(skip-when (< emacs-major-version 27))
|
||||
(with-temp-buffer
|
||||
(setq cperl-electric-keywords t)
|
||||
(cperl-mode)
|
||||
|
|
|
@ -128,7 +128,7 @@
|
|||
|
||||
(ert-deftest eval-last-sexp-print-format-sym-echo ()
|
||||
;; We can only check the echo area when running interactive.
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(with-temp-buffer
|
||||
(let ((current-prefix-arg nil))
|
||||
(erase-buffer) (insert "t") (message nil)
|
||||
|
@ -147,7 +147,7 @@
|
|||
(should (equal (buffer-string) "?A65 (#o101, #x41, ?A)")))))
|
||||
|
||||
(ert-deftest eval-last-sexp-print-format-small-int-echo ()
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(with-temp-buffer
|
||||
(let ((current-prefix-arg nil))
|
||||
(erase-buffer) (insert "?A") (message nil)
|
||||
|
@ -171,7 +171,7 @@
|
|||
(should (equal (buffer-string) "?B66 (#o102, #x42, ?B)"))))))
|
||||
|
||||
(ert-deftest eval-last-sexp-print-format-large-int-echo ()
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(with-temp-buffer
|
||||
(let ((eval-expression-print-maximum-character ?A))
|
||||
(let ((current-prefix-arg nil))
|
||||
|
@ -186,7 +186,7 @@
|
|||
;;; eval-defun
|
||||
|
||||
(ert-deftest eval-defun-prints-edebug-when-instrumented ()
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(with-temp-buffer
|
||||
(let ((current-prefix-arg '(4)))
|
||||
(erase-buffer) (insert "(defun foo ())") (message nil)
|
||||
|
|
|
@ -4918,7 +4918,7 @@ import abc
|
|||
;; Skip the test on macOS, since the standard Python installation uses
|
||||
;; libedit rather than readline which confuses the running of an inferior
|
||||
;; interpreter in this case (see bug#59477 and bug#25753).
|
||||
(skip-unless (not (eq system-type 'darwin)))
|
||||
(skip-when (eq system-type 'darwin))
|
||||
(trace-function 'python-shell-output-filter)
|
||||
(python-tests-with-temp-buffer-with-shell
|
||||
"
|
||||
|
|
|
@ -101,7 +101,7 @@ Per definition, all files are identical on the different hosts of
|
|||
a cluster (or site). This is not tested here; it must be
|
||||
guaranteed by the originator of a cluster definition."
|
||||
:tags '(:expensive-test)
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
|
||||
(let ((text-quoting-style 'grave) ;; We inspect the *Messages* buffer!
|
||||
|
@ -219,7 +219,7 @@ guaranteed by the originator of a cluster definition."
|
|||
Per definition, all files are identical on the different hosts of
|
||||
a cluster (or site). This is not tested here; it must be
|
||||
guaranteed by the originator of a cluster definition."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
|
@ -320,7 +320,7 @@ guaranteed by the originator of a cluster definition."
|
|||
|
||||
(ert-deftest shadow-test02-files ()
|
||||
"Check file manipulation functions."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
|
@ -391,7 +391,7 @@ guaranteed by the originator of a cluster definition."
|
|||
|
||||
(ert-deftest shadow-test03-expand-cluster-in-file-name ()
|
||||
"Check canonical file name of a cluster or site."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
|
@ -456,7 +456,7 @@ guaranteed by the originator of a cluster definition."
|
|||
|
||||
(ert-deftest shadow-test04-contract-file-name ()
|
||||
"Check canonical file name of a cluster or site."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
|
@ -511,7 +511,7 @@ guaranteed by the originator of a cluster definition."
|
|||
|
||||
(ert-deftest shadow-test05-file-match ()
|
||||
"Check `shadow-same-site' and `shadow-file-match'."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
|
@ -563,7 +563,7 @@ guaranteed by the originator of a cluster definition."
|
|||
|
||||
(ert-deftest shadow-test06-literal-groups ()
|
||||
"Check literal group definitions."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
|
@ -648,7 +648,7 @@ guaranteed by the originator of a cluster definition."
|
|||
|
||||
(ert-deftest shadow-test07-regexp-groups ()
|
||||
"Check regexp group definitions."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
|
@ -710,7 +710,7 @@ guaranteed by the originator of a cluster definition."
|
|||
|
||||
(ert-deftest shadow-test08-shadow-todo ()
|
||||
"Check that needed shadows are added to todo."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
(skip-unless (file-writable-p ert-remote-temporary-file-directory))
|
||||
|
||||
|
@ -855,7 +855,7 @@ guaranteed by the originator of a cluster definition."
|
|||
(ert-deftest shadow-test09-shadow-copy-files ()
|
||||
"Check that needed shadow files are copied."
|
||||
:tags '(:expensive-test)
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
|
||||
(skip-unless (file-writable-p ert-remote-temporary-file-directory))
|
||||
|
||||
|
|
|
@ -742,7 +742,7 @@ See Bug#21722."
|
|||
|
||||
(ert-deftest eval-expression-print-format-sym-echo ()
|
||||
;; We can only check the echo area when running interactive.
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(with-temp-buffer
|
||||
(cl-letf (((symbol-function 'read--expression) (lambda (&rest _) t)))
|
||||
(let ((current-prefix-arg nil))
|
||||
|
@ -763,7 +763,7 @@ See Bug#21722."
|
|||
(should (equal (buffer-string) "65 (#o101, #x41, ?A)"))))))
|
||||
|
||||
(ert-deftest eval-expression-print-format-small-int-echo ()
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(with-temp-buffer
|
||||
(cl-letf (((symbol-function 'read--expression) (lambda (&rest _) ?A)))
|
||||
(let ((current-prefix-arg nil))
|
||||
|
@ -789,7 +789,7 @@ See Bug#21722."
|
|||
(should (equal (buffer-string) "66 (#o102, #x42, ?B)"))))))
|
||||
|
||||
(ert-deftest eval-expression-print-format-large-int-echo ()
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(with-temp-buffer
|
||||
(cl-letf (((symbol-function 'read--expression) (lambda (&rest _) ?B))
|
||||
(eval-expression-print-maximum-character ?A))
|
||||
|
|
|
@ -110,7 +110,7 @@
|
|||
(buffer-substring (point-min) (point-max))))))
|
||||
|
||||
(ert-deftest term-simple-lines ()
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(let ((str "\
|
||||
first line\r
|
||||
next line\r\n"))
|
||||
|
@ -118,14 +118,14 @@ next line\r\n"))
|
|||
(string-replace "\r" "" str)))))
|
||||
|
||||
(ert-deftest term-carriage-return ()
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(let ((str "\
|
||||
first line\r_next line\r\n"))
|
||||
(should (equal (term-test-screen-from-input 40 12 str)
|
||||
"_next line\n"))))
|
||||
|
||||
(ert-deftest term-line-wrap ()
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(should (string-match-p
|
||||
;; Don't be strict about trailing whitespace.
|
||||
"\\`a\\{40\\}\na\\{20\\} *\\'"
|
||||
|
@ -137,7 +137,7 @@ first line\r_next line\r\n"))
|
|||
(list str str))))))
|
||||
|
||||
(ert-deftest term-colors ()
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(pcase-dolist (`(,str ,expected) ansi-test-strings)
|
||||
(let ((result (term-test-screen-from-input 40 12 str)))
|
||||
(should (equal result expected))
|
||||
|
@ -145,7 +145,7 @@ first line\r_next line\r\n"))
|
|||
(text-properties-at 0 expected))))))
|
||||
|
||||
(ert-deftest term-colors-bold-is-bright ()
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(let ((ansi-color-bold-is-bright t))
|
||||
(pcase-dolist (`(,str ,expected ,bright-expected) ansi-test-strings)
|
||||
(let ((expected (or bright-expected expected))
|
||||
|
@ -155,7 +155,7 @@ first line\r_next line\r\n"))
|
|||
(text-properties-at 0 expected)))))))
|
||||
|
||||
(ert-deftest term-cursor-movement ()
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
;; Absolute positioning.
|
||||
(should (equal "ab\ncd"
|
||||
(term-test-screen-from-input
|
||||
|
@ -186,7 +186,7 @@ first line\r_next line\r\n"))
|
|||
"\e[D\e[Da")))))
|
||||
|
||||
(ert-deftest term-scrolling-region ()
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(should (equal "\
|
||||
line3
|
||||
line4
|
||||
|
@ -338,7 +338,7 @@ line6\r
|
|||
line7")))))
|
||||
|
||||
(ert-deftest term-set-directory ()
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(let ((term-ansi-at-user (user-real-login-name)))
|
||||
(should (equal (term-test-screen-from-input
|
||||
40 12 "\eAnSiTc /foo/\n" 'default-directory)
|
||||
|
@ -354,7 +354,7 @@ A real-life example is the default zsh prompt which writes spaces
|
|||
to the end of line (triggering line-wrapping state), and then
|
||||
sends a carriage return followed by another space to overwrite
|
||||
the first character of the line."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(let* ((width 10)
|
||||
(strs (list "x" (make-string (1- width) ?_)
|
||||
"\r_")))
|
||||
|
@ -364,7 +364,7 @@ the first character of the line."
|
|||
(ert-deftest term-to-margin ()
|
||||
"Test cursor movement at the scroll margin.
|
||||
This is a reduced example from GNU nano's initial screen."
|
||||
(skip-unless (not (memq system-type '(windows-nt ms-dos))))
|
||||
(skip-when (memq system-type '(windows-nt ms-dos)))
|
||||
(let* ((width 10)
|
||||
(x (make-string width ?x))
|
||||
(y (make-string width ?y)))
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
|
||||
(ert-deftest thread-tests-list-threads-error-when-not-configured ()
|
||||
"Signal an error running `list-threads' if threads are not configured."
|
||||
(skip-unless (not (featurep 'threads)))
|
||||
(skip-when (featurep 'threads))
|
||||
(should-error (list-threads)))
|
||||
|
||||
(provide 'thread-tests)
|
||||
|
|
|
@ -781,7 +781,7 @@ This checks also `vc-backend' and `vc-responsible-backend'."
|
|||
;; CVS calls vc-delete-file, which insists on prompting
|
||||
;; "Really want to delete ...?", and `vc-mtn.el' does not implement
|
||||
;; `delete-file' at all.
|
||||
(skip-unless (not (memq ',backend '(CVS Mtn))))
|
||||
(skip-when (memq ',backend '(CVS Mtn)))
|
||||
(vc-test--rename-file ',backend))
|
||||
|
||||
(ert-deftest
|
||||
|
@ -796,7 +796,7 @@ This checks also `vc-backend' and `vc-responsible-backend'."
|
|||
(format "vc-test-%s01-register" backend-string))))))
|
||||
;; `vc-mtn.el' gives me:
|
||||
;; "Failed (status 1): mtn commit -m Testing vc-version-diff\n\n foo"
|
||||
(skip-unless (not (memq ',backend '(Mtn))))
|
||||
(skip-when (memq ',backend '(Mtn)))
|
||||
(vc-test--version-diff ',backend))
|
||||
))))
|
||||
|
||||
|
|
|
@ -80,25 +80,25 @@
|
|||
,@body)))
|
||||
|
||||
(ert-deftest scroll-tests-scroll-margin-0 ()
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(scroll-tests-with-buffer-window
|
||||
(scroll-tests-up-and-down 0)))
|
||||
|
||||
(ert-deftest scroll-tests-scroll-margin-negative ()
|
||||
"A negative `scroll-margin' should be the same as 0."
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(scroll-tests-with-buffer-window
|
||||
(scroll-tests-up-and-down -10 0)))
|
||||
|
||||
(ert-deftest scroll-tests-scroll-margin-max ()
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(scroll-tests-with-buffer-window
|
||||
(let ((max-margin (/ (window-text-height) 4)))
|
||||
(scroll-tests-up-and-down max-margin))))
|
||||
|
||||
(ert-deftest scroll-tests-scroll-margin-over-max ()
|
||||
"A `scroll-margin' more than max should be the same as max."
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(scroll-tests-with-buffer-window 7
|
||||
(let ((max-margin (/ (window-text-height) 4)))
|
||||
(scroll-tests-up-and-down (+ max-margin 1) max-margin)
|
||||
|
@ -155,7 +155,7 @@ middle of the window."
|
|||
(should (scroll-tests--point-in-middle-of-window-p)))))
|
||||
|
||||
(ert-deftest scroll-tests-scroll-margin-whole-window ()
|
||||
(skip-unless (not noninteractive))
|
||||
(skip-when noninteractive)
|
||||
(scroll-tests--scroll-margin-whole-window))
|
||||
|
||||
(ert-deftest scroll-tests-scroll-margin-whole-window-line-spacing ()
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
:tags '(:expensive-test)
|
||||
:expected-result :failed ; FIXME: See above.
|
||||
;; This test is very slow, and IMO not worth the time it takes.
|
||||
(skip-unless (not (getenv "EMACS_HYDRA_CI")))
|
||||
(skip-when (getenv "EMACS_HYDRA_CI"))
|
||||
(skip-unless (file-readable-p custom-test-admin-cus-test))
|
||||
(load custom-test-admin-cus-test)
|
||||
(cus-test-libs t)
|
||||
|
|
|
@ -457,7 +457,7 @@ See Bug#36226."
|
|||
|
||||
(ert-deftest module/async-pipe ()
|
||||
"Check that writing data from another thread works."
|
||||
(skip-unless (not (eq system-type 'windows-nt))) ; FIXME!
|
||||
(skip-when (eq system-type 'windows-nt)) ; FIXME!
|
||||
(with-temp-buffer
|
||||
(let ((process (make-pipe-process :name "module/async-pipe"
|
||||
:buffer (current-buffer)
|
||||
|
|
|
@ -50,7 +50,7 @@ Also check that an encoding error can appear in a symlink."
|
|||
;; Some Windows versions don't support symlinks, and those which do
|
||||
;; will pop up UAC elevation prompts, so we disable this test on
|
||||
;; MS-Windows.
|
||||
(skip-unless (not (eq system-type 'windows-nt)))
|
||||
(skip-when (eq system-type 'windows-nt))
|
||||
(should (equal nil (fileio-tests--symlink-failure))))
|
||||
|
||||
(ert-deftest fileio-tests--directory-file-name ()
|
||||
|
|
|
@ -109,7 +109,7 @@ the case)."
|
|||
|
||||
(ert-deftest filelock-tests-lock-spoiled ()
|
||||
"Check `lock-buffer'."
|
||||
(skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
|
||||
(skip-when (eq system-type 'ms-dos)) ; no filelock support
|
||||
(filelock-tests--fixture
|
||||
(filelock-tests--spoil-lock-file buffer-file-truename)
|
||||
;; FIXME: errors when locking a file are ignored; should they be?
|
||||
|
@ -119,7 +119,7 @@ the case)."
|
|||
|
||||
(ert-deftest filelock-tests-file-locked-p-spoiled ()
|
||||
"Check that `file-locked-p' fails if the lockfile is \"spoiled\"."
|
||||
(skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
|
||||
(skip-when (eq system-type 'ms-dos)) ; no filelock support
|
||||
(filelock-tests--fixture
|
||||
(filelock-tests--spoil-lock-file buffer-file-truename)
|
||||
(let ((err (should-error (file-locked-p (buffer-file-name)))))
|
||||
|
@ -130,7 +130,7 @@ the case)."
|
|||
|
||||
(ert-deftest filelock-tests-unlock-spoiled ()
|
||||
"Check that `unlock-buffer' fails if the lockfile is \"spoiled\"."
|
||||
(skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
|
||||
(skip-when (eq system-type 'ms-dos)) ; no filelock support
|
||||
(filelock-tests--fixture
|
||||
;; Set the buffer modified with file locking temporarily disabled.
|
||||
(let ((create-lockfiles nil))
|
||||
|
@ -150,7 +150,7 @@ the case)."
|
|||
|
||||
(ert-deftest filelock-tests-kill-buffer-spoiled ()
|
||||
"Check that `kill-buffer' fails if a lockfile is \"spoiled\"."
|
||||
(skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
|
||||
(skip-when (eq system-type 'ms-dos)) ; no filelock support
|
||||
(filelock-tests--fixture
|
||||
;; Set the buffer modified with file locking temporarily disabled.
|
||||
(let ((create-lockfiles nil))
|
||||
|
@ -176,7 +176,7 @@ the case)."
|
|||
|
||||
(ert-deftest filelock-tests-detect-external-change ()
|
||||
"Check that an external file modification is reported."
|
||||
(skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
|
||||
(skip-when (eq system-type 'ms-dos)) ; no filelock support
|
||||
(skip-unless (executable-find "touch"))
|
||||
(skip-unless (executable-find "echo"))
|
||||
(dolist (cl '(t nil))
|
||||
|
|
|
@ -44,15 +44,15 @@
|
|||
(xpm . ,(find-image '((:file "splash.xpm" :type xpm))))))
|
||||
|
||||
(ert-deftest image-tests-image-size/error-on-nongraphical-display ()
|
||||
(skip-unless (not (display-images-p)))
|
||||
(skip-when (display-images-p))
|
||||
(should-error (image-size 'invalid-spec)))
|
||||
|
||||
(ert-deftest image-tests-image-mask-p/error-on-nongraphical-display ()
|
||||
(skip-unless (not (display-images-p)))
|
||||
(skip-when (display-images-p))
|
||||
(should-error (image-mask-p (cdr (assq 'xpm image-tests--images)))))
|
||||
|
||||
(ert-deftest image-tests-image-metadata/error-on-nongraphical-display ()
|
||||
(skip-unless (not (display-images-p)))
|
||||
(skip-when (display-images-p))
|
||||
(should-error (image-metadata (cdr (assq 'xpm image-tests--images)))))
|
||||
|
||||
(ert-deftest image-tests-imagemagick-types ()
|
||||
|
|
|
@ -231,7 +231,7 @@ process to complete."
|
|||
(with-timeout (60 (ert-fail "Test timed out"))
|
||||
;; Frequent random (?) failures on hydra.nixos.org, with no process output.
|
||||
;; Maybe this test should be tagged unstable? See bug#31214.
|
||||
(skip-unless (not (getenv "EMACS_HYDRA_CI")))
|
||||
(skip-when (getenv "EMACS_HYDRA_CI"))
|
||||
(with-temp-buffer
|
||||
(let ((process (make-process
|
||||
:name "mix-stderr"
|
||||
|
@ -723,7 +723,7 @@ FD_SETSIZE file descriptors (Bug#24325)."
|
|||
(skip-unless (featurep 'make-network-process '(:server t)))
|
||||
(skip-unless (featurep 'make-network-process '(:family local)))
|
||||
;; Avoid hang due to connect/accept handshake on Cygwin (bug#49496).
|
||||
(skip-unless (not (eq system-type 'cygwin)))
|
||||
(skip-when (eq system-type 'cygwin))
|
||||
(with-timeout (60 (ert-fail "Test timed out"))
|
||||
(ert-with-temp-directory directory
|
||||
(process-tests--with-processes processes
|
||||
|
@ -763,7 +763,7 @@ FD_SETSIZE file descriptors (Bug#24325)."
|
|||
"Check that Emacs doesn't crash when trying to use more than
|
||||
FD_SETSIZE file descriptors (Bug#24325)."
|
||||
;; This test cannot be run if PTYs aren't supported.
|
||||
(skip-unless (not (eq system-type 'windows-nt)))
|
||||
(skip-when (eq system-type 'windows-nt))
|
||||
(with-timeout (60 (ert-fail "Test timed out"))
|
||||
(process-tests--with-processes processes
|
||||
;; In order to use `make-serial-process', we need to create some
|
||||
|
|
Loading…
Add table
Reference in a new issue