/usr/lib/python3/dist-packages/ly/indent.py is in python3-ly 0.9.3-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',
)
|