在编辑器中

编辑器由多个不同的子系统组成,旨在使编辑器文本模型与文本视图和用户界面分开。

这些部分介绍编辑器的不同方面:

这些部分介绍编辑器的功能:

子系统

文本模型子系统

文本模型子系统负责表示文本并启用其操作。 文本模型子系统包含 ITextBuffer 接口,该接口描述编辑器要显示的字符序列。 可以通过多种方式修改、跟踪和操作此文本。 文本模型还提供以下方面的类型:

  • 将文本与文件关联的服务,并管理在文件系统中读取和写入它们。

  • 一种差异服务,用于查找两个对象序列之间的最小差异。

  • 一个系统,用于描述缓冲区中其他缓冲区中文本子集的文本。

文本模型子系统没有用户界面(UI)概念。 例如,它不负责文本格式或文本布局,并且它不知道可能与文本关联的视觉装饰。

文本模型子系统的公共类型包含在 Microsoft.VisualStudio.Text.Data.dllMicrosoft.VisualStudio.CoreUtility.dll中,这仅依赖于 .NET Framework 基类库和托管扩展性框架(MEF)。

文本视图子系统

文本视图子系统负责设置文本格式和显示文本。 此子系统中的类型分为两个层,具体取决于类型是否依赖于 Windows Presentation Foundation (WPF)。 最重要的类型是 ITextViewIWpfTextView控制要显示的文本行集,以及插入符号、选择以及用于使用 WPF UI 元素装饰文本的设施。 此子系统还提供文本显示区域周围的边距。 可以扩展这些边距,并且可以包含不同类型的内容和视觉效果。 边距的示例包括行号显示和滚动条。

文本视图子系统的公共类型包含在 Microsoft.VisualStudio.Text.UI.dllMicrosoft.VisualStudio.Text.UI.Wpf.dll中。 第一个程序集包含独立于平台的元素,第二个程序集包含特定于 WPF 的元素。

分类子系统

分类子系统负责确定文本的字体属性。 分类器将文本拆分为不同的类,例如“关键字 (keyword)”或“comment”。 分类格式映射将这些类与实际的字体属性(例如“Blue Consolas 10 pt”)相关联。 当文本视图设置文本格式并呈现文本时,将使用此信息。 标记在本主题后面更详细地介绍,使数据能够与文本跨度相关联。

分类子系统的公共类型包含在Microsoft.VisualStudio.Text.Logic.dll中,它们与Microsoft.VisualStudio.Text.UI.Wpf.dll中包含的分类视觉方面进行交互。

操作子系统

操作子系统定义编辑器行为。 它为 Visual Studio 编辑器命令和撤消系统提供实现。

仔细查看文本模型和文本视图

文本模型

文本模型子系统由不同的文本类型分组组成。 其中包括文本缓冲区、文本快照和文本跨度。

文本缓冲区和文本快照

ITextBuffer 接口表示使用 UTF-16 编码的 Unicode 字符序列,这是 .NET Framework 中类型使用的 String 编码。 文本缓冲区可以保留为文件系统文档,但这不是必需的。

用于 ITextBufferFactoryService 创建空文本缓冲区,或从字符串或从 TextReader中初始化的文本缓冲区。 文本缓冲区可以保留为文件系统 ITextDocument

任何线程都可以编辑文本缓冲区,直到线程通过调用获取 TakeThreadOwnership文本缓冲区的所有权。 之后,只有该线程才能执行编辑。

文本缓冲区可以在其生存期内经历许多版本。 每次编辑缓冲区时都会生成新版本,不可变 ITextSnapshot 表示该版本的缓冲区的内容。 由于文本快照是不可变的,因此可以在任何线程上访问文本快照,即使它表示的文本缓冲区继续更改也是如此。

文本快照和文本快照行

