Module: Rib::Autoindent

Extended by:
Plugin
Defined in:
lib/rib/extra/autoindent.rb

Constant Summary collapse

BLOCK_REGEXP =

begin block could be simpler, because it should also trigger SyntaxError, otherwise indention would be wiped out. but end block should be exactly match, because we don’t have SyntaxError information, also triggering SyntaxError doesn’t mean it’s not an end block, thinking about nested multiline!

{
  # rescue Expression? (=> VariableName)?
  # consider cases:
  # rescue
  # rescue=>e
  # rescue => e
  # rescue =>e
  # rescue E=>e
  # rescue E
  # rescue E => e
  # rescue E=> e
  # rescue E =>e
  # ensure
  /^begin$/ =>
    /^(end)\b|^else$|^rescue *((\w+)? *(=> *\w+)?)?$|^ensure$/,
  /^def\b/  =>
    /^(end)\b|^else$|^rescue *((\w+)? *(=> *\w+)?)?$|^ensure$/,
  # elsif Expression
  # consider cases:
  # elsif(true)
  # elsif true
  # elsif true == true
  # elsif (a = true) && false
  /^if\b/            => /^(end)\b|^else$|^elsif\b/,
  /^unless\b/        => /^(end)\b|^else$|^elsif\b/,
  /^case\b/          => /^(end)\b|^else$|when\b/  ,
  /^class\b/         => /^(end)\b/                ,
  /^module\b/        => /^(end)\b/                ,
  /^while\b/         => /^(end)\b/                ,
  /^for\b/           => /^(end)\b/                ,
  /^until\b/         => /^(end)\b/                ,
  # consider cases:
  # 'do
  # ' do
  # "' do
  # /do
  # '{
  # %q{
  # %q| do
  # hey, two lines are even harder!
  # "
  # begin
  /do( *\|.*\|)?$/ => /^(end)\b/                ,
  /\{( *\|.*\|)?$/ => /^(\})\B/                 ,
  /\($/            => /^(\))\B/                 ,
  /\[$/            => /^(\])\B/                 ,
  # those are too hard to deal with, so we use syntax error to double check
  # what about this then?
  # v = if true
  #     else
  #     end
}

Instance Attribute Summary

Attributes included from Plugin

#disabled

Instance Method Summary collapse

Methods included from Plugin

const_missing, disable, disabled?, enable, enabled?, extended

Instance Method Details

#before_loopObject

————— Rib API —————



70
71
72
73
74
# File 'lib/rib/extra/autoindent.rb', line 70

def before_loop
  return super if Autoindent.disabled?
  autoindent_spaces
  super
end

#detect_autoindent(input) ⇒ Object

————— Plugin API —————



104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/rib/extra/autoindent.rb', line 104

def detect_autoindent input
  _, backmark = BLOCK_REGEXP.find{ |key,  _| input =~ key }
  if backmark # e.g. begin
    [:right, backmark]
  elsif input =~ autoindent_stack.last
    if $1     # e.g. end, }, etc
      [:left_end]
    else      # e.g. elsif, rescue, etc
      [:left_tmp]
    end
  else
    [:stay]
  end
end

#eval_input(raw_input) ⇒ Object



93
94
95
96
97
98
99
100
# File 'lib/rib/extra/autoindent.rb', line 93

def eval_input raw_input
  return super if Autoindent.disabled?
  input  = raw_input.strip
  indent = detect_autoindent(input)
  result, err = super
  handle_autoindent(input, indent, err)
  [result, err]
end

#get_inputObject



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rib/extra/autoindent.rb', line 76

def get_input
  return super if Autoindent.disabled?
  # this is only a fix in case we don't autoindent correctly
  # if we're autoindenting 100% correct, then this is a useless check
  autoindent_stack.clear if multiline_buffer.empty?

  # this should be called after ::Readline.readline, but it's blocking,
  # and i don't know if there's any hook to do this, so here we use thread
  Thread.new do
    sleep(0.01)
    ::Readline.line_buffer = current_autoindent if
      ::Readline.line_buffer && ::Readline.line_buffer.empty?
  end

  super
end

#handle_autoindent(input, indent, err) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/rib/extra/autoindent.rb', line 119

def handle_autoindent input, indent, err
  case indent.first
  when :right    # we need to go deeper
    if multiline?(err)
      if err.message =~ /unterminated \w+ meets end of file/
        # skip if we're in the middle of a string or regexp
      else
        # indent.last is the way (input regexp matches) to go back
        autoindent_stack << indent.last
      end
    end

  when :left_end # we need to go back
    # could happen in either multiline or not
    handle_last_line(input)
    autoindent_stack.pop

  when :left_tmp # temporarily go back
    handle_last_line(input) if multiline?(err)
  end
end

#handle_last_line(input, indent = current_autoindent(autoindent_stack.size-1)) ⇒ Object



141
142
143
144
145
146
# File 'lib/rib/extra/autoindent.rb', line 141

def handle_last_line input,
                     indent=current_autoindent(autoindent_stack.size-1)
  new_input = "#{indent}#{input}"
  puts("\e[1A\e[K#{prompt}#{new_input}")
  new_input
end