[Visual Studio Extensibility] Adding/Installing snippets programmatically

Malte Klapper 1 Reputation point
2022-09-22T08:25:18.413+00:00

Hi,

I would like to fill a custom completion set/expansion menu with code snippets obtained from a server.

Is there a way to create snippet completion items programmatically without the need to create XML files?
I'm looking for something like VS Code has with CompletionItem and CompletionItemKind.Snippet.

Thank you!

Not Monitored
Not Monitored
Tag not monitored by Microsoft.
35,782 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Malte Klapper 1 Reputation point
    2022-10-12T07:32:50.957+00:00

    So I finally found a way to do in-memory snippets.

    The solution can be divided into two parts:

    • The completion session with the known completion dropdown
    • The snippet session, which allows switching between literals and so on

    1 Provide custom completion
    For the first part, I used this example of the AsyncCompletion.

    2 Starting expansion session with in-memory snippet
    For the snippet session I found a method on IVsExpansion called InsertSpecificExpansion (see documentation)
    This allows starting an expansion session with an in-memory XMLDOMNode. To be able to use this with my custom in memory snippet structure I had to do the following:

    • Create a serializable C#-Model for the Visual Studio Snippet Schema
    • Parse my custom snippets to that VS-Model
    • Use XML Serializer to create a IXMLDOMNode

    3 Link the parts together
    In order to link the two parts, I used the following approach: When a CompletionItem is chosen from the list, I cancel the regular completion and instead perform a ExpansionSession.
    First I had to add an additional Property to the CompletionItem so that the ExpansionSession later can access the XMLNode:

    completionItem.Properties.AddProperty("SnippetXML", snippetXMLNode);  
    

    This can later be used by the ExpansionSession to provide the node Parameter of InsertSpecificExpansion

    In CommitManager.TryCommit I first start the ExpansionSession and mark the CompletionSession as handled so no further actions are done:

            // use IVsEditorAdaptersFactoryService to get access to IVsTextview  
    	var vsTextView = VsEditorAdapter.GetViewAdapter(session.TextView);  
    	// start a snippet session using in-memory XML rather than .xml files  
    	ExpansionClient.StartExpansion(vsTextView, item);  
    
    	// we handled the completion by starting an expansion session so no other handlers should participate  
    	return CommitResult.Handled;  
    

    The ExpansionClient is an implementation of IOleCommandTarget and IVsExpansionClient. The Expansion follows the approach described here but the difference is it calls InsertSpecificExpansion instead of InsertNamedExpansion.

    I hope this may help anyone with the same problem. At some point, I will add a complete example on GitHub.