diff options
| -rw-r--r-- | assets/abbrev_defs | 13 | ||||
| -rw-r--r-- | init.el | 1 | ||||
| -rw-r--r-- | modules/org-export-config.el | 2 | ||||
| -rw-r--r-- | modules/prog-json.el | 60 | ||||
| -rw-r--r-- | modules/prog-webdev.el | 8 | ||||
| -rw-r--r-- | modules/prog-yaml.el | 44 | ||||
| -rw-r--r-- | tests/test-prog-json--json-format-buffer.el | 109 | ||||
| -rw-r--r-- | tests/test-prog-yaml--yaml-format-buffer.el | 76 |
8 files changed, 292 insertions, 21 deletions
diff --git a/assets/abbrev_defs b/assets/abbrev_defs index c3af4548..559a4df2 100644 --- a/assets/abbrev_defs +++ b/assets/abbrev_defs @@ -34,7 +34,7 @@ ("aethetically" "aesthetically" nil :count 0) ("agression" "aggression" nil :count 0) ("agressive" "aggressive" nil :count 0) - ("ahve" "have" nil :count 15) + ("ahve" "have" nil :count 16) ("aknowledge" "acknowledge" nil :count 0) ("alegiance" "allegiance" nil :count 0) ("allegaince" "allegiance" nil :count 0) @@ -64,6 +64,7 @@ ("autopayment" "auto-payment" nil :count 0) ("autopayments" "auto-payments" nil :count 0) ("availabilty" "availability" nil :count 0) + ("avialable" "available" nil :count 0) ("balconly" "balcony" nil :count 0) ("bandanas" "bandannas" nil :count 3) ("beatiful" "beautiful" nil :count 0) @@ -183,7 +184,7 @@ ("finidng" "finding" nil :count 0) ("finshed" "finished" nil :count 0) ("firey" "fiery" nil :count 0) - ("firsst" "first" nil :count 3) + ("firsst" "first" nil :count 4) ("flekey" "flakey" nil :count 0) ("forevr" "forever" nil :count 0) ("foriegn" "foreign" nil :count 0) @@ -197,6 +198,7 @@ ("garanty" "guarantee" nil :count 0) ("garentee" "guarantee" nil :count 0) ("generousity" "generosity" nil :count 0) + ("geolocation" "geo" nil :count 0) ("goinig" "going" nil :count 0) ("grat" "great" nil :count 0) ("greatful" "grateful" nil :count 0) @@ -298,7 +300,7 @@ ("oppositiion" "opposition" nil :count 0) ("opppsite" "opposite" nil :count 0) ("orignal" "original" nil :count 0) - ("ot" "to" nil :count 43) + ("ot" "to" nil :count 44) ("otehr" "other" nil :count 3) ("otes" "notes" nil :count 0) ("outgoign" "outgoing" nil :count 0) @@ -366,6 +368,7 @@ ("rythem" "rhythm" nil :count 0) ("rythm" "rhythm" nil :count 0) ("sargent" "sergeant" nil :count 0) + ("sattelite" "satellite" nil :count 0) ("sattelites" "satellites" nil :count 0) ("scheudle" "schedule" nil :count 4) ("secratary" "secretary" nil :count 0) @@ -398,8 +401,8 @@ ("takss" "tasks" nil :count 3) ("talekd" "talked" nil :count 0) ("talkign" "talking" nil :count 6) - ("teh" "the" nil :count 163) - ("tehir" "their" nil :count 6) + ("teh" "the" nil :count 170) + ("tehir" "their" nil :count 7) ("tehre" "there" nil :count 3) ("testimentary" "testamentary" nil :count 1) ("thansk" "thanks" nil :count 3) @@ -103,6 +103,7 @@ (require 'prog-shell) ;; combine elsewhere (require 'prog-python) (require 'prog-webdev) +(require 'prog-json) (require 'prog-yaml) ;; ---------------------------------- Org Mode --------------------------------- diff --git a/modules/org-export-config.el b/modules/org-export-config.el index 4451eddd..688d8f99 100644 --- a/modules/org-export-config.el +++ b/modules/org-export-config.el @@ -49,7 +49,7 @@ (setq org-export-with-tasks '("TODO")) ;; export with tasks by default (setq org-export-with-tasks nil) ;; export WITHOUT tasks by default (setq org-export-with-toc t) ;; export WITH table of contents by default - (setq org-export-initial-scope 'subtree) ;; 'buffer is your other choice + (setq org-export-initial-scope 'buffer) ;; 'subtree is your other choice (setq org-export-with-author nil)) ;; export without author by default (use-package ox-html diff --git a/modules/prog-json.el b/modules/prog-json.el new file mode 100644 index 00000000..6dba6dee --- /dev/null +++ b/modules/prog-json.el @@ -0,0 +1,60 @@ +;;; prog-json.el --- JSON Editing, Formatting, and jq Integration -*- lexical-binding: t; coding: utf-8; -*- +;; Author: Craig Jennings <c@cjennings.net> + +;;; Commentary: +;; JSON editing with tree-sitter highlighting, one-key formatting, and +;; interactive jq queries against the current buffer. +;; +;; Features: +;; - Tree-sitter: Better syntax highlighting and structural navigation +;; - Formatting: Pretty-print with sorted keys via C-; f +;; - jq: Interactive jq REPL against current JSON buffer +;; +;; Workflow: +;; 1. Open .json file → json-ts-mode with tree-sitter highlighting +;; 2. C-; f → Format/pretty-print the buffer +;; 3. C-c C-q → Open jq interactive buffer to query/transform JSON + +;;; Code: + +(defvar json-ts-mode-map) + +;; -------------------------------- JSON Mode ---------------------------------- +;; tree-sitter mode for JSON files (built-in, Emacs 29+) +;; NOTE: No :mode directive here — treesit-auto (in prog-general.el) handles +;; the auto-mode-alist mapping and auto-installs the grammar on first use. + +(use-package json-ts-mode + :ensure nil + :defer t) + +;; -------------------------------- Formatting --------------------------------- +;; pretty-print with sorted keys, bound to standard format key + +(defun cj/json-format-buffer () + "Format the current JSON buffer with sorted keys. +Uses jq if available for reliable formatting, otherwise falls +back to the built-in `json-pretty-print-buffer-ordered'." + (interactive) + (if (executable-find "jq") + (let ((point (point))) + (shell-command-on-region (point-min) (point-max) "jq --sort-keys ." nil t) + (goto-char (min point (point-max)))) + (json-pretty-print-buffer-ordered))) + +(defun cj/json-setup () + "Set up JSON buffer keybindings." + (local-set-key (kbd "C-; f") #'cj/json-format-buffer)) + +(add-hook 'json-ts-mode-hook #'cj/json-setup) + +;; --------------------------------- jq Mode ----------------------------------- +;; interactive jq queries against JSON buffers + +(use-package jq-mode + :defer t + :bind (:map json-ts-mode-map + ("C-c C-q" . jq-interactively))) + +(provide 'prog-json) +;;; prog-json.el ends here. diff --git a/modules/prog-webdev.el b/modules/prog-webdev.el index a45bd376..c0a5980b 100644 --- a/modules/prog-webdev.el +++ b/modules/prog-webdev.el @@ -3,7 +3,6 @@ ;; ;;; Commentary: ;; Open a project file and Emacs selects the right helper: -;; - *.json buffers drop into json-mode for quick structural edits. ;; - *.js buffers jump into js2-mode for linty feedback. ;; - Mixed HTML templates land in web-mode which chains Tide and CSS Eldoc. ;; @@ -17,13 +16,6 @@ ;;; Code: -;; --------------------------------- JSON Mode --------------------------------- -;; mode for editing JavaScript Object Notation (JSON) data files - -(use-package json-mode - :mode ("\\.json\\'" . json-mode) - :defer .5) - ;; ---------------------------------- JS2 Mode --------------------------------- ;; javascript editing mode diff --git a/modules/prog-yaml.el b/modules/prog-yaml.el index 1a970313..8411f04c 100644 --- a/modules/prog-yaml.el +++ b/modules/prog-yaml.el @@ -2,17 +2,47 @@ ;; author: Craig Jennings <c@cjennings.net> ;;; Commentary: +;; YAML editing with tree-sitter highlighting and one-key formatting. +;; +;; Features: +;; - Tree-sitter: Syntax highlighting and structural navigation +;; - Formatting: Normalize indentation and style via C-; f +;; +;; Workflow: +;; 1. Open .yml/.yaml file → yaml-ts-mode with tree-sitter highlighting +;; 2. C-; f → Format buffer with prettier ;;; Code: -(use-package yaml-mode - :defer .5 - :commands (yaml-mode) - :config - (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)) - (add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-mode))) +;; -------------------------------- YAML Mode ---------------------------------- +;; tree-sitter mode for YAML files (built-in, Emacs 29+) +;; NOTE: No :mode directive — treesit-auto (in prog-general.el) handles +;; the auto-mode-alist mapping and auto-installs the grammar on first use. -(add-hook 'yaml-mode-hook ' flycheck-mode-hook) +(use-package yaml-ts-mode + :ensure nil + :defer t) + +;; -------------------------------- Formatting --------------------------------- +;; normalize indentation and style, bound to standard format key + +(defun cj/yaml-format-buffer () + "Format the current YAML buffer with prettier. +Preserves point position as closely as possible." + (interactive) + (if (executable-find "prettier") + (let ((point (point))) + (shell-command-on-region (point-min) (point-max) + "prettier --parser yaml" nil t) + (goto-char (min point (point-max)))) + (user-error "prettier not found; install with: npm install -g prettier"))) + +(defun cj/yaml-setup () + "Set up YAML buffer keybindings and linting." + (local-set-key (kbd "C-; f") #'cj/yaml-format-buffer) + (flycheck-mode 1)) + +(add-hook 'yaml-ts-mode-hook #'cj/yaml-setup) (provide 'prog-yaml) ;;; prog-yaml.el ends here diff --git a/tests/test-prog-json--json-format-buffer.el b/tests/test-prog-json--json-format-buffer.el new file mode 100644 index 00000000..227eafb9 --- /dev/null +++ b/tests/test-prog-json--json-format-buffer.el @@ -0,0 +1,109 @@ +;;; test-prog-json--json-format-buffer.el --- Tests for cj/json-format-buffer -*- lexical-binding: t -*- + +;;; Commentary: +;; Tests for the cj/json-format-buffer function in prog-json.el. +;; Tests both the jq path and the built-in fallback path. + +;;; Code: + +(require 'ert) +(require 'json) +(require 'prog-json) + +;;; Normal Cases — jq path + +(ert-deftest test-prog-json--json-format-buffer-normal-formats-object () + "Compact JSON object is pretty-printed with sorted keys." + (with-temp-buffer + (insert "{\"zebra\":1,\"alpha\":2}") + (cj/json-format-buffer) + (should (string= (string-trim (buffer-string)) + "{\n \"alpha\": 2,\n \"zebra\": 1\n}")))) + +(ert-deftest test-prog-json--json-format-buffer-normal-formats-array () + "Compact JSON array is pretty-printed." + (with-temp-buffer + (insert "[1,2,3]") + (cj/json-format-buffer) + (should (string= (string-trim (buffer-string)) + "[\n 1,\n 2,\n 3\n]")))) + +(ert-deftest test-prog-json--json-format-buffer-normal-nested () + "Nested JSON is pretty-printed with sorted keys at all levels." + (with-temp-buffer + (insert "{\"b\":{\"d\":1,\"c\":2},\"a\":3}") + (cj/json-format-buffer) + (should (string-match-p "\"a\": 3" (buffer-string))) + (should (string-match-p "\"c\": 2" (buffer-string))) + ;; "a" should appear before "b" (sorted) + (should (< (string-match "\"a\"" (buffer-string)) + (string-match "\"b\"" (buffer-string)))))) + +(ert-deftest test-prog-json--json-format-buffer-normal-already-formatted () + "Already-formatted JSON is unchanged." + (let ((formatted "{\n \"alpha\": 1,\n \"beta\": 2\n}\n")) + (with-temp-buffer + (insert formatted) + (cj/json-format-buffer) + (should (string= (buffer-string) formatted))))) + +;;; Boundary Cases + +(ert-deftest test-prog-json--json-format-buffer-boundary-empty-object () + "Empty JSON object formats cleanly." + (with-temp-buffer + (insert "{}") + (cj/json-format-buffer) + (should (string= (string-trim (buffer-string)) "{}")))) + +(ert-deftest test-prog-json--json-format-buffer-boundary-empty-array () + "Empty JSON array formats cleanly." + (with-temp-buffer + (insert "[]") + (cj/json-format-buffer) + (should (string= (string-trim (buffer-string)) "[]")))) + +(ert-deftest test-prog-json--json-format-buffer-boundary-scalar-string () + "Bare JSON string scalar formats without error." + (with-temp-buffer + (insert "\"hello\"") + (cj/json-format-buffer) + (should (string= (string-trim (buffer-string)) "\"hello\"")))) + +(ert-deftest test-prog-json--json-format-buffer-boundary-unicode () + "JSON with unicode characters is preserved." + (with-temp-buffer + (insert "{\"emoji\":\"\\u2764\",\"name\":\"café\"}") + (cj/json-format-buffer) + (should (string-match-p "café" (buffer-string))))) + +;;; Fallback path — built-in formatter + +(ert-deftest test-prog-json--json-format-buffer-fallback-formats-without-jq () + "Falls back to built-in formatter when jq is not found." + (cl-letf (((symbol-function 'executable-find) (lambda (_) nil))) + (with-temp-buffer + (insert "{\"b\":1,\"a\":2}") + (cj/json-format-buffer) + ;; Built-in formatter should pretty-print (key order may vary) + (should (string-match-p "\"a\"" (buffer-string))) + (should (string-match-p "\"b\"" (buffer-string))) + ;; Should be multi-line (formatted, not compact) + (should (> (count-lines (point-min) (point-max)) 1))))) + +;;; Error Cases + +(ert-deftest test-prog-json--json-format-buffer-error-invalid-json () + "Invalid JSON produces an error, does not silently corrupt buffer." + (with-temp-buffer + (insert "{not valid json}") + (let ((original (buffer-string))) + ;; jq will fail on invalid JSON — buffer should not be emptied + (condition-case _err + (cj/json-format-buffer) + (error nil)) + ;; Buffer should still have content (not wiped) + (should (> (length (buffer-string)) 0))))) + +(provide 'test-prog-json--json-format-buffer) +;;; test-prog-json--json-format-buffer.el ends here diff --git a/tests/test-prog-yaml--yaml-format-buffer.el b/tests/test-prog-yaml--yaml-format-buffer.el new file mode 100644 index 00000000..4e928a2c --- /dev/null +++ b/tests/test-prog-yaml--yaml-format-buffer.el @@ -0,0 +1,76 @@ +;;; test-prog-yaml--yaml-format-buffer.el --- Tests for cj/yaml-format-buffer -*- lexical-binding: t -*- + +;;; Commentary: +;; Tests for the cj/yaml-format-buffer function in prog-yaml.el. + +;;; Code: + +(require 'ert) +(require 'prog-yaml) + +;;; Normal Cases + +(ert-deftest test-prog-yaml--yaml-format-buffer-normal-fixes-indentation () + "Badly indented YAML is normalized to 2-space indent." + (with-temp-buffer + (insert "items:\n - one\n - two\n") + (cj/yaml-format-buffer) + (should (string= (buffer-string) "items:\n - one\n - two\n")))) + +(ert-deftest test-prog-yaml--yaml-format-buffer-normal-nested-map () + "Nested map with inconsistent indentation is normalized." + (with-temp-buffer + (insert "server:\n host: localhost\n port: 8080\n") + (cj/yaml-format-buffer) + (should (string= (buffer-string) "server:\n host: localhost\n port: 8080\n")))) + +(ert-deftest test-prog-yaml--yaml-format-buffer-normal-already-formatted () + "Already well-formatted YAML is unchanged." + (let ((formatted "name: test\nversion: 1.0\n")) + (with-temp-buffer + (insert formatted) + (cj/yaml-format-buffer) + (should (string= (buffer-string) formatted))))) + +(ert-deftest test-prog-yaml--yaml-format-buffer-normal-preserves-key-order () + "Key order is preserved (not sorted)." + (with-temp-buffer + (insert "zebra: 1\nalpha: 2\n") + (cj/yaml-format-buffer) + (should (string-match-p "zebra.*\nalpha" (buffer-string))))) + +;;; Boundary Cases + +(ert-deftest test-prog-yaml--yaml-format-buffer-boundary-empty-document () + "Empty YAML document formats without error." + (with-temp-buffer + (insert "") + (cj/yaml-format-buffer) + (should (string= (string-trim (buffer-string)) "")))) + +(ert-deftest test-prog-yaml--yaml-format-buffer-boundary-single-key () + "Single key-value pair formats cleanly." + (with-temp-buffer + (insert "key: value\n") + (cj/yaml-format-buffer) + (should (string= (buffer-string) "key: value\n")))) + +(ert-deftest test-prog-yaml--yaml-format-buffer-boundary-unicode () + "YAML with unicode values is preserved." + (with-temp-buffer + (insert "name: café\ncity: Zürich\n") + (cj/yaml-format-buffer) + (should (string-match-p "café" (buffer-string))) + (should (string-match-p "Zürich" (buffer-string))))) + +;;; Error Cases + +(ert-deftest test-prog-yaml--yaml-format-buffer-error-no-prettier () + "Signals user-error when prettier is not found." + (cl-letf (((symbol-function 'executable-find) (lambda (_) nil))) + (with-temp-buffer + (insert "key: value\n") + (should-error (cj/yaml-format-buffer) :type 'user-error)))) + +(provide 'test-prog-yaml--yaml-format-buffer) +;;; test-prog-yaml--yaml-format-buffer.el ends here |
