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))