squared 0.5.16 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ unless defined?(Readline)
4
+ if RUBY_ENGINE == 'ruby' && RUBY_VERSION < '2.7'
5
+ require 'readline'
6
+ else
7
+ begin
8
+ require 'reline'
9
+ Object.send(:remove_const, :Readline) if Object.const_defined?(:Readline)
10
+ Readline = Reline
11
+ rescue LoadError
12
+ require 'readline'
13
+ end
14
+ end
15
+ end
16
+
3
17
  module Squared
4
18
  module Common
5
19
  module Prompt
6
20
  module_function
7
21
 
8
- def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts: 5, timeout: 30)
9
- require 'readline'
22
+ def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts: 3, timeout: 60)
10
23
  require 'timeout'
11
24
  if agree == 'Y' && cancel == 'N' && !msg.match?(%r{\[(?:Yn|nY|Y/n|y/N)\]})
12
25
  case default
@@ -38,9 +51,8 @@ module Squared
38
51
  end
39
52
  end
40
53
 
41
- def choice(msg, list = nil, min: 1, max: 1, multiple: false, force: true, grep: nil, auto: true,
42
- attempts: 5, timeout: 0)
43
- require 'readline'
54
+ def choice(msg, list = nil, min: 1, max: 1, multiple: false, index: false, grep: nil, border: nil, auto: true,
55
+ force: true, attempts: 3, timeout: 0)
44
56
  require 'timeout'
45
57
  if list
46
58
  grep &&= Array(grep).map { |val| Regexp.new(val) }
@@ -52,39 +64,51 @@ module Squared
52
64
  puts '%2d. %s' % [items.size, val]
53
65
  end
54
66
  max = items.size
55
- raise_error 'empty selection list' if max == 0
67
+ raise ArgumentError, 'empty selection list' if max == 0
68
+
56
69
  min = grep ? 1 : [min, max].min
57
70
  if auto
58
- msg = "#{msg}: [#{min}-#{max}#{if multiple
59
- "|,#{multiple.is_a?(::Numeric) ? "{#{multiple}}" : ''}"
60
- end}] "
71
+ auto.times { puts } if auto.is_a?(::Numeric)
72
+ if border == true
73
+ puts print_footer
74
+ elsif border
75
+ puts print_footer(border: border)
76
+ end
77
+ msg = "#{msg + (force ? ':' : '?')} [#{min}-#{max}#{if (n = multiple)
78
+ "|,#{n.is_a?(::Numeric) ? "{#{n}}" : '*'}"
79
+ end}] "
61
80
  end
62
81
  end
63
- valid = ->(s) { s.match?(/^\d+$/) && s.to_i.between?(min, max) }
82
+ between = ->(s) { s.match?(/^\d+$/) && s.to_i.between?(min, max) }
64
83
  Timeout.timeout(timeout) do
65
84
  while (ch = Readline.readline(msg))
66
85
  unless (ch = ch.strip).empty?
67
86
  if multiple
68
- a = ch.split(/\s*,\s*/)
69
- b = a.map do |s|
70
- if s =~ /^(\d+)-(\d+)$/
71
- next unless valid.call($1) && valid.call($2)
87
+ k = if ch == '*'
88
+ (min..max).to_a
89
+ else
90
+ ch.split(/\s*,\s*/).map! do |s|
91
+ if s =~ /^(\d+)-(\d+)$/
92
+ next unless between.call($1) && between.call($2)
72
93
 
73
- c = $1.to_i
74
- d = $2.to_i
75
- next (c..d).to_a if c < d
76
- elsif valid.call(s)
77
- s.to_i
94
+ i = $1.to_i
95
+ j = $2.to_i
96
+ next (i..j).to_a if i < j
97
+ elsif between.call(s)
98
+ s.to_i
99
+ end
100
+ end
101
+ end
102
+ unless k.include?(nil)
103
+ k.flatten!
104
+ k.uniq!
105
+ k.sort!
106
+ unless multiple.is_a?(::Numeric) && multiple != k.size
107
+ return index || !items ? k : k.map! { |i| items[i.pred] }
78
108
  end
