diff options
-rw-r--r-- | config/nimdoc.tex.cfg | 2 | ||||
-rw-r--r-- | doc/contributing.rst | 177 | ||||
-rw-r--r-- | doc/docstyle.rst | 16 | ||||
-rw-r--r-- | doc/nimdoc.css | 31 | ||||
-rw-r--r-- | lib/packages/docutils/highlite.nim | 83 | ||||
-rw-r--r-- | lib/packages/docutils/rst.nim | 49 | ||||
-rw-r--r-- | lib/packages/docutils/rstast.nim | 4 | ||||
-rw-r--r-- | lib/packages/docutils/rstgen.nim | 18 | ||||
-rw-r--r-- | nimdoc/testproject/expected/nimdoc.out.css | 31 | ||||
-rw-r--r-- | tests/stdlib/trstgen.nim | 20 |
10 files changed, 291 insertions, 140 deletions
diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 69266f85d..471f20dc1 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -137,6 +137,8 @@ bottomline=false} \newcommand{\spanReference}[1]{#1} \newcommand{\spanOther}[1]{#1} \newcommand{\spantok}[1]{\frame{#1}} +\newcommand{\spanprogram}[1]{\textbf{\underline{#1}}} +\newcommand{\spanoption}[1]{\textbf{#1}} $content \end{document} diff --git a/doc/contributing.rst b/doc/contributing.rst index 3d3da2ce0..81fa9f8eb 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -1,9 +1,10 @@ -.. default-role:: code - ============ Contributing ============ +.. default-role:: code +.. include:: rstcommon.rst + .. contents:: @@ -19,21 +20,23 @@ Writing tests There are 4 types of tests: -1. `runnableExamples` documentation comment tests, ran by `nim doc mymod.nim` +1. `runnableExamples` documentation comment tests, ran by `nim doc mymod.nim`:cmd: These end up in documentation and ensure documentation stays in sync with code. -2. separate test files, e.g.: `tests/stdlib/tos.nim`. - In nim repo, `testament` (see below) runs all `$nim/tests/*/t*.nim` test files; +2. separate test files, e.g.: ``tests/stdlib/tos.nim``. + In nim repo, `testament`:cmd: (see below) runs all + ``$nim/tests/*/t*.nim`` test files; for nimble packages, see https://github.com/nim-lang/nimble#tests. -3. (deprecated) tests in `when isMainModule:` block, ran by `nim r mymod.nim`. - `nimble test` can run those in nimble packages when specified in a +3. (deprecated) tests in `when isMainModule:` block, ran by `nim r mymod.nim`:cmd:. + `nimble test`:cmd: can run those in nimble packages when specified in a `task "test"`. -4. (not preferred) `.. code-block:: nim` RST snippets; these should only be used in rst sources, +4. (not preferred) ``.. code-block:: nim`` RST snippets; + these should only be used in rst sources, in nim sources `runnableExamples` should now always be preferred to those for several reasons (cleaner syntax, syntax highlights, batched testing, and - `rdoccmd` allows customization). + parameter `rdoccmd` allows customization). Not all the tests follow the convention here, feel free to change the ones that don't. Always leave the code cleaner than you found it. @@ -41,8 +44,8 @@ that don't. Always leave the code cleaner than you found it. Stdlib ------ -Each stdlib module (anything under `lib/`, e.g. `lib/pure/os.nim`) should -preferably have a corresponding separate test file, e.g. `tests/stdlib/tos.nim`. +Each stdlib module (anything under ``lib/``, e.g. ``lib/pure/os.nim``) should +preferably have a corresponding separate test file, e.g. ``tests/stdlib/tos.nim``. The old convention was to add a `when isMainModule:` block in the source file, which only gets executed when the tester is building the file. @@ -71,36 +74,36 @@ Sample test: # doAssert with `not` can now be done as follows: doAssert not (1 == 2) -Always refer to a GitHub issue using the following exact syntax: `bug #1234` as shown +Always refer to a GitHub issue using the following exact syntax: ``bug #1234`` as shown above, so that it's consistent and easier to search or for tooling. Some browser extensions (e.g. https://github.com/sindresorhus/refined-github) will even turn those in clickable links when it works. Rationale for using a separate test file instead of `when isMainModule:` block: * allows custom compiler flags or testing options (see details below) -* faster CI since they can be joined in `megatest` (combined into a single test) +* faster CI since they can be joined in ``megatest`` (combined into a single test) * avoids making the parser do un-necessary work when a source file is merely imported * avoids mixing source and test code when reporting line of code statistics or code coverage Compiler -------- -The tests for the compiler use a testing tool called `testament`. They are all -located in `tests/` (e.g.: `tests/destructor/tdestructor3.nim`). +The tests for the compiler use a testing tool called `testament`:cmd:. They are all +located in ``tests/`` (e.g.: ``tests/destructor/tdestructor3.nim``). Each test has its own file. All test files are prefixed with `t`. If you want to create a file for import into another test only, use the prefix `m`. At the beginning of every test is the expected behavior of the test. Possible keys are: -- `cmd`: A compilation command template e.g. `nim $target --threads:on $options $file` +- `cmd`: A compilation command template e.g. `nim $target --threads:on $options $file`:cmd: - `output`: The expected output (stdout + stderr), most likely via `echo` - `exitcode`: Exit code of the test (via `exit(number)`) - `errormsg`: The expected compiler error message - `file`: The file the errormsg was produced at - `line`: The line the errormsg was produced at -For a full spec, see here: `testament/specs.nim` +For a full spec, see here: ``testament/specs.nim`` An example of a test: @@ -124,51 +127,51 @@ Running tests You can run the tests with -:: +.. code-block:: cmd ./koch tests which will run a good subset of tests. Some tests may fail. If you only want to see the output of failing tests, go for -:: - +```cmd ./koch tests --failing all +``` You can also run only a single category of tests. A category is a subdirectory -in the `tests` directory. There are a couple of special categories; for a -list of these, see `testament/categories.nim`, at the bottom. +in the ``tests/`` directory. There are a couple of special categories; for a +list of these, see ``testament/categories.nim``, at the bottom. -:: +.. code:: cmd - ./koch tests c lib # compiles/runs stdlib modules, including `isMainModule` tests + ./koch tests c lib # compiles / runs stdlib modules, including `isMainModule` tests ./koch tests c megatest # runs a set of tests that can be combined into 1 To run a single test: -:: +.. code:: cmd ./koch test run <category>/<name> # e.g.: tuples/ttuples_issues ./koch test run tests/stdlib/tos.nim # can also provide relative path For reproducible tests (to reproduce an environment more similar to the one run by Continuous Integration on travis/appveyor), you may want to disable your -local configuration (e.g. in `~/.config/nim/nim.cfg`) which may affect some +local configuration (e.g. in ``~/.config/nim/nim.cfg``) which may affect some tests; this can also be achieved by using -`export XDG_CONFIG_HOME=pathtoAlternateConfig` before running `./koch` +`export XDG_CONFIG_HOME=pathtoAlternateConfig`:cmd: before running `./koch`:cmd: commands. Comparing tests =============== -Test failures can be grepped using `Failure:`. +Test failures can be grepped using ``Failure:``. The tester can compare two test runs. First, you need to create a reference test. You'll also need to the commit id, because that's what the tester needs to know in order to compare the two. -:: +.. code:: cmd git checkout devel DEVEL_COMMIT=$(git rev-parse HEAD) @@ -176,15 +179,15 @@ the tester needs to know in order to compare the two. Then switch over to your changes and run the tester again. -:: +.. code:: cmd git checkout your-changes ./koch tests -Then you can ask the tester to create a `testresults.html` which will +Then you can ask the tester to create a ``testresults.html`` which will tell you if any new tests passed/failed. -:: +.. code:: cmd ./koch tests --print html $DEVEL_COMMIT @@ -219,14 +222,14 @@ Documentation When contributing new procs, be sure to add documentation, especially if the proc is public. Even private procs benefit from documentation and can be -viewed using `nim doc --docInternal foo.nim`. +viewed using `nim doc --docInternal foo.nim`:cmd:. Documentation begins on the line following the `proc` definition, and is prefixed by `##` on each line. Runnable code examples are also encouraged, to show typical behavior with a few test cases (typically 1 to 3 `assert` statements, depending on complexity). -These `runnableExamples` are automatically run by `nim doc mymodule.nim` -as well as `testament` and guarantee they stay in sync. +These `runnableExamples` are automatically run by `nim doc mymodule.nim`:cmd: +as well as `testament`:cmd: and guarantee they stay in sync. .. code-block:: nim proc addBar*(a: string): string = @@ -238,7 +241,7 @@ as well as `testament` and guarantee they stay in sync. See `parentDir <os.html#parentDir,string>`_ example. The RestructuredText Nim uses has a special syntax for including code snippets -embedded in documentation; these are not run by `nim doc` and therefore are +embedded in documentation; these are not run by `nim doc`:cmd: and therefore are not guaranteed to stay in sync, so `runnableExamples` is almost always preferred: .. code-block:: nim @@ -250,9 +253,9 @@ not guaranteed to stay in sync, so `runnableExamples` is almost always preferred ## echo someProc() # "something" result = "something" # single-hash comments do not produce documentation -The `.. code-block:: nim` followed by a newline and an indentation instructs the -`nim doc` command to produce syntax-highlighted example code with the -documentation (`.. code-block::` is sufficient from inside a nim module). +The ``.. code-block:: nim`` followed by a newline and an indentation instructs the +`nim doc`:cmd: command to produce syntax-highlighted example code with the +documentation (``.. code-block::`` is sufficient from inside a nim module). When forward declaration is used, the documentation should be included with the first appearance of the proc. @@ -298,10 +301,10 @@ example below) from `Nim Index`_ can be used in doc comment this way: Inline monospaced text can be input using \`single backticks\` or \`\`double backticks\`\`. The former are syntactically highlighted, the latter are not. -To avoid accidental highlighting follow this rule in `*.nim` files: +To avoid accidental highlighting follow this rule in ``*.nim`` files: * use single backticks for fragments of code in Nim and other - programming languages, including identifiers, in `*.nim` files. + programming languages, including identifiers, in ``*.nim`` files. For languages other than Nim add a role after final backtick, e.g. for C++ inline highlighting:: @@ -313,18 +316,20 @@ To avoid accidental highlighting follow this rule in `*.nim` files: `SELECT * FROM <table_name>;`:code: + Highlight shell commands by ``:cmd:`` role; for command line options use + ``:option:`` role, e.g.: \`--docInternal\`:option:. + * prefer double backticks otherwise: * for file names: \`\`os.nim\`\` * for fragments of strings **not** enclosed by `"` and `"` and not related to code, e.g. text of compiler messages - * for command line options: \`\`--docInternal\`\` * also when code ends with a standalone ``\`` (otherwise a combination of ``\`` and a final \` would get escaped) -.. Note:: `*.rst` files have `:literal:` as their default role. - So for them the rule above is only applicable if the `:nim:` role - is set up manually as the default:: +.. Note:: ``*.rst`` files have ``:literal:`` as their default role. + So for them the rule above is only applicable if the ``:nim:`` role + is set up manually as the default [*]_:: .. role:: nim(code) :language: nim @@ -333,6 +338,8 @@ To avoid accidental highlighting follow this rule in `*.nim` files: The first 2 lines are for other RST implementations, including Github one. + .. [*] this is fulfilled when ``doc/rstcommon.rst`` is included. + Best practices ============== @@ -350,7 +357,7 @@ to avoid name conflicts across packages. # if in nim sources when defined(allocStats): discard # bad, can cause conflicts when defined(nimAllocStats): discard # preferred - # if in a pacakge `cligen`: + # if in a package `cligen`: when defined(debug): discard # bad, can cause conflicts when defined(cligenDebug): discard # preferred @@ -372,7 +379,7 @@ Design with method call syntax chaining in mind # can be called as: `getLines().foo(false)` .. _avoid_quit: -Use exceptions (including assert / doAssert) instead of `quit` +Use exceptions (including `assert` / `doAssert`) instead of `quit` rationale: https://forum.nim-lang.org/t/4089 .. code-block:: nim @@ -382,7 +389,7 @@ rationale: https://forum.nim-lang.org/t/4089 .. _tests_use_doAssert: Use `doAssert` (or `unittest.check`, `unittest.require`), not `assert` in all -tests so they'll be enabled even with `--assertions:off`. +tests so they'll be enabled even with `--assertions:off`:option:. .. code-block:: nim @@ -391,10 +398,10 @@ tests so they'll be enabled even with `--assertions:off`. doAssert foo() # preferred .. _runnableExamples_use_assert: -An exception to the above rule is `runnableExamples` and `code-block` rst blocks +An exception to the above rule is `runnableExamples` and ``code-block`` rst blocks intended to be used as `runnableExamples`, which for brevity use `assert` -instead of `doAssert`. Note that `nim doc -d:danger main` won't pass `-d:danger` to the -`runnableExamples`, but `nim doc --doccmd:-d:danger main` would, and so would the +instead of `doAssert`. Note that `nim doc -d:danger main`:cmd: won't pass `-d:danger`:option: to the +`runnableExamples`, but `nim doc --doccmd:-d:danger main`:cmd: would, and so would the second example below: .. code-block:: nim @@ -437,8 +444,8 @@ https://github.com/nim-lang/Nim/pull/9335 and https://forum.nim-lang.org/t/4089 doAssert foo() == [1, 2] # preferred, except when not possible to do so. -The Git stuff -============= +The `git`:cmd: stuff +==================== General commit rules -------------------- @@ -446,12 +453,12 @@ General commit rules 1. Important, critical bugfixes that have a tiny chance of breaking somebody's code should be backported to the latest stable release branch (currently 1.4.x) and maybe also all the way back to the 1.0.x branch. - The commit message should contain the tag `[backport]` for "backport to all - stable releases" and the tag `[backport:$VERSION]` for backporting to the + The commit message should contain the tag ``[backport]`` for "backport to all + stable releases" and the tag ``[backport:$VERSION]`` for backporting to the given $VERSION. 2. If you introduce changes which affect backward compatibility, - make breaking changes, or have PR which is tagged as `[feature]`, + make breaking changes, or have PR which is tagged as ``[feature]``, the changes should be mentioned in `the changelog <https://github.com/nim-lang/Nim/blob/devel/changelog.md>`_. @@ -462,29 +469,29 @@ General commit rules your editor reformatted automatically the code or whatever different reason, this should be excluded from the commit. - *Tip:* Never commit everything as is using `git commit -a`, but review - carefully your changes with `git add -p`. + *Tip:* Never commit everything as is using `git commit -a`:cmd:, but review + carefully your changes with `git add -p`:cmd:. 4. Changes should not introduce any trailing whitespace. - Always check your changes for whitespace errors using `git diff --check` - or add the following `pre-commit` hook: + Always check your changes for whitespace errors using `git diff --check`:cmd: + or add the following ``pre-commit`` hook: - .. code-block:: sh + .. code:: cmd #!/bin/sh git diff --check --cached || exit $? 5. Describe your commit and use your common sense. - Example commit message: + Example commit message:: - `Fixes #123; refs #124` + Fixes #123; refs #124 - indicates that issue `#123` is completely fixed (GitHub may automatically - close it when the PR is committed), wheres issue `#124` is referenced + indicates that issue ``#123`` is completely fixed (GitHub may automatically + close it when the PR is committed), wheres issue ``#124`` is referenced (e.g.: partially fixed) and won't close the issue when committed. 6. PR body (not just PR title) should contain references to fixed/referenced github - issues, e.g.: `fix #123` or `refs #123`. This is so that you get proper cross + issues, e.g.: ``fix #123`` or ``refs #123``. This is so that you get proper cross referencing from linked issue to the PR (github won't make those links with just PR title, and commit messages aren't always sufficient to ensure that, e.g. can't be changed after a PR is merged). @@ -492,7 +499,7 @@ General commit rules 7. Commits should be always be rebased against devel (so a fast forward merge can happen) - e.g.: use `git pull --rebase origin devel`. This is to avoid messing up + e.g.: use `git pull --rebase origin devel`:cmd:. This is to avoid messing up git history. Exceptions should be very rare: when rebase gives too many conflicts, simply squash all commits using the script shown in @@ -508,7 +515,7 @@ Continuous Integration (CI) 1. Continuous Integration is by default run on every push in a PR; this clogs the CI pipeline and affects other PR's; if you don't need it (e.g. for WIP or - documentation only changes), add `[skip ci]` to your commit message title. + documentation only changes), add ``[skip ci]`` to your commit message title. This convention is supported by our github actions pipelines and our azure pipeline as well as our former other pipelines: `Appveyor <https://www.appveyor.com/docs/how-to/filtering-commits/#skip-directive-in-commit-message>`_ @@ -531,16 +538,16 @@ Debugging CI failures, flaky tests, etc will re-trigger all CI jobs (even successful ones, which can be wasteful). Instead, follow these instructions to only restart the jobs that failed: - * Azure: if on your own fork, it's possible from inside azure console - (e.g. `dev.azure.com/username/username/_build/results?buildId=1430&view=results`) via `rerun failed jobs` on top. - If either on you own fork or in Nim repo, it's possible from inside GitHub UI - under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569 - * GitHub actions: under "Checks" tab, click "Re-run jobs" in the right. - * builds.sr.ht: create a sourcehut account so you can restart a PR job as illustrated. - builds.sr.ht also allows you to ssh to a CI machine which can help a lot for debugging - issues, see docs in https://man.sr.ht/builds.sr.ht/build-ssh.md and - https://drewdevault.com/2019/08/19/Introducing-shell-access-for-builds.html; see - https://man.sr.ht/tutorials/set-up-account-and-git.md to generate and upload ssh keys. + * Azure: if on your own fork, it's possible from inside azure console + (e.g. ``dev.azure.com/username/username/_build/results?buildId=1430&view=results``) via ``rerun failed jobs`` on top. + If either on you own fork or in Nim repo, it's possible from inside GitHub UI + under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569 + * GitHub actions: under "Checks" tab, click "Re-run jobs" in the right. + * builds.sr.ht: create a sourcehut account so you can restart a PR job as illustrated. + builds.sr.ht also allows you to ssh to a CI machine which can help a lot for debugging + issues, see docs in https://man.sr.ht/builds.sr.ht/build-ssh.md and + https://drewdevault.com/2019/08/19/Introducing-shell-access-for-builds.html; see + https://man.sr.ht/tutorials/set-up-account-and-git.md to generate and upload ssh keys. Code reviews @@ -554,15 +561,15 @@ Code reviews doesn't help much as it doesn't highlight moves. Instead, you can use something like this, see visual results `here <https://github.com/nim-lang/Nim/pull/10431#issuecomment-456968196>`_: - .. code-block:: sh + .. code:: cmd git fetch origin pull/10431/head && git checkout FETCH_HEAD git diff --color-moved-ws=allow-indentation-change --color-moved=blocks HEAD^ 3. In addition, you can view GitHub-like diffs locally to identify what was changed - within a code block using `diff-highlight` or `diff-so-fancy`, e.g.: + within a code block using `diff-highlight`:cmd: or `diff-so-fancy`:cmd:, e.g.: - .. code-block:: sh + :: # put this in ~/.gitconfig: [core] @@ -651,15 +658,15 @@ to existing modules is acceptable. For two reasons: Conventions ----------- -1. New stdlib modules should go under `Nim/lib/std/`. The rationale is to +1. New stdlib modules should go under ``Nim/lib/std/``. The rationale is to require users to import via `import std/foo` instead of `import foo`, which would cause potential conflicts with nimble packages. Note that this still applies for new modules in existing logical - directories, e.g.: use `lib/std/collections/foo.nim`, - not `lib/pure/collections/foo.nim`. + directories, e.g.: use ``lib/std/collections/foo.nim``, + not ``lib/pure/collections/foo.nim``. 2. New module names should prefer plural form whenever possible, e.g.: - `std/sums.nim` instead of `std/sum.nim`. In particular, this reduces + ``std/sums.nim`` instead of ``std/sum.nim``. In particular, this reduces chances of conflicts between module name and the symbols it defines. Furthermore, module names should use `snake_case` and not use capital letters, which cause issues when going from an OS without case diff --git a/doc/docstyle.rst b/doc/docstyle.rst index 7f3fa8cf2..df1f36dad 100644 --- a/doc/docstyle.rst +++ b/doc/docstyle.rst @@ -6,17 +6,17 @@ General Guidelines * See also `nep1<https://nim-lang.github.io/Nim/nep1.html>`_ which should probably be merged here. * Authors should document anything that is exported; documentation for private - procs can be useful too (visible via `nim doc --docInternal foo.nim`). + procs can be useful too (visible via `nim doc --docInternal foo.nim`:cmd:). * Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, each sentence after the first should be complete and in present tense. * Documentation is parsed as a custom ReStructuredText (RST) with partial markdown support. * In nim sources, prefer single backticks to double backticks since it's simpler - and `nim doc` supports it. Likewise with rst files: `nim rst2html` will render those as monospace, and - adding `.. default-role:: code` to an rst file will also make those render as monospace when rendered directly + and `nim doc`:cmd: supports it. Likewise with ``rst`` files: `nim rst2html`:cmd: will render those as monospace, and + adding ``.. default-role:: code`` to an ``rst`` file will also make those render as monospace when rendered directly in tools such as github. -* In nim sources, for links, prefer `[link text](link.html)` to `` `link text<link.html>`_ `` - since the syntax is simpler and markdown is more common (likewise, `nim rst2html` also supports it in rst files). +* (debatable) In nim sources, for links, prefer ``[link text](link.html)`` to `\`link text<link.html>\`_`:code: + since the syntax is simpler and markdown is more common (likewise, `nim rst2html`:cmd: also supports it in ``rst`` files). .. code-block:: nim @@ -29,7 +29,7 @@ Module-level documentation -------------------------- Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (`##`). -Sometimes `##[ multiline docs containing code ]##` is preferable, see `lib/pure/times.nim`. +Sometimes `##[ multiline docs containing code ]##` is preferable, see ``lib/pure/times.nim``. Code samples are encouraged, and should follow the general RST syntax: .. code-block:: Nim @@ -76,11 +76,11 @@ Whenever an example of usage would be helpful to the user, you should include on ## echo execCmdEx("git pull") ## drawOnScreen() runnableExamples: - # `runnableExamples` is usually preferred to `code-block`, when possible. + # `runnableExamples` is usually preferred to ``code-block``, when possible. doAssert addThree(3, 125, 6) == -122 result = x +% y +% z -The command `nim doc` will then correctly syntax highlight the Nim code within the documentation. +The command `nim doc`:cmd: will then correctly syntax highlight the Nim code within the documentation. Types ----- diff --git a/doc/nimdoc.css b/doc/nimdoc.css index ced791d16..6366d33dc 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -35,6 +35,8 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #c4891b; --number: #252dbe; --literal: #a4255b; + --program: #6060c0; + --option: #508000; --raw-data: #a4255b; } @@ -63,6 +65,8 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #bd93f9; --number: #bd93f9; --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; --raw-data: #8be9fd; } @@ -527,7 +531,6 @@ div.option-list-label { margin-left: -11.5em; margin-right: 0em; min-width: 11.5em; - font-weight: bolder; display: inline-block; vertical-align: top; } @@ -546,7 +549,7 @@ blockquote { border-left: 5px solid #bbc; } -.pre { +.pre, span.tok { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; font-weight: 500; font-size: 0.85em; @@ -557,6 +560,12 @@ blockquote { border-radius: 4px; } +span.tok { + border: 1px solid #808080; + padding-bottom: 0.1em; + margin-right: 0.2em; +} + pre { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; color: var(--text); @@ -844,9 +853,6 @@ span.classifier { span.classifier-delimiter { font-weight: bold; } -span.option { - white-space: nowrap; } - span.problematic { color: #b30000; } @@ -926,6 +932,21 @@ span.Preprocessor { span.Directive { color: #252dbe; } +span.option { + font-weight: bold; + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + color: var(--option); +} + +span.program { + font-weight: bold; + color: var(--program); + text-decoration: underline; + text-decoration-color: var(--hint); + text-decoration-thickness: 0.05em; + text-underline-offset: 0.15em; +} + span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, span.Other { color: var(--other); } diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 94cb2ebde..c0f4c9760 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -37,6 +37,18 @@ ## .. code:: Nim ## for l in ["C", "c++", "jAvA", "Nim", "c#"]: echo getSourceLanguage(l) ## +## There is also a `Cmd` pseudo-language supported, which is a simple generic +## shell/cmdline tokenizer (UNIX shell/Powershell/Windows Command): +## no escaping, no programming language constructs besides variable definition +## at the beginning of line. It supports these operators: +## +## .. code:: Cmd +## & && | || ( ) '' "" ; # for comments +## +## Instead of escaping always use quotes like here +## `nimgrep --ext:'nim|nims' file.name`:cmd: shows how to input ``|``. +## Any argument that contains ``.`` or ``/`` or ``\`` will be treated +## as a file or directory. import strutils @@ -45,7 +57,7 @@ from algorithm import binarySearch type SourceLanguage* = enum langNone, langNim, langCpp, langCsharp, langC, langJava, - langYaml, langPython + langYaml, langPython, langCmd TokenClass* = enum gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber, gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit, @@ -53,7 +65,7 @@ type gtOperator, gtPunctuation, gtComment, gtLongComment, gtRegularExpression, gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler, gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel, - gtReference, gtOther + gtReference, gtProgram, gtOption, gtOther GeneralTokenizer* = object of RootObj kind*: TokenClass start*, length*: int @@ -64,14 +76,17 @@ type const sourceLanguageToStr*: array[SourceLanguage, string] = ["none", - "Nim", "C++", "C#", "C", "Java", "Yaml", "Python"] + "Nim", "C++", "C#", "C", "Java", "Yaml", "Python", "Cmd"] tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace", "DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber", "Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit", "EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment", "RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData", "Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink", - "Label", "Reference", "Other"] + "Label", "Reference", + # start from lower-case if there is a corresponding RST role (see rst.nim) + "program", "option", + "Other"] # The following list comes from doc/keywords.txt, make sure it is # synchronized with this array by running the module itself as a test case. @@ -898,6 +913,65 @@ proc pythonNextToken(g: var GeneralTokenizer) = "with", "yield"] nimNextToken(g, keywords) +proc cmdNextToken(g: var GeneralTokenizer) = + var pos = g.pos + g.start = g.pos + if g.state == low(TokenClass): + g.state = gtProgram + case g.buf[pos] + of ' ', '\t'..'\r': + g.kind = gtWhitespace + while g.buf[pos] in {' ', '\t'..'\r'}: + if g.buf[pos] == '\n': + g.state = gtProgram + inc(pos) + of '\'', '"': + g.kind = gtOption + let q = g.buf[pos] + inc(pos) + while g.buf[pos] notin {q, '\0'}: + inc(pos) + if g.buf[pos] == q: inc(pos) + of '#': + g.kind = gtComment + while g.buf[pos] notin {'\n', '\0'}: + inc(pos) + of '&', '|': + g.kind = gtOperator + inc(pos) + if g.buf[pos] == g.buf[pos-1]: inc(pos) + g.state = gtProgram + of '(': + g.kind = gtOperator + g.state = gtProgram + inc(pos) + of ')': + g.kind = gtOperator + inc(pos) + of ';': + g.state = gtProgram + g.kind = gtOperator + inc(pos) + of '\0': g.kind = gtEof + else: + if g.state == gtProgram: + g.kind = gtProgram + g.state = gtOption + else: + g.kind = gtOption + while g.buf[pos] notin {' ', '\t'..'\r', '&', '|', '(', ')', '\'', '"', '\0'}: + if g.buf[pos] == ';' and g.buf[pos+1] == ' ': + # (check space because ';' can be used inside arguments in Win bat) + break + if g.kind == gtOption and g.buf[pos] in {'/', '\\', '.'}: + g.kind = gtIdentifier # for file/dir name + elif g.kind == gtProgram and g.buf[pos] == '=': + g.kind = gtIdentifier # for env variable setting at beginning of line + g.state = gtProgram + inc(pos) + g.length = pos - g.pos + g.pos = pos + proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) = g.lang = lang case lang @@ -909,6 +983,7 @@ proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) = of langJava: javaNextToken(g) of langYaml: yamlNextToken(g) of langPython: pythonNextToken(g) + of langCmd: cmdNextToken(g) when isMainModule: var keywords: seq[string] diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 7c4b92f88..cb65791ff 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -23,10 +23,10 @@ ## ## Nim can output the result to HTML [#html]_ or Latex [#latex]_. ## -## .. [#html] commands ``nim doc`` for ``*.nim`` files and -## ``nim rst2html`` for ``*.rst`` files +## .. [#html] commands `nim doc`:cmd: for ``*.nim`` files and +## `nim rst2html`:cmd: for ``*.rst`` files ## -## .. [#latex] command ``nim rst2tex`` for ``*.rst``. +## .. [#latex] command `nim rst2tex`:cmd: for ``*.rst``. ## ## If you are new to RST please consider reading the following: ## @@ -78,14 +78,21 @@ ## ## * directives: ``code-block`` [cmp:Sphinx]_, ``title``, ## ``index`` [cmp:Sphinx]_ -## * predefined roles ``:nim:`` (default), ``:c:`` (C programming language), -## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). -## That is every language that `highlite <highlite.html>`_ supports. -## They turn on appropriate syntax highlighting in inline code. +## * predefined roles +## - ``:nim:`` (default), ``:c:`` (C programming language), +## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). +## That is every language that `highlite <highlite.html>`_ supports. +## They turn on appropriate syntax highlighting in inline code. ## -## .. Note:: default role for Nim files is ``:nim:``, -## for ``*.rst`` it's currently ``:literal:``. +## .. Note:: default role for Nim files is ``:nim:``, +## for ``*.rst`` it's currently ``:literal:``. ## +## - generic command line highlighting roles: +## - ``:cmd:`` for commands and common shells syntax +## - ``:program:`` for executable names [cmp:Sphinx]_ +## (one can just use ``:cmd:`` on single word) +## - ``:option:`` for command line options [cmp:Sphinx]_ +## - ``:tok:``, a role for highlighting of programming language tokens ## * ***triple emphasis*** (bold and italic) using \*\*\* ## * ``:idx:`` role for \`interpreted text\` to include the link to this ## text into an index (example: `Nim index`_). @@ -95,11 +102,11 @@ ## //compile compile the project ## //doc generate documentation ## -## Here the dummy `//` will disappear, while options ``compile`` -## and ``doc`` will be left in the final document. +## Here the dummy `//` will disappear, while options `compile`:option: +## and `doc`:option: will be left in the final document. ## ## .. [cmp:Sphinx] similar but different from the directives of -## Python `Sphinx directives`_ extensions +## Python `Sphinx directives`_ and `Sphinx roles`_ extensions ## ## .. _`extra features`: ## @@ -144,7 +151,7 @@ ## ----- ## ## See `Nim DocGen Tools Guide <docgen.html>`_ for the details about -## ``nim doc``, ``nim rst2html`` and ``nim rst2tex`` commands. +## `nim doc`:cmd:, `nim rst2html`:cmd: and `nim rst2tex`:cmd: commands. ## ## See `packages/docutils/rstgen module <rstgen.html>`_ to know how to ## generate HTML or Latex strings to embed them into your documents. @@ -156,6 +163,7 @@ ## .. _RST roles list: https://docutils.sourceforge.io/docs/ref/rst/roles.html ## .. _Nim index: https://nim-lang.org/docs/theindex.html ## .. _Sphinx directives: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html +## .. _Sphinx roles: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html import os, strutils, rstast, std/enumutils, algorithm, lists, sequtils, @@ -530,7 +538,7 @@ proc defaultRole(options: RstParseOptions): string = # mirror highlite.nim sourceLanguageToStr with substitutions c++ cpp, c# csharp const supportedLanguages = ["nim", "yaml", "python", "java", "c", - "cpp", "csharp"] + "cpp", "csharp", "cmd"] proc whichRoleAux(sym: string): RstNodeKind = let r = sym.toLowerAscii @@ -543,6 +551,7 @@ proc whichRoleAux(sym: string): RstNodeKind = of "sup", "superscript": result = rnSup # literal and code are the same in our implementation of "code": result = rnInlineLiteral + of "program", "option", "tok": result = rnCodeFragment # c++ currently can be spelled only as cpp, c# only as csharp elif r in supportedLanguages: result = rnInlineCode @@ -1113,10 +1122,10 @@ proc toInlineCode(n: PRstNode, language: string): PRstNode = lb.add newLeaf(s) result.add lb -proc toUnknownRole(n: PRstNode, roleName: string): PRstNode = +proc toOtherRole(n: PRstNode, kind: RstNodeKind, roleName: string): PRstNode = let newN = newRstNode(rnInner, n.sons) let newSons = @[newN, newLeaf(roleName)] - result = newRstNode(rnUnknownRole, newSons) + result = newRstNode(kind, newSons) proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = var newKind = n.kind @@ -1144,8 +1153,8 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = # a role: let (roleName, lastIdx) = getRefname(p, p.idx+1) newKind = whichRole(p, roleName) - if newKind == rnUnknownRole: - result = n.toUnknownRole(roleName) + if newKind in {rnUnknownRole, rnCodeFragment}: + result = n.toOtherRole(newKind, roleName) elif newKind == rnInlineCode: result = n.toInlineCode(language=roleName) else: @@ -1417,8 +1426,8 @@ proc parseInline(p: var RstParser, father: PRstNode) = if k == rnInlineCode: n = n.toInlineCode(language=roleName) parseUntil(p, n, "`", false) # bug #17260 - if k == rnUnknownRole: - n = n.toUnknownRole(roleName) + if k in {rnUnknownRole, rnCodeFragment}: + n = n.toOtherRole(k, roleName) father.add(n) elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 00f3f2b35..81e3ba6d9 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -56,7 +56,9 @@ type # * `file#id <file#id>'_ rnSubstitutionDef, # a definition of a substitution # Inline markup: - rnInlineCode, + rnInlineCode, # interpreted text with code in a known language + rnCodeFragment, # inline code for highlighting with the specified + # class (which cannot be inferred from context) rnUnknownRole, # interpreted text with an unknown role rnSub, rnSup, rnIdx, rnEmphasis, # "*" diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 1b9334a77..40ed88954 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1198,7 +1198,8 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = "$1", result) of rnOptionGroup: renderAux(d, n, - "<div class=\"option-list-label\">$1</div>", + "<div class=\"option-list-label\"><tt><span class=\"option\">" & + "$1</span></tt></div>", "\\item[$1]", result) of rnDescription: renderAux(d, n, "<div class=\"option-list-description\">$1</div>", @@ -1319,13 +1320,22 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "|$1|", "|$1|", result) of rnDirective: renderAux(d, n, "", "", result) - of rnUnknownRole: + of rnUnknownRole, rnCodeFragment: var tmp0 = "" var tmp1 = "" renderRstToOut(d, n.sons[0], tmp0) renderRstToOut(d, n.sons[1], tmp1) - dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", - [tmp0, tmp1]) + var class = tmp1 + # don't allow missing role break latex compilation: + if d.target == outLatex and n.kind == rnUnknownRole: class = "Other" + if n.kind == rnCodeFragment: + dispA(d.target, result, + "<tt class=\"docutils literal\"><span class=\"pre $2\">" & + "$1</span></tt>", + "\\texttt{\\span$2{$1}}", [tmp0, class]) + else: # rnUnknownRole, not necessarily code/monospace font + dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", + [tmp0, class]) of rnSub: renderAux(d, n, "<sub>$1</sub>", "\\rstsub{$1}", result) of rnSup: renderAux(d, n, "<sup>$1</sup>", "\\rstsup{$1}", result) of rnEmphasis: renderAux(d, n, "<em>$1</em>", "\\emph{$1}", result) diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index ced791d16..6366d33dc 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -35,6 +35,8 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #c4891b; --number: #252dbe; --literal: #a4255b; + --program: #6060c0; + --option: #508000; --raw-data: #a4255b; } @@ -63,6 +65,8 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #bd93f9; --number: #bd93f9; --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; --raw-data: #8be9fd; } @@ -527,7 +531,6 @@ div.option-list-label { margin-left: -11.5em; margin-right: 0em; min-width: 11.5em; - font-weight: bolder; display: inline-block; vertical-align: top; } @@ -546,7 +549,7 @@ blockquote { border-left: 5px solid #bbc; } -.pre { +.pre, span.tok { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; font-weight: 500; font-size: 0.85em; @@ -557,6 +560,12 @@ blockquote { border-radius: 4px; } +span.tok { + border: 1px solid #808080; + padding-bottom: 0.1em; + margin-right: 0.2em; +} + pre { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; color: var(--text); @@ -844,9 +853,6 @@ span.classifier { span.classifier-delimiter { font-weight: bold; } -span.option { - white-space: nowrap; } - span.problematic { color: #b30000; } @@ -926,6 +932,21 @@ span.Preprocessor { span.Directive { color: #252dbe; } +span.option { + font-weight: bold; + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + color: var(--option); +} + +span.program { + font-weight: bold; + color: var(--program); + text-decoration: underline; + text-decoration-color: var(--hint); + text-decoration-thickness: 0.05em; + text-underline-offset: 0.15em; +} + span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, span.Other { color: var(--other); } diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 29747f4e8..0af6ba566 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -40,6 +40,10 @@ proc toHtml(input: string, proc id(str: string): string = """<span class="Identifier">""" & str & "</span>" proc op(str: string): string = """<span class="Operator">""" & str & "</span>" proc pu(str: string): string = """<span class="Punctuation">""" & str & "</span>" +proc optionListLabel(opt: string): string = + """<div class="option-list-label"><tt><span class="option">""" & + opt & + "</span></tt></div>" suite "YAML syntax highlighting": test "Basics": @@ -1382,10 +1386,10 @@ Test1 check(output.count("<ul") == 1) check(output.count("<li>") == 2) check(output.count("<div class=\"option-list\"") == 1) - check("""<div class="option-list-label">-m</div>""" & + check(optionListLabel("-m") & """<div class="option-list-description">desc</div></div>""" in output) - check("""<div class="option-list-label">-n</div>""" & + check(optionListLabel("-n") & """<div class="option-list-description">very long desc</div></div>""" in output) @@ -1400,13 +1404,13 @@ Test1 let output = input.toHtml check(output.count("<ul") == 1) check output.count("<div class=\"option-list\"") == 2 - check("""<div class="option-list-label">-m</div>""" & + check(optionListLabel("-m") & """<div class="option-list-description">desc</div></div>""" in output) - check("""<div class="option-list-label">-n</div>""" & + check(optionListLabel("-n") & """<div class="option-list-description">very long desc</div></div>""" in output) - check("""<div class="option-list-label">-d</div>""" & + check(optionListLabel("-d") & """<div class="option-list-description">option</div></div>""" in output) check "<p>option</p>" notin output @@ -1421,13 +1425,13 @@ Test1 let output = input.toHtml check(output.count("<ul") == 1) check output.count("<div class=\"option-list\"") == 2 - check("""<div class="option-list-label">compile</div>""" & + check(optionListLabel("compile") & """<div class="option-list-description">compile1</div></div>""" in output) - check("""<div class="option-list-label">doc</div>""" & + check(optionListLabel("doc") & """<div class="option-list-description">doc1 cont</div></div>""" in output) - check("""<div class="option-list-label">-d</div>""" & + check(optionListLabel("-d") & """<div class="option-list-description">option</div></div>""" in output) check "<p>option</p>" notin output |