1
# vim: noexpandtab:ts=4:sw=4
2
# This file is part of ReText
3
# Copyright: Maurice van der Pot 2014
4
# License: GNU GPL v2 or higher
7
from ReText import DOCTYPE_MARKDOWN, DOCTYPE_REST
9
from PyQt5.QtGui import QTextCursor
11
LARGER_THAN_ANYTHING = sys.maxsize
14
def __init__(self, block=None, text=None, separatorline=False, paddingchar=' '):
17
self.separatorline = separatorline
18
self.paddingchar = paddingchar
21
return "<Row '%s' %s '%s'>" % (self.text, self.separatorline, self.paddingchar)
23
def _getTableLines(doc, pos, docType):
24
startblock = doc.findBlock(pos)
26
offset = pos - startblock.position()
28
rows = [ Row(block = startblock,
29
text = startblock.text()) ]
31
block = startblock.previous()
32
while any(c in block.text() for c in '+|'):
33
rows.insert(0, Row(block = block,
36
block = block.previous()
38
block = startblock.next()
39
while any(c in block.text() for c in '+|'):
40
rows.append(Row(block = block,
44
if docType == DOCTYPE_MARKDOWN:
45
for i, row in enumerate(rows):
47
row.separatorline = True
49
elif docType == DOCTYPE_REST:
50
for i, row in enumerate(rows):
51
if i & 1 == 0: # i is even
52
row.separatorline = True
53
row.paddingchar = '=' if (i == 2) else '-'
54
row.text = row.text.replace('+', '|')
56
return rows, editedlineindex, offset
58
def _sortaUndoEdit(rows, editedlineindex, editsize):
59
aftertext = rows[editedlineindex].text
61
beforetext = ' ' * -editsize + aftertext
63
beforetext = aftertext[editsize:]
65
rows[editedlineindex].text = beforetext
67
def _determineRoomInCell(row, edge, shrinking, startposition=0):
68
if edge >= len(row.text) or row.text[edge] != '|':
69
room = LARGER_THAN_ANYTHING
74
for i in range(edge - 1, startposition - 1, -1):
75
if row.text[i] == '|':
78
if row.text[i] == row.paddingchar and afterContent:
86
# do not shrink separator cells below 3
87
room = max(0, cellwidth - 3)
89
# start expanding the cell if only the space for a right-align marker is left
90
room = max(0, cellwidth - 1)
96
def _performShift(row, rowShift, edge, shift):
99
if len(row.text) > edge and row.text[edge] == '|' and rowShift != shift:
100
editsize = -(rowShift - shift)
103
# Insert one position further to the left on separator lines, because
104
# there may be a space (for esthetical reasons) or an alignment marker
105
# on the last position before the edge and that should stay next to the
107
if row.separatorline:
110
editlist.append((edge, editsize))
112
return editlist, rowShift
114
def _determineNextEdge(rows, rowShifts, offset):
116
for row, rowShift in zip(rows, rowShifts):
118
edge = row.text.find('|', offset)
119
if edge != -1 and (nextedge == None or edge < nextedge):
123
def _determineEditLists(rows, editedlineindex, offset, editsize):
124
rowShifts = [0 for _ in rows]
125
rowShifts[editedlineindex] = editsize
127
editLists = [[] for _ in rows]
129
currentedge = _determineNextEdge(rows, rowShifts, offset)
136
leastLeftShift = min((-rowShift + _determineRoomInCell(row, currentedge, True)
137
for row, rowShift in zip(rows, rowShifts)))
139
shift = max(editsize, -leastLeftShift)
142
room = _determineRoomInCell(rows[editedlineindex], currentedge, False, offset)
143
shift = max(0, editsize - room)
145
for i, row in enumerate(rows):
146
editList, newRowShift = _performShift(row, rowShifts[i], currentedge, shift)
147
rowShifts[i] = newRowShift
148
editLists[i].extend(editList)
150
currentedge = _determineNextEdge(rows, rowShifts, currentedge + 1)
155
def _performEdits(cursor, rows, editLists, linewithoffset, offset):
156
cursor.joinPreviousEditBlock()
157
for i, (row, editList) in enumerate(zip(rows, editLists)):
159
for editpos, editsize in sorted(editList, reverse=True):
161
if i == linewithoffset:
164
cursor.setPosition(row.block.position() + editpos)
166
cursor.insertText(editsize * row.paddingchar)
168
for _ in range(-editsize):
169
cursor.deletePreviousChar()
170
cursor.endEditBlock()
172
def adjustTableToChanges(doc, pos, editsize, docType):
173
if docType in (DOCTYPE_MARKDOWN, DOCTYPE_REST):
174
rows, editedlineindex, offset = _getTableLines(doc, pos, docType)
176
_sortaUndoEdit(rows, editedlineindex, editsize)
178
editLists = _determineEditLists(rows, editedlineindex, offset, editsize)
180
cursor = QTextCursor(doc)
181
_performEdits(cursor, rows, editLists, editedlineindex, editsize)