Skip to content

Commit 78c30e5

Browse files
authored
Rename independent of util::LanguageServer (#1012)
* Rename independent of `util::LanguageServer` * Expose text edit definitions to LS writers. * Fix documentation links. * Fixes (h/t @DavyLandman) * Rename module with text edits. * Extend module with original definitions. * Fix tutor links after renaming.
1 parent 5a4856d commit 78c30e5

7 files changed

Lines changed: 106 additions & 37 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@license{
2+
Copyright (c) 2018-2025, NWO-I CWI and Swat.engineering
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice,
9+
this list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25+
POSSIBILITY OF SUCH DAMAGE.
26+
}
27+
module analysis::diff::edits::AnnotatedTextEdits
28+
29+
extend analysis::diff::edits::TextEdits;
30+
31+
@synopsis{A ((analysis::diff::edits::TextEdits::TextEdit)) with additional context for LSP.}
32+
@description{
33+
In LSP, text edits can contain extra information w.r.t. ((analysis::diff::edits::TextEdits::TextEdit)).
34+
* label: Human-readable string that describes the change.
35+
* description: Human-readable string that additionally describes the change, rendered less prominently.
36+
* needsConfirmation: Flags whether the user should confirm this change. By default, this is false, which means that ((analysis::diff::edits::AnnotatedTextEdits::TextEdit))s are applied without user confirmation.
37+
38+
Typically, clients provide options to group edits by label/description when showing them to the user.
39+
See the [LSP documentation](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#changeAnnotation) for more details.
40+
41+
Note: to easily annotate all text edits in a ((analysis::diff::edits::TextEdits::FileSystemChange)), use the convenience keywords on ((analysis::diff::edits::AnnotatedTextEdits::FileSystemChange)).
42+
}
43+
@pitfalls{
44+
When `needsConfirmation = false` for all edits, the client will typically apply them without showing any information from the annotations to the user.
45+
}
46+
data TextEdit(str label = "", str description = label, bool needsConfirmation = false);
47+
48+
@synopsis{A ((analysis::diff::edits::TextEdits::FileSystemChange)) with additional context for LSP.}
49+
@description{
50+
Provides extra context for all contained ((analysis::diff::edits::AnnotatedTextEdits::TextEdit))s at once.
51+
}
52+
data FileSystemChange(str label = "", str description = "", bool needsConfirmation = false);
53+
54+
@synopsis{Shorthand for file changes, with additional context for LSP.}
55+
@description{
56+
Provides extra context for all contained ((analysis::diff::edits::AnnotatedTextEdits::TextEdit))s at once.
57+
}
58+
FileSystemChange changed(list[TextEdit] edits:[replace(loc l, str _), *_], str label = "", str description = "", bool needsConfirmation = false)
59+
= changed(l.top, edits, label=label, description=description, needsConfirmation=needsConfirmation);

rascal-lsp/src/main/rascal/library/util/LanguageServer.rsc

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Language Server Protocol.
4040
module util::LanguageServer
4141

4242
import util::Reflective;
43-
import analysis::diff::edits::TextEdits;
43+
extend analysis::diff::edits::AnnotatedTextEdits;
4444
import IO;
4545
import ParseTree;
4646
import Message;
@@ -727,36 +727,6 @@ the right ((DocumentEdit))s immediately.
727727
}
728728
data Message(list[CodeAction] fixes = []);
729729

730-
@synopsis{A ((analysis::diff::edits::TextEdits::TextEdit)) with additional context for LSP.}
731-
@description{
732-
In LSP, text edits can contain extra information w.r.t. ((analysis::diff::edits::TextEdits::TextEdit)).
733-
* label: Human-readable string that describes the change.
734-
* description: Human-readable string that additionally describes the change, rendered less prominently.
735-
* needsConfirmation: Flags whether the user should confirm this change. By default, this is false, which means that ((util::LanguageServer::TextEdit))s are applied without user confirmation.
736-
737-
Typically, clients provide options to group edits by label/description when showing them to the user.
738-
See the [LSP documentation](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#changeAnnotation) for more details.
739-
740-
Note: to easily annotate all text edits in a ((analysis::diff::edits::TextEdits::FileSystemChange)), use the convenience keywords on ((util::LanguageServer::FileSystemChange)).
741-
}
742-
@pitfalls{
743-
When `needsConfirmation = false` for all edits, the client will typically apply them without showing any information from the annotations to the user.
744-
}
745-
data TextEdit(str label = "", str description = label, bool needsConfirmation = false);
746-
747-
@synopsis{A ((analysis::diff::edits::TextEdits::FileSystemChange)) with additional context for LSP.}
748-
@description{
749-
Provides extra context for all contained ((util::LanguageServer::TextEdit))s at once.
750-
}
751-
data FileSystemChange(str label = "", str description = "", bool needsConfirmation = false);
752-
753-
@synopsis{Shorthand for file changes, with additional context for LSP.}
754-
@description{
755-
Provides extra context for all contained ((util::LanguageServer::TextEdit))s at once.
756-
}
757-
FileSystemChange changed(list[TextEdit] edits:[replace(loc l, str _), *_], str label = "", str description = "", bool needsConfirmation = false)
758-
= changed(l.top, edits, label=label, description=description, needsConfirmation=needsConfirmation);
759-
760730
@synopsis{A Command is a parameter to a CommandExecutor function.}
761731
@description{
762732
Commands can be any closed term a() pure value without open variables or function/closure values embedded in it). Add any constructor you need to express the execution parameters
@@ -822,7 +792,7 @@ interactive content have to be cleaned or closed in their own respective fashion
822792
}
823793
@benefits{
824794
* CodeActions provide tight integration with the user experience in the IDE. Including sometimes previews, and always the undo stack.
825-
* CodeActions can be implemented "on the language level", abstracting from UI and scheduling details. See also ((analysis::diff::edits)) for
795+
* CodeActions can be implemented "on the language level", abstracting from UI and scheduling details. See also ((analysis::diff::edits::HiFiTreeDiff)) and ((analysis::diff::edits::HiFiLayoutDiff)) for
826796
tools that can produce lists of ((DocumentEdit))s by diffing parse trees or abstract syntax trees.
827797
* `edits` are applied on the latest editor content for the current editor; live to the user.
828798
* ((util::IDEServices::applyDocumentsEdits)) also works on open editor contents for the current editor.

rascal-lsp/src/main/rascal/lsp/lang/rascal/lsp/Templates.rsc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ module lang::rascal::lsp::Templates
3030
import IO;
3131
import Location;
3232
import String;
33-
import util::LanguageServer;
3433
import util::PathConfig;
3534
import util::Reflective;
35+
import analysis::diff::edits::AnnotatedTextEdits;
3636

3737
import analysis::diff::edits::TextEdits;
3838

rascal-lsp/src/main/rascal/lsp/lang/rascal/lsp/refactor/Rename.rsc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ extend analysis::typepal::refactor::Rename;
6666
import util::Util;
6767

6868
import util::FileSystem;
69-
import util::LanguageServer;
7069
import util::Maybe;
7170
import util::Reflective;
7271

rascal-lsp/src/main/rascal/lsp/lang/rascal/lsp/refactor/rename/Common.rsc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ import Relation;
4545
import Set;
4646
import String;
4747
import util::FileSystem;
48-
import util::LanguageServer;
4948
import util::Maybe;
5049
import util::Monitor;
5150
import util::Reflective;
51+
import analysis::diff::edits::AnnotatedTextEdits;
5252
import util::Util;
5353

5454
data RenameConfig(

rascal-lsp/src/main/rascal/lsp/lang/rascal/lsp/refactor/rename/Fields.rsc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import analysis::diff::edits::TextEdits;
4646

4747
import Map;
4848
import util::Maybe;
49-
import util::LanguageServer;
5049

5150
set[IdRole] fieldRoles = {fieldId(), keywordFieldId(), keywordFormalId()};
5251
bool isFieldRole(IdRole role) = role in fieldRoles;

rascal-lsp/src/main/rascal/lsp/lang/rascal/tests/rename/TestUtils.rsc

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import lang::rascalcore::compile::util::Names;
4848
import analysis::diff::edits::ExecuteTextEdits;
4949

5050
import util::FileSystem;
51-
import util::LanguageServer;
5251
import util::Math;
5352
import util::Maybe;
5453
import util::Monitor;
@@ -120,6 +119,49 @@ bool expectEq(&T expected, &T actual, str epilogue = "") {
120119
return true;
121120
}
122121

122+
// Rascal port of TreeSearch::computeFocusList
123+
private list[Tree] computeFocusList(amb(set[Tree] alts), int line, int col) {
124+
if (a <- alts) {
125+
return computeFocusList(a, line, col);
126+
}
127+
return [];
128+
}
129+
130+
private list[Tree] computeFocusList(tr:appl(Production p, _), int line, int col) = [tr] when isLexical(p.def) && isInside(tr, line, col);
131+
132+
private bool isLexical(\lex(_)) = true;
133+
private bool isLexical(\parameterized-lex(_, _)) = true;
134+
private default bool isLexical(_) = false;
135+
136+
private list[Tree] computeFocusList(appl(prod, _), int _, int _) = [] when prod.def is \layouts;
137+
138+
private list[Tree] computeFocusList(tr:appl(_, args), int line, int col) {
139+
list[Tree] focus = isInside(tr, line, col) ? [tr] : [];
140+
for (a <- args, isInside(a, line, col)) {
141+
return computeFocusList(a, line, col) + focus;
142+
}
143+
return focus;
144+
}
145+
146+
private default list[Tree] computeFocusList(Tree _, int _, int _) = [];
147+
148+
private bool isInside(Tree tr, int line, int col) = tr.src? && isInside(tr.src, line, col);
149+
private bool isInside(loc l, int line, int col) {
150+
if (!(l.begin? && l.end?)) return false;
151+
if (line < l.begin.line || line > l.end.line) return false;
152+
153+
if (line == l.begin.line) {
154+
if (line < l.end.line) {
155+
return l.begin.column <= col;
156+
}
157+
return l.begin.column <= col && col <= l.end.column;
158+
}
159+
if (line == l.end.line) {
160+
return col <= l.end.column;
161+
}
162+
return true;
163+
}
164+
123165
bool testProject(set[TestModule] modules, str testName, bool(set[TestModule] mods, loc testDir, PathConfig pcfg) doCheck) {
124166
loc testDir = |unknown:///|;
125167
bool moduleExistsOnDisk = any(mmm <- modules, mmm is byLoc);

0 commit comments

Comments
 (0)