doing 2.1.113 → 2.1.117
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/CHANGELOG.md +20 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/docs/doc/Array.html +1 -1
- data/docs/doc/BooleanTermParser/Clause.html +1 -1
- data/docs/doc/BooleanTermParser/Operator.html +1 -1
- data/docs/doc/BooleanTermParser/Query.html +1 -1
- data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
- data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/ArrayCleanup.html +1 -1
- data/docs/doc/Doing/ArrayNestedHash.html +1 -1
- data/docs/doc/Doing/ArrayTags.html +1 -1
- data/docs/doc/Doing/ByDayExport.html +1 -1
- data/docs/doc/Doing/CSVExport.html +1 -1
- data/docs/doc/Doing/CalendarImport.html +1 -1
- data/docs/doc/Doing/Change.html +1 -1
- data/docs/doc/Doing/Changes.html +1 -1
- data/docs/doc/Doing/ChronifyArray.html +1 -1
- data/docs/doc/Doing/ChronifyNumeric.html +1 -1
- data/docs/doc/Doing/ChronifyString.html +1 -1
- data/docs/doc/Doing/Color.html +1 -1
- data/docs/doc/Doing/Completion/BashCompletions.html +1 -1
- data/docs/doc/Doing/Completion/FigCompletions.html +1 -1
- data/docs/doc/Doing/Completion/FishCompletions.html +1 -1
- data/docs/doc/Doing/Completion/StringUtils.html +1 -1
- data/docs/doc/Doing/Completion/ZshCompletions.html +1 -1
- data/docs/doc/Doing/Completion.html +1 -1
- data/docs/doc/Doing/Configuration.html +1 -1
- data/docs/doc/Doing/DayOneRenderer.html +1 -1
- data/docs/doc/Doing/DayoneExport.html +1 -1
- data/docs/doc/Doing/DoingExport.html +1 -1
- data/docs/doc/Doing/DoingImport.html +1 -1
- data/docs/doc/Doing/Entry.html +1 -1
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
- data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
- data/docs/doc/Doing/Errors/HistoryLimitError.html +1 -1
- data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
- data/docs/doc/Doing/Errors/MissingBackupFile.html +1 -1
- data/docs/doc/Doing/Errors/NoResults.html +1 -1
- data/docs/doc/Doing/Errors/PluginException.html +1 -1
- data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
- data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/HTMLExport.html +1 -1
- data/docs/doc/Doing/Hooks.html +1 -1
- data/docs/doc/Doing/Item.html +1 -1
- data/docs/doc/Doing/ItemDates.html +1 -1
- data/docs/doc/Doing/ItemQuery.html +1 -1
- data/docs/doc/Doing/ItemState.html +1 -1
- data/docs/doc/Doing/ItemTags.html +1 -1
- data/docs/doc/Doing/Items.html +1 -1
- data/docs/doc/Doing/JSONExport.html +1 -1
- data/docs/doc/Doing/JSONImport.html +1 -1
- data/docs/doc/Doing/Logger.html +1 -1
- data/docs/doc/Doing/MarkdownExport.html +1 -1
- data/docs/doc/Doing/Note.html +1 -1
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +1 -1
- data/docs/doc/Doing/Prompt.html +1 -1
- data/docs/doc/Doing/PromptChoose.html +1 -1
- data/docs/doc/Doing/PromptFZF.html +1 -1
- data/docs/doc/Doing/PromptInput.html +1 -1
- data/docs/doc/Doing/PromptSTD.html +1 -1
- data/docs/doc/Doing/PromptYN.html +1 -1
- data/docs/doc/Doing/Section.html +1 -1
- data/docs/doc/Doing/StringHighlight.html +1 -1
- data/docs/doc/Doing/StringNormalize.html +1 -1
- data/docs/doc/Doing/StringQuery.html +1 -1
- data/docs/doc/Doing/StringTags.html +1 -1
- data/docs/doc/Doing/StringTransform.html +1 -1
- data/docs/doc/Doing/StringTruncate.html +1 -1
- data/docs/doc/Doing/StringURL.html +1 -1
- data/docs/doc/Doing/SymbolNormalize.html +1 -1
- data/docs/doc/Doing/TaskPaperExport.html +1 -1
- data/docs/doc/Doing/TemplateExport.html +1 -1
- data/docs/doc/Doing/TemplateString.html +1 -1
- data/docs/doc/Doing/TimingImport.html +1 -1
- data/docs/doc/Doing/Types.html +1 -1
- data/docs/doc/Doing/Util/Backup.html +1 -1
- data/docs/doc/Doing/Util.html +1 -1
- data/docs/doc/Doing/Version.html +1 -1
- data/docs/doc/Doing/WWID.html +18 -2
- data/docs/doc/Doing.html +2 -2
- data/docs/doc/FalseClass.html +1 -1
- data/docs/doc/GLI/Commands/Help.html +1 -1
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/docs/doc/GLI/Commands.html +1 -1
- data/docs/doc/GLI.html +1 -1
- data/docs/doc/Hash.html +1 -1
- data/docs/doc/Numeric.html +1 -1
- data/docs/doc/Object.html +1 -1
- data/docs/doc/PhraseParser/Operator.html +1 -1
- data/docs/doc/PhraseParser/PhraseClause.html +1 -1
- data/docs/doc/PhraseParser/Query.html +1 -1
- data/docs/doc/PhraseParser/QueryParser.html +1 -1
- data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
- data/docs/doc/PhraseParser/TermClause.html +1 -1
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +1 -1
- data/docs/doc/String.html +1 -1
- data/docs/doc/Symbol.html +1 -1
- data/docs/doc/Time.html +1 -1
- data/docs/doc/TrueClass.html +1 -1
- data/docs/doc/_index.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/top-level-namespace.html +1 -1
- data/doing.rdoc +1 -1
- data/lib/doing/plugins/export/template_export.rb +1 -1
- data/lib/doing/string/transform.rb +1 -1
- data/lib/doing/template_string.rb +149 -7
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid/display.rb +45 -2
- data/man/doing.1.ronn +2 -0
- metadata +1 -1
|
@@ -46,7 +46,7 @@ module Doing
|
|
|
46
46
|
##
|
|
47
47
|
def wrap(len, pad: 0, indent: ' ', offset: 0, prefix: '', color: '', after: '', reset: '', pad_first: false)
|
|
48
48
|
color.empty? ? '' : after.last_color
|
|
49
|
-
note_rx = /(?mi)(?<!\\)%(?<width
|
|
49
|
+
note_rx = /(?mi)(?<!\\)%(?<width>\*|-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?note/
|
|
50
50
|
note = ''
|
|
51
51
|
after = after.dup if after.frozen?
|
|
52
52
|
after.sub!(note_rx) do
|
|
@@ -15,6 +15,7 @@ module Doing
|
|
|
15
15
|
@original = string
|
|
16
16
|
super(Doing::Color.coloring? ? Color.reset + string : string)
|
|
17
17
|
|
|
18
|
+
@stretch_widths = stretch_widths(placeholders)
|
|
18
19
|
placeholders.each { |k, v| fill(k, v, wrap_width: wrap_width, color: color, tags_color: tags_color) }
|
|
19
20
|
end
|
|
20
21
|
|
|
@@ -97,15 +98,15 @@ module Doing
|
|
|
97
98
|
|
|
98
99
|
def fill(placeholder, value, wrap_width: 0, color: '', tags_color: '', reset: '')
|
|
99
100
|
reparse
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)}(?<after>.*?)$/
|
|
101
|
+
placeholder_name = placeholder.sub(/^%/, '')
|
|
102
|
+
rx = /(?mi)(?<!\\)%(?<width>\*|-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?#{placeholder_name}(?<after>.*?)$/
|
|
103
103
|
ph = raw.match(rx)
|
|
104
104
|
|
|
105
105
|
return unless ph
|
|
106
106
|
|
|
107
107
|
placeholder_offset = ph.begin(0)
|
|
108
|
-
|
|
108
|
+
color_offset = parsed_colors[:string].match(rx)&.begin(0) || placeholder_offset
|
|
109
|
+
last_colors = parsed_colors[:colors].select { |v| v[:index] <= color_offset + 4 }
|
|
109
110
|
|
|
110
111
|
last_color = last_colors.map { |v| v[:color] }.pop(3).join('')
|
|
111
112
|
|
|
@@ -117,9 +118,9 @@ module Doing
|
|
|
117
118
|
if !value.good?
|
|
118
119
|
after
|
|
119
120
|
else
|
|
120
|
-
pad = m['width'].to_i
|
|
121
|
+
pad = m['width'] == '*' ? next_stretch_width(placeholder_name) : m['width'].to_i
|
|
121
122
|
mark = m['mchar'] || ''
|
|
122
|
-
if
|
|
123
|
+
if placeholder_name == 'shortdate' && m['width'].nil?
|
|
123
124
|
fmt_string = Doing.setting('shortdate_format.older', '%m/%d/%y %_I:%M%P', exact: true)
|
|
124
125
|
pad = Date.today.strftime(fmt_string).length
|
|
125
126
|
end
|
|
@@ -142,7 +143,6 @@ module Doing
|
|
|
142
143
|
|
|
143
144
|
if wrap_width.positive? || pad.positive?
|
|
144
145
|
width = pad.positive? ? pad : wrap_width
|
|
145
|
-
|
|
146
146
|
out = value.gsub(/%/, '\%').strip.wrap(width,
|
|
147
147
|
pad: pad,
|
|
148
148
|
indent: indent,
|
|
@@ -184,5 +184,147 @@ module Doing
|
|
|
184
184
|
end
|
|
185
185
|
@parsed_colors = parse_colors
|
|
186
186
|
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
def stretch_widths(placeholders)
|
|
191
|
+
keys = placeholders.keys.map { |k| Regexp.escape(k.sub(/^%/, '')) }.sort_by(&:length).reverse
|
|
192
|
+
return {} if keys.empty?
|
|
193
|
+
|
|
194
|
+
token_rx = /(?<!\\)%(?<width>\*|-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?(?<name>#{keys.join('|')})/
|
|
195
|
+
queues = Hash.new { |h, k| h[k] = [] }
|
|
196
|
+
terminal_width = detected_terminal_width
|
|
197
|
+
raw.each_line do |line|
|
|
198
|
+
tokens = line.to_enum(:scan, token_rx).map { Regexp.last_match.dup }
|
|
199
|
+
next if tokens.empty?
|
|
200
|
+
|
|
201
|
+
literal_width = visible_literal_width(line.gsub(token_rx, ''))
|
|
202
|
+
reserved_width = literal_width
|
|
203
|
+
stretch_tokens = []
|
|
204
|
+
|
|
205
|
+
tokens.each do |token|
|
|
206
|
+
width_token = token['width']
|
|
207
|
+
name = token['name']
|
|
208
|
+
value = placeholders[name] || placeholders["%#{name}"]
|
|
209
|
+
natural = natural_placeholder_width(name, value)
|
|
210
|
+
if width_token == '*'
|
|
211
|
+
if block_placeholder?(name)
|
|
212
|
+
queues[name] << block_placeholder_width(terminal_width, token)
|
|
213
|
+
else
|
|
214
|
+
stretch_tokens << token
|
|
215
|
+
end
|
|
216
|
+
else
|
|
217
|
+
reserved_width += reserved_placeholder_width(width_token, natural)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
next if stretch_tokens.empty?
|
|
222
|
+
|
|
223
|
+
remaining = terminal_width - reserved_width
|
|
224
|
+
widths = split_stretch_widths(remaining, stretch_tokens.length)
|
|
225
|
+
stretch_tokens.each_with_index do |token, idx|
|
|
226
|
+
queues[token['name']] << widths[idx]
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
queues
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def split_stretch_widths(remaining, count)
|
|
234
|
+
return [] if count.zero?
|
|
235
|
+
return Array.new(count, 1) if remaining < count
|
|
236
|
+
|
|
237
|
+
base = remaining / count
|
|
238
|
+
extra = remaining % count
|
|
239
|
+
Array.new(count) { |idx| base + (idx < extra ? 1 : 0) }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def natural_placeholder_width(name, value)
|
|
243
|
+
# note placeholders are rendered on their own wrapped lines and should not
|
|
244
|
+
# reserve horizontal width on the title line
|
|
245
|
+
return 0 if block_placeholder?(name)
|
|
246
|
+
return 0 unless value.good?
|
|
247
|
+
|
|
248
|
+
normalized = if name =~ /^tags/ && value.respond_to?(:map)
|
|
249
|
+
value.map(&:to_s).join(' ')
|
|
250
|
+
elsif value.respond_to?(:join)
|
|
251
|
+
value.join("\n")
|
|
252
|
+
else
|
|
253
|
+
value.to_s
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
normalized.split("\n").map { |line| visible_literal_width(line) }.max || 0
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def reserved_placeholder_width(width_token, natural_width)
|
|
260
|
+
return natural_width if width_token.nil? || width_token.empty?
|
|
261
|
+
|
|
262
|
+
minimum = width_token.to_i.abs
|
|
263
|
+
[natural_width, minimum].max
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def block_placeholder?(name)
|
|
267
|
+
%w[note idnote odnote].include?(name)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def block_placeholder_width(terminal_width, token)
|
|
271
|
+
padding = block_placeholder_padding(token)
|
|
272
|
+
[terminal_width - padding, 1].max
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def block_placeholder_padding(token)
|
|
276
|
+
indent = if token['ichar']
|
|
277
|
+
char = token['ichar'] =~ /t/ ? "\t" : ' '
|
|
278
|
+
char * token['icount'].to_i
|
|
279
|
+
else
|
|
280
|
+
"\t"
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
visible_literal_width("#{indent}#{token['prefix']}")
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def next_stretch_width(name)
|
|
287
|
+
widths = @stretch_widths[name]
|
|
288
|
+
return 1 if widths.nil? || widths.empty?
|
|
289
|
+
|
|
290
|
+
[widths.shift.to_i, 1].max
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def visible_literal_width(string)
|
|
294
|
+
visible = strip_template_colors(string).gsub(/\\(%)/, '\1')
|
|
295
|
+
visible.uncolor.length
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def detected_terminal_width
|
|
299
|
+
if $stdout.tty?
|
|
300
|
+
begin
|
|
301
|
+
require 'io/console'
|
|
302
|
+
console = IO.console
|
|
303
|
+
width = console.winsize[1].to_i if console
|
|
304
|
+
return width if width&.positive?
|
|
305
|
+
rescue StandardError
|
|
306
|
+
# Fall through to tty-screen detection.
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
tty_width = TTY::Screen.columns.to_i
|
|
311
|
+
return tty_width if tty_width.positive?
|
|
312
|
+
|
|
313
|
+
env_width = ENV['COLUMNS'].to_i
|
|
314
|
+
return env_width if env_width.positive?
|
|
315
|
+
|
|
316
|
+
80
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def strip_template_colors(string)
|
|
320
|
+
working = string.dup
|
|
321
|
+
string.scan(/(?<!\\)(%((?:[fb]g?)?#[a-fA-F0-9]{6}|[a-z]+))/).each do |color|
|
|
322
|
+
valid_color = color[1].validate_color
|
|
323
|
+
next unless valid_color
|
|
324
|
+
|
|
325
|
+
working.sub!(/(?<!\\)%#{valid_color}/, '')
|
|
326
|
+
end
|
|
327
|
+
working
|
|
328
|
+
end
|
|
187
329
|
end
|
|
188
330
|
end
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid/display.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Doing
|
|
4
4
|
class WWID
|
|
5
|
+
TEMPLATE_WIDTH_PLACEHOLDERS = %w[id tags date interval duration shortdate section title note idnote odnote chompnote].freeze
|
|
5
6
|
##
|
|
6
7
|
## Display contents of a section based on options
|
|
7
8
|
##
|
|
@@ -43,7 +44,7 @@ module Doing
|
|
|
43
44
|
opt[:order] ||= cfg['order'] || :asc
|
|
44
45
|
opt[:tag_order] ||= :asc
|
|
45
46
|
opt[:tags_color] = cfg['tags_color'] || false if opt[:tags_color].nil?
|
|
46
|
-
opt[:template] ||= cfg
|
|
47
|
+
opt[:template] ||= resolved_template(cfg)
|
|
47
48
|
opt[:sort_tags] ||= opt[:tag_sort]
|
|
48
49
|
end
|
|
49
50
|
|
|
@@ -263,7 +264,7 @@ module Doing
|
|
|
263
264
|
opt[:wrap_width] = cfg['wrap_width']
|
|
264
265
|
opt[:count] = count
|
|
265
266
|
opt[:format] = cfg['date_format']
|
|
266
|
-
opt[:template] = opt[:template] || cfg
|
|
267
|
+
opt[:template] = opt[:template] || resolved_template(cfg)
|
|
267
268
|
opt[:order] = :asc
|
|
268
269
|
end
|
|
269
270
|
|
|
@@ -372,6 +373,48 @@ module Doing
|
|
|
372
373
|
|
|
373
374
|
private
|
|
374
375
|
|
|
376
|
+
def resolved_template(cfg)
|
|
377
|
+
template = cfg['template']
|
|
378
|
+
return template unless Doing.setting('template_version', 1).to_i >= 2
|
|
379
|
+
|
|
380
|
+
placeholders = cfg['placeholders']
|
|
381
|
+
return template unless placeholders.is_a?(Hash)
|
|
382
|
+
|
|
383
|
+
apply_placeholder_widths(template, placeholders)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def apply_placeholder_widths(template, placeholders)
|
|
387
|
+
keys = TEMPLATE_WIDTH_PLACEHOLDERS.map { |k| Regexp.escape(k) }.sort_by(&:length).reverse
|
|
388
|
+
token_rx = /(?<!\\)%(?<width>\*|-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?(?<name>#{keys.join('|')})/i
|
|
389
|
+
|
|
390
|
+
template.gsub(token_rx) do
|
|
391
|
+
m = Regexp.last_match
|
|
392
|
+
name = m['name']
|
|
393
|
+
|
|
394
|
+
width = normalized_placeholder_width(placeholders[name] || placeholders[name.to_sym])
|
|
395
|
+
mchar = m['mchar'] ? "^#{m['mchar']}" : ''
|
|
396
|
+
indent = m['ichar'] ? "#{m['ichar']}#{m['icount']}" : ''
|
|
397
|
+
prefix = m['prefix'] || ''
|
|
398
|
+
|
|
399
|
+
"%#{width}#{mchar}#{indent}#{prefix}#{name}"
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def normalized_placeholder_width(config)
|
|
404
|
+
width = config['width'] || config[:width] if config.is_a?(Hash)
|
|
405
|
+
|
|
406
|
+
return '' if width.nil?
|
|
407
|
+
|
|
408
|
+
case width.to_s
|
|
409
|
+
when /^stretch$/i
|
|
410
|
+
'*'
|
|
411
|
+
when /^auto$/i, /^$/
|
|
412
|
+
''
|
|
413
|
+
else
|
|
414
|
+
width.to_i.to_s
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
375
418
|
##
|
|
376
419
|
## Generate output using available export plugins
|
|
377
420
|
##
|
data/man/doing.1.ronn
CHANGED
|
@@ -199,6 +199,8 @@ The config also contains templates for various command outputs. Include placehol
|
|
|
199
199
|
|
|
200
200
|
Date formats are based on Ruby [`strftime`](http://www.ruby-doc.org/stdlib-2.1.1/libdoc/date/rdoc/Date.html#method-i-strftime) formatting.
|
|
201
201
|
|
|
202
|
+
`%title` and `%note` placeholders accept optional width formatting. Numeric width (for example `%60title`) wraps to that width. Negative width applies a minimum padded width. You can also use `*` as a stretch width marker (for example `%*title`), which expands that placeholder to fill remaining space on the template line based on terminal columns. Stretch width now accounts for the rendered minimum width of trailing fixed placeholders, so `%*title` expands up to where right-side content begins and that trailing content can sit at the right edge. Placeholders without any width marker keep natural rendered width and do not expand. When multiple `%*...` placeholders are on one line, remaining width is split evenly from left to right.
|
|
203
|
+
|
|
202
204
|
My normal template for the `recent` command looks like this:
|
|
203
205
|
|
|
204
206
|
recent:
|