doing 1.0.90 → 1.0.91
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -12
- data/bin/doing +62 -32
- data/lib/doing/helpers.rb +81 -11
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +122 -23
- data/lib/doing.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 365e58ed7377d560d531ce2a1585d115c0a1b73fc3039621a2fef230f52de5dc
|
4
|
+
data.tar.gz: 0efbf78887502113048946df9942e58ad751921b4fc997efd7556ba40e428bb5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53fba5968f465d3dc40821360d60f2ff63573b8e5b47e9eb851fbf6592a3a7baea0436efb118d08d4f92e10f035a5f9ee5fa47ae5df65aa6929c40146a0a6f5f
|
7
|
+
data.tar.gz: 6a98c715220355434ed2e05e4582d04ddd7eade5f4588ea4e881a025654ac5e0d90df1f845f73406a50bda14d887540f34254ffe8de50252840a2c070cbb5c75
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
**A command line tool for remembering what you were doing and tracking what you've done.**
|
4
4
|
|
5
|
-
_If you're one of the
|
5
|
+
_If you're one of the rarev people like me who find this useful, feel free to [buy me some coffee](http://brettterpstra.com/donate/)._
|
6
6
|
|
7
7
|
## Contents
|
8
8
|
|
@@ -27,7 +27,7 @@ If there's something I want to look at later but doesn't need to be added to a t
|
|
27
27
|
|
28
28
|
## Installation
|
29
29
|
|
30
|
-
The current version of `doing` is <!--VER-->1.0.
|
30
|
+
The current version of `doing` is <!--VER-->1.0.90<!--END VER-->.
|
31
31
|
|
32
32
|
$ [sudo] gem install doing
|
33
33
|
|
@@ -104,6 +104,7 @@ A basic configuration looks like this:
|
|
104
104
|
html_template:
|
105
105
|
haml:
|
106
106
|
css:
|
107
|
+
markdown:
|
107
108
|
|
108
109
|
|
109
110
|
The config file is stored in `~/.doingrc`, and a skeleton file is created on the first run. Just run `doing` on its own to create the file.
|
@@ -114,7 +115,7 @@ Any options found in a `.doingrc` anywhere in the hierarchy between your current
|
|
114
115
|
|
115
116
|
Possible uses:
|
116
117
|
|
117
|
-
- Define custom HTML output on a per-project basis using the html_template option for custom templates. Customize time tracking reports based on project or client.
|
118
|
+
- Define custom HTML or Markdown output on a per-project basis using the html_template option for custom templates. Customize time tracking reports based on project or client.
|
118
119
|
- Define `default_tags` for a project so that every time you `doing now` from within that project directory or its subfolders, it gets tagged with that project automatically.
|
119
120
|
|
120
121
|
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.
|
@@ -191,7 +192,7 @@ And it outputs:
|
|
191
192
|
12:45pm > I think this thing (doing) is ready to document and distribute
|
192
193
|
$
|
193
194
|
|
194
|
-
You can get pretty clever and include line breaks and other formatting inside of double quotes. If you want
|
195
|
+
You can get pretty clever and include line breaks and other formatting inside of double quotes. If you want multi-line templates, just use `\n` in the template line, and after the next run it will be rewritten as proper YAML automatically.
|
195
196
|
|
196
197
|
For example, this block:
|
197
198
|
|
@@ -300,7 +301,7 @@ Outputs:
|
|
300
301
|
|
301
302
|
![](http://ckyp.us/XKpj+)
|
302
303
|
|
303
|
-
You can also specify a default output format for a view. Most of the optional output formats override the template specification (`html`, `csv`, `json`). If the `view` command is used with the `-o` flag, it will override what's specified for the view in the config.
|
304
|
+
You can also specify a default output format for a view. Most of the optional output formats override the template specification (`html`, `csv`, `json`, `markdown`). If the `view` command is used with the `-o` flag, it will override what's specified for the view in the config.
|
304
305
|
|
305
306
|
### Colors
|
306
307
|
|
@@ -342,13 +343,13 @@ And a few special colors you'll just have to try out to see (or just run `doing
|
|
342
343
|
|
343
344
|
Any time you use one of the foreground colors it will reset the bold and background settings to their default automatically. You can force a reset to default terminal colors using `%default`.
|
344
345
|
|
345
|
-
### HTML Templates
|
346
|
+
### HTML/Markdown Templates
|
346
347
|
|
347
|
-
For commands that provide an HTML output option, you can customize the templates used for markup and CSS. The markup uses [HAML](http://haml.info/), and the styles are pure CSS.
|
348
|
+
For commands that provide an HTML or Markdown output option, you can customize the templates used for markup and CSS. The HTML markup uses [HAML](http://haml.info/), and the styles are pure CSS. The Markdown template uses Ruby ERB templating.
|
348
349
|
|
349
|
-
To export the default configurations for customization, use `doing templates --type=[HAML|CSS]`. This will output to STDOUT where you can pipe it to a file, e.g. `doing templates --type=HAML > my_template.haml`. You can modify the markup, the CSS, or both.
|
350
|
+
To export the default configurations for customization, use `doing templates --type=[HAML|CSS|MARKDOWN]`. This will output to STDOUT where you can pipe it to a file, e.g. `doing templates --type=HAML > my_template.haml`. You can modify the markup, the CSS, or both.
|
350
351
|
|
351
|
-
Once you have
|
352
|
+
Once you have a template file prepared, edit `.doingrc` and look for the `html_template:` section. There are three sub-values, `haml:`, `css:`, and `markdown:`. Add the template paths to the appropriate keys. A tilde may be substituted for your home directory, e.g. `css: ~/styles/doing.css`.
|
352
353
|
|
353
354
|
### Autotagging
|
354
355
|
|
@@ -549,7 +550,7 @@ All of the display commands (view, show, today, yesterday, search) support date
|
|
549
550
|
|
550
551
|
`doing on` allows for full date ranges and filtering. `doing on saturday`, or `doing on one month to today` will give you ranges. You can use the same terms with the `show` command by adding the `-f` or `--from` flag. `doing show @done --from "monday to friday"` will give you all of your completed items for the last week (assuming it's the weekend). There's also `doing since` a simple alias for `doing on PAST_DATE to now`, e.g. `doing since monday`.
|
551
552
|
|
552
|
-
You can also show entries matching a search string with `doing grep` (synonym `doing search`). If you want to search with regular expressions or for an exact match, surround your search query with forward slashes, e.g. `doing search /project name/`. If you pass a search string without slashes, it's treated as a fuzzy search string, meaning matches can be found as long as the characters in the search string are in order and with no more than three other characters between each. By default searches are across all sections, but you can limit it to one with the `-s SECTION_NAME` flag. Searches can be displayed with the default template, or output as HTML, CSV, or JSON.
|
553
|
+
You can also show entries matching a search string with `doing grep` (synonym `doing search`). If you want to search with regular expressions or for an exact match, surround your search query with forward slashes, e.g. `doing search /project name/`. If you pass a search string without slashes, it's treated as a fuzzy search string, meaning matches can be found as long as the characters in the search string are in order and with no more than three other characters between each. By default searches are across all sections, but you can limit it to one with the `-s SECTION_NAME` flag. Searches can be displayed with the default template, or output as HTML, Markdown, CSV, or JSON.
|
553
554
|
|
554
555
|
##### Modifying the last entry
|
555
556
|
|
@@ -618,7 +619,7 @@ Doing can currently only import tasks from Timing.app reports. If you want to sy
|
|
618
619
|
7. Include short entries if desired
|
619
620
|
8. Export the report to a new file
|
620
621
|
|
621
|
-
Now you can run `doing import --type timing -s SECTION PATH`, where SECTION is the name of the section you want to import the entries to (
|
622
|
+
Now you can run `doing import --type timing -s SECTION PATH`, where SECTION is the name of the section you want to import the entries to (defaults to Currently), and PATH is the path to the JSON file. You can also add a tag (or tags) to all entries, or a custom prefix.
|
622
623
|
|
623
624
|
(`--type timing` is the only option right now, so it doesn't need to be included)
|
624
625
|
|
@@ -718,7 +719,7 @@ Ruby is rife with encoding inconsistencies across platforms and versions. Feel f
|
|
718
719
|
### Support
|
719
720
|
|
720
721
|
|
721
|
-
As a free project, `doing` isn't heavily supported, but you can get support from myself and other users on GitHub. If you run into a
|
722
|
+
As a free project, `doing` isn't heavily supported, but you can get support from myself and other users on GitHub. If you run into a replicable bug in your environment, please [post an issue](https://github.com/ttscoff/doing/issues) and include your platform, OS version, and the result of `ruby -v`, along with a copy/paste of the error message. To get a more verbose error message, try running `GLI_DEBUG=true doing [...]` for a full trace.
|
722
723
|
|
723
724
|
Please try not to email me directly about GitHub projects.
|
724
725
|
|
data/bin/doing
CHANGED
@@ -243,23 +243,25 @@ command :meanwhile do |c|
|
|
243
243
|
end
|
244
244
|
end
|
245
245
|
|
246
|
-
desc 'Output HTML and
|
246
|
+
desc 'Output HTML, CSS, and Markdown (ERB) templates for customization'
|
247
247
|
long_desc %(
|
248
248
|
Templates are printed to STDOUT for piping to a file.
|
249
249
|
Save them and use them in the configuration file under html_template.
|
250
250
|
|
251
251
|
Example `doing template HAML > ~/styles/my_doing.haml`
|
252
252
|
)
|
253
|
-
arg_name 'TYPE', must_match: /^(?:html|haml|css)/i
|
253
|
+
arg_name 'TYPE', must_match: /^(?:html|haml|css|markdown|md|erb)/i
|
254
254
|
command :template do |c|
|
255
255
|
c.action do |_global_options, options, args|
|
256
|
-
exit_now! 'No type specified, use `doing template [HAML|CSS]`' if args.empty?
|
256
|
+
exit_now! 'No type specified, use `doing template [HAML|CSS|MARKDOWN]`' if args.empty?
|
257
257
|
|
258
258
|
case args[0]
|
259
259
|
when /html|haml/i
|
260
260
|
$stdout.puts wwid.haml_template
|
261
261
|
when /css/i
|
262
262
|
$stdout.puts wwid.css_template
|
263
|
+
when /markdown|md|erb/i
|
264
|
+
$stdout.puts wwid.markdown_template
|
263
265
|
else
|
264
266
|
exit_now! 'Invalid type specified, must be HAML or CSS'
|
265
267
|
end
|
@@ -321,9 +323,9 @@ command :select do |c|
|
|
321
323
|
c.arg_name 'FILE'
|
322
324
|
c.flag %i[save_to]
|
323
325
|
|
324
|
-
c.desc 'Output entries to format (doing|taskpaper|csv|html|json|template|timeline)'
|
326
|
+
c.desc 'Output entries to format (doing|taskpaper|csv|html|json|template|timeline|taskpaper|markdown)'
|
325
327
|
c.arg_name 'FORMAT'
|
326
|
-
c.flag %i[o output], must_match: /^(?:doing|taskpaper|html|csv|json|template|timeline)$/i
|
328
|
+
c.flag %i[o output], must_match: /^(?:doing|taskpaper|html|csv|json|template|timeline|taskpaper|markdown|md)$/i
|
327
329
|
|
328
330
|
c.action do |_global_options, options, args|
|
329
331
|
exit_now! "--no-menu requires --query" if !options[:menu] && !options[:query]
|
@@ -738,6 +740,19 @@ command [:again, :resume] do |c|
|
|
738
740
|
end
|
739
741
|
|
740
742
|
desc 'Add tag(s) to last entry'
|
743
|
+
long_desc 'Add (or remove) tags from the last entry, or from multiple entries
|
744
|
+
(with `--count`), entries matching a search (with `--search), or entries
|
745
|
+
containing another tag (with `--tag`).
|
746
|
+
|
747
|
+
When removing tags with `-r`, wildcards are allowed (`*` to match
|
748
|
+
multiple characters, `?` to match a single character). With `--regex`,
|
749
|
+
regular expressions will be interpreted instead of wildcards.
|
750
|
+
|
751
|
+
For all tag removals the match is case insensitive by default, but if
|
752
|
+
the tag search string contains any uppercase letters, the match will
|
753
|
+
become case sensitive automatically.
|
754
|
+
|
755
|
+
Tag name arguments do not need to be prefixed with @.'
|
741
756
|
arg_name 'TAG', :multiple
|
742
757
|
command :tag do |c|
|
743
758
|
c.desc 'Section'
|
@@ -748,6 +763,10 @@ command :tag do |c|
|
|
748
763
|
c.arg_name 'COUNT'
|
749
764
|
c.flag %i[c count], default_value: 1
|
750
765
|
|
766
|
+
c.desc 'Replace existing tag with tag argument, wildcards (*,?) allowed, or use with --regex'
|
767
|
+
c.arg_name 'ORIG_TAG'
|
768
|
+
c.flag %i[rename]
|
769
|
+
|
751
770
|
c.desc 'Don\'t ask permission to tag all entries when count is 0'
|
752
771
|
c.switch %i[force], negatable: false, default_value: false
|
753
772
|
|
@@ -757,6 +776,9 @@ command :tag do |c|
|
|
757
776
|
c.desc 'Remove given tag(s)'
|
758
777
|
c.switch %i[r remove], negatable: false, default_value: false
|
759
778
|
|
779
|
+
c.desc 'Interpret tag string as regular expression (with --remove)'
|
780
|
+
c.switch %i[regex], negatable: false, default_value: false
|
781
|
+
|
760
782
|
c.desc 'Tag last entry (or entries) not marked @done'
|
761
783
|
c.switch %i[u unfinished], negatable: false, default_value: false
|
762
784
|
|
@@ -843,19 +865,26 @@ command :tag do |c|
|
|
843
865
|
exit_now! 'Cancelled' unless res
|
844
866
|
end
|
845
867
|
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
868
|
+
options[:count] = count
|
869
|
+
options[:section] = section
|
870
|
+
options[:tag] = search_tags
|
871
|
+
options[:tags] = tags
|
872
|
+
options[:tag_bool] = options[:bool]
|
873
|
+
|
874
|
+
# opts = {
|
875
|
+
# autotag: options[:a],
|
876
|
+
# count: count,
|
877
|
+
# date: options[:date],
|
878
|
+
# iregex: options[:iregex]
|
879
|
+
# remove: options[:r],
|
880
|
+
# search: options[:search],
|
881
|
+
# section: section,
|
882
|
+
# tag: search_tags,
|
883
|
+
# tag_bool: options[:bool],
|
884
|
+
# tags: tags,
|
885
|
+
# unfinished: options[:unfinished]
|
886
|
+
# }
|
887
|
+
wwid.tag_last(options)
|
859
888
|
end
|
860
889
|
end
|
861
890
|
|
@@ -948,9 +977,9 @@ command :show do |c|
|
|
948
977
|
c.desc 'Only show items with recorded time intervals'
|
949
978
|
c.switch [:only_timed], default_value: false, negatable: false
|
950
979
|
|
951
|
-
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
980
|
+
c.desc 'Output to export format (csv|html|json|template|timeline|taskpaper|markdown)'
|
952
981
|
c.arg_name 'FORMAT'
|
953
|
-
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
982
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline|taskpaper|markdown|md)$/i
|
954
983
|
c.action do |_global_options, options, args|
|
955
984
|
tag_filter = false
|
956
985
|
tags = []
|
@@ -1068,9 +1097,9 @@ command [:grep, :search] do |c|
|
|
1068
1097
|
c.arg_name 'DATE_STRING'
|
1069
1098
|
c.flag [:after]
|
1070
1099
|
|
1071
|
-
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1100
|
+
c.desc 'Output to export format (csv|html|json|template|timeline|taskpaper|markdown)'
|
1072
1101
|
c.arg_name 'FORMAT'
|
1073
|
-
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1102
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline|taskpaper|markdown|md)$/i
|
1074
1103
|
|
1075
1104
|
c.desc 'Show time intervals on @done tasks'
|
1076
1105
|
c.switch %i[t times], default_value: true, negatable: true
|
@@ -1178,9 +1207,9 @@ command :today do |c|
|
|
1178
1207
|
c.arg_name 'KEY'
|
1179
1208
|
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
1180
1209
|
|
1181
|
-
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1210
|
+
c.desc 'Output to export format (csv|html|json|template|timeline|taskpaper|markdown)'
|
1182
1211
|
c.arg_name 'FORMAT'
|
1183
|
-
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1212
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline|taskpaper|markdown|md)$/i
|
1184
1213
|
|
1185
1214
|
c.desc 'View entries before specified time (e.g. 8am, 12:30pm, 15:00)'
|
1186
1215
|
c.arg_name 'TIME_STRING'
|
@@ -1226,9 +1255,9 @@ command :on do |c|
|
|
1226
1255
|
c.arg_name 'KEY'
|
1227
1256
|
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
1228
1257
|
|
1229
|
-
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1258
|
+
c.desc 'Output to export format (csv|html|json|template|timeline|taskpaper|markdown)'
|
1230
1259
|
c.arg_name 'FORMAT'
|
1231
|
-
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1260
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline|taskpaper|markdown|md)$/i
|
1232
1261
|
|
1233
1262
|
c.action do |_global_options, options, args|
|
1234
1263
|
exit_now! 'Missing date argument' if args.empty?
|
@@ -1279,9 +1308,9 @@ command :since do |c|
|
|
1279
1308
|
c.arg_name 'KEY'
|
1280
1309
|
c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
|
1281
1310
|
|
1282
|
-
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1311
|
+
c.desc 'Output to export format (csv|html|json|template|timeline|taskpaper|markdown)'
|
1283
1312
|
c.arg_name 'FORMAT'
|
1284
|
-
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1313
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline|taskpaper|markdown|md)$/i
|
1285
1314
|
|
1286
1315
|
c.action do |_global_options, options, args|
|
1287
1316
|
exit_now! 'Missing date argument' if args.empty?
|
@@ -1313,9 +1342,9 @@ command :yesterday do |c|
|
|
1313
1342
|
c.arg_name 'NAME'
|
1314
1343
|
c.flag %i[s section], default_value: 'All'
|
1315
1344
|
|
1316
|
-
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1345
|
+
c.desc 'Output to export format (csv|html|json|template|timeline|taskpaper|markdown)'
|
1317
1346
|
c.arg_name 'FORMAT'
|
1318
|
-
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1347
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline|taskpaper|markdown|md)$/i
|
1319
1348
|
|
1320
1349
|
c.desc 'Show time intervals on @done tasks'
|
1321
1350
|
c.switch %i[t times], default_value: true, negatable: true
|
@@ -1468,9 +1497,9 @@ command :view do |c|
|
|
1468
1497
|
c.arg_name 'COUNT'
|
1469
1498
|
c.flag %i[c count], must_match: /^\d+$/, type: Integer
|
1470
1499
|
|
1471
|
-
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1500
|
+
c.desc 'Output to export format (csv|html|json|template|timeline|taskpaper|markdown)'
|
1472
1501
|
c.arg_name 'FORMAT'
|
1473
|
-
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
|
1502
|
+
c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline|taskpaper|markdown|md)$/i
|
1474
1503
|
|
1475
1504
|
c.desc 'Show time intervals on @done tasks'
|
1476
1505
|
c.switch %i[t times], default_value: true, negatable: true
|
@@ -1932,6 +1961,7 @@ end
|
|
1932
1961
|
post do |global, _command, _options, _args|
|
1933
1962
|
# Use skips_post before a command to skip this
|
1934
1963
|
# block on that command only
|
1964
|
+
|
1935
1965
|
if global[:stdout]
|
1936
1966
|
$stdout.print wwid.results.join("\n")
|
1937
1967
|
else
|
data/lib/doing/helpers.rb
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
##
|
2
|
+
## @brief Date helpers
|
3
|
+
##
|
4
|
+
class ::Time
|
5
|
+
def relative_date
|
6
|
+
if self > Date.today.to_time
|
7
|
+
strftime(' %_I:%M%P')
|
8
|
+
elsif self > (Date.today - 6).to_time
|
9
|
+
strftime('%a %_I:%M%P')
|
10
|
+
elsif self.year == Date.today.year
|
11
|
+
strftime('%m/%d %_I:%M%P')
|
12
|
+
else
|
13
|
+
strftime('%m/%d/%Y %_I:%M%P')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
1
18
|
##
|
2
19
|
## @brief Hash helpers
|
3
20
|
##
|
@@ -84,29 +101,82 @@ class ::String
|
|
84
101
|
end
|
85
102
|
|
86
103
|
|
104
|
+
##
|
105
|
+
## @brief Remove duplicate tags, leaving only first occurrence
|
106
|
+
##
|
107
|
+
## @return Deduplicated string
|
108
|
+
##
|
109
|
+
def dedup_tags!
|
110
|
+
replace dedup_tags
|
111
|
+
end
|
112
|
+
|
113
|
+
def dedup_tags
|
114
|
+
item = self.dup
|
115
|
+
tags = item.scan(/(?<=^| )(\@(\S+?)(\([^)]+\))?)(?=\b|$)/).uniq
|
116
|
+
tags.each do |tag|
|
117
|
+
found = false
|
118
|
+
item.gsub!(/( |^)#{tag[0]}\b/) do |m|
|
119
|
+
if found
|
120
|
+
''
|
121
|
+
else
|
122
|
+
found = true
|
123
|
+
m
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
item
|
128
|
+
end
|
129
|
+
|
87
130
|
##
|
88
131
|
## @brief Turn raw urls into HTML links
|
89
132
|
##
|
90
133
|
## @param opt (Hash) Additional Options
|
91
134
|
##
|
135
|
+
def link_urls!(opt = {})
|
136
|
+
replace link_urls(opt)
|
137
|
+
end
|
138
|
+
|
92
139
|
def link_urls(opt = {})
|
93
140
|
opt[:format] ||= :html
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
end.gsub(/<(\w+:.*?)>/) do |match|
|
141
|
+
str = self.dup
|
142
|
+
|
143
|
+
if :format == :markdown
|
144
|
+
# Remove <self-linked> formatting
|
145
|
+
str.gsub!(/<(.*?)>/) do |match|
|
100
146
|
m = Regexp.last_match
|
101
|
-
if m[1] =~
|
102
|
-
|
147
|
+
if m[1] =~ /^https?:/
|
148
|
+
m[1]
|
103
149
|
else
|
104
|
-
|
150
|
+
match
|
105
151
|
end
|
106
152
|
end
|
107
|
-
else
|
108
|
-
self
|
109
153
|
end
|
154
|
+
|
155
|
+
# Replace qualified urls
|
156
|
+
str.gsub!(%r{(?mi)(?<!["'\[(\\])((http|https)://)([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&:/~+#]*[\w\-@^=%&/~+#])?}) do |_match|
|
157
|
+
m = Regexp.last_match
|
158
|
+
proto = m[1].nil? ? 'http://' : ''
|
159
|
+
case opt[:format]
|
160
|
+
when :html
|
161
|
+
%(<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>)
|
162
|
+
when :markdown
|
163
|
+
"[#{m[0]}](#{proto}#{m[0]})"
|
164
|
+
else
|
165
|
+
m[0]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Clean up unlinked <urls>
|
170
|
+
str.gsub!(/<(\w+:.*?)>/) do |match|
|
171
|
+
m = Regexp.last_match
|
172
|
+
if m[1] =~ /<a href/
|
173
|
+
match
|
174
|
+
else
|
175
|
+
%(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
str
|
110
180
|
end
|
111
181
|
end
|
112
182
|
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -4,6 +4,7 @@ require 'deep_merge'
|
|
4
4
|
require 'open3'
|
5
5
|
require 'pp'
|
6
6
|
require 'shellwords'
|
7
|
+
require 'erb'
|
7
8
|
|
8
9
|
##
|
9
10
|
## @brief Main "What Was I Doing" methods
|
@@ -104,6 +105,7 @@ class WWID
|
|
104
105
|
@config['html_template'] ||= {}
|
105
106
|
@config['html_template']['haml'] ||= nil
|
106
107
|
@config['html_template']['css'] ||= nil
|
108
|
+
@config['html_template']['markdown'] ||= nil
|
107
109
|
|
108
110
|
@config['templates'] ||= {}
|
109
111
|
@config['templates']['default'] ||= {
|
@@ -233,6 +235,15 @@ class WWID
|
|
233
235
|
end
|
234
236
|
end
|
235
237
|
|
238
|
+
##
|
239
|
+
## @brief Return the contents of the ERB template for Markdown output
|
240
|
+
##
|
241
|
+
## @return (String) ERB template
|
242
|
+
##
|
243
|
+
def markdown_template
|
244
|
+
IO.read(File.join(File.dirname(__FILE__), '../templates/doing-markdown.erb'))
|
245
|
+
end
|
246
|
+
|
236
247
|
##
|
237
248
|
## @brief Return the contents of the HAML template for HTML output
|
238
249
|
##
|
@@ -1025,8 +1036,6 @@ class WWID
|
|
1025
1036
|
case opt[:output]
|
1026
1037
|
when /doing/
|
1027
1038
|
options[:template] = '- %date | %title%note'
|
1028
|
-
when /taskpaper/
|
1029
|
-
options[:template] = '- %title @date(%date)%note'
|
1030
1039
|
else
|
1031
1040
|
options[:output] = opt[:output]
|
1032
1041
|
end
|
@@ -1151,10 +1160,31 @@ class WWID
|
|
1151
1160
|
title = item['title']
|
1152
1161
|
opt[:tags].each do |tag|
|
1153
1162
|
tag = tag.strip
|
1154
|
-
if opt[:remove]
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1163
|
+
if opt[:remove] || opt[:rename]
|
1164
|
+
case_sensitive = tag !~ /[A-Z]/
|
1165
|
+
replacement = ''
|
1166
|
+
if opt[:rename]
|
1167
|
+
replacement = tag
|
1168
|
+
tag = opt[:rename]
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
if opt[:regex]
|
1172
|
+
rx_tag = tag.gsub(/\./, '\S')
|
1173
|
+
else
|
1174
|
+
rx_tag = tag.gsub(/\?/, '.').gsub(/\*/, '\S*?')
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
if title =~ / @#{rx_tag}\b/
|
1178
|
+
rx = Regexp.new("(^| )@#{rx_tag}(\\([^)]*\\))?(?=\\b|$)", case_sensitive)
|
1179
|
+
removed_tags = []
|
1180
|
+
title.gsub!(rx) do |mtch|
|
1181
|
+
removed_tags.push(mtch.strip.sub(/\(.*?\)$/, ''))
|
1182
|
+
replacement
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
title.dedup_tags!
|
1186
|
+
|
1187
|
+
@results.push(%(Removed #{removed_tags.join(', ')}: "#{title}" in #{section}))
|
1158
1188
|
end
|
1159
1189
|
elsif title !~ /@#{tag}/
|
1160
1190
|
title.chomp!
|
@@ -1201,6 +1231,15 @@ class WWID
|
|
1201
1231
|
write(@doing_file)
|
1202
1232
|
end
|
1203
1233
|
|
1234
|
+
##
|
1235
|
+
## @brief Move item from current section to
|
1236
|
+
## destination section
|
1237
|
+
##
|
1238
|
+
## @param item The item
|
1239
|
+
## @param section The destination section
|
1240
|
+
##
|
1241
|
+
## @return Updated item
|
1242
|
+
##
|
1204
1243
|
def move_item(item, section)
|
1205
1244
|
old_section = item['section']
|
1206
1245
|
new_item = item.dup
|
@@ -1701,11 +1740,13 @@ class WWID
|
|
1701
1740
|
|
1702
1741
|
# opt[:highlight] ||= true
|
1703
1742
|
section = ''
|
1743
|
+
is_single = true
|
1704
1744
|
if opt[:section].nil?
|
1705
1745
|
section = choose_section
|
1706
1746
|
opt[:section] = @content[section]
|
1707
1747
|
elsif opt[:section].instance_of?(String)
|
1708
1748
|
if opt[:section] =~ /^all$/i
|
1749
|
+
is_single = false
|
1709
1750
|
combined = { 'items' => [] }
|
1710
1751
|
@content.each do |_k, v|
|
1711
1752
|
combined['items'] += v['items']
|
@@ -1792,7 +1833,7 @@ class WWID
|
|
1792
1833
|
|
1793
1834
|
out = ''
|
1794
1835
|
|
1795
|
-
exit_now! 'Unknown output format' if opt[:output] && (opt[:output] !~ /^(template|html|csv|json|timeline)$/i)
|
1836
|
+
exit_now! 'Unknown output format' if opt[:output] && (opt[:output] !~ /^(template|html|csv|json|timeline|markdown|md|taskpaper)$/i)
|
1796
1837
|
|
1797
1838
|
case opt[:output]
|
1798
1839
|
when /^csv$/i
|
@@ -1871,7 +1912,7 @@ class WWID
|
|
1871
1912
|
puts JSON.pretty_generate({
|
1872
1913
|
'section' => section,
|
1873
1914
|
'items' => items_out,
|
1874
|
-
'timers' => tag_times(format:
|
1915
|
+
'timers' => tag_times(format: :json, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order])
|
1875
1916
|
})
|
1876
1917
|
elsif opt[:output] == 'timeline'
|
1877
1918
|
template = <<~EOTEMPLATE
|
@@ -1952,10 +1993,60 @@ class WWID
|
|
1952
1993
|
css_template
|
1953
1994
|
end
|
1954
1995
|
|
1955
|
-
totals = opt[:totals] ? tag_times(format:
|
1996
|
+
totals = opt[:totals] ? tag_times(format: :html, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
1956
1997
|
engine = Haml::Engine.new(template)
|
1957
1998
|
out = engine.render(Object.new,
|
1958
1999
|
{ :@items => items_out, :@page_title => page_title, :@style => style, :@totals => totals })
|
2000
|
+
when /^taskpaper$/i
|
2001
|
+
options = opt
|
2002
|
+
options[:highlight] = false
|
2003
|
+
options[:wrap_width] = 0
|
2004
|
+
options[:tags_color] = false
|
2005
|
+
options[:output] = nil
|
2006
|
+
options[:template] = '- %title @date(%date)%note'
|
2007
|
+
|
2008
|
+
out = list_section(options)
|
2009
|
+
when /^(md|markdown|gfm)$/i
|
2010
|
+
page_title = section
|
2011
|
+
all_items = []
|
2012
|
+
items.each do |i|
|
2013
|
+
if String.method_defined? :force_encoding
|
2014
|
+
title = i['title'].force_encoding('utf-8').link_urls({format: :markdown})
|
2015
|
+
note = i['note'].map { |line| line.force_encoding('utf-8').strip.link_urls({format: :markdown}) } if i['note']
|
2016
|
+
else
|
2017
|
+
title = i['title'].link_urls({format: :markdown})
|
2018
|
+
note = i['note'].map { |line| line.strip.link_urls({format: :markdown}) } if i['note']
|
2019
|
+
end
|
2020
|
+
|
2021
|
+
title = "#{title} @project(#{i['section']})" unless is_single
|
2022
|
+
|
2023
|
+
interval = get_interval(i) if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
2024
|
+
interval ||= false
|
2025
|
+
|
2026
|
+
done = i['title'] =~ /(?<= |^)@done/ ? 'x' : ' '
|
2027
|
+
|
2028
|
+
all_items << {
|
2029
|
+
date: i['date'].strftime('%a %-I:%M%p'),
|
2030
|
+
shortdate: i['date'].relative_date,
|
2031
|
+
done: done,
|
2032
|
+
note: note,
|
2033
|
+
section: i['section'],
|
2034
|
+
time: interval,
|
2035
|
+
title: title.strip
|
2036
|
+
}
|
2037
|
+
end
|
2038
|
+
|
2039
|
+
template = if @config['html_template']['markdown'] && File.exist?(File.expand_path(@config['html_template']['markdown']))
|
2040
|
+
IO.read(File.expand_path(@config['html_template']['markdown']))
|
2041
|
+
else
|
2042
|
+
markdown_template
|
2043
|
+
end
|
2044
|
+
|
2045
|
+
totals = opt[:totals] ? tag_times(format: :markdown, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
2046
|
+
|
2047
|
+
mdx = MarkdownExport.new(page_title, all_items, totals)
|
2048
|
+
engine = ERB.new(template)
|
2049
|
+
out = engine.result(mdx.get_binding)
|
1959
2050
|
else
|
1960
2051
|
items.each do |item|
|
1961
2052
|
if opt[:highlight] && item['title'] =~ /@#{@config['marker_tag']}\b/i
|
@@ -1970,7 +2061,9 @@ class WWID
|
|
1970
2061
|
note_lines = item['note'].delete_if do |line|
|
1971
2062
|
line =~ /^\s*$/
|
1972
2063
|
end
|
1973
|
-
note_lines.map! { |line|
|
2064
|
+
note_lines.map! { |line|
|
2065
|
+
"\t#{line.sub(/^\t*(— )?/, '').sub(/^- /, '— ')} "
|
2066
|
+
}
|
1974
2067
|
if opt[:wrap_width]&.positive?
|
1975
2068
|
width = opt[:wrap_width]
|
1976
2069
|
note_lines.map! do |line|
|
@@ -1998,15 +2091,7 @@ class WWID
|
|
1998
2091
|
output.sub!(/%interval/, interval)
|
1999
2092
|
|
2000
2093
|
output.sub!(/%shortdate/) do
|
2001
|
-
|
2002
|
-
item['date'].strftime(' %_I:%M%P')
|
2003
|
-
elsif item['date'] > (Date.today - 6).to_time
|
2004
|
-
item['date'].strftime('%a %_I:%M%P')
|
2005
|
-
elsif item['date'].year == Date.today.year
|
2006
|
-
item['date'].strftime('%m/%d %_I:%M%P')
|
2007
|
-
else
|
2008
|
-
item['date'].strftime('%m/%d/%Y %_I:%M%P')
|
2009
|
-
end
|
2094
|
+
item['date'].relative_date
|
2010
2095
|
end
|
2011
2096
|
|
2012
2097
|
output.sub!(/%title/) do |_m|
|
@@ -2044,7 +2129,7 @@ class WWID
|
|
2044
2129
|
out += "#{output}\n"
|
2045
2130
|
end
|
2046
2131
|
|
2047
|
-
out += tag_times(format:
|
2132
|
+
out += tag_times(format: :text, sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) if opt[:totals]
|
2048
2133
|
end
|
2049
2134
|
out
|
2050
2135
|
end
|
@@ -2352,7 +2437,7 @@ class WWID
|
|
2352
2437
|
## @param sort_by_name (Boolean) Sort by name if true, otherwise by time
|
2353
2438
|
## @param sort_order (String) The sort order (asc or desc)
|
2354
2439
|
##
|
2355
|
-
def tag_times(format:
|
2440
|
+
def tag_times(format: :text, sort_by_name: false, sort_order: 'asc')
|
2356
2441
|
return '' if @timers.empty?
|
2357
2442
|
|
2358
2443
|
max = @timers.keys.sort_by { |k| k.length }.reverse[0].length + 1
|
@@ -2367,8 +2452,9 @@ class WWID
|
|
2367
2452
|
end
|
2368
2453
|
|
2369
2454
|
sorted_tags_data.reverse! if sort_order =~ /^asc/i
|
2455
|
+
case format
|
2456
|
+
when :html
|
2370
2457
|
|
2371
|
-
if format == 'html'
|
2372
2458
|
output = <<EOS
|
2373
2459
|
<table>
|
2374
2460
|
<caption id="tagtotals">Tag Totals</caption>
|
@@ -2403,7 +2489,20 @@ EOS
|
|
2403
2489
|
</table>
|
2404
2490
|
EOS
|
2405
2491
|
output + tail
|
2406
|
-
|
2492
|
+
when :markdown
|
2493
|
+
pad = sorted_tags_data.map {|k, v| k }.group_by(&:size).max.last[0].length
|
2494
|
+
output = <<-EOS
|
2495
|
+
| #{' ' * (pad - 7) }project | time |
|
2496
|
+
| #{'-' * (pad - 1)}: | :------- |
|
2497
|
+
EOS
|
2498
|
+
sorted_tags_data.reverse.each do |k, v|
|
2499
|
+
if v > 0
|
2500
|
+
output += "| #{' ' * (pad - k.length)}#{k} | #{'%02d:%02d:%02d' % fmt_time(v)} |\n"
|
2501
|
+
end
|
2502
|
+
end
|
2503
|
+
tail = "[Tag Totals]"
|
2504
|
+
output + tail
|
2505
|
+
when :json
|
2407
2506
|
output = []
|
2408
2507
|
sorted_tags_data.reverse.each do |k, v|
|
2409
2508
|
output << {
|
data/lib/doing.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.91
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|