我正在开发自定义语言服务。该语言的代码文件包含元数据和源代码,因此元数据存储为标题(在文件的开头;不能更改此要求)。语言服务提供两个逻辑视图;一个用于代码,另一个用于编辑元数据。
现在,问题是我想隐藏代码编辑器视图中的元数据 - 我认为使用投影实现它是可行的方法,但是Visual Studio SDK既没有包含任何示例,也没有关于如何实现实现这样的功能。关于这个概念的简短解释见:Inside the Editor, Projection
我还在Codeplex找到了一个语言服务项目:Python Tools for Visual Studio,它支持Django(在这个项目中,投影缓冲区用于混合HTML标记和Python代码),但这似乎是一个不同的场景。 。
这就是我试过的......
在我的编辑器工厂中,我创建了一个IVsTextLines
实例,代码编辑器视图使用以下代码...
private IVsTextLines GetTextBuffer(IntPtr docDataExisting)
{
if (docDataExisting == IntPtr.Zero)
{
Type t = typeof(IVsTextLines);
Guid clsId = typeof(VsTextBufferClass).GUID;
Guid interfaceId = t.GUID;
IVsTextLines bufferInstance;
if ((bufferInstance = this.Package.CreateInstance(ref clsId, ref interfaceId, t) as IVsTextLines) != null)
{
object oleServiceProvider = this.GetService(typeof(IServiceProvider));
var withSite = (IObjectWithSite)bufferInstance;
withSite.SetSite(oleServiceProvider);
return bufferInstance;
}
}
return null;
}
据我所见,IVsTextLines
界面与IProjectionBuffer
,ITextBuffer
等不兼容。我从Python Tools项目中了解到,我可以使用IVsEditorAdaptersFactoryService
从ITextBuffer
对象获取IVsTextLines
实例;适配器工厂服务(以及一些其他必需的服务实例)通过MEF注入我的编辑器工厂......
IVsTextBuffer textBuffer = this.GetTextBuffer(...);
ITextBuffer documentBuffer = this.editorAdaptersFactoryService.GetDocumentBuffer(textBuffer);
ITextBuffer
可以用作投影的源缓冲区...我的想法是创建一个必须跨越的省略缓冲区;一个用于元数据,一个用于代码,因此将省略元数据跨度,这使得代码消失,并使用此省略缓冲区作为投影。这就是我试过的......
private ITextBuffer CreateBuffer(
IProjectionEditResolver editResolver,
ITextBuffer documentBuffer)
{
IContentType contentType = this.ContentTypeRegistryService.GetContentType("...");
ITextSnapshot currentSnapshot = documentBuffer.CurrentSnapshot;
string text = currentSnapshot.GetText(0, currentSnapshot.Length);
TextRange textRange = Parser.GetMetadataTextRange(text); // parse code; return a text range representing the code to elide
ITrackingSpan elideSpan = currentSnapshot.CreateTrackingSpan(textRange.Start, textRange.Length, SpanTrackingMode.EdgeExclusive);
int offset = textRange.Start + textRange.Length;
ITrackingSpan expandSpan = currentSnapshot.CreateTrackingSpan(offset, currentSnapshot.Length - offset, SpanTrackingMode.EdgeInclusive);
SnapshotSpan elide = elideSpan.GetSpan(currentSnapshot);
SnapshotSpan expand = expandSpan.GetSpan(currentSnapshot);
var snapshotSpans = new List<SnapshotSpan>
{
elide,
expand
};
var spanCollection = new NormalizedSnapshotSpanCollection(snapshotSpans);
const ElisionBufferOptions BufferOptions = ElisionBufferOptions.FillInMappingMode;
IElisionBuffer buffer = this.ProjectionBufferFactoryService.CreateElisionBuffer(editResolver, spanCollection, BufferOptions, contentType);
buffer.ModifySpans(new NormalizedSpanCollection(elide), new NormalizedSpanCollection(expand));
return buffer;
}
创建elision缓冲区后,我使用SetDataBuffer
方法将其与编辑器连接...
ITextBuffer buffer = this.CreateBuffer(..., documentBuffer);
this.editorAdaptersFactory.SetDataBuffer(textBuffer, buffer);
这适用于某些家庭;至少元数据代码从代码编辑器中消失了,但是一旦尝试编辑代码,我得到InvalidOperationException
,如果我只用一个跨度创建elision缓冲区,或者如果我不隐藏elideSpan
。
向ActivityLog
报告以下错误:
System.InvalidOperationException: Shim buffer length mismatch. Document=11594 Surface=11577 Difference at 0
 at
Microsoft.VisualStudio.Editor.Implementation.VsTextBufferAdapter.OnTextBufferChanged(Object sender, TextContentChangedEventArgs e)
 at
Microsoft.VisualStudio.Text.Utilities.GuardedOperations.RaiseEvent[TArgs](Object sender, EventHandler`1 eventHandlers, TArgs args)
我认为我非常接近最终解决方案;那么我做错了什么?