summaryrefslogtreecommitdiffstats
path: root/src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py')
-rw-r--r--src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py b/src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py
new file mode 100644
index 0000000..0f2ae0f
--- /dev/null
+++ b/src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py
@@ -0,0 +1,210 @@
+import textwrap
+from argparse import BooleanOptionalAction
+
+from debputy.commands.debputy_cmd.context import ROOT_COMMAND, CommandContext, add_arg
+from debputy.util import _error
+
+
+_EDITOR_SNIPPETS = {
+ "emacs": "emacs+eglot",
+ "emacs+eglot": textwrap.dedent(
+ """\
+ ;; `deputy lsp server` glue for emacs eglot (eglot is built-in these days)
+ ;;
+ ;; Add to ~/.emacs or ~/.emacs.d/init.el and then activate via `M-x eglot`.
+ ;;
+ ;; Requires: apt install elpa-dpkg-dev-el
+
+ ;; Make emacs recognize debian/debputy.manifest as a YAML file
+ (add-to-list 'auto-mode-alist '("/debian/debputy.manifest\\'" . yaml-mode))
+ ;; Inform eglot about the debputy LSP
+ (with-eval-after-load 'eglot
+ (add-to-list 'eglot-server-programs
+ '(debian-control-mode . ("debputy" "lsp" "server")))
+ (add-to-list 'eglot-server-programs
+ '(debian-changelog-mode . ("debputy" "lsp" "server")))
+ (add-to-list 'eglot-server-programs
+ '(debian-copyright-mode . ("debputy" "lsp" "server")))
+ ;; The debian/rules file uses the qmake mode.
+ (add-to-list 'eglot-server-programs
+ '(makefile-gmake-mode . ("debputy" "lsp" "server")))
+ )
+
+ ;; Auto-start eglot for the relevant modes.
+ (add-hook 'debian-control-mode-hook 'eglot-ensure)
+ ;; NOTE: changelog disabled by default because for some reason it
+ ;; this hook causes perceivable delay (several seconds) when
+ ;; opening the first changelog. It seems to be related to imenu.
+ ;; (add-hook 'debian-changelog-mode-hook 'eglot-ensure)
+ (add-hook 'debian-copyright-mode-hook 'eglot-ensure)
+ (add-hook 'makefile-gmake-mode-hook 'eglot-ensure)
+ """
+ ),
+ "vim": "vim+youcompleteme",
+ "vim+youcompleteme": textwrap.dedent(
+ """\
+ # debputy lsp server glue for vim with vim-youcompleteme. Add to ~/.vimrc
+ #
+ # Requires: apt install vim-youcompleteme
+
+ # Make vim recognize debputy.manifest as YAML file
+ au BufNewFile,BufRead debputy.manifest setf yaml
+ # Inform vim/ycm about the debputy LSP
+ let g:ycm_language_server = [
+ \\ { 'name': 'debputy',
+ \\ 'filetypes': [ 'debcontrol', 'debcopyright', 'debchangelog', 'make'],
+ \\ 'cmdline': [ 'debputy', 'lsp', 'server' ]
+ \\ },
+ \\ ]
+
+ packadd! youcompleteme
+ nmap <leader>d <plug>(YCMHover)
+ """
+ ),
+}
+
+
+lsp_command = ROOT_COMMAND.add_dispatching_subcommand(
+ "lsp",
+ dest="lsp_command",
+ help_description="Language server related subcommands",
+)
+
+
+@lsp_command.register_subcommand(
+ "server",
+ log_only_to_stderr=True,
+ help_description="Start the language server",
+ argparser=[
+ add_arg(
+ "--tcp",
+ action="store_true",
+ help="Use TCP server",
+ ),
+ add_arg(
+ "--ws",
+ action="store_true",
+ help="Use WebSocket server",
+ ),
+ add_arg(
+ "--host",
+ default="127.0.0.1",
+ help="Bind to this address (Use with --tcp / --ws)",
+ ),
+ add_arg(
+ "--port",
+ type=int,
+ default=2087,
+ help="Bind to this port (Use with --tcp / --ws)",
+ ),
+ ],
+)
+def lsp_server_cmd(context: CommandContext) -> None:
+ parsed_args = context.parsed_args
+
+ try:
+ import lsprotocol
+ import pygls
+ except ImportError:
+ _error(
+ "This feature requires lsprotocol and pygls (apt-get install python3-lsprotocol python3-pygls)"
+ )
+
+ from debputy.lsp.lsp_features import ensure_lsp_features_are_loaded
+ from debputy.lsp.lsp_dispatch import DEBPUTY_LANGUAGE_SERVER
+
+ ensure_lsp_features_are_loaded()
+ debputy_language_server = DEBPUTY_LANGUAGE_SERVER
+
+ if parsed_args.tcp:
+ debputy_language_server.start_tcp(parsed_args.host, parsed_args.port)
+ elif parsed_args.ws:
+ debputy_language_server.start_ws(parsed_args.host, parsed_args.port)
+ else:
+ debputy_language_server.start_io()
+
+
+@lsp_command.register_subcommand(
+ "editor-config",
+ help_description="Provide editor configuration snippets",
+ argparser=[
+ add_arg(
+ "editor_name",
+ metavar="editor",
+ choices=_EDITOR_SNIPPETS,
+ help="The editor to provide a snippet for",
+ ),
+ ],
+)
+def lsp_editor_glue(context: CommandContext) -> None:
+ editor_name = context.parsed_args.editor_name
+ result = _EDITOR_SNIPPETS[editor_name]
+ while result in _EDITOR_SNIPPETS:
+ result = _EDITOR_SNIPPETS[result]
+ print(result)
+
+
+@lsp_command.register_subcommand(
+ "features",
+ help_description="Describe language ids and features",
+)
+def lsp_editor_glue(_context: CommandContext) -> None:
+ try:
+ import lsprotocol
+ import pygls
+ except ImportError:
+ _error(
+ "This feature requires lsprotocol and pygls (apt-get install python3-lsprotocol python3-pygls)"
+ )
+
+ from debputy.lsp.lsp_features import describe_lsp_features
+
+ describe_lsp_features()
+
+
+@ROOT_COMMAND.register_subcommand(
+ "lint",
+ log_only_to_stderr=True,
+ argparser=[
+ add_arg(
+ "--spellcheck",
+ dest="spellcheck",
+ action="store_true",
+ shared=True,
+ help="Enable spellchecking",
+ ),
+ add_arg(
+ "--auto-fix",
+ dest="auto_fix",
+ action="store_true",
+ shared=True,
+ help="Automatically fix problems with trivial or obvious corrections.",
+ ),
+ add_arg(
+ "--linter-exit-code",
+ dest="linter_exit_code",
+ default=True,
+ action=BooleanOptionalAction,
+ help='Enable or disable the "linter" convention of exiting with an error if severe issues were found',
+ ),
+ ],
+)
+def lint_cmd(context: CommandContext) -> None:
+ try:
+ import lsprotocol
+ except ImportError:
+ _error("This feature requires lsprotocol (apt-get install python3-lsprotocol)")
+
+ from debputy.linting.lint_impl import perform_linting
+
+ # For the side effect of validating that we are run from a debian directory.
+ context.binary_packages()
+ perform_linting(context)
+
+
+def ensure_lint_and_lsp_commands_are_loaded():
+ # Loading the module does the heavy lifting
+ # However, having this function means that we do not have an "unused" import that some tool
+ # gets tempted to remove
+ assert ROOT_COMMAND.has_command("lsp")
+ assert ROOT_COMMAND.has_command("lint")