79
109
  end
80
- unless b.include?(nil)
81
- b.flatten!
82
- b.uniq!
83
- b.sort!
84
- return items ? b.map! { |i| items[i - 1] } : b unless multiple.is_a?(::Numeric) && multiple != b.size
85
- end
86
- elsif valid.call(ch)
87
- return items ? items[ch.to_i - 1] : ch.to_i
110
+ elsif between.call(ch)
111
+ return index || !items ? ch.to_i : items[ch.to_i.pred]
88
112
  end
89
113
  end
90
114
  attempts -= 1
@@ -97,12 +121,11 @@ module Squared
97
121
  puts
98
122
  exit 0
99
123
  else
100
- multiple ? [] : nil
124
+ [] if multiple
101
125
  end
102
126
  end
103
127
 
104
128
  def readline(msg, history = false, force: nil, multiline: nil, &blk)
105
- require 'readline'
106
129
  multiline = if multiline && Readline.respond_to?(:readmultiline)
107
130
  multiline.is_a?(::Enumerable) || block_given? ? multiline : [multiline.to_s]
108
131
  end
@@ -117,11 +140,11 @@ module Squared
117
140
  end
118
141
  case force
119
142
  when ::TrueClass, ::FalseClass
120
- msg = "#{msg} %s " % if multiline
121
- multiline.is_a?(::Enumerable) ? "{#{multiline.to_a.join('|')}}" : multiline
122
- else
123
- "(#{force ? 'required' : 'optional'}):"
124
- end
143
+ msg = "#{msg}%s%s " % if multiline
144
+ [' ', multiline.is_a?(::Enumerable) ? "{#{multiline.to_a.join('|')}}" : multiline]
145
+ else
146
+ [force ? ':' : '?', '']
147
+ end
125
148
  ret = (prompt.call || '').strip
126
149
  multiline.each { |val| break if ret.delete_suffix!(val.to_s) } if multiline.is_a?(::Enumerable)
127
150
  exit 1 if force && ret.empty?
@@ -6,14 +6,11 @@ require 'shellwords'
6
6
  module Squared
7
7
  module Common
8
8
  module Shell
