4
4
using System . Collections . Generic ;
5
5
using System . Linq ;
6
6
using System . Management . Automation . Language ;
7
+ using System . Text . RegularExpressions ;
7
8
using Microsoft . PowerShell . EditorServices . Services . TextDocument ;
8
9
9
10
namespace Microsoft . PowerShell . EditorServices . Services . Symbols
@@ -16,14 +17,26 @@ internal class ScriptDocumentSymbolProvider : IDocumentSymbolProvider
16
17
{
17
18
string IDocumentSymbolProvider . ProviderId => nameof ( ScriptDocumentSymbolProvider ) ;
18
19
20
+ // This regular expression are used to match lines which mark the start of a region comment in a script.
21
+ // Based on the defaults in the VS Code Language Configuration at;
22
+ // https://github.com/Microsoft/vscode/blob/64186b0a26/extensions/powershell/language-configuration.json#L26-L31
23
+ // https://github.com/Microsoft/vscode/issues/49070
24
+ private static readonly Regex s_startRegionTextRegex = new (
25
+ @"^\s*#[rR]egion\b" , RegexOptions . Compiled ) ;
26
+
19
27
IEnumerable < ISymbolReference > IDocumentSymbolProvider . ProvideDocumentSymbols (
20
28
ScriptFile scriptFile )
21
29
{
30
+ if ( scriptFile ? . ScriptAst is null )
31
+ {
32
+ return Enumerable . Empty < SymbolReference > ( ) ;
33
+ }
34
+
22
35
// If we have an AST, then we know it's a PowerShell file
23
36
// so lets try to find symbols in the document.
24
- return scriptFile ? . ScriptAst != null
25
- ? FindSymbolsInDocument ( scriptFile . ScriptAst )
26
- : Enumerable . Empty < SymbolReference > ( ) ;
37
+ IEnumerable < SymbolReference > astSymbols = FindSymbolsInDocument ( scriptFile . ScriptAst ) ;
38
+ IEnumerable < SymbolReference > regions = FindRegionsInDocument ( scriptFile . ScriptTokens ) ;
39
+ return astSymbols . Concat ( regions ) ;
27
40
}
28
41
29
42
/// <summary>
@@ -48,5 +61,31 @@ public static IEnumerable<SymbolReference> FindSymbolsInDocument(Ast scriptAst)
48
61
scriptAst . Visit ( findSymbolsVisitor ) ;
49
62
return findSymbolsVisitor . SymbolReferences ;
50
63
}
64
+
65
+ private static IEnumerable < SymbolReference > FindRegionsInDocument ( Token [ ] tokens )
66
+ {
67
+ for ( int i = 0 ; i < tokens . Length ; i ++ )
68
+ {
69
+ Token token = tokens [ i ] ;
70
+
71
+ // Exclude everything but single-line comments
72
+ if ( token . Kind != TokenKind . Comment ||
73
+ token . Extent . StartLineNumber != token . Extent . EndLineNumber )
74
+ {
75
+ continue ;
76
+ }
77
+
78
+ // Look for <newline> #region <optional name>
79
+ // Document symbols only care about the symbol start and regex is expensive,
80
+ // so skip checking if region is actually closed with #endregion.
81
+ if ( TokenOperations . IsBlockComment ( i , tokens ) &&
82
+ s_startRegionTextRegex . IsMatch ( token . Text ) )
83
+ {
84
+ yield return new SymbolReference (
85
+ SymbolType . Region ,
86
+ token . Extent ) ;
87
+ }
88
+ }
89
+ }
51
90
}
52
91
}
0 commit comments