# Module `lib.shell.editor`

Class defining a VT100 text editor. This editor works directly in the board. This allows you to make quick and easy changes directly on the board, without having to use synchronization tools. This editor allows script execution, and displays errors and execution time.

It also works on linux and osx, and can also be used autonomously, download the editor.zip and execute editor.py.

All the keyboard shortcuts are at the start of the script.

On the boards with low memory, it may work, but on very small files, otherwise it may produce an error due to insufficient memory.

Expand source code

    #!/usr/bin/python3
    # Distributed under Pycameresp License
    # Copyright (c) 2023 Remi BERTHOLET
    # pylint:disable=multiple-statements
    # pylint:disable=too-many-lines
    # pylint:disable=consider-using-f-string
    # pylint:disable=unspecified-encoding

    """ Class defining a VT100 text editor.
    This editor works directly in the board.
    This allows you to make quick and easy changes directly on the board, without having to use synchronization tools.
    This editor allows script execution, and displays errors and execution time.

    It also works on linux and osx, and can also be used autonomously, download the editor.zip and execute editor.py.

    All the keyboard shortcuts are at the start of the script.

    On the boards with low memory, it may work, but on very small files, otherwise it may produce an error due to insufficient memory.
    """
    import sys
    import tools.useful
    import tools.logger
    import tools.strings
    import tools.terminal
    import tools.filesystem
    import tools.jsonconfig

    HORIZONTAL_MOVE = 8    # Scrolling minimal deplacement

    SELECTION_START = b"\x1B[7m"
    SELECTION_END   = b"\x1B[m"

    class EditorConfig(tools.jsonconfig.JsonConfig):
            """ Editor configuration for shortcuts and tab size"""
            def __init__(self):
                    tools.jsonconfig.JsonConfig.__init__(self)
                    self.tabsize              = 4    # Tabulation size

                    # For the same action several shortcuts can be used
                    # Move shortcuts
                    self.key_up               = ["\x1b[A"]
                    self.key_down             = ["\x1b[B"]
                    self.key_right            = ["\x1b[C"]
                    self.key_left             = ["\x1b[D"]
                    self.key_home             = ["\x1b[1;3D", "\x1b[H", "\x1b\x1b[D", "\x1b[1~", "\x1bb"]
                    self.key_end              = ["\x1b[1;3C", "\x1b[F", "\x1b\x1b[C", "\x1b[4~", "\x1bf"]
                    self.key_page_up          = ["\x1b[1;3A", "\x1b[A", "\x1b\x1b[A", "\x1b[5~"]
                    self.key_page_down        = ["\x1b[1;3B", "\x1b[B", "\x1b\x1b[B", "\x1b[6~"]
                    self.key_top              = ["\x1b[1;5H"]
                    self.key_bottom           = ["\x1b[1;5F"]
                    self.key_next_word        = ["\x1b[1;5C"]
                    self.key_previous_word    = ["\x1b[1;5D"]

                    # selection shortcuts
                    self.key_sel_up           = ["\x1b[1;2A"]
                    self.key_sel_down         = ["\x1b[1;2B"]
                    self.key_sel_right        = ["\x1b[1;2C"]
                    self.key_sel_left         = ["\x1b[1;2D"]
                    self.key_sel_page_up      = ["\x1b[1;10A","\x1b[1;4A","\x1b[5;2~"]
                    self.key_sel_page_down    = ["\x1b[1;10B","\x1b[1;4B","\x1b[6;2~"]
                    self.key_sel_home         = ["\x1b[1;2H","\x1b[1;10D"]
                    self.key_sel_end          = ["\x1b[1;2F","\x1b[1;10C"]
                    self.key_sel_top          = ["\x1b[1;6H"]
                    self.key_sel_bottom       = ["\x1b[1;6F"]
                    self.key_sel_next_word    = ["\x1b[1;6C","\x1b[1;4C"]
                    self.key_sel_prev_word    = ["\x1b[1;6D","\x1b[1;4D"]

                    self.key_sel_all          = ["\x01"]                              # Select All
                    # ctrl-b unused
                    self.key_copy             = ["\x03","\x1bc"]                      # Copy
                    # ctrl-d unused
                    self.key_execute          = ["\x05", "\x1b[15~"]                  # Execute script
                    self.key_find             = ["\x06", "\x1BOQ"]                    # Find
                    self.key_goto             = ["\x07"]                              # Goto line
                    self.key_backspace        = ["\x08","\x7F"]                       # Backspace
                    self.key_indent           = ["\x09"]                              # Indent
                    # line feed reserved
                    # ctrl-k unused
                    self.key_del_line         = ["\x0C"]                              # Delete line
                    self.key_new_line         = ["\x0D", "\0x0A"]                     # New line pressed
                    self.key_find_next        = ["\x0E", "\x1bOR"]                    # Find next
                    # ctrl-o unused
                    self.key_find_previous    = ["\x10", "\x1b[1;2R"]                 # Find previous
                    self.key_comment          = ["\x11"]                              # Comment block
                    self.key_replace          = ["\x12"]                              # Replace
                    self.key_save             = ["\x13", "\x1bs"]                     # Save
                    self.key_toggle_mode      = ["\x14"]                              # Toggle replace/insert mode
                    self.key_change_case      = ["\x15"]                              # Change case
                    self.key_paste            = ["\x16","\x1bv"]                      # Paste
                    # ctrl-w unused
                    self.key_cut              = ["\x18","\x1bx"]                      # Cut
                    # ctrl-y unused
                    # ctrl-z unused

                    self.key_exit             = ["\x1b"]                              # Exit

                    self.key_delete           = ["\x1b[3~"]                           # Delete pressed
                    self.key_unindent         = ["\x1b[Z"]                            # Unindent

    def is_enough_memory():
            """ Indicate if it has enough memory """
            import gc
            try:
                    # pylint: disable=no-member
                    memory  = gc.mem_free()
            except:
                    memory  = 256*1024
            if memory < 50*1024:
                    return False
            return True

    class View:
            """ Class which manage the view of the edit field """
            def __init__(self, cfg, view_height, view_top, extension=None):
                    """ Constructor """
                    self.line     = 0
                    self.column   = 0
                    self.cfg      = cfg
                    if view_height is None:
                            self.height   = 20
                    else:
                            self.height             = view_height

                    self.colorize = self.colorize_none
                    if is_enough_memory():
                            if extension is not None:
                                    if extension.lower() == ".py":
                                            # pylint: disable=import-error
                                            try:
                                                    try:
                                                            from editor_py import Colorizer
                                                    except:
                                                            from shell.editor_py import Colorizer
                                                    self.colorizer = Colorizer()
                                                    self.colorize = self.colorize_syntax
                                            except:
                                                    pass

                    self.width                  = 80
                    self.top                    = view_top
                    self.is_refresh_all         = True
                    self.is_refresh_line        = False
                    self.is_refresh_line_before = False
                    self.is_refresh_line_after  = False
                    self.refresh_part           = None
                    self.text                   = None
                    self.tab_cursor_column      = 0
                    self.sel_line_start         = None
                    self.sel_line_end           = None
                    self.screen_height          = 1
                    self.screen_width           = 1
                    if tools.filesystem.ismicropython():
                            self.write = self.write_byte
                    else:
                            self.write = self.write_string

            def write_byte(self, data):
                    """ Write data to stdout in byte (for micropython only) """
                    sys.stdout.write(data)

            def write_string(self, data):
                    """ Write data to stdout """
                    sys.stdout.write(tools.strings.tostrings(data))

            def flush(self):
                    """ Flush text to stdout """
                    try:
                            sys.stdout.flush()
                    except:
                            pass

            def set_text(self, text):
                    """ Set the text object """
                    self.text = text

            def get_screen_position(self):
                    """ Get the screen position of cursor """
                    return (self.text.get_cursor_line() - self.line + self.top, self.tab_cursor_column - self.column)

            def reset(self):
                    """ Reset VT100 """
                    self.write(b"\x1B"+b"c")
                    self.flush()

            def reset_scroll_region(self):
                    """ Reset VT100 scroll region """
                    if self.screen_height > 0:
                            self.set_scrolling_region(0, self.screen_height-1)

            def set_scrolling_region(self, top_line, bottom_line):
                    """ Define VT100 scroll region """
                    if top_line < bottom_line:
                            self.write(b"\x1B[%d;%dr"%(top_line+1,bottom_line+1))

            def scroll_up(self):
                    """ Scroll to up """
                    self.set_scrolling_region(self.top, self.height+1)
                    self.write(b"\x1B[1S")

            def scroll_down(self):
                    """ Scroll to down """
                    self.set_scrolling_region(self.top, self.height+1)
                    self.write(b"\x1B[1T")

            def scroll_part_up(self):
                    """ Scroll the upper part """
                    line, column = self.get_screen_position()
                    if line < self.height:
                            self.set_scrolling_region(line +1, self.height+1)
                            self.write(b"\x1B[1S")
                    elif line == self.height:
                            self.write(b"\x1B[%d;1f\x1B[K"%(self.height + 1 + self.top))

            def scroll_part_down(self):
                    """ Scroll the lower part """
                    line, column = self.get_screen_position()
                    if line < self.height:
                            self.set_scrolling_region(line+1, self.height+1)
                            self.write(b"\x1B[1T")
                    else:
                            self.is_refresh_line_after = True

            def move(self):
                    """ Move the view """
                    self.tab_cursor_column = self.text.get_tab_cursor(self.text.get_cursor_line())
                    # Move view port
                    if self.tab_cursor_column < self.column:
                            self.is_refresh_all = True
                            if self.tab_cursor_column > HORIZONTAL_MOVE:
                                    self.column = self.tab_cursor_column-HORIZONTAL_MOVE
                            else:
                                    self.column = 0
                    elif self.tab_cursor_column >= self.column + self.width:
                            self.column = self.tab_cursor_column-self.width+HORIZONTAL_MOVE
                            self.is_refresh_all = True
                    if self.text.get_cursor_line() < self.line:
                            delta = self.line - self.text.get_cursor_line()
                            self.line = self.text.get_cursor_line()
                            if self.line < 0:
                                    self.line = 0
                            if delta <= 1:
                                    self.scroll_down()
                                    self.is_refresh_line = True
                            else:
                                    self.is_refresh_all = True
                    elif self.text.get_cursor_line() > self.line + self.height:
                            delta =  self.text.get_cursor_line() - self.line - self.height
                            self.line = self.text.get_cursor_line()-self.height
                            if delta <= 1:
                                    self.scroll_up()
                                    self.is_refresh_line = True
                            else:
                                    self.is_refresh_all = True

            def set_refresh_line(self):
                    """ Indicates that the line must be refreshed """
                    self.is_refresh_line = True

            def set_refresh_after(self):
                    """ Indicates that all lines after the current line must be refreshed """
                    self.is_refresh_line = True
                    self.is_refresh_line_after = True

            def set_refresh_before(self):
                    """ Indicates that all lines before the current line must be refreshed """
                    self.is_refresh_line = True
                    self.is_refresh_line_before = True

            def set_refresh_all(self):
                    """ Indicates that all lines must be refreshed """
                    self.is_refresh_all = True

            def set_refresh_bottom(self, cursor):
                    """ Refresh from the cursor to the end of screen """
                    self.refresh_part = [cursor, cursor+self.height+self.height]

            def show_line(self, current_line, screen_line, selection_start, selection_end, quick=False):
                    """ Show one line """
                    if not quick:
                            clear_line = b"\x1B[%d;1f\x1B[K"%(screen_line+1)
                    else:
                            clear_line = b""
                    count_line = self.text.get_count_lines()
                    if current_line < count_line and current_line >= 0:
                            # If the line selected
                            if selection_start is not None:
                                    _, sel_line_start, sel_column_start = selection_start
                                    _, sel_line_end,   sel_column_end   = selection_end
                                    # If the line is completly selected
                                    if current_line > sel_line_start and current_line < sel_line_end:
                                            part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, True)
                                            if (len(part_line) == 0):
                                                    part_line = b" "
                                            self.write(clear_line+SELECTION_START+part_line+SELECTION_END)
                                    # If the line is partially selected
                                    else:
                                            part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, False)
                                            # part_line = line[self.column:self.column+self.width]
                                            if len(part_line) > 0:
                                                    # If the end of selection is outside the visible part
                                                    if sel_column_end - self.column < 0:
                                                            sel_column_end = 0
                                                    else:
                                                            sel_column_end -= self.column

                                                    # If the start of selection is outside the visible part
                                                    if sel_column_start - self.column < 0:
                                                            sel_column_start = 0
                                                    else:
                                                            sel_column_start -= self.column

                                                    # If the selection is on alone line
                                                    if current_line == sel_line_end and current_line == sel_line_start:
                                                            self.write(clear_line)
                                                            self.colorize(part_line[:sel_column_start].encode("utf8"))
                                                            self.write(SELECTION_START+part_line[sel_column_start:sel_column_end].encode("utf8")+SELECTION_END)
                                                            self.colorize(part_line[sel_column_end:].encode("utf8"))
                                                    # If current line is on the last selection line
                                                    elif current_line == sel_line_end:
                                                            self.write(clear_line+SELECTION_START+part_line[:sel_column_end].encode("utf8")+SELECTION_END)
                                                            self.colorize(part_line[sel_column_end:].encode("utf8"))
                                                    # If current line is on the first selection line
                                                    elif current_line == sel_line_start:
                                                            self.write(clear_line)
                                                            self.colorize(part_line[:sel_column_start].encode("utf8"))
                                                            self.write(SELECTION_START+part_line[sel_column_start:].encode("utf8")+SELECTION_END)
                                                    # Else the line is not selected
                                                    else:
                                                            self.write(clear_line)
                                                            self.colorize(part_line.encode("utf8"))
                                            else:
                                                    if current_line >= sel_line_start and current_line <= sel_line_end:
                                                            self.write(clear_line+SELECTION_START+b" "+SELECTION_END)
                                                    else:
                                                            self.write(clear_line)
                            else:
                                    part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, True)
                                    self.write(clear_line)
                                    self.colorize(part_line)
                    elif current_line == count_line:
                            self.write(clear_line)

            def colorize_none(self, text):
                    """ No colorization """
                    self.write(text)

            def colorize_syntax(self, text):
                    """ Syntax colorization """
                    self.colorizer.colorize(text, self)

            def refresh_line(self, selection_start, selection_end):
                    """ Refresh line """
                    screen_line,     screen_column = self.get_screen_position()
                    refreshed = False

                    # If the line must be refreshed before the cursor line
                    if self.is_refresh_line_before:
                            self.is_refresh_line_before = False
                            self.show_line(self.text.get_cursor_line()-1, screen_line-1, selection_start, selection_end)
                            refreshed = True
                    # If the line must be refreshed after the cursor line
                    if self.is_refresh_line_after:
                            self.is_refresh_line_after = False
                            self.show_line(self.text.get_cursor_line()+1, screen_line+1, selection_start, selection_end)
                            offset = self.height - screen_line
                            self.show_line(self.text.get_cursor_line()+offset+1, screen_line+offset+1, selection_start, selection_end)
                            refreshed = True
                    # If only the cursor line must be refresh
                    if self.is_refresh_line:
                            self.is_refresh_line = False
                            self.show_line(self.text.get_cursor_line(), screen_line, selection_start, selection_end)
                            refreshed = True

                    # If no refresh detected and a selection started
                    if selection_start is not None and refreshed is False:
                            # Refresh the selection
                            self.show_line(self.text.get_cursor_line(), screen_line, selection_start, selection_end)

            def refresh(self):
                    """ Refresh view """
                    selection_start, selection_end = self.text.get_selection()
                    if self.refresh_part is not None:
                            self.refresh_content(selection_start, selection_end, self.refresh_part)
                            self.refresh_part = None
                    # Refresh all required
                    if self.is_refresh_all:
                            self.refresh_content(selection_start, selection_end, True)
                            self.is_refresh_all  = False
                            self.is_refresh_line = False
                    else:
                            # If no selection activated
                            if selection_start is None:
                                    # Refresh the current line
                                    self.refresh_line(selection_start, selection_end)
                            else:
                                    # Refresh the selection
                                    self.refresh_content(selection_start, selection_end, False)
                    self.move_cursor()
                    self.flush()

            def refresh_content(self, selection_start, selection_end, all_):
                    """ Refresh content """
                    # If selection present
                    if selection_start is not None:
                            # Get the selection
                            dummy, sel_line_start, sel_column_start = selection_start
                            dummy, sel_line_end,   sel_column_end   = selection_end
                            line_start = sel_line_start
                            line_end   = sel_line_end
                            # The aim of this part is to limit the refresh area
                            # If the precedent display show a selection
                            if self.sel_line_end is not None and self.sel_line_start is not None:
                                    # If the start and end of selection is on the sames lines
                                    if self.sel_line_end == sel_line_end and self.sel_line_start == sel_line_start:
                                            line_start = line_end = self.text.get_cursor_line()
                                    else:
                                            # If the end of selection is after the precedent display
                                            if self.sel_line_end > sel_line_end:
                                                    line_end = self.sel_line_end
                                            # If the end of selection is on the same line than the precedent display
                                            elif self.sel_line_end == sel_line_end:
                                                    # If the start of selection is before the precedent display
                                                    if self.sel_line_start < sel_line_start:
                                                            line_end = sel_line_start
                                                    else:
                                                            line_end = self.sel_line_start
                                            # If the start of selection is before the precedent display
                                            if self.sel_line_start < sel_line_start:
                                                    line_start = self.sel_line_start
                                            # If the start of selection is on the same line than the precedent display
                                            elif self.sel_line_start == sel_line_start:
                                                    # If the end of selection is after the precedent display
                                                    if self.sel_line_end > sel_line_end:
                                                            line_start = sel_line_end
                                                    else:
                                                            line_start = self.sel_line_end
                    else:
                            line_start = 0
                            line_end = self.line + self.height
                    current_line = self.line
                    screen_line = self.top
                    if type(all_) == type([]):
                            line_start, line_end = all_
                            all_ = False
                    count_line = self.text.get_count_lines()
                    max_line = self.line + self.height
                    if all_:
                            # Erase the rest of the screen with empty line (used when the text is shorter than the screen)
                            self.move_cursor(screen_line, 0)
                            self.write(b"\x1B[J")
                            # Refresh all lines visible
                            while current_line < count_line and current_line <= max_line:
                                    self.show_line(current_line, screen_line, selection_start, selection_end, True)
                                    screen_line  += 1
                                    current_line += 1
                                    if (current_line < count_line and current_line <= max_line):
                                            self.write(b"\n\r")
                    else:
                            # Refresh all lines visible
                            while current_line < count_line and current_line <= max_line:
                                    # If the line is in selection or all must be refreshed
                                    if line_start <= current_line <= line_end or all_:
                                            self.show_line(current_line, screen_line, selection_start, selection_end)
                                    screen_line  += 1
                                    current_line += 1
                            if line_end > max_line:
                                    self.cls_end_screen()

                    # If selection present
                    if selection_start is not None:
                            # Save current selection
                            _, self.sel_line_start, _ = selection_start
                            _, self.sel_line_end,   _ = selection_end

            def hide_selection(self):
                    """ Hide the selection """
                    selection_start, selection_end = self.text.get_selection()
                    if selection_start is not None:
                            self.set_refresh_selection()
                            self.sel_line_start = None
                            self.sel_line_end   = None

            def set_refresh_selection(self):
                    """ Indicates that the selection must be refreshed """
                    selection_start, selection_end = self.text.get_selection()
                    if selection_start is not None:
                            line_start = selection_start[1]
                            if self.sel_line_start is not None:
                                    if self.sel_line_start < line_start:
                                            line_start = self.sel_line_start
                            line_end = selection_end[1]
                            if self.sel_line_end is not None:
                                    if self.sel_line_end > line_end:
                                            line_end = self.sel_line_end
                            self.refresh_part = [line_start, line_end]

            def move_cursor(self, screen_line=None, screen_column=None):
                    """ Move the cursor in the view """
                    if screen_line is None and screen_column is None:
                            screen_line, screen_column = self.get_screen_position()
                    self.write(b"\x1B[%d;%df"%(screen_line+1, screen_column+1))

            def get_screen_size(self):
                    """ Get the screen size """
                    height, width = tools.terminal.get_screen_size(True)
                    self.screen_height = height
                    self.screen_width = width
                    self.height = height-self.top-1
                    self.width  = width
                    self.move_cursor()

            def cls(self):
                    """ clear the screen """
                    self.write(b"\x1B[2J")
                    self.move_cursor(0,0)

            def cls_end_screen(self):
                    """ clear the end of screen """
                    self.write(b"\x1B[0J")

            def get_position(self):
                    """ Get the position of view """
                    return self.line, self.column

    class Text:
            """ Class which manage the text edition """
            def __init__(self, cfg, read_only=False):
                    """ Constructor """
                    self.lines = [""]
                    self.cursor_line   = 0
                    self.cursor_column = 0
                    self.tab_cursor_column   = 0
                    self.modified     = False
                    self.replace_mode  = False
                    self.read_only     = read_only
                    self.cfg = cfg
                    self.view         = None
                    self.tab_size      = self.cfg.tabsize
                    self.selection_start = None
                    self.selection_end   = None
                    self.selection = []
                    self.filename = None
                    self.MOVE_KEYS = self.cfg.key_up+self.cfg.key_down+self.cfg.key_left+self.cfg.key_right+self.cfg.key_home+self.cfg.key_end+self.cfg.key_page_up+self.cfg.key_page_down+self.cfg.key_top+self.cfg.key_bottom+self.cfg.key_next_word+self.cfg.key_previous_word
                    self.SELECT_KEYS = self.cfg.key_sel_up+self.cfg.key_sel_down+self.cfg.key_sel_right+self.cfg.key_sel_left+self.cfg.key_sel_home+self.cfg.key_sel_end+self.cfg.key_sel_top+self.cfg.key_sel_bottom+self.cfg.key_sel_page_up+self.cfg.key_sel_page_down+self.cfg.key_sel_all+self.cfg.key_sel_next_word+self.cfg.key_sel_prev_word
                    self.NOT_READ_ONLY_KEYS = self.cfg.key_copy+self.cfg.key_cut+self.cfg.key_paste+self.cfg.key_indent+self.cfg.key_unindent+self.cfg.key_change_case+self.cfg.key_comment+self.cfg.key_backspace+self.cfg.key_delete+self.cfg.key_new_line+self.cfg.key_del_line
                    self.begin_line = 1
                    self.begin_column = 1

            def set_view(self, view):
                    """ Define the view attached to the text """
                    self.view = view

            def get_count_lines(self):
                    """ Get the total of lines """
                    return len(self.lines)

            def get_cursor_line(self):
                    """ Get the current line of the cursor """
                    return self.cursor_line

            def get_tab_cursor(self, current_line, current_column=None):
                    """ Get position of cursor with line with tabulation """
                    if current_column is None:
                            cursor_column = self.cursor_column
                    else:
                            cursor_column = current_column
                    line = self.lines[current_line]
                    if "\t" in line:
                            tab_cursor_column   = 0
                            column = 0
                            len_line = len(line)
                            while column < cursor_column:
                                    if line[column] == "\t":
                                            pos = tab_cursor_column%self.tab_size
                                            tab_cursor_column += self.tab_size-pos
                                            column          += 1
                                    else:
                                            tab = line.find("\t",column)
                                            if tab > 0:
                                                    partSize = tab - column
                                            else:
                                                    partSize = len_line - column
                                            if column + partSize > cursor_column:
                                                    partSize = cursor_column - column
                                            tab_cursor_column += partSize
                                            column          += partSize
                            return tab_cursor_column
                    else:
                            return cursor_column

            def get_tab_line(self, current_line, start_column, end_column, binary = True):
                    """ Get the tabuled line """
                    accent = False
                    line = self.lines[current_line]
                    if "\t" in line:
                            tab_line = b""
                            cursor   = 0
                            len_line = len(line)
                            column = 0
                            while column < len_line:
                                    char = line[column]
                                    if char == "\t":
                                            pos = cursor%self.tab_size
                                            cursor += self.tab_size-pos
                                            tab_line          += b" "*(self.tab_size-pos)
                                            column            += 1
                                    else:
                                            tab = line.find("\t",column)
                                            if tab < 0:
                                                    tab = len_line
                                            part = line[column:tab]
                                            cursor += len(part)
                                            bin_part = part.encode("utf8")
                                            tab_line          += bin_part
                                            column            += len(part)
                                            if len(part) != len(bin_part):
                                                    accent = True

                            tab_line = tab_line.replace(b"\n",b"")
                            if binary is False:
                                    tab_line = tab_line.decode("utf8")
                    else:
                            if binary:
                                    tab_line = line.encode("utf8")
                                    if len(line) != len(tab_line):
                                            accent = True
                                    tab_line = tab_line.replace(b"\n",b"")
                            else:
                                    tab_line = line
                                    tab_line = tab_line.replace("\n","")

                    if binary and accent:
                            result = tab_line.decode("utf8")[start_column:end_column].encode("utf8")
                    else:
                            result = tab_line[start_column:end_column]

                    return result

            def get_tab_cursor_column(self):
                    """ Get the column of cursor in tabuled line """
                    line = self.lines[self.cursor_line]
                    column = 0
                    self.tab_cursor_column = 0
                    while column < self.cursor_column:
                            if line[column] == "\t":
                                    pos = self.tab_cursor_column%self.tab_size
                                    self.tab_cursor_column += self.tab_size-pos
                                    column += 1
                            else:
                                    tab = line.find("\t",column)
                                    if tab > 0:
                                            delta = tab - column
                                            if column + delta > self.cursor_column:
                                                    delta = self.cursor_column - column
                                                    self.tab_cursor_column += delta
                                                    column += delta
                                            else:
                                                    self.tab_cursor_column += delta
                                                    column += delta
                                    else:
                                            delta = self.cursor_column - column
                                            self.tab_cursor_column += delta
                                            column += delta

            def set_cursor_column(self):
                    """ When the line change compute the cursor position with tabulation in the line """
                    line = self.lines[self.cursor_line]
                    column = 0
                    tab_cursor_column = 0
                    len_line = len(line)
                    column = 0
                    while column < len_line:
                            char = line[column]
                            # If the previous position found exactly in the current line
                            if tab_cursor_column == self.tab_cursor_column:
                                    self.cursor_column = column
                                    break
                            # If the previous position not found in the current line
                            if tab_cursor_column > self.tab_cursor_column:
                                    # Keep last existing position
                                    self.cursor_column = column
                                    break
                            # If tabulation found
                            if char == "\t":
                                    tab_cursor_column += self.tab_size-(tab_cursor_column%self.tab_size)
                                    column += 1
                            else:
                                    # Optimization to accelerate the cursor position
                                    tab = line.find("\t", column)

                                    # Tabulation found
                                    if tab > 0:
                                            delta = tab - column
                                            # If the tabulation position is after the previous tabulation cursor
                                            if delta + tab_cursor_column > self.tab_cursor_column:
                                                    # Move the cursor to the left
                                                    self.cursor_column = column + (self.tab_cursor_column - tab_cursor_column)
                                                    break
                                            else:
                                                    # Another tabulation found, move it after
                                                    tab_cursor_column += delta
                                                    column += delta
                                    # Tabulation not found
                                    else:
                                            # Move the cursor to the end of line
                                            self.cursor_column = column + (self.tab_cursor_column - tab_cursor_column)
                                            break
                    else:
                            if len(line) >= 1:
                                    self.cursor_column = len(line)-1
                            else:
                                    self.cursor_column = 0

            def getFilename(self):
                    """ Return the filename """
                    return self.filename

            def load(self, filename):
                    """ Load file in the editor """
                    self.filename = None
                    try:
                            self.lines = []
                            self.filename = filename
                            file = open(filename, "r")
                            line = file.readline()
                            while line != "":
                                    self.lines.append(line.replace("\r\n","\n"))
                                    line = file.readline()
                            file.close()
                            if len(self.lines) == 0:
                                    self.lines = [""]
                    except MemoryError:
                            # pylint: disable=raise-missing-from
                            raise MemoryError()
                    except OSError:
                            self.lines = [""]
                            # File not existing
                    except Exception as err:
                            tools.logger.syslog(err)
                            self.lines = [""]

            def save(self, filename=None):
                    """ Save text in the file """
                    result = False
                    if self.read_only is False:
                            if filename is None:
                                    filename = self.filename
                            if filename is not None:
                                    try:
                                            file = open(filename, "w")
                                            for line in self.lines:
                                                    file.write(line)
                                            file.close()
                                            self.modified = False
                                            result = True
                                    except Exception as err:
                                            tools.logger.syslog(err)
                    return result

            def change_line(self, moveLine):
                    """ Move the cursor on another line """
                    # If cursor is before the first line
                    if moveLine + self.cursor_line < 0:
                            # Set the cursor to the first line
                            self.cursor_line = 0
                            self.cursor_column = 0
                            self.change_column(0)
                    # If the cursor is after the last line
                    elif moveLine + self.cursor_line >= len(self.lines):
                            self.cursor_line = len(self.lines) -1
                            self.cursor_column = len(self.lines[self.cursor_line])
                            self.change_column(0)
                    # else the cursor is in the lines of text
                    else:
                            self.cursor_line += moveLine
                            if len(self.lines) - 1 == self.cursor_line:
                                    len_line = len(self.lines[self.cursor_line])
                            else:
                                    len_line = len(self.lines[self.cursor_line])-1

                            self.set_cursor_column()
                            # If the new cursor position is outside the line of text
                            if self.cursor_column > len_line:
                                    self.change_column(tools.terminal.MAXINT)

                    if self.selection_start is not None:
                            self.selection_end = [self.cursor_column, self.cursor_line,self.get_tab_cursor(self.cursor_line)]
                    self.view.move()

            def change_column(self, move_column, with_move_view=True):
                    """ Move the cursor on another column """
                    cursor_line   = self.cursor_line
                    cursor_column = self.cursor_column
                    # If the cursor go to the previous line
                    if move_column + self.cursor_column < 0:
                            # If start of line
                            if abs(move_column) > 1:
                                    self.cursor_column = 0
                            # If move to the left and must go to previous line
                            elif self.cursor_line > 0:
                                    self.cursor_line -= 1
                                    self.cursor_column = len(self.lines[self.cursor_line])-1
                    # If the cursor is at the end of line
                    elif move_column + self.cursor_column > len(self.lines[self.cursor_line])-1:
                            # If the cursor is on the last line of file
                            if abs(move_column) > 1 or self.cursor_line+1 == len(self.lines):
                                    # If the file is empty
                                    if self.lines[self.cursor_line] == "":
                                            self.cursor_column = 0
                                            self.tab_cursor_column = 0
                                    # If the last line of contains return char
                                    elif self.lines[self.cursor_line][-1] == "\n":
                                            # Move cursor before return
                                            self.cursor_column = len(self.lines[self.cursor_line])-1
                                    else:
                                            # Move cursor after the last char
                                            self.cursor_column = len(self.lines[self.cursor_line])

                            # If the cursor is on the end of line and must change of line
                            elif self.cursor_line+1 < len(self.lines):
                                    self.cursor_line += 1
                                    self.cursor_column = 0
                                    self.tab_cursor_column = 0
                    # Normal move of cursor
                    else:
                            # Next or previous column
                            self.cursor_column += move_column
                    if abs(move_column) > 0:
                            self.get_tab_cursor_column()
                    self.close_selection()
                    if with_move_view:
                            self.view.move()
                    if self.cursor_column == cursor_column and self.cursor_line == cursor_line:
                            return False
                    else:
                            return True

            def backspace(self, keys=None):
                    """ Manage the backspace key """
                    self.modified = True
                    if self.remove_selection() is False:
                            # The cursor not in the begining of line
                            if self.cursor_column >= 1:
                                    line = self.lines[self.cursor_line]
                                    line = line[0:self.cursor_column-1:]+ line[self.cursor_column  : :]
                                    self.lines[self.cursor_line] = line
                                    self.change_column(-1)
                                    self.view.set_refresh_line()
                            # The cursor is on the begining of line
                            else:
                                    # If the cursor not on the first line
                                    if self.cursor_line >= 1:
                                            # Copy the current line to the end of previous line
                                            self.cursor_column = len(self.lines[self.cursor_line-1])
                                            self.lines[self.cursor_line-1] = self.lines[self.cursor_line-1][:-1] + self.lines[self.cursor_line]
                                            del self.lines[self.cursor_line]
                                            self.view.scroll_part_up()
                                            self.cursor_line -= 1
                                            self.view.set_refresh_after()
                                            self.change_column(-1)

            def delete(self, keys=None):
                    """ Manage the delete key """
                    self.modified = True
                    if self.remove_selection() is False:
                            line = self.lines[self.cursor_line]
                            if self.cursor_column < len(line):
                                    # If the line is empty
                                    if line[self.cursor_column] == "\n":
                                            # If the cursor not at end of files
                                            if self.cursor_line < len(self.lines)-1:
                                                    # Copy the next line to the current line
                                                    self.lines[self.cursor_line] = line[:self.cursor_column] + self.lines[self.cursor_line+1]
                                                    del self.lines[self.cursor_line+1]
                                                    self.view.scroll_part_up()
                                                    self.view.set_refresh_after()
                                    # Else the char is deleted in the middle of line
                                    else:
                                            line = line[0:self.cursor_column:]+ line[self.cursor_column+1  : :]
                                            self.lines[self.cursor_line] = line
                                            self.change_column(0)
                                            self.view.is_refresh_line = True

            def delete_line(self, keys=None):
                    """ Manage the delete of line key """
                    self.hide_selection()
                    self.modified = True
                    # If file contains one or none line
                    if len(self.lines) <= 1:
                            # Clean the content of file
                            self.lines = [""]
                            self.cursor_column = 0
                            self.cursor_line = 0
                            self.change_column(0)
                    # If the current line is not the last of file
                    elif self.cursor_line < len(self.lines):
                            # Delete the line
                            self.cursor_column = 0
                            del self.lines[self.cursor_line]
                            self.view.scroll_part_up()
                            if self.cursor_line >= len(self.lines):
                                    self.cursor_line = len(self.lines)-1
                            self.change_column(0)
                    self.view.set_refresh_after()

            def new_line(self, keys=None):
                    """ Manage the newline key """
                    self.modified = True
                    self.remove_selection()
                    line1 = self.lines[self.cursor_line][:self.cursor_column]+"\n"
                    line2 = self.lines[self.cursor_line][self.cursor_column:]
                    self.lines[self.cursor_line]=line1
                    self.lines.insert(self.cursor_line+1, line2)
                    self.view.scroll_part_down()
                    self.change_column(1)
                    self.view.set_refresh_before()

            def insert_char(self, char):
                    """ Insert character """
                    self.modified = True
                    self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char + self.lines[self.cursor_line][self.cursor_column:]
                    self.change_column(len(char))
                    self.view.set_refresh_line()

            def replace_char(self, char):
                    """ Replace character """
                    self.modified = True
                    if self.cursor_line == len(self.lines)-1 and self.cursor_column >= len(self.lines[self.cursor_line])-1:
                            self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char
                            self.change_column(1)
                            self.view.set_refresh_line()
                    # If it is the last char in the line
                    elif self.lines[self.cursor_line][self.cursor_column] == "\n":
                            # Append char to the line
                            self.insert_char(char)
                    # Else the char must be replaced in the line
                    else:
                            self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char + self.lines[self.cursor_line][self.cursor_column+1:]
                            self.change_column(1)
                            self.view.set_refresh_line()

            def open_selection(self):
                    """ Start a selection """
                    if self.selection_start is None:
                            self.selection_start = [self.cursor_column, self.cursor_line, self.get_tab_cursor(self.cursor_line, self.cursor_column)]

            def close_selection(self):
                    """ Terminate selection """
                    if self.selection_start is not None:
                            self.selection_end = [self.cursor_column, self.cursor_line,self.get_tab_cursor(self.cursor_line)]

            def select_all(self, keys=None):
                    """ Do a select all """
                    self.selection_start = [0,0,0]
                    lastLine = len(self.lines)-1
                    lastColumn = len(self.lines[lastLine])
                    self.move_cursor(lastLine, lastColumn)
                    self.selection_end  = [lastColumn, lastLine, self.get_tab_cursor(lastLine, lastColumn)]
                    self.view.set_refresh_all()

            def get_selection(self):
                    """ Get information about selection """
                    if self.selection_start is not None and self.selection_end is not None:
                            if self.selection_start[1] > self.selection_end[1]:
                                    return self.selection_end, self.selection_start
                            elif self.selection_start[1] < self.selection_end[1]:
                                    return self.selection_start, self.selection_end
                            elif self.selection_start[0] < self.selection_end[0]:
                                    return self.selection_start, self.selection_end
                            else:
                                    return self.selection_end, self.selection_start
                    else:
                            return None, None

            def arrow_up(self, keys=None):
                    """ Manage arrow up key """
                    self.hide_selection()
                    self.change_line(-1)

            def arrow_down(self, keys=None):
                    """ Manage arrow down key """
                    self.hide_selection()
                    self.change_line(1)

            def arrow_left(self, keys=None):
                    """ Manage arrow left key """
                    self.hide_selection()
                    self.change_column(-len(keys))

            def arrow_right(self, keys=None):
                    """ Manage arrow right key """
                    self.hide_selection()
                    self.change_column(len(keys))

            def select_up(self, keys=None):
                    """ Manage select up key """
                    self.open_selection()
                    self.change_line(-1)

            def select_down(self, keys=None):
                    """ Manage select down key """
                    self.open_selection()
                    self.change_line(1)

            def select_left(self, keys=None):
                    """ Manage select left key """
                    self.open_selection()
                    self.change_column(-len(keys))

            def select_right(self, keys=None):
                    """ Manage select right key """
                    self.open_selection()
                    self.change_column(len(keys))

            def select_home(self, keys=None):
                    """ Manage home key """
                    self.open_selection()
                    self.change_column(-tools.terminal.MAXINT)

            def select_end(self, keys=None):
                    """ Manage end key """
                    self.open_selection()
                    self.change_column(tools.terminal.MAXINT)

            def select_page_up(self, keys=None):
                    """ Manage select page up key """
                    self.open_selection()
                    self.change_line((-(self.view.height)) * len(keys))
                    self.change_column(-tools.terminal.MAXINT)

            def select_page_down(self, keys=None):
                    """ Manage select page down key """
                    self.open_selection()
                    self.change_line((self.view.height) * len(keys))
                    self.change_column(tools.terminal.MAXINT)

            def select_next_word(self, keys=None):
                    """ Manage select next word key """
                    self.open_selection()
                    self.move_word(1)

            def select_previous_word(self, keys=None):
                    """ Manage select previous word key """
                    self.open_selection()
                    self.move_word(-1)

            def select_top(self, keys=None):
                    """ Manage select to the first line of text """
                    self.open_selection()
                    self.change_line(-tools.terminal.MAXINT)

            def select_bottom(self, keys=None):
                    """ Manage select to the last line of text """
                    self.open_selection()
                    self.change_line(tools.terminal.MAXINT)

            def page_up(self, keys=None):
                    """ Manage page up key """
                    self.hide_selection()
                    self.change_line((-(self.view.height)) * len(keys))

            def page_down(self, keys=None):
                    """ Manage page down key """
                    self.hide_selection()
                    self.change_line((self.view.height) * len(keys))

            def home(self, keys=None):
                    """ Manage home key """
                    self.hide_selection()
                    self.change_column(-tools.terminal.MAXINT)

            def end(self, keys=None):
                    """ Manage end key """
                    self.hide_selection()
                    self.change_column(tools.terminal.MAXINT)

            def add_char(self, keys=None):
                    """ Manage other key, add character """
                    result = False

                    if tools.strings.isascii(keys[0]):
                            self.remove_selection()
                            for char in keys:
                                    if tools.strings.isascii(char):
                                            if self.replace_mode:
                                                    self.replace_char(char)
                                            else:
                                                    self.insert_char(char)
                                            result = True
                    # if result is False:
                            # print(tools.strings.dump(keys[0]))
                    return result

            def find_next(self, text):
                    """ Find next researched text """
                    result = False
                    # Get the selection
                    selection_start, selection_end = self.get_selection()

                    # Hide the selection
                    self.hide_selection()

                    # Set the start of search at the cursor position
                    current_line   = self.cursor_line
                    current_column = self.cursor_column

                    # If selection activated
                    if selection_start is not None and selection_end is not None:
                            # If selection is on one line
                            if selection_start[1] == selection_end[1] and current_line == selection_start[1]:
                                    # If selection is exactly the size of text
                                    if selection_start[0] == current_column:
                                            # Move the start of search after the text selected
                                            current_column = selection_end[0]

                    # Find the text in next lines
                    while current_line < len(self.lines):
                            # Search text
                            pos = self.lines[current_line].find(text, current_column)

                            # If text found
                            if pos >= 0:
                                    # Move the cursor to the text found
                                    self.cursor_line = current_line
                                    self.cursor_column = pos + len(text)
                                    self.change_column(0)
                                    self.selection_start = [pos, current_line,self.get_tab_cursor(current_line,pos)]
                                    self.selection_end   = [pos + len(text), current_line, self.get_tab_cursor(current_line, pos + len(text))]
                                    result = True
                                    break
                            else:
                                    # Set the search position at the begin of next line
                                    current_column = 0
                                    current_line += 1
                    self.view.move()
                    return result

            def find_previous(self, text):
                    """ Find previous researched text """
                    result = False
                    # Get the selection
                    selection_start, selection_end = self.get_selection()

                    # Hide the selection
                    self.hide_selection()

                    # Set the start of search at the cursor position
                    current_line   = self.cursor_line
                    current_column = self.cursor_column

                    # If selection activated
                    if selection_start is not None and selection_end is not None:
                            # If selection is on one line
                            if selection_start[1] == selection_end[1] and current_line == selection_start[1]:
                                    # If selection is exactly the size of text
                                    if selection_end[0] - selection_start[0] == len(text):
                                            # Move the start of search before the text selected
                                            current_column = selection_start[0]

                    # While the line before the first line not reached
                    while current_line >= 0:
                            # Get the current line
                            line = self.lines[current_line]

                            # If the current column is negative
                            if current_column < 0:
                                    # Set the end of line
                                    current_column = len(line)

                            # Search the text in reverse
                            pos = line.rfind(text, 0, current_column)

                            # If text found
                            if pos >= 0:
                                    self.cursor_line = current_line
                                    self.cursor_column = pos
                                    self.change_column(0)
                                    self.selection_start = [pos, current_line,self.get_tab_cursor(current_line,pos)]
                                    self.selection_end   = [pos + len(text), current_line, self.get_tab_cursor(current_line, pos + len(text))]
                                    result = True
                                    break
                            else:
                                    # Set the search position at the end of line
                                    current_column = -1
                                    current_line -= 1
                    self.view.move()
                    return result

            def hide_selection(self):
                    """ Hide selection """
                    self.view.hide_selection()
                    self.selection_start = self.selection_end = None

            def goto(self, line, column=None, clear_selection=True):
                    """ Goto specified line """
                    if clear_selection:
                            self.hide_selection()
                    if line < 0:
                            self.cursor_line = len(self.lines)-1
                    elif line < 1:
                            self.cursor_line = 1
                    elif line <= len(self.lines):
                            self.cursor_line = line - 1
                    else:
                            self.cursor_line = len(self.lines)-1
                            if column is not None:
                                    column = tools.terminal.MAXINT
                    self.cursor_column = 0

                    if column is not None:
                            if column > 1:
                                    cur_line = self.cursor_line
                                    self.change_column(0, with_move_view=False)
                                    for i in range(len(self.lines[self.cursor_line]) + 1):
                                            self.change_column(1, with_move_view=False)
                                            if self.tab_cursor_column >= column-1:
                                                    break

                                            if self.cursor_line != cur_line:
                                                    self.change_column(-1, with_move_view=False)
                                                    break

                    self.view.move()

            def copy_clipboard(self):
                    """ Copy selection to clipboard """
                    result = []
                    if self.selection_start is not None:
                            selection_start, selection_end = self.get_selection()
                            sel_column_start, sel_line_start, dummy = selection_start
                            sel_column_end,   sel_line_end,   dummy = selection_end
                            result = []
                            if sel_line_start == sel_line_end:
                                    result.append(self.lines[sel_line_start][sel_column_start:sel_column_end])
                            else:
                                    for line in range(sel_line_start, sel_line_end+1):
                                            if line == sel_line_start:
                                                    part = self.lines[line][sel_column_start:]
                                                    if part != "":
                                                            result.append(self.lines[line][sel_column_start:])
                                            elif line == sel_line_end:
                                                    part = self.lines[line][:sel_column_end]
                                                    if part != "":
                                                            result.append(self.lines[line][:sel_column_end])
                                            else:
                                                    result.append(self.lines[line])
                    return result

            def remove_selection(self):
                    """ Remove selection """
                    if self.selection_start is not None:
                            self.modified = True
                            selection_start, selection_end = self.get_selection()
                            sel_column_start, sel_line_start, _ = selection_start
                            sel_column_end,   sel_line_end,   _ = selection_end
                            start = self.lines[sel_line_start][:sel_column_start]
                            end   = self.lines[sel_line_end  ][sel_column_end:]
                            self.lines[sel_line_start] = start + end
                            if sel_line_start < sel_line_end:
                                    for line in range(sel_line_end, sel_line_start,-1):
                                            del self.lines[line]
                            self.move_cursor(sel_line_start, sel_column_start)
                            self.hide_selection()
                            if sel_line_end == sel_line_start:
                                    self.view.set_refresh_line()
                            else:
                                    self.view.set_refresh_bottom(sel_line_start)
                            return True
                    return False

            def paste_clipboard(self, selection):
                    """ Paste clipboard at the cursor position """
                    if selection != [] and selection != [""]:
                            start_line = self.cursor_line
                            # Split the line with insertion
                            start = self.lines[self.cursor_line][:self.cursor_column]
                            end   = self.lines[self.cursor_line][self.cursor_column:]

                            # Paste the first line
                            self.lines[self.cursor_line] = start + selection[0]

                            self.cursor_line += 1

                            # Insert all lines from clipboard
                            for line in selection[1:-1]:
                                    self.lines.insert(self.cursor_line, line)
                                    self.cursor_line += 1

                            # If the last line of clipboard is not empty
                            if len(selection[-1]) >= 1:
                                    # If the last line of clipboard contains carriage return at the end
                                    if selection[-1][-1] == "\n":
                                            if len(selection) > 1:
                                                    # Add the new line
                                                    self.lines.insert(self.cursor_line, selection[-1])
                                                    self.cursor_line += 1

                                            # Add the part after the insertion
                                            self.lines.insert(self.cursor_line, end)
                                            self.cursor_column = 0
                                            if len(selection) >= self.view.height:
                                                    self.view.set_refresh_all()
                                            else:
                                                    self.view.set_refresh_bottom(start_line)
                                    else:
                                            # If many lines with last line without carriage return at the end
                                            if len(selection) > 1:
                                                    self.lines.insert(self.cursor_line, selection[-1] + end)
                                                    self.cursor_column = len(selection[-1])
                                                    if len(selection) >= self.view.height:
                                                            self.view.set_refresh_all()
                                                    else:
                                                            self.view.set_refresh_bottom(start_line)
                                            else:
                                                    # Only one line without carriage return
                                                    self.cursor_line -= 1
                                                    self.lines[self.cursor_line] += end
                                                    self.cursor_column = len(start) + len(selection[-1])
                                                    self.view.set_refresh_line()
                            self.move_cursor(self.cursor_line, self.cursor_column)

            def move_cursor(self, line, column):
                    """ Move the cursor """
                    self.cursor_line   = line
                    self.cursor_column = column
                    self.change_column(0)
                    self.get_tab_cursor_column()

            def copy(self, keys=None):
                    """ Manage copy key """
                    self.selection = self.copy_clipboard()

            def cut(self, keys=None):
                    """ Manage cut key """
                    self.modified = True
                    self.selection = self.copy_clipboard()
                    self.remove_selection()

            def paste(self, keys=None):
                    """ Manage paste key """
                    self.modified = True
                    self.remove_selection()
                    self.paste_clipboard(self.selection)
                    self.hide_selection()

            def change_case(self, keys=None):
                    """ Change the case of selection """
                    selection = self.copy_clipboard()
                    if len(selection) > 0:
                            self.modified = True
                            selection_start = self.selection_start
                            selection_end   = self.selection_end

                            self.remove_selection()
                            isUpper = None
                            for line in selection:
                                    for char in line:
                                            if tools.strings.isupper(char):
                                                    isUpper = True
                                                    break
                                            elif tools.strings.islower(char):
                                                    isUpper = False
                                                    break
                                    if isUpper is not None:
                                            break
                            # pylint:disable=consider-using-enumerate
                            for line in range(len(selection)):
                                    if isUpper:
                                            selection[line] = selection[line].lower()
                                    else:
                                            selection[line] = selection[line].upper()
                            self.paste_clipboard(selection)
                            self.view.set_refresh_selection()
                            self.selection_start = selection_start
                            self.selection_end   = selection_end

            def comment(self, keys=None):
                    """ Comment the selection """
                    self.modified = True

                    # If selection
                    if self.selection_start is not None:
                            selection_start, selection_end = self.get_selection()
                            _, sel_line_start, _ = selection_start
                            _, sel_line_end,   _ = selection_end

                            # Add tabulation
                            for line in range(sel_line_start, sel_line_end+1):
                                    if len(self.lines[line]) >= 1:
                                            if self.lines[line][0] != '#':
                                                    self.lines[line] = "#" + self.lines[line]
                                            else:
                                                    if len(self.lines[line]) >= 1:
                                                            self.lines[line] = self.lines[line][1:]

                            # Move the start selection to the start of first selected line
                            self.selection_start = [0,sel_line_start, 0]

                            # Get the length of last selected line
                            len_line_end =  len(self.lines[sel_line_end])

                            # Move the end of selection at the end of line selected
                            self.selection_end   = [len_line_end-1, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end-1)]
                            self.view.set_refresh_selection()
                    else:
                            if len(self.lines[self.cursor_line]) >= 1:
                                    # If nothing selected
                                    if self.lines[self.cursor_line][0] == "#":
                                            self.lines[self.cursor_line] = self.lines[self.cursor_line][1:]
                                            if self.cursor_column > 0:
                                                    self.change_column(-1)
                                    else:
                                            self.lines[self.cursor_line] = "#" + self.lines[self.cursor_line]
                                            self.change_column(1)
                            self.view.set_refresh_line()

            def indent(self, keys=None):
                    """ Manage tabulation key """
                    # If nothing selected
                    if self.selection_start is None:
                            self.add_char(keys)
                    else:
                            self.modified = True
                            # Indent selection
                            selection_start, selection_end = self.get_selection()
                            sel_column_start, sel_line_start, dummy = selection_start
                            sel_column_end,   sel_line_end,   dummy = selection_end

                            # If a part of line selected
                            if sel_line_start == sel_line_end and not (sel_column_start == 0 and sel_column_end == len(self.lines[sel_line_end])-1):
                                    self.add_char(self.cfg.key_indent)
                            else:
                                    # If the last line selected is at beginning of line
                                    if sel_column_end == 0:
                                            # This line must not be indented
                                            sel_line_end -= 1

                                    # Add tabulation
                                    for line in range(sel_line_start, sel_line_end+1):
                                            self.lines[line] = "\t" + self.lines[line]

                                    # Move the start selection to the start of first selected line
                                    self.selection_start = [0,sel_line_start, 0]

                                    # If the last line selected is not at beginning of line
                                    if sel_column_end > 0:
                                            # Get the length of last selected line
                                            len_line_end =  len(self.lines[sel_line_end])

                                            # If the end of selection is not on the last line
                                            if sel_line_end < len(self.lines)-1:
                                                    len_line_end -= 1

                                            # Move the end of selection at the end of line selected
                                            self.selection_end   = [len_line_end, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end)]
                                    else:
                                            # Move the end of selection at the start of the last line selected
                                            self.selection_end  = [0, sel_line_end+1, 0]
                            self.view.set_refresh_selection()

            def unindent(self, keys=None):
                    """ Manage the unindentation key """
                    # If nothing selected
                    if self.selection_start is None:
                            self.backspace()
                    else:
                            self.modified = True

                            # Unindent selection
                            selection_start, selection_end = self.get_selection()
                            sel_column_start, sel_line_start, dummy = selection_start
                            sel_column_end,   sel_line_end,   dummy = selection_end

                            # If the selection is only alone line
                            if sel_line_start == sel_line_end:
                                    self.hide_selection()
                            else:
                                    # If the last line selected is at beginning of line
                                    if sel_column_end == 0:
                                            # This line must not be indented
                                            sel_line_end -= 1

                                    # Remove indentation
                                    for line in range(sel_line_start, sel_line_end+1):
                                            if len(self.lines[line]) >= 1:
                                                    if self.lines[line][0] == "\t" or self.lines[line][0] == " ":
                                                            self.lines[line] = self.lines[line][1:]

                                    # Move the start selection to the start of first selected line
                                    self.selection_start = [0,sel_line_start, 0]

                                    # If the last line selected is not at beginning of line
                                    if sel_column_end > 0:
                                            # Get the length of last selected line
                                            len_line_end =  len(self.lines[sel_line_end])

                                            # If the end of selection is not on the last line
                                            if sel_line_end < len(self.lines)-1:
                                                    len_line_end -= 1

                                            # Move the end of selection at the end of line selected
                                            self.selection_end   = [len_line_end, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end)]
                                    else:
                                            # Move the end of selection at the start of the last line selected
                                            self.selection_end  = [0, sel_line_end+1, 0]
                            self.view.set_refresh_selection()

            def replace(self, old, new):
                    """ Replace the selection """
                    if self.read_only is False:
                            selection = self.copy_clipboard()
                            if len(selection) == 1:
                                    if selection[0] == old:
                                            self.delete()
                                            self.insert_char(new)
                                            return True
                    return False

            def get_cursor_char(self):
                    """ Get the char on the cursor """
                    try:
                            return self.lines[self.cursor_line][self.cursor_column]
                    except:
                            return None

            def move_word(self, direction):
                    """ Move the cursor to the word """
                    state = 0
                    while self.change_column(direction):
                            current_char = self.get_cursor_char()
                            if current_char is None:
                                    break
                            elif tools.strings.ispunctuation(current_char):
                                    if state == 0:
                                            state = 2
                                    elif state == 1:
                                            break
                            elif tools.strings.isalpha(current_char):
                                    if state == 0:
                                            state = 1
                                    elif state == 2:
                                            break
                            elif tools.strings.isspace(current_char):
                                    if state == 1:
                                            break
                                    if state == 2:
                                            break

            def next_word(self, keys=None):
                    """ Move the cursor to the next word """
                    self.hide_selection()
                    self.move_word(1)
                    self.view.move()

            def previous_word(self, keys=None):
                    """ Move the cursor to the previous word """
                    self.hide_selection()
                    self.move_word(-1)
                    self.view.move()

            def top(self, keys=None):
                    """ Move the cursor to the first line of text """
                    self.goto(1)

            def bottom(self, keys=None):
                    """ Move the cursor to the last line of text """
                    self.goto(tools.terminal.MAXINT)

            def treat_char(self, keys=None):
                    """ Treat character entered """
                    char = ord(keys[0][0])
                    if self.read_only is False:
                            if char >= 0x20 and char != 0x7F:
                                    self.add_char(keys)
                                    return True
                    return False

            def treat_key(self, keys=None):
                    """ Treat keys """
                    key_callback = None
                    if self.treat_char(keys) is False:
                            # Move in the edit field
                            if keys[0] in self.MOVE_KEYS:
                                    if   keys[0] in self.cfg.key_left:            key_callback = self.arrow_left
                                    elif keys[0] in self.cfg.key_right:           key_callback = self.arrow_right
                                    elif keys[0] in self.cfg.key_up  :            key_callback = self.arrow_up
                                    elif keys[0] in self.cfg.key_down:            key_callback = self.arrow_down
                                    elif keys[0] in self.cfg.key_home:            key_callback = self.home
                                    elif keys[0] in self.cfg.key_end:             key_callback = self.end
                                    elif keys[0] in self.cfg.key_page_up:         key_callback = self.page_up
                                    elif keys[0] in self.cfg.key_page_down:       key_callback = self.page_down
                                    elif keys[0] in self.cfg.key_top:             key_callback = self.top
                                    elif keys[0] in self.cfg.key_bottom:          key_callback = self.bottom
                                    elif keys[0] in self.cfg.key_next_word:       key_callback = self.next_word
                                    elif keys[0] in self.cfg.key_previous_word:   key_callback = self.previous_word
                            elif keys[0] in self.SELECT_KEYS:
                                    # Selection the edit field
                                    if   keys[0] in self.cfg.key_sel_right:    key_callback = self.select_right
                                    elif keys[0] in self.cfg.key_sel_left:     key_callback = self.select_left
                                    elif keys[0] in self.cfg.key_sel_up:       key_callback = self.select_up
                                    elif keys[0] in self.cfg.key_sel_down:     key_callback = self.select_down
                                    elif keys[0] in self.cfg.key_sel_home:     key_callback = self.select_home
                                    elif keys[0] in self.cfg.key_sel_end:      key_callback = self.select_end
                                    elif keys[0] in self.cfg.key_sel_top:      key_callback = self.select_top
                                    elif keys[0] in self.cfg.key_sel_bottom:   key_callback = self.select_bottom
                                    elif keys[0] in self.cfg.key_sel_page_up:  key_callback = self.select_page_up
                                    elif keys[0] in self.cfg.key_sel_page_down:key_callback = self.select_page_down
                                    elif keys[0] in self.cfg.key_sel_all:      key_callback = self.select_all
                                    elif keys[0] in self.cfg.key_sel_next_word:key_callback = self.select_next_word
                                    elif keys[0] in self.cfg.key_sel_prev_word:key_callback = self.select_previous_word

                            # If the edit is not in read only
                            elif self.read_only is False:
                                    if keys[0] in self.NOT_READ_ONLY_KEYS:
                                            # Modification in the edit field
                                            if   keys[0] in self.cfg.key_copy:        key_callback = self.copy
                                            elif keys[0] in self.cfg.key_cut:         key_callback = self.cut
                                            elif keys[0] in self.cfg.key_paste:       key_callback = self.paste
                                            elif keys[0] in self.cfg.key_indent:      key_callback = self.indent
                                            elif keys[0] in self.cfg.key_unindent:    key_callback = self.unindent
                                            elif keys[0] in self.cfg.key_change_case: key_callback = self.change_case
                                            elif keys[0] in self.cfg.key_comment:     key_callback = self.comment
                                            elif keys[0] in self.cfg.key_backspace:   key_callback = self.backspace
                                            elif keys[0] in self.cfg.key_delete:      key_callback = self.delete
                                            elif keys[0] in self.cfg.key_new_line:    key_callback = self.new_line
                                            elif keys[0] in self.cfg.key_del_line:    key_callback = self.delete_line
                            # else: self.add_char(keys)
                            if key_callback is not None:
                                    key_callback(keys)
                            else:
                                    if len(keys[0]) > 3:
                                            # If camflasher mouse selection
                                            if keys[0][0:2] == "\x1B[" and keys[0][-1] in ["x","y"]:
                                                    try:
                                                            pos = keys[0][2:-1]
                                                            line, column = pos.split(";")
                                                            self.begin_line, self.begin_column = self.view.get_position()
                                                            if keys[0][-1] == "x":
                                                                    self.goto(int(line)+self.begin_line,int(column)+self.begin_column, True)
                                                                    self.open_selection()
                                                                    self.close_selection()
                                                            else:
                                                                    self.goto(int(line)+self.begin_line,int(column)+self.begin_column, False)
                                                                    self.close_selection()
                                                    except Exception as err:
                                                            pass

    class Edit:
            """ Class which aggregate the View and Text """
            def __init__(self, cfg, view_top=1, view_height=None, read_only=False, extension=None):
                    """ Constructor """
                    self.view = View(cfg, view_height, view_top, extension=extension)
                    self.text = Text(cfg, read_only)
                    self.text.set_view(self.view)
                    self.view.set_text(self.text)

            def get_selected(self):
                    """ Return the selected text """
                    return self.text.copy_clipboard()

    class Editor:
            """ Class which manage a complete editor """
            def __init__(self, filename, no_color=False, read_only=False):
                    """ Constructor """
                    self.cfg = EditorConfig()
                    if self.cfg.load(tobytes=False, errorlog=False) is False:
                            self.cfg.save()

                    self.displayed_filename = tools.filesystem.split(filename)[1]
                    if no_color:
                            extension = ""
                    else:
                            extension = tools.filesystem.splitext(filename)[1]
                    self.edit = Edit(self.cfg, read_only=read_only, extension=extension)
                    self.edit.text.load(filename)
                    self.is_refresh_header = True
                    self.find_text = None
                    self.replace_text = None
                    self.keys= []
                    self.loop = None
                    self.key_callback = None
                    self.precedent_callback = None
                    self.trace = None
                    if tools.filesystem.ismicropython() is False:
                            if filename == "newfile.py":
                                    self.trace = open(self.cfg.config_root() + "/key.txt","w")

                    if (not tools.filesystem.exists(filename) and read_only is True) or tools.filesystem.isdir(filename):
                            print("Cannot open '%s'"%self.displayed_filename)
                    else:
                            try:
                                    self.run()
                            except Exception as err:
                                    self.edit.view.cls()
                                    tools.logger.syslog(err)
                                    print("Failed edit '%s'"%self.displayed_filename)

            def refresh_header(self):
                    """ Refresh the header of editor """
                    if self.is_refresh_header:
                            self.edit.view.move_cursor(0, 0)
                            filename = "\u25C1 File: %s"%(self.displayed_filename)
                            if self.edit.text.read_only is False:
                                    filename += " (*)" if self.edit.text.modified else ""
                                    end = " Mode: %s "%("Replace" if self.edit.text.replace_mode else "Insert")
                            else:
                                    end = " Read only " if self.edit.text.read_only else ""
                            end = "L%d C%d "%(self.edit.text.cursor_line+1, self.edit.view.tab_cursor_column+1) + end + "\u25B7"

                            header = "\x1B[7m%s%s%s\x1B[m"%(filename, " "*(self.edit.view.width - len(filename) - len(end)), end)
                            self.edit.view.write(header)
                            self.edit.view.move_cursor()
                            self.is_refresh_header = False

            def refresh(self):
                    """ Refresh the editor """
                    self.refresh_header()
                    self.edit.view.refresh()

            def toggle_mode(self, keys=None):
                    """ Change the replace mode """
                    if self.edit.text.replace_mode:
                            self.edit.text.replace_mode = False
                    else:
                            self.edit.text.replace_mode = True
                    self.is_refresh_header = True

            def save(self, keys=None, filename=None):
                    """ Save the file edited """
                    self.edit.text.save(filename)
                    self.is_refresh_header = True

            def exit(self, keys=None):
                    """ Exit from editor """
                    self.edit.view.cls()
                    if self.edit.text.modified:
                            self.edit.view.write("\nSave file '%s' (\x1b[7mY\x1b[m:Yes, \x1b[7mN\x1b[m:No, \x1b[7mEsc\x1b[m:Cancel) : "%self.displayed_filename)
                            self.edit.view.flush()
                            while 1:
                                    key = tools.terminal.getch()
                                    if key == "Y" or key == "y":
                                            if self.edit.text.save():
                                                    self.edit.view.write("Saved\n")
                                                    self.edit.view.flush()
                                            else:
                                                    self.edit.view.write("Failed to save\n")
                                                    self.edit.view.flush()
                                            self.loop = False
                                            break
                                    elif key == "N" or key == "n":
                                            self.edit.view.write("Not saved\n")
                                            self.edit.view.flush()
                                            self.loop = False
                                            break
                                    elif key == "\x1B":
                                            self.edit.view.set_refresh_all()
                                            self.is_refresh_header = True
                                            break
                    else:
                            self.loop = False

            def input(self, text, help_=None, selected=None):
                    """ Input value, used to get a line number, or text searched """
                    edit_ = Edit(cfg=self.cfg, view_top=4, view_height=1, read_only=False)
                    edit_.view.cls()
                    edit_.view.move_cursor(3,0)
                    edit_.view.write(text)
                    edit_.view.move_cursor(1,0)
                    if help_ is not None:
                            for item in help_:
                                    key, text = item
                                    edit_.view.write("\x1B[7m%s\x1B[m:%s \t"%(key, text))
                    result = None

                    if selected is not None and len(selected) > 0:
                            for key in selected[0]:
                                    edit_.text.treat_key(key)
                    while 1:
                            edit_.view.refresh()
                            key = self.get_key()
                            if key[0] in self.cfg.key_new_line:
                                    result = edit_.text.lines[0]
                                    break
                            elif key[0] in "\x1B":
                                    break
                            else:
                                    edit_.text.treat_key(key)
                    return result

            def find(self, keys=None):
                    """ Find a text """
                    self.find_text = self.input("Find :",[("Esc","Abort"),("Shift-F3 or Ctrl-P","Previous"),("F3 or Ctrl-N","Next")], selected=self.edit.get_selected())
                    self.find_next()
                    self.edit.view.set_refresh_all()
                    self.is_refresh_header = True
                    self.replace_text = None

            def replace(self, keys=None):
                    """ Replace a text """
                    self.find_text    = self.input("Find to replace :",[("Esc","Abort")], selected=self.edit.get_selected())
                    if self.find_text:
                            self.replace_text = self.input("Replace with :",[("Esc","Abort"),("Shift-F3 or Ctrl-P","Previous"),("F3 or Ctrl-N","Next"),("Enter","Replace current")])
                            self.find_next()

                    self.edit.view.set_refresh_all()
                    self.is_refresh_header = True

            def replace_current(self, keys=None):
                    """ Replace current """
                    if self.find_text and self.replace_text:
                            if self.edit.text.replace(self.find_text, self.replace_text):
                                    self.find_next()

            def replace_all(self, keys=None):
                    """ Replace replace all """
                    if self.find_text and self.replace_text:
                            self.edit.text.top()
                            while self.find_next():
                                    self.edit.text.replace(self.find_text, self.replace_text)
                            self.edit.view.set_refresh_all()

            def find_next(self, keys=None):
                    """ Find next text """
                    result = False
                    if self.find_text:
                            result = self.edit.text.find_next(self.find_text)
                    return result

            def find_previous(self, keys=None):
                    """ Find previous text """
                    result = False
                    if self.find_text:
                            result = self.edit.text.find_previous(self.find_text)
                    return result

            def goto(self, keys=None):
                    """ Goto line """
                    lineNumber = self.input("Goto line :",[("Esc","Abort")])
                    try:
                            lineNumber = int(lineNumber)
                            self.edit.text.goto(int(lineNumber))
                    except:
                            pass
                    self.edit.view.set_refresh_all()
                    self.is_refresh_header = True

            def group_key(self):
                    """ Group similar key to optimize move of cursor and edition """
                    result = [self.keys.pop(0)]
                    while len(self.keys) > 0 and len(result) <= 10:
                            if self.keys[0] == result[0]:
                                    result.append(self.keys.pop(0))
                            else:
                                    if tools.strings.isascii(result[0]) and tools.strings.isascii(self.keys[0]):
                                            result.append(self.keys.pop(0))
                                    else:
                                            break
                    return result

            def get_key(self, duration=tools.terminal.MAXINT):
                    """ Get a key pressed """
                    if len(self.keys) == 0:
                            while True:
                                    try:
                                            key = tools.terminal.getch(duration=duration)
                                    except KeyboardInterrupt:
                                            key = "\x03"

                                    self.keys.append(key)
                                    if tools.terminal.kbhit() is False or len(self.keys) > 5:
                                            break
                    return self.group_key()

            def execute(self, keys=None):
                    """ Execute the python script edited """
                    self.save()
                    loop = True
                    while loop:
                            self.edit.view.reset_scroll_region()
                            self.edit.view.cls()
                            self.edit.view.flush()
                            startTime = tools.strings.ticks()
                            try:
                                    error_line = tools.useful.run(self.displayed_filename)
                            except KeyboardInterrupt:
                                    error_line = None

                            if error_line is not None:
                                    self.edit.text.goto(error_line)

                            endTime = tools.strings.ticks()
                            print( "\x1B[7mTime: %d.%03d s Press enter to stop\x1B[m"%((endTime-startTime)/1000, (endTime-startTime)%1000))
                            while 1:
                                    keys = self.get_key()
                                    if keys[0] in self.cfg.key_new_line:
                                            loop = False
                                            break
                                    elif keys[0] in self.cfg.key_execute:
                                            break
                                    # else:
                                            # print(tools.strings.dump(keys[0]))
                    self.edit.view.cls()
                    self.edit.view.set_refresh_all()
                    self.is_refresh_header = True

            def run(self):
                    """ Core of the editor """
                    try:
                            self.edit.view.cls()
                            self.edit.view.get_screen_size()
                            self.loop = True
                            self.EDIT_KEYS = self.cfg.key_toggle_mode+self.cfg.key_find+self.cfg.key_replace+self.cfg.key_find_previous+self.cfg.key_find_next+self.cfg.key_exit+self.cfg.key_goto+self.cfg.key_save+self.cfg.key_execute
                            while(self.loop):
                                    try:
                                            self.refresh()
                                            keys = self.get_key(duration=0.4)
                                            if len(keys[0]) == 0:
                                                    self.is_refresh_header = True
                                                    self.refresh_header()
                                                    keys = self.get_key()

                                            if keys == ["\x1B[23~"]:
                                                    keys = ["\x1B[15;5x"]
                                            if keys == ["\x1B[24~"]:
                                                    keys = ["\x1B[20;5y"]

                                            if self.trace is not None:
                                                    for key in keys:
                                                            self.trace.write(tools.strings.dump(key, withColor=False) + "\n")
                                                            self.trace.flush()
                                            modified = self.edit.text.modified
                                            self.precedent_callback = self.key_callback
                                            self.key_callback = None

                                            if ord(keys[0][0]) < 0x20:
                                                    if keys[0] in self.EDIT_KEYS:
                                                            if   keys[0] in self.cfg.key_toggle_mode:    self.key_callback = self.toggle_mode
                                                            elif keys[0] in self.cfg.key_find:           self.key_callback = self.find
                                                            elif keys[0] in self.cfg.key_replace:        self.key_callback = self.replace
                                                            elif keys[0] in self.cfg.key_find_previous:  self.key_callback = self.find_previous
                                                            elif keys[0] in self.cfg.key_find_next:      self.key_callback = self.find_next
                                                            elif keys[0] in self.cfg.key_exit:           self.key_callback = self.exit
                                                            elif keys[0] in self.cfg.key_goto:           self.key_callback = self.goto
                                                            elif keys[0] in self.cfg.key_save:           self.key_callback = self.save
                                                            elif keys[0] in self.cfg.key_execute:        self.key_callback = self.execute

                                            # If a replacement is in progress and new line pressed
                                            if keys[0] in self.cfg.key_new_line:
                                                    if self.precedent_callback is not None:
                                                            # The next check is compatible with micropython
                                                            if self.precedent_callback.__name__ in [self.find_next.__name__, self.find_previous.__name__, self.replace.__name__, self.replace_current.__name__]:
                                                                    if self.replace_text is not None:
                                                                            # Replace current found
                                                                            self.key_callback = self.replace_current
                                            # If a replacement is in progress and select all pressed
                                            if keys[0] in self.cfg.key_sel_all:
                                                    if self.precedent_callback is not None:
                                                            # The next check is compatible with micropython
                                                            if self.precedent_callback.__name__ in [self.find_next.__name__, self.find_previous.__name__, self.replace.__name__, self.replace_current.__name__]:
                                                                    if self.replace_text is not None:
                                                                            # Replace all
                                                                            self.key_callback = self.replace_all

                                            if self.key_callback is not None:
                                                    self.key_callback(keys)
                                            else:
                                                    self.edit.text.treat_key(keys)
                                            if modified != self.edit.text.modified:
                                                    self.is_refresh_header = True
                                    except KeyboardInterrupt:
                                            pass
                            self.edit.view.reset_scroll_region()
                            self.edit.view.reset()
                    except Exception as err:
                            print(tools.logger.exception(err))
                            filename = self.edit.text.getFilename() + "_backup"
                            self.save(filename=filename)
                            print("After the crash, a copy of the file was saved in '%s'"%filename)

    def main():
            """ Main function """
            if len(sys.argv) > 1:
                    filename = sys.argv[1]
            else:
                    filename = "newfile.py"
            edit = Editor(filename, read_only=False)

    if __name__ == "__main__":
            main()

