Skip to content
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
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased
<!-- Add all new changes here. They will be moved under a version at release -->
* `FIX` Rename type names referenced by `---@see`, including renaming from the annotation [#3470](https://github.com/LuaLS/lua-language-server/issues/3470)

## 3.19.2
`2026-10-13`
Expand Down
49 changes: 47 additions & 2 deletions script/core/rename.lua
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,22 @@ local function ofLabel(source, newname, callback)
end
end

local function getSeeType(source)
local globalVar = vm.getGlobal('type', source[1])
if not globalVar then
return
end
for _, target in ipairs(globalVar:getSets(guide.getUri(source))) do
if target.type == 'doc.class' then
return target.class
elseif target.type == 'doc.alias' then
return target.alias
elseif target.type == 'doc.enum' then
return target.enum
end
end
end

---@async
local function ofDocTypeName(source, newname, callback)
local oldname = source[1]
Expand All @@ -243,7 +259,9 @@ local function ofDocTypeName(source, newname, callback)
return
end
local uri = guide.getUri(source)
local declarations = {}
for _, doc in ipairs(globalVar:getSets(uri)) do
declarations[doc] = true
if doc.type == 'doc.class' then
callback(doc, doc.class.start, doc.class.finish, newname)
end
Expand All @@ -261,6 +279,23 @@ local function ofDocTypeName(source, newname, callback)
callback(doc, doc.start, doc.finish, newname)
end
end
for refUri in files.eachFile(uri) do
local text = files.getText(refUri)
if text and text:find(oldname, 1, true) then
local state = files.getState(refUri)
if state then
guide.eachSourceType(state.ast, 'doc.see.name', function (doc)
if doc[1] ~= oldname then
return
end
local target = getSeeType(doc)
if target and declarations[target.parent] then
callback(doc, doc.start, doc.finish, newname)
end
end)
end
end
end
end

local function ofDocParamName(source, newname, callback)
Expand All @@ -283,7 +318,12 @@ end

---@async
local function rename(source, newname, callback)
if source.type == 'label'
if source.type == 'doc.see.name' then
local target = getSeeType(source)
if target then
return ofDocTypeName(target, newname, callback)
end
elseif source.type == 'label'
or source.type == 'goto' then
return ofLabel(source, newname, callback)
elseif source.type == 'local' then
Expand Down Expand Up @@ -325,7 +365,11 @@ local function rename(source, newname, callback)
end

local function prepareRename(source)
if source.type == 'label'
if source.type == 'doc.see.name' then
if getSeeType(source) then
return source, source[1]
end
elseif source.type == 'label'
or source.type == 'goto'
or source.type == 'local'
or source.type == 'setlocal'
Expand Down Expand Up @@ -384,6 +428,7 @@ local accept = {
['doc.enum.name'] = true,
['doc.field.name'] = true,
['doc.extends.name'] = true,
['doc.see.name'] = true,
}

local m = {}
Expand Down
118 changes: 118 additions & 0 deletions test/rename/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,121 @@ TEST ('A', 'C') [[
]] [[
---@class B: C
]]

for _, declaration in ipairs {
'---@class OldName',
'---@alias OldName string',
'---@enum OldName',
} do
local renamed = declaration:gsub('OldName', 'NewName')
TEST ('OldName', 'NewName') (declaration .. [[

---@see OldName
---@type OldName
local value
OldName = 1
print(OldName)
]]) (renamed .. [[

---@see NewName
---@type NewName
local value
OldName = 1
print(OldName)
]])

TEST ('OldName', 'NewName') ([[
---@see OldName
]] .. declaration .. [[

---@type OldName
local value
OldName = 1
print(OldName)
]]) ([[
---@see NewName
]] .. renamed .. [[

---@type NewName
local value
OldName = 1
print(OldName)
]])
end

TEST ('Missing', 'NewName') [[
---@see Missing
]] [[
---@see Missing
]]

TEST ('OldName', 'NewName') [[
---@see OldName
OldName = 1
]] [[
---@see OldName
OldName = 1
]]

TEST ('RepeatedType', 'RenamedType') (
'---@class RepeatedType\n'
.. ('---@see RepeatedType\n'):rep(64)
.. 'RepeatedType = 1\nprint(RepeatedType)\n'
) (
'---@class RenamedType\n'
.. ('---@see RenamedType\n'):rep(64)
.. 'RepeatedType = 1\nprint(RepeatedType)\n'
)

do
local scope = require 'workspace.scope'
local folder = scope.createFolder(TESTURI .. '.rename-a')
local otherFolder = scope.createFolder(TESTURI .. '.rename-b')
local classUri = folder.uri .. '/class.lua'
local uri = folder.uri .. '/see.lua'
local otherUri = otherFolder.uri .. '/other.lua'
local classScript = '---@class OldName\n'
local seeScript = '---@see OldName\nOldName = 1\nprint(OldName)\n'
local _ <close> = function ()
files.remove(classUri)
files.remove(uri)
files.remove(otherUri)
folder:remove()
otherFolder:remove()
end
files.setText(classUri, classScript)
files.setText(uri, seeScript)
files.setText(otherUri, classScript .. seeScript)
for _, sourceUri in ipairs { classUri, uri } do
local state = files.getState(sourceUri)
local script = files.getText(sourceUri)
local offset = assert(script:find('OldName', 1, true))
local position = guide.offsetToPosition(state, offset)
local prepared = assert(core.prepareRename(sourceUri, position))
assert(prepared.text == 'OldName')
local positions = assert(core.rename(sourceUri, position, 'NewName'))
assert(#positions == 2)
local found = {}
for _, edit in ipairs(positions) do
local editState = files.getState(edit.uri)
local editScript = files.getText(edit.uri)
local editOffset = assert(editScript:find('OldName', 1, true)) - 1
assert(edit.start == guide.offsetToPosition(editState, editOffset))
assert(edit.finish == guide.offsetToPosition(editState, editOffset + #'OldName'))
assert(edit.text == 'NewName')
found[edit.uri] = true
end
assert(found[classUri] and found[uri])
end
end

for _, script in ipairs {
'---@see Missing\n',
'---@see OldName\nOldName = 1\n',
} do
files.setText(TESTURI, script)
local state = files.getState(TESTURI)
local position = guide.offsetToPosition(state, #'---@see ')
assert(core.prepareRename(TESTURI, position) == nil)
files.remove(TESTURI)
end
Loading