可以将文本的内容快照为字符序列或行序列。 字符和行都从零开始编制索引。 空文本快照包含零个字符和一个空行。 行由任何有效的 Unicode 换行字符序列或缓冲区的开头或结尾分隔。 换行符在文本快照中显式表示,文本中的换行符快照不必相同。

注意

有关 Visual Studio 编辑器中的换行符的详细信息,请参阅 编码和换行符

文本行由对象ITextSnapshotLine表示,该对象可从特定行号或特定字符位置的文本快照获取。

SnapshotPoints、SnapshotSpans 和 NormalizedSnapshotSpanCollections

A SnapshotPoint 表示快照中的字符位置。 该位置可以保证介于零和快照的长度之间。 一SnapshotSpan个表示快照中的文本范围。 其结束位置保证介于零和快照的长度之间。 由NormalizedSnapshotSpanCollection同一快照中的一组SnapshotSpan对象组成。

Spans 和 NormalizedSpanCollections

表示Span可应用于文本快照中文本范围的间隔。 快照位置从零开始,因此范围可以从任何位置开始,包括零。 End范围的属性等于其Start属性及其属性的总和Length。 A Span 不包括由属性编制索引的 End 字符。 例如,具有 Start=5 且 Length=3 的范围具有 End=8,并且包含位置 5、6 和 7 处的字符。 此范围的表示法为 [5..8]。

