doing 2.1.3 → 2.1.6
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 +19 -15
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/.yardopts +1 -1
- data/CHANGELOG.md +48 -0
- data/Gemfile.lock +25 -1
- data/README.md +5 -1
- data/bin/doing +429 -142
- 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 +157 -11
- 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 +134 -73
- data/{doc → docs/doc}/Doing/Items.html +36 -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 +35 -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 +16 -4
- data/{doc → docs/doc}/Doing/WWID.html +133 -73
- 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 +285 -13
- data/{doc → docs/doc}/Symbol.html +35 -1
- data/{doc → docs/doc}/Time.html +70 -2
- data/{doc → docs/doc}/_index.html +132 -4
- data/docs/doc/class_list.html +51 -0
- 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 +624 -136
- data/{doc → docs/doc}/top-level-namespace.html +2 -2
- data/docs/index.md +60 -0
- data/doing.gemspec +3 -0
- data/doing.rdoc +222 -74
- 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/boolean_term_parser.rb +86 -0
- data/lib/doing/completion/fish_completion.rb +46 -3
- data/lib/doing/completion/zsh_completion.rb +1 -1
- data/lib/doing/configuration.rb +45 -14
- data/lib/doing/item.rb +104 -9
- data/lib/doing/items.rb +6 -0
- data/lib/doing/log_adapter.rb +28 -0
- data/lib/doing/note.rb +31 -30
- data/lib/doing/phrase_parser.rb +124 -0
- 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 +17 -6
- data/lib/doing/string.rb +84 -29
- data/lib/doing/string_chronify.rb +5 -1
- data/lib/doing/symbol.rb +4 -0
- data/lib/doing/template_string.rb +197 -0
- data/lib/doing/time.rb +32 -0
- data/lib/doing/util.rb +6 -7
- data/lib/doing/util_backup.rb +287 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +105 -41
- data/lib/doing.rb +9 -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 +136 -51
- data/doc/class_list.html +0 -51
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
|
##
|
|
@@ -240,6 +259,10 @@ module Doing
|
|
|
240
259
|
end
|
|
241
260
|
end
|
|
242
261
|
|
|
262
|
+
def pluralize(number)
|
|
263
|
+
number == 1 ? self : "#{self}s"
|
|
264
|
+
end
|
|
265
|
+
|
|
243
266
|
##
|
|
244
267
|
## Convert a sort order string to a qualified type
|
|
245
268
|
##
|
|
@@ -271,7 +294,7 @@ module Doing
|
|
|
271
294
|
|
|
272
295
|
def normalize_case(default = :smart)
|
|
273
296
|
case self
|
|
274
|
-
when /^c/i
|
|
297
|
+
when /^(c|sens)/i
|
|
275
298
|
:sensitive
|
|
276
299
|
when /^i/i
|
|
277
300
|
:ignore
|
|
@@ -299,11 +322,35 @@ module Doing
|
|
|
299
322
|
:or
|
|
300
323
|
when /(not|none)/i
|
|
301
324
|
:not
|
|
325
|
+
when /^p/i
|
|
326
|
+
:pattern
|
|
302
327
|
else
|
|
303
328
|
default.is_a?(Symbol) ? default : default.normalize_bool
|
|
304
329
|
end
|
|
305
330
|
end
|
|
306
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
|
+
|
|
307
354
|
def normalize_trigger!
|
|
308
355
|
replace normalize_trigger
|
|
309
356
|
end
|
|
@@ -312,8 +359,16 @@ module Doing
|
|
|
312
359
|
gsub(/\((?!\?:)/, '(?:').downcase
|
|
313
360
|
end
|
|
314
361
|
|
|
362
|
+
def wildcard_to_rx
|
|
363
|
+
gsub(/\?/, '\S').gsub(/\*/, '\S*?')
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def add_at
|
|
367
|
+
strip.sub(/^([+-]*)@/, '\1')
|
|
368
|
+
end
|
|
369
|
+
|
|
315
370
|
def to_tags
|
|
316
|
-
gsub(/ *, */, ' ').gsub(/ +/, ' ').split(/ /).sort.uniq.map
|
|
371
|
+
gsub(/ *, */, ' ').gsub(/ +/, ' ').split(/ /).sort.uniq.map(&:add_at)
|
|
317
372
|
end
|
|
318
373
|
|
|
319
374
|
def add_tags!(tags, remove: false)
|
|
@@ -533,7 +588,7 @@ module Doing
|
|
|
533
588
|
end
|
|
534
589
|
else
|
|
535
590
|
case self
|
|
536
|
-
when / *,
|
|
591
|
+
when /(^\[.*?\]$| *, *)/
|
|
537
592
|
gsub(/^\[ *| *\]$/, '').split(/ *, */)
|
|
538
593
|
when /^[0-9]+$/
|
|
539
594
|
to_i
|
|
@@ -41,7 +41,11 @@ module Doing
|
|
|
41
41
|
if secs_ago
|
|
42
42
|
now - secs_ago
|
|
43
43
|
else
|
|
44
|
-
Chronic.parse(self, {
|
|
44
|
+
Chronic.parse(self, {
|
|
45
|
+
guess: options.fetch(:guess, :begin),
|
|
46
|
+
context: options.fetch(:future, false) ? :future : :past,
|
|
47
|
+
ambiguous_time_range: 8
|
|
48
|
+
})
|
|
45
49
|
end
|
|
46
50
|
end
|
|
47
51
|
|
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/time.rb
CHANGED
|
@@ -14,5 +14,37 @@ module Doing
|
|
|
14
14
|
strftime('%m/%d/%Y %_I:%M%P')
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
def humanize(seconds)
|
|
19
|
+
s = seconds
|
|
20
|
+
m = (s / 60).floor
|
|
21
|
+
s = (s % 60).floor
|
|
22
|
+
h = (m / 60).floor
|
|
23
|
+
m = (m % 60).floor
|
|
24
|
+
d = (h / 24).floor
|
|
25
|
+
h = h % 24
|
|
26
|
+
|
|
27
|
+
output = []
|
|
28
|
+
output.push("#{d} #{'day'.pluralize(d)}") if d.positive?
|
|
29
|
+
output.push("#{h} #{'hour'.pluralize(h)}") if h.positive?
|
|
30
|
+
output.push("#{m} #{'minute'.pluralize(m)}") if m.positive?
|
|
31
|
+
output.push("#{s} #{'second'.pluralize(s)}") if s.positive?
|
|
32
|
+
output.join(', ')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def time_ago
|
|
36
|
+
if self > Date.today.to_time
|
|
37
|
+
output = humanize(Time.now - self)
|
|
38
|
+
"#{output} ago"
|
|
39
|
+
elsif self > (Date.today - 1).to_time
|
|
40
|
+
"Yesterday at #{strftime('%_I:%M:%S%P')}"
|
|
41
|
+
elsif self > (Date.today - 6).to_time
|
|
42
|
+
strftime('%a %I:%M:%S%P')
|
|
43
|
+
elsif self.year == Date.today.year
|
|
44
|
+
strftime('%m/%d %I:%M:%S%P')
|
|
45
|
+
else
|
|
46
|
+
strftime('%m/%d/%Y %I:%M:%S%P')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
17
49
|
end
|
|
18
50
|
end
|
data/lib/doing/util.rb
CHANGED
|
@@ -112,20 +112,19 @@ 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
|
-
|
|
119
|
-
# Create a backup copy for the undo command
|
|
120
|
-
FileUtils.cp(file, "#{file}~")
|
|
121
|
-
end
|
|
118
|
+
Backup.write_backup(file) if backup
|
|
122
119
|
|
|
123
120
|
File.open(file, 'w+') do |f|
|
|
124
121
|
f.puts content
|
|
125
122
|
Doing.logger.debug('Write:', "File written: #{file}")
|
|
126
123
|
end
|
|
127
|
-
|
|
124
|
+
Doing.logger.benchmark(:_post_write_hook, :start)
|
|
128
125
|
Hooks.trigger :post_write, file
|
|
126
|
+
Doing.logger.benchmark(:_post_write_hook, :finish)
|
|
127
|
+
Doing.logger.benchmark(:write_file, :finish)
|
|
129
128
|
end
|
|
130
129
|
|
|
131
130
|
def safe_load_file(filename)
|
|
@@ -133,7 +132,7 @@ module Doing
|
|
|
133
132
|
end
|
|
134
133
|
|
|
135
134
|
def default_editor
|
|
136
|
-
@default_editor
|
|
135
|
+
@default_editor ||= find_default_editor
|
|
137
136
|
end
|
|
138
137
|
|
|
139
138
|
def editor_with_args
|