Building a very simple wrapper for ANSI escape sequences - ruby

Maybe a mission impossible. Once upon a time I wrote a Ruby module SimpleDialog for text coloring in DOS windows. It does so by talking to the Windows APIs. Now I want to make it suitable for Mac OS as well, or even for the entire unix universe. Okay, just Terminal and xterm would satisfy my needs for the moment. ANSI escape sequences seem to be the appropriate weapons for the job. But using them I cannot always handle things as I would like to. Here is the functionality I try to realize:
def clear_screen
print "\e[H\e[2J\ec"
color( ... old colors stored in an instance variable ... )
end
The "\e[H\e[2J" part is the same as the clear command (that's how I've found this sequence [edit next day after some experiments: "\e[2J" does not erase any text sent before the "\e[H" sequence, even when it's visible on the screen]). Terminal and xterm only clear the visible part of the screen [issue 1]. The "\ec" part doesn't make sense for Terminal anymore, but this time xterm does succeed to clear the entire screen buffer. At doing so, it rings the bell [issue 2], while Terminal keeps silent. Since the "\ec" sequence resets the colors, the last settings have to be restored again. Not very elegant, altogether. The clear command submitted from the command line normally clears the entire screen buffer of xterm. However, it cannot wipe out the part that's left by my attempt to clear the screen by just sending "\e[H\e[2J" from Ruby.
def color( ... )
... too big to show here ...
end
The color method works almost fine, there's only one disappointment. In Windows, I can change the color of a rectangle without modifying the text that is already placed within it. I see no way how to do this by ANSI escape sequences [issue 3]. As a workaround I could memorize the text involved and restore it afterwards, of course. And this one is really puzzling me: the respons to "\e[6n" (report cursor position) is printed to the screen, with the last few
characters repeated after the command prompt. Can it be intercepted by Ruby [issue 4]?
The other methods are a piece of cake:
def shrink_window # to its original size
print "\e[9;0t"
end
def stretch_window # to the maximum size that fits on the screen
print "\e[9;1t"
end
def title(title = nil)
print "\e]2;#{title}\a"
end
def write(... text containing human-friendly color codes ...)
... trivial ...
end
MS-Windows allows me to get the current window title too, but I don't see real use for that (xterm doesn't listen at all when the title argument is an empty string, btw). The write method makes use of the color method. No problems here. So, in the end, four obstacles to overcome. Or to live with?

If you want help with all of these Mac OS and Unix terminal UI issues, try using the curses/ncurses libraries, through the Curses gem.
Since you already have a working Windows solution for this, you can create two classes that drive your dialog boxes. I suggest this because the presence of a curses library on Windows isn't always reliable or easy to install (see this question and its answers for examples of that problem in action). Keep your DOS implementation, then make an alternate that uses curses/ncurses. Then your code can chose which implementation to go with based on operating system or based on whether a working curses/ncurses library exists. Naturally, this path also simplifies adding even more methods of user interface later, too.

Related

Terminal MacOS emoji support

I was just wondering if anyone knows the emoji support that the Terminal.app has in MacOS?
I'm using zsh, and I end up with this when I type it out, I get the following (note I didn't add a space after the flag on the first one, but added a space on the second one but in both cases you can see there's an overlap on the flag, also note the duplication on the spaces and f on the end of the input)
And when using vim I also get
Just wondering how people resolve these?
Thanks in advance!

How curses differs Enter from Ctrl+Enter and other key strokes with overlapping escape sequences?

As far as I can see, in Konsole both CTRL+ENTER and ENTER look like byte 13 in the stdin of the running app. But when I run mc which obviously uses ncurses lib, pressing CTRL+ENTER inserts the name of the file into the command line while ENTER opens the file. How is it implemented? I tried to look into sources, but they are totally unreadable for me.
mc (midnight commander) doesn't use ncurses for input, but may use it for output. Essentially it is looking for specific character sequences.
mc makes little use of the terminfo database, essentially only to check on the xterm mouse- and alternate-screen features.
In principle, it could read the user-defined capabilities from the ncurses terminfo database (see for example ncurses trapping extended keys (Control-left, Shift-Function etc)), but it does not.
Since you are looking at sources, see the source of mc, in lib/tty/key.c, which contains tables which mc uses as a set of predefined keys. Doing it that way "works" when mc is configured to use slang, for instance, though it has the drawback that it's hardcoded and may not actually match your terminal.
However - as I said, mc does its own input. Further down in key.c, you may see a chunk in get_modifier() ifdef'd with HAVE_TEXTMODE_X11_SUPPORT. Inside that is a call which ultimately goes to XQueryPointer, which mc uses to find the current state of the modifier keys — if it happens to be running in an X display, and if the feature is enabled. You are probably seeing that.

Any way to make terminal control sequences portable?

I'm currently in the process of planning out a custom Vim-like editor. It's going to be written in C and I want it to be as portable as possible between as many types of systems as possible.
I'm aware of curses (ncurses, I suppose), the tput command, and how terminals use control sequence (Esc-[ and the CSI character) to change backgrounds, move the cursor, etc.
Of the options above, it seems like ncurses would be the most recommended way of printing for the editor. BUT ncurses also has a LOT of stuff that I rather wouldn't use, and if it's reasonably feasible I'd rather make my own system. I'm not against using it, but .. anyways.
So, my question is: Is there any way to use control sequences in the vast majority of terminals without using a library? Whether through tput or another method?
Thanks!
tput(1) uses the terminfo(5) (or older termcap(5)) database, which provides the mapping from abstract commands such as move cursor to x,y to escape sequences for different terminals. When you run a command such as
$ tput cup 10 3 # move cursor to row/column 10/3
, the terminfo database is queried to find the correct string for your terminal, which is then simply written to stdout. To find the available commands (e.g., cup), look at the cap-name column in terminfo(5). tput determines what terminal you are using by looking at the TERM environment variable.
(This means that you can check what escape characters are being generated by simply doing $ tput [command] > [file] and opening [file] in some editor that can show control characters, which can be handy for exploration. The infocmp(1) command can also be used for this.)
If you use tput (or the underlying tputs(3)), your program is hence automatically portable to different terminals. This is what Vim uses by the way.
However -- in the modern world, pretty much all terminals (or terminal emulators rather) use ANSI escape codes, along with some extensions (see XTerm Control Sequences). I believe the escapes supported by xterm and their behavior have become something of a de facto standard at this point, with other terminal emulators simply copying xterm's behavior. Some text-based UI libraries like termbox seem to do away with support for non-ANSI terminals altogether, and output ANSI escapes directly.
Besides the already-mentioned termbox, there's also S-Lang, which includes a terminal handling component. I believe those are the two most popular "ncurses replacements". I'd give ncurses some time first though.

How to run Ruby file on Sublime Text

I have a file named add_and_power.rb as below, and want to run it on Sublime Text.
def add_and_power a,b
(a+b)**(a+b)
end
puts "First number please? "
input1 = gets
puts "Second number please? "
input2 = gets
puts "The result is: ", add_and_power(input1.to_i, input2.to_i)
I run cmd+b, but it just displays,
First number please?
Second number please?
The result is:
1
[Finished in 0.9s]
I want to input 2 and 3 to get the answer. How can I make Sublime Text 2 ask for inputs and give back an answer?
If you want to run code within ST2, check out the SublimeREPL plugin, available through Package Control. You can either use IRB or pry, which is a lot more powerful. You can use it as a classic REPL (think Clojure or LISP), and you can also transfer your code from one tab into the running REPL in another tab by selection, line range, or block.
In some of my tests the pry REPL doesn't handle input through gets very well, but I haven't played around with it that much. YMMV - Edit - As AGS mentions below, use my_var = $stdin.gets for interactive input within SublimeREPL Ruby.
I highly highly recommend SublimeREPL, as it's a really powerful tool, and is self-contained within ST2, so you don't have to keep flipping back and forth to your terminal, saving and reloading your programs.
It is possible to run your programs when you need to have user input from the keyboard, but it isn't very nice.
When I do so, I need to input from a terminal that opens when I run Sublime, while also reading the response from the program at the bottom of the editor.
It is simply easier to run the program from the console/terminal.
So, the answer is, while it is possible to do so, there are concerns. You may need to use STDOUT.sync = true or STDOUT.flush to help manage the buffer with the OS, you have two thing to look at, while doing so... yuck.
It may not be the answer you are looking for, but as a developer, you should be comfortable running things from the console/terminal.

Windows 'choice' command messing up Ruby 'gets' method

Open up irb and
type gets. It should work fine.
Then try system("choice /c YN") It should work as expected.
Now try gets again, it behaves oddly.
Can someone tell me why this is?
EDIT: For some clarification on the "odd" behavior, it allows me to type for gets, but doesn't show me the characters and I have to press the enter key twice.
Terminal input-output handling is dark and mysterious art. Anyone trying to make colorized output of bash work in windows PowerShell via ssh knows that. (And various shortcutting habits like Ctrl+Backspace only make things worse.)
One of the possible reasons for your problem is special characters handling. Every terminal out there can type characters in number of different modes, and it parses its own output in search for certain character sequences in order to toggle states.
F.e. here one can find ANSI escape code sequences, one of possible supported standards among different kind of terminals.
See there Esc[5;45m? That will make all the following output to blink on magenta background. And there is significantly more stuff like that out there.
So, the answer to your question taken literally is — your choice command messes something with output modes using special escape sequences, and ruby's gets breaks in that quirk special mode of terminal operation.
But more useful will be the link to HighLine gem documentation. Why one might want to implement platform-specific and obtrusive behavior when it is possible to implement the same with about 12 LOC? All the respect for the Gist goes to botimer, I've only stumbled into his code using search.

Resources