diff --git a/Context.sublime-menu b/Context.sublime-menu index 5defb140f..0b240f854 100644 --- a/Context.sublime-menu +++ b/Context.sublime-menu @@ -42,12 +42,7 @@ "caption": "Source Action…" }, { - "command": "lsp_format_document", - "caption": "Format File" - }, - { - "command": "lsp_format_document_range", - "caption": "Format Selection" + "command": "lsp_format" }, { "command": "lsp_expand_selection", diff --git a/boot.py b/boot.py index ed441e1ce..5c3e4fc8c 100644 --- a/boot.py +++ b/boot.py @@ -41,6 +41,7 @@ from .plugin.documents import TextChangeListener from .plugin.edit import LspApplyDocumentEditCommand from .plugin.execute_command import LspExecuteCommand +from .plugin.formatting import LspFormatCommand from .plugin.formatting import LspFormatDocumentCommand from .plugin.formatting import LspFormatDocumentRangeCommand from .plugin.goto import LspSymbolDeclarationCommand diff --git a/plugin/core/views.py b/plugin/core/views.py index 897f75de7..e961ebd3c 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -431,6 +431,11 @@ def first_selection_region(view: sublime.View) -> Optional[sublime.Region]: return None +def has_single_nonempty_selection(view: sublime.View) -> bool: + selections = view.sel() + return len(selections) == 1 and not selections[0].empty() + + def entire_content_region(view: sublime.View) -> sublime.Region: return sublime.Region(0, view.size()) diff --git a/plugin/formatting.py b/plugin/formatting.py index 18473b42f..6d111cc39 100644 --- a/plugin/formatting.py +++ b/plugin/formatting.py @@ -8,6 +8,7 @@ from .core.typing import Any, Callable, List, Optional, Iterator, Union from .core.views import entire_content_region from .core.views import first_selection_region +from .core.views import has_single_nonempty_selection from .core.views import text_document_formatting from .core.views import text_document_range_formatting from .core.views import will_save_wait_until @@ -114,10 +115,7 @@ class LspFormatDocumentRangeCommand(LspTextCommand): def is_enabled(self, event: Optional[dict] = None, point: Optional[int] = None) -> bool: if super().is_enabled(event, point): - if len(self.view.sel()) == 1: - region = self.view.sel()[0] - if region.begin() != region.end(): - return True + return has_single_nonempty_selection(self.view) return False def run(self, edit: sublime.Edit, event: Optional[dict] = None) -> None: @@ -126,3 +124,25 @@ def run(self, edit: sublime.Edit, event: Optional[dict] = None) -> None: if session and selection is not None: req = text_document_range_formatting(self.view, selection) session.send_request(req, lambda response: apply_text_edits_to_view(response, self.view)) + + +class LspFormatCommand(LspTextCommand): + + def is_enabled(self, event: Optional[dict] = None, point: Optional[int] = None) -> bool: + if not super().is_enabled(): + return False + return bool(self.best_session('documentFormattingProvider')) or \ + bool(self.best_session('documentRangeFormattingProvider')) + + def is_visible(self, event: Optional[dict] = None, point: Optional[int] = None) -> bool: + return self.is_enabled(event, point) + + def description(self, **kwargs) -> str: + return "Format Selection" if self._range_formatting_available() else "Format File" + + def run(self, edit: sublime.Edit, event: Optional[dict] = None) -> None: + command = 'lsp_format_document_range' if self._range_formatting_available() else 'lsp_format_document' + self.view.run_command(command) + + def _range_formatting_available(self) -> bool: + return has_single_nonempty_selection(self.view) and bool(self.best_session('documentRangeFormattingProvider'))