This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'forwardable'
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class Config
|
||||
extend Forwardable
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
heading_color: :bright_blue,
|
||||
public_method_color: :default,
|
||||
private_method_color: :blue,
|
||||
protected_method_color: :blue,
|
||||
method_missing_color: :bright_red,
|
||||
local_var_color: :yellow,
|
||||
pry_var_color: :default, # e.g. _, pry_instance, _file_
|
||||
instance_var_color: :blue, # e.g. @foo
|
||||
class_var_color: :bright_blue, # e.g. @@foo
|
||||
global_var_color: :default, # e.g. $CODERAY_DEBUG, $eventmachine_library
|
||||
builtin_global_color: :cyan, # e.g. $stdin, $-w, $PID
|
||||
pseudo_global_color: :cyan, # e.g. $~, $1..$9, $LAST_MATCH_INFO
|
||||
constant_color: :default, # e.g. VERSION, ARGF
|
||||
class_constant_color: :blue, # e.g. Object, Kernel
|
||||
exception_constant_color: :magenta, # e.g. Exception, RuntimeError
|
||||
unloaded_constant_color: :yellow, # Constant still in .autoload? state
|
||||
separator: " ",
|
||||
ceiling: [Object, Module, Class]
|
||||
}.freeze
|
||||
|
||||
DEFAULT_OPTIONS.each_key do |key|
|
||||
define_method key do
|
||||
@config[key]
|
||||
end
|
||||
|
||||
define_method "#{key}=" do |value|
|
||||
@config[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
def_delegators :@config, :[], :[]=, :each, :each_pair, :values, :keys, :to_a
|
||||
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def self.default
|
||||
new(DEFAULT_OPTIONS.dup)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class Constants < Pry::Command::Ls::Formatter
|
||||
DEPRECATED_CONSTANTS = %i[
|
||||
Data Fixnum Bignum TimeoutError NIL FALSE TRUE
|
||||
].tap do |constants|
|
||||
constants << :JavaPackageModuleTemplate if Helpers::Platform.jruby?
|
||||
end
|
||||
include Pry::Command::Ls::Interrogatable
|
||||
|
||||
def initialize(interrogatee, no_user_opts, opts, pry_instance)
|
||||
super(pry_instance)
|
||||
@interrogatee = interrogatee
|
||||
@no_user_opts = no_user_opts
|
||||
@default_switch = opts[:constants]
|
||||
@verbose_switch = opts[:verbose]
|
||||
@dconstants = opts.dconstants?
|
||||
end
|
||||
|
||||
def correct_opts?
|
||||
super || (@no_user_opts && interrogating_a_module?)
|
||||
end
|
||||
|
||||
def output_self
|
||||
mod = interrogatee_mod
|
||||
constants = WrappedModule.new(mod).constants(@verbose_switch)
|
||||
output_section('constants', grep.regexp[format(mod, constants)])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def show_deprecated_constants?
|
||||
@dconstants == true
|
||||
end
|
||||
|
||||
def format(mod, constants)
|
||||
constants.sort_by(&:downcase).map do |name|
|
||||
if Object.respond_to?(:deprecate_constant) &&
|
||||
DEPRECATED_CONSTANTS.include?(name) &&
|
||||
!show_deprecated_constants?
|
||||
next
|
||||
end
|
||||
|
||||
if (const = (begin
|
||||
!mod.autoload?(name) && (mod.const_get(name) || true)
|
||||
rescue StandardError
|
||||
nil
|
||||
end))
|
||||
if begin
|
||||
const < Exception
|
||||
rescue StandardError
|
||||
false
|
||||
end
|
||||
color(:exception_constant, name)
|
||||
elsif begin
|
||||
mod.const_get(name).is_a?(Module)
|
||||
rescue StandardError
|
||||
false
|
||||
end
|
||||
color(:class_constant, name)
|
||||
else
|
||||
color(:constant, name)
|
||||
end
|
||||
else
|
||||
color(:unloaded_constant, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class Formatter
|
||||
attr_writer :grep
|
||||
attr_reader :pry_instance
|
||||
|
||||
def initialize(pry_instance)
|
||||
@pry_instance = pry_instance
|
||||
@target = pry_instance.current_context
|
||||
@default_switch = nil
|
||||
end
|
||||
|
||||
def write_out
|
||||
return false unless correct_opts?
|
||||
|
||||
output_self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def color(type, str)
|
||||
Pry::Helpers::Text.send pry_instance.config.ls.send("#{type}_color"), str
|
||||
end
|
||||
|
||||
# Add a new section to the output.
|
||||
# Outputs nothing if the section would be empty.
|
||||
def output_section(heading, body)
|
||||
return '' if body.compact.empty?
|
||||
|
||||
fancy_heading = Pry::Helpers::Text.bold(color(:heading, heading))
|
||||
Pry::Helpers.tablify_or_one_line(fancy_heading, body, @pry_instance)
|
||||
end
|
||||
|
||||
def format_value(value)
|
||||
Pry::ColorPrinter.pp(value, ''.dup)
|
||||
end
|
||||
|
||||
def correct_opts?
|
||||
@default_switch
|
||||
end
|
||||
|
||||
def output_self
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def grep
|
||||
@grep || proc { |x| x }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class Globals < Pry::Command::Ls::Formatter
|
||||
# Taken from "puts global_variables.inspect".
|
||||
BUILTIN_GLOBALS =
|
||||
%w[$" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\
|
||||
$: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT
|
||||
$FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER
|
||||
$INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS
|
||||
$ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID
|
||||
$PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout].freeze
|
||||
|
||||
# `$SAFE` and `$?` are thread-local, the exception stuff only works in a
|
||||
# rescue clause, everything else is basically a local variable with a `$`
|
||||
# in its name.
|
||||
PSEUDO_GLOBALS =
|
||||
%w[$! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9
|
||||
$CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO
|
||||
$LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH].freeze
|
||||
|
||||
def initialize(opts, pry_instance)
|
||||
super(pry_instance)
|
||||
@default_switch = opts[:globals]
|
||||
end
|
||||
|
||||
def output_self
|
||||
variables = format(@target.eval('global_variables'))
|
||||
output_section('global variables', grep.regexp[variables])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format(globals)
|
||||
globals.map(&:to_s).sort_by(&:downcase).map do |name|
|
||||
if PSEUDO_GLOBALS.include?(name)
|
||||
color(:pseudo_global, name)
|
||||
elsif BUILTIN_GLOBALS.include?(name)
|
||||
color(:builtin_global, name)
|
||||
else
|
||||
color(:global_var, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class Grep
|
||||
def initialize(grep_regexp)
|
||||
@grep_regexp = grep_regexp
|
||||
end
|
||||
|
||||
def regexp
|
||||
proc { |x|
|
||||
if x.instance_of?(Array)
|
||||
x.grep(@grep_regexp)
|
||||
else
|
||||
x =~ @grep_regexp
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class InstanceVars < Pry::Command::Ls::Formatter
|
||||
include Pry::Command::Ls::Interrogatable
|
||||
|
||||
def initialize(interrogatee, no_user_opts, opts, pry_instance)
|
||||
super(pry_instance)
|
||||
@interrogatee = interrogatee
|
||||
@no_user_opts = no_user_opts
|
||||
@default_switch = opts[:ivars]
|
||||
end
|
||||
|
||||
def correct_opts?
|
||||
super || @no_user_opts
|
||||
end
|
||||
|
||||
def output_self
|
||||
ivars = if Object === @interrogatee # rubocop:disable Style/CaseEquality
|
||||
Pry::Method.safe_send(@interrogatee, :instance_variables)
|
||||
else
|
||||
[] # TODO: BasicObject support
|
||||
end
|
||||
kvars = Pry::Method.safe_send(interrogatee_mod, :class_variables)
|
||||
ivars_out = output_section('instance variables', format(:instance_var, ivars))
|
||||
kvars_out = output_section('class variables', format(:class_var, kvars))
|
||||
ivars_out + kvars_out
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format(type, vars)
|
||||
vars.sort_by { |var| var.to_s.downcase }.map { |var| color(type, var) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
module Interrogatable
|
||||
private
|
||||
|
||||
def interrogating_a_module?
|
||||
Module === @interrogatee # rubocop:disable Style/CaseEquality
|
||||
end
|
||||
|
||||
def interrogatee_mod
|
||||
if interrogating_a_module?
|
||||
@interrogatee
|
||||
else
|
||||
singleton = Pry::Method.singleton_class_of(@interrogatee)
|
||||
singleton.ancestors.grep(::Class).reject { |c| c == singleton }.first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
module JRubyHacks
|
||||
private
|
||||
|
||||
# JRuby creates lots of aliases for methods imported from java in an attempt
|
||||
# to make life easier for ruby programmers. (e.g. getFooBar becomes
|
||||
# get_foo_bar and foo_bar, and maybe foo_bar? if it returns a Boolean). The
|
||||
# full transformations are in the assignAliases method of:
|
||||
# https://github.com/jruby/jruby/blob/master/src/org/jruby/javasupport/JavaClass.java
|
||||
#
|
||||
# This has the unfortunate side-effect of making the output of ls even more
|
||||
# incredibly verbose than it normally would be for these objects; and so we
|
||||
# filter out all but the nicest of these aliases here.
|
||||
#
|
||||
# TODO: This is a little bit vague, better heuristics could be used.
|
||||
# JRuby also has a lot of scala-specific logic, which we don't copy.
|
||||
def trim_jruby_aliases(methods)
|
||||
grouped = methods.group_by do |m|
|
||||
m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase
|
||||
end
|
||||
|
||||
grouped.flat_map do |_key, values|
|
||||
values = values.sort_by do |m|
|
||||
rubbishness(m.name)
|
||||
end
|
||||
|
||||
found = []
|
||||
values.select do |x|
|
||||
(found.none? { |y| x == y }) && found << x
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# When removing jruby aliases, we want to keep the alias that is
|
||||
# "least rubbish" according to this metric.
|
||||
def rubbishness(name)
|
||||
name.each_char.map do |x|
|
||||
case x
|
||||
when /[A-Z]/
|
||||
1
|
||||
when '?', '=', '!'
|
||||
-2
|
||||
else
|
||||
0
|
||||
end
|
||||
end.inject(&:+) + (name.size / 100.0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class LocalNames < Pry::Command::Ls::Formatter
|
||||
def initialize(no_user_opts, args, pry_instance)
|
||||
super(pry_instance)
|
||||
@no_user_opts = no_user_opts
|
||||
@args = args
|
||||
@sticky_locals = pry_instance.sticky_locals
|
||||
end
|
||||
|
||||
def correct_opts?
|
||||
super || (@no_user_opts && @args.empty?)
|
||||
end
|
||||
|
||||
def output_self
|
||||
local_vars = grep.regexp[@target.eval('local_variables')]
|
||||
output_section('locals', format(local_vars))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format(locals)
|
||||
locals.sort_by(&:downcase).map do |name|
|
||||
if @sticky_locals.include?(name.to_sym)
|
||||
color(:pry_var, name)
|
||||
else
|
||||
color(:local_var, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,47 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class LocalVars < Pry::Command::Ls::Formatter
|
||||
def initialize(opts, pry_instance)
|
||||
super(pry_instance)
|
||||
@default_switch = opts[:locals]
|
||||
@sticky_locals = pry_instance.sticky_locals
|
||||
end
|
||||
|
||||
def output_self
|
||||
locals = @target.eval('local_variables').reject do |e|
|
||||
@sticky_locals.key?(e.to_sym)
|
||||
end
|
||||
name_value_pairs = locals.map do |name|
|
||||
[name, @target.eval(name.to_s)]
|
||||
end
|
||||
format(name_value_pairs).join('')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format(name_value_pairs)
|
||||
sorted = name_value_pairs.sort_by do |_name, value|
|
||||
value.to_s.size
|
||||
end
|
||||
sorted.reverse.map do |name, value|
|
||||
colorized_assignment_style(name, format_value(value))
|
||||
end
|
||||
end
|
||||
|
||||
def colorized_assignment_style(lhs, rhs, desired_width = 7)
|
||||
colorized_lhs = color(:local_var, lhs)
|
||||
color_escape_padding = colorized_lhs.size - lhs.size
|
||||
pad = desired_width + color_escape_padding
|
||||
Kernel.format(
|
||||
"%-#{pad}<name>s = %<value>s",
|
||||
name: color(:local_var, colorized_lhs),
|
||||
value: rhs
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class LsEntity
|
||||
attr_reader :pry_instance
|
||||
|
||||
def initialize(opts)
|
||||
@interrogatee = opts[:interrogatee]
|
||||
@no_user_opts = opts[:no_user_opts]
|
||||
@opts = opts[:opts]
|
||||
@args = opts[:args]
|
||||
@grep = Grep.new(Regexp.new(opts[:opts][:G] || '.'))
|
||||
@pry_instance = opts.delete(:pry_instance)
|
||||
end
|
||||
|
||||
def entities_table
|
||||
entities.map(&:write_out).select { |o| o }.join('')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def grep(entity)
|
||||
entity.tap { |o| o.grep = @grep }
|
||||
end
|
||||
|
||||
def globals
|
||||
grep Globals.new(@opts, pry_instance)
|
||||
end
|
||||
|
||||
def constants
|
||||
grep Constants.new(@interrogatee, @no_user_opts, @opts, pry_instance)
|
||||
end
|
||||
|
||||
def methods
|
||||
grep(Methods.new(@interrogatee, @no_user_opts, @opts, pry_instance))
|
||||
end
|
||||
|
||||
def self_methods
|
||||
grep SelfMethods.new(@interrogatee, @no_user_opts, @opts, pry_instance)
|
||||
end
|
||||
|
||||
def instance_vars
|
||||
grep InstanceVars.new(@interrogatee, @no_user_opts, @opts, pry_instance)
|
||||
end
|
||||
|
||||
def local_names
|
||||
grep LocalNames.new(@no_user_opts, @args, pry_instance)
|
||||
end
|
||||
|
||||
def local_vars
|
||||
LocalVars.new(@opts, pry_instance)
|
||||
end
|
||||
|
||||
def entities
|
||||
[
|
||||
globals, constants, methods, self_methods, instance_vars, local_names,
|
||||
local_vars
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class Methods < Pry::Command::Ls::Formatter
|
||||
include Pry::Command::Ls::Interrogatable
|
||||
include Pry::Command::Ls::MethodsHelper
|
||||
|
||||
def initialize(interrogatee, no_user_opts, opts, pry_instance)
|
||||
super(pry_instance)
|
||||
@interrogatee = interrogatee
|
||||
@no_user_opts = no_user_opts
|
||||
@default_switch = opts[:methods]
|
||||
@instance_methods_switch = opts['instance-methods']
|
||||
@ppp_switch = opts[:ppp]
|
||||
@jruby_switch = opts['all-java']
|
||||
@quiet_switch = opts[:quiet]
|
||||
@verbose_switch = opts[:verbose]
|
||||
end
|
||||
|
||||
def output_self
|
||||
methods = all_methods.group_by(&:owner)
|
||||
# Reverse the resolution order so that the most useful information
|
||||
# appears right by the prompt.
|
||||
resolution_order.take_while(&below_ceiling).reverse.map do |klass|
|
||||
methods_here = (methods[klass] || []).select { |m| grep.regexp[m.name] }
|
||||
heading = "#{Pry::WrappedModule.new(klass).method_prefix}methods"
|
||||
output_section(heading, format(methods_here))
|
||||
end.join('')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def correct_opts?
|
||||
super || @instance_methods_switch || @ppp_switch || @no_user_opts
|
||||
end
|
||||
|
||||
# Get a lambda that can be used with `take_while` to prevent over-eager
|
||||
# traversal of the Object's ancestry graph.
|
||||
def below_ceiling
|
||||
ceiling = if @quiet_switch
|
||||
[Pry::Method.safe_send(interrogatee_mod, :ancestors)[1]] +
|
||||
pry_instance.config.ls.ceiling
|
||||
elsif @verbose_switch
|
||||
[]
|
||||
else
|
||||
pry_instance.config.ls.ceiling.dup
|
||||
end
|
||||
->(klass) { !ceiling.include?(klass) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
module MethodsHelper
|
||||
include Pry::Command::Ls::JRubyHacks
|
||||
|
||||
private
|
||||
|
||||
# Get all the methods that we'll want to output.
|
||||
def all_methods(instance_methods = false)
|
||||
methods = if instance_methods || @instance_methods_switch
|
||||
Pry::Method.all_from_class(@interrogatee)
|
||||
else
|
||||
Pry::Method.all_from_obj(@interrogatee)
|
||||
end
|
||||
|
||||
if Pry::Helpers::Platform.jruby? && !@jruby_switch
|
||||
methods = trim_jruby_aliases(methods)
|
||||
end
|
||||
|
||||
methods.select { |method| @ppp_switch || method.visibility == :public }
|
||||
end
|
||||
|
||||
def resolution_order
|
||||
if @instance_methods_switch
|
||||
Pry::Method.instance_resolution_order(@interrogatee)
|
||||
else
|
||||
Pry::Method.resolution_order(@interrogatee)
|
||||
end
|
||||
end
|
||||
|
||||
def format(methods)
|
||||
methods.sort_by(&:name).map do |method|
|
||||
if method.name == 'method_missing'
|
||||
color(:method_missing, 'method_missing')
|
||||
elsif method.visibility == :private
|
||||
color(:private_method, method.name)
|
||||
elsif method.visibility == :protected
|
||||
color(:protected_method, method.name)
|
||||
else
|
||||
color(:public_method, method.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
class Command
|
||||
class Ls < Pry::ClassCommand
|
||||
class SelfMethods < Pry::Command::Ls::Formatter
|
||||
include Pry::Command::Ls::Interrogatable
|
||||
include Pry::Command::Ls::MethodsHelper
|
||||
|
||||
def initialize(interrogatee, no_user_opts, opts, pry_instance)
|
||||
super(pry_instance)
|
||||
@interrogatee = interrogatee
|
||||
@no_user_opts = no_user_opts
|
||||
@ppp_switch = opts[:ppp]
|
||||
@jruby_switch = opts['all-java']
|
||||
end
|
||||
|
||||
def output_self
|
||||
methods = all_methods(true).select do |m|
|
||||
m.owner == @interrogatee && grep.regexp[m.name]
|
||||
end
|
||||
heading = "#{Pry::WrappedModule.new(@interrogatee).method_prefix}methods"
|
||||
output_section(heading, format(methods))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def correct_opts?
|
||||
@no_user_opts && interrogating_a_module?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user