Code snippet dynamic parameter setting.

SyntaxEditor for WPF Forum

Posted 3 years ago by Sunshine - Appeon
Version: 21.1.1
Avatar

Does the code snippet support the setting of dynamic parameters?

For example, I want to insert a constructor.

In C# usually enter 'ctor', and then press Tab.

public class Class1
{
	public Class1()
	{

        }
}

But the result I got is:

public class Class1
{
	public DefaultClassName()
	{

        }
}

When inserting the fragment, I have no chance to set the value of DefaultClassName, it will only use the default value in the snippet file.

What should I do to achieve the desired effect?

[Modified 3 years ago]

Comments (15)

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

It doesn't appear to support that feature at the moment.  Can you paste the source of this particular code snippet so we can make sure we're looking at the same thing?  Also, what API would you ideally like to have added to support this feature?


Actipro Software Support

Posted 3 years ago by Sunshine - Appeon
Avatar

This is a sample snippet file for the constructor.

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>ctor</Title>
			<Shortcut>ctor</Shortcut>
			<Description>Code snippet for constructor</Description>
			<Author>Microsoft Corporation</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal IsEditable="False">
					<ID>classname</ID>
					<ToolTip>Class name</ToolTip>
					<Default>ClassNamePlaceholder</Default>
					<Function>ClassName()</Function>
				</Literal>
			</Declarations>
			<Code Language="csharp"><![CDATA[public $classname$ ()
{
	$end$
}]]></Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

From my point of view, only a user-defined method is needed to return the actual value corresponding to each ID.

When formatting the content of the code segment, please call the method implemented by the user to obtain the ID mapping table. Use the data in the mapping table to replace the actual content of the ID instead of directly replacing the default value.

Do you think there is a problem with this method. I think this is both effective and easy to implement.

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

We already are fully populating the information in the ICodeSnippet, including the ICodeSnippetLiteralDeclaration.FunctionInvocation property.  Based on what you've described, you would like us to have some kind of callback method so that when we go to retrieve the default value for a literal declaration, we pass in the ICodeSnippetDeclaration and the default text value is returned.  That callback would have a default implementation of returning the ICodeSnippetDeclaration.DefaultText, but you would have an opportunity to override the result.  Is that what you need?

Where would you ideally like to see this implemented and what context information (ICodeSnippet, IEditorView, etc.) would you require passed in to make a successful implementation of this?


Actipro Software Support

Posted 3 years ago by Sunshine - Appeon
Avatar

Yes, I need an opportunity to override the default value in the code snippet.In the language of C#, some parameters of the code snippet must be dynamic.

I only need ICodeSnippet, IEditorView information to complete this requirement.

When can this feature be supported?

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

For the next maintenance release we are adding a ICodeSnippetTemplateSessionEventSink.NotifySessionOpening method.  So if you make a class that inherits ICodeSnippetTemplateSessionEventSink and register an instance of that class on your syntax language you can then add code like the following into the new NotifySessionOpening method to adjust the default text for a declaration prior to the default text being inserted:

public void NotifySessionOpening(ICodeSnippetTemplateSession session) {
	if ((session.CodeSnippet.Title == "ctor") && (session.CodeSnippet.Declarations.Count > 0)) {
		if ((session.CodeSnippet.Declarations[0] is CodeSnippetDeclarationBase decl) && (decl.FunctionInvocation == "ClassName()")) {
			// session.View will get access to the editor and document...
			//   Determine the class name and set the result in the following line...
			decl.DefaultText = "FindClassNameAndSetHere";
		}
	}
}


Actipro Software Support

Posted 3 years ago by Sunshine - Appeon
Avatar

Okay, I will give feedback after trying this feature in the next version

Posted 3 years ago by Sunshine - Appeon
Avatar
<Literal IsEditable="False">
	<ID>classname</ID>
	<ToolTip>Class name</ToolTip>
	<Default>ClassNamePlaceholder</Default>
	<Function>ClassName()</Function>
</Literal>

The attribute IsEditable in the text label indicates whether the item needs to be selected. If False, the cursor will not select it. Currently this property setting seems to be invalid.

[Modified 3 years ago]

Posted 3 years ago by Sunshine - Appeon
Avatar
<Snippet>
	<Declarations>
		<Literal Editable="false">
			<ID>SystemConsole</ID>
			<Function>SimpleTypeName(global::System.Console) </Function>
		</Literal>
	</Declarations>
	<Code Language="csharp"><![CDATA[$SystemConsole$.WriteLine($end$);]]></Code>
</Snippet>

There is also a kind of function snippet.

In this case, I need to get the scope of the "declaration" tag in the text. Then, I have the opportunity to decide whether to simplify the function name.

