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.
This commit is contained in:
parent
947e0fb547
commit
7301b8fb0e
3 changed files with 186 additions and 3 deletions
|
@ -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.
|
||||
|
|
128
ha-programming-python.org
Normal file
128
ha-programming-python.org
Normal file
|
@ -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 <http://gitlab.com/howardabrams>
|
||||
;; Maintainer: Howard X. Abrams <howard.abrams@gmail.com>
|
||||
;; 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
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue