Skip to content

Commit fe5cac3

Browse files
committed
implement replacement
1 parent 7b95065 commit fe5cac3

File tree

3 files changed

+142
-136
lines changed

3 files changed

+142
-136
lines changed

src/features/constraintMenu/AutoCompletion.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ export interface ValidationError {
1313
endColumn: number;
1414
}
1515

16-
interface Token {
16+
export interface Token {
1717
text: string;
1818
line: number;
1919
column: number;
20+
whiteSpaceAfter?: string;
2021
}
2122

2223
export type WordCompletion = RequiredCompletionParts & Partial<monaco.languages.CompletionItem>;
@@ -98,22 +99,27 @@ export class NegatableWord implements AbstractWord {
9899
export class AutoCompleteTree {
99100
constructor(protected roots: AutoCompleteNode<AbstractWord>[]) {}
100101

101-
private tokenize(text: string[]): Token[] {
102+
protected tokenize(text: string[]): Token[] {
102103
if (!text || text.length == 0) {
103104
return [];
104105
}
105106

106107
const tokens: Token[] = [];
107108
for (const [lineNumber, line] of text.entries()) {
108-
const lineTokens = line.split(/\s+/).filter((t) => t.length > 0);
109+
const lineTokens = line.split(/(\s+)/);
109110
let column = 0;
110-
for (const token of lineTokens) {
111-
column = line.indexOf(token, column);
112-
tokens.push({
113-
text: token,
114-
line: lineNumber + 1,
115-
column: column + 1,
116-
});
111+
for (let i = 0; i < lineTokens.length; i += 2) {
112+
const token = lineTokens[i];
113+
if (token.length > 0) {
114+
tokens.push({
115+
text: token,
116+
line: lineNumber + 1,
117+
column: column + 1,
118+
whiteSpaceAfter: lineTokens[i + 1],
119+
});
120+
}
121+
column += token.length;
122+
column += lineTokens[i + 1] ? lineTokens[i + 1].length : 0; // Add whitespace length
117123
}
118124
}
119125

@@ -217,7 +223,6 @@ export class AutoCompleteTree {
217223
skipStartCheck = false,
218224
): WordCompletion[] {
219225
// check for new start
220-
221226
if (!skipStartCheck && tokens[index].column == 1) {
222227
const matchesAnyRoot = this.roots.some((n) => n.word.verifyWord(tokens[index].text).length === 0);
223228
if (matchesAnyRoot) {

src/features/dfdElements/AssignmentLanguage.ts

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
AutoCompleteNode,
55
AutoCompleteTree,
66
ConstantWord,
7+
Token,
78
WordCompletion,
89
} from "../constraintMenu/AutoCompletion";
910
import { SModelElementImpl, SModelRootImpl, SParentElementImpl, SPortImpl } from "sprotty";
@@ -76,7 +77,7 @@ export const assignemntLanguageMonarchDefinition: monaco.languages.IMonarchLangu
7677
};
7778

7879
interface ReplaceableAbstractWord extends AbstractWord {
79-
replaceWord: (text: string, old: string, replacement: string) => string;
80+
replaceWord(text: string, old: string, replacement: string): string;
8081
}
8182

8283
type WordOrReplacableWord = ReplaceableAbstractWord | AbstractWord;
@@ -85,6 +86,60 @@ export class ReplaceAutoCompleteTree extends AutoCompleteTree {
8586
constructor(protected roots: AutoCompleteNode<WordOrReplacableWord>[]) {
8687
super(roots);
8788
}
89+
90+
public replace(lines: string[], old: string, replacement: string): string[] {
91+
const tokens = this.tokenize(lines);
92+
const replaced = this.replaceToken(this.roots, tokens, 0, old, replacement);
93+
const newLines: string[] = [];
94+
let currentLine = "";
95+
for (let i = 0; i < tokens.length; i++) {
96+
const token = tokens[i];
97+
const newText = replaced[i];
98+
currentLine += newText;
99+
currentLine += token.whiteSpaceAfter || "";
100+
if (i == tokens.length - 1 || tokens[i + 1].line !== token.line) {
101+
newLines.push(currentLine);
102+
currentLine = "";
103+
}
104+
}
105+
return newLines;
106+
}
107+
108+
private replaceToken(
109+
nodes: AutoCompleteNode<WordOrReplacableWord>[],
110+
tokens: Token[],
111+
index: number,
112+
old: string,
113+
replacement: string,
114+
skipStartCheck = false,
115+
): string[] {
116+
if (index >= tokens.length) {
117+
return [];
118+
}
119+
// check for new start
120+
if (!skipStartCheck && tokens[index].column == 1) {
121+
const matchesAnyRoot = this.roots.some((n) => n.word.verifyWord(tokens[index].text).length === 0);
122+
if (matchesAnyRoot) {
123+
return this.replaceToken(this.roots, tokens, index, old, replacement, true);
124+
}
125+
}
126+
let text = tokens[index].text;
127+
for (const n of nodes) {
128+
if ((n.word as ReplaceableAbstractWord).replaceWord) {
129+
text = (n.word as ReplaceableAbstractWord).replaceWord(text, old, replacement);
130+
}
131+
}
132+
return [
133+
text,
134+
...this.replaceToken(
135+
nodes.flatMap((n) => n.children),
136+
tokens,
137+
index + 1,
138+
old,
139+
replacement,
140+
),
141+
];
142+
}
88143
}
89144

90145
export namespace TreeBuilder {
@@ -277,7 +332,11 @@ class LabelListWord implements ReplaceableAbstractWord {
277332
completionOptions(word: string): WordCompletion[] {
278333
const parts = word.split(",");
279334
const lastPart = parts[parts.length - 1];
280-
return this.labelWord.completionOptions(lastPart);
335+
const prefixLength = parts.slice(0, -1).reduce((acc, part) => acc + part.length + 1, 0); // +1 for the commas
336+
return this.labelWord.completionOptions(lastPart).map((c) => ({
337+
...c,
338+
startOffset: prefixLength + (c.startOffset ?? 0),
339+
}));
281340
}
282341

283342
verifyWord(word: string): string[] {
@@ -298,7 +357,8 @@ class LabelListWord implements ReplaceableAbstractWord {
298357

299358
class InputWord extends InputAwareWord implements ReplaceableAbstractWord {
300359
completionOptions(): WordCompletion[] {
301-
return this.getAvailableInputs().map((input) => ({
360+
const inputs = this.getAvailableInputs();
361+
return inputs.map((input) => ({
302362
insertText: input,
303363
kind: monaco.languages.CompletionItemKind.Variable,
304364
}));
@@ -313,9 +373,8 @@ class InputWord extends InputAwareWord implements ReplaceableAbstractWord {
313373
}
314374

315375
replaceWord(text: string, old: string, replacement: string) {
316-
const availableInputs = this.getAvailableInputs();
317-
if (availableInputs.includes(old)) {
318-
return text.replace(old, replacement);
376+
if (text == old) {
377+
return replacement;
319378
}
320379
return text;
321380
}

0 commit comments

Comments
 (0)