Search code examples
pythoncolorscoloramatermcolor

How to change the color of a specific word from user in "input()"


Probably in every code editor there is such a function when you type the valid word "class" and it is highlighted by another color. Is it possible to do this in a console with "input()"? The user writes any word as the same "class" and it is highlighted by some color in the console, all during input.

I know a way to do all the input in one piece, but I need to repaint just some of the words that the user is typing.


Solution

  • While you can alter the colours in the terminal using a library such as curses or termcolor, it is more challenging than the basic input function provided by python.

    I have an example here for you, using curses but it could be accomplished easier in your own window, rather than the terminal, using for example tkinter or one of the python qt libraries.

    Essentially you will need to read the input from the user, see if it was a match to any of your special words, then go replace the special word with the coloured variant. The version I have here is very minimal, it doesn't support backspace and doesn't check if your special word is buried in a larger word and so on, but might be enough to get you started, if you choose to go this route.

    import curses
    
    RED_PAIR = 1
    BLUE_PAIR = 2
    
    def main(stdscr: curses.window):
        # This must be called before you can initialize the colour pairs
        curses.start_color()
        # Choose a couple random colours
        curses.init_pair(RED_PAIR, curses.COLOR_RED, curses.COLOR_BLACK)
        curses.init_pair(BLUE_PAIR, curses.COLOR_CYAN, curses.COLOR_BLACK)
        # Create a lookup of all the special words that should be highlighted
        special_words = {
            'class': curses.color_pair(RED_PAIR),
            'def': curses.color_pair(BLUE_PAIR)
        }
        # Clear the window
        stdscr.clear()
        # Look forever, which can exited using ctrl-c
        while True:
            # Refresh the screen
            stdscr.refresh()
            # Get the next user input
            new_char = stdscr.getkey()
            # Put that in the screen
            stdscr.addstr(new_char)
            # Refresh so that the cursor moves and the text is drawn to the screen
            stdscr.refresh()
            # Get the cursor position it can be reset after all these function calls
            y, x = curses.getsyx()
            # Check each special word, looking for a match
            for special_word, colour_pair in special_words.items():
                # First, check the length to make sure there is enough space
                char_len = len(special_word)
                if x < char_len:
                    # Not enough space for this word
                    continue
                # Grab the characters to check
                match_candidate = stdscr.instr(y, x - char_len, char_len).decode()
                if match_candidate == special_word:
                    # There was a match, set the colour and add the text on top of it
                    stdscr.attron(colour_pair)
                    stdscr.addstr(y, x - char_len, special_word)
                    stdscr.attroff(colour_pair)
            # Reset the cursor, all the above functions move it around
            stdscr.move(y, x)
    
    if __name__ == '__main__':
        curses.wrapper(main)
    

    Which produces an output like the following image:

    sample image showing class and def highlighted using curses

    Let me know if you have any questions.