我尝试在我的Visual Studio编辑器中为C#代码仅为某些关键字添加自定义着色。我希望能够将任何实现IDisposable
的类型着色为不同的颜色。理想情况下,我想在我可以编辑的某种配置中创建一个简单的类/接口列表,这些类/接口派生自IDisposable
。 (虽然如果你说有一个方法/插件会自动找到所有的一次性类型并独立地为它们着色,那就是圣杯)。
我做了大量的研究,它看起来像一个编辑分类器"扩展可能会成功。然而,我创建了一个仅仅试图为单词" Stream"虽然它确实击中了我试图突出显示该单词的代码,但它并没有在编辑器中突出显示。
我已将我的VS扩展程序添加到Github here
这看起来真的应该是相当简单的,但我已经在这个问题上找到了许多小巷,但却发现了死胡同。有没有更简单的方法来做到这一点,还是我的扩展被破坏了?
更新
很奇怪。我刚刚再次运行我的扩展程序,虽然它没有突出显示编辑器中的文本,但它突出显示了" Stream"在鼠标悬停在类型/变量上的弹出文本中!有没有办法让它适用于编辑器?
答案 0 :(得分:3)
根据您是否正在使用Jetbrains Resharper,您可以为此编写插件。这样,您不仅可以在变量上添加IDisposable的可视通知,而且还可以提供快速修正,当且仅当它没有被调用时,这就是我想要捕获的内容。请注意,我可以想象已经有一个R#插件。我知道我也考虑过这一点,但是我懒得为它写一个插件。
不要误会我的意思 - 如果您还没有使用r#,那么您应该考虑尝试一下。
除此之外,您还可以使用此功能:API-QuickFix
还有一些方法来定义自定义关键字,就像resharper一样,由自定义标记给出并对其应用快速修正。
PS:不,我不在Jetbrains工作。它就是那么好了:)更新:
潜在的VS扩展修复?
检查一下:MSDN Link Highlighting Text
我尝试打开你的github项目,但是我不知道我只会检查msdn。你似乎是从错误的阶级派生出来满足你的需求?
MSDN关键字"编辑器 - 扩展编辑器 - 演练:突出显示文本"
我知道SO需要网站上的代码,但msdn链接下降是不太可能的,并且使用给定的信息可以很容易地找到内容:)
答案 1 :(得分:2)
我参加派对有点晚了,但是,嘿,为什么不把我的2美分扔进去。
正如您在问题中解释的那样,您的项目有两个基本部分:
IDisposable
第一个是迄今为止最困难的,但并非不可能。基于单词列表的方法可能是最简单的,但是Roslyn应该可以在运行中弄清楚哪些类继承IDisposible
。
您还可以在构建后在后台加载项目的已编译的.exe / .dll并找出其中的类型,但您仍然需要编写一些神奇的胶水代码,以找出代码中的哪些短类名称引用程序集中的实际全名类。
第二部分,突出显示,一旦你知道如何做就很容易(这有助于我花了最后几个月全职工作来扩展VS)。当然,使用Visual Studio,没有什么比它看起来那么简单(尽管微软努力使其变得用户友好)。所以,我已经构建了一个示例扩展,它突出了名为" Stream"在C#文件中,可以帮助您入门。
相关代码如下,完整project source is on GitHub)。它从分类标记提供者开始:
[Export(typeof(ITaggerProvider))]
[ContentType("CSharp")]
[TagType(typeof(ClassificationTag))]
[Name("HighlightDisposableTagger")]
public class HighlightDisposableTaggerProvider : ITaggerProvider
{
[Import]
private IClassificationTypeRegistryService _classificationRegistry = null;
[Import]
private IClassifierAggregatorService _classifierAggregator = null;
private bool _reentrant;
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
if (_reentrant)
return null;
try {
_reentrant = true;
var classifier = _classifierAggregator.GetClassifier(buffer);
return new HighlightDisposableTagger(buffer, _classificationRegistry, classifier) as ITagger<T>;
}
finally {
_reentrant = false;
}
}
}
然后是标记器本身:
public class HighlightDisposableTagger : ITagger<ClassificationTag>
{
private const string DisposableFormatName = "HighlightDisposableFormat";
[Export]
[Name(DisposableFormatName)]
public static ClassificationTypeDefinition DisposableFormatType = null;
[Export(typeof(EditorFormatDefinition))]
[Name(DisposableFormatName)]
[ClassificationType(ClassificationTypeNames = DisposableFormatName)]
[UserVisible(true)]
public class DisposableFormatDefinition : ClassificationFormatDefinition
{
public DisposableFormatDefinition()
{
DisplayName = "Disposable Format";
ForegroundColor = Color.FromRgb(0xFF, 0x00, 0x00);
}
}
public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { };
private ITextBuffer _subjectBuffer;
private ClassificationTag _tag;
private IClassifier _classifier;
private bool _reentrant;
public HighlightDisposableTagger(ITextBuffer subjectBuffer, IClassificationTypeRegistryService typeService, IClassifier classifier)
{
_subjectBuffer = subjectBuffer;
var classificationType = typeService.GetClassificationType(DisposableFormatName);
_tag = new ClassificationTag(classificationType);
_classifier = classifier;
}
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
if (_reentrant) {
return Enumerable.Empty<ITagSpan<ClassificationTag>>();
}
var tags = new List<ITagSpan<ClassificationTag>>();
try {
_reentrant = true;
foreach (var span in spans) {
if (span.IsEmpty)
continue;
foreach (var token in _classifier.GetClassificationSpans(span)) {
if (token.ClassificationType.IsOfType(/*PredefinedClassificationTypeNames.Identifier*/ "User Types")) {
// TODO: Somehow figure out if this refers to a class which implements IDisposable
if (token.Span.GetText() == "Stream") {
tags.Add(new TagSpan<ClassificationTag>(token.Span, _tag));
}
}
}
}
return tags;
}
finally {
_reentrant = false;
}
}
}
我只在VS2010上对此进行了测试,但它也应该适用于VS2013(唯一可能不同的是类别分类名称,但是通过良好放置的断点很容易发现)。我从来没有写过VS2012的扩展程序,因此我无法对此发表评论,但我知道它在大多数方面与VS2013非常接近。
答案 2 :(得分:1)
所以,一个可能的解决方案(我相信这个有效):
1)创建自己的内容类型,该类型继承自csharp。
2)创建新的TextViewCreationListener,它将使用您自己的内容替换所有“csharp”内容类型,从而可能“撤消”所有其他分类器。
3)注册您的分类器以处理您自己的内容类型。
以下是一些代码:
[Export(typeof(IVsTextViewCreationListener))]
[ContentType("csharp")]
[TextViewRole(PredefinedTextViewRoles.Editable)]
class TextViewCreationListener : IVsTextViewCreationListener {
internal readonly IVsEditorAdaptersFactoryService _adaptersFactory;
[Import] internal IContentTypeRegistryService ContentTypeRegistryService = null;
[ImportingConstructor]
public TextViewCreationListener(IVsEditorAdaptersFactoryService adaptersFactory) {
_adaptersFactory = adaptersFactory;
}
#region IVsTextViewCreationListener Members
public void VsTextViewCreated(VisualStudio.TextManager.Interop.IVsTextView textViewAdapter) {
var textView = _adaptersFactory.GetWpfTextView(textViewAdapter);
var myContent = ContentTypeRegistryService.GetContentType(MyContentType);
if(myContent == null)
{
ContentTypeRegistryService.AddContentType(MyContentType, new[] {"csharp"});
myContent = ContentTypeRegistryService.GetContentType(MyContentType);
}
// some kind of check if the content type is not already MyContentType.
textView.TextBuffer.ChangeContentType(myContent, null);
}
#endregion
}
现在,只需修改IClassifierProvider即可注册您自己的内容类型,如下所示:[ContentType(MyContentType)]
在你自己的IClassifier中,你基本上可以进行自己的计算,一旦你认为你无法处理这些东西,就可以将控制传递给其他分类器。
如果您使用MEF并导入IClassifierAggregatorService
,您可以获得一个“MASTER-classifier”,它将为您运行所有逻辑。我还没有实现它,但我在过去提出类似的建议,似乎有效。或者,您可以将[ImportMany]
与List<IClassifier>
一起使用,并过滤掉csharp的内容?!