Added some more sort features to my "data" commands

Including a nifty ability to sort lines by a user-definable column.

Oh, and the ability to run a `jq` command on a JSON buffer to whittle
it down, which seems to be very data-centric.
This commit is contained in:
Howard Abrams 2023-08-11 16:29:45 -07:00
parent 4a6a3c87b1
commit a00b70c54a
2 changed files with 135 additions and 14 deletions

View file

@ -49,11 +49,14 @@ These functions focus on the data in the buffer as a series of lines:
#+begin_src emacs-lisp
(ha-leader
"d l" '(:ignore t :which-key "on lines")
"d l f" '("flush lines" . flush-lines)
"d l k" '("keep lines" . keep-lines)
"d l s" '("sort lines" . ha-sort-lines)
"d l u" '("unique lines" . delete-duplicate-lines)
"d l b" '("flush blanks" . flush-blank-lines))
"d l d" '("flush lines" . flush-lines)
"d l k" '("keep lines" . keep-lines)
"d l s" '("sort lines" . ha-sort-lines)
"d l f" '("sort fields" . ha-sort-fields)
"d l n" '("sort field num" . ha-sort-fields-numerically)
"d l r" '("reverse lines" . ha-reverse-lines)
"d l u" '("unique lines" . delete-duplicate-lines)
"d l b" '("flush blanks" . flush-blank-lines))
#+end_src
One issue I have is [[help:keep-lines][keep-lines]] operate on the lines /starting with the point/, not on the entire buffer. Lets fix that:
@ -69,16 +72,20 @@ One issue I have is [[help:keep-lines][keep-lines]] operate on the lines /starti
(advice-add 'flush-lines :around #'call-function-at-buffer-beginning)
#+end_src
The [[help:sort-lines][sort-lines]] is useful, but insists on an /active/ region. Lets made a data-focused version:
The [[help:sort-lines][sort-lines]] is useful, but insists on an /active/ region. Lets make a collection of data-focused versions that work on both a region (if it is active) or the entire buffer, regardless of the position of the cursor.
#+begin_src emacs-lisp
(defun ha-sort-lines (prefix)
"Sort the lines in a buffer or region (if active).
If PREFIX given, sort in reverse order."
(interactive "P")
(save-excursion
(if (region-active-p)
(sort-lines prefix (region-beginning) (region-end))
(sort-lines prefix (point-min) (point-max)))))
(dolist (tuple '((ha-sort-lines sort-lines)
(ha-sort-fields sort-fields)
(ha-sort-fields-numerically sort-numeric-fields)))
(cl-destructuring-bind (func orig-func) tuple
(eval `(defun ,func (prefix)
,(format "Call `%s' with all lines in a buffer or region (if active).
Passes PREFIX to the function." orig-func)
(interactive "P")
(save-excursion
(if (region-active-p)
(,orig-func prefix (region-beginning) (region-end))
(,orig-func prefix (point-min) (point-max))))))))
#+end_src
Getting rid of blank lines seems somewhat useful:
@ -95,6 +102,8 @@ These functions focus on the data in the buffer as a table consisting of columns
#+begin_src emacs-lisp
(ha-leader
"d t" '(:ignore t :which-key "on tables")
"d t f" '("sort by columns" . ha-sort-fields)
"d t n" '("sort by columns numerically" . ha-sort-fields-numerically)
"d t k" '("keep columns" . keep-columns)
"d t f" '("flush columns" . flush-columns))
#+end_src
@ -215,6 +224,40 @@ Does this work?
(should (equal (numbers-to-number-list "1, 2 3") '(1 2 3)))
(should (equal (numbers-to-number-list "1, 4-7 9") '(1 4 5 6 7 9))))
#+end_src
The [[help:sort-fields][sort-fields]] function does a good job if the table is space separated, but if we separate by some other character(s), it doesnt work. Can we write a function that does this? Here we make a /helper/ to some regular expression for the [[help:sort-regexp-fields][sort-regexp-fields]] function.
#+begin_src emacs-lisp
(defun ha-sort-table-by-column (separator field)
"Sort the active region or entire buffer by column, FIELD.
Columns are denoted by a regular expression, SEPARATOR, which
could be a single character. For instance, given a buffer:
d, a, c, b
x, y
e, f, g
i, m, a, o
Calling this function with a `,' separator, and `2' for the
column, would result in:
d, a, c, b
e, f, g
i, m, a, o
x, y"
(interactive "sSeparator: \nnSorting field: ")
;; Create a regular expression of grouped fields, separated
;; by the separator sequence, for commas, this would be e.g.
;; \\(.*\\),\\(.*\\),\\(.*\\),\\(.*\\)
(let* ((rx-list (mapconcat (lambda (x) (rx (group (zero-or-more any))))
(number-sequence 1 field)
separator))
;; Prepend the beginning of line to the regular expression:
(regexp (concat (rx bol) rx-list))
(start (if (region-active-p) (region-beginning) (point-min)))
(end (if (region-active-p) (region-end) (point-max))))
(save-excursion
(sort-regexp-fields nil regexp (format "\\%d" field) start end))))
#+end_src
* Buffer-Oriented Functions
If there is no specific function, but you can think of a shell command that will work, then
#+begin_src emacs-lisp

View file

@ -849,6 +849,84 @@ So many configuration files to track:
#+end_src
** JSON
While interested in the [[https://github.com/emacs-tree-sitter/tree-sitter-langs][tree-sitter]] extensions for JSON, e.g. =json-ts-mode=, that comes with Emacs 29, Ill deal with what is bundled now.
However, what about taking a buffer of JSON data, and whittling it down with [[https://jqlang.github.io/jq/][jq]]?
#+begin_src emacs-lisp
(defun ha-json-buffer-to-jq (query)
"Runs JSON buffer with QUERY through an external `jq' program.
Attempts to find the first JSON object in the buffer, and limits
the data to that region. The `jq' program is the first found in
the standard path."
(interactive "sjq Query: ")
(let (s e)
(save-excursion
(if (region-active-p)
(setq s (region-beginning)
e (region-end))
(goto-char (point-min))
(unless (looking-at "{")
(re-search-forward "{")
(goto-char (match-beginning 0)))
(setq s (point))
(evil-jump-item)
(setq e (1+ (point))))
;; (narrow-to-region s e)
(shell-command-on-region s e (concat "jq " query) nil t "*jq errors*"))))
(ha-local-leader :keymaps '(js-json-mode-map json-ts-mode-map)
"j" 'ha-json-buffer-to-jq)
#+end_src
This means, that some data like:
#+begin_src json :tangle no
{
"common_id": "GMC|F2BADC23|64D52BF7|awardlateengine",
"data": {
"name": "Create And Wait for Service Image",
"description": "Creates a new Service Image using IMaaS",
"long_description": "This job creates a new yawxway service image with name yawxway-howard.abrams-test and docker-dev-artifactory.workday.com/dev/yawxway-service:latest docker url in development folder",
"job_id": "5e077245-0f4a-4dc9-b473-ce3ec0b811ba",
"state": "success",
"progress": "100",
"timeout": {
"seconds": 300,
"strategy": "real_time",
"elapsed": 1291.8504
},
"started_at": "2023-08-10T16:20:49Z",
"finished_at": "2023-08-10T16:42:20Z",
"links": [
{
"rel": "child-4aa5978c-4537-4aa9-9568-041ad97c2374",
"href": "https://eng501.garmet.howardism.org/api/jobs/4aa5978c-4537-4aa9-9568-041ad97c2374"
},
{
"rel": "project",
"href": "https://eng501.garmet.howardism.org/api/projects/8abe0f6e-161e-4423-ab27-d4fb0d5cfd0c"
},
{
"rel": "details",
"href": "https://eng501.garmet.howardism.org/api/jobs/5e077245-0f4a-4dc9-b473-ce3ec0b811ba/details"
}
],
"tags": [
"foobar", "birdie"
],
"progress_comment": null,
"children": [
{
"id": "4aa5978c-4537-4aa9-9568-041ad97c2374"
}
]
},
"status": "SUCCESS"
}
#+end_src
I can type, ~, j~ and then type =.data.timeout.seconds= and end up with:
#+begin_src json
300
#+end_src
** Markdown
All the READMEs and other documentation use [[https://jblevins.org/projects/markdown-mode/][markdown-mode]].
#+begin_src emacs-lisp