Skip to content

Commit

Permalink
experimental dir-locals
Browse files Browse the repository at this point in the history
  • Loading branch information
wyuenho committed Aug 25, 2023
1 parent eb0c013 commit 6b8459c
Show file tree
Hide file tree
Showing 2 changed files with 224 additions and 449 deletions.
251 changes: 66 additions & 185 deletions pet.el
Original file line number Diff line number Diff line change
Expand Up @@ -612,18 +612,6 @@ Selects a virtualenv in the follow order:



(defvar flycheck-mode)
(defvar flycheck-python-mypy-config)
(defvar flycheck-pylintrc)
(defvar flycheck-python-flake8-executable)
(defvar flycheck-python-pylint-executable)
(defvar flycheck-python-mypy-executable)
(defvar flycheck-python-pyright-executable)
(defvar flycheck-python-pycompile-executable)
(declare-function flycheck-string-or-nil-p "ext:flycheck")
(declare-function flycheck-register-option-var "ext:flycheck")
(declare-function flycheck-checker-get "ext:flycheck")

(defun pet-flycheck-python-pylint-find-pylintrc ()
"Polyfill `flycheck-pylintrc'.
Expand Down Expand Up @@ -659,145 +647,71 @@ algorithm described at
(and (file-exists-p home-dir-pylintrc) home-dir-pylintrc)))
(t "/etc/pylintrc")))))

