1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00

Pry::Indent now handles single line statements.

Previously certain lines of code would break the indentation process. For
example, the following code would result in incorrect indentation:

    def hello; end
    puts "Hello world"

This would result in the following:

    def hello; end
      puts "Hello world"

I've worked around this issue by adding a new method
(Pry::Indent#skip_indentation?) that does a lookahead on the list of tokens to
determine if a line should be indented or not. It's probably not the most
efficient way of doing it but it makes it quite easy to add more tokens to the
list without adding a lot more complexity.

Signed-off-by: Yorick Peterse <yorickpeterse@gmail.com>
This commit is contained in:
Yorick Peterse 2011-10-08 15:30:06 +02:00
parent 971223d94a
commit e2db26574f
2 changed files with 113 additions and 30 deletions

View file

@ -18,19 +18,21 @@ class Pry
# The amount of spaces to insert for each indent level.
Spaces = ' '
# Array containing all the tokens that should increase the indentation
# level.
OpenTokens = [
'def',
'class',
'module',
'[',
'{',
'do',
'if',
'while',
'for'
]
# Hash containing all the tokens that should increase the indentation
# level. The keys of this hash are open tokens, the values the matching
# tokens that should prevent a line from being indented if they appear on
# the same line.
OpenTokens = {
'def' => 'end',
'class' => 'end',
'module' => 'end',
'do' => 'end',
'if' => 'end',
'while' => 'end',
'for' => 'end',
'[' => ']',
'{' => '}',
}
# Collection of tokens that decrease the indentation level.
ClosingTokens = ['end', ']', '}']
@ -82,6 +84,7 @@ class Pry
#
def indent(input)
output = ''
open_tokens = OpenTokens.keys
input.lines.each do |line|
# Remove manually added indentation.
@ -100,7 +103,11 @@ class Pry
break
# Start token found (such as "class"). Update the stack and indent the
# current line.
elsif OpenTokens.include?(token)
elsif open_tokens.include?(token)
# Skip indentation if there's a matching closing token on the same
# line.
next if skip_indentation?(tokens, token)
add = ''
last = @stack[-1]
@ -133,9 +140,60 @@ class Pry
return output.gsub!(/\s+$/, '')
end
##
# Based on a set of tokens and an open token this method will determine if
# a line has to be indented or not. Perhaps not the most efficient way of
# doing it so if you feel it can be improved patches are more than welcome
# :).
#
# @author Yorick Peterse
# @since 08-10-2011
# @param [Array] tokens A list of tokens to scan.
# @param [String] open_token The token who's closing token may or may not
# be included in the list of tokens.
# @return [Boolean]
#
def skip_indentation?(tokens, open_token)
return false if !OpenTokens.key?(open_token)
# Fix the indentation for closing tags (notably 'end').
closing = OpenTokens[open_token]
open = OpenTokens.keys
skip = false
# If the list of tokens contains a matching closing token the line should
# not be indented (and thus we should return true).
tokens.each do |token, kind|
next if IgnoreTokens.include?(kind)
# Skip the indentation if we've found a matching closing token.
if token == closing
skip = true
# Sometimes a line contains a matching closing token followed by another
# open token. In this case the line *should* be indented. An example of
# this is the following:
#
# [10, 15].each do |num|
# puts num
# end
#
# Here we have an open token (the "[") as well as it's closing token
# ("]"). However, there's also a "do" which indicates that the next
# line *should* be indented.
elsif open.include?(token)
skip = false
end
end
return skip
end
##
# Fix the indentation for closing tags (notably 'end'). Note that this
# method will not work on Win32 based systems (or other systems that don't
# have the tput command).
#
# @param [String] full_line The full line of input, including the prompt.
#
def correct_indentation(full_line)
# The whitespace is used to "clear" the current line so existing
# characters don't show up.

View file

@ -12,21 +12,21 @@ describe Pry::Indent do
input = "array = [\n10,\n15\n]"
output = "array = [\n 10,\n 15\n]"
@indent.indent(input).should === output
@indent.indent(input).should == output
end
it 'should indent a hash' do
input = "hash = {\n:name => 'Ruby'\n}"
output = "hash = {\n :name => 'Ruby'\n}"
@indent.indent(input).should === output
@indent.indent(input).should == output
end
it 'should indent a function' do
input = "def\nreturn 10\nend"
output = "def\n return 10\nend"
@indent.indent(input).should === output
@indent.indent(input).should == output
end
it 'should indent a module and class' do
@ -35,14 +35,39 @@ describe Pry::Indent do
input_class = "class Foo\n# Hello world\nend"
output_class = "class Foo\n # Hello world\nend"
@indent.indent(input).should === output
@indent.indent(input_class).should === output_class
@indent.indent(input).should == output
@indent.indent(input_class).should == output_class
end
it 'should indent separate lines' do
@indent.indent('def foo').should === 'def foo'
@indent.indent('return 10').should === ' return 10'
@indent.indent('end').should === 'end'
@indent.indent('def foo').should == 'def foo'
@indent.indent('return 10').should == ' return 10'
@indent.indent('end').should == 'end'
end
it 'should not indent single line statements' do
input = <<TXT.strip
def hello; end
puts "Hello"
TXT
@indent.indent(input).should == input
end
it 'should handle multiple open and closing tokens on a line' do
input = <<TXT.strip
[10, 15].each do |num|
puts num
end
TXT
output = <<TXT.strip
[10, 15].each do |num|
puts num
end
TXT
@indent.indent(input).should == output
end
it 'should properly indent nested code' do
@ -74,14 +99,14 @@ module A
end
TXT
@indent.indent(input).should === output
@indent.indent(input).should == output
end
it 'should indent statements such as if, else, etc' do
input = <<TXT.strip
if a === 10
if a == 10
#
elsif a === 15
elsif a == 15
#
else
#
@ -101,9 +126,9 @@ end
TXT
output = <<TXT.strip
if a === 10
if a == 10
#
elsif a === 15
elsif a == 15
#
else
#
@ -122,6 +147,6 @@ for num in [10, 15, 20] do
end
TXT
@indent.indent(input).should === output
@indent.indent(input).should == output
end
end