Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.BraceCompletion;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
Expand All @@ -31,52 +32,46 @@ internal partial class BraceCompletionSessionProvider
// fortunately, editor provides another extension point where we have more control over brace completion but we do not
// want to re-implement logics base session provider already provides. so I ported editor's default session and
// modified it little bit so that we can use it as base class.
private class BraceCompletionSession : IBraceCompletionSession
private class BraceCompletionSession(
ITextView textView,
ITextBuffer subjectBuffer,
SnapshotPoint openingPoint,
char openingBrace,
char closingBrace,
ITextUndoHistory undoHistory,
IEditorOperationsFactoryService editorOperationsFactoryService,
EditorOptionsService editorOptionsService,
IBraceCompletionService service,
IThreadingContext threadingContext) : IBraceCompletionSession
{
public char OpeningBrace { get; }
public char ClosingBrace { get; }
private readonly ITextUndoHistory _undoHistory = undoHistory;
private readonly IEditorOperations _editorOperations = editorOperationsFactoryService.GetEditorOperations(textView);
private readonly EditorOptionsService _editorOptionsService = editorOptionsService;
private readonly IBraceCompletionService _service = service;
private readonly IThreadingContext _threadingContext = threadingContext;

public char OpeningBrace { get; } = openingBrace;
public char ClosingBrace { get; } = closingBrace;

public ITrackingPoint OpeningPoint { get; private set; }
public ITrackingPoint ClosingPoint { get; private set; }
public ITextBuffer SubjectBuffer { get; }
public ITextView TextView { get; }

private readonly ITextUndoHistory _undoHistory;
private readonly IEditorOperations _editorOperations;
private readonly EditorOptionsService _editorOptionsService;
private readonly IBraceCompletionService _service;
private readonly IThreadingContext _threadingContext;

public BraceCompletionSession(
ITextView textView, ITextBuffer subjectBuffer,
SnapshotPoint openingPoint, char openingBrace, char closingBrace, ITextUndoHistory undoHistory,
IEditorOperationsFactoryService editorOperationsFactoryService,
EditorOptionsService editorOptionsService, IBraceCompletionService service, IThreadingContext threadingContext)
{
TextView = textView;
SubjectBuffer = subjectBuffer;
OpeningBrace = openingBrace;
ClosingBrace = closingBrace;
ClosingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(openingPoint.Position, PointTrackingMode.Positive);
_undoHistory = undoHistory;
_editorOperations = editorOperationsFactoryService.GetEditorOperations(textView);
_editorOptionsService = editorOptionsService;
_service = service;
_threadingContext = threadingContext;
}
public ITrackingPoint ClosingPoint { get; private set; } = subjectBuffer.CurrentSnapshot.CreateTrackingPoint(openingPoint.Position, PointTrackingMode.Positive);

public ITextBuffer SubjectBuffer { get; } = subjectBuffer;
public ITextView TextView { get; } = textView;

#region IBraceCompletionSession Methods

public void Start()
{
_threadingContext.ThrowIfNotOnUIThread();

// Brace completion is not cancellable.
if (!this.TryStart(CancellationToken.None))
{
var success = _threadingContext.JoinableTaskFactory.Run(() => TryStartAsync(CancellationToken.None));
if (!success)
EndSession();
}
}

private bool TryStart(CancellationToken cancellationToken)
private async Task<bool> TryStartAsync(CancellationToken cancellationToken)
{
_threadingContext.ThrowIfNotOnUIThread();
var closingSnapshotPoint = ClosingPoint.GetPoint(SubjectBuffer.CurrentSnapshot);
Expand All @@ -101,18 +96,15 @@ private bool TryStart(CancellationToken cancellationToken)

var document = SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document == null)
{
return false;
}

var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken);
var context = GetBraceCompletionContext(parsedDocument, document.Project.GetFallbackAnalyzerOptions());

// Note: completes synchronously unless Semantic Model is needed to determine the result:
if (!_service.HasBraceCompletionAsync(context, document, cancellationToken).WaitAndGetResult(cancellationToken))
{
var hasBraceCompletions = await _service.HasBraceCompletionAsync(context, document, cancellationToken).ConfigureAwait(true);
if (!hasBraceCompletions)
return false;
}

