操作指南:顯示燈泡提示
燈泡是 Visual Studio 編輯器中的圖示,可展開以顯示一組動作,例如,修正內建程式代碼分析器或程式碼重構所識別的問題。
在 Visual C# 和 Visual Basic 編輯器中,您也可以使用 .NET 編譯器平臺(“Roslyn”)撰寫並封裝您自己的程式碼分析器,這些分析器具有自動顯示燈泡的操作。 如需詳細資訊,請參閱:
-
C++ 等其他語言也提供提示燈,以便進行某些快速操作,例如,建議建立該函式的存根實作。
以下是燈泡的外觀。 在 Visual Basic 或 Visual C# 專案中,當遇到無效的變數名稱時,紅色波浪線會顯示在該變數名稱下。 如果您將滑鼠停留在無效的標識碼上,游標附近會出現燈泡。
如果您點擊燈泡旁的向下箭號,就會顯示一系列建議的動作,以及所選動作的預覽。 在此情況下,它會顯示當您執行動作時,對程式代碼所做的變更。
您可以使用燈泡來提供您自己的建議動作。 例如,您可以提供動作,將左大括弧移到新行,或將它們移至前一行的結尾。 下列逐步解說示範如何建立出現在目前單字上的燈泡,並有兩個建議的動作:轉換成大寫,轉換成小寫。
建立受控擴充性架構 (MEF) 專案
建立 C# VSIX 專案。 (在 [新增專案] 對話框中,選取 [Visual C# / Extensibility],然後 VSIX 專案。將專案命名為
LightBulbTest
。將 編輯器分類器 項目範本新增至專案。 如需詳細資訊,請參閱 使用編輯器專案範本建立延伸模組。
刪除現有的類別檔案。
新增下列參考至專案,並將 [Copy Local] 設定為
False
:Microsoft.VisualStudio.Language.Intellisense
新增類別檔案,並將它命名 LightBulbTest。
新增下列 using 指令:
using System; using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities; using System.ComponentModel.Composition; using System.Threading;
實現燈泡來源供應商
在 LightBulbTest.cs 類別檔案中,刪除 LightBulbTest 類別。 新增名為 TestSuggestedActionsSourceProvider 的類別, 實作 ISuggestedActionsSourceProvider。 將它導出並命名為 測試建議動作,並設置類型為“text”的 ContentTypeAttribute。
[Export(typeof(ISuggestedActionsSourceProvider))] [Name("Test Suggested Actions")] [ContentType("text")] internal class TestSuggestedActionsSourceProvider : ISuggestedActionsSourceProvider
在來源提供者類別內,匯入 ITextStructureNavigatorSelectorService,並將其新增為 屬性。
[Import(typeof(ITextStructureNavigatorSelectorService))] internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
實作 CreateSuggestedActionsSource 方法,以傳回 ISuggestedActionsSource 物件。 下一節將討論來源。
public ISuggestedActionsSource CreateSuggestedActionsSource(ITextView textView, ITextBuffer textBuffer) { if (textBuffer == null || textView == null) { return null; } return new TestSuggestedActionsSource(this, textView, textBuffer); }
實現 ISuggestedActionSource 介面
建議動作的來源負責收集建議動作的集合,並在適當的背景中新增它們。 在此情況下,上下文是目前的文字,建議的動作是 UpperCaseSuggestedAction 和 LowerCaseSuggestedAction,這些會在以下的章節中進一步討論。
新增實作 ISuggestedActionsSource的類別 到 TestSuggestedActionsSource。
internal class TestSuggestedActionsSource : ISuggestedActionsSource
為建議的動作來源提供者、文字緩衝區和文字檢視新增私用、唯讀字段。
private readonly TestSuggestedActionsSourceProvider m_factory; private readonly ITextBuffer m_textBuffer; private readonly ITextView m_textView;
新增可設定私用欄位的建構函式。
public TestSuggestedActionsSource(TestSuggestedActionsSourceProvider testSuggestedActionsSourceProvider, ITextView textView, ITextBuffer textBuffer) { m_factory = testSuggestedActionsSourceProvider; m_textBuffer = textBuffer; m_textView = textView; }
新增一個私有方法,該方法返回目前位於游標下的單字。 下列方法會查看游標目前的位置,並詢問文字結構導航器以獲取該字的範圍。 如果游標位於單字上,則會在 out 參數中傳回 TextExtent;否則,
out
參數會null
,而 方法會傳回false
。private bool TryGetWordUnderCaret(out TextExtent wordExtent) { ITextCaret caret = m_textView.Caret; SnapshotPoint point; if (caret.Position.BufferPosition > 0) { point = caret.Position.BufferPosition - 1; } else { wordExtent = default(TextExtent); return false; } ITextStructureNavigator navigator = m_factory.NavigatorService.GetTextStructureNavigator(m_textBuffer); wordExtent = navigator.GetExtentOfWord(point); return true; }
實作 HasSuggestedActionsAsync 方法。 編輯器會呼叫此方法,以找出是否要顯示燈泡。 例如,當游標從一行移動到另一行,或滑鼠懸停在錯誤波浪線上方時,這個呼叫常常會被執行。 這是非同步的,目的是允許其他用戶界面操作在此方法運行時繼續執行。 在大部分情況下,此方法需要執行目前行的一些解析和分析,因此處理可能需要一些時間。
在此實作中,它會以異步方式取得 TextExtent,並判斷範圍是否具有意義,也就是說,範圍內是否有除了空白符以外的文字。
public Task<bool> HasSuggestedActionsAsync(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken) { return Task.Factory.StartNew(() => { TextExtent extent; if (TryGetWordUnderCaret(out extent)) { // don't display the action if the extent has whitespace return extent.IsSignificant; } return false; }); }
實作 GetSuggestedActions 方法,這個方法會傳回包含不同 ISuggestedAction 物件之 SuggestedActionSet 對象的陣列。 當燈泡膨脹時,會呼叫這個方法。
警告
您應該確定
HasSuggestedActionsAsync()
和GetSuggestedActions()
的實作是一致的;也就是說,如果HasSuggestedActionsAsync()
傳回true
,則GetSuggestedActions()
應該要顯示一些動作。 在許多情況下,HasSuggestedActionsAsync()
會在GetSuggestedActions()
之前呼叫,但情況不一定如此。 例如,如果使用者按下 (CTRL+ .) 來叫用燈泡動作,則只會呼叫GetSuggestedActions()
。public IEnumerable<SuggestedActionSet> GetSuggestedActions(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken) { TextExtent extent; if (TryGetWordUnderCaret(out extent) && extent.IsSignificant) { ITrackingSpan trackingSpan = range.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive); var upperAction = new UpperCaseSuggestedAction(trackingSpan); var lowerAction = new LowerCaseSuggestedAction(trackingSpan); return new SuggestedActionSet[] { new SuggestedActionSet(new ISuggestedAction[] { upperAction, lowerAction }) }; } return Enumerable.Empty<SuggestedActionSet>(); }
定義
SuggestedActionsChanged
事件。public event EventHandler<EventArgs> SuggestedActionsChanged;
若要完成實作,請新增
Dispose()
和TryGetTelemetryId()
方法的實作。 您不想執行遙測,因此只要傳回false
,並將 GUID 設定為Empty
。public void Dispose() { } public bool TryGetTelemetryId(out Guid telemetryId) { // This is a sample provider and doesn't participate in LightBulb telemetry telemetryId = Guid.Empty; return false; }
執行燈泡動作
在專案中,新增
Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll 的參考,並將 [複製本機設定為 。 建立兩個類別,第一個名為
UpperCaseSuggestedAction
,第二個名為LowerCaseSuggestedAction
。 這兩個類別都會實作 ISuggestedAction。internal class UpperCaseSuggestedAction : ISuggestedAction internal class LowerCaseSuggestedAction : ISuggestedAction
這兩個類別都相同,不同之處在於一個呼叫 ToUpper,另一個呼叫 ToLower。 下列步驟只涵蓋大寫動作類別,但您必須實作這兩個類別。 使用實作大寫動作的步驟作為實作小寫動作的模式。
為這些類別新增下列 using 指令:
using Microsoft.VisualStudio.Imaging.Interop; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media;
宣告一組私有欄位。
private ITrackingSpan m_span; private string m_upper; private string m_display; private ITextSnapshot m_snapshot;
新增可設定欄位的建構函式。
public UpperCaseSuggestedAction(ITrackingSpan span) { m_span = span; m_snapshot = span.TextBuffer.CurrentSnapshot; m_upper = span.GetText(m_snapshot).ToUpper(); m_display = string.Format("Convert '{0}' to upper case", span.GetText(m_snapshot)); }
實作 GetPreviewAsync 方法,使其顯示動作預覽。
public Task<object> GetPreviewAsync(CancellationToken cancellationToken) { var textBlock = new TextBlock(); textBlock.Padding = new Thickness(5); textBlock.Inlines.Add(new Run() { Text = m_upper }); return Task.FromResult<object>(textBlock); }
實作 GetActionSetsAsync 方法,使其傳回空的 SuggestedActionSet 列舉。
public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken) { return Task.FromResult<IEnumerable<SuggestedActionSet>>(null); }
將屬性實施如下所示。
public bool HasActionSets { get { return false; } } public string DisplayText { get { return m_display; } } public ImageMoniker IconMoniker { get { return default(ImageMoniker); } } public string IconAutomationText { get { return null; } } public string InputGestureText { get { return null; } } public bool HasPreview { get { return true; } }
實作 Invoke 方法,將範圍中的文字替換為其大寫形式。
public void Invoke(CancellationToken cancellationToken) { m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper); }
警告
燈泡操作 呼叫 方法不預期會顯示使用者介面。 如果您的操作會顯示新的UI(例如預覽或選取對話框),不要直接從 Invoke 方法內顯示UI,而是應該在從 Invoke返回後安排顯示UI。
若要完成實作,請新增
Dispose()
和TryGetTelemetryId()
方法。public void Dispose() { } public bool TryGetTelemetryId(out Guid telemetryId) { // This is a sample action and doesn't participate in LightBulb telemetry telemetryId = Guid.Empty; return false; }
別忘了將
LowerCaseSuggestedAction
顯示的文字變更為「將 '{0}' 轉換成小寫」,並將 ToUpper 的呼叫變更為 ToLower。
建置及測試程序代碼
若要測試此程式碼,請建置 LightBulbTest 解決方案,並在實驗實例中執行。
建置解決方案。
當您在調試程式中執行此專案時,會啟動第二個Visual Studio實例。
建立文字文件並輸入文字。 您應該會看到文字左邊的燈泡。
指向燈泡。 您應該會看到向下箭頭。
當您按兩下燈泡時,應該會顯示兩個建議的動作,以及所選動作的預覽。
如果您按下第一個動作,則目前單字中的所有文字都應該轉換成大寫。 如果您按下第二個動作,則所有文字都應該轉換成小寫。