rbnotes 0.2.2 → 0.4.2
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 +24 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -4
- data/README.md +55 -4
- data/conf/config.yml +7 -0
- data/conf/config_deve.yml +7 -0
- data/conf/config_test.yml +5 -0
- data/exe/rbnotes +30 -4
- data/lib/rbnotes/commands.rb +60 -22
- data/lib/rbnotes/commands/add.rb +52 -9
- data/lib/rbnotes/commands/delete.rb +10 -14
- data/lib/rbnotes/commands/import.rb +3 -3
- data/lib/rbnotes/commands/list.rb +13 -8
- data/lib/rbnotes/commands/search.rb +44 -0
- data/lib/rbnotes/commands/show.rb +12 -16
- data/lib/rbnotes/commands/update.rb +24 -13
- data/lib/rbnotes/error.rb +6 -4
- data/lib/rbnotes/utils.rb +35 -0
- data/lib/rbnotes/version.rb +2 -2
- data/rbnotes.gemspec +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a43f87f2f4b03c9f8e27d06eb4fc426abb6dafbd420d5bbbcdb4ec2ea38d497
|
4
|
+
data.tar.gz: 0f9c060bfefde2e6c3f611d7d7e683fdd5ced20327b3ae6a3cbfcac39cdaf5a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e28ed07cde6f385945a9ed3b92ff6402b8ec2dcdd2a75f6d1d0a8e391a2bf5e000245d708903fa815ec5faecfb2cfc39034240ef83a44818bd9a9ccb39c326ed
|
7
|
+
data.tar.gz: 0e2a87a92c5b5a674d143c3d4461577976e5f62508d67ebbdb5202bfbc771c04aacaab520f8860e29429411f50677351f059e01a919e0836fc889b6285507fbd
|
data/CHANGELOG.md
CHANGED
@@ -7,8 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
## [Unreleased]
|
8
8
|
Nothing to record here.
|
9
9
|
|
10
|
+
## [0.4.2] - 2020-11-05
|
11
|
+
### Added
|
12
|
+
- Add a feature to keep the timestamp in `update` command. (#44)
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
- Fix issue #45: hanging up of `add` command.
|
16
|
+
|
17
|
+
## [0.4.1] - 2020-11-04
|
18
|
+
### Added
|
19
|
+
- Add a feature to accept a timestamp in `add` command. (#34)
|
20
|
+
|
21
|
+
## [0.4.0] - 2020-11-03
|
22
|
+
### Added
|
23
|
+
- Add a new command `search` to perform full text search. (#33)
|
24
|
+
|
25
|
+
## [0.3.1] - 2020-10-30
|
26
|
+
### Added
|
27
|
+
- Add feature to specify configuration file in the command line. (#21)
|
28
|
+
|
29
|
+
## [0.3.0] - 2020-10-29
|
30
|
+
### Added
|
31
|
+
- Add feature to read argument from the standard input. (#27)
|
32
|
+
|
10
33
|
## [0.2.2] - 2020-10-27
|
11
|
-
###
|
34
|
+
### Added
|
12
35
|
- Add feature to accept a timestamp pattern in `list` command. (#22)
|
13
36
|
|
14
37
|
## [0.2.1] - 2020-10-25
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rbnotes (0.
|
5
|
-
textrepo (~> 0.4)
|
4
|
+
rbnotes (0.4.2)
|
5
|
+
textrepo (~> 0.5.4)
|
6
6
|
unicode-display_width (~> 1.7)
|
7
7
|
|
8
8
|
GEM
|
@@ -10,7 +10,7 @@ GEM
|
|
10
10
|
specs:
|
11
11
|
minitest (5.14.2)
|
12
12
|
rake (13.0.1)
|
13
|
-
textrepo (0.4
|
13
|
+
textrepo (0.5.4)
|
14
14
|
unicode-display_width (1.7.0)
|
15
15
|
|
16
16
|
PLATFORMS
|
@@ -20,7 +20,7 @@ DEPENDENCIES
|
|
20
20
|
minitest (~> 5.0)
|
21
21
|
rake (~> 13.0)
|
22
22
|
rbnotes!
|
23
|
-
textrepo (~> 0.4)
|
23
|
+
textrepo (~> 0.5.4)
|
24
24
|
|
25
25
|
BUNDLED WITH
|
26
26
|
2.1.4
|
data/README.md
CHANGED
@@ -4,6 +4,9 @@
|
|
4
4
|
|
5
5
|
Rbnotes is a simple utility to write a note in the single repository.
|
6
6
|
|
7
|
+
This document provides the basic information to use rbnotes.
|
8
|
+
You may find more useful information in [Wiki pages](https://github.com/mnbi/rbnotes/wiki).
|
9
|
+
|
7
10
|
## Installation
|
8
11
|
|
9
12
|
Add this line to your application's Gemfile:
|
@@ -28,12 +31,19 @@ General syntax is:
|
|
28
31
|
rbnotes [global_opts] [command] [command_opts] [args]
|
29
32
|
```
|
30
33
|
|
34
|
+
### Global options
|
35
|
+
|
36
|
+
- "-c CONF_FILE", "--conf"
|
37
|
+
- specifies a configuration file
|
38
|
+
|
31
39
|
### Commands
|
32
40
|
|
33
41
|
- import
|
34
42
|
- imports existing files
|
35
43
|
- list
|
36
44
|
- lists notes in the repository with their timestamps and subject
|
45
|
+
- search
|
46
|
+
- search a word (or words) in the repository
|
37
47
|
- show
|
38
48
|
- shows the content of a note
|
39
49
|
- add
|
@@ -57,6 +67,8 @@ Searches the configuration file in the following order:
|
|
57
67
|
|
58
68
|
None of them is found, then the default configuration is used.
|
59
69
|
|
70
|
+
The global option "-c CONF_FILE" will override the location.
|
71
|
+
|
60
72
|
#### Content
|
61
73
|
|
62
74
|
The format of the configuration file must be written in YAML.
|
@@ -75,14 +87,16 @@ A real example:
|
|
75
87
|
|
76
88
|
``` yaml
|
77
89
|
---
|
78
|
-
:run_mode: :
|
90
|
+
:run_mode: :production
|
79
91
|
:repository_type: :file_system
|
80
92
|
:repository_name: "notes"
|
81
93
|
:repository_base: "~"
|
82
|
-
:pager: "bat"
|
94
|
+
:pager: "bat -l md"
|
83
95
|
:editor: "/usr/local/bin/emacsclient"
|
84
96
|
```
|
85
97
|
|
98
|
+
The content is identical to `conf/config.yml`.
|
99
|
+
|
86
100
|
#### Variables
|
87
101
|
|
88
102
|
##### :run-mode (mandatory)
|
@@ -124,13 +138,50 @@ the `:repository_name` value. It would be:
|
|
124
138
|
|
125
139
|
> :repository_base/:repository_name
|
126
140
|
|
127
|
-
The value
|
128
|
-
|
141
|
+
The value is recommended to be an absolute path in the production
|
142
|
+
time, since relative path will be expanded with the current working
|
143
|
+
directory. However, in the development and testing time, the relative
|
144
|
+
path may be useful. See `conf/config_deve.yml` or
|
145
|
+
`conf/config_test.yml`.
|
146
|
+
|
147
|
+
The short-hand notation of the home directory ("~") is usable.
|
129
148
|
|
130
149
|
##### Miscellaneous variables (optional)
|
131
150
|
|
132
151
|
- :pager : specify a pager program
|
133
152
|
- :editor : specify a editor program
|
153
|
+
- :searcher: specify a program to perform search
|
154
|
+
- :searcher_options: specify options to pass to the searcher program
|
155
|
+
|
156
|
+
Be careful to set `:searcher` and `:searcher_options`. The searcher
|
157
|
+
program must be expected to behave equivalent to `grep` with `-inRE`.
|
158
|
+
At least, its output must be the same format and it must runs
|
159
|
+
recursively to a directory.
|
160
|
+
|
161
|
+
If your favorite searcher is one of the followings, see the default
|
162
|
+
options which `textrepo` sets for those searchers. In most cases, you
|
163
|
+
don't have to set `:searcher_options` for them.
|
164
|
+
|
165
|
+
| searcher | default options in `textrepo` |
|
166
|
+
|:---------|:---------------------------------------------------|
|
167
|
+
| `grep` | `["-i", "-n", "-R", "-E"]` |
|
168
|
+
| `egrep` | `["-i", "-n", "-R"]` |
|
169
|
+
| `ggrep` | `["-i", "-n", "-R", "-E"]` |
|
170
|
+
| `gegrep` | `["-i", "-n", "-R"]` |
|
171
|
+
| `rg` | `["-S", "-n", "--no-heading", "--color", "never"]` |
|
172
|
+
|
173
|
+
Those searcher names are used in macOS (with Homebrew). Any other OS
|
174
|
+
might use different names.
|
175
|
+
|
176
|
+
- `grep` and `egrep` -> BSD grep (macOS default)
|
177
|
+
- `ggrep` and `gegrep` -> GNU grep
|
178
|
+
- `rg` -> ripgrep
|
179
|
+
|
180
|
+
If the name is different, `:searcher_options` should be set with the
|
181
|
+
same value. For example, if you system use the name `gnugrep` as GNU
|
182
|
+
grep, and you want to use it as the searcher (that is, set `gnugrep`
|
183
|
+
to `:searcher`), you should set `:searcher_options` value with `["-i",
|
184
|
+
"-n", "-R", "-E"]`.
|
134
185
|
|
135
186
|
##### Default values for mandatory variables
|
136
187
|
|
data/conf/config.yml
ADDED
data/exe/rbnotes
CHANGED
@@ -4,20 +4,46 @@ require "rbnotes"
|
|
4
4
|
|
5
5
|
include Rbnotes
|
6
6
|
|
7
|
-
DEBUG = true
|
8
|
-
|
9
7
|
class App
|
8
|
+
def initialize
|
9
|
+
@gopts = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def options
|
13
|
+
@gopts
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_global_options(args)
|
17
|
+
while args.size > 0
|
18
|
+
arg = args.shift
|
19
|
+
case arg
|
20
|
+
when "-c", "--conf"
|
21
|
+
file = args.shift
|
22
|
+
raise ArgumentError, args.unshift(arg) if file.nil?
|
23
|
+
file = File.expand_path(file)
|
24
|
+
raise ArgumentError, "no such file: %s" % file unless FileTest.exist?(file)
|
25
|
+
@gopts[:conf_file] = file
|
26
|
+
else
|
27
|
+
args.unshift(arg)
|
28
|
+
break
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
10
33
|
def run(args)
|
11
34
|
cmd = args.shift
|
12
|
-
Commands.load(cmd).execute(args, Rbnotes.conf)
|
35
|
+
Commands.load(cmd).execute(args, Rbnotes.conf(@gopts[:conf_file]))
|
13
36
|
end
|
14
37
|
end
|
15
38
|
|
16
39
|
app = App.new
|
17
40
|
begin
|
41
|
+
app.parse_global_options(ARGV)
|
18
42
|
app.run(ARGV)
|
19
43
|
rescue MissingArgumentError, MissingTimestampError,
|
20
|
-
NoEditorError, ProgramAbortError
|
44
|
+
NoEditorError, ProgramAbortError,
|
45
|
+
Textrepo::InvalidTimestampStringError,
|
46
|
+
ArgumentError => e
|
21
47
|
puts e.message
|
22
48
|
exit 1
|
23
49
|
end
|
data/lib/rbnotes/commands.rb
CHANGED
@@ -2,16 +2,20 @@ module Rbnotes
|
|
2
2
|
##
|
3
3
|
# This module defines all command classes of rbnotes. Each command
|
4
4
|
# class must be derived from Rbnotes::Commands::Command class.
|
5
|
+
|
5
6
|
module Commands
|
6
7
|
##
|
7
8
|
# The base class for a command class.
|
9
|
+
|
8
10
|
class Command
|
11
|
+
|
9
12
|
##
|
10
13
|
# :call-seq:
|
11
|
-
# Array, Hash -> nil
|
14
|
+
# execute(Array, Hash) -> nil
|
15
|
+
#
|
12
16
|
# - Array: arguments for each command
|
13
17
|
# - Hash : rbnotes configuration
|
14
|
-
|
18
|
+
|
15
19
|
def execute(args, conf)
|
16
20
|
Builtins::DEFAULT_CMD.new.execute(args, conf)
|
17
21
|
end
|
@@ -28,22 +32,55 @@ module Rbnotes
|
|
28
32
|
class Help < Command
|
29
33
|
def execute(_, _)
|
30
34
|
puts <<USAGE
|
31
|
-
usage:
|
35
|
+
usage:
|
36
|
+
rbnotes [-c|--conf CONF_FILE] [command] [args]
|
37
|
+
|
38
|
+
option:
|
39
|
+
-c, --conf [CONF_FILE] : specifiy the configuration file
|
40
|
+
|
41
|
+
CONF_FILE must be written in YAML. To know about details of the
|
42
|
+
configuration file, see README.md or Wiki page.
|
32
43
|
|
33
44
|
command:
|
34
|
-
add
|
35
|
-
|
36
|
-
|
37
|
-
list
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
add : create a new note
|
46
|
+
import FILE : import a FILE into the repository
|
47
|
+
|
48
|
+
list [STAMP_PATTERN] : list notes those timestamp matches PATTERN
|
49
|
+
search PATTERN [STAMP_PATTERN] : search PATTERN
|
50
|
+
|
51
|
+
STAMP_PATTERN must be:
|
52
|
+
|
53
|
+
(a) full qualified timestamp (with suffix): "20201030160200"
|
54
|
+
(b) year and date part: "20201030"
|
55
|
+
(c) year and month part: "202010"
|
56
|
+
(d) year part only: "2020"
|
57
|
+
(e) date part only: "1030"
|
58
|
+
|
59
|
+
PATTERN is a word (or words) to search, it may also be a regular
|
60
|
+
expression.
|
61
|
+
|
62
|
+
show [STAMP] : show the note specified with STAMP
|
63
|
+
update [STAMP] : edit the note with external editor
|
64
|
+
delete [STAMP] : delete the note specified with STAMP
|
65
|
+
|
66
|
+
STAMP must be a sequence of digits to represent year, date and
|
67
|
+
time (and suffix), such "20201030160200" or "20201030160200_012".
|
68
|
+
|
69
|
+
show/update/delete reads its argument from the standard input when
|
70
|
+
no argument was passed in the command line.
|
71
|
+
|
72
|
+
version : print version
|
73
|
+
help : show help
|
74
|
+
|
75
|
+
command for development:
|
76
|
+
conf : print the current configuraitons
|
77
|
+
repo : print the repository path
|
78
|
+
stamp TIME_STR : convert TIME_STR into a timestamp
|
79
|
+
time STAMP : convert STAMP into a time string
|
80
|
+
|
81
|
+
For more information, see Wiki page.
|
82
|
+
- https://github.com/mnbi/rbnotes/wiki
|
83
|
+
|
47
84
|
USAGE
|
48
85
|
end
|
49
86
|
end
|
@@ -64,9 +101,9 @@ USAGE
|
|
64
101
|
|
65
102
|
puts case type
|
66
103
|
when :file_system
|
67
|
-
|
104
|
+
File.expand_path(name, base)
|
68
105
|
else
|
69
|
-
|
106
|
+
File.join(base, name)
|
70
107
|
end
|
71
108
|
end
|
72
109
|
end
|
@@ -113,15 +150,16 @@ USAGE
|
|
113
150
|
# :startdoc:
|
114
151
|
|
115
152
|
class << self
|
153
|
+
|
116
154
|
##
|
117
155
|
# Loads a class to perfom the command, then returns an instance
|
118
156
|
# of the class.
|
119
157
|
#
|
120
158
|
# :call-seq:
|
121
|
-
# "import" ->
|
122
|
-
# "list" ->
|
123
|
-
# "show" ->
|
124
|
-
|
159
|
+
# load("import") -> Rbnotes::Commnads::Import
|
160
|
+
# load("list") -> Rbnotes::Commands::List
|
161
|
+
# load("show") -> Rbnotes::Commands::Show
|
162
|
+
|
125
163
|
def load(cmd_name)
|
126
164
|
cmd_name ||= DEFAULT_CMD_NAME
|
127
165
|
klass_name = cmd_name.capitalize
|
data/lib/rbnotes/commands/add.rb
CHANGED
@@ -1,9 +1,20 @@
|
|
1
1
|
module Rbnotes::Commands
|
2
|
+
|
2
3
|
##
|
3
|
-
# Adds a new note to the repository.
|
4
|
-
# at the execution time, then it is attached to the
|
5
|
-
# timestamp has already existed in the repository, the
|
6
|
-
# fails.
|
4
|
+
# Adds a new note to the repository. If no options, a new timestamp
|
5
|
+
# is generated at the execution time, then it is attached to the
|
6
|
+
# note. If the timestamp has already existed in the repository, the
|
7
|
+
# command fails.
|
8
|
+
#
|
9
|
+
# Accepts an option with `-t STAMP_PATTERN` (or `--timestamp`), a
|
10
|
+
# timestamp is generated according to `STAMP_PATTERN`.
|
11
|
+
#
|
12
|
+
# STAMP_PATTERN could be one of followings:
|
13
|
+
#
|
14
|
+
# "20201104172230_078" : full qualified timestamp string
|
15
|
+
# "20201104172230" : full qualified timestamp string (no suffix)
|
16
|
+
# "202011041722" : year, date and time (omit second part)
|
17
|
+
# "11041722" : date and time (omit year and second part)
|
7
18
|
#
|
8
19
|
# This command starts the external editor program to prepare text to
|
9
20
|
# store. The editor program will be searched in the following order:
|
@@ -14,23 +25,38 @@ module Rbnotes::Commands
|
|
14
25
|
# 4. "vi"
|
15
26
|
#
|
16
27
|
# If none of the above editor is available, the command fails.
|
17
|
-
|
28
|
+
|
18
29
|
class Add < Command
|
19
30
|
include ::Rbnotes::Utils
|
20
31
|
|
21
32
|
def execute(args, conf)
|
22
|
-
|
33
|
+
@opts = {}
|
34
|
+
while args.size > 0
|
35
|
+
arg = args.shift
|
36
|
+
case arg
|
37
|
+
when "-t", "--timestamp"
|
38
|
+
stamp_str = args.shift
|
39
|
+
raise ArgumentError, "missing timestamp: %s" % args.unshift(arg) if stamp_str.nil?
|
40
|
+
stamp_str = complement_timestamp_pattern(stamp_str)
|
41
|
+
@opts[:timestamp] = Textrepo::Timestamp.parse_s(stamp_str)
|
42
|
+
else
|
43
|
+
args.unshift(arg)
|
44
|
+
break
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
stamp = @opts[:timestamp] || Textrepo::Timestamp.new(Time.now)
|
23
49
|
|
24
50
|
candidates = [conf[:editor], ENV["EDITOR"], "nano", "vi"].compact
|
25
51
|
editor = find_program(candidates)
|
26
52
|
raise Rbnotes::NoEditorError, candidates if editor.nil?
|
27
53
|
|
28
|
-
tmpfile = run_with_tmpfile(editor,
|
54
|
+
tmpfile = run_with_tmpfile(editor, stamp.to_s)
|
29
55
|
text = File.readlines(tmpfile)
|
30
56
|
|
31
57
|
repo = Textrepo.init(conf)
|
32
58
|
begin
|
33
|
-
repo.create(
|
59
|
+
repo.create(stamp, text)
|
34
60
|
rescue Textrepo::DuplicateTimestampError => e
|
35
61
|
puts e.message
|
36
62
|
puts "Just wait a second, then retry."
|
@@ -39,11 +65,28 @@ module Rbnotes::Commands
|
|
39
65
|
rescue StandardError => e
|
40
66
|
puts e.message
|
41
67
|
else
|
42
|
-
puts "Add a note [%s]" %
|
68
|
+
puts "Add a note [%s]" % stamp.to_s
|
43
69
|
ensure
|
44
70
|
# Don't forget to remove the temporary file.
|
45
71
|
File.delete(tmpfile)
|
46
72
|
end
|
47
73
|
end
|
74
|
+
|
75
|
+
# :stopdoc:
|
76
|
+
private
|
77
|
+
def complement_timestamp_pattern(pattern)
|
78
|
+
stamp_str = nil
|
79
|
+
case pattern.to_s.size
|
80
|
+
when "yyyymoddhhmiss_lll".size, "yyyymoddhhmiss".size
|
81
|
+
stamp_str = pattern.dup
|
82
|
+
when "yyyymoddhhmi".size # omit sec part
|
83
|
+
stamp_str = "#{pattern}00"
|
84
|
+
when "moddhhmi".size # omit year and sec part
|
85
|
+
stamp_str = "#{Time.now.year}#{pattern}00"
|
86
|
+
else
|
87
|
+
raise Textrepo::InvalidTimestampStringError, pattern
|
88
|
+
end
|
89
|
+
stamp_str
|
90
|
+
end
|
48
91
|
end
|
49
92
|
end
|
@@ -14,21 +14,17 @@
|
|
14
14
|
module Rbnotes
|
15
15
|
class Commands::Delete < Commands::Command
|
16
16
|
def execute(args, conf)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
puts e.message
|
27
|
-
else
|
28
|
-
puts "Delete [%s]" % stamp.to_s
|
29
|
-
end
|
17
|
+
stamp = Rbnotes::Utils.read_timestamp(args)
|
18
|
+
|
19
|
+
repo = Textrepo.init(conf)
|
20
|
+
begin
|
21
|
+
repo.delete(stamp)
|
22
|
+
rescue Textrepo::MissingTimestampError => e
|
23
|
+
puts e.message
|
24
|
+
rescue StandardError => e
|
25
|
+
puts e.message
|
30
26
|
else
|
31
|
-
|
27
|
+
puts "Delete [%s]" % stamp.to_s
|
32
28
|
end
|
33
29
|
end
|
34
30
|
end
|
@@ -29,7 +29,7 @@ module Rbnotes
|
|
29
29
|
puts "The note [%s] in the repository exactly matches" \
|
30
30
|
" the specified file." % stamp
|
31
31
|
puts "It seems there is no need to import the file [%s]." % file
|
32
|
-
|
32
|
+
break
|
33
33
|
else
|
34
34
|
puts "The text in the repository does not match the" \
|
35
35
|
" specified file."
|
@@ -40,8 +40,8 @@ module Rbnotes
|
|
40
40
|
end
|
41
41
|
rescue Textrepo::EmptyTextError => _
|
42
42
|
puts "... aborted."
|
43
|
-
puts "The specified file is
|
44
|
-
|
43
|
+
puts "The specified file is empty."
|
44
|
+
break
|
45
45
|
end
|
46
46
|
end
|
47
47
|
if count > 999
|
@@ -24,6 +24,9 @@ module Rbnotes
|
|
24
24
|
# "20201027": specifies year and date
|
25
25
|
# - all Timestamps those have the same year and date would match
|
26
26
|
#
|
27
|
+
# "202011": specifies year and month
|
28
|
+
# - all Timestamps those have the same year and month would match
|
29
|
+
#
|
27
30
|
# "2020": specifies year only
|
28
31
|
# - all Timestamps those have the same year would match
|
29
32
|
#
|
@@ -54,11 +57,11 @@ module Rbnotes
|
|
54
57
|
# Makes a headline with the timestamp and subject of the notes, it
|
55
58
|
# looks like as follows:
|
56
59
|
#
|
57
|
-
# |<------------------ console column size
|
58
|
-
# +-- timestamp ---+
|
59
|
-
# | | |
|
60
|
-
# 20101010001000_123:
|
61
|
-
# 20100909090909_999:
|
60
|
+
# |<------------------ console column size ------------------->|
|
61
|
+
# +-- timestamp ---+ +- subject (the 1st line of each note) -+
|
62
|
+
# | | | |
|
63
|
+
# 20101010001000_123: I love Macintosh. [EOL]
|
64
|
+
# 20100909090909_999: This is very very long long loooong subje[EOL]
|
62
65
|
# ++
|
63
66
|
# ^--- delimiter (2 characters)
|
64
67
|
#
|
@@ -69,9 +72,7 @@ module Rbnotes
|
|
69
72
|
delimiter = ": "
|
70
73
|
subject_width = column - TIMESTAMP_STR_MAX_WIDTH - delimiter.size - 1
|
71
74
|
|
72
|
-
subject = @repo.read(timestamp)[0]
|
73
|
-
prefix = '# '
|
74
|
-
subject = prefix + subject.lstrip if subject[0, 2] != prefix
|
75
|
+
subject = remove_heading_markup(@repo.read(timestamp)[0])
|
75
76
|
|
76
77
|
ts_part = "#{timestamp.to_s} "[0..(TIMESTAMP_STR_MAX_WIDTH - 1)]
|
77
78
|
sj_part = truncate_str(subject, subject_width)
|
@@ -90,6 +91,10 @@ module Rbnotes
|
|
90
91
|
result
|
91
92
|
end
|
92
93
|
|
94
|
+
def remove_heading_markup(str)
|
95
|
+
str.sub(/^#+ +/, '')
|
96
|
+
end
|
97
|
+
|
93
98
|
# :startdoc:
|
94
99
|
end
|
95
100
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rbnotes
|
2
|
+
|
3
|
+
##
|
4
|
+
# Searches a given pattern in notes those have timestamps match a
|
5
|
+
# given timestamp pattern. The first argument is a pattern to search.
|
6
|
+
# It is a String object represents a portion of text or it may a
|
7
|
+
# String represents a regular expression. The second argument is
|
8
|
+
# optional and it is a timestamp pattern to specify the search target.
|
9
|
+
#
|
10
|
+
# A pattern for search is mandatory. If no pattern, raises
|
11
|
+
# Rbnotes::MissingArgumentError.
|
12
|
+
#
|
13
|
+
# Example of PATTERN for search:
|
14
|
+
#
|
15
|
+
# "rbnotes" (a word)
|
16
|
+
# "macOS Big Sur" (a few words)
|
17
|
+
# "2[0-9]{3}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])" (a regular expression)
|
18
|
+
#
|
19
|
+
# A timestamp pattern is optional. If no timestamp pattern, all notes
|
20
|
+
# in the repository would be target of search.
|
21
|
+
#
|
22
|
+
# See the document of `Rbnotes::Commands::List#execute` to know about
|
23
|
+
# a timestamp pattern.
|
24
|
+
|
25
|
+
class Commands::Search < Commands::Command
|
26
|
+
def execute(args, conf)
|
27
|
+
pattern = args.shift
|
28
|
+
raise MissingArgumentError, args if pattern.nil?
|
29
|
+
|
30
|
+
timestamp_pattern = args.shift # `nil` is acceptable
|
31
|
+
|
32
|
+
repo = Textrepo.init(conf)
|
33
|
+
begin
|
34
|
+
result = repo.search(pattern, timestamp_pattern)
|
35
|
+
rescue Textrepo::InvalidSearchResultError => e
|
36
|
+
puts e.message
|
37
|
+
else
|
38
|
+
result.each { |stamp, num, match|
|
39
|
+
puts "#{stamp}:#{num}:#{match}"
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,24 +1,20 @@
|
|
1
1
|
module Rbnotes
|
2
2
|
class Commands::Show < Commands::Command
|
3
3
|
def execute(args, conf)
|
4
|
-
|
5
|
-
unless stamp_str.nil?
|
6
|
-
repo = Textrepo.init(conf)
|
7
|
-
stamp = Textrepo::Timestamp.parse_s(stamp_str)
|
8
|
-
content = repo.read(stamp)
|
4
|
+
stamp = Rbnotes::Utils.read_timestamp(args)
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
6
|
+
repo = Textrepo.init(conf)
|
7
|
+
content = repo.read(stamp)
|
8
|
+
|
9
|
+
pager = conf[:pager]
|
10
|
+
unless pager.nil?
|
11
|
+
require 'open3'
|
12
|
+
Open3.pipeline_w(pager) { |stdin|
|
13
|
+
stdin.puts content
|
14
|
+
stdin.close
|
15
|
+
}
|
20
16
|
else
|
21
|
-
|
17
|
+
puts content
|
22
18
|
end
|
23
19
|
end
|
24
20
|
end
|
@@ -5,6 +5,9 @@ module Rbnotes::Commands
|
|
5
5
|
# The timestamp associated with the note will be updated to new one,
|
6
6
|
# which is generated while the command exection.
|
7
7
|
#
|
8
|
+
# When "-k" (or "--keep") option is specified, the timestamp will
|
9
|
+
# remain unchanged.
|
10
|
+
#
|
8
11
|
# A timestamp string must be specified as the only argument. It
|
9
12
|
# must exactly match to the one of the target note in the
|
10
13
|
# repository. When the given timestamp was not found, the command
|
@@ -19,7 +22,7 @@ module Rbnotes::Commands
|
|
19
22
|
# as add command.
|
20
23
|
#
|
21
24
|
# If none of editors is available, the command fails.
|
22
|
-
|
25
|
+
|
23
26
|
class Update < Command
|
24
27
|
include ::Rbnotes::Utils
|
25
28
|
|
@@ -30,19 +33,22 @@ module Rbnotes::Commands
|
|
30
33
|
#
|
31
34
|
# :call-seq:
|
32
35
|
# "20201020112233" -> "20201021123400"
|
33
|
-
#
|
34
|
-
def execute(args, conf)
|
35
|
-
raise Rbnotes::MissingArgumentError, args if args.size < 1
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
def execute(args, conf)
|
38
|
+
@opts = {}
|
39
|
+
while args.size > 0
|
40
|
+
arg = args.shift
|
41
|
+
case arg
|
42
|
+
when "-k", "--keep"
|
43
|
+
@opts[:keep_timestamp] = true
|
44
|
+
else
|
45
|
+
args.unshift(arg)
|
46
|
+
break
|
47
|
+
end
|
42
48
|
end
|
43
49
|
|
50
|
+
target_stamp = Rbnotes::Utils.read_timestamp(args)
|
44
51
|
editor = find_editor(conf[:editor])
|
45
|
-
|
46
52
|
repo = Textrepo.init(conf)
|
47
53
|
|
48
54
|
text = nil
|
@@ -53,16 +59,21 @@ module Rbnotes::Commands
|
|
53
59
|
end
|
54
60
|
|
55
61
|
tmpfile = run_with_tmpfile(editor, target_stamp.to_s, text)
|
56
|
-
text = File.readlines(tmpfile)
|
62
|
+
text = File.readlines(tmpfile, :chomp => true)
|
57
63
|
|
58
64
|
unless text.empty?
|
65
|
+
keep = @opts[:keep_timestamp] || false
|
59
66
|
newstamp = nil
|
60
67
|
begin
|
61
|
-
newstamp = repo.update(target_stamp, text)
|
68
|
+
newstamp = repo.update(target_stamp, text, keep)
|
62
69
|
rescue StandardError => e
|
63
70
|
puts e.message
|
64
71
|
else
|
65
|
-
|
72
|
+
if keep
|
73
|
+
puts "Update the note content, the timestamp unchanged [%s]" % newstamp
|
74
|
+
else
|
75
|
+
puts "Update the note [%s -> %s]" % [target_stamp, newstamp] unless target_stamp == newstamp
|
76
|
+
end
|
66
77
|
ensure
|
67
78
|
# Don't forget to remove the temporary file.
|
68
79
|
File.delete(tmpfile)
|
data/lib/rbnotes/error.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
module Rbnotes
|
2
2
|
##
|
3
3
|
# A base class for each error class of rbnotes.
|
4
|
-
|
4
|
+
|
5
5
|
class Error < StandardError; end
|
6
6
|
|
7
7
|
# :stopdoc:
|
8
|
+
|
8
9
|
module ErrMsg
|
9
10
|
MISSING_ARGUMENT = "missing argument: %s"
|
10
11
|
MISSING_TIMESTAMP = "missing timestamp: %s"
|
@@ -16,7 +17,7 @@ module Rbnotes
|
|
16
17
|
|
17
18
|
##
|
18
19
|
# An error raised if an essential argument was missing.
|
19
|
-
|
20
|
+
|
20
21
|
class MissingArgumentError < Error
|
21
22
|
def initialize(args)
|
22
23
|
super(ErrMsg::MISSING_ARGUMENT % args.to_s)
|
@@ -26,7 +27,7 @@ module Rbnotes
|
|
26
27
|
##
|
27
28
|
# An error raised if a given timestamp was not found in the
|
28
29
|
# repository.
|
29
|
-
|
30
|
+
|
30
31
|
class MissingTimestampError < Error
|
31
32
|
def initialize(timestamp)
|
32
33
|
super(ErrMsg::MISSING_TIMESTAMP % timestamp)
|
@@ -36,7 +37,7 @@ module Rbnotes
|
|
36
37
|
##
|
37
38
|
# An error raised if no external editor is available to edit a note,
|
38
39
|
# even "nano" or "vi".
|
39
|
-
|
40
|
+
|
40
41
|
class NoEditorError < Error
|
41
42
|
def initialize(names)
|
42
43
|
super(ErrMsg::NO_EDITOR % names.to_s)
|
@@ -46,6 +47,7 @@ module Rbnotes
|
|
46
47
|
##
|
47
48
|
# An error raised when a external program such an editor was aborted
|
48
49
|
# during its execution.
|
50
|
+
|
49
51
|
class ProgramAbortError < Error
|
50
52
|
def initialize(cmdline)
|
51
53
|
super(ErrMsg::PROGRAM_ABORT % cmdline.join(" "))
|
data/lib/rbnotes/utils.rb
CHANGED
@@ -86,6 +86,41 @@ module Rbnotes
|
|
86
86
|
end
|
87
87
|
module_function :run_with_tmpfile
|
88
88
|
|
89
|
+
##
|
90
|
+
# Generates a Textrepo::Timestamp object from a String which comes
|
91
|
+
# from the command line arguments. When no argument is given,
|
92
|
+
# then reads from STDIN.
|
93
|
+
#
|
94
|
+
# :call-seq:
|
95
|
+
# read_timestamp(args) -> String
|
96
|
+
|
97
|
+
def read_timestamp(args)
|
98
|
+
str = args.shift || read_arg($stdin)
|
99
|
+
Textrepo::Timestamp.parse_s(str)
|
100
|
+
end
|
101
|
+
module_function :read_timestamp
|
102
|
+
|
103
|
+
##
|
104
|
+
# Reads an argument from the IO object. Typically, it is intended
|
105
|
+
# to be used with STDIN.
|
106
|
+
#
|
107
|
+
# :call-seq:
|
108
|
+
# read_arg(IO) -> String
|
109
|
+
|
110
|
+
def read_arg(io)
|
111
|
+
# assumes the reading line looks like:
|
112
|
+
#
|
113
|
+
# foo bar baz ...
|
114
|
+
#
|
115
|
+
# then, only the first string is interested
|
116
|
+
begin
|
117
|
+
io.gets.split(":")[0].rstrip
|
118
|
+
rescue NoMethodError => _
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
module_function :read_arg
|
123
|
+
|
89
124
|
# :stopdoc:
|
90
125
|
|
91
126
|
private
|
data/lib/rbnotes/version.rb
CHANGED
data/rbnotes.gemspec
CHANGED
@@ -25,6 +25,6 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
26
|
spec.require_paths = ["lib"]
|
27
27
|
|
28
|
-
spec.add_dependency "textrepo", "~> 0.4"
|
28
|
+
spec.add_dependency "textrepo", "~> 0.5.4"
|
29
29
|
spec.add_dependency "unicode-display_width", "~> 1.7"
|
30
30
|
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
|
+
version: 0.4.2
|
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
|
+
date: 2020-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: textrepo
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.5.4
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.5.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: unicode-display_width
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -56,6 +56,9 @@ files:
|
|
56
56
|
- Rakefile
|
57
57
|
- bin/console
|
58
58
|
- bin/setup
|
59
|
+
- conf/config.yml
|
60
|
+
- conf/config_deve.yml
|
61
|
+
- conf/config_test.yml
|
59
62
|
- exe/rbnotes
|
60
63
|
- lib/rbnotes.rb
|
61
64
|
- lib/rbnotes/commands.rb
|
@@ -63,6 +66,7 @@ files:
|
|
63
66
|
- lib/rbnotes/commands/delete.rb
|
64
67
|
- lib/rbnotes/commands/import.rb
|
65
68
|
- lib/rbnotes/commands/list.rb
|
69
|
+
- lib/rbnotes/commands/search.rb
|
66
70
|
- lib/rbnotes/commands/show.rb
|
67
71
|
- lib/rbnotes/commands/update.rb
|
68
72
|
- lib/rbnotes/conf.rb
|