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:
parent
971223d94a
commit
e2db26574f
2 changed files with 113 additions and 30 deletions
|
@ -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', ']', '}']
|
||||||
|
@ -82,6 +84,7 @@ class Pry
|
||||||
#
|
#
|
||||||
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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue