Coverage for src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py: 26%
65 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-07 12:14 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-07 12:14 +0200
1import textwrap
2from argparse import BooleanOptionalAction
4from debputy.commands.debputy_cmd.context import ROOT_COMMAND, CommandContext, add_arg
5from debputy.util import _error
8_EDITOR_SNIPPETS = {
9 "emacs": "emacs+eglot",
10 "emacs+eglot": textwrap.dedent(
11 """\
12 ;; `deputy lsp server` glue for emacs eglot (eglot is built-in these days)
13 ;;
14 ;; Add to ~/.emacs or ~/.emacs.d/init.el and then activate via `M-x eglot`.
15 ;;
16 ;; Requires: apt install elpa-dpkg-dev-el elpa-yaml-mode
17 ;; Recommends: apt install elpa-markdown-mode
19 ;; Make emacs recognize debian/debputy.manifest as a YAML file
20 (add-to-list 'auto-mode-alist '("/debian/debputy.manifest\\'" . yaml-mode))
21 ;; Inform eglot about the debputy LSP
22 (with-eval-after-load 'eglot
23 (add-to-list 'eglot-server-programs
24 '(debian-control-mode . ("debputy" "lsp" "server")))
25 (add-to-list 'eglot-server-programs
26 '(debian-changelog-mode . ("debputy" "lsp" "server")))
27 (add-to-list 'eglot-server-programs
28 '(debian-copyright-mode . ("debputy" "lsp" "server")))
29 ;; Requires elpa-dpkg-dev-el (>> 37.11)
30 ;; (add-to-list 'eglot-server-programs
31 ;; '(debian-autopkgtest-control-mode . ("debputy" "lsp" "server")))
32 ;; The debian/rules file uses the qmake mode.
33 (add-to-list 'eglot-server-programs
34 '(makefile-gmake-mode . ("debputy" "lsp" "server")))
35 (add-to-list 'eglot-server-programs
36 '(yaml-mode . ("debputy" "lsp" "server")))
37 )
39 ;; Auto-start eglot for the relevant modes.
40 (add-hook 'debian-control-mode-hook 'eglot-ensure)
41 ;; NOTE: changelog disabled by default because for some reason it
42 ;; this hook causes perceivable delay (several seconds) when
43 ;; opening the first changelog. It seems to be related to imenu.
44 ;; (add-hook 'debian-changelog-mode-hook 'eglot-ensure)
45 (add-hook 'debian-copyright-mode-hook 'eglot-ensure)
46 ;; Requires elpa-dpkg-dev-el (>> 37.11)
47 ;; (add-hook 'debian-autopkgtest-control-mode-hook 'eglot-ensure)
48 (add-hook 'makefile-gmake-mode-hook 'eglot-ensure)
49 (add-hook 'yaml-mode-hook 'eglot-ensure)
50 """
51 ),
52 "vim": "vim+youcompleteme",
53 "vim+youcompleteme": textwrap.dedent(
54 """\
55 # debputy lsp server glue for vim with vim-youcompleteme. Add to ~/.vimrc
56 #
57 # Requires: apt install vim-youcompleteme
59 # Make vim recognize debputy.manifest as YAML file
60 au BufNewFile,BufRead debputy.manifest setf yaml
61 # Inform vim/ycm about the debputy LSP
62 # - NB: No known support for debian/tests/control that we can hook into.
63 # Feel free to provide one :)
64 let g:ycm_language_server = [
65 \\ { 'name': 'debputy',
66 \\ 'filetypes': [ 'debcontrol', 'debcopyright', 'debchangelog', 'make', 'yaml'],
67 \\ 'cmdline': [ 'debputy', 'lsp', 'server' ]
68 \\ },
69 \\ ]
71 packadd! youcompleteme
72 # Add relevant ycm keybinding such as:
73 # nmap <leader>d <plug>(YCMHover)
74 """
75 ),
76}
79lsp_command = ROOT_COMMAND.add_dispatching_subcommand(
80 "lsp",
81 dest="lsp_command",
82 help_description="Language server related subcommands",
83)
86@lsp_command.register_subcommand(
87 "server",
88 log_only_to_stderr=True,
89 help_description="Start the language server",
90 argparser=[
91 add_arg(
92 "--tcp",
93 action="store_true",
94 help="Use TCP server",
95 ),
96 add_arg(
97 "--ws",
98 action="store_true",
99 help="Use WebSocket server",
100 ),
101 add_arg(
102 "--host",
103 default="127.0.0.1",
104 help="Bind to this address (Use with --tcp / --ws)",
105 ),
106 add_arg(
107 "--port",
108 type=int,
109 default=2087,
110 help="Bind to this port (Use with --tcp / --ws)",
111 ),
112 ],
113)
114def lsp_server_cmd(context: CommandContext) -> None:
115 parsed_args = context.parsed_args
117 try:
118 import lsprotocol
119 import pygls
120 except ImportError:
121 _error(
122 "This feature requires lsprotocol and pygls (apt-get install python3-lsprotocol python3-pygls)"
123 )
125 feature_set = context.load_plugins()
127 from debputy.lsp.lsp_features import (
128 ensure_lsp_features_are_loaded,
129 )
130 from debputy.lsp.lsp_dispatch import DEBPUTY_LANGUAGE_SERVER
132 ensure_lsp_features_are_loaded()
133 debputy_language_server = DEBPUTY_LANGUAGE_SERVER
134 debputy_language_server.plugin_feature_set = feature_set
136 if parsed_args.tcp:
137 debputy_language_server.start_tcp(parsed_args.host, parsed_args.port)
138 elif parsed_args.ws:
139 debputy_language_server.start_ws(parsed_args.host, parsed_args.port)
140 else:
141 debputy_language_server.start_io()
144@lsp_command.register_subcommand(
145 "editor-config",
146 help_description="Provide editor configuration snippets",
147 argparser=[
148 add_arg(
149 "editor_name",
150 metavar="editor",
151 choices=_EDITOR_SNIPPETS,
152 default=None,
153 nargs="?",
154 help="The editor to provide a snippet for",
155 ),
156 ],
157)
158def lsp_editor_glue(context: CommandContext) -> None:
159 editor_name = context.parsed_args.editor_name
161 if editor_name is None:
162 content = []
163 for editor_name, payload in _EDITOR_SNIPPETS.items():
164 alias_of = ""
165 if payload in _EDITOR_SNIPPETS:
166 alias_of = f" (short for: {payload})"
167 content.append((editor_name, alias_of))
168 max_name = max(len(c[0]) for c in content)
169 print("This version of debputy has editor snippets for the following editors: ")
170 for editor_name, alias_of in content:
171 print(f" * {editor_name:<{max_name}}{alias_of}")
172 return
173 result = _EDITOR_SNIPPETS[editor_name]
174 while result in _EDITOR_SNIPPETS:
175 result = _EDITOR_SNIPPETS[result]
176 print(result)
179@lsp_command.register_subcommand(
180 "features",
181 help_description="Describe language ids and features",
182)
183def lsp_editor_glue(_context: CommandContext) -> None:
184 try:
185 import lsprotocol
186 import pygls
187 except ImportError:
188 _error(
189 "This feature requires lsprotocol and pygls (apt-get install python3-lsprotocol python3-pygls)"
190 )
192 from debputy.lsp.lsp_features import describe_lsp_features
194 describe_lsp_features()
197@ROOT_COMMAND.register_subcommand(
198 "lint",
199 log_only_to_stderr=True,
200 argparser=[
201 add_arg(
202 "--spellcheck",
203 dest="spellcheck",
204 action="store_true",
205 shared=True,
206 help="Enable spellchecking",
207 ),
208 add_arg(
209 "--auto-fix",
210 dest="auto_fix",
211 action="store_true",
212 shared=True,
213 help="Automatically fix problems with trivial or obvious corrections.",
214 ),
215 add_arg(
216 "--linter-exit-code",
217 dest="linter_exit_code",
218 default=True,
219 action=BooleanOptionalAction,
220 help='Enable or disable the "linter" convention of exiting with an error if severe issues were found',
221 ),
222 ],
223)
224def lint_cmd(context: CommandContext) -> None:
225 try:
226 import lsprotocol
227 except ImportError:
228 _error("This feature requires lsprotocol (apt-get install python3-lsprotocol)")
230 from debputy.linting.lint_impl import perform_linting
232 context.must_be_called_in_source_root()
233 perform_linting(context)
236def ensure_lint_and_lsp_commands_are_loaded():
237 # Loading the module does the heavy lifting
238 # However, having this function means that we do not have an "unused" import that some tool
239 # gets tempted to remove
240 assert ROOT_COMMAND.has_command("lsp")
241 assert ROOT_COMMAND.has_command("lint")