Skip to content

Commit ce46294

Browse files
committed
Test & fix list focus.
1 parent b23a5ea commit ce46294

File tree

3 files changed

+76
-12
lines changed

3 files changed

+76
-12
lines changed

rascal-lsp/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@
153153
<version>3.5.3</version>
154154
</dependency>
155155
</dependencies>
156+
<configuration>
157+
<systemPropertyVariables>
158+
<forkMode>always</forkMode>
159+
<log4j2.configurationFactory>org.rascalmpl.vscode.lsp.log.LogRedirectConfiguration</log4j2.configurationFactory>
160+
</systemPropertyVariables>
161+
</configuration>
156162
</plugin>
157163
<plugin>
158164
<groupId>org.rascalmpl</groupId>

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/util/locations/impl/TreeSearch.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ private static boolean rightOfBegin(ISourceLocation loc, int line, int column) {
101101
return line == loc.getBeginLine() && column > loc.getBeginColumn();
102102
}
103103

104+
private static boolean rightOfEnd(ISourceLocation loc, int line, int column) {
105+
if (!loc.hasLineColumn()) {
106+
return false;
107+
}
108+
109+
if (line > loc.getEndLine()) {
110+
return true;
111+
}
112+
return line == loc.getEndLine() && column > loc.getEndColumn();
113+
}
114+
104115
/**
105116
* Produces a list of trees that are "in focus" at given line and column offset (UTF-32).
106117
*
@@ -180,14 +191,16 @@ public static IList computeFocusList(ITree tree, int startLine, int startColumn,
180191
final var startList = computeFocusList(tree, startLine, startColumn);
181192
final var endList = computeFocusList(tree, endLine, endColumn);
182193

194+
logger.trace("Focus at range start: {}", startList.length());
195+
logger.trace("Focus at range end: {}", endList.length());
196+
183197
final var commonSuffix = startList.intersect(endList);
184-
if (commonSuffix.equals(startList) || commonSuffix.equals(endList)) {
185-
// We do not have enough information to extend the focus
186-
return commonSuffix;
187-
}
198+
199+
logger.trace("Common focus suffix length: {}", commonSuffix.length());
188200
// The range spans multiple subtrees. The easy way out is not to focus farther down than
189201
// their smallest common subtree (i.e. `commonSuffix`) - let's see if we can do any better.
190202
if (TreeAdapter.isList((ITree) commonSuffix.get(0))) {
203+
logger.trace("Focus range spans a (partial) list: {}", TreeAdapter.getType((ITree) commonSuffix.get(0)));
191204
return computeListRangeFocus(commonSuffix, startLine, startColumn, endLine, endColumn);
192205
}
193206

@@ -210,9 +223,22 @@ private static IList computeListRangeFocus(final IList commonSuffix, int startLi
210223
// Find the elements in the list that are (partially) selected.
211224
final var selected = elements.stream()
212225
.map(ITree.class::cast)
213-
.dropWhile(t -> !inside(TreeAdapter.getLocation(t), startLine, startColumn))
214-
.takeWhile(t -> rightOfBegin(TreeAdapter.getLocation(t), endLine, endColumn))
226+
.dropWhile(t -> {
227+
final var l = TreeAdapter.getLocation(t);
228+
// only include layout if the element before it is selected as well
229+
return TreeAdapter.isLayout(t)
230+
? rightOfBegin(l, startLine, startColumn)
231+
: rightOfEnd(l, startLine, startColumn);
232+
})
233+
.takeWhile(t -> {
234+
final var l = TreeAdapter.getLocation(t);
235+
// only include layout if the element after it is selected as well
236+
return TreeAdapter.isLayout(t)
237+
? rightOfEnd(l, endLine, endColumn)
238+
: rightOfBegin(l, endLine, endColumn);
239+
})
215240
.collect(VF.listWriter());
241+
216242
final int nSelected = selected.length();
217243

218244
logger.trace("Range covers {} (of {}) elements in the parent list", nSelected, nElements);

rascal-lsp/src/test/java/engineering/swat/rascal/lsp/util/TreeSearchTests.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
package engineering.swat.rascal.lsp.util;
2828

2929
import static org.junit.Assert.assertEquals;
30+
import static org.junit.Assert.assertFalse;
3031
import static org.junit.Assert.assertTrue;
3132
import static org.rascalmpl.vscode.lsp.util.locations.impl.TreeSearch.computeFocusList;
3233

@@ -68,26 +69,57 @@ public static void setUpSuite() {
6869
tree = RascalServices.parseRascalModule(VF.sourceLocation(URI), CONTENTS.toCharArray());
6970
}
7071

72+
@Test
73+
public void focusStartsWithLexical() {
74+
final var focus = computeFocusList(tree, 8, 13);
75+
final var first = (ITree) focus.get(0);
76+
assertTrue(TreeAdapter.isLexical(first));
77+
}
78+
7179
@Test
7280
public void focusEndsWithModule() {
7381
final var focus = computeFocusList(tree, 6, 4);
74-
final var last = focus.get(focus.length() - 1);
82+
final var last = (ITree) focus.get(focus.length() - 1);
7583
assertEquals(tree, last);
7684
}
7785

7886
@Test
79-
public void listPartialRange() {
80-
final var focus = computeFocusList(tree, 4, 8, 6, 8);
87+
public void listRangePartial() {
88+
final var focus = computeFocusList(tree, 5, 8, 6, 8);
8189
final var selection = (ITree) focus.get(0);
8290
final var originalList = (ITree) focus.get(1);
8391

84-
assertListLength(selection, 3);
85-
assertListLength(originalList, 4);
92+
assertValidListWithLength(selection, 2);
93+
assertValidListWithLength(originalList, 4);
8694
}
8795

96+
@Test
97+
public void listRangeStartsWithWhitespace() {
98+
final var focus = computeFocusList(tree, 7, 0, 8, 15);
99+
final var selection = (ITree) focus.get(0);
100+
final var originalList = (ITree) focus.get(1);
101+
102+
assertValidListWithLength(selection, 1);
103+
assertValidListWithLength(originalList, 4);
104+
}
88105

89-
private static void assertListLength(final ITree list, int length) {
106+
@Test
107+
public void listRangeEndsWithWhitespace() {
108+
final var focus = computeFocusList(tree, 4, 3, 7, 0);
109+
final var selection = (ITree) focus.get(0);
110+
final var originalList = (ITree) focus.get(1);
111+
112+
assertValidListWithLength(selection, 3);
113+
assertValidListWithLength(originalList, 4);
114+
}
115+
116+
private static void assertValidListWithLength(final ITree list, int length) {
90117
assertTrue(String.format("Not a list: %s", TreeAdapter.getType(list)), TreeAdapter.isList(list));
91118
assertEquals(TreeAdapter.yield(list), length, TreeAdapter.getListASTArgs(list).size());
119+
120+
// assert no layout padding
121+
final var args = TreeAdapter.getArgs(list);
122+
assertFalse("List tree malformed: starts with layout", TreeAdapter.isLayout((ITree) args.get(0)));
123+
assertFalse("List tree malformed: ends with layout", TreeAdapter.isLayout((ITree) args.get(args.length() - 1)));
92124
}
93125
}

0 commit comments

Comments
 (0)