Sublime的View.classify如何工作?

时间:2019-04-03 21:58:50

标签: python sublimetext3 sublimetext

我想弄清楚Sublime的View.classify例程在内部如何工作。这是一个可以玩的mcve:

import re
import textwrap

import sublime
import sublime_plugin
from sublime import Region

CLASS_WORD_START = 1
CLASS_WORD_END = 2
CLASS_PUNCTUATION_START = 4
CLASS_PUNCTUATION_END = 8
CLASS_SUB_WORD_START = 16
CLASS_SUB_WORD_END = 32
CLASS_LINE_START = 64
CLASS_LINE_END = 128
CLASS_EMPTY_LINE = 256
CLASS_MIDDLE_WORD = 512
CLASS_WORD_START_WITH_PUNCTUATION = 1024
CLASS_WORD_END_WITH_PUNCTUATION = 2048
CLASS_OPENING_PARENTHESIS = 4096
CLASS_CLOSING_PARENTHESIS = 8192


class PythonVsSublimeCommand(sublime_plugin.TextCommand):

    def class_flags(self, flags):
        res = []
        if flags & CLASS_WORD_START:
            res.append("CLASS_WORD_START")
        if flags & CLASS_WORD_END:
            res.append("CLASS_WORD_END")
        if flags & CLASS_PUNCTUATION_START:
            res.append("CLASS_PUNCTUATION_START")
        if flags & CLASS_PUNCTUATION_END:
            res.append("CLASS_PUNCTUATION_END")
        if flags & CLASS_SUB_WORD_START:
            res.append("CLASS_SUB_WORD_START")
        if flags & CLASS_SUB_WORD_END:
            res.append("CLASS_SUB_WORD_END")
        if flags & CLASS_LINE_START:
            res.append("CLASS_LINE_START")
        if flags & CLASS_LINE_END:
            res.append("CLASS_LINE_END")
        if flags & CLASS_EMPTY_LINE:
            res.append("CLASS_EMPTY_LINE")
        if flags & CLASS_MIDDLE_WORD:
            res.append("CLASS_MIDDLE_WORD")
        if flags & CLASS_WORD_START_WITH_PUNCTUATION:
            res.append("CLASS_WORD_START_WITH_PUNCTUATION")
        if flags & CLASS_WORD_END_WITH_PUNCTUATION:
            res.append("CLASS_WORD_END_WITH_PUNCTUATION")
        if flags & CLASS_OPENING_PARENTHESIS:
            res.append("CLASS_OPENING_PARENTHESIS")
        if flags & CLASS_CLOSING_PARENTHESIS:
            res.append("CLASS_CLOSING_PARENTHESIS")
        return " | ".join(reversed(res))

    def classify(self, point):
        # Classifies point, returning a bitwise OR of zero or more of defined flags
        #
        # Note: This should be taken from word_separator settings
        view = self.view

        ws = r"[-[\]!\"#$%&'()*+,./:;<=>?@\\^`{|}~]"
        res = 0
        a, b = "", ""

        if point > 0:
            a = view.substr(Region(point - 1, point))

        if point < view.size():
            b = view.substr(Region(point, point + 1))

        # Out of range
        if view.size() == 0 or point < 0 or point > view.size():
            return 3520

        # If before and after the point are separators return 0
        p = re.compile(ws)
        if a == b and p.match(a):
            return 0

        # SubWord start & end
        p = re.compile("[A-Z]")
        if p.match(b) and not p.match(a):
            res |= CLASS_SUB_WORD_START
            res |= CLASS_SUB_WORD_END

        if a == "_" and b != "_":
            res |= CLASS_SUB_WORD_START

        if b == "_" and a != "_":
            res |= CLASS_SUB_WORD_END

        # Punc start & end
        p = re.compile(ws)

        # Why ws != ""? See https:#github.com/limetext/rubex/issues/2
        if ((p.match(b) and ws != "") or b == "") and not (p.match(a) and ws != ""):
            res |= CLASS_PUNCTUATION_START
        if ((p.match(a) and ws != "") or a == "") and not (p.match(b) and ws != ""):
            res |= CLASS_PUNCTUATION_END

        # Word start & end
        re1 = re.compile(r"\w")
        re2 = re.compile(r"\s")

        if re1.match(b) and ((p.match(a) and ws != "") or re2.match(a) or a == ""):
            res |= CLASS_WORD_START
        if re1.match(a) and ((p.match(b) and ws != "") or re2.match(b) or b == ""):
            res |= CLASS_WORD_END

        # Line start & end
        if a == "\n" or a == "":
            res |= CLASS_LINE_START
        if b == "\n" or b == "":
            res |= CLASS_LINE_END
            if ws == "":
                res |= CLASS_WORD_END

        # Empty line
        if (a == "\n" and b == "\n") or (a == "" and b == ""):
            res |= CLASS_EMPTY_LINE

        # Middle word
        p = re.compile(r"\w")
        if p.match(a) and p.match(b):
            res |= CLASS_MIDDLE_WORD

        # Word start & end with punc
        p = re.compile(r"\s")
        if (res & CLASS_PUNCTUATION_START != 0) and (p.match(a) or a == ""):
            res |= CLASS_WORD_START_WITH_PUNCTUATION
        if (res & CLASS_PUNCTUATION_END != 0) and (p.match(b) or b == ""):
            res |= CLASS_WORD_END_WITH_PUNCTUATION

        # Openning & closing parentheses
        p = re.compile(r"[[({]")
        if p.match(a) or p.match(b):
            res |= CLASS_OPENING_PARENTHESIS

        # print(res)

        p = re.compile(r"[)\]}]")
        if p.match(a) or p.match(b):
            res |= CLASS_CLOSING_PARENTHESIS

        # TODO: isn't this a bug? what's the relation between
        # ',' and parentheses
        if a == ",":
            res |= CLASS_OPENING_PARENTHESIS
        if b == ",":
            res |= CLASS_CLOSING_PARENTHESIS

        return res

    def run(self, edit, block=False):
        self.view.sel().clear()

        for i in range(self.view.size()):
            c1 = self.classify(i)
            c2 = self.view.classify(i)
            if c1 != c2:
                print("Mismatch position {} - {} => {}/{} vs {}/{}".format(
                    i, self.view.substr(i),
                    c1, self.class_flags(c1),
                    c2, self.class_flags(c2),
                ))
                self.view.sel().add(Region(i, i + 1))

如您所见,我已经尝试创建View.classify的克隆,但是不幸的是,行为与Sublime并不完全相同。如果绑定以上命令并在任何视图上运行它,您将看到两者之间的区别。

问题:如何调整例程以使其与1:1 Sublime的例程匹配?

Ps。我上面发布的例程是Limetext的例程的转译,您可以找到它here

0 个答案:

没有答案