## Functions

`def is_enough_memory()`

  Indicate if it has enough memory

  Expand source code

      def is_enough_memory():
              """ Indicate if it has enough memory """
              import gc
              try:
                      # pylint: disable=no-member
                      memory  = gc.mem_free()
              except:
                      memory  = 256*1024
              if memory < 50*1024:
                      return False
              return True

`def main()`

  Main function

  Expand source code

      def main():
              """ Main function """
              if len(sys.argv) > 1:
                      filename = sys.argv[1]
              else:
                      filename = "newfile.py"
              edit = Editor(filename, read_only=False)

## Classes

`class Edit (cfg, view_top=1, view_height=None, read_only=False, extension=None)`

  Class which aggregate the View and Text

  Constructor

  Expand source code

      class Edit:
              """ Class which aggregate the View and Text """
              def __init__(self, cfg, view_top=1, view_height=None, read_only=False, extension=None):
                      """ Constructor """
                      self.view = View(cfg, view_height, view_top, extension=extension)
                      self.text = Text(cfg, read_only)
                      self.text.set_view(self.view)
                      self.view.set_text(self.text)

              def get_selected(self):
                      """ Return the selected text """
                      return self.text.copy_clipboard()

  ### Methods

  `def get_selected(self)`

    Return the selected text

    Expand source code

        def get_selected(self):
                """ Return the selected text """
                return self.text.copy_clipboard()

