Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

- [Installing](#installing)
- [Setup](#setup)
- [Commands](#commands)
- [JSON view](#json-view)
- [Example keymaps](#example-keymaps)
- [Supported Codecs](#supported-codecs)
- [base32](#base32)
Expand Down Expand Up @@ -100,6 +102,32 @@ 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`.

The command supports command previewing, see `:h 'inccommand'`.

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`.

The command supports command previewing, see `:h 'inccommand'`.

Example: `:'<,'>DecipherDecode base64 preview=true`

## JSON view

### Applying
Expand Down
39 changes: 39 additions & 0 deletions doc/decipher.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down Expand Up @@ -213,6 +214,40 @@ 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`.

The command supports command previewing, see
|'inccommand'|.

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`.

The command supports command previewing, see
|'inccommand'|.

Example:
>
:'<,'>DecipherDecode base64 preview=true

==============================================================================
Mappings *decipher.mappings*

Expand Down Expand Up @@ -257,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*

Expand Down
52 changes: 37 additions & 15 deletions lua/decipher/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,29 @@ 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
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

---@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)

Expand All @@ -53,14 +60,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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -227,6 +247,8 @@ function decipher.setup(user_config)

config.setup(user_config)
ui.float.setup()

vim.cmd([[hi default link DecipherCommandPreview Title]])
end

return decipher
157 changes: 157 additions & 0 deletions plugin/decipher.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---@alias decipher.CommandPreviewFunc fun(
---options: vim.api.keyset.create_user_command.command_args,
---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())
end

return command_arguments
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 "encode" | "decode"
---@return decipher.CommandPreviewFunc
local function create_preview_func(codec_func_name)
return function(options, ns_id, preview_buffer)
local codecs, _, unrecognised_args, errors = parse_command_args(options.args)

if #codecs ~= 1 or #unrecognised_args > 0 or #errors > 0 then
-- Do not show preview
return 0
end

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 + 1, {})
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

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, 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, new_end_lnum + 1, true)

vim.api.nvim_buf_set_lines(preview_buffer, 0, -1, false, lines)
vim.hl.range(preview_buffer, ns_id, "DecipherCommandPreview", { 0, start_col }, { #new_lines, new_end_col })
end

return 2
end
end

vim.api.nvim_create_user_command("DecipherVersion", function()
local version = require("decipher").version()
vim.cmd.echo("'" .. version .. "'")
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")

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"),
})
1 change: 1 addition & 0 deletions tests/minimal_init.lua
Original file line number Diff line number Diff line change
@@ -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 })
Loading
Loading