Поставил у себя VS2010 RC1. На винчестере семь гигов занимает, однако.
Посмотрел как делаются аддоны и в частности раскраска синтаксиса. Читал я об этом ещё год назад, а тут в связи с выходом RC решил опробовать в деле.
Для примера решил сделать подсветку синтаксиса для ассемблера Z80.
Первые результаты удалось получить через пару часов, ещё за пару часов сделал однострочный разбор - метки, комментарии, операторы и псевдо-операторы:
В Visual Studio 10 применяется новая модель расширений - весь код аддонов пишется исключительно на managed-коде, никаких COM. Это очень серьёзно облегчает жизнь, да и на общей стабильности системы сказывается очень положительно.
Собственно, к делу. Во-первых, существует несколько способов распространения расширений студии, я использовал VSIX - наиболее удобно для отладки. Имеет смысл поставить Microsoft Visual Studio 2010 SDK - студия дополняется шаблонами проектов для быстрого создания расширений. Но насколько я понимаю создавать их можно и так. С использованием VS2010 SDK новый проект для кастомной подсветки синтаксиса создаётся через пункт Extensibility > Editor Classifier.
Подсветка синтаксиса определяется классом, реализующим интерфейс IClassifier - этот класс задаёт логику разбора текста. Для использования классификатора нужен провайдер - реализующий IClassifierProvider и определяющий как наш классификатор будет использоваться.
[Export(typeof(IClassifierProvider))]
[ContentType("text")]
internal class Z80EditorClassifierProvider : IClassifierProvider
{
[Import]
internal IClassificationTypeRegistryService ClassificationRegistry = null; // Set via MEF
public IClassifier GetClassifier(ITextBuffer buffer)
{
return buffer.Properties.GetOrCreateSingletonProperty<Z80EditorClassifier>(
delegate { return new Z80EditorClassifier(ClassificationRegistry); });
}
}
Основной метод классификатора - GetClassificationSpans() - для заданного фрагмента текста функция должна возвратить список фрагментов с указанием типа фрагмента - класса, реализующего интерфейс IClassificationType.
class Z80EditorClassifier : IClassifier
{
public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
{
List<ClassificationSpan> result = new List<ClassificationSpan>();
...
return result;
}
}
При этом из GetClassificationSpans() конечно можно получить доступ к разбираемому тексту - через свойство span.Snapshot. В моём случае меня интересовал простой синтаксис, сводящийся к разбору одной строки.
public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
{
List<ClassificationSpan> result = new List<ClassificationSpan>();
if (span.Length == 0) return result;
ITextSnapshot snapshot = span.Snapshot;
ITextSnapshotLine line = snapshot.GetLineFromPosition(span.Start.Position);
ITextSnapshotLine endLine = snapshot.GetLineFromPosition(span.End.Position);
while (true)
{
// Process current line
ProcessLine(line, result);
if (line.LineNumber == endLine.LineNumber)
break;
// Next line
line = snapshot.GetLineFromPosition(line.EndIncludingLineBreak + 1);
}
return result;
}
Внутри ProcessLine остаётся идентифицировать отдельные фрагменты строки и добавить их в список, указав для каждого тип фрагмента. Например, вот так добавляется подсветка для комментария:
IClassificationType commentClassifType = registry.GetClassificationType("z80comment");
result.Add(new ClassificationSpan(
new SnapshotSpan(line.Snapshot, new Span(line.Start + commentPos, commentLength)),
commentClassifType));
Типы фрагментов можно описать все сразу в одном классе:
internal static class Z80EditorClassifierClassificationDefinition
{
/// <summary>
/// Defines the "z80operator" classification type - Z80 Assembly Operator.
/// </summary>
[Export(typeof(ClassificationTypeDefinition))]
[Name("z80operator")]
internal static ClassificationTypeDefinition Z80OperatorDefinition = null;
/// <summary>
/// Defines the "z80comment" classification type - Z80 Assembly Comment.
/// </summary>
[Export(typeof(ClassificationTypeDefinition))]
[Name("z80comment")]
internal static ClassificationTypeDefinition Z80CommentDefinition = null;
}
Классификатору может быть сопоставлен форматтер - класс, порождённый от ClassificationFormatDefinition и определяющий способ подсветки текста.
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "z80operator")]
[Name("z80operator")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class Z80EditorOperatorFormat : ClassificationFormatDefinition
{
public Z80EditorOperatorFormat()
{
this.DisplayName = "Z80 Assembly Operator";
this.BackgroundColor = Color.FromRgb(230,255,230);
this.ForegroundColor = Colors.Blue;
}
}
В общем-то, для такого небольшого результата - это практически всё что нужно знать.