如何擴充套件 Visual Studio 編輯器

發表於2015-10-15

在 Visual Studio 2010 的時代,擴充套件 Visual Studio 的途徑有很多,開發者可以選擇巨集、Add-in、MEF 和 VSPackages 進行自定義的擴充套件。但是巨集在 Visual Studio 2012 的時候被閹割了,Add-in 也在 Visual Studio 2013 裡被抹殺了,這樣的調整對於 Visual Studio 來說是好的,但是對於那些習慣了使用巨集和Add-in的團隊可能就鬱悶了。

 

本文將一步步教你如何實現對 Visual Studio 程式碼編輯器的擴充套件,最終將實現一個可以支援如下兩個功能的擴充套件。

  1. 自動任務註釋(支援後期擴充套件)

任務註釋就是 //TODO、//FIXME 之類可以被 Visual Studio 區別對待的註釋,不同的任務註釋可能需要不同的格式,比如有些需要先註釋掉方法體,有些需要在程式碼行的前後加上開始、結束標記等,過去在巨集還支援的時候,我們可以使用巨集來實現此類操作。不同的專案組使用這些標記的方法不同,因此會有不同的要求,本工具支援自定義的擴充套件。

  2. 跳轉到方法的頭部或尾部

這個功能看似無用,但是當一個方法有幾百行,甚至上千行的時候,如何快速跳轉到方法的開頭或結尾就比較麻煩了。

圖1 最終效果動畫演示(請點選放大後檢視)

閱讀此文,需要您對如下知識點有一定了解:

1. MVVM 與 WPF -> 趕緊去了解一下 (此篇文章躺在自己網站上,沒有來得及同步到部落格園,大家將就一下)
2. MEF -> 趕緊去了解一下

如果想趕緊體驗下這個擴充套件的話,請猛擊這裡! (僅限 Visual Studio 2012)

本文提綱

MEF 和 VSPackage

準備工作

各種 Editor 模板的區別

Visual Studio 實驗環境

Editor Viewport Adornment 原理解析

新增控制元件和基礎程式碼

獲取DTE物件

完成功能邏輯

增加擴充套件點,讓註釋支援後期擴充套件

Why VSPackages or MEF ?

原始碼管理

參考資源

在進入正文前,我們先來快速認識下這僅存的兩位英雄~

MEF 和 VSPackage

從 Visual Studio 2013 開始,對 Visual Studio 的擴充套件只剩下 MEF 和 VSPackage。來來來,給大家介紹下你們自己~~

  MEF(Managed Extensibility Framework)

這個框架最初是獨立於 .Net Framework 釋出,後來整合到了 .Net 4.0 中,並伴隨著 .Net 4.0 一起釋出(包含在 System.ComponentModel.Composition.dll 程式集中)。從名字上看得出這個框架主要就是為編寫可擴充套件的應用程式而生的。隨著 .Net 4.0 的推出,還有一項重大的改變就是 Visual Studio IDE中的編輯器,該部分原先和其它元件一樣都是採用 COM 方式開發,但現在卻被 WPF 技術頂替了。採用了WPF技術搭建的編輯可以完全支援使用 MEF 來進行擴充套件,這不能不說是一個非常完美的改進。唯一遺憾的是,截止2013的出現,Visual Studio 的其餘部分仍然沒有從 COM 中脫離出來。

The MEF is a .NET library that lets you add and modify features of an application or component that follows the MEF programming model. The Visual Studio editor can both provide and consume MEF component parts. The MEF is contained in the .NET Framework version 4 System.ComponentModel.Composition.dll assembly.

 

— Managed Extensibility Framework in the Editor

