source: branches/CodeEditor/HeuristicLab.CodeEditor/3.4/CodeEditor.cs @ 11700

Last change on this file since 11700 was 11700, checked in by jkarder, 8 years ago

#2077: created branch and added first version

File size: 13.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.CodeDom.Compiler;
24using System.Collections.Generic;
25using System.ComponentModel;
26using System.Reflection;
27using System.Threading.Tasks;
28using System.Windows.Documents;
29using HeuristicLab.Common;
30using ICSharpCode.AvalonEdit;
31using ICSharpCode.AvalonEdit.AddIn;
32using ICSharpCode.AvalonEdit.CodeCompletion;
33using ICSharpCode.AvalonEdit.Document;
34using ICSharpCode.AvalonEdit.Highlighting;
35using ICSharpCode.AvalonEdit.Search;
36using ICSharpCode.NRefactory.Editor;
37using ICSharpCode.NRefactory.TypeSystem;
38using ICSharpCode.SharpDevelop.Editor;
39using Forms = System.Windows.Forms;
40using Input = System.Windows.Input;
41using Media = System.Windows.Media;
42
43namespace HeuristicLab.CodeEditor {
44  public partial class CodeEditor : Forms.UserControl {
45    private static readonly Media.Color WarningColor = Media.Colors.Blue;
46    private static readonly Media.Color ErrorColor = Media.Colors.Red;
47    private static readonly Media.Color ReadOnlyColor = Media.Colors.Moccasin;
48
49    private const string DefaultDocumentFileName = "Document";
50    private const string DefaultTextEditorSyntaxHighlighting = "C#";
51    private const string DefaultTextEditorFontFamily = "Consolas";
52    private const double DefaultTextEditorFontSize = 13.0;
53    private const bool DefaultTextEditorShowLineNumbers = true;
54    private const bool DefaultTextEditorShowEndOfLine = true;
55    private const bool DefaultTextEditorShowSpaces = true;
56    private const bool DefaultTextEditorShowTabs = true;
57    private const bool DefaultTextEditorConvertTabsToSpaces = true;
58    private const bool DefaultTextEditorHighlightCurrentLine = true;
59    private const int DefaultTextEditorIndentationSize = 2;
60
61    private AssemblyLoader assemblyLoader;
62    private ILanguageFeatures languageFeatures;
63    private TextMarkerService textMarkerService;
64
65    #region Properties
66    internal TextEditor TextEditor { get { return avalonEditWrapper.TextEditor; } }
67    internal Input.RoutedCommand CompletionCommand;
68    internal CompletionWindow CompletionWindow;
69    internal OverloadInsightWindow OverloadInsightWindow;
70
71    private TextDocument Doc { get { return TextEditor.Document; } }
72
73    private ITextMarker prefixMarker;
74    private string prefix = string.Empty;
75    public string Prefix {
76      get { return prefix; }
77      set {
78        if (value == null) value = string.Empty;
79        if (prefix == value) return;
80        if (prefixMarker != null) textMarkerService.Remove(prefixMarker);
81        Doc.Remove(0, prefix.Length);
82        prefix = value;
83        if (value.Length > 0) {
84          Doc.Insert(0, prefix);
85          prefixMarker = textMarkerService.Create(0, prefix.Length);
86          prefixMarker.BackgroundColor = ReadOnlyColor;
87        }
88      }
89    }
90
91    private ITextMarker suffixMarker;
92    private string suffix = string.Empty;
93    public string Suffix {
94      get { return suffix; }
95      set {
96        if (value == null) value = string.Empty;
97        if (suffix == value) return;
98        if (suffixMarker != null) textMarkerService.Remove(suffixMarker);
99        Doc.Remove(Doc.TextLength - suffix.Length, suffix.Length);
100        suffix = value;
101        if (value.Length > 0) {
102          int offset = Doc.TextLength;
103          Doc.Insert(offset, suffix);
104          suffixMarker = textMarkerService.Create(offset, suffix.Length);
105          suffixMarker.BackgroundColor = ReadOnlyColor;
106        }
107      }
108    }
109
110    public string UserCode {
111      get { return Doc.GetText(prefix.Length, Doc.TextLength - suffix.Length - prefix.Length); }
112      set {
113        if (Doc.Text == value) return;
114        Doc.Replace(prefix.Length, Doc.TextLength - suffix.Length - prefix.Length, value);
115      }
116    }
117
118    #region TextEditor
119    [DefaultValue(DefaultTextEditorSyntaxHighlighting)]
120    public string TextEditorSyntaxHighlighting {
121      get { return TextEditor.SyntaxHighlighting.Name; }
122      set {
123        TextEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition(value);
124        ApplyLanguageFeatures();
125      }
126    }
127
128    [DefaultValue(DefaultTextEditorShowLineNumbers)]
129    public bool TextEditorShowLineNumbers {
130      get { return TextEditor.ShowLineNumbers; }
131      set { TextEditor.ShowLineNumbers = value; }
132    }
133
134    [DefaultValue(DefaultTextEditorShowEndOfLine)]
135    public bool TextEditorShowEndOfLine {
136      get { return TextEditor.Options.ShowEndOfLine; }
137      set { TextEditor.Options.ShowEndOfLine = value; }
138    }
139
140    [DefaultValue(DefaultTextEditorShowSpaces)]
141    public bool TextEditorShowSpaces {
142      get { return TextEditor.Options.ShowSpaces; }
143      set { TextEditor.Options.ShowSpaces = value; }
144    }
145
146    [DefaultValue(DefaultTextEditorShowTabs)]
147    public bool TextEditorShowTabs {
148      get { return TextEditor.Options.ShowTabs; }
149      set { TextEditor.Options.ShowTabs = value; }
150    }
151
152    [DefaultValue(DefaultTextEditorConvertTabsToSpaces)]
153    public bool TextEditorConvertTabsToSpaces {
154      get { return TextEditor.Options.ConvertTabsToSpaces; }
155      set { TextEditor.Options.ConvertTabsToSpaces = value; }
156    }
157
158    [DefaultValue(DefaultTextEditorHighlightCurrentLine)]
159    public bool TextEditorHighlightCurrentLine {
160      get { return TextEditor.Options.HighlightCurrentLine; }
161      set { TextEditor.Options.HighlightCurrentLine = value; }
162    }
163
164    [DefaultValue(DefaultTextEditorIndentationSize)]
165    public int TextEditorIndentationSize {
166      get { return TextEditor.Options.IndentationSize; }
167      set { TextEditor.Options.IndentationSize = value; }
168    }
169    #endregion
170
171    public bool ReadOnly {
172      get { return TextEditor.IsReadOnly; }
173      set { TextEditor.IsReadOnly = value; }
174    }
175    #endregion
176
177    public CodeEditor() {
178      InitializeComponent();
179      InitializeTextEditor();
180    }
181
182    private void InitializeTextEditor() {
183      #region AssemblyLoader
184      assemblyLoader = new AssemblyLoader();
185      assemblyLoader.AssembliesLoading += (sender, args) => OnAssembliesLoading(args.Value);
186      assemblyLoader.InternalAssembliesLoaded += (sender, args) => OnInternalAssembliesLoaded(args.Value);
187      assemblyLoader.AssembliesLoaded += (sender, args) => OnAssembliesLoaded(args.Value);
188      assemblyLoader.AssembliesUnloading += (sender, args) => OnAssembliesUnloading(args.Value);
189      assemblyLoader.InternalAssembliesUnloaded += (sender, args) => OnInternalAssembliesUnloaded(args.Value);
190      assemblyLoader.AssembliesUnloaded += (sender, args) => OnAssembliesUnloaded(args.Value);
191      #endregion
192
193      #region TextMarkerService
194      textMarkerService = new TextMarkerService(TextEditor.Document);
195      TextEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService);
196      TextEditor.TextArea.TextView.LineTransformers.Add(textMarkerService);
197      TextEditor.TextArea.TextView.Services.AddService(typeof(ITextMarkerService), textMarkerService);
198
199      #endregion
200
201      #region ReadOnlySectionProvider
202      TextEditor.TextArea.ReadOnlySectionProvider = new MethodDefinitionReadOnlySectionProvider(this);
203      #endregion
204
205      #region SearchPanel
206      SearchPanel.Install(TextEditor);
207      #endregion
208
209      #region CompletionCommand
210      CompletionCommand = new Input.RoutedCommand();
211      CompletionCommand.InputGestures.Add(new Input.KeyGesture(Input.Key.Space, Input.ModifierKeys.Control));
212      #endregion
213
214      TextEditorSyntaxHighlighting = DefaultTextEditorSyntaxHighlighting;
215      TextEditorShowLineNumbers = DefaultTextEditorShowLineNumbers;
216      TextEditorShowEndOfLine = DefaultTextEditorShowEndOfLine;
217      TextEditorShowSpaces = DefaultTextEditorShowSpaces;
218      TextEditorShowTabs = DefaultTextEditorShowTabs;
219      TextEditorConvertTabsToSpaces = DefaultTextEditorConvertTabsToSpaces;
220      TextEditorHighlightCurrentLine = DefaultTextEditorHighlightCurrentLine;
221      TextEditorIndentationSize = DefaultTextEditorIndentationSize;
222
223      Doc.FileName = DefaultDocumentFileName;
224
225      TextEditor.FontFamily = new Media.FontFamily(DefaultTextEditorFontFamily);
226      TextEditor.FontSize = DefaultTextEditorFontSize;
227
228      TextEditor.TextChanged += (sender, args) => {
229        textMarkerService.RemoveAll(x => x != prefixMarker && x != suffixMarker);
230        OnTextEditorTextChanged();
231      };
232    }
233
234    #region Assembly Management
235    public void AddAssembly(Assembly a) {
236      assemblyLoader.AddAssembly(a);
237    }
238
239    public void AddAssemblies(IEnumerable<Assembly> assemblies) {
240      assemblyLoader.AddAssemblies(assemblies);
241    }
242
243    public async Task AddAssembliesAsync(IEnumerable<Assembly> assemblies) {
244      await assemblyLoader.AddAssembliesAsync(assemblies);
245    }
246
247    public void RemoveAssembly(Assembly a) {
248      assemblyLoader.RemoveAssembly(a);
249    }
250    #endregion
251
252    public void ScrollToPosition(int line, int column) {
253      var segment = GetSegmentAtLocation(line, column);
254      TextEditor.CaretOffset = segment.Offset + segment.Length;
255      TextEditor.ScrollToLine(line);
256    }
257
258    public void ScrollAfterPrefix() {
259      var location = Doc.GetLocation(prefix.Length);
260      ScrollToPosition(location.Line, location.Column);
261    }
262
263    #region Compiler Errors
264    public void ShowCompileErrors(CompilerErrorCollection compilerErrors) {
265      if (compilerErrors == null) return;
266
267      textMarkerService.RemoveAll(x => x != prefixMarker && x != suffixMarker);
268
269      foreach (CompilerError error in compilerErrors) {
270        var startLocation = Doc.GetLocation(prefix.Length);
271        if (error.Line == 1) error.Column += startLocation.Column;
272        error.Line += startLocation.Line;
273        AddErrorMarker(error);
274      }
275    }
276
277    private void AddErrorMarker(CompilerError error) {
278      var segment = GetSegmentAtLocation(error.Line, error.Column);
279      var marker = textMarkerService.Create(segment.Offset, segment.Length);
280      marker.MarkerTypes = TextMarkerTypes.SquigglyUnderline;
281      marker.MarkerColor = error.IsWarning ? WarningColor : ErrorColor;
282    }
283
284    private ISegment GetSegmentAtLocation(int line, int column) {
285      line = Math.Max(Doc.GetLocation(prefix.Length).Line, line - 1);
286      line = Math.Min(Doc.GetLocation(Doc.TextLength - suffix.Length).Line, line);
287
288      var startOffset = Doc.GetOffset(line, column);
289      var lineEndOffset = Doc.GetLineByNumber(line).EndOffset;
290      var endOffset = TextUtilities.GetNextCaretPosition(Doc, startOffset, LogicalDirection.Forward, CaretPositioningMode.WordBorder);
291      if (endOffset < 0) endOffset = startOffset;
292      endOffset = Math.Min(lineEndOffset + 1, endOffset);
293
294      var segment = new TextSegment { StartOffset = startOffset, EndOffset = endOffset };
295
296      return segment;
297    }
298    #endregion
299
300    private void ApplyLanguageFeatures() {
301      switch (TextEditorSyntaxHighlighting) {
302        case "XML":
303          languageFeatures = new XmlLanguageFeatures(this);
304          break;
305        default:
306          languageFeatures = new CSharpLanguageFeatures(this);
307          break;
308      }
309    }
310
311    #region Events
312    public event EventHandler TextEditorTextChanged;
313    private void OnTextEditorTextChanged() {
314      var handler = TextEditorTextChanged;
315      if (handler != null) handler(this, EventArgs.Empty);
316    }
317
318    public event EventHandler<EventArgs<IEnumerable<Assembly>>> AssembliesLoading;
319    private void OnAssembliesLoading(IEnumerable<Assembly> args) {
320      var handler = AssembliesLoading;
321      if (handler != null) handler(this, new EventArgs<IEnumerable<Assembly>>(args));
322    }
323
324    public event EventHandler<EventArgs<IEnumerable<Assembly>>> AssembliesLoaded;
325    private void OnAssembliesLoaded(IEnumerable<Assembly> args) {
326      var handler = AssembliesLoaded;
327      if (handler != null) handler(this, new EventArgs<IEnumerable<Assembly>>(args));
328    }
329
330    public event EventHandler<EventArgs<IEnumerable<IUnresolvedAssembly>>> InternalAssembliesLoaded;
331    private void OnInternalAssembliesLoaded(IEnumerable<IUnresolvedAssembly> args) {
332      var handler = InternalAssembliesLoaded;
333      if (handler != null) handler(this, new EventArgs<IEnumerable<IUnresolvedAssembly>>(args));
334    }
335
336    public event EventHandler<EventArgs<IEnumerable<Assembly>>> AssembliesUnloading;
337    private void OnAssembliesUnloading(IEnumerable<Assembly> args) {
338      var handler = AssembliesUnloading;
339      if (handler != null) handler(this, new EventArgs<IEnumerable<Assembly>>(args));
340    }
341
342    public event EventHandler<EventArgs<IEnumerable<Assembly>>> AssembliesUnloaded;
343    private void OnAssembliesUnloaded(IEnumerable<Assembly> args) {
344      var handler = AssembliesUnloaded;
345      if (handler != null) handler(this, new EventArgs<IEnumerable<Assembly>>(args));
346    }
347
348    public event EventHandler<EventArgs<IEnumerable<IUnresolvedAssembly>>> InternalAssembliesUnloaded;
349    private void OnInternalAssembliesUnloaded(IEnumerable<IUnresolvedAssembly> args) {
350      var handler = InternalAssembliesUnloaded;
351      if (handler != null) handler(this, new EventArgs<IEnumerable<IUnresolvedAssembly>>(args));
352    }
353    #endregion
354  }
355}
Note: See TracBrowser for help on using the repository browser.