polytexnic 0.9.9 → 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 821fd49a7e64e757649554fe43230066a4adabde
4
- data.tar.gz: 691430267b60c4c687bb84f7ba6fb70e1a0ea9c6
3
+ metadata.gz: 7d1357e1fdbafae6d29c54a966649109ceaef92f
4
+ data.tar.gz: a15026b6f6b37bc2c37fe9d9fa81571b62e3bf59
5
5
  SHA512:
6
- metadata.gz: d44043bfb3c04a58e30e1a85e5cf1c0a0046f92018c27e22310b22e145c2c1bceb23077d2cedabcdc7ec8db0802d9d7bbdcbae43a946edc7a2854cf33bda791e
7
- data.tar.gz: d07ba71163b7acc22d019d4d7b798838512a092db4791e2d56a4a867ad07bb220b79e6454e20646f9e5c19b130bdcde3161d256ce1ab35e6ebbbc4f307334bb6
6
+ metadata.gz: a38508985733c163261cd035a86d60c793b22c4027d737dee582431859163765ace742a67649599aba8225739f672f3018104e7c06f0225d899aa2e592cc95f3
7
+ data.tar.gz: da175c2ed00d3b95b8ff7c849123ec76dc6fb4980e3fb445a96fff73f93b75e93918ba461998605d121217a807bf8abad75fb0b7aef2220c0b91a324c2007413
@@ -0,0 +1,247 @@
1
+ module CodeInclusion
2
+ class CodeInclusionException < Exception; end;
3
+
4
+ class Code
5
+ CODE_REGEX = /^\s*%=\s+<<\s*\( # opening
6
+ \s*([^\s]+?) # path to file
7
+ (?:\[(.+?)\])? # optional section name
8
+ (?:,\s*tag:\s*([\w\.\/\-]+?))? # optional git tag
9
+ (?:,\s*lang:\s*(\w+))? # optional lang
10
+ (,\s*options:\s*.*)? # optional options
11
+ \s*\) # closing paren
12
+ /x
13
+
14
+ DEFAULT_LANGUAGE = 'text'
15
+
16
+ # Returns an instance of CodeInclusion::Code or nil
17
+ def self.for(line)
18
+ if (line =~ CODE_REGEX)
19
+ opts = {}
20
+ opts[:tag] = $3
21
+ opts[:custom_language] = $4
22
+ opts[:highlight] = $5
23
+ new($1, $2, opts)
24
+ end
25
+ end
26
+
27
+ attr_reader :filename, :sectionname, :opts
28
+
29
+ def initialize(filename, sectionname, opts)
30
+ @filename = filename
31
+ @sectionname = sectionname
32
+ @opts = opts
33
+ end
34
+
35
+ # Returns the formatted code or an error message
36
+ def to_s
37
+ return unless filename
38
+
39
+ result = []
40
+ result << "%= lang:#{language}#{opts[:highlight]}"
41
+ result << '\begin{code}'
42
+ result.concat(raw_code)
43
+ result << '\end{code}'
44
+
45
+ rescue CodeInclusionException => e
46
+ code_error(e.message)
47
+ end
48
+
49
+ def raw_code
50
+ reader.read
51
+ end
52
+
53
+ private
54
+
55
+ # Manufacture a reader of the appriopriate type
56
+ def reader
57
+ @reader ||=
58
+ if opts[:tag]
59
+ GitTaggedFileReader.new(filename,
60
+ sectionname,
61
+ opts.delete(:tag),
62
+ opts)
63
+ elsif sectionname
64
+ SectionReader.new(filename, sectionname)
65
+ else
66
+ FileReader.new(filename)
67
+ end
68
+ end
69
+
70
+ def language
71
+ extension_array = File.extname(filename).scan(/\.(.*)/).first
72
+ lang_from_extension = extension_array.nil? ? nil : extension_array[0]
73
+ (opts[:custom_language] || lang_from_extension || DEFAULT_LANGUAGE)
74
+ end
75
+
76
+ def code_error(details)
77
+ ["\\verb+ERROR: #{details}+"]
78
+ end
79
+ end
80
+
81
+
82
+ # GitTaggedFileReader retrieves code from a
83
+ # tagged file in your local git repository.
84
+ #
85
+ # Example: <<(lib/polytexnic/literal.rb, tag: v0.9.4)
86
+ class GitTaggedFileReader
87
+
88
+ def self.git
89
+ Git.new
90
+ end
91
+
92
+ attr_reader :filename, :sectionname, :tagname, :opts, :git
93
+
94
+ def initialize(filename, sectionname, tagname, opts={}, git=self.class.git)
95
+ @filename = filename
96
+ @sectionname = sectionname
97
+ @tagname = tagname
98
+ @opts = opts
99
+ @git = git
100
+ end
101
+
102
+ def read
103
+ ensure_tag_exists!
104
+
105
+ Dir.mktmpdir {|tmpdir|
106
+ checkout_file!(tmpdir)
107
+ read_file(tmpdir)
108
+ }
109
+ end
110
+
111
+ private
112
+
113
+ def ensure_tag_exists!
114
+ unless git.tag_exists?(tagname)
115
+ raise(CodeInclusionException, "Tag '#{tagname}' does not exist.")
116
+ end
117
+ end
118
+
119
+ def checkout_file!(tmpdir)
120
+ output = git.checkout(tmpdir, filename, tagname)
121
+ unless git.checkout_succeeded?
122
+ raise(CodeInclusionException, improve_error_message(output, tmpdir))
123
+ end
124
+ end
125
+
126
+ def read_file(tmpdir)
127
+ tmpfilename = File.join(tmpdir, filename)
128
+ CodeInclusion::Code.new(tmpfilename, sectionname, opts).raw_code
129
+ end
130
+
131
+ def improve_error_message(msg, tmpdir)
132
+ msg.gsub(/#{tmpdir}/, '').chomp(".\n") + " in tag #{tagname}."
133
+ end
134
+
135
+
136
+ class Git
137
+ def checkout(tmpdir, filename, tagname)
138
+ `git --work-tree=#{tmpdir} checkout #{tagname} #{filename} 2>&1`
139
+ end
140
+
141
+ def checkout_succeeded?
142
+ $? == 0
143
+ end
144
+
145
+ def tags
146
+ `git tag`
147
+ end
148
+
149
+ def tag_exists?(tagname)
150
+ tags.split("\n").include?(tagname)
151
+ end
152
+ end
153
+ end
154
+
155
+
156
+ # FileReader retrieves code from a file on disk.
157
+ #
158
+ # Example: <<(lib/polytexnic/literal.rb)
159
+ class FileReader
160
+ attr_reader :filename
161
+
162
+ def initialize(filename)
163
+ @filename = filename
164
+ end
165
+
166
+ def read
167
+ ensure_file_exists!
168
+ File.read(filename).split("\n")
169
+ end
170
+
171
+ def ensure_file_exists!
172
+ unless File.exist?(filename)
173
+ raise(CodeInclusionException, "File '#{filename}' does not exist")
174
+ end
175
+ end
176
+ end
177
+
178
+
179
+ # SectionReader retrieves code from a marked section in a file on disk.
180
+ #
181
+ # Example: <<(lib/polytexnic/literal.rb[my_section])
182
+ #
183
+ # Sections are delineated by '#// begin section_name' and '#// end',
184
+ # for example:
185
+ #
186
+ # #// begin my_section
187
+ # some code
188
+ # #// end
189
+ class SectionReader < FileReader
190
+ attr_reader :lines, :sectionname
191
+
192
+ def initialize(filename, sectionname)
193
+ super(filename)
194
+ @sectionname = sectionname
195
+ end
196
+
197
+ def read
198
+ @lines = super
199
+ ensure_section_exists!
200
+ lines.slice(index_of_first_line, length)
201
+ end
202
+
203
+ private
204
+ def exist?
205
+ !!index_of_section_begin
206
+ end
207
+
208
+ def index_of_section_begin
209
+ @section_begin_i ||=
210
+ lines.index {|line| clean(line) == section_begin_text }
211
+ end
212
+
213
+ def index_of_first_line
214
+ @first_line_i ||= index_of_section_begin + 1
215
+ end
216
+
217
+ def length
218
+ lines.slice(index_of_first_line, lines.size).index { |line|
219
+ clean(line) == (section_end_text)
220
+ }
221
+ end
222
+
223
+ def marker
224
+ '#//'
225
+ end
226
+
227
+ def section_begin_text
228
+ "#{marker} begin #{sectionname}"
229
+ end
230
+
231
+ def section_end_text
232
+ "#{marker} end"
233
+ end
234
+
235
+ def clean(str)
236
+ str.strip.squeeze(" ")
237
+ end
238
+
239
+ def ensure_section_exists!
240
+ unless exist?
241
+ section_err = "Could not find section header '#{section_begin_text}'"
242
+ file_err = " in file '#{filename}'"
243
+ raise(CodeInclusionException, section_err + file_err)
244
+ end
245
+ end
246
+ end
247
+ end
@@ -6,15 +6,6 @@ module Polytexnic
6
6
  # %= lang: <language>[, options: ...]
7
7
  LANG_REGEX = /^\s*%=\s+lang:\s*(\w+)(?:,\s*options:(.*))?/
8
8
 
9
- # Matches the line for code inclusion.
10
- # %= <</path/to/code.ext
11
- CODE_INCLUSION_REGEX = /^\s*%=\s+<<\s*\( # opening
12
- \s*([^\s]+?) # path to file
13
- (?:\[(.+?)\])? # optional section name
14
- (?:,\s*lang:\s*(\w+))? # optional lang
15
- (,\s*options:\s*.*)? # optional options
16
- \s*\) # closing paren
17
- /x
18
9
 
19
10
  # Makes the caches for literal environments.
20
11
  def cache_literal(polytex, format = :html)
@@ -74,7 +65,7 @@ module Polytexnic
74
65
  elsif line =~ /\s*\\end\{codelisting\}/ && !in_verbatim
75
66
  in_codelisting = false
76
67
  output << line
77
- elsif line =~ CODE_INCLUSION_REGEX && !in_verbatim
68
+ elsif (included_code = CodeInclusion::Code.for(line)) && !in_verbatim
78
69
  # Reduce to a previously solved problem.
79
70
  # We transform
80
71
  # %= <<(/path/to/file.rb)
@@ -84,10 +75,7 @@ module Polytexnic
84
75
  # <content of file or section.rb>
85
76
  # \end{code}
86
77
  # and then prepend the code to the current `lines` array.
87
- filename, sectionname, custom_language, highlight_options = $1, $2, $3, $4
88
- if filename
89
- lines.unshift(*include_code(filename, sectionname, custom_language, highlight_options))
90
- end
78
+ lines.unshift(*included_code.to_s)
91
79
  elsif line.begin_literal?
92
80
  in_verbatim = true
93
81
  literal_type = line.literal_type
@@ -160,34 +148,6 @@ module Polytexnic
160
148
  end
161
149
  end
162
150
 
163
- # Returns the marked up file or section to be included,
164
- # or an error message if file or section does not exist.
165
- def include_code(filename, sectionname, custom_language, highlight_options)
166
- reader = (sectionname ? IncludedSectionReader : IncludedFileReader).new
167
- lang = "#{code_language(filename, custom_language)}#{highlight_options}"
168
- code = ["%= lang:#{lang}"]
169
- code << '\begin{code}'
170
- code.concat(reader.read(filename, sectionname))
171
- code << '\end{code}'
172
-
173
- rescue FileNotFound => e
174
- code_error("File '#{e.message}' does not exist")
175
- rescue SectionNotFound => e
176
- msg = e.message
177
- err = "Could not find section header '#{msg}' in file '#{filename}'"
178
- code_error(err)
179
- end
180
-
181
- def code_error(details)
182
- "\\verb+ERROR: #{details}+"
183
- end
184
-
185
- def code_language(filename, custom_language)
186
- extension_array = File.extname(filename).scan(/\.(.*)/).first
187
- lang_from_extension = extension_array.nil? ? nil : extension_array[0]
188
- language = custom_language || lang_from_extension || 'text'
189
- end
190
-
191
151
  # Returns a permanent salt for the syntax highlighting cache.
192
152
  def code_salt
193
153
  'fbbc13ed4a51e27608037365e1d27a5f992b6339'
@@ -288,61 +248,9 @@ module Polytexnic
288
248
  literal_type
289
249
  end
290
250
  end
291
-
292
-
293
- class FileNotFound < Exception; end;
294
- class IncludedFileReader
295
- def read(filename, _)
296
- raise(FileNotFound, filename) unless File.exist?(filename)
297
- File.read(filename).split("\n")
298
- end
299
- end
300
-
301
- class SectionNotFound < Exception; end;
302
- class IncludedSectionReader < IncludedFileReader
303
- attr_reader :lines, :sectionname
304
-
305
- def read(filename, sectionname)
306
- @lines = super
307
- @sectionname = sectionname
308
-
309
- raise(SectionNotFound, section_begin_text) unless exist?
310
- lines.slice(index_of_first_line, length)
311
- end
312
-
313
- private
314
- def exist?
315
- !!index_of_section_begin
316
- end
317
-
318
- def index_of_section_begin
319
- @section_begin_i ||= lines.index(section_begin_text)
320
- end
321
-
322
- def index_of_first_line
323
- @first_line_i ||= index_of_section_begin + 1
324
- end
325
-
326
- def length
327
- lines.slice(index_of_first_line, lines.size).index(section_end_text)
328
- end
329
-
330
- def marker
331
- '#//'
332
- end
333
-
334
- def section_begin_text
335
- "#{marker} begin #{sectionname}"
336
- end
337
-
338
- def section_end_text
339
- "#{marker} end"
340
- end
341
- end
342
251
  end
343
252
  end
344
253
 
345
-
346
254
  class String
347
255
  include Polytexnic::Literal
348
256
 
@@ -391,3 +299,4 @@ class String
391
299
  end.join('|')
392
300
  end
393
301
  end
302
+
@@ -27,6 +27,7 @@ module Polytexnic
27
27
  headings(doc)
28
28
  sout(doc)
29
29
  kode(doc)
30
+ coloredtext(doc)
30
31
  filepath(doc)
31
32
  backslash_break(doc)
32
33
  spaces(doc)
@@ -601,6 +602,14 @@ module Polytexnic
601
602
  end
602
603
  end
603
604
 
605
+ def coloredtext(doc)
606
+ doc.xpath('//coloredtext').each do |node|
607
+ node.name = 'span'
608
+ node['style'] = "color: #{node['color']}"
609
+ clean_node node, 'color'
610
+ end
611
+ end
612
+
604
613
  # Converts filesystem path (\filepath) to the proper tag.
605
614
  def filepath(doc)
606
615
  doc.xpath('//filepath').each do |node|
@@ -1,5 +1,6 @@
1
1
  # encoding=utf-8
2
2
  require 'polytexnic/literal'
3
+ require 'polytexnic/code_inclusion'
3
4
  require 'polytexnic/preprocessors/html'
4
5
  require 'polytexnic/preprocessors/latex'
5
6
  require 'polytexnic/preprocessors/polytex'
@@ -112,6 +112,7 @@ module Polytexnic
112
112
  \newcommand{\codecaption}[1]{\xmlelt{heading}{#1}}
113
113
  \newcommand{\sout}[1]{\xmlelt{sout}{#1}}
114
114
  \newcommand{\kode}[1]{\xmlelt{kode}{#1}}
115
+ \newcommand{\coloredtext}[2]{\xmlelt{coloredtext}{\AddAttToCurrent{color}{#1}#2}}
115
116
  \newcommand{\filepath}[1]{\xmlelt{filepath}{#1}}
116
117
  \newcommand{\image}[1]{\xmlelt{image}{#1}}
117
118
  \newcommand{\imagebox}[1]{\xmlelt{imagebox}{#1}}
@@ -1,3 +1,3 @@
1
1
  module Polytexnic
2
- VERSION = "0.9.9"
2
+ VERSION = "0.9.10"
3
3
  end
@@ -238,6 +238,140 @@ describe Polytexnic::Pipeline do
238
238
  end
239
239
  it { should include "ERROR: File 'foobar.rb' does not exist" }
240
240
  end
241
+
242
+
243
+
244
+ context "from a git tag" do
245
+ shared_examples "an inclusion" do
246
+ it "resembles the given output" do
247
+ allow(CodeInclusion::GitTaggedFileReader).to receive(:git).
248
+ and_return(FakeGit.new)
249
+ expect(processed_text).to resemble(output)
250
+ end
251
+ end
252
+
253
+ context "the tag and file exist" do
254
+ before(:all) do
255
+ class FakeGit < CodeInclusion::GitTaggedFileReader::Git
256
+ # Write fake data to location on disk where git _would_ have placed the file
257
+ def checkout(tmpdir, filename, tagname)
258
+ f = File.join(tmpdir, filename)
259
+ File.open(f, 'w') { |file| file.write("Fake data") }
260
+ end
261
+ def tag_exists?(tagname)
262
+ true
263
+ end
264
+ def checkout_succeeded?
265
+ true
266
+ end
267
+ end
268
+ end
269
+
270
+ context "with tag only" do
271
+ let(:polytex) do <<-'EOS'
272
+ %= <<(tagged_file.rb, tag: fake_tag.1.0)
273
+ EOS
274
+ end
275
+ let(:output) do <<-'EOS'
276
+ <div class="code">
277
+ <div class="highlight">
278
+ <pre>
279
+ <span class="no">Fake</span> <span class="n">data</span>
280
+ </pre>
281
+ </div>
282
+ EOS
283
+ end
284
+ it_behaves_like "an inclusion"
285
+ end
286
+
287
+ context "with other params" do
288
+ let(:output) do <<-'EOS'
289
+ <div class="code">
290
+ <div class="highlight">
291
+ <pre>Fake data</pre>
292
+ </div>
293
+ </div>
294
+ EOS
295
+ end
296
+
297
+ context "with tag and lang" do
298
+ let(:polytex) do <<-'EOS'
299
+ %= <<(tagged_file.rb, tag: slashes/and-dashes-are/ok/too, lang: tex)
300
+ EOS
301
+ end
302
+ it_behaves_like "an inclusion"
303
+ end
304
+
305
+ context "with tag, lang and options" do
306
+ let(:polytex) do <<-'EOS'
307
+ %= <<(tagged_file.rb, tag: v0.9.4, lang: tex, options: "hl_lines": [5])
308
+ EOS
309
+ end
310
+ it_behaves_like "an inclusion"
311
+ end
312
+ end
313
+ end
314
+
315
+ context "the tag does not exist" do
316
+ before(:all) do
317
+ class FakeGit < CodeInclusion::GitTaggedFileReader::Git
318
+ def checkout(tmpdir, filename, tagname)
319
+ end
320
+ def tag_exists?(tagname)
321
+ false
322
+ end
323
+ def checkout_succeeded?
324
+ false
325
+ end
326
+ end
327
+ end
328
+
329
+ let(:polytex) do <<-'EOS'
330
+ %= <<(tagged_file.rb, tag: non_existent_tag)
331
+ EOS
332
+ end
333
+ let(:output) do <<-'EOS'
334
+ <p>
335
+ <span class="inline_verbatim">
336
+ ERROR: Tag 'non_existent_tag' does not exist.
337
+ </span>
338
+ </p>
339
+ EOS
340
+ end
341
+ it_behaves_like "an inclusion"
342
+ end
343
+
344
+ context "the file does not exist" do
345
+ before(:all) do
346
+ class FakeGit < CodeInclusion::GitTaggedFileReader::Git
347
+ def checkout(tmpdir, filename, tagname)
348
+ "pathspec #{tmpdir}/non_existent_file did not match any file(s) known to git.\n"
349
+ end
350
+ def tag_exists?(tagname)
351
+ true
352
+ end
353
+ def checkout_succeeded?
354
+ false
355
+ end
356
+ end
357
+ end
358
+
359
+ let(:polytex) do <<-'EOS'
360
+ %= <<(non_existent_file, tag: v0.9.4)
361
+ EOS
362
+ end
363
+ let(:output) do <<-'EOS'
364
+ <p>
365
+ <span class="inline_verbatim">
366
+ ERROR: pathspec /non_existent_file did not match any file(s) known to git in tag v0.9.4.
367
+ </span>
368
+ </p>
369
+ EOS
370
+ end
371
+ it_behaves_like "an inclusion"
372
+ end
373
+
374
+ end
241
375
  end
242
376
  end
243
377
 
@@ -254,4 +388,4 @@ end
254
388
  #// begin section_z
255
389
  "This is section_z; it's used by a test."
256
390
  "Section Z is your friend."
257
- #// end
391
+ #// end
@@ -46,5 +46,10 @@ describe 'Polytexnic::Pipeline#to_html' do
46
46
  let(:polytex) { '\kode{function\_name}' }
47
47
  it { should resemble '<code>function_name</code>' }
48
48
  end
49
+
50
+ describe "color command" do
51
+ let(:polytex) { '\coloredtext{red}{text}' }
52
+ it { should resemble '<span style="color: red">text</span>' }
53
+ end
49
54
  end
50
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polytexnic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.9
4
+ version: 0.9.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Hartl
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-07 00:00:00.000000000 Z
12
+ date: 2014-06-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -212,6 +212,7 @@ files:
212
212
  - README.md
213
213
  - Rakefile
214
214
  - lib/polytexnic.rb
215
+ - lib/polytexnic/code_inclusion.rb
215
216
  - lib/polytexnic/literal.rb
216
217
  - lib/polytexnic/postprocessor.rb
217
218
  - lib/polytexnic/postprocessors/html.rb