doing 2.1.29 → 2.1.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/.irbrc +1 -0
  3. data/CHANGELOG.md +4962 -0
  4. data/Dockerfile-2.6 +3 -1
  5. data/Dockerfile-2.7 +4 -2
  6. data/Dockerfile-3.0 +3 -1
  7. data/Gemfile.lock +2 -67
  8. data/README.md +1 -1
  9. data/bash_profile +13 -0
  10. data/bin/commands/again.rb +1 -1
  11. data/bin/commands/archive.rb +3 -3
  12. data/bin/commands/cancel.rb +1 -1
  13. data/bin/commands/changes.rb +2 -2
  14. data/bin/commands/commands.rb +8 -8
  15. data/bin/commands/completion.rb +61 -19
  16. data/bin/commands/config.rb +20 -17
  17. data/bin/commands/done.rb +1 -1
  18. data/bin/commands/flag.rb +1 -1
  19. data/bin/commands/grep.rb +5 -5
  20. data/bin/commands/last.rb +1 -1
  21. data/bin/commands/meanwhile.rb +1 -1
  22. data/bin/commands/now.rb +1 -1
  23. data/bin/commands/on.rb +1 -1
  24. data/bin/commands/open.rb +4 -4
  25. data/bin/commands/recent.rb +4 -4
  26. data/bin/commands/show.rb +8 -8
  27. data/bin/commands/since.rb +1 -1
  28. data/bin/commands/tag_dir.rb +27 -3
  29. data/bin/commands/today.rb +1 -1
  30. data/bin/commands/view.rb +3 -3
  31. data/bin/commands/yesterday.rb +2 -2
  32. data/bin/doing +26 -135
  33. data/docs/doc/Array.html +1 -1
  34. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  35. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  36. data/docs/doc/BooleanTermParser/Query.html +1 -1
  37. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  38. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  39. data/docs/doc/BooleanTermParser.html +1 -1
  40. data/docs/doc/Doing/Color.html +1 -1
  41. data/docs/doc/Doing/Completion.html +324 -4
  42. data/docs/doc/Doing/Configuration.html +3 -3
  43. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  44. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  45. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  46. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  47. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  48. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  49. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  50. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  51. data/docs/doc/Doing/Errors.html +1 -1
  52. data/docs/doc/Doing/Hooks.html +1 -1
  53. data/docs/doc/Doing/Item.html +125 -1
  54. data/docs/doc/Doing/Items.html +1 -1
  55. data/docs/doc/Doing/LogAdapter.html +1 -1
  56. data/docs/doc/Doing/Note.html +109 -3
  57. data/docs/doc/Doing/Pager.html +1 -1
  58. data/docs/doc/Doing/Plugins.html +1 -1
  59. data/docs/doc/Doing/Prompt.html +1 -1
  60. data/docs/doc/Doing/Section.html +1 -1
  61. data/docs/doc/Doing/TemplateString.html +1 -1
  62. data/docs/doc/Doing/Types.html +2 -2
  63. data/docs/doc/Doing/Util/Backup.html +1 -1
  64. data/docs/doc/Doing/Util.html +1 -1
  65. data/docs/doc/Doing/WWID.html +6 -6
  66. data/docs/doc/Doing.html +2 -2
  67. data/docs/doc/FalseClass.html +1 -1
  68. data/docs/doc/GLI/Commands/Help.html +1 -1
  69. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  70. data/docs/doc/GLI/Commands.html +1 -1
  71. data/docs/doc/GLI.html +1 -1
  72. data/docs/doc/Hash.html +1 -1
  73. data/docs/doc/Object.html +1 -1
  74. data/docs/doc/PhraseParser/Operator.html +1 -1
  75. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  76. data/docs/doc/PhraseParser/Query.html +1 -1
  77. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  78. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  79. data/docs/doc/PhraseParser/TermClause.html +1 -1
  80. data/docs/doc/PhraseParser.html +1 -1
  81. data/docs/doc/Status.html +1 -1
  82. data/docs/doc/String.html +1 -1
  83. data/docs/doc/Symbol.html +1 -1
  84. data/docs/doc/Time.html +1 -1
  85. data/docs/doc/TrueClass.html +1 -1
  86. data/docs/doc/_index.html +3 -1
  87. data/docs/doc/file.README.html +2 -2
  88. data/docs/doc/index.html +2 -2
  89. data/docs/doc/method_list.html +337 -241
  90. data/docs/doc/top-level-namespace.html +105 -1
  91. data/docs/index.md +1 -1
  92. data/doing.gemspec +24 -23
  93. data/doing.rdoc +34 -13
  94. data/example_plugin.rb +7 -5
  95. data/inputrc +57 -0
  96. data/lib/completion/_doing.zsh +46 -46
  97. data/lib/completion/doing.bash +2 -2
  98. data/lib/completion/doing.fish +4 -4
  99. data/lib/doing/add_options.rb +117 -0
  100. data/lib/doing/array/array.rb +16 -0
  101. data/lib/doing/changelog/changes.rb +8 -6
  102. data/lib/doing/changelog/version.rb +11 -3
  103. data/lib/doing/completion/bash_completion.rb +12 -51
  104. data/lib/doing/completion/fish_completion.rb +17 -53
  105. data/lib/doing/completion/zsh_completion.rb +21 -54
  106. data/lib/doing/completion.rb +203 -17
  107. data/lib/doing/configuration.rb +12 -6
  108. data/lib/doing/item.rb +21 -3
  109. data/lib/doing/items.rb +5 -5
  110. data/lib/doing/note.rb +24 -8
  111. data/lib/doing/plugins/export/dayone_export.rb +8 -6
  112. data/lib/doing/plugins/export/html_export.rb +4 -4
  113. data/lib/doing/plugins/export/json_export.rb +19 -20
  114. data/lib/doing/plugins/export/markdown_export.rb +2 -2
  115. data/lib/doing/plugins/export/template_export.rb +4 -4
  116. data/lib/doing/plugins/import/calendar_import.rb +1 -1
  117. data/lib/doing/plugins/import/doing_import.rb +1 -1
  118. data/lib/doing/plugins/import/timing_import.rb +1 -1
  119. data/lib/doing/section.rb +1 -1
  120. data/lib/doing/string/highlight.rb +3 -4
  121. data/lib/doing/string/string.rb +8 -0
  122. data/lib/doing/types.rb +1 -1
  123. data/lib/doing/util.rb +1 -1
  124. data/lib/doing/util_backup.rb +12 -12
  125. data/lib/doing/version.rb +1 -1
  126. data/lib/doing/wwid.rb +75 -76
  127. data/lib/doing.rb +58 -0
  128. data/lib/examples/commands/wiki.rb +27 -19
  129. data/lib/helpers/threaded_tests.rb +2 -0
  130. data/scripts/setting_replace.rb +11 -0
  131. metadata +107 -102
  132. data/.yardoc/checksums +0 -29
  133. data/.yardoc/complete +0 -0
  134. data/.yardoc/object_types +0 -0
  135. data/.yardoc/objects/root.dat +0 -0
  136. data/.yardoc/proxy_types +0 -0
