diff --git a/ha-programming-python.org b/ha-programming-python.org index c34aff7..c9afb10 100644 --- a/ha-programming-python.org +++ b/ha-programming-python.org @@ -318,85 +318,6 @@ This allows me to delete a Python “block” using ~dal~. ("Misc" (("t" python-tests/body "Tests...")))))) #+end_src -* Elpy -The [[https://elpy.readthedocs.io/en/latest/introduction.html][Elpy Project]] expands on the =python-mode=. - -#+BEGIN_SRC emacs-lisp - (use-package elpy - :init - (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 - -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 doesn’t 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 we’ve 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 - -Let’s expand our =major-mode-hydra= with some extras: - -#+begin_src emacs-lisp - (use-package major-mode-hydra - :after elpy - :config - - (pretty-hydra-define python-evaluate (:color blue :quit-key "q" - :title ha-python-eval-title) - ("Section" - (("F" elpy-shell-send-defun "Function") - ("E" elpy-shell-send-statement "Statement") - (";" python-shell-send-string "Expression")) - "Entirety" - (("B" elpy-shell-send-buffer "Buffer") - ("r" elpy-shell-send-region-or-buffer "region")) - "And Step..." - (("f" elpy-shell-send-defun-and-step "Function" :color pink) - ("e" elpy-shell-send-statement-and-step "Statement" :color pink)))) - - (pretty-hydra-define+ python-refactor nil - ("Elpy" - (("r" elpy-refactor-rename "Rename") - ("i" elpy-refactor-inline "Inline var") - ("v" elpy-refactor-extract-variable "To variable") - ("f" elpy-refactor-extract-function "To function") - ("a" elpy-refactor-mode "All...")))) - - (major-mode-hydra-define+ python-mode (:quit-key "q" :color blue) - ("Server" - (("s" elpy-shell-switch-to-shell "Go to Server") - ("C" elpy-config "Config Elpy")) - "Edit" - (("f" elpy-black-fix-code "Fix/format code")) - "Docs" - (("d" elpy-eldoc-documentation "Describe Symbol") - ("D" elpy-doc "Docs Symbol"))))) -#+end_src - -* Anaconda -The [[https://github.com/pythonic-emacs/anaconda-mode][anaconda-mode project]] seems as good as Elpy, but also include Evil keybindings. - -#+BEGIN_SRC emacs-lisp :tangle no - (use-mode anaconda-mode - :hook ((python-mode . anaconda-mode) - (python-mode ../ anaconda-eldoc-mode))) -#+END_SRC - -Since we are using * LSP Integration of 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=): @@ -422,100 +343,60 @@ Each Python project's =requirements-dev.txt= file would reference the [[https:// # ... #+end_src ** Pyright -I’m using the Microsoft-supported [[https://github.com/Microsoft/pyright][pyright]] package instead. Adding this to my =requirements.txt= files: +I’m using the Microsoft-supported [[https://github.com/Microsoft/pyright][pyright]] package instead, adding it to the global Python scope. + #+begin_src conf :tangle no pyright #+end_src -The [[https://github.com/emacs-lsp/lsp-pyright][pyright package]] works with LSP. +While the [[https://github.com/emacs-lsp/lsp-pyright][pyright package]] works with LSP, this isn’t needed, as =eglot= works directly with it. +** eglot +The built-in Eglot package natively supports [[https://github.com/Microsoft/pyright][pyright]] via =eglot-server-programs= (no extra package needed): -#+begin_src emacs-lisp :tangle no - (use-package lsp-pyright - :hook (python-mode . (lambda () (require 'lsp-pyright))) - :init (when (executable-find "python3") - (setq lsp-pyright-python-executable-cmd "python3"))) +#+begin_src emacs-lisp + (use-package eglot + :straight nil + :after python + :defer t + :hook (python-mode . eglot-ensure) + :config + (add-to-list 'eglot-server-programs + '((python-mode python-ts-mode) . ("pyright-langserver" "--stdio")))) #+end_src -*** Keybindings -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. + +Keybindings for the eglot-based LSP sub-menu, wired into the existing =python-mode= hydra: #+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) - + (use-package eglot + :straight nil + :after major-mode-hydra + :config (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")) + (("D" eglot-shutdown "Disconnect") + ("R" eglot-reconnect "Reconnect") + ("?" eglot-show-workspace-configuration "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" - (("=" lsp-format-region "Format")) - "Navigate" - (("A" lsp-workspace-folders-add "Add Folder") - ("R" lsp-workspace-folders-remove "Remove Folder")) + (("a" eglot-code-actions "Code Actions") + ("o" eglot-code-action-organize-imports "Organize Imports") + ("r" eglot-rename "Rename")) + "Navigation" + (("." xref-find-definitions "Definition") + ("/" xref-find-references "References") + ("t" eglot-find-typeDefinition "Type Definition") + ("i" eglot-find-implementation "Implementation")) "Docs" - (("D" lsp-describe-thing-at-point "Describe LSP Symbol") - ("h" lsp-ui-doc-glance "Glance Help") - ("H" lsp-document-highlight "Highlight")))) + (("d" eldoc-doc-buffer "Eldoc Buffer") + ("h" eglot-inlay-hints-mode "Inlay Hints") + ("=" eglot-format-buffer "Format Buffer")))) - (call-interactively 'lsp)) - - (use-package lsp-mode - :config (major-mode-hydra-define+ python-mode (:quit-key "q") ("Server" - (("L" ha-setup-python-lsp "Start LSP Server"))))) - - ;; ---------------------------------------------------------------------- - ;; 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) + ( ;; ("L" eglot-ensure "Start eglot") --> Not needed with eglot-ensure + ("l" python-lsp/body "LSP..."))))) #+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/: @@ -541,17 +422,9 @@ I work with a lot of projects with my team where I need to /configure/ the proje (shell-command "pipconf --local") (shell-command "pip install --upgrade pip")) - (message "Configuring pip for LSP") + (message "Configuring pip for eglot/pyright") (with-temp-file "requirements-dev.txt" - (insert "python-lsp-server[all]\n") - - ;; Let's install these extra packages individually ... - (insert "pyls-flake8\n") - ;; (insert "pylsp-mypy") - ;; (insert "pyls-isort") - ;; (insert "python-lsp-black") - ;; (insert "pyls-memestra") - (insert "pylsp-rope\n")) + (insert "pyright\n")) (shell-command "pip install -r requirements-dev.txt"))) #+end_src * Major Mode Hydra diff --git a/ha-programming-ruby.org b/ha-programming-ruby.org index 33e95e7..477d518 100644 --- a/ha-programming-ruby.org +++ b/ha-programming-ruby.org @@ -229,37 +229,6 @@ The [[https://github.com/r0man/ruby-test-mode][ruby-test-mode]] project aims a r "t A" '("test all" . ruby-test-run) "t a" '("retest" . ruby-test-rerun))) #+end_src -** Robe -The [[https://github.com/dgutov/robe][Robe project]] can be used instead of [[file:ha-programming.org::*Language Server Protocol (LSP) Integration][LSP]]. -#+begin_src emacs-lisp - (use-package robe - - :config - (ha-local-leader 'ruby-mode-map - "w" '(:ignore t :which-key "robe") - "ws" '("start" . robe-start)) - - ;; The following leader-like keys, are only available when I have - ;; started LSP, and is an alternate to Command-m: - :general - (:states 'normal :keymaps 'robe-mode-map - ", w r" '("restart" . lsp-reconnect) - ", w b" '("events" . lsp-events-buffer) - ", w e" '("errors" . lsp-stderr-buffer) - ", w q" '("quit" . lsp-shutdown) - ", w l" '("load file" . ruby-load-file) - - ", l r" '("rename" . lsp-rename) - ", l f" '("format" . lsp-format) - ", l a" '("actions" . lsp-code-actions) - ", l i" '("imports" . lsp-code-action-organize-imports) - ", l d" '("doc" . lsp-lookup-documentation))) -#+end_src - -Do we want to load Robe /automatically/? -#+begin_src emacs-lisp - (use-package robe :hook (ruby-mode . robe-mode)) -#+end_src ** Bundler The [[https://github.com/endofunky/bundler.el][Bundler project]] integrates [[https://bundler.io/][bundler]] to install a projects Gems. @@ -295,6 +264,34 @@ Seems that to understand and edit Cucumber /feature/ definitions, you need [[htt ** RSpec https://github.com/pezra/rspec-mode * LSP +The built-in Eglot package natively supports [[https://github.com/castwide/solargraph][Solargraph]] via =eglot-server-programs= (no extra package needed): + +#+begin_src emacs-lisp + (use-package eglot + :straight nil + :after ruby-mode + :defer t + :hook (ruby-mode . eglot-ensure)) +#+end_src + +Keybindings for the eglot-based LSP sub-menu, wired into the =ruby-mode= local leader: + +#+begin_src emacs-lisp + (use-package eglot + :straight nil + :after ruby-mode + :config + (ha-local-leader 'ruby-mode-map + "w" '(:ignore t :which-key "eglot") + "wD" '("disconnect" . eglot-shutdown) + "wR" '("reconnect" . eglot-reconnect) + "w?" '("describe" . eglot-show-workspace-configuration) + "wa" '("code actions" . eglot-code-actions) + "wo" '("organize imports" . eglot-code-action-organize-imports) + "wr" '("rename" . eglot-rename) + "w=" '("format buffer" . eglot-format-buffer))) +#+end_src + Need to install [[https://github.com/castwide/solargraph][Solargraph]] for the LSP server experience: #+begin_src sh gem install solargraph @@ -303,29 +300,7 @@ Or add it to your =Gemfile=: #+begin_src ruby gem 'solargraph', group: :development #+end_src -* XRef Interface with GNU Global -The [[http://www.gnu.org/software/global/][GNU Global]] has the ability to generate a tags file for large, multi-project Ruby code bases. - -First, issue these two: -#+begin_src sh :dir ~/work/gourmet - find . -name .git | while read DOTGIT - do - REPO=$(dirname $DOTGIT) - (cd $REPO && git pull origin master) - done - - find . -name "*.rb" > gtags.files - gtags --gtagslabel=new-ctags --file gtags.files -#+end_src - -And now we need the GNU Global for Emacs, we are using the most up-to-date version of [[https://github.com/leoliu/ggtags][ggtags]]. - -#+begin_src emacs-lisp - (use-package ggtags - :hook ((ruby-mode . #'ggtags-mode))) -#+end_src - -Careful observers will note that +Once installed, [[*eglot][eglot]] finds and talks to =solargraph= automatically; no further Emacs configuration is needed. * Technical Artifacts :noexport: Let's =provide= a name so we can =require= this file: diff --git a/ha-programming-rust.org b/ha-programming-rust.org index 4a81f2c..1b95f30 100644 --- a/ha-programming-rust.org +++ b/ha-programming-rust.org @@ -68,24 +68,10 @@ The [[https://github.com/brotzeit/rustic][rustic project]] incorporates [[https: #+begin_src emacs-lisp (use-package rustic - :init + :config (setq rustic-lsp-client 'eglot - rustic-analyzer-command '("/opt/homebrew/bin/rust-analyzer")) - ;; :bind (:map rustic-mode-map - ;; ("M-j" . lsp-ui-imenu) - ;; ("M-?" . lsp-find-references) - ;; ("C-c C-c l" . flycheck-list-errors) - ;; ("C-c C-c a" . lsp-execute-code-action) - ;; ("C-c C-c r" . lsp-rename) - ;; ("C-c C-c q" . lsp-workspace-restart) - ;; ("C-c C-c Q" . lsp-workspace-shutdown) - ;; ("C-c C-c s" . lsp-rust-analyzer-status) - ;; ("C-c C-c e" . lsp-rust-analyzer-expand-macro) - ;; ("C-c C-c d" . dap-hydra) - ;; ("C-c C-c h" . lsp-ui-doc-glance)) - - :config - (setq rustic-format-on-save t)) + rustic-analyzer-command '("/opt/homebrew/bin/rust-analyzer") + rustic-format-on-save t)) #+end_src ** The Playground @@ -102,7 +88,6 @@ The [[https://github.com/dryman/toml-mode.el][toml-mode]] adds syntax highlighti #+begin_src emacs-lisp (use-package toml-mode) #+end_src -* LSP * Technical Artifacts :noexport: Let's =provide= a name so we can =require= this file: diff --git a/ha-programming.org b/ha-programming.org index 664b59f..fdd243e 100644 --- a/ha-programming.org +++ b/ha-programming.org @@ -683,86 +683,21 @@ I have two different /jumping/ systems, the [[info:emacs#Xref][Xref interface]] The [[https://microsoft.github.io/language-server-protocol/][LSP]] is a way to connect /editors/ (like Emacs) to /languages/ (like Lisp)… wait, no. While originally designed for VS Code and probably Python, we can abstract away [[https://github.com/davidhalter/jedi][Jedi]] and the [[http://tkf.github.io/emacs-jedi/latest/][Emacs integration to Jedi]] (and duplicate everything for Ruby, and Clojure, and…). Emacs has two LSP projects, and while I have used [[LSP Mode]], but since I don’t have heavy IDE requirements, I am finding that [[eglot]] to be simpler. -*** LSP + #+begin_src emacs-lisp - (use-package lsp-mode - :commands (lsp lsp-deferred) - :init - ;; Let's make lsp-doctor happy with these settings: - (setq gc-cons-threshold (* 100 1024 1024) - read-process-output-max (* 1024 1024) - company-idle-delay 0.0 ; Are thing fast enough to do this? - lsp-keymap-prefix "s-m") - + (use-package eglot + :straight nil + :commands eglot-ensure :config - (global-set-key (kbd "s-m") 'lsp) - ;; (ha-local-leader :keymaps 'prog-mode-map - ;; "w" '(:ignore t :which-key "lsp") - ;; "l" '(:ignore t :which-key "lsp") - ;; "ws" '("start" . lsp)) - - ;; ;; The following leader-like keys, are only available when I have - ;; ;; started LSP, and is an alternate to Command-m: - ;; :general - ;; (:states 'normal :keymaps 'lsp-mode-map - ;; ", w r" '("restart" . lsp-reconnect) - ;; ", w b" '("events" . lsp-events-buffer) - ;; ", w e" '("errors" . lsp-stderr-buffer) - ;; ", w q" '("quit" . lsp-shutdown) - ;; ", w Q" '("quit all" . lsp-shutdown-all) - - ;; ", l r" '("rename" . lsp-rename) - ;; ", l f" '("format" . lsp-format) - ;; ", l a" '("actions" . lsp-code-actions) - ;; ", l i" '("imports" . lsp-code-action-organize-imports) - ;; ", l d" '("doc" . lsp-lookup-documentation)) - - :hook ((lsp-mode . lsp-enable-which-key-integration))) + (setq eglot-autoshutdown t)) #+end_src -I will want to start adding commands under my =,= mode-specific key sequence leader, but in the meantime, all LSP-related keybindings are available under ~⌘-m~. See [[https://emacs-lsp.github.io/lsp-mode/page/keybindings/][this page]] for the default keybindings. -Using the [[https://github.com/seagle0128/doom-modeline][Doom Modeline]] to add notifications: -#+begin_src emacs-lisp :tangle no - (use-package doom-modeline - :config - (setq doom-modeline-lsp t - doom-modeline-env-version t)) -#+end_src -**** UI -The [[https://github.com/emacs-lsp/lsp-ui][lsp-ui]] project offers much of the display and interface to LSP. Seems to make the screen cluttered. +The [[https://github.com/flycheck/flycheck-eglot][flycheck-eglot]] package bridges eglot diagnostics into flycheck, so existing checkers and keybindings continue to work unchanged: #+begin_src emacs-lisp - (use-package lsp-ui - :commands lsp-ui-mode + (use-package flycheck-eglot + :after (flycheck eglot) :config - (setq lsp-ui-sideline-ignore-duplicate t - lsp-ui-sideline-show-hover t - lsp-ui-sideline-show-diagnostics t) - :hook (lsp-mode . lsp-ui-mode)) -#+end_src -*** Company Completion -The [[https://github.com/tigersoldier/company-lsp][company-lsp]] offers a [[http://company-mode.github.io/][company]] completion backend for [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]]: - -#+begin_src emacs-lisp :tangle no - (use-package company-lsp - :config - (push 'company-lsp company-backends)) -#+end_src -To options that might be interesting: - - =company-lsp-async=: When set to non-nil, fetch completion candidates asynchronously. - - =company-lsp-enable-snippet=: Set it to non-nil if you want to enable snippet expansion on completion. Set it to nil to disable this feature. - -*** LSP iMenu -The [[https://github.com/emacs-lsp/lsp-ui/blob/master/lsp-ui-imenu.el][lsp-imenu]] project offers a =lsp-ui-imenu= function for jumping to functions: - -#+begin_src emacs-lisp :tangle no - (use-package lsp-ui-imenu - :straight nil - :after lsp-ui - :config - (ha-local-leader :keymaps 'prog-mode-map - "g" '(:ignore t :which-key "goto") - "g m" '("imenu" . lsp-ui-imenu)) - (add-hook 'lsp-after-open-hook 'lsp-enable-imenu)) + (global-flycheck-eglot-mode 1)) #+end_src ** General Code Editing *** iEdit diff --git a/ha-theme.org b/ha-theme.org index 9566c9d..3e30248 100644 --- a/ha-theme.org +++ b/ha-theme.org @@ -330,6 +330,7 @@ Let’s make a /theme/: `(mode-line ((t (:background ,active :foreground "white")))) `(mode-line-active ((t (:background ,active)))) `(mode-line-inactive ((t (:background ,inactive)))) + `(which-func ((t (:foreground ,orange-lt)))) `(tab-bar ((t :foreground ,default-fg :background ,inactive :inherit variable-pitch))) `(tab-line ((t :foreground ,default-fg :background ,inactive))) diff --git a/initialize b/initialize index a4c96a3..9650d1a 100755 --- a/initialize +++ b/initialize @@ -31,7 +31,7 @@ cat > "$HAMACS_DEST/early-init.el" < "$HAMACS_DEST/init.el" < "$HAMACS_DEST/init.el" <