diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin index 6495ff9251..c2c90356af 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin @@ -65,11 +65,9 @@ extensions=".cs" /> - True ..\..\..\..\..\AddIns\BackendBindings\CSharpBinding\ + + DEBUG + @@ -62,6 +65,7 @@ + @@ -80,6 +84,7 @@ Configuration\GlobalAssemblyInfo.cs + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Resources/CSharp-Semantic.xshd b/src/AddIns/BackendBindings/CSharpBinding/Project/Resources/CSharp-Semantic.xshd new file mode 100644 index 0000000000..ca2c3f407a --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Resources/CSharp-Semantic.xshd @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TODO + FIXME + + + HACK + UNDONE + + + + + + + \# + + + + (define|undef|if|elif|else|endif|line)\b + + + + // + + + + + + (region|endregion|error|warning|pragma)\b + + + + + + + /// + + + + + + + + // + + + + /\* + \*/ + + + + " + " + + + + + + + + ' + ' + + + + + + + + @" + " + + + + + + + + + @[\w\d_]+ + + + + this + base + + + + as + is + new + sizeof + typeof + stackalloc + + + + true + false + + + + else + if + switch + case + default + do + for + foreach + in + while + lock + + + + break + continue + goto + return + + + + yield + partial + global + where + select + group + by + into + from + ascending + descending + orderby + let + join + on + equals + var + dynamic + await + + + + try + throw + catch + finally + + + + checked + unchecked + + + + fixed + unsafe + + + + bool + byte + char + decimal + double + enum + float + int + long + sbyte + short + struct + uint + ushort + ulong + + + + class + interface + delegate + object + string + void + + + + explicit + implicit + operator + + + + params + ref + out + + + + abstract + const + event + extern + override + readonly + sealed + static + virtual + volatile + async + + + + public + protected + private + internal + + + + namespace + using + + + + get + set + add + remove + + + + null + + + + + + \b0[xX][0-9a-fA-F]+ # hex number + | + ( \b\d+(\.[0-9]+)? #number with optional floating point + | \.[0-9]+ #or just starting with floating point + ) + ([eE][+-]?[0-9]+)? # optional exponent + + + + [?,.;()\[\]{}+\-/%*<>^+~!|&]+ + + + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs index 98b74f8093..7bf746c3dc 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs @@ -20,14 +20,34 @@ namespace CSharpBinding // public override LanguageProperties Properties { // get { return LanguageProperties.CSharp; } // } -// +// public override IBracketSearcher BracketSearcher { get { return new CSharpBracketSearcher(); } } + ITextEditor editor; + CSharpSemanticHighlighter semanticHighlighter; + public override void Attach(ITextEditor editor) { - //CSharpBackgroundCompiler.Init(); + base.Attach(editor); + this.editor = editor; + ISyntaxHighlighter highlighter = editor.GetService(typeof(ISyntaxHighlighter)) as ISyntaxHighlighter; + if (highlighter != null) { + semanticHighlighter = new CSharpSemanticHighlighter(editor, highlighter.HighlightingDefinition); + highlighter.AddAdditionalHighlighter(semanticHighlighter); + } + } + + public override void Detach() + { + ISyntaxHighlighter highlighter = editor.GetService(typeof(ISyntaxHighlighter)) as ISyntaxHighlighter; + if (highlighter != null) { + highlighter.RemoveAdditionalHighlighter(semanticHighlighter); + semanticHighlighter = null; + } + this.editor = null; + base.Detach(); } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs new file mode 100644 index 0000000000..5cbf975504 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs @@ -0,0 +1,261 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Parser; + +namespace CSharpBinding +{ + /// + /// Semantic highlighting for C#. + /// + public class CSharpSemanticHighlighter : DepthFirstAstVisitor, IHighlighter, IResolveVisitorNavigator + { + readonly ITextEditor textEditor; + readonly HighlightingColor typeReferenceColor; + readonly HighlightingColor methodCallColor; + readonly HighlightingColor fieldAccessColor; + readonly HighlightingColor valueKeywordColor; + + int lineNumber; + HighlightedLine line; + ResolveVisitor resolveVisitor; + + bool isInAccessor; + + public CSharpSemanticHighlighter(ITextEditor textEditor, IHighlightingDefinition highlightingDefinition) + { + if (textEditor == null) + throw new ArgumentNullException("textEditor"); + if (highlightingDefinition == null) + throw new ArgumentNullException("highlightingDefinition"); + this.textEditor = textEditor; + this.typeReferenceColor = highlightingDefinition.GetNamedColor("TypeReferences"); + this.methodCallColor = highlightingDefinition.GetNamedColor("MethodCall"); + this.fieldAccessColor = highlightingDefinition.GetNamedColor("FieldAccess"); + this.valueKeywordColor = highlightingDefinition.GetNamedColor("NullOrValueKeywords"); + } + + public IDocument Document { + get { return textEditor.Document; } + } + + IEnumerable IHighlighter.GetColorStack(int lineNumber) + { + return null; + } + + public HighlightedLine HighlightLine(int lineNumber) + { + Task parseInfoTask = ParserService.ParseAsync(textEditor.FileName, textEditor.Document); + if (!parseInfoTask.IsCompleted) { + Debug.WriteLine("Semantic highlighting for line {0} - parser not completed", lineNumber); + return null; + } + ParseInformation parseInfo = parseInfoTask.Result; + CSharpParsedFile parsedFile = parseInfo.ParsedFile as CSharpParsedFile; + CompilationUnit cu = parseInfo.Annotation(); + if (cu == null || parsedFile == null) + return null; + + using (var ctx = ParserService.GetTypeResolveContext(parseInfo.ProjectContent).Synchronize()) { + CSharpResolver resolver = new CSharpResolver(ctx); + resolveVisitor = new ResolveVisitor(resolver, parsedFile, this); + + resolveVisitor.Scan(cu); + + HighlightedLine line = new HighlightedLine(textEditor.Document, textEditor.Document.GetLineByNumber(lineNumber)); + this.line = line; + this.lineNumber = lineNumber; + cu.AcceptVisitor(this); + this.line = null; + this.resolveVisitor = null; + Debug.WriteLine("Semantic highlighting for line {0} - added {1} sections", lineNumber, line.Sections.Count); + return line; + } + } + + HighlightingColor GetColor(ResolveResult rr) + { + if (rr is TypeResolveResult) + return typeReferenceColor; + MemberResolveResult mrr = rr as MemberResolveResult; + if (mrr != null) { + if (mrr.Member is IField) + return fieldAccessColor; + } + return null; + } + + void Colorize(AstNode node, HighlightingColor color) + { + if (node.IsNull || color == null) + return; + Colorize(node.StartLocation, node.EndLocation, color); + } + + void Colorize(TextLocation start, TextLocation end, HighlightingColor color) + { + if (color == null) + return; + if (start.Line == lineNumber && end.Line == lineNumber) { + int lineStartOffset = line.DocumentLine.Offset; + int startOffset = lineStartOffset + start.Column - 1; + int endOffset = lineStartOffset + end.Column - 1; + line.Sections.Add(new HighlightedSection { + Offset = startOffset, + Length = endOffset - startOffset, + Color = color + }); + } + } + + ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node) + { + if (node.StartLocation.Line <= lineNumber && node.EndLocation.Line >= lineNumber) { + if (node is SimpleType || node is MemberType + || node is IdentifierExpression || node is MemberReferenceExpression + || node is InvocationExpression) + { + return ResolveVisitorNavigationMode.Resolve; + } else { + return ResolveVisitorNavigationMode.Scan; + } + } else { + return ResolveVisitorNavigationMode.Skip; + } + } + + void IResolveVisitorNavigator.Resolved(AstNode node, ResolveResult result) + { + } + + void IResolveVisitorNavigator.ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) + { + } + + protected override object VisitChildren(AstNode node, object data) + { + for (var child = node.FirstChild; child != null; child = child.NextSibling) { + if (child.StartLocation.Line <= lineNumber && child.EndLocation.Line >= lineNumber) + child.AcceptVisitor(this); + } + return null; + } + + public override object VisitSimpleType(SimpleType simpleType, object data) + { + if (resolveVisitor.GetResolveResult(simpleType) is TypeResolveResult) + Colorize(simpleType.IdentifierToken, typeReferenceColor); + foreach (AstNode node in simpleType.TypeArguments) + node.AcceptVisitor(this); + return null; + } + + public override object VisitMemberType(MemberType memberType, object data) + { + // Ensure we visit/colorize the children in the correct order. + // This is required so that the resulting HighlightedSections are sorted correctly. + memberType.Target.AcceptVisitor(this); + if (resolveVisitor.GetResolveResult(memberType) is TypeResolveResult) + Colorize(memberType.MemberNameToken, typeReferenceColor); + foreach (AstNode node in memberType.TypeArguments) + node.AcceptVisitor(this); + return null; + } + + public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) + { + Identifier ident = identifierExpression.GetChildByRole(IdentifierExpression.Roles.Identifier); + if (isInAccessor && identifierExpression.Identifier == "value") { + Colorize(ident, valueKeywordColor); + } else { + ResolveResult rr = resolveVisitor.GetResolveResult(identifierExpression); + Colorize(ident, GetColor(rr)); + } + + foreach (AstNode node in identifierExpression.TypeArguments) + node.AcceptVisitor(this); + return null; + } + + public override object VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) + { + memberReferenceExpression.Target.AcceptVisitor(this); + + ResolveResult rr = resolveVisitor.GetResolveResult(memberReferenceExpression); + Colorize(memberReferenceExpression.MemberNameToken, GetColor(rr)); + + foreach (AstNode node in memberReferenceExpression.TypeArguments) + node.AcceptVisitor(this); + return null; + } + + public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) + { + Expression target = invocationExpression.Target; + target.AcceptVisitor(this); + + var rr = resolveVisitor.GetResolveResult(invocationExpression) as CSharpInvocationResolveResult; + if (rr != null && !rr.IsDelegateInvocation) { + if (target is IdentifierExpression || target is MemberReferenceExpression || target is PointerReferenceExpression) { + Colorize(target.GetChildByRole(AstNode.Roles.Identifier), methodCallColor); + } + } + + foreach (AstNode node in invocationExpression.Arguments) + node.AcceptVisitor(this); + return null; + } + + public override object VisitAccessor(Accessor accessor, object data) + { + isInAccessor = true; + try { + return base.VisitAccessor(accessor, data); + } finally { + isInAccessor = false; + } + } + + public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) + { + methodDeclaration.ReturnType.AcceptVisitor(this); + Colorize(methodDeclaration.NameToken, methodCallColor); + foreach (var node in methodDeclaration.TypeParameters) + node.AcceptVisitor(this); + foreach (var node in methodDeclaration.Parameters) + node.AcceptVisitor(this); + foreach (var node in methodDeclaration.Constraints) + node.AcceptVisitor(this); + methodDeclaration.Body.AcceptVisitor(this); + return null; + } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + Colorize(typeDeclaration, typeReferenceColor); + foreach (var node in typeDeclaration.TypeParameters) + node.AcceptVisitor(this); + foreach (var node in typeDeclaration.BaseTypes) + node.AcceptVisitor(this); + foreach (var node in typeDeclaration.Constraints) + node.AcceptVisitor(this); + foreach (var node in typeDeclaration.Members) + node.AcceptVisitor(this); + return null; + } + } +} diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index 36f10866fa..fcce6b8df2 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -212,8 +212,6 @@ namespace ICSharpCode.AvalonEdit.AddIn codeEditorView.TextArea.LeftMargins.Add(new ChangeMarkerMargin(changeWatcher)); } - textView.Services.AddService(typeof(ISyntaxHighlighter), new AvalonEditSyntaxHighlighterAdapter(textView)); - codeEditorView.TextArea.MouseRightButtonDown += TextAreaMouseRightButtonDown; codeEditorView.TextArea.ContextMenuOpening += TextAreaContextMenuOpening; codeEditorView.TextArea.TextCopied += textEditor_TextArea_TextCopied; diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs index 8daeadb8c0..1bd06dc36b 100755 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs @@ -515,7 +515,7 @@ namespace ICSharpCode.AvalonEdit.AddIn protected override IVisualLineTransformer CreateColorizer(IHighlightingDefinition highlightingDefinition) { return new CustomizableHighlightingColorizer( - highlightingDefinition.MainRuleSet, + highlightingDefinition, FetchCustomizations(highlightingDefinition.Name)); } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CustomizableHighlightingColorizer.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CustomizableHighlightingColorizer.cs index e8ab5e3a2b..b9c7616a9a 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CustomizableHighlightingColorizer.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CustomizableHighlightingColorizer.cs @@ -14,6 +14,7 @@ using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.NRefactory.Editor; +using ICSharpCode.SharpDevelop.Editor; namespace ICSharpCode.AvalonEdit.AddIn { @@ -22,6 +23,7 @@ namespace ICSharpCode.AvalonEdit.AddIn /// public class CustomizableHighlightingColorizer : HighlightingColorizer { + #region ApplyCustomizationsToDefaultElements public const string DefaultTextAndBackground = "Default text/background"; public const string SelectedText = "Selected text"; public const string NonPrintableCharacters = "Non-printable characters"; @@ -93,31 +95,47 @@ namespace ICSharpCode.AvalonEdit.AddIn } } } + #endregion + readonly IHighlightingDefinition highlightingDefinition; readonly IEnumerable customizations; - public CustomizableHighlightingColorizer(HighlightingRuleSet ruleSet, IEnumerable customizations) - : base(ruleSet) + public CustomizableHighlightingColorizer(IHighlightingDefinition highlightingDefinition, IEnumerable customizations) + : base(highlightingDefinition.MainRuleSet) { if (customizations == null) throw new ArgumentNullException("customizations"); + this.highlightingDefinition = highlightingDefinition; this.customizations = customizations; } + protected override void OnDocumentChanged(TextView textView) + { + textView.Services.RemoveService(typeof(ISyntaxHighlighter)); + base.OnDocumentChanged(textView); + textView.Services.AddService(typeof(ISyntaxHighlighter), (CustomizingHighlighter)textView.GetService(typeof(IHighlighter))); + } + protected override IHighlighter CreateHighlighter(TextView textView, TextDocument document) { - return new CustomizingHighlighter(customizations, base.CreateHighlighter(textView, document)); + return new CustomizingHighlighter(customizations, highlightingDefinition, base.CreateHighlighter(textView, document)); } - sealed class CustomizingHighlighter : IHighlighter + sealed class CustomizingHighlighter : IHighlighter, ISyntaxHighlighter { readonly IEnumerable customizations; + readonly IHighlightingDefinition highlightingDefinition; readonly IHighlighter baseHighlighter; + List additionalHighlighters = new List(); - public CustomizingHighlighter(IEnumerable customizations, IHighlighter baseHighlighter) + public CustomizingHighlighter(IEnumerable customizations, IHighlightingDefinition highlightingDefinition, IHighlighter baseHighlighter) { Debug.Assert(customizations != null); + Debug.Assert(highlightingDefinition != null); + Debug.Assert(baseHighlighter != null); + this.customizations = customizations; + this.highlightingDefinition = highlightingDefinition; this.baseHighlighter = baseHighlighter; } @@ -125,20 +143,161 @@ namespace ICSharpCode.AvalonEdit.AddIn get { return baseHighlighter.Document; } } - public ImmutableStack GetSpanStack(int lineNumber) + public IHighlightingDefinition HighlightingDefinition { + get { return highlightingDefinition; } + } + + public void AddAdditionalHighlighter(IHighlighter highlighter) { - return baseHighlighter.GetSpanStack(lineNumber); + if (highlighter == null) + throw new ArgumentNullException("highlighter"); + if (highlighter.Document != baseHighlighter.Document) + throw new ArgumentException("Additional highlighters must use the same document as the base highlighter"); + additionalHighlighters.Add(highlighter); + } + + public void RemoveAdditionalHighlighter(IHighlighter highlighter) + { + additionalHighlighters.Remove(highlighter); + } + + public IEnumerable GetSpanColorNamesFromLineStart(int lineNumber) + { + // delayed evaluation doesn't cause a problem here: GetColorStack is called immediately, + // only the where/select portion is evaluated later. But that won't be a problem because the + // HighlightingColor instance shouldn't change once it's in use. + return from color in GetColorStack(lineNumber - 1) + where color.Name != null + select color.Name; + } + + public IEnumerable GetColorStack(int lineNumber) + { + List list = new List(); + for (int i = additionalHighlighters.Count - 1; i >= 0; i--) { + var s = additionalHighlighters[i].GetColorStack(lineNumber); + if (s != null) + list.AddRange(s); + } + list.AddRange(baseHighlighter.GetColorStack(lineNumber)); + return list; } public HighlightedLine HighlightLine(int lineNumber) { HighlightedLine line = baseHighlighter.HighlightLine(lineNumber); + foreach (IHighlighter h in additionalHighlighters) { + MergeHighlighting(line, h.HighlightLine(lineNumber)); + } foreach (HighlightedSection section in line.Sections) { section.Color = CustomizeColor(section.Color); } return line; } + /// + /// Merges the highlighting sections from additionalLine into line. + /// + void MergeHighlighting(HighlightedLine line, HighlightedLine additionalLine) + { + if (additionalLine == null) + return; + ValidateInvariants(line); + ValidateInvariants(additionalLine); + + int pos = 0; + Stack activeSectionEndOffsets = new Stack(); + int lineEndOffset = line.DocumentLine.EndOffset; + activeSectionEndOffsets.Push(lineEndOffset); + foreach (HighlightedSection newSection in additionalLine.Sections) { + int newSectionStart = newSection.Offset; + // Track the existing sections using the stack, up to the point where + // we need to insert the first part of the newSection + while (pos < line.Sections.Count) { + HighlightedSection s = line.Sections[pos]; + if (newSection.Offset < s.Offset) + break; + while (s.Offset > activeSectionEndOffsets.Peek()) { + activeSectionEndOffsets.Pop(); + } + activeSectionEndOffsets.Push(s.Offset + s.Length); + pos++; + } + // Now insert the new section + // Create a copy of the stack so that we can track the sections we traverse + // during the insertion process: + Stack insertionStack = new Stack(activeSectionEndOffsets.Reverse()); + // The stack enumerator reverses the order of the elements, so we call Reverse() to restore + // the original order. + int i; + for (i = pos; i < line.Sections.Count; i++) { + HighlightedSection s = line.Sections[i]; + if (newSection.Offset + newSection.Length <= s.Offset) + break; + // Insert a segment in front of s: + Insert(line.Sections, ref i, ref newSectionStart, s.Offset, newSection.Color, insertionStack); + + while (s.Offset > insertionStack.Peek()) { + insertionStack.Pop(); + } + insertionStack.Push(s.Offset + s.Length); + } + Insert(line.Sections, ref i, ref newSectionStart, newSection.Offset + newSection.Length, newSection.Color, insertionStack); + } + + ValidateInvariants(line); + } + + void Insert(IList sections, ref int pos, ref int newSectionStart, int insertionEndPos, HighlightingColor color, Stack insertionStack) + { + if (newSectionStart >= insertionEndPos) { + // nothing to insert here + return; + } + + while (insertionStack.Peek() <= newSectionStart) { + insertionStack.Pop(); + } + while (insertionStack.Peek() < insertionEndPos) { + int end = insertionStack.Pop(); + // insert the portion from newSectionStart to end + sections.Insert(pos++, new HighlightedSection { + Offset = newSectionStart, + Length = end - newSectionStart, + Color = color + }); + newSectionStart = end; + } + sections.Insert(pos++, new HighlightedSection { + Offset = newSectionStart, + Length = insertionEndPos - newSectionStart, + Color = color + }); + newSectionStart = insertionEndPos; + } + + [Conditional("DEBUG")] + void ValidateInvariants(HighlightedLine line) + { + int lineStartOffset = line.DocumentLine.Offset; + int lineEndOffset = line.DocumentLine.EndOffset; + for (int i = 0; i < line.Sections.Count; i++) { + HighlightedSection s1 = line.Sections[i]; + if (s1.Offset < lineStartOffset || s1.Length < 0 || s1.Offset + s1.Length > lineEndOffset) + throw new InvalidOperationException("Section is outside line bounds"); + for (int j = i + 1; j < line.Sections.Count; j++) { + HighlightedSection s2 = line.Sections[j]; + if (s2.Offset >= s1.Offset + s1.Length) { + // s2 is after s1 + } else if (s2.Offset >= s1.Offset && s2.Offset + s2.Length <= s1.Offset + s1.Length) { + // s2 is nested within s1 + } else { + throw new InvalidOperationException("Sections are overlapping or incorrectly sorted."); + } + } + } + } + HighlightingColor CustomizeColor(HighlightingColor color) { if (color == null || color.Name == null) diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Options/HighlightingOptions.xaml.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Options/HighlightingOptions.xaml.cs index 0ba535891d..223dae5adc 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Options/HighlightingOptions.xaml.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Options/HighlightingOptions.xaml.cs @@ -349,7 +349,7 @@ namespace ICSharpCode.AvalonEdit.AddIn.Options colorizer = null; if (item != null) { if (item.ParentDefinition != null) { - colorizer = new CustomizableHighlightingColorizer(item.ParentDefinition.MainRuleSet, customizationsForCurrentLanguage); + colorizer = new CustomizableHighlightingColorizer(item.ParentDefinition, customizationsForCurrentLanguage); textView.LineTransformers.Add(colorizer); } textEditor.Select(0, 0); diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs index 3822cf54c1..1451164acd 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs @@ -36,15 +36,6 @@ namespace ICSharpCode.AvalonEdit.Folding document.VerifyAccess(); TextDocumentWeakEventManager.Changed.AddListener(document, this); } - - /// - /// Creates a new FoldingManager instance. - /// - [Obsolete("Use the (TextDocument) constructor instead.")] - public FoldingManager(TextView textView, TextDocument document) - : this(document) - { - } #endregion #region ReceiveWeakEvent diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs index a2200ee781..358b87be57 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs @@ -157,7 +157,14 @@ namespace ICSharpCode.AvalonEdit.Highlighting } } - /// + /// + /// Gets the span stack at the end of the specified line. + /// -> GetSpanStack(1) returns the spans at the start of the second line. + /// + /// + /// GetSpanStack(0) is valid and will return . + /// The elements are returned in inside-out order (first element of result enumerable is the color of the innermost span). + /// public SpanStack GetSpanStack(int lineNumber) { ThrowUtil.CheckInRangeInclusive(lineNumber, "lineNumber", 0, document.LineCount); @@ -173,6 +180,12 @@ namespace ICSharpCode.AvalonEdit.Highlighting return storedSpanStacks[lineNumber]; } + /// + public IEnumerable GetColorStack(int lineNumber) + { + return GetSpanStack(lineNumber).Select(s => s.SpanColor).Where(s => s != null); + } + void CheckIsHighlighting() { if (isHighlighting) { diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs index ecfa17f2d2..5d30dccfac 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs @@ -30,36 +30,20 @@ namespace ICSharpCode.AvalonEdit.Highlighting this.ruleSet = ruleSet; } - /// - /// This constructor is obsolete - please use the other overload instead. - /// - /// UNUSED - /// The root highlighting rule set. - [Obsolete("The TextView parameter is no longer used, please use the constructor taking only HighlightingRuleSet instead")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "textView")] - public HighlightingColorizer(TextView textView, HighlightingRuleSet ruleSet) - : this(ruleSet) - { - } - void textView_DocumentChanged(object sender, EventArgs e) { OnDocumentChanged((TextView)sender); } - void OnDocumentChanged(TextView textView) + protected virtual void OnDocumentChanged(TextView textView) { // remove existing highlighter, if any exists textView.Services.RemoveService(typeof(IHighlighter)); - textView.Services.RemoveService(typeof(DocumentHighlighter)); TextDocument document = textView.Document; if (document != null) { IHighlighter highlighter = CreateHighlighter(textView, document); textView.Services.AddService(typeof(IHighlighter), highlighter); - // for backward compatiblity, we're registering using both the interface and concrete types - if (highlighter is DocumentHighlighter) - textView.Services.AddService(typeof(DocumentHighlighter), highlighter); } } @@ -99,7 +83,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting // We need to detect this case and issue a redraw (through TextViewDocumentHighligher.OnHighlightStateChanged) // before the visual line construction reuses existing lines that were built using the invalid highlighting state. lineNumberBeingColorized = e.FirstLineInView.LineNumber - 1; - highlighter.GetSpanStack(lineNumberBeingColorized); + highlighter.GetColorStack(lineNumberBeingColorized); lineNumberBeingColorized = 0; } } @@ -119,7 +103,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting // But even if we didn't highlight it, we'll have to update the highlighting state for it so that the // proof inside TextViewDocumentHighlighter.OnHighlightStateChanged holds. lineNumberBeingColorized = context.VisualLine.LastDocumentLine.LineNumber; - highlighter.GetSpanStack(lineNumberBeingColorized); + highlighter.GetColorStack(lineNumberBeingColorized); lineNumberBeingColorized = 0; } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingManager.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingManager.cs index 8c86206007..2eb93eae12 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingManager.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingManager.cs @@ -137,18 +137,6 @@ namespace ICSharpCode.AvalonEdit.Highlighting } } - /// - /// Gets the names of the registered highlightings. - /// - [ObsoleteAttribute("Use the HighlightingDefinitions property instead.")] - public IEnumerable HighlightingNames { - get { - lock (lockObj) { - return new List(highlightingsByName.Keys); - } - } - } - /// /// Gets a highlighting definition by extension. /// Returns null if the definition is not found. diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs index d956cffbdd..55a6f70e97 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs @@ -2,7 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; -using ICSharpCode.AvalonEdit.Utils; +using System.Collections.Generic; using ICSharpCode.NRefactory.Editor; namespace ICSharpCode.AvalonEdit.Highlighting @@ -18,12 +18,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting /// IDocument Document { get; } - /// - /// Gets the span stack at the end of the specified line. - /// -> GetSpanStack(1) returns the spans at the start of the second line. + /// + /// Gets the stack of active colors (the colors associated with the active spans) at the end of the specified line. + /// -> GetColorStack(1) returns the colors at the start of the second line. /// - /// GetSpanStack(0) is valid and will always return the empty stack. - ImmutableStack GetSpanStack(int lineNumber); + /// + /// GetColorStack(0) is valid and will return the empty stack. + /// The elements are returned in inside-out order (first element of result enumerable is the color of the innermost span). + /// + IEnumerable GetColorStack(int lineNumber); + + // Starting with SD 5.0, this interface exports GetColorStack() instead of GetSpanStack(). + // This was done because custom highlighter implementations might not use the HighlightingSpan class (AST-based highlighting). /// /// Highlights the specified document line. diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 319f98bd8a..ba8057a8df 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -96,7 +96,6 @@ - diff --git a/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs b/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs deleted file mode 100644 index 772f6536f1..0000000000 --- a/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Linq; - -using ICSharpCode.AvalonEdit; -using ICSharpCode.AvalonEdit.Highlighting; - -namespace ICSharpCode.SharpDevelop.Editor.AvalonEdit -{ - public class AvalonEditSyntaxHighlighterAdapter : ISyntaxHighlighter - { - ITextEditorComponent textEditor; - - public AvalonEditSyntaxHighlighterAdapter(ITextEditorComponent textEditor) - { - if (textEditor == null) - throw new ArgumentNullException("textEditor"); - this.textEditor = textEditor; - } - - public IEnumerable GetSpanColorNamesFromLineStart(int lineNumber) - { - IHighlighter highlighter = textEditor.GetService(typeof(IHighlighter)) as IHighlighter; - if (highlighter != null) { - // delayed evaluation doesn't cause a problem here: GetSpanStack is called immediately, - // only the where/select portian is evaluated later. But that won't be a problem because the - // HighlightingSpan instance shouldn't change once it's in use. - return from span in highlighter.GetSpanStack(lineNumber - 1) - where span.SpanColor != null && span.SpanColor.Name != null - select span.SpanColor.Name; - } else { - return Enumerable.Empty(); - } - } - } -} diff --git a/src/Main/Base/Project/Src/Editor/ISyntaxHighlighter.cs b/src/Main/Base/Project/Src/Editor/ISyntaxHighlighter.cs index 7f78dc8459..b4e6a5acf3 100644 --- a/src/Main/Base/Project/Src/Editor/ISyntaxHighlighter.cs +++ b/src/Main/Base/Project/Src/Editor/ISyntaxHighlighter.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using ICSharpCode.AvalonEdit.Highlighting; namespace ICSharpCode.SharpDevelop.Editor { @@ -17,6 +18,21 @@ namespace ICSharpCode.SharpDevelop.Editor /// Nested spans are returned in inside-out order (first element of result enumerable is the innermost span). /// IEnumerable GetSpanColorNamesFromLineStart(int lineNumber); + + /// + /// Gets the highlighting definition that is being used. + /// + IHighlightingDefinition HighlightingDefinition { get; } + + /// + /// Adds an additional highlighting engine that runs in addition to the XSHD-based highlighting. + /// + void AddAdditionalHighlighter(IHighlighter highlighter); + + /// + /// Removes an additional highlighting engine. + /// + void RemoveAdditionalHighlighter(IHighlighter highlighter); } public static class SyntaxHighligherKnownSpanNames diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs index fe931ca3af..8aa4ba69e5 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs @@ -658,13 +658,30 @@ namespace ICSharpCode.SharpDevelop.Parser } } + Task runningAsyncParseTask; + ITextSourceVersion runningAsyncParseFileContentVersion; + public Task ParseAsync(ITextSource fileContent) { bool lookupOpenFileOnTargetThread; SnapshotFileContentForAsyncOperation(ref fileContent, out lookupOpenFileOnTargetThread); - // TODO: don't use background task if fileContent was specified and up-to-date parse info is available - return System.Threading.Tasks.Task.Factory.StartNew( + ITextSourceVersion fileContentVersion = fileContent != null ? fileContent.Version : null; + if (fileContentVersion != null) { + // Optimization: + // don't start a background task if fileContent was specified and up-to-date parse info is available + lock (this) { + if (cachedParseInformation != null && bufferVersion != null && bufferVersion.BelongsToSameDocumentAs(fileContentVersion)) { + if (bufferVersion.CompareAge(fileContentVersion) >= 0) { + TaskCompletionSource tcs = new TaskCompletionSource(); + tcs.SetResult(cachedParseInformation); + return tcs.Task; + } + } + } + } + + var task = new Task( delegate { try { if (lookupOpenFileOnTargetThread) { @@ -677,9 +694,29 @@ namespace ICSharpCode.SharpDevelop.Parser } catch (Exception ex) { MessageService.ShowException(ex, "Error during async parse"); return null; + } finally { + lock (this) { + this.runningAsyncParseTask = null; + this.runningAsyncParseFileContentVersion = null; + } } } ); + if (fileContentVersion != null) { + // Optimization: when additional async parse runs are requested while the parser is already + // running for that file content, return the task that's already running + // instead of starting additional copies. + lock (this) { + if (runningAsyncParseTask != null && runningAsyncParseFileContentVersion.BelongsToSameDocumentAs(fileContentVersion)) { + if (runningAsyncParseFileContentVersion.CompareAge(fileContentVersion) >= 0) + return runningAsyncParseTask; + } + this.runningAsyncParseTask = task; + this.runningAsyncParseFileContentVersion = fileContentVersion; + } + } + task.Start(); + return task; } public Task ParseFileAsync(IProjectContent parentProjectContent, ITextSource fileContent) @@ -1097,8 +1134,7 @@ namespace ICSharpCode.SharpDevelop.Parser var parseInfo = entry.Parse(fileContent); if (parseInfo == null) return null; - IProject project = GetProject(parseInfo.ProjectContent); - var context = project != null ? project.TypeResolveContext : GetDefaultTypeResolveContext(); + var context = GetTypeResolveContext(parseInfo.ProjectContent); ResolveResult rr; using (var ctx = context.Synchronize()) { rr = entry.parser.Resolve(parseInfo, location, ctx, cancellationToken); @@ -1118,8 +1154,7 @@ namespace ICSharpCode.SharpDevelop.Parser var parseInfo = parseInfoTask.Result; if (parseInfo == null) return null; - IProject project = GetProject(parseInfo.ProjectContent); - var context = project != null ? project.TypeResolveContext : GetDefaultTypeResolveContext(); + var context = GetTypeResolveContext(parseInfo.ProjectContent); ResolveResult rr; using (var ctx = context.Synchronize()) { rr = entry.parser.Resolve(parseInfo, location, ctx, cancellationToken);