深入探索編輯器
編輯器是由數個不同的子系統所組成,其設計目的是讓編輯器文字模型與文字檢視及使用者介面分開。
這些章節描述編輯器的不同層面:
這些章節旨在描述編輯器的功能:
子系統
文字模型子系統
文字模型子系統負責表示文字並啟用其操作。 文字模型子系統包含 ITextBuffer 介面,其描述編輯器要顯示的字元序列。 此文字可以透過許多方式修改、追蹤及操作。 文字模型也提供下列層面的類型:
將文字與檔案建立關聯的服務,並負責在檔案系統中讀取和寫入它們。
差異服務,可找出兩個物件序列之間的最小差異。
用來描述緩衝區中文字的系統,以其他緩衝區中的文字子集來描述。
文字模型子系統沒有使用者介面 (UI) 概念。 例如,它不負責文字格式設定或文字版面配置,而且它不知道可能與文字相關聯的視覺裝飾。
文字模型子系統的公用類型包含在 Microsoft.VisualStudio.Text.Data.dll 和 Microsoft.VisualStudio.CoreUtility.dll 中,這只取決於 .NET Framework 基礎類別庫和 Managed Extensibility Framework (MEF)。
文字檢視子系統
文字檢視子系統負責格式化和顯示文字。 此子系統中的類型會分成兩層,視類型是否依賴 Windows Presentation Foundation (WPF) 而定。 最重要的類型是 ITextView 和 IWpfTextView,可控制要顯示的文字行集,以及插入號、選取範圍,以及使用 WPF UI 元素裝飾文字的設施。 此子系統也會提供文字顯示區域周圍的邊界。 這些邊界可以延伸,而且可以包含不同類型的內容和視覺效果。 邊界的範例包括行號顯示和捲動列。
文字檢視子系統的公用類型包含在 Microsoft.VisualStudio.Text.UI.dll 和 Microsoft.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, and NormalizedSnapshotSpanCollections
SnapshotPoint 表示快照集中的字元位置。 此位置保證位於零和快照集的長度之間。 SnapshotSpan 表示快照中的文字範圍。 其位置保證位於零和快照集的長度之間。 NormalizedSnapshotSpanCollection 是由來自相同快照集的一組 SnapshotSpan 物件所組成。
Spans 和 NormalizedSpanCollections
Span 表示可以套用至文字快照集內文字範圍的間隔。 快照集位置是以零起始,因此範圍可以從任何位置開始,包括零。 範圍的 End
屬性等於其 Start
屬性和其 Length
屬性的總和。 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) 是空範圍。)
如果兩個範圍有通用的位置,則兩個範圍會重疊,但結束位置除外。 空範圍永遠不會與任何其他範圍重疊,而兩個範圍的重疊絕不是空的。
NormalizedSpanCollection 是以範圍 [開始] 屬性排序的範圍清單。 在清單中,會合併重疊或鄰接範圍。 例如,假設範圍集為 [5..9)、[0..1)、[3..6) 和 [9..10),則範圍正規化清單為 [0..1]、[3..10]。
ITextEdit、TextVersion 和文字變更通知
您可以使用 ITextEdit 物件來變更文字緩衝區的內容。 建立這類物件 (使用 ITextBuffer 的 CreateEdit()
方法) 會啟動由文字編輯組成的文字交易。 每個編輯都是以字串取代緩衝區中某些文字範圍。 當交易啟動時,每個編輯的座標和內容都會相對於緩衝區的快照集來表示。 ITextEdit 物件會調整受相同交易中其他編輯影響的編輯座標。
例如,請考慮包含此字串的文字緩衝區:
abcdefghij
套用包含兩個編輯的交易,一個編輯會使用 X
字元取代 [2..4) 範圍,另一個編輯則使用 Y
字元取代 [6..9) 範圍。 結果是這個緩衝區:
abXefYj
第二次編輯的座標是在套用第一次編輯之前,先計算交易開始時緩衝區的內容。
呼叫 ITextEdit 物件的 Apply()
方法,對緩衝區所做的變更就會生效。 如果至少有一個非空白的編輯,則會建立新的 ITextVersion、建立新的 ITextSnapshot,並引發一個 Changed
事件。 每個文字版本都有不同的文字快照集。 文字快照集代表編輯交易之後文字緩衝區的完整狀態,但文字版本只會描述從一個快照集到下一個快照集的變更。 一般而言,文字快照集應該使用一次後就捨棄,而文字版本必須保持運作一段時間。
文字版本包含 INormalizedTextChangeCollection。 此集合描述套用至快照集時產生後續快照集的變更。 集合中的每個 ITextChange 都包含變更的字元位置、已取代的字串和取代字串。 已取代的字串對於基本插入而言是空的,而取代字串則為空白,用於基本刪除作業。 針對最新版本的文字緩衝區,標準化集合一律為 null
。
您可以隨時為文字緩衝區具現化一個 ITextEdit 物件,而且所有文字編輯都必須在擁有文字緩衝區的執行緒上執行 (如果已宣告擁有權)。 您可以藉由呼叫其 Cancel
方法或 Dispose
方法來放棄文字編輯。
ITextBuffer 也會提供 Insert()
、Delete()
和 Replace()
方法,類似於 ITextEdit 介面上找到的方法。 呼叫這些屬性的效果類似於建立 ITextEdit 物件、進行類似的呼叫,然後套用編輯。
追蹤點和追蹤範圍
ITrackingPoint 表示文字緩衝區中的字元位置。 如果緩衝區是以導致字元位置移位的方式進行編輯,追蹤點會隨它移動。 例如,如果追蹤點參照緩衝區中的位置 10,而且會在緩衝區開頭插入 5 個字元,追蹤點接著會參考位置 15。 如果插入恰好發生在追蹤點所表示的位置,則其行為是由其 PointTrackingMode 所決定,它可以是 Positive
或 Negative
。 如果追蹤模式為正數,追蹤點會參考相同的字元,而該字元現在位於插入的結尾。 如果追蹤模式為負數,追蹤點會參考原始位置的第一個插入字元。 如果刪除追蹤點所表示位置的字元,追蹤點會移至刪除範圍後面的第一個字元。 例如,如果追蹤點參照位置 5 的字元,而位置 3 到 6 的字元會被刪除,追蹤點會參考位置 3 的字元。
ITrackingSpan 表示字元範圍,而不只是一個位置。 其行為取決於其 SpanTrackingMode。 如果範圍追蹤模式是 SpanTrackingMode.EdgeInclusive,追蹤範圍就會增長為併入在其邊緣插入的文字。 如果範圍追蹤模式是 SpanTrackingMode.EdgeExclusive,追蹤範圍不會併入在其邊緣插入的文字。 不過,如果範圍追蹤模式是 SpanTrackingMode.EdgePositive,則插入會將目前位置推向開始處,而如果範圍追蹤模式是 SpanTrackingMode.EdgeNegative,則插入會將目前位置推向結尾處。
您可以取得追蹤點的位置或追蹤範圍範圍,以取得其所屬之文字緩衝區的任何快照集。 您可以從任何執行緒安全地參考追蹤點和追蹤範圍。
內容類型
內容類型是定義不同類型內容的機制。 內容類型可以是檔案類型 (例如「text」、「code」或「binary」),或技術類型 (例如「xml」、「vb」或「c#」)。 例如,「using」一詞是 C# 和 Visual Basic 中的關鍵詞,但在其他程式設計語言中則並非如此。 因此,此關鍵詞的定義會限制為「c#」和「vb」內容類型。
內容類型會用來做為編輯器裝飾和其他元素的篩選。 每個內容類型都會定義許多編輯器功能和擴充點。 例如,純文字檔案、XML 檔案和 Visual Basic 原始程式碼檔案的文字色彩不同。 建立文字緩衝區時通常會指派內容類型,而且可以變更文字緩衝區的內容類型。
內容類型可以多重繼承自其他內容類型。 ContentTypeDefinition 可讓您將多個基底類型指定為指定內容類型的上層。
開發人員可以使用 IContentTypeRegistryService 來定義自己的內容類型並加以註冊。 許多編輯器功能都可以使用 ContentTypeAttribute 來定義特定內容類型。 例如,可以定義編輯器邊界、裝飾和滑鼠處理常式,使其僅適用於顯示特定內容類型的編輯器。
文字檢視
模型檢視控制器 (MVC) 模式的檢視部分會定義文字檢視、檢視的格式設定、捲動列等圖形元素,以及插入號。 Visual Studio 編輯器的所有呈現元素都是以 WPF 為基礎。
文字檢視
ITextView 介面是與平台無關的文字檢視表示法。 它主要用於在視窗中顯示文字文件,但也可以用於其他用途,例如,用於工具提示。
文字檢視會參考不同類型的文字緩衝區。 TextViewModel 屬性是指指向以下這三個不同文字緩衝區的 ITextViewModel 物件:資料緩衝區 (也就是最上層資料層級緩衝區)、編輯緩衝區 (編輯發生所在的緩衝區),以及視覺緩衝區 (顯示在文字檢視中的緩衝區)。
文字會根據附加至基礎文字緩衝區的分類器進行格式化,並使用附加至文字檢視本身的裝飾提供者裝飾。
文字檢視座標系統
文字檢視座標系統會指定文字檢視中的位置。 在此座標系統中,x 值 0.0 會對應至所顯示的文字左側邊緣,而 y 值 0.0 會對應到所顯示文字的頂端邊緣。 x 座標會從左到右增加,而 y 座標會從上到下增加。
檢視區 (文字視窗中可見的文字部分) 無法像垂直捲動一樣水平捲動。 檢視區會藉由變更其左座標來水平捲動,使其相對於繪圖介面移動。 不過,檢視區只能藉由變更轉譯的文字來垂直捲動,這會導致引發 LayoutChanged 事件。
座標系統中的距離會對應至邏輯像素。 如果文字轉譯介面在沒有縮放轉換的情況下顯示,則文字轉譯座標系統中的一個單位會對應到顯示器上的一個像素。
邊界
ITextViewMargin 介面代表邊界,並可控制邊界及其大小的可見性。 有四個預先定義的邊界,其名稱為「頂端」、「左側」、「右側」和「底部」,並附加至檢視的頂端、底部、左側或右側邊緣。 這些邊界是可以放置其他邊界的容器。 介面會定義傳回邊界大小和邊界可見度的方法。 邊界是視覺元素,可提供附加文字檢視的其他資訊。 例如,行號邊界會顯示文字檢視的行號。 圖像邊界會顯示 UI 元素。
IWpfTextViewMarginProvider 介面會處理邊界的建立和放置。 邊界可以相對於其他邊界來排序。 較高優先順序的邊界位於更接近文字檢視的位置。 例如,如果有兩個左側邊界 (邊界 A 和邊界 B),而邊界 B 的優先順序低於邊界 A,則邊界 B 會顯示在邊界 A 的左側。
文字檢視主機
IWpfTextViewHost 介面包含文字檢視,以及隨附檢視的任何鄰接裝飾,例如捲動軸。 文字檢視主機也包含附加至檢視框線的邊界。
格式化文字
文字檢視中顯示的文字是由 ITextViewLine 物件所組成。 每個文字檢視行都會對應至文字檢視中的一行文字。 基礎文字緩衝區中的長文字行可以部分遮蔽 (如果未啟用自動換行),或分成多個文字檢視行。 ITextViewLine 介面包含座標和字元之間對應的方法和屬性,以及可能與這一行相關聯的裝飾。
ITextViewLine 物件是使用 IFormattedLineSource 介面所建立。 如果您只是擔心目前顯示在檢視中的文字,您可以忽略格式設定來源。 如果您對檢視中未顯示的文字格式感興趣 (例如,為了支援 RTF 剪下和貼上),您可以使用 IFormattedLineSource 來格式化文字緩衝區中的文字。
文字檢視會一次格式化一個 ITextSnapshotLine。
編輯器功能
編輯器功能採用的設計可讓功能定義與其實作分開。 編輯器包含這些功能:
標籤和分類器
裝飾
Projection
大綱
滑鼠和按鍵繫結
作業和基本類型
IntelliSense
標籤和分類器
標籤是與文字範圍相關聯的標記。 例如,您可以使用文字色彩、底線、圖形或快顯,以不同的方式呈現它們。 分類器是一種標籤。
其他類型的標籤適用於 TextMarkerTag 文字醒目提示、OutliningRegionTag 大綱和 ErrorTag 編譯錯誤。
分類類型
IClassificationType 介面代表相等類別,這是文字的抽象類別。 分類類型可以多重繼承自其他分類類型。 例如,程式設計語言分類可能包含「關鍵字」、「註解」和「識別碼」,這些分類全都繼承自「程式碼」。 自然語言分類類型可能包含「名詞」、「動詞」和「形容詞」,全都繼承自「自然語言」。
分類
分類是特定分類類型的執行個體,通常跨越文字範圍。 ClassificationSpan 可用於表示分類。 分類範圍可以視為涵蓋特定文字範圍的標籤,並告訴系統此文字範圍是特定分類類型。
分類器
IClassifier 是一種將文字分成一組分類的機制。 必須針對特定內容類型定義分類器,並針對特定文字緩衝區具現化。 用戶端必須實作 IClassifier 才能參與文字分類。
分類器匯總工具
分類器匯總工具是一種機制,可將一個文字緩衝區的所有分類器合併成一組分類。 例如,C# 分類器和英文語言分類器都可以在 C# 檔案中建立註解的分類。 請考量此註解:
// This method produces a classifier
C# 分類器可能會將整個範圍標記為註解,而英文分類器可能會將「產生」分類為「動詞」,並將「方法」分類為「名詞」。 匯總工具會產生一組非重迭分類,而集合的類型是以所有貢獻為基礎。
分類器匯總工具也是分類器,因為它會將文字分成一組分類。 分類器匯總工具也可確保沒有重疊的分類,且分類已排序。 個別分類器可以任意傳回任何一組分類,依任何順序和以任何方式重疊。
分類格式設定和文字色彩
文字格式設定是以文字分類為基礎的功能範例。 文字檢視層會使用它來判斷應用程式中文字的顯示。 文字格式設定區域取決於 WPF,但分類的邏輯定義則不相同。
分類格式是特定分類類型的一組格式化屬性。 這些格式繼承自分類類型的上層格式。
IClassificationFormatMap 是從分類類型到一組文字格式設定屬性的對應。 編輯器中格式對應的實作會處理分類格式的所有匯出。
裝飾
裝飾是圖形效果,與文字檢視中字元的字型和色彩不直接相關。 例如,許多程式設計語言中用來標記非編譯程式代碼的紅色波浪線底線是內嵌裝飾,而工具提示則是快顯裝飾。 裝飾是從 UIElement 衍生而來,並實作 ITag。 兩種特殊類型的裝飾標記是:SpaceNegotiatingAdornmentTag (用於佔用與檢視中文字相同空間的裝飾),以及 ErrorTag (用於波浪線底線的裝飾)。
內嵌裝飾是構成格式化文字檢視一部分的圖形。 它們會以不同的 Z 順序層進行整理。 有三個內建層,如下所示:文字、插入號和選取範圍。 不過,開發人員可以定義更多層,並彼此依順序排列。 三種內嵌裝飾是文字相對裝飾 (在文字移動時移動,並在刪除文字時刪除)、檢視相對裝飾 (與檢視的非文字部分有關),以及擁有者控制的裝飾品 (開發人員必須管理其放置)。
快顯裝飾是出現在文字檢視上方小型視窗的圖形,例如工具提示。
Projection
投影是建構不同類型文字緩衝區的技術,該緩衝區實際上不會儲存文字,而是結合來自其他文字緩衝區的文字。 例如,投影緩衝區可用來串連其他兩個緩衝區的文字,並呈現結果,就好像它只位於一個緩衝區中,或隱藏一個緩衝區中的文字部分。 投影緩衝區可以做為另一個投影緩衝區的來源緩衝區。 可以建構一組與投影相關的緩衝區,以許多不同方式重新排列文字。 (這類集合也稱為緩衝區圖形)。Visual Studio 文字大綱功能是使用投影緩衝區來隱藏折疊的文字,而適用於 ASP.NET 頁面的 Visual Studio 編輯器會使用投影來支援內嵌語言,例如 Visual Basic 和 C#。
IProjectionBuffer 是使用 IProjectionBufferFactoryService 建立的。 投影緩衝區是由稱為來源範圍的 ITrackingSpan 物件的排序順序表示。 這些範圍的內容會以字元序列呈現。 從中繪製來源範圍的文字緩衝區會命名為來源緩衝區。 投影緩衝區的用戶端不一定知道它與一般文字緩衝區不同。
投影緩衝區會接聽來源緩衝區上的文字變更事件。 當來源範圍中的文字變更時,投影緩衝區會將變更的文字座標對應至自己的座標,並引發適當的文字變更事件。 例如,假設來源緩衝區 A 和 B 具有下列內容:
A: ABCDE
B: vwxyz
如果投影緩衝區 P 是從兩個文字範圍形成,一個包含所有緩衝區 A,另一個具有所有緩衝區 B,則 P 具有下列內容:
P: ABCDEvwxyz
如果從緩衝區 B 中刪除子字串 xy
,則緩衝區 P 會引發事件,指出位置為 7 和 8 的字元已刪除。
也可以直接編輯投影緩衝區。 它會將編輯傳播至適當的來源緩衝區。 例如,如果在位置 6 的緩衝區 P 中插入字串 (字元「v」的原始位置),則插入會傳播至位置 1 的緩衝區 B。
對參與投影緩衝區的來源範圍設有限制。 來源範圍可能不會重疊;投影緩衝區中的位置無法對應至任何來源緩衝區中的多個位置,而且來源緩衝區中的位置無法對應至投影緩衝區中的多個位置。 來源緩衝區關聯性中不允許迴圈。
當投影緩衝區的來源緩衝區集合變更,以及來源集合變更時,就會引發事件。 省略緩衝區是一種特殊的投影緩衝區。 它主要用於大綱和展開和折疊文字區塊的作業。 省略緩衝區只以一個來源緩衝區為基礎,而且省略緩衝區中的範圍必須與在來源緩衝區中排序相同。
緩衝區圖形
IBufferGraph 介面可跨投影緩衝區的圖形進行對應。 所有文字緩衝區和投影緩衝區都會收集在有向非循環圖中,這與語言編譯程式所產生的抽象語法樹狀結構非常類似。 圖形是由頂端緩衝區所定義,可以是任何文字緩衝區。 緩衝區圖形可以從頂端緩衝區中的某個點對應至來源緩衝區中的某個點,或從頂端緩衝區中的範圍對應到來源緩衝區中的一組範圍。 同樣地,它可以將點或範圍從來源緩衝區對應至頂端緩衝區中的點。 緩衝區圖形是使用 IBufferGraphFactoryService 所建立。
事件和投影緩衝區
修改投影緩衝區時,會將修改從投影緩衝區傳送到相依的緩衝區。 修改所有緩衝區之後,會從最深的緩衝區開始引發緩衝區變更事件。
大綱
大綱是能夠在文字檢視中展開或折疊不同的文字區塊。 大綱定義為一種 ITag,就像定義裝飾一樣。 OutliningRegionTag 是一個標籤,定義可以展開或折疊的文字區域。 若要使用大綱,您必須匯入 IOutliningManagerService 以取得 IOutliningManager。 大綱管理員會列舉、折疊和展開以 ICollapsible 物件表示的不同區塊,並據此引發事件。
滑鼠繫結
滑鼠繫結會將滑鼠移動連結至不同的命令。 滑鼠繫結是使用 IMouseProcessorProvider 來定義,而索引鍵繫結是使用 IKeyProcessorProvider 來定義。 IWpfTextViewHost 會自動具現化所有繫結,並將其連接到檢視中的滑鼠事件。
IMouseProcessor 介面包含不同滑鼠事件的前置程序和後續處理常式。 若要處理其中一個事件,您可以覆寫 MouseProcessorBase 中的某些方法。
編輯器作業
編輯器作業可用來自動化與編輯器的互動,以用於指令碼編寫或其他用途。 您可以匯入 IEditorOperationsFactoryService,以存取指定 ITextView 上的作業。 然後,您可以使用這些物件來修改選取範圍、捲動檢視,或將插入號移至檢視的不同部分。
IntelliSense
IntelliSense 支援語句完成、簽章說明 (也稱為參數資訊)、快速資訊和燈泡。
語句完成提供方法名稱、XML 元素和其他編碼或標記元素之潛在完成的快顯清單。 一般而言,使用者手勢會叫用完成工作階段。 工作階段會顯示可能的完成清單,使用者可以選取一個或關閉清單。 ICompletionBroker 負責建立和觸發 ICompletionSession。 ICompletionSource 會計算工作階段完成項目的 CompletionSet。
疑難排解匯入/匯出問題:存取 MEF 組合錯誤記錄檔
如果您嘗試匯入目前 VS 安裝中不存在的項目,或您不正確地撰寫匯入或匯出,此時可能會遇到問題。 尋找並解決這些問題的主要方法是參考儲存在 %localappdata%\Microsoft\VisualStudio[yourVSVersion]\ComponentModelCache\Microsoft.VisualStudio.Default.err 的 Managed Extensibility Framework (MEF) 組合錯誤記錄檔。