#region License Information
/* HeuristicLab
* Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Documents;
using HeuristicLab.Common;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.AddIn;
using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Search;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Editor;
using Forms = System.Windows.Forms;
using Input = System.Windows.Input;
using Media = System.Windows.Media;
namespace HeuristicLab.CodeEditor {
public partial class CodeEditor : Forms.UserControl {
private static readonly Media.Color WarningColor = Media.Colors.Blue;
private static readonly Media.Color ErrorColor = Media.Colors.Red;
private static readonly Media.Color ReadOnlyColor = Media.Colors.Moccasin;
private const string DefaultDocumentFileName = "Document";
private const string DefaultTextEditorSyntaxHighlighting = "C#";
private const string DefaultTextEditorFontFamily = "Consolas";
private const double DefaultTextEditorFontSize = 13.0;
private const bool DefaultTextEditorShowLineNumbers = true;
private const bool DefaultTextEditorShowEndOfLine = true;
private const bool DefaultTextEditorShowSpaces = true;
private const bool DefaultTextEditorShowTabs = true;
private const bool DefaultTextEditorConvertTabsToSpaces = true;
private const bool DefaultTextEditorHighlightCurrentLine = true;
private const int DefaultTextEditorIndentationSize = 2;
private AssemblyLoader assemblyLoader;
private ILanguageFeatures languageFeatures;
private TextMarkerService textMarkerService;
#region Properties
internal TextEditor TextEditor { get { return avalonEditWrapper.TextEditor; } }
internal Input.RoutedCommand CompletionCommand;
internal CompletionWindow CompletionWindow;
internal OverloadInsightWindow OverloadInsightWindow;
private TextDocument Doc { get { return TextEditor.Document; } }
private ITextMarker prefixMarker;
private string prefix = string.Empty;
public string Prefix {
get { return prefix; }
set {
if (value == null) value = string.Empty;
if (prefix == value) return;
if (prefixMarker != null) textMarkerService.Remove(prefixMarker);
Doc.Remove(0, prefix.Length);
prefix = value;
if (value.Length > 0) {
Doc.Insert(0, prefix);
prefixMarker = textMarkerService.Create(0, prefix.Length);
prefixMarker.BackgroundColor = ReadOnlyColor;
}
}
}
private ITextMarker suffixMarker;
private string suffix = string.Empty;
public string Suffix {
get { return suffix; }
set {
if (value == null) value = string.Empty;
if (suffix == value) return;
if (suffixMarker != null) textMarkerService.Remove(suffixMarker);
Doc.Remove(Doc.TextLength - suffix.Length, suffix.Length);
suffix = value;
if (value.Length > 0) {
int offset = Doc.TextLength;
Doc.Insert(offset, suffix);
suffixMarker = textMarkerService.Create(offset, suffix.Length);
suffixMarker.BackgroundColor = ReadOnlyColor;
}
}
}
public string UserCode {
get { return Doc.GetText(prefix.Length, Doc.TextLength - suffix.Length - prefix.Length); }
set {
if (Doc.Text == value) return;
Doc.Replace(prefix.Length, Doc.TextLength - suffix.Length - prefix.Length, value);
}
}
#region TextEditor
[DefaultValue(DefaultTextEditorSyntaxHighlighting)]
public string TextEditorSyntaxHighlighting {
get { return TextEditor.SyntaxHighlighting.Name; }
set {
TextEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition(value);
ApplyLanguageFeatures();
}
}
[DefaultValue(DefaultTextEditorShowLineNumbers)]
public bool TextEditorShowLineNumbers {
get { return TextEditor.ShowLineNumbers; }
set { TextEditor.ShowLineNumbers = value; }
}
[DefaultValue(DefaultTextEditorShowEndOfLine)]
public bool TextEditorShowEndOfLine {
get { return TextEditor.Options.ShowEndOfLine; }
set { TextEditor.Options.ShowEndOfLine = value; }
}
[DefaultValue(DefaultTextEditorShowSpaces)]
public bool TextEditorShowSpaces {
get { return TextEditor.Options.ShowSpaces; }
set { TextEditor.Options.ShowSpaces = value; }
}
[DefaultValue(DefaultTextEditorShowTabs)]
public bool TextEditorShowTabs {
get { return TextEditor.Options.ShowTabs; }
set { TextEditor.Options.ShowTabs = value; }
}
[DefaultValue(DefaultTextEditorConvertTabsToSpaces)]
public bool TextEditorConvertTabsToSpaces {
get { return TextEditor.Options.ConvertTabsToSpaces; }
set { TextEditor.Options.ConvertTabsToSpaces = value; }
}
[DefaultValue(DefaultTextEditorHighlightCurrentLine)]
public bool TextEditorHighlightCurrentLine {
get { return TextEditor.Options.HighlightCurrentLine; }
set { TextEditor.Options.HighlightCurrentLine = value; }
}
[DefaultValue(DefaultTextEditorIndentationSize)]
public int TextEditorIndentationSize {
get { return TextEditor.Options.IndentationSize; }
set { TextEditor.Options.IndentationSize = value; }
}
#endregion
public bool ReadOnly {
get { return TextEditor.IsReadOnly; }
set { TextEditor.IsReadOnly = value; }
}
#endregion
public CodeEditor() {
InitializeComponent();
InitializeTextEditor();
}
private void InitializeTextEditor() {
#region AssemblyLoader
assemblyLoader = new AssemblyLoader();
assemblyLoader.AssembliesLoading += (sender, args) => OnAssembliesLoading(args.Value);
assemblyLoader.InternalAssembliesLoaded += (sender, args) => OnInternalAssembliesLoaded(args.Value);
assemblyLoader.AssembliesLoaded += (sender, args) => OnAssembliesLoaded(args.Value);
assemblyLoader.AssembliesUnloading += (sender, args) => OnAssembliesUnloading(args.Value);
assemblyLoader.InternalAssembliesUnloaded += (sender, args) => OnInternalAssembliesUnloaded(args.Value);
assemblyLoader.AssembliesUnloaded += (sender, args) => OnAssembliesUnloaded(args.Value);
#endregion
#region TextMarkerService
textMarkerService = new TextMarkerService(TextEditor.Document);
TextEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService);
TextEditor.TextArea.TextView.LineTransformers.Add(textMarkerService);
TextEditor.TextArea.TextView.Services.AddService(typeof(ITextMarkerService), textMarkerService);
#endregion
#region ReadOnlySectionProvider
TextEditor.TextArea.ReadOnlySectionProvider = new MethodDefinitionReadOnlySectionProvider(this);
#endregion
#region SearchPanel
SearchPanel.Install(TextEditor);
#endregion
#region CompletionCommand
CompletionCommand = new Input.RoutedCommand();
CompletionCommand.InputGestures.Add(new Input.KeyGesture(Input.Key.Space, Input.ModifierKeys.Control));
#endregion
TextEditorSyntaxHighlighting = DefaultTextEditorSyntaxHighlighting;
TextEditorShowLineNumbers = DefaultTextEditorShowLineNumbers;
TextEditorShowEndOfLine = DefaultTextEditorShowEndOfLine;
TextEditorShowSpaces = DefaultTextEditorShowSpaces;
TextEditorShowTabs = DefaultTextEditorShowTabs;
TextEditorConvertTabsToSpaces = DefaultTextEditorConvertTabsToSpaces;
TextEditorHighlightCurrentLine = DefaultTextEditorHighlightCurrentLine;
TextEditorIndentationSize = DefaultTextEditorIndentationSize;
Doc.FileName = DefaultDocumentFileName;
TextEditor.FontFamily = new Media.FontFamily(DefaultTextEditorFontFamily);
TextEditor.FontSize = DefaultTextEditorFontSize;
TextEditor.TextChanged += (sender, args) => {
textMarkerService.RemoveAll(x => x != prefixMarker && x != suffixMarker);
OnTextEditorTextChanged();
};
}
#region Assembly Management
public void AddAssembly(Assembly a) {
assemblyLoader.AddAssembly(a);
}
public void AddAssemblies(IEnumerable assemblies) {
assemblyLoader.AddAssemblies(assemblies);
}
public async Task AddAssembliesAsync(IEnumerable assemblies) {
await assemblyLoader.AddAssembliesAsync(assemblies);
}
public void RemoveAssembly(Assembly a) {
assemblyLoader.RemoveAssembly(a);
}
#endregion
public void ScrollToPosition(int line, int column) {
var segment = GetSegmentAtLocation(line, column);
TextEditor.CaretOffset = segment.Offset + segment.Length;
TextEditor.ScrollToLine(line);
}
public void ScrollAfterPrefix() {
var location = Doc.GetLocation(prefix.Length);
ScrollToPosition(location.Line, location.Column);
}
#region Compiler Errors
public void ShowCompileErrors(CompilerErrorCollection compilerErrors) {
if (compilerErrors == null) return;
textMarkerService.RemoveAll(x => x != prefixMarker && x != suffixMarker);
foreach (CompilerError error in compilerErrors) {
var startLocation = Doc.GetLocation(prefix.Length);
if (error.Line == 1) error.Column += startLocation.Column;
error.Line += startLocation.Line;
AddErrorMarker(error);
}
}
private void AddErrorMarker(CompilerError error) {
var segment = GetSegmentAtLocation(error.Line, error.Column);
var marker = textMarkerService.Create(segment.Offset, segment.Length);
marker.MarkerTypes = TextMarkerTypes.SquigglyUnderline;
marker.MarkerColor = error.IsWarning ? WarningColor : ErrorColor;
}
private ISegment GetSegmentAtLocation(int line, int column) {
line = Math.Max(Doc.GetLocation(prefix.Length).Line, line - 1);
line = Math.Min(Doc.GetLocation(Doc.TextLength - suffix.Length).Line, line);
var startOffset = Doc.GetOffset(line, column);
var lineEndOffset = Doc.GetLineByNumber(line).EndOffset;
var endOffset = TextUtilities.GetNextCaretPosition(Doc, startOffset, LogicalDirection.Forward, CaretPositioningMode.WordBorder);
if (endOffset < 0) endOffset = startOffset;
endOffset = Math.Min(lineEndOffset + 1, endOffset);
var segment = new TextSegment { StartOffset = startOffset, EndOffset = endOffset };
return segment;
}
#endregion
private void ApplyLanguageFeatures() {
switch (TextEditorSyntaxHighlighting) {
case "XML":
languageFeatures = new XmlLanguageFeatures(this);
break;
default:
languageFeatures = new CSharpLanguageFeatures(this);
break;
}
}
#region Events
public event EventHandler TextEditorTextChanged;
private void OnTextEditorTextChanged() {
var handler = TextEditorTextChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler>> AssembliesLoading;
private void OnAssembliesLoading(IEnumerable args) {
var handler = AssembliesLoading;
if (handler != null) handler(this, new EventArgs>(args));
}
public event EventHandler>> AssembliesLoaded;
private void OnAssembliesLoaded(IEnumerable args) {
var handler = AssembliesLoaded;
if (handler != null) handler(this, new EventArgs>(args));
}
public event EventHandler>> InternalAssembliesLoaded;
private void OnInternalAssembliesLoaded(IEnumerable args) {
var handler = InternalAssembliesLoaded;
if (handler != null) handler(this, new EventArgs>(args));
}
public event EventHandler>> AssembliesUnloading;
private void OnAssembliesUnloading(IEnumerable args) {
var handler = AssembliesUnloading;
if (handler != null) handler(this, new EventArgs>(args));
}
public event EventHandler>> AssembliesUnloaded;
private void OnAssembliesUnloaded(IEnumerable args) {
var handler = AssembliesUnloaded;
if (handler != null) handler(this, new EventArgs>(args));
}
public event EventHandler>> InternalAssembliesUnloaded;
private void OnInternalAssembliesUnloaded(IEnumerable args) {
var handler = InternalAssembliesUnloaded;
if (handler != null) handler(this, new EventArgs>(args));
}
#endregion
}
}