[LSP] Respect settings passed in by LSP in format handlers#52044
Conversation
| var span = textSpan ?? new TextSpan(0, root.FullSpan.Length); | ||
| var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, span); | ||
|
|
||
| var options = await document.GetDocumentOptionsWithInferredIndentationAsync(explicitFormat: true, indentationManagerService: _indentationManagerService, cancellationToken: cancellationToken).ConfigureAwait(false); |
There was a problem hiding this comment.
A bunch of lines in this PR were really long on my laptop, so I broke them up for easier future readability.
| /// Returns the text changes necessary to format the document with custom document options. | ||
| /// If "textSpan" is provided, only the text changes necessary to format that span are needed. | ||
| /// </summary> | ||
| Task<IList<TextChange>> GetFormattingChangesAsync(Document document, TextSpan? textSpan, DocumentOptionSet documentOptions, CancellationToken cancellationToken); |
There was a problem hiding this comment.
TS + F# both use this interface so I had to add new methods instead of modifying the existing ones.
There was a problem hiding this comment.
Do they not use the ExternalAccess patterns?
There was a problem hiding this comment.
I believe they do for some interfaces, but for this one it looks like they just implement it directly?
There was a problem hiding this comment.
I don't understand why this is better than adding a parameter? Isn't it a breaking change either way?
There was a problem hiding this comment.
If we add the methods and merge the TypeScript change first, I don't think it would be a breaking change, since TypeScript will just have unused members that aren't associated with any interface method. When we then merge the interface changes in from the Roslyn side, those unassociated members will be tied to interface members.
However, if others feel strongly that editing the existing interface methods is the correct way to go here, then maybe a breaking change would be OK? It would just have to be a coordinated insertion with TypeScript (I think).
There was a problem hiding this comment.
I don't think it would be a breaking change, since TypeScript will just have unused members that aren't associated with any interface method. When we then merge the interface changes in from the Roslyn side, those unassociated members will be tied to interface members.
This is still a breaking change AFAIK, since it's at the moment TypeScript is compiled that the compiler is sorting out what is and isn't a interface implementation. TypeScript needs to be recompiled no matter what.
There was a problem hiding this comment.
Gotcha, in that case I modified the code to just update the existing signature.
|
|
||
| // The editor calls this handler for C# and VB comment characters, but we only need to process the one for the language that matches the document | ||
| if (autoInsertParams.Character == "\n" || autoInsertParams.Character == service.DocumentationCommentCharacter) | ||
| if (request.Character == "\n" || request.Character == service.DocumentationCommentCharacter) |
There was a problem hiding this comment.
For consistency, I changed this from autoInsertParams -> request since that's the variable name all other handlers use.
jasonmalinowski
left a comment
There was a problem hiding this comment.
See note about tab size vs. indentation size; I suspect this will do something pretty strange if we're not careful. In either case I didn't see any tests that set something other than 4. which we probably should add.
| int position, | ||
| DocumentOptionSet documentOptions, | ||
| CancellationToken cancellationToken) | ||
| // We don't need to pass in options here since C#/VB always returns no text changes for this case. |
There was a problem hiding this comment.
Will we need to at some point though? Or does F#/TypeScript need to? This is more a "if we're breaking the interface, do we need to break this too" sort of question.
There was a problem hiding this comment.
Hmm, good question. I guess it makes sense to add it just in case we do add support for this method in the future.
| var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); | ||
| var updatedOptions = documentOptions | ||
| .WithChangedOption(Formatting.FormattingOptions.UseTabs, !options.InsertSpaces) | ||
| .WithChangedOption(Formatting.FormattingOptions.TabSize, options.TabSize); |
There was a problem hiding this comment.
Does this also need to set Formatting.FormattingOptions.IndentationSize as well to match tab size? I wouldn't be surprised if VS Code doesn't support the distinction but usually you want these the same unless you have really good reasons not to do so.
There was a problem hiding this comment.
Ah I see. LSP doesn't support indent size currently, only tab size, but I'll update the code so the two match.
dibarbet
left a comment
There was a problem hiding this comment.
the changes here look fine, as mentioned offline though I'm still not sure of the pros/cons of forking the document (for the request) with new options vs passing them in
| /// If "textSpan" is provided, only the text changes necessary to format that span are needed. | ||
| /// </summary> | ||
| Task<IList<TextChange>> GetFormattingChangesAsync(Document document, TextSpan? textSpan, CancellationToken cancellationToken); | ||
| Task<IList<TextChange>> GetFormattingChangesAsync(Document document, TextSpan? textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken); |
There was a problem hiding this comment.
this is fine, just wanted to bikeshed a little bit - should this be something like documentOptionsOverride? to clearly indicate that these are optional options to override whatever the default document options are
There was a problem hiding this comment.
I would just do OptionsSet options to match how we do this elsewhere.
There was a problem hiding this comment.
The implementations of these methods depend on specifically using DocumentOptionsSet as opposed to OptionsSet, so I don't know if we can switch the types here.
Today, LSP passes in tabs/spaces settings as part of formatting requests. However, we do not respect these settings and instead use the document's options. We should respect the LSP settings.
Follow up from #51916.
Note, I think this will also require a change on the TypeScript side as they implement
IEditorFormattingService. I opened a PR on their side here - https://devdiv.visualstudio.com/DevDiv/_git/TypeScript-VS/pullrequest/312160