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

1import textwrap 

2from argparse import BooleanOptionalAction 

3 

4from debputy.commands.debputy_cmd.context import ROOT_COMMAND, CommandContext, add_arg 

5from debputy.util import _error 

6 

7 

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 

18 

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 ) 

38 

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 

58 

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 \\ ] 

70 

71 packadd! youcompleteme 

72 # Add relevant ycm keybinding such as: 

73 # nmap <leader>d <plug>(YCMHover) 

74 """ 

75 ), 

76} 

77 

78 

79lsp_command = ROOT_COMMAND.add_dispatching_subcommand( 

80 "lsp", 

81 dest="lsp_command", 

82 help_description="Language server related subcommands", 

83) 

84 

85 

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 

116 

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 ) 

124 

125 feature_set = context.load_plugins() 

126 

127 from debputy.lsp.lsp_features import ( 

128 ensure_lsp_features_are_loaded, 

129 ) 

130 from debputy.lsp.lsp_dispatch import DEBPUTY_LANGUAGE_SERVER 

131 

132 ensure_lsp_features_are_loaded() 

133 debputy_language_server = DEBPUTY_LANGUAGE_SERVER 

134 debputy_language_server.plugin_feature_set = feature_set 

135 

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() 

142 

143 

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 

160 

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) 

177 

178 

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 ) 

191 

192 from debputy.lsp.lsp_features import describe_lsp_features 

193 

194 describe_lsp_features() 

195 

196 

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)") 

229 

230 from debputy.linting.lint_impl import perform_linting 

231 

232 context.must_be_called_in_source_root() 

233 perform_linting(context) 

234 

235 

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")