lydown 0.10.0 → 0.12.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +452 -206
- data/bin/lydown +5 -1
- data/lib/lydown/cache.rb +1 -2
- data/lib/lydown/cli.rb +2 -0
- data/lib/lydown/cli/commands.rb +25 -7
- data/lib/lydown/cli/compiler.rb +32 -9
- data/lib/lydown/cli/diff.rb +1 -1
- data/lib/lydown/cli/installer.rb +175 -0
- data/lib/lydown/cli/proofing.rb +44 -41
- data/lib/lydown/cli/repl.rb +232 -0
- data/lib/lydown/cli/support.rb +33 -0
- data/lib/lydown/core_ext.rb +8 -0
- data/lib/lydown/lilypond.rb +1 -0
- data/lib/lydown/ly_lib/lib.ly +1 -1
- data/lib/lydown/parsing.rb +1 -1
- data/lib/lydown/rendering.rb +34 -0
- data/lib/lydown/rendering/base.rb +1 -1
- data/lib/lydown/rendering/movement.rb +0 -11
- data/lib/lydown/rendering/music.rb +2 -0
- data/lib/lydown/rendering/notes.rb +2 -0
- data/lib/lydown/rendering/settings.rb +34 -8
- data/lib/lydown/rendering/source_ref.rb +3 -2
- data/lib/lydown/templates/lilypond_doc.erb +5 -0
- data/lib/lydown/templates/movement.erb +1 -1
- data/lib/lydown/templates/variables.erb +5 -0
- data/lib/lydown/version.rb +1 -1
- data/lib/lydown/work.rb +9 -4
- data/lib/lydown/work_context.rb +6 -8
- metadata +7 -33
@@ -0,0 +1,232 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'readline'
|
5
|
+
|
6
|
+
module Lydown::CLI::REPL
|
7
|
+
class << self
|
8
|
+
def run
|
9
|
+
require 'lydown/version'
|
10
|
+
puts "Lydown version #{Lydown::VERSION}"
|
11
|
+
|
12
|
+
lilypond_version = Lydown::CLI::Support.detect_lilypond_version(true)
|
13
|
+
if lilypond_version
|
14
|
+
puts "Lilypond version #{lilypond_version}"
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'lydown'
|
18
|
+
|
19
|
+
orig_dir = FileUtils.pwd
|
20
|
+
|
21
|
+
trap("INT") do
|
22
|
+
Lydown::CLI::Proofing.unwatch # stop file watcher for proofing
|
23
|
+
save_history
|
24
|
+
puts
|
25
|
+
FileUtils.cd(orig_dir)
|
26
|
+
exit(0)
|
27
|
+
end
|
28
|
+
|
29
|
+
load_history
|
30
|
+
|
31
|
+
update_proofing_watcher(orig_dir)
|
32
|
+
|
33
|
+
while line = Readline.readline(prompt, true).chomp
|
34
|
+
maintain_clean_history(line)
|
35
|
+
process(line)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def maintain_clean_history(line)
|
40
|
+
if line =~ /^\s*$/ or Readline::HISTORY.to_a[-2] == line
|
41
|
+
Readline::HISTORY.pop
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
HISTORY_FN = File.expand_path('~/.lydown_history')
|
46
|
+
|
47
|
+
def load_history
|
48
|
+
IO.read(HISTORY_FN).lines.each do |l|
|
49
|
+
Readline::HISTORY << l.chomp
|
50
|
+
end
|
51
|
+
rescue
|
52
|
+
end
|
53
|
+
|
54
|
+
def save_history
|
55
|
+
File.open(HISTORY_FN, 'w+') do |f|
|
56
|
+
Readline::HISTORY.to_a.each do |l|
|
57
|
+
f.puts l
|
58
|
+
end
|
59
|
+
end
|
60
|
+
rescue => e
|
61
|
+
puts "Failed to save history: #{e.message}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def prompt
|
65
|
+
"#{File.basename(FileUtils.pwd)}♫ "
|
66
|
+
end
|
67
|
+
|
68
|
+
def process(line)
|
69
|
+
if line =~ /^([a-z]+)\s?(.*)$/
|
70
|
+
cmd = $1
|
71
|
+
args = $2
|
72
|
+
cmd_method = :"handle_#{cmd}"
|
73
|
+
if respond_to?(cmd_method)
|
74
|
+
send(cmd_method, args)
|
75
|
+
else
|
76
|
+
handle_default(line)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
HELP = <<EOF
|
82
|
+
|
83
|
+
pwd Print working directory
|
84
|
+
cd <path> Change working directory
|
85
|
+
ls [<path>] Show lydown files in working directory
|
86
|
+
|
87
|
+
cat <file> Show content of file
|
88
|
+
cat <file>:<line> Show content of file at specified line, or range of
|
89
|
+
lines, e.g. cat flute1:13, or cat flute1:25-27
|
90
|
+
|
91
|
+
edit <file> Edit file using vim
|
92
|
+
edit <file>:<line> Edit file using vim, place cursor at specified line
|
93
|
+
|
94
|
+
score Compile & open score
|
95
|
+
midi Compile & open MIDI file
|
96
|
+
<part> Compile & open part
|
97
|
+
|
98
|
+
To exit press ctrl-C
|
99
|
+
EOF
|
100
|
+
|
101
|
+
def handle_help(args)
|
102
|
+
puts HELP; puts
|
103
|
+
end
|
104
|
+
|
105
|
+
def handle_pwd(args)
|
106
|
+
puts FileUtils.pwd
|
107
|
+
end
|
108
|
+
|
109
|
+
def handle_cd(args)
|
110
|
+
path = File.expand_path(args)
|
111
|
+
FileUtils.cd(path)
|
112
|
+
update_proofing_watcher(path)
|
113
|
+
rescue => e
|
114
|
+
puts e.message
|
115
|
+
end
|
116
|
+
|
117
|
+
def handle_git(args)
|
118
|
+
puts `git #{args}`
|
119
|
+
end
|
120
|
+
|
121
|
+
def handle_edit(args)
|
122
|
+
if args =~ /(.*):(\d+)$/
|
123
|
+
fn = $1; line = $2
|
124
|
+
else
|
125
|
+
fn = args; line = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
return unless fn = validate_filename(fn)
|
129
|
+
|
130
|
+
if line
|
131
|
+
system "vim +#{line} #{fn} -c 'normal zz'"
|
132
|
+
else
|
133
|
+
system "vim #{fn}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def validate_filename(fn)
|
138
|
+
unless File.file?(fn)
|
139
|
+
fn += '.ld'
|
140
|
+
unless File.file?(fn)
|
141
|
+
puts "File not found"
|
142
|
+
return nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
fn
|
146
|
+
end
|
147
|
+
|
148
|
+
def update_proofing_watcher(path)
|
149
|
+
opts = {path: path, proof_mode: true, open_target: true,
|
150
|
+
no_progress_bar: true, silent: true, repl: true}
|
151
|
+
Lydown::CLI::Support.detect_work_directory(opts)
|
152
|
+
Lydown::CLI::Support.detect_filename(opts)
|
153
|
+
|
154
|
+
Lydown::CLI::Proofing.watch(opts)
|
155
|
+
end
|
156
|
+
|
157
|
+
def handle_ls(args)
|
158
|
+
system 'ls'
|
159
|
+
end
|
160
|
+
|
161
|
+
def handle_cat(args)
|
162
|
+
if args =~ /^([^\s:]+):?(\d+)?\-?(\d+)?$/
|
163
|
+
fn = $1
|
164
|
+
line_start = $2
|
165
|
+
line_end = $3
|
166
|
+
|
167
|
+
if line_start
|
168
|
+
if line_end
|
169
|
+
line_range = (line_start.to_i - 1)..(line_end.to_i - 1)
|
170
|
+
else
|
171
|
+
line_range = (line_start.to_i - 1)..(line_start.to_i - 1)
|
172
|
+
end
|
173
|
+
else
|
174
|
+
line_range = nil
|
175
|
+
end
|
176
|
+
|
177
|
+
return unless fn = validate_filename(fn)
|
178
|
+
|
179
|
+
content = IO.read(fn)
|
180
|
+
unless line_range
|
181
|
+
puts content
|
182
|
+
else
|
183
|
+
puts content.lines[line_range].join
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def handle_score(args)
|
189
|
+
opts = {path: '.', open_target: true, mode: :score, repl: true}
|
190
|
+
$stderr.puts "Processing score..."
|
191
|
+
compile(opts)
|
192
|
+
end
|
193
|
+
|
194
|
+
def handle_midi(args)
|
195
|
+
opts = {path: '.', open_target: true, mode: :score, format: :midi,
|
196
|
+
repl: true
|
197
|
+
}
|
198
|
+
$stderr.puts "Processing MIDI..."
|
199
|
+
compile(opts)
|
200
|
+
end
|
201
|
+
|
202
|
+
def handle_default(args)
|
203
|
+
opts = {}
|
204
|
+
|
205
|
+
args.split(',').map(&:strip).each do |a|
|
206
|
+
if File.file?(a) || File.file?("#{a}.ld")
|
207
|
+
opts = {path: '.', parts: [a], mode: :part, open_target: true, repl: true}
|
208
|
+
elsif File.directory?(a)
|
209
|
+
opts = {path: '.', movements: [a], mode: :score, open_target: true, repl: true}
|
210
|
+
else
|
211
|
+
raise LydownError, "Invalid path specified - #{a}"
|
212
|
+
end
|
213
|
+
$stderr.puts "Processing #{a}..."
|
214
|
+
compile(opts)
|
215
|
+
end
|
216
|
+
rescue => e
|
217
|
+
# do nothing
|
218
|
+
end
|
219
|
+
|
220
|
+
def compile(opts)
|
221
|
+
Lydown::CLI::Support.detect_work_directory(opts)
|
222
|
+
Lydown::CLI::Support.detect_filename(opts)
|
223
|
+
|
224
|
+
prev_handler = trap("INT") do
|
225
|
+
puts
|
226
|
+
end
|
227
|
+
Lydown::CLI::Compiler.process(opts)
|
228
|
+
ensure
|
229
|
+
trap("INT", prev_handler)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
data/lib/lydown/cli/support.rb
CHANGED
@@ -49,6 +49,39 @@ module Lydown::CLI::Support
|
|
49
49
|
opts[:path] = parent_dir
|
50
50
|
end
|
51
51
|
end
|
52
|
+
|
53
|
+
MINIMAL_LILYPOND_VERSION = Gem::Version.new('2.18')
|
54
|
+
|
55
|
+
# detect the lilypond version. If lilypond is not found, or the version is
|
56
|
+
# less than the minimal supported version, display an error message.
|
57
|
+
def self.detect_lilypond_version(exit_on_error)
|
58
|
+
msg = `lilypond --version`
|
59
|
+
version = nil
|
60
|
+
if msg.lines.first =~ /LilyPond ([\d\.]+)/
|
61
|
+
version = $1
|
62
|
+
end
|
63
|
+
unless version && Gem::Version.new(version) >= MINIMAL_LILYPOND_VERSION
|
64
|
+
display_lilypond_version_error_msg(version)
|
65
|
+
exit!(1) if exit_on_error
|
66
|
+
version = nil
|
67
|
+
end
|
68
|
+
version
|
69
|
+
rescue => e
|
70
|
+
display_lilypond_version_error_msg(nil)
|
71
|
+
exit!(1) if exit_on_error
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.display_lilypond_version_error_msg(version)
|
75
|
+
if version
|
76
|
+
STDERR.puts "ERROR: The installed lilypond (version #{version}) is too old.
|
77
|
+
You can install lilypond by running `lydown install lilypond` or by
|
78
|
+
downloading a recent version from http://lilypond.org/"
|
79
|
+
else
|
80
|
+
STDERR.puts "ERROR: No copy of lilypond found.
|
81
|
+
You can install lilypond by running `lydown install lilypond` or by
|
82
|
+
downloading a recent version from http://lilypond.org/"
|
83
|
+
end
|
84
|
+
end
|
52
85
|
end
|
53
86
|
|
54
87
|
|
data/lib/lydown/core_ext.rb
CHANGED
@@ -213,3 +213,11 @@ class Pathname
|
|
213
213
|
self.new(path).relative_path_from(pwd).to_s
|
214
214
|
end
|
215
215
|
end
|
216
|
+
|
217
|
+
unless Binding.method_defined?(:local_variable_set)
|
218
|
+
class Binding
|
219
|
+
def local_variable_set(var, value)
|
220
|
+
eval("#{var.to_s} = #{value.inspect}")
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
data/lib/lydown/lilypond.rb
CHANGED
data/lib/lydown/ly_lib/lib.ly
CHANGED
data/lib/lydown/parsing.rb
CHANGED
data/lib/lydown/rendering.rb
CHANGED
@@ -63,6 +63,40 @@ module Lydown::Rendering
|
|
63
63
|
|
64
64
|
"\"#{varname}\""
|
65
65
|
end
|
66
|
+
|
67
|
+
def add_includes(filenames, context, key, opts)
|
68
|
+
includes = context.get_setting(key, opts)
|
69
|
+
filenames.concat(includes) if includes
|
70
|
+
end
|
71
|
+
|
72
|
+
def include_files(context, opts)
|
73
|
+
filenames = []
|
74
|
+
if opts.has_key?(:movement)
|
75
|
+
add_includes(filenames, context, :includes, opts)
|
76
|
+
case context.render_mode
|
77
|
+
when :score
|
78
|
+
add_includes(filenames, context, 'score/includes', opts)
|
79
|
+
when :part
|
80
|
+
add_includes(filenames, context, 'parts/includes', opts)
|
81
|
+
if opts[:part]
|
82
|
+
add_includes(filenames, context, "parts/#{opts[:part]}/includes", opts)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
else
|
86
|
+
# paths to be included at top of lilypond doc should be defined under
|
87
|
+
# document/includes
|
88
|
+
add_includes(filenames, context, 'document/includes', opts)
|
89
|
+
end
|
90
|
+
|
91
|
+
filenames.map do |fn|
|
92
|
+
case File.extname(fn)
|
93
|
+
when '.ely'
|
94
|
+
Lydown::Templates.render(fn, context)
|
95
|
+
else
|
96
|
+
"\\include \"#{fn}\""
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
66
100
|
end
|
67
101
|
end
|
68
102
|
|
@@ -43,17 +43,6 @@ module Lydown::Rendering
|
|
43
43
|
PAGE_BREAKS[setting] || {}
|
44
44
|
end
|
45
45
|
|
46
|
-
def self.include_files(context, opts)
|
47
|
-
(context.get_setting(:includes, opts) || []).map do |fn|
|
48
|
-
case File.extname(fn)
|
49
|
-
when '.ely'
|
50
|
-
Lydown::Templates.render(fn, context)
|
51
|
-
else
|
52
|
-
"\\include \"#{fn}\""
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
46
|
# Groups movements by bookparts. Whenever a movement requires a page break
|
58
47
|
# before, a new group is created
|
59
48
|
def self.bookparts(context, opts)
|
@@ -1,3 +1,8 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'pathname'
|
5
|
+
|
1
6
|
module Lydown::Rendering
|
2
7
|
class Setting < Base
|
3
8
|
include Notes
|
@@ -7,7 +12,7 @@ module Lydown::Rendering
|
|
7
12
|
'accidentals', 'beams', 'end_barline', 'macros', 'empty_staves',
|
8
13
|
'midi_tempo', 'instrument_names', 'instrument_name_style',
|
9
14
|
'parts', 'score', 'movement_source', 'colla_parte', 'include',
|
10
|
-
'mode', 'nomode', 'bar_numbers'
|
15
|
+
'mode', 'nomode', 'bar_numbers', 'document', 'notation_size'
|
11
16
|
]
|
12
17
|
|
13
18
|
RENDERABLE_SETTING_KEYS = [
|
@@ -22,7 +27,8 @@ module Lydown::Rendering
|
|
22
27
|
'instrument_name_style' => ['normal', 'smallcaps'],
|
23
28
|
'page_break' => ['none', 'before', 'after', 'before and after', 'blank page before'],
|
24
29
|
'mode' => ['score', 'part', 'none'],
|
25
|
-
'bar_numbers' => ['hide', 'shows']
|
30
|
+
'bar_numbers' => ['hide', 'shows'],
|
31
|
+
'notation_size' => ['huge', 'large', 'normalsize', 'small', 'tiny', 'teeny']
|
26
32
|
}
|
27
33
|
|
28
34
|
def translate
|
@@ -60,10 +66,7 @@ module Lydown::Rendering
|
|
60
66
|
@context[:movement] = value
|
61
67
|
@context.reset(:movement)
|
62
68
|
when 'include'
|
63
|
-
|
64
|
-
includes ||= []
|
65
|
-
includes << value
|
66
|
-
@context.set_setting(:includes, includes)
|
69
|
+
add_include(:includes, value)
|
67
70
|
when 'mode'
|
68
71
|
set_mode(value.nil? ? :none : value.to_sym)
|
69
72
|
when 'nomode'
|
@@ -81,8 +84,13 @@ module Lydown::Rendering
|
|
81
84
|
while l < level
|
82
85
|
path << "#{@context['process/setting_levels'][l]}/"; l += 1
|
83
86
|
end
|
84
|
-
|
85
|
-
|
87
|
+
case key
|
88
|
+
when 'include'
|
89
|
+
add_include(path + 'includes', value)
|
90
|
+
else
|
91
|
+
path << key
|
92
|
+
@context.set_setting(path, value)
|
93
|
+
end
|
86
94
|
end
|
87
95
|
|
88
96
|
@context['process/setting_levels'] ||= {}
|
@@ -154,6 +162,24 @@ module Lydown::Rendering
|
|
154
162
|
def set_mode(mode)
|
155
163
|
@context['process/mode'] = (mode == :none) ? nil : mode
|
156
164
|
end
|
165
|
+
|
166
|
+
def add_include(includes_path, path)
|
167
|
+
includes = @context.get_current_setting(includes_path) || []
|
168
|
+
|
169
|
+
source_filename = @context['process/last_filename']
|
170
|
+
if source_filename
|
171
|
+
absolute_path = File.expand_path(
|
172
|
+
File.join(File.dirname(source_filename), path)
|
173
|
+
)
|
174
|
+
# calculate relative path to working directory
|
175
|
+
pwd = Pathname.new(FileUtils.pwd)
|
176
|
+
includes << Pathname.new(absolute_path).relative_path_from(pwd).to_s
|
177
|
+
else
|
178
|
+
includes << path
|
179
|
+
end
|
180
|
+
|
181
|
+
@context.set_setting(includes_path, includes)
|
182
|
+
end
|
157
183
|
end
|
158
184
|
end
|
159
185
|
|