summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert <r-obert@users.noreply.github.com>2017-09-01 01:23:17 +0100
committerGitHub <noreply@github.com>2017-09-01 01:23:17 +0100
commit17e21dcd6a40b783cae7e2fdbe0b68389d5d9df7 (patch)
tree66c0f5fb8c26c63753c73c70e11f6a94b376ce54
parent9bc74de9a7434f6361627c8975ac3b53161eb167 (diff)
parent3cc7f056df3ceae9fbf705f29d48c2f89591eaad (diff)
downloadpry-include-text-module.tar.gz
Merge branch 'master' into include-text-moduleinclude-text-module
-rw-r--r--.travis.yml4
-rw-r--r--CHANGELOG.md8
-rw-r--r--Gemfile2
-rw-r--r--lib/pry/commands/alias_prompt.rb33
-rw-r--r--lib/pry/commands/change_prompt.rb23
-rw-r--r--lib/pry/commands/list_prompts.rb28
-rw-r--r--lib/pry/config/default.rb2
-rw-r--r--lib/pry/prompt.rb208
-rw-r--r--lib/pry/pry_instance.rb8
-rw-r--r--lib/pry/wrapped_module/candidate.rb10
-rw-r--r--spec/prompt_spec.rb84
-rw-r--r--spec/wrapped_module_spec.rb13
12 files changed, 369 insertions, 54 deletions
diff --git a/.travis.yml b/.travis.yml
index aaba3066..67ff931b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,10 @@
rvm:
- - 1.9.2
- 1.9.3
- 2.0.0
- 2.1
- 2.2
- 2.3.1
- - 2.4
+ - 2.4.1
- ruby-head
- rbx-2
- jruby
@@ -21,6 +20,7 @@ matrix:
- rvm: ruby-head
- rvm: jruby-head
- rvm: rbx-2
+ - rvm: 2.4.1 # because of https://bugs.ruby-lang.org/issues/13537
notifications:
irc: "irc.freenode.org#pry"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 427c3fd0..fd8ac564 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,16 @@
### HEAD
* Include Pry::Helpers::Text into Pry::Command, and deprecate `Pry::Command#text`. ([#1629](https://github.com/pry/pry/pull/1629))
+* Add 'alias-prompt' command.
+
+* Add `Pry::Prompt.[]`, `Pry::Prompt.add_prompt()`, `Pry::Prompt.alias_prompt()` and
+ `Pry::Prompt.remove_prompt()`, plus more, for integrating custom prompts with Pry
+ ([#1628](https://github.com/pry/pry/pull/1628)). See Pry::Prompt for complete details.
+
* Add text helpers for background colors ([#1624](https://github.com/pry/pry/pull/1624))
* Fix string literal methods completion. ([#1590](https://github.com/pry/pry/pull/1590))
+* Make sure Pry::WrappedModule::Candidate#source_location returns non-nil value when `.name` has
+ been redefined on a Class/Module ([#1623](https://github.com/pry/pry/pull/1623))
* Add alias 'whereami[?!]+' for 'whereami' command. ([#1597](https://github.com/pry/pry/pull/1597))
* Improve Ruby 2.4 support ([#1611](https://github.com/pry/pry/pull/1611)):
* Deprecated constants are hidden from `ls` output by default, use the `-d` switch to see them.
diff --git a/Gemfile b/Gemfile
index 43c0d13f..7c199855 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,11 +2,9 @@ source 'https://rubygems.org'
gemspec
gem 'rake', '~> 10.0'
-# For Guard
group :development do
gem 'gist'
gem 'yard'
- gem 'rb-inotify', :require => false
gem 'rb-fsevent', :require => false
end
diff --git a/lib/pry/commands/alias_prompt.rb b/lib/pry/commands/alias_prompt.rb
new file mode 100644
index 00000000..a5f7a5ab
--- /dev/null
+++ b/lib/pry/commands/alias_prompt.rb
@@ -0,0 +1,33 @@
+class Pry
+ class Command::AliasPrompt < Pry::ClassCommand
+ match "alias-prompt"
+ group 'Input and Output'
+ description "Create an alternative alias for a prompt"
+ banner <<-BANNER
+ alias-prompt PROMPT_NAME ALIAS_PROMPT
+
+ Create an alternative alias for a prompt that can be seen from list-prompts and
+ used by the change-prompt commands.
+ BANNER
+
+ command_options argument_required: true
+
+ def process(prompt_name, alias_name)
+ if not args_ok?([prompt_name, alias_name])
+ return output.puts help
+ end
+ if Pry::Prompt[prompt_name]
+ Pry::Prompt.alias_prompt prompt_name, alias_name
+ output.puts "Alias '#{alias_name}' created"
+ else
+ raise Pry::CommandError, "prompt #{prompt_name} cannot be aliased because it doesn't exist."
+ end
+ end
+
+ private
+ def args_ok?(args)
+ args.size == 2 and args.all?{|s| String === s}
+ end
+ Pry::Commands.add_command(self)
+ end
+end
diff --git a/lib/pry/commands/change_prompt.rb b/lib/pry/commands/change_prompt.rb
index 13d36cbb..859825a2 100644
--- a/lib/pry/commands/change_prompt.rb
+++ b/lib/pry/commands/change_prompt.rb
@@ -11,16 +11,29 @@ class Pry::Command::ChangePrompt < Pry::ClassCommand
BANNER
def process(prompt)
- if prompt_map.key?(prompt)
- _pry_.prompt = prompt_map[prompt][:value]
+ prompts = Pry::Prompt.all_by_name(prompt)
+ if prompts.size == 1
+ _pry_.prompt = prompts[0]
+ elsif prompts.size > 1
+ multiple_choice(prompts)
else
raise Pry::CommandError, "'#{prompt}' isn't a known prompt!"
end
end
-private
- def prompt_map
- Pry::Prompt::MAP
+ private
+ def multiple_choice(prompts)
+ _pry_.pager.page "Multiple aliases found, please choose:\n"
+ prompts.each.with_index(1) do |prompt, i|
+ _pry_.pager.page "#{i}) #{prompt.name} => #{prompt.alias_for}"
+ end
+ output.write "Choice: "
+ reply = (_pry_.input.respond_to?(:gets) ? _pry_.input.gets : _pry_.input.readline).strip
+ if reply =~ /^[1-9][0-9]*$/ and reply.to_i <= prompts.size
+ _pry_.prompt = prompts[reply.to_i-1]
+ else
+ multiple_choice(prompts)
+ end
end
Pry::Commands.add_command(self)
end
diff --git a/lib/pry/commands/list_prompts.rb b/lib/pry/commands/list_prompts.rb
index 436f719b..b8c2b29b 100644
--- a/lib/pry/commands/list_prompts.rb
+++ b/lib/pry/commands/list_prompts.rb
@@ -10,26 +10,28 @@ class Pry::Command::ListPrompts < Pry::ClassCommand
BANNER
def process
- output.puts heading("Available prompts") + "\n"
- prompt_map.each do |name, prompt|
- output.write "Name: #{text.bold(name)}"
- output.puts selected_prompt?(prompt) ? selected_text : ""
- output.puts prompt[:description]
+ output.puts heading("Available prompts") + "\n\n"
+ all_prompts.each do |prompt|
+ next if prompt.alias?
+ aliases = Pry::Prompt.aliases_for(prompt.name)
+ output.write "Name: #{text.bold(prompt.name)}"
+ output.puts selected_prompt?([prompt].concat(aliases)) ? text.green(" [active]") : ""
+ output.puts "Aliases: #{aliases.map {|s| text.bold(s.name) }.join(',')}" if aliases.any?
+ output.puts prompt.description
output.puts
end
end
-private
- def prompt_map
- Pry::Prompt::MAP
- end
+ private
- def selected_text
- text.red " (selected) "
+ def all_prompts
+ Pry::Prompt.all_prompts
end
- def selected_prompt?(prompt)
- _pry_.prompt == prompt[:value]
+ def selected_prompt?(prompts)
+ prompts.any? do |prompt|
+ _pry_.prompt == prompt or _pry_.prompt == prompt.proc_array
+ end
end
Pry::Commands.add_command(self)
end
diff --git a/lib/pry/config/default.rb b/lib/pry/config/default.rb
index e4ae3fb6..1a653520 100644
--- a/lib/pry/config/default.rb
+++ b/lib/pry/config/default.rb
@@ -16,7 +16,7 @@ class Pry::Config::Default
Pry::DEFAULT_PROMPT_NAME
},
prompt: proc {
- Pry::DEFAULT_PROMPT
+ Pry::Prompt['default']
},
prompt_safe_objects: proc {
Pry::DEFAULT_PROMPT_SAFE_OBJECTS
diff --git a/lib/pry/prompt.rb b/lib/pry/prompt.rb
index 62f0bc1f..ea71b346 100644
--- a/lib/pry/prompt.rb
+++ b/lib/pry/prompt.rb
@@ -1,26 +1,184 @@
-class Pry::Prompt
- MAP = {
- "default" => {
- value: Pry::DEFAULT_PROMPT,
- description: "The default Pry prompt. Includes information about the\n" \
- "current expression number, evaluation context, and nesting\n" \
- "level, plus a reminder that you're using Pry."
- },
-
- "simple" => {
- value: Pry::SIMPLE_PROMPT,
- description: "A simple '>>'."
- },
-
- "nav" => {
- value: Pry::NAV_PROMPT,
- description: "A prompt that displays the binding stack as a path and\n" \
- "includes information about _in_ and _out_."
- },
-
- "none" => {
- value: Pry::NO_PROMPT,
- description: "Wave goodbye to the Pry prompt."
- }
- }
+require 'set'
+module Pry::Prompt
+ extend self
+ PROMPT_MAP = {}
+ private_constant :PROMPT_MAP
+ AliasError = Class.new(RuntimeError)
+ PromptInfo = Struct.new(:name, :description, :proc_array, :alias_for) do
+ #
+ # @return [Boolean]
+ # Returns true if the prompt is an alias of another prompt.
+ #
+ def alias?
+ alias_for != nil
+ end
+
+ def <=>(other)
+ name == other.alias_for ? 1 : 0
+ end
+
+ def to_a
+ proc_array
+ end
+
+ def eql?(other)
+ return false if not Pry::Prompt::PromptInfo === other
+ # Aliases are eql?
+ [:proc_array].all? {|m| public_send(m) == other.public_send(m) }
+ end
+ end
+
+ #
+ # @return [Array<PromptInfo>]
+ # Returns an Array of {PromptInfo} objects.
+ #
+ def all_prompts
+ PROMPT_MAP.values.map{|s| s.to_a}.flatten
+ end
+
+ #
+ # @param [String] prompt
+ # The name of a prompt.
+ #
+ # @return [Array<PromptInfo>]
+ # Returns an array of aliases for _prompt_, as {PromptInfo} objects.
+ #
+ def aliases_for(prompt)
+ all_prompts.select{|prompt_info| prompt_info.alias_for == prompt.to_s}
+ end
+
+ #
+ # @return [Array<PromptInfo>]
+ # Returns an array of all prompt aliases, as {PromptInfo} objects.
+ #
+ def aliased_prompts
+ all_prompts.select(&:alias?)
+ end
+
+ #
+ # @param [Array<Proc,Proc>] proc_array
+ # An array in the form of [proc{}, proc{}]
+ #
+ # @return [PromptInfo]
+ # Returns the first {PromptInfo} object who holds value eql? to `proc_array`.
+ #
+ def first_matching_proc_array(proc_array)
+ all_prompts.find do |prompt|
+ prompt.proc_array == proc_array and not prompt.alias?
+ end
+ end
+
+ #
+ # Integrate a custom prompt with Pry.
+ # The prompt will be visible in the output of the "list-prompts" command, and
+ # it can be used with the "change-prompt"command.
+ #
+ # @param [String] name
+ # The name of the prompt.
+ #
+ # @param [String] description
+ # A short description of the prompt.
+ #
+ # @param [Array<Proc,Proc>] value
+ # A prompt in the form of [proc {}, proc {}].
+ #
+ def add_prompt(name, description, value)
+ PROMPT_MAP[name.to_s] = SortedSet.new [PromptInfo.new(name.to_s, description.to_s, value, nil)]
+ end
+
+ #
+ # @example
+ #
+ # # .pryrc
+ # Pry.configure do |config|
+ # config.prompt = Pry::Prompt['simple'].proc_array
+ # end
+ #
+ # @return [PromptInfo]
+ # Returns a prompt in the form of a PromptInfo object.
+ #
+ def [](name)
+ all_prompts.find {|prompt| prompt.name == name.to_s }
+ end
+
+ #
+ # @param [String] name
+ # The name of a prompt.
+ #
+ # @return [Array<PromptInfo>]
+ # An array of {PromptInfo} objects.
+ #
+ def all_by_name(name)
+ name = name.to_s
+ all_prompts.select{|prompt| prompt.name == name}
+ end
+
+ #
+ # Remove a prompt from Pry.
+ # It will no longer be visible in the output of "list-prompts" or usable with the
+ # "change-prompt" command.
+ #
+ # @note
+ # Aliases are also removed.
+ #
+ # @param [String] name
+ # The name of a prompt.
+ #
+ # @return [Object, nil]
+ # Returns truthy if a prompt was deleted, otherwise nil.
+ #
+ def remove_prompt(name)
+ prompt = self[name.to_s]
+ PROMPT_MAP.delete name.to_s if prompt
+ end
+
+ #
+ # Provide alternative name for a prompt, which can be used from the list-prompts
+ # and change-prompt commands.
+ #
+ # @param [String] prompt_name
+ # The name of the prompt to alias.
+ #
+ # @param [String] aliased_prompt
+ # The name of the aliased prompt.
+ #
+ def alias_prompt(prompt_name, aliased_prompt)
+ prompt_name = prompt_name.to_s
+ prompt = self[prompt_name]
+ if not prompt
+ raise AliasError, "prompt '#{prompt}' cannot be aliased because it doesn't exist"
+ elsif prompt.alias?
+ prompt_name = prompt.alias_for
+ end
+ PROMPT_MAP[prompt_name].add PromptInfo.new *[aliased_prompt.to_s, prompt.description,
+ prompt.proc_array, prompt_name]
+ end
+
+ #
+ # @param [String] alias_name
+ # Name of a prompt alias.
+ #
+ # @return [Integer]
+ # Returns the number of removed aliases.
+ #
+ def remove_alias(alias_name)
+ alias_name = alias_name.to_s
+ all_prompts.count do |prompt|
+ PROMPT_MAP[prompt.alias_for].delete(prompt) if prompt.alias? and prompt.name == alias_name
+ end
+ end
+
+ add_prompt "default",
+ "The default Pry prompt. Includes information about the\n" \
+ "current expression number, evaluation context, and nesting\n" \
+ "level, plus a reminder that you're using Pry.",
+ Pry::DEFAULT_PROMPT
+
+ add_prompt "nav",
+ "A prompt that displays the binding stack as a path and\n" \
+ "includes information about _in_ and _out_.",
+ Pry::NAV_PROMPT
+
+ add_prompt "simple", "A simple '>>'.", Pry::SIMPLE_PROMPT
+ add_prompt "none", "Wave goodbye to the Pry prompt.", Pry::NO_PROMPT
end
diff --git a/lib/pry/pry_instance.rb b/lib/pry/pry_instance.rb
index 0ee7b790..4ffe4c58 100644
--- a/lib/pry/pry_instance.rb
+++ b/lib/pry/pry_instance.rb
@@ -88,14 +88,16 @@ class Pry
# self.prompt = Pry::SIMPLE_PROMPT
# self.prompt # => Pry::SIMPLE_PROMPT
#
- # @return [Array<Proc>] Current prompt.
+ # @return [Pry::Prompt::PromptInfo, Array<Proc, Proc>] Current prompt.
def prompt
- prompt_stack.last
+ proc_array = prompt_stack.last
+ Pry::Prompt.first_matching_proc_array(proc_array) or proc_array
end
def prompt=(new_prompt)
+ new_prompt = new_prompt.to_a
if prompt_stack.empty?
- push_prompt new_prompt
+ push_prompt(new_prompt)
else
prompt_stack[-1] = new_prompt
end
diff --git a/lib/pry/wrapped_module/candidate.rb b/lib/pry/wrapped_module/candidate.rb
index 67ef1925..3481b2d0 100644
--- a/lib/pry/wrapped_module/candidate.rb
+++ b/lib/pry/wrapped_module/candidate.rb
@@ -97,9 +97,13 @@ class Pry
def class_regexes
mod_type_string = wrapped.class.to_s.downcase
- [/^\s*#{mod_type_string}\s+(?:(?:\w*)::)*?#{wrapped.name.split(/::/).last}/,
- /^\s*(::)?#{wrapped.name.split(/::/).last}\s*?=\s*?#{wrapped.class}/,
- /^\s*(::)?#{wrapped.name.split(/::/).last}\.(class|instance)_eval/]
+
+ # workaround for things that redefine Module.name - yes, they're out there.
+ wrapped_name = Pry::Helpers::BaseHelpers.safe_send wrapped, :name
+
+ [/^\s*#{mod_type_string}\s+(?:(?:\w*)::)*?#{wrapped_name.split(/::/).last}/,
+ /^\s*(::)?#{wrapped_name.split(/::/).last}\s*?=\s*?#{wrapped.class}/,
+ /^\s*(::)?#{wrapped_name.split(/::/).last}\.(class|instance)_eval/]
end
# This method is used by `Candidate#source_location` as a
diff --git a/spec/prompt_spec.rb b/spec/prompt_spec.rb
index fdd087f0..55692703 100644
--- a/spec/prompt_spec.rb
+++ b/spec/prompt_spec.rb
@@ -1,5 +1,89 @@
require_relative 'helper'
+RSpec.describe Pry::Prompt do
+ let(:prompt_value) do
+ [proc{},proc{}]
+ end
+
+ let(:prompt_desc) do
+ "$my custom prompt description$"
+ end
+
+ before do
+ described_class.add_prompt "prompt-name", prompt_desc, prompt_value
+ end
+
+ after do
+ described_class.remove_prompt "prompt-name"
+ end
+
+ describe ".add_prompt" do
+ specify "adds new Prompt" do
+ expect(described_class['prompt-name']).to be_instance_of(Pry::Prompt::PromptInfo)
+ end
+
+ specify "prompt appears in list-prompts" do
+ expect(pry_eval("list-prompts")).to include("prompt-name")
+ expect(pry_eval("list-prompts")).to include(prompt_desc)
+ end
+
+ specify "prompt is changed to via change-prompt" do
+ expect(pry_eval("change-prompt prompt-name", "_pry_.prompt")).to eq(described_class['prompt-name'])
+ end
+ end
+
+ describe ".remove_prompt" do
+ specify "removes a prompt" do
+ described_class.remove_prompt 'prompt-name'
+ expect(described_class['prompt-name']).to eq(nil)
+ end
+
+ specify "prompt disappears from list-prompts" do
+ described_class.remove_prompt 'prompt-name'
+ expect(pry_eval("list-prompts")).to_not include("prompt-name")
+ end
+
+ specify "does not remove aliases" do
+ described_class.alias_prompt "prompt-name", "prompt-alias"
+ described_class.remove_prompt 'prompt-alias'
+ expect(described_class['prompt-alias']).to_not be_nil
+ end
+ end
+
+ describe ".alias_prompt" do
+ before do
+ described_class.alias_prompt "prompt-name", "prompt-alias"
+ end
+
+ specify "creates alias" do
+ expect(described_class.aliases_for("prompt-name")).to eq([described_class['prompt-alias']])
+ end
+
+ specify "alias appears in list-prompts" do
+ expect(pry_eval("list-prompts")).to include("Aliases: prompt-alias")
+ end
+
+ specify "alias is changed to via change-prompt" do
+ expect(pry_eval("change-prompt prompt-alias", "_pry_.prompt")).to eq(described_class['prompt-name'])
+ end
+ end
+
+ describe ".remove_alias" do
+ before do
+ described_class.alias_prompt "prompt-name", "prompt-alias"
+ end
+
+ specify "returns number of removed aliases" do
+ expect(described_class.remove_alias("prompt-alias")).to eq(1)
+ end
+
+ specify "removes alias from Pry::Prompt" do
+ expect(described_class.remove_alias("prompt-alias")).to eq(1)
+ expect(described_class['prompt-alias']).to eq(nil)
+ end
+ end
+end
+
describe "Prompts" do
describe "one-parameter prompt proc" do
it 'should get full config object' do
diff --git a/spec/wrapped_module_spec.rb b/spec/wrapped_module_spec.rb
index b46d9cac..2482fd28 100644
--- a/spec/wrapped_module_spec.rb
+++ b/spec/wrapped_module_spec.rb
@@ -35,6 +35,19 @@ describe Pry::WrappedModule do
end
end
end
+
+ module ModuleNameOverride
+ def self.name; 'Not a good idea, but it happens in the wild.' end
+ end
+ end
+
+ describe 'overidden Module#name' do
+ specify '#source_location does not return nil' do
+ c = Pry::WrappedModule(Host::ModuleNameOverride).candidate(0)
+ expect(c.wrapped).to eq(Host::ModuleNameOverride)
+ expect(c.nonblank_name).to eq(Host::ModuleNameOverride.name)
+ expect(c.source_location).not_to be_nil
+ end
end
describe "number_of_candidates" do