data/lib/doing/item.rb CHANGED
@@ -272,7 +272,7 @@ module Doing
272
272
  end
273
273
 
274
274
  def highlight_search(search, distance: nil, negate: false, case_type: nil)
275
- prefs = Doing.config.settings['search'] || {}
275
+ prefs = Doing.setting('search', {})
276
276
  matching = prefs.fetch('matching', 'pattern').normalize_matching
277
277
  distance ||= prefs.fetch('distance', 3).to_i
278
278
  case_type ||= prefs.fetch('case', 'smart').normalize_case
@@ -311,7 +311,7 @@ module Doing
311
311
  ## @return [Boolean] matches search criteria
312
312
  ##
313
313
  def search(search, distance: nil, negate: false, case_type: nil)
314
- prefs = Doing.config.settings['search'] || {}
314
+ prefs = Doing.setting('search', {})
315
315
  matching = prefs.fetch('matching', 'pattern').normalize_matching
316
316
  distance ||= prefs.fetch('distance', 3).to_i
317
317
  case_type ||= prefs.fetch('case', 'smart').normalize_case
@@ -354,6 +354,24 @@ module Doing
354
354
  negate ? !matches : matches
355
355
  end
356
356
 
357
+ ##
358
+ ## Test if item has a @done tag
359
+ ##
360
+ ## @return [Boolean] true item has @done tag
361
+ ##
362
+ def finished?
363
+ tags?('done')
364
+ end
365
+
366
+ ##
367
+ ## Test if item does not contain @done tag
368
+ ##
369
+ ## @return [Boolean] true if item is missing @done tag
370
+ ##
371
+ def unfinished?
372
+ tags?('done', negate: true)
373
+ end
374
+
357
375
  ##
