Skip to content

Conversation

@dwnusbaum
Copy link
Member

@dwnusbaum dwnusbaum commented Nov 16, 2018

Fixes #135.

This PR currently adds tests and fixes for 2 issues:

  1. If an escape sequence is not closed at the end of a line, its tag cannot be closed and its style modifications will carry over to all subsequent lines.
    • This PR currently closes all tags that are open at the end of a line and then reopens them on the next line so that ANSI sequences in the line can close those tags correctly. I am not sure whether this is the desired behavior, or if we should instead close any tags that are open at the end of a line and leave them closed for good.
  2. When using a color map with a default foreground or background, the default foreground and background are not used.
    • This PR handles default foreground and background settings. I am not sure how often default foreground/background colors are used, so maybe we don't care about this, but it was easy enough to add support for it.

@dblock
Copy link
Member

dblock commented Nov 16, 2018

This test seems to have passed ...

}

@Test
@Ignore
Copy link
Member Author

Choose a reason for hiding this comment

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

@dblock The test is ignored, so it never even ran.

@dwnusbaum dwnusbaum changed the title Demonstrate problem with multiline escape sequences Demonstrate problem with escape sequences spanning multiple lines Nov 16, 2018
@dwnusbaum
Copy link
Member Author

I am working on a fix for this which may help with JENKINS-34019 because the fix here will close open tags at the end of each line and reopen them on the next line, which seems to be the suggested approach to fix that ticket.

return this;
return openTags == nextOpenTags
? this
: new ColorConsoleAnnotator(colorMapName, charset, nextOpenTags);
Copy link
Member Author

Choose a reason for hiding this comment

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

Based on my reading of the ConsoleAnnotator Javadocs, if we want to track state from line-to-line then we need to handle it by returning a new instance like this. Maybe wrong to return this instead of creating a new state when the open tags match.

Copy link
Member

Choose a reason for hiding this comment

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

I thought the suggested fix for JENKINS-34019 was just to close all outstanding tags at the end of a line, as if this were a user error, not to try to make them apply across lines.

Copy link
Member Author

@dwnusbaum dwnusbaum Nov 19, 2018

Choose a reason for hiding this comment

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

When a style continues to the next line, the ConsoleAnnotatorFactory can insert a closing tag at the end of the line and an opening tag at the beginning of the next line. This prevents the timestamps from being affected.

Full comment. What you propose would certainly be simpler, but I'm not sure what the expected behavior is. What do you think should be the behavior for the test case I added?

Copy link
Member

@jglick jglick Jan 3, 2019

Choose a reason for hiding this comment

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

I feel like it does not matter much, since it is unlikely people would have multiline formatting intentionally, so it is OK to keep the implementation simpler.

Copy link
Member Author

Choose a reason for hiding this comment

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

#145 shows a problem with output of terraform at the command line, and based on a screenshot of the text it looks like it might be using multiline formatting, but I'm not sure. I'll leave this as-is for now until I have time to investigate in more depth.

Copy link
Member

Choose a reason for hiding this comment

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

Confirmed via

docker run --rm hashicorp/terraform plan 2>&1 | od -a

(Though it also looks like the Terraform output is buggy—ANSI sequences are not nestable, and it is emitting several esc [ 0 m sequences in a row.)

System.out.print(html);
assertThat(html.replaceAll("<span style=\"display: none\">.+?</span>", ""),
allOf(
containsString("<b><span style=\"color: #1E90FF;\">This text should be bold and blue\n</span></b>"),
Copy link
Member Author

Choose a reason for hiding this comment

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

The literal \n characters in these tests would probably cause them to fail on Windows.

});
}

@Test
Copy link
Member Author

Choose a reason for hiding this comment

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

I should create a JIRA issue for these and add @Issue.

@dwnusbaum
Copy link
Member Author

Needs to be merged with master after the change to use comments instead of display: none in #142.

@dblock
Copy link
Member

dblock commented Dec 16, 2018

@jglick Can I leave this to you to review/merge/comment?

try (AnsiHtmlOutputStream ansiOs = new AnsiHtmlOutputStream(outgoing, colorMap, emitter, openTags);
CountingOutputStream incoming = new CountingOutputStream(ansiOs)) {
emitter.incoming = incoming;
// We need to write one UTF-16 code unit at a time so that byte offsets match Java character offsets when inserting HTML.
Copy link
Member

Choose a reason for hiding this comment

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

Needs to be resolved in favor of #143, right?

@dwnusbaum dwnusbaum changed the title Demonstrate problem with escape sequences spanning multiple lines Allow escape sequences to span multiple lines and support default fg/bg colors Jan 4, 2019
Copy link
Member

@jglick jglick left a comment

Choose a reason for hiding this comment

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

Not sure the extra complexity is worth it for a relatively uncommon use case, but maybe users would consider lack of multiline support a regression from the old implementation.

Anyway, it has test coverage, so (other than the logging buglet) go for it.

this.colorMapName = colorMapName;
LOGGER.fine("creating annotator with colorMapName=" + colorMapName);
this.openTags = openTags;
LOGGER.log(Level.FINE, "creating annotator with colorMapName={0} openTags={2}", new Object[] { colorMapName, openTags });
Copy link
Member

Choose a reason for hiding this comment

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

{1} not {2}

AnsiColorMap colorMap = Jenkins.get().getDescriptorByType(AnsiColorBuildWrapper.DescriptorImpl.class).getColorMap(colorMapName);
List<AnsiAttributeElement> nextOpenTags = openTags;
// TODO: As a performance improvement, we could create a branch where `s.indexOf('\u001B') == -1` but other
// conditions are true that surrounds the text in the appropriate tags without going through AnsiHtmlOutputStream.
Copy link
Member

Choose a reason for hiding this comment

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

Unnecessary—this case is likely quite rare.

int outCount = outgoing.getCount() + adjustment;
// All ANSI escapes sequences contain at least 2 bytes on modern platforms, so any HTML emitted
// directly after the first character is received is due to the initialization process of the stream and
// belongs at position 0 (i.e. default background/foreground colors).
Copy link
Member

Choose a reason for hiding this comment

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

I think I need more caffeine to understand this. :-)

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 this code was and still is quite confusing to me. Thankfully, the if statement is covered by the newly added tests, and if it is removed the tests fail, so we should be able to avoid future regressions.

if (colorMap.getDefaultBackground() != null || colorMap.getDefaultForeground() != null) {
// The default color scheme will be opened automatically at the beginning of the stream on the next
// line, so we don't want to duplicate it.
nextOpenTags.remove(0);
Copy link
Member

Choose a reason for hiding this comment

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

.subList(1) might be less scary to readers, though I see that the getter does make a fresh copy.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. I tried switching to sublist, but the implementation is not serializable so it would require another copy, which seems excessive, so I added a comment to help clarify for future readers but left the original code.

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.

sgr0 not supported anymore

3 participants