|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.VisualStudio.LanguageServices.Implementation.DebuggerIntelliSense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService;
internal abstract partial class AbstractLanguageService<TPackage, TLanguageService> : IVsImmediateStatementCompletion2
{
protected Dictionary<IVsTextView, DebuggerIntelliSenseFilter> filters = [];
int IVsImmediateStatementCompletion2.EnableStatementCompletion(int enable, int startIndex, int endIndex, IVsTextView textView)
{
if (filters.TryGetValue(textView, out var filter))
{
if (enable != 0)
{
filter.EnableCompletion();
}
else
{
filter.DisableCompletion();
}
}
// Debugger wants Roslyn to return OK in all cases,
// for example, even if Rolsyn tried to enable the one already enabled.
return VSConstants.S_OK;
}
int IVsImmediateStatementCompletion2.InstallStatementCompletion(int install, IVsTextView textView, int initialEnable)
{
// We'll install a filter whenever the debugger asks, but it won't do anything but call
// the next filter until the context is set. To ensure that we correctly install and
// uninstall from the many possible textviews we can work on, we maintain a dictionary
// of textview->filters.
if (install != 0)
{
if (!this.filters.TryGetValue(textView, out var filter))
{
filter = new DebuggerIntelliSenseFilter(
this.EditorAdaptersFactoryService.GetWpfTextView(textView),
this.Package.ComponentModel,
this.Package.ComponentModel.GetService<IFeatureServiceFactory>());
this.filters[textView] = filter;
Marshal.ThrowExceptionForHR(textView.AddCommandFilter(filter, out var nextFilter));
filter.SetNextFilter(nextFilter);
}
filter.SetContentType(install: true);
}
else
{
Marshal.ThrowExceptionForHR(textView.RemoveCommandFilter(this.filters[textView]));
this.filters[textView].SetContentType(install: false);
this.filters[textView].Dispose();
this.filters.Remove(textView);
}
return VSConstants.S_OK;
}
int IVsImmediateStatementCompletion2.SetCompletionContext(string filePath,
IVsTextLines buffer,
TextSpan[] currentStatementSpan,
object punkContext,
IVsTextView textView)
{
// The immediate window is always marked read-only and the language service is
// responsible for asking the buffer to make itself writable. We'll have to do that for
// commit, so we need to drag the IVsTextLines around, too.
Marshal.ThrowExceptionForHR(textView.GetBuffer(out var debuggerBuffer));
var view = EditorAdaptersFactoryService.GetWpfTextView(textView);
// Sometimes, they give us a null context buffer. In that case, there's probably not any
// work to do.
if (buffer != null)
{
var contextBuffer = EditorAdaptersFactoryService.GetDataBuffer(buffer);
if (!contextBuffer.ContentType.IsOfType(this.ContentTypeName))
{
FatalError.ReportAndCatch(
new ArgumentException($"Expected content type {this.ContentTypeName} " +
$"but got buffer of content type {contextBuffer.ContentType}"));
return VSConstants.E_FAIL;
}
// Clean the old context in any case upfront:
// even if we fail to initialize, the old context must be cleaned.
this.filters[textView].RemoveContext();
var context = CreateContext(view, textView, debuggerBuffer, contextBuffer, currentStatementSpan);
if (context.TryInitialize())
{
this.filters[textView].SetContext(context);
}
}
return VSConstants.S_OK;
}
// Let our deriving language services build up an appropriate context.
protected abstract AbstractDebuggerIntelliSenseContext CreateContext(IWpfTextView view,
IVsTextView vsTextView,
IVsTextLines debuggerBuffer,
ITextBuffer contextBuffer,
Microsoft.VisualStudio.TextManager.Interop.TextSpan[] currentStatementSpan);
#region Methods that are never called
int IVsImmediateStatementCompletion2.EnableStatementCompletion_Deprecated(int enable, int startIndex, int endIndex)
{
Debug.Assert(false);
return VSConstants.S_OK;
}
int IVsImmediateStatementCompletion2.GetFilter(IVsTextView textView, out IVsTextViewFilter filter)
{
// They never even call this, so just make it compile
Debug.Assert(false);
filter = null;
return VSConstants.S_OK;
}
int IVsImmediateStatementCompletion.EnableStatementCompletion_Deprecated(int enable, int startIndex, int endIndex)
{
Debug.Assert(false);
return VSConstants.S_OK;
}
int IVsImmediateStatementCompletion.InstallStatementCompletion(int install, IVsTextView textView, int initialEnable)
{
Debug.Assert(false);
return VSConstants.S_OK;
}
int IVsImmediateStatementCompletion.SetCompletionContext_Deprecated(string filePath, IVsTextLines buffer, TextSpan[] currentStatementSpan, object punkContext)
{
Debug.Assert(false);
return VSConstants.S_OK;
}
int IVsImmediateStatementCompletion2.SetCompletionContext_Deprecated(string filepath, IVsTextLines buffer, TextSpan[] currentStatementSpan, object punkContext)
{
Debug.Assert(false);
return VSConstants.S_OK;
}
#endregion
}
|