因此,如果想要擴充套件已有的編輯器,就可以基於MEF進行開發(比如修改針對C#程式碼編輯器的高亮顏色、智慧提示、括號補全等)。

  VSPackage

可以說除了編輯器, Visual Studio 就是多個 VSPackage 的集合,因此使用 VSPackage 可以完美得與 Visual Studio 進行整合,而且能夠獲得幾乎全部的能力。如果想開發對工具欄、選單欄,甚至是全新的編輯器時(提供對新語言的解析、智慧提示等)可以選擇VSPackage。

準備工作

童鞋們,請檢查下吃飯的傢伙準備好了嗎?

1. 英文版的 Visual Studio,中文版的 Visual Studio 無法看到 Editor Text Adornment 等模板。

2. 想要擴充套件編輯器或整個 Visual Stuio,必須先下載安裝 Visual Studio SDK(VS2012版本,請點選連結下載),安裝完後,就可以在 “其它專案型別” 的模板上找到想要的模板了。

圖2 SDK安裝後的模板

本文所有程式碼均基於 Visual Studio 2012 開發,如果您使用的是其它版本,請下載安裝適合您版本的 SDK。擴充套件開發的過程在各個版本間可能會有細小的差距,但不影響整體開發流程。

各種 Editor 模板的區別

安裝完 SDK 後,就會擁有如上圖中所示的多種擴充套件模板,其中包含四種和 Editor 相關的模板,它們之間有什麼區別嗎?

  Editor Classifier

可以修改編輯器中程式碼的高亮、新增一些智慧的標籤(比如當我們修改了某個變數名時,會在變數名下出現一個小短橫,當你滑鼠移上去後會提示你是否要修改所有引用的地方)等,示例效果如下:

圖3 Editor Classifier 示例

  Editor Margin

在編輯器的周圍新增一些WPF元素,比如當前檔案是隻讀的時候,可以在編輯器下邊沿提示檔案為只讀,示例效果如下:

圖4 在下邊沿新增了一條綠色的資訊框

  Editor Text Adornment

用於對編輯器中的文字進行修飾,新增一些WPF的元素,示例效果如下:

圖5 用框框包裹所有字元 a

  Editor Viewport Adornment

用於對編輯器本身進行修飾,新增一些WPF元素,示例如下:

圖6 在編輯器的右上角新增了一個矩形元素

本工具使用 Editor Viewport Adornment 作為模板。

Visual Studio 實驗環境

對於這些擴充套件的測試,Visual Studio 提供了 Experimental Instance 用於實驗環境,該環境和真實的 Visual Studio 完全一樣,只不過它和真實版本各自獨享一套配置檔案,對於實驗環境的配置不會影響到真實環境。

第一次啟動實驗環境,會進入如圖7所示的預設環境配置的介面。

圖7 預設環境配置

  實驗環境中的資料可以初始化

SDK目錄中的 Tools提供了 “Reset the Visual Studio 2012 Experimental Instance” 命令列工具,執行該工具就會初始化實驗環境。

圖10 初始化工具

Editor Viewport Adornment 原理解析

要想理解它,必先使用它。通過模板建立好的新 Editor Viewport Adornment 專案時,已經包含了示例程式碼,該示例程式碼的功能就是如圖6所示在編輯區新增一個紫色的矩形框。

圖11 剛建立完的樣子

在執行示例程式碼前必須先修改 source.extension.vsixmanifest 檔案中的 author 欄位,否則執行將報錯。

圖12 補全 Author 欄位

  實現原理

在介紹 MEF 和 VSPackage 的時候,我說過整個 Editor 部分都是基於 MEF 思想開發的。簡單來說,Visual Studio Editor 向第三方擴充套件提供了產物(Export)、接受者(Import)及各種協議,第三方擴充套件根據對應的協議製作生產出符合的產物,然後 VS 會將第三方的產物與自己的接受者進行組合(就好像是把符合形狀的積木放入盒子中)。這樣,我們就能在下次啟動 VS 的時候使用這個擴充套件了。

圖13 MEF 思想

這個專案中主要的就兩個檔案:TskCommentFactory.cs 和 TskComment.cs

其中,TskCommentFactory 檔案中的 PurpleBoxAdornmentFactory 就是基於 IWpfTextVIewCreationListener 這個協議的產物,也是本專案的主要入口。使用該協議可以在編輯器檢視建立的時候加入我們想要的操作。其中最重要的就是 TextViewCreated 方法,該方法呼叫了 TskComment 建構函式,從而在編輯器上增加了一塊紫色的區域。

TskComment 檔案中主要就兩個方法:建構函式 和 onSizeChange 方法。在建構函式中,通過 Brush 畫出了一個紫色的矩形,併為編輯器檢視繫結了 onSizeChange 方法。

上面程式碼建立了一個紫色的矩形。如果看不懂也不要緊,因為這部分程式碼是要被刪除的。

onSizeChange 中呼叫了 AddAdornment 這個方法,這才把紫色的矩形加到了編輯器上。

到現在,原理部分已經講完了,不管你信不信,你已經可以通過修改 TskComment 中這兩個方法來實現自己的擴充套件了。

新增控制元件和基礎程式碼

如何實現我要的功能呢?

首先,我們的工具需要一個可以互動的介面,這沒辦法單純的用 Brush 繪製。因此需要新建一個 WPF 控制元件(不能是 Winform 控制元件,AddAdornment 只能接受 WPF 元素)。

圖14 新建 WPF 使用者控制元件

 

按照 MVVM 的思想,依次新增 DelegateCommand、ViewModelBase、MainViewModel 這幾個檔案,我們的核心邏輯全部在 MainViewModel 中。

圖15 新增的檔案

 

MainWindow.xaml 中的程式碼如下(省略了一些與邏輯無關的元素)。

MainViewModel 中的程式碼如下(省略部分無關程式碼),其中的 MoveToTopOrBottomOfBlock 和 Execute 兩個方法的程式碼因為缺少關鍵元素,暫時為空。

獲取DTE物件

上一節中所缺少的關鍵元素其實就是DTE,該物件相當於 Visual Studio 的例項,可以通過操作該例項對編輯器中的東東進行控制(如果想要進一步瞭解,請見參考資源[1]),比如剪下、貼上、新建行、跳轉到方法體等。所以,如果想實現開頭我所講的工具,就要依託這個物件。

 

在巨集編輯器中,可以很輕鬆的獲取該物件,但是在這裡稍微就有點麻煩了。我們必須藉助 Visual Studio 的其中一個產物(Export) — SVsServiceProvider。該產物的 GetService 可以獲得這個物件。

修改 TskCommentFactory 程式碼,如下:

注:DTE 存在於 EnvDTE.dll 程式集中,SVsServiceProvider 存在於 Microsoft.VisualStudio.Shell.Immutable.10.0.dll 程式集中,需要先新增這些程式集到專案中

完成功能邏輯

既然已經獲取了關鍵元素,我們就把 MainViewModel 中的程式碼完善一下吧。

修改 MainWindow 的程式碼讓它能夠接受 DTE。

修改 TskComment.cs 中對應的部分

哦啦,現在可以執行了!

圖16 動畫演示

增加擴充套件點,讓註釋支援後期擴充套件

上面的程式碼已經完成了,可惜這個註釋太不人性化了,要是我想增加一個 Phase0 的註釋或者 FixMe 的註釋,還得修改程式碼。因此,這裡也按照 MEF 的思想,對程式碼進行升級。

這裡只對關鍵程式碼進行解釋說明,其它部分,請童鞋們檢視原始碼。

  新建 “協議” 專案

增加一個獨立的專案,用於存放協議介面,同時基於此介面提供一個抽象類。

 新建 “接受者” 

有了協議,就該在我們的工具上增加一個接收者從而讓 MEF 幫我們把第三方的產物和我們的接受者組合在一起。

修改 MainViewModel, 增加接收者,因為可能會有不只一個的註釋,所以要使用 ImportMany。

 新建 “組合引擎”

大功告成,如果您還能跟住我的節奏,那可喜可賀,您已經基本掌握了 MEF 的思想和擴充套件 Editor 的能力了。

Why VSPackages or MEF ?

換個問法就是 “為什麼巨集和Add-in不支援了?” 這答案谷歌一下肯定很多,但是考慮到文章的完整性,還是打算來做一下搬運工。

根據Microsoft所做的使用率跟蹤資料,Visual Studio中巨集的使用人數不到開發人員總數的1%。這還不足以讓Microsoft放棄這個功能,Visual Studio中的巨集功能維護成本過高,是另外一個原因,與其他功能不同,對巨集的支援,必須要隨著Visual Studio每個新版本更新,並做大量令人疲倦的測試。理論上,使用者應該可以在巨集IDE中錄製並播放任何功能,這給微軟的維護增加了巨大負擔。 Matt Kaufman說:巨集IDE已經好幾個版本都沒有更新了。使用者把它啟動之後,很快就能看出來它像一個老版本的Visual Studio。更麻煩的是:它還是隻支援Visual Basic。使用者不能使用C#或是其他新的.NET語言來建立巨集。

— Visual Studio 11拋棄巨集

微軟已經棄用Visual Studio載入項這一基礎結構。根據MSDN上的說法,“Visual Studio 2013棄用了載入項。開發人員應該將載入項升級為VSPackage擴充套件。”稍後的文件指出,“VSPackage是Visual Studio的主要架構單位,也是部署、許可和安全的單位。Visual Studio本身的大部分就寫成了VSPackage集合。”

— Visual Studio 2013棄用載入項

原始碼管理

CodePlex: Visual Studio Editor Extension — TaskComment

TskComment.vsix: 您也可以直接點選這裡獲取此擴充套件,並安裝到您的 Visual Studio 當中。

參考資源

[1] 《Visual Studio 巨集的高階用法》

[2] Managed Extensibility Framework (MEF)

[3] Extending the Editor

 

本文來源於 《如何擴充套件 Visual Studio 編輯器》

相關文章