Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap terminal line output to 80 characters #646

Open
jsejcksn opened this issue Jan 30, 2020 · 16 comments
Open

Wrap terminal line output to 80 characters #646

jsejcksn opened this issue Jan 30, 2020 · 16 comments

Comments

@jsejcksn
Copy link
Contributor

Scannability would be improved greatly if lines were wrapped at 80 characters.

Here is example terminal output from the command asdf help. The longest unwrapped line is 125 characters:

asdf help
version: v0.7.6-6207e42

MANAGE PLUGINS
  asdf plugin add <name> [<git-url>]       Add a plugin from the plugin repo OR,
 add a Git repo
                                           as a plugin by specifying the name an
d repo url
  asdf plugin list [--urls] [--refs]       List installed plugins. Optionally sh
ow git urls and git-ref.
  asdf plugin list all                     List plugins registered on asdf-plugi
ns repository with URLs
  asdf plugin remove <name>                Remove plugin and package versions
  asdf plugin update <name> [<git-ref>]    Update a plugin to latest commit or a
 particular git-ref.
  asdf plugin update --all                 Update all plugins


MANAGE PACKAGES
  asdf install [<name> <version>]          Install a specific version of a packa
ge or,
                                           with no arguments, install all the pa
ckage
                                           versions listed in the .tool-versions
 file
  asdf uninstall <name> <version>          Remove a specific version of a packag
e
  asdf current                             Display current version set or being 
used for all packages
  asdf current <name>                      Display current version set or being 
used for package
  asdf where <name> [<version>]            Display install path for an installed
 or current version
  asdf which <command>                     Display the path to an executable
  asdf shell <name> <version>              Set the package version in the curren
t shell
  asdf local <name> <version>              Set the package local version
  asdf global <name> <version>             Set the package global version
  asdf list <name>                         List installed versions of a package
  asdf list all <name>                     List all versions of a package


UTILS
  asdf exec <command> [args..]             Executes the command shim for current
 version
  asdf env <command> [util]                Runs util (default: `env`) inside the
 environment used for command shim execution.
  asdf reshim <name> <version>             Recreate shims for version of a packa
ge
  asdf shim-versions <command>             List on which plugins and versions is
 command available
  asdf update                              Update asdf to the latest stable rele
ase
  asdf update --head                       Update asdf to the latest on the mast
er branch

"Late but latest"
-- Rajinikanth
@Stratus3D
Copy link
Member

Thanks for opening this issue. I think we should limit line lengths to 80 characters so they can fit on terminals of the traditional size.

I'm usually working on a 15 inch screen or smaller, and like to have two terminals side by side, which means lines need to be less than 90-100 characters to not wrap.

@jthegedus
Copy link
Contributor

A bash utility that handled this and the column issue in #661 would be amazing.

@jsejcksn
Copy link
Contributor Author

Wouldn't the util need to be aware of all of the indentation formats used by all of the commands in order to automatically wrap them?

For example, the help command output above looks like it uses a static indent of 41 characters for the descriptions. Are all the other output types in this format?

Or is there maybe an existing tool for generating tabular text content wrapped at 80 chars?

@jthegedus jthegedus self-assigned this Jun 13, 2020
@jthegedus jthegedus changed the title [enhancement] Wrap terminal line output to 80 characters Wrap terminal line output to 80 characters Jun 15, 2020
@jthegedus
Copy link
Contributor

jthegedus commented Jun 16, 2020

@jsejcksn #742 should resolve the wrapping issue for the asdf help command. Though I am looking to revamp our output formatting for all content that is >1 column of information.

How do we feel about not necessarily a hard cap at 80 chars, but a solution that wraps by terminal width at the time of execution?

Additionally, how do we feel about rendering multiple columns as alternating rows, eg:

