Skip to content

Commit 7af07bd

Browse files
author
Kapil Borle
committed
Handle PSSA settings hashtable input
This enables the client to send settings as a hashtable instead of pointing to a settings file for script analysis. Our current code structure allows us to pass either a set of rules or a settings file path but not both. However, to provide code formatting settings we not only need to send a settings file to configure the given rules but also the rules in a particular order. The best approach in this case is to update and send a settings hashtable everytime we request code formatting markers.
1 parent a5fa1ad commit 7af07bd

File tree

4 files changed

+82
-26
lines changed

4 files changed

+82
-26
lines changed

src/PowerShellEditorServices.Protocol/LanguageServer/ScriptFileMarkersRequest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class ScriptFileMarkerRequestParams
1818
{
1919
public string filePath;
2020
public string[] rules;
21+
public string settings;
2122
}
2223

2324
class ScriptFileMarkerRequestResultParams

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.PowerShell.EditorServices.Utility;
1313
using Newtonsoft.Json.Linq;
1414
using System;
15+
using System.Collections;
1516
using System.Collections.Generic;
1617
using System.IO;
1718
using System.Linq;
@@ -239,7 +240,7 @@ private async Task HandleScriptFileMarkersRequest(
239240
var markers = editorSession.AnalysisService.GetSemanticMarkers(
240241
editorSession.Workspace.GetFile(requestParams.filePath),
241242
requestParams.rules,
242-
null);
243+
editorSession.LanguageService.GetHashtableFromString(requestParams.settings));
243244
await requestContext.SendResult(new ScriptFileMarkerRequestResultParams {
244245
markers = markers
245246
});

src/PowerShellEditorServices/Analysis/AnalysisService.cs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.Management.Automation;
1414
using System.Collections.Generic;
1515
using System.Text;
16+
using System.Collections;
1617

