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

Buildkite timestamps only at start of line, via screenLine.metadata #99

Merged
merged 1 commit into from
Aug 9, 2022

Conversation

pda
Copy link
Member

@pda pda commented Aug 5, 2022

As discussed in #96 and #97, Buildkite timestamps (<ESC>_bk;t=123456<BEL>) were corrupting virtual terminal X position when moving the cursor backwards through them (“CUB”: <ESC>[<distance>D). This is because they occupied a position on the terminal intermediate representation X,Y grid, and parsing/appending them incremented the internal X-position state. Buildkite timestamps should generally only appear at the start of lines, where this isn't a problem, but certain ANSI sequences cause buildkite/agent to place them mid-line.

This pull request introduces a “line metadata” structure, supporting namespaced key:value data attached to entire lines. For example the Buildkite APC <ESC>_bk;t=1234<BEL> places t1234 under the bk namespace. Subsequent _bk;t APCs on the same line are ignored. The bk namespace is then rendered as <?bk t="1234"?> prior to the line's terminal data, so it's always at the start of the line.

Some refactoring to achieve that:

  • _bk data is no longer represented as a subtype of struct element, but as metadata map[string]map[string]string on a new struct screenLine which wraps the bare []node that previously represented a line.
  • A new screen.setnxLineMetadata() method, with a Redis-inspired name indicating that it only sets keys that don't already exist.
  • screen.getCurrentLineForWriting() *screenLine replaces the previous growScreenHeight() and growLineWidth() since those were always used in conjunction to prepare for writing data that might be out of existing bounds, and this was also required prior to adding _bk;t metadata to a new line.
  • rendering <?bk …?> to HTML is separated out of the rendering of struct element because it's no longer one of those.

This PR is currently based on some other more minor refactoring that was extracted from a previous approach that I don't want to merge.

Related:

Copy link

@moskyb moskyb left a comment

Choose a reason for hiding this comment

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

This LGTM other than some minor readability and stylistic quibbles. Awesome work!

Comment on lines +32 to +39
keys := make([]string, len(data))
// Make a list of the map's keys
i := 0
for key := range data {
keys[i] = key
i++
}
sort.Strings(keys)
Copy link

@moskyb moskyb Aug 7, 2022

Choose a reason for hiding this comment

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

I'm not 100% sure if we have another use for i here, but if not we could replace it with either of these:

Suggested change
keys := make([]string, len(data))
// Make a list of the map's keys
i := 0
for key := range data {
keys[i] = key
i++
}
sort.Strings(keys)
keys := maps.Keys(data)
sort.Strings(keys)

or alternatively, if we don't want to use the experimental maps module:

Suggested change
keys := make([]string, len(data))
// Make a list of the map's keys
i := 0
for key := range data {
keys[i] = key
i++
}
sort.Strings(keys)
keys := make([]string, 0, len(data))
for key := range data {
keys := append(keys, key)
}
sort.Strings(keys)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah interesting. TIL about https://pkg.go.dev/golang.org/x/exp/maps but I think I'll avoid bringing that in while shifting this existing code. Hopefully x/exp/maps is in stdlib by the time we look at this again and we'll use it then :)

Re. eliminating the i++ etc, looks like the existing code was following the pattern described in https://stackoverflow.com/a/27848197 which is “slightly [less] concise, but slightly [more] efficient … You already know how big it's going to be so you don't even need to use append”.

I'll just let sleeping dogs lie 🙈

b.buf.WriteString("<?" + namespace)
for i := range keys {
key := keys[i]
value := strings.Replace(data[key], `"`, "&quot;", -1)
Copy link

@moskyb moskyb Aug 7, 2022

Choose a reason for hiding this comment

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

we could probably do this a little more rigorously by doing:

Suggested change
value := strings.Replace(data[key], `"`, "&quot;", -1)
value := html.EscapeString(data[key])

... buuuuut, looking into it, that method uses and HTML escape code for quotes (&#34;) rather than an entity name (&quot;), so if we can't deal with that on the backedn then we should probably keep as is.

Copy link
Member Author

@pda pda Aug 9, 2022

Choose a reason for hiding this comment

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

👍🏼 I think the numeric entity would be fine, and escaping the other 4 chars (<, >, & and ' in addition to ") would probably be beneficial in cases where we include those chars in the input.

However, another let-sleeping-dogs-lie for this existing code that I'm moving 😅 🐶💤🛌

parser.go Outdated Show resolved Hide resolved
@@ -38,7 +37,6 @@ func TestParseZeroWidthAPC(t *testing.T) {
// Application Program Command can be followed by normal text
func TestParseAPCPrefix(t *testing.T) {
s := parsedScreen("\x1b_bk;t=0\x07hello")
t.Skip("zero-width _bk;t=... bug")
Copy link

Choose a reason for hiding this comment

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

🙌

Base automatically changed from refactor-before-zero-width-bk-timestamps to main August 9, 2022 04:02
@pda pda merged commit ed99d1a into main Aug 9, 2022
@pda pda deleted the bk-timestamp-per-line branch August 9, 2022 05:35
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.

Bug with rendering characters in Gradle progress output
2 participants