private SnippetLinkRange addSnippetLink(SnippetLinkRange range) { string key = range.Key; SnippetLink sl = null; for (int i = 0; i < this._snippetLinks.Count; i++) { if (this._snippetLinks[i].Key.Equals(key, StringComparison.CurrentCultureIgnoreCase)) { sl = this._snippetLinks[i]; break; } } if (sl == null) { sl = new SnippetLink(key); this._snippetLinks.Add(sl); } sl.Ranges.Add(range); range.Parent = sl.Ranges; return range; }
private void cascadeSnippetLinkRangeChange(SnippetLink oldActiveSnippetLink, SnippetLinkRange oldActiveRange) { Scintilla.ManagedRanges.Sort(); int offset = 0; string newText = oldActiveRange.Text; Scintilla.NativeInterface.SetModEventMask(0); foreach (ManagedRange mr in Scintilla.ManagedRanges) { if (offset != 0) mr.Change(mr.Start + offset, mr.End + offset); var slr = mr as SnippetLinkRange; if (slr == null || !oldActiveSnippetLink.Ranges.Contains(slr) || slr.Text == newText) continue; int oldLength = slr.Length; slr.Text = newText; slr.End += newText.Length - oldLength; offset += newText.Length - oldLength; } Scintilla.NativeInterface.SetModEventMask(Constants.SC_MODEVENTMASKALL); }
internal void InsertSnippet(Snippet snip, int startPos) { NativeScintilla.BeginUndoAction(); this.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; var dropMarkers = new SortedList<int, int>(); var indexedRangesToActivate = new SortedList<int, SnippetLinkRange>(); var unindexedRangesToActivate = new List<SnippetLinkRange>(); Match m = this.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 findRegex 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 = this.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 var 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) this.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 (this._snippetLinks.Count > 0) { var t = new Timer { Interval = 10 }; // Oh how I love anonymous delegates, this is starting to remind // me of JavaScript and SetTimeout... t.Tick += delegate { t.Dispose(); this.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) { var eci = new SnippetLinkEnd(endPos + startPos, Scintilla); Scintilla.ManagedRanges.Add(eci); this._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(); }