Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.DatasetRefactor/sources/HeuristicLab.CodeEditor/3.3/CodeEditor.cs @ 12012

Last change on this file since 12012 was 11571, checked in by bburlacu, 10 years ago

#2276: Commit initial version of IDataset interface and code refactoring.

File size: 15.6 KB
Line 
1// CSharp Editor Example with Code Completion
2// Copyright (c) 2006, Daniel Grunwald
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification, are
6// permitted provided that the following conditions are met:
7//
8// - Redistributions of source code must retain the above copyright notice, this list
9//   of conditions and the following disclaimer.
10//
11// - Redistributions in binary form must reproduce the above copyright notice, this list
12//   of conditions and the following disclaimer in the documentation and/or other materials
13//   provided with the distribution.
14//
15// - Neither the name of the ICSharpCode team nor the names of its contributors may be used to
16//   endorse or promote products derived from this software without specific prior written
17//   permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &AS IS& AND ANY EXPRESS
20// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28using System;
29using System.CodeDom.Compiler;
30using System.Collections.Generic;
31using System.Diagnostics;
32using System.Drawing;
33using System.IO;
34using System.Reflection;
35using System.Threading;
36using System.Windows.Forms;
37using HeuristicLab.Common.Resources;
38using ICSharpCode.TextEditor;
39using ICSharpCode.TextEditor.Document;
40using Dom = ICSharpCode.SharpDevelop.Dom;
41using NRefactory = ICSharpCode.NRefactory;
42
43namespace HeuristicLab.CodeEditor {
44
45  public partial class CodeEditor : UserControl {
46
47    #region Fields & Properties
48
49    internal Dom.ProjectContentRegistry projectContentRegistry;
50    internal Dom.DefaultProjectContent projectContent;
51    internal Dom.ParseInformation parseInformation = new Dom.ParseInformation();
52    Dom.ICompilationUnit lastCompilationUnit;
53    Thread parserThread;
54
55    /// <summary>
56    /// Many SharpDevelop.Dom methods take a file name, which is really just a unique identifier
57    /// for a file - Dom methods don't try to access code files on disk, so the file does not have
58    /// to exist.
59    /// SharpDevelop itself uses internal names of the kind "[randomId]/Class1.cs" to support
60    /// code-completion in unsaved files.
61    /// </summary>
62    public const string DummyFileName = "edited.cs";
63
64    private IDocument Doc {
65      get {
66        return textEditor.Document;
67      }
68    }
69
70    private string prefix = "";
71    private TextMarker prefixMarker =
72      new TextMarker(0, 1, TextMarkerType.SolidBlock, Color.LightGray) {
73        IsReadOnly = true,
74      };
75    public string Prefix {
76      get {
77        return prefix;
78      }
79      set {
80        if (value == null) value = "";
81        if (prefix == value) return;
82        Doc.MarkerStrategy.RemoveMarker(prefixMarker);
83        Doc.Remove(0, prefix.Length);
84        prefix = value;
85        if (value.Length > 0) {
86          Doc.Insert(0, value);
87          prefixMarker.Offset = 0;
88          prefixMarker.Length = prefix.Length;
89          Doc.MarkerStrategy.AddMarker(prefixMarker);
90        }
91        Doc.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
92        Doc.CommitUpdate();
93      }
94    }
95
96    private string suffix = "";
97    private TextMarker suffixMarker =
98      new TextMarker(0, 1, TextMarkerType.SolidBlock, Color.LightGray) {
99        IsReadOnly = true,
100      };
101    public string Suffix {
102      get {
103        return suffix;
104      }
105      set {
106        if (value == null) value = "";
107        if (suffix == value) return;
108        Doc.MarkerStrategy.RemoveMarker(suffixMarker);
109        Doc.Remove(Doc.TextLength - suffix.Length, suffix.Length);
110        suffix = value;
111        if (value.Length > 0) {
112          suffixMarker.Offset = Doc.TextLength;
113          Doc.Insert(Doc.TextLength, value);
114          suffixMarker.Length = suffix.Length;
115          Doc.MarkerStrategy.AddMarker(suffixMarker);
116        }
117        Doc.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
118        Doc.CommitUpdate();
119      }
120    }
121
122    public string UserCode {
123      get {
124        return Doc.GetText(
125          prefix.Length,
126          Doc.TextLength - suffix.Length - prefix.Length);
127      }
128      set {
129        Doc.Replace(prefix.Length, Doc.TextLength - suffix.Length - prefix.Length, value);
130        Doc.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
131        Doc.CommitUpdate();
132      }
133    }
134
135    public bool ReadOnly {
136      get { return Doc.ReadOnly; }
137      set { Doc.ReadOnly = value; }
138    }
139
140    #endregion
141
142    public event EventHandler TextEditorValidated;
143
144    protected void OnTextEditorValidated() {
145      if (TextEditorValidated != null)
146        TextEditorValidated(this, EventArgs.Empty);
147    }
148
149    public event EventHandler TextEditorTextChanged;
150
151    protected void OnTextEditorTextChanged() {
152      if (TextEditorTextChanged != null)
153        TextEditorTextChanged(this, EventArgs.Empty);
154    }
155
156    public CodeEditor() {
157      InitializeComponent();
158
159
160      var fontFamily = new FontFamily("Consolas");
161      textEditor.Font = new Font(fontFamily, 10, FontStyle.Regular);
162      textEditor.ActiveTextAreaControl.TextEditorProperties.SupportReadOnlySegments = true;
163
164      textEditor.SetHighlighting("C#");
165      textEditor.ShowEOLMarkers = false;
166      textEditor.ShowInvalidLines = false;
167      HostCallbackImplementation.Register(this);
168      CodeCompletionKeyHandler.Attach(this, textEditor);
169      ToolTipProvider.Attach(this, textEditor);
170
171      projectContentRegistry = new Dom.ProjectContentRegistry(); // Default .NET 2.0 registry
172      try {
173        string persistencePath = Path.Combine(Path.GetTempPath(), "HeuristicLab.CodeEditor");
174        if (!Directory.Exists(persistencePath))
175          Directory.CreateDirectory(persistencePath);
176        FileStream fs = File.Create(Path.Combine(persistencePath, "test.tmp"));
177        fs.Close();
178        File.Delete(Path.Combine(persistencePath, "test.tmp"));
179        // if we made it this far, enable on-disk parsing cache
180        projectContentRegistry.ActivatePersistence(persistencePath);
181      }
182      catch (NotSupportedException) {
183      }
184      catch (IOException) {
185      }
186      catch (System.Security.SecurityException) {
187      }
188      catch (UnauthorizedAccessException) {
189      }
190      catch (ArgumentException) {
191      }
192      projectContent = new Dom.DefaultProjectContent();
193      projectContent.Language = Dom.LanguageProperties.CSharp;
194    }
195
196    protected override void OnLoad(EventArgs e) {
197      base.OnLoad(e);
198
199      if (DesignMode)
200        return;
201
202      textEditor.ActiveTextAreaControl.TextArea.KeyEventHandler += new ICSharpCode.TextEditor.KeyEventHandler(TextArea_KeyEventHandler);
203      textEditor.ActiveTextAreaControl.TextArea.DoProcessDialogKey += new DialogKeyProcessor(TextArea_DoProcessDialogKey);
204
205      parserThread = new Thread(ParserThread);
206      parserThread.IsBackground = true;
207      parserThread.Start();
208
209      textEditor.Validated += (s, a) => { OnTextEditorValidated(); };
210      textEditor.TextChanged += (s, a) => { OnTextEditorTextChanged(); };
211      InitializeImageList();
212    }
213
214    private void InitializeImageList() {
215      imageList1.Images.Clear();
216      imageList1.Images.Add("Icons.16x16.Class.png", VSImageLibrary.Class);
217      imageList1.Images.Add("Icons.16x16.Method.png", VSImageLibrary.Method);
218      imageList1.Images.Add("Icons.16x16.Property.png", VSImageLibrary.Properties);
219      imageList1.Images.Add("Icons.16x16.Field.png", VSImageLibrary.Field);
220      imageList1.Images.Add("Icons.16x16.Enum.png", VSImageLibrary.Enum);
221      imageList1.Images.Add("Icons.16x16.NameSpace.png", VSImageLibrary.Namespace);
222      imageList1.Images.Add("Icons.16x16.Event.png", VSImageLibrary.Event);
223    }
224
225    #region keyboard handlers: filter input in read-only areas
226
227    bool TextArea_KeyEventHandler(char ch) {
228      int caret = textEditor.ActiveTextAreaControl.Caret.Offset;
229      return caret < prefix.Length || caret > Doc.TextLength - suffix.Length;
230    }
231
232    bool TextArea_DoProcessDialogKey(Keys keyData) {
233      if (keyData == Keys.Return) {
234        int caret = textEditor.ActiveTextAreaControl.Caret.Offset;
235        if (caret < prefix.Length ||
236            caret > Doc.TextLength - suffix.Length) {
237          return true;
238        }
239      }
240      return false;
241    }
242
243    #endregion
244
245    public void ScrollAfterPrefix() {
246      int lineNr = prefix != null ? Doc.OffsetToPosition(prefix.Length).Line : 0;
247      textEditor.ActiveTextAreaControl.JumpTo(lineNr + 1);
248    }
249
250    private List<TextMarker> errorMarkers = new List<TextMarker>();
251    private List<Bookmark> errorBookmarks = new List<Bookmark>();
252    public void ShowCompileErrors(CompilerErrorCollection compilerErrors, string filename) {
253      Doc.MarkerStrategy.RemoveAll(m => errorMarkers.Contains(m));
254      Doc.BookmarkManager.RemoveMarks(m => errorBookmarks.Contains(m));
255      errorMarkers.Clear();
256      errorBookmarks.Clear();
257      errorLabel.Text = "";
258      errorLabel.ToolTipText = null;
259      if (compilerErrors == null)
260        return;
261      foreach (CompilerError error in compilerErrors) {
262        if (!error.FileName.EndsWith(filename)) {
263          errorLabel.Text = "Error in generated code";
264          errorLabel.ToolTipText = string.Format("{0}{1}:{2} -> {3}",
265            errorLabel.ToolTipText != null ? (errorLabel.ToolTipText + "\n\n") : "",
266            error.Line, error.Column,
267            error.ErrorText);
268          continue;
269        }
270        var startPosition = Doc.OffsetToPosition(prefix.Length);
271        if (error.Line == 1)
272          error.Column += startPosition.Column;
273        error.Line += startPosition.Line;
274        AddErrorMarker(error);
275        AddErrorBookmark(error);
276      }
277      Doc.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
278      Doc.CommitUpdate();
279    }
280
281    private void AddErrorMarker(CompilerError error) {
282      var segment = GetSegmentAtOffset(error.Line, error.Column);
283      Color color = error.IsWarning ? Color.Blue : Color.Red;
284      var marker = new TextMarker(segment.Offset, segment.Length, TextMarkerType.WaveLine, color) {
285        ToolTip = error.ErrorText,
286      };
287      errorMarkers.Add(marker);
288      Doc.MarkerStrategy.AddMarker(marker);
289    }
290
291    private void AddErrorBookmark(CompilerError error) {
292      var bookmark = new ErrorBookmark(Doc, new TextLocation(error.Column, error.Line - 1));
293      errorBookmarks.Add(bookmark);
294      Doc.BookmarkManager.AddMark(bookmark);
295    }
296
297    private AbstractSegment GetSegmentAtOffset(int lineNr, int columnNr) {
298      lineNr = Math.Max(Doc.OffsetToPosition(prefix.Length).Line, lineNr);
299      lineNr = Math.Min(Doc.OffsetToPosition(Doc.TextLength - suffix.Length).Line, lineNr);
300      var line = Doc.GetLineSegment(lineNr - 1);
301      columnNr = Math.Max(0, columnNr);
302      columnNr = Math.Min(line.Length, columnNr);
303      var word = line.GetWord(columnNr);
304      AbstractSegment segment = new AbstractSegment();
305      if (word != null) {
306        segment.Offset = line.Offset + word.Offset;
307        segment.Length = word.Length;
308      } else {
309        segment.Offset = line.Offset + columnNr - 1;
310        segment.Length = 1;
311      }
312      return segment;
313    }
314
315    private HashSet<Assembly> assemblies = new HashSet<Assembly>();
316    public IEnumerable<Assembly> ReferencedAssemblies {
317      get { return assemblies; }
318    }
319    public void AddAssembly(Assembly a) {
320      ShowMessage("Loading " + a.GetName().Name + "...");
321      if (!assemblies.Contains(a)) {
322        var reference = projectContentRegistry.GetProjectContentForReference(a.GetName().Name, a.Location);
323        projectContent.AddReferencedContent(reference);
324        assemblies.Add(a);
325      }
326      ShowMessage("Ready");
327    }
328    public void RemoveAssembly(Assembly a) {
329      ShowMessage("Unloading " + a.GetName().Name + "...");
330      if (assemblies.Contains(a)) {
331        var content = projectContentRegistry.GetExistingProjectContent(a.Location);
332        if (content != null) {
333          projectContent.ReferencedContents.Remove(content);
334          projectContentRegistry.UnloadProjectContent(content);
335        }
336      }
337      ShowMessage("Ready");
338    }
339
340    private bool runParser = true;
341    private void ParserThread() {
342      BeginInvoke(new MethodInvoker(delegate { parserThreadLabel.Text = "Loading mscorlib..."; }));
343      projectContent.AddReferencedContent(projectContentRegistry.Mscorlib);
344      ParseStep();
345      BeginInvoke(new MethodInvoker(delegate { parserThreadLabel.Text = "Ready"; }));
346      while (runParser && !IsDisposed) {
347        ParseStep();
348        Thread.Sleep(2000);
349      }
350    }
351
352    private void ParseStep() {
353      try {
354        string code = null;
355        Invoke(new MethodInvoker(delegate { code = textEditor.Text; }));
356        TextReader textReader = new StringReader(code);
357        Dom.ICompilationUnit newCompilationUnit;
358        NRefactory.SupportedLanguage supportedLanguage;
359        supportedLanguage = NRefactory.SupportedLanguage.CSharp;
360        using (NRefactory.IParser p = NRefactory.ParserFactory.CreateParser(supportedLanguage, textReader)) {
361          p.ParseMethodBodies = false;
362          p.Parse();
363          newCompilationUnit = ConvertCompilationUnit(p.CompilationUnit);
364        }
365        projectContent.UpdateCompilationUnit(lastCompilationUnit, newCompilationUnit, DummyFileName);
366        lastCompilationUnit = newCompilationUnit;
367        parseInformation.SetCompilationUnit(newCompilationUnit);
368      }
369      catch { }
370    }
371
372    Dom.ICompilationUnit ConvertCompilationUnit(NRefactory.Ast.CompilationUnit cu) {
373      Dom.NRefactoryResolver.NRefactoryASTConvertVisitor converter;
374      converter = new Dom.NRefactoryResolver.NRefactoryASTConvertVisitor(projectContent);
375      cu.AcceptVisitor(converter, null);
376      return converter.Cu;
377    }
378
379    private void ShowMessage(string m) {
380      if (this.Handle == null)
381        return;
382      BeginInvoke(new Action(() => parserThreadLabel.Text = m));
383    }
384
385    private void toolStripStatusLabel1_Click(object sender, EventArgs e) {
386      var proc = new Process();
387      proc.StartInfo.FileName = sharpDevelopLabel.Tag.ToString();
388      proc.Start();
389    }
390
391    private void CodeEditor_Resize(object sender, EventArgs e) {
392      var textArea = textEditor.ActiveTextAreaControl.TextArea;
393      var vScrollBar = textEditor.ActiveTextAreaControl.VScrollBar;
394      var hScrollBar = textEditor.ActiveTextAreaControl.HScrollBar;
395
396      textArea.SuspendLayout();
397      textArea.Width = textEditor.Width - vScrollBar.Width;
398      textArea.Height = textEditor.Height - hScrollBar.Height;
399      textArea.ResumeLayout();
400
401      vScrollBar.SuspendLayout();
402      vScrollBar.Location = new Point(textArea.Width, 0);
403      vScrollBar.Height = textArea.Height;
404      vScrollBar.ResumeLayout();
405
406      hScrollBar.SuspendLayout();
407      hScrollBar.Location = new Point(0, textArea.Height);
408      hScrollBar.Width = textArea.Width;
409      hScrollBar.ResumeLayout();
410    }
411  }
412}
Note: See TracBrowser for help on using the repository browser.