9
- QUOTE_VALUE = /\A(["'])(.*)\1\z/m.freeze
10
- private_constant :QUOTE_VALUE
11
-
12
9
  module_function
13
10
 
14
11
  def shell_escape(val, quote: false, force: false, double: false, option: false, override: false)
15
- if (r = /\A(--?)([^=\s]+)((=|\s+)(["'])?(?(5)(.*)\5|(.*)))?\z/m.match(val = val.to_s))
16
- if (data = r[2].match(QUOTE_VALUE))
12
+ if (r = /\A(--?)([^= ]+)((=|\s+)(["'])?(?(5)(.*)\5|(.*)))?\z/m.match(val = val.to_s))
13
+ if (data = r[2].match(/\A(["'])(.+)\1\z/))
17
14
  double = data[1] == '"'
18
15
  override = true
19
16
  elsif !r[3] || r[6]
@@ -28,9 +25,9 @@ module Squared
28
25
 
29
26
  r[7]
30
27
  end
31
- r[1] + (data ? data[2] : r[2]) + r[4] + shell_quote(opt, force: force, double: double, override: override)
32
- elsif option && val =~ /\A(-{0,2}[^=\s-][^=\s]*)=(.+)\z/m
33
- return val if $2.match?(QUOTE_VALUE)
28
+ r[1] + (data ? data[2] : r[2]) + r[4] + shell_quote(opt, double: double, force: force, override: override)
29
+ elsif option && val =~ /\A([^=]+)=(.+)\z/m
30
+ return val if $2.match?(/\A(["']).+\1\z/m)
34
31
 
35
32
  "#{$1}=%s" % if $2.include?(' ')
36
33
  shell_quote($2, option: false)
@@ -40,7 +37,7 @@ module Squared
40
37
  Shellwords.escape($2)
41
38
  end
42
39
  elsif Rake::Win32.windows?
43
- quote ? shell_quote(val, force: force, double: double) : val
40
+ quote ? shell_quote(val, double: double, force: force) : val
44
41
  elsif val.empty?
45
42
  ''
46
43
  else
@@ -48,56 +45,46 @@ module Squared
48
45
  end
49
46
  end
50
47
 
51
- def shell_quote(val, option: true, force: true, double: false, preserve: true, override: false)
48
+ def shell_quote(val, option: true, force: true, double: false, override: false)
52
49
  val = val.to_s
53
50
  return val if (!force && !val.include?(' ')) || val.empty?
54
51
 
55
- if option
56
- pat = /\A(?:-[^=\s-](?:=|\s+)?|(--)?[^=\s-][^=\s]*(?(1)(?:=|\s+)|=))(["']).+\2\z/m
57
- return val if val.match?(pat)
58
- end
59
- q = ->(s) { s.gsub("'\\\\''", "'") }
60
- if val =~ QUOTE_VALUE
61
- return val if $1 == '"' && Rake::Win32.windows? && val.match?(/(?:[#{File::SEPARATOR} ]|\\")/o)
62
-
63
- base = $2 unless preserve
64
- end
65
- if double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
66
- "\"#{q.call(base || val).gsub(/(?<!\\)"/, '\\"')}\""
52
+ if option && val.match?(/(?:\A|\A[^=\s]+(?:=|\s+)|#{Rake::Win32.windows? ? '[\\\/]' : '\/'})(["']).+\1\z/mo)
53
+ val
54
+ elsif double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
55
+ "\"#{val.gsub(/(?<!\\)"/, '\\"')}\""
67
56
  else
68
- base ? val : "'#{q.call(val).gsub("'", "'\\\\''")}'"
57
+ "'#{val.gsub("'", "'\\\\''")}'"
69
58
  end
70
59
  end
71
60
 
72
- def shell_option(flag, val = nil, escape: true, quote: true, option: true, force: true, double: false,
73
- merge: false, override: false)
61
+ def shell_option(flag, val = nil, sep: '=', escape: true, quote: true, force: true, double: false, merge: false,
62
+ override: false)
74
63
  flag = flag.to_s
75
- if flag =~ QUOTE_VALUE
64
+ if flag =~ /\A(["'])(.+)\1\z/
76
65
  double = $1 == '"'
77
66
  flag = $2
78
67
  escape = false
79
68
  override = true
80
69
  end
81
- sep = unless flag.empty?
82
- if flag[0] == '-'
83
- flag[1] == '-' ? '=' : ' '
84
- elsif flag.size == 1
85
- pre = '-'
86
- merge ? '' : ' '
87
- else
88
- pre = '--'
89
- '='
90
- end
91
- end
92
- "#{pre}#{flag}#{unless val.nil?
93
- "#{sep}#{if escape
94
- shell_escape(val, quote: quote, double: double, override: override)
95
- elsif quote
96
- shell_quote(val, option: option, force: force, double: double, override: override)
97
- else
98
- val
99
- end}"
100
- end}"
70
+ b = if flag[0] == '-'
71
+ flag[1] == '-' ? sep : ' '
72
+ elsif flag.size == 1
73
+ a = '-'
74
+ merge ? '' : ' '
75
+ else
76
+ a = '--'
77
+ sep
78
+ end
79
+ "#{a}#{flag}#{unless val.nil?
80
+ "#{b}#{if escape
81
+ shell_escape(val, quote: quote, double: double, override: override)
82
+ elsif quote
83
+ shell_quote(val, option: false, force: force, double: double, override: override)
84
+ else
85
+ val
86
+ end}"
87
+ end}"
101
88
  end
102
89
 
103
90
  def shell_split(val, join: nil, **kwargs)
@@ -107,10 +94,45 @@ module Squared
107
94
  ret.join(join.is_a?(::String) ? join : ' ')
108
95
  end
109
96
 
97
+ def shell_parse(val, escape: false, force: true, **kwargs)
98
+ a = []
99
+ b = []
100
+ c = []
101
+ d = []
102
+ e = [a, b]
103
+ j = -1
104
+ val.shellsplit.each_with_index do |opt, i|
105
+ if opt == '--'
106
+ e = [c, d]
107
+ elsif opt =~ /\A--?[^=]+(=|\z)/
108
+ j = $1 == '=' ? -1 : i
109
+ e[0] << [opt]
110
+ elsif j >= 0
111
+ e[0][j] << opt
112
+ else
113
+ e[1] << shell_quote(opt, option: false, force: force)
114
+ end
115
+ end
116
+ ret = [[a, b], [], [c, d]].flat_map do |e, f|
117
+ next '--' unless e
118
+
119
+ e.flat_map do |item|
120
+ if item.size == 1
121
+ fill_option(item.first)
122
+ else
123
+ flag = item.shift
124
+ item.map! { |s| shell_option(flag, s, escape: escape, force: force, **kwargs) }
125
+ end
126
+ end.concat(f)
127
+ end
128
+ ret.pop if ret.last == '--'
129
+ ret
130
+ end
131
+
110
132
  def shell_bin(name, env: true)
111
133
  key = name.upcase
112
- shell_quote((env && ENV["PATH_#{key}"]) || PATH[key] || PATH[key.to_sym] || name,
113
- option: false, force: false, double: true)
134
+ shell_quote((env && ENV["PATH_#{key}"]) || PATH[key] || PATH[key.to_sym] || name, option: false, force: false,
135
+ double: true)
114
136
  end
115
137
 
116
138
  def line_width(lines)
@@ -119,7 +141,6 @@ module Squared
119
141
  end
120
142
 
121
143
  def fill_option(val, **kwargs)
122
- return val unless val.is_a?(::String)
123
144
  return "-#{val}" if val.match?(/\A(?:[a-z]\d*|\d)\z/i)
124
145
 
125
146
  shell_escape(val.start_with?('-') ? val : "--#{val}", **kwargs)
@@ -6,6 +6,25 @@ require 'rake'
6
6
  module Squared
7
7
  module Common
8
8
  module System
9
+ class << self
10
+ private
11
+
12
+ def parse_link(val)
13
+ case val
14
+ when ::TrueClass, 's'
15
+ 1
16
+ when 'r'
17
+ 2
18
+ when 'h'
19
+ 3
20
+ else
21
+ raise ArgumentError, "unrecognized 'link' flag: #{val}" if val
22
+
23
+ 0
24
+ end
25
+ end
26
+ end
27
+
9
28
  module_function
10
29
 
11
30
  def shell(*args, name: :system, **kwargs)
@@ -29,11 +48,11 @@ module Squared
29
48
  raise $?.to_s
30
49
  end
31
50
 
32
- def copy_dir(src, dest, glob = ['**/*'], create: false, link: nil, force: false, pass: nil, hidden: false,
33
- verbose: true)
51
+ def copy_dir(src, dest, glob = ['**/*'], create: false, link: nil, preserve: nil, force: false, verbose: true,
52
+ pass: nil, hidden: false)
34
53
  base = Pathname.new(src)
35
54
  target = Pathname.new(dest)
36
- raise "#{target.cleanpath} (not found)" if !create && !target.parent.exist?
55
+ raise Errno::ENOENT, dest.cleanpath.to_s unless create || target.parent.exist?
37
56
 
38
57
  subdir = {}
39
58
  target.mkpath if create
@@ -57,53 +76,67 @@ module Squared
57
76
  end
58
77
  count = 0
59
78
  soft = 0
79
+ type = System.send :parse_link, link
60
80
  subdir.each do |dir, files|
61
- if link
62
- files.dup.tap do |items|
63
- files.clear
64
- items.each do |file|
65
- if file.exist?
66
- if !file.symlink?
67
- files << file
68
- elsif !force
69
- next
70
- end
71
- end
72
- if link == 'hard'
73
- FileUtils.ln(file, dir, force: force, verbose: false)
74
- else
75
- FileUtils.ln_s(file, dir, force: force, verbose: false)
81
+ unless type == 0
82
+ items = files.dup
83
+ files.clear
84
+ items.each do |file|
85
+ if file.exist?
86
+ if !file.symlink?
87
+ files << file
88
+ elsif !force
89
+ next
76
90
  end
77
- soft += 1
78
91
  end
92
+ case type
93
+ when 1
94
+ FileUtils.ln_s(file, dir, force: force, verbose: false)
95
+ when 2
96
+ FileUtils.ln_s(file.relative_path_from(dir), dir, force: force, verbose: false)
97
+ else
98
+ FileUtils.ln(file, dir, force: force, verbose: false)
99
+ end
100
+ soft += 1
79
101
  end
80
102
  end
81
103
  next if files.empty?
82
104
 
83
- out = FileUtils.cp(files, dir, verbose: false)
105
+ out = FileUtils.cp(files, dir, preserve: preserve, verbose: false)
84
106
  count += out.size
85
107
  end
86
108
  puts [target.realpath, subdir.size, soft > 0 ? "#{count}+#{soft}" : count].join(' => ') if verbose
87
109
  end
88
110
 
89
- def copy_guard(src, dest, link: nil, force: false, verbose: true)
90
- unless force
91
- target = Pathname.new(dest)
92
- if target.directory?
93
- src = Array(src).reject { |val| target.join(File.basename(val)).exist? }
94
- return if src.empty?
95
- elsif target.exist?
96
- return
111
+ def copy_guard(*src, dest, base: '.', create: false, link: nil, preserve: nil, force: false, verbose: true)
112
+ src = src.compact.flatten
113
+ dest = Pathname.new(dest).realdirpath
114
+ base = Pathname.new(base).realpath
115
+ dir = if dest.directory?
116
+ true
117
+ elsif src.size > 1
118
+ raise Errno::ENOENT, dest.cleanpath.to_s unless create && !dest.exist?
119
+
120
+ dest.mkpath
121
+ true
122
+ end
123
+ targets = src.map! { |file| [base + file, dir ? dest + File.basename(file) : dest] }
124
+ return if !force && (targets = targets.reject { |to| to[1].exist? }).empty?
125
+
126
+ type = System.send :parse_link, link
127
+ targets.each do |file, to|
128
+ case type
129
+ when 0
130
+ FileUtils.cp(file, to, preserve: preserve, verbose: verbose)
131
+ when 1
132
+ FileUtils.ln_s(file, to, force: force, verbose: verbose)
133
+ when 2
134
+ FileUtils.ln_s(file.relative_path_from(dir ? to.dirname : to), to, force: force, verbose: verbose)
135
+ else
136
+ FileUtils.ln(file, to, force: force, verbose: verbose)
97
137
  end
98
138
  end
99
- case link
100
- when 'hard', 1
101
- FileUtils.ln(src, dest, force: force, verbose: verbose)
102
- when ::TrueClass, 'soft', 0
103
- FileUtils.ln_s(src, dest, force: force, verbose: verbose)
104
- else
105
- FileUtils.cp(src, dest, verbose: verbose)
106
- end
139
+ nil
107
140
  end
108
141
  end
109
142
  end
@@ -9,21 +9,44 @@ module Squared
9
9
  module Utils
10
10
  module_function
11
11
 
12
- def split_escape(val, char: ',')
13
- val.split(/\s*(?<!\\)#{char}\s*/)
12
+ def as_a(obj, *meth, flat: nil, compact: false, &blk)
13
+ return [] if obj.nil?
14
+
15
+ unless obj.is_a?(::Array)
16
+ obj = if obj.respond_to?(:to_ary)
17
+ obj.to_ary
18
+ elsif obj.respond_to?(:to_a) && !obj.is_a?(::Hash) && (val = obj.to_a).is_a?(::Array)
19
+ val
20
+ else
21
+ [obj]
22
+ end
23
+ end
24
+ obj = flat.is_a?(::Numeric) ? obj.flatten(flat) : obj.flatten if flat
25
+ obj = obj.compact if compact
26
+ obj = obj.map(&meth.shift) until meth.empty?
27
+ return obj unless block_given?
28
+
29
+ obj.select(&blk)
30
+ end
31
+
32
+ def split_escape(val, char: ',', &blk)
33
+ ret = val.split(/\s*(?<!\\)#{char}\s*/)
34
+ return ret unless block_given?
35
+
36
+ ret.each(&blk)
14
37
  end
15
38
 
16
39
  def split_option(val)
17
40
  val = val.strip
18
41
  return [val, '', ''] unless (i = val.index('='))
19
42
 
20
- last = val[(i + 1)..-1].strip
43
+ last = val[i.succ..-1].strip
21
44
  quote = ''
22
45
  if last =~ /\A(["'])(.+)\1\z/
23
46
  last = $2
24
47
  quote = $1
25
48
  end
26
- [val[0..(i - 1)], last, quote]
49
+ [val[0..i.pred], last, quote]
27
50
  end
28
51
 
29
52
  def task_invoke(*cmd, args: [], exception: true, warning: true)
@@ -105,9 +128,9 @@ module Squared
105
128
  end
106
129
  end
107
130
 
108
- def env(key, default = nil, suffix: nil, strict: false, equals: nil, ignore: nil)
131
+ def env(key, default = nil, suffix: nil, strict: false, equals: nil, ignore: nil, **)
109
132
  ret = env_value(key, suffix: suffix, strict: strict)
110
- return ret == equals.to_s unless equals.nil?
133
+ return Array(equals).any? { |val| val.to_s == ret } unless equals.nil?
111
134
 
112
135
  ret.empty? || (ignore && Array(ignore).any? { |val| val.to_s == ret }) ? default : ret
113
136
  end
@@ -12,14 +12,15 @@ module Squared
12
12
  include Rake::DSL
13
13
 
14
14
  class << self
15
- def parse(gem, namespace, ext = [gem])
15
+ def parse(gem, namespace, ext = [pkg])
16
16
  require gem
17
- [eval(namespace), Array(ext)].tap do |data|
18
- data.last.each { |key| @@mime_obj[key] = data }
19
- end
17
+ obj = eval namespace
18
+ Array(ext).each { |val| @@mime_obj[val] = [obj, ext] }
20
19
  rescue LoadError, NameError => e
21
20
  warn e
22
21
  nil
22
+ else
23
+ @@mime_obj[ext.first]
23
24
  end
24
25
 
25
26
  def link(project, main = project.dependfile.basename, name = nil, **kwargs, &blk)
@@ -85,7 +86,7 @@ module Squared
85
86
  ['path not found', realpath]
86
87
  else
87
88
  @required = true
88
- project ? [project, 'not found'] : %w[name missing]
89
+ project ? [project, 'missing'] : %w[name missing]
89
90
  end
90
91
  warn log_message(Logger::WARN, msg, subject: self.class, hint: hint)
91
92
  end
@@ -105,7 +106,7 @@ module Squared
105
106
  next unless (data = Viewer.parse(type, type.upcase, ext))
106
107
  end
107
108
  obj, ext = data
108
- target = file || target? ? file || realpath : nil
109
+ target = file || (realpath if target?)
109
110
 
110
111
  task_desc(command, *ext, target: target)
111
112
  task type, [:keys] do |_, args|
@@ -204,14 +205,14 @@ module Squared
204
205
 
205
206
  def read_keys(reader, type, file, keys, ext: [type], opts: {})
206
207
  if file && (mime = mimetype(file)) && basepath(file).exist?
207
- raise_error(file, mime, hint: 'invalid') unless ext.include?(mime)
208
+ raise_error file, mime, hint: 'invalid' unless ext.include?(mime)
208
209
  else
209
210
  if ext.include?(mime)
210
211
  alt = file
211
212
  file = nil
212
213
  ext[0] = mime
213
214
  elsif file
214
- keys.prepend(file)
215
+ keys.unshift(file)
215
216
  alt = basepath "#{main}.{#{ext.join(',')}}"
216
217
  file = Dir[alt].first
217
218
  else
@@ -219,7 +220,7 @@ module Squared
219
220
  args = { hint: 'no keys' }
220
221
  end
221
222
  unless file
222
- args ||= { hint: 'not found', kind: LoadError }
223
+ args ||= { kind: Errno::ENOENT }
223
224
  raise_error(reader.name, "#{File.basename(alt, '.*')}.#{ext.first}", **args)
224
225
  end
225
226
  end
@@ -236,22 +237,18 @@ module Squared
236
237
  title = Pathname.new(file)
237
238
  .realpath
238
239
  .to_s
239
- .sub(/\A#{Regexp.escape(File.join(Dir.pwd, ''))}/, '')
240
+ .sub(/^#{Regexp.escape(File.join(Dir.pwd, ''))}/, '')
240
241
  emphasize(lines, title: title, sub: unless stdin?
241
242
  [
242
- { pat: /\A((?:[^:]|(?<! ):(?! ))+)\z/, styles: theme[:banner] },
243
- { pat: /\A(.*?)(<[^>]+>)(.+)\z/m, styles: theme[:undefined], index: 2 },
244
- { pat: /\A((?~ : ))( : (?!undefined).+)\z/m, styles: theme[:key] },
245
- { pat: /\A((?~: ): )(-?[\d.]+)(\s*)\z/m, styles: theme[:number],
246
- index: 2 },
247
- { pat: /\A((?~: ): ")(.+)("\s*)\z/m, styles: theme[:string], index: 2 },
248
- { pat: /\A((?~: ): \{)(.+)(}\s*)\z/m, styles: theme[:hash], index: 2 },
249
- { pat: /\A((?~: ): \[)(.+)(\]\s*)\z/m, styles: theme[:array],
250
- index: 2 },
251
- { pat: /\A((?~: ): )(true|false)(\s*)\z/m, styles: theme[:boolean],
252
- index: 2 },
253
- { pat: /\A((?~: ): (?!undefined))([^"\[{].*)\z/m, styles: theme[:value],
254
- index: 2 }
243
+ opt_style(theme[:banner], /\A((?:[^:]|(?<! ):(?! ))+)\z/),
244
+ opt_style(theme[:undefined], /\A(.*?)(<[^>]+>)(.+)\z/m, 2),
245
+ opt_style(theme[:key], /\A((?~ : ))( : (?!undefined).+)\z/m),
246
+ opt_style(theme[:number], /\A((?~: ): )(-?[\d.]+)(\s*)\z/m, 2),
247
+ opt_style(theme[:string], /\A((?~: ): ")(.+)("\s*)\z/m, 2),
248
+ opt_style(theme[:hash], /\A((?~: ): \{)(.+)(\}\s*)\z/m, 2),
249
+ opt_style(theme[:array], /\A((?~: ): \[)(.+)(\]\s*)\z/m, 2),
250
+ opt_style(theme[:boolean], /\A((?~: ): )(true|false)(\s*)\z/m, 2),
251
+ opt_style(theme[:value], /\A((?~: ): (?!undefined))([^"\[{].*)\z/m, 2)
255
252
  ]
256
253
  end, border: theme[:border])
257
254
  end
@@ -262,7 +259,7 @@ module Squared
262
259
  symbolize = opts[:symbolize_names]
263
260
  keys.each do |key|
264
261
  begin
265
- items = key.split('.').flat_map { |name| name =~ /^(.+)\[(\d+)\]$/ ? [$1, $2.to_i] : name }
262
+ items = key.split('.')
266
263
  items = items.map(&:to_sym) if symbolize
267
264
  val = data.dig(*items)
268
265
  if val.nil?
@@ -308,8 +305,8 @@ module Squared
308
305
  def task_desc(command, *ext, target: nil)
309
306
  return unless Rake::TaskManager.record_task_metadata
310
307
 
311
- val = "#{ext.first}[#{target ? '' : "file?=#{File.basename(main)}.#{ext.last},"}keys+]"
312
- args = *name.split(':').append(command, val)
308
+ val = "#{ext.first}[#{"file?=#{File.basename(main)}.#{ext.last}," if target}keys+]"
309
+ args = *name.split(':').push(command, val)
313
310
  if project
314
311
  project.workspace.task_desc(*args)
315
312
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.5.16'
4
+ VERSION = '0.6.0'
5
5
  end