<commit_before>﻿namespace Koffee

open FSharp.Desktop.UI
open System.Text.RegularExpressions
open System.Threading.Tasks
open Koffee.ConfigExt
open Acadian.FSharp

type ModifierKeys = System.Windows.Input.ModifierKeys
type Key = System.Windows.Input.Key

module MainLogic =
    let loadConfig (fsReader: IFileSystemReader) (config: Config) (model: MainModel) =
        model.YankRegister <-
            config.YankRegister |> Option.bind (fun (path, action) ->
                match fsReader.GetNode path with
                | Ok (Some node) -> Some (node, action)
                | _ -> None
            )
        model.PathFormat <- config.PathFormat
        model.ShowHidden <- config.ShowHidden
        model.ShowFullPathInTitle <- config.Window.ShowFullPathInTitle

    let actionError actionName = Result.mapError (fun e -> ActionError (actionName, e))
    let itemActionError item pathFormat = Result.mapError (fun e -> ItemActionError (item, pathFormat, e))

    module Navigation =
        let openPath (fsReader: IFileSystemReader) path select (model: MainModel) = result {
            let! nodes = fsReader.GetNodes model.ShowHidden path |> actionError "open path"
            let sortField, sortDesc = model.Sort
            let nodes = nodes |> SortField.SortByTypeThen sortField sortDesc
            if path <> model.Path then
                model.BackStack <- (model.Path, model.Cursor) :: model.BackStack
                model.ForwardStack <- []
                model.Path <- path
                model.Cursor <- 0
            let cursor = model.Cursor
            model.Nodes <- nodes
            model.SetCursor (
                match select with
                | SelectIndex index -> index
                | SelectName name -> List.tryFindIndex (fun n -> n.Name = name) nodes |? cursor
                | SelectNone -> cursor)
            model.Status <- None
        }

        let openUserPath (fsReader: IFileSystemReader) pathStr (model: MainModel) =
            match Path.Parse pathStr with
            | Some path ->
                match fsReader.GetNode path with
                | Ok (Some node) when node.Type = File ->
                    openPath fsReader path.Parent (SelectName node.Name) model
                | Ok _ ->
                    openPath fsReader path SelectNone model
                | Error e -> Error <| ActionError ("open path", e)
            | None -> Error <| InvalidPath pathStr

        let openSelected fsReader (os: IOperatingSystem) (model: MainModel) =
            let node = model.SelectedNode
            match node.Type with
            | Folder | Drive | NetHost | NetShare ->
                openPath fsReader node.Path SelectNone model
            | File ->
                os.OpenFile node.Path |> actionError (sprintf "open '%s'" node.Name)
                |> Result.map (fun () ->
                    model.Status <- Some <| MainStatus.openFile node.Name)
            | _ -> Ok ()

        let refresh fsReader (model: MainModel) =
            openPath fsReader model.Path SelectNone model

        let back fsReader (model: MainModel) = result {
            match model.BackStack with
            | (path, cursor) :: backTail ->
                let newForwardStack = (model.Path, model.Cursor) :: model.ForwardStack
                do! openPath fsReader path (SelectIndex cursor) model
                model.BackStack <- backTail
                model.ForwardStack <- newForwardStack
            | [] -> ()
        }

        let forward fsReader (model: MainModel) = result {
            match model.ForwardStack with
            | (path, cursor) :: forwardTail ->
                do! openPath fsReader path (SelectIndex cursor) model
                model.ForwardStack <- forwardTail
            | [] -> ()
        }

        let sortList fsReader field (model: MainModel) = result {
            let desc =
                match model.Sort with
                | f, desc when f = field -> not desc
                | _ -> field = Modified
            model.Sort <- field, desc
            do! refresh fsReader model
            model.Status <- Some <| MainStatus.sort field desc
        }

    let initModel (config: Config) (fsReader: IFileSystemReader) startupOptions isFirstInstance (model: MainModel) =
        loadConfig fsReader config model

        model.WindowLocation <-
            startupOptions.Location |> Option.defaultWith (fun () ->
                if isFirstInstance then (config.Window.Left, config.Window.Top)
                else (config.Window.Left + 30, config.Window.Top + 30))
        model.WindowSize <- startupOptions.Size |? (config.Window.Width, config.Window.Height)
        let defaultPath = config.DefaultPath |> Path.Parse |? Path.Root
        model.Path <-
            match config.StartupPath with
            | RestorePrevious -> defaultPath
            | DefaultPath -> config.PreviousPath |> Path.Parse |? defaultPath
        let startupPath =
            startupOptions.StartupPath |? (
                match config.StartupPath with
                | RestorePrevious -> config.PreviousPath
                | DefaultPath -> config.DefaultPath
            )
        Navigation.openUserPath fsReader startupPath model

    module Cursor =
        let private moveCursorToNext predicate reverse (model: MainModel) =
            let indexed = model.Nodes |> List.indexed
            let c = model.Cursor<commit_msg>Maintains selection based on name when sorting<commit_after>﻿namespace Koffee

open FSharp.Desktop.UI
open System.Text.RegularExpressions
open System.Threading.Tasks
open Koffee.ConfigExt
open Acadian.FSharp

