sup 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sup might be problematic. Click here for more details.
- data/History.txt +9 -0
- data/Manifest.txt +5 -1
- data/Rakefile +2 -1
- data/bin/sup +27 -10
- data/bin/sup-add +2 -1
- data/bin/sup-sync-back +51 -23
- data/doc/FAQ.txt +29 -37
- data/doc/Hooks.txt +38 -0
- data/doc/{UserGuide.txt → NewUserGuide.txt} +27 -21
- data/doc/TODO +91 -57
- data/lib/sup.rb +17 -1
- data/lib/sup/buffer.rb +80 -16
- data/lib/sup/colormap.rb +0 -2
- data/lib/sup/contact.rb +3 -2
- data/lib/sup/crypto.rb +110 -0
- data/lib/sup/draft.rb +2 -6
- data/lib/sup/hook.rb +131 -0
- data/lib/sup/imap.rb +27 -16
- data/lib/sup/index.rb +38 -14
- data/lib/sup/keymap.rb +0 -2
- data/lib/sup/label.rb +30 -9
- data/lib/sup/logger.rb +12 -1
- data/lib/sup/maildir.rb +48 -3
- data/lib/sup/mbox.rb +1 -1
- data/lib/sup/mbox/loader.rb +22 -12
- data/lib/sup/mbox/ssh-loader.rb +1 -1
- data/lib/sup/message-chunks.rb +198 -0
- data/lib/sup/message.rb +154 -115
- data/lib/sup/modes/compose-mode.rb +18 -0
- data/lib/sup/modes/contact-list-mode.rb +1 -1
- data/lib/sup/modes/edit-message-mode.rb +112 -31
- data/lib/sup/modes/file-browser-mode.rb +1 -1
- data/lib/sup/modes/inbox-mode.rb +1 -1
- data/lib/sup/modes/label-list-mode.rb +8 -6
- data/lib/sup/modes/label-search-results-mode.rb +4 -1
- data/lib/sup/modes/log-mode.rb +1 -1
- data/lib/sup/modes/reply-mode.rb +18 -16
- data/lib/sup/modes/search-results-mode.rb +1 -1
- data/lib/sup/modes/thread-index-mode.rb +61 -33
- data/lib/sup/modes/thread-view-mode.rb +111 -102
- data/lib/sup/person.rb +5 -1
- data/lib/sup/poll.rb +36 -7
- data/lib/sup/sent.rb +1 -0
- data/lib/sup/source.rb +7 -3
- data/lib/sup/textfield.rb +48 -34
- data/lib/sup/thread.rb +9 -5
- data/lib/sup/util.rb +16 -22
- metadata +7 -3
@@ -13,11 +13,10 @@ cryptic messages. You can also press '?' at any point to get a list of
|
|
13
13
|
keyboard commands, but in the absence of any email, these will be
|
14
14
|
mostly useless. When you get bored, press 'q' to quit.
|
15
15
|
|
16
|
-
To
|
17
|
-
index
|
18
|
-
|
19
|
-
|
20
|
-
its index.
|
16
|
+
To use Sup for email, we need to load messages into the index. The
|
17
|
+
index is where Sup stores all message state (e.g. read or unread, any
|
18
|
+
message labels), and all information necessary for searching and for
|
19
|
+
threading messages. Sup only knows about messages in its index.
|
21
20
|
|
22
21
|
We can add messages to the index by telling Sup about the "source"
|
23
22
|
where the messages reside. Sources are things like IMAP folders, mbox
|
@@ -68,7 +67,7 @@ messages together into threads: each line in the inbox is a thread,
|
|
68
67
|
and the number in parentheses is the number of messages in that
|
69
68
|
thread. (If there's no number, there's just one message in the
|
70
69
|
thread.) In Sup, most operations are on threads, not individual
|
71
|
-
messages. The
|
70
|
+
messages. The idea is that you rarely want to operate on a message
|
72
71
|
independent of its context. You typically want to view, archive, kill,
|
73
72
|
or label all the messages in a thread at one time.
|
74
73
|
|
@@ -168,15 +167,15 @@ can figure out how to:
|
|
168
167
|
- Star an individual message, not just a thread
|
169
168
|
|
170
169
|
There's one last thing to be aware of when using Sup: how it interacts
|
171
|
-
with other email programs. As I described
|
172
|
-
|
173
|
-
|
174
|
-
source
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
170
|
+
with other email programs. As I described above, Sup stores data about
|
171
|
+
messages in the index, but doesn't duplicate the message contents
|
172
|
+
themselves. The messages remain on the source. If the index and the
|
173
|
+
source every fall out of sync, e.g. due to another email client
|
174
|
+
modifying the source, then Sup will be unable to operate on that
|
175
|
+
source. For example, for mbox files, Sup stores a byte offset into the
|
176
|
+
file for each message. If a message deleted from that file by another
|
177
|
+
client, or even marked as read (yeah, mbox sucks), all succeeding
|
178
|
+
offsets will be wrong.
|
180
179
|
|
181
180
|
That's the bad news. The good news is that Sup is pretty good at being
|
182
181
|
able to detect this type of situation, and fixing it is just a matter
|
@@ -185,7 +184,7 @@ how to invoke sup-sync when it detects a problem. This is a
|
|
185
184
|
complication you will almost certainly run in to if you use both Sup
|
186
185
|
and another MUA on the same source, so it's good to be aware of it.
|
187
186
|
|
188
|
-
Have fun, and let me know if you have any problems
|
187
|
+
Have fun, and let me know if you have any problems!
|
189
188
|
|
190
189
|
Appending A: sup-add and sup-sync
|
191
190
|
---------------------------------
|
@@ -201,7 +200,8 @@ Instead of using sup-config to add a new source, you can manually run
|
|
201
200
|
Before you add the source, you need make three decisions. The first is
|
202
201
|
whether you want Sup to regularly poll this source for new messages.
|
203
202
|
By default it will, but if this is a source that will never have new
|
204
|
-
messages, you can specify --unusual. Sup polls only "usual" sources
|
203
|
+
messages, you can specify --unusual. Sup polls only "usual" sources
|
204
|
+
when checking for new mail (unless you manually invoke sup-sync).
|
205
205
|
|
206
206
|
The second is whether you want messages from the source to be
|
207
207
|
automatically archived. An archived message will not show up in your
|
@@ -225,12 +225,12 @@ import except your actual inbox. You can also specify --read to mark
|
|
225
225
|
all imported messages as read; the default is to preserve the
|
226
226
|
read/unread status from the source.
|
227
227
|
|
228
|
-
|
228
|
+
Sup-sync will now load all the messages from the source into the
|
229
229
|
index. Depending on the size of the source, this may take a while.
|
230
230
|
Don't panic! It's a one-time process.
|
231
231
|
|
232
|
-
|
233
|
-
|
232
|
+
Appendix B: Handling high-volume mailing lists
|
233
|
+
----------------------------------------------
|
234
234
|
|
235
235
|
Here's what I recommend:
|
236
236
|
1. Use procmail to filter messages from the list into a distinct source.
|
@@ -238,8 +238,14 @@ Here's what I recommend:
|
|
238
238
|
on, and with a label corresponding to the mailing list name.
|
239
239
|
(E.g.: sup-add mbox:/home/me/Mail/ruby-talk -a -l ruby-talk)
|
240
240
|
3. Voila! Sup will load new messages into the index but not into the
|
241
|
-
inbox, and you can browse the mailing list
|
241
|
+
inbox, and you can browse the mailing list traffic at any point by
|
242
242
|
searching for that label.
|
243
243
|
|
244
244
|
|
245
|
+
Appendix C: Reading blogs with Sup
|
246
|
+
----------------------------------
|
245
247
|
|
248
|
+
Really, blog posts should be read like emails are read---you should be
|
249
|
+
able to mark them as unread, flag them, label them, etc. Use rss2email
|
250
|
+
to transform RSS feeds into emails, direct them all into a source, and
|
251
|
+
add that source to Sup. Voila!
|
data/doc/TODO
CHANGED
@@ -1,12 +1,101 @@
|
|
1
|
-
for 0.
|
1
|
+
for 0.2
|
2
2
|
-------
|
3
|
+
|
4
|
+
x bugfix: contacts.txt isn't parsed correctly when there are spaces in
|
5
|
+
aliases
|
6
|
+
x bugfix: @ signs in names make sendmail die silently
|
7
|
+
x bugfix: sent.mbox and >From
|
8
|
+
x bugfix: tokenized email addresses (amazon.com, etc)
|
9
|
+
x bugfix: trailing spaces in buffermanager.ask
|
10
|
+
x bugfix: need to URL-unescape maildir folders
|
11
|
+
x bugfix: downcasing tab completion
|
12
|
+
x warnings: top-posting, missing attachment
|
13
|
+
x hookability
|
14
|
+
|
15
|
+
for 0.3
|
16
|
+
-------
|
17
|
+
_ mark thread as unread should remember the unread messages and mark
|
18
|
+
only them as unread, just like gmail
|
19
|
+
_ mark thread as unread should have a version within thread-view-mode
|
20
|
+
which then also closes the buffer
|
21
|
+
_ bugfix: time zone parsing broken?
|
22
|
+
_ mailing list auto-subscribe/unsubscribe
|
23
|
+
_ forwards optionally include attachments
|
24
|
+
_ attach messages
|
25
|
+
_ flesh out gpg integration: sign & encrypt outgoing
|
26
|
+
_ mbox: don't keep filehandles open, and protect all reads with dotlockfile
|
27
|
+
_ bugfix: screwing with the headers when editing causes a crash
|
28
|
+
_ need a better way to force an address to a particular name,
|
29
|
+
for things like evite addresses
|
30
|
+
_ pressing A in thread-view-mode should jump to next message
|
31
|
+
_ imap "add all folders on this server" option in sup-add
|
32
|
+
_ for new message flashes, add new message counts until keypress
|
33
|
+
_ bugfix: missing sources should be handled better
|
34
|
+
_ search results: highlight relevant snippets and open to relevant
|
35
|
+
portion of thread
|
36
|
+
_ have "notes" (treated as emails to oneself, never sent) as
|
37
|
+
first-class objects.
|
38
|
+
|
39
|
+
future
|
40
|
+
------
|
41
|
+
_ emlx support (some os x thing)
|
42
|
+
_ tab completion for mid-text cursors
|
43
|
+
_ bugfix: not horizontal scrolling for ncurses text field entry
|
44
|
+
_ use trac or something. this file is getting a little silly.
|
45
|
+
_ saved searches
|
46
|
+
_ bugfix: sometimes, when one new message comes into an imap folder,
|
47
|
+
we don't catch it until a reload. but we do see a message
|
48
|
+
indicating they're loaded to inbox (imap only? hard to reproduce.)
|
49
|
+
_ bugfix: ferret flakiness: just added message but can't find it.
|
50
|
+
possibly a message id tokenization issue.
|
51
|
+
_ bugfix: read before thread-index has finished loading then hides the
|
52
|
+
thread?!? wtf. (on jamie) (? still valid ?)
|
53
|
+
_ bugfix: display field width in index-mode needs to be determined
|
54
|
+
per-character rather than per-byte
|
55
|
+
_ select all, starred, to me, etc
|
56
|
+
_ undo
|
57
|
+
_ Net::SMTP support
|
58
|
+
_ ruby-talk:XXXX detection (via hooks?)
|
59
|
+
_ more control character support in buffer line editing
|
60
|
+
_ mboxz, mboxbz
|
61
|
+
_ swappable keymappings
|
62
|
+
_ bugfix: when returning from a shelling out, sometime ncurses is
|
63
|
+
crazy and refuses to interpret any keystrokes
|
64
|
+
_ configurable colors
|
65
|
+
_ better batch deletion (extend to non-mbox sources)
|
66
|
+
_ annotations on messages
|
67
|
+
_ pop support
|
68
|
+
_ toggleble wrapping of text
|
69
|
+
_ maybe: de-archived messages auto-added to inbox
|
70
|
+
_ prune old entries from people.txt so that it doesn't grow without
|
71
|
+
bound
|
72
|
+
_ maildir+ssh
|
73
|
+
|
74
|
+
maybe
|
75
|
+
-----
|
76
|
+
_ split out threading & message chunk parsing to a separate library
|
77
|
+
_ rangefilter on the initial inbox to only consider the most recent 1000 messages
|
78
|
+
|
79
|
+
denied
|
80
|
+
------
|
81
|
+
x rss feed reading: use rss2email
|
82
|
+
x gmail support: obsoleted by imap
|
83
|
+
|
84
|
+
done
|
85
|
+
----
|
86
|
+
x bugfix: deadlock (on rubyforge) (? still valid ?)
|
87
|
+
x bugfix: ferret corrupt index problem at index.c:901. see http://ferret.davebalmain.com/trac/ticket/279
|
88
|
+
x tab completion for to: and cc: in compose-mode
|
89
|
+
x individual labeling in thread-view-mode
|
90
|
+
x translate aliases in queries on to: and from: fields
|
91
|
+
x tab completion on labeling
|
3
92
|
x bugfix: any interactive prompt after "No new messages." flash has an
|
4
93
|
empty line above it.
|
5
94
|
x detect other sup instances and do something intelligent (because
|
95
|
+
ferret crashes violently with more than one index writer open)
|
6
96
|
x refactor all the *-search-results-mode classes
|
7
97
|
x decode RFC 2047 ("encoded word") headers
|
8
98
|
- see: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/101949, http://dev.rubyonrails.org/ticket/6807
|
9
|
-
ferret crashes violently with more than one index writer open)
|
10
99
|
x create attachments
|
11
100
|
x add arbitrary labels to sources
|
12
101
|
x improve sup-config
|
@@ -21,61 +110,6 @@ x bugfix: sup-add not prompting for old accounts, i think? possibly because
|
|
21
110
|
x wide character support
|
22
111
|
x i18n support
|
23
112
|
x tab completion on labels
|
24
|
-
|
25
|
-
for next release
|
26
|
-
----------------
|
27
|
-
_ use trac or something. this file is getting a little silly.
|
28
|
-
_ gpg integration
|
29
|
-
_ user-defined hooks
|
30
|
-
_ saved searches
|
31
|
-
_ bugfix: missing sources should be handled better
|
32
|
-
_ tab completion for contacts
|
33
|
-
_ bugfix: screwing the headers when editing causes a crash
|
34
|
-
_ bugfix: sometimes, when one new message comes into an imap folder,
|
35
|
-
we don't catch it until a reload. but we do see a message
|
36
|
-
indicating they're loaded to inbox (imap only? hard to reproduce.)
|
37
|
-
_ bugfix: need a better way to force an address to a particular name,
|
38
|
-
for things like evite addresses
|
39
|
-
_ bugfix: ferret flakiness: just added message but can't find it
|
40
|
-
_ add new message counts until keypress
|
41
|
-
_ bugfix: deadlock (on rubyforge)
|
42
|
-
_ bugfix: ferret corrupt index problem at index.c:901. see http://ferret.davebalmain.com/trac/ticket/279
|
43
|
-
_ bugfix: read before thread-index has finished loading then hides the
|
44
|
-
thread?!? wtf. (on jamie)_ bugfix: width in index-mode needs to be
|
45
|
-
determined per-character rather than per-byte
|
46
|
-
_ search results: highlight relevant snippets and open to relevant
|
47
|
-
portion of thread
|
48
|
-
_ rss feed reading
|
49
|
-
_ forward attachments
|
50
|
-
_ select all, starred, to me, etc
|
51
|
-
_ undo
|
52
|
-
_ gmail support
|
53
|
-
_ warnings: top-posting, missing attachment, ruby-talk:XXXX detection
|
54
|
-
_ Net::SMTP support
|
55
|
-
_ more control character support in buffer line editing
|
56
|
-
|
57
|
-
future
|
58
|
-
------
|
59
|
-
mboxz, mboxbz
|
60
|
-
swappable keymappings
|
61
|
-
bugfix: when returning from a shelling out, sometime ncurses is crazy
|
62
|
-
and refuses to interpret any keystrokes
|
63
|
-
better batch deletion (extend to non-mbox sources)
|
64
|
-
annotations on messages
|
65
|
-
pop
|
66
|
-
be able to mark individual messages as spam in thread-view-mode
|
67
|
-
toggle wrapping
|
68
|
-
maybe: de-archived messages auto-added to inbox
|
69
|
-
prune old entries from contacts.txt so that it doesn't grow without bound
|
70
|
-
|
71
|
-
maybe
|
72
|
-
-----
|
73
|
-
split out threading & message chunk parsing to a separate library
|
74
|
-
filters
|
75
|
-
rangefilter on the initial inbox to only consider the most recent 1000 messages
|
76
|
-
|
77
|
-
done
|
78
|
-
----
|
79
113
|
x nice little startup config program
|
80
114
|
x bugfix: triggering a pageup when cursor scrolling up jumps to the
|
81
115
|
bottom of the page rather than the next line
|
data/lib/sup.rb
CHANGED
@@ -3,6 +3,7 @@ require 'yaml'
|
|
3
3
|
require 'zlib'
|
4
4
|
require 'thread'
|
5
5
|
require 'fileutils'
|
6
|
+
require 'curses'
|
6
7
|
|
7
8
|
class Object
|
8
9
|
## this is for debugging purposes because i keep calling #id on the
|
@@ -31,7 +32,7 @@ class Module
|
|
31
32
|
end
|
32
33
|
|
33
34
|
module Redwood
|
34
|
-
VERSION = "0.
|
35
|
+
VERSION = "0.2"
|
35
36
|
|
36
37
|
BASE_DIR = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
|
37
38
|
CONFIG_FN = File.join(BASE_DIR, "config.yaml")
|
@@ -43,6 +44,7 @@ module Redwood
|
|
43
44
|
SENT_FN = File.join(BASE_DIR, "sent.mbox")
|
44
45
|
LOCK_FN = File.join(BASE_DIR, "lock")
|
45
46
|
SUICIDE_FN = File.join(BASE_DIR, "please-kill-yourself")
|
47
|
+
HOOK_DIR = File.join(BASE_DIR, "hooks")
|
46
48
|
|
47
49
|
YAML_DOMAIN = "masanjin.net"
|
48
50
|
YAML_DATE = "2006-10-01"
|
@@ -111,6 +113,7 @@ module Redwood
|
|
111
113
|
Redwood::UpdateManager.new
|
112
114
|
Redwood::PollManager.new
|
113
115
|
Redwood::SuicideManager.new Redwood::SUICIDE_FN
|
116
|
+
Redwood::CryptoManager.new
|
114
117
|
end
|
115
118
|
|
116
119
|
def finish
|
@@ -193,6 +196,10 @@ else
|
|
193
196
|
:editor => ENV["EDITOR"] || "/usr/bin/vim -f -c 'setlocal spell spelllang=en_us' -c 'set filetype=mail'",
|
194
197
|
:thread_by_subject => false,
|
195
198
|
:edit_signature => false,
|
199
|
+
:ask_for_cc => true,
|
200
|
+
:ask_for_bcc => false,
|
201
|
+
:confirm_no_attachments => true,
|
202
|
+
:confirm_top_posting => true,
|
196
203
|
}
|
197
204
|
begin
|
198
205
|
FileUtils.mkdir_p Redwood::BASE_DIR
|
@@ -203,8 +210,16 @@ else
|
|
203
210
|
end
|
204
211
|
|
205
212
|
require "sup/util"
|
213
|
+
require "sup/hook"
|
214
|
+
|
215
|
+
## we have to initialize this guy first, because other classes must
|
216
|
+
## reference it in order to register hooks, and they do that at parse
|
217
|
+
## time.
|
218
|
+
Redwood::HookManager.new Redwood::HOOK_DIR
|
219
|
+
|
206
220
|
require "sup/update"
|
207
221
|
require "sup/suicide"
|
222
|
+
require "sup/message-chunks"
|
208
223
|
require "sup/message"
|
209
224
|
require "sup/source"
|
210
225
|
require "sup/mbox"
|
@@ -224,6 +239,7 @@ require "sup/contact"
|
|
224
239
|
require "sup/tagger"
|
225
240
|
require "sup/draft"
|
226
241
|
require "sup/poll"
|
242
|
+
require "sup/crypto"
|
227
243
|
require "sup/modes/scroll-mode"
|
228
244
|
require "sup/modes/text-mode"
|
229
245
|
require "sup/modes/line-cursor-mode"
|
data/lib/sup/buffer.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'etc'
|
2
2
|
require 'thread'
|
3
|
+
require 'ncurses'
|
3
4
|
|
5
|
+
if defined? Ncurses
|
4
6
|
module Ncurses
|
5
7
|
def rows
|
6
8
|
lame, lamer = [], []
|
@@ -14,6 +16,12 @@ module Ncurses
|
|
14
16
|
lamer.first
|
15
17
|
end
|
16
18
|
|
19
|
+
def curx
|
20
|
+
lame, lamer = [], []
|
21
|
+
stdscr.getyx lame, lamer
|
22
|
+
lamer.first
|
23
|
+
end
|
24
|
+
|
17
25
|
def mutex; @mutex ||= Mutex.new; end
|
18
26
|
def sync &b; mutex.synchronize(&b); end
|
19
27
|
|
@@ -27,12 +35,16 @@ module Ncurses
|
|
27
35
|
end
|
28
36
|
end
|
29
37
|
|
30
|
-
module_function :rows, :cols, :nonblocking_getch, :mutex, :sync
|
38
|
+
module_function :rows, :cols, :curx, :nonblocking_getch, :mutex, :sync
|
39
|
+
|
40
|
+
remove_const :KEY_ENTER
|
41
|
+
remove_const :KEY_CANCEL
|
31
42
|
|
32
43
|
KEY_ENTER = 10
|
33
|
-
KEY_CANCEL =
|
44
|
+
KEY_CANCEL = 7 # ctrl-g
|
34
45
|
KEY_TAB = 9
|
35
46
|
end
|
47
|
+
end
|
36
48
|
|
37
49
|
module Redwood
|
38
50
|
|
@@ -328,12 +340,29 @@ class BufferManager
|
|
328
340
|
|
329
341
|
def ask_with_completions domain, question, completions, default=nil
|
330
342
|
ask domain, question, default do |s|
|
331
|
-
completions.select { |x| x =~ /^#{s}/i }.map { |x| [x
|
343
|
+
completions.select { |x| x =~ /^#{s}/i }.map { |x| [x, x] }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def ask_many_with_completions domain, question, completions, default=nil, sep=" "
|
348
|
+
ask domain, question, default do |partial|
|
349
|
+
prefix, target =
|
350
|
+
case partial#.gsub(/#{sep}+/, sep)
|
351
|
+
when /^\s*$/
|
352
|
+
["", ""]
|
353
|
+
when /^(.+#{sep})$/
|
354
|
+
[$1, ""]
|
355
|
+
when /^(.*#{sep})?(.+?)$/
|
356
|
+
[$1 || "", $2]
|
357
|
+
else
|
358
|
+
raise "william screwed up completion: #{partial.inspect}"
|
359
|
+
end
|
360
|
+
|
361
|
+
completions.select { |x| x =~ /^#{target}/i }.map { |x| [prefix + x, x] }
|
332
362
|
end
|
333
363
|
end
|
334
364
|
|
335
|
-
|
336
|
-
def ask_for_filenames domain, question, default=nil
|
365
|
+
def ask_for_filename domain, question, default=nil
|
337
366
|
answer = ask domain, question, default do |s|
|
338
367
|
if s =~ /(~([^\s\/]*))/ # twiddle directory expansion
|
339
368
|
full = $1
|
@@ -361,13 +390,53 @@ class BufferManager
|
|
361
390
|
elsif File.directory?(answer)
|
362
391
|
spawn_modal "file browser", FileBrowserMode.new(answer)
|
363
392
|
else
|
364
|
-
|
393
|
+
answer
|
365
394
|
end
|
366
395
|
end
|
367
396
|
|
368
|
-
answer
|
397
|
+
answer
|
398
|
+
end
|
399
|
+
|
400
|
+
## returns an array of labels
|
401
|
+
def ask_for_labels domain, question, default_labels, forbidden_labels=[]
|
402
|
+
default_labels = default_labels - forbidden_labels - LabelManager::RESERVED_LABELS
|
403
|
+
default = default_labels.join(" ")
|
404
|
+
default += " " unless default.empty?
|
405
|
+
|
406
|
+
applyable_labels = (LabelManager.applyable_labels - forbidden_labels).map { |l| LabelManager.string_for l }.sort_by { |s| s.downcase }
|
407
|
+
|
408
|
+
answer = ask_many_with_completions domain, question, applyable_labels, default
|
409
|
+
|
410
|
+
return unless answer
|
411
|
+
|
412
|
+
user_labels = answer.split(/\s+/).map { |l| l.intern }
|
413
|
+
user_labels.each do |l|
|
414
|
+
if forbidden_labels.include?(l) || LabelManager::RESERVED_LABELS.include?(l)
|
415
|
+
BufferManager.flash "'#{l}' is a reserved label!"
|
416
|
+
return
|
417
|
+
end
|
418
|
+
end
|
419
|
+
user_labels
|
420
|
+
end
|
421
|
+
|
422
|
+
def ask_for_contacts domain, question, default_contacts=[]
|
423
|
+
default = default_contacts.map { |s| s.to_s }.join(" ")
|
424
|
+
default += " " unless default.empty?
|
425
|
+
|
426
|
+
recent = Index.load_contacts(AccountManager.user_emails, :num => 10).map { |c| [c.longname, c.email] }
|
427
|
+
contacts = ContactManager.contacts.map { |c| [ContactManager.alias_for(c), c.longname, c.email] }
|
428
|
+
|
429
|
+
Redwood::log "recent: #{recent.inspect}"
|
430
|
+
|
431
|
+
completions = (recent + contacts).flatten.uniq.sort
|
432
|
+
answer = BufferManager.ask_many_with_completions domain, question, completions, default, /\s*,\s*/
|
433
|
+
|
434
|
+
if answer
|
435
|
+
answer.split_on_commas.map { |x| ContactManager.contact_for(x.downcase) || PersonManager.person_for(x) }
|
436
|
+
end
|
369
437
|
end
|
370
438
|
|
439
|
+
|
371
440
|
def ask domain, question, default=nil, &block
|
372
441
|
raise "impossible!" if @asking
|
373
442
|
@asking = true
|
@@ -393,27 +462,22 @@ class BufferManager
|
|
393
462
|
|
394
463
|
while true
|
395
464
|
c = Ncurses.nonblocking_getch
|
396
|
-
next unless c
|
465
|
+
next unless c # getch timeout
|
397
466
|
break unless tf.handle_input c # process keystroke
|
398
467
|
|
399
468
|
if tf.new_completions?
|
400
469
|
kill_buffer completion_buf if completion_buf
|
401
470
|
|
402
|
-
|
403
|
-
|
404
|
-
0
|
405
|
-
else
|
406
|
-
File.basename(tf.value).length
|
407
|
-
end
|
471
|
+
shorts = tf.completions.map { |full, short| short }
|
472
|
+
prefix_len = shorts.shared_prefix.length
|
408
473
|
|
409
|
-
mode = CompletionMode.new
|
474
|
+
mode = CompletionMode.new shorts, :header => "Possible completions for \"#{tf.value}\": ", :prefix_len => prefix_len
|
410
475
|
completion_buf = spawn "<completions>", mode, :height => 10
|
411
476
|
|
412
477
|
draw_screen :skip_minibuf => true
|
413
478
|
tf.position_cursor
|
414
479
|
elsif tf.roll_completions?
|
415
480
|
completion_buf.mode.roll
|
416
|
-
|
417
481
|
draw_screen :skip_minibuf => true
|
418
482
|
tf.position_cursor
|
419
483
|
end
|