unison/doc2.u
Paul Chiusano 7d213d8798 Some tweaks to Doc representation
Embedded evaluation type can't include the evaluated result, since that won't be recomputed when the dependencies are updated. Correct approach is to use the watch cache when evaluating embedded expressions in a doc.
2021-03-16 09:42:23 -05:00

528 lines
13 KiB
Plaintext

{-
- [x] Debug the lexer/parser combination
- [x] Implement display logic for doc type (Unison side)
- [ ] Add to IOSource
- [ ] Implement display logic for new doc type in DisplayValues
- [ ] Write illustrative transcript (and possibly separate tests)
- [ ] Review with Simon, Rúnar, to see if we're missing any elements
- [ ] PR writeup
- [ ] (Later) Pretty-printer should reverse the syntax
- [ ] (Later) docs command should look for foo.doc if nothing is linked, or just have it do: display foo.doc
- [ ] (Later) Top level types should allow anon doc blocks
- [ ] (Later) Remove backticks syntax
- [ ] (Later) Instant docs preview
- [ ] (Later) Remove old doc, rename Doc2 to Doc
-}
id x = x
unique[fb488e55e66e2492c2946388e4e846450701db04] type Doc2.Term = Term Any
Doc2.term : 'a -> Doc2.Term
Doc2.term a = Doc2.Term.Term (Any a)
unique[da70bff6431da17fa515f3d18ded11852b6a745f] type Doc2.SpecialForm
= Source [Either Link.Type Doc2.Term]
| Example Nat Doc2.Term
| Link (Either Link.Type Doc2.Term)
| Signature [Doc2.Term]
| InlineSignature Doc2.Term
| Eval Doc2.Term
| InlineEval Doc2.Term
| Embed Any
| InlineEmbed Any
unique[b7a4fb87e34569319591130bf3ec6e24c9955b6a] type Doc2
= Word Text
| Code Doc2
| CodeBlock Text Doc2
| Bold Doc2
| Italic Doc2
| Strikethrough Doc2
-- style {{ myclass }} mydoc
| Style Doc2 Doc2
| Blockquote Doc2
| Blankline
| SectionBreak
| Aside (Optional Doc2) Doc2
-- callout icon content
| Callout (Optional Doc2) Doc2
| Folded Boolean Doc2 Doc2
| Paragraph [Doc2]
| BulletedList [Doc2]
| NumberedList Nat [Doc2]
| Section Doc2 [Doc2]
| NamedLink Doc2 Doc2
| Special Doc2.SpecialForm
| Docs [Doc2]
unique[d7b2ced8c08b2c6e54050d1f5acedef3395f293d] type Pretty.Annotated w txt
= Empty
| Group w (Pretty.Annotated w txt)
| Lit w txt
| Wrap w (Pretty.Annotated w txt)
| OrElse w (Pretty.Annotated w txt) (Pretty.Annotated w txt)
-- Indent _ initialIndent indentAfterNewline p prefixes the first
-- line of `p` with `initialIndent`, and subsequent lines by `indentAfterNewline`.
| Indent w (Pretty.Annotated w txt) (Pretty.Annotated w txt) (Pretty.Annotated w txt)
| Append w [Pretty.Annotated w txt]
type Pretty txt = Pretty (Pretty.Annotated () txt)
Pretty.get = cases Pretty p -> p
Pretty.map : (txt ->{g} txt2) ->{} Pretty txt ->{g} Pretty txt2
Pretty.map f p =
go = cases
Empty -> Empty
Group _ p -> Group () (go p)
Lit _ t -> Lit () (f t)
Wrap _ p -> Wrap () (go p)
OrElse _ p1 p2 -> OrElse () (go p1) (go p2)
Indent _ i0 iN p -> Indent () (go i0) (go iN) (go p)
-- Append _ ps -> Append () (List.map go ps)
Pretty (go (Pretty.get p))
Pretty.empty : Pretty txt
Pretty.empty = Pretty Empty
{- A group adds a level of breaking. Layout tries not to break a group
unless needed to fit in available width. Breaking is done "outside in".
(a | b) <> (c | d) will try (a <> c)
then (b <> d)
(a | b) <> group (c | d) will try (a <> c)
then (b <> c)
then (b <> d)
-}
Pretty.group : Pretty txt -> Pretty txt
Pretty.group p = Pretty (Group () (Pretty.get p))
-- Create a leaf-level `Pretty` that cannot be broken.
Pretty.lit : txt -> Pretty txt
Pretty.lit txt = Pretty (Lit () txt)
-- Turn on wrapping for `p`, which means that it inserts
-- softbreaks (either a space or a newline) between each
-- subgroup or leaf.
-- wrap (lit a <> lit b <> group c) ==
-- wrap (lit a <> group (orElse (lit " ") (lit "\n")
Pretty.wrap : Pretty txt -> Pretty txt
Pretty.wrap p = Pretty (Wrap () (Pretty.get p))
-- If `p1` fits on the current line at its preferred width,
-- it will be chosen, otherwise `p2` is chosen.
Pretty.orElse : Pretty txt -> Pretty txt -> Pretty txt
Pretty.orElse p1 p2 = Pretty (OrElse () (Pretty.get p1) (Pretty.get p2))
-- Prefixes all lines of `p` by `by`.
Pretty.indent : Pretty txt -> Pretty txt -> Pretty txt
Pretty.indent by p = Pretty (Indent () (Pretty.get by) (Pretty.get by) (Pretty.get p))
-- Prefixes the first line of `p` with `initialIndent`, and
-- subsequent lines by `indentAfterNewline`.
Pretty.indent' : Pretty txt -> Pretty txt -> Pretty txt -> Pretty txt
Pretty.indent' initialIndent indentAfterNewline p =
Pretty (Indent () (Pretty.get initialIndent)
(Pretty.get indentAfterNewline)
(Pretty.get p))
Pretty.append : Pretty txt -> Pretty txt -> Pretty txt
Pretty.append p1 p2 =
use Pretty.Annotated Empty Append
match (Pretty.get p1, Pretty.get p2) with
(_, Empty) -> p1
(Empty, _) -> p2
(Append _ ps1, Append _ ps2) -> Pretty (Append () (ps1 List.++ ps2))
(Append _ ps1, p2) -> Pretty (Append () (ps1 :+ p2))
(p1, Append _ ps2) -> Pretty (Append () (p1 +: ps2))
(p1,p2) -> Pretty (Append () [p1,p2])
Pretty.join : [Pretty txt] -> Pretty txt
Pretty.join =
go acc = cases [] -> acc
h +: t -> go (append acc h) t
go Pretty.empty
Pretty.sepBy : Pretty txt -> [Pretty txt] -> Pretty txt
Pretty.sepBy sep ps =
go acc insertSep = cases
[] -> acc
ps | insertSep -> go (append acc sep) false ps
h +: t -> go (append acc h) true t
go Pretty.empty false ps
syntax.doc.docs = cases [d] -> d
ds -> Docs ds
syntax.doc.word = Word
syntax.doc.bold = Doc2.Bold
syntax.doc.italic = Italic
syntax.doc.strikethrough = Strikethrough
syntax.doc.paragraph = Paragraph
syntax.doc.embedTermLink tm =
guid = "b7a4fb87e34569319591130bf3ec6e24"
Right (Doc2.term tm)
syntax.doc.embedTypeLink typ =
guid = "b7a4fb87e34569319591130bf3ec6e24"
Left typ
syntax.doc.source t = Special (Source t)
syntax.doc.signature ts = Special (Signature ts)
syntax.doc.inlineSignature t = Special (InlineSignature t)
syntax.doc.inlineEval e = Special (InlineEval (Doc2.term e))
syntax.doc.embedSignatureLink tm =
guid = "d9a4fb87e34569319591130bf3ec6e24"
Doc2.term tm
syntax.doc.code c = Code c
syntax.doc.codeBlock typ c = CodeBlock typ (word c)
syntax.doc.verbatim c = CodeBlock "raw" c
syntax.doc.evalBlock d = Special (Eval (Doc2.term d))
syntax.doc.eval a = Special (InlineEval (Doc2.term a))
syntax.doc.example n a = Special (Example n (Doc2.term a))
syntax.doc.link t = Special (Link t)
syntax.doc.transclude d =
guid = "b7a4fb87e34569319591130bf3ec6e24"
d
syntax.doc.namedLink = NamedLink
syntax.doc.bulletedList = BulletedList
syntax.doc.numberedList = NumberedList
syntax.doc.section = Section
unique[e25bc44d251ae0301517ad0bd02cbd294161dc89] type ConsoleText
= Plain Text
| Foreground ANSI.Color ConsoleText
| Background ANSI.Color ConsoleText
| Bold ConsoleText
| Underline ConsoleText
| Invert ConsoleText
unique[de2e0ee924578939213c950dfd8e0ba1047703ae] type ANSI.Color
= Black | Red | Green | Yellow | Blue | Magenta | Cyan | White
| BrightBlack | BrightRed | BrightGreen | BrightYellow | BrightBlue
| BrightMagenta | BrightCyan | BrightWhite
List.map : (a ->{g} b) ->{} [a] ->{g} [b]
List.map f as =
go acc = cases
[] -> acc
h +: t -> go (acc :+ f h) t
go [] as
Either.mapRight : (a -> b) -> Either e a -> Either e b
Either.mapRight f = cases
Right a -> Right (f a)
Left e -> Left e
syntax.doc.formatConsole : Doc2 -> Pretty (Either SpecialForm ConsoleText)
syntax.doc.formatConsole d =
lit t = Pretty.lit (Right (Plain t))
p1 <> p2 = Pretty.append p1 p2
nl = lit "\n"
map f p = Pretty.map (mapRight f) p
go = cases
Word t -> lit t
Code d -> lit "`" <> go d <> lit "`"
CodeBlock typ d ->
lit "``` " <> lit typ <> nl <>
go d <> nl <>
lit "```"
Italic d -> lit "*" <> go d <> lit "*"
Strikethrough d -> lit "~~" <> go d <> lit "~~"
Doc2.Bold d -> map ConsoleText.Bold (go d)
Style _ d -> go d
Blockquote d -> Pretty.indent (lit "> ") (go d)
Blankline -> lit "\n\n"
SectionBreak -> lit "܍"
Aside None d -> Pretty.indent (lit " | ") (go d)
Aside (Some title) d ->
Pretty.indent (lit " | ") (go (Doc2.Bold title) <> nl <> nl <> go d)
Callout icon d -> go (Aside icon d) --
Folded _ _closed open -> go open
Paragraph ds -> Pretty.wrap (Pretty.join (List.map go ds))
BulletedList ds ->
item d = Pretty.indent' (lit "* ") (lit " ") (go d)
items = List.map item ds
Pretty.sepBy nl items
NumberedList n ds ->
dot = ". "
w = Text.size (Nat.toText (n + List.size ds)) + size dot
num n = lit (Text.alignRightWith w ?\s (Nat.toText n Text.++ dot))
indent = lit (Text.repeat w " ")
item : Nat -> Doc2 -> Pretty (Either SpecialForm ConsoleText)
item n d = Pretty.indent' (num n) indent (go d)
items n acc = cases [] -> acc
d +: ds -> items (n+1) (acc :+ item n d) ds
Pretty.sepBy nl (items n [] ds)
Section title ds ->
t = Pretty.indent' (lit "# ") (lit " ") (go (Doc2.Bold title))
subs = List.map (d -> Pretty.indent (lit " ") (go d)) ds
Pretty.sepBy (nl <> nl) (t +: subs)
Docs ds -> Pretty.join (List.map go ds)
NamedLink name _target -> map ConsoleText.Underline (go name)
Special sf -> Pretty.lit (Left sf)
go d
doc1 = {{
# Unison documentation format
The syntax tries to be close to markdown.
## Basic formatting
Paragraphs are separated by one or more blanklines. There's syntax for bold, italics, and strikethrough text:
'''
_italics_ or *italics*
**bold text** or __moar bold text__
~~striken text~~
''some code''
[The Unison website](https://unisonweb.org)
A link to a term: {Some}. A link to a type {type List}.
'''
Renders as:
_italics_ or *italics*
**bold text** or __moar bold text__
~~stricken text~~
''some code''
[The Unison website](https://unisonweb.org)
A link to a term: {Some}. A link to a type {type List}.
### Escaping formatting
If you want the text.
'''
{{ syntax.doc.word "__not bold__"}}
'''
Renders as:
{{ syntax.doc.word "__not bold__" }}
If you have some inline text you want to leave unparsed and have it render in a monospace font, do:
'''
An example of bold text syntax: ''__some bold text__''
'''
Renders as:
An example of bold text syntax: ''__some bold text__''
## Sections and subsections
'''
# Section title!
Sections consist of section titles followed by zero or more paragraphs or other section elements (such as subsections).
## Subsection title
* Item 1
* Item 2
## An empty subsection!
### Subsection 2.1
Some deeply nested content in subsection 2.1
'''
Sections start with a title, then zero or more documents, separated by blanklines. Sections whose title starts with ''##'' are subsections of sections whose title starts with one ''#''. Sections may be nested arbitrarily.
## Lists
### Bulleted lists
Bulleted lists can use ''+'', ''-'', or ''*'' for the bullets. They can be nested, to any depth:
'''
+ A
+ B
+ C
- C1
* C2
'''
Renders as:
+ A
+ B
+ C
- C1
* C2
### Numbered lists
'''
1. A
2. B
3. C
'''
Renders as:
1. A
2. B
3. C
The first number of the list determines the starting number in the rendered output. The other numbers are ignored:
'''
10. A
99. B
102. C
'''
Renders as:
10. A
99. B
102. C
Numbered lists can be nested as well, and combined with bulleted lists:
'''
1. Wake up.
+ What am I doing here?
+ In this nested list.
2. Take shower.
3. Get dressed.
'''
1. Wake up.
+ What am I doing here?
+ In this nested list.
2. Take shower.
3. Get dressed.
## Evaluation
'''
Expressions can be evaluated inline, for instance @eval{1 + 1}.
'''
Renders as:
Expressions can be evaluated inline, for instance @eval{1 + 1}.
Blocks of code can be evaluated as well, for instance:
'''
```
id x = x
id 99
```
'''
Renders as:
```
id x = x
id 99
```
## Including Unison source code
'''
@source{ type Optional, id }
'''
Renders as:
@source{ type Optional, id }
You can also render just the signatures of a collection of terms:
'''
@signatures{ id, Some, None }
'''
Renders as:
@signatures{ id, Some, None }
You can reference a signature inline, like so
'''
An inline signature @signature{id}.
'''
Renders as:
An inline signature @signature{id}.
## Including other documents
'''
"And what is the use of a book," thought {{name}}, "without pictures or conversations?"
* [The documentation]({{baseUrl}}/docs)
'''
"And what is the use of a book," thought {{name}}, "without pictures or conversations?"
## Non-unison code blocks
A code block with no syntax highlighting starts with at least two {{ code (word "'") }} characters, and is terminated by the same number of {{ code (word "'") }} characters.
''''
'''
Not formatted. Rendered as a code block with no syntax highlighting.
'''
''''
Renders as:
'''
Not formatted. Rendered as a code block with no syntax highlighting.
'''
You can use triple (or greater) backticks plus a language name for blocks with syntax highlighting.
'''
``` Haskell
-- A fenced code block which isn't parsed by Unison
reverse = foldl (flip (:)) []
```
``` Scala
// A fenced code block which isn't parsed by Unison
def reverse[A](xs: List[A]) =
xs.foldLeft(Nil : List[A])((acc,a) => a +: acc)
```
'''
Renders as:
``` Haskell
-- A fenced code block which isn't parsed by Unison
reverse = foldl (flip (:)) []
```
``` Scala
// A fenced code block which isn't parsed by Unison
def reverse[A](xs: List[A]) =
xs.foldLeft(Nil : List[A])((acc,a) => a +: acc)
```
}}
name = {{ Alice }}
baseUrl = {{ https://www.unisonweb.org }}