/main/src/addins/TextTemplating/MonoDevelop.TextTemplating/Gui/T4EditorExtension.cs
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}