diff --git a/src/pocof.Test/Pocof.fs b/src/pocof.Test/Pocof.fs index 1eff97ac..19304b6b 100644 --- a/src/pocof.Test/Pocof.fs +++ b/src/pocof.Test/Pocof.fs @@ -42,21 +42,6 @@ let propMap = Map.empty let results = [ "a"; "b"; "c"; "d"; "e" ] |> List.map box -type MockGetKey(keys: ConsoleKeyInfo list list) = - let mutable keys = keys - - member __.getKey() = - match keys with - | [] -> failwith "no keys remains. probably test is broken." - | x :: xs -> - keys <- xs - x - - member __.check() = - match keys with - | [] -> () - | _ -> failwith "keys remains. probably test is broken." - module calculateWindowBeginningCursor = [] let ``should return 0.`` () = @@ -104,21 +89,22 @@ module calculateWindowBeginningCursor = actual |> shouldEqual 0 module loop = + [] let ``should return result when finishing.`` () = let input = results |> List.map toObj let state, context = pocof.PocofQuery.prepare state - let m = - MockGetKey [ [ new ConsoleKeyInfo('\000', ConsoleKey.Enter, false, false, false) ] ] + let rui = new MockRawUI(60, 30, [ MockRawUI.consoleKey '\000' ConsoleKey.Enter ]) + use buff = new Buff(rui, (fun _ -> Seq.empty)) let args = { keymaps = pocof.PocofAction.defaultKeymap input = input propMap = propMap writeScreen = writeScreen - getKey = m.getKey - getConsoleWidth = fun () -> 60 + getKey = buff.getKey + getConsoleWidth = buff.getConsoleWidth getLengthInBufferCells = String.length } let actual = loop args input state pos context @@ -127,6 +113,8 @@ module loop = actual |> List.iteri (fun i x -> x = results.[i] |> shouldEqual true) + rui.check () + [] let ``shouldn't return result when canceling.`` () = let input = results |> List.map toObj @@ -134,20 +122,21 @@ module loop = let state, context = pocof.PocofQuery.prepare { state with SuppressProperties = true } - let m = - MockGetKey [ [ new ConsoleKeyInfo('\000', ConsoleKey.Escape, false, false, false) ] ] + let rui = new MockRawUI(60, 30, [ MockRawUI.consoleKey '\000' ConsoleKey.Escape ]) + use buff = new Buff(rui, (fun _ -> Seq.empty)) let args = { keymaps = pocof.PocofAction.defaultKeymap input = input propMap = propMap writeScreen = writeScreen - getKey = m.getKey - getConsoleWidth = fun () -> 60 + getKey = buff.getKey + getConsoleWidth = buff.getConsoleWidth getLengthInBufferCells = String.length } let actual = loop args input state pos context actual |> List.length |> shouldEqual 0 + rui.check () [] let ``should return result when finishing after noop.`` () = @@ -155,17 +144,25 @@ module loop = let state, context = pocof.PocofQuery.prepare { state with Refresh = NotRequired } - let m = - MockGetKey [ [ new ConsoleKeyInfo('\000', ConsoleKey.Escape, true, true, false) ] - [ new ConsoleKeyInfo('\000', ConsoleKey.Enter, false, false, false) ] ] + let rui = + new MockRawUI( + 60, + 30, + [ new ConsoleKeyInfo('\000', ConsoleKey.Escape, true, true, false) + |> Some + None + MockRawUI.consoleKey '\000' ConsoleKey.Enter ] + ) + + use buff = new Buff(rui, (fun _ -> Seq.empty)) let args = { keymaps = pocof.PocofAction.defaultKeymap input = input propMap = propMap writeScreen = writeScreen - getKey = m.getKey - getConsoleWidth = fun () -> 60 + getKey = buff.getKey + getConsoleWidth = buff.getConsoleWidth getLengthInBufferCells = String.length } let actual = loop args input state pos context @@ -174,34 +171,40 @@ module loop = actual |> List.iteri (fun i x -> x = results.[i] |> shouldEqual true) - m.check () + rui.check () [] let ``should return result when finishing with filter.`` () = let input = results |> List.map toObj - let state, context = pocof.PocofQuery.prepare state - let m = - MockGetKey [ [ new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false) - new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, false, false) - new ConsoleKeyInfo('d', ConsoleKey.D, false, false, false) ] - [ new ConsoleKeyInfo('\000', ConsoleKey.Enter, false, false, false) ] ] + let rui = + new MockRawUI( + 60, + 30, + [ MockRawUI.consoleKey 'a' ConsoleKey.A + MockRawUI.consoleKey ' ' ConsoleKey.Spacebar + MockRawUI.consoleKey 'd' ConsoleKey.D + None + MockRawUI.consoleKey '\000' ConsoleKey.Enter ] + ) + + use buff = new Buff(rui, (fun _ -> Seq.empty)) let args = { keymaps = pocof.PocofAction.defaultKeymap input = input propMap = propMap writeScreen = writeScreen - getKey = m.getKey - getConsoleWidth = fun () -> 60 + getKey = buff.getKey + getConsoleWidth = buff.getConsoleWidth getLengthInBufferCells = String.length } let actual = loop args input state pos context actual |> List.length |> shouldEqual 2 actual.[0] = results.[0] |> shouldEqual true actual.[1] = results.[3] |> shouldEqual true - m.check () + rui.check () [] let ``should update QueryState.WindowWidth based on ConsoleWidth.`` () = @@ -210,11 +213,15 @@ module loop = let state, context = pocof.PocofQuery.prepare { state with SuppressProperties = true } - let m = - MockGetKey [ [ new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false) ] - [ new ConsoleKeyInfo('\000', ConsoleKey.Enter, false, false, false) ] ] + let rui = + new MockRawUI( + 60, + 30, + [ MockRawUI.consoleKey 'a' ConsoleKey.A + None + MockRawUI.consoleKey '\000' ConsoleKey.Enter ] + ) - let rui = new MockRawUI(60, 30) use buff = new Buff(rui, (fun _ -> Seq.empty)) let args = @@ -222,7 +229,7 @@ module loop = input = input propMap = propMap writeScreen = buff.writeTopDown - getKey = m.getKey + getKey = buff.getKey getConsoleWidth = fun () -> rui.width <- 80 @@ -238,23 +245,9 @@ module loop = :: (generateLine 80 (rui.height - 1)) rui.screen |> shouldEqual expected + rui.check () module interact = - type MockGetKey(keys: ConsoleKeyInfo list list) = - let mutable keys = keys - - member __.getKey() = - match keys with - | [] -> failwith "no keys remains. probably test is broken." - | x :: xs -> - keys <- xs - x - - member __.check() = - match keys with - | [] -> () - | _ -> failwith "keys remains. probably test is broken." - [] let ``should return result with NonInteractive mode.`` () = let config: InternalConfig = diff --git a/src/pocof.Test/PocofUI.fs b/src/pocof.Test/PocofUI.fs index 89a05e6a..00b12ed3 100644 --- a/src/pocof.Test/PocofUI.fs +++ b/src/pocof.Test/PocofUI.fs @@ -3,7 +3,6 @@ module PocofUI open Xunit open FsUnitTyped open System -open pocof.LanguageExtension open pocof.PocofData open pocof.PocofQuery open pocof.PocofScreen @@ -19,9 +18,11 @@ type MockRawUI = val mutable x: int val mutable y: int val mutable screen: string list - + val mutable keys: ConsoleKeyInfo option list static xx = 50 static yy = 30 + + new() = // NOTE: accessing Console.TreatControlCAsInput will raise System.IO.IOException when running on GitHub Actions windows runner. { caAsInput = true @@ -30,6 +31,7 @@ type MockRawUI = width = MockRawUI.xx height = MockRawUI.yy screen = generateLine MockRawUI.xx MockRawUI.yy + keys = [MockRawUI.consoleKey '\000' ConsoleKey.Enter] } new(x:int, y:int) = // NOTE: accessing Console.TreatControlCAsInput will raise System.IO.IOException when running on GitHub Actions windows runner. @@ -39,8 +41,20 @@ type MockRawUI = width = x height = y screen = generateLine x y + keys = [MockRawUI.consoleKey '\000' ConsoleKey.Enter] } + new(x:int, y:int, keys: ConsoleKeyInfo option list) = + // NOTE: accessing Console.TreatControlCAsInput will raise System.IO.IOException when running on GitHub Actions windows runner. + { caAsInput = true + x = x + y = y + width = x + height = y + screen = generateLine x y + keys = keys + } + interface IRawUI with member __.SetCursorPosition (x: int) (y: int) = __.x <- x @@ -52,8 +66,10 @@ type MockRawUI = code >= 0xFF00 && code <= 0xFF60 s |> Seq.cast |> Seq.map (fun c -> if isFullWidth c then 2 else 1) |> Seq.sum + member __.GetWindowWidth() = __.width member __.GetWindowHeight() = __.height + member __.Write x y s = __.screen <- __.screen |>List.mapi (fun i ss -> match i with @@ -64,12 +80,39 @@ type MockRawUI = match l <= __.width with | true -> ss + String.replicate (__.width - l) " " | _ -> ss) - member __.ReadKey(_) = new ConsoleKeyInfo('\000', ConsoleKey.Enter, false, false, false) - member __.KeyAvailable() = false + + member __.ReadKey(_) = + match __.keys with + | [] -> failwith "key sequence is empty. check your test key sequence." + | k::ks -> + match k with + | None -> failwith "key is none. check your test key sequence." + | Some k -> + __.keys <- ks + k + + member __.KeyAvailable() = + match __.keys with + | [] -> false + | k::ks -> + match k with + | None -> + __.keys <- ks + false + | Some _ -> true interface IDisposable with member __.Dispose() = () + static member consoleKey keyChar key = + new ConsoleKeyInfo(keyChar, key, false, false, false) + |> Some + + member __.check() = + match __.keys with + | [] -> () + | _ -> failwith "keys remains. probably test is broken." + module ``Buff writeScreen`` = open System.Collections open System.Management.Automation diff --git a/src/pocof/Main.fs b/src/pocof/Main.fs index 6efaa567..0b91ec7a 100644 --- a/src/pocof/Main.fs +++ b/src/pocof/Main.fs @@ -126,7 +126,7 @@ module Pocof = (state: InternalState) (pos: Position) (rui: unit -> PocofScreen.IRawUI) - (invoke: obj list -> seq) + (invoke: obj list -> string seq) (input: Entry list) = @@ -160,8 +160,7 @@ module Pocof = let buildInput acc (input: PSObject array) = input - |> List.ofArray - |> List.fold + |> Seq.fold (fun acc o -> match o.BaseObject with | :? IDictionary as dct -> diff --git a/src/pocof/Query.fs b/src/pocof/Query.fs index 4281667e..2d9b5d52 100644 --- a/src/pocof/Query.fs +++ b/src/pocof/Query.fs @@ -57,7 +57,7 @@ module PocofQuery = with | _ -> None - type TesterType<'a> = ('a -> bool) -> list<'a> -> bool + type TesterType<'a> = ('a -> bool) -> 'a list -> bool type QueryContext = { Queries: Query list diff --git a/src/pocof/UI.fs b/src/pocof/UI.fs index 301f07ee..57b6bdf9 100644 --- a/src/pocof/UI.fs +++ b/src/pocof/UI.fs @@ -63,27 +63,39 @@ module PocofScreen = let rui: IRawUI = r let invoke: obj list -> string seq = i - let info (state: PocofData.InternalState) = - let rec getQuery (q: string) = - let cl = rui.GetLengthInBufferCells q - - match cl <= state.QueryState.WindowWidth with - | true -> - q - + String.replicate (state.QueryState.WindowWidth - cl) " " - | _ -> - let ql = String.length q - 2 - let q = q.[..ql] - getQuery q + [] + let rec getQuery (w: int) (q: string) (l: int) = + let cl = rui.GetLengthInBufferCells q + + match w - cl with + | x when x = 0 || x = 1 -> q + | x -> + let l = l + (x + Math.Sign(x)) / 2 + let q = q.[..l] +#if DEBUG + Logger.logFile [ $"l '{l}'" ] +#endif + getQuery w q l + let info (state: PocofData.InternalState) = let q = - state.QueryState.Query.[state.QueryState.WindowBeginningCursor .. state.QueryState.WindowWidth - + state.QueryState.WindowBeginningCursor] + let q = + state.QueryState.Query.[state.QueryState.WindowBeginningCursor .. state.QueryState.WindowWidth + + state.QueryState.WindowBeginningCursor] + + let l = + match state.QueryState.WindowWidth - String.length q with + | l when l > 0 -> l + | _ -> 0 + + q + String.replicate l " " #if DEBUG Logger.logFile [ $"q '{q}' ql '{String.length q}' WindowBeginningCursor '{state.QueryState.WindowBeginningCursor}' WindowWidth '{state.QueryState.WindowWidth}'" ] #endif - let q = getQuery q + let q = + getQuery state.QueryState.WindowWidth q + <| String.length q [ PocofData.InternalState.prompt state q