/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. using System;
  27. using System.Collections.Generic;
  28. using MonoDevelop.Ide.CodeCompletion;
  29. using MonoDevelop.Projects.Dom;
  30. using MonoDevelop.Ide.Gui.Content;
  31. using MonoDevelop.DesignerSupport;
  32. using MonoDevelop.TextTemplating.Parser;
  33. using MonoDevelop.Ide;
  34. namespace MonoDevelop.TextTemplating.Gui
  35. {
  36. public class T4EditorExtension : CompletionTextEditorExtension, IOutlinedDocument
  37. {
  38. bool disposed;
  39. T4ParsedDocument parsedDoc;
  40. public T4EditorExtension ()
  41. {
  42. }
  43. public override void Initialize ()
  44. {
  45. base.Initialize ();
  46. MonoDevelop.Projects.Dom.Parser.ProjectDomService.ParsedDocumentUpdated += OnParseInformationChanged;
  47. parsedDoc = (T4ParsedDocument)Document.ParsedDocument;
  48. if (parsedDoc != null) {
  49. RefreshOutline ();
  50. }
  51. }
  52. public override void Dispose ()
  53. {
  54. if (disposed)
  55. return;
  56. disposed = true;
  57. MonoDevelop.Projects.Dom.Parser.ProjectDomService.ParsedDocumentUpdated
  58. -= OnParseInformationChanged;
  59. base.Dispose ();
  60. }
  61. void OnParseInformationChanged (object sender, MonoDevelop.Projects.Dom.ParsedDocumentEventArgs args)
  62. {
  63. if (FileName == args.FileName && args.ParsedDocument != null) {
  64. parsedDoc = (T4ParsedDocument)args.ParsedDocument;
  65. RefreshOutline ();
  66. }
  67. }
  68. #region Convenience accessors, from BaseXmlEditorExtension
  69. protected T4ParsedDocument ParsedDoc {
  70. get { return parsedDoc; }
  71. }
  72. protected ITextBuffer Buffer {
  73. get {
  74. if (Document == null)
  75. throw new InvalidOperationException ("Editor extension not yet initialized");
  76. return Document.GetContent<ITextBuffer> ();
  77. }
  78. }
  79. protected IEditableTextBuffer EditableBuffer {
  80. get {
  81. if (Document == null)
  82. throw new InvalidOperationException ("Editor extension not yet initialized");
  83. return Document.GetContent<IEditableTextBuffer> ();
  84. }
  85. }
  86. protected string GetBufferText (DomRegion region)
  87. {
  88. MonoDevelop.Ide.Gui.Content.ITextBuffer buf = Buffer;
  89. int start = buf.GetPositionFromLineColumn (region.Start.Line, region.Start.Column);
  90. int end = buf.GetPositionFromLineColumn (region.End.Line, region.End.Column);
  91. if (end > start && start >= 0)
  92. return buf.GetText (start, end);
  93. else
  94. return null;
  95. }
  96. #endregion
  97. #region Code completion
  98. public override ICompletionDataList CodeCompletionCommand (CodeCompletionContext completionContext)
  99. {
  100. int pos = completionContext.TriggerOffset;
  101. if (pos <= 0)
  102. return null;
  103. int triggerWordLength = 0;
  104. return HandleCodeCompletion ((CodeCompletionContext) completionContext, true, ref triggerWordLength);
  105. }
  106. public override ICompletionDataList HandleCodeCompletion (
  107. CodeCompletionContext completionContext, char completionChar, ref int triggerWordLength)
  108. {
  109. int pos = completionContext.TriggerOffset;
  110. if (pos > 0 && Editor.GetCharAt (pos - 1) == completionChar) {
  111. return HandleCodeCompletion ((CodeCompletionContext) completionContext,
  112. false, ref triggerWordLength);
  113. }
  114. return null;
  115. }
  116. protected virtual ICompletionDataList HandleCodeCompletion (
  117. CodeCompletionContext completionContext, bool forced, ref int triggerWordLength)
  118. {
  119. //IEditableTextBuffer buf = this.EditableBuffer;
  120. return null;
  121. }
  122. #endregion
  123. #region Outline
  124. bool refreshingOutline = false;
  125. MonoDevelop.Ide.Gui.Components.PadTreeView outlineTreeView;
  126. Gtk.TreeStore outlineTreeStore;
  127. Gtk.Widget IOutlinedDocument.GetOutlineWidget ()
  128. {
  129. if (outlineTreeView != null)
  130. return outlineTreeView;
  131. outlineTreeStore = new Gtk.TreeStore (typeof(string), typeof (Gdk.Color), typeof (Mono.TextTemplating.ISegment));
  132. outlineTreeView = new MonoDevelop.Ide.Gui.Components.PadTreeView (outlineTreeStore);
  133. outlineTreeView.Realized += delegate { RefillOutlineStore (); };
  134. outlineTreeView.TextRenderer.Xpad = 0;
  135. outlineTreeView.TextRenderer.Ypad = 0;
  136. outlineTreeView.AppendColumn ("Node", outlineTreeView.TextRenderer, "text", 0, "foreground-gdk", 1);
  137. outlineTreeView.HeadersVisible = false;
  138. outlineTreeView.Selection.Changed += delegate {
  139. Gtk.TreeIter iter;
  140. if (!outlineTreeView.Selection.GetSelected (out iter))
  141. return;
  142. SelectSegment ((Mono.TextTemplating.ISegment )outlineTreeStore.GetValue (iter, 2));
  143. };
  144. RefillOutlineStore ();
  145. var sw = new MonoDevelop.Components.CompactScrolledWindow ();;
  146. sw.Add (outlineTreeView);
  147. sw.ShowAll ();
  148. return sw;
  149. }
  150. IEnumerable<Gtk.Widget> IOutlinedDocument.GetToolbarWidgets ()
  151. {
  152. return null;
  153. }
  154. void RefreshOutline ()
  155. {
  156. if (refreshingOutline || outlineTreeView == null )
  157. return;
  158. refreshingOutline = true;
  159. GLib.Timeout.Add (3000, refillOutlineStoreIdleHandler);
  160. }
  161. bool refillOutlineStoreIdleHandler ()
  162. {
  163. refreshingOutline = false;
  164. RefillOutlineStore ();
  165. return false;
  166. }
  167. void RefillOutlineStore (T4ParsedDocument doc, Gtk.TreeStore store)
  168. {
  169. if (doc == null)
  170. return;
  171. Gdk.Color normal = new Gdk.Color (0x00, 0x00, 0x00);
  172. Gdk.Color blue = new Gdk.Color (0x10, 0x40, 0xE0);
  173. Gdk.Color green = new Gdk.Color (0x08, 0xC0, 0x30);
  174. Gdk.Color orange = new Gdk.Color (0xFF, 0xA0, 0x00);
  175. Gdk.Color red = new Gdk.Color (0xC0, 0x00, 0x20);
  176. Gtk.TreeIter parent = Gtk.TreeIter.Zero;
  177. foreach (Mono.TextTemplating.ISegment segment in doc.TemplateSegments) {
  178. Mono.TextTemplating.Directive dir = segment as Mono.TextTemplating.Directive;
  179. if (dir != null) {
  180. parent = Gtk.TreeIter.Zero;
  181. store.AppendValues ("<#@ " + dir.Name + " #>", red, segment);
  182. continue;
  183. }
  184. Mono.TextTemplating.TemplateSegment ts = segment as Mono.TextTemplating.TemplateSegment;
  185. if (ts != null) {
  186. string name;
  187. if (ts.Text.Length > 40) {
  188. name = ts.Text.Substring (0, 40) + "...";
  189. } else {
  190. name = ts.Text;
  191. }
  192. name = name.Replace ('\n', ' ').Trim ();
  193. if (name.Length == 0)
  194. continue;
  195. if (ts.Type == Mono.TextTemplating.SegmentType.Expression) {
  196. store.AppendValues (parent, "<#= " + name + " #>", orange, segment);
  197. } else {
  198. if (ts.Type == Mono.TextTemplating.SegmentType.Block) {
  199. name = "<#" + name + " #>";
  200. store.AppendValues (name, blue, segment);
  201. parent = Gtk.TreeIter.Zero;
  202. } else if (ts.Type == Mono.TextTemplating.SegmentType.Helper) {
  203. name = "<#+" + name + " #>";
  204. store.AppendValues (name, green, segment);
  205. parent = Gtk.TreeIter.Zero;
  206. } else if (ts.Type == Mono.TextTemplating.SegmentType.Content) {
  207. parent = store.AppendValues (name, normal, segment);
  208. }
  209. }
  210. }
  211. }
  212. }
  213. void RefillOutlineStore ()
  214. {
  215. DispatchService.AssertGuiThread ();
  216. Gdk.Threads.Enter ();
  217. refreshingOutline = false;
  218. if (outlineTreeStore == null || !outlineTreeView.IsRealized)
  219. return;
  220. outlineTreeStore.Clear ();
  221. if (ParsedDoc != null) {
  222. DateTime start = DateTime.Now;
  223. RefillOutlineStore (ParsedDoc, outlineTreeStore);
  224. outlineTreeView.ExpandAll ();
  225. outlineTreeView.ExpandAll ();
  226. MonoDevelop.Core.LoggingService.LogDebug ("Built outline in {0}ms", (DateTime.Now - start).Milliseconds);
  227. }
  228. Gdk.Threads.Leave ();
  229. }
  230. void IOutlinedDocument.ReleaseOutlineWidget ()
  231. {
  232. if (outlineTreeView == null)
  233. return;
  234. Gtk.ScrolledWindow w = (Gtk.ScrolledWindow) outlineTreeView.Parent;
  235. w.Destroy ();
  236. outlineTreeView.Destroy ();
  237. outlineTreeStore.Dispose ();
  238. outlineTreeStore = null;
  239. outlineTreeView = null;
  240. }
  241. void SelectSegment (Mono.TextTemplating.ISegment seg)
  242. {
  243. int s = Editor.Document.LocationToOffset (seg.TagStartLocation.Line, seg.TagStartLocation.Column);
  244. if (s > -1) {
  245. Editor.Caret.Offset = s;
  246. Editor.CenterTo (s);
  247. }
  248. }
  249. #endregion
  250. }
  251. }