diff --git a/modules/lang/python/autoload/pyenv.el b/modules/lang/python/autoload/pyenv.el index a129cd5b4..9fd159832 100644 --- a/modules/lang/python/autoload/pyenv.el +++ b/modules/lang/python/autoload/pyenv.el @@ -7,7 +7,7 @@ ;;;###autoload (defun +python-pyenv-mode-set-auto-h () "Set pyenv-mode version from buffer-local variable." - (when (eq major-mode 'python-mode) + (when (memq major-mode '(python-mode python-ts-mode)) (when (not (local-variable-p '+pyenv--version)) (make-local-variable '+pyenv--version) (setq +pyenv--version (+python-pyenv-read-version-from-file))) diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el index 6739dc71d..1e61c960c 100644 --- a/modules/lang/python/config.el +++ b/modules/lang/python/config.el @@ -14,28 +14,38 @@ ;;; Packages (use-package! python - :mode ("[./]flake8\\'" . conf-mode) - :mode ("/Pipfile\\'" . conf-mode) + :mode ("/\\(?:Pipfile\\|\\.?flake8\\)\\'" . conf-mode) :init (setq python-environment-directory doom-cache-dir python-indent-guess-indent-offset-verbose nil) + (when (modulep! +tree-sitter) + (set-tree-sitter! 'python-mode 'python-ts-mode + '((python :url "https://github.com/tree-sitter/tree-sitter-python")))) + + :config + ;; HACK: `python-base-mode' (and `python-ts-mode') don't exist on pre-29 + ;; versions of Emacs, Rather than litter this module with conditionals, I + ;; shim the keymap in. + (unless (boundp 'python-base-mode-map) + (defvaralias 'python-base-mode-map 'python-mode-map)) + (when (modulep! +lsp) (add-hook 'python-mode-local-vars-hook #'lsp! 'append) + (add-hook 'python-ts-mode-local-vars-hook #'lsp! 'append) ;; Use "mspyls" in eglot if in PATH (when (executable-find "Microsoft.Python.LanguageServer") - (set-eglot-client! 'python-mode '("Microsoft.Python.LanguageServer")))) + (set-eglot-client! '(python-mode python-ts-mode) '("Microsoft.Python.LanguageServer")))) - (when (modulep! +tree-sitter) - (add-hook 'python-mode-local-vars-hook #'tree-sitter! 'append)) - :config - (set-repl-handler! 'python-mode #'+python/open-repl + (set-repl-handler! '(python-mode python-ts-mode) #'+python/open-repl :persist t :send-region #'python-shell-send-region :send-buffer #'python-shell-send-buffer) - (set-docsets! '(python-mode inferior-python-mode) "Python 3" "NumPy" "SciPy" "Pandas") - (set-ligatures! 'python-mode + (set-docsets! '(python-mode python-ts-mode inferior-python-mode) + "Python 3" "NumPy" "SciPy" "Pandas") + + (set-ligatures! '(python-mode python-ts-mode) ;; Functional :def "def" :lambda "lambda" @@ -62,7 +72,7 @@ (string= python-shell-interpreter "python")) (setq python-shell-interpreter "python3")) - (add-hook! 'python-mode-hook + (add-hook! '(python-mode-hook python-ts-mode-hook) (defun +python-use-correct-flycheck-executables-h () "Use the correct Python executables for Flycheck." (let ((executable python-shell-interpreter)) @@ -86,14 +96,25 @@ (advice-add #'pythonic-activate :after-while #'+modeline-update-env-in-all-windows-h) (advice-add #'pythonic-deactivate :after #'+modeline-clear-env-in-all-windows-h)) - (setq-hook! 'python-mode-hook tab-width python-indent-offset)) + ;; HACK: `python-mode' doesn't update `tab-width' to reflect + ;; `python-indent-offset', causing issues anywhere `tab-width' is respected. + (setq-hook! '(python-mode-hook python-ts-mode-hook) tab-width python-indent-offset) + + ;; HACK: `python-ts-mode' changes `auto-mode-alist' and + ;; `interpreter-mode-alist' every time the mode is activated, which runs the + ;; risk of overwriting user (or Doom) entries. + ;; REVIEW: Should be addressed upstream. + (defadvice! +python--undo-ts-side-effects-a (fn &rest args) + :around #'python-ts-mode + (let (auto-mode-alist interpreter-mode-alist) + (apply fn args)))) (use-package! pyimport :defer t :init (map! :after python - :map python-mode-map + :map python-base-mode-map :localleader :prefix ("i" . "imports") :desc "Insert missing imports" "i" #'pyimport-insert-missing @@ -105,12 +126,13 @@ :defer t :init (map! :after python - :map python-mode-map + :map python-base-mode-map :localleader (:prefix ("i" . "imports") :desc "Sort imports" "s" #'py-isort-buffer :desc "Sort region" "r" #'py-isort-region))) + (use-package! nose :commands nose-mode :preface (defvar nose-mode-map (make-sparse-keymap)) @@ -138,7 +160,7 @@ :init (map! :after python :localleader - :map python-mode-map + :map python-base-mode-map :prefix ("t" . "test") "a" #'python-pytest "f" #'python-pytest-file-dwim @@ -157,7 +179,7 @@ :hook (python-mode . pipenv-mode) :init (setq pipenv-with-projectile nil) :config - (set-eval-handler! 'python-mode + (set-eval-handler! '(python-mode python-ts-mode) '((:command . (lambda () python-shell-interpreter)) (:exec (lambda () (if-let* ((bin (executable-find "pipenv" t)) @@ -165,7 +187,7 @@ (format "PIPENV_MAX_DEPTH=9999 %s run %%c %%o %%s %%a" bin) "%c %o %s %a"))) (:description . "Run Python script"))) - (map! :map python-mode-map + (map! :map python-base-mode-map :localleader :prefix ("e" . "pipenv") :desc "activate" "a" #'pipenv-activate @@ -185,7 +207,8 @@ (add-hook 'pyvenv-post-activate-hooks #'+modeline-update-env-in-all-windows-h) (add-hook 'pyvenv-pre-deactivate-hooks #'+modeline-clear-env-in-all-windows-h)) :config - (add-hook 'python-mode-local-vars-hook #'pyvenv-track-virtualenv) + (add-hook! '(python-mode-local-vars-hook python-ts-mode-local-vars-hook) + #'pyvenv-track-virtualenv) (add-to-list 'global-mode-string '(pyvenv-virtual-env-name (" venv:" pyvenv-virtual-env-name " ")) 'append)) @@ -199,7 +222,8 @@ (when (executable-find "pyenv") (pyenv-mode +1) (add-to-list 'exec-path (expand-file-name "shims" (or (getenv "PYENV_ROOT") "~/.pyenv")))) - (add-hook 'python-mode-local-vars-hook #'+python-pyenv-mode-set-auto-h) + (add-hook! '(python-mode-local-vars-hook python-ts-mode-local-vars-hook) + #'+python-pyenv-mode-set-auto-h) (add-hook 'doom-switch-buffer-hook #'+python-pyenv-mode-set-auto-h)) diff --git a/modules/lang/python/doctor.el b/modules/lang/python/doctor.el index 65928df91..47af20fcd 100644 --- a/modules/lang/python/doctor.el +++ b/modules/lang/python/doctor.el @@ -8,6 +8,10 @@ (modulep! :tools tree-sitter)) "This module requires (:tools tree-sitter)") +(assert! (or (not (modulep! +tree-sitter)) + (fboundp 'python-ts-mode)) + "Can't find `python-ts-mode'; Emacs 29.1+ is required") + (if (not (or (executable-find "python") (executable-find "python3"))) (error! "Couldn't find python in your PATH") diff --git a/modules/tools/tree-sitter/config.el b/modules/tools/tree-sitter/config.el index e47845e87..f8d32059a 100644 --- a/modules/tools/tree-sitter/config.el +++ b/modules/tools/tree-sitter/config.el @@ -48,7 +48,6 @@ (org "https://github.com/milisims/tree-sitter-org" nil nil nil nil) (perl "https://github.com/ganezdragon/tree-sitter-perl" nil nil nil nil) (proto "https://github.com/mitchellh/tree-sitter-proto" nil nil nil nil) - (python "https://github.com/tree-sitter/tree-sitter-python" nil nil nil nil) (r "https://github.com/r-lib/tree-sitter-r" nil nil nil nil) (ruby "https://github.com/tree-sitter/tree-sitter-ruby" nil nil nil nil) (rust "https://github.com/tree-sitter/tree-sitter-rust" nil nil nil nil)