diff --git a/README.org b/README.org index 037236e1..32072381 100644 --- a/README.org +++ b/README.org @@ -97,11 +97,11 @@ Let's start by making some basic ui changes like turning off the scrollbar, tool #+begin_src emacs-lisp (setq inhibit-startup-message t) -(scroll-bar-mode -1) +(setq default-frame-alist '((undecorated . t))) (tool-bar-mode -1) (tooltip-mode -1) (set-fringe-mode +1) - +(scroll-bar-mode -1) (menu-bar-mode -1) (blink-cursor-mode -1) (column-number-mode +1) @@ -425,6 +425,7 @@ Let's make xref use ripgrep instead of grep for speed. I am currently using NixOS. In order for emacs to have access to certain programs, we need to set some environment variables #+begin_src emacs-lisp (add-to-list 'exec-path "/usr/bin") +(add-to-list 'exec-path "~/.cargo/bin") (setenv "NIX_CONF_DIR" "/etc/nix") (setenv "NIX_REMOTE" "daemon") (setenv "XCURSOR_THEME" "phinger-cursors-light") @@ -581,6 +582,9 @@ Here let's try to add ligatures to our font system since the VictorMono Nerd Fon There are two major packages we need to get the functionality I desire. Evil and general. *** Evil +:PROPERTIES: +:ID: 20250304T164537.064599 +:END: #+begin_src emacs-lisp (use-package evil @@ -614,6 +618,12 @@ This evil-collection package includes a lot of other evil based things. :after evil :init (evil-escape-mode +1) :config + (defun chris/toggle-evil-escape-sequence () + (interactive) + (if (string= evil-escape-key-sequence "ae") + (setq evil-escape-key-sequence "fd") + (setq evil-escape-key-sequence "ae"))) + (setq evil-escape-key-sequence (kbd "ae") evil-escape-delay 0.3)) #+end_src @@ -772,6 +782,9 @@ This evil-collection package includes a lot of other evil based things. "e" '(sly-eval-defun :which-key "evaluate top level")) (general-def 'minibuffer-local-map "C-v" 'evil-paste-after) + (general-def 'visual + "" 'evil-next-visual-line + "" 'evil-previous-visual-line) (general-def 'normal "gcc" 'comment-line "K" 'helpful-at-point @@ -2362,6 +2375,17 @@ Idk, let's try this i guess "lew" 'ellama-improve-wording "ls" 'ellama-provider-select)) #+end_src +*** aider +#+begin_src emacs-lisp +(use-package aidermacs + :config + (setq aidermacs-default-model "ollama_chat/llama3.2:latest" + aidermacs-backend 'comint + aidermacs-watch-files t) + (add-hook 'aidermacs-before-run-backend-hook + ((lambda () + (setenv "OLLAMA_API_BASE" "https://ai.tfcconnection.org"))))) +#+end_src ** Jinx Jinx is an enchanted spell checker for emacs. I think I'll just turn it on globally for a while and see how I feel. @@ -3248,7 +3272,14 @@ In order to view created websites, I'll use =simple-httpd= to get a web server r Avy provides a lot of functions to search through the current buffer. Most of the time I use evil or consult functions to find what I'm looking for, but avy provides a lot of small movements that are more useful for visible movements. #+begin_src emacs-lisp (use-package avy - :after evil) + :after evil + :config + (setq avy-keys '(99 105 101 97 104 116 115 110)) + :general + (general-define-key + :states 'normal + :keymaps '(override rustic-mode) + "gh" 'avy-goto-word-0)) #+end_src These are some evil bindings to avy. @@ -3779,6 +3810,8 @@ I'd like to start learning and using rust if I can. #+begin_src emacs-lisp (use-package ron-mode :mode "\\.ron\\'") + +;; (load-file "./wgsl-ts-mode.el") #+end_src *** Web @@ -3945,13 +3978,13 @@ I'm gonnna dabble in using clojure for the website :ensure t) ;; then install the checker as soon as `clojure-mode' is loaded -(use-package clojure-mode +(use-package clojure-ts-mode :ensure t :config (require 'flycheck-clj-kondo)) (use-package cider - :after clojure-mode) + :after clojure-ts-mode) #+end_src *** Yaml @@ -4027,6 +4060,8 @@ It's probably smart to have markdown. ("\\.rmd\\'". markdown-mode)) :config + (setq markdown-regex-yaml-metadata-border "\\(-\\{3\\}\\|+\\{3\\}\\)$") + (dolist (face '((markdown-header-face-1 1.4 ultra-bold) (markdown-header-face-2 1.2 extra-bold) @@ -4171,6 +4206,13 @@ I'm making a small function in here to open files in the appropriate program usi dired-kill-when-opening-new-dired-buffer t) (add-hook 'dired-mode-hook 'chris/setup-dired) + (defun chris/convert-doc-to-org () + "Convert a word doc to an org file" + (interactive) + (let* ((file (dired-get-filename)) + (out (string-replace "docx" "org" file))) + (async-shell-command (format "pandoc --wrap=preserve -o '%s' '%s'" out file)))) + (custom-set-faces '(dired-directory ((t :foreground "#57c7ff" :inherit dired-header)))) :general @@ -4814,7 +4856,7 @@ Let's add our own eshell prompt. and set the password cache to a significantly h (setq eshell-banner-message "") - (setq eshell-path-env "/usr/local/bin:/usr/bin:/opt/android-sdk/cmdline-tools/latest/bin:/home/chris/.cargo/bin") + (add-to-list 'eshell-path-env-list "/home/chris/.cargo/bin") ;; this makes it so flutter works properly (setenv "ANDROID_SDK_ROOT" "/opt/android-sdk") @@ -5035,23 +5077,50 @@ With empv we can perhaps control mpv much more fine grainly and even search yout (message args) (empv-play-or-enqueue video)) - (defun chris/empv-yt-dlp () + (defun chris/empv-yt-dlp (&optional file) "Download the current video and and play it" (interactive) - (let* ((item (empv-youtube-results--current-item)) - (video-id (alist-get 'videoId item)) - (playlist-id (alist-get 'playlistId item)) - (title (alist-get 'title item)) - (url (format - "https://youtube.com/%s=%s" - (if video-id "watch?v" "playlist?list") - (or video-id playlist-id)))) - (async-shell-command (concat - "yt-dlp" " -o '/home/chris/vids/%(title)s.%(ext)s'" - " \"" url "\"")) - (let ((file (format "/home/chris/vids/%s.%s" title "webm"))) - (empv-play file)) - (message url))) + (setq lexical-binding t) + (let* ((item (when (not file) (empv-youtube-results--current-item))) + (video-id (when (not file) (alist-get 'videoId item))) + (playlist-id (when (not file) (alist-get 'playlistId item))) + (url (if file + (progn + (message file) + (string-replace "inv.cochrun.xyz" "youtube.com" file)) + (format + "https://youtube.com/%s=%s" + (if video-id "watch?v" "playlist?list") + (or video-id playlist-id)))) + (title (if file (shell-command-to-string + (format "yt-dlp --no-warnings --quiet --get-title %s" url)) + (alist-get 'title item))) + (output-buffer (generate-new-buffer "*yt-dlp*")) + (process (progn + (message "Starting to download %s at %s" title url) + (make-process + :name "yt-dlp" + :buffer output-buffer + :command `("yt-dlp" "-o" + "~/vids/%(title)s.%(ext)s" + "-f best[ext=mp4]" + ,(cl-coerce url 'string) + "--embed-thumbnail" + "--sponsorblock-remove=sponsor,intro,outro")) + (get-buffer-process output-buffer))) + (enqueue `(lambda (process event) + (when (eq (process-status process) 'exit) + (progn (message "running %s because %s" process event) + (empv-enqueue (concat "/home/chris/vids/" (string-trim ,title) ".mp4")) + ;; (kill-buffer ,output-buffer) + ))))) + (message url) + (if (process-live-p process) + (set-process-sentinel + process enqueue) + (message "No process running")))) + + ;; (chris/empv-yt-dlp "https://inv.cochrun.xyz/watch?v=mNYcUMZfSP0") (defun chris/empv-yt-dlp-jellyfin () "Grabs the current video at point and downloads it to my jellyfin server" @@ -5258,7 +5327,7 @@ q quit-window (elfeed-search-untag-all-unread) (if (process-live-p proc) (set-process-sentinel - proc `(empv-play-or-enqueue + proc `(empv-enqueue ,(concat "/home/chris/vids/" title ".webm"))) (message "No process running")))) diff --git a/init.el b/init.el index 19f2e5e9..c4ab3c10 100644 --- a/init.el +++ b/init.el @@ -9,11 +9,11 @@ (setq inhibit-startup-message t) -(scroll-bar-mode -1) +(setq default-frame-alist '((undecorated . t))) (tool-bar-mode -1) (tooltip-mode -1) (set-fringe-mode +1) - +(scroll-bar-mode -1) (menu-bar-mode -1) (blink-cursor-mode -1) (column-number-mode +1) @@ -207,6 +207,7 @@ (setq xref-search-program 'ripgrep) (add-to-list 'exec-path "/usr/bin") +(add-to-list 'exec-path "~/.cargo/bin") (setenv "NIX_CONF_DIR" "/etc/nix") (setenv "NIX_REMOTE" "daemon") (setenv "XCURSOR_THEME" "phinger-cursors-light") @@ -350,6 +351,12 @@ :after evil :init (evil-escape-mode +1) :config + (defun chris/toggle-evil-escape-sequence () + (interactive) + (if (string= evil-escape-key-sequence "ae") + (setq evil-escape-key-sequence "fd") + (setq evil-escape-key-sequence "ae"))) + (setq evil-escape-key-sequence (kbd "ae") evil-escape-delay 0.3)) @@ -500,6 +507,9 @@ "e" '(sly-eval-defun :which-key "evaluate top level")) (general-def 'minibuffer-local-map "C-v" 'evil-paste-after) + (general-def 'visual + "" 'evil-next-visual-line + "" 'evil-previous-visual-line) (general-def 'normal "gcc" 'comment-line "K" 'helpful-at-point @@ -1775,6 +1785,15 @@ Describe everything that follows in the present tense, in response to what I typ "la" 'gptel-send "lm" 'gptel-menu)) +(use-package aidermacs + :config + (setq aidermacs-default-model "ollama_chat/llama3.2:latest" + aidermacs-backend 'comint + aidermacs-watch-files t) + (add-hook 'aidermacs-before-run-backend-hook + ((lambda () + (setenv "OLLAMA_API_BASE" "https://ai.tfcconnection.org"))))) + (use-package jinx ;; :hook (emacs-startup . global-jinx-mode) :init (flyspell-mode -1) @@ -2346,7 +2365,14 @@ targets." :ensure t) (use-package avy - :after evil) + :after evil + :config + (setq avy-keys '(99 105 101 97 104 116 115 110)) + :general + (general-define-key + :states 'normal + :keymaps '(override rustic-mode) + "gh" 'avy-goto-word-0)) (use-package evil-avy :after avy @@ -2794,6 +2820,8 @@ targets." (use-package ron-mode :mode "\\.ron\\'") +;; (load-file "./wgsl-ts-mode.el") + (defun chris/web-mode-setup () "some setup for web development" (setq-local completion-at-point-functions @@ -2875,13 +2903,13 @@ targets." :ensure t) ;; then install the checker as soon as `clojure-mode' is loaded -(use-package clojure-mode +(use-package clojure-ts-mode :ensure t :config (require 'flycheck-clj-kondo)) (use-package cider - :after clojure-mode) + :after clojure-ts-mode) (use-package yaml-mode :mode ("\\.yml\\'" . yaml-mode)) @@ -2928,6 +2956,8 @@ targets." ("\\.rmd\\'". markdown-mode)) :config + (setq markdown-regex-yaml-metadata-border "\\(-\\{3\\}\\|+\\{3\\}\\)$") + (dolist (face '((markdown-header-face-1 1.4 ultra-bold) (markdown-header-face-2 1.2 extra-bold) @@ -3042,6 +3072,13 @@ targets." dired-kill-when-opening-new-dired-buffer t) (add-hook 'dired-mode-hook 'chris/setup-dired) + (defun chris/convert-doc-to-org () + "Convert a word doc to an org file" + (interactive) + (let* ((file (dired-get-filename)) + (out (string-replace "docx" "org" file))) + (async-shell-command (format "pandoc --wrap=preserve -o '%s' '%s'" out file)))) + (custom-set-faces '(dired-directory ((t :foreground "#57c7ff" :inherit dired-header)))) :general @@ -3500,7 +3537,7 @@ targets." (setq eshell-banner-message "") - (setq eshell-path-env "/usr/local/bin:/usr/bin:/opt/android-sdk/cmdline-tools/latest/bin:/home/chris/.cargo/bin") + (add-to-list 'eshell-path-env-list "/home/chris/.cargo/bin") ;; this makes it so flutter works properly (setenv "ANDROID_SDK_ROOT" "/opt/android-sdk") @@ -3684,23 +3721,50 @@ targets." (message args) (empv-play-or-enqueue video)) - (defun chris/empv-yt-dlp () + (defun chris/empv-yt-dlp (&optional file) "Download the current video and and play it" (interactive) - (let* ((item (empv-youtube-results--current-item)) - (video-id (alist-get 'videoId item)) - (playlist-id (alist-get 'playlistId item)) - (title (alist-get 'title item)) - (url (format - "https://youtube.com/%s=%s" - (if video-id "watch?v" "playlist?list") - (or video-id playlist-id)))) - (async-shell-command (concat - "yt-dlp" " -o '/home/chris/vids/%(title)s.%(ext)s'" - " \"" url "\"")) - (let ((file (format "/home/chris/vids/%s.%s" title "webm"))) - (empv-play file)) - (message url))) + (setq lexical-binding t) + (let* ((item (when (not file) (empv-youtube-results--current-item))) + (video-id (when (not file) (alist-get 'videoId item))) + (playlist-id (when (not file) (alist-get 'playlistId item))) + (url (if file + (progn + (message file) + (string-replace "inv.cochrun.xyz" "youtube.com" file)) + (format + "https://youtube.com/%s=%s" + (if video-id "watch?v" "playlist?list") + (or video-id playlist-id)))) + (title (if file (shell-command-to-string + (format "yt-dlp --no-warnings --quiet --get-title %s" url)) + (alist-get 'title item))) + (output-buffer (generate-new-buffer "*yt-dlp*")) + (process (progn + (message "Starting to download %s at %s" title url) + (make-process + :name "yt-dlp" + :buffer output-buffer + :command `("yt-dlp" "-o" + "~/vids/%(title)s.%(ext)s" + "-f best[ext=mp4]" + ,(cl-coerce url 'string) + "--embed-thumbnail" + "--sponsorblock-remove=sponsor,intro,outro")) + (get-buffer-process output-buffer))) + (enqueue `(lambda (process event) + (when (eq (process-status process) 'exit) + (progn (message "running %s because %s" process event) + (empv-enqueue (concat "/home/chris/vids/" (string-trim ,title) ".mp4")) + ;; (kill-buffer ,output-buffer) + ))))) + (message url) + (if (process-live-p process) + (set-process-sentinel + process enqueue) + (message "No process running")))) + + ;; (chris/empv-yt-dlp "https://inv.cochrun.xyz/watch?v=mNYcUMZfSP0") (defun chris/empv-yt-dlp-jellyfin () "Grabs the current video at point and downloads it to my jellyfin server" @@ -3825,7 +3889,7 @@ targets." (elfeed-search-untag-all-unread) (if (process-live-p proc) (set-process-sentinel - proc `(empv-play-or-enqueue + proc `(empv-enqueue ,(concat "/home/chris/vids/" title ".webm"))) (message "No process running")))) diff --git a/wgsl-ts-mode.el b/wgsl-ts-mode.el new file mode 100644 index 00000000..eb01d9f1 --- /dev/null +++ b/wgsl-ts-mode.el @@ -0,0 +1,340 @@ +;;; wgsl-ts-mode.el --- Tree-sitter support for the WebGPU Shading Language -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Anthony Cowley +;; Author: Anthony Cowley +;; URL: https://github.com/acowley/wgsl-ts-mode +;; Package-Requires: ((emacs "29.1")) +;; Keywords: wgsl tree-sitter +;; Version: 1.0 + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Syntax highlighting for the WebGPU Shading Language (WGSL) based on a tree-sitter grammar. +;; +;; The approach taken here is based on `rust-ts-mode' by Randy Taylor. + +;;; Code: + +(require 'prog-mode) +(require 'treesit) +(require 'c-ts-common) + +(defvar wgsl-ts-mode--operators + '("!" "!=" "%" "%=" "&" "&=" "&&" "*" "*=" "+" "+=" "," "-" "-=" + "->" "." "/" "/=" ":" ";" "<<" "<" "<=" + "=" "==" ">" ">=" ">>" "@" "^" "^=" "|" "|=" "||") + "WGSL operators for tree-sitter font-locking.") + +(defvar wgsl-ts-mode--keywords + '("if" "else" "fn" "switch" "case" "break" "default" "loop" + "continue" "continuing" "for" "let" "var" "return" "struct" + "type" "while") + "WGSL keywords for tree-sitter font-locking.") + +(defvar wgsl-ts-mode--builtins + '(;; Constructor built-in functions + ;"array" "bool" "f16" "f32" "i32" "mat2x2" "mat2x3" "mat2x4" "mat3x3" + ;"mat3x3" "mat3x4" "mat4x2" "mat4x3" "mat4x4" "u32" "vec2" "vec3" "vec4" + ;; Bit reinterpretation built-in functions + "bitcast" + ;; Logical built-in functions + "all" "any" "select" + ;; Array built-in functions + "arrayLength" + ;; Numeric built-in functions + "abs" "acos" "acosh" "asin" "asinh" "atan" "atanh" "atan2" "ceil" "clamp" + "cos" "cosh" "countLeadingZeros" "countOneBits" "countTrailingZeros" + "cross" "degrees" "determinant" "distance" "dot" "exp" "exp2" + "extractBits" "faceForward" "firstLeadingBit" "firstTrailingBit" + "floor" "fma" "fract" "frexp" "insertBits" "inverseSqrt" "ldexp" + "length" "log" "log2" "max" "min" "mix" "modf" "normalize" "pow" + "quantizeToF16" "radians" "reflect" "refract" "reverseBits" "round" + "saturate" "sign" "sin" "sinh" "smoothstep" "sqrt" "step" "tan" "tanh" + "transpose" "trunc" + ;; Derivative built-in functions + "dpdx" "dpdxCoarse" "dpdxFine" "dpdy" "dpdyCoarse" "dpdyFine" "fwidth" + "fwidthCoarse" "fwidthFine" + ;; Texture built-in functions + "textureDimensions" "textureGather" "textureGatherCompare" "textureLoad" + "textureNumLayers" "textureNumLevels" "textureNumSamples" "textureSample" + "textureSampleBias" "textureSampleCompare" "textureSampleCompareLevel" + "textureSampleGrad" "textureSampleLevel" "textureSampleBaseClampToEdge" + "textureStore" + ;; Data packing built-in functions + "pack4x8snorm" "pack4x8unorm" "pack2x16snorm" "pack2x16unorm" + "pack2x16float" + ;; Data unpacking built-in functions + "unpack4x8snorm" "unpack4x8unorm" "unpack2x16snorm" "unpack2x16unorm" + "unpack2x16float" + ;; Synchronization built-in functions + "storageBarrier" "textureBarrier" "workgroupBarrier" "workgroupUniformLoad" + ;; Built-in inputs and outputs + "frag_depth" "front_facing" "global_invocation_id" "instance_index" + "local_invocation_id" "local_invocation_index" "num_workgroups" + "position" "sample_index" "sample_mask" "vertex_index" "workgroup_id" + ) + "WGSL built-in functions from https://www.w3.org/TR/WGSL/") + +;; Note: The built-in inputs and outputs should perhaps not be lumped +;; in with the other built-in keywords. They are used in attributes, +;; but classifying them as general built-ins means that any use of +;; these identifiers receives the syntax highlighting of a built-in +;; value rather than a regular identifier. + +(setq wgsl-ts-mode--builtins-hash-table + (let ((tbl (make-hash-table :test 'equal))) + (mapc (lambda (x) (puthash x t tbl)) wgsl-ts-mode--builtins) + tbl)) + +(defun wgsl-ts-mode--is-builtin? (x) + (gethash (treesit-node-text x) wgsl-ts-mode--builtins-hash-table)) + +(defvar wgsl-ts-mode--font-lock-rules + `(:language wgsl + :override t + :feature comment + (([(line_comment) (block_comment)]) @font-lock-comment-face) + + :language wgsl + :override t + :feature bitcast + ((bitcast_expression) @font-lock-builtin-face) + + :language wgsl + :override t + :feature operator + (([,@wgsl-ts-mode--operators]) @font-lock-operator-face) + + :language wgsl + :override t + :feature constant + ((identifier) @font-lock-constant-face) + + :language wgsl + :override t + :feature attribute + ((attribute) @font-lock-preprocessor-face) + + :language wgsl + :override t + :feature type + ((type_declaration) @font-lock-type-face) + + :language wgsl + :override t + :feature funcall + ((type_constructor_or_function_call_expression) @font-lock-function-call-face) + + :language wgsl + :override t + :feature definition + ((function_declaration name: (identifier) @font-lock-function-name-face) + (variable_identifier_declaration name: (identifier) @font-lock-property-name-face)) + + :language wgsl + :override t + :feature bracket + ((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face) + + :language wgsl + :override t + :feature texel_format + ((texel_format) @font-lock-builtin-face) + + :language wgsl + :override t + :feature builtin + ;; (([,@wgsl-ts-mode--builtins]) @font-lock-builtin-face) + (((identifier) @font-lock-builtin-face + (:pred wgsl-ts-mode--is-builtin? @font-lock-builtin-face))) + + :language wgsl + :override t + :feature declaration + ((struct_declaration) @font-lock-keyword-face) + + :language wgsl + :override t + :feature keyword + (([,@wgsl-ts-mode--keywords]) @font-lock-keyword-face) + + :language wgsl + :override t + :feature address_space + (([(address_space) (access_mode)]) @font-lock-builtin-face) + + :language wgsl + :override t + :feature number + (([(float_literal) (int_literal)]) @font-lock-number-face) + + :language wgsl + :override t + :feature constant + ((bool_literal) @font-lock-constant-face) + + :language wgsl + :override t + :feature delimiter + ((["," "." ";" ":"]) @font-lock-delimiter-face) +)) + +(defcustom wgsl-ts-mode-indent-offset 2 + "Number of spaces for each indentation step in `wgsl-ts-mode'." + :version "29.1" + :type 'integer + :safe 'integerp + :group 'wgsl) + +(defvar wgsl-ts-mode--indent-rules + `((wgsl + ((parent-is "source_file") column-0 0) + ((node-is ")") parent-bol 0) + ((node-is "]") parent-bol 0) + ((node-is "}") (and parent parent-bol) 0) + ((and (parent-is "comment") c-ts-common-looking-at-star) + c-ts-common-comment-start-after-first-star -1) + ((parent-is "comment") prev-adaptive-prefix 0) + ((parent-is "arguments") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "assignment_statement") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "array_expression") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "binary_expression") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "compound_statement") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "declaration_list") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "enum_variant_list") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "field_declaration_list") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "field_expression") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "field_initializer_list") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "let_declaration") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "var_declaration") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "parameters") parent-bol wgsl-ts-mode-indent-offset) + ((parent-is "struct_pattern") parent-bol wgsl-ts-mode-indent-offset))) + "Tree-sitter indent rules for `wgsl-ts-mode'.") + +(defun wgsl-ts-mode--syntax-propertize (beg end) + "Apply syntax properties to special characters between BEG and END. + +Apply syntax properties to various special characters with +contextual meaning between BEG and END. + +The apostrophe \\=' should be treated as string when used for char literals. + +< and > are usually punctuation, e.g., as greater/less-than. But +when used for types, they should be considered pairs. + +This function checks for < and > in the changed RANGES and apply +appropriate text property to alter the syntax of template +delimiters < and >'s." + (goto-char beg) + (while (search-forward "'" end t) + (when (string-equal "char_literal" + (treesit-node-type + (treesit-node-at (match-beginning 0)))) + (put-text-property (match-beginning 0) (match-end 0) + 'syntax-table (string-to-syntax "\"")))) + (goto-char beg) + (while (re-search-forward (rx (or "<" ">")) end t) + (pcase (treesit-node-type + (treesit-node-parent + (treesit-node-at (match-beginning 0)))) + (;(or "type_declaration" "type_parameters") + "type_declaration" + (put-text-property (match-beginning 0) + (match-end 0) + 'syntax-table + (pcase (char-before) + (?< '(4 . ?>)) + (?> '(5 . ?<)))))))) + +(defun wgsl-ts-mode--defun-name (node) + "Return the defun name of NODE. +Return nil if there is no name or if NODE is not a defun node." + (pcase (treesit-node-type node) + ("function_declaration" + (treesit-node-text + (treesit-node-child-by-field-name node "name") t)) + ("struct_declaration" + (treesit-node-text + (treesit-node-child-by-field-name node "name") t)) + ("type_declaration" + (treesit-node-text + (treesit-node-child-by-field-name node "name") t)))) + +(defvar wgsl-ts-mode--syntax-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?+ "." table) + (modify-syntax-entry ?- "." table) + (modify-syntax-entry ?= "." table) + (modify-syntax-entry ?% "." table) + (modify-syntax-entry ?& "." table) + (modify-syntax-entry ?| "." table) + (modify-syntax-entry ?^ "." table) + (modify-syntax-entry ?! "." table) + (modify-syntax-entry ?@ "." table) + (modify-syntax-entry ?~ "." table) + (modify-syntax-entry ?< "." table) + (modify-syntax-entry ?> "." table) + (modify-syntax-entry ?/ ". 124b" table) + (modify-syntax-entry ?* ". 23" table) + (modify-syntax-entry ?\n "> b" table) + (modify-syntax-entry ?\^m "> b" table) + table) + "Syntax table for `wgsl-ts-mode'.") + +(defun wgsl-ts-setup () + "Setup tree-sitter for wgsl-ts-mode." + (setq-local syntax-propertize-function + #'wgsl-ts-mode--syntax-propertize) + (c-ts-common-comment-setup) + + (setq-local treesit-font-lock-settings + (apply #'treesit-font-lock-rules + wgsl-ts-mode--font-lock-rules)) + + (setq-local treesit-font-lock-feature-list + '((comment definition) + (keyword string) + (assignment attribute builtin constant escape-sequence number + type address_space texel_format bitcast funcall) + (bracket delimiter error function operator property variable))) + + (setq-local treesit-simple-imenu-settings + `(("Struct" "\\`struct_declaration\\'" nil nil) + ("Fn" "\\`function_declaration\\'" nil nil))) + + (setq-local treesit-font-lock-level 4) + (setq-local indent-tabs-mode nil + treesit-simple-indent-rules wgsl-ts-mode--indent-rules) + (setq-local treesit-defun-type-regexp + (regexp-opt '("function_declaration" + "struct_declaration"))) + (setq-local treesit-defun-name-function #'wgsl-ts-mode--defun-name) + (treesit-major-mode-setup)) + +;;;###autoload +(define-derived-mode wgsl-ts-mode prog-mode "WGSL[ts]" + "Major mode for editing WGSL with tree-sitter." + :syntax-table wgsl-ts-mode--syntax-table + (when (treesit-ready-p 'wgsl) + (treesit-parser-create 'wgsl) + (wgsl-ts-setup))) + +(if (treesit-ready-p 'wgsl) + (add-to-list 'auto-mode-alist '("\\.wgsl\\'" . wgsl-ts-mode))) + +(provide 'wgsl-ts-mode) + +;;; wgsl-ts-mode.el ends here