From 7b29b08d46a99724574ec90d560db4e43e33e35a Mon Sep 17 00:00:00 2001 From: Luc Bourlier Date: Wed, 18 Apr 2018 09:03:58 +0000 Subject: [PATCH] Adds support for the signature directive to the paradox configuration (copied from the akka-http configuration) --- .../src/main/paradox/stream/reference/++.md | 23 --------- .../main/paradox/stream/reference/actorRef.md | 4 ++ .../stream/reference/actorRefWithAck.md | 2 - .../main/paradox/stream/reference/alsoTo.md | 4 ++ .../main/paradox/stream/reference/apply.md | 4 ++ .../paradox/stream/reference/asInputStream.md | 10 ++++ .../paradox/stream/reference/asJavaStream.md | 12 +++++ .../stream/reference/asOutputStream.md | 10 ++++ .../paradox/stream/reference/asPublisher.md | 6 ++- .../paradox/stream/reference/asSubscriber.md | 6 ++- .../src/main/paradox/stream/reference/ask.md | 4 ++ .../stream/reference/backpressureTimeout.md | 5 ++ .../main/paradox/stream/reference/balance.md | 2 - .../main/paradox/stream/reference/batch.md | 5 ++ .../paradox/stream/reference/batchWeighted.md | 5 ++ .../paradox/stream/reference/broadcast.md | 2 - .../main/paradox/stream/reference/buffer.md | 37 ++++++++++++++ .../stream/reference/bufferBackpressure.md | 24 --------- .../paradox/stream/reference/bufferDrop.md | 29 ----------- .../paradox/stream/reference/bufferFail.md | 24 --------- .../paradox/stream/reference/fromIterator.md | 2 + .../main/paradox/stream/stages-overview.md | 5 +- build.sbt | 1 + project/ParadoxSupport.scala | 51 +++++++++++++++++++ 24 files changed, 165 insertions(+), 112 deletions(-) delete mode 100644 akka-docs/src/main/paradox/stream/reference/++.md create mode 100644 akka-docs/src/main/paradox/stream/reference/buffer.md delete mode 100644 akka-docs/src/main/paradox/stream/reference/bufferBackpressure.md delete mode 100644 akka-docs/src/main/paradox/stream/reference/bufferDrop.md delete mode 100644 akka-docs/src/main/paradox/stream/reference/bufferFail.md diff --git a/akka-docs/src/main/paradox/stream/reference/++.md b/akka-docs/src/main/paradox/stream/reference/++.md deleted file mode 100644 index b075b34d04..0000000000 --- a/akka-docs/src/main/paradox/stream/reference/++.md +++ /dev/null @@ -1,23 +0,0 @@ -# ++ - -++ - -## Signature - -## Description - -Just a shorthand for concat - - -@@@div { .callout } - -**emits** when the current stream has an element available; if the current input completes, it tries the next one - -**backpressures** when downstream backpressures - -**completes** when all upstreams complete - -@@@ - -## Example - diff --git a/akka-docs/src/main/paradox/stream/reference/actorRef.md b/akka-docs/src/main/paradox/stream/reference/actorRef.md index 180a495c94..dbc7d676d7 100644 --- a/akka-docs/src/main/paradox/stream/reference/actorRef.md +++ b/akka-docs/src/main/paradox/stream/reference/actorRef.md @@ -2,8 +2,12 @@ Send the elements from the stream to an `ActorRef`. +@@@ div { .group-scala } ## Signature +@@signature [Sink.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Sink.scala) { #actorRef } +@@@ + ## Description Send the elements from the stream to an `ActorRef`. No backpressure so care must be taken to not overflow the inbox. diff --git a/akka-docs/src/main/paradox/stream/reference/actorRefWithAck.md b/akka-docs/src/main/paradox/stream/reference/actorRefWithAck.md index 3743a56c46..5d6cf56e05 100644 --- a/akka-docs/src/main/paradox/stream/reference/actorRefWithAck.md +++ b/akka-docs/src/main/paradox/stream/reference/actorRefWithAck.md @@ -3,8 +3,6 @@ Send the elements from the stream to an `ActorRef` which must then acknowledge reception after completing a message, to provide back pressure onto the sink. -## Signature - ## Description Send the elements from the stream to an `ActorRef` which must then acknowledge reception after completing a message, diff --git a/akka-docs/src/main/paradox/stream/reference/alsoTo.md b/akka-docs/src/main/paradox/stream/reference/alsoTo.md index dfb12efbfb..5a22f90bcc 100644 --- a/akka-docs/src/main/paradox/stream/reference/alsoTo.md +++ b/akka-docs/src/main/paradox/stream/reference/alsoTo.md @@ -2,8 +2,12 @@ Attaches the given `Sink` to this `Flow`, meaning that elements that pass through this `Flow` will also be sent to the `Sink`. +@@@ div { .group-scala } ## Signature +@@signature [Flow.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala) { #alsoTo } +@@@ + ## Description Attaches the given `Sink` to this `Flow`, meaning that elements that pass through this `Flow` will also be sent to the `Sink`. diff --git a/akka-docs/src/main/paradox/stream/reference/apply.md b/akka-docs/src/main/paradox/stream/reference/apply.md index f260dee7cd..8a95a382b4 100644 --- a/akka-docs/src/main/paradox/stream/reference/apply.md +++ b/akka-docs/src/main/paradox/stream/reference/apply.md @@ -2,8 +2,12 @@ Stream the values of an `immutable. +@@@ div { .group-scala } ## Signature +@@signature [Source.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Source.scala) { #apply } +@@@ + ## Description Stream the values of an `immutable.Seq`. diff --git a/akka-docs/src/main/paradox/stream/reference/asInputStream.md b/akka-docs/src/main/paradox/stream/reference/asInputStream.md index 5fa4698f9d..daf7ca9ed5 100644 --- a/akka-docs/src/main/paradox/stream/reference/asInputStream.md +++ b/akka-docs/src/main/paradox/stream/reference/asInputStream.md @@ -2,14 +2,24 @@ Create a sink which materializes into an `InputStream` that can be read to trigger demand through the sink. +@@@ div { .group-scala } ## Signature +@@signature [StreamConverters.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/StreamConverters.scala) { #asInputStream } +@@@ + ## Description +Create a sink which materializes into an `InputStream` that can be read to trigger demand through the sink. +Bytes emitted through the stream will be available for reading through the `InputStream` +The `InputStream` will be ended when the stream flowing into this `Sink` completes, and the closing the +`InputStream` will cancel the inflow of this `Sink`. @@@div { .callout } +**cancels** when the `InputStream` is closed +**backpressures** when no read is pending on the `InputStream` @@@ ## Example diff --git a/akka-docs/src/main/paradox/stream/reference/asJavaStream.md b/akka-docs/src/main/paradox/stream/reference/asJavaStream.md index 3b9dec9af8..6641965d12 100644 --- a/akka-docs/src/main/paradox/stream/reference/asJavaStream.md +++ b/akka-docs/src/main/paradox/stream/reference/asJavaStream.md @@ -2,14 +2,26 @@ Create a sink which materializes into Java 8 `Stream` that can be run to trigger demand through the sink. +@@@ div { .group-scala } ## Signature +@@signature [StreamConverters.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/StreamConverters.scala) { #asJavaStream } +@@@ + ## Description +Create a sink which materializes into Java 8 `Stream` that can be run to trigger demand through the sink. +Elements emitted through the stream will be available for reading through the Java 8 `Stream`. +The Java 8 `Stream` will be ended when the stream flowing into this `Sink` completes, and closing the Java +`Stream` will cancel the inflow of this `Sink`. Java `Stream` throws exception in case reactive stream failed. + +Be aware that Java `Stream` blocks current thread while waiting on next element from downstream. @@@div { .callout } +**cancels** when the Java Stream is closed +**backpressures** when no read is pending on the Java Stream @@@ ## Example diff --git a/akka-docs/src/main/paradox/stream/reference/asOutputStream.md b/akka-docs/src/main/paradox/stream/reference/asOutputStream.md index a553460bdd..8cf634f879 100644 --- a/akka-docs/src/main/paradox/stream/reference/asOutputStream.md +++ b/akka-docs/src/main/paradox/stream/reference/asOutputStream.md @@ -2,14 +2,24 @@ Create a source that materializes into an `OutputStream`. +@@@ div { .group-scala } ## Signature +@@signature [StreamConverters.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/StreamConverters.scala) { #asOutputStream } +@@@ + ## Description +Create a source that materializes into an `OutputStream`. When bytes are written to the `OutputStream` they +are emitted from the source. +The `OutputStream` will no longer be writable when the `Source` has been canceled from its downstream, and +closing the `OutputStream` will complete the `Source`. @@@div { .callout } +**emits** when bytes are written to the `OutputStream` +**completes** when the `OutputStream` is closed @@@ ## Example diff --git a/akka-docs/src/main/paradox/stream/reference/asPublisher.md b/akka-docs/src/main/paradox/stream/reference/asPublisher.md index 7beeca46b9..04560b3c45 100644 --- a/akka-docs/src/main/paradox/stream/reference/asPublisher.md +++ b/akka-docs/src/main/paradox/stream/reference/asPublisher.md @@ -1,9 +1,13 @@ # asPublisher -Integration with Reactive Streams, materializes into a `org. +Integration with Reactive Streams, materializes into a `org.reactivestreams.Publisher`. +@@@ div { .group-scala } ## Signature +@@signature [Sink.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Sink.scala) { #asPublisher } +@@@ + ## Description diff --git a/akka-docs/src/main/paradox/stream/reference/asSubscriber.md b/akka-docs/src/main/paradox/stream/reference/asSubscriber.md index 326ef41ee1..bb79d1c83c 100644 --- a/akka-docs/src/main/paradox/stream/reference/asSubscriber.md +++ b/akka-docs/src/main/paradox/stream/reference/asSubscriber.md @@ -1,9 +1,13 @@ # asSubscriber -Integration with Reactive Streams, materializes into a `org. +Integration with Reactive Streams, materializes into a `org.reactivestreams.Subscriber`. +@@@ div { .group-scala } ## Signature +@@signature [Source.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Source.scala) { #asSubscriber } +@@@ + ## Description diff --git a/akka-docs/src/main/paradox/stream/reference/ask.md b/akka-docs/src/main/paradox/stream/reference/ask.md index 76378f30ee..50dd328394 100644 --- a/akka-docs/src/main/paradox/stream/reference/ask.md +++ b/akka-docs/src/main/paradox/stream/reference/ask.md @@ -2,8 +2,12 @@ Use the `ask` pattern to send a request-reply message to the target `ref` actor. +@@@ div { .group-scala } ## Signature +@@signature [Flow.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala) { #ask } +@@@ + ## Description Use the `ask` pattern to send a request-reply message to the target `ref` actor. diff --git a/akka-docs/src/main/paradox/stream/reference/backpressureTimeout.md b/akka-docs/src/main/paradox/stream/reference/backpressureTimeout.md index 2dc196e79a..8588f55bb5 100644 --- a/akka-docs/src/main/paradox/stream/reference/backpressureTimeout.md +++ b/akka-docs/src/main/paradox/stream/reference/backpressureTimeout.md @@ -3,8 +3,13 @@ If the time between the emission of an element and the following downstream demand exceeds the provided timeout, the stream is failed with a `TimeoutException`. +@@@ div { .group-scala } ## Signature +@@signature [Flow.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala) { #backpressureTimeout } +@@@ + + ## Description If the time between the emission of an element and the following downstream demand exceeds the provided timeout, diff --git a/akka-docs/src/main/paradox/stream/reference/balance.md b/akka-docs/src/main/paradox/stream/reference/balance.md index b66fcf309f..04af7b8855 100644 --- a/akka-docs/src/main/paradox/stream/reference/balance.md +++ b/akka-docs/src/main/paradox/stream/reference/balance.md @@ -2,8 +2,6 @@ Fan-out the stream to several streams. -## Signature - ## Description Fan-out the stream to several streams. Each upstream element is emitted to the first available downstream consumer. diff --git a/akka-docs/src/main/paradox/stream/reference/batch.md b/akka-docs/src/main/paradox/stream/reference/batch.md index 29eb5ebdff..9bab795b5e 100644 --- a/akka-docs/src/main/paradox/stream/reference/batch.md +++ b/akka-docs/src/main/paradox/stream/reference/batch.md @@ -3,8 +3,13 @@ Allow for a slower downstream by passing incoming elements and a summary into an aggregate function as long as there is backpressure and a maximum number of batched elements is not yet reached. +@@@ div { .group-scala } ## Signature +@@signature [Flow.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala) { #batch } +@@@ + + ## Description Allow for a slower downstream by passing incoming elements and a summary into an aggregate function as long as there diff --git a/akka-docs/src/main/paradox/stream/reference/batchWeighted.md b/akka-docs/src/main/paradox/stream/reference/batchWeighted.md index d1bfd5d560..06e735253b 100644 --- a/akka-docs/src/main/paradox/stream/reference/batchWeighted.md +++ b/akka-docs/src/main/paradox/stream/reference/batchWeighted.md @@ -3,8 +3,13 @@ Allow for a slower downstream by passing incoming elements and a summary into an aggregate function as long as there is backpressure and a maximum weight batched elements is not yet reached. +@@@ div { .group-scala } ## Signature +@@signature [Flow.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala) { #batchWeighted } +@@@ + + ## Description Allow for a slower downstream by passing incoming elements and a summary into an aggregate function as long as there diff --git a/akka-docs/src/main/paradox/stream/reference/broadcast.md b/akka-docs/src/main/paradox/stream/reference/broadcast.md index 85a157ef47..8c2dd56727 100644 --- a/akka-docs/src/main/paradox/stream/reference/broadcast.md +++ b/akka-docs/src/main/paradox/stream/reference/broadcast.md @@ -2,8 +2,6 @@ Emit each incoming element each of `n` outputs. -## Signature - ## Description Emit each incoming element each of `n` outputs. diff --git a/akka-docs/src/main/paradox/stream/reference/buffer.md b/akka-docs/src/main/paradox/stream/reference/buffer.md new file mode 100644 index 0000000000..5c04939fa8 --- /dev/null +++ b/akka-docs/src/main/paradox/stream/reference/buffer.md @@ -0,0 +1,37 @@ +# buffer + +Allow for a temporarily faster upstream events by buffering `size` elements. + +@@@ div { .group-scala } +## Signature + +@@signature [Flow.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Flow.scala) { #buffer } +@@@ + + +## Description + +Allow for a temporarily faster upstream events by buffering `size` elements. When the buffer is full, a new element is +handled according to the specified `OverflowStrategy`: + + * `backpressure` backpressue is applied upstream + * `dropHead` drops the oldest element in the buffer to make space for the new element + * `dropTail` drops the youngest element in the buffer to make space for the new element + * `dropBuffer` drops the entire buffer and buffers the new element + * `dropNew` drops the new element + * `fail` fails the flow with a `BufferOverflowException` + + + +@@@div { .callout } + +**emits** when downstream stops backpressuring and there is a pending element in the buffer + +**backpressures** when `OverflowStrategy` is `backpressue` and buffer is full + +**completes** when upstream completes and buffered elements has been drained, or when `OverflowStrategy` is `fail`, the buffer is full and a new element arrives + +@@@ + +## Example + diff --git a/akka-docs/src/main/paradox/stream/reference/bufferBackpressure.md b/akka-docs/src/main/paradox/stream/reference/bufferBackpressure.md deleted file mode 100644 index f005ffba41..0000000000 --- a/akka-docs/src/main/paradox/stream/reference/bufferBackpressure.md +++ /dev/null @@ -1,24 +0,0 @@ -# buffer (Backpressure) - -Allow for a temporarily faster upstream events by buffering `size` elements. - -## Signature - -## Description - -Allow for a temporarily faster upstream events by buffering `size` elements. When the buffer is full backpressure -is applied. - - -@@@div { .callout } - -**emits** when downstream stops backpressuring and there is a pending element in the buffer - -**backpressures** when buffer is full - -**completes** when upstream completes and buffered elements has been drained - -@@@ - -## Example - diff --git a/akka-docs/src/main/paradox/stream/reference/bufferDrop.md b/akka-docs/src/main/paradox/stream/reference/bufferDrop.md deleted file mode 100644 index b8cbba484b..0000000000 --- a/akka-docs/src/main/paradox/stream/reference/bufferDrop.md +++ /dev/null @@ -1,29 +0,0 @@ -# buffer (Drop) - -Allow for a temporarily faster upstream events by buffering `size` elements. - -## Signature - -## Description - -Allow for a temporarily faster upstream events by buffering `size` elements. When the buffer is full elements are -dropped according to the specified `OverflowStrategy`: - - * `dropHead` drops the oldest element in the buffer to make space for the new element - * `dropTail` drops the youngest element in the buffer to make space for the new element - * `dropBuffer` drops the entire buffer and buffers the new element - * `dropNew` drops the new element - - -@@@div { .callout } - -**emits** when downstream stops backpressuring and there is a pending element in the buffer - -**backpressures** never (when dropping cannot keep up with incoming elements) - -**completes** upstream completes and buffered elements has been drained - -@@@ - -## Example - diff --git a/akka-docs/src/main/paradox/stream/reference/bufferFail.md b/akka-docs/src/main/paradox/stream/reference/bufferFail.md deleted file mode 100644 index 9f309daec3..0000000000 --- a/akka-docs/src/main/paradox/stream/reference/bufferFail.md +++ /dev/null @@ -1,24 +0,0 @@ -# buffer (Fail) - -Allow for a temporarily faster upstream events by buffering `size` elements. - -## Signature - -## Description - -Allow for a temporarily faster upstream events by buffering `size` elements. When the buffer is full the stage fails -the flow with a `BufferOverflowException`. - - -@@@div { .callout } - -**emits** when downstream stops backpressuring and there is a pending element in the buffer - -**backpressures** never, fails the stream instead of backpressuring when buffer is full - -**completes** when upstream completes and buffered elements has been drained - -@@@ - -## Example - diff --git a/akka-docs/src/main/paradox/stream/reference/fromIterator.md b/akka-docs/src/main/paradox/stream/reference/fromIterator.md index 1cf60bc5e4..8ab8cf938b 100644 --- a/akka-docs/src/main/paradox/stream/reference/fromIterator.md +++ b/akka-docs/src/main/paradox/stream/reference/fromIterator.md @@ -4,6 +4,8 @@ Stream the values from an `Iterator`, requesting the next value when there is de ## Signature +@@signature [BasicDirectives.scala]($akka$/akka-stream/src/main/scala/akka/stream/scaladsl/Source.scala) { #fromIterator } + ## Description Stream the values from an `Iterator`, requesting the next value when there is demand. The iterator will be created anew diff --git a/akka-docs/src/main/paradox/stream/stages-overview.md b/akka-docs/src/main/paradox/stream/stages-overview.md index 8effc11954..5f86a33cdc 100644 --- a/akka-docs/src/main/paradox/stream/stages-overview.md +++ b/akka-docs/src/main/paradox/stream/stages-overview.md @@ -2093,9 +2093,7 @@ event, and may therefore affect performance. * [batchWeighted](reference/batchWeighted.md) * [extrapolate](reference/extrapolate.md) * [expand](reference/expand.md) -* [buffer (Backpressure)](reference/bufferBackpressure.md) -* [buffer (Drop)](reference/bufferDrop.md) -* [buffer (Fail)](reference/bufferFail.md) +* [buffer](reference/buffer.md) * [prefixAndTail](reference/prefixAndTail.md) * [groupBy](reference/groupBy.md) * [splitWhen](reference/splitWhen.md) @@ -2116,7 +2114,6 @@ event, and may therefore affect performance. * [zipWith](reference/zipWith.md) * [zipWithIndex](reference/zipWithIndex.md) * [concat](reference/concat.md) -* [++](reference/++.md) * [prepend](reference/prepend.md) * [orElse](reference/orElse.md) * [interleave](reference/interleave.md) diff --git a/build.sbt b/build.sbt index ef2baf4909..7d270f9e77 100644 --- a/build.sbt +++ b/build.sbt @@ -228,6 +228,7 @@ lazy val docs = akkaModule("akka-docs") "google.analytics.domain.name" -> "akka.io", "snip.code.base_dir" -> (sourceDirectory in Test).value.getAbsolutePath, "snip.akka.base_dir" -> (baseDirectory in ThisBuild).value.getAbsolutePath, + "signature.akka.base_dir" -> (baseDirectory in ThisBuild).value.getAbsolutePath, "fiddle.code.base_dir" -> (sourceDirectory in Test).value.getAbsolutePath ), paradoxGroups := Map("Language" -> Seq("Scala", "Java")), diff --git a/project/ParadoxSupport.scala b/project/ParadoxSupport.scala index f23ece217f..0f3739ab41 100644 --- a/project/ParadoxSupport.scala +++ b/project/ParadoxSupport.scala @@ -4,6 +4,8 @@ package akka +import java.io.{File, FileNotFoundException} + import _root_.io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import com.lightbend.paradox.markdown._ import com.lightbend.paradox.sbt.ParadoxPlugin.autoImport._ @@ -12,16 +14,22 @@ import org.pegdown.ast._ import sbt.Keys._ import sbt._ +import scala.io.{Codec, Source} + import scala.collection.JavaConverters._ object ParadoxSupport { val paradoxWithCustomDirectives = Seq( paradoxDirectives ++= Def.taskDyn { + val log = streams.value.log val classpath = (fullClasspath in Compile).value.files.map(_.toURI.toURL).toArray val classloader = new java.net.URLClassLoader(classpath, this.getClass().getClassLoader()) lazy val scanner = new FastClasspathScanner("akka").addClassLoader(classloader).scan() val allClasses = scanner.getNamesOfAllClasses.asScala.toVector Def.task { Seq( + { context: Writer.Context ⇒ + new SignatureDirective(context.location.tree.label, context.properties, msg ⇒ log.warn(msg)) + }, { _: Writer.Context ⇒ new UnidocDirective(allClasses) } )} }.value @@ -84,4 +92,47 @@ object ParadoxSupport { } } } + + class SignatureDirective(page: Page, variables: Map[String, String], logWarn: String => Unit) extends LeafBlockDirective("signature") { + def render(node: DirectiveNode, visitor: Visitor, printer: Printer): Unit = + try { + val labels = node.attributes.values("identifier").asScala.map(_.toLowerCase()) + val source = node.source match { + case direct: DirectiveNode.Source.Direct => direct.value + case _ => sys.error("Source references are not supported") + } + val file = + if (source startsWith "$") { + val baseKey = source.drop(1).takeWhile(_ != '$') + val base = new File(PropertyUrl(s"signature.$baseKey.base_dir", variables.get).base.trim) + val effectiveBase = if (base.isAbsolute) base else new File(page.file.getParentFile, base.toString) + new File(effectiveBase, source.drop(baseKey.length + 2)) + } else new File(page.file.getParentFile, source) + + val Signature = """\s*((def|val|type) (\w+)(?=[:(\[]).*)(\s+\=.*)""".r // stupid approximation to match a signature + //println(s"Looking for signature regex '$Signature'") + val text = + Source.fromFile(file)(Codec.UTF8).getLines.collect { + case line@Signature(signature, kind, l, definition) if labels contains l.toLowerCase() => + //println(s"Found label '$l' with sig '$full' in line $line") + if (kind == "type") signature + definition + else signature + }.mkString("\n") + + if (text.trim.isEmpty) { + logWarn( + s"Did not find any signatures with one of those names [${labels.mkString(", ")}] in ${node.source} " + + s"(was referenced from [${page.path}])") + + new HtmlBlockNode(s"""
[Broken signature inclusion [${labels.mkString(", ")}] to [${node.source}]
""").accept(visitor) + } else { + val lang = Option(node.attributes.value("type")).getOrElse(Snippet.language(file)) + new VerbatimNode(text, lang).accept(visitor) + } + } catch { + case e: FileNotFoundException => + throw new SnipDirective.LinkException(s"Unknown snippet [${e.getMessage}] referenced from [${page.path}]") + } + } + }