Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@1394 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
8 changed files with 290 additions and 133 deletions
@ -0,0 +1,205 @@
@@ -0,0 +1,205 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Drawing; |
||||
using System.IO; |
||||
using System.Xml; |
||||
using System.Windows.Forms; |
||||
using ICSharpCode.TextEditor; |
||||
using ICSharpCode.TextEditor.Document; |
||||
using ICSharpCode.SharpDevelop.Gui; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// Interface for advanced syntax highlighters.
|
||||
/// </summary>
|
||||
public interface IAdvancedHighlighter : IDisposable |
||||
{ |
||||
/// <summary>
|
||||
/// Is called once after creating the highlighter. Gives your highlighter a chance
|
||||
/// to register events on the text editor.
|
||||
/// </summary>
|
||||
void Initialize(TextEditorControl textEditor); |
||||
|
||||
void BeginUpdate(IDocument document, IList<LineSegment> inputLines); |
||||
void EndUpdate(); |
||||
|
||||
/// <summary>
|
||||
/// Gives your highlighter the chance to change the highlighting of the words.
|
||||
/// </summary>
|
||||
void MarkLine(int lineNumber, LineSegment currentLine, List<TextWord> words); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Advanced syntax highlighter that stores a list of changed lines and can mark them
|
||||
/// later by calling <see cref="MarkOutstanding"/>.
|
||||
/// </summary>
|
||||
public abstract class AsynchronousAdvancedHighlighter : IAdvancedHighlighter |
||||
{ |
||||
protected abstract void MarkWords(int lineNumber, LineSegment currentLine, List<TextWord> words); |
||||
|
||||
readonly object lockObject = new object(); |
||||
Dictionary<LineSegment, List<TextWord>> outstanding = new Dictionary<LineSegment, List<TextWord>>(); |
||||
TextEditorControl textEditor; |
||||
IDocument document; |
||||
|
||||
#region Settings
|
||||
public TextEditorControl TextEditor { |
||||
get { |
||||
return textEditor; |
||||
} |
||||
} |
||||
|
||||
public IDocument Document { |
||||
get { |
||||
return document; |
||||
} |
||||
} |
||||
|
||||
int immediateMarkLimit = 3; |
||||
|
||||
/// <summary>
|
||||
/// Maximum number of changed lines to immediately mark the changes.
|
||||
/// </summary>
|
||||
protected int ImmediateMarkLimit { |
||||
get { |
||||
return immediateMarkLimit; |
||||
} |
||||
set { |
||||
if (value < 0) throw new ArgumentOutOfRangeException("value", value, "value must be >= 0"); |
||||
immediateMarkLimit = value; |
||||
} |
||||
} |
||||
|
||||
bool markVisibleOnly = true; |
||||
/// <summary>
|
||||
/// Gets/Sets whether to only mark lines in the visible region of the text area.
|
||||
/// </summary>
|
||||
protected bool MarkVisibleOnly { |
||||
get { |
||||
return markVisibleOnly; |
||||
} |
||||
set { |
||||
if (markVisibleOnly != value) { |
||||
if (textEditor != null) |
||||
throw new InvalidOperationException("Cannot change value after initialization"); |
||||
markVisibleOnly = value; |
||||
} |
||||
} |
||||
} |
||||
int markVisibleAdditional = 5; |
||||
/// <summary>
|
||||
/// Number of additional lines around the visible region that should be marked.
|
||||
/// </summary>
|
||||
public int MarkVisibleAdditional { |
||||
get { |
||||
return markVisibleAdditional; |
||||
} |
||||
set { |
||||
if (value < 0) throw new ArgumentOutOfRangeException("value", value, "value must be >= 0"); |
||||
markVisibleAdditional = value; |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
public virtual void Initialize(TextEditorControl textEditor) |
||||
{ |
||||
if (textEditor == null) |
||||
throw new ArgumentNullException("textEditor"); |
||||
if (this.textEditor != null) |
||||
throw new InvalidOperationException("Already initialized"); |
||||
this.textEditor = textEditor; |
||||
this.document = textEditor.Document; |
||||
} |
||||
|
||||
public virtual void Dispose() |
||||
{ |
||||
textEditor = null; |
||||
document = null; |
||||
} |
||||
|
||||
int directMark; |
||||
|
||||
public virtual void BeginUpdate(IDocument document, IList<LineSegment> inputLines) |
||||
{ |
||||
if (this.document == null) |
||||
throw new InvalidOperationException("Not initialized"); |
||||
if (document != this.document) |
||||
throw new InvalidOperationException("document != this.document"); |
||||
if (inputLines == null) { |
||||
lock (lockObject) { |
||||
outstanding.Clear(); |
||||
} |
||||
} else { |
||||
directMark = inputLines.Count > immediateMarkLimit ? 0 : inputLines.Count; |
||||
} |
||||
} |
||||
|
||||
public virtual void EndUpdate() |
||||
{ |
||||
} |
||||
|
||||
void IAdvancedHighlighter.MarkLine(int lineNumber, LineSegment currentLine, List<TextWord> words) |
||||
{ |
||||
if (directMark > 0) { |
||||
directMark--; |
||||
MarkWords(lineNumber, currentLine, words); |
||||
} else { |
||||
lock (lockObject) { |
||||
outstanding[currentLine] = words; |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected virtual void MarkOutstanding() |
||||
{ |
||||
if (WorkbenchSingleton.InvokeRequired) { |
||||
// TODO: allow calling MarkOutstanding in separate threads
|
||||
throw new InvalidOperationException("Invoke required"); |
||||
} |
||||
|
||||
IEnumerable<KeyValuePair<LineSegment, List<TextWord>>> oldOutstanding; |
||||
lock (lockObject) { |
||||
oldOutstanding = outstanding; |
||||
outstanding = new Dictionary<LineSegment, List<TextWord>>(); |
||||
} |
||||
// We cannot call MarkLine inside lock(lockObject) because then the main
|
||||
// thread could deadlock with the highlighter thread.
|
||||
foreach (KeyValuePair<LineSegment, List<TextWord>> pair in oldOutstanding) { |
||||
int offset = pair.Key.Offset; |
||||
if (offset < 0 || offset >= document.TextLength) |
||||
continue; |
||||
int lineNumber = document.GetLineNumberForOffset(offset); |
||||
if (markVisibleOnly && IsVisible(lineNumber) == false) { |
||||
lock (lockObject) { |
||||
outstanding[pair.Key] = pair.Value; |
||||
} |
||||
} else { |
||||
MarkWords(lineNumber, pair.Key, pair.Value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool IsVisible(int lineNumber) |
||||
{ |
||||
TextView textView = textEditor.ActiveTextAreaControl.TextArea.TextView; |
||||
int firstLine = textView.FirstVisibleLine; |
||||
if (lineNumber < firstLine - markVisibleAdditional) { |
||||
return false; |
||||
} |
||||
int lastLine = document.GetFirstLogicalLine(textView.FirstPhysicalLine + textView.VisibleLineCount); |
||||
if (lineNumber > lastLine + markVisibleAdditional) { |
||||
return false; |
||||
} |
||||
// line is visible if it is not folded away
|
||||
return document.GetVisibleLine(lineNumber) != document.GetVisibleLine(lineNumber - 1); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Drawing; |
||||
using System.IO; |
||||
using System.Xml; |
||||
using System.Windows.Forms; |
||||
using ICSharpCode.TextEditor; |
||||
using ICSharpCode.TextEditor.Document; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// Modifies the TextEditor's IHighlightingStrategy to be able to plug in
|
||||
/// an <see cref="IAdvancedHighlighter"/>.
|
||||
/// </summary>
|
||||
internal class AdvancedHighlightingStrategy : DefaultHighlightingStrategy |
||||
{ |
||||
readonly IAdvancedHighlighter highlighter; |
||||
|
||||
public AdvancedHighlightingStrategy(DefaultHighlightingStrategy baseStrategy, IAdvancedHighlighter highlighter) |
||||
{ |
||||
if (highlighter == null) |
||||
throw new ArgumentNullException("highlighter"); |
||||
ImportSettingsFrom(baseStrategy); |
||||
this.highlighter = highlighter; |
||||
} |
||||
|
||||
public override void MarkTokens(IDocument document) |
||||
{ |
||||
highlighter.BeginUpdate(document, null); |
||||
base.MarkTokens(document); |
||||
highlighter.EndUpdate(); |
||||
} |
||||
|
||||
public override void MarkTokens(IDocument document, List<LineSegment> inputLines) |
||||
{ |
||||
highlighter.BeginUpdate(document, inputLines); |
||||
base.MarkTokens(document, inputLines); |
||||
highlighter.EndUpdate(); |
||||
} |
||||
|
||||
protected override void OnParsedLine(IDocument document, LineSegment currentLine, List<TextWord> words) |
||||
{ |
||||
highlighter.MarkLine(currentLineNumber, currentLine, words); |
||||
} |
||||
} |
||||
} |
@ -1,101 +0,0 @@
@@ -1,101 +0,0 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Drawing; |
||||
using System.IO; |
||||
using System.Xml; |
||||
using System.Windows.Forms; |
||||
using ICSharpCode.TextEditor; |
||||
using ICSharpCode.TextEditor.Document; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor |
||||
{ |
||||
public interface IAdvancedHighlighter : IDisposable |
||||
{ |
||||
/// <summary>
|
||||
/// Is called once after creating the highlighter. Gives your highlighter a chance
|
||||
/// to register events on the text editor.
|
||||
/// </summary>
|
||||
void Initialize(TextEditorControl textEditor); |
||||
|
||||
/// <summary>
|
||||
/// Gives your highlighter the chance to change the highlighting of the words.
|
||||
/// </summary>
|
||||
void MarkLine(IDocument document, LineSegment currentLine, List<TextWord> words); |
||||
|
||||
/// <summary>
|
||||
/// When your class fires this event, MarkLine will be called for all waiting lines.
|
||||
/// You can fire this event on any thread, but MarkLine will be called on that thread
|
||||
/// - so if you use multithreading, you must invoke when accessing the document inside
|
||||
/// MarkLine.
|
||||
/// </summary>
|
||||
event EventHandler MarkOutstandingRequests; |
||||
} |
||||
|
||||
internal class ParserHighlightingStrategy : DefaultHighlightingStrategy |
||||
{ |
||||
readonly object lockObject = new object(); |
||||
readonly IAdvancedHighlighter highlighter; |
||||
IDocument document; |
||||
Dictionary<LineSegment, List<TextWord>> outstanding = new Dictionary<LineSegment, List<TextWord>>(); |
||||
|
||||
public ParserHighlightingStrategy(DefaultHighlightingStrategy baseStrategy, IAdvancedHighlighter highlighter) |
||||
{ |
||||
ImportSettingsFrom(baseStrategy); |
||||
this.highlighter = highlighter; |
||||
highlighter.MarkOutstandingRequests += OnMarkOutstandingRequest; |
||||
} |
||||
|
||||
int directMark; // counter for immediate marking when only few lines have changed
|
||||
|
||||
public override void MarkTokens(IDocument document) |
||||
{ |
||||
lock (lockObject) { |
||||
outstanding.Clear(); |
||||
} |
||||
base.MarkTokens(document); |
||||
} |
||||
|
||||
public override void MarkTokens(IDocument document, List<LineSegment> inputLines) |
||||
{ |
||||
directMark = (inputLines.Count < 3) ? inputLines.Count : 0; |
||||
base.MarkTokens(document, inputLines); |
||||
directMark = 0; |
||||
} |
||||
|
||||
protected override void OnParsedLine(IDocument document, LineSegment currentLine, List<TextWord> words) |
||||
{ |
||||
int ln = currentLineNumber; |
||||
|
||||
if (directMark > 0) { |
||||
directMark--; |
||||
highlighter.MarkLine(document, currentLine, words); |
||||
} else { |
||||
this.document = document; |
||||
lock (lockObject) { |
||||
outstanding[currentLine] = words; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void OnMarkOutstandingRequest(object sender, EventArgs e) |
||||
{ |
||||
Dictionary<LineSegment, List<TextWord>> oldOutstanding; |
||||
lock (lockObject) { |
||||
oldOutstanding = outstanding; |
||||
outstanding = new Dictionary<LineSegment, List<TextWord>>(); |
||||
} |
||||
// We cannot call MarkLine inside lock(lockObject) because then the main
|
||||
// thread could deadlock with the highlighter thread.
|
||||
foreach (KeyValuePair<LineSegment, List<TextWord>> pair in oldOutstanding) { |
||||
highlighter.MarkLine(document, pair.Key, pair.Value); |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue