Skip to content

Commit e75c2a1

Browse files
committed
Separate out AstExtensions and continue Functions Reimplement. FunctionsSingle test works at least
1 parent 5be01f5 commit e75c2a1

File tree

4 files changed

+222
-407
lines changed

4 files changed

+222
-407
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#nullable enable
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Management.Automation.Language;
9+
using Microsoft.PowerShell.EditorServices.Services;
10+
11+
namespace Microsoft.PowerShell.EditorServices.Language;
12+
13+
public static class AstExtensions
14+
{
15+
/// <summary>
16+
/// Finds the most specific Ast at the given script position, or returns null if none found.<br/>
17+
/// For example, if the position is on a variable expression within a function definition,
18+
/// the variable will be returned even if the function definition is found first.
19+
/// </summary>
20+
internal static Ast? FindAtPosition(this Ast ast, IScriptPosition position, Type[]? allowedTypes)
21+
{
22+
// Short circuit quickly if the position is not in the provided range, no need to traverse if not
23+
// TODO: Maybe this should be an exception instead? I mean technically its not found but if you gave a position outside the file something very wrong probably happened.
24+
if (!new ScriptExtentAdapter(ast.Extent).Contains(position)) { return null; }
25+
26+
// This will be updated with each loop, and re-Find to dig deeper
27+
Ast? mostSpecificAst = null;
28+
Ast? currentAst = ast;
29+
30+
do
31+
{
32+
currentAst = currentAst.Find(thisAst =>
33+
{
34+
if (thisAst == mostSpecificAst) { return false; }
35+
36+
int line = position.LineNumber;
37+
int column = position.ColumnNumber;
38+
39+
// Performance optimization, skip statements that don't contain the position
40+
if (
41+
thisAst.Extent.EndLineNumber < line
42+
|| thisAst.Extent.StartLineNumber > line
43+
|| (thisAst.Extent.EndLineNumber == line && thisAst.Extent.EndColumnNumber < column)
44+
|| (thisAst.Extent.StartLineNumber == line && thisAst.Extent.StartColumnNumber > column)
45+
)
46+
{
47+
return false;
48+
}
49+
50+
if (allowedTypes is not null && !allowedTypes.Contains(thisAst.GetType()))
51+
{
52+
return false;
53+
}
54+
55+
if (new ScriptExtentAdapter(thisAst.Extent).Contains(position))
56+
{
57+
mostSpecificAst = thisAst;
58+
return true; //Stops this particular find and looks more specifically
59+
}
60+
61+
return false;
62+
}, true);
63+
64+
if (currentAst is not null)
65+
{
66+
mostSpecificAst = currentAst;
67+
}
68+
} while (currentAst is not null);
69+
70+
return mostSpecificAst;
71+
}
72+
73+
public static FunctionDefinitionAst? FindFunctionDefinition(this Ast ast, CommandAst command)
74+
{
75+
string? name = command.GetCommandName();
76+
if (name is null) { return null; }
77+
78+
List<FunctionDefinitionAst> FunctionDefinitions = ast.FindAll(ast =>
79+
{
80+
return ast is FunctionDefinitionAst funcDef &&
81+
funcDef.Name.ToLower() == name &&
82+
(funcDef.Extent.EndLineNumber < command.Extent.StartLineNumber ||
83+
(funcDef.Extent.EndColumnNumber <= command.Extent.StartColumnNumber &&
84+
funcDef.Extent.EndLineNumber <= command.Extent.StartLineNumber));
85+
}, true).Cast<FunctionDefinitionAst>().ToList();
86+
87+
// return the function def if we only have one match
88+
if (FunctionDefinitions.Count == 1)
89+
{
90+
return FunctionDefinitions[0];
91+
}
92+
// Determine which function definition is the right one
93+
FunctionDefinitionAst? CorrectDefinition = null;
94+
for (int i = FunctionDefinitions.Count - 1; i >= 0; i--)
95+
{
96+
FunctionDefinitionAst element = FunctionDefinitions[i];
97+
98+
Ast parent = element.Parent;
99+
// walk backwards till we hit a functiondefinition if any
100+
while (null != parent)
101+
{
102+
if (parent is FunctionDefinitionAst)
103+
{
104+
break;
105+
}
106+
parent = parent.Parent;
107+
}
108+
// we have hit the global scope of the script file
109+
if (null == parent)
110+
{
111+
CorrectDefinition = element;
112+
break;
113+
}
114+
115+
if (command?.Parent == parent)
116+
{
117+
CorrectDefinition = (FunctionDefinitionAst)parent;
118+
}
119+
}
120+
return CorrectDefinition;
121+
}
122+
123+
124+
public static Ast[] FindParents(this Ast ast, params Type[] type)
125+
{
126+
List<Ast> parents = new();
127+
Ast parent = ast;
128+
while (parent is not null)
129+
{
130+
if (type.Contains(parent.GetType()))
131+
{
132+
parents.Add(parent);
133+
}
134+
parent = parent.Parent;
135+
}
136+
return parents.ToArray();
137+
}
138+
139+
public static Ast GetHighestParent(this Ast ast)
140+
=> ast.Parent is null ? ast : ast.Parent.GetHighestParent();
141+
142+
public static Ast GetHighestParent(this Ast ast, params Type[] type)
143+
=> FindParents(ast, type).LastOrDefault() ?? ast;
144+
145+
/// <summary>
146+
/// Gets the closest parent that matches the specified type or null if none found.
147+
/// </summary>
148+
public static Ast? FindParent(this Ast ast, params Type[] type)
149+
=> FindParents(ast, type).FirstOrDefault();
150+
151+
/// <summary>
152+
/// Gets the closest parent that matches the specified type or null if none found.
153+
/// </summary>
154+
public static T? FindParent<T>(this Ast ast) where T : Ast
155+
=> ast.FindParent(typeof(T)) as T;
156+
157+
public static bool HasParent(this Ast ast, Ast parent)
158+
{
159+
Ast? current = ast;
160+
while (current is not null)
161+
{
162+
if (current == parent)
163+
{
164+
return true;
165+
}
166+
current = current.Parent;
167+
}
168+
return false;
169+
}
170+
171+
}

src/PowerShellEditorServices/Services/PowerShell/Refactoring/IterativeFunctionRename.cs

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

src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ private CompletionItem CreateProviderItemCompletion(
374374
}
375375

376376
InsertTextFormat insertFormat;
377-
OmniSharp.Extensions.LanguageServer.Protocol.Models.TextEdit edit;
377+
TextEdit edit;
378378
CompletionItemKind itemKind;
379379
if (result.ResultType is CompletionResultType.ProviderContainer
380380
&& SupportsSnippets

0 commit comments

Comments
 (0)