Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

created transform plugin #488

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
13 changes: 13 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2257,6 +2257,19 @@
"type": "library",
"version": "1.3"
},
{
"description": "Commands for transforming cases",
"id": "transform",
"name": "Transform",
"path": "plugins/transform.lua",
"mod_version": "3",
"tags":["transform", "snake_case", "camelCase", "PascalCase"],
"type": "plugin",
"version" : "0.1.0",
"extra": {
"author": "floppyDisco"
}
},
{
"description": "Tree-sitter bindings based on `lua-tree-sitter`",
"id": "tree_sitter",
Expand Down
268 changes: 268 additions & 0 deletions transform.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
-- mod-version:3
local command = require "core.command"
local keymap = require "core.keymap"
local config = require "core.config"

-- |-------------------------|
-- | Transform |
-- |-------------------------|

--[[

The Transform plugin provides commands for transforming text
into the specified case.

this plugin supports multiple cursors.

if a selection is highlighted,
the transformation will be performed on
the entire selection

if not,
the transformation will be performed on
the word under the cursor
the word will be determined using the nonWordChars settings value
-- if a change is made to this setting the plugin will need to be reloaded

]]

local command_prefix = "Transform"
local command_names = {
["snake_case"] = command_prefix .. ": To snake_case",
["camel_case"] = command_prefix .. ": To camelCase",
["pascal_case"] = command_prefix .. ": To PascalCase",
-- determine how to display "-" in commandview
["kebab_case"] = command_prefix .. ": To kebab-case (hyphen)",
["uppercase"] = command_prefix .. ": To UPPERCASE",
["lowercase"] = command_prefix .. ": To lowercase",
["capitalize_word"] = command_prefix .. ": Capitalize Word",
["uppercase_next"] = command_prefix .. ": Uppercase Next Char",
["lowercase_next"] = command_prefix .. ": lowercase Next Char",
}

-- Retrieve non-word characters from the Lite XL settings
local nonWordChars = config.non_word_chars
local nonWordCharsSet = {[" "]=true}
for i = 1, #nonWordChars do
local char = nonWordChars:sub(i, i)
nonWordCharsSet[char] = true
end

-- Feature?
-- ------------
-- allow the user to define "wordPartSeparators"


local function get_word_or_selection(document, line1, col1, line2, col2)
-- if first position does not match second position
if line1 ~= line2 or col1 ~= col2 then

-- There is a selection
-- ------------------------
local selected_text = document:get_text(line1, col1, line2, col2)
return line1, col1, line2, col2, selected_text
else

-- No selection, get the word under the cursor
-- -----------------------------------------------
local text = document.lines[line1]
-- Find the start of the word
local word_start = col1
while word_start > 1 and not nonWordCharsSet[text:sub(word_start - 1, word_start - 1)] do
word_start = word_start - 1
end
-- Find the end of the word
local word_end = col1
while word_end <= #text and not nonWordCharsSet[text:sub(word_end, word_end)] do
word_end = word_end + 1
end
local selected_text = text:sub(word_start, word_end - 1)
return line1, word_start, line1, word_end, selected_text
end
end

local function split(s)
local result = {}
-- wordPartSeparators would be used here
for part in s:gmatch("([^-_ \n\t]+)") do
table.insert(result, part)
end
return result
end

---transforms the word or selection using the specified separator and clean function
---@param document core.doc
---@param word_part_join_value string
---@param word_part_clean fun(word_part: string, i: number) | nil
local function transform(document, word_part_join_value , word_part_clean)

if word_part_clean == nil then
word_part_clean = function (w,i) return w end
end

-- for each cursor ... iterate backwards to avoid shifting the text
for cursor_index, line1, col1, line2, col2 in document:get_selections(true, true) do

local start_line, start_col, end_line, end_col, selected_text = get_word_or_selection(document, line1, col1, line2, col2)

-- split word parts
-- --------------------
-- by (lower)(upper)
selected_text = selected_text:gsub("([%l%d])([%u])", "%1_%2")
-- by single (upper)s
selected_text = selected_text:gsub("([%u%d])(%u)(%l)", "%1_%2%3")
local parts = split(selected_text)

-- clean each word part
for i,word_part in ipairs(parts) do
parts[i] = word_part_clean(word_part, i)
end

-- replace text
local transformed_text = table.concat(parts, word_part_join_value)
document:replace_cursor(cursor_index,start_line,start_col,end_line,end_col, function() return transformed_text end)
document:move_to_cursor(cursor_index,0,#transformed_text)
end
end

local function capitalize(word)
return word:gsub("(%w)(%w*)", function(first, rest)
return first:upper() .. rest:lower()
end)
end


-- |----------------------------------|
-- | Transform Commands |
-- |----------------------------------|

-- To PascalCase
-- -----------------
command.add("core.docview", {
[command_names.pascal_case] = function(dv)
transform(dv.doc, "", capitalize)
end,
})

-- To Snake_Case
-- -----------------
command.add("core.docview", {
[command_names.snake_case] = function(dv)
transform(dv.doc, "_", function(word_part)
return word_part:lower()
end)
end,
})

-- To camelCase
-- ----------------
command.add("core.docview", {
[command_names.camel_case] = function(dv)
transform(dv.doc, "", function(word_part, i)
if i==1 then
return word_part:lower()
else
return capitalize(word_part)
end
end)
end,
})

-- To kebab-case
-- -----------------
command.add("core.docview", {
[command_names.kebab_case] = function(dv)
transform(dv.doc, "-")
end,
})

-- To UPPERCASE
-- ----------------
command.add("core.docview", {
[command_names.uppercase] = function(dv)
local document = dv.doc
for cursor_index, line1, col1, line2, col2 in document:get_selections(true, true) do
local start_line, start_col, end_line, end_col, selected_text = get_word_or_selection(document, line1, col1, line2, col2)
document:replace_cursor(cursor_index,start_line, start_col, end_line, end_col,
function ()
return selected_text:upper()
end
)
end
end,
})

-- To lowercase
-- ----------------
command.add("core.docview", {
[command_names.lowercase] = function(dv)
local document = dv.doc
for cursor_index, line1, col1, line2, col2 in document:get_selections(true, true) do
local start_line, start_col, end_line, end_col, selected_text = get_word_or_selection(document, line1, col1, line2, col2)
document:replace_cursor(cursor_index,start_line, start_col, end_line, end_col,
function ()
return selected_text:lower()
end
)
end
end,
})

-- Capitalize Word
-- -------------------
command.add("core.docview", {
[command_names.capitalize_word] = function(dv)
local document = dv.doc
for cursor_index, line1, col1, line2, col2 in document:get_selections(true, true) do
local start_line, start_col, end_line, end_col, selected_text = get_word_or_selection(document, line1, col1, line2, col2)
document:replace_cursor(cursor_index, start_line, start_col, end_line, end_col,
function ()
local words = split(selected_text)
for i,word in ipairs(words) do
words[i] = capitalize(word)
end
return table.concat(words," ")
end
)
end
end,
})

-- Uppercase Next Char
-- -----------------------
command.add("core.docview", {
[command_names.uppercase_next] = function(dv)
local document = dv.doc
for cursor_index, line1, col1 in document:get_selections(true, true) do
document:replace_cursor(cursor_index, line1, col1, line1, col1+1,
function (text)
return text:upper()
end
)
document:move_to_cursor(cursor_index,0,1)
end
end,
})

-- lowercase Next Char
-- -----------------------
command.add("core.docview", {
[command_names.lowercase_next] = function(dv)
local document = dv.doc
for cursor_index, line1, col1 in document:get_selections(true, true) do
document:replace_cursor(cursor_index, line1, col1, line1, col1+1,
function (text)
return text:lower()
end)
document:move_to_cursor(cursor_index,0,1)
end
end,
})

-- not sure best way to add a keybind for both platforms
keymap.add {
["cmd+option+u"] = command_names.uppercase_next,
["cmd+option+l"] = command_names.lowercase_next,
-- ["ctrl+alt+u"] = command_names.uppercase_next,
-- ["ctrl+alt+l"] = command_names.lowercase_next,
}