Beispiel #1
0
    def __init__(self, name, stylename, close='', closestyle=None):
        super().__init__(name, stylename)

        self.close = self.closestyle = ''
        if close:
            self.close = '(?P<CLOSE>{})|'.format(close)
            self.closestyle = closestyle

        self.CSS_TOP = doc_re.compile(
            self.close + r'(?P<COMMENT>/\*)|(?P<STRING>[\'"])|({)')
        self.END_COMMENT = doc_re.compile(r'\*/')
        self.PROPERTY_TOP = doc_re.compile(
            self.close + r'(?P<COMMENT>/\*)|(?P<STRING>[\'"])|:|}')
        self.PROPERTY_VALUE = doc_re.compile(
            self.close + r'(?P<COMMENT>/\*)|(?P<STRING>[\'"])|;|}')
Beispiel #2
0
    def on_start(self, tokenizer, doc, pos, match):
        f = match.start()
        c, t = self.get_close(doc, match.end(), ']')
        if t != doc.endpos():
            next = doc.gettext(t, t + 1)
            if next == ':':
                yield (f, t + 1, self.text)
                return t + 1, None, False
            elif next == ' ':
                t += 1
        yield (f, t, self.text)

        m = doc_re.compile(r'\s*(\[|\()').match(doc, t)
        if not m:
            return t, None, False

        c = m.group(1)
        if c == '[':
            c, end = self.get_close(doc, m.end(), ']')
            yield t, end, self.link
        else:
            c, end = self.get_close(doc, m.end(), '")')
            if c == '"':
                c, end = self.get_close(doc, end, '"')
                c, end = self.get_close(doc, end, ')')
            yield t, end, self.url

        return end, None, False
Beispiel #3
0
 def get_indent_range(self, pos):
     tol = self.document.gettol(pos)
     regex = doc_re.compile(self.RE_WHITESPACE)
     m = regex.match(self.document, tol)
     if m:
         return m.span()
     else:
         return (tol, tol)
Beispiel #4
0
    def __init__(self, name, stylename, start, end, escape=None,
                 capture_end=True):

        super().__init__(name, stylename)

        self.start = start
        self.escape = escape
        if escape:
            end = '({}.)|({})'.format(doc_re.escape(escape), end)
        self.end = doc_re.compile(end, doc_re.X + doc_re.M + doc_re.S)
        self._capture_end = capture_end
Beispiel #5
0
    def get_close(self, doc, pos, chars):
        r = doc_re.compile(r'\\.|' + '|'.join('\\' + c for c in chars))
        while True:
            m = r.search(doc, pos)
            if not m:
                return '', doc.endpos()

            h = m.group()
            if h[0] == '\\':
                pos = m.end()
            else:
                return h, m.end()
Beispiel #6
0
    def _py_end_comment(self, m):
        close = r'(\\\n)|(\n)'
        reobj = doc_re.compile(close)

        pos = m.end()
        while True:
            m = reobj.search(self.document, pos)
            if not m:
                return self.document.endpos()
            if m.lastindex == 2:
                return m.end()
            else:
                pos = m.end()
Beispiel #7
0
    def _py_end_string(self, m):
        close = r'\\.|(?P<CLOSE>%s)' % m.group()
        reobj = doc_re.compile(close)

        pos = m.end()
        while True:
            m = reobj.search(self.document, pos)
            if not m:
                return self.document.endpos()
            if m.lastgroup == 'CLOSE':
                return m.end()
            else:
                pos = m.end()
Beispiel #8
0
 def parse_string(self, doc, pos, qmark):
     reobj = doc_re.compile(r'(\\.)|({0})'.format(qmark), doc_re.DOTALL)
     cur = pos
     while True:
         m = reobj.search(doc, cur)
         if not m:
             yield pos, doc.endpos(), self.span_string
             return doc.endpos()
         if m.group(1):
             cur = m.end()
         else:
             yield pos, m.start(), self.span_string
             yield m.start(), m.end(), self.span_string_end
             return m.end()
Beispiel #9
0
    def prepare(self):
        self.register_styles([(self._stylename, "styleid_span")])

        end = self._end
        if self.tokenizer.terminates:
            end = '(?P<TERMINATES>{})|({})'.format(self.tokenizer.terminates,
                                                   end)

        if self._terminate_tokens:
            end = '(?P<TERMINATES2>{})|({})'.format(self._terminate_tokens,
                                                    end)

        if self._escape:
            end = '(?P<ESCAPE>{}.)|({})'.format(doc_re.escape(self._escape),
                                                end)
        self._re_end = doc_re.compile(end, doc_re.X + doc_re.M + doc_re.S)
Beispiel #10
0
    def prepare(self):
        self.register_styles([(self._stylename, "styleid_span")])

        end = self._end
        if self.tokenizer.terminates:
            end = '(?P<TERMINATES>{})|({})'.format(
                self.tokenizer.terminates, end)

        if self._terminate_tokens:
            end = '(?P<TERMINATES2>{})|({})'.format(
                self._terminate_tokens, end)

        if self._escape:
            end = '(?P<ESCAPE>{}.)|({})'.format(
                doc_re.escape(self._escape), end)
        self._re_end = doc_re.compile(end, doc_re.X + doc_re.M + doc_re.S)
