Skip to content

Remove Semantic Search inline Copilot prototype #78323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using System;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
Expand All @@ -16,7 +15,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Navigation;
Expand Down Expand Up @@ -59,8 +57,6 @@ internal sealed partial class SemanticSearchToolWindowImpl(
VisualStudioWorkspace workspace,
IStreamingFindUsagesPresenter resultsPresenter,
ITextUndoHistoryRegistry undoHistoryRegistry,
ISemanticSearchCopilotService copilotService,
Lazy<ISemanticSearchCopilotUIProvider> copilotUIProvider, // lazy to avoid loading Microsoft.VisualStudio.LanguageServices.ExternalAccess.Copilot
IVsService<SVsUIShell, IVsUIShell> vsUIShellProvider) : IDisposable
{
private const int ToolBarHeight = 26;
Expand Down Expand Up @@ -120,9 +116,7 @@ private async Task<FrameworkElement> CreateContentAsync(CancellationToken cancel

var vsUIShell = await vsUIShellProvider.GetValueAsync(cancellationToken).ConfigureAwait(false);

var copilotUI = CreateCopilotUI();

var textViewHost = CreateTextViewHost(vsUIShell, copilotUI);
var textViewHost = CreateTextViewHost(vsUIShell);
var textViewControl = textViewHost.HostControl;
_textView = textViewHost.TextView;
_textBuffer = textViewHost.TextView.TextBuffer;
Expand Down Expand Up @@ -167,11 +161,6 @@ private async Task<FrameworkElement> CreateContentAsync(CancellationToken cancel

toolWindowGrid.Children.Add(toolbarGrid);

if (copilotUI != null)
{
toolWindowGrid.Children.Add(copilotUI.Control);
}

toolWindowGrid.Children.Add(textViewControl);
toolbarGrid.Children.Add(executeButton);
toolbarGrid.Children.Add(cancelButton);
Expand All @@ -181,12 +170,6 @@ private async Task<FrameworkElement> CreateContentAsync(CancellationToken cancel
Grid.SetRow(toolbarGrid, 0);
Grid.SetColumn(toolbarGrid, 0);

if (copilotUI != null)
{
Grid.SetRow(copilotUI.Control, 1);
Grid.SetColumn(copilotUI.Control, 0);
}

Grid.SetRow(textViewControl, 2);
Grid.SetColumn(textViewControl, 0);

Expand All @@ -205,106 +188,6 @@ private async Task<FrameworkElement> CreateContentAsync(CancellationToken cancel
return toolWindowGrid;
}

private CopilotUI? CreateCopilotUI()
{
if (!copilotUIProvider.Value.IsAvailable || !copilotService.IsAvailable)
{
return null;
}

if (!globalOptions.GetOption(SemanticSearchFeatureFlag.PromptEnabled))
{
return null;
}

var outerGrid = new Grid()
{
Background = (Brush)Application.Current.FindResource(CommonControlsColors.TextBoxBackgroundBrushKey),
};

ImageThemingUtilities.SetImageBackgroundColor(outerGrid, (Color)Application.Current.Resources[CommonDocumentColors.PageBackgroundColorKey]);
ThemedDialogStyleLoader.SetUseDefaultThemedDialogStyles(outerGrid, true);

// [ prompt border | empty ]
outerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
outerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

var promptGrid = new Grid();

// [ input | panel ]
promptGrid.ColumnDefinitions.Add(new ColumnDefinition { MaxWidth = 600, Width = GridLength.Auto });
promptGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

var promptTextBox = copilotUIProvider.Value.GetTextBox();

var panel = new StackPanel()
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Bottom,
Margin = new Thickness(8, 8, 0, 8),
};

Grid.SetColumn(promptTextBox.Control, 0);
promptGrid.Children.Add(promptTextBox.Control);

Grid.SetColumn(panel, 1);
promptGrid.Children.Add(panel);

var promptGridBorder = new Border
{
Name = "PromptBorder",
BorderBrush = (Brush)Application.Current.Resources[EnvironmentColors.SystemHighlightBrushKey],
BorderThickness = new Thickness(1),
Child = promptGrid
};

Grid.SetColumn(promptGridBorder, 0);
outerGrid.Children.Add(promptGridBorder);

// ComboBox for model selection
var modelPicker = new ComboBox
{
SelectedIndex = 0,
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(4, 0, 4, 0),
Height = 24,
IsEditable = false,
IsReadOnly = true,
BorderThickness = new Thickness(0),
MinHeight = 24,
VerticalContentAlignment = VerticalAlignment.Top,
TabIndex = 1,
Style = (Style)Application.Current.FindResource(VsResourceKeys.ComboBoxStyleKey)
};

modelPicker.Items.Add("gpt-4o");
modelPicker.Items.Add("gpt-4o-mini");
modelPicker.Items.Add("o1");
modelPicker.Items.Add("o1-ga");
modelPicker.Items.Add("o1-mini");

panel.Children.Add(modelPicker);

var submitButton = CreateButton(
KnownMonikers.Send,
automationName: "Generate query",
acceleratorKey: "Ctrl+Enter",
toolTip: "Generate query");

panel.Children.Add(submitButton);

submitButton.Click += (_, _) => SubmitCopilotQuery(promptTextBox.Text, modelPicker.Text);

return new CopilotUI()
{
Control = outerGrid,
Input = promptTextBox,
ModelPicker = modelPicker,
};
}

private static Button CreateButton(
Imaging.Interop.ImageMoniker moniker,
string automationName,
Expand Down Expand Up @@ -383,7 +266,7 @@ private static ControlTemplate CreateButtonTemplate()
""", context);
}

private IWpfTextViewHost CreateTextViewHost(IVsUIShell vsUIShell, CopilotUI? copilotUI)
private IWpfTextViewHost CreateTextViewHost(IVsUIShell vsUIShell)
{
Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread);

Expand Down Expand Up @@ -422,7 +305,7 @@ private IWpfTextViewHost CreateTextViewHost(IVsUIShell vsUIShell, CopilotUI? cop

ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID.VSFPROPID_ViewHelper, textViewAdapter));

_ = new CommandFilter(this, textViewAdapter, copilotUI);
_ = new CommandFilter(this, textViewAdapter);

return textViewHost;
}
Expand All @@ -448,62 +331,6 @@ private void UpdateUIState()
_cancelButton.IsEnabled = isExecuting;
}

private void SubmitCopilotQuery(string input, string model)
{
Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread);
Contract.ThrowIfNull(_textBuffer);
Contract.ThrowIfNull(copilotService);

// TODO: hook up cancel button for copilot queries
var cancellationSource = new CancellationTokenSource();

// TODO: fade out current content and show overlay spinner

var completionToken = _asyncListener.BeginAsyncOperation(nameof(SemanticSearchToolWindow) + "." + nameof(SubmitCopilotQuery));
_ = ExecuteAsync(cancellationSource.Token).ReportNonFatalErrorAsync().CompletesAsyncOperation(completionToken);

async Task ExecuteAsync(CancellationToken cancellationToken)
{
await TaskScheduler.Default;

SemanticSearchCopilotGeneratedQuery query;

// TODO: generate list from SemanticSearch.ReferenceAssemblies:
var codeAnalysisVersion = new Version(4, 14, 0);
var sdkVersion = new Version(9, 0, 0);

var context = new SemanticSearchCopilotContext()
{
ModelName = model,
AvailablePackages =
[
("Microsoft.CodeAnalysis", codeAnalysisVersion),
("Microsoft.CodeAnalysis.CSharp", codeAnalysisVersion),
("System.Collections.Immutable", sdkVersion),
("System.Collections", sdkVersion),
("System.Linq", sdkVersion),
("System.Runtime", sdkVersion),
]
};

try
{
query = await copilotService.TryGetQueryAsync(input, context, cancellationToken).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical))
{
return;
}
catch (OperationCanceledException)
{
return;
}

await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(CancellationToken.None);
SetEditorText(query.Text);
}
}

/// <summary>
/// Replaces current text buffer content with specified <paramref name="text"/>. Allow using Ctrl+Z to revert to the previous content.
/// Must be invoked on UI thread.
Expand Down Expand Up @@ -613,36 +440,19 @@ public NavigableLocation GetNavigableLocation(TextSpan textSpan)
return true;
});

private sealed class CopilotUI
{
public required FrameworkElement Control { get; init; }
public required ITextBoxControl Input { get; init; }
public required ComboBox ModelPicker { get; init; }
}

private sealed class CommandFilter : IOleCommandTarget
{
private readonly SemanticSearchToolWindowImpl _window;
private readonly IOleCommandTarget _editorCommandTarget;
private readonly CopilotUI? _copilotUI;

public CommandFilter(SemanticSearchToolWindowImpl window, IVsTextView textView, CopilotUI? copilotUI)
public CommandFilter(SemanticSearchToolWindowImpl window, IVsTextView textView)
{
_window = window;
_copilotUI = copilotUI;
ErrorHandler.ThrowOnFailure(textView.AddCommandFilter(this, out _editorCommandTarget));
}

[MemberNotNullWhen(true, nameof(_copilotUI))]
private bool HasCopilotInputFocus
=> _copilotUI?.Input.View.HasAggregateFocus == true;

public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
{
var target = HasCopilotInputFocus ? _copilotUI.Input.CommandTarget : _editorCommandTarget;

return target.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
}
=> _editorCommandTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);

public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
Expand All @@ -651,12 +461,6 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv
switch ((VSConstants.VSStd2KCmdID)nCmdID)
{
case VSConstants.VSStd2KCmdID.OPENLINEABOVE:
if (HasCopilotInputFocus)
{
_window.SubmitCopilotQuery(_copilotUI.Input.Text, _copilotUI.ModelPicker.Text);
return VSConstants.S_OK;
}

if (!_window.IsExecutingUIState())
{
_window.RunQuery();
Expand All @@ -676,8 +480,7 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv
}
}

var target = HasCopilotInputFocus ? _copilotUI.Input.CommandTarget : _editorCommandTarget;
return target.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
return _editorCommandTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti
{"dotnet_unsupported_report_invalid_json_patterns", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ReportInvalidJsonPatterns")},
{"visual_studio_enable_key_binding_reset", new FeatureFlagStorage("Roslyn.KeybindingResetEnabled")},
{"visual_studio_enable_semantic_search", new FeatureFlagStorage("Roslyn.SemanticSearchEnabled")},
{"visual_studio_enable_semantic_search_prompt", new FeatureFlagStorage("Roslyn.ShowPromptInSemanticSearch")},
{"visual_studio_enable_copilot_rename_context", new FeatureFlagStorage("Roslyn.CopilotRenameGetContext")},
{"visual_studio_key_binding_needs_reset", new LocalUserProfileStorage(@"Roslyn\Internal\KeybindingsStatus", "NeedsReset")},
{"visual_studio_key_binding_reset_never_show_again", new LocalUserProfileStorage(@"Roslyn\Internal\KeybindingsStatus", "NeverShowAgain")},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace Microsoft.VisualStudio.LanguageServices;
internal static class SemanticSearchFeatureFlag
{
public static readonly Option2<bool> Enabled = new("visual_studio_enable_semantic_search", defaultValue: false);
public static readonly Option2<bool> PromptEnabled = new("visual_studio_enable_semantic_search_prompt", defaultValue: false);

/// <summary>
/// Context id that indicates that Semantic Search feature is enabled.
Expand Down

This file was deleted.

Loading