diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/ExtensionsTests.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/ExtensionsTests.cs new file mode 100644 index 0000000000..d960ace613 --- /dev/null +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/ExtensionsTests.cs @@ -0,0 +1,73 @@ +// +// +// +// +// $Revision$ +// + + +using System; +using NUnit.Framework; + +namespace ICSharpCode.XamlBinding.Tests +{ + [TestFixture] + public class ExtensionsTests + { + [Test] + public void StringReplaceTest1() + { + string text = "Hello World!"; + int index = 0; + int length = 5; + string newText = "Bye"; + + string result = text.Replace(index, length, newText); + string expected = "Bye World!"; + + Assert.AreEqual(expected, result); + } + + [Test] + public void StringReplaceTest2() + { + string text = "My Hello World!"; + int index = 3; + int length = 5; + string newText = "Bye"; + + string result = text.Replace(index, length, newText); + string expected = "My Bye World!"; + + Assert.AreEqual(expected, result); + } + + [Test] + public void StringReplaceTest3() + { + string text = "Hello World!"; + int index = 6; + int length = 5; + string newText = "Byte"; + + string result = text.Replace(index, length, newText); + string expected = "Hello Byte!"; + + Assert.AreEqual(expected, result); + } + + [Test] + public void StringReplaceTest4() + { + string text = "Hello World!"; + int index = 11; + int length = 1; + string newText = "?"; + + string result = text.Replace(index, length, newText); + string expected = "Hello World?"; + + Assert.AreEqual(expected, result); + } + } +} diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/ResolveContextTests.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/ResolveContextTests.cs new file mode 100644 index 0000000000..c4772876ef --- /dev/null +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/ResolveContextTests.cs @@ -0,0 +1,71 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.XmlEditor; +using System; +using NUnit.Framework; + +namespace ICSharpCode.XamlBinding.Tests +{ + [TestFixture] + public class ResolveContextTests + { + [Test] + public void ContextNoneDescriptionTest() + { + string xaml = "\n\t\n"; + XamlContext context = CompletionDataHelper.ResolveContext(xaml, "", 2, 1); + + Assert.AreEqual(XamlContextDescription.None, context.Description); + } + + [Test] + public void ContextNoneDescriptionTest2() + { + string xaml = "\n\t\n"; + XamlContext context = CompletionDataHelper.ResolveContext(xaml, "", 1, 7); + + Assert.AreEqual(XamlContextDescription.None, context.Description); + } + + [Test] + public void ContextNoneDescriptionTest3() + { + string xaml = "\n\t\n"; + XamlContext context = CompletionDataHelper.ResolveContext(xaml, "", 3, 1); + + Assert.AreEqual(XamlContextDescription.None, context.Description); + } + + [Test] + public void ContextAtTagDescriptionTest() + { + string xaml = "\n\t\n"; + XamlContext context = CompletionDataHelper.ResolveContext(xaml, "", 1, 2); + + Assert.AreEqual(XamlContextDescription.AtTag, context.Description); + } + + [Test] + public void ContextAtTagDescriptionTest2() + { + string xaml = "\n\t\n"; + XamlContext context = CompletionDataHelper.ResolveContext(xaml, "", 2, 11); + + Assert.AreEqual(XamlContextDescription.AtTag, context.Description); + } + + [Test] + public void ContextInTagDescriptionTest() + { + string xaml = "\n\t\n"; + XamlContext context = CompletionDataHelper.ResolveContext(xaml, "", 2, 26); + + Assert.AreEqual(XamlContextDescription.InTag, context.Description); + } + } +} \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XamlBinding.Tests.csproj b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XamlBinding.Tests.csproj index f23aade326..2b64693727 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XamlBinding.Tests.csproj +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XamlBinding.Tests.csproj @@ -53,8 +53,10 @@ Properties\GlobalAssemblyInfo.cs + + @@ -63,6 +65,7 @@ {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} ICSharpCode.SharpDevelop.Dom + False {DCA2703D-250A-463E-A68A-07ED105AE6BD} diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs index 1ed209f9d6..ccc364a196 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs @@ -6,8 +6,11 @@ // using System; -using System.Linq; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.SharpDevelop; @@ -15,24 +18,135 @@ using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.XmlEditor; -using System.Diagnostics; +using System.Windows.Threading; namespace ICSharpCode.XamlBinding { + using Tasks = System.Threading.Tasks; + public class XamlColorizer : DocumentColorizingTransformer { static readonly XamlColorizerSettings defaultSettings = new XamlColorizerSettings(); + + public struct Highlight { + public IMember Member { get; set; } + public HighlightingInfo Info { get; set; } + } + + public sealed class HighlightTask { + // input + public string FileContent { get; private set; } + public string FileName { get; private set; } + public string LineText { get; private set; } + public int LineNumber { get; private set; } + public int Offset { get; private set; } + + TextView textView; + + public HighlightTask(string fileContent, string fileName, DocumentLine line, TextView textView) + { + this.FileContent = fileContent; + this.FileName = fileName; + this.LineText = line.Text; + this.LineNumber = line.LineNumber; + this.Offset = line.Offset; + this.task = new System.Threading.Tasks.Task(Process); + this.textView = textView; + } + + IList results; + + // output + public IList GetResults() + { + return results; + } + + public bool IsStillValid(DocumentLine line) + { + return this.Offset == line.Offset && this.LineText == line.Text; + } + + Tasks.Task task; + + public void Start() + { + task.Start(); + } + + public void Cancel() + { + if (task != null) + task.Cancel(); + } + + public bool IsCompleted { + get { + return task.IsCompleted; + } + } + + void Process() + { + List results = new List(); + + foreach (HighlightingInfo info in GetInfo()) { + IMember member = null; + + if (!info.Token.StartsWith("xmlns")) { + MemberResolveResult rr = new XamlResolver().Resolve(info.GetExpressionResult(), info.Context.ParseInformation, FileContent) as MemberResolveResult; + member = (rr != null) ? rr.ResolvedMember : null; + } + + results.Add(new Highlight() { Member = member, Info = info }); + } + + this.results = results; + + WorkbenchSingleton.SafeThreadAsyncCall(InvokeRedraw); + } + + void InvokeRedraw() + { + textView.Redraw(this.Offset, this.LineText.Length, DispatcherPriority.Background); + } + + IEnumerable GetInfo() + { + int index = -1; + int ltCharIndex = -1; + XamlContext context = null; + List infos = new List(); + + do { + index = LineText.IndexOf('=', index + 1); + if (index > -1) { + context = CompletionDataHelper.ResolveContext(FileContent, FileName, LineNumber, index + 1); + if (!string.IsNullOrEmpty(context.AttributeName)) { + int startIndex = LineText.Substring(0, index).LastIndexOf(context.AttributeName); + infos.Add(new HighlightingInfo(context.AttributeName, startIndex, startIndex + context.AttributeName.Length, Offset, context)); + } + } + } while (index > -1); + + return infos; + } + } + XamlColorizerSettings settings = defaultSettings; string fileContent; string fileName; - ParseInformation parseInfo; - XamlResolver resolver = new XamlResolver(); + + Dictionary highlightCache = new Dictionary(); public IViewContent Content { get; set; } - public XamlColorizer(IViewContent content) + public AvalonEdit.Rendering.TextView TextView { get; set; } + + public XamlColorizer(IViewContent content, TextView textView) { this.Content = content; + this.TextView = textView; } protected override void Colorize(ITextRunConstructionContext context) @@ -42,7 +156,6 @@ namespace ICSharpCode.XamlBinding if (document == null) return; - this.parseInfo = ParserService.GetParseInformation(Content.PrimaryFileName); this.fileContent = document.GetDocumentForFile(this.Content.PrimaryFile).CreateSnapshot().Text; this.fileName = this.Content.PrimaryFileName; @@ -51,45 +164,37 @@ namespace ICSharpCode.XamlBinding protected override void ColorizeLine(DocumentLine line) { - if (!line.IsDeleted) { - HighlightingInfo[] infos = GetInfoForLine(line); - - foreach (HighlightingInfo info in infos) { - MemberResolveResult rr = resolver.Resolve(info.GetExpressionResult(), parseInfo, fileContent) as MemberResolveResult; - IMember member = (rr != null) ? rr.ResolvedMember : null; - - if (member != null) { - if (member is IEvent) - ChangeLinePart(line.Offset + info.StartOffset, line.Offset + info.EndOffset, HighlightEvent); - else - ChangeLinePart(line.Offset + info.StartOffset, line.Offset + info.EndOffset, HighlightProperty); - } else { - if (info.Token.StartsWith("xmlns")) - ChangeLinePart(line.Offset + info.StartOffset, line.Offset + info.EndOffset, HighlightNamespaceDeclaration); - } + if (line.IsDeleted) + return; + + if (!highlightCache.ContainsKey(line.LineNumber)) { + HighlightTask task = new HighlightTask(this.fileContent, this.fileName, line, this.TextView); + task.Start(); + highlightCache.Add(line.LineNumber, task); + } else { + HighlightTask task = highlightCache[line.LineNumber]; + if (task.IsCompleted && task.IsStillValid(line)) { + task.GetResults().ForEach(result => ColorizeMember(result.Info, line, result.Member)); + } else { + task.Cancel(); + task = new HighlightTask(this.fileContent, this.fileName, line, this.TextView); + task.Start(); + highlightCache[line.LineNumber] = task; } } } - HighlightingInfo[] GetInfoForLine(DocumentLine line) + void ColorizeMember(HighlightingInfo info, DocumentLine line, IMember member) { - int index = -1; - int ltCharIndex = -1; - XamlContext context = null; - List infos = new List(); - - do { - index = line.Text.IndexOf('=', index + 1); - if (index > -1) { - context = CompletionDataHelper.ResolveContext(this.fileContent, this.fileName, line.LineNumber, index + 1); - if (!string.IsNullOrEmpty(context.AttributeName)) { - int startIndex = line.Text.Substring(0, index).LastIndexOf(context.AttributeName); - infos.Add(new HighlightingInfo(context.AttributeName, startIndex, startIndex + context.AttributeName.Length, line.Offset, context)); - } - } - } while (index > -1); - - return infos.ToArray(); + if (member != null) { + if (member is IEvent) + ChangeLinePart(line.Offset + info.StartOffset, line.Offset + info.EndOffset, HighlightEvent); + else + ChangeLinePart(line.Offset + info.StartOffset, line.Offset + info.EndOffset, HighlightProperty); + } else { + if (info.Token.StartsWith("xmlns")) + ChangeLinePart(line.Offset + info.StartOffset, line.Offset + info.EndOffset, HighlightNamespaceDeclaration); + } } void HighlightProperty(VisualLineElement element) @@ -110,7 +215,7 @@ namespace ICSharpCode.XamlBinding element.TextRunProperties.SetBackgroundBrush(settings.NamespaceDeclarationBackgroundBrush); } - struct HighlightingInfo + public struct HighlightingInfo { public static readonly HighlightingInfo Empty = new HighlightingInfo(string.Empty, 0, 0, 0, new XamlContext()); @@ -154,4 +259,4 @@ namespace ICSharpCode.XamlBinding } } } -} +} \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizerServer.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizerServer.cs index 4cb6b4a8b0..e2c3c12591 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizerServer.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizerServer.cs @@ -5,6 +5,7 @@ // $Revision$ // +using ICSharpCode.XmlEditor; using System; using System.IO; using ICSharpCode.AvalonEdit.Rendering; @@ -32,9 +33,10 @@ namespace ICSharpCode.XamlBinding ITextEditorProvider textEditor = e.Content as ITextEditorProvider; if (textEditor != null) { TextView textView = textEditor.TextEditor.GetService(typeof(TextView)) as TextView; + if (textView != null) - textView.LineTransformers.Add(new XamlColorizer(e.Content)); + textView.LineTransformers.Add(new XamlColorizer(e.Content, textView)); } } } -} +} \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs index e2172fe9fd..9428c59940 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs @@ -235,7 +235,7 @@ namespace ICSharpCode.XamlBinding { if (propertyOrEvent == null) return null; - if (propertyOrEvent is IEvent) { + if (propertyOrEvent is IEvent && callingClass != null) { return new MethodGroupResolveResult(callingClass, null, callingClass.DefaultReturnType, expression); }