Beispiel #11
0
    def prepare(self):
        self.groupnames = {}
        starts = []
        for i, token in enumerate(self._token_list):
            start = token.re_start()
            if start:
                name = 'G{}'.format(i)
                self.groupnames[name] = token
                starts.append(r'(?P<{}>{})'.format(name, start))

        if self.terminates:
            starts.insert(0, r'(?P<TERMINATE>{})'.format(self.terminates))
            self.groupnames['TERMINATE'] = None

        if starts:
            self.re_starts = doc_re.compile('|'.join(starts),
                                            doc_re.M + doc_re.X)
Beispiel #12
0
    def prepare(self):
        self.groupnames = {}
        starts = []
        for i, token in enumerate(self._token_list):
            start = token.re_start()
            if start:
                name = 'G{}'.format(i)
                self.groupnames[name] = token
                starts.append(r'(?P<{}>{})'.format(name, start))

        if self.terminates:
            starts.insert(
                0, r'(?P<TERMINATE>{})'.format(self.terminates))
            self.groupnames['TERMINATE'] = None

        if starts:
            self.re_starts = doc_re.compile(
                '|'.join(starts),
                doc_re.M + doc_re.X)
Beispiel #13
0
class INIMode(defaultmode.DefaultMode):
    MODENAME = 'INI'
    LINE_COMMENT = ';'

    RE_SECTION = doc_re.compile(r'^\[(?P<SECTION>.+)\]$', doc_re.M)

    tokenizer = make_tokenizer()

    def init_keybind(self):
        super().init_keybind()
        self.register_keys(self.keybind, [ini_keys])

    def init_themes(self):
        super().init_themes()
        self.themes.append(INIThemes)

    def init_menu(self):
        super().init_menu()
        self.menu['CODE'] = copy.deepcopy(INIMENU)

    def get_sections(self):
        pos = 0
        while True:
            m = self.RE_SECTION.search(self.document, pos)
            if not m:
                break

            pos = m.end()
            name = m.group('SECTION')

            yield self.HeaderInfo('namespace', None, name, name, None,
                                  m.start())

    @commandid('toc.showlist')
    @norec
    @norerun
    def show_toclist(self, wnd):
        from kaa.ui.toclist import toclistmode
        sections = list(self.get_sections())
        toclistmode.show_toclist(wnd, sections)
Beispiel #14
0
    def _py_close_parenthesis(self, m):
        close = '|(?P<CLOSE>\\%s)' % self.PARENSIS_PAIR[m.group()]
        reobj = doc_re.compile(self.TOKENIZE_SEARCH_CLOSE + close, doc_re.X)
        pos = m.end()
        while True:
            m = reobj.search(self.document, pos)
            if not m:
                return self.document.endpos()

            g = m.lastgroup
            if g == 'CLOSE':
                return m.end()
            elif g == 'BACKSLASH':
                pos = m.end()
            elif g == 'PARENTHESIS':
                pos = self._py_close_parenthesis(m)
            elif g == 'STRING':
                pos = self._py_end_string(m)
            elif g == 'COMMENT':
                pos = self._py_end_comment(m)
            else:
                assert 0
Beispiel #15
0
    def prepare(self, highlighter):
        self.highlighter = highlighter
        self.nulltoken = self.register_tokenid(None)

        starts = []
        for i, token in enumerate(self.tokens):
            if not token:
                continue
            token.prepare(self)
            start = token.re_start()
            if start:
                name = 'G{}'.format(i)
                self.groupnames[name] = token
                starts.append(r'(?P<{}>{})'.format(name, start))

        if self.terminates:
            starts.insert(
                0, r'(?P<{}>{})'.format('TERMINATE', self.terminates))
            self.groupnames['TERMINATE'] = None

        if starts:
            self.re_starts = doc_re.compile(
                '|'.join(starts),
                doc_re.M + doc_re.X)
Beispiel #16
0
 def _is_comment_line(self, wnd, pos):
     reobj = doc_re.compile(r'[ \t]*({})'.format(
         doc_re.escape(wnd.document.mode.LINE_COMMENT)))
     return reobj.match(wnd.document, pos)
Beispiel #17
0
        doc.mode.stack_upper = True
    else:
        doc.mode.stack_upper = False

    dlg = kaa.app.show_dialog(doc)

    filterlistdoc = wordcompletemode.WordCompleteList.build()
    filterlistdoc.mode.SEP = ' '
    list = dlg.add_doc('dlg_filterlist', 0, filterlistdoc)

    doc.mode.start(wnd, list, f, t, s, pwl, sugest)

    return dlg


RE_WORD = doc_re.compile(r"\b[a-zA-Z][a-z']{2,}\b", doc_re.A)


def iter_words(wnd, pos):
    d = enchant.DictWithPWL("en_US", kaa.app.config.spellchecker_pwl)

    while True:
        posto = wnd.document.marks['spellchecker'][1]
        m = RE_WORD.search(wnd.document, pos, posto)
        if not m:
            return

        f, t = m.span()
        s = wnd.document.gettext(f, t)
        if d.check(s):
            pos = t
Beispiel #18
0
        for name, value in iter_b_attr(b[m.end():]):
            if name == b'encoding':
                return str(value.strip().strip(), 'utf-8')


HTMLThemes = {
    'basic': [
        Style('html-decl', 'Red', 'default'),
        Style('html-tag', 'Magenta', 'default'),
        Style('html-attrname', 'green', 'default'),
        Style('html-attrvalue', 'yellow', 'default'),
    ],
}


RE_ATTRNAME = doc_re.compile(
    r'>|(?P<ATTRNAME>[-._:a-zA-Z0-9]+)(?P<EQUAL>\s*=)?\s*')
