diff --git a/src/cg/01-stdlib.cg b/src/cg/01-stdlib.cg index 3f0ebf3..2825f8a 100644 --- a/src/cg/01-stdlib.cg +++ b/src/cg/01-stdlib.cg @@ -1,5 +1,8 @@ -# TODO delete -double = a \ a * 2; +# nth : Int \ List a \ Maybe a +nth = i list \ [i, list] + | [_, []] \ None + | [0, [x, ...xs]] \ (Some x) + | [n, [x, ...xs]] \ nth (n - 1) xs; # map : (a \ b) \ List a \ List b map = f list \ list diff --git a/src/cg/05-palette.cg b/src/cg/05-palette.cg index 44c73de..b5d1b80 100644 --- a/src/cg/05-palette.cg +++ b/src/cg/05-palette.cg @@ -1,22 +1,3 @@ -listRow = config \ - color = (config.selected | True \ "rgba(255,255,255,0.2)" | False \ "transparent"); - - Clickable { - event = config.onClick, - child = Stack { - children = [ - Rect { w = config.w, h = config.h, color = color }, - centerV config.h ( - Positioned { - x = 10, - y = 10, - child = Text { content = config.child, color = "white" } - } - ) - ] - } - }; - palette = config \ focusedIndex = 0; windowHeight = 400; @@ -31,6 +12,25 @@ palette = config \ contentHeight = windowHeight - (padding * 2); listHeight = contentHeight - 40; + paletteRow = config \ + color = (config.selected | True \ "rgba(255,255,255,0.2)" | False \ "transparent"); + + Clickable { + event = config.onClick, + child = Stack { + children = [ + Rect { w = config.w, h = config.h, color = color }, + centerV config.h ( + Positioned { + x = 10, + y = 10, + child = Text { content = config.child, color = "white" } + } + ) + ] + } + }; + Positioned { x = (config.viewport.width - windowWidth) / 2, y = (config.viewport.height - windowHeight) / 2, @@ -44,7 +44,7 @@ palette = config \ gap = 0, children = [ textInput { - key = "query", + key = "palette-query", initialValue = config.state.query, initialFocus = True, color = "white", @@ -55,6 +55,7 @@ palette = config \ onKeyDown = key \ key | ArrowUp \ config.state.focusedIndex := max 0 (config.state.focusedIndex - 1) | ArrowDown \ config.state.focusedIndex := (config.state.focusedIndex + 1) + | Enter \ (\ config.onSelect (unwrapOr "" (nth config.state.focusedIndex results))) | _ \ NoOp }, Clip { @@ -62,7 +63,13 @@ palette = config \ h = listHeight, child = Column { gap = 1, - children = mapWithIndex (t i \ listRow { child = t, w = contentWidth, h = textInputHeight, selected = (config.state.focusedIndex == i), onClick = NoOp }) results + children = mapWithIndex (t i \ paletteRow { + child = t, + w = contentWidth, + h = textInputHeight, + selected = (config.state.focusedIndex == i), + onClick = \ config.onSelect t + }) results } } ] diff --git a/src/cg/10-os.cg b/src/cg/10-os.cg index 49e6036..1fbe5e8 100644 --- a/src/cg/10-os.cg +++ b/src/cg/10-os.cg @@ -2,7 +2,7 @@ osState = { palette = { query = "", focusedIndex = 0, - } + }, }; init = {}; @@ -17,7 +17,7 @@ view = state viewport \ palette { state = osState.palette, search = storeSearch, - onSelect = item \ debug "selected" item, + onSelect = item \ (debug "selected" item), viewport = viewport, } ] diff --git a/src/parser.ts b/src/parser.ts index f9786a7..24d6483 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -101,7 +101,7 @@ export class Parser { return true; } - if (kind !== 'ident') { + if (kind !== 'ident' && kind !== 'underscore') { return false; } @@ -109,7 +109,7 @@ export class Parser { while (true) { const token = this.peek(offset); if (token.kind === 'backslash') return true; - if (token.kind !== 'ident') return false; + if (token.kind !== 'ident' && token.kind !== 'underscore') return false; offset++; } } diff --git a/src/runtime-compiled.ts b/src/runtime-compiled.ts index 92b5c78..529a796 100644 --- a/src/runtime-compiled.ts +++ b/src/runtime-compiled.ts @@ -142,6 +142,12 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) { } function handleEvent(event: any) { + // Thunk + if (typeof event === 'function') { + handleEvent(event(null)); + return; + } + if (!event || !event._tag) return; if (event._tag === 'Batch' && event._0) { diff --git a/src/runtime-js.ts b/src/runtime-js.ts index 7ca1411..6c3e3a1 100644 --- a/src/runtime-js.ts +++ b/src/runtime-js.ts @@ -35,15 +35,17 @@ export const _rt = { } return text.length * 10; // fallback }, - storeSearch: (query: string) => { - const results: string[] = []; - const searchTerm = query.toLowerCase(); - for (const name of Object.keys(store)) { - if (searchTerm === '' || name.toLowerCase().includes(searchTerm)) { - results.push(name); - } + fuzzyMatch: (query: string) => (target: string) => { + const q = query.toLowerCase(); + const t = target.toLowerCase(); + let qi = 0; + for (let ti = 0; ti < t.length && qi < q.length; ti++) { + if (t[ti] === q[qi]) qi++; } - return results; + return { _tag: qi === q.length ? 'True' : 'False' }; + }, + storeSearch: (query: string) => { + return Object.keys(store).filter(name => _rt.fuzzyMatch(query)(name)._tag === 'True'); }, getSource: (name: string) => { const ast = definitions.get(name);