rbnotes 0.4.3 → 0.4.8
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/CHANGELOG.md +40 -13
- data/Gemfile.lock +2 -2
- data/README.md +4 -4
- data/conf/config.yml +1 -0
- data/conf/config_deve.yml +1 -0
- data/exe/rbnotes +5 -0
- data/lib/rbnotes.rb +6 -0
- data/lib/rbnotes/commands.rb +1 -1
- data/lib/rbnotes/commands/add.rb +2 -3
- data/lib/rbnotes/commands/delete.rb +1 -1
- data/lib/rbnotes/commands/export.rb +1 -1
- data/lib/rbnotes/commands/list.rb +30 -63
- data/lib/rbnotes/commands/pick.rb +47 -0
- data/lib/rbnotes/commands/search.rb +47 -4
- data/lib/rbnotes/commands/show.rb +1 -1
- data/lib/rbnotes/commands/update.rb +3 -4
- data/lib/rbnotes/error.rb +11 -0
- data/lib/rbnotes/utils.rb +173 -8
- data/lib/rbnotes/version.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de852db7e0679690dc371c256eb8f65a5f8022772b04ed6024c4f4b55c5cebae
|
|
4
|
+
data.tar.gz: 27764e6f940f29be341745d50db658ef419af0e576b674533398910694f294f7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de49f9046ad826a346cabd37bab304e1d0ab08dfda4e6f5ca6ce7b216cc717ed39645baf092aa993e8287bd65761a605d1f2d1c158e465bc75efbac68ceeb785
|
|
7
|
+
data.tar.gz: 9c28c82c7b486695a5e5fe5875fda39e69bc1673e85434368fba4a2a1a71c0cff1918e60c88ecfd43bf22546bbb79d121319044b1fb068da5445dbc78b239639
|
data/CHANGELOG.md
CHANGED
|
@@ -7,23 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
Nothing to record here.
|
|
9
9
|
|
|
10
|
+
## [0.4.8] - 2020-11-16
|
|
11
|
+
### Fixed
|
|
12
|
+
- Fix issue #65: messy output of the `search` command.
|
|
13
|
+
|
|
14
|
+
## [0.4.7] - 2020-11-15
|
|
15
|
+
### Changed
|
|
16
|
+
- Beautify output of the `search` command. (#63)
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Fix issue #61: `list` command fails in pipeline.
|
|
20
|
+
|
|
21
|
+
## [0.4.6] - 2020-11-13
|
|
22
|
+
### Added
|
|
23
|
+
- Add a new command `pick` to select a note with picker program. (#59)
|
|
24
|
+
|
|
25
|
+
## [0.4.5] - 2020-11-12
|
|
26
|
+
### Changed
|
|
27
|
+
- Add a feature to accept multiple args for `list`. (#57)
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- Fix issue #54: Notes list does not sort correctly.
|
|
31
|
+
|
|
32
|
+
## [0.4.4] - 2020-11-09
|
|
33
|
+
### Changed
|
|
34
|
+
- Add a feature to use a keyword as an argument for `list`. (#47)
|
|
35
|
+
|
|
10
36
|
## [0.4.3] - 2020-11-08
|
|
11
37
|
### Added
|
|
12
|
-
- Add a new command `export` to write out a note into a file (#51)
|
|
38
|
+
- Add a new command `export` to write out a note into a file. (#51)
|
|
13
39
|
- Add individual help for each command. (#42)
|
|
14
40
|
|
|
15
41
|
### Fixed
|
|
16
|
-
- Fix `add` fails without modification
|
|
42
|
+
- Fix issue #48: `add` fails without modification.
|
|
17
43
|
|
|
18
44
|
## [0.4.2] - 2020-11-05
|
|
19
|
-
###
|
|
45
|
+
### Changed
|
|
20
46
|
- Add a feature to keep the timestamp in `update` command. (#44)
|
|
21
47
|
|
|
22
48
|
### Fixed
|
|
23
49
|
- Fix issue #45: hanging up of `add` command.
|
|
24
50
|
|
|
25
51
|
## [0.4.1] - 2020-11-04
|
|
26
|
-
###
|
|
52
|
+
### Changed
|
|
27
53
|
- Add a feature to accept a timestamp in `add` command. (#34)
|
|
28
54
|
|
|
29
55
|
## [0.4.0] - 2020-11-03
|
|
@@ -32,20 +58,21 @@ Nothing to record here.
|
|
|
32
58
|
|
|
33
59
|
## [0.3.1] - 2020-10-30
|
|
34
60
|
### Added
|
|
35
|
-
- Add feature to specify configuration file in the command
|
|
61
|
+
- Add a feature to specify configuration file in the command
|
|
62
|
+
line. (#21)
|
|
36
63
|
|
|
37
64
|
## [0.3.0] - 2020-10-29
|
|
38
|
-
###
|
|
65
|
+
### Changed
|
|
39
66
|
- Add feature to read argument from the standard input. (#27)
|
|
40
67
|
|
|
41
68
|
## [0.2.2] - 2020-10-27
|
|
42
|
-
###
|
|
43
|
-
- Add feature to accept a timestamp pattern in `list` command. (#22)
|
|
69
|
+
### Changed
|
|
70
|
+
- Add a feature to accept a timestamp pattern in `list` command. (#22)
|
|
44
71
|
|
|
45
72
|
## [0.2.1] - 2020-10-25
|
|
46
73
|
### Added
|
|
47
|
-
- Add feature to load the configuration from an external file.
|
|
48
|
-
- Add description about the configuration file in README.md
|
|
74
|
+
- Add a feature to load the configuration from an external file.
|
|
75
|
+
- Add a description about the configuration file in README.md.
|
|
49
76
|
|
|
50
77
|
## [0.2.0] - 2020-10-23
|
|
51
78
|
### Added
|
|
@@ -55,15 +82,15 @@ Nothing to record here.
|
|
|
55
82
|
- Add a new task into `Rakefile` to generate RI docs.
|
|
56
83
|
- The intention of the task is to verify RI docs.
|
|
57
84
|
|
|
58
|
-
###
|
|
85
|
+
### Changed
|
|
59
86
|
- Refactor some tests.
|
|
60
87
|
|
|
61
88
|
## [0.1.3] - 2020-10-15
|
|
62
|
-
###
|
|
89
|
+
### Changed
|
|
63
90
|
- Add help text for the `conf` command.
|
|
64
91
|
|
|
65
92
|
## [0.1.2] - 2020-10-15
|
|
66
|
-
###
|
|
93
|
+
### Changed
|
|
67
94
|
- Adapt the API change in `textrepo` (0.4.0).
|
|
68
95
|
|
|
69
96
|
## [0.1.0] - 2020-10-12
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
rbnotes (0.4.
|
|
4
|
+
rbnotes (0.4.8)
|
|
5
5
|
textrepo (~> 0.5.4)
|
|
6
6
|
unicode-display_width (~> 1.7)
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ GEM
|
|
|
10
10
|
specs:
|
|
11
11
|
minitest (5.14.2)
|
|
12
12
|
rake (13.0.1)
|
|
13
|
-
textrepo (0.5.
|
|
13
|
+
textrepo (0.5.7)
|
|
14
14
|
unicode-display_width (1.7.0)
|
|
15
15
|
|
|
16
16
|
PLATFORMS
|
data/README.md
CHANGED
|
@@ -164,10 +164,10 @@ don't have to set `:searcher_options` for them.
|
|
|
164
164
|
|
|
165
165
|
| searcher | default options in `textrepo` |
|
|
166
166
|
|:---------|:---------------------------------------------------|
|
|
167
|
-
| `grep` | `["-i", "-n", "-R", "-E"]`
|
|
168
|
-
| `egrep` | `["-i", "-n", "-R"]`
|
|
169
|
-
| `ggrep` | `["-i", "-n", "-R", "-E"]`
|
|
170
|
-
| `gegrep` | `["-i", "-n", "-R"]`
|
|
167
|
+
| `grep` | `["-i", "-n", "-H", "-R", "-E"]` |
|
|
168
|
+
| `egrep` | `["-i", "-n", "-H", "-R"]` |
|
|
169
|
+
| `ggrep` | `["-i", "-n", "-H", "-R", "-E"]` |
|
|
170
|
+
| `gegrep` | `["-i", "-n", "-H", "-R"]` |
|
|
171
171
|
| `rg` | `["-S", "-n", "--no-heading", "--color", "never"]` |
|
|
172
172
|
|
|
173
173
|
Those searcher names are used in macOS (with Homebrew). Any other OS
|
data/conf/config.yml
CHANGED
data/conf/config_deve.yml
CHANGED
data/exe/rbnotes
CHANGED
|
@@ -48,6 +48,11 @@ app = App.new
|
|
|
48
48
|
begin
|
|
49
49
|
app.parse_global_options(ARGV)
|
|
50
50
|
app.run(ARGV)
|
|
51
|
+
rescue Errno::EPIPE => e
|
|
52
|
+
# Fix issue #61: When the pipeline which rbnotes connects is
|
|
53
|
+
# discarded by the other program, the execption was raised. It does
|
|
54
|
+
# not end abnormally for rbnotes. So, just ignores the exception.
|
|
55
|
+
exit 0
|
|
51
56
|
rescue MissingArgumentError, MissingTimestampError,
|
|
52
57
|
NoEditorError, ProgramAbortError,
|
|
53
58
|
Textrepo::InvalidTimestampStringError,
|
data/lib/rbnotes.rb
CHANGED
data/lib/rbnotes/commands.rb
CHANGED
|
@@ -61,7 +61,7 @@ Example usage:
|
|
|
61
61
|
#{Rbnotes::NAME} delete [TIMESTAMP]
|
|
62
62
|
#{Rbnotes::NAME} export [TIMESTAMP [FILENAME]]
|
|
63
63
|
#{Rbnotes::NAME} import FILE
|
|
64
|
-
#{Rbnotes::NAME} list [STAMP_PATTERN]
|
|
64
|
+
#{Rbnotes::NAME} list [STAMP_PATTERN|KEYWORD]
|
|
65
65
|
#{Rbnotes::NAME} search PATTERN [STAMP_PATTERN]
|
|
66
66
|
#{Rbnotes::NAME} show [TIMESTAMP]
|
|
67
67
|
#{Rbnotes::NAME} update [TIMESTAMP]
|
data/lib/rbnotes/commands/add.rb
CHANGED
|
@@ -27,7 +27,6 @@ module Rbnotes::Commands
|
|
|
27
27
|
# If none of the above editor is available, the command fails.
|
|
28
28
|
|
|
29
29
|
class Add < Command
|
|
30
|
-
include ::Rbnotes::Utils
|
|
31
30
|
|
|
32
31
|
def description # :nodoc:
|
|
33
32
|
"Add a new note"
|
|
@@ -52,10 +51,10 @@ module Rbnotes::Commands
|
|
|
52
51
|
stamp = @opts[:timestamp] || Textrepo::Timestamp.new(Time.now)
|
|
53
52
|
|
|
54
53
|
candidates = [conf[:editor], ENV["EDITOR"], "nano", "vi"].compact
|
|
55
|
-
editor = find_program(candidates)
|
|
54
|
+
editor = Rbnotes.utils.find_program(candidates)
|
|
56
55
|
raise Rbnotes::NoEditorError, candidates if editor.nil?
|
|
57
56
|
|
|
58
|
-
tmpfile = run_with_tmpfile(editor, stamp.to_s)
|
|
57
|
+
tmpfile = Rbnotes.utils.run_with_tmpfile(editor, stamp.to_s)
|
|
59
58
|
|
|
60
59
|
unless FileTest.exist?(tmpfile)
|
|
61
60
|
puts "Cancel adding, since nothing to store"
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
require "unicode/display_width"
|
|
2
|
-
require "io/console/size"
|
|
3
|
-
|
|
4
1
|
module Rbnotes::Commands
|
|
5
2
|
|
|
6
3
|
##
|
|
@@ -14,14 +11,25 @@ module Rbnotes::Commands
|
|
|
14
11
|
end
|
|
15
12
|
|
|
16
13
|
##
|
|
17
|
-
# Shows a list of notes in the repository.
|
|
18
|
-
# optional. If
|
|
19
|
-
# timestamp
|
|
20
|
-
#
|
|
21
|
-
#
|
|
14
|
+
# Shows a list of notes in the repository. Arguments are
|
|
15
|
+
# optional. If several args are passed, each of them must be a
|
|
16
|
+
# timestamp pattern or a keyword.
|
|
17
|
+
#
|
|
18
|
+
# Any order of timestamp patterns and keywords mixture is
|
|
19
|
+
# acceptable. The redundant patterns are just ignored.
|
|
20
|
+
#
|
|
21
|
+
# A timestamp pattern is a string which would match several
|
|
22
|
+
# Timestamp objects. A timestamp is an instance of
|
|
23
|
+
# Textrepo::Timestamp class.
|
|
22
24
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
+
# A keyword must be one of them:
|
|
26
|
+
#
|
|
27
|
+
# - "today" (or "to")
|
|
28
|
+
# - "yeasterday" (or "ye")
|
|
29
|
+
# - "this_week" (or "tw")
|
|
30
|
+
# - "last_week" (or "lw")
|
|
31
|
+
#
|
|
32
|
+
# Here is several examples of timestamp patterns.
|
|
25
33
|
#
|
|
26
34
|
# "20201027093600_012": a complete string to represent a timestamp
|
|
27
35
|
# - this pattern would match exactly one Timestamp object
|
|
@@ -43,24 +51,23 @@ module Rbnotes::Commands
|
|
|
43
51
|
# execute(Array, Rbnotes::Conf or Hash) -> nil
|
|
44
52
|
|
|
45
53
|
def execute(args, conf)
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
patterns = Rbnotes.utils.expand_keyword_in_args(args)
|
|
48
55
|
@repo = Textrepo.init(conf)
|
|
49
56
|
# newer stamp shoud be above
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
puts make_headline(timestamp)
|
|
57
|
+
Rbnotes.utils.find_notes(patterns, @repo).each { |timestamp|
|
|
58
|
+
puts Rbnotes.utils.make_headline(timestamp, @repo.read(timestamp))
|
|
53
59
|
}
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
def help # :nodoc:
|
|
57
63
|
puts <<HELP
|
|
58
64
|
usage:
|
|
59
|
-
#{Rbnotes::NAME} list [STAMP_PATTERN]
|
|
65
|
+
#{Rbnotes::NAME} list [STAMP_PATTERN|KEYWORD]
|
|
60
66
|
|
|
61
67
|
Show a list of notes. When no arguments, make a list with all notes
|
|
62
68
|
in the repository. When specified STAMP_PATTERN, only those match the
|
|
63
|
-
pattern are listed.
|
|
69
|
+
pattern are listed. Instead of STAMP_PATTERN, some KEYWORDs could be
|
|
70
|
+
used.
|
|
64
71
|
|
|
65
72
|
STAMP_PATTERN must be:
|
|
66
73
|
|
|
@@ -69,56 +76,16 @@ STAMP_PATTERN must be:
|
|
|
69
76
|
(c) year and month part: "202010"
|
|
70
77
|
(d) year part only: "2020"
|
|
71
78
|
(e) date part only: "1030"
|
|
72
|
-
HELP
|
|
73
|
-
end
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
KEYWORD:
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
- "today" (or "to")
|
|
83
|
+
- "yeasterday" (or "ye")
|
|
84
|
+
- "this_week" (or "tw")
|
|
85
|
+
- "last_week" (or "lw")
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
# Makes a headline with the timestamp and subject of the notes, it
|
|
82
|
-
# looks like as follows:
|
|
83
|
-
#
|
|
84
|
-
# |<------------------ console column size ------------------->|
|
|
85
|
-
# +-- timestamp ---+ +- subject (the 1st line of each note) -+
|
|
86
|
-
# | | | |
|
|
87
|
-
# 20101010001000_123: I love Macintosh. [EOL]
|
|
88
|
-
# 20100909090909_999: This is very very long long loooong subje[EOL]
|
|
89
|
-
# ++
|
|
90
|
-
# ^--- delimiter (2 characters)
|
|
91
|
-
#
|
|
92
|
-
# The subject part will truncate when it is long.
|
|
93
|
-
|
|
94
|
-
def make_headline(timestamp)
|
|
95
|
-
_, column = IO.console_size
|
|
96
|
-
delimiter = ": "
|
|
97
|
-
subject_width = column - TIMESTAMP_STR_MAX_WIDTH - delimiter.size - 1
|
|
98
|
-
|
|
99
|
-
subject = remove_heading_markup(@repo.read(timestamp)[0])
|
|
100
|
-
|
|
101
|
-
ts_part = "#{timestamp.to_s} "[0..(TIMESTAMP_STR_MAX_WIDTH - 1)]
|
|
102
|
-
sj_part = truncate_str(subject, subject_width)
|
|
103
|
-
|
|
104
|
-
ts_part + delimiter + sj_part
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def truncate_str(str, size)
|
|
108
|
-
count = 0
|
|
109
|
-
result = ""
|
|
110
|
-
str.each_char { |c|
|
|
111
|
-
count += Unicode::DisplayWidth.of(c)
|
|
112
|
-
break if count > size
|
|
113
|
-
result << c
|
|
114
|
-
}
|
|
115
|
-
result
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def remove_heading_markup(str)
|
|
119
|
-
str.sub(/^#+ +/, '')
|
|
87
|
+
HELP
|
|
120
88
|
end
|
|
121
89
|
|
|
122
|
-
# :startdoc:
|
|
123
90
|
end
|
|
124
91
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Rbnotes::Commands
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Picks a timestamp with a picker program, like `fzf`.
|
|
5
|
+
|
|
6
|
+
class Pick < Command
|
|
7
|
+
|
|
8
|
+
def description # :nodoc:
|
|
9
|
+
"Pick a timestamp with a picker program"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def execute(args, conf)
|
|
13
|
+
patterns = Rbnotes.utils.expand_keyword_in_args(args)
|
|
14
|
+
@repo = Textrepo.init(conf)
|
|
15
|
+
|
|
16
|
+
list = []
|
|
17
|
+
Rbnotes.utils.find_notes(patterns, @repo).each { |timestamp|
|
|
18
|
+
list << Rbnotes.utils.make_headline(timestamp, @repo.read(timestamp))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
picker = conf[:picker]
|
|
22
|
+
unless picker.nil?
|
|
23
|
+
require 'open3'
|
|
24
|
+
result = Open3.pipeline_rw(picker) { |stdin, stdout, _|
|
|
25
|
+
stdin.puts list
|
|
26
|
+
stdin.close
|
|
27
|
+
stdout.read
|
|
28
|
+
}
|
|
29
|
+
puts result
|
|
30
|
+
else
|
|
31
|
+
puts list
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def help # :nodoc:
|
|
36
|
+
puts <<HELP
|
|
37
|
+
usage:
|
|
38
|
+
#{Rbnotes::NAME} pick
|
|
39
|
+
|
|
40
|
+
Pick a timestamp with a picker program, like `fzf`. This command
|
|
41
|
+
refers the configuration setting of ":picker". If no picker program
|
|
42
|
+
is specified, it will behave as same as "list" command.
|
|
43
|
+
|
|
44
|
+
HELP
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -39,11 +39,8 @@ module Rbnotes::Commands
|
|
|
39
39
|
result = repo.search(pattern, timestamp_pattern)
|
|
40
40
|
rescue Textrepo::InvalidSearchResultError => e
|
|
41
41
|
puts e.message
|
|
42
|
-
else
|
|
43
|
-
result.each { |stamp, num, match|
|
|
44
|
-
puts "#{stamp}:#{num}:#{match}"
|
|
45
|
-
}
|
|
46
42
|
end
|
|
43
|
+
print_search_result(result.map{ |e| SearchEntry.new(*e) })
|
|
47
44
|
end
|
|
48
45
|
|
|
49
46
|
def help # :nodoc:
|
|
@@ -63,5 +60,51 @@ STAMP_PATTERN must be:
|
|
|
63
60
|
(e) date part only: "1030"
|
|
64
61
|
HELP
|
|
65
62
|
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# Each entry of search result is:
|
|
67
|
+
#
|
|
68
|
+
# [<timestamp>, <line_number>, <matched_text>]
|
|
69
|
+
#
|
|
70
|
+
# The sort must be done in;
|
|
71
|
+
#
|
|
72
|
+
# - descending order for <timestamp>,
|
|
73
|
+
# - ascending ordier for <line_number>.
|
|
74
|
+
#
|
|
75
|
+
|
|
76
|
+
SearchEntry = Struct.new(:timestamp, :line_number, :matched_text) {
|
|
77
|
+
def timestamp_size
|
|
78
|
+
timestamp.to_s.size
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def line_number_digits_size
|
|
82
|
+
line_number.to_s.size
|
|
83
|
+
end
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
def print_search_result(entries)
|
|
87
|
+
maxcol_stamp = entries.map(&:timestamp_size).max
|
|
88
|
+
maxcol_num = entries.map(&:line_number_digits_size).max
|
|
89
|
+
|
|
90
|
+
sort(entries).each { |e|
|
|
91
|
+
stamp_display = "%- *s" % [maxcol_stamp, e.timestamp]
|
|
92
|
+
num_display = "%*d" % [maxcol_num, e.line_number]
|
|
93
|
+
|
|
94
|
+
puts "#{stamp_display}: #{num_display}: #{e.matched_text}"
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def sort(search_result)
|
|
99
|
+
search_result.sort { |a, b|
|
|
100
|
+
stamp_comparison = (b.timestamp <=> a.timestamp)
|
|
101
|
+
if stamp_comparison == 0
|
|
102
|
+
a.line_number <=> b.line_number
|
|
103
|
+
else
|
|
104
|
+
stamp_comparison
|
|
105
|
+
end
|
|
106
|
+
}
|
|
107
|
+
end
|
|
108
|
+
|
|
66
109
|
end
|
|
67
110
|
end
|
|
@@ -18,7 +18,6 @@ module Rbnotes::Commands
|
|
|
18
18
|
# none of editors is available, the execution fails.
|
|
19
19
|
|
|
20
20
|
class Update < Command
|
|
21
|
-
include ::Rbnotes::Utils
|
|
22
21
|
|
|
23
22
|
def description # :nodoc:
|
|
24
23
|
"Update the content of a note"
|
|
@@ -44,8 +43,8 @@ module Rbnotes::Commands
|
|
|
44
43
|
end
|
|
45
44
|
end
|
|
46
45
|
|
|
47
|
-
target_stamp = Rbnotes
|
|
48
|
-
editor = find_editor(conf[:editor])
|
|
46
|
+
target_stamp = Rbnotes.utils.read_timestamp(args)
|
|
47
|
+
editor = Rbnotes.utils.find_editor(conf[:editor])
|
|
49
48
|
repo = Textrepo.init(conf)
|
|
50
49
|
|
|
51
50
|
text = nil
|
|
@@ -55,7 +54,7 @@ module Rbnotes::Commands
|
|
|
55
54
|
raise Rbnotes::MissingTimestampError, target_stamp
|
|
56
55
|
end
|
|
57
56
|
|
|
58
|
-
tmpfile = run_with_tmpfile(editor, target_stamp.to_s, text)
|
|
57
|
+
tmpfile = Rbnotes.utils.run_with_tmpfile(editor, target_stamp.to_s, text)
|
|
59
58
|
text = File.readlines(tmpfile, :chomp => true)
|
|
60
59
|
|
|
61
60
|
unless text.empty?
|
data/lib/rbnotes/error.rb
CHANGED
|
@@ -11,6 +11,7 @@ module Rbnotes
|
|
|
11
11
|
MISSING_TIMESTAMP = "missing timestamp: %s"
|
|
12
12
|
NO_EDITOR = "No editor is available: %s"
|
|
13
13
|
PROGRAM_ABORT = "External program was aborted: %s"
|
|
14
|
+
UNKNOWN_KEYWORD = "Unknown keyword: %s"
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
# :startdoc:
|
|
@@ -53,4 +54,14 @@ module Rbnotes
|
|
|
53
54
|
super(ErrMsg::PROGRAM_ABORT % cmdline.join(" "))
|
|
54
55
|
end
|
|
55
56
|
end
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
# An eeror raised when an unknown keyword was specified as a
|
|
60
|
+
# timestamp string pattern.
|
|
61
|
+
|
|
62
|
+
class UnknownKeywordError < Error
|
|
63
|
+
def initialize(keyword)
|
|
64
|
+
super(ErrMsg::UNKNOWN_KEYWORD % keyword)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
56
67
|
end
|
data/lib/rbnotes/utils.rb
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
require "singleton"
|
|
1
2
|
require "pathname"
|
|
2
3
|
require "tmpdir"
|
|
4
|
+
require "date"
|
|
5
|
+
require "io/console/size"
|
|
6
|
+
|
|
7
|
+
require "unicode/display_width"
|
|
3
8
|
|
|
4
9
|
module Rbnotes
|
|
5
10
|
##
|
|
6
11
|
# Defines several utility methods those are intended to be used in
|
|
7
12
|
# Rbnotes classes.
|
|
8
13
|
#
|
|
9
|
-
|
|
14
|
+
class Utils
|
|
15
|
+
include Singleton
|
|
10
16
|
|
|
11
17
|
##
|
|
12
18
|
# Finds a external editor program which is specified with the
|
|
@@ -26,7 +32,6 @@ module Rbnotes
|
|
|
26
32
|
def find_editor(preferred_editor)
|
|
27
33
|
find_program([preferred_editor, ENV["EDITOR"], "nano", "vi"].compact)
|
|
28
34
|
end
|
|
29
|
-
module_function :find_editor
|
|
30
35
|
|
|
31
36
|
##
|
|
32
37
|
# Finds a executable program in given names. When the executable
|
|
@@ -60,7 +65,6 @@ module Rbnotes
|
|
|
60
65
|
}
|
|
61
66
|
nil
|
|
62
67
|
end
|
|
63
|
-
module_function :find_program
|
|
64
68
|
|
|
65
69
|
##
|
|
66
70
|
# Executes the program with passing the given filename as argument.
|
|
@@ -84,7 +88,6 @@ module Rbnotes
|
|
|
84
88
|
raise ProgramAbortError, [prog, tmpfile] unless rc
|
|
85
89
|
tmpfile
|
|
86
90
|
end
|
|
87
|
-
module_function :run_with_tmpfile
|
|
88
91
|
|
|
89
92
|
##
|
|
90
93
|
# Generates a Textrepo::Timestamp object from a String which comes
|
|
@@ -98,7 +101,6 @@ module Rbnotes
|
|
|
98
101
|
str = args.shift || read_arg($stdin)
|
|
99
102
|
Textrepo::Timestamp.parse_s(str)
|
|
100
103
|
end
|
|
101
|
-
module_function :read_timestamp
|
|
102
104
|
|
|
103
105
|
##
|
|
104
106
|
# Reads an argument from the IO object. Typically, it is intended
|
|
@@ -119,7 +121,111 @@ module Rbnotes
|
|
|
119
121
|
nil
|
|
120
122
|
end
|
|
121
123
|
end
|
|
122
|
-
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# Parses the given arguments and expand keywords if found. Each
|
|
127
|
+
# of the arguments is assumed to represent a timestamp pattern (or
|
|
128
|
+
# a keyword to be expand into several timestamp pattern). Returns
|
|
129
|
+
# an Array of timestamp partterns (each pattern is a String
|
|
130
|
+
# object).
|
|
131
|
+
#
|
|
132
|
+
# A timestamp pattern looks like:
|
|
133
|
+
#
|
|
134
|
+
# (a) full qualified timestamp (with suffix): "20201030160200"
|
|
135
|
+
# (b) year and date part: "20201030"
|
|
136
|
+
# (c) year and month part: "202010"
|
|
137
|
+
# (d) year part only: "2020"
|
|
138
|
+
# (e) date part only: "1030"
|
|
139
|
+
#
|
|
140
|
+
# KEYWORD:
|
|
141
|
+
#
|
|
142
|
+
# - "today" (or "to")
|
|
143
|
+
# - "yeasterday" (or "ye")
|
|
144
|
+
# - "this_week" (or "tw")
|
|
145
|
+
# - "last_week" (or "lw")
|
|
146
|
+
#
|
|
147
|
+
# :call-seq:
|
|
148
|
+
# expand_keyword_in_args(Array of Strings) -> Array of Strings
|
|
149
|
+
|
|
150
|
+
def expand_keyword_in_args(args)
|
|
151
|
+
return [nil] if args.empty?
|
|
152
|
+
|
|
153
|
+
patterns = []
|
|
154
|
+
while args.size > 0
|
|
155
|
+
arg = args.shift
|
|
156
|
+
if ["today", "to", "yesterday", "ye",
|
|
157
|
+
"this_week", "tw", "last_week", "lw"].include?(arg)
|
|
158
|
+
patterns.concat(Rbnotes.utils.expand_keyword(arg))
|
|
159
|
+
else
|
|
160
|
+
patterns << arg
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
patterns.sort.uniq
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
##
|
|
167
|
+
# Expands a keyword to timestamp strings.
|
|
168
|
+
#
|
|
169
|
+
# :call-seq:
|
|
170
|
+
# expand_keyword(keyword as String) -> Array of timestamp Strings
|
|
171
|
+
|
|
172
|
+
def expand_keyword(keyword)
|
|
173
|
+
patterns = []
|
|
174
|
+
case keyword
|
|
175
|
+
when "today", "to"
|
|
176
|
+
patterns << timestamp_pattern(date_of_today)
|
|
177
|
+
when "yesterday", "ye"
|
|
178
|
+
patterns << timestamp_pattern(date_of_yesterday)
|
|
179
|
+
when "this_week", "tw"
|
|
180
|
+
patterns.concat(dates_in_this_week.map { |d| timestamp_pattern(d) })
|
|
181
|
+
when "last_week", "lw"
|
|
182
|
+
patterns.concat(dates_in_last_week.map { |d| timestamp_pattern(d) })
|
|
183
|
+
else
|
|
184
|
+
raise UnknownKeywordError, keyword
|
|
185
|
+
end
|
|
186
|
+
patterns
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
##
|
|
190
|
+
# Makes a headline with the timestamp and subject of the notes, it
|
|
191
|
+
# looks like as follows:
|
|
192
|
+
#
|
|
193
|
+
# |<------------------ console column size ------------------->|
|
|
194
|
+
# +-- timestamp ---+ +- subject (the 1st line of each note) -+
|
|
195
|
+
# | | | |
|
|
196
|
+
# 20101010001000_123: I love Macintosh. [EOL]
|
|
197
|
+
# 20100909090909_999: This is very very long long loooong subje[EOL]
|
|
198
|
+
# ++
|
|
199
|
+
# ^--- delimiter (2 characters)
|
|
200
|
+
#
|
|
201
|
+
# The subject part will truncate when it is long.
|
|
202
|
+
|
|
203
|
+
def make_headline(timestamp, text)
|
|
204
|
+
_, column = IO.console_size
|
|
205
|
+
delimiter = ": "
|
|
206
|
+
timestamp_width = timestamp.to_s.size
|
|
207
|
+
subject_width = column - timestamp_width - delimiter.size - 1
|
|
208
|
+
|
|
209
|
+
subject = remove_heading_markup(text[0])
|
|
210
|
+
|
|
211
|
+
ts_part = "#{timestamp.to_s} "[0..(timestamp_width - 1)]
|
|
212
|
+
sj_part = truncate_str(subject, subject_width)
|
|
213
|
+
|
|
214
|
+
ts_part + delimiter + sj_part
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
##
|
|
218
|
+
# Finds all notes those timestamps match to given patterns in the
|
|
219
|
+
# given repository. Returns an Array contains Timestamp objects.
|
|
220
|
+
#
|
|
221
|
+
# :call-seq:
|
|
222
|
+
# find_notes(Array of timestamp patterns, Textrepo::Repository)
|
|
223
|
+
|
|
224
|
+
def find_notes(timestamp_patterns, repo)
|
|
225
|
+
timestamp_patterns.map { |pat|
|
|
226
|
+
repo.entries(pat)
|
|
227
|
+
}.flatten.sort{ |a, b| b <=> a }.uniq
|
|
228
|
+
end
|
|
123
229
|
|
|
124
230
|
# :stopdoc:
|
|
125
231
|
|
|
@@ -132,11 +238,70 @@ module Rbnotes
|
|
|
132
238
|
}
|
|
133
239
|
found.compact[0]
|
|
134
240
|
end
|
|
135
|
-
module_function :search_in_path
|
|
136
241
|
|
|
137
242
|
def add_extension(basename)
|
|
138
243
|
"#{basename}.md"
|
|
139
244
|
end
|
|
140
|
-
|
|
245
|
+
|
|
246
|
+
def timestamp_pattern(date)
|
|
247
|
+
date.strftime("%Y%m%d")
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def date_of_today
|
|
251
|
+
date(Time.now)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def date_of_yesterday
|
|
255
|
+
date(Time.now).prev_day
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def date(time)
|
|
259
|
+
Date.new(time.year, time.mon, time.day)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def dates_in_this_week
|
|
263
|
+
dates_in_week(start_date_in_this_week)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def dates_in_last_week
|
|
267
|
+
dates_in_week(start_date_in_last_week)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def start_date_in_this_week
|
|
271
|
+
today = Time.now
|
|
272
|
+
Date.new(today.year, today.mon, today.day).prev_day(wday(today))
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def start_date_in_last_week
|
|
276
|
+
start_date_in_this_week.prev_day(7)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def wday(time)
|
|
280
|
+
(time.wday - 1) % 7
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def dates_in_week(start_date)
|
|
284
|
+
dates = [start_date]
|
|
285
|
+
1.upto(6) { |i| dates << start_date.next_day(i) }
|
|
286
|
+
dates
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def truncate_str(str, size)
|
|
290
|
+
count = 0
|
|
291
|
+
result = ""
|
|
292
|
+
str.each_char { |c|
|
|
293
|
+
count += Unicode::DisplayWidth.of(c)
|
|
294
|
+
break if count > size
|
|
295
|
+
result << c
|
|
296
|
+
}
|
|
297
|
+
result
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def remove_heading_markup(str)
|
|
301
|
+
str.sub(/^#+ +/, '')
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# :startdoc:
|
|
305
|
+
|
|
141
306
|
end
|
|
142
307
|
end
|
data/lib/rbnotes/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.4.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- mnbi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-11-
|
|
11
|
+
date: 2020-11-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: textrepo
|
|
@@ -68,6 +68,7 @@ files:
|
|
|
68
68
|
- lib/rbnotes/commands/help.rb
|
|
69
69
|
- lib/rbnotes/commands/import.rb
|
|
70
70
|
- lib/rbnotes/commands/list.rb
|
|
71
|
+
- lib/rbnotes/commands/pick.rb
|
|
71
72
|
- lib/rbnotes/commands/search.rb
|
|
72
73
|
- lib/rbnotes/commands/show.rb
|
|
73
74
|
- lib/rbnotes/commands/update.rb
|