Skip to content

Commit b7e2531

Browse files
Completion Support (PowerShell#1007)
* completion support * misc codacy fixes * use BUILD_ARTIFACTSTAGINGDIRECTORY so logs can be uploaded * publish artifacts even if build fails * handle log messages * give PSES a chance to run what it needs to run * switch to using xUnit output helper * treat DynamicKeywords as Keyword
1 parent 95dc4ab commit b7e2531

File tree

8 files changed

+780
-91
lines changed

8 files changed

+780
-91
lines changed

.vsts-ci/templates/ci-general.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ steps:
2323
inputs:
2424
ArtifactName: PowerShellEditorServices-CI
2525
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
26+
condition: succeededOrFailed()

src/PowerShellEditorServices.Engine/LanguageServer/OmnisharpLanguageServer.cs

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public async Task StartAsync()
110110
.WithHandler<CodeLensHandlers>()
111111
.WithHandler<CodeActionHandler>()
112112
.WithHandler<InvokeExtensionCommandHandler>()
113+
.WithHandler<CompletionHandler>()
113114
.OnInitialize(
114115
async (languageServer, request) =>
115116
{

src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs

-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ public class SymbolsService
2121
{
2222
#region Private Fields
2323

24-
const int DefaultWaitTimeoutMilliseconds = 5000;
25-
2624
private readonly ILogger _logger;
2725

2826
private readonly IDocumentSymbolProvider[] _documentSymbolProviders;

src/PowerShellEditorServices.Engine/Services/Symbols/Vistors/AstOperations.cs

+93-87
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,15 @@
55

66
using System;
77
using System.Collections.Generic;
8+
using System.Diagnostics;
89
using System.Linq;
10+
using System.Management.Automation;
911
using System.Management.Automation.Language;
12+
using System.Reflection;
13+
using System.Threading;
14+
using System.Threading.Tasks;
15+
using Microsoft.Extensions.Logging;
16+
using Microsoft.PowerShell.EditorServices.Utility;
1017

1118
namespace Microsoft.PowerShell.EditorServices.Symbols
1219
{
@@ -17,11 +24,11 @@ internal static class AstOperations
1724
{
1825
// TODO: When netstandard is upgraded to 2.0, see if
1926
// Delegate.CreateDelegate can be used here instead
20-
//private static readonly MethodInfo s_extentCloneWithNewOffset = typeof(PSObject).GetTypeInfo().Assembly
21-
// .GetType("System.Management.Automation.Language.InternalScriptPosition")
22-
// .GetMethod("CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic);
27+
private static readonly MethodInfo s_extentCloneWithNewOffset = typeof(PSObject).GetTypeInfo().Assembly
28+
.GetType("System.Management.Automation.Language.InternalScriptPosition")
29+
.GetMethod("CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic);
2330

24-
//private static readonly SemaphoreSlim s_completionHandle = AsyncUtils.CreateSimpleLockingSemaphore();
31+
private static readonly SemaphoreSlim s_completionHandle = AsyncUtils.CreateSimpleLockingSemaphore();
2532

2633
// TODO: BRING THIS BACK
2734
/// <summary>
@@ -48,89 +55,88 @@ internal static class AstOperations
4855
/// A CommandCompletion instance that contains completions for the
4956
/// symbol at the given offset.
5057
/// </returns>
51-
// static public async Task<CommandCompletion> GetCompletionsAsync(
52-
// Ast scriptAst,
53-
// Token[] currentTokens,
54-
// int fileOffset,
55-
// PowerShellContext powerShellContext,
56-
// ILogger logger,
57-
// CancellationToken cancellationToken)
58-
// {
59-
// if (!s_completionHandle.Wait(0))
60-
// {
61-
// return null;
62-
// }
63-
64-
// try
65-
// {
66-
// IScriptPosition cursorPosition = (IScriptPosition)s_extentCloneWithNewOffset.Invoke(
67-
// scriptAst.Extent.StartScriptPosition,
68-
// new object[] { fileOffset });
69-
70-
// logger.Write(
71-
// LogLevel.Verbose,
72-
// string.Format(
73-
// "Getting completions at offset {0} (line: {1}, column: {2})",
74-
// fileOffset,
75-
// cursorPosition.LineNumber,
76-
// cursorPosition.ColumnNumber));
77-
78-
// if (!powerShellContext.IsAvailable)
79-
// {
80-
// return null;
81-
// }
82-
83-
// var stopwatch = new Stopwatch();
84-
85-
// // If the current runspace is out of process we can use
86-
// // CommandCompletion.CompleteInput because PSReadLine won't be taking up the
87-
// // main runspace.
88-
// if (powerShellContext.IsCurrentRunspaceOutOfProcess())
89-
// {
90-
// using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandleAsync(cancellationToken))
91-
// using (PowerShell powerShell = PowerShell.Create())
92-
// {
93-
// powerShell.Runspace = runspaceHandle.Runspace;
94-
// stopwatch.Start();
95-
// try
96-
// {
97-
// return CommandCompletion.CompleteInput(
98-
// scriptAst,
99-
// currentTokens,
100-
// cursorPosition,
101-
// options: null,
102-
// powershell: powerShell);
103-
// }
104-
// finally
105-
// {
106-
// stopwatch.Stop();
107-
// logger.Write(LogLevel.Verbose, $"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms.");
108-
// }
109-
// }
110-
// }
111-
112-
// CommandCompletion commandCompletion = null;
113-
// await powerShellContext.InvokeOnPipelineThreadAsync(
114-
// pwsh =>
115-
// {
116-
// stopwatch.Start();
117-
// commandCompletion = CommandCompletion.CompleteInput(
118-
// scriptAst,
119-
// currentTokens,
120-
// cursorPosition,
121-
// options: null,
122-
// powershell: pwsh);
123-
// });
124-
// stopwatch.Stop();
125-
// logger.Write(LogLevel.Verbose, $"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms.");
126-
127-
// return commandCompletion;
128-
// }
129-
// finally
130-
// {
131-
// s_completionHandle.Release();
132-
// }
133-
// }
58+
static public async Task<CommandCompletion> GetCompletionsAsync(
59+
Ast scriptAst,
60+
Token[] currentTokens,
61+
int fileOffset,
62+
PowerShellContextService powerShellContext,
63+
ILogger logger,
64+
CancellationToken cancellationToken)
65+
{
66+
if (!s_completionHandle.Wait(0))
67+
{
68+
return null;
69+
}
70+
71+
try
72+
{
73+
IScriptPosition cursorPosition = (IScriptPosition)s_extentCloneWithNewOffset.Invoke(
74+
scriptAst.Extent.StartScriptPosition,
75+
new object[] { fileOffset });
76+
77+
logger.LogTrace(
78+
string.Format(
79+
"Getting completions at offset {0} (line: {1}, column: {2})",
80+
fileOffset,
81+
cursorPosition.LineNumber,
82+
cursorPosition.ColumnNumber));
83+
84+
if (!powerShellContext.IsAvailable)
85+
{
86+
return null;
87+
}
88+
89+
var stopwatch = new Stopwatch();
90+
91+
// If the current runspace is out of process we can use
92+
// CommandCompletion.CompleteInput because PSReadLine won't be taking up the
93+
// main runspace.
94+
if (powerShellContext.IsCurrentRunspaceOutOfProcess())
95+
{
96+
using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandleAsync(cancellationToken))
97+
using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create())
98+
{
99+
powerShell.Runspace = runspaceHandle.Runspace;
100+
stopwatch.Start();
101+
try
102+
{
103+
return CommandCompletion.CompleteInput(
104+
scriptAst,
105+
currentTokens,
106+
cursorPosition,
107+
options: null,
108+
powershell: powerShell);
109+
}
110+
finally
111+
{
112+
stopwatch.Stop();
113+
logger.LogTrace($"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms.");
114+
}
115+
}
116+
}
117+
118+
CommandCompletion commandCompletion = null;
119+
await powerShellContext.InvokeOnPipelineThreadAsync(
120+
pwsh =>
121+
{
122+
stopwatch.Start();
123+
commandCompletion = CommandCompletion.CompleteInput(
124+
scriptAst,
125+
currentTokens,
126+
cursorPosition,
127+
options: null,
128+
powershell: pwsh);
129+
});
130+
stopwatch.Stop();
131+
logger.LogTrace($"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms.");
132+
133+
return commandCompletion;
134+
}
135+
finally
136+
{
137+
s_completionHandle.Release();
138+
}
139+
}
134140

135141
/// <summary>
136142
/// Finds the symbol at a given file location

0 commit comments

Comments
 (0)