Skip to content

Commit

Permalink
feat(api): add vscode.to_op and refactor code actions (#1570)
Browse files Browse the repository at this point in the history
* feat(api): add `vscode.to_op`

feat: refactor `==` and `gc`, support motions and dotrepeat

* feat: improve

* docs: clean up

* feat: map gq

add usage in comment

* chore: tweak formatting
  • Loading branch information
xiyaowong committed Oct 27, 2023
1 parent 814344d commit c9d600f
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 32 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ Load module: `local vscode = require("vscode-neovim")`
5. `vscode.get_config` get a configuration value
6. `vscode.update_config` update a configuration
7. `vscode.notify` like `vim.notify`, but use vscode notification to show the message
8. `vscode.to_op` A helper for `map-operator`. See [code_actions.lua](./runtime/plugin/code_actions.lua) for the usage

#### Actions

Expand Down
2 changes: 2 additions & 0 deletions runtime/lua/vscode-neovim.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ local vscode = {
update_config = api.update_config,
-- notifications
notify = api.notify,
-- operatorfunc helper
to_op = api.to_op,
}

return vscode
83 changes: 83 additions & 0 deletions runtime/lua/vscode-neovim/api.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
local api = vim.api
local fn = vim.fn

local M = {}

------------------------
Expand Down Expand Up @@ -278,4 +281,84 @@ function M.notify(msg, level, opts)
M.action("notify", { args = { msg, level_name } })
end

do
---@class Context
---@field range lsp.Range
---@field is_linewise boolean true indicates linewise, otherwise it is charwise.
---@field is_single_line boolean true if start.line and end.line are equal.
---@field is_current_line boolean is single line, and is current line

local op_func_id = 0

---@see map-operator
---
---Example: Remap 'gq' to use 'editor.action.formatSelection'
---
---```lua
--- local format = vscode.to_op(function(ctx)
--- vscode.action("editor.action.formatSelection", { range = ctx.range })
--- end)
---
--- vim.keymap.set({ "n", "x" }, "gq", format, { expr = true })
--- vim.keymap.set({ "n" }, "gqq", function()
--- return format() .. "_"
--- end, { expr = true })
---````
function M.to_op(func)
op_func_id = op_func_id + 1
local op_func_name = "__vscode_op_func_" .. tostring(op_func_id)
local operatorfunc = "v:lua." .. op_func_name

local op_func = function(motion)
local mode = api.nvim_get_mode().mode
if not motion then
if mode == "n" then
vim.go.operatorfunc = operatorfunc
return "g@"
elseif mode ~= "\x16" and mode:lower() ~= "v" then
return "<Ignore>"
end
end

local start_pos
local end_pos
if motion then
start_pos = api.nvim_buf_get_mark(0, "[")
end_pos = api.nvim_buf_get_mark(0, "]")
else
local a = fn.getpos("v")
local b = fn.getpos(".")
start_pos = { a[2], a[3] - 1 }
end_pos = { b[2], b[3] - 1 }
end

if start_pos[1] > end_pos[1] or (start_pos[1] == end_pos[1] and start_pos[2] > end_pos[2]) then
start_pos, end_pos = end_pos, start_pos
end

local is_linewise = motion == "line" or mode == "V"
if is_linewise then
start_pos = { start_pos[1], 0 }
end_pos = { end_pos[1], api.nvim_strwidth(fn.getline(end_pos[1])) - 1 }
end

local range = vim.lsp.util.make_given_range_params(start_pos, end_pos, 0, "utf-16").range
local is_single_line = range.start.line == range["end"].line
local is_current_line = is_single_line and range.start.line == fn.line(".") - 1
---@type Context
local ctx = {
range = range,
is_linewise = is_linewise,
is_single_line = is_single_line,
is_current_line = is_current_line,
}
func(ctx)
return "<Ignore>"
end

_G[op_func_name] = op_func
return _G[op_func_name]
end
end

return M
53 changes: 53 additions & 0 deletions runtime/plugin/code_actions.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
local vscode = require("vscode-neovim")

local function esc()
local key = vim.api.nvim_replace_termcodes("<esc>", true, true, true)
vim.api.nvim_feedkeys(key, "n", false)
end

local k = function(mode, lhs, rhs)
vim.keymap.set(mode, lhs, rhs, { expr = true }) -- expr is required
end

------------
-- Format --
------------
local format = vscode.to_op(function(ctx)
vscode.action("editor.action.formatSelection", { range = ctx.range, callback = esc })
end)
local format_line = function()
return format() .. "_"
end

k({ "n", "x" }, "gq", format)
k({ "n" }, "gqq", format_line)
k({ "n", "x" }, "=", format)
k({ "n" }, "==", format_line)

-------------
-- Comment --
-------------
local comment = vscode.to_op(function(ctx)
local cmd = ctx.is_linewise and "editor.action.commentLine" or "editor.action.blockComment"
local opts = { range = ctx.range, callback = esc }
if ctx.is_linewise and ctx.is_current_line then
opts.range = nil
end
vscode.action(cmd, opts)
end)

local comment_line = function()
return comment() .. "_"
end

k({ "n", "x" }, "gc", comment)
k({ "n" }, "gcc", comment_line)
k({ "x" }, "<C-/>", comment)
k({ "n" }, "<C-/>", comment_line)
-- legacy {{{
k({ "n", "x" }, "<Plug>VSCodeCommentary", comment)
k({ "n" }, "<Plug>VSCodeCommentaryLine", comment_line)
vim.api.nvim_create_user_command("VSCodeCommentary", function(arg)
vscode.action("editor.action.commentLine", { range = { arg.line1 - 1, arg.line2 - 1 } })
end, { bang = true, range = true })
-- }}}
29 changes: 0 additions & 29 deletions vim/vscode-code-actions.vim
Original file line number Diff line number Diff line change
@@ -1,32 +1,3 @@
" Bind format and comment to vscode format/comment command
xnoremap = <Cmd>call VSCodeCall('editor.action.formatSelection')<CR>
nnoremap = <Cmd>call VSCodeCall('editor.action.formatSelection')<CR><Esc>
nnoremap == <Cmd>call VSCodeCall('editor.action.formatSelection')<CR>
function! s:vscodeCommentary(...) abort
if !a:0
let &operatorfunc = matchstr(expand('<sfile>'), '[^. ]*$')
return 'g@'
elseif a:0 > 1
let [line1, line2] = [a:1, a:2]
else
let [line1, line2] = [line("'["), line("']")]
endif

call VSCodeCallRange('editor.action.commentLine', line1, line2, 0)
endfunction


command! -range -bar VSCodeCommentary call s:vscodeCommentary(<line1>, <line2>)

xnoremap <expr> <Plug>VSCodeCommentary <SID>vscodeCommentary()
nnoremap <expr> <Plug>VSCodeCommentary <SID>vscodeCommentary()
nnoremap <expr> <Plug>VSCodeCommentaryLine <SID>vscodeCommentary() . '_'
" Bind C-/ to vscode commentary to add dot-repeat and auto-deselection
xnoremap <expr> <C-/> <SID>vscodeCommentary()
nnoremap <expr> <C-/> <SID>vscodeCommentary() . '_'
function! s:vscodeGoToDefinition(str)
if exists('b:vscode_controlled') && b:vscode_controlled
call VSCodeNotify('editor.action.' . a:str)
Expand Down
5 changes: 2 additions & 3 deletions vim/vscode-neovim.vim
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,5 @@ augroup VscodeGeneral
augroup END


lua << EOF
require("vscode-neovim")
EOF
lua require("vscode-neovim")
execute 'source ' . fnamemodify(s:currDir, ':h') . '/runtime/plugin/*.lua'

0 comments on commit c9d600f

Please sign in to comment.