Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d8033b7

Browse files
Improve algorithm to avoid excess allocations
1 parent 972e04e commit d8033b7

File tree

1 file changed

+69
-47
lines changed

1 file changed

+69
-47
lines changed

‎src/PowerShellEditorServices/Services/TextDocument/Handlers/DocumentSymbolHandler.cs‎

Lines changed: 69 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -40,81 +40,106 @@ public PsesDocumentSymbolHandler(ILoggerFactory factory, WorkspaceService worksp
4040
DocumentSelector = LspUtils.PowerShellDocumentSelector
4141
};
4242

43-
// This turns a flat list of symbols into a hierarchical list. It's ugly because we're
44-
// dealing with records and so sadly must slowly copy and replace things whenever need to do
45-
// a modification, but it seems to work.
46-
private static async Task<List<DocumentSymbol>> SortDocumentSymbols(List<DocumentSymbol> symbols, CancellationToken cancellationToken)
43+
// This turns a flat list of symbols into a hierarchical list.
44+
private static async Task<List<HierarchicalSymbol>> SortHierarchicalSymbols(List<HierarchicalSymbol> symbols, CancellationToken cancellationToken)
4745
{
48-
// Sort by the start of the symbol definition.
46+
// Sort by the start of the symbol definition (they're probably sorted but we need to be
47+
// certain otherwise this algorithm won't work).
4948
symbols.Sort((x1, x2) => x1.Range.Start.CompareTo(x2.Range.Start));
5049

51-
List<DocumentSymbol> parents = new();
50+
List<HierarchicalSymbol> parents = new();
5251

53-
foreach (DocumentSymbol symbol in symbols)
52+
foreach (HierarchicalSymbol symbol in symbols)
5453
{
5554
// This async method is pretty dense with synchronous code
5655
// so it's helpful to add some yields.
5756
await Task.Yield();
5857
if (cancellationToken.IsCancellationRequested)
5958
{
60-
return symbols;
59+
return parents;
6160
}
62-
63-
// Base case.
61+
// Base case where we haven't found any parents yet.
6462
if (parents.Count == 0)
6563
{
6664
parents.Add(symbol);
6765
}
68-
// Symbol starts after end of last symbol parsed.
66+
// If the symbol starts after end of last symbol parsed then it's a new parent.
6967
else if (symbol.Range.Start > parents[parents.Count - 1].Range.End)
7068
{
7169
parents.Add(symbol);
7270
}
73-
// Find where it fits.
71+
// Otherwise it's a child, we just need to figure out whose child it is.
7472
else
7573
{
76-
for(inti=0;i<parents.Count;i++)
74+
foreach(HierarchicalSymbolparentinparents)
7775
{
78-
DocumentSymbol parent = parents[i];
79-
if (parent.Range.Start <= symbol.Range.Start && symbol.Range.End <= parent.Range.End)
76+
// If the symbol starts after the parent starts and ends before the parent
77+
// ends then its a child.
78+
if (symbol.Range.Start > parent.Range.Start && symbol.Range.End < parent.Range.End)
8079
{
81-
List<DocumentSymbol> children = new();
82-
if (parent.Children is not null)
83-
{
84-
children.AddRange(parent.Children);
85-
}
86-
children.Add(symbol);
87-
parents[i] = parent with { Children = children };
80+
parent.Children.Add(symbol);
8881
break;
8982
}
9083
}
84+
// TODO: If we somehow exist the list of parents and didn't find a place for the
85+
// child...we have a problem.
9186
}
9287
}
9388

94-
// Recursively sort the children.
95-
for(inti=0;i<parents.Count;i++)
89+
// Now recursively sort the children into nested buckets of children too.
90+
foreach(HierarchicalSymbolparentinparents)
9691
{
97-
DocumentSymbol parent = parents[i];
98-
if (parent.Children is not null)
99-
{
100-
List<DocumentSymbol> children = new(parent.Children);
101-
children = await SortDocumentSymbols(children, cancellationToken).ConfigureAwait(false);
102-
parents[i] = parent with { Children = children };
103-
}
92+
List<HierarchicalSymbol> sortedChildren = await SortHierarchicalSymbols(parent.Children, cancellationToken).ConfigureAwait(false);
93+
// Since this is a foreach we can't just assign to parent.Children and have to do
94+
// this instead.
95+
parent.Children.Clear();
96+
parent.Children.AddRange(sortedChildren);
10497
}
10598

10699
return parents;
107100
}
108101

