summaryrefslogtreecommitdiff
path: root/modules/prog-python.el
blob: 2eee7c505941d5d738773d42b07382ae2745d633 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
;;; prog-python --- Python Specific Setup and Functionality -*- lexical-binding: t; coding: utf-8; -*-
;; author Craig Jennings <c@cjennings.net>

;;; Commentary:
;; Python programming environment with LSP, tree-sitter, and formatting.
;;
;; Installation:
;;   pip install python-lsp-server[all] flake8
;;
;; LSP will provide:
;;   - Intelligent code completion
;;   - Jump to definition (M-.)
;;   - Find references
;;   - On-the-fly error checking (flake8)
;;   - Documentation on hover

;;; Code:

(defvar python-ts-mode-map)

;; Forward declarations for LSP
(declare-function lsp-deferred "lsp-mode")
(defvar lsp-pylsp-server-command)
(defvar lsp-pylsp-plugins-flake8-enabled)
(defvar lsp-pylsp-plugins-pylint-enabled)
(defvar lsp-pylsp-plugins-pycodestyle-enabled)
(defvar lsp-pylsp-plugins-autopep8-enabled)
(defvar lsp-pylsp-plugins-yapf-enabled)
(defvar lsp-pylsp-plugins-pydocstyle-enabled)
(defvar lsp-pylsp-plugins-rope-completion-enabled)

;; Forward declarations for external packages
(declare-function company-mode "company")
(declare-function cj/disabled "system-defaults")
(declare-function pdb "gud")
(defvar poetry-tracking-strategy)

(defvar pylsp-path "pylsp"
  "Path to Python LSP server (pylsp or pyright).
Install with: pip install python-lsp-server[all]
Or for pyright: pip install pyright")

(defvar mypy-path "mypy"
  "Path to mypy static type checker.
Install with: pip install mypy")

;; -------------------------------- Python Setup -------------------------------
;; preferences for Python programming

(defun cj/python-setup ()
  "My default code preferences for Python coding."
  (company-mode)                      ;; completion framework
  (flyspell-prog-mode)                ;; spell check comments
  (superword-mode)                    ;; see-this-as-one-word
  (setq-local fill-column 80)         ;; wrap at 80 columns
  (setq-local tab-width 4)            ;; set the tab width to 4 spaces
  (setq-local standard-indent 4)      ;; indent 4 spaces
  (setq-local indent-tabs-mode nil)   ;; disable tab characters
  (electric-pair-mode t)              ;; match delimiters automatically

  ;; Enable LSP if available
  (when (and (fboundp 'lsp-deferred)
             (executable-find pylsp-path))
    (lsp-deferred)))

(defun cj/python-mypy ()
  "Run mypy static type checker on the current Python file or directory."
  (interactive)
  (if (executable-find mypy-path)
      (let ((target (or (buffer-file-name) default-directory)))
        (compile (format "%s %s" mypy-path (shell-quote-argument target))))
    (message "mypy not found. Install with: pip install mypy")))

(defun cj/python-debug ()
  "Start Python debugger (pdb) on the current file."
  (interactive)
  (if buffer-file-name
      (pdb (format "python3 -m pdb %s" (shell-quote-argument buffer-file-name)))
    (message "No file associated with this buffer")))

(defun cj/python-mode-keybindings ()
  "Set up keybindings for Python programming.
Overrides default prog-mode keybindings with Python-specific commands."
  ;; S-f5: Run mypy (static type checking)
  (local-set-key (kbd "S-<f5>") #'cj/python-mypy)

  ;; S-f6: Debug with pdb
  (local-set-key (kbd "S-<f6>") #'cj/python-debug))

;; ----------------------------------- Python ----------------------------------
;; configuration for python-ts-mode (treesit-based Python editing)

(use-package python
  :ensure nil ;; built-in
  :hook
  ((python-ts-mode . cj/python-setup)
   (python-ts-mode . cj/python-mode-keybindings))
  :custom
  (python-shell-interpreter "python3")
  :config
  ;; remove the "guess indent" python message
  (setq python-indent-guess-indent-offset-verbose nil)
  ;; Remove python-mode from auto-mode-alist to prefer python-ts-mode
  (setq auto-mode-alist
        (rassq-delete-all 'python-mode auto-mode-alist)))

;; ------------------------------- LSP for Python ------------------------------
;; Python-specific LSP configuration
;; Core LSP setup is in prog-general.el

(use-package lsp-mode
  :hook (python-ts-mode . lsp-deferred)
  :config
  ;; Use pylsp (python-lsp-server) - more lightweight than pyright
  (setq lsp-pylsp-server-command pylsp-path)

  ;; Configure pylsp plugins
  (setq lsp-pylsp-plugins-flake8-enabled t)
  (setq lsp-pylsp-plugins-pylint-enabled nil)  ;; too slow
  (setq lsp-pylsp-plugins-pycodestyle-enabled nil)  ;; use flake8 instead
  (setq lsp-pylsp-plugins-autopep8-enabled nil)  ;; use blacken instead
  (setq lsp-pylsp-plugins-yapf-enabled nil)
  (setq lsp-pylsp-plugins-pydocstyle-enabled t)
  (setq lsp-pylsp-plugins-rope-completion-enabled t))

;; ----------------------------------- Poetry ----------------------------------
;; virtual environments and dependencies

(use-package poetry
  :defer t
  :after (python)
  :hook (python-ts-mode . poetry-tracking-mode)
  :config
  ;; Checks for the correct virtualenv. Better strategy IMO because the default
  ;; one is quite slow.
  (setq poetry-tracking-strategy 'switch-buffer))

;; ---------------------------------- Blacken ----------------------------------
;; formatting on save

(use-package blacken
  :custom
  (blacken-allow-py36 t)
  (blacken-skip-string-normalization t)
  :hook (python-ts-mode . blacken-mode)
  :bind (:map python-ts-mode-map
              ("<f6>" . blacken-buffer)
              ("C-; f" . blacken-buffer)))

;; ---------------------------------- Numpydoc ---------------------------------
;; automatically insert NumPy style docstrings in Python function definitions

(use-package numpydoc
  :custom
  (numpydoc-insert-examples-block nil)
  (numpydoc-template-long nil)
  :bind (:map python-ts-mode-map
			  ("C-c C-n" . numpydoc-generate)))

;; ------------------------------------ TOML -----------------------------------
;; editing support and documentation for TOML files

(use-package toml-mode
  :mode "\\.toml\\'")

(use-package eldoc-toml
  :hook (toml-mode . eldoc-toml-mode))


(provide 'prog-python)
;;; prog-python.el ends here