Как я могу сделать QScintilla авто-отступ, как SublimeText? - PullRequest
13 голосов
/ 24 апреля 2019

Рассмотрим приведенный ниже mcve:

import sys
import textwrap

from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import *


if __name__ == '__main__':

    app = QApplication(sys.argv)
    view = QsciScintilla()

    view.SendScintilla(view.SCI_SETMULTIPLESELECTION, True)
    view.SendScintilla(view.SCI_SETMULTIPASTE, 1)
    view.SendScintilla(view.SCI_SETADDITIONALSELECTIONTYPING, True)

    view.setAutoIndent(True)
    view.setTabWidth(4)
    view.setIndentationGuides(True)
    view.setIndentationsUseTabs(False)
    view.setBackspaceUnindents(True)

    view.setText(textwrap.dedent("""\
        def foo(a,b):
            print('hello')
    """))

    view.show()
    app.exec_()

Поведение авто-отступа вышеупомянутого фрагмента действительно плохо при сравнении его с редакторами, такими как SublimeText или CodeMirror.Сначала давайте посмотрим, как хорошо будет вести себя функция автоматического отступа в SublimeText с одним или несколькими выделениями.

enter image description here

А теперь давайте посмотрим, как работает автоматический отступс приведенным выше фрагментом:

enter image description here

По сравнению с SublimeText, способ, которым QScintilla работает, когда автоматическое отступление с обоими одиночными / множественными выборами, является пробковым и действительноплохо / непригодно для использования.

Первым шагом к тому, чтобы сделать виджет более похожим на SublimeText / Codemirror, было бы отключить текущий слот, из-за которого автоиндентирование ведет себя плохо, мы можем добиться этого, выполнив:

print(view.receivers(view.SCN_CHARADDED))
view.SCN_CHARADDED.disconnect()
print(view.receivers(view.SCN_CHARADDED))

В этот момент вы будете готовы соединить SCN_CHARADDED со своим пользовательским слотом, выполняя всю магию:)

ВОПРОС: Как бы вы изменили приведенный выше фрагмент, чтобы все варианты выбора былисохраняется, и авто-отступ будет вести себя точно так же, как SublimeText, Codemirror или любой другой серьезный текстовый редактор?

Ссылки:

qsciscintilla.h

