Kaynağa Gözat

LibManual: Associate SubsectionNode with similarly named markdown file

For a subsection named `Foo` with a markdown file `Foo.md` in the same
directory, the document `Foo.md` will be returned when the subsection is
selected.

This prevents PageNodes and SubsectionNodes from sharing the same
name, which fixes an issue in Help where subsections could not be
navigated.
Tim Ledbetter 2 yıl önce
ebeveyn
işleme
10700ca4c1

+ 26 - 13
Userland/Libraries/LibManual/SectionNode.cpp

@@ -8,6 +8,7 @@
 #include "PageNode.h"
 #include "Path.h"
 #include "SubsectionNode.h"
+#include <AK/HashTable.h>
 #include <AK/LexicalPath.h>
 #include <AK/QuickSort.h>
 #include <LibCore/DirIterator.h>
@@ -43,7 +44,16 @@ ErrorOr<void> SectionNode::reify_if_needed() const
     m_reified = true;
 
     auto own_path = TRY(path());
-    Core::DirIterator dir_iter { own_path.to_deprecated_string(), Core::DirIterator::Flags::SkipDots };
+    Core::DirIterator dir_iterator { own_path.to_deprecated_string(), Core::DirIterator::Flags::SkipDots };
+    Vector<DeprecatedString> directories;
+    HashTable<DeprecatedString> files;
+    while (dir_iterator.has_next()) {
+        auto entry = dir_iterator.next();
+        if (entry->type == Core::DirectoryEntry::Type::Directory)
+            TRY(directories.try_append(entry->name));
+        else if (entry->type == Core::DirectoryEntry::Type::File && entry->name.ends_with(".md"sv, CaseSensitivity::CaseInsensitive))
+            TRY(files.try_set(entry->name));
+    }
 
     struct Child {
         NonnullRefPtr<Node const> node;
@@ -51,18 +61,21 @@ ErrorOr<void> SectionNode::reify_if_needed() const
     };
     Vector<Child> children;
 
-    while (dir_iter.has_next()) {
-        LexicalPath lexical_path(dir_iter.next_path());
-        if (lexical_path.extension() != "md") {
-            if (FileSystem::is_directory(LexicalPath::absolute_path(own_path.to_deprecated_string(), lexical_path.string()))) {
-                dbgln("Found subsection {}", lexical_path);
-                children.append({ .node = TRY(try_make_ref_counted<SubsectionNode>(*this, lexical_path.title())),
-                    .name_for_sorting = TRY(String::from_utf8(lexical_path.title())) });
-            }
-        } else {
-            children.append({ .node = TRY(try_make_ref_counted<PageNode>(*this, TRY(String::from_utf8(lexical_path.title())))),
-                .name_for_sorting = TRY(String::from_utf8(lexical_path.title())) });
-        }
+    for (auto const& directory : directories) {
+        LexicalPath lexical_path(directory);
+        RefPtr<PageNode> associated_page;
+        auto matching_page_name = DeprecatedString::formatted("{}.md", directory);
+        if (files.remove(matching_page_name))
+            associated_page = TRY(try_make_ref_counted<PageNode>(*this, TRY(String::from_utf8(lexical_path.title()))));
+
+        TRY(children.try_append({ .node = TRY(try_make_ref_counted<SubsectionNode>(*this, lexical_path.title(), associated_page)),
+            .name_for_sorting = TRY(String::from_utf8(lexical_path.title())) }));
+    }
+
+    for (auto const& file : files) {
+        LexicalPath lexical_path(file);
+        children.append({ .node = TRY(try_make_ref_counted<PageNode>(*this, TRY(String::from_utf8(lexical_path.title())))),
+            .name_for_sorting = TRY(String::from_utf8(lexical_path.title())) });
     }
 
     quick_sort(children, [](auto const& a, auto const& b) { return a.name_for_sorting < b.name_for_sorting; });

+ 3 - 18
Userland/Libraries/LibManual/SubsectionNode.cpp

@@ -10,31 +10,16 @@
 
 namespace Manual {
 
-SubsectionNode::SubsectionNode(NonnullRefPtr<SectionNode const> parent, StringView name)
+SubsectionNode::SubsectionNode(NonnullRefPtr<SectionNode const> parent, StringView name, RefPtr<PageNode> page)
     : SectionNode(name, name)
     , m_parent(move(parent))
+    , m_page(move(page))
 {
 }
 
 Node const* SubsectionNode::parent() const { return m_parent; }
 
-PageNode const* SubsectionNode::document() const
-{
-    auto maybe_siblings = parent()->children();
-    if (maybe_siblings.is_error())
-        return nullptr;
-    auto siblings = maybe_siblings.release_value();
-    for (auto const& sibling : siblings) {
-        if (&*sibling == this)
-            continue;
-        auto sibling_name = sibling->name();
-        if (sibling_name.is_error())
-            continue;
-        if (sibling_name.value() == m_name && is<PageNode>(*sibling))
-            return static_cast<PageNode const*>(&*sibling);
-    }
-    return nullptr;
-}
+PageNode const* SubsectionNode::document() const { return m_page; }
 
 ErrorOr<String> SubsectionNode::name() const { return m_name; }
 

+ 5 - 1
Userland/Libraries/LibManual/SubsectionNode.h

@@ -6,6 +6,7 @@
 
 #pragma once
 
+#include <LibManual/PageNode.h>
 #include <LibManual/SectionNode.h>
 
 namespace Manual {
@@ -13,7 +14,7 @@ namespace Manual {
 // A non-toplevel (i.e. not numbered) manual section.
 class SubsectionNode : public SectionNode {
 public:
-    SubsectionNode(NonnullRefPtr<SectionNode const> parent, StringView name);
+    SubsectionNode(NonnullRefPtr<SectionNode const> parent, StringView name, RefPtr<PageNode> page = {});
     virtual ~SubsectionNode() = default;
 
     virtual Node const* parent() const override;
@@ -23,6 +24,9 @@ public:
 
 protected:
     NonnullRefPtr<SectionNode const> m_parent;
+
+private:
+    RefPtr<PageNode> m_page;
 };
 
 }