-
Notifications
You must be signed in to change notification settings - Fork 25
/
lsp-pyright.el
335 lines (285 loc) · 13.8 KB
/
lsp-pyright.el
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
;;; lsp-pyright.el --- Python LSP client using Pyright -*- lexical-binding: t; -*-
;; Copyright (C) 2020 emacs-lsp maintainers
;; Author: Arif Rezai, Vincent Zhang, Andrew Christianson
;; Version: 0.3.0
;; Package-Requires: ((emacs "26.1") (lsp-mode "7.0") (dash "2.18.0") (ht "2.0"))
;; Homepage: https://github.com/emacs-lsp/lsp-pyright
;; Keywords: languages, tools, lsp
;; This file is not part of GNU Emacs
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; For a full copy of the GNU General Public License
;; see <https://www.gnu.org/licenses/>.
;;
;;; Commentary:
;;
;; Pyright language server.
;;
;;; Code:
(require 'lsp-mode)
(require 'dash)
(require 'ht)
(require 'cl-lib)
;; Group declaration
(defgroup lsp-pyright nil
"LSP support for python using the Pyright Language Server."
:group 'lsp-mode
:link '(url-link "https://github.com/microsoft/pyright")
:link '(url-link "https://github.com/DetachHead/basedpyright"))
(defcustom lsp-pyright-langserver-command "pyright"
"Choose whether to use Pyright or the BasedPyright fork."
:type '(choice
(const "pyright")
(const "basedpyright"))
:group 'lsp-pyright)
(defcustom lsp-pyright-langserver-command-args '("--stdio")
"Command to start pyright-langserver."
:type '(repeat string)
:group 'lsp-pyright)
(defcustom lsp-pyright-disable-language-services nil
"Disables all language services except for \"hover\"."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-disable-organize-imports nil
"Disables the \"Organize Imports\" command."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-disable-tagged-hints nil
"Disables grayed out special hint diagnostics tags."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-diagnostic-mode "openFilesOnly"
"Determines pyright diagnostic mode.
Whether pyright analyzes (and reports errors for) all files
in the workspace, as indicated by the config file.
If this option is set to \"openFilesOnly\", pyright analyzes only open files."
:type '(choice
(const "openFilesOnly")
(const "workspace"))
:group 'lsp-pyright)
(defcustom lsp-pyright-type-checking-mode "standard"
"Specifies the default rule set to use.
\"off\" disables all type-checking rules, but Python syntax and semantic
errors are still reported. \"all\" reports all errors in basedpyright,
but is not supported by pyright. "
:type '(choice
(const :tag "Off" "off")
(const :tag "Basic" "basic")
(const :tag "Standard" "standard")
(const :tag "Strict" "strict")
(const :tag "All (basedpyright only)" "all"))
:group 'lsp-pyright)
(defcustom lsp-pyright-log-level "info"
"Determines the default log level used by pyright.
This can be overridden in the configuration file."
:type '(choice
(const "error")
(const "warning")
(const "info")
(const "trace"))
:group 'lsp-pyright)
(defcustom lsp-pyright-auto-search-paths t
"Determines whether pyright automatically adds common search paths.
i.e: Paths like \"src\" if there are no execution environments defined in the
config file."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-extra-paths []
"Paths to add to the default execution environment extra paths.
If there are no execution environments defined in the config file."
:type 'lsp-string-vector
:group 'lsp-pyright)
(make-variable-buffer-local 'lsp-pyright-extra-paths)
(defcustom lsp-pyright-auto-import-completions t
"Determines whether pyright offers auto-import completions."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-venv-path nil
"Path to folder with subdirectories that contain virtual environments.
Virtual Envs specified in pyrightconfig.json will be looked up in this path."
:type '(choice (const :tag "None" nil) file)
:group 'lsp-pyright)
(defcustom lsp-pyright-venv-directory nil
"Folder with subdirectories that contain virtual environments.
Virtual Envs specified in pyrightconfig.json will be looked up in this path."
:type '(choice (const :tag "None" nil) directory)
:group 'lsp-pyright)
(defcustom lsp-pyright-multi-root t
"If non nil, lsp-pyright will be started in multi-root mode."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-python-executable-cmd "python"
"Command to specify the Python command for pyright.
Similar to the `python-shell-interpreter', but used only with mspyls.
Useful when there are multiple python versions in system.
e.g, there are `python2' and `python3', both in system PATH,
and the default `python' links to python2,
set as `python3' to let ms-pyls use python 3 environments."
:type 'string
:group 'lsp-pyright)
(defcustom lsp-pyright-prefer-remote-env t
"If non nil, lsp-pyright will prefer remote python environment.
Only available in Emacs 27 and above."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-python-search-functions
'(lsp-pyright--locate-python-venv
lsp-pyright--locate-python-python)
"List of functions to search for python executable."
:type 'list
:group 'lsp-pyright)
(defcustom lsp-pyright-diagnostic-severity-overrides '()
"Overrides certain rules severity leve l"
:group 'lsp-pyright
:type `(alist :key-type (string :tag "Rule Name")
:value-type (choice
(const :tag "No Error" "none")
(const :tag "Warning""warning")
(const :tag "Information" "information")
(const :tag "Error" "error")
(const :tag "An error is generated" t)
(const :tag "No error is generated" ,json-false))))
(defcustom lsp-pyright-basedpyright-inlay-hints-variable-types t
"Whether to show inlay hints on assignments to variables.
Basedpyright only."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-basedpyright-inlay-hints-call-argument-names t
"Whether to show inlay hints on function arguments.
Basedpyright only."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-basedpyright-inlay-hints-function-return-types t
"Whether to show inlay hints on function return types.
Basedpyright only."
:type 'boolean
:group 'lsp-pyright)
(defcustom lsp-pyright-basedpyright-inlay-hints-generic-types nil
"Whether to show inlay hints on inferred generic types.
Basedpyright only."
:type 'boolean
:group 'lsp-pyright)
(defun lsp-pyright--locate-venv ()
"Look for virtual environments local to the workspace."
(or lsp-pyright-venv-path
(and lsp-pyright-venv-directory
(-when-let (venv-base-directory (locate-dominating-file default-directory lsp-pyright-venv-directory))
(concat venv-base-directory lsp-pyright-venv-directory)))
(-when-let (venv-base-directory (locate-dominating-file default-directory "venv/"))
(concat venv-base-directory "venv"))
(-when-let (venv-base-directory (locate-dominating-file default-directory ".venv/"))
(concat venv-base-directory ".venv"))))
(defun lsp-pyright--locate-python-venv ()
"Find a python executable based on the current virtual environment."
(executable-find (f-expand "bin/python" (lsp-pyright--locate-venv))))
(defun lsp-pyright--locate-python-python ()
"Find a python executable based on the version of python on the PATH."
(with-no-warnings
(if (>= emacs-major-version 27)
(executable-find lsp-pyright-python-executable-cmd lsp-pyright-prefer-remote-env)
(executable-find lsp-pyright-python-executable-cmd))))
(defun lsp-pyright-locate-python ()
"Find a python executable cmd for the workspace."
(cl-some #'funcall lsp-pyright-python-search-functions))
(defun lsp-pyright--begin-progress-callback (workspace &rest _)
"Log begin progress information.
Current LSP WORKSPACE should be passed in."
(when lsp-progress-via-spinner
(with-lsp-workspace workspace
(--each (lsp--workspace-buffers workspace)
(when (buffer-live-p it)
(with-current-buffer it
(lsp--spinner-start))))))
(lsp-log "Pyright language server is analyzing..."))
(defun lsp-pyright--report-progress-callback (_workspace params)
"Log report progress information.
First element of PARAMS will be passed into `lsp-log'."
(when (and (arrayp params) (> (length params) 0))
(lsp-log (aref params 0))))
(defun lsp-pyright--end-progress-callback (workspace &rest _)
"Log end progress information.
Current LSP WORKSPACE should be passed in."
(when lsp-progress-via-spinner
(with-lsp-workspace workspace
(--each (lsp--workspace-buffers workspace)
(when (buffer-live-p it)
(with-current-buffer it
(lsp--spinner-stop))))))
(lsp-log "Pyright language server is analyzing...done"))
(defun lsp-pyright-organize-imports ()
"Organize imports in current buffer."
(interactive)
(lsp-send-execute-command (concat lsp-pyright-langserver-command ".organizeimports")
(vector (concat "file://" (buffer-file-name)))))
(lsp-register-custom-settings
`((,(concat lsp-pyright-langserver-command ".disableLanguageServices") lsp-pyright-disable-language-services t)
(,(concat lsp-pyright-langserver-command ".disableOrganizeImports") lsp-pyright-disable-organize-imports t)
(,(concat lsp-pyright-langserver-command ".disableTaggedHints") lsp-pyright-disable-tagged-hints t)
(,(concat lsp-pyright-langserver-command ".typeCheckingMode") lsp-pyright-type-checking-mode)
(,(concat lsp-pyright-langserver-command ".analysis.diagnosticSeverityOverrides") (lambda () (ht-from-alist lsp-pyright-diagnostic-severity-overrides)))
("python.analysis.diagnosticSeverityOverrides" (lambda () (ht-from-alist lsp-pyright-diagnostic-severity-overrides)))
("basedpyright.analysis.inlayHints.variableTypes" lsp-pyright-basedpyright-inlay-hints-variable-types t)
("basedpyright.analysis.inlayHints.callArgumentNames" lsp-pyright-basedpyright-inlay-hints-call-argument-names t)
("basedpyright.analysis.inlayHints.functionReturnTypes" lsp-pyright-basedpyright-inlay-hints-function-return-types t)
("basedpyright.analysis.inlayHints.genericTypes" lsp-pyright-basedpyright-inlay-hints-generic-types t)
("python.analysis.typeCheckingMode" lsp-pyright-type-checking-mode)
("python.analysis.autoImportCompletions" lsp-pyright-auto-import-completions t)
("python.analysis.diagnosticMode" lsp-pyright-diagnostic-mode)
("python.analysis.logLevel" lsp-pyright-log-level)
("python.analysis.autoSearchPaths" lsp-pyright-auto-search-paths t)
("python.analysis.extraPaths" lsp-pyright-extra-paths)
("python.pythonPath" lsp-pyright-locate-python)
;; We need to send empty string, otherwise pyright-langserver fails with parse error
("python.venvPath" (lambda () (or lsp-pyright-venv-path "")))))
(lsp-dependency 'pyright
`(:system ,(concat lsp-pyright-langserver-command "-langserver"))
`(:npm :package ,lsp-pyright-langserver-command
:path ,(concat lsp-pyright-langserver-command "-langserver")))
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection (lambda ()
(cons (lsp-package-path 'pyright)
lsp-pyright-langserver-command-args)))
:major-modes '(python-mode python-ts-mode)
:server-id 'pyright
:multi-root lsp-pyright-multi-root
:priority 2
:initialized-fn (lambda (workspace)
(with-lsp-workspace workspace
;; we send empty settings initially, LSP server will ask for the
;; configuration of each workspace folder later separately
(lsp--set-configuration
(make-hash-table :test 'equal))))
:download-server-fn (lambda (_client callback error-callback _update?)
(lsp-package-ensure 'pyright callback error-callback))
:notification-handlers (lsp-ht ((concat lsp-pyright-langserver-command "/beginProgress") 'lsp-pyright--begin-progress-callback)
((concat lsp-pyright-langserver-command "/reportProgress") 'lsp-pyright--report-progress-callback)
((concat lsp-pyright-langserver-command "/endProgress") 'lsp-pyright--end-progress-callback))))
(lsp-register-client
(make-lsp-client
:new-connection
(lsp-tramp-connection (lambda ()
(cons (executable-find (concat lsp-pyright-langserver-command "-langserver") t)
lsp-pyright-langserver-command-args)))
:major-modes '(python-mode python-ts-mode)
:server-id 'pyright-remote
:multi-root lsp-pyright-multi-root
:remote? t
:priority 1
:initialized-fn (lambda (workspace)
(with-lsp-workspace workspace
;; we send empty settings initially, LSP server will ask for the
;; configuration of each workspace folder later separately
(lsp--set-configuration
(make-hash-table :test 'equal))))
:notification-handlers (lsp-ht ((concat lsp-pyright-langserver-command "/beginProgress") 'lsp-pyright--begin-progress-callback)
((concat lsp-pyright-langserver-command "/reportProgress") 'lsp-pyright--report-progress-callback)
((concat lsp-pyright-langserver-command "/endProgress") 'lsp-pyright--end-progress-callback))))
(provide 'lsp-pyright)
;;; lsp-pyright.el ends here