22// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33// See the LICENSE file in the project root for more information
44
5+ using System . Collections . Immutable ;
56using System . Diagnostics ;
67using Elastic . Documentation . Configuration . Assembler ;
78using Elastic . Documentation . Configuration . DocSet ;
@@ -18,7 +19,8 @@ public class SiteNavigation : IRootNavigationItem<IDocumentationFile, INavigatio
1819{
1920 private readonly string ? _sitePrefix ;
2021
21- public SiteNavigation ( SiteNavigationFile siteNavigationFile ,
22+ public SiteNavigation (
23+ SiteNavigationFile siteNavigationFile ,
2224 IDocumentationContext context ,
2325 IReadOnlyCollection < IDocumentationSetNavigation > documentationSetNavigations ,
2426 string ? sitePrefix
@@ -35,6 +37,9 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
3537 Id = ShortId . Create ( "site" ) ;
3638 IsUsingNavigationDropdown = false ;
3739 Phantoms = siteNavigationFile . Phantoms ;
40+ DeclaredPhantoms = [ .. siteNavigationFile . Phantoms . Select ( p => new Uri ( p . Source ) ) ] ;
41+ DeclaredTableOfContents = SiteNavigationFile . GetAllDeclaredSources ( siteNavigationFile ) ;
42+
3843 _nodes = [ ] ;
3944 foreach ( var setNavigation in documentationSetNavigations )
4045 {
@@ -73,13 +78,21 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
7378 Index = this . FindIndex < IDocumentationFile > ( new NotFoundModel ( "/index.md" ) ) ;
7479 }
7580
76- private readonly Dictionary < Uri , INodeNavigationItem < IDocumentationFile , INavigationItem > > _nodes ;
77- public IReadOnlyDictionary < Uri , INodeNavigationItem < IDocumentationFile , INavigationItem > > Nodes => _nodes ;
81+ public HashSet < Uri > DeclaredPhantoms { get ; }
82+
83+ /// <summary> All the table of contents explicitly declared in the navigation</summary>
84+ public ImmutableHashSet < Uri > DeclaredTableOfContents { get ; set ; }
85+
86+ private readonly Dictionary < Uri , IRootNavigationItem < IDocumentationFile , INavigationItem > > _nodes ;
87+ public IReadOnlyDictionary < Uri , IRootNavigationItem < IDocumentationFile , INavigationItem > > Nodes => _nodes ;
7888
7989 private HashSet < Uri > UnseenNodes { get ; }
8090
8191 public IReadOnlyCollection < PhantomRegistration > Phantoms { get ; }
8292
93+ /// <inheritdoc />
94+ public Uri Identifier { get ; } = new Uri ( "site://" ) ;
95+
8396 //TODO Obsolete?
8497 public IReadOnlyCollection < INodeNavigationItem < INavigationModel , INavigationItem > > TopLevelItems =>
8598 NavigationItems . OfType < INodeNavigationItem < INavigationModel , INavigationItem > > ( ) . ToList ( ) ;
@@ -120,6 +133,9 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
120133 /// <inheritdoc />
121134 public IReadOnlyCollection < INavigationItem > NavigationItems { get ; }
122135
136+ void IAssignableChildrenNavigation . SetNavigationItems ( IReadOnlyCollection < INavigationItem > navigationItems ) =>
137+ throw new NotSupportedException ( "SetNavigationItems is not supported on SiteNavigation" ) ;
138+
123139 /// <summary>
124140 /// Normalizes the site prefix to ensure it has a leading slash and no trailing slash.
125141 /// Returns null for null or empty/whitespace input.
@@ -156,20 +172,20 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
156172 // we allow not setting path prefixes for toc references from the narrative repository
157173 if ( tocRef . Source . Scheme != NarrativeRepository . RepositoryName )
158174 context . EmitError ( context . ConfigurationPath , $ "path_prefix is required for TOC reference: { tocRef . Source } ") ;
159-
160- if ( ! string . IsNullOrEmpty ( tocRef . Source . Host ) )
161- pathPrefix += $ "/{ tocRef . Source . Host } ";
162- if ( ! string . IsNullOrEmpty ( tocRef . Source . AbsolutePath ) && tocRef . Source . AbsolutePath != "/" )
163- pathPrefix += $ "/{ tocRef . Source . AbsolutePath } ";
175+ else
176+ {
177+ if ( ! string . IsNullOrEmpty ( tocRef . Source . Host ) )
178+ pathPrefix += $ "/{ tocRef . Source . Host } ";
179+ if ( ! string . IsNullOrEmpty ( tocRef . Source . AbsolutePath ) && tocRef . Source . AbsolutePath != "/" )
180+ pathPrefix += $ "/{ tocRef . Source . AbsolutePath } ";
181+ }
164182 }
165183
166184 // Normalize pathPrefix to remove leading/trailing slashes for a consistent combination
167185 pathPrefix = pathPrefix . Trim ( '/' ) ;
168186
169187 // Combine with site prefix if present, otherwise ensure leading slash
170- pathPrefix = ! string . IsNullOrWhiteSpace ( _sitePrefix )
171- ? $ "{ _sitePrefix } /{ pathPrefix } "
172- : "/" + pathPrefix ;
188+ pathPrefix = ! string . IsNullOrWhiteSpace ( _sitePrefix ) ? $ "{ _sitePrefix } /{ pathPrefix } " : "/" + pathPrefix ;
173189
174190 // Look up the node in the collected nodes
175191 if ( ! _nodes . TryGetValue ( tocRef . Source , out var node ) )
@@ -183,25 +199,33 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
183199 return null ;
184200 }
185201
202+ if ( node . Identifier == new Uri ( "docs-content://reference/glossary" ) )
203+ {
204+ }
205+
206+ root ??= node ;
207+
186208 _ = UnseenNodes . Remove ( tocRef . Source ) ;
187209 // Set the navigation index
210+ node . Parent = parent ;
188211 node . NavigationIndex = index ;
189- homeAccessor . HomeProvider = new NavigationHomeProvider ( pathPrefix , root ?? homeAccessor . HomeProvider . NavigationRoot ) ;
212+ homeAccessor . HomeProvider = new NavigationHomeProvider ( pathPrefix , root ) ;
190213
191- var wrapped = new SiteTableOfContentsNavigation < IDocumentationFile > ( node , homeAccessor . HomeProvider , parent , root ) ;
192- parent = wrapped ;
193- root ??= wrapped . NavigationRoot ;
214+ //var wrapped = new SiteTableOfContentsNavigation<IDocumentationFile>(node, homeAccessor.HomeProvider, parent, root);
215+ //parent = wrapped;
194216
195217 var children = new List < INavigationItem > ( ) ;
196218 var nodeChildren = node . NavigationItems ;
197219 foreach ( var nodeChild in nodeChildren )
198220 {
199- nodeChild . Parent = parent ;
200- if ( nodeChild is IRootNavigationItem < IDocumentationFile , INavigationItem > )
221+ nodeChild . Parent = node ;
222+ if ( nodeChild is INavigationHomeAccessor childAccessor )
223+ childAccessor . HomeProvider = homeAccessor . HomeProvider ;
224+
225+ if ( nodeChild is IRootNavigationItem < INavigationModel , INavigationItem > )
201226 continue ;
202227 children . Add ( nodeChild ) ;
203228 }
204- // Recursively create child navigation items if children are specified
205229 if ( tocRef . Children . Count > 0 )
206230 {
207231 var childIndex = 0 ;
@@ -218,10 +242,27 @@ public SiteNavigation(SiteNavigationFile siteNavigationFile,
218242 children . Add ( childItem ) ;
219243 }
220244 }
245+ foreach ( var nodeChild in nodeChildren )
246+ {
247+ if ( nodeChild is not IRootNavigationItem < INavigationModel , INavigationItem > rootChild )
248+ continue ;
249+ if ( DeclaredTableOfContents . Contains ( rootChild . Identifier ) || DeclaredPhantoms . Contains ( rootChild . Identifier ) )
250+ continue ;
251+
252+ context . EmitWarning ( context . ConfigurationPath , $ "Navigation does not explicitly declare: { rootChild . Identifier } ") ;
253+ }
221254
222- wrapped . NavigationItems = children ;
223- // Always return a wrapper to ensure path_prefix is the URL (not path_prefix + node's URL)
224- return wrapped ;
255+ switch ( node )
256+ {
257+ case SiteNavigation :
258+ break ;
259+ case IAssignableChildrenNavigation documentationSetNavigation :
260+ documentationSetNavigation . SetNavigationItems ( children ) ;
261+ break ;
262+ default :
263+ throw new Exception ( $ "node is not a known type: { node . GetType ( ) . Name } ") ;
264+ }
265+ return node ;
225266 }
226267}
227268
0 commit comments