Compare commits
	
		
			5 commits
		
	
	
		
			281e45431a
			...
			a1118e0c2e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						a1118e0c2e | ||
| 
							 | 
						9bb159c087 | ||
| 
							 | 
						de618dfde7 | ||
| 
							 | 
						e7e61b4134 | ||
| 
							 | 
						65ff19ba32 | 
					 9 changed files with 255 additions and 167 deletions
				
			
		| 
						 | 
				
			
			@ -26,9 +26,9 @@ If I want to build from source (and not build from Homebrew), install all the de
 | 
			
		|||
    libtiff jansson libpng librsvg gnutls cmake
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
To get the native compilation for Emacs working, install:
 | 
			
		||||
To get the native compilation for Emacs working, I [[https://github.com/d12frosted/homebrew-emacs-plus/issues/680#issuecomment-2481633820][need to *un*-install]], as the new brew recipe will take care of this:
 | 
			
		||||
#+begin_src sh
 | 
			
		||||
  brew install libgccjit
 | 
			
		||||
  brew uninstall libgccjit gcc
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
Oh, and if we are still building with [[https://imagemagick.org/][ImageMagick]], install that first:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,7 +98,8 @@
 | 
			
		|||
 | 
			
		||||
(defun org-confluence-checkbox (checkbox info)
 | 
			
		||||
  "Format CHECKBOX into Confluence (even though Confluence won't take it.  We take the CHECKBOX to be either 'on' or 'off' and we ignore INFO."
 | 
			
		||||
  (case checkbox (on "(/)")
 | 
			
		||||
  (cl-case checkbox
 | 
			
		||||
    (on "(/)")
 | 
			
		||||
    (off "(x)")
 | 
			
		||||
    (trans "(-)")
 | 
			
		||||
    (t "")))
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +108,7 @@
 | 
			
		|||
  "Wrap a list with the html-ish stuff, but only if descriptive."
 | 
			
		||||
  (let* (arg1 ;; (assoc :counter (org-element-map plain-list 'item
 | 
			
		||||
         (type (org-element-property :type plain-list)))
 | 
			
		||||
    (case type
 | 
			
		||||
    (cl-case type
 | 
			
		||||
      (descriptive (format "{html}<dl>\n%s\n</dl>{html}" contents))
 | 
			
		||||
      (otherwise   contents))))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +121,7 @@
 | 
			
		|||
        (br "\n\n"))
 | 
			
		||||
    (message "Got %s with %s" type contents)
 | 
			
		||||
    (concat
 | 
			
		||||
     (case type
 | 
			
		||||
     (cl-case type
 | 
			
		||||
       (ordered
 | 
			
		||||
        (let* ((counter term-counter-id)
 | 
			
		||||
               (extra (if counter (format " value=\"%s\"" counter) "")))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,20 @@ I get issues with Magic and Dimmer, so let’s turn off this feature in certain
 | 
			
		|||
#+end_src
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
* Find the Bloody Cursor
 | 
			
		||||
** 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.
 | 
			
		||||
 | 
			
		||||
#+BEGIN_SRC emacs-lisp
 | 
			
		||||
  (use-package ultra-scroll
 | 
			
		||||
    :straight (:type git :host github :repo "jdtsmith/ultra-scroll")
 | 
			
		||||
    :config
 | 
			
		||||
    (setq scroll-conservatively 101 ; important!
 | 
			
		||||
        scroll-margin 0)
 | 
			
		||||
    (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+=, 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,9 @@ I like =debug-on-error=, but not in Eshell, so I set this up when entering Eshel
 | 
			
		|||
    (setq mode-line-format nil))
 | 
			
		||||
#+end_src
 | 
			
		||||
** Directory Notification
 | 
			
		||||
:PROPERTIES:
 | 
			
		||||
:ID:       AD963BB5-6346-46C0-A364-4C8AEBA29882
 | 
			
		||||
:END:
 | 
			
		||||
While most people put the “current directory” in their shell prompt, I’ve always found that pretty distracting, difficult when copy/pasting commands, and cuts out on the length of my commands (which might be a good thing, tbh).
 | 
			
		||||
 | 
			
		||||
I use the =header-line= for this.
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +158,7 @@ Keep in mind that the predicates are somewhat finicky. For instance, the followi
 | 
			
		|||
  mv $f $f(:r).txt
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
But you could call this:
 | 
			
		||||
Instead, call this:
 | 
			
		||||
 | 
			
		||||
#+begin_src sh
 | 
			
		||||
  mv $f $f(:s/org$/txt/)
 | 
			
		||||
| 
						 | 
				
			
			@ -1247,23 +1250,6 @@ I use =ex= to refer to both =en= / =ec=. Use cases:
 | 
			
		|||
 | 
			
		||||
The =-c= option can be combined with the /command/, but I don’t want it to grab the last output, as I think I would just like to send text to the notebook as after thoughts. If the option to =-c= is blank, perhaps it just calls the capture template to allow me to enter voluminous content.
 | 
			
		||||
 | 
			
		||||
This requires capture templates that don’t do any formatting. I will reused =c c= from [[file:ha-capturing-notes.org::*General Notes][capturing-notes]] code, and create other templates under =e= prefix:
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  ;; (setq org-capture-templates nil)
 | 
			
		||||
  (add-to-list 'org-capture-templates
 | 
			
		||||
               '("e" "Engineering Notebook"))
 | 
			
		||||
 | 
			
		||||
  (add-to-list 'org-capture-templates
 | 
			
		||||
               '("ee" "Notes and Commentary" plain
 | 
			
		||||
                 (file+olp+datetree org-default-notes-file "General Notes")
 | 
			
		||||
                 "%i" :empty-lines 1 :tree-type month :unnarrowed t))
 | 
			
		||||
 | 
			
		||||
  (add-to-list 'org-capture-templates
 | 
			
		||||
               '("ef" "Piped-in Contents" plain
 | 
			
		||||
                 (file+olp+datetree org-default-notes-file "General Notes")
 | 
			
		||||
                 "%i" :immediate-finish t :empty-lines 1 :tree-type month))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun ha-eshell-engineering-notebook (capture-template args)
 | 
			
		||||
    "Capture commands and output from Eshell into an Engineering Notebook.
 | 
			
		||||
| 
						 | 
				
			
			@ -1328,7 +1314,33 @@ This requires capture templates that don’t do any formatting. I will reused =c
 | 
			
		|||
      ;; Return output from the command, or nothing if there wasn't anything:
 | 
			
		||||
      (or output "")))
 | 
			
		||||
#+end_src
 | 
			
		||||
And now we have a =en= and a =ec= version:
 | 
			
		||||
 | 
			
		||||
This requires capture templates that don’t do any formatting. I will reused =c c= from [[file:ha-capturing-notes.org::*General Notes][capturing-notes]] code, and create other templates under =e= prefix:
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (add-to-list 'org-capture-templates '("e" "Engineering Notebook"))
 | 
			
		||||
 | 
			
		||||
  (add-to-list 'org-capture-templates
 | 
			
		||||
               '("ee" "Notes and Commentary" plain
 | 
			
		||||
                 (file+olp+datetree org-default-notes-file "General Notes")
 | 
			
		||||
                 "%i" :empty-lines 1 :tree-type month :unnarrowed t))
 | 
			
		||||
 | 
			
		||||
  (add-to-list 'org-capture-templates
 | 
			
		||||
               '("ef" "Piped-in Contents" plain
 | 
			
		||||
                 (file+olp+datetree org-default-notes-file "General Notes")
 | 
			
		||||
                 "%i" :immediate-finish t :empty-lines 1 :tree-type month))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
The =cap= function simply calls [[help-org-capture][org-capture]] with [[info:org#Template elements][a template]]:
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun eshell/cap (&rest args)
 | 
			
		||||
    "Call `org-capture' with the `ee' template to enter text into the engineering notebook."
 | 
			
		||||
    (org-capture nil "ee"))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
The =en= and =ec= functions, since they do /pre-processing/ before calling =org-capture= calls some helper functions:
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun eshell/en (&rest args)
 | 
			
		||||
    "Call `ha-eshell-engineering-notebook' to \"General Notes\"."
 | 
			
		||||
| 
						 | 
				
			
			@ -1341,12 +1353,7 @@ And now we have a =en= and a =ec= version:
 | 
			
		|||
    (ha-eshell-engineering-notebook "cc" args))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
This function simply calls [[help-org-capture][org-capture]] with [[info:org#Template elements][a template]]:
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun eshell/cap (&rest args)
 | 
			
		||||
    "Call `org-capture' with the `ee' template to enter text into the engineering notebook."
 | 
			
		||||
    (org-capture nil "ee"))
 | 
			
		||||
#+end_src
 | 
			
		||||
In order to have /devices/, for instance =some-command > e=, we define some functions:
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun ha-eshell-target-engineering-notebook (output)
 | 
			
		||||
| 
						 | 
				
			
			@ -1357,7 +1364,9 @@ This function simply calls [[help-org-capture][org-capture]] with [[info:org#Tem
 | 
			
		|||
    "Write OUTPUT into the current clocked in task via `org-capture'."
 | 
			
		||||
    (ha-eshell-engineering-capture "cc" nil nil output))
 | 
			
		||||
#+end_src
 | 
			
		||||
And finally, add our new functions to [[elisp(describe-variable 'eshell-virtual-targets)][eshell-virtual-targets]]:
 | 
			
		||||
 | 
			
		||||
And add these functions to [[elisp(describe-variable 'eshell-virtual-targets)][eshell-virtual-targets]], after =eshell= has been loaded:
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (with-eval-after-load "eshell"
 | 
			
		||||
    (add-to-list 'eshell-virtual-targets '("/dev/e" ha-eshell-target-engineering-notebook nil))
 | 
			
		||||
| 
						 | 
				
			
			@ -1505,8 +1514,11 @@ Now tie it all together with a prompt function can color each of the prompts com
 | 
			
		|||
 | 
			
		||||
  (setq-default eshell-prompt-function #'eshell/eshell-local-prompt-function)
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
Here is the result:
 | 
			
		||||
[[http://imgur.com/nkpwII0.png]]
 | 
			
		||||
[[file:screenshots/eshell-special-prompt.png]]
 | 
			
		||||
 | 
			
		||||
To be honest, this sort of prompt is garish and distracting. A better approach is to have that information in either the mode line, or a [[Directory Notification][header-line]].
 | 
			
		||||
** Simple Prompt with Mode Line
 | 
			
		||||
To achieve more /screen estate/, leave your prompt simple:
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
| 
						 | 
				
			
			@ -1615,6 +1627,17 @@ And let’s bind it:
 | 
			
		|||
#+begin_src emacs-lisp
 | 
			
		||||
  (bind-key "C-!" 'eshell-here)
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
Obviously, I usually want to open a shell in the root of the current project.
 | 
			
		||||
 | 
			
		||||
#+BEGIN_SRC emacs-lisp
 | 
			
		||||
  (defun eshell-project ()
 | 
			
		||||
    "Call `eshell-there' with the current project root."
 | 
			
		||||
    (interactive)
 | 
			
		||||
    (eshell-there (or (project-root (project-current))
 | 
			
		||||
                      default-directory)))
 | 
			
		||||
    #+END_SRC
 | 
			
		||||
 | 
			
		||||
** Shell Over There
 | 
			
		||||
Would be nice to be able to run an eshell session and use Tramp to connect to the remote host in one fell swoop:
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -892,7 +892,7 @@ Since the /definitions/ do not work, so let's use the [[https://github.com/abo-a
 | 
			
		|||
    :bind ("s-d" . define-word-at-point)
 | 
			
		||||
    :config
 | 
			
		||||
    (ha-leader :keymaps 'text-mode-map
 | 
			
		||||
      "s d"  '(:ignore t :which-key "thesaurus")
 | 
			
		||||
      "s d"  '(:ignore t :which-key "dictionary")
 | 
			
		||||
      "s d d" '("define this word" . define-word-at-point)
 | 
			
		||||
      "s d a" '("define any word" . define-word)))
 | 
			
		||||
#+end_src
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -337,7 +337,7 @@ All the above loveliness can be easily accessible with a [[https://github.com/je
 | 
			
		|||
#+begin_src emacs-lisp
 | 
			
		||||
  (use-package major-mode-hydra
 | 
			
		||||
    :config
 | 
			
		||||
    (major-mode-hydra-define emacs-lisp-mode (:quit-key "q" :color pink)
 | 
			
		||||
    (major-mode-hydra-define emacs-lisp-mode (:quit-key "q" :color blue)
 | 
			
		||||
      ("Evaluating"
 | 
			
		||||
       (("e" ha-eval-current-expression "Current")
 | 
			
		||||
        ("d" lispy-debug/body "Debugging")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,6 +49,53 @@ Note: Install the following checks:
 | 
			
		|||
  pip install flake8 pylint pyright mypy pycompile
 | 
			
		||||
#+end_src
 | 
			
		||||
Or better yet, add those to the =requirements-dev.txt= file.
 | 
			
		||||
 | 
			
		||||
All the above loveliness can be easily accessible with a [[https://github.com/jerrypnz/major-mode-hydra.el][major-mode-hydra]] defined for =emacs-lisp-mode=:
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
    (use-package major-mode-hydra
 | 
			
		||||
      :config
 | 
			
		||||
      (defvar ha-python-eval-title (font-icons 'mdicon "run" :title "Python Evaluation"))
 | 
			
		||||
      (defvar ha-python-goto-title (font-icons 'faicon "python" :title "Python Symbol References"))
 | 
			
		||||
 | 
			
		||||
      (pretty-hydra-define python-evaluate (:color blue :quit-key "q"
 | 
			
		||||
                                            :title ha-python-eval-title)
 | 
			
		||||
        ("Section"
 | 
			
		||||
         (("f" python-shell-send-defun "Function/Class")
 | 
			
		||||
          ("e" python-shell-send-statement "Line")
 | 
			
		||||
          (";" python-shell-send-string "Expression"))
 | 
			
		||||
         "Entirety"
 | 
			
		||||
         (("F" python-shell-send-file "File")
 | 
			
		||||
          ("B" python-shell-send-buffer "Buffer")
 | 
			
		||||
          ("r" python-shell-send-region "Region"))))
 | 
			
		||||
 | 
			
		||||
      (pretty-hydra-define python-goto (:color blue :quit-key "q"
 | 
			
		||||
                                        :title ha-python-goto-title)
 | 
			
		||||
        ("Symbols"
 | 
			
		||||
         (("s" xref-find-apropos "Find Symbol")
 | 
			
		||||
          ("e" python-shell-send-statement "Line")
 | 
			
		||||
          (";" python-shell-send-string "Expression"))
 | 
			
		||||
         "Entirety"
 | 
			
		||||
         (("F" python-shell-send-file "File")
 | 
			
		||||
          ("B" python-shell-send-buffer "Buffer")
 | 
			
		||||
          ("r" python-shell-send-region "Region"))))
 | 
			
		||||
 | 
			
		||||
      (major-mode-hydra-define python-mode (:quit-key "q" :color blue)
 | 
			
		||||
        ("Server"
 | 
			
		||||
         (("S" run-python "Start Server")
 | 
			
		||||
          ("s" python-shell-switch-to-shell "Go to Server"))
 | 
			
		||||
         "Edit"
 | 
			
		||||
         (("r" iedit-mode "Rename")
 | 
			
		||||
          (">" python-indent-shift-left "Shift Left")
 | 
			
		||||
          ("<" python-indent-shift-right "Shift Right"))
 | 
			
		||||
         "Navigate/Eval"
 | 
			
		||||
         (("e" python-evaluate/body "Evaluate...")
 | 
			
		||||
          ("g" python-goto/body "Go to..."))
 | 
			
		||||
         "Docs"
 | 
			
		||||
         (("d" python-eldoc-at-point "Docs on Symbol")
 | 
			
		||||
          ("D" python-describe-at-point "Describe Symbol")))))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
** Virtual Environment
 | 
			
		||||
For a local virtual machine, put the following in your =.envrc= file:
 | 
			
		||||
#+begin_src conf
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +115,7 @@ use python 3.7.1
 | 
			
		|||
 | 
			
		||||
Also, you need the following in your =~/.config/direnv/direnvrc= file (which I have):
 | 
			
		||||
#+begin_src shell
 | 
			
		||||
use_python() {
 | 
			
		||||
  use_python() {
 | 
			
		||||
    local python_root=$(pyenv root)/versions/$1
 | 
			
		||||
    load_prefix "$python_root"
 | 
			
		||||
    if [[ -x "$python_root/bin/python" ]]; then
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +124,7 @@ use_python() {
 | 
			
		|||
      echo "Error: $python_root/bin/python can't be executed."
 | 
			
		||||
      exit
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
  }
 | 
			
		||||
#+end_src
 | 
			
		||||
** Editing Python Code
 | 
			
		||||
Let’s integrate this [[https://github.com/wbolster/evil-text-object-python][Python support for evil-text-object]] project:
 | 
			
		||||
| 
						 | 
				
			
			@ -96,11 +143,11 @@ Enough of the rant (I go back and forth), after getting Docker installed and run
 | 
			
		|||
 | 
			
		||||
Your project's =.envrc= file would contain something like:
 | 
			
		||||
#+begin_src shell
 | 
			
		||||
CONTAINER_NAME=my-docker-container
 | 
			
		||||
CONTAINER_WRAPPERS=(python3 pip3 yamllint)
 | 
			
		||||
CONTAINER_EXTRA_ARGS="--env SOME_ENV_VAR=${SOME_ENV_VAR}"
 | 
			
		||||
  CONTAINER_NAME=my-docker-container
 | 
			
		||||
  CONTAINER_WRAPPERS=(python3 pip3 yamllint)
 | 
			
		||||
  CONTAINER_EXTRA_ARGS="--env SOME_ENV_VAR=${SOME_ENV_VAR}"
 | 
			
		||||
 | 
			
		||||
container_layout
 | 
			
		||||
  container_layout
 | 
			
		||||
#+end_src
 | 
			
		||||
** Unit Tests
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
| 
						 | 
				
			
			@ -108,21 +155,31 @@ container_layout
 | 
			
		|||
    :after python
 | 
			
		||||
    :commands python-pytest-dispatch
 | 
			
		||||
    :init
 | 
			
		||||
    (ha-local-leader :keymaps 'python-mode-map
 | 
			
		||||
      "t" '(:ignore t :which-key "tests")
 | 
			
		||||
      "t a" '("all" . python-pytest)
 | 
			
		||||
      "t f" '("file dwim" . python-pytest-file-dwim)
 | 
			
		||||
      "t F" '("file" . python-pytest-file)
 | 
			
		||||
      "t t" '("function-dwim" . python-pytest-function-dwim)
 | 
			
		||||
      "t T" '("function" . python-pytest-function)
 | 
			
		||||
      "t r" '("repeat" . python-pytest-repeat)
 | 
			
		||||
      "t p" '("dispatch" . python-pytest-dispatch)))
 | 
			
		||||
    (use-package major-mode-hydra
 | 
			
		||||
      :config
 | 
			
		||||
      (defvar ha-python-tests-title (font-icons 'devicon "pytest" :title "Python Test Framework"))
 | 
			
		||||
      (pretty-hydra-define python-tests (:color blue :quit-key "q"
 | 
			
		||||
                                                :title ha-python-tests-title)
 | 
			
		||||
        ("Suite"
 | 
			
		||||
         (("a" python-pytest "All")
 | 
			
		||||
          ("f" python-pytest-file-dwim "File DWIM")
 | 
			
		||||
          ("F" python-pytest-file "File"))
 | 
			
		||||
         "Specific"
 | 
			
		||||
         (("d" python-pytest-function-dwim "Function DWIM")
 | 
			
		||||
          ("D" python-pytest-function "Function"))
 | 
			
		||||
         "Again"
 | 
			
		||||
         (("r" python-pytest-repeat "Repeat tests")
 | 
			
		||||
          ("p" python-pytest-dispatch "Dispatch"))))
 | 
			
		||||
 | 
			
		||||
      (major-mode-hydra-define+ python-mode (:quit-key "q" :color blue)
 | 
			
		||||
        ("Misc"
 | 
			
		||||
         (("t" python-tests/body "Tests..."))))))
 | 
			
		||||
#+end_src
 | 
			
		||||
** Python Dependencies
 | 
			
		||||
Each Python project's =requirements-dev.txt= file would reference the [[https://pypi.org/project/python-lsp-server/][python-lsp-server]] (not the /unmaintained/ project, =python-language-server=):
 | 
			
		||||
 | 
			
		||||
#+begin_src conf :tangle no
 | 
			
		||||
python-lsp-server[all]
 | 
			
		||||
  python-lsp-server[all]
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
*Note:* This does mean, you would have a =tox.ini= with this line:
 | 
			
		||||
| 
						 | 
				
			
			@ -144,114 +201,99 @@ python-lsp-server[all]
 | 
			
		|||
*** Pyright
 | 
			
		||||
I’m using the Microsoft-supported [[https://github.com/Microsoft/pyright][pyright]] package instead. Adding this to my =requirements.txt= files:
 | 
			
		||||
#+begin_src conf :tangle no
 | 
			
		||||
pyright
 | 
			
		||||
  pyright
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
The [[https://github.com/emacs-lsp/lsp-pyright][pyright package]] works with LSP.
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp :tangle no
 | 
			
		||||
(use-package lsp-pyright
 | 
			
		||||
  (use-package lsp-pyright
 | 
			
		||||
      :hook (python-mode . (lambda () (require 'lsp-pyright)))
 | 
			
		||||
      :init (when (executable-find "python3")
 | 
			
		||||
                (setq lsp-pyright-python-executable-cmd "python3")))
 | 
			
		||||
#+end_src
 | 
			
		||||
* LSP Integration of Python
 | 
			
		||||
Now that the [[file:ha-programming.org::*Language Server Protocol (LSP) Integration][LSP Integration]] is complete, we can stitch the two projects together, by calling =lsp=. I oscillate between automatically turning on LSP mode with every Python file, but I sometimes run into issues when starting, so I turn it on with ~, w s~.
 | 
			
		||||
Now that the [[file:ha-programming.org::*Language Server Protocol (LSP) Integration][LSP Integration]] is complete, we can stitch the two projects together, by calling =lsp=. I oscillate between automatically turning on LSP mode with every Python file, but I sometimes run into issues when starting, so I conditionally turn it on.
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defvar ha-python-lsp-title (font-icons 'faicon "python" :title "Python LSP"))
 | 
			
		||||
 | 
			
		||||
  (defun ha-setup-python-lsp ()
 | 
			
		||||
    "Configure the keybindings for LSP in Python."
 | 
			
		||||
    (interactive)
 | 
			
		||||
 | 
			
		||||
    (pretty-hydra-define python-lsp (:color blue :quit-key "q"
 | 
			
		||||
                                     :title ha-python-lsp-title)
 | 
			
		||||
      ("Server"
 | 
			
		||||
       (("D" lsp-disconnect "Disconnect")
 | 
			
		||||
        ("R" lsp-workspace-restart "Restart")
 | 
			
		||||
        ("S" lsp-workspace-shutdown "Shutdown")
 | 
			
		||||
        ("?" lsp-describe-session "Describe"))
 | 
			
		||||
       "Refactoring"
 | 
			
		||||
       (("a" lsp-execute-code-action "Code Actions")
 | 
			
		||||
        ("o" lsp-organize-imports "Organize Imports")
 | 
			
		||||
        ("l" lsp-avy-lens "Avy Lens"))
 | 
			
		||||
       "Toggles"
 | 
			
		||||
       (("b" lsp-headerline-breadcrumb-mode "Breadcrumbs")
 | 
			
		||||
        ("d" lsp-ui-doc-mode "Documentation Popups")
 | 
			
		||||
        ("m" lsp-modeline-diagnostics-mode "Modeline Diagnostics")
 | 
			
		||||
        ("s" lsp-ui-sideline-mode "Sideline Mode"))
 | 
			
		||||
       ""
 | 
			
		||||
       (("t" lsp-toggle-on-type-formatting "Type Formatting")
 | 
			
		||||
        ("h" lsp-toggle-symbol-highlight "Symbol Highlighting")
 | 
			
		||||
        ("L" lsp-toggle-trace-io "Log I/O"))))
 | 
			
		||||
 | 
			
		||||
    (pretty-hydra-define+ python-goto (:quit-key "q")
 | 
			
		||||
      ("LSP"
 | 
			
		||||
       (("g" lsp-find-definition "Definition")
 | 
			
		||||
        ("d" lsp-find-declaration "Declaration")
 | 
			
		||||
        ("r" lsp-find-references "References")
 | 
			
		||||
        ("t" lsp-find-type-definition "Type Definition"))
 | 
			
		||||
       "Peek"
 | 
			
		||||
       (("D" lsp-ui-peek-find-definitions "Definitions")
 | 
			
		||||
        ("I" lsp-ui-peek-find-implementation "Implementations")
 | 
			
		||||
        ("R" lsp-ui-peek-find-references "References")
 | 
			
		||||
        ("S" lsp-ui-peek-find-workspace-symbol "Symbols"))
 | 
			
		||||
       "LSP+"
 | 
			
		||||
       (("u" lsp-ui-imenu "UI Menu")
 | 
			
		||||
        ("i" lsp-find-implementation "Implementations")
 | 
			
		||||
        ("h" lsp-treemacs-call-hierarchy "Hierarchy")
 | 
			
		||||
        ("E" lsp-treemacs-errors-list "Error List"))))
 | 
			
		||||
 | 
			
		||||
    (major-mode-hydra-define+ python-mode nil
 | 
			
		||||
      ("Server"
 | 
			
		||||
       (("l" python-lsp/body "LSP..."))
 | 
			
		||||
       "Edit"
 | 
			
		||||
       (("r" lsp-rename "Rename")
 | 
			
		||||
        ("=" lsp-format-region "Format"))
 | 
			
		||||
       "Navigate"
 | 
			
		||||
       (("A" lsp-workspace-folders-add "Add Folder")
 | 
			
		||||
        ("R" lsp-workspace-folders-remove "Remove Folder"))
 | 
			
		||||
       "Docs"
 | 
			
		||||
       (("D" lsp-describe-thing-at-point "Describe LSP Symbol")
 | 
			
		||||
        ("h" lsp-ui-doc-glance "Glance Help")
 | 
			
		||||
        ("H" lsp-document-highlight "Highlight"))))
 | 
			
		||||
 | 
			
		||||
    (call-interactively 'lsp))
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp :tangle no
 | 
			
		||||
  (use-package lsp-mode
 | 
			
		||||
    ;; :hook ((python-mode . lsp)))
 | 
			
		||||
    :config
 | 
			
		||||
    (ha-local-leader :keymaps 'lsp-mode-map
 | 
			
		||||
      "0" '("treemacs" . lsp-treemacs-symbols)
 | 
			
		||||
    (major-mode-hydra-define+ python-mode (:quit-key "q")
 | 
			
		||||
      ("Server"
 | 
			
		||||
       (("L" ha-setup-python-lsp "Start LSP Server")))))
 | 
			
		||||
 | 
			
		||||
      "/" '("complete" . completion-at-point)
 | 
			
		||||
      "k" '("check code" . python-check)
 | 
			
		||||
      "]" '("shift left" . python-indent-shift-left)
 | 
			
		||||
      "[" '("shift right" . python-indent-shift-right)
 | 
			
		||||
 | 
			
		||||
      ;; actions
 | 
			
		||||
      "a" '(:ignore t :which-key "code actions")
 | 
			
		||||
      "aa" '("code actions" . lsp-execute-code-action)
 | 
			
		||||
      "ah" '("highlight symbol" . lsp-document-highlight)
 | 
			
		||||
      "al" '("lens" . lsp-avy-lens)
 | 
			
		||||
 | 
			
		||||
      ;; formatting
 | 
			
		||||
      "=" '(:ignore t :which-key "formatting")
 | 
			
		||||
      "==" '("format buffer" . lsp-format-buffer)
 | 
			
		||||
      "=r" '("format region" . lsp-format-region)
 | 
			
		||||
 | 
			
		||||
      "e" '(:ignore t :which-key "eval")
 | 
			
		||||
      "e P" '("run python" . run-python)
 | 
			
		||||
      "e e" '("send statement" . python-shell-send-statement)
 | 
			
		||||
      "e b" '("send buffer" . python-shell-send-buffer)
 | 
			
		||||
      "e f" '("send defun" . python-shell-send-defun)
 | 
			
		||||
      "e F" '("send file" . python-shell-send-file)
 | 
			
		||||
      "e r" '("send region" . python-shell-send-region)
 | 
			
		||||
      "e ;" '("expression" . python-shell-send-string)
 | 
			
		||||
      "e p" '("switch-to-shell" . python-shell-switch-to-shell)
 | 
			
		||||
 | 
			
		||||
      ;; folders
 | 
			
		||||
      "F" '(:ignore t :which-key "folders")
 | 
			
		||||
      "Fa" '("add folder" . lsp-workspace-folders-add)
 | 
			
		||||
      "Fb" '("un-blacklist folder" . lsp-workspace-blacklist-remove)
 | 
			
		||||
      "Fr" '("remove folder" . lsp-workspace-folders-remove)
 | 
			
		||||
 | 
			
		||||
      ;; goto
 | 
			
		||||
      "g" '(:ignore t :which-key "goto")
 | 
			
		||||
      "ga" '("find symbol in workspace" . xref-find-apropos)
 | 
			
		||||
      "gd" '("find declarations" . lsp-find-declaration)
 | 
			
		||||
      "ge" '("show errors" . lsp-treemacs-errors-list)
 | 
			
		||||
      "gg" '("find definitions" . lsp-find-definition)
 | 
			
		||||
      "gh" '("call hierarchy" . lsp-treemacs-call-hierarchy)
 | 
			
		||||
      "gi" '("find implementations" . lsp-find-implementation)
 | 
			
		||||
      "gm" '("imenu" . lsp-ui-imenu)
 | 
			
		||||
      "gr" '("find references" . lsp-find-references)
 | 
			
		||||
      "gt" '("find type definition" . lsp-find-type-definition)
 | 
			
		||||
 | 
			
		||||
      ;; peeks
 | 
			
		||||
      "G" '(:ignore t :which-key "peek")
 | 
			
		||||
      "Gg" '("peek definitions" . lsp-ui-peek-find-definitions)
 | 
			
		||||
      "Gi" '("peek implementations" . lsp-ui-peek-find-implementation)
 | 
			
		||||
      "Gr" '("peek references" . lsp-ui-peek-find-references)
 | 
			
		||||
      "Gs" '("peek workspace symbol" . lsp-ui-peek-find-workspace-symbol)
 | 
			
		||||
 | 
			
		||||
      ;; help
 | 
			
		||||
      "h" '(:ignore t :which-key "help")
 | 
			
		||||
      "he" '("eldoc" . python-eldoc-at-point)
 | 
			
		||||
      "hg" '("glance symbol" . lsp-ui-doc-glance)
 | 
			
		||||
      "hh" '("describe symbol at point" . lsp-describe-thing-at-point)
 | 
			
		||||
      "gH" '("describe python symbol" . python-describe-at-point)
 | 
			
		||||
      "hs" '("signature help" . lsp-signature-activate)
 | 
			
		||||
 | 
			
		||||
      "i" 'imenu
 | 
			
		||||
 | 
			
		||||
      ;; refactoring
 | 
			
		||||
      "r" '(:ignore t :which-key "refactor")
 | 
			
		||||
      "ro" '("organize imports" . lsp-organize-imports)
 | 
			
		||||
      "rr" '("rename" . lsp-rename)
 | 
			
		||||
 | 
			
		||||
      ;; toggles
 | 
			
		||||
      "t" '(:ignore t :which-key "toggle")
 | 
			
		||||
      "tD" '("toggle modeline diagnostics" . lsp-modeline-diagnostics-mode)
 | 
			
		||||
      "tL" '("toggle log io" . lsp-toggle-trace-io)
 | 
			
		||||
      "tS" '("toggle sideline" . lsp-ui-sideline-mode)
 | 
			
		||||
      "tT" '("toggle treemacs integration" . lsp-treemacs-sync-mode)
 | 
			
		||||
      "ta" '("toggle modeline code actions" . lsp-modeline-code-actions-mode)
 | 
			
		||||
      "tb" '("toggle breadcrumb" . lsp-headerline-breadcrumb-mode)
 | 
			
		||||
      "td" '("toggle documentation popup" . lsp-ui-doc-mode)
 | 
			
		||||
      "tf" '("toggle on type formatting" . lsp-toggle-on-type-formatting)
 | 
			
		||||
      "th" '("toggle highlighting" . lsp-toggle-symbol-highlight)
 | 
			
		||||
      "tl" '("toggle lenses" . lsp-lens-mode)
 | 
			
		||||
      "ts" '("toggle signature" . lsp-toggle-signature-auto-activate)
 | 
			
		||||
 | 
			
		||||
      ;; workspaces
 | 
			
		||||
      "w" '(:ignore t :which-key "workspaces")
 | 
			
		||||
      "wD" '("disconnect" . lsp-disconnect)
 | 
			
		||||
      "wd" '("describe session" . lsp-describe-session)
 | 
			
		||||
      "wq" '("shutdown server" . lsp-workspace-shutdown)
 | 
			
		||||
      "wr" '("restart server" . lsp-workspace-restart)
 | 
			
		||||
      "ws" '("start server" . lsp)))
 | 
			
		||||
  ;; ----------------------------------------------------------------------
 | 
			
		||||
  ;; Missing Symbols to be integrated?
 | 
			
		||||
  ;; "0" '("treemacs" . lsp-treemacs-symbols)
 | 
			
		||||
  ;; "/" '("complete" . completion-at-point)
 | 
			
		||||
  ;; "k" '("check code" . python-check)
 | 
			
		||||
  ;; "Fb" '("un-blacklist folder" . lsp-workspace-blacklist-remove)
 | 
			
		||||
  ;; "hs" '("signature help" . lsp-signature-activate)
 | 
			
		||||
  ;; "tT" '("toggle treemacs integration" . lsp-treemacs-sync-mode)
 | 
			
		||||
  ;; "ta" '("toggle modeline code actions" . lsp-modeline-code-actions-mode)
 | 
			
		||||
  ;; "th" '("toggle highlighting" . lsp-toggle-symbol-highlight)
 | 
			
		||||
  ;; "tl" '("toggle lenses" . lsp-lens-mode)
 | 
			
		||||
  ;; "ts" '("toggle signature" . lsp-toggle-signature-auto-activate)
 | 
			
		||||
#+end_src
 | 
			
		||||
* Project Configuration
 | 
			
		||||
I work with a lot of projects with my team where I need to /configure/ the project such that LSP and my Emacs setup works. Let's suppose I could point a function at a project directory, and have it /set it up/:
 | 
			
		||||
| 
						 | 
				
			
			@ -290,6 +332,7 @@ I work with a lot of projects with my team where I need to /configure/ the proje
 | 
			
		|||
        (insert "pylsp-rope\n"))
 | 
			
		||||
      (shell-command "pip install -r requirements-dev.txt")))
 | 
			
		||||
#+end_src
 | 
			
		||||
* Major Mode Hydra
 | 
			
		||||
* Technical Artifacts                                :noexport:
 | 
			
		||||
Let's =provide= a name so we can =require= this file:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1157,7 +1157,8 @@ And then we can use it. For some reason, the =pymarkdown= (which I need to use f
 | 
			
		|||
  (use-package markdown-mode
 | 
			
		||||
    :after flycheck
 | 
			
		||||
    :config
 | 
			
		||||
    (setq flycheck-markdown-pymarkdown-config ".pymarkdown.yml")
 | 
			
		||||
    (setq flycheck-markdown-pymarkdown-config
 | 
			
		||||
          (expand-file-name ".pymarkdown.yml" (getenv "HOME")))
 | 
			
		||||
    (flycheck-may-enable-checker 'markdown-pymarkdown))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,6 +162,7 @@ While not as fast as [[https://github.com/akermu/emacs-libvterm][vterm]], the [[
 | 
			
		|||
                             ("terminfo/65" "terminfo/65/*")
 | 
			
		||||
                             ("integration" "integration/*")
 | 
			
		||||
                             (:exclude ".dir-locals.el" "*-tests.el")))
 | 
			
		||||
    :commands (eat eat-make eat-project)
 | 
			
		||||
    :bind (:map eat-semi-char-mode-map
 | 
			
		||||
                ("C-c C-t" . ha-eat-narrow-to-shell-prompt-dwim))
 | 
			
		||||
    :config
 | 
			
		||||
| 
						 | 
				
			
			@ -475,7 +476,7 @@ We'll give =openstack= CLI a =--format json= option to make it easier for parsin
 | 
			
		|||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun ha-ssh-overcloud-query-for-hosts ()
 | 
			
		||||
    "If the overcloud cache hasn't be populated, ask the user if we want to run the command."
 | 
			
		||||
    (when (not ha-ssh-favorite-hostnames)
 | 
			
		||||
    (unless ha-ssh-favorite-hostnames
 | 
			
		||||
      (when (y-or-n-p "Cache of Overcloud hosts aren't populated. Retrieve hosts?")
 | 
			
		||||
        (call-interactively 'ha-ssh-overcloud-cache-populate))))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -488,7 +489,7 @@ We'll do the work of getting the /server list/ with this function:
 | 
			
		|||
  (defun ha-ssh-overcloud-cache-populate (cluster)
 | 
			
		||||
    "Given an `os-cloud' entry, stores all available hostnames.
 | 
			
		||||
  Calls `ha-ssh-add-favorite-host' for each host found."
 | 
			
		||||
    (interactive (list (completing-read "Cluster: " '(devprod501 devprod502))))
 | 
			
		||||
    (interactive (list (completing-read "Cluster: " '(devprod501 devprod502 devprod502-yawxway))))
 | 
			
		||||
    (message "Calling the `openstack' command...this will take a while. Grab a coffee, eh?")
 | 
			
		||||
    (let* ((command (format "openstack --os-cloud %s server list --no-name-lookup -f json" cluster))
 | 
			
		||||
           (json-data (thread-last command
 | 
			
		||||
| 
						 | 
				
			
			@ -496,10 +497,12 @@ We'll do the work of getting the /server list/ with this function:
 | 
			
		|||
                                   (json-read-from-string))))
 | 
			
		||||
      (dolist (entry (seq--into-list json-data))
 | 
			
		||||
        (ha-ssh-add-favorite-host (alist-get 'Name entry)
 | 
			
		||||
                                  (or
 | 
			
		||||
                                   (thread-last entry
 | 
			
		||||
                                                (alist-get 'Networks)
 | 
			
		||||
                                                (alist-get 'cedev13)
 | 
			
		||||
                                               (seq-first))))
 | 
			
		||||
                                                (seq-first))
 | 
			
		||||
                                   (alist-get 'Name entry))))
 | 
			
		||||
      (message "Call to `openstack' complete. Found %d hosts." (length json-data))))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -554,3 +557,7 @@ Before you can build this on a new system, make sure that you put the cursor ove
 | 
			
		|||
#+options:     num:nil toc:t todo:nil tasks:nil tags:nil date:nil
 | 
			
		||||
#+options:     skip:nil author:nil email:nil creator:nil timestamp:nil
 | 
			
		||||
#+infojs_opt:  view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
 | 
			
		||||
 | 
			
		||||
# Local Variables:
 | 
			
		||||
# eval: (org-next-visible-heading 1)
 | 
			
		||||
# End:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue