Inserting snippet in OnAutoCompleteAccept event

Topics: Developer Forum, User Forum
Jun 20, 2012 at 9:29 AM
Edited Jun 20, 2012 at 9:41 AM

Hi.

I encounter a "ArgumentOutOfRangeException -> Index was out of range. Only ref in the call stack is Program.Main() or the Application.Run call, that is, so I'm guessing this is purely on scintilla?

Here is exactly what I'm doing in C#:

 

this.textEditor.AutoCompleteAccepted += new EventHandler<AutoCompleteAcceptedEventArgs>(OnAutoCompleteAccepted);

...

public void OnAutoCompleteAccepted(object sender, AutoCompleteAcceptedEventArgs e)
{
     textEditor.Snippets.InsertSnippet("vmtTextParamValueSnip");
}

 

...and here is the snippet from the lang xml:

 

<Snippets IsOneKeySelectionEmbedEnabled="True" IsEnabled="true">
      <Snippet Shortcut="vmtTextParamValueSnip" IsSurroundsWith="true">"$path$"</Snippet>
</Snippets>

The snippets can be perfectly placed using Ctrl+J (the snippet insert window) and pressing enter. I can also insert snippets in other methods using InsertSnippet(""), but not this one ( I havent tried in other scintillaNET events than OnCharAdded and this one

I have tried to locate the problem in ScintillaNET source sln. for C#, but have not found much yet.

Any pointers or help would be greatly appreciated, as I have to get this working soon.

PS: I _have_ to be able to call this insertsnippet function when a autocomplete has been completed/accepted

Edit:

Forgot to mention that if I add a messagebox.show(""); call right before the insertsnippet() call in the even handler, the insertSnippet function works, but the autocomplete word is not "accepted", meaning it's more or less cancelled by the messagebox event message.

Is there some underlying conflict in the messaging queue, or a cancelling before it's ready?

For reference, if it might help ( SnippetNET -> SnippetChooser.cs )  Seems like it is doable here:

 

private void txtSnippet_AutoCompleteAccepted(object sender, AutoCompleteAcceptedEventArgs e)
{
      string shortcut = txtSnippet.AutoComplete.SelectedText;
      Hide();
      Scintilla.Snippets.InsertSnippet(shortcut);
}

Coordinator
Jun 20, 2012 at 11:10 PM

This is a lot of good information but I expect that I would still have a heck of a time reproducing this. However, I think with a little more digging you'll be able to identify the root exception. Make sure you have the PDB files from our binary package included in your bin and also download and unzip our source code package. From the Debug menu, select Exceptions and check the Thrown box for System.ArgumentOutOfRangeException.

Run your app again.

This should cause the debugger to break on your ArgumentOutOfRangeException. If the source of the exception is within the ScintillaNET source code, the debugger will ask your for the location of the CS file. Browse to the matching file from the unzipped source and you should get a detailed stack trace, variables, etc...

Report back with any additional info you find.

 

Thanks,

Jacob

 

Jun 21, 2012 at 1:26 AM
Edited Jun 21, 2012 at 1:53 AM

Hello again, and thank you for the response.

I did what you suggested, and was directed to the code throwing the exception (It was inside SnippetManager.cs)

Here is the property causing it (marked the line that threw):

 

[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool IsActive
        {
            get
            {
                return _snippetLinks.IsActive;
            }
            internal set
            {
                _snippetLinks.IsActive = value;

                if (value)
                {
                    SetIndicators();
                    _snippetLinks[0].Ranges[0].Select(); // ArgumentOutOfRangeException thrown by this
                }
                else
                {
                    //	Deactivating Snippet Link mode. First make sure all
                    //	the snippet link ranges have their indicators cleared
                    foreach (SnippetLink sl in _snippetLinks.Values)
                        foreach (Range r in sl.Ranges)
                        {
                            r.ClearIndicator(Scintilla.Snippets.InactiveSnippetIndicator);
                            r.ClearIndicator(Scintilla.Snippets.ActiveSnippetIndicator);
                        }

                    //	Then clear out the _snippetLinks list cuz we're done with them
                    _snippetLinks.Clear();

                    if (_snippetLinks.EndPoint != null)
                    {
                        _snippetLinks.EndPoint.Dispose();
                        _snippetLinks.EndPoint = null;
                    }
                }
            }
        }

 

Here are the locals at that point (sorry for the big mess. Expanded lines marked with underline):

 

-this	{ScintillaNET.SnippetManager}	ScintillaNET.SnippetManager
+	base	{ScintillaNET.SnippetManager}	ScintillaNET.TopLevelHelper {ScintillaNET.SnippetManager}
+	_activeSnippetColor	"{Name=Lime, ARGB=(255, 0, 255, 0)}"	System.Drawing.Color
	_activeSnippetIndicator	1	int
	_activeSnippetIndicatorStyle	RoundBox	ScintillaNET.IndicatorStyle
	_defaultDelimeter	36 '$'	char
+	_inactiveSnippetColor	"{Name=Lime, ARGB=(255, 0, 255, 0)}"	System.Drawing.Color
	_inactiveSnippetIndicator	16	int
	_inactiveSnippetIndicatorStyle	Box	ScintillaNET.IndicatorStyle
	_isEnabled	true	bool
	_isOneKeySelectionEmbedEnabled	true	bool
+	_list	Count = 1	ScintillaNET.SnippetList
	_pendingUndo	false	bool
	_snipperChooser	null	ScintillaNET.SnippetChooser
-	_snippetLinks	{ScintillaNET.SnippetLinkCollection}	ScintillaNET.SnippetLinkCollection
	_activeLinkIndex	-1	int
  	_activeRange	null	ScintillaNET.SnippetLinkRange
	_endPoint	null	ScintillaNET.SnippetLinkEnd
	_isActive	true	bool
-	_snippetLinks	Count = 1	System.Collections.Generic.List<ScintillaNET.SnippetLink>
-	[0]	{ScintillaNET.SnippetLink}	ScintillaNET.SnippetLink
	_key	"path"	string
-	_ranges	Count = 0	System.Collections.Generic.List<ScintillaNET.SnippetLinkRange>
-	Raw View		                       
		Capacity 4	int
		Count	 0	int
+		Static members		
+		Non-Public members		
	Key	"path"	string
+	Ranges	Count = 0	System.Collections.Generic.List<ScintillaNET.SnippetLinkRange>
+	Raw View		
	ActiveRange	null	ScintillaNET.SnippetLinkRange
	ActiveSnippetLink	null	ScintillaNET.SnippetLink
	Count	1	int
	EndPoint	null	ScintillaNET.SnippetLinkEnd
	IsActive	true	bool
	IsReadOnly	false	bool
+	Keys	{string[1]}	System.Collections.Generic.ICollection<string> {string[]}
	NextActiveSnippetLink	null	ScintillaNET.SnippetLink
	PreviousActiveSnippetLink	null	ScintillaNET.SnippetLink
+	Values	{ScintillaNET.SnippetLink[1]}	System.Collections.Generic.ICollection<ScintillaNET.SnippetLink> {ScintillaNET.SnippetLink[]}
+	_snippetLinkTimer	{Interval = 1}	System.Windows.Forms.Timer
+	ActiveSnippetColor	"{Name=Lime, ARGB=(255, 0, 255, 0)}"	System.Drawing.Color
	ActiveSnippetIndicator	1	int
	ActiveSnippetIndicatorStyle	RoundBox	ScintillaNET.IndicatorStyle
	DefaultDelimeter	36 '$'	char
+	InactiveSnippetColor	"{Name=Lime, ARGB=(255, 0, 255, 0)}"	System.Drawing.Color
	InactiveSnippetIndicator	16	int
	InactiveSnippetIndicatorStyle	Box	ScintillaNET.IndicatorStyle
	IsActive	true	bool
	IsEnabled	true	bool
	IsOneKeySelectionEmbedEnabled	true	bool
+	List	Count = 1	ScintillaNET.SnippetList
+	snippetRegex1	{(?<dm>DropMarker(?<dmi>\[[0-9]*\])?)|(?<c>caret)|(?<a>anchor)|(?<e>_end)|(?<s>selected)|(?<l>.+?(?<li>\[[0-9]*\])?)}	System.Text.RegularExpressions.Regex

 

So, one step closer.

I added the check for double-checking the trace:

 

if( Snippets.IsActive )
                Snippets.InsertSnippet("vmtTextParamValueSnip");

 

.. this returns false, which is weird as the;

 

if (value)
{
    SetIndicators();
    _snippetLinks[0].Ranges[0].Select();
}

 

goes through if it IS set to active (I presume).

Here is a additional test I did, which made it work, but not after the autocomplete text was shown, but right before (but the snippet was appended AFTER the autocompleted word when the handler was finished. 

 

(in AutoComplete handler):

 

Snippets.ShowSnippetList(); // This obviously activates it properly
Snippets.InsertSnippet("vmtTextParamValueSnip");

 

So basically it semi-worked, but I don't wan't the list popping up, as it's supposed to be automatically inserted at a special condition, so it's not something the user chooses.

I guess I could also tell you what exactly I'm trying to achieve, in case there is another method I haven't stumbled upon;

I'm adding autocompleted value fields based on a entered keyword. So, if the user types i.e: "$keyword" a snippet that holds the appropriate value tags is inserted after it, with the caret inbetween the tags for instant typing of the value.

 

i.e: $keyword - > "caret_is_here"

or

$colorkeyword -> "[ .5 .5 .5 ]" (contents of [ ] is selected)

 

 

I hope any of this helps, for both our sakes!

 

Thanks,

Oyvind

Jan 20, 2015 at 10:59 PM
I ran into the same problem.
The solution I found was in hindsight obvious.
When you take over the handling of autocompletion you have to inform the other handlers that you have taken action.

if (you have inserted a snippet)
{
e.Cancel = true;
}

return;

Don't forget to set the caret back to the begin of word before inserting the snippet.