]> git.cworth.org Git - sup/blobdiff - lib/sup/modes/thread-view-mode.rb
add --commit option to spawn a compose-mode upon startup
[sup] / lib / sup / modes / thread-view-mode.rb
index 2f500750ee8bd37f8659d25f36eadada826d61d5..961fc5d0335e39a80332e813e4dcff010fa15aea 100644 (file)
@@ -34,8 +34,9 @@ EOS
     k.add :expand_all_quotes, "Expand/collapse all quotes in a message", 'o'
     k.add :jump_to_next_open, "Jump to next open message", 'n'
     k.add :jump_to_prev_open, "Jump to previous open message", 'p'
+    k.add :align_current_message, "Align current message in buffer", 'z'
     k.add :toggle_starred, "Star or unstar message", '*'
-    k.add :toggle_new, "Toggle new/read status of message", 'N'
+    k.add :toggle_new, "Toggle unread/read status of message", 'N'
 #    k.add :collapse_non_new_messages, "Collapse all but unread messages", 'N'
     k.add :reply, "Reply to a message", 'r'
     k.add :forward, "Forward a message or attachment", 'f'
@@ -48,12 +49,19 @@ EOS
     k.add :unsubscribe_from_list, "Subscribe to/unsubscribe from mailing list", ")"
     k.add :pipe_message, "Pipe message or attachment to a shell command", '|'
 
-    ## dispatch-and-kill commands
-    k.add_multi "(A)rchive/(d)elete/mark as (s)pam/do (n)othing:", ',' do |kk|
-      kk.add :archive_and_kill, "Archive this thread and view next", 'a'
-      kk.add :delete_and_kill, "Delete this thread and view next", 'd'
-      kk.add :spam_and_kill, "Mark this thread as spam and view next", 's'
-      kk.add :skip_and_kill, "Skip this thread and view next", 'n'
+    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'
+      kk.add :spam_and_kill, "Mark this thread as spam and kill buffer", 's'
+      kk.add :unread_and_kill, "Mark this thread as unread and kill buffer", 'N'
+    end
+
+    k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read/do (n)othing:", ',' do |kk|
+      kk.add :archive_and_next, "Archive this thread, kill buffer, and view next", 'a'
+      kk.add :delete_and_next, "Delete this thread, kill buffer, and view next", 'd'
+      kk.add :spam_and_next, "Mark this thread as spam, kill buffer, and view next", 's'
+      kk.add :unread_and_next, "Mark this thread as unread, kill buffer, and view next", 'N'
+      kk.add :do_nothing_and_next, "Kill buffer, and view next", 'n'
     end
   end
 
@@ -69,7 +77,7 @@ EOS
     @thread = thread
     @hidden_labels = hidden_labels
 
-    ## used for dispatch-and-kill
+    ## used for dispatch-and-next
     @index_mode = index_mode
     @dying = false
 
@@ -267,27 +275,34 @@ EOS
     end
   end
 
-  def jump_to_first_open
+  def jump_to_first_open loose_alignment=false
     m = @message_lines[0] or return
     if @layout[m].state != :closed
-      jump_to_message m
+      jump_to_message m, loose_alignment
     else
-      jump_to_next_open
+      jump_to_next_open loose_alignment
     end
   end
 
-  def jump_to_next_open
+  def jump_to_next_open loose_alignment=false
     return continue_search_in_buffer if in_search? # hack: allow 'n' to apply to both operations
-    m = @message_lines[curpos] or return
+    m = (curpos ... @message_lines.length).argfind { |i| @message_lines[i] }
+    return unless m
     while nextm = @layout[m].next
       break if @layout[nextm].state != :closed
       m = nextm
     end
-    jump_to_message nextm if nextm
+    jump_to_message nextm, loose_alignment if nextm
   end
 
-  def jump_to_prev_open
+  def align_current_message
     m = @message_lines[curpos] or return
+    jump_to_message m
+  end
+
+  def jump_to_prev_open loose_alignment=false
+    m = (0 .. curpos).to_a.reverse.argfind { |i| @message_lines[i] } # bah, .to_a
+    return unless m
     ## jump to the top of the current message if we're in the body;
     ## otherwise, to the previous message
     