358
376
  ## Test if item is included in never_finish config and
359
377
  ## thus should not receive a @done tag
@@ -436,7 +454,7 @@ module Doing
436
454
  private
437
455
 
438
456
  def should?(key)
439
- config = Doing.config.settings
457
+ config = Doing.settings
440
458
  return true unless config[key].is_a?(Array)
441
459
 
442
460
  config[key].each do |tag|
data/lib/doing/items.rb CHANGED
@@ -131,9 +131,9 @@ module Doing
131
131
  out = []
132
132
  @sections.each do |section|
133
133
  out.push(section.original)
134
- items = in_section(section.title).sort_by { |i| i.date }
135
- items.reverse! if Doing.config.settings['doing_file_sort'].normalize_order == :desc
136
- items.each { |item| out.push(item.to_s)}
134
+ items = in_section(section.title).sort_by(&:date)
135
+ items.reverse! if Doing.setting('doing_file_sort').normalize_order == :desc
136
+ items.each { |item| out.push(item.to_s) }
137
137
  end
138
138
 
139
139
  out.join("\n")
@@ -141,8 +141,8 @@ module Doing
141
141
 
142
142
  # @private
143
143
  def inspect
144
- "#<Doing::Items #{count} items, #{@sections.count} sections: #{@sections.map { |s| "<Section:#{s.title} #{in_section(s.title).count} items>" }.join(', ')}>"
144
+ sections = @sections.map { |s| "<Section:#{s.title} #{in_section(s.title).count} items>" }.join(', ')
145
+ "#<Doing::Items #{count} items, #{@sections.count} sections: #{sections}>"
145
146
  end
146
-
147
147
  end
148
148
  end
data/lib/doing/note.rb CHANGED
@@ -5,7 +5,6 @@ module Doing
5
5
  ## This class describes an item note.
6
6
  ##
7
7
  class Note < Array
8
-
9
8
  ##
10
9
  ## Initializes a new note
11
10
  ##
@@ -28,9 +27,10 @@ module Doing
28
27
  ##
29
28
  def add(note, replace: false)
30
29
  clear if replace
31
- if note.is_a?(String)
30
+ case note
31
+ when String
32
32
  append_string(note)
33
- elsif note.is_a?(Array)
33
+ when Array
34
34
  append(note)
35
35
  end
36
36
  end
@@ -55,7 +55,7 @@ module Doing
55
55
  ## @return [Array] Stripped note
56
56
  ##
57
57
  def strip_lines
58
- map(&:strip)
58
+ Note.new(map(&:strip))
59
59
  end
60
60
 
61
61
  def strip_lines!
@@ -64,8 +64,24 @@ module Doing
64
64
 
65
65
  ##
66
66
  ## Note as multi-line string
