Skip to content

Commit a00b582

Browse files
committed
Fix issues with renaming parameters and methods
1 parent 0b938ae commit a00b582

2 files changed

Lines changed: 254 additions & 3 deletions

File tree

src/main/java/nextflow/lsp/services/script/ScriptReferenceProvider.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.codehaus.groovy.ast.Variable;
3434
import org.codehaus.groovy.ast.expr.ClassExpression;
3535
import org.codehaus.groovy.ast.expr.ConstantExpression;
36+
import org.codehaus.groovy.ast.expr.MethodCallExpression;
3637
import org.eclipse.lsp4j.Location;
3738
import org.eclipse.lsp4j.Position;
3839
import org.eclipse.lsp4j.TextDocumentIdentifier;
@@ -224,11 +225,30 @@ else if( oldName.equals(alias) )
224225
}
225226

226227
if( node instanceof Variable v ) {
227-
if( oldName.equals(v.getName()) )
228-
return new TextEdit(range, newName);
228+
if( !oldName.equals(v.getName()) )
229+
return null;
230+
231+
var start = range.getStart();
232+
var end = range.getEnd();
233+
end.setLine(start.getLine());
234+
end.setCharacter(start.getCharacter() + oldName.length());
235+
236+
return new TextEdit(range, newName);
237+
}
238+
239+
if( node instanceof MethodCallExpression mce ) {
240+
if( !mce.isImplicitThis() || !oldName.equals(mce.getMethodAsString()) )
241+
return null;
242+
243+
var start = range.getStart();
244+
var end = range.getEnd();
245+
end.setLine(start.getLine());
246+
end.setCharacter(start.getCharacter() + oldName.length());
247+
248+
return new TextEdit(range, newName);
229249
}
230250

