Skip to content

Commit

Permalink
Merge pull request #25 from wyuenho/eglot
Browse files Browse the repository at this point in the history
Eglot support
  • Loading branch information
wyuenho authored Sep 5, 2023
2 parents 1987f18 + a972d48 commit d903458
Show file tree
Hide file tree
Showing 6 changed files with 503 additions and 72 deletions.
60 changes: 60 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: "CI"
on:
push:
paths-ignore:
- "README.rst"
branches:
- "main"
pull_request:
branches:
- "*"

jobs:
ci:
runs-on: "ubuntu-latest"
continue-on-error: "${{ matrix.experimental }}"
strategy:
fail-fast: false
matrix:
emacs-version:
- "26.1"
- "26.2"
- "26.3"
- "27.1"
- "27.2"
- "28.1"
- "28.2"
- "29.1"
experimental: [false]
include:
- emacs-version: "snapshot"
experimental: true

steps:
- uses: "actions/checkout@v4"

- uses: "purcell/setup-emacs@master"
with:
version: "${{ matrix.emacs-version }}"

- uses: "cask/setup-cask@master"
with:
version: "0.9.0"

- name: "Compile"
run: |
make compile
- uses: "actions/setup-go@v4"
with:
go-version: "stable"
check-latest: true
cache: false

- name: "Install tomljson"
run: |
go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest
- name: "Test"
run: |
make test
64 changes: 0 additions & 64 deletions .github/workflows/test.yml

This file was deleted.

2 changes: 2 additions & 0 deletions Cask
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
(source melpa)

(depends-on "f" "0.6.0")
(depends-on "map" "3.3.1")
(depends-on "seq" "2.24")

