PageRenderTime 94ms CodeModel.GetById 52ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/Gui/T4EditorExtension.cs

https://github.com/jfcantin/monodevelop
C# | 290 lines | 225 code | 39 blank | 26 comment | 52 complexity | 53522cb0e44cb8070f6e5f24747239ef MD5 | raw file
  1// 
  2// T4EditorExtension.cs
  3//  
  4// Author:
  5//       Michael Hutchinson <mhutchinson@novell.com>
  6// 
  7// Copyright (c) 2009 Novell, Inc. (http://www.novell.com)
  8// 
  9// Permission is hereby granted, free of charge, to any person obtaining a copy
 10// of this software and associated documentation files (the "Software"), to deal
 11// in the Software without restriction, including without limitation the rights
 12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 13// copies of the Software, and to permit persons to whom the Software is
 14// furnished to do so, subject to the following conditions:
 15// 
 16// The above copyright notice and this permission notice shall be included in
 17// all copies or substantial portions of the Software.
 18// 
 19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 25// THE SOFTWARE.
 26
 27using System;
 28using System.Collections.Generic;
 29using MonoDevelop.Ide.CodeCompletion;
 30using MonoDevelop.Projects.Dom;
 31using MonoDevelop.Ide.Gui.Content;
 32using MonoDevelop.DesignerSupport;
 33using MonoDevelop.TextTemplating.Parser;
 34using MonoDevelop.Ide;
 35
 36namespace MonoDevelop.TextTemplating.Gui
 37{
 38	public class T4EditorExtension : CompletionTextEditorExtension, IOutlinedDocument
 39	{
 40		bool disposed;
 41		T4ParsedDocument parsedDoc;
 42		
 43		public T4EditorExtension ()
 44		{
 45		}
 46		
 47		public override void Initialize ()
 48		{
 49			base.Initialize ();
 50			MonoDevelop.Projects.Dom.Parser.ProjectDomService.ParsedDocumentUpdated += OnParseInformationChanged;
 51			parsedDoc = (T4ParsedDocument)Document.ParsedDocument;
 52			if (parsedDoc != null) {
 53				RefreshOutline ();
 54			}
 55		}
 56		
 57		public override void Dispose ()
 58		{
 59			if (disposed)
 60				return;
 61			disposed = true;
 62			MonoDevelop.Projects.Dom.Parser.ProjectDomService.ParsedDocumentUpdated
 63				-= OnParseInformationChanged;
 64			base.Dispose ();
 65		}
 66		
 67		void OnParseInformationChanged (object sender, MonoDevelop.Projects.Dom.ParsedDocumentEventArgs args)
 68		{
 69			if (FileName == args.FileName && args.ParsedDocument != null) {
 70				parsedDoc = (T4ParsedDocument)args.ParsedDocument;
 71				RefreshOutline ();
 72			}
 73		}
 74		
 75		#region Convenience accessors, from BaseXmlEditorExtension
 76		
 77		protected T4ParsedDocument ParsedDoc {
 78			get { return parsedDoc; }
 79		}
 80		
 81		protected ITextBuffer Buffer {
 82			get {
 83				if (Document == null)
 84					throw new InvalidOperationException ("Editor extension not yet initialized");
 85				return Document.GetContent<ITextBuffer> ();
 86			}
 87		}
 88		
 89		protected IEditableTextBuffer EditableBuffer {
 90			get {
 91				if (Document == null)
 92					throw new InvalidOperationException ("Editor extension not yet initialized");
 93				return Document.GetContent<IEditableTextBuffer> ();
 94			}
 95		}
 96		
 97		protected string GetBufferText (DomRegion region)
 98		{
 99			MonoDevelop.Ide.Gui.Content.ITextBuffer buf = Buffer;
100			int start = buf.GetPositionFromLineColumn (region.Start.Line, region.Start.Column);
101			int end = buf.GetPositionFromLineColumn (region.End.Line, region.End.Column);
102			if (end > start && start >= 0)
103				return buf.GetText (start, end);
104			else
105				return null;
106		}
107		
108		#endregion
109		
110		#region Code completion
111
112		public override ICompletionDataList CodeCompletionCommand (CodeCompletionContext completionContext)
113		{
114			int pos = completionContext.TriggerOffset;
115			if (pos <= 0)
116				return null;
117			int triggerWordLength = 0;
118			return HandleCodeCompletion ((CodeCompletionContext) completionContext, true, ref triggerWordLength);
119		}
120
121		public override ICompletionDataList HandleCodeCompletion (
122		    CodeCompletionContext completionContext, char completionChar, ref int triggerWordLength)
123		{
124			int pos = completionContext.TriggerOffset;
125			if (pos > 0 && Editor.GetCharAt (pos - 1) == completionChar) {
126				return HandleCodeCompletion ((CodeCompletionContext) completionContext, 
127				                             false, ref triggerWordLength);
128			}
129			return null;
130		}
131
132		protected virtual ICompletionDataList HandleCodeCompletion (
133		    CodeCompletionContext completionContext, bool forced, ref int triggerWordLength)
134		{
135			//IEditableTextBuffer buf = this.EditableBuffer;
136			return null;
137		}
138		
139		#endregion
140		
141		#region Outline
142		
143		bool refreshingOutline = false;
144		MonoDevelop.Ide.Gui.Components.PadTreeView outlineTreeView;
145		Gtk.TreeStore outlineTreeStore;
146		
147		Gtk.Widget IOutlinedDocument.GetOutlineWidget ()
148		{
149			if (outlineTreeView != null)
150				return outlineTreeView;
151			
152			outlineTreeStore = new Gtk.TreeStore (typeof(string), typeof (Gdk.Color), typeof (Mono.TextTemplating.ISegment));
153			outlineTreeView = new MonoDevelop.Ide.Gui.Components.PadTreeView (outlineTreeStore);
154			outlineTreeView.Realized += delegate { RefillOutlineStore (); };
155			
156			outlineTreeView.TextRenderer.Xpad = 0;
157			outlineTreeView.TextRenderer.Ypad = 0;
158			outlineTreeView.AppendColumn ("Node", outlineTreeView.TextRenderer, "text", 0, "foreground-gdk", 1);
159			
160			outlineTreeView.HeadersVisible = false;
161			
162			outlineTreeView.Selection.Changed += delegate {
163				Gtk.TreeIter iter;
164				if (!outlineTreeView.Selection.GetSelected (out iter))
165					return;
166				SelectSegment ((Mono.TextTemplating.ISegment )outlineTreeStore.GetValue (iter, 2));
167			};
168			
169			RefillOutlineStore ();
170			var sw = new MonoDevelop.Components.CompactScrolledWindow ();;
171			sw.Add (outlineTreeView);
172			sw.ShowAll ();
173			return sw;
174		}
175		
176		IEnumerable<Gtk.Widget> IOutlinedDocument.GetToolbarWidgets ()
177		{
178			return null;
179		}
180		
181		void RefreshOutline ()
182		{
183			if (refreshingOutline || outlineTreeView == null )
184				return;
185			refreshingOutline = true;
186			GLib.Timeout.Add (3000, refillOutlineStoreIdleHandler);
187		}
188		
189		bool refillOutlineStoreIdleHandler ()
190		{
191			refreshingOutline = false;
192			RefillOutlineStore ();
193			return false;
194		}
195		
196		void RefillOutlineStore (T4ParsedDocument doc, Gtk.TreeStore store)
197		{
198			if (doc == null)
199				return;
200			
201			Gdk.Color normal   = new Gdk.Color (0x00, 0x00, 0x00);
202			Gdk.Color blue     = new Gdk.Color (0x10, 0x40, 0xE0);
203			Gdk.Color green    = new Gdk.Color (0x08, 0xC0, 0x30);
204			Gdk.Color orange   = new Gdk.Color (0xFF, 0xA0, 0x00);
205			Gdk.Color red      = new Gdk.Color (0xC0, 0x00, 0x20);
206			
207			Gtk.TreeIter parent = Gtk.TreeIter.Zero;
208			foreach (Mono.TextTemplating.ISegment segment in doc.TemplateSegments) {
209				Mono.TextTemplating.Directive dir = segment as Mono.TextTemplating.Directive;
210				if (dir != null) {
211					parent = Gtk.TreeIter.Zero;
212					store.AppendValues ("<#@ " + dir.Name + " #>", red, segment);
213					continue;
214				}
215				Mono.TextTemplating.TemplateSegment ts = segment as Mono.TextTemplating.TemplateSegment;
216				if (ts != null) {
217					string name;
218					if (ts.Text.Length > 40) {
219						name = ts.Text.Substring (0, 40) + "...";
220					} else {
221						name = ts.Text;
222					}
223					name = name.Replace ('\n', ' ').Trim ();
224					if (name.Length == 0)
225						continue;
226					
227					if (ts.Type == Mono.TextTemplating.SegmentType.Expression) {
228						store.AppendValues (parent, "<#= " + name + " #>", orange, segment);
229					} else {
230						if (ts.Type == Mono.TextTemplating.SegmentType.Block) {
231							name = "<#" + name + " #>";
232							store.AppendValues (name, blue, segment);
233							parent = Gtk.TreeIter.Zero;
234						} else if (ts.Type == Mono.TextTemplating.SegmentType.Helper) {
235							name = "<#+" + name + " #>";
236							store.AppendValues (name, green, segment);
237							parent = Gtk.TreeIter.Zero;
238						} else if (ts.Type == Mono.TextTemplating.SegmentType.Content) {
239							parent = store.AppendValues (name, normal, segment);
240						}
241					}
242				}
243			}
244		}
245		
246		void RefillOutlineStore ()
247		{
248			DispatchService.AssertGuiThread ();
249			Gdk.Threads.Enter ();
250			refreshingOutline = false;
251			if (outlineTreeStore == null || !outlineTreeView.IsRealized)
252				return;
253			outlineTreeStore.Clear ();
254			
255			if (ParsedDoc != null) {
256				DateTime start = DateTime.Now;
257				RefillOutlineStore (ParsedDoc, outlineTreeStore);
258				outlineTreeView.ExpandAll ();
259				outlineTreeView.ExpandAll ();
260				MonoDevelop.Core.LoggingService.LogDebug ("Built outline in {0}ms", (DateTime.Now - start).Milliseconds);
261			}
262			
263			Gdk.Threads.Leave ();
264		}
265
266		void IOutlinedDocument.ReleaseOutlineWidget ()
267		{
268			if (outlineTreeView == null)
269				return;
270			
271			Gtk.ScrolledWindow w = (Gtk.ScrolledWindow) outlineTreeView.Parent;
272			w.Destroy ();
273			outlineTreeView.Destroy ();
274			outlineTreeStore.Dispose ();
275			outlineTreeStore = null;
276			outlineTreeView = null;
277		}
278		
279		void SelectSegment (Mono.TextTemplating.ISegment seg)
280		{
281			int s = Editor.Document.LocationToOffset (seg.TagStartLocation.Line, seg.TagStartLocation.Column);
282			if (s > -1) {
283				Editor.Caret.Offset = s;
284				Editor.CenterTo (s);
285			}
286		}
287		
288		#endregion
289	}
290}