type ModifierKeys = System.Windows.Input.ModifierKeys
type Key = System.Windows.Input.Key

module MainLogic =
    let loadConfig (fsReader: IFileSystemReader) (config: Config) (model: MainModel) =
        model.YankRegister <-
            config.YankRegister |> Option.bind (fun (path, action) ->
                match fsReader.GetNode path with
                | Ok (Some node) -> Some (node, action)
                | _ -> None
            )
        model.PathFormat <- config.PathFormat
        model.ShowHidden <- config.ShowHidden
        model.ShowFullPathInTitle <- config.Window.ShowFullPathInTitle

    let actionError actionName = Result.mapError (fun e -> ActionError (actionName, e))
    let itemActionError item pathFormat = Result.mapError (fun e -> ItemActionError (item, pathFormat, e))

    module Navigation =
        let openPath (fsReader: IFileSystemReader) path select (model: MainModel) = result {
            let! nodes = fsReader.GetNodes model.ShowHidden path |> actionError "open path"
            let sortField, sortDesc = model.Sort
            let nodes = nodes |> SortField.SortByTypeThen sortField sortDesc
            if path <> model.Path then
                model.BackStack <- (model.Path, model.Cursor) :: model.BackStack
                model.ForwardStack <- []
                model.Path <- path
                model.Cursor <- 0
            let cursor = model.Cursor
            model.Nodes <- nodes
            model.SetCursor (
                match select with
                | SelectIndex index -> index
                | SelectName name -> List.tryFindIndex (fun n -> n.Name = name) nodes |? cursor
                | SelectNone -> cursor)
            model.Status <- None
        }

        let openUserPath (fsReader: IFileSystemReader) pathStr (model: MainModel) =
            match Path.Parse pathStr with
            | Some path ->
                match fsReader.GetNode path with
                | Ok (Some node) when node.Type = File ->
                    openPath fsReader path.Parent (SelectName node.Name) model
                | Ok _ ->
                    openPath fsReader path SelectNone model
                | Error e -> Error <| ActionError ("open path", e)
            | None -> Error <| InvalidPath pathStr

        let openSelected fsReader (os: IOperatingSystem) (model: MainModel) =
            let node = model.SelectedNode
            match node.Type with
            | Folder | Drive | NetHost | NetShare ->
                openPath fsReader node.Path SelectNone model
            | File ->
                os.OpenFile node.Path |> actionError (sprintf "open '%s'" node.Name)
                |> Result.map (fun () ->
                    model.Status <- Some <| MainStatus.openFile node.Name)
            | _ -> Ok ()

        let refresh fsReader (model: MainModel) =
            openPath fsReader model.Path SelectNone model

        let back fsReader (model: MainModel) = result {
            match model.BackStack with
            | (path, cursor) :: backTail ->
                let newForwardStack = (model.Path, model.Cursor) :: model.ForwardStack
                do! openPath fsReader path (SelectIndex cursor) model
                model.BackStack <- backTail
                model.ForwardStack <- newForwardStack
            | [] -> ()
        }

        let forward fsReader (model: MainModel) = result {
            match model.ForwardStack with
            | (path, cursor) :: forwardTail ->
                do! openPath fsReader path (SelectIndex cursor) model
                model.ForwardStack <- forwardTail
            | [] -> ()
        }

        let sortList fsReader field (model: MainModel) = result {
            let selected = model.SelectedNode.Name
            let desc =
                match model.Sort with
                | f, desc when f = field -> not desc
                | _ -> field = Modified
            model.Sort <- field, desc
            do! refresh fsReader model
            model.Status <- Some <| MainStatus.sort field desc
            model.Nodes |> List.iteri (fun idx node -> if node.Name = selected then model.SetCursor idx)
        }

    let initModel (config: Config) (fsReader: IFileSystemReader) startupOptions isFirstInstance (model: MainModel) =
        loadConfig fsReader config model

        model.WindowLocation <-
            startupOptions.Location |> Option.defaultWith (fun () ->
                if isFirstInstance then (config.Window.Left, config.Window.Top)
                else (config.Window.Left + 30, config.Window.Top + 30))
        model.WindowSize <- startupOptions.Size |? (config.Window.Width, config.Window.Height)
        let defaultPath = config.DefaultPath |> Path.Parse |? Path.Root
        model.Path <-
            match config.StartupPath with
            | RestorePrevious -> defaultPath
            | DefaultPath -> config.PreviousPath |> Path.Parse |? defaultPath
        let startupPath =
            startupOptions.StartupPath |? (
                match config.StartupPath with
                | RestorePrevious -> config.PreviousPath
                | DefaultPath -> config.DefaultPath
            )
        Navigation.openUserPath fsReader startupPath model

    module Cursor =
        let private moveCursorToNext predicate reverse (model: MainModel) =
            let indexed = model.Nodes |> List.indexed
            let c = model.Cursor
{'fraction_non_alphanumeric': 0.08592404604368713, 'fraction_numerical': 0.0009063717937097798, 'mean_word_length': 1.8104941416199694, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1425906', 'n_tokens_mistral': 2848, 'n_tokens_neox': 2688, 'n_words': 799}