From c7c6646ea3ed5cae6ebb0cf52e71ce173efbc662 Mon Sep 17 00:00:00 2001 From: yashanil98 <79158725+yashanil98@users.noreply.github.com> Date: Thu, 25 Jun 2026 02:20:14 -0700 Subject: [PATCH] config: fix root-relative markdown links in interactive config help - fixes #8239 Option help strings are also used to generate the website documentation, so some contain markdown links with root-relative targets such as [encoding section in the overview](/overview/#encoding). These render correctly on rclone.org but are confusing in the interactive config prompt, where the user sees the raw markdown and the link has no reachable root. Rewrite such links to text (https://rclone.org/path) when showing an option's help in the interactive config. The raw help is left unchanged so documentation generation is unaffected. --- fs/config/ui.go | 17 ++++++++++++- fs/config/ui_internal_test.go | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 fs/config/ui_internal_test.go diff --git a/fs/config/ui.go b/fs/config/ui.go index 1a897aefe..ab2163e6e 100644 --- a/fs/config/ui.go +++ b/fs/config/ui.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "os" + "regexp" "slices" "sort" "strconv" @@ -441,7 +442,7 @@ func backendConfig(ctx context.Context, name string, m configmap.Mapper, ri *fs. out.Option.Examples[1].Value == "false" && out.Option.Exclusive { // Use Confirm for Yes/No questions as it has a nicer interface= - fmt.Println(out.Option.Help) + fmt.Println(renderHelpForTerminal(out.Option.Help)) in.Result = fmt.Sprint(Confirm(Default)) } else { value := ChooseOption(out.Option, name) @@ -483,12 +484,26 @@ func RemoteConfig(ctx context.Context, name string) error { return PostConfig(ctx, name, m, ri) } +// rootRelativeMarkdownLink matches a markdown link with a root-relative +// target, e.g. [encoding section in the overview](/overview/#encoding). +var rootRelativeMarkdownLink = regexp.MustCompile(`\[([^\]]+)\]\((/[^)]*)\)`) + +// renderHelpForTerminal makes an option's help string readable on the +// terminal. The same help text is also used to generate the website +// documentation, where markdown links with root-relative targets resolve +// correctly. On the terminal those links are confusing, so rewrite them to +// "text (https://rclone.org/path)" using rclone.org as the implied root. +func renderHelpForTerminal(help string) string { + return rootRelativeMarkdownLink.ReplaceAllString(help, "$1 (https://rclone.org$2)") +} + // ChooseOption asks the user to choose an option func ChooseOption(o *fs.Option, name string) string { fmt.Printf("Option %s.\n", o.Name) if o.Help != "" { // Show help string without empty lines. help := strings.ReplaceAll(strings.TrimSpace(o.Help), "\n\n", "\n") + help = renderHelpForTerminal(help) fmt.Println(help) } diff --git a/fs/config/ui_internal_test.go b/fs/config/ui_internal_test.go new file mode 100644 index 000000000..0f125db15 --- /dev/null +++ b/fs/config/ui_internal_test.go @@ -0,0 +1,45 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRenderHelpForTerminal(t *testing.T) { + for _, test := range []struct { + name string + help string + want string + }{ + { + name: "no link", + help: "The encoding for the backend.", + want: "The encoding for the backend.", + }, + { + name: "root relative link", + help: "See the [encoding section in the overview](/overview/#encoding) for more info.", + want: "See the encoding section in the overview (https://rclone.org/overview/#encoding) for more info.", + }, + { + name: "root relative link without anchor", + help: "See [rclone serve sftp](/commands/rclone_serve_sftp) for details.", + want: "See rclone serve sftp (https://rclone.org/commands/rclone_serve_sftp) for details.", + }, + { + name: "multiple links", + help: "[the time option docs](/docs/#time-options) and [authentication docs](/azureblob#authentication).", + want: "the time option docs (https://rclone.org/docs/#time-options) and authentication docs (https://rclone.org/azureblob#authentication).", + }, + { + name: "absolute url left untouched", + help: "See [rclone forum](https://forum.rclone.org/) for help.", + want: "See [rclone forum](https://forum.rclone.org/) for help.", + }, + } { + t.Run(test.name, func(t *testing.T) { + assert.Equal(t, test.want, renderHelpForTerminal(test.help)) + }) + } +}