sup 0.12.1 → 0.13.0
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.tar.gz.sig +1 -0
- data/CONTRIBUTORS +25 -12
- data/History.txt +6 -1
- data/README.md +70 -0
- data/ReleaseNotes +5 -0
- data/bin/sup +22 -15
- data/bin/sup-add +3 -3
- data/bin/sup-config +3 -4
- data/bin/sup-import-dump +1 -1
- data/bin/sup-sync +1 -1
- data/bin/sup-sync-back +2 -2
- data/bin/sup-tweak-labels +1 -1
- data/lib/sup.rb +39 -23
- data/lib/sup/account.rb +4 -0
- data/lib/sup/buffer.rb +4 -7
- data/lib/sup/colormap.rb +10 -2
- data/lib/sup/contact.rb +11 -5
- data/lib/sup/crypto.rb +278 -101
- data/lib/sup/draft.rb +3 -2
- data/lib/sup/horizontal-selector.rb +5 -2
- data/lib/sup/index.rb +47 -42
- data/lib/sup/label.rb +1 -1
- data/lib/sup/message-chunks.rb +4 -2
- data/lib/sup/message.rb +14 -3
- data/lib/sup/modes/buffer-list-mode.rb +1 -1
- data/lib/sup/modes/compose-mode.rb +1 -1
- data/lib/sup/modes/contact-list-mode.rb +2 -2
- data/lib/sup/modes/edit-message-async-mode.rb +109 -0
- data/lib/sup/modes/edit-message-mode.rb +148 -16
- data/lib/sup/modes/file-browser-mode.rb +2 -2
- data/lib/sup/modes/forward-mode.rb +4 -4
- data/lib/sup/modes/line-cursor-mode.rb +2 -2
- data/lib/sup/modes/reply-mode.rb +34 -30
- data/lib/sup/modes/resume-mode.rb +4 -1
- data/lib/sup/modes/scroll-mode.rb +8 -6
- data/lib/sup/modes/text-mode.rb +1 -1
- data/lib/sup/modes/thread-index-mode.rb +44 -25
- data/lib/sup/modes/thread-view-mode.rb +26 -24
- data/lib/sup/person.rb +18 -7
- data/lib/sup/poll.rb +1 -1
- data/lib/sup/rfc2047.rb +1 -1
- data/lib/sup/sent.rb +2 -2
- data/lib/sup/source.rb +1 -1
- data/lib/sup/textfield.rb +38 -1
- data/lib/sup/thread.rb +1 -1
- data/lib/sup/time.rb +83 -0
- data/lib/sup/util.rb +38 -74
- data/lib/sup/version.rb +3 -0
- metadata +333 -168
- metadata.gz.sig +0 -0
- data/README.txt +0 -128
- data/bin/sup-cmd +0 -138
- data/bin/sup-server +0 -44
- data/lib/sup/client.rb +0 -92
- data/lib/sup/protocol.rb +0 -161
- data/lib/sup/server.rb +0 -116
data/lib/sup/draft.rb
CHANGED
@@ -23,7 +23,7 @@ class DraftManager
|
|
23
23
|
def discard m
|
24
24
|
raise ArgumentError, "not a draft: source id #{m.source.id.inspect}, should be #{DraftManager.source_id.inspect} for #{m.id.inspect}" unless m.source.id.to_i == DraftManager.source_id
|
25
25
|
Index.delete m.id
|
26
|
-
File.delete @source.fn_for_offset(m.source_info)
|
26
|
+
File.delete @source.fn_for_offset(m.source_info) rescue Errono::ENOENT
|
27
27
|
UpdateManager.relay self, :single_message_deleted, m
|
28
28
|
end
|
29
29
|
end
|
@@ -70,8 +70,9 @@ class DraftLoader < Source
|
|
70
70
|
def load_header offset
|
71
71
|
File.open(fn_for_offset(offset)) { |f| parse_raw_email_header f }
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
def load_message offset
|
75
|
+
raise SourceError, "Draft not found" unless File.exists? fn_for_offset(offset)
|
75
76
|
File.open fn_for_offset(offset) do |f|
|
76
77
|
RMail::Mailbox::MBoxReader.new(f).each_message do |input|
|
77
78
|
return RMail::Parser.read(input)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Redwood
|
2
2
|
|
3
3
|
class HorizontalSelector
|
4
|
-
attr_accessor :label
|
4
|
+
attr_accessor :label, :changed_by_user
|
5
5
|
|
6
6
|
def initialize label, vals, labels, base_color=:horizontal_selector_unselected_color, selected_color=:horizontal_selector_selected_color
|
7
7
|
@label = label
|
@@ -10,6 +10,7 @@ class HorizontalSelector
|
|
10
10
|
@base_color = base_color
|
11
11
|
@selected_color = selected_color
|
12
12
|
@selection = 0
|
13
|
+
@changed_by_user = false
|
13
14
|
end
|
14
15
|
|
15
16
|
def set_to val; @selection = @vals.index(val) end
|
@@ -24,7 +25,7 @@ class HorizontalSelector
|
|
24
25
|
"#{@label} "
|
25
26
|
end
|
26
27
|
|
27
|
-
[[@base_color, label]] +
|
28
|
+
[[@base_color, label]] +
|
28
29
|
(0 ... @labels.length).inject([]) do |array, i|
|
29
30
|
array + [
|
30
31
|
if i == @selection
|
@@ -37,10 +38,12 @@ class HorizontalSelector
|
|
37
38
|
|
38
39
|
def roll_left
|
39
40
|
@selection = (@selection - 1) % @labels.length
|
41
|
+
@changed_by_user = true
|
40
42
|
end
|
41
43
|
|
42
44
|
def roll_right
|
43
45
|
@selection = (@selection + 1) % @labels.length
|
46
|
+
@changed_by_user = true
|
44
47
|
end
|
45
48
|
end
|
46
49
|
|
data/lib/sup/index.rb
CHANGED
@@ -13,6 +13,10 @@ rescue LoadError => e
|
|
13
13
|
$have_chronic = false
|
14
14
|
end
|
15
15
|
|
16
|
+
if ([Xapian.major_version, Xapian.minor_version, Xapian.revision] <=> [1,2,1]) < 0
|
17
|
+
fail "Xapian version 1.2.1 or higher required"
|
18
|
+
end
|
19
|
+
|
16
20
|
module Redwood
|
17
21
|
|
18
22
|
# This index implementation uses Xapian for searching and storage. It
|
@@ -21,7 +25,6 @@ module Redwood
|
|
21
25
|
class Index
|
22
26
|
include InteractiveLock
|
23
27
|
|
24
|
-
STEM_LANGUAGE = "english"
|
25
28
|
INDEX_VERSION = '4'
|
26
29
|
|
27
30
|
## dates are converted to integers for xapian, and are used for document ids,
|
@@ -206,7 +209,9 @@ EOS
|
|
206
209
|
:labels => entry[:labels],
|
207
210
|
:snippet => entry[:snippet]
|
208
211
|
|
209
|
-
|
212
|
+
# Try to find person from contacts before falling back to
|
213
|
+
# generating it from the address.
|
214
|
+
mk_person = lambda { |x| Person.from_name_and_email(*x.reverse!) }
|
210
215
|
entry[:from] = mk_person[entry[:from]]
|
211
216
|
entry[:to].map!(&mk_person)
|
212
217
|
entry[:cc].map!(&mk_person)
|
@@ -231,7 +236,7 @@ EOS
|
|
231
236
|
m = b.call
|
232
237
|
([m.from]+m.to+m.cc+m.bcc).compact.each { |p| contacts << [p.name, p.email] }
|
233
238
|
end
|
234
|
-
contacts.to_a.compact.map { |n,e| Person.
|
239
|
+
contacts.to_a.compact[0...num].map { |n,e| Person.from_name_and_email n, e }
|
235
240
|
end
|
236
241
|
|
237
242
|
## Yield each message-id matching query
|
@@ -422,12 +427,12 @@ EOS
|
|
422
427
|
|
423
428
|
qp = Xapian::QueryParser.new
|
424
429
|
qp.database = @xapian
|
425
|
-
qp.stemmer = Xapian::Stem.new(
|
430
|
+
qp.stemmer = Xapian::Stem.new($config[:stem_language])
|
426
431
|
qp.stemming_strategy = Xapian::QueryParser::STEM_SOME
|
427
432
|
qp.default_op = Xapian::Query::OP_AND
|
428
433
|
qp.add_valuerangeprocessor(Xapian::NumberValueRangeProcessor.new(DATE_VALUENO, 'date:', true))
|
429
|
-
NORMAL_PREFIX.each { |k,
|
430
|
-
BOOLEAN_PREFIX.each { |k,
|
434
|
+
NORMAL_PREFIX.each { |k,info| info[:prefix].each { |v| qp.add_prefix k, v } }
|
435
|
+
BOOLEAN_PREFIX.each { |k,info| info[:prefix].each { |v| qp.add_boolean_prefix k, v, info[:exclusive] } }
|
431
436
|
|
432
437
|
begin
|
433
438
|
xapian_query = qp.parse_query(subs, Xapian::QueryParser::FLAG_PHRASE|Xapian::QueryParser::FLAG_BOOLEAN|Xapian::QueryParser::FLAG_LOVEHATE|Xapian::QueryParser::FLAG_WILDCARD)
|
@@ -478,31 +483,31 @@ EOS
|
|
478
483
|
|
479
484
|
# Stemmed
|
480
485
|
NORMAL_PREFIX = {
|
481
|
-
'subject' => 'S',
|
482
|
-
'body' => 'B',
|
483
|
-
'from_name' => 'FN',
|
484
|
-
'to_name' => 'TN',
|
485
|
-
'name' => %w(FN TN),
|
486
|
-
'attachment' => 'A',
|
487
|
-
'email_text' => 'E',
|
488
|
-
'' => %w(S B FN TN A E),
|
486
|
+
'subject' => {:prefix => 'S', :exclusive => false},
|
487
|
+
'body' => {:prefix => 'B', :exclusive => false},
|
488
|
+
'from_name' => {:prefix => 'FN', :exclusive => false},
|
489
|
+
'to_name' => {:prefix => 'TN', :exclusive => false},
|
490
|
+
'name' => {:prefix => %w(FN TN), :exclusive => false},
|
491
|
+
'attachment' => {:prefix => 'A', :exclusive => false},
|
492
|
+
'email_text' => {:prefix => 'E', :exclusive => false},
|
493
|
+
'' => {:prefix => %w(S B FN TN A E), :exclusive => false},
|
489
494
|
}
|
490
495
|
|
491
496
|
# Unstemmed
|
492
497
|
BOOLEAN_PREFIX = {
|
493
|
-
'type' => 'K',
|
494
|
-
'from_email' => 'FE',
|
495
|
-
'to_email' => 'TE',
|
496
|
-
'email' => %w(FE TE),
|
497
|
-
'date' => 'D',
|
498
|
-
'label' => 'L',
|
499
|
-
'source_id' => 'I',
|
500
|
-
'attachment_extension' => 'O',
|
501
|
-
'msgid' => 'Q',
|
502
|
-
'id' => 'Q',
|
503
|
-
'thread' => 'H',
|
504
|
-
'ref' => 'R',
|
505
|
-
'location' => 'J',
|
498
|
+
'type' => {:prefix => 'K', :exclusive => true},
|
499
|
+
'from_email' => {:prefix => 'FE', :exclusive => false},
|
500
|
+
'to_email' => {:prefix => 'TE', :exclusive => false},
|
501
|
+
'email' => {:prefix => %w(FE TE), :exclusive => false},
|
502
|
+
'date' => {:prefix => 'D', :exclusive => true},
|
503
|
+
'label' => {:prefix => 'L', :exclusive => false},
|
504
|
+
'source_id' => {:prefix => 'I', :exclusive => true},
|
505
|
+
'attachment_extension' => {:prefix => 'O', :exclusive => false},
|
506
|
+
'msgid' => {:prefix => 'Q', :exclusive => true},
|
507
|
+
'id' => {:prefix => 'Q', :exclusive => true},
|
508
|
+
'thread' => {:prefix => 'H', :exclusive => false},
|
509
|
+
'ref' => {:prefix => 'R', :exclusive => false},
|
510
|
+
'location' => {:prefix => 'J', :exclusive => false},
|
506
511
|
}
|
507
512
|
|
508
513
|
PREFIX = NORMAL_PREFIX.merge BOOLEAN_PREFIX
|
@@ -668,8 +673,8 @@ EOS
|
|
668
673
|
# Person names are indexed with several prefixes
|
669
674
|
person_termer = lambda do |d|
|
670
675
|
lambda do |p|
|
671
|
-
doc.index_text p.name, PREFIX["#{d}_name"] if p.name
|
672
|
-
doc.index_text p.email, PREFIX['email_text']
|
676
|
+
doc.index_text p.name, PREFIX["#{d}_name"][:prefix] if p.name
|
677
|
+
doc.index_text p.email, PREFIX['email_text'][:prefix]
|
673
678
|
doc.add_term mkterm(:email, d, p.email)
|
674
679
|
end
|
675
680
|
end
|
@@ -680,9 +685,9 @@ EOS
|
|
680
685
|
# Full text search content
|
681
686
|
subject_text = m.indexable_subject
|
682
687
|
body_text = m.indexable_body
|
683
|
-
doc.index_text subject_text, PREFIX['subject']
|
684
|
-
doc.index_text body_text, PREFIX['body']
|
685
|
-
m.attachments.each { |a| doc.index_text a, PREFIX['attachment'] }
|
688
|
+
doc.index_text subject_text, PREFIX['subject'][:prefix]
|
689
|
+
doc.index_text body_text, PREFIX['body'][:prefix]
|
690
|
+
m.attachments.each { |a| doc.index_text a, PREFIX['attachment'][:prefix] }
|
686
691
|
|
687
692
|
# Miscellaneous terms
|
688
693
|
doc.add_term mkterm(:date, m.date) if m.date
|
@@ -760,25 +765,25 @@ EOS
|
|
760
765
|
def mkterm type, *args
|
761
766
|
case type
|
762
767
|
when :label
|
763
|
-
PREFIX['label'] + args[0].to_s.downcase
|
768
|
+
PREFIX['label'][:prefix] + args[0].to_s.downcase
|
764
769
|
when :type
|
765
|
-
PREFIX['type'] + args[0].to_s.downcase
|
770
|
+
PREFIX['type'][:prefix] + args[0].to_s.downcase
|
766
771
|
when :date
|
767
|
-
PREFIX['date'] + args[0].getutc.strftime("%Y%m%d%H%M%S")
|
772
|
+
PREFIX['date'][:prefix] + args[0].getutc.strftime("%Y%m%d%H%M%S")
|
768
773
|
when :email
|
769
774
|
case args[0]
|
770
|
-
when :from then PREFIX['from_email']
|
771
|
-
when :to then PREFIX['to_email']
|
775
|
+
when :from then PREFIX['from_email'][:prefix]
|
776
|
+
when :to then PREFIX['to_email'][:prefix]
|
772
777
|
else raise "Invalid email term type #{args[0]}"
|
773
778
|
end + args[1].to_s.downcase
|
774
779
|
when :source_id
|
775
|
-
PREFIX['source_id'] + args[0].to_s.downcase
|
780
|
+
PREFIX['source_id'][:prefix] + args[0].to_s.downcase
|
776
781
|
when :location
|
777
|
-
PREFIX['location'] + [args[0]].pack('n') + args[1].to_s
|
782
|
+
PREFIX['location'][:prefix] + [args[0]].pack('n') + args[1].to_s
|
778
783
|
when :attachment_extension
|
779
|
-
PREFIX['attachment_extension'] + args[0].to_s.downcase
|
784
|
+
PREFIX['attachment_extension'][:prefix] + args[0].to_s.downcase
|
780
785
|
when :msgid, :ref, :thread
|
781
|
-
PREFIX[type.to_s] + args[0][0...(MAX_TERM_LENGTH-1)]
|
786
|
+
PREFIX[type.to_s][:prefix] + args[0][0...(MAX_TERM_LENGTH-1)]
|
782
787
|
else
|
783
788
|
raise "Invalid term type #{type}"
|
784
789
|
end
|
@@ -798,7 +803,7 @@ class Xapian::Document
|
|
798
803
|
|
799
804
|
def index_text text, prefix, weight=1
|
800
805
|
term_generator = Xapian::TermGenerator.new
|
801
|
-
term_generator.stemmer = Xapian::Stem.new(
|
806
|
+
term_generator.stemmer = Xapian::Stem.new($config[:stem_language])
|
802
807
|
term_generator.document = self
|
803
808
|
term_generator.index_text text, weight, prefix
|
804
809
|
end
|
data/lib/sup/label.rb
CHANGED
data/lib/sup/message-chunks.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tempfile'
|
2
|
+
require 'rbconfig'
|
2
3
|
|
3
4
|
## Here we define all the "chunks" that a message is parsed
|
4
5
|
## into. Chunks are used by ThreadViewMode to render a message. Chunks
|
@@ -146,7 +147,7 @@ EOS
|
|
146
147
|
def initial_state; :open end
|
147
148
|
def viewable?; @lines.nil? end
|
148
149
|
def view_default! path
|
149
|
-
case
|
150
|
+
case RbConfig::CONFIG['arch']
|
150
151
|
when /darwin/
|
151
152
|
cmd = "open '#{path}'"
|
152
153
|
else
|
@@ -198,7 +199,7 @@ EOS
|
|
198
199
|
def initialize lines
|
199
200
|
@lines = lines
|
200
201
|
end
|
201
|
-
|
202
|
+
|
202
203
|
def inlineable?; @lines.length == 1 end
|
203
204
|
def quotable?; true end
|
204
205
|
def expandable?; !inlineable? end
|
@@ -272,6 +273,7 @@ EOS
|
|
272
273
|
def patina_color
|
273
274
|
case status
|
274
275
|
when :valid then :cryptosig_valid_color
|
276
|
+
when :valid_untrusted then :cryptosig_valid_untrusted_color
|
275
277
|
when :invalid then :cryptosig_invalid_color
|
276
278
|
else :cryptosig_unknown_color
|
277
279
|
end
|
data/lib/sup/message.rb
CHANGED
@@ -503,7 +503,7 @@ private
|
|
503
503
|
filename = Rfc2047.decode_to $encoding, filename
|
504
504
|
# add this to the attachments list if its not a generated html
|
505
505
|
# attachment (should we allow images with generated names?).
|
506
|
-
# Lowercase the filename because searches are easier that way
|
506
|
+
# Lowercase the filename because searches are easier that way
|
507
507
|
@attachments.push filename.downcase unless filename =~ /^sup-attachment-/
|
508
508
|
add_label :attachment unless filename =~ /^sup-attachment-/
|
509
509
|
content_type = (m.header.content_type || "application/unknown").downcase # sometimes RubyMail gives us nil
|
@@ -590,9 +590,20 @@ private
|
|
590
590
|
state = :text # one of :text, :quote, or :sig
|
591
591
|
chunks = []
|
592
592
|
chunk_lines = []
|
593
|
+
nextline_index = -1
|
593
594
|
|
594
595
|
lines.each_with_index do |line, i|
|
595
|
-
|
596
|
+
if i >= nextline_index
|
597
|
+
# look for next nonblank line only when needed to avoid O(n²)
|
598
|
+
# behavior on sequences of blank lines
|
599
|
+
if nextline_index = lines[(i+1)..-1].index { |l| l !~ /^\s*$/ } # skip blank lines
|
600
|
+
nextline_index += i + 1
|
601
|
+
nextline = lines[nextline_index]
|
602
|
+
else
|
603
|
+
nextline_index = lines.length
|
604
|
+
nextline = nil
|
605
|
+
end
|
606
|
+
end
|
596
607
|
|
597
608
|
case state
|
598
609
|
when :text
|
@@ -604,7 +615,7 @@ private
|
|
604
615
|
## like ":a:a:a:a:a" that occurred in certain emails.
|
605
616
|
if line =~ QUOTE_PATTERN || (line =~ /:$/ && line =~ /\w/ && nextline =~ QUOTE_PATTERN)
|
606
617
|
newstate = :quote
|
607
|
-
elsif line =~ SIG_PATTERN && (lines.length - i) < MAX_SIG_DISTANCE
|
618
|
+
elsif line =~ SIG_PATTERN && (lines.length - i) < MAX_SIG_DISTANCE && !lines[(i+1)..-1].index { |l| l =~ /^-- $/ }
|
608
619
|
newstate = :sig
|
609
620
|
elsif line =~ BLOCK_QUOTE_PATTERN
|
610
621
|
newstate = :block_quote
|
@@ -28,7 +28,7 @@ protected
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def regen_text
|
31
|
-
@bufs = BufferManager.buffers.reject { |name, buf| buf.mode == self }.sort_by { |name, buf| buf.atime }.reverse
|
31
|
+
@bufs = BufferManager.buffers.reject { |name, buf| buf.mode == self || buf.hidden? }.sort_by { |name, buf| buf.atime }.reverse
|
32
32
|
width = @bufs.max_of { |name, buf| buf.mode.name.length }
|
33
33
|
@text = @bufs.map do |name, buf|
|
34
34
|
base_color = buf.system? ? :system_buf_color : :regular_buf_color
|
@@ -26,7 +26,7 @@ class ComposeMode < EditMessageMode
|
|
26
26
|
cc = opts[:cc] || (BufferManager.ask_for_contacts(:people, "Cc: ") or return if $config[:ask_for_cc])
|
27
27
|
bcc = opts[:bcc] || (BufferManager.ask_for_contacts(:people, "Bcc: ") or return if $config[:ask_for_bcc])
|
28
28
|
subj = opts[:subj] || (BufferManager.ask(:subject, "Subject: ") or return if $config[:ask_for_subject])
|
29
|
-
|
29
|
+
|
30
30
|
mode = ComposeMode.new :from => from, :to => to, :cc => cc, :bcc => bcc, :subj => subj
|
31
31
|
BufferManager.spawn "New Message", mode
|
32
32
|
mode.edit_message
|
@@ -89,7 +89,7 @@ class ContactListMode < LineCursorMode
|
|
89
89
|
def search
|
90
90
|
p = @contacts[curpos] or return
|
91
91
|
multi_search [p]
|
92
|
-
end
|
92
|
+
end
|
93
93
|
|
94
94
|
def reload
|
95
95
|
@tags.drop_all_tags
|
@@ -114,7 +114,7 @@ class ContactListMode < LineCursorMode
|
|
114
114
|
@contacts = (@user_contacts + recentc).sort_by { |p| p.sort_by_me }.uniq
|
115
115
|
end
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
protected
|
119
119
|
|
120
120
|
def update
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Redwood
|
2
|
+
|
3
|
+
class EditMessageAsyncMode < LineCursorMode
|
4
|
+
|
5
|
+
HookManager.register "async-edit", <<EOS
|
6
|
+
Runs when 'H' is pressed in async edit mode. You can run whatever code
|
7
|
+
you want here - though the default case would be launching a text
|
8
|
+
editor. Your hook is assumed to not block, so you should use exec() or
|
9
|
+
fork() to launch the editor.
|
10
|
+
|
11
|
+
Once the hook has returned then sup will be responsive as usual. You will
|
12
|
+
still need to press 'E' to exit this buffer and send the message.
|
13
|
+
|
14
|
+
Variables:
|
15
|
+
file_path: The full path to the file containing the message to be edited.
|
16
|
+
|
17
|
+
Return value: None
|
18
|
+
EOS
|
19
|
+
|
20
|
+
register_keymap do |k|
|
21
|
+
k.add :run_async_hook, "Run the async-edit hook", 'H'
|
22
|
+
k.add :edit_finished, "Finished editing message", 'E'
|
23
|
+
k.add :path_to_clipboard, "Copy file path to the clipboard", :enter
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize parent_edit_mode, file_path, msg_subject
|
27
|
+
@parent_edit_mode = parent_edit_mode
|
28
|
+
@file_path = file_path
|
29
|
+
@orig_mtime = File.mtime @file_path
|
30
|
+
|
31
|
+
@text = ["ASYNC MESSAGE EDIT",
|
32
|
+
"", "Your message with subject:", msg_subject, "is saved in a file:", "", @file_path, "",
|
33
|
+
"You can edit your message in the editor of your choice and continue to",
|
34
|
+
"use sup while you edit your message.", "",
|
35
|
+
"Press <Enter> to have the file path copied to the clipboard.", "",
|
36
|
+
"When you have finished editing, select this buffer and press 'E'.",]
|
37
|
+
super()
|
38
|
+
end
|
39
|
+
|
40
|
+
def lines; @text.length end
|
41
|
+
|
42
|
+
def [] i
|
43
|
+
@text[i]
|
44
|
+
end
|
45
|
+
|
46
|
+
def killable?
|
47
|
+
if file_being_edited?
|
48
|
+
if !BufferManager.ask_yes_or_no("It appears the file is still being edited. Are you sure?")
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
@parent_edit_mode.edit_message_async_resume true
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def unsaved?
|
58
|
+
!file_being_edited? && !file_has_been_edited?
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def edit_finished
|
64
|
+
if file_being_edited?
|
65
|
+
if !BufferManager.ask_yes_or_no("It appears the file is still being edited. Are you sure?")
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
@parent_edit_mode.edit_message_async_resume
|
71
|
+
BufferManager.kill_buffer buffer
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def path_to_clipboard
|
76
|
+
if system("which xsel > /dev/null 2>&1")
|
77
|
+
# linux/unix path
|
78
|
+
IO.popen('xsel --clipboard --input', 'r+') { |clipboard| clipboard.puts(@file_path) }
|
79
|
+
BufferManager.flash "Copied file path to clipboard."
|
80
|
+
elsif system("which pbcopy > /dev/null 2>&1")
|
81
|
+
# mac path
|
82
|
+
IO.popen('pbcopy', 'r+') { |clipboard| clipboard.puts(@file_path) }
|
83
|
+
BufferManager.flash "Copied file path to clipboard."
|
84
|
+
else
|
85
|
+
BufferManager.flash "No way to copy text to clipboard - try installing xsel."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def run_async_hook
|
90
|
+
HookManager.run("async-edit", {:file_path => @file_path})
|
91
|
+
end
|
92
|
+
|
93
|
+
def file_being_edited?
|
94
|
+
# check for common editor lock files
|
95
|
+
vim_lock_file = File.join(File.dirname(@file_path), '.'+File.basename(@file_path)+'.swp')
|
96
|
+
emacs_lock_file = File.join(File.dirname(@file_path), '.#'+File.basename(@file_path))
|
97
|
+
|
98
|
+
return true if File.exist?(vim_lock_file) || File.exist?(emacs_lock_file)
|
99
|
+
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
def file_has_been_edited?
|
104
|
+
File.mtime(@file_path) > @orig_mtime
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|