67
- def to_s
68
- compress.strip_lines.map { |l| "\t\t#{l}" }.join("\n")
67
+ ##
68
+ ## @param prefix [String] prefix for each line (default two tabs, TaskPaper format)
69
+ ##
70
+ def to_s(prefix: "\t\t")
71
+ compress.strip_lines.map { |l| "#{prefix}#{l}" }.join("\n")
72
+ end
73
+
74
+ ##
75
+ ## Returns note as a single line, newlines separated by
76
+ ## space
77
+ ##
78
+ ## @return [String] Line representation of the Note.
79
+ ##
80
+ ## @param separator The separator with which to
81
+ ## join multiple lines
82
+ ##
83
+ def to_line(separator: ' ')
84
+ compress.strip_lines.join(separator)
69
85
  end
70
86
 
71
87
  # @private
@@ -94,7 +110,7 @@ module Doing
94
110
  ## @param lines [Array] Array of strings
95
111
  ##
96
112
  def append(lines)
97
- concat(lines)
113
+ concat(lines.utf8)
98
114
  replace compress
99
115
  end
100
116
 
@@ -105,7 +121,7 @@ module Doing
105
121
  ## newlines will be split
106
122
  ##
107
123
  def append_string(input)
108
- concat(input.split(/\n/).map(&:strip))
124
+ concat(input.utf8.split(/\n/).map(&:strip))
109
125
  replace compress
110
126
  end
111
127
  end
@@ -45,7 +45,9 @@ module Doing
45
45
 
46
46
  def self.render(wwid, items, variables: {})
47
47
 
48
- return if items.nil?
48
+ return unless items.good?
49
+
50
+ config = Doing.settings
49
51
 
50
52
  opt = variables[:options]
51
53
  trigger = opt[:output]
@@ -78,7 +80,7 @@ module Doing
78
80
  title = "#{title} @section(#{i.section})" unless variables[:is_single]
79
81
 
80
82
  tags.concat(i.tag_array).sort!.uniq!
81
- flagged = day_flagged = true if i.tags?(wwid.config['marker_tag'])
83
+ flagged = day_flagged = true if i.tags?(config['marker_tag'])
82
84
 
83
85
  interval = wwid.get_interval(i, record: true) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
84
86
  interval ||= false
@@ -123,8 +125,8 @@ module Doing
123
125
  end
124
126
 
125
127
 
126
- template = if wwid.config['export_templates']['dayone'] && File.exist?(File.expand_path(wwid.config['export_templates']['dayone']))
127
- IO.read(File.expand_path(wwid.config['export_templates']['dayone']))
128
+ template = if config['export_templates']['dayone'] && File.exist?(File.expand_path(config['export_templates']['dayone']))
129
+ IO.read(File.expand_path(config['export_templates']['dayone']))
128
130
  else
129
131
  self.template('dayone')
130
132
  end
@@ -144,8 +146,8 @@ module Doing
144
146
  starred: hsh[:starred])
145
147
  end
146
148
  when :entries
147
- entry_template = if wwid.config['export_templates']['dayone_entry'] && File.exist?(File.expand_path(wwid.config['export_templates']['dayone_entry']))
148
- IO.read(File.expand_path(wwid.config['export_templates']['dayone_entry']))
149
+ entry_template = if config['export_templates']['dayone_entry'] && File.exist?(File.expand_path(config['export_templates']['dayone_entry']))
150
+ IO.read(File.expand_path(config['export_templates']['dayone_entry']))
149
151
  else
150
152
  self.template('dayone-entry')
151
153
  end
@@ -58,14 +58,14 @@ module Doing
58
58
  }
59
59
  end
60
60
 
61
- template = if wwid.config['export_templates']['haml'] && File.exist?(File.expand_path(wwid.config['export_templates']['haml']))
62
- IO.read(File.expand_path(wwid.config['export_templates']['haml']))
61
+ template = if Doing.setting('export_templates.haml') && File.exist?(File.expand_path(Doing.setting('export_templates.haml')))
62
+ IO.read(File.expand_path(Doing.setting('export_templates.haml')))
63
63
  else
64
64
  self.template('html')
65
65
  end
66
66
 
67
- style = if wwid.config['export_templates']['css'] && File.exist?(File.expand_path(wwid.config['export_templates']['css']))
68
- IO.read(File.expand_path(wwid.config['export_templates']['css']))
67
+ style = if Doing.setting('export_templates.css') && File.exist?(File.expand_path(Doing.setting('export_templates.css')))
68
+ IO.read(File.expand_path(Doing.setting('export_templates.css')))
69
69
  else
