openterm
openterm copied to clipboard
Suggestion: Better xterm compatibility
Would it be possible to have the terminal window emulate a xterm? With control caracters to change font color, bold, etc?
Try "curl -s wttr.in/london" in a MacOS Terminal window and in Terminal so see the effects.
This could probably be done by using a WebView instead of the TextView, and combining with xterm.js https://github.com/xtermjs/xterm.js?files=1 Seems like a big change, though.
You can look at my app for how to do this: https://github.com/tbodt/ish/blob/master/app/TerminalView.m https://github.com/tbodt/ish/tree/master/app/xtermjs
I have some objective-c code for converting a NSString
into a NSAttributtedString
where some ANSI escape sequences are interpreted. Avoiding a trip through a web-context might be worth considering.
You can get really far by supporting foreground & background colours sequences and ignoring all other escape sequences.
@tbodt Yes, I'm also using xterm.js, it's very useful and easy to implement
I think NSAttributedString & CoreText is probably the best approach for this app. I'd like to not have to deal with WKWebView and its own sandboxing & file I/O issues if possible.
@palmin Where can I find this code? I'd like to Swift-ify it.. 🙂
Very simple and only supports the most basic foreground/background color commands, but it should be easy to adjustAnsiTextState
to support additional commands.
@interface AnsiTextState : NSObject {
UIColor* foregroundColor;
UIColor* backgroundColor;
}
-(NSUInteger)parseRange:(NSRange)range text:(NSString*)text;
-(NSDictionary*)attributes;
@end
@implementation AnsiTextState
-(void)reset {
foregroundColor = nil;
backgroundColor = nil;
}
-(UIColor*)colorWithIndex:(int)index {
if(index == 0) return [UIColor blackColor];
if(index == 1) return [UIColor redColor];
if(index == 2) return [UIColor greenColor];
if(index == 3) return [UIColor yellowColor];
if(index == 4) return [UIColor blueColor];
if(index == 5) return [UIColor magentaColor];
if(index == 6) return [UIColor cyanColor];
if(index == 7) return [UIColor whiteColor];
return nil;
}
-(void)setForegroundColor:(int)index {
foregroundColor = [self colorWithIndex:index];
}
-(void)setBackgroundColor:(int)index {
backgroundColor = [self colorWithIndex:index];
}
-(NSUInteger)parseRange:(NSRange)range text:(NSString*)text {
NSUInteger pos = range.location, end = range.location + range.length;
while(pos < end) {
// Ends at ASCII 64 to 126 (@ to ~ / hex 0x40 to 0x7E)
unichar ch = [text characterAtIndex:pos];
if(ch >= 64 && ch <= 126) {
// we have start and end and run through updating state
NSString* payload = [text substringWithRange:NSMakeRange(range.location, pos - range.location)];
NSArray<NSString*>* commands = [payload componentsSeparatedByString:@";"];
for (NSString* command in commands) {
int cmd = [command intValue];
if(cmd == 0) [self reset];
if(cmd >= 30 && cmd <= 37) [self setForegroundColor: cmd-30];
if(cmd >= 40 && cmd <= 47) [self setBackgroundColor: cmd-40];
}
return pos+1;
}
pos += 1;
}
return end;
}
-(NSDictionary*)attributes {
NSMutableDictionary* attr = [NSMutableDictionary new];
if(foregroundColor != nil) attr[NSForegroundColorAttributeName] = foregroundColor;
if(backgroundColor != nil) attr[NSBackgroundColorAttributeName] = backgroundColor;
return attr;
}
@end
@implementation NSString (AnsiEscapeSequence)
-(NSAttributedString*)interpretedEscapeSequences {
NSMutableAttributedString* string = [NSMutableAttributedString new];
AnsiTextState* state = [AnsiTextState new];
NSUInteger pos = 0, len = self.length;
while(pos < len) {
// skip forward to next escape sequence
NSUInteger next = [self rangeOfString:@"\e[" options:0 range:NSMakeRange(pos, len-pos)].location;
if(next == NSNotFound) next = len;
if(pos < next) {
NSString* substr = [self substringWithRange:NSMakeRange(pos, next - pos)];
NSAttributedString* substring = [[NSAttributedString alloc] initWithString:substr
attributes:state.attributes];
[string appendAttributedString: substring];
}
// parse escape sequence and adjust state
NSUInteger done = [state parseRange: NSMakeRange(next+2, len-next-2) text:self];
pos = done;
}
return string;
}
@end
Maybe it would make sense to disable the ansi color commands when using a non-standard background color in the app or perhaps just to have a warning where you change the background color?
Most of these ansi color coded outputs are only readable if you have a dark background or if the colors are adjusted to match a non-standard background color.
This issue is being (partially?) addressed by #62, btw.
@palmin Good point. I noticed Terminal on macOS lets users change ANSI colors, which solves background color issues. Not sure if we should go that far, though.
Yes. The escape sequence color/style stuff seems to happen in #62 which seems like a good idea as this issue here is very broad.
More work on this in #85. We can support curl
progress bars now!