]> git.cworth.org Git - sup/blob - bin/sup
major improvements in broken source handling
[sup] / bin / sup
1 #!/usr/bin/env ruby
2
3 require 'rubygems'
4 require 'ncurses'
5 require "sup"
6
7 Thread.abort_on_exception = true # make debugging possible
8
9 module Redwood
10
11 $exception = nil
12 def reporting_thread
13   ::Thread.new do
14     begin
15       yield
16     rescue Exception => e
17       $exception ||= e
18       raise
19     end
20   end
21 end
22 module_function :reporting_thread
23
24 global_keymap = Keymap.new do |k|
25   k.add :quit, "Quit Redwood", 'q'
26   k.add :help, "Show help", 'H', '?'
27   k.add :roll_buffers, "Switch to next buffer", 'b'
28   k.add :roll_buffers_backwards, "Switch to previous buffer", 'B'
29   k.add :kill_buffer, "Kill the current buffer", 'x'
30   k.add :list_buffers, "List all buffers", 'A'
31   k.add :list_contacts, "List contacts", 'C'
32   k.add :redraw, "Redraw screen", :ctrl_l
33   k.add :search, "Search messages", '/'
34   k.add :list_labels, "List labels", 'L'
35   k.add :poll, "Poll for new messages", 'P'
36   k.add :compose, "Compose new message", 'm'
37 end
38
39 def start_cursing
40   Ncurses.initscr
41   Ncurses.noecho
42   Ncurses.cbreak
43   Ncurses.stdscr.keypad 1
44   Ncurses.curs_set 0
45   Ncurses.start_color
46 end
47
48 def stop_cursing
49   Ncurses.curs_set 1
50   Ncurses.echo
51   Ncurses.endwin
52 end
53 module_function :start_cursing, :stop_cursing
54
55 Redwood::start
56
57 Index.new.load
58 log "loaded #{Index.size} messages from index"
59
60 if(s = Index.source_for DraftManager.source_name)
61   DraftManager.source = s
62 else
63   Index.add_source DraftManager.new_source
64 end
65
66 if(s = Index.source_for SentManager.source_name)
67   SentManager.source = s
68 else
69   Index.add_source SentManager.new_source
70 end
71
72 begin
73   log "starting curses"
74   start_cursing
75
76   log "initializing colormap"
77   Colormap.new do |c|
78     c.add :status_color, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLUE, Ncurses::A_BOLD
79     c.add :index_old_color, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK
80     c.add :index_new_color, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK, 
81            Ncurses::A_BOLD
82     c.add :labellist_old_color, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK
83     c.add :labellist_new_color, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK, 
84            Ncurses::A_BOLD
85     c.add :twiddle_color, Ncurses::COLOR_BLUE, Ncurses::COLOR_BLACK
86     c.add :label_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK
87     c.add :message_patina_color, Ncurses::COLOR_BLACK, Ncurses::COLOR_GREEN
88     c.add :mime_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
89     c.add :quote_patina_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK
90     c.add :sig_patina_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK
91     c.add :quote_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK
92     c.add :sig_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK
93     c.add :to_me_color, Ncurses::COLOR_GREEN, Ncurses::COLOR_BLACK
94     c.add :starred_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK,
95           Ncurses::A_BOLD
96     c.add :starred_patina_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_GREEN,
97           Ncurses::A_BOLD
98     c.add :snippet_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
99     c.add :option_color, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK
100     c.add :tagged_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK,
101           Ncurses::A_BOLD
102     c.add :draft_notification_color, Ncurses::COLOR_RED, Ncurses::COLOR_BLACK,
103           Ncurses::A_BOLD
104   end
105
106   log "initializing buffer manager"
107   bm = BufferManager.new
108
109   log "initializing mail index buffer"
110   imode = InboxMode.new
111   ibuf = bm.spawn "inbox", imode
112
113   log "ready for (inter)action!"
114   Logger.make_buf
115
116   bm.draw_screen
117   imode.load_more_threads ibuf.content_height
118
119   reporting_thread { sleep 3; PollManager.poll }
120   PollManager.start_thread
121
122   until $exception
123     bm.draw_screen
124     c = Ncurses.nonblocking_getch
125     bm.erase_flash
126
127     if c == Ncurses::KEY_RESIZE
128       bm.handle_resize
129     elsif c
130       unless bm.handle_input(c)
131         x = global_keymap.action_for c
132         case x
133         when :quit
134           break
135         when :help
136           curmode = bm.focus_buf.mode
137           bm.spawn_unless_exists("<help for #{curmode.name}>") { HelpMode.new curmode, global_keymap }
138         when :roll_buffers
139           bm.roll_buffers
140         when :roll_buffers_backwards
141           bm.roll_buffers_backwards
142         when :kill_buffer
143           bm.kill_buffer bm.focus_buf if bm.focus_buf.mode.killable?
144         when :list_buffers
145           bm.spawn_unless_exists("Buffer List") { BufferListMode.new }
146         when :list_contacts
147           bm.spawn_unless_exists("Contact List") { ContactListMode.new }
148         when :search
149           text = bm.ask :search, "query: "
150           next unless text && text !~ /^\s*$/
151
152           begin
153             qobj = Index.parse_user_query_string text
154             short_text = text.length < 20 ? text : text[0 ... 20] + "..."
155             log "built query from #{text.inspect}: #{qobj}"
156             mode = SearchResultsMode.new qobj
157             bm.spawn "search: \"#{short_text}\"", mode
158             mode.load_more_threads mode.buffer.content_height
159           rescue Ferret::QueryParser::QueryParseException => e
160             bm.flash "Couldn't parse query."
161           end
162
163         when :list_labels
164           b = bm.spawn_unless_exists("Label List") { LabelListMode.new }
165           b.mode.load_in_background
166         when :compose
167           mode = ComposeMode.new
168           bm.spawn "New Message", mode
169           mode.edit
170         when :poll
171           bm.raise_to_front PollManager.buffer
172           reporting_thread { PollManager.poll }
173         when :nothing
174         when :redraw
175           bm.completely_redraw_screen
176         else
177           bm.flash "Unknown key press '#{c.to_character}' for #{bm.focus_buf.mode.name}."
178         end
179       end
180     end
181   end
182   bm.kill_all_buffers
183 rescue Exception => e
184   $exception ||= e
185 ensure
186   Redwood::finish
187   stop_cursing
188 end
189
190 Index.save unless $exception # TODO: think about this
191
192 if $exception 
193   case $exception
194   when IndexError
195     $stderr.puts <<EOS
196 An error occurred while parsing a message from source:
197    #{$exception.source}.
198 Typically, this means that the source has been modified in some
199 way which has rendered the messages invalid. For example, if it's
200 an mbox file, you may have read or deleted messages using another
201 mail client.
202
203 You must rebuild the index for this source. Please run:
204   sup-import --rebuild #{$exception.source}
205 to correct this error.
206 EOS
207 #' stupid ruby-mode
208   else
209     $stderr.puts <<EOS
210 ----------------------------------------------------------------
211 I'm very sorry, but it seems that an error occurred in Sup. 
212 Please accept my sincere apologies. If you don't mind, please
213 send the backtrace below and a brief report of the circumstances
214 to user wmorgan-sup at site masanjin dot net so that I might
215 address this problem. Thank you!
216
217 Sincerely,
218 William
219 ----------------------------------------------------------------
220
221 The problem was: #{$exception.message} (error type #{$exception.class.name})
222 A backtrace follows:
223 EOS
224   end
225   raise $exception
226 end
227
228 end