9
9
using Microsoft . PowerShell . EditorServices . Protocol . MessageProtocol . Channel ;
10
10
using Microsoft . PowerShell . EditorServices . Session ;
11
11
using Microsoft . PowerShell . EditorServices . Utility ;
12
+ using Newtonsoft . Json . Linq ;
12
13
using System ;
13
14
using System . Collections . Generic ;
14
15
using System . IO ;
@@ -30,6 +31,8 @@ public class LanguageServer : LanguageServerBase
30
31
private OutputDebouncer outputDebouncer ;
31
32
private LanguageServerEditorOperations editorOperations ;
32
33
private LanguageServerSettings currentSettings = new LanguageServerSettings ( ) ;
34
+ private Dictionary < string , Dictionary < string , MarkerCorrection > > codeActionsPerFile =
35
+ new Dictionary < string , Dictionary < string , MarkerCorrection > > ( ) ;
33
36
34
37
/// <param name="hostDetails">
35
38
/// Provides details about the host application.
@@ -92,6 +95,7 @@ protected override void Initialize()
92
95
this . SetRequestHandler ( HoverRequest . Type , this . HandleHoverRequest ) ;
93
96
this . SetRequestHandler ( DocumentSymbolRequest . Type , this . HandleDocumentSymbolRequest ) ;
94
97
this . SetRequestHandler ( WorkspaceSymbolRequest . Type , this . HandleWorkspaceSymbolRequest ) ;
98
+ this . SetRequestHandler ( CodeActionRequest . Type , this . HandleCodeActionRequest ) ;
95
99
96
100
this . SetRequestHandler ( ShowOnlineHelpRequest . Type , this . HandleShowOnlineHelpRequest ) ;
97
101
this . SetRequestHandler ( ExpandAliasRequest . Type , this . HandleExpandAliasRequest ) ;
@@ -146,6 +150,7 @@ await requestContext.SendResult(
146
150
DocumentSymbolProvider = true ,
147
151
WorkspaceSymbolProvider = true ,
148
152
HoverProvider = true ,
153
+ CodeActionProvider = true ,
149
154
CompletionProvider = new CompletionOptions
150
155
{
151
156
ResolveProvider = true ,
@@ -226,17 +231,17 @@ function __Expand-Alias {
226
231
param($targetScript)
227
232
228
233
[ref]$errors=$null
229
-
230
- $tokens = [System.Management.Automation.PsParser]::Tokenize($targetScript, $errors).Where({$_.type -eq 'command'}) |
234
+
235
+ $tokens = [System.Management.Automation.PsParser]::Tokenize($targetScript, $errors).Where({$_.type -eq 'command'}) |
231
236
Sort Start -Descending
232
237
233
238
foreach ($token in $tokens) {
234
239
$definition=(Get-Command ('`'+$token.Content) -CommandType Alias -ErrorAction SilentlyContinue).Definition
235
240
236
- if($definition) {
241
+ if($definition) {
237
242
$lhs=$targetScript.Substring(0, $token.Start)
238
243
$rhs=$targetScript.Substring($token.Start + $token.Length)
239
-
244
+
240
245
$targetScript=$lhs + $definition + $rhs
241
246
}
242
247
}
@@ -346,13 +351,13 @@ protected async Task HandleDidChangeConfigurationNotification(
346
351
EventContext eventContext )
347
352
{
348
353
bool oldLoadProfiles = this . currentSettings . EnableProfileLoading ;
349
- bool oldScriptAnalysisEnabled =
354
+ bool oldScriptAnalysisEnabled =
350
355
this . currentSettings . ScriptAnalysis . Enable . HasValue ;
351
356
string oldScriptAnalysisSettingsPath =
352
357
this . currentSettings . ScriptAnalysis . SettingsPath ;
353
358
354
359
this . currentSettings . Update (
355
- configChangeParams . Settings . Powershell ,
360
+ configChangeParams . Settings . Powershell ,
356
361
this . editorSession . Workspace . WorkspacePath ) ;
357
362
358
363
if ( ! this . profilesLoaded &&
@@ -386,6 +391,7 @@ protected async Task HandleDidChangeConfigurationNotification(
386
391
await PublishScriptDiagnostics (
387
392
scriptFile ,
388
393
emptyAnalysisDiagnostics ,
394
+ this . codeActionsPerFile ,
389
395
eventContext ) ;
390
396
}
391
397
}
@@ -815,6 +821,35 @@ private bool IsQueryMatch(string query, string symbolName)
815
821
return symbolName . IndexOf ( query , StringComparison . OrdinalIgnoreCase ) >= 0 ;
816
822
}
817
823
824
+ protected async Task HandleCodeActionRequest (
825
+ CodeActionRequest codeActionParams ,
826
+ RequestContext < CodeActionCommand [ ] > requestContext )
827
+ {
828
+ MarkerCorrection correction = null ;
829
+ Dictionary < string , MarkerCorrection > markerIndex = null ;
830
+ List < CodeActionCommand > codeActionCommands = new List < CodeActionCommand > ( ) ;
831
+
832
+ if ( this . codeActionsPerFile . TryGetValue ( codeActionParams . TextDocument . Uri , out markerIndex ) )
833
+ {
834
+ foreach ( var diagnostic in codeActionParams . Context . Diagnostics )
835
+ {
836
+ if ( markerIndex . TryGetValue ( diagnostic . Code , out correction ) )
837
+ {
838
+ codeActionCommands . Add (
839
+ new CodeActionCommand
840
+ {
841
+ Title = correction . Name ,
842
+ Command = "PowerShell.ApplyCodeActionEdits" ,
843
+ Arguments = JArray . FromObject ( correction . Edits )
844
+ } ) ;
845
+ }
846
+ }
847
+ }
848
+
849
+ await requestContext . SendResult (
850
+ codeActionCommands . ToArray ( ) ) ;
851
+ }
852
+
818
853
protected Task HandleEvaluateRequest (
819
854
DebugAdapterMessages . EvaluateRequestArguments evaluateParams ,
820
855
RequestContext < DebugAdapterMessages . EvaluateResponseBody > requestContext )
@@ -974,6 +1009,7 @@ private Task RunScriptDiagnostics(
974
1009
DelayThenInvokeDiagnostics (
975
1010
750 ,
976
1011
filesToAnalyze ,
1012
+ this . codeActionsPerFile ,
977
1013
editorSession ,
978
1014
eventContext ,
979
1015
existingRequestCancellation . Token ) ,
@@ -987,6 +1023,7 @@ private Task RunScriptDiagnostics(
987
1023
private static async Task DelayThenInvokeDiagnostics (
988
1024
int delayMilliseconds ,
989
1025
ScriptFile [ ] filesToAnalyze ,
1026
+ Dictionary < string , Dictionary < string , MarkerCorrection > > correctionIndex ,
990
1027
EditorSession editorSession ,
991
1028
EventContext eventContext ,
992
1029
CancellationToken cancellationToken )
@@ -1034,28 +1071,45 @@ private static async Task DelayThenInvokeDiagnostics(
1034
1071
await PublishScriptDiagnostics (
1035
1072
scriptFile ,
1036
1073
semanticMarkers ,
1074
+ correctionIndex ,
1037
1075
eventContext ) ;
1038
1076
}
1039
1077
}
1040
1078
1041
1079
private static async Task PublishScriptDiagnostics (
1042
1080
ScriptFile scriptFile ,
1043
1081
ScriptFileMarker [ ] semanticMarkers ,
1082
+ Dictionary < string , Dictionary < string , MarkerCorrection > > correctionIndex ,
1044
1083
EventContext eventContext )
1045
1084
{
1046
- var allMarkers = scriptFile . SyntaxMarkers . Concat ( semanticMarkers ) ;
1085
+ List < Diagnostic > diagnostics = new List < Diagnostic > ( ) ;
1086
+
1087
+ // Hold on to any corrections that may need to be applied later
1088
+ Dictionary < string , MarkerCorrection > fileCorrections =
1089
+ new Dictionary < string , MarkerCorrection > ( ) ;
1090
+
1091
+ foreach ( var marker in scriptFile . SyntaxMarkers . Concat ( semanticMarkers ) )
1092
+ {
1093
+ // Does the marker contain a correction?
1094
+ Diagnostic markerDiagnostic = GetDiagnosticFromMarker ( marker ) ;
1095
+ if ( marker . Correction != null )
1096
+ {
1097
+ fileCorrections . Add ( markerDiagnostic . Code , marker . Correction ) ;
1098
+ }
1099
+
1100
+ diagnostics . Add ( markerDiagnostic ) ;
1101
+ }
1102
+
1103
+ correctionIndex [ scriptFile . ClientFilePath ] = fileCorrections ;
1047
1104
1048
- // Always send syntax and semantic errors. We want to
1105
+ // Always send syntax and semantic errors. We want to
1049
1106
// make sure no out-of-date markers are being displayed.
1050
1107
await eventContext . SendEvent (
1051
1108
PublishDiagnosticsNotification . Type ,
1052
1109
new PublishDiagnosticsNotification
1053
1110
{
1054
1111
Uri = scriptFile . ClientFilePath ,
1055
- Diagnostics =
1056
- allMarkers
1057
- . Select ( GetDiagnosticFromMarker )
1058
- . ToArray ( )
1112
+ Diagnostics = diagnostics . ToArray ( )
1059
1113
} ) ;
1060
1114
}
1061
1115
@@ -1065,6 +1119,7 @@ private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMar
1065
1119
{
1066
1120
Severity = MapDiagnosticSeverity ( scriptFileMarker . Level ) ,
1067
1121
Message = scriptFileMarker . Message ,
1122
+ Code = Guid . NewGuid ( ) . ToString ( ) ,
1068
1123
Range = new Range
1069
1124
{
1070
1125
// TODO: What offsets should I use?
@@ -1145,7 +1200,7 @@ private static CompletionItem CreateCompletionItem(
1145
1200
if ( ! completionDetails . ListItemText . Equals (
1146
1201
completionDetails . ToolTipText ,
1147
1202
StringComparison . OrdinalIgnoreCase ) &&
1148
- ! Regex . IsMatch ( completionDetails . ToolTipText ,
1203
+ ! Regex . IsMatch ( completionDetails . ToolTipText ,
1149
1204
@"^\s*" + escapedToolTipText + @"\s+\[" ) )
1150
1205
{
1151
1206
detailString = completionDetails . ToolTipText ;
@@ -1158,7 +1213,7 @@ private static CompletionItem CreateCompletionItem(
1158
1213
// default (with common params at the end). We just need to make sure the default
1159
1214
// order also be the lexicographical order which we do by prefixig the ListItemText
1160
1215
// with a leading 0's four digit index. This would not sort correctly for a list
1161
- // > 999 parameters but surely we won't have so many items in the "parameter name"
1216
+ // > 999 parameters but surely we won't have so many items in the "parameter name"
1162
1217
// completion list. Technically we don't need the ListItemText at all but it may come
1163
1218
// in handy during debug.
1164
1219
var sortText = ( completionDetails . CompletionType == CompletionType . ParameterName )
0 commit comments