在Visual Studio中搜索了很长时间来更改#region
指令文本颜色的简单方法之后,我得出结论,没有简单的方法可以做到这一点。
我知道如何更改#region
语句颜色,以及如何更改折叠的区域颜色,但我想使用区域描述更改文本的颜色。所以:
#region Some text <--- all this text should be in a different color
public void Test()
{
}
#endregion <--- this too
似乎很多人都在寻找这样的东西 - 请参阅如何在VS2008中更改扩展区域标题的颜色?。
因此,我一直在考虑编写一个简单的Visual Studio加载项来更改颜色。但是,它比我想象的要复杂得多,有Snapshot
、Tagger
、Classifier
、WpfTextViewCreationListener
、AdornmentLayer
等类。
简单地说,我不知道从哪里开始!我遵循了 MSDN 站点上的几个教程,但它们对于我尝试执行的操作来说似乎太复杂了。
有人可以指出我最简单的方法吗?即。我应该使用VS SDK中的哪些类/方法/事件。我不介意颜色是否无法通过 UI 等自定义。我正在使用VS2010。
编辑:刚刚有 mztools 网站推荐给我;我也去那里看看。还注意到 StackOverflow 对 region
s 的语法突出显示几乎正是我想要的!
我最终想出了一个解决方案,至少对于VS2010。虽然我已经使用它来着色"#region
"和"#endregion
"标签,但类似的解决方案应该适用于Visual Studio窗口中的任何文本内容。
似乎可以通过创建一个IViewTaggerProvider
来解决这类问题,该该将使用"分类"标记源代码的某些部分。Visual Studio将为使用该分类标记的文本提供样式,然后用户可以通过"工具">">选项...">"环境">"字体和颜色"将其更改为所需的样式。
标记器提供程序如下所示:
[Export(typeof(IViewTaggerProvider))]
[ContentType("any")]
[TagType(typeof(ClassificationTag))]
public sealed class RegionTaggerProvider : IViewTaggerProvider
{
[Import]
public IClassificationTypeRegistryService Registry;
[Import]
internal ITextSearchService TextSearchService { get; set; }
public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
{
if (buffer != textView.TextBuffer)
return null;
var classType = Registry.GetClassificationType("region-foreground");
return new RegionTagger(textView, TextSearchService, classType) as ITagger<T>;
}
}
这将创建一个ITagger
对象,给定 Visual Studio 文本视图,该对象将使用给定的分类类型标记文本的某些部分。请注意,这将适用于所有文本视图(即源代码编辑器、"查找结果"窗口等(。可以通过编辑 ContentType
属性来更改此设置(仅C#
分类类型(在本例中为"区域-前景"(定义为:
public static class TypeExports
{
[Export(typeof(ClassificationTypeDefinition))]
[Name("region-foreground")]
public static ClassificationTypeDefinition OrdinaryClassificationType;
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "region-foreground")]
[Name("region-foreground")]
[UserVisible(true)]
[Order(After = Priority.High)]
public sealed class RegionForeground : ClassificationFormatDefinition
{
public RegionForeground()
{
DisplayName = "Region Foreground";
ForegroundColor = Colors.Gray;
}
}
Order
属性确定何时应用分类,与其他分类(也可能适用于文本范围(相比。该DisplayName
将在">工具>选项..."对话框中使用。
定义分类后,ITagger
类可以搜索视图的文本,并为找到的文本的适用部分提供分类。
简单地说,它的工作是侦听文本视图的ViewLayoutChanged
事件,当提供的文本视图的内容发生变化时(例如,因为用户键入了某些内容(,该事件就会触发。
必须在文本中搜索它感兴趣的文本区域(称为"跨度"(。在这里,它返回包含 #region
或 #endregion
的行跨度。我一直保持简单,但用于查找匹配项的TextSearchService
也可以使用正则表达式进行搜索。
最后,为Visual Studio提供了一个方法来检索它找到的文本的标签,称为GetTags()
。对于给定的跨度集合,这将返回带有分类标签的文本跨度,即那些跨度中应以某种方式分类的区域。
它的代码是:
public sealed class RegionTagger : ITagger<ClassificationTag>
{
private readonly ITextView m_View;
private readonly ITextSearchService m_SearchService;
private readonly IClassificationType m_Type;
private NormalizedSnapshotSpanCollection m_CurrentSpans;
public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { };
public RegionTagger(ITextView view, ITextSearchService searchService, IClassificationType type)
{
m_View = view;
m_SearchService = searchService;
m_Type = type;
m_CurrentSpans = GetWordSpans(m_View.TextSnapshot);
m_View.GotAggregateFocus += SetupSelectionChangedListener;
}
private void SetupSelectionChangedListener(object sender, EventArgs e)
{
if (m_View != null)
{
m_View.LayoutChanged += ViewLayoutChanged;
m_View.GotAggregateFocus -= SetupSelectionChangedListener;
}
}
private void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
{
if (e.OldSnapshot != e.NewSnapshot)
{
m_CurrentSpans = GetWordSpans(e.NewSnapshot);
TagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(e.NewSnapshot, 0, e.NewSnapshot.Length)));
}
}
private NormalizedSnapshotSpanCollection GetWordSpans(ITextSnapshot snapshot)
{
var wordSpans = new List<SnapshotSpan>();
wordSpans.AddRange(FindAll(@"#region", snapshot).Select(regionLine => regionLine.Start.GetContainingLine().Extent));
wordSpans.AddRange(FindAll(@"#endregion", snapshot).Select(regionLine => regionLine.Start.GetContainingLine().Extent));
return new NormalizedSnapshotSpanCollection(wordSpans);
}
private IEnumerable<SnapshotSpan> FindAll(String searchPattern, ITextSnapshot textSnapshot)
{
if (textSnapshot == null)
return null;
return m_SearchService.FindAll(
new FindData(searchPattern, textSnapshot) {
FindOptions = FindOptions.WholeWord | FindOptions.MatchCase
});
}
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
if (spans == null || spans.Count == 0 || m_CurrentSpans.Count == 0)
yield break;
ITextSnapshot snapshot = m_CurrentSpans[0].Snapshot;
spans = new NormalizedSnapshotSpanCollection(spans.Select(s => s.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive)));
foreach (var span in NormalizedSnapshotSpanCollection.Intersection(m_CurrentSpans, spans))
{
yield return new TagSpan<ClassificationTag>(span, new ClassificationTag(m_Type));
}
}
}
为简洁起见,我省略了命名空间和 using 语句,它们通常采用 Microsoft.VisualStudio.Text.*
的形式。要使这些内容可用,必须先下载 Visual Studio 2010 SDK。
在过去的几个月里,我一直在使用这个解决方案,没有问题。
我注意到的一个限制是颜色不是"混合"的,因此不透明度小于 100% 的颜色不会"淡出"跨度中的现有颜色 - 这对于保留语法突出显示可能很有用。
我也不知道它的效率,因为它看起来会在每次按键时重复搜索文档。我还没有做过研究,看看Visual Studio是否以某种方式优化了这一点。我确实注意到Visual Studio在大文件(>~1000行(上的速度变慢,但我也使用Resharper,所以我不能将其单独归因于这个插件。
由于这主要是使用猜测进行编码的,因此我欢迎任何可以澄清或简化事情或提高代码性能的评论或代码更改。
我想你可以从Visual Studio Add-in项目开始,它将使用EnvDTE,它被认为是Visual Studio对象模型,请在这里找到MSDN文档:http://msdn.microsoft.com/en-us/vstudio/bb968855您可以通过EnvDTE控制Visual Studio行为,例如调试器,代码编辑器等。