File: reformat.py

package info (click to toggle)
python-ly 0.9.9-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,604 kB
  • sloc: python: 12,559; xml: 1,887; makefile: 155
file content (110 lines) | stat: -rw-r--r-- 3,962 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# This file is part of python-ly, https://pypi.python.org/pypi/python-ly
#
# Copyright (c) 2008 - 2015 by Wilbert Berendsen
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# See http://www.gnu.org/licenses/ for more information.

"""
Formatting tools to improve the readability of a ly.document.Document without
changing the semantic meaning of the LilyPond source.

Basically the tools only change whitespace to make the source-code more 
readable.

See also ly.indent.

"""

from __future__ import unicode_literals

import ly.indent
import ly.lex


def break_indenters(cursor):
    """Add newlines around indent and dedent tokens where needed.
    
    If there is stuff after a { or << (that's not closed on the same line) 
    it is put on a new line, and if there if stuff before a } or >>, the } 
    or >> is put on a new line.
    
    It is necessary to run the indenter again over the same part of the 
    document, as it will look garbled with the added newlines.
    
    """
    with cursor.document as d:
        for b in cursor.blocks():
            denters = []
            tokens = d.tokens(b)
            nonspace_index = -1
            for i, t in enumerate(tokens):
                if isinstance(t, ly.lex.Indent) and t in ('{', '<<'):
                    denters.append(i)
                elif isinstance(t, ly.lex.Dedent) and t in ('}', '>>'):
                    if denters:
                        denters.pop()
                    elif nonspace_index != -1:
                        # add newline before t
                        pos = d.position(b) + t.pos
                        d[pos:pos] = '\n'
                if not isinstance(t, ly.lex.Space):
                    nonspace_index = i
            for i in denters:
                if i < nonspace_index:
                    # add newline after tokens[i]
                    pos = d.position(b) + tokens[i].end
                    d[pos:pos] = '\n'


def move_long_comments(cursor):
    """Move line comments with more than 2 comment characters to column 0."""
    with cursor.document as d:
        for b in cursor.blocks():
            tokens = d.tokens(b)
            if (len(tokens) == 2
                and isinstance(tokens[0], ly.lex.Space)
                and isinstance(tokens[1], (
                    ly.lex.lilypond.LineComment,
                    ly.lex.scheme.LineComment))
                and tokens[1][:3] in ('%%%', ';;;')):
                del d[d.position(b):d.position(b) + tokens[1].pos]


def remove_trailing_whitespace(cursor):
    """Removes whitespace from all lines in the cursor's range."""
    with cursor.document as d:
        for b in cursor.blocks():
            tokens = d.tokens(b)
            if tokens:
                t = tokens[-1]
                end = d.position(b) + t.end
                if isinstance(t, ly.lex.Space):
                    del d[end-len(t):end]
                elif not isinstance(t, ly.lex.String):
                    offset = len(t) - len(t.rstrip())
                    if offset:
                        del d[end-offset:end]


def reformat(cursor, indenter):
    """A do-it-all function improving the LilyPond source formatting."""
    break_indenters(cursor)
    indenter.indent(cursor)
    move_long_comments(cursor)
    remove_trailing_whitespace(cursor)