class QSCINTILLA_EXPORT QsciScintilla : public QsciScintillaBase
{
    Q_OBJECT

public:
    ...
    private slots:
        void handleCharAdded(int charadded);
    ...
    private:
        void autoIndentation(char ch, long pos);

qsciscintilla.cpp

connect(this,SIGNAL(SCN_CHARADDED(int)),
         SLOT(handleCharAdded(int)));

...

// Handle the addition of a character.
void QsciScintilla::handleCharAdded(int ch)
{
    // Ignore if there is a selection.
    long pos = SendScintilla(SCI_GETSELECTIONSTART);

    if (pos != SendScintilla(SCI_GETSELECTIONEND) || pos == 0)
        return;

    // If auto-completion is already active then see if this character is a
    // start character.  If it is then create a new list which will be a subset
    // of the current one.  The case where it isn't a start character seems to
    // be handled correctly elsewhere.
    if (isListActive() && isStartChar(ch))
    {
        cancelList();
        startAutoCompletion(acSource, false, use_single == AcusAlways);

        return;
    }

    // Handle call tips.
    if (call_tips_style != CallTipsNone && !lex.isNull() && strchr("(),", ch) != NULL)
        callTip();

    // Handle auto-indentation.
    if (autoInd)
    {
        if (lex.isNull() || (lex->autoIndentStyle() & AiMaintain))
            maintainIndentation(ch, pos);
        else
            autoIndentation(ch, pos);
    }

    // See if we might want to start auto-completion.
    if (!isCallTipActive() && acSource != AcsNone)
    {
        if (isStartChar(ch))
            startAutoCompletion(acSource, false, use_single == AcusAlways);
        else if (acThresh >= 1 && isWordCharacter(ch))
            startAutoCompletion(acSource, true, use_single == AcusAlways);
    }
}

ВАЖНО: Я решил опубликовать соответствующие биты c ++, чтобы вы получили больше информации о том, как добиться внутреннего отступа, чтобы дать больше подсказок о возможной замене ... Цель этого потока - попытаться найти чистое решение Python хотя.Я бы не хотел изменять исходный код QScintilla (если это возможно), чтобы обслуживание и обновление оставались максимально простыми, а QScintilla dep все еще можно было бы увидеть как черный ящик.

Ответы [ 2 ]

0 голосов
/ 02 мая 2019

В QScintilla нет интегрированного способа заставить авто-отступ работать (особенно в SublimeText).Это поведение зависит от языка и пользователя.Собственная документация Scintilla содержит примеры того, как вызывать авто-отступ.Извините, но написано в C # .Я не нашел его написанным на Python.

Вот код (я знаю, что QScintilla - это Port to Qt, этот Scintilla-ориентированный код должен работать и с QScintilla, или, в худшем случае, вы можете его адаптировать.для C ++):

private void Scintilla_InsertCheck(object sender, InsertCheckEventArgs e) {

    if ((e.Text.EndsWith("" + Constants.vbCr) || e.Text.EndsWith("" + Constants.vbLf))) {
        int startPos = Scintilla.Lines(Scintilla.LineFromPosition(Scintilla.CurrentPosition)).Position;
        int endPos = e.Position;
        string curLineText = Scintilla.GetTextRange(startPos, (endPos - startPos)); 
        // Text until the caret so that the whitespace is always
        // equal in every line.

        Match indent = Regex.Match(curLineText, "^[ \\t]*");
        e.Text = (e.Text + indent.Value);
        if (Regex.IsMatch(curLineText, "{\\s*$")) {
            e.Text = (e.Text + Constants.vbTab);
        }
    }
}

private void Scintilla_CharAdded(object sender, CharAddedEventArgs e) {

    //The '}' char.
    if (e.Char == 125) {
        int curLine = Scintilla.LineFromPosition(Scintilla.CurrentPosition);

        if (Scintilla.Lines(curLine).Text.Trim() == "}") { 
        //Check whether the bracket is the only thing on the line. 
        //For cases like "if() { }".
            SetIndent(Scintilla, curLine, GetIndent(Scintilla, curLine) - 4);
        }
    }
}

//Codes for the handling the Indention of the lines.
//They are manually added here until they get officially 
//added to the Scintilla control.

#region "CodeIndent Handlers"
    const int SCI_SETLINEINDENTATION = 2126;
    const int SCI_GETLINEINDENTATION = 2127;
    private void SetIndent(ScintillaNET.Scintilla scin, int line, int indent) {
        scin.DirectMessage(SCI_SETLINEINDENTATION, new IntPtr(line), new IntPtr(indent));
    }
    private int GetIndent(ScintillaNET.Scintilla scin, int line) {
        return (scin.DirectMessage(SCI_GETLINEINDENTATION, new IntPtr(line), null).ToInt32);
    }
#endregion

Надеюсь, это поможет.

0 голосов
/ 27 апреля 2019

Похоже, вы должны написать свою собственную версию, в документации упоминается самый важный момент, о котором уже говорилось в главе Установка :

В комплект поставки QScintilla будет встроена как общая библиотека / DLL и установлена ​​в те же каталоги, что и библиотеки Qt и включаемые файлы.

Если вы хотите создать статическую версию библиотеки, передайте CONFIG + = staticlib в командной строке qmake.

Если вы хотите внести более существенные изменения в конфигурацию, отредактируйте файл qscintilla.pro в каталоге Qt4Qt5.

Если вы вносите изменения, в частности в имена каталогов установки или в название библиотеки, вам также может потребоваться обновить файл Qt4Qt5 / features / qscintilla2.prf. *

Здесь также объясняются дальнейшие шаги.

...