doing 1.0.8pre → 1.0.10pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -3
- data/bin/doing +27 -10
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +148 -71
- data/lib/doing.rb +0 -1
- metadata +29 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f80fbe9f636b31d608391b7723a14407b515dbe7
|
4
|
+
data.tar.gz: 9aefb646ddc4465ed3b86a0dfc3403d3e1fc69be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc9e0fa945d7cebb0402b3098a0879373907323b3ce6c6a3a5e26a710a2a11d3ec57bd1f1d9a1d3f0a0ec4ebc17b226f92f61f32faa4187e276ce029d43f90be
|
7
|
+
data.tar.gz: d559d691bd3ba1c9a360d08500b720790a99267c84ae05cd7962764b1cddf137f60197bae59fe418f9079609427420b12a9bc959f9e26dc66175e6cf6b8c6e5b
|
data/README.md
CHANGED
@@ -89,7 +89,13 @@ A basic configuration looks like this:
|
|
89
89
|
:include_notes: true
|
90
90
|
|
91
91
|
|
92
|
-
The config file is stored in "~/.doingrc", and is created on the first run.
|
92
|
+
The config file is stored in "~/.doingrc", and is created on the first run.
|
93
|
+
|
94
|
+
### Per-folder configuration
|
95
|
+
|
96
|
+
Any options found in a `.doingrc` anywhere in the hierarchy between your current folder and your home folder will be appended to the base configuration, overriding or extending existing options. This allows you to put a `.doingrc` file into the base of a project and add specific configurations (such as default tags) when working in that project on the command line.
|
97
|
+
|
98
|
+
Any part of the configuration can be copied into these local files and modified. You only need to include the parts you want to change or add.
|
93
99
|
|
94
100
|
### Doing file location
|
95
101
|
|
@@ -125,6 +131,7 @@ The config also contains templates for various command outputs. Include placehol
|
|
125
131
|
- `%note`: Any note in the entry will be included here, a newline and tabs are automatically added.
|
126
132
|
- `%odnote`: The notes with a leading tab removed (outdented note)
|
127
133
|
- `%chompnote`: Notes on one line, beginning and trailing whitespace removed.
|
134
|
+
- `%section`: The section/project the entry is currently in
|
128
135
|
- `%hr`: a horizontal rule (`-`) the width of the terminal
|
129
136
|
- `%hr_under`: a horizontal rule (`_`) the width of the terminal
|
130
137
|
- `%n`: inserts a newline
|
@@ -322,7 +329,7 @@ As mentioned above, `finish` also accepts `--back "2 hours"` (sets the finish da
|
|
322
329
|
|
323
330
|
... will mark the last three entries as "@client @cancelled." Add `-r` as a switch to remove the listed tags instead.
|
324
331
|
|
325
|
-
You can optionally define keywords for common tasks and projects in your `.doingrc` file. When these keywords appear in an item title, they'll automatically be converted into @tags. The "whitelist" tags are exact (but case insensitive) matches. You can also define "synonyms" which will add a tag at the end based on keywords associated with it.
|
332
|
+
You can optionally define keywords for common tasks and projects in your `.doingrc` file. When these keywords appear in an item title, they'll automatically be converted into @tags. The "whitelist" tags are exact (but case insensitive) matches. You can also define "synonyms" which will add a tag at the end based on keywords associated with it. When defining synonym keys, be sure to indent but _not_ hyphenate the keys themselves, while hyphenating the list of synonyms at the same indent level as their key. See "playing" and "writing" in the list below for illustration. Follow standard yaml syntax.
|
326
333
|
|
327
334
|
To add autotagging, include a section like this in your `~/.doingrc` file:
|
328
335
|
|
@@ -489,7 +496,7 @@ Please try not to email me directly about GitHub projects.
|
|
489
496
|
|
490
497
|
I'll try to document some of the code structure as I flesh it out. I'm currently working on adding a CLI reporting structure and logging methods, as well as santizing and standardizing all the flags and switches for consistency. Feel free to [poke around](http://github.com/ttscoff/doing/), I'll try to add more comments in the future (and retroactively).
|
491
498
|
|
492
|
-
|
499
|
+
## Changelog
|
493
500
|
|
494
501
|
#### 1.0.8pre
|
495
502
|
|
data/bin/doing
CHANGED
@@ -1,17 +1,29 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
2
3
|
require 'gli'
|
3
4
|
require 'doing'
|
4
5
|
require 'tempfile'
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def class_exists?(class_name)
|
8
|
+
klass = Module.const_get(class_name)
|
9
|
+
return klass.is_a?(Class)
|
10
|
+
rescue NameError
|
11
|
+
return false
|
9
12
|
end
|
10
13
|
|
14
|
+
if class_exists? 'Encoding'
|
15
|
+
Encoding.default_external = Encoding::UTF_8 if Encoding.respond_to?('default_external')
|
16
|
+
Encoding.default_internal = Encoding::UTF_8 if Encoding.respond_to?('default_internal')
|
17
|
+
end
|
18
|
+
|
19
|
+
|
11
20
|
include GLI::App
|
12
21
|
version Doing::VERSION
|
13
22
|
|
14
23
|
wwid = WWID.new
|
24
|
+
wwid.user_home = %x{echo $HOME}.strip
|
25
|
+
wwid.configure
|
26
|
+
|
15
27
|
|
16
28
|
program_desc 'A CLI for a What Was I Doing system'
|
17
29
|
|
@@ -120,7 +132,7 @@ command :note do |c|
|
|
120
132
|
|
121
133
|
input = args.length > 0 ? args.join(" ") : ""
|
122
134
|
|
123
|
-
prev_input = wwid.last_note(section)
|
135
|
+
prev_input = wwid.last_note(section) || ""
|
124
136
|
if prev_input.class == Array
|
125
137
|
prev_input = prev_input.join("\n")
|
126
138
|
end
|
@@ -726,8 +738,13 @@ command :on do |c|
|
|
726
738
|
start = wwid.chronify(date_string)
|
727
739
|
finish = false
|
728
740
|
end
|
741
|
+
|
729
742
|
exit_now! "Unrecognized date string" unless start
|
730
743
|
|
744
|
+
message = "Date interpreted as #{start}"
|
745
|
+
message += " to #{finish}" if finish
|
746
|
+
wwid.results.push(message)
|
747
|
+
|
731
748
|
options[:t] = true if options[:totals]
|
732
749
|
|
733
750
|
puts wwid.list_date([start,finish],options[:s],options[:t],options[:output],{:totals => options[:totals]}).chomp
|
@@ -993,22 +1010,22 @@ command :config do |c|
|
|
993
1010
|
c.action do |global_options,options,args|
|
994
1011
|
if `uname` =~ /Darwins/
|
995
1012
|
if options[:x]
|
996
|
-
%x{open -a "#{wwid.config['editor_app']}" "#{File.join(Dir.home,
|
1013
|
+
%x{open -a "#{wwid.config['editor_app']}" "#{File.join(Dir.home, wwid.default_config_file)}"}
|
997
1014
|
elsif options[:a] || options[:b]
|
998
1015
|
if options[:a]
|
999
|
-
%x{open -a "#{options[:a]}" "#{File.join(Dir.home,
|
1016
|
+
%x{open -a "#{options[:a]}" "#{File.join(Dir.home, wwid.default_config_file)}"}
|
1000
1017
|
elsif options[:b]
|
1001
|
-
%x{open -b #{options[:b]} "#{File.join(Dir.home,
|
1018
|
+
%x{open -b #{options[:b]} "#{File.join(Dir.home, wwid.default_config_file)}"}
|
1002
1019
|
end
|
1003
1020
|
else
|
1004
1021
|
raise "No EDITOR variable defined in environment" if options[:e].nil? && ENV['EDITOR'].nil?
|
1005
1022
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
1006
|
-
system %Q{#{editor} "#{File.
|
1023
|
+
system %Q{#{editor} "#{File.join(Dir.home, wwid.default_config_file)}"}
|
1007
1024
|
end
|
1008
1025
|
else
|
1009
1026
|
raise "No EDITOR variable defined in environment" if options[:e].nil? && ENV['EDITOR'].nil?
|
1010
1027
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
1011
|
-
system %Q{#{editor} "#{File.join(Dir.home,
|
1028
|
+
system %Q{#{editor} "#{File.join(Dir.home, wwid.default_config_file)}"}
|
1012
1029
|
end
|
1013
1030
|
end
|
1014
1031
|
end
|
@@ -1047,7 +1064,7 @@ end
|
|
1047
1064
|
post do |global,command,options,args|
|
1048
1065
|
# Use skips_post before a command to skip this
|
1049
1066
|
# block on that command only
|
1050
|
-
puts wwid.results.join("\n")
|
1067
|
+
$stderr.puts wwid.results.join("\n")
|
1051
1068
|
end
|
1052
1069
|
|
1053
1070
|
on_error do |exception|
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -9,18 +9,47 @@ class String
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
def link_urls(opt={})
|
13
|
+
opt[:format] ||= :html
|
14
|
+
if opt[:format] == :html
|
15
|
+
self.gsub(/(?mi)((http|https):\/\/)?([\w\-_]+(\.[\w\-_]+)+)([\w\-\.,\@?^=%&:\/~\+#]*[\w\-\@^=%&\/~\+#])?/) {|match|
|
16
|
+
m = Regexp.last_match
|
17
|
+
proto = m[1].nil? ? "http://" : ""
|
18
|
+
%Q{<a href="#{proto}#{m[0]}" title="link to #{m[0]}">[#{m[3]}]</a>}
|
19
|
+
}.gsub(/\<(\w+:.*?)\>/) {|match|
|
20
|
+
m = Regexp.last_match
|
21
|
+
unless m[1] =~ /<a href/
|
22
|
+
%Q{<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>}
|
23
|
+
else
|
24
|
+
match
|
25
|
+
end
|
26
|
+
}
|
27
|
+
else
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
12
31
|
end
|
13
32
|
|
14
33
|
class WWID
|
15
|
-
attr_accessor :content, :sections, :current_section, :doing_file, :config, :results
|
16
|
-
|
34
|
+
attr_accessor :content, :sections, :current_section, :doing_file, :config, :user_home, :default_config_file, :results
|
17
35
|
|
18
36
|
def initialize
|
19
37
|
@content = {}
|
38
|
+
@doingrc_needs_update = false
|
39
|
+
@default_config_file = '.doingrc'
|
40
|
+
end
|
41
|
+
|
42
|
+
def configure(opt={})
|
20
43
|
@timers = {}
|
44
|
+
@config_file == File.join(@user_home, @default_config_file)
|
45
|
+
|
21
46
|
@config = read_config
|
47
|
+
user_config = @config.dup
|
22
48
|
@results = []
|
23
49
|
|
50
|
+
@config['autotag'] ||= {}
|
51
|
+
@config['autotag']['whitelist'] ||= []
|
52
|
+
@config['autotag']['synonyms'] ||= {}
|
24
53
|
@config['doing_file'] ||= "~/what_was_i_doing.md"
|
25
54
|
@config['current_section'] ||= 'Currently'
|
26
55
|
@config['editor_app'] ||= nil
|
@@ -75,11 +104,19 @@ class WWID
|
|
75
104
|
|
76
105
|
@config[:include_notes] ||= true
|
77
106
|
|
78
|
-
File.open(
|
107
|
+
File.open(@config_file, 'w') { |yf| YAML::dump(@config, yf) } if @config != user_config || @doingrc_needs_update
|
108
|
+
|
109
|
+
|
110
|
+
@config = @config.deep_merge(@local_config)
|
111
|
+
|
112
|
+
@current_section = @config['current_section']
|
113
|
+
@default_template = @config['templates']['default']['template']
|
114
|
+
@default_date_format = @config['templates']['default']['date_format']
|
115
|
+
|
79
116
|
end
|
80
117
|
|
81
118
|
def init_doing_file(input=nil)
|
82
|
-
@doing_file = File.expand_path(config['doing_file'])
|
119
|
+
@doing_file = File.expand_path(@config['doing_file'])
|
83
120
|
|
84
121
|
if input.nil?
|
85
122
|
create(@doing_file) unless File.exists?(@doing_file)
|
@@ -114,7 +151,7 @@ class WWID
|
|
114
151
|
elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*)/
|
115
152
|
date = Time.parse($1)
|
116
153
|
title = $2
|
117
|
-
@content[section]['items'].push({'title' => title, 'date' => date})
|
154
|
+
@content[section]['items'].push({'title' => title, 'date' => date, 'section' => section})
|
118
155
|
current += 1
|
119
156
|
else
|
120
157
|
# if content[section]['items'].length - 1 == current
|
@@ -127,7 +164,7 @@ class WWID
|
|
127
164
|
unless @content[section]['items'][current - 1].has_key? 'note'
|
128
165
|
@content[section]['items'][current - 1]['note'] = []
|
129
166
|
end
|
130
|
-
@content[section]['items'][current - 1]['note'].push(line)
|
167
|
+
@content[section]['items'][current - 1]['note'].push(line.gsub(/ *$/,''))
|
131
168
|
end
|
132
169
|
end
|
133
170
|
# end
|
@@ -146,27 +183,42 @@ class WWID
|
|
146
183
|
end
|
147
184
|
end
|
148
185
|
|
149
|
-
def
|
150
|
-
if Dir.respond_to?('home')
|
151
|
-
File.join(Dir.home, DOING_CONFIG_NAME)
|
152
|
-
else
|
153
|
-
File.join(File.expand_path("~"), DOING_CONFIG_NAME)
|
154
|
-
end
|
155
|
-
end
|
186
|
+
def find_local_config
|
156
187
|
|
157
|
-
def read_config
|
158
188
|
config = {}
|
159
189
|
dir = Dir.pwd
|
160
|
-
|
161
|
-
|
162
|
-
|
190
|
+
|
191
|
+
local_config_file = nil
|
192
|
+
|
193
|
+
while (dir != '/' && (dir =~ /[A-Z]:\//) == nil)
|
194
|
+
if File.exists? File.join(dir, @default_config_file)
|
195
|
+
return File.join(dir, @default_config_file)
|
163
196
|
end
|
197
|
+
|
164
198
|
dir = File.dirname(dir)
|
165
199
|
end
|
166
|
-
|
167
|
-
|
200
|
+
|
201
|
+
false
|
202
|
+
end
|
203
|
+
|
204
|
+
def read_config
|
205
|
+
if Dir.respond_to?('home')
|
206
|
+
@config_file = File.join(Dir.home, @default_config_file)
|
207
|
+
else
|
208
|
+
@config_file = File.join(File.expand_path("~"), @default_config_file)
|
209
|
+
end
|
210
|
+
@doingrc_needs_update = true if File.exists? @config_file
|
211
|
+
additional = find_local_config
|
212
|
+
|
213
|
+
begin
|
214
|
+
@config = YAML.load_file(@config_file) || {} if File.exists?(@config_file)
|
215
|
+
@local_config = YAML.load_file(additional) || {} if additional
|
216
|
+
@config.deep_merge(@local_config)
|
217
|
+
rescue
|
218
|
+
@config = {}
|
219
|
+
@local_config = {}
|
220
|
+
# raise "error reading config"
|
168
221
|
end
|
169
|
-
config
|
170
222
|
end
|
171
223
|
|
172
224
|
def fork_editor(input="")
|
@@ -220,38 +272,31 @@ class WWID
|
|
220
272
|
[title, note]
|
221
273
|
end
|
222
274
|
|
223
|
-
# Converts
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
275
|
+
# Converts `input` string into a Time object when `input` takes on the
|
276
|
+
# following formats:
|
277
|
+
# - interval format e.g. '1d2h30m', '45m' etc.
|
278
|
+
# - a semantic phrase e.g. 'yesterday 5:30pm'
|
279
|
+
# - a strftime e.g. '2016-03-15 15:32:04 PDT'
|
228
280
|
def chronify(input)
|
281
|
+
now = Time.now
|
282
|
+
raise "Invalid time expression #{input.inspect}" if input.to_s.strip == ""
|
283
|
+
secs_ago = if input.match /^(\d+)$/
|
284
|
+
# plain number, assume minutes
|
285
|
+
$1.to_i * 60
|
286
|
+
elsif (m = input.match /^((?<day>\d+)d)?((?<hour>\d+)h)?((?<min>\d+)m)?$/i)
|
287
|
+
# day/hour/minute format e.g. 1d2h30m
|
288
|
+
[[m['day'], 24*3600],
|
289
|
+
[m['hour'], 3600],
|
290
|
+
[m['min'], 60]].map {|qty, secs| qty ? (qty.to_i * secs) : 0 }.reduce(0, :+)
|
291
|
+
end
|
229
292
|
|
230
|
-
|
231
|
-
|
232
|
-
if had_to_try.class == FalseClass
|
233
|
-
if input =~ /^(\d+)([mhd])?$/i
|
234
|
-
amt = $1
|
235
|
-
type = $2.nil? ? "m" : $2
|
236
|
-
input = case type.downcase
|
237
|
-
when 'm'
|
238
|
-
amt + " minutes ago"
|
239
|
-
when 'h'
|
240
|
-
amt + " hours ago"
|
241
|
-
when 'd'
|
242
|
-
amt + " days ago"
|
243
|
-
else
|
244
|
-
input
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
Chronic.parse(input, {:context => :past, :ambiguous_time_range => 8})
|
293
|
+
if secs_ago
|
294
|
+
now - secs_ago
|
249
295
|
else
|
250
|
-
|
296
|
+
Chronic.parse(input, {:context => :past, :ambiguous_time_range => 8})
|
251
297
|
end
|
252
298
|
end
|
253
299
|
|
254
|
-
|
255
300
|
# Converts simple strings into seconds that can be added to a Time object
|
256
301
|
# Params:
|
257
302
|
# +qty+:: HH:MM or XX[dhm][[XXhm][XXm]] (1d2h30m, 45m, 1.5d, 1h20m, etc.)
|
@@ -308,13 +353,13 @@ class WWID
|
|
308
353
|
input = STDIN.gets
|
309
354
|
if input =~ /^y/i
|
310
355
|
add_section(frag.cap_first)
|
311
|
-
write(doing_file)
|
356
|
+
write(@doing_file)
|
312
357
|
return frag.cap_first
|
313
358
|
end
|
314
359
|
raise "Unknown section: #{frag}"
|
315
360
|
end
|
316
361
|
end
|
317
|
-
section ? section.cap_first :
|
362
|
+
section ? section.cap_first : guessed
|
318
363
|
end
|
319
364
|
|
320
365
|
def guess_view(frag,guessed=false)
|
@@ -349,9 +394,21 @@ class WWID
|
|
349
394
|
|
350
395
|
title = [title.strip.cap_first] + @config['default_tags'].map{|t| '@' + t.sub(/^ *@/,'').chomp}
|
351
396
|
title = autotag(title.join(' '))
|
352
|
-
|
397
|
+
unless @config['default_tags'].empty?
|
398
|
+
title += @config['default_tags'].map{|t|
|
399
|
+
unless t.nil?
|
400
|
+
dt = t.sub(/^ *@/,'').chomp
|
401
|
+
if title =~ /@#{dt}/
|
402
|
+
""
|
403
|
+
else
|
404
|
+
'@' + dt
|
405
|
+
end
|
406
|
+
end
|
407
|
+
}.delete_if {|t| t == "" }.join(" ")
|
408
|
+
end
|
409
|
+
entry = {'title' => title.strip, 'date' => opt[:back]}
|
353
410
|
unless opt[:note] =~ /^\s*$/s
|
354
|
-
entry['note'] = opt[:note]
|
411
|
+
entry['note'] = opt[:note].map {|n| n.gsub(/ *$/,'')}
|
355
412
|
end
|
356
413
|
items = @content[section]['items']
|
357
414
|
if opt[:timed]
|
@@ -462,7 +519,9 @@ class WWID
|
|
462
519
|
|
463
520
|
if opt[:archive] && section != "Archive" && opt[:count] > 0
|
464
521
|
# concat [count] items from [section] and archive section
|
465
|
-
archived = @content[section]['items'][0..opt[:count]-1].
|
522
|
+
archived = @content[section]['items'][0..opt[:count]-1].map {|i|
|
523
|
+
i['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{i['section']}")
|
524
|
+
}.concat(@content['Archive']['items'])
|
466
525
|
# chop [count] items off of [section] items
|
467
526
|
@content[opt[:section]]['items'] = @content[opt[:section]]['items'][opt[:count]..-1]
|
468
527
|
# overwrite archive section with concatenated array
|
@@ -543,7 +602,9 @@ class WWID
|
|
543
602
|
|
544
603
|
if opt[:archive] && opt[:section] != "Archive"
|
545
604
|
@results.push(%Q{Completed and archived "#{@content[opt[:section]]['items'][i]['title']}"})
|
546
|
-
@content[
|
605
|
+
archive_item = @content[opt[:section]]['items'][i]
|
606
|
+
archive_item['title'] = i['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{i['section']}")
|
607
|
+
@content['Archive']['items'].push(archive_item)
|
547
608
|
@content[opt[:section]]['items'].delete_at(i)
|
548
609
|
else
|
549
610
|
@results.push(%Q{Completed "#{@content[opt[:section]]['items'][i]['title']}"})
|
@@ -555,9 +616,9 @@ class WWID
|
|
555
616
|
|
556
617
|
if opt[:new_item]
|
557
618
|
title, note = format_input(opt[:new_item])
|
558
|
-
note.push(opt[:note]) if opt[:note]
|
619
|
+
note.push(opt[:note].gsub(/ *$/,'')) if opt[:note]
|
559
620
|
title += " @#{tag}"
|
560
|
-
add_item(title.cap_first, opt[:section], {:note => note, :back => opt[:back]})
|
621
|
+
add_item(title.cap_first, opt[:section], {:note => note.gsub(/ *$/,''), :back => opt[:back]})
|
561
622
|
end
|
562
623
|
|
563
624
|
write(@doing_file)
|
@@ -748,7 +809,7 @@ class WWID
|
|
748
809
|
raise "Unknown output format" unless opt[:output] =~ /(template|html|csv|json|timeline)/
|
749
810
|
end
|
750
811
|
if opt[:output] == "csv"
|
751
|
-
output = [CSV.generate_line(['date','title','note','timer'])]
|
812
|
+
output = [CSV.generate_line(['date','title','note','timer','section'])]
|
752
813
|
items.each {|i|
|
753
814
|
note = ""
|
754
815
|
if i['note']
|
@@ -759,7 +820,7 @@ class WWID
|
|
759
820
|
interval = get_interval(i, false)
|
760
821
|
end
|
761
822
|
interval ||= 0
|
762
|
-
output.push(CSV.generate_line([i['date'],i['title'],note,interval]))
|
823
|
+
output.push(CSV.generate_line([i['date'],i['title'],note,interval,i['section']]))
|
763
824
|
}
|
764
825
|
out = output.join("")
|
765
826
|
elsif opt[:output] == "json" || opt[:output] == "timeline"
|
@@ -768,12 +829,12 @@ class WWID
|
|
768
829
|
max = items[-1]['date'].strftime('%F')
|
769
830
|
min = items[0]['date'].strftime('%F')
|
770
831
|
items.each_with_index {|i,index|
|
771
|
-
if
|
832
|
+
if String.method_defined? :force_encoding
|
772
833
|
title = i['title'].force_encoding('utf-8')
|
773
834
|
note = i['note'].map {|line| line.force_encoding('utf-8').strip } if i['note']
|
774
835
|
else
|
775
836
|
title = i['title']
|
776
|
-
note = i['note'].map { |line| line.strip }
|
837
|
+
note = i['note'].map { |line| line.strip } if i['note']
|
777
838
|
end
|
778
839
|
|
779
840
|
if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
@@ -869,12 +930,12 @@ EOTEMPLATE
|
|
869
930
|
# else
|
870
931
|
# note = ''
|
871
932
|
# end
|
872
|
-
if
|
873
|
-
title = i['title'].force_encoding('utf-8')
|
874
|
-
note = i['note'].map {|line| line.force_encoding('utf-8').strip } if i['note']
|
933
|
+
if String.method_defined? :force_encoding
|
934
|
+
title = i['title'].force_encoding('utf-8').link_urls
|
935
|
+
note = i['note'].map {|line| line.force_encoding('utf-8').strip.link_urls } if i['note']
|
875
936
|
else
|
876
|
-
title = i['title']
|
877
|
-
note = i['note'].map { |line| line.strip }
|
937
|
+
title = i['title'].link_urls
|
938
|
+
note = i['note'].map { |line| line.strip.link_urls } if i['note']
|
878
939
|
end
|
879
940
|
|
880
941
|
if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
@@ -886,11 +947,12 @@ EOTEMPLATE
|
|
886
947
|
:date => i['date'].strftime('%a %-I:%M%p'),
|
887
948
|
:title => title.gsub(/(@[^ \(]+(\(.*?\))?)/im,'<span class="tag">\1</span>').strip, #+ " #{note}"
|
888
949
|
:note => note,
|
889
|
-
:time => interval
|
950
|
+
:time => interval,
|
951
|
+
:section => i['section']
|
890
952
|
}
|
891
953
|
}
|
892
954
|
|
893
|
-
style = "body{background:#fff;color:#333;font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:16px;line-height:120%;text-align:justify;padding:20px}h1{text-align:left;position:relative;left:220px;margin-bottom:1em}ul{list-style-position:outside;position:relative;left:170px;margin-right:170px;text-align:left}ul li{list-style-type:none;border-left:solid 1px #ccc;padding-left:10px;line-height:2;position:relative}ul li .date{font-size:14px;position:absolute;left:-122px;color:#7d9ca2;text-align:right;width:110px;line-height:2}ul li .tag{color:#999}ul li .note{display:block;color:#666;padding:0 0 0 22px;line-height:1.4;font-size:15px}ul li .note:before{content:'\\25BA';font-weight:300;position:absolute;left:40px;font-size:8px;color:#aaa;line-height:3}ul li:hover .note{display:block}span.time{color:#729953;float:left;position:relative;padding:0 5px;font-size:15px;border-bottom:dashed 1px #ccc;text-align:right;background:#f9fced;margin-right:4px}table td{border-bottom:solid 1px #ddd;height:24px}caption{text-align:left;border-bottom:solid 1px #aaa;margin:10px 0}table{width:400px;margin:50px 0 0 211px}th{padding-bottom:10px}th,td{padding-right:20px}table{max-width:400px;margin:50px 0 0 221px}"
|
955
|
+
style = "body{background:#fff;color:#333;font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:16px;line-height:120%;text-align:justify;padding:20px}h1{text-align:left;position:relative;left:220px;margin-bottom:1em}ul{list-style-position:outside;position:relative;left:170px;margin-right:170px;text-align:left}ul li{list-style-type:none;border-left:solid 1px #ccc;padding-left:10px;line-height:2;position:relative}ul li .date{font-size:14px;position:absolute;left:-122px;color:#7d9ca2;text-align:right;width:110px;line-height:2}ul li .tag{color:#999}ul li .note{display:block;color:#666;padding:0 0 0 22px;line-height:1.4;font-size:15px}ul li .note:before{content:'\\25BA';font-weight:300;position:absolute;left:40px;font-size:8px;color:#aaa;line-height:3}ul li:hover .note{display:block}span.time{color:#729953;float:left;position:relative;padding:0 5px;font-size:15px;border-bottom:dashed 1px #ccc;text-align:right;background:#f9fced;margin-right:4px}table td{border-bottom:solid 1px #ddd;height:24px}caption{text-align:left;border-bottom:solid 1px #aaa;margin:10px 0}table{width:400px;margin:50px 0 0 211px}th{padding-bottom:10px}th,td{padding-right:20px}table{max-width:400px;margin:50px 0 0 221px}ul li .section{color:#dbbfad;border-left:solid 1px #dbbfad;border-right:solid 1px #dbbfad;border-radius:25px;padding:0 4px;line-height:1 !important;font-size:.8em}ul li .section:hover{color:#c5753f}ul li a:link{color:#64a9a5;text-decoration:none;background-color:rgba(203,255,251,.15)}"
|
894
956
|
template =<<EOT
|
895
957
|
!!!
|
896
958
|
%html
|
@@ -908,6 +970,7 @@ EOTEMPLATE
|
|
908
970
|
%li
|
909
971
|
%span.date= i[:date]
|
910
972
|
= i[:title]
|
973
|
+
%span.section= i[:section]
|
911
974
|
- if i[:time] && i[:time] != "00:00:00"
|
912
975
|
%span.time= i[:time]
|
913
976
|
- if i[:note]
|
@@ -977,6 +1040,9 @@ EOT
|
|
977
1040
|
flag+item['title'].chomp+reset
|
978
1041
|
end
|
979
1042
|
}
|
1043
|
+
|
1044
|
+
output.sub!(/%section/,item['section']) if item['section']
|
1045
|
+
|
980
1046
|
if opt[:tags_color]
|
981
1047
|
output.gsub!(/\s(@\S+(?:\(.*?\))?)/," #{colors[opt[:tags_color]]}\\1")
|
982
1048
|
end
|
@@ -1066,7 +1132,8 @@ EOT
|
|
1066
1132
|
}
|
1067
1133
|
moved_items.each {|item|
|
1068
1134
|
if label
|
1069
|
-
item['title']
|
1135
|
+
item['title'] = item['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{section}") unless section == "Currently"
|
1136
|
+
# item['title'] += " @from(#{section})" unless section == "Currently" || item['title'] =~ /@from\(/
|
1070
1137
|
end
|
1071
1138
|
}
|
1072
1139
|
@content[section]['items'] = moved_items
|
@@ -1083,9 +1150,11 @@ EOT
|
|
1083
1150
|
|
1084
1151
|
items.each{|item|
|
1085
1152
|
if label
|
1086
|
-
item['title']
|
1153
|
+
item['title'] = item['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{section}") unless section == "Currently"
|
1154
|
+
# item['title'] += " @from(#{section})" unless section == "Currently"
|
1087
1155
|
end
|
1088
1156
|
}
|
1157
|
+
|
1089
1158
|
@content[destination]['items'] += items[count..-1]
|
1090
1159
|
@results.push("Archived #{items.length - count} items from #{section} to #{destination}")
|
1091
1160
|
end
|
@@ -1254,7 +1323,10 @@ EOS
|
|
1254
1323
|
def autotag(title)
|
1255
1324
|
return unless title
|
1256
1325
|
@config['autotag']['whitelist'].each {|tag|
|
1257
|
-
title.sub!(/(?<!@)(#{tag.strip})\b/i
|
1326
|
+
title.sub!(/(?<!@)(#{tag.strip})\b/i) do |m|
|
1327
|
+
m.downcase! if tag =~ /[a-z]/
|
1328
|
+
"@#{m}"
|
1329
|
+
end unless title =~ /@#{tag}\b/i
|
1258
1330
|
}
|
1259
1331
|
tail_tags = []
|
1260
1332
|
@config['autotag']['synonyms'].each {|tag, v|
|
@@ -1264,7 +1336,11 @@ EOS
|
|
1264
1336
|
end
|
1265
1337
|
}
|
1266
1338
|
}
|
1267
|
-
|
1339
|
+
if tail_tags.length > 0
|
1340
|
+
title + ' ' + tail_tags.uniq.map {|t| '@'+t }.join(' ')
|
1341
|
+
else
|
1342
|
+
title
|
1343
|
+
end
|
1268
1344
|
end
|
1269
1345
|
|
1270
1346
|
def autotag_item(item)
|
@@ -1293,7 +1369,7 @@ EOS
|
|
1293
1369
|
seconds = (done - start).to_i
|
1294
1370
|
|
1295
1371
|
item['title'].scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each {|m|
|
1296
|
-
k = m[0] == "done" ? "All" : m[0]
|
1372
|
+
k = m[0] == "done" ? "All" : m[0].downcase
|
1297
1373
|
if @timers.has_key?(k)
|
1298
1374
|
@timers[k] += seconds
|
1299
1375
|
else
|
@@ -1310,8 +1386,9 @@ EOS
|
|
1310
1386
|
minutes = (seconds / 60).to_i
|
1311
1387
|
hours = (minutes / 60).to_i
|
1312
1388
|
days = (hours / 24).to_i
|
1313
|
-
hours = (hours %
|
1389
|
+
hours = (hours % 24).to_i
|
1314
1390
|
minutes = (minutes % 60).to_i
|
1315
1391
|
[days, hours, minutes]
|
1316
1392
|
end
|
1393
|
+
|
1317
1394
|
end
|
data/lib/doing.rb
CHANGED
metadata
CHANGED
@@ -1,77 +1,77 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.10pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-11-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rdoc
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '4.1'
|
34
|
-
- -
|
34
|
+
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: 4.1.1
|
37
37
|
type: :development
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- - ~>
|
41
|
+
- - "~>"
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '4.1'
|
44
|
-
- -
|
44
|
+
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 4.1.1
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: aruba
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - ~>
|
51
|
+
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0'
|
54
54
|
type: :development
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- - ~>
|
58
|
+
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: gli
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- -
|
65
|
+
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: 2.
|
67
|
+
version: 2.17.1
|
68
68
|
type: :runtime
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- -
|
72
|
+
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: 2.
|
74
|
+
version: 2.17.1
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: haml
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,48 +90,48 @@ dependencies:
|
|
90
90
|
name: chronic
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- - ~>
|
93
|
+
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '0.10'
|
96
|
-
- -
|
96
|
+
- - ">="
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: 0.10.2
|
99
99
|
type: :runtime
|
100
100
|
prerelease: false
|
101
101
|
version_requirements: !ruby/object:Gem::Requirement
|
102
102
|
requirements:
|
103
|
-
- - ~>
|
103
|
+
- - "~>"
|
104
104
|
- !ruby/object:Gem::Version
|
105
105
|
version: '0.10'
|
106
|
-
- -
|
106
|
+
- - ">="
|
107
107
|
- !ruby/object:Gem::Version
|
108
108
|
version: 0.10.2
|
109
109
|
- !ruby/object:Gem::Dependency
|
110
110
|
name: deep_merge
|
111
111
|
requirement: !ruby/object:Gem::Requirement
|
112
112
|
requirements:
|
113
|
-
- -
|
113
|
+
- - ">="
|
114
114
|
- !ruby/object:Gem::Version
|
115
115
|
version: '0'
|
116
116
|
type: :runtime
|
117
117
|
prerelease: false
|
118
118
|
version_requirements: !ruby/object:Gem::Requirement
|
119
119
|
requirements:
|
120
|
-
- -
|
120
|
+
- - ">="
|
121
121
|
- !ruby/object:Gem::Version
|
122
122
|
version: '0'
|
123
123
|
- !ruby/object:Gem::Dependency
|
124
124
|
name: json
|
125
125
|
requirement: !ruby/object:Gem::Requirement
|
126
126
|
requirements:
|
127
|
-
- - ~>
|
127
|
+
- - "~>"
|
128
128
|
- !ruby/object:Gem::Version
|
129
129
|
version: 1.8.1
|
130
130
|
type: :runtime
|
131
131
|
prerelease: false
|
132
132
|
version_requirements: !ruby/object:Gem::Requirement
|
133
133
|
requirements:
|
134
|
-
- - ~>
|
134
|
+
- - "~>"
|
135
135
|
- !ruby/object:Gem::Version
|
136
136
|
version: 1.8.1
|
137
137
|
description: A tool for managing a TaskPaper-like file of recent activites. Perfect
|
@@ -155,29 +155,29 @@ licenses:
|
|
155
155
|
metadata: {}
|
156
156
|
post_install_message:
|
157
157
|
rdoc_options:
|
158
|
-
- --title
|
158
|
+
- "--title"
|
159
159
|
- doing
|
160
|
-
- --main
|
160
|
+
- "--main"
|
161
161
|
- README.md
|
162
|
-
- --markup
|
162
|
+
- "--markup"
|
163
163
|
- markdown
|
164
|
-
- -ri
|
164
|
+
- "-ri"
|
165
165
|
require_paths:
|
166
166
|
- lib
|
167
167
|
- lib
|
168
168
|
required_ruby_version: !ruby/object:Gem::Requirement
|
169
169
|
requirements:
|
170
|
-
- -
|
170
|
+
- - ">="
|
171
171
|
- !ruby/object:Gem::Version
|
172
172
|
version: '0'
|
173
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
174
174
|
requirements:
|
175
|
-
- -
|
175
|
+
- - ">"
|
176
176
|
- !ruby/object:Gem::Version
|
177
177
|
version: 1.3.1
|
178
178
|
requirements: []
|
179
179
|
rubyforge_project:
|
180
|
-
rubygems_version: 2.
|
180
|
+
rubygems_version: 2.6.13
|
181
181
|
signing_key:
|
182
182
|
specification_version: 4
|
183
183
|
summary: A command line tool for managing What Was I Doing reminders
|