RE_ATTRVALUE = doc_re.compile(r'\s*(?P<ATTRVALUE>({}))'.format(
    '|'.join(['[-._:a-zA-Z0-9]+', '(?P<Q1>"[^"]*")', "(?P<Q2>'[^']*')"])))


def iter_attrs(doc, pos):
    while True:
        m = RE_ATTRNAME.search(doc, pos)
        if not m:
            return pos
        if m.group() == '>':
            return m.start()

        attrname = m.group('ATTRNAME')
        pos = m.end()
Beispiel #19
0
class FilenameIndexMode(defaultmode.DefaultMode):
    # todo: rename key/command names
    DOCUMENT_MODE = False
    USE_UNDO = False
    HIGHLIGHT_CURSOR_ROW = True

    FILENAMEINDEX_KEY_BINDS = [
        filenameindex_keys,
    ]

    encoding = None
    newline = None

    def init_themes(self):
        super().init_themes()
        self.themes.append(FilenameIndexThemes)

    def init_keybind(self):
        super().init_keybind()
        self.register_keys(self.keybind, self.FILENAMEINDEX_KEY_BINDS)

    def _locate_doc(self, wnd, doc, lineno):
        wnd.show_doc(doc)

        pos = doc.get_lineno_pos(lineno)
        tol = doc.gettol(pos)
        wnd.cursor.setpos(wnd.cursor.adjust_nextpos(wnd.cursor.pos, tol))
        wnd.activate()

    @commandid('filenameindex.showmatch')
    @norec
    @norerun
    def show_hit(self, wnd):
        pos = wnd.cursor.pos
        tol = self.document.gettol(pos)
        eol, line = self.document.getline(tol)

        try:
            filename, lineno, line = line.split(':', 2)
        except ValueError:
            return

        if not filename:
            return

        try:
            lineno = int(lineno)
        except ValueError:
            lineno = 1

        filename = os.path.abspath(os.path.expanduser(filename))
        doc = document.Document.find_filename(filename)
        if not doc:
            enc = self.encoding
            if not enc:
                enc = kaa.app.config.DEFAULT_ENCODING
            if enc == 'japanese':
                enc = _enc_japanese(filename)

            newline = self.newline
            if not newline:
                newline = kaa.app.config.DEFAULT_NEWLINE

            doc = kaa.app.storage.openfile(
                filename, encoding=enc, newline=newline)

        buddy = wnd.splitter.get_buddy()
        if not buddy:
            buddy = wnd.splitter.split(vert=False, doc=doc)
            self._locate_doc(buddy.wnd, doc, lineno)
        else:
            if buddy.wnd and buddy.wnd.document is doc:
                self._locate_doc(buddy.wnd, doc, lineno)
                return

            def callback(canceled):
                if not canceled:
                    buddy.show_doc(doc)
                    self._locate_doc(buddy.wnd, doc, lineno)
            kaa.app.app_commands.save_splitterdocs(wnd, buddy, callback)

    RE_FILENAME = doc_re.compile(
        r'(?P<FILENAME>^[^:\n]+)\:(?P<LINENO>\d+)\:.*$',
        doc_re.M | doc_re.X)

    def is_match(self, pos):
        m = self.RE_FILENAME.match(self.document, pos)
        return m

    def on_global_prev(self, wnd):
        if kaa.app.focus in self.document.wnds:
            if self.is_match(self.document.gettol(wnd.cursor.pos)):
                self.show_hit(kaa.app.focus)
                return True

        pos = wnd.cursor.pos

        while True:
            eol = self.document.gettol(pos)
            if eol:
                tol = self.document.gettol(eol - 1)
            else:
                eol = self.document.endpos()
                tol = self.document.gettol(eol)

            if self.is_match(tol):
                wnd.cursor.setpos(tol)
                self.show_hit(wnd)
                return True

            if tol == 0:
                wnd.cursor.setpos(tol)
                self.document.wnds[0].activate()
                return True

            pos = tol

    def on_global_next(self, wnd):
        if kaa.app.focus in self.document.wnds:
            if self.is_match(self.document.gettol(wnd.cursor.pos)):
                self.show_hit(kaa.app.focus)
                return True

        pos = wnd.cursor.pos
        while True:
            tol = self.document.geteol(pos)
            m = self.is_match(tol)
            if m:
                wnd.cursor.setpos(tol)
                self.show_hit(wnd)
                return True

            if self.document.geteol(tol) == self.document.endpos():
                wnd.cursor.setpos(0)
                self.document.wnds[0].activate()
                return True

            pos = tol
