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
  /^begin$/        => /^(end)\b|^else$|^rescue *((\w+)? *(=> *\w+)?)?$/,
  # elsif Expression
  # consider cases:
  # elsif(true)
  # elsif true
  # elsif true == true
  # elsif (a = true) && false
  /^if/            => /^(end)\b|^else$|^elsif\b/,
  /^unless/        => /^(end)\b|^else$|^elsif\b/,
  /^case/          => /^(end)\b|^when\b/        ,
  /^def/           => /^(end)\b/                ,
  /^class/         => /^(end)\b/                ,
  /^module/        => /^(end)\b/                ,
  /^while/         => /^(end)\b/                ,
  /^until/         => /^(end)\b/                ,
  # consider cases:
  # 'do
  # ' do
  # "' do
  # /do
  # '{
  # %q{
  # %q| do
  # hey, two lines are even harder!
  # "
  # begin
  /do( *\|.*\|)?$/ => /^(end)\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

disable, disabled?, enable, enabled?, extended

Instance Method Details

#before_loopObject

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



64
65
66
67
68
# File 'lib/rib/extra/autoindent.rb', line 64

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

#detect_autoindent(input) ⇒ Object

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



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

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



87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/rib/extra/autoindent.rb', line 87

def eval_input raw_input
  return super if Autoindent.disabled?
  input  = raw_input.strip
  indent = detect_autoindent(input)
  result, err = if indent.first.to_s.start_with?('left')
                  super(handle_last_line(input))
                else
                  super
                end
  handle_autoindent(input, indent, err)
  [result, err]
end

#get_inputObject



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/rib/extra/autoindent.rb', line 70

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



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

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
    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



138
139
140
141
142
143
# File 'lib/rib/extra/autoindent.rb', line 138

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