gly 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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}
|