Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5f35398
Removed "sealed" from OptionAttribute
SJFriedl Aug 15, 2023
d8c247f
Initial start to re-framing this package locally
SJFriedl Mar 17, 2026
d2fdafb
Turned off `GeneratePackageOnBuild` in the project file so `dotnet pa…
SJFriedl Mar 17, 2026
87b197c
Removed `sealed` from `ValueAttribute`, minor README updates.
SJFriedl Mar 17, 2026
361e705
Updated version info properly, updated README
SJFriedl Mar 17, 2026
b46bc5e
Updated 2.9.3-unixwiz, frameworks all .NET 8.0
SJFriedl Mar 17, 2026
2f2f90c
Updated all code to use nameof(variable) rather than raw strings.
SJFriedl Mar 17, 2026
5be081c
Now use Array.Empty constructs rather than explicit `new` allocations
SJFriedl Mar 17, 2026
65679fb
ReSharper pass: "Empty statement is redundant"
SJFriedl Mar 17, 2026
8ddcf81
ReSharper: fixed a bunch of grammar/typo issues
SJFriedl Mar 17, 2026
d6c8d55
ReSharper: minor fixed, added CommandLine.sln.DotSettings to hold the…
SJFriedl Mar 17, 2026
e8937ea
Fixed a widespread typo (Exercize --> Exercise)
SJFriedl Mar 17, 2026
d8bab6b
Removed most (but not all) F# support
SJFriedl Mar 17, 2026
565e129
Dumped all the rest of the F# support (elided all SKIP_FSHARP)
SJFriedl Mar 17, 2026
c92fcda
ReSharper: lots of properties turned into simpler forms (auto-propert…
SJFriedl Mar 17, 2026
c00e8f4
ReSharper: "Use range indexer"
SJFriedl Mar 17, 2026
a4adc1b
ReSharper: "Join null check with assignment"
SJFriedl Mar 17, 2026
815a33e
ReSharper: "Use pattern matching"
SJFriedl Mar 17, 2026
58e5247
ReSharper: "Namespace does not correspond to file location"
SJFriedl Mar 17, 2026
1fdc51b
ReSharper: Using directive is not required by the code and can be saf…
SJFriedl Mar 17, 2026
779e77f
ReSharper: spelling
SJFriedl Mar 17, 2026
f328563
ReSharper: Code body does not conform to code style settings: use ex…
SJFriedl Mar 17, 2026
6c7b6f8
Whitespace fixes
SJFriedl Mar 17, 2026
7ad5ac3
Converted a number of properties to lambda format
SJFriedl Mar 17, 2026
d336f44
reSharper: merge 'out' declaration
SJFriedl Mar 17, 2026
8258d57
ReSharper: merge conditional expression
SJFriedl Mar 17, 2026
28d6d24
ReSharper: Suppressed CheckNamespace in a few places
SJFriedl Mar 17, 2026
84346bd
Updated CHANGELOG with ReSharper fix notes
SJFriedl Mar 17, 2026
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
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file.

CommandLineParser project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.9.5] 2026-03-17

###
- got rid of the rest of the F# support
- lots of ReSharper fixes

## [2.9.4] 2026-03-17

###
- Lots of little ReSharper fixes
- Ripped out most of the F# support

## [2.9.3-unixwiz]

###
- Updated frameworks, went from "netstandard2.0;net40;net45;net461" to "net8.0".
- Deleted LangVersion = 8.0 (way too old)

## [2.9.2-unixwiz]

###
- First Unixwiz private build
- Ensured that OptionAttribute and ValueAttribute classes are not sealed.
- Started mucking w/ the package version stuff

## [2.9.0-preview2]

### Added
Expand Down
2 changes: 2 additions & 0 deletions CommandLine.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stelluti/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
[![NuGet](https://img.shields.io/nuget/vpre/commandlineparser.svg)](https://www.nuget.org/packages/CommandLineParser/)
[![Join the Gitter chat!](https://badges.gitter.im/gsscoder/commandline.svg)](https://gitter.im/gsscoder/commandline?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

# Unixwiz.net update

This is a private, independently-maintained fork of the excellent CommandLineParser package
for modern .NET applications, and has dropped support for anything but recent .NET versions.
We have an application that required a change - un-sealing the "OptionValue" and "ValueAttribute"
classes - and needed to share this with a small circle of customers.

Sorry, FSharp and .netstandard support has been removed. The docs below will be edited at some
point to ensure that this README.md is not lying.

Do not enable `GeneratePackageOnBuild` - it interferes with expected behavior of `dotnet pack -c Release`
after a clean build and caused missing-output packaging failures.

# Command Line Parser Library for CLR and NetStandard

**Note:** the API surface has changed since v1.9.x and earlier. If you are looking for documentation on v1.9.x, please see [stable-1.9.71.2](https://github.com/gsscoder/commandline/tree/stable-1.9.71.2)
Expand Down
40 changes: 10 additions & 30 deletions src/CommandLine/BaseAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public abstract class BaseAttribute : Attribute
{
private int min;
private int max;
private object @default;
private Infrastructure.LocalizableAttributeProperty helpText;
private string metaValue;
private Type resourceType;
Expand Down Expand Up @@ -44,12 +43,12 @@ public bool Required
/// <remarks>If not set, no lower range is enforced.</remarks>
public int Min
{
get { return min; }
get => min;
set
{
if (value < 0)
{
throw new ArgumentNullException("value");
throw new ArgumentNullException(nameof(value));
}

min = value;
Expand All @@ -63,12 +62,12 @@ public int Min
/// <remarks>If not set, no upper range is enforced.</remarks>
public int Max
{
get { return max; }
get => max;
set
{
if (value < 0)
{
throw new ArgumentNullException("value");
throw new ArgumentNullException(nameof(value));
}

max = value;
Expand All @@ -78,39 +77,24 @@ public int Max
/// <summary>
/// Gets or sets mapped property default value.
/// </summary>
public object Default
{
get { return @default; }
set
{
@default = value;
}
}
public object Default { get; set; }

/// <summary>
/// Gets or sets a short description of this command line option. Usually a sentence summary.
/// </summary>
public string HelpText
{
get => helpText.Value??string.Empty;
set => helpText.Value = value ?? throw new ArgumentNullException("value");
set => helpText.Value = value ?? throw new ArgumentNullException(nameof(value));
}

/// <summary>
/// Gets or sets mapped property meta value. Usually an uppercase hint of required value type.
/// </summary>
public string MetaValue
{
get { return metaValue; }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}

metaValue = value;
}
get => metaValue;
set => metaValue = value ?? throw new ArgumentNullException(nameof(value));
}

/// <summary>
Expand All @@ -127,12 +111,8 @@ public bool Hidden
/// </summary>
public Type ResourceType
{
get { return resourceType; }
set
{
resourceType =
helpText.ResourceType = value;
}
get => resourceType;
set => resourceType = helpText.ResourceType = value;
}
}
}
33 changes: 10 additions & 23 deletions src/CommandLine/CommandLine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,36 @@
<PropertyGroup>
<AssemblyName>CommandLine</AssemblyName>
<OutputType>Library</OutputType>
<TargetFrameworks>netstandard2.0;net40;net45;net461</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>
<DefineConstants>$(DefineConstants);CSX_EITHER_INTERNAL;CSX_REM_EITHER_BEYOND_2;CSX_ENUM_INTERNAL;ERRH_INTERNAL;CSX_MAYBE_INTERNAL;CSX_REM_EITHER_FUNC;CSX_REM_CRYPTORAND;ERRH_ADD_MAYBE_METHODS</DefineConstants>
<DefineConstants Condition="'$(BuildTarget)' != 'fsharp'">$(DefineConstants);SKIP_FSHARP</DefineConstants>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyOriginatorKeyFile>..\..\CommandLine.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PackageId Condition="'$(BuildTarget)' != 'fsharp'">CommandLineParser</PackageId>
<PackageId Condition="'$(BuildTarget)' == 'fsharp'">CommandLineParser.FSharp</PackageId>
<Authors>gsscoder;nemec;ericnewton76;moh-hassan</Authors>
<Title>Command Line Parser Library</Title>
<PackageId>Unixwiz.CommandLineParser</PackageId>
<Authors>gsscoder;nemec;ericnewton76;moh-hassan;SJFriedl</Authors>
<Title>Command Line Parser Library - Unixwiz.net fork</Title>
<Version Condition="'$(VersionSuffix)' != ''">$(VersionSuffix)</Version>
<Version Condition="'$(VersionSuffix)' == ''">0.0.0</Version>
<Description Condition="'$(BuildTarget)' != 'fsharp'">Terse syntax C# command line parser for .NET. For FSharp support see CommandLineParser.FSharp. The Command Line Parser Library offers to CLR applications a clean and concise API for manipulating command line arguments and related tasks.</Description>
<Description Condition="'$(BuildTarget)' == 'fsharp'">Terse syntax C# command line parser for .NET with F# support. The Command Line Parser Library offers to CLR applications a clean and concise API for manipulating command line arguments and related tasks.</Description>
<Version Condition="'$(VersionSuffix)' == ''">2.9.5</Version>
<Description>Terse syntax C# command line parser for .NET. The Command Line Parser Library offers to CLR applications a clean and concise API for manipulating command line arguments and related tasks.</Description>
<Copyright>Copyright (c) 2005 - 2020 Giacomo Stelluti Scala &amp; Contributors</Copyright>
<PackageLicenseFile>License.md</PackageLicenseFile>
<PackageIcon>CommandLine20.png</PackageIcon>
<PackageProjectUrl>https://github.com/commandlineparser/commandline</PackageProjectUrl>
<PackageProjectUrl>https://github.com/SJFriedl/commandline</PackageProjectUrl>
<PackageTags>command line;commandline;argument;option;parser;parsing;library;syntax;shell</PackageTags>
<PackageReleaseNotes>https://github.com/commandlineparser/commandline/blob/master/CHANGELOG.md</PackageReleaseNotes>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<LangVersion>8.0</LangVersion>
<PackageReleaseNotes>https://github.com/SJFriedl/commandline/blob/master/CHANGELOG.md</PackageReleaseNotes>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageId>Unixwiz.CommandLineParser</PackageId>
</PropertyGroup>

<ItemGroup Condition="'$(BuildTarget)' != 'fsharp'">
<Compile Remove="Infrastructure\FSharpOptionHelper.cs" />
</ItemGroup>

<ItemGroup>
<Content Include="..\..\README.md" Link="README.md">
<Pack>true</Pack>
<PackagePath>README.md</PackagePath>
</Content>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'net40' and '$(BuildTarget)' == 'fsharp'">
<PackageReference Include="FSharp.Core" Version="4.5.1" Condition="'$(BuildTarget)' == 'fsharp'" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net40' and '$(BuildTarget)' == 'fsharp'">
<PackageReference Include="FSharp.Core" Version="4.0.0.1" Condition="'$(BuildTarget)' == 'fsharp'" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\License.md" Pack="true" PackagePath="$(PackageLicenseFile)" />
<None Include="..\..\art\CommandLine20.png" Pack="true" PackagePath="$(PackageIcon)" />
Expand Down
8 changes: 3 additions & 5 deletions src/CommandLine/Core/GetoptTokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CommandLine.Infrastructure;
using CSharpx;
using RailwaySharp.ErrorHandling;
using System.Text.RegularExpressions;

namespace CommandLine.Core
{
Expand Down Expand Up @@ -145,7 +143,7 @@ private static IEnumerable<Token> TokenizeShortName(

// First option char that requires a value means we swallow the rest of the string as the value
// But if there is no rest of the string, then instead we swallow the next argument
string chars = arg.Substring(1);
string chars = arg[1..];
int len = chars.Length;
if (len > 0 && Char.IsDigit(chars[0]))
{
Expand All @@ -164,7 +162,7 @@ private static IEnumerable<Token> TokenizeShortName(
if (i+1 < len)
{
// Rest of this is the value (e.g. "-sfoo" where "-s" is a string-consuming arg)
yield return Token.Value(chars.Substring(i+1));
yield return Token.Value(chars[(i+1)..]);
yield break;
}
else
Expand Down Expand Up @@ -192,7 +190,7 @@ private static IEnumerable<Token> TokenizeLongName(
Action<string> onUnknownOption,
Action<int> onConsumeNext)
{
string[] parts = arg.Substring(2).Split(new char[] { '=' }, 2);
string[] parts = arg[2..].Split(new char[] { '=' }, 2);
string name = parts[0];
string value = (parts.Length > 1) ? parts[1] : null;
// A parameter like "--stringvalue=" is acceptable, and makes stringvalue be the empty string
Expand Down
14 changes: 7 additions & 7 deletions src/CommandLine/Core/InstanceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static ParserResult<T> Build<T>(
var valueSpecPropsResult =
ValueMapper.MapValues(
(from pt in specProps where pt.Specification.IsValue() orderby ((ValueSpecification)pt.Specification).Index select pt),
valuesPartition,
valuesPartition,
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, false, parsingCulture, ignoreValueCase));

var missingValueErrors = from token in errorsPartition
Expand All @@ -110,7 +110,7 @@ public static ParserResult<T> Build<T>(

//build the instance, determining if the type is mutable or not.
T instance;
if(typeInfo.IsMutable() == true)
if (typeInfo.IsMutable() == true)
{
instance = BuildMutable(factory, specPropsWithValue, setPropertyErrors);
}
Expand Down Expand Up @@ -156,8 +156,8 @@ private static T BuildMutable<T>(Maybe<Func<T>> factory, IEnumerable<Specificati

setPropertyErrors.AddRange(
mutable.SetProperties(
specPropsWithValue,
sp => sp.Value.IsJust(),
specPropsWithValue,
sp => sp.Value.IsJust(),
sp => sp.Value.FromJustOrFail()
)
);
Expand All @@ -173,8 +173,8 @@ private static T BuildMutable<T>(Maybe<Func<T>> factory, IEnumerable<Specificati
setPropertyErrors.AddRange(
mutable.SetProperties(
specPropsWithValue,
sp => sp.Value.IsNothing()
&& sp.Specification.TargetType == TargetType.Sequence
sp => sp.Value.IsNothing()
&& sp.Specification.TargetType == TargetType.Sequence
&& sp.Specification.DefaultValue.MatchNothing(),
sp => sp.Property.PropertyType.GetTypeInfo().GetGenericArguments().Single().CreateEmptyArray()
)
Expand All @@ -189,7 +189,7 @@ private static T BuildImmutable<T>(Type typeInfo, Maybe<Func<T>> factory, IEnume
specProps.Select(sp => sp.Property.PropertyType).ToArray()
);

if(ctor == null)
if (ctor == null)
{
throw new InvalidOperationException($"Type {typeInfo.FullName} appears to be immutable, but no constructor found to accept values.");
}
Expand Down
3 changes: 1 addition & 2 deletions src/CommandLine/Core/InstanceChooser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using CommandLine.Infrastructure;
using CSharpx;
using RailwaySharp.ErrorHandling;

Expand Down Expand Up @@ -141,7 +140,7 @@ private static ParserResult<object> MatchVerb(
parsingCulture,
autoHelp,
autoVersion,
allowMultiInstance,
allowMultiInstance,
nonFatalErrors);
}

Expand Down
49 changes: 12 additions & 37 deletions src/CommandLine/Core/OptionSpecification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,18 @@ namespace CommandLine.Core
{
sealed class OptionSpecification : Specification
{
private readonly string shortName;
private readonly string longName;
private readonly char separator;
private readonly string setName;
private readonly string group;
private readonly bool flagCounter;

public OptionSpecification(string shortName, string longName, bool required, string setName, Maybe<int> min, Maybe<int> max,
char separator, Maybe<object> defaultValue, string helpText, string metaValue, IEnumerable<string> enumValues,
Type conversionType, TargetType targetType, string group, bool flagCounter = false, bool hidden = false)
: base(SpecificationType.Option,
required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, conversionType == typeof(int) && flagCounter ? TargetType.Switch : targetType, hidden)
{
this.shortName = shortName;
this.longName = longName;
this.separator = separator;
this.setName = setName;
this.group = group;
this.flagCounter = flagCounter;
this.ShortName = shortName;
this.LongName = longName;
this.Separator = separator;
this.SetName = setName;
this.Group = group;
this.FlagCounter = flagCounter;
}

public static OptionSpecification FromAttribute(OptionAttribute attribute, Type conversionType, IEnumerable<string> enumValues)
Expand Down Expand Up @@ -57,37 +50,19 @@ public static OptionSpecification NewSwitch(string shortName, string longName, b
'\0', Maybe.Nothing<object>(), helpText, metaValue, Enumerable.Empty<string>(), typeof(bool), TargetType.Switch, string.Empty, false, hidden);
}

public string ShortName
{
get { return shortName; }
}
public string ShortName { get; }

public string LongName
{
get { return longName; }
}
public string LongName { get; }

public char Separator
{
get { return separator; }
}
public char Separator { get; }

public string SetName
{
get { return setName; }
}
public string SetName { get; }

public string Group
{
get { return group; }
}
public string Group { get; }

/// <summary>
/// Whether this is an int option that counts how many times a flag was set rather than taking a value on the command line
/// </summary>
public bool FlagCounter
{
get { return flagCounter; }
}
public bool FlagCounter { get; }
}
}
Loading