Compare commits

..

No commits in common. "590bc6f567aa164b763005e4487fb13b12e89cc4" and "9dcfb216d3536c82dc76a61d0cf44c5bafb2f9ff" have entirely different histories.

17 changed files with 238 additions and 390 deletions

View file

@ -44,7 +44,7 @@ Best success comes from using the [[https://github.com/d12frosted/homebrew-emacs
I find that I need to … at least, on my work computer, install two different versions of Emacs that I use to distinguish one for “work” and the other for other activities, like IRC and [[file:ha-feed-reader.org][elfeed]]. To that end, I run the following command to install Emacs:
#+begin_src sh
brew reinstall $(brew deps emacs-plus@30)
brew reinstall emacs-plus@30 --with-mailutils
brew install emacs-plus@30 --with-native-comp --with-mailutils --with-imagemagick --with-savchenkovaleriy-big-sur-icon --with-no-frame-refocus --debug
#+end_src
And if it fails, choose =shell= and type:
#+begin_src sh

View file

@ -19,7 +19,7 @@
"A WAV or AU file used at the completion of a function.")
;; My replacement in case we can't play internal sounds:
(defun beep-beep (&rest _)
(defun beep-beep ()
"Play a default notification sound file.
Customize the variable, `beep-alert-sound-file' to adjust the sound."
(if (fboundp 'play-sound-internal)

View file

@ -33,7 +33,7 @@ start some music to indicate we are working, and set a timer.
Call `ha-focus-break' when finished."
(interactive)
(ha-focus-countdown-timer 25 'ha-focus-break)
(ha-focus--command "tell application \"VLC\" to play")
(ha-focus--command "tell application \"Spotify\" to play")
(if (eq major-mode 'org-mode)
(org-clock-in)
(org-clock-in-last))
@ -47,7 +47,7 @@ This also starts another break timer, that calls
`ha-focus-break-over' when finished."
(interactive)
(run-with-idle-timer 30 nil 'ha-focus-capture)
(ha-focus--command "tell application \"VLC\" to pause")
(ha-focus--command "tell application \"Spotify\" to pause")
(ha-focus-countdown-timer 5 'ha-focus-break-over)
(message "🍅 Time to take a break."))

View file

@ -26,34 +26,6 @@ A literate programming file configuring critical applications.
#+end_src
Can we call the following /applications/? I guess.
* Agentic Interface
Ethic issues aside, Im [[https://technobabble.bearblog.dev/fine-ill-try-ai/][trying AI]] … primarily because my company requires my participation. I appreciate the approaches from my fellow Emacsians, for while VSCode may be a fine editor, it cant compete with my creation here.
** Agent Shell
Installing Xenodiums [[https://github.com/xenodium/agent-shell][agent-shell]], requires installing the ACP libraries:
#+BEGIN_SRC emacs-lisp
(use-package acp
:straight (:type git :host github :repo "xenodium/acp.el"))
(use-package agent-shell
:straight (:type git :host github :repo "xenodium/agent-shell")
:after acp
:config
(ha-leader "a i" '("agent chat" . agent-shell))
;; Evil state-specific RET behavior: insert mode = newline, normal mode = send
(evil-define-key 'insert agent-shell-mode-map (kbd "RET") #'newline)
(evil-define-key 'insert agent-shell-mode-map (kbd "C-RET") #'agent-shell-submit)
(evil-define-key 'normal agent-shell-mode-map (kbd "RET") #'comint-send-input)
;; Configure *agent-shell-diff* buffers to start in Emacs state
(add-hook 'diff-mode-hook
(lambda ()
(when (string-match-p "\\*agent-shell-diff\\*" (buffer-name))
(evil-emacs-state)))))
#+END_SRC
* Git and Magit
Can not live without [[https://magit.vc/][Magit]], a Git porcelain for Emacs. I stole the bulk of this work from Doom Emacs.
#+begin_src emacs-lisp
@ -370,51 +342,15 @@ Let's extend Magit with [[https://github.com/magit/forge][Magit Forge]] for work
Every /so often/, pop over to the following URLs and generate a new token where the *Note* is =forge=, and then copy that into the [[file:~/.authinfo.gpg][~/.authinfo.gpg]]:
- [[https://gitlab.com/-/user_settings/personal_access_tokens][Gitlab]]
- [[https://github.com/settings/tokens][Github]]
and make sure this works:
Make sure this works:
#+begin_src emacs-lisp :tangle no :results replace
#+begin_src emacs-lisp :tangle no :results replace
(ghub-request "GET" "/user" nil
:forge 'github
:host "api.github.com"
:username "howardabrams"
:auth 'forge)
#+end_src
** Magit Github
Jonathan Chus [[https://github.com/jonathanchu/magit-gh][magit-gh]] project is /simpler/ than [[#Forge][Forge]] (see [[https://jonathanchu.is/posts/introducing-magit-gh/][this essay]] for details).
First, install and configure the [[https://github.com/cli/cli/blob/trunk/docs/install_macos.md#homebrew][Github CLI]] program.
#+BEGIN_SRC sh
brew install gh
#+END_SRC
Create a =GITHUB_TOKEN= under =/settings/tokens=.
The required scopes are =repo=, =read:org=, =admin:public_key=.
Also, these dont last long, so return and regenerate routinely.
Next, [[https://cli.github.com/manual/gh_auth_login][configure it]] with:
#+BEGIN_SRC sh
gh auth login --hostname ${GH_HOST:-github.com}
#+END_SRC
And pass in the =GITHUB_TOKEN= environment variable.
Verify that this works:
#+BEGIN_SRC sh
gh pr list
#+END_SRC
With the =gh= CLI working, we can install and use this project:
#+BEGIN_SRC emacs-lisp
(use-package magit-gh
:after magit
:straight (:type git :host github :repo "jonathanchu/magit-gh"))
#+END_SRC
#+end_src
** Pushing is Bad
Pushing directly to the upstream branch is /bad form/, as one should create a pull request, etc. To prevent an accidental push, we /double-check/ first:
@ -428,11 +364,10 @@ Pushing directly to the upstream branch is /bad form/, as one should create a pu
(magit-get "branch" branch "remote"))))
(user-error "Push to upstream aborted by user"))))
#+end_src
** Github Search?
Wanna see an example of how others use a particular function?
#+begin_src emacs-lisp
(defun ha-github-code-search(&optional search)
(defun my-github-search(&optional search)
(interactive (list (read-string "Search: " (thing-at-point 'symbol))))
(let* ((language (cond ((eq major-mode 'python-mode) "Python")
((eq major-mode 'emacs-lisp-mode) "Emacs Lisp")
@ -970,5 +905,4 @@ Let's provide a name so that the file can be required:
# Local Variables:
# eval: (add-hook 'after-save-hook #'org-babel-tangle t t)
# jinx-local-words: "Emacsians VSCode"
# End:

View file

@ -26,7 +26,7 @@ A literate programming file for helper apps in Emacs.
#+end_src
* Introduction
The following applications are not needed. I alternate between trying to /stay in Emacs/ taking advantage of the consistent interface, and using a stand-alone app on my Workday computer.
* Social Media Apps
* Federation
** Mastodon
Glad to see the 2FA feature is working on the [[https://codeberg.org/martianh/mastodon.el][mastodon.el]] project, and even more glad to see the great birdland diaspora arrive in the land of the toots.
#+begin_src emacs-lisp
@ -143,6 +143,19 @@ Perhaps we can make it more attractive:
ement-room-timestamp-format ""
ement-room-send-message-filter 'ement-room-send-org-filter))
#+end_src
* Other
** Twitter
The venerable [[https://github.com/hayamiz/twittering-mode/tree/master][twittering-mode]] allows me to follow all the twits.
#+begin_src emacs-lisp :tangle no
(use-package twittering-mode
:init
(setq twittering-use-master-password t
epa-pinentry-mode 'loopback)
:config
(defalias 'epa--decode-coding-string 'decode-coding-string)
(ha-leader "a t" '("twitter" . twit)))
#+end_src
And we are no longer using this package.
** Telega
I'm thinking the [[https://zevlg.github.io/telega.el/][Telega package]] would be better than Bitlbee for Telegram communication.
@ -214,89 +227,17 @@ And my [[https://gitlab.com/howardabrams/emacs-ironsworn][new Ironsworn project]
#+end_src
The project is interesting, and I should record a tutorial how to use it.
* Playing Music
After building a NAS out of a Raspberry Pi to hold my /ripped/ CD collection, lets control my playlists from Emacs.
A more /rich/ experience than the [[https://github.com/isamert/empv.el][empv]] project is Álvaros [[https://github.com/xenodium/ready-player][ready-player]]. First install [[https://mpv.io/][mpv]]:
#+BEGIN_SRC sh
Why not? Lets see if the [[https://github.com/isamert/empv.el][empv]] project is sufficient. First install =mpv=, as in:
#+begin_src sh
brew install mpv
#+END_SRC
And make sure we can play it:
#+BEGIN_SRC sh
mpv "/Volumes/music/Steely Dan/Aja/01 Black Cow.mp3"
#+END_SRC
And stream my favorite local Jazz radio station:
#+BEGIN_SRC sh
mpv --terminal --force-window=no --no-resume-playback \
'https://ais-sa3.cdnstream1.com/2442_128.aac/playlist.m3u8' &
#+END_SRC
And then the configuration:
#+BEGIN_SRC emacs-lisp
(use-package ready-player
:straight (:type git :host github :repo "xenodium/ready-player")
:config
(when (eq system-type 'darwin)
(set-fontset-font t nil "SF Pro Display" nil 'append)
(ready-player-macos-use-sf-symbols))
(setq ready-player-my-media-collection-location "/Volumes/music")
(defun ha-ready-player-load-playlist ()
"Open a playlist in the correct location."
(interactive)
(let ((default-directory
(expand-file-name "Playlists/"
ready-player-my-media-collection-location)))
(setq ready-player-shuffle t)
(call-interactively 'ready-player-load-m3u-playlist)))
(transient-append-suffix 'ready-player-menu "?"
'("P" "Load Playlist" ha-ready-player-load-playlist))
(defun ha-ready-player-load-directory ()
"Open a directory in the correct location."
(interactive)
(let ((default-directory
(file-name-as-directory ready-player-my-media-collection-location)))
(setq ready-player-shuffle nil)
(call-interactively 'ready-player-load-directory)))
(transient-append-suffix 'ready-player-menu "d"
'("A" "Play Album" ha-ready-player-load-directory))
(ha-leader "a r" '("ready player" . ready-player-menu))
(ready-player-mode +1))
#+END_SRC
Since ready-player doesnt support [[https://github.com/xenodium/ready-player/issues/36][adjusting the volume]], I can, at least, set the /default volume/ low enough that my speaker adjustment affects it:
#+BEGIN_SRC conf :tangle ~/.config/mpv/mpv.conf
volume=60
#+END_SRC
Would love to have the /playing/ media in a particular tab-bar and in a particular location.
#+BEGIN_SRC emacs-lisp :tangle no
(defun ha-ready-player-switch ()
"Switch to the buffer window of the ready-player."
(interactive)
(let* ((buf-list
(seq-filter (lambda (buf)
(string-match (rx string-start "ready-player: "
(+ (not "*")) string-end)
(buffer-name buf)))
(buffer-list)))
(buffer (car buf-list))
(tab (tab-bar-get-buffer-tab (buffer-name buffer) t)))
(if tab
(tab-bar-switch-to-tab (alist-get 'name tab))
(tab-bar-switch-to-tab "main"))))
#+END_SRC
#+end_src
What else?
#+begin_src emacs-lisp
(use-package empv
:straight (:host github :repo "isamert/empv.el")
:general (ha-leader
"a p" '(empv-map :wk "play music")))
#+end_src
* Technical Artifacts :noexport:
Let's =provide= a name so we can =require= this file:

View file

@ -244,7 +244,6 @@ Im use [[https://github.com/deseven/icanhazshortcut][ICanHazShortcut]] to hav
#+begin_src sh :shebang "#!/bin/bash" :tangle ~/bin/emacs-capture-clock
/usr/bin/osascript ~/bin/emacs-capture-clock.scr
#+end_src
But the following Applescript does the work:
#+begin_src applescript :sheband "#!/usr/bin/osascript" :tangle ~/bin/emacs-capture-clock.scr
tell application "System Events" to set theApp to name of first application process whose frontmost is true
@ -274,7 +273,6 @@ Now we have some goodies on the clipboard, and the script uses =emacsclient= to
(ignore-errors
(delete-frame)))
#+end_src
Oh, and it this is from the Terminal program, lets wrap it in a block:
#+begin_src emacs-lisp
(defun ha-external-capture-code-to-org ()
@ -290,10 +288,136 @@ Oh, and it this is from the Terminal program, lets wrap it in a block:
(ignore-errors
(delete-frame)))
#+end_src
#+begin_src conf :tangle ~/.config/iCanHazShortcut/config.ini
[main]
config version = 2
shell = /bin/bash -l
populate_menu_with_actions = yes
show_hotkeys_in_menu = yes
check_for_updates = yes
start_on_login = yes
show_icon_in_statusbar = yes
set_workdir_with_cd = no
window_x = -988
window_y = 172
window_width = 600
window_height = 361
shortcut_column_enabled = yes
action_column_enabled = yes
command_column_enabled = yes
workdir_column_enabled = no
shortcut_column_width = 80
action_column_width = 160
command_column_width = 173
workdir_column_width = 100
[shortcut1]
shortcut = ⇧⌃⌥⌘E
action = Personal Emacs
command = open -a Emacs
workdir =
enabled = yes
[shortcut2]
shortcut = ⇧⌃⌥E
action = Work Emacs
command = FOR_WORK=yes open -a Emacs-Work
workdir =
enabled = yes
[shortcut3]
shortcut = ⇧⌃⌥⌘X
action = Emacs Capture
command = ~/bin/emacs-capture
workdir =
enabled = yes
[shortcut4]
shortcut = ⇧⌃⌥X
action = Emacs Capture Clipboard
command = ~/bin/emacs-capture-clock
workdir =
enabled = yes
[shortcut5]
shortcut = ⇧⌃⌥T
action = iTerm
command = open -a iTerm
workdir =
enabled = yes
[shortcut6]
shortcut = ⇧⌃⌥S
action = Slack
command = open -a Slack
workdir =
enabled = yes
[shortcut7]
shortcut = ⇧⌃⌥W
action = Spotify
command = open -a Spotify
workdir =
enabled = yes
[shortcut8]
shortcut = ⇧⌃⌥F
action = Firefox
command = open -a Firefox
workdir =
enabled = yes
[shortcut9]
shortcut = ⇧⌃⌥C
action = Chome
command = ~/bin/chrome.scr
workdir =
enabled = yes
[shortcut10]
shortcut = ⇧⌃⌥Q
action = Keepass
command = open -a KeepassXC
workdir =
enabled = yes
[shortcut11]
shortcut = ⇧⌃⌥Z
action = Zoom
command = open -a zoom.us
workdir =
enabled = yes
[shortcut12]
shortcut = ⌃F1
action = Mute Zoom
command = ~/bin/zoom-muter
workdir =
enabled = yes
[shortcut13]
shortcut = ⇧⌃⌥⌘M
action = Capture Meeting
command = ~/bin/emacs-capture-meeting
workdir =
enabled = yes
[shortcut14]
shortcut = ⇧⌃⌥⌘B
action = Outlook
command = open -a "Microsoft Outlook"
workdir =
enabled = yes
[shortcut15]
shortcut = ⇧⌃⌥⌘D
action = Discord
command = open -a Discord
workdir =
enabled = yes
#+end_src
Configure the *ICanHazShortcut* shortcuts to call these scripts, as in this screenshot:
[[file:screenshots/icanhazshortcuts.png]]
And here is the configuration file for that:
#+begin_src conf :tangle ~/.config/iCanHazShortcut/config.ini :mkdirp yes
[main]

View file

@ -164,20 +164,16 @@ Ive often called =imenu= to easily jump to a function definition in a file (o
#+begin_src emacs-lisp
(defun ha-imenu-setup ()
"Set up the imenu customization. Use in hooks."
(condition-case err
(ignore-errors
(imenu-add-menubar-index)
(setq-local imenu-auto-rescan t)
(when (derived-mode-p 'prog-mode)
(setq-local imenu-sort-function 'imenu--sort-by-name))
(imenu-unavailable
(let ((inhibit-message t))
(message "Warning: %s" (error-message-string err))))))
(setq-local imenu-sort-function 'imenu--sort-by-name))))
(add-hook 'org-mode-hook 'ha-imenu-setup)
(add-hook 'markdown-mode-hook 'ha-imenu-setup)
(add-hook 'makefile-mode-hook 'ha-imenu-setup)
(add-hook 'prog-mode-hook 'ha-imenu-setup)
(add-hook 'makefile-mode-hook 'ha-imenu-setup)
#+end_src
** File Access
*** Remote Files
@ -809,37 +805,6 @@ If you hit the following keys /before/ you select a target, you get special acti
- ~z~ :: =zap-to-char= … kill from current point to the target
Im not thinking of ideas of what would be useful, e.g. ~v~ to highlight from cursor to target, etc.
What is missing is copying the entire line:
#+BEGIN_SRC emacs-lisp
(use-package avy
:config
(defun avy-action-copy-whole-line (pt)
(save-excursion
(goto-char pt)
(cl-destructuring-bind (start . end)
(bounds-of-thing-at-point 'line)
(copy-region-as-kill start end)))
(select-window
(cdr
(ring-ref avy-ring 0)))
t)
(defun avy-action-copy-rest-line (pt)
(save-excursion
(goto-char pt)
(let ((start pt)
(end (line-end-position)))
(copy-region-as-kill start end)))
(select-window
(cdr
(ring-ref avy-ring 0)))
t)
(setf (alist-get ?w avy-dispatch-alist) 'avy-action-copy
(alist-get ?W avy-dispatch-alist) 'avy-action-copy-whole-line
(alist-get ?N avy-dispatch-alist) 'avy-action-copy-rest-line))
#+END_SRC
Want to know something amazing. In a Terminal, like =vterm= or =eshell=, I run ~s-g~ and pinpoint the UUID in the output of a long command. Then type ~y~ and then ~C-y~ to paste that ID without even moving the mouse.
*** Link Hint, the Link Jumper
The [[info:emacs#Goto Address mode][Goto Address]] mode (see this [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Goto-Address-mode.html][online link]]) turns URLs into clickable links. Nice feature and built into Emacs, but it requires using the mouse or moving to the URL and hitting ~Return~ (if you like this idea, check out [[https://xenodium.com/actionable-urls-in-emacs-buffers/][Álvaro Ramírez's configuration]] for this).
@ -991,6 +956,7 @@ Since I wasnt using all the features that [[https://github.com/bbatsov/projec
:config
(ha-leader
"p" '(:ignore t :which-key "projects")
"p W" '("initialize workspace" . ha-workspace-initialize)
"p p" '("switch project" . ha-tab-bar-new-project)
"p !" '("run cmd in project root" . project-shell-command)
@ -1022,8 +988,9 @@ Couple notes:
tab-bar-close-button-show nil ; hide tab close / X button
tab-bar-new-tab-choice "*dashboard*" ; buffer to show in new tabs
tab-bar-tab-hints t ; show tab numbers
tab-bar-separator " "
tab-bar-button-relief 0
tab-bar-button-margin '(40 . 1)
;; Jump to a tab by numbers (see the keybindings set later):
tab-bar-select-tab-modifiers '(super control))
@ -1040,8 +1007,8 @@ New workspace is a tab with a specific name that opens up a specific buffer or a
#+begin_src emacs-lisp
(defun ha-tab-bar-new (name &optional bff)
"Create a new tab with a NAME.
With a non-nil BFF, call BFF as a function or switch
to the BFF buffer or the files listed."
With a non-nil IFF, call IFF as a function or switch
to the IFF buffer or the files listed."
(interactive "sWorkspace Name: ")
(tab-bar-switch-to-tab name)
(when bff
@ -1091,20 +1058,9 @@ If we close a tab that is a project, we want to close all the buffers associated
(buffer-list)))
#+end_src
And lets make one tab to be /special/:
#+BEGIN_SRC emacs-lisp
(defun ha-switch-to-special ()
"Change the perspective to the Sprint/Notes project."
(interactive)
(tab-bar-switch-to-tab "main"))
#+END_SRC
And some shortcut keys from the =general= project:
#+BEGIN_SRC emacs-lisp
(use-package emacs
:config
(general-nmap :prefix "SPC"
"<tab>" '(:ignore t :which-key "workspaces")
"<tab> <tab>" '("switch" . tab-switch)
@ -1112,15 +1068,13 @@ And some shortcut keys from the =general= project:
"<tab> n" '("new space" . ha-tab-bar-new)
"<tab> u" '("update names" . ha-tab-bar-update-names)
"<tab> d" '("delete space" . ha-tab-bar-delete)
"<tab> t" '("toggle tab-bar" . tab-bar-mode)
"<tab> w" '("special space" . ha-switch-to-special)
"<tab> `" '("recent" . tab-bar-switch-to-recent-tab))
(global-set-key (kbd "s-C-t") 'ha-tab-bar-new)
(global-set-key (kbd "s-C-[") 'tab-bar-switch-to-prev-tab)
(global-set-key (kbd "s-C-]") 'tab-bar-switch-to-next-tab)
(tab-bar-mode 1))
(tab-bar-mode 1)
#+END_SRC
I want to quickly jump, by the number shown on the tab, to that grouping. The following two functions create leader sequences with the name of the tab group:
@ -1224,6 +1178,24 @@ First, create a variable that contains the Unicode values for the numbers. I am
And then use this function to replace the standard =tab-bar-tab-name-format-function=:
#+BEGIN_SRC emacs-lisp
(defun ha/tab-bar-tab-name-format-default (tab i)
"Replacement for `tab-bar-tab-name-format-function'.
Places a special symbol for the initial digit."
(let ((current-p (eq (car tab) 'current-tab))
(tab-num (if (and tab-bar-tab-hints (< i 10))
(alist-get i ha/circle-numbers-alist) "")))
(propertize
(concat tab-num
" "
(alist-get 'name tab)
(or (and tab-bar-close-button-show
(not (eq tab-bar-close-button-show
(if current-p 'non-selected 'selected)))
tab-bar-close-button)
"")
" ")
'face (funcall tab-bar-tab-face-function tab))))
(defun ha/tab-bar-tab-name-format-default (tab i)
"Replacement for `tab-bar-tab-name-format-function'.
Places a special symbol for the initial digit."
@ -1233,12 +1205,10 @@ And then use this function to replace the standard =tab-bar-tab-name-format-fun
(propertize
(if (and tab-bar-tab-hints (< i 10)) (alist-get i ha/circle-numbers-alist) "")
'face 'ha/tab-bar-numbers)
;; Fixed-width gap so active (variable-pitch) and inactive tabs match
(propertize " " 'face 'tab-bar)
;; Add tab name with the face returned by tab-bar-tab-face-function
(propertize
(concat
" "
" " ; Add initial space
(alist-get 'name tab)
(or (and tab-bar-close-button-show
(not (eq tab-bar-close-button-show

View file

@ -76,7 +76,8 @@ To make the active window /more noticeable/, we /dim/ the in-active windows with
As an interesting alternative, check out the [[https://www.emacs.dyerdwelling.family/emacs/20240208164549-emacs-selected-window-accent-mode-now-on-melpa/][selected-window-accent]] project.
** Ultra Scroll
The [[https://github.com/jdtsmith/ultra-scroll][ultra-scroll]] project allows smoother scrolling of text and images. While this splits text at the top/bottom of buffer windows, we no longer work within a 80x24 text matrix. Large images would either be "there or not" which resulted large jumps and large distractions.
The [[https://github.com/jdtsmith/ultra-scroll][ultra-scroll]] project allows smoother scrolling of text and images. While this splits text at the top/bottom of buffer windows, we no longer work within a 80x24 text matrix. Large images would
either be "there or not" which resulted large jumps and large distractions.
#+BEGIN_SRC emacs-lisp
(use-package ultra-scroll
@ -88,45 +89,16 @@ The [[https://github.com/jdtsmith/ultra-scroll][ultra-scroll]] project allows sm
(ultra-scroll-mode 1))
#+END_SRC
** Find the Bloody Cursor
Large screen, lots of windows, so where is the cursor? While I used to use =hl-line+=, Sacha Chuas [[https://fediscience.org/@ericsfraga/116279043710841253][suggestion]] solves the problem of the built-in =hl-line= that /highlights paragraphs/ in Org with a single function:
#+BEGIN_SRC emacs-lisp
(defun get-visual-line-range ()
"Identify current visual line (for highlighting mostly)
Use the visual line functions to define the range in the buffer
that should be highlighted when ~hl-line-mode~ is enabled. By
default, the whole line in the file is highlighted. This is not
particularly useful in org files where I have whole paragraphs in a
single file line."
(let (b e)
(save-excursion
(beginning-of-visual-line)
(setq b (point))
(end-of-visual-line)
(setq e (point)))
(cons b e)))
(setq hl-line-range-function #'get-visual-line-range)
(global-hl-line-mode 1)
#+END_SRC
The real trick should /visually/ highlight the current line without being too light and interfering with seeing the text or being too obnoxious (see my [[file:ha-theme.org::*Dark Theme][Dark Theme]]).
I found that the prolific [[https://protesilaos.com/][Protesilaos Stavrou]] [[https://protesilaos.com/codelog/2022-03-14-emacs-pulsar-demo/][introduced his Pulsar project]] is just what I need. Specifically, I might /loose the cursor/ and need to have it highlighted (using ~F8~), but also, this automatically highlights the cursor line with specific /actions/ , like changing windows.
Large screen, lots of windows, so where is the cursor? While I used to use =hl-line+=, I found that the prolific [[https://protesilaos.com/][Protesilaos Stavrou]] [[https://protesilaos.com/codelog/2022-03-14-emacs-pulsar-demo/][introduced his Pulsar project]] is just what I need. Specifically, I might /loose the cursor/ and need to have it highlighted (using ~F8~), but also, this automatically highlights the cursor line with specific /actions/ , like changing windows.
#+begin_src emacs-lisp
(use-package pulsar
:straight (:host github :repo "protesilaos/pulsar")
:after winum ace-window
:custom
(pulsar-face 'pulsar-generic)
(pulsar-delay 0.15)
:config
(set-face-attribute 'pulsar-generic nil :background "orange")
(dolist (built-in-function '(recenter-top-bottom move-to-window-line-top-bottom reposition-window
bookmark-jump other-window delete-window delete-other-windows
forward-page backward-page scroll-up-command scroll-down-command

View file

@ -54,7 +54,9 @@ I split the configuration of Evil mode into sections. First, global settings:
evil-want-keybinding nil ; work with evil-collection
evil-want-integration t
evil-want-C-u-scroll nil
evil-want-C-i-jump nil))
evil-want-C-i-jump nil
evil-escape-key-sequence "jk"
evil-escape-unordered-key-sequence t))
#+end_src
The Escape key act like ~C-g~ and always go back to normal mode?
@ -197,7 +199,7 @@ I find that myself not noticing Im in /insert/ mode, and start typing =j= rep
To remedy this, Im using the [[https://www.emacswiki.org/emacs/KeyChord][KeyChord]] code:
#+BEGIN_SRC emacs-lisp
#+BEGIN_SRC emacs-lisp :tangle no
(use-package key-chord
:config
(setq key-chord-two-keys-delay 0.1)

View file

@ -286,7 +286,7 @@ Each year, specify the first day of the first sprint of the year:
#+BEGIN_SRC emacs-lisp
;; CHANGEME Each year as this should update:
(defvar sprint-start-date (get-date-time "2026-01-06")
(defvar sprint-start-date (get-date-time "2026-01-13")
"The date of the first day of the first sprint of the year.
See `sprint-range'.")
#+END_SRC
@ -301,20 +301,15 @@ My company has sprints two weeks long that we can calculate from
The number of the sprint comes from the number of /bi-weeks/ (14 day increments) from the =sprint-start-date=. We can calculate the number of seconds from this /start date/ and divide it by the =sprint-length=:
#+begin_src emacs-lisp
(defun sprint-number (&optional date)
(defun sprint-number (date)
"Return the number of 14-day intervals since SPRINT-START-DATE to DATE.
DATE is a string in YYYY-MM-DD format."
(unless date
(setq date (format-time-string "%Y-%m-%d")))
(let* ((end-time (get-date-time date))
(diff-seconds (float-time (time-subtract end-time sprint-start-date))))
;; After calculating the number of 'bi-weeks' (2 week sprint increments),
;; we add one to return the correct sprint for the networking team:
;; (1+ (floor (/ diff-seconds sprint-length)))
;; Or for the platform storage team:
(1+ (* 2 (floor (/ diff-seconds sprint-length))))))
;; we add one to return the correct sprint:
(1+ (floor (/ diff-seconds sprint-length)))))
#+end_src
And some tests to verify that:

View file

@ -3,7 +3,7 @@
#+date: 2020-09-18
#+tags: emacs org
#+startup: inlineimages
#+lastmod: [2026-04-08 Wed]
#+lastmod: [2025-12-02 Tue]
A literate programming file for configuring org-mode and those files.
@ -1022,8 +1022,8 @@ Next, create a configuration file, =~/.config/proselint/config= file, to turn on
}
#+end_src
And tell [[https://www.flycheck.org/][flycheck]] to use Proselint:
#+begin_src emacs-lisp :tangle no
And tell [[https://www.flycheck.org/][flycheck]] to use this:
#+begin_src emacs-lisp
(use-package flycheck
:config
(add-to-list 'flycheck-checkers 'proselint)

View file

@ -33,9 +33,8 @@ While Emacs supplies a Python editing environment, well still use =use-packag
#+begin_src emacs-lisp
(use-package python
:after flycheck
:mode (((rx ".flake8" eol) . conf-mode)
((rx "Pipfile" eol) . conf-mode)
((rx ".wsgi" eol) . python-mode))
:mode ("[./]flake8\\'" . conf-mode)
:mode ("/Pipfile\\'" . conf-mode)
:init
(setq python-indent-guess-indent-offset-verbose nil
flycheck-flake8-maximum-line-length 120)
@ -333,13 +332,6 @@ The [[https://elpy.readthedocs.io/en/latest/introduction.html][Elpy Project]] ex
(setq elpy-modules (delq 'elpy-module-flymake elpy-modules)))
#+END_SRC
Elpy uses its *own* virtual environment for its backend process (jedi, rope, etc.), keeping elpy's own dependencies out of your project venvs. While there is a [[help:elpy-rpc-reinstall-virtualenv][elpy-rpc-reinstall-virtualenv]] function, I find it doesnt seem to install everything correctly, and I need to do this by hand:
#+BEGIN_SRC sh
source ~/.emacs.d/elpy/rpc-venv/bin/activate
pip install jedi black flake8 ipython
#+END_SRC
After weve loaded the Company section, we can add jedi to the list of completions:
#+BEGIN_SRC emacs-lisp

View file

@ -138,10 +138,10 @@ I appreciate calling =hi-lock-face-symbol-at-point= (or =highlight-symbol-at-poi
:config
(setq ahs-idle-interval 0.1)
(set-face-attribute ahs-face nil :foreground 'unspecified :background 'unspecified
(set-face-attribute ahs-face nil :foreground nil :background nil
:weight 'ultra-bold :slant 'italic)
(set-face-attribute ahs-plugin-default-face nil :foreground 'unspecified
:background 'unspecified :weight 'bold :slant 'normal))
(set-face-attribute ahs-plugin-default-face nil :foreground nil
:background nil :weight 'bold :slant 'normal))
#+end_src
Instead of calling =global-auto-highlight-symbol-mode=, we should just hook it to the =prog-mode=:
@ -370,7 +370,7 @@ Normally, you would need to add all the projects to directory clones in =~/src=
https://github.com/tree-sitter/tree-sitter-go
https://github.com/tree-sitter/tree-sitter-javascript
https://github.com/tree-sitter/tree-sitter-templ
https://github.com/tree-sitter-grammars/tree-sitter-yaml
https://github.com/ikatyang/tree-sitter-yaml
https://github.com/tree-sitter/tree-sitter-json
https://github.com/tree-sitter/tree-sitter-css
https://github.com/tree-sitter/tree-sitter-python
@ -422,7 +422,7 @@ In most cases,the =npm install= /usually/ works, but I may work on some sort of
#+end_src
At this point, we can now parse stuff using: =tree-sitter parse <source-code-file>=
**** Emacs Part
Note that Emacs already has the ability to download and install grammars, so following instructions from Mickey Petersens essay on [[https://www.masteringemacs.org/article/combobulate-structured-movement-editing-treesitter][using Tree-sitter with Combobulate]]:
However, Emacs already has the ability to download and install grammars, so following instructions from Mickey Petersens essay on [[https://www.masteringemacs.org/article/combobulate-structured-movement-editing-treesitter][using Tree-sitter with Combobulate]]:
#+begin_src emacs-lisp
(when (treesit-available-p)
(use-package treesit
@ -460,7 +460,7 @@ Note that Emacs already has the ability to download and install grammars, so fol
(toml "https://github.com/tree-sitter/tree-sitter-toml")
;; (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
;; (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
(yaml "https://github.com/tree-sitter-grammars/tree-sitter-yaml")))
(yaml "https://github.com/ikatyang/tree-sitter-yaml")))
(defun mp-setup-install-grammars ()
"Install Tree-sitter grammars if they are absent."

View file

@ -50,19 +50,16 @@ Will Schenk has [[https://willschenk.com/articles/2020/tramp_tricks/][a simple e
(tramp-remote-shell-args ("-i") ("-c"))))
tramp-methods)
(defun ha-tramp-completion-docker (orig-fun &rest args)
"Advice for `tramp-completion-handle-file-name-all-completions`.
Return active Docker container names when completing for `/docker:`."
(if (equal (nth 1 args) "/docker:")
(defadvice tramp-completion-handle-file-name-all-completions
(around dotemacs-completion-docker activate)
"(tramp-completion-handle-file-name-all-completions \"\" \"/docker:\" returns
a list of active Docker container names, followed by colons."
(if (equal (ad-get-arg 1) "/docker:")
(let* ((command "docker ps --format '{{.Names}}:'")
(dockernames-raw (shell-command-to-string command))
(dockernames (split-string dockernames-raw "\n" t)))
dockernames)
;; Call the original function
(apply orig-fun args)))
(advice-add 'tramp-completion-handle-file-name-all-completions
:around #'ha-tramp-completion-docker))
(dockernames (split-string dockernames-raw "\n")))
(setq ad-return-value dockernames))
ad-do-it)))
#+end_src
Keep in mind you need to /name/ your Docker session, with the =—name= option. I actually do more docker work on remote systems (as Docker seems to make my fans levitate my laptop over the desk). Granted, the =URL= is a bit lengthy, for instance:
@ -157,26 +154,6 @@ VTerm has an issue (at least for me) with ~M-Backspace~ not deleting the previou
#+end_src
The advantage of running terminals in Emacs is the ability to copy text without a mouse. For that, hit ~C-c C-t~ to enter a special copy-mode. If I go into this mode, I might as well also go into normal mode to move the cursor. To exit the copy-mode (and copy the selected text to the clipboard), hit ~Return~.
An odd use case I have: working in a Terminal session in one buffer window, and reading instructions in another. I want to copy commands from the document into the session using Avy to mark the command. Normally, I would use ~s-g~ to call [[file:ha-config.org::*Jump with Avy][avy-goto-char-timer]], and typing the /beginning of the command/, but before /jumping/, I press ~N~ to copy the line into the clipboard (kill-ring), and then /paste/ that into the Terminal using ~C-y~. One-too-many keystrokes.
Now, I can type: ~s-G~ (with the shift), select the line with Avy, and type ~N~ to have it copy/paste into the buffer:
#+BEGIN_SRC emacs-lisp
(use-package vterm
:config
(defun ha-vterm-avy-paste-line ()
"docstring"
(interactive)
(let ((buf (current-buffer)))
(call-interactively 'avy-goto-char-timer)
(when (equal buf (current-buffer))
(vterm-yank))))
(general-def 'vterm-mode-map "s-G" 'ha-vterm-avy-paste-line))
#+END_SRC
** Eat
While not as fast as [[https://github.com/akermu/emacs-libvterm][vterm]], the [[https://codeberg.org/akib/emacs-eat][Emulate a Terminal]] project (eat) is fast enough, and doesnt require a dedicate library that requires re-compilation. While offering [[https://elpa.nongnu.org/nongnu-devel/doc/eat.html][online documentation]], Im glad for an [[info:eat#Top][Info version]].

View file

@ -325,16 +325,15 @@ Lets make a /theme/:
`(window-divider ((t :foreground "black")))
`(cursor ((t (:foreground ,gray-10 :background ,cursor))))
`(region ((t (:background ,region))))
`(hl-line ((t (:background ,gray-20))))
`(mode-line ((t (:background ,active :foreground "white"))))
`(mode-line-active ((t (:background ,active))))
`(mode-line-inactive ((t (:background ,inactive))))
`(tab-bar ((t :foreground ,default-fg :background ,inactive :inherit variable-pitch)))
`(tab-bar ((t :foreground ,default-fg :background ,inactive)))
`(tab-line ((t :foreground ,default-fg :background ,inactive)))
`(tab-bar-tab ((t (:inherit variable-pitch :background ,active :weight bold))))
`(tab-bar-tab-inactive ((t (:inherit variable-pitch :background ,inactive :weight normal))))
`(tab-bar-tab ((t (:inherit variable-pitch :background ,active))))
`(tab-bar-tab-inactive ((t (:inherit variable-pitch :background ,inactive))))
`(doom-modeline-buffer-path ((t (:foreground ,almond))))
`(doom-modeline-buffer-file ((t (:foreground "white" :weight bold))))

View file

@ -2,7 +2,7 @@
#+author: Howard X. Abrams
#+date: 2025-11-24
#+filetags: emacs hamacs
#+lastmod: [2026-02-04 Wed]
#+lastmod: [2025-12-02 Tue]
A literate programming file for configuring Hammerspoon.
@ -79,11 +79,6 @@ To create special key bindings, I can:
hs.hotkey.bind({"alt", "ctrl", "shift"}, "M", function()
hs.execute("~/bin/emacs-capture-meeting")
end)
-- Current music system is actually in Emacs:
hs.hotkey.bind({"alt", "ctrl", "shift"}, "R", function()
hs.execute("/opt/homebrew/bin/emacsclient -s work -e '(ready-player-toggle-play-stop)'")
end)
#+END_SRC
* Zoom
Library extensions to Hammerspoon are called /spoons/, and most of these are a simple Lua script, =init.lua= stored in the =Spoons= subdirectory. We grab these with a =git clone=, typically. To use the Zoom spoon, clone it:

View file

@ -1,14 +1,8 @@
;; -*- mode:lisp; -*-
;;
;; Here is a keyboard setup for an Apple Macbook keyboard to work with
;; "home row mods" where holding down keys on the home row can act
;; like a modifier, so holding down `d` and hitting `p` results in a
;; capital `P` being entered.
;;
;; This also includes a dedicated Hyper and Mega modifier layers,
;; where holding down the `m' key and striking `a' creates a
;; Control+Shift+Option+a key combination. This is useful for fancy
;; hotkeys for Hammerspoon and the like.
;; "home row mods" where holding down keys on the home row can act like a
;; modifier, so holding down `d` and hitting `p` results in `P` being
;; entered.
;;
;; Installation:
;; brew install kanata
@ -49,7 +43,6 @@
;; the `h' or `g' keys.
(defalias
;; Home row mods
a (tap-hold $tap-time $hold-time a lmet)
s (tap-hold $tap-time $hold-time s lctl)
d (tap-hold $tap-time $hold-time d lsft)
@ -60,34 +53,7 @@
k (tap-hold $tap-time $hold-time k rsft)
l (tap-hold $tap-time $hold-time l rctl)
; (tap-hold $tap-time $hold-time ; rmet)
caps (tap-hold $tap-time $hold-time esc lctl)
;; ----------------------------------------------------
;; HYPER ALIASES (Shift + Ctrl + Option)
;; ----------------------------------------------------
hq (multi lsft lctl lalt q) hw (multi lsft lctl lalt w) he (multi lsft lctl lalt e) hr (multi lsft lctl lalt r) ht (multi lsft lctl lalt t)
hy (multi lsft lctl lalt y) hu (multi lsft lctl lalt u) hi (multi lsft lctl lalt i) ho (multi lsft lctl lalt o) hp (multi lsft lctl lalt p)
ha (multi lsft lctl lalt a) hs (multi lsft lctl lalt s) hd (multi lsft lctl lalt d) hf (multi lsft lctl lalt f) hg (multi lsft lctl lalt g)
hh (multi lsft lctl lalt h) hj (multi lsft lctl lalt j) hk (multi lsft lctl lalt k) hl (multi lsft lctl lalt l) h; (multi lsft lctl lalt ;)
hz (multi lsft lctl lalt z) hx (multi lsft lctl lalt x) hc (multi lsft lctl lalt c) hv (multi lsft lctl lalt v) hb (multi lsft lctl lalt b)
hn (multi lsft lctl lalt n) hm (multi lsft lctl lalt m) h, (multi lsft lctl lalt ,) h. (multi lsft lctl lalt .) h/ (multi lsft lctl lalt /)
;; ----------------------------------------------------
;; MEGA ALIASES (Shift + Ctrl + Option + Cmd)
;; ----------------------------------------------------
mq (multi lsft lctl lalt lmet q) mw (multi lsft lctl lalt lmet w) me (multi lsft lctl lalt lmet e) mr (multi lsft lctl lalt lmet r) mt (multi lsft lctl lalt lmet t)
my (multi lsft lctl lalt lmet y) mu (multi lsft lctl lalt lmet u) mi (multi lsft lctl lalt lmet i) mo (multi lsft lctl lalt lmet o) mp (multi lsft lctl lalt lmet p)
ma (multi lsft lctl lalt lmet a) ms (multi lsft lctl lalt lmet s) md (multi lsft lctl lalt lmet d) mf (multi lsft lctl lalt lmet f) mg (multi lsft lctl lalt lmet g)
mh (multi lsft lctl lalt lmet h) mj (multi lsft lctl lalt lmet j) mk (multi lsft lctl lalt lmet k) ml (multi lsft lctl lalt lmet l) m; (multi lsft lctl lalt lmet ;)
mz (multi lsft lctl lalt lmet z) mx (multi lsft lctl lalt lmet x) mc (multi lsft lctl lalt lmet c) mv (multi lsft lctl lalt lmet v) mb (multi lsft lctl lalt lmet b)
mn (multi lsft lctl lalt lmet n) mm (multi lsft lctl lalt lmet m) m, (multi lsft lctl lalt lmet ,) m. (multi lsft lctl lalt lmet .) m/ (multi lsft lctl lalt lmet /)
m (tap-hold $tap-time $hold-time m (layer-while-held HYPER))
c (tap-hold $tap-time $hold-time c (layer-while-held MEGA)))
caps (tap-hold $tap-time $hold-time esc lctl))
;; The base layer is fairly normal, except we all out aliases defined
;; above, os the @a is both a `tap-hold' feature as well as a regular
@ -98,7 +64,7 @@
` 1 2 3 4 5 6 7 8 9 0 - = bspc
tab q w e r t y u i o p [ ] \
@caps @a @s @d @f @g @h @j @k @l @; ' ret
lsft z x @c v b n @m , . / rsft
lsft z x c v b n m , . / rsft
@h lctl lalt lmet spc rmet ralt)
;; The other layer is our `symbols' which allows me to hold down the
@ -111,22 +77,3 @@
_ S-3 S-4 S-9 S-0 ` left down up right pgdn _ _
_ S-5 S-6 [ ] S-` F11 F12 F13 F14 F15 _
_ _ _ _ _ _ _)
;; The other layer is our `magic' which allows me to hold down the
;; `m' key to invoke a magical VI-like h/j/k/l arrow keys:
(deflayer HYPER
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _
_ @hq @hw @he @hr @ht @hy @hu @hi @ho @hp _ _ _
_ @ha @hs @hd @hf @hg @hh @hj @hk @hl @h; _ _
_ @hz @hx @hc @hv @hb @hn @hm _ _ _ _
_ _ _ _ _ _ _)
(deflayer MEGA
_ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _
_ @mq @mw @me @mr @mt @my @mu @mi @mo @mp _ _ _
_ @ma @ms @md @mf @mg @mh @mj @mk @ml @m; _ _
_ @mz @mx @mc @mv @mb @mn @mm _ _ _ _
_ _ _ _ _ _ _)