diff --git a/src/Framework/Logging/AnsiCodes.cs b/src/Framework/Logging/AnsiCodes.cs index 016260d55f8..8466220026b 100644 --- a/src/Framework/Logging/AnsiCodes.cs +++ b/src/Framework/Logging/AnsiCodes.cs @@ -141,4 +141,13 @@ public static string MakeBold(string? s) return $"{CSI}{SetBold}{s}{SetDefaultColor}"; } + + public static string MoveCursorBackward(int count) => $"{CSI}{count}{MoveBackward}"; + + /// + /// Moves cursor to the specified column, or the rightmost column if is greater than the width of the terminal. + /// + /// Column index. + /// Control codes to set the desired position. + public static string SetCursorHorizontal(int column) => $"{CSI}{column}G"; } diff --git a/src/MSBuild.UnitTests/NodeStatus_Tests.cs b/src/MSBuild.UnitTests/NodeStatus_Tests.cs new file mode 100644 index 00000000000..50ae7b38a51 --- /dev/null +++ b/src/MSBuild.UnitTests/NodeStatus_Tests.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Microsoft.Build.Logging.TerminalLogger; + +using VerifyTests; +using VerifyXunit; +using Xunit; + +using static VerifyXunit.Verifier; + + +namespace Microsoft.Build.CommandLine.UnitTests; + +[UsesVerify] +public class NodeStatus_Tests +{ + private readonly NodeStatus _status = new("Namespace.Project", "TargetFramework", "Target", new()); + + public NodeStatus_Tests() + { + UseProjectRelativeDirectory("Snapshots"); + } + + [Fact] + public async Task EverythingFits() + { + NodesFrame frame = new(new[] { _status }, width: 80, height: 5); + + await Verify(frame.RenderNodeStatus(_status).ToString()); + } + + [Fact] + public async Task TargetIsTruncatedFirst() + { + NodesFrame frame = new(new[] { _status }, width: 45, height: 5); + + await Verify(frame.RenderNodeStatus(_status).ToString()); + } + + [Fact] + public async Task NamespaceIsTruncatedNext() + { + NodesFrame frame = new(new[] { _status }, width: 40, height: 5); + + await Verify(frame.RenderNodeStatus(_status).ToString()); + } + + [Fact] + public async Task GoesToProject() + { + NodesFrame frame = new(new[] { _status }, width: 10, height: 5); + + await Verify(frame.RenderNodeStatus(_status).ToString()); + } +} diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.EverythingFits.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.EverythingFits.verified.txt new file mode 100644 index 00000000000..a889f734e14 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.EverythingFits.verified.txt @@ -0,0 +1 @@ + Namespace.Project TargetFramework Target (0.0s) \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.GoesToProject.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.GoesToProject.verified.txt new file mode 100644 index 00000000000..74eb4993b40 --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.GoesToProject.verified.txt @@ -0,0 +1 @@ +Project \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt new file mode 100644 index 00000000000..a06cd82177c --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.NamespaceIsTruncatedNext.verified.txt @@ -0,0 +1 @@ + Project TargetFramework  (0.0s) \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.TargetIsTruncatedFirst.verified.txt b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.TargetIsTruncatedFirst.verified.txt new file mode 100644 index 00000000000..014bb0cb3be --- /dev/null +++ b/src/MSBuild.UnitTests/Snapshots/NodeStatus_Tests.TargetIsTruncatedFirst.verified.txt @@ -0,0 +1 @@ + Namespace.Project TargetFramework  (0.0s) \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.Linux.verified.txt index 0777ef5cc91..fe12f4e9dec 100644 --- a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.Linux.verified.txt +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.Linux.verified.txt @@ -1,5 +1,5 @@ ]9;4;3;\[?25l - project tfName Build (0.0s) + project tfName Build (0.0s) [?25h[?25l - project tf2 Build (0.0s) + project tf2 Build (0.0s) [?25h \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.OSX.verified.txt index d860724e3ab..8d078e61f5c 100644 --- a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.OSX.verified.txt +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.OSX.verified.txt @@ -1,5 +1,5 @@ [?25l - project tfName Build (0.0s) + project tfName Build (0.0s) [?25h[?25l - project tf2 Build (0.0s) + project tf2 Build (0.0s) [?25h \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.Windows.verified.txt index 0777ef5cc91..fe12f4e9dec 100644 --- a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.Windows.verified.txt +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesOverwritesWithNewTargetFramework.Windows.verified.txt @@ -1,5 +1,5 @@ ]9;4;3;\[?25l - project tfName Build (0.0s) + project tfName Build (0.0s) [?25h[?25l - project tf2 Build (0.0s) + project tf2 Build (0.0s) [?25h \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.Linux.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.Linux.verified.txt index d0cb5b914e0..1f7b782f2ef 100644 --- a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.Linux.verified.txt +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.Linux.verified.txt @@ -1,3 +1,3 @@ ]9;4;3;\[?25l - project Build (0.0s) + project Build (0.0s) [?25h \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.OSX.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.OSX.verified.txt index edce93c06c4..143745dea40 100644 --- a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.OSX.verified.txt +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.OSX.verified.txt @@ -1,3 +1,3 @@ [?25l - project Build (0.0s) + project Build (0.0s) [?25h \ No newline at end of file diff --git a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.Windows.verified.txt b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.Windows.verified.txt index d0cb5b914e0..1f7b782f2ef 100644 --- a/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.Windows.verified.txt +++ b/src/MSBuild.UnitTests/Snapshots/TerminalLogger_Tests.DisplayNodesShowsCurrent.Windows.verified.txt @@ -1,3 +1,3 @@ ]9;4;3;\[?25l - project Build (0.0s) + project Build (0.0s) [?25h \ No newline at end of file diff --git a/src/MSBuild/Resources/Strings.resx b/src/MSBuild/Resources/Strings.resx index a753a09c5b6..98c41c4823c 100644 --- a/src/MSBuild/Resources/Strings.resx +++ b/src/MSBuild/Resources/Strings.resx @@ -1536,26 +1536,11 @@ {0}: VT100 coded hyperlink to project output directory - - {0}{1} {2} ({3}s) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - + + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + Build failed. Properties, Items, and Target results cannot be obtained. See details in stderr above. diff --git a/src/MSBuild/Resources/xlf/Strings.cs.xlf b/src/MSBuild/Resources/xlf/Strings.cs.xlf index 9a4dee64212..55929f8714c 100644 --- a/src/MSBuild/Resources/xlf/Strings.cs.xlf +++ b/src/MSBuild/Resources/xlf/Strings.cs.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: Přepínač -noAutoResponse nelze zadat v souboru automatických odpovědí MSBuild.rsp ani v žádném jiném souboru odpovědí, na který se v souboru automatických odpovědí odkazuje. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1488,29 +1495,6 @@ Když se nastaví na MessageUponIsolationViolation (nebo jeho krátký Proces = {0} - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}s) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}s) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}s) diff --git a/src/MSBuild/Resources/xlf/Strings.de.xlf b/src/MSBuild/Resources/xlf/Strings.de.xlf index 0a93c2b4b6d..226e0960175 100644 --- a/src/MSBuild/Resources/xlf/Strings.de.xlf +++ b/src/MSBuild/Resources/xlf/Strings.de.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: Der Schalter "-noAutoResponse" kann weder in der automatischen Antwortdatei "MSBuild.rsp" noch in einer anderen Antwortdatei verwendet werden, auf die die automatische Antwortdatei verweist. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1476,29 +1483,6 @@ Dieses Protokollierungsformat ist standardmäßig aktiviert. Prozess = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}s) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}s) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}s) diff --git a/src/MSBuild/Resources/xlf/Strings.es.xlf b/src/MSBuild/Resources/xlf/Strings.es.xlf index cfa87dc7547..1846d8646a1 100644 --- a/src/MSBuild/Resources/xlf/Strings.es.xlf +++ b/src/MSBuild/Resources/xlf/Strings.es.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: El modificador -noAutoResponse no puede especificarse en el archivo de respuesta automática MSBuild.rsp ni en ningún archivo de respuesta al que el archivo de respuesta automática haga referencia. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1482,29 +1489,6 @@ Esta marca es experimental y puede que no funcione según lo previsto. Proceso: "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}s) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}s) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}s) diff --git a/src/MSBuild/Resources/xlf/Strings.fr.xlf b/src/MSBuild/Resources/xlf/Strings.fr.xlf index b4b2ae8bfde..f4d88741a76 100644 --- a/src/MSBuild/Resources/xlf/Strings.fr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.fr.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: Impossible de spécifier le commutateur -noAutoResponse dans le fichier réponse automatique MSBuild.rsp, ni dans aucun autre fichier réponse référencé par le fichier réponse automatique. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1475,29 +1482,6 @@ Remarque : verbosité des enregistreurs d’événements de fichiers Processus = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}s) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}s) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}s) diff --git a/src/MSBuild/Resources/xlf/Strings.it.xlf b/src/MSBuild/Resources/xlf/Strings.it.xlf index d5b83a7548a..12e7aa2ac94 100644 --- a/src/MSBuild/Resources/xlf/Strings.it.xlf +++ b/src/MSBuild/Resources/xlf/Strings.it.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: non è possibile specificare l'opzione -noAutoResponse nel file di risposta automatica MSBuild.rsp o in file di risposta a cui il file di risposta automatica fa riferimento. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1486,29 +1493,6 @@ Nota: livello di dettaglio dei logger di file Processo = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}s) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}s) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}s) diff --git a/src/MSBuild/Resources/xlf/Strings.ja.xlf b/src/MSBuild/Resources/xlf/Strings.ja.xlf index b10dcf784c2..cd05368111a 100644 --- a/src/MSBuild/Resources/xlf/Strings.ja.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ja.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: MSBuild.rsp 自動応答ファイルや、自動応答ファイルによって参照される応答ファイルに -noAutoResponse スイッチを指定することはできません。 {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1475,29 +1482,6 @@ プロセス = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3} 秒) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4} 秒) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3} 秒) diff --git a/src/MSBuild/Resources/xlf/Strings.ko.xlf b/src/MSBuild/Resources/xlf/Strings.ko.xlf index d6d76449a65..dfa81b71e90 100644 --- a/src/MSBuild/Resources/xlf/Strings.ko.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ko.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: MSBuild.rsp 자동 지시 파일과 자동 지시 파일에서 참조하는 모든 지시 파일에는 -noAutoResponse 스위치를 지정할 수 없습니다. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1475,29 +1482,6 @@ 프로세스 = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}초) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}초) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}초) diff --git a/src/MSBuild/Resources/xlf/Strings.pl.xlf b/src/MSBuild/Resources/xlf/Strings.pl.xlf index 2a48d1d5450..133760011df 100644 --- a/src/MSBuild/Resources/xlf/Strings.pl.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pl.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: przełącznika -noAutoResponse nie można określić w pliku autoodpowiedzi MSBuild.rsp ani w żadnym pliku odpowiedzi, do którego odwołuje się plik autoodpowiedzi. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1486,29 +1493,6 @@ Ta flaga jest eksperymentalna i może nie działać zgodnie z oczekiwaniami. Proces = „{0}” - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}s) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}s) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}s) diff --git a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf index 15c9b624a84..247e14490ef 100644 --- a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: A opção /noAutoResponse não pode ser especificada no arquivo de resposta automática MSBuild.rsp nem em qualquer arquivo de resposta usado como referência para o arquivo de resposta automática. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1476,29 +1483,6 @@ arquivo de resposta. Processo = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}s) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}s) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}s) diff --git a/src/MSBuild/Resources/xlf/Strings.ru.xlf b/src/MSBuild/Resources/xlf/Strings.ru.xlf index 6dca92a8e84..1d89ce20f45 100644 --- a/src/MSBuild/Resources/xlf/Strings.ru.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ru.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: ключ noAutoResponse не может быть указан в файле автоответа MSBuild.rsp или в любом другом файле ответа, на который файл автоответа ссылается. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1474,29 +1481,6 @@ Процесс = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3} с) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4} с) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3} с) diff --git a/src/MSBuild/Resources/xlf/Strings.tr.xlf b/src/MSBuild/Resources/xlf/Strings.tr.xlf index ff6f95f2eae..d3fc5c7e9ed 100644 --- a/src/MSBuild/Resources/xlf/Strings.tr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.tr.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: -noAutoResponse anahtarı, MSBuild.rsp otomatik yanıt dosyasında ve bu dosyanın başvuruda bulunduğu herhangi bir yanıt dosyasında belirtilemez. {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1479,29 +1486,6 @@ İşlem = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}sn) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3}({4}sn) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}sn) diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf index db252831300..12f6f98c04d 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: 不能在 MSBuild.rsp 自动响应文件中或由该自动响应文件引用的任何响应文件中指定 -noAutoResponse 开关。 {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1475,29 +1482,6 @@ 进程 = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3}) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4}) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3}) diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf index f5907cf51e5..b603ad790a6 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf @@ -77,6 +77,13 @@ MSBUILD : error MSB1027: -noAutoResponse 參數不能在 MSBuild.rsp 自動回應檔中指定,也不能在自動回應檔所參考的任何回應檔中指定。 {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. + + ({0:F1}s) + ({0:F1}s) + + {0}: duration in seconds with 1 decimal point + + -question (Experimental) Question whether there is any build work. @@ -1475,29 +1482,6 @@ 流程 = "{0}" - - {0}{1} {2} ({3}s) - {0}{1} {2} ({3} 秒) - - Project building. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target - {3}: duration in seconds with 1 decimal point - - - - {0}{1} {2} {3} ({4}s) - {0}{1} {2} {3} ({4} 秒) - - Project building including target framework information. - {0}: indentation - few spaces to visually indent row - {1}: project name - {2}: target framework - {3}: target - {4}: duration in seconds with 1 decimal point - - {0}{1} {2} ({3}s) {0}{1} {2} ({3} 秒) diff --git a/src/MSBuild/TerminalLogger/NodeStatus.cs b/src/MSBuild/TerminalLogger/NodeStatus.cs new file mode 100644 index 00000000000..c82a3d106d7 --- /dev/null +++ b/src/MSBuild/TerminalLogger/NodeStatus.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.Logging.TerminalLogger; + +/// +/// Encapsulates the per-node data shown in live node output. +/// +internal class NodeStatus +{ + public string Project { get; } + public string? TargetFramework { get; } + public string Target { get; } + public Stopwatch Stopwatch { get; } + + public NodeStatus(string project, string? targetFramework, string target, Stopwatch stopwatch) + { + Project = project; + TargetFramework = targetFramework; + Target = target; + Stopwatch = stopwatch; + } + + /// + /// Equality is based on the project, target framework, and target, but NOT the elapsed time. + /// + public override bool Equals(object? obj) => + obj is NodeStatus status && + Project == status.Project && + TargetFramework == status.TargetFramework && + Target == status.Target; + + public override string ToString() + { + string duration = Stopwatch.Elapsed.TotalSeconds.ToString("F1"); + + return string.IsNullOrEmpty(TargetFramework) + ? string.Format("{0}{1} {2} ({3}s)", + TerminalLogger.Indentation, + Project, + Target, + duration) + : string.Format("{0}{1} {2} {3} ({4}s)", + TerminalLogger.Indentation, + Project, + AnsiCodes.Colorize(TargetFramework, TerminalLogger.TargetFrameworkColor), + Target, + duration); + } + + public override int GetHashCode() + { + throw new System.NotImplementedException(); + } +} diff --git a/src/MSBuild/TerminalLogger/NodesFrame.cs b/src/MSBuild/TerminalLogger/NodesFrame.cs new file mode 100644 index 00000000000..144288950fa --- /dev/null +++ b/src/MSBuild/TerminalLogger/NodesFrame.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using System.Text; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.Logging.TerminalLogger; + +/// +/// Capture states on nodes to be rendered on display. +/// +internal sealed class NodesFrame +{ + private const int MaxColumn = 120; + + private readonly NodeStatus[] _nodes; + + private readonly StringBuilder _renderBuilder = new(); + + public int Width { get; } + public int Height { get; } + public int NodesCount { get; private set; } + + public NodesFrame(NodeStatus?[] nodes, int width, int height) + { + Width = Math.Min(width, MaxColumn); + Height = height; + + _nodes = new NodeStatus[nodes.Length]; + + foreach (NodeStatus? status in nodes) + { + if (status is not null) + { + _nodes[NodesCount++] = status; + } + } + } + + internal ReadOnlySpan RenderNodeStatus(NodeStatus status) + { + string durationString = ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword( + "DurationDisplay", + status.Stopwatch.Elapsed.TotalSeconds); + + string project = status.Project; + string? targetFramework = status.TargetFramework; + string target = status.Target; + + int renderedWidth = Length(durationString, project, targetFramework, target); + + if (renderedWidth > Width) + { + renderedWidth -= target.Length; + target = string.Empty; + + if (renderedWidth > Width) + { + int lastDotInProject = project.LastIndexOf('.'); + renderedWidth -= lastDotInProject; + project = project.Substring(lastDotInProject + 1); + + if (renderedWidth > Width) + { + return project.AsSpan(); + } + } + } + + return $"{TerminalLogger.Indentation}{project}{(targetFramework is null ? string.Empty : " ")}{AnsiCodes.Colorize(targetFramework, TerminalLogger.TargetFrameworkColor)} {AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(target.Length + durationString.Length + 1)}{target} {durationString}".AsSpan(); + + static int Length(string durationString, string project, string? targetFramework, string target) => + TerminalLogger.Indentation.Length + + project.Length + 1 + + (targetFramework?.Length ?? -1) + 1 + + target.Length + 1 + + durationString.Length; + } + + /// + /// Render VT100 string to update from current to next frame. + /// + public string Render(NodesFrame previousFrame) + { + StringBuilder sb = _renderBuilder; + sb.Clear(); + + int i = 0; + for (; i < NodesCount; i++) + { + ReadOnlySpan needed = RenderNodeStatus(_nodes[i]); + + // Do we have previous node string to compare with? + if (previousFrame.NodesCount > i) + { + if (previousFrame._nodes[i] == _nodes[i]) + { + // Same everything except time + string durationString = ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("DurationDisplay", _nodes[i].Stopwatch.Elapsed.TotalSeconds); + sb.Append($"{AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(durationString.Length)}{durationString}"); + } + else + { + // TODO: check components to figure out skips and optimize this + sb.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInLine}"); + sb.Append(needed); + } + } + else + { + // From now on we have to simply WriteLine + sb.Append(needed); + } + + // Next line + sb.AppendLine(); + } + + // clear no longer used lines + if (i < previousFrame.NodesCount) + { + sb.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInDisplay}"); + } + + return sb.ToString(); + } + + public void Clear() + { + NodesCount = 0; + } +} diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index ba0e8abf043..221d6e4e4fa 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -1,11 +1,9 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; -using System.Text; using System.Threading; using Microsoft.Build.Framework; using Microsoft.Build.Shared; @@ -52,36 +50,12 @@ public ProjectContext(BuildEventContext context) { } } - /// - /// Encapsulates the per-node data shown in live node output. - /// - internal record NodeStatus(string Project, string? TargetFramework, string Target, Stopwatch Stopwatch) - { - public override string ToString() - { - string duration = Stopwatch.Elapsed.TotalSeconds.ToString("F1"); - - return string.IsNullOrEmpty(TargetFramework) - ? ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("ProjectBuilding_NoTF", - Indentation, - Project, - Target, - duration) - : ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("ProjectBuilding_WithTF", - Indentation, - Project, - AnsiCodes.Colorize(TargetFramework, TargetFrameworkColor), - Target, - duration); - } - } - /// /// The indentation to use for all build output. /// - private const string Indentation = " "; + internal const string Indentation = " "; - private const TerminalColor TargetFrameworkColor = TerminalColor.Cyan; + internal const TerminalColor TargetFrameworkColor = TerminalColor.Cyan; /// /// Protects access to state shared between the logger callbacks and the rendering thread. @@ -221,7 +195,7 @@ public string Parameters /// public void Initialize(IEventSource eventSource, int nodeCount) { - // When MSBUILDNOINPROCNODE enabled, NodeId's reported by build start with 2. We need to reserve an extra spot for this case. + // When MSBUILDNOINPROCNODE enabled, NodeId's reported by build start with 2. We need to reserve an extra spot for this case. _nodes = new NodeStatus[nodeCount + 1]; Initialize(eventSource); @@ -745,137 +719,6 @@ private void EraseNodes() _currentFrame.Clear(); } - /// - /// Capture states on nodes to be rendered on display. - /// - private sealed class NodesFrame - { - private readonly List _nodeStrings = new(); - private readonly StringBuilder _renderBuilder = new(); - - public int Width { get; } - public int Height { get; } - public int NodesCount { get; private set; } - - public NodesFrame(NodeStatus?[] nodes, int width, int height) - { - Width = width; - Height = height; - Init(nodes); - } - - public string NodeString(int index) - { - if (index >= NodesCount) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - return _nodeStrings[index]; - } - - private void Init(NodeStatus?[] nodes) - { - int i = 0; - foreach (NodeStatus? n in nodes) - { - if (n is null) - { - continue; - } - string str = n.ToString(); - - if (i < _nodeStrings.Count) - { - _nodeStrings[i] = str; - } - else - { - _nodeStrings.Add(str); - } - i++; - - // We cant output more than what fits on screen - // -2 because cursor command F cant reach, in Windows Terminal, very 1st line, and last line is empty caused by very last WriteLine - if (i >= Height - 2) - { - break; - } - } - - NodesCount = i; - } - - private ReadOnlySpan FitToWidth(ReadOnlySpan input) - { - return input.Slice(0, Math.Min(input.Length, Width - 1)); - } - - /// - /// Render VT100 string to update from current to next frame. - /// - public string Render(NodesFrame previousFrame) - { - StringBuilder sb = _renderBuilder; - sb.Clear(); - - int i = 0; - for (; i < NodesCount; i++) - { - var needed = FitToWidth(NodeString(i).AsSpan()); - - // Do we have previous node string to compare with? - if (previousFrame.NodesCount > i) - { - var previous = FitToWidth(previousFrame.NodeString(i).AsSpan()); - - if (!previous.SequenceEqual(needed)) - { - int commonPrefixLen = previous.CommonPrefixLength(needed); - - if (commonPrefixLen != 0 && needed.Slice(0, commonPrefixLen).IndexOf('\x1b') == -1) - { - // no escape codes, so can trivially skip substrings - sb.Append($"{AnsiCodes.CSI}{commonPrefixLen}{AnsiCodes.MoveForward}"); - sb.Append(needed.Slice(commonPrefixLen)); - } - else - { - sb.Append(needed); - } - - // Shall we clear rest of line - if (needed.Length < previous.Length) - { - sb.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInLine}"); - } - } - } - else - { - // From now on we have to simply WriteLine - sb.Append(needed); - } - - // Next line - sb.AppendLine(); - } - - // clear no longer used lines - if (i < previousFrame.NodesCount) - { - sb.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInDisplay}"); - } - - return sb.ToString(); - } - - public void Clear() - { - NodesCount = 0; - } - } - #endregion #region Helpers