Skip to content

Commit 11943a8

Browse files
committed
merged PR #2346 into this one to clean up git-filter-repo mess
1 parent b714065 commit 11943a8

File tree

17 files changed

+2532
-343
lines changed

17 files changed

+2532
-343
lines changed

src/org/rascalmpl/library/IO.rsc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ Append a textual representation of some values to an existing or a newly created
140140
* All other values are printed as-is.
141141
* Each value is terminated by a newline character.
142142
143-
The existing file can be stored using any character set possible, if you know the character set, please use ((appendToFileEnc)).
144-
Else the same method of deciding the character set is used as in ((readFile)).
143+
The existing file can be stored using any character set possible.
144+
If you know the character set, please use the `charset` keyword parameter.
145+
Otherwise, the same method of deciding the character set is used as in ((readFile)).
145146
}
146147
@pitfalls{
147148
* The same encoding pitfalls as the ((readFile)) function.
@@ -239,6 +240,8 @@ public java bool isDirectory(loc file);
239240
See ((IO-iprintExp)) for a version that returns its argument as result
240241
and ((IO-iprintln)) for a version that adds a newline
241242
and ((IO-iprintToFile)) for a version that prints to a file.
243+
244+
With a negative `lineLimit` the limit is ignored and the entire value will be printed.
242245
}
243246
@examples{
244247
```rascal-shell
@@ -254,6 +257,7 @@ public java void iprint(value arg, int lineLimit = 1000);
254257
See ((IO-iprint)) for a version that displays the result on the console
255258
and ((IO-iprintExp)) for a version that returns its argument as result
256259
and ((IO-iprintln)) for a version that adds a newline.
260+
257261
}
258262
@examples{
259263
```rascal-shell
@@ -308,6 +312,8 @@ and ((IO-iprint)) for a version that does not add a newline.
308312
309313
By default we only print the first 1000 lines, if you want to print larger values, either
310314
use ((ValueIO-writeTextValueFile)) or change the limit with the lineLimit parameter.
315+
316+
With a negative `lineLimit` the limit is ignored and the entire value will be printed.
311317
}
312318
@examples{
313319
```rascal-shell

src/org/rascalmpl/library/List.rsc

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ module List
2020
import Exception;
2121
import Map;
2222

23-
2423
@synopsis{Concatenate a list of lists.}
2524
@examples{
2625
```rascal-shell
@@ -261,7 +260,6 @@ intercalate(", ", ["zebra", "elephant", "snake", "owl"]);
261260
str intercalate(str sep, list[value] l) =
262261
"<for(int i <- index(l)){><i == 0 ? "" : sep><l[i]><}>";
263262

264-
265263
@synopsis{Intersperses a list of values with a separator.}
266264
@examples{
267265
```rascal-shell
@@ -272,9 +270,8 @@ intersperse(1, []);
272270
intersperse([], [1]);
273271
```
274272
}
275-
list[&T] intersperse(&T sep, list[&T] xs) =
276-
(isEmpty(xs))? [] : ([head(xs)] | it + [sep,x] | x <- tail(xs));
277-
273+
list[&T] intersperse(&T sep, list[&T] xs) =
274+
[x, sep | &T x <- xs][..-1];
278275

279276
@synopsis{Test whether a list is empty.}
280277
@description{
@@ -655,6 +652,29 @@ tuple[list[&T],list[&T]] split(list[&T] l) {
655652
return <take(half,l), drop(half,l)>;
656653
}
657654
655+
@synopsis{Groups sublists for consecutive elements which are `similar`}
656+
@description{
657+
This function does not change the order of the elements. Only elements
658+
which are similar end-up in a sub-list with more than one element. The
659+
elements which are not similar to their siblings, end up in singleton
660+
lists.
661+
}
662+
@examples{
663+
```rascal-shell
664+
import List;
665+
bool bothEvenOrBothOdd(int a, int b) = (a % 2 == 0 && b % 2 == 0) || (a % 2 == 1 && b % 2 == 1);
666+
group([1,7,3,6,2,9], bothEvenOrBothOdd);
667+
```
668+
}
669+
public list[list[&T]] group(list[&T] input, bool (&T a, &T b) similar) {
670+
lres = while ([hd, *tl] := input) {
671+
sim = [hd, *takeWhile(tl, bool (&T a) { return similar(a, hd); })];
672+
append sim;
673+
input = drop(size(sim), input);
674+
}
675+
676+
return lres;
677+
}
658678
659679
@synopsis{Sum the elements of a list.}
660680
@examples{

src/org/rascalmpl/library/Type.rsc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The ((subtype)) relation of Rascal has all the mathematical properties of a _fin
3030
This is a core design principle of Rascal with the following benefits:
3131
* Type inference has a guaranteed least or greatest solution, always. This means that constraints are always solvable in an unambiguous manner.
3232
* A _principal type_ can always be computed, which is a most precise and unique solution of a type inference problem. Without the lattice, solution candidates could become incomparable and thus ambiguous. Without
33-
this principal type property, type inference is predictable for programmers.
33+
this principal type property, type inference is unpredictable for programmers.
3434
* Solving type inference constraints can be implemented efficiently. The algorithm, based on ((lub)) and ((glb)), makes progress _deterministically_ and does not require backtracking
3535
to find better solutions. Since the lattice is not very deep, fixed-point solutions are always found quickly.
3636

src/org/rascalmpl/library/analysis/diff/edits/ExecuteTextEdits.rsc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ void executeFileSystemChange(changed(loc file, list[TextEdit] edits)) {
4646
}
4747

4848
str executeTextEdits(str content, list[TextEdit] edits) {
49-
assert isSorted(edits, less=bool (TextEdit e1, TextEdit e2) {
50-
return e1.range.offset < e2.range.offset;
51-
});
49+
// assert isSorted(edits, less=bool (TextEdit e1, TextEdit e2) {
50+
// return e1.range.offset < e2.range.offset;
51+
// });
5252

53-
for (replace(loc range, str repl) <- reverse(edits)) {
54-
content = "<content[..range.offset]><repl><content[range.offset+range.length..]>";
55-
}
53+
int cursor = 0;
5654

57-
return content;
55+
// linear-time streamed reconstruction of the entire text
56+
return "<for (replace(loc range, str repl) <- edits) {><content[cursor..range.offset]><repl><
57+
cursor = range.offset + range.length;}><content[cursor..]>";
5858
}

src/org/rascalmpl/library/analysis/diff/edits/HiFiLayoutDiff.rsc

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ module analysis::diff::edits::HiFiLayoutDiff
2727
extend analysis::diff::edits::HiFiTreeDiff;
2828
import ParseTree; // this should not be necessary because imported by HiFiTreeDiff
2929
import String; // this should not be be necessary because imported by HiFiTreeDiff
30+
import lang::rascal::grammar::definition::Characters;
31+
import IO;
3032

3133
@synopsis{Normalization choices for case-insensitive literals.}
3234
data CaseInsensitivity
@@ -56,7 +58,7 @@ list[TextEdit] layoutDiff(Tree original, Tree formatted, bool recoverComments =
5658
list[TextEdit] rec(
5759
t:appl(prod(Symbol tS, _, _), list[Tree] tArgs), // layout is not necessarily parsed with the same rules (i.e. comments are lost!)
5860
u:appl(prod(Symbol uS, _, _), list[Tree] uArgs))
59-
= [replace(t@\loc, recoverComments ? learnComments(t, u) : "<u>") | tArgs != uArgs, "<t>" != "<u>" /* avoid useless edits */]
61+
= [replace(t@\loc, repl) | tArgs != uArgs, str repl := (recoverComments ? learnComments(t, u) : "<u>"), repl != "<t>" /* do not edit anything if nothing has changed */]
6062
when
6163
delabel(tS) is layouts,
6264
delabel(uS) is layouts,
@@ -106,22 +108,31 @@ list[TextEdit] layoutDiff(Tree original, Tree formatted, bool recoverComments =
106108
default list[TextEdit] rec(
107109
Tree t:appl(Production p, list[Tree] argsA),
108110
appl(p /* must be the same by the above assert */, list[Tree] argsB))
109-
= [*rec(a, b) | <a, b> <- zip2(argsA, argsB)];
111+
= [*rec(argsA[i], argsB[i]) | i <- [0..size(argsA)]];
110112

111113
// first add required locations to layout nodes
112-
original = reposition(original, markLit=true, markLayout=true, markSubLayout=true);
114+
// TODO: check if indeed repositioning is never needed
115+
// original = reposition(original, markLit=true, markLayout=true, markSubLayout=true);
113116

114117
return rec(original, formatted);
115118
}
116119

120+
private Symbol newlineClass = \char-class([range(10,10)]);
121+
117122
@synopsis{Make sure the new layout still contains all the source code comments of the original layout}
118123
@description{
119-
This algorithm uses the @category("Comments") tag to detect source code comments inside layout substrings. If the original
124+
This algorithm uses the `@category(/[cC]omments/)` tag to detect source code comments inside layout substrings. If the original
120125
layout contains comments, we re-introduce the comments at the expected level of indentation. New comments present in the
121126
replacement are kept and will overwrite any original comments.
122127
123-
This trick is complicated by the syntax of multiline comments and single line comments that have
124-
to end with a newline.
128+
There are corner cases with respect to the original comments:
129+
* the single line comment that does not end with a newline itself, yet it must always end with a newline after it.
130+
* multiple single line comments after each other
131+
132+
Then there are corner cases with respect to the replacement whitespace:
133+
* the last line of the replacement whitespace is special. This is the indentation to use for all comments.
134+
* but there could be no newlines in the replacement whitespace; and still there is a single line comment to be included.
135+
Now we need to infer an indentation level for what follows the comment from "thin air".
125136
}
126137
@benefits{
127138
* if comments are kepts and formatted by tools like Tree2Box, then this algorithm does not overwrite these.
@@ -132,7 +143,14 @@ to end with a newline.
132143
* if comments are not marked with `@category("Comment")` in the original grammar, then this algorithm recovers nothing.
133144
}
134145
private str learnComments(Tree original, Tree replacement) {
135-
originalComments = ["<c>" | /c:appl(prod(_,_,{\tag("category"(/^[Cc]omment$/)), *_}), _) := original];
146+
bool mustEndWithNewline(lit("\n")) = true;
147+
bool mustEndWithNewline(conditional(Symbol s, _)) = mustEndWithNewline(s);
148+
// if a comment can not contain newline characters, but everything else, then it must be followed by one:
149+
bool mustEndWithNewline(\iter(Symbol cc:\char-class(_))) = intersection(cc, newlineClass) != newlineClass;
150+
bool mustEndWithNewline(\iter-star(Symbol cc:\char-class(_))) = intersection(cc, newlineClass) != newlineClass;
151+
default bool mustEndWithNewline(_) = false;
152+
153+
originalComments = [<s, s[-1] == "\n" || mustEndWithNewline(lastSym)> | /c:appl(prod(_,[*_,Symbol lastSym],{\tag("category"(/^[Cc]omment$/)), *_}), _) := original, str s := "<c>"];
136154
137155
if (originalComments == []) {
138156
// if the original did not contain comments, stick with the replacements
@@ -146,23 +164,42 @@ private str learnComments(Tree original, Tree replacement) {
146164
return "<replacement>";
147165
}
148166

149-
// At this point, we know that: (a) comments are not present in the replacement and (b) they used to be there in the original.
150-
// So the old comments are going to be the new output. however, we want to learn indentation from the replacement.
167+
// At this point, we know that:
168+
// (a) comments are not present in the replacement and
169+
// (b) they used to be there in the original.
170+
// So the old comments are going to be copied to the new output.
171+
// But, we want to indent them using the style of the replacement.
172+
173+
// The last line of the replacement string typically has the indentation for the construct that follows:
174+
// | // a comment
175+
// | if (true) {
176+
// ^^^^
177+
// newIndent
178+
//
179+
// However, if the replacement string is on a single line, then we don't have the indentation
180+
// for the string on the next line readily available. In this case we indent the next line
181+
// to the start column of the replacement layout, as a proxy.
182+
183+
str replString = "<replacement>";
184+
str newIndent = split("\n", replString)[-1] ? "";
151185

152-
// Drop the last newline of single-line comments, because we don't want two newlines in the output for every comment:
153-
str dropEndNl(str line:/^.*\n$/) = (line[..-1]);
154-
default str dropEndNl(str line) = line;
186+
if (/\n/ !:= replString) {
187+
// no newline in the repl string, so no indentation available for what follows the comment...
188+
newIndent = "<for (_ <- [0..replacement@\loc.begin.column]) {> <}>";
189+
}
155190

156-
// the first line of the replacement ,is the indentation to use.
157-
str replString = "<replacement>";
158-
str replacementIndent = /^\n+$/ !:= replString
159-
? split("\n", replString)[0]
160-
: "";
161-
162-
// trimming each line makes sure we forget about the original indentation, and drop accidental spaces after comment lines
163-
return replString + indent(replacementIndent,
164-
"<for (c <- originalComments, str line <- split("\n", dropEndNl(c))) {><indent(replacementIndent, trim(line), indentFirstLine=true)>
165-
'<}>"[..-1], indentFirstLine=false) + replString;
191+
// we always place sequential comments vertically, because we don't know if we are dealing
192+
// we a single line comment that has to end with newline by follow restriction or by a literal "\n".
193+
// TODO: a deeper analysis of the comment rule that's in use could also be used to discover this.
194+
str trimmedOriginals = "<for (<c, newLine> <- originalComments) {><trim(c)><if (newLine) {>
195+
'<}><}>";
196+
197+
// we wrap the comment with the formatted whitespace to assure the proper indentation
198+
// of its first line, and the proper indentation of what comes after this layout node
199+
return replString
200+
+ indent(newIndent, trimmedOriginals, indentFirstLine=false)
201+
+ newIndent
202+
;
166203
}
167204

168205
private Symbol delabel(label(_, Symbol t)) = t;

0 commit comments

Comments
 (0)