Permalink
Browse files

Add semantic highlighting for C#.

  • Loading branch information...
1 parent 2bbeefd commit b7b259896d7ac84668d8bddf25f5bcaee15ec9fc @dgrunwald dgrunwald committed Sep 19, 2011
Showing with 835 additions and 110 deletions.
  1. +2 −4 src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin
  2. +5 −0 src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj
  3. +291 −0 src/AddIns/BackendBindings/CSharpBinding/Project/Resources/CSharp-Semantic.xshd
  4. +22 −2 src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs
  5. +261 −0 src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs
  6. +0 −2 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  7. +1 −1 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
  8. +166 −7 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CustomizableHighlightingColorizer.cs
  9. +1 −1 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Options/HighlightingOptions.xaml.cs
  10. +0 −9 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs
  11. +14 −1 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs
  12. +3 −19 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs
  13. +0 −12 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingManager.cs
  14. +12 −6 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs
  15. +0 −1 src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  16. +0 −39 src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs
  17. +16 −0 src/Main/Base/Project/Src/Editor/ISyntaxHighlighter.cs
  18. +41 −6 src/Main/Base/Project/Src/Services/ParserService/ParserService.cs
View
6 src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin
@@ -65,11 +65,9 @@
extensions=".cs" />
</Path>
- <!--
- <Path name = "/AddIns/DefaultTextEditor/AdvancedHighlighter/C#">
- <Class id = "CSharpHighlighter" class = "CSharpBinding.CSharpAdvancedHighlighter"/>
+ <Path name = "/SharpDevelop/ViewContent/AvalonEdit/SyntaxModes">
+ <SyntaxMode id="C#" name="C#" extensions=".cs" resource="Resources.CSharp-Semantic.xshd" />
</Path>
- -->
<Path path = "/SharpDevelop/BackendBindings/ProjectOptions/C#">
<OptionPanel id = "Application"
View
5 src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj
@@ -39,6 +39,9 @@
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<OutputPath>..\..\..\..\..\AddIns\BackendBindings\CSharpBinding\</OutputPath>
</PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <DefineConstants>DEBUG</DefineConstants>
+ </PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Build.Utilities.v4.0" />
<Reference Include="PresentationCore">
@@ -62,6 +65,7 @@
<Compile Include="Src\CSharpBracketSearcher.cs" />
<Compile Include="Src\CSharpLanguageBinding.cs" />
<Compile Include="Src\CSharpProjectBinding.cs" />
+ <Compile Include="Src\CSharpSemanticHighlighter.cs" />
<Compile Include="Src\FormattingStrategy\CSharpFormattingStrategy.cs" />
<Compile Include="Src\FormattingStrategy\DocumentAccessor.cs" />
<Compile Include="Src\OptionPanels\BuildOptions.cs">
@@ -80,6 +84,7 @@
<Compile Include="..\..\..\..\Main\GlobalAssemblyInfo.cs">
<Link>Configuration\GlobalAssemblyInfo.cs</Link>
</Compile>
+ <EmbeddedResource Include="Resources\CSharp-Semantic.xshd" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj">
View
291 src/AddIns/BackendBindings/CSharpBinding/Project/Resources/CSharp-Semantic.xshd
@@ -0,0 +1,291 @@
+<?xml version="1.0"?>
+<SyntaxDefinition name="C#" extensions=".cs" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <!-- This is a variant of the AvalonEdit C# highlighting that has several constructs disabled.
+ The disabled constructs (e.g. contextual keywords) are highlighted using the CSharpSemanticHighlighter instead.
+ -->
+
+ <!-- The named colors 'Comment' and 'String' are used in SharpDevelop to detect if a line is inside a multiline string/comment -->
+ <Color name="Comment" foreground="Green" exampleText="// comment" />
+ <Color name="String" foreground="Blue" exampleText="string text = &quot;Hello, World!&quot;"/>
+ <Color name="Char" foreground="Magenta" exampleText="char linefeed = '\n';"/>
+ <Color name="Preprocessor" foreground="Green" exampleText="#region Title" />
+ <Color name="Punctuation" exampleText="a(b.c);" />
+ <Color name="ValueTypes" fontWeight="bold" foreground="Red" exampleText="bool b = true;" />
+ <Color name="ReferenceTypes" foreground="Red" exampleText="object o;" />
+ <Color name="NumberLiteral" foreground="DarkBlue" exampleText="3.1415f"/>
+ <Color name="ThisOrBaseReference" fontWeight="bold" exampleText="this.Do(); base.Do();"/>
+ <Color name="NullOrValueKeywords" fontWeight="bold" exampleText="if (value == null)"/>
+ <Color name="Keywords" fontWeight="bold" foreground="Blue" exampleText="if (a) {} else {}"/>
+ <Color name="GotoKeywords" foreground="Navy" exampleText="continue; return null;"/>
+ <Color name="ContextKeywords" foreground="Navy" exampleText="var a = from x in y select z;"/>
+ <Color name="ExceptionKeywords" fontWeight="bold" foreground="Teal" exampleText="try {} catch {} finally {}"/>
+ <Color name="CheckedKeyword" fontWeight="bold" foreground="DarkGray" exampleText="checked {}"/>
+ <Color name="UnsafeKeywords" foreground="Olive" exampleText="unsafe { fixed (..) {} }"/>
+ <Color name="OperatorKeywords" fontWeight="bold" foreground="Pink" exampleText="public static implicit operator..."/>
+ <Color name="ParameterModifiers" fontWeight="bold" foreground="DeepPink" exampleText="(ref int a, params int[] b)"/>
+ <Color name="Modifiers" foreground="Brown" exampleText="static readonly int a;"/>
+ <Color name="Visibility" fontWeight="bold" foreground="Blue" exampleText="public override void ToString();"/>
+ <Color name="NamespaceKeywords" fontWeight="bold" foreground="Green" exampleText="namespace A.B { using System; }"/>
+ <Color name="GetSetAddRemove" foreground="SaddleBrown" exampleText="int Prop { get; set; }"/>
+ <Color name="TrueFalse" fontWeight="bold" foreground="DarkCyan" exampleText="b = false; a = true;" />
+ <Color name="TypeKeywords" fontWeight="bold" foreground="DarkCyan" exampleText="if (x is int) { a = x as int; type = typeof(int); size = sizeof(int); c = new object(); }"/>
+
+ <!-- Colors used for semantic highlighting -->
+ <Color name="TypeReferences" foreground="DarkCyan" exampleText="System.Uri uri;"/>
+ <Color name="MethodCall" foreground="MidnightBlue" fontWeight="bold" exampleText="o.ToString();"/>
+ <Color name="FieldAccess" fontStyle="italic" exampleText="return this.name;"/>
+
+ <RuleSet name="CommentMarkerSet">
+ <Keywords fontWeight="bold" foreground="Red">
+ <Word>TODO</Word>
+ <Word>FIXME</Word>
+ </Keywords>
+ <Keywords fontWeight="bold" foreground="#E0E000">
+ <Word>HACK</Word>
+ <Word>UNDONE</Word>
+ </Keywords>
+ </RuleSet>
+
+ <!-- This is the main ruleset. -->
+ <RuleSet>
+ <Span color="Preprocessor">
+ <Begin>\#</Begin>
+ <RuleSet name="PreprocessorSet">
+ <Span> <!-- preprocessor directives that allow comments -->
+ <Begin fontWeight="bold">
+ (define|undef|if|elif|else|endif|line)\b
+ </Begin>
+ <RuleSet>
+ <Span color="Comment" ruleSet="CommentMarkerSet">
+ <Begin>//</Begin>
+ </Span>
+ </RuleSet>
+ </Span>
+ <Span> <!-- preprocessor directives that don't allow comments -->
+ <Begin fontWeight="bold">
+ (region|endregion|error|warning|pragma)\b
+ </Begin>
+ </Span>
+ </RuleSet>
+ </Span>
+
+ <Span color="Comment">
+ <Begin color="XmlDoc/DocComment">///</Begin>
+ <RuleSet>
+ <Import ruleSet="XmlDoc/DocCommentSet"/>
+ <Import ruleSet="CommentMarkerSet"/>
+ </RuleSet>
+ </Span>
+
+ <Span color="Comment" ruleSet="CommentMarkerSet">
+ <Begin>//</Begin>
+ </Span>
+
+ <Span color="Comment" ruleSet="CommentMarkerSet" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin="\\" end="."/>
+ </RuleSet>
+ </Span>
+
+ <Span color="Char">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin="\\" end="."/>
+ </RuleSet>
+ </Span>
+
+ <Span color="String" multiline="true">
+ <Begin color="String">@"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin='""' end=""/>
+ </RuleSet>
+ </Span>
+
+ <!-- don't highlight "@int" as keyword -->
+ <Rule>
+ @[\w\d_]+
+ </Rule>
+
+ <Keywords color="ThisOrBaseReference">
+ <Word>this</Word>
+ <Word>base</Word>
+ </Keywords>
+
+ <Keywords color="TypeKeywords">
+ <Word>as</Word>
+ <Word>is</Word>
+ <Word>new</Word>
+ <Word>sizeof</Word>
+ <Word>typeof</Word>
+ <Word>stackalloc</Word>
+ </Keywords>
+
+ <Keywords color="TrueFalse">
+ <Word>true</Word>
+ <Word>false</Word>
+ </Keywords>
+
+ <Keywords color="Keywords">
+ <Word>else</Word>
+ <Word>if</Word>
+ <Word>switch</Word>
+ <Word>case</Word>
+ <Word>default</Word>
+ <Word>do</Word>
+ <Word>for</Word>
+ <Word>foreach</Word>
+ <Word>in</Word>
+ <Word>while</Word>
+ <Word>lock</Word>
+ </Keywords>
+
+ <Keywords color="GotoKeywords">
+ <Word>break</Word>
+ <Word>continue</Word>
+ <Word>goto</Word>
+ <Word>return</Word>
+ </Keywords>
+
+ <Keywords color="ContextKeywords">
+ <Word>yield</Word>
+ <Word>partial</Word>
+ <Word>global</Word>
+ <Word>where</Word>
+ <Word>select</Word>
+ <Word>group</Word>
+ <Word>by</Word>
+ <Word>into</Word>
+ <Word>from</Word>
+ <Word>ascending</Word>
+ <Word>descending</Word>
+ <Word>orderby</Word>
+ <Word>let</Word>
+ <Word>join</Word>
+ <Word>on</Word>
+ <Word>equals</Word>
+ <Word>var</Word>
+ <Word>dynamic</Word>
+ <Word>await</Word>
+ </Keywords>
+
+ <Keywords color="ExceptionKeywords">
+ <Word>try</Word>
+ <Word>throw</Word>
+ <Word>catch</Word>
+ <Word>finally</Word>
+ </Keywords>
+
+ <Keywords color="CheckedKeyword">
+ <Word>checked</Word>
+ <Word>unchecked</Word>
+ </Keywords>
+
+ <Keywords color="UnsafeKeywords">
+ <Word>fixed</Word>
+ <Word>unsafe</Word>
+ </Keywords>
+
+ <Keywords color="ValueTypes">
+ <Word>bool</Word>
+ <Word>byte</Word>
+ <Word>char</Word>
+ <Word>decimal</Word>
+ <Word>double</Word>
+ <Word>enum</Word>
+ <Word>float</Word>
+ <Word>int</Word>
+ <Word>long</Word>
+ <Word>sbyte</Word>
+ <Word>short</Word>
+ <Word>struct</Word>
+ <Word>uint</Word>
+ <Word>ushort</Word>
+ <Word>ulong</Word>
+ </Keywords>
+
+ <Keywords color="ReferenceTypes">
+ <Word>class</Word>
+ <Word>interface</Word>
+ <Word>delegate</Word>
+ <Word>object</Word>
+ <Word>string</Word>
+ <Word>void</Word>
+ </Keywords>
+
+ <Keywords color="OperatorKeywords">
+ <Word>explicit</Word>
+ <Word>implicit</Word>
+ <Word>operator</Word>
+ </Keywords>
+
+ <Keywords color="ParameterModifiers">
+ <Word>params</Word>
+ <Word>ref</Word>
+ <Word>out</Word>
+ </Keywords>
+
+ <Keywords color="Modifiers">
+ <Word>abstract</Word>
+ <Word>const</Word>
+ <Word>event</Word>
+ <Word>extern</Word>
+ <Word>override</Word>
+ <Word>readonly</Word>
+ <Word>sealed</Word>
+ <Word>static</Word>
+ <Word>virtual</Word>
+ <Word>volatile</Word>
+ <Word>async</Word>
+ </Keywords>
+
+ <Keywords color="Visibility">
+ <Word>public</Word>
+ <Word>protected</Word>
+ <Word>private</Word>
+ <Word>internal</Word>
+ </Keywords>
+
+ <Keywords color="NamespaceKeywords">
+ <Word>namespace</Word>
+ <Word>using</Word>
+ </Keywords>
+
+ <Keywords color="GetSetAddRemove">
+ <Word>get</Word>
+ <Word>set</Word>
+ <Word>add</Word>
+ <Word>remove</Word>
+ </Keywords>
+
+ <Keywords color="NullOrValueKeywords">
+ <Word>null</Word>
+ <!--<Word>value</Word>-->
+ </Keywords>
+
+ <!-- Digits -->
+ <Rule color="NumberLiteral">
+ \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
+ </Rule>
+
+ <Rule color="Punctuation">
+ [?,.;()\[\]{}+\-/%*&lt;&gt;^+~!|&amp;]+
+ </Rule>
+ </RuleSet>
+</SyntaxDefinition>
View
24 src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs
@@ -20,14 +20,34 @@ public class CSharpLanguageBinding : DefaultLanguageBinding
// 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();
}
}
}
View
261 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
+{
+ /// <summary>
+ /// Semantic highlighting for C#.
+ /// </summary>
+ public class CSharpSemanticHighlighter : DepthFirstAstVisitor<object, object>, 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<HighlightingColor> IHighlighter.GetColorStack(int lineNumber)
+ {
+ return null;
+ }
+
+ public HighlightedLine HighlightLine(int lineNumber)
+ {
+ Task<ParseInformation> 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<CompilationUnit>();
+ 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;
+ }
+ }
+}
View
2 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
@@ -212,8 +212,6 @@ protected virtual CodeEditorView CreateTextEditor()
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;
View
2 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
@@ -515,7 +515,7 @@ public void UpdateParseInformationForFolding(ParseInformation parseInfo)
protected override IVisualLineTransformer CreateColorizer(IHighlightingDefinition highlightingDefinition)
{
return new CustomizableHighlightingColorizer(
- highlightingDefinition.MainRuleSet,
+ highlightingDefinition,
FetchCustomizations(highlightingDefinition.Name));
}
View
173 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CustomizableHighlightingColorizer.cs
@@ -14,6 +14,7 @@
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
/// </summary>
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,52 +95,209 @@ public static void ApplyCustomizationsToDefaultElements(TextEditor textEditor, I
}
}
}
+ #endregion
+ readonly IHighlightingDefinition highlightingDefinition;
readonly IEnumerable<CustomizedHighlightingColor> customizations;
- public CustomizableHighlightingColorizer(HighlightingRuleSet ruleSet, IEnumerable<CustomizedHighlightingColor> customizations)
- : base(ruleSet)
+ public CustomizableHighlightingColorizer(IHighlightingDefinition highlightingDefinition, IEnumerable<CustomizedHighlightingColor> 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<CustomizedHighlightingColor> customizations;
+ readonly IHighlightingDefinition highlightingDefinition;
readonly IHighlighter baseHighlighter;
+ List<IHighlighter> additionalHighlighters = new List<IHighlighter>();
- public CustomizingHighlighter(IEnumerable<CustomizedHighlightingColor> customizations, IHighlighter baseHighlighter)
+ public CustomizingHighlighter(IEnumerable<CustomizedHighlightingColor> 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;
}
public IDocument Document {
get { return baseHighlighter.Document; }
}
- public ImmutableStack<HighlightingSpan> 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<string> 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<HighlightingColor> GetColorStack(int lineNumber)
+ {
+ List<HighlightingColor> list = new List<HighlightingColor>();
+ 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;
}
+ /// <summary>
+ /// Merges the highlighting sections from additionalLine into line.
+ /// </summary>
+ void MergeHighlighting(HighlightedLine line, HighlightedLine additionalLine)
+ {
+ if (additionalLine == null)
+ return;
+ ValidateInvariants(line);
+ ValidateInvariants(additionalLine);
+
+ int pos = 0;
+ Stack<int> activeSectionEndOffsets = new Stack<int>();
+ 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<int> insertionStack = new Stack<int>(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<HighlightedSection> sections, ref int pos, ref int newSectionStart, int insertionEndPos, HighlightingColor color, Stack<int> 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)
View
2 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Options/HighlightingOptions.xaml.cs
@@ -349,7 +349,7 @@ void UpdatePreview()
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);
View
9 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs
@@ -36,15 +36,6 @@ public FoldingManager(TextDocument document)
document.VerifyAccess();
TextDocumentWeakEventManager.Changed.AddListener(document, this);
}
-
- /// <summary>
- /// Creates a new FoldingManager instance.
- /// </summary>
- [Obsolete("Use the (TextDocument) constructor instead.")]
- public FoldingManager(TextView textView, TextDocument document)
- : this(document)
- {
- }
#endregion
#region ReceiveWeakEvent
View
15 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs
@@ -157,7 +157,14 @@ public HighlightedLine HighlightLine(int lineNumber)
}
}
- /// <inheritdoc/>
+ /// <summary>
+ /// Gets the span stack at the end of the specified line.
+ /// -> GetSpanStack(1) returns the spans at the start of the second line.
+ /// </summary>
+ /// <remarks>
+ /// GetSpanStack(0) is valid and will return <see cref="InitialSpanStack"/>.
+ /// The elements are returned in inside-out order (first element of result enumerable is the color of the innermost span).
+ /// </remarks>
public SpanStack GetSpanStack(int lineNumber)
{
ThrowUtil.CheckInRangeInclusive(lineNumber, "lineNumber", 0, document.LineCount);
@@ -173,6 +180,12 @@ public SpanStack GetSpanStack(int lineNumber)
return storedSpanStacks[lineNumber];
}
+ /// <inheritdoc/>
+ public IEnumerable<HighlightingColor> GetColorStack(int lineNumber)
+ {
+ return GetSpanStack(lineNumber).Select(s => s.SpanColor).Where(s => s != null);
+ }
+
void CheckIsHighlighting()
{
if (isHighlighting) {
View
22 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs
@@ -30,36 +30,20 @@ public HighlightingColorizer(HighlightingRuleSet ruleSet)
this.ruleSet = ruleSet;
}
- /// <summary>
- /// This constructor is obsolete - please use the other overload instead.
- /// </summary>
- /// <param name="textView">UNUSED</param>
- /// <param name="ruleSet">The root highlighting rule set.</param>
- [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 @@ void textView_VisualLineConstructionStarting(object sender, VisualLineConstructi
// 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 @@ protected override void Colorize(ITextRunConstructionContext context)
// 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;
}
}
View
12 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingManager.cs
@@ -138,18 +138,6 @@ public IHighlightingDefinition GetDefinition(string name)
}
/// <summary>
- /// Gets the names of the registered highlightings.
- /// </summary>
- [ObsoleteAttribute("Use the HighlightingDefinitions property instead.")]
- public IEnumerable<string> HighlightingNames {
- get {
- lock (lockObj) {
- return new List<string>(highlightingsByName.Keys);
- }
- }
- }
-
- /// <summary>
/// Gets a highlighting definition by extension.
/// Returns null if the definition is not found.
/// </summary>
View
18 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 @@ public interface IHighlighter
/// </summary>
IDocument Document { get; }
- /// <summary>
- /// Gets the span stack at the end of the specified line.
- /// -> GetSpanStack(1) returns the spans at the start of the second line.
+ /// <summary>
+ /// 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.
/// </summary>
- /// <remarks>GetSpanStack(0) is valid and will always return the empty stack.</remarks>
- ImmutableStack<HighlightingSpan> GetSpanStack(int lineNumber);
+ /// <remarks>
+ /// 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).
+ /// </remarks>
+ IEnumerable<HighlightingColor> 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).
/// <summary>
/// Highlights the specified document line.
View
1 src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
@@ -96,7 +96,6 @@
</Compile>
<Compile Include="Src\Commands\SharpDevelopRoutedCommands.cs" />
<Compile Include="Src\Editor\AddInHighlightingResource.cs" />
- <Compile Include="Src\Editor\AvalonEdit\AvalonEditSyntaxHighlighterAdapter.cs" />
<Compile Include="Src\Editor\AvalonEdit\AvalonEditTextEditorAdapter.cs" />
<Compile Include="Src\Editor\AvalonEdit\IndentationStrategyAdapter.cs" />
<Compile Include="Src\Editor\AvalonEdit\ISnippetElementProvider.cs" />
View
39 src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs
@@ -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<string> 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<string>();
- }
- }
- }
-}
View
16 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 @@ public interface ISyntaxHighlighter
/// Nested spans are returned in inside-out order (first element of result enumerable is the innermost span).
/// </summary>
IEnumerable<string> GetSpanColorNamesFromLineStart(int lineNumber);
+
+ /// <summary>
+ /// Gets the highlighting definition that is being used.
+ /// </summary>
+ IHighlightingDefinition HighlightingDefinition { get; }
+
+ /// <summary>
+ /// Adds an additional highlighting engine that runs in addition to the XSHD-based highlighting.
+ /// </summary>
+ void AddAdditionalHighlighter(IHighlighter highlighter);
+
+ /// <summary>
+ /// Removes an additional highlighting engine.
+ /// </summary>
+ void RemoveAdditionalHighlighter(IHighlighter highlighter);
}
public static class SyntaxHighligherKnownSpanNames
View
47 src/Main/Base/Project/Src/Services/ParserService/ParserService.cs
@@ -658,13 +658,30 @@ void SnapshotFileContentForAsyncOperation(ref ITextSource fileContent, out bool
}
}
+ Task<ParseInformation> runningAsyncParseTask;
+ ITextSourceVersion runningAsyncParseFileContentVersion;
+
public Task<ParseInformation> 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<ParseInformation> tcs = new TaskCompletionSource<ParseInformation>();
+ tcs.SetResult(cachedParseInformation);
+ return tcs.Task;
+ }
+ }
+ }
+ }
+
+ var task = new Task<ParseInformation>(
delegate {
try {
if (lookupOpenFileOnTargetThread) {
@@ -677,9 +694,29 @@ public Task<ParseInformation> ParseAsync(ITextSource fileContent)
} 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<IParsedFile> ParseFileAsync(IProjectContent parentProjectContent, ITextSource fileContent)
@@ -1097,8 +1134,7 @@ static void ClearAllFileEntries()
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 @@ static void ClearAllFileEntries()
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);

0 comments on commit b7b2598

Please sign in to comment.