`class Editor (filename, no_color=False, read_only=False)`

  Class which manage a complete editor

  Constructor

  Expand source code

      class Editor:
              """ Class which manage a complete editor """
              def __init__(self, filename, no_color=False, read_only=False):
                      """ Constructor """
                      self.cfg = EditorConfig()
                      if self.cfg.load(tobytes=False, errorlog=False) is False:
                              self.cfg.save()

                      self.displayed_filename = tools.filesystem.split(filename)[1]
                      if no_color:
                              extension = ""
                      else:
                              extension = tools.filesystem.splitext(filename)[1]
                      self.edit = Edit(self.cfg, read_only=read_only, extension=extension)
                      self.edit.text.load(filename)
                      self.is_refresh_header = True
                      self.find_text = None
                      self.replace_text = None
                      self.keys= []
                      self.loop = None
                      self.key_callback = None
                      self.precedent_callback = None
                      self.trace = None
                      if tools.filesystem.ismicropython() is False:
                              if filename == "newfile.py":
                                      self.trace = open(self.cfg.config_root() + "/key.txt","w")

                      if (not tools.filesystem.exists(filename) and read_only is True) or tools.filesystem.isdir(filename):
                              print("Cannot open '%s'"%self.displayed_filename)
                      else:
                              try:
                                      self.run()
                              except Exception as err:
                                      self.edit.view.cls()
                                      tools.logger.syslog(err)
                                      print("Failed edit '%s'"%self.displayed_filename)

              def refresh_header(self):
                      """ Refresh the header of editor """
                      if self.is_refresh_header:
                              self.edit.view.move_cursor(0, 0)
                              filename = "\u25C1 File: %s"%(self.displayed_filename)
                              if self.edit.text.read_only is False:
                                      filename += " (*)" if self.edit.text.modified else ""
                                      end = " Mode: %s "%("Replace" if self.edit.text.replace_mode else "Insert")
                              else:
                                      end = " Read only " if self.edit.text.read_only else ""
                              end = "L%d C%d "%(self.edit.text.cursor_line+1, self.edit.view.tab_cursor_column+1) + end + "\u25B7"

                              header = "\x1B[7m%s%s%s\x1B[m"%(filename, " "*(self.edit.view.width - len(filename) - len(end)), end)
                              self.edit.view.write(header)
                              self.edit.view.move_cursor()
                              self.is_refresh_header = False

              def refresh(self):
                      """ Refresh the editor """
                      self.refresh_header()
                      self.edit.view.refresh()

              def toggle_mode(self, keys=None):
                      """ Change the replace mode """
                      if self.edit.text.replace_mode:
                              self.edit.text.replace_mode = False
                      else:
                              self.edit.text.replace_mode = True
                      self.is_refresh_header = True

              def save(self, keys=None, filename=None):
                      """ Save the file edited """
                      self.edit.text.save(filename)
                      self.is_refresh_header = True

              def exit(self, keys=None):
                      """ Exit from editor """
                      self.edit.view.cls()
                      if self.edit.text.modified:
                              self.edit.view.write("\nSave file '%s' (\x1b[7mY\x1b[m:Yes, \x1b[7mN\x1b[m:No, \x1b[7mEsc\x1b[m:Cancel) : "%self.displayed_filename)
                              self.edit.view.flush()
                              while 1:
                                      key = tools.terminal.getch()
                                      if key == "Y" or key == "y":
                                              if self.edit.text.save():
                                                      self.edit.view.write("Saved\n")
                                                      self.edit.view.flush()
                                              else:
                                                      self.edit.view.write("Failed to save\n")
                                                      self.edit.view.flush()
                                              self.loop = False
                                              break
                                      elif key == "N" or key == "n":
                                              self.edit.view.write("Not saved\n")
                                              self.edit.view.flush()
                                              self.loop = False
                                              break
                                      elif key == "\x1B":
                                              self.edit.view.set_refresh_all()
                                              self.is_refresh_header = True
                                              break
                      else:
                              self.loop = False

              def input(self, text, help_=None, selected=None):
                      """ Input value, used to get a line number, or text searched """
                      edit_ = Edit(cfg=self.cfg, view_top=4, view_height=1, read_only=False)
                      edit_.view.cls()
                      edit_.view.move_cursor(3,0)
                      edit_.view.write(text)
                      edit_.view.move_cursor(1,0)
                      if help_ is not None:
                              for item in help_:
                                      key, text = item
                                      edit_.view.write("\x1B[7m%s\x1B[m:%s \t"%(key, text))
                      result = None

                      if selected is not None and len(selected) > 0:
                              for key in selected[0]:
                                      edit_.text.treat_key(key)
                      while 1:
                              edit_.view.refresh()
                              key = self.get_key()
                              if key[0] in self.cfg.key_new_line:
                                      result = edit_.text.lines[0]
                                      break
                              elif key[0] in "\x1B":
                                      break
                              else:
                                      edit_.text.treat_key(key)
                      return result

              def find(self, keys=None):
                      """ Find a text """
                      self.find_text = self.input("Find :",[("Esc","Abort"),("Shift-F3 or Ctrl-P","Previous"),("F3 or Ctrl-N","Next")], selected=self.edit.get_selected())
                      self.find_next()
                      self.edit.view.set_refresh_all()
                      self.is_refresh_header = True
                      self.replace_text = None

              def replace(self, keys=None):
                      """ Replace a text """
                      self.find_text    = self.input("Find to replace :",[("Esc","Abort")], selected=self.edit.get_selected())
                      if self.find_text:
                              self.replace_text = self.input("Replace with :",[("Esc","Abort"),("Shift-F3 or Ctrl-P","Previous"),("F3 or Ctrl-N","Next"),("Enter","Replace current")])
                              self.find_next()

                      self.edit.view.set_refresh_all()
                      self.is_refresh_header = True

              def replace_current(self, keys=None):
                      """ Replace current """
                      if self.find_text and self.replace_text:
                              if self.edit.text.replace(self.find_text, self.replace_text):
                                      self.find_next()

              def replace_all(self, keys=None):
                      """ Replace replace all """
                      if self.find_text and self.replace_text:
                              self.edit.text.top()
                              while self.find_next():
                                      self.edit.text.replace(self.find_text, self.replace_text)
                              self.edit.view.set_refresh_all()

              def find_next(self, keys=None):
                      """ Find next text """
                      result = False
                      if self.find_text:
                              result = self.edit.text.find_next(self.find_text)
                      return result

              def find_previous(self, keys=None):
                      """ Find previous text """
                      result = False
                      if self.find_text:
                              result = self.edit.text.find_previous(self.find_text)
                      return result

              def goto(self, keys=None):
                      """ Goto line """
                      lineNumber = self.input("Goto line :",[("Esc","Abort")])
                      try:
                              lineNumber = int(lineNumber)
                              self.edit.text.goto(int(lineNumber))
                      except:
                              pass
                      self.edit.view.set_refresh_all()
                      self.is_refresh_header = True

              def group_key(self):
                      """ Group similar key to optimize move of cursor and edition """
                      result = [self.keys.pop(0)]
                      while len(self.keys) > 0 and len(result) <= 10:
                              if self.keys[0] == result[0]:
                                      result.append(self.keys.pop(0))
                              else:
                                      if tools.strings.isascii(result[0]) and tools.strings.isascii(self.keys[0]):
                                              result.append(self.keys.pop(0))
                                      else:
                                              break
                      return result

              def get_key(self, duration=tools.terminal.MAXINT):
                      """ Get a key pressed """
                      if len(self.keys) == 0:
                              while True:
                                      try:
                                              key = tools.terminal.getch(duration=duration)
                                      except KeyboardInterrupt:
                                              key = "\x03"

                                      self.keys.append(key)
                                      if tools.terminal.kbhit() is False or len(self.keys) > 5:
                                              break
                      return self.group_key()

              def execute(self, keys=None):
                      """ Execute the python script edited """
                      self.save()
                      loop = True
                      while loop:
                              self.edit.view.reset_scroll_region()
                              self.edit.view.cls()
                              self.edit.view.flush()
                              startTime = tools.strings.ticks()
                              try:
                                      error_line = tools.useful.run(self.displayed_filename)
                              except KeyboardInterrupt:
                                      error_line = None

                              if error_line is not None:
                                      self.edit.text.goto(error_line)

                              endTime = tools.strings.ticks()
                              print( "\x1B[7mTime: %d.%03d s Press enter to stop\x1B[m"%((endTime-startTime)/1000, (endTime-startTime)%1000))
                              while 1:
                                      keys = self.get_key()
                                      if keys[0] in self.cfg.key_new_line:
                                              loop = False
                                              break
                                      elif keys[0] in self.cfg.key_execute:
                                              break
                                      # else:
                                              # print(tools.strings.dump(keys[0]))
                      self.edit.view.cls()
                      self.edit.view.set_refresh_all()
                      self.is_refresh_header = True

              def run(self):
                      """ Core of the editor """
                      try:
                              self.edit.view.cls()
                              self.edit.view.get_screen_size()
                              self.loop = True
                              self.EDIT_KEYS = self.cfg.key_toggle_mode+self.cfg.key_find+self.cfg.key_replace+self.cfg.key_find_previous+self.cfg.key_find_next+self.cfg.key_exit+self.cfg.key_goto+self.cfg.key_save+self.cfg.key_execute
                              while(self.loop):
                                      try:
                                              self.refresh()
                                              keys = self.get_key(duration=0.4)
                                              if len(keys[0]) == 0:
                                                      self.is_refresh_header = True
                                                      self.refresh_header()
                                                      keys = self.get_key()

                                              if keys == ["\x1B[23~"]:
                                                      keys = ["\x1B[15;5x"]
                                              if keys == ["\x1B[24~"]:
                                                      keys = ["\x1B[20;5y"]

                                              if self.trace is not None:
                                                      for key in keys:
                                                              self.trace.write(tools.strings.dump(key, withColor=False) + "\n")
                                                              self.trace.flush()
                                              modified = self.edit.text.modified
                                              self.precedent_callback = self.key_callback
                                              self.key_callback = None

                                              if ord(keys[0][0]) < 0x20:
                                                      if keys[0] in self.EDIT_KEYS:
                                                              if   keys[0] in self.cfg.key_toggle_mode:    self.key_callback = self.toggle_mode
                                                              elif keys[0] in self.cfg.key_find:           self.key_callback = self.find
                                                              elif keys[0] in self.cfg.key_replace:        self.key_callback = self.replace
                                                              elif keys[0] in self.cfg.key_find_previous:  self.key_callback = self.find_previous
                                                              elif keys[0] in self.cfg.key_find_next:      self.key_callback = self.find_next
                                                              elif keys[0] in self.cfg.key_exit:           self.key_callback = self.exit
                                                              elif keys[0] in self.cfg.key_goto:           self.key_callback = self.goto
                                                              elif keys[0] in self.cfg.key_save:           self.key_callback = self.save
                                                              elif keys[0] in self.cfg.key_execute:        self.key_callback = self.execute

                                              # If a replacement is in progress and new line pressed
                                              if keys[0] in self.cfg.key_new_line:
                                                      if self.precedent_callback is not None:
                                                              # The next check is compatible with micropython
                                                              if self.precedent_callback.__name__ in [self.find_next.__name__, self.find_previous.__name__, self.replace.__name__, self.replace_current.__name__]:
                                                                      if self.replace_text is not None:
                                                                              # Replace current found
                                                                              self.key_callback = self.replace_current
                                              # If a replacement is in progress and select all pressed
                                              if keys[0] in self.cfg.key_sel_all:
                                                      if self.precedent_callback is not None:
                                                              # The next check is compatible with micropython
                                                              if self.precedent_callback.__name__ in [self.find_next.__name__, self.find_previous.__name__, self.replace.__name__, self.replace_current.__name__]:
                                                                      if self.replace_text is not None:
                                                                              # Replace all
                                                                              self.key_callback = self.replace_all

                                              if self.key_callback is not None:
                                                      self.key_callback(keys)
                                              else:
                                                      self.edit.text.treat_key(keys)
                                              if modified != self.edit.text.modified:
                                                      self.is_refresh_header = True
                                      except KeyboardInterrupt:
                                              pass
                              self.edit.view.reset_scroll_region()
                              self.edit.view.reset()
                      except Exception as err:
                              print(tools.logger.exception(err))
                              filename = self.edit.text.getFilename() + "_backup"
                              self.save(filename=filename)
                              print("After the crash, a copy of the file was saved in '%s'"%filename)

  ### Methods

  `def execute(self, keys=None)`

    Execute the python script edited

    Expand source code

        def execute(self, keys=None):
                """ Execute the python script edited """
                self.save()
                loop = True
                while loop:
                        self.edit.view.reset_scroll_region()
                        self.edit.view.cls()
                        self.edit.view.flush()
                        startTime = tools.strings.ticks()
                        try:
                                error_line = tools.useful.run(self.displayed_filename)
                        except KeyboardInterrupt:
                                error_line = None

                        if error_line is not None:
                                self.edit.text.goto(error_line)

                        endTime = tools.strings.ticks()
                        print( "\x1B[7mTime: %d.%03d s Press enter to stop\x1B[m"%((endTime-startTime)/1000, (endTime-startTime)%1000))
                        while 1:
                                keys = self.get_key()
                                if keys[0] in self.cfg.key_new_line:
                                        loop = False
                                        break
                                elif keys[0] in self.cfg.key_execute:
                                        break
                                # else:
                                        # print(tools.strings.dump(keys[0]))
                self.edit.view.cls()
                self.edit.view.set_refresh_all()
                self.is_refresh_header = True

  `def exit(self, keys=None)`

    Exit from editor

    Expand source code

        def exit(self, keys=None):
                """ Exit from editor """
                self.edit.view.cls()
                if self.edit.text.modified:
                        self.edit.view.write("\nSave file '%s' (\x1b[7mY\x1b[m:Yes, \x1b[7mN\x1b[m:No, \x1b[7mEsc\x1b[m:Cancel) : "%self.displayed_filename)
                        self.edit.view.flush()
                        while 1:
                                key = tools.terminal.getch()
                                if key == "Y" or key == "y":
                                        if self.edit.text.save():
                                                self.edit.view.write("Saved\n")
                                                self.edit.view.flush()
                                        else:
                                                self.edit.view.write("Failed to save\n")
                                                self.edit.view.flush()
                                        self.loop = False
                                        break
                                elif key == "N" or key == "n":
                                        self.edit.view.write("Not saved\n")
                                        self.edit.view.flush()
                                        self.loop = False
                                        break
                                elif key == "\x1B":
                                        self.edit.view.set_refresh_all()
                                        self.is_refresh_header = True
                                        break
                else:
                        self.loop = False

  `def find(self, keys=None)`

    Find a text

    Expand source code

        def find(self, keys=None):
                """ Find a text """
                self.find_text = self.input("Find :",[("Esc","Abort"),("Shift-F3 or Ctrl-P","Previous"),("F3 or Ctrl-N","Next")], selected=self.edit.get_selected())
                self.find_next()
                self.edit.view.set_refresh_all()
                self.is_refresh_header = True
                self.replace_text = None

  `def find_next(self, keys=None)`

    Find next text

    Expand source code

        def find_next(self, keys=None):
                """ Find next text """
                result = False
                if self.find_text:
                        result = self.edit.text.find_next(self.find_text)
                return result

  `def find_previous(self, keys=None)`

    Find previous text

    Expand source code

        def find_previous(self, keys=None):
                """ Find previous text """
                result = False
                if self.find_text:
                        result = self.edit.text.find_previous(self.find_text)
                return result

  `def get_key(self, duration=100000000)`

    Get a key pressed

    Expand source code

        def get_key(self, duration=tools.terminal.MAXINT):
                """ Get a key pressed """
                if len(self.keys) == 0:
                        while True:
                                try:
                                        key = tools.terminal.getch(duration=duration)
                                except KeyboardInterrupt:
                                        key = "\x03"

                                self.keys.append(key)
                                if tools.terminal.kbhit() is False or len(self.keys) > 5:
                                        break
                return self.group_key()

  `def goto(self, keys=None)`

    Goto line

    Expand source code

        def goto(self, keys=None):
                """ Goto line """
                lineNumber = self.input("Goto line :",[("Esc","Abort")])
                try:
                        lineNumber = int(lineNumber)
                        self.edit.text.goto(int(lineNumber))
                except:
                        pass
                self.edit.view.set_refresh_all()
                self.is_refresh_header = True

  `def group_key(self)`

    Group similar key to optimize move of cursor and edition

    Expand source code

        def group_key(self):
                """ Group similar key to optimize move of cursor and edition """
                result = [self.keys.pop(0)]
                while len(self.keys) > 0 and len(result) <= 10:
                        if self.keys[0] == result[0]:
                                result.append(self.keys.pop(0))
                        else:
                                if tools.strings.isascii(result[0]) and tools.strings.isascii(self.keys[0]):
                                        result.append(self.keys.pop(0))
                                else:
                                        break
                return result

  `def input(self, text, help_=None, selected=None)`

    Input value, used to get a line number, or text searched

    Expand source code

        def input(self, text, help_=None, selected=None):
                """ Input value, used to get a line number, or text searched """
                edit_ = Edit(cfg=self.cfg, view_top=4, view_height=1, read_only=False)
                edit_.view.cls()
                edit_.view.move_cursor(3,0)
                edit_.view.write(text)
                edit_.view.move_cursor(1,0)
                if help_ is not None:
                        for item in help_:
                                key, text = item
                                edit_.view.write("\x1B[7m%s\x1B[m:%s \t"%(key, text))
                result = None

                if selected is not None and len(selected) > 0:
                        for key in selected[0]:
                                edit_.text.treat_key(key)
                while 1:
                        edit_.view.refresh()
                        key = self.get_key()
                        if key[0] in self.cfg.key_new_line:
                                result = edit_.text.lines[0]
                                break
                        elif key[0] in "\x1B":
                                break
                        else:
                                edit_.text.treat_key(key)
                return result

  `def refresh(self)`

    Refresh the editor

    Expand source code

        def refresh(self):
                """ Refresh the editor """
                self.refresh_header()
                self.edit.view.refresh()

  `def refresh_header(self)`

    Refresh the header of editor

    Expand source code

        def refresh_header(self):
                """ Refresh the header of editor """
                if self.is_refresh_header:
                        self.edit.view.move_cursor(0, 0)
                        filename = "\u25C1 File: %s"%(self.displayed_filename)
                        if self.edit.text.read_only is False:
                                filename += " (*)" if self.edit.text.modified else ""
                                end = " Mode: %s "%("Replace" if self.edit.text.replace_mode else "Insert")
                        else:
                                end = " Read only " if self.edit.text.read_only else ""
                        end = "L%d C%d "%(self.edit.text.cursor_line+1, self.edit.view.tab_cursor_column+1) + end + "\u25B7"

                        header = "\x1B[7m%s%s%s\x1B[m"%(filename, " "*(self.edit.view.width - len(filename) - len(end)), end)
                        self.edit.view.write(header)
                        self.edit.view.move_cursor()
                        self.is_refresh_header = False

  `def replace(self, keys=None)`

    Replace a text

    Expand source code

        def replace(self, keys=None):
                """ Replace a text """
                self.find_text    = self.input("Find to replace :",[("Esc","Abort")], selected=self.edit.get_selected())
                if self.find_text:
                        self.replace_text = self.input("Replace with :",[("Esc","Abort"),("Shift-F3 or Ctrl-P","Previous"),("F3 or Ctrl-N","Next"),("Enter","Replace current")])
                        self.find_next()

                self.edit.view.set_refresh_all()
                self.is_refresh_header = True

  `def replace_all(self, keys=None)`

    Replace replace all

    Expand source code

        def replace_all(self, keys=None):
                """ Replace replace all """
                if self.find_text and self.replace_text:
                        self.edit.text.top()
                        while self.find_next():
                                self.edit.text.replace(self.find_text, self.replace_text)
                        self.edit.view.set_refresh_all()

  `def replace_current(self, keys=None)`

    Replace current

    Expand source code

        def replace_current(self, keys=None):
                """ Replace current """
                if self.find_text and self.replace_text:
                        if self.edit.text.replace(self.find_text, self.replace_text):
                                self.find_next()

  `def run(self)`

    Core of the editor

    Expand source code

        def run(self):
                """ Core of the editor """
                try:
                        self.edit.view.cls()
                        self.edit.view.get_screen_size()
                        self.loop = True
                        self.EDIT_KEYS = self.cfg.key_toggle_mode+self.cfg.key_find+self.cfg.key_replace+self.cfg.key_find_previous+self.cfg.key_find_next+self.cfg.key_exit+self.cfg.key_goto+self.cfg.key_save+self.cfg.key_execute
                        while(self.loop):
                                try:
                                        self.refresh()
                                        keys = self.get_key(duration=0.4)
                                        if len(keys[0]) == 0:
                                                self.is_refresh_header = True
                                                self.refresh_header()
                                                keys = self.get_key()

                                        if keys == ["\x1B[23~"]:
                                                keys = ["\x1B[15;5x"]
                                        if keys == ["\x1B[24~"]:
                                                keys = ["\x1B[20;5y"]

                                        if self.trace is not None:
                                                for key in keys:
                                                        self.trace.write(tools.strings.dump(key, withColor=False) + "\n")
                                                        self.trace.flush()
                                        modified = self.edit.text.modified
                                        self.precedent_callback = self.key_callback
                                        self.key_callback = None

                                        if ord(keys[0][0]) < 0x20:
                                                if keys[0] in self.EDIT_KEYS:
                                                        if   keys[0] in self.cfg.key_toggle_mode:    self.key_callback = self.toggle_mode
                                                        elif keys[0] in self.cfg.key_find:           self.key_callback = self.find
                                                        elif keys[0] in self.cfg.key_replace:        self.key_callback = self.replace
                                                        elif keys[0] in self.cfg.key_find_previous:  self.key_callback = self.find_previous
                                                        elif keys[0] in self.cfg.key_find_next:      self.key_callback = self.find_next
                                                        elif keys[0] in self.cfg.key_exit:           self.key_callback = self.exit
                                                        elif keys[0] in self.cfg.key_goto:           self.key_callback = self.goto
                                                        elif keys[0] in self.cfg.key_save:           self.key_callback = self.save
                                                        elif keys[0] in self.cfg.key_execute:        self.key_callback = self.execute

                                        # If a replacement is in progress and new line pressed
                                        if keys[0] in self.cfg.key_new_line:
                                                if self.precedent_callback is not None:
                                                        # The next check is compatible with micropython
                                                        if self.precedent_callback.__name__ in [self.find_next.__name__, self.find_previous.__name__, self.replace.__name__, self.replace_current.__name__]:
                                                                if self.replace_text is not None:
                                                                        # Replace current found
                                                                        self.key_callback = self.replace_current
                                        # If a replacement is in progress and select all pressed
                                        if keys[0] in self.cfg.key_sel_all:
                                                if self.precedent_callback is not None:
                                                        # The next check is compatible with micropython
                                                        if self.precedent_callback.__name__ in [self.find_next.__name__, self.find_previous.__name__, self.replace.__name__, self.replace_current.__name__]:
                                                                if self.replace_text is not None:
                                                                        # Replace all
                                                                        self.key_callback = self.replace_all

                                        if self.key_callback is not None:
                                                self.key_callback(keys)
                                        else:
                                                self.edit.text.treat_key(keys)
                                        if modified != self.edit.text.modified:
                                                self.is_refresh_header = True
                                except KeyboardInterrupt:
                                        pass
                        self.edit.view.reset_scroll_region()
                        self.edit.view.reset()
                except Exception as err:
                        print(tools.logger.exception(err))
                        filename = self.edit.text.getFilename() + "_backup"
                        self.save(filename=filename)
                        print("After the crash, a copy of the file was saved in '%s'"%filename)

  `def save(self, keys=None, filename=None)`

    Save the file edited

    Expand source code

        def save(self, keys=None, filename=None):
                """ Save the file edited """
                self.edit.text.save(filename)
                self.is_refresh_header = True

  `def toggle_mode(self, keys=None)`

    Change the replace mode

    Expand source code

        def toggle_mode(self, keys=None):
                """ Change the replace mode """
                if self.edit.text.replace_mode:
                        self.edit.text.replace_mode = False
                else:
                        self.edit.text.replace_mode = True
                self.is_refresh_header = True

