unison/doc2.u
2021-03-31 19:37:40 -05:00

530 lines
13 KiB
Plaintext

{-
- [x] Debug the lexer/parser combination
- [x] Implement display logic for doc type (Unison side)
- [x] Add to IOSource
- [x] Implement display logic for new doc type in DisplayValues
- [x] (Later) docs command should look for foo.doc if nothing is linked, or just have it do: display foo.doc
- [x] (Later) Instant docs preview
- [x] Review with Simon, Rúnar, to see if we're missing any elements
- [x] Add missing element types to syntax tree
- [ ] Write illustrative transcript (and possibly separate tests)
- [ ] PR writeup
- [ ] (Later) `data DisplayObject b a = BuiltinObject b | MissingObject ShortHash | UserObject a`
- [ ] (Later) Pretty-printer should reverse the syntax
- [ ] (Later) Top level types should allow anon doc blocks
- [ ] (Later) Remove backticks syntax
- [ ] (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 named link to the ''List'' 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 }}