10

I am using Python 2's cmd module to make a command line for a program. Everything works nicely as long as I don't add color to my prompt.

Working code:

from cmd import Cmd

class App(Cmd):

    def __init__(self):
        Cmd.__init__(self)
        self.prompt = "PG ["+ (str('username'), 'green') +"@"+ str('hostname') +"]: "

    def do_exit(self, line):
        '''
        '''
        return True

App().cmdloop()

When I change my code as below, if I enter a long command or try to search in the command history, some characters stick to my prompt.

Problem code:

from cmd import Cmd

class App(Cmd):

    def __init__(self):
        Cmd.__init__(self)
        self.prompt = "PG ["+ self.colorize(str('username'), 'green') +"@"+ str('hostname') +"]: "

    colorcodes =    {'green':{True:'\x1b[32m',False:'\x1b[39m'}}


    def colorize(self, val, color):
        return self.colorcodes[color][True] + val + self.colorcodes[color][False]

    def do_exit(self, line):
        '''
        '''
        return True

App().cmdloop()

You can see this problem in asciicasts. The problem also exists with the cmd2 module.

cxw
  • 16,685
  • 2
  • 45
  • 81
RaminNietzsche
  • 2,683
  • 1
  • 20
  • 34

1 Answers1

11

Just add markers to your color codes:

    colorcodes =    {'green':{True:'\x01\x1b[32m\x02',False:'\x01\x1b[39m\x02'}}
                                  # ^^^^        ^^^^         ^^^^        ^^^^

In your asciicast, you have problems when you leave i-search mode and the prompt is re-printed. That is because Python doesn't know that the escape characters don't actually take up space on the screen. Putting \x01 before each escape sequence, and \x02 after each escape sequence, tells Python to assume those characters take up no space, and so the prompt will be correctly reprinted.

This is the same solution as in this answer, which had the corresponding problem in a different context. There is an open issue to mention this in the Python readline documentation, but I don't see that it has yet been done.

I tested the above colorcodes values with Python 2.7.12 on Cygwin, running in mintty. In the prompt, username printed green and everything else printed the default (light gray). I used the standard system cmd module, not cmd2 (which you linked).

Community
  • 1
  • 1
cxw
  • 16,685
  • 2
  • 45
  • 81
  • 1
    Thank you so much! I had this issue with a `readline`-based interface and it was driving me nuts. I ended up with some ugly workaround of including some parts of my coloured prompt in `print()` with `end=''` and some in `input()`. This answer was exactly what I was looking for, but could not find when I needed it. – creativecoding Sep 02 '20 at 22:04