燈泡是 Visual Studio 編輯器中的圖示,可展開以顯示一組動作,例如,修正內建程式代碼分析器或程式碼重構所識別的問題。
在 Visual C# 和 Visual Basic 編輯器中,您也可以使用 .NET 編譯程式平臺 (“Roslyn”) 撰寫並封裝您自己的程式代碼分析器,並自動顯示燈泡的動作。 如需詳細資訊,請參閱:
-
C++等其他語言也提供燈泡,以供某些快速動作使用,例如,建議建立該函式的存根實作。
以下是燈泡的外觀。 在 Visual Basic 或 Visual C# 專案中,當變數名稱無效時,紅色波浪線會出現在變數名稱下。 如果您將滑鼠停留在無效的標識碼上,游標附近會出現燈泡。
如果您按兩下燈泡的向下箭號,就會顯示一組建議的動作,以及選取動作的預覽。 在此情況下,它會顯示當您執行動作時,對程式代碼所做的變更。
您可以使用燈泡來提供您自己的建議動作。 例如,您可以提供動作,將左大括弧移到新行,或將它們移至前一行的結尾。 下列逐步解說展示如何建立一個會出現在當前單字上的燈泡,並提供兩個建議動作:轉換成大寫 和 轉換成小寫。
建立受控擴充性架構 (MEF) 專案
建立 C# VSIX 專案。 (在 [新增專案] 對話框中,選取 [Visual C# / Extensibility],然後 VSIX 專案。將專案命名為
LightBulbTest
。將 編輯器分類器 項目範本新增至專案。 如需詳細資訊,請參閱 使用編輯器專案範本建立延伸模組。
刪除現有的類別檔案。
將下列參考新增至專案,並將 [複製本機] 設定為
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。 將其以名稱為 的測試建議動作 和 ContentTypeAttribute 的“text” 導出。
[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,詳情將在下一節討論。
在 中新增一個 Class,並實作 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 的參考,並將 複製本機 設定為
False
。建立兩個類別,第一個名為
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 方法,將 span 中的文字替換為其大寫形式。
public void Invoke(CancellationToken cancellationToken) { m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper); }
警告
燈泡動作 Invoke 方法預期不會顯示使用者介面。 如果您的動作會顯示新的使用者介面(例如預覽或選擇對話框),請勿直接從 Invoke 方法內呈現介面,而應在從 Invoke返回後安排顯示介面的時機。
若要完成實作,請新增
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實例。
建立文字文件並輸入文字。 您應該會看到文字左邊的燈泡。
指向燈泡。 您應該會看到向下箭號。
當您按兩下燈泡時,應該會顯示兩個建議的動作,以及所選動作的預覽。
如果您按下第一個動作,則目前單字中的所有文字都應該轉換成大寫。 如果您按下第二個動作,則所有文字都應該轉換成小寫。