➜ asdf plugin list --urls
PLUGIN		URL
deno        https://github.com/asdf-community/asdf-deno.git
firebase    https://github.com/jthegedus/asdf-firebase.git
gcloud      https://github.com/jthegedus/asdf-gcloud.git
golang      https://github.com/kennyp/asdf-golang.git
gradle      https://github.com/rfrancis/asdf-gradle.git
hadolint    https://github.com/looztra/asdf-hadolint.git
java        https://github.com/halcyon/asdf-java.git
maven       https://github.com/halcyon/asdf-maven.git
nodejs      https://github.com/asdf-vm/asdf-nodejs.git
ocaml       https://github.com/asdf-community/asdf-ocaml.git
python      https://github.com/danhper/asdf-python.git
shellcheck  https://github.com/luizm/asdf-shellcheck.git
terraform   https://github.com/Banno/asdf-hashicorp.git

Becoming

➜ asdf plugin list --urls
PLUGIN
URL
deno
https://github.com/asdf-community/asdf-deno.git
firebase
https://github.com/jthegedus/asdf-firebase.git
gcloud
https://github.com/jthegedus/asdf-gcloud.git
golang
https://github.com/kennyp/asdf-golang.git
gradle
https://github.com/rfrancis/asdf-gradle.git
hadolint
https://github.com/looztra/asdf-hadolint.git
java
https://github.com/halcyon/asdf-java.git
maven
https://github.com/halcyon/asdf-maven.git
nodejs
https://github.com/asdf-vm/asdf-nodejs.git
ocaml
https://github.com/asdf-community/asdf-ocaml.git
python
https://github.com/danhper/asdf-python.git
shellcheck
https://github.com/luizm/asdf-shellcheck.git
terraform
https://github.com/Banno/asdf-hashicorp.git

on terminals where the screen width is less than required for horizontal layout?

@jsejcksn
Copy link
Contributor Author

How do we feel about not necessarily a hard cap at 80 chars, but a solution that wraps by terminal width at the time of execution?

If, by that, you mean a responsively-formatted output in which "it will wrap the contents to 80 chars if the terminal is 80 columns, or 90 chars if the terminal is 90 columns, etc." then I think that's a fantastic idea. I'm very curious how you plan to accomplish this if you are doing it in a POSIX-compliant way, and there are many projects which would benefit from such a formatter.

Alternating rows:

...on terminals where the screen width is less than required for horizontal layout?

I think that a consistently-formatted output might be important for parsers. I have used the output of various asdf commands to guide automated scripts (extracting versions, URLs, etc.). If you decide to use the alternating format, I think an extra newline character before each name will improve readability greatly.

Finally, this makes me wonder: Is all of this data available in a machine-readable format somewhere? Or it is the responsibility of each plugin to source and correctly format the output of commands like asdf list all <plugin>?

@jthegedus
Copy link
Contributor

jthegedus commented Jun 17, 2020

If, by that, you mean a responsively-formatted output in which "it will wrap the contents to 80 chars if the terminal is 80 columns, or 90 chars if the terminal is 90 columns, etc."

Yes, that is the idea. It would not be an external package, just some more complex awk. Looking at our formatting, we only have a few instances where we render multiple columns of data, the issue I have with columns is wrapping within a column. I would like our help.txt to not be formatted inline (2nd column wraps within the column of text). Given we have a known set of outputs, we don't need to be generic as tools like column are, and thus, have no need to build a new tool or increase our dependency list. Just have more complex code here.

To be clear, I would like line wrapping to be mostly handled by the terminal. There's just times, like with the help text, where wrapping within a column is what is desired, not wrapping to the start of the next line as would happen with multiple columns where there's overflow. If the content can be wrapped in a better way without inline formatting, then (I think) we should. For instance, 80 cols is a standard width and is a fine starting point, but by wrapping at 80 our help.txt is now 18 rows taller (totaling 55 rows) than without wrapping. I personally use a wide terminal that is not very tall, so this is cumbersome. If there's an opportunity to be flexible, then I think we should discuss it.

I think that a consistently-formatted output might be important for parsers.

I had not considered this. Good point. POSIX [:space:] character class is apparently all whitespace, including newlines, so perhaps REGEX-based parsing wouldn't be affected by such a formatting change. 🤔

I will spend more time thinking on this as I do have ideas for outputs with more columns of information that would not be 80 col friendly by default, perhaps I could use a --less flag instead of changing the format.

