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

feat: LSP rename converts next word if located in a non word character #114

Merged
merged 2 commits into from
Dec 15, 2023
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
4 changes: 3 additions & 1 deletion lua/textcase/plugin/conversion.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@ function M.do_lsp_rename(method)
"method textDocument/rename is not supported by any of the servers registered for the current buffer"
)
else
local current_word = vim.fn.expand("<cword>")
local current_word_info = utils.get_current_word_info()
local current_word = current_word_info.word
local params = lsp.util.make_position_params()
params.position = current_word_info.position
params.newName = method(current_word)

lsp.buf_request_all(0, "textDocument/rename", params, function(results)
Expand Down
26 changes: 26 additions & 0 deletions lua/textcase/shared/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,32 @@ function utils.trim_str(str, _trimmable_chars)
return trim_info, trimmed_str
end

-- Gets the position and text of the current word under the cursor
-- If the cursor is located on a word it returns information about that word
-- If the cursor is not on a word, it returns information about the next word
-- The method could have some validation when there is no word to be returned
-- not required at the moment
-- One of the cases where this method is useful is on LSP rename,
-- If there cursor is not on a word, TextDocument/rename acts on the previous
-- word, while vim.fn.expand returns information about the following word
-- By using this method the plugin has a way of referring to the same word
function utils.get_current_word_info()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to have this method documented on a high level what it does.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local cursor_pos = vim.fn.getpos(".")
-- This could be customized to read exactly the word under the cursor, ignoring
-- close words, or even considering words before the cursor. Consult values like Wn and Wb
local start_the_search_at_cursor_position = "W"
local word = "\\w"
local current_word_pos = vim.fn.searchpos(word, start_the_search_at_cursor_position)
vim.fn.setpos(".", cursor_pos)

local line = current_word_pos[1] - 1
local character = current_word_pos[2]

local position = { line = line, character = character }

return { position = position, word = vim.fn.expand("<cword>") }
end

function utils.get_list(str, mode)
local limit = 0
local initial = nil
Expand Down
17 changes: 17 additions & 0 deletions tests/test_helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,21 @@ M.wait_for_language_server_to_start = function()
end)
end

-- This method duplicates wait_for_language_server_to_start for
-- the destructuring file. Using Write Everything Twice and
-- avoiding to come up with a wrong abstraction too early
M.wait_for_language_server_to_start_on_destructuring_file = function()
M.execute_keys("ww") -- Move to `foovar`
local hover = ""
M.wait_for(30 * 1000, function()
-- This prints one "Error detected while processing command line:" but this can be ignored
vim.lsp.buf_request_all(0, "textDocument/hover", vim.lsp.util.make_position_params(), function(results)
-- Hover will print the type definition of the variable under the cursor. Hence,
-- it should contain "fooVar".
hover = results[1].result.contents.value
end)
return string.find(hover, "fooVar")
end)
end

return M
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const [fooVar, BAR_VAR] = [0, 1]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const [FOO_VAR, barVar] = [0, 1]
1 change: 1 addition & 0 deletions tests/textcase/lsp/fixtures/destructuring.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const [fooVar, barVar] = [0, 1]
60 changes: 59 additions & 1 deletion tests/textcase/lsp/lsp_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ local test_helpers = require("tests.test_helpers")
local textcase = require("textcase")

describe("LSP", function()
describe("Rename", function()
describe("Rename case conversion", function()
before_each(function()
textcase.setup({})

Expand Down Expand Up @@ -45,4 +45,62 @@ describe("LSP", function()
assert.are.same(table.concat(content, "\n"), expected_code)
end)
end)

describe("Renaming according to cursor position", function()
before_each(function()
textcase.setup({})

local path = "./tests/textcase/lsp/fixtures/destructuring.ts"
local cmd = " silent exe 'e " .. path .. "'"
vim.cmd(cmd)
vim.bo.filetype = "typescript"

test_helpers.wait_for_language_server_to_start_on_destructuring_file()
test_helpers.execute_keys("gg0")
end)

after_each(function()
-- Close the buffer so the next test can open it again.
vim.cmd("silent exe 'bd! %'")
end)

it("Should convert word when cursor is on its last character", function()
test_helpers.execute_keys("12lgaN")
local content = nil
test_helpers.wait_for(5 * 1000, function()
content = test_helpers.get_buf_lines()
local found_modified_variable = not not string.find(content[1], "FOO_VAR")
return found_modified_variable
end)

local expected_code = test_helpers.read_file("./tests/textcase/lsp/fixtures/destructuring-foo-as-constant.ts")
assert.are.same(table.concat(content, "\n") .. "\n", expected_code)
end)

it("Should convert the next word when the cursor isn't on a word", function()
test_helpers.execute_keys("13lgaN")
local content = nil
test_helpers.wait_for(5 * 1000, function()
content = test_helpers.get_buf_lines()
local found_modified_variable = not not string.find(content[1], "BAR_VAR")
return found_modified_variable
end)

local expected_code = test_helpers.read_file("./tests/textcase/lsp/fixtures/destructuring-bar-as-constant.ts")
assert.are.same(table.concat(content, "\n") .. "\n", expected_code)
end)

it("Shouldn't modify variables when the cursor isn't on a word and no word is found next", function()
test_helpers.execute_keys("$gaN")
local content = nil
test_helpers.wait_for(5 * 1000, function()
content = test_helpers.get_buf_lines()
local found_modified_variable = not not string.find(content[1], "BAR_VAR")
return found_modified_variable
end)

local expected_code = test_helpers.read_file("./tests/textcase/lsp/fixtures/destructuring.ts")
assert.are.same(table.concat(content, "\n") .. "\n", expected_code)
end)
end)
end)