commit 73423a1e0e98534512f63929e174a8578a1ac4f2 (HEAD, refs/remotes/origin/master) Author: Roi Martin Date: Mon Oct 6 22:25:57 2025 +0200 Fix font lock in csharp-ts-mode Fix font lock in csharp-ts-mode when the tree-sitter grammar is automatically installed (Bug#79363). * lisp/progmodes/csharp-mode.el (csharp-ts-mode--font-lock-settings): Evaluate the rules only after the tree-sitter grammar is installed. (csharp-ts-mode): Call the new `csharp-ts-mode--font-lock-settings' function. diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index d8b20e1f6f0..236354ece27 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -784,340 +784,349 @@ compilation and evaluation time conflicts." (if (csharp-ts-mode--test-method-declaration-type-field) 'type: 'returns:)) -(defvar csharp-ts-mode--font-lock-settings - (treesit-font-lock-rules - :language 'c-sharp - :feature 'expression - '((conditional_expression (identifier) @font-lock-variable-use-face) - (postfix_unary_expression (identifier)* @font-lock-variable-use-face) - (initializer_expression (assignment_expression left: (identifier) @font-lock-property-use-face)) - (anonymous_object_creation_expression - (identifier) @font-lock-property-use-face - (identifier) @font-lock-variable-use-face) - (anonymous_object_creation_expression - (identifier) @font-lock-property-use-face - [(object_creation_expression) - (integer_literal) - (string_literal) - (binary_expression) - (invocation_expression) - (member_access_expression) - (conditional_expression)]) - (interpolated_string_expression - (interpolation - (identifier) @font-lock-variable-use-face)) - (interpolated_string_expression - (interpolation - (member_access_expression - name: (identifier) @font-lock-property-use-face))) - ((interpolated_string_expression - (interpolation - (member_access_expression - expression: (identifier) @font-lock-variable-use-face))) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((element_access_expression (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((element_access_expression (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((return_statement (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((return_statement (member_access_expression - expression: (identifier) @font-lock-variable-use-face)) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((is_pattern_expression - expression: (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((is_pattern_expression - expression: (member_access_expression - expression: (identifier) @font-lock-variable-use-face)) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - (is_pattern_expression - expression: (member_access_expression - name: (identifier) @font-lock-property-use-face)) - (is_pattern_expression - pattern: (constant_pattern (identifier) @font-lock-type-face)) - (is_pattern_expression - pattern: (constant_pattern (member_access_expression - name: (identifier) @font-lock-type-face))) - ((binary_expression - left: (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((binary_expression - right: (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - (assignment_expression - right: (identifier) @font-lock-variable-use-face) - (expression_statement ;; capture parent node to NOT shadow variable_declaration. - (assignment_expression - left: (identifier) @font-lock-variable-use-face)) - (if_statement condition: (identifier) @font-lock-variable-use-face) - - ;; handle more specific matchers before generalized variable-use fallback. - (invocation_expression - function: (member_access_expression - name: (identifier) @font-lock-function-call-face)) - (invocation_expression - function: (member_access_expression - name: (generic_name (identifier) @font-lock-function-call-face))) - (member_access_expression - expression: (identifier) @font-lock-variable-use-face - name: (identifier) @font-lock-property-use-face)) - - :language 'c-sharp - :feature 'bracket - '((["(" ")" "[" "]" "{" "}" (interpolation_brace)]) @font-lock-bracket-face) - - :language 'c-sharp - :feature 'delimiter - '((["," ":" ";"]) @font-lock-delimiter-face) - - :language 'c-sharp - :feature 'error - '((ERROR) @font-lock-warning-face) - - :language 'c-sharp - :override t - :feature 'comment - '((comment) @font-lock-comment-face) - - :language 'c-sharp - :override t - :feature 'keyword - `([,@csharp-ts-mode--keywords] @font-lock-keyword-face - (modifier) @font-lock-keyword-face - ,@(if (csharp-ts-mode--test-this-expression) - '((this_expression) @font-lock-keyword-face) - '("this" @font-lock-keyword-face)) - - ;; avoid fontifying indentifiers with a keyword-values as identifiers. - ((identifier) @font-lock-keyword-face - (:match ,(concat "\\`" (regexp-opt csharp-ts-mode--keywords t) "\\'") @font-lock-keyword-face))) - - :language 'c-sharp - :override t - :feature 'attribute - `((attribute_list - "[" @csharp-ts-mode-attribute-face - (attribute name: (identifier) @csharp-ts-mode-attribute-face) - "]" @csharp-ts-mode-attribute-face)) - - :language 'c-sharp - :override t - :feature 'escape-sequence - '((escape_sequence) @font-lock-escape-face) - - :language 'c-sharp - :override t - :feature 'literal - `((integer_literal) @font-lock-number-face - (real_literal) @font-lock-number-face - (null_literal) @font-lock-constant-face - (boolean_literal) @font-lock-constant-face) - - :language 'c-sharp - :feature 'string - `([(string_literal) - (verbatim_string_literal) - ,@ (when (csharp-ts-mode--test-string-content) - '((string_content) - "\"")) - ,@(if (csharp-ts-mode--test-interpolated-string-text) - '((interpolated_string_text) - (interpolated_verbatim_string_text) - (character_literal) - "\"" - "$\"" - "@$\"" - "$@\"") - '((interpolation_start) - (interpolation_quote)))] - @font-lock-string-face) - - :language 'c-sharp - :feature 'type - `((predefined_type) @font-lock-type-face - (implicit_type) @font-lock-type-face - (nullable_type) @font-lock-type-face - (type_parameter - (identifier) @font-lock-type-face) - (type_argument_list - (identifier) @font-lock-type-face) - (type_argument_list - (generic_name - (identifier) @font-lock-type-face)) - (base_list - (generic_name - (identifier) @font-lock-type-face)) - (array_type - (identifier) @font-lock-type-face) - (qualified_name - name: (generic_name (identifier) @font-lock-type-face)) - (cast_expression (identifier) @font-lock-type-face) - (cast_expression (generic_name (identifier) @font-lock-type-face)) - ["operator"] @font-lock-type-face - (type_parameter_constraints_clause - (identifier) @font-lock-type-face) - ,@(if (csharp-ts-mode--test-type-constraint) - '((type_constraint type: (identifier) @font-lock-type-face) - (type_constraint type: (generic_name (identifier) @font-lock-type-face))) - '((type_parameter_constraint (type type: (identifier) @font-lock-type-face)) - (type_parameter_constraint (type type: (generic_name (identifier) @font-lock-type-face))))) - - ,@(when (csharp-ts-mode--test-type-of-expression) - '((type_of_expression (identifier) @font-lock-type-face))) - - ,@(when (csharp-ts-mode--test-typeof-expression) - '((typeof_expression (identifier) @font-lock-type-face))) - - (object_creation_expression - type: (identifier) @font-lock-type-face) - (object_creation_expression - type: (generic_name (identifier) @font-lock-type-face)) - (as_expression right: (identifier) @font-lock-type-face) - (as_expression right: (generic_name (identifier) @font-lock-type-face))) - - :language 'c-sharp - :feature 'definition - `((qualified_name (identifier) @font-lock-type-face) - (using_directive (identifier) @font-lock-type-face) - ,@(when (csharp-ts-mode--test-name-equals) - '((using_directive (name_equals - (identifier) @font-lock-type-face)))) - - (enum_declaration (identifier) @font-lock-type-face) - (enum_member_declaration (identifier) @font-lock-variable-name-face) - (field_declaration (variable_declaration (variable_declarator - name: (identifier) @font-lock-variable-name-face))) - - (interface_declaration (identifier) @font-lock-type-face) - - (struct_declaration (identifier) @font-lock-type-face) - - (record_declaration (identifier) @font-lock-type-face) - (namespace_declaration (identifier) @font-lock-type-face) - (base_list (identifier) @font-lock-type-face) - (property_declaration - type: (nullable_type) @font-lock-type-face - name: (identifier) @font-lock-variable-name-face) - (property_declaration - type: (predefined_type) @font-lock-type-face - name: (identifier) @font-lock-variable-name-face) - (property_declaration - type: (identifier) @font-lock-type-face - name: (identifier) @font-lock-variable-name-face) - (class_declaration (identifier) @font-lock-type-face) - - (constructor_declaration name: (_) @font-lock-type-face) - ;;; Handle different releases of tree-sitter-c-sharp. - ;;; Check if keyword void_keyword is available, then return the correct rule." - ,@(condition-case nil - (progn (treesit-query-capture 'csharp '((void_keyword) @capture)) - `((method_declaration ,csharp-ts-mode--type-field [(identifier) (void_keyword)] @font-lock-type-face))) - (error - `((method_declaration ,csharp-ts-mode--type-field [(identifier) (predefined_type)] @font-lock-type-face)))) - (method_declaration ,csharp-ts-mode--type-field (generic_name (identifier) @font-lock-type-face)) - (method_declaration name: (_) @font-lock-function-name-face) - - ;; only fontify known expression-types, to avoid the need to use :override - ;; for lambda-expressions in 'function below. - (variable_declarator - name: (identifier) @font-lock-variable-name-face - [(object_creation_expression) - (integer_literal) - (string_literal) - (binary_expression) - (invocation_expression) - (member_access_expression) - (conditional_expression)]) - - (catch_declaration - ((identifier) @font-lock-type-face)) - (catch_declaration - ((identifier) @font-lock-type-face - (identifier) @font-lock-variable-name-face)) - - (variable_declaration (identifier) @font-lock-type-face) - (variable_declaration (qualified_name - name: (generic_name (identifier) @font-lock-type-face))) - (variable_declaration (generic_name (identifier) @font-lock-type-face)) - - (parameter type: (identifier) @font-lock-type-face) - (parameter type: (generic_name (identifier) @font-lock-type-face)) - (parameter name: (identifier) @font-lock-variable-name-face) - - (lambda_expression (identifier) @font-lock-variable-name-face) - (lambda_expression - parameters: (implicit_parameter) @font-lock-variable-name-face) - - (declaration_expression type: (identifier) @font-lock-type-face) - (declaration_expression name: (identifier) @font-lock-variable-name-face)) - - :language 'c-sharp - :feature 'function - '((invocation_expression - function: (identifier) @font-lock-function-call-face) - ((invocation_expression - function: (member_access_expression - expression: (identifier) @font-lock-variable-use-face)) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - (argument (identifier) @font-lock-variable-use-face) - ((argument (member_access_expression - expression: (identifier) @font-lock-variable-use-face)) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - (argument (member_access_expression +(defvar csharp-ts-mode--font-lock-settings-cached nil + "Cached tree-sitter font-lock settings for `csharp-ts-mode'.") + +(defun csharp-ts-mode--font-lock-settings () + "Return tree-sitter font-lock settings for `csharp-ts-mode'. + +Tree-sitter font-lock settings are evaluated the first time this +function is called. Subsequent calls return the first evaluated value." + (or csharp-ts-mode--font-lock-settings-cached + (setq csharp-ts-mode--font-lock-settings-cached + (treesit-font-lock-rules + :language 'c-sharp + :feature 'expression + '((conditional_expression (identifier) @font-lock-variable-use-face) + (postfix_unary_expression (identifier)* @font-lock-variable-use-face) + (initializer_expression (assignment_expression left: (identifier) @font-lock-property-use-face)) + (anonymous_object_creation_expression + (identifier) @font-lock-property-use-face + (identifier) @font-lock-variable-use-face) + (anonymous_object_creation_expression + (identifier) @font-lock-property-use-face + [(object_creation_expression) + (integer_literal) + (string_literal) + (binary_expression) + (invocation_expression) + (member_access_expression) + (conditional_expression)]) + (interpolated_string_expression + (interpolation + (identifier) @font-lock-variable-use-face)) + (interpolated_string_expression + (interpolation + (member_access_expression + name: (identifier) @font-lock-property-use-face))) + ((interpolated_string_expression + (interpolation + (member_access_expression + expression: (identifier) @font-lock-variable-use-face))) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((element_access_expression (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((element_access_expression (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((return_statement (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((return_statement (member_access_expression + expression: (identifier) @font-lock-variable-use-face)) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((is_pattern_expression + expression: (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((is_pattern_expression + expression: (member_access_expression + expression: (identifier) @font-lock-variable-use-face)) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + (is_pattern_expression + expression: (member_access_expression + name: (identifier) @font-lock-property-use-face)) + (is_pattern_expression + pattern: (constant_pattern (identifier) @font-lock-type-face)) + (is_pattern_expression + pattern: (constant_pattern (member_access_expression + name: (identifier) @font-lock-type-face))) + ((binary_expression + left: (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((binary_expression + right: (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + (assignment_expression + right: (identifier) @font-lock-variable-use-face) + (expression_statement ;; capture parent node to NOT shadow variable_declaration. + (assignment_expression + left: (identifier) @font-lock-variable-use-face)) + (if_statement condition: (identifier) @font-lock-variable-use-face) + + ;; handle more specific matchers before generalized variable-use fallback. + (invocation_expression + function: (member_access_expression + name: (identifier) @font-lock-function-call-face)) + (invocation_expression + function: (member_access_expression + name: (generic_name (identifier) @font-lock-function-call-face))) + (member_access_expression + expression: (identifier) @font-lock-variable-use-face name: (identifier) @font-lock-property-use-face)) - ;; only highlight as function if variable contains lambda expression - (variable_declarator - name: (identifier) @font-lock-function-name-face - (lambda_expression))) - - :language 'c-sharp - :feature 'escape-sequence - :override t - '((escape_sequence) @font-lock-escape-face) - - :language 'c-sharp - :feature 'directives - :override t - (if (csharp-ts-mode--test-if-directive) - '((if_directive - "if" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (elif_directive - "elif" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (else_directive) @font-lock-preprocessor-face - (endif_directive) @font-lock-preprocessor-face - (define_directive - "define" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (nullable_directive) @font-lock-preprocessor-face - (pragma_directive) @font-lock-preprocessor-face - (region_directive) @font-lock-preprocessor-face - (endregion_directive) @font-lock-preprocessor-face - (region_directive - (preproc_message) @font-lock-variable-use-face) - (endregion_directive - (preproc_message) @font-lock-variable-use-face)) - '((preproc_if - "#if" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (preproc_elif - "#elif" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (preproc_else) @font-lock-preprocessor-face - "#endif" @font-lock-preprocessor-face - (preproc_define - "#define" @font-lock-preprocessor-face - (preproc_arg) @font-lock-variable-use-face) - (preproc_nullable) @font-lock-preprocessor-face - (preproc_pragma) @font-lock-preprocessor-face - (preproc_region) @font-lock-preprocessor-face - (preproc_endregion) @font-lock-preprocessor-face - (preproc_region - (preproc_arg) @font-lock-variable-use-face) - (preproc_endregion - (preproc_arg) @font-lock-variable-use-face))))) + + :language 'c-sharp + :feature 'bracket + '((["(" ")" "[" "]" "{" "}" (interpolation_brace)]) @font-lock-bracket-face) + + :language 'c-sharp + :feature 'delimiter + '((["," ":" ";"]) @font-lock-delimiter-face) + + :language 'c-sharp + :feature 'error + '((ERROR) @font-lock-warning-face) + + :language 'c-sharp + :override t + :feature 'comment + '((comment) @font-lock-comment-face) + + :language 'c-sharp + :override t + :feature 'keyword + `([,@csharp-ts-mode--keywords] @font-lock-keyword-face + (modifier) @font-lock-keyword-face + ,@(if (csharp-ts-mode--test-this-expression) + '((this_expression) @font-lock-keyword-face) + '("this" @font-lock-keyword-face)) + + ;; avoid fontifying indentifiers with a keyword-values as identifiers. + ((identifier) @font-lock-keyword-face + (:match ,(concat "\\`" (regexp-opt csharp-ts-mode--keywords t) "\\'") @font-lock-keyword-face))) + + :language 'c-sharp + :override t + :feature 'attribute + `((attribute_list + "[" @csharp-ts-mode-attribute-face + (attribute name: (identifier) @csharp-ts-mode-attribute-face) + "]" @csharp-ts-mode-attribute-face)) + + :language 'c-sharp + :override t + :feature 'escape-sequence + '((escape_sequence) @font-lock-escape-face) + + :language 'c-sharp + :override t + :feature 'literal + `((integer_literal) @font-lock-number-face + (real_literal) @font-lock-number-face + (null_literal) @font-lock-constant-face + (boolean_literal) @font-lock-constant-face) + + :language 'c-sharp + :feature 'string + `([(string_literal) + (verbatim_string_literal) + ,@ (when (csharp-ts-mode--test-string-content) + '((string_content) + "\"")) + ,@(if (csharp-ts-mode--test-interpolated-string-text) + '((interpolated_string_text) + (interpolated_verbatim_string_text) + (character_literal) + "\"" + "$\"" + "@$\"" + "$@\"") + '((interpolation_start) + (interpolation_quote)))] + @font-lock-string-face) + + :language 'c-sharp + :feature 'type + `((predefined_type) @font-lock-type-face + (implicit_type) @font-lock-type-face + (nullable_type) @font-lock-type-face + (type_parameter + (identifier) @font-lock-type-face) + (type_argument_list + (identifier) @font-lock-type-face) + (type_argument_list + (generic_name + (identifier) @font-lock-type-face)) + (base_list + (generic_name + (identifier) @font-lock-type-face)) + (array_type + (identifier) @font-lock-type-face) + (qualified_name + name: (generic_name (identifier) @font-lock-type-face)) + (cast_expression (identifier) @font-lock-type-face) + (cast_expression (generic_name (identifier) @font-lock-type-face)) + ["operator"] @font-lock-type-face + (type_parameter_constraints_clause + (identifier) @font-lock-type-face) + ,@(if (csharp-ts-mode--test-type-constraint) + '((type_constraint type: (identifier) @font-lock-type-face) + (type_constraint type: (generic_name (identifier) @font-lock-type-face))) + '((type_parameter_constraint (type type: (identifier) @font-lock-type-face)) + (type_parameter_constraint (type type: (generic_name (identifier) @font-lock-type-face))))) + + ,@(when (csharp-ts-mode--test-type-of-expression) + '((type_of_expression (identifier) @font-lock-type-face))) + + ,@(when (csharp-ts-mode--test-typeof-expression) + '((typeof_expression (identifier) @font-lock-type-face))) + + (object_creation_expression + type: (identifier) @font-lock-type-face) + (object_creation_expression + type: (generic_name (identifier) @font-lock-type-face)) + (as_expression right: (identifier) @font-lock-type-face) + (as_expression right: (generic_name (identifier) @font-lock-type-face))) + + :language 'c-sharp + :feature 'definition + `((qualified_name (identifier) @font-lock-type-face) + (using_directive (identifier) @font-lock-type-face) + ,@(when (csharp-ts-mode--test-name-equals) + '((using_directive (name_equals + (identifier) @font-lock-type-face)))) + + (enum_declaration (identifier) @font-lock-type-face) + (enum_member_declaration (identifier) @font-lock-variable-name-face) + (field_declaration (variable_declaration (variable_declarator + name: (identifier) @font-lock-variable-name-face))) + + (interface_declaration (identifier) @font-lock-type-face) + + (struct_declaration (identifier) @font-lock-type-face) + + (record_declaration (identifier) @font-lock-type-face) + (namespace_declaration (identifier) @font-lock-type-face) + (base_list (identifier) @font-lock-type-face) + (property_declaration + type: (nullable_type) @font-lock-type-face + name: (identifier) @font-lock-variable-name-face) + (property_declaration + type: (predefined_type) @font-lock-type-face + name: (identifier) @font-lock-variable-name-face) + (property_declaration + type: (identifier) @font-lock-type-face + name: (identifier) @font-lock-variable-name-face) + (class_declaration (identifier) @font-lock-type-face) + + (constructor_declaration name: (_) @font-lock-type-face) + ;; Handle different releases of tree-sitter-c-sharp. + ;; Check if keyword void_keyword is available, then return the correct rule." + ,@(condition-case nil + (progn (treesit-query-capture 'csharp '((void_keyword) @capture)) + `((method_declaration ,csharp-ts-mode--type-field [(identifier) (void_keyword)] @font-lock-type-face))) + (error + `((method_declaration ,csharp-ts-mode--type-field [(identifier) (predefined_type)] @font-lock-type-face)))) + (method_declaration ,csharp-ts-mode--type-field (generic_name (identifier) @font-lock-type-face)) + (method_declaration name: (_) @font-lock-function-name-face) + + ;; only fontify known expression-types, to avoid the need to use :override + ;; for lambda-expressions in 'function below. + (variable_declarator + name: (identifier) @font-lock-variable-name-face + [(object_creation_expression) + (integer_literal) + (string_literal) + (binary_expression) + (invocation_expression) + (member_access_expression) + (conditional_expression)]) + + (catch_declaration + ((identifier) @font-lock-type-face)) + (catch_declaration + ((identifier) @font-lock-type-face + (identifier) @font-lock-variable-name-face)) + + (variable_declaration (identifier) @font-lock-type-face) + (variable_declaration (qualified_name + name: (generic_name (identifier) @font-lock-type-face))) + (variable_declaration (generic_name (identifier) @font-lock-type-face)) + + (parameter type: (identifier) @font-lock-type-face) + (parameter type: (generic_name (identifier) @font-lock-type-face)) + (parameter name: (identifier) @font-lock-variable-name-face) + + (lambda_expression (identifier) @font-lock-variable-name-face) + (lambda_expression + parameters: (implicit_parameter) @font-lock-variable-name-face) + + (declaration_expression type: (identifier) @font-lock-type-face) + (declaration_expression name: (identifier) @font-lock-variable-name-face)) + + :language 'c-sharp + :feature 'function + '((invocation_expression + function: (identifier) @font-lock-function-call-face) + ((invocation_expression + function: (member_access_expression + expression: (identifier) @font-lock-variable-use-face)) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + (argument (identifier) @font-lock-variable-use-face) + ((argument (member_access_expression + expression: (identifier) @font-lock-variable-use-face)) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + (argument (member_access_expression + name: (identifier) @font-lock-property-use-face)) + ;; only highlight as function if variable contains lambda expression + (variable_declarator + name: (identifier) @font-lock-function-name-face + (lambda_expression))) + + :language 'c-sharp + :feature 'escape-sequence + :override t + '((escape_sequence) @font-lock-escape-face) + + :language 'c-sharp + :feature 'directives + :override t + (if (csharp-ts-mode--test-if-directive) + '((if_directive + "if" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (elif_directive + "elif" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (else_directive) @font-lock-preprocessor-face + (endif_directive) @font-lock-preprocessor-face + (define_directive + "define" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (nullable_directive) @font-lock-preprocessor-face + (pragma_directive) @font-lock-preprocessor-face + (region_directive) @font-lock-preprocessor-face + (endregion_directive) @font-lock-preprocessor-face + (region_directive + (preproc_message) @font-lock-variable-use-face) + (endregion_directive + (preproc_message) @font-lock-variable-use-face)) + '((preproc_if + "#if" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (preproc_elif + "#elif" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (preproc_else) @font-lock-preprocessor-face + "#endif" @font-lock-preprocessor-face + (preproc_define + "#define" @font-lock-preprocessor-face + (preproc_arg) @font-lock-variable-use-face) + (preproc_nullable) @font-lock-preprocessor-face + (preproc_pragma) @font-lock-preprocessor-face + (preproc_region) @font-lock-preprocessor-face + (preproc_endregion) @font-lock-preprocessor-face + (preproc_region + (preproc_arg) @font-lock-variable-use-face) + (preproc_endregion + (preproc_arg) @font-lock-variable-use-face))))))) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode)) @@ -1245,7 +1254,7 @@ Key bindings: (setq-local treesit-defun-name-function #'csharp-ts-mode--defun-name) ;; Font-lock. - (setq-local treesit-font-lock-settings csharp-ts-mode--font-lock-settings) + (setq-local treesit-font-lock-settings (csharp-ts-mode--font-lock-settings)) (setq-local treesit-font-lock-feature-list '(( comment definition) ( keyword string type directives) commit 844a510a2b847edb472a9fc73a3511620d47dca0 Author: Roi Martin Date: Fri Sep 19 14:46:45 2025 +0200 Fix font lock in java-ts-mode Fix font lock in java-ts-mode when the tree-sitter grammar is automatically installed (Bug#79363). * lisp/progmodes/java-ts-mode.el (java-ts-mode--font-lock-settings): Evaluate the rules only after the tree-sitter grammar is installed. (java-ts-mode): Call the new `java-ts-mode--font-lock-settings' function. diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el index 979f5456c6d..f8c486e1bea 100644 --- a/lisp/progmodes/java-ts-mode.el +++ b/lisp/progmodes/java-ts-mode.el @@ -237,135 +237,143 @@ For NODE, OVERRIDE, START, and END, see `treesit-font-lock-rules'." 'font-lock-constant-face override start end)))) -(defvar java-ts-mode--font-lock-settings - (treesit-font-lock-rules - :language 'java - :override t - :feature 'comment - `((line_comment) @font-lock-comment-face - (block_comment) @font-lock-comment-face) - :language 'java - :override t - :feature 'keyword - `([,@java-ts-mode--keywords - (this) - (super)] @font-lock-keyword-face - (labeled_statement - (identifier) @font-lock-keyword-face)) - :language 'java - :override t - :feature 'operator - `([,@java-ts-mode--operators] @font-lock-operator-face - "@" @font-lock-constant-face) - :language 'java - :override t - :feature 'annotation - `((annotation - name: (identifier) @font-lock-constant-face) - - (marker_annotation - name: (identifier) @font-lock-constant-face)) - :language 'java - :override t - :feature 'string - (java-ts-mode--string-highlight-helper) - :language 'java - :override t - :feature 'literal - `((null_literal) @font-lock-constant-face - (binary_integer_literal) @font-lock-number-face - (decimal_integer_literal) @font-lock-number-face - (hex_integer_literal) @font-lock-number-face - (octal_integer_literal) @font-lock-number-face - (decimal_floating_point_literal) @font-lock-number-face - (hex_floating_point_literal) @font-lock-number-face) - :language 'java - :override t - :feature 'type - '((annotation_type_declaration - name: (identifier) @font-lock-type-face) - - (interface_declaration - name: (identifier) @font-lock-type-face) - - (class_declaration - name: (identifier) @font-lock-type-face) - - (record_declaration - name: (identifier) @font-lock-type-face) - - (enum_declaration - name: (identifier) @font-lock-type-face) - - (constructor_declaration - name: (identifier) @font-lock-type-face) - - (compact_constructor_declaration - name: (identifier) @font-lock-type-face) - - (field_access - object: (identifier) @font-lock-type-face) - - (method_reference (identifier) @font-lock-type-face) - - (scoped_identifier (identifier) @font-lock-constant-face) - - ((scoped_identifier name: (identifier) @font-lock-type-face) - (:match "\\`[A-Z]" @font-lock-type-face)) - - (type_identifier) @font-lock-type-face - - [(boolean_type) - (integral_type) - (floating_point_type) - (void_type)] @font-lock-type-face) - :language 'java - :override t - :feature 'definition - `((annotation_type_element_declaration - name: (identifier) @font-lock-function-name-face) - - (method_declaration - name: (identifier) @font-lock-function-name-face) - - (variable_declarator - name: (identifier) @font-lock-variable-name-face) - - (element_value_pair - key: (identifier) @font-lock-property-use-face) - - (formal_parameter - name: (identifier) @font-lock-variable-name-face) - - (catch_formal_parameter - name: (identifier) @font-lock-variable-name-face)) - :language 'java - :override t - :feature 'expression - '((method_invocation - object: (identifier) @font-lock-variable-use-face) - - (method_invocation - name: (identifier) @font-lock-function-call-face) - - (argument_list (identifier) @font-lock-variable-name-face) - - (expression_statement (identifier) @font-lock-variable-use-face)) - ;; Make sure the constant feature is after expression and definition, - ;; because those two applies variable-name-face on some constants. - :language 'java - :override t - :feature 'constant - `((identifier) @java-ts-mode--fontify-constant - [(true) (false)] @font-lock-constant-face) - :language 'java - :feature 'bracket - '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face) - - :language 'java - :feature 'delimiter - '((["," ":" ";"]) @font-lock-delimiter-face)) - "Tree-sitter font-lock settings for `java-ts-mode'.") +(defvar java-ts-mode--font-lock-settings-cached nil + "Cached tree-sitter font-lock settings for `java-ts-mode'.") + +(defun java-ts-mode--font-lock-settings () + "Return tree-sitter font-lock settings for `java-ts-mode'. + +Tree-sitter font-lock settings are evaluated the first time this +function is called. Subsequent calls return the first evaluated value." + (or java-ts-mode--font-lock-settings-cached + (setq java-ts-mode--font-lock-settings-cached + (treesit-font-lock-rules + :language 'java + :override t + :feature 'comment + `((line_comment) @font-lock-comment-face + (block_comment) @font-lock-comment-face) + :language 'java + :override t + :feature 'keyword + `([,@java-ts-mode--keywords + (this) + (super)] @font-lock-keyword-face + (labeled_statement + (identifier) @font-lock-keyword-face)) + :language 'java + :override t + :feature 'operator + `([,@java-ts-mode--operators] @font-lock-operator-face + "@" @font-lock-constant-face) + :language 'java + :override t + :feature 'annotation + `((annotation + name: (identifier) @font-lock-constant-face) + + (marker_annotation + name: (identifier) @font-lock-constant-face)) + :language 'java + :override t + :feature 'string + (java-ts-mode--string-highlight-helper) + :language 'java + :override t + :feature 'literal + `((null_literal) @font-lock-constant-face + (binary_integer_literal) @font-lock-number-face + (decimal_integer_literal) @font-lock-number-face + (hex_integer_literal) @font-lock-number-face + (octal_integer_literal) @font-lock-number-face + (decimal_floating_point_literal) @font-lock-number-face + (hex_floating_point_literal) @font-lock-number-face) + :language 'java + :override t + :feature 'type + '((annotation_type_declaration + name: (identifier) @font-lock-type-face) + + (interface_declaration + name: (identifier) @font-lock-type-face) + + (class_declaration + name: (identifier) @font-lock-type-face) + + (record_declaration + name: (identifier) @font-lock-type-face) + + (enum_declaration + name: (identifier) @font-lock-type-face) + + (constructor_declaration + name: (identifier) @font-lock-type-face) + + (compact_constructor_declaration + name: (identifier) @font-lock-type-face) + + (field_access + object: (identifier) @font-lock-type-face) + + (method_reference (identifier) @font-lock-type-face) + + (scoped_identifier (identifier) @font-lock-constant-face) + + ((scoped_identifier name: (identifier) @font-lock-type-face) + (:match "\\`[A-Z]" @font-lock-type-face)) + + (type_identifier) @font-lock-type-face + + [(boolean_type) + (integral_type) + (floating_point_type) + (void_type)] @font-lock-type-face) + :language 'java + :override t + :feature 'definition + `((annotation_type_element_declaration + name: (identifier) @font-lock-function-name-face) + + (method_declaration + name: (identifier) @font-lock-function-name-face) + + (variable_declarator + name: (identifier) @font-lock-variable-name-face) + + (element_value_pair + key: (identifier) @font-lock-property-use-face) + + (formal_parameter + name: (identifier) @font-lock-variable-name-face) + + (catch_formal_parameter + name: (identifier) @font-lock-variable-name-face)) + :language 'java + :override t + :feature 'expression + '((method_invocation + object: (identifier) @font-lock-variable-use-face) + + (method_invocation + name: (identifier) @font-lock-function-call-face) + + (argument_list (identifier) @font-lock-variable-name-face) + + (expression_statement (identifier) @font-lock-variable-use-face)) + ;; Make sure the constant feature is after expression and definition, + ;; because those two applies variable-name-face on some constants. + :language 'java + :override t + :feature 'constant + `((identifier) @java-ts-mode--fontify-constant + [(true) (false)] @font-lock-constant-face) + :language 'java + :feature 'bracket + '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face) + + :language 'java + :feature 'delimiter + '((["," ":" ";"]) @font-lock-delimiter-face))))) (defun java-ts-mode--defun-name (node) "Return the defun name of NODE. @@ -488,7 +496,7 @@ Return nil if there is no name or if NODE is not a defun node." ;; Font-lock. (setq-local treesit-font-lock-settings - java-ts-mode--font-lock-settings) + (java-ts-mode--font-lock-settings)) ;; Inject doxygen parser for comment. (when (and java-ts-mode-enable-doxygen commit 16be837bb773343a5376911722abb539f5729c34 Author: Stefan Monnier Date: Wed Oct 8 19:20:44 2025 -0400 modes.texi: Declare official support for a standard "abuse" * doc/lispref/modes.texi (Search-based Fontification): Explicitly mention the "abuse" of matchers that perform the highlighting instead of matching. diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index 449078197ef..d98d8b21807 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -3402,6 +3402,12 @@ and with point where the previous invocation left it, until @var{function} fails. On failure, @var{function} need not reset point in any particular way. +@var{function} can also take on the responsibility of performing the +highlighting of the region between point and the limit argument it +receives. In that case, it should return @code{nil}, otherwise +font-lock will highlight the match described by the match data +and may call the function again with the same limit. + @item (@var{matcher} . @var{subexp}) In this kind of element, @var{matcher} is either a regular expression or a function, as described above. The @sc{cdr}, commit b469506030a7a0d4027922a72d8ed3355fac5163 Author: LdBeth Date: Wed Oct 8 21:12:47 2025 +0800 newsticker.el: fix escaping of XML strings * lisp/net/newst-backend.el (newsticker--unxml): XML-escape the parsed string in case it contains characters that could confuse the HTML parser. (Bug#79600) diff --git a/lisp/net/newst-backend.el b/lisp/net/newst-backend.el index 4474459fc68..45883fe306f 100644 --- a/lisp/net/newst-backend.el +++ b/lisp/net/newst-backend.el @@ -1110,9 +1110,9 @@ same as in `newsticker--parse-atom-1.0'." (defun newsticker--unxml (node) "Reverse parsing of an xml string. Restore an xml-string from a an xml NODE that was returned by xml-parse..." - (if (or (not node) (stringp node)) - node - (newsticker--unxml-node node))) + (cond ((not node) node) + ((stringp node) (xml-escape-string node)) + (t (newsticker--unxml-node node)))) (defun newsticker--unxml-node (node) "Actually restore xml-string of an xml NODE." commit ebcebaed69cbb2fa0e7206cb3dda910aca452d3b Author: Michael Albinus Date: Wed Oct 8 16:09:15 2025 +0200 Fix another inconsistency in auth-source.el * lisp/auth-source.el (auth-source-backends): Filter out backends with type `ignore'. (auth-source-search-spec): Use `_' in `and-let*' diff --git a/lisp/auth-source.el b/lisp/auth-source.el index 402f2617ca8..a06c9820060 100644 --- a/lisp/auth-source.el +++ b/lisp/auth-source.el @@ -376,8 +376,14 @@ soon as a function returns non-nil.") (defmacro auth-source-backends () "List of usable backends from `auth-sources'. +Filter out backends with type `ignore'. A fallback backend is added to ensure, that at least `read-passwd' is called." - `(or (mapcar #'auth-source-backend-parse auth-sources) + `(or (seq-keep + (lambda (entry) + (and-let* ((backend (auth-source-backend-parse entry)) + ((not (eq (slot-value backend 'type) 'ignore))) + backend))) + auth-sources) ;; Fallback. (list (auth-source-backend :source "" @@ -407,9 +413,9 @@ A fallback backend is added to ensure, that at least `read-passwd' is called." If a search key is nil or t (match anything), skip it." `(apply #'append (mapcar (lambda (k) - (and-let* ((v (plist-get ,spec k)) - ((not (eq t v))) - ((list k (auth-source-ensure-strings v)))))) + (when-let* ((v (plist-get ,spec k)) + (_ (not (eq t v)))) + (list k (auth-source-ensure-strings v)))) (auth-source-search-keys ,spec)))) (defcustom auth-source-ignore-non-existing-file t commit fb58ccfdc4ad7c4f0140b0b1a3cf27bcfe387b69 Author: Sean Whitton Date: Wed Oct 8 12:38:37 2025 +0100 vc-dir-resynch-file: Be more defensive about default-directory * lisp/vc/vc-dir.el (vc-dir-resynch-file): Use both expand-file-name and buffer-local-toplevel-value on default-directory before using the value. diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el index 18848577052..f6f5714519d 100644 --- a/lisp/vc/vc-dir.el +++ b/lisp/vc/vc-dir.el @@ -1240,13 +1240,21 @@ that file." (set-buffer status-buf) (if (not (derived-mode-p 'vc-dir-mode)) (push status-buf drop) - (let ((ddir default-directory)) + (let ((ddir (expand-file-name + ;; The actual contents of this VC-Dir buffer, + ;; which is what we care about here, is always + ;; relative to the toplevel value. + ;; If we invoked the current command from + ;; STATUS-BUF then it might have shadowed + ;; `default-directory' in order to do its work, + ;; but that's irrelevant to us here. + (buffer-local-toplevel-value 'default-directory)))) (when (string-prefix-p ddir file) (if (file-directory-p file) (progn (vc-dir-resync-directory-files file) (ewoc-set-hf vc-ewoc - (vc-dir-headers vc-dir-backend default-directory) "")) + (vc-dir-headers vc-dir-backend ddir) "")) (let* ((complete-state (vc-dir-recompute-file-state file ddir)) (state (cadr complete-state))) (vc-dir-update diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index 6b6d01bc04c..e03313b5730 100644 --- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el @@ -26,6 +26,11 @@ ;; This is the preloaded portion of VC. It takes care of VC-related ;; activities that are done when you visit a file, so that vc.el itself ;; is loaded only when you use a VC command. See commentary of vc.el. +;; +;; The noninteractive hooks into the rest of Emacs are: +;; - `vc-refresh-state' in `find-file-hook' +;; - `vc-kill-buffer-hook' in `kill-buffer-hook' +;; - `vc-after-save' which is called by `basic-save-buffer'. ;;; Code: @@ -916,9 +921,9 @@ In the latter case, VC mode is deactivated for this buffer." (not (equal buffer-file-name truename)) (vc-backend truename)))) (cond ((not link-type) nil) ;Nothing to do. - ((eq vc-follow-symlinks nil) - (message - "Warning: symbolic link to %s-controlled source file" link-type)) + ((not vc-follow-symlinks) + (message "Warning: symbolic link to %s-controlled source file" + link-type)) ((or (not (eq vc-follow-symlinks 'ask)) ;; Assume we cannot ask, default to yes. noninteractive commit 43cbdfb69846e0ab4552f74bab372a38915db66d Author: Sean Whitton Date: Wed Oct 8 11:37:37 2025 +0100 define-globalized-minor-mode: Fix multiple globalized modes * lisp/emacs-lisp/easy-mmode.el (define-globalized-minor-mode): Suppress MODE-set-explicitly function when enabling the mode ourselves. diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index 88fd30c18d6..25e4f882295 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -504,6 +504,8 @@ on if the hook has explicitly disabled it. (intern (concat global-mode-name "-enable-in-buffer"))) (minor-MODE-hook (intern (concat mode-name "-hook"))) (MODE-set-explicitly (intern (concat mode-name "--set-explicitly"))) + (MODE-suppress-set-explicitly (intern (concat mode-name + "--suppress-set-explicitly"))) (MODE-major-mode (intern (concat global-mode-name "--major-mode"))) (MODE-predicate (intern (concat (replace-regexp-in-string "-mode\\'" "" global-mode-name) @@ -611,8 +613,10 @@ list." ;; MODE-set-explicitly is set in MODE-set-explicitly and cleared by ;; kill-all-local-variables. (defvar-local ,MODE-set-explicitly nil) + (defvar ,MODE-suppress-set-explicitly nil) (defun ,MODE-set-explicitly () - (setq ,MODE-set-explicitly t)) + (unless ,MODE-suppress-set-explicitly + (setq ,MODE-set-explicitly t))) (put ',MODE-set-explicitly 'definition-name ',global-mode) ;; A function which checks whether MODE has been disabled in the major @@ -623,11 +627,18 @@ list." (defun ,MODE-enable-in-buffer () (unless (or ,MODE-set-explicitly (eq ,MODE-major-mode major-mode)) - (if ,MODE-variable - (progn - (,mode -1) - (funcall ,turn-on-function)) - (funcall ,turn-on-function))) + (let (;; We are not part of the major mode hook so we don't + ;; want to set MODE-set-explicitly to t. + ;; In particular this is necessary when there are + ;; multiple globalized versions of a single minor mode. + ;; If one of them declines to turn the minor mode on, + ;; that should not mean the others can't. + (,MODE-suppress-set-explicitly t)) + (if ,MODE-variable + (progn + (,mode -1) + (funcall ,turn-on-function)) + (funcall ,turn-on-function)))) (setq ,MODE-major-mode major-mode)) (put ',MODE-enable-in-buffer 'definition-name ',global-mode)))) commit c510608cb2c5bb7da3db23b0a22c23105c563b46 Author: Sean Whitton Date: Tue Oct 7 20:18:43 2025 +0100 ; define-globalized-minor-mode: Slightly simplify control flow. diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index a3ab5f18690..88fd30c18d6 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -621,13 +621,13 @@ list." ;; The function that calls TURN-ON in the current buffer. (defun ,MODE-enable-in-buffer () - (unless ,MODE-set-explicitly - (unless (eq ,MODE-major-mode major-mode) - (if ,MODE-variable - (progn - (,mode -1) - (funcall ,turn-on-function)) - (funcall ,turn-on-function)))) + (unless (or ,MODE-set-explicitly + (eq ,MODE-major-mode major-mode)) + (if ,MODE-variable + (progn + (,mode -1) + (funcall ,turn-on-function)) + (funcall ,turn-on-function))) (setq ,MODE-major-mode major-mode)) (put ',MODE-enable-in-buffer 'definition-name ',global-mode)))) commit 3c20a2bb25a0c4ef6e4cea5c815a46f0d14f37dd Author: Michael Heerdegen Date: Fri Sep 26 14:33:27 2025 +0200 Fix a regression introduced by Bug#79294 fix * lisp/progmodes/hideshow.el (hs-hide-block-at-point): Try to restore old semantics. Do nothing when no block positions are found. Co-authored-by: Elijah Gabe Pérez diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 1d9a6b7988b..b90b85e6bf8 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -883,19 +883,19 @@ The block beginning is adjusted by `hs-adjust-block-beginning' and then further adjusted to be at the end of the line." (if comment-reg (hs-hide-comment-region (car comment-reg) (cadr comment-reg) end) - (let* ((block (hs-block-positions)) - (p (car-safe block)) - (q (cdr-safe block)) - ov) - (if (hs-hideable-region-p p q) - (progn - (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) - (delete-overlay ov)) - ((not hs-allow-nesting) - (hs-discard-overlays p q))) - (goto-char q) - (hs-make-overlay p q 'code (- (match-end 0) p))) - (goto-char (if end q (min p (match-end 0)))))))) + (when-let* ((block (hs-block-positions))) + (let ((p (car-safe block)) + (q (cdr-safe block)) + ov) + (if (hs-hideable-region-p p q) + (progn + (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) + (delete-overlay ov)) + ((not hs-allow-nesting) + (hs-discard-overlays p q))) + (goto-char q) + (hs-make-overlay p q 'code (- (match-end 0) p))) + (goto-char (if end q (min p (match-end 0))))))))) (defun hs-inside-comment-p () "Return non-nil if point is inside a comment, otherwise nil. commit ce863b620977587de72858149d61384de59a5817 Author: Elijah Gabe Pérez Date: Tue Oct 7 14:08:21 2025 -0400 (hs-hideable-region-p): Optimize the optimization * lisp/progmodes/hideshow.el (hs-hideable-region-p): Simplify diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 2d4042be808..1d9a6b7988b 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -623,12 +623,10 @@ Skip \"internal\" overlays if `hs-allow-nesting' is non-nil." (defun hs-hideable-region-p (beg end) "Return t if region in BEG and END can be hidden." ;; Check if BEG and END are not in the same line number, - ;; since using `count-lines' is slow, only check if both - ;; positions do not share the same BOL. + ;; since using `count-lines' is slow. (save-excursion - (let ((pos1 (progn (goto-char beg) (line-beginning-position))) - (pos2 (progn (goto-char end) (line-beginning-position)))) - (and (< pos1 pos2) (not (= pos1 pos2)))))) + (let ((pos2 (progn (goto-char end) (line-beginning-position)))) + (< beg pos2)))) (defun hs-make-overlay (b e kind &optional b-offset e-offset) "Return a new overlay in region defined by B and E with type KIND. commit 438d8f7351bee72e5d8f7016ef0be2ab798c3a83 Merge: 38178570b3b c7ab3700675 Author: Sean Whitton Date: Tue Oct 7 18:04:20 2025 +0100 Merge from origin/emacs-30 c7ab3700675 ; * doc/lispref/control.texi (Conditionals): Grammar fix. f3c29c1415e ; * doc/lispref/control.texi (Conditionals): Document SYM... da47fa2f23b if-let*/when-let*/and-let*: Don't recommend (VALUEFORM) form commit 38178570b3b886740eadc9a9521f999285b4ed07 Merge: c95b33da9c4 db7fd704dee Author: Sean Whitton Date: Tue Oct 7 18:04:20 2025 +0100 ; Merge from origin/emacs-30 The following commit was skipped: db7fd704dee EWW: Don't clobber global value of 'text-property-default... commit c95b33da9c4ae3aaf33db3b3cbb40216ca3649c1 Merge: 0c80d3bee62 cd400326b2f Author: Sean Whitton Date: Tue Oct 7 18:04:20 2025 +0100 Merge from origin/emacs-30 cd400326b2f ; Improve documentation of 'string-glyph-split' commit c7ab37006757d26ccae74cc705e453690a2ba0ec Author: Sean Whitton Date: Tue Oct 7 17:40:39 2025 +0100 ; * doc/lispref/control.texi (Conditionals): Grammar fix. diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index d092b741c7e..722ba95c1c2 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -317,7 +317,7 @@ following way instead: (do-something result2)) @end example -There are a number of variations on this theme, and they're briefly +There's a number of variations on this theme, and they're briefly described below. @defmac if-let* varlist then-form else-forms... commit f3c29c1415edce3f70be9daf90b9aed7ae5a1560 Author: Sean Whitton Date: Tue Oct 7 17:35:32 2025 +0100 ; * doc/lispref/control.texi (Conditionals): Document SYMBOL form. diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index 8f1f144e765..d092b741c7e 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -330,7 +330,10 @@ Each element of @code{varlist} has the form @w{@code{(@var{symbol} locally bound to the result. Bindings are sequential, as in @code{let*} (@pxref{Local Variables}). If only the test result of @var{value-form} is of interest, use @code{_} for @var{symbol}. Then @var{value-form} is -evaluated and checked for @code{nil}, but its value is not bound. +evaluated and checked for @code{nil}, but its value is not bound. As a +special case, an entry of @code{varlist} may be of the form just +@code{@var{symbol}}, which means to check the current binding of symbol +for @code{nil}. @end defmac @defmac when-let* varlist then-forms... @@ -342,9 +345,12 @@ form in @var{then-forms}. @code{varlist} has the form @w{@code{(@var{symbol} @var{value-form})}}, in which @var{value-form} is evaluated and @var{symbol} is locally bound to the result. Bindings are sequential, as in @code{let*} (@pxref{Local -Variables}). If only the test result of @var{value-form} -is of interest, use @code{_} for @var{symbol}. Then @var{value-form} is -evaluated and checked for @code{nil}, but its value is not bound. +Variables}). If only the test result of @var{value-form} is of +interest, use @code{_} for @var{symbol}. Then @var{value-form} is +evaluated and checked for @code{nil}, but its value is not bound. As a +special case, an entry of @code{varlist} may be of the form just +@code{@var{symbol}}, which means to check the current binding of symbol +for @code{nil}. @end defmac @defmac and-let* varlist then-forms... @@ -359,7 +365,10 @@ in which @var{value-form} is evaluated and @var{symbol} is locally bound to the result. Bindings are sequential, as in @code{let*} (@pxref{Local Variables}). If only the test result of @var{value-form} is of interest, use @code{_} for @var{symbol}. Then @var{value-form} is -evaluated and checked for @code{nil}, but its value is not bound. +evaluated and checked for @code{nil}, but its value is not bound. As a +special case, an entry of @code{varlist} may be of the form just +@code{@var{symbol}}, which means to check the current binding of symbol +for @code{nil}. @end defmac Some Lisp programmers follow the convention that @code{and} and @@ -383,7 +392,10 @@ in which @var{value-form} is evaluated and @var{symbol} is locally bound to the result. Bindings are sequential, as in @code{let*} (@pxref{Local Variables}). If only the test result of @var{value-form} is of interest, use @code{_} for @var{symbol}. Then @var{value-form} is -evaluated and checked for @code{nil}, but its value is not bound. +evaluated and checked for @code{nil}, but its value is not bound. As a +special case, an entry of @code{varlist} may be of the form just +@code{@var{symbol}}, which means to check the current binding of symbol +for @code{nil}. The return value of @code{while-let} is always @code{nil}. @end defmac commit da47fa2f23bde6cc96f90f56ccf383128affb748 Author: Sean Whitton Date: Tue Oct 7 17:31:34 2025 +0100 if-let*/when-let*/and-let*: Don't recommend (VALUEFORM) form * doc/lispref/control.texi (Conditionals): * lisp/subr.el (if-let*): Document '(_ VALUEFORM)' instead of '(VALUEFORM)'. diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index 689e7324d43..8f1f144e765 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -317,7 +317,7 @@ following way instead: (do-something result2)) @end example -There's a number of variations on this theme, and they're briefly +There are a number of variations on this theme, and they're briefly described below. @defmac if-let* varlist then-form else-forms... @@ -328,10 +328,9 @@ Evaluate each binding in @var{varlist}, stopping if a binding value is Each element of @code{varlist} has the form @w{@code{(@var{symbol} @var{value-form})}}: @var{value-form} is evaluated and @var{symbol} is locally bound to the result. Bindings are sequential, as in @code{let*} -(@pxref{Local Variables}). As a special case, @var{symbol} can be -omitted if only the test result of @var{value-form} is of interest: -@var{value-form} is evaluated and checked for @code{nil}, but its value -is not bound. +(@pxref{Local Variables}). If only the test result of @var{value-form} +is of interest, use @code{_} for @var{symbol}. Then @var{value-form} is +evaluated and checked for @code{nil}, but its value is not bound. @end defmac @defmac when-let* varlist then-forms... @@ -343,8 +342,8 @@ form in @var{then-forms}. @code{varlist} has the form @w{@code{(@var{symbol} @var{value-form})}}, in which @var{value-form} is evaluated and @var{symbol} is locally bound to the result. Bindings are sequential, as in @code{let*} (@pxref{Local -Variables}). As a special case, @var{symbol} can be omitted if only the -test result of @var{value-form} is of interest: @var{value-form} is +Variables}). If only the test result of @var{value-form} +is of interest, use @code{_} for @var{symbol}. Then @var{value-form} is evaluated and checked for @code{nil}, but its value is not bound. @end defmac @@ -358,8 +357,8 @@ the value of the last binding. @code{varlist} has the form @w{@code{(@var{symbol} @var{value-form})}}, in which @var{value-form} is evaluated and @var{symbol} is locally bound to the result. Bindings are sequential, as in @code{let*} (@pxref{Local -Variables}). As a special case, @var{symbol} can be omitted if only the -test result of @var{value-form} is of interest: @var{value-form} is +Variables}). If only the test result of @var{value-form} is of +interest, use @code{_} for @var{symbol}. Then @var{value-form} is evaluated and checked for @code{nil}, but its value is not bound. @end defmac @@ -382,8 +381,8 @@ established anew. @code{varlist} has the form @w{@code{(@var{symbol} @var{value-form})}}, in which @var{value-form} is evaluated and @var{symbol} is locally bound to the result. Bindings are sequential, as in @code{let*} (@pxref{Local -Variables}). As a special case, @var{symbol} can be omitted if only the -test result of @var{value-form} is of interest: @var{value-form} is +Variables}). If only the test result of @var{value-form} is of +interest, use @code{_} for @var{symbol}. Then @var{value-form} is evaluated and checked for @code{nil}, but its value is not bound. The return value of @code{while-let} is always @code{nil}. diff --git a/lisp/subr.el b/lisp/subr.el index 50ebc598e80..7bd436d898a 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -2632,6 +2632,9 @@ Affects only hooks run in the current buffer." binding)) bindings))) +;; FIXME: Once Emacs 29 is ancient history we can consider a +;; byte-compiler warning. This is because Emacs 29 and older will warn +;; about unused variables with (_ VALUEFORM). (defmacro if-let* (varlist then &rest else) "Bind variables according to VARLIST and evaluate THEN or ELSE. Evaluate each binding in turn, as in `let*', stopping if a @@ -2639,12 +2642,18 @@ binding value is nil. If all are non-nil return the value of THEN, otherwise the value of the last form in ELSE, or nil if there are none. -Each element of VARLIST is a list (SYMBOL VALUEFORM) that binds -SYMBOL to the value of VALUEFORM. An element can additionally be -of the form (VALUEFORM), which is evaluated and checked for nil; -i.e. SYMBOL can be omitted if only the test result is of -interest. It can also be of the form SYMBOL, then the binding of -SYMBOL is checked for nil." +Each element of VARLIST is a list (SYMBOL VALUEFORM) that binds SYMBOL +to the value of VALUEFORM. If only the test result is of interest, use +`_' as SYMBOL, i.e. (_ VALUEFORM), in which case VALUEFORM is evaluated +and checked for nil but the result is not bound. +An element of VARLIST can also be of the form SYMBOL, in which case the +binding of SYMBOL is checked for nil, only. + +An older form for entries of VARLIST is also supported, where SYMBOL is +omitted, i.e. (VALUEFORM). This means the same as (_ VALUEFORM). +This form is not recommended because many programmers find it +significantly less readable. A future release of Emacs may introduce a +byte-compiler warning for uses of (VALUEFORM) in VARLIST." (declare (indent 2) (debug ((&rest [&or symbolp (symbolp form) (form)]) body))) commit 0c80d3bee62df9a4bc8a3eb99a5feebb1296ed31 Author: Ilya Chernyshov Date: Sat Sep 20 17:31:04 2025 +0700 decoded-time-add: Use 'floor' * lisp/calendar/time-date.el (decoded-time-add): Use 'floor' to count new year field value. Copyright-paperwork-exempt: yes diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el index 01f96305edb..9041b04ae62 100644 --- a/lisp/calendar/time-date.el +++ b/lisp/calendar/time-date.el @@ -547,7 +547,7 @@ changes in daylight saving time are not taken into account." (when (decoded-time-month delta) (let ((new (+ (1- (decoded-time-month time)) (decoded-time-month delta)))) (setf (decoded-time-month time) (1+ (mod new 12))) - (incf (decoded-time-year time) (- (/ new 12) (if (< new 0) 1 0))))) + (incf (decoded-time-year time) (floor new 12)))) ;; Adjust for month length (as described in the doc string). (setf (decoded-time-day time) commit db7fd704deee3cbb7d7af72126c992cbc55383f9 Author: Eli Zaretskii Date: Tue Oct 7 09:32:36 2025 +0300 EWW: Don't clobber global value of 'text-property-default-nonsticky' * lisp/net/eww.el (eww-mode): Set 'text-property-default-nonsticky' only in current buffer. Do not merge to master. (Bug#79591) diff --git a/lisp/net/eww.el b/lisp/net/eww.el index 0bf7fe5f307..f56f8b99e6e 100644 --- a/lisp/net/eww.el +++ b/lisp/net/eww.el @@ -1363,8 +1363,8 @@ within text input fields." (setq buffer-read-only t) ;; Insertion at the first character of a field should inherit the ;; field's face, form and field, not the previous character's. - (setq text-property-default-nonsticky '((face . t) (eww-form . t) - (field . t)))) + (setq-local text-property-default-nonsticky '((face . t) (eww-form . t) + (field . t)))) (declare-function imagep "image.c") (defvar text-scale-mode) commit cd400326b2f9991cbca527caaa61649e48cf790b Author: Eli Zaretskii Date: Sun Oct 5 11:04:34 2025 +0300 ; Improve documentation of 'string-glyph-split' * doc/lispref/display.texi (Size of Displayed Text): * lisp/emacs-lisp/subr-x.el (string-glyph-split): Document a caveat (bug#79576). diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index f363b044203..6abbefff7a5 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -2459,6 +2459,10 @@ you can say: @example (apply #'insert (cdr (string-glyph-split string)))) @end example + +Caveat: for this function to recognize and process character +compositions, @code{auto-composition-mode} must be enabled, and the +current buffer must be displayed in some window. @end defun When a buffer is displayed with line numbers (@pxref{Display Custom,,, diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 2fa0652bc5c..aa8ef0d4ecc 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -365,7 +365,10 @@ substring that does not include newlines." This takes into account combining characters and grapheme clusters: if compositions are enabled, each sequence of characters composed on display into a single grapheme cluster is treated as a single -indivisible unit." +indivisible unit. +Caveat: for this function to recognize characters compositions, the +automatic compositions should be enabled (see `auto-composition-mode') +and the current buffer must be displayed in some window." (declare (side-effect-free t)) (let ((result nil) (start 0)