Skip to content

feat: help improvements and customizability#184

Merged
RodEsp merged 12 commits into
masterfrom
td/help
Jun 17, 2021
Merged

feat: help improvements and customizability#184
RodEsp merged 12 commits into
masterfrom
td/help

Conversation

@amphro
Copy link
Copy Markdown
Contributor

@amphro amphro commented Jun 10, 2021

Command and Flag Summary

Oclif will use the first line of command.description as the "summary" which is used as the first line on command help as well as in the command list. This is clever, but can also be hidden and confusing. There is now a summary property on command and flag, which if provided, will be used as the first line on command help and in the command list. It will also be repeated in the DESCRIPTION section of help for increased usability.

The summary property should be one line.

Flag Descriptions

Multiline flag descriptions can be hard to read in a wrapped list view. Oclif will render the list different if there is one descriptions is more than 4 lines, but at that point it may be too late. Not only that, it can still be useful to have a short, easy to digest flag list, with more description down below. Now the summary property on flag, if provided, will be used in the in the flag list and the description will be put in an extended section down below.

FLAGS
  -e, --target-env=<value>...  [default: dsfr] Environment name or alias to open.
  ...

DESCRIPTION
   ...

EXAMPLES
  ...

FLAG DESCRIPTIONS
  -e, --target-env=<value>...

    Specify the login user or alias that’s associated with the environment. For scratch orgs, the login user is generated
    by the command that created the scratch org. You can also set an alias for the scratch org when you create it.

    For Dev Hubs, sandboxes, and production orgs, specify the alias you set when you logged into the org with "sf login".

Flag Help Groups

Help for commands with a large number of flags can be hard to read. It can be hard to pick out related flags. Or it can be hard to pick out flags that may not be directly related to the command, such as flags added by a CLI or a particular plugin. For example, --json or table flags.

Flag help groups help solve this.

FLAGS
  --[no-]opt  my boolean optional flag

TABLE FLAGS
  -x, --extended          show extra columns
  --columns=columns       only show provided columns (comma-separated)
  --csv                   output is csv format [alias: --output=csv]
  --filter=filter         filter property by partial string matching, ex: name=foo
  --hidden                show hidden commands
  --no-header             hide table header from output
  --no-truncate           do not truncate output to fit screen
  --output=csv|json|yaml  output in a more machine friendly format
  --sort=sort             property to sort by (prepend '-' for descending)

CLI FLAGS
  --json  format output as json

Refactor help and extend customizability

There are time where a CLI may just want to add an extra help section or change a section heading in the command help. Or maybe the CLI wants to override the help options. With oclif@1, a CLI could really only override the Help class which did not provide any helpful methods for command help or options. Now, the CommandHelp class is loaded from the Help class so it can be extended. Also, help options can be defined in pjson.oclif.helpOptions. See test/help/show-customized-help.test.ts.

In addition, code like wrap(body, this.opts.maxWidth - spacing, {hard: true, trim: false} and creating a section with [bold(header), indent(body, 2)].join('\n') was called all over the place. These have been abstracted into a base class which all help classes now extend, providing those utilities to custom help classes as well.

Example Descriptions

Having a list of examples with no descriptions is not very useful, which the existing example structure encourages. Consumers can add descriptions themselves but it is less than idea.

Flag.string({
  description: 'my flag',
  examples: [
    'happy path\n<%= config.bin %> <%= command.id %> --flag'
    'run with foo bar\n<%= config.bin %> <%= command.id %> --flag --foo=bar'
  ]
})

Now, examples can be an object {description, command} which will be formatted appropriately. If a sting is provided, it will try to figure our what is a description and what is a command to format in a more readable output.

EXAMPLES
  happy path:

    $ bin command --flag

  run with foo bar:

    $ bin command --flag --foo=bar

@amphro amphro requested review from RodEsp and mdonnalley June 10, 2021 04:21
Comment thread src/help/formatter.ts
return indent(body, spacing)
}