`class EditorConfig`

  Editor configuration for shortcuts and tab size

  Constructor

  Expand source code

      class EditorConfig(tools.jsonconfig.JsonConfig):
              """ Editor configuration for shortcuts and tab size"""
              def __init__(self):
                      tools.jsonconfig.JsonConfig.__init__(self)
                      self.tabsize              = 4    # Tabulation size

                      # For the same action several shortcuts can be used
                      # Move shortcuts
                      self.key_up               = ["\x1b[A"]
                      self.key_down             = ["\x1b[B"]
                      self.key_right            = ["\x1b[C"]
                      self.key_left             = ["\x1b[D"]
                      self.key_home             = ["\x1b[1;3D", "\x1b[H", "\x1b\x1b[D", "\x1b[1~", "\x1bb"]
                      self.key_end              = ["\x1b[1;3C", "\x1b[F", "\x1b\x1b[C", "\x1b[4~", "\x1bf"]
                      self.key_page_up          = ["\x1b[1;3A", "\x1b[A", "\x1b\x1b[A", "\x1b[5~"]
                      self.key_page_down        = ["\x1b[1;3B", "\x1b[B", "\x1b\x1b[B", "\x1b[6~"]
                      self.key_top              = ["\x1b[1;5H"]
                      self.key_bottom           = ["\x1b[1;5F"]
                      self.key_next_word        = ["\x1b[1;5C"]
                      self.key_previous_word    = ["\x1b[1;5D"]

                      # selection shortcuts
                      self.key_sel_up           = ["\x1b[1;2A"]
                      self.key_sel_down         = ["\x1b[1;2B"]
                      self.key_sel_right        = ["\x1b[1;2C"]
                      self.key_sel_left         = ["\x1b[1;2D"]
                      self.key_sel_page_up      = ["\x1b[1;10A","\x1b[1;4A","\x1b[5;2~"]
                      self.key_sel_page_down    = ["\x1b[1;10B","\x1b[1;4B","\x1b[6;2~"]
                      self.key_sel_home         = ["\x1b[1;2H","\x1b[1;10D"]
                      self.key_sel_end          = ["\x1b[1;2F","\x1b[1;10C"]
                      self.key_sel_top          = ["\x1b[1;6H"]
                      self.key_sel_bottom       = ["\x1b[1;6F"]
                      self.key_sel_next_word    = ["\x1b[1;6C","\x1b[1;4C"]
                      self.key_sel_prev_word    = ["\x1b[1;6D","\x1b[1;4D"]

                      self.key_sel_all          = ["\x01"]                              # Select All
                      # ctrl-b unused
                      self.key_copy             = ["\x03","\x1bc"]                      # Copy
                      # ctrl-d unused
                      self.key_execute          = ["\x05", "\x1b[15~"]                  # Execute script
                      self.key_find             = ["\x06", "\x1BOQ"]                    # Find
                      self.key_goto             = ["\x07"]                              # Goto line
                      self.key_backspace        = ["\x08","\x7F"]                       # Backspace
                      self.key_indent           = ["\x09"]                              # Indent
                      # line feed reserved
                      # ctrl-k unused
                      self.key_del_line         = ["\x0C"]                              # Delete line
                      self.key_new_line         = ["\x0D", "\0x0A"]                     # New line pressed
                      self.key_find_next        = ["\x0E", "\x1bOR"]                    # Find next
                      # ctrl-o unused
                      self.key_find_previous    = ["\x10", "\x1b[1;2R"]                 # Find previous
                      self.key_comment          = ["\x11"]                              # Comment block
                      self.key_replace          = ["\x12"]                              # Replace
                      self.key_save             = ["\x13", "\x1bs"]                     # Save
                      self.key_toggle_mode      = ["\x14"]                              # Toggle replace/insert mode
                      self.key_change_case      = ["\x15"]                              # Change case
                      self.key_paste            = ["\x16","\x1bv"]                      # Paste
                      # ctrl-w unused
                      self.key_cut              = ["\x18","\x1bx"]                      # Cut
                      # ctrl-y unused
                      # ctrl-z unused

                      self.key_exit             = ["\x1b"]                              # Exit

                      self.key_delete           = ["\x1b[3~"]                           # Delete pressed
                      self.key_unindent         = ["\x1b[Z"]                            # Unindent

  ### Ancestors

  - tools.jsonconfig.JsonConfig
