Skip to content

Commit

Permalink
add ls sample implementation; incorporate bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis committed Jul 6, 2016
1 parent 50bbffd commit ee54508
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 19 deletions.
13 changes: 12 additions & 1 deletion Argu.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{FB06D972-6D76-4220-AD16-8E5F20DADAA6}"
ProjectSection(SolutionItems) = preProject
Expand Down Expand Up @@ -46,6 +46,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{33A4833E
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Argu.Tests", "tests\Argu.Tests\Argu.Tests.fsproj", "{B94D60AD-2083-4E08-B28F-43122BE14819}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Argu.Samples.LS", "samples\Argu.Samples.LS\Argu.Samples.LS.fsproj", "{C7615A27-8D3D-466A-9B0B-D6C068B31CB1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{AF8469CE-9BA8-4EDD-BFFA-FF67EA8FFD86}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -64,6 +68,12 @@ Global
{B94D60AD-2083-4E08-B28F-43122BE14819}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B94D60AD-2083-4E08-B28F-43122BE14819}.Release|Any CPU.Build.0 = Release|Any CPU
{B94D60AD-2083-4E08-B28F-43122BE14819}.Release-NET35|Any CPU.ActiveCfg = Release|Any CPU
{C7615A27-8D3D-466A-9B0B-D6C068B31CB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7615A27-8D3D-466A-9B0B-D6C068B31CB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7615A27-8D3D-466A-9B0B-D6C068B31CB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7615A27-8D3D-466A-9B0B-D6C068B31CB1}.Release|Any CPU.Build.0 = Release|Any CPU
{C7615A27-8D3D-466A-9B0B-D6C068B31CB1}.Release-NET35|Any CPU.ActiveCfg = Release|Any CPU
{C7615A27-8D3D-466A-9B0B-D6C068B31CB1}.Release-NET35|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -72,5 +82,6 @@ Global
{036AEA2A-4F01-4834-A345-B7C6D27754FB} = {31C8F489-6FEC-4DE5-A247-8447D56B5C04}
{745EFEBD-8B8D-4B9B-9743-6AC51D586724} = {31C8F489-6FEC-4DE5-A247-8447D56B5C04}
{33A4833E-3B3D-40FD-8FFE-734E9C510A46} = {31C8F489-6FEC-4DE5-A247-8447D56B5C04}
{C7615A27-8D3D-466A-9B0B-D6C068B31CB1} = {AF8469CE-9BA8-4EDD-BFFA-FF67EA8FFD86}
EndGlobalSection
EndGlobal
6 changes: 6 additions & 0 deletions samples/Argu.Samples.LS/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
87 changes: 87 additions & 0 deletions samples/Argu.Samples.LS/Argu.Samples.LS.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>c7615a27-8d3d-466a-9b0b-d6c068b31cb1</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Argu.Samples.LS</RootNamespace>
<AssemblyName>ls</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFSharpCoreVersion>4.4.0.0</TargetFSharpCoreVersion>
<Name>Argu.Samples.LS</Name>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>..\bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<DocumentationFile>..\bin\ls.XML</DocumentationFile>
<Prefer32Bit>true</Prefer32Bit>
<OtherFlags>--standalone</OtherFlags>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>..\bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<DocumentationFile>..\bin\ls.XML</DocumentationFile>
<Prefer32Bit>true</Prefer32Bit>
<OtherFlags>--standalone</OtherFlags>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<Compile Include="Arguments.fs" />
<Compile Include="Program.fs" />
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Argu\Argu.fsproj">
<Name>Argu</Name>
<Project>{49e38cf1-8b37-4a4f-83cf-eafe9577bcc6}</Project>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '11.0'">
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</Otherwise>
</Choose>
<Import Project="$(FSharpTargetsPath)" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
121 changes: 121 additions & 0 deletions samples/Argu.Samples.LS/Arguments.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
namespace Argu.Samples.LS

open Argu

// This sample attempts to replicate the command line syntax found in
// the GNU coreutils ls command.

type Size =
| B = 1
| K = 2
| M = 3
| G = 4

type ColorWhen =
| Never = 0
| Always = 1
| Auto = 2

type QuotingStyle =
| Literal = 1
| Locale = 2
| Shell = 3
| Shell_Always = 4
| Shell_Escape = 5
| Shell_Escape_Always = 6
| C = 7
| Escape = 8

[<CliPrefix(CliPrefix.DoubleDash)>]
[<NoAppSettings>]
type LsArguments =
| [<AltCommandLine("-a")>] All
| [<AltCommandLine("-A")>] Almost_All
| Author
| [<AltCommandLine("-b")>] Escape
| [<EqualsAssignment>] Block_Size of SIZE:Size
| [<AltCommandLine("-B")>] Ignore_Backups
| [<AltCommandLine("-C"); EqualsAssignment>] Color of WHEN:ColorWhen option
| [<AltCommandLine("-d")>] Directory
| [<AltCommandLine("-D")>] Dired
| [<CliPrefix(CliPrefix.Dash)>] F
| [<AltCommandLine("-F", "--classify", "--file-type"); EqualsAssignment>] Format of WORD:string option
| [<AltCommandLine("-g")>] Group_Directories_First
| [<AltCommandLine("-G")>] No_Group
| [<AltCommandLine("-h")>] Human_Readable
| [<AltCommandLine("-i")>] INode
| [<AltCommandLine("-I")>] Ignore of PATTERN:string
| [<AltCommandLine("-k")>] KibiBytes
| [<CliPrefix(CliPrefix.Dash)>] L
| [<AltCommandLine("-L")>] Dereference
| [<CliPrefix(CliPrefix.Dash)>] M
| [<AltCommandLine("-n")>] Numeric_Uid_Guid
| [<AltCommandLine("-N")>] Literal
| [<CliPrefix(CliPrefix.Dash)>] O
| [<AltCommandLine("-p"); EqualsAssignment>] Indicator_Style of slash:char
| [<AltCommandLine("-q")>] Hide_Control_Chars
| Show_Control_Chars
| [<AltCommandLine("-Q")>] Quote_Name
| [<EqualsAssignment>] Quoting_Style of WORD:QuotingStyle
| [<AltCommandLine("-r")>] Reverse
| [<AltCommandLine("-R")>] Recursive
| [<AltCommandLine("-s")>] Size
| [<CustomCommandLine("-S")>] S
| [<CliPrefix(CliPrefix.Dash)>] T
| [<AltCommandLine("-T")>] TabSize of COLS:int
| [<CliPrefix(CliPrefix.Dash)>] U
| [<CliPrefix(CliPrefix.Dash)>] V
| [<AltCommandLine("-w")>] Width of COLS:int
| [<CustomCommandLine("-x")>] List_By_Lines
| [<CustomCommandLine("-X")>] Sort_By_Entry
| [<AltCommandLine("-Z")>] Context
| [<CustomCommandLine("-1")>] List_One
| Version
| [<Rest; GatherUnrecognized>] Files of path:string
with
interface IArgParserTemplate with
member arg.Usage =
match arg with
| All -> "do not ignore entries starting with ."
| Almost_All -> "do not list implied . and .."
| Author -> "with -l, print the author of each file"
| Escape -> "print C-style escapes for nongraphic characters"
| Block_Size _ -> "scale sizes by SIZE before printing them; e.g.,\n'--block-size=M' prints sizes in units of\n 1,048,576 bytes"
| Ignore_Backups -> "do not list implied entries ending with ~"
| Color _ -> "colorize the output; WHEN can be 'always' (default\nif omitted), 'auto', or 'never'"
| Directory -> "list directories themselves, not their contents"
| Dired _ -> "generate output designed for Emacs' dired mode"
| F -> "do not sort, enable -aU, disable -ls --color"
| Format _ -> "append indicator (one of */=>@|) to entries"
| Group_Directories_First -> "group directories before files;\ncan be augmented with a --sort option, but any\nuse of --sort=none (-U) disables grouping"
| No_Group -> "in a long listing, don't print group names"
| Human_Readable -> "with -l and/or -s, print human readable sizes\n(e.g., 1K 234M 2G)"
| INode -> "print the index number of each file"
| Ignore _ -> "do not list implied entries matching shell PATTERN"
| KibiBytes -> "default to 1024-byte blocks for disk usage"
| L -> "use a long listing format"
| Dereference -> "when showing file information for a symbolic\nlink, show information for the file the link\nreferences rather than for the link itself"
| M -> "fill width with a comma separated list of entries"
| Numeric_Uid_Guid -> "like -l, but list numeric user and group IDs"
| Literal -> "print raw entry names (don't treat e.g. control\ncharacters specially)"
| O -> "like -l, but do not list group information"
| Indicator_Style _ -> "append / indicator to directories"
| Hide_Control_Chars -> "print ? instead of nongraphic characters"
| Show_Control_Chars -> "show nongraphic characters as-is (the default,\nunless program is 'ls' and output is a terminal)"
| Quote_Name -> "enclose entry names in double quotes"
| Quoting_Style _ -> "use quoting style for entry names"
| Reverse -> "reverse order while sorting"
| Recursive -> "list subdirectories recursively"
| Size -> "print the allocated size of each file, in blocks"
| S -> "sort by file size, largest first"
| T -> "sort by modification time, newest first"
| TabSize _ -> "assume tab stops at each COLS instead of 8"
| U -> "with -lt: sort by, and show, access time;\nwith -l: show access time and sort by name;\notherwise: sort by access time, newest first"
| V -> "natural sort of (version) numbers within text"
| Width _ -> "set output width to COLS. 0 means no limit"
| Context -> "print any security context of each file"
| List_One -> "list one file per line. Avoid '\n' with -q or -b"
| List_By_Lines -> "list entries by lines instead of by columns"
| Sort_By_Entry -> "sort alphabetically by entry extension"
| Version -> "output version information and exit"
| Files _ -> "File expression to list"
16 changes: 16 additions & 0 deletions samples/Argu.Samples.LS/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Argu.Samples.LS.Main

open System
open Argu

[<EntryPoint>]
let main argv =
let parser = ArgumentParser.Create<LsArguments>(programName = "ls", errorHandler = ProcessExiter())

let results = parser.ParseCommandLine argv

printfn "Got parse results %A" <| results.GetAllResults()
let files = results.GetResults <@ Files @>
printfn "Listing files %A" files

0
4 changes: 4 additions & 0 deletions src/Argu/Parsers/Cli.fs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ let rec private parseCommandLinePartial (state : CliParseState) (argInfo : Union
| Primitives [||] ->
let result = mkUnionCase caseInfo aggregator.ResultCount ParseSource.CommandLine sw [||]
aggregator.AppendResult result
| OptionalParam _ ->
let result = mkUnionCase caseInfo aggregator.ResultCount ParseSource.CommandLine sw [|None|]
aggregator.AppendResult result

| _ -> error argInfo ErrorCode.CommandLine "argument '%s' cannot be grouped with other switches." sw

| CliParam(_, _, caseInfo, Assignment(name,sep,_)) when caseInfo.Arity <> 1 || not <| caseInfo.IsMatchingAssignmentSeparator sep ->
Expand Down
23 changes: 14 additions & 9 deletions src/Argu/PreCompute.fs
Original file line number Diff line number Diff line change
Expand Up @@ -209,18 +209,14 @@ let rec private preComputeUnionCaseArgInfo (stack : Type list) (helpParam : Help

let caseCtor = FSharpValue.PreComputeUnionConstructor(uci, bindingFlags = allBindings)

// create a dummy instance for given union case
let dummyFields = types |> Array.map Unchecked.UntypedDefaultOf
let dummy = caseCtor dummyFields :?> IArgParserTemplate

// use ref cell for late binding of parent argInfo
let current = ref None
let tryGetCurrent = fun () -> !current

/// create a dummy instance for the current union case
let usageString =
let dummyFields = types |> Array.map Unchecked.UntypedDefaultOf
let dummy = caseCtor dummyFields :?> IArgParserTemplate
try dummy.Usage
with _ ->
arguExn "Error generating usage string from IArgParserTemplate for case %O." uci

let isFirst = uci.ContainsAttribute<FirstAttribute> ()
let isAppSettingsCSV = uci.ContainsAttribute<ParseCSVAttribute> ()
let isExactlyOnce = uci.ContainsAttribute<ExactlyOnceAttribute> (true)
Expand Down Expand Up @@ -367,6 +363,15 @@ let rec private preComputeUnionCaseArgInfo (stack : Type list) (helpParam : Help
if isAppSettingsCSV && fields.Length <> 1 then
arguExn "CSV attribute is only compatible with branches of unary fields."

// extract the description string for given union case
let description =
try dummy.Usage.Split([|'\n'|], StringSplitOptions.RemoveEmptyEntries) |> Array.toList
with _ ->
arguExn "Error generating usage string from IArgParserTemplate for case %O." uci

if List.isEmpty description then
arguExn "Usage string for case '%O' was empty." uci

let uai = {
UnionCaseInfo = uci
Arity = fields.Length
Expand All @@ -380,7 +385,7 @@ let rec private preComputeUnionCaseArgInfo (stack : Type list) (helpParam : Help
AppSettingsName = appSettingsName
AppSettingsSeparators = appSettingsSeparators
AppSettingsSplitOptions = appSettingsSplitOptions
Description = usageString
Description = description
ParameterInfo = parsers
AppSettingsCSV = isAppSettingsCSV
IsMandatory = isMandatory
Expand Down
4 changes: 2 additions & 2 deletions src/Argu/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ type ArgumentCaseInfo =
AppSettingsName : string option

/// Description of the parameter
Description : string
Description : string list

/// AppSettings parameter separator
AppSettingsSeparators : string []
AppSettingsSeparators : string list
/// AppSettings parameter split options
AppSettingsSplitOptions : StringSplitOptions

Expand Down
17 changes: 12 additions & 5 deletions src/Argu/UnParsers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,15 @@ let mkArgUsage (aI : UnionCaseArgInfo) = stringExpr {
else
yield! StringExpr.whiteSpace (descriptionOffset - finish + start)

yield aI.Description
yield Environment.NewLine
match aI.Description with
| [] -> ()
| h :: tail ->
yield h
yield Environment.NewLine
for t in tail do
yield! StringExpr.whiteSpace descriptionOffset
yield t
yield Environment.NewLine
}

/// <summary>
Expand Down Expand Up @@ -289,7 +296,7 @@ let mkAppSettingsDocument (argInfo : UnionArgInfo) printComments (args : 'Templa
let mkComment () =
stringExpr {
yield ' '
yield aI.Description
yield aI.Description.[0]

match parsers |> Array.toList with
| [] -> ()
Expand All @@ -314,7 +321,7 @@ let mkAppSettingsDocument (argInfo : UnionArgInfo) printComments (args : 'Templa
|> Seq.map (fun t -> fp.UnParser (t :> _))
|> String.concat aI.AppSettingsSeparators.[0]

let mkComment () = sprintf " %s : %s ..." aI.Description fp.Description
let mkComment () = sprintf " %s : %s ..." aI.Description.[0] fp.Description

mkElem mkComment key values }

Expand All @@ -326,7 +333,7 @@ let mkAppSettingsDocument (argInfo : UnionArgInfo) printComments (args : 'Templa
| None -> ""
| Some t -> fp.UnParser (t :> _)

let mkComment () = sprintf " %s : ?%s" aI.Description fp.Description
let mkComment () = sprintf " %s : ?%s" aI.Description.[0] fp.Description

mkElem mkComment key value }

Expand Down
Loading

0 comments on commit ee54508

Please sign in to comment.