Beispiel #20
0
class RstMode(defaultmode.DefaultMode):
    MODENAME = 'Rst'
    tokenizer = make_tokenizer()

    def init_keybind(self):
        super().init_keybind()
        self.register_keys(self.keybind, [rst_keys])

    def init_menu(self):
        super().init_menu()
        self.menu['CODE'] = copy.deepcopy(RSTMENU)

    def init_themes(self):
        super().init_themes()
        self.themes.append(RstThemes)

    HEADER1 = r'''^(?P<H>[{}])(?P=H)+\n
                  (?P<TITLE>.+)\n
                  (?P=H)+$'''.format(RST_HEADERS)

    HEADER2 = r'''^(?P<TITLE2>.+)\n
                (?P<H2>[{}])(?P=H2)+$'''.format(RST_HEADERS)

    RE_HEADER = doc_re.compile('|'.join([HEADER1, HEADER2]),
                               doc_re.X | doc_re.M)

    def get_headers(self):
        levels = {}
        stack = []
        pos = 0
        while True:
            m = self.RE_HEADER.search(self.document, pos)
            if not m:
                break

            pos = m.end()
            name = m.group('TITLE') or m.group('TITLE2')
            name = name.strip()

            bar = (m.group('H'), m.group('H2'))
            if bar not in levels:
                level = len(levels)
                levels[bar] = level
            else:
                level = levels[bar]

            if not stack:
                header = self.HeaderInfo('namespace', None, name, name, None,
                                         m.start())
                yield header
                stack.append((level, header))
                continue

            for i, (parent_level, info) in enumerate(stack):
                if level <= parent_level:
                    del stack[i:]
                    break

            if stack:
                parent = stack[-1][1]
            else:
                parent = None

            header = self.HeaderInfo('namespace', parent, name, name, None,
                                     m.start())
            yield header
            stack.append((level, header))

    @commandid('toc.showlist')
    @norec
    @norerun
    def show_toclist(self, wnd):
        from kaa.ui.toclist import toclistmode
        headers = list(self.get_headers())
        toclistmode.show_toclist(wnd, headers)
Beispiel #21
0
class MarkdownMode(defaultmode.DefaultMode):
    MODENAME = 'Markdown'
    tokenizer = make_tokenizer()

    def init_keybind(self):
        super().init_keybind()
        self.register_keys(self.keybind, [md_keys])

    def init_menu(self):
        super().init_menu()
        self.menu['CODE'] = copy.deepcopy(MDMENU)

    def init_themes(self):
        super().init_themes()
        self.themes.append(MarkdownThemes)

    def init_tokenizer(self):
        self.tokenizer = MarkdownTokenizer

    HEADER1 = r'^(?P<TITLE>.+)\n(?P<H1>[=-])(?P=H1)+$'
    HEADER2 = r'^(?P<H2>\#{1,6})(?P<TITLE2>.+)$'

    RE_HEADER = doc_re.compile(
        '|'.join([HEADER1, HEADER2]), doc_re.X | doc_re.M)

    def get_headers(self):
        stack = []
        pos = 0
        while True:
            m = self.RE_HEADER.search(self.document, pos)
            if not m:
                break

            pos = m.end()
            name = m.group('TITLE') or m.group('TITLE2')
            name = name.strip()

            bar = m.group('H1')
            if bar:
                if bar == '=':
                    level = 0
                else:
                    level = 1
            else:
                level = len(m.group('H2')) - 1

            if not stack:
                header = self.HeaderInfo('namespace', None, name, name,
                                         None, m.start())
                yield header
                stack.append((level, header))
                continue

            for i, (parent_level, info) in enumerate(stack):
                if level <= parent_level:
                    del stack[i:]
                    break

            if stack:
                parent = stack[-1][1]
            else:
                parent = None

            header = self.HeaderInfo(
                'namespace', parent, name,
                name, None, m.start())
            yield header
            stack.append((level, header))

    @commandid('toc.showlist')
    @norec
    @norerun
    def show_toclist(self, wnd):
        from kaa.ui.toclist import toclistmode
        headers = list(self.get_headers())
        toclistmode.show_toclist(wnd, headers)
Beispiel #22
0
 def delete_ws(self, wnd, pos, update_cursor=False):
     m = doc_re.compile(r'[ \t]+').match(self.document, pos)
     if m:
         f, t = m.span()
         self.delete_string(wnd, f, t, update_cursor=update_cursor)
         return t - f
