commit f0e63558bd3dfd9e57cacfba8690f9dd29774947 (HEAD, refs/remotes/origin/master) Author: Yuan Fu Date: Sun Jan 12 23:41:47 2025 -0800 Add 'and', 'named', and 'anonymous' predicate for tree-sitter * doc/lispref/parsing.texi (User-defined Things): Mention the new predicate. * src/treesit.c (treesit_traverse_validate_predicate): Recognize named, anonymous, and and predicates. (treesit_traverse_match_predicate): Handle named, anonymous, and and predicates. diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi index e16aea6a38d..3e44f31c12c 100644 --- a/doc/lispref/parsing.texi +++ b/doc/lispref/parsing.texi @@ -1596,6 +1596,8 @@ the thing. @var{pred} can also be recursively defined. It can be @w{@code{(or @var{pred}@dots{})}}, meaning that satisfying any one of the @var{pred}s +qualifies the node as the thing. It can be @w{@code{(and +@var{pred}@dots{})}}, meaning that satisfying all of the @var{pred}s qualifies the node as the thing. It can be @w{@code{(not @var{pred})}}, meaning that not satisfying @var{pred} qualifies the node. @@ -1604,6 +1606,10 @@ list. For example, @w{@code{(or sexp sentence)}} defines something that's either a @code{sexp} thing or a @code{sentence} thing, as defined by some other rule in the alist. +There are two pre-defined predicates: @code{named} and @code{anonymous}, +that qualifies named and anonymous nodes, respectively. They can be +combined with @code{and} to narrow down the match. + Here's an example @code{treesit-thing-settings} for C and C++: @example @@ -1662,7 +1668,6 @@ signals @code{treesit-invalid-predicate} error. If @var{ignore-missing} is @code{t}, this function doesn't signal the error when @var{thing} is undefined and just returns @code{nil}; but it still signals the error if @var{thing} is a malformed predicate. - @end defun @defun treesit-thing-prev position thing @@ -2179,8 +2184,6 @@ navigation commands that move, respectively, by sexps and sentences by defining variables such as @code{forward-sexp-function} and @code{forward-sentence-function}. @end itemize - -@c TODO: Add treesit-thing-settings stuff once we finalize it. @end defun For more information on these built-in tree-sitter features, diff --git a/src/treesit.c b/src/treesit.c index 878c1f0b340..918b1a510ea 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -3623,6 +3623,9 @@ treesit_traverse_validate_predicate (Lisp_Object pred, return true; else if (SYMBOLP (pred)) { + if (BASE_EQ (pred, Qnamed) || BASE_EQ (pred, Qanonymous)) + return true; + Lisp_Object definition = treesit_traverse_get_predicate (pred, language); if (NILP (definition)) @@ -3667,13 +3670,13 @@ treesit_traverse_validate_predicate (Lisp_Object pred, signal_data, recursion_level + 1); } - else if (BASE_EQ (car, Qor)) + else if (BASE_EQ (car, Qor) || BASE_EQ (car, Qand)) { if (!CONSP (cdr) || NILP (cdr)) { *signal_data = list3 (Qtreesit_invalid_predicate, - build_string ("`or' must have a list " - "of patterns as " + build_string ("`or' or `and' must have " + "a list of patterns as " "arguments "), pred); return false; @@ -3729,6 +3732,14 @@ treesit_traverse_match_predicate (TSTreeCursor *cursor, Lisp_Object pred, Lisp_Object lisp_node = make_treesit_node (parser, node); return !NILP (CALLN (Ffuncall, pred, lisp_node)); } + else if (SYMBOLP (pred) && BASE_EQ (pred, Qnamed)) + { + return ts_node_is_named (node); + } + else if (SYMBOLP (pred) && BASE_EQ (pred, Qanonymous)) + { + return !ts_node_is_named (node); + } else if (SYMBOLP (pred)) { Lisp_Object language = XTS_PARSER (parser)->language_symbol; @@ -3755,6 +3766,16 @@ treesit_traverse_match_predicate (TSTreeCursor *cursor, Lisp_Object pred, } return false; } + else if (BASE_EQ (car, Qand)) + { + FOR_EACH_TAIL (cdr) + { + if (!treesit_traverse_match_predicate (cursor, XCAR (cdr), + parser, named)) + return false; + } + return true; + } else if (STRINGP (car) && FUNCTIONP (cdr)) { /* A bit of code duplication here, but should be fine. */ @@ -4297,6 +4318,7 @@ syms_of_treesit (void) DEFSYM (Qtreesit_compiled_query_p, "treesit-compiled-query-p"); DEFSYM (Qtreesit_query_p, "treesit-query-p"); DEFSYM (Qnamed, "named"); + DEFSYM (Qanonymous, "anonymous"); DEFSYM (Qmissing, "missing"); DEFSYM (Qextra, "extra"); DEFSYM (Qoutdated, "outdated"); @@ -4338,6 +4360,7 @@ syms_of_treesit (void) DEFSYM (Qtreesit_thing_symbol, "treesit-thing-symbol"); DEFSYM (Qor, "or"); + DEFSYM (Qand, "and"); #ifdef WINDOWSNT DEFSYM (Qtree_sitter, "tree-sitter"); @@ -4420,8 +4443,12 @@ cons (REGEXP . FN), which is a combination of a regexp and a predicate function, and the node has to match both to qualify as the thing. PRED can also be recursively defined. It can be (or PRED...), meaning -satisfying anyone of the inner PREDs qualifies the node; or (not -PRED), meaning not satisfying the inner PRED qualifies the node. +satisfying anyone of the inner PREDs qualifies the node; or (and +PRED...) meaning satisfying all of the inner PREDs qualifies the node; +or (not PRED), meaning not satisfying the inner PRED qualifies the node. + +There are two pre-defined predicates, `named' and `anonymous`. They +match named nodes and anonymous nodes, respectively. Finally, PRED can refer to other THINGs defined in this list by using the symbol of that THING. For example, (or sexp sentence). */); commit 4687fff4f0a473fd887bf0800976c356eecd7eb2 Author: Yuan Fu Date: Sun Jan 12 23:38:24 2025 -0800 ; Move c-ts-mode keyword variables forward * lisp/progmodes/c-ts-mode.el (c-ts-mode--type-keywords): (c-ts-mode--operators): (c-ts-mode--c++-operators): (c-ts-mode--c++-operator-keywords): Move before c-ts-mode--keywords to avoid undefined variable error. diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index d0ed110abbb..213c748ad2d 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -635,6 +635,25 @@ NODE, PARENT, BOL, ARGS are as usual." "__fastcall" "__thiscall" "__vectorcall" "_unaligned" "__unaligned") "MSVC keywords.") +(defvar c-ts-mode--type-keywords + '("long" "short" "signed" "unsigned") + "Keywords that should be considered as part of a type.") + +(defvar c-ts-mode--operators + '("=" "-" "*" "/" "+" "%" "~" "|" "&" "^" "<<" ">>" "->" + "." "<" "<=" ">=" ">" "==" "!=" "!" "&&" "||" "-=" + "+=" "*=" "/=" "%=" "|=" "&=" "^=" ">>=" "<<=" "--" "++") + "C/C++ operators for tree-sitter font-locking.") + +(defvar c-ts-mode--c++-operators + '(".*" "->*" "<=>") + "C++ operators that aren't supported by C.") + +(defvar c-ts-mode--c++-operator-keywords + '("and" "and_eq" "bitand" "bitor" "compl" "not" "not_eq" "or" "or_eq" + "xor" "xor_eq") + "C++ operators that we fontify as keywords.") + (defun c-ts-mode--compute-optional-keywords (mode) "Return a list of keywords that are supported by the grammar. MODE should be either `c' or `cpp'." @@ -678,25 +697,6 @@ MODE is either `c' or `cpp'." "thread_local")) (append '("auto") c-keywords)))) -(defvar c-ts-mode--type-keywords - '("long" "short" "signed" "unsigned") - "Keywords that should be considered as part of a type.") - -(defvar c-ts-mode--operators - '("=" "-" "*" "/" "+" "%" "~" "|" "&" "^" "<<" ">>" "->" - "." "<" "<=" ">=" ">" "==" "!=" "!" "&&" "||" "-=" - "+=" "*=" "/=" "%=" "|=" "&=" "^=" ">>=" "<<=" "--" "++") - "C/C++ operators for tree-sitter font-locking.") - -(defvar c-ts-mode--c++-operators - '(".*" "->*" "<=>") - "C++ operators that aren't supported by C.") - -(defvar c-ts-mode--c++-operator-keywords - '("and" "and_eq" "bitand" "bitor" "compl" "not" "not_eq" "or" "or_eq" - "xor" "xor_eq") - "C++ operators that we fontify as keywords.") - (defvar c-ts-mode--for-each-tail-regexp (rx "FOR_EACH_" (or "TAIL" "TAIL_SAFE" "ALIST_VALUE" "LIVE_BUFFER" "FRAME")) commit 8cfa3447b71a3586378e90e5690da6c1faaa77b6 Author: Yuan Fu Date: Sun Jan 12 23:03:41 2025 -0800 Add special indent rule for FOR_EACH_TAIL in c-ts-mode * lisp/progmodes/c-ts-mode.el: (c-ts-mode--emacs-macro-rules): New function (c-ts-mode--simple-indent-rules): Add new rule. diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 99e624412b2..d0ed110abbb 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -445,6 +445,18 @@ NODE and PARENT are the same as other indent rules." (cons (funcall parent-bol) c-ts-mode-indent-offset)))))) +(defun c-ts-mode--emacs-macro-rules (_ parent &rest _) + "Rules for indenting macros in Emacs C source. + +PARENT is the same as other simple-indent rules." + (cond + ((and (treesit-node-match-p parent "function_definition") + (equal (treesit-node-text + (treesit-node-child-by-field-name parent "type")) + "FOR_EACH_TAIL")) + (cons (treesit-node-start parent) + c-ts-mode-indent-offset)))) + (defun c-ts-mode--simple-indent-rules (mode style) "Return the indent rules for MODE and STYLE. @@ -466,6 +478,7 @@ MODE can be `c' or `cpp'. STYLE can be `gnu', `k&r', `linux', `bsd'." c-ts-mode--label-indent-rules ,@c-ts-mode--preproc-indent-rules c-ts-mode--macro-heuristic-rules + c-ts-mode--emacs-macro-rules ;; Make sure type and function definition components align and ;; don't indent. Also takes care of GNU style opening braces. commit 04032cd00af7617c709140f3c80b5604b05c11e8 Author: Yuan Fu Date: Sun Jan 12 22:23:36 2025 -0800 Fix c-ts-mode indentation (bug#75442) * lisp/progmodes/c-ts-mode.el (c-ts-mode--simple-indent-rules): Use standalone-parent instead of parent. * test/lisp/progmodes/c-ts-mode-resources/indent.erts: New test. diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index f4f6bef2775..99e624412b2 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -474,7 +474,7 @@ MODE can be `c' or `cpp'. STYLE can be `gnu', `k&r', `linux', `bsd'." "enum_specifier" "function_declarator" "template_declaration"))) - parent 0) + standalone-parent 0) ;; This is for the trailing-star stype: int * ;; func() ((match "function_declarator" nil "declarator") parent-bol 0) diff --git a/test/lisp/progmodes/c-ts-mode-resources/indent.erts b/test/lisp/progmodes/c-ts-mode-resources/indent.erts index 691f5b6ecfd..3d875e3113e 100644 --- a/test/lisp/progmodes/c-ts-mode-resources/indent.erts +++ b/test/lisp/progmodes/c-ts-mode-resources/indent.erts @@ -149,6 +149,16 @@ fn() }; =-=-= +Name: typedef with struct definition (bug#75442) + +=-= +typedef struct Point +{ + int x; + int y; +} Point; +=-=-= + Name: Multiline Parameter List (bug#60398) =-= commit 7d3bc1ff2f44215ad24fdd8b243e7cee8ff66fa0 Author: Yuan Fu Date: Sun Jan 12 00:35:20 2025 -0800 Add newly supported keywords to c-ts-mode (bug#75226) For the new keywords, test if the grammar supports them before useing it. * lisp/progmodes/c-ts-mode.el: (c-ts-mode--optional-c-keywords): New variable. (c-ts-mode--ms-keywords): New variable. (c-ts-mode--compute-optional-keywords): New function. (c-ts-mode--keywords): Add new optional keywords. (c-ts-mode--c++-operators): New variable. (c-ts-mode--c++-operator-keywords): New variable. (c-ts-mode--font-lock-settings): Use c-ts-mode--c++-operators. diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index dd08731edb4..f4f6bef2775 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -593,30 +593,76 @@ NODE, PARENT, BOL, ARGS are as usual." "#else" "#elif" "#endif" "#include") "C/C++ keywords for tree-sitter font-locking.") +(defvar c-ts-mode--optional-c-keywords + ;; v0.20.4 actually contains all the new keywords I can find that + ;; aren't in c-ts-mode before. The version doesn't really matter, we + ;; just want a rough grouping so that we can enable as much keywords + ;; as possible. + '(("v0.20.4" . ("_Generic" "_Noreturn" "noreturn" + "__attribute__" "__restrict__" + "offsetof" "thread_local")) + ("v0.20.5" . ("__extension__" "__extension__" + "__forceinline" "__inline" "__inline__" + "__thread" + "__alignof__" "__alignof" "alignof" "_Alignof")) + ("v0.20.7" . ("__try" "__except" "__finally" "__leave")) + ;; GitHub release jumped from v0.20.7 to v0.23.1. There are + ;; intermediate git tags, but there aren't many keywords here, so I + ;; skipped intermediate versions too. + ("v0.23.1" ("_Nonnull" "__attribute" + "alignas" "_Alignas" "__asm" "__volatile__")) + ;; No changes to keywords in v0.23.2, v0.23.3, v0.23.4. + ) + "Keywords added in each tree-sitter-c version.") + +(defvar c-ts-mode--ms-keywords + ;; For some reason, "__restrict" "__uptr" and "__sptr" are not + ;; recognized by the grammar, although being in grammar.js. + '("__declspec" "__based" "__cdecl" "__clrcall" "__stdcall" + "__fastcall" "__thiscall" "__vectorcall" "_unaligned" "__unaligned") + "MSVC keywords.") + +(defun c-ts-mode--compute-optional-keywords (mode) + "Return a list of keywords that are supported by the grammar. +MODE should be either `c' or `cpp'." + (if (eq mode 'c) + (mapcan + (lambda (entry) + (let ((keywords (cdr entry))) + (if (ignore-errors + (treesit-query-compile 'c `([,@keywords] @cap) t) + t) + (copy-sequence keywords) + nil))) + c-ts-mode--optional-c-keywords) + ;; As for now, there aren't additional optional keywords for C++. + ())) + (defun c-ts-mode--keywords (mode) "C/C++ keywords for tree-sitter font-locking. MODE is either `c' or `cpp'." (let ((c-keywords - '("_Atomic" "break" "case" "const" "continue" + `("_Atomic" "break" "case" "const" "continue" "default" "do" "else" "enum" "extern" "for" "goto" "if" "inline" "register" "restrict" "return" "sizeof" "static" "struct" "switch" "typedef" "union" - "volatile" "while"))) + "volatile" "while" + ,@c-ts-mode--ms-keywords + ,@(c-ts-mode--compute-optional-keywords mode)))) (if (eq mode 'cpp) (append c-keywords - '("and" "and_eq" "bitand" "bitor" - "catch" "class" "co_await" "co_return" - "co_yield" "compl" "concept" "consteval" + c-ts-mode--c++-operator-keywords + '("catch" "class" "co_await" "co_return" + "co_yield" "concept" "consteval" "constexpr" "constinit" "decltype" "delete" "explicit" "final" "friend" "mutable" "namespace" "new" "noexcept" - "not" "not_eq" "operator" "or" - "or_eq" "override" "private" "protected" - "public" "requires" "template" "throw" + "operator" "override" "private" "protected" + "public" "requires" "static_assert" "template" "throw" "try" "typename" "using" - "xor" "xor_eq" "thread_local")) + "thread_local")) (append '("auto") c-keywords)))) (defvar c-ts-mode--type-keywords @@ -629,6 +675,15 @@ MODE is either `c' or `cpp'." "+=" "*=" "/=" "%=" "|=" "&=" "^=" ">>=" "<<=" "--" "++") "C/C++ operators for tree-sitter font-locking.") +(defvar c-ts-mode--c++-operators + '(".*" "->*" "<=>") + "C++ operators that aren't supported by C.") + +(defvar c-ts-mode--c++-operator-keywords + '("and" "and_eq" "bitand" "bitor" "compl" "not" "not_eq" "or" "or_eq" + "xor" "xor_eq") + "C++ operators that we fontify as keywords.") + (defvar c-ts-mode--for-each-tail-regexp (rx "FOR_EACH_" (or "TAIL" "TAIL_SAFE" "ALIST_VALUE" "LIVE_BUFFER" "FRAME")) @@ -697,7 +752,9 @@ MODE is either `c' or `cpp'." :language mode :feature 'operator - `([,@c-ts-mode--operators] @font-lock-operator-face + `([,@c-ts-mode--operators + ,@(when (eq mode 'cpp) c-ts-mode--c++-operators)] + @font-lock-operator-face "!" @font-lock-negation-char-face) :language mode commit e6591b549f35af1be31f5bf3547e1170b7604a99 Author: Stephen Gildea Date: Sun Jan 12 20:39:02 2025 -0800 New symbol property 'definition-type' used by 'find-function'. * lisp/emacs-lisp/find-func.el (find-function-search-for-symbol): Look for new property 'definition-type' on the symbol. * doc/lispref/symbols.texi (Standard Properties): Add 'Definition-type'. * doc/lispref/functions.texi (Defining Functions): * doc/lispref/tips.texi (Coding Conventions): * doc/misc/ert.texi (How to Write Tests): Add cross reference to new property 'definition-type'. * etc/NEWS: Announce new symbol property 'definition-type'. Thanks to Eli Zaretskii for reviewing this change. diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index 772ffd8a136..bad3c926b27 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -752,9 +752,9 @@ commands, such as @kbd{C-h f}, which present in the @file{*Help*} buffer a button to jump to the function's definition, might be unable to find the source code because generating a function dynamically usually looks very different from the usual static calls to -@code{defun}. You can make the job of finding the code which +@code{defun}. You can make the job of finding the code that generates such functions easier by using the @code{definition-name} -property, @pxref{Standard Properties}. +or @code{definition-type} property, @pxref{Standard Properties}. @cindex override existing functions @cindex redefine existing functions diff --git a/doc/lispref/symbols.texi b/doc/lispref/symbols.texi index acd55fcbb81..508ee13a244 100644 --- a/doc/lispref/symbols.texi +++ b/doc/lispref/symbols.texi @@ -536,10 +536,18 @@ Do not set them directly; they are managed by @code{defcustom} and related functions. @xref{Variable Definitions}. @cindex @code{definition-name} (symbol property) +@cindex @code{definition-type} (symbol property) @item definition-name -This property is used to find the definition of a symbol in the source -code, when it might be hard to find the definition by textual search -of the source file. For example, a @code{define-derived-mode} +@itemx definition-type +These properties help find the definition of a symbol in the source +code when it might be hard to find the definition by textual search +of the source file. +The Emacs Help commands such as @kbd{C-h f} (@pxref{Help,,, +emacs, The GNU Emacs Manual}) use these properties to show the definition +of a symbol via a button in the @file{*Help*} buffer where the +symbol's documentation is shown. + +For example, a @code{define-derived-mode} (@pxref{Derived Modes}) might define a mode-specific function or a variable implicitly; or your Lisp program might generate a run-time call to @code{defun} to define a function (@pxref{Defining @@ -548,10 +556,56 @@ property of the symbol should be another symbol whose definition can be found by textual search and whose code defines the original symbol. In the example with @code{define-derived-mode}, the value of this property of the functions and variables it defines should be the mode -symbol. The Emacs Help commands such as @kbd{C-h f} (@pxref{Help,,, -emacs, The GNU Emacs Manual}) use this property to show the definition -of a symbol via a button in the @file{*Help*} buffer where the -symbol's documentation is shown. +symbol. + +In some cases, the definition cannot be found by looking for the +definition of another symbol. For example, a test file might use a +macro to generate calls to @code{ert-deftest} +(@pxref{,,,ert, ERT: Emacs Lisp Regression Testing}) where the code +is boiler plate and only varying data need to be passed in. +In such cases, the @code{definition-type} property of the symbol can +be a symbol that has an entry in @code{find-function-regexp-alist} +telling how to find the definition of symbols of this type. + +In the example of a macro defining calls to @code{ert-deftest}, +the macro could put the property @code{definition-type} on each +test defined. The file defining the macro would also define a +definition-finding function or regexp and add it to +@code{find-function-regexp-alist} after that variable is loaded. +Here is an example using a function to find the definition: + +@example +@group +(defmacro define-foo-test (data) + "Define a test of the foo system using DATA." + (declare (debug (&rest sexp))) + (let ((test-name (intern (concat ...)))) + `(progn + (put ',test-name 'definition-type 'foo-test-type) + (ert-deftest ,test-name () + ,(concat "Test foo with " ...) + ...)))) +@end group + +@group +(defun foo-find-test-def-function (test-name) + "Search for the `define-foo-test' call defining TEST-NAME. +Return non-nil if the definition is found." + (save-match-data + (let ((regexp ...)) + (save-restriction + (widen) + (goto-char (point-min)) + (re-search-forward regexp nil t))))) +@end group + +@group +(with-eval-after-load "find-func" + (add-to-list + 'find-function-regexp-alist + '(foo-test-type . foo-find-test-def-function))) +@end group +@end example @item disabled If the value is non-@code{nil}, the named function is disabled as a diff --git a/doc/lispref/tips.texi b/doc/lispref/tips.texi index d48921a0bf8..c2fba3cba8d 100644 --- a/doc/lispref/tips.texi +++ b/doc/lispref/tips.texi @@ -226,6 +226,10 @@ The macro should receive the name to be defined as the first argument. That will help various tools find the definition automatically. Avoid constructing the names in the macro itself, since that would confuse these tools. +If your macro cannot be written in this style, the macro can still +help these tools find the defining call by putting the property +@code{definition-name} or @code{definition-type} on the name. +@xref{Standard Properties}. @item In some other systems there is a convention of choosing variable names diff --git a/doc/misc/ert.texi b/doc/misc/ert.texi index 9e60647f3ba..c8aac971ec7 100644 --- a/doc/misc/ert.texi +++ b/doc/misc/ert.texi @@ -518,9 +518,14 @@ can type @code{ert-deftest} forms in a buffer and evaluate them there with @code{eval-defun} or @code{compile-defun}, or you can save the file and load it, optionally byte-compiling it first. -Just like @code{find-function} is only able to find where a function -was defined if the function was loaded from a file, ERT is only able -to find where a test was defined if the test was loaded from a file. +Just like @code{find-function} is able to find where a function was +defined only if the function was loaded from a file, ERT is able to +find where a test was defined only if the test was loaded from a file. + +If the test definition is generated by a macro, the macro may want to +help ERT find the defining call to the macro by putting the property +@code{definition-type} on the test name. +@xref{Standard Properties,,,elisp, GNU Emacs Lisp Reference Manual}. @menu diff --git a/etc/NEWS b/etc/NEWS index 62b90aa11aa..2f04204ad94 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1165,6 +1165,13 @@ It offers a more concise way to create a completion table with metadata. +++ ** 'all-completions' and 'unintern' no longer support old calling conventions. ++++ +** New symbol property 'definition-type' used by 'find-function' and friends. +Macros that define an object in a way that makes the object's name and +the macro call site defining the object hard to associate can put the +property 'definition-type' on the object's name to provide instructions +for finding the definition. + * Changes in Emacs 31.1 on Non-Free Operating Systems diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el index 0837b37023e..643b6aba2a6 100644 --- a/lisp/emacs-lisp/find-func.el +++ b/lisp/emacs-lisp/find-func.el @@ -400,9 +400,12 @@ See `find-library' for more details." Visit the library in a buffer, and return a cons cell (BUFFER . POSITION), or just (BUFFER . nil) if the definition can't be found in the file. -If TYPE is nil, look for a function definition. -Otherwise, TYPE specifies the kind of definition, -and it is interpreted via `find-function-regexp-alist'. +If TYPE is nil, look for a function definition, +otherwise, TYPE specifies the kind of definition. +If SYMBOL has a property `definition-type', +the property value is used instead of TYPE. +TYPE is interpreted via `find-function-regexp-alist'. + The search is done in the source for library LIBRARY." (if (null library) (error "Don't know where `%s' is defined" symbol)) @@ -410,6 +413,8 @@ The search is done in the source for library LIBRARY." ;; that defines something else. (while (and (symbolp symbol) (get symbol 'definition-name)) (setq symbol (get symbol 'definition-name))) + (setq type (or (get symbol 'definition-type) + type)) (if (string-match "\\`src/\\(.*\\.\\(c\\|m\\)\\)\\'" library) (find-function-C-source symbol (match-string 1 library) type) (when (string-match "\\.el\\(c\\)\\'" library) commit c98d9e8bf58c75e3ed6c7d2f9c5e63fc17bf9cf7 Author: Stephen Gildea Date: Sun Jan 12 20:13:08 2025 -0800 Lisp Reference Manual: Index standard symbol properties. * doc/lispref/symbols.texi (Standard Properties): * doc/lispref/commands.texi: * doc/lispref/customize.texi (Variable Definitions): * doc/lispref/help.texi: * doc/lispref/keymaps.texi: * doc/lispref/minibuf.texi (Minibuffer History): * doc/lispref/modes.texi (Setting Hooks): * doc/lispref/sequences.texi (Char-Tables): * doc/lispref/text.texi (Undo): * doc/lispref/variables.texi: Each standard symbol property has exactly one index entry, uniformly formatted as "(symbol property)". diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 7cc32a7fdb3..9fe8b4b9e21 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -122,14 +122,13 @@ serves as a flag, telling the Emacs command loop that the function can be called interactively. The argument of the @code{interactive} form specifies how the arguments for an interactive call should be read. -@cindex @code{interactive-form} property Alternatively, an @code{interactive} form may be specified in a function symbol's @code{interactive-form} property. A non-@code{nil} value for this property takes precedence over any @code{interactive} form in the function body itself. This feature is seldom used. @anchor{The interactive-only property} -@cindex @code{interactive-only} property +@cindex @code{interactive-only} (symbol property) Sometimes, a function is only intended to be called interactively, never directly from Lisp. In that case, give the function a non-@code{nil} @code{interactive-only} property, either directly @@ -174,7 +173,7 @@ A command may be called from Lisp programs like any other function, but then the caller supplies the arguments and @var{arg-descriptor} has no effect. -@cindex @code{interactive-form}, symbol property +@cindex @code{interactive-form} (symbol property) The @code{interactive} form must be located at top-level in the function body, or in the function symbol's @code{interactive-form} property (@pxref{Symbol Properties}). It has its effect because the @@ -2124,7 +2123,7 @@ been intercepted by another program.) This is dubbed ``simple translation'', and produces a simple correspondence between touchpoint motion and mouse motion. -@cindex @code{ignored-mouse-command}, a symbol property +@cindex @code{ignored-mouse-command} (symbol property) However, some commands bound to @code{down-mouse-1}--@code{mouse-drag-region}, for example--either conflict with defined touch screen gestures (such as ``long-press to @@ -2161,7 +2160,7 @@ non-@code{nil} value, as, for example, it may be by a caller of @code{read-key} expecting to receive @code{mouse-movement} and @code{drag-mouse-1} events. -@cindex @code{mouse-1-menu-command}, a symbol property +@cindex @code{mouse-1-menu-command} (symbol property) Since certain commands are also bound to @code{down-mouse-1} for the purpose of displaying pop-up menus, Emacs additionally behaves as illustrated in the last paragraph if @code{down-mouse-1} is bound to a @@ -4488,7 +4487,7 @@ confirmation before it can be executed. Disabling is used for commands which might be confusing to beginning users, to prevent them from using the commands by accident. -@kindex disabled +@cindex @code{disabled} (symbol property) The low-level mechanism for disabling a command is to put a non-@code{nil} @code{disabled} property on the Lisp symbol for the command. These properties are normally set up by the user's diff --git a/doc/lispref/customize.texi b/doc/lispref/customize.texi index b5a4c965fe9..09c05fa18c6 100644 --- a/doc/lispref/customize.texi +++ b/doc/lispref/customize.texi @@ -511,6 +511,10 @@ previously, Lisp programs can use this function to add values for user options not yet defined. @end defun +@cindex @code{customized-value} (symbol property) +@cindex @code{saved-value} (symbol property) +@cindex @code{standard-value} (symbol property) +@cindex @code{theme-value} (symbol property) Internally, @code{defcustom} uses the symbol property @code{standard-value} to record the expression for the standard value, @code{saved-value} to record the value saved by the user with the diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi index 810a7f042fa..f39ab3e4d05 100644 --- a/doc/lispref/help.texi +++ b/doc/lispref/help.texi @@ -60,7 +60,7 @@ Documentation}. You can also put function documentation in the @code{function-documentation} property of a function name (@pxref{Accessing Documentation}). -@cindex @code{variable-documentation} property +@cindex @code{variable-documentation} (symbol property) In a variable definition (a @code{defvar} form), the documentation string is specified after the initial value. @xref{Defining Variables}. The string is stored in the variable's @@ -388,6 +388,7 @@ argument @var{no-face} is non-@code{nil}, the function doesn't add this face to the produced string. @cindex advertised binding +@cindex @code{:advertised-binding} (symbol property) If a command has multiple bindings, this function normally uses the first one it finds. You can specify one particular key binding by assigning an @code{:advertised-binding} symbol property to the diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi index 56fd330a84d..7095942d7b2 100644 --- a/doc/lispref/keymaps.texi +++ b/doc/lispref/keymaps.texi @@ -2473,6 +2473,7 @@ is on that item in the same way as @code{help-echo} text properties If @var{real-binding} is @code{nil}, then @var{item-string} appears in the menu but cannot be selected. +@cindex @code{menu-enable} (symbol property) If @var{real-binding} is a symbol and has a non-@code{nil} @code{menu-enable} property, that property is an expression that controls whether the menu item is enabled. Every time the keymap is @@ -2716,6 +2717,7 @@ items. Here's an example that makes two aliases for (put 'make-writable 'menu-enable 'buffer-read-only) @end example +@cindex @code{menu-alias} (symbol property) When using aliases in menus, often it is useful to display the equivalent key bindings for the real command name, not the aliases (which typically don't have any key bindings except for the menu diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index d8e34da8584..d8e7e6c2e76 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -671,6 +671,7 @@ function, this automatic addition is disabled, and you can also set this variable to your own function which adds only some candidates, or some other values, to the ``future history''. +@cindex @code{history-length} (symbol property) Emacs functions that add a new element to a history list can also delete old elements if the list gets too long. The variable @code{history-length} specifies the maximum length for most history diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index 710be4cd730..7c532002670 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -183,6 +183,7 @@ adds @code{my-text-hook-function} to the hook called @code{text-mode-hook}. If @var{function} is already present in @var{hook} (comparing using @code{equal}), then @code{add-hook} does not add it a second time. +@cindex @code{permanent-local-hook} (symbol property) If @var{function} has a non-@code{nil} property @code{permanent-local-hook}, then @code{kill-all-local-variables} (or changing major modes) won't delete it from the hook variable's local @@ -565,6 +566,8 @@ If something special should be done if the user switches a buffer from this mode to any other major mode, this mode can set up a buffer-local value for @code{change-major-mode-hook} (@pxref{Creating Buffer-Local}). +@cindex @code{mode-class} (symbol property) +@cindex @code{special} modes @item If this mode is appropriate only for specially-prepared text produced by the mode itself (rather than by the user typing at the keyboard or by an @@ -572,8 +575,6 @@ external file), then the major mode command symbol should have a property named @code{mode-class} with value @code{special}, put on as follows: -@kindex mode-class @r{(property)} -@cindex @code{special} modes @example (put 'funny-mode 'mode-class 'special) @end example diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 283dd1c9610..5588d32c5e9 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -1629,6 +1629,7 @@ as the subtype, and syntax tables are char-tables with @code{syntax-table} as the subtype. The subtype can be queried using the function @code{char-table-subtype}, described below. +@cindex @code{char-table-extra-slots} (symbol property) @item The subtype controls the number of @dfn{extra slots} in the char-table. This number is specified by the subtype's diff --git a/doc/lispref/symbols.texi b/doc/lispref/symbols.texi index 24b4e892024..acd55fcbb81 100644 --- a/doc/lispref/symbols.texi +++ b/doc/lispref/symbols.texi @@ -535,6 +535,7 @@ value, saved value, customized-but-unsaved value, and themed values. Do not set them directly; they are managed by @code{defcustom} and related functions. @xref{Variable Definitions}. +@cindex @code{definition-name} (symbol property) @item definition-name This property is used to find the definition of a symbol in the source code, when it might be hard to find the definition by textual search @@ -565,10 +566,27 @@ The value, if non-@code{nil}, specifies the maximum minibuffer history length for the named history list variable. @xref{Minibuffer History}. +@cindex @code{important-return-value} (symbol property) +@item important-return-value +A non-@code{nil} value makes the byte compiler warn about code that +calls the named function without using its returned value. This is +useful for functions where doing so is likely to be a mistake. +This property is normally added to a function with @code{declare} +(@pxref{Declare Form}). + @item interactive-form The value is an interactive form for the named function. Normally, you should not set this directly; use the @code{interactive} special -form instead. @xref{Interactive Call}. +form instead. @xref{Using Interactive}. + +@item interactive-only +If the value is non-@code{nil}, the named function should not be called +from Lisp. The value is an error string or the function to call +instead. @xref{Defining Commands}. + +@item menu-alias +If non-nil, this symbol is an alias menu entry, and its own key binding +should not be shown. @xref{Alias Menu Items}. @item menu-enable The value is an expression for determining whether the named menu item @@ -578,6 +596,11 @@ should be enabled in menus. @xref{Simple Menu Items}. If the value is @code{special}, the named major mode is special. @xref{Major Mode Conventions}. +@item ignored-mouse-command +@itemx mouse-1-menu-command +These properties affect how commands bound to @code{down-mouse-1} behave. +@xref{Touchscreen Events}. + @item permanent-local If the value is non-@code{nil}, the named variable is a buffer-local variable whose value should not be reset when changing major modes. @@ -588,18 +611,20 @@ If the value is non-@code{nil}, the named function should not be deleted from the local value of a hook variable when changing major modes. @xref{Setting Hooks}. +@cindex @code{pure} (symbol property) @item pure -@cindex @code{pure} property If the value is non-@code{nil}, the named function is considered to be pure (@pxref{What Is a Function}). Calls with constant arguments can be evaluated at compile time. This may shift run time errors to -compile time. Not to be confused with pure storage (@pxref{Pure -Storage}). +compile time. This property is normally added to a function with +@code{declare} (@pxref{Declare Form}). Not to be confused with pure +storage (@pxref{Pure Storage}). @item risky-local-variable If the value is non-@code{nil}, the named variable is considered risky as a file-local variable. @xref{File Local Variables}. +@cindex @code{safe-function} (symbol property) @item safe-function If the value is non-@code{nil}, the named function is considered generally safe for evaluation. @xref{Function Safety}. @@ -610,25 +635,18 @@ file-local evaluation forms. @xref{File Local Variables}. @item safe-local-variable The value specifies a function for determining safe file-local values -for the named variable. @xref{File Local Variables}. Since this -value is consulted when loading files, the function should be -efficient and should ideally not lead to loading any libraries to -determine the safeness (e.g., it should not be an autoloaded function). +for the named variable. @xref{File Local Variables}. +@cindex @code{side-effect-free} (symbol property) @item side-effect-free -@cindex @code{side-effect-free} property A non-@code{nil} value indicates that the named function is free of side effects (@pxref{What Is a Function}), so the byte compiler may ignore a call whose value is unused. If the property's value is @code{error-free}, the byte compiler may even delete such unused calls. In addition to byte compiler optimizations, this property is also used for determining function safety (@pxref{Function Safety}). - -@item important-return-value -@cindex @code{important-return-value} property -A non-@code{nil} value makes the byte compiler warn about code that -calls the named function without using its returned value. This is -useful for functions where doing so is likely to be a mistake. +This property is normally added to a function with +@code{declare} (@pxref{Declare Form}). @item undo-inhibit-region If non-@code{nil}, the named function prevents the @code{undo} operation @@ -638,7 +656,7 @@ immediately after the function. @xref{Undo}. @item variable-documentation If non-@code{nil}, this specifies the named variable's documentation string. This is set automatically by @code{defvar} and related -functions. @xref{Defining Faces}. +functions. @xref{Documentation Basics}. @end table @node Shorthands diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index 5e072b8697b..2d24436d214 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -1524,6 +1524,7 @@ This macro removes all the undo boundaries inserted during the execution of @var{body} so that it can be undone as a single step. @end defmac +@cindex @code{undo-inhibit-region} (symbol property) Some commands leave the region active after execution in such a way that it interferes with selective undo of that command. To make @code{undo} ignore the active region when invoked immediately after such a command, diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi index fed72235801..a2bb1834477 100644 --- a/doc/lispref/variables.texi +++ b/doc/lispref/variables.texi @@ -1774,6 +1774,7 @@ subsequent major mode. @xref{Hooks}. @end defvar @cindex permanent local variable +@cindex @code{permanent-local} (symbol property) A buffer-local variable is @dfn{permanent} if the variable name (a symbol) has a @code{permanent-local} property that is non-@code{nil}. Such variables are unaffected by @code{kill-all-local-variables}, and @@ -2051,7 +2052,7 @@ file-local variables stored in @code{file-local-variables-alist}. @end defvar @cindex safe local variable -@cindex @code{safe-local-variable}, property of variable +@cindex @code{safe-local-variable} (symbol property) You can specify safe values for a variable with a @code{safe-local-variable} property. The property has to be a function of one argument; any value is safe if the function returns @@ -2061,6 +2062,11 @@ variables have @code{safe-local-variable} properties; these include For boolean-valued variables that are safe, use @code{booleanp} as the property value. + Since the value of @code{safe-local-variable} is consulted when +loading files, the function should be efficient and should ideally not +lead to loading any libraries to determine the safeness (e.g., it should +not be an autoloaded function). + If you want to define @code{safe-local-variable} properties for variables defined in C source code, add the names and the properties of those variables to the list in the ``Safe local variables'' section @@ -2118,7 +2124,7 @@ This function returns non-@code{nil} if it is safe to give @var{sym} the value @var{val}, based on the above criteria. @end defun -@c @cindex risky local variable Duplicates risky-local-variable +@cindex @code{risky-local-variable} (symbol property) Some variables are considered @dfn{risky}. If a variable is risky, it is never entered automatically into @code{safe-local-variable-values}; Emacs always queries before setting @@ -2167,6 +2173,7 @@ evaluate when found in the @samp{Eval:} ``variable'' in a file local variables list. @end defopt +@cindex @code{safe-local-eval-function} (symbol property) If the expression is a function call and the function has a @code{safe-local-eval-function} property, the property value determines whether the expression is safe to evaluate. The property commit 615f06ea534cd565f439b08d563a31ca2b0eea92 Author: Roland Winkler Date: Sun Jan 12 18:43:16 2025 -0600 fix for bibtex.el in prior commit * lisp/textmodes/bibtex.el (bibtex-string-file-path) (bibtex-file-path): Check for undefined environment variable BIBINPUTS. diff --git a/lisp/textmodes/bibtex.el b/lisp/textmodes/bibtex.el index 83d8f2e6e70..a337c7167ca 100644 --- a/lisp/textmodes/bibtex.el +++ b/lisp/textmodes/bibtex.el @@ -1095,7 +1095,9 @@ to the directories specified in `bibtex-string-file-path'." :group 'bibtex :type '(repeat file)) -(defcustom bibtex-string-file-path (split-string (getenv "BIBINPUTS") ":+" t) +(defcustom bibtex-string-file-path + (let ((bibinputs (getenv "BIBINPUTS"))) + (if bibinputs (split-string bibinputs ":+" t))) "List of directories to search for `bibtex-string-files'. By default, initialized from the BIBINPUTS environment variable. For backward compatibility, considered obsolete, it may also be @@ -1117,7 +1119,9 @@ See also `bibtex-search-entry-globally'." :type '(repeat (choice (const :tag "bibtex-file-path" bibtex-file-path) directory file))) -(defcustom bibtex-file-path (split-string (getenv "BIBINPUTS") ":+" t) +(defcustom bibtex-file-path + (let ((bibinputs (getenv "BIBINPUTS"))) + (if bibinputs (split-string bibinputs ":+" t))) "List of directories to search for `bibtex-files'. By default, initialized from the BIBINPUTS environment variable. For backward compatibility, considered obsolete, it may also be commit ccd927d741df928d4de578dffac74bc67d24c8b0 Author: Stefan Kangas Date: Sun Jan 12 22:41:30 2025 +0100 Use eabs in Fcurrent_time_zone * src/timefns.c (Fcurrent_time_zone): Use eabs. * test/src/timefns-tests.el (timefns-tests-current-time-zone): New test. diff --git a/src/timefns.c b/src/timefns.c index e2b39388606..4fb142f2f1b 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -1950,7 +1950,7 @@ the data it can't find. */) /* No local time zone name is available; use numeric zone instead. */ long int hour = offset / 3600; int min_sec = offset % 3600; - int amin_sec = min_sec < 0 ? - min_sec : min_sec; + int amin_sec = eabs (min_sec); int min = amin_sec / 60; int sec = amin_sec % 60; int min_prec = min_sec ? 2 : 0; diff --git a/test/src/timefns-tests.el b/test/src/timefns-tests.el index 3e75a3f9b63..89a199f37fc 100644 --- a/test/src/timefns-tests.el +++ b/test/src/timefns-tests.el @@ -261,4 +261,8 @@ a fixed place on the right and are padded on the left." (should (time-equal-p time- (time-convert time- form))) (should (time-equal-p time+ (time-convert time+ form)))))))) +(ert-deftest current-time-zone () + (should (listp (current-time-zone))) + (should (= (length (current-time-zone)) 2))) + ;;; timefns-tests.el ends here commit 85c73bb901e2adbad78e19c9a4f4d1ef74ac3d6d Author: Stefan Kangas Date: Sun Jan 12 22:08:51 2025 +0100 Delete obsolete coccinelle files These files relates to a 13 year old attempt at a generational GC that was never merged. * admin/coccinelle/frame.cocci: * admin/coccinelle/window.cocci: Delete files. diff --git a/admin/coccinelle/frame.cocci b/admin/coccinelle/frame.cocci deleted file mode 100644 index a817382120a..00000000000 --- a/admin/coccinelle/frame.cocci +++ /dev/null @@ -1,133 +0,0 @@ -// Change direct access to Lisp_Object fields of struct frame to FVAR. -@@ -expression F; -@@ -( -- F->icon_name -+ FVAR (F, icon_name) -| -- F->title -+ FVAR (F, title) -| -- F->focus_frame -+ FVAR (F, focus_frame) -| -- F->root_window -+ FVAR (F, root_window) -| -- F->selected_window -+ FVAR (F, selected_window) -| -- F->minibuffer_window -+ FVAR (F, minibuffer_window) -| -- F->param_alist -+ FVAR (F, param_alist) -| -- F->scroll_bars -+ FVAR (F, scroll_bars) -| -- F->condemned_scroll_bars -+ FVAR (F, condemned_scroll_bars) -| -- F->menu_bar_items -+ FVAR (F, menu_bar_items) -| -- F->face_alist -+ FVAR (F, face_alist) -| -- F->menu_bar_vector -+ FVAR (F, menu_bar_vector) -| -- F->buffer_predicate -+ FVAR (F, buffer_predicate) -| -- F->buffer_list -+ FVAR (F, buffer_list) -| -- F->buried_buffer_list -+ FVAR (F, buried_buffer_list) -| -- F->menu_bar_window -+ FVAR (F, menu_bar_window) -| -- F->tool_bar_window -+ FVAR (F, tool_bar_window) -| -- F->tool_bar_items -+ FVAR (F, tool_bar_items) -| -- F->tool_bar_position -+ FVAR (F, tool_bar_position) -| -- F->desired_tool_bar_string -+ FVAR (F, desired_tool_bar_string) -| -- F->current_tool_bar_string -+ FVAR (F, current_tool_bar_string) - -| - -- XFRAME (F)->icon_name -+ FVAR (XFRAME (F), icon_name) -| -- XFRAME (F)->title -+ FVAR (XFRAME (F), title) -| -- XFRAME (F)->focus_frame -+ FVAR (XFRAME (F), focus_frame) -| -- XFRAME (F)->root_window -+ FVAR (XFRAME (F), root_window) -| -- XFRAME (F)->selected_window -+ FVAR (XFRAME (F), selected_window) -| -- XFRAME (F)->minibuffer_window -+ FVAR (XFRAME (F), minibuffer_window) -| -- XFRAME (F)->param_alist -+ FVAR (XFRAME (F), param_alist) -| -- XFRAME (F)->scroll_bars -+ FVAR (XFRAME (F), scroll_bars) -| -- XFRAME (F)->condemned_scroll_bars -+ FVAR (XFRAME (F), condemned_scroll_bars) -| -- XFRAME (F)->menu_bar_items -+ FVAR (XFRAME (F), menu_bar_items) -| -- XFRAME (F)->face_alist -+ FVAR (XFRAME (F), face_alist) -| -- XFRAME (F)->menu_bar_vector -+ FVAR (XFRAME (F), menu_bar_vector) -| -- XFRAME (F)->buffer_predicate -+ FVAR (XFRAME (F), buffer_predicate) -| -- XFRAME (F)->buffer_list -+ FVAR (XFRAME (F), buffer_list) -| -- XFRAME (F)->buried_buffer_list -+ FVAR (XFRAME (F), buried_buffer_list) -| -- XFRAME (F)->menu_bar_window -+ FVAR (XFRAME (F), menu_bar_window) -| -- XFRAME (F)->tool_bar_window -+ FVAR (XFRAME (F), tool_bar_window) -| -- XFRAME (F)->tool_bar_items -+ FVAR (XFRAME (F), tool_bar_items) -| -- XFRAME (F)->tool_bar_position -+ FVAR (XFRAME (F), tool_bar_position) -| -- XFRAME (F)->desired_tool_bar_string -+ FVAR (XFRAME (F), desired_tool_bar_string) -| -- XFRAME (F)->current_tool_bar_string -+ FVAR (XFRAME (F), current_tool_bar_string) -) diff --git a/admin/coccinelle/window.cocci b/admin/coccinelle/window.cocci deleted file mode 100644 index 4543fb2ce15..00000000000 --- a/admin/coccinelle/window.cocci +++ /dev/null @@ -1,236 +0,0 @@ -// Change direct access to Lisp_Object fields of struct window to WVAR. -@@ -struct window *W; -Lisp_Object O; -@@ -( -- W->frame -+ WVAR (W, frame) -| -- W->next -+ WVAR (W, next) -| -- W->prev -+ WVAR (W, prev) -| -- W->hchild -+ WVAR (W, hchild) -| -- W->vchild -+ WVAR (W, vchild) -| -- W->parent -+ WVAR (W, parent) -| -- W->left_col -+ WVAR (W, left_col) -| -- W->top_line -+ WVAR (W, top_line) -| -- W->total_lines -+ WVAR (W, total_lines) -| -- W->total_cols -+ WVAR (W, total_cols) -| -- W->normal_lines -+ WVAR (W, normal_lines) -| -- W->normal_cols -+ WVAR (W, normal_cols) -| -- W->new_total -+ WVAR (W, new_total) -| -- W->new_normal -+ WVAR (W, new_normal) -| -- W->buffer -+ WVAR (W, buffer) -| -- W->start -+ WVAR (W, start) -| -- W->pointm -+ WVAR (W, pointm) -| -- W->temslot -+ WVAR (W, temslot) -| -- W->vertical_scroll_bar -+ WVAR (W, vertical_scroll_bar) -| -- W->left_margin_cols -+ WVAR (W, left_margin_cols) -| -- W->right_margin_cols -+ WVAR (W, right_margin_cols) -| -- W->left_fringe_width -+ WVAR (W, left_fringe_width) -| -- W->right_fringe_width -+ WVAR (W, right_fringe_width) -| -- W->scroll_bar_width -+ WVAR (W, scroll_bar_width) -| -- W->vertical_scroll_bar_type -+ WVAR (W, vertical_scroll_bar_type) -| -- W->window_end_pos -+ WVAR (W, window_end_pos) -| -- W->window_end_vpos -+ WVAR (W, window_end_vpos) -| -- W->window_end_valid -+ WVAR (W, window_end_valid) -| -- W->display_table -+ WVAR (W, display_table) -| -- W->dedicated -+ WVAR (W, dedicated) -| -- W->base_line_number -+ WVAR (W, base_line_number) -| -- W->base_line_pos -+ WVAR (W, base_line_pos) -| -- W->region_showing -+ WVAR (W, region_showing) -| -- W->column_number_displayed -+ WVAR (W, column_number_displayed) -| -- W->combination_limit -+ WVAR (W, combination_limit) -| -- W->prev_buffers -+ WVAR (W, prev_buffers) -| -- W->next_buffers -+ WVAR (W, next_buffers) -| -- W->window_parameters -+ WVAR (W, window_parameters) - -| - -- XWINDOW (O)->frame -+ WVAR (XWINDOW (O), frame) -| -- XWINDOW (O)->next -+ WVAR (XWINDOW (O), next) -| -- XWINDOW (O)->prev -+ WVAR (XWINDOW (O), prev) -| -- XWINDOW (O)->hchild -+ WVAR (XWINDOW (O), hchild) -| -- XWINDOW (O)->vchild -+ WVAR (XWINDOW (O), vchild) -| -- XWINDOW (O)->parent -+ WVAR (XWINDOW (O), parent) -| -- XWINDOW (O)->left_col -+ WVAR (XWINDOW (O), left_col) -| -- XWINDOW (O)->top_line -+ WVAR (XWINDOW (O), top_line) -| -- XWINDOW (O)->total_lines -+ WVAR (XWINDOW (O), total_lines) -| -- XWINDOW (O)->total_cols -+ WVAR (XWINDOW (O), total_cols) -| -- XWINDOW (O)->normal_lines -+ WVAR (XWINDOW (O), normal_lines) -| -- XWINDOW (O)->normal_cols -+ WVAR (XWINDOW (O), normal_cols) -| -- XWINDOW (O)->new_total -+ WVAR (XWINDOW (O), new_total) -| -- XWINDOW (O)->new_normal -+ WVAR (XWINDOW (O), new_normal) -| -- XWINDOW (O)->buffer -+ WVAR (XWINDOW (O), buffer) -| -- XWINDOW (O)->start -+ WVAR (XWINDOW (O), start) -| -- XWINDOW (O)->pointm -+ WVAR (XWINDOW (O), pointm) -| -- XWINDOW (O)->temslot -+ WVAR (XWINDOW (O), temslot) -| -- XWINDOW (O)->vertical_scroll_bar -+ WVAR (XWINDOW (O), vertical_scroll_bar) -| -- XWINDOW (O)->left_margin_cols -+ WVAR (XWINDOW (O), left_margin_cols) -| -- XWINDOW (O)->right_margin_cols -+ WVAR (XWINDOW (O), right_margin_cols) -| -- XWINDOW (O)->left_fringe_width -+ WVAR (XWINDOW (O), left_fringe_width) -| -- XWINDOW (O)->right_fringe_width -+ WVAR (XWINDOW (O), right_fringe_width) -| -- XWINDOW (O)->scroll_bar_width -+ WVAR (XWINDOW (O), scroll_bar_width) -| -- XWINDOW (O)->vertical_scroll_bar_type -+ WVAR (XWINDOW (O), vertical_scroll_bar_type) -| -- XWINDOW (O)->window_end_pos -+ WVAR (XWINDOW (O), window_end_pos) -| -- XWINDOW (O)->window_end_vpos -+ WVAR (XWINDOW (O), window_end_vpos) -| -- XWINDOW (O)->window_end_valid -+ WVAR (XWINDOW (O), window_end_valid) -| -- XWINDOW (O)->display_table -+ WVAR (XWINDOW (O), display_table) -| -- XWINDOW (O)->dedicated -+ WVAR (XWINDOW (O), dedicated) -| -- XWINDOW (O)->base_line_number -+ WVAR (XWINDOW (O), base_line_number) -| -- XWINDOW (O)->base_line_pos -+ WVAR (XWINDOW (O), base_line_pos) -| -- XWINDOW (O)->region_showing -+ WVAR (XWINDOW (O), region_showing) -| -- XWINDOW (O)->column_number_displayed -+ WVAR (XWINDOW (O), column_number_displayed) -| -- XWINDOW (O)->combination_limit -+ WVAR (XWINDOW (O), combination_limit) -| -- XWINDOW (O)->prev_buffers -+ WVAR (XWINDOW (O), prev_buffers) -| -- XWINDOW (O)->next_buffers -+ WVAR (XWINDOW (O), next_buffers) -| -- XWINDOW (O)->window_parameters -+ WVAR (XWINDOW (O), window_parameters) -) commit 14b5ba7c61fd8fb1e2c8fe956ee925b4a59c10c8 Author: Roland Winkler Date: Sun Jan 12 15:17:28 2025 -0600 bibtex-string-file-path and bibtex-file-path are lists of directories * lisp/textmodes/bibtex.el (bibtex-string-file-path, bibtex-file-path): These user options are now lists of directories. For backward compatibility, the old, obsolete format is still supported. (bibtex-string-files-init, bibtex-initialize): Change accordingly. Use file-name-extension and file-name-with-extension. * etc/NEWS: Entry for new format of bibtex-string-file-path and bibtex-file-path. diff --git a/etc/NEWS b/etc/NEWS index d017c872fa3..62b90aa11aa 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -826,6 +826,11 @@ Use 'bibtex-aux-opt-alist' instead. *** New user option 'bibtex-entry-ask-for-key'. When enabled, 'bibtex-entry' asks for a key. +--- +*** 'bibtex-string-file-path' and 'bibtex-file-path' are lists of directories. +For backward compatibility, considered obsolete, these user options +may still be strings with colon separated lists of directories. + ** Midnight mode --- diff --git a/lisp/textmodes/bibtex.el b/lisp/textmodes/bibtex.el index 601331cbce2..83d8f2e6e70 100644 --- a/lisp/textmodes/bibtex.el +++ b/lisp/textmodes/bibtex.el @@ -1095,13 +1095,16 @@ to the directories specified in `bibtex-string-file-path'." :group 'bibtex :type '(repeat file)) -(defcustom bibtex-string-file-path (getenv "BIBINPUTS") - "Colon-separated list of paths to search for `bibtex-string-files'. -Initialized from the BIBINPUTS environment variable." +(defcustom bibtex-string-file-path (split-string (getenv "BIBINPUTS") ":+" t) + "List of directories to search for `bibtex-string-files'. +By default, initialized from the BIBINPUTS environment variable. +For backward compatibility, considered obsolete, it may also be +a single string with a colon separated list of directories." :group 'bibtex - :version "27.1" - :type '(choice string - (const :tag "Not Set" nil))) + :version "31.1" + :type '(choice (repeat directory) + (string :tag "String (obsolete)") + (const :tag "Not set" nil))) (defcustom bibtex-files nil "List of BibTeX files that are searched for entry keys. @@ -1114,13 +1117,16 @@ See also `bibtex-search-entry-globally'." :type '(repeat (choice (const :tag "bibtex-file-path" bibtex-file-path) directory file))) -(defcustom bibtex-file-path (getenv "BIBINPUTS") - "Colon separated list of paths to search for `bibtex-files'. -Initialized from the BIBINPUTS environment variable." +(defcustom bibtex-file-path (split-string (getenv "BIBINPUTS") ":+" t) + "List of directories to search for `bibtex-files'. +By default, initialized from the BIBINPUTS environment variable. +For backward compatibility, considered obsolete, it may also be +a single string with a colon separated list of directories." :group 'bibtex - :version "27.1" - :type '(choice string - (const :tag "Not Set" nil))) + :version "31.1" + :type '(choice (repeat directory) + (string :tag "String (obsolete)") + (const :tag "Not set" nil))) (defcustom bibtex-search-entry-globally nil "If non-nil, interactive calls of `bibtex-search-entry' search globally. @@ -3195,30 +3201,35 @@ Return alist of strings if parsing was completed, `aborted' otherwise." (defun bibtex-string-files-init () "Return initialization for `bibtex-strings'. -Use `bibtex-predefined-strings' and BibTeX files `bibtex-string-files'." - (save-match-data - (let ((dirlist (split-string (or bibtex-string-file-path default-directory) - ":+")) - (case-fold-search) - string-files fullfilename compl bounds found) - ;; collect absolute file names of valid string files - (dolist (filename bibtex-string-files) - (unless (string-match "\\.bib\\'" filename) - (setq filename (concat filename ".bib"))) - ;; test filenames - (if (file-name-absolute-p filename) - (if (file-readable-p filename) - (push filename string-files) - (user-error "BibTeX strings file %s not found" filename)) +Use `bibtex-predefined-strings' and BibTeX files `bibtex-string-files' +with `bibtex-string-file-path'." + (let ((dirlist + (or (if (stringp bibtex-string-file-path) ; obsolete format + (save-match-data + (split-string bibtex-string-file-path ":+" t)) + bibtex-string-file-path) + (list default-directory))) + string-files) + ;; collect absolute file names of valid string files + (dolist (filename bibtex-string-files) + (unless (string= "bib" (file-name-extension filename)) + (setq filename (file-name-with-extension filename "bib"))) + ;; test filenames + (if (file-name-absolute-p filename) + (if (file-readable-p filename) + (push filename string-files) + (user-error "BibTeX strings file %s not found" filename)) + (let (found) (dolist (dir dirlist) - (when (file-readable-p - (setq fullfilename (expand-file-name filename dir))) - (push fullfilename string-files) - (setq found t))) + ;; filename may exist in multiple directories + (let ((fullfilename (expand-file-name filename dir))) + (when (file-readable-p fullfilename) + (push fullfilename string-files) + (setq found t)))) (unless found - (user-error "File %s not in paths defined via bibtex-string-file-path" - filename)))) - ;; parse string files + (user-error "File %s not in bibtex-string-file-path" filename))))) + ;; parse string files + (let (compl bounds) (dolist (filename string-files) (with-temp-buffer (insert-file-contents filename) @@ -3270,9 +3281,20 @@ already set. If SELECT is non-nil interactively select a BibTeX buffer. When called interactively, FORCE is t, CURRENT is t if current buffer visits a file using `bibtex-mode', and SELECT is t if current buffer does not use `bibtex-mode'." - (interactive (list (eq major-mode 'bibtex-mode) t + ;; Interactively, exclude current buffer if it does not visit a file + ;; using `bibtex-mode'. This way, we exclude BibTeX buffers such as + ;; `bibtex-search-buffer' that are not visiting a BibTeX file. + ;; Also, calling `bibtex-initialize' gives meaningful results for any + ;; current buffer. + (interactive (list (eq major-mode 'bibtex-mode) + (and buffer-file-name (eq major-mode 'bibtex-mode)) (not (eq major-mode 'bibtex-mode)))) - (let ((file-path (split-string (or bibtex-file-path default-directory) ":+")) + (let ((file-path + (or (if (stringp bibtex-file-path) ; obsolete format + (save-match-data + (split-string bibtex-file-path ":+" t)) + bibtex-file-path) + (list default-directory))) file-list dir-list buffer-list) ;; generate list of BibTeX files (dolist (file bibtex-files) @@ -3280,20 +3302,20 @@ does not use `bibtex-mode'." (setq dir-list (append dir-list file-path))) ((file-accessible-directory-p file) (push file dir-list)) - ((progn (unless (string-match "\\.bib\\'" file) - (setq file (concat file ".bib"))) + ((progn (unless (string= "bib" (file-name-extension file)) + (setq file (file-name-with-extension file "bib"))) (file-name-absolute-p file)) (push file file-list)) (t (let (expanded-file-name found) (dolist (dir file-path) + ;; filename may exist in multiple directories (when (file-readable-p (setq expanded-file-name (expand-file-name file dir))) (push expanded-file-name file-list) (setq found t))) (unless found - (user-error "File `%s' not in paths defined via bibtex-file-path" - file)))))) + (user-error "File `%s' not in bibtex-file-path" file)))))) (dolist (file file-list) (unless (file-readable-p file) (user-error "BibTeX file `%s' not found" file))) @@ -3307,12 +3329,6 @@ does not use `bibtex-mode'." (if (file-readable-p file) (push (find-file-noselect file) buffer-list))) ;; Include current buffer iff we want it. - ;; Exclude current buffer if it does not visit a file using `bibtex-mode'. - ;; This way we exclude BibTeX buffers such as `bibtex-search-buffer' - ;; that are not visiting a BibTeX file. Also, calling `bibtex-initialize' - ;; gives meaningful results for any current buffer. - (unless (and current (eq major-mode 'bibtex-mode) buffer-file-name) - (setq current nil)) (cond ((and current (not (memq (current-buffer) buffer-list))) (push (current-buffer) buffer-list)) ((and (not current) (memq (current-buffer) buffer-list)) @@ -3321,6 +3337,8 @@ does not use `bibtex-mode'." (let (string-init) (dolist (buffer buffer-list) (with-current-buffer buffer + ;; `bibtex-reference-keys' and `bibtex-strings' are buffer-local + ;; lazy completion tables. So we only initiate them as needed. (if (or force (functionp bibtex-reference-keys)) (bibtex-parse-keys)) (when (or force (functionp bibtex-strings)) commit 99b85e116f09e68e0d5750c9772d0a2489680078 Author: Mauro Aranda Date: Sun Jan 12 15:19:40 2025 -0300 Emphasize the use of :tag for new customization types * doc/lispref/customize.texi (Type Keywords): Name important use cases of the :tag keyword. (Defining New Types): Emphasize the use of the :tag keyword when using the lazy widget. (Bug#74409) diff --git a/doc/lispref/customize.texi b/doc/lispref/customize.texi index 0a4be71a215..b5a4c965fe9 100644 --- a/doc/lispref/customize.texi +++ b/doc/lispref/customize.texi @@ -1180,7 +1180,10 @@ The symbol's value is used. @item :tag @var{tag} Use @var{tag} (a string) as the tag for the value (or part of the value) -that corresponds to this type. +that corresponds to this type. It's important to provide an informative +tag for the customization interface, especially if you're using the +@code{restricted-sexp} type or if you're defining a new type. +@xref{Defining New Types}. @item :doc @var{doc} @kindex doc@r{, customization keyword} @@ -1349,10 +1352,15 @@ with this widget. Here a @code{binary-tree-of-string} is described as being either a string, or a cons-cell whose car and cdr are themselves both @code{binary-tree-of-string}. Note the reference to the widget type we are currently in the process of defining. The @code{:tag} -attribute is a string to name the widget in the user interface, and the -@code{:offset} argument is there to ensure that child nodes are -indented four spaces relative to the parent node, making the tree -structure apparent in the customization buffer. +is another important keyword argument because we are using the +@code{lazy} widget for our new widget. By default, the @code{lazy} +widget doesn't have a tag, and in its absence the customization buffer +will show the entire widget's value (that is, the value of the user +option being customized). Since that's almost never a good idea, we +provide a string to name the @code{binary-tree-or-string} widget. The +@code{:offset} argument is there to ensure that child nodes are indented +four spaces relative to the parent node, making the tree structure +apparent in the customization buffer. The @code{defcustom} shows how the new widget can be used as an ordinary customization type. commit 2d2bc6f1bc678b21c46af26cf343e24acdb48b79 Author: Juri Linkov Date: Sun Jan 12 20:17:16 2025 +0200 Add treesit-thing-settings to yaml-ts-mode and enable transpose-sexps * lisp/treesit.el (treesit-major-mode-setup): Move setting of 'transpose-sexps-function' outside of 'treesit-thing-defined-p' since 'treesit-transpose-sexps' doesn't depend on the 'sexp' thing. * lisp/textmodes/yaml-ts-mode.el (yaml-ts-mode): Add 'treesit-thing-settings' with the 'list' thing, but use it only for list commands, not sexp commands (bug#73404). diff --git a/lisp/textmodes/yaml-ts-mode.el b/lisp/textmodes/yaml-ts-mode.el index 957102950b4..defef096aa6 100644 --- a/lisp/textmodes/yaml-ts-mode.el +++ b/lisp/textmodes/yaml-ts-mode.el @@ -167,7 +167,22 @@ boundaries. JUSTIFY is passed to `fill-paragraph'." (setq-local fill-paragraph-function #'yaml-ts-mode--fill-paragraph) - (treesit-major-mode-setup))) + ;; Navigation. + (setq-local treesit-thing-settings + `((yaml + (list ,(regexp-opt '("block_mapping_pair" + "flow_sequence")) + 'symbols)))) + + (treesit-major-mode-setup) + + ;; Use the `list' thing defined above to navigate only lists + ;; with `C-M-n', `C-M-p', `C-M-u', `C-M-d', but not sexps + ;; with `C-M-f', `C-M-b' neither adapt to 'show-paren-mode' + ;; that is problematic in languages without explicit + ;; opening/closing nodes. + (setq-local forward-sexp-function nil) + (setq-local show-paren-data-function 'show-paren--default))) (derived-mode-add-parents 'yaml-ts-mode '(yaml-mode)) diff --git a/lisp/treesit.el b/lisp/treesit.el index 056d96ad7ad..ac34edaf84d 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -3593,9 +3593,10 @@ before calling this function." (setq-local add-log-current-defun-function #'treesit-add-log-current-defun)) + (setq-local transpose-sexps-function #'treesit-transpose-sexps) + (when (treesit-thing-defined-p 'sexp nil) - (setq-local forward-sexp-function #'treesit-forward-sexp) - (setq-local transpose-sexps-function #'treesit-transpose-sexps)) + (setq-local forward-sexp-function #'treesit-forward-sexp)) (when (treesit-thing-defined-p 'list nil) (setq-local forward-sexp-function #'treesit-forward-sexp-list) commit 7648faedd3aeabadc66c67630809ef6178f4851a Author: Pranshu Sharma Date: Sun Jan 12 18:45:30 2025 +0100 Add new option 'rotate-windows-change-selected' * lisp/window-x.el: Fix header information. (rotate-windows-change-selected): New option. (rotate-window-layout-counterclockwise) (rotate-window-layout-clockwise): Fix doc-strings. (rotate-windows): Handle 'rotate-windows-change-selected'. diff --git a/lisp/window-x.el b/lisp/window-x.el index 82271aa1d4c..0411400917e 100644 --- a/lisp/window-x.el +++ b/lisp/window-x.el @@ -1,11 +1,11 @@ -;;; window-x.el --- Extra window organization commands -*- lexical-binding: t; -*- +;;; window-x.el --- Extra window related commands -*- lexical-binding: t; -*- ;; Copyright (C) 2025 Free Software Foundation, Inc. ;; Author: Pranshu Sharma ;; Martin Rudalics ;; Maintainer: emacs-devel@gnu.org -;; Keywords: files +;; Keywords: window, convenience ;; Package: emacs ;; This file is part of GNU Emacs. @@ -29,6 +29,14 @@ ;;; Code: +(defcustom rotate-windows-change-selected t + "If nil the selected window will not change with `rotate-windows'. + +The selected window before and after the function call will stay +unchanged if nil. `rotate-windows-back' is also affected." + :type 'boolean + :group 'windows) + (defun window-tree-normal-sizes (window &optional next) "Return normal sizes of all windows rooted at WINDOW. @@ -70,7 +78,7 @@ where HEIGHT and WIDTH are the normal height and width of the window. ;;;###autoload (defun rotate-window-layout-counterclockwise (&optional window) - "Rotate windows under WINDOW counterclockwise by 90 degrees. + "Rotate window layout of WINDOW counterclockwise by 90 degrees. If WINDOW is nil, it defaults to the root window of the selected frame. @@ -81,7 +89,7 @@ selected window." ;;;###autoload (defun rotate-window-layout-clockwise (&optional window) - "Rotate windows under WINDOW clockwise by 90 degrees. + "Rotate window layout under WINDOW clockwise by 90 degrees. If WINDOW is nil, it defaults to the root window of the selected frame. @@ -178,14 +186,13 @@ selected window." (when (or (seq-some #'window-atom-root winls) (seq-some #'window-fixed-size-p winls)) (user-error "Cannot rotate windows due to fixed size or atom windows")) - ;; All child windows need to be recursively deleted. (delete-other-windows-internal first-window window) - ;; (delete-dups atom-windows) (window--transpose-1 new-win-tree first-window '(below . right) t nil) (set-frame-selected-window frame selected-window) - (other-window other-window-arg) - (while (not (memq (selected-window) winls)) - (other-window other-window-arg)))) + (when rotate-windows-change-selected + (other-window other-window-arg) + (while (not (memq (selected-window) winls)) + (other-window other-window-arg))))) (defun window--transpose (window conf no-resize) "Rearrange windows under WINDOW recursively. commit 7dcc7605d58165dac233bcc029509f1ad9807f72 Author: Eshel Yaron Date: Sat Jan 11 13:57:26 2025 +0100 ; Touch-ups for new window-x.el Discussion: https://lists.gnu.org/archive/html/emacs-devel/2025-01/msg00322.html * lisp/window-x.el: Autoload commands, provide feature. (window-tree-normal-sizes): Improve docstring. (window--window-to-transpose): Remove. (window--rotate-interactive-arg): New function. (rotate-window-layout-anticlockwise): Rename to... (rotate-window-layout-counterclockwise): ...this. (rotate-window-layout-clockwise) (flip-window-layout-horizontally) (flip-window-layout-vertically, transpose-window-layout) (rotate-windows-back, rotate-windows, window--transpose) (window--transpose-1): Cosmetics. diff --git a/lisp/window-x.el b/lisp/window-x.el index d6c1851ddeb..82271aa1d4c 100644 --- a/lisp/window-x.el +++ b/lisp/window-x.el @@ -1,4 +1,4 @@ -;;; window-x.el --- extended window commands -*- lexical-binding: t; -*- +;;; window-x.el --- Extra window organization commands -*- lexical-binding: t; -*- ;; Copyright (C) 2025 Free Software Foundation, Inc. @@ -25,19 +25,21 @@ ;;; Commentary: -;; This file defines additional infrequently used window commands that -;; should not be in window.el to not make the dumped image bigger. +;; This file defines less frequently used window organization commands. ;;; Code: (defun window-tree-normal-sizes (window &optional next) "Return normal sizes of all windows rooted at WINDOW. -A list of the form (SPLIT-TYPE PARENT-WIN PARENT-WIN-HEIGHT -PARENT-WIN-WIDTH W1 W2 ...) is returned. SPLIT-TYPE is non-nil if -PARENT-WIN is split horizontally. PARENT-WIN is the internal window. + +The return value is a list of the form (SPLIT-TYPE PARENT-WIN +PARENT-WIN-HEIGHT PARENT-WIN-WIDTH . WS), where SPLIT-TYPE is non-nil if +PARENT-WIN is split horizontally; PARENT-WIN is the internal window; PARENT-WIN-HEIGHT and PARENT-WIN-WIDTH are the normal heights of -PARENT-WIN. Wn is a list of the form (WINDOW HEIGHT WIDTH) where HEIGHT -and WIDTH are the normal height and width of the window." +PARENT-WIN; and WS is a list of lists the form (WINDOW HEIGHT WIDTH) +where HEIGHT and WIDTH are the normal height and width of the window. + +(fn WINDOW)" (let (list) (while window (setq list @@ -62,192 +64,159 @@ and WIDTH are the normal height and width of the window." (setq window (when next (window-next-sibling window)))) (nreverse list))) -(defun window--window-to-transpose (frame-or-window) - "Return the window to be acted upon by `window--transpose'. -If FRAME-OR-WINDOW is a window return FRAME-OR-WINDOW. If -FRAME-OR-WINDOW is a frame, return FRAME-OR-WINDOW's main window. If -FRAME-OR-WINDOW is nil, than the frames main window wil be returned. If -FRAME-OR-WINDOW is non-nil, and not a frame or a window or a number, -than the return value will be the parent window of the selected window." - (cond - ((windowp frame-or-window) - frame-or-window) - ((or (framep frame-or-window) (not frame-or-window)) - (window-main-window frame-or-window)) - (frame-or-window - (window-parent)))) - -(defun rotate-window-layout-anticlockwise (&optional frame-or-window) - "Rotate windows of FRAME-OR-WINDOW anticlockwise by 90 degrees. -Transform the layout of windows such that a window on top becomes a -window on the right, a window on the right moves to the bottom, a window -on the bottom moves to the left and a window on the left becomes one on -the top. - -If FRAME-OR-WINDOW is nil, rotate the main window of the selected -frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main -window of that frame. If FRAME-OR-WINDOW specifies a parent window, -rotate that window. In any other case and interactively with a prefix -argument rotate the parent window of the selected window." - (interactive "P") - (let ((window (window--window-to-transpose frame-or-window))) - (window--transpose window '(right . above) nil))) - -(defun rotate-window-layout-clockwise (&optional frame-or-window) - "Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees. -Transform the layout of windows such that a window on top becomes a -window on the right, a window on the right moves to the bottom, a -window on the bottom moves to the left and a window on the left becomes -one on the top. - -If FRAME-OR-WINDOW is nil, rotate the main window of the selected frame. -If FRAME-OR-WINDOW specifies a live frame, rotate the main window of -that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that -window. In any other case and interactively with a prefix argument -rotate the parent window of the selected window." - (interactive "P") - (let ((window (window--window-to-transpose frame-or-window))) - (window--transpose window '(left . below) nil))) - -(defun flip-window-layout-horizontally (&optional frame-or-window) - "Horizontally flip windows of FRAME-OR-WINDOW. +(defsubst window--rotate-interactive-arg () + "Return interative window argument for window rotation commands." + (if current-prefix-arg (window-parent) (window-main-window))) + +;;;###autoload +(defun rotate-window-layout-counterclockwise (&optional window) + "Rotate windows under WINDOW counterclockwise by 90 degrees. + +If WINDOW is nil, it defaults to the root window of the selected frame. + +Interactively, a prefix argument says to rotate the parent window of the +selected window." + (interactive (list (window--rotate-interactive-arg))) + (window--transpose window '(right . above) nil)) + +;;;###autoload +(defun rotate-window-layout-clockwise (&optional window) + "Rotate windows under WINDOW clockwise by 90 degrees. + +If WINDOW is nil, it defaults to the root window of the selected frame. + +Interactively, a prefix argument says to rotate the parent window of the +selected window." + (interactive (list (window--rotate-interactive-arg))) + (window--transpose window '(left . below) nil)) + +;;;###autoload +(defun flip-window-layout-horizontally (&optional window) + "Horizontally flip windows under WINDOW. + Flip the window layout so that the window on the right becomes the window on the left, and vice-versa. -If FRAME-OR-WINDOW is nil, flip the main window of the selected frame. -If FRAME-OR-WINDOW specifies a live frame, rotate the main window of -that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that -window. In any other case and interactively with a prefix argument -rotate the parent window of the selected window." - (interactive "P") - (let ((window (window--window-to-transpose frame-or-window))) - (window--transpose window '(below . left) t))) - -(defun flip-window-layout-vertically (&optional frame-or-window) - "Verticlly flip windows of FRAME-OR-WINDOW. -Flip the window layout so that the top window becomes the bottom window +If WINDOW is nil, it defaults to the root window of the selected frame. + +Interactively, a prefix argument says to flip the parent window of the +selected window." + (interactive (list (window--rotate-interactive-arg))) + (window--transpose window '(below . left) t)) + +;;;###autoload +(defun flip-window-layout-vertically (&optional window) + "Vertically flip windows under WINDOW. + +Flip the window layout so that the top window becomes the bottom window, and vice-versa. -If FRAME-OR-WINDOW is nil, flip the main window of the selected frame. -If FRAME-OR-WINDOW specifies a live frame, rotate the main window of -that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that -window. In any other case and interactively with a prefix argument -rotate the parent window of the selected window." - (interactive "P") - (let ((window (window--window-to-transpose frame-or-window))) - (window--transpose window '(above . right) t))) - -(defun transpose-window-layout (&optional frame-or-window) - "Transpose windows of FRAME-OR-WINDOW. -Make the windows on FRAME-OR-WINDOW so that every horizontal split +If WINDOW is nil, it defaults to the root window of the selected frame. + +Interactively, a prefix argument says to flip the parent window of the +selected window." + (interactive (list (window--rotate-interactive-arg))) + (window--transpose window '(above . right) t)) + +;;;###autoload +(defun transpose-window-layout (&optional window) + "Transpose windows under WINDOW. + +Reorganize the windows under WINDOW so that every horizontal split becomes a vertical split, and vice versa. This is equivalent to diagonally flipping. -If FRAME-OR-WINDOW is nil, transpose the main window of the selected frame. -If FRAME-OR-WINDOW specifies a live frame, rotate the main window of -that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that -window. In any other case and interactively with a prefix argument -rotate the parent window of the selected window." - (interactive "P") - (let ((window (window--window-to-transpose frame-or-window))) - (window--transpose window '(right . below) nil))) - -(defun window--depmap(fun ls) - "Map FUN across all nodes of list LS." - (if (consp ls) - (cons - (if (consp (car ls)) - (window--depmap fun (car ls)) - (funcall fun (car ls))) - (window--depmap fun (cdr ls))) - (funcall fun ls))) - -(defun rotate-windows-back(&optional frame-or-window) - "Move windows into locations of their predecessors in cyclic ordering. - -If FRAME-OR-WINDOW is nil, rotate the main window of the selected frame. -If FRAME-OR-WINDOW specifies a live frame, rotate the main window of -that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that -window. In any other case and interactively with a prefix argument -rotate the parent window of the selected window." - (interactive "P") - (rotate-windows frame-or-window t)) - -(defun rotate-windows (&optional frame-or-window reverse) - "Move windows into locations of their forerunners in cyclic ordering. - -Else if FRAME-OR-WINDOW is nil, rotate the main window of the -selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the -main window of that frame. If FRAME-OR-WINDOW specifies a parent -window, rotate that window. In any other case and interactively with a -prefix argument rotate the parent window of the selected window." - (interactive "P") - (let ((window (window--window-to-transpose frame-or-window))) - (if (or (not window) - (window-live-p window)) - (message "No windows to transpose") - (let* ((frame (window-frame window)) - (selected-window (frame-selected-window window)) - (win-tree (car (window-tree-normal-sizes window))) - (winls (seq-filter 'window-live-p (flatten-list win-tree))) - (rotated-ls (if reverse - (append (cdr winls) (list (car winls))) - (append (last winls) winls))) - (other-window-arg (if reverse 1 -1)) - (first-window (car rotated-ls)) - (new-win-tree (window--depmap - (lambda (x) - (if (window-live-p x) - (pop rotated-ls) - x)) - win-tree))) - (if (or (seq-some 'window-atom-root winls) - (seq-some 'window-fixed-size-p winls)) - (message "This does not work with fixed size or atom windows.") - (progn - ;; All child windows need to be recursively deleted. - (delete-other-windows-internal first-window window) - ;; (delete-dups atom-windows) - (window--transpose-1 new-win-tree first-window '(below . right) t nil) - (set-frame-selected-window frame selected-window) - (other-window other-window-arg) - (while (not (memq (selected-window) winls)) - (other-window other-window-arg)))))))) +If WINDOW is nil, it defaults to the root window of the selected frame. + +Interactively, a prefix argument says to transpose the parent window of +the selected window." + (interactive (list (window--rotate-interactive-arg))) + (window--transpose window '(right . below) nil)) + +;;;###autoload +(defun rotate-windows-back (&optional window) + "Rotate windows under WINDOW backward in cyclic ordering. + +If WINDOW is nil, it defaults to the root window of the selected frame. + +Interactively, a prefix argument says to rotate the parent window of the +selected window." + (interactive (list (window--rotate-interactive-arg))) + (rotate-windows window t)) + +;;;###autoload +(defun rotate-windows (&optional window reverse) + "Rotate windows under WINDOW in cyclic ordering. + +Optional argument REVERSE says to rotate windows backward, in reverse +cyclic order. + +If WINDOW is nil, it defaults to the root window of the selected frame. + +Interactively, a prefix argument says to rotate the parent window of the +selected window." + (interactive (list (window--rotate-interactive-arg))) + (when (or (not window) (window-live-p window)) + (user-error "No windows to transpose")) + (let* ((frame (window-frame window)) + (selected-window (frame-selected-window window)) + (win-tree (car (window-tree-normal-sizes window))) + (winls (seq-filter #'window-live-p (flatten-list win-tree))) + (rotated-ls (if reverse + (append (cdr winls) (list (car winls))) + (append (last winls) winls))) + (other-window-arg (if reverse 1 -1)) + (first-window (car rotated-ls)) + (new-win-tree + ;; Recursively process `win-tree' and construct a new tree + ;; with the same shape and rotated windows at the leaves. + (named-let rec ((tree win-tree)) + (cond + ((consp tree) (cons (rec (car tree)) (rec (cdr tree)))) + ((window-live-p tree) (pop rotated-ls)) + (t tree))))) + (when (or (seq-some #'window-atom-root winls) + (seq-some #'window-fixed-size-p winls)) + (user-error "Cannot rotate windows due to fixed size or atom windows")) + ;; All child windows need to be recursively deleted. + (delete-other-windows-internal first-window window) + ;; (delete-dups atom-windows) + (window--transpose-1 new-win-tree first-window '(below . right) t nil) + (set-frame-selected-window frame selected-window) + (other-window other-window-arg) + (while (not (memq (selected-window) winls)) + (other-window other-window-arg)))) (defun window--transpose (window conf no-resize) - "Rearrange windows of WINDOW recursively. -CONF should be a cons cell: (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where + "Rearrange windows under WINDOW recursively. +CONF should be a cons cell (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where HORIZONTAL-SPLIT will be used as the third argument of `split-window' when splitting a window that was previously horizontally split, and VERTICAL-SPLIT as third argument of `split-window' for a window that was previously vertically split. If NO-RESIZE is nil, the SIDE argument of the window-split is converted from vertical to horizontal or vice versa, with the same proportion of the total split." - (if (or (not window) - (window-live-p window)) - (message "No windows to transpose") - (let* ((frame (window-frame window)) - (first-window window) - (selected-window (frame-selected-window window)) - (win-tree (car (window-tree-normal-sizes window))) - (win-list (seq-filter 'window-live-p (flatten-list win-tree))) - (atom-windows - (remq nil (mapcar 'window-atom-root - win-list)))) - (if (and (not (eq (car atom-windows) window)) - (or no-resize - (and (not atom-windows) - (not (seq-some 'window-fixed-size-p win-list))))) - (progn - (delete-dups atom-windows) - (while (not (window-live-p first-window)) - (setq first-window (window-child first-window))) - (delete-other-windows-internal first-window window) - (window--transpose-1 win-tree first-window conf no-resize atom-windows) - ;; Go back to previously selected window. - (set-frame-selected-window frame selected-window) - (mapc 'window-make-atom atom-windows)) - (message "This does not work with fixed size or atom windows."))))) + (when (or (not window) (window-live-p window)) + (user-error "No windows to transpose")) + (let* ((frame (window-frame window)) + (first-window window) + (selected-window (frame-selected-window window)) + (win-tree (car (window-tree-normal-sizes window))) + (win-list (seq-filter #'window-live-p (flatten-list win-tree))) + (atom-windows (seq-keep #'window-atom-root win-list))) + (unless (and (not (eq (car atom-windows) window)) + (or no-resize + (and (not atom-windows) + (not (seq-some #'window-fixed-size-p win-list))))) + (user-error "Cannot rotate windows due to fixed size or atom windows")) + (delete-dups atom-windows) + (while (not (window-live-p first-window)) + (setq first-window (window-child first-window))) + (delete-other-windows-internal first-window window) + (window--transpose-1 win-tree first-window conf no-resize atom-windows) + ;; Go back to previously selected window. + (set-frame-selected-window frame selected-window) + (mapc #'window-make-atom atom-windows))) (defun window--transpose-1 (subtree cwin conf no-resize atom-windows) "Subroutine of `window--transpose'. @@ -259,8 +228,7 @@ ones in `window--transpose'." ;; `flen' is max size the window could be converted to the opposite ;; of the given split type. (let ((parent-window-is-set t) - (flen (if (funcall (if no-resize 'not 'identity) - (car subtree)) + (flen (if (xor no-resize (car subtree)) (float (window-pixel-width cwin)) (float (window-pixel-height cwin))))) (mapc @@ -268,7 +236,7 @@ ones in `window--transpose'." (prog1 (let* ((split-size (- (round (* flen size)))) (split-type - (funcall (if (car subtree) 'car 'cdr) conf)) + (funcall (if (car subtree) #'car #'cdr) conf)) (return-win (if (listp window) ;; `window' is a window subtree. @@ -293,9 +261,7 @@ ones in `window--transpose'." (if window-combination-limit (cons (caar (cddddr first-child)) (cadr subtree)) (caar (cddddr first-child))))) - (if is-atom - '(nil . t) - conf) + (if is-atom '(nil . t) conf) no-resize atom-windows)) ;; `window' is a window. @@ -323,14 +289,16 @@ ones in `window--transpose'." (if (car subtree) (cadr window-size-info) (caddr window-size-info))))) - ;; We need to ingore first 5 elements of window list, we ignore + ;; We need to ignore first 5 elements of window list, we ignore ;; window split type, sizes and the first window (it's ;; implicitly created). We just have a list of windows. (nreverse (cdr (cddddr subtree))))) ;; (caar (cddddr subtree)) is the first child window of subtree. (unless (windowp (caar (cddddr subtree))) (let ((is-atom (memq (cadr (cadr (cddddr subtree))) atom-windows))) - (window--transpose-1 (car (cddddr subtree)) cwin (if is-atom '(nil . t) conf) + (window--transpose-1 (car (cddddr subtree)) cwin + (if is-atom '(nil . t) conf) no-resize atom-windows))))) +(provide 'window-x) ;;; window-x.el ends here