Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion etc/completion.bash
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ _crystal()
_crystal_compgen_options "${opts}" "${cur}"
else
if [[ "${prev}" == "tool" ]] ; then
local subcommands="context dependencies format hierarchy implementations types"
local subcommands="context dependencies flags format hierarchy implementations types"
_crystal_compgen_options "${subcommands}" "${cur}"
else
_crystal_compgen_sources "${cur}"
Expand Down
4 changes: 3 additions & 1 deletion etc/completion.fish
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
set -l crystal_commands init build clear_cache docs env eval i interactive play run spec tool help version
set -l tool_subcommands context expand format hierarchy implementations types
set -l tool_subcommands context expand flags format hierarchy implementations types

complete -c crystal -s h -l help -d "Show help" -x

Expand Down Expand Up @@ -173,6 +173,8 @@ complete -c crystal -n "__fish_seen_subcommand_from expand" -s p -l progress -d
complete -c crystal -n "__fish_seen_subcommand_from expand" -s t -l time -d "Enable execution time output"
complete -c crystal -n "__fish_seen_subcommand_from expand" -l stdin-filename -d "Source file name to be read from STDIN"

complete -c crystal -n "__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands" -a "flags" -d "print all macro 'flag?' values" -x

complete -c crystal -n "__fish_seen_subcommand_from tool; and not __fish_seen_subcommand_from $tool_subcommands" -a "format" -d "format project, directories and/or files" -x
complete -c crystal -n "__fish_seen_subcommand_from format" -l check -d "Checks that formatting code produces no changes"
complete -c crystal -n "__fish_seen_subcommand_from format" -s i -l include -d "Include path"
Expand Down
7 changes: 7 additions & 0 deletions etc/completion.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ _crystal-tool() {
"context:show context for given location"
"dependencies:show tree of required source files"
"expand:show macro expansion for given location"
"flags:print all macro 'flag?' values"
"format:format project, directories and/or files"
"hierarchy:show type hierarchy"
"implementations:show implementations for given call in location"
Expand Down Expand Up @@ -211,6 +212,12 @@ _crystal-tool() {
$cursor_args
;;

(flags)
_arguments \
$programfile \
$help_args
;;

(format)
_arguments \
$programfile \
Expand Down
4 changes: 3 additions & 1 deletion man/crystal.1
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ Disable colored output.
.Op --
.Op arguments
.Pp
Run a tool. The available tools are: context, dependencies, format, hierarchy, implementations, and types.
Run a tool. The available tools are: context, dependencies, flags, format, hierarchy, implementations, and types.
.Pp
Tools:
.Bl -tag -offset indent
Expand Down Expand Up @@ -396,6 +396,8 @@ Show skipped and heads of filtered paths
.El
.It Cm expand
Show macro expansion for given location.
.It Cm flags
Print all macro 'flag?' values
.It Cm format
Format project, directories and/or files with the coding style used in the standard library. You can use the
.Fl -check
Expand Down
44 changes: 44 additions & 0 deletions spec/compiler/crystal/tools/flags_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require "../../../spec_helper"
include Crystal

private def parse_flags(source)
Crystal::Command::FlagsVisitor.new.tap do |visitor|
Parser.parse(source).accept(visitor)
end
end

describe Crystal::Command::FlagsVisitor do
it "different flags" do
visitor = parse_flags <<-CRYSTAL
{%
flag?(:foo)
flag?("bar")
flag?(1)
flag?(true)
%}
CRYSTAL
visitor.flag_names.should eq %w[1 bar foo true]
end

it "unique flags" do
visitor = parse_flags <<-CRYSTAL
{%
flag?(:foo)
flag?("foo")
flag?(:foo)
%}
CRYSTAL
visitor.flag_names.should eq %w[foo]
end

it "only macro" do
visitor = parse_flags <<-CRYSTAL
flag?(:flag)
f.flag?(:foo)
F.flag?(:bar)
{% f.flag?(:baz) %}
{% f.flag?(:qux, other: true) %}
CRYSTAL
visitor.flag_names.should eq %w[]
end
end
4 changes: 4 additions & 0 deletions src/compiler/crystal/command.cr
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Crystal::Command
Tool:
context show context for given location
expand show macro expansion for given location
flags print all macro `flag?` values
format format project, directories and/or files
hierarchy show type hierarchy
dependencies show file dependency tree
Expand Down Expand Up @@ -177,6 +178,9 @@ class Crystal::Command
when "format".starts_with?(tool)
options.shift
format
when "flags" == tool
options.shift
flags
when "expand".starts_with?(tool)
options.shift
expand
Expand Down
104 changes: 104 additions & 0 deletions src/compiler/crystal/tools/flags.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
require "colorize"
require "../syntax/ast"

class Crystal::Command
private def flags
OptionParser.parse(@options) do |opts|
opts.banner = "Usage: crystal tool flags [path...]\n\nOptions:"

opts.on("-h", "--help", "Show this message") do
puts opts
exit
end

opts.on("--no-color", "Disable colored output") do
@color = false
end
end

visitor = FlagsVisitor.new
find_sources(options) do |source|
Parser.parse(source.code).accept(visitor)
end
visitor.flag_names.each do |flag|
puts flag
end
end

def find_sources(
paths : Array(String),
stdin : IO = STDIN,
& : Compiler::Source ->
) : Nil
stdin_content = nil
paths.each do |path|
if path == "-"
stdin_content ||= stdin.gets_to_end
yield Compiler::Source.new(path, stdin_content)
elsif File.file?(path)
yield Compiler::Source.new(path, File.read(path))
elsif Dir.exists?(path)
Dir.glob(::Path[path].to_posix.join("**/*.cr")) do |dir_path|
if File.file?(dir_path)
yield Compiler::Source.new(path, File.read(dir_path))
end
end
else
Crystal.error "file or directory does not exist: #{path}", @color, leading_error: false
end
end
end

class FlagsVisitor < Visitor
@in_macro_expression = false

getter all_flags = [] of ASTNode

def initialize(@flag_name : String = "flag?")
end

def flag_names
all_flags.map { |flag| string_flag(flag) }.uniq!.sort!
end

private def string_flag(node)
case node
when StringLiteral, SymbolLiteral
node.value
else
node.to_s
end
end

def visit(node)
true
end

def visit(node : Crystal::MacroExpression | Crystal::MacroIf | Crystal::MacroFor)
@in_macro_expression = true

true
end

def end_visit(node : Crystal::MacroExpression | Crystal::MacroIf | Crystal::MacroFor)
@in_macro_expression = false
end

def visit(node : Crystal::Call)
check_call(node)
true
end

private def check_call(node)
return unless @in_macro_expression
return unless node.name == @flag_name
return unless node.obj.nil? && node.block.nil? && node.named_args.nil?

args = node.args
return unless args.size == 1
arg = args[0]

all_flags << arg
end
end
end