RLCO2SNKO5OJBEQFRXTB7TCAES6KML4IBOCW5SA7Q24RALW6JCWQC AQMZJXUR5NFNATJ4LPTVGVLQFIKRKRSPYAICXWHGQCQ4WLMQ2JTQC F4RUTONDM6GET6RT6ZBJKHJWJZWHJLCI2NM5ZMOFJGWAOEUCBDQQC PFT5Y2ZYGQA6XXOZ5HH75WVUGA4B3KTDRHSFOZRAUKTPSFOPMNRAC BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC I64IPGJXWRTGHHVAYJUBUIWFR4BY6NM5P7TLTV4JOD7K4BVYDECQC ISOFHXB2DX6IRN4HVBYWLADZM7QXQKRNAAS577G542KS4L6G5H3QC UPCIYZEUIFO2UJ3WPAFOD7VLNZEIIYYGJQGEMJOP5TSSE5PM4ZWAC AIHGJ4BTQNEUC22KVGVL6J7QNS6HVUSJJJWBTWFMSRTKOM6H64EQC JLU2RMC4WICQSGLPVSQNJ4OSAUNHH3IP3FT37WIPBCKCUUQE65GAC H4R5BHVYKFKF2JOFITQ45VEVW32BOWIMHWFOQ35KAUS6QUI7G5QQC S2QMLRXLULVA6M73YVC5VQJ2SYUBHG2DTGXEMOBKEPR4OU7ZKG3AC NDHQN23GI2IFUYGNYSO4BC467L37CQDDYL4C7NYCLD47QHOG6WFQC HGC5RGJPK34K5HRPG6FB7VIKOT6VTWVEC3CBYS5QVH5LVKAL6WGQC LAW2O3NWVFTPBSKIMIXPAGYBDOCHYJNKCAVWKNKH62G42DIKZCYQC R5OKMVVCPAKL2IUMIY7A7ZMTJQZS6UWKW4EVLAVCPLPVNI5DCEYQC PLKNHYZ4KXWWKC2DHXCI4WVO23I7VMEVYT5H2J6JDE4S3D3CHDJQC for frag in line.data:gmatch('%S*%s*') dolocal frag_width = State.font:getWidth(frag)--? print('-- frag:', frag, pos, x, frag_width, State.width)while x + frag_width > State.width do--? print('frag:', frag, pos, x, frag_width, State.width)if x < 0.8 * State.width then-- long word; chop it at some letter-- We're not going to reimplement TeX here.local bpos = Text.nearest_pos_less_than(State.font, frag, State.width - x)if x == 0 and bpos == 0 thenassert(false, ("Infinite loop while line-wrapping. Editor is %dpx wide; window is %dpx wide"):format(State.width, App.screen.width))endpos = pos + bposlocal boffset = Text.offset(frag, bpos+1) -- byte _after_ bposfrag = string.sub(frag, boffset)--? if bpos > 0 then--? print('after chop:', frag)--? endfrag_width = State.font:getWidth(frag)end--? print('screen line:', pos)
for pos,char in utf8chars(line.data) dolocal w = State.font:getWidth(char)if Text.should_word_wrap(State, line.data, pos, char, x)or x+w > State.width -- truncate within a wordthen
x = 0 -- new screen line
x = 0endx = x + wendend-- Check whether to word-wrap line at pos which will be positioned at x.---- We wrap at the start of a word (non-space just after space) if the word-- (non-spaces followed by spaces) wouldn't fit in the rest of the line.---- x lies between 0 and editor.width.---- Postcondition:-- Current line is not wider than editor.width---- Desired properties in priority order:-- Next line doesn't start with whitespace-- Current line ends with whitespace (a.k.a. word wrap)-- Current line is close to full-- None of these is guaranteed. But we should never satisfy a lower priority-- before a higher one.function Text.should_word_wrap(editor, line, pos, char, x)if char:match('%s') then return false endif pos == 1 then return false endif Text.match(line, pos-1, '%S') then return false endlocal offset = Text.offset(line, pos)-- most of the time a word is printable chars + whitespacelocal s = line:match('%S+%s*', offset)assert(s)local w = editor.font:getWidth(s)if x+w < editor.width then return false endif w > editor.width then return false end -- we're going to need to truncate the next word anywayif x < 0.8*editor.width thenlocal s2 = line:match('%S+', offset)local w2 = editor.font:getWidth(s2)if x+w2 > editor.width then-- there'll be some non-whitespace left over for the next linereturn false
-- create a new iterator for s which provides the index and UTF-8 bytes corresponding to each codepointfunction utf8chars(s, startpos)local next_pos = startpos or 1 -- in code pointslocal next_offset = utf8.offset(s, next_pos) -- in bytesreturn function()assert(next_offset) -- never call the iterator after it returns nillocal curr_pos = next_posnext_pos = next_pos+1local curr_offset = next_offsetnext_offset = utf8.offset(s, 2, next_offset)if next_offset == nil then return endlocal curr_char = s:sub(curr_offset, next_offset-1)return curr_pos, curr_charendend