Compare commits

..

5 commits

Author SHA1 Message Date
Howard Abrams
a22c1a7ac7 Putting the 'company' back in
As this works better with LSP.
2026-01-02 14:29:23 -08:00
Howard Abrams
9b2c7abd4e Got tab-bar mode working as well as old perspective code 2026-01-02 14:27:57 -08:00
Howard Abrams
b9f593caba Minor bug fixes 2026-01-02 14:27:20 -08:00
Howard Abrams
bc5a1c8ea2 Details for python virtual environments
This also fixes a bug when using LSP for Python projects.
2026-01-02 14:25:13 -08:00
Howard Abrams
9f2eb65d06 Reformat the layout and comments for the kanata keyboard 2026-01-02 14:23:44 -08:00
9 changed files with 204 additions and 76 deletions

View file

@ -53,7 +53,7 @@ I would like a dedicate perspective to Mastodon, and I would like a leader key s
(use-package major-mode-hydra (use-package major-mode-hydra
:config :config
(major-mode-hydra-define mastodon-mode nil (major-mode-hydra-define mastodon-mode (:quit-key "q")
("Timelines" ("Timelines"
(("u" mastodon-tl-update "update") (("u" mastodon-tl-update "update")
("F" mastodon-tl-get-federated-timeline "Federated") ("F" mastodon-tl-get-federated-timeline "Federated")

View file

@ -585,12 +585,42 @@ Each programming environment might need some particular love. For instance:
#+END_SRC #+END_SRC
*** Company
The [[http://company-mode.github.io/][company project]] for completion back-ends work well with LSP.
Do I want it to display whenever there is a pause in the conversation (set =company-minimum-prefix-length= to a large number, or set =company-idle-delay=), or do I want it to only show when I push the =TAB= or other key (see =company-indent-or-complete-common=).
#+BEGIN_SRC emacs-lisp
(use-package company
:custom
(company-minimum-prefix-length 3) ; default
(company-idle-delay 0.5)
(company-tooltip-align-annotations t)
(company-tooltip-limit 9)
(company-tooltip-flip-when-above t)
(company-show-quick-access 'left)
:bind (("M-/" . company-complete)
:map company-mode-map
("M-/" . company-other-backend))
:config
(add-to-list 'company-backends 'company-yasnippet)
(set-face-attribute 'company-tooltip nil
:family "Cascadia Code NF"
:height 120)
:hook (after-init . global-company-mode))
#+END_SRC
Another idea, is I can trigger the company with a M-/ but then view /other backends/ by re-hitting that keybinding.
*** Corfu *** Corfu
The default completion system either inserts the first option directly in the text (without cycling, so lets hope it gets it right the first time), or presents choices in another buffer (who wants to hop to it to select an expansion). The default completion system either inserts the first option directly in the text (without cycling, so lets hope it gets it right the first time), or presents choices in another buffer (who wants to hop to it to select an expansion).
After using [[http://company-mode.github.io/][company]] for my completion back-end, I switched to [[https://github.com/minad/corfu][corfu]] as it works with the variable-spaced font of my org files (also see [[https://takeonrules.com/2022/01/17/switching-from-company-to-corfu-for-emacs-completion/][this essay]] for my initial motivation). After using [[http://company-mode.github.io/][company]] for my completion back-end, I switched to [[https://github.com/minad/corfu][corfu]] as it works with the variable-spaced font of my org files (also see [[https://takeonrules.com/2022/01/17/switching-from-company-to-corfu-for-emacs-completion/][this essay]] for my initial motivation).
#+begin_src emacs-lisp #+begin_src emacs-lisp :tangle no
(use-package corfu (use-package corfu
:custom :custom
(corfu-cycle t) (corfu-cycle t)
@ -598,6 +628,7 @@ After using [[http://company-mode.github.io/][company]] for my completion back-
:init :init
(global-corfu-mode)) (global-corfu-mode))
#+end_src #+end_src
*** Snippets *** Snippets
Using [[https://github.com/joaotavora/yasnippet][yasnippet]] to expand templates into text: Using [[https://github.com/joaotavora/yasnippet][yasnippet]] to expand templates into text:
@ -1036,7 +1067,8 @@ And some shortcut keys from the =general= project:
"<tab> p" '("new project" . ha-tab-bar-new-project) "<tab> p" '("new project" . ha-tab-bar-new-project)
"<tab> n" '("new space" . ha-tab-bar-new) "<tab> n" '("new space" . ha-tab-bar-new)
"<tab> u" '("update names" . ha-tab-bar-update-names) "<tab> u" '("update names" . ha-tab-bar-update-names)
"<tab> d" '("delete space" . ha-tab-bar-delete)) "<tab> d" '("delete space" . ha-tab-bar-delete)
"<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-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-prev-tab)
@ -1070,7 +1102,11 @@ I want to quickly jump, by the number shown on the tab, to that grouping. The fo
;; match the tab labels by incrementing it by one ... unless, ;; match the tab labels by incrementing it by one ... unless,
;; we are at 10, where we use 0 instead: ;; we are at 10, where we use 0 instead:
(format "<tab> %d" (if (= indx 9) 0 (1+ indx))) (format "<tab> %d" (if (= indx 9) 0 (1+ indx)))
`(,name . (lambda () (interactive) (tab-bar-select-tab ,(1+ indx)))))))) `(,name . (lambda () (interactive)
"Switch to a tab-bar by number"
(setq ha-tab-bar-previous
(tab-bar-tab-name-current))
(tab-bar-select-tab ,(1+ indx))))))))
#+END_SRC #+END_SRC
Any time I create or delete a new tab, we can call =ha-tab-bar-update-names=: Any time I create or delete a new tab, we can call =ha-tab-bar-update-names=:

View file

@ -132,7 +132,7 @@ Also, let's do some basic configuration of Emacs' mail system:
#+end_src #+end_src
Create a special mail perspective: Create a special mail perspective:
#+begin_src emacs-lisp #+begin_src emacs-lisp :tangle no
(ha-leader "a M" `("mail" . ,(ha-tab-bar-new "mail" #'notmuch))) (ha-leader "a M" `("mail" . ,(ha-tab-bar-new "mail" #'notmuch)))
#+end_src #+end_src
* Configuration * Configuration

View file

@ -27,7 +27,6 @@ A literate programming file for making Org file more readable.
#+end_src #+end_src
* Introduction * Introduction
I like having org-mode files look more like a word processor than having it look like programming code. But that is me. The end results: I like having org-mode files look more like a word processor than having it look like programming code. But that is me. The end results:
[[file:screenshots/org-as-word-processor.png]] [[file:screenshots/org-as-word-processor.png]]
* General Org Settings * General Org Settings
Since I use ellipsis in my writing… to /change/ how org renders a collapsed heading. Since I use ellipsis in my writing… to /change/ how org renders a collapsed heading.
@ -102,7 +101,7 @@ And hook this function to Org:
The list of things to try: The list of things to try:
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(setq org-hide-leading-stars nil) ; or t (setq org-hide-leading-stars t) ; or t
#+END_SRC #+END_SRC
** Markup View ** Markup View

View file

@ -136,7 +136,14 @@ Next, after reading David Vujics [[https://davidvujic.blogspot.com/2025/03/ar
# c.InteractiveShellApp.exec_lines = ['%autoreload 2'] # c.InteractiveShellApp.exec_lines = ['%autoreload 2']
#+END_SRC #+END_SRC
** Virtual Environment ** Isolated Python Environments
While the Python community (and my work at my company) had difficulty transitioning from Python 2 to 3, I often run into issues needing a particular Python version and modules. After playing around with different approaches, Im finding:
* Docker environments are nicely isolated, but annoying to work from outside the container
* The Builtin =venv= is works well for different library modules, but not for different versions
* The =pyenv= deals with different Python versions, but is overkill for library isolation
While the [[https://github.com/marcwebbie/auto-virtualenv][auto-virtualenv]] project attempts to resolve this, Im using the [[file:ha-programming.org::*Virtual Environments with direnv][direnv project]] abstraction for situations where I need project-specific isolation in more than just Python.
*** Virtual Environments
Use the built-in module, venv, to create isolated Python environments for specific projects, enabling you to manage dependencies separately. Use the built-in module, venv, to create isolated Python environments for specific projects, enabling you to manage dependencies separately.
Create a virtual environment, either in the projects directory, or in a global spot: Create a virtual environment, either in the projects directory, or in a global spot:
@ -160,8 +167,8 @@ Now, do what you need to do with this isolation:
#+BEGIN_SRC sh :tangle no #+BEGIN_SRC sh :tangle no
pip install -r test-requirements.txt pip install -r test-requirements.txt
#+END_SRC #+END_SRC
** Virtual Environment with new Python Version *** Managing Python Versions
Pyenv is a tool for managing multiple versions of Python on your machine, allowing you to switch between them easily. On a Mac, installed it via Homebrew: [[https://github.com/pyenv/pyenv][Pyenv]] is a tool for managing multiple versions of Python on your machine, allowing you to switch between them easily (see [[https://realpython.com/intro-to-pyenv/][this essay]]). On a Mac, installed it via Homebrew:
#+BEGIN_SRC sh #+BEGIN_SRC sh
brew install readline xz brew install readline xz
@ -213,16 +220,55 @@ Also, you need the following in your =~/.config/direnv/direnvrc= file (which I h
fi fi
} }
#+end_src #+end_src
** Editing Python Code
Lets integrate this [[https://github.com/wbolster/evil-text-object-python][Python support for evil-text-object]] project: Tell Emacs about [[https://github.com/pythonic-emacs/pyenv-mode][pyenv-mode]]:
#+begin_src emacs-lisp
(when (fboundp 'evil-define-text-object) #+BEGIN_SRC emacs-lisp
(use-package evil-text-object-python (use-package pyenv-mode
:hook (python-mode . evil-text-object-python-add-bindings))) :config
#+end_src (defun setup-pyenv ()
This allows me to delete a Python “block” using ~dal~. "Pyenv."
** Docker Environment (setenv "WORKON_HOME" "~/.pyenv/versions")
Docker really allows you to isolate your project's environment. The downside is that you are using Docker and probably a bloated container. On my work laptop, a Mac, this creates a behemoth virtual machine that immediately spins the fans like a wind tunnel. (pyenv-mode +1)))
#+END_SRC
Now specify the =pyenv= Python version by calling [[help:pyenv-mode-set][pyenv-mode-set]]:
#+begin_example
M-x pyenv-mode-set
#+end_example
When you run inferior Python processes (like =run-python=), the process will start inside the specified Python installation. You can unset the current version with:
#+begin_example
M-x pyenv-mode-unset
#+end_example
Or, we can do it automatically when we get into a project (if the project has a =.python-version= file):
#+BEGIN_SRC emacs-lisp
(use-package pyenv-mode
:config
(defun project-pyenv-mode-set (&rest _)
"Set pyenv version matching project name."
(let* ((filename (thread-first
(project-current)
(project-root)
(file-name-concat ".python-version")))
(version (when (file-exists-p filename)
(with-temp-buffer
(insert-file-contents filename)
(buffer-string)))))
(when version
(pyenv-mode-set version)
(pyenv-mode-unset))))
;; Either set/unset the pyenv version whenever changing tabs:
(add-hook 'tab-bar-tab-post-select-functions 'project-pyenv-mode-set))
#+END_SRC
*** Docker Environment
Docker allows you to isolate your project's environment. The downside is that you are using Docker and probably a bloated container. On my work laptop, a Mac, this creates a behemoth virtual machine that immediately spins the fans like a wind tunnel.
But, but... think of the dependencies! But, but... think of the dependencies!
@ -236,6 +282,14 @@ Your project's =.envrc= file would contain something like:
container_layout container_layout
#+end_src #+end_src
** Editing Python Code
Lets integrate this [[https://github.com/wbolster/evil-text-object-python][Python support for evil-text-object]] project:
#+begin_src emacs-lisp
(when (fboundp 'evil-define-text-object)
(use-package evil-text-object-python
:hook (python-mode . evil-text-object-python-add-bindings)))
#+end_src
This allows me to delete a Python “block” using ~dal~.
** Unit Tests ** Unit Tests
#+begin_src emacs-lisp #+begin_src emacs-lisp
(use-package python-pytest (use-package python-pytest
@ -267,9 +321,22 @@ The [[https://elpy.readthedocs.io/en/latest/introduction.html][Elpy Project]] ex
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package elpy (use-package elpy
:ensure t
:init :init
(elpy-enable)) (advice-add 'python-mode :before 'elpy-enable)
:config
;; (elpy-enable)
(setq elpy-test-runner 'elpy-test-pytest-runner)
(setq elpy-formatter 'black)
(setq elpy-shell-echo-input nil)
(setq elpy-modules (delq 'elpy-module-flymake elpy-modules)))
#+END_SRC
After weve loaded the Company section, we can add jedi to the list of completions:
#+BEGIN_SRC emacs-lisp
(use-package elpy
:after company-mode
:config (add-to-list 'company-backends 'company-jedi))
#+END_SRC #+END_SRC
Lets expand our =major-mode-hydra= with some extras: Lets expand our =major-mode-hydra= with some extras:

View file

@ -670,26 +670,26 @@ Emacs has two LSP projects, and while I have used [[LSP Mode]], but since I don
:config :config
(global-set-key (kbd "s-m") 'lsp) (global-set-key (kbd "s-m") 'lsp)
(ha-local-leader :keymaps 'prog-mode-map ;; (ha-local-leader :keymaps 'prog-mode-map
"w" '(:ignore t :which-key "lsp") ;; "w" '(:ignore t :which-key "lsp")
"l" '(:ignore t :which-key "lsp") ;; "l" '(:ignore t :which-key "lsp")
"ws" '("start" . lsp)) ;; "ws" '("start" . lsp))
;; The following leader-like keys, are only available when I have ;; ;; The following leader-like keys, are only available when I have
;; started LSP, and is an alternate to Command-m: ;; ;; started LSP, and is an alternate to Command-m:
:general ;; :general
(:states 'normal :keymaps 'lsp-mode-map ;; (:states 'normal :keymaps 'lsp-mode-map
", w r" '("restart" . lsp-reconnect) ;; ", w r" '("restart" . lsp-reconnect)
", w b" '("events" . lsp-events-buffer) ;; ", w b" '("events" . lsp-events-buffer)
", w e" '("errors" . lsp-stderr-buffer) ;; ", w e" '("errors" . lsp-stderr-buffer)
", w q" '("quit" . lsp-shutdown) ;; ", w q" '("quit" . lsp-shutdown)
", w Q" '("quit all" . lsp-shutdown-all) ;; ", w Q" '("quit all" . lsp-shutdown-all)
", l r" '("rename" . lsp-rename) ;; ", l r" '("rename" . lsp-rename)
", l f" '("format" . lsp-format) ;; ", l f" '("format" . lsp-format)
", l a" '("actions" . lsp-code-actions) ;; ", l a" '("actions" . lsp-code-actions)
", l i" '("imports" . lsp-code-action-organize-imports) ;; ", l i" '("imports" . lsp-code-action-organize-imports)
", l d" '("doc" . lsp-lookup-documentation)) ;; ", l d" '("doc" . lsp-lookup-documentation))
:hook ((lsp-mode . lsp-enable-which-key-integration))) :hook ((lsp-mode . lsp-enable-which-key-integration)))
#+end_src #+end_src

View file

@ -1,34 +1,46 @@
;; -*- mode:lisp; -*-
;; Here is a keyboard setup for an Apple Macbook keyboard to work with ;; 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 ;; "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 ;; modifier, so holding down `d` and hitting `p` results in `P` being
;; entered. ;; entered.
;;
;; Installation:
;; brew install kanata
;;
;; Running:
;; sudo kanata --cfg ~/src/hamacs/laptop_keyboard.kbd
(deflocalkeys-macos (deflocalkeys-macos
ì 13 ì 13)
)
;; Also includes a symbol layer accessible while holding down either ;; While this works on both my Macbook running MacOS and Linux, I have
;; the `h' or `g' keys. ;; yet to figure out how to get the same file to work in both, as I
;; have to modify the code in the `defcfg' section below.
(defcfg (defcfg
macos-dev-names-include ("Apple Internal Keyboard / Trackpad") ;; linux-dev-names-include ("Apple Internal Keyboard / Trackpad")
;; linux-dev-names-include ( macos-dev-names-include ("Apple Internal Keyboard / Trackpad"))
;; "Microsoft Surface 045E:091C Keyboard"
;; ) ;; What keys are we re-defining? This is my Macbook Pro layouts, as
) ;; both laptops are this hardware.
;;
;; Notice lmet/rmet = Apple's Command Key
;; lalt/ralt = Option, or a real meta key
(defsrc (defsrc
esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12
` 1 2 3 4 5 6 7 8 9 0 - = bspc ` 1 2 3 4 5 6 7 8 9 0 - = bspc
tab q w e r t y u i o p [ ] \ tab q w e r t y u i o p [ ] \
caps a s d f g h j k l ; ' ret 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
fn lctl lalt lmet spc rmet ralt fn lctl lalt lmet spc rmet ralt)
)
(defvar (defvar
tap-time 200 tap-time 200
hold-time 200 hold-time 200)
)
;; Also includes a symbol layer accessible while holding down either
;; the `h' or `g' keys.
(defalias (defalias
a (tap-hold $tap-time $hold-time a lmet) a (tap-hold $tap-time $hold-time a lmet)
@ -41,23 +53,27 @@
k (tap-hold $tap-time $hold-time k rsft) k (tap-hold $tap-time $hold-time k rsft)
l (tap-hold $tap-time $hold-time l rctl) l (tap-hold $tap-time $hold-time l rctl)
; (tap-hold $tap-time $hold-time ; rmet) ; (tap-hold $tap-time $hold-time ; rmet)
caps (tap-hold $tap-time $hold-time esc lctl) 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
;; `a` key:
(deflayer base (deflayer base
esc F1 F2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12
` 1 2 3 4 5 6 7 8 9 0 - = bspc ` 1 2 3 4 5 6 7 8 9 0 - = bspc
tab q w e r t y u i o p [ ] \ tab q w e r t y u i o p [ ] \
@caps @a @s @d @f @g @h @j @k @l @; ' ret @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 @h lctl lalt lmet spc rmet ralt)
)
;; The other layer is our `symbols' which allows me to hold down the
;; `g' key to invoke a magical VI-like h/j/k/l arrow keys:
(deflayer SYMBOLS (deflayer SYMBOLS
esc 🔅 🔆 F3 F4 f5 f16 ◀◀ ▶⏸ ▶▶ 🔇 🔉 🔊 esc 🔅 🔆 F3 F4 f5 f16 ◀◀ ▶⏸ ▶▶ 🔇 🔉 🔊
_ f11 f12 f13 f14 f15 f16 f17 f18 f19 f20 _ _ _ _ f11 f12 f13 f14 f15 f16 f17 f18 f19 f20 _ _ _
_ S-1 S-2 { } S-\ F16 F17 F18 F19 pgup _ _ _ _ S-1 S-2 { } S-\ F16 F17 F18 F19 pgup _ _ _
_ S-3 S-4 S-9 S-0 ` left down up right pgdn _ _ _ S-3 S-4 S-9 S-0 ` left down up right pgdn _ _
_ S-5 S-6 [ ] S-` F11 F12 F13 F14 F15 _ _ S-5 S-6 [ ] S-` F11 F12 F13 F14 F15 _
_ _ _ _ _ _ _ _ _ _ _ _ _ _)
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

After

Width:  |  Height:  |  Size: 449 KiB

View file

@ -416,15 +416,25 @@ Favorite feature is the [[https://iterm2.com/documentation-status-bar.html][Stat
Currently, I show the currently defined Kube namespace. Currently, I show the currently defined Kube namespace.
#+BEGIN_SRC zsh #+BEGIN_SRC zsh
function iterm2_python_version() {
echo $(pyenv version-name):$(echo "$VIRTUAL_ENV" | sed "
s|^$HOME|~|
s|^~/src/wpc-gerrit.inday.io/||
s|^~/work/||
s|^~/.venv/||
s|/\.venv$||
s|\.venv$||")
}
function iterm2_print_user_vars() { function iterm2_print_user_vars() {
# iterm2_set_user_var kubecontext $($ yq '.users[0].name' ~/.kube/config):$(kubectl config view --minify --output 'jsonpath={..namespace}') # iterm2_set_user_var kubecontext $($ yq '.users[0].name' ~/.kube/config):$(kubectl config view --minify --output 'jsonpath={..namespace}')
# Correct version: # Correct version:
# iterm2_set_user_var kubecontext $(kubectl config current-context):$(kubectl config view --minify --output 'jsonpath={..namespace}') # iterm2_set_user_var kubecontext $(kubectl config current-context):$(kubectl config view --minify --output 'jsonpath={..namespace}')
# Faster version: # Faster version:
iterm2_set_user_var kubecontext $(awk '/^current-context:/{print $2;exit;}' <~/.kube/config) iterm2_set_user_var kubecontext $(awk '/^current-context:/{print $2;exit;}' <~/.kube/config)
iterm2_set_user_var pycontext "$(pyenv version-name):$(echo $VIRTUAL_ENV | sed 's/.*.venv\///')" iterm2_set_user_var pycontext $(iterm2_python_version)
} }
#+END_SRC #+END_SRC