Skip to content

Commit a6af037

Browse files
authored
Merge pull request PowerShell#17 from TylerLeonhardt/working-textdocumentsyncer
2 parents b022751 + 08735a2 commit a6af037

File tree

11 files changed

+336
-16
lines changed

11 files changed

+336
-16
lines changed

src/PowerShellEditorServices.Engine/LanguageServer/OmnisharpLanguageServer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ public async Task StartAsync()
6969
options.MinimumLogLevel = _configuration.MinimumLogLevel;
7070
options.Services = _configuration.Services;
7171
options.WithHandler<WorkspaceSymbolsHandler>();
72+
options.WithHandler<TextDocumentHandler>();
73+
options.WithHandler<GetVersionHandler>();
7274
});
7375

7476
_serverStart.SetResult(true);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Microsoft.Extensions.Logging;
5+
using Microsoft.PowerShell.EditorServices;
6+
7+
namespace PowerShellEditorServices.Engine.Services.Workspace.Handlers
8+
{
9+
public class GetVersionHandler : IGetVersionHandler
10+
{
11+
private readonly ILogger<GetVersionHandler> _logger;
12+
13+
public GetVersionHandler(ILoggerFactory factory)
14+
{
15+
_logger = factory.CreateLogger<GetVersionHandler>();
16+
}
17+
18+
public Task<PowerShellVersionDetails> Handle(GetVersionParams request, CancellationToken cancellationToken)
19+
{
20+
var architecture = PowerShellProcessArchitecture.Unknown;
21+
// This should be changed to using a .NET call sometime in the future... but it's just for logging purposes.
22+
string arch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
23+
if (arch != null)
24+
{
25+
if (string.Equals(arch, "AMD64", StringComparison.CurrentCultureIgnoreCase))
26+
{
27+
architecture = PowerShellProcessArchitecture.X64;
28+
}
29+
else if (string.Equals(arch, "x86", StringComparison.CurrentCultureIgnoreCase))
30+
{
31+
architecture = PowerShellProcessArchitecture.X86;
32+
}
33+
}
34+
35+
return Task.FromResult(new PowerShellVersionDetails {
36+
Version = VersionUtils.PSVersion.ToString(),
37+
Edition = VersionUtils.PSEdition,
38+
DisplayVersion = VersionUtils.PSVersion.ToString(2),
39+
Architecture = architecture.ToString()
40+
});
41+
}
42+
43+
private enum PowerShellProcessArchitecture
44+
{
45+
Unknown,
46+
X86,
47+
X64
48+
}
49+
}
50+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using OmniSharp.Extensions.Embedded.MediatR;
2+
using OmniSharp.Extensions.JsonRpc;
3+
4+
namespace PowerShellEditorServices.Engine.Services.Workspace.Handlers
5+
{
6+
[Serial, Method("powerShell/getVersion")]
7+
public interface IGetVersionHandler : IJsonRpcRequestHandler<GetVersionParams, PowerShellVersionDetails> { }
8+
9+
public class GetVersionParams : IRequest<PowerShellVersionDetails> { }
10+
11+
public class PowerShellVersionDetails {
12+
public string Version { get; set; }
13+
public string DisplayVersion { get; set; }
14+
public string Edition { get; set; }
15+
public string Architecture { get; set; }
16+
}
17+
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ public override AstVisitAction VisitHashtable(HashtableAst hashtableAst)
120120

121121
foreach (var kvp in hashtableAst.KeyValuePairs)
122122
{
123-
var keyStrConstExprAst = kvp.Item1 as StringConstantExpressionAst;
124-
if (keyStrConstExprAst != null)
123+
if (kvp.Item1 is StringConstantExpressionAst keyStrConstExprAst)
125124
{
126125
IScriptExtent nameExtent = new ScriptExtent()
127126
{
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Microsoft.Extensions.Logging;
6+
using Microsoft.PowerShell.EditorServices;
7+
using OmniSharp.Extensions.Embedded.MediatR;
8+
using OmniSharp.Extensions.LanguageServer.Protocol;
9+
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
10+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
11+
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
12+
using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities;
13+
14+
namespace PowerShellEditorServices.Engine.Services.Workspace.Handlers
15+
{
16+
class TextDocumentHandler : ITextDocumentSyncHandler
17+
{
18+
19+
private readonly ILogger _logger;
20+
private readonly WorkspaceService _workspaceService;
21+
22+
private readonly DocumentSelector _documentSelector = new DocumentSelector(
23+
new DocumentFilter()
24+
{
25+
Pattern = "**/*.ps*1"
26+
}
27+
);
28+
29+
private SynchronizationCapability _capability;
30+
31+
public TextDocumentSyncKind Change => TextDocumentSyncKind.Incremental;
32+
33+
public TextDocumentHandler(ILoggerFactory factory, WorkspaceService workspaceService)
34+
{
35+
_logger = factory.CreateLogger<TextDocumentHandler>();
36+
_workspaceService = workspaceService;
37+
}
38+
39+
public Task<Unit> Handle(DidChangeTextDocumentParams notification, CancellationToken token)
40+
{
41+
List<ScriptFile> changedFiles = new List<ScriptFile>();
42+
43+
// A text change notification can batch multiple change requests
44+
foreach (TextDocumentContentChangeEvent textChange in notification.ContentChanges)
45+
{
46+
ScriptFile changedFile = _workspaceService.GetFile(notification.TextDocument.Uri.ToString());
47+
48+
changedFile.ApplyChange(
49+
GetFileChangeDetails(
50+
textChange.Range,
51+
textChange.Text));
52+
53+
changedFiles.Add(changedFile);
54+
}
55+
56+
// TODO: Get all recently edited files in the workspace
57+
// this.RunScriptDiagnosticsAsync(
58+
// changedFiles.ToArray(),
59+
// editorSession,
60+
// eventContext);
61+
return Unit.Task;
62+
}
63+
64+
TextDocumentChangeRegistrationOptions IRegistration<TextDocumentChangeRegistrationOptions>.GetRegistrationOptions()
65+
{
66+
return new TextDocumentChangeRegistrationOptions()
67+
{
68+
DocumentSelector = _documentSelector,
69+
SyncKind = Change
70+
};
71+
}
72+
73+
public void SetCapability(SynchronizationCapability capability)
74+
{
75+
_capability = capability;
76+
}
77+
78+
public Task<Unit> Handle(DidOpenTextDocumentParams notification, CancellationToken token)
79+
{
80+
ScriptFile openedFile =
81+
_workspaceService.GetFileBuffer(
82+
notification.TextDocument.Uri.ToString(),
83+
notification.TextDocument.Text);
84+
85+
// TODO: Get all recently edited files in the workspace
86+
// this.RunScriptDiagnosticsAsync(
87+
// new ScriptFile[] { openedFile },
88+
// editorSession,
89+
// eventContext);
90+
91+
_logger.LogTrace("Finished opening document.");
92+
return Unit.Task;
93+
}
94+
95+
TextDocumentRegistrationOptions IRegistration<TextDocumentRegistrationOptions>.GetRegistrationOptions()
96+
{
97+
return new TextDocumentRegistrationOptions()
98+
{
99+
DocumentSelector = _documentSelector,
100+
};
101+
}
102+
103+
public Task<Unit> Handle(DidCloseTextDocumentParams notification, CancellationToken token)
104+
{
105+
// Find and close the file in the current session
106+
var fileToClose = _workspaceService.GetFile(notification.TextDocument.Uri.ToString());
107+
108+
if (fileToClose != null)
109+
{
110+
_workspaceService.CloseFile(fileToClose);
111+
// await ClearMarkersAsync(fileToClose, eventContext);
112+
}
113+
114+
_logger.LogTrace("Finished closing document.");
115+
return Unit.Task;
116+
}
117+
118+
public Task<Unit> Handle(DidSaveTextDocumentParams notification, CancellationToken token)
119+
{
120+
ScriptFile savedFile =
121+
_workspaceService.GetFile(
122+
notification.TextDocument.Uri.ToString());
123+
124+
// if (savedFile != null)
125+
// {
126+
// if (this.editorSession.RemoteFileManager.IsUnderRemoteTempPath(savedFile.FilePath))
127+
// {
128+
// await this.editorSession.RemoteFileManager.SaveRemoteFileAsync(
129+
// savedFile.FilePath);
130+
// }
131+
// }
132+
return Unit.Task;
133+
}
134+
135+
TextDocumentSaveRegistrationOptions IRegistration<TextDocumentSaveRegistrationOptions>.GetRegistrationOptions()
136+
{
137+
return new TextDocumentSaveRegistrationOptions()
138+
{
139+
DocumentSelector = _documentSelector,
140+
IncludeText = true
141+
};
142+
}
143+
public TextDocumentAttributes GetTextDocumentAttributes(Uri uri)
144+
{
145+
return new TextDocumentAttributes(uri, "powershell");
146+
}
147+
148+
private static FileChange GetFileChangeDetails(Range changeRange, string insertString)
149+
{
150+
// The protocol's positions are zero-based so add 1 to all offsets
151+
152+
if (changeRange == null) return new FileChange { InsertString = insertString, IsReload = true };
153+
154+
return new FileChange
155+
{
156+
InsertString = insertString,
157+
Line = (int)(changeRange.Start.Line + 1),
158+
Offset = (int)(changeRange.Start.Character + 1),
159+
EndLine = (int)(changeRange.End.Line + 1),
160+
EndOffset = (int)(changeRange.End.Character + 1),
161+
IsReload = false
162+
};
163+
}
164+
165+
// private async Task ClearMarkersAsync(ScriptFile scriptFile, EventContext eventContext)
166+
// {
167+
// // send empty diagnostic markers to clear any markers associated with the given file
168+
// await PublishScriptDiagnosticsAsync(
169+
// scriptFile,
170+
// new List<ScriptFileMarker>(),
171+
// this.codeActionsPerFile,
172+
// eventContext);
173+
// }
174+
}
175+
}

src/PowerShellEditorServices.Engine/Services/TextDocument/Handlers/TextDocumentNotificationHandler.cs

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/PowerShellEditorServices.Engine/Services/Workspace/Handlers/WorkspaceSymbolsHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class WorkspaceSymbolsHandler : IWorkspaceSymbolsHandler
1818
private readonly ILogger _logger;
1919
private readonly SymbolsService _symbolsService;
2020
private readonly WorkspaceService _workspaceService;
21+
private WorkspaceSymbolCapability _capability;
2122

2223
public WorkspaceSymbolsHandler(ILoggerFactory loggerFactory, SymbolsService symbols, WorkspaceService workspace) {
2324
_logger = loggerFactory.CreateLogger<WorkspaceSymbolsHandler>();
@@ -73,7 +74,7 @@ public Task<SymbolInformationContainer> Handle(WorkspaceSymbolParams request, Ca
7374

7475
public void SetCapability(WorkspaceSymbolCapability capability)
7576
{
76-
// throw new NotImplementedException();
77+
_capability = capability;
7778
}
7879

7980
#region private Methods

src/PowerShellEditorServices.Engine/Utility/VersionUtils.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ internal static class VersionUtils
1919
/// </summary>
2020
public static Version PSVersion { get; } = PowerShellReflectionUtils.PSVersion;
2121

22+
/// <summary>
23+
/// Get's the Edition of PowerShell being used.
24+
/// </summary>
25+
public static string PSEdition { get; } = PowerShellReflectionUtils.PSEdition;
26+
2227
/// <summary>
2328
/// True if we are running in Windows PowerShell, false otherwise.
2429
/// </summary>
@@ -38,13 +43,21 @@ internal static class VersionUtils
3843
internal static class PowerShellReflectionUtils
3944
{
4045

41-
private static readonly Assembly _psRuntimeAssembly = typeof(System.Management.Automation.Runspaces.Runspace).Assembly;
42-
private static readonly PropertyInfo _psVersionProperty = _psRuntimeAssembly.GetType("System.Management.Automation.PSVersionInfo")
46+
private static readonly Type s_psVersionInfoType = typeof(System.Management.Automation.Runspaces.Runspace).Assembly.GetType("System.Management.Automation.PSVersionInfo");
47+
private static readonly PropertyInfo s_psVersionProperty = s_psVersionInfoType
4348
.GetProperty("PSVersion", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
49+
private static readonly PropertyInfo s_psEditionProperty = s_psVersionInfoType
50+
.GetProperty("PSEdition", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
4451

4552
/// <summary>
46-
/// Get's the Version of PowerShell being used.
53+
/// Get's the Version of PowerShell being used. Note: this will get rid of the SemVer 2.0 suffix because apparently
54+
/// that property is added as a note property and it is not there when we reflect.
55+
/// </summary>
56+
public static Version PSVersion { get; } = s_psVersionProperty.GetValue(null) as Version;
57+
58+
/// <summary>
59+
/// Get's the Edition of PowerShell being used.
4760
/// </summary>
48-
public static Version PSVersion { get; } = _psVersionProperty.GetValue(null) as Version;
61+
public static string PSEdition { get; } = s_psEditionProperty.GetValue(null) as string;
4962
}
5063
}

test/Pester/EditorServices.Integration.Tests.ps1

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,40 @@ Describe "Loading and running PowerShellEditorServices" {
7878
#ReportLogErrors -LogPath $psesServer.LogPath -FromIndex ([ref]$logIdx)
7979
}
8080

81+
It "Can handle powerShell/getVersion request" {
82+
$request = Send-LspRequest -Client $client -Method "powerShell/getVersion"
83+
$response = Get-LspResponse -Client $client -Id $request.Id
84+
if ($IsCoreCLR) {
85+
$response.Result.edition | Should -Be "Core"
86+
} else {
87+
$response.Result.edition | Should -Be "Desktop"
88+
}
89+
}
90+
8191
It "Can handle WorkspaceSymbol request" {
92+
$script = "
93+
function Get-Foo {
94+
Write-Host 'hello'
95+
}
96+
"
97+
98+
$file = Set-Content -Path TestDrive:\foo.ps1 -Value $script -PassThru
99+
$response = Send-LspDidOpenTextDocumentRequest -Client $client `
100+
-Uri ([Uri]::new($file.PSPath).AbsoluteUri) `
101+
-Text ($file[0].ToString())
102+
103+
# There's no response for this message, but we need to call Get-LspResponse
104+
# to increment the counter.
105+
Get-LspResponse -Client $client -Id $response.Id | Out-Null
106+
82107
$request = Send-LspRequest -Client $client -Method "workspace/symbol" -Parameters @{
83108
query = ""
84109
}
85-
$response = Get-LspResponse -Client $client -Id $request.Id #-WaitMillis 99999
110+
$response = Get-LspResponse -Client $client -Id $request.Id -WaitMillis 99999
86111
$response.Id | Should -BeExactly $request.Id
112+
113+
$response.Result.Count | Should -Be 1
114+
$response.Result.name | Should -BeLike "Get-Foo*"
87115
CheckErrorResponse -Response $response
88116

89117
# ReportLogErrors -LogPath $psesServer.LogPath -FromIndex ([ref]$logIdx)

tools/PsesPsClient/PsesPsClient.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ FunctionsToExport = @(
7474
'Connect-PsesServer',
7575
'Send-LspRequest',
7676
'Send-LspInitializeRequest',
77+
'Send-LspDidOpenTextDocumentRequest',
7778
'Send-LspShutdownRequest',
7879
'Get-LspResponse'
7980
)

0 commit comments

Comments
 (0)