var braceResult = _service.GetBraceCompletion(context);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion;
[ExportBraceCompletionService(LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class BracketBraceCompletionService() : AbstractCurlyBraceOrBracketCompletionService
internal sealed class BracketBraceCompletionService() : AbstractCurlyBraceOrBracketCompletionService
{
protected override char OpeningBrace => Bracket.OpenCharacter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion;
[ExportBraceCompletionService(LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class CharLiteralBraceCompletionService() : AbstractCSharpBraceCompletionService
internal sealed class CharLiteralBraceCompletionService() : AbstractCSharpBraceCompletionService
{
protected override char OpeningBrace => SingleQuote.OpenCharacter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion;
[ExportBraceCompletionService(LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class CurlyBraceCompletionService() : AbstractCurlyBraceOrBracketCompletionService
internal sealed class CurlyBraceCompletionService() : AbstractCurlyBraceOrBracketCompletionService
{
protected override char OpeningBrace => CurlyBrace.OpenCharacter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion;
[ExportBraceCompletionService(LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class InterpolationBraceCompletionService() : AbstractCSharpBraceCompletionService
internal sealed class InterpolationBraceCompletionService() : AbstractCSharpBraceCompletionService
{
protected override char OpeningBrace => CurlyBrace.OpenCharacter;
protected override char ClosingBrace => CurlyBrace.CloseCharacter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion;

[ExportBraceCompletionService(LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class LessAndGreaterThanBraceCompletionService() : AbstractCSharpBraceCompletionService
internal sealed class LessAndGreaterThanBraceCompletionService() : AbstractCSharpBraceCompletionService
{
protected override bool NeedsSemantics => true;

Expand All @@ -34,20 +35,20 @@ protected override bool IsValidOpeningBraceToken(SyntaxToken token)
protected override bool IsValidClosingBraceToken(SyntaxToken token)
=> token.IsKind(SyntaxKind.GreaterThanToken);

protected override Task<bool> IsValidOpenBraceTokenAtPositionAsync(Document document, SyntaxToken token, int position, CancellationToken cancellationToken)
protected override ValueTask<bool> IsValidOpenBraceTokenAtPositionAsync(Document document, SyntaxToken token, int position, CancellationToken cancellationToken)
{
// check what parser thinks about the newly typed "<" and only proceed if parser thinks it is "<" of
// type argument or parameter list
if (token.CheckParent<TypeParameterListSyntax>(n => n.LessThanToken == token) ||
token.CheckParent<TypeArgumentListSyntax>(n => n.LessThanToken == token) ||
token.CheckParent<FunctionPointerParameterListSyntax>(n => n.LessThanToken == token))
{
return Task.FromResult(true);
return ValueTaskFactory.FromResult(true);
}

// type argument can be easily ambiguous with normal < operations
if (token.Parent is not BinaryExpressionSyntax(SyntaxKind.LessThanExpression) node || node.OperatorToken != token)
return Task.FromResult(false);
return ValueTaskFactory.FromResult(false);

// type_argument_list only shows up in the following grammar construct:
//
Expand All @@ -57,11 +58,11 @@ protected override Task<bool> IsValidOpenBraceTokenAtPositionAsync(Document docu
// So if the prior token is not an identifier, this could not be a type-argument-list.
var previousToken = token.GetPreviousToken();
if (previousToken.Parent is not IdentifierNameSyntax identifier)
return Task.FromResult(false);
return ValueTaskFactory.FromResult(false);

return IsSemanticTypeArgumentAsync(document, node.SpanStart, identifier, cancellationToken);

static async Task<bool> IsSemanticTypeArgumentAsync(Document document, int position, IdentifierNameSyntax identifier, CancellationToken cancellationToken)
static async ValueTask<bool> IsSemanticTypeArgumentAsync(Document document, int position, IdentifierNameSyntax identifier, CancellationToken cancellationToken)
{
var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);
var info = semanticModel.GetSymbolInfo(identifier, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion;
[ExportBraceCompletionService(LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class ParenthesisBraceCompletionService() : AbstractCSharpBraceCompletionService
internal sealed class ParenthesisBraceCompletionService() : AbstractCSharpBraceCompletionService
{
protected override char OpeningBrace => Parenthesis.OpenCharacter;
protected override char ClosingBrace => Parenthesis.CloseCharacter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,14 @@ internal abstract class AbstractBraceCompletionService : IBraceCompletionService

public abstract bool AllowOverType(BraceCompletionContext braceCompletionContext, CancellationToken cancellationToken);

public Task<bool> HasBraceCompletionAsync(BraceCompletionContext context, Document document, CancellationToken cancellationToken)
public ValueTask<bool> HasBraceCompletionAsync(BraceCompletionContext context, Document document, CancellationToken cancellationToken)
{
if (!context.HasCompletionForOpeningBrace(OpeningBrace))
{
return Task.FromResult(false);
}
return ValueTaskFactory.FromResult(false);

var openingToken = context.GetOpeningToken();
if (!NeedsSemantics)
{
return Task.FromResult(IsValidOpenBraceTokenAtPosition(context.Document.Text, openingToken, context.OpeningPoint));
}
return ValueTaskFactory.FromResult(IsValidOpenBraceTokenAtPosition(context.Document.Text, openingToken, context.OpeningPoint));

// Pass along a document with frozen partial semantics. Brace completion is a highly latency sensitive
// operation. We don't want to wait on things like source generators to figure things out.
Expand Down Expand Up @@ -104,7 +100,7 @@ public virtual bool CanProvideBraceCompletion(char brace, int openingPosition, P
/// <summary>
/// Only called if <see cref="NeedsSemantics"/> returns true;
/// </summary>
protected virtual Task<bool> IsValidOpenBraceTokenAtPositionAsync(Document document, SyntaxToken token, int position, CancellationToken cancellationToken)
protected virtual ValueTask<bool> IsValidOpenBraceTokenAtPositionAsync(Document document, SyntaxToken token, int position, CancellationToken cancellationToken)
{
// Subclass should have overridden this.
throw ExceptionUtilities.Unreachable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ internal interface IBraceCompletionService
/// True if <see cref="BraceCompletionResult"/> is available in the given <paramref name="context"/>.
/// Completes synchronously unless the service needs Semantic Model to determine the brace completion result.
/// </summary>
Task<bool> HasBraceCompletionAsync(BraceCompletionContext context, Document document, CancellationToken cancellationToken);
ValueTask<bool> HasBraceCompletionAsync(BraceCompletionContext context, Document document, CancellationToken cancellationToken);

/// <summary>
/// Returns the text change to add the closing brace given the context.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.BraceCompletion
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.LanguageService
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic.BraceCompletion
<ExportBraceCompletionService(LanguageNames.VisualBasic), [Shared]>
Friend Class BracketBraceCompletionService
Friend NotInheritable Class BracketBraceCompletionService
Inherits AbstractVisualBasicBraceCompletionService

<ImportingConstructor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.BraceCompletion
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.VisualBasic

Namespace Microsoft.CodeAnalysis.VisualBasic.BraceCompletion
<ExportBraceCompletionService(LanguageNames.VisualBasic), [Shared]>
Friend Class CurlyBraceCompletionService
Friend NotInheritable Class CurlyBraceCompletionService
Inherits AbstractVisualBasicBraceCompletionService

<ImportingConstructor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.BraceCompletion
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery

Namespace Microsoft.CodeAnalysis.VisualBasic.BraceCompletion
<ExportBraceCompletionService(LanguageNames.VisualBasic), [Shared]>
Friend Class InterpolatedStringBraceCompletionService
Friend NotInheritable Class InterpolatedStringBraceCompletionService
Inherits AbstractVisualBasicBraceCompletionService

<ImportingConstructor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.BraceCompletion
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic

Namespace Microsoft.CodeAnalysis.VisualBasic.BraceCompletion
<ExportBraceCompletionService(LanguageNames.VisualBasic), [Shared]>
Friend Class InterpolationBraceCompletionService
Friend NotInheritable Class InterpolationBraceCompletionService
Inherits AbstractVisualBasicBraceCompletionService

<ImportingConstructor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.BraceCompletion
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic.BraceCompletion
<ExportBraceCompletionService(LanguageNames.VisualBasic), [Shared]>
Friend Class LessAndGreaterThanCompletionService
Friend NotInheritable Class LessAndGreaterThanCompletionService
Inherits AbstractVisualBasicBraceCompletionService

<ImportingConstructor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic.BraceCompletion
<ExportBraceCompletionService(LanguageNames.VisualBasic), [Shared]>
Friend Class ParenthesisBraceCompletionService
Friend NotInheritable Class ParenthesisBraceCompletionService
Inherits AbstractVisualBasicBraceCompletionService

<ImportingConstructor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.BraceCompletion
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.VisualBasic

Namespace Microsoft.CodeAnalysis.VisualBasic.BraceCompletion
<ExportBraceCompletionService(LanguageNames.VisualBasic), [Shared]>
Friend Class StringLiteralBraceCompletionService
Friend NotInheritable Class StringLiteralBraceCompletionService
Inherits AbstractVisualBasicBraceCompletionService

<ImportingConstructor>
Expand Down
Loading