mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-08 07:20:28 -08:00
Merge from origin/emacs-29
db96b1282f* lisp/help.el: Use 'C-h C-q' to toggle 'help-quick' wind...489865c21e; Improve markup of long key sequencesd42c2668cf; * etc/NEWS: Fix wording of a recently edited entry.7a0eaee198* lisp/isearch.el: Small fixes.b69bffeec0* lisp/vc/diff-mode.el (diff-minor-mode-prefix): Replace ...9263847ab7; * etc/NEWS: Move the paragraph with 'C-u RET' closer to...62fb2dc37d* doc/emacs/display.texi (Text Scale): Improve section ab...70480d3b6b* lisp/repeat.el (repeat-echo-function): Suggest 'add-fun...fd48201ffe* lisp/tab-line.el (tab-line-cache-key-default): More cac...b164660260* etc/package-keyring.gpg: Update with new keyc0be51389e; Yet another declare-function to avoid treesit-related w...8676bec51d; * lisp/treesit.el (treesit--simple-imenu-1): Doc fix; w...2ddc480f44Warn of absent networks module in ERC19d00fab9aAvoid "already compiled" warning in erc-compat2d8f7b66bc; Fix one more treesit byte-compilation warning.2d0a921486; Avoid treesit-related byte-compiler warnings8503b370be(python--treesit-settings): Remove duplicate matcherb464e6c490Make last change of w32 GUI dialogs conditional and rever...eedc9d79aeFix tree-sitter typos248c13dcfeUpdate tree-sitter major modes to use the new Imenu facilityb39dc7ab27Add tree-sitter helper functions for Imenuba1ddea9daFix treesit--things-around (bug#60355)7512b9025a; * lisp/treesit.el (treesit-traverse-parent): Remove alias.5326b04198Improve treesit-node-top-level and treesit-parent-until637f5b164f; Add "src" to the heuristic sub-directory heuristic8ab6df0c9f; * lisp/epa-ks.el (epa-ks-do-key-to-fetch): Fix 'when' u...2b55a48d3e* src/w32menu.c (simple_dialog_show): Use MB_YESNOCANCEL ...8b8b791567; Improve documentation of TAB/SPC indentation624e382211; Improve doc strings of some new faces41f12e1019; * lisp/elide-head.el (elide-head): Doc fix to silence c...e3b4cd0ac1; * lisp/htmlfontify.el (hfy-text-p): Fix whitespace.1b4dc4691cFix htmlfontify.el command injection vulnerability.1fe4b98b4dImprove support for Scheme R6RS and R7RS libraries (bug#5...2347f37f67; * test/src/treesit-tests.el: remove dead store (bytecom...a6d961ae2fAdd a new tree-sitter query predicate 'pred'835a80dcc4; Fix tree-sitter defun testsa14821d615Improve gnutls-min-prime-bits docstringb14bbd108eImprove handling of tab-bar height.669160d47b; * nt/INSTALL.W64: More fixes and updates.26b2ec7cb8Simplify last change (bug#60311)082fc6e308Fix 'json-available-p' on MS-Windows6c86faec29loaddefs-gen: Group results by absolute file named90d7d15f2; Fix vindexes in parsing.texieb26872837Fix imenu for c-ts-mode (bug#60296)8f68b6497eClean up python-ts-mode font-lock features28f26b11a1Add comment indent and filling to other tree-sitter major...c6b0282645; Remove unused function in c-ts-mode6e52a9fcad; * doc/lispref/modes.texi (Parser-based Font Lock): Mino...2bcd1e9a99; * doc/lispref/parsing.texi (Retrieving Nodes): Add notice.7c7950fe00Add maintainer stub for tree-sitter filescf32776622; * doc/lispref/parsing.texi (Using Parser): Remove delet... # Conflicts: # etc/NEWS # lisp/progmodes/c-ts-mode.el # lisp/progmodes/typescript-ts-mode.el # lisp/treesit.el
This commit is contained in:
commit
dce6791e99
50 changed files with 945 additions and 813 deletions
|
|
@ -164,10 +164,11 @@ mode hooks."
|
|||
(defun elide-head (&optional arg)
|
||||
"Hide header material in buffer according to `elide-head-headers-to-hide'.
|
||||
|
||||
The header is made invisible with an overlay. With a prefix arg, show
|
||||
an elided material again.
|
||||
The header is made invisible with an overlay. With a prefix
|
||||
argument ARG, show an elided material again.
|
||||
|
||||
This is suitable as an entry on `find-file-hook' or appropriate mode hooks."
|
||||
This is suitable as an entry on `find-file-hook' or appropriate
|
||||
mode hooks."
|
||||
(declare (obsolete elide-head-mode "29.1"))
|
||||
(interactive "P")
|
||||
(if arg
|
||||
|
|
|
|||
|
|
@ -608,7 +608,8 @@ instead of just updating them with the new/changed autoloads."
|
|||
(write-region (point-min) (point-max) output-file nil 'silent))
|
||||
;; We have some data, so generate the loaddef files. First
|
||||
;; group per output file.
|
||||
(dolist (fdefs (seq-group-by #'car defs))
|
||||
(dolist (fdefs (seq-group-by (lambda (x) (expand-file-name (car x)))
|
||||
defs))
|
||||
(let ((loaddefs-file (car fdefs))
|
||||
hash)
|
||||
(with-temp-buffer
|
||||
|
|
|
|||
|
|
@ -613,18 +613,21 @@ checkout. This overrides the `:branch' attribute in PKG-SPEC."
|
|||
|
||||
;; When nothing is specified about a `lisp-dir', then should
|
||||
;; heuristically check if there is a sub-directory with lisp
|
||||
;; files. These are conventionally just called "lisp". If this
|
||||
;; directory exists and contains non-zero number of lisp files, we
|
||||
;; will use that instead of `pkg-dir'.
|
||||
(when-let* (((null lisp-dir))
|
||||
(dir (expand-file-name "lisp" pkg-dir))
|
||||
((file-directory-p dir))
|
||||
((directory-files dir nil "\\`[^.].+\\.el\\'" t 1)))
|
||||
;; We won't use `dir', since dir is an absolute path and we
|
||||
;; don't want `lisp-dir' to depend on the current location of
|
||||
;; the package installation, ie. to break if moved around the
|
||||
;; file system or between installations.
|
||||
(setq lisp-dir "lisp"))
|
||||
;; files. These are conventionally just called "lisp" or "src".
|
||||
;; If this directory exists and contains non-zero number of lisp
|
||||
;; files, we will use that instead of `pkg-dir'.
|
||||
(catch 'done
|
||||
(dolist (name '("lisp" "src"))
|
||||
(when-let* (((null lisp-dir))
|
||||
(dir (expand-file-name name pkg-dir))
|
||||
((file-directory-p dir))
|
||||
((directory-files dir nil "\\`[^.].+\\.el\\'" t 1)))
|
||||
;; We won't use `dir', since dir is an absolute path and we
|
||||
;; don't want `lisp-dir' to depend on the current location of
|
||||
;; the package installation, ie. to break if moved around the
|
||||
;; file system or between installations.
|
||||
(throw 'done (setq lisp-dir name)))))
|
||||
|
||||
(when lisp-dir
|
||||
(push (cons :lisp-dir lisp-dir)
|
||||
(package-desc-extras pkg-desc)))
|
||||
|
|
|
|||
|
|
@ -135,9 +135,9 @@ Keys are marked using `epa-ks-mark-key-to-fetch'."
|
|||
keys))
|
||||
(forward-line))
|
||||
(when (yes-or-no-p (format "Proceed with fetching all %d key(s)? "
|
||||
(length keys))))
|
||||
(dolist (id keys)
|
||||
(epa-ks--fetch-key id))))
|
||||
(length keys)))
|
||||
(dolist (id keys)
|
||||
(epa-ks--fetch-key id)))))
|
||||
(tabulated-list-clear-all-tags))
|
||||
|
||||
(defun epa-ks--query-url (query exact)
|
||||
|
|
|
|||
|
|
@ -320,6 +320,15 @@ session when reconnecting. Once `erc-reuse-buffers' is retired
|
|||
and fully removed, modules can switch to leveraging the
|
||||
`permanent-local' property instead.")
|
||||
|
||||
(defvar erc--server-post-connect-hook '(erc-networks--warn-on-connect)
|
||||
"Functions to run when a network connection is successfully opened.
|
||||
Though internal, this complements `erc-connect-pre-hook' in that
|
||||
it bookends the process rather than the logical connection, which
|
||||
is the domain of `erc-before-connect' and `erc-after-connect'.
|
||||
Note that unlike `erc-connect-pre-hook', this only runs in server
|
||||
buffers, and it does so immediately before the first protocol
|
||||
exchange.")
|
||||
|
||||
(defvar-local erc-server-timed-out nil
|
||||
"Non-nil if the IRC server failed to respond to a ping.")
|
||||
|
||||
|
|
@ -646,6 +655,7 @@ The current buffer is given by BUFFER."
|
|||
|
||||
(cl-defmethod erc--register-connection ()
|
||||
"Perform opening IRC protocol exchange with server."
|
||||
(run-hooks 'erc--server-post-connect-hook)
|
||||
(erc-login))
|
||||
|
||||
(defvar erc--server-connect-dumb-ipv6-regexp
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ If START or END is negative, it counts from the end."
|
|||
(when-let* ((s (plist-get e :secret))
|
||||
(v (auth-source--obfuscate s)))
|
||||
(setf (plist-get e :secret)
|
||||
(byte-compile (lambda () (auth-source--deobfuscate v)))))
|
||||
(apply-partially #'auth-source--deobfuscate v)))
|
||||
(push e out)))
|
||||
rv)))
|
||||
|
||||
|
|
|
|||
|
|
@ -1472,14 +1472,16 @@ to be a false alarm. If `erc-reuse-buffers' is nil, let
|
|||
(t (rename-buffer (generate-new-buffer-name name)))))
|
||||
nil)
|
||||
|
||||
;; Soju v0.4.0 only sends ISUPPORT on upstream reconnect, so this
|
||||
;; doesn't apply. ZNC 1.8.2, however, still sends the entire burst.
|
||||
(defconst erc-networks--bouncer-targets '(*status bouncerserv)
|
||||
"Case-mapped symbols matching known bouncer service-bot targets.")
|
||||
;; Soju v0.4.0 sends ISUPPORT and nothing else on upstream reconnect,
|
||||
;; so this actually doesn't apply. ZNC 1.8.2, however, still sends
|
||||
;; the entire burst.
|
||||
(defvar erc-networks--bouncer-targets '(*status bouncerserv)
|
||||
"Symbols matching proxy-bot targets.")
|
||||
|
||||
(defun erc-networks-on-MOTD-end (proc parsed)
|
||||
"Call on-connect functions with server PROC and PARSED message.
|
||||
This must run before `erc-server-connected' is set."
|
||||
"Call on-connect functions with server PROC and PARSED message."
|
||||
;; This should normally run before `erc-server-connected' is set.
|
||||
;; However, bouncers and other proxies may interfere with that.
|
||||
(when erc-server-connected
|
||||
(unless (erc-buffer-filter (lambda ()
|
||||
(and erc--target
|
||||
|
|
@ -1502,6 +1504,18 @@ This must run before `erc-server-connected' is set."
|
|||
((remove-hook 'erc-server-376-functions #'erc-networks-on-MOTD-end)
|
||||
(remove-hook 'erc-server-422-functions #'erc-networks-on-MOTD-end)))
|
||||
|
||||
(defun erc-networks--warn-on-connect ()
|
||||
"Emit warning when the `networks' module hasn't been loaded.
|
||||
Ideally, do so upon opening the network process."
|
||||
(unless (or erc--target erc-networks-mode)
|
||||
(require 'info nil t)
|
||||
(let ((m (concat "Required module `networks' not loaded. If this "
|
||||
" was unexpected, please add it to `erc-modules'.")))
|
||||
;; Assume the server buffer has been marked as active.
|
||||
(erc-display-error-notice
|
||||
nil (concat m " See Info:\"(erc) Required Modules\" for more."))
|
||||
(lwarn 'erc :warning m))))
|
||||
|
||||
(defun erc-ports-list (ports)
|
||||
"Return a list of PORTS.
|
||||
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ Otherwise, expect it to disappear in subsequent versions.")
|
|||
(if (eq :user (alist-get 'user erc-sasl--options))
|
||||
(erc-current-nick)
|
||||
erc-session-username)))
|
||||
(erc-login))
|
||||
(cl-call-next-method))
|
||||
(when erc-sasl--send-cap-ls
|
||||
(erc-server-send "CAP REQ :sasl"))
|
||||
(erc-server-send (format "AUTHENTICATE %s" m)))
|
||||
|
|
|
|||
|
|
@ -1607,7 +1607,8 @@ same manner."
|
|||
(when target ; compat
|
||||
(setq tgt-info (erc--target-from-string target)))
|
||||
(if tgt-info
|
||||
(let* ((esid (erc-networks--id-symbol erc-networks--id))
|
||||
(let* ((esid (and erc-networks--id
|
||||
(erc-networks--id-symbol erc-networks--id)))
|
||||
(name (if esid
|
||||
(erc-networks--reconcile-buffer-names tgt-info
|
||||
erc-networks--id)
|
||||
|
|
@ -6760,7 +6761,8 @@ This should be a string with substitution variables recognized by
|
|||
If the name of the network is not available, then use the
|
||||
shortened server name instead."
|
||||
(if-let ((erc--target)
|
||||
(name (if-let ((esid (erc-networks--id-symbol erc-networks--id)))
|
||||
(name (if-let ((erc-networks--id)
|
||||
(esid (erc-networks--id-symbol erc-networks--id)))
|
||||
(symbol-name esid)
|
||||
(erc-shorten-server-name (or erc-server-announced-name
|
||||
erc-session-server)))))
|
||||
|
|
|
|||
|
|
@ -2850,7 +2850,7 @@ since only a single case-insensitive search through the alist is made."
|
|||
("\\.emacs-places\\'" . lisp-data-mode)
|
||||
("\\.el\\'" . emacs-lisp-mode)
|
||||
("Project\\.ede\\'" . emacs-lisp-mode)
|
||||
("\\.\\(scm\\|stk\\|ss\\|sch\\)\\'" . scheme-mode)
|
||||
("\\.\\(scm\\|sls\\|sld\\|stk\\|ss\\|sch\\)\\'" . scheme-mode)
|
||||
("\\.l\\'" . lisp-mode)
|
||||
("\\.li?sp\\'" . lisp-mode)
|
||||
("\\.[fF]\\'" . fortran-mode)
|
||||
|
|
|
|||
|
|
@ -2110,7 +2110,7 @@ For example, the declaration and use of fields in a struct."
|
|||
|
||||
(defface font-lock-punctuation-face
|
||||
'((t nil))
|
||||
"Font Lock mode face used to highlight punctuation."
|
||||
"Font Lock mode face used to highlight punctuation characters."
|
||||
:group 'font-lock-faces
|
||||
:version "29.1")
|
||||
|
||||
|
|
@ -2122,7 +2122,9 @@ For example, the declaration and use of fields in a struct."
|
|||
|
||||
(defface font-lock-delimiter-face
|
||||
'((t :inherit font-lock-punctuation-face))
|
||||
"Font Lock mode face used to highlight delimiters."
|
||||
"Font Lock mode face used to highlight delimiters.
|
||||
What exactly is a delimiter depends on the major mode, but usually
|
||||
these are characters like comma, colon, and semi-colon."
|
||||
:group 'font-lock-faces
|
||||
:version "29.1")
|
||||
|
||||
|
|
|
|||
32
lisp/help.el
32
lisp/help.el
|
|
@ -76,6 +76,7 @@ buffer.")
|
|||
"C-n" #'view-emacs-news
|
||||
"C-o" #'describe-distribution
|
||||
"C-p" #'view-emacs-problems
|
||||
"C-q" #'help-quick-toggle
|
||||
"C-s" #'search-forward-help-for-help
|
||||
"C-t" #'view-emacs-todo
|
||||
"C-w" #'describe-no-warranty
|
||||
|
|
@ -116,7 +117,7 @@ buffer.")
|
|||
"v" #'describe-variable
|
||||
"w" #'where-is
|
||||
"x" #'describe-command
|
||||
"q" #'help-quit-or-quick)
|
||||
"q" #'help-quit)
|
||||
|
||||
(define-key global-map (char-to-string help-char) 'help-command)
|
||||
(define-key global-map [help] 'help-command)
|
||||
|
|
@ -243,7 +244,17 @@ buffer.")
|
|||
;; ... and shrink it immediately.
|
||||
(fit-window-to-buffer))
|
||||
(message
|
||||
(substitute-command-keys "Toggle the quick help buffer using \\[help-quit-or-quick]."))))
|
||||
(substitute-command-keys "Toggle the quick help buffer using \\[help-quick-toggle]."))))
|
||||
|
||||
(defun help-quick-toggle ()
|
||||
"Toggle the quick-help window."
|
||||
(interactive)
|
||||
(if (and-let* ((window (get-buffer-window "*Quick Help*")))
|
||||
(quit-window t window))
|
||||
;; Clear the message we may have gotten from `C-h' and then
|
||||
;; waiting before hitting `q'.
|
||||
(message "")
|
||||
(help-quick)))
|
||||
|
||||
(defalias 'cheat-sheet #'help-quick)
|
||||
|
||||
|
|
@ -252,21 +263,6 @@ buffer.")
|
|||
(interactive)
|
||||
nil)
|
||||
|
||||
(defun help-quit-or-quick ()
|
||||
"Call `help-quit' or `help-quick' depending on the context."
|
||||
(interactive)
|
||||
(cond
|
||||
(help-buffer-under-preparation
|
||||
;; FIXME: There should be a better way to detect if we are in the
|
||||
;; help command loop.
|
||||
(help-quit))
|
||||
((and-let* ((window (get-buffer-window "*Quick Help*")))
|
||||
(quit-window t window)
|
||||
;; Clear the message we may have gotten from `C-h' and then
|
||||
;; waiting before hitting `q'.
|
||||
(message "")))
|
||||
((help-quick))))
|
||||
|
||||
(defvar help-return-method nil
|
||||
"What to do to \"exit\" the help buffer.
|
||||
This is a list
|
||||
|
|
@ -416,7 +412,7 @@ Do not call this in the scope of `with-help-window'."
|
|||
("describe-package" "Describe a specific Emacs package")
|
||||
""
|
||||
("help-with-tutorial" "Start the Emacs tutorial")
|
||||
("help-quick-or-quit" "Display the quick help buffer.")
|
||||
("help-quick-toggle" "Display the quick help buffer.")
|
||||
("view-echo-area-messages"
|
||||
"Show recent messages (from echo area)")
|
||||
("view-lossage" ,(format "Show last %d input keystrokes (lossage)"
|
||||
|
|
|
|||
|
|
@ -1850,8 +1850,9 @@ Hardly bombproof, but good enough in the context in which it is being used."
|
|||
|
||||
(defun hfy-text-p (srcdir file)
|
||||
"Is SRCDIR/FILE text? Use `hfy-istext-command' to determine this."
|
||||
(let* ((cmd (format hfy-istext-command (expand-file-name file srcdir)))
|
||||
(rsp (shell-command-to-string cmd)))
|
||||
(let* ((cmd (format hfy-istext-command
|
||||
(shell-quote-argument (expand-file-name file srcdir))))
|
||||
(rsp (shell-command-to-string cmd)))
|
||||
(string-match "text" rsp)))
|
||||
|
||||
;; open a file, check fontification, if fontified, write a fontified copy
|
||||
|
|
|
|||
|
|
@ -784,7 +784,8 @@ If PREV is non-nil, return the previous one instead."
|
|||
(defun tab-to-tab-stop ()
|
||||
"Insert spaces or tabs to next defined tab-stop column.
|
||||
The variable `tab-stop-list' is a list of columns at which there are tab stops.
|
||||
Use \\[edit-tab-stops] to edit them interactively."
|
||||
Use \\[edit-tab-stops] to edit them interactively.
|
||||
Whether this inserts tabs or spaces depends on `indent-tabs-mode'."
|
||||
(interactive)
|
||||
(and abbrev-mode (= (char-syntax (preceding-char)) ?w)
|
||||
(expand-abbrev))
|
||||
|
|
|
|||
|
|
@ -181,7 +181,9 @@ When t (by default), signal an error when no more matches are found.
|
|||
Then after repeating the search, wrap with `isearch-wrap-function'.
|
||||
When `no', wrap immediately after reaching the last match.
|
||||
When `no-ding', wrap immediately without flashing the screen.
|
||||
When nil, never wrap, just stop at the last match."
|
||||
When nil, never wrap, just stop at the last match.
|
||||
With the values `no' and `no-ding' the search will try
|
||||
to wrap around also on typing a character."
|
||||
:type '(choice (const :tag "Pause before wrapping" t)
|
||||
(const :tag "No pause before wrapping" no)
|
||||
(const :tag "No pause and no flashing" no-ding)
|
||||
|
|
@ -880,6 +882,7 @@ matches literally, against one space. You can toggle the value of this
|
|||
variable by the command `isearch-toggle-lax-whitespace', usually bound to
|
||||
`M-s SPC' during isearch."
|
||||
:type 'boolean
|
||||
:group 'isearch
|
||||
:version "25.1")
|
||||
|
||||
(defvar isearch-regexp-lax-whitespace nil
|
||||
|
|
@ -1179,6 +1182,7 @@ Each element of the list should be one of the symbols supported by
|
|||
`isearch-forward-thing-at-point' to yank the initial \"thing\"
|
||||
as text to the search string."
|
||||
:type '(repeat (symbol :tag "Thing symbol"))
|
||||
:group 'isearch
|
||||
:version "28.1")
|
||||
|
||||
(defun isearch-forward-thing-at-point ()
|
||||
|
|
@ -2525,10 +2529,11 @@ If no input items have been entered yet, just beep."
|
|||
(ding)
|
||||
(isearch-pop-state))
|
||||
;; When going back to the hidden match, reopen it and close other overlays.
|
||||
(when (and (eq search-invisible 'open) isearch-hide-immediately)
|
||||
(when (and (eq isearch-invisible 'open) isearch-hide-immediately)
|
||||
(if isearch-other-end
|
||||
(isearch-range-invisible (min (point) isearch-other-end)
|
||||
(max (point) isearch-other-end))
|
||||
(let ((search-invisible isearch-invisible))
|
||||
(isearch-range-invisible (min (point) isearch-other-end)
|
||||
(max (point) isearch-other-end)))
|
||||
(isearch-close-unnecessary-overlays (point) (point))))
|
||||
(isearch-update))
|
||||
|
||||
|
|
|
|||
|
|
@ -128,10 +128,7 @@ key exchange is against man-in-the-middle attacks.)
|
|||
|
||||
A value of nil says to use the default GnuTLS value.
|
||||
|
||||
The default value of this variable is such that virtually any
|
||||
connection can be established, whether this connection can be
|
||||
considered cryptographically \"safe\" or not. However, Emacs
|
||||
network security is handled at a higher level via
|
||||
Emacs network security is handled at a higher level via
|
||||
`open-network-stream' and the Network Security Manager. See Info
|
||||
node `(emacs) Network Security'."
|
||||
:type '(choice (const :tag "Use default value" nil)
|
||||
|
|
|
|||
|
|
@ -487,92 +487,44 @@ For NODE, OVERRIDE, START, and END, see
|
|||
|
||||
(defun c-ts-mode--defun-name (node)
|
||||
"Return the name of the defun NODE.
|
||||
Return nil if NODE is not a defun node, return an empty string if
|
||||
NODE doesn't have a name."
|
||||
Return nil if NODE is not a defun node or doesn't have a name."
|
||||
(treesit-node-text
|
||||
(pcase (treesit-node-type node)
|
||||
((or "function_definition" "declaration")
|
||||
(c-ts-mode--declarator-identifier
|
||||
(treesit-node-child-by-field-name node "declarator")))
|
||||
("struct_specifier"
|
||||
((or "struct_specifier" "enum_specifier"
|
||||
"union_specifier" "class_specifier")
|
||||
(treesit-node-child-by-field-name node "name")))
|
||||
t))
|
||||
|
||||
(defun c-ts-mode--imenu-1 (node)
|
||||
"Helper for `c-ts-mode--imenu'.
|
||||
Find string representation for NODE and set marker, then recurse
|
||||
the subtrees."
|
||||
(let* ((ts-node (car node))
|
||||
(subtrees (mapcan #'c-ts-mode--imenu-1 (cdr node)))
|
||||
(name (when ts-node
|
||||
(treesit-defun-name ts-node)))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
;; A struct_specifier could be inside a parameter list, another
|
||||
;; struct definition, a variable declaration, a function
|
||||
;; declaration. In those cases we don't include it.
|
||||
((string-match-p
|
||||
(rx (or "parameter_declaration" "field_declaration"
|
||||
"declaration" "function_definition"))
|
||||
(or (treesit-node-type (treesit-node-parent ts-node))
|
||||
""))
|
||||
nil)
|
||||
;; Ignore function local variable declarations.
|
||||
((and (equal (treesit-node-type ts-node) "declaration")
|
||||
(not (equal (treesit-node-type (treesit-node-parent ts-node))
|
||||
"translation_unit")))
|
||||
nil)
|
||||
((or (null ts-node) (null name)) subtrees)
|
||||
(subtrees
|
||||
`((,name ,(cons name marker) ,@subtrees)))
|
||||
(t
|
||||
`((,name . ,marker))))))
|
||||
|
||||
(defun c-ts-mode--imenu ()
|
||||
"Return Imenu alist for the current buffer."
|
||||
(let* ((node (treesit-buffer-root-node))
|
||||
(func-tree (treesit-induce-sparse-tree
|
||||
node "^function_definition$" nil 1000))
|
||||
(var-tree (treesit-induce-sparse-tree
|
||||
node "^declaration$" nil 1000))
|
||||
(struct-tree (treesit-induce-sparse-tree
|
||||
node "^struct_specifier$" nil 1000))
|
||||
(func-index (c-ts-mode--imenu-1 func-tree))
|
||||
(var-index (c-ts-mode--imenu-1 var-tree))
|
||||
(struct-index (c-ts-mode--imenu-1 struct-tree)))
|
||||
(append
|
||||
(when struct-index `(("Struct" . ,struct-index)))
|
||||
(when var-index `(("Variable" . ,var-index)))
|
||||
(when func-index `(("Function" . ,func-index))))))
|
||||
|
||||
;;; Defun navigation
|
||||
|
||||
(defun c-ts-mode--end-of-defun ()
|
||||
"`end-of-defun-function' of `c-ts-mode'."
|
||||
;; A struct/enum/union_specifier node doesn't include the ; at the
|
||||
;; end, so we manually skip it.
|
||||
(treesit-end-of-defun)
|
||||
(when (looking-at (rx (* " ") ";"))
|
||||
(goto-char (match-end 0))
|
||||
;; This part is copied from `end-of-defun'.
|
||||
(unless (bolp)
|
||||
(skip-chars-forward " \t")
|
||||
(if (looking-at "\\s<\\|\n")
|
||||
(forward-line 1)))))
|
||||
|
||||
(defun c-ts-mode--defun-valid-p (node)
|
||||
(if (string-match-p
|
||||
(rx (or "struct_specifier"
|
||||
"enum_specifier"
|
||||
"union_specifier"))
|
||||
(treesit-node-type node))
|
||||
(null
|
||||
(treesit-node-top-level
|
||||
node (rx (or "function_definition"
|
||||
"type_definition"))))
|
||||
t))
|
||||
"Return non-nil if NODE is a valid defun node.
|
||||
Ie, NODE is not nested."
|
||||
(not (or (and (member (treesit-node-type node)
|
||||
'("struct_specifier"
|
||||
"enum_specifier"
|
||||
"union_specifier"
|
||||
"declaration"))
|
||||
;; If NODE's type is one of the above, make sure it is
|
||||
;; top-level.
|
||||
(treesit-node-top-level
|
||||
node (rx (or "function_definition"
|
||||
"type_definition"
|
||||
"struct_specifier"
|
||||
"enum_specifier"
|
||||
"union_specifier"
|
||||
"declaration"))))
|
||||
|
||||
(and (equal (treesit-node-type node) "declaration")
|
||||
;; If NODE is a declaration, make sure it is not a
|
||||
;; function declaration.
|
||||
(equal (treesit-node-type
|
||||
(treesit-node-child-by-field-name
|
||||
node "declarator"))
|
||||
"function_declarator")))))
|
||||
|
||||
(defun c-ts-mode--defun-skipper ()
|
||||
"Custom defun skipper for `c-ts-mode' and friends.
|
||||
|
|
@ -660,6 +612,59 @@ ARG is passed to `fill-paragraph'."
|
|||
;; itself.
|
||||
t)))
|
||||
|
||||
(defun c-ts-mode-comment-setup ()
|
||||
"Set up local variables for C-like comment.
|
||||
|
||||
Set up:
|
||||
- `comment-start'
|
||||
- `comment-end'
|
||||
- `comment-start-skip'
|
||||
- `comment-end-skip'
|
||||
- `adaptive-fill-mode'
|
||||
- `adaptive-fill-first-line-regexp'
|
||||
- `paragraph-start'
|
||||
- `paragraph-separate'
|
||||
- `fill-paragraph-function'"
|
||||
(setq-local comment-start "// ")
|
||||
(setq-local comment-end "")
|
||||
(setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
|
||||
(seq "/" (+ "*")))
|
||||
(* (syntax whitespace))))
|
||||
(setq-local comment-end-skip
|
||||
(rx (* (syntax whitespace))
|
||||
(group (or (syntax comment-end)
|
||||
(seq (+ "*") "/")))))
|
||||
(setq-local adaptive-fill-mode t)
|
||||
;; This matches (1) empty spaces (the default), (2) "//", (3) "*",
|
||||
;; but do not match "/*", because we don't want to use "/*" as
|
||||
;; prefix when filling. (Actually, it doesn't matter, because
|
||||
;; `comment-start-skip' matches "/*" which will cause
|
||||
;; `fill-context-prefix' to use "/*" as a prefix for filling, that's
|
||||
;; why we mask the "/*" in `c-ts-mode--fill-paragraph'.)
|
||||
(setq-local adaptive-fill-regexp
|
||||
(concat (rx (* (syntax whitespace))
|
||||
(group (or (seq "/" (+ "/")) (* "*"))))
|
||||
adaptive-fill-regexp))
|
||||
;; Same as `adaptive-fill-regexp'.
|
||||
(setq-local adaptive-fill-first-line-regexp
|
||||
(rx bos
|
||||
(seq (* (syntax whitespace))
|
||||
(group (or (seq "/" (+ "/")) (* "*")))
|
||||
(* (syntax whitespace)))
|
||||
eos))
|
||||
;; Same as `adaptive-fill-regexp'.
|
||||
(setq-local paragraph-start
|
||||
(rx (or (seq (* (syntax whitespace))
|
||||
(group (or (seq "/" (+ "/")) (* "*")))
|
||||
(* (syntax whitespace))
|
||||
;; Add this eol so that in
|
||||
;; `fill-context-prefix', `paragraph-start'
|
||||
;; doesn't match the prefix.
|
||||
eol)
|
||||
"\f")))
|
||||
(setq-local paragraph-separate paragraph-start)
|
||||
(setq-local fill-paragraph-function #'c-ts-mode--fill-paragraph))
|
||||
|
||||
;;; Modes
|
||||
|
||||
(defvar-keymap c-ts-mode-map
|
||||
|
|
@ -694,44 +699,25 @@ ARG is passed to `fill-paragraph'."
|
|||
(when (eq c-ts-mode-indent-style 'linux)
|
||||
(setq-local indent-tabs-mode t))
|
||||
|
||||
(setq-local adaptive-fill-mode t)
|
||||
;; This matches (1) empty spaces (the default), (2) "//", (3) "*",
|
||||
;; but do not match "/*", because we don't want to use "/*" as
|
||||
;; prefix when filling. (Actually, it doesn't matter, because
|
||||
;; `comment-start-skip' matches "/*" which will cause
|
||||
;; `fill-context-prefix' to use "/*" as a prefix for filling, that's
|
||||
;; why we mask the "/*" in `c-ts-mode--fill-paragraph'.)
|
||||
(setq-local adaptive-fill-regexp
|
||||
(concat (rx (* (syntax whitespace))
|
||||
(group (or (seq "/" (+ "/")) (* "*"))))
|
||||
adaptive-fill-regexp))
|
||||
;; Same as `adaptive-fill-regexp'.
|
||||
(setq-local adaptive-fill-first-line-regexp
|
||||
(rx bos
|
||||
(seq (* (syntax whitespace))
|
||||
(group (or (seq "/" (+ "/")) (* "*")))
|
||||
(* (syntax whitespace)))
|
||||
eos))
|
||||
;; Same as `adaptive-fill-regexp'.
|
||||
(setq-local paragraph-start
|
||||
(rx (or (seq (* (syntax whitespace))
|
||||
(group (or (seq "/" (+ "/")) (* "*")))
|
||||
(* (syntax whitespace))
|
||||
;; Add this eol so that in
|
||||
;; `fill-context-prefix', `paragraph-start'
|
||||
;; doesn't match the prefix.
|
||||
eol)
|
||||
"\f")))
|
||||
(setq-local paragraph-separate paragraph-start)
|
||||
(setq-local fill-paragraph-function #'c-ts-mode--fill-paragraph)
|
||||
;; Comment
|
||||
(c-ts-mode-comment-setup)
|
||||
|
||||
;; Electric
|
||||
(setq-local electric-indent-chars
|
||||
(append "{}():;," electric-indent-chars))
|
||||
|
||||
;; Imenu.
|
||||
(setq-local imenu-create-index-function #'c-ts-mode--imenu)
|
||||
(setq-local which-func-functions nil)
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
(let ((pred #'c-ts-mode--defun-valid-p))
|
||||
`(("Struct" ,(rx bos (or "struct" "enum" "union")
|
||||
"_specifier" eos)
|
||||
,pred nil)
|
||||
("Variable" ,(rx bos "declaration" eos) ,pred nil)
|
||||
("Function" "\\`function_definition\\'" ,pred nil)
|
||||
("Class" ,(rx bos (or "class_specifier"
|
||||
"function_definition")
|
||||
eos)
|
||||
,pred nil))))
|
||||
|
||||
(setq-local treesit-font-lock-feature-list
|
||||
'(( comment definition)
|
||||
|
|
@ -752,13 +738,6 @@ ARG is passed to `fill-paragraph'."
|
|||
;; Comments.
|
||||
(setq-local comment-start "/* ")
|
||||
(setq-local comment-end " */")
|
||||
(setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
|
||||
(seq "/" (+ "*")))
|
||||
(* (syntax whitespace))))
|
||||
(setq-local comment-end-skip
|
||||
(rx (* (syntax whitespace))
|
||||
(group (or (syntax comment-end)
|
||||
(seq (+ "*") "/")))))
|
||||
|
||||
(setq-local treesit-simple-indent-rules
|
||||
(c-ts-mode--set-indent-style 'c))
|
||||
|
|
@ -766,11 +745,7 @@ ARG is passed to `fill-paragraph'."
|
|||
;; Font-lock.
|
||||
(setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'c))
|
||||
|
||||
(treesit-major-mode-setup)
|
||||
|
||||
;; Override default value of end-of-defun-function set by
|
||||
;; `treesit-major-mode-setup'.
|
||||
(setq-local end-of-defun-function #'c-ts-mode--end-of-defun))
|
||||
(treesit-major-mode-setup))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode c++-ts-mode c-ts-base-mode "C++"
|
||||
|
|
@ -781,17 +756,6 @@ ARG is passed to `fill-paragraph'."
|
|||
(unless (treesit-ready-p 'cpp)
|
||||
(error "Tree-sitter for C++ isn't available"))
|
||||
|
||||
;; Comments.
|
||||
(setq-local comment-start "// ")
|
||||
(setq-local comment-end "")
|
||||
(setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
|
||||
(seq "/" (+ "*")))
|
||||
(* (syntax whitespace))))
|
||||
(setq-local comment-end-skip
|
||||
(rx (* (syntax whitespace))
|
||||
(group (or (syntax comment-end)
|
||||
(seq (+ "*") "/")))))
|
||||
|
||||
(setq-local treesit-text-type-regexp
|
||||
(regexp-opt '("comment"
|
||||
"raw_string_literal")))
|
||||
|
|
@ -804,11 +768,7 @@ ARG is passed to `fill-paragraph'."
|
|||
;; Font-lock.
|
||||
(setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'cpp))
|
||||
|
||||
(treesit-major-mode-setup)
|
||||
|
||||
;; Override default value of end-of-defun-function set by
|
||||
;; `treesit-major-mode-setup'.
|
||||
(setq-local end-of-defun-function #'c-ts-mode--end-of-defun))
|
||||
(treesit-major-mode-setup))
|
||||
|
||||
(provide 'c-ts-mode)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
(require 'cc-mode)
|
||||
(require 'cc-langs)
|
||||
(require 'treesit)
|
||||
(require 'c-ts-mode) ; For comment indenting and filling.
|
||||
|
||||
(eval-when-compile
|
||||
(require 'cc-fonts)
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
(declare-function treesit-parser-create "treesit.c")
|
||||
(declare-function treesit-induce-sparse-tree "treesit.c")
|
||||
(declare-function treesit-node-start "treesit.c")
|
||||
(declare-function treesit-node-type "treesit.c")
|
||||
(declare-function treesit-node-child-by-field-name "treesit.c")
|
||||
|
||||
(defgroup csharp nil
|
||||
|
|
@ -632,6 +634,9 @@ compilation and evaluation time conflicts."
|
|||
((node-is "}") parent-bol 0)
|
||||
((node-is ")") parent-bol 0)
|
||||
((node-is "]") parent-bol 0)
|
||||
((and (parent-is "comment") c-ts-mode--looking-at-star)
|
||||
c-ts-mode--comment-start-after-first-star -1)
|
||||
((parent-is "comment") prev-adaptive-prefix 0)
|
||||
((parent-is "namespace_declaration") parent-bol 0)
|
||||
((parent-is "class_declaration") parent-bol 0)
|
||||
((parent-is "constructor_declaration") parent-bol 0)
|
||||
|
|
@ -853,54 +858,6 @@ Return nil if there is no name or if NODE is not a defun node."
|
|||
node "name")
|
||||
t))))
|
||||
|
||||
(defun csharp-ts-mode--imenu-1 (node)
|
||||
"Helper for `csharp-ts-mode--imenu'.
|
||||
Find string representation for NODE and set marker, then recurse
|
||||
the subtrees."
|
||||
(let* ((ts-node (car node))
|
||||
(subtrees (mapcan #'csharp-ts-mode--imenu-1 (cdr node)))
|
||||
(name (when ts-node
|
||||
(or (treesit-defun-name ts-node)
|
||||
"Unnamed node")))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
((null ts-node) subtrees)
|
||||
(subtrees
|
||||
`((,name ,(cons name marker) ,@subtrees)))
|
||||
(t
|
||||
`((,name . ,marker))))))
|
||||
|
||||
(defun csharp-ts-mode--imenu ()
|
||||
"Return Imenu alist for the current buffer."
|
||||
(let* ((node (treesit-buffer-root-node))
|
||||
(class-tree (treesit-induce-sparse-tree
|
||||
node "^class_declaration$" nil 1000))
|
||||
(interface-tree (treesit-induce-sparse-tree
|
||||
node "^interface_declaration$" nil 1000))
|
||||
(enum-tree (treesit-induce-sparse-tree
|
||||
node "^enum_declaration$" nil 1000))
|
||||
(struct-tree (treesit-induce-sparse-tree
|
||||
node "^struct_declaration$" nil 1000))
|
||||
(record-tree (treesit-induce-sparse-tree
|
||||
node "^record_declaration$" nil 1000))
|
||||
(method-tree (treesit-induce-sparse-tree
|
||||
node "^method_declaration$" nil 1000))
|
||||
(class-index (csharp-ts-mode--imenu-1 class-tree))
|
||||
(interface-index (csharp-ts-mode--imenu-1 interface-tree))
|
||||
(enum-index (csharp-ts-mode--imenu-1 enum-tree))
|
||||
(record-index (csharp-ts-mode--imenu-1 record-tree))
|
||||
(struct-index (csharp-ts-mode--imenu-1 struct-tree))
|
||||
(method-index (csharp-ts-mode--imenu-1 method-tree)))
|
||||
(append
|
||||
(when class-index `(("Class" . ,class-index)))
|
||||
(when interface-index `(("Interface" . ,interface-index)))
|
||||
(when enum-index `(("Enum" . ,enum-index)))
|
||||
(when record-index `(("Record" . ,record-index)))
|
||||
(when struct-index `(("Struct" . ,struct-index)))
|
||||
(when method-index `(("Method" . ,method-index))))))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode))
|
||||
|
||||
|
|
@ -929,15 +886,7 @@ Key bindings:
|
|||
(treesit-parser-create 'c-sharp)
|
||||
|
||||
;; Comments.
|
||||
(setq-local comment-start "// ")
|
||||
(setq-local comment-end "")
|
||||
(setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
|
||||
(seq "/" (+ "*")))
|
||||
(* (syntax whitespace))))
|
||||
(setq-local comment-end-skip
|
||||
(rx (* (syntax whitespace))
|
||||
(group (or (syntax comment-end)
|
||||
(seq (+ "*") "/")))))
|
||||
(c-ts-mode-comment-setup)
|
||||
|
||||
(setq-local treesit-text-type-regexp
|
||||
(regexp-opt '("comment"
|
||||
|
|
@ -964,8 +913,14 @@ Key bindings:
|
|||
( bracket delimiter)))
|
||||
|
||||
;; Imenu.
|
||||
(setq-local imenu-create-index-function #'csharp-ts-mode--imenu)
|
||||
(setq-local which-func-functions nil) ;; Piggyback on imenu
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
'(("Class" "\\`class_declaration\\'" nil nil)
|
||||
("Interface" "\\`interface_declaration\\'" nil nil)
|
||||
("Enum" "\\`enum_declaration\\'" nil nil)
|
||||
("Record" "\\`record_declaration\\'" nil nil)
|
||||
("Struct" "\\`struct_declaration\\'" nil nil)
|
||||
("Method" "\\`method_declaration\\'" nil nil)))
|
||||
|
||||
(treesit-major-mode-setup))
|
||||
|
||||
(provide 'csharp-mode)
|
||||
|
|
|
|||
|
|
@ -29,10 +29,12 @@
|
|||
|
||||
(require 'treesit)
|
||||
(eval-when-compile (require 'rx))
|
||||
(require 'c-ts-mode) ; For comment indent and filling.
|
||||
|
||||
(declare-function treesit-parser-create "treesit.c")
|
||||
(declare-function treesit-induce-sparse-tree "treesit.c")
|
||||
(declare-function treesit-node-start "treesit.c")
|
||||
(declare-function treesit-node-type "treesit.c")
|
||||
(declare-function treesit-node-child-by-field-name "treesit.c")
|
||||
|
||||
(defcustom java-ts-mode-indent-offset 4
|
||||
|
|
@ -71,8 +73,9 @@
|
|||
((node-is "}") (and parent parent-bol) 0)
|
||||
((node-is ")") parent-bol 0)
|
||||
((node-is "]") parent-bol 0)
|
||||
((and (parent-is "comment") comment-end) comment-start -1)
|
||||
((parent-is "comment") comment-start-skip 0)
|
||||
((and (parent-is "comment") c-ts-mode--looking-at-star)
|
||||
c-ts-mode--comment-start-after-first-star -1)
|
||||
((parent-is "comment") prev-adaptive-prefix 0)
|
||||
((parent-is "text_block") no-indent)
|
||||
((parent-is "class_body") parent-bol java-ts-mode-indent-offset)
|
||||
((parent-is "interface_body") parent-bol java-ts-mode-indent-offset)
|
||||
|
|
@ -264,50 +267,6 @@ Return nil if there is no name or if NODE is not a defun node."
|
|||
(treesit-node-child-by-field-name node "name")
|
||||
t))))
|
||||
|
||||
(defun java-ts-mode--imenu-1 (node)
|
||||
"Helper for `java-ts-mode--imenu'.
|
||||
Find string representation for NODE and set marker, then recurse
|
||||
the subtrees."
|
||||
(let* ((ts-node (car node))
|
||||
(subtrees (mapcan #'java-ts-mode--imenu-1 (cdr node)))
|
||||
(name (when ts-node
|
||||
(or (treesit-defun-name ts-node)
|
||||
"Unnamed node")))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
((null ts-node) subtrees)
|
||||
(subtrees
|
||||
`((,name ,(cons name marker) ,@subtrees)))
|
||||
(t
|
||||
`((,name . ,marker))))))
|
||||
|
||||
(defun java-ts-mode--imenu ()
|
||||
"Return Imenu alist for the current buffer."
|
||||
(let* ((node (treesit-buffer-root-node))
|
||||
(class-tree (treesit-induce-sparse-tree
|
||||
node "^class_declaration$" nil 1000))
|
||||
(interface-tree (treesit-induce-sparse-tree
|
||||
node "^interface_declaration$" nil 1000))
|
||||
(enum-tree (treesit-induce-sparse-tree
|
||||
node "^enum_declaration$" nil 1000))
|
||||
(record-tree (treesit-induce-sparse-tree
|
||||
node "^record_declaration$" nil 1000))
|
||||
(method-tree (treesit-induce-sparse-tree
|
||||
node "^method_declaration$" nil 1000))
|
||||
(class-index (java-ts-mode--imenu-1 class-tree))
|
||||
(interface-index (java-ts-mode--imenu-1 interface-tree))
|
||||
(enum-index (java-ts-mode--imenu-1 enum-tree))
|
||||
(record-index (java-ts-mode--imenu-1 record-tree))
|
||||
(method-index (java-ts-mode--imenu-1 method-tree)))
|
||||
(append
|
||||
(when class-index `(("Class" . ,class-index)))
|
||||
(when interface-index `(("Interface" . ,interface-index)))
|
||||
(when enum-index `(("Enum" . ,enum-index)))
|
||||
(when record-index `(("Record" . ,record-index)))
|
||||
(when method-index `(("Method" . ,method-index))))))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode java-ts-mode prog-mode "Java"
|
||||
"Major mode for editing Java, powered by tree-sitter."
|
||||
|
|
@ -320,15 +279,7 @@ the subtrees."
|
|||
(treesit-parser-create 'java)
|
||||
|
||||
;; Comments.
|
||||
(setq-local comment-start "// ")
|
||||
(setq-local comment-end "")
|
||||
(setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
|
||||
(seq "/" (+ "*")))
|
||||
(* (syntax whitespace))))
|
||||
(setq-local comment-end-skip
|
||||
(rx (* (syntax whitespace))
|
||||
(group (or (syntax comment-end)
|
||||
(seq (+ "*") "/")))))
|
||||
(c-ts-mode-comment-setup)
|
||||
|
||||
(setq-local treesit-text-type-regexp
|
||||
(regexp-opt '("line_comment"
|
||||
|
|
@ -363,8 +314,11 @@ the subtrees."
|
|||
( bracket delimiter operator)))
|
||||
|
||||
;; Imenu.
|
||||
(setq-local imenu-create-index-function #'java-ts-mode--imenu)
|
||||
(setq-local which-func-functions nil) ;; Piggyback on imenu
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
'(("Class" "\\`class_declaration\\'" nil nil)
|
||||
("Interface" "\\`interface_declaration\\'" nil nil)
|
||||
("Enum" "\\`record_declaration\\'" nil nil)
|
||||
("Method" "\\`method_declaration\\'" nil nil)))
|
||||
(treesit-major-mode-setup))
|
||||
|
||||
(provide 'java-ts-mode)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
(require 'json)
|
||||
(require 'prog-mode)
|
||||
(require 'treesit)
|
||||
(require 'c-ts-mode) ; For comment indent and filling.
|
||||
|
||||
(eval-when-compile
|
||||
(require 'cl-lib)
|
||||
|
|
@ -3425,9 +3426,9 @@ This function is intended for use in `after-change-functions'."
|
|||
((node-is ")") parent-bol 0)
|
||||
((node-is "]") parent-bol 0)
|
||||
((node-is ">") parent-bol 0)
|
||||
((parent-is "comment") comment-start 0)
|
||||
((and (parent-is "comment") comment-end) comment-start -1)
|
||||
((parent-is "comment") comment-start-skip 0)
|
||||
((and (parent-is "comment") c-ts-mode--looking-at-star)
|
||||
c-ts-mode--comment-start-after-first-star -1)
|
||||
((parent-is "comment") prev-adaptive-prefix 0)
|
||||
((parent-is "ternary_expression") parent-bol js-indent-level)
|
||||
((parent-is "member_expression") parent-bol js-indent-level)
|
||||
((node-is ,switch-case) parent-bol 0)
|
||||
|
|
@ -3669,70 +3670,11 @@ Return nil if there is no name or if NODE is not a defun node."
|
|||
"name")
|
||||
t))
|
||||
|
||||
(defun js--treesit-imenu-1 (node)
|
||||
"Given a sparse tree, create an imenu alist.
|
||||
|
||||
NODE is the root node of the tree returned by
|
||||
`treesit-induce-sparse-tree' (not a tree-sitter node, its car is
|
||||
a tree-sitter node). Walk that tree and return an imenu alist.
|
||||
|
||||
Return a list of ENTRY where
|
||||
|
||||
ENTRY := (NAME . MARKER)
|
||||
| (NAME . ((JUMP-LABEL . MARKER)
|
||||
ENTRY
|
||||
...)
|
||||
|
||||
NAME is the function/class's name, JUMP-LABEL is like \"*function
|
||||
definition*\"."
|
||||
(let* ((ts-node (car node))
|
||||
(children (cdr node))
|
||||
(subtrees (mapcan #'js--treesit-imenu-1
|
||||
children))
|
||||
(type (pcase (treesit-node-type ts-node)
|
||||
("lexical_declaration" 'variable)
|
||||
("class_declaration" 'class)
|
||||
("method_definition" 'method)
|
||||
("function_declaration" 'function)))
|
||||
;; The root of the tree could have a nil ts-node.
|
||||
(name (when ts-node
|
||||
(or (treesit-defun-name ts-node)
|
||||
"Anonymous")))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
((null ts-node)
|
||||
subtrees)
|
||||
;; Don't included non-top-level variable declarations.
|
||||
((and (eq type 'variable)
|
||||
(treesit-node-top-level ts-node))
|
||||
nil)
|
||||
(subtrees
|
||||
`((,name
|
||||
,(cons "" marker)
|
||||
,@subtrees)))
|
||||
(t (list (cons name marker))))))
|
||||
|
||||
(defun js--treesit-imenu ()
|
||||
"Return Imenu alist for the current buffer."
|
||||
(let* ((node (treesit-buffer-root-node))
|
||||
(class-tree (treesit-induce-sparse-tree
|
||||
node (rx (or "class_declaration"
|
||||
"method_definition"))
|
||||
nil 1000))
|
||||
(func-tree (treesit-induce-sparse-tree
|
||||
node "function_declaration" nil 1000))
|
||||
(var-tree (treesit-induce-sparse-tree
|
||||
node "lexical_declaration" nil 1000)))
|
||||
;; When a sub-tree is empty, we should not return that pair at all.
|
||||
(append
|
||||
(and func-tree
|
||||
`(("Function" . ,(js--treesit-imenu-1 func-tree))))
|
||||
(and var-tree
|
||||
`(("Variable" . ,(js--treesit-imenu-1 var-tree))))
|
||||
(and class-tree
|
||||
`(("Class" . ,(js--treesit-imenu-1 class-tree)))))))
|
||||
(defun js--treesit-valid-imenu-entry (node)
|
||||
"Return nil if NODE is a non-top-level \"lexical_declaration\"."
|
||||
(pcase (treesit-node-type node)
|
||||
("lexical_declaration" (treesit-node-top-level node))
|
||||
(_ t)))
|
||||
|
||||
;;; Main Function
|
||||
|
||||
|
|
@ -3845,15 +3787,7 @@ Currently there are `js-mode' and `js-ts-mode'."
|
|||
;; Which-func.
|
||||
(setq-local which-func-imenu-joiner-function #'js--which-func-joiner)
|
||||
;; Comment.
|
||||
(setq-local comment-start "// ")
|
||||
(setq-local comment-end "")
|
||||
(setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
|
||||
(seq "/" (+ "*")))
|
||||
(* (syntax whitespace))))
|
||||
(setq-local comment-end-skip
|
||||
(rx (* (syntax whitespace))
|
||||
(group (or (syntax comment-end)
|
||||
(seq (+ "*") "/")))))
|
||||
(c-ts-mode-comment-setup)
|
||||
(setq-local comment-multi-line t)
|
||||
|
||||
(setq-local treesit-text-type-regexp
|
||||
|
|
@ -3887,10 +3821,14 @@ Currently there are `js-mode' and `js-ts-mode'."
|
|||
identifier jsx number pattern property)
|
||||
( bracket delimiter operator)))
|
||||
;; Imenu
|
||||
(setq-local imenu-create-index-function
|
||||
#'js--treesit-imenu)
|
||||
;; Which-func (use imenu).
|
||||
(setq-local which-func-functions nil)
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
`(("Function" "\\`function_declaration\\'" nil nil)
|
||||
("Variable" "\\`lexical_declaration\\'"
|
||||
js--treesit-valid-imenu-entry nil)
|
||||
("Class" ,(rx bos (or "class_declaration"
|
||||
"method_definition")
|
||||
eos)
|
||||
nil nil)))
|
||||
(treesit-major-mode-setup)))
|
||||
|
||||
;;;###autoload
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
(declare-function treesit-parser-create "treesit.c")
|
||||
(declare-function treesit-induce-sparse-tree "treesit.c")
|
||||
(declare-function treesit-node-start "treesit.c")
|
||||
(declare-function treesit-node-type "treesit.c")
|
||||
(declare-function treesit-node-child-by-field-name "treesit.c")
|
||||
|
||||
|
||||
|
|
@ -112,36 +113,11 @@
|
|||
Return nil if there is no name or if NODE is not a defun node."
|
||||
(pcase (treesit-node-type node)
|
||||
((or "pair" "object")
|
||||
(treesit-node-text
|
||||
(treesit-node-child-by-field-name
|
||||
node "key")
|
||||
t))))
|
||||
|
||||
(defun json-ts-mode--imenu-1 (node)
|
||||
"Helper for `json-ts-mode--imenu'.
|
||||
Find string representation for NODE and set marker, then recurse
|
||||
the subtrees."
|
||||
(let* ((ts-node (car node))
|
||||
(subtrees (mapcan #'json-ts-mode--imenu-1 (cdr node)))
|
||||
(name (when ts-node
|
||||
(or (treesit-defun-name ts-node)
|
||||
"Anonymous")))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
((null ts-node) subtrees)
|
||||
(subtrees
|
||||
`((,name ,(cons name marker) ,@subtrees)))
|
||||
(t
|
||||
`((,name . ,marker))))))
|
||||
|
||||
(defun json-ts-mode--imenu ()
|
||||
"Return Imenu alist for the current buffer."
|
||||
(let* ((node (treesit-buffer-root-node))
|
||||
(tree (treesit-induce-sparse-tree
|
||||
node "pair" nil 1000)))
|
||||
(json-ts-mode--imenu-1 tree)))
|
||||
(string-trim (treesit-node-text
|
||||
(treesit-node-child-by-field-name
|
||||
node "key")
|
||||
t)
|
||||
"\"" "\""))))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode json-ts-mode prog-mode "JSON"
|
||||
|
|
@ -179,8 +155,8 @@ the subtrees."
|
|||
(bracket delimiter error)))
|
||||
|
||||
;; Imenu.
|
||||
(setq-local imenu-create-index-function #'json-ts-mode--imenu)
|
||||
(setq-local which-func-functions nil) ;; Piggyback on imenu
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
'((nil "\\`pair\\'" nil nil)))
|
||||
|
||||
(treesit-major-mode-setup))
|
||||
|
||||
|
|
|
|||
|
|
@ -1080,7 +1080,6 @@ fontified."
|
|||
|
||||
:feature 'string
|
||||
:language 'python
|
||||
:override t
|
||||
'((string) @python--treesit-fontify-string)
|
||||
|
||||
:feature 'string-interpolation
|
||||
|
|
@ -1097,9 +1096,7 @@ fontified."
|
|||
|
||||
:feature 'function
|
||||
:language 'python
|
||||
'((function_definition
|
||||
name: (identifier) @font-lock-function-name-face)
|
||||
(call function: (identifier) @font-lock-function-name-face)
|
||||
'((call function: (identifier) @font-lock-function-name-face)
|
||||
(call function: (attribute
|
||||
attribute: (identifier) @font-lock-function-name-face)))
|
||||
|
||||
|
|
@ -1130,7 +1127,7 @@ fontified."
|
|||
@font-lock-variable-name-face)
|
||||
(assignment left: (attribute
|
||||
attribute: (identifier)
|
||||
@font-lock-variable-name-face))
|
||||
@font-lock-property-face))
|
||||
(pattern_list (identifier)
|
||||
@font-lock-variable-name-face)
|
||||
(tuple_pattern (identifier)
|
||||
|
|
@ -1162,12 +1159,10 @@ fontified."
|
|||
|
||||
:feature 'number
|
||||
:language 'python
|
||||
:override t
|
||||
'([(integer) (float)] @font-lock-number-face)
|
||||
|
||||
:feature 'property
|
||||
:language 'python
|
||||
:override t
|
||||
'((attribute
|
||||
attribute: (identifier) @font-lock-property-face)
|
||||
(class_definition
|
||||
|
|
@ -1178,20 +1173,44 @@ fontified."
|
|||
|
||||
:feature 'operator
|
||||
:language 'python
|
||||
:override t
|
||||
`([,@python--treesit-operators] @font-lock-operator-face)
|
||||
|
||||
:feature 'bracket
|
||||
:language 'python
|
||||
:override t
|
||||
'(["(" ")" "[" "]" "{" "}"] @font-lock-bracket-face)
|
||||
|
||||
:feature 'delimiter
|
||||
:language 'python
|
||||
:override t
|
||||
'(["," "." ":" ";" (ellipsis)] @font-lock-delimiter-face))
|
||||
'(["," "." ":" ";" (ellipsis)] @font-lock-delimiter-face)
|
||||
|
||||
:feature 'variable
|
||||
:language 'python
|
||||
'((identifier) @python--treesit-fontify-variable))
|
||||
"Tree-sitter font-lock settings.")
|
||||
|
||||
(defun python--treesit-variable-p (node)
|
||||
"Check whether NODE is a variable.
|
||||
NODE's type should be \"identifier\"."
|
||||
;; An identifier can be a function/class name, a property, or a
|
||||
;; variables. This funtion filters out function/class names and
|
||||
;; properties.
|
||||
(pcase (treesit-node-type (treesit-node-parent node))
|
||||
((or "function_definition" "class_definition") nil)
|
||||
("attribute"
|
||||
(pcase (treesit-node-field-name node)
|
||||
("object" t)
|
||||
(_ nil)))
|
||||
(_ t)))
|
||||
|
||||
(defun python--treesit-fontify-variable (node override start end &rest _)
|
||||
"Fontify an identifier node if it is a variable.
|
||||
For NODE, OVERRIDE, START, END, and ARGS, see
|
||||
`treesit-font-lock-rules'."
|
||||
(when (python--treesit-variable-p node)
|
||||
(treesit-fontify-with-override
|
||||
(treesit-node-start node) (treesit-node-end node)
|
||||
'font-lock-variable-name-face override start end)))
|
||||
|
||||
|
||||
;;; Indentation
|
||||
|
||||
|
|
@ -6646,7 +6665,7 @@ implementations: `python-mode' and `python-ts-mode'."
|
|||
( keyword string type)
|
||||
( assignment builtin constant decorator
|
||||
escape-sequence number property string-interpolation )
|
||||
( function bracket delimiter operator)))
|
||||
( bracket delimiter function operator variable)))
|
||||
(setq-local treesit-font-lock-settings python--treesit-settings)
|
||||
(setq-local imenu-create-index-function
|
||||
#'python-imenu-treesit-create-index)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
(require 'treesit)
|
||||
(eval-when-compile (require 'rx))
|
||||
(require 'c-ts-mode) ; For comment indent and filling.
|
||||
|
||||
(declare-function treesit-parser-create "treesit.c")
|
||||
(declare-function treesit-induce-sparse-tree "treesit.c")
|
||||
|
|
@ -70,6 +71,9 @@
|
|||
((node-is ")") parent-bol 0)
|
||||
((node-is "]") parent-bol 0)
|
||||
((node-is "}") (and parent parent-bol) 0)
|
||||
((and (parent-is "comment") c-ts-mode--looking-at-star)
|
||||
c-ts-mode--comment-start-after-first-star -1)
|
||||
((parent-is "comment") prev-adaptive-prefix 0)
|
||||
((parent-is "arguments") parent-bol rust-ts-mode-indent-offset)
|
||||
((parent-is "await_expression") parent-bol rust-ts-mode-indent-offset)
|
||||
((parent-is "array_expression") parent-bol rust-ts-mode-indent-offset)
|
||||
|
|
@ -244,35 +248,6 @@
|
|||
'((ERROR) @font-lock-warning-face))
|
||||
"Tree-sitter font-lock settings for `rust-ts-mode'.")
|
||||
|
||||
(defun rust-ts-mode--imenu ()
|
||||
"Return Imenu alist for the current buffer."
|
||||
(let* ((node (treesit-buffer-root-node))
|
||||
(enum-tree (treesit-induce-sparse-tree
|
||||
node "enum_item" nil))
|
||||
(enum-index (rust-ts-mode--imenu-1 enum-tree))
|
||||
(func-tree (treesit-induce-sparse-tree
|
||||
node "function_item" nil))
|
||||
(func-index (rust-ts-mode--imenu-1 func-tree))
|
||||
(impl-tree (treesit-induce-sparse-tree
|
||||
node "impl_item" nil))
|
||||
(impl-index (rust-ts-mode--imenu-1 impl-tree))
|
||||
(mod-tree (treesit-induce-sparse-tree
|
||||
node "mod_item" nil))
|
||||
(mod-index (rust-ts-mode--imenu-1 mod-tree))
|
||||
(struct-tree (treesit-induce-sparse-tree
|
||||
node "struct_item" nil))
|
||||
(struct-index (rust-ts-mode--imenu-1 struct-tree))
|
||||
(type-tree (treesit-induce-sparse-tree
|
||||
node "type_item" nil))
|
||||
(type-index (rust-ts-mode--imenu-1 type-tree)))
|
||||
(append
|
||||
(when mod-index `(("Module" . ,mod-index)))
|
||||
(when enum-index `(("Enum" . ,enum-index)))
|
||||
(when impl-index `(("Impl" . ,impl-index)))
|
||||
(when type-index `(("Type" . ,type-index)))
|
||||
(when struct-index `(("Struct" . ,struct-index)))
|
||||
(when func-index `(("Fn" . ,func-index))))))
|
||||
|
||||
(defun rust-ts-mode--defun-name (node)
|
||||
"Return the defun name of NODE.
|
||||
Return nil if there is no name or if NODE is not a defun node."
|
||||
|
|
@ -300,27 +275,6 @@ Return nil if there is no name or if NODE is not a defun node."
|
|||
(treesit-node-text
|
||||
(treesit-node-child-by-field-name node "name") t))))
|
||||
|
||||
(defun rust-ts-mode--imenu-1 (node)
|
||||
"Helper for `rust-ts-mode--imenu'.
|
||||
Find string representation for NODE and set marker, then recurse
|
||||
the subtrees."
|
||||
(let* ((ts-node (car node))
|
||||
(children (cdr node))
|
||||
(subtrees (mapcan #'rust-ts-mode--imenu-1
|
||||
children))
|
||||
(name (when ts-node
|
||||
(or (treesit-defun-name ts-node)
|
||||
"Anonymous")))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
((or (null ts-node) (null name)) subtrees)
|
||||
(subtrees
|
||||
`((,name ,(cons name marker) ,@subtrees)))
|
||||
(t
|
||||
`((,name . ,marker))))))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode))
|
||||
|
||||
|
|
@ -334,15 +288,7 @@ the subtrees."
|
|||
(treesit-parser-create 'rust)
|
||||
|
||||
;; Comments.
|
||||
(setq-local comment-start "// ")
|
||||
(setq-local comment-end "")
|
||||
(setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
|
||||
(seq "/" (+ "*")))
|
||||
(* (syntax whitespace))))
|
||||
(setq-local comment-end-skip
|
||||
(rx (* (syntax whitespace))
|
||||
(group (or (syntax comment-end)
|
||||
(seq (+ "*") "/")))))
|
||||
(c-ts-mode-comment-setup)
|
||||
|
||||
;; Font-lock.
|
||||
(setq-local treesit-font-lock-settings rust-ts-mode--font-lock-settings)
|
||||
|
|
@ -354,8 +300,13 @@ the subtrees."
|
|||
( bracket delimiter error operator)))
|
||||
|
||||
;; Imenu.
|
||||
(setq-local imenu-create-index-function #'rust-ts-mode--imenu)
|
||||
(setq-local which-func-functions nil)
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
`(("Module" "\\`mod_item\\'" nil nil)
|
||||
("Enum" "\\`enum_item\\'" nil nil)
|
||||
("Impl" "\\`impl_item\\'" nil nil)
|
||||
("Type" "\\`type_item\\'" nil nil)
|
||||
("Struct" "\\`struct_item\\'" nil nil)
|
||||
("Fn" "\\`function_item\\'" nil nil)))
|
||||
|
||||
;; Indent.
|
||||
(setq-local indent-tabs-mode nil
|
||||
|
|
|
|||
|
|
@ -115,7 +115,8 @@
|
|||
|
||||
(defvar scheme-imenu-generic-expression
|
||||
`((nil
|
||||
,(rx bol "(define"
|
||||
,(rx bol (zero-or-more space)
|
||||
"(define"
|
||||
(zero-or-one "*")
|
||||
(zero-or-one "-public")
|
||||
(one-or-more space)
|
||||
|
|
@ -123,36 +124,41 @@
|
|||
(group (one-or-more (or word (syntax symbol)))))
|
||||
1)
|
||||
("Methods"
|
||||
,(rx bol "(define-"
|
||||
,(rx bol (zero-or-more space)
|
||||
"(define-"
|
||||
(or "generic" "method" "accessor")
|
||||
(one-or-more space)
|
||||
(zero-or-one "(")
|
||||
(group (one-or-more (or word (syntax symbol)))))
|
||||
1)
|
||||
("Classes"
|
||||
,(rx bol "(define-class"
|
||||
,(rx bol (zero-or-more space)
|
||||
"(define-class"
|
||||
(one-or-more space)
|
||||
(zero-or-one "(")
|
||||
(group (one-or-more (or word (syntax symbol)))))
|
||||
1)
|
||||
("Records"
|
||||
,(rx bol "(define-record-type"
|
||||
,(rx bol (zero-or-more space)
|
||||
"(define-record-type"
|
||||
(zero-or-one "*")
|
||||
(one-or-more space)
|
||||
(group (one-or-more (or word (syntax symbol)))))
|
||||
1)
|
||||
("Conditions"
|
||||
,(rx bol "(define-condition-type"
|
||||
,(rx bol (zero-or-more space)
|
||||
"(define-condition-type"
|
||||
(one-or-more space)
|
||||
(group (one-or-more (or word (syntax symbol)))))
|
||||
1)
|
||||
("Modules"
|
||||
,(rx bol "(define-module"
|
||||
,(rx bol (zero-or-more space)
|
||||
"(define-module"
|
||||
(one-or-more space)
|
||||
(group "(" (one-or-more any) ")"))
|
||||
1)
|
||||
("Macros"
|
||||
,(rx bol "("
|
||||
,(rx bol (zero-or-more space) "("
|
||||
(or (and "defmacro"
|
||||
(zero-or-one "*")
|
||||
(zero-or-one "-public"))
|
||||
|
|
|
|||
|
|
@ -150,6 +150,8 @@
|
|||
(require 'executable)
|
||||
(require 'treesit)
|
||||
|
||||
(declare-function treesit-parser-create "treesit.c")
|
||||
|
||||
(autoload 'comint-completion-at-point "comint")
|
||||
(autoload 'comint-filename-completion "comint")
|
||||
(autoload 'comint-send-string "comint")
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
(require 'treesit)
|
||||
(require 'js)
|
||||
(eval-when-compile (require 'rx))
|
||||
(require 'c-ts-mode) ; For comment indent and filling.
|
||||
|
||||
(declare-function treesit-parser-create "treesit.c")
|
||||
|
||||
|
|
@ -73,8 +74,9 @@ Argument LANGUAGE is either `typescript' or `tsx'."
|
|||
((node-is ")") parent-bol 0)
|
||||
((node-is "]") parent-bol 0)
|
||||
((node-is ">") parent-bol 0)
|
||||
((and (parent-is "comment") comment-end) comment-start -1)
|
||||
((parent-is "comment") comment-start-skip 0)
|
||||
((and (parent-is "comment") c-ts-mode--looking-at-star)
|
||||
c-ts-mode--comment-start-after-first-star -1)
|
||||
((parent-is "comment") prev-adaptive-prefix 0)
|
||||
((parent-is "ternary_expression") parent-bol typescript-ts-mode-indent-offset)
|
||||
((parent-is "member_expression") parent-bol typescript-ts-mode-indent-offset)
|
||||
((parent-is "named_imports") parent-bol typescript-ts-mode-indent-offset)
|
||||
|
|
@ -331,18 +333,12 @@ Argument LANGUAGE is either `typescript' or `tsx'."
|
|||
:syntax-table typescript-ts-mode--syntax-table
|
||||
|
||||
;; Comments.
|
||||
(setq-local comment-start "// ")
|
||||
(setq-local comment-end "")
|
||||
(setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
|
||||
(setq-local comment-end-skip
|
||||
(rx (* (syntax whitespace))
|
||||
(group (or (syntax comment-end)
|
||||
(seq (+ "*") "/")))))
|
||||
(c-ts-mode-comment-setup)
|
||||
(setq-local treesit-defun-prefer-top-level t)
|
||||
|
||||
(setq-local treesit-text-type-regexp
|
||||
(regexp-opt '("comment"
|
||||
"template_string")))
|
||||
(setq-local treesit-defun-prefer-top-level t)
|
||||
|
||||
;; Electric
|
||||
(setq-local electric-indent-chars
|
||||
|
|
@ -354,11 +350,17 @@ Argument LANGUAGE is either `typescript' or `tsx'."
|
|||
"method_definition"
|
||||
"function_declaration"
|
||||
"lexical_declaration")))
|
||||
;; Imenu.
|
||||
(setq-local imenu-create-index-function #'js--treesit-imenu)
|
||||
(setq-local treesit-defun-name-function #'js--treesit-defun-name)
|
||||
|
||||
;; Which-func (use imenu).
|
||||
(setq-local which-func-functions nil))
|
||||
;; Imenu (same as in `js-ts-mode').
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
`(("Function" "\\`function_declaration\\'" nil nil)
|
||||
("Variable" "\\`lexical_declaration\\'"
|
||||
js--treesit-valid-imenu-entry nil)
|
||||
("Class" ,(rx bos (or "class_declaration"
|
||||
"method_definition")
|
||||
eos)
|
||||
nil nil))))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode typescript-ts-mode typescript-ts-base-mode "TypeScript"
|
||||
|
|
|
|||
|
|
@ -399,7 +399,8 @@ but the property value is `t', then check the last key."
|
|||
(defcustom repeat-echo-function #'repeat-echo-message
|
||||
"Function to display a hint about available keys.
|
||||
Function is called after every repeatable command with one argument:
|
||||
a repeating map, or nil after deactivating the transient repeating mode."
|
||||
a repeating map, or nil after deactivating the transient repeating mode.
|
||||
You can use `add-function' for multiple functions simultaneously."
|
||||
:type '(choice (const :tag "Show hints in the echo area"
|
||||
repeat-echo-message)
|
||||
(const :tag "Show indicator in the mode line"
|
||||
|
|
|
|||
|
|
@ -6905,11 +6905,8 @@ sentence (see Info node `(elisp) Documentation Tips')."
|
|||
|
||||
(defun json-available-p ()
|
||||
"Return non-nil if Emacs has libjansson support."
|
||||
(and (fboundp 'json-serialize)
|
||||
(condition-case nil
|
||||
(json-serialize t)
|
||||
(:success t)
|
||||
(json-unavailable nil))))
|
||||
(and (fboundp 'json--available-p)
|
||||
(json--available-p)))
|
||||
|
||||
(defun ensure-list (object)
|
||||
"Return OBJECT as a list.
|
||||
|
|
|
|||
|
|
@ -572,9 +572,14 @@ For use in `tab-line-tab-face-functions'."
|
|||
|
||||
(defvar tab-line-auto-hscroll)
|
||||
|
||||
(defun tab-line-cache-key-default (_tabs)
|
||||
(defun tab-line-cache-key-default (tabs)
|
||||
"Return default list of cache keys."
|
||||
(list
|
||||
tabs
|
||||
;; handle buffer renames
|
||||
(buffer-name (window-buffer))
|
||||
;; handle tab-line scrolling
|
||||
(window-parameter nil 'tab-line-hscroll)
|
||||
;; for setting face 'tab-line-tab-current'
|
||||
(mode-line-window-selected-p)
|
||||
;; for `tab-line-tab-face-modified'
|
||||
|
|
@ -591,12 +596,7 @@ of cache keys. You can use `add-function' to add more cache keys.")
|
|||
(defun tab-line-format ()
|
||||
"Format for displaying the tab line of the selected window."
|
||||
(let* ((tabs (funcall tab-line-tabs-function))
|
||||
(cache-key (append (list tabs
|
||||
;; handle buffer renames
|
||||
(buffer-name (window-buffer))
|
||||
;; handle tab-line scrolling
|
||||
(window-parameter nil 'tab-line-hscroll))
|
||||
(funcall tab-line-cache-key-function tabs)))
|
||||
(cache-key (funcall tab-line-cache-key-function tabs))
|
||||
(cache (window-parameter nil 'tab-line-cache)))
|
||||
;; Enable auto-hscroll again after it was disabled on manual scrolling.
|
||||
;; The moment to enable it is when the window-buffer was updated.
|
||||
|
|
|
|||
|
|
@ -1425,33 +1425,6 @@ Return nil if there is no name or if NODE is not a defun node."
|
|||
(treesit-node-start node)
|
||||
(treesit-node-start block)))))))
|
||||
|
||||
(defun css--treesit-imenu-1 (node)
|
||||
"Helper for `css--treesit-imenu'.
|
||||
Find string representation for NODE and set marker, then recurse
|
||||
the subtrees."
|
||||
(let* ((ts-node (car node))
|
||||
(subtrees (mapcan #'css--treesit-imenu-1 (cdr node)))
|
||||
(name (when ts-node
|
||||
(or (treesit-defun-name ts-node)
|
||||
"Anonymous")))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
((or (null ts-node) (null name)) subtrees)
|
||||
(subtrees
|
||||
`((,name ,(cons name marker) ,@subtrees)))
|
||||
(t
|
||||
`((,name . ,marker))))))
|
||||
|
||||
(defun css--treesit-imenu ()
|
||||
"Return Imenu alist for the current buffer."
|
||||
(let* ((node (treesit-buffer-root-node))
|
||||
(tree (treesit-induce-sparse-tree
|
||||
node (rx (or "rule_set" "media_statement"))
|
||||
nil 1000)))
|
||||
(css--treesit-imenu-1 tree)))
|
||||
|
||||
;;; Completion
|
||||
|
||||
(defun css--complete-property ()
|
||||
|
|
@ -1847,8 +1820,9 @@ can also be used to fill comments.
|
|||
'((selector comment query keyword)
|
||||
(property constant string)
|
||||
(error variable function operator bracket)))
|
||||
(setq-local imenu-create-index-function #'css--treesit-imenu)
|
||||
(setq-local which-func-functions nil)
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
`( nil ,(rx bos (or "rule_set" "media_statement") eos)
|
||||
nil nil))
|
||||
(treesit-major-mode-setup)))
|
||||
|
||||
;;;###autoload
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
(declare-function treesit-parser-create "treesit.c")
|
||||
(declare-function treesit-induce-sparse-tree "treesit.c")
|
||||
(declare-function treesit-node-start "treesit.c")
|
||||
(declare-function treesit-node-type "treesit.c")
|
||||
(declare-function treesit-node-child "treesit.c")
|
||||
(declare-function treesit-node-child-by-field-name "treesit.c")
|
||||
|
||||
(defcustom toml-ts-mode-indent-offset 2
|
||||
|
|
@ -112,39 +114,8 @@
|
|||
Return nil if there is no name or if NODE is not a defun node."
|
||||
(pcase (treesit-node-type node)
|
||||
((or "table" "table_array_element")
|
||||
(car (cdr (treesit-node-children node))))))
|
||||
|
||||
(defun toml-ts-mode--imenu-1 (node)
|
||||
"Helper for `toml-ts-mode--imenu'.
|
||||
Find string representation for NODE and set marker, then recurse
|
||||
the subtrees."
|
||||
(let* ((ts-node (car node))
|
||||
(subtrees (mapcan #'toml-ts-mode--imenu-1 (cdr node)))
|
||||
(name (or (treesit-defun-name ts-node)
|
||||
"Root table"))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
((null ts-node) subtrees)
|
||||
(subtrees
|
||||
`((,name ,(cons name marker) ,@subtrees)))
|
||||
(t
|
||||
`((,name . ,marker))))))
|
||||
|
||||
(defun toml-ts-mode--imenu ()
|
||||
"Return Imenu alist for the current buffer."
|
||||
(let* ((node (treesit-buffer-root-node))
|
||||
(table-tree (treesit-induce-sparse-tree
|
||||
node "^table$" nil 1000))
|
||||
(table-array-tree (treesit-induce-sparse-tree
|
||||
node "^table_array_element$" nil 1000))
|
||||
(table-index (toml-ts-mode--imenu-1 table-tree))
|
||||
(table-array-index (toml-ts-mode--imenu-1 table-array-tree)))
|
||||
(append
|
||||
(when table-index `(("Headers" . ,table-index)))
|
||||
(when table-array-index `(("Arrays" . ,table-array-index))))))
|
||||
|
||||
(or (treesit-node-text (treesit-node-child node 1) t)
|
||||
"Root table"))))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.toml\\'" . toml-ts-mode))
|
||||
|
|
@ -179,8 +150,9 @@ the subtrees."
|
|||
(delimiter error)))
|
||||
|
||||
;; Imenu.
|
||||
(setq-local imenu-create-index-function #'toml-ts-mode--imenu)
|
||||
(setq-local which-func-functions nil) ;; Piggyback on imenu
|
||||
(setq-local treesit-simple-imenu-settings
|
||||
'(("Header" "\\`table\\'" nil nil)
|
||||
("Array" "\\`table_array_element\\'" nil nil)))
|
||||
|
||||
(treesit-major-mode-setup)))
|
||||
|
||||
|
|
|
|||
246
lisp/treesit.el
246
lisp/treesit.el
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
;; Copyright (C) 2021-2022 Free Software Foundation, Inc.
|
||||
|
||||
;; Maintainer: 付禹安 (Yuan Fu) <casouri@gmail.com>
|
||||
;; Keywords: treesit, tree-sitter, languages
|
||||
;; Package: emacs
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||||
|
|
@ -230,19 +234,27 @@ is nil, try to guess the language at BEG using `treesit-language-at'."
|
|||
(or parser-or-lang (treesit-language-at beg))))))
|
||||
(treesit-node-descendant-for-range root beg (or end beg) named)))
|
||||
|
||||
(defun treesit-node-top-level (node &optional type)
|
||||
(defun treesit-node-top-level (node &optional pred include-node)
|
||||
"Return the top-level equivalent of NODE.
|
||||
|
||||
Specifically, return the highest parent of NODE that has the same
|
||||
type as it. If no such parent exists, return nil.
|
||||
|
||||
If TYPE is non-nil, match each parent's type with TYPE as a
|
||||
regexp, rather than using NODE's type."
|
||||
(let ((type (or type (treesit-node-type node)))
|
||||
If PRED is non-nil, match each parent's type with PRED as a
|
||||
regexp, rather than using NODE's type. PRED can also be a
|
||||
function that takes the node as an argument, and return
|
||||
non-nil/nil for match/no match.
|
||||
|
||||
If INCLUDE-NODE is non-nil, return NODE if it satisfies PRED."
|
||||
(let ((pred (or pred (treesit-node-type node)))
|
||||
(result nil))
|
||||
(cl-loop for cursor = (treesit-node-parent node)
|
||||
(cl-loop for cursor = (if include-node node
|
||||
(treesit-node-parent node))
|
||||
then (treesit-node-parent cursor)
|
||||
while cursor
|
||||
if (string-match-p type (treesit-node-type cursor))
|
||||
if (if (stringp pred)
|
||||
(string-match-p pred (treesit-node-type cursor))
|
||||
(funcall pred cursor))
|
||||
do (setq result cursor))
|
||||
result))
|
||||
|
||||
|
|
@ -286,11 +298,16 @@ properties."
|
|||
(treesit-node-start node)
|
||||
(treesit-node-end node))))))
|
||||
|
||||
(defun treesit-parent-until (node pred)
|
||||
(defun treesit-parent-until (node pred &optional include-node)
|
||||
"Return the closest parent of NODE that satisfies PRED.
|
||||
|
||||
Return nil if none was found. PRED should be a function that
|
||||
takes one argument, the parent node."
|
||||
(let ((node (treesit-node-parent node)))
|
||||
takes one argument, the parent node, and return non-nil/nil for
|
||||
match/no match.
|
||||
|
||||
If INCLUDE-NODE is non-nil, return NODE if it satisfies PRED."
|
||||
(let ((node (if include-node node
|
||||
(treesit-node-parent node))))
|
||||
(while (and node (not (funcall pred node)))
|
||||
(setq node (treesit-node-parent node)))
|
||||
node))
|
||||
|
|
@ -305,8 +322,6 @@ takes one argument, the parent node."
|
|||
node (treesit-node-parent node)))
|
||||
last))
|
||||
|
||||
(defalias 'treesit-traverse-parent #'treesit-parent-until)
|
||||
|
||||
(defun treesit-node-children (node &optional named)
|
||||
"Return a list of NODE's children.
|
||||
If NAMED is non-nil, collect named child only."
|
||||
|
|
@ -1644,7 +1659,7 @@ For example, \"(function|class)_definition\".
|
|||
|
||||
Sometimes not all nodes matched by the regexp are valid defuns.
|
||||
In that case, set this variable to a cons cell of the
|
||||
form (REGEXP . FILTER), where FILTER is a function that takes a
|
||||
form (REGEXP . PRED), where PRED is a function that takes a
|
||||
node (the matched node) and returns t if node is valid, or nil
|
||||
for invalid node.
|
||||
|
||||
|
|
@ -1793,78 +1808,67 @@ sound things exists.
|
|||
|
||||
REGEXP and PRED are the same as in `treesit-thing-at-point'."
|
||||
(let* ((node (treesit-node-at pos))
|
||||
;; NODE-BEFORE/AFTER = NODE when POS is completely in NODE,
|
||||
;; but if not, that means point could be in between two
|
||||
;; defun, in that case we want to use a node that's actually
|
||||
;; before/after point.
|
||||
(node-before (if (>= (treesit-node-start node) pos)
|
||||
(save-excursion
|
||||
(treesit-search-forward-goto node "" t t t))
|
||||
node))
|
||||
(node-after (if (<= (treesit-node-end node) pos)
|
||||
(save-excursion
|
||||
(treesit-search-forward-goto
|
||||
node "" nil nil t))
|
||||
node))
|
||||
(result (list nil nil nil))
|
||||
(pred (or pred (lambda (_) t))))
|
||||
(result (list nil nil nil)))
|
||||
;; 1. Find previous and next sibling defuns.
|
||||
(cl-loop
|
||||
for idx from 0 to 1
|
||||
for node in (list node-before node-after)
|
||||
for backward in '(t nil)
|
||||
;; Make sure we go in the right direction, and the defun we find
|
||||
;; doesn't cover POS.
|
||||
for pos-pred in (list (lambda (n) (<= (treesit-node-end n) pos))
|
||||
(lambda (n) (>= (treesit-node-start n) pos)))
|
||||
;; If point is inside a defun, our process below will never
|
||||
;; return a next/prev sibling outside of that defun, effectively
|
||||
;; any prev/next sibling is locked inside the smallest defun
|
||||
;; covering point, which is the correct behavior. That's because
|
||||
;; when there exists a defun that covers point,
|
||||
;; `treesit-search-forward' will first reach that defun, after
|
||||
;; that we only go upwards in the tree, so other defuns outside
|
||||
;; of the covering defun is never reached. (Don't use
|
||||
;; `treesit-search-forward-goto' as it breaks when NODE-AFTER is
|
||||
;; the last token of a parent defun: it will skip the parent
|
||||
;; defun because it wants to ensure progress.)
|
||||
do (cl-loop for cursor = (when node
|
||||
(save-excursion
|
||||
(treesit-search-forward
|
||||
node regexp backward backward)))
|
||||
then (treesit-node-parent cursor)
|
||||
while cursor
|
||||
if (and (string-match-p
|
||||
regexp (treesit-node-type cursor))
|
||||
(funcall pred cursor)
|
||||
(funcall pos-pred cursor))
|
||||
do (setf (nth idx result) cursor)))
|
||||
;; We repeatedly find next defun candidate with
|
||||
;; `treesit-search-forward', and check if it is a valid defun,
|
||||
;; until the node we find covers POS, meaning we've gone through
|
||||
;; every possible sibling defuns. But there is a catch:
|
||||
;; `treesit-search-forward' searches bottom-up, so for each
|
||||
;; candidate we need to go up the tree and find the top-most
|
||||
;; valid sibling, this defun will be at the same level as POS.
|
||||
;; Don't use `treesit-search-forward-goto', it skips nodes in
|
||||
;; order to enforce progress.
|
||||
when node
|
||||
do (let ((cursor node)
|
||||
(iter-pred (lambda (node)
|
||||
(and (string-match-p
|
||||
regexp (treesit-node-type node))
|
||||
(or (null pred) (funcall pred node))
|
||||
(funcall pos-pred node)))))
|
||||
;; Find the node just before/after POS to start searching.
|
||||
(save-excursion
|
||||
(while (and cursor (not (funcall pos-pred cursor)))
|
||||
(setq cursor (treesit-search-forward-goto
|
||||
cursor "" backward backward t))))
|
||||
;; Keep searching until we run out of candidates.
|
||||
(while (and cursor
|
||||
(funcall pos-pred cursor)
|
||||
(null (nth idx result)))
|
||||
(setf (nth idx result)
|
||||
(treesit-node-top-level cursor iter-pred t))
|
||||
(setq cursor (treesit-search-forward
|
||||
cursor regexp backward backward)))))
|
||||
;; 2. Find the parent defun.
|
||||
(setf (nth 2 result)
|
||||
(cl-loop for cursor = (or (nth 0 result)
|
||||
(nth 1 result)
|
||||
node)
|
||||
then (treesit-node-parent cursor)
|
||||
while cursor
|
||||
if (and (string-match-p
|
||||
regexp (treesit-node-type cursor))
|
||||
(funcall pred cursor)
|
||||
(not (member cursor result)))
|
||||
return cursor))
|
||||
(let ((cursor (or (nth 0 result) (nth 1 result) node))
|
||||
(iter-pred (lambda (node)
|
||||
(and (string-match-p
|
||||
regexp (treesit-node-type node))
|
||||
(or (null pred) (funcall pred node))
|
||||
(not (treesit-node-eq node (nth 0 result)))
|
||||
(not (treesit-node-eq node (nth 1 result)))
|
||||
(< (treesit-node-start node)
|
||||
pos
|
||||
(treesit-node-end node))))))
|
||||
(setf (nth 2 result)
|
||||
(treesit-parent-until cursor iter-pred)))
|
||||
result))
|
||||
|
||||
(defun treesit--top-level-thing (node regexp &optional pred)
|
||||
"Return the top-level parent thing of NODE.
|
||||
REGEXP and PRED are the same as in `treesit-thing-at-point'."
|
||||
(let* ((pred (or pred (lambda (_) t))))
|
||||
;; `treesit-search-forward-goto' will make sure the matched node
|
||||
;; is before POS.
|
||||
(cl-loop for cursor = node
|
||||
then (treesit-node-parent cursor)
|
||||
while cursor
|
||||
if (and (string-match-p
|
||||
regexp (treesit-node-type cursor))
|
||||
(funcall pred cursor))
|
||||
do (setq node cursor))
|
||||
node))
|
||||
(treesit-node-top-level
|
||||
node (lambda (node)
|
||||
(and (string-match-p regexp (treesit-node-type node))
|
||||
(or (null pred) (funcall pred node))))
|
||||
t))
|
||||
|
||||
;; The basic idea for nested defun navigation is that we first try to
|
||||
;; move across sibling defuns in the same level, if no more siblings
|
||||
|
|
@ -2040,6 +2044,91 @@ The delimiter between nested defun names is controlled by
|
|||
(setq node (treesit-node-parent node)))
|
||||
name))
|
||||
|
||||
;;; Imenu
|
||||
|
||||
(defvar treesit-simple-imenu-settings nil
|
||||
"Settings that configure `treesit-simple-imenu'.
|
||||
|
||||
It should be a list of (CATEGORY REGEXP PRED NAME-FN).
|
||||
|
||||
CATEGORY is the name of a category, like \"Function\", \"Class\",
|
||||
etc. REGEXP should be a regexp matching the type of nodes that
|
||||
belong to CATEGORY. PRED should be either nil or a function
|
||||
that takes a node an the argument. It should return non-nil if
|
||||
the node is a valid node for CATEGORY, or nil if not.
|
||||
|
||||
CATEGORY could also be nil. In that case the entries matched by
|
||||
REGEXP and PRED are not grouped under CATEGORY.
|
||||
|
||||
NAME-FN should be either nil or a function that takes a defun
|
||||
node and returns the name of that defun node. If NAME-FN is nil,
|
||||
`treesit-defun-name' is used.
|
||||
|
||||
`treesit-major-mode-setup' automatically sets up Imenu if this
|
||||
variable is non-nil.")
|
||||
|
||||
(defun treesit--simple-imenu-1 (node pred name-fn)
|
||||
"Given a sparse tree, create an Imenu index.
|
||||
|
||||
NODE is a node in the tree returned by
|
||||
`treesit-induce-sparse-tree' (not a tree-sitter node, its car is
|
||||
a tree-sitter node). Walk that tree and return an Imenu index.
|
||||
|
||||
Return a list of entries where each ENTRY has the form:
|
||||
|
||||
ENTRY := (NAME . MARKER)
|
||||
| (NAME . ((\" \" . MARKER)
|
||||
ENTRY
|
||||
...)
|
||||
|
||||
PRED and NAME-FN are the same as described in
|
||||
`treesit-simple-imenu-settings'. NAME-FN computes NAME in an
|
||||
ENTRY. MARKER marks the start of each tree-sitter node."
|
||||
(let* ((ts-node (car node))
|
||||
(children (cdr node))
|
||||
(subtrees (mapcan (lambda (node)
|
||||
(treesit--simple-imenu-1 node pred name-fn))
|
||||
children))
|
||||
;; The root of the tree could have a nil ts-node.
|
||||
(name (when ts-node
|
||||
(or (if name-fn
|
||||
(funcall name-fn ts-node)
|
||||
(treesit-defun-name ts-node))
|
||||
"Anonymous")))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
;; The tree-sitter node in the root node of the tree returned by
|
||||
;; `treesit-induce-sparse-tree' is often nil.
|
||||
((null ts-node)
|
||||
subtrees)
|
||||
;; This tree-sitter node is not a valid entry, skip it.
|
||||
((and pred (not (funcall pred ts-node)))
|
||||
subtrees)
|
||||
;; Non-leaf node, return a (list of) subgroup.
|
||||
(subtrees
|
||||
`((,name
|
||||
,(cons " " marker)
|
||||
,@subtrees)))
|
||||
;; Leaf node, return a (list of) plain index entry.
|
||||
(t (list (cons name marker))))))
|
||||
|
||||
(defun treesit-simple-imenu ()
|
||||
"Return an Imenu index for the current buffer."
|
||||
(let ((root (treesit-buffer-root-node)))
|
||||
(mapcan (lambda (setting)
|
||||
(pcase-let ((`(,category ,regexp ,pred ,name-fn)
|
||||
setting))
|
||||
(when-let* ((tree (treesit-induce-sparse-tree
|
||||
root regexp))
|
||||
(index (treesit--simple-imenu-1
|
||||
tree pred name-fn)))
|
||||
(if category
|
||||
(list (cons category index))
|
||||
index))))
|
||||
treesit-simple-imenu-settings)))
|
||||
|
||||
;;; Activating tree-sitter
|
||||
|
||||
(defun treesit-ready-p (language &optional quiet)
|
||||
|
|
@ -2097,6 +2186,11 @@ If `treesit-simple-indent-rules' is non-nil, setup indentation.
|
|||
If `treesit-defun-type-regexp' is non-nil, setup
|
||||
`beginning/end-of-defun' functions.
|
||||
|
||||
If `treesit-defun-name-function' is non-nil, setup
|
||||
`add-log-current-defun'.
|
||||
|
||||
If `treesit-simple-imenu-settings' is non-nil, setup Imenu.
|
||||
|
||||
Make sure necessary parsers are created for the current buffer
|
||||
before calling this function."
|
||||
;; Font-lock.
|
||||
|
|
@ -2138,7 +2232,13 @@ before calling this function."
|
|||
(when treesit-defun-name-function
|
||||
(setq-local add-log-current-defun-function
|
||||
#'treesit-add-log-current-defun))
|
||||
(setq-local transpose-sexps-function #'treesit-transpose-sexps))
|
||||
|
||||
(setq-local transpose-sexps-function #'treesit-transpose-sexps)
|
||||
|
||||
;; Imenu.
|
||||
(when treesit-simple-imenu-settings
|
||||
(setq-local imenu-create-index-function
|
||||
#'treesit-simple-imenu)))
|
||||
|
||||
;;; Debugging
|
||||
|
||||
|
|
|
|||
|
|
@ -272,8 +272,7 @@ and hunk-based syntax highlighting otherwise as a fallback."
|
|||
|
||||
(defcustom diff-minor-mode-prefix "\C-c="
|
||||
"Prefix key for `diff-minor-mode' commands."
|
||||
:type '(choice (string "ESC")
|
||||
(string "\C-c=") string))
|
||||
:type '(choice (string "\e") (string "\C-c=") string))
|
||||
|
||||
(defvar-keymap diff-minor-mode-map
|
||||
:doc "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue