From 630a7396c5626ce15f679754fa7b40f955b80483 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Fri, 9 Apr 2021 21:57:40 -0700 Subject: shell: highlight matching paren for cursor --- shell/gap-buffer.mu | 111 +++++++++++++++++++++++++- shell/grapheme-stack.mu | 207 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 301 insertions(+), 17 deletions(-) diff --git a/shell/gap-buffer.mu b/shell/gap-buffer.mu index 0e023da3..b04c072c 100644 --- a/shell/gap-buffer.mu +++ b/shell/gap-buffer.mu @@ -91,9 +91,12 @@ fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) { fn render-gap-buffer-wrapping-right-then-down screen: (addr screen), _gap: (addr gap-buffer), xmin: int, ymin: int, xmax: int, ymax: int, render-cursor?: boolean -> _/eax: int, _/ecx: int { var gap/esi: (addr gap-buffer) <- copy _gap var left/edx: (addr grapheme-stack) <- get gap, left + var highlight-matching-open-paren?/ebx: boolean <- copy 0/false + var matching-open-paren-depth/edi: int <- copy 0 + highlight-matching-open-paren?, matching-open-paren-depth <- highlight-matching-open-paren? gap, render-cursor? var x2/eax: int <- copy 0 var y2/ecx: int <- copy 0 - x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, left, xmin, ymin, xmax, ymax, xmin, ymin + x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, left, xmin, ymin, xmax, ymax, xmin, ymin, highlight-matching-open-paren?, matching-open-paren-depth var right/edx: (addr grapheme-stack) <- get gap, right x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, right, xmin, ymin, xmax, ymax, x2, y2, render-cursor? # decide whether we still need to print a cursor @@ -594,15 +597,115 @@ fn test-render-gap-buffer-highlight-matching-close-paren { # var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-close-paren" - check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren: result" - check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-gap-buffer-highlight-matching-open-paren: cursor" - check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, " ) ", "F - test-render-gap-buffer-highlight-matching-open-paren: matching paren" + check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-close-paren: result" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-gap-buffer-highlight-matching-close-paren: cursor" + check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, " ) ", "F - test-render-gap-buffer-highlight-matching-close-paren: matching paren" } fn test-render-gap-buffer-highlight-matching-open-paren { + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "(a)" + gap-to-end gap + var dummy/eax: grapheme <- gap-left gap + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor + check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren" + check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren: result" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-gap-buffer-highlight-matching-open-paren: cursor" + check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren: matching paren" } fn test-render-gap-buffer-highlight-matching-open-paren-of-end { + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "(a)" + gap-to-end gap + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor + check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end" + check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: result" + check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " |", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: cursor" + check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: matching paren" +} + +# should I highlight a matching open paren? And if so, at what depth from top of left? +# basically there are two cases to disambiguate here: +# Usually the cursor is at top of right. Highlight first '(' at depth 0 from top of left. +# If right is empty, match the ')' _before_ cursor. Highlight first '(' at depth _1_ from top of left. +fn highlight-matching-open-paren? _gap: (addr gap-buffer), render-cursor?: boolean -> _/ebx: boolean, _/edi: int { + # if not rendering cursor, return + compare render-cursor?, 0/false + { + break-if-!= + return 0/false, 0 + } + var gap/esi: (addr gap-buffer) <- copy _gap + var stack/edi: (addr grapheme-stack) <- get gap, right + var top-addr/eax: (addr int) <- get stack, top + var top-index/ecx: int <- copy *top-addr + compare top-index, 0 + { + break-if-> + # if cursor at end, return (char before cursor == ')', 1) + stack <- get gap, left + top-addr <- get stack, top + top-index <- copy *top-addr + compare top-index, 0 + { + break-if-> + return 0/false, 0 + } + top-index <- decrement + var data-ah/eax: (addr handle array grapheme) <- get stack, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var g/eax: (addr grapheme) <- index data, top-index + compare *g, 0x29/close-paren + { + break-if-= + return 0/false, 0 + } + return 1/true, 1 + } + # cursor is not at end; return (char at cursor == ')') + top-index <- decrement + var data-ah/eax: (addr handle array grapheme) <- get stack, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var g/eax: (addr grapheme) <- index data, top-index + compare *g, 0x29/close-paren + { + break-if-= + return 0/false, 0 + } + return 1/true, 0 +} + +fn test-highlight-matching-open-paren { + var gap-storage: gap-buffer + var gap/esi: (addr gap-buffer) <- address gap-storage + initialize-gap-buffer-with gap, "(a)" + gap-to-end gap + var highlight-matching-open-paren?/ebx: boolean <- copy 0/false + var open-paren-depth/edi: int <- copy 0 + highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 0/no-cursor + check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: no cursor" + highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor + check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: at end immediately after ')'" + check-ints-equal open-paren-depth, 1, "F - test-highlight-matching-open-paren: depth at end immediately after ')'" + var dummy/eax: grapheme <- gap-left gap + highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor + check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: on ')'" + dummy <- gap-left gap + highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor + check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: not on ')'" } ## some primitives for scanning through a gap buffer diff --git a/shell/grapheme-stack.mu b/shell/grapheme-stack.mu index 870e32d4..7308b31b 100644 --- a/shell/grapheme-stack.mu +++ b/shell/grapheme-stack.mu @@ -83,21 +83,29 @@ fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) # dump stack to screen from bottom to top # colors hardcoded -fn render-stack-from-bottom-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int -> _/eax: int, _/ecx: int { +fn render-stack-from-bottom-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, highlight-matching-open-paren?: boolean, open-paren-depth: int -> _/eax: int, _/ecx: int { var self/esi: (addr grapheme-stack) <- copy _self + var matching-open-paren-index/edx: int <- get-matching-open-paren-index self, highlight-matching-open-paren?, open-paren-depth var data-ah/edi: (addr handle array grapheme) <- get self, data var _data/eax: (addr array grapheme) <- lookup *data-ah var data/edi: (addr array grapheme) <- copy _data var x/eax: int <- copy _x var y/ecx: int <- copy _y - var top-addr/edx: (addr int) <- get self, top + var top-addr/esi: (addr int) <- get self, top var i/ebx: int <- copy 0 { compare i, *top-addr break-if->= { - var g/edx: (addr grapheme) <- index data, i - x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, 3/fg=cyan, 0/bg + var g/esi: (addr grapheme) <- index data, i + var fg: int + copy-to fg, 3/cyan + { + compare i, matching-open-paren-index + break-if-!= + copy-to fg, 0xf/highlight + } + x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, 0/bg } i <- increment loop @@ -106,7 +114,7 @@ fn render-stack-from-bottom-wrapping-right-then-down screen: (addr screen), _sel } # helper for small words -fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), x: int, y: int -> _/eax: int { +fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, highlight-matching-open-paren?: boolean, open-paren-depth: int -> _/eax: int { var _width/eax: int <- copy 0 var _height/ecx: int <- copy 0 _width, _height <- screen-size screen @@ -114,7 +122,7 @@ fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), var height/ebx: int <- copy _height var x2/eax: int <- copy 0 var y2/ecx: int <- copy 0 - x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, self, x, y, width, height, x, y + x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, self, x, y, width, height, x, y, highlight-matching-open-paren?, open-paren-depth return x2 # y2? yolo } @@ -122,13 +130,13 @@ fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), # optionally render a 'cursor' with the top grapheme fn render-stack-from-top-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, render-cursor?: boolean -> _/eax: int, _/ecx: int { var self/esi: (addr grapheme-stack) <- copy _self + var matching-close-paren-index/edx: int <- get-matching-close-paren-index self, render-cursor? var data-ah/eax: (addr handle array grapheme) <- get self, data var _data/eax: (addr array grapheme) <- lookup *data-ah var data/edi: (addr array grapheme) <- copy _data var x/eax: int <- copy _x var y/ecx: int <- copy _y var top-addr/ebx: (addr int) <- get self, top - var matching-close-paren-index/edx: int <- get-matching-close-paren-index self, render-cursor? var i/ebx: int <- copy *top-addr i <- decrement # if render-cursor?, peel off first iteration @@ -191,7 +199,7 @@ fn test-render-grapheme-stack { var screen/esi: (addr screen) <- address screen-on-stack initialize-screen screen, 5, 4 # - var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y + var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y, 0/no-highlight-matching-open-paren, 0/open-paren-depth check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom" check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result" check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-render-grapheme-stack from bottom: bg" @@ -208,7 +216,7 @@ fn test-render-grapheme-stack { } fn test-render-grapheme-stack-while-highlighting-matching-close-paren { - # setup: gs = "abc" + # setup: gs = "(b)" var gs-storage: grapheme-stack var gs/edi: (addr grapheme-stack) <- address gs-storage initialize-grapheme-stack gs, 5 @@ -224,10 +232,135 @@ fn test-render-grapheme-stack-while-highlighting-matching-close-paren { initialize-screen screen, 5, 4 # var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true - check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren" - check-ints-equal x, 3, "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: result" - check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: cursor" - check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: matching paren" + check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: cursor" + check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: matching paren" +} + +fn test-render-grapheme-stack-while-highlighting-matching-close-paren-2 { + # setup: gs = "(a (b)) c" + var gs-storage: grapheme-stack + var gs/edi: (addr grapheme-stack) <- address gs-storage + initialize-grapheme-stack gs, 0x10 + var g/eax: grapheme <- copy 0x63/c + push-grapheme-stack gs, g + g <- copy 0x20/space + push-grapheme-stack gs, g + g <- copy 0x29/close-paren + push-grapheme-stack gs, g + g <- copy 0x29/close-paren + push-grapheme-stack gs, g + g <- copy 0x62/b + push-grapheme-stack gs, g + g <- copy 0x28/open-paren + push-grapheme-stack gs, g + g <- copy 0x20/space + push-grapheme-stack gs, g + g <- copy 0x61/a + push-grapheme-stack gs, g + g <- copy 0x28/open-paren + push-grapheme-stack gs, g + # setup: screen + var screen-on-stack: screen + var screen/esi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true + check-screen-row screen, 2/y, "(a (b)) c ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2" + check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "| ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: cursor" + check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: matching paren" +} + +fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end { + # setup: gs = "(b)" + var gs-storage: grapheme-stack + var gs/edi: (addr grapheme-stack) <- address gs-storage + initialize-grapheme-stack gs, 5 + var g/eax: grapheme <- copy 0x28/open-paren + push-grapheme-stack gs, g + g <- copy 0x62/b + push-grapheme-stack gs, g + g <- copy 0x29/close-paren + push-grapheme-stack gs, g + # setup: screen + var screen-on-stack: screen + var screen/esi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth + check-screen-row screen, 2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end" + check-screen-row-in-color screen, 0xf/fg=white, 2/y, "( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end: matching paren" +} + +fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2 { + # setup: gs = "a((b))" + var gs-storage: grapheme-stack + var gs/edi: (addr grapheme-stack) <- address gs-storage + initialize-grapheme-stack gs, 0x10 + var g/eax: grapheme <- copy 0x61/a + push-grapheme-stack gs, g + g <- copy 0x28/open-paren + push-grapheme-stack gs, g + g <- copy 0x28/open-paren + push-grapheme-stack gs, g + g <- copy 0x62/b + push-grapheme-stack gs, g + g <- copy 0x29/close-paren + push-grapheme-stack gs, g + g <- copy 0x29/close-paren + push-grapheme-stack gs, g + # setup: screen + var screen-on-stack: screen + var screen/esi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth + check-screen-row screen, 2/y, "a((b)) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2" + check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2: matching paren" +} + +fn test-render-grapheme-stack-while-highlighting-matching-open-paren { + # setup: gs = "(b" + var gs-storage: grapheme-stack + var gs/edi: (addr grapheme-stack) <- address gs-storage + initialize-grapheme-stack gs, 5 + var g/eax: grapheme <- copy 0x28/open-paren + push-grapheme-stack gs, g + g <- copy 0x62/b + push-grapheme-stack gs, g + # setup: screen + var screen-on-stack: screen + var screen/esi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth + check-screen-row screen, 2/y, "(b ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren" + check-screen-row-in-color screen, 0xf/fg=white, 2/y, "( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren: matching paren" +} + +fn test-render-grapheme-stack-while-highlighting-matching-open-paren-2 { + # setup: gs = "a((b)" + var gs-storage: grapheme-stack + var gs/edi: (addr grapheme-stack) <- address gs-storage + initialize-grapheme-stack gs, 0x10 + var g/eax: grapheme <- copy 0x61/a + push-grapheme-stack gs, g + g <- copy 0x28/open-paren + push-grapheme-stack gs, g + g <- copy 0x28/open-paren + push-grapheme-stack gs, g + g <- copy 0x62/b + push-grapheme-stack gs, g + g <- copy 0x29/close-paren + push-grapheme-stack gs, g + # setup: screen + var screen-on-stack: screen + var screen/esi: (addr screen) <- address screen-on-stack + initialize-screen screen, 5, 4 + # + var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth + check-screen-row screen, 2/y, "a((b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2" + check-screen-row-in-color screen, 0xf/fg=white, 2/y, " ( ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2: matching paren" } # return the index of the matching close-paren of the grapheme at cursor (top of stack) @@ -286,6 +419,54 @@ fn get-matching-close-paren-index _self: (addr grapheme-stack), render-cursor?: return *top-addr } +# return the index of the first open-paren at the given depth +# or top index if there's no matching close-paren +fn get-matching-open-paren-index _self: (addr grapheme-stack), control: boolean, depth: int -> _/edx: int { + var self/esi: (addr grapheme-stack) <- copy _self + var top-addr/edx: (addr int) <- get self, top + # if not rendering cursor, return + compare control, 0/false + { + break-if-!= + return *top-addr + } + var data-ah/eax: (addr handle array grapheme) <- get self, data + var data/eax: (addr array grapheme) <- lookup *data-ah + var i/ecx: int <- copy *top-addr + # if stack is empty, return + compare i, 0 + { + break-if-> + return *top-addr + } + # scan to matching open paren + var paren-count/ebx: int <- copy 0 + i <- decrement + { + compare i, 0 + break-if-< + var g/esi: (addr grapheme) <- index data, i + compare *g, 0x29/close-paren + { + break-if-!= + paren-count <- increment + } + compare *g, 0x28/open-paren + { + break-if-!= + compare paren-count, depth + { + break-if-!= + return i + } + paren-count <- decrement + } + i <- decrement + loop + } + return *top-addr +} + # compare from bottom # beware: modifies 'stream', which must be disposed of after a false result fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean { -- cgit 1.4.1-2-gfad0