gly 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/gly.rb +4 -1
- data/lib/gly/cli.rb +71 -8
- data/lib/gly/document.rb +20 -8
- data/lib/gly/document_gabc_convertor.rb +10 -2
- data/lib/gly/exceptions.rb +16 -0
- data/lib/gly/exceptions.rb~ +3 -0
- data/lib/gly/gabc_convertor.rb +29 -11
- data/lib/gly/gly_convertor.rb +52 -0
- data/lib/gly/gly_convertor.rb~ +11 -0
- data/lib/gly/glyfy_cli.rb~ +92 -0
- data/lib/gly/headers.rb +3 -1
- data/lib/gly/lister.rb +41 -6
- data/lib/gly/lyrics.rb +7 -7
- data/lib/gly/markup.rb +15 -0
- data/lib/gly/markup.rb~ +11 -0
- data/lib/gly/parser.rb +65 -23
- data/lib/gly/preview_builder.rb +13 -4
- data/lib/gly/preview_generator.rb +44 -23
- data/lib/gly/{parsed_score.rb → score.rb} +1 -1
- data/lib/gly/templates/lualatex_document.tex +11 -11
- data/tests/examples.rb +27 -7
- data/tests/examples/gly/expected/block_lyrics.gabc +1 -1
- data/tests/examples/gly/expected/block_lyrics.gabc~ +2 -0
- data/tests/examples/gly/expected/block_music.gabc +2 -0
- data/tests/examples/gly/expected/block_music.gabc~ +4 -0
- data/tests/examples/gly/expected/blocks.gabc +3 -0
- data/tests/examples/gly/expected/differentia_final.gabc +2 -0
- data/tests/examples/gly/expected/empty_lyric_syllable.gabc +2 -0
- data/tests/examples/gly/expected/empty_lyric_syllable.gabc~ +1 -0
- data/tests/examples/gly/expected/explicit_lyrics.gabc +1 -1
- data/tests/examples/gly/expected/header_colon.gabc +3 -0
- data/tests/examples/gly/expected/header_colon.gabc~ +3 -0
- data/tests/examples/gly/expected/header_empty.gabc +2 -0
- data/tests/examples/gly/expected/header_empty.gabc~ +2 -0
- data/tests/examples/gly/expected/nabc.gabc +3 -0
- data/tests/examples/gly/expected/no_clef.gabc +2 -0
- data/tests/examples/gly/expected/no_clef.gabc~ +1 -0
- data/tests/examples/gly/expected/real1.gabc~ +15 -0
- data/tests/examples/gly/expected/unsingables.gabc +2 -0
- data/tests/examples/gly/expected/unsingables.gabc~ +2 -0
- data/tests/examples/gly/given/block_lyrics.gly~ +3 -0
- data/tests/examples/gly/given/block_music.gly +4 -0
- data/tests/examples/gly/given/blocks.gly +6 -0
- data/tests/examples/gly/given/differentia_final.gly +5 -0
- data/tests/examples/gly/given/differentia_final.gly~ +2 -0
- data/tests/examples/gly/given/empty_lyric_syllable.gly +2 -0
- data/tests/examples/gly/given/empty_lyric_syllable.gly~ +2 -0
- data/tests/examples/gly/given/header_colon.gly +3 -0
- data/tests/examples/gly/given/header_colon.gly~ +4 -0
- data/tests/examples/gly/given/header_empty.gly +1 -0
- data/tests/examples/gly/given/header_empty.gly~ +2 -0
- data/tests/examples/gly/given/nabc.gly +3 -0
- data/tests/examples/gly/given/nabc.gly~ +2 -0
- data/tests/examples/gly/given/no_clef.gly +2 -0
- data/tests/examples/gly/given/no_clef.gly~ +1 -0
- data/tests/examples/gly/given/real1.gly~ +18 -0
- data/tests/examples/gly/given/unsingables.gly +2 -0
- data/tests/examples/gly/given/unsingables.gly~ +2 -0
- data/tests/examples/glyfy/expected/header.gly +3 -0
- data/tests/examples/glyfy/expected/header.gly~ +2 -0
- data/tests/examples/glyfy/expected/simple.gly +3 -0
- data/tests/examples/glyfy/expected/simple.gly~ +2 -0
- data/tests/examples/glyfy/given/header.gabc +3 -0
- data/tests/examples/glyfy/given/simple.gabc +2 -0
- data/tests/examples/parser/markup_after_header.gly +4 -0
- data/tests/examples/parser/markups.gly +10 -0
- data/tests/examples/parser/markups.gly~ +5 -0
- data/tests/examples/parser/score_markup_order.gly +19 -0
- data/tests/examples/parser/score_markup_order.gly~ +13 -0
- data/tests/parser.rb +51 -0
- data/tests/parser.rb~ +12 -0
- data/tests/run.rb +1 -0
- data/tests/test_helper.rb +19 -0
- metadata +59 -10
- data/bin/glyfy +0 -4
- data/tests/examples.rb~ +0 -9
- data/tests/programmed_examples.rb~ +0 -14
- data/tests/run.rb~ +0 -3
- data/tests/string_helpers_test.rb~ +0 -19
data/lib/gly/headers.rb
CHANGED
@@ -32,6 +32,7 @@ initial-style
|
|
32
32
|
centering-scheme
|
33
33
|
user-notes
|
34
34
|
annotation
|
35
|
+
nabc-lines
|
35
36
|
)
|
36
37
|
|
37
38
|
def initialize
|
@@ -47,7 +48,8 @@ annotation
|
|
47
48
|
@pairs << [key, value]
|
48
49
|
end
|
49
50
|
|
50
|
-
|
51
|
+
def_delegators :@headers, :[], :size, :keys, :values,
|
52
|
+
:has_key?, :includes?, :include?
|
51
53
|
def_delegator :@pairs, :empty?
|
52
54
|
|
53
55
|
# some header fields may appear more than once;
|
data/lib/gly/lister.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module Gly
|
2
2
|
class Lister
|
3
|
-
def initialize(files)
|
3
|
+
def initialize(files, format=nil)
|
4
4
|
@files = files
|
5
5
|
@error = false
|
6
|
+
@format = find_formatter(format || :grep)
|
6
7
|
end
|
7
8
|
|
8
9
|
def list(io, errio)
|
@@ -15,12 +16,9 @@ module Gly
|
|
15
16
|
next
|
16
17
|
end
|
17
18
|
|
18
|
-
io.puts
|
19
|
-
io.puts "== #{f}"
|
20
|
-
|
19
|
+
io.puts @format.file(f)
|
21
20
|
document.scores.each do |s|
|
22
|
-
|
23
|
-
io.puts l unless l.empty?
|
21
|
+
io.puts @format.score(s)
|
24
22
|
end
|
25
23
|
end
|
26
24
|
end
|
@@ -28,5 +26,42 @@ module Gly
|
|
28
26
|
def error?
|
29
27
|
@error
|
30
28
|
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def find_formatter(name)
|
33
|
+
formatter_name = name.to_s.capitalize + 'Format'
|
34
|
+
begin
|
35
|
+
klass = self.class.const_get(formatter_name)
|
36
|
+
rescue NameError
|
37
|
+
raise Gly::Exception.new("Invalid list format '#{name}'")
|
38
|
+
end
|
39
|
+
|
40
|
+
klass.new
|
41
|
+
end
|
42
|
+
|
43
|
+
# simple, sort of beautiful, easy to read for a human
|
44
|
+
class OverviewFormat
|
45
|
+
def file(f)
|
46
|
+
"\n== #{f}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def score(s)
|
50
|
+
l = s.lyrics.readable
|
51
|
+
l unless l.empty?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# less beauty, more repetition, for easier grepping
|
56
|
+
class GrepFormat
|
57
|
+
def file(f)
|
58
|
+
@file = f
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def score(s)
|
63
|
+
"#{@file}##{s.headers['id']} #{s.lyrics.readable}"
|
64
|
+
end
|
65
|
+
end
|
31
66
|
end
|
32
67
|
end
|
data/lib/gly/lyrics.rb
CHANGED
@@ -11,13 +11,13 @@ module Gly
|
|
11
11
|
def each_syllable
|
12
12
|
return enum_for(:each_syllable) unless block_given?
|
13
13
|
|
14
|
-
@words.
|
15
|
-
w.each_syllable.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
@words.each_with_index do |w,wi|
|
15
|
+
w.each_syllable.each do |s|
|
16
|
+
yield s
|
17
|
+
end
|
18
|
+
|
19
|
+
if (wi + 1) < @words.size
|
20
|
+
yield ' '
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/lib/gly/markup.rb
ADDED
data/lib/gly/markup.rb~
ADDED
data/lib/gly/parser.rb
CHANGED
@@ -5,17 +5,12 @@ module Gly
|
|
5
5
|
class Parser
|
6
6
|
def initialize(syllable_separator=nil)
|
7
7
|
@syllable_separator = syllable_separator || '--'
|
8
|
+
@current_block = :score
|
8
9
|
end
|
9
10
|
|
10
11
|
def parse(source)
|
11
12
|
if source.is_a? String
|
12
|
-
|
13
|
-
parse_fname source
|
14
|
-
elsif source == '-'
|
15
|
-
parse_io STDIN
|
16
|
-
else
|
17
|
-
parse_str source
|
18
|
-
end
|
13
|
+
parse_fname source
|
19
14
|
else
|
20
15
|
parse_io source
|
21
16
|
end
|
@@ -33,7 +28,7 @@ module Gly
|
|
33
28
|
|
34
29
|
def parse_io(io)
|
35
30
|
@doc = Document.new
|
36
|
-
@score =
|
31
|
+
@score = Score.new
|
37
32
|
|
38
33
|
if io.respond_to? :path
|
39
34
|
@doc.path = io.path
|
@@ -42,24 +37,40 @@ module Gly
|
|
42
37
|
io.each do |line|
|
43
38
|
line = strip_comments(line)
|
44
39
|
|
45
|
-
if empty?
|
40
|
+
if empty?(line) && @current_block != :markup
|
46
41
|
next
|
42
|
+
# keywords specifying line or block type
|
47
43
|
elsif new_score? line
|
48
44
|
push_score
|
49
|
-
@score =
|
45
|
+
@score = Score.new
|
46
|
+
@current_block = :score
|
50
47
|
elsif header_start? line
|
51
48
|
push_score
|
52
49
|
@score = @doc.header
|
53
|
-
|
54
|
-
|
50
|
+
@current_block = :header
|
51
|
+
elsif markup_start? line
|
52
|
+
push_score
|
53
|
+
@doc.content << Markup.new
|
54
|
+
@current_block = :markup
|
55
|
+
elsif block_start? line
|
56
|
+
@current_block = line.match(/\w+/)[0].to_sym
|
55
57
|
elsif explicit_lyrics? line
|
56
58
|
parse_lyrics line
|
57
59
|
elsif explicit_music? line
|
58
60
|
parse_music line
|
61
|
+
elsif explicit_markup? line
|
62
|
+
push_score
|
63
|
+
parse_markup line
|
64
|
+
# line in a typed block
|
65
|
+
elsif @current_block != :score
|
66
|
+
parse_default line
|
67
|
+
# content type autodetection
|
68
|
+
elsif header_line? line
|
69
|
+
parse_header line
|
59
70
|
elsif lyrics_line? line
|
60
71
|
parse_lyrics line
|
61
72
|
else
|
62
|
-
|
73
|
+
parse_default line
|
63
74
|
end
|
64
75
|
end
|
65
76
|
|
@@ -75,11 +86,19 @@ module Gly
|
|
75
86
|
end
|
76
87
|
|
77
88
|
def new_score?(str)
|
78
|
-
str =~ /\A\s*\\score/
|
89
|
+
str =~ /\A\s*\\(score)\s*\Z/
|
79
90
|
end
|
80
91
|
|
81
92
|
def header_start?(str)
|
82
|
-
str =~ /\A\s*\\header/
|
93
|
+
str =~ /\A\s*\\(header)\s*\Z/
|
94
|
+
end
|
95
|
+
|
96
|
+
def markup_start?(str)
|
97
|
+
str =~ /\A\s*\\(markup)\s*\Z/
|
98
|
+
end
|
99
|
+
|
100
|
+
def block_start?(str)
|
101
|
+
str =~ /\A\s*\\(lyrics|music)\s*\Z/
|
83
102
|
end
|
84
103
|
|
85
104
|
def strip_comments(str)
|
@@ -87,7 +106,7 @@ module Gly
|
|
87
106
|
end
|
88
107
|
|
89
108
|
def header_line?(str)
|
90
|
-
|
109
|
+
@current_block == :score && @score.lyrics.empty? && @score.music.empty? && str =~ /\A[\w_-]+:/
|
91
110
|
end
|
92
111
|
|
93
112
|
EXPLICIT_LYRICS_RE = /\A\\l(yrics)?\s+/
|
@@ -102,12 +121,14 @@ module Gly
|
|
102
121
|
str =~ EXPLICIT_MUSIC_RE
|
103
122
|
end
|
104
123
|
|
105
|
-
|
106
|
-
|
124
|
+
EXPLICIT_MARKUP_RE = /\A\\markup\s*/
|
125
|
+
|
126
|
+
def explicit_markup?(str)
|
127
|
+
str =~ EXPLICIT_MARKUP_RE
|
107
128
|
end
|
108
129
|
|
109
|
-
def
|
110
|
-
|
130
|
+
def lyrics_line?(str)
|
131
|
+
!contains_square_brackets?(str) && (str.include?(@syllable_separator) || contains_unmusical_letters?(str))
|
111
132
|
end
|
112
133
|
|
113
134
|
def contains_unmusical_letters?(str)
|
@@ -120,7 +141,7 @@ module Gly
|
|
120
141
|
end
|
121
142
|
|
122
143
|
def parse_header(str)
|
123
|
-
hid, hvalue = str.split(':').collect(&:strip)
|
144
|
+
hid, hvalue = str.split(':', 2).collect(&:strip)
|
124
145
|
@score.headers[hid] = hvalue
|
125
146
|
end
|
126
147
|
|
@@ -144,10 +165,31 @@ module Gly
|
|
144
165
|
end
|
145
166
|
end
|
146
167
|
|
168
|
+
def parse_markup(line)
|
169
|
+
if line =~ EXPLICIT_MARKUP_RE
|
170
|
+
@doc << Markup.new(line.sub(EXPLICIT_MARKUP_RE, ''))
|
171
|
+
else
|
172
|
+
@doc.content.last << line
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def parse_default(line)
|
177
|
+
if @current_block == :score
|
178
|
+
return parse_music line
|
179
|
+
end
|
180
|
+
|
181
|
+
send "parse_#{@current_block}", line
|
182
|
+
end
|
183
|
+
|
147
184
|
def push_score
|
148
|
-
if @score.is_a?(
|
149
|
-
|
185
|
+
if @score.is_a?(Score) && !@score.empty?
|
186
|
+
begin
|
187
|
+
@doc << @score
|
188
|
+
rescue ArgumentError => ex
|
189
|
+
raise ParseError.wrap ex
|
190
|
+
end
|
150
191
|
end
|
192
|
+
@score = nil
|
151
193
|
end
|
152
194
|
end
|
153
195
|
end
|
data/lib/gly/preview_builder.rb
CHANGED
@@ -15,10 +15,11 @@ module Gly
|
|
15
15
|
|
16
16
|
def build
|
17
17
|
@gabcs.each do |g|
|
18
|
-
|
18
|
+
outfile = g.sub /(\.gabc)?$/i, '.gtex'
|
19
|
+
benevolent_exec('gregorio', '-o', outfile, g)
|
19
20
|
end
|
20
21
|
|
21
|
-
exec 'lualatex', @main_tex
|
22
|
+
exec 'lualatex', '--interaction=nonstopmode', @main_tex
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
@@ -28,11 +29,19 @@ module Gly
|
|
28
29
|
unless ok
|
29
30
|
case $?.exitstatus
|
30
31
|
when 127
|
31
|
-
|
32
|
+
raise Gly::Exception.new "'#{progname}' is required for this gly command to work, but it was not found. Please, ensure that '#{progname}' is installed in one of the directories listed in your PATH and try again."
|
32
33
|
else
|
33
|
-
|
34
|
+
raise Gly::Exception.new "'#{progname}' exited with exit code #{$?.to_i}."
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
38
|
+
|
39
|
+
def benevolent_exec(progname, *args)
|
40
|
+
begin
|
41
|
+
exec progname, *args
|
42
|
+
rescue Gly::Exception => err
|
43
|
+
STDERR.puts err.message + " Let's continue and see what happens ..."
|
44
|
+
end
|
45
|
+
end
|
37
46
|
end
|
38
47
|
end
|
@@ -27,37 +27,25 @@ module Gly
|
|
27
27
|
fw.puts header_table document.header
|
28
28
|
end
|
29
29
|
|
30
|
-
convertor.each_score_with_gabcname
|
31
|
-
|
32
|
-
|
33
|
-
if
|
34
|
-
fw.puts
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
piece_title = %w(book manuscript arranger author).collect do |m| score.headers[m]
|
39
|
-
end.delete_if(&:nil?).join ', '
|
40
|
-
fw.puts "\\commentary{\\footnotesize{#{piece_title}}}\n" unless piece_title.empty?
|
41
|
-
|
42
|
-
annotations = score.headers.each_value('annotation')
|
43
|
-
begin
|
44
|
-
fw.puts "\\setfirstannotation{#{annotations.next}}"
|
45
|
-
fw.puts "\\setsecondannotation{#{annotations.next}}"
|
46
|
-
rescue StopIteration
|
47
|
-
# ok, no more annotations
|
30
|
+
scores_with_names = convertor.each_score_with_gabcname
|
31
|
+
|
32
|
+
document.content.each do |c|
|
33
|
+
if c.is_a? Markup
|
34
|
+
fw.puts render_markup(c)
|
35
|
+
else
|
36
|
+
score, gabc_fname, gtex_fname = scores_with_names.next
|
37
|
+
fw.puts render_score(score, gabc_fname, gtex_fname)
|
48
38
|
end
|
49
|
-
|
50
|
-
fw.puts "\\includescore{#{gtex_fname}}\n\\vspace{1cm}"
|
51
39
|
end
|
52
40
|
|
53
41
|
if @options['no_document']
|
54
42
|
tex = doc_body.string
|
55
43
|
else
|
56
44
|
replacements = {
|
57
|
-
glyvars
|
58
|
-
body
|
45
|
+
'glyvars' => header_variables(document.header),
|
46
|
+
'body' => doc_body.string
|
59
47
|
}
|
60
|
-
tex = @template
|
48
|
+
tex = @template.gsub(/\{\{(\w+)\}\}/) {|m| replacements[m[2..-3]] }
|
61
49
|
end
|
62
50
|
|
63
51
|
with_preview_io(document.path) do |fw|
|
@@ -74,6 +62,39 @@ module Gly
|
|
74
62
|
|
75
63
|
private
|
76
64
|
|
65
|
+
def render_markup(markup)
|
66
|
+
markup.text
|
67
|
+
end
|
68
|
+
|
69
|
+
def render_score(score, gabc_fname, gtex_fname)
|
70
|
+
r = StringIO.new
|
71
|
+
|
72
|
+
@builder.add_gabc gabc_fname
|
73
|
+
|
74
|
+
if @options[:full_headers]
|
75
|
+
r.puts header_table score.headers
|
76
|
+
end
|
77
|
+
|
78
|
+
piece_title = %w(id book manuscript arranger author).collect do |m|
|
79
|
+
val = score.headers[m]
|
80
|
+
val = "\\texttt{\\##{val}}" if val && m == 'id'
|
81
|
+
val
|
82
|
+
end.delete_if(&:nil?).join ', '
|
83
|
+
r.puts "\\commentary{\\footnotesize{#{piece_title}}}\n" unless piece_title.empty?
|
84
|
+
|
85
|
+
annotations = score.headers.each_value('annotation')
|
86
|
+
begin
|
87
|
+
r.puts "\\setfirstannotation{#{annotations.next}}"
|
88
|
+
r.puts "\\setsecondannotation{#{annotations.next}}"
|
89
|
+
rescue StopIteration
|
90
|
+
# ok, no more annotations
|
91
|
+
end
|
92
|
+
|
93
|
+
r.puts "\\includescore{#{gtex_fname}}\n\\vspace{1cm}"
|
94
|
+
|
95
|
+
r.string
|
96
|
+
end
|
97
|
+
|
77
98
|
def with_preview_io(src_name)
|
78
99
|
if @preview_dest
|
79
100
|
yield @preview_dest
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
% LuaLaTeX
|
2
2
|
|
3
3
|
\documentclass[a4paper, 12pt]{article}
|
4
4
|
\usepackage[latin]{babel}
|
@@ -8,19 +8,19 @@
|
|
8
8
|
\usepackage[hidelinks]{hyperref}
|
9
9
|
\usepackage{etoolbox}
|
10
10
|
|
11
|
-
|
11
|
+
% for gregorio
|
12
12
|
\usepackage{luatextra}
|
13
13
|
\usepackage{graphicx}
|
14
14
|
\usepackage{gregoriotex}
|
15
15
|
|
16
|
-
|
16
|
+
{{glyvars}}
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
% header fields like title, transcription-date etc.
|
19
|
+
% are translated by 'gly preview' to latex commands like
|
20
|
+
% \glyTitle \glyTranscriptionDate etc.
|
21
|
+
% (All of your document header fields are handled this way.)
|
22
|
+
% These commands can be used for various purposes,
|
23
|
+
% but we will use them only to assemble document title:
|
24
24
|
\newcommand{\glyPreviewTitle}{
|
25
25
|
\begin{center}
|
26
26
|
\ifdef{\glyTitle}{{\huge \glyTitle}}{}
|
@@ -43,9 +43,9 @@
|
|
43
43
|
|
44
44
|
\glyPreviewTitle
|
45
45
|
|
46
|
-
|
46
|
+
{{body}}
|
47
47
|
|
48
|
-
|
48
|
+
% tagline
|
49
49
|
\vfill
|
50
50
|
\glyPreviewTagline
|
51
51
|
\end{document}
|