Beispiel #23
0
class ModeBase:
    (UNDO_INSERT,
     UNDO_REPLACE,
     UNDO_DELETE) = range(3)

    CLOSE_ON_DEL_WINDOW = True

    SCREEN_NOWRAP = False
    NO_WRAPINDENT = False
    SHOW_LINENO = False
    SHOW_BLANK_LINE = False
    USE_UNDO = True
    DOCUMENT_MODE = False
    HIGHLIGHT_CURSOR_ROW = False
    SHOW_CURSOR = True
    DELAY_STR = True

    tab_width = 8
    indent_width = 4
    indent_tab = False
    auto_indent = True

    closed = False
    theme = None
    _check_fileupdate = 0
    _last_autoindent = None

    tokenizer = DefaultTokenizer

    DEFAULT_MENU_MESSAGE = ''

    @classmethod
    def update_fileinfo(cls, fileinfo, document=None):
        pass

    def __init__(self):
        self.commands = {}
        self.is_availables = {}

        self.keybind = keyboard.KeyBind()
        self.keybind_vi_commandmode = keyboard.KeyBind()
        self.keybind_vi_visualmode = keyboard.KeyBind()
        self.keybind_vi_visuallinewisemode = keyboard.KeyBind()
        self.init_keybind()
        self.init_addon_keys()

        self.init_commands()
        self.init_addon_commands()

        self.init_menu()

        self.themes = []
        self.init_themes()
        self.init_addon_themes()

        self.tokenizers = []
        self.init_tokenizers()

        self._highlight_done = 0
        self._highlight_iter = None

        self.stylemap = {}
        self.stylenamemap = {}

        self.setup()
        self.setup_addon()

        self._build_theme()
        kaa.app.translate_theme(self.theme)

    def setup_addon(self):
        names = []
        for cls in self.__class__.__mro__:
            modulename = cls.__module__
            name = '.'.join((modulename, cls.__name__))
            names.insert(0, name)

        for name in names:
            for f in addon.get_addon(name, 'setup'):
                f(self)

    def setup(self):
        pass

    def close(self):
        self.document = None
        self.closed = True

        self.keybind.clear()

        self.theme = None

        self.commands.clear()
        self.commands = None

        self.is_availables = None
        self.tokenizers = None
        self.tokenizer = None
        self.stylemap = None
        self._highlight_done = 0
        self._highlight_iter = None

    def _build_style_map(self):
        self.stylemap[0] = self.theme.get_style('default')
        for styleid, token in self.tokenizer.styleid_map.items():
            if token:
                stylename = token.get_stylename(styleid)
                style = self.theme.get_style(stylename)

                self.stylemap[styleid] = style
                self.stylenamemap[style.name] = styleid
            else:
                style = self.theme.get_style('default')
                self.stylemap[styleid] = style
                self.stylenamemap[style.name] = styleid

    def on_set_document(self, document):
        self.document = document
        self.document.styles.setints(0, len(self.document.styles), 0)

        self._build_style_map()
        self.document.use_undo(self.USE_UNDO)

    def on_document_updated(self, pos, inslen, dellen):
        self._last_autoindent = None
        if pos <= self._highlight_done:
            self._highlight_done = pos
            self._highlight_iter = None

    def on_file_saved(self, fileinfo):
        pass

    def on_add_window(self, wnd):
        self.editmode_insert(wnd)

    def on_del_window(self, wnd):
        if len(self.document.wnds) == 1:
            if self.DOCUMENT_MODE and self.document.fileinfo:
                self.document.fileinfo.save_screenloc(wnd.screen.pos)

    def on_focus(self, wnd):
        kaa.app.show_cursor(self.is_cursor_visible())

        # relocate cursor
        wnd.cursor.setpos(wnd.cursor.pos)

    def register_keys(self, keybind, keys):
        for d in keys:
            keybind.add_keybind(d)

    def init_keybind(self):
        pass

    def get_class_name(self):
        modulename = self.__class__.__module__
        name = '.'.join((modulename, self.__class__.__name__))
        return name

    def add_keybinds(self, editmode='input', keys=None):
        if not keys:
            return
        if editmode == 'input':
            self.keybind.add_keybind(keys)
        elif editmode == 'command':
            self.keybind_vi_commandmode.add_keybind(keys)
        elif editmode == 'visual':
            self.keybind_vi_visualmode.add_keybind(keys)
        elif editmode == 'visualline':
            self.keybind_vi_visuallinewisemode.add_keybind(keys)

    def init_addon_keys(self):
        name = self.get_class_name()
        keydef = addon.get_addon(name, 'keybind')
        for edit_mode, keys in keydef:
            if edit_mode == 'input':
                self.keybind.add_keybind(keys)
            elif edit_mode == 'command':
                self.keybind_vi_commandmode.add_keybind(keys)
            elif edit_mode == 'visual':
                self.keybind_vi_visualmode.add_keybind(keys)
            elif edit_mode == 'visualline':
                self.keybind_vi_visuallinewisemode.add_keybind(keys)

    def init_commands(self):
        from kaa.commands import (editorcommand, editmodecommand, vicommand)

        # todo: re-design command injection
        self.register_commandobj(editorcommand.CursorCommands())
        self.register_commandobj(editorcommand.EditCommands())
        self.register_commandobj(editorcommand.CodeCommands())
        self.register_commandobj(editorcommand.SelectionCommands())
        self.register_commandobj(editorcommand.SearchCommands())
        self.register_commandobj(editmodecommand.EditModeCommands())
        self.register_commandobj(vicommand.ViCommands())

        for name in dir(self):
            attr = getattr(self, name)
            if hasattr(attr, 'COMMAND_ID') and callable(attr):
                self.commands[getattr(attr, 'COMMAND_ID')] = attr

    def init_addon_commands(self):
        name = self.get_class_name()
        commands = addon.get_addon(name, 'command')

        for f in commands:
            self.commands[f.COMMAND_ID] = f

    def init_menu(self):
        pass

    def init_themes(self):
        pass

    def init_addon_themes(self):
        name = self.get_class_name()
        themes = addon.get_addon(name, 'style')
        self.themes.extend(themes)

    def add_theme(self, theme):
        self.themes.append(theme)

    def init_tokenizers(self):
        pass

    def add_command(self, f, command_id=None):
        if command_id is not None:
            f.COMMAND_ID = command_id

        self.commands[f.COMMAND_ID] = f

    def register_commandobj(self, cmds):
        self.commands.update(cmds.get_commands())
        self.is_availables.update(cmds.get_commands_is_enable())

    def get_command(self, commandid):
        ret = kaa.app.get_command(commandid)
        if ret:
            return ret
        is_available = self.is_availables.get(commandid, None)
        cmd = self.commands.get(commandid, None)
        return (is_available, cmd)

    def editmode_insert(self, wnd):
        wnd.set_editmode(editmode.EditMode())

    def editmode_replace(self, wnd):
        wnd.set_editmode(editmode.ReplaceMode())

    def editmode_visual(self, wnd):
        wnd.set_editmode(editmode.VisualMode())

    def editmode_visual_linewise(self, wnd):
        wnd.set_editmode(editmode.VisualLinewiseMode())

    def editmode_command(self, wnd):
        wnd.set_editmode(editmode.CommandMode())

    def get_styleid(self, stylename):
        if stylename == 'default':
            return 0

        styleid = self.stylenamemap.get(stylename, None)
        if styleid is not None:
            return styleid

        if not self.stylemap:
            ret = 1
        else:
            ret = max(self.stylemap.keys()) + 1

        style = self.theme.get_style(stylename)
        self.stylemap[ret] = style
        self.stylenamemap[stylename] = ret
        return ret

    def select_theme(self, theme_name, themes):
        theme = themes.get(theme_name, None)
        if theme is None:
            theme = themes[kaa.app.DEFAULT_THEME]
        return theme

    def _build_theme(self):
        theme_name = kaa.app.get_current_theme()
        self.theme = theme.Theme([])
        for t in self.themes:
            self.theme.update(self.select_theme(theme_name, t))
        self.theme.finish_update()

    def get_style(self, tokenid):
        ret = self.stylemap.get(tokenid, None)
        if ret is None:
            ret = self.theme.get_style('default')
        return ret

    def get_menu(self, itemname):
        return self.menu.get(itemname, None)

    def is_cursor_visible(self):
        return self.SHOW_CURSOR

    def on_keypressed(self, wnd, event, s, commands, candidate):
        return s, commands, candidate

    def filter_string(self, wnd, s):
        return s

    def on_edited(self, wnd):
        pass

    def on_str(self, wnd, s, overwrite=False):
        self.put_string(wnd, s, overwrite=overwrite)
        wnd.screen.selection.clear()

        if kaa.app.macro.is_recording():
            kaa.app.macro.record_string(s, overwrite)

        # run highlighter a bit to display changes immediately.
        self.run_tokenizer(batch=50)

    def on_commands(self, wnd, commandids, n_repeat=1):
        wnd.set_command_repeat(n_repeat)
        try:
            if callable(commandids):
                commandids(wnd)
                return

            current_lastcommands = []
            for commandid in commandids:
                is_available, command = self.get_command(commandid)
                if not command:
                    msg = 'command {!r} is not registered.'.format(commandid)
                    kaa.app.messagebar.set_message(msg)
                    kaa.log.error(msg)
                    return

                command(wnd)
                if kaa.app.macro.is_recording():
                    kaa.app.macro.record(n_repeat, command)

                if not getattr(command, 'NORERUN', False):
                    current_lastcommands.append(commandid)

            if current_lastcommands:
                kaa.app.lastcommands = (n_repeat, current_lastcommands)

        finally:
            wnd.set_command_repeat(1)

        def f():
            import kaa.cui.frame
            frames = kaa.cui.frame.MainFrame.childframes
            frame_panels = [f._panel for f in frames]

            import curses.panel
            panels = [curses.panel.top_panel()]
            while True:
                panel = panels[-1].below()
                if panel:
                    panels.append(panel)
                else:
                    break

            for panel in panels:
                if panel in frame_panels:
                    if panel is not frame_panels[0]:
                        _trace('Invalid frame order: %s%s' %
                               (self, commandids,))
                break

        f()

    def on_esc_pressed(self, wnd, event):
        pass

    def on_cursor_located(self, wnd, pos, y, x):
        pass

    def insert_string(self, wnd, pos, s, update_cursor=True):
        """Insert string"""

        cur_pos = wnd.cursor.pos

        wnd.document.insert(pos, s)

        if update_cursor:
            wnd.cursor.setpos(wnd.cursor.pos + len(s))
            wnd.cursor.savecol()

        if wnd.document.undo:
            wnd.document.undo.add(self.UNDO_INSERT, pos, s,
                                  cur_pos, wnd.cursor.pos)

        self.on_edited(wnd)

    def replace_string(self, wnd, pos, posto, s, update_cursor=True):
        """Replace string"""

        cur_pos = wnd.cursor.pos

        deled = wnd.document.gettext(pos, posto)
        wnd.document.replace(pos, posto, s)

        if update_cursor:
            wnd.cursor.setpos(pos + len(s))
            wnd.cursor.savecol()

        if wnd.document.undo:
            wnd.document.undo.add(self.UNDO_REPLACE, pos, posto, s,
                                  deled, cur_pos, wnd.cursor.pos)

        self.on_edited(wnd)

    def delete_string(self, wnd, pos, posto, update_cursor=True):
        """Delete string"""

        cur_pos = wnd.cursor.pos

        if pos < posto:
            deled = wnd.document.gettext(pos, posto)
            wnd.document.delete(pos, posto)

            if update_cursor:
                wnd.cursor.setpos(pos)
                wnd.cursor.savecol()

            if wnd.document.undo:
                wnd.document.undo.add(self.UNDO_DELETE, pos, posto, deled,
                                      cur_pos, wnd.cursor.pos)
            self.on_edited(wnd)

    def delete_ws(self, wnd, pos, update_cursor=False):
        m = doc_re.compile(r'[ \t]+').match(self.document, pos)
        if m:
            f, t = m.span()
            self.delete_string(wnd, f, t, update_cursor=update_cursor)
            return t - f

    def replace_rect(self, wnd, repto):
        with wnd.document.undo_group():
            (posfrom, posto, colfrom, colto
             ) = wnd.screen.selection.get_rect_range()

            for s in repto:
                if posto <= posfrom:
                    break

                sel = wnd.screen.selection.get_col_string(
                    posfrom, colfrom, colto)
                if sel:
                    f, t, org = sel
                    if org.endswith('\n'):
                        t = max(f, t - 1)
                    self.replace_string(wnd, f, t, s)
                    posto += (len(s) - (t - f))
                posfrom = wnd.document.geteol(posfrom)

    def put_string(self, wnd, s, overwrite=False):
        s = self.filter_string(wnd, s)

        if wnd.screen.selection.is_selected():
            if wnd.screen.selection.is_rectangular():
                if '\n' not in s:
                    self.replace_rect(wnd, itertools.repeat(s))
                else:
                    self.replace_rect(wnd, s.split('\n'))

            else:
                sel = wnd.screen.selection.get_selrange()
                f, t = sel
                self.replace_string(wnd, f, t, s)
        else:
            if not overwrite:
                self.insert_string(wnd, wnd.cursor.pos, s)
            else:
                eol = wnd.document.get_line_to(wnd.cursor.pos)
                posto = min(wnd.cursor.pos + len(s), eol)
                self.replace_string(wnd, wnd.cursor.pos, posto, s)

    def update_charattr(self, wnd):
        if wnd.charattrs:
            wnd.charattrs.clear()
            wnd.screen.style_updated()
            return True

    def on_idle(self):
        if self.closed:
            return

        ret = self.run_tokenizer()
        return ret

    def get_line_overlays(self):
        return {}

    def _get_highlight_range(self):
        return (0, self.document.endpos())

    HIGHLIGHTBATCH = 300

    def run_tokenizer(self, batch=HIGHLIGHTBATCH):
        range_start, range_end = self._get_highlight_range()

        if self._highlight_done < range_start:
            self._highlight_done = range_start

        if not self._highlight_iter:
            f = max(range_start, self._highlight_done - 1)
            self._highlight_iter = syntax_highlight.begin_tokenizer(
                self.document, self.tokenizer, f)

        updatefrom = self.document.endpos()
        updateto = range_start
        updated = False
        finished = False

        for n, (f, t, style) in enumerate(self._highlight_iter):
            f = max(range_start, f)
            t = min(range_end, t)
            if f < t:
                self.document.styles.setints(f, t, style)

            updated = True
            updatefrom = min(f, updatefrom)
            updateto = max(t, updateto)

            if batch and (n > batch):
                break

            if t >= range_end:
                finished = True
                break
        else:
            finished = True

        if self.document.endpos() == 0 or updated and (updatefrom != updateto):
            self.document.style_updated(updatefrom, updateto)
            self._highlight_done = updateto

        # returns False if finished to terminate idle loop.
        return not finished

    def _split_chars(self, begin, end):
        """split characters by character category."""

        s = self.document.gettext(begin, end)

        def get_category(c):
            return unicodedata.category(c)[0]

        for key, chars in itertools.groupby(s, get_category):
            chars = ''.join(chars)
            end = begin + len(chars)
            yield begin, end, chars, key
            begin = end

    RE_WORDCHAR = r"(?P<L_WORDCHAR>[a-zA-Z0-9_]+)"
    RE_WHITESPACE = r"(?P<Z_WHITESPACE>[\t ]+)"
    RE_LF = r"(?P<_LF>\n)"
    RE_HIRAGANA = r"(?P<L_HIRAGANA>[\u3040-\u309f\u30fc]+)"
    RE_KATAKANA = r"(?P<L_KATAKANA>[\u30a0-\u30ff\u30fc]+)"

    RE_SPLITWORD = doc_re.compile('|'.join((
        RE_WORDCHAR, RE_WHITESPACE, RE_LF, RE_HIRAGANA, RE_KATAKANA)))

