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) # Use raw messages to Scintilla here # (all messages are documented here: http://www.scintilla.org/ScintillaDoc.html) # Ensure the width of the currently visible lines can be scrolled self.SendScintilla(QsciScintilla.SCI_SETSCROLLWIDTHTRACKING, 1) # Multiple cursor support self.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True) self.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1) self.SendScintilla( QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True) # Comment feature goes here # self.commenter = Commenter(self, "//") QShortcut(QKeySequence("Ctrl+7"), self, self.comment_blocks) def init_test_selections(self): # initialize multiple selections offset1 = self.positionFromLineIndex(21, 0) offset2 = self.positionFromLineIndex(29, 5) self.SendScintilla(self.SCI_SETSELECTION, offset1, offset2) offset1 = self.positionFromLineIndex(31, 0) offset2 = self.positionFromLineIndex(33, 20) self.SendScintilla(self.SCI_ADDSELECTION, offset1, offset2) offset1 = self.positionFromLineIndex(37, 0) offset2 = self.positionFromLineIndex(39, 5) self.SendScintilla(self.SCI_ADDSELECTION, offset1, offset2) def comment_blocks(self): comment_chars = '// ' selections = [(self.SendScintilla(QsciScintilla.SCI_GETSELECTIONNSTART, i), self.SendScintilla(QsciScintilla.SCI_GETSELECTIONNEND, i)) for i in range(self.SendScintilla(QsciScintilla.SCI_GETSELECTIONS))] def block_indentation(lineFrom, lineTo): """Get the minimum indentation for the line range""" indent = min(self.indentation(line) for line in range(lineFrom, lineTo)) return indent def comment(selFrom, selTo): lineFrom = selFrom[0] lineTo = selTo[0] + 1 indent = block_indentation(lineFrom, lineTo) # for line in range(lineTo-1, lineFrom-1, -1): for line in range(lineFrom, lineTo): text = self.text(line).lstrip() if not text: self.insertAt(' ' * indent + comment_chars, line, 0) # Make sure blank lines are preserved else: self.insertAt(comment_chars, line, indent) self.setSelection(lineFrom, selFrom[1], lineTo, selTo[1]) # Restore selection TODO: for muliple selections see init_test_selections() for sel in reversed(selections): # Positions will change due to inserted comment chars..so run loop in reverse sel_from = self.lineIndexFromPosition(sel[0]) sel_to = self.lineIndexFromPosition(sel[1]) comment(sel_from, sel_to) def main(): app = QApplication(sys.argv) ex = Foo() ex.setText("""\ #include 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.init_test_selections() ex.comment_blocks() ex.resize(800, 600) ex.show() sys.exit(app.exec_()) if __name__ == "__main__": main()