|  | 
| 2 | 2 | // Licensed under the MIT License. | 
| 3 | 3 | 
 | 
| 4 | 4 | using System.Collections.Generic; | 
|  | 5 | +using System.Diagnostics; | 
| 5 | 6 | using System.Threading; | 
| 6 | 7 | using System.Threading.Tasks; | 
| 7 | 8 | using Microsoft.Extensions.Logging; | 
| @@ -40,63 +41,75 @@ public PsesDocumentSymbolHandler(ILoggerFactory factory, WorkspaceService worksp | 
| 40 | 41 |  DocumentSelector = LspUtils.PowerShellDocumentSelector | 
| 41 | 42 |  }; | 
| 42 | 43 | 
 | 
| 43 |  | - // This turns a flat list of symbols into a hierarchical list. | 
| 44 |  | - private static asyncTask<List<HierarchicalSymbol>> SortHierarchicalSymbols(List<HierarchicalSymbol> symbols, CancellationToken cancellationToken) | 
|  | 44 | + // Modifies a flat list of symbols into a hierarchical list. | 
|  | 45 | + private static Task SortHierarchicalSymbols(List<HierarchicalSymbol> symbols, CancellationToken cancellationToken) | 
| 45 | 46 |  { | 
| 46 | 47 |  // 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). | 
|  | 48 | + // certain otherwise this algorithm won't work). We only need to sort the list once, and | 
|  | 49 | + // since the implementation is recursive, it's easiest to use the stack to track that | 
|  | 50 | + // this is the first call. | 
| 48 | 51 |  symbols.Sort((x1, x2) => x1.Range.Start.CompareTo(x2.Range.Start)); | 
|  | 52 | + return SortHierarchicalSymbolsImpl(symbols, cancellationToken); | 
|  | 53 | + } | 
| 49 | 54 | 
 | 
| 50 |  | - List<HierarchicalSymbol> parents=new(); | 
| 51 |  | - | 
| 52 |  | - foreach(HierarchicalSymbolsymbolinsymbols) | 
|  | 55 | + privatestaticasyncTaskSortHierarchicalSymbolsImpl(List<HierarchicalSymbol> symbols,CancellationTokencancellationToken) | 
|  | 56 | +{ | 
|  | 57 | + for(inti=0;i<symbols.Count;i++) | 
| 53 | 58 |  { | 
| 54 | 59 |  // This async method is pretty dense with synchronous code | 
| 55 | 60 |  // so it's helpful to add some yields. | 
| 56 | 61 |  await Task.Yield(); | 
| 57 | 62 |  if (cancellationToken.IsCancellationRequested) | 
| 58 | 63 |  { | 
| 59 |  | - returnparents; | 
|  | 64 | + return; | 
| 60 | 65 |  } | 
| 61 |  | - // Base case where we haven't found any parents yet. | 
| 62 |  | - if (parents.Count == 0) | 
|  | 66 | + | 
|  | 67 | + HierarchicalSymbol symbol = symbols[i]; | 
|  | 68 | + | 
|  | 69 | + // Base case where we haven't found any parents yet (the first symbol must be a | 
|  | 70 | + // parent by definition). | 
|  | 71 | + if (i == 0) | 
| 63 | 72 |  { | 
| 64 |  | - parents.Add(symbol); | 
|  | 73 | + continue; | 
| 65 | 74 |  } | 
| 66 | 75 |  // If the symbol starts after end of last symbol parsed then it's a new parent. | 
| 67 |  | - else if (symbol.Range.Start > parents[parents.Count - 1].Range.End) | 
|  | 76 | + else if (symbol.Range.Start > symbols[i - 1].Range.End) | 
| 68 | 77 |  { | 
| 69 |  | - parents.Add(symbol); | 
|  | 78 | + continue; | 
| 70 | 79 |  } | 
| 71 |  | - // Otherwise it's a child, we just need to figure out whose child it is. | 
|  | 80 | + // Otherwise it's a child, we just need to figure out whose child it is and move it there (which also means removing it from the current list). | 
| 72 | 81 |  else | 
| 73 | 82 |  { | 
| 74 |  | - foreach(HierarchicalSymbolparentinparents) | 
|  | 83 | + for(intj=0;j<=i;j++) | 
| 75 | 84 |  { | 
|  | 85 | + // While we should only check up to j < i, we iterate up to j <= i so that | 
|  | 86 | + // we can check this assertion that we didn't exhaust the parents. | 
|  | 87 | + Debug.Assert(j != i, "We didn't find the child's parent!"); | 
|  | 88 | + | 
|  | 89 | + HierarchicalSymbol parent = symbols[j]; | 
| 76 | 90 |  // If the symbol starts after the parent starts and ends before the parent | 
| 77 | 91 |  // ends then its a child. | 
| 78 | 92 |  if (symbol.Range.Start > parent.Range.Start && symbol.Range.End < parent.Range.End) | 
| 79 | 93 |  { | 
|  | 94 | + // Add it to the parent's list. | 
| 80 | 95 |  parent.Children.Add(symbol); | 
|  | 96 | + // Remove it from this "parents" list (because it's a child) and adjust | 
|  | 97 | + // our loop counter because it's been removed. | 
|  | 98 | + symbols.RemoveAt(i); | 
|  | 99 | + i--; | 
| 81 | 100 |  break; | 
| 82 | 101 |  } | 
| 83 | 102 |  } | 
| 84 |  | - // TODO: If we somehow exist the list of parents and didn't find a place for the | 
| 85 |  | - // child...we have a problem. | 
| 86 | 103 |  } | 
| 87 | 104 |  } | 
| 88 | 105 | 
 | 
| 89 | 106 |  // Now recursively sort the children into nested buckets of children too. | 
| 90 |  | - foreach (HierarchicalSymbol parent in parents) | 
|  | 107 | + foreach (HierarchicalSymbol parent in symbols) | 
| 91 | 108 |  { | 
| 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); | 
|  | 109 | + // Since this modifies in place we just recurse, no re-assignment or clearing from | 
|  | 110 | + // parent.Children necessary. | 
|  | 111 | + await SortHierarchicalSymbols(parent.Children, cancellationToken).ConfigureAwait(false); | 
| 97 | 112 |  } | 
| 98 |  | - | 
| 99 |  | - return parents; | 
| 100 | 113 |  } | 
| 101 | 114 | 
 | 
| 102 | 115 |  // This struct and the mapping function below exist to allow us to skip a *bunch* of | 
| @@ -174,8 +187,8 @@ public override async Task<SymbolInformationOrDocumentSymbolContainer> Handle(Do | 
| 174 | 187 |  return s_emptySymbolInformationOrDocumentSymbolContainer; | 
| 175 | 188 |  } | 
| 176 | 189 | 
 | 
| 177 |  | - // Otherwise slowly sort them into a hierarchy. | 
| 178 |  | - hierarchicalSymbols=await SortHierarchicalSymbols(hierarchicalSymbols, cancellationToken).ConfigureAwait(false); | 
|  | 190 | + // Otherwise slowly sort them into a hierarchy (this modifies the list). | 
|  | 191 | + await SortHierarchicalSymbols(hierarchicalSymbols, cancellationToken).ConfigureAwait(false); | 
| 179 | 192 | 
 | 
| 180 | 193 |  // And finally convert them to the silly SymbolInformationOrDocumentSymbol wrapper. | 
| 181 | 194 |  List<SymbolInformationOrDocumentSymbol> container = new(); | 
|  | 
0 commit comments