Fix DB Relayout
This commit is contained in:
parent
195a11ec08
commit
47b0936a75
3 changed files with 325 additions and 24 deletions
|
|
@ -94,6 +94,7 @@ from util.wolfdawn import names as wolf_names
|
|||
from util.wolfdawn import codes as wolf_codes
|
||||
from util.wolfdawn import db_classify as wolf_db
|
||||
from util.wolfdawn import wrap_search as wolf_ws
|
||||
import util.dazedwrap as dazedwrap
|
||||
|
||||
from util.paths import PROJECT_ROOT
|
||||
from util.project_scanner import (
|
||||
|
|
@ -2667,7 +2668,8 @@ class WolfWorkflowTab(QWidget):
|
|||
idx = self._wrap_mode_combo.findData(saved_mode)
|
||||
self._wrap_mode_combo.setCurrentIndex(idx if idx >= 0 else 0)
|
||||
self._wrap_mode_combo.setToolTip(
|
||||
"Relayout: wolf names-wrap / JP slot geometry (cell width + max lines).\n"
|
||||
"Relayout: names use wolf names-wrap; DB/dialogue wrap to cell width "
|
||||
"and shrink \\f[N] to fit Max lines.\n"
|
||||
"Manual: force wrap width and body font; emphasis \\f[N] scales with the body."
|
||||
)
|
||||
self._wrap_mode_combo.currentIndexChanged.connect(self._on_wrap_mode_changed)
|
||||
|
|
@ -2706,8 +2708,10 @@ class WolfWorkflowTab(QWidget):
|
|||
self._wrap_max_lines_spin.setValue(0)
|
||||
self._wrap_max_lines_spin.setFixedWidth(70)
|
||||
self._wrap_max_lines_spin.setToolTip(
|
||||
"wolf names-wrap --lines (0 = each entry's JP line count). "
|
||||
"Saved as maxLines in wolfdawn-roles.json."
|
||||
"Max soft lines after Relayout. For names: wolf names-wrap --lines "
|
||||
"(0 = each entry's JP line count). For DB/dialogue: wrap to width, "
|
||||
"then shrink \\f[N] until the line count fits. "
|
||||
"Saved per sheet in wolfdawn-roles.json."
|
||||
)
|
||||
self._wrap_max_lines_spin.valueChanged.connect(self._on_wrap_max_lines_changed)
|
||||
relayout_row.addWidget(w_lbl)
|
||||
|
|
@ -3081,19 +3085,60 @@ class WolfWorkflowTab(QWidget):
|
|||
text = self._current_wrap_hit.text
|
||||
width = self._active_wrap_width()
|
||||
body_font = self._wrap_font_value() if self._wrap_mode() == "manual" else None
|
||||
# Manual mode: preview the scaled fonts Wrap would write (document unchanged).
|
||||
# Manual: preview scaled fonts. Relayout + max lines: preview fit-to-box.
|
||||
preview_src = text
|
||||
preview_width = width
|
||||
if body_font is not None and preview_src.strip():
|
||||
preview_src, _ = wolf_codes.scale_font_sizes(preview_src, body_font)
|
||||
summary = wolf_ws.wrap_preview_summary(preview_src, width)
|
||||
elif self._wrap_mode() == "relayout" and width > 0:
|
||||
max_lines = self._wrap_max_lines_value()
|
||||
if max_lines > 0 and preview_src.strip():
|
||||
box_font = None
|
||||
line = None
|
||||
if self._current_wrap_doc and self._current_wrap_hit_id:
|
||||
line = wolf_ws.locate_line(
|
||||
self._current_wrap_doc, self._current_wrap_hit_id
|
||||
)
|
||||
if isinstance(line, dict):
|
||||
source = line.get("source")
|
||||
if isinstance(source, str) and wolf_codes.detect_font_sizes(source):
|
||||
box_font = wolf_codes.infer_base_font_size(source)
|
||||
preview_src, _ = wolf_ws.fit_text_to_box(
|
||||
preview_src, width, max_lines=max_lines, box_font=box_font
|
||||
)
|
||||
# Soft breaks are already final; don't reflow at the narrow cell width.
|
||||
preview_width = max(
|
||||
width,
|
||||
dazedwrap.max_line_visible_length(preview_src),
|
||||
)
|
||||
summary = wolf_ws.wrap_preview_summary(preview_src, preview_width)
|
||||
preview_html = wolf_ws.format_wrap_preview_html(
|
||||
text, width, body_font=body_font
|
||||
preview_src if self._wrap_mode() == "relayout" else text,
|
||||
preview_width,
|
||||
body_font=body_font,
|
||||
)
|
||||
info = wolf_ws.wrap_preview_info(preview_src, width)
|
||||
info = wolf_ws.wrap_preview_info(preview_src, preview_width)
|
||||
if hasattr(self, "_wrap_preview_label"):
|
||||
if summary:
|
||||
soft = wolf_ws.count_soft_lines(preview_src)
|
||||
max_lines = self._wrap_max_lines_value()
|
||||
if (
|
||||
self._wrap_mode() == "relayout"
|
||||
and max_lines > 0
|
||||
and soft > 0
|
||||
):
|
||||
summary = f"{summary} · {soft}/{max_lines} soft lines"
|
||||
self._wrap_preview_label.setText(summary)
|
||||
color = "#ce9178" if info.get("needs_wrap") else "#8fbc8f"
|
||||
over_lines = (
|
||||
self._wrap_mode() == "relayout"
|
||||
and max_lines > 0
|
||||
and soft > max_lines
|
||||
)
|
||||
color = (
|
||||
"#ce9178"
|
||||
if info.get("needs_wrap") or over_lines
|
||||
else "#8fbc8f"
|
||||
)
|
||||
self._wrap_preview_label.setStyleSheet(
|
||||
f"color:{color};font-size:11px;background:transparent;"
|
||||
)
|
||||
|
|
@ -3103,7 +3148,11 @@ class WolfWorkflowTab(QWidget):
|
|||
"color:#858585;font-size:11px;background:transparent;"
|
||||
)
|
||||
self._wrap_preview.setHtml(preview_html)
|
||||
border = "#ce9178" if info.get("needs_wrap") else "#007acc"
|
||||
over_lines = False
|
||||
if self._wrap_mode() == "relayout":
|
||||
max_lines = self._wrap_max_lines_value()
|
||||
over_lines = max_lines > 0 and wolf_ws.count_soft_lines(preview_src) > max_lines
|
||||
border = "#ce9178" if info.get("needs_wrap") or over_lines else "#007acc"
|
||||
self._wrap_preview.setStyleSheet(
|
||||
"QTextEdit{background-color:#1e1e1e;color:#d4d4d4;border:1px solid #3c3c3c;"
|
||||
f"border-left:3px solid {border};padding:6px;}}"
|
||||
|
|
@ -3284,6 +3333,7 @@ class WolfWorkflowTab(QWidget):
|
|||
self._current_wrap_doc,
|
||||
self._current_wrap_hit_id,
|
||||
width,
|
||||
max_lines=max_lines or None,
|
||||
)
|
||||
if self._current_wrap_hit:
|
||||
self._remember_sheet_width(
|
||||
|
|
@ -3302,7 +3352,19 @@ class WolfWorkflowTab(QWidget):
|
|||
self._current_wrap_doc = doc
|
||||
if line is not None:
|
||||
self._current_wrap_text = str(line.get("text") or "")
|
||||
msg = f"Wrapped line at width {width}." if changed else "Line already fits at this width."
|
||||
if changed:
|
||||
if max_lines > 0:
|
||||
msg = (
|
||||
f"Relayout at width {width}, max {max_lines} line(s) "
|
||||
f"(shrink \\f if needed)."
|
||||
)
|
||||
else:
|
||||
msg = f"Wrapped line at width {width}."
|
||||
else:
|
||||
msg = (
|
||||
f"Already fits width {width}"
|
||||
+ (f" / {max_lines} line(s)." if max_lines > 0 else ".")
|
||||
)
|
||||
self._wrap_status_label.setText(msg + " Inject all from Step 7 when ready.")
|
||||
self._log(f"{'✅' if changed else 'ℹ'} {msg}")
|
||||
self._update_wrap_preview()
|
||||
|
|
@ -3374,12 +3436,19 @@ class WolfWorkflowTab(QWidget):
|
|||
self._current_wrap_doc,
|
||||
hit,
|
||||
width,
|
||||
max_lines=max_lines or None,
|
||||
)
|
||||
self._remember_sheet_width(
|
||||
hit.sheet_name, width, hit.json_file, max_lines=max_lines or None
|
||||
)
|
||||
scope = wolf_ws.scope_label(hit)
|
||||
msg = f"Wrapped {count} line(s) in {scope} at width {width}."
|
||||
if max_lines > 0:
|
||||
msg = (
|
||||
f"Relayout {count} line(s) in {scope} "
|
||||
f"(width {width}, max {max_lines} lines)."
|
||||
)
|
||||
else:
|
||||
msg = f"Wrapped {count} line(s) in {scope} at width {width}."
|
||||
self._wrap_status_label.setText(msg + " Inject all from Step 7 when ready.")
|
||||
self._log(f"✅ {msg}")
|
||||
self._reload_wrap_hit_editor(hit, width)
|
||||
|
|
|
|||
|
|
@ -281,5 +281,97 @@ class TestEventScopeWrap(unittest.TestCase):
|
|||
self.assertEqual(ws.wrap_overflow_in_scope(path, doc, hit, 34), 1)
|
||||
|
||||
|
||||
class TestFitTextToBox(unittest.TestCase):
|
||||
"""DB Relayout: wrap + font shrink until soft line count fits."""
|
||||
|
||||
RUMOR = (
|
||||
'\\f[14]"I heard there\'s a super hot female adventurer who\n'
|
||||
"came to town recently!? Maybe I'll catch a glimpse\n"
|
||||
'of her at the \\c[21]\\f[15]tavern\\c[19]\\f[14]!"'
|
||||
)
|
||||
|
||||
def test_count_soft_lines(self):
|
||||
self.assertEqual(ws.count_soft_lines("a\nb\nc"), 3)
|
||||
self.assertEqual(ws.count_soft_lines("one line"), 1)
|
||||
self.assertEqual(ws.count_soft_lines(""), 0)
|
||||
|
||||
def test_max_lines_zero_wraps_only(self):
|
||||
fitted, changed = ws.fit_text_to_box(self.RUMOR, 44, max_lines=0)
|
||||
self.assertTrue(changed)
|
||||
self.assertEqual(ws.count_soft_lines(fitted), 3)
|
||||
self.assertIn(r"\f[14]", fitted)
|
||||
|
||||
def test_max_lines_shrinks_font_and_fits(self):
|
||||
fitted, changed = ws.fit_text_to_box(self.RUMOR, 44, max_lines=2)
|
||||
self.assertTrue(changed)
|
||||
self.assertLessEqual(ws.count_soft_lines(fitted), 2)
|
||||
from util.wolfdawn import codes as wolf_codes
|
||||
|
||||
self.assertLess(wolf_codes.infer_base_font_size(fitted), 14)
|
||||
self.assertIn("tavern", fitted)
|
||||
|
||||
def test_box_font_from_jp_source_scales_width(self):
|
||||
"""Native box px from JP source lets more cells fit when \\f shrinks."""
|
||||
from util.wolfdawn import codes as wolf_codes
|
||||
|
||||
fitted, changed = ws.fit_text_to_box(
|
||||
self.RUMOR, 44, max_lines=2, box_font=18
|
||||
)
|
||||
self.assertTrue(changed)
|
||||
self.assertLessEqual(ws.count_soft_lines(fitted), 2)
|
||||
# With box_font=18, body can stay larger than when native=14.
|
||||
self.assertGreaterEqual(wolf_codes.infer_base_font_size(fitted), 12)
|
||||
fitted2, changed2 = ws.fit_text_to_box(
|
||||
fitted, 44, max_lines=2, box_font=18
|
||||
)
|
||||
self.assertFalse(changed2)
|
||||
self.assertEqual(fitted2, fitted)
|
||||
|
||||
def test_wrap_hit_in_file_honors_max_lines(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = Path(tmp) / "DataBase.project.json"
|
||||
source = (
|
||||
"「最近町に来た女冒険者が激マブらしいぞ!?\n"
|
||||
" \\c[21]\\f[20]酒場\\c[19]\\f[18]に行ったら会えるかな!」"
|
||||
)
|
||||
doc = {
|
||||
"kind": "db",
|
||||
"file": "DataBase.project",
|
||||
"groups": [
|
||||
{
|
||||
"typeName": RUMOR_SHEET,
|
||||
"lines": [
|
||||
{
|
||||
"row": 21,
|
||||
"field": 25,
|
||||
"fieldName": GATEKEEPER_FIELD,
|
||||
"source": source,
|
||||
"text": self.RUMOR,
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
path.write_text(json.dumps(doc, ensure_ascii=False), encoding="utf-8")
|
||||
hit_id = {
|
||||
"kind": "db",
|
||||
"sheet_name": RUMOR_SHEET,
|
||||
"row": 21,
|
||||
"field_name": GATEKEEPER_FIELD,
|
||||
}
|
||||
self.assertTrue(
|
||||
ws.wrap_hit_in_file(path, doc, hit_id, 44, max_lines=2)
|
||||
)
|
||||
line = ws.locate_line(doc, hit_id)
|
||||
self.assertIsNotNone(line)
|
||||
self.assertLessEqual(ws.count_soft_lines(str(line["text"])), 2)
|
||||
from util.wolfdawn import codes as wolf_codes
|
||||
|
||||
# JP body is \\f[18]; shrink should land near 12, not floor at 8.
|
||||
self.assertGreaterEqual(
|
||||
wolf_codes.infer_base_font_size(str(line["text"])), 12
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -633,6 +633,110 @@ def wrap_line_in_doc(line: dict[str, Any], width: int) -> bool:
|
|||
return True
|
||||
|
||||
|
||||
def count_soft_lines(text: str) -> int:
|
||||
"""Number of soft-wrapped lines in *text* (at least 1 for non-empty)."""
|
||||
if not isinstance(text, str) or not text:
|
||||
return 0
|
||||
norm = text.replace("\r\n", "\n").replace("\r", "\n")
|
||||
return max(1, len(norm.split("\n")))
|
||||
|
||||
|
||||
def fit_text_to_box(
|
||||
text: str,
|
||||
width: int,
|
||||
*,
|
||||
max_lines: int | None = None,
|
||||
min_font: int = 8,
|
||||
box_font: int | None = None,
|
||||
) -> tuple[str, bool]:
|
||||
"""Reflow *text* to *width*, then shrink ``\\f[N]`` until it fits *max_lines*.
|
||||
|
||||
Used by Relayout for DB / dialogue sheets. ``max_lines`` ``None``/``0`` means
|
||||
wrap only (no font shrink).
|
||||
|
||||
Wolf message boxes are a fixed pixel width. Shrinking ``\\f[N]`` lets more
|
||||
halfwidth cells fit on a line, so each shrink pass reflows at
|
||||
``round(width * box_font / current_font)`` (same idea as ``wolf relayout`` /
|
||||
``desc-relayout``). *box_font* defaults to the text's current body size
|
||||
(or 18 when there is no ``\\f`` yet).
|
||||
"""
|
||||
from util.wolfdawn import codes as wolf_codes
|
||||
|
||||
if not isinstance(text, str) or not text.strip() or width <= 0:
|
||||
return text if isinstance(text, str) else "", False
|
||||
|
||||
limit = int(max_lines) if max_lines is not None else 0
|
||||
sizes = wolf_codes.detect_font_sizes(text)
|
||||
if box_font is not None and int(box_font) > 0:
|
||||
native = int(box_font)
|
||||
elif sizes:
|
||||
native = wolf_codes.infer_base_font_size(text)
|
||||
else:
|
||||
native = 18
|
||||
native = max(1, native)
|
||||
min_font = max(1, int(min_font))
|
||||
|
||||
def _effective_width(current_font: int) -> int:
|
||||
cur = max(1, int(current_font))
|
||||
return max(1, int(round(width * native / cur)))
|
||||
|
||||
current = wolf_codes.infer_base_font_size(text, default=native) if sizes else native
|
||||
new_text = wrap_line_text(text, _effective_width(current) if sizes else width)
|
||||
if limit <= 0:
|
||||
return new_text, new_text != text
|
||||
|
||||
# No \\f yet and already over budget: introduce the native body size first.
|
||||
if not sizes and count_soft_lines(new_text) > limit:
|
||||
new_text, _ = wolf_codes.scale_font_sizes(new_text, native)
|
||||
current = native
|
||||
new_text = wrap_line_text(new_text, _effective_width(current))
|
||||
|
||||
guard = 0
|
||||
while count_soft_lines(new_text) > limit and guard < 64:
|
||||
guard += 1
|
||||
current = wolf_codes.infer_base_font_size(new_text, default=current)
|
||||
if current <= min_font:
|
||||
break
|
||||
target = current - 1
|
||||
new_text, _ = wolf_codes.scale_font_sizes(new_text, target)
|
||||
current = target
|
||||
new_text = wrap_line_text(new_text, _effective_width(current))
|
||||
|
||||
return new_text, new_text != text
|
||||
|
||||
|
||||
def apply_relayout_to_line_dict(
|
||||
line: dict[str, Any],
|
||||
width: int,
|
||||
*,
|
||||
max_lines: int | None = None,
|
||||
min_font: int = 8,
|
||||
box_font: int | None = None,
|
||||
) -> bool:
|
||||
"""Apply Relayout fit to one ``{text}`` leaf. Returns True if changed."""
|
||||
from util.wolfdawn import codes as wolf_codes
|
||||
|
||||
text = line.get("text")
|
||||
if not isinstance(text, str) or not text.strip() or width <= 0:
|
||||
return False
|
||||
native = box_font
|
||||
if native is None:
|
||||
source = line.get("source")
|
||||
if isinstance(source, str) and wolf_codes.detect_font_sizes(source):
|
||||
native = wolf_codes.infer_base_font_size(source)
|
||||
new_text, changed = fit_text_to_box(
|
||||
text,
|
||||
width,
|
||||
max_lines=max_lines,
|
||||
min_font=min_font,
|
||||
box_font=native,
|
||||
)
|
||||
if not changed:
|
||||
return False
|
||||
line["text"] = new_text
|
||||
return True
|
||||
|
||||
|
||||
def apply_manual_font_and_wrap(
|
||||
text: str,
|
||||
width: int,
|
||||
|
|
@ -925,7 +1029,24 @@ def scope_stats(
|
|||
return ScopeStats(label=scope_label(hit), total=total, overflow=overflow)
|
||||
|
||||
|
||||
def _wrap_overflow_in_event_file(path: Path, doc: dict[str, Any], width: int) -> int:
|
||||
def _line_needs_relayout(text: str, width: int, max_lines: int | None) -> bool:
|
||||
"""True if width wrap or max-lines fit would change *text*."""
|
||||
if line_needs_wrap(text, width):
|
||||
return True
|
||||
limit = int(max_lines) if max_lines is not None else 0
|
||||
if limit <= 0:
|
||||
return False
|
||||
wrapped = wrap_line_text(text, width)
|
||||
return count_soft_lines(wrapped) > limit or wrapped != text
|
||||
|
||||
|
||||
def _wrap_overflow_in_event_file(
|
||||
path: Path,
|
||||
doc: dict[str, Any],
|
||||
width: int,
|
||||
*,
|
||||
max_lines: int | None = None,
|
||||
) -> int:
|
||||
"""Wrap overflowing dialogue lines in one map or CommonEvent JSON file."""
|
||||
if doc.get("kind") not in ("map", "common"):
|
||||
return 0
|
||||
|
|
@ -935,16 +1056,22 @@ def _wrap_overflow_in_event_file(path: Path, doc: dict[str, Any], width: int) ->
|
|||
text = line.get("text")
|
||||
if not isinstance(text, str) or not text.strip():
|
||||
continue
|
||||
if not line_needs_wrap(text, width):
|
||||
if not _line_needs_relayout(text, width, max_lines):
|
||||
continue
|
||||
if wrap_line_in_doc(line, width):
|
||||
if apply_relayout_to_line_dict(line, width, max_lines=max_lines):
|
||||
changed += 1
|
||||
if changed:
|
||||
save_document(path, doc)
|
||||
return changed
|
||||
|
||||
|
||||
def _wrap_overflow_in_gamedat(path: Path, doc: dict[str, Any], width: int) -> int:
|
||||
def _wrap_overflow_in_gamedat(
|
||||
path: Path,
|
||||
doc: dict[str, Any],
|
||||
width: int,
|
||||
*,
|
||||
max_lines: int | None = None,
|
||||
) -> int:
|
||||
if doc.get("kind") != "gamedat":
|
||||
return 0
|
||||
changed = 0
|
||||
|
|
@ -954,9 +1081,9 @@ def _wrap_overflow_in_gamedat(path: Path, doc: dict[str, Any], width: int) -> in
|
|||
text = line.get("text")
|
||||
if not isinstance(text, str) or not text.strip():
|
||||
continue
|
||||
if not line_needs_wrap(text, width):
|
||||
if not _line_needs_relayout(text, width, max_lines):
|
||||
continue
|
||||
if wrap_line_in_doc(line, width):
|
||||
if apply_relayout_to_line_dict(line, width, max_lines=max_lines):
|
||||
changed += 1
|
||||
if changed:
|
||||
save_document(path, doc)
|
||||
|
|
@ -968,8 +1095,15 @@ def wrap_overflow_in_scope(
|
|||
doc: dict[str, Any],
|
||||
hit: WrapHit | dict[str, Any],
|
||||
width: int,
|
||||
*,
|
||||
max_lines: int | None = None,
|
||||
) -> int:
|
||||
"""Wrap every overflowing line in the same group as *hit*."""
|
||||
"""Wrap overflowing lines in the same group/file as *hit*.
|
||||
|
||||
For DB / map / common / gamedat, *max_lines* also shrinks ``\\f[N]`` until
|
||||
the soft line count fits (Relayout). Names categories ignore *max_lines*
|
||||
here - use ``wolf names-wrap`` via the GUI instead.
|
||||
"""
|
||||
if isinstance(hit, WrapHit):
|
||||
kind, sheet_name = hit.kind, hit.sheet_name
|
||||
else:
|
||||
|
|
@ -979,11 +1113,13 @@ def wrap_overflow_in_scope(
|
|||
if kind == "names":
|
||||
return _wrap_overflow_in_names_category(path, doc, sheet_name, width)
|
||||
if kind == "db":
|
||||
return wrap_overflow_in_sheet(path, doc, sheet_name, width, kind="db")
|
||||
return wrap_overflow_in_sheet(
|
||||
path, doc, sheet_name, width, kind="db", max_lines=max_lines
|
||||
)
|
||||
if kind in ("map", "common"):
|
||||
return _wrap_overflow_in_event_file(path, doc, width)
|
||||
return _wrap_overflow_in_event_file(path, doc, width, max_lines=max_lines)
|
||||
if kind == "gamedat":
|
||||
return _wrap_overflow_in_gamedat(path, doc, width)
|
||||
return _wrap_overflow_in_gamedat(path, doc, width, max_lines=max_lines)
|
||||
return 0
|
||||
|
||||
|
||||
|
|
@ -992,11 +1128,14 @@ def wrap_hit_in_file(
|
|||
doc: dict[str, Any],
|
||||
hit_id: dict[str, Any],
|
||||
width: int,
|
||||
*,
|
||||
max_lines: int | None = None,
|
||||
) -> bool:
|
||||
"""Relayout one hit line (wrap + optional max-lines font shrink)."""
|
||||
line = locate_line(doc, hit_id)
|
||||
if line is None:
|
||||
return False
|
||||
if not wrap_line_in_doc(line, width):
|
||||
if not apply_relayout_to_line_dict(line, width, max_lines=max_lines):
|
||||
return False
|
||||
save_document(path, doc)
|
||||
return True
|
||||
|
|
@ -1009,6 +1148,7 @@ def wrap_overflow_in_sheet(
|
|||
width: int,
|
||||
*,
|
||||
kind: str | None = None,
|
||||
max_lines: int | None = None,
|
||||
) -> int:
|
||||
"""Wrap every overflowing line in one DB sheet or names.json category."""
|
||||
doc_kind = kind or doc.get("kind")
|
||||
|
|
@ -1024,9 +1164,9 @@ def wrap_overflow_in_sheet(
|
|||
text = line.get("text")
|
||||
if not isinstance(text, str) or not text.strip():
|
||||
continue
|
||||
if not line_needs_wrap(text, width):
|
||||
if not _line_needs_relayout(text, width, max_lines):
|
||||
continue
|
||||
if wrap_line_in_doc(line, width):
|
||||
if apply_relayout_to_line_dict(line, width, max_lines=max_lines):
|
||||
changed += 1
|
||||
if changed:
|
||||
save_document(path, doc)
|
||||
|
|
|
|||
Loading…
Reference in a new issue