Browse Source

implemented ErrorDrawer for ITextEditor

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4828 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Siegfried Pammer 16 years ago
parent
commit
f15a135bf9
  1. 4
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs
  2. 38
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  3. 66
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
  4. 9
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/BackgroundGeometryBuilder.cs
  5. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LayerPosition.cs
  6. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  7. 27
      src/Main/Base/Project/Src/Editor/ITextMarker.cs
  8. 151
      src/Main/Base/Project/Src/Services/Tasks/ErrorDrawer.cs

4
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs

@ -155,9 +155,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -155,9 +155,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
{
base.Dispose();
BookmarksDetach();
codeEditor.DisposeLanguageBinding();
// Unload document on dispose.
codeEditor.Document = null;
codeEditor.Dispose();
}
public override string ToString()

38
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -38,7 +38,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
/// Integrates AvalonEdit with SharpDevelop.
/// Also provides support for Split-View (showing two AvalonEdit instances using the same TextDocument)
/// </summary>
public class CodeEditor : Grid
public class CodeEditor : Grid, IDisposable
{
const string contextMenuPath = "/SharpDevelop/ViewContent/AvalonEdit/ContextMenu";
@ -49,6 +49,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -49,6 +49,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
CodeEditorAdapter secondaryTextEditorAdapter;
readonly IconBarManager iconBarManager;
readonly TextMarkerService textMarkerService;
readonly ErrorDrawer errorDrawer;
public TextEditor PrimaryTextEditor {
get { return primaryTextEditor; }
@ -64,7 +65,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -64,7 +65,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
get {
return document;
}
set {
private set {
if (document != value) {
if (document != null)
document.UpdateFinished -= DocumentUpdateFinished;
@ -124,13 +125,6 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -124,13 +125,6 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
}
internal void DisposeLanguageBinding()
{
primaryTextEditorAdapter.Language.Detach();
if (secondaryTextEditorAdapter != null)
secondaryTextEditorAdapter.Language.Detach();
}
public void Redraw(ISegment segment, DispatcherPriority priority)
{
primaryTextEditor.TextArea.TextView.Redraw(segment, priority);
@ -150,6 +144,8 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -150,6 +144,8 @@ namespace ICSharpCode.AvalonEdit.AddIn
primaryTextEditorAdapter = (CodeEditorAdapter)primaryTextEditor.TextArea.GetService(typeof(ITextEditor));
Debug.Assert(primaryTextEditorAdapter != null);
this.errorDrawer = new ErrorDrawer(primaryTextEditorAdapter);
this.Document = primaryTextEditor.Document;
primaryTextEditor.SetBinding(TextEditor.DocumentProperty, new Binding("Document") { Source = this });
@ -312,6 +308,20 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -312,6 +308,20 @@ namespace ICSharpCode.AvalonEdit.AddIn
args.LogicalPosition = AvalonEditDocumentAdapter.ToLocation(pos.Value);
}
var markersAtOffset = textMarkerService.GetMarkersAtOffset(args.Editor.Document.PositionToOffset(args.LogicalPosition.Line, args.LogicalPosition.Column));
ITextMarker markerWithToolTip = markersAtOffset.FirstOrDefault(marker => marker.ToolTip != null);
if (markerWithToolTip != null) {
if (toolTip == null) {
toolTip = new ToolTip();
toolTip.Closed += toolTip_Closed;
}
toolTip.Content = markerWithToolTip.ToolTip;
toolTip.IsOpen = true;
e.Handled = true;
}
ToolTipRequestService.RequestToolTip(args);
if (args.ContentToShow != null) {
@ -648,5 +658,15 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -648,5 +658,15 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
iconBarManager.UpdateClassMemberBookmarks(parseInfo);
}
public void Dispose()
{
primaryTextEditorAdapter.Language.Detach();
if (secondaryTextEditorAdapter != null)
secondaryTextEditorAdapter.Language.Detach();
errorDrawer.Dispose();
this.Document = null;
}
}
}

66
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
@ -59,6 +60,11 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -59,6 +60,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
return m;
}
public IEnumerable<ITextMarker> GetMarkersAtOffset(int offset)
{
return markers.FindSegmentsContaining(offset);
}
public IEnumerable<ITextMarker> TextMarkers {
get { return markers; }
}
@ -152,8 +158,41 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -152,8 +158,41 @@ namespace ICSharpCode.AvalonEdit.AddIn
drawingContext.DrawGeometry(brush, null, geometry);
}
}
if (marker.MarkerType != TextMarkerType.None) {
foreach (Rect r in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker)) {
Point startPoint = r.BottomLeft;
Point endPoint = r.BottomRight;
Pen usedPen = new Pen(new SolidColorBrush(marker.MarkerColor), 1);
usedPen.Freeze();
switch (marker.MarkerType) {
case TextMarkerType.SquigglyUnderline:
double offset = 2.5;
int count = (int)((endPoint.X - startPoint.X) / offset) + 1;
StreamGeometry geometry = new StreamGeometry();
using (StreamGeometryContext ctx = geometry.Open()) {
ctx.BeginFigure(startPoint, false, false);
ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false);
}
geometry.Freeze();
drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry);
break;
}
}
}
}
}
IEnumerable<Point> CreatePoints(Point start, Point end, double offset, int count)
{
for (int i = 0; i < count; i++)
yield return new Point(start.X + i * offset, start.Y - ((i + 1) % 2 == 0 ? offset : 0));
}
#endregion
}
@ -168,6 +207,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -168,6 +207,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
this.service = service;
this.StartOffset = startOffset;
this.Length = length;
this.markerType = TextMarkerType.None;
}
public event EventHandler Deleted;
@ -217,5 +257,31 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -217,5 +257,31 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
public object Tag { get; set; }
TextMarkerType markerType;
public TextMarkerType MarkerType {
get { return markerType; }
set {
if (markerType != value) {
markerType = value;
Redraw();
}
}
}
Color markerColor;
public Color MarkerColor {
get { return markerColor; }
set {
if (markerColor != value) {
markerColor = value;
Redraw();
}
}
}
public object ToolTip { get; set; }
}
}

