diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 38c51f78eac8822ecab3a650ddaa157d745a76ed..d88f2716eabef41419abd735fd6d392b855b8482 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -685,7 +685,9 @@ - + + + \ No newline at end of file diff --git a/src/Features/Core/Portable/NavigateTo/DefaultNavigateToEngineService.SearchResult.cs b/src/Features/Core/Portable/NavigateTo/DefaultNavigateToEngineService.SearchResult.cs index 5677e7dacda7df189444a4b69f6b9a7a6ca7f8b3..982a706559cc89b67ee294df5b140d706279bffa 100644 --- a/src/Features/Core/Portable/NavigateTo/DefaultNavigateToEngineService.SearchResult.cs +++ b/src/Features/Core/Portable/NavigateTo/DefaultNavigateToEngineService.SearchResult.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.NavigateTo { - internal sealed partial class DefaultNavigateToEngineService + internal partial class DefaultNavigateToEngineService { private class SearchResult : INavigateToSearchResult { diff --git a/src/Features/Core/Portable/NavigateTo/DefaultNavigateToEngineService.cs b/src/Features/Core/Portable/NavigateTo/DefaultNavigateToEngineService.cs index 05124883187adf1e1f5a508d2ae3980a3e188360..af1ed0f30e07416a2f8e4caafc3a7f9aaa818eb3 100644 --- a/src/Features/Core/Portable/NavigateTo/DefaultNavigateToEngineService.cs +++ b/src/Features/Core/Portable/NavigateTo/DefaultNavigateToEngineService.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.NavigateTo { [ExportWorkspaceService(typeof(INavigateToEngineService), layer: ServiceLayer.Default), Shared] - internal partial class DefaultNavigateToEngineService : INavigateToEngineService + internal sealed partial class DefaultNavigateToEngineService : INavigateToEngineService { public Task> SearchProjectAsync( Project project, string searchPattern, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Next/NavigateTo/VisualStudioNavigateToEngineService.cs b/src/VisualStudio/Core/Next/NavigateTo/VisualStudioNavigateToEngineService.cs new file mode 100644 index 0000000000000000000000000000000000000000..7d4cbbd8fdd5bdb09f81d14cccc853362e031d26 --- /dev/null +++ b/src/VisualStudio/Core/Next/NavigateTo/VisualStudioNavigateToEngineService.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.NavigateTo; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Remote.Arguments; +using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; +using Microsoft.VisualStudio.LanguageServices.Remote; + +namespace Microsoft.VisualStudio.LanguageServices.NavigateTo +{ + [ExportWorkspaceService(typeof(INavigateToEngineService)), Shared] + internal class VisualStudioNavigateToEngineService : INavigateToEngineService + { + public async Task> SearchDocumentAsync( + Document document, string searchPattern, CancellationToken cancellationToken) + { + var client = await GetRemoteHostClientAsync(document.Project, cancellationToken).ConfigureAwait(false); + if (client == null) + { + return await DefaultNavigateToEngineService.SearchDocumentInCurrentProcessAsync( + document, searchPattern, cancellationToken).ConfigureAwait(false); + } + else + { + return await SearchDocumentInRemoteProcessAsync( + client, document, searchPattern, cancellationToken).ConfigureAwait(false); + } + } + + private async Task> SearchDocumentInRemoteProcessAsync( + RemoteHostClient client, Document document, string searchPattern, CancellationToken cancellationToken) + { + var solution = document.Project.Solution; + + using (var session = await client.CreateCodeAnalysisServiceSessionAsync( + solution, cancellationToken).ConfigureAwait(false)) + { + var serializableResults = await session.InvokeAsync( + WellKnownServiceHubServices.CodeAnalysisService_SearchDocumentAsync, + SerializableDocumentId.Dehydrate(document), + searchPattern).ConfigureAwait(false); + + return serializableResults.Select(r => r.Rehydrate(solution)).ToImmutableArray(); + } + } + + public async Task> SearchProjectAsync( + Project project, string searchPattern, CancellationToken cancellationToken) + { + var client = await GetRemoteHostClientAsync(project, cancellationToken).ConfigureAwait(false); + if (client == null) + { + return await DefaultNavigateToEngineService.SearchProjectInCurrentProcessAsync( + project, searchPattern, cancellationToken).ConfigureAwait(false); + } + else + { + return await SearchProjectInRemoteProcessAsync( + client, project, searchPattern, cancellationToken).ConfigureAwait(false); + } + } + + private async Task> SearchProjectInRemoteProcessAsync( + RemoteHostClient client, Project project, string searchPattern, CancellationToken cancellationToken) + { + var solution = project.Solution; + + using (var session = await client.CreateCodeAnalysisServiceSessionAsync( + solution, cancellationToken).ConfigureAwait(false)) + { + var serializableResults = await session.InvokeAsync( + WellKnownServiceHubServices.CodeAnalysisService_SearchProjectAsync, + SerializableProjectId.Dehydrate(project.Id), + searchPattern).ConfigureAwait(false); + + return serializableResults.Select(r => r.Rehydrate(solution)).ToImmutableArray(); + } + } + + private static Task GetRemoteHostClientAsync( + Project project, CancellationToken cancellationToken) + { + var clientService = project.Solution.Workspace.Services.GetService(); + return clientService.GetRemoteHostClientAsync(cancellationToken); + } + } +} diff --git a/src/VisualStudio/Core/Next/ServicesVisualStudio.Next.csproj b/src/VisualStudio/Core/Next/ServicesVisualStudio.Next.csproj index 923b81c8a530f65bac281620c82782622e6d45e4..57fe37eea6efa050a1338dddf39581cc668def1d 100644 --- a/src/VisualStudio/Core/Next/ServicesVisualStudio.Next.csproj +++ b/src/VisualStudio/Core/Next/ServicesVisualStudio.Next.csproj @@ -89,6 +89,9 @@ Shared\Extensions.cs + + Shared\RemoteArguments.cs + Shared\ServerDirectStream.cs @@ -110,6 +113,7 @@ + @@ -133,5 +137,6 @@ + \ No newline at end of file diff --git a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj index 209b2209d3b0a57c323f0a8df268a03e2364f5c2..c3bc711045d78b5f4c731e1a90e79c18fb48fddc 100644 --- a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj +++ b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj @@ -19,6 +19,10 @@ {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} CodeAnalysis + + {3cdeeab7-2256-418a-beb2-620b5cb16302} + EditorFeatures + {edc68a0e-c68d-4a74-91b7-bf38ec909888} Features @@ -50,6 +54,7 @@ + @@ -74,6 +79,7 @@ + diff --git a/src/Workspaces/Remote/ServiceHub/Shared/RemoteArguments.cs b/src/Workspaces/Remote/ServiceHub/Shared/RemoteArguments.cs new file mode 100644 index 0000000000000000000000000000000000000000..439b74c406c84acf38d44b6e113adeb189c8ce15 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Shared/RemoteArguments.cs @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo; +using Microsoft.CodeAnalysis.NavigateTo; +using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Remote.Arguments +{ + #region Common Arguments + + /// + /// Arguments to pass from client to server when performing operations + /// + internal class SerializableProjectId + { + public Guid Id; + public string DebugName; + + public static SerializableProjectId Dehydrate(ProjectId id) + { + return new SerializableProjectId { Id = id.Id, DebugName = id.DebugName }; + } + + public ProjectId Rehydrate() + { + return ProjectId.CreateFromSerialized(Id, DebugName); + } + } + + internal class SerializableDocumentId + { + public SerializableProjectId ProjectId; + public Guid Id; + public string DebugName; + + public static SerializableDocumentId Dehydrate(Document document) + { + return Dehydrate(document.Id); + } + + public static SerializableDocumentId Dehydrate(DocumentId id) + { + return new SerializableDocumentId + { + ProjectId = SerializableProjectId.Dehydrate(id.ProjectId), + Id = id.Id, + DebugName = id.DebugName + }; + } + + public DocumentId Rehydrate() + { + return DocumentId.CreateFromSerialized( + ProjectId.Rehydrate(), Id, DebugName); + } + } + + internal class SerializableTextSpan + { + public int Start; + public int Length; + + public static SerializableTextSpan Dehydrate(TextSpan textSpan) + { + return new SerializableTextSpan { Start = textSpan.Start, Length = textSpan.Length }; + } + + public TextSpan Rehydrate() + { + return new TextSpan(Start, Length); + } + } + + internal class SerializableTaggedText + { + public string Tag; + public string Text; + + public static SerializableTaggedText Dehydrate(TaggedText taggedText) + { + return new SerializableTaggedText { Tag = taggedText.Tag, Text = taggedText.Text }; + } + + public TaggedText Rehydrate() + { + return new TaggedText(Tag, Text); + } + } + + #endregion + + #region NavigateTo + + internal class SerializableNavigateToSearchResult + { + public string AdditionalInformation; + + public string Kind; + public NavigateToMatchKind MatchKind; + public bool IsCaseSensitive; + public string Name; + public string SecondarySort; + public string Summary; + + public SerializableNavigableItem NavigableItem; + + internal INavigateToSearchResult Rehydrate(Solution solution) + { + return new NavigateToSearchResult( + AdditionalInformation, Kind, MatchKind, IsCaseSensitive, + Name, SecondarySort, Summary, NavigableItem.Rehydrate(solution)); + } + + private class NavigateToSearchResult : INavigateToSearchResult + { + public string AdditionalInformation { get; } + public string Kind { get; } + public NavigateToMatchKind MatchKind { get; } + public bool IsCaseSensitive { get; } + public string Name { get; } + public string SecondarySort { get; } + public string Summary { get; } + + public INavigableItem NavigableItem { get; } + + public NavigateToSearchResult(string additionalInformation, string kind, NavigateToMatchKind matchKind, bool isCaseSensitive, string name, string secondarySort, string summary, INavigableItem navigableItem) + { + AdditionalInformation = additionalInformation; + Kind = kind; + MatchKind = matchKind; + IsCaseSensitive = isCaseSensitive; + Name = name; + SecondarySort = secondarySort; + Summary = summary; + NavigableItem = navigableItem; + } + } + } + + internal class SerializableNavigableItem + { + public Glyph Glyph; + + public SerializableTaggedText[] DisplayTaggedParts; + + public bool DisplayFileLocation; + + public bool IsImplicitlyDeclared; + + public SerializableDocumentId Document; + public SerializableTextSpan SourceSpan; + + SerializableNavigableItem[] ChildItems; + + public INavigableItem Rehydrate(Solution solution) + { + var childItems = ChildItems == null + ? ImmutableArray.Empty + : ChildItems.Select(c => c.Rehydrate(solution)).ToImmutableArray(); + return new NavigableItem( + Glyph, DisplayTaggedParts.Select(p => p.Rehydrate()).ToImmutableArray(), + DisplayFileLocation, IsImplicitlyDeclared, + solution.GetDocument(Document.Rehydrate()), + SourceSpan.Rehydrate(), + childItems); + } + + private class NavigableItem : INavigableItem + { + public Glyph Glyph { get; } + public ImmutableArray DisplayTaggedParts { get; } + public bool DisplayFileLocation { get; } + public bool IsImplicitlyDeclared { get; } + + public Document Document { get; } + public TextSpan SourceSpan { get; } + + public ImmutableArray ChildItems { get; } + + public NavigableItem( + Glyph glyph, ImmutableArray displayTaggedParts, + bool displayFileLocation, bool isImplicitlyDeclared, Document document, TextSpan sourceSpan, ImmutableArray childItems) + { + Glyph = glyph; + DisplayTaggedParts = displayTaggedParts; + DisplayFileLocation = displayFileLocation; + IsImplicitlyDeclared = isImplicitlyDeclared; + Document = document; + SourceSpan = sourceSpan; + ChildItems = childItems; + } + } + } + + #endregion +} \ No newline at end of file diff --git a/src/Workspaces/Remote/ServiceHub/Shared/WellKnownServiceHubServices.cs b/src/Workspaces/Remote/ServiceHub/Shared/WellKnownServiceHubServices.cs index 8ccd643640cd3a0377046a0b8685abce07a6e389..c7db34f37be4a91132e4905a9a57e934e72a0cc1 100644 --- a/src/Workspaces/Remote/ServiceHub/Shared/WellKnownServiceHubServices.cs +++ b/src/Workspaces/Remote/ServiceHub/Shared/WellKnownServiceHubServices.cs @@ -14,6 +14,10 @@ internal static class WellKnownServiceHubServices public const string CodeAnalysisService_FindReferenceMethodsAsync = "FindReferenceMethodsAsync"; public const string CodeAnalysisService_GetFullyQualifiedName = "GetFullyQualifiedName"; + // NavigateTo + public const string CodeAnalysisService_SearchDocumentAsync = "SearchDocumentAsync"; + public const string CodeAnalysisService_SearchProjectAsync = "SearchProjectAsync"; + public const string AssetService_RequestAssetAsync = "RequestAssetAsync"; } }