aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilip Kaludercic2022-10-20 20:00:32 +0200
committerPhilip Kaludercic2022-10-20 20:00:32 +0200
commit37bfb623e4b253443e8280c3de4ff91f8db5f51b (patch)
tree39e48d687ace2d9b88b10bf82412301c4e723743
parente08e9bc40f2c309bf119659a6496759493bd35e1 (diff)
parent937ae0cf55d31c332fba3d96061e2ac3653e5437 (diff)
downloademacs-37bfb623e4b253443e8280c3de4ff91f8db5f51b.tar.gz
emacs-37bfb623e4b253443e8280c3de4ff91f8db5f51b.zip
Merge remote-tracking branch 'origin/master' into feature/package+vc
-rwxr-xr-xadmin/automerge23
-rwxr-xr-xadmin/diff-tar-files8
-rw-r--r--admin/emacs-shell-lib87
-rwxr-xr-xadmin/make-manuals13
-rwxr-xr-xadmin/update_autogen20
-rwxr-xr-xadmin/upload-manuals10
-rw-r--r--doc/emacs/maintaining.texi7
-rw-r--r--doc/emacs/programs.texi14
-rw-r--r--doc/lispref/functions.texi38
-rw-r--r--doc/lispref/modes.texi4
-rw-r--r--doc/misc/Makefile.in2
-rw-r--r--doc/misc/eglot.texi1138
-rw-r--r--etc/NEWS10
-rw-r--r--etc/PROBLEMS11
-rwxr-xr-xlib-src/rcs2log2
-rw-r--r--lisp/emacs-lisp/comp.el3
-rw-r--r--lisp/eshell/esh-util.el2
-rw-r--r--lisp/info-look.el1
-rw-r--r--lisp/leim/quail/indian.el161
-rw-r--r--lisp/leim/quail/slovak.el125
-rw-r--r--lisp/menu-bar.el4
-rw-r--r--lisp/net/ldap.el8
-rw-r--r--lisp/play/zone.el8
-rw-r--r--lisp/progmodes/cc-engine.el35
-rw-r--r--lisp/progmodes/cc-fonts.el17
-rw-r--r--lisp/progmodes/cc-mode.el5
-rw-r--r--lisp/progmodes/eglot.el3461
-rw-r--r--lisp/progmodes/modula2.el63
-rw-r--r--lisp/progmodes/perl-mode.el9
-rw-r--r--lisp/subr.el9
-rw-r--r--src/window.c17
-rw-r--r--src/xterm.c146
-rw-r--r--test/lisp/image/wallpaper-tests.el7
-rw-r--r--test/lisp/progmodes/cperl-mode-resources/here-docs.pl66
34 files changed, 5396 insertions, 138 deletions
diff --git a/admin/automerge b/admin/automerge
index c7c17dfb5ec..d2c92948e17 100755
--- a/admin/automerge
+++ b/admin/automerge
@@ -35,18 +35,7 @@
35## it with the -d option in the repository directory, in case a pull 35## it with the -d option in the repository directory, in case a pull
36## updates this script while it is working. 36## updates this script while it is working.
37 37
38set -o nounset 38source "${0%/*}/emacs-shell-lib"
39
40die () # write error to stderr and exit
41{
42 [ $# -gt 0 ] && echo "$PN: $*" >&2
43 exit 1
44}
45
46PN=${0##*/} # basename of script
47PD=${0%/*}
48
49[ "$PD" = "$0" ] && PD=. # if PATH includes PWD
50 39
51usage () 40usage ()
52{ 41{
@@ -129,13 +118,7 @@ OPTIND=1
129[ "$test" ] && build=1 118[ "$test" ] && build=1
130 119
131 120
132if [ -x "$(command -v mktemp)" ]; then 121tempfile="$(emacs_mktemp)"
133 tempfile=$(mktemp "/tmp/$PN.XXXXXXXXXX")
134else
135 tempfile=/tmp/$PN.$$
136fi
137
138trap 'rm -f $tempfile 2> /dev/null' EXIT
139 122
140 123
141[ -e Makefile ] && [ "$build" ] && { 124[ -e Makefile ] && [ "$build" ] && {
@@ -263,5 +246,3 @@ git push || die "push error"
263 246
264 247
265exit 0 248exit 0
266
267### automerge ends here
diff --git a/admin/diff-tar-files b/admin/diff-tar-files
index 6ab39eab2f5..869c9421502 100755
--- a/admin/diff-tar-files
+++ b/admin/diff-tar-files
@@ -1,4 +1,4 @@
1#! /bin/sh 1#!/bin/bash
2 2
3# Copyright (C) 2001-2022 Free Software Foundation, Inc. 3# Copyright (C) 2001-2022 Free Software Foundation, Inc.
4 4
@@ -17,6 +17,7 @@
17# You should have received a copy of the GNU General Public License 17# You should have received a copy of the GNU General Public License
18# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 18# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
19 19
20source "${0%/*}/emacs-shell-lib"
20 21
21if [ $# != 2 ]; then 22if [ $# != 2 ]; then
22 cat <<EOF 23 cat <<EOF
@@ -31,9 +32,8 @@ fi
31old_tar=$1 32old_tar=$1
32new_tar=$2 33new_tar=$2
33 34
34old_tmp=/tmp/old.$$ 35old_tmp="$(emacs_mktemp ${PN}-old)"
35new_tmp=/tmp/new.$$ 36new_tmp="$(emacs_mktemp ${PN}-new)"
36trap "rm -f $old_tmp $new_tmp; exit 1" 1 2 15
37 37
38tar tf "$old_tar" | sed -e 's,^[^/]*,,' | sort > $old_tmp 38tar tf "$old_tar" | sed -e 's,^[^/]*,,' | sort > $old_tmp
39tar tf "$new_tar" | sed -e 's,^[^/]*,,' | sort > $new_tmp 39tar tf "$new_tar" | sed -e 's,^[^/]*,,' | sort > $new_tmp
diff --git a/admin/emacs-shell-lib b/admin/emacs-shell-lib
new file mode 100644
index 00000000000..750f81e0577
--- /dev/null
+++ b/admin/emacs-shell-lib
@@ -0,0 +1,87 @@
1#!/bin/bash
2### emacs-shell-lib - shared code for Emacs shell scripts
3
4## Copyright (C) 2022 Free Software Foundation, Inc.
5
6## Author: Stefan Kangas <stefankangas@gmail.com>
7
8## This file is part of GNU Emacs.
9
10## GNU Emacs is free software: you can redistribute it and/or modify
11## it under the terms of the GNU General Public License as published by
12## the Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14
15## GNU Emacs is distributed in the hope that it will be useful,
16## but WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18## GNU General Public License for more details.
19
20## You should have received a copy of the GNU General Public License
21## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
22
23### Code:
24
25# Set an explicit umask.
26umask 077
27
28# Treat unset variables as an error.
29set -o nounset
30
31# Exit immediately on error.
32set -o errexit
33
34# Avoid non-standard command output from non-C locales.
35unset LANG LC_ALL LC_MESSAGES
36
37PN=${0##*/} # basename of script
38PD=${0%/*} # script directory
39
40[ "$PD" = "$0" ] && PD=. # if PATH includes PWD
41
42die () # write error to stderr and exit
43{
44 [ $# -gt 0 ] && echo "$PN: $@" >&2
45 exit 1
46}
47
48emacs_tempfiles=()
49
50emacs_tempfiles_cleanup ()
51{
52 for file in ${emacs_tempfiles[@]}; do
53 rm -f "${file}" 2> /dev/null
54 done
55}
56
57trap '
58 ret=$?
59 emacs_tempfiles_cleanup
60 exit $ret
61' EXIT
62
63emacs_mktemp ()
64{
65 local readonly file="${1-}"
66 local tempfile
67 local prefix
68
69 if [ -z "$file" ]; then
70 prefix="$PN"
71 else
72 prefix="$1"
73 fi
74
75 if [ -x "$(command -v mktemp)" ]; then
76 tempfile=$(mktemp "${TMPDIR-/tmp}/${prefix}.XXXXXXXXXX")
77 else
78 tempfile="${TMPDIR-/tmp}/${prefix}.$RANDOM$$"
79 (umask 077 && touch "$tempfile")
80 fi
81
82 [ -z "${tempfile}" ] && die "Creating temporary file failed"
83
84 emacs_tempfiles+=("${tempfile}")
85
86 echo "$tempfile"
87}
diff --git a/admin/make-manuals b/admin/make-manuals
index cb0c00a423f..a252bf20f1e 100755
--- a/admin/make-manuals
+++ b/admin/make-manuals
@@ -33,15 +33,7 @@
33 33
34### Code: 34### Code:
35 35
36set -o nounset 36source "${0%/*}/emacs-shell-lib"
37
38die () # write error to stderr and exit
39{
40 [ $# -gt 0 ] && echo "$PN: $@" >&2
41 exit 1
42}
43
44PN=${0##*/} # basename of script
45 37
46usage () 38usage ()
47{ 39{
@@ -96,8 +88,7 @@ OPTIND=1
96[ -e admin/admin.el ] || die "admin/admin.el not found" 88[ -e admin/admin.el ] || die "admin/admin.el not found"
97 89
98 90
99tempfile=/tmp/$PN.$$ 91tempfile="$(emacs_mktemp)"
100trap "rm -f $tempfile 2> /dev/null" EXIT
101 92
102 93
103[ "$continue" ] || rm -rf $outdir 94[ "$continue" ] || rm -rf $outdir
diff --git a/admin/update_autogen b/admin/update_autogen
index d1f49d9f25e..55e11be95c7 100755
--- a/admin/update_autogen
+++ b/admin/update_autogen
@@ -32,18 +32,7 @@
32 32
33### Code: 33### Code:
34 34
35set -o nounset 35source "${0%/*}/emacs-shell-lib"
36
37die () # write error to stderr and exit
38{
39 [ $# -gt 0 ] && echo "$PN: $@" >&2
40 exit 1
41}
42
43PN=${0##*/} # basename of script
44PD=${0%/*}
45
46[ "$PD" = "$0" ] && PD=. # if PATH includes PWD
47 36
48## This should be the admin directory. 37## This should be the admin directory.
49cd $PD || exit 38cd $PD || exit
@@ -102,10 +91,7 @@ done
102 91
103[ "$basegen" ] || die "internal error" 92[ "$basegen" ] || die "internal error"
104 93
105tempfile=/tmp/$PN.$$ 94tempfile="$(emacs_mktemp)"
106
107trap 'rm -f $tempfile 2> /dev/null' EXIT
108
109 95
110while getopts ":hcfqA:CL" option ; do 96while getopts ":hcfqA:CL" option ; do
111 case $option in 97 case $option in
@@ -312,5 +298,3 @@ commit "loaddefs" $modified || die "commit error"
312 298
313 299
314exit 0 300exit 0
315
316### update_autogen ends here
diff --git a/admin/upload-manuals b/admin/upload-manuals
index 50336ee64c0..04f7c3acc72 100755
--- a/admin/upload-manuals
+++ b/admin/upload-manuals
@@ -36,15 +36,7 @@
36 36
37### Code: 37### Code:
38 38
39set -o nounset 39source "${0%/*}/emacs-shell-lib"
40
41die () # write error to stderr and exit
42{
43 [ $# -gt 0 ] && echo "$PN: $@" >&2
44 exit 1
45}
46
47PN=${0##*/} # basename of script
48 40
49usage () 41usage ()
50{ 42{
diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi
index ad4a3ea3506..94171b3a089 100644
--- a/doc/emacs/maintaining.texi
+++ b/doc/emacs/maintaining.texi
@@ -2095,6 +2095,13 @@ that it only knows about subunits that were loaded into the
2095interpreter.) 2095interpreter.)
2096 2096
2097@item 2097@item
2098If Eglot is activated for the current buffer's project
2099(@pxref{Projects}) and the current buffer's major mode, Eglot consults
2100an external language server program and provides the data supplied by
2101the server regarding the definitions of the identifiers in the
2102project. @xref{Eglot Features,,, eglot, Eglot: The Emacs LSP Client}.
2103
2104@item
2098An external program can extract references by scanning the relevant 2105An external program can extract references by scanning the relevant
2099files, and build a database of these references. A backend can then 2106files, and build a database of these references. A backend can then
2100access this database whenever it needs to list or look up references. 2107access this database whenever it needs to list or look up references.
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 818deb39415..b5e577d96a4 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -287,6 +287,13 @@ they occur in the buffer; if you want alphabetic sorting, use the
287symbol @code{imenu--sort-by-name} as the value. You can also 287symbol @code{imenu--sort-by-name} as the value. You can also
288define your own comparison function by writing Lisp code. 288define your own comparison function by writing Lisp code.
289 289
290 If Eglot is activated for the current buffer's project
291(@pxref{Projects}) and the current buffer's major mode, Eglot provides
292its own facility for producing the buffer's index based on the
293analysis of the program source by the language-server which manages
294the current buffer. @xref{Eglot Features,,, eglot, Eglot: The Emacs
295LSP Client}.
296
290 Imenu provides the information to guide Which Function mode 297 Imenu provides the information to guide Which Function mode
291@ifnottex 298@ifnottex
292(@pxref{Which Function}). 299(@pxref{Which Function}).
@@ -1439,6 +1446,13 @@ candidates:
1439 1446
1440@itemize @bullet 1447@itemize @bullet
1441@item 1448@item
1449If Eglot is activated for the current buffer's project
1450(@pxref{Projects}) and the current buffer's major mode, the command
1451tries to use the corresponding language server for producing the list
1452of completion candidates. @xref{Eglot Features,,, eglot, Eglot: The
1453Emacs LSP Client}.
1454
1455@item
1442If Semantic mode is enabled (@pxref{Semantic}), the command tries to 1456If Semantic mode is enabled (@pxref{Semantic}), the command tries to
1443use the Semantic parser data for completion. 1457use the Semantic parser data for completion.
1444 1458
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 8b858e0aa01..7ffde7d43d1 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -533,6 +533,44 @@ Instead, use the @code{advertised-calling-convention} declaration
533compiler emit a warning message when it compiles Lisp programs which 533compiler emit a warning message when it compiles Lisp programs which
534use the deprecated calling convention. 534use the deprecated calling convention.
535 535
536@cindex computed documentation string
537@kindex :documentation
538Documentation strings are usually static, but occasionally it can be
539necessary to generate them dynamically. In some cases you can do so
540by writing a macro which generates at compile time the code of the
541function, including the desired documentation string. But you can
542also generate the docstring dynamically by writing
543@code{(:documentation @var{form})} instead of the documentation
544string. This will evaluate @var{form} at run-time when the function
545is defined and use it as the documentation string@footnote{This only
546works in code using @code{lexical-binding}.}. You can also compute
547the documentation string on the fly when it is requested, by setting
548the @code{function-documentation} property of the function's symbol to
549a Lisp form that evaluates to a string.
550
551For example:
552@example
553@group
554(defun adder (x)
555 (lambda (y)
556 (:documentation (format "Add %S to the argument Y." x))
557 (+ x y)))
558(defalias 'adder5 (adder 5))
559(documentation 'adder5)
560 @result{} "Add 5 to the argument Y."
561@end group
562
563@group
564(put 'adder5 'function-documentation
565 '(concat (documentation (symbol-function 'adder5) 'raw)
566 " Consulted at " (format-time-string "%H:%M:%S")))
567(documentation 'adder5)
568 @result{} "Add 5 to the argument Y. Consulted at 15:52:13"
569(documentation 'adder5)
570 @result{} "Add 5 to the argument Y. Consulted at 15:52:18"
571@end group
572@end example
573
536@node Function Names 574@node Function Names
537@section Naming a Function 575@section Naming a Function
538@cindex function definition 576@cindex function definition
diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi
index 73c5fcd44d7..434538dcbf8 100644
--- a/doc/lispref/modes.texi
+++ b/doc/lispref/modes.texi
@@ -1851,7 +1851,9 @@ to enable or disable the buffer-local minor mode @var{mode} in all (or
1851some; see below) buffers. It also executes the @var{body} forms. To 1851some; see below) buffers. It also executes the @var{body} forms. To
1852turn on the minor mode in a buffer, it uses the function 1852turn on the minor mode in a buffer, it uses the function
1853@var{turn-on}; to turn off the minor mode, it calls @var{mode} with 1853@var{turn-on}; to turn off the minor mode, it calls @var{mode} with
1854@minus{}1 as argument. 1854@minus{}1 as argument. (The function @var{turn-on} is a separate
1855function so it could determine whether to enable the minor mode or not
1856when it is not a priori clear that it should always be enabled.)
1855 1857
1856Globally enabling the mode also affects buffers subsequently created 1858Globally enabling the mode also affects buffers subsequently created
1857by visiting files, and buffers that use a major mode other than 1859by visiting files, and buffers that use a major mode other than
diff --git a/doc/misc/Makefile.in b/doc/misc/Makefile.in
index 1d881a5fc7f..b6eef7ea799 100644
--- a/doc/misc/Makefile.in
+++ b/doc/misc/Makefile.in
@@ -68,7 +68,7 @@ DOCMISC_W32 = @DOCMISC_W32@
68 68
69## Info files to build and install on all platforms. 69## Info files to build and install on all platforms.
70INFO_COMMON = auth autotype bovine calc ccmode cl \ 70INFO_COMMON = auth autotype bovine calc ccmode cl \
71 dbus dired-x ebrowse ede ediff edt eieio \ 71 dbus dired-x ebrowse ede ediff edt eglot eieio \
72 emacs-mime epa erc ert eshell eudc efaq eww \ 72 emacs-mime epa erc ert eshell eudc efaq eww \
73 flymake forms gnus emacs-gnutls htmlfontify idlwave ido info.info \ 73 flymake forms gnus emacs-gnutls htmlfontify idlwave ido info.info \
74 mairix-el message mh-e modus-themes newsticker nxml-mode octave-mode \ 74 mairix-el message mh-e modus-themes newsticker nxml-mode octave-mode \
diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
new file mode 100644
index 00000000000..033464f9909
--- /dev/null
+++ b/doc/misc/eglot.texi
@@ -0,0 +1,1138 @@
1\input texinfo @c -*-texinfo-*-
2@c %**start of header
3@setfilename ../../eglot.info
4@settitle Eglot: The Emacs Client for the Language Server Protocol
5@include docstyle.texi
6@syncodeindex vr cp
7@syncodeindex fn cp
8@c %**end of header
9
10@copying
11This manual is for Eglot, the Emacs LSP client.
12
13Copyright @copyright{} 2022 Free Software Foundation, Inc.
14
15@quotation
16Permission is granted to copy, distribute and/or modify this document
17under the terms of the GNU Free Documentation License, Version 1.3 or
18any later version published by the Free Software Foundation; with no
19Invariant Sections, with the Front-Cover Texts being ``A GNU Manual'',
20and with the Back-Cover Texts as in (a) below. A copy of the license
21is included in the section entitled ``GNU Free Documentation License''.
22
23(a) The FSF's Back-Cover Text is: ``You have the freedom to copy and
24modify this GNU manual.''
25@end quotation
26@end copying
27
28@dircategory Emacs misc features
29@direntry
30* Eglot: (eglot). Language Server Protocol client for Emacs.
31@end direntry
32
33@titlepage
34@sp 4
35@c The title is printed in a large font.
36@center @titlefont{User's Guide}
37@sp 1
38@center @titlefont{to}
39@sp 1
40@center @titlefont{Eglot: The Emacs LSP Client}
41@ignore
42@sp 2
43@center release 1.8
44@c -release-
45@end ignore
46@sp 3
47@center Jo@~ao T@'avora & Eli Zaretskii
48@c -date-
49
50@page
51@vskip 0pt plus 1filll
52@insertcopying
53@end titlepage
54
55@contents
56
57@ifnottex
58@node Top
59@top Eglot
60
61@cindex LSP
62@cindex language server protocol
63Eglot is the Emacs client for the @dfn{Language Server Protocol}
64(@acronym{LSP}). The name ``Eglot'' is an acronym that stands for
65@ifhtml
66``@emph{E}macs Poly@emph{glot}''.
67@end ifhtml
68@ifnothtml
69``Emacs polyGLOT''.
70@end ifnothtml
71@footnote{
72A @dfn{polyglot} is a
73person who is able to use several languages.
74} Eglot provides infrastructure and a set of commands for enriching
75the source code editing capabilities of Emacs via LSP. LSP is a
76standardized communications protocol between source code editors (such
77as Emacs) and language servers---programs external to Emacs which
78analyze the source code on behalf of Emacs. The protocol allows Emacs
79to receive various source code services from the server, such as
80description and location of functions calls, types of variables, class
81definitions, syntactic errors, etc. This way, Emacs doesn't need to
82implement the language-specific parsing and analysis capabilities in
83its own code, but is still capable of providing sophisticated editing
84features that rely on such capabilities, such as automatic code
85completion, go-to definition of function/class, documentation of
86symbol at-point, refactoring, on-the-fly diagnostics, and more.
87
88Eglot itself is completely language-agnostic, but it can support any
89programming language for which there is a language server and an Emacs
90major mode.
91
92This manual documents how to configure, use, and customize Eglot.
93
94@insertcopying
95
96@menu
97* Quick Start:: For the impatient.
98* Eglot and LSP Servers:: How to work with language servers.
99* Using Eglot:: Important Eglot commands and variables.
100* Customizing Eglot:: Eglot customization and advanced features.
101* Troubleshooting Eglot:: Troubleshooting and reporting bugs.
102* GNU Free Documentation License:: The license for this manual.
103* Index::
104@end menu
105@end ifnottex
106
107@node Quick Start
108@chapter Quick Start
109@cindex quick start
110
111This chapter provides concise instructions for setting up and using
112Eglot with your programming project in common usage scenarios. For
113more detailed instructions regarding Eglot setup, @pxref{Eglot and LSP
114Servers}. @xref{Using Eglot}, for detailed description of using Eglot,
115and see @ref{Customizing Eglot}, for adapting Eglot to less common use
116patterns.
117
118Here's how to start using Eglot with your programming project:
119
120@enumerate
121@item
122Select and install a language server.
123
124Eglot comes pre-configured with many popular language servers, see the
125value of @code{eglot-server-programs}. If the server(s) mentioned
126there satisfy your needs for the programming language(s) with which
127you want to use Eglot, you just need to make sure those servers are
128installed on your system. Alternatively, install one or more servers
129of your choice and add them to the value of
130@code{eglot-server-programs}, as described in @ref{Setting Up LSP
131Servers}.
132
133@item
134Turn on Eglot for your project.
135
136To start using Eglot for a project, type @kbd{M-x eglot @key{RET}} in
137a buffer visiting any file that belongs to the project. This starts
138the language server configured for the programming language of that
139buffer, and causes Eglot to start managing all the files of the
140project which use the same programming language. The notion of a
141``project'' used by Eglot is the same Emacs uses (@pxref{Projects,,,
142emacs, GNU Emacs Manual}): in the simplest case, the ``project'' is
143the single file you are editing, but it can also be all the files in a
144single directory or a directory tree under some version control
145system, such as Git.
146
147Alternatively, you can start Eglot automatically from the major-mode
148hook of the mode used for the programming language; see @ref{Starting
149Eglot}.
150
151@item
152Use Eglot.
153
154Most Eglot facilities are integrated into Emacs features, such as
155ElDoc, Flymake, Xref, and Imenu. However, Eglot also provides
156commands of its own, mainly to perform tasks by the LSP server, such
157as @kbd{M-x eglot-rename} (to rename an identifier across the entire
158project), @kbd{M-x eglot-format} (to reformat and reindent code), and
159some others. @xref{Eglot Commands}, for the detailed list of Eglot
160commands.
161
162@item
163That's it!
164@end enumerate
165
166@node Eglot and LSP Servers
167@chapter Eglot and LSP Servers
168
169This chapter describes how to set up Eglot for your needs, and how to
170start it.
171
172@menu
173* Setting Up LSP Servers:: How to configure LSP servers for your needs.
174* Starting Eglot:: Ways of starting Eglot for your project.
175* Shutting Down LSP Servers::
176@end menu
177
178@node Setting Up LSP Servers
179@section Setting Up LSP Servers
180@cindex setting up LSP server for Eglot
181@cindex language server for Eglot
182
183For Eglot to be useful, it must first be combined with a suitable
184language server. Usually, that means running the server program
185locally as a child process of Emacs (@pxref{Processes,,, elisp, GNU
186Emacs Lisp Reference Manual}) and communicating with it via the
187standard input and output streams.
188
189The language server program must be installed separately, and is not
190further discussed in this manual; refer to the documentation of the
191particular server(s) you want to install.
192
193To use a language server, Eglot must know how to start it and which
194programming languages each server supports. This information is
195provided by the variable @code{eglot-server-programs}.
196
197@defvar eglot-server-programs
198This variable associates major modes with names and command-line
199arguments of the language server programs corresponding to the
200programming language of each major mode. It provides all the
201information that Eglot needs to know about the programming language of
202the source you are editing.
203
204The value of the variable is an alist, whose elements are of the form
205@w{@code{(@var{major-mode} . @var{server})}}.
206
207The @var{major-mode} of the alist elements can be either a symbol of
208an Emacs major mode or a list of the form @w{@code{(@var{mode}
209:language-id @var{id})}}, with @var{mode} being a major-mode symbol
210and @var{id} a string that identifies the language to the server (if
211Eglot cannot by itself convert the major-mode to the language
212identifier string required by the server). In addition,
213@var{major-mode} can be a list of several major modes specified in one
214of the above forms -- this means a running instance of the associated
215server is responsible for files of multiple major modes or languages
216in the project.
217
218The @var{server} part of the alist elements can be one of the
219following:
220
221@table @code
222@item (@var{program} @var{args}@dots{})
223This says to invoke @var{program} with zero or more arguments
224@var{args}; the program is expected to communicate with Emacs via the
225standard input and standard output streams.
226
227@item (@var{program} @var{args}@dots{} :initializationOptions @var{options}@dots{})
228Like above, but with @var{options} specifying the options to be
229used for constructing the @samp{initializationOptions} JSON object for
230the server. @var{options} can also be a function of one argument, in
231which case it will be called with the server instance as the argument,
232and should return the JSON object to use for initialization.
233
234@item (@var{host} @var{port} @var{args}@dots{})
235Here @var{host} is a string and @var{port} is a positive integer
236specifying a TCP connection to a remote server. The @var{args} are
237passed to @code{open-network-stream}, e.g.@: if the connection needs
238to use encryption or other non-default parameters (@pxref{Network,,,
239elisp, GNU Emacs Lisp Reference Manual}).
240
241@item (@var{program} @var{args}@dots{} :autoport @var{moreargs}@dots{})
242@var{program} is started with a command line constructed from
243@var{args} followed by an available server port and the rest of
244arguments in @var{moreargs}; Eglot then establishes a TCP connection
245with the server via that port on the local host.
246
247@item @var{function}
248This should be a function of a single argument: non-@code{nil} if the
249connection was requested interactively (e.g., by the @code{eglot}
250command), otherwise @code{nil}. The function should return a value of
251any of the forms described above. This allows interaction with the
252user for determining the program to start and its command-line
253arguments.
254@end table
255
256@end defvar
257
258Eglot comes with a fairly complete set of associations of major-modes
259to popular language servers predefined. If you need to add server
260associations to the default list, use @code{add-to-list}. For
261example, if there is a hypothetical language server program
262@command{fools} for the language @code{Foo} which is supported by an
263Emacs major-mode @code{foo-mode}, you can add it to the alist like
264this:
265
266@lisp
267(add-to-list 'eglot-server-programs
268 '(foo-mode . ("fools" "--stdio")))
269@end lisp
270
271This will invoke the program @command{fools} with the command-line
272argument @option{--stdio} in support of editing source files for which
273Emacs turns on @code{foo-mode}, and will communicate with the program
274via the standard streams. As usual with invoking programs, the
275executable file @file{fools} should be in one of the directories
276mentioned by the @code{exec-path} variable (@pxref{Subprocess
277Creation,,, elisp, GNU Emacs Lisp Reference Manual}), for Eglot to be
278able to find it.
279
280@node Starting Eglot
281@section Starting Eglot
282@cindex starting Eglot
283@cindex activating Eglot for a project
284
285@findex eglot
286The most common way to start Eglot is to simply visit a source file of
287a given language and use the command @kbd{M-x eglot}. This starts the
288language server suitable for the visited file's major-mode, and
289attempts to connect to it. If the connection to the language server
290is successful, you will see the @code{[eglot:@var{project}]} indicator
291on the mode line which reflects the server that was started. If the
292server program couldn't be started or connection to it failed, you
293will see an error message; in that case, try to troubleshoot the
294problem as described in @ref{Troubleshooting Eglot}. Once a language
295server was successfully started and Eglot connected to it, you can
296immediately start using the Emacs features supported by Eglot, as
297described in @ref{Eglot Features}.
298
299A single Eglot session for a certain major-mode usually serves all the
300buffers under that mode which visit files from the same project, so
301you don't need to invoke @kbd{M-x eglot} again when you visit another
302file from the same project which is edited using the same major-mode.
303This is because Eglot uses the Emacs project infrastructure, as
304described in @ref{Eglot and Buffers}, and this knows about files that
305belong to the same project. Thus, after starting an Eglot session for
306some buffer, that session is automatically reused when visiting files
307in the same project with the same major-mode.
308
309@findex eglot-ensure
310Alternatively, you could configure Eglot to start automatically for
311one or more major-modes from the respective mode hooks. Here's an
312example for a hypothetical @code{foo-mode}:
313
314@lisp
315 (add-hook 'foo-mode-hook 'eglot-ensure)
316@end lisp
317
318@noindent
319The function @code{eglot-ensure} will start an Eglot session for each
320buffer in which @code{foo-mode} is turned on, if there isn't already
321an Eglot session that handles the buffer. Note that this variant of
322starting an Eglot session is non-interactive, so it should be used
323only when you are confident that Eglot can be started reliably for any
324file which may be visited with the major-mode in question.
325
326When Eglot connects to a language server for the first time in an
327Emacs session, it runs the hook @code{eglot-connect-hook}
328(@pxref{Eglot Variables}).
329
330@node Shutting Down LSP Servers
331@section Shutting Down LSP Servers
332@cindex shutting down LSP server
333
334When Eglot is turned on, it arranges for turning itself off
335automatically if the language server process terminates. Turning off
336Eglot means that it shuts down the server connection, ceases its
337management of all the buffers that use the server connection which was
338terminated, deactivates its minor mode, and restores the original
339values of the Emacs variables that Eglot changed when it was turned
340on. @xref{Eglot and Buffers}, for more details of what Eglot
341management of a buffer entails.
342
343@findex eglot-shutdown
344You can also shut down a language server manually, by using the
345command @kbd{M-x eglot-shutdown}. This prompts for the server (unless
346there's only one connection and it's used in the current buffer), and
347then shuts it down. By default, it also kills the server's events
348buffer (@pxref{Troubleshooting Eglot}), but a prefix argument prevents
349that.
350
351Alternatively, you can customize the variable
352@code{eglot-autoshutdown} to a non-@code{nil} value, in which case
353Eglot will automatically shut down the language server process when
354the last buffer served by that language server is killed. The default
355of this variable is @code{nil}, so that visiting another file would
356automatically activate Eglot even when the project which started Eglot
357with the server no longer has any buffer associated with it. This
358default allows you to start a server only once in each Emacs session.
359
360@node Using Eglot
361@chapter Using Eglot
362
363This chapter describes in detail the features that Eglot provides and
364how it does that. It also provides reference sections for Eglot
365commands and variables.
366
367@menu
368* Eglot Features::
369* Eglot and Buffers::
370* Eglot Commands::
371* Eglot Variables::
372@end menu
373
374@node Eglot Features
375@section Eglot Features
376@cindex features in buffers supported by Eglot
377
378Once Eglot is enabled in a buffer, it uses LSP and the language-server
379capabilities to activate, enable, and enhance modern IDE features in
380Emacs. The features themselves are usually provided via other Emacs
381packages. Here's the list of the main features that Eglot enables and
382provides:
383
384@itemize @bullet
385@item
386At-point documentation: when point is at or near a symbol or an
387identifier, the information about the symbol/identifier, such as the
388signature of a function or class method and server-generated
389diagnostics, is made available via the ElDoc package (@pxref{Lisp
390Doc,,, emacs, GNU Emacs Manual}). This allows major modes to provide
391extensive help and documentation about the program identifiers.
392
393@item
394On-the-fly diagnostic annotations with server-suggested fixes, via the
395Flymake package (@pxref{Top,,, flymake, GNU Flymake manual}). This
396improves and enhances the Flymake diagnostics, replacing the other
397Flymake backends.
398
399@item
400Finding definitions and uses of identifiers, via Xref (@pxref{Xref,,,
401emacs, GNU Emacs Manual}). Eglot provides a backend for the Xref
402capabilities which uses the language-server understanding of the
403program source. In particular, it eliminates the need to generate
404tags tables (@pxref{Tags tables,,, emacs, GNU Emacs Manual}) for
405languages which are only supported by the @code{etags} backend.
406
407@item
408Buffer navigation by name of function, class, method, etc., via Imenu
409(@pxref{Imenu,,, emacs, GNU Emacs Manual}). Eglot provides its own
410variant of @code{imenu-create-index-function}, which generates the
411index for the buffer based on language-server program source analysis.
412
413@item
414Enhanced completion of symbol at point by the
415@code{completion-at-point} command (@pxref{Symbol Completion,,, emacs,
416GNU Emacs Manual}). This uses the language-server's parser data for
417the completion candidates.
418
419@item
420Automatic reformatting of source code as you type it. This is similar
421to what the @code{eglot-format} command does (see below), but is
422activated automatically as you type.
423
424@item
425If a completion package such as @code{company-mode}, a popular
426third-party completion package (or any other completion package), is
427installed, Eglot enhances it by providing completion candidates based
428on the language-server analysis of the source code.
429(@code{company-mode} can be installed from GNU ELPA.)
430
431@item
432If @code{yasnippet}, a popular third-party package for automatic
433insertion of code templates (snippets), is installed, and the language
434server supports snippet completion candidates, Eglot arranges for the
435completion package to instantiate these snippets using
436@code{yasnippet}. (@code{yasnippet} can be installed from GNU ELPA.)
437
438@item
439If the popular third-party package @code{markdown-mode} is installed,
440and the server provides at-point documentation formatted as Markdown
441in addition to plain text, Eglot arranges for the ElDoc package to
442enrich this text with fontifications and other nice formatting before
443displaying it to the user. This makes the documentation shown by
444ElDoc look nicer on display.
445
446@item
447In addition to enabling and enhancing other features and packages,
448Eglot also provides a small number of user commands based directly on
449the capabilities of language servers. These commands are:
450
451@table @kbd
452@item M-x eglot-rename
453This prompts for a new name for the symbol at point, and then modifies
454all the project source files to rename the symbol to the new name,
455based on editing data received from the language-server. @xref{Eglot
456and Buffers}, for the details of how project files are defined.
457
458@item M-x eglot-format
459This reformats and prettifies the current active region according to
460source formatting rules of the language-server. If the region is not
461active, it reformats the entire buffer instead.
462
463@item M-x eglot-format-buffer
464This reformats and prettifies the current buffer according to source
465formatting rules of the language-server.
466
467@cindex code actions
468@item M-x eglot-code-actions
469@itemx M-x eglot-code-action-organize-imports
470@itemx M-x eglot-code-action-quickfix
471@itemx M-x eglot-code-action-extract
472@itemx M-x eglot-code-action-inline
473@itemx M-x eglot-code-action-rewrite
474These command allow you to invoke the so-called @dfn{code actions}:
475requests for the language-server to provide editing commands for
476various code fixes, typically either to fix an error diagnostic or to
477beautify/refactor code. For example,
478@code{eglot-code-action-organize-imports} rearranges the program
479@dfn{imports}---declarations of modules whose capabilities the program
480uses. These commands affect all the files that belong to the
481project. The command @kbd{M-x eglot-code-actions} will pop up a menu
482of code applicable actions at point.
483@end table
484
485@end itemize
486
487Not all servers support the full set of LSP capabilities, but most of
488them support enough to enable the basic set of features mentioned
489above. Conversely, some servers offer capabilities for which no
490equivalent Emacs package exists yet, and so Eglot cannot (yet) expose
491these capabilities to Emacs users.
492
493@node Eglot and Buffers
494@section Buffers, Projects, and Eglot
495@cindex buffers managed by Eglot
496@cindex projects and Eglot
497
498@cindex workspace
499One of the main strong points of using a language server is that a
500language server has a broad view of the program: it considers more
501than just the single source file you are editing. Ideally, the
502language server should know about all the source files of your program
503which are written in the language supported by the server. In the
504language-server parlance, the set of the source files of a program is
505known as a @dfn{workspace}. The Emacs equivalent of a workspace is a
506@dfn{project} (@pxref{Projects,,, emacs, GNU Emacs Manual}). Eglot
507fully supports Emacs projects, and considers the file in whose buffer
508Eglot is turned on as belonging to a project. In the simplest case,
509that file is the entire project, i.e.@: your project consists of a
510single file. But there are other more complex projects:
511
512@itemize @bullet
513@item
514A single-directory project: several source files in a single common
515directory.
516
517@item
518A VC project: source files in a directory hierarchy under some VCS,
519e.g.@: a VCS repository (@pxref{Version Control,,, emacs, GNU Emacs
520Manual}).
521
522@item
523An EDE project: source files in a directory hierarchy managed via the
524Emacs Development Environment (@pxref{EDE,,, emacs, GNU Emacs
525Manual}).
526@end itemize
527
528Eglot uses the Emacs's project management infrastructure to figure out
529which files and buffers belong to what project, so any kind of project
530supported by that infrastructure is automatically supported by Eglot.
531
532When Eglot starts a server program, it does so in the project's root
533directory, which is usually the top-level directory of the project's
534directory hierarchy. This ensures the language server has the same
535comprehensive view of the project's files as you do.
536
537For example, if you visit the file @file{~/projects/fooey/lib/x.foo}
538and @file{x.foo} belongs to a project rooted at
539@file{~/projects/fooey} (perhaps because a @file{.git} directory
540exists there), then @kbd{M-x eglot} causes the server program to start
541with that root as the current working directory. The server then will
542analyze not only the file @file{lib/x.foo} you visited, but likely
543also all the other @file{*.foo} files under the
544@file{~/projects/fooey} directory.
545
546In some cases, additional information specific to a given project will
547need to be provided to the language server when starting it. The
548variable @code{eglot-workspace-configuration} (@pxref{Customizing
549Eglot}) exists for that purpose. It specifies the parameters and
550their values to communicate to each language server which needs that.
551
552When Eglot is active for a project, it performs several background
553activities on behalf of the project and its buffers:
554
555@itemize @bullet
556@cindex mode-line indication of language server
557@cindex mouse clicks on mode-line, and Eglot
558@vindex eglot-menu
559@item
560All of the project's file-visiting buffers under the same major-mode
561are served by a single language-server connection. (If the project
562uses several programming languages, there will usually be a separate
563server connection for each group of files written in the same language
564and using the same Emacs major-mode.) Eglot adds the
565@samp{[eglot:@var{project}]} indication to the mode line of
566each such buffer, where @var{server} is the name of the server and
567@var{project} identifies the project by its root directory. Clicking
568the mouse on the Eglot mode-line indication activates a menu with
569server-specific items.
570
571@item
572For each buffer in which Eglot is active, it notifies the language
573server that Eglot is @dfn{managing} the file visited by that buffer.
574This tells the language server that the file's contents on disk may no
575longer be up-to-date due to unsaved edits. Eglot reports to the
576server any changes in the text of each managed buffer, to make the
577server aware of unsaved changes. This includes your editing of the
578buffer and also changes done automatically by other Emacs features and
579commands. Killing a buffer relinquishes its management by Eglot and
580notifies the server that the file on disk is up-to-date.
581
582@vindex eglot-managed-mode-hook
583@vindex eglot-managed-p
584@item
585Eglot turns on a special minor mode in each buffer it manages. This
586minor mode ensures the server is notified about files Eglot manages,
587and also arranges for other Emacs features supported by Eglot
588(@pxref{Eglot Features}) to receive information from the language
589server, by changing the settings of these features. Unlike other
590minor-modes, this special minor mode is not activated manually by the
591user, but automatically as result of starting an Eglot session for the
592buffer. However, this minor mode provides a hook variable
593@code{eglot-managed-mode-hook} that can be used to customize the Eglot
594management of the buffer. This hook is run both when the minor mode
595is turned on and when it's turned off; use the variable
596@code{eglot-managed-p} to tell if current buffer is still being
597managed or not. When Eglot stops managing the buffer, this minor mode
598is turned off, and all the settings that Eglot changed are restored to
599their original values.
600
601@item
602When you visit a file under the same project, whether an existing or a
603new file, its buffer is automatically added to the set of buffers
604managed by Eglot, and the server which supports the buffer's
605major-mode is notified about that. Thus, visiting a non-existent file
606@file{/home/joe/projects/fooey/lib/y.foo} in the above example will
607notify the server of the @file{*.foo} files' language that a new file
608was added to the project, even before the file appears on disk. The
609special Eglot minor mode is also turned on automatically in the buffer
610visiting the file.
611@end itemize
612
613@node Eglot Commands
614@section Eglot Commands
615@cindex commands, Eglot
616
617This section provides a reference of the most commonly used Eglot
618commands:
619
620@ftable @code
621@item M-x eglot
622This command adds the current buffer and the file it visits to the
623group of buffers and files managed by Eglot on behalf of a suitable
624language server. If a language server for the buffer's
625@code{major-mode} (@pxref{Major Modes,,, emacs, GNU Emacs Manual}) is
626not yet running, it will be started; otherwise the buffer and its file
627will be added to those managed by an existing server session.
628
629The command attempts to figure out the buffer's major mode and the
630suitable language server; in case it fails, it might prompt for the
631major mode to use and for the server program to start. If invoked
632with @kbd{C-u}, it always prompts for the server program, and if
633invoked with @kbd{C-u C-u}, it also prompts for the major mode.
634
635If the language server is successfully started and contacted, this
636command arranges for any other buffers belonging to the same project
637and using the same major mode to use the same language-server session.
638That includes any buffers created by visiting files after this command
639succeeds to connect to a language server.
640
641All the Emacs features that are capable of using Eglot services
642(@pxref{Eglot Features}) are automatically configured by this command
643to start using the language server via Eglot. To customize which
644Emacs features will be configured to use Eglot, use the
645@code{eglot-stay-out-of} option (@pxref{Customizing Eglot}).
646
647@item M-x eglot-reconnect
648This command shuts down the current connection to the language
649server and immediately restarts it using the same options used
650originally. This can sometimes be useful to unclog a partially
651malfunctioning server connection.
652
653@item M-x eglot-shutdown
654This command shuts down a language server. It prompts for a language
655server to shut down (unless there's only one server session, and it
656manages the current buffer). Then the command shuts down the server
657and stops managing the buffers the server was used for. Emacs
658features (@pxref{Eglot Features}) that Eglot configured to work with
659the language server are restored back to their original configuration.
660
661Normally, this command kills the buffers used for communicating with
662the language server, but if invoked with a prefix argument @kbd{C-u},
663the command doesn't kill those buffers, allowing them to be used for
664diagnostics and problem reporting (@pxref{Troubleshooting Eglot}).
665
666@item M-x eglot-shutdown-all
667This command shuts down all the language servers active in the current
668Emacs session. As with @code{eglot-shutdown}, invoking this command
669with a prefix argument avoids killing the buffers used for
670communications with the language servers.
671
672@item M-x eglot-rename
673This command renames the program symbol (a.k.a.@: @dfn{identifier}) at
674point to another name. It prompts for the new name of the symbol, and
675then modifies all the files in the project which arte managed by the
676language server of the current buffer to implement the renaming.
677
678@item M-x eglot-format
679This command reformats the active region according to the
680language-server rules. If no region is active, it reformats the
681entire current buffer.
682
683@item M-x eglot-format-buffer
684This command reformats the current buffer, in the same manner as
685@code{eglot-format} does.
686
687@item M-x eglot-code-actions
688@itemx mouse-1
689This command asks the server for any @dfn{code actions} applicable at
690point. It can also be invoked by @kbd{mouse-1} clicking on
691diagnostics provided by the server.
692
693@item M-x eglot-code-action-organize-imports
694@itemx M-x eglot-code-action-quickfix
695@itemx M-x eglot-code-action-extract
696@itemx M-x eglot-code-action-inline
697@itemx M-x eglot-code-action-rewrite
698These commands invoke specific code actions supported by the language
699server.
700@c FIXME: Need more detailed description of each action.
701@end ftable
702
703The following Eglot commands are used less commonly, mostly for
704diagnostic and troubleshooting purposes:
705
706@ftable @code
707@item M-x eglot-events-buffer
708This command pops up the events buffer used for communication with the
709language server of the current buffer.
710
711@item M-x eglot-stderr-buffer
712This command pops up the buffer with the debug info printed by the
713language server to its standard error stream.
714
715@item M-x eglot-forget-pending-continuations
716Forget pending requests for the server of the current buffer.
717@c FIXME: Better description of the need.
718
719@item M-x eglot-signal-didChangeConfiguration
720This command updates the language server configuration according to
721the current value of the variable @code{eglot-workspace-configuration}
722(@pxref{Customizing Eglot}).
723
724@item M-x eglot-clear-status
725Clear the last JSONRPC error for the server of the current buffer.
726Eglot keeps track of erroneous situations encountered by the server in
727its mode-line indication so that the user may inspect the
728communication leading up to it (@pxref{Troubleshooting Eglot}). If
729the situation is deemed uninteresting or temporary, this command can
730be used to ``forget'' the error. Note that the command @code{M-x
731eglot-reconnect} can sometimes be used to unclog a temporarily
732malfunctioning server.
733@end ftable
734
735As described in @ref{Eglot Features} most features associated with
736Eglot are actually provided by other Emacs packages and features, and
737Eglot only enhances them by allowing them to use the information
738coming from the language servers. For completeness, here's the list
739of commands of those other packages that are very commonly used in
740Eglot-managed buffers:
741
742@c Not @ftable, because the index entries should mention Eglot
743@table @code
744@cindex eldoc, and Eglot
745@cindex documentation using Eglot
746@item M-x eldoc
747Ask the ElDoc system for help at point.
748
749@cindex flymake, and Eglot
750@cindex on-the-fly diagnostics using Eglot
751@item M-x flymake-show-buffer-diagnostics
752Ask Flymake system to display diagnostics for the current buffer.
753
754@item M-x flymake-show-project-diagnostics
755Ask Flymake to list diagnostics for all the files in the current
756project.
757
758@cindex xref, and Eglot
759@cindex finding definitions of identifiers using Eglot
760@item M-x xref-find-definitions
761Ask Xref to go the definition of the identifier at point.
762
763@cindex imenu navigation using Eglot
764@item M-x imenu
765Let the user navigate the program source code using buffer index,
766categorizing program elements by syntactic class (class, method,
767variable, etc.) and offering completion.
768
769@cindex symbol completion using Eglot
770@item M-x completion-at-point
771Request completion of the symbol at point.
772@end table
773
774@node Eglot Variables
775@section Eglot Variables
776@cindex variables, Eglot
777
778This section provides a reference of the Eglot' user options.
779
780@vtable @code
781@item eglot-autoreconnect
782This option controls the ability to reconnect automatically to the
783language server when Eglot detects that the server process terminated
784unexpectedly. The default value 3 means to attempt reconnection only
785if the previous successful connection lasted for more than that number
786of seconds; a different positive value changes the minimal length of
787the connection to trigger reconnection. A value of @code{t} means
788always reconnect automatically, and @code{nil} means never reconnect
789(in which case you will need to reconnect manually using @kbd{M-x
790eglot}).
791
792@item eglot-connect-timeout
793This specifies the number of seconds before connection attempt to a
794language server times out. The value of @code{nil} means never time
795out. The default is 30 seconds.
796
797@item eglot-sync-connect
798This setting is mainly important for connections which are slow to
799establish. Whereas the variable @code{eglot-connect-timeout} controls
800how long to wait for, this variable controls whether to block Emacs's
801user interface while waiting. The default value is 3; a positive
802value means block for that many seconds, then wait for the connection
803in the background. The value of @code{t} means block during the whole
804waiting period. The value of @code{nil} or zero means don't block at
805all during the waiting period.
806
807@item eglot-events-buffer-size
808This determines the size of the Eglot events buffer. @xref{Eglot
809Commands, eglot-events-buffer}, for how to display that buffer. If
810the value is changed, for it to take effect the connection should be
811restarted using @kbd{M-x eglot-reconnect}.
812@c FIXME: Shouldn't the defcustom do this by itself using the :set
813@c attribute?
814@xref{Troubleshooting Eglot}, for when this could be useful.
815
816@item eglot-autoshutdown
817If this is non-@code{nil}, Eglot shuts down a language server when the
818last buffer managed by it is killed. @xref{Shutting Down LSP Servers}.
819The default is @code{nil}; if you want to shut down a server, use
820@kbd{M-x eglot-shutdown} (@pxref{Eglot Commands}).
821
822@item eglot-confirm-server-initiated-edits
823Various Eglot commands and code actions result in the language server
824sending editing commands to Emacs. If this option's value is
825non-@code{nil} (the default), Eglot will ask for confirmation before
826performing edits initiated by the server or edits whose scope affects
827buffers other than the one where the user initiated the request.
828
829@item eglot-ignored-server-capabilities
830This variable's value is a list of language server capabilities that
831Eglot should not use. The default is @code{nil}: Eglot uses all of
832the capabilities supported by each server.
833
834@item eglot-extend-to-xref
835If this is non-@code{nil}, and @kbd{M-.}
836(@code{xref-find-definitions}) lands you in a file outside of your
837project, such as a system-installed library or header file,
838transiently consider that file as managed by the same language server.
839That file is still outside your project (i.e. @code{project-find-file}
840won't find it), but Eglot and the server will consider it to be part
841of the workspace. The default is @code{nil}.
842
843@item eglot-mode-map
844This variable is the keymap for binding Eglot-related command. It is
845in effect only as long as the buffer is managed by Eglot. By default,
846it is empty, with the single exception: @kbd{C-h .} is remapped to
847invoke @code{eldoc-doc-buffer}. You can bind additional commands in
848this map. For example:
849
850@lisp
851 (define-key eglot-mode-map (kbd "C-c r") 'eglot-rename)
852 (define-key eglot-mode-map (kbd "C-c o") 'eglot-code-action-organize-imports)
853 (define-key eglot-mode-map (kbd "C-c h") 'eldoc)
854 (define-key eglot-mode-map (kbd "<f6>") 'xref-find-definitions)
855@end lisp
856
857@end vtable
858
859Additional variables, which are relevant for customizing the server
860connections, are documented in @ref{Customizing Eglot}.
861
862@node Customizing Eglot
863@chapter Customizing Eglot
864@cindex customizing Eglot
865
866Eglot itself has a relatively small number of customization options.
867A large part of customizing Eglot to your needs and preferences should
868actually be done via options of the Emacs packages and features which
869Eglot supports and enhances (@pxref{Eglot Features}). For example:
870
871@itemize @bullet
872@item
873To configure the face used for server-derived errors and warnings,
874customize the Flymake faces @code{flymake-error} and
875@code{flymake-error}.
876
877@item
878To configure the amount of space taken up by documentation in the
879echo area, customize the ElDoc variable
880@code{eldoc-echo-area-use-multiline-p}.
881
882@item
883To completely change how ElDoc displays the at-point documentation
884destination, customize the ElDoc variable
885@code{eldoc-display-functions}.
886@end itemize
887
888For this reason, this manual describes only how to customize the
889Eglot's own operation, which mainly has to do with the server
890connections and the server features to be used by Eglot.
891
892@c @table, not @vtable, because some of the variables are indexed
893@c elsewhere
894@table @code
895@item eglot-server-programs
896This variable determines which language server to start for each
897supported major mode, and how to invoke that server's program.
898@xref{Setting Up LSP Servers}, for the details.
899
900@vindex eglot-strict-mode
901@item eglot-strict-mode
902This is @code{nil} by default, meaning that Eglot is generally lenient
903about non-conforming servers. If you need to debug a server, set this
904to @w{@code{(disallow-non-standard-keys enforce-required-keys)}}.
905
906@vindex eglot-server-initialized-hook
907@item eglot-server-initialized-hook
908A hook run after the server object is successfully initialized.
909
910@vindex eglot-connect-hook
911@item eglot-connect-hook
912A hook run after connection to the server is successfully
913established. @xref{Starting Eglot}.
914
915@item eglot-managed-mode-hook
916A hook run after Eglot started or stopped managing a buffer.
917@xref{Eglot and Buffers}, for details of its usage.
918
919@vindex eglot-stay-out-of
920@item eglot-stay-out-of
921This variable's value lists Emacs features that Eglot shouldn't
922automatically try to manage on user's behalf. It is useful, for
923example, when you need to use non-LSP Flymake or Company back-ends.
924To have Eglot stay away of some Emacs feature, add that feature's
925symbol or a regexp that will match a symbol's name to the list: for
926example, the symbol @code{xref} to leave Xref alone, or the string
927@samp{company} to stay away of your Company customizations. Here's an
928example:
929
930@lisp
931(add-to-list 'eglot-stay-out-of 'flymake)
932@end lisp
933
934Note that you can still configure the excluded Emacs features manually
935to use Eglot in your @code{eglot-managed-mode-hook} or via some other
936mechanism.
937
938@vindex eglot-workspace-configuration
939@cindex server workspace configuration
940@item eglot-workspace-configuration
941This variable is meant to be set in the @file{.dir-locals.el} file, to
942provide per-project settings, as described below in more detail.
943@end table
944
945Some language servers need to know project-specific settings, which
946the LSP calls @dfn{workspace configuration}. Eglot allows such fine
947tuning of per-project settings via the variable
948@code{eglot-workspace-configuration}. Eglot sends the portion of the
949settings contained in this variable to each server for which such
950settings were defined in the variable. These settings are
951communicated to the server initially (upon establishing the
952connection) or when the settings are changed, or in response to the
953configuration request from the server.
954
955In many cases, servers can be configured globally using a
956configuration file in the user's home directory or in the project
957directory, which the language server reads. For example, the
958@command{pylsp} server for Python reads the file
959@file{~/.config/pycodestyle} and the @command{clangd} server reads the
960file @file{.clangd} anywhere in the current project's directory tree.
961If possible, we recommend to use these configuration files that are
962independent of Eglot and Emacs; they have the advantage that they will
963work with other LSP clients as well.
964
965If you do need to provide Emacs-specific configuration for a language
966server, we recommend to define the appropriate value in the
967@file{.dir-locals.el} file in the project's directory. The value of
968this variable should be a property list of the following format:
969
970@lisp
971 (:@var{server} @var{plist}@dots{})
972@end lisp
973
974@noindent
975Here @code{:@var{server}} identifies a particular language server and
976@var{plist} is the corresponding keyword-value property list of one or
977more parameter settings for that server, serialized by Eglot as a JSON
978object. @var{plist} may be arbitrarity complex, generally containing
979other keywork-value property sublists corresponding to JSON subobjects.
980The JSON values @code{true}, @code{false}, @code{null} and @code{@{@}}
981are represented by the Lisp values @code{t}, @code{:json-false},
982@code{nil}, and @code{eglot-@{@}}, respectively.
983
984@findex eglot-show-workspace-configuration
985When experimenting with workspace settings, you can use the command
986@kbd{M-x eglot-show-workspace-configuration} to inspect and debug the
987JSON value to be sent to the server. This helper command works even
988before actually connecting to the server.
989
990Here's an example of defining the workspace-configuration settings for
991a project that uses two different language servers, one for Python,
992whose server is @command{pylsp}, the other one for Go, with
993@command{gopls} as its server (presumably, the project is written in a
994combination of these two languages):
995
996@lisp
997((python-mode
998 . ((eglot-workspace-configuration
999 . (:pylsp (:plugins (:jedi_completion (:include_params t
1000 :fuzzy t)
1001 :pylint (:enabled :json-false)))))))
1002 (go-mode
1003 . ((eglot-workspace-configuration
1004 . (:gopls (:usePlaceholders t))))))
1005@end lisp
1006
1007@noindent
1008This should go into the @file{.dir-locals.el} file in the project's
1009root directory. It sets up the value of
1010@code{eglot-workspace-configuration} separately for each major mode.
1011
1012Alternatively, the same configuration could be defined as follows:
1013
1014@lisp
1015((nil
1016 . ((eglot-workspace-configuration
1017 . (:pylsp (:plugins (:jedi_completion (:include_params t
1018 :fuzzy t)
1019 :pylint (:enabled :json-false)))
1020 :gopls (:usePlaceholders t))))))
1021@end lisp
1022
1023This is an equivalent setup which sets the value for all the
1024major-modes inside the project; Eglot will use for each server only
1025the section of the parameters intended for that server.
1026
1027As yet another alternative, you can set the value of
1028@code{eglot-workspace-configuration} programmatically, via the
1029@code{dir-locals-set-class-variables} function, @pxref{Directory Local
1030Variables,,, elisp, GNU Emacs Lisp Reference Manual}.
1031
1032Finally, if one needs to determine the workspace configuration based
1033on some dynamic context, @code{eglot-workspace-configuration} can be
1034set to a function. The function is called with the
1035@code{eglot-lsp-server} instance of the connected server (if any) and
1036with @code{default-directory} set to the root of the project. The
1037function should return a value of the form described above.
1038
1039Some servers need special hand-holding to operate correctly. If your
1040server has some quirks or non-conformity, it's possible to extend
1041Eglot via Elisp to adapt to it, by defining a suitable
1042@code{eglot-initialization-options} method via @code{cl-defmethod}
1043(@pxref{Generic Functions,,, elisp, GNU Emacs Lisp Reference Manual}).
1044
1045Here's an example:
1046
1047@lisp
1048(add-to-list 'eglot-server-programs
1049 '((c++ mode c-mode) . (eglot-cquery "cquery")))
1050
1051(defclass eglot-cquery (eglot-lsp-server) ()
1052 :documentation "A custom class for cquery's C/C++ langserver.")
1053
1054(cl-defmethod eglot-initialization-options ((server eglot-cquery))
1055 "Passes through required cquery initialization options"
1056 (let* ((root (car (project-roots (eglot--project server))))
1057 (cache (expand-file-name ".cquery_cached_index/" root)))
1058 (list :cacheDirectory (file-name-as-directory cache)
1059 :progressReportFrequencyMs -1)))
1060@end lisp
1061
1062@noindent
1063See the doc string of @code{eglot-initialization-options} for more
1064details.
1065@c FIXME: The doc string of eglot-initialization-options should be
1066@c enhanced and extended.
1067
1068@node Troubleshooting Eglot
1069@chapter Troubleshooting Eglot
1070@cindex troubleshooting Eglot
1071
1072This section documents commands and variables that can be used to
1073troubleshoot Eglot problems. It also provides guidelines for
1074reporting Eglot bugs in a way that facilitates their resolution.
1075
1076When you encounter problems with Eglot, try first using the commands
1077@kbd{M-x eglot-events-server} and @kbd{M-x eglot-stderr-buffer}. They
1078pop up special buffers that can be used to inspect the communications
1079between the Eglot and language server. In many cases, this will
1080indicate the problems or at least provide a hint.
1081
1082A common and easy-to-fix cause of performance problems is the length
1083of these two buffers. If Eglot is operating correctly but slowly,
1084customize the variable @code{eglot-events-buffer-size} (@pxref{Eglot
1085Variables}) to limit logging, and thus speed things up.
1086
1087If you need to report an Eglot bug, please keep in mind that, because
1088there are so many variables involved, it is generally both very
1089@emph{difficult} and @emph{absolutely essential} to reproduce bugs
1090exactly as they happened to you, the user. Therefore, every bug
1091report should include:
1092
1093@enumerate
1094@item
1095The transcript of events obtained from the buffer popped up by
1096@kbd{M-x eglot-events-buffer}. If the transcript can be narrowed down
1097to show the problematic exchange, so much the better. This is
1098invaluable for the investigation and reproduction of the problem.
1099
1100@item
1101If Emacs signaled an error (an error message was seen or heard), make
1102sure to repeat the process after toggling @code{debug-on-error} on
1103(via @kbd{M-x toggle-debug-on-error}). This normally produces a
1104backtrace of the error that should also be attached to the bug report.
1105
1106@item
1107An explanation how to obtain and install the language server you used.
1108If possible, try to replicate the problem with the C/C@t{++} or Python
1109servers, as these are very easy to install.
1110
1111@item
1112A description of how to setup the @emph{minimal} project (one or two
1113files and their contents) where the problem happens.
1114
1115@item
1116A recipe to replicate the problem with @emph{a clean Emacs run}. This
1117means @kbd{emacs -Q} invocation or a very minimal (no more that 10
1118lines) @file{.emacs} initialization file. @code{eglot-ensure} and
1119@code{use-package} calls are generally @emph{not} needed.
1120
1121@item
1122Make sure to double check all the above elements and re-run the
1123recipe to see that the problem is reproducible.
1124@end enumerate
1125
1126Please keep in mind that some problems reported against Eglot may
1127actually be bugs in the language server or the Emacs feature/package
1128that used Eglot to communicate with the language server.
1129
1130@node GNU Free Documentation License
1131@appendix GNU Free Documentation License
1132@include doclicense.texi
1133
1134@node Index
1135@unnumbered Index
1136@printindex cp
1137
1138@bye
diff --git a/etc/NEWS b/etc/NEWS
index 4b89e6d52a3..19c90141167 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1357,6 +1357,16 @@ The default input method for the Tamil language environment is now
1357change the input method's translation rules, customize the user option 1357change the input method's translation rules, customize the user option
1358'tamil-translation-rules'. 1358'tamil-translation-rules'.
1359 1359
1360---
1361*** New tamil99 input method for the Tamil language.
1362This supports the keyboard layout specifically designed for the Tamil
1363language.
1364
1365---
1366*** New input method 'slovak-qwerty'.
1367This is a variant of the 'slovak' input method, which corresponds to
1368the QWERTY Slovak keyboards.
1369
1360 1370
1361* Changes in Specialized Modes and Packages in Emacs 29.1 1371* Changes in Specialized Modes and Packages in Emacs 29.1
1362 1372
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index ed2bc1ae051..aaecc41f6e8 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -1229,6 +1229,17 @@ you should use an Emacs input method instead.
1229 1229
1230** X keyboard problems 1230** X keyboard problems
1231 1231
1232*** `x-focus-frame' fails to activate the frame.
1233
1234Some window managers prevent `x-focus-frame' from activating the given
1235frame when Emacs is in the background.
1236
1237Emacs tries to work around this problem by default, but the workaround
1238does not work on all window managers. You can try different
1239workarounds by changing the value of `x-allow-focus-stealing' (see its
1240doc string for more details). The value `imitate-pager' may be
1241required on some versions of KWin.
1242
1232*** You "lose characters" after typing Compose Character key. 1243*** You "lose characters" after typing Compose Character key.
1233 1244
1234This is because the Compose Character key is defined as the keysym 1245This is because the Compose Character key is defined as the keysym
diff --git a/lib-src/rcs2log b/lib-src/rcs2log
index bc7875cfdd2..2a72404d9e5 100755
--- a/lib-src/rcs2log
+++ b/lib-src/rcs2log
@@ -209,7 +209,7 @@ month_data='
209if type mktemp >/dev/null 2>&1; then 209if type mktemp >/dev/null 2>&1; then
210 logdir=`mktemp -d` 210 logdir=`mktemp -d`
211else 211else
212 logdir=$TMPDIR/rcs2log$$ 212 logdir="${TMPDIR-/tmp}/rcs2log$$"
213 (umask 077 && mkdir "$logdir") 213 (umask 077 && mkdir "$logdir")
214fi || exit 214fi || exit
215case $logdir in 215case $logdir in
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 2c9b79334ba..5a05fe4854b 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -3928,6 +3928,7 @@ processes from `comp-async-compilations'"
3928 "Start compiling files from `comp-files-queue' asynchronously. 3928 "Start compiling files from `comp-files-queue' asynchronously.
3929When compilation is finished, run `native-comp-async-all-done-hook' and 3929When compilation is finished, run `native-comp-async-all-done-hook' and
3930display a message." 3930display a message."
3931 (cl-assert (null comp-no-spawn))
3931 (if (or comp-files-queue 3932 (if (or comp-files-queue
3932 (> (comp-async-runnings) 0)) 3933 (> (comp-async-runnings) 0))
3933 (unless (>= (comp-async-runnings) (comp-effective-async-max-jobs)) 3934 (unless (>= (comp-async-runnings) (comp-effective-async-max-jobs))
@@ -4048,7 +4049,7 @@ the deferred compilation mechanism."
4048 (stringp function-or-file)) 4049 (stringp function-or-file))
4049 (signal 'native-compiler-error 4050 (signal 'native-compiler-error
4050 (list "Not a function symbol or file" function-or-file))) 4051 (list "Not a function symbol or file" function-or-file)))
4051 (unless comp-no-spawn 4052 (when (or (null comp-no-spawn) comp-async-compilation)
4052 (catch 'no-native-compile 4053 (catch 'no-native-compile
4053 (let* ((print-symbols-bare t) 4054 (let* ((print-symbols-bare t)
4054 (data function-or-file) 4055 (data function-or-file)
diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el
index 9b464a0a137..f47373c115f 100644
--- a/lisp/eshell/esh-util.el
+++ b/lisp/eshell/esh-util.el
@@ -455,7 +455,7 @@ list."
455 ;; runs while point is in the minibuffer and the users attempt 455 ;; runs while point is in the minibuffer and the users attempt
456 ;; to use completion. Don't ask me. 456 ;; to use completion. Don't ask me.
457 (condition-case nil 457 (condition-case nil
458 (sit-for 0 0) 458 (sit-for 0)
459 (error nil))) 459 (error nil)))
460 460
461(defun eshell-read-passwd-file (file) 461(defun eshell-read-passwd-file (file)
diff --git a/lisp/info-look.el b/lisp/info-look.el
index ce0a08dcbe6..2eec6f49f5c 100644
--- a/lisp/info-look.el
+++ b/lisp/info-look.el
@@ -1051,6 +1051,7 @@ Return nil if there is nothing appropriate in the buffer near point."
1051 ("eieio" "Function Index") 1051 ("eieio" "Function Index")
1052 ("gnutls" "(emacs-gnutls)Variable Index" "(emacs-gnutls)Function Index") 1052 ("gnutls" "(emacs-gnutls)Variable Index" "(emacs-gnutls)Function Index")
1053 ("mm" "(emacs-mime)Index") 1053 ("mm" "(emacs-mime)Index")
1054 ("eglot" "Index")
1054 ("epa" "Variable Index" "Function Index") 1055 ("epa" "Variable Index" "Function Index")
1055 ("ert" "Index") 1056 ("ert" "Index")
1056 ("eshell" "Function and Variable Index") 1057 ("eshell" "Function and Variable Index")
diff --git a/lisp/leim/quail/indian.el b/lisp/leim/quail/indian.el
index 048e16e8d80..31a34bc1de2 100644
--- a/lisp/leim/quail/indian.el
+++ b/lisp/leim/quail/indian.el
@@ -30,6 +30,8 @@
30 30
31;;; Code: 31;;; Code:
32 32
33(require 'pcase)
34(require 'seq)
33(require 'quail) 35(require 'quail)
34(require 'ind-util) 36(require 'ind-util)
35 37
@@ -699,6 +701,165 @@ is."
699 "tamil-inscript-digits" "Tamil" "TmlISD" 701 "tamil-inscript-digits" "Tamil" "TmlISD"
700 "Tamil keyboard Inscript with Tamil digits support.") 702 "Tamil keyboard Inscript with Tamil digits support.")
701 703
704;; Tamil99 input method
705;;
706;; Tamil99 is a keyboard layout and input method that is specifically
707;; designed for the Tamil language. Vowels and vowel modifiers are
708;; input with your left hand, and consonants are input with your right
709;; hand. See https://en.wikipedia.org/wiki/Tamil_99
710;;
711;; தமிழ்99 உள்ளீட்டு முறை
712;;
713;; தமிழ்99 தமிழுக்கென்றே உருவாக்கப்பட்ட விசைப்பலகை அமைப்பும் உள்ளீட்டு முறையும்
714;; ஆகும். உயிர்களை இடக்கையுடனும் மெய்களை வலக்கையுடனும் தட்டச்சிடும்படி
715;; அமைக்கப்பட்டது. https://ta.wikipedia.org/wiki/%E0%AE%A4%E0%AE%AE%E0%AE%BF%E0%AE%B4%E0%AF%8D_99
716;; காண்க.
717
718(quail-define-package
719 "tamil99" "Tamil" "தமிழ்99"
720 t "Tamil99 input method"
721 nil t t t t nil nil nil nil nil t)
722
723(defconst tamil99-vowels
724 '(("q" "ஆ")
725 ("w" "ஈ")
726 ("e" "ஊ")
727 ("r" "ஐ")
728 ("t" "ஏ")
729 ("a" "அ")
730 ("s" "இ")
731 ("d" "உ")
732 ("g" "எ")
733 ("z" "ஔ")
734 ("x" "ஓ")
735 ("c" "ஒ"))
736 "Mapping for vowels.")
737
738(defconst tamil99-vowel-modifiers
739 '(("q" "ா")
740 ("w" "ீ")
741 ("e" "ூ")
742 ("r" "ை")
743 ("t" "ே")
744 ("a" "")
745 ("s" "ி")
746 ("d" "ு")
747 ("g" "ெ")
748 ("z" "ௌ")
749 ("x" "ோ")
750 ("c" "ொ")
751 ("f" "்"))
752 "Mapping for vowel modifiers.")
753
754(defconst tamil99-hard-consonants
755 '(("h" "க")
756 ("[" "ச")
757 ("o" "ட")
758 ("l" "த")
759 ("j" "ப")
760 ("u" "ற"))
761 "Mapping for hard consonants (வல்லினம்).")
762
763(defconst tamil99-soft-consonants
764 '(("b" "ங")
765 ("]" "ஞ")
766 ("p" "ண")
767 (";" "ந")
768 ("k" "ம")
769 ("i" "ன"))
770 "Mapping for soft consonants (மெல்லினம்).")
771
772(defconst tamil99-medium-consonants
773 '(("'" "ய")
774 ("m" "ர")
775 ("n" "ல")
776 ("v" "வ")
777 ("/" "ழ")
778 ("y" "ள"))
779 "Mapping for medium consonants (இடையினம்).")
780
781(defconst tamil99-grantham-consonants
782 '(("Q" "ஸ")
783 ("W" "ஷ")
784 ("E" "ஜ")
785 ("R" "ஹ"))
786 "Mapping for grantham consonants (கிரந்தம்).")
787
788(defconst tamil99-consonants
789 (append tamil99-hard-consonants
790 tamil99-soft-consonants
791 tamil99-medium-consonants
792 tamil99-grantham-consonants)
793 "Mapping for all consonants.")
794
795(defconst tamil99-other
796 `(("T" ,(vector "க்ஷ"))
797 ("Y" ,(vector "ஶஂரீ"))
798 ("O" "[")
799 ("P" "]")
800 ("A" "௹")
801 ("S" "௺")
802 ("D" "௸")
803 ("F" "ஃ")
804 ("K" "\"")
805 ("L" ":")
806 (":" ";")
807 ("\"" "'")
808 ("Z" "௳")
809 ("X" "௴")
810 ("C" "௵")
811 ("V" "௶")
812 ("B" "௷")
813 ("M" "/"))
814 "Mapping for miscellaneous characters.")
815
816;; உயிர்
817;; vowel
818(mapc (pcase-lambda (`(,vowel-key ,vowel))
819 (quail-defrule vowel-key vowel))
820 tamil99-vowels)
821
822(mapc (pcase-lambda (`(,consonant-key ,consonant))
823 ;; அகர உயிர்மெய்
824 ;; consonant symbol (consonant combined with the first vowel அ)
825 (quail-defrule consonant-key consonant)
826 ;; மெய்யொற்று பின் அகர உயிர்மெய்
827 ;; pulli on double consonant
828 (quail-defrule (concat consonant-key consonant-key)
829 (vector (concat consonant "்" consonant)))
830 (mapc (pcase-lambda (`(,vowel-key ,vowel-modifier))
831 ;; உயிர்மெய்
832 ;; vowelised consonant
833 (quail-defrule (concat consonant-key vowel-key)
834 (vector (concat consonant vowel-modifier)))
835 ;; மெய்யொற்று பின் பிற உயிர்மெய்
836 ;; vowelised consonant after double consonant
837 (quail-defrule (concat consonant-key consonant-key vowel-key)
838 (vector (concat consonant "்" consonant vowel-modifier))))
839 tamil99-vowel-modifiers))
840 tamil99-consonants)
841
842(seq-mapn (pcase-lambda (`(,soft-consonant-key ,soft-consonant)
843 `(,hard-consonant-key ,hard-consonant))
844 ;; மெல்லினம் பின் வல்லினம்
845 ;; hard consonant after soft consonant
846 (quail-defrule (concat soft-consonant-key hard-consonant-key)
847 (vector (concat soft-consonant "்" hard-consonant)))
848 (mapc (pcase-lambda (`(,vowel-key ,vowel-modifier))
849 ;; மெல்லின ஒற்றொட்டிய வல்லினம் பின் உயிர்மெய்
850 ;; vowelised consonant after soft-hard consonant pair
851 (quail-defrule (concat soft-consonant-key hard-consonant-key vowel-key)
852 (vector (concat soft-consonant "்" hard-consonant vowel-modifier))))
853 tamil99-vowel-modifiers))
854 tamil99-soft-consonants
855 tamil99-hard-consonants)
856
857;; பிற வரியுருக்கள்
858;; other characters
859(mapc (pcase-lambda (`(,key ,translation))
860 (quail-defrule key translation))
861 tamil99-other)
862
702;; Probhat Input Method 863;; Probhat Input Method
703(quail-define-package 864(quail-define-package
704 "bengali-probhat" "Bengali" "BngPB" t 865 "bengali-probhat" "Bengali" "BngPB" t
diff --git a/lisp/leim/quail/slovak.el b/lisp/leim/quail/slovak.el
index acde11d02a7..8ddd92d5b4d 100644
--- a/lisp/leim/quail/slovak.el
+++ b/lisp/leim/quail/slovak.el
@@ -3,7 +3,8 @@
3;; Copyright (C) 1998, 2001-2022 Free Software Foundation, Inc. 3;; Copyright (C) 1998, 2001-2022 Free Software Foundation, Inc.
4 4
5;; Authors: Tibor Šimko <tibor.simko@fmph.uniba.sk> 5;; Authors: Tibor Šimko <tibor.simko@fmph.uniba.sk>
6;; Milan Zamazal <pdm@zamazal.org> 6;; Milan Zamazal <pdm@zamazal.org>
7;; Rudolf Adamkovič <salutis@me.com>
7;; Maintainer: Pavel Janík <Pavel@Janik.cz> 8;; Maintainer: Pavel Janík <Pavel@Janik.cz>
8;; Keywords: i18n, multilingual, input method, Slovak 9;; Keywords: i18n, multilingual, input method, Slovak
9 10
@@ -25,8 +26,9 @@
25;;; Commentary: 26;;; Commentary:
26 27
27;; This file defines the following Slovak keyboards: 28;; This file defines the following Slovak keyboards:
28;; - standard Slovak keyboard 29;; - standard Slovak keyboards, QWERTZ and QWERTY variants
29;; - three Slovak keyboards for programmers 30;; - three Slovak keyboards for programmers
31;; LocalWords: QWERTZ
30 32
31;;; Code: 33;;; Code:
32 34
@@ -35,7 +37,7 @@
35 37
36(quail-define-package 38(quail-define-package
37 "slovak" "Slovak" "SK" t 39 "slovak" "Slovak" "SK" t
38 "Standard Slovak keyboard." 40 "Standard Slovak QWERTZ keyboard."
39 nil t nil nil t nil nil nil nil nil t) 41 nil t nil nil t nil nil nil nil nil t)
40 42
41(quail-define-rules 43(quail-define-rules
@@ -155,6 +157,123 @@
155 157
156 158
157(quail-define-package 159(quail-define-package
160 "slovak-qwerty" "Slovak" "SK" t
161 "Standard Slovak QWERTY keyboard."
162 nil t nil nil t nil nil nil nil nil t)
163
164(quail-define-rules
165 ("1" ?+)
166 ("2" ?ľ)
167 ("3" ?š)
168 ("4" ?č)
169 ("5" ?ť)
170 ("6" ?ž)
171 ("7" ?ý)
172 ("8" ?á)
173 ("9" ?í)
174 ("0" ?é)
175 ("!" ?1)
176 ("@" ?2)
177 ("#" ?3)
178 ("$" ?4)
179 ("%" ?5)
180 ("^" ?6)
181 ("&" ?7)
182 ("*" ?8)
183 ("(" ?9)
184 (")" ?0)
185 ("-" ?=)
186 ("_" ?%)
187 ("=" ?')
188 ("[" ?ú)
189 ("{" ?/)
190 ("]" ?ä)
191 ("}" ?\()
192 ("\\" ?ň)
193 ("|" ?\))
194 (";" ?ô)
195 (":" ?\")
196 ("'" ?§)
197 ("\"" ?!)
198 ("<" ??)
199 (">" ?:)
200 ("/" ?-)
201 ("?" ?_)
202 ("`" ?\;)
203 ("~" ?^)
204 ("=a" ?á)
205 ("+a" ?ä)
206 ("+=a" ?ä)
207 ("+c" ?č)
208 ("+d" ?ď)
209 ("=e" ?é)
210 ("+e" ?ě)
211 ("=i" ?í)
212 ("=l" ?ĺ)
213 ("+l" ?ľ)
214 ("+n" ?ň)
215 ("=o" ?ó)
216 ("+o" ?ô)
217 ("~o" ?ô)
218 ("+=o" ?ö)
219 ("=r" ?ŕ)
220 ("+r" ?ř)
221 ("=s" ?ß)
222 ("+s" ?š)
223 ("+t" ?ť)
224 ("=u" ?ú)
225 ("+u" ?ů)
226 ("+=u" ?ü)
227 ("=y" ?ý)
228 ("+z" ?ž)
229 ("=A" ?Á)
230 ("+A" ?Ä)
231 ("+=A" ?Ä)
232 ("+C" ?Č)
233 ("+D" ?Ď)
234 ("=E" ?É)
235 ("+E" ?Ě)
236 ("=I" ?Í)
237 ("=L" ?Ĺ)
238 ("+L" ?Ľ)
239 ("+N" ?Ň)
240 ("=O" ?Ó)
241 ("+O" ?Ô)
242 ("~O" ?Ô)
243 ("+=O" ?Ö)
244 ("=R" ?Ŕ)
245 ("+R" ?Ř)
246 ("=S" ?ß)
247 ("+S" ?Š)
248 ("+T" ?Ť)
249 ("=U" ?Ú)
250 ("+U" ?Ů)
251 ("+=U" ?Ü)
252 ("=Y" ?Ý)
253 ("+Z" ?Ž)
254 ("=q" ?`)
255 ("=2" ?@)
256 ("=3" ?#)
257 ("=4" ?$)
258 ("=5" ?%)
259 ("=6" ?^)
260 ("=7" ?&)
261 ("=8" ?*)
262 ("=9" ?\()
263 ("=0" ?\))
264 ("+1" ?!)
265 ("+2" ?@)
266 ("+3" ?#)
267 ("+4" ?$)
268 ("+5" ?%)
269 ("+6" ?^)
270 ("+7" ?&)
271 ("+8" ?*)
272 ("+9" ?\()
273 ("+0" ?\)))
274
275
276(quail-define-package
158 "slovak-prog-1" "Slovak" "SK" t 277 "slovak-prog-1" "Slovak" "SK" t
159 "Slovak (non-standard) keyboard for programmers #1. 278 "Slovak (non-standard) keyboard for programmers #1.
160 279
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index 526bccbbac9..849e0f77236 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -1847,6 +1847,10 @@ mail status in mode line"))
1847 :help "Toggle automatic parsing in source code buffers (Semantic mode)" 1847 :help "Toggle automatic parsing in source code buffers (Semantic mode)"
1848 :button (:toggle . (bound-and-true-p semantic-mode)))) 1848 :button (:toggle . (bound-and-true-p semantic-mode))))
1849 1849
1850 (bindings--define-key menu [eglot]
1851 '(menu-item "Language Server Support (Eglot)" eglot
1852 :help "Start language server suitable for this buffer's major-mode"))
1853
1850 (bindings--define-key menu [ede] 1854 (bindings--define-key menu [ede]
1851 '(menu-item "Project Support (EDE)" 1855 '(menu-item "Project Support (EDE)"
1852 global-ede-mode 1856 global-ede-mode
diff --git a/lisp/net/ldap.el b/lisp/net/ldap.el
index 062ff05d69c..ccad8c4edb1 100644
--- a/lisp/net/ldap.el
+++ b/lisp/net/ldap.el
@@ -715,14 +715,14 @@ an alist of attribute/value pairs."
715 (eq (string-match "/\\(.:.*\\)$" value) 0)) 715 (eq (string-match "/\\(.:.*\\)$" value) 0))
716 (setq value (match-string 1 value))) 716 (setq value (match-string 1 value)))
717 ;; Do not try to open non-existent files 717 ;; Do not try to open non-existent files
718 (if (equal value "") 718 (if (match-string 3)
719 (setq value " ") 719 (with-current-buffer bufval
720 (with-current-buffer bufval
721 (erase-buffer) 720 (erase-buffer)
722 (set-buffer-multibyte nil) 721 (set-buffer-multibyte nil)
723 (insert-file-contents-literally value) 722 (insert-file-contents-literally value)
724 (delete-file value) 723 (delete-file value)
725 (setq value (buffer-string)))) 724 (setq value (buffer-string)))
725 (setq value " "))
726 (setq record (cons (list name value) 726 (setq record (cons (list name value)
727 record)) 727 record))
728 (forward-line 1)) 728 (forward-line 1))
diff --git a/lisp/play/zone.el b/lisp/play/zone.el
index 5ea5bbc9267..e3a9507f1cc 100644
--- a/lisp/play/zone.el
+++ b/lisp/play/zone.el
@@ -139,7 +139,7 @@ run a specific program. The program must be a member of
139 (untabify (point-min) (point-max)) 139 (untabify (point-min) (point-max))
140 (set-window-start (selected-window) (point-min)) 140 (set-window-start (selected-window) (point-min))
141 (set-window-point (selected-window) wp) 141 (set-window-point (selected-window) wp)
142 (sit-for 0 500) 142 (sit-for 0.500)
143 (let ((ct (and f (frame-parameter f 'cursor-type))) 143 (let ((ct (and f (frame-parameter f 'cursor-type)))
144 (show-trailing-whitespace nil) 144 (show-trailing-whitespace nil)
145 restore) 145 restore)
@@ -249,7 +249,7 @@ run a specific program. The program must be a member of
249 (while (not (input-pending-p)) 249 (while (not (input-pending-p))
250 (funcall (elt ops (random (length ops)))) 250 (funcall (elt ops (random (length ops))))
251 (goto-char (point-min)) 251 (goto-char (point-min))
252 (sit-for 0 10)))) 252 (sit-for 0.01))))
253 253
254 254
255;;;; whacking chars 255;;;; whacking chars
@@ -262,7 +262,7 @@ run a specific program. The program must be a member of
262 (aset tbl i (+ 48 (random (- 123 48)))) 262 (aset tbl i (+ 48 (random (- 123 48))))
263 (setq i (1+ i))) 263 (setq i (1+ i)))
264 (translate-region (point-min) (point-max) tbl) 264 (translate-region (point-min) (point-max) tbl)
265 (sit-for 0 2))))) 265 (sit-for 0.002)))))
266 266
267(put 'zone-pgm-whack-chars 'wc-tbl 267(put 'zone-pgm-whack-chars 'wc-tbl
268 (let ((tbl (make-string 128 ?x)) 268 (let ((tbl (make-string 128 ?x))
@@ -290,7 +290,7 @@ run a specific program. The program must be a member of
290 (delete-char 1) 290 (delete-char 1)
291 (insert " "))) 291 (insert " ")))
292 (forward-char 1)))) 292 (forward-char 1))))
293 (sit-for 0 2)))) 293 (sit-for 0.002))))
294 294
295(defun zone-pgm-dissolve () 295(defun zone-pgm-dissolve ()
296 (zone-remove-text) 296 (zone-remove-text)
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index 596cccdf48e..e71560fa25f 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -9106,7 +9106,9 @@ multi-line strings (but not C++, for example)."
9106 (when (save-excursion 9106 (when (save-excursion
9107 (goto-char post-prefix-pos) 9107 (goto-char post-prefix-pos)
9108 (looking-at c-self-contained-typename-key)) 9108 (looking-at c-self-contained-typename-key))
9109 (c-add-type pos (point))) 9109 (c-add-type pos (save-excursion
9110 (c-backward-syntactic-ws)
9111 (point))))
9110 (when (and c-record-type-identifiers 9112 (when (and c-record-type-identifiers
9111 c-last-identifier-range) 9113 c-last-identifier-range)
9112 (c-record-type-id c-last-identifier-range))) 9114 (c-record-type-id c-last-identifier-range)))
@@ -9191,7 +9193,10 @@ multi-line strings (but not C++, for example)."
9191 (goto-char id-end) 9193 (goto-char id-end)
9192 (if (or res c-promote-possible-types) 9194 (if (or res c-promote-possible-types)
9193 (progn 9195 (progn
9194 (c-add-type id-start id-end) 9196 (c-add-type id-start (save-excursion
9197 (goto-char id-end)
9198 (c-backward-syntactic-ws)
9199 (point)))
9195 (when (and c-record-type-identifiers id-range) 9200 (when (and c-record-type-identifiers id-range)
9196 (c-record-type-id id-range)) 9201 (c-record-type-id id-range))
9197 (unless res 9202 (unless res
@@ -10762,8 +10767,16 @@ This function might do hidden buffer changes."
10762 (setq backup-if-not-cast t) 10767 (setq backup-if-not-cast t)
10763 (throw 'at-decl-or-cast t))) 10768 (throw 'at-decl-or-cast t)))
10764 10769
10765 (setq backup-if-not-cast t) 10770 ;; If we're in declaration or template delimiters, or one
10766 (throw 'at-decl-or-cast t))) 10771 ;; of a certain set of characters follows, we've got a
10772 ;; type and variable.
10773 (if (or (memq context '(decl <>))
10774 (memq (char-after) '(?\; ?, ?= ?\( ?{ ?:)))
10775 (progn
10776 (setq backup-if-not-cast t)
10777 (throw 'at-decl-or-cast t))
10778 ;; We're probably just typing a statement.
10779 (throw 'at-decl-or-cast nil))))
10767 10780
10768 ;; CASE 4 10781 ;; CASE 4
10769 (when (and got-suffix 10782 (when (and got-suffix
@@ -10879,8 +10892,13 @@ This function might do hidden buffer changes."
10879 10892
10880 ;; CASE 10 10893 ;; CASE 10
10881 (when at-decl-or-cast 10894 (when at-decl-or-cast
10882 ;; By now we've located the type in the declaration that we know 10895 ;; By now we've located the type in the declaration that we think
10883 ;; we're in. 10896 ;; we're in. Do we have enough evidence to promote the putative
10897 ;; type to a found type? The user may be halfway through typing
10898 ;; a statement beginning with an identifier.
10899 (when (and (eq at-type 'maybe)
10900 (not (eq context 'top)))
10901 (setq c-record-type-identifiers nil))
10884 (throw 'at-decl-or-cast t)) 10902 (throw 'at-decl-or-cast t))
10885 10903
10886 ;; CASE 11 10904 ;; CASE 11
@@ -11123,7 +11141,10 @@ This function might do hidden buffer changes."
11123 (not (c-on-identifier))))))))) 11141 (not (c-on-identifier)))))))))
11124 11142
11125 ;; Handle the cast. 11143 ;; Handle the cast.
11126 (when (and c-record-type-identifiers at-type (not (eq at-type t))) 11144 (when (and c-record-type-identifiers
11145 at-type
11146 (not (memq at-type '(t maybe)))) ; 'maybe isn't strong enough
11147 ; evidence to promote the type.
11127 (let ((c-promote-possible-types t)) 11148 (let ((c-promote-possible-types t))
11128 (goto-char type-start) 11149 (goto-char type-start)
11129 (c-forward-type))) 11150 (c-forward-type)))
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index b4ff32b9070..aa16da70703 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -1197,8 +1197,21 @@ casts and declarations are fontified. Used on level 2 and higher."
1197 ;; arguments lists (i.e. lists enclosed by <...>) is more strict about what 1197 ;; arguments lists (i.e. lists enclosed by <...>) is more strict about what
1198 ;; characters it allows within the list. 1198 ;; characters it allows within the list.
1199 (let ((type (and (> match-pos (point-min)) 1199 (let ((type (and (> match-pos (point-min))
1200 (c-get-char-property (1- match-pos) 'c-type)))) 1200 (c-get-char-property (1- match-pos) 'c-type)))
1201 (cond ((not (memq (char-before match-pos) '(?\( ?, ?\[ ?< ?{))) 1201 id-pos)
1202 (cond
1203 ;; Are we just after something like "(foo((bar))" ?
1204 ((and (eq (char-before match-pos) ?\))
1205 (c-go-list-backward match-pos)
1206 (progn
1207 (c-backward-syntactic-ws)
1208 (and (setq id-pos (c-on-identifier))
1209 (goto-char id-pos)
1210 (progn
1211 (c-backward-syntactic-ws)
1212 (eq (char-before) ?\()))))
1213 (c-get-fontification-context (point) not-front-decl toplev))
1214 ((not (memq (char-before match-pos) '(?\( ?, ?\[ ?< ?{)))
1202 (cons (and toplev 'top) nil)) 1215 (cons (and toplev 'top) nil))
1203 ;; A control flow expression or a decltype 1216 ;; A control flow expression or a decltype
1204 ((and (eq (char-before match-pos) ?\() 1217 ((and (eq (char-before match-pos) ?\()
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index dce300f33c9..2aa6b90dea3 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -2080,13 +2080,14 @@ with // and /*, not more generic line and block comments."
2080(defun c-update-new-id (end) 2080(defun c-update-new-id (end)
2081 ;; Note the bounds of any identifier that END is in or just after, in 2081 ;; Note the bounds of any identifier that END is in or just after, in
2082 ;; `c-new-id-start' and `c-new-id-end'. Otherwise set these variables to 2082 ;; `c-new-id-start' and `c-new-id-end'. Otherwise set these variables to
2083 ;; nil. 2083 ;; nil. Set `c-new-id-is-type' unconditionally to nil.
2084 (save-excursion 2084 (save-excursion
2085 (goto-char end) 2085 (goto-char end)
2086 (let ((id-beg (c-on-identifier))) 2086 (let ((id-beg (c-on-identifier)))
2087 (setq c-new-id-start id-beg 2087 (setq c-new-id-start id-beg
2088 c-new-id-end (and id-beg 2088 c-new-id-end (and id-beg
2089 (progn (c-end-of-current-token) (point))))))) 2089 (progn (c-end-of-current-token) (point)))
2090 c-new-id-is-type nil))))
2090 2091
2091(defun c-post-command () 2092(defun c-post-command ()
2092 ;; If point was inside of a new identifier and no longer is, record that 2093 ;; If point was inside of a new identifier and no longer is, record that
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
new file mode 100644
index 00000000000..ada8b01fec2
--- /dev/null
+++ b/lisp/progmodes/eglot.el
@@ -0,0 +1,3461 @@
1;;; eglot.el --- The Emacs Client for LSP servers -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2018-2022 Free Software Foundation, Inc.
4
5;; Version: 1.9
6;; Author: João Távora <joaotavora@gmail.com>
7;; Maintainer: João Távora <joaotavora@gmail.com>
8;; URL: https://github.com/joaotavora/eglot
9;; Keywords: convenience, languages
10;; Package-Requires: ((emacs "26.3") (jsonrpc "1.0.14") (flymake "1.2.1") (project "0.3.0") (xref "1.0.1") (eldoc "1.11.0") (seq "2.23"))
11
12;; This is is a GNU ELPA :core package. Avoid adding functionality
13;; that is not available in the version of Emacs recorded above or any
14;; of the package dependencies.
15
16;; This file is part of GNU Emacs.
17
18;; GNU Emacs is free software: you can redistribute it and/or modify
19;; it under the terms of the GNU General Public License as published by
20;; the Free Software Foundation, either version 3 of the License, or
21;; (at your option) any later version.
22
23;; GNU Emacs is distributed in the hope that it will be useful,
24;; but WITHOUT ANY WARRANTY; without even the implied warranty of
25;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26;; GNU General Public License for more details.
27
28;; You should have received a copy of the GNU General Public License
29;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
30
31;;; Commentary:
32
33;; Eglot ("Emacs Polyglot") is an Emacs LSP client that stays out of
34;; your way.
35;;
36;; Typing M-x eglot in some source file is often enough to get you
37;; started, if the language server you're looking to use is installed
38;; in your system. Please refer to the manual, available from
39;; https://joaotavora.github.io/eglot/ or from M-x info for more usage
40;; instructions.
41;;
42;; If you wish to contribute changes to Eglot, please do read the user
43;; manual first. Additionally, take the following in consideration:
44
45;; * Eglot's main job is to hook up the information that language
46;; servers offer via LSP to Emacs's UI facilities: Xref for
47;; definition-chasing, Flymake for diagnostics, Eldoc for at-point
48;; documentation, etc. Eglot's job is generally *not* to provide
49;; such a UI itself, though a small number of simple
50;; counter-examples do exist, for example in the `eglot-rename'
51;; command. When a new UI is evidently needed, consider adding a
52;; new package to Emacs, or extending an existing one.
53;;
54;; * Eglot was designed to function with just the UI facilities found
55;; in the latest Emacs core, as long as those facilities are also
56;; available as GNU ELPA :core packages. Historically, a number of
57;; :core packages were added or reworked in Emacs to make this
58;; possible. This principle should be upheld when adding new LSP
59;; features or tweaking exising ones. Design any new facilities in
60;; a way that they could work in the absence of LSP or using some
61;; different protocol, then make sure Eglot can link up LSP
62;; information to it.
63
64;; * There are few Eglot configuration variables. This principle
65;; should also be upheld. If Eglot had these variables, it could be
66;; duplicating configuration found elsewhere, bloating itself up,
67;; and making it generally hard to integrate with the ever growing
68;; set of LSP features and Emacs packages. For instance, this is
69;; why one finds a single variable
70;; `eglot-ignored-server-capabilities' instead of a number of
71;; capability-specific flags, or why customizing the display of
72;; LSP-provided documentation is done via ElDoc's variables, not
73;; Eglot's.
74;;
75;; * Linking up LSP information to other libraries is generally done
76;; in the `eglot--managed-mode' minor mode function, by
77;; buffer-locally setting the other library's variables to
78;; Eglot-specific versions. When deciding what to set the variable
79;; to, the general idea is to choose a good default for beginners
80;; that doesn't clash with Emacs's defaults. The settings are only
81;; in place during Eglot's LSP-enriched tenure over a project. Even
82;; so, some of those decisions will invariably aggravate a minority
83;; of Emacs power users, but these users can use `eglot-stay-out-of'
84;; and `eglot-managed-mode-hook' to quench their OCD.
85;;
86;; * On occasion, to enable new features, Eglot can have soft
87;; dependencies on popular libraries that are not in Emacs core.
88;; "Soft" means that the dependency doesn't impair any other use of
89;; Eglot beyond that feature. Such is the case of the snippet
90;; functionality, via the Yasnippet package, Markdown formatting of
91;; at-point documentation via the markdown-mode package, and nicer
92;; looking completions when the Company package is used.
93
94;;; Code:
95
96(require 'imenu)
97(require 'cl-lib)
98(require 'project)
99(require 'url-parse)
100(require 'url-util)
101(require 'pcase)
102(require 'compile) ; for some faces
103(require 'warnings)
104(require 'flymake)
105(require 'xref)
106(eval-when-compile
107 (require 'subr-x))
108(require 'jsonrpc)
109(require 'filenotify)
110(require 'ert)
111(require 'array)
112
113;; ElDoc is preloaded in Emacs, so `require'-ing won't guarantee we are
114;; using the latest version from GNU Elpa when we load eglot.el. Use an
115;; heuristic to see if we need to `load' it in Emacs < 28.
116(if (and (< emacs-major-version 28)
117 (not (boundp 'eldoc-documentation-strategy)))
118 (load "eldoc")
119 (require 'eldoc))
120
121;; Similar issue as above for Emacs 26.3 and seq.el.
122(if (< emacs-major-version 27)
123 (load "seq")
124 (require 'seq))
125
126;; forward-declare, but don't require (Emacs 28 doesn't seem to care)
127(defvar markdown-fontify-code-blocks-natively)
128(defvar company-backends)
129(defvar company-tooltip-align-annotations)
130
131
132
133;;; User tweakable stuff
134(defgroup eglot nil
135 "Interaction with Language Server Protocol servers."
136 :prefix "eglot-"
137 :group 'applications)
138
139(defun eglot-alternatives (alternatives)
140 "Compute server-choosing function for `eglot-server-programs'.
141Each element of ALTERNATIVES is a string PROGRAM or a list of
142strings (PROGRAM ARGS...) where program names an LSP server
143program to start with ARGS. Returns a function of one argument.
144When invoked, that function will return a list (ABSPATH ARGS),
145where ABSPATH is the absolute path of the PROGRAM that was
146chosen (interactively or automatically)."
147 (lambda (&optional interactive)
148 ;; JT@2021-06-13: This function is way more complicated than it
149 ;; could be because it accounts for the fact that
150 ;; `eglot--executable-find' may take much longer to execute on
151 ;; remote files.
152 (let* ((listified (cl-loop for a in alternatives
153 collect (if (listp a) a (list a))))
154 (err (lambda ()
155 (error "None of '%s' are valid executables"
156 (mapconcat #'car listified ", ")))))
157 (cond (interactive
158 (let* ((augmented (mapcar (lambda (a)
159 (let ((found (eglot--executable-find
160 (car a) t)))
161 (and found
162 (cons (car a) (cons found (cdr a))))))
163 listified))
164 (available (remove nil augmented)))
165 (cond ((cdr available)
166 (cdr (assoc
167 (completing-read
168 "[eglot] More than one server executable available:"
169 (mapcar #'car available)
170 nil t nil nil (car (car available)))
171 available #'equal)))
172 ((cdr (car available)))
173 (t
174 ;; Don't error when used interactively, let the
175 ;; Eglot prompt the user for alternative (github#719)
176 nil))))
177 (t
178 (cl-loop for (p . args) in listified
179 for probe = (eglot--executable-find p t)
180 when probe return (cons probe args)
181 finally (funcall err)))))))
182
183(defvar eglot-server-programs `((rust-mode . ,(eglot-alternatives '("rust-analyzer" "rls")))
184 (cmake-mode . ("cmake-language-server"))
185 (vimrc-mode . ("vim-language-server" "--stdio"))
186 (python-mode
187 . ,(eglot-alternatives
188 '("pylsp" "pyls" ("pyright-langserver" "--stdio") "jedi-language-server")))
189 ((js-mode typescript-mode)
190 . ("typescript-language-server" "--stdio"))
191 (sh-mode . ("bash-language-server" "start"))
192 ((php-mode phps-mode)
193 . ("php" "vendor/felixfbecker/\
194language-server/bin/php-language-server.php"))
195 ((c++-mode c-mode) . ,(eglot-alternatives
196 '("clangd" "ccls")))
197 (((caml-mode :language-id "ocaml")
198 (tuareg-mode :language-id "ocaml") reason-mode)
199 . ("ocamllsp"))
200 (ruby-mode
201 . ("solargraph" "socket" "--port" :autoport))
202 (haskell-mode
203 . ("haskell-language-server-wrapper" "--lsp"))
204 (elm-mode . ("elm-language-server"))
205 (mint-mode . ("mint" "ls"))
206 (kotlin-mode . ("kotlin-language-server"))
207 (go-mode . ("gopls"))
208 ((R-mode ess-r-mode) . ("R" "--slave" "-e"
209 "languageserver::run()"))
210 (java-mode . ("jdtls"))
211 (dart-mode . ("dart" "language-server"
212 "--client-id" "emacs.eglot-dart"))
213 (elixir-mode . ("language_server.sh"))
214 (ada-mode . ("ada_language_server"))
215 (scala-mode . ("metals-emacs"))
216 (racket-mode . ("racket" "-l" "racket-langserver"))
217 ((tex-mode context-mode texinfo-mode bibtex-mode)
218 . ("digestif"))
219 (erlang-mode . ("erlang_ls" "--transport" "stdio"))
220 (yaml-mode . ("yaml-language-server" "--stdio"))
221 (nix-mode . ("rnix-lsp"))
222 (gdscript-mode . ("localhost" 6008))
223 ((fortran-mode f90-mode) . ("fortls"))
224 (futhark-mode . ("futhark" "lsp"))
225 (lua-mode . ("lua-lsp"))
226 (zig-mode . ("zls"))
227 (css-mode . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio") ("css-languageserver" "--stdio"))))
228 (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio"))))
229 (json-mode . ,(eglot-alternatives '(("vscode-json-language-server" "--stdio") ("json-languageserver" "--stdio"))))
230 (dockerfile-mode . ("docker-langserver" "--stdio"))
231 ((clojure-mode clojurescript-mode clojurec-mode)
232 . ("clojure-lsp"))
233 (csharp-mode . ("omnisharp" "-lsp"))
234 (purescript-mode . ("purescript-language-server" "--stdio"))
235 (perl-mode . ("perl" "-MPerl::LanguageServer" "-e" "Perl::LanguageServer::run"))
236 (markdown-mode . ("marksman" "server")))
237 "How the command `eglot' guesses the server to start.
238An association list of (MAJOR-MODE . CONTACT) pairs. MAJOR-MODE
239identifies the buffers that are to be managed by a specific
240language server. The associated CONTACT specifies how to connect
241to a server for those buffers.
242
243MAJOR-MODE can be:
244
245* In the most common case, a symbol such as `c-mode';
246
247* A list (MAJOR-MODE-SYMBOL :LANGUAGE-ID ID) where
248 MAJOR-MODE-SYMBOL is the aforementioned symbol and ID is a
249 string identifying the language to the server;
250
251* A list combining the previous two alternatives, meaning
252 multiple major modes will be associated with a single server
253 program. This association is such that the same resulting
254 server process will manage buffers of different major modes.
255
256CONTACT can be:
257
258* In the most common case, a list of strings (PROGRAM [ARGS...]).
259 PROGRAM is called with ARGS and is expected to serve LSP requests
260 over the standard input/output channels.
261
262* A list (PROGRAM [ARGS...] :initializationOptions OPTIONS),
263 whereupon PROGRAM is called with ARGS as in the first option,
264 and the LSP \"initializationOptions\" JSON object is
265 constructed from OPTIONS. If OPTIONS is a unary function, it
266 is called with the server instance and should return a JSON
267 object.
268
269* A list (HOST PORT [TCP-ARGS...]) where HOST is a string and
270 PORT is a positive integer for connecting to a server via TCP.
271 Remaining ARGS are passed to `open-network-stream' for
272 upgrading the connection with encryption or other capabilities.
273
274* A list (PROGRAM [ARGS...] :autoport [MOREARGS...]), whereupon a
275 combination of previous options is used. First, an attempt is
276 made to find an available server port, then PROGRAM is launched
277 with ARGS; the `:autoport' keyword substituted for that number;
278 and MOREARGS. Eglot then attempts to establish a TCP
279 connection to that port number on the localhost.
280
281* A cons (CLASS-NAME . INITARGS) where CLASS-NAME is a symbol
282 designating a subclass of `eglot-lsp-server', for representing
283 experimental LSP servers. INITARGS is a keyword-value plist
284 used to initialize the object of CLASS-NAME, or a plain list
285 interpreted as the previous descriptions of CONTACT. In the
286 latter case that plain list is used to produce a plist with a
287 suitable :PROCESS initarg to CLASS-NAME. The class
288 `eglot-lsp-server' descends from `jsonrpc-process-connection',
289 which you should see for the semantics of the mandatory
290 :PROCESS argument.
291
292* A function of a single argument producing any of the above
293 values for CONTACT. The argument's value is non-nil if the
294 connection was requested interactively (e.g. from the `eglot'
295 command), and nil if it wasn't (e.g. from `eglot-ensure'). If
296 the call is interactive, the function can ask the user for
297 hints on finding the required programs, etc. Otherwise, it
298 should not ask the user for any input, and return nil or signal
299 an error if it can't produce a valid CONTACT.")
300
301(defface eglot-highlight-symbol-face
302 '((t (:inherit bold)))
303 "Face used to highlight the symbol at point.")
304
305(defface eglot-mode-line
306 '((t (:inherit font-lock-constant-face :weight bold)))
307 "Face for package-name in Eglot's mode line.")
308
309(defface eglot-diagnostic-tag-unnecessary-face
310 '((t (:inherit shadow)))
311 "Face used to render unused or unnecessary code.")
312
313(defface eglot-diagnostic-tag-deprecated-face
314 '((t . (:inherit shadow :strike-through t)))
315 "Face used to render deprecated or obsolete code.")
316
317(defcustom eglot-autoreconnect 3
318 "Control ability to reconnect automatically to the LSP server.
319If t, always reconnect automatically (not recommended). If nil,
320never reconnect automatically after unexpected server shutdowns,
321crashes or network failures. A positive integer number says to
322only autoreconnect if the previous successful connection attempt
323lasted more than that many seconds."
324 :type '(choice (boolean :tag "Whether to inhibit autoreconnection")
325 (integer :tag "Number of seconds")))
326
327(defcustom eglot-connect-timeout 30
328 "Number of seconds before timing out LSP connection attempts.
329If nil, never time out."
330 :type 'number)
331
332(defcustom eglot-sync-connect 3
333 "Control blocking of LSP connection attempts.
334If t, block for `eglot-connect-timeout' seconds. A positive
335integer number means block for that many seconds, and then wait
336for the connection in the background. nil has the same meaning
337as 0, i.e. don't block at all."
338 :type '(choice (boolean :tag "Whether to inhibit autoreconnection")
339 (integer :tag "Number of seconds")))
340
341(defcustom eglot-autoshutdown nil
342 "If non-nil, shut down server after killing last managed buffer."
343 :type 'boolean)
344
345(defcustom eglot-send-changes-idle-time 0.5
346 "Don't tell server of changes before Emacs's been idle for this many seconds."
347 :type 'number)
348
349(defcustom eglot-events-buffer-size 2000000
350 "Control the size of the Eglot events buffer.
351If a number, don't let the buffer grow larger than that many
352characters. If 0, don't use an event's buffer at all. If nil,
353let the buffer grow forever.
354
355For changes on this variable to take effect on a connection
356already started, you need to restart the connection. That can be
357done by `eglot-reconnect'."
358 :type '(choice (const :tag "No limit" nil)
359 (integer :tag "Number of characters")))
360
361(defcustom eglot-confirm-server-initiated-edits 'confirm
362 "Non-nil if server-initiated edits should be confirmed with user."
363 :type '(choice (const :tag "Don't show confirmation prompt" nil)
364 (symbol :tag "Show confirmation prompt" 'confirm)))
365
366(defcustom eglot-extend-to-xref nil
367 "If non-nil, activate Eglot in cross-referenced non-project files."
368 :type 'boolean)
369
370(defcustom eglot-menu-string "eglot"
371 "String displayed in mode line when Eglot is active."
372 :type 'string)
373
374(defvar eglot-withhold-process-id nil
375 "If non-nil, Eglot will not send the Emacs process id to the language server.
376This can be useful when using docker to run a language server.")
377
378;; Customizable via `completion-category-overrides'.
379(when (assoc 'flex completion-styles-alist)
380 (add-to-list 'completion-category-defaults '(eglot (styles flex basic))))
381
382
383;;; Constants
384;;;
385(defconst eglot--symbol-kind-names
386 `((1 . "File") (2 . "Module")
387 (3 . "Namespace") (4 . "Package") (5 . "Class")
388 (6 . "Method") (7 . "Property") (8 . "Field")
389 (9 . "Constructor") (10 . "Enum") (11 . "Interface")
390 (12 . "Function") (13 . "Variable") (14 . "Constant")
391 (15 . "String") (16 . "Number") (17 . "Boolean")
392 (18 . "Array") (19 . "Object") (20 . "Key")
393 (21 . "Null") (22 . "EnumMember") (23 . "Struct")
394 (24 . "Event") (25 . "Operator") (26 . "TypeParameter")))
395
396(defconst eglot--kind-names
397 `((1 . "Text") (2 . "Method") (3 . "Function") (4 . "Constructor")
398 (5 . "Field") (6 . "Variable") (7 . "Class") (8 . "Interface")
399 (9 . "Module") (10 . "Property") (11 . "Unit") (12 . "Value")
400 (13 . "Enum") (14 . "Keyword") (15 . "Snippet") (16 . "Color")
401 (17 . "File") (18 . "Reference") (19 . "Folder") (20 . "EnumMember")
402 (21 . "Constant") (22 . "Struct") (23 . "Event") (24 . "Operator")
403 (25 . "TypeParameter")))
404
405(defconst eglot--tag-faces
406 `((1 . eglot-diagnostic-tag-unnecessary-face)
407 (2 . eglot-diagnostic-tag-deprecated-face)))
408
409(defvaralias 'eglot-{} 'eglot--{})
410(defconst eglot--{} (make-hash-table :size 1) "The empty JSON object.")
411
412(defun eglot--executable-find (command &optional remote)
413 "Like Emacs 27's `executable-find', ignore REMOTE on Emacs 26."
414 (if (>= emacs-major-version 27) (executable-find command remote)
415 (executable-find command)))
416
417
418;;; Message verification helpers
419;;;
420(eval-and-compile
421 (defvar eglot--lsp-interface-alist
422 `(
423 (CodeAction (:title) (:kind :diagnostics :edit :command :isPreferred))
424 (ConfigurationItem () (:scopeUri :section))
425 (Command ((:title . string) (:command . string)) (:arguments))
426 (CompletionItem (:label)
427 (:kind :detail :documentation :deprecated :preselect
428 :sortText :filterText :insertText :insertTextFormat
429 :textEdit :additionalTextEdits :commitCharacters
430 :command :data :tags))
431 (Diagnostic (:range :message) (:severity :code :source :relatedInformation :codeDescription :tags))
432 (DocumentHighlight (:range) (:kind))
433 (FileSystemWatcher (:globPattern) (:kind))
434 (Hover (:contents) (:range))
435 (InitializeResult (:capabilities) (:serverInfo))
436 (Location (:uri :range))
437 (LocationLink (:targetUri :targetRange :targetSelectionRange) (:originSelectionRange))
438 (LogMessageParams (:type :message))
439 (MarkupContent (:kind :value))
440 (ParameterInformation (:label) (:documentation))
441 (Position (:line :character))
442 (Range (:start :end))
443 (Registration (:id :method) (:registerOptions))
444 (ResponseError (:code :message) (:data))
445 (ShowMessageParams (:type :message))
446 (ShowMessageRequestParams (:type :message) (:actions))
447 (SignatureHelp (:signatures) (:activeSignature :activeParameter))
448 (SignatureInformation (:label) (:documentation :parameters :activeParameter))
449 (SymbolInformation (:name :kind :location)
450 (:deprecated :containerName))
451 (DocumentSymbol (:name :range :selectionRange :kind)
452 ;; `:containerName' isn't really allowed , but
453 ;; it simplifies the impl of `eglot-imenu'.
454 (:detail :deprecated :children :containerName))
455 (TextDocumentEdit (:textDocument :edits) ())
456 (TextEdit (:range :newText))
457 (VersionedTextDocumentIdentifier (:uri :version) ())
458 (WorkspaceEdit () (:changes :documentChanges))
459 (WorkspaceSymbol (:name :kind) (:containerName :location :data)))
460 "Alist (INTERFACE-NAME . INTERFACE) of known external LSP interfaces.
461
462INTERFACE-NAME is a symbol designated by the spec as
463\"interface\". INTERFACE is a list (REQUIRED OPTIONAL) where
464REQUIRED and OPTIONAL are lists of KEYWORD designating field
465names that must be, or may be, respectively, present in a message
466adhering to that interface. KEY can be a keyword or a cons (SYM
467TYPE), where type is used by `cl-typep' to check types at
468runtime.
469
470Here's what an element of this alist might look like:
471
472 (Command ((:title . string) (:command . string)) (:arguments))"))
473
474(eval-and-compile
475 (defvar eglot-strict-mode
476 '(;; Uncomment next lines for fun and debugging
477 ;; disallow-non-standard-keys
478 ;; enforce-required-keys
479 ;; enforce-optional-keys
480 )
481 "How strictly to check LSP interfaces at compile- and run-time.
482
483Value is a list of symbols (if the list is empty, no checks are
484performed).
485
486If the symbol `disallow-non-standard-keys' is present, an error
487is raised if any extraneous fields are sent by the server. At
488compile-time, a warning is raised if a destructuring spec
489includes such a field.
490
491If the symbol `enforce-required-keys' is present, an error is
492raised if any required fields are missing from the message sent
493from the server. At compile-time, a warning is raised if a
494destructuring spec doesn't use such a field.
495
496If the symbol `enforce-optional-keys' is present, nothing special
497happens at run-time. At compile-time, a warning is raised if a
498destructuring spec doesn't use all optional fields.
499
500If the symbol `disallow-unknown-methods' is present, Eglot warns
501on unknown notifications and errors on unknown requests."))
502
503(cl-defun eglot--check-object (interface-name
504 object
505 &optional
506 (enforce-required t)
507 (disallow-non-standard t)
508 (check-types t))
509 "Check that OBJECT conforms to INTERFACE. Error otherwise."
510 (cl-destructuring-bind
511 (&key types required-keys optional-keys &allow-other-keys)
512 (eglot--interface interface-name)
513 (when-let ((missing (and enforce-required
514 (cl-set-difference required-keys
515 (eglot--plist-keys object)))))
516 (eglot--error "A `%s' must have %s" interface-name missing))
517 (when-let ((excess (and disallow-non-standard
518 (cl-set-difference
519 (eglot--plist-keys object)
520 (append required-keys optional-keys)))))
521 (eglot--error "A `%s' mustn't have %s" interface-name excess))
522 (when check-types
523 (cl-loop
524 for (k v) on object by #'cddr
525 for type = (or (cdr (assoc k types)) t) ;; FIXME: enforce nil type?
526 unless (cl-typep v type)
527 do (eglot--error "A `%s' must have a %s as %s, but has %s"
528 interface-name )))
529 t))
530
531(eval-and-compile
532 (defun eglot--keywordize-vars (vars)
533 (mapcar (lambda (var) (intern (format ":%s" var))) vars))
534
535 (defun eglot--ensure-type (k) (if (consp k) k (cons k t)))
536
537 (defun eglot--interface (interface-name)
538 (let* ((interface (assoc interface-name eglot--lsp-interface-alist))
539 (required (mapcar #'eglot--ensure-type (car (cdr interface))))
540 (optional (mapcar #'eglot--ensure-type (cadr (cdr interface)))))
541 (list :types (append required optional)
542 :required-keys (mapcar #'car required)
543 :optional-keys (mapcar #'car optional))))
544
545 (defun eglot--check-dspec (interface-name dspec)
546 "Check destructuring spec DSPEC against INTERFACE-NAME."
547 (cl-destructuring-bind (&key required-keys optional-keys &allow-other-keys)
548 (eglot--interface interface-name)
549 (cond ((or required-keys optional-keys)
550 (let ((too-many
551 (and
552 (memq 'disallow-non-standard-keys eglot-strict-mode)
553 (cl-set-difference
554 (eglot--keywordize-vars dspec)
555 (append required-keys optional-keys))))
556 (ignored-required
557 (and
558 (memq 'enforce-required-keys eglot-strict-mode)
559 (cl-set-difference
560 required-keys (eglot--keywordize-vars dspec))))
561 (missing-out
562 (and
563 (memq 'enforce-optional-keys eglot-strict-mode)
564 (cl-set-difference
565 optional-keys (eglot--keywordize-vars dspec)))))
566 (when too-many (byte-compile-warn
567 "Destructuring for %s has extraneous %s"
568 interface-name too-many))
569 (when ignored-required (byte-compile-warn
570 "Destructuring for %s ignores required %s"
571 interface-name ignored-required))
572 (when missing-out (byte-compile-warn
573 "Destructuring for %s is missing out on %s"
574 interface-name missing-out))))
575 (t
576 (byte-compile-warn "Unknown LSP interface %s" interface-name))))))
577
578(cl-defmacro eglot--dbind (vars object &body body)
579 "Destructure OBJECT, binding VARS in BODY.
580VARS is ([(INTERFACE)] SYMS...)
581Honour `eglot-strict-mode'."
582 (declare (indent 2) (debug (sexp sexp &rest form)))
583 (let ((interface-name (if (consp (car vars))
584 (car (pop vars))))
585 (object-once (make-symbol "object-once"))
586 (fn-once (make-symbol "fn-once")))
587 (cond (interface-name
588 (eglot--check-dspec interface-name vars)
589 `(let ((,object-once ,object))
590 (cl-destructuring-bind (&key ,@vars &allow-other-keys) ,object-once
591 (eglot--check-object ',interface-name ,object-once
592 (memq 'enforce-required-keys eglot-strict-mode)
593 (memq 'disallow-non-standard-keys eglot-strict-mode)
594 (memq 'check-types eglot-strict-mode))
595 ,@body)))
596 (t
597 `(let ((,object-once ,object)
598 (,fn-once (lambda (,@vars) ,@body)))
599 (if (memq 'disallow-non-standard-keys eglot-strict-mode)
600 (cl-destructuring-bind (&key ,@vars) ,object-once
601 (funcall ,fn-once ,@vars))
602 (cl-destructuring-bind (&key ,@vars &allow-other-keys) ,object-once
603 (funcall ,fn-once ,@vars))))))))
604
605
606(cl-defmacro eglot--lambda (cl-lambda-list &body body)
607 "Function of args CL-LAMBDA-LIST for processing INTERFACE objects.
608Honour `eglot-strict-mode'."
609 (declare (indent 1) (debug (sexp &rest form)))
610 (let ((e (cl-gensym "jsonrpc-lambda-elem")))
611 `(lambda (,e) (eglot--dbind ,cl-lambda-list ,e ,@body))))
612
613(cl-defmacro eglot--dcase (obj &rest clauses)
614 "Like `pcase', but for the LSP object OBJ.
615CLAUSES is a list (DESTRUCTURE FORMS...) where DESTRUCTURE is
616treated as in `eglot-dbind'."
617 (declare (indent 1) (debug (sexp &rest (sexp &rest form))))
618 (let ((obj-once (make-symbol "obj-once")))
619 `(let ((,obj-once ,obj))
620 (cond
621 ,@(cl-loop
622 for (vars . body) in clauses
623 for vars-as-keywords = (eglot--keywordize-vars vars)
624 for interface-name = (if (consp (car vars))
625 (car (pop vars)))
626 for condition =
627 (cond (interface-name
628 (eglot--check-dspec interface-name vars)
629 ;; In this mode, in runtime, we assume
630 ;; `eglot-strict-mode' is partially on, otherwise we
631 ;; can't disambiguate between certain types.
632 `(ignore-errors
633 (eglot--check-object
634 ',interface-name ,obj-once
635 t
636 (memq 'disallow-non-standard-keys eglot-strict-mode)
637 t)))
638 (t
639 ;; In this interface-less mode we don't check
640 ;; `eglot-strict-mode' at all: just check that the object
641 ;; has all the keys the user wants to destructure.
642 `(null (cl-set-difference
643 ',vars-as-keywords
644 (eglot--plist-keys ,obj-once)))))
645 collect `(,condition
646 (cl-destructuring-bind (&key ,@vars &allow-other-keys)
647 ,obj-once
648 ,@body)))
649 (t
650 (eglot--error "%S didn't match any of %S"
651 ,obj-once
652 ',(mapcar #'car clauses)))))))
653
654
655;;; API (WORK-IN-PROGRESS!)
656;;;
657(cl-defmacro eglot--when-live-buffer (buf &rest body)
658 "Check BUF live, then do BODY in it." (declare (indent 1) (debug t))
659 (let ((b (cl-gensym)))
660 `(let ((,b ,buf)) (if (buffer-live-p ,b) (with-current-buffer ,b ,@body)))))
661
662(cl-defmacro eglot--when-buffer-window (buf &body body)
663 "Check BUF showing somewhere, then do BODY in it." (declare (indent 1) (debug t))
664 (let ((b (cl-gensym)))
665 `(let ((,b ,buf))
666 ;;notice the exception when testing with `ert'
667 (when (or (get-buffer-window ,b) (ert-running-test))
668 (with-current-buffer ,b ,@body)))))
669
670(cl-defmacro eglot--widening (&rest body)
671 "Save excursion and restriction. Widen. Then run BODY." (declare (debug t))
672 `(save-excursion (save-restriction (widen) ,@body)))
673
674(cl-defgeneric eglot-handle-request (server method &rest params)
675 "Handle SERVER's METHOD request with PARAMS.")
676
677(cl-defgeneric eglot-handle-notification (server method &rest params)
678 "Handle SERVER's METHOD notification with PARAMS.")
679
680(cl-defgeneric eglot-execute-command (server command arguments)
681 "Ask SERVER to execute COMMAND with ARGUMENTS.")
682
683(cl-defgeneric eglot-initialization-options (server)
684 "JSON object to send under `initializationOptions'."
685 (:method (s)
686 (let ((probe (plist-get (eglot--saved-initargs s) :initializationOptions)))
687 (cond ((functionp probe) (funcall probe s))
688 (probe)
689 (t eglot--{})))))
690
691(cl-defgeneric eglot-register-capability (server method id &rest params)
692 "Ask SERVER to register capability METHOD marked with ID."
693 (:method
694 (_s method _id &rest _params)
695 (eglot--warn "Server tried to register unsupported capability `%s'"
696 method)))
697
698(cl-defgeneric eglot-unregister-capability (server method id &rest params)
699 "Ask SERVER to register capability METHOD marked with ID."
700 (:method
701 (_s method _id &rest _params)
702 (eglot--warn "Server tried to unregister unsupported capability `%s'"
703 method)))
704
705(cl-defgeneric eglot-client-capabilities (server)
706 "What the Eglot LSP client supports for SERVER."
707 (:method (s)
708 (list
709 :workspace (list
710 :applyEdit t
711 :executeCommand `(:dynamicRegistration :json-false)
712 :workspaceEdit `(:documentChanges t)
713 :didChangeWatchedFiles
714 `(:dynamicRegistration
715 ,(if (eglot--trampish-p s) :json-false t))
716 :symbol `(:dynamicRegistration :json-false)
717 :configuration t
718 :workspaceFolders t)
719 :textDocument
720 (list
721 :synchronization (list
722 :dynamicRegistration :json-false
723 :willSave t :willSaveWaitUntil t :didSave t)
724 :completion (list :dynamicRegistration :json-false
725 :completionItem
726 `(:snippetSupport
727 ,(if (eglot--snippet-expansion-fn)
728 t
729 :json-false)
730 :deprecatedSupport t
731 :tagSupport (:valueSet [1]))
732 :contextSupport t)
733 :hover (list :dynamicRegistration :json-false
734 :contentFormat
735 (if (fboundp 'gfm-view-mode)
736 ["markdown" "plaintext"]
737 ["plaintext"]))
738 :signatureHelp (list :dynamicRegistration :json-false
739 :signatureInformation
740 `(:parameterInformation
741 (:labelOffsetSupport t)
742 :activeParameterSupport t))
743 :references `(:dynamicRegistration :json-false)
744 :definition (list :dynamicRegistration :json-false
745 :linkSupport t)
746 :declaration (list :dynamicRegistration :json-false
747 :linkSupport t)
748 :implementation (list :dynamicRegistration :json-false
749 :linkSupport t)
750 :typeDefinition (list :dynamicRegistration :json-false
751 :linkSupport t)
752 :documentSymbol (list
753 :dynamicRegistration :json-false
754 :hierarchicalDocumentSymbolSupport t
755 :symbolKind `(:valueSet
756 [,@(mapcar
757 #'car eglot--symbol-kind-names)]))
758 :documentHighlight `(:dynamicRegistration :json-false)
759 :codeAction (list
760 :dynamicRegistration :json-false
761 :codeActionLiteralSupport
762 '(:codeActionKind
763 (:valueSet
764 ["quickfix"
765 "refactor" "refactor.extract"
766 "refactor.inline" "refactor.rewrite"
767 "source" "source.organizeImports"]))
768 :isPreferredSupport t)
769 :formatting `(:dynamicRegistration :json-false)
770 :rangeFormatting `(:dynamicRegistration :json-false)
771 :rename `(:dynamicRegistration :json-false)
772 :publishDiagnostics (list :relatedInformation :json-false
773 ;; TODO: We can support :codeDescription after
774 ;; adding an appropriate UI to
775 ;; Flymake.
776 :codeDescriptionSupport :json-false
777 :tagSupport
778 `(:valueSet
779 [,@(mapcar
780 #'car eglot--tag-faces)])))
781 :experimental eglot--{})))
782
783(cl-defgeneric eglot-workspace-folders (server)
784 "Return workspaceFolders for SERVER."
785 (let ((project (eglot--project server)))
786 (vconcat
787 (mapcar (lambda (dir)
788 (list :uri (eglot--path-to-uri dir)
789 :name (abbreviate-file-name dir)))
790 `(,(project-root project) ,@(project-external-roots project))))))
791
792(defclass eglot-lsp-server (jsonrpc-process-connection)
793 ((project-nickname
794 :documentation "Short nickname for the associated project."
795 :accessor eglot--project-nickname
796 :reader eglot-project-nickname)
797 (major-modes
798 :documentation "Major modes server is responsible for in a given project."
799 :accessor eglot--major-modes)
800 (language-id
801 :documentation "Language ID string for the mode."
802 :accessor eglot--language-id)
803 (capabilities
804 :documentation "JSON object containing server capabilities."
805 :accessor eglot--capabilities)
806 (server-info
807 :documentation "JSON object containing server info."
808 :accessor eglot--server-info)
809 (shutdown-requested
810 :documentation "Flag set when server is shutting down."
811 :accessor eglot--shutdown-requested)
812 (project
813 :documentation "Project associated with server."
814 :accessor eglot--project)
815 (spinner
816 :documentation "List (ID DOING-WHAT DONE-P) representing server progress."
817 :initform `(nil nil t) :accessor eglot--spinner)
818 (inhibit-autoreconnect
819 :initform t
820 :documentation "Generalized boolean inhibiting auto-reconnection if true."
821 :accessor eglot--inhibit-autoreconnect)
822 (file-watches
823 :documentation "Map ID to list of WATCHES for `didChangeWatchedFiles'."
824 :initform (make-hash-table :test #'equal) :accessor eglot--file-watches)
825 (managed-buffers
826 :documentation "List of buffers managed by server."
827 :accessor eglot--managed-buffers)
828 (saved-initargs
829 :documentation "Saved initargs for reconnection purposes."
830 :accessor eglot--saved-initargs)
831 (inferior-process
832 :documentation "Server subprocess started automatically."
833 :accessor eglot--inferior-process))
834 :documentation
835 "Represents a server. Wraps a process for LSP communication.")
836
837(cl-defmethod initialize-instance :before ((_server eglot-lsp-server) &optional args)
838 (cl-remf args :initializationOptions))
839
840
841;;; Process management
842(defvar eglot--servers-by-project (make-hash-table :test #'equal)
843 "Keys are projects. Values are lists of processes.")
844
845(defun eglot-shutdown (server &optional _interactive timeout preserve-buffers)
846 "Politely ask SERVER to quit.
847Interactively, read SERVER from the minibuffer unless there is
848only one and it's managing the current buffer.
849
850Forcefully quit it if it doesn't respond within TIMEOUT seconds.
851TIMEOUT defaults to 1.5 seconds. Don't leave this function with
852the server still running.
853
854If PRESERVE-BUFFERS is non-nil (interactively, when called with a
855prefix argument), do not kill events and output buffers of
856SERVER."
857 (interactive (list (eglot--read-server "Shutdown which server"
858 (eglot-current-server))
859 t nil current-prefix-arg))
860 (eglot--message "Asking %s politely to terminate" (jsonrpc-name server))
861 (unwind-protect
862 (progn
863 (setf (eglot--shutdown-requested server) t)
864 (jsonrpc-request server :shutdown nil :timeout (or timeout 1.5))
865 (jsonrpc-notify server :exit nil))
866 ;; Now ask jsonrpc.el to shut down the server.
867 (jsonrpc-shutdown server (not preserve-buffers))
868 (unless preserve-buffers (kill-buffer (jsonrpc-events-buffer server)))))
869
870(defun eglot-shutdown-all (&optional preserve-buffers)
871 "Politely ask all language servers to quit, in order.
872PRESERVE-BUFFERS as in `eglot-shutdown', which see."
873 (interactive (list current-prefix-arg))
874 (cl-loop for ss being the hash-values of eglot--servers-by-project
875 do (cl-loop for s in ss do (eglot-shutdown s nil preserve-buffers))))
876
877(defun eglot--on-shutdown (server)
878 "Called by jsonrpc.el when SERVER is already dead."
879 ;; Turn off `eglot--managed-mode' where appropriate.
880 (dolist (buffer (eglot--managed-buffers server))
881 (let (;; Avoid duplicate shutdowns (github#389)
882 (eglot-autoshutdown nil))
883 (eglot--when-live-buffer buffer (eglot--managed-mode-off))))
884 ;; Kill any expensive watches
885 (maphash (lambda (_id watches)
886 (mapcar #'file-notify-rm-watch watches))
887 (eglot--file-watches server))
888 ;; Kill any autostarted inferior processes
889 (when-let (proc (eglot--inferior-process server))
890 (delete-process proc))
891 ;; Sever the project/server relationship for `server'
892 (setf (gethash (eglot--project server) eglot--servers-by-project)
893 (delq server
894 (gethash (eglot--project server) eglot--servers-by-project)))
895 (cond ((eglot--shutdown-requested server)
896 t)
897 ((not (eglot--inhibit-autoreconnect server))
898 (eglot--warn "Reconnecting after unexpected server exit.")
899 (eglot-reconnect server))
900 ((timerp (eglot--inhibit-autoreconnect server))
901 (eglot--warn "Not auto-reconnecting, last one didn't last long."))))
902
903(defun eglot--all-major-modes ()
904 "Return all known major modes."
905 (let ((retval))
906 (mapatoms (lambda (sym)
907 (when (plist-member (symbol-plist sym) 'derived-mode-parent)
908 (push sym retval))))
909 retval))
910
911(defvar eglot--command-history nil
912 "History of CONTACT arguments to `eglot'.")
913
914(defun eglot--lookup-mode (mode)
915 "Lookup `eglot-server-programs' for MODE.
916Return (MANAGED-MODES LANGUAGE-ID CONTACT-PROXY).
917
918MANAGED-MODES is a list with MODE as its first elements.
919Subsequent elements are other major modes also potentially
920managed by the server that is to manage MODE.
921
922If not specified in `eglot-server-programs' (which see),
923LANGUAGE-ID is determined from MODE's name.
924
925CONTACT-PROXY is the value of the corresponding
926`eglot-server-programs' entry."
927 (cl-loop
928 for (modes . contact) in eglot-server-programs
929 for mode-symbols = (cons mode
930 (delete mode
931 (mapcar #'car
932 (mapcar #'eglot--ensure-list
933 (eglot--ensure-list modes)))))
934 thereis (cl-some
935 (lambda (spec)
936 (cl-destructuring-bind (probe &key language-id &allow-other-keys)
937 (eglot--ensure-list spec)
938 (and (provided-mode-derived-p mode probe)
939 (list
940 mode-symbols
941 (or language-id
942 (or (get mode 'eglot-language-id)
943 (get spec 'eglot-language-id)
944 (string-remove-suffix "-mode" (symbol-name mode))))
945 contact))))
946 (if (or (symbolp modes) (keywordp (cadr modes)))
947 (list modes) modes))))
948
949(defun eglot--guess-contact (&optional interactive)
950 "Helper for `eglot'.
951Return (MANAGED-MODE PROJECT CLASS CONTACT LANG-ID). If INTERACTIVE is
952non-nil, maybe prompt user, else error as soon as something can't
953be guessed."
954 (let* ((guessed-mode (if buffer-file-name major-mode))
955 (main-mode
956 (cond
957 ((and interactive
958 (or (>= (prefix-numeric-value current-prefix-arg) 16)
959 (not guessed-mode)))
960 (intern
961 (completing-read
962 "[eglot] Start a server to manage buffers of what major mode? "
963 (mapcar #'symbol-name (eglot--all-major-modes)) nil t
964 (symbol-name guessed-mode) nil (symbol-name guessed-mode) nil)))
965 ((not guessed-mode)
966 (eglot--error "Can't guess mode to manage for `%s'" (current-buffer)))
967 (t guessed-mode)))
968 (triplet (eglot--lookup-mode main-mode))
969 (managed-modes (car triplet))
970 (language-id (or (cadr triplet)
971 (string-remove-suffix "-mode" (symbol-name guessed-mode))))
972 (guess (caddr triplet))
973 (guess (if (functionp guess)
974 (funcall guess interactive)
975 guess))
976 (class (or (and (consp guess) (symbolp (car guess))
977 (prog1 (unless current-prefix-arg (car guess))
978 (setq guess (cdr guess))))
979 'eglot-lsp-server))
980 (program (and (listp guess)
981 (stringp (car guess))
982 ;; A second element might be the port of a (host, port)
983 ;; pair, but in that case it is not a string.
984 (or (null (cdr guess)) (stringp (cadr guess)))
985 (car guess)))
986 (base-prompt
987 (and interactive
988 "Enter program to execute (or <host>:<port>): "))
989 (full-program-invocation
990 (and program
991 (cl-every #'stringp guess)
992 (combine-and-quote-strings guess)))
993 (prompt
994 (and base-prompt
995 (cond (current-prefix-arg base-prompt)
996 ((null guess)
997 (format "[eglot] Sorry, couldn't guess for `%s'!\n%s"
998 main-mode base-prompt))
999 ((and program
1000 (not (file-name-absolute-p program))
1001 (not (eglot--executable-find program t)))
1002 (if full-program-invocation
1003 (concat (format "[eglot] I guess you want to run `%s'"
1004 full-program-invocation)
1005 (format ", but I can't find `%s' in PATH!"
1006 program)
1007 "\n" base-prompt)
1008 (eglot--error
1009 (concat "`%s' not found in PATH, but can't form"
1010 " an interactive prompt for to fix %s!")
1011 program guess))))))
1012 (contact
1013 (or (and prompt
1014 (split-string-and-unquote
1015 (read-shell-command
1016 prompt
1017 full-program-invocation
1018 'eglot-command-history)))
1019 guess)))
1020 (list managed-modes (eglot--current-project) class contact language-id)))
1021
1022(defvar eglot-lsp-context)
1023(put 'eglot-lsp-context 'variable-documentation
1024 "Dynamically non-nil when searching for projects in LSP context.")
1025
1026(defvar eglot--servers-by-xrefed-file
1027 (make-hash-table :test 'equal :weakness 'value))
1028
1029(defun eglot--current-project ()
1030 "Return a project object for Eglot's LSP purposes.
1031This relies on `project-current' and thus on
1032`project-find-functions'. Functions in the latter
1033variable (which see) can query the value `eglot-lsp-context' to
1034decide whether a given directory is a project containing a
1035suitable root directory for a given LSP server's purposes."
1036 (let ((eglot-lsp-context t))
1037 (or (project-current) `(transient . ,default-directory))))
1038
1039;;;###autoload
1040(defun eglot (managed-major-mode project class contact language-id
1041 &optional interactive)
1042 "Start LSP server in support of PROJECT's buffers under MANAGED-MAJOR-MODE.
1043
1044This starts a Language Server Protocol (LSP) server suitable for the
1045buffers of PROJECT whose `major-mode' is MANAGED-MAJOR-MODE.
1046CLASS is the class of the LSP server to start and CONTACT specifies
1047how to connect to the server.
1048
1049Interactively, the command attempts to guess MANAGED-MAJOR-MODE
1050from the current buffer's `major-mode', CLASS and CONTACT from
1051`eglot-server-programs' looked up by the major mode, and PROJECT from
1052`project-find-functions'. The search for active projects in this
1053context binds `eglot-lsp-context' (which see).
1054
1055If it can't guess, it prompts the user for the mode and the server.
1056With a single \\[universal-argument] prefix arg, it always prompts for COMMAND.
1057With two \\[universal-argument], it also always prompts for MANAGED-MAJOR-MODE.
1058
1059The LSP server of CLASS is started (or contacted) via CONTACT.
1060If this operation is successful, current *and future* file
1061buffers of MANAGED-MAJOR-MODE inside PROJECT become \"managed\"
1062by the LSP server, meaning the information about their contents is
1063exchanged periodically with the server to provide enhanced
1064code-analysis via `xref-find-definitions', `flymake-mode',
1065`eldoc-mode', and `completion-at-point', among others.
1066
1067PROJECT is a project object as returned by `project-current'.
1068
1069CLASS is a subclass of `eglot-lsp-server'.
1070
1071CONTACT specifies how to contact the server. It is a
1072keyword-value plist used to initialize CLASS or a plain list as
1073described in `eglot-server-programs', which see.
1074
1075LANGUAGE-ID is the language ID string to send to the server for
1076MANAGED-MAJOR-MODE, which matters to a minority of servers.
1077
1078INTERACTIVE is t if called interactively."
1079 (interactive (append (eglot--guess-contact t) '(t)))
1080 (let* ((current-server (eglot-current-server))
1081 (live-p (and current-server (jsonrpc-running-p current-server))))
1082 (if (and live-p
1083 interactive
1084 (y-or-n-p "[eglot] Live process found, reconnect instead? "))
1085 (eglot-reconnect current-server interactive)
1086 (when live-p (ignore-errors (eglot-shutdown current-server)))
1087 (eglot--connect managed-major-mode project class contact language-id))))
1088
1089(defun eglot-reconnect (server &optional interactive)
1090 "Reconnect to SERVER.
1091INTERACTIVE is t if called interactively."
1092 (interactive (list (eglot--current-server-or-lose) t))
1093 (when (jsonrpc-running-p server)
1094 (ignore-errors (eglot-shutdown server interactive nil 'preserve-buffers)))
1095 (eglot--connect (eglot--major-modes server)
1096 (eglot--project server)
1097 (eieio-object-class-name server)
1098 (eglot--saved-initargs server)
1099 (eglot--language-id server))
1100 (eglot--message "Reconnected!"))
1101
1102(defvar eglot--managed-mode) ; forward decl
1103
1104;;;###autoload
1105(defun eglot-ensure ()
1106 "Start Eglot session for current buffer if there isn't one."
1107 (let ((buffer (current-buffer)))
1108 (cl-labels
1109 ((maybe-connect
1110 ()
1111 (remove-hook 'post-command-hook #'maybe-connect nil)
1112 (eglot--when-live-buffer buffer
1113 (unless eglot--managed-mode
1114 (apply #'eglot--connect (eglot--guess-contact))))))
1115 (when buffer-file-name
1116 (add-hook 'post-command-hook #'maybe-connect 'append nil)))))
1117
1118(defun eglot-events-buffer (server)
1119 "Display events buffer for SERVER.
1120Use current server's or first available Eglot events buffer."
1121 (interactive (list (eglot-current-server)))
1122 (let ((buffer (if server (jsonrpc-events-buffer server)
1123 (cl-find "\\*EGLOT.*events\\*"
1124 (buffer-list)
1125 :key #'buffer-name :test #'string-match))))
1126 (if buffer (display-buffer buffer)
1127 (eglot--error "Can't find an Eglot events buffer!"))))
1128
1129(defun eglot-stderr-buffer (server)
1130 "Display stderr buffer for SERVER."
1131 (interactive (list (eglot--current-server-or-lose)))
1132 (display-buffer (jsonrpc-stderr-buffer server)))
1133
1134(defun eglot-forget-pending-continuations (server)
1135 "Forget pending requests for SERVER."
1136 (interactive (list (eglot--current-server-or-lose)))
1137 (jsonrpc-forget-pending-continuations server))
1138
1139(defvar eglot-connect-hook
1140 '(eglot-signal-didChangeConfiguration)
1141 "Hook run after connecting in `eglot--connect'.")
1142
1143(defvar eglot-server-initialized-hook
1144 '()
1145 "Hook run after a `eglot-lsp-server' instance is created.
1146
1147That is before a connection was established. Use
1148`eglot-connect-hook' to hook into when a connection was
1149successfully established and the server on the other side has
1150received the initializing configuration.
1151
1152Each function is passed the server as an argument")
1153
1154(defun eglot--cmd (contact)
1155 "Helper for `eglot--connect'."
1156 (if (file-remote-p default-directory)
1157 ;; TODO: this seems like a bug, although it’s everywhere. For
1158 ;; some reason, for remote connections only, over a pipe, we
1159 ;; need to turn off line buffering on the tty.
1160 ;;
1161 ;; Not only does this seem like there should be a better way,
1162 ;; but it almost certainly doesn’t work on non-unix systems.
1163 (list "sh" "-c"
1164 (string-join (cons "stty raw > /dev/null;"
1165 (mapcar #'shell-quote-argument contact))
1166 " "))
1167 contact))
1168
1169(defvar-local eglot--cached-server nil
1170 "A cached reference to the current Eglot server.")
1171
1172(defun eglot--connect (managed-modes project class contact language-id)
1173 "Connect to MANAGED-MODES, LANGUAGE-ID, PROJECT, CLASS and CONTACT.
1174This docstring appeases checkdoc, that's all."
1175 (let* ((default-directory (project-root project))
1176 (nickname (file-name-base (directory-file-name default-directory)))
1177 (readable-name (format "EGLOT (%s/%s)" nickname managed-modes))
1178 autostart-inferior-process
1179 server-info
1180 (contact (if (functionp contact) (funcall contact) contact))
1181 (initargs
1182 (cond ((keywordp (car contact)) contact)
1183 ((integerp (cadr contact))
1184 (setq server-info (list (format "%s:%s" (car contact)
1185 (cadr contact))))
1186 `(:process ,(lambda ()
1187 (apply #'open-network-stream
1188 readable-name nil
1189 (car contact) (cadr contact)
1190 (cddr contact)))))
1191 ((and (stringp (car contact)) (memq :autoport contact))
1192 (setq server-info (list "<inferior process>"))
1193 `(:process ,(lambda ()
1194 (pcase-let ((`(,connection . ,inferior)
1195 (eglot--inferior-bootstrap
1196 readable-name
1197 contact)))
1198 (setq autostart-inferior-process inferior)
1199 connection))))
1200 ((stringp (car contact))
1201 (let* ((probe (cl-position-if #'keywordp contact))
1202 (more-initargs (and probe (cl-subseq contact probe)))
1203 (contact (cl-subseq contact 0 probe)))
1204 `(:process
1205 ,(lambda ()
1206 (let ((default-directory default-directory))
1207 (make-process
1208 :name readable-name
1209 :command (setq server-info (eglot--cmd contact))
1210 :connection-type 'pipe
1211 :coding 'utf-8-emacs-unix
1212 :noquery t
1213 :stderr (get-buffer-create
1214 (format "*%s stderr*" readable-name))
1215 :file-handler t)))
1216 ,@more-initargs)))))
1217 (spread (lambda (fn) (lambda (server method params)
1218 (let ((eglot--cached-server server))
1219 (apply fn server method (append params nil))))))
1220 (server
1221 (apply
1222 #'make-instance class
1223 :name readable-name
1224 :events-buffer-scrollback-size eglot-events-buffer-size
1225 :notification-dispatcher (funcall spread #'eglot-handle-notification)
1226 :request-dispatcher (funcall spread #'eglot-handle-request)
1227 :on-shutdown #'eglot--on-shutdown
1228 initargs))
1229 (cancelled nil)
1230 (tag (make-symbol "connected-catch-tag")))
1231 (when server-info
1232 (jsonrpc--debug server "Running language server: %s"
1233 (string-join server-info " ")))
1234 (setf (eglot--saved-initargs server) initargs)
1235 (setf (eglot--project server) project)
1236 (setf (eglot--project-nickname server) nickname)
1237 (setf (eglot--major-modes server) (eglot--ensure-list managed-modes))
1238 (setf (eglot--language-id server) language-id)
1239 (setf (eglot--inferior-process server) autostart-inferior-process)
1240 (run-hook-with-args 'eglot-server-initialized-hook server)
1241 ;; Now start the handshake. To honour `eglot-sync-connect'
1242 ;; maybe-sync-maybe-async semantics we use `jsonrpc-async-request'
1243 ;; and mimic most of `jsonrpc-request'.
1244 (unwind-protect
1245 (condition-case _quit
1246 (let ((retval
1247 (catch tag
1248 (jsonrpc-async-request
1249 server
1250 :initialize
1251 (list :processId
1252 (unless (or eglot-withhold-process-id
1253 (file-remote-p default-directory)
1254 (eq (jsonrpc-process-type server)
1255 'network))
1256 (emacs-pid))
1257 ;; Maybe turn trampy `/ssh:foo@bar:/path/to/baz.py'
1258 ;; into `/path/to/baz.py', so LSP groks it.
1259 :rootPath (file-local-name
1260 (expand-file-name default-directory))
1261 :rootUri (eglot--path-to-uri default-directory)
1262 :initializationOptions (eglot-initialization-options
1263 server)
1264 :capabilities (eglot-client-capabilities server)
1265 :workspaceFolders (eglot-workspace-folders server))
1266 :success-fn
1267 (eglot--lambda ((InitializeResult) capabilities serverInfo)
1268 (unless cancelled
1269 (push server
1270 (gethash project eglot--servers-by-project))
1271 (setf (eglot--capabilities server) capabilities)
1272 (setf (eglot--server-info server) serverInfo)
1273 (jsonrpc-notify server :initialized eglot--{})
1274 (dolist (buffer (buffer-list))
1275 (with-current-buffer buffer
1276 ;; No need to pass SERVER as an argument: it has
1277 ;; been registered in `eglot--servers-by-project',
1278 ;; so that it can be found (and cached) from
1279 ;; `eglot--maybe-activate-editing-mode' in any
1280 ;; managed buffer.
1281 (eglot--maybe-activate-editing-mode)))
1282 (setf (eglot--inhibit-autoreconnect server)
1283 (cond
1284 ((booleanp eglot-autoreconnect)
1285 (not eglot-autoreconnect))
1286 ((cl-plusp eglot-autoreconnect)
1287 (run-with-timer
1288 eglot-autoreconnect nil
1289 (lambda ()
1290 (setf (eglot--inhibit-autoreconnect server)
1291 (null eglot-autoreconnect)))))))
1292 (let ((default-directory (project-root project))
1293 (major-mode (car managed-modes)))
1294 (hack-dir-local-variables-non-file-buffer)
1295 (run-hook-with-args 'eglot-connect-hook server))
1296 (eglot--message
1297 "Connected! Server `%s' now managing `%s' buffers \
1298in project `%s'."
1299 (or (plist-get serverInfo :name)
1300 (jsonrpc-name server))
1301 managed-modes
1302 (eglot-project-nickname server))
1303 (when tag (throw tag t))))
1304 :timeout eglot-connect-timeout
1305 :error-fn (eglot--lambda ((ResponseError) code message)
1306 (unless cancelled
1307 (jsonrpc-shutdown server)
1308 (let ((msg (format "%s: %s" code message)))
1309 (if tag (throw tag `(error . ,msg))
1310 (eglot--error msg)))))
1311 :timeout-fn (lambda ()
1312 (unless cancelled
1313 (jsonrpc-shutdown server)
1314 (let ((msg (format "Timed out after %s seconds"
1315 eglot-connect-timeout)))
1316 (if tag (throw tag `(error . ,msg))
1317 (eglot--error msg))))))
1318 (cond ((numberp eglot-sync-connect)
1319 (accept-process-output nil eglot-sync-connect))
1320 (eglot-sync-connect
1321 (while t (accept-process-output
1322 nil eglot-connect-timeout)))))))
1323 (pcase retval
1324 (`(error . ,msg) (eglot--error msg))
1325 (`nil (eglot--message "Waiting in background for server `%s'"
1326 (jsonrpc-name server))
1327 nil)
1328 (_ server)))
1329 (quit (jsonrpc-shutdown server) (setq cancelled 'quit)))
1330 (setq tag nil))))
1331
1332(defun eglot--inferior-bootstrap (name contact &optional connect-args)
1333 "Use CONTACT to start a server, then connect to it.
1334Return a cons of two process objects (CONNECTION . INFERIOR).
1335Name both based on NAME.
1336CONNECT-ARGS are passed as additional arguments to
1337`open-network-stream'."
1338 (let* ((port-probe (make-network-process :name "eglot-port-probe-dummy"
1339 :server t
1340 :host "localhost"
1341 :service 0))
1342 (port-number (unwind-protect
1343 (process-contact port-probe :service)
1344 (delete-process port-probe)))
1345 inferior connection)
1346 (unwind-protect
1347 (progn
1348 (setq inferior
1349 (make-process
1350 :name (format "autostart-inferior-%s" name)
1351 :stderr (format "*%s stderr*" name)
1352 :noquery t
1353 :command (cl-subst
1354 (format "%s" port-number) :autoport contact)))
1355 (setq connection
1356 (cl-loop
1357 repeat 10 for i from 1
1358 do (accept-process-output nil 0.5)
1359 while (process-live-p inferior)
1360 do (eglot--message
1361 "Trying to connect to localhost and port %s (attempt %s)"
1362 port-number i)
1363 thereis (ignore-errors
1364 (apply #'open-network-stream
1365 (format "autoconnect-%s" name)
1366 nil
1367 "localhost" port-number connect-args))))
1368 (cons connection inferior))
1369 (cond ((and (process-live-p connection)
1370 (process-live-p inferior))
1371 (eglot--message "Done, connected to %s!" port-number))
1372 (t
1373 (when inferior (delete-process inferior))
1374 (when connection (delete-process connection))
1375 (eglot--error "Could not start and connect to server%s"
1376 (if inferior
1377 (format " started with %s"
1378 (process-command inferior))
1379 "!")))))))
1380
1381
1382;;; Helpers (move these to API?)
1383;;;
1384(defun eglot--error (format &rest args)
1385 "Error out with FORMAT with ARGS."
1386 (error "[eglot] %s" (apply #'format format args)))
1387
1388(defun eglot--message (format &rest args)
1389 "Message out with FORMAT with ARGS."
1390 (message "[eglot] %s" (apply #'format format args)))
1391
1392(defun eglot--warn (format &rest args)
1393 "Warning message with FORMAT and ARGS."
1394 (apply #'eglot--message (concat "(warning) " format) args)
1395 (let ((warning-minimum-level :error))
1396 (display-warning 'eglot (apply #'format format args) :warning)))
1397
1398(defun eglot-current-column () (- (point) (line-beginning-position)))
1399
1400(defvar eglot-current-column-function #'eglot-lsp-abiding-column
1401 "Function to calculate the current column.
1402
1403This is the inverse operation of
1404`eglot-move-to-column-function' (which see). It is a function of
1405no arguments returning a column number. For buffers managed by
1406fully LSP-compliant servers, this should be set to
1407`eglot-lsp-abiding-column' (the default), and
1408`eglot-current-column' for all others.")
1409
1410(defun eglot-lsp-abiding-column (&optional lbp)
1411 "Calculate current COLUMN as defined by the LSP spec.
1412LBP defaults to `line-beginning-position'."
1413 (/ (- (length (encode-coding-region (or lbp (line-beginning-position))
1414 ;; Fix github#860
1415 (min (point) (point-max)) 'utf-16 t))
1416 2)
1417 2))
1418
1419(defun eglot--pos-to-lsp-position (&optional pos)
1420 "Convert point POS to LSP position."
1421 (eglot--widening
1422 (list :line (1- (line-number-at-pos pos t)) ; F!@&#$CKING OFF-BY-ONE
1423 :character (progn (when pos (goto-char pos))
1424 (funcall eglot-current-column-function)))))
1425
1426(defvar eglot-move-to-column-function #'eglot-move-to-lsp-abiding-column
1427 "Function to move to a column reported by the LSP server.
1428
1429According to the standard, LSP column/character offsets are based
1430on a count of UTF-16 code units, not actual visual columns. So
1431when LSP says position 3 of a line containing just \"aXbc\",
1432where X is a multi-byte character, it actually means `b', not
1433`c'. However, many servers don't follow the spec this closely.
1434
1435For buffers managed by fully LSP-compliant servers, this should
1436be set to `eglot-move-to-lsp-abiding-column' (the default), and
1437`eglot-move-to-column' for all others.")
1438
1439(defun eglot-move-to-column (column)
1440 "Move to COLUMN without closely following the LSP spec."
1441 ;; We cannot use `move-to-column' here, because it moves to *visual*
1442 ;; columns, which can be different from LSP columns in case of
1443 ;; `whitespace-mode', `prettify-symbols-mode', etc. (github#296,
1444 ;; github#297)
1445 (goto-char (min (+ (line-beginning-position) column)
1446 (line-end-position))))
1447
1448(defun eglot-move-to-lsp-abiding-column (column)
1449 "Move to COLUMN abiding by the LSP spec."
1450 (save-restriction
1451 (cl-loop
1452 with lbp = (line-beginning-position)
1453 initially
1454 (narrow-to-region lbp (line-end-position))
1455 (move-to-column column)
1456 for diff = (- column
1457 (eglot-lsp-abiding-column lbp))
1458 until (zerop diff)
1459 do (condition-case eob-err
1460 (forward-char (/ (if (> diff 0) (1+ diff) (1- diff)) 2))
1461 (end-of-buffer (cl-return eob-err))))))
1462
1463(defun eglot--lsp-position-to-point (pos-plist &optional marker)
1464 "Convert LSP position POS-PLIST to Emacs point.
1465If optional MARKER, return a marker instead"
1466 (save-excursion
1467 (save-restriction
1468 (widen)
1469 (goto-char (point-min))
1470 (forward-line (min most-positive-fixnum
1471 (plist-get pos-plist :line)))
1472 (unless (eobp) ;; if line was excessive leave point at eob
1473 (let ((tab-width 1)
1474 (col (plist-get pos-plist :character)))
1475 (unless (wholenump col)
1476 (eglot--warn
1477 "Caution: LSP server sent invalid character position %s. Using 0 instead."
1478 col)
1479 (setq col 0))
1480 (funcall eglot-move-to-column-function col)))
1481 (if marker (copy-marker (point-marker)) (point)))))
1482
1483(defconst eglot--uri-path-allowed-chars
1484 (let ((vec (copy-sequence url-path-allowed-chars)))
1485 (aset vec ?: nil) ;; see github#639
1486 vec)
1487 "Like `url-path-allows-chars' but more restrictive.")
1488
1489(defun eglot--path-to-uri (path)
1490 "URIfy PATH."
1491 (let ((truepath (file-truename path)))
1492 (concat "file://"
1493 ;; Add a leading "/" for local MS Windows-style paths.
1494 (if (and (eq system-type 'windows-nt)
1495 (not (file-remote-p truepath)))
1496 "/")
1497 (url-hexify-string
1498 ;; Again watch out for trampy paths.
1499 (directory-file-name (file-local-name truepath))
1500 eglot--uri-path-allowed-chars))))
1501
1502(defun eglot--uri-to-path (uri)
1503 "Convert URI to file path, helped by `eglot--current-server'."
1504 (when (keywordp uri) (setq uri (substring (symbol-name uri) 1)))
1505 (let* ((server (eglot-current-server))
1506 (remote-prefix (and server (eglot--trampish-p server)))
1507 (retval (url-unhex-string (url-filename (url-generic-parse-url uri))))
1508 ;; Remove the leading "/" for local MS Windows-style paths.
1509 (normalized (if (and (not remote-prefix)
1510 (eq system-type 'windows-nt)
1511 (cl-plusp (length retval)))
1512 (substring retval 1)
1513 retval)))
1514 (concat remote-prefix normalized)))
1515
1516(defun eglot--snippet-expansion-fn ()
1517 "Compute a function to expand snippets.
1518Doubles as an indicator of snippet support."
1519 (and (boundp 'yas-minor-mode)
1520 (symbol-value 'yas-minor-mode)
1521 'yas-expand-snippet))
1522
1523(defun eglot--format-markup (markup)
1524 "Format MARKUP according to LSP's spec."
1525 (pcase-let ((`(,string ,mode)
1526 (if (stringp markup) (list markup 'gfm-view-mode)
1527 (list (plist-get markup :value)
1528 (pcase (plist-get markup :kind)
1529 ("markdown" 'gfm-view-mode)
1530 ("plaintext" 'text-mode)
1531 (_ major-mode))))))
1532 (with-temp-buffer
1533 (setq-local markdown-fontify-code-blocks-natively t)
1534 (insert string)
1535 (let ((inhibit-message t)
1536 (message-log-max nil))
1537 (ignore-errors (delay-mode-hooks (funcall mode))))
1538 (font-lock-ensure)
1539 (string-trim (buffer-string)))))
1540
1541(define-obsolete-variable-alias 'eglot-ignored-server-capabilites
1542 'eglot-ignored-server-capabilities "1.8")
1543
1544(defcustom eglot-ignored-server-capabilities (list)
1545 "LSP server capabilities that Eglot could use, but won't.
1546You could add, for instance, the symbol
1547`:documentHighlightProvider' to prevent automatic highlighting
1548under cursor."
1549 :type '(set
1550 :tag "Tick the ones you're not interested in"
1551 (const :tag "Documentation on hover" :hoverProvider)
1552 (const :tag "Code completion" :completionProvider)
1553 (const :tag "Function signature help" :signatureHelpProvider)
1554 (const :tag "Go to definition" :definitionProvider)
1555 (const :tag "Go to type definition" :typeDefinitionProvider)
1556 (const :tag "Go to implementation" :implementationProvider)
1557 (const :tag "Go to declaration" :implementationProvider)
1558 (const :tag "Find references" :referencesProvider)
1559 (const :tag "Highlight symbols automatically" :documentHighlightProvider)
1560 (const :tag "List symbols in buffer" :documentSymbolProvider)
1561 (const :tag "List symbols in workspace" :workspaceSymbolProvider)
1562 (const :tag "Execute code actions" :codeActionProvider)
1563 (const :tag "Code lens" :codeLensProvider)
1564 (const :tag "Format buffer" :documentFormattingProvider)
1565 (const :tag "Format portion of buffer" :documentRangeFormattingProvider)
1566 (const :tag "On-type formatting" :documentOnTypeFormattingProvider)
1567 (const :tag "Rename symbol" :renameProvider)
1568 (const :tag "Highlight links in document" :documentLinkProvider)
1569 (const :tag "Decorate color references" :colorProvider)
1570 (const :tag "Fold regions of buffer" :foldingRangeProvider)
1571 (const :tag "Execute custom commands" :executeCommandProvider)))
1572
1573(defun eglot--server-capable (&rest feats)
1574 "Determine if current server is capable of FEATS."
1575 (unless (cl-some (lambda (feat)
1576 (memq feat eglot-ignored-server-capabilities))
1577 feats)
1578 (cl-loop for caps = (eglot--capabilities (eglot--current-server-or-lose))
1579 then (cadr probe)
1580 for (feat . more) on feats
1581 for probe = (plist-member caps feat)
1582 if (not probe) do (cl-return nil)
1583 if (eq (cadr probe) :json-false) do (cl-return nil)
1584 if (not (listp (cadr probe))) do (cl-return (if more nil (cadr probe)))
1585 finally (cl-return (or (cadr probe) t)))))
1586
1587(defun eglot--range-region (range &optional markers)
1588 "Return region (BEG . END) that represents LSP RANGE.
1589If optional MARKERS, make markers."
1590 (let* ((st (plist-get range :start))
1591 (beg (eglot--lsp-position-to-point st markers))
1592 (end (eglot--lsp-position-to-point (plist-get range :end) markers)))
1593 (cons beg end)))
1594
1595(defun eglot--read-server (prompt &optional dont-if-just-the-one)
1596 "Read a running Eglot server from minibuffer using PROMPT.
1597If DONT-IF-JUST-THE-ONE and there's only one server, don't prompt
1598and just return it. PROMPT shouldn't end with a question mark."
1599 (let ((servers (cl-loop for servers
1600 being hash-values of eglot--servers-by-project
1601 append servers))
1602 (name (lambda (srv)
1603 (format "%s %s" (eglot-project-nickname srv)
1604 (eglot--major-modes srv)))))
1605 (cond ((null servers)
1606 (eglot--error "No servers!"))
1607 ((or (cdr servers) (not dont-if-just-the-one))
1608 (let* ((default (when-let ((current (eglot-current-server)))
1609 (funcall name current)))
1610 (read (completing-read
1611 (if default
1612 (format "%s (default %s)? " prompt default)
1613 (concat prompt "? "))
1614 (mapcar name servers)
1615 nil t
1616 nil nil
1617 default)))
1618 (cl-find read servers :key name :test #'equal)))
1619 (t (car servers)))))
1620
1621(defun eglot--trampish-p (server)
1622 "Tell if SERVER's project root is `file-remote-p'."
1623 (file-remote-p (project-root (eglot--project server))))
1624
1625(defun eglot--plist-keys (plist) "Get keys of a plist."
1626 (cl-loop for (k _v) on plist by #'cddr collect k))
1627
1628(defun eglot--ensure-list (x) (if (listp x) x (list x)))
1629
1630
1631;;; Minor modes
1632;;;
1633(defvar eglot-mode-map
1634 (let ((map (make-sparse-keymap)))
1635 (define-key map [remap display-local-help] #'eldoc-doc-buffer)
1636 map))
1637
1638(defvar-local eglot--current-flymake-report-fn nil
1639 "Current flymake report function for this buffer.")
1640
1641(defvar-local eglot--saved-bindings nil
1642 "Bindings saved by `eglot--setq-saving'.")
1643
1644(defvar eglot-stay-out-of '()
1645 "List of Emacs things that Eglot should try to stay of.
1646Each element is a string, a symbol, or a regexp which is matched
1647against a variable's name. Examples include the string
1648\"company\" or the symbol `xref'.
1649
1650Before Eglot starts \"managing\" a particular buffer, it
1651opinionatedly sets some peripheral Emacs facilities, such as
1652Flymake, Xref and Company. These overriding settings help ensure
1653consistent Eglot behaviour and only stay in place until
1654\"managing\" stops (usually via `eglot-shutdown'), whereupon the
1655previous settings are restored.
1656
1657However, if you wish for Eglot to stay out of a particular Emacs
1658facility that you'd like to keep control of add an element to
1659this list and Eglot will refrain from setting it.
1660
1661For example, to keep your Company customization, add the symbol
1662`company' to this variable.")
1663
1664(defun eglot--stay-out-of-p (symbol)
1665 "Tell if Eglot should stay of of SYMBOL."
1666 (cl-find (symbol-name symbol) eglot-stay-out-of
1667 :test (lambda (s thing)
1668 (let ((re (if (symbolp thing) (symbol-name thing) thing)))
1669 (string-match re s)))))
1670
1671(defmacro eglot--setq-saving (symbol binding)
1672 `(unless (or (not (boundp ',symbol)) (eglot--stay-out-of-p ',symbol))
1673 (push (cons ',symbol (symbol-value ',symbol)) eglot--saved-bindings)
1674 (setq-local ,symbol ,binding)))
1675
1676(defun eglot-managed-p ()
1677 "Tell if current buffer is managed by Eglot."
1678 eglot--managed-mode)
1679
1680(defvar eglot-managed-mode-hook nil
1681 "A hook run by Eglot after it started/stopped managing a buffer.
1682Use `eglot-managed-p' to determine if current buffer is managed.")
1683
1684(define-minor-mode eglot--managed-mode
1685 "Mode for source buffers managed by some Eglot project."
1686 :init-value nil :lighter nil :keymap eglot-mode-map
1687 (cond
1688 (eglot--managed-mode
1689 (add-hook 'after-change-functions 'eglot--after-change nil t)
1690 (add-hook 'before-change-functions 'eglot--before-change nil t)
1691 (add-hook 'kill-buffer-hook #'eglot--managed-mode-off nil t)
1692 ;; Prepend "didClose" to the hook after the "nonoff", so it will run first
1693 (add-hook 'kill-buffer-hook 'eglot--signal-textDocument/didClose nil t)
1694 (add-hook 'before-revert-hook 'eglot--signal-textDocument/didClose nil t)
1695 (add-hook 'after-revert-hook 'eglot--after-revert-hook nil t)
1696 (add-hook 'before-save-hook 'eglot--signal-textDocument/willSave nil t)
1697 (add-hook 'after-save-hook 'eglot--signal-textDocument/didSave nil t)
1698 (unless (eglot--stay-out-of-p 'xref)
1699 (add-hook 'xref-backend-functions 'eglot-xref-backend nil t))
1700 (add-hook 'completion-at-point-functions #'eglot-completion-at-point nil t)
1701 (add-hook 'change-major-mode-hook #'eglot--managed-mode-off nil t)
1702 (add-hook 'post-self-insert-hook 'eglot--post-self-insert-hook nil t)
1703 (add-hook 'pre-command-hook 'eglot--pre-command-hook nil t)
1704 (eglot--setq-saving eldoc-documentation-functions
1705 '(eglot-signature-eldoc-function
1706 eglot-hover-eldoc-function))
1707 (eglot--setq-saving eldoc-documentation-strategy
1708 #'eldoc-documentation-enthusiast)
1709 (eglot--setq-saving xref-prompt-for-identifier nil)
1710 (eglot--setq-saving flymake-diagnostic-functions '(eglot-flymake-backend))
1711 (eglot--setq-saving company-backends '(company-capf))
1712 (eglot--setq-saving company-tooltip-align-annotations t)
1713 (unless (eglot--stay-out-of-p 'imenu)
1714 (add-function :before-until (local 'imenu-create-index-function)
1715 #'eglot-imenu))
1716 (unless (eglot--stay-out-of-p 'flymake) (flymake-mode 1))
1717 (unless (eglot--stay-out-of-p 'eldoc) (eldoc-mode 1))
1718 (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server))))
1719 (t
1720 (remove-hook 'after-change-functions 'eglot--after-change t)
1721 (remove-hook 'before-change-functions 'eglot--before-change t)
1722 (remove-hook 'kill-buffer-hook #'eglot--managed-mode-off t)
1723 (remove-hook 'kill-buffer-hook 'eglot--signal-textDocument/didClose t)
1724 (remove-hook 'before-revert-hook 'eglot--signal-textDocument/didClose t)
1725 (remove-hook 'after-revert-hook 'eglot--after-revert-hook t)
1726 (remove-hook 'before-save-hook 'eglot--signal-textDocument/willSave t)
1727 (remove-hook 'after-save-hook 'eglot--signal-textDocument/didSave t)
1728 (remove-hook 'xref-backend-functions 'eglot-xref-backend t)
1729 (remove-hook 'completion-at-point-functions #'eglot-completion-at-point t)
1730 (remove-hook 'change-major-mode-hook #'eglot--managed-mode-off t)
1731 (remove-hook 'post-self-insert-hook 'eglot--post-self-insert-hook t)
1732 (remove-hook 'pre-command-hook 'eglot--pre-command-hook t)
1733 (cl-loop for (var . saved-binding) in eglot--saved-bindings
1734 do (set (make-local-variable var) saved-binding))
1735 (remove-function (local 'imenu-create-index-function) #'eglot-imenu)
1736 (when eglot--current-flymake-report-fn
1737 (eglot--report-to-flymake nil)
1738 (setq eglot--current-flymake-report-fn nil))
1739 (let ((server eglot--cached-server))
1740 (setq eglot--cached-server nil)
1741 (when server
1742 (setf (eglot--managed-buffers server)
1743 (delq (current-buffer) (eglot--managed-buffers server)))
1744 (when (and eglot-autoshutdown
1745 (null (eglot--managed-buffers server)))
1746 (eglot-shutdown server))))))
1747 ;; Note: the public hook runs before the internal eglot--managed-mode-hook.
1748 (run-hooks 'eglot-managed-mode-hook))
1749
1750(defun eglot--managed-mode-off ()
1751 "Turn off `eglot--managed-mode' unconditionally."
1752 (eglot--managed-mode -1))
1753
1754(defun eglot-current-server ()
1755 "Return logical Eglot server for current buffer, nil if none."
1756 (setq eglot--cached-server
1757 (or eglot--cached-server
1758 (cl-find major-mode
1759 (gethash (eglot--current-project) eglot--servers-by-project)
1760 :key #'eglot--major-modes
1761 :test #'memq)
1762 (and eglot-extend-to-xref
1763 buffer-file-name
1764 (gethash (expand-file-name buffer-file-name)
1765 eglot--servers-by-xrefed-file)))))
1766
1767(defun eglot--current-server-or-lose ()
1768 "Return current logical Eglot server connection or error."
1769 (or (eglot-current-server)
1770 (jsonrpc-error "No current JSON-RPC connection")))
1771
1772(defvar-local eglot--diagnostics nil
1773 "Flymake diagnostics for this buffer.")
1774
1775(defvar revert-buffer-preserve-modes)
1776(defun eglot--after-revert-hook ()
1777 "Eglot's `after-revert-hook'."
1778 (when revert-buffer-preserve-modes (eglot--signal-textDocument/didOpen)))
1779
1780(defun eglot--maybe-activate-editing-mode ()
1781 "Maybe activate `eglot--managed-mode'.
1782
1783If it is activated, also signal textDocument/didOpen."
1784 (unless eglot--managed-mode
1785 ;; Called when `revert-buffer-in-progress-p' is t but
1786 ;; `revert-buffer-preserve-modes' is nil.
1787 (when (and buffer-file-name (eglot-current-server))
1788 (setq eglot--diagnostics nil)
1789 (eglot--managed-mode)
1790 (eglot--signal-textDocument/didOpen))))
1791
1792(add-hook 'find-file-hook 'eglot--maybe-activate-editing-mode)
1793(add-hook 'after-change-major-mode-hook 'eglot--maybe-activate-editing-mode)
1794
1795(defun eglot-clear-status (server)
1796 "Clear the last JSONRPC error for SERVER."
1797 (interactive (list (eglot--current-server-or-lose)))
1798 (setf (jsonrpc-last-error server) nil))
1799
1800
1801;;; Mode-line, menu and other sugar
1802;;;
1803(defvar eglot--mode-line-format `(:eval (eglot--mode-line-format)))
1804
1805(put 'eglot--mode-line-format 'risky-local-variable t)
1806
1807(defun eglot--mouse-call (what)
1808 "Make an interactive lambda for calling WHAT from mode-line."
1809 (lambda (event)
1810 (interactive "e")
1811 (let ((start (event-start event))) (with-selected-window (posn-window start)
1812 (save-excursion
1813 (goto-char (or (posn-point start)
1814 (point)))
1815 (call-interactively what)
1816 (force-mode-line-update t))))))
1817
1818(defun eglot-manual () "Open on-line documentation."
1819 (interactive) (browse-url "https://github.com/joaotavora/eglot#readme"))
1820
1821(easy-menu-define eglot-menu nil "Eglot"
1822 `("Eglot"
1823 ;; Commands for getting information and customization.
1824 ["Read manual" eglot-manual]
1825 ["Customize Eglot" (lambda () (interactive) (customize-group "eglot"))]
1826 "--"
1827 ;; xref like commands.
1828 ["Find definitions" xref-find-definitions
1829 :help "Find definitions of identifier at point"
1830 :active (eglot--server-capable :definitionProvider)]
1831 ["Find references" xref-find-references
1832 :help "Find references to identifier at point"
1833 :active (eglot--server-capable :referencesProvider)]
1834 ["Find symbols in workspace (apropos)" xref-find-apropos
1835 :help "Find symbols matching a query"
1836 :active (eglot--server-capable :workspaceSymbolProvider)]
1837 ["Find declaration" eglot-find-declaration
1838 :help "Find declaration for identifier at point"
1839 :active (eglot--server-capable :declarationProvider)]
1840 ["Find implementation" eglot-find-implementation
1841 :help "Find implementation for identifier at point"
1842 :active (eglot--server-capable :implementationProvider)]
1843 ["Find type definition" eglot-find-typeDefinition
1844 :help "Find type definition for identifier at point"
1845 :active (eglot--server-capable :typeDefinitionProvider)]
1846 "--"
1847 ;; LSP-related commands (mostly Eglot's own commands).
1848 ["Rename symbol" eglot-rename
1849 :active (eglot--server-capable :renameProvider)]
1850 ["Format buffer" eglot-format-buffer
1851 :active (eglot--server-capable :documentFormattingProvider)]
1852 ["Format active region" eglot-format
1853 :active (and (region-active-p)
1854 (eglot--server-capable :documentRangeFormattingProvider))]
1855 ["Show Flymake diagnostics for buffer" flymake-show-buffer-diagnostics]
1856 ["Show Flymake diagnostics for project" flymake-show-project-diagnostics]
1857 ["Show Eldoc documentation at point" eldoc-doc-buffer]
1858 "--"
1859 ["All possible code actions" eglot-code-actions
1860 :active (eglot--server-capable :codeActionProvider)]
1861 ["Organize imports" eglot-code-action-organize-imports
1862 :visible (eglot--server-capable :codeActionProvider)]
1863 ["Extract" eglot-code-action-extract
1864 :visible (eglot--server-capable :codeActionProvider)]
1865 ["Inline" eglot-code-action-inline
1866 :visible (eglot--server-capable :codeActionProvider)]
1867 ["Rewrite" eglot-code-action-rewrite
1868 :visible (eglot--server-capable :codeActionProvider)]
1869 ["Quickfix" eglot-code-action-quickfix
1870 :visible (eglot--server-capable :codeActionProvider)]))
1871
1872(easy-menu-define eglot-server-menu nil "Monitor server communication"
1873 '("Debugging the server communication"
1874 ["Reconnect to server" eglot-reconnect]
1875 ["Quit server" eglot-shutdown]
1876 "--"
1877 ["LSP events buffer" eglot-events-buffer]
1878 ["Server stderr buffer" eglot-stderr-buffer]
1879 ["Customize event buffer size"
1880 (lambda ()
1881 (interactive)
1882 (customize-variable 'eglot-events-buffer-size))]))
1883
1884(defun eglot--mode-line-props (thing face defs &optional prepend)
1885 "Helper for function `eglot--mode-line-format'.
1886Uses THING, FACE, DEFS and PREPEND."
1887 (cl-loop with map = (make-sparse-keymap)
1888 for (elem . rest) on defs
1889 for (key def help) = elem
1890 do (define-key map `[mode-line ,key] (eglot--mouse-call def))
1891 concat (format "%s: %s" key help) into blurb
1892 when rest concat "\n" into blurb
1893 finally (return `(:propertize ,thing
1894 face ,face
1895 keymap ,map help-echo ,(concat prepend blurb)
1896 mouse-face mode-line-highlight))))
1897
1898(defun eglot--mode-line-format ()
1899 "Compose the Eglot's mode-line."
1900 (pcase-let* ((server (eglot-current-server))
1901 (nick (and server (eglot-project-nickname server)))
1902 (pending (and server (hash-table-count
1903 (jsonrpc--request-continuations server))))
1904 (`(,_id ,doing ,done-p ,_detail) (and server (eglot--spinner server)))
1905 (last-error (and server (jsonrpc-last-error server))))
1906 (append
1907 `(,(propertize
1908 eglot-menu-string
1909 'face 'eglot-mode-line
1910 'mouse-face 'mode-line-highlight
1911 'help-echo "Eglot: Emacs LSP client\nmouse-1: Display minor mode menu"
1912 'keymap (let ((map (make-sparse-keymap)))
1913 (define-key map [mode-line down-mouse-1] eglot-menu)
1914 map)))
1915 (when nick
1916 `(":"
1917 ,(propertize
1918 nick
1919 'face 'eglot-mode-line
1920 'mouse-face 'mode-line-highlight
1921 'help-echo (format "Project '%s'\nmouse-1: LSP server control menu" nick)
1922 'keymap (let ((map (make-sparse-keymap)))
1923 (define-key map [mode-line down-mouse-1] eglot-server-menu)
1924 map))
1925 ,@(when last-error
1926 `("/" ,(eglot--mode-line-props
1927 "error" 'compilation-mode-line-fail
1928 '((mouse-3 eglot-clear-status "Clear this status"))
1929 (format "An error occurred: %s\n" (plist-get last-error
1930 :message)))))
1931 ,@(when (and doing (not done-p))
1932 `("/" ,(eglot--mode-line-props doing
1933 'compilation-mode-line-run '())))
1934 ,@(when (cl-plusp pending)
1935 `("/" ,(eglot--mode-line-props
1936 (format "%d" pending) 'warning
1937 '((mouse-3 eglot-forget-pending-continuations
1938 "Forget pending continuations"))
1939 "Number of outgoing, \
1940still unanswered LSP requests to the server\n"))))))))
1941
1942(add-to-list 'mode-line-misc-info
1943 `(eglot--managed-mode (" [" eglot--mode-line-format "] ")))
1944
1945
1946;;; Flymake customization
1947;;;
1948(put 'eglot-note 'flymake-category 'flymake-note)
1949(put 'eglot-warning 'flymake-category 'flymake-warning)
1950(put 'eglot-error 'flymake-category 'flymake-error)
1951
1952(defalias 'eglot--make-diag 'flymake-make-diagnostic)
1953(defalias 'eglot--diag-data 'flymake-diagnostic-data)
1954
1955(cl-loop for i from 1
1956 for type in '(eglot-note eglot-warning eglot-error )
1957 do (put type 'flymake-overlay-control
1958 `((mouse-face . highlight)
1959 (priority . ,(+ 50 i))
1960 (keymap . ,(let ((map (make-sparse-keymap)))
1961 (define-key map [mouse-1]
1962 (eglot--mouse-call 'eglot-code-actions))
1963 map)))))
1964
1965
1966;;; Protocol implementation (Requests, notifications, etc)
1967;;;
1968(cl-defmethod eglot-handle-notification
1969 (_server method &key &allow-other-keys)
1970 "Handle unknown notification."
1971 (unless (or (string-prefix-p "$" (format "%s" method))
1972 (not (memq 'disallow-unknown-methods eglot-strict-mode)))
1973 (eglot--warn "Server sent unknown notification method `%s'" method)))
1974
1975(cl-defmethod eglot-handle-request
1976 (_server method &key &allow-other-keys)
1977 "Handle unknown request."
1978 (when (memq 'disallow-unknown-methods eglot-strict-mode)
1979 (jsonrpc-error "Unknown request method `%s'" method)))
1980
1981(cl-defmethod eglot-execute-command
1982 (server command arguments)
1983 "Execute COMMAND on SERVER with `:workspace/executeCommand'.
1984COMMAND is a symbol naming the command."
1985 (jsonrpc-request server :workspace/executeCommand
1986 `(:command ,(format "%s" command) :arguments ,arguments)))
1987
1988(cl-defmethod eglot-handle-notification
1989 (_server (_method (eql window/showMessage)) &key type message)
1990 "Handle notification window/showMessage."
1991 (eglot--message (propertize "Server reports (type=%s): %s"
1992 'face (if (<= type 1) 'error))
1993 type message))
1994
1995(cl-defmethod eglot-handle-request
1996 (_server (_method (eql window/showMessageRequest)) &key type message actions)
1997 "Handle server request window/showMessageRequest."
1998 (let* ((actions (append actions nil)) ;; gh#627
1999 (label (completing-read
2000 (concat
2001 (format (propertize "[eglot] Server reports (type=%s): %s"
2002 'face (if (<= type 1) 'error))
2003 type message)
2004 "\nChoose an option: ")
2005 (or (mapcar (lambda (obj) (plist-get obj :title)) actions)
2006 '("OK"))
2007 nil t (plist-get (elt actions 0) :title))))
2008 (if label `(:title ,label) :null)))
2009
2010(cl-defmethod eglot-handle-notification
2011 (_server (_method (eql window/logMessage)) &key _type _message)
2012 "Handle notification window/logMessage.") ;; noop, use events buffer
2013
2014(cl-defmethod eglot-handle-notification
2015 (_server (_method (eql telemetry/event)) &rest _any)
2016 "Handle notification telemetry/event.") ;; noop, use events buffer
2017
2018(cl-defmethod eglot-handle-notification
2019 (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics
2020 &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode'
2021 "Handle notification publishDiagnostics."
2022 (cl-flet ((eglot--diag-type (sev)
2023 (cond ((null sev) 'eglot-error)
2024 ((<= sev 1) 'eglot-error)
2025 ((= sev 2) 'eglot-warning)
2026 (t 'eglot-note)))
2027 (mess (source code message)
2028 (concat source (and code (format " [%s]" code)) ": " message)))
2029 (if-let ((buffer (find-buffer-visiting (eglot--uri-to-path uri))))
2030 (with-current-buffer buffer
2031 (cl-loop
2032 for diag-spec across diagnostics
2033 collect (eglot--dbind ((Diagnostic) range code message severity source tags)
2034 diag-spec
2035 (setq message (mess source code message))
2036 (pcase-let
2037 ((`(,beg . ,end) (eglot--range-region range)))
2038 ;; Fallback to `flymake-diag-region' if server
2039 ;; botched the range
2040 (when (= beg end)
2041 (if-let* ((st (plist-get range :start))
2042 (diag-region
2043 (flymake-diag-region
2044 (current-buffer) (1+ (plist-get st :line))
2045 (plist-get st :character))))
2046 (setq beg (car diag-region) end (cdr diag-region))
2047 (eglot--widening
2048 (goto-char (point-min))
2049 (setq beg
2050 (line-beginning-position
2051 (1+ (plist-get (plist-get range :start) :line))))
2052 (setq end
2053 (line-end-position
2054 (1+ (plist-get (plist-get range :end) :line)))))))
2055 (eglot--make-diag
2056 (current-buffer) beg end
2057 (eglot--diag-type severity)
2058 message `((eglot-lsp-diag . ,diag-spec))
2059 (when-let ((faces
2060 (cl-loop for tag across tags
2061 when (alist-get tag eglot--tag-faces)
2062 collect it)))
2063 `((face . ,faces))))))
2064 into diags
2065 finally (cond ((and
2066 ;; only add to current report if Flymake
2067 ;; starts on idle-timer (github#958)
2068 (not (null flymake-no-changes-timeout))
2069 eglot--current-flymake-report-fn)
2070 (eglot--report-to-flymake diags))
2071 (t
2072 (setq eglot--diagnostics diags)))))
2073 (cl-loop
2074 with path = (expand-file-name (eglot--uri-to-path uri))
2075 for diag-spec across diagnostics
2076 collect (eglot--dbind ((Diagnostic) code range message severity source) diag-spec
2077 (setq message (mess source code message))
2078 (let* ((start (plist-get range :start))
2079 (line (1+ (plist-get start :line)))
2080 (char (1+ (plist-get start :character))))
2081 (eglot--make-diag
2082 path (cons line char) nil (eglot--diag-type severity) message)))
2083 into diags
2084 finally
2085 (setq flymake-list-only-diagnostics
2086 (assoc-delete-all path flymake-list-only-diagnostics #'string=))
2087 (push (cons path diags) flymake-list-only-diagnostics)))))
2088
2089(cl-defun eglot--register-unregister (server things how)
2090 "Helper for `registerCapability'.
2091THINGS are either registrations or unregisterations (sic)."
2092 (cl-loop
2093 for thing in (cl-coerce things 'list)
2094 do (eglot--dbind ((Registration) id method registerOptions) thing
2095 (apply (cl-ecase how
2096 (register 'eglot-register-capability)
2097 (unregister 'eglot-unregister-capability))
2098 server (intern method) id registerOptions))))
2099
2100(cl-defmethod eglot-handle-request
2101 (server (_method (eql client/registerCapability)) &key registrations)
2102 "Handle server request client/registerCapability."
2103 (eglot--register-unregister server registrations 'register))
2104
2105(cl-defmethod eglot-handle-request
2106 (server (_method (eql client/unregisterCapability))
2107 &key unregisterations) ;; XXX: "unregisterations" (sic)
2108 "Handle server request client/unregisterCapability."
2109 (eglot--register-unregister server unregisterations 'unregister))
2110
2111(cl-defmethod eglot-handle-request
2112 (_server (_method (eql workspace/applyEdit)) &key _label edit)
2113 "Handle server request workspace/applyEdit."
2114 (eglot--apply-workspace-edit edit eglot-confirm-server-initiated-edits)
2115 `(:applied t))
2116
2117(cl-defmethod eglot-handle-request
2118 (server (_method (eql workspace/workspaceFolders)))
2119 "Handle server request workspace/workspaceFolders."
2120 (eglot-workspace-folders server))
2121
2122(defun eglot--TextDocumentIdentifier ()
2123 "Compute TextDocumentIdentifier object for current buffer."
2124 `(:uri ,(eglot--path-to-uri (or buffer-file-name
2125 (ignore-errors
2126 (buffer-file-name
2127 (buffer-base-buffer)))))))
2128
2129(defvar-local eglot--versioned-identifier 0)
2130
2131(defun eglot--VersionedTextDocumentIdentifier ()
2132 "Compute VersionedTextDocumentIdentifier object for current buffer."
2133 (append (eglot--TextDocumentIdentifier)
2134 `(:version ,eglot--versioned-identifier)))
2135
2136(defun eglot--TextDocumentItem ()
2137 "Compute TextDocumentItem object for current buffer."
2138 (append
2139 (eglot--VersionedTextDocumentIdentifier)
2140 (list :languageId
2141 (eglot--language-id (eglot--current-server-or-lose))
2142 :text
2143 (eglot--widening
2144 (buffer-substring-no-properties (point-min) (point-max))))))
2145
2146(defun eglot--TextDocumentPositionParams ()
2147 "Compute TextDocumentPositionParams."
2148 (list :textDocument (eglot--TextDocumentIdentifier)
2149 :position (eglot--pos-to-lsp-position)))
2150
2151(defvar-local eglot--last-inserted-char nil
2152 "If non-nil, value of the last inserted character in buffer.")
2153
2154(defun eglot--post-self-insert-hook ()
2155 "Set `eglot--last-inserted-char', maybe call on-type-formatting."
2156 (setq eglot--last-inserted-char last-input-event)
2157 (let ((ot-provider (eglot--server-capable :documentOnTypeFormattingProvider)))
2158 (when (and ot-provider
2159 (ignore-errors ; github#906, some LS's send empty strings
2160 (or (eq last-input-event
2161 (seq-first (plist-get ot-provider :firstTriggerCharacter)))
2162 (cl-find last-input-event
2163 (plist-get ot-provider :moreTriggerCharacter)
2164 :key #'seq-first))))
2165 (eglot-format (point) nil last-input-event))))
2166
2167(defvar eglot--workspace-symbols-cache (make-hash-table :test #'equal)
2168 "Cache of `workspace/Symbol' results used by `xref-find-definitions'.")
2169
2170(defun eglot--pre-command-hook ()
2171 "Reset some temporary variables."
2172 (clrhash eglot--workspace-symbols-cache)
2173 (setq eglot--last-inserted-char nil))
2174
2175(defun eglot--CompletionParams ()
2176 (append
2177 (eglot--TextDocumentPositionParams)
2178 `(:context
2179 ,(if-let (trigger (and (characterp eglot--last-inserted-char)
2180 (cl-find eglot--last-inserted-char
2181 (eglot--server-capable :completionProvider
2182 :triggerCharacters)
2183 :key (lambda (str) (aref str 0))
2184 :test #'char-equal)))
2185 `(:triggerKind 2 :triggerCharacter ,trigger) `(:triggerKind 1)))))
2186
2187(defvar-local eglot--recent-changes nil
2188 "Recent buffer changes as collected by `eglot--before-change'.")
2189
2190(cl-defmethod jsonrpc-connection-ready-p ((_server eglot-lsp-server) _what)
2191 "Tell if SERVER is ready for WHAT in current buffer."
2192 (and (cl-call-next-method) (not eglot--recent-changes)))
2193
2194(defvar-local eglot--change-idle-timer nil "Idle timer for didChange signals.")
2195
2196(defun eglot--before-change (beg end)
2197 "Hook onto `before-change-functions' with BEG and END."
2198 (when (listp eglot--recent-changes)
2199 ;; Records BEG and END, crucially convert them into LSP
2200 ;; (line/char) positions before that information is lost (because
2201 ;; the after-change thingy doesn't know if newlines were
2202 ;; deleted/added). Also record markers of BEG and END
2203 ;; (github#259)
2204 (push `(,(eglot--pos-to-lsp-position beg)
2205 ,(eglot--pos-to-lsp-position end)
2206 (,beg . ,(copy-marker beg nil))
2207 (,end . ,(copy-marker end t)))
2208 eglot--recent-changes)))
2209
2210(defun eglot--after-change (beg end pre-change-length)
2211 "Hook onto `after-change-functions'.
2212Records BEG, END and PRE-CHANGE-LENGTH locally."
2213 (cl-incf eglot--versioned-identifier)
2214 (pcase (and (listp eglot--recent-changes)
2215 (car eglot--recent-changes))
2216 (`(,lsp-beg ,lsp-end
2217 (,b-beg . ,b-beg-marker)
2218 (,b-end . ,b-end-marker))
2219 ;; github#259 and github#367: With `capitalize-word' or somesuch,
2220 ;; `before-change-functions' always records the whole word's
2221 ;; `b-beg' and `b-end'. Similarly, when coalescing two lines
2222 ;; into one, `fill-paragraph' they mark the end of the first line
2223 ;; up to the end of the second line. In both situations, args
2224 ;; received here contradict that information: `beg' and `end'
2225 ;; will differ by 1 and will likely only encompass the letter
2226 ;; that was capitalized or, in the sentence-joining situation,
2227 ;; the replacement of the newline with a space. That's we keep
2228 ;; markers _and_ positions so we're able to detect and correct
2229 ;; this. We ignore `beg', `len' and `pre-change-len' and send
2230 ;; "fuller" information about the region from the markers. I've
2231 ;; also experimented with doing this unconditionally but it seems
2232 ;; to break when newlines are added.
2233 (if (and (= b-end b-end-marker) (= b-beg b-beg-marker)
2234 (or (/= beg b-beg) (/= end b-end)))
2235 (setcar eglot--recent-changes
2236 `(,lsp-beg ,lsp-end ,(- b-end-marker b-beg-marker)
2237 ,(buffer-substring-no-properties b-beg-marker
2238 b-end-marker)))
2239 (setcar eglot--recent-changes
2240 `(,lsp-beg ,lsp-end ,pre-change-length
2241 ,(buffer-substring-no-properties beg end)))))
2242 (_ (setf eglot--recent-changes :emacs-messup)))
2243 (when eglot--change-idle-timer (cancel-timer eglot--change-idle-timer))
2244 (let ((buf (current-buffer)))
2245 (setq eglot--change-idle-timer
2246 (run-with-idle-timer
2247 eglot-send-changes-idle-time
2248 nil (lambda () (eglot--when-live-buffer buf
2249 (when eglot--managed-mode
2250 (eglot--signal-textDocument/didChange)
2251 (setq eglot--change-idle-timer nil))))))))
2252
2253;; HACK! Launching a deferred sync request with outstanding changes is a
2254;; bad idea, since that might lead to the request never having a
2255;; chance to run, because `jsonrpc-connection-ready-p'.
2256(advice-add #'jsonrpc-request :before
2257 (cl-function (lambda (_proc _method _params &key
2258 deferred &allow-other-keys)
2259 (when (and eglot--managed-mode deferred)
2260 (eglot--signal-textDocument/didChange))))
2261 '((name . eglot--signal-textDocument/didChange)))
2262
2263(defvar-local eglot-workspace-configuration ()
2264 "Configure LSP servers specifically for a given project.
2265
2266This variable's value should be a plist (SECTION VALUE ...).
2267SECTION is a keyword naming a parameter section relevant to a
2268particular server. VALUE is a plist or a primitive type
2269converted to JSON also understood by that server.
2270
2271Instead of a plist, an alist ((SECTION . VALUE) ...) can be used
2272instead, but this variant is less reliable and not recommended.
2273
2274This variable should be set as a directory-local variable. See
2275See info node `(emacs)Directory Variables' for various ways to to
2276that.
2277
2278Here's an example value that establishes two sections relevant to
2279the Pylsp and Gopls LSP servers:
2280
2281 (:pylsp (:plugins (:jedi_completion (:include_params t
2282 :fuzzy t)
2283 :pylint (:enabled :json-false)))
2284 :gopls (:usePlaceholders t))
2285
2286The value of this variable can also be a unary function of a
2287single argument, which will be a connected `eglot-lsp-server'
2288instance. The function runs with `default-directory' set to the
2289root of the current project. It should return an object of the
2290format described above.")
2291
2292;;;###autoload
2293(put 'eglot-workspace-configuration 'safe-local-variable 'listp)
2294
2295(defun eglot-show-workspace-configuration (&optional server)
2296 "Dump `eglot-workspace-configuration' as JSON for debugging."
2297 (interactive (list (and (eglot-current-server)
2298 (eglot--read-server "Server configuration"
2299 (eglot-current-server)))))
2300 (let ((conf (eglot--workspace-configuration-plist server)))
2301 (with-current-buffer (get-buffer-create "*EGLOT workspace configuration*")
2302 (erase-buffer)
2303 (insert (jsonrpc--json-encode conf))
2304 (with-no-warnings
2305 (require 'json)
2306 (when (require 'json-mode nil t) (json-mode))
2307 (json-pretty-print-buffer))
2308 (pop-to-buffer (current-buffer)))))
2309
2310(defun eglot--workspace-configuration (server)
2311 (if (functionp eglot-workspace-configuration)
2312 (funcall eglot-workspace-configuration server)
2313 eglot-workspace-configuration))
2314
2315(defun eglot--workspace-configuration-plist (server)
2316 "Returns `eglot-workspace-configuration' suitable for serialization."
2317 (let ((val (eglot--workspace-configuration server)))
2318 (or (and (consp (car val))
2319 (cl-loop for (section . v) in val
2320 collect (if (keywordp section) section
2321 (intern (format ":%s" section)))
2322 collect v))
2323 val)))
2324
2325(defun eglot-signal-didChangeConfiguration (server)
2326 "Send a `:workspace/didChangeConfiguration' signal to SERVER.
2327When called interactively, use the currently active server"
2328 (interactive (list (eglot--current-server-or-lose)))
2329 (jsonrpc-notify
2330 server :workspace/didChangeConfiguration
2331 (list
2332 :settings
2333 (or (eglot--workspace-configuration-plist server)
2334 eglot--{}))))
2335
2336(cl-defmethod eglot-handle-request
2337 (server (_method (eql workspace/configuration)) &key items)
2338 "Handle server request workspace/configuration."
2339 (apply #'vector
2340 (mapcar
2341 (eglot--lambda ((ConfigurationItem) scopeUri section)
2342 (with-temp-buffer
2343 (let* ((uri-path (eglot--uri-to-path scopeUri))
2344 (default-directory
2345 (if (and (not (string-empty-p uri-path))
2346 (file-directory-p uri-path))
2347 (file-name-as-directory uri-path)
2348 (project-root (eglot--project server)))))
2349 (setq-local major-mode (car (eglot--major-modes server)))
2350 (hack-dir-local-variables-non-file-buffer)
2351 (cl-loop for (wsection o)
2352 on (eglot--workspace-configuration-plist server)
2353 by #'cddr
2354 when (string=
2355 (if (keywordp wsection)
2356 (substring (symbol-name wsection) 1)
2357 wsection)
2358 section)
2359 return o))))
2360 items)))
2361
2362(defun eglot--signal-textDocument/didChange ()
2363 "Send textDocument/didChange to server."
2364 (when eglot--recent-changes
2365 (let* ((server (eglot--current-server-or-lose))
2366 (sync-capability (eglot--server-capable :textDocumentSync))
2367 (sync-kind (if (numberp sync-capability) sync-capability
2368 (plist-get sync-capability :change)))
2369 (full-sync-p (or (eq sync-kind 1)
2370 (eq :emacs-messup eglot--recent-changes))))
2371 (jsonrpc-notify
2372 server :textDocument/didChange
2373 (list
2374 :textDocument (eglot--VersionedTextDocumentIdentifier)
2375 :contentChanges
2376 (if full-sync-p
2377 (vector `(:text ,(eglot--widening
2378 (buffer-substring-no-properties (point-min)
2379 (point-max)))))
2380 (cl-loop for (beg end len text) in (reverse eglot--recent-changes)
2381 ;; github#259: `capitalize-word' and commands based
2382 ;; on `casify_region' will cause multiple duplicate
2383 ;; empty entries in `eglot--before-change' calls
2384 ;; without an `eglot--after-change' reciprocal.
2385 ;; Weed them out here.
2386 when (numberp len)
2387 vconcat `[,(list :range `(:start ,beg :end ,end)
2388 :rangeLength len :text text)]))))
2389 (setq eglot--recent-changes nil)
2390 (setf (eglot--spinner server) (list nil :textDocument/didChange t))
2391 (jsonrpc--call-deferred server))))
2392
2393(defun eglot--signal-textDocument/didOpen ()
2394 "Send textDocument/didOpen to server."
2395 (setq eglot--recent-changes nil eglot--versioned-identifier 0)
2396 (jsonrpc-notify
2397 (eglot--current-server-or-lose)
2398 :textDocument/didOpen `(:textDocument ,(eglot--TextDocumentItem))))
2399
2400(defun eglot--signal-textDocument/didClose ()
2401 "Send textDocument/didClose to server."
2402 (with-demoted-errors
2403 "[eglot] error sending textDocument/didClose: %s"
2404 (jsonrpc-notify
2405 (eglot--current-server-or-lose)
2406 :textDocument/didClose `(:textDocument ,(eglot--TextDocumentIdentifier)))))
2407
2408(defun eglot--signal-textDocument/willSave ()
2409 "Send textDocument/willSave to server."
2410 (let ((server (eglot--current-server-or-lose))
2411 (params `(:reason 1 :textDocument ,(eglot--TextDocumentIdentifier))))
2412 (when (eglot--server-capable :textDocumentSync :willSave)
2413 (jsonrpc-notify server :textDocument/willSave params))
2414 (when (eglot--server-capable :textDocumentSync :willSaveWaitUntil)
2415 (ignore-errors
2416 (eglot--apply-text-edits
2417 (jsonrpc-request server :textDocument/willSaveWaitUntil params
2418 :timeout 0.5))))))
2419
2420(defun eglot--signal-textDocument/didSave ()
2421 "Send textDocument/didSave to server."
2422 (eglot--signal-textDocument/didChange)
2423 (jsonrpc-notify
2424 (eglot--current-server-or-lose)
2425 :textDocument/didSave
2426 (list
2427 ;; TODO: Handle TextDocumentSaveRegistrationOptions to control this.
2428 :text (buffer-substring-no-properties (point-min) (point-max))
2429 :textDocument (eglot--TextDocumentIdentifier))))
2430
2431(defun eglot-flymake-backend (report-fn &rest _more)
2432 "A Flymake backend for Eglot.
2433Calls REPORT-FN (or arranges for it to be called) when the server
2434publishes diagnostics. Between calls to this function, REPORT-FN
2435may be called multiple times (respecting the protocol of
2436`flymake-backend-functions')."
2437 (cond (eglot--managed-mode
2438 (setq eglot--current-flymake-report-fn report-fn)
2439 (eglot--report-to-flymake eglot--diagnostics))
2440 (t
2441 (funcall report-fn nil))))
2442
2443(defun eglot--report-to-flymake (diags)
2444 "Internal helper for `eglot-flymake-backend'."
2445 (save-restriction
2446 (widen)
2447 (funcall eglot--current-flymake-report-fn diags
2448 ;; If the buffer hasn't changed since last
2449 ;; call to the report function, flymake won't
2450 ;; delete old diagnostics. Using :region
2451 ;; keyword forces flymake to delete
2452 ;; them (github#159).
2453 :region (cons (point-min) (point-max))))
2454 (setq eglot--diagnostics diags))
2455
2456(defun eglot-xref-backend () "Eglot xref backend." 'eglot)
2457
2458(defvar eglot--temp-location-buffers (make-hash-table :test #'equal)
2459 "Helper variable for `eglot--handling-xrefs'.")
2460
2461(defvar eglot-xref-lessp-function #'ignore
2462 "Compare two `xref-item' objects for sorting.")
2463
2464(cl-defmacro eglot--collecting-xrefs ((collector) &rest body)
2465 "Sort and handle xrefs collected with COLLECTOR in BODY."
2466 (declare (indent 1) (debug (sexp &rest form)))
2467 (let ((collected (cl-gensym "collected")))
2468 `(unwind-protect
2469 (let (,collected)
2470 (cl-flet ((,collector (xref) (push xref ,collected)))
2471 ,@body)
2472 (setq ,collected (nreverse ,collected))
2473 (sort ,collected eglot-xref-lessp-function))
2474 (maphash (lambda (_uri buf) (kill-buffer buf)) eglot--temp-location-buffers)
2475 (clrhash eglot--temp-location-buffers))))
2476
2477(defun eglot--xref-make-match (name uri range)
2478 "Like `xref-make-match' but with LSP's NAME, URI and RANGE.
2479Try to visit the target file for a richer summary line."
2480 (pcase-let*
2481 ((file (eglot--uri-to-path uri))
2482 (visiting (or (find-buffer-visiting file)
2483 (gethash uri eglot--temp-location-buffers)))
2484 (collect (lambda ()
2485 (eglot--widening
2486 (pcase-let* ((`(,beg . ,end) (eglot--range-region range))
2487 (bol (progn (goto-char beg) (line-beginning-position)))
2488 (substring (buffer-substring bol (line-end-position)))
2489 (hi-beg (- beg bol))
2490 (hi-end (- (min (line-end-position) end) bol)))
2491 (add-face-text-property hi-beg hi-end 'xref-match
2492 t substring)
2493 (list substring (line-number-at-pos (point) t)
2494 (eglot-current-column) (- end beg))))))
2495 (`(,summary ,line ,column ,length)
2496 (cond
2497 (visiting (with-current-buffer visiting (funcall collect)))
2498 ((file-readable-p file) (with-current-buffer
2499 (puthash uri (generate-new-buffer " *temp*")
2500 eglot--temp-location-buffers)
2501 (insert-file-contents file)
2502 (funcall collect)))
2503 (t ;; fall back to the "dumb strategy"
2504 (let* ((start (cl-getf range :start))
2505 (line (1+ (cl-getf start :line)))
2506 (start-pos (cl-getf start :character))
2507 (end-pos (cl-getf (cl-getf range :end) :character)))
2508 (list name line start-pos (- end-pos start-pos)))))))
2509 (setf (gethash (expand-file-name file) eglot--servers-by-xrefed-file)
2510 (eglot--current-server-or-lose))
2511 (xref-make-match summary (xref-make-file-location file line column) length)))
2512
2513(defun eglot--workspace-symbols (pat &optional buffer)
2514 "Ask for :workspace/symbol on PAT, return list of formatted strings.
2515If BUFFER, switch to it before."
2516 (with-current-buffer (or buffer (current-buffer))
2517 (unless (eglot--server-capable :workspaceSymbolProvider)
2518 (eglot--error "This LSP server isn't a :workspaceSymbolProvider"))
2519 (mapcar
2520 (lambda (wss)
2521 (eglot--dbind ((WorkspaceSymbol) name containerName kind) wss
2522 (propertize
2523 (format "%s%s %s"
2524 (if (zerop (length containerName)) ""
2525 (concat (propertize containerName 'face 'shadow) " "))
2526 name
2527 (propertize (alist-get kind eglot--symbol-kind-names "Unknown")
2528 'face 'shadow))
2529 'eglot--lsp-workspaceSymbol wss)))
2530 (jsonrpc-request (eglot--current-server-or-lose) :workspace/symbol
2531 `(:query ,pat)))))
2532
2533(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql eglot)))
2534 "Yet another tricky connection between LSP and Elisp completion semantics."
2535 (let ((buf (current-buffer)) (cache eglot--workspace-symbols-cache))
2536 (cl-labels ((refresh (pat) (eglot--workspace-symbols pat buf))
2537 (lookup-1 (pat) ;; check cache, else refresh
2538 (let ((probe (gethash pat cache :missing)))
2539 (if (eq probe :missing) (puthash pat (refresh pat) cache)
2540 probe)))
2541 (lookup (pat)
2542 (let ((res (lookup-1 pat))
2543 (def (and (string= pat "") (gethash :default cache))))
2544 (append def res nil)))
2545 (score (c)
2546 (cl-getf (get-text-property
2547 0 'eglot--lsp-workspaceSymbol c)
2548 :score 0)))
2549 (lambda (string _pred action)
2550 (pcase action
2551 (`metadata `(metadata
2552 (cycle-sort-function
2553 . ,(lambda (completions)
2554 (cl-sort completions #'> :key #'score)))
2555 (category . eglot-indirection-joy)))
2556 (`(eglot--lsp-tryc . ,point) `(eglot--lsp-tryc . (,string . ,point)))
2557 (`(eglot--lsp-allc . ,_point) `(eglot--lsp-allc . ,(lookup string)))
2558 (_ nil))))))
2559
2560(defun eglot--recover-workspace-symbol-meta (string)
2561 "Search `eglot--workspace-symbols-cache' for rich entry of STRING."
2562 (catch 'found
2563 (maphash (lambda (_k v)
2564 (while (consp v)
2565 ;; Like mess? Ask minibuffer.el about improper lists.
2566 (when (equal (car v) string) (throw 'found (car v)))
2567 (setq v (cdr v))))
2568 eglot--workspace-symbols-cache)))
2569
2570(add-to-list 'completion-category-overrides
2571 '(eglot-indirection-joy (styles . (eglot--lsp-backend-style))))
2572
2573(cl-defmethod xref-backend-identifier-at-point ((_backend (eql eglot)))
2574 (let ((attempt
2575 (and (xref--prompt-p this-command)
2576 (puthash :default
2577 (ignore-errors
2578 (eglot--workspace-symbols (symbol-name (symbol-at-point))))
2579 eglot--workspace-symbols-cache))))
2580 (if attempt (car attempt) "LSP identifier at point")))
2581
2582(defvar eglot--lsp-xref-refs nil
2583 "`xref' objects for overriding `xref-backend-references''s.")
2584
2585(cl-defun eglot--lsp-xrefs-for-method (method &key extra-params capability)
2586 "Make `xref''s for METHOD, EXTRA-PARAMS, check CAPABILITY."
2587 (unless (eglot--server-capable
2588 (or capability
2589 (intern
2590 (format ":%sProvider"
2591 (cadr (split-string (symbol-name method)
2592 "/"))))))
2593 (eglot--error "Sorry, this server doesn't do %s" method))
2594 (let ((response
2595 (jsonrpc-request
2596 (eglot--current-server-or-lose)
2597 method (append (eglot--TextDocumentPositionParams) extra-params))))
2598 (eglot--collecting-xrefs (collect)
2599 (mapc
2600 (lambda (loc-or-loc-link)
2601 (let ((sym-name (symbol-name (symbol-at-point))))
2602 (eglot--dcase loc-or-loc-link
2603 (((LocationLink) targetUri targetSelectionRange)
2604 (collect (eglot--xref-make-match sym-name
2605 targetUri targetSelectionRange)))
2606 (((Location) uri range)
2607 (collect (eglot--xref-make-match sym-name
2608 uri range))))))
2609 (if (vectorp response) response (and response (list response)))))))
2610
2611(cl-defun eglot--lsp-xref-helper (method &key extra-params capability )
2612 "Helper for `eglot-find-declaration' & friends."
2613 (let ((eglot--lsp-xref-refs (eglot--lsp-xrefs-for-method
2614 method
2615 :extra-params extra-params
2616 :capability capability)))
2617 (if eglot--lsp-xref-refs
2618 (xref-find-references "LSP identifier at point.")
2619 (eglot--message "%s returned no references" method))))
2620
2621(defun eglot-find-declaration ()
2622 "Find declaration for SYM, the identifier at point."
2623 (interactive)
2624 (eglot--lsp-xref-helper :textDocument/declaration))
2625
2626(defun eglot-find-implementation ()
2627 "Find implementation for SYM, the identifier at point."
2628 (interactive)
2629 (eglot--lsp-xref-helper :textDocument/implementation))
2630
2631(defun eglot-find-typeDefinition ()
2632 "Find type definition for SYM, the identifier at point."
2633 (interactive)
2634 (eglot--lsp-xref-helper :textDocument/typeDefinition))
2635
2636(cl-defmethod xref-backend-definitions ((_backend (eql eglot)) id)
2637 (let ((probe (eglot--recover-workspace-symbol-meta id)))
2638 (if probe
2639 (eglot--dbind ((WorkspaceSymbol) name location)
2640 (get-text-property 0 'eglot--lsp-workspaceSymbol probe)
2641 (eglot--dbind ((Location) uri range) location
2642 (list (eglot--xref-make-match name uri range))))
2643 (eglot--lsp-xrefs-for-method :textDocument/definition))))
2644
2645(cl-defmethod xref-backend-references ((_backend (eql eglot)) _identifier)
2646 (or
2647 eglot--lsp-xref-refs
2648 (eglot--lsp-xrefs-for-method
2649 :textDocument/references :extra-params `(:context (:includeDeclaration t)))))
2650
2651(cl-defmethod xref-backend-apropos ((_backend (eql eglot)) pattern)
2652 (when (eglot--server-capable :workspaceSymbolProvider)
2653 (eglot--collecting-xrefs (collect)
2654 (mapc
2655 (eglot--lambda ((SymbolInformation) name location)
2656 (eglot--dbind ((Location) uri range) location
2657 (collect (eglot--xref-make-match name uri range))))
2658 (jsonrpc-request (eglot--current-server-or-lose)
2659 :workspace/symbol
2660 `(:query ,pattern))))))
2661
2662(defun eglot-format-buffer ()
2663 "Format contents of current buffer."
2664 (interactive)
2665 (eglot-format nil nil))
2666
2667(defun eglot-format (&optional beg end on-type-format)
2668 "Format region BEG END.
2669If either BEG or END is nil, format entire buffer.
2670Interactively, format active region, or entire buffer if region
2671is not active.
2672
2673If non-nil, ON-TYPE-FORMAT is a character just inserted at BEG
2674for which LSP on-type-formatting should be requested."
2675 (interactive (and (region-active-p) (list (region-beginning) (region-end))))
2676 (pcase-let ((`(,method ,cap ,args)
2677 (cond
2678 ((and beg on-type-format)
2679 `(:textDocument/onTypeFormatting
2680 :documentOnTypeFormattingProvider
2681 ,`(:position ,(eglot--pos-to-lsp-position beg)
2682 :ch ,(string on-type-format))))
2683 ((and beg end)
2684 `(:textDocument/rangeFormatting
2685 :documentRangeFormattingProvider
2686 (:range ,(list :start (eglot--pos-to-lsp-position beg)
2687 :end (eglot--pos-to-lsp-position end)))))
2688 (t
2689 '(:textDocument/formatting :documentFormattingProvider nil)))))
2690 (unless (eglot--server-capable cap)
2691 (eglot--error "Server can't format!"))
2692 (eglot--apply-text-edits
2693 (jsonrpc-request
2694 (eglot--current-server-or-lose)
2695 method
2696 (cl-list*
2697 :textDocument (eglot--TextDocumentIdentifier)
2698 :options (list :tabSize tab-width
2699 :insertSpaces (if indent-tabs-mode :json-false t)
2700 :insertFinalNewline (if require-final-newline t :json-false)
2701 :trimFinalNewlines (if delete-trailing-lines t :json-false))
2702 args)
2703 :deferred method))))
2704
2705(defun eglot-completion-at-point ()
2706 "Eglot's `completion-at-point' function."
2707 ;; Commit logs for this function help understand what's going on.
2708 (when-let (completion-capability (eglot--server-capable :completionProvider))
2709 (let* ((server (eglot--current-server-or-lose))
2710 (sort-completions
2711 (lambda (completions)
2712 (cl-sort completions
2713 #'string-lessp
2714 :key (lambda (c)
2715 (or (plist-get
2716 (get-text-property 0 'eglot--lsp-item c)
2717 :sortText)
2718 "")))))
2719 (metadata `(metadata (category . eglot)
2720 (display-sort-function . ,sort-completions)))
2721 resp items (cached-proxies :none)
2722 (proxies
2723 (lambda ()
2724 (if (listp cached-proxies) cached-proxies
2725 (setq resp
2726 (jsonrpc-request server
2727 :textDocument/completion
2728 (eglot--CompletionParams)
2729 :deferred :textDocument/completion
2730 :cancel-on-input t))
2731 (setq items (append
2732 (if (vectorp resp) resp (plist-get resp :items))
2733 nil))
2734 (setq cached-proxies
2735 (mapcar
2736 (jsonrpc-lambda
2737 (&rest item &key label insertText insertTextFormat
2738 &allow-other-keys)
2739 (let ((proxy
2740 (cond ((and (eql insertTextFormat 2)
2741 (eglot--snippet-expansion-fn))
2742 (string-trim-left label))
2743 ((and insertText
2744 (not (string-empty-p insertText)))
2745 insertText)
2746 (t
2747 (string-trim-left label)))))
2748 (unless (zerop (length proxy))
2749 (put-text-property 0 1 'eglot--lsp-item item proxy))
2750 proxy))
2751 items)))))
2752 (resolved (make-hash-table))
2753 (resolve-maybe
2754 ;; Maybe completion/resolve JSON object `lsp-comp' into
2755 ;; another JSON object, if at all possible. Otherwise,
2756 ;; just return lsp-comp.
2757 (lambda (lsp-comp)
2758 (or (gethash lsp-comp resolved)
2759 (setf (gethash lsp-comp resolved)
2760 (if (and (eglot--server-capable :completionProvider
2761 :resolveProvider)
2762 (plist-get lsp-comp :data))
2763 (jsonrpc-request server :completionItem/resolve
2764 lsp-comp :cancel-on-input t)
2765 lsp-comp)))))
2766 (bounds (bounds-of-thing-at-point 'symbol)))
2767 (list
2768 (or (car bounds) (point))
2769 (or (cdr bounds) (point))
2770 (lambda (probe pred action)
2771 (cond
2772 ((eq action 'metadata) metadata) ; metadata
2773 ((eq action 'lambda) ; test-completion
2774 (test-completion probe (funcall proxies)))
2775 ((eq (car-safe action) 'boundaries) nil) ; boundaries
2776 ((null action) ; try-completion
2777 (try-completion probe (funcall proxies)))
2778 ((eq action t) ; all-completions
2779 (all-completions
2780 ""
2781 (funcall proxies)
2782 (lambda (proxy)
2783 (let* ((item (get-text-property 0 'eglot--lsp-item proxy))
2784 (filterText (plist-get item :filterText)))
2785 (and (or (null pred) (funcall pred proxy))
2786 (string-prefix-p
2787 probe (or filterText proxy) completion-ignore-case))))))))
2788 :annotation-function
2789 (lambda (proxy)
2790 (eglot--dbind ((CompletionItem) detail kind)
2791 (get-text-property 0 'eglot--lsp-item proxy)
2792 (let* ((detail (and (stringp detail)
2793 (not (string= detail ""))
2794 detail))
2795 (annotation
2796 (or detail
2797 (cdr (assoc kind eglot--kind-names)))))
2798 (when annotation
2799 (concat " "
2800 (propertize annotation
2801 'face 'font-lock-function-name-face))))))
2802 :company-kind
2803 ;; Associate each lsp-item with a lsp-kind symbol.
2804 (lambda (proxy)
2805 (when-let* ((lsp-item (get-text-property 0 'eglot--lsp-item proxy))
2806 (kind (alist-get (plist-get lsp-item :kind)
2807 eglot--kind-names)))
2808 (intern (downcase kind))))
2809 :company-deprecated
2810 (lambda (proxy)
2811 (when-let ((lsp-item (get-text-property 0 'eglot--lsp-item proxy)))
2812 (or (seq-contains-p (plist-get lsp-item :tags)
2813 1)
2814 (eq t (plist-get lsp-item :deprecated)))))
2815 :company-docsig
2816 ;; FIXME: autoImportText is specific to the pyright language server
2817 (lambda (proxy)
2818 (when-let* ((lsp-comp (get-text-property 0 'eglot--lsp-item proxy))
2819 (data (plist-get (funcall resolve-maybe lsp-comp) :data))
2820 (import-text (plist-get data :autoImportText)))
2821 import-text))
2822 :company-doc-buffer
2823 (lambda (proxy)
2824 (let* ((documentation
2825 (let ((lsp-comp (get-text-property 0 'eglot--lsp-item proxy)))
2826 (plist-get (funcall resolve-maybe lsp-comp) :documentation)))
2827 (formatted (and documentation
2828 (eglot--format-markup documentation))))
2829 (when formatted
2830 (with-current-buffer (get-buffer-create " *eglot doc*")
2831 (erase-buffer)
2832 (insert formatted)
2833 (current-buffer)))))
2834 :company-require-match 'never
2835 :company-prefix-length
2836 (save-excursion
2837 (when (car bounds) (goto-char (car bounds)))
2838 (when (listp completion-capability)
2839 (looking-back
2840 (regexp-opt
2841 (cl-coerce (cl-getf completion-capability :triggerCharacters) 'list))
2842 (line-beginning-position))))
2843 :exit-function
2844 (lambda (proxy status)
2845 (when (memq status '(finished exact))
2846 ;; To assist in using this whole `completion-at-point'
2847 ;; function inside `completion-in-region', ensure the exit
2848 ;; function runs in the buffer where the completion was
2849 ;; triggered from. This should probably be in Emacs itself.
2850 ;; (github#505)
2851 (with-current-buffer (if (minibufferp)
2852 (window-buffer (minibuffer-selected-window))
2853 (current-buffer))
2854 (eglot--dbind ((CompletionItem) insertTextFormat
2855 insertText textEdit additionalTextEdits label)
2856 (funcall
2857 resolve-maybe
2858 (or (get-text-property 0 'eglot--lsp-item proxy)
2859 ;; When selecting from the *Completions*
2860 ;; buffer, `proxy' won't have any properties.
2861 ;; A lookup should fix that (github#148)
2862 (get-text-property
2863 0 'eglot--lsp-item
2864 (cl-find proxy (funcall proxies) :test #'string=))))
2865 (let ((snippet-fn (and (eql insertTextFormat 2)
2866 (eglot--snippet-expansion-fn))))
2867 (cond (textEdit
2868 ;; Undo (yes, undo) the newly inserted completion.
2869 ;; If before completion the buffer was "foo.b" and
2870 ;; now is "foo.bar", `proxy' will be "bar". We
2871 ;; want to delete only "ar" (`proxy' minus the
2872 ;; symbol whose bounds we've calculated before)
2873 ;; (github#160).
2874 (delete-region (+ (- (point) (length proxy))
2875 (if bounds
2876 (- (cdr bounds) (car bounds))
2877 0))
2878 (point))
2879 (eglot--dbind ((TextEdit) range newText) textEdit
2880 (pcase-let ((`(,beg . ,end)
2881 (eglot--range-region range)))
2882 (delete-region beg end)
2883 (goto-char beg)
2884 (funcall (or snippet-fn #'insert) newText))))
2885 (snippet-fn
2886 ;; A snippet should be inserted, but using plain
2887 ;; `insertText'. This requires us to delete the
2888 ;; whole completion, since `insertText' is the full
2889 ;; completion's text.
2890 (delete-region (- (point) (length proxy)) (point))
2891 (funcall snippet-fn (or insertText label))))
2892 (when (cl-plusp (length additionalTextEdits))
2893 (eglot--apply-text-edits additionalTextEdits)))
2894 (eglot--signal-textDocument/didChange)
2895 (eldoc)))))))))
2896
2897(defun eglot--hover-info (contents &optional _range)
2898 (mapconcat #'eglot--format-markup
2899 (if (vectorp contents) contents (list contents)) "\n"))
2900
2901(defun eglot--sig-info (sigs active-sig sig-help-active-param)
2902 (cl-loop
2903 for (sig . moresigs) on (append sigs nil) for i from 0
2904 concat
2905 (eglot--dbind ((SignatureInformation) label documentation parameters activeParameter) sig
2906 (with-temp-buffer
2907 (save-excursion (insert label))
2908 (let ((active-param (or activeParameter sig-help-active-param))
2909 params-start params-end)
2910 ;; Ad-hoc attempt to parse label as <name>(<params>)
2911 (when (looking-at "\\([^(]+\\)(\\([^)]+\\))")
2912 (setq params-start (match-beginning 2) params-end (match-end 2))
2913 (add-face-text-property (match-beginning 1) (match-end 1)
2914 'font-lock-function-name-face))
2915 (when (eql i active-sig)
2916 ;; Decide whether to add one-line-summary to signature line
2917 (when (and (stringp documentation)
2918 (string-match "[[:space:]]*\\([^.\r\n]+[.]?\\)"
2919 documentation))
2920 (setq documentation (match-string 1 documentation))
2921 (unless (string-prefix-p (string-trim documentation) label)
2922 (goto-char (point-max))
2923 (insert ": " (eglot--format-markup documentation))))
2924 ;; Decide what to do with the active parameter...
2925 (when (and (eql i active-sig) active-param
2926 (< -1 active-param (length parameters)))
2927 (eglot--dbind ((ParameterInformation) label documentation)
2928 (aref parameters active-param)
2929 ;; ...perhaps highlight it in the formals list
2930 (when params-start
2931 (goto-char params-start)
2932 (pcase-let
2933 ((`(,beg ,end)
2934 (if (stringp label)
2935 (let ((case-fold-search nil))
2936 (and (re-search-forward
2937 (concat "\\<" (regexp-quote label) "\\>")
2938 params-end t)
2939 (list (match-beginning 0) (match-end 0))))
2940 (mapcar #'1+ (append label nil)))))
2941 (if (and beg end)
2942 (add-face-text-property
2943 beg end
2944 'eldoc-highlight-function-argument))))
2945 ;; ...and/or maybe add its doc on a line by its own.
2946 (when documentation
2947 (goto-char (point-max))
2948 (insert "\n"
2949 (propertize
2950 (if (stringp label)
2951 label
2952 (apply #'buffer-substring (mapcar #'1+ label)))
2953 'face 'eldoc-highlight-function-argument)
2954 ": " (eglot--format-markup documentation))))))
2955 (buffer-string))))
2956 when moresigs concat "\n"))
2957
2958(defun eglot-signature-eldoc-function (cb)
2959 "A member of `eldoc-documentation-functions', for signatures."
2960 (when (eglot--server-capable :signatureHelpProvider)
2961 (let ((buf (current-buffer)))
2962 (jsonrpc-async-request
2963 (eglot--current-server-or-lose)
2964 :textDocument/signatureHelp (eglot--TextDocumentPositionParams)
2965 :success-fn
2966 (eglot--lambda ((SignatureHelp)
2967 signatures activeSignature activeParameter)
2968 (eglot--when-buffer-window buf
2969 (funcall cb
2970 (unless (seq-empty-p signatures)
2971 (eglot--sig-info signatures
2972 activeSignature
2973 activeParameter)))))
2974 :deferred :textDocument/signatureHelp))
2975 t))
2976
2977(defun eglot-hover-eldoc-function (cb)
2978 "A member of `eldoc-documentation-functions', for hover."
2979 (when (eglot--server-capable :hoverProvider)
2980 (let ((buf (current-buffer)))
2981 (jsonrpc-async-request
2982 (eglot--current-server-or-lose)
2983 :textDocument/hover (eglot--TextDocumentPositionParams)
2984 :success-fn (eglot--lambda ((Hover) contents range)
2985 (eglot--when-buffer-window buf
2986 (let ((info (unless (seq-empty-p contents)
2987 (eglot--hover-info contents range))))
2988 (funcall cb info :buffer t))))
2989 :deferred :textDocument/hover))
2990 (eglot--highlight-piggyback cb)
2991 t))
2992
2993(defvar eglot--highlights nil "Overlays for textDocument/documentHighlight.")
2994
2995(defun eglot--highlight-piggyback (_cb)
2996 "Request and handle `:textDocument/documentHighlight'."
2997 ;; FIXME: Obviously, this is just piggy backing on eldoc's calls for
2998 ;; convenience, as shown by the fact that we just ignore cb.
2999 (let ((buf (current-buffer)))
3000 (when (eglot--server-capable :documentHighlightProvider)
3001 (jsonrpc-async-request
3002 (eglot--current-server-or-lose)
3003 :textDocument/documentHighlight (eglot--TextDocumentPositionParams)
3004 :success-fn
3005 (lambda (highlights)
3006 (mapc #'delete-overlay eglot--highlights)
3007 (setq eglot--highlights
3008 (eglot--when-buffer-window buf
3009 (mapcar
3010 (eglot--lambda ((DocumentHighlight) range)
3011 (pcase-let ((`(,beg . ,end)
3012 (eglot--range-region range)))
3013 (let ((ov (make-overlay beg end)))
3014 (overlay-put ov 'face 'eglot-highlight-symbol-face)
3015 (overlay-put ov 'modification-hooks
3016 `(,(lambda (o &rest _) (delete-overlay o))))
3017 ov)))
3018 highlights))))
3019 :deferred :textDocument/documentHighlight)
3020 nil)))
3021
3022(defun eglot-imenu ()
3023 "Eglot's `imenu-create-index-function'.
3024Returns a list as described in docstring of `imenu--index-alist'."
3025 (cl-labels
3026 ((unfurl (obj)
3027 (eglot--dcase obj
3028 (((SymbolInformation)) (list obj))
3029 (((DocumentSymbol) name children)
3030 (cons obj
3031 (mapcar
3032 (lambda (c)
3033 (plist-put
3034 c :containerName
3035 (let ((existing (plist-get c :containerName)))
3036 (if existing (format "%s::%s" name existing)
3037 name))))
3038 (mapcan #'unfurl children)))))))
3039 (mapcar
3040 (pcase-lambda (`(,kind . ,objs))
3041 (cons
3042 (alist-get kind eglot--symbol-kind-names "Unknown")
3043 (mapcan (pcase-lambda (`(,container . ,objs))
3044 (let ((elems (mapcar
3045 (lambda (obj)
3046 (cons (plist-get obj :name)
3047 (car (eglot--range-region
3048 (eglot--dcase obj
3049 (((SymbolInformation) location)
3050 (plist-get location :range))
3051 (((DocumentSymbol) selectionRange)
3052 selectionRange))))))
3053 objs)))
3054 (if container (list (cons container elems)) elems)))
3055 (seq-group-by
3056 (lambda (e) (plist-get e :containerName)) objs))))
3057 (seq-group-by
3058 (lambda (obj) (plist-get obj :kind))
3059 (mapcan #'unfurl
3060 (jsonrpc-request (eglot--current-server-or-lose)
3061 :textDocument/documentSymbol
3062 `(:textDocument
3063 ,(eglot--TextDocumentIdentifier))
3064 :cancel-on-input non-essential))))))
3065
3066(defun eglot--apply-text-edits (edits &optional version)
3067 "Apply EDITS for current buffer if at VERSION, or if it's nil."
3068 (unless (or (not version) (equal version eglot--versioned-identifier))
3069 (jsonrpc-error "Edits on `%s' require version %d, you have %d"
3070 (current-buffer) version eglot--versioned-identifier))
3071 (atomic-change-group
3072 (let* ((change-group (prepare-change-group))
3073 (howmany (length edits))
3074 (reporter (make-progress-reporter
3075 (format "[eglot] applying %s edits to `%s'..."
3076 howmany (current-buffer))
3077 0 howmany))
3078 (done 0))
3079 (mapc (pcase-lambda (`(,newText ,beg . ,end))
3080 (let ((source (current-buffer)))
3081 (with-temp-buffer
3082 (insert newText)
3083 (let ((temp (current-buffer)))
3084 (with-current-buffer source
3085 (save-excursion
3086 (save-restriction
3087 (narrow-to-region beg end)
3088
3089 ;; On emacs versions < 26.2,
3090 ;; `replace-buffer-contents' is buggy - it calls
3091 ;; change functions with invalid arguments - so we
3092 ;; manually call the change functions here.
3093 ;;
3094 ;; See emacs bugs #32237, #32278:
3095 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32237
3096 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32278
3097 (let ((inhibit-modification-hooks t)
3098 (length (- end beg))
3099 (beg (marker-position beg))
3100 (end (marker-position end)))
3101 (run-hook-with-args 'before-change-functions
3102 beg end)
3103 (replace-buffer-contents temp)
3104 (run-hook-with-args 'after-change-functions
3105 beg (+ beg (length newText))
3106 length))))
3107 (progress-reporter-update reporter (cl-incf done)))))))
3108 (mapcar (eglot--lambda ((TextEdit) range newText)
3109 (cons newText (eglot--range-region range 'markers)))
3110 (reverse edits)))
3111 (undo-amalgamate-change-group change-group)
3112 (progress-reporter-done reporter))))
3113
3114(defun eglot--apply-workspace-edit (wedit &optional confirm)
3115 "Apply the workspace edit WEDIT. If CONFIRM, ask user first."
3116 (eglot--dbind ((WorkspaceEdit) changes documentChanges) wedit
3117 (let ((prepared
3118 (mapcar (eglot--lambda ((TextDocumentEdit) textDocument edits)
3119 (eglot--dbind ((VersionedTextDocumentIdentifier) uri version)
3120 textDocument
3121 (list (eglot--uri-to-path uri) edits version)))
3122 documentChanges)))
3123 (unless (and changes documentChanges)
3124 ;; We don't want double edits, and some servers send both
3125 ;; changes and documentChanges. This unless ensures that we
3126 ;; prefer documentChanges over changes.
3127 (cl-loop for (uri edits) on changes by #'cddr
3128 do (push (list (eglot--uri-to-path uri) edits) prepared)))
3129 (if (or confirm
3130 (cl-notevery #'find-buffer-visiting
3131 (mapcar #'car prepared)))
3132 (unless (y-or-n-p
3133 (format "[eglot] Server wants to edit:\n %s\n Proceed? "
3134 (mapconcat #'identity (mapcar #'car prepared) "\n ")))
3135 (jsonrpc-error "User cancelled server edit")))
3136 (cl-loop for edit in prepared
3137 for (path edits version) = edit
3138 do (with-current-buffer (find-file-noselect path)
3139 (eglot--apply-text-edits edits version))
3140 finally (eldoc) (eglot--message "Edit successful!")))))
3141
3142(defun eglot-rename (newname)
3143 "Rename the current symbol to NEWNAME."
3144 (interactive
3145 (list (read-from-minibuffer
3146 (format "Rename `%s' to: " (or (thing-at-point 'symbol t)
3147 "unknown symbol"))
3148 nil nil nil nil
3149 (symbol-name (symbol-at-point)))))
3150 (unless (eglot--server-capable :renameProvider)
3151 (eglot--error "Server can't rename!"))
3152 (eglot--apply-workspace-edit
3153 (jsonrpc-request (eglot--current-server-or-lose)
3154 :textDocument/rename `(,@(eglot--TextDocumentPositionParams)
3155 :newName ,newname))
3156 current-prefix-arg))
3157
3158(defun eglot--region-bounds ()
3159 "Region bounds if active, else bounds of things at point."
3160 (if (use-region-p) `(,(region-beginning) ,(region-end))
3161 (let ((boftap (bounds-of-thing-at-point 'sexp)))
3162 (list (car boftap) (cdr boftap)))))
3163
3164(defun eglot-code-actions (beg &optional end action-kind interactive)
3165 "Find LSP code actions of type ACTION-KIND between BEG and END.
3166Interactively, offer to execute them.
3167If ACTION-KIND is nil, consider all kinds of actions.
3168Interactively, default BEG and END to region's bounds else BEG is
3169point and END is nil, which results in a request for code actions
3170at point. With prefix argument, prompt for ACTION-KIND."
3171 (interactive
3172 `(,@(eglot--region-bounds)
3173 ,(and current-prefix-arg
3174 (completing-read "[eglot] Action kind: "
3175 '("quickfix" "refactor.extract" "refactor.inline"
3176 "refactor.rewrite" "source.organizeImports")))
3177 t))
3178 (unless (or (not interactive)
3179 (eglot--server-capable :codeActionProvider))
3180 (eglot--error "Server can't execute code actions!"))
3181 (let* ((server (eglot--current-server-or-lose))
3182 (actions
3183 (jsonrpc-request
3184 server
3185 :textDocument/codeAction
3186 (list :textDocument (eglot--TextDocumentIdentifier)
3187 :range (list :start (eglot--pos-to-lsp-position beg)
3188 :end (eglot--pos-to-lsp-position end))
3189 :context
3190 `(:diagnostics
3191 [,@(cl-loop for diag in (flymake-diagnostics beg end)
3192 when (cdr (assoc 'eglot-lsp-diag
3193 (eglot--diag-data diag)))
3194 collect it)]
3195 ,@(when action-kind `(:only [,action-kind]))))
3196 :deferred t))
3197 ;; Redo filtering, in case the `:only' didn't go through.
3198 (actions (cl-loop for a across actions
3199 when (or (not action-kind)
3200 (equal action-kind (plist-get a :kind)))
3201 collect a)))
3202 (if interactive
3203 (eglot--read-execute-code-action actions server action-kind)
3204 actions)))
3205
3206(defun eglot--read-execute-code-action (actions server &optional action-kind)
3207 "Helper for interactive calls to `eglot-code-actions'"
3208 (let* ((menu-items
3209 (or (cl-loop for a in actions
3210 collect (cons (plist-get a :title) a))
3211 (apply #'eglot--error
3212 (if action-kind `("No \"%s\" code actions here" ,action-kind)
3213 `("No code actions here")))))
3214 (preferred-action (cl-find-if
3215 (lambda (menu-item)
3216 (plist-get (cdr menu-item) :isPreferred))
3217 menu-items))
3218 (default-action (car (or preferred-action (car menu-items))))
3219 (chosen (if (and action-kind (null (cadr menu-items)))
3220 (cdr (car menu-items))
3221 (if (listp last-nonmenu-event)
3222 (x-popup-menu last-nonmenu-event `("Eglot code actions:"
3223 ("dummy" ,@menu-items)))
3224 (cdr (assoc (completing-read
3225 (format "[eglot] Pick an action (default %s): "
3226 default-action)
3227 menu-items nil t nil nil default-action)
3228 menu-items))))))
3229 (eglot--dcase chosen
3230 (((Command) command arguments)
3231 (eglot-execute-command server (intern command) arguments))
3232 (((CodeAction) edit command)
3233 (when edit (eglot--apply-workspace-edit edit))
3234 (when command
3235 (eglot--dbind ((Command) command arguments) command
3236 (eglot-execute-command server (intern command) arguments)))))))
3237
3238(defmacro eglot--code-action (name kind)
3239 "Define NAME to execute KIND code action."
3240 `(defun ,name (beg &optional end)
3241 ,(format "Execute `%s' code actions between BEG and END." kind)
3242 (interactive (eglot--region-bounds))
3243 (eglot-code-actions beg end ,kind)))
3244
3245(eglot--code-action eglot-code-action-organize-imports "source.organizeImports")
3246(eglot--code-action eglot-code-action-extract "refactor.extract")
3247(eglot--code-action eglot-code-action-inline "refactor.inline")
3248(eglot--code-action eglot-code-action-rewrite "refactor.rewrite")
3249(eglot--code-action eglot-code-action-quickfix "quickfix")
3250
3251
3252;;; Dynamic registration
3253;;;
3254(cl-defmethod eglot-register-capability
3255 (server (method (eql workspace/didChangeWatchedFiles)) id &key watchers)
3256 "Handle dynamic registration of workspace/didChangeWatchedFiles."
3257 (eglot-unregister-capability server method id)
3258 (let* (success
3259 (globs (mapcar
3260 (eglot--lambda ((FileSystemWatcher) globPattern)
3261 (eglot--glob-compile globPattern t t))
3262 watchers))
3263 (dirs-to-watch
3264 (delete-dups (mapcar #'file-name-directory
3265 (project-files
3266 (eglot--project server))))))
3267 (cl-labels
3268 ((handle-event
3269 (event)
3270 (pcase-let ((`(,desc ,action ,file ,file1) event))
3271 (cond
3272 ((and (memq action '(created changed deleted))
3273 (cl-find file globs :test (lambda (f g) (funcall g f))))
3274 (jsonrpc-notify
3275 server :workspace/didChangeWatchedFiles
3276 `(:changes ,(vector `(:uri ,(eglot--path-to-uri file)
3277 :type ,(cl-case action
3278 (created 1)
3279 (changed 2)
3280 (deleted 3)))))))
3281 ((eq action 'renamed)
3282 (handle-event `(,desc 'deleted ,file))
3283 (handle-event `(,desc 'created ,file1)))))))
3284 (unwind-protect
3285 (progn
3286 (dolist (dir dirs-to-watch)
3287 (push (file-notify-add-watch dir '(change) #'handle-event)
3288 (gethash id (eglot--file-watches server))))
3289 (setq
3290 success
3291 `(:message ,(format "OK, watching %s directories in %s watchers"
3292 (length dirs-to-watch) (length watchers)))))
3293 (unless success
3294 (eglot-unregister-capability server method id))))))
3295
3296(cl-defmethod eglot-unregister-capability
3297 (server (_method (eql workspace/didChangeWatchedFiles)) id)
3298 "Handle dynamic unregistration of workspace/didChangeWatchedFiles."
3299 (mapc #'file-notify-rm-watch (gethash id (eglot--file-watches server)))
3300 (remhash id (eglot--file-watches server))
3301 (list t "OK"))
3302
3303
3304;;; Glob heroics
3305;;;
3306(defun eglot--glob-parse (glob)
3307 "Compute list of (STATE-SYM EMITTER-FN PATTERN)."
3308 (with-temp-buffer
3309 (save-excursion (insert glob))
3310 (cl-loop
3311 with grammar = '((:** "\\*\\*/?" eglot--glob-emit-**)
3312 (:* "\\*" eglot--glob-emit-*)
3313 (:? "\\?" eglot--glob-emit-?)
3314 (:{} "{[^][*{}]+}" eglot--glob-emit-{})
3315 (:range "\\[\\^?[^][/,*{}]+\\]" eglot--glob-emit-range)
3316 (:literal "[^][,*?{}]+" eglot--glob-emit-self))
3317 until (eobp)
3318 collect (cl-loop
3319 for (_token regexp emitter) in grammar
3320 thereis (and (re-search-forward (concat "\\=" regexp) nil t)
3321 (list (cl-gensym "state-") emitter (match-string 0)))
3322 finally (error "Glob '%s' invalid at %s" (buffer-string) (point))))))
3323
3324(defun eglot--glob-compile (glob &optional byte-compile noerror)
3325 "Convert GLOB into Elisp function. Maybe BYTE-COMPILE it.
3326If NOERROR, return predicate, else erroring function."
3327 (let* ((states (eglot--glob-parse glob))
3328 (body `(with-current-buffer (get-buffer-create " *eglot-glob-matcher*")
3329 (erase-buffer)
3330 (save-excursion (insert string))
3331 (cl-labels ,(cl-loop for (this that) on states
3332 for (self emit text) = this
3333 for next = (or (car that) 'eobp)
3334 collect (funcall emit text self next))
3335 (or (,(caar states))
3336 (error "Glob done but more unmatched text: '%s'"
3337 (buffer-substring (point) (point-max)))))))
3338 (form `(lambda (string) ,(if noerror `(ignore-errors ,body) body))))
3339 (if byte-compile (byte-compile form) form)))
3340
3341(defun eglot--glob-emit-self (text self next)
3342 `(,self () (re-search-forward ,(concat "\\=" (regexp-quote text))) (,next)))
3343
3344(defun eglot--glob-emit-** (_ self next)
3345 `(,self () (or (ignore-errors (save-excursion (,next)))
3346 (and (re-search-forward "\\=/?[^/]+/?") (,self)))))
3347
3348(defun eglot--glob-emit-* (_ self next)
3349 `(,self () (re-search-forward "\\=[^/]")
3350 (or (ignore-errors (save-excursion (,next))) (,self))))
3351
3352(defun eglot--glob-emit-? (_ self next)
3353 `(,self () (re-search-forward "\\=[^/]") (,next)))
3354
3355(defun eglot--glob-emit-{} (arg self next)
3356 (let ((alternatives (split-string (substring arg 1 (1- (length arg))) ",")))
3357 `(,self ()
3358 (or (re-search-forward ,(concat "\\=" (regexp-opt alternatives)) nil t)
3359 (error "Failed matching any of %s" ',alternatives))
3360 (,next))))
3361
3362(defun eglot--glob-emit-range (arg self next)
3363 (when (eq ?! (aref arg 1)) (aset arg 1 ?^))
3364 `(,self () (re-search-forward ,(concat "\\=" arg)) (,next)))
3365
3366
3367;;; List connections mode
3368
3369(define-derived-mode eglot-list-connections-mode tabulated-list-mode
3370 "" "Eglot mode for listing server connections
3371\\{eglot-list-connections-mode-map}"
3372 (setq-local tabulated-list-format
3373 `[("Language server" 16) ("Project name" 16) ("Modes handled" 16)])
3374 (tabulated-list-init-header))
3375
3376(defun eglot-list-connections ()
3377 "List currently active Eglot connections."
3378 (interactive)
3379 (with-current-buffer
3380 (get-buffer-create "*EGLOT connections*")
3381 (let ((inhibit-read-only t))
3382 (erase-buffer)
3383 (eglot-list-connections-mode)
3384 (setq-local tabulated-list-entries
3385 (mapcar
3386 (lambda (server)
3387 (list server
3388 `[,(or (plist-get (eglot--server-info server) :name)
3389 (jsonrpc-name server))
3390 ,(eglot-project-nickname server)
3391 ,(mapconcat #'symbol-name
3392 (eglot--major-modes server)
3393 ", ")]))
3394 (cl-reduce #'append
3395 (hash-table-values eglot--servers-by-project))))
3396 (revert-buffer)
3397 (pop-to-buffer (current-buffer)))))
3398
3399
3400;;; Hacks
3401;;;
3402;; FIXME: Although desktop.el compatibility is Emacs bug#56407, the
3403;; optimal solution agreed to there is a bit more work than what I
3404;; have time to right now. See
3405;; e.g. https://debbugs.gnu.org/cgi/bugreport.cgi?bug=bug%2356407#68.
3406;; For now, just use `with-eval-after-load'
3407(with-eval-after-load 'desktop
3408 (add-to-list 'desktop-minor-mode-handlers '(eglot--managed-mode . ignore)))
3409
3410
3411;;; Obsolete
3412;;;
3413
3414(make-obsolete-variable 'eglot--managed-mode-hook
3415 'eglot-managed-mode-hook "1.6")
3416(provide 'eglot)
3417
3418
3419;;; Backend completion
3420
3421;; Written by Stefan Monnier circa 2016. Something to move to
3422;; minibuffer.el "ASAP" (with all the `eglot--lsp-' replaced by
3423;; something else. The very same code already in SLY and stable for a
3424;; long time.
3425
3426;; This "completion style" delegates all the work to the "programmable
3427;; completion" table which is then free to implement its own
3428;; completion style. Typically this is used to take advantage of some
3429;; external tool which already has its own completion system and
3430;; doesn't give you efficient access to the prefix completion needed
3431;; by other completion styles. The table should recognize the symbols
3432;; 'eglot--lsp-tryc and 'eglot--lsp-allc as ACTION, reply with
3433;; (eglot--lsp-tryc COMP...) or (eglot--lsp-allc . (STRING . POINT)),
3434;; accordingly. tryc/allc names made akward/recognizable on purpose.
3435
3436(add-to-list 'completion-styles-alist
3437 '(eglot--lsp-backend-style
3438 eglot--lsp-backend-style-try-completion
3439 eglot--lsp-backend-style-all-completions
3440 "Ad-hoc completion style provided by the completion table."))
3441
3442(defun eglot--lsp-backend-style-call (op string table pred point)
3443 (when (functionp table)
3444 (let ((res (funcall table string pred (cons op point))))
3445 (when (eq op (car-safe res))
3446 (cdr res)))))
3447
3448(defun eglot--lsp-backend-style-try-completion (string table pred point)
3449 (eglot--lsp-backend-style-call 'eglot--lsp-tryc string table pred point))
3450
3451(defun eglot--lsp-backend-style-all-completions (string table pred point)
3452 (eglot--lsp-backend-style-call 'eglot--lsp-allc string table pred point))
3453
3454
3455;; Local Variables:
3456;; bug-reference-bug-regexp: "\\(github#\\([0-9]+\\)\\)"
3457;; bug-reference-url-format: "https://github.com/joaotavora/eglot/issues/%s"
3458;; checkdoc-force-docstrings-flag: nil
3459;; End:
3460
3461;;; eglot.el ends here
diff --git a/lisp/progmodes/modula2.el b/lisp/progmodes/modula2.el
index e668570ba17..09cb848fd52 100644
--- a/lisp/progmodes/modula2.el
+++ b/lisp/progmodes/modula2.el
@@ -65,39 +65,36 @@
65 "Column for aligning the end of a comment, in Modula-2." 65 "Column for aligning the end of a comment, in Modula-2."
66 :type 'integer) 66 :type 'integer)
67 67
68;;; Added by TEP 68(defvar-keymap m2-mode-map
69(defvar m2-mode-map 69 :doc "Keymap used in Modula-2 mode."
70 (let ((map (make-sparse-keymap))) 70 ;; FIXME: Many of those bindings are contrary to coding conventions.
71 ;; FIXME: Many of those bindings are contrary to coding conventions. 71 "C-c b" #'m2-begin
72 (define-key map "\C-cb" #'m2-begin) 72 "C-c c" #'m2-case
73 (define-key map "\C-cc" #'m2-case) 73 "C-c d" #'m2-definition
74 (define-key map "\C-cd" #'m2-definition) 74 "C-c e" #'m2-else
75 (define-key map "\C-ce" #'m2-else) 75 "C-c f" #'m2-for
76 (define-key map "\C-cf" #'m2-for) 76 "C-c h" #'m2-header
77 (define-key map "\C-ch" #'m2-header) 77 "C-c i" #'m2-if
78 (define-key map "\C-ci" #'m2-if) 78 "C-c m" #'m2-module
79 (define-key map "\C-cm" #'m2-module) 79 "C-c l" #'m2-loop
80 (define-key map "\C-cl" #'m2-loop) 80 "C-c o" #'m2-or
81 (define-key map "\C-co" #'m2-or) 81 "C-c p" #'m2-procedure
82 (define-key map "\C-cp" #'m2-procedure) 82 "C-c C-w" #'m2-with
83 (define-key map "\C-c\C-w" #'m2-with) 83 "C-c r" #'m2-record
84 (define-key map "\C-cr" #'m2-record) 84 "C-c s" #'m2-stdio
85 (define-key map "\C-cs" #'m2-stdio) 85 "C-c t" #'m2-type
86 (define-key map "\C-ct" #'m2-type) 86 "C-c u" #'m2-until
87 (define-key map "\C-cu" #'m2-until) 87 "C-c v" #'m2-var
88 (define-key map "\C-cv" #'m2-var) 88 "C-c w" #'m2-while
89 (define-key map "\C-cw" #'m2-while) 89 "C-c x" #'m2-export
90 (define-key map "\C-cx" #'m2-export) 90 "C-c y" #'m2-import
91 (define-key map "\C-cy" #'m2-import) 91 "C-c {" #'m2-begin-comment
92 (define-key map "\C-c{" #'m2-begin-comment) 92 "C-c }" #'m2-end-comment
93 (define-key map "\C-c}" #'m2-end-comment) 93 "C-c C-z" #'suspend-emacs
94 (define-key map "\C-c\C-z" #'suspend-emacs) 94 "C-c C-v" #'m2-visit
95 (define-key map "\C-c\C-v" #'m2-visit) 95 "C-c C-t" #'m2-toggle
96 (define-key map "\C-c\C-t" #'m2-toggle) 96 "C-c C-l" #'m2-link
97 (define-key map "\C-c\C-l" #'m2-link) 97 "C-c C-c" #'m2-compile)
98 (define-key map "\C-c\C-c" #'m2-compile)
99 map)
100 "Keymap used in Modula-2 mode.")
101 98
102(defcustom m2-indent 5 99(defcustom m2-indent 5
103 "This variable gives the indentation in Modula-2 mode." 100 "This variable gives the indentation in Modula-2 mode."
diff --git a/lisp/progmodes/perl-mode.el b/lisp/progmodes/perl-mode.el
index 7b7a2cdf019..c5d5d703fc9 100644
--- a/lisp/progmodes/perl-mode.el
+++ b/lisp/progmodes/perl-mode.el
@@ -215,11 +215,16 @@
215(eval-and-compile 215(eval-and-compile
216 (defconst perl--syntax-exp-intro-keywords 216 (defconst perl--syntax-exp-intro-keywords
217 '("split" "if" "unless" "until" "while" "print" "printf" 217 '("split" "if" "unless" "until" "while" "print" "printf"
218 "grep" "map" "not" "or" "and" "for" "foreach" "return")) 218 "grep" "map" "not" "or" "and" "for" "foreach" "return" "die"
219 "warn" "eval"))
219 220
220 (defconst perl--syntax-exp-intro-regexp 221 (defconst perl--syntax-exp-intro-regexp
221 (concat "\\(?:\\(?:^\\|[^$@&%[:word:]]\\)" 222 (concat "\\(?:\\(?:^\\|[^$@&%[:word:]]\\)"
222 (regexp-opt perl--syntax-exp-intro-keywords) 223 (regexp-opt perl--syntax-exp-intro-keywords)
224 ;; A HERE document as an argument to printf?
225 ;; when printing to a filehandle.
226 "\\|printf?[ \t]*$?[_[:alpha:]][_[:alnum:]]*"
227 "\\|=>"
223 "\\|[?:.,;|&*=!~({[]" 228 "\\|[?:.,;|&*=!~({[]"
224 "\\|[^-+][-+]" ;Bug#42168: `+' is intro but `++' isn't! 229 "\\|[^-+][-+]" ;Bug#42168: `+' is intro but `++' isn't!
225 "\\|\\(^\\)\\)[ \t\n]*"))) 230 "\\|\\(^\\)\\)[ \t\n]*")))
@@ -335,7 +340,7 @@
335 "<<\\(~\\)?[ \t]*\\('[^'\n]*'\\|\"[^\"\n]*\"\\|\\\\[[:alpha:]][[:alnum:]]*\\)" 340 "<<\\(~\\)?[ \t]*\\('[^'\n]*'\\|\"[^\"\n]*\"\\|\\\\[[:alpha:]][[:alnum:]]*\\)"
336 ;; The <<EOF case which needs perl--syntax-exp-intro-regexp, to 341 ;; The <<EOF case which needs perl--syntax-exp-intro-regexp, to
337 ;; disambiguate with the left-bitshift operator. 342 ;; disambiguate with the left-bitshift operator.
338 "\\|" perl--syntax-exp-intro-regexp "<<\\(?2:\\sw+\\)\\)" 343 "\\|" perl--syntax-exp-intro-regexp "<<\\(?1:~\\)?\\(?2:\\sw+\\)\\)"
339 ".*\\(\n\\)") 344 ".*\\(\n\\)")
340 (4 (let* ((eol (match-beginning 4)) 345 (4 (let* ((eol (match-beginning 4))
341 (st (get-text-property eol 'syntax-table)) 346 (st (get-text-property eol 'syntax-table))
diff --git a/lisp/subr.el b/lisp/subr.el
index 08dfe7aa430..e49c22158f9 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3270,7 +3270,14 @@ An obsolete, but still supported form is
3270where the optional arg MILLISECONDS specifies an additional wait period, 3270where the optional arg MILLISECONDS specifies an additional wait period,
3271in milliseconds; this was useful when Emacs was built without 3271in milliseconds; this was useful when Emacs was built without
3272floating point support." 3272floating point support."
3273 (declare (advertised-calling-convention (seconds &optional nodisp) "22.1")) 3273 (declare (advertised-calling-convention (seconds &optional nodisp) "22.1")
3274 (compiler-macro
3275 (lambda (form)
3276 (if (not (or (numberp nodisp) obsolete)) form
3277 (macroexp-warn-and-return
3278 "Obsolete calling convention for 'sit-for'"
3279 `(,(car form) (+ ,seconds (/ (or ,nodisp 0) 1000.0)) ,obsolete)
3280 '(obsolete sit-for))))))
3274 ;; This used to be implemented in C until the following discussion: 3281 ;; This used to be implemented in C until the following discussion:
3275 ;; https://lists.gnu.org/r/emacs-devel/2006-07/msg00401.html 3282 ;; https://lists.gnu.org/r/emacs-devel/2006-07/msg00401.html
3276 ;; Then it was moved here using an implementation based on an idle timer, 3283 ;; Then it was moved here using an implementation based on an idle timer,
diff --git a/src/window.c b/src/window.c
index 4e8b352e164..f116b9a9d72 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5441,12 +5441,13 @@ window_wants_mode_line (struct window *w)
5441 * Return 1 if window W wants a header line and is high enough to 5441 * Return 1 if window W wants a header line and is high enough to
5442 * accommodate it, 0 otherwise. 5442 * accommodate it, 0 otherwise.
5443 * 5443 *
5444 * W wants a header line if it's a leaf window and neither a minibuffer 5444 * W wants a header line if it's a leaf window and neither a
5445 * nor a pseudo window. Moreover, its 'window-mode-line-format' 5445 * minibuffer nor a pseudo window. Moreover, its
5446 * parameter must not be 'none' and either that parameter or W's 5446 * 'window-header-line-format' parameter must not be 'none' and either
5447 * buffer's 'mode-line-format' value must be non-nil. Finally, W must 5447 * that parameter or W's buffer's 'header-line-format' value must be
5448 * be higher than its frame's canonical character height and be able to 5448 * non-nil. Finally, W must be higher than its frame's canonical
5449 * accommodate a mode line too if necessary (the mode line prevails). 5449 * character height and be able to accommodate a mode line too if
5450 * necessary (the mode line prevails).
5450 */ 5451 */
5451bool 5452bool
5452window_wants_header_line (struct window *w) 5453window_wants_header_line (struct window *w)
@@ -5474,9 +5475,9 @@ window_wants_header_line (struct window *w)
5474 * accommodate it, 0 otherwise. 5475 * accommodate it, 0 otherwise.
5475 * 5476 *
5476 * W wants a tab line if it's a leaf window and neither a minibuffer 5477 * W wants a tab line if it's a leaf window and neither a minibuffer
5477 * nor a pseudo window. Moreover, its 'window-mode-line-format' 5478 * nor a pseudo window. Moreover, its 'window-tab-line-format'
5478 * parameter must not be 'none' and either that parameter or W's 5479 * parameter must not be 'none' and either that parameter or W's
5479 * buffer's 'mode-line-format' value must be non-nil. Finally, W must 5480 * buffer's 'tab-line-format' value must be non-nil. Finally, W must
5480 * be higher than its frame's canonical character height and be able 5481 * be higher than its frame's canonical character height and be able
5481 * to accommodate a mode line and a header line too if necessary (the 5482 * to accommodate a mode line and a header line too if necessary (the
5482 * mode line and a header line prevail). 5483 * mode line and a header line prevail).
diff --git a/src/xterm.c b/src/xterm.c
index 7c3ab87e87b..8b3d6f77a6c 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -17857,7 +17857,7 @@ x_handle_wm_state (struct frame *f, struct input_event *ie)
17857 17857
17858static bool 17858static bool
17859x_handle_selection_monitor_event (struct x_display_info *dpyinfo, 17859x_handle_selection_monitor_event (struct x_display_info *dpyinfo,
17860 XEvent *event) 17860 const XEvent *event)
17861{ 17861{
17862 XFixesSelectionNotifyEvent *notify; 17862 XFixesSelectionNotifyEvent *notify;
17863 int i; 17863 int i;
@@ -17940,7 +17940,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
17940 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpyinfo->display); 17940 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpyinfo->display);
17941#endif 17941#endif
17942 int dx, dy; 17942 int dx, dy;
17943
17944 /* Avoid warnings when SAFE_ALLOCA is not actually used. */
17945#if defined HAVE_XINPUT2 || defined HAVE_XKB || defined HAVE_X_I18N
17943 USE_SAFE_ALLOCA; 17946 USE_SAFE_ALLOCA;
17947#endif
17944 17948
17945 /* This function is not reentrant, so input should be blocked before 17949 /* This function is not reentrant, so input should be blocked before
17946 it is called. */ 17950 it is called. */
@@ -24220,7 +24224,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
24220 count++; 24224 count++;
24221 } 24225 }
24222 24226
24227#if defined HAVE_XINPUT2 || defined HAVE_XKB || defined HAVE_X_I18N
24223 SAFE_FREE (); 24228 SAFE_FREE ();
24229#endif
24230
24224 return count; 24231 return count;
24225} 24232}
24226 24233
@@ -25691,6 +25698,14 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
25691 25698
25692#ifdef HAVE_X11R6 25699#ifdef HAVE_X11R6
25693 25700
25701/* HAVE_X11R6 means Xlib conforms to the R6 specification or later.
25702 HAVE_X11R6_XIM, OTOH, means that Emacs should try to use R6-style
25703 callback driven input method initialization. They are separate
25704 because Sun apparently ships buggy Xlib with some versions of
25705 Solaris... */
25706
25707#ifdef HAVE_X11R6_XIM
25708
25694/* If preedit text is set on F, cancel preedit, free the text, and 25709/* If preedit text is set on F, cancel preedit, free the text, and
25695 generate the appropriate events to cancel the preedit display. 25710 generate the appropriate events to cancel the preedit display.
25696 25711
@@ -25756,6 +25771,8 @@ xim_destroy_callback (XIM xim, XPointer client_data, XPointer call_data)
25756 unblock_input (); 25771 unblock_input ();
25757} 25772}
25758 25773
25774#endif
25775
25759#endif /* HAVE_X11R6 */ 25776#endif /* HAVE_X11R6 */
25760 25777
25761/* Open the connection to the XIM server on display DPYINFO. 25778/* Open the connection to the XIM server on display DPYINFO.
@@ -27117,6 +27134,64 @@ xembed_request_focus (struct frame *f)
27117 XEMBED_REQUEST_FOCUS, 0, 0, 0); 27134 XEMBED_REQUEST_FOCUS, 0, 0, 0);
27118} 27135}
27119 27136
27137static Bool
27138server_timestamp_predicate (Display *display, XEvent *xevent,
27139 XPointer arg)
27140{
27141 XID *args = (XID *) arg;
27142
27143 if (xevent->type == PropertyNotify
27144 && xevent->xproperty.window == args[0]
27145 && xevent->xproperty.atom == args[1])
27146 return True;
27147
27148 return False;
27149}
27150
27151/* Get the server time. The X server is guaranteed to deliver the
27152 PropertyNotify event, so there is no reason to use x_if_event. */
27153
27154static Time
27155x_get_server_time (struct frame *f)
27156{
27157 Atom property_atom;
27158 XEvent property_dummy;
27159 struct x_display_info *dpyinfo;
27160 XID client_data[2];
27161#if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME
27162 uint_fast64_t current_monotonic_time;
27163#endif
27164
27165 /* If the server time is the same as the monotonic time, avoid a
27166 roundtrip by using that instead. */
27167
27168#if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME
27169 if (FRAME_DISPLAY_INFO (f)->server_time_monotonic_p)
27170 {
27171 current_monotonic_time = x_sync_current_monotonic_time ();
27172
27173 if (current_monotonic_time)
27174 /* Truncate the time to CARD32. */
27175 return (current_monotonic_time / 1000) & X_ULONG_MAX;
27176 }
27177#endif
27178
27179 dpyinfo = FRAME_DISPLAY_INFO (f);
27180 property_atom = dpyinfo->Xatom_EMACS_SERVER_TIME_PROP;
27181 client_data[0] = FRAME_OUTER_WINDOW (f);
27182 client_data[1] = property_atom;
27183
27184 XChangeProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f),
27185 property_atom, XA_ATOM, 32,
27186 PropModeReplace,
27187 (unsigned char *) &property_atom, 1);
27188
27189 XIfEvent (dpyinfo->display, &property_dummy,
27190 server_timestamp_predicate, (XPointer) client_data);
27191
27192 return property_dummy.xproperty.time;
27193}
27194
27120/* Activate frame with Extended Window Manager Hints */ 27195/* Activate frame with Extended Window Manager Hints */
27121 27196
27122static void 27197static void
@@ -27124,6 +27199,7 @@ x_ewmh_activate_frame (struct frame *f)
27124{ 27199{
27125 XEvent msg; 27200 XEvent msg;
27126 struct x_display_info *dpyinfo; 27201 struct x_display_info *dpyinfo;
27202 Time time;
27127 27203
27128 dpyinfo = FRAME_DISPLAY_INFO (f); 27204 dpyinfo = FRAME_DISPLAY_INFO (f);
27129 27205
@@ -27144,6 +27220,43 @@ x_ewmh_activate_frame (struct frame *f)
27144 msg.xclient.data.l[3] = 0; 27220 msg.xclient.data.l[3] = 0;
27145 msg.xclient.data.l[4] = 0; 27221 msg.xclient.data.l[4] = 0;
27146 27222
27223 /* No frame is currently focused on that display, so apply any
27224 bypass for focus stealing prevention that the user has
27225 specified. */
27226 if (!dpyinfo->x_focus_frame)
27227 {
27228 if (EQ (Vx_allow_focus_stealing, Qimitate_pager))
27229 msg.xclient.data.l[0] = 2;
27230 else if (EQ (Vx_allow_focus_stealing, Qnewer_time))
27231 {
27232 block_input ();
27233 time = x_get_server_time (f);
27234#ifdef USE_GTK
27235 x_set_gtk_user_time (f, time);
27236#endif
27237 /* Temporarily override dpyinfo->x_focus_frame so the
27238 user time property is set on the right window. */
27239 dpyinfo->x_focus_frame = f;
27240 x_display_set_last_user_time (dpyinfo, time, true, true);
27241 dpyinfo->x_focus_frame = NULL;
27242 unblock_input ();
27243
27244 msg.xclient.data.l[1] = time;
27245 }
27246 else if (EQ (Vx_allow_focus_stealing, Qraise_and_focus))
27247 {
27248 time = x_get_server_time (f);
27249
27250 x_ignore_errors_for_next_request (dpyinfo);
27251 XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
27252 RevertToParent, time);
27253 XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
27254 x_stop_ignoring_errors (dpyinfo);
27255
27256 return;
27257 }
27258 }
27259
27147 XSendEvent (dpyinfo->display, dpyinfo->root_window, 27260 XSendEvent (dpyinfo->display, dpyinfo->root_window,
27148 False, (SubstructureRedirectMask 27261 False, (SubstructureRedirectMask
27149 | SubstructureNotifyMask), &msg); 27262 | SubstructureNotifyMask), &msg);
@@ -30281,7 +30394,7 @@ mark_xterm (void)
30281{ 30394{
30282 Lisp_Object val; 30395 Lisp_Object val;
30283#if defined HAVE_XINPUT2 || defined USE_TOOLKIT_SCROLL_BARS \ 30396#if defined HAVE_XINPUT2 || defined USE_TOOLKIT_SCROLL_BARS \
30284 || defined HAVE_XRANDR || defined USE_GTK 30397 || defined HAVE_XRANDR || defined USE_GTK || defined HAVE_X_I18N
30285 struct x_display_info *dpyinfo; 30398 struct x_display_info *dpyinfo;
30286#if defined HAVE_XINPUT2 || defined USE_TOOLKIT_SCROLL_BARS 30399#if defined HAVE_XINPUT2 || defined USE_TOOLKIT_SCROLL_BARS
30287 int i; 30400 int i;
@@ -30505,8 +30618,14 @@ x_get_keyboard_modifiers (struct x_display_info *dpyinfo)
30505 /* This sometimes happens when the function is called during display 30618 /* This sometimes happens when the function is called during display
30506 initialization, which can happen while obtaining vendor specific 30619 initialization, which can happen while obtaining vendor specific
30507 keysyms. */ 30620 keysyms. */
30621
30622#ifdef HAVE_XKB
30508 if (!dpyinfo->xkb_desc && !dpyinfo->modmap) 30623 if (!dpyinfo->xkb_desc && !dpyinfo->modmap)
30509 x_find_modifier_meanings (dpyinfo); 30624 x_find_modifier_meanings (dpyinfo);
30625#else
30626 if (!dpyinfo->modmap)
30627 x_find_modifier_meanings (dpyinfo);
30628#endif
30510 30629
30511 return list5 (make_uint (dpyinfo->hyper_mod_mask), 30630 return list5 (make_uint (dpyinfo->hyper_mod_mask),
30512 make_uint (dpyinfo->super_mod_mask), 30631 make_uint (dpyinfo->super_mod_mask),
@@ -30626,6 +30745,9 @@ With MS Windows, Haiku windowing or Nextstep, the value is t. */);
30626 Fput (Qsuper, Qmodifier_value, make_fixnum (super_modifier)); 30745 Fput (Qsuper, Qmodifier_value, make_fixnum (super_modifier));
30627 DEFSYM (QXdndSelection, "XdndSelection"); 30746 DEFSYM (QXdndSelection, "XdndSelection");
30628 DEFSYM (Qx_selection_alias_alist, "x-selection-alias-alist"); 30747 DEFSYM (Qx_selection_alias_alist, "x-selection-alias-alist");
30748 DEFSYM (Qimitate_pager, "imitate-pager");
30749 DEFSYM (Qnewer_time, "newer-time");
30750 DEFSYM (Qraise_and_focus, "raise-and-focus");
30629 30751
30630 DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym, 30752 DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym,
30631 doc: /* Which keys Emacs uses for the ctrl modifier. 30753 doc: /* Which keys Emacs uses for the ctrl modifier.
@@ -30879,4 +31001,24 @@ connection setup. */);
30879 /* The default value of this variable is chosen so that updating the 31001 /* The default value of this variable is chosen so that updating the
30880 tool bar does not require a call to _XReply. */ 31002 tool bar does not require a call to _XReply. */
30881 Vx_fast_selection_list = list1 (QCLIPBOARD); 31003 Vx_fast_selection_list = list1 (QCLIPBOARD);
31004
31005 DEFVAR_LISP ("x-allow-focus-stealing", Vx_allow_focus_stealing,
31006 doc: /* How to bypass window manager focus stealing prevention.
31007
31008Some window managers prevent `x-focus-frame' from activating the given
31009frame when Emacs is in the background, which is especially prone to
31010cause problems when the Emacs server wants to activate itself. This
31011variable specifies the strategy used to activate frames when that is
31012the case, and has several valid values (any other value means to not
31013bypass window manager focus stealing prevention):
31014
31015 - The symbol `imitate-pager', which means to pretend that Emacs is a
31016 pager.
31017
31018 - The symbol `newer-time', which means to fetch the current time
31019 from the X server and use it to activate the frame.
31020
31021 - The symbol `raise-and-focus', which means to raise the window and
31022 focus it manually. */);
31023 Vx_allow_focus_stealing = Qnewer_time;
30882} 31024}
diff --git a/test/lisp/image/wallpaper-tests.el b/test/lisp/image/wallpaper-tests.el
index cb6818f8c1b..a5d3343bd4d 100644
--- a/test/lisp/image/wallpaper-tests.el
+++ b/test/lisp/image/wallpaper-tests.el
@@ -99,9 +99,12 @@
99 ("touch" "touch" fil 99 ("touch" "touch" fil
100 :init-action (lambda () (setq called t))))) 100 :init-action (lambda () (setq called t)))))
101 (wallpaper-command (wallpaper--find-command)) 101 (wallpaper-command (wallpaper--find-command))
102 (wallpaper-command-args (wallpaper--find-command-args))) 102 (wallpaper-command-args (wallpaper--find-command-args))
103 process)
103 (should (functionp (wallpaper-setter-init-action wallpaper--current-setter))) 104 (should (functionp (wallpaper-setter-init-action wallpaper--current-setter)))
104 (wallpaper-set fil-jpg) 105 (setq process (wallpaper-set fil-jpg))
106 ;; Wait for "touch" process to exit so temp file is removed.
107 (accept-process-output process 3)
105 (should called))))) 108 (should called)))))
106 109
107(ert-deftest wallpaper-set/calls-wallpaper-set-function () 110(ert-deftest wallpaper-set/calls-wallpaper-set-function ()
diff --git a/test/lisp/progmodes/cperl-mode-resources/here-docs.pl b/test/lisp/progmodes/cperl-mode-resources/here-docs.pl
index bb3d4871a91..13d879bf762 100644
--- a/test/lisp/progmodes/cperl-mode-resources/here-docs.pl
+++ b/test/lisp/progmodes/cperl-mode-resources/here-docs.pl
@@ -140,4 +140,70 @@ HERE
140 140
141. 'indent-level'; # Continuation, should be indented 141. 'indent-level'; # Continuation, should be indented
142 142
143=head2 Test case 7
144
145An indented HERE document using a bare identifier.
146
147=cut
148
149## test case
150
151$text = <<~HERE;
152look-here
153HERE
154
155$noindent = "New statement in this line";
156
157=head2 Test case 8
158
159A HERE document as an argument to print when printing to a filehandle.
160
161=cut
162
163## test case
164
165print $fh <<~HERE;
166look-here
167HERE
168
169$noindent = "New statement in this line";
170
171=head2 Test case 9
172
173A HERE document as a hash value.
174
175=cut
176
177my %foo = (
178 text => <<~HERE
179look-here
180HERE
181 );
182
183$noindent = "New statement in this line";
184
185=head2 Test case 10
186
187A HERE document as an argument to die.
188
189=cut
190
1911 or die <<HERE;
192look-here
193HERE
194
195$noindent = "New statement in this line";
196
197=head2 Test case 11
198
199A HERE document as an argument to warn.
200
201=cut
202
2031 or warn <<HERE;
204look-here
205HERE
206
207$noindent = "New statement in this line";
208
143__END__ 209__END__