public renderList(input: (string | undefined)[][], opts: {indentation: number; multiline?: boolean; stripAnsi?: boolean; spacer?: string}): string {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is unchanged, just brought over from list.ts.

Comment thread src/help/formatter.ts Outdated
* `config=Interfaces.Config` or `opts=Interface.HelpOptions`.
*
* ```javascript
* '<%= config.bin =>` // will resolve to the bin defined in `pjson.oclif`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you meant to have a ` here instead of a '

Comment thread src/command.ts Outdated
/**
* An array of examples to show at the end of the command's help.
*
* IF only a string is provide, it will try to look for a line that starts
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this worth mentioning in the migration doc?

@mdonnalley mdonnalley self-requested a review June 11, 2021 14:50
Copy link
Copy Markdown
Contributor

@mdonnalley mdonnalley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Found one issue while testing:

In the messages.md folder I have:

# examples

- happy path example\n<%= config.bin %> <%= command.id %> --jwt-key-file myorg.key --username me@salesforce.com --clientid XXXX

But the help shows:

EXAMPLES
  happy path example\nsf login org jwt --jwt-key-file myorg.key --username me@salesforce.com --clientid XXXX
  1. I noticed that the FLAG DESCRIPTIONS didn't behave as I expected: When I added a summary to one of my flags, I expected to see the new section in help but I didn't. I had to make sure that my description was also multiple lines in order to see the new section. I think my preference is to always add the FLAG DESCRIPTIONS section if the summary prop exists - regardless of whether or not my description is long enough
  2. I think the FLAGS and CLI FLAGS distinction is unclear. I feel like there's a better way to communicate "these flags are specific to this command, and these other flags are generally available on all commands"
  3. Lastly, if I understand correctly consumers have to implement flag groups on their own? Would it better to simply expose that as part of the flag options, e.g.
    alias: Flags.string({
      description: messages.getMessage('alias'),
      group: 'foo',
    }),
    clientid: Flags.string({
      description: messages.getMessage('clientId'),
      group: 'bar',
    }),

@amphro
Copy link
Copy Markdown
Contributor Author

amphro commented Jun 14, 2021

  1. You can't have \n in markdown since markdown is just text. Just hit enter, or if you want to use \n use a js or ts file instead.
  2. I agree. Fixed and added a test.
  3. Suggestions? I changed to "GLOBAL FLAGS" but I'm impartial.
  4. That is exactly how it is done. See how the --json flag
  alias: Flags.string({
      description: messages.getMessage('alias'),
      helpGroup: 'foo',
  }),
  clientid: Flags.string({
      description: messages.getMessage('clientId'),
      helpGroup: 'bar',
  }),

Copy link
Copy Markdown
Contributor

@RodEsp RodEsp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing all the references to \n for EOL for windows compatibility?

Comment thread src/command.ts Outdated
Comment thread src/help/command.ts Outdated
Comment thread src/help/command.ts Outdated
amphro and others added 3 commits June 15, 2021 15:08
Co-authored-by: Rodrigo Espinosa de los Monteros <1084688+RodEsp@users.noreply.github.com>
Co-authored-by: Rodrigo Espinosa de los Monteros <1084688+RodEsp@users.noreply.github.com>
Co-authored-by: Rodrigo Espinosa de los Monteros <1084688+RodEsp@users.noreply.github.com>
Comment thread src/help/index.ts
const title = command.description && this.render(command.description).split('\n')[0]
if (title) console.log(title + '\n')
const summary = this.summary(command) // command.description && this.render(command.description).split('\n')[0]
if (summary) console.log(summary + '\n')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still pulling from the command description, not the summary but I can't understand why.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test exemplifies what I mean
#186

Comment thread src/help/index.ts Outdated
protected summary(c: Interfaces.Command): string {
if (c.summary) return this.render(c.summary.split('\n')[0])

return this.render((c.description || '').split('\n')[0])
Copy link
Copy Markdown
Contributor

@RodEsp RodEsp Jun 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this should be c.summary but for some reason the .summary isn't being added to the command. See comment above.

Suggested change
return this.render((c.description || '').split('\n')[0])
return this.render((c.summary|| '').split('\n')[0])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this is related but .\bin\dev commandreference generate seems to not be able to find .name in here when this branch is yarn linked.

So it's like it's not getting the .plugin property here.

Copy link
Copy Markdown
Contributor

@RodEsp RodEsp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super long flag descriptions don't align correctly on Windows. They just continue on a newline at the same indentation as the flags.
image

Comment thread src/help/command.ts

const body = flagsWithExtendedDescriptions.map(flag => {
return `${this.flagHelpLabel(flag, true)} ${flag.summary}\n\n${this.indent(this.wrap(flag.description || '', this.indentSpacing * 2))}`
// Guaranteed to be set because of the filter above, but make ts happy
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, lol.

@RodEsp RodEsp merged commit cb2109b into master Jun 17, 2021
@RodEsp RodEsp deleted the td/help branch June 17, 2021 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants