private void Scintilla_BeforeTextDelete(object sender, TextModifiedEventArgs e) { if (!_isEnabled) { return; } if (_snippetLinks.IsActive && !_pendingUndo && !(e.UndoRedoFlags.IsUndo || e.UndoRedoFlags.IsRedo)) { _pendingUndo = true; Scintilla.UndoRedo.BeginUndoAction(); _snippetLinkTimer.Enabled = true; } ManagedRange undoneSnippetLinkRange = null; if (e.UndoRedoFlags.IsUndo && _snippetLinks.IsActive) { foreach (ManagedRange mr in Scintilla.ManagedRanges) { if (mr.Start == e.Position && mr.Length == e.Length && mr.Length > 1) { undoneSnippetLinkRange = mr; // Expanding the range So that it won't get marked for deletion mr.End++; } } } // It's possible that the end point may have been deleted. The endpoint // is an ultra persistent marker that cannot be deleted until the Snippet // Link mode is deactivated. Place a new EndPoint at the begining of the // deleted range. if (_snippetLinks.IsActive && _snippetLinks.EndPoint != null && _snippetLinks.EndPoint.Scintilla == null) { SnippetLinkEnd eci = new SnippetLinkEnd(e.Position, Scintilla); Scintilla.ManagedRanges.Add(eci); _snippetLinks.EndPoint = eci; } // Now collapse the Undone range in preparation for the // newly inserted text that will be put in here if (undoneSnippetLinkRange != null) { undoneSnippetLinkRange.End = undoneSnippetLinkRange.Start; } // Check to see if all SnippetLink ranges have been deleted. // If this is the case we need to turn Deactivate SnippetLink // mode. bool deactivate = true; foreach (SnippetLink sl in _snippetLinks.Values) { if (sl.Ranges.Count > 0) { foreach (SnippetLinkRange slr in sl.Ranges) { if (slr.Scintilla != null) { deactivate = false; break; } } } if (!deactivate) { break; } } if (deactivate && IsActive) { IsActive = false; } }
private void Scintilla_BeforeTextDelete(object sender, TextModifiedEventArgs e) { if (!_isEnabled) return; if (_snippetLinks.IsActive && !_pendingUndo && !(e.UndoRedoFlags.IsUndo || e.UndoRedoFlags.IsRedo)) { _pendingUndo = true; Scintilla.UndoRedo.BeginUndoAction(); _snippetLinkTimer.Enabled = true; } ManagedRange undoneSnippetLinkRange = null; if (e.UndoRedoFlags.IsUndo && _snippetLinks.IsActive) { foreach (ManagedRange mr in Scintilla.ManagedRanges) { if (mr.Start == e.Position && mr.Length == e.Length && mr.Length > 1) { undoneSnippetLinkRange = mr; // Expanding the range So that it won't get marked for deletion mr.End++; } } } // It's possible that the end point may have been deleted. The endpoint // is an ultra persistent marker that cannot be deleted until the Snippet // Link mode is deactivated. Place a new EndPoint at the begining of the // deleted range. if (_snippetLinks.IsActive && _snippetLinks.EndPoint != null && _snippetLinks.EndPoint.Scintilla == null) { SnippetLinkEnd eci = new SnippetLinkEnd(e.Position, Scintilla); Scintilla.ManagedRanges.Add(eci); _snippetLinks.EndPoint = eci; } // Now collapse the Undone range in preparation for the // newly inserted text that will be put in here if (undoneSnippetLinkRange != null) undoneSnippetLinkRange.End = undoneSnippetLinkRange.Start; // Check to see if all SnippetLink ranges have been deleted. // If this is the case we need to turn Deactivate SnippetLink // mode. bool deactivate = true; foreach (SnippetLink sl in _snippetLinks.Values) { if (sl.Ranges.Count > 0) { foreach (SnippetLinkRange slr in sl.Ranges) { if (slr.Scintilla != null) { deactivate = false; break; } } } if (!deactivate) break; } if (deactivate && IsActive) IsActive = false; }
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(); }
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(); }