Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Client-side on-type formatting support #745

Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
192de8e
Fresh version of lsp_on_type_formatting branch that adds support for …
SCWells72 Jan 14, 2025
503ba8d
Merge branch 'main' into lsp_client_side_on_type_formatting
SCWells72 Jan 15, 2025
16788ba
Improvements and fixes based on testing client-side on-type formattin…
SCWells72 Jan 20, 2025
7912c4b
Merge branch 'main' into lsp_client_side_on_type_formatting_merged
SCWells72 Jan 20, 2025
1570e22
Disabling client-side format-on-close brace for jdtls because that la…
SCWells72 Jan 20, 2025
8141e65
Removed debugging statement that should not have been included in com…
SCWells72 Jan 21, 2025
18a7d8b
Merge branch 'main' into lsp_client_side_on_type_formatting_merged
SCWells72 Jan 24, 2025
8169857
Renamed the completion trigger handler to be more specific about its …
SCWells72 Jan 24, 2025
e376359
Added a client configuration for disabling the LSP "textDocument/onTy…
SCWells72 Jan 25, 2025
7ff8a26
Added a client configuration option for disabling the LSP "textDocume…
SCWells72 Jan 25, 2025
908b2a3
Merge branch 'lsp_server_on_type_formatting_config' into lsp_client_s…
SCWells72 Jan 25, 2025
bec2ea6
Merge branch 'redhat-developer:main' into lsp_client_side_on_type_for…
SCWells72 Jan 27, 2025
d74ebfe
Merged/integrated latest from main.
SCWells72 Jan 28, 2025
d6ce58f
Merge branch 'main' into lsp_client_side_on_type_formatting
SCWells72 Jan 28, 2025
638d54a
Updated to use the unified client configuration schema.
SCWells72 Jan 28, 2025
920db1a
Updated documentation for client-side on-type formatting behavior, co…
SCWells72 Jan 28, 2025
7fdfbc5
Updated the code block provider docs to be clear that code blocks are…
SCWells72 Jan 28, 2025
4fed3f3
Addressed PR review feedback regarding how the relevant language serv…
SCWells72 Jan 29, 2025
51a3acf
Feature doc improvements.
SCWells72 Jan 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions docs/LSPApi.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,13 +383,21 @@ public class MyLSPDocumentSymbolFeature extends LSPDocumentSymbolFeature {

## LSP Formatting Feature

| API | Description | Default Behaviour |
|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|
| boolean isEnabled(PsiFile file) | Returns `true` if the LSP feature is enabled for the given file and `false` otherwise. | `true` |
| boolean isSupported(PsiFile file) | Returns `true` if the LSP feature is supported for the given file and `false` otherwise. <br/>This supported state is called after starting the language server, which matches the file and user with the LSP server capabilities. | Check the server capability |
| boolean isRangeFormattingSupported(PsiFile file) | Returns `true` if the range formatting is supported for the given file and `false` otherwise. | Check the server capability |
| boolean isExistingFormatterOverrideable(PsiFile file) | Returns `true` if existing formatters are overrideable and `false` otherwise. | `false` |
| boolean isOnTypeFormattingEnabled(PsiFile file) | Whether or not server-side on-type formatting is enabled if `textDocument/onTypeFormatting` is supported by the server. | `true` |
| API | Description | Default Behaviour |
|----------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|
| boolean isEnabled(PsiFile file) | Returns `true` if the LSP feature is enabled for the given file and `false` otherwise. | `true` |
| boolean isSupported(PsiFile file) | Returns `true` if the LSP feature is supported for the given file and `false` otherwise. <br/>This supported state is called after starting the language server, which matches the file and user with the LSP server capabilities. | Check the server capability |
| boolean isRangeFormattingSupported(PsiFile file) | Returns `true` if the range formatting is supported for the given file and `false` otherwise. | Check the server capability |
| boolean isExistingFormatterOverrideable(PsiFile file) | Returns `true` if existing formatters are overrideable and `false` otherwise. | `false` |
| boolean isOnTypeFormattingEnabled(PsiFile file) | Whether or not server-side on-type formatting is enabled if `textDocument/onTypeFormatting` is supported by the server. | `true` |
| boolean isFormatOnCloseBrace(PsiFile file) | Whether or not to format the file when close braces are typed. | `false` |
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the new client configuration options for client-side on-type formatting.

| boolean getFormatOnCloseBraceCharacters(PsiFile file) | The specific close brace characters that should trigger on-type formatting in the file. | The language's standard close brace characters. |
| boolean getFormatOnCloseBraceScope(PsiFile file) | The scope that should be formatted when a close brace is typed. Allowed values are `CODE_BLOCK` and `FILE`. | `CODE_BLOCK` |
| boolean isFormatOnStatementTerminator(PsiFile file) | Whether or not to format the file when statement terminators are typed. | `false` |
| boolean getFormatOnStatementTerminatorCharacters(PsiFile file) | The specific statement terminator characters that should trigger on-type formatting in the file. | None. |
| boolean getFormatOnStatementTerminatorScope(PsiFile file) | The scope that should be formatted when a statement terminator is typed. Allowed values are `STATEMENT`, `CODE_BLOCK` and `FILE`. | `STATEMENT` |
| boolean isFormatOnCompletionTrigger(PsiFile file) | Whether or not to format the file when completion triggers are typed. | `false` |
| boolean getFormatOnCompletionTriggerCharacters(PsiFile file) | The specific completion trigger characters that should trigger on-type formatting in the file. | The language's standard completion trigger characters. |

Here is an example of code that allows executing the LSP formatter even if there is a specific formatter registered by an IntelliJ plugin:

Expand Down
89 changes: 76 additions & 13 deletions docs/LSPSupport.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,29 +389,31 @@ showing automatic folding of both the file header comment and imports when the f

![textDocument/foldingRange_collapseByDefault](./images/lsp-support/textDocument_foldingRange_collapseByDefault.gif)

#### Code block provider
### Selection range

[textDocument/selectionRange](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_selectionRange) is implemented with
the `extendWordSelectionHandler` extension point and used via the IDE's **Extend Selection** (**Ctrl+W** on Windows/Linux; **Opt+Up** on Mac) and **Shrink Selection** (**Ctrl+Shift+W** on Windows/Linux; **Opt+Down** on Mac) actions.

Here is an example with the [TypeScript Language Server](https://github.com/typescript-language-server/typescript-language-server) showing **Extend/Shrink Selection** using `textDocument/selectionRange`:

![textDocument/selectionRange](./images/lsp-support/textDocument_selectionRange.gif)

Additionally LSP4IJ registers the an implementation of the `codeBlockProvider` extension point with
### Code block provider
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also updated the code block provider documentation to clearly state that code blocks are derived using selection ranges first and then folding ranges if necessary.


LSP4IJ registers the an implementation of the `codeBlockProvider` extension point with
[LSPCodeBlockProvider](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/features/foldingRange/LSPCodeBlockProvider.java) for `TEXT` and `textmate` languages to provide block brace matching and easy navigation to
the beginning/end of the containing block.
the beginning/end of the containing block. For maximum flexibility in the absence of a true AST in the LSP client, code
blocks are derived from `textDocument/selectionRange` first and, failing that, from `textDocument/foldingRange`.

As with `lang.foldingBuilder`, if you use another language, you will have to declare `codeBlockProvider` with your language.
As with other language-specific EP implementations, if you use another language, you will have to declare
`codeBlockProvider` with your language.

Below is an example with the [TypeScript Language Server](./user-defined-ls/typescript-language-server.md) showing code
block functionality. The IDE's Presentation Assistant shows the default keyboard shortcuts for each supported operating
system to trigger these actions.

![codeBlockProvider](./images/lsp-support/codeBlockProvider.gif)

### Selection range
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it was removed but it's actually still right above the "Code block provider" section.


[textDocument/selectionRange](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_selectionRange) is implemented with
the `extendWordSelectionHandler` extension point and used via the IDE's **Extend Selection** (**Ctrl+W** on Windows/Linux; **Opt+Up** on Mac) and **Shrink Selection** (**Ctrl+Shift+W** on Windows/Linux; **Opt+Down** on Mac) actions.

Here is an example with the [TypeScript Language Server](https://github.com/typescript-language-server/typescript-language-server) showing **Extend/Shrink Selection** using `textDocument/selectionRange`:

![textDocument/selectionRange](./images/lsp-support/textDocument_selectionRange.gif)

### Publish Diagnostics

[textDocument/publishDiagnostics](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_publishDiagnostics) is implemented with an `externalAnnotator` extension point. As this extension point supports `any` language, it works out-of-the-box.
Expand Down Expand Up @@ -458,6 +460,67 @@ Here is an example with the [Java Language Server](https://github.com/eclipse-jd

![textDocument/onTypeFormatting](./images/lsp-support/textDocument_onTypeFormatting.gif)

If desired &mdash; for example, for those who want to control when formatting is performed &mdash; server-side/LSP-based
Copy link
Contributor Author

@SCWells72 SCWells72 Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While updating the on-type formatting docs for this feature, I added details on how to disable server-side on-type formatting if desired.

on-type formatting can be disabled via client configuration as follows:

```json
{
"format": {
"onTypeFormatting": {
"serverSide": {
"enabled": false
}
}
}
}
```

#### Client-side On-Type Formatting
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I documented client-side on-type formatting extensively including the TypeScript server configuration example and a demo.


Not all language servers support the `textDocument/onTypeFormatting` feature. To provide an improved editor experience
for users of those that do not, LSP4IJ includes support for _client-side on-type formatting_. Client-side on-type
formatting can be enabled via client configuration with the following settings:

* `format.onTypeFormatting.clientSide.formatOnCloseBrace` - When set to `true`, formatting is automatically applied when a close brace character is typed. Defaults to `false`.
* `format.onTypeFormatting.clientSide.formatOnCloseBraceCharacters` - Specifies the exact characters that should treated as a close brace character for purposes of client-side on-type formatting. Defaults to the close brace characters for the language, typically `}`, `]`, and `)`.
* `format.onTypeFormatting.clientSide.formatOnCloseBraceScope` - Specifies the scope that should be formatted when a close brace is typed. Valid values are `CODE_BLOCK` and `FILE`. Defaults to `CODE_BLOCK`. `FILE` is most useful for language servers that do not support range formatting or yield incorrect results for range formatting.
* `format.onTypeFormatting.clientSide.formatOnStatementTerminator` - When set to `true`, formatting is automatically applied when a statement terminator character is typed. Defaults to `false`.
* `format.onTypeFormatting.clientSide.formatOnStatementTerminatorCharacters` - Specifies the exact characters that should treated as a statement terminator character for purposes of client-side on-type formatting. Defaults to empty and must be specified if `formatOnStatementTerminator` is enabled.
* `format.onTypeFormatting.clientSide.formatOnStatementTerminatorScope` - Specifies the scope that should be formatted when a statement terminator is typed. Valid values are `STATEMENT`, `CODE_BLOCK` and `FILE`. Defaults to `STATEMENT`. The other values are most useful for language servers that do not support range formatting or yield incorrect results for range formatting.
* `format.onTypeFormatting.clientSide.formatOnCompletionTrigger` - When set to `true`, formatting is automatically applied when a completion trigger character is typed. Defaults to `false`.
* `format.onTypeFormatting.clientSide.formatOnCompletionTriggerCharacters` - Specifies the exact characters that should treated as a completion trigger character for purposes of client-side on-type formatting. Defaults to the completion trigger characters specified by the language server.
* Note that there is no configurable scope for completion trigger-based formatting. Exactly the type completion trigger character is formatted. As above, support for this may vary by language server.

For example, the [TypeScript Language Server](./user-defined-ls/typescript-language-server.md) does not support server-side on-type formatting, but client-side
on-type formatting is included in its language server configuration template as:

```json
{
"format": {
"onTypeFormatting": {
"clientSide": {
"formatOnCloseBrace": true,
"formatOnStatementTerminator": true,
"formatOnStatementTerminatorCharacters": ";",
"formatOnCompletionTrigger": true
}
}
}
}
```

Here is an example for client-side on-type formatting with that configuration showing automatic indentation of a
statement continuation when the completion trigger character `.` is typed and automatic formatting of an entire code
block when the closing brace character `}` is typed for a surrounding conditional statement:

![Client-side on-type formatting](./images/lsp-support/clientSideOnTypeFormatting.gif)

#### Server-side / Client-side On-Type Formatting Relationship
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wanted to call out the relationship between server-side and client-side on-type formatting if the former is available in the language server and enabled and the latter is also enabled in config.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for having taken time to write this doc, it is excellent!


If server-side on-type formatting is supported by the language server and enabled _and_ client-side on-type formatting
is enabled for specific trigger characters, _only client-side on-type formatting will be applied_ when those specific
trigger characters are typed.

### Show Message

[window/showMessage](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_showMessage) supports Markdown messages and clickable links.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading