Click here to Skip to main content
15,861,168 members
Articles / Programming Languages / C#

CodeDom Assistant

Rate me:
Please Sign up or sign in to vote.
4.84/5 (26 votes)
21 Sep 20074 min read 136.5K   6.6K   82  
Generating CodeDom Code By Parsing C# or VB
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.ComponentModel;


namespace ScintillaNet
{
	[TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))]
	public class SnippetManager : ScintillaHelperBase
	{
		
		#region Private Member variables
		private SnippetChooser _snipperChooser;
		private Timer _snippetLinkTimer = new Timer();
		private bool _pendingUndo = false;
		private readonly Regex snippetRegex1 = new Regex(@"(?<dm>\�DropMarker(?<dmi>\[[0-9]*\])?\�)|(?<c>\�caret\�)|(?<a>\�anchor\�)|(?<e>\�end\�)|(?<s>\�selected\�)|(?<l>\�.+?(?<li>\[[0-9]*\])?\�)", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
		private SnippetLinkCollection _snippetLinks = new SnippetLinkCollection();

		#endregion

		public SnippetManager(Scintilla scintilla)
			: base(scintilla)
		{
			_list = new SnippetList(this);

			_snippetLinkTimer.Interval = 1;
			_snippetLinkTimer.Tick += new EventHandler(_snippetLinkTimer_Tick);

			IsEnabled = _isEnabled;
		}

		internal void SetIndicators()
		{
			Scintilla.Indicators[_activeSnippetIndicator].Style = _activeSnippetIndicatorStyle;
			Scintilla.Indicators[_activeSnippetIndicator].Color = _activeSnippetColor;

			Scintilla.Indicators[_inactiveSnippetIndicator].Style = _inactiveSnippetIndicatorStyle;
			Scintilla.Indicators[_inactiveSnippetIndicator].Color = _inactiveSnippetColor;
		}

		internal bool ShouldSerialize()
		{
			return ShouldSerializeActiveSnippetColor() ||
				ShouldSerializeActiveSnippetIndicator() ||
				ShouldSerializeActiveSnippetIndicatorStyle() ||
				ShouldSerializeInactiveSnippetColor() ||
				ShouldSerializeInactiveSnippetIndicator() ||
				ShouldSerializeInactiveSnippetIndicatorStyle()||
				ShouldSerializeIsOneKeySelectionEmbedEnabled() ||
				ShouldSerializeIsOneKeySelectionEmbedEnabled() ||
				ShouldSerializeDefaultDelimeter();
		}

		#region Public Methods
		public bool NextSnippetRange()
		{
			//	This would be a whole lot easier if I had the Command Contexts set
			//	up. The way it's working now is that this command will always execute
			//	irregardlessly of if the SnippetLinks are active. Since we may not have
			//	a valid context to execute we don't necessarily want to eat the
			//	keystroke in all circumstances, hence the bool return
			if (!_snippetLinks.IsActive || Scintilla.AutoComplete.IsActive)
				return false;

			//	OK So we want to find the next SnippetLink in
			//	whatever order they are in and then select it
			//	so that they can fill it out.
			SnippetLink sl = _snippetLinks.NextActiveSnippetLink;
			if (sl != null)
			{
				//	However it is possible that all of this Snippet Links'
				//	ranges have been deleted by the user. If this is the case
				//	we need to remove this snippet link from the list and go
				//	to the next link.

				while (sl.Ranges.Count == 0)
				{
					_snippetLinks.Remove(sl);
					sl = _snippetLinks.NextActiveSnippetLink;

					//	No more snippet links? Nothing to do but quit
					if (sl == null)
					{
						Scintilla.Commands.StopProcessingCommands = true;
						return true;
					}
				}

				//	Yay we have it. Select the first Range in the Snippet Link's Series
				sl.Ranges[0].Select();
				Scintilla.Commands.StopProcessingCommands = true;
				return true;
			}

			return false;
		}

		public bool PreviousSnippetRange()
		{
			//	Same as NextSnippetRange but going in the opposite direction
			if (!_snippetLinks.IsActive || Scintilla.AutoComplete.IsActive)
				return false;

			SnippetLink sl = _snippetLinks.PreviousActiveSnippetLink;
			if (sl != null)
			{
				while (sl.Ranges.Count == 0)
				{
					_snippetLinks.Remove(sl);
					sl = _snippetLinks.PreviousActiveSnippetLink;
					if (sl == null)
					{
						Scintilla.Commands.StopProcessingCommands = true;
						return true;
					}
				}

				sl.Ranges[0].Select();
				Scintilla.Commands.StopProcessingCommands = true;
				return true;
			}

			return false;
		}

		public bool CancelActiveSnippets()
		{
			if (_snippetLinks.IsActive && !Scintilla.AutoComplete.IsActive)
			{
				IsActive = false;
				Scintilla.Commands.StopProcessingCommands = true;
				return true;
			}

			return false;
		}

		public bool AcceptActiveSnippets()
		{
			if (_snippetLinks.IsActive && !Scintilla.AutoComplete.IsActive)
			{
				int pos = Scintilla.Caret.Position;
				bool end = false;
				foreach (SnippetLink sl in _snippetLinks.Values)
				{
					foreach (SnippetLinkRange sr in sl.Ranges)
					{
						if (sr.PositionInRange(pos))
						{
							end = true;
							break;
						}
					}
					if (end)
						break;
				}

				if (end)
				{
					cascadeSnippetLinkRangeChange(_snippetLinks.ActiveSnippetLink, _snippetLinks.ActiveRange);

					if (_snippetLinks.EndPoint != null)
						Scintilla.Caret.Goto(_snippetLinks.EndPoint.Start);

					IsActive = false;
					Scintilla.Commands.StopProcessingCommands = true;
					return true;
				}
			}

			return false;
		}

		public bool DoSnippetCheck()
		{
			if (!_isEnabled || _snippetLinks.IsActive || Scintilla.AutoComplete.IsActive || Scintilla.Selection.Length > 0)
				return false;

			int pos = Scintilla.NativeInterface.GetCurrentPos();

			//	In order to even start searching for a snippet keyword the
			//	current position needs to meet these conditions:
			//	Can't be at the very beginning of the document. Why? becuase
			//	then obviously there can't be a preceding keyword then can it?
			//	The preceding character can't be whitespace (same reason) 
			//
			//	I decided I like expanding a template in the middle of a word
			if (pos <= 0 || Scintilla.NativeInterface.GetCharAt(pos - 1).ToString().IndexOfAny(Scintilla.Lexing.WhiteSpaceCharsArr) >= 0)
				return false;

			//	We also don't want a template expand if we're in a Comment or 
			//	String. Be sure and mask out any indicator style that may be applied
			int currentStyle = Scintilla.NativeInterface.GetStyleAt(pos - 1) & 0x1f;
			if (currentStyle == 1 || currentStyle == 2 || currentStyle == 4)
				return false;

			//	Let Scintilla figure out where the beginning of
			//	the word to the left is.
			int newPos = Scintilla.NativeInterface.WordStartPosition(pos, true);

			Range snipRange = Scintilla.GetRange(newPos, pos);
			string keyworkCandidate = snipRange.Text;

			Snippet snip;
			if (!_list.TryGetValue(keyworkCandidate, out snip))
			{
				//	Not a match. Buh-bye
				return false;
			}

			InsertSnippet(snip, newPos);
			Scintilla.Commands.StopProcessingCommands = true;
			return true;
		}

		public void InsertSnippet(string shortcut)
		{
			Snippet snip;
			if (!_list.TryGetValue(shortcut, out snip))
			{
				//	Not a match. Buh-bye
				return;
			}

			InsertSnippet(snip, Math.Min(NativeScintilla.GetCurrentPos(), NativeScintilla.GetAnchor()));
		}

		public void InsertSnippet(Snippet snip)
		{
			InsertSnippet(snip, Math.Min(NativeScintilla.GetCurrentPos(), NativeScintilla.GetAnchor()));
		}

		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 (true)
				{
					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();
		}

		#endregion

		#region Event Handlers
		private void _snippetLinkTimer_Tick(object sender, EventArgs e)
		{
			_snippetLinkTimer.Enabled = false;
			Range sr = Scintilla.Selection.Range;

			if (_snippetLinks.IsActive)
			{
				SnippetLink oldActiveSnippetLink = _snippetLinks.ActiveSnippetLink;
				SnippetLinkRange oldActiveRange = _snippetLinks.ActiveRange;

				if (oldActiveRange != null && (oldActiveRange.IntersectsWith(sr) || oldActiveRange.Equals(sr)))
				{
					Scintilla.BeginInvoke(new MethodInvoker(delegate()
					{
						cascadeSnippetLinkRangeChange(oldActiveSnippetLink, oldActiveRange);

						foreach (SnippetLink sl in _snippetLinks.Values)
							foreach (Range r in sl.Ranges)
							{
								if (sl == _snippetLinks.ActiveSnippetLink)
								{
									r.ClearIndicator(Scintilla.Snippets.InactiveSnippetIndicator);
									r.SetIndicator(Scintilla.Snippets.ActiveSnippetIndicator);
								}
								else
								{
									r.SetIndicator(Scintilla.Snippets.InactiveSnippetIndicator);
									r.ClearIndicator(Scintilla.Snippets.ActiveSnippetIndicator);
								}

							}

						if (_pendingUndo)
						{
							_pendingUndo = false;
							Scintilla.UndoRedo.EndUndoAction();
						}

						Scintilla.NativeInterface.Colourise(0, -1);
					}));
				}
			}
		}

		private void Scintilla_TextInserted(object sender, TextModifiedEventArgs e)
		{
			//	I'm going to have to look into making this a little less "sledge hammer to
			//	the entire documnet"ish
			if (_snippetLinks.IsActive && (e.UndoRedoFlags.IsUndo || e.UndoRedoFlags.IsRedo))
				Scintilla.NativeInterface.Colourise(0, -1);
		}

		private void Scintilla_BeforeTextInsert(object sender, TextModifiedEventArgs e)
		{
			if (_snippetLinks.IsActive && !_pendingUndo && !(e.UndoRedoFlags.IsUndo || e.UndoRedoFlags.IsRedo))
			{
				_pendingUndo = true;
				Scintilla.UndoRedo.BeginUndoAction();
				_snippetLinkTimer.Enabled = true;
			}
		}

		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_SelectionChanged(object sender, EventArgs e)
		{
			Range sr = Scintilla.Selection.Range;

			if (_snippetLinks.IsActive)
			{
				SnippetLink oldActiveSnippetLink = _snippetLinks.ActiveSnippetLink;
				SnippetLinkRange oldActiveRange = _snippetLinks.ActiveRange;

				_snippetLinks.ActiveSnippetLink = null;
				_snippetLinks.ActiveRange = null;

				for (int i = 0; i < _snippetLinks.Count; i++)
				{

					SnippetLink sl = _snippetLinks[i];

					foreach (SnippetLinkRange r in sl.Ranges)
					{
						if (r.IntersectsWith(sr))
						{
							_snippetLinks.ActiveSnippetLink = sl;
							_snippetLinks.ActiveRange = r;
							break;
						}
					}
					if (_snippetLinks.ActiveRange != null)
						break;
				}

				foreach (SnippetLink sl in _snippetLinks.Values)
					foreach (Range r in sl.Ranges)
					{
						if (sl == _snippetLinks.ActiveSnippetLink)
						{
							r.ClearIndicator(Scintilla.Snippets.InactiveSnippetIndicator);
							r.SetIndicator(Scintilla.Snippets.ActiveSnippetIndicator);
						}
						else
						{
							r.SetIndicator(Scintilla.Snippets.InactiveSnippetIndicator);
							r.ClearIndicator(Scintilla.Snippets.ActiveSnippetIndicator);
						}
					}
			}
		}

		#endregion

		#region IsOneKeySelectionEmbedEnabled
		//	Yeah I know this is a bit unwieldly but I can't come up with a better name
		private bool _isOneKeySelectionEmbedEnabled = false;

		public bool IsOneKeySelectionEmbedEnabled
		{
			get
			{
				return _isOneKeySelectionEmbedEnabled;
			}
			set
			{
				_isOneKeySelectionEmbedEnabled = value;
			}
		}
		private bool ShouldSerializeIsOneKeySelectionEmbedEnabled()
		{
			return _isOneKeySelectionEmbedEnabled;
		}

		private void ResetIsOneKeySelectionEmbedEnabled()
		{
			_isOneKeySelectionEmbedEnabled = false;
		} 
		#endregion

		#region Private Methods
		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);

				SnippetLinkRange 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);
		}

		private SnippetLinkRange addSnippetLink(SnippetLinkRange range)
		{
			string key = range.Key;
			SnippetLink sl = null;
			for (int i = 0; i < _snippetLinks.Count; i++)
			{
				if (_snippetLinks[i].Key.Equals(key, StringComparison.CurrentCultureIgnoreCase))
				{
					sl = _snippetLinks[i];
					break;
				}
			}
			if (sl == null)
			{
				sl = new SnippetLink(key);
				_snippetLinks.Add(sl);
			}

			sl.Ranges.Add(range);
			range.Parent = sl.Ranges;

			return range;
		}


		#endregion

		#region Public Properties

		#region Active
		[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();
				}
				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;
					}
				}
			}
		} 
		#endregion

		#region ActiveSnippetColor
		private Color _activeSnippetColor = Color.Lime;
		public Color ActiveSnippetColor
		{
			get
			{
				return _activeSnippetColor;
			}
			set
			{
				_activeSnippetColor = value;
			}
		}

		private bool ShouldSerializeActiveSnippetColor()
		{
			return _activeSnippetColor != Color.Lime;
		}

		private void ResetActiveSnippetColor()
		{
			_activeSnippetColor = Color.Lime;
		}
		#endregion

		#region ActiveSnippetIndicator
		private int _activeSnippetIndicator = 15;
		public int ActiveSnippetIndicator
		{
			get
			{
				return _activeSnippetIndicator;
			}
			set
			{
				_activeSnippetIndicator = value;
			}
		}

		private bool ShouldSerializeActiveSnippetIndicator()
		{
			return _activeSnippetIndicator != 15;
		}

		private void ResetActiveSnippetIndicator()
		{
			_activeSnippetIndicator = 15;
		} 
		#endregion

		#region ActiveSnippetIndicatorStyle
		private IndicatorStyle _activeSnippetIndicatorStyle = IndicatorStyle.RoundBox;
		public IndicatorStyle ActiveSnippetIndicatorStyle
		{
			get
			{
				return _activeSnippetIndicatorStyle;
			}
			set
			{
				_activeSnippetIndicatorStyle = value;
			}
		}

		private bool ShouldSerializeActiveSnippetIndicatorStyle()
		{
			return _activeSnippetIndicatorStyle != IndicatorStyle.RoundBox;
		}

		private void ResetActiveSnippetIndicatorStyle()
		{
			_activeSnippetIndicatorStyle = IndicatorStyle.RoundBox;
		} 
		#endregion

		#region DefaultDelimeter
		private char _defaultDelimeter = '$';
		public char DefaultDelimeter
		{
			get
			{
				return _defaultDelimeter;
			}
			set
			{
				_defaultDelimeter = value;
			}
		}

		private bool ShouldSerializeDefaultDelimeter()
		{
			return _defaultDelimeter != '$';
		}

		private void ResetDefaultDelimeter()
		{
			_defaultDelimeter = '$';
		} 
		#endregion

		#region IsEnabled
		private bool _isEnabled = true; 
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		public bool IsEnabled
		{
			get
			{
				return _isEnabled;
			}
			set
			{
				_isEnabled = value;

				if (value)
				{
					Scintilla.TextInserted += new EventHandler<TextModifiedEventArgs>(Scintilla_TextInserted);
					Scintilla.BeforeTextInsert += new EventHandler<TextModifiedEventArgs>(Scintilla_BeforeTextInsert);
					Scintilla.BeforeTextDelete += new EventHandler<TextModifiedEventArgs>(Scintilla_BeforeTextDelete);
					Scintilla.SelectionChanged += new EventHandler(Scintilla_SelectionChanged);
				}
				else
				{
					Scintilla.TextInserted -= new EventHandler<TextModifiedEventArgs>(Scintilla_TextInserted);
					Scintilla.BeforeTextInsert -= new EventHandler<TextModifiedEventArgs>(Scintilla_BeforeTextInsert);
					Scintilla.BeforeTextDelete -= new EventHandler<TextModifiedEventArgs>(Scintilla_BeforeTextDelete);
					Scintilla.SelectionChanged -= new EventHandler(Scintilla_SelectionChanged);
				}
			}
		}

		private bool ShouldSerializeIsEnabled()
		{
			return !_isEnabled;
		}

		private void ResetIsEnabled()
		{
			_isEnabled = true;
		}
		#endregion

		#region InactiveSnippetColor
		private Color _inactiveSnippetColor = Color.Lime;
		public Color InactiveSnippetColor
		{
			get
			{
				return _inactiveSnippetColor;
			}
			set
			{
				_inactiveSnippetColor = value;
			}
		}

		private bool ShouldSerializeInactiveSnippetColor()
		{
			return _inactiveSnippetColor != Color.Lime;
		}

		private void ResetInactiveSnippetColor()
		{
			_inactiveSnippetColor = Color.Lime;
		} 
		#endregion

		#region InactiveSnippetIndicator
		private int _inactiveSnippetIndicator = 16;
		public int InactiveSnippetIndicator
		{
			get
			{
				return _inactiveSnippetIndicator;
			}
			set
			{
				_inactiveSnippetIndicator = value;
			}
		}

		private bool ShouldSerializeInactiveSnippetIndicator()
		{
			return _inactiveSnippetIndicator != 16;
		}

		private void ResetInactiveSnippetIndicator()
		{
			_inactiveSnippetIndicator = 16;
		}
		#endregion

		#region InactiveSnippetIndicatorStyle
		private IndicatorStyle _inactiveSnippetIndicatorStyle = IndicatorStyle.Box;
		public IndicatorStyle InactiveSnippetIndicatorStyle
		{
			get
			{
				return _inactiveSnippetIndicatorStyle;
			}
			set
			{
				_inactiveSnippetIndicatorStyle = value;
			}
		}

		private bool ShouldSerializeInactiveSnippetIndicatorStyle()
		{
			return _inactiveSnippetIndicatorStyle != IndicatorStyle.Box;
		}

		private void ResetInactiveSnippetIndicatorStyle()
		{
			_inactiveSnippetIndicatorStyle = IndicatorStyle.Box;
		}

		#endregion

		#region List
		private SnippetList _list;
		[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public SnippetList List
		{
			get
			{
				return _list;
			}
			set
			{
				_list = value;
			}
		} 
		#endregion

		#endregion

		public void ShowSnippetList()
		{
			if (_list.Count == 0)
				return;

			if (_snipperChooser == null)
			{
				_snipperChooser = new SnippetChooser();
				_snipperChooser.Scintilla = Scintilla;
				_snipperChooser.SnippetList = _list.ToString();
				_snipperChooser.Scintilla.Controls.Add(_snipperChooser);
			}
			_snipperChooser.SnippetList = _list.ToString();
			_snipperChooser.Show();
		}

		public void ShowSurroundWithList()
		{
			SnippetList sl = new SnippetList(null);
			foreach (Snippet s in _list)
			{
				if (s.IsSurroundsWith)
					sl.Add(s);
			}

			if (sl.Count == 0)
				return;

			if (_snipperChooser == null)
			{
				_snipperChooser = new SnippetChooser();
				_snipperChooser.Scintilla = Scintilla;
				_snipperChooser.SnippetList = _list.ToString();
				_snipperChooser.Scintilla.Controls.Add(_snipperChooser);
			}
			_snipperChooser.SnippetList = sl.ToString();
			_snipperChooser.Show();
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions