This file is indexed.

/usr/lib/python3/dist-packages/ly/indent.py is in python3-ly 0.9.4-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# 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.

"""
Indent and auto-indent.
"""

from __future__ import unicode_literals

import ly.lex.lilypond
import ly.lex.scheme


class Indenter(object):
    
    # variables
    indent_tabs = False     # use tabs for indent
    indent_width = 2        # amount of spaces if indent_tabs == False
    
    def __init__(self):
        pass
    
    def indent(self, cursor, indent_blank_lines=False):
        """Indent all lines in the cursor's range.
        
        If indent_blank_lines is True, the indent of blank lines is made 
        larger if necessary. If False (the default), the indent of blank 
        lines if not changed if it is shorter than it should be.
        
        """
        indents = ['']
        start_block, end_block = cursor.start_block(), cursor.end_block()
        in_range = False
        pline = None
        prev_indent = ''
        with cursor.document as d:
            for b in cursor.document:
                if b == start_block:
                    in_range = True
                
                line = Line(d, b)
                
                # handle indents of prev line
                if pline:
                    if pline.indent is not False:
                        prev_indent = pline.indent
                    if pline.indenters:
                        current_indent = indents[-1]
                        for align, indent in pline.indenters:
                            new_indent = current_indent
                            if align:
                                new_indent += ' ' * (align - len(prev_indent))
                            if indent:
                                new_indent += '\t' if self.indent_tabs else ' ' * self.indent_width
                            indents.append(new_indent)
                del indents[max(1, len(indents) - line.dedenters_start):]
                
                # if we may not change the indent just remember the current
                if line.indent is not False:
                    if not in_range:
                        indents[-1] = line.indent
                    elif not indent_blank_lines and line.isblank and indents[-1].startswith(line.indent):
                        pass # don't make shorter indents longer on blank lines
                    elif line.indent != indents[-1]:
                        d[d.position(b):d.position(b)+len(line.indent)] = indents[-1]
                del indents[max(1, len(indents) - line.dedenters_end):]
                
                if b == end_block:
                    break
                
                pline = line
    
    def increase_indent(self, cursor):
        """Manually add indent to all lines of cursor."""
        indent = '\t' if self.indent_tabs else ' ' * self.indent_width
        with cursor.document as d:
            for block in cursor.blocks():
                ins = d.position(block)
                tokens = d.tokens(block)
                if tokens and isinstance(tokens[0], ly.lex.Space):
                    tab_pos = tokens[0].rfind('\t')
                    if tab_pos != -1:
                        ins += tokens[0].pos + tab_pos + 1
                    else:
                        ins += tokens[0].end
                d[ins:ins] = indent
    
    def decrease_indent(self, cursor):
        """Manually remove one level of indent from all lines of cursor."""
        with cursor.document as d:
            for block in cursor.blocks():
                tokens = d.tokens(block)
                if tokens:
                    token = tokens[0]
                    if isinstance(token, ly.lex.Space):
                        space = token
                    else:
                        space = token[0:-len(token.lstrip())]
                    pos = d.position(block)
                    end = pos + len(space)
                    if '\t' in space and space.endswith(' '):
                        # strip alignment
                        del d[pos + space.rfind('\t') + 1 : end]
                    elif space.endswith('\t'):
                        # just strip one tab
                        del d[end - 1]
                    elif space.endswith(' ' * self.indent_width):
                        del d[end - self.indent_width: end]
                    else:
                        del d[pos:end]
    
    def get_indent(self, document, block):
        """Return the indent the block currently has.
        
        Returns False if the block is not indentable, e.g. when it is part of
        a multiline string.
        
        """
        return Line(document, block).indent
    
    def compute_indent(self, document, block):
        """Return the indent the specified block should have.
        
        Returns False if the block is not indentable, e.g. when it is part of
        a multiline string.
        
        This method is used to determine the indent of one line, and just 
        looks to previous lines, copying the indent of the line where the 
        current indent depth starts, and/or adding a level of indent or 
        alignment space.
        
        Use this method only for one line or the first of a group you're 
        indenting.
        
        """
        line = Line(document, block)
        if line.indent is False:
            return False
        depth = line.dedenters_start
        blocks = document.blocks_backward(document.previous_block(block))
        align, indent = None, False
        for b in blocks:
            line = Line(document, b)
            indents = len(line.indenters)
            if 0 <= depth < indents:
                # we found the indent token
                index = indents - depth - 1
                align, indent = line.indenters[index]
                break
            depth -= indents
            depth += line.dedenters_end
            if depth == 0:
                # same indent as this line
                break
            depth += line.dedenters_start
        else:
            return ""
        
        # here we arrive after 'break'
        i = line.indent
        if i is False:
            for b in blocks:
                i = Line(document, b).indent
                if i is not False:
                    break
            else:
                i = ""
        if align:
            i += ' ' * (align - len(i))
        if indent:
            i += '\t' if self.indent_tabs else ' ' * self.indent_width
        return i


class Line(object):
    """Brings together all relevant information about a line (block)."""
    def __init__(self, document, block):
        """Initialize with a block (line) of the document.
        
        After init, the following attributes are set:
        
        indent
        
        The indent the current line has. This is a string containing 
        whitespace (i.e. spaces and/or tabs) which can be empty. A special 
        case is False, which means the current line is not indentable, e.g. 
        it is a multiline string and should never be automatically be 
        re-indented.
        
        
        isblank
        
        True if the line is empty or white-space only. It is False when the 
        indent attribute is also False (e.g. when the line is part of a 
        multiline string).
        
        
        dedenters_start
        
        The number of dedent tokens that should cause the indenter to go a 
        level up.
        
        
        dedenters_end
        
        The number of dedent tokens that should cause the next line to go a 
        level up.
        
        
        indenters
        
        A list of tuples (align, indent). Each item corresponds with an 
        indent that starts on the line. The align value (integer) determines 
        the position the next line should be padded to with spaces, 0 or 
        None means no alignment. The indent value (bool) specifies if there 
        should a new indent level be added (a tab or some amount of spaces).
        
        """
        state = document.state(block)
        tokens = document.tokens(block)
        
        # are we in a multi-line string?
        if isinstance(state.parser(), (
            ly.lex.lilypond.ParseString,
            ly.lex.scheme.ParseString,
            )):
            self.indent = False
            self.isblank = False
        # or a multi-line comment?
        elif isinstance(state.parser(), (
            ly.lex.lilypond.ParseBlockComment,
            ly.lex.scheme.ParseBlockComment,
            )):
            # do allow indent the last line of a block comment if it only
            # contains space
            if tokens and isinstance(tokens[0], ly.lex.BlockCommentEnd):
                self.indent = ""
            elif (len(tokens) > 1
                  and isinstance(tokens[0], ly.lex.BlockComment)
                  and isinstance(tokens[1], ly.lex.BlockCommentEnd)
                  and tokens[0].isspace()):
                self.indent = tokens[0]
            else:
                self.indent = False
            self.isblank = False
        elif tokens and isinstance(tokens[0], ly.lex.Space):
            self.indent = tokens[0]
            self.isblank = len(tokens) == 1
        else:
            self.indent = ""
            self.isblank = not tokens

        find_dedenters = True
        self.dedenters_start = 0
        self.dedenters_end = 0
        
        # quickly iter over the tokens, collecting the indent tokens and 
        # possible stuff to align to after the indent tokens
        indenters = []
        for t in tokens:
            if isinstance(t, ly.lex.Indent):
                find_dedenters = False
                if indenters:
                    indenters[-1].append(t)
                indenters.append([t])
            elif isinstance(t, ly.lex.Dedent):
                if find_dedenters and not isinstance(t, ly.lex.scheme.CloseParen):
                    self.dedenters_start += 1
                else:
                    find_dedenters = False
                    if indenters:
                        indenters.pop()
                    else:
                        self.dedenters_end += 1
            elif not isinstance(t, ly.lex.Space):
                find_dedenters = False
                if indenters:
                    indenters[-1].append(t)
        
        # now analyse the indent tokens that are not closed on the same line
        # and determine how the next line should be indented
        self.indenters = []
        for indent in indenters:
            token, rest = indent[0], indent[1:]
            if isinstance(token, ly.lex.scheme.OpenParen):
                if len(rest) > 1 and self.is_alignable_scheme_keyword(rest[0]):
                    align, indent = rest[1].pos, False
                elif len(rest) == 1 and not isinstance(rest[0], ly.lex.Comment):
                    align, indent = rest[0].pos, False
                else:
                    align, indent = token.pos, True
            elif rest and not isinstance(rest[0], ly.lex.Comment):
                align, indent = rest[0].pos, False
            else:
                align, indent = None, True
            self.indenters.append((align, indent))
    
    def is_alignable_scheme_keyword(self, token):
        """Return True if token is an alignable Scheme word like "if", etc."""
        return isinstance(token, ly.lex.scheme.Word) and token in (

            # Scheme commands that can have one argument on the same line and 
            # then want the next arguments on the next lines at the same 
            # position.
            'if', 'and', 'or', 'set!',
            '=', '<', '<=', '>', '>=',
            'eq?', 'eqv?', 'equal?',
            'filter',
        )