Consider the below 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_()
The behaviour of the auto-indent of the above snippet is really bad when comparing it with editors such as SublimeText or CodeMirror. First let's see how nice will behave the autoindent feature in SublimeText with single or multiple selections.
And now let's see how the auto-indent works in with the above snippet:
In comparison to SublimeText the way QScintilla works when auto-indentation is enabled with both single/multi selections is corky and really bad/unusable.
The first step to make the widget more like SublimeText/Codemirror would be disconnecting the current slot that makes autoindentation to behave badly, we can achieve that by doing:
print(view.receivers(view.SCN_CHARADDED))
view.SCN_CHARADDED.disconnect()
print(view.receivers(view.SCN_CHARADDED))
At this point you'd be ready to connect SCN_CHARADDED
with your custom slot doing all the magic :)
QUESTION: How would you modify the above snippet so all selections will be preserved and the auto-indentation will behave exactly like SublimeText, Codemirror or any serious text editor out there?
REFERENCES:
https://www.riverbankcomputing.com/static/Docs/QScintilla/classQsciScintillaBase.html#signals
QScintilla source code, below you can see what the private slot we've disconnected by using disconnect
would look like:
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);
}
}
IMPORTANT: I've decided to post the relevant c++ bits so you'll got more background about how the indentation is achieved internally to give more clues about a possible replacement... The goal of this thread is to try to find a pure python solution though. I'd like to avoid modifying the QScintilla source code (if possible) so maintenance/upgrading will remain as simple as possible and QScintilla dep can still be seen as a black box.
It looks like you have to code your own version, the documentation mentions the most important point's about it already in the chapter Installation:
As supplied QScintilla will be built as a shared library/DLL and installed in the same directories as the Qt libraries and include files.
If you wish to build a static version of the library then pass CONFIG+=staticlib on the qmake command line.
If you want to make more significant changes to the configuration then edit the file qscintilla.pro in the Qt4Qt5 directory.
If you do make changes, specifically to the names of the installation directories or the name of the library, then you may also need to update the Qt4Qt5/features/qscintilla2.prf file.*
Further steps are explained there too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With