From f299a7af064e4e70278c010abc508de6c015e755 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Sun, 11 Jun 2023 22:09:58 +0300 Subject: [PATCH] Recover lost commits --- .gitmodules | 2 +- dict | 2 +- dotfiles | 2 +- source/notes/bozhidar-batsov.org | 13 ++ source/notes/eli-zaretskii.org | 13 ++ source/notes/emacs.org | 1 - source/notes/gnu.org | 1 - source/notes/isearch.org | 13 ++ source/notes/oleh-krehel.org | 13 ++ source/notes/torsten-hilbrich.org | 13 ++ ...n-documents-with-emacs-and-tree-sitter.org | 84 ++++++++++ ...6-extending-emacs-s-dictionary-library.org | 147 ++++++++++++++++++ sweep | 2 +- 13 files changed, 300 insertions(+), 6 deletions(-) create mode 100644 source/notes/bozhidar-batsov.org create mode 100644 source/notes/eli-zaretskii.org create mode 100644 source/notes/isearch.org create mode 100644 source/notes/oleh-krehel.org create mode 100644 source/notes/torsten-hilbrich.org create mode 100644 source/posts/2023-05-17-orientation-in-json-documents-with-emacs-and-tree-sitter.org create mode 100644 source/posts/2023-05-26-extending-emacs-s-dictionary-library.org diff --git a/.gitmodules b/.gitmodules index 065acc7..20eb15c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "dotfiles"] path = dotfiles - url = https://git.sr.ht/~eshel/dotfiles + url = git://git.eshelyaron.com/dotfiles.git [submodule "sweep"] path = sweep url = git@git.sr.ht:~eshel/sweep diff --git a/dict b/dict index eebbd4f..bbeaef4 160000 --- a/dict +++ b/dict @@ -1 +1 @@ -Subproject commit eebbd4fdb1ea67081db95c84d5504bc8c1ad74a2 +Subproject commit bbeaef43fb199a2f2a49b6d05b0553a67ecd3402 diff --git a/dotfiles b/dotfiles index 42d80e8..3306e2c 160000 --- a/dotfiles +++ b/dotfiles @@ -1 +1 @@ -Subproject commit 42d80e822cd6d07d88484d2e30eaca6b130dc052 +Subproject commit 3306e2caceac642a7099bdc899736ad9fd54e89a diff --git a/source/notes/bozhidar-batsov.org b/source/notes/bozhidar-batsov.org new file mode 100644 index 0000000..9e639af --- /dev/null +++ b/source/notes/bozhidar-batsov.org @@ -0,0 +1,13 @@ +#+TITLE: Bozhidar Batsov +#+SUBTITLE: Emacs hacker +#+DESCRIPTION: Eshel Yaron's notes about Bozhidar Batsov +#+KEYWORDS: emacs +#+DATE: 2023-06-11 + + +* References in published posts +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/posts/" +#+END: +* References in other notes +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/notes/" +#+END: diff --git a/source/notes/eli-zaretskii.org b/source/notes/eli-zaretskii.org new file mode 100644 index 0000000..f04ab9a --- /dev/null +++ b/source/notes/eli-zaretskii.org @@ -0,0 +1,13 @@ +#+TITLE: Eli Zaretskii +#+SUBTITLE: Emacs maintainer +#+DESCRIPTION: Eshel Yaron's notes about Eli Zaretskii +#+KEYWORDS: emacs +#+DATE: 2023-06-11 + + +* References in published posts +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/posts/" +#+END: +* References in other notes +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/notes/" +#+END: diff --git a/source/notes/emacs.org b/source/notes/emacs.org index 0f241f3..8c0ce6b 100644 --- a/source/notes/emacs.org +++ b/source/notes/emacs.org @@ -14,7 +14,6 @@ free/libre text editor". - [[file:../posts/2023-04-08-making-shell-scripts-executable-just-in-time.org][Making Shell Scripts Executable Just-in-Time]] :: A different take on adding exec permissions to shell script in Emacs - [[file:../posts/2023-04-11-optimizing-project-selection-in-emacs.org][Optimizing Project Selection in Emacs]] :: Leveraging a new Emacs customization option to streamline project selection #+END: - * References in other notes #+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/notes" #+END: diff --git a/source/notes/gnu.org b/source/notes/gnu.org index e1b221a..d55d96f 100644 --- a/source/notes/gnu.org +++ b/source/notes/gnu.org @@ -8,7 +8,6 @@ * References in published posts #+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/posts" #+END: - * References in other notes #+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/notes" #+END: diff --git a/source/notes/isearch.org b/source/notes/isearch.org new file mode 100644 index 0000000..7ff46d6 --- /dev/null +++ b/source/notes/isearch.org @@ -0,0 +1,13 @@ +#+TITLE: Isearch +#+SUBTITLE: Emacs's incremental search system +#+DESCRIPTION: Eshel Yaron's notes about Isearch +#+KEYWORDS: emacs +#+DATE: 2023-06-11 + + +* References in published posts +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/posts/" +#+END: +* References in other notes +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/notes/" +#+END: diff --git a/source/notes/oleh-krehel.org b/source/notes/oleh-krehel.org new file mode 100644 index 0000000..fe08266 --- /dev/null +++ b/source/notes/oleh-krehel.org @@ -0,0 +1,13 @@ +#+TITLE: Oleh Krehel +#+SUBTITLE: abo-abo, Emacs hacker +#+DESCRIPTION: Eshel Yaron's notes about Oleh Krehel +#+KEYWORDS: emacs +#+DATE: 2023-06-11 + + +* References in published posts +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/posts/" +#+END: +* References in other notes +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/notes/" +#+END: diff --git a/source/notes/torsten-hilbrich.org b/source/notes/torsten-hilbrich.org new file mode 100644 index 0000000..84fc31a --- /dev/null +++ b/source/notes/torsten-hilbrich.org @@ -0,0 +1,13 @@ +#+TITLE: Torsten Hilbrich +#+SUBTITLE: Original author of the Emacs Dictionary client +#+DESCRIPTION: Eshel Yaron's notes about Torsten Hilbrich +#+KEYWORDS: emacs +#+DATE: 2023-06-11 + + +* References in published posts +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/posts/" +#+END: +* References in other notes +#+BEGIN: links-to-note :dir "/Users/eshelyaron/checkouts/esy-publish/source/notes/" +#+END: diff --git a/source/posts/2023-05-17-orientation-in-json-documents-with-emacs-and-tree-sitter.org b/source/posts/2023-05-17-orientation-in-json-documents-with-emacs-and-tree-sitter.org new file mode 100644 index 0000000..f81386b --- /dev/null +++ b/source/posts/2023-05-17-orientation-in-json-documents-with-emacs-and-tree-sitter.org @@ -0,0 +1,84 @@ +#+TITLE: Orientation in JSON documents with Emacs and Tree-sitter +#+SUBTITLE: Adventures with the new Tree-sitter based Emacs mode for JSON +#+DESCRIPTION: A post by Eshel Yaron about his adventures with the new Tree-sitter based Emacs mode for JSON +#+KEYWORDS: emacs,lisp,treesitter +#+DATE: 2023-05-17 + +@@html:
@@Created on [{{{date}}}], last updated [{{{modification-time(%Y-%m-%d, t)}}}]@@html:
@@ + +I often find myself opening a large JSON document, usually a sample from a +larger dataset I'm analyzing, in order to figure out where in that JSON some +interesting fields are buried. In most cases, I have some idea of what these +fields should contain, so I can locate them quite easily by searching with +Emacs's [[note:isearch][Isearch]] for some string that I expect to find there. + +If I end up somewhere in the middle of a large JSON document, the challenge then +becomes determining the path from the root of the document to the position of my +cursor. [[note:emacs][Emacs]] has a built-in function ~json-path-to-position~ in =json.el= that +parses the buffer as a JSON document and returns the path to a given position, +but since I've recently started using the new Tree-sitter based JSON major mode +~json-ts-mode~, I figured it'd be a nice exercise to implement a +~json-path-at-point~ command that leverages Tree-sitter's knowledge of the +buffer's parse tree, and doesn't require =json.el=. + +There it is, I give you ~esy/json-path-at-point~: + +#+begin_src emacs-lisp + (defun esy/json-path-to-position (pos) + "Return the JSON path from the document's root to the element at POS. + + The path is represented as a list of strings and integers, + corresponding to the object keys and array indices that lead from + the root to the element at POS." + (named-let loop ((node (treesit-node-at pos)) (acc nil)) + (if-let ((parent (treesit-parent-until + node + (lambda (n) + (member (treesit-node-type n) + '("pair" "array")))))) + (loop parent + (cons + (pcase (treesit-node-type parent) + ("pair" + (treesit-node-text + (treesit-node-child (treesit-node-child parent 0) 1) t)) + ("array" + (named-let check ((i 1)) + (if (< pos (treesit-node-end (treesit-node-child parent i))) + (/ (1- i) 2) + (check (+ i 2)))))) + acc)) + acc))) + + (defun esy/json-path-at-point (point &optional kill) + "Display the JSON path at POINT. When KILL is non-nil, kill it too. + + Interactively, POINT is point and KILL is the prefix argument." + (interactive "d\nP" json-ts-mode) + (let ((path (mapconcat (lambda (o) (format "%s" o)) + (esy/json-path-to-position point) + "."))) + (if kill + (progn (kill-new path) (message "Copied: %s" path)) + (message path)) + path)) +#+end_src + +As an example of its usage, consider the following JSON: + +#+begin_src json-ts + { + "foo" : [ + { + "bar" : "baz" + }, + { + "one" : "two" + } + ] + } +#+end_src + +With point on "baz", typing ~M-x esy/json-path-at-point~ displays ~foo.0.bar~ in +the echo area. With a prefix argument (~C-u~), it copies the path to the kill +ring as well so I can, for instance, later copy it into some script I'm writing. diff --git a/source/posts/2023-05-26-extending-emacs-s-dictionary-library.org b/source/posts/2023-05-26-extending-emacs-s-dictionary-library.org new file mode 100644 index 0000000..378d512 --- /dev/null +++ b/source/posts/2023-05-26-extending-emacs-s-dictionary-library.org @@ -0,0 +1,147 @@ +#+TITLE: Extending Emacs's Dictionary Library +#+SUBTITLE: Introducing new customization options in the Emacs package dictionary.el +#+DESCRIPTION: A post from Eshel Yaron introducing new customization options in the Emacs package dictionary.el +#+KEYWORDS: emacs,lisp,dictionary +#+DATE: 2023-05-26 + +@@html:
@@Created on [{{{date}}}], last updated [{{{modification-time(%Y-%m-%d, t)}}}]@@html:
@@ + +Yesterday [2023-05-26] [[note:eli-zaretskii][Eli Zaretskii]] applied a patch that I've submitted to the +Emacs =master= branch which introduces new customization options to Emacs's +=dictionary.el= library. + +=dictionary.el= is a library that lets Emacs query [[https://www.rfc-editor.org/rfc/rfc2229.html][RFC2229]] dictionary servers +for word definitions, and display these definitions to the user upon request. +This package was initially written by [[note:torsten-hilbrich][Torsten Hilbrich]] to support both [[note:emacs][GNU Emacs]] +and XEmacs, and was added as a library to Emacs core in version 28. + +I've started using =dictionary.el= slightly over a month ago after reading +[[note:bozhidar-batsov][Bozhidar Batsov]]'s post [[https://emacsredux.com/blog/2023/04/11/looking-up-words-in-a-dictionary/][Looking Up Words in a Dictionary]]. Beforehand, I've been +using the ~define-word~ package by [[note:oleh-krehel][Oleh Krehel]] which achieves a similar goal of +obtaining and displaying word definitions, but instead of querying RFC2229 +dictionary servers like =dictionary.el= does, ~define-word~ performs HTTP +requests to web-based dictionary services and parses their HTML responses. +~define-word~ served me pretty well, but since =dictionary.el= is built into +Emacs I thought I'd give it a try. Another notable difference between these +packages is that ~define-word~ displays word definitions as transient messages +in the echo area, while =dictionary.el= pops up a dedicated =*Dictionary*= +buffer. + +My first impression of =dictionary.el= was quite positive. I consult the +dictionary quite often, and I liked how =dictionary.el= puts the definition in a +buffer that stays visible while I write rather than a message that disappears on +my first keystroke. But then there were also some quirks. First, I found that +whenever =dictionary.el= displays a definition in the =*Dictionary*= buffer, it +also selects that buffer. That's inconvenient when I'm in the middle of writing +a sentence and just want to quickly glance at a word's definition to make sure +I'm using it correctly. This is similar in nature to consulting a function's +definition or docstring when writing code, it should be a frictionless process. +Having to switch windows from the =*Dictionary*= buffer back to whatever buffer +I was in the middle of editing means unnecessary friction, especially if I have +multiple open windows to navigate between. + +I can imagine that at this point you're thinking "well, this is Emacs after all, +can't you customize it not to switch buffers or something?", and no, I couldn't +really. But now you can. More on that later though, for now let me rant just a +bit longer. + +The other problem I came across when trying to adopt =dictionary.el= was its +"cool" feature of restoring the window configuration you had while searching the +dictionary when you quit the =*Dictionary*= buffer. The way this works is that +=dictionary.el= saves the current window configuration when creating the +=*Dictionary*= buffer, and binds ~q~ to the command ~dictionary-close~ which, +among other things, tries to restore the saved window configuration. This is, +in my view, plain harmful. I can understand the idea behind it -- +=dictionary.el= wants to let you quit the =*Dictionary*= buffer, so it needs to +do something with the window it inhabits. It doesn't want to make an arbitrary +decision for the user so it takes the seemingly safe choice of reverting to how +things were when the buffer was created. The intention is admirable, but the +implementation is unfortunately shortsighted. The problem is that a lot of time +can pass between the moment you perform a dictionary search and the moment you +quit the =*Dictionary*= buffer, and in that time you may have moved on to +working on something else entirely. Perhaps you've even set up a perfect window +configuration that shows you exactly the right context for the task at hand. +And then you quit the =*Dictionary*= window. And your carefully crafted context +is gone, nowhere to be found. You're left looking at a bunch of random buffers +that happened to be open when you looked up "palaver" or some other word you've +read on [[https://protesilaos.com/codelog/2023-05-11-accessibility-software-freedom/][Prot's blog]]. + +This behavior basically assumes that you quit the =*Dictionary*= window right +after performing a search, without doing anything else with Emacs in the +meantime. In all other cases--it's a footgun. What =dictionary.el= should have +done instead, is to bind ~q~ to ~quit-window~ as is the case in ~special-mode~ +and its derivatives. This command is specifically designed for getting the +current buffer out of view: paraphrasing from the docstring, it "deletes the +window or shows some other buffer in it". And it's standard Emacs stuff, +tweakable via standard Emacs mechanisms. + +This brings me to my subtler, broader issue with =dictionary.el=, which is that +it doesn't make good use of existing Emacs facilities, and instead it tries to +reinvent the wheel in too many places for my taste. Maybe it's because it was +originally supposed to also support XEmacs which had some differences compared +to GNU Emacs, I'm really not sure. The result is a library that's harder for me +as a user to customize and extend. + +In face of this dissatisfaction, I decided to write my own dictionary lookup +package for Emacs. I've put together a minimal yet powerful RFC2229 dictionary +client package for Emacs in less than 300 lines of Elisp, including headers and +docstrings. I called it /Dict/, because it was shorter than =dictionary.el=. + +I've [[https://lists.gnu.org/archive/html/emacs-devel/2023-05/msg00318.html][proposed my Dict package for inclusion in GNU ELPA]] over the =emacs-devel= +mailing list earlier this month, noting that I wouldn't mind trying to modify +the built-in =dictionary.el= instead of adding this new package, if the Emacs +maintainers would be open for that kind of change. Eli [[https://lists.gnu.org/archive/html/emacs-devel/2023-05/msg00319.html][responded]] that +augmenting =dictionary.el= would indeed be preferable, which led after some more +back and forth to the patch this post is really all about. + +My patch adds a few new user options to =dictionary.el= that affect the behavior +of the ~dictionary-search~ command. The most influential new option is +~dictionary-display-definition-function~. It controls how ~dictionary-search~ +displays dictionary definitions, by specifying a function that responsible for +taking a definition and displaying it. We now also have a new function called +~dictionary-display-definition-in-help-buffer~ that you can use as the value of +~dictionary-display-definition-function~ to have definitions appear in the +standard Emacs =*Help*= buffer. The way I see it, the =*Help*= buffer is ideal +for this task as it is the same interface I use for viewing transient reference +information in Emacs--such as variables' docstrings or active keybindings--all +the time. In the =*Help*= buffer we can click on references to other dictionary +definitions to display them instead, just like we do with references to Elisp +functions. We can than press ~l~ to go back and ~r~ to go forward, and ~q~ +naturally invokes ~quit-window~ rather than a bespoke +window-quitting-and-restoring command. Other notable new options are +~dictionary-read-word-function~ and ~dictionary-read-dictionary-function~ that +control how ~dictionary-search~ prompts you for a word and a dictionary to +search in, respectively. + +On Eli's advice, I've also added a more high-level user option called +~dictionary-search-interface~ that uses the ~:set~ property of ~defcustom~ to +modify the values of all three aforementioned options together when you set it. +The idea is that this option acts as a bundle that users can customize once +instead of configuring several separate options. If you customize +~dictionary-search-interface~ to the symbol ~help~, it makes the +~dictionary-search~ command behave similarly to the corresponding command from +my short-lived Dict package. Crucially, definitions are displayed in a =*Help*= +buffer. It also enables completion when prompting for a word to search for +based on matching dictionary definitions and improves the default word choice. + +With this patch in place, my settings for =dictionary.el= are now as follows: + +#+begin_src emacs-lisp + (setopt dictionary-search-interface 'help + dictionary-default-strategy "prefix" + dictionary-default-dictionary "gcide" + dictionary-server "dict.org") + + (keymap-global-set "M-#" #'dictionary-search) +#+end_src + +If you also use the =dict.org= dictionary server, I strongly suggest setting +~dictionary-default-strategy~ (which designates the word-matching strategy to +use) to something other than the default ~"."~. Otherwise, the dictionary +server uses a matching strategy of its choosing, and this server's default +choice was [[https://lists.gnu.org/archive/html/emacs-devel/2023-05/msg00611.html][reported]] to misbehave for some words. As you can see above, I +currently use the ~"prefix"~ matching strategy. Other strategies I've found +useful are ~"substring"~ for matching words that contain the string we're +searching for, and ~"re"~ for regular expression matching. There's also +~"soundex"~ for matching words that sound similar to the input string, which is +helpful when I'm not totally sure how to spell the word I have in mind. diff --git a/sweep b/sweep index 41c40c0..cf01edd 160000 --- a/sweep +++ b/sweep @@ -1 +1 @@ -Subproject commit 41c40c0e1f26903ab16f65605e83a33563058fc8 +Subproject commit cf01edd0d8ebbdcb4120454bd6f985f00b76e520 -- 2.39.2