Skip to content

Commit 41c1d5a

Browse files
DocumentSymbols and References support (#997)
* working formatting * add tests * delete commented out code
1 parent 0a24525 commit 41c1d5a

17 files changed

+1396
-3
lines changed

src/PowerShellEditorServices.Engine/LanguageServer/OmnisharpLanguageServer.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ public async Task StartAsync()
7575
.WithHandler<ConfigurationHandler>()
7676
.WithHandler<FoldingRangeHandler>()
7777
.WithHandler<DocumentFormattingHandler>()
78-
.WithHandler<DocumentRangeFormattingHandler>();
78+
.WithHandler<DocumentRangeFormattingHandler>()
79+
.WithHandler<ReferencesHandler>()
80+
.WithHandler<DocumentSymbolHandler>();
7981
});
8082

8183
_serverStart.SetResult(true);

src/PowerShellEditorServices.Engine/Services/Symbols/SymbolsService.cs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
using System;
12
using System.Collections.Generic;
3+
using System.Collections.Specialized;
4+
using System.Runtime.InteropServices;
25
using Microsoft.Extensions.Logging;
36
using Microsoft.PowerShell.EditorServices.Symbols;
47

@@ -68,5 +71,110 @@ public List<SymbolReference> FindSymbolsInFile(ScriptFile scriptFile)
6871

6972
return foundOccurrences;
7073
}
74+
75+
/// <summary>
76+
/// Finds the symbol in the script given a file location
77+
/// </summary>
78+
/// <param name="scriptFile">The details and contents of a open script file</param>
79+
/// <param name="lineNumber">The line number of the cursor for the given script</param>
80+
/// <param name="columnNumber">The coulumn number of the cursor for the given script</param>
81+
/// <returns>A SymbolReference of the symbol found at the given location
82+
/// or null if there is no symbol at that location
83+
/// </returns>
84+
public SymbolReference FindSymbolAtLocation(
85+
ScriptFile scriptFile,
86+
int lineNumber,
87+
int columnNumber)
88+
{
89+
SymbolReference symbolReference =
90+
AstOperations.FindSymbolAtPosition(
91+
scriptFile.ScriptAst,
92+
lineNumber,
93+
columnNumber);
94+
95+
if (symbolReference != null)
96+
{
97+
symbolReference.FilePath = scriptFile.FilePath;
98+
}
99+
100+
return symbolReference;
101+
}
102+
103+
/// <summary>
104+
/// Finds all the references of a symbol
105+
/// </summary>
106+
/// <param name="foundSymbol">The symbol to find all references for</param>
107+
/// <param name="referencedFiles">An array of scriptFiles too search for references in</param>
108+
/// <param name="workspace">The workspace that will be searched for symbols</param>
109+
/// <returns>FindReferencesResult</returns>
110+
public List<SymbolReference> FindReferencesOfSymbol(
111+
SymbolReference foundSymbol,
112+
ScriptFile[] referencedFiles,
113+
WorkspaceService workspace)
114+
{
115+
if (foundSymbol == null)
116+
{
117+
return null;
118+
}
119+
120+
int symbolOffset = referencedFiles[0].GetOffsetAtPosition(
121+
foundSymbol.ScriptRegion.StartLineNumber,
122+
foundSymbol.ScriptRegion.StartColumnNumber);
123+
124+
// NOTE: we use to make sure aliases were loaded but took it out because we needed the pipeline thread.
125+
126+
// We want to look for references first in referenced files, hence we use ordered dictionary
127+
// TODO: File system case-sensitivity is based on filesystem not OS, but OS is a much cheaper heuristic
128+
var fileMap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
129+
? new OrderedDictionary()
130+
: new OrderedDictionary(StringComparer.OrdinalIgnoreCase);
131+
132+
foreach (ScriptFile scriptFile in referencedFiles)
133+
{
134+
fileMap[scriptFile.FilePath] = scriptFile;
135+
}
136+
137+
foreach (string filePath in workspace.EnumeratePSFiles())
138+
{
139+
if (!fileMap.Contains(filePath))
140+
{
141+
if (!workspace.TryGetFile(filePath, out ScriptFile scriptFile))
142+
{
143+
// If we can't access the file for some reason, just ignore it
144+
continue;
145+
}
146+
147+
fileMap[filePath] = scriptFile;
148+
}
149+
}
150+
151+
var symbolReferences = new List<SymbolReference>();
152+
foreach (object fileName in fileMap.Keys)
153+
{
154+
var file = (ScriptFile)fileMap[fileName];
155+
156+
IEnumerable<SymbolReference> references = AstOperations.FindReferencesOfSymbol(
157+
file.ScriptAst,
158+
foundSymbol,
159+
needsAliases: false);
160+
161+
foreach (SymbolReference reference in references)
162+
{
163+
try
164+
{
165+
reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber);
166+
}
167+
catch (ArgumentOutOfRangeException e)
168+
{
169+
reference.SourceLine = string.Empty;
170+
_logger.LogException("Found reference is out of range in script file", e);
171+
}
172+
reference.FilePath = file.FilePath;
173+
symbolReferences.Add(reference);
174+
}
175+
}
176+
177+
return symbolReferences;
178+
}
71179
}
72180
}

0 commit comments

Comments
 (0)