Is all of this data available in a machine-readable format somewhere? Or it is the responsibility of each plugin to source and correctly format the output of commands like asdf list all ?

Each plugin needs to follow specific output guidelines for each command and then the core asdf takes that data and renders it. So the terminal output formatting would be within the core.

🆕 Another question while I am looking at the output formatting, some of our outputs have headings, some do not. I feel we should be consistent. What are peoples feelings on these differences:
➜ asdf list                    
deno
  1.0.0
  1.0.5
firebase
  8.4.1

Weird formatting. Should probably be either of:

➜ asdf list
PLUGIN		VERSION
deno		1.0.0
			1.0.5
firebase	8.4.1

or

➜ asdf list                    
deno
1.0.0
1.0.5
firebase
8.4.1

➜ asdf list python
  3.8.2
➜ asdf plugin list
deno
firebase
python
➜ asdf plugin list --urls
deno                         https://github.com/asdf-community/asdf-deno.git
firebase                     https://github.com/jthegedus/asdf-firebase.git
python                       https://github.com/danhper/asdf-python.git

Columnar data, no column titles.

➜ asdf plugin list --refs
deno                         master 3860217
firebase                     master fdab358
python                       master b544ac9

Columnar data, no column titles, last column isn't spaced consistently (just happen to align because all on master).

➜ asdf plugin list --refs --urls
deno                         https://github.com/asdf-community/asdf-deno.git master 3860217
firebase                     https://github.com/jthegedus/asdf-firebase.git master fdab358
python                       https://github.com/danhper/asdf-python.git master b544ac9

Columnar data, no column titles, third & fourth columns aren't spaced consistently.

➜ asdf plugin list all
1password                     https://github.com/samtgarson/asdf-1password.git
adr-tools                     https://gitlab.com/td7x/asdf/adr-tools.git
aks-engine                    https://github.com/robsonpeixoto/asdf-aks-engine.git
alp                           https://github.com/asdf-community/asdf-alp.git
...

Columnar data, no column titles.

➜ asdf current
deno           1.0.0    (set by /home/jthegedus/.tool-versions)
version 8.0.2 is not installed for firebase
firebase       
python         3.8.2    (set by /home/jthegedus/.tool-versions)

Errors break rendering flow, see #528. Columnar data, no column titles.

Any thoughts? While it would break some scripts and is annoying to trim, I prefer column titles. I also prefer titles everywhere instead of only sometimes. Spacing changes should be easily compatible if people are using Regex.

Perhaps this should be another issue.

@jsejcksn
Copy link
Contributor Author

I think headings are useful.

➜ asdf list                    
deno
  1.0.0
  1.0.5
firebase
  8.4.1

I don’t think that first example is weird. This example seems odd though:

➜ asdf list                    
deno
1.0.0
1.0.5
firebase
8.4.1 

Does asdf strictly require semver version strings? If not, then how do I know that firebase and 8.4.1 aren’t deno versions? Unique visual formatting (fixed indentation / relative positioning / etc.) of different data types is important, I think.

@jthegedus
Copy link
Contributor

jthegedus commented Jun 17, 2020

@jsejcksn

Does asdf strictly require semver version strings? If not, then how do I know that firebase and 8.4.1 aren’t deno versions?

EDITED below here:

It doesn't require semver version strings. EG:

java           adopt-openjdk-11.0.6+10  (set by /home/jthegedus/.tool-versions)

Good points again, thanks for you input.

I think headings are useful.

For clarity, I meant column headings, not the weird list with indentation:

PLUGIN		VERSION
deno		1.0.0
deno		1.0.5
firebase	8.4.1

I've been saying weird, but I mean inconsistent. Cols with headings is explicit and better uses both horizontal and vertical space

@jthegedus
Copy link
Contributor

Ironically, GitHub comments are now capped at ~120 chars instead of 80 😉

@Stratus3D
Copy link
Member

Couple thoughts:

  • Tools like git provide a --porcelain flag that causes commands to format output in an easy to parse format. Typically this is used by scripts that want to parse the output of a git command with another command in Bash. We could implement a similar feature. With the --porcelain flag things like indentation and column headers would be omitted.
  • I think it is possible to detect if the destination of the output we are printing is an interactive shell or not. It may make sense to provide extra information and additional formatting when printing something for a human reader, and omit such things when we know we are writing to a pipe. This may not be the most intuitive approach however.

