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

Fix graphical label alignment issues #202

Merged
merged 2 commits into from
Sep 10, 2022

Conversation

olivia-fl
Copy link
Contributor

Fixes #87 and #97.

Turns out that reliably determining the width of a string when displayed in a terminal is impossible without using ANSI escapes to get the current cursor position. Relying on the ANSI escapes is an option, but won't work if your output isn't a TTY. Supporting non-TTY output is important for things like redirecting error messages to a file, so I implemented a mostly correct label alignment process that makes some specific tradeoffs. In the future, we could also add the escape-based option, and just fall back to this behavior on non-TTY output.

Back in #87 (comment), I had an idea for a hack to fix tab alignment while still respecting the terminal's tabstop setting. Unfortunately, I couldn't figure out a way to make this work for underlines, so the remaining option is to give up and replace tabs with spaces. This allows us to determine the visual width of the output reliably, but it does mean that we're ignoring the user's tabstop preferences. The original default behavior, where tabs were emitted verbatim and highlight alignment was broken, is pretty much never desirable, so I ended up just removing that option.

One of the issues with replacing tabs with a fixed number of spaces is that it will produce bad results if tabs are used for alignment in the middle of a line. To avoid this, we can emulate the behavior of the terminal, where tabs shift the cursor to the next tabstop rather than by a fixed width. Existing compilers that I checked seem to go both ways on this one. Gcc, clang, and rustc all emulate tabstops, while ghc uses a fixed width. I implemented the tabstop emulation option, which is a second breaking change, but this should be easy enough to swap out if we don't want it.

The other tradeoff here is that unicode_width isn't actually going to be able to predict the width of characters displayed on an arbitrary terminal. For example, 🏳️‍🌈 could show up as 1, 2, 3, or 4 columns. Predicting this is, in the general case, not possible. unicode_width is still better than assuming every character is 1 column though, so yay for incremental improvement.

Computers are a mess lol.

Fixes: zkat#87

BREAKING CHANGE:
Tabs are always expanded to spaces by the graphical handler, and `tab_width` now defaults to 4. Instead of replacing every tab with a fixed number of spaces, spaces are used to align to the next tabstop. `tab_width` controls the space between tabstops rather than the fixed width of each tab character.
Copy link
Owner

@zkat zkat left a comment

Choose a reason for hiding this comment

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

This is a great PR! Thanks so much for taking the time to do this!

I think you made the right compromises here and I think it's probably overkill to start trying to understand terminals themselves. Maybe some day, but not now :P

I'm really happy to see this so significantly improved! I'll push out a release with this soon.

@zkat
Copy link
Owner

zkat commented Sep 10, 2022

Note: I don't consider this a breaking change because rendering isn't part of the API contract for miette, so this is gonna just go out as a patch release.

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.

Indentation with tabs throws off labels
2 participants