# http://unicodebook.readthedocs.org/en/latest/unicode.html
# Unicode 6.0 has 7 character categories, and each category has subcategories:
#
# Letter (L): lowercase (Ll), modifier (Lm), titlecase (Lt), uppercase (Lu), other (Lo)
# Mark (M): spacing combining (Mc), enclosing (Me), non-spacing (Mn)
# Number (N): decimal digit (Nd), letter (Nl), other (No)
# Punctuation (P): connector (Pc), dash (Pd), initial quote (Pi), final quote (Pf), open (Ps), close (Pe), other (Po)
# Symbol (S): currency (Sc), modifier (Sk), math (Sm), other (So)
# Separator (Z): line (Zl), paragraph (Zp), space (Zs)
# Other (C): control (Cc), format (Cf), not assigned (Cn), private use
# (Co), surrogate (Cs)

    def split_word(self, begin, all=False):
        """yield word in the document until line ends"""

        for m in self.RE_SPLITWORD.finditer(self.document, pos=begin):
            # split unmatched characters by character category.
            f, t = m.span()
            if f != begin:
                yield from self._split_chars(begin, f)
            begin = t

            yield (f, t, m.group(), m.lastgroup)

            # finish if we reach '\n'
            if not all and m.lastgroup == '_LF':
                return

        yield from self._split_chars(begin, self.document.endpos())

    def get_word_at(self, pos):
        tol = self.document.gettol(pos)
        prev = None
        for f, t, s, cg in self.split_word(tol):
            if pos == t:
                if cg not in {'Z_WHITESPACE', '_LF'}:
                    prev = (f, t, cg)
            elif pos < t:
                if cg not in {'Z_WHITESPACE', '_LF'}:
                    return (f, t, cg)
                else:
                    return prev

        return prev

    def get_word_list(self):
        words = collections.OrderedDict()
        for f, t, s, cg in self.split_word(0, all=True):
            if cg in {'Z_WHITESPACE', '_LF'}:
                continue
            if cg[0] in 'LMN':  # Letter, Mark, Number
                if len(s) > 2:
                    words[s] = None
        return list(words.keys())

    def search_next(self, pos, searchinfo):
        regex = searchinfo.get_regex()
        pos = min(max(0, pos), self.document.endpos())
        m = regex.search(self.document, pos)
        return m

    def search_prev(self, pos, searchinfo):
        regex = searchinfo.get_regex()
        last = None
        for m in regex.finditer(self.document, 0):
            span = m.span()
            if span[1] >= pos:
                break
            last = m
        return last

    def get_line_sel(self, wnd):
        doc = wnd.document
        sel = wnd.screen.selection.get_selrange()
        if not sel:
            f = t = wnd.cursor.pos
        else:
            f, t = sel

        tol = doc.gettol(f)

        t_tol = doc.gettol(t)
        if t != t_tol:
            eol = doc.geteol(t)
        else:
            eol = t

        return tol, eol

    def get_indent_range(self, pos):
        tol = self.document.gettol(pos)
        regex = doc_re.compile(self.RE_WHITESPACE)
        m = regex.match(self.document, tol)
        if m:
            return m.span()
        else:
            return (tol, tol)

    def build_indent_str(self, col):
        if self.indent_tab:
            ctab = col // self.tab_width
            cspc = col % self.tab_width
            return '\t' * ctab + ' ' * cspc
        else:
            return ' ' * col

    def get_parent_indent(self, pos):
        tol = self.document.gettol(pos)
        f, t = self.get_indent_range(tol)
        cols = self.calc_cols(f, t)

        while tol:
            eol = tol
            tol = self.document.gettol(eol - 1)
            f, t = self.get_indent_range(tol)
            pcols = self.calc_cols(f, t)
            if pcols < cols:
                return pcols

    def cancel_auto_indent(self, wnd):
        if self._last_autoindent:
            f, t = (min(max(0, p), self.document.endpos())
                    for p in self._last_autoindent)
            self._last_autoindent = None
            if f != t and wnd.cursor.pos == t:
                e = self.document.get_line_break(wnd.cursor.pos)
                if e == t:
                    s = self.document.gettext(f, t)
                    m = re.match(self.RE_WHITESPACE, s)
                    if m and m.group() == s:
                        self.delete_string(
                            wnd, f, t, update_cursor=False)
                        wnd.cursor.setpos(f)
                        return True

    def calc_next_indent(self, pos):
        return None

    MAX_AUTO_INDENT = 30

    def on_auto_indent(self, wnd):
        pos = wnd.cursor.pos
        f, t = self.get_indent_range(pos)
        if pos <= t:
            b = self.document.get_line_break(pos)
            self.insert_string(wnd, f, '\n', update_cursor=False)
            wnd.cursor.setpos(pos + 1)
            wnd.cursor.savecol()

            # if new line don't hava non-ws char,
            # thie line should be subject to cancel-indent.
            if t == b:
                self._last_autoindent = (f + 1, wnd.cursor.pos)
            return

        indentcol = self.calc_next_indent(pos)
        if indentcol is None:
            indent = self.document.gettext(f, min(pos, t))
        else:
            indent = self.build_indent_str(
                min(self.MAX_AUTO_INDENT, indentcol))

        self.insert_string(wnd, pos, '\n' + indent, update_cursor=False)
        self.delete_ws(wnd, pos + len(indent) + 1)
        wnd.cursor.setpos(pos + len(indent) + 1)
        wnd.cursor.savecol()
        self._last_autoindent = (pos + 1, wnd.cursor.pos)

    def calc_cols(self, f, t):
        chars = self.document.gettext(f, t)
        (dispchrs, dispcols, positions,
         intervals) = screen.translate_chars(f, chars, self.tab_width,
                                             kaa.app.config.AMBIGUOUS_WIDTH)

        return sum(dispcols)

    def on_global_next(self, wnd):
        return

    def on_global_prev(self, wnd):
        return
