From 7301b8fb0eac15cba8d4cca5479de3a6ade28a07 Mon Sep 17 00:00:00 2001 From: Howard Abrams Date: Thu, 18 Nov 2021 08:17:20 -0800 Subject: [PATCH] Integrate LSP and Python to Emacs Using the typical `lsp` project, but am not sure if I should switch over to `eglot`. It will be interesting to mess with this. --- README.org | 1 + ha-programming-python.org | 128 ++++++++++++++++++++++++++++++++++++++ ha-programming.org | 60 +++++++++++++++++- 3 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 ha-programming-python.org diff --git a/README.org b/README.org index 332e743..33bcdc1 100644 --- a/README.org +++ b/README.org @@ -29,5 +29,6 @@ This creates [[file:~/.emacs.d/init.el][~/.emacs.d/init.el]] that starts the pro - [[file:ha-irc.org][ha-irc.org]] :: Connection to IRC servers using rcirc and bitlbee. - [[file:ha-passwords.org][ha-passwords.org]] :: Code for generating passwords. - [[file:ha-programming.org][ha-programming.org]] :: configuration for /all/ programming languages, or at least, the simple ones. + - [[file:ha-programming-python.org][ha-programming-python.org]] :: configuration for working with Python and LSP. *Note:* Other functions and files come from essays written on [[http://www.howardism.org][my blog]]. To help with this, see [[file:support/final-initialize.el][support/final-initialize.el]] file. diff --git a/ha-programming-python.org b/ha-programming-python.org new file mode 100644 index 0000000..47a37de --- /dev/null +++ b/ha-programming-python.org @@ -0,0 +1,128 @@ +#+TITLE: Configuring Python in Emacs +#+AUTHOR: Howard X. Abrams +#+EMAIL: howard.abrams@gmail.com +#+DATE: 2021-11-16 +#+FILETAGS: :emacs: + +A literate programming file for configuring Python. + +#+BEGIN_SRC emacs-lisp :exports none + ;;; ha-programming-python.el --- A literate programming file for configuring Python. -*- lexical-binding: t; -*- + ;; + ;; Copyright (C) 2021 Howard X. Abrams + ;; + ;; Author: Howard X. Abrams + ;; Maintainer: Howard X. Abrams + ;; Created: November 16, 2021 + ;; + ;; This file is not part of GNU Emacs. + ;; + ;; *NB:* Do not edit this file. Instead, edit the original literate file at: + ;; /Users/howard.abrams/other/hamacs/ha-programming-python.org + ;; And tangle the file to recreate this one. + ;; + ;;; Code: + #+END_SRC +* Introduction +The critical part of Python integration with Emacs is running LSP in Python using [[file:ha-programming.org::*direnv][direnv]]. And the only question to ask is if the Python we run it in Docker or in a virtual environment. +** Virtual Environment +For a local virtual machine, simply put the following in your =.envrc= file: +#+begin_src conf +layout_python3 +#+end_src +That is pretty slick and simple. + +The old way, that we still use if you need a particular version of Python, is to install [[https://github.com/pyenv/pyenv][pyenv]] globally: +#+BEGIN_SRC sh +pip install pyenv +#+END_SRC + +And have this in your =.envrc= file: +#+begin_src conf +use python 3.7.1 +#+end_src + +Also, you need the following in your =~/.config/direnv/direnvrc= file (which I have): +#+begin_src shell +use_python() { + local python_root=$(pyenv root)/versions/$1 + load_prefix "$python_root" + if [[ -x "$python_root/bin/python" ]]; then + layout python "$python_root/bin/python" + else + echo "Error: $python_root/bin/python can't be executed." + exit + fi +} +#+end_src +** Docker Environment +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. + +But, but... think of the dependencies! + +Enough of the rant (I go back and forth), after getting Docker installed and running (ooo Podman ... shiny), and you've created a =Dockerfile= for your project, let's install [[https://github.com/snbuback/container-env][container-env]]. + +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_layout +#+end_src +** Python Dependencies +Whew. Each Python project's =requirements-dev.txt= file would reference the [[https://pypi.org/project/python-lsp-server/][python-lsp-server]] (not the /unmaintained/ =python-language-server=): +#+begin_src conf +python-lsp-server[all] +#+end_src + +*Note:* This does mean, you would have a =tox.ini= with this line: +#+BEGIN_SRC conf + [tox] + minversion = 1.6 + skipsdist = True + envlist = linters + ignore_basepython_conflict = True + + [testenv] + basepython = python3 + install_command = pip install {opts} {packages} + deps = -r{toxinidir}/test-requirements.txt + commands = stestr run {posargs} + stestr slowest + # ... +#+END_SRC +*** Using Jedi Instead +Do we want to use the Jedi version of LSP? Not sure what it buys us. +#+BEGIN_SRC emacs-lisp :tangle no +(use-package lsp-jedi + :config + (with-eval-after-load "lsp-mode" + (add-to-list 'lsp-disabled-clients 'pyls) + (add-to-list 'lsp-enabled-clients 'jedi))) +#+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: + +#+BEGIN_SRC emacs-lisp + (use-package lsp-mode + :hook ((python-mode . lsp))) +#+END_SRC +And we're done. +* Technical Artifacts :noexport: +Let's =provide= a name so we can =require= this file: + +#+BEGIN_SRC emacs-lisp :exports none + (provide 'ha-programming-python) + ;;; ha-programming-python.el ends here + #+END_SRC + +#+DESCRIPTION: A literate programming file for configuring Python. + +#+PROPERTY: header-args:sh :tangle no +#+PROPERTY: header-args:emacs-lisp :tangle yes +#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil +#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js diff --git a/ha-programming.org b/ha-programming.org index 12c7200..dafec0c 100644 --- a/ha-programming.org +++ b/ha-programming.org @@ -32,9 +32,63 @@ The following work for all programming languages. ** direnv Farm off commands into /virtual environments/: #+BEGIN_SRC emacs-lisp -(use-package direnv - :init - (setq direnv--executable "/usr/local/bin/direnv")) + (use-package direnv + :init + (setq direnv--executable "/usr/local/bin/direnv") + :config + (direnv-mode)) +#+END_SRC +** Language Server Protocol (LSP) Integration +The [[https://microsoft.github.io/language-server-protocol/][LSP]] is a way to connect /editors/ (like Emacs) to /languages/ (like Lisp) ... wait, no, it was originally designed for VS Code and probably Python, but we now 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...). + +Instead, we install [[https://emacs-lsp.github.io/lsp-mode/][LSP Mode]] (and friends), which simplifies my configuration: +#+BEGIN_SRC emacs-lisp + (use-package lsp-mode + :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") + :config + :hook ((lsp-mode . lsp-enable-which-key-integration)) + :commands lsp) +#+END_SRC +I will want to start adding commands under my =SPC m= mode-specific key sequence leader, but ... later. + +The [[https://github.com/emacs-lsp/lsp-ui][lsp-ui]] project offers much of the display and interface to LSP: +#+BEGIN_SRC emacs-lisp + (use-package lsp-ui + :commands lsp-ui-mode + :config + (setq lsp-ui-sideline-ignore-duplicate t + lsp-ui-sideline-show-hover t + lsp-ui-sideline-show-diagnostics t) + (add-hook 'lsp-mode-hook 'lsp-ui-mode)) +#+END_SRC + +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 + (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. + +The [[https://github.com/emacs-lsp/lsp-ui/blob/master/lsp-ui-imenu.el][lsp-imenu]] offers a =lsp-ui-imenu= function for jumping to functions: +#+BEGIN_SRC emacs-lisp + (use-package lsp-ui-imenu + :straight nil + :after lsp-ui + :config + (add-hook 'lsp-after-open-hook 'lsp-enable-imenu)) +#+END_SRC + +Do we want to use a debugger? +#+BEGIN_SRC emacs-lisp :tangle no +(use-package dap-mode) #+END_SRC ** Function Call Notifications As I've mentioned [[http://www.howardism.org/Technical/Emacs/beep-for-emacs.html][on my website]], I've created a [[file:~/website/Technical/Emacs/beep-for-emacs.org][beep function]] that notifies when long running processes complete.