前文介紹方案總管操作,但開發者大多數時間面對的是編輯器。VS2008 編輯器功能已很強大,善用熱鍵編碼極為順手,正如《卓有成效的程序員》所言:「編程時始終優先使用鍵盤而非滑鼠」。但 VS 面向所有開發者,只能提供通用功能;若有額外需求,只能自己動手擴展。
預設以標籤式呈現開啟的文件。重要成員:
Count、Add()、CloseAll()、Item()、Open()、SaveAll()可遍歷取得所有文件資訊,單一文件對應 Document 介面。
代表正在編輯的文件。關鍵屬性/方法:
FullName/Path/Name、Language、ProjectItem、Selection、TypeActivate()、Close()、Redo()/Undo()、Save()Selection 極為實用(多為先選取再操作),DTE.ActiveDocument 可快速取得作用中文件。
功能極全面,幾乎滿足所有編輯需求,詳見 MSDN。
代表文件中某位置(含行號、欄號等),關鍵屬性:
AbsoluteCharOffset、AtEndOfDocument/Line、AtStartOfDocument/LineDisplayColumn、Line、LineCharOffset、LineLengthCreateEditPoint():建立可編輯的 EditPointEqualTo()、GreaterThan()、LessThan()繼承 TextPoint 所有功能,另提供插入、刪除、剪貼、貼上、書籤、位置移動等編輯方法,賦予程式化編輯強大能力。
繼承 TextPoint,少量擴充屬性/方法,用於操作螢幕可見範圍外的文字。
了解前幾個介面後,TextDocument 很容易上手。操作文字時多從 TextSelection 切入,特定情境下 TextDocument 為佳起點。
專案要求每次修改程式碼需加入特定格式註解:
//-------------Change Begin------------------------------------
//-----------Code change log for Item 1001 ------------------------------
//-----------Modified by : Anders Cui Change Date: 03/30/2009
//-----------Changes Begin-------------------------------------------------
Console.WriteLine("Hello, World");
//-----------Changes End for Item 1001 ----------------------------------
雖不情願但必須重複。分析註解結構:Item 1001 為需求單號、作者、日期、包含選取代碼。曾考慮 Code Snippet,但兩大痛點:新需求需更新 Snippet、無法自動產生日期。
建立參數化模板:
//-------------Change Begin------------------------------------
//-----------Code change log for $item$ -------------------------------
//-----------Modified by : $author$ Change Date: $today$
//-----------Changes Begin-------------------------------------------------
$selected$
//-----------Changes End for $item$ ----------------------------------
將變動部分提取為參數($item$、$author$),日期設為內建參數($today$)。使用時置換參數並插入文件。對應 XML 設定檔:
<?xml version="1.0" encoding="utf-8"?>
<templates>
<builtInParams>
<param format="MM/dd/yyyy">today</param>
<param format="yyyy-MM-dd">now</param>
<param>guid</param>
<param>solution</param>
<param>project</param>
<param>file</param>
</builtInParams>
<template name="Comment">
<content><![CDATA[
//-------------Change Begin------------------------------------
//-----------Code change log for $item$ -------------------------------
//-----------Modified by : $author$ Change Date: $today$
//-----------Changes Begin-------------------------------------------------
$selected$
//-----------Changes End for $item$ ----------------------------------
]]></content>
<params>
<param name="item">1001</param>
<param name="author">Anders Cui</param>
</params>
</template>
<template name="Codes">...</template>
</templates>
約定:參數以 $ 包裹,$selected$ 指示選取文字插入位置。
模板可多,若每個都加選單項會過長。採用彈出選單(CommandBarPopup)承載所有模板項目:
int templatePopupIndex = codeWinCommandBar.Controls.Count + 1;
CommandBarPopup codeTemplatePopup = codeWinCommandBar.Controls.Add(
MsoControlType.msoControlPopup, Type.Missing, Type.Missing,
templatePopupIndex, true) as CommandBarPopup;
codeTemplatePopup.Caption = "Code Template";
CommandBarButton codeTemplateCmd = helper.AddButtonToPopup(codeTemplatePopup,
codeTemplatePopup.Controls.Count + 1, "Code Template 1", "Code Template 1");
codeTemplateCmdEvent = _applicationObject.Events.get_CommandBarEvents(codeTemplateCmd) as CommandBarEvents;
codeTemplateCmdEvent.Click += new _dispCommandBarControlEvents_ClickEventHandler(CodeTemplateCmdEvent_Click);
運行時讀取設定檔,依模板數量動態產生選單項,共用同一個事件處理函式。
初版直接用 TextSelection.Text 整體替換,速度較慢。改用 EditPoint 分段寫入:
private void codeTemplateCmdEvent_Click(...) {
CommandBarControl ctrl = CommandBarControl as CommandBarControl;
string content = CodeTemplateManager.Instance.GetTemplateContent(ctrl.Caption);
int indexOfSelectedParam = CodeTemplateManager.Instance.IndexOfSelectedParam(content);
bool surroundSelectedText = (indexOfSelectedParam >= 0);
TextSelection selected = _applicationObject.ActiveDocument.Selection as TextSelection;
EditPoint topPoint = selected.TopPoint.CreateEditPoint();
EditPoint bottomPoint = selected.BottomPoint.CreateEditPoint();
if (surroundSelectedText) {
string beforeSelectedParam = CodeTemplateManager.Instance.GetTextBeforeSelectedParam(content);
string afterSelectedParam = CodeTemplateManager.Instance.GetTextAfterSelectedParam(content);
topPoint.LineUp(1);
topPoint.EndOfLine();
topPoint.Insert(Environment.NewLine);
topPoint.Insert(beforeSelectedParam);
bottomPoint.EndOfLine();
bottomPoint.Insert(Environment.NewLine);
bottomPoint.Insert(afterSelectedParam);
} else {
topPoint.Delete(bottomPoint);
topPoint.Insert(content);
}
}
將模板拆為「選取文字前」、「選取文字本身」、「選取文字後」三段,僅在前後寫入文字,速度大幅提升。若無 $selected$ 則刪除選取內容後寫入模板。
此功能已在小團隊實戰使用,節省大量時間。
本文系統介紹編輯器擴展核心 API(TextSelection、TextPoint、EditPoint、VirtualPoint、TextDocument),並以參數化代碼模板為實戰範例,示範動態選單建立與高效文字插入技巧。