Beispiel #24
0
 def _is_comment_line(self, wnd, pos):
     reobj = doc_re.compile(r'[ \t]*({})'.format(
         doc_re.escape(wnd.document.mode.LINE_COMMENT)))
     return reobj.match(wnd.document, pos)
Beispiel #25
0
    if m:
        for name, value in iter_b_attr(b[m.end():]):
            if name == b'encoding':
                return str(value.strip().strip(), 'utf-8')


HTMLThemes = {
    'basic': [
        Style('html-decl', 'Red', 'default'),
        Style('html-tag', 'Magenta', 'default'),
        Style('html-attrname', 'green', 'default'),
        Style('html-attrvalue', 'yellow', 'default'),
    ],
}

RE_ATTRNAME = doc_re.compile(
    r'>|(?P<ATTRNAME>[-._:a-zA-Z0-9]+)(?P<EQUAL>\s*=)?\s*')
RE_ATTRVALUE = doc_re.compile(r'\s*(?P<ATTRVALUE>({}))'.format('|'.join(
    ['[-._:a-zA-Z0-9]+', '(?P<Q1>"[^"]*")', "(?P<Q2>'[^']*')"])))


def iter_attrs(doc, pos):
    while True:
        m = RE_ATTRNAME.search(doc, pos)
        if not m:
            return pos
        if m.group() == '>':
            return m.start()

        attrname = m.group('ATTRNAME')
        pos = m.end()