From patchwork Wed Sep 1 11:12:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsCBCamFybWFzb24=?= X-Patchwork-Id: 12469197 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9D064C4320E for ; Wed, 1 Sep 2021 11:13:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7447960F92 for ; Wed, 1 Sep 2021 11:13:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241575AbhIALOD (ORCPT ); Wed, 1 Sep 2021 07:14:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39966 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238424AbhIALN4 (ORCPT ); Wed, 1 Sep 2021 07:13:56 -0400 Received: from mail-wr1-x435.google.com (mail-wr1-x435.google.com [IPv6:2a00:1450:4864:20::435]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 86021C061760 for ; Wed, 1 Sep 2021 04:12:59 -0700 (PDT) Received: by mail-wr1-x435.google.com with SMTP id b6so3858503wrh.10 for ; Wed, 01 Sep 2021 04:12:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=saP5m1yaPfDo9N1o2iyHUVZIaQcNMlqgxPy+yOsjRhU=; b=mdAz1wUB/q27oEUEgai3CldobCnw2S81dpYi8xnD4yBSj4fadIzt8z9cyeSNUX7W/A eGfU8isL/+/+Mwf/q3Q2semjkBoPS1RjSWtdozLJBSgM8Kgd12prPoFGJOLs3wzxKf2t nfJD5HQydE2Ou7IfF9pUyQIpYEagnKk07vj8T4bD7y3vPUBQEl+yGYhR58b7+epLPig5 GzSdNiA8K8fd1AxDNyjX7Fvuk6TxxmyzH8cQp7BF+K5e9g7DjDaJo0m9VPa9gOg7iVrk epn2w7C65z5XoOfBUiPeI1gFvqS2ZcMZraA3NjzJvNdRdKBokyib0tCci7ptQQ8ooXA2 7p/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=saP5m1yaPfDo9N1o2iyHUVZIaQcNMlqgxPy+yOsjRhU=; b=IXsslgd+wKTM0E1T6Sb274uLnuV2R4HXL8Bj9wFsIl8cSUXGK/PqLj2wqKdQ2n/yEe JcC4Pe4e/TcMozYsdhfJIemUlYqX3CsHCVMypcrVo0zTOT2Y8lehZYkX3z7BHX6lCenP pjj1SlCBpAZGgQ/ElxjlpfP5a0bTvrrfTJ6i05OAlyWmh9/eJPjZNuuy1+jvXTfw5Jad a4GTfyaRujvwY6X4AnGhZWZ1zXG2khQl2/9v4ZQE/yvdVNQ0+wu/Qg9qMEnxRsgYk99r dpqHBx6wZ1+SC8/1ubxP/OEnrppACH9PhG//2q1OMX3x2OIm+pbc+XVU1VDZ5s9CVFZw jhPg== X-Gm-Message-State: AOAM530xLAAY/7hs7TbDX72GslIgSEqcO0O2b9uIGoKFW8POMCOhpKZQ keil7zml369V/n28vFzyXa7+A+SUxhmHqw== X-Google-Smtp-Source: ABdhPJycGC70ALroiRXxkq8EPWQ0a+KewjvmCoD0pMZRFs0+IMe1Ynpwr2v68bm23VvTj3N7sdCpmQ== X-Received: by 2002:a05:6000:1244:: with SMTP id j4mr22289624wrx.335.1630494777952; Wed, 01 Sep 2021 04:12:57 -0700 (PDT) Received: from vm.nix.is (vm.nix.is. [2a01:4f8:120:2468::2]) by smtp.gmail.com with ESMTPSA id u25sm5151499wmj.10.2021.09.01.04.12.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Sep 2021 04:12:57 -0700 (PDT) From: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsCBCamFybWFzb24=?= To: git@vger.kernel.org Cc: Junio C Hamano , Jeff King , Carlo Arenas , =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsCBCamFy?= =?utf-8?b?bWFzb24=?= Subject: [PATCH 1/2] built-ins: "properly" align continued usage output Date: Wed, 1 Sep 2021 13:12:54 +0200 Message-Id: X-Mailer: git-send-email 2.33.0.807.gf14ecf9c2e9 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Let's "fix" various "git -h" output by "properly" aligning the output in cases where we continue usage output after a "\n". The "fix" and "properly" scare quotes are because this actually makes things worse in some cases, because e.g. in the case of "git tag -h" the "\t\t" effectively works around how parse-options.c aligns this output. But two wrongs don't make a right, let's "fix" this by making it worse temporarily, in anticipating of improving parse-options.c to handle this alignment. The issue is that we should have whitespace corresponding to the length of the command name here, e.g. in the case of "git ls-remote" it should be 14 characters, or the length of ""git ls-remote ". Instead we had 21 characters in builtin/ls-remote.c, those extra 7 characters are the length of "usage: " (and also " or:"). So in the C locale the resulting output aligns nicely: $ git ls-remote -h usage: git ls-remote [--heads] [--tags] [--refs] [--upload-pack=] [-q | --quiet] [--exit-code] [--get-url] [--symref] [ [...]] But that's fragile, we might not be under the C locale. We really should have parse-options.c itself add this padding. In a subsequent commit I'll make it do that. In the case of "tag" and "show-branch" and "stash save" the output was not properly aligned, although in the "git tag" case it was near-enough (aligned with the "-" in "git tag -l") to look good, assuming C locale & a tab-width of 8. In any case, let's align this in a way that looks obviously correct when looking at the source itself, and then improve parse-options.c itself. Signed-off-by: Ævar Arnfjörð Bjarmason --- builtin/ls-remote.c | 4 ++-- builtin/show-branch.c | 6 +++--- builtin/stash.c | 2 +- builtin/tag.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 1794548c711..ef9036974ce 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -7,8 +7,8 @@ static const char * const ls_remote_usage[] = { N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=]\n" - " [-q | --quiet] [--exit-code] [--get-url]\n" - " [--symref] [ [...]]"), + " [-q | --quiet] [--exit-code] [--get-url]\n" + " [--symref] [ [...]]"), NULL }; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index d77ce7aeb38..a82cd1534fc 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -11,9 +11,9 @@ static const char* show_branch_usage[] = { N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" - " [--current] [--color[=] | --no-color] [--sparse]\n" - " [--more= | --list | --independent | --merge-base]\n" - " [--no-name | --sha1-name] [--topics] [( | )...]"), + " [--current] [--color[=] | --no-color] [--sparse]\n" + " [--more= | --list | --independent | --merge-base]\n" + " [--no-name | --sha1-name] [--topics] [( | )...]"), N_("git show-branch (-g | --reflog)[=[,]] [--list] []"), NULL }; diff --git a/builtin/stash.c b/builtin/stash.c index 8f42360ca91..45b19007d7c 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -85,7 +85,7 @@ static const char * const git_stash_push_usage[] = { static const char * const git_stash_save_usage[] = { N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] []"), + " [-u|--include-untracked] [-a|--all] []"), NULL }; diff --git a/builtin/tag.c b/builtin/tag.c index 452558ec957..9b1165d2a4e 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -26,7 +26,7 @@ static const char * const git_tag_usage[] = { "\t\t []"), N_("git tag -d ..."), N_("git tag -l [-n[]] [--contains ] [--no-contains ] [--points-at ]\n" - "\t\t[--format=] [--merged ] [--no-merged ] [...]"), + " [--format=] [--merged ] [--no-merged ] [...]"), N_("git tag -v [--format=] ..."), NULL }; From patchwork Wed Sep 1 11:12:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsCBCamFybWFzb24=?= X-Patchwork-Id: 12469199 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 59A8BC43214 for ; Wed, 1 Sep 2021 11:13:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3EFDE6101B for ; Wed, 1 Sep 2021 11:13:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241797AbhIALOF (ORCPT ); Wed, 1 Sep 2021 07:14:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39974 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238988AbhIALN5 (ORCPT ); Wed, 1 Sep 2021 07:13:57 -0400 Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com [IPv6:2a00:1450:4864:20::42e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 637D4C061764 for ; Wed, 1 Sep 2021 04:13:00 -0700 (PDT) Received: by mail-wr1-x42e.google.com with SMTP id q11so3867851wrr.9 for ; Wed, 01 Sep 2021 04:13:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=iwiHe8TxZMRMJdFeD6B9F0RCHk98AKvtBGippsvFH9I=; b=eVnYfMOWlZk3wZsJROX6M8azBwcMR1y5M5SK4H1GsheQHwg1pHVjuaVRwGybhJ1xnv A1WDDBwMLrhJvgGmbll6efWb/B/Unxv0tN9tnWjH1HGpoIx3wQ8au2644wDgtZvCMqPU Po/olKiVbNdHraTm+BsSlOlAuLhQnbeEYTIZRXiFjTyYx7wtorZnF7URp1AWXK1ITkIr Kk248O6ZiFWAoHiqzuXjdiLC938Ccl8ioecZZ20/Lb7Whg2aO2tSiWCoUiz9Tle4IQbe f2NnrTHSYccVmA/ellq5/fa3Mx86aArN4/0JLt9dhNdHhyhup64PSJjYKaN9UZPzblJ3 Drcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=iwiHe8TxZMRMJdFeD6B9F0RCHk98AKvtBGippsvFH9I=; b=PqUe016VSOu4UOX5BClf7dWfcd3gp6LZMpLNNyQ7wAQmNf/eL/yfoMZ0nSIHI/sPO3 86yqvV19SgdQNwuVVh2f9amqxRFNIIZnHXFen48ida071af90ltlo73hlcXX8BybcX3x RGZXn78I1Om4PiW1WdzCwe9H/xQVB5WJ/Yt38CSNkJsqMEqB4P5jeGDb3HVtphIe7eF+ l2IeJe8eKbK93DjmapQBM97vI+0do2jBSATRsn5o49c4I/B+TzMjB3icQ4EkD4leKyLE VrJofSCAqITm/O2vCCVg2TgFzLDCmg3S6bdim9QksDOChjKfxJiievCK7JLnG4fRhXcw xaaQ== X-Gm-Message-State: AOAM532LkNLHkOSDNVmL0fQfxrQdbDtXZPz3a9CCE4xjZc4up/2+3JJX A7q79NyJX65DgR7tHWYe1bsrld5g+2GmVw== X-Google-Smtp-Source: ABdhPJzT8wU50oJ7+X257kGBe9E3mSYTAUaXhqFsS1RU9imCTc3yxgnGvuSsVD3ByGLT5ny08ibR4Q== X-Received: by 2002:adf:d1ab:: with SMTP id w11mr28765238wrc.372.1630494778717; Wed, 01 Sep 2021 04:12:58 -0700 (PDT) Received: from vm.nix.is (vm.nix.is. [2a01:4f8:120:2468::2]) by smtp.gmail.com with ESMTPSA id u25sm5151499wmj.10.2021.09.01.04.12.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Sep 2021 04:12:58 -0700 (PDT) From: =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsCBCamFybWFzb24=?= To: git@vger.kernel.org Cc: Junio C Hamano , Jeff King , Carlo Arenas , =?utf-8?b?w4Z2YXIgQXJuZmrDtnLDsCBCamFy?= =?utf-8?b?bWFzb24=?= Subject: [PATCH 2/2] parse-options: properly align continued usage output Date: Wed, 1 Sep 2021 13:12:55 +0200 Message-Id: X-Mailer: git-send-email 2.33.0.807.gf14ecf9c2e9 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Some commands such as "git stash" emit continued options output with e.g. "git stash -h", because usage_with_options_internal() prefixes with its own whitespace the resulting output wasn't properly aligned. Let's account for the added whitespace, which properly aligns the output. The "git stash" command has usage output with a N_() translation that legitimately stretches across multiple lines; N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" " [-u|--include-untracked] [-a|--all] [-m|--message ]\n" We'd like to have that output aligned with the length of the initial "git stash " output, but since usage_with_options_internal() adds its own whitespace prefixing we fell short, before this change we'd emit: $ git stash -h usage: git stash list [] or: git stash show [] [] [...] or: git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] [-u|--include-untracked] [-a|--all] [-m|--message ] [...] Now we'll properly emit aligned output. I.e. the last four lines above will instead be (a whitespace-only change to the above): [...] or: git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] [-u|--include-untracked] [-a|--all] [-m|--message ] [...] In making this change we can can fold the two for-loops over *usagestr into one. We had two of them purely to account for the case where an empty string in the array delimits the usage output from free-form text output. We could skip the string_list_split() with a strchr(str, '\n') check, but we'd then need to duplicate our state machine for strings that do and don't contain a "\n". It's simpler to just always split into a "struct string_list", even though the common case is that that "struct string_list" will contain only one element. This is not performance-sensitive code. This change is relatively more complex since I've accounted for making it future-proof for RTL translation support. Later in usage_with_options_internal() we have some existing padding code dating back to d7a38c54a6c (parse-options: be able to generate usages automatically, 2007-10-15) which isn't RTL-safe, but that code would be easy to fix. Let's not introduce new RTL translation problems here. Signed-off-by: Ævar Arnfjörð Bjarmason --- parse-options.c | 79 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/parse-options.c b/parse-options.c index 2abff136a17..a06968bf4f5 100644 --- a/parse-options.c +++ b/parse-options.c @@ -917,25 +917,78 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx, FILE *outfile = err ? stderr : stdout; int need_newline; + const char *usage_prefix = _("usage: %s"); + /* + * TRANSLATORS: the colon here should align with the + * one in "usage: %s" translation. + */ + const char *or_prefix = _(" or: %s"); + /* + * TRANSLATORS: You should only need to translate this format + * string if your language is a RTL language (e.g. Arabic, + * Hebrew etc.), not if it's a LTR language (e.g. German, + * Russian, Chinese etc.). + * + * When a translated usage string has an embedded "\n" it's + * because options have wrapped o the next line. The line + * after the "\n" will then be padded to align with the + * command name, such as N_("git cmd [opt]\n<8 + * spaces>[opt2]"), where the 8 spaces are the same length as + * "git cmd ". + * + * This format string prints out that already-translated + * line. The "%*s" is whitespace padding to account for the + * padding at the start of the line that we add in this + * function, the "%s" is a line in the (hopefully already + * translated) N_() usage string, which contained embedded + * newlines before we split it up. + */ + const char *usage_continued = _("%*s%s"); + + /* + * The translation could be anything, but we can count on + * msgfmt(1)'s --check option to have asserted that "%s" is in + * the translation. So compute the length of the " or: " + * part. We are assuming that the translator wasn't overly + * clever and used e.g. "%1$s" instead of "%s", there's only + * one "%s" in "or_prefix" above, so there's no reason to do + * so even with a RTL language. + */ + size_t or_len = strlen(or_prefix) - strlen("%s"); + int i; + int saw_empty_line = 0; + if (!usagestr) return PARSE_OPT_HELP; if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL) fprintf(outfile, "cat <<\\EOF\n"); - fprintf_ln(outfile, _("usage: %s"), _(*usagestr++)); - while (*usagestr && **usagestr) - /* - * TRANSLATORS: the colon here should align with the - * one in "usage: %s" translation. - */ - fprintf_ln(outfile, _(" or: %s"), _(*usagestr++)); - while (*usagestr) { - if (**usagestr) - fprintf_ln(outfile, _(" %s"), _(*usagestr)); - else - fputc('\n', outfile); - usagestr++; + for (i = 0; *usagestr; i++) { + const char *str = _(*usagestr++); + struct string_list list = STRING_LIST_INIT_DUP; + unsigned int j; + + string_list_split(&list, str, '\n', -1); + for (j = 0; j < list.nr; j++) { + const char *line = list.items[j].string; + + if (!saw_empty_line && !*line) + saw_empty_line = 1; + + if (saw_empty_line && *line) + fprintf_ln(outfile, _(" %s"), line); + else if (saw_empty_line) + fputc('\n', outfile); + else if (!j && !i) + fprintf_ln(outfile, usage_prefix, line); + else if (!j) + fprintf_ln(outfile, or_prefix, line); + else + fprintf_ln(outfile, usage_continued, + (int)or_len, "", line); + } + string_list_clear(&list, 0); } need_newline = 1;