From e9c2f383eb6a9e3a4134f51f60ddf9c29d6ce165 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 5 Oct 2011 19:04:44 +0200 Subject: [PATCH] Pry now indents code similar to IRB. Code is indented using the class Pry::Indent. This class uses an internal stack that contains the indentation levels for each line of code. Certain keywords such as "class" or "def" will add data to this stack so that the next line is indented, other keywords such as "end" or "}" will remove data from the stack, causing the next line to be un-indented. Pry::Indent is hooked into Pry#retrieve_line as well as Pry#readline. This means that both input strings as well as the ones displayed by "show-method" are indented. Sadly due to the way Readline works input strings are indented similar to IRB. This means that instead of the following: > class User > def initialize > end > end You'll get the following: > class User > def initialize > end > end While annoying there doesn't seem to be a way to work around this issue. Luckily the "show-method" command indents your code properly. By default indentation is turned on. This can be turned off (or back on) using the configuration item Pry.config.indent. However, if you turn this option off after a method is defined "show-method" will still show it with indentation as indentation happens on input rather than only when code is displayed. For more information see Pry::Indent#indent in lib/pry/indent.rb. Signed-off-by: Yorick Peterse --- .gitignore | 5 ++ lib/pry.rb | 1 + lib/pry/config.rb | 4 ++ lib/pry/indent.rb | 136 ++++++++++++++++++++++++++++++++++++++++ lib/pry/pry_class.rb | 1 + lib/pry/pry_instance.rb | 27 +++++--- test/helper.rb | 5 +- test/test_indent.rb | 127 +++++++++++++++++++++++++++++++++++++ 8 files changed, 297 insertions(+), 9 deletions(-) create mode 100644 lib/pry/indent.rb create mode 100644 test/test_indent.rb diff --git a/.gitignore b/.gitignore index d0b8649e..b4777dcf 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,8 @@ pkg/ coverage/ .yardoc/ /tags + +# Not sure if Pry devs use RVM or want the Gemfile.lock in the repo, thus I'm +# ignoring them for now. +.rvmrc +Gemfile.lock diff --git a/lib/pry.rb b/lib/pry.rb index d364b110..fcd6ef7d 100644 --- a/lib/pry.rb +++ b/lib/pry.rb @@ -191,3 +191,4 @@ require "pry/plugins" require "pry/core_extensions" require "pry/pry_class" require "pry/pry_instance" +require "pry/indent" diff --git a/lib/pry/config.rb b/lib/pry/config.rb index 37e236de..f5999214 100644 --- a/lib/pry/config.rb +++ b/lib/pry/config.rb @@ -126,6 +126,10 @@ class Pry # The proc is passed the pry output object, the command string # to eval, and a reference to the pry instance attr_accessor :system + + # @return [TrueClass|FalseClass] Whether or not code should be indented + # using Pry::Indent. + attr_accessor :indent end end diff --git a/lib/pry/indent.rb b/lib/pry/indent.rb new file mode 100644 index 00000000..2d33508a --- /dev/null +++ b/lib/pry/indent.rb @@ -0,0 +1,136 @@ +require 'coderay' + +class Pry + ## + # Pry::Indent is a class that can be used to indent a number of lines + # containing Ruby code similar as to how IRB does it (but better). The class + # works by tokenizing a string using CodeRay and then looping over those + # tokens. Based on the tokens in a line of code that line (or the next one) + # will be indented or un-indented by 2 spaces. + # + # @author Yorick Peterse + # @since 04-10-2011 + # + class Indent + # Array containing all the indentation levels. + attr_reader :stack + + # The amount of spaces to insert for each indent level. + Spaces = ' '.freeze + + # Array containing all the tokens that should increase the indentation + # level. + OpenTokens = [ + 'def', + 'class', + 'module', + '[', + '{', + 'do', + 'if', + 'while', + 'for' + ] + + # Collection of tokens that decrease the indentation level. + ClosingTokens = ['end', ']', '}'] + + # Collection of token types that should be ignored. Without this list + # keywords such as "class" inside strings would cause the code to be + # indented incorrectly. + IgnoreTokens = [:space, :content, :string, :delimiter] + + # Collection of tokens that should only increase the indentation level of + # the next line. + OpenTokensNext = ['else', 'elsif'] + + ## + # Creates a new instance of the class and starts with a fresh stack. The + # stack is used to keep track of the indentation level for each line of + # code. + # + # @author Yorick Peterse + # @since 05-10-2011 + # + def initialize + @stack = [] + end + + ## + # Indents a string and returns it. This string can either be a single line + # or multiple ones. + # + # @example + # str = <