rbnotes 0.4.11 → 0.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +18 -0
- data/.gitignore +0 -1
- data/CHANGELOG.md +36 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +7 -7
- data/README.md +2 -1
- data/Rakefile +1 -1
- data/conf/config.yml +1 -0
- data/conf/config_deve.yml +1 -0
- data/conf/config_deve_fzf_no_opts.yml +8 -0
- data/conf/config_deve_no_picker.yml +7 -0
- data/conf/config_deve_peco.yml +8 -0
- data/exe/rbnotes +9 -1
- data/lib/rbnotes.rb +0 -5
- data/lib/rbnotes/commands/add.rb +45 -14
- data/lib/rbnotes/commands/commands.rb +17 -12
- data/lib/rbnotes/commands/import.rb +21 -10
- data/lib/rbnotes/commands/list.rb +69 -32
- data/lib/rbnotes/commands/pick.rb +40 -4
- data/lib/rbnotes/commands/show.rb +45 -3
- data/lib/rbnotes/commands/statistics.rb +32 -13
- data/lib/rbnotes/commands/update.rb +21 -10
- data/lib/rbnotes/conf.rb +20 -17
- data/lib/rbnotes/error.rb +37 -7
- data/lib/rbnotes/utils.rb +207 -72
- data/lib/rbnotes/version.rb +2 -2
- data/rbnotes.gemspec +1 -1
- metadata +9 -6
- data/.travis.yml +0 -6
@@ -9,19 +9,35 @@ module Rbnotes::Commands
|
|
9
9
|
"Pick a timestamp with a picker program"
|
10
10
|
end
|
11
11
|
|
12
|
+
DEFAULT_BEHAVIOR = "today" # :nodoc:
|
13
|
+
|
12
14
|
def execute(args, conf)
|
13
|
-
|
15
|
+
@opts = {}
|
16
|
+
parse_opts(args)
|
17
|
+
|
18
|
+
if args.empty?
|
19
|
+
default_behavior = conf[:list_default] || DEFAULT_BEHAVIOR
|
20
|
+
args << default_behavior
|
21
|
+
end
|
22
|
+
|
23
|
+
utils = Rbnotes.utils
|
24
|
+
patterns = utils.read_timestamp_patterns(args, enum_week: @opts[:enum_week])
|
25
|
+
|
14
26
|
@repo = Textrepo.init(conf)
|
15
27
|
|
16
28
|
list = []
|
17
|
-
|
18
|
-
list <<
|
29
|
+
utils.find_notes(patterns, @repo).each { |timestamp|
|
30
|
+
list << utils.make_headline(timestamp, @repo.read(timestamp))
|
19
31
|
}
|
20
32
|
|
21
33
|
picker = conf[:picker]
|
22
34
|
unless picker.nil?
|
35
|
+
picker_opts = conf[:picker_option]
|
36
|
+
cmds = [picker]
|
37
|
+
cmds.concat(picker_opts.split) unless picker_opts.nil?
|
38
|
+
|
23
39
|
require 'open3'
|
24
|
-
result = Open3.pipeline_rw(
|
40
|
+
result = Open3.pipeline_rw(cmds) { |stdin, stdout, _|
|
25
41
|
stdin.puts list
|
26
42
|
stdin.close
|
27
43
|
stdout.read
|
@@ -43,5 +59,25 @@ is specified, it will behave as same as "list" command.
|
|
43
59
|
|
44
60
|
HELP
|
45
61
|
end
|
62
|
+
|
63
|
+
# :stopdoc:
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def parse_opts(args)
|
68
|
+
while args.size > 0
|
69
|
+
arg = args.shift
|
70
|
+
case arg
|
71
|
+
when "-w", "--week"
|
72
|
+
@opts[:enum_week] = true
|
73
|
+
else
|
74
|
+
args.unshift(arg)
|
75
|
+
break
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# :startdoc:
|
81
|
+
|
46
82
|
end
|
47
83
|
end
|
@@ -5,13 +5,17 @@ module Rbnotes::Commands
|
|
5
5
|
# argument must be a string which can be converted into
|
6
6
|
# Textrepo::Timestamp object.
|
7
7
|
#
|
8
|
+
# Accepts an option with `-n NUMBER` (or `--num-of-lines`), to show
|
9
|
+
# the first NUMBER lines of the content of each note.
|
10
|
+
#
|
8
11
|
# A string for Textrepo::Timestamp must be:
|
9
12
|
#
|
10
13
|
# "20201106112600" : year, date, time and sec
|
11
14
|
# "20201106112600_012" : with suffix
|
12
15
|
#
|
13
16
|
# If no argument is passed, reads the standard input for arguments.
|
14
|
-
|
17
|
+
# If a specified timestamp does not exist in the repository as a key,
|
18
|
+
# Rbnotes::MissingTimestampError will occur.
|
15
19
|
class Show < Command
|
16
20
|
|
17
21
|
def description # :nodoc:
|
@@ -19,10 +23,26 @@ module Rbnotes::Commands
|
|
19
23
|
end
|
20
24
|
|
21
25
|
def execute(args, conf)
|
26
|
+
@opts = {}
|
27
|
+
parse_opts(args)
|
28
|
+
|
22
29
|
stamps = Rbnotes.utils.read_multiple_timestamps(args)
|
23
30
|
repo = Textrepo.init(conf)
|
24
31
|
|
25
|
-
content = stamps.map { |stamp|
|
32
|
+
content = stamps.map { |stamp|
|
33
|
+
begin
|
34
|
+
text = repo.read(stamp)
|
35
|
+
rescue Textrepo::MissingTimestampError => _
|
36
|
+
raise Rbnotes::MissingTimestampError, stamp
|
37
|
+
end
|
38
|
+
|
39
|
+
lines = text.size
|
40
|
+
if @opts[:num_of_lines].to_i > 0
|
41
|
+
lines = [@opts[:num_of_lines], lines].min
|
42
|
+
end
|
43
|
+
|
44
|
+
[stamp, text[0, lines]]
|
45
|
+
}.to_h
|
26
46
|
|
27
47
|
pager = conf[:pager]
|
28
48
|
unless pager.nil?
|
@@ -35,11 +55,14 @@ module Rbnotes::Commands
|
|
35
55
|
def help # :nodoc:
|
36
56
|
puts <<HELP
|
37
57
|
usage:
|
38
|
-
#{Rbnotes::NAME} show [TIMESTAMP...]
|
58
|
+
#{Rbnotes::NAME} show [(-n|--num-of-lines) NUMBER] [TIMESTAMP...]
|
39
59
|
|
40
60
|
Show the content of given notes. TIMESTAMP must be a fully qualified
|
41
61
|
one, such "20201016165130" or "20201016165130_012" if it has a suffix.
|
42
62
|
|
63
|
+
Accept an option with `-n NUMBER` (or `--num-of-lines`), to show the
|
64
|
+
first NUMBER lines of the content of each note.
|
65
|
+
|
43
66
|
The command try to read its argument from the standard input when no
|
44
67
|
argument was passed in the command line.
|
45
68
|
HELP
|
@@ -49,6 +72,25 @@ HELP
|
|
49
72
|
|
50
73
|
private
|
51
74
|
|
75
|
+
def parse_opts(args)
|
76
|
+
while args.size > 0
|
77
|
+
arg = args.shift
|
78
|
+
case arg
|
79
|
+
when "-n", "--num-of-lines"
|
80
|
+
num_of_lines = args.shift
|
81
|
+
raise ArgumentError, "missing number: %s" % args.unshift(arg) if num_of_lines.nil?
|
82
|
+
|
83
|
+
num_of_lines = num_of_lines.to_i
|
84
|
+
raise ArgumentError, "illegal number (must be greater than 0): %d" % num_of_lines unless num_of_lines > 0
|
85
|
+
|
86
|
+
@opts[:num_of_lines] = num_of_lines
|
87
|
+
else
|
88
|
+
args.unshift(arg)
|
89
|
+
break
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
52
94
|
def puts_with_pager(pager, output)
|
53
95
|
require "open3"
|
54
96
|
Open3.pipeline_w(pager) { |stdin|
|
@@ -9,20 +9,14 @@ module Rbnotes::Commands
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def execute(args, conf)
|
12
|
+
@opts = {}
|
13
|
+
parse_opts(args)
|
14
|
+
|
12
15
|
report = :total
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
report = :yearly
|
18
|
-
break
|
19
|
-
when "-m", "--monthly"
|
20
|
-
report = :monthly
|
21
|
-
break
|
22
|
-
else
|
23
|
-
args.unshift(arg)
|
24
|
-
raise ArgumentError, "invalid option or argument: %s" % args.join(" ")
|
25
|
-
end
|
16
|
+
if @opts[:yearly]
|
17
|
+
report = :yearly
|
18
|
+
elsif @opts[:monthly]
|
19
|
+
report = :monthly
|
26
20
|
end
|
27
21
|
|
28
22
|
stats = Rbnotes::Statistics.new(conf)
|
@@ -51,5 +45,30 @@ In the version #{Rbnotes::VERSION}, only number of notes is supported.
|
|
51
45
|
HELP
|
52
46
|
end
|
53
47
|
|
48
|
+
# :stopdoc:
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def parse_opts(args)
|
53
|
+
while args.size > 0
|
54
|
+
arg = args.shift
|
55
|
+
case arg
|
56
|
+
when "-y", "--yearly"
|
57
|
+
@opts[:yearly] = true
|
58
|
+
@opts[:monthly] = false
|
59
|
+
break
|
60
|
+
when "-m", "--monthly"
|
61
|
+
@opts[:yearly] = false
|
62
|
+
@opts[:monthly] = true
|
63
|
+
break
|
64
|
+
else
|
65
|
+
args.unshift(arg)
|
66
|
+
raise ArgumentError, "invalid option or argument: %s" % args.join(" ")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# :startdoc:
|
72
|
+
|
54
73
|
end
|
55
74
|
end
|
@@ -32,16 +32,7 @@ module Rbnotes::Commands
|
|
32
32
|
|
33
33
|
def execute(args, conf)
|
34
34
|
@opts = {}
|
35
|
-
|
36
|
-
arg = args.shift
|
37
|
-
case arg
|
38
|
-
when "-k", "--keep"
|
39
|
-
@opts[:keep_timestamp] = true
|
40
|
-
else
|
41
|
-
args.unshift(arg)
|
42
|
-
break
|
43
|
-
end
|
44
|
-
end
|
35
|
+
parse_opts(args)
|
45
36
|
|
46
37
|
target_stamp = Rbnotes.utils.read_timestamp(args)
|
47
38
|
editor = Rbnotes.utils.find_editor(conf[:editor])
|
@@ -100,5 +91,25 @@ editor program will be searched as same as add command. If none of
|
|
100
91
|
editors is available, the execution fails.
|
101
92
|
HELP
|
102
93
|
end
|
94
|
+
|
95
|
+
# :stopdoc:
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def parse_opts(args)
|
100
|
+
while args.size > 0
|
101
|
+
arg = args.shift
|
102
|
+
case arg
|
103
|
+
when "-k", "--keep"
|
104
|
+
@opts[:keep_timestamp] = true
|
105
|
+
else
|
106
|
+
args.unshift(arg)
|
107
|
+
break
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# :startdoc:
|
113
|
+
|
103
114
|
end
|
104
115
|
end
|
data/lib/rbnotes/conf.rb
CHANGED
@@ -31,19 +31,26 @@ module Rbnotes
|
|
31
31
|
|
32
32
|
DIRNAME_COMMON_CONF = ".config"
|
33
33
|
|
34
|
-
def initialize(
|
35
|
-
@conf_path = conf_path
|
34
|
+
def initialize(path = nil) # :nodoc:
|
36
35
|
@conf = {}
|
37
36
|
|
38
|
-
|
39
|
-
|
37
|
+
unless path.nil?
|
38
|
+
abspath = File.expand_path(path)
|
39
|
+
raise NoConfFileError, path unless FileTest.exist?(abspath)
|
40
|
+
@conf[:path] = abspath
|
40
41
|
else
|
41
|
-
@
|
42
|
-
raise NoConfFileError, @conf_path unless File.exist?(@conf_path)
|
43
|
-
|
44
|
-
yaml_str = File.open(@conf_path, "r") { |f| f.read }
|
45
|
-
@conf = YAML.load(yaml_str)
|
42
|
+
@conf[:path] = default_conf_path
|
46
43
|
end
|
44
|
+
|
45
|
+
values =
|
46
|
+
if FileTest.exist?(@conf[:path])
|
47
|
+
yaml_str = File.open(@conf[:path], "r") { |f| f.read }
|
48
|
+
YAML.load(yaml_str)
|
49
|
+
else
|
50
|
+
DEFAULT_VALUES
|
51
|
+
end
|
52
|
+
@conf.merge!(values)
|
53
|
+
@conf[:config_home] = config_home
|
47
54
|
end
|
48
55
|
|
49
56
|
def_delegators(:@conf,
|
@@ -91,7 +98,7 @@ module Rbnotes
|
|
91
98
|
:test => "_test",
|
92
99
|
}
|
93
100
|
|
94
|
-
def
|
101
|
+
def config_home
|
95
102
|
path = nil
|
96
103
|
xdg, user = ["XDG_CONFIG_HOME", "HOME"].map{|n| ENV[n]}
|
97
104
|
if xdg
|
@@ -99,15 +106,11 @@ module Rbnotes
|
|
99
106
|
else
|
100
107
|
path = File.join(user, DIRNAME_COMMON_CONF, DIRNAME_RBNOTES)
|
101
108
|
end
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
def default_conf_file
|
106
|
-
File.join(base_path, FILENAME_CONF)
|
109
|
+
path
|
107
110
|
end
|
108
111
|
|
109
|
-
def
|
110
|
-
|
112
|
+
def default_conf_path
|
113
|
+
File.join(config_home, FILENAME_CONF)
|
111
114
|
end
|
112
115
|
|
113
116
|
# :startdoc:
|
data/lib/rbnotes/error.rb
CHANGED
@@ -7,13 +7,15 @@ module Rbnotes
|
|
7
7
|
# :stopdoc:
|
8
8
|
|
9
9
|
module ErrMsg
|
10
|
-
MISSING_ARGUMENT = "
|
11
|
-
MISSING_TIMESTAMP = "
|
12
|
-
NO_EDITOR = "
|
13
|
-
PROGRAM_ABORT = "
|
14
|
-
UNKNOWN_KEYWORD = "
|
15
|
-
INVALID_TIMESTAMP_PATTERN = "
|
16
|
-
NO_CONF_FILE = "
|
10
|
+
MISSING_ARGUMENT = "missing argument: %s"
|
11
|
+
MISSING_TIMESTAMP = "missing timestamp: %s"
|
12
|
+
NO_EDITOR = "no editor is available: %s"
|
13
|
+
PROGRAM_ABORT = "external program was aborted: %s"
|
14
|
+
UNKNOWN_KEYWORD = "unknown keyword: %s"
|
15
|
+
INVALID_TIMESTAMP_PATTERN = "invalid timestamp pattern: %s"
|
16
|
+
NO_CONF_FILE = "no configuration file: %s"
|
17
|
+
NO_TEMPLATE_FILE = "no template file: %s"
|
18
|
+
INVALID_TIMESTAMP_PATTERN_AS_DATE = "invalid timestamp pattern as date: %s"
|
17
19
|
end
|
18
20
|
|
19
21
|
# :startdoc:
|
@@ -86,4 +88,32 @@ module Rbnotes
|
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
91
|
+
##
|
92
|
+
# An error raised when no arguments is spcified.
|
93
|
+
|
94
|
+
class NoArgumentError < Error
|
95
|
+
def initialize
|
96
|
+
super
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# An error raised when the specified template files does not exist.
|
102
|
+
#
|
103
|
+
class NoTemplateFileError < Error
|
104
|
+
def initialize(filepath)
|
105
|
+
super(ErrMsg::NO_TEMPLATE_FILE % filepath)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# An error raised when the specified pattern cannot be converted
|
111
|
+
# into a date.
|
112
|
+
#
|
113
|
+
class InvalidTimestampPatternAsDateError < Error
|
114
|
+
def initialize(pattern)
|
115
|
+
super(ErrMsg::INVALID_TIMESTAMP_PATTERN_AS_DATE % pattern)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
89
119
|
end
|
data/lib/rbnotes/utils.rb
CHANGED
@@ -7,6 +7,20 @@ require "io/console/size"
|
|
7
7
|
require "unicode/display_width"
|
8
8
|
|
9
9
|
module Rbnotes
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
##
|
14
|
+
# Retrieves the singleton instance of Rbnotes::Utils class.
|
15
|
+
# Typical usage is as follows:
|
16
|
+
#
|
17
|
+
# Rbnotes.utils.find_editor("emacsclient")
|
18
|
+
#
|
19
|
+
def utils
|
20
|
+
Utils.instance
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
10
24
|
##
|
11
25
|
# Defines several utility methods those are intended to be used in
|
12
26
|
# Rbnotes classes.
|
@@ -89,6 +103,19 @@ module Rbnotes
|
|
89
103
|
tmpfile
|
90
104
|
end
|
91
105
|
|
106
|
+
# Acceptable delimiters to separate a timestamp string for human
|
107
|
+
# being to read and input easily.
|
108
|
+
#
|
109
|
+
# Here is some examples:
|
110
|
+
#
|
111
|
+
# - "2021-04-15 15:34:56" -> "20210415153456" (a timestamp string)
|
112
|
+
# - "2020-04-15_15:34:56" -> (same as above)
|
113
|
+
# - "2020-04-15-15-34-56" -> (same as above)
|
114
|
+
# - "2020 04 15 15 34 56" -> (same as above)
|
115
|
+
# - "2020-04-15" -> "20200415" (a timestamp pattern)
|
116
|
+
|
117
|
+
TIMESTAMP_DELIMITERS = /[-:_\s]/
|
118
|
+
|
92
119
|
##
|
93
120
|
# Generates a Textrepo::Timestamp object from a String which comes
|
94
121
|
# from the command line arguments. When no argument is given,
|
@@ -99,6 +126,9 @@ module Rbnotes
|
|
99
126
|
|
100
127
|
def read_timestamp(args)
|
101
128
|
str = args.shift || read_arg($stdin)
|
129
|
+
raise NoArgumentError if str.nil?
|
130
|
+
|
131
|
+
str = remove_delimiters_from_timestamp_string(str)
|
102
132
|
Textrepo::Timestamp.parse_s(str)
|
103
133
|
end
|
104
134
|
|
@@ -107,12 +137,91 @@ module Rbnotes
|
|
107
137
|
# line arguments. When no argument is given, try to read from
|
108
138
|
# STDIN.
|
109
139
|
#
|
140
|
+
# When multiple strings those point the identical time are
|
141
|
+
# included the arguments (passed or read form STDIN), the
|
142
|
+
# redundant strings will be removed.
|
143
|
+
#
|
144
|
+
# The order of the arguments will be preserved into the return
|
145
|
+
# value, even if the redundant strings were removed.
|
146
|
+
#
|
110
147
|
# :call-seq:
|
111
148
|
# read_multiple_timestamps(args) -> [String]
|
112
149
|
|
113
150
|
def read_multiple_timestamps(args)
|
114
151
|
strings = args.size < 1 ? read_multiple_args($stdin) : args
|
115
|
-
strings.
|
152
|
+
raise NoArgumentError if (strings.nil? || strings.empty?)
|
153
|
+
strings.uniq.map { |str|
|
154
|
+
str = remove_delimiters_from_timestamp_string(str)
|
155
|
+
Textrepo::Timestamp.parse_s(str)
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Reads timestamp patterns in an array of arguments. It supports
|
161
|
+
# keywords expansion and enumeration of week. The function is
|
162
|
+
# intended to be used from Commands::List#execute and
|
163
|
+
# Commands::Pick#execute.
|
164
|
+
#
|
165
|
+
def read_timestamp_patterns(args, enum_week: false)
|
166
|
+
patterns = nil
|
167
|
+
if enum_week
|
168
|
+
patterns = []
|
169
|
+
while args.size > 0
|
170
|
+
arg = args.shift
|
171
|
+
begin
|
172
|
+
patterns.concat(timestamp_patterns_in_week(arg.dup))
|
173
|
+
rescue InvalidTimestampPatternAsDateError => _e
|
174
|
+
raise InvalidTimestampPatternAsDateError, args.unshift(arg)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
else
|
178
|
+
patterns = expand_keyword_in_args(args)
|
179
|
+
end
|
180
|
+
patterns
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Enumerates all timestamp patterns in a week which contains a
|
185
|
+
# given timestamp as a day of the week.
|
186
|
+
#
|
187
|
+
# The argument must be one of the followings:
|
188
|
+
# - "yyyymodd" (eg. "20201220")
|
189
|
+
# - "yymoddhhmiss" (eg. "20201220120048")
|
190
|
+
# - "yymoddhhmiss_sfx" (eg. "20201220120048_012")
|
191
|
+
# - "modd" (eg. "1220") (assums in the current year)
|
192
|
+
# - nil (assumes today)
|
193
|
+
#
|
194
|
+
# :call-seq:
|
195
|
+
# timestamp_patterns_in_week(String) -> [Array of Strings]
|
196
|
+
#
|
197
|
+
def timestamp_patterns_in_week(arg)
|
198
|
+
date_str = nil
|
199
|
+
|
200
|
+
if arg
|
201
|
+
date_str = remove_delimiters_from_timestamp_string(arg)
|
202
|
+
else
|
203
|
+
date_str = Textrepo::Timestamp.now[0, 8]
|
204
|
+
end
|
205
|
+
|
206
|
+
case date_str.size
|
207
|
+
when "yyyymodd".size
|
208
|
+
# nothing to do
|
209
|
+
when "yyyymoddhhmiss".size, "yyyymoddhhmiss_sfx".size
|
210
|
+
date_str = date_str[0, 8]
|
211
|
+
when "modd".size
|
212
|
+
this_year = Time.now.year.to_s
|
213
|
+
date_str = "#{this_year}#{date_str}"
|
214
|
+
else
|
215
|
+
raise InvalidTimestampPatternAsDateError, arg
|
216
|
+
end
|
217
|
+
|
218
|
+
begin
|
219
|
+
date = Date.parse(date_str)
|
220
|
+
rescue Date::Error => _e
|
221
|
+
raise InvalidTimestampPatternAsDateError, arg
|
222
|
+
end
|
223
|
+
|
224
|
+
dates_in_week(date).map { |date| timestamp_pattern(date) }
|
116
225
|
end
|
117
226
|
|
118
227
|
##
|
@@ -136,72 +245,54 @@ module Rbnotes
|
|
136
245
|
# - "yeasterday" (or "ye")
|
137
246
|
# - "this_week" (or "tw")
|
138
247
|
# - "last_week" (or "lw")
|
248
|
+
# - "this_month" (or "tm")
|
249
|
+
# - "last_month" (or "lm")
|
250
|
+
# - "all"
|
139
251
|
#
|
140
252
|
# :call-seq:
|
141
253
|
# expand_keyword_in_args(Array of Strings) -> Array of Strings
|
142
|
-
|
254
|
+
#
|
143
255
|
def expand_keyword_in_args(args)
|
144
|
-
return [nil] if args.empty?
|
145
|
-
|
146
256
|
patterns = []
|
147
257
|
while args.size > 0
|
148
258
|
arg = args.shift
|
149
|
-
if
|
150
|
-
|
151
|
-
|
259
|
+
if arg == "all"
|
260
|
+
return [nil]
|
261
|
+
elsif KEYWORDS.include?(arg)
|
262
|
+
patterns.concat(expand_keyword(arg))
|
152
263
|
else
|
153
264
|
patterns << arg
|
154
265
|
end
|
155
266
|
end
|
156
|
-
patterns.sort
|
157
|
-
end
|
158
|
-
|
159
|
-
##
|
160
|
-
# Expands a keyword to timestamp strings.
|
161
|
-
#
|
162
|
-
# :call-seq:
|
163
|
-
# expand_keyword(keyword as String) -> Array of timestamp Strings
|
164
|
-
|
165
|
-
def expand_keyword(keyword)
|
166
|
-
patterns = []
|
167
|
-
case keyword
|
168
|
-
when "today", "to"
|
169
|
-
patterns << timestamp_pattern(date_of_today)
|
170
|
-
when "yesterday", "ye"
|
171
|
-
patterns << timestamp_pattern(date_of_yesterday)
|
172
|
-
when "this_week", "tw"
|
173
|
-
patterns.concat(dates_in_this_week.map { |d| timestamp_pattern(d) })
|
174
|
-
when "last_week", "lw"
|
175
|
-
patterns.concat(dates_in_last_week.map { |d| timestamp_pattern(d) })
|
176
|
-
else
|
177
|
-
raise UnknownKeywordError, keyword
|
178
|
-
end
|
179
|
-
patterns
|
267
|
+
patterns.uniq.sort
|
180
268
|
end
|
181
269
|
|
182
270
|
##
|
183
271
|
# Makes a headline with the timestamp and subject of the notes, it
|
184
272
|
# looks like as follows:
|
185
273
|
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
# |
|
189
|
-
# 20101010001000_123: I love Macintosh.
|
190
|
-
# 20100909090909_999: This is very very long
|
191
|
-
#
|
192
|
-
#
|
274
|
+
# |<--------------- console column size -------------------->|
|
275
|
+
# | |+-- timestamp ---+ +-subject (the 1st line of note) -+
|
276
|
+
# | | | |
|
277
|
+
# | |20101010001000_123: I love Macintosh. [EOL]
|
278
|
+
# | |20100909090909_999: This is very very long looong subj[EOL]
|
279
|
+
# |<->| | |
|
280
|
+
# ^--- pad ++
|
281
|
+
# ^--- delimiter (2 characters)
|
193
282
|
#
|
194
283
|
# The subject part will truncate when it is long.
|
195
284
|
|
196
|
-
def make_headline(timestamp, text)
|
285
|
+
def make_headline(timestamp, text, pad = nil)
|
197
286
|
_, column = IO.console_size
|
198
287
|
delimiter = ": "
|
199
288
|
timestamp_width = timestamp.to_s.size
|
200
289
|
subject_width = column - timestamp_width - delimiter.size - 1
|
290
|
+
subject_width -= pad.size unless pad.nil?
|
201
291
|
|
202
292
|
subject = remove_heading_markup(text[0])
|
203
293
|
|
204
294
|
ts_part = "#{timestamp.to_s} "[0..(timestamp_width - 1)]
|
295
|
+
ts_part.prepend(pad) unless pad.nil?
|
205
296
|
sj_part = truncate_str(subject, subject_width)
|
206
297
|
|
207
298
|
ts_part + delimiter + sj_part
|
@@ -210,6 +301,7 @@ module Rbnotes
|
|
210
301
|
##
|
211
302
|
# Finds all notes those timestamps match to given patterns in the
|
212
303
|
# given repository. Returns an Array contains Timestamp objects.
|
304
|
+
# The returned Array is sorted by Timestamp.
|
213
305
|
#
|
214
306
|
# :call-seq:
|
215
307
|
# find_notes(Array of timestamp patterns, Textrepo::Repository)
|
@@ -220,17 +312,6 @@ module Rbnotes
|
|
220
312
|
}.flatten.sort{ |a, b| b <=> a }.uniq
|
221
313
|
end
|
222
314
|
|
223
|
-
##
|
224
|
-
# Enumerates all timestamp patterns in a week which contains a
|
225
|
-
# given timestamp as a day of the week.
|
226
|
-
#
|
227
|
-
# :call-seq:
|
228
|
-
# timestamp_patterns_in_week(timestamp) -> [Array of Strings]
|
229
|
-
|
230
|
-
def timestamp_patterns_in_week(timestamp)
|
231
|
-
dates_in_week(start_date_in_the_week(timestamp.time)).map { |date| timestamp_pattern(date) }
|
232
|
-
end
|
233
|
-
|
234
315
|
# :stopdoc:
|
235
316
|
|
236
317
|
private
|
@@ -269,6 +350,48 @@ module Rbnotes
|
|
269
350
|
}.compact
|
270
351
|
end
|
271
352
|
|
353
|
+
def remove_delimiters_from_timestamp_string(stamp_str) # :nodoc:
|
354
|
+
str = stamp_str.gsub(TIMESTAMP_DELIMITERS, "")
|
355
|
+
base_size = "yyyymiddhhmoss".size
|
356
|
+
if str.size > base_size # when suffix is specified
|
357
|
+
str = str[0...base_size] + "_" + str[base_size..-1]
|
358
|
+
end
|
359
|
+
str
|
360
|
+
end
|
361
|
+
|
362
|
+
##
|
363
|
+
# Expands a keyword to timestamp strings.
|
364
|
+
#
|
365
|
+
# :call-seq:
|
366
|
+
# expand_keyword(keyword as String) -> Array of timestamp Strings
|
367
|
+
#
|
368
|
+
def expand_keyword(keyword)
|
369
|
+
patterns = []
|
370
|
+
case keyword
|
371
|
+
when "today", "to"
|
372
|
+
patterns << timestamp_pattern(Date.today)
|
373
|
+
when "yesterday", "ye"
|
374
|
+
patterns << timestamp_pattern(Date.today.prev_day)
|
375
|
+
when "this_week", "tw"
|
376
|
+
patterns.concat(dates_in_this_week.map { |d| timestamp_pattern(d) })
|
377
|
+
when "last_week", "lw"
|
378
|
+
patterns.concat(dates_in_last_week.map { |d| timestamp_pattern(d) })
|
379
|
+
when "this_month", "tm"
|
380
|
+
patterns.concat(dates_in_this_month.map { |d| timestamp_pattern(d) })
|
381
|
+
when "last_month", "lm"
|
382
|
+
patterns.concat(dates_in_last_month.map { |d| timestamp_pattern(d) })
|
383
|
+
else
|
384
|
+
raise UnknownKeywordError, keyword
|
385
|
+
end
|
386
|
+
patterns
|
387
|
+
end
|
388
|
+
|
389
|
+
KEYWORDS = %w(
|
390
|
+
today to yesterday ye
|
391
|
+
this_week tw last_week lw
|
392
|
+
this_month tm last_month lm
|
393
|
+
)
|
394
|
+
|
272
395
|
def search_in_path(name)
|
273
396
|
search_paths = ENV["PATH"].split(":")
|
274
397
|
found = search_paths.map { |path|
|
@@ -286,49 +409,61 @@ module Rbnotes
|
|
286
409
|
date.strftime("%Y%m%d")
|
287
410
|
end
|
288
411
|
|
289
|
-
def date_of_today
|
290
|
-
date(Time.now)
|
291
|
-
end
|
292
|
-
|
293
|
-
def date_of_yesterday
|
294
|
-
date(Time.now).prev_day
|
295
|
-
end
|
296
|
-
|
297
412
|
def date(time)
|
298
413
|
Date.new(time.year, time.mon, time.day)
|
299
414
|
end
|
300
415
|
|
301
416
|
def dates_in_this_week
|
302
|
-
dates_in_week(
|
417
|
+
dates_in_week(Date.today)
|
303
418
|
end
|
304
419
|
|
305
420
|
def dates_in_last_week
|
306
|
-
dates_in_week(
|
421
|
+
dates_in_week(Date.today.prev_day(7))
|
422
|
+
end
|
423
|
+
|
424
|
+
def dates_in_week(date)
|
425
|
+
start_date = start_date_of_week(date)
|
426
|
+
dates = [start_date]
|
427
|
+
1.upto(6) { |i| dates << start_date.next_day(i) }
|
428
|
+
dates
|
307
429
|
end
|
308
430
|
|
309
|
-
def
|
310
|
-
|
431
|
+
def start_date_of_week(date)
|
432
|
+
# week day in monday start calendar
|
433
|
+
date.prev_day((date.wday - 1) % 7)
|
311
434
|
end
|
312
435
|
|
313
|
-
def
|
314
|
-
|
436
|
+
def first_date_of_this_month
|
437
|
+
today = Time.now
|
438
|
+
date(Time.new(today.year, today.mon, 1))
|
315
439
|
end
|
316
440
|
|
317
|
-
def
|
318
|
-
|
319
|
-
Date.new(*parts).prev_day(wday(time))
|
441
|
+
def dates_in_this_month
|
442
|
+
dates_in_month(first_date_of_this_month)
|
320
443
|
end
|
321
444
|
|
322
|
-
def
|
323
|
-
(
|
445
|
+
def dates_in_last_month
|
446
|
+
dates_in_month(first_date_of_this_month.prev_month)
|
324
447
|
end
|
325
448
|
|
326
|
-
def
|
327
|
-
|
328
|
-
|
449
|
+
def dates_in_month(first_date)
|
450
|
+
days = days_in_month(first_date.mon, leap: first_date.leap?)
|
451
|
+
dates = [first_date]
|
452
|
+
1.upto(days - 1) { |i| dates << first_date.next_day(i) }
|
329
453
|
dates
|
330
454
|
end
|
331
455
|
|
456
|
+
DAYS = {
|
457
|
+
# 1 2 3 4 5 6 7 8 9 10 11 12
|
458
|
+
# Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
|
459
|
+
false => [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
|
460
|
+
true => [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
|
461
|
+
}
|
462
|
+
|
463
|
+
def days_in_month(mon, leap: false)
|
464
|
+
DAYS[leap][mon]
|
465
|
+
end
|
466
|
+
|
332
467
|
def truncate_str(str, size)
|
333
468
|
count = 0
|
334
469
|
result = ""
|