def cut_assign(cursor): """Cuts selected text and assigns it to a LilyPond variable.""" # ask the variable name name = inputdialog.getText(None, _("Cut and Assign"), _( "Please enter the name for the variable to assign the selected " "text to:"), regexp="[A-Za-z]+") if not name: return cursortools.strip_selection(cursor) # determine state at cursor block = cursortools.block(cursor) state = tokeniter.state(block) for t in tokeniter.partition(cursor).left: state.follow(t) mode = "" for p in state.parsers(): if isinstance(p, ly.lex.lilypond.ParseInputMode): if isinstance(p, ly.lex.lilypond.ParseLyricMode): mode = " \\lyricmode" elif isinstance(p, ly.lex.lilypond.ParseChordMode): mode = " \\chordmode" elif isinstance(p, ly.lex.lilypond.ParseFigureMode): mode = " \\figuremode" elif isinstance(p, ly.lex.lilypond.ParseDrumMode): mode = " \\drummode" break # find insertion place: found = False while block.previous().isValid(): block = block.previous() state = tokeniter.state(block) if isinstance(state.parser(), ly.lex.lilypond.ParseGlobal): found = True break tokens = tokeniter.tokens(block) for t in tokens: if isinstance(t, ly.lex.lilypond.Name): found = True break elif not isinstance(t, (ly.lex.Space, ly.lex.Comment)): break if found: break insert = QTextCursor(block) text = cursor.selection().toPlainText() space = '\n' if '\n' in text else ' ' text = ''.join((name, ' =', mode, ' {', space, text, space, '}\n\n')) with cursortools.compress_undo(cursor): cursor.insertText('\\' + name) pos = insert.selectionStart() insert.insertText(text) if metainfo.info(cursor.document()).auto_indent: insert.setPosition(pos, QTextCursor.KeepAnchor) with cursortools.compress_undo(insert, True): indent.re_indent(insert)
def change_indent(cursor, direction): """Changes the indent in the desired direction (-1 for left and +1 for right). Returns True if the indent operation was applied. The cursor may contain a selection. """ # get some variables from the document indent_vars = indent_variables(cursor.document()) blocks = list(cursortools.blocks(cursor)) block = blocks[0] pos = cursor.selectionStart() - block.position() token = tokeniter.tokens(block)[0] if tokeniter.tokens(block) else None if cursor.hasSelection() or pos == 0 or (token and isinstance(token, ly.lex.Space) and token.end >= pos): # decrease the indent state = tokeniter.state(block) current_indent = get_indent(block) new_indent = current_indent + direction * indent_vars['indent-width'] if state.mode() in ('lilypond', 'scheme'): computed_indent = compute_indent(block) if cmp(computed_indent, new_indent) == direction: new_indent = computed_indent diff = new_indent - current_indent with cursortools.compress_undo(cursor): for block in blocks: set_indent(block, get_indent(block) + diff) return True
def pixmap(cursor, num_lines=6, scale=0.8): """Return a QPixmap displaying the selected lines of the document. If the cursor has no selection, num_lines are drawn. By default the text is drawn 0.8 * the normal font size. You can change that by supplying the scale parameter. """ block = cursor.document().findBlock(cursor.selectionStart()) c2 = QTextCursor(block) if cursor.hasSelection(): c2.setPosition(cursor.selectionEnd(), QTextCursor.KeepAnchor) c2.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) else: c2.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor, num_lines) data = textformats.formatData('editor') doc = QTextDocument() font = QFont(data.font) font.setPointSizeF(font.pointSizeF() * scale) doc.setDefaultFont(font) doc.setPlainText(c2.selection().toPlainText()) if metainfo.info(cursor.document()).highlighting: highlighter.highlight(doc, state=tokeniter.state(block)) size = doc.size().toSize() + QSize(8, -4) pix = QPixmap(size) pix.fill(data.baseColors['background']) doc.drawContents(QPainter(pix)) return pix
def show(cursor, pos=None, num_lines=6): """Displays a tooltip showing part of the cursor's Document. If the cursor has a selection, those blocks are displayed. Otherwise, num_lines lines are displayed. If pos is not given, the global mouse position is used. """ block = cursor.document().findBlock(cursor.selectionStart()) c2 = QTextCursor(block) if cursor.hasSelection(): c2.setPosition(cursor.selectionEnd(), QTextCursor.KeepAnchor) c2.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) else: c2.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor, num_lines) data = textformats.formatData('editor') doc = QTextDocument() font = QFont(data.font) font.setPointSizeF(font.pointSizeF() * .8) doc.setDefaultFont(font) doc.setPlainText(c2.selection().toPlainText()) if metainfo.info(cursor.document()).highlighting: highlighter.highlight(doc, state=tokeniter.state(block)) size = doc.size().toSize() + QSize(8, -4) pix = QPixmap(size) pix.fill(data.baseColors['background']) doc.drawContents(QPainter(pix)) label = QLabel() label.setPixmap(pix) label.setStyleSheet("QLabel { border: 1px solid #777; }") label.resize(size) widgets.customtooltip.show(label, pos)
def edit(self, cursor): """Edit the block at the specified QTextCursor.""" if self._document: self._document.closed.disconnect(self.reject) self._document = cursor.document() self._document.closed.connect(self.reject) # don't change the cursor c = self._range = QTextCursor(cursor) cursorpos = c.position() - c.block().position() cursortools.strip_indent(c) indentpos = c.position() - c.block().position() c.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) self.view.setPlainText(c.selection().toPlainText()) self.highlighter.setInitialState(tokeniter.state(cursortools.block(cursor))) self.highlighter.setHighlighting(metainfo.info(cursor.document()).highlighting) self.highlighter.rehighlight() # let autocomplete query the real document as if we're at the start # of the current block self.completer.document_cursor = QTextCursor(cursor.block()) self.completer.autoComplete = QSettings().value("autocomplete", True, bool) cursor = self.view.textCursor() cursor.setPosition(max(0, cursorpos-indentpos)) self.view.setTextCursor(cursor) self.updateMessage()
def edit(self, cursor): """Edit the block at the specified QTextCursor.""" if self._document: self._document.closed.disconnect(self.reject) self._document = cursor.document() self._document.closed.connect(self.reject) # don't change the cursor c = self._range = QTextCursor(cursor) cursorpos = c.position() - c.block().position() cursortools.strip_indent(c) indentpos = c.position() - c.block().position() c.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) self.view.setPlainText(c.selection().toPlainText()) self.highlighter.setInitialState( tokeniter.state(cursortools.block(cursor))) self.highlighter.setHighlighting( metainfo.info(cursor.document()).highlighting) self.highlighter.rehighlight() # let autocomplete query the real document as if we're at the start # of the current block self.completer.document_cursor = QTextCursor(cursor.block()) self.completer.autoComplete = QSettings().value("autocomplete", True) not in ('false', False) cursor = self.view.textCursor() cursor.setPosition(max(0, cursorpos - indentpos)) self.view.setTextCursor(cursor) self.updateMessage()
def get_definition(cursor): block = cursor.block() while block.isValid(): state = tokeniter.state(block) if isinstance(state.parser(), ly.lex.lilypond.ParseGlobal): for t in tokeniter.tokens(block)[:2]: if type(t) is ly.lex.lilypond.Name: return t[:] elif isinstance(t, ly.lex.lilypond.Keyword) and t == '\\score': return '\\score' block = block.previous()
def state(cursor): """Returns the simplestate string for the position of the cursor.""" import simplestate pos = cursor.selectionStart() block = cursor.document().findBlock(pos) tokens = tokeniter.tokens(block) state = tokeniter.state(block) column = pos - block.position() for t in tokens: if t.end > column: break state.follow(t) return simplestate.state(state)
def re_indent(cursor): """Re-indents the selected region or the whole document.""" if cursor.hasSelection(): blocks = cursortools.blocks(cursor) else: blocks = cursortools.all_blocks(cursor.document()) with cursortools.compress_undo(cursor): for block in blocks: tokeniter.update(block) if tokeniter.state(block).mode() in ('lilypond', 'scheme'): indent = compute_indent(block) else: indent = get_indent(block) if set_indent(block, indent): tokeniter.update(block) # force token update if changed
def get_definition(cursor): """Return the variable name the cursor's music expression is assigned to. If the music is in a \\score instead, "\\score" is returned. Returns None if no variable name can be found. """ block = cursor.block() while block.isValid(): state = tokeniter.state(block) if isinstance(state.parser(), ly.lex.lilypond.ParseGlobal): for t in tokeniter.tokens(block)[:2]: if type(t) is ly.lex.lilypond.Name: return t[:] elif isinstance(t, ly.lex.lilypond.Keyword) and t == '\\score': return '\\score' block = block.previous()
def analyze(self, cursor): """Do the analyzing work; set the attributes column and model.""" self.cursor = cursor block = cursor.block() self.column = column = cursor.position() - block.position() self.text = text = block.text()[:column] self.model = None # make a list of tokens exactly ending at the cursor position # and let state follow state = self.state = tokeniter.state(block) tokens = self.tokens = [] for t in tokeniter.tokens(cursor.block()): if t.end > column: # cut off the last token and run the parser on it tokens.extend(state.tokens(text, t.pos)) break tokens.append(t) state.follow(t) if t.end == column: break self.last = tokens[-1] if tokens else '' self.lastpos = self.last.pos if self.last else column parser = state.parser() # Map the parser class to a group of tests to return the model. # Try the tests until a model is returned. try: tests = self.tests[parser.__class__] except KeyError: return else: for function in tests: model = function(self) if model: self.model = model return
def cut_assign(cursor): """Cuts selected text and assigns it to a LilyPond variable.""" # ask the variable name name = inputdialog.getText( None, _("Cut and Assign"), _("Please enter the name for the variable to assign the selected " "text to:"), regexp="[A-Za-z]+") if not name: return cursortools.strip_selection(cursor) # determine state at cursor block = cursortools.block(cursor) state = tokeniter.state(block) for t in tokeniter.partition(cursor).left: state.follow(t) mode = "" for p in state.parsers(): if isinstance(p, ly.lex.lilypond.ParseInputMode): if isinstance(p, ly.lex.lilypond.ParseLyricMode): mode = " \\lyricmode" elif isinstance(p, ly.lex.lilypond.ParseChordMode): mode = " \\chordmode" elif isinstance(p, ly.lex.lilypond.ParseFigureMode): mode = " \\figuremode" elif isinstance(p, ly.lex.lilypond.ParseDrumMode): mode = " \\drummode" break # find insertion place: found = False while block.previous().isValid(): block = block.previous() state = tokeniter.state(block) if isinstance(state.parser(), ly.lex.lilypond.ParseGlobal): found = True break tokens = tokeniter.tokens(block) for t in tokens: if isinstance(t, ly.lex.lilypond.Name): found = True break elif not isinstance(t, (ly.lex.Space, ly.lex.Comment)): break if found: break insert = QTextCursor(block) text = cursor.selection().toPlainText() space = '\n' if '\n' in text else ' ' text = ''.join((name, ' =', mode, ' {', space, text, space, '}\n\n')) with cursortools.compress_undo(cursor): cursor.insertText('\\' + name) pos = insert.selectionStart() insert.insertText(text) if metainfo.info(cursor.document()).auto_indent: insert.setPosition(pos, QTextCursor.KeepAnchor) with cursortools.compress_undo(insert, True): indent.re_indent(insert)
def updateView(self): """Recreate the items in the view.""" with qutil.signalsBlocked(self): self.clear() doc = self.parent().mainwindow().currentDocument() if not doc: return view_cursor_position = self.parent().mainwindow().textCursor( ).position() structure = documentstructure.DocumentStructure.instance(doc) last_item = None current_item = None last_block = None for i in structure.outline(): position = i.start() block = doc.findBlock(position) depth = tokeniter.state(block).depth() if block == last_block: parent = last_item elif last_block is None or depth == 1: # a toplevel item anyway parent = self else: while last_item and depth <= last_item.depth: last_item = last_item.parent() if not last_item: parent = self else: # the item could belong to a parent item, but see if they # really are in the same (toplevel) state b = last_block.next() while b < block: depth2 = tokeniter.state(b).depth() if depth2 == 1: parent = self break while last_item and depth2 <= last_item.depth: last_item = last_item.parent() if not last_item: parent = self break b = b.next() else: parent = last_item item = last_item = QTreeWidgetItem(parent) # set item text and display style bold if 'title' was used for name, text in i.groupdict().items(): if text: if name.startswith('title'): font = item.font(0) font.setWeight(QFont.Bold) item.setFont(0, font) break elif name.startswith('alert'): color = item.foreground(0).color() color = qutil.addcolor(color, 128, 0, 0) item.setForeground(0, QBrush(color)) font = item.font(0) font.setStyle(QFont.StyleItalic) item.setFont(0, font) elif name.startswith('text'): break else: text = i.group() item.setText(0, text) # remember whether is was collapsed by the user try: collapsed = block.userData().collapsed except AttributeError: collapsed = False item.setExpanded(not collapsed) item.depth = depth item.position = position last_block = block # scroll to the item at the view's cursor later if position <= view_cursor_position: current_item = item if current_item: self.scrollToItem(current_item)
def state(self, block): """Return the state at the start of the specified block.""" return tokeniter.state(block)
def updateView(self): """Recreate the items in the view.""" with qutil.signalsBlocked(self): self.clear() doc = self.parent().mainwindow().currentDocument() if not doc: return view_cursor_position = self.parent().mainwindow().textCursor().position() structure = documentstructure.DocumentStructure.instance(doc) last_item = None current_item = None last_block = None for i in structure.outline(): position = i.start() block = doc.findBlock(position) depth = tokeniter.state(block).depth() if block == last_block: parent = last_item elif last_block is None or depth == 1: # a toplevel item anyway parent = self else: while last_item and depth <= last_item.depth: last_item = last_item.parent() if not last_item: parent = self else: # the item could belong to a parent item, but see if they # really are in the same (toplevel) state b = last_block.next() while b < block: depth2 = tokeniter.state(b).depth() if depth2 == 1: parent = self break while last_item and depth2 <= last_item.depth: last_item = last_item.parent() if not last_item: parent = self break b = b.next() else: parent = last_item item = last_item = QTreeWidgetItem(parent) # set item text and display style bold if 'title' was used for name, text in i.groupdict().items(): if text: if name.startswith('title'): font = item.font(0) font.setWeight(QFont.Bold) item.setFont(0, font) break elif name.startswith('alert'): color = item.foreground(0).color() color = qutil.addcolor(color, 128, 0, 0) item.setForeground(0, QBrush(color)) font = item.font(0) font.setStyle(QFont.StyleItalic) item.setFont(0, font) elif name.startswith('text'): break else: text = i.group() item.setText(0, text) # remember whether is was collapsed by the user try: collapsed = block.userData().collapsed except AttributeError: collapsed = False item.setExpanded(not collapsed) item.depth = depth item.position = position last_block = block # scroll to the item at the view's cursor later if position <= view_cursor_position: current_item = item if current_item: self.scrollToItem(current_item)