From 0f87c4ddb5e49344306fdfb600711ec833f0b173 Mon Sep 17 00:00:00 2001 From: Howard Abrams Date: Wed, 19 Apr 2023 08:47:47 -0700 Subject: [PATCH] Text object for functions I've been wanting the ability to have a text object select a function. Sure, I've had the ability to grab an s-expression, but a function, in other inferior languages, isn't bound by such syntax. --- elisp/beep.el | 2 +- ha-config.org | 43 +++++++++++++++++++++++++ ha-org.org | 13 +++++--- ha-programming.org | 79 ++-------------------------------------------- 4 files changed, 55 insertions(+), 82 deletions(-) diff --git a/elisp/beep.el b/elisp/beep.el index c5d06a9..5da411e 100644 --- a/elisp/beep.el +++ b/elisp/beep.el @@ -33,7 +33,7 @@ Customize the variable, `beep-alert-sound-file' to adjust the sound." "Call a program to speak the string, PHRASE. Customize the variable, `beep-speech-executable'." (let ((command (format beep-speech-executable phrase))) - (shell-command command))) + (async-shell-command command))) (defun beep--when-finished (phrase &optional to-speak) "Notify us with string, PHRASE, to grab our attention. diff --git a/ha-config.org b/ha-config.org index 734c359..27c21de 100644 --- a/ha-config.org +++ b/ha-config.org @@ -430,6 +430,7 @@ I took the following clever idea and code from [[http://blog.binchen.org/posts/c (setq found-range range))))) found-range)) #+end_src + Extend the text object to call this function for both /inner/ and /outer/: #+begin_src emacs-lisp (evil-define-text-object ha-evil-a-paren (count &optional beg end type) @@ -442,12 +443,54 @@ Extend the text object to call this function for both /inner/ and /outer/: :extend-selection nil (ha-evil-paren-range count beg end type nil)) #+end_src + And the keybindings: #+begin_src emacs-lisp (define-key evil-inner-text-objects-map "x" #'ha-evil-inner-paren) (define-key evil-outer-text-objects-map "x" #'ha-evil-a-paren) #+end_src +*** Text Object for Functions +While Emacs has the ability to recognize functions, the Evil text object does not. But text objects have both an /inner/ and /outer/ form, and what does that mean for a function? The /inner/ will be the /function itself/ and the /outer/ (like words) would be the surrounding /non-function/ stuff … in other words, the distance between the next functions. +#+begin_src emacs-lisp + (defun ha-evil-defun-range (count beg end type inclusive) + "Get minimum range of `defun` as a text object. + COUNT, is the number of _following_ defuns to count. BEG, END, + TYPE are not used. If INCLUSIVE is t, the text object is + inclusive acquiring the areas between the surrounding defuns." + (let ((start (save-excursion + (beginning-of-defun) + (when inclusive + (beginning-of-defun) + (end-of-defun)) + (point))) + (end (save-excursion + (end-of-defun count) + (when inclusive + (end-of-defun) + (beginning-of-defun)) + (point)))) + (list start end))) +#+end_src +Extend the text object to call this function for both /inner/ and /outer/: +#+begin_src emacs-lisp + (evil-define-text-object ha-evil-a-defun (count &optional beg end type) + "Select a defun and surrounding non-defun content." + :extend-selection t + (ha-evil-defun-range count beg end type t)) + + (evil-define-text-object ha-evil-inner-defun (count &optional beg end type) + "Select 'inner' (actual) defun." + :extend-selection nil + (ha-evil-defun-range count beg end type nil)) +#+end_src + +And the keybindings: +#+begin_src emacs-lisp + (define-key evil-inner-text-objects-map "d" #'ha-evil-inner-defun) + (define-key evil-outer-text-objects-map "d" #'ha-evil-a-defun) +#+end_src +Why not use ~f~? I’m reserving the ~f~ for a tree-sitter version that is not always available for all modes… yet. *** Key Chord Using the key-chord project allows me to make Escape be on two key combo presses on both sides of my keyboard: #+begin_src emacs-lisp diff --git a/ha-org.org b/ha-org.org index 6dd82d0..8801851 100644 --- a/ha-org.org +++ b/ha-org.org @@ -616,11 +616,16 @@ Splitting out HTML snippets is often a way that I can transfer org-formatted con "@import url('https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,300;0,600;1,300;1,600&display=swap');" "body { font-family: 'Literata', sans-serif; color: #333; }" "h1,h2,h3,h4,h5 { font-family: 'Overpass', sans-serif; color: #333; }" - "pre.src { background-color: #eee; }" + "code { color: steelblue }" + "pre { background-color: #eee; border-color: #aaa; }" + "a { text-decoration-style: dotted }" "@media (prefers-color-scheme: dark) {" - " body { background-color: #1d1f21; color: white; }" - " h1,h2,h3,h4,h5 { color: #fcca1b; }" - " pre.src { background-color: black; }" + " body { background-color: #1d1f21; color: white; }" + " h1,h2,h3,h4,h5 { color: #fcca1b; }" + " code { color: lightsteelblue; }" + " pre { background-color: black; border-color: #777; }" + " a:link { color: lightblue }" + " a:visited { color: violet }" "}" "") hard-newline)) diff --git a/ha-programming.org b/ha-programming.org index b3e4e7a..619c4ef 100644 --- a/ha-programming.org +++ b/ha-programming.org @@ -298,10 +298,10 @@ Now, I can create an /interface/ of keystrokes to jump around like a boss: (when (string-search "TREE_SITTER" system-configuration-features) (use-package combobulate :general + (:states 'visual :keymaps 'combobulate-key-map + "o" '("mark node" . combobulate-mark-node-dwim)) ; Mark symbol since "o" doesn't do anything (:states 'normal :keymaps 'combobulate-key-map - "v" 'combobulate-mark-node-dwim "g J" '("avy jump" . combobulate-avy) - "[ [" '("prev node" . combobulate-navigate-logical-previous) "] ]" '("next node" . combobulate-navigate-logical-next) "[ f" '("prev defun" . combobulate-navigate-beginning-of-defun) @@ -979,81 +979,6 @@ I think the [[https://fishshell.com/][fish shell]] is an interesting experiment :hook (fish-mode . (lambda () (add-hook 'before-save-hook 'fish_indent-before-save)))) #+end_src -* Aux -** AI Robot Helpers -Seems like a number of personal projects to interface with the ChatGPT. Let’s try a few, and see what I like. All of the project require the OpenAI key that I store in [[file:~/.authinfo.gpg][~/.authinfo.gpg]]: -#+begin_src emacs-lisp - (defvar ha-openai-key nil - "Decoded the OpenAI Key from the authinfo database.") - - (defun ha-openai-key () - "Return the decoded OpenAI Key stored in the authinfo database." - (unless ha-openai-key - (let* ((openai-encfun (--> "openai.com" - (auth-source-search :host it) - (first it) - (plist-get it :secret)))) - (setq ha-openai-key (funcall openai-encfun)))) - ha-openai-key) -#+end_src - -The [[https://github.com/xenodium/chatgpt-shell][chatgpt-shell]] project attempts to be an interactive session. -#+begin_src emacs-lisp - (use-package chatgpt-shell - :straight (:host github :repo "xenodium/chatgpt-shell") - :init - :config - (defun chatgpt-start-shell () - "Get the password and then start the `chatgpt-shell' program." - (interactive) - (setq chatgpt-shell-openai-key (ha-openai-key)) - (chatgpt-shell)) - - (ha-prog-leader - "a" '(:ignore t :which-key "ChatGPT") - "a s" '("shell" . chatgpt-start-shell))) -#+end_src - -Let’s play with [[https://github.com/CarlQLange/chatgpt-arcana.el][ChatGPT Arcana]] project. -#+begin_src emacs-lisp - (use-package chatgpt-arcana - :straight (:host github :repo "CarlQLange/ChatGPT-Arcana.el" :files ("*.el")) - :config - (defun chatgpt-arcana-start () - "Get the password and then start the `chatgpt-arcana-start-chat' program." - (interactive) - (setq chatgpt-arcana-api-key (ha-openai-key)) - (chatgpt-arcana-start-chat)) - - (use-package all-the-icons - :config - (add-to-list 'all-the-icons-mode-icon-alist - '(chatgpt-arcana-chat-mode all-the-icons-octicon - "comment-discussion" - :height 1.0 - :v-adjust -0.1 - :face all-the-icons-purple))) - (use-package pretty-hydra - :config - (eval `(pretty-hydra-define chatgpt-arcana-hydra (:color blue :quit-key "q" :title "ChatGPT Arcana") - ("Query" - (("a" chatgpt-arcana-query "Query") - ("r" chatgpt-arcana-replace-region "Replace region")) - "Insert" - (("i" chatgpt-arcana-insert-at-point-with-context "At point with context") - ("I" chatgpt-arcana-insert-at-point "At point") - ("j" chatgpt-arcana-insert-after-region "Before region") - ("J" chatgpt-arcana-insert-before-region "After region")) - "Chat" - (("c" chatgpt-arcana-start-chat "Start chat")) - "Shortcuts" - (,@(chatgpt-arcana-generate-prompt-shortcuts)))))) - - (ha-prog-leader - "a" '(:ignore t :which-key "ChatGPT") - "a c" '("Start chat" . chatgpt-arcana-start) - "a a" '("AI commands" . chatgpt-arcana-hydra/body))) -#+end_src * Technical Artifacts :noexport: Provide a name to =require= this code. #+begin_src emacs-lisp :exports none