70
70
  self.template('css')
71
71
  end
@@ -29,16 +29,12 @@ module Doing
29
29
  max = last_date.strftime('%F')
30
30
  min = items[0].date.strftime('%F')
31
31
  items.each_with_index do |i, index|
32
- if String.method_defined? :force_encoding
33
- title = i.title.force_encoding('utf-8')
34
- note = i.note.map { |line| line.force_encoding('utf-8').strip } if i.note
35
- else
36
- title = i.title
37
- note = i.note.map { |line| line.strip } if i.note
38
- end
32
+ title = i.title.utf8
33
+ note = i.note.utf8
39
34
 
40
35
  end_date = i.end_date || ''
41
36
  interval = wwid.get_interval(i, formatted: false) || 0
37
+ duration = i.duration || 0
42
38
  note ||= ''
43
39
 
44
40
  tags = []
@@ -49,14 +45,15 @@ module Doing
49
45
  attributes[tag[0]] = tag[1] if tag[1]
50
46
  end
51
47
 
52
- if opt[:output] == 'json'
53
-
48
+ case opt[:output]
49
+ when 'json'
54
50
  i = {
55
51
  date: i.date,
56
52
  end_date: end_date,
57
53
  title: title.strip, #+ " #{note}"
58
- note: note.instance_of?(Array) ? note.to_s : note,
54
+ note: note.to_s(prefix: ''),
59
55
  time: interval.time_string(format: :clock),
56
+ duration: duration.time_string(format: :clock),
60
57
  tags: tags
61
58
  }
62
59
 
@@ -64,7 +61,7 @@ module Doing
64
61
 
65
62
  items_out << i
66
63
 
67
- elsif opt[:output] == 'timeline'
64
+ when 'timeline'
68
65
  new_item = {
69
66
  'id' => index + 1,
70
67
  'content' => title.strip, #+ " #{note}"
@@ -74,26 +71,28 @@ module Doing
74
71
  'style' => 'color:#4c566b;background-color:#d8dee9;'
75
72
  }
76
73
 
77
-
78
- if interval && interval.to_i > 0
74
+ if interval.to_i&.positive?
79
75
  new_item['end'] = end_date.strftime('%F %T')
80
76
  if interval.to_i > 3600
81
77
  new_item['type'] = 'range'
82
78
  new_item['style'] = 'color:white;background-color:#a2bf8a;'
83
79
  end
84
80
  end
85
- new_item['style'] = 'color:white;background-color:#f7921e;' if i.tags?(wwid.config['marker_tag'])
81
+ new_item['style'] = 'color:white;background-color:#f7921e;' if i.tags?(Doing.setting('marker_tag'))
86
82
  items_out.push(new_item)
87
83
  end
88
84
  end
89
- if opt[:output] == 'json'
85
+ case opt[:output]
86
+ when 'json'
90
87
  Doing.logger.debug('JSON Export:', "#{items_out.count} items output to JSON")
91
88
  JSON.pretty_generate({
92
- 'section' => variables[:page_title],
93
- 'items' => items_out,
94
- 'timers' => wwid.tag_times(format: :json, sort_by: opt[:sort_tags], sort_order: opt[:tag_order])
95
- })
96
- elsif opt[:output] == 'timeline'
89
+ 'section' => variables[:page_title],
90
+ 'items' => items_out,
91
+ 'timers' => wwid.tag_times(format: :json,
92
+ sort_by: opt[:sort_tags],
93
+ sort_order: opt[:tag_order])
94
+ })
95
+ when 'timeline'
97
96
  template = <<~EOTEMPLATE
98
97
  <!doctype html>
99
98
  <html>
@@ -66,8 +66,8 @@ module Doing
66
66
  }
67
67
  end
68
68
 
