Source code for bettersis.texteditor

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
**BETTERSIS.TEXTEDITOR**: A simple text editor for betterSIS.

Features:

- edit (and save) files
- syntax highlighting

.. note:: This code was inspired by the prompt_toolkit example:
          https://github.com/prompt-toolkit/python-prompt-toolkit/blob/7fdd81597597a7a2f47655238b7f0cb3ea637091/examples/full-screen/text-editor.py  # noqa: E501

"""

__author__ = "Zenaro Stefano"

import os

from prompt_toolkit.application import Application
from prompt_toolkit.application.current import get_app
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.containers import (
    HSplit,
    VSplit,
    Window,
    WindowAlign,
)
from prompt_toolkit.layout.controls import FormattedTextControl
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.lexers import Lexer
from prompt_toolkit.styles import Style
from prompt_toolkit.widgets import (
    TextArea,
)

try:
    from ._version import __version__  # noqa: F401
except ImportError:
    from _version import __version__  # noqa: F401

keywords = [
    ".model",
    ".inputs",
    ".outputs",
    ".names",
    ".exdc",
    ".start_kiss",
    ".end_kiss",
    ".i",
    ".o",
    ".s",
    ".p",
    ".r",
    ".code",
    ".subckt",
    ".search",
    ".latch",
    ".end"
]


[docs]class SisLexer(Lexer): """ Defines a lexer for the text editor. """
[docs] def lex_document(self, document): """ Reads document to parse each line. :param document: document to parse :return function get_line: function that parses lines """ def get_line(lineno): """ Reads and parses the lines one by one. :param int lineno: row number of the line :return list colored_words: list of text (colored keywords) """ colored_words = [] commented = False # loop for each word in the line for word in document.lines[lineno].split(" "): color = "White" # set blue color for keywords if word in keywords: color = "DeepSkyBlue" # set green color for comments if commented: color = "Green" # add each char of the word with the correct color for char in word: if char == "#": # remember that the next chars are commented color = "Green" commented = True colored_words.append((color, char)) colored_words.append((color, " ")) return colored_words return get_line
[docs]class SimpleTextEditor(): """ Opens the ``<t_file>`` file with the a simple CLI text editor. :param str t_file: file to open with the text editor """ def __init__(self, t_file): self.saved_file = False # used to set the toolbar text to "saved to file" if not os.path.isfile(t_file): raise Exception("ERROR: '{}' file doesn't exist".format(t_file)) # define highlighter/completer sis_completer = WordCompleter(keywords, ignore_case=False) # create the text field with the content of the file self.text_field = None with open(t_file, "r") as f: self.text_field = TextArea( lexer=SisLexer(), text=f.read(), scrollbar=False, line_numbers=True, completer=sis_completer, style='bg:#131926' ) # create a layout with the text field and the statusbar body = HSplit( [ self.text_field, VSplit( [ Window( FormattedTextControl(self.get_statusbar_text), style="class:status" ), Window( FormattedTextControl(self.get_statusbar_right_text), style="class:status.right", width=9, align=WindowAlign.RIGHT, ), ], height=1, ) ] ) # Global key bindings. bindings = KeyBindings() # define control hotkeys @bindings.add("c-c") def _(event): "Closes the app (hotkey Ctrl + C)" get_app().exit() @bindings.add("c-s") def _(event): """ Saves the file (hotkey Ctrl + S) and sets self.saved_file to True to show in the toolbar the text "saved to file" """ self.saved_file = True with open(t_file, "w") as f: f.write(self.text_field.text) @bindings.add("c-u") def _(event): """ Undoes the last action (hotkey Ctrl + U) """ self.text_field.buffer.undo() style = Style.from_dict( { "status": "reverse", "shadow": "bg:#440044", } ) # create a layout object layout = Layout(body, focused_element=self.text_field) # create and start the app application = Application( layout=layout, enable_page_navigation_bindings=True, style=style, mouse_support=True, full_screen=True, key_bindings=bindings ) application.run()
[docs] def get_statusbar_text(self): """ Shows control hotkeys. If the user saved the file with Ctrl-S show "Saved to file" instead. :return str: string with the control commands """ if self.saved_file: self.saved_file = False return " | Saved to file |" return " | Ctrl-C: exit | Ctrl-S: save | Ctrl-U: undo |"
[docs] def get_statusbar_right_text(self): """ Shows the cursor position on the bottom right of the screen. :return str: string with cursor position """ return " {}:{} ".format( self.text_field.document.cursor_position_row + 1, self.text_field.document.cursor_position_col + 1, )
if __name__ == "__main__": try: SimpleTextEditor("file.txt") except Exception as e: print("expected error:", e) print("-" * 50)