1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
+ using System ;
4
5
using System . Collections . Generic ;
5
6
using System . ComponentModel . Composition ;
6
7
using System . Diagnostics . CodeAnalysis ;
9
10
using System . Threading . Tasks ;
10
11
using System . Windows ;
11
12
using System . Windows . Data ;
12
- using System . Windows . Media ;
13
13
using Microsoft . VisualStudio . Imaging ;
14
14
using Microsoft . VisualStudio . Imaging . Interop ;
15
15
using Microsoft . VisualStudio . PlatformUI ;
19
19
using Microsoft . Web . LibraryManager . Vsix . Shared ;
20
20
using Microsoft . WebTools . Languages . Json . Editor . Completion ;
21
21
using Microsoft . WebTools . Languages . Json . Parser . Nodes ;
22
+ using Microsoft . WebTools . Languages . Shared . Parser ;
22
23
using Microsoft . WebTools . Languages . Shared . Parser . Nodes ;
23
24
24
25
namespace Microsoft . Web . LibraryManager . Vsix . Json . Completion
@@ -45,10 +46,20 @@ protected override IEnumerable<JsonCompletionEntry> GetEntries(JsonCompletionCon
45
46
{
46
47
MemberNode member = context . ContextNode . FindType < MemberNode > ( ) ;
47
48
49
+ // We can show completions for "files". This could be libraries/[n]/files or
50
+ // libraries/[n]/fileMappings/[m]/files.
48
51
if ( member == null || member . UnquotedNameText != "files" )
49
52
yield break ;
50
53
51
- var parent = member . Parent as ObjectNode ;
54
+ // If the current member is "files", then it is either:
55
+ // - a library "files" property
56
+ // - a fileMapping "files" property
57
+ MemberNode possibleFileMappingsNode = member . Parent . FindType < MemberNode > ( ) ;
58
+ bool isFileMapping = possibleFileMappingsNode ? . UnquotedNameText == "fileMappings" ;
59
+
60
+ ObjectNode parent = isFileMapping
61
+ ? possibleFileMappingsNode . Parent as ObjectNode
62
+ : member . Parent as ObjectNode ;
52
63
53
64
if ( ! JsonHelpers . TryGetInstallationState ( parent , out ILibraryInstallationState state ) )
54
65
yield break ;
@@ -67,18 +78,23 @@ protected override IEnumerable<JsonCompletionEntry> GetEntries(JsonCompletionCon
67
78
FrameworkElement presenter = GetPresenter ( context ) ;
68
79
IEnumerable < string > usedFiles = GetUsedFiles ( context ) ;
69
80
81
+ string rootPathPrefix = isFileMapping ? GetRootValue ( member ) : string . Empty ;
82
+ static string GetRootValue ( MemberNode fileMappingNode )
83
+ {
84
+ FindFileMappingRootVisitor visitor = new FindFileMappingRootVisitor ( ) ;
85
+ fileMappingNode . Parent ? . Accept ( visitor ) ;
86
+ return visitor . FoundNode ? . UnquotedValueText ?? string . Empty ;
87
+ }
88
+
70
89
if ( task . IsCompleted )
71
90
{
72
91
if ( ! ( task . Result is ILibrary library ) )
73
92
yield break ;
74
93
75
- foreach ( string file in library . Files . Keys )
94
+ IEnumerable < JsonCompletionEntry > completions = GetFileCompletions ( context , usedFiles , library , rootPathPrefix ) ;
95
+ foreach ( JsonCompletionEntry item in completions )
76
96
{
77
- if ( ! usedFiles . Contains ( file ) )
78
- {
79
- ImageMoniker glyph = WpfUtil . GetImageMonikerForFile ( file ) ;
80
- yield return new SimpleCompletionEntry ( file , glyph , context . Session ) ;
81
- }
97
+ yield return item ;
82
98
}
83
99
}
84
100
else
@@ -94,23 +110,40 @@ protected override IEnumerable<JsonCompletionEntry> GetEntries(JsonCompletionCon
94
110
95
111
if ( ! context . Session . IsDismissed )
96
112
{
97
- var results = new List < JsonCompletionEntry > ( ) ;
98
-
99
- foreach ( string file in library . Files . Keys )
100
- {
101
- if ( ! usedFiles . Contains ( file ) )
102
- {
103
- ImageMoniker glyph = WpfUtil . GetImageMonikerForFile ( file ) ;
104
- results . Add ( new SimpleCompletionEntry ( file , glyph , context . Session ) ) ;
105
- }
106
- }
107
-
108
- UpdateListEntriesSync ( context , results ) ;
113
+ IEnumerable < JsonCompletionEntry > completions = GetFileCompletions ( context , usedFiles , library , rootPathPrefix ) ;
114
+
115
+ UpdateListEntriesSync ( context , completions ) ;
109
116
}
110
117
} , TaskScheduler . Default ) ;
111
118
}
112
119
}
113
120
121
+ private static IEnumerable < JsonCompletionEntry > GetFileCompletions ( JsonCompletionContext context , IEnumerable < string > usedFiles , ILibrary library , string root )
122
+ {
123
+ static bool alwaysInclude ( string s ) => true ;
124
+ bool includeIfUnderRoot ( string s ) => FileHelpers . IsUnderRootDirectory ( s , root ) ;
125
+
126
+ Func < string , bool > filter = string . IsNullOrEmpty ( root )
127
+ ? alwaysInclude
128
+ : includeIfUnderRoot ;
129
+
130
+ bool rootHasTrailingSlash = string . IsNullOrEmpty ( root ) || root . EndsWith ( "/" ) || root . EndsWith ( "\\ " ) ;
131
+ int nameOffset = rootHasTrailingSlash ? root . Length : root . Length + 1 ;
132
+
133
+ foreach ( string file in library . Files . Keys )
134
+ {
135
+ if ( filter ( file ) )
136
+ {
137
+ string fileSubPath = file . Substring ( nameOffset ) ;
138
+ if ( ! usedFiles . Contains ( fileSubPath ) )
139
+ {
140
+ ImageMoniker glyph = WpfUtil . GetImageMonikerForFile ( file ) ;
141
+ yield return new SimpleCompletionEntry ( fileSubPath , glyph , context . Session ) ;
142
+ }
143
+ }
144
+ }
145
+ }
146
+
114
147
private static IEnumerable < string > GetUsedFiles ( JsonCompletionContext context )
115
148
{
116
149
ArrayNode array = context . ContextNode . FindType < ArrayNode > ( ) ;
@@ -139,5 +172,31 @@ private FrameworkElement GetPresenter(JsonCompletionContext context)
139
172
140
173
return presenter ;
141
174
}
175
+
176
+ private class FindFileMappingRootVisitor : INodeVisitor
177
+ {
178
+ public MemberNode FoundNode { get ; private set ; }
179
+
180
+ public VisitNodeResult Visit ( Node node )
181
+ {
182
+ if ( node is ObjectNode )
183
+ {
184
+ return VisitNodeResult . Continue ;
185
+ }
186
+ // we only look at the object and it's members, this is not a recursive search
187
+ if ( node is not MemberNode mn )
188
+ {
189
+ return VisitNodeResult . SkipChildren ;
190
+ }
191
+
192
+ if ( mn . UnquotedNameText == ManifestConstants . Root )
193
+ {
194
+ FoundNode = mn ;
195
+ return VisitNodeResult . Cancel ;
196
+ }
197
+
198
+ return VisitNodeResult . SkipChildren ;
199
+ }
200
+ }
142
201
}
143
202
}
0 commit comments