两个跨度相交(如果它们有任何共同位置,包括 End 位置)。 因此,[3、5)和[2,7)的交集是[3,5),[3,5)和[5,7)的交集是[5,5)。 (请注意 [5, 5) 是空跨度。

如果两个跨度具有共同位置,则两个跨度重叠,但结束位置除外。 空跨度永远不会重叠任何其他范围,并且两个跨度的重叠从不为空。

A NormalizedSpanCollection 是范围列表,其顺序为范围开始属性。 在列表中,将合并重叠或滥用范围。 例如,鉴于范围集 [5..9)、[0..1)、[3..6) 和 [9..10),规范化范围列表为 [0..1)、[3..10)。

ITextEdit、TextVersion 和文本更改通知

可以使用对象更改 ITextEdit 文本缓冲区的内容。 创建此类对象(通过使用其中一种 CreateEdit() 方法 ITextBuffer)将启动由文本编辑组成的文本事务。 每次编辑都是用字符串替换缓冲区中的一些文本范围。 启动事务时,每个编辑的坐标和内容都相对于缓冲区的快照表示。 该 ITextEdit 对象调整受同一事务中其他编辑影响的编辑坐标。

例如,请考虑包含此字符串的文本缓冲区:

abcdefghij

应用包含两个编辑的事务,一个编辑使用字符替换 [2..4] 中的范围,另一个编辑使用字符XY替换 [6..9] 中的范围。 结果是此缓冲区:

abXefYj

第二次编辑的坐标是在应用第一次编辑之前,根据事务开头缓冲区的内容计算的。

通过调用Apply()该对象的方法提交对象时,ITextEdit对缓冲区所做的更改将生效。 如果至少有一个非空编辑,则会创建一个新 ITextVersion 内容,创建一个新 ITextSnapshot 内容,并引发一个 Changed 事件。 每个文本版本都有不同的文本快照。 文本快照表示编辑事务后文本缓冲区的完整状态,但文本版本仅描述从一个快照到下一个更改。 一般情况下,文本快照将一次使用,然后取消卡,而文本版本必须保持活动状态一段时间。

文本版本包含一个 INormalizedTextChangeCollection。 此集合描述应用于快照时生成后续快照的更改。 集合中的每 ITextChange 一个都包含更改的字符位置、替换的字符串和替换字符串。 替换的字符串对于基本插入为空,替换字符串对于基本删除为空。 规范化集合始终 null 适用于最新版本的文本缓冲区。

任何时候只能为文本缓冲区实例化一个 ITextEdit 对象,并且所有文本编辑都必须在拥有文本缓冲区的线程上执行(如果已声明所有权)。 可以通过调用其 Cancel 方法或其 Dispose 方法来放弃文本编辑。

ITextBufferInsert()Replace()Delete()还提供了类似于接口上ITextEdit找到的方法。 调用这些对象的效果与创建 ITextEdit 对象的效果相同,进行类似的调用,然后应用编辑。

跟踪点和跟踪范围

ITrackingPoint 个表示文本缓冲区中的字符位置。 如果以导致字符位置移位的方式编辑缓冲区,跟踪点会随其移动。 例如,如果跟踪点引用缓冲区中的位置 10,并在缓冲区开头插入 5 个字符,则跟踪点将引用位置 15。 如果插入恰好发生在跟踪点所表示的位置,则其行为由其PointTrackingMode确定,可以是或PositiveNegative。 如果跟踪模式为正,跟踪点将引用相同的字符,该字符现在位于插入的末尾。 如果跟踪模式为负数,跟踪点指原始位置的第一个插入字符。 如果删除跟踪点所表示位置的字符,跟踪点将移动到删除范围之后的第一个字符。 例如,如果跟踪点引用位置 5 处的字符,并且删除位置 3 到 6 处的字符,则跟踪点指位置 3 处的字符。

一个 ITrackingSpan 表示一系列字符,而不是只表示一个位置。 其行为由其 SpanTrackingMode确定。 如果范围跟踪模式为 SpanTrackingMode.EdgeInclusive,跟踪范围将增大以合并在其边缘插入的文本。 如果跨度跟踪模式为 SpanTrackingMode.EdgeExclusive,跟踪范围不会包含在其边缘插入的文本。 但是,如果跨度跟踪模式为 SpanTrackingMode.EdgePositive,则插入将当前位置推送到开始位置,如果跨度跟踪模式为 SpanTrackingMode.EdgeNegative,则插入会将当前位置推送到末尾。

你可以获取跟踪点的位置,也可以获取其所属文本缓冲区的任何快照的跟踪范围。 可以从任何线程安全地引用跟踪点和跟踪范围。

内容类型

内容类型是定义不同类型的内容的机制。 内容类型可以是文件类型,例如“text”、“code”或“binary”,也可以是技术类型,如“xml”、“vb”或“c#”。 例如,“using”一词是 C# 和 Visual Basic 中的关键字 (keyword),但不在其他编程语言中。 因此,此关键字 (keyword)的定义将限制为“c#”和“vb”内容类型。

内容类型用作编辑器的装饰和其他元素的筛选器。 每个内容类型定义了许多编辑器功能和扩展点。 例如,纯文本文件、XML 文件和 Visual Basic 源代码文件的文本着色不同。 创建文本缓冲区时通常会为其分配内容类型,并且可以更改文本缓冲区的内容类型。

内容类型可以多继承自其他内容类型。 使用该 ContentTypeDefinition 函数可将多个基类型指定为给定内容类型的父类型。

开发人员可以定义自己的内容类型,并使用 &0> 注册它们。 许多编辑器功能都可以通过使用 ContentTypeAttribute 来定义特定内容类型。 例如,可以定义编辑器边距、装饰和鼠标处理程序,以便它们仅适用于显示特定内容类型的编辑器。

文本视图

模型视图控制器(MVC)模式的视图部分定义文本视图、视图的格式、图形元素(如滚动条)和插入符号。 Visual Studio 编辑器的所有呈现元素都基于 WPF。

文本视图

ITextView 接口是文本视图的平台无关的表示形式。 它主要用于在窗口中显示文本文档,但也可用于其他目的,例如在工具提示中。

文本视图引用不同类型的文本缓冲区。 该 TextViewModel 属性引用指向这三个不同的 ITextViewModel 文本缓冲区的对象:数据缓冲区,即顶级数据级缓冲区、编辑缓冲区、在其中进行编辑的缓冲区,以及显示在文本视图中的缓冲区。

文本的格式基于附加到基础文本缓冲区的分类器,并使用附加到文本视图本身的装饰提供程序进行装饰。

文本视图坐标系

文本视图坐标系指定文本视图中的位置。 在此坐标系中,x 值 0.0 对应于要显示的文本的左边缘,y 值 0.0 对应于要显示的文本的上边缘。 x 坐标从左到右增加,y 坐标从上到下增加。

视区(文本窗口中可见的文本部分)不能以与垂直滚动相同的方式水平滚动。 视区通过更改其左坐标水平滚动,使其相对于绘图图面移动。 但是,只能通过更改呈现的文本来垂直滚动视区,这会导致 LayoutChanged 引发事件。

坐标系中的距离对应于逻辑像素。 如果在没有缩放转换的情况下显示文本呈现图面,则文本呈现坐标系中的一个单位对应于屏幕上的一个像素。

边距

ITextViewMargin 接口表示边距,并支持控制边距及其大小的可见性。 有四个预定义边距,它们名为“Top”、“Left”、“Right”和“Bottom”,并附加到视图的上边缘、下边缘、左边缘或右边缘。 这些边距是可以放置其他边距的容器。 该接口定义返回边距大小和边距可见性的方法。 边距是视觉元素,提供有关附加它们的文本视图的其他信息。 例如,行号边距显示文本视图的行号。 字形边距显示 UI 元素。

IWpfTextViewMarginProvider 接口处理边距的创建和放置。 可以针对其他边距对边距进行排序。 优先级较高的边距靠近文本视图。 例如,如果有两个左边距 A 和边距 B,而边距 B 的优先级比边距 A 低,则边距 B 显示在边距 A 的左侧。

文本视图主机

IWpfTextViewHost 接口包含文本视图和任何伴随视图的修饰,例如滚动条。 文本视图主机还包含附加到视图边框的边距。

带格式文本

文本视图中显示的文本由 ITextViewLine 对象组成。 每个文本视图行对应于文本视图中的一行文本。 基础文本缓冲区中的长行可以部分遮盖(如果未启用换行),也可以分解成多个文本视图行。 该 ITextViewLine 接口包含用于坐标和字符之间映射的方法和属性,以及可能与该行关联的装饰。

ITextViewLine 对象是使用 IFormattedLineSource 接口创建的。 如果只是担心视图中当前显示的文本,则可以忽略格式设置源。 如果你对视图中未显示的文本格式感兴趣(例如,要支持格式文本剪切和粘贴),则可以用于 IFormattedLineSource 设置文本缓冲区中的文本格式。

文本视图一次格式化一个 ITextSnapshotLine

编辑器功能

编辑器的功能设计为使功能的定义与其实现分开。 编辑器包括以下功能:

  • 标记和分类器

  • 装饰品

  • 投影

  • 大纲显示

  • 鼠标和键绑定

  • 操作和基元

  • IntelliSense

标记和分类器

标记是与文本范围关联的标记。 可以通过不同的方式显示它们,例如,使用文本着色、下划线、图形或弹出窗口。 分类器是一种标记。

其他类型的标记用于 TextMarkerTag 文本突出显示、 OutliningRegionTag 大纲显示和 ErrorTag 编译错误。

分类类型

IClassificationType接口表示等效类,该类是文本的抽象类别。 分类类型可以多继承自其他分类类型。 例如,编程语言分类可能包括“关键字 (keyword)”、“comment”和“identifier”,所有这些分类都继承自“code”。 自然语言分类类型可能包括“名词”、“动词”和“形容词”,这些类型都继承自“自然语言”。

分类

分类是特定分类类型的实例,通常跨越文本范围。 A ClassificationSpan 用于表示分类。 分类范围可视为涵盖特定文本范围的标签,并告知系统此文本跨度是特定分类类型。

分类器

IClassifier这是一种将文本分解为一组分类的机制。 必须为特定内容类型定义分类器,并为特定文本缓冲区实例化。 客户端必须实现 IClassifier 才能参与文本分类。

分类器聚合器

分类器聚合器是一种机制,可将一个文本缓冲区的所有分类器合并到一组分类中。 例如,C# 分类器和英语分类器都可以在 C# 文件中通过注释创建分类。 请考虑以下注释:

// This method produces a classifier

C# 分类器可能会将整个范围标记为注释,而英语分类器可能会将“生成”分类器分类为“动词”和“方法”作为“名词”。 聚合器生成一组非重叠分类,集的类型基于所有贡献。

分类器聚合器也是分类器,因为它将文本拆分为一组分类。 分类器聚合器还确保没有重叠的分类,并且分类已排序。 单个分类器可以随意返回任意一组分类,并以任何方式重叠。

分类格式设置和文本着色

文本格式是基于文本分类构建的功能的示例。 文本视图层使用它来确定应用程序中文本的显示。 文本格式区域取决于 WPF,但分类的逻辑定义并不多。

分类格式是特定分类类型的一组格式属性。 这些格式继承自分类类型的父级的格式。

IClassificationFormatMap是分类类型到一组文本格式属性的映射。 编辑器中格式映射的实现处理分类格式的所有导出。

装饰品

装饰是与文本视图中字符的字体和颜色不直接相关的图形效果。 例如,用于标记许多编程语言中的非编译代码的红色波浪线是嵌入装饰,工具提示是弹出装饰。 装饰派生自 UIElement 并实现 ITag。 两种专用类型的装饰标记是 SpaceNegotiatingAdornmentTag,对于占用与视图中文本相同的空间的装饰,以及 ErrorTag波浪下划线的装饰。

嵌入装饰是构成格式化文本视图一部分的图形。 它们按不同的 Z 顺序层进行组织。 有三个内置层,如下所示:文本、插入符号和选定内容。 但是,开发人员可以定义更多层,并按照彼此的顺序排列。 三种嵌入装饰是文本相对装饰(在文本移动时移动,在删除文本时删除)、视图相对装饰(这与视图的非文本部分有关)和所有者控制的装饰(开发人员必须管理其放置)。

弹出窗口装饰是显示在文本视图上方的小窗口中的图形,例如工具提示。

投影

投影是一种用于构造不同类型的文本缓冲区的技术,该缓冲区实际上不存储文本,而是合并其他文本缓冲区中的文本。 例如,投影缓冲区可用于连接其他两个缓冲区中的文本,并显示结果,就好像它只位于一个缓冲区中,或者隐藏一个缓冲区中的部分文本。 投影缓冲区可以充当另一个投影缓冲区的源缓冲区。 可以通过多种不同的方式构造一组由投影相关的缓冲区以重新排列文本。 (此类集也称为 缓冲区图。Visual Studio 文本大纲显示功能通过使用投影缓冲区隐藏折叠的文本来实现,ASP.NET 页面的 Visual Studio 编辑器使用投影来支持嵌入式语言,如 Visual Basic 和 C# 。

使用 IProjectionBufferIProjectionBufferFactoryService.. 投影缓冲区由称为源范围的对象的有序序列ITrackingSpan表示。 这些范围的内容显示为字符序列。 从中绘制源范围的文本缓冲区命名 为源缓冲区。 投影缓冲区的客户端不必知道它与普通文本缓冲区不同。

投影缓冲区侦听源缓冲区上的文本更改事件。 当源范围中的文本发生更改时,投影缓冲区会将更改的文本坐标映射到自己的坐标,并引发相应的文本更改事件。 例如,请考虑包含以下内容的源缓冲区 A 和 B:

A: ABCDE
B: vwxyz

如果投影缓冲区 P 由两个文本范围构成,一个包含所有缓冲区 A,另一个包含所有缓冲区 B,则 P 具有以下内容:

P: ABCDEvwxyz

如果从缓冲区 B 中删除了子字符串 xy ,则缓冲区 P 将引发一个事件,该事件指示删除位置 7 和 8 处的字符。

也可以直接编辑投影缓冲区。 它将编辑传播到相应的源缓冲区。 例如,如果将字符串插入缓冲区 P 位置 6(字符“v”的原始位置),则插入将传播到位置 1 处的缓冲区 B。

源跨度存在限制,这些范围有助于投影缓冲区。 源范围可能不会重叠;投影缓冲区中的位置不能映射到任何源缓冲区中的多个位置,源缓冲区中的位置不能映射到投影缓冲区中的多个位置。 源缓冲区关系中不允许循环。

当投影缓冲区的源缓冲区集更改以及源范围集发生更改时,将引发事件。 elision 缓冲区是一种特殊的投影缓冲区。 它主要用于大纲显示和展开和折叠文本块的操作。 elision 缓冲区仅基于一个源缓冲区,并且 elision 缓冲区中的范围必须与在源缓冲区中排序的跨度相同。

缓冲区图

IBufferGraph 接口允许跨投影缓冲区图进行映射。 所有文本缓冲区和投影缓冲区都收集在定向无环图中,这与语言编译器生成的抽象语法树非常类似。 图形由顶部缓冲区定义,可以是任何文本缓冲区。 缓冲区图可以从顶部缓冲区的点映射到源缓冲区中的某个点,也可以从顶部缓冲区中的范围映射到源缓冲区中的一组范围。 同样,它可以将点或范围从源缓冲区映射到顶部缓冲区中的某个点。 缓冲区图是使用 IBufferGraphFactoryService..

事件和投影缓冲区

修改投影缓冲区时,修改将从投影缓冲区发送到依赖于它的缓冲区。 修改所有缓冲区后,将引发缓冲区更改事件,从最深的缓冲区开始。

大纲显示

大纲显示是能够在文本视图中展开或折叠不同的文本块。 大纲定义为一种 ITag,与定义装饰的方式相同。 一 OutliningRegionTag 个标记,用于定义可以展开或折叠的文本区域。 若要使用大纲显示,必须导入IOutliningManagerService以获取 。IOutliningManager 大纲管理器枚举、折叠和扩展不同的块,这些块表示为 ICollapsible 对象,并相应地引发事件。

鼠标绑定

鼠标绑定将鼠标移动链接到不同的命令。 鼠标绑定是使用 a IMouseProcessorProvider定义,键绑定是通过使用 < a0 IKeyProcessorProvider/> 定义的。 自动 IWpfTextViewHost 实例化所有绑定,并将其连接到视图中的鼠标事件。

IMouseProcessor 接口包含不同鼠标事件的预处理和后处理事件处理程序。 若要处理其中一个事件,可以替代其中的 MouseProcessorBase一些方法。

编辑器操作

编辑器操作可用于自动与编辑器交互,以便编写脚本或其他目的。 可以导入 IEditorOperationsFactoryService 给定的 ITextView访问操作。 然后,可以使用这些对象修改所选内容、滚动视图或将插入点移动到视图的不同部分。

IntelliSense

IntelliSense 支持语句完成、签名帮助(也称为参数信息)、快速信息和灯泡。

语句完成提供方法名称、XML 元素和其他编码或标记元素的潜在完成的弹出列表。 通常,用户手势调用完成会话。 会话显示潜在完成的列表,用户可以选择一个或消除列表。 负责ICompletionBroker创建和触发 .ICompletionSession CompletionSet计算ICompletionSource会话的完成项。

排查导入/导出问题:访问 MEF 组合错误日志

如果尝试导入当前 VS 安装中不存在的内容,或者错误地创作导入或导出,则可能会遇到问题。 查找和解决这些问题的主要方法是引用 托管扩展性框架(MEF)组合错误日志,存储在 %localappdata%\Microsoft\VisualStudio[yourVSVersion]\ComponentModelCache\Microsoft.VisualStudio.Default.err