Как я могу сделать авто-отступ QScintilla как SublimeText?
Рассмотрим ниже 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 с одним или несколькими вариантами выбора.
А теперь давайте посмотрим, как работает авто-отступ с приведенным выше фрагментом:
По сравнению с 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 можно по-прежнему рассматривать как черный ящик.
Ответы
Ответ 1
Похоже, что вы должны кодировать свою собственную версию, в документации упоминается самый важный момент об этом уже в главе Установка:
При поставке QScintilla будет собрана как общая библиотека /DLL и установлена в те же каталоги, что и библиотеки Qt, и включает файлы.
Если вы хотите создать статическую версию библиотеки, введите CONFIG+ = staticlib в командной строке qmake.
Если вы хотите внести более существенные изменения в конфигурацию, отредактируйте файл qscintilla.pro в каталоге Qt4Qt5.
Если вы вносите изменения, в частности, в имена каталогов установки или имя библиотеки, вам также может потребоваться обновить файл Qt4Qt5/features/qscintilla2.prf. *
Дальнейшие шаги объяснены там же.
Ответ 2
В QScintilla
нет интегрированного способа заставить работать QScintilla
(особенно в SublimeText
). Это поведение зависит от языка и пользователя. Документация Native Scintilla содержит примеры того, как вызвать авто-отступ. Извините, но написано на С#. Я не нашел это написано на 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
Надеюсь это поможет.