jektex 0.0.3 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +76 -15
- data/lib/jektex/jektex.rb +99 -55
- data/lib/jektex/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 476a24b916af7e71a30a60cd2476063fa2d627959060179104e8a41871c5d4ab
|
4
|
+
data.tar.gz: 93243e4af6fea3e355f0cc4401e990cdb107f77c717b7e63d4902a172b56a7d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0ae577416fec788441659e670f4e85c0b34c091c81f666ab1cd62f5a39b3500548efb4238f53d77f1ea0dd30bd3571f001aa418311dcf6786a70afbbd3d0e8c
|
7
|
+
data.tar.gz: a7feff881708ffb27bffb76c2f00146c0bb4f7f4a90fe341ea88f121f1442ace72294596112f8c139e9a5628f4f14f0022e2f18dabe5a7e15e56e985d31c9d73
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/jektex.svg)](https://rubygems.org/gems/jektex)
|
2
|
+
|
1
3
|
# Jektex
|
2
4
|
Jekyll plugin for blazing fast server side cached LaTeX rendering with support of macros.
|
3
5
|
Enjoy comfort of latex and markdown without cluttering your site with bloated javascript.
|
@@ -13,14 +15,14 @@ Enjoy comfort of latex and markdown without cluttering your site with bloated ja
|
|
13
15
|
- Does not interfere with Jekyll workflow and project structure
|
14
16
|
- Marks invalid syntax in document
|
15
17
|
- Prints location of invalid expression during rendering
|
16
|
-
-
|
18
|
+
- Highly configurable but still having sensible defaults
|
19
|
+
- Makes sure that cache does not contain expression rendered with outdated configuration
|
17
20
|
|
18
21
|
## Usage
|
19
22
|
|
20
23
|
### Notation
|
21
24
|
**Inline formula**
|
22
25
|
Put formula between two pairs of dolar signs (`$$`) inside of paragraph.
|
23
|
-
|
24
26
|
```latex
|
25
27
|
Lorem ipsum dolor sit amet, consectetur $$e^{i\theta}=\cos(\theta)+i\sin(\theta)$$
|
26
28
|
adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
@@ -43,31 +45,87 @@ display mode?
|
|
43
45
|
This is how [kramdown](https://kramdown.gettalong.org/)(Jekyll's markdown parser) works
|
44
46
|
so I decided to respect this convention. It makes this plugin more consistent and universal._
|
45
47
|
|
46
|
-
###
|
47
|
-
|
48
|
+
### Config
|
49
|
+
Jektex si highly configurable from your `_config.yml` file
|
50
|
+
|
51
|
+
**Disabling cache**
|
52
|
+
You can disable caching with `disable_disk_cache = true` in `_config.yml`. Cache is
|
53
|
+
enabled by default. You can find more information on [Jekyll official website](https://jekyllrb.com/docs/configuration/options/).
|
54
|
+
|
55
|
+
**Setting cache location**
|
56
|
+
By default jektex cache will be saved in `.jekyll-cache` directory. This results in it's
|
57
|
+
deletion when you call `jekyll clean`. To prevent cache deletion or you just want to
|
58
|
+
change location of cache for another reason you can achieve that by specifying
|
59
|
+
`cache_dir` in `_config.yml`.
|
60
|
+
```yaml
|
61
|
+
# Jektex cache dir location
|
62
|
+
jektex:
|
63
|
+
cache_dir: ".jektex-cache"
|
64
|
+
```
|
65
|
+
|
66
|
+
**Ignore**
|
67
|
+
By default jektex tries to render LaTeX in all files not excluded by Jekyll. But
|
68
|
+
sometimes you get in situation when you do not want to render some files. For example
|
69
|
+
_RSS feed_ with excerpts containing LaTeX. As a solution jektex offers `ignore` option.
|
70
|
+
You can use conventional wild cards using `*`. For example:
|
71
|
+
```yaml
|
72
|
+
# Jektex ignore files
|
73
|
+
jektex:
|
74
|
+
ignore: ["*.xml", "README.md", "_drafts/*" ]
|
75
|
+
```
|
76
|
+
|
77
|
+
This example configuration ignores all `.xml` files, `README.md` and all files
|
78
|
+
in `_drafts` directory.
|
79
|
+
|
80
|
+
Another option for ignoring specific posts is setting `jektex` tag in front matter of
|
81
|
+
post to `false`. For example:
|
82
|
+
```yaml
|
83
|
+
---
|
84
|
+
title: "How Jektex works"
|
85
|
+
category: "Development"
|
86
|
+
jektex: false
|
87
|
+
layout: post
|
88
|
+
---
|
89
|
+
```
|
90
|
+
|
91
|
+
Setting `jektex` tag to `true` or not setting at all will result in jektex rendering LaTeX
|
92
|
+
expressions in that post.
|
48
93
|
|
94
|
+
**Macros**
|
95
|
+
You can define global macros like this:
|
49
96
|
```yaml
|
50
97
|
# Jektex macros
|
51
|
-
jektex
|
98
|
+
jektex:
|
99
|
+
macros:
|
52
100
|
- ["\\Q", "\\mathbb{Q}"]
|
53
101
|
- ["\\C", "\\mathbb{C}"]
|
54
102
|
```
|
103
|
+
And yes you have to escape backlash(`\`) with another backlash. This is caused by
|
104
|
+
[yaml definition](https://yaml.org/).
|
55
105
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
106
|
+
**Complete examples**
|
107
|
+
Recommended config:
|
108
|
+
```yaml
|
109
|
+
jektex:
|
110
|
+
cache_dir: ".jektex-cache"
|
111
|
+
ignore: ["*.xml"]
|
112
|
+
macros:
|
113
|
+
- ["\\Q", "\\mathbb{Q}"]
|
114
|
+
- ["\\C", "\\mathbb{C}"]
|
115
|
+
```
|
116
|
+
Having no configuration is equivalent to this:
|
117
|
+
```yaml
|
118
|
+
jektex:
|
119
|
+
cache_dir: ".jekyll-cache"
|
120
|
+
ignore: []
|
121
|
+
macros: []
|
122
|
+
```
|
64
123
|
|
65
124
|
## Installation
|
66
125
|
This plugin is available as a [RubyGem](https://rubygems.org/gems/jektex).
|
67
126
|
|
68
127
|
**Using bundler**
|
69
128
|
Add `jektex` to your `Gemfile` like this:
|
70
|
-
|
71
129
|
```ruby
|
72
130
|
group :jekyll_plugins do
|
73
131
|
gem "jektex"
|
@@ -90,6 +148,9 @@ and do not forget to add `katex.min.css` to you html head:
|
|
90
148
|
```html
|
91
149
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css" integrity="sha384-MlJdn/WNKDGXveldHDdyRP1R4CTHr3FeuDNfhsLPYrq2t0UBkUdK2jyTnXPEK1NQ" crossorigin="anonymous">
|
92
150
|
```
|
93
|
-
It is much better practice to download **css** file and
|
151
|
+
It is much better practice to download [**css** file](https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css) and load it as an asset from your server directly.
|
94
152
|
You can find more information on [KaTeX's website](https://katex.org/docs/browser.html).
|
95
153
|
|
154
|
+
## Contributions and bug reports
|
155
|
+
Feel free to repost any bugs or even make feature request in [issues on official repository](https://github.com/yagarea/jektex/issues).
|
156
|
+
I am opened for pull requests as well.
|
data/lib/jektex/jektex.rb
CHANGED
@@ -3,108 +3,148 @@ require 'digest'
|
|
3
3
|
require 'htmlentities'
|
4
4
|
|
5
5
|
|
6
|
-
PATH_TO_JS = __dir__
|
7
|
-
|
6
|
+
PATH_TO_JS = File.join(__dir__, "/katex.min.js")
|
7
|
+
DEFAULT_CACHE_DIR = ".jekyll-cache"
|
8
8
|
CACHE_FILE = "jektex-cache.marshal"
|
9
|
-
PATH_TO_CACHE = CACHE_DIR + CACHE_FILE
|
10
9
|
KATEX = ExecJS.compile(open(PATH_TO_JS).read)
|
11
10
|
PARSE_ERROR_PLACEHOLDER = "<b style='color: red;'>PARSE ERROR</b>"
|
11
|
+
FRONT_MATTER_TAG = "jektex"
|
12
|
+
INDENT = " " * 13
|
13
|
+
|
12
14
|
$global_macros = Hash.new
|
15
|
+
$updated_global_macros = Array.new
|
16
|
+
|
13
17
|
$count_newly_generated_expressions = 0
|
18
|
+
|
19
|
+
$path_to_cache = File.join(DEFAULT_CACHE_DIR, CACHE_FILE)
|
14
20
|
$cache = nil
|
15
21
|
$disable_disk_cache = false
|
16
22
|
|
17
|
-
|
23
|
+
$ignored = Array.new
|
24
|
+
|
25
|
+
def get_list_of_updated_global_macros(current_macros, cached_global_macros)
|
26
|
+
return Array.new unless cached_global_macros || current_macros
|
27
|
+
return current_macros.keys unless cached_global_macros
|
28
|
+
return cached_global_macros.keys unless current_macros
|
29
|
+
|
30
|
+
macro_set = Set.new(cached_global_macros.keys + current_macros.keys)
|
31
|
+
macro_set.delete_if { |m| cached_global_macros[m] == current_macros[m] }
|
32
|
+
return macro_set.to_a
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_ignored?(page)
|
36
|
+
return true if page.data[FRONT_MATTER_TAG] == "false"
|
37
|
+
return $ignored.any? { |patern| File.fnmatch?(patern, page.relative_path, File::FNM_DOTMATCH) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def contains_updated_global_macro?(expression)
|
41
|
+
return $updated_global_macros.any? { |m| expression[m] }
|
42
|
+
end
|
43
|
+
|
44
|
+
def print_stats
|
45
|
+
print "#{INDENT}LaTeX: " \
|
46
|
+
"#{$count_newly_generated_expressions} expressions rendered " \
|
47
|
+
"(#{$cache.size} already cached)".ljust(72) + "\r"
|
48
|
+
$stdout.flush
|
49
|
+
end
|
50
|
+
|
51
|
+
def render(page)
|
52
|
+
# check if document is not set to be ignored
|
53
|
+
return page.output if !page.data || is_ignored?(page)
|
18
54
|
# convert HTML entities back to characters
|
19
55
|
post = HTMLEntities.new.decode(page.output.to_s)
|
20
|
-
|
21
|
-
post = post.gsub(/(\\\
|
56
|
+
# render inline expressions
|
57
|
+
post = post.gsub(/(\\\()((.|\n)*?)(?<!\\)\\\)/) { |m| escape_method($1, $2, page.relative_path) }
|
58
|
+
# render display mode expressions
|
59
|
+
post = post.gsub(/(\\\[)((.|\n)*?)(?<!\\)\\\]/) { |m| escape_method($1, $2, page.relative_path) }
|
22
60
|
return post
|
23
61
|
end
|
24
62
|
|
25
|
-
def escape_method( type,
|
26
|
-
|
27
|
-
|
28
|
-
# detect if expression is display view
|
29
|
-
case type.downcase
|
30
|
-
when /\(/
|
31
|
-
@display = false
|
32
|
-
else /\[/
|
33
|
-
@display = true
|
34
|
-
end
|
63
|
+
def escape_method( type, expression, doc_path )
|
64
|
+
# detect if expression is in display mode
|
65
|
+
is_in_display_mode? = type.downcase =~ /\[/
|
35
66
|
|
36
67
|
# generate a hash from the math expression
|
37
|
-
|
68
|
+
expression_hash = Digest::SHA2.hexdigest(string) + is_in_display_mode?.to_s
|
38
69
|
|
39
70
|
# use it if it exists
|
40
|
-
if($cache.has_key?(
|
71
|
+
if($cache.has_key?(expression_hash) && !contains_updated_global_macro?(string))
|
72
|
+
# check if expressin conains updated macro
|
41
73
|
$count_newly_generated_expressions += 1
|
42
74
|
print_stats
|
43
|
-
return $cache[
|
75
|
+
return $cache[expression_hash]
|
44
76
|
|
45
77
|
# else generate one and store it
|
46
78
|
else
|
47
79
|
# create the cache directory, if it doesn't exist
|
48
80
|
begin
|
49
81
|
# render using ExecJS
|
50
|
-
|
51
|
-
{
|
82
|
+
result = KATEX.call("katex.renderToString", expression,
|
83
|
+
{is_in_display_mode?Mode: is_in_display_mode?, macros: $global_macros})
|
52
84
|
rescue SystemExit, Interrupt
|
53
85
|
# save cache to disk
|
54
|
-
File.open(
|
55
|
-
# this stops jekyll being immune to
|
86
|
+
File.open($path_to_cache, "w"){|to_file| Marshal.dump($cache, to_file)}
|
87
|
+
# this stops jekyll being immune to interrupts and kill command
|
56
88
|
raise
|
57
|
-
rescue
|
89
|
+
rescue ExecJS::ProgramError => pe
|
58
90
|
# catch parse error
|
59
|
-
puts "\e[31m " +
|
91
|
+
puts "\e[31m " + pe.message.gsub("ParseError: ", "") + "\n\t" + doc_path + "\e[0m"
|
60
92
|
return PARSE_ERROR_PLACEHOLDER
|
61
93
|
end
|
62
94
|
# save to cache
|
63
|
-
$cache[
|
95
|
+
$cache[expression_hash] = @result
|
64
96
|
# update count of newly generated expressions
|
65
97
|
$count_newly_generated_expressions += 1
|
66
98
|
print_stats
|
67
|
-
return
|
99
|
+
return result
|
68
100
|
end
|
69
101
|
end
|
70
102
|
|
71
|
-
|
72
|
-
|
73
|
-
($count_newly_generated_expressions).to_s +
|
74
|
-
" expressions rendered (" + $cache.size.to_s +
|
75
|
-
" already cached) \r"
|
76
|
-
$stdout.flush
|
103
|
+
Jekyll::Hooks.register :pages, :post_render do |page|
|
104
|
+
page.output = render(page)
|
77
105
|
end
|
78
106
|
|
79
|
-
Jekyll::Hooks.register :
|
80
|
-
|
107
|
+
Jekyll::Hooks.register :documents, :post_render do |doc|
|
108
|
+
doc.output = render(doc)
|
81
109
|
end
|
82
110
|
|
83
111
|
Jekyll::Hooks.register :site, :after_init do |site|
|
84
|
-
# load
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
#
|
91
|
-
if $
|
92
|
-
|
112
|
+
# load jektex config from config file and if no config is defined make empty one
|
113
|
+
config = site.config["jektex"] || Hash.new
|
114
|
+
|
115
|
+
# check if there is defined custom cache location in config
|
116
|
+
$path_to_cache = File.join(config["cache_dir"].to_s, CACHE_FILE) if config.has_key?("cache_dir")
|
117
|
+
|
118
|
+
# load content of cache file if it exists
|
119
|
+
if File.exist?($path_to_cache)
|
120
|
+
$cache = File.open($path_to_cache, "r"){ |from_file| Marshal.load(from_file)}
|
93
121
|
else
|
94
|
-
|
95
|
-
($global_macros.size == 1 ? "" : "s") + " loaded"
|
122
|
+
$cache = Hash.new
|
96
123
|
end
|
97
124
|
|
98
|
-
if
|
99
|
-
|
125
|
+
# check if cache is disable in config
|
126
|
+
$disable_disk_cache = site.config["disable_disk_cache"] if site.config.has_key?("disable_disk_cache")
|
127
|
+
|
128
|
+
# load macros
|
129
|
+
if config.has_key?("macros")
|
130
|
+
for macro_definition in config["macros"]
|
131
|
+
$global_macros[macro_definition[0]] = macro_definition[1]
|
132
|
+
end
|
100
133
|
end
|
101
134
|
|
102
|
-
#
|
103
|
-
|
104
|
-
|
135
|
+
# make list of updated macros
|
136
|
+
$updated_global_macros = get_list_of_updated_global_macros($global_macros, $cache["cached_global_macros"])
|
137
|
+
# print macro information
|
138
|
+
if $global_macros.empty?
|
139
|
+
puts "#{INDENT}LaTeX: no macros loaded"
|
105
140
|
else
|
106
|
-
$
|
141
|
+
puts "#{INDENT}LaTeX: #{$global_macros.size} macro" +
|
142
|
+
($global_macros.size == 1 ? "" : "s") + " loaded" +
|
143
|
+
($updated_global_macros.empty? ? "" : " (#{$updated_global_macros.size} updated)")
|
107
144
|
end
|
145
|
+
|
146
|
+
# load list of ignored files
|
147
|
+
$ignored = config["ignore"] if config.has_key?("ignore")
|
108
148
|
end
|
109
149
|
|
110
150
|
Jekyll::Hooks.register :site, :after_reset do
|
@@ -116,9 +156,13 @@ Jekyll::Hooks.register :site, :post_write do
|
|
116
156
|
# print new line to prevent overwriting previous output
|
117
157
|
print "\n"
|
118
158
|
# check if caching is enabled
|
119
|
-
if
|
159
|
+
if !$disable_disk_cache
|
160
|
+
# save global macros to cache
|
161
|
+
$cache["cached_global_macros"] = $global_macros
|
162
|
+
# create cache path
|
163
|
+
Pathname.new($path_to_cache).dirname.mkpath
|
120
164
|
# save cache to disk
|
121
|
-
|
122
|
-
File.open(PATH_TO_CACHE, "w"){|to_file| Marshal.dump($cache, to_file)}
|
165
|
+
File.open($path_to_cache, "w"){|to_file| Marshal.dump($cache, to_file)}
|
123
166
|
end
|
124
167
|
end
|
168
|
+
|
data/lib/jektex/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jektex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Černý
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execjs
|
@@ -108,7 +108,7 @@ licenses:
|
|
108
108
|
- GPL-3.0-or-later
|
109
109
|
metadata:
|
110
110
|
bug_tracker_uri: https://github.com/yagarea/jektex/issues
|
111
|
-
documentation_uri: https://github.com/yagarea/jektex
|
111
|
+
documentation_uri: https://github.com/yagarea/jektex/blob/master/README.md
|
112
112
|
homepage_uri: https://github.com/yagarea/jektex
|
113
113
|
source_code_uri: https://github.com/yagarea/jektex
|
114
114
|
changelog_uri: https://github.com/yagarea/jektex/blob/master/changelog.md
|