diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..fa8d8b2
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,2 @@
+Metrics/LineLength:
+ Enabled: false
\ No newline at end of file
diff --git a/constants.rb b/constants.rb
index f414879..6714b4c 100644
--- a/constants.rb
+++ b/constants.rb
@@ -16,16 +16,16 @@
# You should have received a copy of the GNU General Public License
# along with ltx2any. If not, see .
-# TODO move to a properties file?
-NAME = 'ltx2any'
-VERSION = '0.9a'
-YEAR = '2016'
-AUTHOR = 'Raphael Reitzig'
-TMPSUFFIX = '_tmp'
-HASHFILE = '.hashes' # relative to tmp directory
-# TODO move this constant to HashManager?
+# TODO: move to a properties file?
+NAME = 'ltx2any'.freeze
+VERSION = '0.9b'.freeze
+YEAR = '2018'.freeze
+AUTHOR = 'Raphael Reitzig'.freeze
+TMPSUFFIX = '_tmp'.freeze
+HASHFILE = '.hashes'.freeze # relative to tmp directory
+# TODO: move this constant to HashManager?
-LIBDIR = 'lib'
-EXTDIR = 'extensions'
-ENGDIR = 'engines'
-LOGWDIR = 'logwriters'
+LIBDIR = 'lib'.freeze
+EXTDIR = 'extensions'.freeze
+ENGDIR = 'engines'.freeze
+LOGWDIR = 'logwriters'.freeze
diff --git a/engines/lualatex.rb b/engines/lualatex.rb
index 59efffe..e78aabc 100644
--- a/engines/lualatex.rb
+++ b/engines/lualatex.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,6 +18,7 @@
Dependency.new('lualatex', :binary, [:engine, 'lualatex'], :essential)
+# TODO: document
class LuaLaTeX < Engine
def initialize
@@ -25,28 +26,28 @@ def initialize
@binary = 'lualatex'
@extension = 'pdf'
@description = 'Uses LuaLaTeX to create a PDF'
-
+
@target_file = "#{ParameterManager.instance[:jobname]}.#{extension}"
@old_hash = hash_result
end
-
+
def do?
!File.exist?(@target_file) || hash_result != @old_hash
end
-
+
def hash_result
- HashManager.hash_file(@target_file,
+ HashManager.hash_file(@target_file,
without: /\/CreationDate|\/ModDate|\/ID|\/Type\/XRef\/Index/)
end
def exec
@old_hash = hash_result
-
+
# Command for the main LaTeX compilation work
params = ParameterManager.instance
lualatex = '"lualatex -file-line-error -interaction=nonstopmode #{params[:enginepar]} \"#{params[:jobfile]}\""'
- f = IO::popen(eval(lualatex))
+ f = IO.popen(eval(lualatex))
log = f.readlines.map! { |s| Log.fix(s) }
{ success: File.exist?(@target_file), messages: TeXLogParser.parse(log), log: log.join('').strip! }
diff --git a/engines/pdflatex.rb b/engines/pdflatex.rb
index 85d7be2..8f02105 100644
--- a/engines/pdflatex.rb
+++ b/engines/pdflatex.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,22 +18,23 @@
Dependency.new('pdflatex', :binary, [:engine, 'pdflatex'], :essential)
-class PdfLaTeX < Engine
+# TODO: document
+class PdfLaTeX < Engine
def initialize
super
@binary = 'pdflatex'
@extension = 'pdf'
@description = 'Uses PdfLaTeX to create a PDF'
-
+
@target_file = "#{ParameterManager.instance[:jobname]}.#{extension}"
@old_hash = hash_result
end
-
+
def do?
!File.exist?(@target_file) || hash_result != @old_hash
end
-
+
def hash_result
HashManager.hash_file(@target_file, without: /\/CreationDate|\/ModDate|\/ID/)
end
@@ -45,7 +46,7 @@ def exec
params = ParameterManager.instance
pdflatex = '"pdflatex -file-line-error -interaction=nonstopmode #{params[:enginepar]} \"#{params[:jobfile]}\""'
- f = IO::popen(eval(pdflatex))
+ f = IO.popen(eval(pdflatex))
log = f.readlines.map! { |s| Log.fix(s) }
{ success: File.exist?(@target_file), messages: TeXLogParser.parse(log), log: log.join('').strip! }
diff --git a/engines/xelatex.rb b/engines/xelatex.rb
index c03e0d6..06afb0c 100644
--- a/engines/xelatex.rb
+++ b/engines/xelatex.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,34 +18,34 @@
Dependency.new('xelatex', :binary, [:engine, 'xelatex'], :essential)
+# TODO: document
class XeLaTeX < Engine
-
def initialize
super
@binary = 'xelatex'
@extension = 'pdf'
@description = 'Uses XeLaTeX to create a PDF'
-
+
@target_file = "#{ParameterManager.instance[:jobname]}.#{extension}"
@old_hash = hash_result
end
-
+
def do?
!File.exist?(@target_file) || hash_result != @old_hash
end
-
+
def hash_result
HashManager.hash_file(@target_file, drop_from: /CIDFontType0C|Type1C/)
end
def exec
@old_hash = hash_result
-
+
# Command for the main LaTeX compilation work
params = ParameterManager.instance
xelatex = '"xelatex -file-line-error -interaction=nonstopmode #{params[:enginepar]} \"#{params[:jobfile]}\""'
- f = IO::popen(eval(xelatex))
+ f = IO.popen(eval(xelatex))
log = f.readlines.map! { |s| Log.fix(s) }
{ success: File.exist?(@target_file), messages: TeXLogParser.parse(log), log: log.join('').strip! }
diff --git a/extensions/10_biber.rb b/extensions/10_biber.rb
index 1b91257..9e1a971 100644
--- a/extensions/10_biber.rb
+++ b/extensions/10_biber.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,6 +18,7 @@
Dependency.new('biber', :binary, [:extension, 'Biber'], :essential)
+# TODO: document
class Biber < Extension
def initialize
super
@@ -28,12 +29,12 @@ def initialize
def do?(time)
return false unless time == 1
-
+
params = ParameterManager.instance
-
+
usesbib = File.exist?("#{params[:jobname]}.bcf")
needrerun = false
-
+
if usesbib
# Collect sources (needed for log parsing)
@sources = []
@@ -43,8 +44,8 @@ def do?(time)
end
}
@sources.uniq!
-
-
+
+
# Aside from the first run (no bbl),
# there are two things that prompt us to rerun:
# * changes to the bcf file (which includes all kinds of things,
@@ -53,7 +54,7 @@ def do?(time)
needrerun = !File.exist?("#{params[:jobname]}.bbl") | # Is this the first run?
HashManager.instance.files_changed?("#{params[:jobname]}.bcf",
*@sources)
- # Note: non-strict OR so that hashes are computed for next run
+ # Note: non-strict OR so that hashes are computed for next run
end
usesbib && needrerun
@@ -61,13 +62,13 @@ def do?(time)
def exec(time, progress)
params = ParameterManager.instance
-
+
# Command to process bibtex bibliography if necessary.
# Uses the following variables:
# * jobname -- name of the main LaTeX file (without file ending)
biber = '"biber \"#{params[:jobname]}\""'
- f = IO::popen(eval(biber))
+ f = IO.popen(eval(biber))
log = f.readlines
# Dig trough output and find errors
@@ -92,5 +93,5 @@ def exec(time, progress)
{ success: !errors, messages: msgs, log: log.join('').strip! }
end
end
-
+
Extension.add Biber
diff --git a/extensions/10_bibtex.rb b/extensions/10_bibtex.rb
index ace4c11..cbb6f88 100644
--- a/extensions/10_bibtex.rb
+++ b/extensions/10_bibtex.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,13 +18,14 @@
Dependency.new('bibtex', :binary, [:extension, 'BibTeX'], :essential)
+# TODO: document
class BibTeX < Extension
def initialize
- super
+ super
@name = 'BibTeX'
@description = 'Creates bibliographies (old)'
-
- # For checking whether bibtex has to rerun, we need to keep the
+
+ # For checking whether bibtex has to rerun, we need to keep the
# relevant parts of the _.aux file handy.
# TODO use internal store?
@grepfile = 'bibtex_aux_grep'
@@ -32,9 +33,9 @@ def initialize
def do?(time)
return false unless time == 1
-
+
params = ParameterManager.instance
-
+
# Collect used bibdata files and style file
stylefile = []
bibdata = []
@@ -42,24 +43,24 @@ def do?(time)
if File.exist?("#{params[:jobname]}.aux")
File.open("#{params[:jobname]}.aux", 'r') { |file|
while ( line = file.gets )
- if /^\\bibdata\{(.+?)\}$/ =~ line
+ if /^\\bibdata{(.+?)}$/ =~ line
# If commas occur, add both a split version (multiple files)
# and the hole string (filename with comma), to be safe.
bibdata += $~[1].split(',').map { |s| "#{s}.bib" } + [$~[1]]
- grepdata.push line.strip
- elsif /^\\bibstyle\{(.+?)\}$/ =~ line
+ grepdata.push line.strip
+ elsif /^\\bibstyle{(.+?)}$/ =~ line
stylefile.push "#{$~[1]}.bst"
- grepdata.push line.strip
+ grepdata.push line.strip
elsif /^\\(bibcite|citation)/ =~ line
- grepdata.push line.strip
- end
+ grepdata.push line.strip
+ end
end
}
- end
-
+ end
+
# Check whether bibtex is necessary at all
- usesbib = bibdata.size > 0
-
+ usesbib = !bibdata.empty?
+
# Write relevant part of the _.aux file into a separate file for hashing
if usesbib
File.open(@grepfile, 'w') { |f|
@@ -78,13 +79,13 @@ def do?(time)
def exec(time, progress)
params = ParameterManager.instance
-
+
# Command to process bibtex bibliography if necessary.
# Uses the following variables:
# * jobname -- name of the main LaTeX file (without file ending)
bibtex = '"bibtex \"#{params[:jobname]}\""'
- f = IO::popen(eval(bibtex))
+ f = IO.popen(eval(bibtex))
log = f.readlines
# Dig trough output and find errors
@@ -103,7 +104,7 @@ def exec(time, progress)
msg = lastline
logline = [linectr - 1, linectr]
end
-
+
msgs.push(LogMessage.new(:error, $~[3], [Integer($~[2])], logline, msg))
errors = true
end
@@ -114,5 +115,5 @@ def exec(time, progress)
{ success: !errors, messages: msgs, log: log.join('').strip! }
end
end
-
+
Extension.add BibTeX
diff --git a/extensions/20_makeindex.rb b/extensions/20_makeindex.rb
index d7a12ed..ae52c53 100644
--- a/extensions/20_makeindex.rb
+++ b/extensions/20_makeindex.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,55 +18,56 @@
Dependency.new('makeindex', :binary, [:extension, 'makeindex'], :essential)
+# TODO: document
class MakeIndex < Extension
def initialize
super
-
+
@name = 'makeindex'
@description = 'Creates an index'
end
def do?(time)
return false unless time == 1
-
+
params = ParameterManager.instance
-
- File.exist?("#{params[:jobname]}.idx") \
- && ( !File.exist?("#{params[:jobname]}.ind") \
- | HashManager.instance.files_changed?("#{params[:jobname]}.idx")
- # Note: non-strict OR so that hashes are computed for next run
- )
+
+ File.exist?("#{params[:jobname]}.idx") &&
+ (!File.exist?("#{params[:jobname]}.ind") |
+ HashManager.instance.files_changed?("#{params[:jobname]}.idx")
+ # Note: non-strict OR so that hashes are computed for next run
+ )
end
def exec(time, progress)
params = ParameterManager.instance
-
+
# Command to create the index if necessary. Provide two versions,
# one without and one with stylefile
# Uses the following variables:
# * jobname -- name of the main LaTeX file (without file ending)
# * mistyle -- name of the makeindex style file (with file ending)
- makeindex = {'default' => '"makeindex -q \"#{params[:jobname]}\" 2>&1"',
- 'styled' => '"makeindex -q -s \"#{mistyle}\" \"#{params[:jobname]}\" 2>&1"'}
-
- version = 'default'
+ makeindex = { default: '"makeindex -q \"#{params[:jobname]}\" 2>&1"',
+ styled: '"makeindex -q -s \"#{mistyle}\" \"#{params[:jobname]}\" 2>&1"' }
+
+ version = :default
mistyle = nil
- Dir['*.ist'].each { |f|
- version = 'styled'
+ Dir['*.ist'].each do |f|
+ version = :styled
mistyle = f
- }
+ end
- # Even in quiet mode, some critical errors (e.g. regarding -g)
+ # Even in quiet mode, some critical errors (e.g. regarding -g)
# only end up in the error stream, but not in the file. Doh.
log1 = []
- IO::popen(eval(makeindex[version])) { |f|
- log1 = f.readlines
- }
+ IO.popen(eval(makeindex[version])) do |f|
+ log1 = f.readlines
+ end
log2 = []
- File.open("#{params[:jobname]}.ilg", 'r') { |f|
+ File.open("#{params[:jobname]}.ilg", 'r') do |f|
log2 = f.readlines
- }
+ end
log = [log2[0]] + log1 + log2[1,log2.length]
@@ -74,15 +75,15 @@ def exec(time, progress)
current = []
linectr = 1
errors = false
- log.each { |line|
+ log.each do |line|
if /^!! (.*?) \(file = (.+?), line = (\d+)\):$/ =~ line
current = [:error, $~[2], [Integer($~[3])], [linectr], "#{$~[1]}: "]
errors = true
- elsif /^\#\# (.*?) \(input = (.+?), line = (\d+); output = .+?, line = \d+\):$/ =~ line
+ elsif /^## (.*?) \(input = (.+?), line = (\d+); output = .+?, line = \d+\):$/ =~ line
current = [:warning, $~[2], [Integer($~[3])], [linectr], "#{$~[1]}: "]
elsif current != [] && /^\s+-- (.*)$/ =~ line
current[3][1] = linectr
- msgs.push(LogMessage.new(current[0], current[1], current[2],
+ msgs.push(LogMessage.new(current[0], current[1], current[2],
current[3], current[4] + $~[1].strip))
current = []
elsif /Option -g invalid/ =~ line
@@ -93,7 +94,7 @@ def exec(time, progress)
errors = true
end
linectr += 1
- }
+ end
{ sucess: !errors, messages: msgs, log: log.join('').strip! }
end
diff --git a/extensions/30_metapost.rb b/extensions/30_metapost.rb
index bc1c175..fa84ecf 100644
--- a/extensions/30_metapost.rb
+++ b/extensions/30_metapost.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,12 +18,13 @@
Dependency.new('mpost', :binary, [:extension, 'MetaPost'], :essential)
+# TODO: document
class MetaPost < Extension
def initialize
super
@name = 'MetaPost'
@description = 'Compiles generated MetaPost files'
-
+
@mp_files = []
end
@@ -35,27 +36,27 @@ def job_size
# Count the number of changed _.mp files
# Store because a check for changed hashes in exec later would give false!
# Append because job_size may be called multiple times before exec
- @mp_files += Dir.entries('.').delete_if { |f|
- (/\.mp$/ !~ f) || !HashManager.instance.files_changed?(f)
- }
+ @mp_files += Dir.entries('.').delete_if do |f|
+ (/\.mp$/ !~ f) || !HashManager.instance.files_changed?(f)
+ end
@mp_files.size
-
- # TODO check for (non-)existing result? incorporate ir parameter?
+
+ # TODO: check for (non-)existing result? incorporate ir parameter?
end
def exec(time, progress)
params = ParameterManager.instance
-
+
# Command to process metapost files if necessary.
mpost = '"mpost -tex=#{params[:engine]} -file-line-error -interaction=nonstopmode \"#{f}\" 2>&1"'
-
+
# Run mpost for each job file
log = [[], []]
- if !@mp_files.empty?
+ unless @mp_files.empty?
# Run (latex) engine for each figure
- log = self.class.execute_parts(@mp_files, progress) { |f|
- compile(mpost, f)
- }.transpose
+ log = self.class.execute_parts(@mp_files, progress) do |f|
+ compile(mpost, f)
+ end.transpose
end
@mp_files = [] # reset for next round of checks
@@ -63,83 +64,76 @@ def exec(time, progress)
# w.r.t. its own contribution. Later steps will only add the offset of the
# whole metapost block, not those inside.
offset = 0
- (0..(log[0].size - 1)).each { |i|
- if log[0][i].size > 0
+ (0..(log[0].size - 1)).each do |i|
+ unless log[0][i].empty?
internal_offset = 3 # Stuff we print per plot before log excerpt (see :compile)
- log[0][i].map! { |m|
- LogMessage.new(m.type, m.srcfile, m.srcline,
- if m.logline != nil then
- m.logline.map { |ll| ll + offset + internal_offset}
- else
- nil
+ log[0][i].map! do |m|
+ LogMessage.new(m.type, m.srcfile, m.srcline,
+ unless m.logline.nil?
+ m.logline.map { |ll| ll + offset + internal_offset}
end,
- m.msg, if m.formatted? then :fixed else :none end)
- }
+ m.msg, m.formatted? ? :fixed : :none)
+ end
end
- offset += log[1][i].count(?\n)
- }
+ offset += log[1][i].count("\n")
+ end
log[0].flatten!
errors = log[0].count { |m| m.type == :error }
{ success: errors <= 0, messages: log[0], log: log[1].join }
end
-
- private
- def compile(cmd, f)
- params = ParameterManager.instance
-
- log = ''
- msgs = []
-
- # Run twice to get LaTeX bits right
- IO::popen(eval(cmd)) { |io|
- io.readlines
- # Closes IO
- }
- lines = IO::popen(eval(cmd)) { |io|
- io.readlines
- # Closes IO
- }
- output = lines.join('').strip
-
- log << "# #\n# #{f}\n\n"
- if output != ''
- log << output
- msgs += msgs = parse(lines, f)
- else
- log << 'No output from mpost, so apparently everything went fine!'
- end
- log << "\n\n"
- [msgs, log]
+ private
+
+ def compile(cmd, f)
+ params = ParameterManager.instance
+
+ log = ''
+ msgs = []
+
+ # Run twice to get LaTeX bits right
+ IO.popen(eval(cmd), &:readlines)
+ lines = IO.popen(eval(cmd), &:readlines)
+ output = lines.join('').strip
+
+ log << "# #\n# #{f}\n\n"
+ if output != ''
+ log << output
+ msgs += parse(lines, f)
+ else
+ log << 'No output from mpost, so apparently everything went fine!'
end
-
- def parse(strings, file)
- msgs = []
-
- linectr = 1
- curmsg = nil
- curline = -1
- strings.each { |line|
- # Messages have the format
- # ! message
- # ...
- # l.\d+ ...
- if /^! (.*)$/ =~ line
- curmsg = $~[1].strip
- curline = linectr
- elsif curmsg != nil && /^l\.(\d+)/ =~ line
- msgs.push(LogMessage.new(:error, "#{ParameterManager.instance[:tmpdir]}/#{file}",
- [Integer($~[1])], [curline, linectr],
- curmsg, :none))
- curmsg = nil
- curline = -1
- end
- linectr += 1
- }
+ log << "\n\n"
+
+ [msgs, log]
+ end
+
+ def parse(strings, file)
+ msgs = []
- msgs
+ linectr = 1
+ curmsg = nil
+ curline = -1
+ strings.each do |line|
+ # Messages have the format
+ # ! message
+ # ...
+ # l.\d+ ...
+ if /^! (.*)$/ =~ line
+ curmsg = $LAST_MATCH_INFO[1].strip
+ curline = linectr
+ elsif !curmsg.nil? && /^l\.(\d+)/ =~ line
+ msgs.push(LogMessage.new(:error, "#{ParameterManager.instance[:tmpdir]}/#{file}",
+ [Integer($LAST_MATCH_INFO[1])], [curline, linectr],
+ curmsg, :none))
+ curmsg = nil
+ curline = -1
+ end
+ linectr += 1
end
+
+ msgs
+ end
end
Extension.add MetaPost
diff --git a/extensions/30_sagetex.rb b/extensions/30_sagetex.rb
index 8863ded..328159a 100644
--- a/extensions/30_sagetex.rb
+++ b/extensions/30_sagetex.rb
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with ltx2any. If not, see .
+# TODO: document
class SageTeX < Extension
def initialize
super
@@ -26,8 +27,8 @@ def initialize
def do?(time)
params = ParameterManager.instance
time == 1 &&
- File.exist?("#{params[:jobname]}.sagetex.sage") &&
- HashManager.instance.files_changed?("#{params[:jobname]}.sagetex.sage")
+ File.exist?("#{params[:jobname]}.sagetex.sage") &&
+ HashManager.instance.files_changed?("#{params[:jobname]}.sagetex.sage")
end
def exec(time, progress)
@@ -53,19 +54,20 @@ def parse(lines)
msg = nil
linectr = 1
lines.each { |line|
- if !msg.nil? && line.strip.length == 0
+ if !msg.nil? && line.strip.empty?
msg.logline << linectr - 1
msg = nil
- elsif msg.nil? && line =~ /File "(.+)", line (\d+)/
+ elsif msg.nil? && line =~ /File "(.+)",\s+line (\d+)/
msg = LogMessage.new(:warning, $~[1], [$~[2].to_i], [linectr], '', :fixed)
messages << msg
elsif line =~ /SyntaxError: \w+/
msg.type = :error
msg.logline << linectr
+ msg.msg += line
msg = nil
elsif line =~ /sagetex\.VersionError:/
msg.type = :error
- # TODO what are other patterns?
+ # TODO: what are other patterns?
elsif !msg.nil?
msg.msg += line
end
diff --git a/extensions/30_tikzext.rb b/extensions/30_tikzext.rb
index 56ae7aa..ad84a4f 100644
--- a/extensions/30_tikzext.rb
+++ b/extensions/30_tikzext.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -19,6 +19,7 @@
ParameterManager.instance.addParameter(Parameter.new(
:imagerebuild, 'ir', String, '', "Specify externalised TikZ images to rebuild, separated by ':'. Set to 'all' to rebuild all."))
+# TODO: document
class TikZExt < Extension
def initialize
super
@@ -29,14 +30,14 @@ def initialize
def do?(time)
time == 1 && job_size > 0
end
-
+
def job_size
collect_pending[0].size
end
def exec(time, progress)
params = ParameterManager.instance
-
+
# Command to process externalised TikZ images if necessary.
# Uses the following variables:
# * $params["engine"] -- Engine used by the main job.
@@ -47,134 +48,129 @@ def exec(time, progress)
figures,rebuildlog = collect_pending
log = [[], []]
- if !figures.empty?
+ unless figures.empty?
# Run (latex) engine for each figure
- log = self.class.execute_parts(figures, progress) { |fig|
+ log = self.class.execute_parts(figures, progress) do |fig|
compile(pdflatex, fig)
- }.transpose
+ end.transpose
end
# Log line numbers are wrong since every compile determines log line numbers
# w.r.t. its own contribution. Later steps will only add the offset of the
# whole tikzext block, not those inside.
offset = 0
- (0..(log[0].size - 1)).each { |i|
- if log[0][i].size > 0
+ (0..(log[0].size - 1)).each do |i|
+ if !log[0][i].empty?
internal_offset = 5 # Stuff we print per figure before log excerpt (see :compile)
- log[0][i].map! { |m|
- LogMessage.new(m.type, m.srcfile, m.srcline,
- if m.logline != nil then
+ log[0][i].map! do |m|
+ LogMessage.new(m.type, m.srcfile, m.srcline,
+ if m.logline != nil
m.logline.map { |ll| ll + offset + internal_offset - 1} # -1 because we drop first line!
else
nil
end,
- m.msg, if m.formatted? then :fixed else :none end)
- }
-
- log[0][i] = [LogMessage.new(:info, nil, nil, nil,
- "The following messages refer to figure\n #{figures[i]}.\n" +
+ m.msg, m.formatted? ? :fixed : :none)
+ end
+
+ log[0][i] = [LogMessage.new(:info, nil, nil, nil,
+ "The following messages refer to figure\n #{figures[i]}.\n" \
"See\n #{params[:tmpdir]}/#{figures[i]}.log\nfor the full log.", :fixed)
] + log[0][i]
else
- log[0][i] += [LogMessage.new(:info, nil, nil, nil,
- "No messages for figure\n #{figures[i]}.\nfound. " +
+ log[0][i] += [LogMessage.new(:info, nil, nil, nil,
+ "No messages for figure\n #{figures[i]}.\nfound. " \
"See\n #{params[:tmpdir]}/#{figures[i]}.log\nfor the full log.", :fixed)
]
end
- offset += log[1][i].count(?\n)
- }
-
+ offset += log[1][i].count(?\n)
+ end
+
log[0].flatten!
errors = log[0].count { |m| m.type == :error }
{ success: errors <= 0, messages: rebuildlog[0] + log[0], log: rebuildlog[1] + log[1].join }
end
-
+
private
- def collect_pending
- params = ParameterManager.instance
-
- figures,rebuildlog = [], [[], '']
- if File.exists?("#{params[:jobname]}.figlist")
- figures = IO.readlines("#{params[:jobname]}.figlist").map { |fig|
- if fig.strip != ''
- fig.strip
- else
- nil
- end
- }.compact
- # Remove results of figures that we want to rebuild
- rebuild = []
- if params[:imagerebuild] == 'all'
- rebuild = figures
+ def collect_pending
+ params = ParameterManager.instance
+
+ figures = []
+ rebuildlog = [[], '']
+ if File.exist?("#{params[:jobname]}.figlist")
+ figures = IO.readlines("#{params[:jobname]}.figlist").map do |fig|
+ if fig.strip != ''
+ fig.strip
else
- params[:imagerebuild].split(':').map { |s| s.strip }.each { |fig|
- if figures.include?(fig)
- rebuild.push(fig)
- else
- msg = "User requested rebuild of figure `#{fig}` which does not exist."
- rebuildlog[0].push(LogMessage.new(:warning, nil, nil, nil, msg))
- rebuildlog[1] += "#{msg}\n\n"
- end
- }
+ nil
end
-
-
- figures.select! { |fig|
- !File.exist?("#{fig}.pdf") || rebuild.include?(fig)
- }
- end
+ end.compact
- [figures, rebuildlog]
- end
-
- def compile(cmd, fig)
- params = ParameterManager.instance
-
- msgs = []
- log = "# #\n# Figure: #{fig}\n# See #{ParameterManager.instance[:tmpdir]}/#{fig}.log for full log.\n\n"
-
- # Run twice to clean up log?
- # IO::popen(eval(cmd)).readlines
- IO::popen(eval(cmd)) { |io|
- io.readlines
- # Closes IO
- }
- # Shell output does not contain error messages -> read log
- output = File.open("#{fig}.log", 'r') { |f|
- f.readlines.map { |s| Log.fix(s) }
- }
-
- # These seems to describe reliable boundaries of that part in the log
- # which deals with the processed TikZ figure.
- startregexp = /^\\openout5 = `#{fig}\.dpth'\.\s*$/
- endregexp = /^\[\d+\s*$/
-
- # Cut out relevant part for raw log (heuristic)
- string = output.drop_while { |line|
- startregexp !~ line
- }.take_while { |line|
- endregexp !~ line
- }.drop(1).join('').strip
-
- if string != ''
- log << "\n\n#{string}\n\n"
+ # Remove results of figures that we want to rebuild
+ rebuild = []
+ if params[:imagerebuild] == 'all'
+ rebuild = figures
else
- log << 'No errors detected.'
+ params[:imagerebuild].split(':').map(&:strip).each do |fig|
+ if figures.include?(fig)
+ rebuild.push(fig)
+ else
+ msg = "User requested rebuild of figure `#{fig}` which does not exist."
+ rebuildlog[0].push(LogMessage.new(:warning, nil, nil, nil, msg))
+ rebuildlog[1] += "#{msg}\n\n"
+ end
+ end
end
-
- # Parse whole log for messages (needed for filenames) but restrict
- # to messages from interesting part
- msgs = TeXLogParser.parse(output, startregexp, endregexp)
-
- # Still necessary? Should get *some* error from the recursive call.
- # if ( !File.exist?("#{fig}.pdf") )
- # log << "Fatal error on #{fig}. See #{$params["tmpdir"]}/#{fig}.log for details.\n"
- # end
- log << "\n\n"
-
- [msgs, log]
+
+
+ figures.select! do |fig|
+ !File.exist?("#{fig}.pdf") || rebuild.include?(fig)
+ end
+ end
+
+ [figures, rebuildlog]
+ end
+
+ def compile(cmd, fig)
+ params = ParameterManager.instance
+
+ msgs = []
+ log = "# #\n# Figure: #{fig}\n# See #{ParameterManager.instance[:tmpdir]}/#{fig}.log for full log.\n\n"
+
+ # Run twice to clean up log?
+ # IO::popen(eval(cmd)).readlines
+ IO.popen(eval(cmd), &:readlines)
+ # Shell output does not contain error messages -> read log
+ output = File.open("#{fig}.log", 'r') do |f|
+ f.readlines.map { |s| Log.fix(s) }
end
+
+ # These seems to describe reliable boundaries of that part in the log
+ # which deals with the processed TikZ figure.
+ startregexp = /^\\openout5 = `#{fig}\.dpth'\.\s*$/
+ endregexp = /^\[\d+\s*$/
+
+ # Cut out relevant part for raw log (heuristic)
+ string = output.drop_while do |line|
+ startregexp !~ line
+ end.take_while do |line|
+ endregexp !~ line
+ end.drop(1).join('').strip
+
+ log << (string != '' ? "\n\n#{string}\n\n" : 'No errors detected.')
+
+ # Parse whole log for messages (needed for filenames) but restrict
+ # to messages from interesting part
+ msgs = TeXLogParser.parse(output, startregexp, endregexp)
+
+ # Still necessary? Should get *some* error from the recursive call.
+ # if ( !File.exist?("#{fig}.pdf") )
+ # log << "Fatal error on #{fig}. See #{$params["tmpdir"]}/#{fig}.log for details.\n"
+ # end
+ log << "\n\n"
+
+ [msgs, log]
+ end
end
Extension.add TikZExt
diff --git a/extensions/50_gnuplot.rb b/extensions/50_gnuplot.rb
index dcb40c1..1c5784a 100644
--- a/extensions/50_gnuplot.rb
+++ b/extensions/50_gnuplot.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,12 +18,13 @@
Dependency.new('gnuplot', :binary, [:extension, 'Gnuplot'], :essential)
+# TODO: document
class Gnuplot < Extension
def initialize
super
@name = 'Gnuplot'
@description = 'Executes generated gnuplot files'
-
+
@gnuplot_files = []
end
@@ -49,7 +50,7 @@ def exec(time, progress)
# Run gnuplot for each remaining file
log = [[], []]
- if !@gnuplot_files.empty?
+ unless @gnuplot_files.empty?
log = self.class.execute_parts(@gnuplot_files, progress) { |f|
compile(gnuplot, f)
}.transpose
@@ -60,84 +61,81 @@ def exec(time, progress)
# whole gnuplot block, not those inside.
offset = 0
(0..(log[0].size - 1)).each { |i|
- if log[0][i].size > 0
+ unless log[0][i].empty?
internal_offset = 2 # Stuff we print per plot before log excerpt (see :compile)
log[0][i].map! { |m|
- LogMessage.new(m.type, m.srcfile, m.srcline,
- if m.logline != nil then
- m.logline.map { |ll| ll + offset + internal_offset}
+ LogMessage.new(m.type, m.srcfile, m.srcline,
+ if !m.logline.nil?
+ m.logline.map { |ll| ll + offset + internal_offset}
else
nil
end,
- m.msg, if m.formatted? then :fixed else :none end)
+ m.msg, m.formatted? ? :fixed : :none)
}
end
- offset += log[1][i].count(?\n)
+ offset += log[1][i].count(?\n)
}
log[0].flatten!
errors = log[0].count { |m| m.type == :error }
{ success: errors <= 0, messages: log[0], log: log[1].join }
end
-
- private
- def compile(cmd, f)
- params = ParameterManager.instance
-
- log = ''
- msgs = []
-
- lines = IO::popen(eval(cmd)) { |io|
- io.readlines
- # Closes IO
- }
- output = lines.join('').strip
-
- log << "# #\n# #{f}\n\n"
- if output != ''
- log << output
- msgs += parse(lines)
+
+ private
+
+ def compile(cmd, f)
+ params = ParameterManager.instance
+
+ log = ''
+ msgs = []
+
+ lines = IO.popen(eval(cmd), &:readlines)
+ output = lines.join('').strip
+
+ log << "# #\n# #{f}\n\n"
+ if output != ''
+ log << output
+ msgs += parse(lines)
+ else
+ log << 'No output from gnuplot, so apparently everything went fine!'
+ end
+ log << "\n\n"
+
+ [msgs, log]
+ end
+
+ def parse(strings)
+ msgs = []
+
+ context = ''
+ contextline = 1
+ linectr = 1
+ strings.each { |line|
+ # Messages have the format
+ # * context (at least one line)
+ # * ^ marking the point of issue in its own line
+ # * one line of error statement
+ # I have never seen more than one error (seems to abort).
+ # So I'm going to assume that multiple error messages
+ # are separated by empty lines.
+ if /^"(.+?)", line (\d+): (.*)$/ =~ line
+ msgs.push(LogMessage.new(:error, "#{ParameterManager.instance[:tmpdir]}/#{$~[1]}",
+ [Integer($~[2])], [[contextline, linectr].min, linectr],
+ "#{context}#{$~[3].strip}", :fixed))
+ elsif line.strip == ''
+ context = ''
+ contextline = strings.size + 1 # Larger than every line number
else
- log << 'No output from gnuplot, so apparently everything went fine!'
+ contextline = [contextline, linectr].min
+ context += line
end
- log << "\n\n"
+ linectr += 1
+ # TODO: break/strip long lines? Should be able to figure out relevant parts by position of circumflex
+ # TODO: drop context here and instead give log line numbers?
+ }
- [msgs, log]
- end
-
- def parse(strings)
- msgs = []
-
- context = ''
- contextline = 1
- linectr = 1
- strings.each { |line|
- # Messages have the format
- # * context (at least one line)
- # * ^ marking the point of issue in its own line
- # * one line of error statement
- # I have never seen more than one error (seems to abort).
- # So I'm going to assume that multiple error messages
- # are separated by empty lines.
- if /^"(.+?)", line (\d+): (.*)$/ =~ line
- msgs.push(LogMessage.new(:error, "#{ParameterManager.instance[:tmpdir]}/#{$~[1]}",
- [Integer($~[2])], [[contextline, linectr].min, linectr],
- "#{context}#{$~[3].strip}", :fixed))
- elsif line.strip == ''
- context = ''
- contextline = strings.size + 1 # Larger than every line number
- else
- contextline = [contextline, linectr].min
- context += line
- end
- linectr += 1
- # TODO break/strip long lines? Should be able to figure out relevant parts
- # by position of circumflex
- # TODO drop context here and instead give log line numbers?
- }
-
- msgs
- end
+ msgs
+ end
end
Extension.add Gnuplot
diff --git a/extensions/60_synctex.rb b/extensions/60_synctex.rb
index a9b2a6d..8a59a2f 100644
--- a/extensions/60_synctex.rb
+++ b/extensions/60_synctex.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -22,20 +22,20 @@
:synctex, 'synctex', Boolean, false, 'Set to make engines create SyncTeX files.'))
# Add hook that adapts the :enginepar parameter whenever :synctex changes (including startup)
-ParameterManager.instance.addHook(:synctex) { |key, val|
+ParameterManager.instance.addHook(:synctex) do |key, val|
params = ParameterManager.instance
# Set engine parameter
- # TODO make nicer with array parameters
+ # TODO: make nicer with array parameters
parameter = '--synctex=-1'
- if val && params[:enginepar][parameter] == nil # TODO what is the second access?
+ if val && params[:enginepar][parameter] == nil # TODO: what is the second access?
params.add(:enginepar, parameter)
elsif !val
params[:enginepar] = params[:enginepar].gsub(parameter, '')
end
# Add synctex file to those that should be ignored
- # TODO make nicer with array parameters
+ # TODO: make nicer with array parameters
synctexfile = "#{params[:jobname]}.synctex.gz"
if val && params[:ignore] == nil
params.add(:ignore, synctexfile)
@@ -46,11 +46,11 @@
elsif !val
params[:ignore] = params[:ignore].gsub(/:?#{synctexfile}/, '')
end
-}
+end
class SyncTeX < Extension
def initialize
- super
+ super
@name = 'SyncTeX'
@description = 'Provides support for SyncTeX'
end
@@ -62,26 +62,26 @@ def do?(time)
def exec(time, progress)
params = ParameterManager.instance
- if !File.exist?("#{params[:jobname]}.synctex")
+ unless File.exist?("#{params[:jobname]}.synctex")
return { success: false,
messages: [LogMessage.new(:error, nil, nil, nil, 'SyncTeX file not found.')],
log: 'SyncTeX file not found.' }
end
# Fix paths in synctex file, gzip it and put result in main directory
- Zlib::GzipWriter.open("#{params[:jobpath]}/#{params[:jobname]}.synctex.gz") { |gz|
- File.open("#{params[:jobname]}.synctex", 'r') { |f|
- f.readlines.each { |line|
+ Zlib::GzipWriter.open("#{params[:jobpath]}/#{params[:jobname]}.synctex.gz") do |gz|
+ File.open("#{params[:jobname]}.synctex", 'r') do |f|
+ f.readlines.each do |line|
# Replace tmp path with job path.
# Catch absolute tmp paths first, then try to match paths relative to job path.
gz.write line.sub("#{params[:jobpath]}/#{params[:tmpdir]}", params[:jobpath])\
.sub(params[:tmpdir], params[:jobpath])
- }
- }
- }
+ end
+ end
+ end
{ success: true, messages: [], log: '' }
end
end
-
+
Extension.add SyncTeX
diff --git a/lib/CliHelp.rb b/lib/CliHelp.rb
index 14c5f29..9886af9 100644
--- a/lib/CliHelp.rb
+++ b/lib/CliHelp.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
diff --git a/lib/DependencyManager.rb b/lib/DependencyManager.rb
index 487ad32..6ff2952 100644
--- a/lib/DependencyManager.rb
+++ b/lib/DependencyManager.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
diff --git a/lib/Engine.rb b/lib/Engine.rb
index 0acefd4..5b57d76 100644
--- a/lib/Engine.rb
+++ b/lib/Engine.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
diff --git a/lib/Extension.rb b/lib/Extension.rb
index f39c29e..23aa72c 100644
--- a/lib/Extension.rb
+++ b/lib/Extension.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -27,15 +27,15 @@ def self.add(e)
end
def self.list
- return @@list.values
+ @@list.values
end
def self.[](key)
- return @@list[key]
+ @@list[key]
end
def self.to_sym
- self.new.to_sym
+ new.to_sym
end
def initialize
@@ -45,24 +45,24 @@ def initialize
# Hacky hack? Need to refactor this
def self.name
- self.new.name
+ new.name
end
def self.description
- self.new.description
+ new.description
end
- if !@@dependencies.all? { |d| d.available? }
+ unless @@dependencies.all?(&:available?)
# Define skeleton class for graceful sequential fallback
module Parallel
class << self
- # TODO implement map
+ # TODO: implement map
# TODO test this!
- def each(hash, options={}, &block)
- hash.each { |k,v|
+ def each(hash, options = {}, &block)
+ hash.each do |k, v|
block.call(k, v)
options[:finish].call(nil, nil, nil)
- }
+ end
array
end
end
@@ -71,22 +71,22 @@ def each(hash, options={}, &block)
# Wrap execution of many items
def self.execute_parts(jobs, when_done, &block)
- if @@dependencies.all? { |d| d.available? }
+ if @@dependencies.all?(&:available?)
require 'system'
require 'parallel'
end
- Parallel.map(jobs, :finish => lambda { |a,b,c| when_done.call }) { |job|
+ Parallel.map(jobs, finish: ->(_, _, _) { when_done.call }) do |job|
begin
block.call(job)
rescue Interrupt
- raise Interrupt if !parallel # Sequential fallback needs exception!
+ raise Interrupt unless parallel # Sequential fallback needs exception!
rescue => e
Output.instance.msg("\tAn error occurred: #{e.to_s}")
- # TODO Should we break? Let's see what kinds of errors we get...
+ # TODO: Should we break? Let's see what kinds of errors we get...
end
- }
- # TODO do we have to care about Parallel::DeadWorker?
+ end
+ # TODO: do we have to care about Parallel::DeadWorker?
end
# Parameters
@@ -94,55 +94,54 @@ def self.execute_parts(jobs, when_done, &block)
# - output: an instance of Output
# - log: an instance of Log
def self.run_all(time, output, log)
- list.each { |e|
+ list.each do |e|
e = e.new
- if e.do?(time)
- # TODO make dep check more efficient
- dependencies = DependencyManager.list(source: [:extension, e.name], relevance: :essential)
- if dependencies.all? { |d| d.available? }
- progress, stop = output.start("#{e.name} running", e.job_size)
- r = e.exec(time, progress)
- stop.call(if r[:success] then :success else :error end)
- log.add_messages(e.name, :extension, r[:messages], r[:log])
- else
- # TODO log message?
- output.separate.error('Missing dependencies:', *dependencies.select { |d| !d.available? }.map { |d| d.to_s })
- end
+ next unless e.do?(time)
+
+ # TODO: make dep check more efficient
+ dependencies = DependencyManager.list(source: [:extension, e.name], relevance: :essential)
+ if dependencies.all?(&:available?)
+ progress, stop = output.start("#{e.name} running", e.job_size)
+ r = e.exec(time, progress)
+ stop.call(r[:success] ? :success : :error)
+ log.add_messages(e.name, :extension, r[:messages], r[:log])
+ else
+ # TODO: log message?
+ output.separate.error('Missing dependencies:', *dependencies.reject(&:available?).map(&:to_s))
end
- }
+ end
end
public
- def do?(time)
- false
- end
- def job_size
- 1
- end
+ def do?(time)
+ false
+ end
- def exec(time, progress)
- { success: true, messages: ['No execution code, need to overwrite!'], log: 'No execution code, need to overwrite!' }
- end
+ def job_size
+ 1
+ end
- def to_s
- @name
- end
+ def exec(time, progress)
+ { success: true, messages: ['No execution code, need to overwrite!'], log: 'No execution code, need to overwrite!' }
+ end
- def to_sym
- self.class.name.downcase.to_sym
- end
+ def to_s
+ @name
+ end
+
+ def to_sym
+ self.class.name.downcase.to_sym
+ end
- attr_accessor :name, :description
+ attr_reader :name, :description
protected
- attr_reader :params
- attr_writer :name, :description
+
+ attr_writer :name, :description
end
# Load all extensions
-Dir["#{BASEDIR}/#{EXTDIR}/*.rb"].sort.each { |f|
- if /^\d\d/ =~ File.basename(f)
- load(f)
- end
-}
+Dir["#{BASEDIR}/#{EXTDIR}/*.rb"].sort.each do |f|
+ load(f) if /^\d\d/ =~ File.basename(f)
+end
diff --git a/lib/FileListener.rb b/lib/FileListener.rb
index d759624..e854dd2 100644
--- a/lib/FileListener.rb
+++ b/lib/FileListener.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -39,9 +39,9 @@ def ignoreFileName(jobname = '')
def initialize
@ignore = []
- #ParameterManager.instance.addHook(:listeninterval) { |_,v|
- # TODO implement hook that catches changes to listen interval
- #}
+ # ParameterManager.instance.addHook(:listeninterval) { |_,v|
+ # TODO implement hook that catches changes to listen interval
+ # }
@jobfilelistener = nil
@ignfilelistener = nil
@dependencies = DependencyManager.list(source: [:core, self.class.to_s])
@@ -55,25 +55,25 @@ def ignored
# adds the contained files to the ignore list.
def readIgnoreFile(ignoreFile)
if File.exist?(ignoreFile)
- IO.foreach(ignoreFile) { |line|
+ IO.foreach(ignoreFile) do |line|
@ignore.push(line.strip)
- }
+ end
end
end
def start(jobname, ignores = [])
# Make sure that the listen gem is available
- @dependencies.each { |d|
- if !d.available?
+ @dependencies.each do |d|
+ unless d.available?
raise MissingDependencyError.new(d.to_s)
end
- }
-
- if @jobfilelistener != nil
+ end
+
+ unless @jobfilelistener.nil?
# Should never happen unless I programmed crap
raise StandardError.new('Listener already running, what are you doing?!')
end
-
+
params = ParameterManager.instance
# Add the files to ignore from this process
@@ -82,17 +82,17 @@ def start(jobname, ignores = [])
@ignore.push(@ignorefile)
# Write ignore list for other processes
- File.open("#{params[:jobpath]}/#{@ignorefile}", 'w') { |file|
+ File.open("#{params[:jobpath]}/#{@ignorefile}", 'w') do |file|
file.write(@ignore.join("\n"))
- # TODO make sure this file gets deleted!
- }
+ # TODO: make sure this file gets deleted!
+ end
# Collect all existing ignore files
Dir.entries('.') \
- .select { |f| /(\.\/)?#{Regexp.escape(ignoreFileName(''))}[^\/]+/ =~ f } \
- .each { |f|
+ .select { |f| /(\.\/)?#{Regexp.escape(ignoreFileName(''))}[^\/]+/ =~ f } \
+ .each do |f|
readIgnoreFile(f)
- }
+ end
# Setup daemon mode
@@ -107,53 +107,53 @@ def start(jobname, ignores = [])
/\A(\.\/)?(#{@ignore.map { |s| Regexp.escape(s) }.join('|')})/ ],
) \
do |modified, added, removed|
- # TODO cruel hack; can we do better?
- removed.each { |r|
+ # TODO: cruel hack; can we do better?
+ removed.each do |r|
@vanishedfiles.push File.path(r.to_s).sub(params[:jobpath], params[:tmpdir])
- }
+ end
@changetime = Time.now
end
- params.addHook(:listeninterval) { |key,val|
- # jobfilelistener.latency = val
- # TODO tell change to listener; in worst case, restart?
- }
- # TODO need hook on -i parameter?
-
- # Secondary listener: this one checks for (new) ignore files, i.e. other
- # jobs in the same directory. It then updates the main
- # listener so that it does not react to changes in files
- # generated by the other process.
- @ignfilelistener =
- Listen.to('.',
- only: /\A(\.\/)?#{Regexp.escape(ignoreFileName())}[^\/]+/,
- latency: 0.1
- ) \
- do |modified, added, removed|
- @jobfilelistener.pause
-
- added.each { |ignf|
- files = ignoremore(ignf)
- @jobfilelistener.ignore(/\A(\.\/)?(#{files.map { |s| Regexp.escape(s) }.join('|')})/)
- }
-
- # TODO If another daemon terminates we keep its ignorefiles. Potential leak!
- # If this turns out to be a problem, update list & listener (from scratch)
-
- @jobfilelistener.unpause
+ params.addHook(:listeninterval) do |key,val|
+ # jobfilelistener.latency = val
+ # TODO tell change to listener; in worst case, restart?
+ end
+ # TODO: need hook on -i parameter?
+
+ # Secondary listener: this one checks for (new) ignore files, i.e. other
+ # jobs in the same directory. It then updates the main
+ # listener so that it does not react to changes in files
+ # generated by the other process.
+ @ignfilelistener =
+ Listen.to('.',
+ only: /\A(\.\/)?#{Regexp.escape(ignoreFileName())}[^\/]+/,
+ latency: 0.1
+ ) \
+ do |modified, added, removed|
+ @jobfilelistener.pause
+
+ added.each do |ignf|
+ files = ignoremore(ignf)
+ @jobfilelistener.ignore(/\A(\.\/)?(#{files.map { |s| Regexp.escape(s) }.join('|')})/)
end
- @ignfilelistener.start
- @changetime = Time.now
- @lastraise = @changetime
- @jobfilelistener.start
+ # TODO: If another daemon terminates we keep its ignorefiles. Potential leak!
+ # If this turns out to be a problem, update list & listener (from scratch)
+
+ @jobfilelistener.unpause
+ end
+
+ @ignfilelistener.start
+ @changetime = Time.now
+ @lastraise = @changetime
+ @jobfilelistener.start
end
def waitForChanges(output)
output.start('Waiting for file changes (press ENTER to pause)')
@jobfilelistener.start if @jobfilelistener.paused?
params = ParameterManager.instance
-
+
files = Thread.new do
while @changetime <= @lastraise || Time.now - @changetime < params[:listeninterval]
sleep(params[:listeninterval] * 0.5)
@@ -163,11 +163,11 @@ def waitForChanges(output)
Thread.current[:raisetarget].raise(FilesChanged.new('Files have changed'))
end
files[:raisetarget] = Thread.current
-
+
begin
files.run
STDIN.noecho(&:gets)
- # User wants to enter prompt, so stop listening
+ # User wants to enter prompt, so stop listening
files.kill
@jobfilelistener.pause
output.stop(:cancel)
@@ -180,7 +180,7 @@ def waitForChanges(output)
# Rerun!
output.stop(:success)
@jobfilelistener.pause
- rescue Interrupt => e
+ rescue Interrupt => e
# User hit CTRL+C while waiting
raise e
rescue SystemExit => e
@@ -190,7 +190,7 @@ def waitForChanges(output)
# Remove files reported missing since last run from tmp (so we don't hide errors)
# Be extra careful, we don't want to delete non-tmp files!
- @vanishedfiles.each { |f| FileUtils.rm_rf(f) if f.start_with?(params[:tmpdir]) && File.exists?(f) }
+ @vanishedfiles.each { |f| FileUtils.rm_rf(f) if f.start_with?(params[:tmpdir]) && File.exist?(f) }
@vanishedfiles = []
end
@@ -223,8 +223,8 @@ def runs?
# Removes temporary files outside of the tmp folder,
# closes file handlers, etc.
def cleanup
- # TODO this really needs to be done via CLEAN
- FileUtils::rm("#{ParameterManager.instance[:jobpath]}/#{@ignorefile}")
+ # TODO: this really needs to be done via CLEAN
+ FileUtils.rm("#{ParameterManager.instance[:jobpath]}/#{@ignorefile}")
end
diff --git a/lib/HashManager.rb b/lib/HashManager.rb
index 609843b..6e8667a 100644
--- a/lib/HashManager.rb
+++ b/lib/HashManager.rb
@@ -19,21 +19,22 @@
require 'digest'
require 'singleton'
+# TODO: Document
class HashManager
include Singleton
-
+
def initialize
@hashes = {}
- end
-
+ end
+
public
-
+
# Hashes the given string
def self.hash(string)
Digest::MD5.hexdigest(string)
- # TODO SHA-256?
+ # TODO: SHA-256?
end
-
+
# Computes a hash of the given file.
# Parameters drop_from and without are optional; if specified,
# they have to be regexps.
@@ -50,81 +51,81 @@ def self.hash_file(filename, drop_from: nil, without: nil)
elsif drop_from == nil && without == nil
Digest::MD5.file(filename).to_s
else
- string = File.open(filename, 'r') { |f| f.read }
-
+ string = File.open(filename, 'r', &:read)
+
# Fix string encoding; regexp matching below may fail otherwise
# TODO check if this is necessary with Ruby versions beyond 2.0.0
- if !string.valid_encoding?
+ unless string.valid_encoding?
string = string.encode('UTF-16be',
- :invalid => :replace,
- :replace => '?').encode('UTF-8')
+ invalid: :replace,
+ replace: '?').encode('UTF-8')
end
-
+
# Drop undesired prefix if necessary
- if drop_from != nil && drop_from.is_a?(Regexp)
+ if !drop_from.nil? && drop_from.is_a?(Regexp)
string = string.split(drop_from).first
end
# Drop undesired lines if necessary
- if without != nil && without.is_a?(Regexp)
+ if !without.nil? && without.is_a?(Regexp)
lines = string.split("\n")
lines.reject! { |l| l =~ without }
string = lines.join("\n")
end
-
+
hash(string.strip)
end
end
-
+
# Returns true if (and only if) any of the specified files has been
- # created, changed or deleted between this and the last call of
+ # created, changed or deleted between this and the last call of
# the method of which it was a parameter.
- def files_changed?(*files)
+ def files_changed?(*files)
result = false
-
- files.each { |f|
- # TODO allow arrays with parameters for hash_file?
- if !File.exist?(f)
- if @hashes.has_key?(f)
+
+ files.each do |f|
+ # TODO: allow arrays with parameters for hash_file?
+ if !File.exist?(f)
+ if @hashes.key?(f)
@hashes.remove(f)
result = true
end
else
hash = self.class.hash_file(f)
- result = result || !@hashes.has_key?(f) || hash != @hashes[f]
+ result = result || !@hashes.key?(f) || hash != @hashes[f]
# puts "new or changed file #{f}" if !@@hashes.has_key?(f) || hash != @@hashes[f]
- @hashes[f] = hash
- end
- }
+ @hashes[f] = hash
+ end
+ end
result
end
-
+
# Reads hashes from a file in format
# filename,hash
# Overwrites any hashes that are already known.
def from_file(filename)
if File.exist?(filename)
- File.open(filename, 'r') { |f|
- f.readlines.each { |l|
+ File.open(filename, 'r') do |f|
+ f.readlines.each do |l|
parts = l.split(',')
# Comma may appear as filename, so we have to make sure to only
# use the last component as hash
@hashes[parts.take(parts.size - 1).join(',').strip] = parts.last.strip
- }
- }
+ end
+ end
end
end
-
- # Writes hashes to a file in format
+
+ # Writes hashes to a file in format
# filename,hash
# Overwrites existing file.
def to_file(filename)
- File.open(filename, 'w') { |f|
- @hashes.each_pair { |k,v|
+ File.open(filename, 'w') do |f|
+ @hashes.each_pair do |k,v|
f.write("#{k},#{v}\n")
- }
- }
+ end
+ end
end
def empty?
diff --git a/lib/Log.rb b/lib/Log.rb
index 1ecfbf4..81fcf35 100644
--- a/lib/Log.rb
+++ b/lib/Log.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -18,116 +18,119 @@
require "#{File.dirname(__FILE__)}/LogMessage.rb"
-class Log
+# TODO: Document
+class Log
def initialize
@messages = {}
- @counts = { :error => {:total => 0},
- :warning => {:total => 0},
- :info => {:total => 0}
- }
- #@level = :warning # or :error, :info
+ @counts = { error: { total: 0 },
+ warning: { total: 0 },
+ info: { total: 0 } }
+ # @level = :warning # or :error, :info
@rawoffsets = nil
@mode = :structured # or :flat
@dependencies = DependencyManager.list(source: [:core, self.class.to_s])
end
-
+
def only_level(level)
# Write messages from engine first
# (Since @messages contains only one entry per run engine/extension, this is fast.)
- keys = @messages.keys.select { |k| @messages[k][0] == :engine } +
+ keys = @messages.keys.select { |k| @messages[k][0] == :engine } +
@messages.keys.select { |k| @messages[k][0] == :extension }
- # TODO rewrite for efficiency: this should give an iterator without
+ # TODO: rewrite for efficiency: this should give an iterator without
# actually doing anything.
- keys.map { |k|
- msgs = @messages[k][1].select { |m|
+ keys.map do |k|
+ msgs = @messages[k][1].select do |m|
m.type == :error || # always show errors
- level == :info || # show everything at info level
- level == m.type # remaining case (warnings in :warning level)
- }
+ level == :info || # show everything at info level
+ level == m.type # remaining case (warnings in :warning level)
+ end
- {k => [@messages[k][0], msgs, @messages[k][2]] }
- }.reduce({}) { |res, e| res.merge!(e) }
+ { k => [@messages[k][0], msgs, @messages[k][2]] }
+ end.reduce({}) { |res, e| res.merge!(e) }
end
-
+
public
- attr_accessor :level # TODO implement flat mode?
- attr_reader :rawoffsets # TODO implement differently?
-
- # Parameters
- # 1. name of the source component (extension or engine)
- # 2. :engine or :extension
- # 3. List of LogMessage objects
- # 4. Raw log/output
- def add_messages(source, sourcetype, msgs, raw)
- if !@messages.has_key?(source)
- @messages[source] = [sourcetype, [], '']
- @counts[:error][source] = 0
- @counts[:warning][source] = 0
- @counts[:info][source] = 0
- end
-
- @messages[source][1] += msgs
- @messages[source][2] += "#{raw}"
- [:error, :warning, :info].each { |type|
- cnt = msgs.count { |e| e.type == type }
- @counts[type][source] += cnt
- @counts[type][:total] += cnt
- }
-
- @rawoffsets = nil
- end
-
- def has_messages?(source)
- @messages.has_key?(source)
- end
-
- def messages(source)
- @messages[source].clone
- end
-
- def empty?
- @messages.empty?
+
+ attr_accessor :level # TODO: implement flat mode?
+ attr_reader :rawoffsets # TODO implement differently?
+
+ # Parameters
+ # 1. name of the source component (extension or engine)
+ # 2. :engine or :extension
+ # 3. List of LogMessage objects
+ # 4. Raw log/output
+ def add_messages(source, sourcetype, msgs, raw)
+ unless @messages.key?(source)
+ @messages[source] = [sourcetype, [], '']
+ @counts[:error][source] = 0
+ @counts[:warning][source] = 0
+ @counts[:info][source] = 0
end
-
- def count(type, part = :total)
- @counts[type][part]
+
+ @messages[source][1] += msgs
+ @messages[source][2] += "#{raw}"
+ [:error, :warning, :info].each do |type|
+ cnt = msgs.count { |e| e.type == type }
+ @counts[type][source] += cnt
+ @counts[type][:total] += cnt
end
- # Creates a string with the raw log messages.
- def to_s
- # TODO it should be possible to determine offsets without building the log
- result = ''
- messages = only_level(:info)
-
- offset = 0
- @rawoffsets = {}
- messages.keys.each { |source|
- result << "# # # # #\n"
- result << "# Start #{source}"
- result << "\n# # # # #\n\n"
-
- @rawoffsets[source] = offset + 4
- result << messages[source][2]
-
- result << "\n\n# # # # #\n"
- result << "# Finished #{source}"
- result << "\n# # # # #\n\n"
-
- offset += 10 + messages[source][2].count(?\n)
- }
-
- result
+ @rawoffsets = nil
+ end
+
+ def has_messages?(source)
+ @messages.key?(source)
+ end
+
+ def messages(source)
+ @messages[source].clone
+ end
+
+ def empty?
+ @messages.empty?
+ end
+
+ def count(type, part = :total)
+ @counts[type][part]
+ end
+
+ # Creates a string with the raw log messages.
+ def to_s
+ # TODO: it should be possible to determine offsets without building the log
+ result = ''
+ messages = only_level(:info)
+
+ offset = 0
+ @rawoffsets = {}
+ messages.each_key do |source|
+ result << "# # # # #\n"
+ result << "# Start #{source}"
+ result << "\n# # # # #\n\n"
+
+ @rawoffsets[source] = offset + 4
+ result << messages[source][2]
+
+ result << "\n\n# # # # #\n"
+ result << "# Finished #{source}"
+ result << "\n# # # # #\n\n"
+
+ offset += 10 + messages[source][2].count(?\n)
end
-
- def self.fix(s)
- # Prevents errors when engines write illegal symbols to log.
- # Since the API changed between Ruby 1.8.x and 1.9, be
- # careful.
- RUBY_VERSION.to_f < 1.9 ?
- Iconv.iconv('UTF-8//IGNORE', 'UTF-8', s) :
- s.encode!(Encoding::UTF_16LE, :invalid => :replace,
- :undef => :replace,
- :replace => '?').encode!(Encoding::UTF_8)
+
+ result
+ end
+
+ def self.fix(s)
+ # Prevents errors when engines write illegal symbols to log.
+ # Since the API changed between Ruby 1.8.x and 1.9, be
+ # careful.
+ if RUBY_VERSION.to_f < 1.9
+ Iconv.iconv('UTF-8//IGNORE', 'UTF-8', s)
+ else
+ s.encode!(Encoding::UTF_16LE, invalid: :replace,
+ undef: :replace,
+ replace: '?').encode!(Encoding::UTF_8)
end
+ end
end
diff --git a/lib/LogMessage.rb b/lib/LogMessage.rb
index 30a5399..3318bd4 100644
--- a/lib/LogMessage.rb
+++ b/lib/LogMessage.rb
@@ -17,12 +17,11 @@
# along with ltx2any. If not, see .
class LogMessage
-
# Parameter type: one of :error, :warning, :info
# Parameter srcfile: name of the source file the message originated at
# nil if not available
# Parameter srcline: lines in the given file the message originated at
- # as array of integers [line] or [from,to].
+ # as array of integers [line] or [from,to].
# nil if not available
# Parameter logline: line(s) in the log the message was found at as array
# of integers [line] or [from,to].
@@ -38,36 +37,35 @@ def initialize(type, srcfile, srcline, logline, msg, format = :none)
@msg = msg
@format = format
end
-
+
public
- attr_accessor :type, :srcfile, :srcline, :logline, :msg
-
- def to_s
- result = if @type == :warning
- 'Warning'
- elsif @type == :error
- 'Error'
- else
- 'Info'
- end
-
- if @srcfile != nil
- result += " #{@srcfile}"
- if @srcline != nil
- result += ":#{@srcline.join('-')}"
- end
- end
-
- result += "\n" + @msg
-
- if @logline != nil
- result +="\n\t(For details see original output from line #{@logline[0].to_s}.)"
- end
- result
+ attr_accessor :type, :srcfile, :srcline, :logline, :msg
+
+ def to_s
+ result = if @type == :warning
+ 'Warning'
+ elsif @type == :error
+ 'Error'
+ else
+ 'Info'
+ end
+
+ unless @srcfile.nil?
+ result += " #{@srcfile}"
+ result += ":#{@srcline.join('-')}" unless @srcline.nil?
end
-
- def formatted?
- @format == :fixed
+
+ result += "\n" + @msg
+
+ unless @logline.nil?
+ result += "\n\t(For details see original output from line #{@logline[0]}.)"
end
+
+ result
+ end
+
+ def formatted?
+ @format == :fixed
+ end
end
diff --git a/lib/LogWriter.rb b/lib/LogWriter.rb
index b356da7..d46a413 100644
--- a/lib/LogWriter.rb
+++ b/lib/LogWriter.rb
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with ltx2any. If not, see .
+# TODO: document
class LogWriter
@@list = {}
@@dependencies = DependencyManager.list(source: [:core, 'LogWriter'])
@@ -25,11 +26,11 @@ def self.add(lw)
end
def self.list
- return @@list.values
+ @@list.values
end
def self.[](key)
- return @@list[key]
+ @@list[key]
end
def self.name
@@ -45,7 +46,7 @@ def self.to_sym
end
def self.to_s
- self.name
+ name
end
# Returns the name of the written file, or raises an exception
@@ -72,7 +73,7 @@ def self.break_at_spaces(s, length, indent)
res = ''
line = ' ' * [0, indent - 1].max
- words.each { |w|
+ words.each do |w|
newline = line + ' ' + w
if newline.length > length
res += line + "\n"
@@ -80,23 +81,23 @@ def self.break_at_spaces(s, length, indent)
else
line = newline
end
- }
+ end
res + line
end
end
# Load all extensions
-Dir["#{BASEDIR}/#{LOGWDIR}/*.rb"].sort.each { |f|
+Dir["#{BASEDIR}/#{LOGWDIR}/*.rb"].sort.each do |f|
load(f)
-}
+end
# Add log-writer-related parameters
[
- Parameter.new(:log, 'l', String, '"#{self[:user_jobname]}.log"',
- '(Base-)Name of log file'),
- Parameter.new(:logformat, 'lf', LogWriter.list.map { |lw| lw.to_sym }, :md,
- 'The log format. Call with --logformats for a list.'),
- Parameter.new(:loglevel, 'll', [:error, :warning, :info], :warning,
- "Set to 'error' to see only errors, to 'warning' to also see warnings, or to 'info' for everything.")
+ Parameter.new(:log, 'l', String, '"#{self[:user_jobname]}.log"',
+ '(Base-)Name of log file'),
+ Parameter.new(:logformat, 'lf', LogWriter.list.map(&:to_sym), :md,
+ 'The log format. Call with --logformats for a list.'),
+ Parameter.new(:loglevel, 'll', [:error, :warning, :info], :warning,
+ "Set to 'error' to see only errors, to 'warning' to also see warnings, or to 'info' for everything.")
].each { |p| ParameterManager.instance.addParameter(p) }
\ No newline at end of file
diff --git a/lib/Output.rb b/lib/Output.rb
index 5d296bb..1e95718 100644
--- a/lib/Output.rb
+++ b/lib/Output.rb
@@ -20,6 +20,7 @@
Dependency.new('ruby-progressbar', :gem, [:core, 'Output'], :recommended, 'nice progress indicators', '>=1.7.5')
+# TODO: document
class Output
include Singleton
@@ -35,81 +36,81 @@ def initialize
end
private
- def puts_indented(msgs)
- msgs.each { |m|
- puts "#{' ' * @shortcode.length} #{m}"
- }
- end
+
+ def puts_indented(msgs)
+ msgs.each { |m|
+ puts "#{' ' * @shortcode.length} #{m}"
+ }
+ end
public
- def msg(*msg)
- if !msg.empty? && !@running
- puts "#{@shortcode} #{msg[0]}"
- puts_indented(msg.drop(1)) if msg.size > 1
- STDOUT.flush
- elsif !msg.empty?
- # Store message to be printed after the current
- # process finished
- @pending << msg
- end
+
+ def msg(*msg)
+ if !msg.empty? && !@running
+ puts "#{@shortcode} #{msg[0]}"
+ puts_indented(msg.drop(1)) if msg.size > 1
+ STDOUT.flush
+ elsif !msg.empty?
+ # Store message to be printed after the current
+ # process finished
+ @pending << msg
end
+ end
- def warn(*msg)
- if msg.size > 0
- msg[0] = "#{@warning}: #{msg[0]}"
- msg(*msg)
- end
+ def warn(*msg)
+ unless msg.empty?
+ msg[0] = "#{@warning}: #{msg[0]}"
+ msg(*msg)
end
-
- def error(*msg)
- if msg.size > 0
- msg[0] = "#{@error}: #{msg[0]}"
- msg(*msg)
- end
+ end
+
+ def error(*msg)
+ unless msg.empty?
+ msg[0] = "#{@error}: #{msg[0]}"
+ msg(*msg)
end
+ end
- def start(msg, count=1)
- @running = true
- if count > 1 && @dependencies.all? { |d| d.available? }
- # Set up progress bar if needed
- require 'ruby-progressbar'
- progress = ProgressBar.create(:title => "#{@shortcode} #{msg} ...",
- :total => count,
- :format => '%t [%c/%C]',
- :autofinish => false)
- return [lambda { progress.increment },
- lambda { |state, *msgs|
- progress.format("#{@shortcode} #{msg} ... #{instance_variable_get(("@#{state}").intern).to_s}" + (' ' * 5)) # Add some spaces to hide all for up to 999 steps
- # TODO We *know* that we need 2*ceil(log_2(count)) - 1 spaces...
- progress.stop
- puts_indented(*msgs) if msgs.size > 0
- STDOUT.flush
- }]
- # TODO notify user of missing dependency?
- end
-
- # Fallback if progress bar not needed, or gem not available
- print "#{@shortcode} #{msg} ... "
- STDOUT.flush
- [lambda {}, lambda { |state, *msgs| stop(state, *msgs) }]
+ def start(msg, count = 1)
+ @running = true
+ if count > 1 && @dependencies.all?(&:available?)
+ # Set up progress bar if needed
+ require 'ruby-progressbar'
+ progress = ProgressBar.create(:title => "#{@shortcode} #{msg} ...",
+ :total => count,
+ :format => '%t [%c/%C]',
+ :autofinish => false)
+ return [-> { progress.increment },
+ lambda { |state, *msgs|
+ progress.format("#{@shortcode} #{msg} ... #{instance_variable_get("@#{state}".intern)}" + (' ' * 5)) # Add some spaces to hide all for up to 999 steps
+ # TODO We *know* that we need 2*ceil(log_2(count)) - 1 spaces...
+ progress.stop
+ puts_indented(*msgs) unless msgs.empty?
+ STDOUT.flush
+ }]
+ # TODO: notify user of missing dependency?
end
- def stop(state, *msg)
- puts instance_variable_get(("@#{state}").intern).to_s
- puts_indented(msg) if msg.size > 0
- STDOUT.flush
+ # Fallback if progress bar not needed, or gem not available
+ print "#{@shortcode} #{msg} ... "
+ STDOUT.flush
+ [-> {}, ->(state, *msgs) { stop(state, *msgs) }]
+ end
- # Print messages that were held back during the process
- # that just finished.
- @running = false
- @pending.each { |msgs|
- msg(*msgs)
- }
- @pending.clear
- end
+ def stop(state, *msg)
+ puts instance_variable_get("@#{state}".intern).to_s
+ puts_indented(msg) unless msg.empty?
+ STDOUT.flush
- def separate
- puts ''
- self
- end
+ # Print messages that were held back during the process
+ # that just finished.
+ @running = false
+ @pending.each { |msgs| msg(*msgs) }
+ @pending.clear
+ end
+
+ def separate
+ puts ''
+ self
+ end
end
diff --git a/lib/ParameterManager.rb b/lib/ParameterManager.rb
index 9d0abb0..7dc94c7 100644
--- a/lib/ParameterManager.rb
+++ b/lib/ParameterManager.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
#
# This file is part of ltx2any.
@@ -21,257 +21,254 @@
class ParameterManager
include Singleton
- # TODO Make it so that keys are (also) "long" codes as fas as users are concerned. Interesting for DaemonPrompt!
- # TODO add Array type (for -i -ir -ep ...)
+ # TODO: Make it so that keys are (also) "long" codes as fas as users are concerned. Interesting for DaemonPrompt!
+ # TODO: add Array type (for -i -ir -ep ...)
def initialize
@values = {}
@hooks = {}
@code2key = {}
@processed = false
- #@frozen_copy = nil
- #@copy_dirty = false
- #frozenCopy()
+ # @frozen_copy = nil
+ # @copy_dirty = false
+ # frozenCopy()
end
public
- def addParameter(p)
- if !@processed
- if p.is_a?(Parameter)
- if @values.has_key?(p.key)
- raise ParameterException.new("Parameter #{p.key} already exists.")
- else
- @values[p.key] = p
- @code2key[p.code] = p.key
- @hooks[p.key] = [] unless @hooks.has_key?(p.key)
- end
- else
- raise ParameterException.new("Can not add object of type #{p.class} as parameter.")
- end
- else
- raise ParameterException.new('Can not add parameters after CLI input has been processed.')
- end
+
+ def addParameter(p)
+ if @processed
+ raise ParameterException.new('Can not add parameters after CLI input has been processed.')
end
- def processArgs(args)
- # Check for input file first
- # Try to find an existing file by attaching common endings
- original = ARGV.last
- endings = ['tex', 'ltx', 'latex', '.tex', '.ltx', '.latex']
- jobfile = original
- while !File.exist?(jobfile) || File.directory?(jobfile)
- if endings.length == 0
- raise ParameterException.new("No input file fitting #{original} exists.")
- end
+ unless p.is_a?(Parameter)
+ raise ParameterException.new("Can not add object of type #{p.class} as parameter.")
+ end
- jobfile = "#{original}#{endings.pop}"
- end
- # TODO do basic checks as to whether we really have a LaTeX file?
+ if @values.key?(p.key)
+ raise ParameterException.new("Parameter #{p.key} already exists.")
+ end
- addParameter(Parameter.new(:jobpath, nil, String, File.dirname(File.expand_path(jobfile)),
- 'Absolute path of source directory'))
- addHook(:tmpdir) { |key,val|
- if self[:jobpath].start_with?(File.expand_path(val))
- raise ParameterException.new('Temporary directory may not contain job directory.')
- end
- }
- addParameter(Parameter.new(:jobfile, nil, String, File.basename(jobfile), 'Name of the main input file'))
- addParameter(Parameter.new(:jobname, nil, String, /\A(.+?)\.\w+\z/.match(self[:jobfile])[1],
- 'Internal job name, in particular name of the main file and logs.'))
- set(:user_jobname, self[:jobname]) if self[:user_jobname] == nil
-
- # Read in parameters
- # TODO use/build proper CLI and parameter handler?
- i = 0
- while i < ARGV.length - 1
- p = /\A-(\w+)\z/.match(ARGV[i])
- if p != nil
- code = p[1]
- key = @code2key[code]
-
- if @values.has_key?(key)
- if @values[key].type == Boolean
- set(key, :true)
- i += 1
- else
- val = ARGV[i+1]
- if i + 1 < ARGV.length - 1
- set(key, val) # Does all the checking and converting
- i += 2
- else
- raise ParameterException.new("No value for parameter -#{code}.")
- end
- end
- else
- raise ParameterException.new("Parameter -#{code} does not exist.")
- end
- else
- raise ParameterException.new("Don't know what to do with parameter #{ARGV[i]}.")
- end
- end
-
- # Evaluate remaining defaults that need to/can be evaluated
- # TODO Parameter values now contain user input. Security risk?
- keys.each { |key|
- val = @values[key].value
- if val != nil && val.is_a?(String) && val.length > 0
- begin
- @values[key].value = eval(val)
- rescue Exception => e
- # Leave value unchanged
- # puts "From eval on #{key}: #{e.message}"
- end
- end
- }
+ @values[p.key] = p
+ @code2key[p.code] = p.key
+ @hooks[p.key] = [] unless @hooks.key?(p.key)
+ end
- if jobfile == nil
- raise ParameterException.new('Please provide an input file. Call with --help for details.')
+ def processArgs(args)
+ # Check for input file first
+ # Try to find an existing file by attaching common endings
+ original = ARGV.last
+ endings = ['tex', 'ltx', 'latex', '.tex', '.ltx', '.latex']
+ jobfile = original
+ while !File.exist?(jobfile) || File.directory?(jobfile)
+ if endings.empty?
+ raise ParameterException.new("No input file fitting #{original} exists.")
end
- @processed = true
+ jobfile = "#{original}#{endings.pop}"
end
+ # TODO: do basic checks as to whether we really have a LaTeX file?
- def [](key)
- if @values.has_key?(key)
- @values[key].value
- else
- nil
+ addParameter(Parameter.new(:jobpath, nil, String, File.dirname(File.expand_path(jobfile)),
+ 'Absolute path of source directory'))
+ addHook(:tmpdir) do |key, val|
+ if self[:jobpath].start_with?(File.expand_path(val))
+ raise ParameterException.new('Temporary directory may not contain job directory.')
end
end
+ addParameter(Parameter.new(:jobfile, nil, String, File.basename(jobfile), 'Name of the main input file'))
+ addParameter(Parameter.new(:jobname, nil, String, /\A(.+?)\.\w+\z/.match(self[:jobfile])[1],
+ 'Internal job name, in particular name of the main file and logs.'))
+ set(:user_jobname, self[:jobname]) if self[:user_jobname].nil?
+
+ # Read in parameters
+ # TODO use/build proper CLI and parameter handler?
+ i = 0
+ while i < ARGV.length - 1
+ p = /\A-(\w+)\z/.match(ARGV[i])
+ if p.nil?
+ raise ParameterException.new("Don't know what to do with parameter #{ARGV[i]}.")
+ end
- def []=(key,val)
- set(key, val, false)
- end
+ code = p[1]
+ key = @code2key[code]
- def set(key, val, once=false) # TODO implement "once" behaviour
- # TODO allow for proper validation functions?
- # TODO fall back to defaults instead of killing?
- if !@values.has_key?(key)
- raise ParameterException.new("Parameter #{key} does not exist.")
+ unless @values.key?(key)
+ raise ParameterException.new("Parameter -#{code} does not exist.")
end
- code = @values[key].code
-
- if @values[key].type == String
- @values[key].value = val.strip
- elsif @values[key].type == Integer
- if val.is_a?(Integer)
- @values[key].value = val
- elsif /\d+/ =~ val
- @values[key].value = val.to_i
- else
- raise ParameterException.new("Parameter -#{code} requires an integer ('#{val}' given).")
- end
- elsif @values[key].type == Float
- if val.is_a?(Float)
- @values[key].value = val
- elsif /\d+(\.\d+)?/ =~ val
- @values[key].value = val.to_f
- else
- raise ParameterException.new("Parameter -#{code} requires a number ('#{val}' given).")
- end
- elsif @values[key].type == Boolean
- if val.is_a?(Boolean)
- @values[key].value = val
- elsif val.to_s.to_sym == :true || val.to_s.to_sym == :false
- @values[key].value = ( val.to_s.to_sym == :true )
- else
- raise ParameterException.new("Parameter -#{code} requires a boolean ('#{val}' given).")
- end
- elsif @values[key].type.is_a? Array
- if @values[key].type.include?(val.to_sym)
- @values[key].value = val.to_sym
- else
- raise ParameterException.new("Invalid value '#{val}' for parameter -#{code}\nChoose one of [#{@values[key].type.map { |e| e.to_s }.join(', ')}].")
- end
+
+ if @values[key].type == Boolean
+ set(key, :true)
+ i += 1
else
- # This should never happen
- raise RuntimeError.new("Parameter -#{code} has unknown type #{@values[key].type}.")
+ val = ARGV[i+1]
+ unless i + 1 < ARGV.length - 1
+ raise ParameterException.new("No value for parameter -#{code}.")
+ end
+
+ set(key, val) # Does all the checking and converting
+ i += 2
end
+ end
- @hooks[key].each { |b|
- b.call(key, @values[key].value)
- }
+ # Evaluate remaining defaults that need to/can be evaluated
+ # TODO Parameter values now contain user input. Security risk?
+ keys.each do |key|
+ val = @values[key].value
+ next unless !val.nil? && val.is_a?(String) && !val.empty?
+
+ begin
+ @values[key].value = eval(val)
+ rescue Exception => e
+ # Leave value unchanged
+ # puts "From eval on #{key}: #{e.message}"
+ end
+ end
- #@copy_dirty = true
+ if jobfile.nil?
+ raise ParameterException.new('Please provide an input file. Call with --help for details.')
end
- def add(key, val, once=false) # TODO implement "once" behaviour
- if @values.has_key?(key)
- if @values[key].type == String
- @values[key].value += val.to_s # TODO should we add separating `:`?
+ @processed = true
+ end
+
+ def [](key)
+ if @values.key?(key)
+ @values[key].value
+ else
+ nil
+ end
+ end
- @hooks[key].each { |b|
- b.call(key, @values[key].value)
- }
+ def []=(key, val)
+ set(key, val, false)
+ end
- #@copy_dirty = true
- else
- raise ParameterException.new("Parameter #{key} does not support extension.")
- end
+ def set(key, val, once = false) # TODO: implement "once" behaviour
+ # TODO allow for proper validation functions?
+ # TODO fall back to defaults instead of killing?
+ unless @values.key?(key)
+ raise ParameterException.new("Parameter #{key} does not exist.")
+ end
+ code = @values[key].code
+
+ if @values[key].type == String
+ @values[key].value = val.strip
+ elsif @values[key].type == Integer
+ if val.is_a?(Integer)
+ @values[key].value = val
+ elsif /\d+/ =~ val
+ @values[key].value = val.to_i
else
- raise ParameterException.new("Parameter #{key} does not exist.")
+ raise ParameterException.new("Parameter -#{code} requires an integer ('#{val}' given).")
end
- end
-
- def addHook(key, &block)
- #if ( @values.has_key?(key) )
- if !@hooks.has_key?(key)
- @hooks[key] = []
+ elsif @values[key].type == Float
+ if val.is_a?(Float)
+ @values[key].value = val
+ elsif /\d+(\.\d+)?/ =~ val
+ @values[key].value = val.to_f
+ else
+ raise ParameterException.new("Parameter -#{code} requires a number ('#{val}' given).")
end
-
- if block.arity == 2
- @hooks[key].push(block)
+ elsif @values[key].type == Boolean
+ if val.is_a?(Boolean)
+ @values[key].value = val
+ elsif val.to_s.to_sym == :true || val.to_s.to_sym == :false
+ @values[key].value = ( val.to_s.to_sym == :true )
+ else
+ raise ParameterException.new("Parameter -#{code} requires a boolean ('#{val}' given).")
+ end
+ elsif @values[key].type.is_a? Array
+ if @values[key].type.include?(val.to_sym)
+ @values[key].value = val.to_sym
else
- raise ParameterException.new('Parameter hooks need to take two parameters (key, new value).')
+ raise ParameterException.new("Invalid value '#{val}' for parameter -#{code}\nChoose one of [#{@values[key].type.map { |e| e.to_s }.join(', ')}].")
end
- #else
- # raise ParameterException.new("Parameter #{key} does not exist.")
- #end
+ else
+ # This should never happen
+ raise RuntimeError.new("Parameter -#{code} has unknown type #{@values[key].type}.")
+ end
+
+ @hooks[key].each do |b|
+ b.call(key, @values[key].value)
+ end
+
+ # @copy_dirty = true
+ end
+
+ def add(key, val, once = false) # TODO: implement "once" behaviour
+ unless @values.key?(key)
+ raise ParameterException.new("Parameter #{key} does not exist.")
+ end
+
+ unless @values[key].type == String
+ raise ParameterException.new("Parameter #{key} does not support extension.")
end
- def keys
- @values.keys
+ @values[key].value += val.to_s # TODO: should we add separating `:`?
+
+ @hooks[key].each do |b|
+ b.call(key, @values[key].value)
end
- def reset
- # TODO clear "once" settings
- # @copy_dirty = false
+ # @copy_dirty = true
+ end
+
+ def addHook(key, &block)
+ # if ( @values.has_key?(key) )
+ @hooks[key] = [] unless @hooks.key?(key)
+
+ unless block.arity == 2
+ raise ParameterException.new('Parameter hooks need to take two parameters (key, new value).')
end
- #def frozenCopy
- # if ( @frozen_copy == nil || @copy_dirty )
- # # TODO create a deep copy
- # copy = self.clone
- # copy.values = @values.clone # not deep enough!
- # copy.freeze
- # @frozen_copy = copy
- # @copy_dirty = false
- # end
- #
- # return @frozen_copy
- #end
+ @hooks[key].push(block)
+ end
+
+ def keys
+ @values.keys
+ end
+
+ def reset
+ # TODO: clear "once" settings
+ # @copy_dirty = false
+ end
+
+ # def frozenCopy
+ # if ( @frozen_copy == nil || @copy_dirty )
+ # # TODO create a deep copy
+ # copy = self.clone
+ # copy.values = @values.clone # not deep enough!
+ # copy.freeze
+ # @frozen_copy = copy
+ # @copy_dirty = false
+ # end
+ #
+ # return @frozen_copy
+ # end
def user_info
- @values.keys.select { |key|
- @values[key].code != nil
- }.sort { |a,b|
- @values[a].code <=> @values[b].code
- }. map { |key|
- { :code => @values[key].code, :type => @values[key].type, :help => @values[key].help }
- }
+ @values.keys.reject do |key|
+ @values[key].code.nil?
+ end.sort do |a, b|
+ @values[a].code <=> @values[b].code
+ end.map do |key|
+ { code: @values[key].code, type: @values[key].type, help: @values[key].help }
+ end
end
def to_s
- @values.keys.map { |key|
+ @values.keys.map do |key|
"#{key}\t\t#{self[key]}"
- }.join("\n")
+ end.join("\n")
end
protected
- attr_reader :values
- attr_writer :values
+
+ attr_accessor :values
end
+# TODO: document
class Parameter
# Pass code = nil for an internal parameter that is not shown to users.
def initialize(key, code, type, default, help)
@@ -283,18 +280,20 @@ def initialize(key, code, type, default, help)
end
public
- def value=(val)
- if (@type.is_a?(Array) && val.is_a?(@type[0].class)) || val.is_a?(@type)
- @value = val
- else
- raise ParameterException.new("Value if type #{val.class} is not compatible with parameter #{@key}.")
- end
+
+ def value=(val)
+ unless (@type.is_a?(Array) && val.is_a?(@type[0].class)) || val.is_a?(@type)
+ raise ParameterException.new("Value if type #{val.class} is not compatible with parameter #{@key}.")
end
+ @value = val
+ end
+
attr_reader :key, :code, :type, :value, :help
end
-class ParameterException < Exception
+# TODO: document
+class ParameterException < RuntimeError
def initialize(msg)
super(msg)
end
diff --git a/lib/TeXLogParser.rb b/lib/TeXLogParser.rb
index 74d955b..d346bd7 100644
--- a/lib/TeXLogParser.rb
+++ b/lib/TeXLogParser.rb
@@ -18,9 +18,10 @@
require "#{File.dirname(__FILE__)}/LogMessage.rb"
+# TODO: document
class TeXLogParser
-
- # Input:
+
+ # Input:
# * log -- string array (one entry per line)
# * startregexp -- start collecting messages after first match
# Default: matches any line, thus collects from the start
@@ -31,18 +32,18 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
# Contains a stack of currently "open" files.
# filestack.last is the current one.
filestack = []
-
+
# Result collection
messages = []
-
+
# The stack of files the log is currently "in"
filestack = []
-
+
collecting = false
linectr = 1 # Declared for the increment at the end of the loop
current = Finalizer.new
ongoing = nil
- log.each { |line|
+ log.each do |line|
if !collecting && startregexp =~ line
collecting = true
linectr = 1
@@ -55,37 +56,33 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
# Even when not collecting, we need to keep track of which file
# we are in.
if collecting && line.strip == ''
- # Empty line ends messages
+ # Empty line ends messages
messages += [current.get_msg].compact
elsif /^l\.(\d+) (.*)$/ =~ line
# Line starting with a line number ends messages
- if current.type != nil
- if current.srcline == nil
- current.srcline = [Integer($~[1])]
- end
+ unless current.type.nil?
+ current.srcline = [Integer($~[1])] if current.srcline.nil?
current.message += $~[2].strip
current.logline[1] = linectr
- messages += [current.get_msg].compact
+ messages += [current.get_msg].compact
end
elsif /^<\*> (.*)$/ =~ line
# Some messages end with a line of the form '<*> file'
- if current.type != nil
+ unless current.type.nil?
current.srcfile = $~[1].strip
current.logline[1] = linectr
messages += [current.get_msg].compact
end
elsif /^(\([^()]*\)|[^()])*\)/ =~ line
# End of messages regarding current file
- if collecting
- messages += [current.get_msg].compact
- end
+ messages += [current.get_msg].compact if collecting
filestack.pop
-
+
# Multiple files may close; cut away matching part and start over.
line = line.gsub($~.regexp, '')
redo
- elsif current.type == nil && # When we have an active message, it has
+ elsif current.type.nil? && # When we have an active message, it has
# to complete before a new file can open.
# Probably. (Without, error messages with
# opening but no closing parenthesis would
@@ -98,14 +95,14 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
#
# A new file has started. Match only those that don't close immediately.
candidate = $~[2]
-
+
while !File.exist?(candidate) && candidate != '' do # TODO can be long; use heuristics?
candidate = candidate[0,candidate.length - 1]
end
if File.exist?(candidate)
filestack.push(candidate)
- else
- # Lest we break everything by false negatives (due to linebreaks),
+ else
+ # Lest we break everything by false negatives (due to linebreaks),
# add a dummy and hope it closes.
filestack.push('dummy')
end
@@ -117,10 +114,10 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
line = line.gsub($~.regexp, replace)
redo
elsif collecting # Do all the checks only when collecting
- if /^(\S*?):(\d+): (.*)/ =~ line && ongoing == nil # such lines appear in fontspec-style messages, see below
+ if /^(\S*?):(\d+): (.*)/ =~ line && ongoing.nil? # such lines appear in fontspec-style messages, see below
messages += [current.get_msg].compact
# messages.push(LogMessage.new(:error, $~[1], [Integer($~[2])], [linectr], $~[3].strip))
-
+
current.type = :error
current.srcfile = $~[1]
current.srcline = [Integer($~[2])]
@@ -131,12 +128,12 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
elsif /(Package|Class)\s+([\w]+)\s+(Warning|Error|Info)/ =~ line
# Message from some package or class, may be multi-line
messages += [current.get_msg].compact
-
+
current.type = if $~[3] == 'Warning'
then :warning
elsif $~[3] == 'Info'
then :info
- else :error
+ else :error
end
current.srcfile = filestack.last
current.srcline = nil
@@ -150,7 +147,7 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
current.type = if $~[1] == 'Warning'
then :warning
elsif $~[1] == 'Info'
- then :info
+ then :info
else :error
end
current.srcfile = filestack.last
@@ -161,10 +158,10 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
elsif /^(LaTeX Font Warning: .*?)(?: #{space_sep('on input line')} (\d+).)?$/ =~ line
# Some issue with fonts
messages += [current.get_msg].compact
-
+
current.type = :warning
current.srcfile = filestack.last
- current.srcline = if $~[2] then [Integer($~[2])] else nil end
+ current.srcline = $~[2] ? [Integer($~[2])] : nil
current.logline = [linectr]
current.message = $~[1].strip
current.slicer = /^\(Font\)\s*/
@@ -183,7 +180,7 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
# TODO What for chains?
srcLine = [toLine]
end
-
+
messages.push(LogMessage.new(:warning, filestack.last, srcLine, [linectr], $~[1].strip))
elsif /^((Under|Over)full .*?)[\d\[\]]*$/ =~ line
messages += [current.get_msg].compact
@@ -199,7 +196,7 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
elsif /^!!+/ =~ line
# Messages in the style of fontspec
messages += [current.get_msg].compact
-
+
ongoing = :fontspec
current.type = :error
current.srcfile = filestack.last # may be overwritten later
@@ -221,84 +218,77 @@ def self.parse(log, startregexp = /.*/, endregexp = /(?=a)b/)
# Drop useless note
elsif /^!(.*)/ =~ line
# A new line
- if $~[1].strip.size > 0
- current.message += $~[1].strip + "\n"
- end
- end
+ current.message += $~[1].strip + "\n" unless $~[1].strip.empty?
+ end
elsif /^! (.*?)(after line (\d+).)?$/ =~ line
messages += [current.get_msg].compact
current.type = :error
current.srcfile = filestack.last
- current.srcline = if $~[3] then [Integer($~[3])] else nil end
+ current.srcline = $~[3] ? [Integer($~[3])] : nil
current.logline = [linectr]
- current.message = $~[1] + (if $~[2] then $~[2] else
- ''
- end)
- elsif current.type != nil
- if current.slicer != nil
- line = line.gsub(current.slicer, '')
- end
- if current.format != :fixed
- line = ' ' + line.strip!
- end
+ current.message = $~[1] + ($~[2] ? $~[2] : '')
+ elsif !current.type.nil?
+ line = line.gsub(current.slicer, '') unless current.slicer.nil?
+ line = ' ' + line.strip! if current.format != :fixed
current.message += line
current.logline[1] = linectr
end
end
-
+
linectr += 1
- }
-
+ end
+
return messages
end
-
+
private
-
- # TeX logs may have spaces in weird places. If we don't want our regexp
- # matching to stumble over that, longer strings have to be matched
- # allowing for whitespace everywhere.
- # Use the result of this method for this purpose.
- def self.space_sep(s)
- s.chars.join('\s*')
+
+ # TeX logs may have spaces in weird places. If we don't want our regexp
+ # matching to stumble over that, longer strings have to be matched
+ # allowing for whitespace everywhere.
+ # Use the result of this method for this purpose.
+ def self.space_sep(s)
+ s.chars.join('\s*')
+ end
+
+ # Some messages may run over multiple lines. Use an instance
+ # of this class to collect it completely.
+ class Finalizer
+ def initialize
+ reset
+ end
+
+ def reset
+ @type = nil
+ @srcfile = nil
+ @srcline = nil
+ @logline = nil
+ @message = nil
+ @format = :none
+
+ @slicer = nil
end
-
- # Some messages may run over multiple lines. Use an instance
- # of this class to collect it completely.
- class Finalizer
- def initialize
+
+ public
+
+ attr_accessor :type, :srcfile, :srcline, :logline, :message, :format, :slicer
+
+ # (initially: @currentmessage = [nil, nil, nil, nil, nil, nil, :none] )
+ def get_msg()
+ if !@type.nil?
+ if @srcline.nil? && @message =~ /(.+?) #{TeXLogParser::space_sep('on input line')} (\d+)\.?$/
+ # The first line did not contain the line of warning, but
+ # the last did!
+ @message = $~[1].strip
+ @srcline = [Integer($~[2])]
+ end
+ res = LogMessage.new(@type, @srcfile, @srcline, @logline, @message, @format)
+ reset
+ res
+ else
reset
+ nil
end
-
- def reset
- @type = nil
- @srcfile = nil
- @srcline = nil
- @logline = nil
- @message = nil
- @format = :none
-
- @slicer = nil
- end
-
- public
- attr_accessor :type, :srcfile, :srcline, :logline, :message, :format, :slicer
-
- # (initially: @currentmessage = [nil, nil, nil, nil, nil, nil, :none] )
- def get_msg()
- if @type != nil
- if @srcline == nil && @message =~ /(.+?) #{TeXLogParser::space_sep('on input line')} (\d+)\.?$/
- # The first line did not contain the line of warning, but
- # the last did!
- @message = $~[1].strip
- @srcline = [Integer($~[2])]
- end
- res = LogMessage.new(@type, @srcfile, @srcline, @logline, @message, @format)
- reset
- res
- else
- reset
- nil
- end
- end
end
+ end
end
diff --git a/logwriters/LaTeX.rb b/logwriters/LaTeX.rb
index d62f562..cdc6758 100644
--- a/logwriters/LaTeX.rb
+++ b/logwriters/LaTeX.rb
@@ -31,7 +31,7 @@ def self.to_sym
# Returns the name of the written file, or raises an exception
def self.write(log, level = :warning)
- # TODO compute offsets in a smarter way
+ # TODO: compute offsets in a smarter way
log.to_s if log.rawoffsets == nil # Determines offsets in raw log
params = ParameterManager.instance
@@ -41,24 +41,24 @@ def self.write(log, level = :warning)
File.open("#{File.dirname(__FILE__)}/logtemplate.tex", 'r') { |template|
f.write(template.read)
}
- f.write("\\def\\author{ltx2any}\n\\def\\title{Log for #{params[:user_jobname]}}\n")
- f.write("\\def\\fulllog{#{File.join(params[:tmpdir], "#{params[:log]}.full")}}\n")
- f.write("\n\n\\begin{document}")
+ f.write("\\def\\author{ltx2any}\n\\def\\title{Log for #{params[:user_jobname]}}\n" \
+ "\\def\\fulllog{#{File.join(params[:tmpdir], "#{params[:log]}.full")}}\n" \
+ "\n\n\\begin{document}")
f.write("\\section{Log for \\texttt{\\detokenize{#{params[:user_jobname]}}}}\n\n")
messages = log.only_level(level)
- f.write("\\textbf{Disclaimer:} This is but a digest of the original log file. " +
- "For full detail, check out \\loglink. " +
- 'In case we failed to pick up an error or warning, please ' +
- "\\href{https://github.com/akerbos/ltx2any/issues/new}{report it to us}.\n\n")
+ f.write("\\textbf{Disclaimer:} This is but a digest of the original log file. " \
+ "For full detail, check out \\loglink. " \
+ 'In case we failed to pick up an error or warning, please ' \
+ "\\href{https://github.com/akerbos/ltx2any/issues/new}{report it to us}.\n\n")
- f.write("We found \\errlink{\\textbf{#{log.count(:error)}~error#{pls(log.count(:error))}}}, " +
- "\\textsl{#{log.count(:warning)}~warning#{pls(log.count(:warning))}} " +
- "and #{log.count(:info)}~other message#{pls(log.count(:info))} in total.\n\n")
+ f.write("We found \\errlink{\\textbf{#{log.count(:error)}~error#{pls(log.count(:error))}}}, " \
+ "\\textsl{#{log.count(:warning)}~warning#{pls(log.count(:warning))}} " \
+ "and #{log.count(:info)}~other message#{pls(log.count(:info))} in total.\n\n")
# Write everything
- messages.keys.each { |name|
+ messages.each_key { |name|
# We get one block per tool that ran
msgs = messages[name][1]
@@ -93,9 +93,9 @@ def self.write(log, level = :warning)
f.write("\n\\end{verbatim}")
# Write the raw log reference
- if m.logline != nil
+ unless m.logline.nil?
# We have line offsets in the raw log!
- logline = m.logline.map { |i| i += log.rawoffsets[name] }
+ logline = m.logline.map { |i| i + log.rawoffsets[name] }
logline.push('') if logline.length < 2
f.write("\n\n\\logref{#{logline[0]}}{#{logline[1]}}")
end
@@ -117,8 +117,8 @@ def self.write(log, level = :warning)
def self.makeFileref(file, linefrom, lineto)
fileref = ''
- if file != nil
- #file = file.gsub(/_/, '\_')
+ unless file.nil?
+ # file = file.gsub(/_/, '\_')
fileref = "\\fileref{#{file}}{#{linefrom}}{#{lineto}}"
end
fileref
diff --git a/logwriters/Markdown.rb b/logwriters/Markdown.rb
index 011241c..b431d40 100644
--- a/logwriters/Markdown.rb
+++ b/logwriters/Markdown.rb
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with ltx2any. If not, see .
+# TODO: Document
class Markdown < LogWriter
def self.name
'Markdown'
@@ -31,25 +32,25 @@ def self.to_sym
# Returns the name of the written file, or raises an exception
def self.write(log, level = :warning)
- # TODO compute rawoffsets in Log
- log.to_s if log.rawoffsets == nil # Determines offsets in raw log
+ # TODO: compute rawoffsets in Log
+ log.to_s if log.rawoffsets.nil? # Determines offsets in raw log
params = ParameterManager.instance
result = "# Log for `#{params[:user_jobname]}`\n\n"
messages = log.only_level(level)
- result << "**Disclaimer:** \nThis is but a digest of the original log file.\n" +
- "For full detail, check out `#{params[:tmpdir]}/#{params[:log]}.full`.\n" +
- 'In case we failed to pick up an error or warning, please ' +
- "[report it to us](https://github.com/akerbos/ltx2any/issues/new).\n\n"
+ result << "**Disclaimer:** \nThis is but a digest of the original log file.\n" \
+ "For full detail, check out `#{params[:tmpdir]}/#{params[:log]}.full`.\n" \
+ 'In case we failed to pick up an error or warning, please ' \
+ "[report it to us](https://github.com/akerbos/ltx2any/issues/new).\n\n"
- result << "We found **#{log.count(:error)} error#{pls(log.count(:error))}**, " +
- "*#{log.count(:warning)} warning#{pls(log.count(:warning))}* " +
- "and #{log.count(:info)} other message#{pls(log.count(:info))} in total.\n\n"
+ result << "We found **#{log.count(:error)} error#{pls(log.count(:error))}**, " \
+ "*#{log.count(:warning)} warning#{pls(log.count(:warning))}* " \
+ "and #{log.count(:info)} other message#{pls(log.count(:info))} in total.\n\n"
# Write everything
- messages.keys.each { |name|
+ messages.each_key do |name|
msgs = messages[name][1]
result << "## `#{name}`\n\n"
@@ -58,26 +59,24 @@ def self.write(log, level = :warning)
result << "Lucky you, `#{name}` had nothing to complain about!\n\n"
if (level == :warning && log.count(:info, name) > 0) ||
- (level == :error && log.count(:info, name) + log.count(:warning, name) > 0)
+ (level == :error && log.count(:info, name) + log.count(:warning, name) > 0)
if level != :info
result << 'Note, though, that this log only lists errors'
- if level == :warning
- result << ' and warnings'
- end
+ result << ' and warnings' if level == :warning
result << '. There were '
if level == :error
result << "#{log.count(:warning, name)} warning#{pls(log.count(:warning, name))} and "
end
- result << "#{log.count(:info, name)} information message#{pls(log.count(:info, name))} " +
- "which you find in the full log.\n\n"
+ result << "#{log.count(:info, name)} information message#{pls(log.count(:info, name))} " \
+ "which you find in the full log.\n\n"
end
end
else
- result << "**#{log.count(:error, name)} error#{pls(log.count(:error, name))}**, " +
- "*#{log.count(:warning, name)} warning#{pls(log.count(:warning, name))}* " +
- "and #{log.count(:info, name)} other message#{pls(log.count(:info, name))}\n\n"
+ result << "**#{log.count(:error, name)} error#{pls(log.count(:error, name))}**, " \
+ "*#{log.count(:warning, name)} warning#{pls(log.count(:warning, name))}* " \
+ "and #{log.count(:info, name)} other message#{pls(log.count(:info, name))}\n\n"
- msgs.each { |m|
+ msgs.each do |m|
# Lay out for 80 characters width
# * 4 colums for list stuff
# * 11 columns for type + space
@@ -85,40 +84,34 @@ def self.write(log, level = :warning)
# * The message, indented to the type stands out
# * Log line, flushed right
result << ' * ' +
- { :error => '**Error**',
- :warning => '*Warning*',
- :info => 'Info '
- }[m.type]
- if m.srcfile != nil
+ { error: '**Error**',
+ warning: '*Warning*',
+ info: 'Info ' }[m.type]
+ unless m.srcfile.nil?
srcline = nil
- if m.srcline != nil
- srcline = m.srcline.join('--')
- end
- srcfilelength = 76 - 9 - (if srcline != nil then srcline.length + 1 else 0 end) - 2
+ srcline = m.srcline.join('--') unless m.srcline.nil?
+ srcfilelength = 76 - 9 - (!srcline.nil? ? srcline.length + 1 : 0) - 2
result << if m.srcfile.length > srcfilelength
" `...#{m.srcfile[m.srcfile.length - srcfilelength + 5, m.srcfile.length]}"
else
(' ' * (srcfilelength - m.srcfile.length)) + "`#{m.srcfile}"
end
- if srcline != nil
- result << ":#{srcline}"
- end
+ result << ":#{srcline}" unless srcline.nil?
result << '`'
end
result << "\n\n"
- if m.formatted?
- result << indent(m.msg.strip, 8) + "\n\n"
- else
- result << break_at_spaces(m.msg.strip, 68, 8) + "\n\n"
- end
- if m.logline != nil
- # We have line offset in the raw log!
- logline = m.logline.map { |i| i += log.rawoffsets[name] }.join('--')
- result << (' ' * (80 - (6 + logline.length))) + '`log:' + logline + "`\n\n\n"
- end
- }
+ result << if m.formatted?
+ indent(m.msg.strip, 8) + "\n\n"
+ else
+ break_at_spaces(m.msg.strip, 68, 8) + "\n\n"
+ end
+ next if m.logline.nil?
+ # We have line offset in the raw log!
+ logline = m.logline.map { |i| i + log.rawoffsets[name] }.join('--')
+ result << (' ' * (80 - (6 + logline.length))) + '`log:' + logline + "`\n\n\n"
+ end
end
- }
+ end
target_file = "#{params[:log]}.md"
File.open(target_file, 'w') { |f| f.write(result) }
diff --git a/logwriters/PDF.rb b/logwriters/PDF.rb
index 2c4c91e..cbaecf3 100644
--- a/logwriters/PDF.rb
+++ b/logwriters/PDF.rb
@@ -18,18 +18,19 @@
Dependency.new('xelatex', :binary, [:logwriter, 'pdf'], :essential, 'Compilation of PDF logs')
-ParameterManager.instance.addHook(:logformat) { |_, newValue|
- if newValue == :pdf
+ParameterManager.instance.addHook(:logformat) { |_, new_value|
+ if new_value == :pdf
DependencyManager.list(type: :all, source: [:logwriter, 'pdf'], relevance: :essential).each { |dep|
- unless dep.available?
- Output.instance.warn("#{dep.name} is not available to build PDF logs.", 'Falling back to Markdown log.')
- ParameterManager.instance[:logformat] = :md
- break
- end
+ next if dep.available?
+
+ Output.instance.warn("#{dep.name} is not available to build PDF logs.", 'Falling back to Markdown log.')
+ ParameterManager.instance[:logformat] = :md
+ break
}
end
}
+# TODO: Document
class PDF < LogWriter
def self.name
'PDF'
@@ -49,11 +50,11 @@ def self.write(log, level = :warning)
target_file = "#{params[:log]}.pdf"
latex_log = LogWriter[:latex].write(log, level)
- # TODO which engine to use?
+ # TODO: which engine to use?
xelatex = '"xelatex -file-line-error -interaction=nonstopmode \"#{latex_log}\""'
- IO::popen(eval(xelatex)) { |x| x.readlines }
- IO::popen(eval(xelatex)) { |x| x.readlines }
- # TODO parse log and rewrite a readable version?
+ IO.popen(eval(xelatex), &:readlines)
+ IO.popen(eval(xelatex), &:readlines)
+ # TODO: parse log and rewrite a readable version?
# This is just the default of XeLaTeX
xelatex_target = latex_log.sub(/\.tex$/, '.pdf')
@@ -69,7 +70,7 @@ def self.write(log, level = :warning)
Output.instance.error(*msg)
target_file = LogWriter[:md].write(log, level)
elsif xelatex_target != target_file
- FileUtils::cp(xelatex_target, target_file)
+ FileUtils.cp(xelatex_target, target_file)
end
target_file
diff --git a/logwriters/Raw.rb b/logwriters/Raw.rb
index b0949ed..45ae08b 100644
--- a/logwriters/Raw.rb
+++ b/logwriters/Raw.rb
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with ltx2any. If not, see .
+# TODO: document
class Raw < LogWriter
def self.name
'Original Log'
@@ -30,9 +31,10 @@ def self.to_sym
end
# Returns the name of the written file, or raises an exception
- def self.write(log, level: :warning)
+ # @override
+ def self.write(log, level = :warning)
target_file = "#{ParameterManager.instance[:log]}.full"
- File.open("#{target_file}", 'w') { |f| f.write(log.to_s) }
+ File.open(target_file, 'w') { |f| f.write(log.to_s) }
target_file
end
end
diff --git a/ltx2any.rb b/ltx2any.rb
index 0663e09..c362c2d 100644
--- a/ltx2any.rb
+++ b/ltx2any.rb
@@ -1,4 +1,4 @@
-# Copyright 2010-2016, Raphael Reitzig
+# Copyright 2010-2018, Raphael Reitzig
#
# Version 0.9 alpha
#
@@ -39,9 +39,7 @@
# Initialize CLI output wrapper
OUTPUT = Output.instance
-if CliHelp.instance.provideHelp(ARGV)
- Process.exit
-end
+Process.exit if CliHelp.instance.provideHelp(ARGV)
CLEAN = []
CLEANALL = []
@@ -59,32 +57,30 @@
# Make sure all essential dependencies of core and engine are satisfied
begin
missing = []
-
- (DependencyManager.list(source: :core, relevance: :essential) +
- DependencyManager.list(source: [:engine, PARAMS[:engine].to_s], relevance: :essential)).each { |d|
- missing.push(d) if !d.available?
- }
-
- if !missing.empty? # TODO enter into log?
+
+ (DependencyManager.list(source: :core, relevance: :essential) +
+ DependencyManager.list(source: [:engine, PARAMS[:engine].to_s], relevance: :essential)).each do |d|
+ missing.push(d) unless d.available?
+ end
+
+ unless missing.empty? # TODO: enter into log?
OUTPUT.separate.error('Missing dependencies', *missing)
Process.exit
end
end
-
+
# Check soft dependencies of core and engine; notify user if necessary
begin
missing = []
-
- (DependencyManager.list(source: :core, relevance: :recommended) +
- DependencyManager.list(source: [:engine, PARAMS[:engine].to_s], relevance: :recommended)).each { |d|
- missing.push(d) if !d.available?
- }
-
- if !missing.empty? # TODO enter into log?
- OUTPUT.separate.warn('Missing dependencies', *missing)
+
+ (DependencyManager.list(source: :core, relevance: :recommended) +
+ DependencyManager.list(source: [:engine, PARAMS[:engine].to_s], relevance: :recommended)).each do |d|
+ missing.push(d) unless d.available?
end
+
+ OUTPUT.separate.warn('Missing dependencies', *missing) unless missing.empty? # TODO: enter into log?
end
-
+
# Switch working directory to jobfile residence
Dir.chdir(PARAMS[:jobpath])
@@ -92,9 +88,9 @@
# Some files we don't want to listen to
toignore = [ "#{PARAMS[:tmpdir]}",
- "#{PARAMS[:user_jobname]}.#{Engine[PARAMS[:engine]].extension}",
- "#{PARAMS[:log]}",
- "#{PARAMS[:user_jobname]}.err"
+ "#{PARAMS[:user_jobname]}.#{Engine[PARAMS[:engine]].extension}",
+ "#{PARAMS[:log]}",
+ "#{PARAMS[:user_jobname]}.err"
] + PARAMS[:ignore].split(':')
begin
@@ -120,8 +116,8 @@
exceptions = ignore + ignore.map { |s| "./#{s}" } +
Dir['.*'] + Dir['./.*'] # drop hidden files, in p. . and ..
- define_singleton_method(:copy2tmp) { |files|
- files.each { |f|
+ define_singleton_method(:copy2tmp) do |files|
+ files.each do |f|
if File.symlink?(f)
# Avoid trouble with symlink loops
@@ -130,23 +126,23 @@
# remove the obsolete stuff.
# If there already is a symlink, delete because it might have been
# relinked.
- if File.exists?("#{PARAMS[:tmpdir]}/#{f}")
- FileUtils::rm("#{PARAMS[:tmpdir]}/#{f}")
+ if File.exist?("#{PARAMS[:tmpdir]}/#{f}")
+ FileUtils.rm("#{PARAMS[:tmpdir]}/#{f}")
end
# Create new symlink instead of copying
File.symlink("#{PARAMS[:jobpath]}/#{f}", "#{PARAMS[:tmpdir]}/#{f}")
elsif File.directory?(f)
- FileUtils::mkdir_p("#{PARAMS[:tmpdir]}/#{f}")
+ FileUtils.mkdir_p("#{PARAMS[:tmpdir]}/#{f}")
copy2tmp(Dir.entries(f)\
- .delete_if { |s| ['.', '..', ]\
- .include?(s) }.map { |s| "#{f}/#{s}" })
- # TODO Is this necessary? Why not just copy? (For now, safer and more adaptable.)
+ .delete_if do |s| ['.', '..', ]\
+ .include?(s) end.map { |s| "#{f}/#{s}" })
+ # TODO: Is this necessary? Why not just copy? (For now, safer and more adaptable.)
else
- FileUtils::cp(f,"#{PARAMS[:tmpdir]}/#{f}")
+ FileUtils.cp(f,"#{PARAMS[:tmpdir]}/#{f}")
end
- }
- }
+ end
+ end
# tmp dir may have been removed (either by DaemonPrompt or the outside)
if !File.exist?(PARAMS[:tmpdir])
@@ -165,7 +161,7 @@
# Delete former results in order not to pretend success
if File.exist?("#{PARAMS[:jobname]}.#{engine.extension}")
- FileUtils::rm("#{PARAMS[:jobname]}.#{engine.extension}")
+ FileUtils.rm("#{PARAMS[:jobname]}.#{engine.extension}")
end
# Read hashes
@@ -181,7 +177,7 @@
# Run engine
OUTPUT.start("#{engine.name}(#{run}) running")
result = engine.exec
- OUTPUT.stop(if result[:success] then :success else :error end)
+ OUTPUT.stop(result[:success] ? :success : :error)
break unless File.exist?("#{PARAMS[:jobname]}.#{engine.extension}")
@@ -200,53 +196,46 @@
Extension.run_all(:after, OUTPUT, log)
# Give error/warning counts to user
- errorS = if log.count(:error) != 1 then
- 's'
- else
- ''
- end
- warningS = if log.count(:warning) != 1 then
- 's'
- else
- ''
- end
+ errorS = log.count(:error) != 1 ? 's' : ''
+ warningS = log.count(:warning) != 1 ? 's' : ''
OUTPUT.msg("There were #{log.count(:error)} error#{errorS} " +
"and #{log.count(:warning)} warning#{warningS}.")
# Pick up output if present
if File.exist?("#{PARAMS[:jobname]}.#{engine.extension}")
- FileUtils::cp("#{PARAMS[:jobname]}.#{engine.extension}", "#{PARAMS[:jobpath]}/#{PARAMS[:user_jobname]}.#{engine.extension}")
+ FileUtils.cp("#{PARAMS[:jobname]}.#{engine.extension}",
+ "#{PARAMS[:jobpath]}/#{PARAMS[:user_jobname]}.#{engine.extension}")
OUTPUT.msg("Output generated at #{PARAMS[:user_jobname]}.#{engine.extension}")
else
OUTPUT.msg('No output generated, probably due to fatal errors.')
end
# Write log
- if !log.empty?
+ unless log.empty?
OUTPUT.start('Assembling log files')
# Manage messages from extensions
- Extension.list.each { |ext|
+ Extension.list.each do |ext|
if !log.has_messages?(ext.name) \
- && File.exist?(".#{NAME}_extensionmsg_#{ext.name}")
+ && File.exist?(".#{NAME}_extensionmsg_#{ext.name}")
# Extension did not run but has run before; load messages from then!
- old = File.open(".#{NAME}_extensionmsg_#{ext.name}", 'r') { |f|
+ old = File.open(".#{NAME}_extensionmsg_#{ext.name}", 'r') do |f|
f.readlines.join
- }
+ end
old = YAML.load(old)
log.add_messages(ext.name, old[0], old[1], old[2])
elsif log.has_messages?(ext.name)
# Write new messages
- File.open(".#{NAME}_extensionmsg_#{ext.name}", 'w') { |f|
+ File.open(".#{NAME}_extensionmsg_#{ext.name}", 'w') do |f|
f.write(YAML.dump(log.messages(ext.name)))
- }
+ end
end
- }
+ end
logfile = LogWriter[:raw].write(log)
logfile = LogWriter[PARAMS[:logformat]].write(log, PARAMS[:loglevel])
- FileUtils::cp(logfile, "#{PARAMS[:jobpath]}/#{logfile}")
+ FileUtils.cp(logfile, "#{PARAMS[:jobpath]}/#{logfile}")
OUTPUT.stop(:success)
OUTPUT.msg("Log file generated at #{logfile}")
CLEANALL.push("#{PARAMS[:jobpath]}/#{logfile}")
@@ -255,7 +244,7 @@
runtime = Time.now - start_time
# Don't show runtimes of less than 5s (arbitrary)
if runtime / 60 >= 1 || runtime % 60 >= 5
- OUTPUT.msg('Took ' + sprintf('%d min ', runtime / 60) + ' ' + sprintf('%d sec', runtime % 60))
+ OUTPUT.msg("Took #{format('%d min ', runtime / 60)} #{format('%d sec', runtime % 60)}")
end
end
rescue Interrupt, SystemExit # User cancelled current run
@@ -274,12 +263,12 @@
end while ( PARAMS[:daemon] )
rescue Interrupt, SystemExit
OUTPUT.separate.msg('Shutdown')
-rescue Exception => e
- if PARAMS[:user_jobname] != nil
+rescue StandardError => e
+ if !PARAMS[:user_jobname].nil?
OUTPUT.separate.error(e.message, "See #{PARAMS[:user_jobname]}.err for details.")
- File.open("#{PARAMS[:jobpath]}/#{PARAMS[:user_jobname]}.err", 'w') { |file|
+ File.open("#{PARAMS[:jobpath]}/#{PARAMS[:user_jobname]}.err", 'w') do |file|
file.write("#{e.inspect}\n\n#{e.backtrace.join("\n")}")
- }
+ end
CLEANALL.push("#{PARAMS[:jobpath]}/#{PARAMS[:user_jobname]}.err")
else
# This is reached due to programming errors or if ltx2any quits early,
@@ -300,5 +289,5 @@
# Stop file listeners
FileListener.instance.stop if PARAMS[:daemon] && FileListener.instance.runs?
# Remove temps if so desired.
-CLEAN.each { |f| FileUtils::rm_rf(f) } if PARAMS[:clean]
-CLEANALL.each { |f| FileUtils::rm_rf(f) } if PARAMS[:cleanall]
+CLEAN.each { |f| FileUtils.rm_rf(f) } if PARAMS[:clean]
+CLEANALL.each { |f| FileUtils.rm_rf(f) } if PARAMS[:cleanall]
diff --git a/parameters.rb b/parameters.rb
index f377c64..e602da2 100644
--- a/parameters.rb
+++ b/parameters.rb
@@ -31,6 +31,6 @@
ParameterManager.instance.addParameter(p)
}
-ParameterManager.instance.addHook(:cleanall) { |k,v|
+ParameterManager.instance.addHook(:cleanall) { |_, v|
ParameterManager.instance[:clean] = true if v
}