Skip to content

Commit 832e855

Browse files
authored
Fix array item removal (#409)
1 parent f0c50e0 commit 832e855

File tree

3 files changed

+101
-9
lines changed

3 files changed

+101
-9
lines changed

tests/test_items.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,3 +1032,56 @@ def test_removal_of_arrayitem_with_extra_whitespace():
10321032
docstr = doc.as_string()
10331033
parse(docstr)
10341034
assert docstr == expected
1035+
1036+
1037+
def test_badly_formatted_array_and_item_removal():
1038+
expected = """
1039+
x = [
1040+
'0'#a
1041+
,#b
1042+
1043+
# comment
1044+
'1' #c
1045+
, #d
1046+
# another comment
1047+
'2'
1048+
# yet another comment
1049+
,'3', # f
1050+
# comments here
1051+
'4'#g
1052+
# comments there
1053+
,'5' #h
1054+
# comments everywhere
1055+
# so many comments
1056+
,'6' ,
1057+
# you get a comment
1058+
# you get a comment
1059+
'7' ,#j
1060+
# everybody gets a comment!!!
1061+
"8"#c
1062+
,"9", \n "10"
1063+
]
1064+
"""
1065+
assert expected == parse(expected).as_string()
1066+
for i in range(11):
1067+
doc = parse(expected)
1068+
x = doc["x"]
1069+
assert isinstance(x, Array)
1070+
x.remove(str(i))
1071+
parse(doc.as_string())
1072+
1073+
1074+
def test_array_item_removal_newline_restore_next():
1075+
expected = "x = [\n '0',\n '2'\n]"
1076+
1077+
docstr = "x = [\n '0',\n '1','2'\n]"
1078+
doc = parse(docstr)
1079+
doc["x"].remove("1")
1080+
assert doc.as_string() == expected
1081+
parse(doc.as_string())
1082+
1083+
docstr = "x = [\n '0',\n '1', '2'\n]"
1084+
doc = parse(docstr)
1085+
doc["x"].remove("1")
1086+
assert doc.as_string() == expected
1087+
parse(doc.as_string())

tomlkit/items.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1138,11 +1138,13 @@ def _group_values(self, value: list[Item]) -> list[_ArrayItemGroup]:
11381138
"""Group the values into (indent, value, comma, comment) tuples"""
11391139
groups = []
11401140
this_group = _ArrayItemGroup()
1141+
start_new_group = False
11411142
for item in value:
11421143
if isinstance(item, Whitespace):
1143-
if "," not in item.s:
1144+
if "," not in item.s or start_new_group:
11441145
groups.append(this_group)
11451146
this_group = _ArrayItemGroup(indent=item)
1147+
start_new_group = False
11461148
else:
11471149
if this_group.value is None:
11481150
# when comma is met and no value is provided, add a dummy Null
@@ -1152,6 +1154,8 @@ def _group_values(self, value: list[Item]) -> list[_ArrayItemGroup]:
11521154
if this_group.value is None:
11531155
this_group.value = Null()
11541156
this_group.comment = item
1157+
# Comments are the last item in a group.
1158+
start_new_group = True
11551159
elif this_group.value is None:
11561160
this_group.value = item
11571161
else:
@@ -1399,6 +1403,7 @@ def __delitem__(self, key: int | slice):
13991403
if not isinstance(key, slice):
14001404
raise IndexError("list index out of range") from e
14011405
else:
1406+
group_rm = self._value[idx]
14021407
del self._value[idx]
14031408
if (
14041409
idx == 0
@@ -1408,6 +1413,44 @@ def __delitem__(self, key: int | slice):
14081413
):
14091414
# Remove the indentation of the first item if not newline
14101415
self._value[idx].indent = None
1416+
comma_in_indent = (
1417+
group_rm.indent is not None and "," in group_rm.indent.s
1418+
)
1419+
comma_in_comma = group_rm.comma is not None and "," in group_rm.comma.s
1420+
if comma_in_indent and comma_in_comma:
1421+
# Removed group had both commas. Add one to the next group.
1422+
group = self._value[idx] if len(self._value) > idx else None
1423+
if group is not None:
1424+
if group.indent is None:
1425+
group.indent = Whitespace(",")
1426+
elif "," not in group.indent.s:
1427+
# Insert the comma after the newline
1428+
try:
1429+
newline_index = group.indent.s.index("\n")
1430+
group.indent._s = (
1431+
group.indent.s[: newline_index + 1]
1432+
+ ","
1433+
+ group.indent.s[newline_index + 1 :]
1434+
)
1435+
except ValueError:
1436+
group.indent._s = "," + group.indent.s
1437+
elif not comma_in_indent and not comma_in_comma:
1438+
# Removed group had no commas. Remove the next comma found.
1439+
for j in range(idx, len(self._value)):
1440+
group = self._value[j]
1441+
if group.indent is not None and "," in group.indent.s:
1442+
group.indent._s = group.indent.s.replace(",", "", 1)
1443+
break
1444+
if group_rm.indent is not None and "\n" in group_rm.indent.s:
1445+
# Restore the removed group's newline onto the next group
1446+
# if the next group does not have a newline.
1447+
# i.e. the two were on the same line
1448+
group = self._value[idx] if len(self._value) > idx else None
1449+
if group is not None and (
1450+
group.indent is None or "\n" not in group.indent.s
1451+
):
1452+
group.indent = group_rm.indent
1453+
14111454
if len(self._value) > 0:
14121455
v = self._value[-1]
14131456
if not v.is_whitespace():

tomlkit/parser.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -595,15 +595,11 @@ def _parse_array(self) -> Array:
595595
# consume comma
596596
if prev_value and self._current == ",":
597597
self.inc(exception=UnexpectedEofError)
598-
# Check if the previous item is Whitespace
599-
if isinstance(elems[-1], Whitespace) and " " in elems[-1].s:
600-
# Preserve the previous whitespace
601-
comma = Whitespace(elems[-1].s + ",")
602-
# Remove the replaced item
603-
del elems[-1]
598+
# If the previous item is Whitespace, add to it
599+
if isinstance(elems[-1], Whitespace):
600+
elems[-1]._s = elems[-1].s + ","
604601
else:
605-
comma = Whitespace(",")
606-
elems.append(comma)
602+
elems.append(Whitespace(","))
607603
prev_value = False
608604
continue
609605

0 commit comments

Comments
 (0)