Skip to content

Introduce the new provider model with CodeLens and document symbol providers #497

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 5 commits into from
Jun 8, 2017
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
47 changes: 47 additions & 0 deletions src/PowerShellEditorServices.Host/CodeLens/CodeLensExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System.Management.Automation.Language;
using Microsoft.PowerShell.EditorServices.CodeLenses;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using LanguageServer = Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;

namespace Microsoft.PowerShell.EditorServices
{
public static class ICodeLensExtensions
{
public static LanguageServer.CodeLens ToProtocolCodeLens(
this CodeLens codeLens,
JsonSerializer jsonSerializer)
{
return new LanguageServer.CodeLens
{
Range = codeLens.ScriptExtent.ToRange(),
Command = codeLens.Command.ToProtocolCommand(jsonSerializer)
};
}

public static LanguageServer.CodeLens ToProtocolCodeLens(
this CodeLens codeLens,
object codeLensData,
JsonSerializer jsonSerializer)
{
LanguageServer.ServerCommand command = null;

if (codeLens.Command != null)
{
command = codeLens.Command.ToProtocolCommand(jsonSerializer);
}

return new LanguageServer.CodeLens
{
Range = codeLens.ScriptExtent.ToRange(),
Data = JToken.FromObject(codeLensData, jsonSerializer),
Command = command
};
}
}
}
159 changes: 159 additions & 0 deletions src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using Microsoft.PowerShell.EditorServices.Components;
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using Microsoft.PowerShell.EditorServices.Utility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using LanguageServer = Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;

