Transformation results in error if document contains multiple identically-named link targets #237
Replies: 7 comments
-
Hello Andrew and welcome! Unfortunately what you run into is really a feature not a bug. :-) Laika allows to cross-link to any sections which is based on ids which are derived from the header text and duplicate ids would be illegal in all supported output formats. Manual assignments of ids right in markup is not supported yet, and would require some design thought to support it consistently between different markup languages. There are a few workarounds I can see right now. One is to not use header syntax for the level where there is repetition, e.g. simple strong markup:
or if that is not sufficient, assigning styles:
which is a Laika directive that will give you a header3 class attribute in the rendered HTML (assuming HTML is your target format). There is also a workaround for actual manual assignments of ids via an extension route, as shown in the next comment. |
Beta Was this translation helpful? Give feedback.
-
Sorry, I actually forgot to mention the most relevant extension hook for this feature: the slug builder which generates the ids based on the header text and which is pluggable. This might be the most elegant solution for you. object MyBundle extends ExtensionBundle {
val description: String = "Assigning custom ids"
override def slugBuilder: Option[String => String] = Some(headerText => SlugBuilder.default(headerText))
} The above would be equivalent to the default. I'd recommend to piggy-back on the default builder to ensure you have legal ids and then add custom logic on top. And then you can specify this extenstion bundle in the usual way: Transformer
.from(Markdown)
.to(HTML)
.using(MyBundle)
.build |
Beta Was this translation helpful? Give feedback.
-
Thanks for the explanation @jenshalm! The |
Beta Was this translation helpful? Give feedback.
-
As it turns out, I don't think the bundle will work. In case you'd like to reproduce, this is what I've been using:
|
Beta Was this translation helpful? Give feedback.
-
Apologies, yes, I forgot to mention that Which means that there are either the other alternatives I showed in my first response. Or, if that is not satisfactory, you can add preliminary support for manually assigning ids in markup, similar to what Laika might support in a future release: # Comparison
## Option 1
### {pros-1} Pros
### {cons-1} Cons
## Option 2
### {pros-2} Pros
### {cons-2} Cons To support this manual assignment (the ids in curly braces), surprisingly little effort is required: object Temp extends IOApp {
implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
def run(args: List[String]): IO[ExitCode] = {
Blocker[IO].use { blocker =>
val directoryTransformer = Transformer
.from(Markdown)
.to(HTML)
.using(GitHubFlavor, ManualIds)
.io(blocker)
.parallel[IO]
.mapDocuments(ManualIds.removeIds)
.build
directoryTransformer
.fromDirectory("input")
.toDirectory("output")
.transform
}.as(ExitCode.Success)
}
}
object ManualIds extends ExtensionBundle {
val description = "Processing manual ids from header text"
def hasId(txt: String): Boolean = txt.startsWith("{") && txt.contains("}")
def extractId (txt: String): String = txt.drop(1).split("}", 2).head
def removeId (txt: String): String = txt.split("}", 2).last.trim
def removeIds (doc: Document): Document = doc.rewrite(RewriteRules.forBlocks {
case h: Header => h.content match {
case Text(txt, _) +: rest if hasId(txt) =>
Replace(h.copy(content = Text(removeId(txt)) +: rest))
case _ =>
Retain
}
})
override def slugBuilder: Option[String => String] = Some(headerText => {
val input = if (hasId(headerText)) extractId(headerText) else headerText
SlugBuilder.default(input)
})
} The slugBuilder extracts the id, and the new line I tried this with current master which you shouldn't use as it's too much in flux right now for adding theme support, but I'm quite optimistic it'll work with the last release, too. |
Beta Was this translation helpful? Give feedback.
-
Hello @AndrewSelvia, this ticket has been open for a while, can we close it now? What you described is expected behaviour, and everything that could be done to improve the situation would be best kept in a new feature ticket. One option would be to support manual ids out of the box, roughly as shown in my previous example, another option would be to make the number of levels with automatic id generation configurable, e.g. for only the first two levels, so that name clashes in level-3 headers don't matter. If you'd like to see any of those, feel free to create corresponding tickets. |
Beta Was this translation helpful? Give feedback.
-
Sorry for leaving it hanging! I've been slowly rewriting articles to avoid duplicate header names. I don't want to lose the auto-generated IDs; that feature is really useful. I intend to utilize the second approach you described (i.e. |
Beta Was this translation helpful? Give feedback.
-
Here's the error I'm trying to resolve:
A document which could reproduce this error (id: 'pros' or 'cons') is:
I'm new to Laika (liking it so far, great documentation 😄). I tried separating parsing and rendering so I could alter the Document AST to name each link independently, but it seems the parsing itself is failing so I never get the Document. Is there something obvious I'm missing?
Beta Was this translation helpful? Give feedback.
All reactions