emacs/var/elfeed/db/data/b6/b6cf90bd7e2ebb50425918c230d0a49e8dae29ad
2022-01-03 12:49:32 -06:00

1 line
8.2 KiB
Plaintext

<!-- SC_OFF --><div class="md"><p><code>magit</code> UI is built with <a href="https://github.com/magit/transient">transient</a>. This post and <a href="https://www.reddit.com/r/emacs/comments/m518xh/transient_api_example_alternative_bindings_part_1/">part one</a> were prompted by the lack of API examples in the manual and docs.</p> <p>It&#39;s been a while. I was prodded into action. <strong>The resulting widget in this exercise is fun to use and really simple. Run the code and come back!</strong> Eval and press <code>M-p</code> to see the result. Use <code>SPC</code> <code>s</code> and <code>d</code> to add to the sentence or set it directly. <code>m</code> and <code>n</code> will display the constructed sentence in either a message or notification.</p> <h2>What Transient is Great At</h2> <p>First of all, I&#39;ve reached conclusions about when to use transient vs a modal editing system like <code>meow</code> or <code>evil</code></p> <ol> <li>application UI&#39;s that want to own the entire top-level binding space, such as git</li> <li>modal UI&#39;s for strings of Emacs commands that <ul> <li>tend to follow each other in direct succession</li> <li>have states that need display</li> <li>have rules about which commands are valid in which context</li> </ul></li> <li>grouping esoteric commands that need to use context and state to further refine organization, which commands are displayed, and how they are displayed</li> </ol> <p>Anything less rich can likely be done with <code>ivy</code>, but if you have to begin hacking with one or the other, <code>transient</code> will be more fun and useful pretty quick. It&#39;s just doesn&#39;t have as declarative of an API yet.</p> <h2>Making strings of commands</h2> <p>Let&#39;s go beyond simple prefix or nested prefix behavior by instead issuing multiple commands to construct a sentence. Doing so means we want the transient to stick around. There&#39;s a quick API to accomplish this for a prefix. You just set the <code>transient</code> key to true when calling <code>transient-define-suffix</code>.</p> <p>The example code shows plenty of ways to combine normal Emacs concepts like interactive forms with the transient API.</p> <p>```elisp</p> <pre><code>;; need these loaded (use-package &#39;notifications) (use-package &#39;transient) ;; We&#39;re going to construct a sentence with a transient. This is where it&#39;s stored. (defvar ikaruga--toy-sentence &quot;let&#39;s transient!&quot; &quot;Sentence under construction.&quot;) ;; First we define a suffix with a dynamic description. This allows us to ;; display the current value. (the transient API could use some more options to ;; display arbitrary values without making a suffix) ;; ;; The interactive form returns a single element list, (SENTENCE) which is then ;; passed into this command. ;; ;; Use transient faces with `propertize&#39; to make your prompts match the feel of ;; other transient behaviors such as switches. (transient-define-suffix ikaruga-sentence (sentence) &quot;Set the sentence from minibuffer read&quot; :transient t :description &#39;(lambda () (concat &quot;set sentence: &quot; (propertize (format &quot;%s&quot; ikaruga--toy-sentence) &#39;face &#39;transient-argument))) (interactive (list (read-string &quot;Sentence: &quot; ikaruga--toy-sentence))) (setf ikaruga--toy-sentence sentence)) ;; Next we define some update commands. We don&#39;t want these commands to dismiss ;; the transient, so we set their `:transient&#39; slot to t for `transient--do-stay&#39;. ;; https://github.com/magit/transient/blob/master/docs/transient.org#transient-state (transient-define-suffix ikaruga-append-dot () &quot;Append a dot to current sentence&quot; :description &quot;append dot&quot; :transient t ; true equates to `transient--do-stay&#39; (interactive) (setf ikaruga--toy-sentence (concat ikaruga--toy-sentence &quot;•&quot;))) (transient-define-suffix ikaruga-append-snowman () &quot;Append a snowman to current sentence&quot; :description &quot;append snowman&quot; :transient t (interactive) (setf ikaruga--toy-sentence (concat ikaruga--toy-sentence &quot;☃&quot;))) (transient-define-suffix ikaruga-clear () &quot;Clear current sentence&quot; :description &quot;clear&quot; :transient t (interactive) (setf ikaruga--toy-sentence &quot;&quot;)) ;; Now we want to consume our sentence. These commands are the terminal verbs ;; of our sentence construction, so they use the default `transient-do-exit&#39; ;; behavior. (transient-define-suffix ikaruga-message () &quot;Send the constructed sentence in a message&quot; :description &quot;show sentence&quot; ; :transient nil ; nil is default, `transient--do-exit&#39; behavior (interactive) (message &quot;constructed sentence: %s&quot; (propertize ikaruga--toy-sentence &#39;face &#39;transient-argument)) (setf ikaruga--toy-sentence &quot;&quot;)) (transient-define-suffix ikaruga-notify () &quot;Notify with constructed sentence&quot; :description &quot;notify sentence&quot; (interactive) (notifications-notify :title &quot;Constructed Sentence:&quot; :body ikaruga--toy-sentence) (setf ikaruga--toy-sentence &quot;&quot;)) ;; To bind all of our transient commands into a full transient (a &quot;prefix&quot;), we ;; just need group names and key-command pairs. To put the input sentence onto ;; its own line, we separate the next two groups into their own vector. You can ;; set the classname key to `transient-columns&#39; or `transient-row&#39; etc for more ;; specific arrangements. (transient-define-prefix ikaruga-sentence-toy () &quot;Create a sentence with several objects and a verb&quot; [&quot;Sentence Toy!&quot; (&quot;SPC&quot; ikaruga-sentence)] [[&quot;Transient Suffixes&quot; (&quot;d&quot; ikaruga-append-dot) (&quot;s&quot; ikaruga-append-snowman) &quot;&quot; ; empty string inserts a gap, visually separating the appends from the clear (&quot;c&quot; ikaruga-clear)] [&quot;Non-Transient Suffixes&quot; (&quot;m&quot; ikaruga-message) (&quot;n&quot; ikaruga-notify)]]) (global-set-key (kbd &quot;M-p&quot;) &#39;ikaruga-sentence-toy) </code></pre> <p>```</p> <p><em>edit</em> more compact final form</p> <h3>Compact declaration style!</h3> <p>Properties controlling suffix behavior / display can be set either in the command creation with <code>transient-define-suffix</code> or in the declarative creation of the transient. Since commands ought not know too much about what is binding and invoking them, you will probably prefer this form!</p> <p>Here, all of the <code>transient</code> and <em>simple</em> <code>description</code> keys are moved into the final prefix declaration:</p> <p>```elisp</p> <pre><code>(transient-define-prefix ikaruga-sentence-toy () &quot;Create a sentence with several objects and a verb&quot; [&quot;Sentence Toy!&quot; (&quot;SPC&quot; ikaruga-sentence :transient t)] [[&quot;Transient Suffixes&quot; (&quot;d&quot; &quot;append dot&quot; ikaruga-append-dot :transient t) (&quot;s&quot; &quot;append snowman&quot; ikaruga-append-snowman :transient t) &quot;&quot; ; empty string inserts a gap, visually separating the appends from the clear (&quot;c&quot; &quot;clear string&quot; ikaruga-clear :transient t)] [&quot;Non-Transient Suffixes&quot; (&quot;ms&quot; &quot;message sentence&quot; ikaruga-message) ; multi-key bindings :) (&quot;ns&quot; &quot;notify sentence&quot; ikaruga-notify)]]) </code></pre> <p>```</p> <p><em>end edit</em></p> <h2>Next Up</h2> <p>Currently I&#39;m grouping all of my project management and window &amp; buffer manipulation into one modal UI. This will be my next update. I&#39;m working on a package I call <code>ikaruga</code> to include some useful transients, editing commands, and a setup for the <code>meow</code> modal editing system. I&#39;m combining transients with a modal system. The most common commands will live in modal space while the transients will be used to glaze over special modes and groups of related commands that are normally under multiple prefixes.</p> <p>Any other behaviors you really want to see an example for?</p> </div><!-- SC_ON --> &#32; submitted by &#32; <a href="https://www.reddit.com/user/Psionikus"> /u/Psionikus </a> <br/> <span><a href="https://www.reddit.com/r/emacs/comments/pon0ee/transient_api_example_part_2_transientdostay/">[link]</a></span> &#32; <span><a href="https://www.reddit.com/r/emacs/comments/pon0ee/transient_api_example_part_2_transientdostay/">[comments]</a></span>