diff --git a/fcore/app-update.sml b/fcore/app-update.sml index d86d92b..0baf494 100644 --- a/fcore/app-update.sml +++ b/fcore/app-update.sml @@ -123,6 +123,33 @@ struct in helpMove (app, buffer, cursorIdx, count, fMove) end + fun moveToMatchingPair (app: app_type) = + let + val {buffer, cursorIdx, windowWidth, windowHeight, startLine, ...} = app + + (* move LineGap and buffer to start of line *) + val buffer = LineGap.goToIdx (cursorIdx, buffer) + val cursorIdx = Cursor.matchPair (buffer, cursorIdx) + + val buffer = LineGap.goToIdx (cursorIdx, buffer) + (* todo: + * check if cursorIdx is visible on screen first, + * and only get new startLine if it is not visible *) + val startLine = TextWindow.getStartLineWithCursorCentered + (buffer, cursorIdx, startLine, windowWidth, windowHeight div 2) + + val buffer = LineGap.goToLine (startLine, buffer) + + val newApp = AppWith.bufferAndCursorIdx + (app, buffer, cursorIdx, NORMAL_MODE "", startLine) + + val drawMsg = + TextBuilder.build + (startLine, cursorIdx, buffer, windowWidth, windowHeight) + in + (newApp, drawMsg) + end + fun firstNonSpaceChr (app: app_type) = let val {buffer, cursorIdx, windowWidth, windowHeight, startLine, ...} = app @@ -212,6 +239,7 @@ struct * interpret as "go to line" command; * else, interpret as a command to move to end *) moveToEnd app + | #"%" => moveToMatchingPair app (* multi-char commands which can be appended *) | #"t" => appendChr (app, chr, str) | #"T" => appendChr (app, chr, str) diff --git a/fcore/cursor.sml b/fcore/cursor.sml index bb4e771..fa2e5cc 100644 --- a/fcore/cursor.sml +++ b/fcore/cursor.sml @@ -1575,4 +1575,133 @@ struct fun tillPrevChr (lineGap, cursorIdx, chr) = prevChr (lineGap, cursorIdx, chr, startTillPrevChr) + + fun helpMatchPairNext + ( strPos, str, absIdx, stl, origIdx + , openChr, openNum, closeChr, closeNum + ) = + if strPos = String.size str then + case stl of + hd :: tl => + helpMatchPairNext + ( 0, hd, absIdx, tl, origIdx + , openChr, openNum, closeChr, closeNum + ) + | [] => + origIdx + else + let + val chr = String.sub (str, strPos) + val openNum = if chr = openChr then openNum + 1 else openNum + val closeNum = if chr = closeChr then closeNum + 1 else closeNum + in + if openNum = closeNum then + absIdx + else + helpMatchPairNext + ( strPos + 1, str, absIdx + 1, stl, origIdx + , openChr, openNum, closeChr, closeNum + ) + end + + fun helpMatchPairPrev + ( strPos, str, absIdx, stl, origIdx + , openChr, openNum, closeChr, closeNum + ) = + if strPos < 0 then + case stl of + hd :: tl => + helpMatchPairNext + ( String.size hd - 1, hd, absIdx, tl, origIdx + , openChr, openNum, closeChr, closeNum + ) + | [] => + origIdx + else + let + val chr = String.sub (str, strPos) + val openNum = if chr = openChr then openNum + 1 else openNum + val closeNum = if chr = closeChr then closeNum + 1 else closeNum + in + if openNum = closeNum then + absIdx + else + helpMatchPairPrev + ( strPos - 1, str, absIdx - 1, stl, origIdx + , openChr, openNum, closeChr, closeNum + ) + end + + fun matchPair (lineGap: LineGap.t, cursorIdx) = + let + val {rightStrings, idx = bufferIdx, leftStrings, ...} = lineGap + in + case rightStrings of + shd :: stl => + let + (* convert absolute cursorIdx to idx relative to hd string *) + val strIdx = cursorIdx - bufferIdx + in + if strIdx < String.size shd then + (* strIdx is in this string *) + let + val chr = String.sub (shd, strIdx) + in + (case chr of + #"(" => + helpMatchPairNext + ( strIdx + 1, shd, cursorIdx + 1, stl, cursorIdx + , #"(", 1, #")", 0 + ) + | #")" => + helpMatchPairPrev + ( strIdx - 1, shd, cursorIdx - 1, stl, cursorIdx + , #"(", 0, #")", 1 + ) + | #"[" => + helpMatchPairNext + ( strIdx + 1, shd, cursorIdx + 1, stl, cursorIdx + , #"[", 1, #"]", 0 + ) + | #"]" => + helpMatchPairPrev + ( strIdx - 1, shd, cursorIdx - 1, stl, cursorIdx + , #"[", 0, #"]", 1 + ) + | #"{" => + helpMatchPairNext + ( strIdx + 1, shd, cursorIdx + 1, stl, cursorIdx + , #"{", 1, #"}", 0 + ) + | #"}" => + helpMatchPairPrev + ( strIdx - 1, shd, cursorIdx - 1, stl, cursorIdx + , #"{", 0, #"}", 1 + ) + | #"<" => + helpMatchPairNext + ( strIdx + 1, shd, cursorIdx + 1, stl, cursorIdx + , #"<", 1, #">", 0 + ) + | #">" => + helpMatchPairPrev + ( strIdx - 1, shd, cursorIdx - 1, stl, cursorIdx + , #"<", 0, #">", 1 + ) + | _ => cursorIdx) + end + else + (* strIdx is in tl *) + (case stl of + stlhd :: stltl => + let + val strIdx = strIdx - String.size shd + val leftStrings = shd :: leftStrings + in + (print "match pair throw err\n"; raise Match) + end + | [] => cursorIdx) + end + | [] => cursorIdx + end end diff --git a/shf b/shf index 7e8b68c..0faa994 100755 Binary files a/shf and b/shf differ