├── docs ├── list.png └── source.png ├── global.json ├── examples ├── Examples.slnx ├── CSharp │ ├── Program.cs │ └── CSharp.csproj └── FSharp │ ├── Program.fs │ └── FSharp.fsproj ├── src ├── Example │ ├── Extensions │ │ ├── StringExtensions.cs │ │ ├── FileExtensions.cs │ │ └── EnumerableExtensions.cs │ ├── Properties │ │ └── Usings.cs │ ├── Utilities │ │ ├── ProjectInformation.cs │ │ ├── ProjectParser.cs │ │ ├── ExampleFinder.cs │ │ └── CSharpColorizer.cs │ ├── Example.csproj │ ├── Features │ │ ├── ExampleLister.cs │ │ ├── ExampleSelector.cs │ │ ├── ExampleSourceLister.cs │ │ └── ExampleRunner.cs │ └── Program.cs ├── Example.slnx ├── Directory.Build.props └── .editorconfig ├── .editorconfig ├── .github └── workflows │ ├── ci.yaml │ └── publish.yaml ├── LICENSE.md ├── .gitignore └── README.md /docs/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patriksvensson/dotnet-example/HEAD/docs/list.png -------------------------------------------------------------------------------- /docs/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patriksvensson/dotnet-example/HEAD/docs/source.png -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "10.0.100", 4 | "rollForward": "latestMinor" 5 | } 6 | } -------------------------------------------------------------------------------- /examples/Examples.slnx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/CSharp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace First 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | Console.WriteLine("Hello from C#"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Example/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public static class StringExtensions 4 | { 5 | public static string? EscapeMarkup(this string markup) 6 | { 7 | return markup?.Replace("[", "[[")?.Replace("]", "]]"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/CSharp/CSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net10.0 6 | CSharp 7 | Writes 'Hello from C#!' to the console. 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/FSharp/Program.fs: -------------------------------------------------------------------------------- 1 | // Learn more about F# at http://docs.microsoft.com/dotnet/fsharp 2 | 3 | open System 4 | 5 | // Define a function to construct a message to print 6 | let from whom = 7 | sprintf "from %s" whom 8 | 9 | [] 10 | let main argv = 11 | let message = from "F#" // Call the function 12 | printfn "Hello %s" message 13 | 0 // return an integer exit code -------------------------------------------------------------------------------- /src/Example/Extensions/FileExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public static class FileExtensions 4 | { 5 | public static IEnumerable ReadLines(this IFile file) 6 | { 7 | using var stream = file.OpenRead(); 8 | using var reader = new StreamReader(stream); 9 | while (reader.ReadLine() is { } line) 10 | { 11 | yield return line; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/FSharp/FSharp.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net10.0 6 | FSharp 7 | Writes 'Hello from F#!' to the console. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Example.slnx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Example/Properties/Usings.cs: -------------------------------------------------------------------------------- 1 | global using System.ComponentModel; 2 | global using System.Diagnostics; 3 | global using System.Text; 4 | global using System.Xml.Linq; 5 | global using System.Xml.XPath; 6 | global using CliWrap; 7 | global using CliWrap.EventStream; 8 | global using Microsoft.CodeAnalysis; 9 | global using Microsoft.CodeAnalysis.CSharp; 10 | global using Spectre.Console; 11 | global using Spectre.Console.Cli; 12 | global using Spectre.IO; 13 | global using Environment = Spectre.IO.Environment; -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = LF 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = false 9 | trim_trailing_whitespace = true 10 | 11 | [*.{sln,slnx}] 12 | indent_style = tab 13 | 14 | [*.{csproj,vbproj,vcxproj,vcxproj.filters}] 15 | indent_size = 2 16 | 17 | [*.{xml,config,props,targets,nuspec,ruleset}] 18 | indent_size = 2 19 | 20 | [*.{yml,yaml}] 21 | indent_size = 2 22 | 23 | [*.json] 24 | indent_size = 2 25 | 26 | [*.md] 27 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: pull_request 3 | 4 | env: 5 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 6 | DOTNET_CLI_TELEMETRY_OPTOUT: true 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | if: "!contains(github.event.head_commit.message, 'skip-ci')" 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v5 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Setup .NET SDK 20 | uses: actions/setup-dotnet@v5 21 | with: 22 | dotnet-version: | 23 | 10.0.x 24 | 9.0.x 25 | 8.0.x 26 | 27 | - name: Build 28 | shell: bash 29 | run: dotnet build.cs -------------------------------------------------------------------------------- /src/Example/Utilities/ProjectInformation.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public sealed class ProjectInformation 4 | { 5 | public string Name { get; } 6 | public FilePath Path { get; } 7 | public string Description { get; } 8 | public int Order { get; } 9 | public bool Visible { get; } 10 | public string Group { get; } 11 | 12 | public ProjectInformation( 13 | string name, FilePath path, string description, 14 | int order, bool visible, string group) 15 | { 16 | Name = name; 17 | Path = path; 18 | Description = description; 19 | Order = order; 20 | Visible = visible; 21 | Group = group; 22 | } 23 | 24 | public DirectoryPath GetWorkingDirectory() 25 | { 26 | return Path.GetDirectory(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | branches: 8 | - main 9 | 10 | env: 11 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 12 | DOTNET_CLI_TELEMETRY_OPTOUT: true 13 | 14 | jobs: 15 | 16 | ################################################### 17 | # PUBLISH 18 | ################################################### 19 | 20 | publish: 21 | name: Publish 22 | if: "!contains(github.event.head_commit.message, 'skip-ci')" 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v5 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: Setup .NET SDK 31 | uses: actions/setup-dotnet@v5 32 | with: 33 | dotnet-version: | 34 | 10.0.x 35 | 9.0.x 36 | 8.0.x 37 | 38 | - name: Publish 39 | shell: bash 40 | run: dotnet build.cs --target="publish" --nuget-key="${{secrets.NUGET_API_KEY}}" 41 | -------------------------------------------------------------------------------- /src/Example/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | internal static class EnumerableExtensions 4 | { 5 | public static IEnumerable<(int Index, bool First, bool Last, T Item)> Enumerate(this IEnumerable source) 6 | { 7 | if (source is null) 8 | { 9 | throw new ArgumentNullException(nameof(source)); 10 | } 11 | 12 | return Enumerate(source.GetEnumerator()); 13 | } 14 | 15 | public static IEnumerable<(int Index, bool First, bool Last, T Item)> Enumerate(this IEnumerator source) 16 | { 17 | if (source is null) 18 | { 19 | throw new ArgumentNullException(nameof(source)); 20 | } 21 | 22 | var first = true; 23 | var last = !source.MoveNext(); 24 | T current; 25 | 26 | for (var index = 0; !last; index++) 27 | { 28 | current = source.Current; 29 | last = !source.MoveNext(); 30 | yield return (index, first, last, current); 31 | first = false; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Patrik Svensson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 14 5 | true 6 | enable 7 | true 8 | embedded 9 | true 10 | true 11 | true 12 | true 13 | true 14 | $(NoWarn);SA1633 15 | true 16 | 17 | 18 | 19 | true 20 | 21 | 22 | 23 | true 24 | true 25 | 26 | 27 | 28 | 29 | 30 | 31 | All 32 | 33 | 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Misc folders 2 | [Bb]in/ 3 | [Oo]bj/ 4 | [Tt]emp/ 5 | [Pp]ackages/ 6 | /.artifacts/ 7 | src/Example/examples 8 | src/Example/samples 9 | /[Tt]ools/ 10 | 11 | # Cakeup 12 | cakeup-x86_64-latest.exe 13 | 14 | # .NET Core CLI 15 | /.dotnet/ 16 | /.packages/ 17 | dotnet-install.sh* 18 | *.lock.json 19 | 20 | # Visual Studio 21 | .vs/ 22 | .vscode/ 23 | launchSettings.json 24 | *.sln.ide/ 25 | 26 | # Rider 27 | src/.idea/**/workspace.xml 28 | src/.idea/**/tasks.xml 29 | src/.idea/dictionaries 30 | src/.idea/**/dataSources/ 31 | src/.idea/**/dataSources.ids 32 | src/.idea/**/dataSources.xml 33 | src/.idea/**/dataSources.local.xml 34 | src/.idea/**/sqlDataSources.xml 35 | src/.idea/**/dynamic.xml 36 | src/.idea/**/uiDesigner.xml 37 | 38 | ## Ignore Visual Studio temporary files, build results, and 39 | ## files generated by popular Visual Studio add-ons. 40 | 41 | # User-specific files 42 | *.suo 43 | *.user 44 | *.sln.docstates 45 | *.userprefs 46 | *.GhostDoc.xml 47 | *StyleCop.Cache 48 | 49 | # Build results 50 | [Dd]ebug/ 51 | [Rr]elease/ 52 | x64/ 53 | *_i.c 54 | *_p.c 55 | *.ilk 56 | *.meta 57 | *.obj 58 | *.pch 59 | *.pdb 60 | *.pgc 61 | *.pgd 62 | *.rsp 63 | *.sbr 64 | *.tlb 65 | *.tli 66 | *.tlh 67 | *.tmp 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | 73 | # Visual Studio profiler 74 | *.psess 75 | *.vsp 76 | *.vspx 77 | 78 | # ReSharper is a .NET coding add-in 79 | _ReSharper* 80 | 81 | # NCrunch 82 | *.ncrunch* 83 | .*crunch*.local.xml 84 | _NCrunch_* 85 | 86 | # NuGet Packages Directory 87 | packages 88 | 89 | # Windows 90 | Thumbs.db -------------------------------------------------------------------------------- /src/Example/Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | enable 6 | Example 7 | net10.0;net9.0;net8.0 8 | true 9 | dotnet-example 10 | Major 11 | dotnet-example 12 | $(MSBuildProjectDirectory)/../.. 13 | false 14 | 15 | 16 | 17 | A dotnet tool to run examples. 18 | Patrik Svensson 19 | git 20 | https://github.com/patriksvensson/dotnet-example 21 | https://github.com/patriksvensson/dotnet-example 22 | MIT 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/Example/Features/ExampleLister.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public sealed class ExampleLister 4 | { 5 | private readonly IAnsiConsole _console; 6 | private readonly ExampleFinder _finder; 7 | 8 | public ExampleLister(IAnsiConsole console, ExampleFinder finder) 9 | { 10 | _console = console ?? throw new ArgumentNullException(nameof(console)); 11 | _finder = finder ?? throw new ArgumentNullException(nameof(finder)); 12 | } 13 | 14 | public void List() 15 | { 16 | var examples = _finder.FindExamples(); 17 | if (examples.Count == 0) 18 | { 19 | _console.Markup("[yellow]No examples could be found.[/]"); 20 | return; 21 | } 22 | 23 | _console.WriteLine(); 24 | 25 | var rows = new Grid().Collapse(); 26 | rows.AddColumn(); 27 | foreach (var group in examples.GroupBy(ex => ex.Group)) 28 | { 29 | rows.AddRow(CreateTable(group.Key, group)); 30 | rows.AddEmptyRow(); 31 | } 32 | 33 | _console.Write(rows); 34 | _console.MarkupLine("Type [blue]dotnet example --help[/] for help"); 35 | } 36 | 37 | private static Table CreateTable(string group, IEnumerable projects) 38 | { 39 | var grid = new Table { Border = TableBorder.Rounded }.Expand(); 40 | grid.AddColumn(new TableColumn("[grey]Example[/]") { NoWrap = true, }); 41 | grid.AddColumn(new TableColumn("[grey]Description[/]")); 42 | 43 | if (!string.IsNullOrWhiteSpace(group)) 44 | { 45 | grid.Title = new TableTitle(group); 46 | } 47 | 48 | foreach (var example in projects.OrderBy(e => e.Order)) 49 | { 50 | grid.AddRow( 51 | $"[underline blue]{example.Name}[/]", 52 | example.Description ?? "[grey]N/A[/]"); 53 | } 54 | 55 | return grid; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Example/Utilities/ProjectParser.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public sealed class ProjectParser 4 | { 5 | private readonly IFileSystem _fileSystem; 6 | 7 | public ProjectParser(IFileSystem fileSystem) 8 | { 9 | _fileSystem = fileSystem; 10 | } 11 | 12 | public ProjectInformation Parse(FilePath path) 13 | { 14 | // Load the project file 15 | var file = _fileSystem.GetFile(path); 16 | using var stream = file.OpenRead(); 17 | var xml = XDocument.Load(stream); 18 | 19 | // Visible? 20 | var visible = true; 21 | var visibleString = Parse(xml, "//ExampleVisible"); 22 | if (visibleString?.Equals("false", StringComparison.OrdinalIgnoreCase) ?? false) 23 | { 24 | visible = false; 25 | } 26 | 27 | // Got a description? 28 | var description = Parse(xml, "//ExampleDescription", "//Description"); 29 | 30 | // Belongs to a group? 31 | var group = Parse(xml, "//ExampleGroup"); 32 | 33 | // Got a title? 34 | var name = Parse(xml, "//ExampleTitle", "//Title"); 35 | if (string.IsNullOrWhiteSpace(name)) 36 | { 37 | name = path.GetFilenameWithoutExtension().FullPath; 38 | } 39 | 40 | // Got an order? 41 | var order = 1024; 42 | var orderString = Parse(xml, "//ExampleOrder"); 43 | if (int.TryParse(orderString, out var orderInteger)) 44 | { 45 | order = orderInteger; 46 | } 47 | 48 | return new ProjectInformation(name, path, description, order, visible, group); 49 | } 50 | 51 | private static string Parse(XDocument document, params string[] expressions) 52 | { 53 | foreach (var expression in expressions) 54 | { 55 | var element = document.Root?.XPathSelectElement(expression); 56 | if (element != null) 57 | { 58 | return element.Value; 59 | } 60 | } 61 | 62 | return string.Empty; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Example/Features/ExampleSelector.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public sealed class ExampleSelector 4 | { 5 | private readonly IAnsiConsole _console; 6 | private readonly ExampleFinder _finder; 7 | 8 | public ExampleSelector(IAnsiConsole console, ExampleFinder finder) 9 | { 10 | _console = console ?? throw new ArgumentNullException(nameof(console)); 11 | _finder = finder ?? throw new ArgumentNullException(nameof(finder)); 12 | } 13 | 14 | public ProjectInformation? Select() 15 | { 16 | var examples = _finder.FindExamples(); 17 | if (examples.Count == 0) 18 | { 19 | _console.Markup("[yellow]No examples could be found.[/]"); 20 | return null; 21 | } 22 | 23 | var prompt = new SelectionPrompt(); 24 | var groups = examples.GroupBy(ex => ex.Group); 25 | 26 | if (groups.Count() == 1) 27 | { 28 | prompt.AddChoices(examples.Select(x => x.Name)); 29 | } 30 | else 31 | { 32 | var noGroupExamples = new List(); 33 | 34 | foreach (var group in groups) 35 | { 36 | if (string.IsNullOrEmpty(group.Key)) 37 | { 38 | noGroupExamples.AddRange(group.Select(x => x.Name)); 39 | } 40 | else 41 | { 42 | prompt.AddChoiceGroup( 43 | group.Key, 44 | group.Select(x => x.Name)); 45 | } 46 | } 47 | 48 | if (noGroupExamples.Count > 0) 49 | { 50 | prompt.AddChoices(noGroupExamples); 51 | } 52 | } 53 | 54 | var example = AnsiConsole.Prompt(prompt 55 | .Title("[yellow]Choose an example to run[/]") 56 | .MoreChoicesText("[grey](Move up and down to reveal more examples)[/]") 57 | .Mode(SelectionMode.Leaf)); 58 | 59 | return examples.FirstOrDefault(x => x.Name == example); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Example/Features/ExampleSourceLister.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public sealed class ExampleSourceLister 4 | { 5 | private readonly IFileSystem _fileSystem; 6 | private readonly IGlobber _globber; 7 | 8 | public ExampleSourceLister(IFileSystem fileSystem, IGlobber globber) 9 | { 10 | _fileSystem = fileSystem; 11 | _globber = globber; 12 | } 13 | 14 | public bool List(ProjectInformation project) 15 | { 16 | var result = FindProgram(project); 17 | if (result == null) 18 | { 19 | return false; 20 | } 21 | 22 | var lines = GetLines(result); 23 | 24 | var table = new Table { ShowHeaders = false, Border = TableBorder.Rounded }; 25 | table.AddColumn(new TableColumn(string.Empty) { NoWrap = true }); 26 | table.AddColumn(string.Empty); 27 | 28 | var lineNumber = 1; 29 | foreach (var line in lines) 30 | { 31 | table.AddRow($"[grey]{lineNumber}[/]", line); 32 | lineNumber++; 33 | } 34 | 35 | AnsiConsole.WriteLine(); 36 | AnsiConsole.Write(table); 37 | 38 | return true; 39 | } 40 | 41 | private List GetLines(FilePath programFile) 42 | { 43 | using (var stream = _fileSystem.File.OpenRead(programFile.FullPath)) 44 | using (var reader = new StreamReader(stream)) 45 | { 46 | // F# doesn't have a SyntaxWalker, so just 47 | // return the lines. 48 | if (programFile.GetExtension() == ".fs") 49 | { 50 | var result = new List(); 51 | while (true) 52 | { 53 | var line = reader.ReadLine(); 54 | if (line == null) 55 | { 56 | break; 57 | } 58 | 59 | var escaped = line.EscapeMarkup(); 60 | if (escaped != null) 61 | { 62 | result.Add(escaped); 63 | } 64 | } 65 | 66 | return result; 67 | } 68 | 69 | // Return colorized lines for C# 70 | return CSharpColorizer.Colorize(reader.ReadToEnd()); 71 | } 72 | } 73 | 74 | private FilePath? FindProgram(ProjectInformation project) 75 | { 76 | var directory = project.Path.GetDirectory(); 77 | var result = _globber.Match("**/Program.{f|c}s", new GlobberSettings { Root = directory }).OfType().ToList(); 78 | 79 | if (result.Count == 0) 80 | { 81 | AnsiConsole.Markup("[red]Error:[/] Could not find Program.cs for example [underline]{0}[/].", project.Name); 82 | return null; 83 | } 84 | 85 | if (result.Count > 1) 86 | { 87 | AnsiConsole.Markup("[red]Error:[/] Found multiple Program.cs for example [underline]{0}[/].", project.Name); 88 | return null; 89 | } 90 | 91 | return result.First(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Example/Utilities/ExampleFinder.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public sealed class ExampleFinder 4 | { 5 | private readonly IFileSystem _fileSystem; 6 | private readonly IEnvironment _environment; 7 | private readonly IGlobber _globber; 8 | private readonly string[] _skip; 9 | private readonly ProjectParser _parser; 10 | 11 | public ExampleFinder(IFileSystem fileSystem, IEnvironment environment, IGlobber globber, string[]? skip) 12 | { 13 | _fileSystem = fileSystem; 14 | _environment = environment; 15 | _globber = globber; 16 | _skip = skip ?? Array.Empty(); 17 | _parser = new ProjectParser(fileSystem); 18 | } 19 | 20 | public ProjectInformation? FindExample(string name) 21 | { 22 | var result = new List(); 23 | foreach (var example in FindExamples()) 24 | { 25 | if (example.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) 26 | { 27 | result.Add(example); 28 | } 29 | } 30 | 31 | if (result.Count == 0) 32 | { 33 | AnsiConsole.Markup("[red]Error:[/] The example [underline]{0}[/] could not be found.", name); 34 | return null; 35 | } 36 | 37 | if (result.Count > 1) 38 | { 39 | AnsiConsole.Markup("[red]Error:[/] Found multiple examples called [underline]{0}[/].", name); 40 | return null; 41 | } 42 | 43 | return result[0]; 44 | } 45 | 46 | public IReadOnlyList FindExamples() 47 | { 48 | var result = new List(); 49 | 50 | var folders = GetExampleFolders(); 51 | var examples = folders.Select(FindProjects).Aggregate((acc, xs) => acc.Concat(xs)); 52 | foreach (var example in examples) 53 | { 54 | result.Add(_parser.Parse(example)); 55 | } 56 | 57 | return result 58 | .Where(x => x.Visible && !_skip.Contains(x.Name, StringComparer.OrdinalIgnoreCase)) 59 | .OrderBy(x => x.Order) 60 | .ToList(); 61 | } 62 | 63 | private string[] GetExampleFolders() 64 | { 65 | var dotExamplesFilePath = new FilePath(".examples").MakeAbsolute(_environment); 66 | var folders = _fileSystem.Exist(dotExamplesFilePath) 67 | ? _fileSystem.GetFile(dotExamplesFilePath) 68 | .ReadLines() 69 | .Where(s => !string.IsNullOrWhiteSpace(s) 70 | && !s.StartsWith('#')) // skip comments 71 | .Select(s => s.Trim()) 72 | .ToArray() 73 | : Array.Empty(); 74 | 75 | if (folders.Length == 0) 76 | { 77 | folders = new[] { "examples", "samples" }; 78 | } 79 | 80 | return folders; 81 | } 82 | 83 | private IEnumerable FindProjects(string folder) 84 | { 85 | var root = new DirectoryPath(folder).MakeAbsolute(_environment); 86 | var globberSettings = new GlobberSettings { Comparer = new PathComparer(false), Root = root }; 87 | return _globber.Match(@"**/*.{c|f}sproj", globberSettings).OfType(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Example/Utilities/CSharpColorizer.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public sealed class CSharpColorizer 4 | { 5 | public static List Colorize(string source) 6 | { 7 | var tree = CSharpSyntaxTree.ParseText(source); 8 | var walker = new ColorizerSyntaxWalker(SyntaxWalkerDepth.StructuredTrivia); 9 | walker.Visit(tree.GetRoot()); 10 | 11 | return walker.GetResult(); 12 | } 13 | 14 | private sealed class ColorizerSyntaxWalker : SyntaxWalker 15 | { 16 | private readonly StringBuilder _result; 17 | 18 | public ColorizerSyntaxWalker(SyntaxWalkerDepth depth = SyntaxWalkerDepth.Node) 19 | : base(depth) 20 | { 21 | _result = new StringBuilder(); 22 | } 23 | 24 | public List GetResult() 25 | { 26 | return _result.ToString() 27 | .Replace("\r\n", "\n", StringComparison.OrdinalIgnoreCase) 28 | .Replace("\r", string.Empty, StringComparison.OrdinalIgnoreCase) 29 | .TrimEnd('\n') 30 | .Split(new string[] { "\n" }, StringSplitOptions.None) 31 | .ToList(); 32 | } 33 | 34 | protected override void VisitToken(SyntaxToken token) 35 | { 36 | ProcessTrivia(token.LeadingTrivia); 37 | 38 | if (token.IsKeyword()) 39 | { 40 | _result.Append("[blue]").Append(token.ToString().EscapeMarkup()).Append("[/]"); 41 | } 42 | else 43 | { 44 | if (token.IsKind(SyntaxKind.IdentifierToken)) 45 | { 46 | _result.Append("[white]").Append(token.ToString().EscapeMarkup()).Append("[/]"); 47 | } 48 | else if (token.IsKind(SyntaxKind.StringLiteralToken)) 49 | { 50 | _result.Append("[grey]").Append(token.ToString().EscapeMarkup()).Append("[/]"); 51 | } 52 | else 53 | { 54 | _result.Append("[silver]").Append(token.ToString().EscapeMarkup()).Append("[/]"); 55 | } 56 | } 57 | 58 | ProcessTrivia(token.TrailingTrivia); 59 | 60 | base.VisitToken(token); 61 | } 62 | 63 | private void ProcessTrivia(SyntaxTriviaList list) 64 | { 65 | foreach (var trivia in list) 66 | { 67 | if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia)) 68 | { 69 | _result.Append("[green]").Append(trivia.ToString().EscapeMarkup()).Append("[/]"); 70 | } 71 | else if (trivia.IsKind(SyntaxKind.MultiLineCommentTrivia)) 72 | { 73 | var result = trivia.ToString().Split("\r\n", StringSplitOptions.None); 74 | foreach (var (_, _, last, item) in result.Enumerate()) 75 | { 76 | _result.Append("[green]").Append(item.EscapeMarkup()).Append("[/]"); 77 | if (!last) 78 | { 79 | _result.Append("\r\n"); 80 | } 81 | } 82 | } 83 | else 84 | { 85 | _result.Append(trivia.ToString().EscapeMarkup()); 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dotnet example 2 | 3 | A dotnet tool to list and run examples similar to Rust's `cargo run --example`. 4 | 5 | ## Installing 6 | 7 | ``` 8 | > dotnet tool install -g dotnet-example 9 | ``` 10 | 11 | ## Listing examples 12 | 13 | ``` 14 | > dotnet example 15 | 16 | ╭─────────────┬──────────────────────────────────────────────╮ 17 | │ Example │ Description │ 18 | ├─────────────┼──────────────────────────────────────────────┤ 19 | │ CSharp │ Writes 'Hello from C#' to the console │ 20 | │ FSharp │ Writes 'Hello from F#' to the console │ 21 | ╰─────────────┴──────────────────────────────────────────────╯ 22 | 23 | Type dotnet example --help for help 24 | ``` 25 | 26 | ## Running individual examples 27 | 28 | ``` 29 | > dotnet example csharp 30 | Hello from C# 31 | 32 | > dotnet example fsharp 33 | Hello from F# 34 | ``` 35 | 36 | ## Running all examples 37 | 38 | ``` 39 | > dotnet example --all 40 | ── Example: CSharp ──────────────────────────────────────────────── 41 | Hello from C# 42 | 43 | ── Example: FSharp ──────────────────────────────────────────────── 44 | Hello from F# 45 | ``` 46 | 47 | ## Showing example source code 48 | 49 | ``` 50 | > dotnet example fsharp --source 51 | 52 | ╭────┬───────────────────────────────────────────────────────────────────╮ 53 | │ 1 │ // Learn more about F# at http://docs.microsoft.com/dotnet/fsharp │ 54 | │ 2 │ │ 55 | │ 3 │ open System │ 56 | │ 4 │ │ 57 | │ 5 │ // Define a function to construct a message to print │ 58 | │ 6 │ let from whom = │ 59 | │ 7 │ sprintf "from %s" whom │ 60 | │ 8 │ │ 61 | │ 9 │ [] │ 62 | │ 10 │ let main argv = │ 63 | │ 11 │ let message = from "F#" // Call the function │ 64 | │ 12 │ printfn "Hello %s" message │ 65 | │ 13 │ 0 // return an integer exit code │ 66 | ╰────┴───────────────────────────────────────────────────────────────────╯ 67 | ``` 68 | 69 | ## Conventions 70 | 71 | The convention is simple, if there is an `examples` or `samples` folder 72 | in the directory the tool is executed in, it will fetch all `csproj`/`fsproj` files 73 | and find the best match to the query. 74 | 75 | If examples are located in unconventional folders, add a `.examples` file 76 | with the (relative) paths of the examples folders, one per line. Blank lines 77 | or lines starting with `#` in the `.examples` file are ignored. 78 | 79 | ## Example settings 80 | 81 | To change the name, description, and the order of an example, edit its `csproj`/`fsproj` file, and add the following section: 82 | 83 | ```xml 84 | 85 | Foo 86 | This is the description of the example. 87 | 5 88 | 89 | ``` 90 | 91 | If no name is set in the `csproj` file, the project name will be used. 92 | To ignore an example, add the `ExampleVisible` property in the example's `csproj`/`fsproj` file. 93 | 94 | ```xml 95 | 96 | false 97 | 98 | ``` -------------------------------------------------------------------------------- /src/Example/Features/ExampleRunner.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public class ExampleRunner 4 | { 5 | private readonly IAnsiConsole _console; 6 | private readonly ExampleFinder _finder; 7 | 8 | public ExampleRunner(IAnsiConsole console, ExampleFinder finder) 9 | { 10 | _console = console ?? throw new ArgumentNullException(nameof(console)); 11 | _finder = finder ?? throw new ArgumentNullException(nameof(finder)); 12 | } 13 | 14 | public async Task Run(string name, IRemainingArguments remaining) 15 | { 16 | var example = _finder.FindExample(name); 17 | if (example == null) 18 | { 19 | return -1; 20 | } 21 | 22 | if (!await Build(example).ConfigureAwait(false)) 23 | { 24 | return -1; 25 | } 26 | 27 | var arguments = "run"; 28 | if (remaining.Raw.Count > 0) 29 | { 30 | arguments += $"--no-build --no-restore -- {string.Join(" ", remaining.Raw)}"; 31 | } 32 | 33 | // Run the example using "dotnet run" 34 | var info = new ProcessStartInfo("dotnet") 35 | { 36 | Arguments = arguments, 37 | WorkingDirectory = example.GetWorkingDirectory().FullPath, 38 | }; 39 | 40 | var process = Process.Start(info); 41 | if (process == null) 42 | { 43 | throw new InvalidOperationException("An error occured when starting the 'dotnet' process"); 44 | } 45 | 46 | process.WaitForExit(); 47 | return process.ExitCode; 48 | } 49 | 50 | public async Task RunAll(IRemainingArguments remaining) 51 | { 52 | var examples = _finder.FindExamples(); 53 | foreach (var (_, first, _, example) in examples.Enumerate()) 54 | { 55 | if (!first) 56 | { 57 | _console.WriteLine(); 58 | } 59 | 60 | _console.Write(new Rule($"Example: [silver]{example.Name}[/]").LeftJustified().RuleStyle("grey")); 61 | 62 | var exitCode = await Run(example.Name, remaining).ConfigureAwait(false); 63 | if (exitCode != 0) 64 | { 65 | _console.MarkupLine($"[red]Error:[/] Example [u]{example.Name}[/] did not return a successful exit code."); 66 | return exitCode; 67 | } 68 | } 69 | 70 | return 0; 71 | } 72 | 73 | private async Task Build(ProjectInformation example) 74 | { 75 | var exitCode = await _console.Status().StartAsync($"Building example [yellow]{example.Name}[/]...", async ctx => 76 | { 77 | var cmd = Cli.Wrap("dotnet").WithArguments("build") 78 | .WithWorkingDirectory(example.GetWorkingDirectory().FullPath) 79 | .WithValidation(CommandResultValidation.None); 80 | 81 | await foreach (var cmdEvent in cmd.ListenAsync()) 82 | { 83 | switch (cmdEvent) 84 | { 85 | case StandardErrorCommandEvent stdErr: 86 | _console.MarkupLine($"[red]ERR>[/] {stdErr.Text.EscapeMarkup()}"); 87 | break; 88 | case ExitedCommandEvent exited: 89 | return exited.ExitCode; 90 | } 91 | } 92 | 93 | // Should never occur 94 | return -1; 95 | }).ConfigureAwait(false); 96 | 97 | if (exitCode != 0) 98 | { 99 | _console.MarkupLine($"[red]Error:[/] Could not build example [u]{example.Name}[/]"); 100 | } 101 | 102 | return exitCode == 0; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Example/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Example; 2 | 3 | public static class Program 4 | { 5 | public static async Task Main(string[] args) 6 | { 7 | var app = new CommandApp(); 8 | app.Configure(config => 9 | { 10 | config.SetApplicationName("dotnet example"); 11 | }); 12 | 13 | return await app.RunAsync(args).ConfigureAwait(false); 14 | } 15 | } 16 | 17 | public sealed class DefaultCommand : AsyncCommand 18 | { 19 | private readonly IAnsiConsole _console; 20 | private readonly IFileSystem _fileSystem; 21 | private readonly IEnvironment _environment; 22 | private readonly IGlobber _globber; 23 | 24 | public sealed class Settings : CommandSettings 25 | { 26 | [CommandArgument(0, "[EXAMPLE]")] 27 | [Description("The example to run.\nIf none is specified, all examples will be listed")] 28 | public string? Name { get; set; } 29 | 30 | [CommandOption("-l|--list")] 31 | [Description("Lists all available examples")] 32 | public bool List { get; set; } 33 | 34 | [CommandOption("-a|--all")] 35 | [Description("Runs all available examples")] 36 | public bool All { get; set; } 37 | 38 | [CommandOption("--skip")] 39 | [Description("Skips example when combined with [grey]--all[/]")] 40 | public string[]? Skip { get; set; } 41 | 42 | [CommandOption("-s|--source")] 43 | [Description("Show example source code")] 44 | public bool Source { get; set; } 45 | 46 | [CommandOption("--select")] 47 | [Description("Select an example from a list")] 48 | public bool Select { get; set; } 49 | } 50 | 51 | public DefaultCommand(IAnsiConsole console) 52 | { 53 | _console = console; 54 | _fileSystem = new FileSystem(); 55 | _environment = new Environment(); 56 | _globber = new Globber(_fileSystem, _environment); 57 | } 58 | 59 | public override async Task ExecuteAsync(CommandContext context, Settings settings, CancellationToken cancellationToken) 60 | { 61 | if (settings.All) 62 | { 63 | var finder = new ExampleFinder(_fileSystem, _environment, _globber, settings.Skip); 64 | var runner = new ExampleRunner(_console, finder); 65 | return await runner.RunAll(context.Remaining).ConfigureAwait(false); 66 | } 67 | else if (settings.Select) 68 | { 69 | var finder = new ExampleFinder(_fileSystem, _environment, _globber, settings.Skip); 70 | var selector = new ExampleSelector(_console, finder); 71 | 72 | var example = selector.Select(); 73 | if (example == null) 74 | { 75 | return -1; 76 | } 77 | 78 | if (settings.Source) 79 | { 80 | var sourceLister = new ExampleSourceLister(_fileSystem, _globber); 81 | if (!sourceLister.List(example)) 82 | { 83 | return -1; 84 | } 85 | 86 | return 0; 87 | } 88 | else 89 | { 90 | var runner = new ExampleRunner(_console, finder); 91 | return await runner.Run(example.Name, context.Remaining).ConfigureAwait(false); 92 | } 93 | } 94 | else if (settings.List || string.IsNullOrWhiteSpace(settings.Name)) 95 | { 96 | var finder = new ExampleFinder(_fileSystem, _environment, _globber, settings.Skip); 97 | 98 | // Only one example? 99 | var examples = finder.FindExamples(); 100 | if (examples.Count == 1) 101 | { 102 | // Execute it 103 | var runner = new ExampleRunner(_console, finder); 104 | return await runner.Run(examples[0].Name, context.Remaining).ConfigureAwait(false); 105 | } 106 | 107 | var lister = new ExampleLister(_console, finder); 108 | lister.List(); 109 | return 0; 110 | } 111 | else if (settings.Source) 112 | { 113 | var finder = new ExampleFinder(_fileSystem, _environment, _globber, settings.Skip); 114 | var example = finder.FindExample(settings.Name); 115 | if (example == null) 116 | { 117 | return -1; 118 | } 119 | 120 | var lister = new ExampleSourceLister(_fileSystem, _globber); 121 | if (!lister.List(example)) 122 | { 123 | return -1; 124 | } 125 | 126 | return 0; 127 | } 128 | else 129 | { 130 | var finder = new ExampleFinder(_fileSystem, _environment, _globber, settings.Skip); 131 | var runner = new ExampleRunner(_console, finder); 132 | return await runner.Run(settings.Name, context.Remaining).ConfigureAwait(false); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | root = false 2 | 3 | [*.cs] 4 | # Prefer file scoped namespace declarations 5 | csharp_style_namespace_declarations = file_scoped:warning 6 | 7 | # Sort using and Import directives with System.* appearing first 8 | dotnet_sort_system_directives_first = true 9 | dotnet_separate_import_directive_groups = false 10 | 11 | # Avoid "this." and "Me." if not necessary 12 | dotnet_style_qualification_for_field = false:refactoring 13 | dotnet_style_qualification_for_property = false:refactoring 14 | dotnet_style_qualification_for_method = false:refactoring 15 | dotnet_style_qualification_for_event = false:refactoring 16 | 17 | # Use language keywords instead of framework type names for type references 18 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 19 | dotnet_style_predefined_type_for_member_access = true:suggestion 20 | 21 | # Suggest more modern language features when available 22 | dotnet_style_object_initializer = true:suggestion 23 | dotnet_style_collection_initializer = true:suggestion 24 | dotnet_style_coalesce_expression = true:suggestion 25 | dotnet_style_null_propagation = true:suggestion 26 | dotnet_style_explicit_tuple_names = true:suggestion 27 | 28 | # Non-private static fields are PascalCase 29 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion 30 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields 31 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style 32 | dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field 33 | dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected 34 | dotnet_naming_symbols.non_private_static_fields.required_modifiers = static 35 | dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case 36 | 37 | # Non-private readonly fields are PascalCase 38 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion 39 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields 40 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style 41 | dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field 42 | dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected 43 | dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly 44 | dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case 45 | 46 | # Constants are PascalCase 47 | dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion 48 | dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants 49 | dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style 50 | dotnet_naming_symbols.constants.applicable_kinds = field, local 51 | dotnet_naming_symbols.constants.required_modifiers = const 52 | dotnet_naming_style.constant_style.capitalization = pascal_case 53 | 54 | # Instance fields are camelCase and start with _ 55 | dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion 56 | dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields 57 | dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style 58 | dotnet_naming_symbols.instance_fields.applicable_kinds = field 59 | dotnet_naming_style.instance_field_style.capitalization = camel_case 60 | dotnet_naming_style.instance_field_style.required_prefix = _ 61 | 62 | # Locals and parameters are camelCase 63 | dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion 64 | dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters 65 | dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style 66 | dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local 67 | dotnet_naming_style.camel_case_style.capitalization = camel_case 68 | 69 | # Local functions are PascalCase 70 | dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion 71 | dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions 72 | dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style 73 | dotnet_naming_symbols.local_functions.applicable_kinds = local_function 74 | dotnet_naming_style.local_function_style.capitalization = pascal_case 75 | 76 | # By default, name items with PascalCase 77 | dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion 78 | dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members 79 | dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style 80 | dotnet_naming_symbols.all_members.applicable_kinds = * 81 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 82 | 83 | # Newline settings 84 | csharp_new_line_before_open_brace = all 85 | csharp_new_line_before_else = true 86 | csharp_new_line_before_catch = true 87 | csharp_new_line_before_finally = true 88 | csharp_new_line_before_members_in_object_initializers = true 89 | csharp_new_line_before_members_in_anonymous_types = true 90 | csharp_new_line_between_query_expression_clauses = true 91 | 92 | # Indentation preferences 93 | csharp_indent_block_contents = true 94 | csharp_indent_braces = false 95 | csharp_indent_case_contents = true 96 | csharp_indent_case_contents_when_block = true 97 | csharp_indent_switch_labels = true 98 | csharp_indent_labels = flush_left 99 | 100 | # Prefer "var" everywhere 101 | csharp_style_var_for_built_in_types = true:suggestion 102 | csharp_style_var_when_type_is_apparent = true:suggestion 103 | csharp_style_var_elsewhere = true:suggestion 104 | 105 | # Prefer method-like constructs to have a block body 106 | csharp_style_expression_bodied_methods = false:none 107 | csharp_style_expression_bodied_constructors = false:none 108 | csharp_style_expression_bodied_operators = false:none 109 | 110 | # Prefer property-like constructs to have an expression-body 111 | csharp_style_expression_bodied_properties = true:none 112 | csharp_style_expression_bodied_indexers = true:none 113 | csharp_style_expression_bodied_accessors = true:none 114 | 115 | # Suggest more modern language features when available 116 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 117 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 118 | csharp_style_inlined_variable_declaration = true:suggestion 119 | csharp_style_throw_expression = true:suggestion 120 | csharp_style_conditional_delegate_call = true:suggestion 121 | 122 | # Space preferences 123 | csharp_space_after_cast = false 124 | csharp_space_after_colon_in_inheritance_clause = true 125 | csharp_space_after_comma = true 126 | csharp_space_after_dot = false 127 | csharp_space_after_keywords_in_control_flow_statements = true 128 | csharp_space_after_semicolon_in_for_statement = true 129 | csharp_space_around_binary_operators = before_and_after 130 | csharp_space_around_declaration_statements = do_not_ignore 131 | csharp_space_before_colon_in_inheritance_clause = true 132 | csharp_space_before_comma = false 133 | csharp_space_before_dot = false 134 | csharp_space_before_open_square_brackets = false 135 | csharp_space_before_semicolon_in_for_statement = false 136 | csharp_space_between_empty_square_brackets = false 137 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 138 | csharp_space_between_method_call_name_and_opening_parenthesis = false 139 | csharp_space_between_method_call_parameter_list_parentheses = false 140 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 141 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 142 | csharp_space_between_method_declaration_parameter_list_parentheses = false 143 | csharp_space_between_parentheses = false 144 | csharp_space_between_square_brackets = false 145 | 146 | # Blocks are allowed 147 | csharp_prefer_braces = true:silent 148 | csharp_preserve_single_line_blocks = true 149 | csharp_preserve_single_line_statements = true 150 | 151 | # RS0037: PublicAPI.txt is missing '#nullable enable' 152 | dotnet_diagnostic.RS0037.severity = none 153 | 154 | # IDE0055: Fix formatting 155 | dotnet_diagnostic.IDE0055.severity = warning 156 | 157 | # SA1101: Prefix local calls with this 158 | dotnet_diagnostic.SA1101.severity = none 159 | 160 | # SA1633: File should have header 161 | dotnet_diagnostic.SA1633.severity = none 162 | 163 | # SA1201: Elements should appear in the correct order 164 | dotnet_diagnostic.SA1201.severity = none 165 | 166 | # SA1202: Public members should come before private members 167 | dotnet_diagnostic.SA1202.severity = none 168 | 169 | # SA1309: Field names should not begin with underscore 170 | dotnet_diagnostic.SA1309.severity = none 171 | 172 | # SA1404: Code analysis suppressions should have justification 173 | dotnet_diagnostic.SA1404.severity = none 174 | 175 | # SA1516: Elements should be separated by a blank line 176 | dotnet_diagnostic.SA1516.severity = none 177 | 178 | # CA1303: Do not pass literals as localized parameters 179 | dotnet_diagnostic.CA1303.severity = none 180 | 181 | # CSA1204: Static members should appear before non-static members 182 | dotnet_diagnostic.SA1204.severity = none 183 | 184 | # IDE0052: Remove unread private members 185 | dotnet_diagnostic.IDE0052.severity = warning 186 | 187 | # IDE0063: Use simple 'using' statement 188 | csharp_prefer_simple_using_statement = false:suggestion 189 | 190 | # IDE0018: Variable declaration can be inlined 191 | dotnet_diagnostic.IDE0018.severity = warning 192 | 193 | # SA1625: Element documenation should not be copied and pasted 194 | dotnet_diagnostic.SA1625.severity = none 195 | 196 | # IDE0005: Using directive is unnecessary 197 | dotnet_diagnostic.IDE0005.severity = warning 198 | 199 | # SA1117: Parameters should be on same line or separate lines 200 | dotnet_diagnostic.SA1117.severity = none 201 | 202 | # SA1404: Code analysis suppression should have justification 203 | dotnet_diagnostic.SA1404.severity = none 204 | 205 | # SA1101: Prefix local calls with this 206 | dotnet_diagnostic.SA1101.severity = none 207 | 208 | # SA1633: File should have header 209 | dotnet_diagnostic.SA1633.severity = none 210 | 211 | # SA1649: File name should match first type name 212 | dotnet_diagnostic.SA1649.severity = none 213 | 214 | # SA1402: File may only contain a single type 215 | dotnet_diagnostic.SA1402.severity = none 216 | 217 | # CA1814: Prefer jagged arrays over multidimensional 218 | dotnet_diagnostic.CA1814.severity = none 219 | 220 | # RCS1194: Implement exception constructors. 221 | dotnet_diagnostic.RCS1194.severity = none 222 | 223 | # CA1032: Implement standard exception constructors 224 | dotnet_diagnostic.CA1032.severity = none 225 | 226 | # CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly 227 | dotnet_diagnostic.CA1826.severity = none 228 | 229 | # RCS1079: Throwing of new NotImplementedException. 230 | dotnet_diagnostic.RCS1079.severity = warning 231 | 232 | # RCS1057: Add empty line between declarations. 233 | dotnet_diagnostic.RCS1057.severity = none 234 | 235 | # RCS1057: Validate arguments correctly 236 | dotnet_diagnostic.RCS1227.severity = none 237 | 238 | # IDE0004: Remove Unnecessary Cast 239 | dotnet_diagnostic.IDE0004.severity = warning 240 | 241 | # CA1810: Initialize reference type static fields inline 242 | dotnet_diagnostic.CA1810.severity = none 243 | 244 | # IDE0044: Add readonly modifier 245 | dotnet_diagnostic.IDE0044.severity = warning 246 | 247 | # RCS1047: Non-asynchronous method name should not end with 'Async'. 248 | dotnet_diagnostic.RCS1047.severity = none 249 | 250 | # RCS1090: Call 'ConfigureAwait(false)'. 251 | dotnet_diagnostic.RCS1090.severity = warning 252 | 253 | # SA1633: The file header is missing or not located at the top of the file 254 | dotnet_diagnostic.SA1633.severity = none 255 | 256 | # CA2016: Forward the CancellationToken parameter to methods that take one 257 | dotnet_diagnostic.CA2016.severity = warning --------------------------------------------------------------------------------