(defun pet-flycheck-toggle-local-vars ()
"Toggle buffer local variables for `flycheck' Python checkers.
When `flycheck-mode' is non-nil, set up all supported Python
checker executable variables buffer-locally. Reset them to
default otherwise."
(if (bound-and-true-p flycheck-mode)
(progn
(when (derived-mode-p (if (functionp 'python-base-mode) 'python-base-mode 'python-mode))
(setq-local flycheck-python-mypy-config `("mypy.ini" ".mypy.ini" "pyproject.toml" "setup.cfg"
,(expand-file-name
(concat
(or (when-let ((xdg-config-home (getenv "XDG_CONFIG_HOME")))
(file-name-as-directory xdg-config-home))
"~/.config/")
"mypy/config"))))
(setq-local flycheck-pylintrc (pet-flycheck-python-pylint-find-pylintrc))
(setq-local flycheck-python-flake8-executable (pet-executable-find "flake8"))
(setq-local flycheck-python-pylint-executable (pet-executable-find "pylint"))
(setq-local flycheck-python-mypy-executable (pet-executable-find "mypy"))
(setq-local flycheck-python-mypy-python-executable (pet-executable-find "python"))
(setq-local flycheck-python-pyright-executable (pet-executable-find "pyright"))
(setq-local flycheck-python-pycompile-executable python-shell-interpreter)))
(kill-local-variable 'flycheck-python-mypy-config)
(kill-local-variable 'flycheck-pylintrc)
(kill-local-variable 'flycheck-python-flake8-executable)
(kill-local-variable 'flycheck-python-pylint-executable)
(kill-local-variable 'flycheck-python-mypy-executable)
(kill-local-variable 'flycheck-python-mypy-python-executable)
(kill-local-variable 'flycheck-python-pyright-executable)
(kill-local-variable 'flycheck-python-pycompile-executable)))

;;;###autoload
(defun pet-flycheck-setup ()
"Setup all `flycheck' Python checker configuration."

(add-hook 'flycheck-mode-hook #'pet-flycheck-toggle-local-vars))

;;;###autoload
(defun pet-flycheck-teardown ()
"Reset all `flycheck' Python checker configuration to default."

(remove-hook 'flycheck-mode-hook #'pet-flycheck-toggle-local-vars)
(kill-local-variable 'flycheck-python-mypy-config)
(kill-local-variable 'flycheck-pylintrc)
(kill-local-variable 'flycheck-python-flake8-executable)
(kill-local-variable 'flycheck-python-pylint-executable)
(kill-local-variable 'flycheck-python-mypy-executable)
(kill-local-variable 'flycheck-python-mypy-python-executable)
(kill-local-variable 'flycheck-python-pyright-executable)
(kill-local-variable 'flycheck-python-pycompile-executable))



;;;###autoload
(defun pet-eglot-setup ()
"Setup all `eglot' Python language server configuration."
(with-eval-after-load 'eglot
(declare-function eglot-alternatives 'eglot)
(setq-local eglot-server-programs
(cons
`((python-mode python-ts-mode)
. ,(eglot-alternatives
(mapcar
(lambda (server)
(if (string-suffix-p "pyright-langserver" server)
`(,server "--stdio")
server))
(mapcar (lambda (server)
(pet-executable-find server))
'("pylsp"
"pyls"
"pyright"
"jedi-language-server")))))
eglot-server-programs))))

;;;###autoload
(defun pet-eglot-teardown ()
"Reset all `eglot' Python language server configuration to default."
(kill-local-variable 'eglot-server-programs))



(defvar lsp-jedi-executable-command)
(defvar lsp-pyls-plugins-jedi-environment)
(defvar lsp-pylsp-plugins-jedi-environment)
(defvar lsp-pyright-python-executable-cmd)
(defvar lsp-pyright-venv-path)
(defvar dap-python-executable)
(defvar python-pytest-executable)
(defvar python-black-command)
(defvar python-isort-command)
(defvar blacken-executable)
(defvar yapfify-executable)

(defun pet-buffer-local-vars-setup ()
"Setup the buffer local variables for Python tools.
Assign all supported Python tooling executable variables to
buffer local values."
(setq-local python-shell-interpreter (pet-executable-find "python"))
(setq-local python-shell-virtualenv-root (pet-virtualenv-root))

(pet-flycheck-setup)
(pet-eglot-setup)

(setq-local lsp-jedi-executable-command
(pet-executable-find "jedi-language-server"))
(setq-local lsp-pyls-plugins-jedi-environment python-shell-virtualenv-root)
(setq-local lsp-pylsp-plugins-jedi-environment python-shell-virtualenv-root)
(setq-local lsp-pyright-venv-path python-shell-virtualenv-root)
(setq-local lsp-pyright-python-executable-cmd python-shell-interpreter)
(setq-local dap-python-executable python-shell-interpreter)
(setq-local python-pytest-executable (pet-executable-find "pytest"))
(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")))

(defun pet-buffer-local-vars-teardown ()
"Reset all supported buffer local variable values to default."

(kill-local-variable 'python-shell-interpreter)
(kill-local-variable 'python-shell-virtualenv-root)

(pet-flycheck-teardown)
(pet-eglot-teardown)

(kill-local-variable 'lsp-jedi-executable-command)
(kill-local-variable 'lsp-pyls-plugins-jedi-environment)
(kill-local-variable 'lsp-pylsp-plugins-jedi-environment)
(kill-local-variable 'lsp-pyright-venv-path)
(kill-local-variable 'lsp-pyright-python-executable-cmd)
(kill-local-variable 'dap-python-executable)
(kill-local-variable 'python-pytest-executable)
(kill-local-variable 'python-black-command)
(kill-local-variable 'python-isort-command)
(kill-local-variable 'blacken-executable)
(kill-local-variable 'yapfify-executable))
(defun pet-buffer-local-vars ()
"Construct the local variable values for Python tools.
The format of the returned variables alist is defined in
`dir-locals-set-class-variables'."

(let* ((virtualenv-root (pet-virtualenv-root))
(interpreter (pet-executable-find "python"))
(black-executable (pet-executable-find "black"))
(vars `((python-shell-interpreter . ,interpreter)
(python-shell-virtualenv-root . ,virtualenv-root)
(flycheck-python-mypy-config . ("mypy.ini" ".mypy.ini" "pyproject.toml" "setup.cfg"
,(expand-file-name
(concat
(or (when-let ((xdg-config-home (getenv "XDG_CONFIG_HOME")))
(file-name-as-directory xdg-config-home))
"~/.config/")
"mypy/config"))))
(flycheck-pylintrc . ,(pet-flycheck-python-pylint-find-pylintrc))
(flycheck-python-flake8-executable . ,(pet-executable-find "flake8"))
(flycheck-python-pylint-executable . ,(pet-executable-find "pylint"))
(flycheck-python-mypy-executable . ,(pet-executable-find "mypy"))
(flycheck-python-mypy-python-executable . ,interpreter)
(flycheck-python-pyright-executable . ,(pet-executable-find "pyright"))
(flycheck-python-pycompile-executable . ,interpreter)
(lsp-jedi-executable-command . ,(pet-executable-find "jedi-language-server"))
(lsp-pyls-plugins-jedi-environment . ,virtualenv-root)
(lsp-pylsp-plugins-jedi-environment . ,virtualenv-root)
(lsp-pyright-venv-path . ,virtualenv-root)
(lsp-pyright-python-executable-cmd . ,interpreter)
(dap-python-executable . ,interpreter)
(python-pytest-executable . ,(pet-executable-find "pytest"))
(python-black-command . ,black-executable)
(python-isort-command . ,(pet-executable-find "isort"))
(blacken-executable . ,black-executable)
(yapfify-executable . ,(pet-executable-find "yapf")))))
`((python-mode . ,vars)
(python-ts-mode . ,vars))))

(defun pet-dir-locals-directory-class ()
"Generate a dir locals class name for the current project.
The class name is unique per project and is used to set the class
value for `dir-locals-set-directory-class'."
(intern (concat "pet-" (replace-regexp-in-string "\\(/\\|\[[:blank:]\]\\)+" "-" (pet-project-root)))))

(defun pet-buffer-local-vars-setup()
"Activate the local variables."
(when-let ((cls (pet-dir-locals-directory-class))
(root (pet-project-root)))
(dir-locals-set-class-variables cls (pet-buffer-local-vars))
(unless (eq (car (assoc-default root dir-locals-directory-cache)) cls)
(dir-locals-set-directory-class root cls))
(hack-local-variables)))

(defun pet-buffer-local-vars-teardown()
"Deactivate the local variables."
(when-let ((cls (pet-dir-locals-directory-class))
(root (pet-project-root)))
(cl-loop for (k . _) in (cdr (car (assoc-default cls dir-locals-class-alist)))
do (progn
(kill-local-variable k)
(setf (alist-get k dir-local-variables-alist nil t) nil)))
(setf (alist-get root dir-locals-directory-cache nil t 'equal) nil)
(hack-local-variables)))

(defun pet-verify-setup ()
"Verify the values of buffer local variables visually.
Expand All @@ -820,47 +734,14 @@ has assigned to."
(list ", "))
(abbreviate-file-name (format "%s" val))))
'unbound)))
'(python-shell-interpreter
python-shell-virtualenv-root
flycheck-python-flake8-executable
flycheck-pylintrc
flycheck-python-pylint-executable
flycheck-python-mypy-executable
flycheck-python-mypy-config
flycheck-python-mypy-python-executable
flycheck-python-pyright-executable
flycheck-python-pycompile-executable
lsp-jedi-executable-command
lsp-pyls-plugins-jedi-environment
lsp-pylsp-plugins-jedi-environment
lsp-pyright-python-executable-cmd
lsp-pyright-venv-path
dap-python-executable
python-pytest-executable
python-black-command
blacken-executable
python-isort-command
yapfify-executable))))
(mapcar 'car (cdr (car (assoc-default (pet-dir-locals-directory-class) dir-locals-class-alist)))))))

(with-current-buffer-window "*pet info*" nil nil
(mapc (pcase-lambda (`(,key . ,value))
(insert (propertize (format "%-40s" (concat (symbol-name key) ":")) 'face 'font-lock-variable-name-face))
(insert (format "%s" value))
(insert "\n"))
kvp)
(insert (propertize (format "%-40s" (concat (symbol-name 'eglot-server-programs) " (Python part only):"))
'face 'font-lock-variable-name-face) "\n")
(if (null (featurep 'eglot))
(insert "(not available because eglot is not loaded now)\n")
(defvar eglot-server-programs)
(let* ((matcher (lambda (modes mode)
(apply #'provided-mode-derived-p
mode
(if (consp modes) modes (list modes)))))
(matched (alist-get 'python-mode eglot-server-programs
nil nil matcher))
(langservers (funcall matched)))
(mapc (lambda (langserver) (insert (abbreviate-file-name (format "%s" langserver)) "\n")) langservers)))
(insert (propertize (format "%-40s" (concat (symbol-name 'exec-path) ":")) 'face 'font-lock-variable-name-face) "\n")
(mapc (lambda (dir) (insert (abbreviate-file-name (format "%s" dir)) "\n")) exec-path)
(special-mode))))
Expand Down
Loading

0 comments on commit 6b8459c

Please sign in to comment.