icalPal 3.1.0 → 3.2.0
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/README.md +19 -19
- data/bin/icalPal +90 -56
- data/icalPal.gemspec +9 -9
- data/lib/EventKit.rb +24 -18
- data/lib/ToICalPal.rb +28 -26
- data/lib/calendar.rb +1 -2
- data/lib/defaults.rb +8 -8
- data/lib/event.rb +54 -53
- data/lib/icalPal.rb +35 -23
- data/lib/options.rb +45 -36
- data/lib/rdt.rb +7 -5
- data/lib/reminder.rb +22 -22
- data/lib/store.rb +1 -1
- data/lib/version.rb +2 -2
- metadata +9 -13
- data/bin/icalpal +0 -327
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc2cfd940c507b46cafd9c893beb25c47c82850ba7c1e02aaffcb6bc76eda208
|
4
|
+
data.tar.gz: e28610d838feaf2c05b41badd96bad57185501529cd73c7f0b7f5889290a59ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2cdd430f7a66418a87cccf5c037f24d0e4fe39e594db5b951ea004c13eadafd15f398bfc0beac8b5fe4c987664a1ee56d9e113354a438e06cf2fe9c3d88c3304
|
7
|
+
data.tar.gz: ee2276cb96b75d93727bc9dcf4595cd8bef64ccd0893e4e3647f0c5a74d559cddf86434bf0a392176e3a05f7339d8b9651347e5c6098c45467aaf8c163349b62
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
[](https://badge.fury.io/rb/icalPal)
|
2
2
|
|
3
|
-
#
|
3
|
+
# icalPal
|
4
4
|
|
5
5
|
## Description
|
6
6
|
|
7
|
-
|
7
|
+
icalPal is a command-line tool to query a macOS Calendar and Reminders
|
8
8
|
databases for accounts, calendars, events, and tasks. It can be run
|
9
9
|
on any system with [Ruby](https://www.ruby-lang.org/) and access to a
|
10
10
|
Calendar or Reminders database.
|
@@ -14,27 +14,27 @@ Calendar or Reminders database.
|
|
14
14
|
As a system-wide Ruby gem:
|
15
15
|
|
16
16
|
```
|
17
|
-
gem install
|
17
|
+
gem install icalPal
|
18
18
|
```
|
19
19
|
|
20
20
|
or in your home diretory:
|
21
21
|
|
22
22
|
```
|
23
|
-
gem install --user-install
|
23
|
+
gem install --user-install icalPal
|
24
24
|
```
|
25
25
|
|
26
26
|
As a Homebrew formula:
|
27
27
|
|
28
28
|
```
|
29
|
-
brew tap ajrosen/
|
30
|
-
brew install
|
29
|
+
brew tap ajrosen/icalPal
|
30
|
+
brew install icalPal
|
31
31
|
```
|
32
32
|
|
33
33
|
## Features
|
34
34
|
|
35
35
|
### Compatability with [icalBuddy](https://github.com/ali-rantakari/icalBuddy)
|
36
36
|
|
37
|
-
|
37
|
+
icalPal tries to be compatible with icalBuddy for command-line options
|
38
38
|
and for output. There are a some important differences to be aware
|
39
39
|
of.
|
40
40
|
|
@@ -48,17 +48,17 @@ of.
|
|
48
48
|
|
49
49
|
### Additional commands
|
50
50
|
|
51
|
-
```
|
51
|
+
```icalPal accounts```
|
52
52
|
|
53
|
-
Shows a list of enabled Calendar accounts. Internally they are known as *Stores*; you can run ```
|
53
|
+
Shows a list of enabled Calendar accounts. Internally they are known as *Stores*; you can run ```icalPal stores``` instead.
|
54
54
|
|
55
|
-
```
|
55
|
+
```icalPal datedTasks```
|
56
56
|
|
57
57
|
Shows only reminders that have a due date.
|
58
58
|
|
59
59
|
### Additional options
|
60
60
|
|
61
|
-
* Options can be abbreviated, so long as they are unique. Eg., ```
|
61
|
+
* Options can be abbreviated, so long as they are unique. Eg., ```icalPal -c ev --da 3``` is the same as ```icalPal -c events --days 3```.
|
62
62
|
* The ```-c``` part is optional, but you cannot abbreviate the command if you leave it off.
|
63
63
|
* Use ```-o``` to print the output in different formats. CSV or JSON are intertesting choices.
|
64
64
|
* Copy your Calendar database file and use ```--db``` on it.
|
@@ -70,11 +70,11 @@ Shows only reminders that have a due date.
|
|
70
70
|
* ```--color``` uses a wider color palette. Calendar colors are what you have chosen in the Calendar app. Not supported in all terminals, but looks great in [iTerm2](https://iterm2.com/).
|
71
71
|
* ```--match``` lets you filter the results of any command to items where a *FIELD* matches a regular expression. Eg., ```--match notes=zoom.us``` to show only Zoom meeetings
|
72
72
|
|
73
|
-
Because
|
73
|
+
Because icalPal is written in Ruby, and not a native Mac application, you can run it just about anywhere. It's been tested with the version of Ruby (2.6.10) included with macOS.
|
74
74
|
|
75
75
|
## Usage
|
76
76
|
|
77
|
-
|
77
|
+
icalPal: Usage: icalPal [options] [-c] COMMAND
|
78
78
|
|
79
79
|
COMMAND must be one of the following:
|
80
80
|
```
|
@@ -205,14 +205,14 @@ Environment variables:
|
|
205
205
|
|
206
206
|
## Output formats
|
207
207
|
|
208
|
-
|
208
|
+
icalPal supports several output formats. The **default** format tries
|
209
209
|
to mimic icalBuddy as much as possible.
|
210
210
|
|
211
211
|
CSV, Hash, JSON, XML, and YAML print all fields for all items in their
|
212
212
|
respective formats. From that you can analyze the results any way you
|
213
213
|
like.
|
214
214
|
|
215
|
-
[Remind](https://dianne.skoll.ca/projects/remind/) format uses a minimal implementation built in
|
215
|
+
[Remind](https://dianne.skoll.ca/projects/remind/) format uses a minimal implementation built in icalPal.
|
216
216
|
|
217
217
|
Other formats such as ANSI, HTML, Markdown, RDoc, and TOC, use Ruby's
|
218
218
|
[RDoc::Markup](https://ruby-doc.org/stdlib-2.6.10/libdoc/rdoc/rdoc/RDoc/Markup.html)
|
@@ -259,10 +259,10 @@ Lawton](https://github.com/jimlawton) that it even compiles anymore.
|
|
259
259
|
|
260
260
|
Instead of trying to understand and extend the existing code, I chose
|
261
261
|
to start anew using my language of choice: Ruby. Using Ruby meant
|
262
|
-
there is *much* less code; about 1,
|
263
|
-
|
262
|
+
there is *much* less code; about 1,800 lines vs. 7,000. It also means
|
263
|
+
icalPal is multi-platform.
|
264
264
|
|
265
265
|
I won't pretend to understand **why** you would want to run this on
|
266
|
-
Linux or Windows. But since
|
266
|
+
Linux or Windows. But since icalPal is written in Ruby and gets its
|
267
267
|
data directly from the Calendar and Reminders database files instead
|
268
268
|
of an API, you *can*.
|
data/bin/icalPal
CHANGED
@@ -27,10 +27,15 @@ end
|
|
27
27
|
|
28
28
|
# All kids love log!
|
29
29
|
$log = Logger.new(STDERR, { level: $defaults[:common][:debug] })
|
30
|
-
$log.formatter = proc do |s, t,
|
30
|
+
$log.formatter = proc do |s, t, _p, m| # Severity, time, progname, msg
|
31
31
|
($log.level.positive?)? "#{s}: #{m}\n" :
|
32
|
-
"[%-
|
33
|
-
|
32
|
+
format("[%-5<sev>s] %<time>s [%<caller>s] - %<message>s\n",
|
33
|
+
{
|
34
|
+
sev: s,
|
35
|
+
time: t.strftime('%H:%M:%S.%L'),
|
36
|
+
caller: caller(4, 1)[0].split('/')[-1],
|
37
|
+
message: m
|
38
|
+
})
|
34
39
|
end
|
35
40
|
|
36
41
|
$opts = ICalPal::Options.new.parse_options
|
@@ -53,7 +58,7 @@ $log.info("Options: #{$opts}")
|
|
53
58
|
def add(item)
|
54
59
|
$log.info("Adding #{item.inspect} #{item['UUID']} (#{item['title']})") if item['UUID']
|
55
60
|
|
56
|
-
item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if
|
61
|
+
item['sday'] = ICalPal::RDT.new(*item['sdate'].to_a[0..2]) if ICalPal::Event === item && item['sdate']
|
57
62
|
$items.push(item)
|
58
63
|
end
|
59
64
|
|
@@ -62,24 +67,45 @@ end
|
|
62
67
|
# Load the data
|
63
68
|
|
64
69
|
# What are we getting?
|
65
|
-
klass = ICalPal
|
70
|
+
klass = ICalPal.call($opts[:cmd])
|
71
|
+
success = false
|
66
72
|
|
67
73
|
# Get it
|
68
74
|
$opts[:db].each do |db|
|
69
|
-
|
75
|
+
$log.debug("Trying #{db}")
|
70
76
|
|
71
|
-
|
77
|
+
if klass == ICalPal::Reminder
|
78
|
+
begin
|
72
79
|
# Load all .sqlite files
|
73
80
|
$log.debug("Loading *.sqlite in #{db}")
|
74
|
-
Dir.glob("#{db}/*.sqlite").each
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
81
|
+
Dir.glob("#{db}/*.sqlite").each do |d|
|
82
|
+
$rows += ICalPal.load_data(d, klass::QUERY)
|
83
|
+
success = true
|
84
|
+
|
85
|
+
rescue SQLite3::CantOpenException
|
86
|
+
# Non-fatal exception, try the next one
|
79
87
|
end
|
88
|
+
end
|
89
|
+
else
|
90
|
+
# Load database
|
91
|
+
begin
|
92
|
+
$rows += ICalPal.load_data(db, klass::QUERY)
|
93
|
+
success = true
|
94
|
+
|
95
|
+
rescue SQLite3::CantOpenException
|
96
|
+
# Non-fatal exception, try the next one
|
97
|
+
end
|
80
98
|
end
|
81
99
|
end
|
82
100
|
|
101
|
+
# Make sure we opened at least one database
|
102
|
+
unless success
|
103
|
+
$log.fatal('Could not open database')
|
104
|
+
$opts[:db].each { |db| $log.fatal("Tried #{db}") }
|
105
|
+
|
106
|
+
abort
|
107
|
+
end
|
108
|
+
|
83
109
|
$log.info("Loaded #{$rows.count} #{klass} rows")
|
84
110
|
|
85
111
|
|
@@ -91,25 +117,25 @@ $rows.each_with_index do |row, i|
|
|
91
117
|
$log.debug("Row #{i}: #{row['ROWID']}:#{row['UUID']} - #{row['account']}/#{row['calendar']}/#{row['title']}")
|
92
118
|
|
93
119
|
# --es/--is
|
94
|
-
if $opts[:es].any? row['account']
|
95
|
-
$log.debug(
|
120
|
+
if $opts[:es].any? row['account']
|
121
|
+
$log.debug(':es')
|
96
122
|
next
|
97
123
|
end
|
98
124
|
|
99
|
-
unless $opts[:is].empty?
|
100
|
-
$log.debug(
|
125
|
+
unless $opts[:is].empty? || ($opts[:is].any? row['account'])
|
126
|
+
$log.debug(':is')
|
101
127
|
next
|
102
128
|
end
|
103
129
|
|
104
130
|
# --ec/--ic
|
105
|
-
unless klass == ICalPal::Store
|
106
|
-
if $opts[:ec].any? row['calendar']
|
107
|
-
$log.debug(
|
131
|
+
unless klass == ICalPal::Store || !row['calendar']
|
132
|
+
if $opts[:ec].any? row['calendar']
|
133
|
+
$log.debug(':ec')
|
108
134
|
next
|
109
135
|
end
|
110
136
|
|
111
|
-
unless $opts[:ic].empty?
|
112
|
-
$log.debug(
|
137
|
+
unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
|
138
|
+
$log.debug(':ic')
|
113
139
|
next
|
114
140
|
end
|
115
141
|
end
|
@@ -118,24 +144,24 @@ $rows.each_with_index do |row, i|
|
|
118
144
|
item = klass.new(row)
|
119
145
|
|
120
146
|
# --et/--it
|
121
|
-
if $opts[:et].any? item['type']
|
122
|
-
$log.debug(
|
147
|
+
if $opts[:et].any? item['type']
|
148
|
+
$log.debug(':et')
|
123
149
|
next
|
124
150
|
end
|
125
151
|
|
126
|
-
unless $opts[:it].empty?
|
127
|
-
$log.debug(
|
152
|
+
unless $opts[:it].empty? || ($opts[:it].any? item['type'])
|
153
|
+
$log.debug(':it')
|
128
154
|
next
|
129
155
|
end
|
130
156
|
|
131
157
|
# --el/--il
|
132
|
-
if $opts[:el].any? item['list_name']
|
133
|
-
$log.debug(
|
158
|
+
if $opts[:el].any? item['list_name']
|
159
|
+
$log.debug(':el')
|
134
160
|
next
|
135
161
|
end
|
136
162
|
|
137
|
-
unless $opts[:il].empty?
|
138
|
-
$log.debug(
|
163
|
+
unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
|
164
|
+
$log.debug(':il')
|
139
165
|
next
|
140
166
|
end
|
141
167
|
|
@@ -144,8 +170,8 @@ $rows.each_with_index do |row, i|
|
|
144
170
|
r = $opts[:match].split('=')
|
145
171
|
|
146
172
|
if item[r[0]].to_s.respond_to?(:match)
|
147
|
-
unless item[r[0]].to_s.match(Regexp.new(r[1].to_s, Regexp::IGNORECASE))
|
148
|
-
$log.debug(
|
173
|
+
unless item[r[0]].to_s.match(Regexp.new(r[1].to_s, Regexp::IGNORECASE))
|
174
|
+
$log.debug(':match')
|
149
175
|
next
|
150
176
|
end
|
151
177
|
end
|
@@ -153,18 +179,18 @@ $rows.each_with_index do |row, i|
|
|
153
179
|
|
154
180
|
if ICalPal::Event === item
|
155
181
|
# Check for all-day and cancelled events
|
156
|
-
if $opts[:ea] && item['all_day'].positive?
|
157
|
-
$log.debug(
|
182
|
+
if $opts[:ea] && item['all_day'].positive?
|
183
|
+
$log.debug(':ea')
|
158
184
|
next
|
159
185
|
end
|
160
186
|
|
161
|
-
if $opts[:ia] && !item['all_day'].positive?
|
162
|
-
$log.debug(
|
187
|
+
if $opts[:ia] && !item['all_day'].positive?
|
188
|
+
$log.debug(':ia')
|
163
189
|
next
|
164
190
|
end
|
165
191
|
|
166
|
-
if item['status'] == :canceled
|
167
|
-
$log.debug(
|
192
|
+
if item['status'] == :canceled
|
193
|
+
$log.debug(':canceled')
|
168
194
|
next
|
169
195
|
end
|
170
196
|
|
@@ -173,14 +199,14 @@ $rows.each_with_index do |row, i|
|
|
173
199
|
item.non_recurring.each { |i| add(i) }
|
174
200
|
else
|
175
201
|
# Check for dated reminders
|
176
|
-
if ICalPal::Reminder === item
|
177
|
-
if $opts[:dated] == 1
|
178
|
-
$log.debug(
|
202
|
+
if ICalPal::Reminder === item
|
203
|
+
if $opts[:dated] == 1 && item['due_date'].positive?
|
204
|
+
$log.debug(':undated')
|
179
205
|
next
|
180
206
|
end
|
181
207
|
|
182
|
-
if $opts[:dated] == 2
|
183
|
-
$log.debug(
|
208
|
+
if $opts[:dated] == 2 && item['due_date'].zero?
|
209
|
+
$log.debug(':dated')
|
184
210
|
next
|
185
211
|
end
|
186
212
|
end
|
@@ -216,7 +242,7 @@ $log.debug("#{$items.count} items remain")
|
|
216
242
|
mu = case $opts[:output]
|
217
243
|
when 'ansi' then RDoc::Markup::ToAnsi.new
|
218
244
|
when 'default' then RDoc::Markup::ToICalPal.new($opts)
|
219
|
-
when 'html'
|
245
|
+
when 'html'
|
220
246
|
rdoc = RDoc::Options.new
|
221
247
|
rdoc.pipe = true
|
222
248
|
rdoc.output_decoration = false
|
@@ -236,7 +262,7 @@ unless mu
|
|
236
262
|
$log.debug("Output in #{$opts[:output]} format")
|
237
263
|
|
238
264
|
puts case $opts[:output]
|
239
|
-
when 'csv'
|
265
|
+
when 'csv'
|
240
266
|
# Get all headers
|
241
267
|
headers = []
|
242
268
|
items.each { |i| headers += i.keys }
|
@@ -249,15 +275,15 @@ unless mu
|
|
249
275
|
table
|
250
276
|
when 'hash' then items.map { |i| i.self }
|
251
277
|
when 'json' then items.map { |i| i.self }.to_json
|
252
|
-
when 'xml'
|
253
|
-
xml = items.map { |i| "<#{$opts[:cmd].chomp(
|
254
|
-
"<#{$opts[:cmd]}>\n#{xml.join(
|
278
|
+
when 'xml'
|
279
|
+
xml = items.map { |i| "<#{$opts[:cmd].chomp('s')}>#{i.to_xml}</#{$opts[:cmd].chomp('s')}>" }
|
280
|
+
"<#{$opts[:cmd]}>\n#{xml.join('')}</#{$opts[:cmd]}>"
|
255
281
|
when 'yaml' then items.map { |i| i.self }.to_yaml
|
256
282
|
when 'remind' then items.map { |i|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
283
|
+
"REM #{i['sdate'].strftime('%F AT %R')} " +
|
284
|
+
"DURATION #{((i['edate'] - i['sdate']).to_f * 1440).to_i} " +
|
285
|
+
"MSG #{i['title']}"
|
286
|
+
}.join("\n")
|
261
287
|
else abort "No formatter for #{$opts[:output]}"
|
262
288
|
end
|
263
289
|
|
@@ -302,20 +328,28 @@ items.each_with_index do |i, j|
|
|
302
328
|
|
303
329
|
next unless value
|
304
330
|
next if Array === value && !value[0]
|
305
|
-
next if String === value && value.
|
331
|
+
next if String === value && value.empty?
|
306
332
|
|
307
333
|
$log.debug("#{prop}: #{value}")
|
308
334
|
|
309
335
|
# Use Raw to save the property
|
310
336
|
props << RDoc::Markup::Raw.new(prop)
|
311
337
|
|
312
|
-
|
338
|
+
if k.positive?
|
339
|
+
props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
340
|
+
props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
|
341
|
+
else
|
313
342
|
# First property, value only
|
314
343
|
props << RDoc::Markup::Heading.new(2, value.to_s)
|
315
|
-
else
|
316
|
-
props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
317
|
-
props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless(i['placeholder'])
|
318
344
|
end
|
345
|
+
|
346
|
+
# unless k.positive?
|
347
|
+
# # First property, value only
|
348
|
+
# props << RDoc::Markup::Heading.new(2, value.to_s)
|
349
|
+
# else
|
350
|
+
# props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
351
|
+
# props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
|
352
|
+
# end
|
319
353
|
end
|
320
354
|
|
321
355
|
# Print it
|
data/icalPal.gemspec
CHANGED
@@ -1,26 +1,26 @@
|
|
1
|
-
|
1
|
+
require_relative 'lib/version'
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = ICalPal::NAME
|
5
5
|
s.version = ICalPal::VERSION
|
6
6
|
|
7
|
-
s.summary =
|
7
|
+
s.summary = 'Command-line tool to query the macOS Calendar'
|
8
8
|
s.description = <<-EOF
|
9
9
|
Inspired by icalBuddy and maintains close compatability. Includes
|
10
10
|
many additional features for querying, filtering, and formatting.
|
11
11
|
EOF
|
12
12
|
|
13
|
-
s.authors =
|
14
|
-
s.email =
|
13
|
+
s.authors = 'Andy Rosen'
|
14
|
+
s.email = 'ajr@corp.mlfs.org'
|
15
15
|
s.homepage = "https://github.com/ajrosen/#{s.name}"
|
16
|
-
s.licenses = [
|
16
|
+
s.licenses = [ 'GPL-3.0-or-later' ]
|
17
17
|
|
18
|
-
s.files = Dir[
|
18
|
+
s.files = Dir["#{s.name}.gemspec", 'bin/*', 'lib/*.rb']
|
19
19
|
s.executables = [ "#{s.name}" ]
|
20
|
-
s.extra_rdoc_files = [
|
20
|
+
s.extra_rdoc_files = [ 'README.md' ]
|
21
21
|
|
22
|
-
s.add_runtime_dependency
|
23
|
-
s.add_runtime_dependency
|
22
|
+
s.add_runtime_dependency 'nokogiri-plist', '~> 0.5.0'
|
23
|
+
s.add_runtime_dependency 'sqlite3', '~> 2'
|
24
24
|
|
25
25
|
s.bindir = 'bin'
|
26
26
|
s.required_ruby_version = '>= 2.6.0'
|
data/lib/EventKit.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# rubocop: disable Naming/ConstantName
|
2
|
+
|
1
3
|
# Constants from EventKit[https://developer.apple.com/documentation/eventkit/]
|
4
|
+
|
2
5
|
class EventKit
|
3
|
-
EKEntityType = [
|
4
|
-
|
5
|
-
|
6
|
-
]
|
6
|
+
EKEntityType = %w[
|
7
|
+
event
|
8
|
+
reminder
|
9
|
+
].freeze
|
7
10
|
|
8
11
|
EKEventAvailability = {
|
9
12
|
notSupported: -1,
|
@@ -11,28 +14,28 @@ class EventKit
|
|
11
14
|
free: 1,
|
12
15
|
tentative: 2,
|
13
16
|
unavailable: 3,
|
14
|
-
}
|
17
|
+
}.freeze
|
15
18
|
|
16
19
|
EKEventStatus = {
|
17
20
|
none: 0,
|
18
21
|
confirmed: 1,
|
19
22
|
tentative: 2,
|
20
23
|
canceled: 3,
|
21
|
-
}
|
24
|
+
}.freeze
|
22
25
|
|
23
|
-
EKRecurrenceFrequency = [
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
]
|
26
|
+
EKRecurrenceFrequency = %w[
|
27
|
+
daily
|
28
|
+
weekly
|
29
|
+
monthly
|
30
|
+
yearly
|
31
|
+
].freeze
|
29
32
|
|
30
33
|
EKReminderProperty = [
|
31
34
|
'none', # 0
|
32
35
|
'high', nil, nil, nil, # 1
|
33
36
|
'medium', nil, nil, nil, # 5
|
34
37
|
'low', # 9
|
35
|
-
]
|
38
|
+
].freeze
|
36
39
|
|
37
40
|
# EKSourceType (with color)
|
38
41
|
EKSourceType = [
|
@@ -43,10 +46,13 @@ class EventKit
|
|
43
46
|
{ name: 'Subscribed', color: '#FF0000' }, # Red
|
44
47
|
{ name: 'Birthdays', color: '#FF00FF' }, # Magenta
|
45
48
|
{ name: 'Reminders', color: '#066FF3' }, # Blue
|
46
|
-
]
|
49
|
+
].freeze
|
50
|
+
|
51
|
+
EKSpan = %w[
|
52
|
+
this
|
53
|
+
future
|
54
|
+
].freeze
|
47
55
|
|
48
|
-
EKSpan = [
|
49
|
-
'this',
|
50
|
-
'future',
|
51
|
-
]
|
52
56
|
end
|
57
|
+
|
58
|
+
# rubocop: enable Naming/ConstantName
|
data/lib/ToICalPal.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
##################################################
|
2
2
|
# Render an RDoc::Markup::Document, closely mimicking
|
3
3
|
# icalBuddy[https://github.com/ali-rantakari/icalBuddy]
|
4
|
-
|
5
4
|
class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
6
5
|
# Standard
|
7
6
|
# ANSI[https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.416-199303-I!!PDF-E&type=items]
|
@@ -26,25 +25,25 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
26
25
|
'pink': '38;2;255;45;85',
|
27
26
|
'purple': '38;2;204;115;225',
|
28
27
|
'rose': '38;2;217;166;159',
|
29
|
-
}
|
28
|
+
}.freeze
|
30
29
|
|
31
30
|
# Increased intensity
|
32
|
-
BOLD = '%c[1m'
|
31
|
+
BOLD = format('%c[1m', 27.chr)
|
33
32
|
|
34
33
|
# Default rendition
|
35
|
-
NORM = '%c[0m'
|
34
|
+
NORM = format('%c[0m', 27.chr)
|
36
35
|
|
37
36
|
# Properties for which we don't include labels
|
38
|
-
NO_LABEL = [
|
37
|
+
NO_LABEL = %w[ title datetime ].freeze
|
39
38
|
|
40
39
|
# Properties that are always colorized
|
41
|
-
COLOR_LABEL = [
|
40
|
+
COLOR_LABEL = %w[ title calendar ].freeze
|
42
41
|
|
43
42
|
# Default color for labels
|
44
|
-
LABEL_COLOR = [ 'cyan', '#00ffff' ]
|
43
|
+
LABEL_COLOR = [ 'cyan', '#00ffff' ].freeze
|
45
44
|
|
46
45
|
# Color for datetime value
|
47
|
-
DATE_COLOR = [ 'yellow', '#ffff00' ]
|
46
|
+
DATE_COLOR = [ 'yellow', '#ffff00' ].freeze
|
48
47
|
|
49
48
|
# @param opts [Hash] Used for conditional formatting
|
50
49
|
# @option opts [String] :bullet Bullet
|
@@ -56,6 +55,7 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
56
55
|
# @option opts [Array<String>] :ps List of property separators
|
57
56
|
# @option opts [String] :ss Section separator
|
58
57
|
def initialize(opts)
|
58
|
+
super(opts)
|
59
59
|
@opts = opts
|
60
60
|
end
|
61
61
|
|
@@ -72,15 +72,15 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
72
72
|
|
73
73
|
# Add a bullet for the first property of an item
|
74
74
|
#
|
75
|
-
# @param
|
76
|
-
def accept_list_start(
|
75
|
+
# @param _arg [Array] Ignored
|
76
|
+
def accept_list_start(_arg)
|
77
77
|
begin
|
78
78
|
return if @item['placeholder']
|
79
79
|
rescue
|
80
80
|
end
|
81
81
|
|
82
82
|
begin
|
83
|
-
if (@item['due_date'] + ICalPal::ITIME).between?(ICalPal::ITIME + 1, $now.to_i)
|
83
|
+
if (@item['due_date'] + ICalPal::ITIME).between?(ICalPal::ITIME + 1, $now.to_i)
|
84
84
|
@res << "#{@opts[:ab]} " unless @opts[:nb]
|
85
85
|
return
|
86
86
|
end
|
@@ -96,15 +96,15 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
96
96
|
# @option arg [String] .label Contains the property name
|
97
97
|
def accept_list_item_start(arg)
|
98
98
|
@res << @opts[:ps][@ps] || ' ' unless @item['placeholder']
|
99
|
-
@res << colorize(*LABEL_COLOR, arg.label) <<
|
99
|
+
@res << colorize(*LABEL_COLOR, arg.label) << ': ' unless @opts[:npn] || NO_LABEL.any?(arg.label)
|
100
100
|
|
101
101
|
@ps += 1 unless @ps == @opts[:ps].count - 1
|
102
102
|
end
|
103
103
|
|
104
104
|
# Add a blank line
|
105
105
|
#
|
106
|
-
# @param
|
107
|
-
def accept_blank_line(*
|
106
|
+
# @param *_arg [Array] Ignored
|
107
|
+
def accept_blank_line(*_arg)
|
108
108
|
@res << "\n"
|
109
109
|
end
|
110
110
|
|
@@ -119,9 +119,9 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
119
119
|
@res << h.text
|
120
120
|
|
121
121
|
case h.level
|
122
|
-
when 1
|
123
|
-
@res <<
|
124
|
-
when 2
|
122
|
+
when 1
|
123
|
+
@res << ':'
|
124
|
+
when 2
|
125
125
|
if @prop == 'title' && @item['calendar']
|
126
126
|
@res << bold(" (#{@item['calendar']})") unless @opts[:nc] || @item['title'] == @item['calendar']
|
127
127
|
end
|
@@ -140,8 +140,8 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
140
140
|
|
141
141
|
# Add a section separator
|
142
142
|
#
|
143
|
-
# @param
|
144
|
-
def accept_rule(
|
143
|
+
# @param _weight Ignored
|
144
|
+
def accept_rule(_weight)
|
145
145
|
@res << @opts[:ss]
|
146
146
|
accept_blank_line
|
147
147
|
end
|
@@ -167,6 +167,7 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
167
167
|
# @return [String] str with increased intensity[#BOLD]
|
168
168
|
def bold(str)
|
169
169
|
return str unless @opts[:palette]
|
170
|
+
|
170
171
|
BOLD + str + NORM
|
171
172
|
end
|
172
173
|
|
@@ -177,27 +178,28 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
177
178
|
return str unless c8 && c24 && @opts[:palette]
|
178
179
|
|
179
180
|
case @opts[:palette]
|
180
|
-
when 8
|
181
|
+
when 8 # Default colour table
|
181
182
|
c = ANSI[c8.downcase.to_sym]
|
182
183
|
c ||= ANSI[c24[0..6].downcase.to_sym]
|
183
184
|
c ||= ANSI[:white]
|
184
185
|
|
185
|
-
when 24
|
186
|
+
when 24 # Direct colour in RGB space
|
186
187
|
rgb = c24[1..].split(/(\h\h)(\h\h)(\h\h)/)
|
187
188
|
rgb.map! { |i| i.to_i(16) }
|
188
189
|
c = [ 38, 2, rgb[1..] ].join(';')
|
189
190
|
end
|
190
191
|
|
191
|
-
|
192
|
+
# esc c str esc ansi
|
193
|
+
format('%<esc>c[%<color>sm%<string>s%<esc>c[%<ansi_default>sm',
|
194
|
+
{ esc: 27.chr, color: c, string: str, ansi_default: ANSI[:default] })
|
192
195
|
end
|
193
196
|
|
194
197
|
# @!visibility private
|
195
198
|
|
196
199
|
# @param a [Array] Ignored
|
197
|
-
def accept_list_end(a)
|
198
|
-
end
|
200
|
+
def accept_list_end(a) end
|
199
201
|
|
200
202
|
# @param a [Array] Ignored
|
201
|
-
def accept_list_item_end(a)
|
202
|
-
|
203
|
+
def accept_list_item_end(a) end
|
204
|
+
|
203
205
|
end
|