doing 1.0.92 → 2.0.5.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHORS +19 -0
  3. data/CHANGELOG.md +596 -0
  4. data/COMMANDS.md +1181 -0
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +110 -0
  7. data/LICENSE +23 -0
  8. data/README.md +15 -699
  9. data/Rakefile +79 -0
  10. data/_config.yml +1 -0
  11. data/bin/doing +1012 -486
  12. data/doing.fish +278 -0
  13. data/doing.gemspec +34 -0
  14. data/doing.rdoc +1759 -0
  15. data/example_plugin.rb +209 -0
  16. data/generate_completions.sh +4 -0
  17. data/img/doing-colors.jpg +0 -0
  18. data/img/doing-printf-wrap-800.jpg +0 -0
  19. data/img/doing-show-note-formatting-800.jpg +0 -0
  20. data/lib/completion/_doing.zsh +151 -0
  21. data/lib/completion/doing.bash +416 -0
  22. data/lib/completion/doing.fish +278 -0
  23. data/lib/doing/array.rb +8 -0
  24. data/lib/doing/cli_status.rb +66 -0
  25. data/lib/doing/colors.rb +136 -0
  26. data/lib/doing/configuration.rb +312 -0
  27. data/lib/doing/errors.rb +102 -0
  28. data/lib/doing/hash.rb +31 -0
  29. data/lib/doing/hooks.rb +59 -0
  30. data/lib/doing/item.rb +155 -0
  31. data/lib/doing/log_adapter.rb +342 -0
  32. data/lib/doing/markdown_document_listener.rb +174 -0
  33. data/lib/doing/note.rb +59 -0
  34. data/lib/doing/pager.rb +95 -0
  35. data/lib/doing/plugin_manager.rb +208 -0
  36. data/lib/doing/plugins/export/csv_export.rb +48 -0
  37. data/lib/doing/plugins/export/html_export.rb +83 -0
  38. data/lib/doing/plugins/export/json_export.rb +140 -0
  39. data/lib/doing/plugins/export/markdown_export.rb +85 -0
  40. data/lib/doing/plugins/export/taskpaper_export.rb +34 -0
  41. data/lib/doing/plugins/export/template_export.rb +141 -0
  42. data/lib/doing/plugins/import/cal_to_json.scpt +0 -0
  43. data/lib/doing/plugins/import/calendar_import.rb +76 -0
  44. data/lib/doing/plugins/import/doing_import.rb +144 -0
  45. data/lib/doing/plugins/import/timing_import.rb +78 -0
  46. data/lib/doing/string.rb +347 -0
  47. data/lib/doing/symbol.rb +16 -0
  48. data/lib/doing/time.rb +18 -0
  49. data/lib/doing/util.rb +186 -0
  50. data/lib/doing/version.rb +1 -1
  51. data/lib/doing/wwid.rb +1868 -2356
  52. data/lib/doing/wwidfile.rb +117 -0
  53. data/lib/doing.rb +44 -4
  54. data/lib/examples/commands/wiki.rb +81 -0
  55. data/lib/examples/plugins/hooks.rb +22 -0
  56. data/lib/examples/plugins/say_export.rb +202 -0
  57. data/lib/examples/plugins/templates/wiki.css +169 -0
  58. data/lib/examples/plugins/templates/wiki.haml +27 -0
  59. data/lib/examples/plugins/templates/wiki_index.haml +18 -0
  60. data/lib/examples/plugins/wiki_export.rb +87 -0
  61. data/lib/templates/doing-markdown.erb +5 -0
  62. data/man/doing.1 +964 -0
  63. data/man/doing.1.html +711 -0
  64. data/man/doing.1.ronn +600 -0
  65. data/package-lock.json +3 -0
  66. data/rdoc_to_mmd.rb +42 -0
  67. data/rdocfixer.rb +13 -0
  68. data/scripts/generate_bash_completions.rb +210 -0
  69. data/scripts/generate_fish_completions.rb +201 -0
  70. data/scripts/generate_zsh_completions.rb +164 -0
  71. metadata +82 -7
  72. data/lib/doing/helpers.rb +0 -191
  73. data/lib/doing/markdown_export.rb +0 -16
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doing
4
+ # Plugin handling
5
+ module Plugins
6
+ class << self
7
+ def user_home
8
+ @user_home ||= Util.user_home
9
+ end
10
+
11
+ def plugins
12
+ @plugins ||= {
13
+ import: {},
14
+ export: {}
15
+ }
16
+ end
17
+
18
+ ##
19
+ # Load plugins from plugins folder
20
+ #
21
+ def load_plugins(add_dir = nil)
22
+ plugins_path(add_dir).each do |plugin_search_path|
23
+ Dir.glob(File.join(plugin_search_path, '**', '*.rb')).sort.each do |plugin|
24
+ require plugin
25
+ end
26
+ end
27
+
28
+ plugins
29
+ end
30
+
31
+ # Public: Setup the plugin search path
32
+ #
33
+ # Returns an Array of plugin search paths
34
+ def plugins_path(add_dir = nil)
35
+ paths = Array(File.join(File.dirname(__FILE__), 'plugins'))
36
+ paths << File.join(add_dir) if add_dir
37
+ paths.map { |d| File.expand_path(d) }
38
+ end
39
+
40
+ ##
41
+ # Register a plugin
42
+ #
43
+ # param: +[String|Array]+ title The name of the plugin (can be an array of names)
44
+ #
45
+ # param: +type+ The plugin type (:import, :export)
46
+ #
47
+ # param: +klass+ The class responding to :render or :import
48
+ #
49
+ #
50
+ # returns: Success boolean
51
+ #
52
+ def register(title, type, klass)
53
+ type = validate_plugin(title, type, klass)
54
+ return unless type
55
+
56
+ if title.is_a?(Array)
57
+ title.each { |t| register(t, type, klass) }
58
+ return
59
+ end
60
+
61
+ settings = if klass.respond_to? :settings
62
+ klass.settings
63
+ else
64
+ { trigger: title.normalize_trigger, config: {} }
65
+ end
66
+
67
+ plugins[type] ||= {}
68
+ plugins[type][title] = {
69
+ trigger: settings[:trigger].normalize_trigger || title.normalize_trigger,
70
+ class: klass,
71
+ templates: settings[:templates] || nil,
72
+ config: settings[:config] || {}
73
+ }
74
+
75
+ return unless ENV['DOING_PLUGIN_DEBUG']
76
+
77
+ Doing.logger.debug('Plugin Manager:', "Registered #{type} plugin \"#{title}\"")
78
+ end
79
+
80
+ def validate_plugin(title, type, klass)
81
+ type = valid_type(type)
82
+ if type == :import && !klass.respond_to?(:import)
83
+ raise Errors::PluginUncallable.new('Import plugins must respond to :import', type: type, plugin: title)
84
+ end
85
+
86
+ if type == :export && !klass.respond_to?(:render)
87
+ raise Errors::PluginUncallable.new('Export plugins must respond to :render', type: type, plugin: title)
88
+ end
89
+
90
+ type
91
+ end
92
+
93
+ def valid_type(type, default: nil)
94
+ type ||= default
95
+
96
+ t = type.to_s
97
+ type = case t
98
+ when /^i(m(p(o(r(t)?)?)?)?)?$/
99
+ :import
100
+ when /^e(x(p(o(r(t)?)?)?)?)?$/
101
+ :export
102
+ else
103
+ raise Errors::InvalidPluginType, 'Invalid plugin type'
104
+ end
105
+
106
+ type.to_sym
107
+ end
108
+
109
+ ##
110
+ ## @brief List available plugins to stdout
111
+ ##
112
+ ## @param options { type, separator }
113
+ ##
114
+ def list_plugins(options = {})
115
+ separator = options[:column] ? "\n" : "\t"
116
+ type = options[:type].nil? || options[:type] =~ /all/i ? 'all' : [valid_type(options[:type])]
117
+
118
+ case type
119
+ when :import
120
+ puts plugin_names(type: :import, separator: separator)
121
+ when :export
122
+ puts plugin_names(type: :export, separator: separator)
123
+ else
124
+ print 'Import plugins: '
125
+ puts plugin_names(type: :import, separator: ', ')
126
+ print 'Export plugins: '
127
+ puts plugin_names(type: :export, separator: ', ')
128
+ end
129
+ end
130
+
131
+ ##
132
+ ## @brief Return array of available plugin names
133
+ ##
134
+ ## @param type Plugin type (:import, :export)
135
+ ##
136
+ ## @returns [Array<String>] plugin names
137
+ ##
138
+ def available_plugins(type: :export)
139
+ type = valid_type(type)
140
+ plugins[type].keys.sort
141
+ end
142
+
143
+ ##
144
+ ## @brief Return string version of plugin names
145
+ ##
146
+ ## @param type Plugin type (:import, :export)
147
+ ## @param separator The separator to join names with
148
+ ##
149
+ ## @return [String] Plugin names
150
+ ##
151
+ def plugin_names(type: :export, separator: '|')
152
+ type = valid_type(type)
153
+ available_plugins(type: type).join(separator)
154
+ end
155
+
156
+ ##
157
+ ## @brief Return a regular expression of all
158
+ ## plugin triggers for type
159
+ ##
160
+ ## @param type The type :import or :export
161
+ ##
162
+ def plugin_regex(type: :export)
163
+ type = valid_type(type)
164
+ pattern = []
165
+ plugins[type].each do |_, options|
166
+ pattern << options[:trigger].normalize_trigger
167
+ end
168
+ Regexp.new("^(?:#{pattern.join('|')})$", true)
169
+ end
170
+
171
+ def plugin_templates(type: :export)
172
+ type = valid_type(type)
173
+ templates = []
174
+ plugs = plugins[type].clone
175
+ plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
176
+ options[:templates].each do |t|
177
+ templates << t[:name]
178
+ end
179
+ end
180
+
181
+ templates
182
+ end
183
+
184
+ def template_regex(type: :export)
185
+ type = valid_type(type)
186
+ pattern = []
187
+ plugs = plugins[type].clone
188
+ plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
189
+ options[:templates].each do |t|
190
+ pattern << t[:trigger].normalize_trigger
191
+ end
192
+ end
193
+ Regexp.new("^(?:#{pattern.join('|')})$", true)
194
+ end
195
+
196
+ def template_for_trigger(trigger, type: :export)
197
+ type = valid_type(type)
198
+ plugs = plugins[type].clone
199
+ plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
200
+ options[:templates].each do |t|
201
+ return options[:class].template(trigger) if trigger =~ /^(?:#{t[:trigger].normalize_trigger})$/
202
+ end
203
+ end
204
+ raise Errors::InvalidArgument, "No template type matched \"#{trigger}\""
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # title: CSV Export
4
+ # description: Export CSV formatted data with header row
5
+ # author: Brett Terpstra
6
+ # url: https://brettterpstra.com
7
+ module Doing
8
+ ##
9
+ ## @brief CSV Export
10
+ ##
11
+ class CSVExport
12
+ include Doing::Util
13
+
14
+ def self.settings
15
+ {
16
+ trigger: 'csv'
17
+ }
18
+ end
19
+
20
+ def self.render(wwid, items, variables: {})
21
+ return if items.nil?
22
+
23
+ opt = variables[:options]
24
+
25
+ output = [CSV.generate_line(%w[start end title note timer section])]
26
+ items.each do |i|
27
+ note = format_note(i.note)
28
+ end_date = i.end_date
29
+ interval = end_date && opt[:times] ? wwid.get_interval(i, formatted: false) : 0
30
+ output.push(CSV.generate_line([i.date, end_date, i.title, note, interval, i.section]))
31
+ end
32
+ Doing.logger.debug('CSV Export:', "#{items.count} items output to CSV")
33
+ output.join('')
34
+ end
35
+
36
+ def self.format_note(note)
37
+ out = ''
38
+ if note
39
+ arr = note.map(&:strip).delete_if { |e| e =~ /^\s*$/ }
40
+ out = arr.join("\n") unless arr.empty?
41
+ end
42
+
43
+ out
44
+ end
45
+
46
+ Doing::Plugins.register 'csv', :export, self
47
+ end
48
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # title: HTML Export
4
+ # description: Export styled HTML view of data
5
+ # author: Brett Terpstra
6
+ # url: https://brettterpstra.com
7
+ module Doing
8
+ class HTMLExport
9
+ include Doing::Util
10
+
11
+ def self.settings
12
+ {
13
+ trigger: 'html?|web(?:page)?',
14
+ templates: [
15
+ { name: 'haml', trigger: 'h[ta]ml?|web' },
16
+ { name: 'css', trigger: 'css|styl(?:e|us)' }
17
+ ]
18
+ }
19
+ end
20
+
21
+ def self.template(trigger)
22
+ if trigger =~ /^(css|sty)/
23
+ IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing.css'))
24
+ else
25
+ IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing.haml'))
26
+ end
27
+ end
28
+
29
+ def self.render(wwid, items, variables: {})
30
+ return if items.nil?
31
+
32
+ opt = variables[:options]
33
+
34
+ items_out = []
35
+ items.each do |i|
36
+ # if i.has_key?('note')
37
+ # note = '<span class="note">' + i.note.map{|n| n.strip }.join('<br>') + '</span>'
38
+ # else
39
+ # note = ''
40
+ # end
41
+ if String.method_defined? :force_encoding
42
+ title = i.title.force_encoding('utf-8').link_urls
43
+ note = i.note.map { |line| line.force_encoding('utf-8').strip.link_urls } if i.note
44
+ else
45
+ title = i.title.link_urls
46
+ note = i.note.map { |line| line.strip.link_urls } if i.note
47
+ end
48
+
49
+ interval = wwid.get_interval(i) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
50
+ interval ||= false
51
+
52
+ items_out << {
53
+ date: i.date.strftime('%a %-I:%M%p'),
54
+ title: title.gsub(/(@[^ (]+(\(.*?\))?)/im, '<span class="tag">\1</span>').strip, #+ " #{note}"
55
+ note: note,
56
+ time: interval,
57
+ section: i.section
58
+ }
59
+ end
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']))
63
+ else
64
+ self.template('html')
65
+ end
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']))
69
+ else
70
+ self.template('css')
71
+ end
72
+
73
+ totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
74
+ engine = Haml::Engine.new(template)
75
+ Doing.logger.debug('HTML Export:', "#{items_out.count} items output to HTML")
76
+ @out = engine.render(Object.new,
77
+ { :@items => items_out, :@page_title => variables[:page_title], :@style => style, :@totals => totals })
78
+ end
79
+
80
+ Doing::Plugins.register 'html', :export, self
81
+ end
82
+ end
83
+
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ # title: JSON Export
4
+ # description: Export JSON-formatted data, including entry durations and tag totals
5
+ # author: Brett Terpstra
6
+ # url: https://brettterpstra.com
7
+ module Doing
8
+ class JSONExport
9
+ include Doing::Util
10
+
11
+ def self.settings
12
+ {
13
+ trigger: 'json|time(?:line)?'
14
+ }
15
+ end
16
+
17
+ def self.render(wwid, items, variables: {})
18
+ return if items.nil?
19
+
20
+ opt = variables[:options]
21
+ opt[:output] = case opt[:output]
22
+ when /^t/
23
+ 'timeline'
24
+ else
25
+ 'json'
26
+ end
27
+ items_out = []
28
+ last_date = items[-1].date + (60 * 60 * 24)
29
+ max = last_date.strftime('%F')
30
+ min = items[0].date.strftime('%F')
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
39
+
40
+ end_date = i.end_date || ''
41
+ interval = wwid.get_interval(i, formatted: false) || 0
42
+ note ||= ''
43
+
44
+ tags = []
45
+ attributes = {}
46
+ skip_tags = %w[meanwhile done cancelled flagged]
47
+ i.title.scan(/@([^(\s]+)(?:\((.*?)\))?/).each do |tag|
48
+ tags.push(tag[0]) unless skip_tags.include?(tag[0])
49
+ attributes[tag[0]] = tag[1] if tag[1]
50
+ end
51
+
52
+ if opt[:output] == 'json'
53
+
54
+ i = {
55
+ date: i.date,
56
+ end_date: end_date,
57
+ title: title.strip, #+ " #{note}"
58
+ note: note.instance_of?(Array) ? note.to_s : note,
59
+ time: '%02d:%02d:%02d' % wwid.format_time(interval),
60
+ tags: tags
61
+ }
62
+
63
+ attributes.each { |attr, val| i[attr.to_sym] = val }
64
+
65
+ items_out << i
66
+
67
+ elsif opt[:output] == 'timeline'
68
+ new_item = {
69
+ 'id' => index + 1,
70
+ 'content' => title.strip, #+ " #{note}"
71
+ 'title' => title.strip + " (#{'%02d:%02d:%02d' % wwid.format_time(interval)})",
72
+ 'start' => i.date.strftime('%F %T'),
73
+ 'type' => 'box',
74
+ 'style' => 'color:#4c566b;background-color:#d8dee9;'
75
+ }
76
+
77
+
78
+ if interval && interval.to_i > 0
79
+ new_item['end'] = end_date.strftime('%F %T')
80
+ if interval.to_i > 3600
81
+ new_item['type'] = 'range'
82
+ new_item['style'] = 'color:white;background-color:#a2bf8a;'
83
+ end
84
+ end
85
+ new_item['style'] = 'color:white;background-color:#f7921e;' if i.tags?(wwid.config['marker_tag'])
86
+ items_out.push(new_item)
87
+ end
88
+ end
89
+ if opt[:output] == 'json'
90
+ Doing.logger.debug('JSON Export:', "#{items_out.count} items output to JSON")
91
+ JSON.pretty_generate({
92
+ 'section' => variables[:page_title],
93
+ 'items' => items_out,
94
+ 'timers' => wwid.tag_times(format: :json, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order])
95
+ })
96
+ elsif opt[:output] == 'timeline'
97
+ template = <<~EOTEMPLATE
98
+ <!doctype html>
99
+ <html>
100
+ <head>
101
+ <link href="https://unpkg.com/vis-timeline@7.4.9/dist/vis-timeline-graph2d.min.css" rel="stylesheet" type="text/css" />
102
+ <script src="https://unpkg.com/vis-timeline@7.4.9/dist/vis-timeline-graph2d.min.js"></script>
103
+ </head>
104
+ <body>
105
+ <div id="mytimeline"></div>
106
+ #{' '}
107
+ <script type="text/javascript">
108
+ // DOM element where the Timeline will be attached
109
+ var container = document.getElementById('mytimeline');
110
+ #{' '}
111
+ // Create a DataSet with data (enables two way data binding)
112
+ var data = new vis.DataSet(#{items_out.to_json});
113
+ #{' '}
114
+ // Configuration for the Timeline
115
+ var options = {
116
+ width: '100%',
117
+ height: '800px',
118
+ margin: {
119
+ item: 20
120
+ },
121
+ stack: true,
122
+ min: '#{min}',
123
+ max: '#{max}'
124
+ };
125
+ #{' '}
126
+ // Create a Timeline
127
+ var timeline = new vis.Timeline(container, data, options);
128
+ </script>
129
+ </body>
130
+ </html>
131
+ EOTEMPLATE
132
+ Doing.logger.debug('Timeline Export:', "#{items_out.count} items output to Timeline")
133
+ template
134
+ end
135
+ end
136
+
137
+ Doing::Plugins.register 'timeline', :export, self
138
+ end
139
+ end
140
+
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ # title: Markdown Export
4
+ # description: Export GFM-style task list
5
+ # author: Brett Terpstra
6
+ # url: https://brettterpstra.com
7
+ module Doing
8
+ class MarkdownRenderer
9
+ attr_accessor :items, :page_title, :totals
10
+
11
+ def initialize(page_title, items, totals)
12
+ @page_title = page_title
13
+ @items = items
14
+ @totals = totals
15
+ end
16
+
17
+ def get_binding
18
+ binding()
19
+ end
20
+ end
21
+
22
+ class MarkdownExport
23
+ include Doing::Util
24
+
25
+ def self.settings
26
+ {
27
+ trigger: 'markdown|mk?d|gfm',
28
+ templates: [{ name: 'markdown', trigger: 'mk?d|markdown' }]
29
+ }
30
+ end
31
+
32
+ def self.template(_trigger)
33
+ IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing-markdown.erb'))
34
+ end
35
+
36
+ def self.render(wwid, items, variables: {})
37
+ return if items.nil?
38
+
39
+ opt = variables[:options]
40
+
41
+ all_items = []
42
+ items.each do |i|
43
+ if String.method_defined? :force_encoding
44
+ title = i.title.force_encoding('utf-8').link_urls({format: :markdown})
45
+ note = i.note.map { |line| line.force_encoding('utf-8').strip.link_urls({format: :markdown}) } if i.note
46
+ else
47
+ title = i.title.link_urls({format: :markdown})
48
+ note = i.note.map { |line| line.strip.link_urls({format: :markdown}) } if i.note
49
+ end
50
+
51
+ title = "#{title} @project(#{i.section})" unless variables[:is_single]
52
+
53
+ interval = wwid.get_interval(i, record: true) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
54
+ interval ||= false
55
+
56
+ done = i.title =~ /(?<= |^)@done/ ? 'x' : ' '
57
+
58
+ all_items << {
59
+ date: i.date.strftime('%a %-I:%M%p'),
60
+ shortdate: i.date.relative_date,
61
+ done: done,
62
+ note: note,
63
+ section: i.section,
64
+ time: interval,
65
+ title: title.strip
66
+ }
67
+ end
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']))
71
+ else
72
+ self.template(nil)
73
+ end
74
+
75
+ totals = opt[:totals] ? wwid.tag_times(format: :markdown, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
76
+
77
+ mdx = MarkdownRenderer.new(variables[:page_title], all_items, totals)
78
+ Doing.logger.debug('Markdown Export:', "#{all_items.count} items output to Markdown")
79
+ engine = ERB.new(template)
80
+ @out = engine.result(mdx.get_binding)
81
+ end
82
+
83
+ Doing::Plugins.register 'markdown', :export, self
84
+ end
85
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # title: TaskPaper Export
4
+ # description: Export TaskPaper-friendly data
5
+ # author: Brett Terpstra
6
+ # url: https://brettterpstra.com
7
+ module Doing
8
+ class TaskPaperExport
9
+ include Doing::Util
10
+
11
+ def self.settings
12
+ {
13
+ trigger: 'task(?:paper)?|tp'
14
+ }
15
+ end
16
+
17
+ def self.render(wwid, items, variables: {})
18
+ return if items.nil?
19
+
20
+ options = variables[:options]
21
+
22
+ options[:highlight] = false
23
+ options[:wrap_width] = 0
24
+ options[:tags_color] = false
25
+ options[:output] = 'template'
26
+ options[:template] = '- %title @date(%date)%note'
27
+
28
+ Doing.logger.debug('TaskPaper Export:', "#{items.count} items output to TaskPaper format")
29
+ @out = wwid.list_section(options)
30
+ end
31
+
32
+ Doing::Plugins.register 'taskpaper', :export, self
33
+ end
34
+ end