231-
if( node instanceof ClassExpression || node instanceof ConstantExpression ) {
251+
if( node instanceof ClassExpression ) {
232252
if( oldName.equals(node.getText()) )
233253
return new TextEdit(range, newName);
234254
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
/*
2+
* Copyright 2024-2025, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.lsp.services.script
18+
19+
import nextflow.lsp.util.Positions
20+
import org.eclipse.lsp4j.Position
21+
import org.eclipse.lsp4j.RenameParams
22+
import org.eclipse.lsp4j.TextDocumentIdentifier
23+
import org.eclipse.lsp4j.TextEdit
24+
import spock.lang.Specification
25+
26+
import static nextflow.lsp.TestUtils.*
27+
28+
/**
29+
*
30+
* @author Ben Sherman <bentshermann@gmail.com>
31+
*/
32+
class ScriptRenameTest extends Specification {
33+
34+
Map<String,List<TextEdit>> rename(ScriptService service, String uri, Position position, String newName) {
35+
def workspaceEdit = service.rename(new RenameParams(new TextDocumentIdentifier(uri), position, newName))
36+
def result = workspaceEdit.getChanges()
37+
for( def key : result.keySet() ) {
38+
result[key].sort((a, b) -> (
39+
Positions.COMPARATOR.compare(a.getRange().getStart(), b.getRange().getStart())
40+
))
41+
}
42+
return result
43+
}
44+
45+
def 'should rename a workflow' () {
46+
given:
47+
def service = getScriptService()
48+
def uri = getUri('main.nf')
49+
50+
when:
51+
def contents = '''\
52+
workflow HELLO {
53+
}
54+
55+
workflow {
56+
HELLO()
57+
}
58+
'''
59+
open(service, uri, contents)
60+
service.updateNow()
61+
def changes = rename(service, uri, new Position(0, 9), "hi")
62+
then:
63+
changes[uri].size() == 2
64+
changes[uri][0].getRange().getStart() == new Position(0, 9)
65+
changes[uri][0].getRange().getEnd() == new Position(0, 14)
66+
changes[uri][0].getNewText() == "hi"
67+
changes[uri][1].getRange().getStart() == new Position(4, 4)
68+
changes[uri][1].getRange().getEnd() == new Position(4, 9)
69+
changes[uri][1].getNewText() == "hi"
70+
}
71+
72+
def 'should rename a workflow input' () {
73+
given:
74+
def service = getScriptService()
75+
def uri = getUri('main.nf')
76+
77+
when:
78+
def contents = '''\
79+
workflow HELLO {
80+
take:
81+
greeting
82+
83+
main:
84+
greeting.view()
85+
}
86+
'''
87+
open(service, uri, contents)
88+
service.updateNow()
89+
def changes = rename(service, uri, new Position(2, 4), "message")
90+
then:
91+
changes[uri].size() == 2
92+
changes[uri][0].getRange().getStart() == new Position(2, 4)
93+
changes[uri][0].getRange().getEnd() == new Position(2, 12)
94+
changes[uri][0].getNewText() == "message"
95+
changes[uri][1].getRange().getStart() == new Position(5, 4)
96+
changes[uri][1].getRange().getEnd() == new Position(5, 12)
97+
changes[uri][1].getNewText() == "message"
98+
}
99+
100+
def 'should rename a process' () {
101+
given:
102+
def service = getScriptService()
103+
def uri = getUri('main.nf')
104+
105+
when:
106+
def contents = '''\
107+
process HELLO {
108+
script:
109+
'true'
110+
}
111+
112+
workflow {
113+
HELLO()
114+
}
115+
'''
116+
open(service, uri, contents)
117+
service.updateNow()
118+
def changes = rename(service, uri, new Position(0, 8), "hi")
119+
then:
120+
changes[uri].size() == 2
121+
changes[uri][0].getRange().getStart() == new Position(0, 8)
122+
changes[uri][0].getRange().getEnd() == new Position(0, 13)
123+
changes[uri][0].getNewText() == "hi"
124+
changes[uri][1].getRange().getStart() == new Position(6, 4)
125+
changes[uri][1].getRange().getEnd() == new Position(6, 9)
126+
changes[uri][1].getNewText() == "hi"
127+
}
128+
129+
def 'should rename a process input' () {
130+
given:
131+
def service = getScriptService()
132+
def uri = getUri('main.nf')
133+
134+
when:
135+
def contents = '''\
136+
process HELLO {
137+
input:
138+
val greeting
139+
140+
script:
141+
"echo $greeting"
142+
}
143+
'''
144+
open(service, uri, contents)
145+
service.updateNow()
146+
def changes = rename(service, uri, new Position(2, 8), "message")
147+
then:
148+
changes[uri].size() == 2
149+
changes[uri][0].getRange().getStart() == new Position(2, 8)
150+
changes[uri][0].getRange().getEnd() == new Position(2, 16)
151+
changes[uri][0].getNewText() == "message"
152+
changes[uri][1].getRange().getStart() == new Position(5, 10)
153+
changes[uri][1].getRange().getEnd() == new Position(5, 18)
154+
changes[uri][1].getNewText() == "message"
155+
}
156+
157+
def 'should rename a local variable' () {
158+
given:
159+
def service = getScriptService()
160+
def uri = getUri('main.nf')
161+
162+
when:
163+
def contents = '''\
164+
def msg = 'hello'
165+
println msg
166+
'''
167+
open(service, uri, contents)
168+
service.updateNow()
169+
def changes = rename(service, uri, new Position(0, 4), "message")
170+
then:
171+
changes[uri].size() == 2
172+
changes[uri][0].getRange().getStart() == new Position(0, 4)
173+
changes[uri][0].getRange().getEnd() == new Position(0, 7)
174+
changes[uri][0].getNewText() == "message"
175+
changes[uri][1].getRange().getStart() == new Position(1, 8)
176+
changes[uri][1].getRange().getEnd() == new Position(1, 11)
177+
changes[uri][1].getNewText() == "message"
178+
}
179+
180+
def 'should rename a function' () {
181+
given:
182+
def service = getScriptService()
183+
def uri = getUri('main.nf')
184+
185+
when:
186+
def contents = '''\
187+
def hello() {
188+
}
189+
190+
workflow {
191+
hello()
192+
}
193+
'''
194+
open(service, uri, contents)
195+
service.updateNow()
196+
def changes = rename(service, uri, new Position(0, 4), "hi")
197+
then:
198+
changes[uri].size() == 2
199+
changes[uri][0].getRange().getStart() == new Position(0, 4)
200+
changes[uri][0].getRange().getEnd() == new Position(0, 9)
201+
changes[uri][0].getNewText() == "hi"
202+
changes[uri][1].getRange().getStart() == new Position(4, 4)
203+
changes[uri][1].getRange().getEnd() == new Position(4, 9)
204+
changes[uri][1].getNewText() == "hi"
205+
}
206+
207+
def 'should rename a function parameter' () {
208+
given:
209+
def service = getScriptService()
210+
def uri = getUri('main.nf')
211+
212+
when:
213+
def contents = '''\
214+
def hello(greeting: String) {
215+
println greeting
216+
}
217+
'''
218+
open(service, uri, contents)
219+
service.updateNow()
220+
def changes = rename(service, uri, new Position(0, 10), "message")
221+
then:
222+
changes[uri].size() == 2
223+
changes[uri][0].getRange().getStart() == new Position(0, 10)
224+
changes[uri][0].getRange().getEnd() == new Position(0, 18)
225+
changes[uri][0].getNewText() == "message"
226+
changes[uri][1].getRange().getStart() == new Position(1, 12)
227+
changes[uri][1].getRange().getEnd() == new Position(1, 20)
228+
changes[uri][1].getNewText() == "message"
229+
}
230+
231+
}

0 commit comments

Comments
 (0)