-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[Basic] Add TerminalController class #502
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
Conversation
Will need to refactor ColorWrap and Verbosity utilities once this is in |
|
||
/// Returns the color code which can be prefixed on a string to display it in that color. | ||
/// Note: To avoid unexpected behavior the color codes are private and not exposed to clients | ||
/// other than TerminalController. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the unexpected behavior you are worried about here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Err that comment came out wrong, I meant to write there is no need for clients to know about this string because they shouldn't be manually inserting the color code in string. I'll reword
This is very cool. I have one major annoying request, which is that we have some test coverage for this. It's annoying enough that I fear if we don't do it initially, it won't get done and this is tricky enough that I'd like to know we have some coverage. NOTE: You can open a PTY ( |
Yeah I was planning to write tests, at least writing to a string stream tests, as mentioned in the PR desc. I wanted to know if the class makes sense first. |
36569c5
to
531e8c1
Compare
@ddunbar Updated this with tests |
} | ||
|
||
/// Creates colored or simple progress bar based on output stream provided stream. | ||
public func createProgressBar<Target: OutputStream>(forStream stream: UnsafeMutablePointer<Target>, header: String) -> ProgressBarProtocol { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ddunbar Not sure if this is right place for this method
1b0c270
to
5fa2c28
Compare
private let header: String | ||
private var isClear: Bool | ||
private var stream: UnsafeMutablePointer<Target> | ||
private var stream: OutputByteStream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ddunbar I am not sure which is better here, I think this operating on OutputByteStream
makes more sense but a generic OutputStream
will allow us to stream to things like strings too. I'll fixup or remove this commit once you review.
/// Constructs the instance if the stream is a tty. | ||
public init?(stream: LocalFileOutputByteStream) { | ||
// Make sure this file stream is tty. | ||
guard !(isatty(fileno(stream.fp)) == 0) else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!= 0
?
1681d75
to
3f1a504
Compare
@ddunbar updated |
} | ||
|
||
/// Moves the cursor y columns up. | ||
public func moveCursor(y: Int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, we should probably find a better label here, if this is relative. It reads like an absolute position. I don't have any particular suggestion, the best I could come up with is moveCursor(deltaY y:)
or moveCursor(up y: Int)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moveCursor(up y: Int)
seems good
LGTM!! |
I think this is missing some imports in the relocated tests:
|
huh, checking |
This will start building on liunx after swiftlang/swift#3926 is in |
let barWidth = term.width - prefix.utf8.count | ||
let n = Int(Double(barWidth) * Double(percent)/100.0) | ||
|
||
term.write("=".repeating(n: n) + "-".repeating(n: barWidth - n), inColor: .green) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fatal errors from subtraction resulting in negative Int arguments being < lowerBounds
in String.repeating
have me nervous.
Especially when prefix
is always at least a couple of characters long, precautions against one column terminals seem necessary. (Of course, doubt that anyone would desire a one column terminal 😛 , but one might result briefly from a resize — more so from tmux
or similar than from Terminal.app
.)
I’d even reason that super small terminals need a different strategy — just clamping the Int
s to a floor of 0 might produce unattractive results.
Love the direction of this PR though, cool stuff!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, this does sometimes come up especially if we honor the emacs COLUMNS
since that can sometimes drop to 0. I know we fixed an issue in Clang once where that crashed at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think returning same string (or empty string) from repeating should be good enough for such cases.
@swift-ci please test |
The swiftpm tests succeeded but looks like there is some mistake in the linux ci script (unrelated to this patch)
|
I hit:
with this running locally. |
d1c39d5
to
2d046ed
Compare
I think it ran the wrong bundle -> |
I think it is a race condition, the thread.join should be before the output is checked, no? |
2d046ed
to
344e113
Compare
ah yes. fixed |
this time OSX script has some issue, all tests succeeded tho. @ddunbar can you recheck locally again |
I still see Linux test failures sometimes... after looking at the code, I think the problem is that the pty can be closed before all the data is read off the pipe. As written, the client side needs to be closed before the thread join. |
344e113
to
b2a392d
Compare
Thanks! you're right, closing master fd before reading from it was causing the failure on linux. I wonder why CI din catch this.. |
@swift-ci please test |
I was able to implement a lit-style progress bar without using curses (yay!). Currently how it works is
TerminalController
takes any object conforming toOutputStream
and exposes APIs to perform operations like write with colour and cursor movement on it. If the output stream is not a tty then TerminalController will ignore the tty operations and write in plain text. This PR is not fully complete yet, needs tests and some calculation fixing but works. I want to get feedback for the APIs and direction.