`class Text (cfg, read_only=False)`

  Class which manage the text edition

  Constructor

  Expand source code

      class Text:
              """ Class which manage the text edition """
              def __init__(self, cfg, read_only=False):
                      """ Constructor """
                      self.lines = [""]
                      self.cursor_line   = 0
                      self.cursor_column = 0
                      self.tab_cursor_column   = 0
                      self.modified     = False
                      self.replace_mode  = False
                      self.read_only     = read_only
                      self.cfg = cfg
                      self.view         = None
                      self.tab_size      = self.cfg.tabsize
                      self.selection_start = None
                      self.selection_end   = None
                      self.selection = []
                      self.filename = None
                      self.MOVE_KEYS = self.cfg.key_up+self.cfg.key_down+self.cfg.key_left+self.cfg.key_right+self.cfg.key_home+self.cfg.key_end+self.cfg.key_page_up+self.cfg.key_page_down+self.cfg.key_top+self.cfg.key_bottom+self.cfg.key_next_word+self.cfg.key_previous_word
                      self.SELECT_KEYS = self.cfg.key_sel_up+self.cfg.key_sel_down+self.cfg.key_sel_right+self.cfg.key_sel_left+self.cfg.key_sel_home+self.cfg.key_sel_end+self.cfg.key_sel_top+self.cfg.key_sel_bottom+self.cfg.key_sel_page_up+self.cfg.key_sel_page_down+self.cfg.key_sel_all+self.cfg.key_sel_next_word+self.cfg.key_sel_prev_word
                      self.NOT_READ_ONLY_KEYS = self.cfg.key_copy+self.cfg.key_cut+self.cfg.key_paste+self.cfg.key_indent+self.cfg.key_unindent+self.cfg.key_change_case+self.cfg.key_comment+self.cfg.key_backspace+self.cfg.key_delete+self.cfg.key_new_line+self.cfg.key_del_line
                      self.begin_line = 1
                      self.begin_column = 1

              def set_view(self, view):
                      """ Define the view attached to the text """
                      self.view = view

              def get_count_lines(self):
                      """ Get the total of lines """
                      return len(self.lines)

              def get_cursor_line(self):
                      """ Get the current line of the cursor """
                      return self.cursor_line

              def get_tab_cursor(self, current_line, current_column=None):
                      """ Get position of cursor with line with tabulation """
                      if current_column is None:
                              cursor_column = self.cursor_column
                      else:
                              cursor_column = current_column
                      line = self.lines[current_line]
                      if "\t" in line:
                              tab_cursor_column   = 0
                              column = 0
                              len_line = len(line)
                              while column < cursor_column:
                                      if line[column] == "\t":
                                              pos = tab_cursor_column%self.tab_size
                                              tab_cursor_column += self.tab_size-pos
                                              column          += 1
                                      else:
                                              tab = line.find("\t",column)
                                              if tab > 0:
                                                      partSize = tab - column
                                              else:
                                                      partSize = len_line - column
                                              if column + partSize > cursor_column:
                                                      partSize = cursor_column - column
                                              tab_cursor_column += partSize
                                              column          += partSize
                              return tab_cursor_column
                      else:
                              return cursor_column

              def get_tab_line(self, current_line, start_column, end_column, binary = True):
                      """ Get the tabuled line """
                      accent = False
                      line = self.lines[current_line]
                      if "\t" in line:
                              tab_line = b""
                              cursor   = 0
                              len_line = len(line)
                              column = 0
                              while column < len_line:
                                      char = line[column]
                                      if char == "\t":
                                              pos = cursor%self.tab_size
                                              cursor += self.tab_size-pos
                                              tab_line          += b" "*(self.tab_size-pos)
                                              column            += 1
                                      else:
                                              tab = line.find("\t",column)
                                              if tab < 0:
                                                      tab = len_line
                                              part = line[column:tab]
                                              cursor += len(part)
                                              bin_part = part.encode("utf8")
                                              tab_line          += bin_part
                                              column            += len(part)
                                              if len(part) != len(bin_part):
                                                      accent = True

                              tab_line = tab_line.replace(b"\n",b"")
                              if binary is False:
                                      tab_line = tab_line.decode("utf8")
                      else:
                              if binary:
                                      tab_line = line.encode("utf8")
                                      if len(line) != len(tab_line):
                                              accent = True
                                      tab_line = tab_line.replace(b"\n",b"")
                              else:
                                      tab_line = line
                                      tab_line = tab_line.replace("\n","")

                      if binary and accent:
                              result = tab_line.decode("utf8")[start_column:end_column].encode("utf8")
                      else:
                              result = tab_line[start_column:end_column]

                      return result

              def get_tab_cursor_column(self):
                      """ Get the column of cursor in tabuled line """
                      line = self.lines[self.cursor_line]
                      column = 0
                      self.tab_cursor_column = 0
                      while column < self.cursor_column:
                              if line[column] == "\t":
                                      pos = self.tab_cursor_column%self.tab_size
                                      self.tab_cursor_column += self.tab_size-pos
                                      column += 1
                              else:
                                      tab = line.find("\t",column)
                                      if tab > 0:
                                              delta = tab - column
                                              if column + delta > self.cursor_column:
                                                      delta = self.cursor_column - column
                                                      self.tab_cursor_column += delta
                                                      column += delta
                                              else:
                                                      self.tab_cursor_column += delta
                                                      column += delta
                                      else:
                                              delta = self.cursor_column - column
                                              self.tab_cursor_column += delta
                                              column += delta

              def set_cursor_column(self):
                      """ When the line change compute the cursor position with tabulation in the line """
                      line = self.lines[self.cursor_line]
                      column = 0
                      tab_cursor_column = 0
                      len_line = len(line)
                      column = 0
                      while column < len_line:
                              char = line[column]
                              # If the previous position found exactly in the current line
                              if tab_cursor_column == self.tab_cursor_column:
                                      self.cursor_column = column
                                      break
                              # If the previous position not found in the current line
                              if tab_cursor_column > self.tab_cursor_column:
                                      # Keep last existing position
                                      self.cursor_column = column
                                      break
                              # If tabulation found
                              if char == "\t":
                                      tab_cursor_column += self.tab_size-(tab_cursor_column%self.tab_size)
                                      column += 1
                              else:
                                      # Optimization to accelerate the cursor position
                                      tab = line.find("\t", column)

                                      # Tabulation found
                                      if tab > 0:
                                              delta = tab - column
                                              # If the tabulation position is after the previous tabulation cursor
                                              if delta + tab_cursor_column > self.tab_cursor_column:
                                                      # Move the cursor to the left
                                                      self.cursor_column = column + (self.tab_cursor_column - tab_cursor_column)
                                                      break
                                              else:
                                                      # Another tabulation found, move it after
                                                      tab_cursor_column += delta
                                                      column += delta
                                      # Tabulation not found
                                      else:
                                              # Move the cursor to the end of line
                                              self.cursor_column = column + (self.tab_cursor_column - tab_cursor_column)
                                              break
                      else:
                              if len(line) >= 1:
                                      self.cursor_column = len(line)-1
                              else:
                                      self.cursor_column = 0

              def getFilename(self):
                      """ Return the filename """
                      return self.filename

              def load(self, filename):
                      """ Load file in the editor """
                      self.filename = None
                      try:
                              self.lines = []
                              self.filename = filename
                              file = open(filename, "r")
                              line = file.readline()
                              while line != "":
                                      self.lines.append(line.replace("\r\n","\n"))
                                      line = file.readline()
                              file.close()
                              if len(self.lines) == 0:
                                      self.lines = [""]
                      except MemoryError:
                              # pylint: disable=raise-missing-from
                              raise MemoryError()
                      except OSError:
                              self.lines = [""]
                              # File not existing
                      except Exception as err:
                              tools.logger.syslog(err)
                              self.lines = [""]

              def save(self, filename=None):
                      """ Save text in the file """
                      result = False
                      if self.read_only is False:
                              if filename is None:
                                      filename = self.filename
                              if filename is not None:
                                      try:
                                              file = open(filename, "w")
                                              for line in self.lines:
                                                      file.write(line)
                                              file.close()
                                              self.modified = False
                                              result = True
                                      except Exception as err:
                                              tools.logger.syslog(err)
                      return result

              def change_line(self, moveLine):
                      """ Move the cursor on another line """
                      # If cursor is before the first line
                      if moveLine + self.cursor_line < 0:
                              # Set the cursor to the first line
                              self.cursor_line = 0
                              self.cursor_column = 0
                              self.change_column(0)
                      # If the cursor is after the last line
                      elif moveLine + self.cursor_line >= len(self.lines):
                              self.cursor_line = len(self.lines) -1
                              self.cursor_column = len(self.lines[self.cursor_line])
                              self.change_column(0)
                      # else the cursor is in the lines of text
                      else:
                              self.cursor_line += moveLine
                              if len(self.lines) - 1 == self.cursor_line:
                                      len_line = len(self.lines[self.cursor_line])
                              else:
                                      len_line = len(self.lines[self.cursor_line])-1

                              self.set_cursor_column()
                              # If the new cursor position is outside the line of text
                              if self.cursor_column > len_line:
                                      self.change_column(tools.terminal.MAXINT)

                      if self.selection_start is not None:
                              self.selection_end = [self.cursor_column, self.cursor_line,self.get_tab_cursor(self.cursor_line)]
                      self.view.move()

              def change_column(self, move_column, with_move_view=True):
                      """ Move the cursor on another column """
                      cursor_line   = self.cursor_line
                      cursor_column = self.cursor_column
                      # If the cursor go to the previous line
                      if move_column + self.cursor_column < 0:
                              # If start of line
                              if abs(move_column) > 1:
                                      self.cursor_column = 0
                              # If move to the left and must go to previous line
                              elif self.cursor_line > 0:
                                      self.cursor_line -= 1
                                      self.cursor_column = len(self.lines[self.cursor_line])-1
                      # If the cursor is at the end of line
                      elif move_column + self.cursor_column > len(self.lines[self.cursor_line])-1:
                              # If the cursor is on the last line of file
                              if abs(move_column) > 1 or self.cursor_line+1 == len(self.lines):
                                      # If the file is empty
                                      if self.lines[self.cursor_line] == "":
                                              self.cursor_column = 0
                                              self.tab_cursor_column = 0
                                      # If the last line of contains return char
                                      elif self.lines[self.cursor_line][-1] == "\n":
                                              # Move cursor before return
                                              self.cursor_column = len(self.lines[self.cursor_line])-1
                                      else:
                                              # Move cursor after the last char
                                              self.cursor_column = len(self.lines[self.cursor_line])

                              # If the cursor is on the end of line and must change of line
                              elif self.cursor_line+1 < len(self.lines):
                                      self.cursor_line += 1
                                      self.cursor_column = 0
                                      self.tab_cursor_column = 0
                      # Normal move of cursor
                      else:
                              # Next or previous column
                              self.cursor_column += move_column
                      if abs(move_column) > 0:
                              self.get_tab_cursor_column()
                      self.close_selection()
                      if with_move_view:
                              self.view.move()
                      if self.cursor_column == cursor_column and self.cursor_line == cursor_line:
                              return False
                      else:
                              return True

              def backspace(self, keys=None):
                      """ Manage the backspace key """
                      self.modified = True
                      if self.remove_selection() is False:
                              # The cursor not in the begining of line
                              if self.cursor_column >= 1:
                                      line = self.lines[self.cursor_line]
                                      line = line[0:self.cursor_column-1:]+ line[self.cursor_column  : :]
                                      self.lines[self.cursor_line] = line
                                      self.change_column(-1)
                                      self.view.set_refresh_line()
                              # The cursor is on the begining of line
                              else:
                                      # If the cursor not on the first line
                                      if self.cursor_line >= 1:
                                              # Copy the current line to the end of previous line
                                              self.cursor_column = len(self.lines[self.cursor_line-1])
                                              self.lines[self.cursor_line-1] = self.lines[self.cursor_line-1][:-1] + self.lines[self.cursor_line]
                                              del self.lines[self.cursor_line]
                                              self.view.scroll_part_up()
                                              self.cursor_line -= 1
                                              self.view.set_refresh_after()
                                              self.change_column(-1)

              def delete(self, keys=None):
                      """ Manage the delete key """
                      self.modified = True
                      if self.remove_selection() is False:
                              line = self.lines[self.cursor_line]
                              if self.cursor_column < len(line):
                                      # If the line is empty
                                      if line[self.cursor_column] == "\n":
                                              # If the cursor not at end of files
                                              if self.cursor_line < len(self.lines)-1:
                                                      # Copy the next line to the current line
                                                      self.lines[self.cursor_line] = line[:self.cursor_column] + self.lines[self.cursor_line+1]
                                                      del self.lines[self.cursor_line+1]
                                                      self.view.scroll_part_up()
                                                      self.view.set_refresh_after()
                                      # Else the char is deleted in the middle of line
                                      else:
                                              line = line[0:self.cursor_column:]+ line[self.cursor_column+1  : :]
                                              self.lines[self.cursor_line] = line
                                              self.change_column(0)
                                              self.view.is_refresh_line = True

              def delete_line(self, keys=None):
                      """ Manage the delete of line key """
                      self.hide_selection()
                      self.modified = True
                      # If file contains one or none line
                      if len(self.lines) <= 1:
                              # Clean the content of file
                              self.lines = [""]
                              self.cursor_column = 0
                              self.cursor_line = 0
                              self.change_column(0)
                      # If the current line is not the last of file
                      elif self.cursor_line < len(self.lines):
                              # Delete the line
                              self.cursor_column = 0
                              del self.lines[self.cursor_line]
                              self.view.scroll_part_up()
                              if self.cursor_line >= len(self.lines):
                                      self.cursor_line = len(self.lines)-1
                              self.change_column(0)
                      self.view.set_refresh_after()

              def new_line(self, keys=None):
                      """ Manage the newline key """
                      self.modified = True
                      self.remove_selection()
                      line1 = self.lines[self.cursor_line][:self.cursor_column]+"\n"
                      line2 = self.lines[self.cursor_line][self.cursor_column:]
                      self.lines[self.cursor_line]=line1
                      self.lines.insert(self.cursor_line+1, line2)
                      self.view.scroll_part_down()
                      self.change_column(1)
                      self.view.set_refresh_before()

              def insert_char(self, char):
                      """ Insert character """
                      self.modified = True
                      self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char + self.lines[self.cursor_line][self.cursor_column:]
                      self.change_column(len(char))
                      self.view.set_refresh_line()

              def replace_char(self, char):
                      """ Replace character """
                      self.modified = True
                      if self.cursor_line == len(self.lines)-1 and self.cursor_column >= len(self.lines[self.cursor_line])-1:
                              self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char
                              self.change_column(1)
                              self.view.set_refresh_line()
                      # If it is the last char in the line
                      elif self.lines[self.cursor_line][self.cursor_column] == "\n":
                              # Append char to the line
                              self.insert_char(char)
                      # Else the char must be replaced in the line
                      else:
                              self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char + self.lines[self.cursor_line][self.cursor_column+1:]
                              self.change_column(1)
                              self.view.set_refresh_line()

              def open_selection(self):
                      """ Start a selection """
                      if self.selection_start is None:
                              self.selection_start = [self.cursor_column, self.cursor_line, self.get_tab_cursor(self.cursor_line, self.cursor_column)]

              def close_selection(self):
                      """ Terminate selection """
                      if self.selection_start is not None:
                              self.selection_end = [self.cursor_column, self.cursor_line,self.get_tab_cursor(self.cursor_line)]

              def select_all(self, keys=None):
                      """ Do a select all """
                      self.selection_start = [0,0,0]
                      lastLine = len(self.lines)-1
                      lastColumn = len(self.lines[lastLine])
                      self.move_cursor(lastLine, lastColumn)
                      self.selection_end  = [lastColumn, lastLine, self.get_tab_cursor(lastLine, lastColumn)]
                      self.view.set_refresh_all()

              def get_selection(self):
                      """ Get information about selection """
                      if self.selection_start is not None and self.selection_end is not None:
                              if self.selection_start[1] > self.selection_end[1]:
                                      return self.selection_end, self.selection_start
                              elif self.selection_start[1] < self.selection_end[1]:
                                      return self.selection_start, self.selection_end
                              elif self.selection_start[0] < self.selection_end[0]:
                                      return self.selection_start, self.selection_end
                              else:
                                      return self.selection_end, self.selection_start
                      else:
                              return None, None

              def arrow_up(self, keys=None):
                      """ Manage arrow up key """
                      self.hide_selection()
                      self.change_line(-1)

              def arrow_down(self, keys=None):
                      """ Manage arrow down key """
                      self.hide_selection()
                      self.change_line(1)

              def arrow_left(self, keys=None):
                      """ Manage arrow left key """
                      self.hide_selection()
                      self.change_column(-len(keys))

              def arrow_right(self, keys=None):
                      """ Manage arrow right key """
                      self.hide_selection()
                      self.change_column(len(keys))

              def select_up(self, keys=None):
                      """ Manage select up key """
                      self.open_selection()
                      self.change_line(-1)

              def select_down(self, keys=None):
                      """ Manage select down key """
                      self.open_selection()
                      self.change_line(1)

              def select_left(self, keys=None):
                      """ Manage select left key """
                      self.open_selection()
                      self.change_column(-len(keys))

              def select_right(self, keys=None):
                      """ Manage select right key """
                      self.open_selection()
                      self.change_column(len(keys))

              def select_home(self, keys=None):
                      """ Manage home key """
                      self.open_selection()
                      self.change_column(-tools.terminal.MAXINT)

              def select_end(self, keys=None):
                      """ Manage end key """
                      self.open_selection()
                      self.change_column(tools.terminal.MAXINT)

              def select_page_up(self, keys=None):
                      """ Manage select page up key """
                      self.open_selection()
                      self.change_line((-(self.view.height)) * len(keys))
                      self.change_column(-tools.terminal.MAXINT)

              def select_page_down(self, keys=None):
                      """ Manage select page down key """
                      self.open_selection()
                      self.change_line((self.view.height) * len(keys))
                      self.change_column(tools.terminal.MAXINT)

              def select_next_word(self, keys=None):
                      """ Manage select next word key """
                      self.open_selection()
                      self.move_word(1)

              def select_previous_word(self, keys=None):
                      """ Manage select previous word key """
                      self.open_selection()
                      self.move_word(-1)

              def select_top(self, keys=None):
                      """ Manage select to the first line of text """
                      self.open_selection()
                      self.change_line(-tools.terminal.MAXINT)

              def select_bottom(self, keys=None):
                      """ Manage select to the last line of text """
                      self.open_selection()
                      self.change_line(tools.terminal.MAXINT)

              def page_up(self, keys=None):
                      """ Manage page up key """
                      self.hide_selection()
                      self.change_line((-(self.view.height)) * len(keys))

              def page_down(self, keys=None):
                      """ Manage page down key """
                      self.hide_selection()
                      self.change_line((self.view.height) * len(keys))

              def home(self, keys=None):
                      """ Manage home key """
                      self.hide_selection()
                      self.change_column(-tools.terminal.MAXINT)

              def end(self, keys=None):
                      """ Manage end key """
                      self.hide_selection()
                      self.change_column(tools.terminal.MAXINT)

              def add_char(self, keys=None):
                      """ Manage other key, add character """
                      result = False

                      if tools.strings.isascii(keys[0]):
                              self.remove_selection()
                              for char in keys:
                                      if tools.strings.isascii(char):
                                              if self.replace_mode:
                                                      self.replace_char(char)
                                              else:
                                                      self.insert_char(char)
                                              result = True
                      # if result is False:
                              # print(tools.strings.dump(keys[0]))
                      return result

              def find_next(self, text):
                      """ Find next researched text """
                      result = False
                      # Get the selection
                      selection_start, selection_end = self.get_selection()

                      # Hide the selection
                      self.hide_selection()

                      # Set the start of search at the cursor position
                      current_line   = self.cursor_line
                      current_column = self.cursor_column

                      # If selection activated
                      if selection_start is not None and selection_end is not None:
                              # If selection is on one line
                              if selection_start[1] == selection_end[1] and current_line == selection_start[1]:
                                      # If selection is exactly the size of text
                                      if selection_start[0] == current_column:
                                              # Move the start of search after the text selected
                                              current_column = selection_end[0]

                      # Find the text in next lines
                      while current_line < len(self.lines):
                              # Search text
                              pos = self.lines[current_line].find(text, current_column)

                              # If text found
                              if pos >= 0:
                                      # Move the cursor to the text found
                                      self.cursor_line = current_line
                                      self.cursor_column = pos + len(text)
                                      self.change_column(0)
                                      self.selection_start = [pos, current_line,self.get_tab_cursor(current_line,pos)]
                                      self.selection_end   = [pos + len(text), current_line, self.get_tab_cursor(current_line, pos + len(text))]
                                      result = True
                                      break
                              else:
                                      # Set the search position at the begin of next line
                                      current_column = 0
                                      current_line += 1
                      self.view.move()
                      return result

              def find_previous(self, text):
                      """ Find previous researched text """
                      result = False
                      # Get the selection
                      selection_start, selection_end = self.get_selection()

                      # Hide the selection
                      self.hide_selection()

                      # Set the start of search at the cursor position
                      current_line   = self.cursor_line
                      current_column = self.cursor_column

                      # If selection activated
                      if selection_start is not None and selection_end is not None:
                              # If selection is on one line
                              if selection_start[1] == selection_end[1] and current_line == selection_start[1]:
                                      # If selection is exactly the size of text
                                      if selection_end[0] - selection_start[0] == len(text):
                                              # Move the start of search before the text selected
                                              current_column = selection_start[0]

                      # While the line before the first line not reached
                      while current_line >= 0:
                              # Get the current line
                              line = self.lines[current_line]

                              # If the current column is negative
                              if current_column < 0:
                                      # Set the end of line
                                      current_column = len(line)

                              # Search the text in reverse
                              pos = line.rfind(text, 0, current_column)

                              # If text found
                              if pos >= 0:
                                      self.cursor_line = current_line
                                      self.cursor_column = pos
                                      self.change_column(0)
                                      self.selection_start = [pos, current_line,self.get_tab_cursor(current_line,pos)]
                                      self.selection_end   = [pos + len(text), current_line, self.get_tab_cursor(current_line, pos + len(text))]
                                      result = True
                                      break
                              else:
                                      # Set the search position at the end of line
                                      current_column = -1
                                      current_line -= 1
                      self.view.move()
                      return result

              def hide_selection(self):
                      """ Hide selection """
                      self.view.hide_selection()
                      self.selection_start = self.selection_end = None

              def goto(self, line, column=None, clear_selection=True):
                      """ Goto specified line """
                      if clear_selection:
                              self.hide_selection()
                      if line < 0:
                              self.cursor_line = len(self.lines)-1
                      elif line < 1:
                              self.cursor_line = 1
                      elif line <= len(self.lines):
                              self.cursor_line = line - 1
                      else:
                              self.cursor_line = len(self.lines)-1
                              if column is not None:
                                      column = tools.terminal.MAXINT
                      self.cursor_column = 0

                      if column is not None:
                              if column > 1:
                                      cur_line = self.cursor_line
                                      self.change_column(0, with_move_view=False)
                                      for i in range(len(self.lines[self.cursor_line]) + 1):
                                              self.change_column(1, with_move_view=False)
                                              if self.tab_cursor_column >= column-1:
                                                      break

                                              if self.cursor_line != cur_line:
                                                      self.change_column(-1, with_move_view=False)
                                                      break

                      self.view.move()

              def copy_clipboard(self):
                      """ Copy selection to clipboard """
                      result = []
                      if self.selection_start is not None:
                              selection_start, selection_end = self.get_selection()
                              sel_column_start, sel_line_start, dummy = selection_start
                              sel_column_end,   sel_line_end,   dummy = selection_end
                              result = []
                              if sel_line_start == sel_line_end:
                                      result.append(self.lines[sel_line_start][sel_column_start:sel_column_end])
                              else:
                                      for line in range(sel_line_start, sel_line_end+1):
                                              if line == sel_line_start:
                                                      part = self.lines[line][sel_column_start:]
                                                      if part != "":
                                                              result.append(self.lines[line][sel_column_start:])
                                              elif line == sel_line_end:
                                                      part = self.lines[line][:sel_column_end]
                                                      if part != "":
                                                              result.append(self.lines[line][:sel_column_end])
                                              else:
                                                      result.append(self.lines[line])
                      return result

              def remove_selection(self):
                      """ Remove selection """
                      if self.selection_start is not None:
                              self.modified = True
                              selection_start, selection_end = self.get_selection()
                              sel_column_start, sel_line_start, _ = selection_start
                              sel_column_end,   sel_line_end,   _ = selection_end
                              start = self.lines[sel_line_start][:sel_column_start]
                              end   = self.lines[sel_line_end  ][sel_column_end:]
                              self.lines[sel_line_start] = start + end
                              if sel_line_start < sel_line_end:
                                      for line in range(sel_line_end, sel_line_start,-1):
                                              del self.lines[line]
                              self.move_cursor(sel_line_start, sel_column_start)
                              self.hide_selection()
                              if sel_line_end == sel_line_start:
                                      self.view.set_refresh_line()
                              else:
                                      self.view.set_refresh_bottom(sel_line_start)
                              return True
                      return False

              def paste_clipboard(self, selection):
                      """ Paste clipboard at the cursor position """
                      if selection != [] and selection != [""]:
                              start_line = self.cursor_line
                              # Split the line with insertion
                              start = self.lines[self.cursor_line][:self.cursor_column]
                              end   = self.lines[self.cursor_line][self.cursor_column:]

                              # Paste the first line
                              self.lines[self.cursor_line] = start + selection[0]

                              self.cursor_line += 1

                              # Insert all lines from clipboard
                              for line in selection[1:-1]:
                                      self.lines.insert(self.cursor_line, line)
                                      self.cursor_line += 1

                              # If the last line of clipboard is not empty
                              if len(selection[-1]) >= 1:
                                      # If the last line of clipboard contains carriage return at the end
                                      if selection[-1][-1] == "\n":
                                              if len(selection) > 1:
                                                      # Add the new line
                                                      self.lines.insert(self.cursor_line, selection[-1])
                                                      self.cursor_line += 1

                                              # Add the part after the insertion
                                              self.lines.insert(self.cursor_line, end)
                                              self.cursor_column = 0
                                              if len(selection) >= self.view.height:
                                                      self.view.set_refresh_all()
                                              else:
                                                      self.view.set_refresh_bottom(start_line)
                                      else:
                                              # If many lines with last line without carriage return at the end
                                              if len(selection) > 1:
                                                      self.lines.insert(self.cursor_line, selection[-1] + end)
                                                      self.cursor_column = len(selection[-1])
                                                      if len(selection) >= self.view.height:
                                                              self.view.set_refresh_all()
                                                      else:
                                                              self.view.set_refresh_bottom(start_line)
                                              else:
                                                      # Only one line without carriage return
                                                      self.cursor_line -= 1
                                                      self.lines[self.cursor_line] += end
                                                      self.cursor_column = len(start) + len(selection[-1])
                                                      self.view.set_refresh_line()
                              self.move_cursor(self.cursor_line, self.cursor_column)

              def move_cursor(self, line, column):
                      """ Move the cursor """
                      self.cursor_line   = line
                      self.cursor_column = column
                      self.change_column(0)
                      self.get_tab_cursor_column()

              def copy(self, keys=None):
                      """ Manage copy key """
                      self.selection = self.copy_clipboard()

              def cut(self, keys=None):
                      """ Manage cut key """
                      self.modified = True
                      self.selection = self.copy_clipboard()
                      self.remove_selection()

              def paste(self, keys=None):
                      """ Manage paste key """
                      self.modified = True
                      self.remove_selection()
                      self.paste_clipboard(self.selection)
                      self.hide_selection()

              def change_case(self, keys=None):
                      """ Change the case of selection """
                      selection = self.copy_clipboard()
                      if len(selection) > 0:
                              self.modified = True
                              selection_start = self.selection_start
                              selection_end   = self.selection_end

                              self.remove_selection()
                              isUpper = None
                              for line in selection:
                                      for char in line:
                                              if tools.strings.isupper(char):
                                                      isUpper = True
                                                      break
                                              elif tools.strings.islower(char):
                                                      isUpper = False
                                                      break
                                      if isUpper is not None:
                                              break
                              # pylint:disable=consider-using-enumerate
                              for line in range(len(selection)):
                                      if isUpper:
                                              selection[line] = selection[line].lower()
                                      else:
                                              selection[line] = selection[line].upper()
                              self.paste_clipboard(selection)
                              self.view.set_refresh_selection()
                              self.selection_start = selection_start
                              self.selection_end   = selection_end

              def comment(self, keys=None):
                      """ Comment the selection """
                      self.modified = True

                      # If selection
                      if self.selection_start is not None:
                              selection_start, selection_end = self.get_selection()
                              _, sel_line_start, _ = selection_start
                              _, sel_line_end,   _ = selection_end

                              # Add tabulation
                              for line in range(sel_line_start, sel_line_end+1):
                                      if len(self.lines[line]) >= 1:
                                              if self.lines[line][0] != '#':
                                                      self.lines[line] = "#" + self.lines[line]
                                              else:
                                                      if len(self.lines[line]) >= 1:
                                                              self.lines[line] = self.lines[line][1:]

                              # Move the start selection to the start of first selected line
                              self.selection_start = [0,sel_line_start, 0]

                              # Get the length of last selected line
                              len_line_end =  len(self.lines[sel_line_end])

                              # Move the end of selection at the end of line selected
                              self.selection_end   = [len_line_end-1, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end-1)]
                              self.view.set_refresh_selection()
                      else:
                              if len(self.lines[self.cursor_line]) >= 1:
                                      # If nothing selected
                                      if self.lines[self.cursor_line][0] == "#":
                                              self.lines[self.cursor_line] = self.lines[self.cursor_line][1:]
                                              if self.cursor_column > 0:
                                                      self.change_column(-1)
                                      else:
                                              self.lines[self.cursor_line] = "#" + self.lines[self.cursor_line]
                                              self.change_column(1)
                              self.view.set_refresh_line()

              def indent(self, keys=None):
                      """ Manage tabulation key """
                      # If nothing selected
                      if self.selection_start is None:
                              self.add_char(keys)
                      else:
                              self.modified = True
                              # Indent selection
                              selection_start, selection_end = self.get_selection()
                              sel_column_start, sel_line_start, dummy = selection_start
                              sel_column_end,   sel_line_end,   dummy = selection_end

                              # If a part of line selected
                              if sel_line_start == sel_line_end and not (sel_column_start == 0 and sel_column_end == len(self.lines[sel_line_end])-1):
                                      self.add_char(self.cfg.key_indent)
                              else:
                                      # If the last line selected is at beginning of line
                                      if sel_column_end == 0:
                                              # This line must not be indented
                                              sel_line_end -= 1

                                      # Add tabulation
                                      for line in range(sel_line_start, sel_line_end+1):
                                              self.lines[line] = "\t" + self.lines[line]

                                      # Move the start selection to the start of first selected line
                                      self.selection_start = [0,sel_line_start, 0]

                                      # If the last line selected is not at beginning of line
                                      if sel_column_end > 0:
                                              # Get the length of last selected line
                                              len_line_end =  len(self.lines[sel_line_end])

                                              # If the end of selection is not on the last line
                                              if sel_line_end < len(self.lines)-1:
                                                      len_line_end -= 1

                                              # Move the end of selection at the end of line selected
                                              self.selection_end   = [len_line_end, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end)]
                                      else:
                                              # Move the end of selection at the start of the last line selected
                                              self.selection_end  = [0, sel_line_end+1, 0]
                              self.view.set_refresh_selection()

              def unindent(self, keys=None):
                      """ Manage the unindentation key """
                      # If nothing selected
                      if self.selection_start is None:
                              self.backspace()
                      else:
                              self.modified = True

                              # Unindent selection
                              selection_start, selection_end = self.get_selection()
                              sel_column_start, sel_line_start, dummy = selection_start
                              sel_column_end,   sel_line_end,   dummy = selection_end

                              # If the selection is only alone line
                              if sel_line_start == sel_line_end:
                                      self.hide_selection()
                              else:
                                      # If the last line selected is at beginning of line
                                      if sel_column_end == 0:
                                              # This line must not be indented
                                              sel_line_end -= 1

                                      # Remove indentation
                                      for line in range(sel_line_start, sel_line_end+1):
                                              if len(self.lines[line]) >= 1:
                                                      if self.lines[line][0] == "\t" or self.lines[line][0] == " ":
                                                              self.lines[line] = self.lines[line][1:]

                                      # Move the start selection to the start of first selected line
                                      self.selection_start = [0,sel_line_start, 0]

                                      # If the last line selected is not at beginning of line
                                      if sel_column_end > 0:
                                              # Get the length of last selected line
                                              len_line_end =  len(self.lines[sel_line_end])

                                              # If the end of selection is not on the last line
                                              if sel_line_end < len(self.lines)-1:
                                                      len_line_end -= 1

                                              # Move the end of selection at the end of line selected
                                              self.selection_end   = [len_line_end, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end)]
                                      else:
                                              # Move the end of selection at the start of the last line selected
                                              self.selection_end  = [0, sel_line_end+1, 0]
                              self.view.set_refresh_selection()

              def replace(self, old, new):
                      """ Replace the selection """
                      if self.read_only is False:
                              selection = self.copy_clipboard()
                              if len(selection) == 1:
                                      if selection[0] == old:
                                              self.delete()
                                              self.insert_char(new)
                                              return True
                      return False

              def get_cursor_char(self):
                      """ Get the char on the cursor """
                      try:
                              return self.lines[self.cursor_line][self.cursor_column]
                      except:
                              return None

              def move_word(self, direction):
                      """ Move the cursor to the word """
                      state = 0
                      while self.change_column(direction):
                              current_char = self.get_cursor_char()
                              if current_char is None:
                                      break
                              elif tools.strings.ispunctuation(current_char):
                                      if state == 0:
                                              state = 2
                                      elif state == 1:
                                              break
                              elif tools.strings.isalpha(current_char):
                                      if state == 0:
                                              state = 1
                                      elif state == 2:
                                              break
                              elif tools.strings.isspace(current_char):
                                      if state == 1:
                                              break
                                      if state == 2:
                                              break

              def next_word(self, keys=None):
                      """ Move the cursor to the next word """
                      self.hide_selection()
                      self.move_word(1)
                      self.view.move()

              def previous_word(self, keys=None):
                      """ Move the cursor to the previous word """
                      self.hide_selection()
                      self.move_word(-1)
                      self.view.move()

              def top(self, keys=None):
                      """ Move the cursor to the first line of text """
                      self.goto(1)

              def bottom(self, keys=None):
                      """ Move the cursor to the last line of text """
                      self.goto(tools.terminal.MAXINT)

              def treat_char(self, keys=None):
                      """ Treat character entered """
                      char = ord(keys[0][0])
                      if self.read_only is False:
                              if char >= 0x20 and char != 0x7F:
                                      self.add_char(keys)
                                      return True
                      return False

              def treat_key(self, keys=None):
                      """ Treat keys """
                      key_callback = None
                      if self.treat_char(keys) is False:
                              # Move in the edit field
                              if keys[0] in self.MOVE_KEYS:
                                      if   keys[0] in self.cfg.key_left:            key_callback = self.arrow_left
                                      elif keys[0] in self.cfg.key_right:           key_callback = self.arrow_right
                                      elif keys[0] in self.cfg.key_up  :            key_callback = self.arrow_up
                                      elif keys[0] in self.cfg.key_down:            key_callback = self.arrow_down
                                      elif keys[0] in self.cfg.key_home:            key_callback = self.home
                                      elif keys[0] in self.cfg.key_end:             key_callback = self.end
                                      elif keys[0] in self.cfg.key_page_up:         key_callback = self.page_up
                                      elif keys[0] in self.cfg.key_page_down:       key_callback = self.page_down
                                      elif keys[0] in self.cfg.key_top:             key_callback = self.top
                                      elif keys[0] in self.cfg.key_bottom:          key_callback = self.bottom
                                      elif keys[0] in self.cfg.key_next_word:       key_callback = self.next_word
                                      elif keys[0] in self.cfg.key_previous_word:   key_callback = self.previous_word
                              elif keys[0] in self.SELECT_KEYS:
                                      # Selection the edit field
                                      if   keys[0] in self.cfg.key_sel_right:    key_callback = self.select_right
                                      elif keys[0] in self.cfg.key_sel_left:     key_callback = self.select_left
                                      elif keys[0] in self.cfg.key_sel_up:       key_callback = self.select_up
                                      elif keys[0] in self.cfg.key_sel_down:     key_callback = self.select_down
                                      elif keys[0] in self.cfg.key_sel_home:     key_callback = self.select_home
                                      elif keys[0] in self.cfg.key_sel_end:      key_callback = self.select_end
                                      elif keys[0] in self.cfg.key_sel_top:      key_callback = self.select_top
                                      elif keys[0] in self.cfg.key_sel_bottom:   key_callback = self.select_bottom
                                      elif keys[0] in self.cfg.key_sel_page_up:  key_callback = self.select_page_up
                                      elif keys[0] in self.cfg.key_sel_page_down:key_callback = self.select_page_down
                                      elif keys[0] in self.cfg.key_sel_all:      key_callback = self.select_all
                                      elif keys[0] in self.cfg.key_sel_next_word:key_callback = self.select_next_word
                                      elif keys[0] in self.cfg.key_sel_prev_word:key_callback = self.select_previous_word

                              # If the edit is not in read only
                              elif self.read_only is False:
                                      if keys[0] in self.NOT_READ_ONLY_KEYS:
                                              # Modification in the edit field
                                              if   keys[0] in self.cfg.key_copy:        key_callback = self.copy
                                              elif keys[0] in self.cfg.key_cut:         key_callback = self.cut
                                              elif keys[0] in self.cfg.key_paste:       key_callback = self.paste
                                              elif keys[0] in self.cfg.key_indent:      key_callback = self.indent
                                              elif keys[0] in self.cfg.key_unindent:    key_callback = self.unindent
                                              elif keys[0] in self.cfg.key_change_case: key_callback = self.change_case
                                              elif keys[0] in self.cfg.key_comment:     key_callback = self.comment
                                              elif keys[0] in self.cfg.key_backspace:   key_callback = self.backspace
                                              elif keys[0] in self.cfg.key_delete:      key_callback = self.delete
                                              elif keys[0] in self.cfg.key_new_line:    key_callback = self.new_line
                                              elif keys[0] in self.cfg.key_del_line:    key_callback = self.delete_line
                              # else: self.add_char(keys)
                              if key_callback is not None:
                                      key_callback(keys)
                              else:
                                      if len(keys[0]) > 3:
                                              # If camflasher mouse selection
                                              if keys[0][0:2] == "\x1B[" and keys[0][-1] in ["x","y"]:
                                                      try:
                                                              pos = keys[0][2:-1]
                                                              line, column = pos.split(";")
                                                              self.begin_line, self.begin_column = self.view.get_position()
                                                              if keys[0][-1] == "x":
                                                                      self.goto(int(line)+self.begin_line,int(column)+self.begin_column, True)
                                                                      self.open_selection()
                                                                      self.close_selection()
                                                              else:
                                                                      self.goto(int(line)+self.begin_line,int(column)+self.begin_column, False)
                                                                      self.close_selection()
                                                      except Exception as err:
                                                              pass

  ### Methods

  `def add_char(self, keys=None)`

    Manage other key, add character

    Expand source code

        def add_char(self, keys=None):
                """ Manage other key, add character """
                result = False

                if tools.strings.isascii(keys[0]):
                        self.remove_selection()
                        for char in keys:
                                if tools.strings.isascii(char):
                                        if self.replace_mode:
                                                self.replace_char(char)
                                        else:
                                                self.insert_char(char)
                                        result = True
                # if result is False:
                        # print(tools.strings.dump(keys[0]))
                return result

  `def arrow_down(self, keys=None)`

    Manage arrow down key

    Expand source code

        def arrow_down(self, keys=None):
                """ Manage arrow down key """
                self.hide_selection()
                self.change_line(1)

  `def arrow_left(self, keys=None)`

    Manage arrow left key

    Expand source code

        def arrow_left(self, keys=None):
                """ Manage arrow left key """
                self.hide_selection()
                self.change_column(-len(keys))

  `def arrow_right(self, keys=None)`

    Manage arrow right key

    Expand source code

        def arrow_right(self, keys=None):
                """ Manage arrow right key """
                self.hide_selection()
                self.change_column(len(keys))

  `def arrow_up(self, keys=None)`

    Manage arrow up key

    Expand source code

        def arrow_up(self, keys=None):
                """ Manage arrow up key """
                self.hide_selection()
                self.change_line(-1)

  `def backspace(self, keys=None)`

    Manage the backspace key

    Expand source code

        def backspace(self, keys=None):
                """ Manage the backspace key """
                self.modified = True
                if self.remove_selection() is False:
                        # The cursor not in the begining of line
                        if self.cursor_column >= 1:
                                line = self.lines[self.cursor_line]
                                line = line[0:self.cursor_column-1:]+ line[self.cursor_column  : :]
                                self.lines[self.cursor_line] = line
                                self.change_column(-1)
                                self.view.set_refresh_line()
                        # The cursor is on the begining of line
                        else:
                                # If the cursor not on the first line
                                if self.cursor_line >= 1:
                                        # Copy the current line to the end of previous line
                                        self.cursor_column = len(self.lines[self.cursor_line-1])
                                        self.lines[self.cursor_line-1] = self.lines[self.cursor_line-1][:-1] + self.lines[self.cursor_line]
                                        del self.lines[self.cursor_line]
                                        self.view.scroll_part_up()
                                        self.cursor_line -= 1
                                        self.view.set_refresh_after()
                                        self.change_column(-1)

  `def bottom(self, keys=None)`

    Move the cursor to the last line of text

    Expand source code

        def bottom(self, keys=None):
                """ Move the cursor to the last line of text """
                self.goto(tools.terminal.MAXINT)

  `def change_case(self, keys=None)`

    Change the case of selection

    Expand source code

        def change_case(self, keys=None):
                """ Change the case of selection """
                selection = self.copy_clipboard()
                if len(selection) > 0:
                        self.modified = True
                        selection_start = self.selection_start
                        selection_end   = self.selection_end

                        self.remove_selection()
                        isUpper = None
                        for line in selection:
                                for char in line:
                                        if tools.strings.isupper(char):
                                                isUpper = True
                                                break
                                        elif tools.strings.islower(char):
                                                isUpper = False
                                                break
                                if isUpper is not None:
                                        break
                        # pylint:disable=consider-using-enumerate
                        for line in range(len(selection)):
                                if isUpper:
                                        selection[line] = selection[line].lower()
                                else:
                                        selection[line] = selection[line].upper()
                        self.paste_clipboard(selection)
                        self.view.set_refresh_selection()
                        self.selection_start = selection_start
                        self.selection_end   = selection_end

  `def change_column(self, move_column, with_move_view=True)`

    Move the cursor on another column

    Expand source code

        def change_column(self, move_column, with_move_view=True):
                """ Move the cursor on another column """
                cursor_line   = self.cursor_line
                cursor_column = self.cursor_column
                # If the cursor go to the previous line
                if move_column + self.cursor_column < 0:
                        # If start of line
                        if abs(move_column) > 1:
                                self.cursor_column = 0
                        # If move to the left and must go to previous line
                        elif self.cursor_line > 0:
                                self.cursor_line -= 1
                                self.cursor_column = len(self.lines[self.cursor_line])-1
                # If the cursor is at the end of line
                elif move_column + self.cursor_column > len(self.lines[self.cursor_line])-1:
                        # If the cursor is on the last line of file
                        if abs(move_column) > 1 or self.cursor_line+1 == len(self.lines):
                                # If the file is empty
                                if self.lines[self.cursor_line] == "":
                                        self.cursor_column = 0
                                        self.tab_cursor_column = 0
                                # If the last line of contains return char
                                elif self.lines[self.cursor_line][-1] == "\n":
                                        # Move cursor before return
                                        self.cursor_column = len(self.lines[self.cursor_line])-1
                                else:
                                        # Move cursor after the last char
                                        self.cursor_column = len(self.lines[self.cursor_line])

                        # If the cursor is on the end of line and must change of line
                        elif self.cursor_line+1 < len(self.lines):
                                self.cursor_line += 1
                                self.cursor_column = 0
                                self.tab_cursor_column = 0
                # Normal move of cursor
                else:
                        # Next or previous column
                        self.cursor_column += move_column
                if abs(move_column) > 0:
                        self.get_tab_cursor_column()
                self.close_selection()
                if with_move_view:
                        self.view.move()
                if self.cursor_column == cursor_column and self.cursor_line == cursor_line:
                        return False
                else:
                        return True

  `def change_line(self, moveLine)`

    Move the cursor on another line

    Expand source code

        def change_line(self, moveLine):
                """ Move the cursor on another line """
                # If cursor is before the first line
                if moveLine + self.cursor_line < 0:
                        # Set the cursor to the first line
                        self.cursor_line = 0
                        self.cursor_column = 0
                        self.change_column(0)
                # If the cursor is after the last line
                elif moveLine + self.cursor_line >= len(self.lines):
                        self.cursor_line = len(self.lines) -1
                        self.cursor_column = len(self.lines[self.cursor_line])
                        self.change_column(0)
                # else the cursor is in the lines of text
                else:
                        self.cursor_line += moveLine
                        if len(self.lines) - 1 == self.cursor_line:
                                len_line = len(self.lines[self.cursor_line])
                        else:
                                len_line = len(self.lines[self.cursor_line])-1

                        self.set_cursor_column()
                        # If the new cursor position is outside the line of text
                        if self.cursor_column > len_line:
                                self.change_column(tools.terminal.MAXINT)

                if self.selection_start is not None:
                        self.selection_end = [self.cursor_column, self.cursor_line,self.get_tab_cursor(self.cursor_line)]
                self.view.move()

  `def close_selection(self)`

    Terminate selection

    Expand source code

        def close_selection(self):
                """ Terminate selection """
                if self.selection_start is not None:
                        self.selection_end = [self.cursor_column, self.cursor_line,self.get_tab_cursor(self.cursor_line)]

  `def comment(self, keys=None)`

    Comment the selection

    Expand source code

        def comment(self, keys=None):
                """ Comment the selection """
                self.modified = True

                # If selection
                if self.selection_start is not None:
                        selection_start, selection_end = self.get_selection()
                        _, sel_line_start, _ = selection_start
                        _, sel_line_end,   _ = selection_end

                        # Add tabulation
                        for line in range(sel_line_start, sel_line_end+1):
                                if len(self.lines[line]) >= 1:
                                        if self.lines[line][0] != '#':
                                                self.lines[line] = "#" + self.lines[line]
                                        else:
                                                if len(self.lines[line]) >= 1:
                                                        self.lines[line] = self.lines[line][1:]

                        # Move the start selection to the start of first selected line
                        self.selection_start = [0,sel_line_start, 0]

                        # Get the length of last selected line
                        len_line_end =  len(self.lines[sel_line_end])

                        # Move the end of selection at the end of line selected
                        self.selection_end   = [len_line_end-1, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end-1)]
                        self.view.set_refresh_selection()
                else:
                        if len(self.lines[self.cursor_line]) >= 1:
                                # If nothing selected
                                if self.lines[self.cursor_line][0] == "#":
                                        self.lines[self.cursor_line] = self.lines[self.cursor_line][1:]
                                        if self.cursor_column > 0:
                                                self.change_column(-1)
                                else:
                                        self.lines[self.cursor_line] = "#" + self.lines[self.cursor_line]
                                        self.change_column(1)
                        self.view.set_refresh_line()

  `def copy(self, keys=None)`

    Manage copy key

    Expand source code

        def copy(self, keys=None):
                """ Manage copy key """
                self.selection = self.copy_clipboard()

  `def copy_clipboard(self)`

    Copy selection to clipboard

    Expand source code

        def copy_clipboard(self):
                """ Copy selection to clipboard """
                result = []
                if self.selection_start is not None:
                        selection_start, selection_end = self.get_selection()
                        sel_column_start, sel_line_start, dummy = selection_start
                        sel_column_end,   sel_line_end,   dummy = selection_end
                        result = []
                        if sel_line_start == sel_line_end:
                                result.append(self.lines[sel_line_start][sel_column_start:sel_column_end])
                        else:
                                for line in range(sel_line_start, sel_line_end+1):
                                        if line == sel_line_start:
                                                part = self.lines[line][sel_column_start:]
                                                if part != "":
                                                        result.append(self.lines[line][sel_column_start:])
                                        elif line == sel_line_end:
                                                part = self.lines[line][:sel_column_end]
                                                if part != "":
                                                        result.append(self.lines[line][:sel_column_end])
                                        else:
                                                result.append(self.lines[line])
                return result

  `def cut(self, keys=None)`

    Manage cut key

    Expand source code

        def cut(self, keys=None):
                """ Manage cut key """
                self.modified = True
                self.selection = self.copy_clipboard()
                self.remove_selection()

  `def delete(self, keys=None)`

    Manage the delete key

    Expand source code

        def delete(self, keys=None):
                """ Manage the delete key """
                self.modified = True
                if self.remove_selection() is False:
                        line = self.lines[self.cursor_line]
                        if self.cursor_column < len(line):
                                # If the line is empty
                                if line[self.cursor_column] == "\n":
                                        # If the cursor not at end of files
                                        if self.cursor_line < len(self.lines)-1:
                                                # Copy the next line to the current line
                                                self.lines[self.cursor_line] = line[:self.cursor_column] + self.lines[self.cursor_line+1]
                                                del self.lines[self.cursor_line+1]
                                                self.view.scroll_part_up()
                                                self.view.set_refresh_after()
                                # Else the char is deleted in the middle of line
                                else:
                                        line = line[0:self.cursor_column:]+ line[self.cursor_column+1  : :]
                                        self.lines[self.cursor_line] = line
                                        self.change_column(0)
                                        self.view.is_refresh_line = True

  `def delete_line(self, keys=None)`

    Manage the delete of line key

    Expand source code

        def delete_line(self, keys=None):
                """ Manage the delete of line key """
                self.hide_selection()
                self.modified = True
                # If file contains one or none line
                if len(self.lines) <= 1:
                        # Clean the content of file
                        self.lines = [""]
                        self.cursor_column = 0
                        self.cursor_line = 0
                        self.change_column(0)
                # If the current line is not the last of file
                elif self.cursor_line < len(self.lines):
                        # Delete the line
                        self.cursor_column = 0
                        del self.lines[self.cursor_line]
                        self.view.scroll_part_up()
                        if self.cursor_line >= len(self.lines):
                                self.cursor_line = len(self.lines)-1
                        self.change_column(0)
                self.view.set_refresh_after()

  `def end(self, keys=None)`

    Manage end key

    Expand source code

        def end(self, keys=None):
                """ Manage end key """
                self.hide_selection()
                self.change_column(tools.terminal.MAXINT)

  `def find_next(self, text)`

    Find next researched text

    Expand source code

        def find_next(self, text):
                """ Find next researched text """
                result = False
                # Get the selection
                selection_start, selection_end = self.get_selection()

                # Hide the selection
                self.hide_selection()

                # Set the start of search at the cursor position
                current_line   = self.cursor_line
                current_column = self.cursor_column

                # If selection activated
                if selection_start is not None and selection_end is not None:
                        # If selection is on one line
                        if selection_start[1] == selection_end[1] and current_line == selection_start[1]:
                                # If selection is exactly the size of text
                                if selection_start[0] == current_column:
                                        # Move the start of search after the text selected
                                        current_column = selection_end[0]

                # Find the text in next lines
                while current_line < len(self.lines):
                        # Search text
                        pos = self.lines[current_line].find(text, current_column)

                        # If text found
                        if pos >= 0:
                                # Move the cursor to the text found
                                self.cursor_line = current_line
                                self.cursor_column = pos + len(text)
                                self.change_column(0)
                                self.selection_start = [pos, current_line,self.get_tab_cursor(current_line,pos)]
                                self.selection_end   = [pos + len(text), current_line, self.get_tab_cursor(current_line, pos + len(text))]
                                result = True
                                break
                        else:
                                # Set the search position at the begin of next line
                                current_column = 0
                                current_line += 1
                self.view.move()
                return result

  `def find_previous(self, text)`

    Find previous researched text

    Expand source code

        def find_previous(self, text):
                """ Find previous researched text """
                result = False
                # Get the selection
                selection_start, selection_end = self.get_selection()

                # Hide the selection
                self.hide_selection()

                # Set the start of search at the cursor position
                current_line   = self.cursor_line
                current_column = self.cursor_column

                # If selection activated
                if selection_start is not None and selection_end is not None:
                        # If selection is on one line
                        if selection_start[1] == selection_end[1] and current_line == selection_start[1]:
                                # If selection is exactly the size of text
                                if selection_end[0] - selection_start[0] == len(text):
                                        # Move the start of search before the text selected
                                        current_column = selection_start[0]

                # While the line before the first line not reached
                while current_line >= 0:
                        # Get the current line
                        line = self.lines[current_line]

                        # If the current column is negative
                        if current_column < 0:
                                # Set the end of line
                                current_column = len(line)

                        # Search the text in reverse
                        pos = line.rfind(text, 0, current_column)

                        # If text found
                        if pos >= 0:
                                self.cursor_line = current_line
                                self.cursor_column = pos
                                self.change_column(0)
                                self.selection_start = [pos, current_line,self.get_tab_cursor(current_line,pos)]
                                self.selection_end   = [pos + len(text), current_line, self.get_tab_cursor(current_line, pos + len(text))]
                                result = True
                                break
                        else:
                                # Set the search position at the end of line
                                current_column = -1
                                current_line -= 1
                self.view.move()
                return result

  `def getFilename(self)`

    Return the filename

    Expand source code

        def getFilename(self):
                """ Return the filename """
                return self.filename

  `def get_count_lines(self)`

    Get the total of lines

    Expand source code

        def get_count_lines(self):
                """ Get the total of lines """
                return len(self.lines)

  `def get_cursor_char(self)`

    Get the char on the cursor

    Expand source code

        def get_cursor_char(self):
                """ Get the char on the cursor """
                try:
                        return self.lines[self.cursor_line][self.cursor_column]
                except:
                        return None

  `def get_cursor_line(self)`

    Get the current line of the cursor

    Expand source code

        def get_cursor_line(self):
                """ Get the current line of the cursor """
                return self.cursor_line

  `def get_selection(self)`

    Get information about selection

    Expand source code

        def get_selection(self):
                """ Get information about selection """
                if self.selection_start is not None and self.selection_end is not None:
                        if self.selection_start[1] > self.selection_end[1]:
                                return self.selection_end, self.selection_start
                        elif self.selection_start[1] < self.selection_end[1]:
                                return self.selection_start, self.selection_end
                        elif self.selection_start[0] < self.selection_end[0]:
                                return self.selection_start, self.selection_end
                        else:
                                return self.selection_end, self.selection_start
                else:
                        return None, None

  `def get_tab_cursor(self, current_line, current_column=None)`

    Get position of cursor with line with tabulation

    Expand source code

        def get_tab_cursor(self, current_line, current_column=None):
                """ Get position of cursor with line with tabulation """
                if current_column is None:
                        cursor_column = self.cursor_column
                else:
                        cursor_column = current_column
                line = self.lines[current_line]
                if "\t" in line:
                        tab_cursor_column   = 0
                        column = 0
                        len_line = len(line)
                        while column < cursor_column:
                                if line[column] == "\t":
                                        pos = tab_cursor_column%self.tab_size
                                        tab_cursor_column += self.tab_size-pos
                                        column          += 1
                                else:
                                        tab = line.find("\t",column)
                                        if tab > 0:
                                                partSize = tab - column
                                        else:
                                                partSize = len_line - column
                                        if column + partSize > cursor_column:
                                                partSize = cursor_column - column
                                        tab_cursor_column += partSize
                                        column          += partSize
                        return tab_cursor_column
                else:
                        return cursor_column

  `def get_tab_cursor_column(self)`

    Get the column of cursor in tabuled line

    Expand source code

        def get_tab_cursor_column(self):
                """ Get the column of cursor in tabuled line """
                line = self.lines[self.cursor_line]
                column = 0
                self.tab_cursor_column = 0
                while column < self.cursor_column:
                        if line[column] == "\t":
                                pos = self.tab_cursor_column%self.tab_size
                                self.tab_cursor_column += self.tab_size-pos
                                column += 1
                        else:
                                tab = line.find("\t",column)
                                if tab > 0:
                                        delta = tab - column
                                        if column + delta > self.cursor_column:
                                                delta = self.cursor_column - column
                                                self.tab_cursor_column += delta
                                                column += delta
                                        else:
                                                self.tab_cursor_column += delta
                                                column += delta
                                else:
                                        delta = self.cursor_column - column
                                        self.tab_cursor_column += delta
                                        column += delta

  `def get_tab_line(self, current_line, start_column, end_column, binary=True)`

    Get the tabuled line

    Expand source code

        def get_tab_line(self, current_line, start_column, end_column, binary = True):
                """ Get the tabuled line """
                accent = False
                line = self.lines[current_line]
                if "\t" in line:
                        tab_line = b""
                        cursor   = 0
                        len_line = len(line)
                        column = 0
                        while column < len_line:
                                char = line[column]
                                if char == "\t":
                                        pos = cursor%self.tab_size
                                        cursor += self.tab_size-pos
                                        tab_line          += b" "*(self.tab_size-pos)
                                        column            += 1
                                else:
                                        tab = line.find("\t",column)
                                        if tab < 0:
                                                tab = len_line
                                        part = line[column:tab]
                                        cursor += len(part)
                                        bin_part = part.encode("utf8")
                                        tab_line          += bin_part
                                        column            += len(part)
                                        if len(part) != len(bin_part):
                                                accent = True

                        tab_line = tab_line.replace(b"\n",b"")
                        if binary is False:
                                tab_line = tab_line.decode("utf8")
                else:
                        if binary:
                                tab_line = line.encode("utf8")
                                if len(line) != len(tab_line):
                                        accent = True
                                tab_line = tab_line.replace(b"\n",b"")
                        else:
                                tab_line = line
                                tab_line = tab_line.replace("\n","")

                if binary and accent:
                        result = tab_line.decode("utf8")[start_column:end_column].encode("utf8")
                else:
                        result = tab_line[start_column:end_column]

                return result

  `def goto(self, line, column=None, clear_selection=True)`

    Goto specified line

    Expand source code

        def goto(self, line, column=None, clear_selection=True):
                """ Goto specified line """
                if clear_selection:
                        self.hide_selection()
                if line < 0:
                        self.cursor_line = len(self.lines)-1
                elif line < 1:
                        self.cursor_line = 1
                elif line <= len(self.lines):
                        self.cursor_line = line - 1
                else:
                        self.cursor_line = len(self.lines)-1
                        if column is not None:
                                column = tools.terminal.MAXINT
                self.cursor_column = 0

                if column is not None:
                        if column > 1:
                                cur_line = self.cursor_line
                                self.change_column(0, with_move_view=False)
                                for i in range(len(self.lines[self.cursor_line]) + 1):
                                        self.change_column(1, with_move_view=False)
                                        if self.tab_cursor_column >= column-1:
                                                break

                                        if self.cursor_line != cur_line:
                                                self.change_column(-1, with_move_view=False)
                                                break

                self.view.move()

  `def hide_selection(self)`

    Hide selection

    Expand source code

        def hide_selection(self):
                """ Hide selection """
                self.view.hide_selection()
                self.selection_start = self.selection_end = None

  `def home(self, keys=None)`

    Manage home key

    Expand source code

        def home(self, keys=None):
                """ Manage home key """
                self.hide_selection()
                self.change_column(-tools.terminal.MAXINT)

  `def indent(self, keys=None)`

    Manage tabulation key

    Expand source code

        def indent(self, keys=None):
                """ Manage tabulation key """
                # If nothing selected
                if self.selection_start is None:
                        self.add_char(keys)
                else:
                        self.modified = True
                        # Indent selection
                        selection_start, selection_end = self.get_selection()
                        sel_column_start, sel_line_start, dummy = selection_start
                        sel_column_end,   sel_line_end,   dummy = selection_end

                        # If a part of line selected
                        if sel_line_start == sel_line_end and not (sel_column_start == 0 and sel_column_end == len(self.lines[sel_line_end])-1):
                                self.add_char(self.cfg.key_indent)
                        else:
                                # If the last line selected is at beginning of line
                                if sel_column_end == 0:
                                        # This line must not be indented
                                        sel_line_end -= 1

                                # Add tabulation
                                for line in range(sel_line_start, sel_line_end+1):
                                        self.lines[line] = "\t" + self.lines[line]

                                # Move the start selection to the start of first selected line
                                self.selection_start = [0,sel_line_start, 0]

                                # If the last line selected is not at beginning of line
                                if sel_column_end > 0:
                                        # Get the length of last selected line
                                        len_line_end =  len(self.lines[sel_line_end])

                                        # If the end of selection is not on the last line
                                        if sel_line_end < len(self.lines)-1:
                                                len_line_end -= 1

                                        # Move the end of selection at the end of line selected
                                        self.selection_end   = [len_line_end, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end)]
                                else:
                                        # Move the end of selection at the start of the last line selected
                                        self.selection_end  = [0, sel_line_end+1, 0]
                        self.view.set_refresh_selection()

  `def insert_char(self, char)`

    Insert character

    Expand source code

        def insert_char(self, char):
                """ Insert character """
                self.modified = True
                self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char + self.lines[self.cursor_line][self.cursor_column:]
                self.change_column(len(char))
                self.view.set_refresh_line()

  `def load(self, filename)`

    Load file in the editor

    Expand source code

        def load(self, filename):
                """ Load file in the editor """
                self.filename = None
                try:
                        self.lines = []
                        self.filename = filename
                        file = open(filename, "r")
                        line = file.readline()
                        while line != "":
                                self.lines.append(line.replace("\r\n","\n"))
                                line = file.readline()
                        file.close()
                        if len(self.lines) == 0:
                                self.lines = [""]
                except MemoryError:
                        # pylint: disable=raise-missing-from
                        raise MemoryError()
                except OSError:
                        self.lines = [""]
                        # File not existing
                except Exception as err:
                        tools.logger.syslog(err)
                        self.lines = [""]

  `def move_cursor(self, line, column)`

    Move the cursor

    Expand source code

        def move_cursor(self, line, column):
                """ Move the cursor """
                self.cursor_line   = line
                self.cursor_column = column
                self.change_column(0)
                self.get_tab_cursor_column()

  `def move_word(self, direction)`

    Move the cursor to the word

    Expand source code

        def move_word(self, direction):
                """ Move the cursor to the word """
                state = 0
                while self.change_column(direction):
                        current_char = self.get_cursor_char()
                        if current_char is None:
                                break
                        elif tools.strings.ispunctuation(current_char):
                                if state == 0:
                                        state = 2
                                elif state == 1:
                                        break
                        elif tools.strings.isalpha(current_char):
                                if state == 0:
                                        state = 1
                                elif state == 2:
                                        break
                        elif tools.strings.isspace(current_char):
                                if state == 1:
                                        break
                                if state == 2:
                                        break

  `def new_line(self, keys=None)`

    Manage the newline key

    Expand source code

        def new_line(self, keys=None):
                """ Manage the newline key """
                self.modified = True
                self.remove_selection()
                line1 = self.lines[self.cursor_line][:self.cursor_column]+"\n"
                line2 = self.lines[self.cursor_line][self.cursor_column:]
                self.lines[self.cursor_line]=line1
                self.lines.insert(self.cursor_line+1, line2)
                self.view.scroll_part_down()
                self.change_column(1)
                self.view.set_refresh_before()

  `def next_word(self, keys=None)`

    Move the cursor to the next word

    Expand source code

        def next_word(self, keys=None):
                """ Move the cursor to the next word """
                self.hide_selection()
                self.move_word(1)
                self.view.move()

  `def open_selection(self)`

    Start a selection

    Expand source code

        def open_selection(self):
                """ Start a selection """
                if self.selection_start is None:
                        self.selection_start = [self.cursor_column, self.cursor_line, self.get_tab_cursor(self.cursor_line, self.cursor_column)]

  `def page_down(self, keys=None)`

    Manage page down key

    Expand source code

        def page_down(self, keys=None):
                """ Manage page down key """
                self.hide_selection()
                self.change_line((self.view.height) * len(keys))

  `def page_up(self, keys=None)`

    Manage page up key

    Expand source code

        def page_up(self, keys=None):
                """ Manage page up key """
                self.hide_selection()
                self.change_line((-(self.view.height)) * len(keys))

  `def paste(self, keys=None)`

    Manage paste key

    Expand source code

        def paste(self, keys=None):
                """ Manage paste key """
                self.modified = True
                self.remove_selection()
                self.paste_clipboard(self.selection)
                self.hide_selection()

  `def paste_clipboard(self, selection)`

    Paste clipboard at the cursor position

    Expand source code

        def paste_clipboard(self, selection):
                """ Paste clipboard at the cursor position """
                if selection != [] and selection != [""]:
                        start_line = self.cursor_line
                        # Split the line with insertion
                        start = self.lines[self.cursor_line][:self.cursor_column]
                        end   = self.lines[self.cursor_line][self.cursor_column:]

                        # Paste the first line
                        self.lines[self.cursor_line] = start + selection[0]

                        self.cursor_line += 1

                        # Insert all lines from clipboard
                        for line in selection[1:-1]:
                                self.lines.insert(self.cursor_line, line)
                                self.cursor_line += 1

                        # If the last line of clipboard is not empty
                        if len(selection[-1]) >= 1:
                                # If the last line of clipboard contains carriage return at the end
                                if selection[-1][-1] == "\n":
                                        if len(selection) > 1:
                                                # Add the new line
                                                self.lines.insert(self.cursor_line, selection[-1])
                                                self.cursor_line += 1

                                        # Add the part after the insertion
                                        self.lines.insert(self.cursor_line, end)
                                        self.cursor_column = 0
                                        if len(selection) >= self.view.height:
                                                self.view.set_refresh_all()
                                        else:
                                                self.view.set_refresh_bottom(start_line)
                                else:
                                        # If many lines with last line without carriage return at the end
                                        if len(selection) > 1:
                                                self.lines.insert(self.cursor_line, selection[-1] + end)
                                                self.cursor_column = len(selection[-1])
                                                if len(selection) >= self.view.height:
                                                        self.view.set_refresh_all()
                                                else:
                                                        self.view.set_refresh_bottom(start_line)
                                        else:
                                                # Only one line without carriage return
                                                self.cursor_line -= 1
                                                self.lines[self.cursor_line] += end
                                                self.cursor_column = len(start) + len(selection[-1])
                                                self.view.set_refresh_line()
                        self.move_cursor(self.cursor_line, self.cursor_column)

  `def previous_word(self, keys=None)`

    Move the cursor to the previous word

    Expand source code

        def previous_word(self, keys=None):
                """ Move the cursor to the previous word """
                self.hide_selection()
                self.move_word(-1)
                self.view.move()

  `def remove_selection(self)`

    Remove selection

    Expand source code

        def remove_selection(self):
                """ Remove selection """
                if self.selection_start is not None:
                        self.modified = True
                        selection_start, selection_end = self.get_selection()
                        sel_column_start, sel_line_start, _ = selection_start
                        sel_column_end,   sel_line_end,   _ = selection_end
                        start = self.lines[sel_line_start][:sel_column_start]
                        end   = self.lines[sel_line_end  ][sel_column_end:]
                        self.lines[sel_line_start] = start + end
                        if sel_line_start < sel_line_end:
                                for line in range(sel_line_end, sel_line_start,-1):
                                        del self.lines[line]
                        self.move_cursor(sel_line_start, sel_column_start)
                        self.hide_selection()
                        if sel_line_end == sel_line_start:
                                self.view.set_refresh_line()
                        else:
                                self.view.set_refresh_bottom(sel_line_start)
                        return True
                return False

  `def replace(self, old, new)`

    Replace the selection

    Expand source code

        def replace(self, old, new):
                """ Replace the selection """
                if self.read_only is False:
                        selection = self.copy_clipboard()
                        if len(selection) == 1:
                                if selection[0] == old:
                                        self.delete()
                                        self.insert_char(new)
                                        return True
                return False

  `def replace_char(self, char)`

    Replace character

    Expand source code

        def replace_char(self, char):
                """ Replace character """
                self.modified = True
                if self.cursor_line == len(self.lines)-1 and self.cursor_column >= len(self.lines[self.cursor_line])-1:
                        self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char
                        self.change_column(1)
                        self.view.set_refresh_line()
                # If it is the last char in the line
                elif self.lines[self.cursor_line][self.cursor_column] == "\n":
                        # Append char to the line
                        self.insert_char(char)
                # Else the char must be replaced in the line
                else:
                        self.lines[self.cursor_line] = self.lines[self.cursor_line][:self.cursor_column] + char + self.lines[self.cursor_line][self.cursor_column+1:]
                        self.change_column(1)
                        self.view.set_refresh_line()

  `def save(self, filename=None)`

    Save text in the file

    Expand source code

        def save(self, filename=None):
                """ Save text in the file """
                result = False
                if self.read_only is False:
                        if filename is None:
                                filename = self.filename
                        if filename is not None:
                                try:
                                        file = open(filename, "w")
                                        for line in self.lines:
                                                file.write(line)
                                        file.close()
                                        self.modified = False
                                        result = True
                                except Exception as err:
                                        tools.logger.syslog(err)
                return result

  `def select_all(self, keys=None)`

    Do a select all

    Expand source code

        def select_all(self, keys=None):
                """ Do a select all """
                self.selection_start = [0,0,0]
                lastLine = len(self.lines)-1
                lastColumn = len(self.lines[lastLine])
                self.move_cursor(lastLine, lastColumn)
                self.selection_end  = [lastColumn, lastLine, self.get_tab_cursor(lastLine, lastColumn)]
                self.view.set_refresh_all()

  `def select_bottom(self, keys=None)`

    Manage select to the last line of text

    Expand source code

        def select_bottom(self, keys=None):
                """ Manage select to the last line of text """
                self.open_selection()
                self.change_line(tools.terminal.MAXINT)

  `def select_down(self, keys=None)`

    Manage select down key

    Expand source code

        def select_down(self, keys=None):
                """ Manage select down key """
                self.open_selection()
                self.change_line(1)

  `def select_end(self, keys=None)`

    Manage end key

    Expand source code

        def select_end(self, keys=None):
                """ Manage end key """
                self.open_selection()
                self.change_column(tools.terminal.MAXINT)

  `def select_home(self, keys=None)`

    Manage home key

    Expand source code

        def select_home(self, keys=None):
                """ Manage home key """
                self.open_selection()
                self.change_column(-tools.terminal.MAXINT)

  `def select_left(self, keys=None)`

    Manage select left key

    Expand source code

        def select_left(self, keys=None):
                """ Manage select left key """
                self.open_selection()
                self.change_column(-len(keys))

  `def select_next_word(self, keys=None)`

    Manage select next word key

    Expand source code

        def select_next_word(self, keys=None):
                """ Manage select next word key """
                self.open_selection()
                self.move_word(1)

  `def select_page_down(self, keys=None)`

    Manage select page down key

    Expand source code

        def select_page_down(self, keys=None):
                """ Manage select page down key """
                self.open_selection()
                self.change_line((self.view.height) * len(keys))
                self.change_column(tools.terminal.MAXINT)

  `def select_page_up(self, keys=None)`

    Manage select page up key

    Expand source code

        def select_page_up(self, keys=None):
                """ Manage select page up key """
                self.open_selection()
                self.change_line((-(self.view.height)) * len(keys))
                self.change_column(-tools.terminal.MAXINT)

  `def select_previous_word(self, keys=None)`

    Manage select previous word key

    Expand source code

        def select_previous_word(self, keys=None):
                """ Manage select previous word key """
                self.open_selection()
                self.move_word(-1)

  `def select_right(self, keys=None)`

    Manage select right key

    Expand source code

        def select_right(self, keys=None):
                """ Manage select right key """
                self.open_selection()
                self.change_column(len(keys))

  `def select_top(self, keys=None)`

    Manage select to the first line of text

    Expand source code

        def select_top(self, keys=None):
                """ Manage select to the first line of text """
                self.open_selection()
                self.change_line(-tools.terminal.MAXINT)

  `def select_up(self, keys=None)`

    Manage select up key

    Expand source code

        def select_up(self, keys=None):
                """ Manage select up key """
                self.open_selection()
                self.change_line(-1)

  `def set_cursor_column(self)`

    When the line change compute the cursor position with tabulation in the line

    Expand source code

        def set_cursor_column(self):
                """ When the line change compute the cursor position with tabulation in the line """
                line = self.lines[self.cursor_line]
                column = 0
                tab_cursor_column = 0
                len_line = len(line)
                column = 0
                while column < len_line:
                        char = line[column]
                        # If the previous position found exactly in the current line
                        if tab_cursor_column == self.tab_cursor_column:
                                self.cursor_column = column
                                break
                        # If the previous position not found in the current line
                        if tab_cursor_column > self.tab_cursor_column:
                                # Keep last existing position
                                self.cursor_column = column
                                break
                        # If tabulation found
                        if char == "\t":
                                tab_cursor_column += self.tab_size-(tab_cursor_column%self.tab_size)
                                column += 1
                        else:
                                # Optimization to accelerate the cursor position
                                tab = line.find("\t", column)

                                # Tabulation found
                                if tab > 0:
                                        delta = tab - column
                                        # If the tabulation position is after the previous tabulation cursor
                                        if delta + tab_cursor_column > self.tab_cursor_column:
                                                # Move the cursor to the left
                                                self.cursor_column = column + (self.tab_cursor_column - tab_cursor_column)
                                                break
                                        else:
                                                # Another tabulation found, move it after
                                                tab_cursor_column += delta
                                                column += delta
                                # Tabulation not found
                                else:
                                        # Move the cursor to the end of line
                                        self.cursor_column = column + (self.tab_cursor_column - tab_cursor_column)
                                        break
                else:
                        if len(line) >= 1:
                                self.cursor_column = len(line)-1
                        else:
                                self.cursor_column = 0

  `def set_view(self, view)`

    Define the view attached to the text

    Expand source code

        def set_view(self, view):
                """ Define the view attached to the text """
                self.view = view

  `def top(self, keys=None)`

    Move the cursor to the first line of text

    Expand source code

        def top(self, keys=None):
                """ Move the cursor to the first line of text """
                self.goto(1)

  `def treat_char(self, keys=None)`

    Treat character entered

    Expand source code

        def treat_char(self, keys=None):
                """ Treat character entered """
                char = ord(keys[0][0])
                if self.read_only is False:
                        if char >= 0x20 and char != 0x7F:
                                self.add_char(keys)
                                return True
                return False

  `def treat_key(self, keys=None)`

    Treat keys

    Expand source code

        def treat_key(self, keys=None):
                """ Treat keys """
                key_callback = None
                if self.treat_char(keys) is False:
                        # Move in the edit field
                        if keys[0] in self.MOVE_KEYS:
                                if   keys[0] in self.cfg.key_left:            key_callback = self.arrow_left
                                elif keys[0] in self.cfg.key_right:           key_callback = self.arrow_right
                                elif keys[0] in self.cfg.key_up  :            key_callback = self.arrow_up
                                elif keys[0] in self.cfg.key_down:            key_callback = self.arrow_down
                                elif keys[0] in self.cfg.key_home:            key_callback = self.home
                                elif keys[0] in self.cfg.key_end:             key_callback = self.end
                                elif keys[0] in self.cfg.key_page_up:         key_callback = self.page_up
                                elif keys[0] in self.cfg.key_page_down:       key_callback = self.page_down
                                elif keys[0] in self.cfg.key_top:             key_callback = self.top
                                elif keys[0] in self.cfg.key_bottom:          key_callback = self.bottom
                                elif keys[0] in self.cfg.key_next_word:       key_callback = self.next_word
                                elif keys[0] in self.cfg.key_previous_word:   key_callback = self.previous_word
                        elif keys[0] in self.SELECT_KEYS:
                                # Selection the edit field
                                if   keys[0] in self.cfg.key_sel_right:    key_callback = self.select_right
                                elif keys[0] in self.cfg.key_sel_left:     key_callback = self.select_left
                                elif keys[0] in self.cfg.key_sel_up:       key_callback = self.select_up
                                elif keys[0] in self.cfg.key_sel_down:     key_callback = self.select_down
                                elif keys[0] in self.cfg.key_sel_home:     key_callback = self.select_home
                                elif keys[0] in self.cfg.key_sel_end:      key_callback = self.select_end
                                elif keys[0] in self.cfg.key_sel_top:      key_callback = self.select_top
                                elif keys[0] in self.cfg.key_sel_bottom:   key_callback = self.select_bottom
                                elif keys[0] in self.cfg.key_sel_page_up:  key_callback = self.select_page_up
                                elif keys[0] in self.cfg.key_sel_page_down:key_callback = self.select_page_down
                                elif keys[0] in self.cfg.key_sel_all:      key_callback = self.select_all
                                elif keys[0] in self.cfg.key_sel_next_word:key_callback = self.select_next_word
                                elif keys[0] in self.cfg.key_sel_prev_word:key_callback = self.select_previous_word

                        # If the edit is not in read only
                        elif self.read_only is False:
                                if keys[0] in self.NOT_READ_ONLY_KEYS:
                                        # Modification in the edit field
                                        if   keys[0] in self.cfg.key_copy:        key_callback = self.copy
                                        elif keys[0] in self.cfg.key_cut:         key_callback = self.cut
                                        elif keys[0] in self.cfg.key_paste:       key_callback = self.paste
                                        elif keys[0] in self.cfg.key_indent:      key_callback = self.indent
                                        elif keys[0] in self.cfg.key_unindent:    key_callback = self.unindent
                                        elif keys[0] in self.cfg.key_change_case: key_callback = self.change_case
                                        elif keys[0] in self.cfg.key_comment:     key_callback = self.comment
                                        elif keys[0] in self.cfg.key_backspace:   key_callback = self.backspace
                                        elif keys[0] in self.cfg.key_delete:      key_callback = self.delete
                                        elif keys[0] in self.cfg.key_new_line:    key_callback = self.new_line
                                        elif keys[0] in self.cfg.key_del_line:    key_callback = self.delete_line
                        # else: self.add_char(keys)
                        if key_callback is not None:
                                key_callback(keys)
                        else:
                                if len(keys[0]) > 3:
                                        # If camflasher mouse selection
                                        if keys[0][0:2] == "\x1B[" and keys[0][-1] in ["x","y"]:
                                                try:
                                                        pos = keys[0][2:-1]
                                                        line, column = pos.split(";")
                                                        self.begin_line, self.begin_column = self.view.get_position()
                                                        if keys[0][-1] == "x":
                                                                self.goto(int(line)+self.begin_line,int(column)+self.begin_column, True)
                                                                self.open_selection()
                                                                self.close_selection()
                                                        else:
                                                                self.goto(int(line)+self.begin_line,int(column)+self.begin_column, False)
                                                                self.close_selection()
                                                except Exception as err:
                                                        pass

  `def unindent(self, keys=None)`

    Manage the unindentation key

    Expand source code

        def unindent(self, keys=None):
                """ Manage the unindentation key """
                # If nothing selected
                if self.selection_start is None:
                        self.backspace()
                else:
                        self.modified = True

                        # Unindent selection
                        selection_start, selection_end = self.get_selection()
                        sel_column_start, sel_line_start, dummy = selection_start
                        sel_column_end,   sel_line_end,   dummy = selection_end

                        # If the selection is only alone line
                        if sel_line_start == sel_line_end:
                                self.hide_selection()
                        else:
                                # If the last line selected is at beginning of line
                                if sel_column_end == 0:
                                        # This line must not be indented
                                        sel_line_end -= 1

                                # Remove indentation
                                for line in range(sel_line_start, sel_line_end+1):
                                        if len(self.lines[line]) >= 1:
                                                if self.lines[line][0] == "\t" or self.lines[line][0] == " ":
                                                        self.lines[line] = self.lines[line][1:]

                                # Move the start selection to the start of first selected line
                                self.selection_start = [0,sel_line_start, 0]

                                # If the last line selected is not at beginning of line
                                if sel_column_end > 0:
                                        # Get the length of last selected line
                                        len_line_end =  len(self.lines[sel_line_end])

                                        # If the end of selection is not on the last line
                                        if sel_line_end < len(self.lines)-1:
                                                len_line_end -= 1

                                        # Move the end of selection at the end of line selected
                                        self.selection_end   = [len_line_end, sel_line_end, self.get_tab_cursor(sel_line_end,len_line_end)]
                                else:
                                        # Move the end of selection at the start of the last line selected
                                        self.selection_end  = [0, sel_line_end+1, 0]
                        self.view.set_refresh_selection()

