Let me start by posting some little helper functions I'll use to formulate my questions:
import textwrap
import sys
from pathlib import Path
from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import * # noqa
def set_style(sci):
# Set default font
sci.font = QFont()
sci.font.setFamily('Consolas')
sci.font.setFixedPitch(True)
sci.font.setPointSize(8)
sci.font.setBold(True)
sci.setFont(sci.font)
sci.setMarginsFont(sci.font)
sci.setUtf8(True)
# Set paper
sci.setPaper(QColor(39, 40, 34))
# Set margin defaults
fontmetrics = QFontMetrics(sci.font)
sci.setMarginsFont(sci.font)
sci.setMarginWidth(0, fontmetrics.width("000") + 6)
sci.setMarginLineNumbers(0, True)
sci.setMarginsForegroundColor(QColor(128, 128, 128))
sci.setMarginsBackgroundColor(QColor(39, 40, 34))
sci.setMarginType(1, sci.SymbolMargin)
sci.setMarginWidth(1, 12)
# Set indentation defaults
sci.setIndentationsUseTabs(False)
sci.setIndentationWidth(4)
sci.setBackspaceUnindents(True)
sci.setIndentationGuides(True)
sci.setFoldMarginColors(QColor(39, 40, 34), QColor(39, 40, 34))
# Set caret defaults
sci.setCaretForegroundColor(QColor(247, 247, 241))
sci.setCaretWidth(2)
# Set edge defaults
sci.setEdgeColumn(80)
sci.setEdgeColor(QColor(221, 221, 221))
sci.setEdgeMode(sci.EdgeLine)
# Set folding defaults (http://www.scintilla.org/ScintillaDoc.html#Folding)
sci.setFolding(QsciScintilla.CircledFoldStyle)
# Set wrapping
sci.setWrapMode(sci.WrapNone)
# Set selection color defaults
sci.setSelectionBackgroundColor(QColor(61, 61, 52))
sci.resetSelectionForegroundColor()
# Set scrollwidth defaults
sci.SendScintilla(QsciScintilla.SCI_SETSCROLLWIDTHTRACKING, 1)
# Current line visible with special background color
sci.setCaretLineBackgroundColor(QColor(255, 255, 224))
# Set multiselection defaults
sci.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True)
sci.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1)
sci.SendScintilla(QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True)
def set_state1(sci):
sci.clear_selections()
base = "line{} state1"
view.setText("\n".join([base.format(i) for i in range(10)]))
for i in range(0, 10, 2):
region = (len(base) * i, len(base) * (i + 1) - 1)
if i == 0:
view.set_selection(region)
else:
view.add_selection(region)
def set_state2(sci):
base = "line{} state2"
view.setText("\n".join([base.format(i) for i in range(10)]))
for i in range(1, 10, 2):
region = (len(base) * i, len(base) * (i + 1) - 1)
if i == 1:
view.set_selection(region)
else:
view.add_selection(region)
class Editor(QsciScintilla):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
set_style(self)
def clear_selections(self):
sci = self
sci.SendScintilla(sci.SCI_CLEARSELECTIONS)
def set_selection(self, r):
sci = self
sci.SendScintilla(sci.SCI_SETSELECTION, r[1], r[0])
def add_selection(self, r):
sci = self
sci.SendScintilla(sci.SCI_ADDSELECTION, r[1], r[0])
def sel(self):
sci = self
regions = []
for i in range(sci.SendScintilla(sci.SCI_GETSELECTIONS)):
regions.append(
sci.SendScintilla(sci.SCI_GETSELECTIONNSTART, i),
sci.SendScintilla(sci.SCI_GETSELECTIONNEND, i)
)
return sorted(regions)
I've got a couple of questions actually:
Question 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Editor()
set_state1(view)
view.move(1000, 100)
view.resize(800, 300)
view.show()
app.exec_()
I'll get this (you can see the question in the below snapshot):
Question 2)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Editor()
set_state1(view)
set_state2(view)
view.move(1000, 100)
view.resize(800, 300)
view.show()
app.exec_()
How can I modify the code so I'll be able to restore state1 when pressing ctrl+z?
Right now when using ctrl+z you won't be able to get state1:
mainly because how setText behaves:
Replaces all of the current text with text. Note that the undo/redo history is cleared by this function.
I've already tried some of the functions posted in the undo and redo docs but no luck so far.
For instance, one of my attempts has been first selecting all text and then using replaceSelectedText and finally restoring the selections from the previous state manually, the result was ugly (i don't want the editor scrolling messing up when undoing/redoing)... Basically, I'd like to get the same feeling than SublimeText.
Btw, this is a little minimal example but in the real-case I'll be accumulating a bunch of operations without committing to scintilla very often... that's why I'm interested to figure out how to rollback to a previous state when using the undoable setText... Said otherwise, i'd like to avoid using Scintilla functions such as insertAt, replaceSelectedText or similars... as I'm using python string builtin functions to modify the buffer internally.
EDIT:
I'm pretty sure beginUndoAction & endUndoAction won't help me to answer question2 but... what about SCI_ADDUNDOACTION
? Although the docs are pretty confusing though... :/
Question 1:
The last selection added is automatically set as the Main
selection. To remove it, add line sci.SendScintilla(sci.SCI_SETMAINSELECTION, -1)
at the end of the set_state1
function.
Question 2:
replaceSelectedText
, and then using setCursorPosition
/ reselecting all selections
and setFirstVisibleLine
to restore the scroll position is one way to go.setText
function:// Set the given text.
void QsciScintilla::setText(const QString &text)
{
bool ro = ensureRW();
SendScintilla(SCI_SETTEXT, ScintillaBytesConstData(textAsBytes(text)));
SendScintilla(SCI_EMPTYUNDOBUFFER);
setReadOnly(ro);
}
You could try setting the text using sci.SendScintilla(sci.SCI_SETTEXT, b"some text")
, which doesn't reset the undo/redo buffer.