lib/sup/buffer.rb in sup-0.1 vs lib/sup/buffer.rb in sup-0.2

- old
+ new

@@ -1,8 +1,10 @@ require 'etc' require 'thread' +require 'ncurses' +if defined? Ncurses module Ncurses def rows lame, lamer = [], [] stdscr.getmaxyx lame, lamer lame.first @@ -12,10 +14,16 @@ lame, lamer = [], [] stdscr.getmaxyx lame, lamer lamer.first end + def curx + lame, lamer = [], [] + stdscr.getyx lame, lamer + lamer.first + end + def mutex; @mutex ||= Mutex.new; end def sync &b; mutex.synchronize(&b); end ## magically, this stuff seems to work now. i could swear it didn't ## before. hm. @@ -25,16 +33,20 @@ else nil end end - module_function :rows, :cols, :nonblocking_getch, :mutex, :sync + module_function :rows, :cols, :curx, :nonblocking_getch, :mutex, :sync + remove_const :KEY_ENTER + remove_const :KEY_CANCEL + KEY_ENTER = 10 - KEY_CANCEL = ?\a # ctrl-g + KEY_CANCEL = 7 # ctrl-g KEY_TAB = 9 end +end module Redwood class Buffer attr_reader :mode, :x, :y, :width, :height, :title @@ -326,16 +338,33 @@ end end def ask_with_completions domain, question, completions, default=nil ask domain, question, default do |s| - completions.select { |x| x =~ /^#{s}/i }.map { |x| [x.downcase, x] } + completions.select { |x| x =~ /^#{s}/i }.map { |x| [x, x] } end end - ## returns an ARRAY of filenames! - def ask_for_filenames domain, question, default=nil + def ask_many_with_completions domain, question, completions, default=nil, sep=" " + ask domain, question, default do |partial| + prefix, target = + case partial#.gsub(/#{sep}+/, sep) + when /^\s*$/ + ["", ""] + when /^(.+#{sep})$/ + [$1, ""] + when /^(.*#{sep})?(.+?)$/ + [$1 || "", $2] + else + raise "william screwed up completion: #{partial.inspect}" + end + + completions.select { |x| x =~ /^#{target}/i }.map { |x| [prefix + x, x] } + end + end + + def ask_for_filename domain, question, default=nil answer = ask domain, question, default do |s| if s =~ /(~([^\s\/]*))/ # twiddle directory expansion full = $1 name = $2.empty? ? Etc.getlogin : $2 dir = Etc.getpwnam(name).dir rescue nil @@ -359,17 +388,57 @@ if answer.empty? spawn_modal "file browser", FileBrowserMode.new elsif File.directory?(answer) spawn_modal "file browser", FileBrowserMode.new(answer) else - [answer] + answer end end - answer || [] + answer end + ## returns an array of labels + def ask_for_labels domain, question, default_labels, forbidden_labels=[] + default_labels = default_labels - forbidden_labels - LabelManager::RESERVED_LABELS + default = default_labels.join(" ") + default += " " unless default.empty? + + applyable_labels = (LabelManager.applyable_labels - forbidden_labels).map { |l| LabelManager.string_for l }.sort_by { |s| s.downcase } + + answer = ask_many_with_completions domain, question, applyable_labels, default + + return unless answer + + user_labels = answer.split(/\s+/).map { |l| l.intern } + user_labels.each do |l| + if forbidden_labels.include?(l) || LabelManager::RESERVED_LABELS.include?(l) + BufferManager.flash "'#{l}' is a reserved label!" + return + end + end + user_labels + end + + def ask_for_contacts domain, question, default_contacts=[] + default = default_contacts.map { |s| s.to_s }.join(" ") + default += " " unless default.empty? + + recent = Index.load_contacts(AccountManager.user_emails, :num => 10).map { |c| [c.longname, c.email] } + contacts = ContactManager.contacts.map { |c| [ContactManager.alias_for(c), c.longname, c.email] } + + Redwood::log "recent: #{recent.inspect}" + + completions = (recent + contacts).flatten.uniq.sort + answer = BufferManager.ask_many_with_completions domain, question, completions, default, /\s*,\s*/ + + if answer + answer.split_on_commas.map { |x| ContactManager.contact_for(x.downcase) || PersonManager.person_for(x) } + end + end + + def ask domain, question, default=nil, &block raise "impossible!" if @asking @asking = true @textfields[domain] ||= TextField.new Ncurses.stdscr, Ncurses.rows - 1, 0, Ncurses.cols @@ -391,30 +460,25 @@ tf.position_cursor Ncurses.sync { Ncurses.refresh } while true c = Ncurses.nonblocking_getch - next unless c # getch timeout + next unless c # getch timeout break unless tf.handle_input c # process keystroke if tf.new_completions? kill_buffer completion_buf if completion_buf - prefix_len = - if tf.value =~ /\/$/ - 0 - else - File.basename(tf.value).length - end + shorts = tf.completions.map { |full, short| short } + prefix_len = shorts.shared_prefix.length - mode = CompletionMode.new tf.completions.map { |full, short| short }, :header => "Possible completions for \"#{tf.value}\": ", :prefix_len => prefix_len + mode = CompletionMode.new shorts, :header => "Possible completions for \"#{tf.value}\": ", :prefix_len => prefix_len completion_buf = spawn "<completions>", mode, :height => 10 draw_screen :skip_minibuf => true tf.position_cursor elsif tf.roll_completions? completion_buf.mode.roll - draw_screen :skip_minibuf => true tf.position_cursor end Ncurses.sync { Ncurses.refresh }