@@ -297,22 +312,32 @@ EOS
         break if @layout[prevm].state != :closed
         m = prevm
       end
-      jump_to_message prevm if prevm
+      jump_to_message prevm, loose_alignment if prevm
     else
-      jump_to_message m
+      jump_to_message m, loose_alignment
     end
   end
 
-  def jump_to_message m
+  def jump_to_message m, loose_alignment=false
     l = @layout[m]
     left = l.depth * INDENT_SPACES
     right = left + l.width
 
-    ## jump to the top line unless both top and bottom fit in the current view
-    jump_to_line l.top unless l.top >= topline && l.top <= botline && l.bot >= topline && l.bot <= botline
+    ## jump to the top line
+    if loose_alignment
+      jump_to_line [l.top - 3, 0].max # give 3 lines of top context
+    else
+      jump_to_line l.top
+    end
 
-    ## jump to the left columns unless both left and right fit in the current view
-    jump_to_col left unless left >= leftcol && left <= rightcol && right >= leftcol && right <= rightcol
+    ## jump to the left column
+    if loose_alignment
+      ## try and give 4 columns of left context, but not if it means that
+      ## the right of the message is truncated.
+      jump_to_col [[left - 4, rightcol - l.width - 1].min, 0].max
+    else
+      jump_to_col left
+    end
 
     ## either way, move the cursor to the first line
     set_cursor_pos l.top
@@ -344,46 +369,67 @@ EOS
     @layout = @chunk_layout = @text = nil # for good luck
   end
 
-  def archive_and_kill
-    dispatch_and_kill do
+  def archive_and_kill; archive_and_then :kill end
+  def spam_and_kill; spam_and_then :kill end
+  def delete_and_kill; delete_and_then :kill end
+  def unread_and_kill; unread_and_then :kill end
+
+  def archive_and_next; archive_and_then :next end
+  def spam_and_next; spam_and_then :next end
+  def delete_and_next; delete_and_then :next end
+  def unread_and_next; unread_and_then :next end
+  def do_nothing_and_next; do_nothing_and_then :next end
+
+  def archive_and_then op
+    dispatch op do
       @thread.remove_label :inbox
       UpdateManager.relay self, :archived, @thread.first
     end
   end
 
-  def spam_and_kill
-    dispatch_and_kill do
+  def spam_and_then op
+    dispatch op do
       @thread.apply_label :spam
       UpdateManager.relay self, :spammed, @thread.first
     end
   end
 
-  def delete_and_kill
-    dispatch_and_kill do
+  def delete_and_then op
+    dispatch op do
       @thread.apply_label :deleted
       UpdateManager.relay self, :deleted, @thread.first
     end
   end
 
-  def skip_and_kill
-    dispatch_and_kill { }
+  def unread_and_then op
+    dispatch op do
+      @thread.apply_label :unread
+      UpdateManager.relay self, :unread, @thread.first
+    end
+  end
+
+  def do_nothing_and_then op
+    dispatch op
   end
 
-  def dispatch_and_kill
+  def dispatch op
     return if @dying
     @dying = true
 
-    if @index_mode
+    case op
+    when :next
       @index_mode.launch_next_thread_after(@thread) do
-        @thread.save Index if yield
+        @thread.save Index if block_given? && yield
         BufferManager.kill_buffer_safely buffer
       end
-    else
+    when :kill
       @thread.save Index if yield
       BufferManager.kill_buffer_safely buffer
+    else
+      raise ArgumentError, "unknown thread dispatch operation #{op.inspect}"
     end
   end
-  private :dispatch_and_kill
+  private :dispatch
 
   def pipe_message
     chunk = @chunk_lines[curpos]
@@ -601,7 +647,7 @@ private
     BufferManager.erase_flash
     BufferManager.completely_redraw_screen
     unless success
-      BufferManager.spawn "Attachment: #{chunk.filename}", TextMode.new(chunk.to_s)
+      BufferManager.spawn "Attachment: #{chunk.filename}", TextMode.new(chunk.to_s, chunk.filename)
       BufferManager.flash "Couldn't execute view command, viewing as text."
     end
   end