vim9script import autoload "kg8m/plugin.vim" import autoload "kg8m/util/matchpairs.vim" export def OnSource(): void g:sandwich_no_default_key_mappings = true g:operator_sandwich_no_default_key_mappings = true g:textobj_sandwich_no_default_key_mappings = true enddef export def OnPostSource(): void g:sandwich#recipes = DefaultRecipes() enddef export def OperatorDeleteExpr(): string const key = getcharstr() const recipes = DeleterRecipesFor(key) UseTemporaryRecipes(recipes) return "\(operator-sandwich-delete)\(textobj-sandwich-auto-a)" enddef export def OperatorReplaceExpr(): string const adder_recipes = DefaultRecipes()->mapnew((_, recipe) => { final new_recipe = copy(recipe) new_recipe.action = ["add", "replace"] return new_recipe }) const key = getcharstr() const deleter_recipes = DeleterRecipesFor(key) UseTemporaryRecipes(adder_recipes + deleter_recipes) return "\(operator-sandwich-replace)\(textobj-sandwich-auto-a)" enddef export def AutoTextobjExpr(key: string, mode: string, a_or_i: string): string return textobj#sandwich#auto(mode, a_or_i, {}, AutoRecipesFor(key)) enddef def UseTemporaryRecipes(temporary_recipes: list>): void g:sandwich#recipes = temporary_recipes autocmd SafeState ++once g:sandwich#recipes = DefaultRecipes() enddef def AdderRecipesFor(key: string): list> return RecipesFor(key, (key_pair, japanese_pairs) => { const key_recipes = mapnew(key_pair, (_, key_or_another) => BuildAdderRecipe(key_pair, [key_or_another])) const japanese_recipes = mapnew(japanese_pairs, (_, pair) => BuildAdderRecipe(pair, pair)) return key_recipes + japanese_recipes }) enddef def DeleterRecipesFor(key: string): list> return RecipesFor(key, (key_pair, japanese_pairs) => { const key_recipes = [BuildDeleterRecipe(key_pair, key_pair)] const japanese_recipes = mapnew(japanese_pairs, (_, pair) => BuildDeleterRecipe(pair, key_pair)) return key_recipes + japanese_recipes }) enddef def AutoRecipesFor(key: string): list> return RecipesFor(key, (key_pair, japanese_pairs) => { const key_recipes = [BuildAutoRecipe(key_pair)] const japanese_recipes = mapnew(japanese_pairs, (_, pair) => BuildAutoRecipe(pair)) return key_recipes + japanese_recipes }) enddef def RecipesFor(raw_key: string, Build: func): list> const key = matchpairs.NormalizeKey(raw_key) const japanese_pairs = get(matchpairs.GroupedJapanesePairs(), key, []) if japanese_pairs ==# [] return [BuildFallbackRecipe(key)] else const key_pair = matchpairs.KeyPairFor(key) return Build(key_pair, japanese_pairs) endif enddef def DefaultRecipes(): list> const keys = keys(matchpairs.GroupedJapanesePairs()) const adder_recipes = reduce(keys, (recipes, key) => recipes + AdderRecipesFor(key), []) const deleter_recipes = reduce(keys, (recipes, key) => recipes + DeleterRecipesFor(key), []) return deepcopy(g:sandwich#default_recipes) + adder_recipes + deleter_recipes enddef def BuildAdderRecipe(matchpair: list, input: list): dict const is_start_key = matchpairs.IsBasicStartKey(input[0]) return extendnew({ # For surround.vim-like behavior: # - `foo` => `( foo )` with `(` # - `foo` => `(foo)` with `)` buns: is_start_key && len(input) ==# 1 ? [$"{matchpair[0]} ", $" {matchpair[1]}"] : matchpair, input: input, kind: ["add", "replace", "auto"], action: ["add"], }, CommonRecipeOptionsFor(matchpair)) enddef def BuildDeleterRecipe(matchpair: list, input: list): dict return extendnew({ # For surround.vim-like behavior: # - `( foo )` => `foo` with `(` or `)` # - `(foo)` => `foo` with `(` or `)` buns: [$'\V{matchpair[0]}\s\*', $'\V\s\*{matchpair[1]}'], input: input, kind: ["delete", "replace", "textobj", "auto"], action: ["delete"], regex: true, }, CommonRecipeOptionsFor(matchpair)) enddef def BuildAutoRecipe(matchpair: list): dict return extendnew({ buns: matchpair, kind: ["textobj", "auto"], }, CommonRecipeOptionsFor(matchpair)) enddef def BuildFallbackRecipe(key: string): dict const matchpair = [key, key] return extendnew({ buns: matchpair, input: matchpair, kind: ["add", "delete", "replace", "textobj", "auto"], action: ["add", "delete"], }, CommonRecipeOptionsFor(matchpair)) enddef def CommonRecipeOptionsFor(matchpair: list): dict return { nesting: matchpair[0] !=# matchpair[1], match_syntax: 1, quoteescape: matchpair[0] ==# '"' || matchpair[0] ==# "'", } enddef plugin.EnsureSourced("vim-sandwich")