doing 2.1.113 → 2.1.115
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 +11 -0
- data/Gemfile.lock +1 -1
- data/README.md +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 +147 -6
- 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
|
@@ -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,9 +98,8 @@ 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
|
|
@@ -117,9 +117,9 @@ module Doing
|
|
|
117
117
|
if !value.good?
|
|
118
118
|
after
|
|
119
119
|
else
|
|
120
|
-
pad = m['width'].to_i
|
|
120
|
+
pad = m['width'] == '*' ? next_stretch_width(placeholder_name) : m['width'].to_i
|
|
121
121
|
mark = m['mchar'] || ''
|
|
122
|
-
if
|
|
122
|
+
if placeholder_name == 'shortdate' && m['width'].nil?
|
|
123
123
|
fmt_string = Doing.setting('shortdate_format.older', '%m/%d/%y %_I:%M%P', exact: true)
|
|
124
124
|
pad = Date.today.strftime(fmt_string).length
|
|
125
125
|
end
|
|
@@ -142,7 +142,6 @@ module Doing
|
|
|
142
142
|
|
|
143
143
|
if wrap_width.positive? || pad.positive?
|
|
144
144
|
width = pad.positive? ? pad : wrap_width
|
|
145
|
-
|
|
146
145
|
out = value.gsub(/%/, '\%').strip.wrap(width,
|
|
147
146
|
pad: pad,
|
|
148
147
|
indent: indent,
|
|
@@ -184,5 +183,147 @@ module Doing
|
|
|
184
183
|
end
|
|
185
184
|
@parsed_colors = parse_colors
|
|
186
185
|
end
|
|
186
|
+
|
|
187
|
+
private
|
|
188
|
+
|
|
189
|
+
def stretch_widths(placeholders)
|
|
190
|
+
keys = placeholders.keys.map { |k| Regexp.escape(k.sub(/^%/, '')) }.sort_by(&:length).reverse
|
|
191
|
+
return {} if keys.empty?
|
|
192
|
+
|
|
193
|
+
token_rx = /(?<!\\)%(?<width>\*|-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?(?<name>#{keys.join('|')})/
|
|
194
|
+
queues = Hash.new { |h, k| h[k] = [] }
|
|
195
|
+
terminal_width = detected_terminal_width
|
|
196
|
+
raw.each_line do |line|
|
|
197
|
+
tokens = line.to_enum(:scan, token_rx).map { Regexp.last_match.dup }
|
|
198
|
+
next if tokens.empty?
|
|
199
|
+
|
|
200
|
+
literal_width = visible_literal_width(line.gsub(token_rx, ''))
|
|
201
|
+
reserved_width = literal_width
|
|
202
|
+
stretch_tokens = []
|
|
203
|
+
|
|
204
|
+
tokens.each do |token|
|
|
205
|
+
width_token = token['width']
|
|
206
|
+
name = token['name']
|
|
207
|
+
value = placeholders[name] || placeholders["%#{name}"]
|
|
208
|
+
natural = natural_placeholder_width(name, value)
|
|
209
|
+
if width_token == '*'
|
|
210
|
+
if block_placeholder?(name)
|
|
211
|
+
queues[name] << block_placeholder_width(terminal_width, token)
|
|
212
|
+
else
|
|
213
|
+
stretch_tokens << token
|
|
214
|
+
end
|
|
215
|
+
else
|
|
216
|
+
reserved_width += reserved_placeholder_width(width_token, natural)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
next if stretch_tokens.empty?
|
|
221
|
+
|
|
222
|
+
remaining = terminal_width - reserved_width
|
|
223
|
+
widths = split_stretch_widths(remaining, stretch_tokens.length)
|
|
224
|
+
stretch_tokens.each_with_index do |token, idx|
|
|
225
|
+
queues[token['name']] << widths[idx]
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
queues
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def split_stretch_widths(remaining, count)
|
|
233
|
+
return [] if count.zero?
|
|
234
|
+
return Array.new(count, 1) if remaining < count
|
|
235
|
+
|
|
236
|
+
base = remaining / count
|
|
237
|
+
extra = remaining % count
|
|
238
|
+
Array.new(count) { |idx| base + (idx < extra ? 1 : 0) }
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def natural_placeholder_width(name, value)
|
|
242
|
+
# note placeholders are rendered on their own wrapped lines and should not
|
|
243
|
+
# reserve horizontal width on the title line
|
|
244
|
+
return 0 if block_placeholder?(name)
|
|
245
|
+
return 0 unless value.good?
|
|
246
|
+
|
|
247
|
+
normalized = if name =~ /^tags/ && value.respond_to?(:map)
|
|
248
|
+
value.map(&:to_s).join(' ')
|
|
249
|
+
elsif value.respond_to?(:join)
|
|
250
|
+
value.join("\n")
|
|
251
|
+
else
|
|
252
|
+
value.to_s
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
normalized.split("\n").map { |line| visible_literal_width(line) }.max || 0
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def reserved_placeholder_width(width_token, natural_width)
|
|
259
|
+
return natural_width if width_token.nil? || width_token.empty?
|
|
260
|
+
|
|
261
|
+
minimum = width_token.to_i.abs
|
|
262
|
+
[natural_width, minimum].max
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def block_placeholder?(name)
|
|
266
|
+
%w[note idnote odnote].include?(name)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def block_placeholder_width(terminal_width, token)
|
|
270
|
+
padding = block_placeholder_padding(token)
|
|
271
|
+
[terminal_width - padding, 1].max
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def block_placeholder_padding(token)
|
|
275
|
+
indent = if token['ichar']
|
|
276
|
+
char = token['ichar'] =~ /t/ ? "\t" : ' '
|
|
277
|
+
char * token['icount'].to_i
|
|
278
|
+
else
|
|
279
|
+
"\t"
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
visible_literal_width("#{indent}#{token['prefix']}")
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def next_stretch_width(name)
|
|
286
|
+
widths = @stretch_widths[name]
|
|
287
|
+
return 1 if widths.nil? || widths.empty?
|
|
288
|
+
|
|
289
|
+
[widths.shift.to_i, 1].max
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def visible_literal_width(string)
|
|
293
|
+
visible = strip_template_colors(string).gsub(/\\(%)/, '\1')
|
|
294
|
+
visible.uncolor.length
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def detected_terminal_width
|
|
298
|
+
if $stdout.tty?
|
|
299
|
+
begin
|
|
300
|
+
require 'io/console'
|
|
301
|
+
console = IO.console
|
|
302
|
+
width = console.winsize[1].to_i if console
|
|
303
|
+
return width if width&.positive?
|
|
304
|
+
rescue StandardError
|
|
305
|
+
# Fall through to tty-screen detection.
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
tty_width = TTY::Screen.columns.to_i
|
|
310
|
+
return tty_width if tty_width.positive?
|
|
311
|
+
|
|
312
|
+
env_width = ENV['COLUMNS'].to_i
|
|
313
|
+
return env_width if env_width.positive?
|
|
314
|
+
|
|
315
|
+
80
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def strip_template_colors(string)
|
|
319
|
+
working = string.dup
|
|
320
|
+
string.scan(/(?<!\\)(%((?:[fb]g?)?#[a-fA-F0-9]{6}|[a-z]+))/).each do |color|
|
|
321
|
+
valid_color = color[1].validate_color
|
|
322
|
+
next unless valid_color
|
|
323
|
+
|
|
324
|
+
working.sub!(/(?<!\\)%#{valid_color}/, '')
|
|
325
|
+
end
|
|
326
|
+
working
|
|
327
|
+
end
|
|
187
328
|
end
|
|
188
329
|
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:
|