internal void InsertSnippet(Snippet snip, int startPos) { NativeScintilla.BeginUndoAction(); IsActive = false; string snippet = snip.RealCode; // First properly indent the template. We do this by // getting the indent string of the current line and // adding it to all newlines int indentPoint = 0; string line = Scintilla.Lines.Current.Text; if (line != string.Empty) { while (indentPoint < line.Length) { char c = line[indentPoint]; if (c != ' ' && c != '\t') { break; } indentPoint++; } } // Grab the current selected text in case we have a surrounds with scenario. string selText = Scintilla.Selection.Text; // Now we clear the selection if (selText != string.Empty) { Scintilla.Selection.Clear(); } if (indentPoint > 0) { string indent = line.Substring(0, indentPoint); // This is a bit of a tough decision, but I think the best way to handle it // is to assume that the Snippet's Eol Marker matches the Platform DOCUMENT_DEFAULT // but the target Eol Marker should match the Document's. snippet = snippet.Replace(Environment.NewLine, Scintilla.EndOfLine.EolString + indent); // Same deal with the selected text if any selText = selText.Replace(Environment.NewLine, Scintilla.EndOfLine.EolString + indent); } int anchorPos = -1; int caretPos = -1; int endPos = -1; int selPos = -1; SortedList <int, int> dropMarkers = new SortedList <int, int>(); SortedList <int, SnippetLinkRange> indexedRangesToActivate = new SortedList <int, SnippetLinkRange>(); List <SnippetLinkRange> unindexedRangesToActivate = new List <SnippetLinkRange>(); Match m = snippetRegex1.Match(snippet); while (m.Success) { // Did it match a $DropMarker$ token? if (m.Groups["dm"].Success) { // Yep, was it an indexed or unindexed DropMarker if (m.Groups["dmi"].Success) { // Indexed, set the indexed drop marker's character offset // if it is specified more than once the last one wins. dropMarkers[int.Parse(m.Groups["dmi"].Value)] = m.Groups["dm"].Index; } else { // Unindexed, just tack it on at the _end dropMarkers[dropMarkers.Count] = m.Groups["dm"].Index; } // Take the token out of the string snippet = snippet.Remove(m.Groups["dm"].Index, m.Groups["dm"].Length); } else if (m.Groups["c"].Success) { // We matched the $Caret$ Token. Since there can be // only 1 we set the caretPos. If this is specified // more than once the last one wins caretPos = m.Groups["c"].Index; // Take the token out of the string snippet = snippet.Remove(m.Groups["c"].Index, m.Groups["c"].Length); } else if (m.Groups["a"].Success) { // We matched the $Anchor$ Token. Since there can be // only 1 we set the anchorPos. If this is specified // more than once the last one wins anchorPos = m.Groups["a"].Index; // Take the token out of the string snippet = snippet.Remove(m.Groups["a"].Index, m.Groups["a"].Length); } else if (m.Groups["e"].Success) { // We matched the $End$ Token. Since there can be // only 1 we set the endPos. If this is specified // more than once the last one wins endPos = m.Groups["e"].Index; // Take the token out of the string snippet = snippet.Remove(m.Groups["e"].Index, m.Groups["e"].Length); } else if (m.Groups["s"].Success) { // We matched the $Selection$ Token. Simply insert the // selected text at this position selPos = m.Groups["s"].Index; // Take the token out of the string snippet = snippet.Remove(m.Groups["s"].Index, m.Groups["s"].Length); snippet = snippet.Insert(m.Groups["s"].Index, selText); } else if (m.Groups["l"].Success) { // Finally match for Snippet Link Ranges. This is at the bottom of the if/else // because we want the more specific regex groups to match first so that this // generic expression group doesn't create a SnippetLinkRange for say the // $Caret$ Token. Group g = m.Groups["l"]; int rangeIndex; string groupKey; if (m.Groups["li"].Success) { // We have a subindexed SnippetLinkRange along the lines of $sometoken[1]$ Group sg = m.Groups["li"]; // At this point g.Value = $sometoken[1]$ // and sg.Value = [1]. // We want the range's Key, which would be sometoken groupKey = g.Value.Substring(1, g.Value.Length - sg.Length - 2); // Now we need the range's Index which would be 1 in our fictitional case rangeIndex = int.Parse(sg.Value.Substring(1, sg.Value.Length - 2)); // Now we need to determine the actual _start and _end positions of the range. // Keep in mind we'll be stripping out the 2 $ and the subindex string (eg [1]) int start = startPos + g.Index; int end = start + g.Length - sg.Length - 2; // And now we add (or replace) the snippet link range at the index // keep in mind duplicates will stomp all over each other, the last // one wins. Replaced tokens won't get a range indexedRangesToActivate[rangeIndex] = new SnippetLinkRange(start, end, Scintilla, groupKey);; // And remove all the token info including the subindex from the snippet text // leaving only the key snippet = snippet.Remove(g.Index, 1).Remove(g.Index - 2 + g.Length - sg.Length, sg.Length + 1); } else { // We have a regular old SnippetLinkRange along the lines of $sometoken$ // We want the range's Key, which would be sometoken groupKey = g.Value.Substring(1, g.Value.Length - 2); // Now we need to determine the actual _start and _end positions of the range. // Keep in mind we'll be stripping out the 2 $ int start = startPos + g.Index; int end = start + g.Length - 2; // Now create the range object unindexedRangesToActivate.Add(new SnippetLinkRange(start, end, Scintilla, groupKey)); // And remove all the token info from the snippet text // leaving only the key snippet = snippet.Remove(g.Index, 1).Remove(g.Index + g.Length - 2, 1); } } // Any more matches? Note that I'm rerunning the regexp query // on the snippet string becuase it's contents have been modified // and we need to get the updated index values. m = snippetRegex1.Match(snippet); } // Replace the snippet Keyword with the snippet text. Or if this // isn't triggered by a shortcut, it will insert at the current // Caret Position Scintilla.GetRange(startPos, NativeScintilla.GetCurrentPos()).Text = snippet; // Now that we have the text set we can activate our link ranges // we couldn't do it before becuase they were managed ranges and // would get offset by the text change // Since we are done adding new SnippetLinkRanges we can tack // on the unindexed ranges to the _end of the indexed ranges SnippetLinkRange[] allLinks = new SnippetLinkRange[indexedRangesToActivate.Count + unindexedRangesToActivate.Count]; for (int i = 0; i < indexedRangesToActivate.Values.Count; i++) { allLinks[i] = indexedRangesToActivate[i]; } for (int i = 0; i < unindexedRangesToActivate.Count; i++) { allLinks[i + indexedRangesToActivate.Count] = unindexedRangesToActivate[i]; } foreach (SnippetLinkRange slr in allLinks) { addSnippetLink(slr); } foreach (SnippetLinkRange slr in allLinks) { slr.Init(); } // Now we need to activate the Snippet links. However we have a bit // of a styling confilct. If we set the indicator styles before the // SQL Lexer styles the newly added text it won't get styled. So to // make sure we set the Indicator Styles after we put the call on // a timer. if (_snippetLinks.Count > 0) { Timer t = new Timer(); t.Interval = 10; // Oh how I love anonymous delegates, this is starting to remind // me of JavaScript and SetTimeout... t.Tick += new EventHandler(delegate(object sender, EventArgs te) { t.Dispose(); IsActive = true; }); t.Start(); } // Add all the Drop markers in the indexed order. The // order is reversed of course because drop markers work // in a FILO manner for (int i = dropMarkers.Count - 1; i >= 0; i--) { Scintilla.DropMarkers.Drop(startPos + dropMarkers.Values[i]); } // Place the caret at either the position of the token or // at the _end of the snippet text. if (caretPos >= 0) { Scintilla.Caret.Goto(startPos + caretPos); } else { Scintilla.Caret.Goto(startPos + snippet.Length); } // Ahoy, way anchor! if (anchorPos >= 0) { Scintilla.Caret.Anchor = startPos + anchorPos; } // Do we have an _end cursor? if (endPos >= 0) { // If they have snippet link ranges activated in this snippet // go ahead and set up an EndPoint marker if (allLinks.Length > 0) { SnippetLinkEnd eci = new SnippetLinkEnd(endPos + startPos, Scintilla); Scintilla.ManagedRanges.Add(eci); _snippetLinks.EndPoint = eci; } else { // Otherwise we treat it like an Anchor command because // the SnippetLink mode isn't activated Scintilla.Caret.Goto(endPos + startPos); } } NativeScintilla.EndUndoAction(); }
public void InsertSnippet(Snippet snip) { InsertSnippet(snip, Math.Min(NativeScintilla.GetCurrentPos(), NativeScintilla.GetAnchor())); }