Skip to content

Conversation

@sharwell
Copy link
Contributor

Prerequisite to #71731

@sharwell sharwell requested a review from a team as a code owner January 22, 2024 18:03
@ghost ghost added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Jan 22, 2024
nextCommandHandler();
}

protected override AbstractSnippetExpansionClient GetSnippetExpansionClient(ITextView textView, ITextBuffer subjectBuffer)
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 pull request decouples this factory method

editorOptionsService)
End Sub

Public Shared Function GetSnippetExpansionClient(
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 pull request decouples this factory method

internal interface ICustomCommitCompletionProvider
{
void Commit(CompletionItem completionItem, ITextView textView, ITextBuffer subjectBuffer, ITextSnapshot triggerSnapshot, char? commitChar);
void Commit(CompletionItem completionItem, Document document, ITextView textView, ITextBuffer subjectBuffer, ITextSnapshot triggerSnapshot, char? commitChar);
Copy link
Member

Choose a reason for hiding this comment

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

slight waryness as i believe we support completion in non-Document cases. However, it's likely we don't support Custom completion there, so this is likely fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of the callers already have a Document in scope. This is an internal interface that could be adjusted in the future if we need to support a new scenario.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Left this one as-is

IEditorCommandHandlerServiceFactory editorCommandHandlerServiceFactory,
IVsEditorAdaptersFactoryService editorAdaptersFactoryService,
[ImportMany] IEnumerable<Lazy<ArgumentProvider, OrderableLanguageMetadata>> argumentProviders,
EditorOptionsService editorOptionsService)
Copy link
Member

Choose a reason for hiding this comment

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

consider primary constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Switched to primary constructor

@sharwell sharwell changed the title Extract factory interface for AbstractSnippetCommandHandler Extract factory interface for AbstractSnippetExpansionClient Jan 23, 2024
Comment on lines 16 to 20
if (!textView.Properties.TryGetProperty(typeof(AbstractSnippetExpansionClient), out AbstractSnippetExpansionClient expansionClient))
{
expansionClient = CreateSnippetExpansionClient(textView, subjectBuffer);
textView.Properties.AddProperty(typeof(AbstractSnippetExpansionClient), expansionClient);
}
Copy link
Member

Choose a reason for hiding this comment

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

There's a GetOrCreateSingleton method for this exact pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

GetOrCreateSingleton doesn't support a non-capturing caller, so I kept the pattern we were already using.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Left this one as-is

Copy link
Member

Choose a reason for hiding this comment

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

What's the problem? I don't care if this would allocate a lambda -- the allocation cost here is unmeasurable...

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 ended up keeping this one not because I think the allocation cost would be a problem, but because the old code used the current pattern and I can't be sure the allocation cost wouldn't be a problem.


public AbstractSnippetExpansionClient GetSnippetExpansionClient(ITextView textView, ITextBuffer subjectBuffer)
{
if (!textView.Properties.TryGetProperty(typeof(AbstractSnippetExpansionClient), out AbstractSnippetExpansionClient expansionClient))
Copy link
Member

Choose a reason for hiding this comment

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

would it be possible to add some threading asserts here? it's not a big deal, it just makes me feel less oogy about doing weird stuff with proeprty bags on random threads.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Added assertion


internal interface ISnippetExpansionClientFactory : ILanguageService
{
AbstractSnippetExpansionClient GetSnippetExpansionClient(ITextView textView, ITextBuffer subjectBuffer);
Copy link
Member

Choose a reason for hiding this comment

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

nit: for the future would prefer if this exposed a particular interface. I don't love interfaces exposing abstract-reified types like this. But it's def not blocking, and i don't mind this corner of the LS working this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Left this one as-is

Private ReadOnly _editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory
Private ReadOnly _editorAdaptersFactoryService As IVsEditorAdaptersFactoryService
Private ReadOnly _argumentProviders As ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata))
Private ReadOnly _editorOptionsService As EditorOptionsService
Copy link
Member

Choose a reason for hiding this comment

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

nit. lots of duplication between the C# and VB factories. would love this to move down.

Copy link
Contributor Author

@sharwell sharwell Jan 23, 2024

Choose a reason for hiding this comment

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

➡️ The duplication does not appear to be avoidable, so for now left this one as-is.

Moving it down would add a large number of parameters to the base class, and would not simplify either of the derived type constructors. The current form makes it easier to see if any specific implementation no longer needs its parameters.

Copy link
Member

Choose a reason for hiding this comment

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

I think if we were going to unify this further, the trick would be to get rid of the derivations of AbstractSnippetExpansionClient, and instead move any of the different behaviors there into a language service with the appropriate methods...but one step at a time seems reasonable here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would help tremendously for testing. I'll see how viable that is next.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Now refactored in the latest commit

_signatureHelpControllerProvider = signatureHelpControllerProvider
_editorCommandHandlerServiceFactory = editorCommandHandlerServiceFactory
_editorAdaptersFactoryService = editorAdaptersFactoryService
_argumentProviders = argumentProviders.ToImmutableArray()
Copy link
Member

Choose a reason for hiding this comment

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

same. this is duplicated with teh C# version. Could move into the abstract base (including with a primary cosntructor :D). I would really like to see that in this pr :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Refactored to reduce duplication

@CyrusNajmabadi
Copy link
Member

Signing off with the strong ask about moving the shared field code to the base class. I'm ok with that happening in a quick followup PR though if you would prefer to get this in and unblcokign you.

{
return new CSharpSnippetExpansionClient(
_threadingContext,
Guids.CSharpLanguageServiceId,
Copy link
Member

Choose a reason for hiding this comment

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

Does anything ever pass in a different constant for the this? Or should this just be set directly in CSharpSnippetExpansionClient.cs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ In light of #71752 (comment), leaving this for a follow-up

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Fixed now

Comment on lines 36 to 41
private readonly IThreadingContext _threadingContext = threadingContext;
private readonly SignatureHelpControllerProvider _signatureHelpControllerProvider = signatureHelpControllerProvider;
private readonly IEditorCommandHandlerServiceFactory _editorCommandHandlerServiceFactory = editorCommandHandlerServiceFactory;
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService = editorAdaptersFactoryService;
private readonly ImmutableArray<Lazy<ArgumentProvider, OrderableLanguageMetadata>> _argumentProviders = argumentProviders.ToImmutableArray();
private readonly EditorOptionsService _editorOptionsService = editorOptionsService;
Copy link
Member

Choose a reason for hiding this comment

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

Should we just get rid of these and use the constructor parameters? It'd definitely clean this type up nicely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ For me, this change is blocked on insufficient resolution to #70419

Private ReadOnly _editorCommandHandlerServiceFactory As IEditorCommandHandlerServiceFactory
Private ReadOnly _editorAdaptersFactoryService As IVsEditorAdaptersFactoryService
Private ReadOnly _argumentProviders As ImmutableArray(Of Lazy(Of ArgumentProvider, OrderableLanguageMetadata))
Private ReadOnly _editorOptionsService As EditorOptionsService
Copy link
Member

Choose a reason for hiding this comment

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

I think if we were going to unify this further, the trick would be to get rid of the derivations of AbstractSnippetExpansionClient, and instead move any of the different behaviors there into a language service with the appropriate methods...but one step at a time seems reasonable here.

Comment on lines 46 to 58
If languageName = LanguageNames.CSharp Then
snippetCommandHandler = Workspace.ExportProvider.GetExportedValues(Of ICommandHandler)().Single(
Function(commandHandler)
Return TryCast(commandHandler, CSharp.Snippets.SnippetCommandHandler) IsNot Nothing
End Function)
Else
snippetCommandHandler = Workspace.ExportProvider.GetExportedValues(Of ICommandHandler)().Single(
Function(commandHandler)
Return TryCast(commandHandler, VisualBasic.Snippets.SnippetCommandHandler) IsNot Nothing
End Function)
End If

Me.SnippetCommandHandler = DirectCast(snippetCommandHandler, AbstractSnippetCommandHandler)
Copy link
Member

Choose a reason for hiding this comment

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

Any reason we can't just pick out the one that derives from AbstractSnippetCommandHandler and filter with MEF metadata? This feels pretty clunky.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Fixed by using an existing helper method

End Function
End Class

<ExportLanguageService(GetType(ISnippetExpansionClientFactory), LanguageNames.VisualBasic, ServiceLayer.Test)>
Copy link
Member

Choose a reason for hiding this comment

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

We should be able to do a single export for both languages, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Also leaving this one for #71752 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

➡️ Only one ExportLanguageService attribute can be applied to a type

@dotnet dotnet deleted a comment from azure-pipelines bot Feb 13, 2024
@sharwell sharwell merged commit 5eae863 into dotnet:main Feb 13, 2024
@sharwell sharwell deleted the extract-factory branch February 13, 2024 16:44
@ghost ghost added this to the Next milestone Feb 13, 2024
@jjonescz jjonescz removed this from the Next milestone Feb 27, 2024
@jjonescz jjonescz added this to the 17.10 P2 milestone Feb 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants