Repository: sublimelsp/LSP

Issue:
Format JSON on save
Is there a way to get this LSP to format json files ons save?

It works for other LSPs but just not JSON,

Here are my configs:

Preferences
```
{
        ...
	"lsp_format_on_save": true
        ...
}
```

LSP
```
	"lsp_code_actions_on_save": {
		"source.organizeImports": true,
		"source.fixAll.eslint": true,
	}
```

All LSP-JSON settings are default


Discussion:
That server doesn't provide "documentFormattingProvider" but it does provide "documentRangeFormattingProvider" which we can use to format the whole document. We already do that for the "LSP: Format file" command and VSCode also does that for the "format on save" feature.

So we can fix that.

Resolving patch:
diff --git a/plugin/formatting.py b/plugin/formatting.py
--- a/plugin/formatting.py
+++ b/plugin/formatting.py
@@ -1,9 +1,11 @@
 from .core.edit import parse_text_edit
+from .core.promise import Promise
+from .core.protocol import Error
 from .core.protocol import TextEdit
 from .core.registry import LspTextCommand
 from .core.sessions import Session
 from .core.settings import userprefs
-from .core.typing import Any, Callable, List, Optional, Iterator
+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 text_document_formatting
@@ -13,6 +15,22 @@
 import sublime
 
 
+FormatResponse = Union[List[TextEdit], None, Error]
+
+
+def format_document(text_command: LspTextCommand) -> Promise[FormatResponse]:
+    view = text_command.view
+    session = text_command.best_session(LspFormatDocumentCommand.capability)
+    if session:
+        # Either use the documentFormattingProvider ...
+        return session.send_request_task(text_document_formatting(view))
+    session = text_command.best_session(LspFormatDocumentRangeCommand.capability)
+    if session:
+        # ... or use the documentRangeFormattingProvider and format the entire range.
+        return session.send_request_task(text_document_range_formatting(view, entire_content_region(view)))
+    return Promise.resolve(None)
+
+
 def apply_response_to_view(response: Optional[List[TextEdit]], view: sublime.View) -> None:
     edits = list(parse_text_edit(change) for change in response) if response else []
     view.run_command('lsp_apply_document_edit', {'changes': edits})
@@ -63,16 +81,10 @@ def is_applicable(cls, view: sublime.View) -> bool:
     def run_async(self) -> None:
         super().run_async()
         self._purge_changes_async()
-        session = self._task_runner.best_session(LspFormatDocumentCommand.capability)
-        if session:
-            session.send_request_async(
-                text_document_formatting(self._task_runner.view), self._on_response,
-                lambda error: self._on_response(None))
-        else:
-            self._on_complete()
+        format_document(self._task_runner).then(self._on_response)
 
-    def _on_response(self, response: Any) -> None:
-        if response and not self._cancelled:
+    def _on_response(self, response: FormatResponse) -> None:
+        if response and not isinstance(response, Error) and not self._cancelled:
             apply_response_to_view(response, self._task_runner.view)
         sublime.set_timeout_async(self._on_complete)
 
@@ -89,19 +101,11 @@ def is_enabled(self, event: Optional[dict] = None, point: Optional[int] = None)
         return super().is_enabled() or bool(self.best_session(LspFormatDocumentRangeCommand.capability))
 
     def run(self, edit: sublime.Edit, event: Optional[dict] = None) -> None:
-        session = self.best_session(self.capability)
-        if session:
-            # Either use the documentFormattingProvider ...
-            session.send_request(text_document_formatting(self.view), self.on_result)
-        else:
-            session = self.best_session(LspFormatDocumentRangeCommand.capability)
-            if session:
-                # ... or use the documentRangeFormattingProvider and format the entire range.
-                req = text_document_range_formatting(self.view, entire_content_region(self.view))
-                session.send_request(req, self.on_result)
-
-    def on_result(self, params: Any) -> None:
-        apply_response_to_view(params, self.view)
+        format_document(self).then(self.on_result)
+
+    def on_result(self, result: FormatResponse) -> None:
+        if result and not isinstance(result, Error):
+            apply_response_to_view(result, self.view)
 
 
 class LspFormatDocumentRangeCommand(LspTextCommand):


Test patch: