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.
This commit is contained in:
parent
b7a35fe1dc
commit
0f87c4ddb5
4 changed files with 55 additions and 82 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }"
|
||||
" code { color: lightsteelblue; }"
|
||||
" pre { background-color: black; border-color: #777; }"
|
||||
" a:link { color: lightblue }"
|
||||
" a:visited { color: violet }"
|
||||
"}"
|
||||
"</style>")
|
||||
hard-newline))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue