Skip to content

Commit

Permalink
Add format on paste (#2397)
Browse files Browse the repository at this point in the history
* add format on paste

* include whitespace to improve format on paste

Including whitespace does help some servers to format a range as expected like LSP-json,

See: #2311 (comment)

* tweak comment

* fix flake

* move before run_command

* move up to be before the early return

* introduce lsp_format_on_paste instead of format_on_paste

So it can be defined in Preferences.sublime-settings

* fix flake

* fix over indent flake :(

* use $ref for lsp_format_on_paste

* Rafal having good suggestions as always :)

* handle paste_and_indent as well if lsp_format_on_paste is enabled

* make sure that we have LSP running before we override the paste_and_indent

and generally make sure to run format_on_paste if the session supports documentRangeFormattingProvider

* Update plugin/documents.py

Co-authored-by: Rafał Chłodnicki <[email protected]>

* fix :

* the first paragraph is not necessary

* remove unnecessary line and move inside if

* avoid quick flash of selected region before format is run

Before, occasionally it was possible to see a brief moment when text gets selected,
Now, we move that from the async thread to the main thread, thus we will not see that flash

* update text to be more user friendly

---------

Co-authored-by: Rafał Chłodnicki <[email protected]>
  • Loading branch information
predragnikolic and rchl authored Feb 2, 2024
1 parent 8023408 commit af3b9ee
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 4 deletions.
5 changes: 4 additions & 1 deletion LSP.sublime-settings
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

// --- Document Formatting ------------------------------------------------------------

// Run the server's formatProvider (if supported) on a file before saving.
// If supported, format the pasted text.
"lsp_format_on_paste": false,

// If supported, format a file before saving.
// This option is also supported in syntax-specific settings and/or in the
// "settings" section of project files.
"lsp_format_on_save": false,
Expand Down
2 changes: 1 addition & 1 deletion docs/src/client_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ The port number can be inserted into the server's startup `command` in your clie

## Per-project overrides

Global LSP settings (which currently are `lsp_format_on_save` and `lsp_code_actions_on_save`) can be overridden per-project in `.sublime-project` file:
Global LSP settings (which currently are `lsp_format_on_save`, `lsp_format_on_paste` and `lsp_code_actions_on_save`) can be overridden per-project in `.sublime-project` file:

```jsonc
{
Expand Down
2 changes: 2 additions & 0 deletions plugin/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class Settings:
log_max_size = cast(int, None)
log_server = cast(List[str], None)
lsp_code_actions_on_save = cast(Dict[str, bool], None)
lsp_format_on_paste = cast(bool, None)
lsp_format_on_save = cast(bool, None)
on_save_task_timeout_ms = cast(int, None)
only_show_lsp_completions = cast(bool, None)
Expand Down Expand Up @@ -256,6 +257,7 @@ def r(name: str, default: Union[bool, int, str, list, dict]) -> None:
r("log_debug", False)
r("log_max_size", 8 * 1024)
r("lsp_code_actions_on_save", {})
r("lsp_format_on_paste", False)
r("lsp_format_on_save", False)
r("on_save_task_timeout_ms", 2000)
r("only_show_lsp_completions", False)
Expand Down
46 changes: 45 additions & 1 deletion plugin/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def on_change() -> None:
self._registration = SettingsRegistration(view.settings(), on_change=on_change)
self._completions_task = None # type: Optional[QueryCompletionsTask]
self._stored_selection = [] # type: List[sublime.Region]
self._should_format_on_paste = False
self._setup()

def __del__(self) -> None:
Expand Down Expand Up @@ -525,10 +526,20 @@ def on_text_command(self, command_name: str, args: Optional[dict]) -> Optional[T
session = self.session_async("semanticTokensProvider")
if session:
return ("lsp_show_scope_name", {})
elif command_name == 'paste_and_indent':
# it is easier to find the region to format when `paste` is invoked,
# so we intercept the `paste_and_indent` and replace it with the `paste` command.
format_on_paste = self.view.settings().get('lsp_format_on_paste', userprefs().lsp_format_on_paste)
if format_on_paste and self.session_async("documentRangeFormattingProvider"):
return ('paste', {})
return None

def on_post_text_command(self, command_name: str, args: Optional[Dict[str, Any]]) -> None:
if command_name in ("next_field", "prev_field") and args is None:
if command_name == 'paste':
format_on_paste = self.view.settings().get('lsp_format_on_paste', userprefs().lsp_format_on_paste)
if format_on_paste and self.session_async("documentRangeFormattingProvider"):
self._should_format_on_paste = True
elif command_name in ("next_field", "prev_field") and args is None:
sublime.set_timeout_async(lambda: self.do_signature_help_async(manual=True))
if not self.view.is_popup_visible():
return
Expand Down Expand Up @@ -926,6 +937,9 @@ def _register_async(self) -> None:
listener.on_load_async()

def _on_view_updated_async(self) -> None:
if self._should_format_on_paste:
self._should_format_on_paste = False
self._format_on_paste_async()
self._code_lenses_debouncer_async.debounce(
self._do_code_lenses_async, timeout_ms=self.code_lenses_debounce_time)
first_region, _ = self._update_stored_selection_async()
Expand Down Expand Up @@ -959,6 +973,36 @@ def _update_stored_selection_async(self) -> Tuple[Optional[sublime.Region], bool
self._stored_selection = selection
return changed_first_region, True

def _format_on_paste_async(self) -> None:
clipboard_text = sublime.get_clipboard()
sel = self.view.sel()
split_clipboard_text = clipboard_text.split('\n')
multi_cursor_paste = len(split_clipboard_text) == len(sel) and len(sel) > 1
original_selection = list(sel)
regions_to_format = [] # type: List[sublime.Region]
pasted_text = clipboard_text
# add regions to selection, in order for lsp_format_document_range to format those regions
for index, region in enumerate(sel):
if multi_cursor_paste:
pasted_text = split_clipboard_text[index]
pasted_region = self.view.find(pasted_text, region.end(), sublime.REVERSE | sublime.LITERAL)
if pasted_region:
# Including whitespace may help servers format a range better
# More info at https://github.com/sublimelsp/LSP/pull/2311#issuecomment-1688593038
a = self.view.find_by_class(pasted_region.a, False,
sublime.CLASS_WORD_END | sublime.CLASS_PUNCTUATION_END)
formatting_region = sublime.Region(a, pasted_region.b)
regions_to_format.append(formatting_region)
self.purge_changes_async()

def run_sync() -> None:
sel.add_all(regions_to_format)
self.view.run_command('lsp_format_document_range')
sel.clear()
sel.add_all(original_selection)

sublime.set_timeout(run_sync)

def _clear_session_views_async(self) -> None:
session_views = self._session_views

Expand Down
13 changes: 12 additions & 1 deletion sublime-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
"$id": "sublime://settings/LSP",
"definitions": {
// User preferences that are shared with "Preferences.sublime-settings"
"lsp_format_on_paste": {
"type": "boolean",
"default": false,
"markdownDescription": "If supported, format the pasted text."
},
"lsp_format_on_save": {
"type": "boolean",
"default": false,
"markdownDescription": "Run the server's formatProvider (if supported) on a document before saving. This option is also supported in syntax-specific settings and/or in the `\"settings\"` section of project files."
"markdownDescription": "If supported, format a file before saving. This option is also supported in syntax-specific settings and/or in the `\"settings\"` section of project files."
},
"lsp_code_actions_on_save": {
"type": "object",
Expand Down Expand Up @@ -444,6 +449,9 @@
"default": false,
"markdownDescription": "Show errors and warnings count in the status bar."
},
"lsp_format_on_paste": {
"$ref": "sublime://settings/LSP#/definitions/lsp_format_on_paste"
},
"lsp_format_on_save": {
"$ref": "sublime://settings/LSP#/definitions/lsp_format_on_save"
},
Expand Down Expand Up @@ -796,6 +804,9 @@
],
"schema": {
"properties": {
"lsp_format_on_paste": {
"$ref": "sublime://settings/LSP#/definitions/lsp_format_on_paste",
},
"lsp_format_on_save": {
"$ref": "sublime://settings/LSP#/definitions/lsp_format_on_save",
},
Expand Down

0 comments on commit af3b9ee

Please sign in to comment.