From 4d9619595e79101fb1785b56780df31e1fbfb94b Mon Sep 17 00:00:00 2001 From: Adeel Date: Sun, 30 Aug 2020 02:53:34 +0300 Subject: [PATCH] Read --{in,ex}clude values from stdin Today, `dotnet-format` accepts space-separated list of files. This allows us to pass files via shell's native globbing expansion, heredoc style redirections as well as via pipelines (see #551 for examples). However, in case of pipeline it requires a second utility (`xarg`) to transform pipeline input as space-separated list to `dotnet-format`. This PR implements native pipeline reading support for `--include` and `--exclude` options, while keeping the space-separated list intact. Usage examples: 1. Include all C# source files under `tests/Utilities` directory that are in git source tree: ```sh git ls-files :/tests/Utilities/*.cs | dotnet format --include - --folder ``` 2. Exclude certain `*.cs` and `*.vb` files using `ls(1)`: ```sh ls ../../../../../src/generated/{*.cs,*.vb} | dotnet format --exclude /dev/stdin --folder ``` Rules: * Based on Guideline 13 of [IEEE 1003.1-2017](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02) (POSIX), it accepts `-` as an explicit marker for stdin with addition of: * `/dev/stdin` - which is a universal synonym of `-` on all Unices. * It *only* accepts explicit markers (`/dev/stdin` or `-`) and does not implicit deduce the output if standard input was redirected, but marker was not present. This is because our usage is multi-purpose (both `--include` and `--exclude`). * It is an error if both `--include` and `--exclude` are using stdin marker (`/dev/stdin` or `-`). Limitations: * Currently, it reads the entire input from pipeline in `include`/`exclude` buffer, and then runs the operation on the whole batch. In order to make the pipele , it would require some refactorings; so files `yield return` and enumerator dispatches format operation per file. * At present, we do not have out-of-process functional tests for CLI to effectively validate these kind of use-cases (redirection and shell globbing vs. dotnet-format's built-in globbing support); so PR did not included any new mechanism. --- README.md | 21 ++++++++------ src/Program.cs | 54 +++++++++++++++++++++++++++++++++++ src/Resources.resx | 3 ++ src/xlf/Resources.cs.xlf | 5 ++++ src/xlf/Resources.de.xlf | 5 ++++ src/xlf/Resources.es.xlf | 5 ++++ src/xlf/Resources.fr.xlf | 5 ++++ src/xlf/Resources.it.xlf | 5 ++++ src/xlf/Resources.ja.xlf | 5 ++++ src/xlf/Resources.ko.xlf | 5 ++++ src/xlf/Resources.pl.xlf | 5 ++++ src/xlf/Resources.pt-BR.xlf | 5 ++++ src/xlf/Resources.ru.xlf | 5 ++++ src/xlf/Resources.tr.xlf | 5 ++++ src/xlf/Resources.zh-Hans.xlf | 5 ++++ src/xlf/Resources.zh-Hant.xlf | 5 ++++ 16 files changed, 134 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6be2572278..269fc2ef72 100644 --- a/README.md +++ b/README.md @@ -86,15 +86,18 @@ Add `format` after `dotnet` and before the command arguments that you want to ru | Examples | Description | | ---------------------------------------------------------- |---------------------------------------------------------------------------------------------- | -| dotnet **format** | Formats the project or solution in the current directory. | -| dotnet **format** <workspace> | Formats a specific project or solution. | -| dotnet **format** <workspace> -f | Formats a particular folder and subfolders. | -| dotnet **format** <workspace> --fix-style warn | Formats and fixes codestyle analyzer warnings. | -| dotnet **format** <workspace> --fix-analyzers | Formats and fixes 3rd party analyzer errors. | -| dotnet **format** -v diag | Formats with very verbose logging. | -| dotnet **format** --include Programs.cs Utility\Logging.cs | Formats the files Program.cs and Utility\Logging.cs | -| dotnet **format** --check | Formats but does not save. Returns a non-zero exit code if any files would have been changed. | -| dotnet **format** --report <report-path> | Formats and saves a json report file to the given directory. | +| `dotnet format` | Formats the project or solution in the current directory. | +| `dotnet format ` | Formats a specific project or solution. | +| `dotnet format -f` | Formats a particular folder and subfolders. | +| `dotnet format --fix-style warn` | Formats and fixes codestyle analyzer warnings. | +| `dotnet format --fix-analyzers` | Formats and fixes 3rd party analyzer errors. | +| `dotnet format -v diag` | Formats with very verbose logging. | +| `dotnet format --include Programs.cs Utility\Logging.cs` | Formats the files Program.cs and Utility\Logging.cs | +| `dotnet format --check` | Formats but does not save. Returns a non-zero exit code if any files would have been changed. | +| `dotnet format --report ` | Formats and saves a json report file to the given directory. | +| `dotnet format --include test/Utilities/*.cs --folder` | Formats the files expanded from native shell globbing (e.g. bash). Space-separated list of files are fed to formatter in this case. Also applies to `--exclude` option. | +| `dotnet format --include 'test/Utilities/*.cs' --folder` | With single quotes, formats the files expanded from built-in glob expansion. A single file pattern is fed to formatter, which gets expanded internally. Also applies to `--exclude` option. | +| `ls tests/Utilities/*.cs \| dotnet format --include - --folder` | Formats the list of files redirected from pipeline via standard input. Formatter will iterate over `Console.In` to read the list of files. Also applies to `--exclude` option. | ### How To Uninstall diff --git a/src/Program.cs b/src/Program.cs index ec0706babd..d1770807d1 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Tools.Logging; @@ -24,6 +25,8 @@ internal class Program internal const int UnableToLocateMSBuildExitCode = 3; internal const int UnableToLocateDotNetCliExitCode = 4; + private static readonly string[] s_standardInputKeywords = { "/dev/stdin", "-" }; + private static ParseResult? s_parseResult; private static async Task Main(string[] args) @@ -131,6 +134,8 @@ public static async Task Run( logger.LogTrace(Resources.Using_msbuildexe_located_in_0, msBuildPath); } + HandleStandardInput(logger, ref include, ref exclude); + var fileMatcher = SourceFileMatcher.CreateMatcher(include, exclude); var formatOptions = new FormatOptions( @@ -173,6 +178,55 @@ public static async Task Run( } } + private static int HandleStandardInput(ILogger logger, ref string[] include, ref string[] exclude) + { + bool isStandardMarkerUsed = false; + if (include.Length == 1 && s_standardInputKeywords.Contains(include[0])) + { + if (TryReadFromStandardInput(ref include)) + { + isStandardMarkerUsed = true; + } + } + + if (exclude.Length == 1 && s_standardInputKeywords.Contains(exclude[0])) + { + if (isStandardMarkerUsed) + { + logger.LogCritical(Resources.Standard_input_used_multiple_times); + return CheckFailedExitCode; + } + + TryReadFromStandardInput(ref exclude); + } + + return 0; + + static bool TryReadFromStandardInput(ref string[] subject) + { + if (!Console.IsInputRedirected) + { + return false; // pass + } + + // reset the subject array + Array.Clear(subject, 0, subject.Length); + Array.Resize(ref subject, 0); + + Console.InputEncoding = Encoding.UTF8; + using var reader = new StreamReader(Console.OpenStandardInput(8192)); + Console.SetIn(reader); + + for (int i = 0; Console.In.Peek() != -1; ++i) + { + Array.Resize(ref subject, subject.Length + 1); + subject[i] = Console.In.ReadLine(); + } + + return true; + } + } + internal static int GetExitCode(WorkspaceFormatResult formatResult, bool check) { if (!check) diff --git a/src/Resources.resx b/src/Resources.resx index ef868dbf65..f9f6d8592a 100644 --- a/src/Resources.resx +++ b/src/Resources.resx @@ -222,6 +222,9 @@ Include generated code files in formatting operations. + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Unable to locate dotnet CLI. Ensure that it is on the PATH. diff --git a/src/xlf/Resources.cs.xlf b/src/xlf/Resources.cs.xlf index 13110674ee..ade5dc794e 100644 --- a/src/xlf/Resources.cs.xlf +++ b/src/xlf/Resources.cs.xlf @@ -202,6 +202,11 @@ Řešení {0} nemá žádné projekty. + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. Verze .NET CLI je {0}. diff --git a/src/xlf/Resources.de.xlf b/src/xlf/Resources.de.xlf index bfd5d3c6a2..fc4d9dfe11 100644 --- a/src/xlf/Resources.de.xlf +++ b/src/xlf/Resources.de.xlf @@ -202,6 +202,11 @@ Die Projektmappe "{0}" enthält keine Projekte. + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. Die dotnet-CLI-Version ist "{0}". diff --git a/src/xlf/Resources.es.xlf b/src/xlf/Resources.es.xlf index 3248f7dd9e..0e6ed36f42 100644 --- a/src/xlf/Resources.es.xlf +++ b/src/xlf/Resources.es.xlf @@ -202,6 +202,11 @@ La solución {0} no tiene ningún proyecto. + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. La versión de la CLI de dotnet es "{0}". diff --git a/src/xlf/Resources.fr.xlf b/src/xlf/Resources.fr.xlf index b10190b87b..7c821bfea5 100644 --- a/src/xlf/Resources.fr.xlf +++ b/src/xlf/Resources.fr.xlf @@ -202,6 +202,11 @@ La solution {0} n'a aucun projet + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. La version de l'interface CLI dotnet est '{0}'. diff --git a/src/xlf/Resources.it.xlf b/src/xlf/Resources.it.xlf index 789423bc99..338cf33106 100644 --- a/src/xlf/Resources.it.xlf +++ b/src/xlf/Resources.it.xlf @@ -202,6 +202,11 @@ La soluzione {0} non contiene progetti + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. La versione dell'interfaccia della riga di comando di dotnet è '{0}'. diff --git a/src/xlf/Resources.ja.xlf b/src/xlf/Resources.ja.xlf index cc23cdba3a..0977116e73 100644 --- a/src/xlf/Resources.ja.xlf +++ b/src/xlf/Resources.ja.xlf @@ -202,6 +202,11 @@ ソリューション {0} にプロジェクトがありません + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. dotnet CLI バージョンは '{0}' です。 diff --git a/src/xlf/Resources.ko.xlf b/src/xlf/Resources.ko.xlf index a11d90ff93..4fb502fb61 100644 --- a/src/xlf/Resources.ko.xlf +++ b/src/xlf/Resources.ko.xlf @@ -202,6 +202,11 @@ {0} 솔루션에 프로젝트가 없습니다. + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. dotnet CLI 버전은 '{0}'입니다. diff --git a/src/xlf/Resources.pl.xlf b/src/xlf/Resources.pl.xlf index 1766abb776..c5bbae0f78 100644 --- a/src/xlf/Resources.pl.xlf +++ b/src/xlf/Resources.pl.xlf @@ -202,6 +202,11 @@ Rozwiązanie {0} nie zawiera projektów + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. Wersja interfejsu wiersza polecenia dotnet to „{0}”. diff --git a/src/xlf/Resources.pt-BR.xlf b/src/xlf/Resources.pt-BR.xlf index c542f75de1..7c21de1c7f 100644 --- a/src/xlf/Resources.pt-BR.xlf +++ b/src/xlf/Resources.pt-BR.xlf @@ -202,6 +202,11 @@ A solução {0} não tem nenhum projeto + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. A versão do CLI do dotnet é '{0}'. diff --git a/src/xlf/Resources.ru.xlf b/src/xlf/Resources.ru.xlf index 2bf8c0720e..2d62e733db 100644 --- a/src/xlf/Resources.ru.xlf +++ b/src/xlf/Resources.ru.xlf @@ -202,6 +202,11 @@ Решение {0} не содержит проектов. + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. Версия CLI dotnet: "{0}". diff --git a/src/xlf/Resources.tr.xlf b/src/xlf/Resources.tr.xlf index b661757b0a..8d85b47850 100644 --- a/src/xlf/Resources.tr.xlf +++ b/src/xlf/Resources.tr.xlf @@ -202,6 +202,11 @@ {0} çözümünde proje yok + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. dotnet CLI sürümü: '{0}'. diff --git a/src/xlf/Resources.zh-Hans.xlf b/src/xlf/Resources.zh-Hans.xlf index 65a80b3783..3956a264d5 100644 --- a/src/xlf/Resources.zh-Hans.xlf +++ b/src/xlf/Resources.zh-Hans.xlf @@ -202,6 +202,11 @@ 解决方案 {0} 不包含任何项目 + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. dotnet CLI 版本为“{0}”。 diff --git a/src/xlf/Resources.zh-Hant.xlf b/src/xlf/Resources.zh-Hant.xlf index 4d4ff474a7..b2917b5d44 100644 --- a/src/xlf/Resources.zh-Hant.xlf +++ b/src/xlf/Resources.zh-Hant.xlf @@ -202,6 +202,11 @@ 解決方案 {0} 沒有任何專案 + + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + Standard input markers ('/dev/stdin', '-') can only be used either with `--include` or `--exclude`, but not both. + + The dotnet CLI version is '{0}'. dotnet CLI 版本為 '{0}'。