Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
283 lines (239 sloc) 7.53 KB
module Guard
# The Guard interactor is a Pry REPL with a Guard
# specific command set.
#
class Interactor
require 'pry'
require 'guard'
require 'guard/ui'
require 'guard/commands/all'
require 'guard/commands/change'
require 'guard/commands/notification'
require 'guard/commands/pause'
require 'guard/commands/reload'
require 'guard/commands/scope'
require 'guard/commands/show'
# The default Ruby script to configure Guard Pry if the option `:guard_rc` is not defined.
GUARD_RC = '~/.guardrc'
# The default Guard Pry history file if the option `:history_file` is not defined.
HISTORY_FILE = '~/.guard_history'
# List of shortcuts for each interactor command
SHORTCUTS = {
:help => 'h',
:all => 'a',
:reload => 'r',
:change => 'c',
:show => 's',
:scope => 'o',
:notification => 'n',
:pause => 'p',
:exit => 'e',
:quit => 'q'
}
class << self
# Get the interactor options
#
# @return [Hash] the options
#
def options
@options ||= { }
end
# Set the interactor options
#
# @param [Hash] options the interactor options
# @option options [String] :guard_rc the Ruby script to configure Guard Pry
# @option options [String] :history_file the file to write the Pry history to
#
def options=(options)
@options = options
end
# Is the interactor enabled?
#
# @return [Boolean] true if enabled
#
def enabled
@enabled.nil? ? true : @enabled
end
# Set the enabled status for the interactor
#
# @param [Boolean] status true if enabled
#
def enabled=(status)
@enabled = status
end
end
# Initialize the interactor. This configures
# Pry and creates some custom commands and aliases
# for Guard.
#
def initialize
return if ENV['GUARD_ENV'] == 'test'
Pry.config.should_load_rc = false
Pry.config.should_load_local_rc = false
Pry.config.history.file = self.class.options[:history_file] || HISTORY_FILE
load_guard_rc
create_run_all_command
create_command_aliases
create_guard_commands
create_group_commands
configure_prompt
end
# Loads the `~/.guardrc` file when pry has started.
#
def load_guard_rc
Pry.config.hooks.add_hook :when_started, :load_guard_rc do
load GUARD_RC if File.exist?(File.expand_path(self.class.options[:guard_rc] || GUARD_RC))
end
end
# Creates a command that triggers the `:run_all` action
# when the command is empty (just pressing enter on the
# beginning of a line).
#
def create_run_all_command
Pry.commands.block_command /^$/, 'Hit enter to run all' do
Pry.run_command 'all'
end
end
# Creates command aliases for the commands
# `help`, `reload`, `change`, `scope`, `notification`, `pause`, `exit` and `quit`,
# which will be the first letter of the command.
#
def create_command_aliases
SHORTCUTS.each do |command, shortcut|
Pry.commands.alias_command shortcut, command
end
end
# Create a shorthand command to run the `:run_all`
# action on a specific Guard plugin. For example,
# when guard-rspec is available, then a command
# `rspec` is created that runs `all rspec`.
#
def create_guard_commands
::Guard.guards.each do |guard|
name = guard.class.to_s.downcase.sub('guard::', '')
Pry.commands.create_command name, "Run all #{ name }" do
group 'Guard'
def process
Pry.run_command "all #{ match }"
end
end
end
end
# Create a shorthand command to run the `:run_all`
# action on a specific Guard group. For example,
# when you have a group `frontend`, then a command
# `frontend` is created that runs `all frontend`.
#
def create_group_commands
::Guard.groups.each do |group|
name = group.name.to_s
next if name == 'default'
Pry.commands.create_command name, "Run all #{ name }" do
group 'Guard'
def process
Pry.run_command "all #{ match }"
end
end
end
end
# Configure the pry prompt to see `guard` instead of
# `pry`.
#
def configure_prompt
Pry.config.prompt = [
proc do |target_self, nest_level, pry|
history = pry.input_array.size
process = ::Guard.listener.paused? ? 'pause' : 'guard'
clip = Pry.view_clip(target_self)
level = ":#{ nest_level }" unless nest_level.zero?
scope = if !::Guard.scope[:plugins].empty?
"{#{ ::Guard.scope[:plugins].join }} "
elsif !::Guard.scope[:groups].empty?
"{#{ ::Guard.scope[:groups].join }} "
else
''
end
"[#{ history }] #{ scope }#{ process }(#{ clip })#{ level }> "
end,
proc do |target_self, nest_level, pry|
history = pry.input_array.size
process = ::Guard.listener.paused? ? 'pause' : 'guard'
clip = Pry.view_clip(target_self)
level = ":#{ nest_level }" unless nest_level.zero?
scope = if !::Guard.scope[:plugins].empty?
"{#{ ::Guard.scope[:plugins].join }} "
elsif !::Guard.scope[:groups].empty?
"{#{ ::Guard.scope[:groups].join }} "
else
''
end
"[#{ history }] #{ scope }#{ process }(#{ clip })#{ level }* "
end
]
end
# Start the line reader in its own thread.
#
def start
return if ENV['GUARD_ENV'] == 'test'
store_terminal_settings if stty_exists?
if !@thread || !@thread.alive?
::Guard::UI.debug 'Start interactor'
@thread = Thread.new do
Pry.start
::Guard.stop
exit
end
end
end
# Kill interactor thread if not current
#
def stop
return if !@thread || ENV['GUARD_ENV'] == 'test'
unless Thread.current == @thread
::Guard::UI.reset_line
::Guard::UI.debug 'Stop interactor'
@thread.kill
end
restore_terminal_settings if stty_exists?
end
# Detects whether or not the stty command exists
# on the user machine.
#
# @return [Boolean] the status of stty
#
def stty_exists?
@stty_exists ||= system('hash', 'stty')
end
# Stores the terminal settings so we can resore them
# when stopping.
#
def store_terminal_settings
@stty_save = `stty -g 2>#{ DEV_NULL }`.chomp
end
# Restore terminal settings
#
def restore_terminal_settings
system("stty #{ @stty_save } 2>#{ DEV_NULL }") if @stty_save
end
# Converts and validates a plain text scope
# to a valid plugin or group scope.
#
# @param [Array<String>] entries the text scope
# @return [Hash, Array<String>] the plugin or group scope, the unknown entries
#
def self.convert_scope(entries)
scopes = { :plugins => [], :groups => [] }
unknown = []
entries.each do |entry|
if plugin = ::Guard.guards(entry)
scopes[:plugins] << plugin
elsif group = ::Guard.groups(entry)
scopes[:groups] << group
else
unknown << entry
end
end
[scopes, unknown]
end
end
end
Jump to Line
Something went wrong with that request. Please try again.