forked from PowerShell/PowerShellEditorServices
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPesterDocumentSymbolProvider.cs
163 lines (139 loc) · 5.1 KB
/
PesterDocumentSymbolProvider.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
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation.Language;
namespace Microsoft.PowerShell.EditorServices.Symbols
{
/// <summary>
/// Provides an IDocumentSymbolProvider implementation for
/// enumerating test symbols in Pester test (tests.ps1) files.
/// </summary>
public class PesterDocumentSymbolProvider : FeatureProviderBase, IDocumentSymbolProvider
{
IEnumerable<SymbolReference> IDocumentSymbolProvider.ProvideDocumentSymbols(
ScriptFile scriptFile)
{
if (!scriptFile.FilePath.EndsWith(
"tests.ps1",
StringComparison.OrdinalIgnoreCase))
{
return Enumerable.Empty<SymbolReference>();
}
var commandAsts = scriptFile.ScriptAst.FindAll(ast =>
{
CommandAst commandAst = ast as CommandAst;
return
commandAst != null &&
PesterSymbolReference.GetCommandType(commandAst.GetCommandName()).HasValue &&
commandAst.CommandElements.Count >= 2;
},
true);
return commandAsts.Select(
ast =>
{
// By this point we know the Ast is a CommandAst with 2 or more CommandElements
int testNameParamIndex = 1;
CommandAst testAst = (CommandAst)ast;
// The -Name parameter
for (int i = 1; i < testAst.CommandElements.Count; i++)
{
CommandParameterAst paramAst = testAst.CommandElements[i] as CommandParameterAst;
if (paramAst != null &&
paramAst.ParameterName.Equals("Name", StringComparison.OrdinalIgnoreCase))
{
testNameParamIndex = i + 1;
break;
}
}
if (testNameParamIndex > testAst.CommandElements.Count - 1)
{
return null;
}
StringConstantExpressionAst stringAst =
testAst.CommandElements[testNameParamIndex] as StringConstantExpressionAst;
if (stringAst == null)
{
return null;
}
string testDefinitionLine =
scriptFile.GetLine(
ast.Extent.StartLineNumber);
return
new PesterSymbolReference(
scriptFile,
testAst.GetCommandName(),
testDefinitionLine,
stringAst.Value,
ast.Extent);
}).Where(s => s != null);
}
}
/// <summary>
/// Defines command types for Pester test blocks.
/// </summary>
public enum PesterCommandType
{
/// <summary>
/// Identifies a Describe block.
/// </summary>
Describe,
/// <summary>
/// Identifies a Context block.
/// </summary>
Context,
/// <summary>
/// Identifies an It block.
/// </summary>
It
}
/// <summary>
/// Provides a specialization of SymbolReference containing
/// extra information about Pester test symbols.
/// </summary>
public class PesterSymbolReference : SymbolReference
{
private static char[] DefinitionTrimChars = new char[] { ' ', '{' };
/// <summary>
/// Gets the name of the test
/// </summary>
public string TestName { get; private set; }
/// <summary>
/// Gets the test's command type.
/// </summary>
public PesterCommandType Command { get; private set; }
internal PesterSymbolReference(
ScriptFile scriptFile,
string commandName,
string testLine,
string testName,
IScriptExtent scriptExtent)
: base(
SymbolType.Function,
testLine.TrimEnd(DefinitionTrimChars),
scriptExtent,
scriptFile.FilePath,
testLine)
{
this.Command = GetCommandType(commandName).Value;
this.TestName = testName;
}
internal static PesterCommandType? GetCommandType(string commandName)
{
switch (commandName.ToLower())
{
case "describe":
return PesterCommandType.Describe;
case "context":
return PesterCommandType.Context;
case "it":
return PesterCommandType.It;
default:
return null;
}
}
}
}