/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs
115 строк
6 KB
Cyrus Najmabadi
Reapply "Update methods to be `async`." (#81808)
31 дек 2025, 01:59
31 дек 2025, 01:59
3d73546
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; [ExportLanguageService(typeof(IFormattingInteractionService), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class CSharpFormattingInteractionService(EditorOptionsService editorOptionsService) : IFormattingInteractionService { /// <summary> /// All the characters that might potentially trigger formatting when typed. The punctuation characters are the /// normal C# punctuation characters that start/end constructs we want to format when starting/ending them. The /// letters 'n', 't', and 'e' are the ending characters of certain identifiers that we want to format when they are /// completely written. For example, if the user types <c>#</c> we left align the preprocessor directive /// immediately. However, once they type <c>region</c> (which ends with 'n') we then want to indent it. See <see /// cref="CSharpSyntaxFormattingService.ValidSingleOrMultiCharactersTokenKind"/> for those identifier cases. /// </summary> private const string s_supportedChars = ";{}#:)nte"; private readonly EditorOptionsService _editorOptionsService = editorOptionsService; public bool SupportsFormatDocument => true; public bool SupportsFormatOnPaste => true; public bool SupportsFormatSelection => true; public bool SupportsFormatOnReturn => false; public bool SupportsFormattingOnTypedCharacter(Document document, char ch) { var isSmartIndent = _editorOptionsService.GlobalOptions.GetOption(IndentationOptionsStorage.SmartIndent, LanguageNames.CSharp) == FormattingOptions2.IndentStyle.Smart; // We consider the proper placement of a close curly or open curly when it is typed at // the start of the line to be a smart-indentation operation. As such, even if "format // on typing" is off, if "smart indent" is on, we'll still format this. (However, we // won't touch anything else in the block this close curly belongs to.). // // See extended comment in GetFormattingChangesAsync for more details on this. if (isSmartIndent && ch is '{' or '}') return true; var options = _editorOptionsService.GlobalOptions.GetAutoFormattingOptions(LanguageNames.CSharp); // If format-on-typing is not on, then we don't support formatting on any other characters. var autoFormattingOnTyping = options.FormatOnTyping; if (!autoFormattingOnTyping) return false; if (ch == '}' && !options.FormatOnCloseBrace) return false; if (ch == ';' && !options.FormatOnSemicolon) return false; // don't auto format after these keys if smart indenting is not on. if (ch is '#' or 'n' && !isSmartIndent) return false; return s_supportedChars.IndexOf(ch) >= 0; } public async Task<ImmutableArray<TextChange>> GetFormattingChangesAsync( Document document, ITextBuffer textBuffer, TextSpan? textSpan, CancellationToken cancellationToken) { var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var options = textBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), parsedDocument.LanguageServices, explicitFormat: true); var span = textSpan ?? new TextSpan(0, parsedDocument.Root.FullSpan.Length); var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(parsedDocument.Root, span); return Formatter.GetFormattedTextChanges(parsedDocument.Root, [formattingSpan], document.Project.Solution.Services, options, cancellationToken).ToImmutableArray(); } public async Task<ImmutableArray<TextChange>> GetFormattingChangesOnPasteAsync(Document document, ITextBuffer textBuffer, TextSpan textSpan, CancellationToken cancellationToken) { var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var options = textBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), parsedDocument.LanguageServices, explicitFormat: true); var service = parsedDocument.LanguageServices.GetRequiredService<ISyntaxFormattingService>(); return service.GetFormattingChangesOnPaste(parsedDocument, textSpan, options, cancellationToken); } public async Task<ImmutableArray<TextChange>> GetFormattingChangesOnReturnAsync(Document document, int caretPosition, CancellationToken cancellationToken) => []; public async Task<ImmutableArray<TextChange>> GetFormattingChangesAsync(Document document, ITextBuffer textBuffer, char typedChar, int position, CancellationToken cancellationToken) { var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var service = parsedDocument.LanguageServices.GetRequiredService<ISyntaxFormattingService>(); if (service.ShouldFormatOnTypedCharacter(parsedDocument, typedChar, position, cancellationToken)) { var indentationOptions = textBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), parsedDocument.LanguageServices, explicitFormat: false); return service.GetFormattingChangesOnTypedCharacter(parsedDocument, position, indentationOptions, cancellationToken); } return []; } }