doing 2.1.4pre → 2.1.7
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/.yardoc/checksums +14 -13
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/.yardopts +1 -1
- data/CHANGELOG.md +37 -1
- data/Gemfile.lock +3 -1
- data/README.md +5 -1
- data/bin/doing +192 -95
- data/docs/_config.yml +1 -0
- data/{doc → docs/doc}/Array.html +63 -1
- data/docs/doc/BooleanTermParser/Clause.html +293 -0
- data/docs/doc/BooleanTermParser/Operator.html +172 -0
- data/docs/doc/BooleanTermParser/Query.html +417 -0
- data/docs/doc/BooleanTermParser/QueryParser.html +135 -0
- data/docs/doc/BooleanTermParser/QueryTransformer.html +124 -0
- data/docs/doc/BooleanTermParser.html +115 -0
- data/docs/doc/Doing/CLIFormat.html +131 -0
- data/{doc → docs/doc}/Doing/Color.html +2 -2
- data/{doc → docs/doc}/Doing/Completion.html +1 -1
- data/{doc → docs/doc}/Doing/Configuration.html +117 -12
- data/{doc → docs/doc}/Doing/Content.html +0 -0
- data/{doc → docs/doc}/Doing/Errors/DoingNoTraceError.html +1 -1
- data/{doc → docs/doc}/Doing/Errors/DoingRuntimeError.html +1 -1
- data/{doc → docs/doc}/Doing/Errors/DoingStandardError.html +1 -1
- data/{doc → docs/doc}/Doing/Errors/EmptyInput.html +1 -1
- data/{doc → docs/doc}/Doing/Errors/NoResults.html +1 -1
- data/{doc → docs/doc}/Doing/Errors/PluginException.html +1 -1
- data/{doc → docs/doc}/Doing/Errors/UserCancelled.html +1 -1
- data/{doc → docs/doc}/Doing/Errors/WrongCommand.html +1 -1
- data/{doc → docs/doc}/Doing/Errors.html +1 -1
- data/{doc → docs/doc}/Doing/Hooks.html +1 -1
- data/{doc → docs/doc}/Doing/Item.html +100 -73
- data/{doc → docs/doc}/Doing/Items.html +2 -2
- data/{doc → docs/doc}/Doing/LogAdapter.html +70 -1
- data/{doc → docs/doc}/Doing/Note.html +5 -134
- data/{doc → docs/doc}/Doing/Pager.html +1 -1
- data/{doc → docs/doc}/Doing/Plugins.html +431 -35
- data/{doc → docs/doc}/Doing/Prompt.html +1 -1
- data/{doc → docs/doc}/Doing/Section.html +1 -1
- data/docs/doc/Doing/TemplateString.html +713 -0
- data/docs/doc/Doing/Util/Backup.html +686 -0
- data/{doc → docs/doc}/Doing/Util.html +1 -1
- data/{doc → docs/doc}/Doing/WWID.html +5 -5
- data/{doc → docs/doc}/Doing/WWIDFile.html +0 -0
- data/{doc → docs/doc}/Doing.html +4 -4
- data/{doc → docs/doc}/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/{doc → docs/doc}/GLI/Commands.html +1 -1
- data/{doc → docs/doc}/GLI.html +1 -1
- data/{doc → docs/doc}/Hash.html +1 -1
- data/docs/doc/PhraseParser/Operator.html +172 -0
- data/docs/doc/PhraseParser/PhraseClause.html +303 -0
- data/docs/doc/PhraseParser/Query.html +495 -0
- data/docs/doc/PhraseParser/QueryParser.html +136 -0
- data/docs/doc/PhraseParser/QueryTransformer.html +124 -0
- data/docs/doc/PhraseParser/TermClause.html +293 -0
- data/docs/doc/PhraseParser.html +115 -0
- data/{doc → docs/doc}/Status.html +1 -1
- data/{doc → docs/doc}/String.html +182 -12
- data/{doc → docs/doc}/Symbol.html +35 -1
- data/{doc → docs/doc}/Time.html +1 -1
- data/{doc → docs/doc}/_index.html +21 -14
- data/{doc → docs/doc}/class_list.html +1 -1
- data/{doc → docs/doc}/css/common.css +0 -0
- data/{doc → docs/doc}/css/full_list.css +0 -0
- data/{doc → docs/doc}/css/style.css +0 -0
- data/{doc → docs/doc}/file.README.html +6 -2
- data/{doc → docs/doc}/file_list.html +0 -0
- data/{doc → docs/doc}/frames.html +0 -0
- data/{doc → docs/doc}/index.html +6 -2
- data/{doc → docs/doc}/js/app.js +0 -0
- data/{doc → docs/doc}/js/full_list.js +0 -0
- data/{doc → docs/doc}/js/jquery.js +0 -0
- data/{doc → docs/doc}/method_list.html +313 -161
- data/{doc → docs/doc}/top-level-namespace.html +1 -1
- data/docs/index.md +60 -0
- data/doing.gemspec +1 -0
- data/doing.rdoc +74 -15
- data/example_plugin.rb +3 -1
- data/lib/completion/_doing.zsh +53 -41
- data/lib/completion/doing.bash +17 -6
- data/lib/completion/doing.fish +321 -2
- data/lib/doing/array.rb +9 -0
- data/lib/doing/completion/fish_completion.rb +46 -3
- data/lib/doing/completion/zsh_completion.rb +1 -1
- data/lib/doing/configuration.rb +33 -11
- data/lib/doing/item.rb +12 -3
- data/lib/doing/log_adapter.rb +28 -0
- data/lib/doing/note.rb +31 -30
- data/lib/doing/plugin_manager.rb +84 -21
- data/lib/doing/plugins/export/dayone_export.rb +209 -0
- data/lib/doing/plugins/export/html_export.rb +2 -2
- data/lib/doing/plugins/export/json_export.rb +1 -0
- data/lib/doing/plugins/export/markdown_export.rb +1 -1
- data/lib/doing/plugins/export/template_export.rb +90 -85
- data/lib/doing/prompt.rb +9 -6
- data/lib/doing/string.rb +68 -27
- data/lib/doing/symbol.rb +4 -0
- data/lib/doing/template_string.rb +197 -0
- data/lib/doing/util.rb +4 -2
- data/lib/doing/util_backup.rb +55 -3
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +37 -22
- data/lib/doing.rb +3 -0
- data/lib/examples/plugins/say_export.rb +1 -1
- data/lib/examples/plugins/wiki_export/wiki_export.rb +3 -3
- data/lib/templates/doing-dayone-entry.erb +6 -0
- data/lib/templates/doing-dayone.erb +5 -0
- metadata +95 -53
|
@@ -23,13 +23,15 @@ module Doing
|
|
|
23
23
|
out = ''
|
|
24
24
|
items.each do |item|
|
|
25
25
|
if opt[:highlight] && item.title =~ /@#{wwid.config['marker_tag']}\b/i
|
|
26
|
-
|
|
27
|
-
reset = Doing::Color.default
|
|
26
|
+
flag = Doing::Color.send(wwid.config['marker_color'])
|
|
27
|
+
reset = Doing::Color.reset + Doing::Color.default
|
|
28
28
|
else
|
|
29
|
-
|
|
29
|
+
flag = ''
|
|
30
30
|
reset = ''
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
placeholders = {}
|
|
34
|
+
|
|
33
35
|
if (!item.note.empty?) && wwid.config['include_notes']
|
|
34
36
|
note = item.note.map(&:strip).delete_if(&:empty?)
|
|
35
37
|
note.map! { |line| "#{line.sub(/^\t*/, '')} " }
|
|
@@ -40,26 +42,17 @@ module Doing
|
|
|
40
42
|
line.simple_wrap(width)
|
|
41
43
|
# line.chomp.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
|
|
42
44
|
end
|
|
43
|
-
note = note.
|
|
45
|
+
note = note.delete_if(&:empty?)
|
|
44
46
|
end
|
|
45
47
|
else
|
|
46
48
|
note = []
|
|
47
49
|
end
|
|
48
50
|
|
|
49
|
-
output
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
else
|
|
55
|
-
m
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
output.sub!(/%(\d+)?date/) do
|
|
60
|
-
pad = Regexp.last_match(1).to_i
|
|
61
|
-
format("%#{pad}s", item.date.strftime(opt[:format]))
|
|
62
|
-
end
|
|
51
|
+
# output.sub!(/%(\d+)?date/) do
|
|
52
|
+
# pad = Regexp.last_match(1).to_i
|
|
53
|
+
# format("%#{pad}s", item.date.strftime(opt[:format]))
|
|
54
|
+
# end
|
|
55
|
+
placeholders['date'] = item.date.strftime(opt[:format])
|
|
63
56
|
|
|
64
57
|
interval = wwid.get_interval(item, record: true, formatted: false) if opt[:times]
|
|
65
58
|
if interval
|
|
@@ -74,7 +67,8 @@ module Doing
|
|
|
74
67
|
end
|
|
75
68
|
|
|
76
69
|
interval ||= ''
|
|
77
|
-
output.sub!(/%interval/, interval)
|
|
70
|
+
# output.sub!(/%interval/, interval)
|
|
71
|
+
placeholders['interval'] = interval
|
|
78
72
|
|
|
79
73
|
duration = item.duration if opt[:duration]
|
|
80
74
|
if duration
|
|
@@ -88,78 +82,89 @@ module Doing
|
|
|
88
82
|
end
|
|
89
83
|
end
|
|
90
84
|
duration ||= ''
|
|
91
|
-
output.sub!(/%duration/, duration)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
output.
|
|
85
|
+
# output.sub!(/%duration/, duration)
|
|
86
|
+
placeholders['duration'] = duration
|
|
87
|
+
|
|
88
|
+
# output.sub!(/%(\d+)?shortdate/) do
|
|
89
|
+
# pad = Regexp.last_match(1) || 13
|
|
90
|
+
# format("%#{pad}s", item.date.relative_date)
|
|
91
|
+
# end
|
|
92
|
+
placeholders['shortdate'] = format("%13s", item.date.relative_date)
|
|
93
|
+
# output.sub!(/%section/, item.section) if item.section
|
|
94
|
+
placeholders['section'] = item.section || ''
|
|
95
|
+
placeholders['title'] = item.title
|
|
96
|
+
|
|
97
|
+
# title_rx = /(?mi)%(?<width>-?\d+)?(?:(?<ichar>[ _t])(?<icount>\d+))?(?<prefix>.[ _t]?)?title(?<after>.*?)$/
|
|
98
|
+
# title_color = Doing::Color.reset + output.match(/(?mi)^(.*?)(%.*?title)/)[1].last_color
|
|
99
|
+
|
|
100
|
+
# title_offset = Doing::Color.uncolor(output).match(title_rx).begin(0)
|
|
101
|
+
|
|
102
|
+
# output.sub!(title_rx) do
|
|
103
|
+
# m = Regexp.last_match
|
|
104
|
+
|
|
105
|
+
# after = m['after']
|
|
106
|
+
# pad = m['width'].to_i
|
|
107
|
+
# indent = ''
|
|
108
|
+
# if m['ichar']
|
|
109
|
+
# char = m['ichar'] =~ /t/ ? "\t" : ' '
|
|
110
|
+
# indent = char * m['icount'].to_i
|
|
111
|
+
# end
|
|
112
|
+
# prefix = m['prefix']
|
|
113
|
+
# if opt[:wrap_width]&.positive? || pad.positive?
|
|
114
|
+
# width = pad.positive? ? pad : opt[:wrap_width]
|
|
115
|
+
# item.title.wrap(width, pad: pad, indent: indent, offset: title_offset, prefix: prefix, color: title_color, after: after, reset: reset)
|
|
116
|
+
# # flag + item.title.gsub(/(.{#{opt[:wrap_width]}})(?=\s+|\Z)/, "\\1\n ").sub(/\s*$/, '') + reset
|
|
117
|
+
# else
|
|
118
|
+
# format("%s%#{pad}s%s", prefix, item.title.sub(/\s*$/, ''), after)
|
|
119
|
+
# end
|
|
120
|
+
# end
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
placeholders['note'] = note
|
|
125
|
+
placeholders['idnote'] = note.empty? ? '' : "\n#{note.map { |l| "\t\t#{l.strip} " }.join("\n")}"
|
|
126
|
+
placeholders['odnote'] = note.empty? ? '' : "\n#{note.map { |l| "#{l.strip} " }.join("\n")}"
|
|
127
|
+
placeholders['chompnote'] = note.empty? ? '' : note.map { |l| l.gsub(/\n+/, ' ').gsub(/(^\s*|\s*$)/, '').gsub(/\s+/, ' ') }.join(' ')
|
|
128
|
+
|
|
129
|
+
# if note.empty?
|
|
130
|
+
# output.gsub!(/%(chomp|[io]d|(\^.)?(([ _t]|[^a-z0-9])?\d+)?(.[ _t]?)?)?note/, '')
|
|
131
|
+
# else
|
|
132
|
+
# output.sub!(/%note/, "\n#{note.map { |l| "\t#{l.strip} " }.join("\n")}")
|
|
133
|
+
# output.sub!(/%idnote/, "\n#{note.map { |l| "\t\t#{l.strip} " }.join("\n")}")
|
|
134
|
+
# output.sub!(/%odnote/, "\n#{note.map { |l| "#{l.strip} " }.join("\n")}")
|
|
135
|
+
# output.sub!(/(?mi)%(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])?(?<icount>\d+))?(?<prefix>.[ _t]?)?note/) do
|
|
136
|
+
# m = Regexp.last_match
|
|
137
|
+
# mark = m['mchar'] || ''
|
|
138
|
+
# indent = if m['ichar']
|
|
139
|
+
# char = m['ichar'] =~ /t/ ? "\t" : ' '
|
|
140
|
+
# char * m['icount'].to_i
|
|
141
|
+
# else
|
|
142
|
+
# ''
|
|
143
|
+
# end
|
|
144
|
+
# prefix = m['prefix'] || ''
|
|
145
|
+
# "\n#{note.map { |l| "#{mark}#{indent}#{prefix}#{l.strip} " }.join("\n")}"
|
|
146
|
+
# end
|
|
147
|
+
|
|
148
|
+
# output.sub!(/%chompnote/) do
|
|
149
|
+
# note.map { |l| l.gsub(/\n+/, ' ').gsub(/(^\s*|\s*$)/, '').gsub(/\s+/, ' ') }.join(' ')
|
|
150
|
+
# end
|
|
151
|
+
# end
|
|
152
|
+
|
|
153
|
+
template = opt[:template].dup
|
|
154
|
+
template.sub!(/(?i-m)^([\s\S]*?)(%(?:[io]d|(?:\^[\s\S])?(?:(?:[ _t]|[^a-z0-9])?\d+)?(?:[\s\S][ _t]?)?)?note)([\s\S]*?)$/, '\1\3\2')
|
|
155
|
+
output = Doing::TemplateString.new(template, placeholders: placeholders, wrap_width: opt[:wrap_width], color: flag, tags_color: opt[:tags_color], reset: reset).colored
|
|
156
|
+
|
|
157
|
+
output.gsub!(/(?<!\\)%hr(_under)?/) do
|
|
155
158
|
o = ''
|
|
156
159
|
`tput cols`.to_i.times do
|
|
157
160
|
o += Regexp.last_match(1).nil? ? '-' : '_'
|
|
158
161
|
end
|
|
159
162
|
o
|
|
160
163
|
end
|
|
161
|
-
output.gsub!(
|
|
162
|
-
output.gsub!(
|
|
164
|
+
output.gsub!(/(?<!\\)%n/, "\n")
|
|
165
|
+
output.gsub!(/(?<!\\)%t/, "\t")
|
|
166
|
+
|
|
167
|
+
output.gsub!(/\\%/, '%')
|
|
163
168
|
|
|
164
169
|
out += "#{output}\n"
|
|
165
170
|
end
|
data/lib/doing/prompt.rb
CHANGED
|
@@ -118,14 +118,17 @@ module Doing
|
|
|
118
118
|
return nil unless $stdout.isatty
|
|
119
119
|
|
|
120
120
|
# fzf_args << '-1' # User is expecting a menu, and even if only one it seves as confirmation
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
default_args = []
|
|
122
|
+
default_args << %(--prompt="#{prompt}")
|
|
123
|
+
default_args << "--height=#{options.count + 2}"
|
|
124
|
+
default_args << '--info=inline'
|
|
125
|
+
default_args << '--multi' if multiple
|
|
125
126
|
header = "esc: cancel,#{multiple ? ' tab: multi-select, ctrl-a: select all,' : ''} return: confirm"
|
|
126
|
-
|
|
127
|
+
default_args << %(--header="#{header}")
|
|
128
|
+
default_args.concat(fzf_args)
|
|
127
129
|
options.sort! if sorted
|
|
128
|
-
|
|
130
|
+
|
|
131
|
+
res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} #{default_args.join(' ')}`
|
|
129
132
|
return false if res.strip.size.zero?
|
|
130
133
|
|
|
131
134
|
res
|
data/lib/doing/string.rb
CHANGED
|
@@ -28,7 +28,9 @@ module Doing
|
|
|
28
28
|
##
|
|
29
29
|
## @return [Regexp] Regex pattern
|
|
30
30
|
##
|
|
31
|
-
def to_rx(distance:
|
|
31
|
+
def to_rx(distance: nil, case_type: nil)
|
|
32
|
+
distance ||= Doing.config.settings.dig('search', 'distance').to_i || 3
|
|
33
|
+
case_type ||= Doing.config.settings.dig('search', 'case')&.normalize_case || :smart
|
|
32
34
|
case_sensitive = case case_type
|
|
33
35
|
when :smart
|
|
34
36
|
self =~ /[A-Z]/ ? true : false
|
|
@@ -44,7 +46,9 @@ module Doing
|
|
|
44
46
|
when /^'/
|
|
45
47
|
sub(/^'(.*?)'?$/, '\1')
|
|
46
48
|
else
|
|
47
|
-
split(/ +/).map
|
|
49
|
+
split(/ +/).map do |w|
|
|
50
|
+
w.split('').join(".{0,#{distance}}").gsub(/\+/, '\+').wildcard_to_rx
|
|
51
|
+
end.join('.*?')
|
|
48
52
|
end
|
|
49
53
|
Regexp.new(pattern, !case_sensitive)
|
|
50
54
|
end
|
|
@@ -72,7 +76,7 @@ module Doing
|
|
|
72
76
|
end
|
|
73
77
|
|
|
74
78
|
## @param (see #highlight_tags)
|
|
75
|
-
def highlight_tags!(color = 'yellow')
|
|
79
|
+
def highlight_tags!(color = 'yellow', last_color: nil)
|
|
76
80
|
replace highlight_tags(color)
|
|
77
81
|
end
|
|
78
82
|
|
|
@@ -83,17 +87,18 @@ module Doing
|
|
|
83
87
|
##
|
|
84
88
|
## @return [String] string with @tags highlighted
|
|
85
89
|
##
|
|
86
|
-
def highlight_tags(color = 'yellow')
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
def highlight_tags(color = 'yellow', last_color: nil)
|
|
91
|
+
unless last_color
|
|
92
|
+
escapes = scan(/(\e\[[\d;]+m)[^\e]+@/)
|
|
93
|
+
color = color.split(' ') unless color.is_a?(Array)
|
|
94
|
+
tag_color = color.each_with_object([]) { |c, arr| arr << Doing::Color.send(c) }.join('')
|
|
95
|
+
last_color = if !escapes.empty?
|
|
96
|
+
(escapes.count > 1 ? escapes[-2..-1] : [escapes[-1]]).map { |v| v[0] }.join('')
|
|
97
|
+
else
|
|
98
|
+
Doing::Color.default
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
gsub(/(\s|m)(@[^ ("']+)/, "\\1#{tag_color}\\2#{last_color}")
|
|
97
102
|
end
|
|
98
103
|
|
|
99
104
|
##
|
|
@@ -192,11 +197,25 @@ module Doing
|
|
|
192
197
|
## @param offset [Integer] (Optional) The width to pad each subsequent line
|
|
193
198
|
## @param prefix [String] (Optional) A prefix to add to each line
|
|
194
199
|
##
|
|
195
|
-
def wrap(len, pad: 0, indent: ' ', offset: 0, prefix: '', color: '', after: '', reset: '')
|
|
200
|
+
def wrap(len, pad: 0, indent: ' ', offset: 0, prefix: '', color: '', after: '', reset: '', pad_first: false)
|
|
196
201
|
last_color = color.empty? ? '' : after.last_color
|
|
197
|
-
note_rx = /(?
|
|
202
|
+
note_rx = /(?mi)(?<!\\)%(?<width>-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?note/
|
|
203
|
+
note = ''
|
|
204
|
+
after = after.dup if after.frozen?
|
|
205
|
+
after.sub!(note_rx) do
|
|
206
|
+
note = Regexp.last_match(0)
|
|
207
|
+
''
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
left_pad = ' ' * offset
|
|
211
|
+
left_pad += indent
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# return "#{left_pad}#{prefix}#{color}#{self}#{last_color} #{note}" unless len.positive?
|
|
215
|
+
|
|
198
216
|
# Don't break inside of tag values
|
|
199
|
-
str = gsub(/@\S+\(.*?\)/) { |tag| tag.gsub(/\s/, '%%%%') }
|
|
217
|
+
str = gsub(/@\S+\(.*?\)/) { |tag| tag.gsub(/\s/, '%%%%') }.gsub(/\n/, ' ')
|
|
218
|
+
|
|
200
219
|
words = str.split(/ /).map { |word| word.gsub(/%%%%/, ' ') }
|
|
201
220
|
out = []
|
|
202
221
|
line = []
|
|
@@ -215,18 +234,18 @@ module Doing
|
|
|
215
234
|
line << word.uncolor
|
|
216
235
|
end
|
|
217
236
|
out.push(line.join(' '))
|
|
218
|
-
note = ''
|
|
219
|
-
after = after.dup if after.frozen?
|
|
220
|
-
after.sub!(note_rx) do
|
|
221
|
-
note = Regexp.last_match(0)
|
|
222
|
-
''
|
|
223
|
-
end
|
|
224
237
|
|
|
238
|
+
last_color = ''
|
|
225
239
|
out[0] = format("%-#{pad}s%s%s", out[0], last_color, after)
|
|
226
240
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
241
|
+
out.map.with_index { |l, idx|
|
|
242
|
+
if !pad_first && idx == 0
|
|
243
|
+
"#{color}#{prefix}#{l}#{last_color}"
|
|
244
|
+
else
|
|
245
|
+
"#{left_pad}#{color}#{prefix}#{l}#{last_color}"
|
|
246
|
+
end
|
|
247
|
+
}.join("\n") + " #{note}".chomp
|
|
248
|
+
# res.join("\n").strip + last_color + " #{note}".chomp
|
|
230
249
|
end
|
|
231
250
|
|
|
232
251
|
##
|
|
@@ -275,7 +294,7 @@ module Doing
|
|
|
275
294
|
|
|
276
295
|
def normalize_case(default = :smart)
|
|
277
296
|
case self
|
|
278
|
-
when /^c/i
|
|
297
|
+
when /^(c|sens)/i
|
|
279
298
|
:sensitive
|
|
280
299
|
when /^i/i
|
|
281
300
|
:ignore
|
|
@@ -310,6 +329,28 @@ module Doing
|
|
|
310
329
|
end
|
|
311
330
|
end
|
|
312
331
|
|
|
332
|
+
##
|
|
333
|
+
## Convert a matching configuration string to a symbol
|
|
334
|
+
##
|
|
335
|
+
## @return Symbol :fuzzy, :pattern, :exact
|
|
336
|
+
##
|
|
337
|
+
def normalize_matching!(default = :pattern)
|
|
338
|
+
replace normalize_bool(default)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def normalize_matching(default = :pattern)
|
|
342
|
+
case self
|
|
343
|
+
when /^f/i
|
|
344
|
+
:fuzzy
|
|
345
|
+
when /^p/i
|
|
346
|
+
:pattern
|
|
347
|
+
when /^e/i
|
|
348
|
+
:exact
|
|
349
|
+
else
|
|
350
|
+
default.is_a?(Symbol) ? default : default.normalize_matching
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
313
354
|
def normalize_trigger!
|
|
314
355
|
replace normalize_trigger
|
|
315
356
|
end
|
data/lib/doing/symbol.rb
CHANGED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doing
|
|
4
|
+
##
|
|
5
|
+
## Template string formatting
|
|
6
|
+
##
|
|
7
|
+
class TemplateString < String
|
|
8
|
+
class ::String
|
|
9
|
+
##
|
|
10
|
+
## Extract the longest valid color from a string.
|
|
11
|
+
##
|
|
12
|
+
## Allows %colors to bleed into other text and still
|
|
13
|
+
## be recognized, e.g. %greensomething still finds
|
|
14
|
+
## %green.
|
|
15
|
+
##
|
|
16
|
+
## @return [String] a valid color name
|
|
17
|
+
## @api private
|
|
18
|
+
def validate_color
|
|
19
|
+
valid_color = nil
|
|
20
|
+
compiled = ''
|
|
21
|
+
split('').each do |char|
|
|
22
|
+
compiled += char
|
|
23
|
+
valid_color = compiled if Color.attributes.include?(compiled.to_sym)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
valid_color
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_reader :original
|
|
31
|
+
|
|
32
|
+
include Color
|
|
33
|
+
def initialize(string, placeholders: {}, force_color: false, wrap_width: 0, color: '', tags_color: '', reset: '')
|
|
34
|
+
Color.coloring = true if force_color
|
|
35
|
+
@colors = nil
|
|
36
|
+
@original = string
|
|
37
|
+
super(Color.reset + string)
|
|
38
|
+
|
|
39
|
+
placeholders.each { |k, v| fill(k, v, wrap_width: wrap_width, color: color, tags_color: tags_color) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
## Test if string contains any valid %colors
|
|
44
|
+
##
|
|
45
|
+
## @return [Boolean] True if colors, False otherwise.
|
|
46
|
+
##
|
|
47
|
+
def colors?
|
|
48
|
+
scan(/%([a-z]+)/).each do
|
|
49
|
+
return true if Regexp.last_match(1).validate_color
|
|
50
|
+
end
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def reparse
|
|
55
|
+
@parsed_colors = nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
## Return string with %colors replaced with escape codes
|
|
60
|
+
##
|
|
61
|
+
## @return [String] colorized string
|
|
62
|
+
##
|
|
63
|
+
def colored
|
|
64
|
+
reparse
|
|
65
|
+
parsed_colors[:string].apply_colors(parsed_colors[:colors])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
## Remove all valid %colors from string
|
|
70
|
+
##
|
|
71
|
+
## @return [String] cleaned string
|
|
72
|
+
##
|
|
73
|
+
def raw
|
|
74
|
+
parsed_colors[:string].uncolor
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def parsed_colors
|
|
78
|
+
@parsed_colors ||= parse_colors
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
## Parse a template string for %colors and return a hash
|
|
83
|
+
## of colors and string locations
|
|
84
|
+
##
|
|
85
|
+
## @return [Hash] Uncolored string and array of colors and locations
|
|
86
|
+
def parse_colors
|
|
87
|
+
working = dup
|
|
88
|
+
color_array = []
|
|
89
|
+
|
|
90
|
+
scan(/(?<!\\)(%([a-z]+))/).each do |color|
|
|
91
|
+
valid_color = color[1].validate_color
|
|
92
|
+
next unless valid_color
|
|
93
|
+
|
|
94
|
+
idx = working.match(/(?<!\\)%#{valid_color}/).begin(0)
|
|
95
|
+
color_array.push({ name: valid_color, color: Color.send(valid_color), index: idx })
|
|
96
|
+
working.sub!(/(?<!\\)%#{valid_color}/, '')
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
{ string: working, colors: color_array }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
## Apply a color array to a string
|
|
104
|
+
##
|
|
105
|
+
## @param color_array [Array] Array of hashes
|
|
106
|
+
## containing :name, :color,
|
|
107
|
+
## :index
|
|
108
|
+
##
|
|
109
|
+
def apply_colors(color_array)
|
|
110
|
+
str = dup
|
|
111
|
+
color_array.reverse.each do |color|
|
|
112
|
+
c = color[:color].empty? ? Color.send(color[:name]) : color[:color]
|
|
113
|
+
str.insert(color[:index], c)
|
|
114
|
+
end
|
|
115
|
+
str
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def fill(placeholder, value, wrap_width: 0, color: '', tags_color: '', reset: '')
|
|
119
|
+
reparse
|
|
120
|
+
rx = /(?mi)(?<!\\)%(?<width>-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?#{placeholder.sub(/^%/, '')}(?<after>.*?)$/
|
|
121
|
+
ph = raw.match(rx)
|
|
122
|
+
|
|
123
|
+
return unless ph
|
|
124
|
+
placeholder_offset = ph.begin(0)
|
|
125
|
+
last_colors = parsed_colors[:colors].select { |v| v[:index] <= placeholder_offset + 4 }
|
|
126
|
+
|
|
127
|
+
last_color = last_colors.map { |v| v[:color] }.pop(3).join('')
|
|
128
|
+
|
|
129
|
+
sub!(rx) do
|
|
130
|
+
m = Regexp.last_match
|
|
131
|
+
|
|
132
|
+
after = m['after']
|
|
133
|
+
|
|
134
|
+
if value.nil? || value.empty?
|
|
135
|
+
after
|
|
136
|
+
else
|
|
137
|
+
pad = m['width'].to_i
|
|
138
|
+
mark = m['mchar'] || ''
|
|
139
|
+
if placeholder == 'shortdate' && m['width'].nil?
|
|
140
|
+
pad = 13
|
|
141
|
+
end
|
|
142
|
+
indent = nil
|
|
143
|
+
if m['ichar']
|
|
144
|
+
char = m['ichar'] =~ /t/ ? "\t" : ' '
|
|
145
|
+
indent = char * m['icount'].to_i
|
|
146
|
+
end
|
|
147
|
+
indent ||= placeholder =~ /^title/ ? '' : "\t"
|
|
148
|
+
prefix = m['prefix']
|
|
149
|
+
if placeholder =~ /^title/
|
|
150
|
+
color = last_color + color
|
|
151
|
+
|
|
152
|
+
if wrap_width.positive? || pad.positive?
|
|
153
|
+
width = pad.positive? ? pad : wrap_width
|
|
154
|
+
|
|
155
|
+
out = value.gsub(/%/, '\%').strip.wrap(width,
|
|
156
|
+
pad: pad,
|
|
157
|
+
indent: indent,
|
|
158
|
+
offset: placeholder_offset,
|
|
159
|
+
prefix: prefix,
|
|
160
|
+
color: color,
|
|
161
|
+
after: after,
|
|
162
|
+
reset: reset,
|
|
163
|
+
pad_first: false)
|
|
164
|
+
out.highlight_tags!(tags_color, last_color: color) if tags_color && !tags_color.empty?
|
|
165
|
+
out
|
|
166
|
+
else
|
|
167
|
+
out = format("%s%s%#{pad}s%s", prefix, color, value.gsub(/%/, '\%').sub(/\s*$/, ''), after)
|
|
168
|
+
out.highlight_tags!(tags_color, last_color: color) if tags_color && !tags_color.empty?
|
|
169
|
+
out
|
|
170
|
+
end
|
|
171
|
+
elsif placeholder =~ /^note/
|
|
172
|
+
if wrap_width.positive? || pad.positive?
|
|
173
|
+
width = pad.positive? ? pad : wrap_width
|
|
174
|
+
outstring = value.map do |l|
|
|
175
|
+
if l.empty?
|
|
176
|
+
' '
|
|
177
|
+
else
|
|
178
|
+
line = l.gsub(/%/, '\%').strip.wrap(width, pad: pad, indent: indent, offset: 0, prefix: prefix, color: last_color, after: after, reset: reset, pad_first: true)
|
|
179
|
+
line.highlight_tags!(tags_color, last_color: last_color) unless tags_color.nil? || tags_color.empty?
|
|
180
|
+
"#{line} "
|
|
181
|
+
end
|
|
182
|
+
end.join("\n")
|
|
183
|
+
"\n#{last_color}#{mark}#{outstring} "
|
|
184
|
+
else
|
|
185
|
+
out = format("\n%s%s%s%#{pad}s%s", indent, prefix, last_color, value.join("\n#{indent}#{prefix}").gsub(/%/, '\%').sub(/\s*$/, ''), after)
|
|
186
|
+
out.highlight_tags!(tags_color, last_color: last_color) if tags_color && !tags_color.empty?
|
|
187
|
+
out
|
|
188
|
+
end
|
|
189
|
+
else
|
|
190
|
+
format("%s%#{pad}s%s", prefix, value.gsub(/%/, '\%').sub(/\s*$/, ''), after)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
@parsed_colors = parse_colors
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
data/lib/doing/util.rb
CHANGED
|
@@ -112,7 +112,7 @@ module Doing
|
|
|
112
112
|
puts content
|
|
113
113
|
return
|
|
114
114
|
end
|
|
115
|
-
|
|
115
|
+
Doing.logger.benchmark(:write_file, :start)
|
|
116
116
|
file = File.expand_path(file)
|
|
117
117
|
|
|
118
118
|
Backup.write_backup(file) if backup
|
|
@@ -121,8 +121,10 @@ module Doing
|
|
|
121
121
|
f.puts content
|
|
122
122
|
Doing.logger.debug('Write:', "File written: #{file}")
|
|
123
123
|
end
|
|
124
|
-
|
|
124
|
+
Doing.logger.benchmark(:_post_write_hook, :start)
|
|
125
125
|
Hooks.trigger :post_write, file
|
|
126
|
+
Doing.logger.benchmark(:_post_write_hook, :finish)
|
|
127
|
+
Doing.logger.benchmark(:write_file, :finish)
|
|
126
128
|
end
|
|
127
129
|
|
|
128
130
|
def safe_load_file(filename)
|