69
- template = if wwid.config['export_templates']['markdown'] && File.exist?(File.expand_path(wwid.config['export_templates']['markdown']))
70
- IO.read(File.expand_path(wwid.config['export_templates']['markdown']))
69
+ template = if Doing.setting('export_templates.markdown') && File.exist?(File.expand_path(Doing.setting('export_templates.markdown')))
70
+ IO.read(File.expand_path(Doing.setting('export_templates.markdown')))
71
71
  else
72
72
  self.template(nil)
73
73
  end
@@ -24,8 +24,8 @@ module Doing
24
24
 
25
25
  out = ''
26
26
  items.each do |item|
27
- if opt[:highlight] && item.title =~ /@#{wwid.config['marker_tag']}\b/i
28
- flag = Doing::Color.send(wwid.config['marker_color'])
27
+ if opt[:highlight] && item.title =~ /@#{Doing.setting('marker_tag')}\b/i
28
+ flag = Doing::Color.send(Doing.setting('marker_color'))
29
29
  reset = Doing::Color.reset + Doing::Color.default
30
30
  else
31
31
  flag = ''
@@ -34,7 +34,7 @@ module Doing
34
34
 
35
35
  placeholders = {}
36
36
 
37
- if !item.note.empty? && wwid.config['include_notes']
37
+ if !item.note.empty? && Doing.setting('include_notes')
38
38
  note = item.note.map(&:strip).delete_if(&:empty?)
39
39
  note.map! { |line| "#{line.sub(/^\t*/, '')} " }
40
40
 
@@ -133,7 +133,7 @@ module Doing
133
133
 
134
134
  # Doing.logger.debug('Template Export:', "#{items.count} items output to template #{opt[:template]}")
135
135
  if opt[:totals]
