@@ -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