|
// 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.
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeDefinitionWindow;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeDefinitionWindow;
[Export(typeof(ICodeDefinitionWindowService)), Shared]
internal sealed class VisualStudioCodeDefinitionWindowService : ICodeDefinitionWindowService
{
private readonly IVsService<IVsCodeDefView> _codeDefView;
private readonly IThreadingContext _threadingContext;
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public VisualStudioCodeDefinitionWindowService(IVsService<SVsCodeDefView, IVsCodeDefView> codeDefView, IThreadingContext threadingContext)
{
_codeDefView = codeDefView;
_threadingContext = threadingContext;
}
public async Task<bool> IsWindowOpenAsync(CancellationToken cancellationToken)
{
var vsCodeDefView = await _codeDefView.GetValueAsync(cancellationToken).ConfigureAwait(true);
// Switch to the UI thread before using the IVsCodeDefView service
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
// IsVisible returns S_FALSE if it's not visible
return vsCodeDefView.IsVisible() == VSConstants.S_OK;
}
public async Task SetContextAsync(ImmutableArray<CodeDefinitionWindowLocation> locations, CancellationToken cancellationToken)
{
// If the new context has no location, then just don't update, instead of showing the
// "No definition selected" page.
if (!locations.Any())
{
return;
}
var vsCodeDefView = await _codeDefView.GetValueAsync(cancellationToken).ConfigureAwait(true);
// Switch to the UI thread before using the IVsCodeDefView service
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
Marshal.ThrowExceptionForHR(vsCodeDefView.SetContext(new Context(locations)));
}
private class Context : IVsCodeDefViewContext
{
private readonly ImmutableArray<CodeDefinitionWindowLocation> _locations;
public Context(ImmutableArray<CodeDefinitionWindowLocation> locations)
{
_locations = locations;
}
int IVsCodeDefViewContext.GetCount(out uint pcItems)
{
pcItems = (uint)_locations.Length;
return VSConstants.S_OK;
}
int IVsCodeDefViewContext.GetSymbolName(uint iItem, out string pbstrSymbolName)
{
pbstrSymbolName = GetItem(iItem).DisplayName;
return VSConstants.S_OK;
}
int IVsCodeDefViewContext.GetFileName(uint iItem, out string pbstrFilename)
{
pbstrFilename = GetItem(iItem).FilePath;
return VSConstants.S_OK;
}
int IVsCodeDefViewContext.GetLine(uint iItem, out uint piLine)
{
piLine = (uint)GetItem(iItem).Position.Line;
return VSConstants.S_OK;
}
int IVsCodeDefViewContext.GetCol(uint iItem, out uint piCol)
{
piCol = (uint)GetItem(iItem).Position.Character;
return VSConstants.S_OK;
}
private CodeDefinitionWindowLocation GetItem(uint iItem)
{
var index = (int)iItem;
if (index < 0 || index >= _locations.Length)
{
throw new ArgumentOutOfRangeException(nameof(iItem));
}
return _locations[index];
}
}
}
|