136
- out += wwid.tag_times(format: wwid.config['timer_format'].to_sym,
136
+ out += wwid.tag_times(format: Doing.setting('timer_format').to_sym,
137
137
  sort_by: opt[:sort_tags],
138
138
  sort_order: opt[:tag_order])
139
139
  end
@@ -23,7 +23,7 @@ module Doing
23
23
  limit_start = options[:start].to_i
24
24
  limit_end = options[:end].to_i
25
25
 
26
- section = options[:section] || wwid.config['current_section']
26
+ section = options[:section] || Doing.setting('current_section')
27
27
  options[:no_overlap] ||= false
28
28
  options[:autotag] ||= wwid.auto_tag
29
29
 
@@ -63,7 +63,7 @@ module Doing
63
63
  title.gsub!(/ +/, ' ')
64
64
  title.strip!
65
65
  section = options[:section] || item.section
66
- section ||= wwid.config['current_section']
66
+ section ||= Doing.setting('current_section')
67
67
 
68
68
  new_item = Item.new(item.date, title, section)
69
69
  new_item.note = item.note
@@ -24,7 +24,7 @@ module Doing
24
24
  ##
25
25
  def self.import(wwid, path, options: {})
26
26
  exit_now! 'Path to JSON report required' if path.nil?
27
- section = options[:section] || wwid.config['current_section']
27
+ section = options[:section] || Doing.setting('current_section')
28
28
  options[:no_overlap] ||= false
29
29
  options[:autotag] ||= wwid.auto_tag
30
30
  wwid.content.add_section(section) unless wwid.content.section?(section)
data/lib/doing/section.rb CHANGED
@@ -13,7 +13,7 @@ module Doing
13
13
  @original = if original.nil?
14
14
  "#{title}:"
15
15
  else
16
- original =~ /:(\s+@\S+(\(.*?\))?)*$/ ? original : "#{original}:"
16
+ original =~ /:(\s+@[^ (]+(\([^)]*\))?)*?$/ ? original : "#{original}:"
17
17
  end
18
18
  end
19
19
 
@@ -35,10 +35,9 @@ module Doing
35
35
 
36
36
  def highlight_search(search, distance: nil, negate: false, case_type: nil)
37
37
  out = dup
38
- prefs = Doing.config.settings['search'] || {}
39
- matching = prefs.fetch('matching', 'pattern').normalize_matching
40
- distance ||= prefs.fetch('distance', 3).to_i
41
- case_type ||= prefs.fetch('case', 'smart').normalize_case
38
+ matching = Doing.setting('search.matching', 'pattern').normalize_matching
39
+ distance ||= Doing.setting('search.distance', 3).to_i
40
+ case_type ||= Doing.setting('search.case', 'smart').normalize_case
42
41
 
43
42
  if search.rx? || matching == :fuzzy
44
43
  rx = search.to_rx(distance: distance, case_type: case_type)
@@ -2,6 +2,14 @@
2
2
 
3
3
  class ::String
4
4
  include Doing::Color
5
+
6
+ def utf8
7
+ if String.method_defined? :force_encoding
8
+ dup.force_encoding('utf-8')
9
+ else
10
+ self
11
+ end
12
+ end
5
13
  end
6
14
 
7
15
  require_relative 'highlight'
data/lib/doing/types.rb CHANGED
@@ -11,7 +11,7 @@ module Doing
11
11
  REGEX_TIME = /^#{REGEX_CLOCK}$/i.freeze
12
12
  REGEX_DAY = /^(mon|tue|wed|thur?|fri|sat|sun)(\w+(day)?)?$/i.freeze
13
13
  REGEX_RANGE_INDICATOR = ' +(?:to|through|thru|(?:un)?til|-+) +'
14
- REGEX_RANGE = /^\S+#{REGEX_RANGE_INDICATOR}+\S+/i.freeze
14
+ REGEX_RANGE = /^\S+.*?#{REGEX_RANGE_INDICATOR}\S+.*?$/i.freeze
15
15
  REGEX_TIME_RANGE = /^#{REGEX_CLOCK}#{REGEX_RANGE_INDICATOR}#{REGEX_CLOCK}$/i.freeze
16
16
 
17
17
  InvalidExportType = Class.new(RuntimeError)
data/lib/doing/util.rb CHANGED
@@ -159,7 +159,7 @@ module Doing
159
159
 
160
160
  return ENV['EDITOR'] if ENV['DOING_EDITOR_TEST']
161
161
 
162
- editor_config = Doing.config.settings['editors']
162
+ editor_config = Doing.setting('editors')
163
163
 
164
164
  if editor_config.is_a?(String)
165
165
  msg = "Please update your configuration, 'editors' should be a mapping."
@@ -30,7 +30,7 @@ module Doing
30
30
  ## @param limit Maximum number of backups to retain
31
31
  ##
32
32
  def clear_redo(filename)
33
- filename ||= Doing.config.settings['doing_file']
33
+ filename ||= Doing.setting('doing_file')
34
34
  backups = Dir.glob("undone*___#{File.basename(filename)}", base: backup_dir).sort.reverse
35
35
  backups.each do |file|
36
36
  FileUtils.rm(File.join(backup_dir, file))
@@ -44,7 +44,7 @@ module Doing
44
44
  ## @return [String] filename
45
45
  ##
46
46
  def last_backup(filename = nil, count: 1)
47
- filename ||= Doing.config.settings['doing_file']
47
+ filename ||= Doing.setting('doing_file')
48
48
 
49
49
  backup = get_backups(filename).slice(count - 1)
50
50
  backup.nil? ? nil : File.join(backup_dir, backup)
@@ -59,7 +59,7 @@ module Doing
59
59
  ##
60
60
  def restore_last_backup(filename = nil, count: 1)
61
61
  Doing.logger.benchmark(:restore_backup, :start)
62
- filename ||= Doing.config.settings['doing_file']
62
+ filename ||= Doing.setting('doing_file')
63
63
 
64
64
  backup_file = last_backup(filename, count: count)
65
65
  raise DoingRuntimeError, 'End of undo history' if backup_file.nil?
@@ -77,7 +77,7 @@ module Doing
77
77
  ## @param filename The filename
78
78
  ##
79
79
  def redo_backup(filename = nil, count: 1)
80
- filename ||= Doing.config.settings['doing_file']
80
+ filename ||= Doing.setting('doing_file')
81
81
  # redo_file = File.join(backup_dir, "undone___#{File.basename(filename)}")
82
82
  undones = Dir.glob("undone*#{File.basename(filename)}", base: backup_dir).sort.reverse
83
83
  total = undones.count
@@ -101,7 +101,7 @@ module Doing
101
101
  end
102
102
 
103
103
  def clear_undone(filename = nil)
104
- filename ||= Doing.config.settings['doing_file']
104
+ filename ||= Doing.setting('doing_file')
105
105
  # redo_file = File.join(backup_dir, "undone___#{File.basename(filename)}")
106
106
  Dir.glob("undone*#{File.basename(filename)}", base: backup_dir).each do |f|
107
107
  FileUtils.rm(File.join(backup_dir, f))
@@ -115,7 +115,7 @@ module Doing
115
115
  ## @param filename The filename to restore
116
116
  ##
117
117
  def select_redo(filename = nil)
118
- filename ||= Doing.config.settings['doing_file']
118
+ filename ||= Doing.setting('doing_file')
119
119
 
120
120
  undones = Dir.glob("undone*#{File.basename(filename)}", base: backup_dir).sort
121
121
 
@@ -155,7 +155,7 @@ module Doing
155
155
  ## @param filename The filename to restore
156
156
  ##
157
157
  def select_backup(filename = nil)
158
- filename ||= Doing.config.settings['doing_file']
158
+ filename ||= Doing.setting('doing_file')
159
159
 
160
160
  options = get_backups(filename).each_with_object([]) do |file, arr|
161
161
  d, _base = date_of_backup(file)
@@ -219,7 +219,7 @@ module Doing
219
219
  ##
220
220
  def write_backup(filename = nil)
221
221
  Doing.logger.benchmark(:_write_backup, :start)
222
- filename ||= Doing.config.settings['doing_file']
222
+ filename ||= Doing.setting('doing_file')
223
223
 
224
224
  unless File.exist?(filename)
225
225
  Doing.logger.debug('Backup:', "original file doesn't exist (#{filename})")
@@ -234,7 +234,7 @@ module Doing
234
234
 
235
235
  FileUtils.cp(filename, backup_file)
236
236
 
237
- prune_backups(filename, Doing.config.settings['history_size'].to_i)
237
+ prune_backups(filename, Doing.setting('history_size').to_i)
238
238
  clear_undone(filename)
239
239
  Doing.logger.benchmark(:_write_backup, :finish)
240
240
  end
@@ -246,13 +246,13 @@ module Doing
246
246
  end
247
247
 
248
248
  def get_backups(filename = nil, include_forward: false)
249
- filename ||= Doing.config.settings['doing_file']
249
+ filename ||= Doing.setting('doing_file')
250
250
  backups = Dir.glob("*___#{File.basename(filename)}", base: backup_dir).sort.reverse
251
251
  backups.delete_if { |f| f =~ /^undone/ } unless include_forward
252
252
  end
253
253
 
254
254
  def save_undone(filename = nil)
255
- filename ||= Doing.config.settings['doing_file']
255
+ filename ||= Doing.setting('doing_file')
256
256
  undone_file = File.join(backup_dir, "undone#{timestamp_filename}___#{File.basename(filename)}")
257
257
  FileUtils.cp(filename, undone_file)
258
258
  end
@@ -279,7 +279,7 @@ module Doing
279
279
  end
280
280
 
281
281
  def create_backup_dir
282
- dir = File.expand_path(Doing.config.settings['backup_dir']) || File.join(user_home, '.doing_backup')
282
+ dir = File.expand_path(Doing.setting('backup_dir')) || File.join(user_home, '.doing_backup')
283
283
  if File.exist?(dir) && !File.directory?(dir)
284
284
  raise DoingRuntimeError, "Backup error: #{dir} is not a directory"
285
285
 
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '2.1.29'
2
+ VERSION = '2.1.33'
3
3
  end