For example:

If I using to the System namespace, the content generated by the snippet is 'Console.WriteLine("")'.

If not, generate 'System.Console.WriteLine("")'.

However, the scope of the "declaration" tag is not currently provided. Is it possible to support this requirement?

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

1) Note that in the snippet XML schema, it's Editable, not IsEditable.  That being said, right now it seems like initial focus onto a field when a template session is activated will skip any fields with Editable="false".  But pressing Tab after that is allowing focus back onto fields with Editable="false".  That is the problem being reported here, correct?

2) You can determine the context location by looking at the "session.View.Selection.CaretOffset" property to get the offset of the caret.  Then use that offset with parse data or whatever context mechanism you have available to determine how the content generated should be altered.


Actipro Software Support

Posted 3 years ago by Sunshine - Appeon
Avatar

1) I think this is an Xml parsing problem. Even if the IsEditable property in XML is set to false. The IsEditable property of the ICodeSnippetTemplateSession.CodeSinppet.Declarations object is still true.

You can use the following XML example to test

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>ctor</Title>
			<Shortcut>ctor</Shortcut>
			<Description>Code snippet for constructor</Description>
			<Author>Microsoft Corporation</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal Editable="false">
					<ID>classname</ID>
					<ToolTip>Class name</ToolTip>
					<Function>ClassName()</Function>
					<Default>ClassNamePlaceholder</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp"><![CDATA[public $classname$()
{
	$end$
}]]>
			</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

2) There may be multiple Declaration tags in a snippet, and they may not be continuous in the snippet. I only know the 'session.View.Selection.CaretOffset' property, how do I get the range of each Declaration tag in the document?

Can you provide a relevant example? Thanks a lot!

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

1) I'm sorry but I'm not seeing that here.  When I load that exact code snippet with Editable="false", I see the resulting declaration object having IsEditable=false.

2) I'm not sure that info is provided at this time.  What I meant is that the upcoming ICodeSnippetTemplateSessionEventSink.NotifySessionOpening callback will fire before any text for the snippet in inserted into the document.  You can look at the session.View's selection there to get the context of where the snippet will be inserted.  That is where you would want to alter the declaration's default text anyhow, before fields for the declaration are ever created.


Actipro Software Support

Posted 3 years ago by Sunshine - Appeon
Avatar

1)I'm so sory.This is caused by the caching problem I made myself. As you said, it works normally.

2)I understand what you mean, you mean I am when NotifySessionOpening is triggered. Change the default text of the statement.

But in this case, this is a simplified snippet operation. So if I want to get the correct text, I need to insert the snippet code

$SystemConsole$.WriteLine($end$);

convert to

global::System.Console.WriteLine($end$);

Then I submit the range of ‘global::System.Console’ in the text to the analyzer. The result returned by the analyzer is "global::System.Console" or the simplified "Console". Only then can I modify the default text of the declaration.

Maybe I can also get the scope of each declaration through regular expressions. 

[Modified 3 years ago]

Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

How does the analyzer you mentioned work?  Like what do you feed into it and what does it return?

The main idea behind the code snippets is that you want to determine the default text to insert for each declaration in a NotifySessionOpening handler before the code snippet inserts, not after the code snippet inserts.  You can determine the selection/caret offsets in NotifySessionOpening and thus can figure out where in the document you are at that point.


Actipro Software Support

Posted 3 years ago by Sunshine - Appeon
Avatar

The analyzer I mentioned is actually ‘Roslyn’, and our CSharp grammar service uses it for analysis. I have observed that some of the CSharp feature you provide are also obtained through ‘Roslyn’, so you should have a partial understanding of it.

I need to provide it with the inserted declaration range. Then it judges whether the target namespace is introduced in the document, then determines whether the name needs to be simplified, and finally sends the result back to me. After I got the result, I changed the default text of the corresponding declaration through the NotifySessionOpening event.

For Editor, we should determine the inserted content before inserting the fragment, but this content needs to be dynamically analyzed.

For the Analyzer, we must first update the content of the code snippet to its virtual document, and then we can analyze the result, and then we can know the correct content that the editor needs to insert.

Answer - Posted 3 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

It sounds like by "virtual document" that you are already creating a cloned copy of the document text with the view's selection replaced by the code snippet's content and are running the analyzer on that.  I was going to suggest that as a possibility if needed.  

Our code snippet API doesn't know where the declaration fields will be until after it already knows the default text and handles some other text manipulation (selection injection as needed, etc.).  

Your best bet with the above approach would probably be to search for the various "$.*$" ranges in the snippet to determine their text ranges in your virtual document for your analysis.


Actipro Software Support

The latest build of this product (v24.1.2) was released 1 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.