texlab 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +54 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/README.txt +5 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/bin/texlab-compile +48 -0
- data/bin/texlab-compile.tex +0 -0
- data/bin/texlab-console +49 -0
- data/doc/readme.texlab +284 -0
- data/lib/texlab.rake +7 -0
- data/lib/texlab.rb +20 -0
- data/lib/texlab/boot.rb +373 -0
- data/lib/texlab/texlabfile.rb +164 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/texlab_spec.rb +7 -0
- data/texlab +3 -0
- data/texlab.gemspec +93 -0
- data/vim/texlab.vim +136 -0
- metadata +197 -0
data/lib/texlab.rake
ADDED
data/lib/texlab.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# To be sourced in VIM at startup
|
2
|
+
|
3
|
+
# Set up path
|
4
|
+
$: << (ENV["TEXLAB"]+"/lib") << (ENV["TEXLAB"]+"/lib/ruby")
|
5
|
+
Dir.glob(ENV["TEXLAB"]+"/gems/**/lib").each do |dir|
|
6
|
+
$: << dir
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
require "rubygems"
|
11
|
+
#require "irb"
|
12
|
+
require "yaml"
|
13
|
+
require "rake4latex"
|
14
|
+
|
15
|
+
|
16
|
+
task :compile do
|
17
|
+
Rake::Task[VIM::Buffer.current.name.sub(/\.[^.]+\z/, ".pdf")].invoke
|
18
|
+
end
|
19
|
+
|
20
|
+
|
data/lib/texlab/boot.rb
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
# This is the boot file to be loaded before parsing a .texlab file
|
2
|
+
# encoding: ASCII-8bit
|
3
|
+
require "easystats"
|
4
|
+
require "plusminus"
|
5
|
+
require "to_latex"
|
6
|
+
require "gnuplot"
|
7
|
+
require "open3"
|
8
|
+
|
9
|
+
# tweak Array
|
10
|
+
class Array
|
11
|
+
def extract_options!
|
12
|
+
if last.is_a? Hash
|
13
|
+
pop
|
14
|
+
else
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# tweak Hash
|
21
|
+
class Hash
|
22
|
+
# Object-like behavior
|
23
|
+
def method_missing name, *args
|
24
|
+
name_string = name.to_s
|
25
|
+
case name_string[-1]
|
26
|
+
when "="
|
27
|
+
self[name_string[0..-2].to_sym] = args[0]
|
28
|
+
when "!"
|
29
|
+
self[name_string[0..-2].to_sym] = {}
|
30
|
+
when "?"
|
31
|
+
!! self[name_string[0..-2]]
|
32
|
+
else
|
33
|
+
self[name]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module Enumerable
|
39
|
+
# map-hash
|
40
|
+
def mash
|
41
|
+
res = {}
|
42
|
+
each do |*x|
|
43
|
+
k,v = yield(*x)
|
44
|
+
res[k] = v
|
45
|
+
end
|
46
|
+
res
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Hash
|
51
|
+
def mash! &block
|
52
|
+
replace mash(&block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Average method for array with uncertainty calculation
|
58
|
+
class Array
|
59
|
+
def average
|
60
|
+
m = mean
|
61
|
+
m.pm(Math.sqrt(m.delta**2 + (standard_deviation*3)**2))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# latex generation helper
|
67
|
+
require "texlab/texlabfile"
|
68
|
+
|
69
|
+
$_latexfile = TexlabFile.new $_erbout
|
70
|
+
|
71
|
+
def documentHeader *args
|
72
|
+
$_latexfile.documentHeader *args
|
73
|
+
end
|
74
|
+
|
75
|
+
def env *args
|
76
|
+
$_latexfile.env(*args){yield}
|
77
|
+
end
|
78
|
+
|
79
|
+
def puts string
|
80
|
+
$_erbout << string.to_latex << "\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
# table generation
|
84
|
+
#
|
85
|
+
|
86
|
+
def table title, settings={}
|
87
|
+
@_table = {}
|
88
|
+
yield
|
89
|
+
entries = @_table
|
90
|
+
|
91
|
+
unless @_tables
|
92
|
+
$_latexfile.table(entries, title, settings)
|
93
|
+
else
|
94
|
+
@_tables << [entries, title, settings]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def column title
|
99
|
+
@_table_columns << (@_table_columns.last[title] || {})
|
100
|
+
|
101
|
+
result = yield
|
102
|
+
subcolumns = @_table_columns.pop
|
103
|
+
|
104
|
+
unless subcolumns.empty?
|
105
|
+
result = subcolumns
|
106
|
+
end
|
107
|
+
|
108
|
+
@_table_columns.last[title] = result
|
109
|
+
end
|
110
|
+
|
111
|
+
def row title
|
112
|
+
@_table_columns = [@_table[title] || {}]
|
113
|
+
yield
|
114
|
+
@_table[title] = @_table_columns.first
|
115
|
+
end
|
116
|
+
|
117
|
+
def tables *args
|
118
|
+
opts = args.extract_options!
|
119
|
+
caption = args.shift
|
120
|
+
|
121
|
+
@_tables = []
|
122
|
+
yield
|
123
|
+
|
124
|
+
$_latexfile.tables(@_tables, caption, opts)
|
125
|
+
|
126
|
+
@_tables = nil
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# Figure generation
|
131
|
+
#
|
132
|
+
|
133
|
+
def figure *args
|
134
|
+
opts = args.extract_options!
|
135
|
+
caption = args.shift
|
136
|
+
format = opts[:width] ? "width=#{opts[:width]}" : opts[:format]
|
137
|
+
label = opts[:label]
|
138
|
+
filename = opts[:filename]
|
139
|
+
if @_figures
|
140
|
+
@_figures << Figure.new(format, filename, caption, label)
|
141
|
+
else
|
142
|
+
raise "not implemented"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def figures *args
|
147
|
+
opts = args.extract_options!
|
148
|
+
placement = opts.delete(:placement) || "htbp"
|
149
|
+
label = opts.delete(:label)
|
150
|
+
newPageThreshold = opts.delete(:newPageThreshold) || 29
|
151
|
+
caption = args.shift
|
152
|
+
|
153
|
+
@_figures = []
|
154
|
+
yield
|
155
|
+
|
156
|
+
$_latexfile.figures(caption, @_figures, newPageThreshold, placement, label)
|
157
|
+
|
158
|
+
@_figures = nil
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
# plotting
|
163
|
+
#
|
164
|
+
|
165
|
+
require "gnuplot"
|
166
|
+
|
167
|
+
# override to have "w+" mode
|
168
|
+
def Gnuplot.open( persist=true )
|
169
|
+
cmd = Gnuplot.gnuplot( persist ) or raise 'gnuplot not found'
|
170
|
+
IO::popen( cmd, "w+") { |io| yield io }
|
171
|
+
end
|
172
|
+
|
173
|
+
def plot *args
|
174
|
+
opts = args.extract_options!
|
175
|
+
title = args[0]
|
176
|
+
|
177
|
+
placement = opts.delete(:placement) || "htbp"
|
178
|
+
width = opts.delete(:width) || "17cm"
|
179
|
+
height = opts.delete(:height) || "10cm"
|
180
|
+
cmd = opts.delete(:cmd) || "plot"
|
181
|
+
|
182
|
+
env :figure, "[#{placement}]" do
|
183
|
+
env :center do
|
184
|
+
@_datasets = []
|
185
|
+
yield
|
186
|
+
@_datasets
|
187
|
+
|
188
|
+
gnuplot = Gnuplot.gnuplot(true) or raise "gnuplot not found"
|
189
|
+
Open3.popen3(gnuplot) do |gp, out, err, external|
|
190
|
+
gp <<<<-GP
|
191
|
+
set terminal latex size #{width}, #{height}
|
192
|
+
GP
|
193
|
+
|
194
|
+
Gnuplot::Plot.new(gp, cmd) do |plot|
|
195
|
+
opts.each do |key, value|
|
196
|
+
case value
|
197
|
+
when true
|
198
|
+
plot.send key
|
199
|
+
else
|
200
|
+
plot.send key, value.gsub(/([\\ ])/, "\\\\\\1")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
@_datasets.each do |ds|
|
205
|
+
plot.data << ds
|
206
|
+
end
|
207
|
+
end
|
208
|
+
gp.close
|
209
|
+
$_erbout << out.readlines.join("\n")
|
210
|
+
|
211
|
+
if not external.value.success?
|
212
|
+
errlines = err.readlines
|
213
|
+
raise "could not plot:\n" + errlines.join("\n")
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
$_erbout << "\\caption{#{title}}" if title
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def splot *args, &block
|
222
|
+
opts = args.extract_options!
|
223
|
+
opts.cmd = "splot"
|
224
|
+
plot *args, opts, &block
|
225
|
+
end
|
226
|
+
|
227
|
+
def dataset *args
|
228
|
+
opts = args.extract_options!
|
229
|
+
|
230
|
+
data = args.shift
|
231
|
+
data_is_expr = !!data
|
232
|
+
if data
|
233
|
+
# simple function
|
234
|
+
ds = Gnuplot::DataSet.new data
|
235
|
+
else
|
236
|
+
# data
|
237
|
+
@_datastrings = []
|
238
|
+
yield
|
239
|
+
|
240
|
+
c = @_datastrings.first.count
|
241
|
+
data = (0...c).map do |i|
|
242
|
+
@_datastrings.map{|t|t[i]}
|
243
|
+
end
|
244
|
+
|
245
|
+
ds = Gnuplot::DataSet.new data
|
246
|
+
end
|
247
|
+
|
248
|
+
# check args
|
249
|
+
raise ArgumentError, "Unnecessary parameters: #{args}" unless args.empty?
|
250
|
+
|
251
|
+
# tweak args (syntax candy)
|
252
|
+
opts[:title] = nil unless data_is_expr or opts.has_key? :title
|
253
|
+
if opts.key? :title and not opts[:title]
|
254
|
+
opts.delete(:title)
|
255
|
+
opts[:notitle] = true
|
256
|
+
end
|
257
|
+
|
258
|
+
# send options
|
259
|
+
opts.each do |key, value|
|
260
|
+
case value
|
261
|
+
when true
|
262
|
+
ds.send key
|
263
|
+
else
|
264
|
+
ds.send :"#{key}=", value
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
@_datasets << ds
|
269
|
+
|
270
|
+
end
|
271
|
+
|
272
|
+
def datastring arg
|
273
|
+
@_datastrings << arg
|
274
|
+
end
|
275
|
+
|
276
|
+
def data *args
|
277
|
+
datastring args
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
# fitting
|
282
|
+
def fit expr, opts={}
|
283
|
+
raise "via: is a necessary parameter" if not opts[:via]
|
284
|
+
via = opts[:via]
|
285
|
+
vars = via.split(",")
|
286
|
+
|
287
|
+
using = opts[:using]
|
288
|
+
|
289
|
+
@_datasets = []
|
290
|
+
|
291
|
+
dataset do
|
292
|
+
yield
|
293
|
+
end
|
294
|
+
|
295
|
+
ds = @_datasets.first
|
296
|
+
|
297
|
+
errlines = []
|
298
|
+
result = {}
|
299
|
+
|
300
|
+
gnuplot = Gnuplot.gnuplot(true) or raise "gnuplot not found"
|
301
|
+
Open3.popen3(gnuplot) do |gp, out, err, external|
|
302
|
+
vars.each do |var|
|
303
|
+
gp << "#{var} = #{rand}\n"
|
304
|
+
end
|
305
|
+
gp <<<<-GP
|
306
|
+
fit #{expr} '-' #{"using " + using if using} via #{via}
|
307
|
+
GP
|
308
|
+
gp << ds.to_gplot
|
309
|
+
gp.close
|
310
|
+
|
311
|
+
errlines = err.readlines
|
312
|
+
errlines.each do |l|
|
313
|
+
if l =~ /\A([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*([0-9eE.+-]+)\s*\+\/-\s*([0-9eE.+-]+)/
|
314
|
+
result[$1.to_sym] = $2.to_f.pm($3.to_f)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
vars.map do |v|
|
320
|
+
result[v.to_sym] or raise "could not fit:\n" + errlines.join("\n")
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
|
325
|
+
# macros
|
326
|
+
|
327
|
+
$_macros={}
|
328
|
+
|
329
|
+
# a macro is available in tex (\asdf) and in ruby (:asdf.to_latex or $asdf). It will go to math
|
330
|
+
# mode
|
331
|
+
def macro hash
|
332
|
+
text_macro hash.mash{|key, value| [key, value.latex!.to_latex_math] }
|
333
|
+
end
|
334
|
+
|
335
|
+
# a text macro is available in tex (\asdf) and in ruby (:asdf). It will not be
|
336
|
+
# forced to math mode
|
337
|
+
def text_macro hash
|
338
|
+
hash.each do |key, value|
|
339
|
+
$_macros[key] = value
|
340
|
+
eval "$#{key} = '#{value.gsub("\\","\\\\").gsub("'", "\\'")}'"
|
341
|
+
$_erbout << "\\def\\#{key}{#{value}}"
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
class Symbol
|
346
|
+
# symbol's to_latex is the macro with the same name
|
347
|
+
def to_latex
|
348
|
+
$_macros[self] or raise "Undefined macro: #{self}"
|
349
|
+
end
|
350
|
+
|
351
|
+
def + str
|
352
|
+
"#{to_latex}#{str}".latex!.to_latex_math
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
# debug
|
358
|
+
def debug *args
|
359
|
+
args.each {|a| STDERR.puts a.inspect}
|
360
|
+
return *args
|
361
|
+
end
|
362
|
+
|
363
|
+
# include Math
|
364
|
+
include Math
|
365
|
+
|
366
|
+
# degree radian
|
367
|
+
def degrees x
|
368
|
+
x * 180.0 / Math::PI
|
369
|
+
end
|
370
|
+
|
371
|
+
def radians x
|
372
|
+
x / 180.0 * Math::PI
|
373
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# encoding: ASCII-8bit
|
2
|
+
require "latex"
|
3
|
+
|
4
|
+
class TexlabFile < LatexFile
|
5
|
+
# create new instance.
|
6
|
+
# @param _erbout the string or file to write to
|
7
|
+
def initialize _erbout
|
8
|
+
# we don't call super
|
9
|
+
|
10
|
+
@_erbout = _erbout
|
11
|
+
|
12
|
+
# replay actions in super
|
13
|
+
|
14
|
+
@extras = {
|
15
|
+
:documentclass => :article,
|
16
|
+
:fontsize => "10pt"
|
17
|
+
}
|
18
|
+
|
19
|
+
#use defaults for unused entries:
|
20
|
+
@indent = 0 # indent lines in blocks
|
21
|
+
|
22
|
+
@usePackages = {
|
23
|
+
:inputenc=>:latin1,
|
24
|
+
:graphics=>:final,
|
25
|
+
:graphicx=>:pdftex,
|
26
|
+
:color=>:dvips,
|
27
|
+
:amsfonts=>true,
|
28
|
+
:subfigure=>true,
|
29
|
+
:lscape=>true,
|
30
|
+
:hyperref=>true,
|
31
|
+
:amsmath=>true,
|
32
|
+
:units=>true,
|
33
|
+
:float=>true,
|
34
|
+
:xcolor=>"table"
|
35
|
+
#:subfig=>["lofdepth", "lotdepth"]
|
36
|
+
}
|
37
|
+
|
38
|
+
@default_table_align = 'c'
|
39
|
+
|
40
|
+
@lastWasPrint = false
|
41
|
+
end
|
42
|
+
|
43
|
+
def puts string
|
44
|
+
lines = string.split("\n")
|
45
|
+
for line in lines
|
46
|
+
@indent -= 2 if line[0,5]=='\\end{' && @indent >= 2
|
47
|
+
if @lastWasPrint
|
48
|
+
@_erbout << line.latex! << "\n"
|
49
|
+
@lastWasPrint = false
|
50
|
+
else
|
51
|
+
@_erbout << (" " * @indent).latex! << line.latex! << "\n"
|
52
|
+
end
|
53
|
+
@indent += 2 if line[0,7]=='\\begin{'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def print string
|
58
|
+
@lastWasPrint = true
|
59
|
+
@indent -= 2 if string[/\\end/]
|
60
|
+
@indent += 2 if string[/\\begin/]
|
61
|
+
@_erbout << string.latex! << "\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
def prettyPrintCell x
|
65
|
+
x.to_latex
|
66
|
+
end
|
67
|
+
|
68
|
+
def documentHeader extras={}
|
69
|
+
@extras.merge! extras
|
70
|
+
@usePackages.merge! extras[:usePackages] || {}
|
71
|
+
|
72
|
+
dcsettings = [@extras[:fontsize]]
|
73
|
+
dcsettings << "twocolumn" if @extras[:twocolumn]
|
74
|
+
|
75
|
+
puts "\\documentclass[#{dcsettings.join(",")}]{#{@extras[:documentclass]}}"
|
76
|
+
|
77
|
+
@usePackages.each do |package, settings|
|
78
|
+
case settings
|
79
|
+
when true then puts "\\usepackage{#{package}}"
|
80
|
+
when Array then puts "\\usepackage[#{settings.join(",")}]{#{package}}"
|
81
|
+
else puts "\\usepackage[#{settings}]{#{package}}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
puts "\\addtolength{\\oddsidemargin}{-3.5cm}"
|
85
|
+
puts "\\addtolength{\\textwidth}{7cm}"
|
86
|
+
puts "\\addtolength{\\topmargin}{-3cm}"
|
87
|
+
puts "\\addtolength{\\textheight}{5cm}"
|
88
|
+
puts "\\newcommand{\\hide}[1]{}"
|
89
|
+
|
90
|
+
puts "\\special{landscape}" if @extras[:landscape]
|
91
|
+
|
92
|
+
#puts "\\begin{document}"
|
93
|
+
puts "\\DeclareGraphicsExtensions{.jpg,.pdf,.mps,.png}"
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def env(name, *args)
|
98
|
+
# overridden for compatibility with new Array#to_s
|
99
|
+
puts "\\begin{#{name}}#{args.join("")}"
|
100
|
+
yield if block_given?
|
101
|
+
puts "\\end{#{name}}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def table entries, caption, args={}
|
105
|
+
|
106
|
+
# override defaults
|
107
|
+
args = {
|
108
|
+
:sort=> proc{0},
|
109
|
+
:header_hlines=>true
|
110
|
+
}.merge(args)
|
111
|
+
|
112
|
+
|
113
|
+
args[:rowTitle] = args[:rowTitle].to_latex if args[:rowTitle]
|
114
|
+
|
115
|
+
entries = convertKeysToLatex(entries)
|
116
|
+
|
117
|
+
super(entries, caption, args)
|
118
|
+
end
|
119
|
+
|
120
|
+
def plain_table entries, caption, opts={}
|
121
|
+
# capture output of table()
|
122
|
+
temp_erbout = @_erbout; @_erbout = ""
|
123
|
+
table(entries, caption, opts)
|
124
|
+
src = @_erbout; @_erbout = temp_erbout
|
125
|
+
|
126
|
+
src.sub!(/\A.*?\\begin{center}(.*)\\end{center}.*\z/m, '\1') or raise "internal error"
|
127
|
+
src.sub!(/\\caption{#{caption}}/, "") or raise "internal error"
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
def tables data, caption, opts={}
|
132
|
+
placement = opts.delete(:placement) || "htbp"
|
133
|
+
tableLandscape = opts.delete(:landscape)
|
134
|
+
tableFontSize = opts.delete(:fontSize) || @defaultTableFontSize
|
135
|
+
tableLabel = opts.delete(:label) || ""
|
136
|
+
tableCaption = caption
|
137
|
+
|
138
|
+
puts "\\begin{landscape}" if tableLandscape
|
139
|
+
puts "\n\\begin{table}[#{placement}]\n\\begin{center}"
|
140
|
+
puts "\\#{tableFontSize}" if tableFontSize
|
141
|
+
|
142
|
+
puts data.map { |args|
|
143
|
+
entries, caption, opts = *args
|
144
|
+
"\\subtable[#{caption}]{#{plain_table(*args)}}"
|
145
|
+
}.join("\\qquad")
|
146
|
+
|
147
|
+
puts "\\caption{#{tableCaption}}\n" if tableCaption
|
148
|
+
puts "\\label{tab:#{tableLabel}}" if tableLabel
|
149
|
+
puts "\\end{center}\n\\end{table}\n"
|
150
|
+
puts "\\end{landscape}" if @tableLandscape
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def convertKeysToLatex value
|
156
|
+
case value
|
157
|
+
when Hash
|
158
|
+
value.mash{|k,v| [k.to_latex, convertKeysToLatex(v)] }
|
159
|
+
else
|
160
|
+
value
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|