Repository: comonicon/Comonicon.jl
Pull request #190 (2022-03-04 16:08:37)

add argtypes for better autocompletion
some special types that annotates what kind of string-like input one is expecting, this is really the ideal solution to this since the best way is still to actually have different actual types for path, filename, etc. which is currently missing in Julia, but...


Patch:
diff --git a/src/Comonicon.jl b/src/Comonicon.jl
index bd2bcac..4f6599d 100644
--- a/src/Comonicon.jl
+++ b/src/Comonicon.jl
@@ -15,6 +15,7 @@ export @cast, @main, cmd_error, cmd_exit
 include("compat.jl")
 include("configs.jl")
 include("exceptions.jl")
+include("argtypes.jl")
 include("ast/ast.jl")
 include("codegen/julia.jl")
 include("codegen/zsh.jl")
diff --git a/src/argtypes.jl b/src/argtypes.jl
new file mode 100644
index 0000000..4c62f4a
--- /dev/null
+++ b/src/argtypes.jl
@@ -0,0 +1,70 @@
+module Arg
+
+export FileName, DirName,
+    UserName, Path, Prefix, Suffix,
+    @Prefix_str, @Suffix_str
+
+abstract type ArgType end
+
+struct Path <: ArgType
+    content::String
+end
+
+struct DirName <: ArgType
+    content::String
+end
+
+struct FileName <: ArgType
+    content::String
+end
+
+struct UserName <: ArgType
+    content::String
+end
+
+struct Prefix{name} <: ArgType
+    content::String
+end
+
+struct Suffix{name} <: ArgType
+    content::String
+end
+
+macro Prefix_str(s::String)
+    return Prefix{Symbol(s)}
+end
+
+macro Suffix_str(s::String)
+    return Suffix{Symbol(s)}
+end
+
+Base.show(io::IO, x::Prefix{name}) where name = print(io, "Prefix\"", name, "\"")
+Base.show(io::IO, x::Suffix{name}) where name = print(io, "Suffix\"", name, "\"")
+
+# use rust-like enum instead?
+# @renum ArgType begin
+#     Path
+#     DirName
+#     FileName
+#     UserName
+#     Prefix(name::String)
+#     AnyType(name::String)
+# end
+
+function Base.tryparse(::Type{T}, s::AbstractString) where {T <: ArgType}
+    return T(s)
+end
+
+function Base.tryparse(::Type{Prefix{name}}, s::AbstractString) where {name}
+    prefix = string(name)
+    startswith(s, prefix) && return Prefix{name}(s[length(prefix)+1:end])
+    return
+end
+
+function Base.tryparse(::Type{Suffix{name}}, s::AbstractString) where {name}
+    suffix = string(name)
+    endswith(s, suffix) && return Suffix{name}(s[1:end-length(suffix)])
+    return
+end
+
+end
diff --git a/src/ast/ast.jl b/src/ast/ast.jl
index 0c3d9a0..2f72323 100644
--- a/src/ast/ast.jl
+++ b/src/ast/ast.jl
@@ -1,6 +1,9 @@
 module AST
 
-export ComoniconExpr, Description, LeafCommand, NodeCommand, Entry, Argument, Option, Flag, print_cmd
+export ComoniconExpr,
+    Description,
+    LeafCommand, NodeCommand,
+    Entry, Argument, Option, Flag, print_cmd
 
 include("types.jl")
 include("printing.jl")
diff --git a/src/ast/printing.jl b/src/ast/printing.jl
index c060ba5..757cda9 100644
--- a/src/ast/printing.jl
+++ b/src/ast/printing.jl
@@ -168,7 +168,7 @@ end
 function print_body(io::IO, cmd::NodeCommand, t::Terminal)
     section(io, "Commands")
     for each in values(cmd.subcmds)
-        print_sig_brief(io, each, t)
+        print_name_brief(io, each, t)
         println(io)
     end
     section(io, "Flags")
@@ -216,8 +216,16 @@ function print_help(io::IO, t::Terminal)
 end
 
 function print_sig_brief(io::IO, cmd, t::Terminal)
+    print_with_brief(print_signature, io, cmd, t)
+end
+
+function print_name_brief(io::IO, cmd, t::Terminal)
+    print_with_brief(print_name, io, cmd, t)
+end
+
+function print_with_brief(f, io::IO, cmd, t::Terminal)
     buf = IOBuffer()
-    print_signature(buf, cmd, t)
+    f(buf, cmd, t)
     s = String(take!(buf))
 
     middle = t.width - t.left - t.right
@@ -230,7 +238,7 @@ function print_sig_brief(io::IO, cmd, t::Terminal)
     )
 
     print(io, tab(2))
-    print_signature(io, cmd, t)
+    f(io, cmd, t)
     isnothing(cmd.description.brief) && return
     print_indent_content(io, cmd.description.brief, t, firstline)
     return


Test patch:
diff --git a/test/argtype.jl b/test/argtype.jl
new file mode 100644
index 0000000..8d08bb8
--- /dev/null
+++ b/test/argtype.jl
@@ -0,0 +1,8 @@
+using Test
+using Comonicon: Arg
+
+@testset "tryparse(::ArgType, s)" begin
+    @test tryparse(Arg.Path, "random-string").content == "random-string"
+    @test tryparse(Arg.Prefix"file-", "file-content").content == "content"
+    @test tryparse(Arg.Suffix".py", "script.py").content == "script"    
+end
diff --git a/test/ast/ast.jl b/test/ast/ast.jl
index 7ce54b1..53c0101 100644
--- a/test/ast/ast.jl
+++ b/test/ast/ast.jl
@@ -56,7 +56,7 @@ node = NodeCommand(; name = "foo", subcmds = Dict("leaf" => leaf))
 @test_show MIME"text/plain" begin
     "  foo <command>" in node
     "Commands\n\n" in node
-    "  leaf <arg>" in node
+    "  leaf" in node
     "Flags\n\n" in node
     "-h, --help" in node
 end
diff --git a/test/runtests.jl b/test/runtests.jl
index a2ee2b6..fc32cc3 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -10,6 +10,10 @@ end
     include("options.jl")
 end
 
+@testset "argtype" begin
+    include("argtype.jl")
+end
+
 @testset "ast" begin
     include("ast/ast.jl")
 end