13

在此链接的第一段末尾,它指出:

Visual Studio 文本大纲功能是通过使用投影缓冲区来隐藏折叠的文本来实现的,而用于 ASP.NET 页面的 Visual Studio 编辑器使用投影来支持 Visual Basic 和 C# 等嵌入式语言

我已经搜索和搜索,但根本没有找到任何示例或文档来完成此操作,有人知道这是如何完成的吗?我已经开始分类工作,并创建了一个我想要分类为 C# 代码的跨度的投影缓冲区。我将缓冲区上下文类型设置为“CSharp”,但跨度从未被分类。我也尝试将我的内容类型基于“投影”,但现在也可以。

4

4 回答 4

6

Visual Studio 中的投影缓冲区主要用于处理一种语言区域嵌入另一种语言的情况。经典的例子是 HTML 中的 CSS 和 Javascript。同样,ASP.NET 或 Razor 中的 C# 或 VB。事实上,HTML 编辑器可以处理多种语言,而且它的投影缓冲区架构非常可扩展(我写了很大一部分)。这样,样式块内的所有功能都由 CSS 编辑器处理,而 HTML 编辑器不必做太多事情。

当您了解它的工作原理时,投影缓冲区并不复杂。投影缓冲区形成一个图形,顶层缓冲区显示在视图中。投影缓冲区没有自己的内容,它由投影跨度组成,而投影跨度又是跟踪跨度(ITrackingSpan)或惰性区域(字符串)。

考虑 HTML 中的样式块。首先,您需要创建内容类型为“projection”或从“projection”派生的其他内容类型的投影缓冲区。然后创建投影缓冲区,该缓冲区将保存内容类型为“CSS”的 CSS。从磁盘读取的文件位于内容类型为“HTMLX”的文本缓冲区中(“HTML”内容类型为经典 Web 表单编辑器保留)。HTML 编辑器解析文件并将样式块内容以及内联样式提取到单独的字符串中。内联样式片段被装饰成类,因此它们在 CSS 编辑器中看起来格式良好。

现在构建了投影映射。第一个 CSS 投影缓冲区填充了惰性字符串(它们表示对用户不可见的 CSS,例如内联样式的装饰)以及在磁盘缓冲区 (HTML) 之外创建的跟踪跨度,这些跨度定义了对用户可见的区域 - 特别是样式的内容块。

然后构造视图(顶级)缓冲区的投影。这些投影是一个跟踪范围列表,它是从 CSS 编辑器投影缓冲区(不是 HTML 磁盘缓冲区)创建的跟踪范围和从 HTML 磁盘缓冲区创建的表示视图的 HTML 部分的跟踪范围的组合。

该图看起来大致是这样的

  View Buffer [ContentType = "projection"]
    |      \
    |     CSS Projection [ContentType = CSS]
    |      /
  Disk Buffer [ContentType = HTMLX]

对视图缓冲区的 HTML 部分所做的编辑反映到磁盘缓冲区,HTML 语言服务提供完成、语法检查等。在样式块中所做的编辑转到 CSS 项目缓冲区,CSS 编辑器提供完成和语法检查。它们还通过第二级投影反映到磁盘缓冲区。

现在,将命令转发到嵌入式语言(例如上下文菜单调用)并为 Javascript 或 C# 维护正确的断点映射是单独的代码。投影仅有助于与视图相关的事情,控制器链和调试器操作必须单独处理。HTML 编辑器命令控制器了解嵌入式语言,并根据插入符号的位置将命令向下转发到相应的语言服务。

于 2016-05-18T05:12:47.817 回答
5

我终于成功地将投影缓冲区嵌入到工具窗口中,并将它们连接到 C# 的语言服务。一个警告:这种方法仅适用于使用 Roslyn 的 Visual Studio。我已经发布了一个可供您使用的Github 项目以及随附的博客文章

您的问题的答案很长,并且涉及到太多移动的部分,因此它不适合 StackOverflow 的问答风格。话虽如此,我将总结必要的步骤并包含一些相关代码。

以下示例创建由文件的前 100 个字符组成的文件的投影缓冲区。

我们首先IVsInvisibleEditor为给定的文件路径创建一个并为其创建一个代码窗口。我们将此代码窗口的内容设置IVsTextLinesIVsInvisibleEditor.

"CustomProjectionRole"然后我们在这个代码窗口的文本缓冲区上设置一个自定义角色。此角色允许我们通过导出的 MEF 自定义文本缓冲区ITextViewModelProvider