109-
// AKA the outline feature
102+
// This struct and the mapping function below exist to allow us to skip a *bunch* of
103+
// unnecessary allocations when sorting the symbols since DocumentSymbol (which this is
104+
// pretty much a mirror of) is an immutable record...but we need to constantly modify the
105+
// list of children when sorting.
106+
private struct HierarchicalSymbol
107+
{
108+
public SymbolKind Kind;
109+
public Range Range;
110+
public Range SelectionRange;
111+
public string Name;
112+
public List<HierarchicalSymbol> Children;
113+
}
114+
115+
// Recursively turn our HierarchicalSymbol struct into OmniSharp's DocumentSymbol record.
116+
private static List<DocumentSymbol> GetDocumentSymbolsFromHierarchicalSymbols(IEnumerable<HierarchicalSymbol> hierarchicalSymbols)
117+
{
118+
List<DocumentSymbol> documentSymbols = new();
119+
foreach (HierarchicalSymbol symbol in hierarchicalSymbols)
120+
{
121+
documentSymbols.Add(new DocumentSymbol
122+
{
123+
Kind = symbol.Kind,
124+
Range = symbol.Range,
125+
SelectionRange = symbol.SelectionRange,
126+
Name = symbol.Name,
127+
Children = GetDocumentSymbolsFromHierarchicalSymbols(symbol.Children)
128+
});
129+
}
130+
return documentSymbols;
131+
}
132+
133+
// AKA the outline feature!
110134
public override async Task<SymbolInformationOrDocumentSymbolContainer> Handle(DocumentSymbolParams request, CancellationToken cancellationToken)
111135
{
112136
_logger.LogDebug($"Handling document symbols for {request.TextDocument.Uri}");
113137

114138
ScriptFile scriptFile = _workspaceService.GetFile(request.TextDocument.Uri);
115-
List<DocumentSymbol> symbols = new();
116139

117-
foreach (SymbolReference r in ProvideDocumentSymbols(scriptFile))
140+
List<HierarchicalSymbol> hierarchicalSymbols = new();
141+
142+
foreach (SymbolReference symbolReference in ProvideDocumentSymbols(scriptFile))
118143
{
119144
// This async method is pretty dense with synchronous code
120145
// so it's helpful to add some yields.
@@ -128,36 +153,33 @@ public override async Task<SymbolInformationOrDocumentSymbolContainer> Handle(Do
128153
//
129154
// TODO: We should also include function invocations that are part of DSLs (like
130155
// Invoke-Build etc.).
131-
if (!r.IsDeclaration || r.Type is SymbolType.Parameter)
156+
if (!symbolReference.IsDeclaration || symbolReference.Type is SymbolType.Parameter)
132157
{
133158
continue;
134159
}
135160

136-
// TODO: This now needs the Children property filled out to support hierarchical
137-
// symbols, and we don't have the information nor algorithm to do that currently.
138-
// OmniSharp was previously doing this for us based on the range, perhaps we can
139-
// find that logic and reuse it.
140-
symbols.Add(new DocumentSymbol
161+
hierarchicalSymbols.Add(new HierarchicalSymbol
141162
{
142-
Kind = SymbolTypeUtils.GetSymbolKind(r.Type),
143-
Range = r.ScriptRegion.ToRange(),
144-
SelectionRange = r.NameRegion.ToRange(),
145-
Name = r.Name
163+
Kind = SymbolTypeUtils.GetSymbolKind(symbolReference.Type),
164+
Range = symbolReference.ScriptRegion.ToRange(),
165+
SelectionRange = symbolReference.NameRegion.ToRange(),
166+
Name = symbolReference.Name,
167+
Children = new List<HierarchicalSymbol>()
146168
});
147169
}
148170

149171
// Short-circuit if we have no symbols.
150-
if (symbols.Count == 0)
172+
if (hierarchicalSymbols.Count == 0)
151173
{
152174
return s_emptySymbolInformationOrDocumentSymbolContainer;
153175
}
154176

155177
// Otherwise slowly sort them into a hierarchy.
156-
symbols = await SortDocumentSymbols(symbols, cancellationToken).ConfigureAwait(false);
178+
hierarchicalSymbols = await SortHierarchicalSymbols(hierarchicalSymbols, cancellationToken).ConfigureAwait(false);
157179

158180
// And finally convert them to the silly SymbolInformationOrDocumentSymbol wrapper.
159181
List<SymbolInformationOrDocumentSymbol> container = new();
160-
foreach (DocumentSymbol symbol in symbols)
182+
foreach (DocumentSymbol symbol in GetDocumentSymbolsFromHierarchicalSymbols(hierarchicalSymbols))
161183
{
162184
container.Add(new SymbolInformationOrDocumentSymbol(symbol));
163185
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /