octopress-code-highlighter 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9fc99d5bccc948a519675aa27ad80a7d31a6e02d
4
+ data.tar.gz: f3ae585a771bdaaed7542ef3dd5b62801191671f
5
+ SHA512:
6
+ metadata.gz: 7fe60353f22e04b2dc9b306d9a7e4df39419aead59f1a4b3bcea576b1db7082f315d4e16b498386cebc92c4cd544a4e76f178f0b6d4f4db61b2826b8658ba43e
7
+ data.tar.gz: 469f48187b59a3eb96789564a06b408829a135b8567e545de319108cb98782aa282ae15977f7aa138e83dc449687bf04a6df1430b6834465fe7f402cb6ac0435
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .pygments-cache
19
+ .sass-cache
20
+ .DS_Store
21
+ .code-style-cache
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,2 @@
1
+ language: ruby
2
+ script: bundle exec rake spec
data/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ ## 3.0.0
4
+ - Changed name to octopress-code-highlighter.
5
+ - Now supporting both Pygments.rb and Rouge for highlighting.
6
+
7
+ ## 2.0.1
8
+ - Updated Octopress Ink version, added a simple demo project.
9
+
10
+ ## 2.0.1
11
+ - Updated Octopress Ink version, added a simple demo project.
12
+
13
+ ## 2.0.0
14
+ - Added support for Octopress Ink
15
+
16
+ ## 1.3.1
17
+ - Fixed: line numbers now start at 1 instead of zero.
18
+ - Changed: `escape` option defaults to false.
19
+
20
+ ## 1.3.0
21
+ - Added `escape` option which wraps code in {% raw %} tags to escape liquid. Defaults to true.
22
+
23
+ ## 1.2.3
24
+ - Fixed: figcaptions now work when specifying a title.
25
+
26
+ ## 1.2.2
27
+ - Now you can call Pygments.read_cache directly.
28
+ - Added an option to specify a label on cache files.
29
+ - Simplified caching methods.
30
+
31
+ ## 1.2.1
32
+ - Fix: No longer overwriting options with `nil` in parse_markup
33
+
34
+ ## 1.2.0
35
+ - Added method for users to specify lexer aliases
36
+ - Removed default aliases:
37
+ - ru, yml, coffee - now handled by Pygments correctly
38
+ - pl, m - match multiple languages, enforcing an alias is inappropriate
39
+
40
+ ## 1.1.0
41
+ - Boring bug fixes
42
+
43
+ ## 1.0.0
44
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in octopress-pygments.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Brandon Mathis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Octopress CodeStyle
2
+
3
+ [![Build Status](https://travis-ci.org/octopress/octopress-code-style.png)](https://travis-ci.org/octopress/octopress-code-style)
4
+
5
+ TODO: Write a gem description
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'octopress-code-style'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install octopress-code-style
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :spec do
4
+ rm_rf ".pygments-cache"
5
+ sh "bundle exec rspec"
6
+ end
@@ -0,0 +1,103 @@
1
+ %octopress-diff-bg {
2
+ content: "";
3
+ position: absolute;
4
+ z-index: -1;
5
+ left: 0; right: 0; top: 0; bottom: 0;
6
+ }
7
+
8
+ @mixin octopress-diff-bg($bg) {
9
+ &:after {
10
+ background: $bg;
11
+ @extend %octopress-diff-bg;
12
+ }
13
+ }
14
+
15
+ .code-highlight {
16
+ border: 1px solid $code-highlight-border;
17
+ background: $pre-bg;
18
+ color: $base1;
19
+ span { color: $base1; }
20
+ span { font-style: normal; font-weight: normal; }
21
+
22
+ .c { color: $base01; font-style: italic; } /* Comment */
23
+ .cm { color: $base01; font-style: italic; } /* Comment.Multiline */
24
+ .cp { color: $base01; font-style: italic; } /* Comment.Preproc */
25
+ .c1 { color: $base01; font-style: italic; } /* Comment.Single */
26
+ .cs { /* Comment.Special */
27
+ color: $base01;
28
+ font-weight: bold;
29
+ font-style: italic; }
30
+ .err { color: $solar-red; background: none; } /* Error */
31
+ .k { color: $solar-orange; } /* Keyword */
32
+ .o { color: $base1; font-weight: bold; } /* Operator */
33
+ .p { color: $base1; } /* Operator */
34
+ .ow { color: $solar-cyan; font-weight: bold; } /* Operator.Word */
35
+ .gd {
36
+ color: $base1; /* Generic.Deleted */
37
+ @include octopress-diff-bg(mix($solar-red, $base03, 25%));
38
+ }
39
+ .gd .x { /* Generic.Deleted.Specific */
40
+ color: $base1;
41
+ @include octopress-diff-bg(mix($solar-red, $base03, 35%));
42
+ }
43
+ .ge { /* Generic.Emph */
44
+ color: $base1;
45
+ font-style: italic; }
46
+ //.gr { color: #aa0000 } /* Generic.Error */
47
+ .gh { color: $base01; } /* Generic.Heading */
48
+ .gi { /* Generic.Inserted */
49
+ color: $base1;
50
+ @include octopress-diff-bg(mix($solar-green, $base03, 20%));
51
+ }
52
+ .gi .x { /* Generic.Inserted.Specific */
53
+ color: $base1;
54
+ @include octopress-diff-bg(mix($solar-green, $base03, 40%));
55
+ }
56
+ //.go { color: #888888 } /* Generic.Output */
57
+ //.gp { color: #555555 } /* Generic.Prompt */
58
+ .gs { color: $base1; font-weight: bold; } /* Generic.Strong */
59
+ .gu { color: $solar-violet; } /* Generic.Subheading */
60
+ //.gt { color: #aa0000 } /* Generic.Traceback */
61
+ .kc { color: $solar-green; font-weight: bold; } /* Keyword.Constant */
62
+ .kd { color: $solar-blue; } /* Keyword.Declaration */
63
+ .kp { color: $solar-orange; font-weight: bold; } /* Keyword.Pseudo */
64
+ .kr { color: $solar-magenta; font-weight: bold; } /* Keyword.Reserved */
65
+ .kt { color: $solar-cyan; } /* Keyword.Type */
66
+ .n { color: $solar-blue; }
67
+ .na { color: $solar-blue; } /* Name.Attribute */
68
+ .nb { color: $solar-green; } /* Name.Builtin */
69
+ .nc { color: $solar-magenta;} /* Name.Class */
70
+ .no { color: $solar-yellow; } /* Name.Constant */
71
+ //.ni { color: #800080 } /* Name.Entity */
72
+ .nl { color: $solar-green; }
73
+ .ne { color: $solar-blue; font-weight: bold; } /* Name.Exception */
74
+ .nf { color: $solar-blue; font-weight: bold; } /* Name.Function */
75
+ .nn { color: $solar-yellow; } /* Name.Namespace */
76
+ .nt { color: $solar-blue; font-weight: bold; } /* Name.Tag */
77
+ .nx { color: $solar-yellow; }
78
+ //.bp { color: #999999 } /* Name.Builtin.Pseudo */
79
+ //.vc { color: #008080 } /* Name.Variable.Class */
80
+ .vg { color: $solar-blue; } /* Name.Variable.Global */
81
+ .vi { color: $solar-blue; } /* Name.Variable.Instance */
82
+ .nv { color: $solar-blue; } /* Name.Variable */
83
+ //.w { color: #bbbbbb } /* Text.Whitespace */
84
+ .mf { color: $solar-cyan; } /* Literal.Number.Float */
85
+ .m { color: $solar-cyan; } /* Literal.Number */
86
+ .mh { color: $solar-cyan; } /* Literal.Number.Hex */
87
+ .mi { color: $solar-cyan; } /* Literal.Number.Integer */
88
+ //.mo { color: #009999 } /* Literal.Number.Oct */
89
+ .s { color: $solar-cyan; } /* Literal.String */
90
+ //.sb { color: #d14 } /* Literal.String.Backtick */
91
+ //.sc { color: #d14 } /* Literal.String.Char */
92
+ .sd { color: $solar-cyan; } /* Literal.String.Doc */
93
+ .s2 { color: $solar-cyan; } /* Literal.String.Double */
94
+ .se { color: $solar-red; } /* Literal.String.Escape */
95
+ //.sh { color: #d14 } /* Literal.String.Heredoc */
96
+ .si { color: $solar-blue; } /* Literal.String.Interpol */
97
+ //.sx { color: #d14 } /* Literal.String.Other */
98
+ .sr { color: $solar-cyan; } /* Literal.String.Regex */
99
+ .s1 { color: $solar-cyan; } /* Literal.String.Single */
100
+ //.ss { color: #990073 } /* Literal.String.Symbol */
101
+ //.il { color: #009999 } /* Literal.Number.Integer.Long */
102
+ }
103
+
@@ -0,0 +1,58 @@
1
+ $base03: #002b36 !default; //darkest blue
2
+ $base02: #073642 !default; //dark blue
3
+ $base01: #586e75 !default; //darkest gray
4
+ $base00: #657b83 !default; //dark gray
5
+ $base0: #839496 !default; //medium gray
6
+ $base1: #93a1a1 !default; //medium light gray
7
+ $base2: #f2f2f2 !default; //very light gray
8
+ $base3: #ffffff !default; //white
9
+ $solar-yellow: #b58900 !default;
10
+ $solar-orange: #cb4b16 !default;
11
+ $solar-red: #dc322f !default;
12
+ $solar-magenta: #d33682 !default;
13
+ $solar-violet: #6c71c4 !default;
14
+ $solar-blue: #268bd2 !default;
15
+ $solar-cyan: #2aa198 !default;
16
+ $solar-green: #859900 !default;
17
+
18
+ $marker: rgba(#00baff, .5) !default;
19
+ $marker-bg: rgba($marker, .13) !default;
20
+ $marker-border: rgba($marker, .13) !default;
21
+ $marker-border-left: $marker !default;
22
+
23
+ $solarized: light;// !default;
24
+
25
+ $solar-scroll-bg: rgba(#fff, .15);
26
+ $solar-scroll-thumb: rgba(#fff, .2);
27
+
28
+ @if $solarized == light {
29
+
30
+ $_base03: $base03;
31
+ $_base02: $base02;
32
+ $_base01: $base01;
33
+ $_base00: $base00;
34
+ $_base0: $base0;
35
+ $_base1: $base1;
36
+ $_base2: $base2;
37
+ $_base3: $base3;
38
+
39
+ $base03: $_base3;
40
+ $base02: $_base2;
41
+ $base01: $_base1;
42
+ $base00: $_base0;
43
+ $base0: $_base00;
44
+ $base1: $_base01;
45
+ $base2: $_base02;
46
+ $base3: $_base03;
47
+
48
+ $solar-scroll-bg: rgba(#000, .15);
49
+ $solar-scroll-thumb: rgba(#000, .15);
50
+
51
+ $marker-bg: rgba($marker, .05);
52
+ }
53
+
54
+ $pre-bg: $base03 !default;
55
+ $pre-border: darken($base02, 5) !default;
56
+ $pre-color: $base1 !default;
57
+ $code-highlight-border: $pre-border;
58
+
@@ -0,0 +1,156 @@
1
+ @import 'solarized';
2
+ @import 'code-style';
3
+
4
+ .code-highlight-figure {
5
+ font-size: 13px;
6
+ * { box-sizing: border-box; }
7
+ }
8
+
9
+ .code-highlight-pre {
10
+ background: darken($base03, 1);
11
+ width: 100%;
12
+ margin-bottom: 0;
13
+ display: table;
14
+ }
15
+
16
+ $pre-line-padding: .8em;
17
+
18
+ .code-highlight-row {
19
+ display: table-row;
20
+ width: 100%;
21
+ }
22
+
23
+ .code-highlight-row {
24
+ &:before, .code-highlight-line {
25
+ padding: {left: $pre-line-padding + .8; right: $pre-line-padding + .8 }
26
+ }
27
+ &:first-child {
28
+ &:before, .code-highlight-line { padding-top: $pre-line-padding; }
29
+ }
30
+ &:last-child {
31
+ &:before, .code-highlight-line { padding-bottom: $pre-line-padding; }
32
+ }
33
+ &.numbered {
34
+ &:before, .code-highlight-line {
35
+ padding: {left: $pre-line-padding; right: $pre-line-padding }
36
+ }
37
+ &:before {
38
+ display: table-cell;
39
+ content: attr(data-line);
40
+ min-width: 1.2em;
41
+ color: $base01;
42
+ text-align: right;
43
+ line-height: 1.45em;
44
+ @if $solarized == light {
45
+ background: lighten($base03, 1);
46
+ border-right: 1px solid darken($base02, 2);
47
+ text-shadow: lighten($base02, 2) 0 -1px;
48
+ } @else {
49
+ background: $base02;
50
+ border-right: 1px solid darken($base03, 2);
51
+ box-shadow: lighten($base02, 2) -1px 0 inset;
52
+ text-shadow: darken($base02, 10) 0 -1px;
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ .code-highlight-line {
59
+ display: table-cell;
60
+ width: 100%;
61
+ position: relative;
62
+ z-index: 1;
63
+ }
64
+
65
+ .marked-line {
66
+ position: relative;
67
+ &.unnumbered {
68
+ }
69
+ .code-highlight-line {
70
+ &:before {
71
+ content: "";
72
+ position: absolute;
73
+ background: $marker-bg;
74
+ left: 0; top: 0; bottom: 0; right: 0;
75
+ border-right: 1px solid $marker-border;
76
+ }
77
+ }
78
+ &.unnumbered .code-highlight-line:before {
79
+ border-left: 3px solid $marker-border-left;
80
+ }
81
+ &.numbered:before {
82
+ background: $marker-bg;
83
+ border-left: 3px solid $marker-border-left;
84
+ border-right-color: darken($marker-bg, 20);
85
+ }
86
+ }
87
+
88
+ .start-marked-line {
89
+ &:before, .code-highlight-line:before {
90
+ border-top: 1px solid $marker-border;
91
+ }
92
+ }
93
+ .end-marked-line {
94
+ &:before, .code-highlight-line:before {
95
+ border-bottom: 1px solid $marker-border;
96
+ }
97
+ }
98
+
99
+ .code-highlight {
100
+ overflow: scroll;
101
+ overflow-y: hidden;
102
+ overflow-x: auto;
103
+ line-height: 1.45em;
104
+ }
105
+
106
+ .code-highlight-table {
107
+ margin-bottom: 0;
108
+ background: $base03;
109
+ pre {
110
+ padding: .8em;
111
+ background: none;
112
+ border-radius: 0;
113
+ border: none;
114
+ margin-bottom: 0;
115
+ }
116
+ }
117
+
118
+ .code-highlight-figure {
119
+ margin: {left: 0; right: 0;}
120
+ background: none;
121
+ padding: 0;
122
+ border: 0;
123
+ pre { margin-bottom: 0; }
124
+ }
125
+
126
+ .code-highlight-caption {
127
+ position: relative;
128
+ text-align: center;
129
+ line-height: 2em;
130
+ text-shadow: #fff 0 1px 0;
131
+ color: #474747;
132
+ font-weight: normal;
133
+ margin-bottom: 0;
134
+ background-color: #ccc;
135
+ background-image: linear-gradient(#ffffff, #f0f0f0 6%, #e5e5e5 90%, #e5e5e5);
136
+ border-top-left-radius: 5px;
137
+ border-top-right-radius: 5px;
138
+ font-family: "Helvetica Neue", Arial, "Lucida Grande", "Lucida Sans Unicode", Lucida, sans-serif;
139
+ border: 1px solid #cbcbcb;
140
+ + .code-highlight { border-top: 0; }
141
+ }
142
+
143
+ .code-highlight-caption-link {
144
+ position: absolute; right: .8em;
145
+ color: #666;
146
+ z-index: 1;
147
+ text-shadow: #cbcccc 0 1px 0;
148
+ padding-left: 3em;
149
+ }
150
+
151
+ pre {
152
+ margin-top: 0;
153
+ font-size: 13px;
154
+ font-family: Menlo, Monaco, 'Andale Mono', 'lucida console', 'Courier New', monospace;
155
+ }
156
+
@@ -0,0 +1,67 @@
1
+ require 'octopress-code-highlighter/version'
2
+ require 'fileutils'
3
+ require 'digest/md5'
4
+
5
+ require 'colorator'
6
+ require 'octopress-ink'
7
+
8
+ CODE_CACHE_DIR = '.code-cache'
9
+ FileUtils.mkdir_p(CODE_CACHE_DIR)
10
+
11
+ module Octopress
12
+ module CodeHighlighter
13
+ DEFAULTS = {
14
+ lang: 'plain',
15
+ linenos: true,
16
+ marks: [],
17
+ start: 1
18
+ }
19
+
20
+ autoload :Cache, 'octopress-code-highlighter/cache'
21
+ autoload :OptionsParser, 'octopress-code-highlighter/options_parser'
22
+ autoload :Renderer, 'octopress-code-highlighter/renderer'
23
+
24
+ def self.highlight(code, options={})
25
+ Renderer.new(code, options).highlight
26
+ end
27
+
28
+ def self.read_cache(code, options={})
29
+ Cache.read_cache(code, options)
30
+ end
31
+
32
+ def self.parse_markup(input, defaults={})
33
+ OptionsParser.new(input).parse_markup(defaults)
34
+ end
35
+
36
+ def self.clean_markup(input)
37
+ OptionsParser.new(input).clean_markup
38
+ end
39
+
40
+ def self.highlight_failed(error, syntax, markup, code, file = nil)
41
+ code_snippet = code.split("\n")[0..9].map{|l| " #{l}" }.join("\n")
42
+ fail_message = "\nError while parsing the following markup#{" in #{file}" if file}:\n\n".red
43
+ fail_message += " #{markup}\n#{code_snippet}\n"
44
+ fail_message += "#{" ..." if code.split("\n").size > 10}\n"
45
+ fail_message += "\nValid Syntax:\n\n#{syntax}\n".yellow
46
+ fail_message += "\nError:\n\n#{error.message}".red
47
+ $stderr.puts fail_message.chomp
48
+ raise ArgumentError
49
+ end
50
+ end
51
+ end
52
+
53
+ class CodeHighlighter < Octopress::Ink::Plugin
54
+ def initialize(name, type)
55
+ @assets_path = File.expand_path(File.join(File.dirname(__FILE__), '../assets'))
56
+ @version = Octopress::CodeHighlighter::VERSION
57
+ @description = "For beautiful code snippets."
58
+ super
59
+ end
60
+
61
+ def add_assets
62
+ add_sass 'code.scss'
63
+ end
64
+ end
65
+
66
+ Octopress::Ink.register_plugin(CodeHighlighter, 'octopress-code-highlighter', 'plugin')
67
+
@@ -0,0 +1,30 @@
1
+ module Octopress
2
+ module CodeHighlighter
3
+ class Cache
4
+ CODE_CACHE_DIR = '.code-style-cache'
5
+
6
+ class << self
7
+ def read_cache(code, options)
8
+ cache_label = options[:cache_label] || options[:lang] || ''
9
+ path = get_cache_path(CODE_CACHE_DIR, cache_label, options.to_s + code)
10
+ File.exist?(path) ? File.read(path) : nil unless path.nil?
11
+ end
12
+
13
+ def write_to_cache(contents, options)
14
+ FileUtils.mkdir_p(CODE_CACHE_DIR) unless File.directory?(CODE_CACHE_DIR)
15
+ cache_label = options[:cache_label] || options[:lang] || ''
16
+ path = get_cache_path(CODE_CACHE_DIR, cache_label, options.to_s + contents)
17
+ File.open(path, 'w') do |f|
18
+ f.print(contents)
19
+ end
20
+ end
21
+
22
+ def get_cache_path(dir, label, str)
23
+ label += '-' unless label === ''
24
+ File.join(dir, "#{label}#{Digest::MD5.hexdigest(str)}.html")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,129 @@
1
+ module Octopress
2
+ module CodeHighlighter
3
+ class OptionsParser
4
+ attr_accessor :input
5
+
6
+ def initialize(markup)
7
+ @input = markup.strip
8
+ end
9
+
10
+ def clean_markup
11
+ input.sub(/\s*lang:\s*\S+/i,'')
12
+ .sub(/\s*title:\s*(("(.+?)")|('(.+?)')|(\S+))/i,'')
13
+ .sub(/\s*url:\s*(\S+)/i,'')
14
+ .sub(/\s*link_text:\s*(("(.+?)")|('(.+?)')|(\S+))/i,'')
15
+ .sub(/\s*mark:\s*\d\S*/i,'')
16
+ .sub(/\s*linenos:\s*\w+/i,'')
17
+ .sub(/\s*start:\s*\d+/i,'')
18
+ .sub(/\s*end:\s*\d+/i,'')
19
+ .sub(/\s*range:\s*\d+-\d+/i,'')
20
+ .sub(/\s*escape:\s*\w+/i,'')
21
+ end
22
+
23
+ def parse_markup(defaults = {})
24
+ options = {
25
+ lang: lang,
26
+ url: url,
27
+ title: title,
28
+ linenos: linenos,
29
+ marks: marks,
30
+ link_text: link_text,
31
+ start: start,
32
+ end: endline,
33
+ escape: escape
34
+ }
35
+ options = options.delete_if { |k,v| v.nil? }
36
+ defaults.merge(options)
37
+ end
38
+
39
+ def lang
40
+ extract(/\s*lang:\s*(\S+)/i)
41
+ end
42
+
43
+ def url
44
+ extract(/\s*url:\s*(("(.+?)")|('(.+?)')|(\S+))/i, [3, 5, 6])
45
+ end
46
+
47
+ def title
48
+ extract(/\s*title:\s*(("(.+?)")|('(.+?)')|(\S+))/i, [3, 5, 6])
49
+ end
50
+
51
+ def linenos
52
+ boolize(extract(/\s*linenos:\s*(\w+)/i))
53
+ end
54
+
55
+ def escape
56
+ boolize(extract(/\s*escape:\s*(\w+)/i))
57
+ end
58
+
59
+ # Public: Matches pattern for line marks and returns array of line
60
+ # numbers to mark
61
+ #
62
+ # Example input
63
+ # Input: "mark:1,5-10,2"
64
+ # Output: [1,2,5,6,7,8,9,10]
65
+ #
66
+ # Returns an array of integers corresponding to the lines which are
67
+ # indicated as marked
68
+ def marks
69
+ marks = []
70
+ if input =~ / *mark:(\d\S*)/i
71
+ marks = $1.gsub /(\d+)-(\d+)/ do
72
+ ($1.to_i..$2.to_i).to_a.join(',')
73
+ end
74
+ marks = marks.split(',').collect {|s| s.to_i}.sort
75
+ end
76
+ marks
77
+ end
78
+
79
+ def link_text
80
+ extract(/\s*link[-_]text:\s*(("(.+?)")|('(.+?)')|(\S+))/i, [3, 5, 6], 'link')
81
+ end
82
+
83
+ def start
84
+ if range
85
+ range.first
86
+ else
87
+ num = extract(/\s*start:\s*(\d+)/i)
88
+ num = num.to_i unless num.nil?
89
+ num
90
+ end
91
+ end
92
+
93
+ def endline
94
+ if range
95
+ range.last
96
+ else
97
+ num = extract(/\s*end:\s*(\d+)/i)
98
+ num = num.to_i unless num.nil?
99
+ num
100
+ end
101
+ end
102
+
103
+ def range
104
+ if input.match(/ *range:(\d+)-(\d+)/i)
105
+ [$1.to_i, $2.to_i]
106
+ end
107
+ end
108
+
109
+ def extract(regexp, indices_to_try = [1], default = nil)
110
+ thing = input.match(regexp)
111
+ if thing.nil?
112
+ default
113
+ else
114
+ indices_to_try.each do |index|
115
+ return thing[index] if thing[index]
116
+ end
117
+ end
118
+ end
119
+
120
+ def boolize(str)
121
+ return nil if str.nil?
122
+ return true if str == true || str =~ (/(true|t|yes|y|1)$/i)
123
+ return false if str == false || str =~ (/(false|f|no|n|0)$/i) || str.strip.size > 1
124
+ return str
125
+ end
126
+ end
127
+ end
128
+ end
129
+
@@ -0,0 +1,165 @@
1
+ module Octopress
2
+ module CodeHighlighter
3
+ class Renderer
4
+ attr_reader :code, :options, :lang
5
+
6
+ def initialize(code, options = {})
7
+ @code = code
8
+ @options = options.delete_if { |k,v| v.nil? }
9
+ @options = DEFAULTS.merge(@options)
10
+ @aliases = stringify_keys(@options[:aliases] || {})
11
+ @lang = @options[:lang]
12
+ @options[:title] ||= ' ' if @options[:url]
13
+ @renderer = select_renderer
14
+ end
15
+
16
+ def select_renderer
17
+ begin
18
+ require 'rouge'
19
+ return 'rouge' if defined?(Rouge)
20
+ rescue LoadError; end
21
+ begin
22
+ require 'pygments.rb'
23
+ return 'pygments' if defined?(Pygments)
24
+ rescue LoadError
25
+ puts "To highlight code, install the gem pygments.rb or rouge.".red
26
+ return 'plain'
27
+ end
28
+ end
29
+
30
+ def highlight
31
+ if cache = Cache.read_cache(code, options)
32
+ cache
33
+ else
34
+ rendered_code = render
35
+ rendered_code = encode_liquid(rendered_code)
36
+ rendered_code = tableize_code(rendered_code)
37
+ rendered_code = "<figure class='code-highlight-figure'>#{caption}#{rendered_code}</figure>"
38
+ rendered_code = "{% raw %}#{rendered_code}{% endraw %}" if options[:escape]
39
+ Cache.write_to_cache(rendered_code, options) unless options[:no_cache]
40
+ rendered_code
41
+ end
42
+ end
43
+
44
+ def plain?
45
+ options[:lang].empty? || options[:lang] == 'plain'
46
+ end
47
+
48
+ def render
49
+ case @renderer
50
+ when 'pygments'
51
+ code = render_pygments
52
+ when 'rouge'
53
+ code = render_rouge
54
+ else
55
+ code = render_plain
56
+ end
57
+
58
+ if code =~ /<pre.+?>(.+)<\/pre>/m
59
+ $1.strip
60
+ else
61
+ code
62
+ end
63
+ end
64
+
65
+ def render_plain
66
+ @code.gsub('<','&lt;')
67
+ end
68
+
69
+ def render_pygments
70
+ if lexer = Pygments::Lexer.find(lang) || Pygments::Lexer.find(@aliases[lang])
71
+ begin
72
+ lexer.highlight @code, {
73
+ formatter: 'html',
74
+ options: {
75
+ encoding: 'utf-8'
76
+ }
77
+ }
78
+ rescue MentosError => e
79
+ raise e
80
+ end
81
+ else
82
+ render_plain
83
+ end
84
+ end
85
+
86
+ def render_rouge
87
+ if lexer = Rouge::Lexer.find(lang) || Rouge::Lexer.find(@aliases[lang])
88
+ formatter = ::Rouge::Formatters::HTML.new()
89
+ formatter.format(lexer.lex(@code))
90
+ else
91
+ render_plain
92
+ end
93
+ end
94
+
95
+ def caption
96
+ if options[:title]
97
+ figcaption = "<figcaption class='code-highlight-caption'><span class='code-highlight-caption-title'>#{options[:title]}</span>"
98
+ figcaption += "<a class='code-highlight-caption-link' href='#{options[:url]}'>#{(options[:link_text] || 'link').strip}</a>" if options[:url]
99
+ figcaption += "</figcaption>"
100
+ else
101
+ ''
102
+ end
103
+ end
104
+
105
+ def tableize_code(code)
106
+ start = options[:start]
107
+ lines = options[:linenos]
108
+ marks = options[:marks]
109
+
110
+ table = "<div class='code-highlight'>"
111
+ table += "<pre class='code-highlight-pre'>"
112
+ code.lines.each_with_index do |line,index|
113
+ classes = 'code-highlight-row'
114
+ classes += lines ? ' numbered' : ' unnumbered'
115
+ if marks.include? index + start
116
+ classes += ' marked-line'
117
+ classes += ' start-marked-line' unless marks.include? index - 1 + start
118
+ classes += ' end-marked-line' unless marks.include? index + 1 + start
119
+ end
120
+ line = line.strip.empty? ? ' ' : line
121
+ table += "<div data-line='#{index + start}' class='#{classes}'><div class='code-highlight-line'>#{line}</div></div>"
122
+ end
123
+ table +="</pre></div>"
124
+ end
125
+
126
+ # Public:
127
+ #
128
+ #
129
+ def get_range(code, start, endline)
130
+ length = code.lines.count
131
+ start
132
+ endline ||= length
133
+ if start > 1 or endline < length
134
+ raise "#{filepath} is #{length} lines long, cannot begin at line #{start}" if start > length
135
+ raise "#{filepath} is #{length} lines long, cannot read beyond line #{endline}" if endline > length
136
+ code = code.split(/\n/).slice(start - 1, endline + 1 - start).join("\n")
137
+ end
138
+ code
139
+ end
140
+
141
+ def encode_liquid(code)
142
+ code.gsub(/{{/, '&#x7b;&#x7b;')
143
+ .gsub(/{%/, '&#x7b;&#x25;')
144
+ end
145
+
146
+ private
147
+
148
+ def stringify_keys(hash)
149
+ hash.inject({}){|result, (key, value)|
150
+ new_key = case key
151
+ when String then key.to_s
152
+ else key
153
+ end
154
+ new_value = case value
155
+ when Hash then stringify_keys(value)
156
+ else value
157
+ end
158
+ result[new_key] = new_value
159
+ result
160
+ }
161
+ end
162
+ end
163
+ end
164
+ end
165
+
@@ -0,0 +1,6 @@
1
+ module Octopress
2
+ module CodeHighlighter
3
+ VERSION = "3.0.0"
4
+ end
5
+ end
6
+
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'octopress-code-highlighter/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "octopress-code-highlighter"
8
+ gem.version = Octopress::CodeHighlighter::VERSION
9
+ gem.authors = ["Brandon Mathis"]
10
+ gem.email = ["brandon@imathis.com"]
11
+ gem.description = %q{Octopress's core plugin for rendering nice code blocks}
12
+ gem.summary = %q{Octopress's core plugin for rendering nice code blocks}
13
+ gem.homepage = "https://github.com/octopress/code-highlighter"
14
+ gem.license = "MIT"
15
+
16
+ gem.add_runtime_dependency 'colorator', '~> 0.1.0'
17
+ gem.add_runtime_dependency 'octopress-ink', '>= 1.0.0.alpha.33'
18
+
19
+ gem.add_development_dependency 'pry-debugger'
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec'
22
+ gem.add_development_dependency 'rouge', '~> 1.3.2'
23
+
24
+ gem.files = `git ls-files`.split($/).reject {|f| f =~ /^demo\// }
25
+ gem.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+
3
+ describe Octopress::CodeHighlighter do
4
+ let(:wrapper) do
5
+ Proc.new do |stuff, numbers|
6
+ [
7
+ "<figure class='code-highlight-figure'>",
8
+ "<div class='code-highlight'>",
9
+ "<pre class='code-highlight-pre'>#{stuff}</pre>",
10
+ "</div></figure>"
11
+ ].join
12
+ end
13
+ end
14
+
15
+ let(:expected_output_no_options) do
16
+ stuff = <<-EOF
17
+ <figure class='code-highlight-figure'><div class='code-highlight'><pre class='code-highlight-pre'><div data-line='1' class='code-highlight-row numbered'><div class='code-highlight-line'> require "hi-there-honey"
18
+ </div></div><div data-line='2' class='code-highlight-row numbered'><div class='code-highlight-line'> </div></div><div data-line='3' class='code-highlight-row numbered'><div class='code-highlight-line'> def hi-there-honey
19
+ </div></div><div data-line='4' class='code-highlight-row numbered'><div class='code-highlight-line'> HiThereHoney.new("your name")
20
+ </div></div><div data-line='5' class='code-highlight-row numbered'><div class='code-highlight-line'> end
21
+ </div></div><div data-line='6' class='code-highlight-row numbered'><div class='code-highlight-line'> </div></div><div data-line='7' class='code-highlight-row numbered'><div class='code-highlight-line'> hi-there-honey
22
+ </div></div><div data-line='8' class='code-highlight-row numbered'><div class='code-highlight-line'> # => "Hi, your name"
23
+ </div></div></pre></div></figure>
24
+ EOF
25
+ stuff.strip
26
+ end
27
+
28
+ let(:expected_output_lang_ruby) do
29
+ stuff = <<-EOF
30
+ {% raw %}<figure class='code-highlight-figure'><div class='code-highlight'><pre class='code-highlight-pre'><div data-line='1' class='code-highlight-row numbered'><div class='code-highlight-line'><span class="nb">require</span> <span class="s2">"hi-there-honey"</span>
31
+ </div></div><div data-line='2' class='code-highlight-row numbered'><div class='code-highlight-line'> </div></div><div data-line='3' class='code-highlight-row numbered'><div class='code-highlight-line'> <span class="k">def</span> <span class="nf">hi</span><span class="o">-</span><span class="n">there</span><span class="o">-</span><span class="n">honey</span>
32
+ </div></div><div data-line='4' class='code-highlight-row numbered'><div class='code-highlight-line'> <span class="no">HiThereHoney</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"your name"</span><span class="p">)</span>
33
+ </div></div><div data-line='5' class='code-highlight-row numbered'><div class='code-highlight-line'> <span class="k">end</span>
34
+ </div></div><div data-line='6' class='code-highlight-row numbered'><div class='code-highlight-line'> </div></div><div data-line='7' class='code-highlight-row numbered'><div class='code-highlight-line'> <span class="n">hi</span><span class="o">-</span><span class="n">there</span><span class="o">-</span><span class="n">honey</span>
35
+ </div></div><div data-line='8' class='code-highlight-row numbered'><div class='code-highlight-line'> <span class="c1"># =&gt; "Hi, your name"</span></div></div></pre></div></figure>{% endraw %}
36
+ EOF
37
+ end
38
+
39
+ let(:code) do
40
+ <<-EOF
41
+ require "hi-there-honey"
42
+
43
+ def hi-there-honey
44
+ HiThereHoney.new("your name")
45
+ end
46
+
47
+ hi-there-honey
48
+ # => "Hi, your name"
49
+ EOF
50
+ end
51
+
52
+ let(:markup) do
53
+ [
54
+ "lang:abc",
55
+ 'title:"Hello"',
56
+ "url:http://something.com/hi/fuaiofnioaf.html",
57
+ "link_text:'get it here'",
58
+ "mark:5,8-10,15",
59
+ "linenos: yes",
60
+ "start: 5",
61
+ "end: 15",
62
+ "range: 5-15"
63
+ ].join(" ")
64
+ end
65
+
66
+ let(:bad_markup) do
67
+ [
68
+ "lang:ruby",
69
+ 'title:"Hello"',
70
+ "url:http://something.com/hi/fuaiofnioaf.html",
71
+ "link_text: get it here",
72
+ "mark:5,8-10,15",
73
+ "linenos: yes",
74
+ "start: 5",
75
+ "end: 15",
76
+ "range: 5-15"
77
+ ].join(" ")
78
+ end
79
+
80
+ let(:options) do
81
+ {
82
+ lang: "abc",
83
+ url: "http://something.com/hi/fuaiofnioaf.html",
84
+ title: "Hello",
85
+ linenos: true,
86
+ marks: [5, 8, 9, 10, 15],
87
+ link_text: "get it here",
88
+ start: 5,
89
+ end: 15
90
+ }
91
+ end
92
+
93
+ describe ".highlight" do
94
+ it "returns HTML for an empty code block" do
95
+ expect(described_class.highlight("", {})).to eql(wrapper.call("", ""))
96
+ end
97
+
98
+ context "with no options" do
99
+ it "returns the right HTML for a given set of code" do
100
+ expect(described_class.highlight(code, {})).to eql(expected_output_no_options)
101
+ end
102
+ end
103
+
104
+ context "with a language" do
105
+ it "returns the right HTML for a given set of code" do
106
+ expect(described_class.highlight(code, { lang: 'abc', aliases: {'abc'=>'ruby'}, escape: true })).to eql(expected_output_lang_ruby.chop)
107
+ end
108
+ end
109
+ end
110
+
111
+ describe ".highlight_failed" do
112
+ #described_class.highlight_failed(error, syntax, markup, code, file = nil)
113
+ end
114
+
115
+ describe ".parse_markup" do
116
+ context "with no defaults" do
117
+ it "parses the defaults correctly" do
118
+ expect(described_class.parse_markup(markup, {})).to eql(options)
119
+ end
120
+ end
121
+
122
+ context "with defaults with a nil value" do
123
+ it "overrides the nil values" do
124
+ expect(described_class.parse_markup(markup, { lang: nil })).to eql(options)
125
+ end
126
+ end
127
+ end
128
+
129
+ describe ".clean_markup" do
130
+
131
+ it "returns an empty string with good markup" do
132
+ expect(described_class.clean_markup(markup)).to eql("")
133
+ end
134
+
135
+ it "returns erroneous text that isn't part of the markup" do
136
+ expect(described_class.clean_markup(bad_markup)).to eql(" it here")
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,3 @@
1
+ $:.unshift(File.expand_path("../lib", File.dirname(__FILE__)))
2
+ require "octopress-code-highlighter"
3
+
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: octopress-code-highlighter
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brandon Mathis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorator
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: octopress-ink
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0.alpha.33
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0.alpha.33
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-debugger
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rouge
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.3.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 1.3.2
97
+ description: Octopress's core plugin for rendering nice code blocks
98
+ email:
99
+ - brandon@imathis.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .rspec
106
+ - .travis.yml
107
+ - CHANGELOG.md
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - assets/stylesheets/_code-style.scss
113
+ - assets/stylesheets/_solarized.scss
114
+ - assets/stylesheets/code.scss
115
+ - lib/octopress-code-highlighter.rb
116
+ - lib/octopress-code-highlighter/cache.rb
117
+ - lib/octopress-code-highlighter/options_parser.rb
118
+ - lib/octopress-code-highlighter/renderer.rb
119
+ - lib/octopress-code-highlighter/version.rb
120
+ - octopress-code-highlighter.gemspec
121
+ - spec/pygments_spec.rb
122
+ - spec/spec_helper.rb
123
+ homepage: https://github.com/octopress/code-highlighter
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.1.11
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Octopress's core plugin for rendering nice code blocks
147
+ test_files: []