我ITagger<IClassificationTag>
正在生成标签,但是,Visual Studio 没有显示语法着色。这是代码ITagger<IClassificationTag>
:
[Export(typeof(ITaggerProvider))]
[ContentType("FDL")]
[TagType(typeof(ClassificationTag))]
internal sealed class FDLClassifierProvider : ITaggerProvider
{
[Export]
[Name("FDL")]
[BaseDefinition("code")]
internal static ContentTypeDefinition FDLContentType = null;
[Export]
[FileExtension(".fdl")]
[ContentType("FDL")]
internal static FileExtensionToContentTypeDefinition FDLFileType = null;
[Import]
internal IClassificationTypeRegistryService ClassificationTypeRegistry = null;
[Import]
internal IBufferTagAggregatorFactoryService aggregatorFactory = null;
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
return new FDLClassifier(buffer, ClassificationTypeRegistry) as ITagger<T>;
}
}
/// <summary>
/// This is effectively the replacement to the LineScanner from 2008.
/// This class must handle very quick processing times during GetTags()
/// as it is called very frequently!
/// </summary>
internal sealed class FDLClassifier : ITagger<ClassificationTag>
{
ITextBuffer _buffer;
Grammar _grammar;
Irony.Parsing.Parser _parser;
IDictionary<Irony.Parsing.TokenType, ClassificationTag> _fdlTags;
ClassificationTag _commentTag;
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
Dictionary<int, int> _lineStates = new Dictionary<int, int>();
internal FDLClassifier(ITextBuffer buffer,
IClassificationTypeRegistryService typeService)
{
_buffer = buffer;
_grammar = new Grammar();
_parser = new Irony.Parsing.Parser(_grammar);
_parser.Context.Mode = Irony.Parsing.ParseMode.VsLineScan;
_fdlTags = new Dictionary<Irony.Parsing.TokenType, ClassificationTag>();
_fdlTags[Irony.Parsing.TokenType.Text] = BuildTag(typeService, PredefinedClassificationTypeNames.Character);
_fdlTags[Irony.Parsing.TokenType.Keyword] = BuildTag(typeService, PredefinedClassificationTypeNames.Keyword);
_fdlTags[Irony.Parsing.TokenType.Identifier] = BuildTag(typeService, PredefinedClassificationTypeNames.Identifier);
_fdlTags[Irony.Parsing.TokenType.String] = BuildTag(typeService, PredefinedClassificationTypeNames.String);
_fdlTags[Irony.Parsing.TokenType.Literal] = BuildTag(typeService, PredefinedClassificationTypeNames.Literal);
_fdlTags[Irony.Parsing.TokenType.Operator] = BuildTag(typeService, PredefinedClassificationTypeNames.Operator);
_fdlTags[Irony.Parsing.TokenType.LineComment] = BuildTag(typeService, PredefinedClassificationTypeNames.Comment);
_fdlTags[Irony.Parsing.TokenType.Comment] = BuildTag(typeService, PredefinedClassificationTypeNames.Comment);
_commentTag = BuildTag(typeService, PredefinedClassificationTypeNames.Comment);
InitializeLineStates(_buffer.CurrentSnapshot);
}
/// <summary>
/// In the context of a classification tagger, this is called initially w/ spans for all
/// content in the file.
/// It is called immediately after the user modifies text given the span of text that was modified.
/// It is also called for all lines that are newly visible due to scrolling.
/// This function gets called ALOT. Keep processing times to a minimal and try to only handle 1 line at a
/// time.
/// </summary>
/// <param name="spans"></param>
/// <returns></returns>
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
if (spans.Count == 0)
yield break;
var snapShot = spans[0].Snapshot;
foreach (var span in spans)
{
var startLine = span.Start.GetContainingLine();
var endLine = span.End.GetContainingLine();
var startLineNumber = startLine.LineNumber;
var endLineNumber = endLine.LineNumber;
for (int i = startLineNumber; i <= endLineNumber; i++)
{
var line = spans[0].Snapshot.GetLineFromLineNumber(i);
_parser.Scanner.VsSetSource(line.GetText(), 0);
int state = 0;
_lineStates.TryGetValue(i, out state);
var token = _parser.Scanner.VsReadToken(ref state);
while (token != null)
{
if (token.Category == Irony.Parsing.TokenCategory.Content)
{
if (token.EditorInfo != null)
{
ClassificationTag tag;
if (_fdlTags.TryGetValue(token.EditorInfo.Type, out tag))
{
var location = new SnapshotSpan(snapShot, line.Start.Position + token.Location.Position, token.Length);
yield return new TagSpan<ClassificationTag>(location, tag);
}
}
}
else if (token.Category == Irony.Parsing.TokenCategory.Comment)
{
var location = new SnapshotSpan(snapShot, line.Start.Position + token.Location.Position, token.Length);
yield return new TagSpan<ClassificationTag>(location, _commentTag);
}
token = _parser.Scanner.VsReadToken(ref state);
}
int oldState = 0;
_lineStates.TryGetValue(i + 1, out oldState);
_lineStates[i + 1] = state;
//We're going into overtime, process new tags and send the event that these spans need updating!
if (oldState != state)
{
var lineNumber = endLineNumber;
while (oldState != state && lineNumber < snapShot.LineCount)
{
lineNumber++;
var dummyToken = _parser.Scanner.VsReadToken(ref state);
while (dummyToken != null)
{
dummyToken = _parser.Scanner.VsReadToken(ref state);
}
_lineStates.TryGetValue(lineNumber + 1, out oldState);
_lineStates[lineNumber + 1] = state;
}
if (lineNumber >= snapShot.LineCount)
lineNumber = snapShot.LineCount - 1;
var lastLine = snapShot.GetLineFromLineNumber(lineNumber);
if (lastLine != null && this.TagsChanged != null)
{
int length = lastLine.End.Position - endLine.End.Position;
var snapShotSpan = new SnapshotSpan(snapShot, endLine.End.Position, length);
this.TagsChanged(this, new SnapshotSpanEventArgs(snapShotSpan));
}
}
}
}
}
private ClassificationTag BuildTag(IClassificationTypeRegistryService typeService, string type)
{
var classificationType = typeService.GetClassificationType(type);
return new ClassificationTag(classificationType);
}
/// <summary>
/// Initializes the line states based on the snapshot.
/// </summary>
private void InitializeLineStates(ITextSnapshot snapShot)
{
_lineStates[0] = 0;
foreach (var line in snapShot.Lines)
{
int state = 0;
_parser.Scanner.VsSetSource(line.GetText(), 0);
var dummyToken = _parser.Scanner.VsReadToken(ref state);
while (dummyToken != null)
{
dummyToken = _parser.Scanner.VsReadToken(ref state);
}
_lineStates[line.LineNumber + 1] = state;
}
}
}
语法:
[Language("FDL", "6.0", "Feature Description Language")]
public class Grammar: Irony.Parsing.Grammar
{
public Grammar():base(true)
{
#region NonTerminals
var blockComment = new CommentTerminal("block-comment", "/*", "*/");
var lineComment = new CommentTerminal("line-comment", "//", "\r", "\n", "\u2085", "\u2028", "\u2029");
NonGrammarTerminals.Add(blockComment);
NonGrammarTerminals.Add(lineComment);
var identifier = new IdentifierTerminal("identifier");
var stringLit = new StringLiteral("string");
#endregion
#region Symbols
MarkPunctuation(";", ",", ":");
RegisterBracePair("{", "}");
RegisterBracePair("<", ">");
RegisterBracePair("[", "]");
RegisterBracePair("(", ")");
#endregion
#region Keywords
var action = Keyword("action");
var actionBoxFont = Keyword("actionBoxFont");
var actionBoxFontSize = Keyword("actionBoxFontSize");
var allocate = Keyword("allocate");
// rest of grammar omitted for brevity.
#endregion
}
KeyTerm Keyword(string keyword)
{
var term = ToTerm(keyword);
MarkReservedWords(keyword);
term.EditorInfo = new TokenEditorInfo(TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
return term;
}
}