(development
(depends-on "buttercup")
Expand Down
20 changes: 16 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Supported Emacs Packages
- Built-in `project.el <https://www.gnu.org/software/emacs/manual/html_node/emacs/Projects.html>`_
- `projectile <https://docs.projectile.mx/projectile/index.html>`_
- `envrc <https://github.com/purcell/envrc>`_ (`direnv caveats`_)
- `eglot <https://github.com/joaotavora/eglot>`_
- `flycheck <https://www.flycheck.org/en/latest/>`_
- `lsp-jedi <https://github.com/fredcamps/lsp-jedi>`_
- `lsp-pyright <https://github.com/emacs-lsp/lsp-pyright>`_
Expand Down Expand Up @@ -196,6 +197,8 @@ Complete Example
(use-package dap-python
:after lsp)
(use-package eglot)
(use-package python-pytest)
(use-package python-black)
Expand All @@ -210,6 +213,9 @@ Complete Example
(setq-local python-shell-interpreter (pet-executable-find "python")
python-shell-virtualenv-root (pet-virtualenv-root))
;; (pet-eglot-setup)
;; (eglot-ensure)
(pet-flycheck-setup)
(flycheck-mode)
Expand Down Expand Up @@ -305,12 +311,18 @@ setting the corresponding ``flycheck`` checker executable variable to the
intended absolute path.


``pet`` can't find my virtualenvs, how do I debug it?
+++++++++++++++++++++++++++++++++++++++++++++++++++++
My package didn't pick up the correct paths, how do I debug ``pet``?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

You can turn on ``pet-debug`` and watch what comes out in the ``*Messages*``
buffer. In addition, you can use ``M-x pet-verify-setup`` in your Python
buffers to find out what was detected.
buffer. In addition, you can use ``M-x pet-verify-setup`` in your Python buffers
to find out what was detected.

For ``lsp``, use ``lsp-describe-session``.

For ``eglot``, use ``eglot-show-workspace-configuration``.

For ``flycheck``, use ``flycheck-verify-setup``.


Do I still need any of the 11+ virtualenv Emacs packages?
Expand Down
154 changes: 151 additions & 3 deletions pet.el
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
;; Author: Jimmy Yuen Ho Wong <[email protected]>
;; Maintainer: Jimmy Yuen Ho Wong <[email protected]>
;; Version: 1.1.0
;; Package-Requires: ((emacs "26.1") (f "0.6.0"))
;; Package-Requires: ((emacs "26.1") (f "0.6.0") (map "3.3.1") (seq "2.24"))
;; Homepage: https://github.com/wyuenho/emacs-pet/
;; Keywords: tools

Expand Down Expand Up @@ -37,6 +37,7 @@
(require 'f)
(require 'filenotify)
(require 'let-alist)
(require 'map)
(require 'pcase)
(require 'project)
(require 'python)
Expand Down Expand Up @@ -729,6 +730,149 @@ default otherwise."



(defvar eglot-workspace-configuration)
(declare-function jsonrpc--process "ext:jsonrpc")
(declare-function eglot--executable-find "ext:eglot")
(declare-function eglot--uri-to-path "ext:eglot")
(declare-function eglot--workspace-configuration-plist "ext:eglot")
(declare-function eglot--guess-contact "ext:eglot")

(defun pet-eglot--executable-find-advice (fn &rest args)
"Look up Python language servers using `pet-executable-find'.
FN is `eglot--executable-find', ARGS is the arguments to
`eglot--executable-find'."
(pcase-let ((`(,command . ,_) args))
(if (member command '("pylsp" "pyls" "pyright-langserver" "jedi-language-server"))
(pet-executable-find command)
(apply fn args))))

(defun pet-lookup-eglot-server-initialization-options (command)
"Return LSP initializationOptions for Eglot.
COMMAND is the name of the Python language server command."
(cond ((string-match-p "pylsp" command)
`(:pylsp
(:plugins
(:jedi
(:environment
,(pet-virtualenv-root))
:flake8
(:executable
,(pet-executable-find "flake8"))
:pylint
(:executable
,(pet-executable-find "pylint"))))))
((string-match-p "pyls" command)
`(:pyls
(:plugins
(:jedi
(:environment
,(pet-virtualenv-root))
:pylint
(:executable
,(pet-executable-find "pylint"))))))
((string-match-p "pyright-langserver" command)
`(:python
(:pythonPath
,(pet-executable-find "python")
:venvPath
,(pet-virtualenv-root))))
((string-match-p "jedi-language-server" command)
`(:jedi
(:executable
(:command
,(pet-executable-find "jedi-language-server"))
:workspace
(:environmentPath
,(pet-executable-find "python")))))
(t nil)))

(defalias 'pet--proper-list-p 'proper-list-p)
(eval-when-compile
(when (and (not (functionp 'proper-list-p))
(functionp 'format-proper-list-p))
(defun pet--proper-list-p (l)
(and (format-proper-list-p l)
(length l)))))

(defun pet--plistp (object)
"Non-nil if and only if OBJECT is a valid plist."
(let ((len (pet--proper-list-p object)))
(and len
(zerop (% len 2))
(seq-every-p
(lambda (kvp)
(keywordp (car kvp)))
(seq-split object 2)))))

(defun pet-merge-eglot-initialization-options (a b)
"Deep merge plists A and B."
(map-merge-with 'plist
(lambda (c d)
(cond ((and (pet--plistp c) (pet--plistp d))
(pet-merge-eglot-initialization-options c d))
((and (vectorp c) (vectorp d))
(vconcat (seq-union c d)))
(t d)))
(copy-tree a t)
(copy-tree b t)))

(defun pet-eglot--workspace-configuration-plist-advice (fn &rest args)
"Enrich `eglot-workspace-configuration' with paths found by `pet'.
FN is `eglot--workspace-configuration-plist', ARGS is the
arguments to `eglot--workspace-configuration-plist'."
(let* ((path (cadr args))
(canonical-path (if (and path (file-directory-p path))
(file-name-as-directory path)
path))
(server (car args))
(command (process-command (jsonrpc--process server)))
(program (and (listp command) (car command)))
(pet-config (pet-lookup-eglot-server-initialization-options program))
(user-config (apply fn server (and canonical-path (cons canonical-path (cddr args))))))
(pet-merge-eglot-initialization-options user-config pet-config)))

(defun pet-eglot--guess-contact-advice (fn &rest args)
"Enrich `eglot--guess-contact' with paths found by `pet'.
FN is `eglot--guess-contact', ARGS is the arguments to
`eglot--guess-contact'."
(let* ((result (apply fn args))
(contact (nth 3 result))
(probe (seq-position contact :initializationOptions))
(program-with-args (seq-subseq contact 0 (or probe (length contact))))
(program (car program-with-args))
(init-opts (plist-get (seq-subseq contact (or probe 0)) :initializationOptions)))
(if init-opts
(append (seq-subseq result 0 3)
(list
(append
program-with-args
(list
:initializationOptions
(pet-merge-eglot-initialization-options
init-opts
(pet-lookup-eglot-server-initialization-options
program)))))
(seq-subseq result 4))
result)))

(defun pet-eglot-setup ()
"Setup Eglot to use server executables and virtualenvs found by PET."
(advice-add 'eglot--executable-find :around #'pet-eglot--executable-find-advice)
(advice-add 'eglot--workspace-configuration-plist :around #'pet-eglot--workspace-configuration-plist-advice)
(advice-add 'eglot--guess-contact :around #'pet-eglot--guess-contact-advice))

(defun pet-eglot-teardown ()
"Setup PET advices to Eglot."
(advice-remove 'eglot--executable-find #'pet-eglot--executable-find-advice)
(advice-remove 'eglot--workspace-configuration-plist #'pet-eglot--workspace-configuration-plist-advice)
(advice-remove 'eglot--guess-contact #'pet-eglot--guess-contact-advice))



(defvar lsp-jedi-executable-command)
(defvar lsp-pyls-plugins-jedi-environment)
(defvar lsp-pylsp-plugins-jedi-environment)
Expand Down Expand Up @@ -762,7 +906,9 @@ buffer local values."
(setq-local python-black-command (pet-executable-find "black"))
(setq-local python-isort-command (pet-executable-find "isort"))
(setq-local blacken-executable python-black-command)
(setq-local yapfify-executable (pet-executable-find "yapf")))
(setq-local yapfify-executable (pet-executable-find "yapf"))

(pet-eglot-setup))

(defun pet-buffer-local-vars-teardown ()
"Reset all supported buffer local variable values to default."
Expand All @@ -782,7 +928,9 @@ buffer local values."
(kill-local-variable 'python-black-command)
(kill-local-variable 'python-isort-command)
(kill-local-variable 'blacken-executable)
(kill-local-variable 'yapfify-executable))
(kill-local-variable 'yapfify-executable)

(pet-eglot-teardown))

(defun pet-verify-setup ()
"Verify the values of buffer local variables visually.
Expand Down
Loading

0 comments on commit d903458

Please sign in to comment.