namespace Microsoft.PowerShell.EditorServices.CodeLenses
{
internal class CodeLensFeature : ICodeLenses
{
private EditorSession editorSession;

private JsonSerializer jsonSerializer =
JsonSerializer.Create(
Constants.JsonSerializerSettings);

public IFeatureProviderCollection<ICodeLensProvider> Providers { get; } =
new FeatureProviderCollection<ICodeLensProvider>();

public CodeLensFeature(
EditorSession editorSession,
IMessageHandlers messageHandlers,
ILogger logger)
{
this.editorSession = editorSession;

messageHandlers.SetRequestHandler(
CodeLensRequest.Type,
this.HandleCodeLensRequest);

messageHandlers.SetRequestHandler(
CodeLensResolveRequest.Type,
this.HandleCodeLensResolveRequest);
}

public static CodeLensFeature Create(
IComponentRegistry components,
EditorSession editorSession)
{
var codeLenses =
new CodeLensFeature(
editorSession,
components.Get<IMessageHandlers>(),
components.Get<ILogger>());

codeLenses.Providers.Add(
new ReferencesCodeLensProvider(
editorSession));


editorSession.Components.Register<ICodeLenses>(codeLenses);

return codeLenses;
}

public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile)
{
return
this.Providers
.SelectMany(p => p.ProvideCodeLenses(scriptFile))
.ToArray();
}

private async Task HandleCodeLensRequest(
CodeLensRequest codeLensParams,
RequestContext<LanguageServer.CodeLens[]> requestContext)
{
JsonSerializer jsonSerializer =
JsonSerializer.Create(
Constants.JsonSerializerSettings);

var scriptFile =
this.editorSession.Workspace.GetFile(
codeLensParams.TextDocument.Uri);

var codeLenses =
this.ProvideCodeLenses(scriptFile)
.Select(
codeLens =>
codeLens.ToProtocolCodeLens(
new CodeLensData
{
Uri = codeLens.File.ClientFilePath,
ProviderId = codeLens.Provider.ProviderId
},
this.jsonSerializer))
.ToArray();

await requestContext.SendResult(codeLenses);
}

private async Task HandleCodeLensResolveRequest(
LanguageServer.CodeLens codeLens,
RequestContext<LanguageServer.CodeLens> requestContext)
{
if (codeLens.Data != null)
{
// TODO: Catch deserializtion exception on bad object
CodeLensData codeLensData = codeLens.Data.ToObject<CodeLensData>();

ICodeLensProvider originalProvider =
this.Providers.FirstOrDefault(
provider => provider.ProviderId.Equals(codeLensData.ProviderId));

if (originalProvider != null)
{
ScriptFile scriptFile =
this.editorSession.Workspace.GetFile(
codeLensData.Uri);

ScriptRegion region = new ScriptRegion
{
StartLineNumber = codeLens.Range.Start.Line + 1,
StartColumnNumber = codeLens.Range.Start.Character + 1,
EndLineNumber = codeLens.Range.End.Line + 1,
EndColumnNumber = codeLens.Range.End.Character + 1
};

CodeLens originalCodeLens =
new CodeLens(
originalProvider,
scriptFile,
region);

var resolvedCodeLens =
await originalProvider.ResolveCodeLensAsync(
originalCodeLens,
CancellationToken.None);

await requestContext.SendResult(
resolvedCodeLens.ToProtocolCodeLens(
this.jsonSerializer));
}
else
{
// TODO: Write error!
}
}
}

private class CodeLensData
{
public string Uri { get; set; }

public string ProviderId {get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System.Management.Automation.Language;
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;

namespace Microsoft.PowerShell.EditorServices
{
public static class IScriptExtentExtensions
{
public static Range ToRange(this IScriptExtent scriptExtent)
{
return new Range
{
Start = new Position
{
Line = scriptExtent.StartLineNumber - 1,
Character = scriptExtent.StartColumnNumber - 1
},
End = new Position
{
Line = scriptExtent.EndLineNumber - 1,
Character = scriptExtent.EndColumnNumber - 1
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.PowerShell.EditorServices.Commands;
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;
using Microsoft.PowerShell.EditorServices.Symbols;

namespace Microsoft.PowerShell.EditorServices.CodeLenses
{
internal class ReferencesCodeLensProvider : FeatureProviderBase, ICodeLensProvider
{
private EditorSession editorSession;
private IDocumentSymbolProvider symbolProvider;

public ReferencesCodeLensProvider(EditorSession editorSession)
{
this.editorSession = editorSession;

// TODO: Pull this from components
this.symbolProvider =
new ScriptDocumentSymbolProvider(
editorSession.PowerShellContext.LocalPowerShellVersion.Version);
}

public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile)
{
return
this.symbolProvider
.ProvideDocumentSymbols(scriptFile)
.Where(symbol => symbol.SymbolType == SymbolType.Function)
.Select(
symbol =>
new CodeLens(
this,
scriptFile,
symbol.ScriptRegion))
.ToArray();
}

public async Task<CodeLens> ResolveCodeLensAsync(
CodeLens codeLens,
CancellationToken cancellationToken)
{
ScriptFile[] references =
editorSession.Workspace.ExpandScriptReferences(
codeLens.File);

var foundSymbol =
this.editorSession.LanguageService.FindFunctionDefinitionAtLocation(
codeLens.File,
codeLens.ScriptExtent.StartLineNumber,
codeLens.ScriptExtent.StartColumnNumber);

FindReferencesResult referencesResult =
await editorSession.LanguageService.FindReferencesOfSymbol(
foundSymbol,
references,
editorSession.Workspace);

var referenceLocations =
referencesResult
.FoundReferences
.Select(
r => new Location
{
Uri = GetFileUri(r.FilePath),
Range = r.ScriptRegion.ToRange()
})
.ToArray();

return
new CodeLens(
codeLens,
new ClientCommand(
"editor.action.showReferences",
referenceLocations.Length == 1
? "1 reference"
: $"{referenceLocations.Length} references",
new object[]
{
codeLens.File.ClientFilePath,
codeLens.ScriptExtent.ToRange().Start,
referenceLocations,
}
));
}

private static string GetFileUri(string filePath)
{
// If the file isn't untitled, return a URI-style path
return
!filePath.StartsWith("untitled")
? new Uri("file://" + filePath).AbsoluteUri
: filePath;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using Microsoft.PowerShell.EditorServices.Commands;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using LanguageServer = Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;

namespace Microsoft.PowerShell.EditorServices
{
public static class ClientCommandExtensions
{
public static LanguageServer.ServerCommand ToProtocolCommand(
this ClientCommand clientCommand,
JsonSerializer jsonSerializer)
{
return new LanguageServer.ServerCommand
{
Command = clientCommand.Name,
Title = clientCommand.Title,
Arguments =
JArray.FromObject(
clientCommand.Arguments,
jsonSerializer)
};
}
}
}
Loading