From 37223d2ea95dc21247a2eb0109e58104c150ca52 Mon Sep 17 00:00:00 2001 From: Constantine Molchanov Date: Sun, 23 Aug 2026 14:36:25 +0400 Subject: [PATCH] Feature: Rest: `.. include::`: Support `:start-after:` and `:end-before:` in `:literal:` mode (#26130) With this addition, we can include code samples in the docs using comments as achors. This is analogous to mdBook's [shiftinclude](https://github.com/daviddrysdale/mdbook-shiftinclude) preprocessor, which is used extensively in the Status projects docs, e.g.: https://github.com/status-im/nim-chronos/blob/master/docs/src/tutorials/http_client/chapter1.md?plain=1#L16 P.S. One missing piece would be the ability to de-dent the included code automatically but that's a feature for another PR. This isn't as critical as the ability to include parts of the code. --- lib/packages/docutils/rst.nim | 44 +++++++++++++++++------------------ tests/stdlib/trst.nim | 32 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 4d2ce3e5f6..c34b634fb6 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -3319,31 +3319,31 @@ proc dirInclude(p: var RstParser): PRstNode = rstMessage(p, meCannotOpenFile, filename) else: # XXX: error handling; recursive file inclusion! + let inputString = readFile(path) + let startPosition = + block: + let searchFor = n.getFieldValue("start-after").strip() + if searchFor != "": + let pos = inputString.find(searchFor) + if pos != -1: pos + searchFor.len + else: 0 + else: + 0 + + let endPosition = + block: + let searchFor = n.getFieldValue("end-before").strip() + if searchFor != "": + let pos = inputString.find(searchFor, start = startPosition) + if pos != -1: pos - 1 + else: 0 + else: + inputString.len - 1 + if getFieldValue(n, "literal") != "": result = newRstNode(rnLiteralBlock) - result.add newLeaf(readFile(path)) + result.add newLeaf(inputString[startPosition..endPosition]) else: - let inputString = readFile(path) - let startPosition = - block: - let searchFor = n.getFieldValue("start-after").strip() - if searchFor != "": - let pos = inputString.find(searchFor) - if pos != -1: pos + searchFor.len - else: 0 - else: - 0 - - let endPosition = - block: - let searchFor = n.getFieldValue("end-before").strip() - if searchFor != "": - let pos = inputString.find(searchFor, start = startPosition) - if pos != -1: pos - 1 - else: 0 - else: - inputString.len - 1 - var q: RstParser initParser(q, p.s) let saveFileIdx = p.s.currFileIdx diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 215a051502..df2407f7fd 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -1630,6 +1630,38 @@ And this should **NOT** be visible in `docs.html` doAssert "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") + + test "`:literal:` flag": + "code.nim".writeFile(""" +discard +""") + + let input = """ +.. include:: code.nim + :literal: +""" + check "
discard\n
" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + removeFile("code.nim") + + + test "Include everything between in `:literal:` mode": + "code.nim".writeFile(""" +proc notIncluded = discard +#CodeStart +proc included = discard +#CodeEnd +proc notIncluded = discard +""") + + let input = """ +.. include:: code.nim + :literal: + :start-after: #CodeStart + :end-before: #CodeEnd +""" + check "
\nproc included = discard\n
" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + removeFile("code.nim") + suite "RST escaping": test "backspaces": check("""\ this""".toAst == dedent"""