Class: Redwood::TextField

Inherits:
Object show all
Includes:
Ncurses::Form::DriverHelpers
Defined in:
lib/sup/textfield.rb

Overview

a fully-functional text field supporting completions, expansions, history–everything!

writing this fucking sucked. if you thought ncurses was some 1970s before-people-knew-how-to-program bullshit, wait till you see ncurses forms.

completion comments: completion is done emacs-style, and mostly depends on outside support, as we merely signal the existence of a new set of completions to show (#new_completions?) or that the current list of completions should be rolled if they’re too large to fill the screen (#roll_completions?).

in sup, completion support is implemented through BufferManager#ask and CompletionMode.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeTextField

Returns a new instance of TextField.



23
24
25
26
27
28
29
# File 'lib/sup/textfield.rb', line 23

def initialize
  @i = nil
  @history = []

  @completion_block = nil
  reset_completion_state
end

Instance Attribute Details

#completionsObject (readonly)

Returns the value of attribute completions.



32
33
34
# File 'lib/sup/textfield.rb', line 32

def completions
  @completions
end

Instance Method Details

#activate(window, y, x, width, question, default = nil, &block) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/sup/textfield.rb', line 36

def activate window, y, x, width, question, default=nil, &block
  @w, @y, @x, @width = window, y, x, width
  @question = question
  @completion_block = block
  @field = Ncurses::Form.new_field 1, @width - question.length, @y, @x + question.length, 0, 0
  if @field.respond_to? :opts_off
    @field.opts_off Ncurses::Form::O_STATIC
    @field.opts_off Ncurses::Form::O_BLANK
  end
  @form = Ncurses::Form.new_form [@field]
  @value = default || ''
  Ncurses::Form.post_form @form
  set_cursed_value @value
end

#deactivateObject



59
60
61
62
63
64
65
66
# File 'lib/sup/textfield.rb', line 59

def deactivate
  reset_completion_state
  @form.unpost_form
  @form.free_form
  @field.free_field
  @field = nil
  Ncurses.curs_set 0
end

#handle_input(c) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/sup/textfield.rb', line 68

def handle_input c
  ## short-circuit exit paths
  case c.code
  when Ncurses::KEY_ENTER # submit!
    @value = get_cursed_value
    @history.push @value unless @value =~ /^\s*$/
    @i = @history.size
    return false
  when Ncurses::KEY_CANCEL # cancel
    @value = nil
    return false
  when Ncurses::KEY_TAB # completion
    return true unless @completion_block
    if @completions.empty?
      v = get_cursed_value
      c = @completion_block.call v
      if c.size > 0
        @value = c.map { |full, short| full }.shared_prefix(true)
        set_cursed_value @value
        position_cursor
      end
      if c.size > 1
        @completions = c
        @new_completions = true
        @roll_completions = false
      end
    else
      @new_completions = false
      @roll_completions = true
    end
    return true
  end

  reset_completion_state
  @value = nil

  # ctrl_c: control char
  ctrl_c =
    case c.keycode # only test for keycodes
    when Ncurses::KEY_LEFT
      Ncurses::Form::REQ_PREV_CHAR
    when Ncurses::KEY_RIGHT
      Ncurses::Form::REQ_NEXT_CHAR
    when Ncurses::KEY_DC
      Ncurses::Form::REQ_DEL_CHAR
    when Ncurses::KEY_BACKSPACE
      Ncurses::Form::REQ_DEL_PREV
    when Ncurses::KEY_HOME
      nop
      Ncurses::Form::REQ_BEG_FIELD
    when Ncurses::KEY_END
      Ncurses::Form::REQ_END_FIELD
    when Ncurses::KEY_UP, Ncurses::KEY_DOWN
      unless !@i || @history.empty?
        #debug "history before #{@history.inspect}"
        @i = @i + (c.is_keycode?(Ncurses::KEY_UP) ? -1 : 1)
        @i = 0 if @i < 0
        @i = @history.size if @i > @history.size
        @value = @history[@i] || ''
        #debug "history after #{@history.inspect}"
        set_cursed_value @value
        Ncurses::Form::REQ_END_FIELD
      end
    else
      # return other keycode or nil if it's not a keycode
      c.dumb? ? nil : c.keycode
    end

  # handle keysyms
  # ctrl_c: control char
  ctrl_c = case c
    when ?\177                          # backspace (octal)
      Ncurses::Form::REQ_DEL_PREV
    when ?\C-a                          # home
      nop
      Ncurses::Form::REQ_BEG_FIELD
    when ?\C-e                          # end keysym
      Ncurses::Form::REQ_END_FIELD
    when ?\C-k
      Ncurses::Form::REQ_CLR_EOF
    when ?\C-u
      set_cursed_value cursed_value_after_point
      form_driver_key Ncurses::Form::REQ_END_FIELD
      nop
      Ncurses::Form::REQ_BEG_FIELD
    when ?\C-w
      while action = remove_extra_space
        form_driver_key action
      end
      form_driver_key Ncurses::Form::REQ_PREV_CHAR
      form_driver_key Ncurses::Form::REQ_DEL_WORD
    end if ctrl_c.nil?

  c.replace(ctrl_c).keycode! if ctrl_c  # no effect for dumb CharCode
  form_driver c if c.present?
  true
end

#position_cursorObject



51
52
53
54
55
56
57
# File 'lib/sup/textfield.rb', line 51

def position_cursor
  @w.attrset Colormap.color_for(:none)
  @w.mvaddstr @y, 0, @question
  Ncurses.curs_set 1
  form_driver_key Ncurses::Form::REQ_END_FIELD
  form_driver_key Ncurses::Form::REQ_NEXT_CHAR if @value && @value =~ / $/ # fucking RETARDED
end

#valueObject



34
# File 'lib/sup/textfield.rb', line 34

def value; @value || get_cursed_value end