From 9d5437bcaec37489728aeaed6406da82ecccea76 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Fri, 27 Mar 2026 16:23:26 +0100 Subject: [PATCH 01/14] Add commands --- plugin/decipher.lua | 155 +++++++++++++++++++++++++++++++++++++++++ tests/minimal_init.lua | 1 + tests/plugin_spec.lua | 34 +++++++++ 3 files changed, 190 insertions(+) create mode 100644 plugin/decipher.lua create mode 100644 tests/plugin_spec.lua diff --git a/plugin/decipher.lua b/plugin/decipher.lua new file mode 100644 index 0000000..70714e4 --- /dev/null +++ b/plugin/decipher.lua @@ -0,0 +1,155 @@ +---@alias decipher.CommandPreviewFunc fun( +---options: vim.api.keyset.create_user_command.command_args, +---ns_id: integer, +---preview_buffer: integer): integer + +---@param arg_lead string +---@param cmdline string +---@param cursor_pos integer +local function command_complete(arg_lead, cmdline, cursor_pos) + if #arg_lead > 0 and vim.startswith("preview", arg_lead) then + return { "preview=true", "preview=false" } + end + + return require("decipher").supported_codecs() +end + +---@param args string +---@return string[], string[], string[], string[] +local function parse_command_args(args) + local decipher = require("decipher") + local codecs, options, unrecognised_args, errors = {}, {}, {}, {} + local supported_codecs = decipher.supported_codecs() + + for _, arg in ipairs(vim.split(args, " ", { plain = true, trimempty = true })) do + ---@type string? + local preview_match = arg:match("preview=(%S+)") + + if preview_match then + if preview_match ~= "true" and preview_match ~= "false" then + table.insert(errors, "Invalid value for option 'preview', expected 'true' or 'false'") + else + options.preview = preview_match == "true" + end + elseif vim.tbl_contains(supported_codecs, arg) then + table.insert(codecs, arg) + else + table.insert(unrecognised_args, arg) + end + end + + return codecs, options, unrecognised_args, errors +end + +---@param codecs string[] +---@param unrecognised_args string[] +---@param errors string[] +local function process_command_args(codecs, unrecognised_args, errors) + if #codecs > 1 then + error("Multiple valid codecs given, specify only one") + end + + if #errors > 0 then + error(("Found one or more errors: %s"):format(vim.fn.join(errors, ", "))) + end + + if #unrecognised_args > 0 then + error(("One or more unrecognised arguments: %s"):format(vim.fn.join(unrecognised_args, ", "))) + end +end + +---@param codec_func_name string +---@return decipher.CommandPreviewFunc +local function create_preview_func(codec_func_name) + return function(options, ns_id, preview_buffer) + local buffer = vim.api.nvim_get_current_buf() + local region = require("decipher.selection").get_selection("visual") + local codecs, _ = parse_command_args(options.args) + + if #codecs == 0 then + return 0 + end + + local text = vim.api.nvim_buf_get_text( + buffer, + region.start.lnum - 1, + region.start.col - 1, + region["end"].lnum - 1, + region["end"].col, + {} + ) + + local codec_value = require("decipher")[codec_func_name](codecs[1], text[1]) + + vim.api.nvim_buf_set_text( + buffer, + region.start.lnum - 1, + region.start.col - 1, + region["end"].lnum - 1, + region["end"].col, + { codec_value } + ) + + vim.hl.range( + buffer, + ns_id, + "Title", + { region.start.lnum - 1, region.start.col - 1 }, + { region["end"].lnum - 1, region.start.col - 1 + #codec_value } + ) + + if preview_buffer then + local lines = vim.api.nvim_buf_get_lines(buffer, region.start.lnum - 1, region["end"].lnum, true) + + vim.api.nvim_buf_set_lines(preview_buffer, 0, -1, false, lines) + vim.hl.range(preview_buffer, ns_id, "Title", { 0, region.start.col - 1 }, { -1, region["end"].col - 1 + #codec_value }) + end + + return 2 + end +end + +vim.api.nvim_create_user_command("DecipherVersion", function() + local v = require("decipher").version() + vim.cmd.echo("'" .. v .. "'") +end, { nargs = 0 }) + +vim.api.nvim_create_user_command("DecipherEncode", function(args) + local codecs, options, unrecognised_args, errors = parse_command_args(args.args) + + process_command_args(codecs, unrecognised_args, errors) + + local decipher = require("decipher") + vim.print(vim.inspect(require("decipher.selection").get_visual_selection())) + if #codecs == 0 then + decipher.encode_selection_prompt(options) + else + decipher.encode_selection(codecs[1], options) + end +end, { + range = true, + nargs = "?", + complete = command_complete, + desc = "Encode a visual selection", + preview = create_preview_func("encode"), +}) + +vim.api.nvim_create_user_command("DecipherDecode", function(args) + local codecs, options, unrecognised_args, errors = parse_command_args(args.args) + + process_command_args(codecs, unrecognised_args, errors) + + local decipher = require("decipher") + + if #codecs == 0 then + decipher.decode_selection_prompt(options) + else + decipher.decode_selection(codecs[1], options) + end +end, { + range = true, + nargs = "?", + complete = command_complete, + desc = "Decode a visual selection", + preview = create_preview_func("decode"), +}) diff --git a/tests/minimal_init.lua b/tests/minimal_init.lua index fb62299..3470249 100644 --- a/tests/minimal_init.lua +++ b/tests/minimal_init.lua @@ -1,4 +1,5 @@ vim.opt.rtp:append(".") vim.opt.rtp:append("~/.local/share/nvim/lazy/plenary.nvim") +vim.cmd.runtime({ "plugin/decipher.lua", bang = true }) vim.cmd.runtime({ "plugin/plenary.vim", bang = true }) diff --git a/tests/plugin_spec.lua b/tests/plugin_spec.lua new file mode 100644 index 0000000..75eb188 --- /dev/null +++ b/tests/plugin_spec.lua @@ -0,0 +1,34 @@ +local vader = require("decipher.util.vader") + +local given, normal, expect = vader.given, vader.normal, vader.expect + +describe("commands", function() + it("runs DecipherVersion", function() + assert.are.same(vim.fn.exists(":DecipherVersion"), 2) + + local output = vim.fn.execute("DecipherVersion", "") + local version = vim.split(output, "\n", { plain = true, trimempty = true })[1] + + assert.are.same(version, "3.0.0") -- TODO: Match instead? + end) + + it("runs DecipherEncode", function() + assert.are.same(vim.fn.exists(":DecipherEncode"), 2) + + given({ "light work" }, function(context) + normal("gg0vg_") + vim.cmd("DecipherEncode base64") + expect({ "bGlnaHQgd29yaw==" }) + end) + end) + + it("runs DecipherDecode", function() + assert.are.same(vim.fn.exists(":DecipherDecode"), 2) + + given({ "bGlnaHQgd29yaw==" }, function(context) + normal("gg0vg_") + vim.cmd("DecipherDecode base64") + expect({ "light work" }) + end) + end) +end) From ad8320335fde32952a3d97a67048f3f383152c96 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Fri, 27 Mar 2026 16:23:38 +0100 Subject: [PATCH 02/14] Minor type fixes --- lua/decipher/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/decipher/init.lua b/lua/decipher/init.lua index e1ef5bc..692ad48 100644 --- a/lua/decipher/init.lua +++ b/lua/decipher/init.lua @@ -26,7 +26,7 @@ function decipher.version() return decipher_version end ----@return decipher.Codecs[] a list of the names of supported codecs +---@return decipher.Codecs[] # a list of the names of supported codecs function decipher.supported_codecs() return codecs.supported() end @@ -34,7 +34,7 @@ end ---@param codec_name decipher.CodecArg ---@param value string ---@param func_key "encode" | "decode" ----@return string | nil +---@return string? local function handle_codec(codec_name, value, func_key) local codec = codecs.get(codec_name) @@ -53,14 +53,14 @@ end ---@param codec_name decipher.CodecArg ---@param value string value to encode ----@return string | nil the encoded value or nil if encoding failed +---@return string? the encoded value or nil if encoding failed function decipher.encode(codec_name, value) return handle_codec(codec_name, value, "encode") end ---@param codec_name decipher.CodecArg ---@param value string value to decode ----@return string | nil the decoded value or nil if decoding failed +---@return string? the decoded value or nil if decoding failed function decipher.decode(codec_name, value) return handle_codec(codec_name, value, "decode") end From 343f56df9f27560e40d29ebea7a314673d618014 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Fri, 27 Mar 2026 20:09:33 +0100 Subject: [PATCH 03/14] Remove print statement --- plugin/decipher.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin/decipher.lua b/plugin/decipher.lua index 70714e4..07a186d 100644 --- a/plugin/decipher.lua +++ b/plugin/decipher.lua @@ -120,7 +120,6 @@ vim.api.nvim_create_user_command("DecipherEncode", function(args) process_command_args(codecs, unrecognised_args, errors) local decipher = require("decipher") - vim.print(vim.inspect(require("decipher.selection").get_visual_selection())) if #codecs == 0 then decipher.encode_selection_prompt(options) else From 9f09e52ae2acb7dd8bfdaeb34e805dab634fe1f2 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Fri, 27 Mar 2026 22:01:18 +0100 Subject: [PATCH 04/14] Add tests for invalid command arguments --- tests/plugin_spec.lua | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/plugin_spec.lua b/tests/plugin_spec.lua index 75eb188..acf5954 100644 --- a/tests/plugin_spec.lua +++ b/tests/plugin_spec.lua @@ -22,6 +22,36 @@ describe("commands", function() end) end) + it("handles invalid options for 'preview'", function() + local ok, err = pcall(vim.fn.execute, "DecipherEncode preview=nope", "") + + assert.is_false(ok) + + ---@diagnostic disable-next-line: undefined-field + assert.has_match( + "Found one or more errors: Invalid value for option 'preview', expected 'true' or 'false'", + err + ) + end) + + it("handles more than one codec", function() + local ok, err = pcall(vim.fn.execute, "DecipherEncode base64 base32", "") + + assert.is_false(ok) + + ---@diagnostic disable-next-line: undefined-field + assert.has_match("Multiple valid codecs given, specify only one", err) + end) + + it("handles unknown arguments", function() + local ok, err = pcall(vim.fn.execute, "DecipherEncode nope preview=true", "") + + assert.is_false(ok) + + ---@diagnostic disable-next-line: undefined-field + assert.has_match("One or more unrecognised arguments: nope", err) + end) + it("runs DecipherDecode", function() assert.are.same(vim.fn.exists(":DecipherDecode"), 2) From 40879e0180c4288fb6645bca379c1005d225fb0b Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Fri, 27 Mar 2026 23:00:43 +0100 Subject: [PATCH 05/14] Match on version in test instead --- tests/plugin_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/plugin_spec.lua b/tests/plugin_spec.lua index acf5954..da5614a 100644 --- a/tests/plugin_spec.lua +++ b/tests/plugin_spec.lua @@ -9,7 +9,8 @@ describe("commands", function() local output = vim.fn.execute("DecipherVersion", "") local version = vim.split(output, "\n", { plain = true, trimempty = true })[1] - assert.are.same(version, "3.0.0") -- TODO: Match instead? + ---@diagnostic disable-next-line: undefined-field + assert.has_match("^%d+%.%d+%.%d+$", version) end) it("runs DecipherEncode", function() From 85d9733afb991d6f5d570090d3a7601f8d781c53 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Fri, 27 Mar 2026 23:01:15 +0100 Subject: [PATCH 06/14] Minor refactor --- plugin/decipher.lua | 42 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/plugin/decipher.lua b/plugin/decipher.lua index 07a186d..fbe7e45 100644 --- a/plugin/decipher.lua +++ b/plugin/decipher.lua @@ -62,50 +62,34 @@ end ---@return decipher.CommandPreviewFunc local function create_preview_func(codec_func_name) return function(options, ns_id, preview_buffer) - local buffer = vim.api.nvim_get_current_buf() - local region = require("decipher.selection").get_selection("visual") local codecs, _ = parse_command_args(options.args) if #codecs == 0 then return 0 end - local text = vim.api.nvim_buf_get_text( - buffer, - region.start.lnum - 1, - region.start.col - 1, - region["end"].lnum - 1, - region["end"].col, - {} - ) + local buffer = vim.api.nvim_get_current_buf() + local region = require("decipher.selection").get_selection("visual") + local start_lnum, end_lnum = region.start.lnum - 1, region["end"].lnum - 1 + local start_col, end_col = region.start.col - 1, region["end"].col - 1 + + local text = vim.api.nvim_buf_get_text(buffer, start_lnum, start_col, end_lnum, end_col, {}) local codec_value = require("decipher")[codec_func_name](codecs[1], text[1]) - vim.api.nvim_buf_set_text( - buffer, - region.start.lnum - 1, - region.start.col - 1, - region["end"].lnum - 1, - region["end"].col, - { codec_value } - ) - - vim.hl.range( - buffer, - ns_id, - "Title", - { region.start.lnum - 1, region.start.col - 1 }, - { region["end"].lnum - 1, region.start.col - 1 + #codec_value } - ) + -- Set preview text and highlight in buffer + vim.api.nvim_buf_set_text(buffer, start_lnum, start_col, end_lnum, end_col + 1, { codec_value }) + vim.hl.range(buffer, ns_id, "Title", { start_lnum, start_col }, { end_lnum, start_col + #codec_value }) + -- Set preview text and highlight in preview buffer if enabled if preview_buffer then - local lines = vim.api.nvim_buf_get_lines(buffer, region.start.lnum - 1, region["end"].lnum, true) + local lines = vim.api.nvim_buf_get_lines(buffer, start_lnum, end_lnum, true) vim.api.nvim_buf_set_lines(preview_buffer, 0, -1, false, lines) - vim.hl.range(preview_buffer, ns_id, "Title", { 0, region.start.col - 1 }, { -1, region["end"].col - 1 + #codec_value }) + vim.hl.range(preview_buffer, ns_id, "Title", { 0, start_col - 1 }, { -1, end_col - 1 + #codec_value }) end - return 2 + return 2 -- TODO: Double-check end end From 6dea36c6ce239979cd8b2d037e31b88bf0b47760 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Sun, 19 Apr 2026 15:42:55 +0200 Subject: [PATCH 07/14] Add documentation --- README.md | 24 ++++++++++++++++++++++++ doc/decipher.txt | 29 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/README.md b/README.md index 83bd4c8..2241946 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ - [Installing](#installing) - [Setup](#setup) +- [Commands](#commands) +- [JSON view](#json-view) - [Example keymaps](#example-keymaps) - [Supported Codecs](#supported-codecs) - [base32](#base32) @@ -100,6 +102,28 @@ require("decipher").setup({ }) ``` +## Commands + +### `DecipherVersion` + +Prints the current version + +### `[range]DecipherEncode [options]` + +Encode the selected visual range or the last visual selection with the codec +given on the command line. Prompts for a codec if not given. Also takes an +option for previewing i.e. `preview=true|false`. + +Example: `:'<,'>DecipherEncode crockford preview=false` + +### `[range]DecipherDecode [options]` + +Decode the selected visual range or the last visual selection with the codec +given on the command line. Prompts for a codec if not given. Also takes an +option for previewing i.e. `preview=true|false`. + +Example: `:'<,'>DecipherDecode base64 preview=true` + ## JSON view ### Applying diff --git a/doc/decipher.txt b/doc/decipher.txt index 4bb5299..bbcf5d7 100644 --- a/doc/decipher.txt +++ b/doc/decipher.txt @@ -19,6 +19,7 @@ decipher *decipher* Setup ....................................................... |decipher.setup| Types ....................................................... |decipher.types| Functions ............................................... |decipher.functions| + Commands ................................................. |decipher.commands| Mappings ................................................. |decipher.mappings| Motions ................................................... |decipher.motions| Highlights ............................................. |decipher.highlights| @@ -213,6 +214,34 @@ decode_motion_prompt({options}) *decipher.decode_motion_prompt* Parameters: • {options} (`decipher.Options`) Options to use +============================================================================== +Commands *decipher.commands* + +:DecipherVersion *:DecipherVersion* + Prints the current version + +:[range]DecipherEncode [options] *:DecipherEncode* + Encode the selected visual range or the last + visual selection with the codec given on the + command line. Prompts for a codec if not + given. Also takes an option for previewing + i.e. `preview=true|false`. + + Example: + > + :'<,'>DecipherEncode crockford preview=false + +:[range]DecipherDecode [options] *:DecipherDecode* + Decode the selected visual range or the last + visual selection with the codec given on the + command line. Prompts for a codec if not + given. Also takes an option for previewing + i.e. `preview=true|false`. + + Example: + > + :'<,'>DecipherDecode base64 preview=true + ============================================================================== Mappings *decipher.mappings* From e22d1dcae0617e9a58d01d2edd3f8aac12085249 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Sun, 19 Apr 2026 15:45:18 +0200 Subject: [PATCH 08/14] Simplify command argument completion --- plugin/decipher.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugin/decipher.lua b/plugin/decipher.lua index fbe7e45..8e990a1 100644 --- a/plugin/decipher.lua +++ b/plugin/decipher.lua @@ -3,15 +3,17 @@ ---ns_id: integer, ---preview_buffer: integer): integer +local command_arguments + ---@param arg_lead string ---@param cmdline string ---@param cursor_pos integer local function command_complete(arg_lead, cmdline, cursor_pos) - if #arg_lead > 0 and vim.startswith("preview", arg_lead) then - return { "preview=true", "preview=false" } + if not command_arguments then + command_arguments = vim.list_extend({ "preview=true", "preview=false" }, require("decipher").supported_codecs()) end - return require("decipher").supported_codecs() + return command_arguments end ---@param args string From c9861a76229993513cb766aeaa7da0aa00ad6b30 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Sun, 26 Apr 2026 22:19:17 +0200 Subject: [PATCH 09/14] Mention command previewing --- README.md | 4 ++++ doc/decipher.txt | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index 2241946..dcddb8b 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,8 @@ Encode the selected visual range or the last visual selection with the codec given on the command line. Prompts for a codec if not given. Also takes an option for previewing i.e. `preview=true|false`. +The command supports command previewing, see `:h 'inccommand'`. + Example: `:'<,'>DecipherEncode crockford preview=false` ### `[range]DecipherDecode [options]` @@ -122,6 +124,8 @@ Decode the selected visual range or the last visual selection with the codec given on the command line. Prompts for a codec if not given. Also takes an option for previewing i.e. `preview=true|false`. +The command supports command previewing, see `:h 'inccommand'`. + Example: `:'<,'>DecipherDecode base64 preview=true` ## JSON view diff --git a/doc/decipher.txt b/doc/decipher.txt index bbcf5d7..99cd082 100644 --- a/doc/decipher.txt +++ b/doc/decipher.txt @@ -227,6 +227,9 @@ Commands *decipher.commands* given. Also takes an option for previewing i.e. `preview=true|false`. + The command supports command previewing, see + |'inccommand'|. + Example: > :'<,'>DecipherEncode crockford preview=false @@ -238,6 +241,9 @@ Commands *decipher.commands* given. Also takes an option for previewing i.e. `preview=true|false`. + The command supports command previewing, see + |'inccommand'|. + Example: > :'<,'>DecipherDecode base64 preview=true From 17286cc584acd1ac1cb9d089e466db3f00710afc Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Sat, 2 May 2026 13:01:27 +0200 Subject: [PATCH 10/14] Minor fixes --- README.md | 2 +- doc/decipher.txt | 2 +- plugin/decipher.lua | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dcddb8b..22c7ce6 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ require("decipher").setup({ ### `DecipherVersion` -Prints the current version +Prints the current version. ### `[range]DecipherEncode [options]` diff --git a/doc/decipher.txt b/doc/decipher.txt index 99cd082..1838622 100644 --- a/doc/decipher.txt +++ b/doc/decipher.txt @@ -218,7 +218,7 @@ decode_motion_prompt({options}) *decipher.decode_motion_prompt* Commands *decipher.commands* :DecipherVersion *:DecipherVersion* - Prints the current version + Prints the current version. :[range]DecipherEncode [options] *:DecipherEncode* Encode the selected visual range or the last diff --git a/plugin/decipher.lua b/plugin/decipher.lua index 8e990a1..71f4233 100644 --- a/plugin/decipher.lua +++ b/plugin/decipher.lua @@ -3,11 +3,13 @@ ---ns_id: integer, ---preview_buffer: integer): integer +---@type string[]? local command_arguments ---@param arg_lead string ---@param cmdline string ---@param cursor_pos integer +---@return string[] local function command_complete(arg_lead, cmdline, cursor_pos) if not command_arguments then command_arguments = vim.list_extend({ "preview=true", "preview=false" }, require("decipher").supported_codecs()) @@ -64,7 +66,7 @@ end ---@return decipher.CommandPreviewFunc local function create_preview_func(codec_func_name) return function(options, ns_id, preview_buffer) - local codecs, _ = parse_command_args(options.args) + local codecs, _, _, _ = parse_command_args(options.args) if #codecs == 0 then return 0 @@ -96,8 +98,8 @@ local function create_preview_func(codec_func_name) end vim.api.nvim_create_user_command("DecipherVersion", function() - local v = require("decipher").version() - vim.cmd.echo("'" .. v .. "'") + local version = require("decipher").version() + vim.cmd.echo("'" .. version .. "'") end, { nargs = 0 }) vim.api.nvim_create_user_command("DecipherEncode", function(args) @@ -106,6 +108,7 @@ vim.api.nvim_create_user_command("DecipherEncode", function(args) process_command_args(codecs, unrecognised_args, errors) local decipher = require("decipher") + if #codecs == 0 then decipher.encode_selection_prompt(options) else From af764f6b3c5ac3b47776c07a7f6856e460f84176 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Sat, 16 May 2026 21:44:42 +0200 Subject: [PATCH 11/14] Fix command preview --- lua/decipher/init.lua | 2 ++ plugin/decipher.lua | 28 ++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lua/decipher/init.lua b/lua/decipher/init.lua index 692ad48..b6d97d5 100644 --- a/lua/decipher/init.lua +++ b/lua/decipher/init.lua @@ -227,6 +227,8 @@ function decipher.setup(user_config) config.setup(user_config) ui.float.setup() + + vim.cmd([[hi default link DecipherCommandPreview Title]]) end return decipher diff --git a/plugin/decipher.lua b/plugin/decipher.lua index 71f4233..fe72b9b 100644 --- a/plugin/decipher.lua +++ b/plugin/decipher.lua @@ -62,7 +62,7 @@ local function process_command_args(codecs, unrecognised_args, errors) end end ----@param codec_func_name string +---@param codec_func_name "encode" | "decode" ---@return decipher.CommandPreviewFunc local function create_preview_func(codec_func_name) return function(options, ns_id, preview_buffer) @@ -76,24 +76,36 @@ local function create_preview_func(codec_func_name) local region = require("decipher.selection").get_selection("visual") local start_lnum, end_lnum = region.start.lnum - 1, region["end"].lnum - 1 local start_col, end_col = region.start.col - 1, region["end"].col - 1 + local text = vim.api.nvim_buf_get_text(buffer, start_lnum, start_col, end_lnum, end_col + 1, {}) + local ok, result = pcall(require("decipher")[codec_func_name], codecs[1], table.concat(text, "\n")) - local text = vim.api.nvim_buf_get_text(buffer, start_lnum, start_col, end_lnum, end_col, {}) + if not ok then + return 0 + end - local codec_value = require("decipher")[codec_func_name](codecs[1], text[1]) + local new_lines = vim.split(result, "\r?\n") + local new_end_lnum = end_lnum + #new_lines - 1 + local new_end_col = #new_lines[#new_lines] + + if codec_func_name == "decode" and #new_lines == 1 then + -- If there is only one decoded line offset the new end column by + -- the original start column + new_end_col = start_col + new_end_col + end -- Set preview text and highlight in buffer - vim.api.nvim_buf_set_text(buffer, start_lnum, start_col, end_lnum, end_col + 1, { codec_value }) - vim.hl.range(buffer, ns_id, "Title", { start_lnum, start_col }, { end_lnum, start_col + #codec_value }) + vim.api.nvim_buf_set_text(buffer, start_lnum, start_col, end_lnum, end_col + 1, new_lines) + vim.hl.range(buffer, ns_id, "DecipherCommandPreview", { start_lnum, start_col }, { new_end_lnum, new_end_col }) -- Set preview text and highlight in preview buffer if enabled if preview_buffer then - local lines = vim.api.nvim_buf_get_lines(buffer, start_lnum, end_lnum, true) + local lines = vim.api.nvim_buf_get_lines(buffer, start_lnum, new_end_lnum + 1, true) vim.api.nvim_buf_set_lines(preview_buffer, 0, -1, false, lines) - vim.hl.range(preview_buffer, ns_id, "Title", { 0, start_col - 1 }, { -1, end_col - 1 + #codec_value }) + vim.hl.range(preview_buffer, ns_id, "DecipherCommandPreview", { 0, start_col }, { #new_lines, new_end_col }) end - return 2 -- TODO: Double-check + return 2 end end From fdb5220823c8f221cb74548f3612e4aae587ec5b Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Sat, 16 May 2026 21:49:36 +0200 Subject: [PATCH 12/14] Pass options instead of 5 arguments --- lua/decipher/init.lua | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/lua/decipher/init.lua b/lua/decipher/init.lua index b6d97d5..957d36b 100644 --- a/lua/decipher/init.lua +++ b/lua/decipher/init.lua @@ -19,6 +19,13 @@ decipher.codec = codecs.codec ---@field selection_type decipher.SelectionType ---@field codec_type "encode" | "decode" +---@class decipher.SetTextRegionOptions +---@field codec_name string +---@field codec_type "encode" | "decode" +---@field status boolean +---@field value string +---@field selection_type decipher.SelectionType + ---@alias decipher.CodecArg string | decipher.Codecs ---@return string @@ -96,24 +103,31 @@ local function open_float_handler(codec_name, status, value, selection_type, cod end --- Handler for setting a text region to a value ----@param codec_name string ----@param status boolean ----@param value string ----@param selection_type decipher.SelectionType ----@diagnostic disable-next-line:unused-local -local function set_text_region_handler(codec_name, status, value, selection_type) - if not status then - notifications.error(("%s: %s"):format(codec_name, value)) +---@param options decipher.SetTextRegionOptions +local function set_text_region_handler(options) + local value = options.value + + if not options.status then + notifications.error(("%s: %s"):format(options.codec_name, value)) return end if value == nil then - notifications.codec_not_found("Codec not found: " .. codec_name) + notifications.codec_not_found("Codec not found: " .. options.codec_name) return end + ---@type string[] + local lines + + if options.codec_type == "decode" then + lines = vim.split(value, "\r?\n") + else + lines = { value } + end + -- Escape the string since you cannot set lines in a buffer if it contains newlines - selection.set_text(0, selection_type, str_utils.escape_newlines({ value })) + selection.set_text(0, options.selection_type, str_utils.escape_newlines(lines)) end --- Process a codec action @@ -134,7 +148,13 @@ local function process_codec(codec_name, codec_func, selection_type, codec_type, if do_preview then open_float_handler(_codec_name, status, value, selection_type, codec_type) else - set_text_region_handler(_codec_name, status, value, selection_type) + set_text_region_handler({ + codec_name = _codec_name, + status = status, + value = value, + selection_type = selection_type, + codec_type = codec_type, + }) end end From 698e890dc5d21bb4d23cca916f02809e5a41cc75 Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Sat, 16 May 2026 22:34:07 +0200 Subject: [PATCH 13/14] Do not show preview if there are invalid command args --- plugin/decipher.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugin/decipher.lua b/plugin/decipher.lua index fe72b9b..c02ae47 100644 --- a/plugin/decipher.lua +++ b/plugin/decipher.lua @@ -66,9 +66,10 @@ end ---@return decipher.CommandPreviewFunc local function create_preview_func(codec_func_name) return function(options, ns_id, preview_buffer) - local codecs, _, _, _ = parse_command_args(options.args) + local codecs, _, unrecognised_args, errors = parse_command_args(options.args) - if #codecs == 0 then + if #codecs ~= 1 or #unrecognised_args > 0 or #errors > 0 then + -- Do not show preview return 0 end @@ -80,6 +81,7 @@ local function create_preview_func(codec_func_name) local ok, result = pcall(require("decipher")[codec_func_name], codecs[1], table.concat(text, "\n")) if not ok then + -- Do not show preview return 0 end From e1930558e0046a43ba1df8928e4bb9ec6e092dab Mon Sep 17 00:00:00 2001 From: MisanthropicBit Date: Sat, 16 May 2026 22:36:13 +0200 Subject: [PATCH 14/14] Add DecipherCommandPreview to doc --- doc/decipher.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/decipher.txt b/doc/decipher.txt index 1838622..538e647 100644 --- a/doc/decipher.txt +++ b/doc/decipher.txt @@ -292,6 +292,10 @@ Highlights *decipher.highlights* Highlight group for the title of the floating window preview. Defaults to *Title*. +*DecipherCommandPreview* + Highlight group used in command previews for the encoded/decoded text. + Defaults to *Title*. + ============================================================================== Codecs *decipher.codecs*