9
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/BackgroundGeometryBuilder.cs

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
@ -42,6 +43,12 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -42,6 +43,12 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// Adds the specified segment to the geometry.
/// </summary>
public void AddSegment(TextView textView, ISegment segment)
{
foreach (Rect r in GetRectsForSegment(textView, segment))
AddRectangle(r.Left, r.Top, r.Right, r.Bottom);
}
public static IEnumerable<Rect> GetRectsForSegment(TextView textView, ISegment segment)
{
if (textView == null)
throw new ArgumentNullException("textView");
@ -89,7 +96,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -89,7 +96,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
y -= scrollOffset.Y;
left -= scrollOffset.X;
right -= scrollOffset.X;
AddRectangle(left, y, right, y + line.Height);
yield return new Rect(left, y, right - left, line.Height);
}
}
}

4
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LayerPosition.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -35,7 +35,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// <summary>
/// This layer contains the blinking caret.
/// </summary>
/// <remarks>This layer is above the Text layer.</remarks>
/// <remarks>This layer is above the Text layer. All items on this layer will blink with the same frequency as the caret.</remarks>
Caret
}
@ -92,4 +92,4 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -92,4 +92,4 @@ namespace ICSharpCode.AvalonEdit.Rendering
return this.Position.CompareTo(other.Position);
}
}
}
}

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -259,6 +259,7 @@ @@ -259,6 +259,7 @@
<Compile Include="Src\Services\Profiler\ProfilerService.cs" />
<Compile Include="Src\Services\ProjectService\CompileModifiedProjectsOnly.cs" />
<Compile Include="Src\Services\RefactoringService\ExtractInterfaceOptions.cs" />
<Compile Include="Src\Services\Tasks\ErrorDrawer.cs" />
<Compile Include="Src\Services\Tasks\Task.cs" />
<Compile Include="Src\Services\Tasks\TaskService.cs" />
<Compile Include="Src\Services\DisplayBinding\IDisplayBinding.cs" />

27
src/Main/Base/Project/Src/Editor/ITextMarker.cs

@ -56,10 +56,37 @@ namespace ICSharpCode.SharpDevelop.Editor @@ -56,10 +56,37 @@ namespace ICSharpCode.SharpDevelop.Editor
/// </summary>
Color? ForegroundColor { get; set; }
/// <summary>
/// Gets/Sets the type of the marker. Use TextMarkerType.None for normal markers.
/// </summary>
TextMarkerType MarkerType { get; set; }
/// <summary>
/// Gets/Sets the color of the marker.
/// </summary>
Color MarkerColor { get; set; }
/// <summary>
/// Gets/Sets an object with additional data for this text marker.
/// </summary>
object Tag { get; set; }
/// <summary>
/// Gets/Sets an object that will be displayed as tooltip in the text editor.
/// </summary>
object ToolTip { get; set; }
}
public enum TextMarkerType
{
/// <summary>
/// Use no marker
/// </summary>
None,
/// <summary>
/// Use squiggly underline marker
/// </summary>
SquigglyUnderline
}
public interface ITextMarkerService

