mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 12:56:30 +01:00
Fix :j / utils.justify wiping empty lines. Resolve #3649
This commit is contained in:
parent
ff76bd2388
commit
cd0d896620
4 changed files with 100 additions and 40 deletions
|
|
@ -34,6 +34,7 @@
|
|||
avoid inconsistencies with PostgreSQL databases (Griatch)
|
||||
- [Fix][issue3513]: Fixed issue where OnDemandHandler could traceback on an
|
||||
un-pickle-able object and cause an error at server shutdown (Griatch)
|
||||
- [Fix][issue3649]: The `:j` command in EvEditor would squash empty lines (Griatch)
|
||||
- [Doc][pull3801]: Move Evennia doc build system to latest Sphinx/myST
|
||||
(PowershellNinja, also honorary mention to electroglyph)
|
||||
- [Doc][pull3800]: Describe support for Telnet SSH in HAProxy documentation (holl0wstar)
|
||||
|
|
@ -63,6 +64,7 @@
|
|||
[pull3733]: https://github.com/evennia/evennia/pull/3853
|
||||
[issue3858]: https://github.com/evennia/evennia/issues/3858
|
||||
[issue3813]: https://github.com/evennia/evennia/issues/3513
|
||||
[issue3649]: https://github.com/evennia/evennia/issues/3649
|
||||
|
||||
|
||||
## Evennia 5.0.1
|
||||
|
|
|
|||
|
|
@ -338,6 +338,33 @@ class TestEvEditor(BaseEvenniaCommandTest):
|
|||
self.assertEqual(l3, " " * 37 + "l 3" + " " * 38)
|
||||
self.assertEqual(l4, "l" + " " * 76 + "4")
|
||||
|
||||
def test_eveditor_COLON_J_preserves_paragraph_breaks(self):
|
||||
"""
|
||||
Test to verify fix of issue #3649 (:j command broke input)
|
||||
"""
|
||||
eveditor.EvEditor(self.char1)
|
||||
self.call(
|
||||
eveditor.CmdEditorGroup(),
|
||||
"",
|
||||
raw_string=":",
|
||||
msg="Line Editor []\n01\n[l:01 w:000 c:0000](:h for help)",
|
||||
)
|
||||
text = (
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\n"
|
||||
"Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||
)
|
||||
self.char1.ndb._eveditor.update_buffer(text)
|
||||
self.call(
|
||||
eveditor.CmdEditorGroup(),
|
||||
"=40",
|
||||
raw_string=":j",
|
||||
msg="Left-justified lines 1-3.",
|
||||
)
|
||||
lines = self.char1.ndb._eveditor.get_buffer().split("\n")
|
||||
blank_line_index = lines.index(" " * 40)
|
||||
self.assertEqual(blank_line_index, 2)
|
||||
self.assertTrue(lines[blank_line_index + 1].startswith("Sed do eiusmod"))
|
||||
|
||||
def test_eveditor_bad_commands(self):
|
||||
eveditor.EvEditor(self.char1)
|
||||
self.call(
|
||||
|
|
|
|||
|
|
@ -811,6 +811,25 @@ class TestJustify(TestCase):
|
|||
|
||||
self.assertIn(ANSI_RED, str(result))
|
||||
|
||||
def test_justify_preserves_paragraph_breaks(self):
|
||||
text = "Para one words here.\n\nPara two words there."
|
||||
result = utils.justify(text, width=20, align="l")
|
||||
self.assertEqual(
|
||||
"Para one words here.\n \nPara two words \nthere. ",
|
||||
result,
|
||||
)
|
||||
|
||||
def test_justify_preserves_paragraph_breaks_with_ansi(self):
|
||||
from evennia.utils.ansi import ANSI_RED
|
||||
|
||||
text = ANSIString("Para one has |rred|n text.\n\nPara two.")
|
||||
result = utils.justify(text, width=20, align="l")
|
||||
clean_lines = result.clean().split("\n")
|
||||
|
||||
self.assertIn(ANSI_RED, str(result))
|
||||
self.assertEqual(" " * 20, clean_lines[2])
|
||||
self.assertEqual("Para two. ", clean_lines[3])
|
||||
|
||||
|
||||
class TestAtSearchResult(TestCase):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -249,13 +249,13 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
|
|||
is_ansi = isinstance(text, _ANSISTRING)
|
||||
lb = _ANSISTRING("\n") if is_ansi else "\n"
|
||||
|
||||
def _process_line(line):
|
||||
def _process_line(line, line_word_length, line_gaps):
|
||||
"""
|
||||
helper function that distributes extra spaces between words. The number
|
||||
of gaps is nwords - 1 but must be at least 1 for single-word lines. We
|
||||
distribute odd spaces to one of the gaps.
|
||||
"""
|
||||
line_rest = width - (wlen + ngaps)
|
||||
line_rest = width - (line_word_length + line_gaps)
|
||||
|
||||
gap = _ANSISTRING(" ") if is_ansi else " "
|
||||
|
||||
|
|
@ -277,8 +277,8 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
|
|||
else:
|
||||
line[-1] = line[-1] + pad + sp * (line_rest % 2)
|
||||
else: # align 'f'
|
||||
gap += sp * (line_rest // max(1, ngaps))
|
||||
rest_gap = line_rest % max(1, ngaps)
|
||||
gap += sp * (line_rest // max(1, line_gaps))
|
||||
rest_gap = line_rest % max(1, line_gaps)
|
||||
for i in range(rest_gap):
|
||||
line[i] += sp
|
||||
elif not any(line):
|
||||
|
|
@ -302,49 +302,61 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
|
|||
|
||||
# all other aligns requires splitting into paragraphs and words
|
||||
|
||||
# split into paragraphs and words
|
||||
paragraphs = [text] # re.split("\n\s*?\n", text, re.MULTILINE)
|
||||
words = []
|
||||
for ip, paragraph in enumerate(paragraphs):
|
||||
if ip > 0:
|
||||
words.append(("\n", 0))
|
||||
words.extend((word, m_len(word)) for word in paragraph.split())
|
||||
def _justify_paragraph(words):
|
||||
ngaps = 0
|
||||
wlen = 0
|
||||
line = []
|
||||
lines = []
|
||||
|
||||
if not words:
|
||||
# Just whitespace!
|
||||
return sp * width
|
||||
|
||||
ngaps = 0
|
||||
wlen = 0
|
||||
line = []
|
||||
lines = []
|
||||
|
||||
while words:
|
||||
if not line:
|
||||
# start a new line
|
||||
word = words.pop(0)
|
||||
wlen = word[1]
|
||||
line.append(word[0])
|
||||
elif (words[0][1] + wlen + ngaps) >= width:
|
||||
# next word would exceed word length of line + smallest gaps
|
||||
lines.append(_process_line(line))
|
||||
ngaps, wlen, line = 0, 0, []
|
||||
else:
|
||||
# put a new word on the line
|
||||
word = words.pop(0)
|
||||
line.append(word[0])
|
||||
if word[1] == 0:
|
||||
# a new paragraph, process immediately
|
||||
lines.append(_process_line(line))
|
||||
while words:
|
||||
if not line:
|
||||
# start a new line
|
||||
word = words.pop(0)
|
||||
wlen = word[1]
|
||||
line.append(word[0])
|
||||
elif (words[0][1] + wlen + ngaps) >= width:
|
||||
# next word would exceed word length of line + smallest gaps
|
||||
lines.append(_process_line(line, wlen, ngaps))
|
||||
ngaps, wlen, line = 0, 0, []
|
||||
else:
|
||||
# put a new word on the line
|
||||
word = words.pop(0)
|
||||
line.append(word[0])
|
||||
wlen += word[1]
|
||||
ngaps += 1
|
||||
|
||||
if line: # catch any line left behind
|
||||
lines.append(_process_line(line))
|
||||
if line: # catch any line left behind
|
||||
lines.append(_process_line(line, wlen, ngaps))
|
||||
|
||||
return lines
|
||||
|
||||
paragraphs = []
|
||||
paragraph_words = []
|
||||
for input_line in text.split("\n"):
|
||||
line_words = [(word, m_len(word)) for word in input_line.split()]
|
||||
if line_words:
|
||||
paragraph_words.extend(line_words)
|
||||
else:
|
||||
if paragraph_words:
|
||||
paragraphs.append(paragraph_words)
|
||||
paragraph_words = []
|
||||
paragraphs.append(None)
|
||||
if paragraph_words:
|
||||
paragraphs.append(paragraph_words)
|
||||
|
||||
if not paragraphs:
|
||||
# Just whitespace!
|
||||
return sp * width
|
||||
|
||||
blank_line = _ANSISTRING(sp * width) if is_ansi else sp * width
|
||||
lines = []
|
||||
for paragraph in paragraphs:
|
||||
if paragraph is None:
|
||||
lines.append(blank_line)
|
||||
else:
|
||||
lines.extend(_justify_paragraph(paragraph[:]))
|
||||
|
||||
indentstring = sp * indent
|
||||
out = lb.join([indentstring + line for line in lines])
|
||||
return lb.join([indentstring + line for line in lines])
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue