diff --git a/core/siren-core-utils.el b/core/siren-core-utils.el index 3eb6cd9..eda968a 100644 --- a/core/siren-core-utils.el +++ b/core/siren-core-utils.el @@ -7,6 +7,8 @@ ;;; Code: +(require 'cl-lib) + (defmacro siren-prepend (list-var element) "Add ELEMENT to beginning of LIST-VAR, removing duplicates." `(setq ,list-var (cons ,element (remove ,element ,list-var)))) @@ -55,6 +57,18 @@ (cl-subseq cleaned-list (1+ pos))) (append cleaned-list (list ,element)))))) +(defmacro siren-replace-value (list-var old-value new-value) + "Replace all occurrences of OLD-VALUE with NEW-VALUE in LIST-VAR in-place." + `(cl-loop for item on ,list-var + if (equal (car item) ,old-value) + do (setf (car item) ,new-value))) + +(defmacro siren-replace-if (list-var predicate new-value) + "Replace elements in LIST-VAR that match PREDICATE with NEW-VALUE in-place." + `(cl-loop for item on ,list-var + if (funcall ,predicate (car item)) + do (setf (car item) ,new-value))) + (defun siren-recursive-add-to-load-path (dir) "Add DIR and all its sub-directories to `load-path'." (add-to-list 'load-path dir) diff --git a/modules/languages/siren-golang.el b/modules/languages/siren-golang.el index 1dbd6ea..c91501c 100644 --- a/modules/languages/siren-golang.el +++ b/modules/languages/siren-golang.el @@ -12,6 +12,7 @@ (require 'siren-prog-mode) (require 'siren-projectile) (require 'siren-reformatter) +(require 'siren-treesit) (defgroup siren-go nil "Siren: go-mode configuration." @@ -109,36 +110,48 @@ (defun siren-go-ts-mode-setup () (setq-local tab-width siren-go-tab-width company-minimum-prefix-length 1 - ff-other-file-alist 'go-ts-other-file-alist - treesit-font-lock-settings - (append treesit-font-lock-settings - (siren-go-ts-mode-font-lock-overrides))) + ff-other-file-alist 'go-ts-other-file-alist) + + (siren-treesit-append-font-lock-settings + :default-language 'go + + ;; Highlight `true', `false', `nil' and `iota' as built-in constants, + ;; and const declarations as variable names. + :feature 'constant + :override t + `([(false) (nil) (true)] @font-lock-builtin-face + ,@(when (go-ts-mode--iota-query-supported-p) + '((iota) @font-lock-builtin-face)) + (const_declaration + (const_spec name: (identifier) @font-lock-variable-name-face))) + + ;; Customize struct properties to be highlighted as a constant, as + ;; opposed the same as variables. + :feature 'property + :override t + '((selector_expression + field: (field_identifier) @font-lock-constant-face) + (keyed_element (_ (identifier) @font-lock-constant-face)) + (field_declaration + name: (field_identifier) @font-lock-constant-face)) + + ;; Redefine functions calls without changes after the property feature + ;; change, to ensure they are still highlighted correctly. + :feature 'function + :override t + '((call_expression + function: (identifier) @font-lock-function-call-face) + (call_expression + function: (selector_expression + field: (field_identifier) @font-lock-function-call-face)))) (when (fboundp 'highlight-symbol-mode) (highlight-symbol-mode -1)) (when (fboundp 'auto-highlight-symbol-mode) (auto-highlight-symbol-mode -1))) - (defun siren-go-ts-mode-font-lock-overrides () - "Returns a list of overide treesit font-lock rules." - (let ((language 'go)) - (treesit-font-lock-rules - ;; Highlight `true', `false', `nil' and `iota' as built-in constants, - ;; and const declarations as variable names. - :language language - :override t - :feature 'constant - `([(false) (nil) (true)] @font-lock-builtin-face - ,@(when (go-ts-mode--iota-query-supported-p) - '((iota) @font-lock-builtin-face)) - (const_declaration - (const_spec name: (identifier) @font-lock-variable-name-face))) - ))) - :config - (require 'siren-treesit) (siren-treesit-auto-ensure-grammar 'go) - (siren-treesit-auto-ensure-grammar 'gomod) (siren-define-golines-format-mode) @@ -155,31 +168,20 @@ :preface (defun siren-go-mod-ts-mode-setup () - (setq-local tab-width siren-go-tab-width - treesit-font-lock-settings - (append treesit-font-lock-settings - (siren-go-mod-ts-mode-font-lock-overrides))) + (setq-local tab-width siren-go-tab-width) - (add-to-list 'treesit-font-lock-feature-list '((module) (module_path)))) + (siren-treesit-add-features '(module_path module)) + (siren-treesit-append-font-lock-settings + :default-language 'gomod - (defun siren-go-mod-ts-mode-font-lock-overrides () - "Returns a list of overide treesit font-lock rules." - (let ((language 'gomod)) - (treesit-font-lock-rules - :language language - :override t - :feature 'module_path - '(((module_path) @font-lock-constant-face)) + :feature 'module + '((module_directive (module_path) @font-lock-type-face)) - :language language - :override t - :feature 'module - '((module_directive (module_path) @font-lock-type-face)) + :feature 'module_path + '(((module_path) @font-lock-constant-face)))) - :language language - :override t - :feature 'number - '([(go_version) (version)] @font-lock-number-face)))))) + :config + (siren-treesit-auto-ensure-grammar 'gomod))) (use-package lsp-go :straight lsp-mode diff --git a/modules/languages/siren-ruby.el b/modules/languages/siren-ruby.el index 8ea496e..0ef7351 100644 --- a/modules/languages/siren-ruby.el +++ b/modules/languages/siren-ruby.el @@ -13,6 +13,7 @@ (require 'siren-projectile) (require 'siren-reformatter) (require 'siren-string-inflection) +(require 'siren-treesit) (defun siren-define-stree-format-mode () "Setup stree (syntax_tree) formatter." @@ -138,36 +139,29 @@ (setq-local c-tab-always-indent nil tab-width 2) - (setq-local treesit-font-lock-settings - (append treesit-font-lock-settings - (siren-ruby-ts-mode-font-lock-overrides))) + (siren-treesit-add-features '(block-braces)) + (siren-treesit-append-font-lock-settings + :default-language 'ruby - (hs-minor-mode t)) - - (defun siren-ruby-ts-mode-font-lock-overrides () - "Returns a list of overide treesit font-lock rules." - (let ((language 'ruby)) - (treesit-font-lock-rules - ;; Use custom operators list. - :language language - :override t - :feature 'operator - `("!" @font-lock-negation-char-face + ;; Use custom operators list. + :feature 'operator + :override t + `("!" @font-lock-negation-char-face [,@siren-ruby-ts-operators] @font-lock-operator-face) - ;; Braces, when used to denote a block, have the same function as - ;; "do" and "end" and should be highlighted similarly. - :language language - :override t - :feature 'block-braces - '((block ["{" "}"] @font-lock-keyword-face)) + ;; Braces, when used to denote a block, have the same function as + ;; "do" and "end" and should be highlighted similarly. + :feature 'block-braces + :override t + '((block ["{" "}"] @font-lock-keyword-face)) - ;; Highlight string interpolation begin/end markers as keywords. - :language language - :override t - :feature 'interpolation - '((interpolation "#{" @font-lock-keyword-face) - (interpolation "}" @font-lock-keyword-face))))) + ;; Highlight string interpolation begin/end markers as keywords. + :feature 'interpolation + :override t + '((interpolation "#{" @font-lock-keyword-face) + (interpolation "}" @font-lock-keyword-face))) + + (hs-minor-mode t)) :config (siren-treesit-auto-ensure-grammar 'ruby) @@ -181,7 +175,7 @@ ;; Must be defined after ruby-ts-mode is loaded, as we need access to the ;; `ruby-ts--operators' variable. (defcustom siren-ruby-ts-operators - (append ruby-ts--operators '("->")) + (append ruby-ts--operators '("->" "||=")) "Ruby operators for tree-sitter font-locking." :group 'siren-ruby-ts))) diff --git a/modules/text-editing/siren-treesit.el b/modules/text-editing/siren-treesit.el index 8ee5d08..662d3df 100644 --- a/modules/text-editing/siren-treesit.el +++ b/modules/text-editing/siren-treesit.el @@ -22,6 +22,80 @@ (when (not (treesit-ready-p ',lang t)) (treesit-install-language-grammar ',lang)))))) + (defun siren-treesit-add-features (&rest features) + "Add font-lock features to treesit." + (add-to-list 'treesit-font-lock-feature-list features) t) + + (defun siren-treesit-append-font-lock-settings (&rest query-specs) + "Replace existing features in font-lock settings, retaining feature order." + (setq-local + treesit-font-lock-settings + (append treesit-font-lock-settings + (apply 'treesit-font-lock-rules query-specs)))) + + + (defun siren-treesit-add-font-lock-settings (&rest query-specs) + "Add features after existing ones in font-lock settings." + (dolist (item (apply 'treesit-font-lock-rules query-specs)) + (let* ((feature (nth 2 item)) + (existing (cl-find-if (lambda (x) (eq (nth 2 x) feature)) + (reverse treesit-font-lock-settings)))) + (if existing + (siren-add-after treesit-font-lock-settings existing item) + (add-to-list 'treesit-font-lock-settings item t))))) + + (defun siren-treesit-replace-font-lock-settings (&rest query-specs) + "Replace existing features in font-lock settings, retaining feature order." + (setq-local + treesit-font-lock-settings + (siren-treesit--replace-font-lock-settings + treesit-font-lock-settings + (apply 'treesit-font-lock-rules query-specs)))) + + (defun siren-treesit-replace-or-add-font-lock-settings (&rest query-specs) + "Set font-lock settings for treesit, merging with existing settings." + (setq-local treesit-font-lock-settings + (siren-treesit--merge-font-lock-settings + treesit-font-lock-settings + (apply 'treesit-font-lock-rules query-specs)))) + + (defun siren-treesit--feature (compiled-query) + "Get the feature from a compiled treesit query." + (nth 2 compiled-query)) + + (defun siren-treesit--replace-font-lock-settings (a b) + "Replace treesit font-lock settings A with B based on feature." + (let ((merged (copy-tree a))) + (dolist (replacement b) + (let* ((feature (siren-treesit--feature replacement)) + (existing (cl-find-if + (lambda (x) (eq (siren-treesit--feature x) feature)) + merged))) + (if existing + (setf (car (member existing merged)) replacement) + ))) + merged)) + + (defun siren-treesit--merge-font-lock-settings (a b) + "Merge two treesit font-lock settings A and B into a new list. + +This function merges the two treesit font-lock settings lists, returning +a new list. + +Features from B will replace features in A if they are present in both. +Features only present in A will be kept, and features only present in B +will be appended." + (let ((merged (copy-tree a))) + (dolist (item-b b) + (let* ((key (siren-treesit--feature item-b)) + (existing (cl-find-if + (lambda (item) (eq (siren-treesit--feature item) key)) + merged))) + (if existing + (setf (car (member existing merged)) item-b) + (setq merged (append merged (list item-b)))))) + merged)) + (use-package treesit-auto :demand t :if (fboundp 'treesit-ready-p) @@ -30,9 +104,14 @@ :preface (defun siren-treesit-auto-ensure-grammar (grammar) - "Ensure that treesit GRAMMAR is installed and ready for use." + "Ensure treesit GRAMMAR is installed and ready for use." (when (and (fboundp 'treesit-ready-p) (not (treesit-ready-p grammar t))) + (siren-treesit-install-grammar grammar))) + + (defun siren-treesit-install-grammar (grammar) + "Install treesit GRAMMAR." + (when (fboundp 'treesit-ready-p) (let ((treesit-language-source-alist (treesit-auto--build-treesit-source-alist))) (treesit-install-language-grammar grammar)))) diff --git a/themes/siren-doom-themes-overrides-theme.el b/themes/siren-doom-themes-overrides-theme.el index d53ee50..486fff1 100644 --- a/themes/siren-doom-themes-overrides-theme.el +++ b/themes/siren-doom-themes-overrides-theme.el @@ -25,8 +25,8 @@ '(font-lock-comment-face :foreground (doom-lighten comments 0.15)) '(font-lock-operator-face :inherit 'font-lock-keyword-face) - '(font-lock-property-name-face :foreground (doom-lighten constants 0.1)) - '(font-lock-property-use-face :inherit 'font-lock-property-name-face) + '(font-lock-property-name-face :inherit 'font-lock-variable-name-face) + '(font-lock-property-use-face :inherit 'font-lock-variable-name-face) '(font-lock-variable-name-face :foreground (doom-lighten blue 0.25)) ;; built-in completion faces, based on doom-themes' defaults for orderless