mournmail 0.1.1 → 1.0.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 +5 -5
- data/README.md +50 -27
- data/exe/mournmail_reindex +16 -0
- data/lib/mournmail.rb +2 -2
- data/lib/mournmail/commands.rb +23 -30
- data/lib/mournmail/config.rb +21 -8
- data/lib/mournmail/draft_mode.rb +101 -22
- data/lib/mournmail/faces.rb +0 -2
- data/lib/mournmail/mail_encoded_word_patch.rb +71 -0
- data/lib/mournmail/message_mode.rb +60 -15
- data/lib/mournmail/message_rendering.rb +124 -36
- data/lib/mournmail/search_result_mode.rb +143 -0
- data/lib/mournmail/summary.rb +94 -23
- data/lib/mournmail/summary_mode.rb +427 -48
- data/lib/mournmail/utils.rb +474 -61
- data/lib/mournmail/version.rb +1 -3
- data/lib/textbringer_plugin.rb +0 -2
- data/mournmail.gemspec +7 -3
- metadata +76 -18
data/lib/mournmail/faces.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
require "mail"
|
2
|
+
|
3
|
+
module Mournmail
|
4
|
+
module MailEncodedWordPatch
|
5
|
+
private
|
6
|
+
|
7
|
+
def fold(prepend = 0) # :nodoc:
|
8
|
+
charset = normalized_encoding
|
9
|
+
decoded_string = decoded.to_s
|
10
|
+
if charset != "UTF-8" ||
|
11
|
+
decoded_string.ascii_only? ||
|
12
|
+
!decoded_string.respond_to?(:encoding) ||
|
13
|
+
decoded_string.encoding != Encoding::UTF_8 ||
|
14
|
+
Regexp.new('\p{Han}|\p{Hiragana}|\p{Katakana}') !~ decoded_string
|
15
|
+
# Use Q encoding
|
16
|
+
return super(prepend)
|
17
|
+
end
|
18
|
+
words = decoded_string.split(/[ \t]/)
|
19
|
+
folded_lines = []
|
20
|
+
b_encoding_extra_size = "=?#{charset}?B??=".bytesize
|
21
|
+
while !words.empty?
|
22
|
+
limit = 78 - prepend
|
23
|
+
line = +""
|
24
|
+
fold_line = false
|
25
|
+
while !fold_line && !words.empty?
|
26
|
+
word = words.first
|
27
|
+
s = (line.empty? ? "" : " ").dup
|
28
|
+
if word.ascii_only?
|
29
|
+
s << word
|
30
|
+
break if !line.empty? && line.bytesize + s.bytesize > limit
|
31
|
+
words.shift
|
32
|
+
if prepend + line.bytesize + s.bytesize > 998
|
33
|
+
words.unshift(s.slice!(998 - prepend .. -1))
|
34
|
+
fold_line = true
|
35
|
+
end
|
36
|
+
else
|
37
|
+
words.shift
|
38
|
+
encoded_text = base64_encode(word)
|
39
|
+
min_size = line.bytesize + s.bytesize + b_encoding_extra_size
|
40
|
+
new_size = min_size + encoded_text.bytesize
|
41
|
+
if new_size > limit
|
42
|
+
n = ((limit - min_size) * 3.0 / 4.0).floor
|
43
|
+
if n <= 0
|
44
|
+
words.unshift(word) if !line.empty?
|
45
|
+
break
|
46
|
+
end
|
47
|
+
truncated = word.byteslice(0, n).scrub("")
|
48
|
+
rest = word.byteslice(truncated.bytesize,
|
49
|
+
word.bytesize - truncated.bytesize)
|
50
|
+
words.unshift(rest)
|
51
|
+
encoded_text = base64_encode(truncated)
|
52
|
+
fold_line = true
|
53
|
+
end
|
54
|
+
encoded_word = "=?#{charset}?B?#{encoded_text}?="
|
55
|
+
s << encoded_word
|
56
|
+
end
|
57
|
+
line << s
|
58
|
+
end
|
59
|
+
folded_lines << line
|
60
|
+
prepend = 1 # Space will be prepended
|
61
|
+
end
|
62
|
+
folded_lines
|
63
|
+
end
|
64
|
+
|
65
|
+
def base64_encode(word)
|
66
|
+
Mail::Encodings::Base64.encode(word).gsub(/[\r\n]/, "")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
Mail::UnstructuredField.prepend(Mournmail::MailEncodedWordPatch)
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
require "uri"
|
2
|
+
require "mime/types"
|
2
3
|
|
3
4
|
using Mournmail::MessageRendering
|
4
5
|
|
@@ -7,18 +8,23 @@ module Mournmail
|
|
7
8
|
MESSAGE_MODE_MAP = Keymap.new
|
8
9
|
MESSAGE_MODE_MAP.define_key("\C-m", :message_open_link_or_part_command)
|
9
10
|
MESSAGE_MODE_MAP.define_key("s", :message_save_part_command)
|
11
|
+
MESSAGE_MODE_MAP.define_key("\t", :message_next_link_or_part_command)
|
10
12
|
|
11
13
|
# See http://nihongo.jp/support/mail_guide/dev_guide.txt
|
12
|
-
|
14
|
+
MAILTO_REGEXP = URI.regexp("mailto")
|
15
|
+
URI_REGEXP = /(https?|ftp):\/\/[^ \t\n>)"]*[^\] \t\n>.,:)"]+|#{MAILTO_REGEXP}/
|
16
|
+
MIME_REGEXP = /^\[(([0-9.]+) [A-Za-z._\-]+\/[A-Za-z._\-]+.*|PGP\/MIME .*)\]$/
|
17
|
+
URI_OR_MIME_REGEXP = /#{URI_REGEXP}|#{MIME_REGEXP}/
|
13
18
|
|
14
19
|
define_syntax :field_name, /^[A-Za-z\-]+: /
|
15
20
|
define_syntax :quotation, /^>.*/
|
16
|
-
define_syntax :mime_part,
|
21
|
+
define_syntax :mime_part, MIME_REGEXP
|
17
22
|
define_syntax :link, URI_REGEXP
|
18
23
|
|
19
24
|
def initialize(buffer)
|
20
25
|
super(buffer)
|
21
26
|
buffer.keymap = MESSAGE_MODE_MAP
|
27
|
+
@attached_file = nil
|
22
28
|
end
|
23
29
|
|
24
30
|
define_local_command(:message_open_link_or_part,
|
@@ -45,6 +51,18 @@ module Mournmail
|
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
54
|
+
define_local_command(:message_next_link_or_part,
|
55
|
+
doc: "Go to the next link or MIME part.") do
|
56
|
+
if @buffer.looking_at?(URI_OR_MIME_REGEXP)
|
57
|
+
@buffer.forward_char
|
58
|
+
end
|
59
|
+
if @buffer.re_search_forward(URI_OR_MIME_REGEXP, raise_error: false)
|
60
|
+
goto_char(@buffer.match_beginning(0))
|
61
|
+
else
|
62
|
+
@buffer.beginning_of_buffer
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
48
66
|
private
|
49
67
|
|
50
68
|
def current_part
|
@@ -53,7 +71,7 @@ module Mournmail
|
|
53
71
|
if @buffer.looking_at?(/\[([0-9.]+) .*\]/)
|
54
72
|
index = match_string(1)
|
55
73
|
indices = index.split(".").map(&:to_i)
|
56
|
-
|
74
|
+
@buffer[:mournmail_mail].dig_part(*indices)
|
57
75
|
else
|
58
76
|
nil
|
59
77
|
end
|
@@ -62,8 +80,8 @@ module Mournmail
|
|
62
80
|
|
63
81
|
def part_file_name(part)
|
64
82
|
file_name =
|
65
|
-
part["content-disposition"]&.parameters&.[]("filename") ||
|
66
|
-
part["content-type"]&.parameters&.[]("name") ||
|
83
|
+
(part["content-disposition"]&.parameters&.[]("filename") rescue nil) ||
|
84
|
+
(part["content-type"]&.parameters&.[]("name") rescue nil) ||
|
67
85
|
part_default_file_name(part)
|
68
86
|
decoded_file_name = Mail::Encodings.decode_encode(file_name, :decode)
|
69
87
|
if /\A([A-Za-z0-9_\-]+)'(?:[A-Za-z0-9_\-])*'(.*)/ =~ decoded_file_name
|
@@ -74,7 +92,12 @@ module Mournmail
|
|
74
92
|
end
|
75
93
|
|
76
94
|
def part_default_file_name(part)
|
77
|
-
base_name =
|
95
|
+
base_name =
|
96
|
+
begin
|
97
|
+
part.cid.gsub(/[^A-Za-z0-9_\-]/, "_")
|
98
|
+
rescue NoMethodError
|
99
|
+
"mournmail"
|
100
|
+
end
|
78
101
|
ext = part_extension(part)
|
79
102
|
if ext
|
80
103
|
base_name + "." + ext
|
@@ -111,22 +134,44 @@ module Mournmail
|
|
111
134
|
else
|
112
135
|
file_name = "mournmail"
|
113
136
|
end
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
137
|
+
@attached_file = Tempfile.open(file_name, binmode: true)
|
138
|
+
s = part.decoded
|
139
|
+
if part.content_type == "text/html"
|
140
|
+
s = s.sub(/<meta http-equiv="content-type".*?>/i, "")
|
141
|
+
elsif part.charset
|
142
|
+
s = s.encode(part.charset)
|
143
|
+
end
|
144
|
+
@attached_file.write(s)
|
145
|
+
@attached_file.close
|
146
|
+
if part.main_type == "text" && part.sub_type != "html"
|
147
|
+
find_file(@attached_file.path)
|
119
148
|
else
|
120
149
|
background do
|
121
|
-
system(*CONFIG[:mournmail_file_open_comamnd],
|
150
|
+
system(*CONFIG[:mournmail_file_open_comamnd], @attached_file.path,
|
122
151
|
out: File::NULL, err: File::NULL)
|
123
152
|
end
|
124
153
|
end
|
125
154
|
end
|
126
155
|
|
127
156
|
def open_uri(uri)
|
128
|
-
|
129
|
-
|
157
|
+
case uri
|
158
|
+
when /\Amailto:/
|
159
|
+
u = URI.parse(uri)
|
160
|
+
if u.headers.assoc("subject")
|
161
|
+
re = /^To:\s*\nSubject:\s*\n/
|
162
|
+
else
|
163
|
+
re = /^To:\s*\n/
|
164
|
+
end
|
165
|
+
Commands.mail
|
166
|
+
beginning_of_buffer
|
167
|
+
re_search_forward(re)
|
168
|
+
replace_match("")
|
169
|
+
insert u.to_mailtext.sub(/\n\n\z/, "")
|
170
|
+
end_of_buffer
|
171
|
+
else
|
172
|
+
system(*CONFIG[:mournmail_link_open_comamnd], uri,
|
173
|
+
out: File::NULL, err: File::NULL)
|
174
|
+
end
|
130
175
|
end
|
131
176
|
end
|
132
177
|
end
|
@@ -1,7 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require "mail"
|
4
|
-
require "
|
2
|
+
require "html2text"
|
5
3
|
|
6
4
|
module Mournmail
|
7
5
|
module MessageRendering
|
@@ -10,9 +8,9 @@ module Mournmail
|
|
10
8
|
render_header + "\n" + render_body(indices)
|
11
9
|
end
|
12
10
|
|
13
|
-
def render_header
|
14
|
-
|
15
|
-
val = self[name]
|
11
|
+
def render_header(fields = CONFIG[:mournmail_display_header_fields])
|
12
|
+
fields.map { |name|
|
13
|
+
val = self[name]&.to_s&.gsub(/\t/, " ")
|
16
14
|
val ? "#{name}: #{val}\n" : ""
|
17
15
|
}.join
|
18
16
|
end
|
@@ -20,28 +18,68 @@ module Mournmail
|
|
20
18
|
def render_body(indices = [])
|
21
19
|
if HAVE_MAIL_GPG && encrypted?
|
22
20
|
mail = decrypt(verify: true)
|
23
|
-
|
21
|
+
if mail.signatures.empty?
|
22
|
+
sig = ""
|
23
|
+
else
|
24
|
+
sig = "[PGP/MIME signature]\n" +
|
25
|
+
signature_of(mail)
|
26
|
+
end
|
27
|
+
return "[PGP/MIME encrypted message]\n" + mail.render(indices) + sig
|
24
28
|
end
|
25
29
|
if multipart?
|
26
30
|
parts.each_with_index.map { |part, i|
|
27
|
-
|
31
|
+
no_content = sub_type == "alternative" && i > 0
|
32
|
+
part.render([*indices, i], no_content)
|
28
33
|
}.join
|
29
|
-
|
30
|
-
s = body.decoded
|
31
|
-
if
|
32
|
-
|
34
|
+
elsif main_type.nil? || main_type == "text"
|
35
|
+
s = Mournmail.to_utf8(body.decoded, charset)
|
36
|
+
if sub_type == "html"
|
37
|
+
"[0 text/html]\n" + Html2Text.convert(s)
|
33
38
|
else
|
34
|
-
s
|
35
|
-
end
|
39
|
+
s
|
40
|
+
end
|
41
|
+
else
|
42
|
+
type = Mail::Encodings.decode_encode(self["content-type"].to_s,
|
43
|
+
:decode) rescue
|
44
|
+
"broken/type; error=\"#{$!} (#{$!.class})\""
|
45
|
+
"[0 #{type}]\n"
|
36
46
|
end + pgp_signature
|
37
47
|
end
|
38
48
|
|
49
|
+
def render_text(indices = [])
|
50
|
+
if HAVE_MAIL_GPG && encrypted?
|
51
|
+
mail = decrypt(verify: true)
|
52
|
+
return mail.render_text(indices)
|
53
|
+
end
|
54
|
+
if multipart?
|
55
|
+
parts.each_with_index.map { |part, i|
|
56
|
+
if sub_type == "alternative" && i > 0
|
57
|
+
""
|
58
|
+
else
|
59
|
+
part.render_text([*indices, i])
|
60
|
+
end
|
61
|
+
}.join("\n")
|
62
|
+
elsif main_type.nil? || main_type == "text"
|
63
|
+
s = Mournmail.to_utf8(body.decoded, charset)
|
64
|
+
if sub_type == "html"
|
65
|
+
Html2Text.convert(s)
|
66
|
+
else
|
67
|
+
s
|
68
|
+
end
|
69
|
+
else
|
70
|
+
""
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
39
74
|
def dig_part(i, *rest_indices)
|
40
75
|
if HAVE_MAIL_GPG && encrypted?
|
41
76
|
mail = decrypt(verify: true)
|
42
77
|
return mail.dig_part(i, *rest_indices)
|
43
78
|
end
|
44
|
-
|
79
|
+
if i == 0
|
80
|
+
return self
|
81
|
+
end
|
82
|
+
part = parts[i - 1]
|
45
83
|
if rest_indices.empty?
|
46
84
|
part
|
47
85
|
else
|
@@ -54,31 +92,71 @@ module Mournmail
|
|
54
92
|
def pgp_signature
|
55
93
|
if HAVE_MAIL_GPG && signed?
|
56
94
|
verified = verify
|
57
|
-
|
58
|
-
from = verified.signatures.map { |sig|
|
59
|
-
sig.from rescue sig.fingerprint
|
60
|
-
}.join(", ")
|
61
|
-
"#{validity} signature from #{from}\n"
|
95
|
+
signature_of(verified)
|
62
96
|
else
|
63
97
|
""
|
64
98
|
end
|
65
99
|
end
|
100
|
+
|
101
|
+
def signature_of(m)
|
102
|
+
validity = m.signature_valid? ? "Good" : "Bad"
|
103
|
+
from = m.signatures.map { |sig|
|
104
|
+
sig.from rescue sig.fingerprint
|
105
|
+
}.join(", ")
|
106
|
+
s = "#{validity} signature from #{from}"
|
107
|
+
message(s)
|
108
|
+
s + "\n"
|
109
|
+
end
|
66
110
|
end
|
67
111
|
|
68
112
|
refine ::Mail::Part do
|
69
|
-
def render(indices)
|
70
|
-
index = indices.join(".")
|
113
|
+
def render(indices, no_content = false)
|
114
|
+
index = indices.map { |i| i + 1 }.join(".")
|
71
115
|
type = Mail::Encodings.decode_encode(self["content-type"].to_s,
|
72
|
-
:decode)
|
73
|
-
|
116
|
+
:decode) rescue
|
117
|
+
"broken/type; error=\"#{$!} (#{$!.class})\""
|
118
|
+
"[#{index} #{type}]\n" +
|
119
|
+
render_content(indices, no_content)
|
120
|
+
end
|
121
|
+
|
122
|
+
def render_text(indices)
|
123
|
+
if multipart?
|
124
|
+
parts.each_with_index.map { |part, i|
|
125
|
+
if sub_type == "alternative" && i > 0
|
126
|
+
""
|
127
|
+
else
|
128
|
+
part.render_text([*indices, i])
|
129
|
+
end
|
130
|
+
}.join("\n")
|
131
|
+
else
|
132
|
+
if main_type == "message" && sub_type == "rfc822"
|
133
|
+
mail = Mail.new(body.raw_source)
|
134
|
+
mail.render_header(CONFIG[:mournmail_quote_header_fields]) +
|
135
|
+
"\n" + mail.render_text(indices)
|
136
|
+
elsif attachment?
|
137
|
+
""
|
138
|
+
else
|
139
|
+
if main_type == "text"
|
140
|
+
if sub_type == "html"
|
141
|
+
Html2Text.convert(decoded).sub(/(?<!\n)\z/, "\n")
|
142
|
+
else
|
143
|
+
decoded.sub(/(?<!\n)\z/, "\n").gsub(/\r\n/, "\n")
|
144
|
+
end
|
145
|
+
else
|
146
|
+
""
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
rescue => e
|
151
|
+
""
|
74
152
|
end
|
75
153
|
|
76
154
|
def dig_part(i, *rest_indices)
|
77
155
|
if main_type == "message" && sub_type == "rfc822"
|
78
|
-
mail = Mail.new(body.
|
156
|
+
mail = Mail.new(body.to_s)
|
79
157
|
mail.dig_part(i, *rest_indices)
|
80
158
|
else
|
81
|
-
part = parts[i]
|
159
|
+
part = parts[i - 1]
|
82
160
|
if rest_indices.empty?
|
83
161
|
part
|
84
162
|
else
|
@@ -89,23 +167,33 @@ module Mournmail
|
|
89
167
|
|
90
168
|
private
|
91
169
|
|
92
|
-
def render_content(indices)
|
170
|
+
def render_content(indices, no_content)
|
93
171
|
if multipart?
|
94
172
|
parts.each_with_index.map { |part, i|
|
95
|
-
part.render([*indices, i]
|
173
|
+
part.render([*indices, i],
|
174
|
+
no_content || sub_type == "alternative" && i > 0)
|
96
175
|
}.join
|
97
|
-
elsif main_type == "message" && sub_type == "rfc822"
|
98
|
-
mail = Mail.new(body.raw_source)
|
99
|
-
mail.render(indices)
|
100
|
-
elsif self["content-disposition"]&.disposition_type == "attachment"
|
101
|
-
""
|
102
176
|
else
|
103
|
-
|
104
|
-
|
105
|
-
|
177
|
+
return "" if no_content
|
178
|
+
if main_type == "message" && sub_type == "rfc822"
|
179
|
+
mail = Mail.new(body.raw_source)
|
180
|
+
mail.render(indices)
|
181
|
+
elsif attachment?
|
106
182
|
""
|
183
|
+
else
|
184
|
+
if main_type == "text"
|
185
|
+
if sub_type == "html"
|
186
|
+
Html2Text.convert(decoded).sub(/(?<!\n)\z/, "\n")
|
187
|
+
else
|
188
|
+
decoded.sub(/(?<!\n)\z/, "\n").gsub(/\r\n/, "\n")
|
189
|
+
end
|
190
|
+
else
|
191
|
+
""
|
192
|
+
end
|
107
193
|
end
|
108
194
|
end
|
195
|
+
rescue => e
|
196
|
+
"Broken part: #{e} (#{e.class})"
|
109
197
|
end
|
110
198
|
end
|
111
199
|
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
using Mournmail::MessageRendering
|
2
|
+
|
3
|
+
module Mournmail
|
4
|
+
class SearchResultMode < Mournmail::SummaryMode
|
5
|
+
SEARCH_RESULT_MODE_MAP = Keymap.new
|
6
|
+
SEARCH_RESULT_MODE_MAP.define_key(" ", :summary_read_command)
|
7
|
+
SEARCH_RESULT_MODE_MAP.define_key(:backspace, :summary_scroll_down_command)
|
8
|
+
SEARCH_RESULT_MODE_MAP.define_key("\C-h", :summary_scroll_down_command)
|
9
|
+
SEARCH_RESULT_MODE_MAP.define_key("\C-?", :summary_scroll_down_command)
|
10
|
+
SEARCH_RESULT_MODE_MAP.define_key("w", :summary_write_command)
|
11
|
+
SEARCH_RESULT_MODE_MAP.define_key("a", :summary_reply_command)
|
12
|
+
SEARCH_RESULT_MODE_MAP.define_key("A", :summary_reply_command)
|
13
|
+
SEARCH_RESULT_MODE_MAP.define_key("f", :summary_forward_command)
|
14
|
+
SEARCH_RESULT_MODE_MAP.define_key("v", :summary_view_source_command)
|
15
|
+
SEARCH_RESULT_MODE_MAP.define_key("q", :search_result_close_command)
|
16
|
+
SEARCH_RESULT_MODE_MAP.define_key("k", :previous_line)
|
17
|
+
SEARCH_RESULT_MODE_MAP.define_key("j", :next_line)
|
18
|
+
SEARCH_RESULT_MODE_MAP.define_key("<", :previous_page_command)
|
19
|
+
SEARCH_RESULT_MODE_MAP.define_key(">", :next_page_command)
|
20
|
+
SEARCH_RESULT_MODE_MAP.define_key("/", :summary_search_command)
|
21
|
+
SEARCH_RESULT_MODE_MAP.define_key("t", :summary_show_thread_command)
|
22
|
+
SEARCH_RESULT_MODE_MAP.define_key("@", :summary_change_account_command)
|
23
|
+
|
24
|
+
def initialize(buffer)
|
25
|
+
super(buffer)
|
26
|
+
buffer.keymap = SEARCH_RESULT_MODE_MAP
|
27
|
+
end
|
28
|
+
|
29
|
+
define_local_command(:summary_read, doc: "Read a mail.") do
|
30
|
+
num = scroll_up_or_current_number
|
31
|
+
return if num.nil?
|
32
|
+
Mournmail.background do
|
33
|
+
message = @buffer[:messages][num]
|
34
|
+
if message.nil? || message._key.nil?
|
35
|
+
raise EditorError, "No message found"
|
36
|
+
end
|
37
|
+
mail = Mail.new(Mournmail.read_mail_cache(message._key))
|
38
|
+
foreground do
|
39
|
+
show_message(mail)
|
40
|
+
@buffer[:message_number] = num
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
define_local_command(:summary_scroll_down,
|
46
|
+
doc: "Scroll down the current message.") do
|
47
|
+
num = @buffer.current_line
|
48
|
+
if num == @buffer[:message_number]
|
49
|
+
window = Mournmail.message_window
|
50
|
+
if window.buffer.name == "*message*"
|
51
|
+
old_window = Window.current
|
52
|
+
begin
|
53
|
+
Window.current = window
|
54
|
+
scroll_down
|
55
|
+
return
|
56
|
+
ensure
|
57
|
+
Window.current = old_window
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
define_local_command(:search_result_close,
|
64
|
+
doc: "Close the search result.") do
|
65
|
+
if @buffer.name == "*thread*"
|
66
|
+
buf = Buffer["*search result*"] || "*summary*"
|
67
|
+
else
|
68
|
+
buf = "*summary*"
|
69
|
+
end
|
70
|
+
kill_buffer(@buffer)
|
71
|
+
switch_to_buffer(buf)
|
72
|
+
end
|
73
|
+
|
74
|
+
define_local_command(:previous_page,
|
75
|
+
doc: "Show the previous page.") do
|
76
|
+
messages = @buffer[:messages]
|
77
|
+
page = messages.current_page - 1
|
78
|
+
if page < 1
|
79
|
+
raise EditorError, "No more page."
|
80
|
+
end
|
81
|
+
summary_search(@buffer[:query], page)
|
82
|
+
end
|
83
|
+
|
84
|
+
define_local_command(:next_page,
|
85
|
+
doc: "Show the next page.") do
|
86
|
+
messages = @buffer[:messages]
|
87
|
+
page = messages.current_page + 1
|
88
|
+
if page > messages.n_pages
|
89
|
+
raise EditorError, "No more page."
|
90
|
+
end
|
91
|
+
summary_search(@buffer[:query], page)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def scroll_up_or_current_number
|
97
|
+
begin
|
98
|
+
num = @buffer.current_line
|
99
|
+
if num == @buffer[:message_number]
|
100
|
+
window = Mournmail.message_window
|
101
|
+
if window.buffer.name == "*message*"
|
102
|
+
old_window = Window.current
|
103
|
+
begin
|
104
|
+
Window.current = window
|
105
|
+
scroll_up
|
106
|
+
return nil
|
107
|
+
ensure
|
108
|
+
Window.current = old_window
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
num
|
113
|
+
rescue RangeError # may be raised by scroll_up
|
114
|
+
next_message
|
115
|
+
retry
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def read_current_mail
|
120
|
+
message = @buffer[:messages][@buffer.current_line]
|
121
|
+
if message.nil?
|
122
|
+
raise EditorError, "No message found"
|
123
|
+
end
|
124
|
+
[Mail.new(Mournmail.read_mail_cache(message._key)), false]
|
125
|
+
end
|
126
|
+
|
127
|
+
def next_message
|
128
|
+
@buffer.end_of_line
|
129
|
+
if @buffer.end_of_buffer?
|
130
|
+
raise EditorError, "No more mail"
|
131
|
+
end
|
132
|
+
@buffer.forward_line
|
133
|
+
end
|
134
|
+
|
135
|
+
def current_message
|
136
|
+
message = @buffer[:messages][@buffer.current_line]
|
137
|
+
if message.nil?
|
138
|
+
raise EditorError, "No message found"
|
139
|
+
end
|
140
|
+
message
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|