Skip to content

Commit 715acd3

Browse files
committed
Restore TOC in the root coroutines-guide.md file with links to individual files
* Knit supports new TOC_REF directive for that. * Reknit docs (also fixed out-of-date sample file).
1 parent e5bf9ae commit 715acd3

File tree

3 files changed

+145
-23
lines changed

3 files changed

+145
-23
lines changed

core/kotlinx-coroutines-core/test/guide/example-basic-02b.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ package kotlinx.coroutines.experimental.guide.basic02b
77

88
import kotlinx.coroutines.experimental.*
99

10-
fun main(args: Array<String>) = runBlocking { // start main coroutine
10+
fun main(args: Array<String>) = runBlocking<Unit> { // start main coroutine
1111
GlobalScope.launch { // launch new coroutine in background and continue
1212
delay(1000L)
1313
println("World!")

coroutines-guide.md

+80
Original file line numberDiff line numberDiff line change
@@ -1 +1,81 @@
11
The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.md) and split up into smaller documents.
2+
3+
## Table of contents
4+
5+
<!--- TOC_REF docs/basics.md -->
6+
* <a name='coroutine-basics'></a>[Coroutine basics](docs/basics.md#coroutine-basics)
7+
* <a name='your-first-coroutine'></a>[Your first coroutine](docs/basics.md#your-first-coroutine)
8+
* <a name='bridging-blocking-and-non-blocking-worlds'></a>[Bridging blocking and non-blocking worlds](docs/basics.md#bridging-blocking-and-non-blocking-worlds)
9+
* <a name='waiting-for-a-job'></a>[Waiting for a job](docs/basics.md#waiting-for-a-job)
10+
* <a name='structured-concurrency'></a>[Structured concurrency](docs/basics.md#structured-concurrency)
11+
* <a name='scope-builder'></a>[Scope builder](docs/basics.md#scope-builder)
12+
* <a name='extract-function-refactoring'></a>[Extract function refactoring](docs/basics.md#extract-function-refactoring)
13+
* <a name='coroutines-are-light-weight'></a>[Coroutines ARE light-weight](docs/basics.md#coroutines-are-light-weight)
14+
* <a name='global-coroutines-are-like-daemon-threads'></a>[Global coroutines are like daemon threads](docs/basics.md#global-coroutines-are-like-daemon-threads)
15+
<!--- TOC_REF docs/cancellation-and-timeouts.md -->
16+
* <a name='cancellation-and-timeouts'></a>[Cancellation and timeouts](docs/cancellation-and-timeouts.md#cancellation-and-timeouts)
17+
* <a name='cancelling-coroutine-execution'></a>[Cancelling coroutine execution](docs/cancellation-and-timeouts.md#cancelling-coroutine-execution)
18+
* <a name='cancellation-is-cooperative'></a>[Cancellation is cooperative](docs/cancellation-and-timeouts.md#cancellation-is-cooperative)
19+
* <a name='making-computation-code-cancellable'></a>[Making computation code cancellable](docs/cancellation-and-timeouts.md#making-computation-code-cancellable)
20+
* <a name='closing-resources-with-finally'></a>[Closing resources with finally](docs/cancellation-and-timeouts.md#closing-resources-with-finally)
21+
* <a name='run-non-cancellable-block'></a>[Run non-cancellable block](docs/cancellation-and-timeouts.md#run-non-cancellable-block)
22+
* <a name='timeout'></a>[Timeout](docs/cancellation-and-timeouts.md#timeout)
23+
<!--- TOC_REF docs/composing-suspending-functions.md -->
24+
* <a name='composing-suspending-functions'></a>[Composing suspending functions](docs/composing-suspending-functions.md#composing-suspending-functions)
25+
* <a name='sequential-by-default'></a>[Sequential by default](docs/composing-suspending-functions.md#sequential-by-default)
26+
* <a name='concurrent-using-async'></a>[Concurrent using async](docs/composing-suspending-functions.md#concurrent-using-async)
27+
* <a name='lazily-started-async'></a>[Lazily started async](docs/composing-suspending-functions.md#lazily-started-async)
28+
* <a name='async-style-functions'></a>[Async-style functions](docs/composing-suspending-functions.md#async-style-functions)
29+
* <a name='structured-concurrency-with-async'></a>[Structured concurrency with async](docs/composing-suspending-functions.md#structured-concurrency-with-async)
30+
<!--- TOC_REF docs/coroutine-context-and-dispatchers.md -->
31+
* <a name='coroutine-context-and-dispatchers'></a>[Coroutine context and dispatchers](docs/coroutine-context-and-dispatchers.md#coroutine-context-and-dispatchers)
32+
* <a name='dispatchers-and-threads'></a>[Dispatchers and threads](docs/coroutine-context-and-dispatchers.md#dispatchers-and-threads)
33+
* <a name='unconfined-vs-confined-dispatcher'></a>[Unconfined vs confined dispatcher](docs/coroutine-context-and-dispatchers.md#unconfined-vs-confined-dispatcher)
34+
* <a name='debugging-coroutines-and-threads'></a>[Debugging coroutines and threads](docs/coroutine-context-and-dispatchers.md#debugging-coroutines-and-threads)
35+
* <a name='jumping-between-threads'></a>[Jumping between threads](docs/coroutine-context-and-dispatchers.md#jumping-between-threads)
36+
* <a name='job-in-the-context'></a>[Job in the context](docs/coroutine-context-and-dispatchers.md#job-in-the-context)
37+
* <a name='children-of-a-coroutine'></a>[Children of a coroutine](docs/coroutine-context-and-dispatchers.md#children-of-a-coroutine)
38+
* <a name='parental-responsibilities'></a>[Parental responsibilities](docs/coroutine-context-and-dispatchers.md#parental-responsibilities)
39+
* <a name='naming-coroutines-for-debugging'></a>[Naming coroutines for debugging](docs/coroutine-context-and-dispatchers.md#naming-coroutines-for-debugging)
40+
* <a name='combining-context-elements'></a>[Combining context elements](docs/coroutine-context-and-dispatchers.md#combining-context-elements)
41+
* <a name='cancellation-via-explicit-job'></a>[Cancellation via explicit job](docs/coroutine-context-and-dispatchers.md#cancellation-via-explicit-job)
42+
* <a name='thread-local-data'></a>[Thread-local data](docs/coroutine-context-and-dispatchers.md#thread-local-data)
43+
<!--- TOC_REF docs/exception-handling.md -->
44+
* <a name='exception-handling'></a>[Exception handling](docs/exception-handling.md#exception-handling)
45+
* <a name='exception-propagation'></a>[Exception propagation](docs/exception-handling.md#exception-propagation)
46+
* <a name='coroutineexceptionhandler'></a>[CoroutineExceptionHandler](docs/exception-handling.md#coroutineexceptionhandler)
47+
* <a name='cancellation-and-exceptions'></a>[Cancellation and exceptions](docs/exception-handling.md#cancellation-and-exceptions)
48+
* <a name='exceptions-aggregation'></a>[Exceptions aggregation](docs/exception-handling.md#exceptions-aggregation)
49+
* <a name='supervision'></a>[Supervision](docs/exception-handling.md#supervision)
50+
* <a name='supervision-job'></a>[Supervision job](docs/exception-handling.md#supervision-job)
51+
* <a name='supervision-scope'></a>[Supervision scope](docs/exception-handling.md#supervision-scope)
52+
* <a name='exceptions-in-supervised-coroutines'></a>[Exceptions in supervised coroutines](docs/exception-handling.md#exceptions-in-supervised-coroutines)
53+
<!--- TOC_REF docs/channels.md -->
54+
* <a name='channels-experimental'></a>[Channels (experimental)](docs/channels.md#channels-experimental)
55+
* <a name='channel-basics'></a>[Channel basics](docs/channels.md#channel-basics)
56+
* <a name='closing-and-iteration-over-channels'></a>[Closing and iteration over channels](docs/channels.md#closing-and-iteration-over-channels)
57+
* <a name='building-channel-producers'></a>[Building channel producers](docs/channels.md#building-channel-producers)
58+
* <a name='pipelines'></a>[Pipelines](docs/channels.md#pipelines)
59+
* <a name='prime-numbers-with-pipeline'></a>[Prime numbers with pipeline](docs/channels.md#prime-numbers-with-pipeline)
60+
* <a name='fan-out'></a>[Fan-out](docs/channels.md#fan-out)
61+
* <a name='fan-in'></a>[Fan-in](docs/channels.md#fan-in)
62+
* <a name='buffered-channels'></a>[Buffered channels](docs/channels.md#buffered-channels)
63+
* <a name='channels-are-fair'></a>[Channels are fair](docs/channels.md#channels-are-fair)
64+
* <a name='ticker-channels'></a>[Ticker channels](docs/channels.md#ticker-channels)
65+
<!--- TOC_REF docs/shared-mutable-state-and-concurrency.md -->
66+
* <a name='shared-mutable-state-and-concurrency'></a>[Shared mutable state and concurrency](docs/shared-mutable-state-and-concurrency.md#shared-mutable-state-and-concurrency)
67+
* <a name='the-problem'></a>[The problem](docs/shared-mutable-state-and-concurrency.md#the-problem)
68+
* <a name='volatiles-are-of-no-help'></a>[Volatiles are of no help](docs/shared-mutable-state-and-concurrency.md#volatiles-are-of-no-help)
69+
* <a name='thread-safe-data-structures'></a>[Thread-safe data structures](docs/shared-mutable-state-and-concurrency.md#thread-safe-data-structures)
70+
* <a name='thread-confinement-fine-grained'></a>[Thread confinement fine-grained](docs/shared-mutable-state-and-concurrency.md#thread-confinement-fine-grained)
71+
* <a name='thread-confinement-coarse-grained'></a>[Thread confinement coarse-grained](docs/shared-mutable-state-and-concurrency.md#thread-confinement-coarse-grained)
72+
* <a name='mutual-exclusion'></a>[Mutual exclusion](docs/shared-mutable-state-and-concurrency.md#mutual-exclusion)
73+
* <a name='actors'></a>[Actors](docs/shared-mutable-state-and-concurrency.md#actors)
74+
<!--- TOC_REF docs/select-expression.md -->
75+
* <a name='select-expression-experimental'></a>[Select expression (experimental)](docs/select-expression.md#select-expression-experimental)
76+
* <a name='selecting-from-channels'></a>[Selecting from channels](docs/select-expression.md#selecting-from-channels)
77+
* <a name='selecting-on-close'></a>[Selecting on close](docs/select-expression.md#selecting-on-close)
78+
* <a name='selecting-to-send'></a>[Selecting to send](docs/select-expression.md#selecting-to-send)
79+
* <a name='selecting-deferred-values'></a>[Selecting deferred values](docs/select-expression.md#selecting-deferred-values)
80+
* <a name='switch-over-a-channel-of-deferred-values'></a>[Switch over a channel of deferred values](docs/select-expression.md#switch-over-a-channel-of-deferred-values)
81+
<!--- END -->

knit/src/Knit.kt

+64-22
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const val DIRECTIVE_START = "<!--- "
2222
const val DIRECTIVE_END = "-->"
2323

2424
const val TOC_DIRECTIVE = "TOC"
25+
const val TOC_REF_DIRECTIVE = "TOC_REF"
2526
const val KNIT_DIRECTIVE = "KNIT"
2627
const val INCLUDE_DIRECTIVE = "INCLUDE"
2728
const val CLEAR_DIRECTIVE = "CLEAR"
@@ -50,21 +51,26 @@ const val EXCEPTION_MODE = "EXCEPTION"
5051
const val LINES_START_PREDICATE = "LINES_START"
5152

5253
val API_REF_REGEX = Regex("(^|[ \\](])\\[([A-Za-z0-9_().]+)]($|[^\\[(])")
53-
val LINK_DEF_REGEX = Regex("^\\[([A-Za-z0-9_().]+)\\]: .*")
54+
val LINK_DEF_REGEX = Regex("^\\[([A-Za-z0-9_().]+)]: .*")
55+
56+
val tocRefMap = HashMap<File, List<TocRef>>()
57+
val fileSet = HashSet<File>()
58+
val fileQueue = ArrayDeque<File>()
5459

5560
fun main(args: Array<String>) {
5661
if (args.isEmpty()) {
5762
println("Usage: Knit <markdown-files>")
5863
return
5964
}
60-
args.forEach {
61-
if (!knit(it)) System.exit(1) // abort on first error with error exit code
65+
args.map { File(it) }.toCollection(fileQueue)
66+
fileQueue.toCollection(fileSet)
67+
while (!fileQueue.isEmpty()) {
68+
if (!knit(fileQueue.removeFirst())) System.exit(1) // abort on first error with error exit code
6269
}
6370
}
6471

65-
fun knit(markdownFileName: String): Boolean {
66-
println("*** Reading $markdownFileName")
67-
val markdownFile = File(markdownFileName)
72+
fun knit(markdownFile: File): Boolean {
73+
println("*** Reading $markdownFile")
6874
val tocLines = arrayListOf<String>()
6975
var knitRegex: Regex? = null
7076
val includes = arrayListOf<Include>()
@@ -78,12 +84,12 @@ fun knit(markdownFileName: String): Boolean {
7884
val remainingApiRefNames = mutableSetOf<String>()
7985
var moduleName: String by Delegates.notNull()
8086
var docsRoot: String by Delegates.notNull()
87+
var retryKnitLater = false
88+
val tocRefs = ArrayList<TocRef>().also { tocRefMap[markdownFile] = it }
8189
// read markdown file
82-
var putBackLine: String? = null
8390
val markdown = markdownFile.withMarkdownTextReader {
8491
mainLoop@ while (true) {
85-
val inLine = putBackLine ?: readLine() ?: break
86-
putBackLine = null
92+
val inLine = readLine() ?: break
8793
val directive = directive(inLine)
8894
if (directive != null && markdownPart == MarkdownPart.TOC) {
8995
markdownPart = MarkdownPart.POST_TOC
@@ -96,6 +102,22 @@ fun knit(markdownFileName: String): Boolean {
96102
require(markdownPart == MarkdownPart.PRE_TOC) { "Only one TOC directive is supported" }
97103
markdownPart = MarkdownPart.TOC
98104
}
105+
TOC_REF_DIRECTIVE -> {
106+
requireSingleLine(directive)
107+
require(!directive.param.isEmpty()) { "$TOC_REF_DIRECTIVE directive must include reference file path" }
108+
val refPath = directive.param
109+
val refFile = File(markdownFile.parent, refPath.replace('/', File.separatorChar))
110+
require(fileSet.contains(refFile)) { "Referenced file $refFile is missing from the processed file set" }
111+
val toc = tocRefMap[refFile]
112+
if (toc == null) {
113+
retryKnitLater = true // put this file at the end of the queue and retry later
114+
} else {
115+
val lines = toc.map { (levelPrefix, name, ref) ->
116+
"$levelPrefix <a name='$ref'></a>[$name]($refPath#$ref)"
117+
}
118+
if (!replaceUntilNextDirective(lines)) error("Unexpected end of file after $TOC_REF_DIRECTIVE")
119+
}
120+
}
99121
KNIT_DIRECTIVE -> {
100122
requireSingleLine(directive)
101123
require(!directive.param.isEmpty()) { "$KNIT_DIRECTIVE directive must include regex parameter" }
@@ -153,19 +175,9 @@ fun knit(markdownFileName: String): Boolean {
153175
}
154176
INDEX_DIRECTIVE -> {
155177
requireSingleLine(directive)
156-
val indexLines = processApiIndex(siteRoot + "/" + moduleName, docsRoot, directive.param, remainingApiRefNames)
178+
val indexLines = processApiIndex("$siteRoot/$moduleName", docsRoot, directive.param, remainingApiRefNames)
157179
?: throw IllegalArgumentException("Failed to load index for ${directive.param}")
158-
skip = true
159-
while (true) {
160-
val skipLine = readLine() ?: break@mainLoop
161-
if (directive(skipLine) != null) {
162-
putBackLine = skipLine
163-
break
164-
}
165-
}
166-
skip = false
167-
outText += indexLines
168-
outText += putBackLine!!
180+
if (!replaceUntilNextDirective(indexLines)) error("Unexpected end of file after $INDEX_DIRECTIVE")
169181
}
170182
}
171183
if (inLine.startsWith(CODE_START)) {
@@ -183,7 +195,10 @@ fun knit(markdownFileName: String): Boolean {
183195
val i = inLine.indexOf(' ')
184196
require(i >= 2) { "Invalid section start" }
185197
val name = inLine.substring(i + 1).trim()
186-
tocLines += " ".repeat(i - 2) + "* [$name](#${makeSectionRef(name)})"
198+
val levelPrefix = " ".repeat(i - 2) + "*"
199+
val sectionRef = makeSectionRef(name)
200+
tocLines += "$levelPrefix [$name](#$sectionRef)"
201+
tocRefs += TocRef(levelPrefix, name, sectionRef)
187202
continue@mainLoop
188203
}
189204
val linkDefMatch = LINK_DEF_REGEX.matchEntire(inLine)
@@ -220,6 +235,11 @@ fun knit(markdownFileName: String): Boolean {
220235
}
221236
}
222237
} ?: return false // false when failed
238+
// bailout if retry was requested
239+
if (retryKnitLater) {
240+
fileQueue.add(markdownFile)
241+
return true
242+
}
223243
// update markdown file with toc
224244
val newLines = buildList<String> {
225245
addAll(markdown.preTocText)
@@ -242,6 +262,8 @@ fun knit(markdownFileName: String): Boolean {
242262
return true
243263
}
244264

265+
data class TocRef(val levelPrefix: String, val name: String, val ref: String)
266+
245267
fun makeTest(testOutLines: MutableList<String>, pgk: String, test: List<String>, predicate: String) {
246268
val funName = buildString {
247269
var cap = true
@@ -361,6 +383,7 @@ class MarkdownTextReader(r: Reader) : LineNumberReader(r) {
361383
val postTocText = arrayListOf<String>()
362384
var markdownPart: MarkdownPart = MarkdownPart.PRE_TOC
363385
var skip = false
386+
var putBackLine: String? = null
364387

365388
val outText: MutableList<String> get() = when (markdownPart) {
366389
MarkdownPart.PRE_TOC -> preTocText
@@ -369,12 +392,31 @@ class MarkdownTextReader(r: Reader) : LineNumberReader(r) {
369392
}
370393

371394
override fun readLine(): String? {
395+
putBackLine?.let {
396+
putBackLine = null
397+
return it
398+
}
372399
val line = super.readLine() ?: return null
373400
inText += line
374401
if (!skip && markdownPart != MarkdownPart.TOC)
375402
outText += line
376403
return line
377404
}
405+
406+
fun replaceUntilNextDirective(lines: List<String>): Boolean {
407+
skip = true
408+
while (true) {
409+
val skipLine = readLine() ?: return false
410+
if (directive(skipLine) != null) {
411+
putBackLine = skipLine
412+
break
413+
}
414+
}
415+
skip = false
416+
outText += lines
417+
outText += putBackLine!!
418+
return true
419+
}
378420
}
379421

380422
fun <T : LineNumberReader> File.withLineNumberReader(factory: (Reader) -> T, block: T.() -> Unit): T? {

0 commit comments

Comments
 (0)