From c8562c943ee4d956df93dea76a71e7ff2160fb4b Mon Sep 17 00:00:00 2001 From: William Morgan Date: Tue, 8 Sep 2009 15:55:07 -0400 Subject: [PATCH 01/16] fix broken merge Whoops! --- bin/sup | 3 --- 1 file changed, 3 deletions(-) diff --git a/bin/sup b/bin/sup index b3a4f45..e72dba5 100755 --- a/bin/sup +++ b/bin/sup @@ -296,14 +296,11 @@ begin b, new = BufferManager.spawn_unless_exists("All drafts") { LabelSearchResultsMode.new [:draft] } b.mode.load_threads :num => b.content_height if new end -<<<<<<< HEAD:bin/sup when :show_inbox BufferManager.raise_to_front ibuf -======= when :show_console b, new = bm.spawn_unless_exists("Console", :system => true) { ConsoleMode.new } b.mode.run ->>>>>>> console-mode:bin/sup when :nothing, InputSequenceAborted when :redraw bm.completely_redraw_screen -- 2.43.0 From 8039b8594c5246338406b284ab9b2c4974b77937 Mon Sep 17 00:00:00 2001 From: William Morgan Date: Wed, 9 Sep 2009 10:03:08 -0400 Subject: [PATCH 02/16] protect getch from ctrl-c's outside of the main event loop The main event loop was interpreting ctrl-c's correctly, but other getch callers (like asking for input) were not. This change will treat ctrl-c as ctrl-g in those cases. --- bin/sup | 2 +- lib/sup/buffer.rb | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/bin/sup b/bin/sup index e72dba5..6a35cfd 100755 --- a/bin/sup +++ b/bin/sup @@ -210,7 +210,7 @@ begin until Redwood::exceptions.nonempty? || $die c = begin Ncurses.nonblocking_getch - rescue Interrupt => e + rescue Interrupt raise if BufferManager.ask_yes_or_no "Die ungracefully now?" BufferManager.draw_screen nil diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb index d85090a..df5d23c 100644 --- a/lib/sup/buffer.rb +++ b/lib/sup/buffer.rb @@ -35,7 +35,14 @@ module Ncurses end end - module_function :rows, :cols, :curx, :nonblocking_getch, :mutex, :sync + ## pretends ctrl-c's are ctrl-g's + def safe_nonblocking_getch + nonblocking_getch + rescue Interrupt + KEY_CANCEL + end + + module_function :rows, :cols, :curx, :nonblocking_getch, :safe_nonblocking_getch, :mutex, :sync remove_const :KEY_ENTER remove_const :KEY_CANCEL @@ -383,7 +390,7 @@ EOS draw_screen until mode.done? - c = Ncurses.nonblocking_getch + c = Ncurses.safe_nonblocking_getch next unless c # getch timeout break if c == Ncurses::KEY_CANCEL begin @@ -559,7 +566,7 @@ EOS end while true - c = Ncurses.nonblocking_getch + c = Ncurses.safe_nonblocking_getch next unless c # getch timeout break unless tf.handle_input c # process keystroke @@ -612,7 +619,7 @@ EOS ret = nil done = false until done - key = Ncurses.nonblocking_getch or next + key = Ncurses.safe_nonblocking_getch or next if key == Ncurses::KEY_CANCEL done = true elsif accept.nil? || accept.empty? || accept.member?(key) -- 2.43.0 From b119447d2198667e25bad72566581f1f70021417 Mon Sep 17 00:00:00 2001 From: William Morgan Date: Wed, 9 Sep 2009 10:05:11 -0400 Subject: [PATCH 03/16] don't require log-mode to auto-respawn Don't require log-mode (and more importantly, subclasses thereof) to respawn. If a buffer name is provided during initialization, respawning will be on; otherwise, it will be off. Now e.g. console mode can use log-mode without requiring a buffer name, or incurring potentially weird auto-respawn behavior. --- lib/sup/modes/log-mode.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/sup/modes/log-mode.rb b/lib/sup/modes/log-mode.rb index 07fa9dd..de320ce 100644 --- a/lib/sup/modes/log-mode.rb +++ b/lib/sup/modes/log-mode.rb @@ -9,9 +9,12 @@ class LogMode < TextMode k.add :toggle_follow, "Toggle follow mode", 'f' end - def initialize buffer_name + ## if buffer_name is supplied, this mode will spawn a buffer + ## upon receiving the << message. otherwise, it will act like + ## a regular buffer. + def initialize autospawn_buffer_name=nil @follow = true - @buffer_name = buffer_name + @autospawn_buffer_name = autospawn_buffer_name @on_kill = [] super() end @@ -28,8 +31,8 @@ class LogMode < TextMode end def << s - unless buffer - BufferManager.spawn @buffer_name, self, :hidden => true, :system => true + if buffer.nil? && @autospawn_buffer_name + BufferManager.spawn @autospawn_buffer_name, self, :hidden => true, :system => true end s.split("\n").each { |l| super(l + "\n") } # insane. different << semantics. -- 2.43.0 From 4c3b5b0d20df18dfb914d0732ba96c0617a51c57 Mon Sep 17 00:00:00 2001 From: William Morgan Date: Wed, 9 Sep 2009 10:06:38 -0400 Subject: [PATCH 04/16] bugfix: console mode can't start a message in #initialize Now that it's a regular buffer, it must act like one. I've moved the message to #run, where it kinda makes more sense. Also tweaked a few things. --- lib/sup/modes/console-mode.rb | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/sup/modes/console-mode.rb b/lib/sup/modes/console-mode.rb index e2a69d9..f0c626e 100644 --- a/lib/sup/modes/console-mode.rb +++ b/lib/sup/modes/console-mode.rb @@ -21,6 +21,7 @@ class Console def xapian; Index.instance.instance_variable_get :@xapian; end def ferret; Index.instance.instance_variable_get :@index; end + def special_methods; methods - Object.methods end ## files that won't cause problems when reloaded ## TODO expand this list / convert to blacklist @@ -67,12 +68,6 @@ class ConsoleMode < LogMode super @console = Console.new self @binding = @console.instance_eval { binding } - self << <> " end def run + self << < Date: Wed, 26 Aug 2009 15:40:34 -0700 Subject: [PATCH 05/16] add 'a' and 'd' keybindings to thread-view-mode to archive/delete current thread These behave identically to the existing ",a" and ",d" commands, (that is they archive or delete the current thread and then view the next). --- lib/sup/modes/thread-view-mode.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/sup/modes/thread-view-mode.rb b/lib/sup/modes/thread-view-mode.rb index 27167cb..1c38fbb 100644 --- a/lib/sup/modes/thread-view-mode.rb +++ b/lib/sup/modes/thread-view-mode.rb @@ -65,6 +65,9 @@ EOS k.add :unsubscribe_from_list, "Subscribe to/unsubscribe from mailing list", ")" k.add :pipe_message, "Pipe message or attachment to a shell command", '|' + k.add :archive_and_next, "Archive this thread, kill buffer, and view next", 'a' + k.add :delete_and_next, "Delete this thread, kill buffer, and view next", 'd' + k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read:", '.' do |kk| kk.add :archive_and_kill, "Archive this thread and kill buffer", 'a' kk.add :delete_and_kill, "Delete this thread and kill buffer", 'd' -- 2.43.0 From f6873cee99602ce5aae601f67f3b514f0e11593a Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sun, 6 Sep 2009 23:04:22 +0200 Subject: [PATCH 06/16] sort labels in the dump Sorting labels in the dump is useful when you e.g. want to keep track of your dump using an incremental backup system that records diffs, with this patch lines in the dump will only change when there is a real change and no longer just because the random order of the labels changes. --- bin/sup-dump | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/sup-dump b/bin/sup-dump index 8b5bf07..7b33be5 100755 --- a/bin/sup-dump +++ b/bin/sup-dump @@ -26,5 +26,5 @@ Redwood::SourceManager.init index.load index.each_message :load_spam => true, :load_deleted => true, :load_killed => true do |m| - puts "#{m.id} (#{m.labels.to_a * ' '})" + puts "#{m.id} (#{m.labels.to_a.sort_by { |l| l.to_s } * ' '})" end -- 2.43.0 From db4f8f07bdab8ed4662065595e20850e6ad73da0 Mon Sep 17 00:00:00 2001 From: William Morgan Date: Thu, 10 Sep 2009 10:22:23 -0400 Subject: [PATCH 07/16] remove redundant poll message --- lib/sup/poll.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb index 46fe5c5..b59237f 100644 --- a/lib/sup/poll.rb +++ b/lib/sup/poll.rb @@ -91,7 +91,6 @@ EOS num = 0 numi = 0 each_message_from source do |m| - yield "Found message at #{m.source_info} with labels {#{m.labels.to_a * ', '}}" old_m = Index.build_message m.id if old_m if old_m.source.id != source.id || old_m.source_info != m.source_info -- 2.43.0 From 68bf6a277c5fdefb3b9d6a4b5d4dfbce3f9f9ccf Mon Sep 17 00:00:00 2001 From: William Morgan Date: Thu, 10 Sep 2009 10:22:37 -0400 Subject: [PATCH 08/16] move sup-sync output from stderr to stdout --- bin/sup-sync | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/bin/sup-sync b/bin/sup-sync index ddcf8c5..5e89dac 100755 --- a/bin/sup-sync +++ b/bin/sup-sync @@ -99,13 +99,13 @@ index = Redwood::Index.init restored_state = if opts[:restore] dump = {} - $stderr.puts "Loading state dump from #{opts[:restore]}..." + puts "Loading state dump from #{opts[:restore]}..." IO.foreach opts[:restore] do |l| l =~ /^(\S+) \((.*?)\)$/ or raise "Can't read dump line: #{l.inspect}" mid, labels = $1, $2 dump[mid] = labels.to_set_of_symbols end - $stderr.puts "Read #{dump.size} entries from dump file." + puts "Read #{dump.size} entries from dump file." dump else {} @@ -139,7 +139,7 @@ begin end sources.each do |source| - $stderr.puts "Scanning #{source}..." + puts "Scanning #{source}..." num_added = num_updated = num_scanned = num_restored = 0 last_info_time = start_time = Time.now @@ -207,15 +207,15 @@ begin ## now, actually do the operation case dothis when :add_message - $stderr.puts "Adding new message #{source}###{m.source_info} with labels #{m.labels}" if opts[:verbose] + puts "Adding new message #{source}##{m.source_info} with labels #{m.labels}" if opts[:verbose] index.add_message m unless opts[:dry_run] num_added += 1 when :update_message - $stderr.puts "Updating message #{source}###{m.source_info}; labels #{old_m.labels} => #{m.labels}; offset #{old_m.source_info} => #{m.source_info}" if opts[:verbose] + puts "Updating message #{source}##{m.source_info}; labels #{old_m.labels} => #{m.labels}; offset #{old_m.source_info} => #{m.source_info}" if opts[:verbose] index.update_message m unless opts[:dry_run] num_updated += 1 when :update_message_state - $stderr.puts "Changing flags for #{source}##{m.source_info} from #{m.labels} to #{new_labels}" + puts "Changing flags for #{source}##{m.source_info} from #{m.labels} to #{new_labels}" if opts[:verbose] m.labels = new_labels index.update_message_state m unless opts[:dry_run] num_updated += 1 @@ -226,18 +226,18 @@ begin elapsed = last_info_time - start_time pctdone = source.respond_to?(:pct_done) ? source.pct_done : 100.0 * (source.cur_offset.to_f - source.start_offset).to_f / (source.end_offset - source.start_offset).to_f remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone) - $stderr.printf "## read %dm (about %.0f%%) @ %.1fm/s. %s elapsed, about %s remaining\n", num_scanned, pctdone, num_scanned / elapsed, elapsed.to_time_s, remaining.to_time_s + printf "## read %dm (about %.0f%%) @ %.1fm/s. %s elapsed, about %s remaining\n", num_scanned, pctdone, num_scanned / elapsed, elapsed.to_time_s, remaining.to_time_s end end - $stderr.puts "Scanned #{num_scanned}, added #{num_added}, updated #{num_updated} messages from #{source}." - $stderr.puts "Restored state on #{num_restored} (#{100.0 * num_restored / num_scanned}%) messages." if num_restored > 0 + puts "Scanned #{num_scanned}, added #{num_added}, updated #{num_updated} messages from #{source}." + puts "Restored state on #{num_restored} (#{100.0 * num_restored / num_scanned}%) messages." if num_restored > 0 end ## delete any messages in the index that claim they're from one of ## these sources, but that we didn't see. if (target == :all || target == :changed) - $stderr.puts "Deleting missing messages from the index..." + puts "Deleting missing messages from the index..." num_del, num_scanned = 0, 0 sources.each do |source| raise "no source id for #{source}" unless source.id @@ -251,15 +251,15 @@ begin end end end - $stderr.puts "Deleted #{num_del} / #{num_scanned} messages" + puts "Deleted #{num_del} / #{num_scanned} messages" end index.save if opts[:optimize] - $stderr.puts "Optimizing index..." + puts "Optimizing index..." optt = time { index.optimize unless opts[:dry_run] } - $stderr.puts "Optimized index of size #{index.size} in #{optt}s." + puts "Optimized index of size #{index.size} in #{optt}s." end rescue Redwood::FatalSourceError => e $stderr.puts "Sorry, I couldn't communicate with a source: #{e.message}" -- 2.43.0 From 82a14a27c9cf43ce7137e583330c412d90a1eb9a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Adeodato=20Sim=C3=B3?= Date: Thu, 23 Jul 2009 19:19:51 +0200 Subject: [PATCH 09/16] fix parsing of encrypted messages that contain further multipart elements --- lib/sup/crypto.rb | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb index 772b8b6..64429a3 100644 --- a/lib/sup/crypto.rb +++ b/lib/sup/crypto.rb @@ -128,8 +128,26 @@ class CryptoManager end end + # This is gross. This decrypted payload could very well be a multipart + # element itself, as opposed to a simple payload. For example, a + # multipart/signed element, like those generated by Mutt when encrypting + # and signing a message (instead of just clearsigning the body). + # Supposedly, decrypted_payload being a multipart element ought to work + # out nicely because Message::multipart_encrypted_to_chunks() runs the + # decrypted message through message_to_chunks() again to get any + # children. However, it does not work as intended because these inner + # payloads need not carry a MIME-Version header, yet they are fed to + # RMail as a top-level message, for which the MIME-Version header is + # required. This causes for the part not to be detected as multipart, + # hence being shown as an attachment. If we detect this is happening, + # we force the decrypted payload to be interpreted as MIME. + msg = RMail::Parser.read(decrypted_payload) + if msg.header.content_type =~ %r{^multipart/} and not msg.multipart? + decrypted_payload = "MIME-Version: 1.0\n" + decrypted_payload + msg = RMail::Parser.read(decrypted_payload) + end notice = Chunk::CryptoNotice.new :valid, "This message has been decrypted for display" - [notice, sig, RMail::Parser.read(decrypted_payload)] + [notice, sig, msg] else Chunk::CryptoNotice.new :invalid, "This message could not be decrypted", output.split("\n") end -- 2.43.0 From 27cb7f32d1497e3da2d0afbca56e9d5fb607d023 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Wed, 26 Aug 2009 10:17:20 -0700 Subject: [PATCH 10/16] Add a refine_search command to InboxMode This is just a copy of SearchResultsMode's refine_search command. A much cleaner, but more involved, approach would be to rework InboxMode to derive from SearchResultsMode in the first place. --- lib/sup/modes/inbox-mode.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/sup/modes/inbox-mode.rb b/lib/sup/modes/inbox-mode.rb index ba095da..51f3a51 100644 --- a/lib/sup/modes/inbox-mode.rb +++ b/lib/sup/modes/inbox-mode.rb @@ -7,6 +7,7 @@ class InboxMode < ThreadIndexMode ## overwrite toggle_archived with archive k.add :archive, "Archive thread (remove from inbox)", 'a' k.add :read_and_archive, "Archive thread (remove from inbox) and mark read", 'A' + k.add :refine_search, "Refine search", '|' end def initialize @@ -17,6 +18,12 @@ class InboxMode < ThreadIndexMode def is_relevant? m; (m.labels & [:spam, :deleted, :killed, :inbox]) == Set.new([:inbox]) end + def refine_search + text = BufferManager.ask :search, "refine query: ", "label:inbox AND " + return unless text && text !~ /^\s*$/ + SearchResultsMode.spawn_from_query text + end + ## label-list-mode wants to be able to raise us if the user selects ## the "inbox" label, so we need to keep our singletonness around def self.instance; @@instance; end -- 2.43.0 From e127eef693956080b06bc8c0157071aa0c8549cb Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 15 Sep 2009 14:21:16 -0700 Subject: [PATCH 11/16] Use underline for highlight Caution: This patch is not suitable for upstream, (William would rather see a hook for this instead). --- lib/sup/colormap.rb | 71 +++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/lib/sup/colormap.rb b/lib/sup/colormap.rb index fbbbfc9..24b4842 100644 --- a/lib/sup/colormap.rb +++ b/lib/sup/colormap.rb @@ -80,38 +80,45 @@ class Colormap end def highlight_for fg, bg, attrs - hfg = - case fg - when Curses::COLOR_BLUE - Curses::COLOR_WHITE - when Curses::COLOR_YELLOW, Curses::COLOR_GREEN - fg - else - Curses::COLOR_BLACK - end - - hbg = - case bg - when Curses::COLOR_CYAN - Curses::COLOR_YELLOW - when Curses::COLOR_YELLOW - Curses::COLOR_BLUE - else - Curses::COLOR_CYAN - end - - attrs = - if fg == Curses::COLOR_WHITE && attrs.include?(Curses::A_BOLD) - [Curses::A_BOLD] - else - case hfg - when Curses::COLOR_BLACK - [] - else - [Curses::A_BOLD] - end - end - [hfg, hbg, attrs] + [fg, bg, attrs + [Curses::A_UNDERLINE]] +# hfg = +# case fg +# when Curses::COLOR_YELLOW +# Curses::COLOR_BLACK +# else +# fg +# end +# case fg +# when Curses::COLOR_BLUE +# Curses::COLOR_WHITE +# when Curses::COLOR_YELLOW, Curses::COLOR_GREEN +# fg +# else +# Curses::COLOR_WHITE +# end + +# hbg = Curses::COLOR_YELLOW +# case bg +# when Curses::COLOR_CYAN +# Curses::COLOR_YELLOW +# when Curses::COLOR_YELLOW +# Curses::COLOR_BLUE +# else +# Curses::COLOR_BLUE +# end + +# attrs = +# if attrs.include?(Curses::A_BOLD) +# [Curses::A_BOLD] +# else +# case hfg +# when Curses::COLOR_BLACK +# [] +# else +# [Curses::A_BOLD] +# end +# end +# [hfg, hbg, attrs] end def color_for sym, highlight=false -- 2.43.0 From 93ed1a68e1b4321b62bfd15600b04d3adb6f7c57 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Thu, 17 Sep 2009 12:53:27 -0700 Subject: [PATCH 12/16] Fix message_to_chunks for the m.body == nil case. The code was previously broken in calling EnclosedMessage.new with only 2 instead of 5 arguments. --- lib/sup/message.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sup/message.rb b/lib/sup/message.rb index afa8f00..579c1e5 100644 --- a/lib/sup/message.rb +++ b/lib/sup/message.rb @@ -453,7 +453,7 @@ private cc_people = cc ? Person.from_address_list(cc) : nil [Chunk::EnclosedMessage.new(from_person, to_people, cc_people, msgdate, subj)] + message_to_chunks(payload, encrypted) else - [Chunk::EnclosedMessage.new(nil, "")] + [] end else filename = -- 2.43.0 From 5c9abec637d9162e2c445e7d8f961b71f02fa820 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 22 Sep 2009 11:23:20 -0700 Subject: [PATCH 13/16] Allow thread index view to sort oldest first This allows the thread index view to present oldest messages first instead of always presenting newer messages at the top of the list. This can be toggled using the 'o' key. A config file option :inbox_newest_first controls whether this is turned on by default when viewing the inbox Refine searches inherit the order from the originating thread index. Signed-off-by: Keith Packard --- bin/sup | 6 +++--- lib/sup/modes/inbox-mode.rb | 11 ++++++++++- lib/sup/modes/label-search-results-mode.rb | 7 ++++--- lib/sup/modes/search-results-mode.rb | 9 +++++---- lib/sup/modes/thread-index-mode.rb | 16 +++++++++++++++- 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/bin/sup b/bin/sup index e53a8e8..43a26fc 100755 --- a/bin/sup +++ b/bin/sup @@ -205,7 +205,7 @@ begin end if $opts[:search] - SearchResultsMode.spawn_from_query $opts[:search] + SearchResultsMode.spawn_from_query $opts[:search], true end until Redwood::exceptions.nonempty? || $die @@ -267,9 +267,9 @@ begin when :search query = BufferManager.ask :search, "search all messages: " next unless query && query !~ /^\s*$/ - SearchResultsMode.spawn_from_query query + SearchResultsMode.spawn_from_query query, true when :search_unread - SearchResultsMode.spawn_from_query "is:unread" + SearchResultsMode.spawn_from_query "is:unread", InboxMode.newest_first when :list_labels labels = LabelManager.all_labels.map { |l| LabelManager.string_for l } user_label = bm.ask_with_completions :label, "Show threads with label (enter for listing): ", labels diff --git a/lib/sup/modes/inbox-mode.rb b/lib/sup/modes/inbox-mode.rb index 51f3a51..f347ffb 100644 --- a/lib/sup/modes/inbox-mode.rb +++ b/lib/sup/modes/inbox-mode.rb @@ -10,10 +10,19 @@ class InboxMode < ThreadIndexMode k.add :refine_search, "Refine search", '|' end + def self.newest_first + if !$config[:inbox_newest_first].nil? + $config[:inbox_newest_first] + else + true + end + end + def initialize super [:inbox, :sent, :draft], { :label => :inbox, :skip_killed => true } raise "can't have more than one!" if defined? @@instance @@instance = self + @newest_first = InboxMode.newest_first end def is_relevant? m; (m.labels & [:spam, :deleted, :killed, :inbox]) == Set.new([:inbox]) end @@ -21,7 +30,7 @@ class InboxMode < ThreadIndexMode def refine_search text = BufferManager.ask :search, "refine query: ", "label:inbox AND " return unless text && text !~ /^\s*$/ - SearchResultsMode.spawn_from_query text + SearchResultsMode.spawn_from_query text, @newest_first end ## label-list-mode wants to be able to raise us if the user selects diff --git a/lib/sup/modes/label-search-results-mode.rb b/lib/sup/modes/label-search-results-mode.rb index bca51d4..6899104 100644 --- a/lib/sup/modes/label-search-results-mode.rb +++ b/lib/sup/modes/label-search-results-mode.rb @@ -1,12 +1,13 @@ module Redwood class LabelSearchResultsMode < ThreadIndexMode - def initialize labels + def initialize labels, newest_first @labels = labels opts = { :labels => @labels } opts[:load_deleted] = true if labels.include? :deleted opts[:load_spam] = true if labels.include? :spam super [], opts + @newest_first = newest_first end register_keymap do |k| @@ -17,7 +18,7 @@ class LabelSearchResultsMode < ThreadIndexMode label_query = @labels.size > 1 ? "(#{@labels.join('||')})" : @labels.first query = BufferManager.ask :search, "refine query: ", "+label:#{label_query} " return unless query && query !~ /^\s*$/ - SearchResultsMode.spawn_from_query query + SearchResultsMode.spawn_from_query query, @newest_first end def is_relevant? m; @labels.all? { |l| m.has_label? l } end @@ -29,7 +30,7 @@ class LabelSearchResultsMode < ThreadIndexMode when :inbox BufferManager.raise_to_front InboxMode.instance.buffer else - b, new = BufferManager.spawn_unless_exists("All threads with label '#{label}'") { LabelSearchResultsMode.new [label] } + b, new = BufferManager.spawn_unless_exists("All threads with label '#{label}'") { LabelSearchResultsMode.new [label], true } b.mode.load_threads :num => b.content_height if new end end diff --git a/lib/sup/modes/search-results-mode.rb b/lib/sup/modes/search-results-mode.rb index 121e817..da03b51 100644 --- a/lib/sup/modes/search-results-mode.rb +++ b/lib/sup/modes/search-results-mode.rb @@ -1,9 +1,10 @@ module Redwood class SearchResultsMode < ThreadIndexMode - def initialize query + def initialize query, newest_first @query = query super [], query + @newest_first = newest_first end register_keymap do |k| @@ -13,7 +14,7 @@ class SearchResultsMode < ThreadIndexMode def refine_search text = BufferManager.ask :search, "refine query: ", (@query[:text] + " ") return unless text && text !~ /^\s*$/ - SearchResultsMode.spawn_from_query text + SearchResultsMode.spawn_from_query text, @newest_first end ## a proper is_relevant? method requires some way of asking ferret @@ -22,12 +23,12 @@ class SearchResultsMode < ThreadIndexMode ## the message, and search against it to see if i have > 0 results, ## but that seems pretty insane. - def self.spawn_from_query text + def self.spawn_from_query text, newest_first begin query = Index.parse_query(text) return unless query short_text = text.length < 20 ? text : text[0 ... 20] + "..." - mode = SearchResultsMode.new query + mode = SearchResultsMode.new query, newest_first BufferManager.spawn "search: \"#{short_text}\"", mode mode.load_threads :num => mode.buffer.content_height rescue Index::ParseError => e diff --git a/lib/sup/modes/thread-index-mode.rb b/lib/sup/modes/thread-index-mode.rb index 82f258b..6a6bd58 100644 --- a/lib/sup/modes/thread-index-mode.rb +++ b/lib/sup/modes/thread-index-mode.rb @@ -47,6 +47,7 @@ EOS k.add :tag_matching, "Tag matching threads", 'g' k.add :apply_to_tagged, "Apply next command to all tagged threads", '+', '=' k.add :join_threads, "Force tagged threads to be joined into the same thread", '#' + k.add :toggle_sort, "Toggle newest first/last sort order", 'o' k.add :undo, "Undo the previous action", 'u' end @@ -65,6 +66,8 @@ EOS @hidden_labels = hidden_labels + LabelManager::HIDDEN_RESERVED_LABELS @date_width = DATE_WIDTH + @newest_first = true + @interrupt_search = false initialize_threads # defines @ts and @ts_mutex @@ -219,10 +222,18 @@ EOS UndoManager.undo end + def toggle_sort + @newest_first = !@newest_first + update + end + def update @mutex.synchronize do ## let's see you do THIS in python - @threads = @ts.threads.select { |t| !@hidden_threads[t] }.sort_by { |t| [t.date, t.first.id] }.reverse + @threads = @ts.threads.select { |t| !@hidden_threads[t] }.sort_by { |t| [t.date, t.first.id] } + if @newest_first + @threads = @threads.reverse + end @size_widgets = @threads.map { |t| size_widget_for_thread t } @size_widget_width = @size_widgets.max_of { |w| w.display_length } end @@ -664,6 +675,9 @@ EOS else n = opts[:num] end + if !@newest_first + n = -1 + end myopts = @load_thread_opts.merge({ :when_done => (lambda do |num| opts[:when_done].call(num) if opts[:when_done] -- 2.43.0 From 68898ee727a02712f6fd94efb489d9b616ef425e Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Fri, 25 Sep 2009 12:29:38 -0700 Subject: [PATCH 14/16] Change the default sort for inbox mode to be oldest first. This allows for more natural reading of messages in the order they were actually created. --- lib/sup/modes/inbox-mode.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sup/modes/inbox-mode.rb b/lib/sup/modes/inbox-mode.rb index f347ffb..1cd3b88 100644 --- a/lib/sup/modes/inbox-mode.rb +++ b/lib/sup/modes/inbox-mode.rb @@ -14,7 +14,7 @@ class InboxMode < ThreadIndexMode if !$config[:inbox_newest_first].nil? $config[:inbox_newest_first] else - true + false end end -- 2.43.0 From 3f69617ea17bf316cef829496834c56193dce2fb Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Fri, 25 Sep 2009 13:41:06 -0700 Subject: [PATCH 15/16] Fix uninitialized @name member in person.rb. Apparently a Person can be initialized with a nil name, (presumably from a message where there's no name given), which before this commit resulted in the following warning: ./lib/sup/person.rb:46: warning: instance variable @name not initialized This warning was especially unpleasant since it appeared in the current window, causing the terminal contents to incorrectly scroll up, (as opposed to just appearing in the log or so). --- lib/sup/person.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/sup/person.rb b/lib/sup/person.rb index c4f40a5..cd5b1ea 100644 --- a/lib/sup/person.rb +++ b/lib/sup/person.rb @@ -11,6 +11,8 @@ class Person if @name =~ /^(['"]\s*)(.*?)(\s*["'])$/ @name = $2 end + else + @name = nil end @email = email.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ").downcase -- 2.43.0 From b7e80adc14797014993b983383d6816de3dcb1a7 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Mon, 28 Sep 2009 15:29:44 -0700 Subject: [PATCH 16/16] Add new :crypto_default configuration option. For example, users that want to sign all outgoing messages by default can set: :crypto_default: :sign in ~/.sup/config.yaml. Configuring an invalid value will cause a list of the valid values to be logged at the "error" level. --- lib/sup/horizontal-selector.rb | 8 +++++++- lib/sup/modes/edit-message-mode.rb | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/sup/horizontal-selector.rb b/lib/sup/horizontal-selector.rb index aef16d4..13c63ed 100644 --- a/lib/sup/horizontal-selector.rb +++ b/lib/sup/horizontal-selector.rb @@ -12,7 +12,13 @@ class HorizontalSelector @selection = 0 end - def set_to val; @selection = @vals.index(val) end + def set_to val + if @vals.index(val) + @selection = @vals.index(val) + else + error "Invalid option ", val.inspect, " (valid options: ", @vals.inspect, ")" + end + end def val; @vals[@selection] end diff --git a/lib/sup/modes/edit-message-mode.rb b/lib/sup/modes/edit-message-mode.rb index 8da316f..3449503 100644 --- a/lib/sup/modes/edit-message-mode.rb +++ b/lib/sup/modes/edit-message-mode.rb @@ -89,7 +89,12 @@ EOS if CryptoManager.have_crypto? HorizontalSelector.new "Crypto:", [:none] + CryptoManager::OUTGOING_MESSAGE_OPERATIONS.keys, ["None"] + CryptoManager::OUTGOING_MESSAGE_OPERATIONS.values end - add_selector @crypto_selector if @crypto_selector + if @crypto_selector + if !$config[:crypto_default].nil? + @crypto_selector.set_to $config[:crypto_default] + end + add_selector @crypto_selector + end HookManager.run "before-edit", :header => @header, :body => @body -- 2.43.0