@@ -40,13 +40,79 @@ public PsesDocumentSymbolHandler(ILoggerFactory factory, WorkspaceService worksp
40
40
DocumentSelector = LspUtils . PowerShellDocumentSelector
41
41
} ;
42
42
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
+
43
109
// AKA the outline feature
44
110
public override async Task < SymbolInformationOrDocumentSymbolContainer > Handle ( DocumentSymbolParams request , CancellationToken cancellationToken )
45
111
{
46
112
_logger . LogDebug ( $ "Handling document symbols for { request . TextDocument . Uri } ") ;
47
113
48
114
ScriptFile scriptFile = _workspaceService . GetFile ( request . TextDocument . Uri ) ;
49
- List < SymbolInformationOrDocumentSymbol > symbols = new ( ) ;
115
+ List < DocumentSymbol > symbols = new ( ) ;
50
116
51
117
foreach ( SymbolReference r in ProvideDocumentSymbols ( scriptFile ) )
52
118
{
@@ -71,18 +137,31 @@ public override async Task<SymbolInformationOrDocumentSymbolContainer> Handle(Do
71
137
// symbols, and we don't have the information nor algorithm to do that currently.
72
138
// OmniSharp was previously doing this for us based on the range, perhaps we can
73
139
// find that logic and reuse it.
74
- symbols . Add ( new SymbolInformationOrDocumentSymbol ( new DocumentSymbol
140
+ symbols . Add ( new DocumentSymbol
75
141
{
76
142
Kind = SymbolTypeUtils . GetSymbolKind ( r . Type ) ,
77
143
Range = r . ScriptRegion . ToRange ( ) ,
78
144
SelectionRange = r . NameRegion . ToRange ( ) ,
79
145
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 ;
81
153
}
82
154
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 ;
86
165
}
87
166
88
167
private IEnumerable < SymbolReference > ProvideDocumentSymbols ( ScriptFile scriptFile )
0 commit comments