-
Notifications
You must be signed in to change notification settings - Fork 227
/
Copy pathLanguageServerSettings.cs
392 lines (349 loc) · 14.6 KB
/
LanguageServerSettings.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
//
// 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.Utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Security;
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
{
public class LanguageServerSettings
{
public bool EnableProfileLoading { get; set; }
public ScriptAnalysisSettings ScriptAnalysis { get; set; }
public CodeFormattingSettings CodeFormatting { get; set; }
public CodeFoldingSettings CodeFolding { get; set; }
public LanguageServerSettings()
{
this.ScriptAnalysis = new ScriptAnalysisSettings();
this.CodeFormatting = new CodeFormattingSettings();
this.CodeFolding = new CodeFoldingSettings();
}
public void Update(
LanguageServerSettings settings,
string workspaceRootPath,
ILogger logger)
{
if (settings != null)
{
this.EnableProfileLoading = settings.EnableProfileLoading;
this.ScriptAnalysis.Update(
settings.ScriptAnalysis,
workspaceRootPath,
logger);
this.CodeFormatting = new CodeFormattingSettings(settings.CodeFormatting);
this.CodeFolding.Update(settings.CodeFolding, logger);
}
}
}
public class ScriptAnalysisSettings
{
public bool? Enable { get; set; }
public string SettingsPath { get; set; }
public ScriptAnalysisSettings()
{
this.Enable = true;
}
public void Update(
ScriptAnalysisSettings settings,
string workspaceRootPath,
ILogger logger)
{
if (settings != null)
{
this.Enable = settings.Enable;
string settingsPath = settings.SettingsPath;
try
{
if (string.IsNullOrWhiteSpace(settingsPath))
{
settingsPath = null;
}
else if (!Path.IsPathRooted(settingsPath))
{
if (string.IsNullOrEmpty(workspaceRootPath))
{
// The workspace root path could be an empty string
// when the user has opened a PowerShell script file
// without opening an entire folder (workspace) first.
// In this case we should just log an error and let
// the specified settings path go through even though
// it will fail to load.
logger.Write(
LogLevel.Error,
"Could not resolve Script Analyzer settings path due to null or empty workspaceRootPath.");
}
else
{
settingsPath = Path.GetFullPath(Path.Combine(workspaceRootPath, settingsPath));
}
}
this.SettingsPath = settingsPath;
logger.Write(LogLevel.Verbose, $"Using Script Analyzer settings path - '{settingsPath ?? ""}'.");
}
catch (Exception ex) when (
ex is NotSupportedException ||
ex is PathTooLongException ||
ex is SecurityException)
{
// Invalid chars in path like ${env:HOME} can cause Path.GetFullPath() to throw, catch such errors here
logger.WriteException(
$"Invalid Script Analyzer settings path - '{settingsPath}'.",
ex);
this.SettingsPath = null;
}
}
}
}
/// <summary>
/// Code formatting presets.
/// See https://en.wikipedia.org/wiki/Indent_style for details on indent and brace styles.
/// </summary>
public enum CodeFormattingPreset
{
/// <summary>
/// Use the formatting settings as-is.
/// </summary>
Custom,
/// <summary>
/// Configure the formatting settings to resemble the Allman indent/brace style.
/// </summary>
Allman,
/// <summary>
/// Configure the formatting settings to resemble the one true brace style variant of K&R indent/brace style.
/// </summary>
OTBS,
/// <summary>
/// Configure the formatting settings to resemble the Stroustrup brace style variant of K&R indent/brace style.
/// </summary>
Stroustrup
}
/// <summary>
/// Multi-line pipeline style settings.
/// </summary>
public enum PipelineIndentationStyle
{
/// <summary>
/// After the indentation level only once after the first pipeline and keep this level for the following pipelines.
/// </summary>
IncreaseIndentationForFirstPipeline,
/// <summary>
/// After every pipeline, keep increasing the indentation.
/// </summary>
IncreaseIndentationAfterEveryPipeline,
/// <summary>
/// Do not increase indentation level at all after pipeline.
/// </summary>
NoIndentation
}
public class CodeFormattingSettings
{
/// <summary>
/// Default constructor.
/// </summary>>
public CodeFormattingSettings()
{
}
/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="codeFormattingSettings">An instance of type CodeFormattingSettings.</param>
public CodeFormattingSettings(CodeFormattingSettings codeFormattingSettings)
{
if (codeFormattingSettings == null)
{
throw new ArgumentNullException(nameof(codeFormattingSettings));
}
foreach (var prop in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
prop.SetValue(this, prop.GetValue(codeFormattingSettings));
}
}
public bool AutoCorrectAliases { get; set; }
public CodeFormattingPreset Preset { get; set; }
public bool OpenBraceOnSameLine { get; set; }
public bool NewLineAfterOpenBrace { get; set; }
public bool NewLineAfterCloseBrace { get; set; }
public PipelineIndentationStyle PipelineIndentationStyle { get; set; }
public bool WhitespaceBeforeOpenBrace { get; set; }
public bool WhitespaceBeforeOpenParen { get; set; }
public bool WhitespaceAroundOperator { get; set; }
public bool WhitespaceAfterSeparator { get; set; }
public bool WhitespaceInsideBrace { get; set; }
public bool WhitespaceAroundPipe { get; set; }
public bool IgnoreOneLineBlock { get; set; }
public bool AlignPropertyValuePairs { get; set; }
public bool UseCorrectCasing { get; set; }
/// <summary>
/// Get the settings hashtable that will be consumed by PSScriptAnalyzer.
/// </summary>
/// <param name="tabSize">The tab size in the number spaces.</param>
/// <param name="insertSpaces">If true, insert spaces otherwise insert tabs for indentation.</param>
/// <returns></returns>
public Hashtable GetPSSASettingsHashtable(
int tabSize,
bool insertSpaces)
{
var settings = GetCustomPSSASettingsHashtable(tabSize, insertSpaces);
var ruleSettings = (Hashtable)(settings["Rules"]);
var closeBraceSettings = (Hashtable)ruleSettings["PSPlaceCloseBrace"];
var openBraceSettings = (Hashtable)ruleSettings["PSPlaceOpenBrace"];
switch(Preset)
{
case CodeFormattingPreset.Allman:
openBraceSettings["OnSameLine"] = false;
openBraceSettings["NewLineAfter"] = true;
closeBraceSettings["NewLineAfter"] = true;
break;
case CodeFormattingPreset.OTBS:
openBraceSettings["OnSameLine"] = true;
openBraceSettings["NewLineAfter"] = true;
closeBraceSettings["NewLineAfter"] = false;
break;
case CodeFormattingPreset.Stroustrup:
openBraceSettings["OnSameLine"] = true;
openBraceSettings["NewLineAfter"] = true;
closeBraceSettings["NewLineAfter"] = true;
break;
default:
break;
}
return settings;
}
private Hashtable GetCustomPSSASettingsHashtable(int tabSize, bool insertSpaces)
{
var ruleConfigurations = new Hashtable {
{"PSPlaceOpenBrace", new Hashtable {
{"Enable", true},
{"OnSameLine", OpenBraceOnSameLine},
{"NewLineAfter", NewLineAfterOpenBrace},
{"IgnoreOneLineBlock", IgnoreOneLineBlock}
}},
{"PSPlaceCloseBrace", new Hashtable {
{"Enable", true},
{"NewLineAfter", NewLineAfterCloseBrace},
{"IgnoreOneLineBlock", IgnoreOneLineBlock}
}},
{"PSUseConsistentIndentation", new Hashtable {
{"Enable", true},
{"IndentationSize", tabSize},
{"PipelineIndentation", PipelineIndentationStyle },
{"Kind", insertSpaces ? "space" : "tab"}
}},
{"PSUseConsistentWhitespace", new Hashtable {
{"Enable", true},
{"CheckOpenBrace", WhitespaceBeforeOpenBrace},
{"CheckOpenParen", WhitespaceBeforeOpenParen},
{"CheckOperator", WhitespaceAroundOperator},
{"CheckSeparator", WhitespaceAfterSeparator},
{"CheckInnerBrace", WhitespaceInsideBrace},
{"CheckPipe", WhitespaceAroundPipe},
}},
{"PSAlignAssignmentStatement", new Hashtable {
{"Enable", true},
{"CheckHashtable", AlignPropertyValuePairs}
}},
{"PSUseCorrectCasing", new Hashtable {
{"Enable", UseCorrectCasing}
}},
};
if (AutoCorrectAliases)
{
ruleConfigurations.Add("PSAvoidUsingCmdletAliases", new Hashtable());
}
return new Hashtable
{
{"IncludeRules", new string[] {
"PSPlaceCloseBrace",
"PSPlaceOpenBrace",
"PSUseConsistentWhitespace",
"PSUseConsistentIndentation",
"PSAlignAssignmentStatement",
"PSAvoidUsingCmdletAliases",
}},
{
"Rules", ruleConfigurations
},
};
}
}
/// <summary>
/// Code folding settings
/// </summary>
public class CodeFoldingSettings
{
/// <summary>
/// Whether the folding is enabled. Default is true as per VSCode
/// </summary>
public bool Enable { get; set; } = true;
/// <summary>
/// Whether to show or hide the last line of a folding region. Default is true as per VSCode
/// </summary>
public bool ShowLastLine { get; set; } = true;
/// <summary>
/// Update these settings from another settings object
/// </summary>
public void Update(
CodeFoldingSettings settings,
ILogger logger)
{
if (settings != null) {
if (this.Enable != settings.Enable) {
this.Enable = settings.Enable;
logger.Write(LogLevel.Verbose, string.Format("Using Code Folding Enabled - {0}", this.Enable));
}
if (this.ShowLastLine != settings.ShowLastLine) {
this.ShowLastLine = settings.ShowLastLine;
logger.Write(LogLevel.Verbose, string.Format("Using Code Folding ShowLastLine - {0}", this.ShowLastLine));
}
}
}
}
/// <summary>
/// Additional settings from the Language Client that affect Language Server operations but
/// do not exist under the 'powershell' section
/// </summary>
public class EditorFileSettings
{
/// <summary>
/// Exclude files globs consists of hashtable with the key as the glob and a boolean value to indicate if the
/// the glob is in effect.
/// </summary>
public Dictionary<string, bool> Exclude { get; set; }
}
/// <summary>
/// Additional settings from the Language Client that affect Language Server operations but
/// do not exist under the 'powershell' section
/// </summary>
public class EditorSearchSettings
{
/// <summary>
/// Exclude files globs consists of hashtable with the key as the glob and a boolean value to indicate if the
/// the glob is in effect.
/// </summary>
public Dictionary<string, bool> Exclude { get; set; }
/// <summary>
/// Whether to follow symlinks when searching
/// </summary>
public bool FollowSymlinks { get; set; } = true;
}
public class LanguageServerSettingsWrapper
{
// NOTE: This property is capitalized as 'Powershell' because the
// mode name sent from the client is written as 'powershell' and
// JSON.net is using camelCasing.
public LanguageServerSettings Powershell { get; set; }
// NOTE: This property is capitalized as 'Files' because the
// mode name sent from the client is written as 'files' and
// JSON.net is using camelCasing.
public EditorFileSettings Files { get; set; }
// NOTE: This property is capitalized as 'Search' because the
// mode name sent from the client is written as 'search' and
// JSON.net is using camelCasing.
public EditorSearchSettings Search { get; set; }
}
}