1718
namespace Microsoft.PowerShell.EditorServices
1819
{
@@ -127,9 +128,36 @@ public AnalysisService(IConsoleHost consoleHost, string settingsPath = null)
127128

128129
#region Public Methods
129130

131+
/// <summary>
132+
/// Performs semantic analysis on the given ScriptFile and returns
133+
/// an array of ScriptFileMarkers.
134+
/// </summary>
135+
/// <param name="file">The ScriptFile which will be analyzed for semantic markers.</param>
136+
/// <returns>An array of ScriptFileMarkers containing semantic analysis results.</returns>
137+
public ScriptFileMarker[] GetSemanticMarkers(ScriptFile file)
138+
{
139+
return GetSemanticMarkers(file, activeRules, settingsPath);
140+
}
141+
130142
public ScriptFileMarker[] GetSemanticMarkers(ScriptFile file, string[] rules, string settingsPath)
131143
{
132-
if (this.scriptAnalyzerModuleInfo != null && file.IsAnalysisEnabled)
144+
return GetSemanticMarkers<string>(file, rules, settingsPath);
145+
}
146+
147+
public ScriptFileMarker[] GetSemanticMarkers(ScriptFile file, string[] rules, Hashtable settings)
148+
{
149+
return GetSemanticMarkers<Hashtable>(file, rules, settings);
150+
}
151+
152+
private ScriptFileMarker[] GetSemanticMarkers<TSettings>(
153+
ScriptFile file,
154+
string[] rules,
155+
TSettings settings) where TSettings: class
156+
{
157+
if (this.scriptAnalyzerModuleInfo != null
158+
&& file.IsAnalysisEnabled
159+
&& (typeof(TSettings) == typeof(string)
160+
|| typeof(TSettings) == typeof(Hashtable)))
133161
{
134162
// TODO: This is a temporary fix until we can change how
135163
// ScriptAnalyzer invokes their async tasks.
@@ -139,7 +167,7 @@ public ScriptFileMarker[] GetSemanticMarkers(ScriptFile file, string[] rules, st
139167
() =>
140168
{
141169
return
142-
GetDiagnosticRecords(file, rules, settingsPath)
170+
GetDiagnosticRecords(file, rules, settings)
143171
.Select(ScriptFileMarker.FromDiagnosticRecord)
144172
.ToArray();
145173
},
@@ -156,17 +184,6 @@ public ScriptFileMarker[] GetSemanticMarkers(ScriptFile file, string[] rules, st
156184
}
157185
}
158186

159-
/// <summary>
160-
/// Performs semantic analysis on the given ScriptFile and returns
161-
/// an array of ScriptFileMarkers.
162-
/// </summary>
163-
/// <param name="file">The ScriptFile which will be analyzed for semantic markers.</param>
164-
/// <returns>An array of ScriptFileMarkers containing semantic analysis results.</returns>
165-
public ScriptFileMarker[] GetSemanticMarkers(ScriptFile file)
166-
{
167-
return GetSemanticMarkers(file, activeRules, settingsPath);
168-
}
169-
170187
/// <summary>
171188
/// Returns a list of builtin-in PSScriptAnalyzer rules
172189
/// </summary>
@@ -300,11 +317,18 @@ private IEnumerable<PSObject> GetDiagnosticRecords(ScriptFile file)
300317
return GetDiagnosticRecords(file, this.activeRules, this.settingsPath);
301318
}
302319

303-
private IEnumerable<PSObject> GetDiagnosticRecords(ScriptFile file, string[] rules, string settingsPath)
320+
// TSettings can either be of type Hashtable or string
321+
// as scriptanalyzer settings parameter takes either a hashtable or string
322+
private IEnumerable<PSObject> GetDiagnosticRecords<TSettings>(
323+
ScriptFile file,
324+
string[] rules,
325+
TSettings settings) where TSettings: class
304326
{
305327
IEnumerable<PSObject> diagnosticRecords = Enumerable.Empty<PSObject>();
306328

307-
if (this.scriptAnalyzerModuleInfo != null)
329+
if (this.scriptAnalyzerModuleInfo != null
330+
&& (typeof(TSettings) == typeof(string)
331+
|| typeof(TSettings) == typeof(Hashtable)))
308332
{
309333
lock (runspaceLock)
310334
{
@@ -320,9 +344,9 @@ private IEnumerable<PSObject> GetDiagnosticRecords(ScriptFile file, string[] rul
320344
.AddParameter("ScriptDefinition", file.Contents);
321345

322346
// Use a settings file if one is provided, otherwise use the default rule list.
323-
if (!string.IsNullOrWhiteSpace(settingsPath))
347+
if (settings != null)
324348
{
325-
powerShell.AddParameter("Settings", settingsPath);
349+
powerShell.AddParameter("Settings", settings);
326350
}
327351
else
328352
{

src/PowerShellEditorServices/Language/LanguageService.cs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
using Microsoft.PowerShell.EditorServices.Utility;
77
using System;
8+
using System.Collections;
89
using System.Collections.Generic;
910
using System.Linq;
1011
using System.Management.Automation;
12+
using System.Management.Automation.Language;
1113
using System.Threading;
1214
using System.Threading.Tasks;
1315

@@ -132,7 +134,7 @@ await this.powerShellContext.GetRunspaceHandle(
132134
}
133135

134136
/// <summary>
135-
/// Finds command completion details for the script given a file location
137+
/// Finds command completion details for the script given a file location
136138
/// </summary>
137139
/// <param name="file">The details and contents of a open script file</param>
138140
/// <param name="entryName">The name of the suggestion that needs details</param>
@@ -144,7 +146,7 @@ public CompletionDetails GetCompletionDetailsInFile(
144146
// Makes sure the most recent completions request was the same line and column as this request
145147
if (file.Id.Equals(mostRecentRequestFile))
146148
{
147-
CompletionDetails completionResult =
149+
CompletionDetails completionResult =
148150
mostRecentCompletions.Completions.FirstOrDefault(
149151
result => result.CompletionText.Equals(entryName));
150152

@@ -163,7 +165,7 @@ public CompletionDetails GetCompletionDetailsInFile(
163165
/// <param name="lineNumber">The line number of the cursor for the given script</param>
164166
/// <param name="columnNumber">The coulumn number of the cursor for the given script</param>
165167
/// <returns>A SymbolReference of the symbol found at the given location
166-
/// or null if there is no symbol at that location
168+
/// or null if there is no symbol at that location
167169
/// </returns>
168170
public SymbolReference FindSymbolAtLocation(
169171
ScriptFile scriptFile,
@@ -255,7 +257,7 @@ public FindOccurrencesResult FindSymbolsInFile(ScriptFile scriptFile)
255257
public async Task<FindReferencesResult> FindReferencesOfSymbol(
256258
SymbolReference foundSymbol,
257259
ScriptFile[] referencedFiles)
258-
{
260+
{
259261
if (foundSymbol != null)
260262
{
261263
int symbolOffset = referencedFiles[0].GetOffsetAtPosition(
@@ -336,7 +338,7 @@ public async Task<GetDefinitionResult> GetDefinitionOfSymbol(
336338
}
337339
}
338340

339-
// if definition is not found in referenced files
341+
// if definition is not found in referenced files
340342
// look for it in the builtin commands
341343
if (foundDefinition == null)
342344
{
@@ -345,9 +347,9 @@ await CommandHelpers.GetCommandInfo(
345347
foundSymbol.SymbolName,
346348
this.powerShellContext);
347349

348-
foundDefinition =
350+
foundDefinition =
349351
FindDeclarationForBuiltinCommand(
350-
cmdInfo,
352+
cmdInfo,
351353
foundSymbol,
352354
workspace);
353355
}
@@ -448,6 +450,34 @@ await CommandHelpers.GetCommandInfo(
448450
}
449451
}
450452

453+
public Hashtable GetHashtableFromString(string hashtableString)
454+
{
455+
if (hashtableString == null)
456+
{
457+
return null;
458+
}
459+
460+
ParseError[] parseErrors = null;
461+
Token[] tokens = null;
462+
try
463+
{
464+
var ast = Parser.ParseInput(hashtableString, out tokens, out parseErrors);
465+
if (parseErrors.Length > 0)
466+
{
467+
return null;
468+
}
469+
470+
var hashtableAst = ast.Find(x => x is HashtableAst, false);
471+
return hashtableAst == null
472+
? null
473+
: (hashtableAst as HashtableAst).SafeGetValue() as Hashtable;
474+
}
475+
catch
476+
{
477+
return null;
478+
}
479+
}
480+
451481
#endregion
452482

453483
#region Private Fields
@@ -533,7 +563,7 @@ private ScriptFile[] GetBuiltinCommandScriptFiles(
533563
}
534564

535565
private SymbolReference FindDeclarationForBuiltinCommand(
536-
CommandInfo commandInfo,
566+
CommandInfo commandInfo,
537567
SymbolReference foundSymbol,
538568
Workspace workspace)
539569
{

0 commit comments

Comments
 (0)