rbnotes 0.4.11 → 0.4.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4170ccc84305971d10f3726351cdc4138b5fabaa7293bc10a827ed0458f28b8c
4
- data.tar.gz: 291efc32e2e65462e8c6996ecbbc7ffbb4e6887a5d0065bbb843d01dfbb8c78c
3
+ metadata.gz: 517b5866c81f76286f87b2ed7d2e695c4dd09d5c7ab86f9557079232a58fde37
4
+ data.tar.gz: 1d89412445d155501f67cd0978a8070975ed722ee7440119cf5a9ba89ca946eb
5
5
  SHA512:
6
- metadata.gz: 1f6d2659c1a50f462867a53bff3f7b45e8b38035c3af44407b70d359c6af9409481f1033059384bd8d6b42d03981ce4f955ac87ee5fe7e99f5b50465d47f4e1b
7
- data.tar.gz: f5949cfe44eef0951a80ec8949b6bb8a3440d5b483ddacdaf652d8b4dd258a7eb53db42c40290e9a2ebd6e3a1bf6e204f2382b41dd40b23ac8b271dd05547672
6
+ metadata.gz: 1f0bab361e6f4a7f22a59433369b221b6dffc1e16b79f9d4bdc4027d78c3d61a6e949ab2b81ae1ab3be4c5d52af6bd74aed4b36c70a93f31e710a42b7bb23731
7
+ data.tar.gz: af38ba13276090ae653fee8b1b3f67f3de57596851ce8b645f4ed3463c38d71c4a950b3382bff55ad98853c42e2cb3f2df0e7e7652f756f385d53ef15f917085
@@ -5,6 +5,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/)
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
+ Nothing to record here.
9
+
10
+ ## [0.4.12] - 2020-12-18
11
+ ### Changed
12
+ - Make clear the spec of `list` command args. (#94)
13
+ - Add a feature to use a template file for `add` command. (#87)
14
+ - Add new keywords for `list` command. (#90)
15
+ - `this_month` and `last_month`
16
+ - Add a new option, `verbose` for `list` command. (#76)
17
+
18
+ ### Fixed
19
+ - Fix issue #80: suppress unnecessary error message.
20
+
8
21
  ## [0.4.11] - 2020-12-07
9
22
  ### Added
10
23
  - Add a new command `statistics`. (#73)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbnotes (0.4.11)
4
+ rbnotes (0.4.12)
5
5
  textrepo (~> 0.5.4)
6
6
  unicode-display_width (~> 1.7)
7
7
 
@@ -5,4 +5,5 @@
5
5
  :repository_base: "~"
6
6
  :pager: "bat -l md"
7
7
  :picker: "fzf"
8
+ :picker_option: "-m"
8
9
  :editor: "/usr/local/bin/emacsclient"
@@ -5,4 +5,5 @@
5
5
  :repository_base: "tmp"
6
6
  :pager: "bat -l md"
7
7
  :picker: "fzf"
8
+ :picker_option: "-m"
8
9
  :editor: "/usr/local/bin/emacsclient"
@@ -0,0 +1,8 @@
1
+ ---
2
+ :run_mode: :development
3
+ :repository_type: :file_system
4
+ :repository_name: "notes"
5
+ :repository_base: "tmp"
6
+ :pager: "bat -l md"
7
+ :picker: "fzf"
8
+ :editor: "/usr/local/bin/emacsclient"
@@ -0,0 +1,7 @@
1
+ ---
2
+ :run_mode: :development
3
+ :repository_type: :file_system
4
+ :repository_name: "notes"
5
+ :repository_base: "tmp"
6
+ :pager: "bat -l md"
7
+ :editor: "/usr/local/bin/emacsclient"
@@ -0,0 +1,8 @@
1
+ ---
2
+ :run_mode: :development
3
+ :repository_type: :file_system
4
+ :repository_name: "notes"
5
+ :repository_base: "tmp"
6
+ :pager: "bat -l md"
7
+ :picker: "peco"
8
+ :editor: "/usr/local/bin/emacsclient"
@@ -48,16 +48,24 @@ app = App.new
48
48
  begin
49
49
  app.parse_global_options(ARGV)
50
50
  app.run(ARGV)
51
- rescue Errno::EPIPE => e
51
+ rescue Errno::EPIPE => _
52
52
  # Fix issue #61: When the pipeline which rbnotes connects is
53
53
  # discarded by the other program, the execption was raised. It does
54
54
  # not end abnormally for rbnotes. So, just ignores the exception.
55
55
  exit 0
56
+ rescue NoArgumentError => _
57
+ # Fix issue #80: Typically, this error raises when a command tries
58
+ # to read the standard input for its arguments and gets nil. It
59
+ # means user wants to cancel to execute. So, just ignore the error
60
+ # and exit.
61
+ exit 0
56
62
  rescue MissingArgumentError, MissingTimestampError,
57
63
  NoEditorError, ProgramAbortError,
58
64
  Textrepo::InvalidTimestampStringError,
59
65
  InvalidTimestampPatternError,
66
+ InvalidTimestampPatternAsDateError,
60
67
  NoConfFileError,
68
+ NoTemplateFileError,
61
69
  ArgumentError,
62
70
  Errno::EACCES => e
63
71
  puts e.message
@@ -42,6 +42,9 @@ module Rbnotes::Commands
42
42
  raise ArgumentError, "missing timestamp: %s" % args.unshift(arg) if stamp_str.nil?
43
43
  stamp_str = complement_timestamp_pattern(stamp_str)
44
44
  @opts[:timestamp] = Textrepo::Timestamp.parse_s(stamp_str)
45
+ when "-f", "--template-file"
46
+ template_path = args.shift
47
+ @opts[:template] = template_path
45
48
  else
46
49
  args.unshift(arg)
47
50
  break
@@ -54,7 +57,8 @@ module Rbnotes::Commands
54
57
  editor = Rbnotes.utils.find_program(candidates)
55
58
  raise Rbnotes::NoEditorError, candidates if editor.nil?
56
59
 
57
- tmpfile = Rbnotes.utils.run_with_tmpfile(editor, stamp.to_s)
60
+ template = read_template(conf)
61
+ tmpfile = Rbnotes.utils.run_with_tmpfile(editor, stamp.to_s, template)
58
62
 
59
63
  unless FileTest.exist?(tmpfile)
60
64
  puts "Cancel adding, since nothing to store"
@@ -127,5 +131,26 @@ HELP
127
131
  end
128
132
  stamp_str
129
133
  end
134
+
135
+ def read_template(conf)
136
+ template = nil
137
+ template_path = @opts[:template] || conf[:template]
138
+
139
+ if template_path
140
+ raise Rbnotes::NoTemplateFileError, template_path unless FileTest.exist?(template_path)
141
+ template = File.readlines(template_path, chomp: true)
142
+ else
143
+ template_path = default_template_file(conf)
144
+ template = File.readlines(template_path, chomp: true) if FileTest.exist?(template_path)
145
+ end
146
+
147
+ template
148
+ end
149
+
150
+ def default_template_file(conf)
151
+ dir = File.join(conf[:config_home], "templates")
152
+ File.expand_path("default.md", dir)
153
+ end
154
+
130
155
  end
131
156
  end
@@ -24,10 +24,12 @@ module Rbnotes::Commands
24
24
  #
25
25
  # A keyword must be one of them:
26
26
  #
27
- # - "today" (or "to")
28
- # - "yeasterday" (or "ye")
29
- # - "this_week" (or "tw")
30
- # - "last_week" (or "lw")
27
+ # - "today" (or "to")
28
+ # - "yeasterday" (or "ye")
29
+ # - "this_week" (or "tw")
30
+ # - "last_week" (or "lw")
31
+ # - "this_month" (or "tm")
32
+ # - "last_month" (or "lm")
31
33
  #
32
34
  # Here is several examples of timestamp patterns.
33
35
  #
@@ -57,6 +59,8 @@ module Rbnotes::Commands
57
59
  case arg
58
60
  when "-w", "--week"
59
61
  @opts[:enum_week] = true
62
+ when "-v", "--verbose"
63
+ @opts[:verbose] = true
60
64
  else
61
65
  args.unshift(arg)
62
66
  break
@@ -70,20 +74,41 @@ module Rbnotes::Commands
70
74
  when "yyyymodd".size, "yyyymoddhhmiss".size, "yyyymoddhhmiss_sfx".size
71
75
  stamp_str = "#{arg}000000"[0, 14]
72
76
  timestamp = Textrepo::Timestamp.parse_s(stamp_str)
73
- patterns = Rbnotes.utils.timestamp_patterns_in_week(timestamp)
77
+ when "modd".size
78
+ this_year = Time.now.year.to_s
79
+ stamp_str = "#{this_year}#{arg}000000"
80
+ begin
81
+ timestamp = Textrepo::Timestamp.parse_s(stamp_str)
82
+ rescue Textrepo::InvalidTimestampStringError => _e
83
+ raise InvalidTimestampPatternAsDateError, arg
84
+ end
74
85
  else
75
- raise InvalidTimestampPatternError,
76
- "cannot convert to a date [%s]" % args.unshift(arg)
86
+ raise InvalidTimestampPatternAsDateError, args.unshift(arg)
77
87
  end
88
+ patterns = Rbnotes.utils.timestamp_patterns_in_week(timestamp)
78
89
  else
79
90
  patterns = Rbnotes.utils.expand_keyword_in_args(args)
80
91
  end
81
92
 
82
93
  @repo = Textrepo.init(conf)
83
- # newer stamp shoud be above
84
- Rbnotes.utils.find_notes(patterns, @repo).each { |timestamp|
85
- puts Rbnotes.utils.make_headline(timestamp, @repo.read(timestamp))
86
- }
94
+ notes = Rbnotes.utils.find_notes(patterns, @repo)
95
+ output = []
96
+ if @opts[:verbose]
97
+ collect_timestamps_by_date(notes).each { |date, timestamps|
98
+ output << "#{date} (#{timestamps.size})"
99
+ timestamps.each { |timestamp|
100
+ pad = " "
101
+ output << Rbnotes.utils.make_headline(timestamp,
102
+ @repo.read(timestamp), pad)
103
+ }
104
+ }
105
+ else
106
+ notes.each { |timestamp|
107
+ output << Rbnotes.utils.make_headline(timestamp,
108
+ @repo.read(timestamp))
109
+ }
110
+ end
111
+ puts output
87
112
  end
88
113
 
89
114
  def help # :nodoc:
@@ -110,6 +135,8 @@ KEYWORD:
110
135
  - "yeasterday" (or "ye")
111
136
  - "this_week" (or "tw")
112
137
  - "last_week" (or "lw")
138
+ - "this_month" (or "tm")
139
+ - "last_month" (or "lm")
113
140
 
114
141
  An option "--week" is also acceptable. It specifies to enumerate all
115
142
  days of a week. Typically, the option is used with a STAMP_PATTERN
@@ -124,5 +151,24 @@ would be as same as the KEYWORD, "this_week" was specified.
124
151
  HELP
125
152
  end
126
153
 
154
+ # :stopdoc:
155
+
156
+ private
157
+
158
+ def collect_timestamps_by_date(timestamps)
159
+ result = {}
160
+ timestamps.map { |ts|
161
+ [ts.strftime("%Y-%m-%d"), ts]
162
+ }.reduce(result) { |r, pair|
163
+ date, stamp = pair
164
+ r[date] ||= []
165
+ r[date] << stamp
166
+ r
167
+ }
168
+ result
169
+ end
170
+
171
+ # :startdoc:
172
+
127
173
  end
128
174
  end
@@ -20,8 +20,12 @@ module Rbnotes::Commands
20
20
 
21
21
  picker = conf[:picker]
22
22
  unless picker.nil?
23
+ picker_opts = conf[:picker_option]
24
+ cmds = [picker]
25
+ cmds.concat(picker_opts.split) unless picker_opts.nil?
26
+
23
27
  require 'open3'
24
- result = Open3.pipeline_rw(picker) { |stdin, stdout, _|
28
+ result = Open3.pipeline_rw(cmds) { |stdin, stdout, _|
25
29
  stdin.puts list
26
30
  stdin.close
27
31
  stdout.read
@@ -31,19 +31,26 @@ module Rbnotes
31
31
 
32
32
  DIRNAME_COMMON_CONF = ".config"
33
33
 
34
- def initialize(conf_path = nil) # :nodoc:
35
- @conf_path = conf_path
34
+ def initialize(path = nil) # :nodoc:
36
35
  @conf = {}
37
36
 
38
- if use_default_values?
39
- @conf.merge!(DEFAULT_VALUES)
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
- @conf_path ||= default_conf_file
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 base_path
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
- return path
103
- end
104
-
105
- def default_conf_file
106
- File.join(base_path, FILENAME_CONF)
109
+ path
107
110
  end
108
111
 
109
- def use_default_values?
110
- @conf_path.nil? && !File.exist?(default_conf_file)
112
+ def default_conf_path
113
+ File.join(config_home, FILENAME_CONF)
111
114
  end
112
115
 
113
116
  # :startdoc:
@@ -7,13 +7,15 @@ module Rbnotes
7
7
  # :stopdoc:
8
8
 
9
9
  module ErrMsg
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"
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
@@ -99,6 +99,7 @@ module Rbnotes
99
99
 
100
100
  def read_timestamp(args)
101
101
  str = args.shift || read_arg($stdin)
102
+ raise NoArgumentError if str.nil?
102
103
  Textrepo::Timestamp.parse_s(str)
103
104
  end
104
105
 
@@ -112,6 +113,7 @@ module Rbnotes
112
113
 
113
114
  def read_multiple_timestamps(args)
114
115
  strings = args.size < 1 ? read_multiple_args($stdin) : args
116
+ raise NoArgumentError if (strings.nil? || strings.empty?)
115
117
  strings.map { |str| Textrepo::Timestamp.parse_s(str) }
116
118
  end
117
119
 
@@ -136,6 +138,8 @@ module Rbnotes
136
138
  # - "yeasterday" (or "ye")
137
139
  # - "this_week" (or "tw")
138
140
  # - "last_week" (or "lw")
141
+ # - "this_month" (or "tm")
142
+ # - "last_month" (or "lm")
139
143
  #
140
144
  # :call-seq:
141
145
  # expand_keyword_in_args(Array of Strings) -> Array of Strings
@@ -147,7 +151,8 @@ module Rbnotes
147
151
  while args.size > 0
148
152
  arg = args.shift
149
153
  if ["today", "to", "yesterday", "ye",
150
- "this_week", "tw", "last_week", "lw"].include?(arg)
154
+ "this_week", "tw", "last_week", "lw",
155
+ "this_month", "tm", "last_month", "lm"].include?(arg)
151
156
  patterns.concat(Rbnotes.utils.expand_keyword(arg))
152
157
  else
153
158
  patterns << arg
@@ -173,6 +178,10 @@ module Rbnotes
173
178
  patterns.concat(dates_in_this_week.map { |d| timestamp_pattern(d) })
174
179
  when "last_week", "lw"
175
180
  patterns.concat(dates_in_last_week.map { |d| timestamp_pattern(d) })
181
+ when "this_month", "tm"
182
+ patterns.concat(dates_in_this_month.map { |d| timestamp_pattern(d) })
183
+ when "last_month", "lm"
184
+ patterns.concat(dates_in_last_month.map { |d| timestamp_pattern(d) })
176
185
  else
177
186
  raise UnknownKeywordError, keyword
178
187
  end
@@ -183,25 +192,28 @@ module Rbnotes
183
192
  # Makes a headline with the timestamp and subject of the notes, it
184
193
  # looks like as follows:
185
194
  #
186
- # |<------------------ console column size ------------------->|
187
- # +-- timestamp ---+ +- subject (the 1st line of each note) -+
188
- # | | | |
189
- # 20101010001000_123: I love Macintosh. [EOL]
190
- # 20100909090909_999: This is very very long long loooong subje[EOL]
191
- # ++
192
- # ^--- delimiter (2 characters)
195
+ # |<--------------- console column size -------------------->|
196
+ # | |+-- timestamp ---+ +-subject (the 1st line of note) -+
197
+ # | | | |
198
+ # | |20101010001000_123: I love Macintosh. [EOL]
199
+ # | |20100909090909_999: This is very very long looong subj[EOL]
200
+ # |<->| | |
201
+ # ^--- pad ++
202
+ # ^--- delimiter (2 characters)
193
203
  #
194
204
  # The subject part will truncate when it is long.
195
205
 
196
- def make_headline(timestamp, text)
206
+ def make_headline(timestamp, text, pad = nil)
197
207
  _, column = IO.console_size
198
208
  delimiter = ": "
199
209
  timestamp_width = timestamp.to_s.size
200
210
  subject_width = column - timestamp_width - delimiter.size - 1
211
+ subject_width -= pad.size unless pad.nil?
201
212
 
202
213
  subject = remove_heading_markup(text[0])
203
214
 
204
215
  ts_part = "#{timestamp.to_s} "[0..(timestamp_width - 1)]
216
+ ts_part.prepend(pad) unless pad.nil?
205
217
  sj_part = truncate_str(subject, subject_width)
206
218
 
207
219
  ts_part + delimiter + sj_part
@@ -210,6 +222,7 @@ module Rbnotes
210
222
  ##
211
223
  # Finds all notes those timestamps match to given patterns in the
212
224
  # given repository. Returns an Array contains Timestamp objects.
225
+ # The returned Array is sorted by Timestamp.
213
226
  #
214
227
  # :call-seq:
215
228
  # find_notes(Array of timestamp patterns, Textrepo::Repository)
@@ -329,6 +342,36 @@ module Rbnotes
329
342
  dates
330
343
  end
331
344
 
345
+ def dates_in_this_month
346
+ today = Time.now
347
+ first_date = date(Time.new(today.year, today.mon, 1))
348
+ dates_in_month(first_date)
349
+ end
350
+
351
+ def dates_in_last_month
352
+ today = Time.now
353
+ first_date_of_this_month = date(Time.new(today.year, today.mon, 1))
354
+ dates_in_month(first_date_of_this_month.prev_month)
355
+ end
356
+
357
+ def dates_in_month(first_date)
358
+ days = days_in_month(first_date.mon, leap: first_date.leap?)
359
+ dates = [first_date]
360
+ 1.upto(days - 1) { |i| dates << first_date.next_day(i) }
361
+ dates
362
+ end
363
+
364
+ DAYS = {
365
+ # 1 2 3 4 5 6 7 8 9 10 11 12
366
+ # Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
367
+ false => [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
368
+ true => [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
369
+ }
370
+
371
+ def days_in_month(mon, leap: false)
372
+ DAYS[leap][mon]
373
+ end
374
+
332
375
  def truncate_str(str, size)
333
376
  count = 0
334
377
  result = ""
@@ -1,4 +1,4 @@
1
1
  module Rbnotes
2
- VERSION = "0.4.11"
3
- RELEASE = "2020-12-07"
2
+ VERSION = "0.4.12"
3
+ RELEASE = "2020-12-18"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbnotes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.11
4
+ version: 0.4.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - mnbi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-07 00:00:00.000000000 Z
11
+ date: 2020-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: textrepo
@@ -58,6 +58,9 @@ files:
58
58
  - bin/setup
59
59
  - conf/config.yml
60
60
  - conf/config_deve.yml
61
+ - conf/config_deve_fzf_no_opts.yml
62
+ - conf/config_deve_no_picker.yml
63
+ - conf/config_deve_peco.yml
61
64
  - conf/config_test.yml
62
65
  - etc/zsh/_rbnotes
63
66
  - exe/rbnotes