public IWpfTextViewHost CreateEditor(string filePath, int start = 0, int end = 100)
{
    //IVsInvisibleEditors are in-memory represenations of typical Visual Studio editors.
    //Language services, highlighting and error squiggles are hooked up to these editors
    //for us once we convert them to WpfTextViews. 
    var invisibleEditor = GetInvisibleEditor(filePath);

    var docDataPointer = IntPtr.Zero;
    Guid guidIVsTextLines = typeof(IVsTextLines).GUID;

    ErrorHandler.ThrowOnFailure(invisibleEditor.GetDocData(
        fEnsureWritable: 1
        , riid: ref guidIVsTextLines
        , ppDocData: out docDataPointer));

    IVsTextLines docData = (IVsTextLines)Marshal.GetObjectForIUnknown(docDataPointer);

    //Create a code window adapter
    var codeWindow = _editorAdapter.CreateVsCodeWindowAdapter(VisualStudioServices.OLEServiceProvider);
    ErrorHandler.ThrowOnFailure(codeWindow.SetBuffer(docData));

    //Get a text view for our editor which we will then use to get the WPF control for that editor.
    IVsTextView textView;
    ErrorHandler.ThrowOnFailure(codeWindow.GetPrimaryView(out textView));

    //We add our own role to this text view. Later this will allow us to selectively modify
    //this editor without getting in the way of Visual Studio's normal editors.
    var roles = _editorFactoryService.DefaultRoles.Concat(new string[] { "CustomProjectionRole" });

    var vsTextBuffer = docData as IVsTextBuffer;
    var textBuffer = _editorAdapter.GetDataBuffer(vsTextBuffer);

    textBuffer.Properties.AddProperty("StartPosition", start);
    textBuffer.Properties.AddProperty("EndPosition", end);
    var guid = VSConstants.VsTextBufferUserDataGuid.VsTextViewRoles_guid;
    ((IVsUserData)codeWindow).SetData(ref guid, _editorFactoryService.CreateTextViewRoleSet(roles).ToString());

    _currentlyFocusedTextView = textView;
    var textViewHost = _editorAdapter.GetWpfTextViewHost(textView);
    return textViewHost;
}

我们现在创建一个IVsTextViewModelProvider创建并返回一个ProjectionTextViewModel. 这ProjectionTextViewModel会在其可视缓冲区中保存一个投影缓冲区。这意味着当这个缓冲区被显示时,投影缓冲区就是所显示的。但是,后备数据缓冲区的语言服务运行正常。

[Export(typeof(ITextViewModelProvider)), ContentType("CSharp"), TextViewRole("CustomProjectionRole")]
internal class ProjectionTextViewModelProvider : ITextViewModelProvider
{
    public ITextViewModel CreateTextViewModel(ITextDataModel dataModel, ITextViewRoleSet roles)
    {
        //Create a projection buffer based on the specified start and end position.
        var projectionBuffer = CreateProjectionBuffer(dataModel);
        //Display this projection buffer in the visual buffer, while still maintaining
        //the full file buffer as the underlying data buffer.
        var textViewModel = new ProjectionTextViewModel(dataModel, projectionBuffer);
        return textViewModel;

    }

    public IProjectionBuffer CreateProjectionBuffer(ITextDataModel dataModel)
    {
        //retrieve start and end position that we saved in MyToolWindow.CreateEditor()
        var startPosition = (int)dataModel.DataBuffer.Properties.GetProperty("StartPosition");
        var endPosition = (int)dataModel.DataBuffer.Properties.GetProperty("EndPosition");
        var length = endPosition - startPosition;

        //Take a snapshot of the text within these indices.
        var textSnapshot = dataModel.DataBuffer.CurrentSnapshot;
        var trackingSpan = textSnapshot.CreateTrackingSpan(startPosition, length, SpanTrackingMode.EdgeExclusive);

        //Create the actual projection buffer
        var projectionBuffer = ProjectionBufferFactory.CreateProjectionBuffer(
            null
            , new List<object>() { trackingSpan }
            , ProjectionBufferOptions.None
            );
        return projectionBuffer;
    }


    [Import]
    public IProjectionBufferFactoryService ProjectionBufferFactory { get; set; }
}

希望这能让任何未来的访客有一个良好的开端。

于 2014-08-02T00:28:43.207 回答
1

可以在此处找到一个工作示例,但根据警告评论,这不是一项简单的任务。

// Abandon all hope ye who enters here.
// https://twitter.com/Schabse/status/393092191356076032
// https://twitter.com/jasonmalinowski/status/393094145398407168

// Based on decompiled code from Microsoft.VisualStudio.Html.ContainedLanguage.Server
// Thanks to Jason Malinowski for helping me navigate this mess.
// All of this can go away when the Roslyn editor ships.
于 2014-06-01T12:21:48.963 回答
0

您留下的评论似乎在谈论大纲,但不支持嵌入式语言。如果你想隐藏文本,你可以看看这个IntraText Code

如果您通过将启动项目设置为 devenv 并将命令 args 设置为 /RootSuffix Exp 来下载并安装或调试它,则可以打开一个文本文件并键入 #CCCCCC 或任何其他十六进制网络颜色。代码将折叠并显示一个简单的图形。

据我所知,关于支持嵌入式语言,您必须编写自己的自定义语言服务。我希望能够支持现有的语言服务智能感知功能。但是,当涉及到 Visual Studio 中的这种行为时,您似乎必须重新发明轮子。

于 2014-05-31T08:46:00.583 回答