This is a Jekyll subcommand that embeds code samples into documentation files.
To add the plugin to your Jekyll project, add the Gem to your Gemfile
:
gem 'embed-code', :git => 'https://github.com/SpineEventEngine/embed-code', :group => :jekyll_plugins
Now install the dependencies:
bundle install
Note: Fetching a Gem from Git is a feature of Bundler. Right now, this cannot be replicated with
the standard gem
tool. Also, we don't publish embed-code
into any Gem repository yet.
bundle exec jekyll embedCodeSamples
Synopsis:
<embed-code file="path/to/file" fragment="Fragment Name"></embed-code> (I)
OR
<embed-code file="path/to/file" start="first?line*glob" end="last?line*glob"></embed-code> (II)
The instruction must always be followed by a code fence (opening and closing three backticks):
```java ```
The content of the code fence does not matter — the command will overwrite it automatically.
Note that the code fence may specify the syntax in which the code will be highlighted.
This is true even when embedding into HTML.
You can mark up the code file to select named fragments like this:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// #docfragment "Constructor"
public String() {
this.value = new char[0];
}
// #enddocfragment "Constructor"
}
The #docfragment
and #enddocfragment
tags won't be copied into the resulting code fragment.
To add a new code sample, add the following construct to the Markdown file:
<embed-code file="java/lang/String.java" fragment="Constructor"></embed-code> ```java ```
The file
attribute specifies the path to the code file relative to the code root, specified in
the configuration. The fragment
attribute specifies the name of the code fragment to embed. Omit
this attribute to embed the whole file.
You may use any name for your fragments, just omit double quotes ("
) and symbols forbidden in XML.
Alternatively, the <embed-code>
tag may have the following form:
<embed-code file="java/lang/String.java" start="*class Hello*" end="}*"></embed-code> ```java ```
In this case, the fragment is specified by a pair of glob-style patterns. The patterns match the first and the last lines of the desired code fragment. Any of the patterns may be skipped. In such a case, the fragment starts at the beginning or ends at the end of the code file.
The pattern syntax supports an extended glob syntax:
?
— one arbitrary symbol;*
— zero, one, or many arbitrary symbols;[set]
— one symbol from the given set (equivalent to[set]
in regular expressions);^
at the start of the pattern to signify the start of the line;$
at the end of the pattern to signify the end of the line.
Note that the *
symbols at the start and in the end of the pattern are implied. Use ^
and $
to
mark that the pattern should not assume *
at the start/end.
Note that ^
and $
work as special characters in their respective positions but not in the middle
of the pattern. To match the literal ^
symbol at the start of the line, prepend it with another
^
. Similarly, to match a literal $
at the end of the line, append it with another $
.
In order for the command to work, you need to specify at least two configurations params:
code_root
— the directory where all the embedded code resides;documentation_root
— the directory where all the docs which need embedding reside.
Those parameters should be specified via the Jekyll _config.yml
file.
For example:
embed_code:
code_root: ./_samples
documentation_root: ./docs
# Optional params:
code_includes: ["**/*.java", "**/*.gradle"]
doc_includes: ["docs/*.md", "docs/*.html"]
fragments_dir: ".fragments"
separator: "..."
Other command configuration parameters include:
code_includes
— a list of glob patterns defining which code files to consider. By default, all files (**/*
).doc_includes
— a list of glob patterns defining which doc files to consider. By default, Markdown and HTML files (**/*.md
,**/*.html
).fragments_dir
— a temporary directory for fragment files. The command extracts the code fragments files and stores them in a temporary dir. Consider adding this directory to.gitignore
. By default,./.fragments
separator
— a string which separates partitions of a single fragment in the resulting embedded code. See fragment doc for more. The separator is automatically appended with a new line symbol. By default,...
.
A named fragment may consist of one or several pieces declared in a single file. When rendered, the pieces which belong to a single fragment are joined together. One may also specify the text inserted in each joint point (see Configuration for the corresponding parameter).
Here is an example of how a re-occurring fragment is rendered.
Code:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// #docfragment "Standard Object methods"
public int hashCode() {
// ...
return hash;
}
// #enddocfragment "Standard Object methods"
/* Here goes irrelevant code */
// #docfragment "Standard Object methods"
public boolean equals(Object anObject) {
// ...
return false;
}
// #enddocfragment "Standard Object methods"
/* Here goes more irrelevant code */
// #docfragment "Standard Object methods"
public String toString() {
return this;
}
// #enddocfragment "Standard Object methods"
}
As the name of each fragment is put into quotes, space symbols may be used. So one may use the
natural language (e.g. "My favorite fragment here!") instead of CamelCase
, snake_case
, or
kebab-case
(e.g. "my-favourite-fragment-here-exclamation-mark").
Result:
public int hashCode() {
// ...
return hash;
}
...
public boolean equals(Object anObject) {
// ...
return false;
}
...
public String toString() {
return this;
}
You can start (or end) multiple fragments on a single line. Also, they can overlap:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// #docfragment "Standard Object methods", "All methods"
public int hashCode() {
// ...
return hash;
}
public boolean equals(Object anObject) {
// ...
return false;
}
public String toString() {
return this;
}
// #enddocfragment "Standard Object methods"
public boolean startsWith(String prefix, int toffset) {
// ...
return true;
}
// #enddocfragment "All methods"
}
The fragments can be used in other languages too:
<html lang="en">
<body>
<!-- #docfragment "html-only", "asd" -->
<span class="counter" id="counter"></span>
<!-- #enddocfragment "html-only" -->
</body>
</html>
Sometimes, instead of updating code embeddings in doc files, you just want to make sure that the doc is up-to-date with the code examples. It might be helpful to perform that check on CI when changing the documentation.
For this purpose, embed-code
provides another command:
bundle exec jekyll checkCodeSamples
The command does nothing if the code fragments are up-to-date with the original examples. If, however, the code fragments are outdated, the command reports an error.
Under the hood, the command constructs the same code embeddings as does embedCodeSamples
, but
instead of changing the doc files, checks if the files on the file system are identical to the ones
it constructed. If they are not, the process finishes with a non-0 exit code.