diff --git a/fcore/app-update.sml b/fcore/app-update.sml index 4460afb..36ffb16 100644 --- a/fcore/app-update.sml +++ b/fcore/app-update.sml @@ -426,13 +426,13 @@ struct buildTextAndClear (app, buffer, low) end - fun helpDeleteToChr (app: app_type, buffer, cursorIdx, count, fMove, chr) = + fun helpDeleteToChr (app: app_type, buffer, cursorIdx, count, fMove, fInc, chr) = if count = 0 then buildTextAndClearAfterChr (app, buffer, cursorIdx) else let val buffer = LineGap.goToIdx (cursorIdx, buffer) - val otherIdx = fMove (buffer, cursorIdx, chr) + 1 + val otherIdx = fInc (fMove (buffer, cursorIdx, chr), 1) val low = Int.min (cursorIdx, otherIdx) val high = Int.max (cursorIdx, otherIdx) @@ -440,11 +440,11 @@ struct val buffer = LineGap.delete (low, length, buffer) in - helpDeleteToChr (app, buffer, low, count - 1, fMove, chr) + helpDeleteToChr (app, buffer, low, count - 1, fMove, fInc, chr) end - fun deleteToChr (app: app_type, count, fMove, chr) = - helpDeleteToChr (app, #buffer app, #cursorIdx app, count, fMove, chr) + fun deleteToChr (app: app_type, count, fMove, fInc, chr) = + helpDeleteToChr (app, #buffer app, #cursorIdx app, count, fMove, fInc, chr) (* command-parsing functions *) (** number of characters which are integers *) @@ -573,17 +573,23 @@ struct (* have to continue parsing string *) case String.sub (str, strPos + 1) of #"t" => - (* todo: delete till chr, forwards *) + (* delete till chr, forwards *) (case newCmd of CHAR_EVENT chr => - deleteToChr (app, 1, Cursor.tillNextChr, chr) + deleteToChr (app, 1, Cursor.tillNextChr, op+, chr) | KEY_ESC => clearMode app | RESIZE_EVENT (width, height) => resizeText (app, width, height)) | #"T" => - (* todo: delete till chr, backwards *) - clearMode app + (* delete till chr, backwards *) + (case newCmd of + CHAR_EVENT chr => + deleteToChr (app, 1, Cursor.tillPrevChr, op-, chr) + | KEY_ESC => + clearMode app + | RESIZE_EVENT (width, height) => + resizeText (app, width, height)) | #"d" => (* todo: delete whole line *) clearMode app diff --git a/fcore/cursor.sml b/fcore/cursor.sml index 881d7ec..cc1ac7b 100644 --- a/fcore/cursor.sml +++ b/fcore/cursor.sml @@ -1384,48 +1384,88 @@ struct (* tl is empty; return 0 for lineGap start *) 0 - fun helpTillPrevChr (strPos, str, absIdx, stl, ltl, origIdx, findChr, lastNonLine) = - if strPos < 0 then - case (stl, ltl) of - (shd :: stl, lhd :: ltl) => - helpTillPrevChr - ( String.size shd - 1, shd, absIdx, stl, ltl - , origIdx, findChr, lastNonLine - ) - | (_, _) => - origIdx - else - let - val chr = String.sub (str, strPos) - in - if chr = findChr then - lastNonLine - else - let - val lastNonLine = - if chr = #"\n" orelse chr = #"\r" then + fun helpTillPrevChr + ( strPos, str, absIdx, stl, ltl + , origIdx, findChr + , lastNonLine, lastLine, lastValid + ) = + if strPos < 0 then + case (stl, ltl) of + (shd :: stl, lhd :: ltl) => + helpTillPrevChr + ( String.size shd - 1, shd, absIdx, stl, ltl + , origIdx, findChr + , lastNonLine, lastLine, lastValid + ) + | (_, _) => + origIdx + else + let + val chr = String.sub (str, strPos) + in + if chr = findChr then + if lastLine = lastNonLine then + lastNonLine + else if absIdx + 1 = lastLine then + lastValid + 1 + else + Int.min (lastLine, lastNonLine) + else + let + val lastLine = + if chr = #"\n" then + absIdx + else + lastLine + val lastNonLine = + if chr = #"\n" then lastNonLine else absIdx - in - helpTillPrevChr - ( strPos - 1, str, absIdx - 1 - , stl, ltl, origIdx, findChr, lastNonLine - ) - end - end + + (* There is a slightly tricky edge case + * which is the reason the lastValid variable. + * Say we have a string "a\n\n\nbcd" + * and we type "Ta" with the cursor at the end. + * We want the cursor to go to the second line break + * because (graphical-chr -> \n) should not be selectable. + * However, with only lastLine and lastNonLine variables, + * we only have information about the most recent \n + * and the most recent graphical-chr. + * This means we don't have information about the case + * where a graphical-chr is followed by multiple '\n's. + * The lastValid variable keeps track of this information + * so we can use it to provide the expected behaviour. + * *) + val lastValid = + if lastLine = lastNonLine + 1 then + lastLine + 1 + else + Int.min (lastLine, lastNonLine) + in + helpTillPrevChr + ( strPos - 1, str, absIdx - 1 + , stl, ltl, origIdx, findChr + , lastNonLine, lastLine, lastValid + ) + end + end fun startTillPrevChr (shd, strIdx, absIdx, stl, ltl, findChr) = (* we want to start iterating from Prev char after strIdx *) if strIdx > 0 then helpTillPrevChr - (strIdx - 1, shd, absIdx - 1, stl, ltl, absIdx, findChr, absIdx) + ( strIdx - 1, shd, absIdx - 1 + , stl, ltl, absIdx, findChr + , absIdx, absIdx, absIdx + ) else case (stl, ltl) of (stlhd :: stltl, ltlhd :: ltltl) => helpTillPrevChr ( String.size stlhd - 1, stlhd, absIdx - 1 - , stltl, ltltl, absIdx, findChr, absIdx + , stltl, ltltl, absIdx, findChr + , absIdx, absIdx, absIdx ) | (_, _) => (* tl is empty; return 0 for lineGap start *) diff --git a/shf b/shf index d22d1c9..5c47bd1 100755 Binary files a/shf and b/shf differ