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. # The amount of spaces to insert for each indent level.
Spaces = ' ' Spaces = ' '
# Array containing all the tokens that should increase the indentation # Hash containing all the tokens that should increase the indentation
# level. # level. The keys of this hash are open tokens, the values the matching
OpenTokens = [ # tokens that should prevent a line from being indented if they appear on
'def', # the same line.
'class', OpenTokens = {
'module', 'def' => 'end',
'[', 'class' => 'end',
'{', 'module' => 'end',
'do', 'do' => 'end',
'if', 'if' => 'end',
'while', 'while' => 'end',
'for' 'for' => 'end',
] '[' => ']',
'{' => '}',
}
# Collection of tokens that decrease the indentation level. # Collection of tokens that decrease the indentation level.
ClosingTokens = ['end', ']', '}'] ClosingTokens = ['end', ']', '}']
@ -81,7 +83,8 @@ class Pry
# @return [String] The indented version of +input+. # @return [String] The indented version of +input+.
# #
def indent(input) def indent(input)
output = '' output = ''
open_tokens = OpenTokens.keys
input.lines.each do |line| input.lines.each do |line|
# Remove manually added indentation. # Remove manually added indentation.
@ -100,7 +103,11 @@ class Pry
break break
# Start token found (such as "class"). Update the stack and indent the # Start token found (such as "class"). Update the stack and indent the
# current line. # 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 = '' add = ''
last = @stack[-1] last = @stack[-1]
@ -133,9 +140,60 @@ class Pry
return output.gsub!(/\s+$/, '') return output.gsub!(/\s+$/, '')
end 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. # @param [String] full_line The full line of input, including the prompt.
#
def correct_indentation(full_line) def correct_indentation(full_line)
# The whitespace is used to "clear" the current line so existing # The whitespace is used to "clear" the current line so existing
# characters don't show up. # characters don't show up.

View file

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