`class View (cfg, view_height, view_top, extension=None)`

  Class which manage the view of the edit field

  Constructor

  Expand source code

      class View:
              """ Class which manage the view of the edit field """
              def __init__(self, cfg, view_height, view_top, extension=None):
                      """ Constructor """
                      self.line     = 0
                      self.column   = 0
                      self.cfg      = cfg
                      if view_height is None:
                              self.height   = 20
                      else:
                              self.height             = view_height

                      self.colorize = self.colorize_none
                      if is_enough_memory():
                              if extension is not None:
                                      if extension.lower() == ".py":
                                              # pylint: disable=import-error
                                              try:
                                                      try:
                                                              from editor_py import Colorizer
                                                      except:
                                                              from shell.editor_py import Colorizer
                                                      self.colorizer = Colorizer()
                                                      self.colorize = self.colorize_syntax
                                              except:
                                                      pass

                      self.width                  = 80
                      self.top                    = view_top
                      self.is_refresh_all         = True
                      self.is_refresh_line        = False
                      self.is_refresh_line_before = False
                      self.is_refresh_line_after  = False
                      self.refresh_part           = None
                      self.text                   = None
                      self.tab_cursor_column      = 0
                      self.sel_line_start         = None
                      self.sel_line_end           = None
                      self.screen_height          = 1
                      self.screen_width           = 1
                      if tools.filesystem.ismicropython():
                              self.write = self.write_byte
                      else:
                              self.write = self.write_string

              def write_byte(self, data):
                      """ Write data to stdout in byte (for micropython only) """
                      sys.stdout.write(data)

              def write_string(self, data):
                      """ Write data to stdout """
                      sys.stdout.write(tools.strings.tostrings(data))

              def flush(self):
                      """ Flush text to stdout """
                      try:
                              sys.stdout.flush()
                      except:
                              pass

              def set_text(self, text):
                      """ Set the text object """
                      self.text = text

              def get_screen_position(self):
                      """ Get the screen position of cursor """
                      return (self.text.get_cursor_line() - self.line + self.top, self.tab_cursor_column - self.column)

              def reset(self):
                      """ Reset VT100 """
                      self.write(b"\x1B"+b"c")
                      self.flush()

              def reset_scroll_region(self):
                      """ Reset VT100 scroll region """
                      if self.screen_height > 0:
                              self.set_scrolling_region(0, self.screen_height-1)

              def set_scrolling_region(self, top_line, bottom_line):
                      """ Define VT100 scroll region """
                      if top_line < bottom_line:
                              self.write(b"\x1B[%d;%dr"%(top_line+1,bottom_line+1))

              def scroll_up(self):
                      """ Scroll to up """
                      self.set_scrolling_region(self.top, self.height+1)
                      self.write(b"\x1B[1S")

              def scroll_down(self):
                      """ Scroll to down """
                      self.set_scrolling_region(self.top, self.height+1)
                      self.write(b"\x1B[1T")

              def scroll_part_up(self):
                      """ Scroll the upper part """
                      line, column = self.get_screen_position()
                      if line < self.height:
                              self.set_scrolling_region(line +1, self.height+1)
                              self.write(b"\x1B[1S")
                      elif line == self.height:
                              self.write(b"\x1B[%d;1f\x1B[K"%(self.height + 1 + self.top))

              def scroll_part_down(self):
                      """ Scroll the lower part """
                      line, column = self.get_screen_position()
                      if line < self.height:
                              self.set_scrolling_region(line+1, self.height+1)
                              self.write(b"\x1B[1T")
                      else:
                              self.is_refresh_line_after = True

              def move(self):
                      """ Move the view """
                      self.tab_cursor_column = self.text.get_tab_cursor(self.text.get_cursor_line())
                      # Move view port
                      if self.tab_cursor_column < self.column:
                              self.is_refresh_all = True
                              if self.tab_cursor_column > HORIZONTAL_MOVE:
                                      self.column = self.tab_cursor_column-HORIZONTAL_MOVE
                              else:
                                      self.column = 0
                      elif self.tab_cursor_column >= self.column + self.width:
                              self.column = self.tab_cursor_column-self.width+HORIZONTAL_MOVE
                              self.is_refresh_all = True
                      if self.text.get_cursor_line() < self.line:
                              delta = self.line - self.text.get_cursor_line()
                              self.line = self.text.get_cursor_line()
                              if self.line < 0:
                                      self.line = 0
                              if delta <= 1:
                                      self.scroll_down()
                                      self.is_refresh_line = True
                              else:
                                      self.is_refresh_all = True
                      elif self.text.get_cursor_line() > self.line + self.height:
                              delta =  self.text.get_cursor_line() - self.line - self.height
                              self.line = self.text.get_cursor_line()-self.height
                              if delta <= 1:
                                      self.scroll_up()
                                      self.is_refresh_line = True
                              else:
                                      self.is_refresh_all = True

              def set_refresh_line(self):
                      """ Indicates that the line must be refreshed """
                      self.is_refresh_line = True

              def set_refresh_after(self):
                      """ Indicates that all lines after the current line must be refreshed """
                      self.is_refresh_line = True
                      self.is_refresh_line_after = True

              def set_refresh_before(self):
                      """ Indicates that all lines before the current line must be refreshed """
                      self.is_refresh_line = True
                      self.is_refresh_line_before = True

              def set_refresh_all(self):
                      """ Indicates that all lines must be refreshed """
                      self.is_refresh_all = True

              def set_refresh_bottom(self, cursor):
                      """ Refresh from the cursor to the end of screen """
                      self.refresh_part = [cursor, cursor+self.height+self.height]

              def show_line(self, current_line, screen_line, selection_start, selection_end, quick=False):
                      """ Show one line """
                      if not quick:
                              clear_line = b"\x1B[%d;1f\x1B[K"%(screen_line+1)
                      else:
                              clear_line = b""
                      count_line = self.text.get_count_lines()
                      if current_line < count_line and current_line >= 0:
                              # If the line selected
                              if selection_start is not None:
                                      _, sel_line_start, sel_column_start = selection_start
                                      _, sel_line_end,   sel_column_end   = selection_end
                                      # If the line is completly selected
                                      if current_line > sel_line_start and current_line < sel_line_end:
                                              part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, True)
                                              if (len(part_line) == 0):
                                                      part_line = b" "
                                              self.write(clear_line+SELECTION_START+part_line+SELECTION_END)
                                      # If the line is partially selected
                                      else:
                                              part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, False)
                                              # part_line = line[self.column:self.column+self.width]
                                              if len(part_line) > 0:
                                                      # If the end of selection is outside the visible part
                                                      if sel_column_end - self.column < 0:
                                                              sel_column_end = 0
                                                      else:
                                                              sel_column_end -= self.column

                                                      # If the start of selection is outside the visible part
                                                      if sel_column_start - self.column < 0:
                                                              sel_column_start = 0
                                                      else:
                                                              sel_column_start -= self.column

                                                      # If the selection is on alone line
                                                      if current_line == sel_line_end and current_line == sel_line_start:
                                                              self.write(clear_line)
                                                              self.colorize(part_line[:sel_column_start].encode("utf8"))
                                                              self.write(SELECTION_START+part_line[sel_column_start:sel_column_end].encode("utf8")+SELECTION_END)
                                                              self.colorize(part_line[sel_column_end:].encode("utf8"))
                                                      # If current line is on the last selection line
                                                      elif current_line == sel_line_end:
                                                              self.write(clear_line+SELECTION_START+part_line[:sel_column_end].encode("utf8")+SELECTION_END)
                                                              self.colorize(part_line[sel_column_end:].encode("utf8"))
                                                      # If current line is on the first selection line
                                                      elif current_line == sel_line_start:
                                                              self.write(clear_line)
                                                              self.colorize(part_line[:sel_column_start].encode("utf8"))
                                                              self.write(SELECTION_START+part_line[sel_column_start:].encode("utf8")+SELECTION_END)
                                                      # Else the line is not selected
                                                      else:
                                                              self.write(clear_line)
                                                              self.colorize(part_line.encode("utf8"))
                                              else:
                                                      if current_line >= sel_line_start and current_line <= sel_line_end:
                                                              self.write(clear_line+SELECTION_START+b" "+SELECTION_END)
                                                      else:
                                                              self.write(clear_line)
                              else:
                                      part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, True)
                                      self.write(clear_line)
                                      self.colorize(part_line)
                      elif current_line == count_line:
                              self.write(clear_line)

              def colorize_none(self, text):
                      """ No colorization """
                      self.write(text)

              def colorize_syntax(self, text):
                      """ Syntax colorization """
                      self.colorizer.colorize(text, self)

              def refresh_line(self, selection_start, selection_end):
                      """ Refresh line """
                      screen_line,     screen_column = self.get_screen_position()
                      refreshed = False

                      # If the line must be refreshed before the cursor line
                      if self.is_refresh_line_before:
                              self.is_refresh_line_before = False
                              self.show_line(self.text.get_cursor_line()-1, screen_line-1, selection_start, selection_end)
                              refreshed = True
                      # If the line must be refreshed after the cursor line
                      if self.is_refresh_line_after:
                              self.is_refresh_line_after = False
                              self.show_line(self.text.get_cursor_line()+1, screen_line+1, selection_start, selection_end)
                              offset = self.height - screen_line
                              self.show_line(self.text.get_cursor_line()+offset+1, screen_line+offset+1, selection_start, selection_end)
                              refreshed = True
                      # If only the cursor line must be refresh
                      if self.is_refresh_line:
                              self.is_refresh_line = False
                              self.show_line(self.text.get_cursor_line(), screen_line, selection_start, selection_end)
                              refreshed = True

                      # If no refresh detected and a selection started
                      if selection_start is not None and refreshed is False:
                              # Refresh the selection
                              self.show_line(self.text.get_cursor_line(), screen_line, selection_start, selection_end)

              def refresh(self):
                      """ Refresh view """
                      selection_start, selection_end = self.text.get_selection()
                      if self.refresh_part is not None:
                              self.refresh_content(selection_start, selection_end, self.refresh_part)
                              self.refresh_part = None
                      # Refresh all required
                      if self.is_refresh_all:
                              self.refresh_content(selection_start, selection_end, True)
                              self.is_refresh_all  = False
                              self.is_refresh_line = False
                      else:
                              # If no selection activated
                              if selection_start is None:
                                      # Refresh the current line
                                      self.refresh_line(selection_start, selection_end)
                              else:
                                      # Refresh the selection
                                      self.refresh_content(selection_start, selection_end, False)
                      self.move_cursor()
                      self.flush()

              def refresh_content(self, selection_start, selection_end, all_):
                      """ Refresh content """
                      # If selection present
                      if selection_start is not None:
                              # Get the selection
                              dummy, sel_line_start, sel_column_start = selection_start
                              dummy, sel_line_end,   sel_column_end   = selection_end
                              line_start = sel_line_start
                              line_end   = sel_line_end
                              # The aim of this part is to limit the refresh area
                              # If the precedent display show a selection
                              if self.sel_line_end is not None and self.sel_line_start is not None:
                                      # If the start and end of selection is on the sames lines
                                      if self.sel_line_end == sel_line_end and self.sel_line_start == sel_line_start:
                                              line_start = line_end = self.text.get_cursor_line()
                                      else:
                                              # If the end of selection is after the precedent display
                                              if self.sel_line_end > sel_line_end:
                                                      line_end = self.sel_line_end
                                              # If the end of selection is on the same line than the precedent display
                                              elif self.sel_line_end == sel_line_end:
                                                      # If the start of selection is before the precedent display
                                                      if self.sel_line_start < sel_line_start:
                                                              line_end = sel_line_start
                                                      else:
                                                              line_end = self.sel_line_start
                                              # If the start of selection is before the precedent display
                                              if self.sel_line_start < sel_line_start:
                                                      line_start = self.sel_line_start
                                              # If the start of selection is on the same line than the precedent display
                                              elif self.sel_line_start == sel_line_start:
                                                      # If the end of selection is after the precedent display
                                                      if self.sel_line_end > sel_line_end:
                                                              line_start = sel_line_end
                                                      else:
                                                              line_start = self.sel_line_end
                      else:
                              line_start = 0
                              line_end = self.line + self.height
                      current_line = self.line
                      screen_line = self.top
                      if type(all_) == type([]):
                              line_start, line_end = all_
                              all_ = False
                      count_line = self.text.get_count_lines()
                      max_line = self.line + self.height
                      if all_:
                              # Erase the rest of the screen with empty line (used when the text is shorter than the screen)
                              self.move_cursor(screen_line, 0)
                              self.write(b"\x1B[J")
                              # Refresh all lines visible
                              while current_line < count_line and current_line <= max_line:
                                      self.show_line(current_line, screen_line, selection_start, selection_end, True)
                                      screen_line  += 1
                                      current_line += 1
                                      if (current_line < count_line and current_line <= max_line):
                                              self.write(b"\n\r")
                      else:
                              # Refresh all lines visible
                              while current_line < count_line and current_line <= max_line:
                                      # If the line is in selection or all must be refreshed
                                      if line_start <= current_line <= line_end or all_:
                                              self.show_line(current_line, screen_line, selection_start, selection_end)
                                      screen_line  += 1
                                      current_line += 1
                              if line_end > max_line:
                                      self.cls_end_screen()

                      # If selection present
                      if selection_start is not None:
                              # Save current selection
                              _, self.sel_line_start, _ = selection_start
                              _, self.sel_line_end,   _ = selection_end

              def hide_selection(self):
                      """ Hide the selection """
                      selection_start, selection_end = self.text.get_selection()
                      if selection_start is not None:
                              self.set_refresh_selection()
                              self.sel_line_start = None
                              self.sel_line_end   = None

              def set_refresh_selection(self):
                      """ Indicates that the selection must be refreshed """
                      selection_start, selection_end = self.text.get_selection()
                      if selection_start is not None:
                              line_start = selection_start[1]
                              if self.sel_line_start is not None:
                                      if self.sel_line_start < line_start:
                                              line_start = self.sel_line_start
                              line_end = selection_end[1]
                              if self.sel_line_end is not None:
                                      if self.sel_line_end > line_end:
                                              line_end = self.sel_line_end
                              self.refresh_part = [line_start, line_end]

              def move_cursor(self, screen_line=None, screen_column=None):
                      """ Move the cursor in the view """
                      if screen_line is None and screen_column is None:
                              screen_line, screen_column = self.get_screen_position()
                      self.write(b"\x1B[%d;%df"%(screen_line+1, screen_column+1))

              def get_screen_size(self):
                      """ Get the screen size """
                      height, width = tools.terminal.get_screen_size(True)
                      self.screen_height = height
                      self.screen_width = width
                      self.height = height-self.top-1
                      self.width  = width
                      self.move_cursor()

              def cls(self):
                      """ clear the screen """
                      self.write(b"\x1B[2J")
                      self.move_cursor(0,0)

              def cls_end_screen(self):
                      """ clear the end of screen """
                      self.write(b"\x1B[0J")

              def get_position(self):
                      """ Get the position of view """
                      return self.line, self.column

  ### Methods

  `def cls(self)`

    clear the screen

    Expand source code

        def cls(self):
                """ clear the screen """
                self.write(b"\x1B[2J")
                self.move_cursor(0,0)

  `def cls_end_screen(self)`

    clear the end of screen

    Expand source code

        def cls_end_screen(self):
                """ clear the end of screen """
                self.write(b"\x1B[0J")

  `def colorize_none(self, text)`

    No colorization

    Expand source code

        def colorize_none(self, text):
                """ No colorization """
                self.write(text)

  `def colorize_syntax(self, text)`

    Syntax colorization

    Expand source code

        def colorize_syntax(self, text):
                """ Syntax colorization """
                self.colorizer.colorize(text, self)

  `def flush(self)`

    Flush text to stdout

    Expand source code

        def flush(self):
                """ Flush text to stdout """
                try:
                        sys.stdout.flush()
                except:
                        pass

  `def get_position(self)`

    Get the position of view

    Expand source code

        def get_position(self):
                """ Get the position of view """
                return self.line, self.column

  `def get_screen_position(self)`

    Get the screen position of cursor

    Expand source code

        def get_screen_position(self):
                """ Get the screen position of cursor """
                return (self.text.get_cursor_line() - self.line + self.top, self.tab_cursor_column - self.column)

  `def get_screen_size(self)`

    Get the screen size

    Expand source code

        def get_screen_size(self):
                """ Get the screen size """
                height, width = tools.terminal.get_screen_size(True)
                self.screen_height = height
                self.screen_width = width
                self.height = height-self.top-1
                self.width  = width
                self.move_cursor()

  `def hide_selection(self)`

    Hide the selection

    Expand source code

        def hide_selection(self):
                """ Hide the selection """
                selection_start, selection_end = self.text.get_selection()
                if selection_start is not None:
                        self.set_refresh_selection()
                        self.sel_line_start = None
                        self.sel_line_end   = None

  `def move(self)`

    Move the view

    Expand source code

        def move(self):
                """ Move the view """
                self.tab_cursor_column = self.text.get_tab_cursor(self.text.get_cursor_line())
                # Move view port
                if self.tab_cursor_column < self.column:
                        self.is_refresh_all = True
                        if self.tab_cursor_column > HORIZONTAL_MOVE:
                                self.column = self.tab_cursor_column-HORIZONTAL_MOVE
                        else:
                                self.column = 0
                elif self.tab_cursor_column >= self.column + self.width:
                        self.column = self.tab_cursor_column-self.width+HORIZONTAL_MOVE
                        self.is_refresh_all = True
                if self.text.get_cursor_line() < self.line:
                        delta = self.line - self.text.get_cursor_line()
                        self.line = self.text.get_cursor_line()
                        if self.line < 0:
                                self.line = 0
                        if delta <= 1:
                                self.scroll_down()
                                self.is_refresh_line = True
                        else:
                                self.is_refresh_all = True
                elif self.text.get_cursor_line() > self.line + self.height:
                        delta =  self.text.get_cursor_line() - self.line - self.height
                        self.line = self.text.get_cursor_line()-self.height
                        if delta <= 1:
                                self.scroll_up()
                                self.is_refresh_line = True
                        else:
                                self.is_refresh_all = True

  `def move_cursor(self, screen_line=None, screen_column=None)`

    Move the cursor in the view

    Expand source code

        def move_cursor(self, screen_line=None, screen_column=None):
                """ Move the cursor in the view """
                if screen_line is None and screen_column is None:
                        screen_line, screen_column = self.get_screen_position()
                self.write(b"\x1B[%d;%df"%(screen_line+1, screen_column+1))

  `def refresh(self)`

    Refresh view

    Expand source code

        def refresh(self):
                """ Refresh view """
                selection_start, selection_end = self.text.get_selection()
                if self.refresh_part is not None:
                        self.refresh_content(selection_start, selection_end, self.refresh_part)
                        self.refresh_part = None
                # Refresh all required
                if self.is_refresh_all:
                        self.refresh_content(selection_start, selection_end, True)
                        self.is_refresh_all  = False
                        self.is_refresh_line = False
                else:
                        # If no selection activated
                        if selection_start is None:
                                # Refresh the current line
                                self.refresh_line(selection_start, selection_end)
                        else:
                                # Refresh the selection
                                self.refresh_content(selection_start, selection_end, False)
                self.move_cursor()
                self.flush()

  `def refresh_content(self, selection_start, selection_end, all_)`

    Refresh content

    Expand source code

        def refresh_content(self, selection_start, selection_end, all_):
                """ Refresh content """
                # If selection present
                if selection_start is not None:
                        # Get the selection
                        dummy, sel_line_start, sel_column_start = selection_start
                        dummy, sel_line_end,   sel_column_end   = selection_end
                        line_start = sel_line_start
                        line_end   = sel_line_end
                        # The aim of this part is to limit the refresh area
                        # If the precedent display show a selection
                        if self.sel_line_end is not None and self.sel_line_start is not None:
                                # If the start and end of selection is on the sames lines
                                if self.sel_line_end == sel_line_end and self.sel_line_start == sel_line_start:
                                        line_start = line_end = self.text.get_cursor_line()
                                else:
                                        # If the end of selection is after the precedent display
                                        if self.sel_line_end > sel_line_end:
                                                line_end = self.sel_line_end
                                        # If the end of selection is on the same line than the precedent display
                                        elif self.sel_line_end == sel_line_end:
                                                # If the start of selection is before the precedent display
                                                if self.sel_line_start < sel_line_start:
                                                        line_end = sel_line_start
                                                else:
                                                        line_end = self.sel_line_start
                                        # If the start of selection is before the precedent display
                                        if self.sel_line_start < sel_line_start:
                                                line_start = self.sel_line_start
                                        # If the start of selection is on the same line than the precedent display
                                        elif self.sel_line_start == sel_line_start:
                                                # If the end of selection is after the precedent display
                                                if self.sel_line_end > sel_line_end:
                                                        line_start = sel_line_end
                                                else:
                                                        line_start = self.sel_line_end
                else:
                        line_start = 0
                        line_end = self.line + self.height
                current_line = self.line
                screen_line = self.top
                if type(all_) == type([]):
                        line_start, line_end = all_
                        all_ = False
                count_line = self.text.get_count_lines()
                max_line = self.line + self.height
                if all_:
                        # Erase the rest of the screen with empty line (used when the text is shorter than the screen)
                        self.move_cursor(screen_line, 0)
                        self.write(b"\x1B[J")
                        # Refresh all lines visible
                        while current_line < count_line and current_line <= max_line:
                                self.show_line(current_line, screen_line, selection_start, selection_end, True)
                                screen_line  += 1
                                current_line += 1
                                if (current_line < count_line and current_line <= max_line):
                                        self.write(b"\n\r")
                else:
                        # Refresh all lines visible
                        while current_line < count_line and current_line <= max_line:
                                # If the line is in selection or all must be refreshed
                                if line_start <= current_line <= line_end or all_:
                                        self.show_line(current_line, screen_line, selection_start, selection_end)
                                screen_line  += 1
                                current_line += 1
                        if line_end > max_line:
                                self.cls_end_screen()

                # If selection present
                if selection_start is not None:
                        # Save current selection
                        _, self.sel_line_start, _ = selection_start
                        _, self.sel_line_end,   _ = selection_end

  `def refresh_line(self, selection_start, selection_end)`

    Refresh line

    Expand source code

        def refresh_line(self, selection_start, selection_end):
                """ Refresh line """
                screen_line,     screen_column = self.get_screen_position()
                refreshed = False

                # If the line must be refreshed before the cursor line
                if self.is_refresh_line_before:
                        self.is_refresh_line_before = False
                        self.show_line(self.text.get_cursor_line()-1, screen_line-1, selection_start, selection_end)
                        refreshed = True
                # If the line must be refreshed after the cursor line
                if self.is_refresh_line_after:
                        self.is_refresh_line_after = False
                        self.show_line(self.text.get_cursor_line()+1, screen_line+1, selection_start, selection_end)
                        offset = self.height - screen_line
                        self.show_line(self.text.get_cursor_line()+offset+1, screen_line+offset+1, selection_start, selection_end)
                        refreshed = True
                # If only the cursor line must be refresh
                if self.is_refresh_line:
                        self.is_refresh_line = False
                        self.show_line(self.text.get_cursor_line(), screen_line, selection_start, selection_end)
                        refreshed = True

                # If no refresh detected and a selection started
                if selection_start is not None and refreshed is False:
                        # Refresh the selection
                        self.show_line(self.text.get_cursor_line(), screen_line, selection_start, selection_end)

  `def reset(self)`

    Reset VT100

    Expand source code

        def reset(self):
                """ Reset VT100 """
                self.write(b"\x1B"+b"c")
                self.flush()

  `def reset_scroll_region(self)`

    Reset VT100 scroll region

    Expand source code

        def reset_scroll_region(self):
                """ Reset VT100 scroll region """
                if self.screen_height > 0:
                        self.set_scrolling_region(0, self.screen_height-1)

  `def scroll_down(self)`

    Scroll to down

    Expand source code

        def scroll_down(self):
                """ Scroll to down """
                self.set_scrolling_region(self.top, self.height+1)
                self.write(b"\x1B[1T")

  `def scroll_part_down(self)`

    Scroll the lower part

    Expand source code

        def scroll_part_down(self):
                """ Scroll the lower part """
                line, column = self.get_screen_position()
                if line < self.height:
                        self.set_scrolling_region(line+1, self.height+1)
                        self.write(b"\x1B[1T")
                else:
                        self.is_refresh_line_after = True

  `def scroll_part_up(self)`

    Scroll the upper part

    Expand source code

        def scroll_part_up(self):
                """ Scroll the upper part """
                line, column = self.get_screen_position()
                if line < self.height:
                        self.set_scrolling_region(line +1, self.height+1)
                        self.write(b"\x1B[1S")
                elif line == self.height:
                        self.write(b"\x1B[%d;1f\x1B[K"%(self.height + 1 + self.top))

  `def scroll_up(self)`

    Scroll to up

    Expand source code

        def scroll_up(self):
                """ Scroll to up """
                self.set_scrolling_region(self.top, self.height+1)
                self.write(b"\x1B[1S")

  `def set_refresh_after(self)`

    Indicates that all lines after the current line must be refreshed

    Expand source code

        def set_refresh_after(self):
                """ Indicates that all lines after the current line must be refreshed """
                self.is_refresh_line = True
                self.is_refresh_line_after = True

  `def set_refresh_all(self)`

    Indicates that all lines must be refreshed

    Expand source code

        def set_refresh_all(self):
                """ Indicates that all lines must be refreshed """
                self.is_refresh_all = True

  `def set_refresh_before(self)`

    Indicates that all lines before the current line must be refreshed

    Expand source code

        def set_refresh_before(self):
                """ Indicates that all lines before the current line must be refreshed """
                self.is_refresh_line = True
                self.is_refresh_line_before = True

  `def set_refresh_bottom(self, cursor)`

    Refresh from the cursor to the end of screen

    Expand source code

        def set_refresh_bottom(self, cursor):
                """ Refresh from the cursor to the end of screen """
                self.refresh_part = [cursor, cursor+self.height+self.height]

  `def set_refresh_line(self)`

    Indicates that the line must be refreshed

    Expand source code

        def set_refresh_line(self):
                """ Indicates that the line must be refreshed """
                self.is_refresh_line = True

  `def set_refresh_selection(self)`

    Indicates that the selection must be refreshed

    Expand source code

        def set_refresh_selection(self):
                """ Indicates that the selection must be refreshed """
                selection_start, selection_end = self.text.get_selection()
                if selection_start is not None:
                        line_start = selection_start[1]
                        if self.sel_line_start is not None:
                                if self.sel_line_start < line_start:
                                        line_start = self.sel_line_start
                        line_end = selection_end[1]
                        if self.sel_line_end is not None:
                                if self.sel_line_end > line_end:
                                        line_end = self.sel_line_end
                        self.refresh_part = [line_start, line_end]

  `def set_scrolling_region(self, top_line, bottom_line)`

    Define VT100 scroll region

    Expand source code

        def set_scrolling_region(self, top_line, bottom_line):
                """ Define VT100 scroll region """
                if top_line < bottom_line:
                        self.write(b"\x1B[%d;%dr"%(top_line+1,bottom_line+1))

  `def set_text(self, text)`

    Set the text object

    Expand source code

        def set_text(self, text):
                """ Set the text object """
                self.text = text

  `def show_line(self, current_line, screen_line, selection_start, selection_end, quick=False)`

    Show one line

    Expand source code

        def show_line(self, current_line, screen_line, selection_start, selection_end, quick=False):
                """ Show one line """
                if not quick:
                        clear_line = b"\x1B[%d;1f\x1B[K"%(screen_line+1)
                else:
                        clear_line = b""
                count_line = self.text.get_count_lines()
                if current_line < count_line and current_line >= 0:
                        # If the line selected
                        if selection_start is not None:
                                _, sel_line_start, sel_column_start = selection_start
                                _, sel_line_end,   sel_column_end   = selection_end
                                # If the line is completly selected
                                if current_line > sel_line_start and current_line < sel_line_end:
                                        part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, True)
                                        if (len(part_line) == 0):
                                                part_line = b" "
                                        self.write(clear_line+SELECTION_START+part_line+SELECTION_END)
                                # If the line is partially selected
                                else:
                                        part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, False)
                                        # part_line = line[self.column:self.column+self.width]
                                        if len(part_line) > 0:
                                                # If the end of selection is outside the visible part
                                                if sel_column_end - self.column < 0:
                                                        sel_column_end = 0
                                                else:
                                                        sel_column_end -= self.column

                                                # If the start of selection is outside the visible part
                                                if sel_column_start - self.column < 0:
                                                        sel_column_start = 0
                                                else:
                                                        sel_column_start -= self.column

                                                # If the selection is on alone line
                                                if current_line == sel_line_end and current_line == sel_line_start:
                                                        self.write(clear_line)
                                                        self.colorize(part_line[:sel_column_start].encode("utf8"))
                                                        self.write(SELECTION_START+part_line[sel_column_start:sel_column_end].encode("utf8")+SELECTION_END)
                                                        self.colorize(part_line[sel_column_end:].encode("utf8"))
                                                # If current line is on the last selection line
                                                elif current_line == sel_line_end:
                                                        self.write(clear_line+SELECTION_START+part_line[:sel_column_end].encode("utf8")+SELECTION_END)
                                                        self.colorize(part_line[sel_column_end:].encode("utf8"))
                                                # If current line is on the first selection line
                                                elif current_line == sel_line_start:
                                                        self.write(clear_line)
                                                        self.colorize(part_line[:sel_column_start].encode("utf8"))
                                                        self.write(SELECTION_START+part_line[sel_column_start:].encode("utf8")+SELECTION_END)
                                                # Else the line is not selected
                                                else:
                                                        self.write(clear_line)
                                                        self.colorize(part_line.encode("utf8"))
                                        else:
                                                if current_line >= sel_line_start and current_line <= sel_line_end:
                                                        self.write(clear_line+SELECTION_START+b" "+SELECTION_END)
                                                else:
                                                        self.write(clear_line)
                        else:
                                part_line = self.text.get_tab_line(current_line, self.column, self.column+self.width, True)
                                self.write(clear_line)
                                self.colorize(part_line)
                elif current_line == count_line:
                        self.write(clear_line)

  `def write_byte(self, data)`

    Write data to stdout in byte (for micropython only)

    Expand source code

        def write_byte(self, data):
                """ Write data to stdout in byte (for micropython only) """
                sys.stdout.write(data)

  `def write_string(self, data)`

    Write data to stdout

    Expand source code

        def write_string(self, data):
                """ Write data to stdout """
                sys.stdout.write(tools.strings.tostrings(data))