@eagleoutice/flowr 2.7.4 → 2.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/cli/repl/commands/repl-commands.d.ts +2 -0
- package/cli/repl/commands/repl-commands.js +2 -0
- package/cli/repl/commands/repl-dataflow.d.ts +2 -0
- package/cli/repl/commands/repl-dataflow.js +38 -1
- package/cli/repl/core.js +22 -0
- package/config.d.ts +5 -0
- package/config.js +6 -0
- package/dataflow/graph/graph.js +2 -0
- package/documentation/wiki-analyzer.js +12 -0
- package/documentation/wiki-interface.js +3 -0
- package/documentation/wiki-query.js +1 -1
- package/package.json +3 -1
- package/project/context/flowr-analyzer-dependencies-context.d.ts +5 -1
- package/project/context/flowr-analyzer-functions-context.d.ts +16 -0
- package/project/context/flowr-analyzer-functions-context.js +6 -0
- package/project/context/flowr-analyzer-loading-order-context.d.ts +4 -4
- package/project/context/flowr-analyzer-loading-order-context.js +4 -0
- package/project/plugins/file-plugins/files/flowr-description-file.d.ts +18 -1
- package/project/plugins/file-plugins/files/flowr-description-file.js +47 -13
- package/project/plugins/loading-order-plugins/flowr-analyzer-loading-order-description-file-plugin.js +8 -3
- package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +5 -2
- package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin.js +6 -1
- package/project/plugins/package-version-plugins/package.js +1 -1
- package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +12 -2
- package/queries/catalog/project-query/project-query-executor.js +12 -2
- package/queries/catalog/project-query/project-query-format.d.ts +13 -0
- package/queries/catalog/project-query/project-query-format.js +25 -2
- package/queries/query-print.js +8 -3
- package/util/mermaid/cfg.d.ts +3 -0
- package/util/mermaid/cfg.js +25 -0
- package/util/r-author.d.ts +39 -0
- package/util/r-author.js +194 -0
- package/util/r-license.d.ts +23 -0
- package/util/r-license.js +196 -0
- package/util/simple-df/dfg-ascii.d.ts +5 -0
- package/util/simple-df/dfg-ascii.js +272 -0
- package/util/version.js +1 -1
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseRLicense = parseRLicense;
|
|
4
|
+
const semver_1 = require("semver");
|
|
5
|
+
const assert_1 = require("./assert");
|
|
6
|
+
const objects_1 = require("./objects");
|
|
7
|
+
/**
|
|
8
|
+
* Parses an R license string into its structured representation.
|
|
9
|
+
*/
|
|
10
|
+
function parseRLicense(licenseString) {
|
|
11
|
+
const start = skipWhitespace({ position: 0, remInput: licenseString });
|
|
12
|
+
const parsed = parseLicenseInfo(start);
|
|
13
|
+
return parsed.element;
|
|
14
|
+
}
|
|
15
|
+
function parseLicenseInfo(info) {
|
|
16
|
+
info = skipWhitespace(info);
|
|
17
|
+
if (info.remInput.length === 0) {
|
|
18
|
+
return { ...info, element: { type: 'no-license' } };
|
|
19
|
+
}
|
|
20
|
+
else if (info.remInput.startsWith('(')) {
|
|
21
|
+
return parseParenthesizedExpression(info);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return parseBinaryOperator(info);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function parseParenthesizedExpression(info) {
|
|
28
|
+
const openParen = consumeString(info, '(');
|
|
29
|
+
(0, assert_1.guard)(openParen.element, `Expected (, but found ${info.remInput[0]} at position ${info.position}`);
|
|
30
|
+
const innerInfo = parseLicenseInfo(openParen);
|
|
31
|
+
const closeParen = consumeString(innerInfo, ')');
|
|
32
|
+
(0, assert_1.guard)(closeParen.element, `Expected ), but found ${innerInfo.remInput[0]} at position ${innerInfo.position}`);
|
|
33
|
+
return { ...closeParen, element: innerInfo.element };
|
|
34
|
+
}
|
|
35
|
+
function skipWhitespace({ remInput, position }) {
|
|
36
|
+
let idx = remInput.search(/\S/);
|
|
37
|
+
if (idx === -1) {
|
|
38
|
+
idx = remInput.length;
|
|
39
|
+
}
|
|
40
|
+
position += idx;
|
|
41
|
+
remInput = remInput.slice(idx);
|
|
42
|
+
return { position, remInput };
|
|
43
|
+
}
|
|
44
|
+
function consumeLicenseName(info) {
|
|
45
|
+
info = skipWhitespace(info);
|
|
46
|
+
let licenseName = '';
|
|
47
|
+
let isAtWhitespace = false;
|
|
48
|
+
for (let i = 0; i < info.remInput.length; i++) {
|
|
49
|
+
const char = info.remInput[i];
|
|
50
|
+
if (/[()<>~!=,&|+]/.test(char)) {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
isAtWhitespace = /\s/.test(char);
|
|
55
|
+
}
|
|
56
|
+
if (isAtWhitespace) {
|
|
57
|
+
if (/^\s*(with|and|or)\s+/i.test(info.remInput.slice(i))) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
licenseName += char;
|
|
62
|
+
}
|
|
63
|
+
const newInfo = {
|
|
64
|
+
position: info.position + licenseName.length,
|
|
65
|
+
remInput: info.remInput.slice(licenseName.length)
|
|
66
|
+
};
|
|
67
|
+
// file licenses are a special case!
|
|
68
|
+
if (licenseName && /^file[\s-]+license$/i.test(licenseName.trim())) {
|
|
69
|
+
return { ...newInfo, element: 'file LICENSE' };
|
|
70
|
+
}
|
|
71
|
+
return { ...newInfo, element: licenseName?.trim() ?? '' };
|
|
72
|
+
}
|
|
73
|
+
function makeRange(rangeStr) {
|
|
74
|
+
try {
|
|
75
|
+
rangeStr = rangeStr.trim().replaceAll('==', '=');
|
|
76
|
+
return new semver_1.Range(rangeStr);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function parseLicenseElement(info) {
|
|
83
|
+
const licenseName = consumeLicenseName(info);
|
|
84
|
+
info = skipWhitespace(licenseName);
|
|
85
|
+
// may be followed by version constraint in parentheses or directly
|
|
86
|
+
let versionConstraint;
|
|
87
|
+
if (/^\(\s*?[\d<>=!~]/.test(info.remInput)) {
|
|
88
|
+
// version constraint
|
|
89
|
+
if (info.remInput.startsWith('(')) {
|
|
90
|
+
const openParen = consumeString(info, '(');
|
|
91
|
+
(0, assert_1.guard)(openParen.element, `Expected (, but found ${info.remInput[0]} at position ${info.position}`);
|
|
92
|
+
info = skipWhitespace(openParen);
|
|
93
|
+
// consume until closing parenthesis
|
|
94
|
+
const versionStr = consumeUntilString(info, ')');
|
|
95
|
+
versionConstraint = makeRange(versionStr.element);
|
|
96
|
+
const closeParen = consumeString(versionStr, ')');
|
|
97
|
+
(0, assert_1.guard)(closeParen.element, `Expected ), but found ${versionStr.remInput[0]} at position ${versionStr.position}`);
|
|
98
|
+
info = skipWhitespace(closeParen);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// consume until whitespace or special character
|
|
102
|
+
const versionStr = consumeRegexp(info, /^[\d<>=!~.\s]+/);
|
|
103
|
+
versionConstraint = versionStr.element ? makeRange(versionStr.element) : undefined;
|
|
104
|
+
info = skipWhitespace(versionStr);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const licenseInfo = (0, objects_1.compactRecord)({
|
|
108
|
+
type: 'license',
|
|
109
|
+
license: licenseName.element,
|
|
110
|
+
versionConstraint
|
|
111
|
+
});
|
|
112
|
+
return { ...info, element: licenseInfo };
|
|
113
|
+
}
|
|
114
|
+
const operators = {
|
|
115
|
+
'and': ['and', '+', '&'],
|
|
116
|
+
'or': ['or', '|'],
|
|
117
|
+
'with': ['with']
|
|
118
|
+
};
|
|
119
|
+
function parseBinaryOperator(info) {
|
|
120
|
+
const license = parseLicenseElement(info);
|
|
121
|
+
info = skipWhitespace(license);
|
|
122
|
+
const operator = tryConsumeOperator(info);
|
|
123
|
+
if (operator.element) {
|
|
124
|
+
info = skipWhitespace(operator);
|
|
125
|
+
let rightLicense = parseLicenseInfo(info);
|
|
126
|
+
if (operator.element === 'with' && rightLicense.element.type === 'license') {
|
|
127
|
+
rightLicense = {
|
|
128
|
+
...rightLicense,
|
|
129
|
+
element: {
|
|
130
|
+
type: 'exception',
|
|
131
|
+
exception: rightLicense.element.license
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const combination = {
|
|
136
|
+
type: 'combination',
|
|
137
|
+
combination: operator.element,
|
|
138
|
+
elements: [license.element, rightLicense.element]
|
|
139
|
+
};
|
|
140
|
+
return { ...rightLicense, element: combination };
|
|
141
|
+
}
|
|
142
|
+
return { ...info, element: license.element };
|
|
143
|
+
}
|
|
144
|
+
function tryConsumeOperator(info) {
|
|
145
|
+
for (const [opName, opSymbols] of Object.entries(operators)) {
|
|
146
|
+
for (const symbol of opSymbols) {
|
|
147
|
+
if (info.remInput.toLowerCase().startsWith(symbol)) {
|
|
148
|
+
const newInfo = {
|
|
149
|
+
position: info.position + symbol.length,
|
|
150
|
+
remInput: info.remInput.slice(symbol.length)
|
|
151
|
+
};
|
|
152
|
+
return { ...newInfo, element: opName };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { ...info, element: undefined };
|
|
157
|
+
}
|
|
158
|
+
function consumeString(info, str) {
|
|
159
|
+
info = skipWhitespace(info);
|
|
160
|
+
if (info.remInput.startsWith(str)) {
|
|
161
|
+
const newInfo = {
|
|
162
|
+
position: info.position + str.length,
|
|
163
|
+
remInput: info.remInput.slice(str.length)
|
|
164
|
+
};
|
|
165
|
+
return { ...newInfo, element: true };
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
return { ...info, element: false };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function consumeUntilString(info, str) {
|
|
172
|
+
let idx = info.remInput.indexOf(str);
|
|
173
|
+
if (idx === -1) {
|
|
174
|
+
idx = info.remInput.length;
|
|
175
|
+
}
|
|
176
|
+
const consumed = info.remInput.slice(0, idx);
|
|
177
|
+
const newInfo = {
|
|
178
|
+
position: info.position + idx,
|
|
179
|
+
remInput: info.remInput.slice(idx)
|
|
180
|
+
};
|
|
181
|
+
return { ...newInfo, element: consumed };
|
|
182
|
+
}
|
|
183
|
+
function consumeRegexp(info, regex) {
|
|
184
|
+
info = skipWhitespace(info);
|
|
185
|
+
const match = info.remInput.match(regex);
|
|
186
|
+
if (match && match.index === 0) {
|
|
187
|
+
const matchedStr = match[0];
|
|
188
|
+
const newInfo = {
|
|
189
|
+
position: info.position + matchedStr.length,
|
|
190
|
+
remInput: info.remInput.slice(matchedStr.length)
|
|
191
|
+
};
|
|
192
|
+
return { ...newInfo, element: matchedStr };
|
|
193
|
+
}
|
|
194
|
+
return { ...info, element: null };
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=r-license.js.map
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dfgToAscii = dfgToAscii;
|
|
4
|
+
const dagre_1 = require("dagre");
|
|
5
|
+
const edge_1 = require("../../dataflow/graph/edge");
|
|
6
|
+
const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
|
|
7
|
+
const vertex_1 = require("../../dataflow/graph/vertex");
|
|
8
|
+
function combineAscii(has, add) {
|
|
9
|
+
if (has === ' ' || has === add) {
|
|
10
|
+
return add;
|
|
11
|
+
}
|
|
12
|
+
const [a, b] = [has, add].sort();
|
|
13
|
+
let res = add;
|
|
14
|
+
if (b === edgesChar.vertical && a === edgesChar.horizontal) {
|
|
15
|
+
res = '┼';
|
|
16
|
+
}
|
|
17
|
+
else if ((b === edgesChar.topRight || b === edgesChar.topLeft || b === '┬') && a === edgesChar.horizontal) {
|
|
18
|
+
res = '┬';
|
|
19
|
+
}
|
|
20
|
+
else if ((b === edgesChar.bottomRight || b === edgesChar.bottomLeft || b === '┴') && a === edgesChar.horizontal) {
|
|
21
|
+
res = '┴';
|
|
22
|
+
}
|
|
23
|
+
else if ((b === edgesChar.topRight || b === edgesChar.bottomRight || b === '┤') && a === edgesChar.vertical) {
|
|
24
|
+
res = '┤';
|
|
25
|
+
}
|
|
26
|
+
else if ((b === edgesChar.topLeft || b === edgesChar.bottomLeft || b === '├') && a === edgesChar.vertical) {
|
|
27
|
+
res = '├';
|
|
28
|
+
}
|
|
29
|
+
else if ((b === '┤' || b === '├') && a === edgesChar.horizontal) {
|
|
30
|
+
res = '┼';
|
|
31
|
+
}
|
|
32
|
+
else if ((b === '┬' || b === '┴') && a === edgesChar.vertical) {
|
|
33
|
+
res = '┼';
|
|
34
|
+
}
|
|
35
|
+
return res;
|
|
36
|
+
}
|
|
37
|
+
class AsciiCanvas {
|
|
38
|
+
filler;
|
|
39
|
+
grid;
|
|
40
|
+
shiftX;
|
|
41
|
+
shiftY;
|
|
42
|
+
constructor(filler = ' ', shiftX = 0, shiftY = 0) {
|
|
43
|
+
this.grid = [];
|
|
44
|
+
this.filler = filler;
|
|
45
|
+
this.shiftX = shiftX;
|
|
46
|
+
this.shiftY = shiftY;
|
|
47
|
+
}
|
|
48
|
+
set(x, y, char, overwrite = false) {
|
|
49
|
+
x += this.shiftX;
|
|
50
|
+
y += this.shiftY;
|
|
51
|
+
if (x < 0 || y < 0 || isNaN(x) || isNaN(y)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
while (this.grid.length <= y) {
|
|
55
|
+
this.grid.push([]);
|
|
56
|
+
}
|
|
57
|
+
while (this.grid[y].length <= x) {
|
|
58
|
+
this.grid[y].push(this.filler);
|
|
59
|
+
}
|
|
60
|
+
this.grid[y][x] = overwrite ? char : combineAscii(this.grid[y][x], char);
|
|
61
|
+
}
|
|
62
|
+
drawText(x, y, text) {
|
|
63
|
+
for (let i = 0; i < text.length; i++) {
|
|
64
|
+
this.set(x + i, y, text[i]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
toString() {
|
|
68
|
+
return this.grid.map(r => r.join('')).join('\n');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Converts the given dataflow graph to an ASCII representation.
|
|
73
|
+
*/
|
|
74
|
+
function dfgToAscii(dfg) {
|
|
75
|
+
const g = new dagre_1.graphlib.Graph();
|
|
76
|
+
const verts = Array.from(dfg.vertices(true));
|
|
77
|
+
g.setGraph({
|
|
78
|
+
nodesep: 1,
|
|
79
|
+
ranksep: 4,
|
|
80
|
+
edgesep: 0,
|
|
81
|
+
rankdir: verts.length < 15 ? 'LR' : 'TB',
|
|
82
|
+
ranker: 'longest-path',
|
|
83
|
+
});
|
|
84
|
+
for (const [id, v] of verts) {
|
|
85
|
+
let label = (0, node_id_1.recoverName)(id, dfg.idMap) ?? v.tag;
|
|
86
|
+
if (label.length < 3) {
|
|
87
|
+
label = label.padStart(2, ' ').padEnd(3, ' ');
|
|
88
|
+
}
|
|
89
|
+
g.setNode(String(id), {
|
|
90
|
+
label,
|
|
91
|
+
width: Math.max(3, label.length * 2),
|
|
92
|
+
height: 3,
|
|
93
|
+
shape: 'rectangle'
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const edgesDone = new Set();
|
|
97
|
+
let longestId = 0;
|
|
98
|
+
for (const [from, edges] of dfg.edges()) {
|
|
99
|
+
longestId = Math.max(longestId, String(from).length);
|
|
100
|
+
for (const [to, { types }] of edges) {
|
|
101
|
+
if (!g.hasNode(String(from)) || !g.hasNode(String(to)) || edgesDone.has(`${to}-${from}`)) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
longestId = Math.max(longestId, String(to).length);
|
|
105
|
+
g.setEdge(String(from), String(to), (0, edge_1.edgeTypesToNames)(types));
|
|
106
|
+
edgesDone.add(`${from}-${to}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
(0, dagre_1.layout)(g, {
|
|
110
|
+
minlen: 2
|
|
111
|
+
});
|
|
112
|
+
const canvas = new AsciiCanvas();
|
|
113
|
+
renderEdges(g, canvas);
|
|
114
|
+
renderVertices(dfg, g, canvas);
|
|
115
|
+
const lines = canvas.toString().split('\n').filter(line => line.trim() !== '');
|
|
116
|
+
const edgeLines = [];
|
|
117
|
+
// add all edges
|
|
118
|
+
for (const [from, edges] of dfg.edges()) {
|
|
119
|
+
for (const [to, { types }] of edges) {
|
|
120
|
+
if (!g.hasNode(String(from)) || !g.hasNode(String(to))) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
edgeLines.push(`${from.toString().padStart(longestId, ' ')} -> ${to.toString().padStart(longestId, ' ')}: ${Array.from((0, edge_1.edgeTypesToNames)(types)).join(', ')}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// always merge two edgelines with padding
|
|
127
|
+
if (edgeLines.length > 0) {
|
|
128
|
+
lines.push('Edges:');
|
|
129
|
+
}
|
|
130
|
+
const longestFirstLine = Math.max(...edgeLines.map(l => l.length));
|
|
131
|
+
for (let i = 0; i < edgeLines.length; i += 2) {
|
|
132
|
+
const line1 = edgeLines[i];
|
|
133
|
+
const line2 = edgeLines[i + 1];
|
|
134
|
+
if (line2) {
|
|
135
|
+
lines.push(line1.padEnd(Math.min(50, longestFirstLine), ' ') + line2);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
lines.push(line1);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return lines.join('\n');
|
|
142
|
+
}
|
|
143
|
+
const type2Edge = {
|
|
144
|
+
[vertex_1.VertexType.FunctionCall]: 'c',
|
|
145
|
+
[vertex_1.VertexType.Use]: 'u',
|
|
146
|
+
[vertex_1.VertexType.FunctionDefinition]: 'f',
|
|
147
|
+
[vertex_1.VertexType.VariableDefinition]: 'v',
|
|
148
|
+
[vertex_1.VertexType.Value]: '0'
|
|
149
|
+
};
|
|
150
|
+
function renderVertices(dfg, g, canvas) {
|
|
151
|
+
for (const nodeId of g.nodes()) {
|
|
152
|
+
const node = g.node(nodeId);
|
|
153
|
+
if (!node) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const label = node.label;
|
|
157
|
+
const x = Math.round(node.x);
|
|
158
|
+
const y = Math.round(node.y);
|
|
159
|
+
const tag = dfg.getVertex((0, node_id_1.normalizeIdToNumberIfPossible)(nodeId))?.tag;
|
|
160
|
+
let e = '+';
|
|
161
|
+
if (tag && tag in type2Edge) {
|
|
162
|
+
e = type2Edge[tag];
|
|
163
|
+
}
|
|
164
|
+
canvas.drawText(x - 1, y - 1, `${e}${'-'.repeat(label.length)}${e}`);
|
|
165
|
+
canvas.drawText(x - 1 + Math.round(label.length / 2 - nodeId.length / 2), y - 1, `<${nodeId}>`);
|
|
166
|
+
canvas.drawText(x - 1, y, `|${label}|`);
|
|
167
|
+
canvas.drawText(x - 1, y + 1, `${e}${'-'.repeat(label.length)}${e}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const edgesChar = {
|
|
171
|
+
vertical: '│',
|
|
172
|
+
horizontal: '─',
|
|
173
|
+
topLeft: '┌',
|
|
174
|
+
topRight: '┐',
|
|
175
|
+
bottomLeft: '└',
|
|
176
|
+
bottomRight: '┘',
|
|
177
|
+
};
|
|
178
|
+
function determineCornerChar(lastDirection, px, py, tx, ty) {
|
|
179
|
+
if (px === tx) {
|
|
180
|
+
return edgesChar.vertical;
|
|
181
|
+
}
|
|
182
|
+
else if (py === ty) {
|
|
183
|
+
return edgesChar.horizontal;
|
|
184
|
+
}
|
|
185
|
+
else if (px < tx) {
|
|
186
|
+
if (py < ty) {
|
|
187
|
+
return lastDirection === 'horizontal' ? edgesChar.topRight : edgesChar.bottomLeft;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
return lastDirection === 'horizontal' ? edgesChar.bottomRight : edgesChar.topLeft;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
if (py < ty) {
|
|
195
|
+
return lastDirection === 'horizontal' ? edgesChar.topLeft : edgesChar.bottomRight;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
return lastDirection === 'horizontal' ? edgesChar.bottomLeft : edgesChar.topRight;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function renderEdges(g, canvas) {
|
|
203
|
+
const otherEdges = new Set();
|
|
204
|
+
for (const e of g.edges()) {
|
|
205
|
+
const edge = g.edge(e);
|
|
206
|
+
let points = edge.points;
|
|
207
|
+
// we rework edges into sequences of straight lines only, adding intermediate points as needed
|
|
208
|
+
const newPoints = [points[0]];
|
|
209
|
+
for (let i = 1; i < points.length; i++) {
|
|
210
|
+
const prev = points[i - 1];
|
|
211
|
+
const curr = points[i];
|
|
212
|
+
if (prev.x !== curr.x && prev.y !== curr.y) {
|
|
213
|
+
const intermediate = { x: prev.x, y: curr.y };
|
|
214
|
+
newPoints.push(intermediate);
|
|
215
|
+
}
|
|
216
|
+
newPoints.push(curr);
|
|
217
|
+
}
|
|
218
|
+
points = newPoints;
|
|
219
|
+
let lastDirection = null;
|
|
220
|
+
// let single edges overwrite themselves
|
|
221
|
+
const writtenPoints = new Set();
|
|
222
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
223
|
+
const p = points[i - 1] ?? points[i];
|
|
224
|
+
const a = points[i];
|
|
225
|
+
const b = points[i + 1];
|
|
226
|
+
const px = Math.round(p.x);
|
|
227
|
+
const py = Math.round(p.y);
|
|
228
|
+
const x1 = Math.round(a.x);
|
|
229
|
+
const y1 = Math.round(a.y);
|
|
230
|
+
const x2 = Math.round(b.x);
|
|
231
|
+
const y2 = Math.round(b.y);
|
|
232
|
+
if (x1 === x2) {
|
|
233
|
+
// vertical
|
|
234
|
+
const [start, end] = y1 < y2 ? [y1, y2] : [y2, y1];
|
|
235
|
+
for (let y = start; y <= end; y++) {
|
|
236
|
+
const key = `${x1},${y}`;
|
|
237
|
+
const overwrite = writtenPoints.has(key) && !otherEdges.has(key);
|
|
238
|
+
if (y === (y1 < y2 ? start : end)) {
|
|
239
|
+
const cornerChar = determineCornerChar(lastDirection, px, py, x2, y2);
|
|
240
|
+
canvas.set(x1, y, cornerChar, overwrite);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
canvas.set(x1, y, edgesChar.vertical, overwrite);
|
|
244
|
+
}
|
|
245
|
+
writtenPoints.add(`${x1},${y}`);
|
|
246
|
+
}
|
|
247
|
+
lastDirection = 'vertical';
|
|
248
|
+
}
|
|
249
|
+
else if (y1 === y2) {
|
|
250
|
+
// horizontal
|
|
251
|
+
const [start, end] = x1 < x2 ? [x1, x2] : [x2, x1];
|
|
252
|
+
for (let x = start; x <= end; x++) {
|
|
253
|
+
const key = `${x},${y1}`;
|
|
254
|
+
const overwrite = writtenPoints.has(key) && !otherEdges.has(key);
|
|
255
|
+
if (x === (x1 < x2 ? start : end)) {
|
|
256
|
+
const cornerChar = determineCornerChar(lastDirection, px, py, x2, y2);
|
|
257
|
+
canvas.set(x, y1, cornerChar, overwrite);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
canvas.set(x, y1, edgesChar.horizontal, overwrite);
|
|
261
|
+
}
|
|
262
|
+
writtenPoints.add(`${x},${y1}`);
|
|
263
|
+
}
|
|
264
|
+
lastDirection = 'horizontal';
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
for (const p of writtenPoints) {
|
|
268
|
+
otherEdges.add(p);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=dfg-ascii.js.map
|
package/util/version.js
CHANGED
|
@@ -6,7 +6,7 @@ exports.printVersionInformation = printVersionInformation;
|
|
|
6
6
|
const semver_1 = require("semver");
|
|
7
7
|
const assert_1 = require("./assert");
|
|
8
8
|
// this is automatically replaced with the current version by release-it
|
|
9
|
-
const version = '2.7.
|
|
9
|
+
const version = '2.7.6';
|
|
10
10
|
/**
|
|
11
11
|
* Retrieves the current flowR version as a new {@link SemVer} object.
|
|
12
12
|
*/
|