@jsejcksn
Copy link
Contributor Author

+1 for a machine-readable-formatted option using a flag. I suggest JSON.

@jthegedus
Copy link
Contributor

jthegedus commented Aug 22, 2020

I also think that a flag to indicate output useful for scripting would be good.

I do not like --porcelain as the flag name because in Git the option --porcelain can designate inverting the command output format target. That is, if the command would output human-readable information then --porcelain will output a machine-readable format. If the command was "porcelain" by default and output machine-readable by default, then --porcelain will output human-readable.

@jsejcksn what I've meant with machine-readable is that there's a standard, known format sent to stdout/stderr. That is, no headers, no forced wrapping of content. I don't think we'll be tackling outputting in JSON. If each row of data is predictably formatted, then simple Shell scripting tools like grep,sed,awk, cut etc is what I would be expecting people to use.

What I am thinking is this:

  • by default: output human-readable, 80 char width (see below), with headers.
  • with --some-flag: output machine-readable, no char limit per row, no headers.

I'm leaning away from wrapping content within columns as I initially proposed.


Thinking out loud, but perhaps in the default human-readable output we don't limit to 80 char and instead also introduce a flag for limiting to 80 char width. I say this because some of the command output we have is very difficult to limit to 80 chars. As an example, asdf current outputs 3 columns of data, the second of which is a version of a plugin installed. In the case of Java, the average length of a version is 30 chars, with the longest being 48 chars alone. The third column is a Path to the tool-versions file or debug information. All data sources for these columns, except the debug information, come from the user (plugin name can be anything they want) or the plugin itself.

With printf we can truncate output in columns easily and achieve some semblance of 80 char limit while still outputting useful information, but the users would need to understand we're truncating the output and how to get the actual values.

Ultimately, I'm leaning more towards not truncating the data, letting the rows be as wide as they want, and then if provided a flag (or ASDF config value, or we detect Shell columns) we can use a different printf format that does truncate. Essentially, swapping out the format arg to printf. The flag --less comes to mind.

@jsejcksn
Copy link
Contributor Author

jsejcksn commented Sep 9, 2020

a standard, known format

Does this mean something like TSV with variable-length whitespace instead of single tabs?

JSON was just a suggestion, but if it's machine-readable I think adhering to any standard with a specification would be preferable to a consistent, arbitrary format. That way, no one has to write a new parser.

@jthegedus
Copy link
Contributor

Does this mean something like TSV with variable-length whitespace instead of single tabs?

Yes, TSV or space-delimited with variable-length whitespace. Again, my proposal is to make it essentially the same as the human readable without header rows and a known number of cols and separator. Though we could investigate a separate output just for machines along the lines of git --porcelain

That way, no one has to write a new parser.

Our goal here would be to align with existing shell applications that allow simple scripting and piping. I wouldn't put that in the same bucket as parsing nor --porcelain in the same bucket as defining yet another JSON spec. Targeting simple scripting and piping is pedestrian IMO.

As an example, here is what git status --porcelain outputs when run against a repo with an untracked file:

asdf-jthegedus on  fix/update-all-plugins [?] 
➜ git status            
On branch fix/update-all-plugins
Your branch is up to date with 'origin/fix/update-all-plugins'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        test.txt

nothing added to commit but untracked files present (use "git add" to track)

asdf-jthegedus on  fix/update-all-plugins [?] 
➜ git status --porcelain
?? test.txt

The output is intended to be machine-readable, but certainly not a data-interchange format like JSON

@jsejcksn
Copy link
Contributor Author

jsejcksn commented Sep 9, 2020

Thanks for explaining; I understand better.

Since there are two modes:

  • one intended for humans to read using eyes (or ears with a screen reader)
  • another for machines to parse via scripting

...is there any disadvantage to utilizing a standardized interchange format (like TSV, JSON, etc.) for the latter?

@jthegedus
Copy link
Contributor

I was leaning towards simple TSV for the --porcelain flag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants