Skip to content

Add support for mapping blog directory. Set blog directory to 'blog' #14483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/sidebar.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
index: index.md
subsection:
- title: Blog
directory: blog
- title: Usage
directory: docs/usage
subsection:
Expand Down
13 changes: 8 additions & 5 deletions scaladoc/src/dotty/tools/scaladoc/site/SidebarParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ object Sidebar:

private object RawInputTypeRef extends TypeReference[RawInput]

private def toSidebar(r: RawInput)(using CompilerContext): Sidebar = r match
private def toSidebar(r: RawInput)(using CompilerContext): Option[Sidebar] = r match
case RawInput(title, page, index, subsection, dir, hidden) if page.nonEmpty && index.isEmpty && subsection.isEmpty() =>
Sidebar.Page(Option.when(title.nonEmpty)(title), page, hidden)
Some(Sidebar.Page(Option.when(title.nonEmpty)(title), page, hidden))
case RawInput(title, page, index, subsection, dir, hidden) if page.isEmpty && (!subsection.isEmpty() || !index.isEmpty()) =>
Sidebar.Category(Option.when(title.nonEmpty)(title), Option.when(index.nonEmpty)(index), subsection.asScala.map(toSidebar).toList, Option.when(dir.nonEmpty)(dir))
Some(Sidebar.Category(Option.when(title.nonEmpty)(title), Option.when(index.nonEmpty)(index), subsection.asScala.flatMap(toSidebar).toList, Option.when(dir.nonEmpty)(dir)))
case RawInput(title, page, index, subsection, dir, hidden) if page.isEmpty && title == "Blog" && dir.nonEmpty =>
Some(Sidebar.Category(Option.when(title.nonEmpty)(title), None, List.empty, Option.when(dir.nonEmpty)(dir)))
case RawInput(title, page, index, subsection, dir, hidden) =>
report.error(s"Error parsing YAML configuration file.\n$schemaMessage")
Sidebar.Page(None, page, hidden)
None

private def schemaMessage: String =
s"""Static site YAML configuration file should comply with the following description:
Expand All @@ -51,6 +53,7 @@ object Sidebar:
| subsection: # optional - If not provided, pages are loaded from the index directory
| - <subsection> | <page>
| # either index or subsection needs to be present
| # the only exception is subsection representing Blog which needs title == "Blog" and directory
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of relaxing the requirement of providing a directory (and using blog as a default value)?

What happens if someone creates a directory _blog but no entry title: Blog in the sidebar? Conversely, is it possible to create an entry with title: Blog and index: _news/index.html, for instance (to read the blog posts from the _news directory)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There I also have some doubts. It's nice to be able to map blog directory but on the other hand it creates additional complexity to configuration. My idea was to generate blog regardless of presence Blog subsection in YAML (The only condition would be existence of "_blog" directory) but it might be misleading since we use YAML to define which static sites appear. I see two solutions here:

  • We remove changes that I've made here and only change blog directory to "blog"
  • We handle Blog as a special subsection where we can provide output directory for blog (and maybe the input directory?) but I feel like Blog is a special element in static site and it's not trivial to implement this solution without increasing complexity of logic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it’s good to allow the users to define the blog entry in their sidebar so that they can control where it appears in the navigation menu. Yes, the blog is a special entry. Maybe it requires a special AST node as well to make this clearer in the logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should control whether blog should be rendered with yaml config?

Copy link
Contributor Author

@pikinier20 pikinier20 Feb 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for now we should just repair dotty.epfl.ch (implement the second solution) and create an issue about customizing Blog with YAML. Especially since we want to make the default blog directory to 'blog'.

|<page>:
| title: <string> # optional - Default value is file name. Title can be also set using front-matter.
| page: <string>
Expand All @@ -76,7 +79,7 @@ object Sidebar:
identity
)
toSidebar(root) match
case c: Sidebar.Category => c
case Some(c: Sidebar.Category) => c
case _ =>
report.error(s"Root element is not a subsection.\n$schemaMessage")
Sidebar.Category(None, None, List.empty, None)
25 changes: 21 additions & 4 deletions scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,23 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite
val templateFile = loadTemplateFile(file, title)
LoadedTemplate(templateFile, List.empty, pathFromRoot.resolve(file.getName).toFile, hidden)
}
val rootTemplate = LoadedTemplate(rootIndex, yamlRoot.nested.map(c => loadChild(ctx.docsPath)(c)) ++ loadBlog(), rootDest)
val blogDirectory = yamlRoot.nested.collect {
case blog @ Sidebar.Category(optionTitle, optionIndexPath, nested, dir)
if optionTitle.exists(_ == "Blog") => blog
}.headOption.flatMap(_.directory)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, you could use find instead of collect + headOption

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, maybe it could improve maintainability to use partitionWith to compute both blogDirectory and rootChildrenWithoutBlog at the same time?


val rootChildrenWithoutBlog = yamlRoot.nested.filter {
case Sidebar.Category(optionTitle, optionIndexPath, nested, dir)
if optionTitle.exists(_ == "Blog") => false
case _ => true
}

val rootTemplate =
LoadedTemplate(
rootIndex,
rootChildrenWithoutBlog.map(c => loadChild(ctx.docsPath)(c)) ++ loadBlog(blogDirectory),
rootDest
)
val mappings = createMapping(rootTemplate)
StaticSiteRoot(rootTemplate, mappings)
}
Expand All @@ -113,8 +129,9 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite
StaticSiteRoot(withBlog, mappings)
}

def loadBlog(): Option[LoadedTemplate] = {
def loadBlog(directory: Option[String] = None): Option[LoadedTemplate] = {
type Date = (String, String, String)
val destDirectory = directory.getOrElse("_blog")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it weird to use _blog as a default target directory. I would rather use just blog instead, what do you think?

val rootPath = ctx.blogPath
if (!Files.exists(rootPath)) None
else {
Expand All @@ -131,7 +148,7 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite
val indexTemplateOpt = indexPageOpt.map(p => loadTemplateFile(p.toFile))

val indexPage = indexTemplateOpt.getOrElse(emptyTemplate(rootPath.resolve("index.html").toFile, "Blog"))
val indexDest = ctx.docsPath.resolve("_blog").resolve("index.html")
val indexDest = ctx.docsPath.resolve(destDirectory).resolve("index.html")
val regex = raw"(\d*)-(\d*)-(\d*)-(.*)".r
def splitDateName(tf: TemplateFile): (Date, String) = tf.file.getName match
case regex(year, month, day, name) => ((year, month, day), name)
Expand All @@ -150,7 +167,7 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite
.map { postFile =>
val templateFile = loadTemplateFile(postFile)
val ((year, month, day), name) = splitDateName(templateFile)
val destPath = ctx.docsPath.resolve("_blog").resolve(year).resolve(month).resolve(day).resolve(name)
val destPath = ctx.docsPath.resolve(destDirectory).resolve(year).resolve(month).resolve(day).resolve(name)
val date = dateFrom(templateFile, s"$year-$month-$day")
date -> LoadedTemplate(templateFile, List.empty, destPath.toFile)
}.sortBy(_._1).reverse.map(_._2)
Expand Down