151
src/Main/Base/Project/Src/Services/Tasks/ErrorDrawer.cs

@ -0,0 +1,151 @@ @@ -0,0 +1,151 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="siegfriedpammer@gmail.com"/>
// <version>$Revision$</version>
// </file>
using ICSharpCode.Core;
using System;
using System.Windows.Media;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.SharpDevelop
{
/// <summary>
/// Synchronizes the ITextEditors with the TaskService. It adds and removes error markers.
/// </summary>
public class ErrorDrawer : IDisposable
{
ITextEditor textEditor;
ITextMarkerService markerService;
public ErrorDrawer(ITextEditor textEditor)
{
this.textEditor = textEditor;
this.markerService = this.textEditor.GetService(typeof(ITextMarkerService)) as ITextMarkerService;
if (this.markerService == null)
throw new InvalidOperationException("this ITextEditor has no text marker service!");
TaskService.Added += new TaskEventHandler(OnAdded);
TaskService.Removed += new TaskEventHandler(OnRemoved);
TaskService.Cleared += new EventHandler(OnCleared);
DebuggerService.DebugStarted += OnDebugStarted;
DebuggerService.DebugStopped += OnDebugStopped;
}
bool isDisposed;
/// <summary>
/// Deregisters the event handlers so the error drawer (and associated TextEditorControl)
/// can be garbage collected.
/// </summary>
public void Dispose()
{
if (isDisposed)
return;
isDisposed = true;
TaskService.Added -= new TaskEventHandler(OnAdded);
TaskService.Removed -= new TaskEventHandler(OnRemoved);
TaskService.Cleared -= new EventHandler(OnCleared);
DebuggerService.DebugStarted -= OnDebugStarted;
DebuggerService.DebugStopped -= OnDebugStopped;
ClearErrors();
}
void OnDebugStarted(object sender, EventArgs e)
{
ClearErrors();
}
void OnDebugStopped(object sender, EventArgs e)
{
foreach (Task task in TaskService.Tasks) {
AddTask(task);
}
}
void OnAdded(object sender, TaskEventArgs e)
{
AddTask(e.Task);
}
void OnRemoved(object sender, TaskEventArgs e)
{
markerService.RemoveAll(marker => marker.Tag == e.Task);
}
void OnCleared(object sender, EventArgs e)
{
ClearErrors();
}
/// <summary>
/// Clears all TextMarkers representing errors.
/// </summary>
/// <returns>Returns true when there were markers deleted, false when there were no error markers.</returns>
void ClearErrors()
{
markerService.RemoveAll(marker => marker.Tag is Task);
}
bool CheckTask(Task task)
{
if (textEditor.FileName == null)
return false;
if (task.FileName == null || task.FileName.Length == 0 || task.Column < 0)
return false;
if (task.TaskType != TaskType.Warning && task.TaskType != TaskType.Error)
return false;
return FileUtility.IsEqualFileName(task.FileName, textEditor.FileName);
}
void AddTask(Task task)
{
if (!CheckTask(task)) return;
if (task.Line >= 0 && task.Line < textEditor.Document.TotalNumberOfLines) {
int offset = textEditor.Document.PositionToOffset(task.Line, task.Column);
int length = textEditor.Document.GetWordAt(offset).Length;
if (length < 2)
length = 2;
ITextMarker marker = this.markerService.Create(offset, length);
Color markerColor = Colors.Transparent;
switch (task.TaskType) {
case TaskType.Error:
markerColor = Colors.Red;
break;
case TaskType.Message:
markerColor = Colors.Blue;
break;
case TaskType.Warning:
markerColor = Colors.Yellow;
break;
}
marker.MarkerColor = markerColor;
marker.MarkerType = TextMarkerType.SquigglyUnderline;
marker.ToolTip = task.Description;
marker.Tag = task;
}
}
/// <summary>
/// Clears all errors and adds them again.
/// </summary>
public void UpdateErrors()
{
ClearErrors();
foreach (Task task in TaskService.Tasks) {
AddTask(task);
}
}
}
}
Loading…
Cancel
Save