Как реализовать функцию складывания уровня SublimeText в QScintilla - PullRequest
0 голосов
/ 04 мая 2018

Я пытаюсь реализовать функцию fold_by_level SublimeText3 в компоненте QScintilla, но я не очень хорошо знаю, как это сделать, до сих пор я придумал этот код:

import sys
import re
import math

from PyQt5.Qt import *  # noqa

from PyQt5.Qsci import QsciScintilla
from PyQt5 import Qsci
from PyQt5.Qsci import QsciLexerCPP


class Foo(QsciScintilla):

    def __init__(self, parent=None):
        super().__init__(parent)

        # http://www.scintilla.org/ScintillaDoc.html#Folding
        self.setFolding(QsciScintilla.BoxedTreeFoldStyle)

        # Indentation
        self.setIndentationsUseTabs(False)
        self.setIndentationWidth(4)
        self.setBackspaceUnindents(True)
        self.setIndentationGuides(True)

        # Set the default font
        self.font = QFont()
        self.font.setFamily('Consolas')
        self.font.setFixedPitch(True)
        self.font.setPointSize(10)
        self.setFont(self.font)
        self.setMarginsFont(self.font)

        # Margin 0 is used for line numbers
        fontmetrics = QFontMetrics(self.font)
        self.setMarginsFont(self.font)
        self.setMarginWidth(0, fontmetrics.width("000") + 6)
        self.setMarginLineNumbers(0, True)
        self.setMarginsBackgroundColor(QColor("#cccccc"))

        # Indentation
        self.setIndentationsUseTabs(False)
        self.setIndentationWidth(4)
        self.setBackspaceUnindents(True)

        lexer = QsciLexerCPP()
        lexer.setFoldAtElse(True)
        lexer.setFoldComments(True)
        lexer.setFoldCompact(False)
        lexer.setFoldPreprocessor(True)
        self.setLexer(lexer)

        QShortcut(QKeySequence("Ctrl+K, Ctrl+J"), self,
                  lambda level=-1: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+1"), self,
                  lambda level=1: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+2"), self,
                  lambda level=2: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+3"), self,
                  lambda level=3: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+4"), self,
                  lambda level=4: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+5"), self,
                  lambda level=5: self.fold_by_level(level))

    def fold_by_level(self, lvl):
        if lvl < 0:
            self.foldAll(True)
        else:
            for i in range(self.lines()):
                level = self.SendScintilla(
                    QsciScintilla.SCI_GETFOLDLEVEL, i) & QsciScintilla.SC_FOLDLEVELNUMBERMASK
                level -= 0x400
                print(f"line={i+1}, level={level}")
                if lvl == level:
                    self.foldLine(i)


def main():
    app = QApplication(sys.argv)
    ex = Foo()
    ex.setText("""\
#include <iostream>
using namespace std;

void Function0() {
    cout << "Function0";
}

void Function1() {
    cout << "Function1";
}

void Function2() {
    cout << "Function2";
}

void Function3() {
    cout << "Function3";
}


int main(void) {
    if (1) {
        if (1) {
            if (1) {
                if (1) {
                    int yay;
                }
            }
        }
    }

    if (1) {
        if (1) {
            if (1) {
                if (1) {
                    int yay2;
                }
            }
        }
    }

    return 0;
}\
    """)
    ex.resize(800, 600)
    ex.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Документы, за которыми я следовал: https://www.scintilla.org/ScintillaDoc.html#Folding и http://pyqt.sourceforge.net/Docs/QScintilla2/classQsciScintilla.html.

Как я уже сказал, функция fold_by_level должна вести себя точно так же, как SublimeText, но я не уверен в деталях реализации функции ST. В любом случае, позвольте мне опубликовать несколько скриншотов после тестирования некоторых основных последовательностей на SublimeText, которые могли бы немного прояснить, чего я пытаюсь достичь здесь:

Sequence1: {ctrl+k, ctrl+5}, {ctrl+k, ctrl+j} {ctrl+k, ctrl+4}, {ctrl+k, ctrl+j} {ctrl+k, ctrl+3}, {ctrl+k, ctrl+j} {ctrl+k, ctrl+2}, {ctrl+k, ctrl+j} {ctrl+k, ctrl+1}, {ctrl+k, ctrl+j}

enter image description here

Sequence2: {ctrl+k, ctrl+5}, {ctrl+k, ctrl+4}, {ctrl+k, ctrl+3}, {ctrl+k, ctrl+2}, {ctrl+k, ctrl+1}

enter image description here

Я уверен, что есть больше внутренних деталей о поведении SublimeText, но если мой пример вел себя точно так же, как опубликовано на этих снимках после тестирования последовательностей, вы могли бы сказать, что эта функция стала довольно удобной для использования.

1 Ответ

0 голосов
/ 10 мая 2018

Проблемы с вашим примером в основном вызваны неправильным именованием в API QsciScintilla. Методы foldLine и foldAll действительно должны вызываться toggleFoldLine и toggleFoldAll, поскольку они на самом деле отменяют предыдущее состояние. Это означает, например, что если две последовательные линии имеют одинаковый уровень сгиба, двойной вызов foldLine не приведет к чистым изменениям.

В приведенной ниже реализации я использовал более явные сообщения Scintilla, так что затрагиваются только те строки, которые действительно требуют свертывания. Я также изменил сочетания клавиш в соответствии со значениями по умолчанию в SublimeText:

class Foo(QsciScintilla):
    def __init__(self, parent=None):
        ...
        QShortcut(QKeySequence("Ctrl+K, Ctrl+J"), self, self.fold_by_level)
        QShortcut(QKeySequence("Ctrl+K, Ctrl+0"), self, self.fold_by_level)
        ...

    def fold_by_level(self, level=0):
        SCI = self.SendScintilla
        if level:
            level += 0x400
            MASK = QsciScintilla.SC_FOLDLEVELNUMBERMASK
            for line in range(self.lines()):
                foldlevel = SCI(QsciScintilla.SCI_GETFOLDLEVEL, line) & MASK
                print('line=%i, level=%i' % (line + 1, foldlevel), end='')
                if foldlevel == level:
                    line = SCI(QsciScintilla.SCI_GETFOLDPARENT, line)
                    if SCI(QsciScintilla.SCI_GETFOLDEXPANDED, line):
                        print(', foldline:', line + 1, end='')
                        SCI(QsciScintilla.SCI_FOLDLINE, line,
                            QsciScintilla.SC_FOLDACTION_CONTRACT)
                print()
        else:
            SCI(QsciScintilla.SCI_FOLDALL, QsciScintilla.SC_FOLDACTION_EXPAND)
...