@@ -40,13 +40,79 @@ 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 ) 
47+  { 
48+  // Sort by the start of the symbol definition. 
49+  symbols . Sort ( ( x1 ,  x2 )  =>  x1 . Range . Start . CompareTo ( x2 . Range . Start ) ) ; 
50+ 51+  List < DocumentSymbol >  parents  =  new ( ) ; 
52+ 53+  foreach  ( DocumentSymbol  symbol  in  symbols ) 
54+  { 
55+  // This async method is pretty dense with synchronous code 
56+  // so it's helpful to add some yields. 
57+  await  Task . Yield ( ) ; 
58+  if  ( cancellationToken . IsCancellationRequested ) 
59+  { 
60+  return  symbols ; 
61+  } 
62+ 63+  // Base case. 
64+  if  ( parents . Count  ==  0 ) 
65+  { 
66+  parents . Add ( symbol ) ; 
67+  } 
68+  // Symbol starts after end of last symbol parsed. 
69+  else  if  ( symbol . Range . Start  >  parents [ parents . Count  -  1 ] . Range . End ) 
70+  { 
71+  parents . Add ( symbol ) ; 
72+  } 
73+  // Find where it fits. 
74+  else 
75+  { 
76+  for  ( int  i  =  0 ;  i  <  parents . Count ;  i ++ ) 
77+  { 
78+  DocumentSymbol  parent  =  parents [ i ] ; 
79+  if  ( parent . Range . Start  <=  symbol . Range . Start  &&  symbol . Range . End  <=  parent . Range . End ) 
80+  { 
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  } ; 
88+  break ; 
89+  } 
90+  } 
91+  } 
92+  } 
93+ 94+  // Recursively sort the children. 
95+  for  ( int  i  =  0 ;  i  <  parents . Count ;  i ++ ) 
96+  { 
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+  } 
104+  } 
105+ 106+  return  parents ; 
107+  } 
108+ 43109 // AKA the outline feature 
44110 public  override  async  Task < SymbolInformationOrDocumentSymbolContainer >  Handle ( DocumentSymbolParams  request ,  CancellationToken  cancellationToken ) 
45111 { 
46112 _logger . LogDebug ( $ "Handling document symbols for { request . TextDocument . Uri } ") ; 
47113
48114 ScriptFile  scriptFile  =  _workspaceService . GetFile ( request . TextDocument . Uri ) ; 
49-  List < SymbolInformationOrDocumentSymbol >  symbols  =  new ( ) ; 
115+  List < DocumentSymbol >  symbols  =  new ( ) ; 
50116
51117 foreach  ( SymbolReference  r  in  ProvideDocumentSymbols ( scriptFile ) ) 
52118 { 
@@ -71,18 +137,31 @@ public override async Task<SymbolInformationOrDocumentSymbolContainer> Handle(Do
71137 // symbols, and we don't have the information nor algorithm to do that currently. 
72138 // OmniSharp was previously doing this for us based on the range, perhaps we can 
73139 // find that logic and reuse it. 
74-  symbols . Add ( new  SymbolInformationOrDocumentSymbol ( new DocumentSymbol 
140+  symbols . Add ( new  DocumentSymbol 
75141 { 
76142 Kind  =  SymbolTypeUtils . GetSymbolKind ( r . Type ) , 
77143 Range  =  r . ScriptRegion . ToRange ( ) , 
78144 SelectionRange  =  r . NameRegion . ToRange ( ) , 
79145 Name  =  r . Name 
80-  } ) ) ; 
146+  } ) ; 
147+  } 
148+ 149+  // Short-circuit if we have no symbols. 
150+  if  ( symbols . Count  ==  0 ) 
151+  { 
152+  return  s_emptySymbolInformationOrDocumentSymbolContainer ; 
81153 } 
82154
83-  return  symbols . Count  ==  0 
84-  ?  s_emptySymbolInformationOrDocumentSymbolContainer 
85-  :  new  SymbolInformationOrDocumentSymbolContainer ( symbols ) ; 
155+  // Otherwise slowly sort them into a hierarchy. 
156+  symbols  =  await  SortDocumentSymbols ( symbols ,  cancellationToken ) . ConfigureAwait ( false ) ; 
157+ 158+  // And finally convert them to the silly SymbolInformationOrDocumentSymbol wrapper. 
159+  List < SymbolInformationOrDocumentSymbol >  container  =  new ( ) ; 
160+  foreach  ( DocumentSymbol  symbol  in  symbols ) 
161+  { 
162+  container . Add ( new  SymbolInformationOrDocumentSymbol ( symbol ) ) ; 
163+  } 
164+  return  container ; 
86165 } 
87166
88167 private  IEnumerable < SymbolReference >  ProvideDocumentSymbols ( ScriptFile  scriptFile ) 
0 commit comments