An element that shows source code
The code entity takes the name of the language as its optional first
argument. The text option holds the source, or the src option gives the
path of a file that holds it. Expresso.Highlight makes the markup of each
line at render time, and it names the languages.
The src option takes a path that is relative to the working directory of
the command or to the root option of the deck, as for an image. The lines option takes a range of the lines
of the file, such as 3086..3095, and the element then shows only those
lines. It can also take a start text and an end text, such as
[from: "def start(", to: " end"]. The excerpt then starts at the line
of the start text and ends at the line of the end text, so a change above
the excerpt does not move it. to: :block in place of an end text ends
the excerpt at the end of the block that the start line opens. build/1 reads the file through
Expresso.DeckFile, so the watch
mode renders the deck again after a change to the file. The element takes
text or src, and not both.
Each line has a number. The first line of the text is line 1. With src,
the number of a line is its number in the file, so the first line of
lines: 3086..3095 is line 3086. The line_numbers option shows the
number in front of each line.
The reveal option shows the lines in groups, one group at each step. It
takes a list of line numbers and of ranges, such as [1..3, 4..8, 10].
build/1 makes one Expresso.Element.Lines child for each item, with the
specification [from: :next], and the transformer then gives each group
its steps as it does for the items of a list. A line that is in no group
shows at each step. A hidden line keeps its space, so the block does not
move when a group shows. A line number that the element does not show gives
an error, because such a group shows nothing.
A group of reveal or highlight can also be texts, such as
[from: "let hit", to: "return hit;"], in the form of the lines option.
The group then runs from the line of the start text to the line of the end
text, inside the text of the element. A group with only from is the line
of the start text. A group with to: :block runs to the end of the block
that the start line opens, as block_end/2 finds it. The relative option counts the numbers of the groups
from the first line of the element, so 1 is the first line of an excerpt.
resolve/2 turns each group into line numbers before the other checks.
The dim option gives each group the state dim from the first step of a
later group. A line that is in no group does not dim. docs/overlays.md
gives the rules.
The highlight option takes groups of lines in the same form as reveal.
Each line shows at each step, and each group is in focus at its own step:
the lines of the group get the state highlight, and each other line gets
the state dim. build/1 gives each group an on entity with :next, so
the transformer gives the groups one step each from the counter of the
slide. Each line in no group goes into one more group with no on entity.
spotlight/1 then gives each group the state dim at the steps of the
other groups, before the render.
A group of highlight can also be [each: "count"], each token of the
highlighter with the text count. Its Expresso.Element.Lines child has
each and no line numbers. At the step of the group, each line dims, and
the tokens with the text show in full color with a bar under them. A token
group does not dim at the steps of the other groups, because its tokens
dim with their lines. reveal takes no such group, because a hidden token
leaves a hole in its line.
The whole_first option gives the code element one step before its first
group, as a pause in front of it does. At that step, no group is in focus,
so the whole code shows in full color. Expresso.Overlay.Expand reads the
option with lead/1.
Summary
Functions
Return the position of the first byte of the last line of the block that the line at a position opens
Read the source, and make the groups of lines from the reveal option
Check the options of a code element, and build an element with text
Make the assigns of the render function from the struct
Return the number of steps that a code element takes before its groups
Make sure that a lines option is a range of line numbers, or a start text and an end text
Make the HTML of a code element
Make sure that a reveal option is a list of line numbers, ranges and texts
Dim the lines of each code element that are not in focus
Types
@type t() :: %Expresso.Element.Code{ __spark_metadata__: term(), at: term(), class: term(), dim: term(), easing: term(), effect: term(), el: term(), elements: term(), first: term(), highlight: term(), lang: term(), line_numbers: term(), lines: term(), morph: term(), on: term(), relative: term(), reveal: term(), signature: term(), speed: term(), src: term(), steps: term(), text: term(), trailing_comments: term(), whole_first: term(), window: term(), window_title: term(), wrap: term() }
The struct of a code element
Functions
@spec block_end(String.t(), non_neg_integer()) :: non_neg_integer()
Return the position of the first byte of the last line of the block that the line at a position opens
The block holds each later line with more indent than the start line, and
blank lines between them. The first later line with the same indent or
less ends the block. That line is the last line of the block when it
starts with }, ], ) or end, as in most languages. Otherwise the
last line of the block is the last line with more indent, as in Python. A
start line with no line of more indent after it is a block of one line, so
the block does not take the closing line of a block around it.
iex> code = "if (a) {\n if (b) {\n c();\n }\n}\nd();\n"
iex> Expresso.Element.Code.block_end(code, 0)
33
iex> Expresso.Element.Code.block_end(code, 9)
29
iex> Expresso.Element.Code.block_end("def f():\n a\n\n b\nc\n", 0)
16
iex> Expresso.Element.Code.block_end("a\nb\n", 0)
0
Read the source, and make the groups of lines from the reveal option
check/1 calls this function for an element with text, and
Expresso.PathTransformer calls it for an element with src, after it
joins the path to the root option of the deck. With src, the function reads
the file, puts the lines of the lines option into text, and puts the
number of the first line into first. A lines option with from and
to gives its range from the text of the file. Then it puts one
Expresso.Element.Lines child into elements for each item of the
reveal option, in order.
The function gives an error for:
- an element with both
textandsrc, or with neither, - a
linesoption withoutsrc, - a file that it cannot read, or a range that goes past the end of the file,
- a start text that is not in the file or that is in it more than one time, and an end text that is not in the file after the start text,
- a line number of
revealthat the element does not show. Such a group shows nothing, and it takes one step of the slide, so the deck gets a step at which nothing changes, - a
whole_firstoption withouthighlight, - an element with both
revealandhighlight, ahighlightwithdim, a line number ofhighlightthat the element does not show, and a line that is in two groups ofhighlight.
Check the options of a code element, and build an element with text
The transform of the code entity calls this function after it makes the
struct, in the DSL and in Expresso.Builder. An element with src keeps
its file until Expresso.PathTransformer runs, because the root option
of the deck can change the path. The function checks each option that needs
no file, so the error of such an option comes from the entity.
Make the assigns of the render function from the struct
The key overlay holds the attributes of the overlay contract, from
Expresso.Overlay.Render.attributes/1. The key lines holds one triple
for each line: the number of the line, the HTML of the line and the overlay
attributes of its group. The key numbers holds the width of the widest
number in characters, or nil for an element without the line_numbers
option.
@spec lead(t()) :: 0 | 1
Return the number of steps that a code element takes before its groups
The whole_first option gives one step, and the counter of the slide then
goes one further before the first group takes its step.
Make sure that a lines option is a range of line numbers, or a start text and an end text
This function is the custom type of the option. A range starts at 1 or
more, and it has a step of 1. A keyword list has the key from, the key
to or both, one time each, and each value is a text that is not empty.
The value of to can also be :block.
iex> Expresso.Element.Code.lines(10..24)
{:ok, 10..24}
iex> Expresso.Element.Code.lines(from: "def start(", to: "end")
{:ok, [from: "def start(", to: "end"]}
iex> Expresso.Element.Code.lines(from: "def start(", to: :block)
{:ok, [from: "def start(", to: :block]}
@spec render(map()) :: Phoenix.HTML.safe()
Make the HTML of a code element
Each line goes into a span element with the class line, and the line
break is inside the fragment of the line. With the line_numbers option,
the number of the line goes into a span element with the class
line-number at the start of the line. A screen reader does not read the
number, and a copy of the text does not take it. The code element sets
--line-number-width, so each number takes the width of the widest one.
@spec reveal(term()) :: {:ok, [pos_integer() | Range.t()]} | {:error, String.t()}
Make sure that a reveal option is a list of line numbers, ranges and texts
This function is the custom type of the options reveal and highlight.
Each item is a positive integer, a range with a step of 1, or a keyword list
with from and an optional to, each a text that is not empty, or
to: :block. An item can also be [each: text], a token group, and
build/1 refuses it in reveal.
iex> Expresso.Element.Code.reveal([1..2, [from: "let hit", to: "return hit;"], 7])
{:ok, [1..2, [from: "let hit", to: "return hit;"], 7]}
@spec spotlight(Expresso.Deck.t()) :: Expresso.Deck.t()
Dim the lines of each code element that are not in focus
A code element with the highlight option has one group of lines for each
item of the option, and one more group for the lines in no item. The
transformer gives each group of an item its step, in the on entity with
the state highlight. This function gives each other group of the element
an on entity with the state dim at that step. The other lines then dim,
and the group in focus shows in full.
Expresso.Renderer calls this function before Expresso.Overlay.Render.identify/1,
because a group needs an identity for its on entities.