예제 #1
0
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)
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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)
예제 #5
0
 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()
예제 #6
0
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
예제 #7
0
    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()
예제 #8
0
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()
예제 #9
0
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()
예제 #10
0
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)
예제 #11
0
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)
예제 #12
0
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
예제 #13
0
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()
예제 #14
0
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()
예제 #15
0
 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
예제 #16
0
    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
예제 #17
0
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)
예제 #18
0
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)
예제 #19
0
    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)
예제 #20
0
 def state(self, block):
     """Return the state at the start of the specified block."""
     return tokeniter.state(block)
예제 #21
0
 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)
예제 #22
0
 def state(self, block):
     """Return the state at the start of the specified block."""
     return tokeniter.state(block)