Parcourir la source

- Fixed various bugs in the source editor

- Added ability to remove a selected block of text
Stan Hebben il y a 6 ans
Parent
révision
cccd95ba21

+ 2
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DDrawingContext.java Voir le fichier

@@ -20,6 +20,8 @@ public interface DDrawingContext {
20 20
 	
21 21
 	void scrollInView(int x, int y, int width, int height);
22 22
 	
23
+	DTimerHandle setTimer(int millis, Runnable target);
24
+	
23 25
 	DFontMetrics getFontMetrics(DFont font);
24 26
 	
25 27
 	enum Cursor {

+ 17
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DTimerHandle.java Voir le fichier

@@ -0,0 +1,17 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.drawablegui;
7
+
8
+import java.io.Closeable;
9
+
10
+/**
11
+ *
12
+ * @author Hoofdgebruiker
13
+ */
14
+public interface DTimerHandle extends Closeable {
15
+	@Override
16
+	public void close();
17
+}

+ 6
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/scroll/DScrollPane.java Voir le fichier

@@ -14,6 +14,7 @@ import org.openzen.drawablegui.DFont;
14 14
 import org.openzen.drawablegui.DFontMetrics;
15 15
 import org.openzen.drawablegui.DIRectangle;
16 16
 import org.openzen.drawablegui.DMouseEvent;
17
+import org.openzen.drawablegui.DTimerHandle;
17 18
 import org.openzen.drawablegui.listeners.ListenerHandle;
18 19
 import org.openzen.drawablegui.live.LiveInt;
19 20
 import org.openzen.drawablegui.live.LiveObject;
@@ -248,6 +249,11 @@ public class DScrollPane implements DComponent {
248 249
 			if (y + height > offsetY.getValue() + bounds.height)
249 250
 				offsetY.setValue(y + height - bounds.height);
250 251
 		}
252
+
253
+		@Override
254
+		public DTimerHandle setTimer(int millis, Runnable target) {
255
+			return context.setTimer(millis, target);
256
+		}
251 257
 	}
252 258
 	
253 259
 	private class ScrollListener implements LiveInt.Listener {

+ 9
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingGraphicsContext.java Voir le fichier

@@ -9,12 +9,14 @@ import java.awt.Font;
9 9
 import java.awt.Graphics;
10 10
 import java.awt.geom.GeneralPath;
11 11
 import java.util.WeakHashMap;
12
+import javax.swing.Timer;
12 13
 import org.openzen.drawablegui.DComponent;
13 14
 import org.openzen.drawablegui.DPath;
14 15
 import org.openzen.drawablegui.DDrawingContext;
15 16
 import org.openzen.drawablegui.DFont;
16 17
 import org.openzen.drawablegui.DFontMetrics;
17 18
 import org.openzen.drawablegui.DPathTracer;
19
+import org.openzen.drawablegui.DTimerHandle;
18 20
 
19 21
 /**
20 22
  *
@@ -101,6 +103,13 @@ public class SwingGraphicsContext implements DDrawingContext {
101 103
 	public void scrollInView(int x, int y, int width, int height) {
102 104
 		// not in a scrollable context
103 105
 	}
106
+
107
+	@Override
108
+	public DTimerHandle setTimer(int millis, Runnable target) {
109
+		Timer timer = new Timer(millis, e -> target.run());
110
+		timer.start();
111
+		return () -> timer.stop();
112
+	}
104 113
 	
105 114
 	private class PathTracer implements DPathTracer {
106 115
 		private final GeneralPath path;

+ 72
- 117
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourceEditor.java Voir le fichier

@@ -6,8 +6,6 @@
6 6
 package org.openzen.zenscript.ide.ui.view.editor;
7 7
 
8 8
 import java.io.IOException;
9
-import java.util.Timer;
10
-import java.util.TimerTask;
11 9
 import org.openzen.drawablegui.DCanvas;
12 10
 import org.openzen.drawablegui.DComponent;
13 11
 import org.openzen.drawablegui.DDimensionPreferences;
@@ -52,10 +50,9 @@ public class SourceEditor implements DComponent {
52 50
 	private int selectionLineHeight;
53 51
 	
54 52
 	private int lineBarWidth;
55
-	private CursorPosition cursorStart = null;
56
-	private CursorPosition cursorEnd = null;
53
+	private SourcePosition cursorStart = null;
54
+	private SourcePosition cursorEnd = null;
57 55
 	
58
-	private Timer blink = new Timer();
59 56
 	private boolean cursorBlink = true;
60 57
 	
61 58
 	private int mouseDownX = -1;
@@ -67,13 +64,6 @@ public class SourceEditor implements DComponent {
67 64
 		tokens = new TokenModel(sourceFile.getName(), tab.length());
68 65
 		tokenListener = tokens.addListener(new TokenListener());
69 66
 		
70
-		blink.scheduleAtFixedRate(new TimerTask() {
71
-			@Override
72
-			public void run() {
73
-				blink();
74
-			}
75
-		}, 300, 300);
76
-		
77 67
 		try {
78 68
 			TokenParser<ZSToken, ZSTokenType> parser = ZSTokenParser.createRaw(sourceFile.getName(), new ReaderCharReader(sourceFile.read()), tab.length());
79 69
 			tokens.set(parser);
@@ -91,6 +81,7 @@ public class SourceEditor implements DComponent {
91 81
 		selectionLineHeight = textLineHeight + style.selectionPaddingTop + style.selectionPaddingBottom;
92 82
 		
93 83
 		dimensionPreferences.setValue(new DDimensionPreferences(0, fullLineHeight * tokens.getLineCount()));
84
+		context.setTimer(300, this::blink);
94 85
 	}
95 86
 
96 87
 	@Override
@@ -126,25 +117,25 @@ public class SourceEditor implements DComponent {
126 117
 		
127 118
 		if (cursorStart != null && !cursorStart.equals(cursorEnd)) {
128 119
 			if (cursorStart.line == cursorEnd.line) {
129
-				int y = cursorStart.getY();
130
-				int x1 = cursorStart.getX();
131
-				int x2 = cursorEnd.getX();
120
+				int y = getY(cursorStart);
121
+				int x1 = getX(cursorStart);
122
+				int x2 = getX(cursorEnd);
132 123
 				int fromX = Math.min(x1, x2);
133 124
 				int toX = Math.max(x1, x2);
134 125
 				canvas.fillRectangle(fromX, y, toX - fromX, selectionLineHeight, style.selectionColor);
135 126
 			} else {
136
-				CursorPosition from = cursorStart.line < cursorEnd.line ? cursorStart : cursorEnd;
137
-				CursorPosition to = cursorStart.line < cursorEnd.line ? cursorEnd : cursorStart;
127
+				SourcePosition from = SourcePosition.min(cursorStart, cursorEnd);
128
+				SourcePosition to = SourcePosition.max(cursorStart, cursorEnd);
138 129
 				
139
-				int fromX = from.getX();
140
-				canvas.fillRectangle(fromX, from.getY(), bounds.width - fromX, selectionLineHeight, style.selectionColor);
130
+				int fromX = getX(from);
131
+				canvas.fillRectangle(fromX, getY(from), bounds.width - fromX, selectionLineHeight, style.selectionColor);
141 132
 				
142 133
 				for (int i = from.line + 1; i < to.line; i++) {
143 134
 					canvas.fillRectangle(x, lineToY(i), bounds.width - x, selectionLineHeight, style.selectionColor);
144 135
 				}
145 136
 				
146
-				int toX = to.getX();
147
-				canvas.fillRectangle(x, to.getY(), toX - x, selectionLineHeight, style.selectionColor);
137
+				int toX = getX(to);
138
+				canvas.fillRectangle(x, getY(to), toX - x, selectionLineHeight, style.selectionColor);
148 139
 			}
149 140
 		}
150 141
 		
@@ -169,8 +160,8 @@ public class SourceEditor implements DComponent {
169 160
 		}
170 161
 		
171 162
 		if (cursorEnd != null && cursorBlink) {
172
-			int cursorX = cursorEnd.getX();
173
-			int cursorY = cursorEnd.getY();
163
+			int cursorX = getX(cursorEnd);
164
+			int cursorY = getY(cursorEnd);
174 165
 			canvas.fillRectangle(cursorX, cursorY, 2, selectionLineHeight, 0xFF000000);
175 166
 		}
176 167
 	}
@@ -189,23 +180,23 @@ public class SourceEditor implements DComponent {
189 180
 	public void onMouseClick(DMouseEvent e) {
190 181
 		context.focus(this);
191 182
 		
192
-		CursorPosition position = getPositionAt(e.x, e.y);
183
+		SourcePosition position = getPositionAt(e.x, e.y);
193 184
 		if (e.isDoubleClick()) {
194 185
 			// select entire word
195
-			TokenPosition token = getTokenAt(position);
186
+			TokenModel.Position tokenPosition = tokens.getPosition(position.line, position.offset);
196 187
 			setCursor(
197
-					new CursorPosition(position.line, position.offset - token.offset),
198
-					new CursorPosition(position.line, position.offset + token.token.content.length() - token.offset));
188
+					new SourcePosition(tokens, position.line, position.offset - tokenPosition.offset),
189
+					new SourcePosition(tokens, position.line, position.offset - tokenPosition.offset + tokens.getTokenAt(tokenPosition).content.length()));
199 190
 		} else if (e.isTripleClick()) {
200 191
 			setCursor(
201
-					new CursorPosition(position.line, 0),
202
-					new CursorPosition(position.line + 1, 0));
192
+					new SourcePosition(tokens, position.line, 0),
193
+					new SourcePosition(tokens, position.line + 1, 0));
203 194
 		} else {
204 195
 			setCursor(position, position);
205 196
 		}
206 197
 	}
207 198
 	
208
-	private void setCursor(CursorPosition start, CursorPosition end) {
199
+	private void setCursor(SourcePosition start, SourcePosition end) {
209 200
 		if (cursorStart != null)
210 201
 			repaint(cursorStart, cursorEnd);
211 202
 		
@@ -231,7 +222,7 @@ public class SourceEditor implements DComponent {
231 222
 	
232 223
 	@Override
233 224
 	public void onMouseDrag(DMouseEvent e) {
234
-		CursorPosition start = cursorStart;
225
+		SourcePosition start = cursorStart;
235 226
 		if (!dragging)
236 227
 			start = getPositionAt(mouseDownX, mouseDownY);
237 228
 		
@@ -248,7 +239,8 @@ public class SourceEditor implements DComponent {
248 239
 				
249 240
 				{
250 241
 					int line = cursorEnd.line - 1;
251
-					CursorPosition position = new CursorPosition(
242
+					SourcePosition position = new SourcePosition(
243
+							tokens,
252 244
 							line,
253 245
 							Math.min(tokens.getLineLength(line), cursorEnd.offset));
254 246
 					setCursor(shift ? cursorStart : position, position);
@@ -260,7 +252,8 @@ public class SourceEditor implements DComponent {
260 252
 				
261 253
 				{
262 254
 					int line = cursorEnd.line + 1;
263
-					CursorPosition position = new CursorPosition(
255
+					SourcePosition position = new SourcePosition(
256
+							tokens,
264 257
 							line,
265 258
 							Math.min(tokens.getLineLength(line), cursorEnd.offset));
266 259
 					setCursor(shift ? cursorStart : position, position);
@@ -271,12 +264,12 @@ public class SourceEditor implements DComponent {
271 264
 					return;
272 265
 				
273 266
 				{
274
-					CursorPosition position;
267
+					SourcePosition position;
275 268
 					if (cursorEnd.offset == 0) {
276 269
 						int line = cursorEnd.line - 1;
277
-						position = new CursorPosition(line, tokens.getLineLength(line));
270
+						position = new SourcePosition(tokens, line, tokens.getLineLength(line));
278 271
 					} else {
279
-						position = new CursorPosition(cursorEnd.line, cursorEnd.offset - 1);
272
+						position = new SourcePosition(tokens, cursorEnd.line, cursorEnd.offset - 1);
280 273
 					}
281 274
 					setCursor(shift ? cursorStart : position, position);
282 275
 				}
@@ -286,11 +279,11 @@ public class SourceEditor implements DComponent {
286 279
 					return;
287 280
 				
288 281
 				{
289
-					CursorPosition position;
282
+					SourcePosition position;
290 283
 					if (cursorEnd.offset == tokens.getLineLength(cursorEnd.line)) {
291
-						position = new CursorPosition(cursorEnd.line + 1, 0);
284
+						position = new SourcePosition(tokens, cursorEnd.line + 1, 0);
292 285
 					} else {
293
-						position = new CursorPosition(cursorEnd.line, cursorEnd.offset + 1);
286
+						position = new SourcePosition(tokens, cursorEnd.line, cursorEnd.offset + 1);
294 287
 					}
295 288
 					setCursor(shift ? cursorStart : position, position);
296 289
 				}
@@ -344,10 +337,17 @@ public class SourceEditor implements DComponent {
344 337
 		}
345 338
 		
346 339
 		tokens.deleteCharacter(cursorEnd.line, cursorEnd.offset);
347
-		repaintLine(cursorEnd.line);
348 340
 	}
349 341
 	
350 342
 	private void backspace() {
343
+		if (!cursorEnd.equals(cursorStart)) {
344
+			SourcePosition min = SourcePosition.min(cursorStart, cursorEnd);
345
+			SourcePosition max = SourcePosition.max(cursorStart, cursorEnd);
346
+			tokens.delete(min.line, min.offset, max.line, max.offset);
347
+			setCursor(min, min);
348
+			return;
349
+		}
350
+		
351 351
 		if (cursorEnd.offset == 0) {
352 352
 			if (cursorEnd.line == 0)
353 353
 				return;
@@ -355,56 +355,40 @@ public class SourceEditor implements DComponent {
355 355
 			int length = tokens.getLineLength(cursorEnd.line - 1);
356 356
 			tokens.deleteNewline(cursorEnd.line - 1);
357 357
 			
358
-			CursorPosition position = new CursorPosition(cursorEnd.line - 1, length);
358
+			SourcePosition position = new SourcePosition(tokens, cursorEnd.line - 1, length);
359 359
 			setCursor(position, position);
360 360
 			return;
361 361
 		}
362 362
 		
363
-		tokens.deleteCharacter(cursorEnd.line, cursorEnd.offset - 1);
364
-		CursorPosition position = new CursorPosition(cursorEnd.line, cursorEnd.offset - 1);
365
-		setCursor(position, position);
363
+		try {
364
+			tokens.deleteCharacter(cursorEnd.line, cursorEnd.offset - 1);
365
+			SourcePosition position = new SourcePosition(tokens, cursorEnd.line, cursorEnd.offset - 1);
366
+			setCursor(position, position);
367
+		} catch (Exception ex) {
368
+			ex.printStackTrace();
369
+		}
366 370
 	}
367 371
 	
368 372
 	private void type(String value) {
369 373
 		tokens.insert(cursorEnd.line, cursorEnd.offset, value);
370
-		CursorPosition position = new CursorPosition(cursorEnd.line, cursorEnd.offset + value.length());
374
+		SourcePosition position = new SourcePosition(tokens, cursorEnd.line, cursorEnd.offset + value.length());
371 375
 		setCursor(position, position);
372 376
 	}
373 377
 	
374 378
 	private void newline() {
375 379
 		String indent = tokens.getLine(cursorEnd.line).getIndent();
376 380
 		tokens.insert(cursorEnd.line, cursorEnd.offset, "\n" + indent);
377
-		CursorPosition position = new CursorPosition(cursorEnd.line + 1, indent.length());
381
+		SourcePosition position = new SourcePosition(tokens, cursorEnd.line + 1, indent.length());
378 382
 		setCursor(position, position);
379 383
 	}
380 384
 	
381
-	private TokenPosition getTokenAt(CursorPosition position) {
382
-		TokenLine line = tokens.getLine(position.line);
383
-		int offset = 0;
384
-		for (ZSToken token : line.getTokens()) {
385
-			if (offset + token.content.length() > position.offset)
386
-				return new TokenPosition(token, position.offset - offset);
387
-			offset += token.content.length();
388
-		}
389
-		
390
-		return new TokenPosition(line.getLastToken(), position.offset - offset);
391
-	}
392
-	
393
-	private class TokenPosition {
394
-		public final ZSToken token;
395
-		public final int offset;
396
-		
397
-		public TokenPosition(ZSToken token, int offset) {
398
-			this.token = token;
399
-			this.offset = offset;
400
-		}
401
-	}
402
-	
403
-	private void repaint(CursorPosition from, CursorPosition to) {
385
+	private void repaint(SourcePosition from, SourcePosition to) {
404 386
 		if (from.line == to.line) {
405 387
 			int y = lineToY(from.line);
406
-			int fromX = offsetToX(from.line, Math.min(from.offset, to.offset));
407
-			int toX = offsetToX(from.line, Math.max(from.offset, to.offset)) + 2;
388
+			int x1 = getX(from);
389
+			int x2 = getX(to);
390
+			int fromX = Math.min(x1, x2);
391
+			int toX = Math.max(x1, x2) + 2;
408 392
 			context.repaint(fromX, y, toX - fromX, selectionLineHeight);
409 393
 		} else {
410 394
 			int fromY = lineToY(Math.min(from.line, to.line));
@@ -420,10 +404,8 @@ public class SourceEditor implements DComponent {
420 404
 		context.repaint(bounds.x, lineToY(line), bounds.width, selectionLineHeight);
421 405
 	}
422 406
 	
423
-	private void scrollTo(CursorPosition position) {
424
-		int y = lineToY(position.line);
425
-		int x = offsetToX(position.line, position.offset);
426
-		context.scrollInView(x, y, 2, selectionLineHeight);
407
+	public void scrollTo(SourcePosition position) {
408
+		context.scrollInView(getX(position), getY(position), 2, selectionLineHeight);
427 409
 	}
428 410
 	
429 411
 	private void blink() {
@@ -433,7 +415,7 @@ public class SourceEditor implements DComponent {
433 415
 		}
434 416
 	}
435 417
 	
436
-	private CursorPosition getPositionAt(int x, int y) {
418
+	public SourcePosition getPositionAt(int x, int y) {
437 419
 		int line = yToLine(y);
438 420
 		int offset = xToOffset(line, x);
439 421
 		
@@ -442,7 +424,7 @@ public class SourceEditor implements DComponent {
442 424
 		if (line >= tokens.getLineCount())
443 425
 			line = tokens.getLineCount() - 1;
444 426
 		
445
-		return new CursorPosition(line, offset);
427
+		return new SourcePosition(tokens, line, offset);
446 428
 	}
447 429
 	
448 430
 	private int xToOffset(int lineIndex, int x) {
@@ -493,28 +475,23 @@ public class SourceEditor implements DComponent {
493 475
 		return startY + line * fullLineHeight;
494 476
 	}
495 477
 	
496
-	private int offsetToX(int line, int offset) {
497
-		if (line >= tokens.getLineCount())
498
-			return 0;
499
-		
500
-		int tokensOffset = 0;
478
+	public int getX(SourcePosition position) {
501 479
 		int x = bounds.x + lineBarWidth + 10;
502
-		TokenLine lineData = tokens.getLine(line);
503
-		for (ZSToken token : lineData.getTokens()) {
504
-			String content = getDisplayContent(token);
505
-			if (tokensOffset + token.content.length() >= offset) {
506
-				if (token.type == ZSTokenType.T_WHITESPACE_TAB)
507
-					return offset == tokensOffset ? x : x + fontMetrics.getWidth(tab);
508
-				
509
-				return x + fontMetrics.getWidth(token.content, 0, offset - tokensOffset);
510
-			}
511
-			
512
-			x += fontMetrics.getWidth(content);
513
-			tokensOffset += token.content.length();
514
-		}
480
+		TokenLine lineData = tokens.getLine(position.line);
481
+		
482
+		TokenModel.Position tokenPosition = position.asTokenPosition();
483
+		for (int i = 0; i < tokenPosition.token; i++)
484
+			x += fontMetrics.getWidth(getDisplayContent(lineData.getToken(i)));
485
+		if (tokenPosition.offset > 0)
486
+			x += fontMetrics.getWidth(getDisplayContent(lineData.getToken(tokenPosition.token)), 0, tokenPosition.offset);
487
+		
515 488
 		return x;
516 489
 	}
517 490
 	
491
+	public int getY(SourcePosition position) {
492
+		return lineToY(position.line);
493
+	}
494
+	
518 495
 	private String getDisplayContent(ZSToken token) {
519 496
 		return token.type == ZSTokenType.T_WHITESPACE_TAB ? tab : token.content;
520 497
 	}
@@ -658,26 +635,4 @@ public class SourceEditor implements DComponent {
658 635
 			}
659 636
 		}
660 637
 	}
661
-	
662
-	public class CursorPosition {
663
-		public final int line;
664
-		public final int offset;
665
-		
666
-		public CursorPosition(int line, int offset) {
667
-			this.line = line;
668
-			this.offset = offset;
669
-		}
670
-		
671
-		public int getX() {
672
-			return offsetToX(line, offset);
673
-		}
674
-		
675
-		public int getY() {
676
-			return lineToY(line);
677
-		}
678
-		
679
-		public boolean equals(CursorPosition other) {
680
-			return line == other.line && offset == other.offset;
681
-		}
682
-	}
683 638
 }

+ 64
- 0
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourcePosition.java Voir le fichier

@@ -0,0 +1,64 @@
1
+/*
2
+ * To change this license header, choose License Headers in Project Properties.
3
+ * To change this template file, choose Tools | Templates
4
+ * and open the template in the editor.
5
+ */
6
+package org.openzen.zenscript.ide.ui.view.editor;
7
+
8
+/**
9
+ *
10
+ * @author Hoofdgebruiker
11
+ */
12
+public class SourcePosition {
13
+	public final TokenModel tokens;
14
+	public final int line;
15
+	public final int offset;
16
+	
17
+	private long positionVersion;
18
+	private TokenModel.Position position;
19
+
20
+	public SourcePosition(TokenModel tokens, int line, int offset) {
21
+		this.tokens = tokens;
22
+		this.line = line;
23
+		this.offset = offset;
24
+	}
25
+	
26
+	public static SourcePosition min(SourcePosition a, SourcePosition b) {
27
+		if (a.line < b.line)
28
+			return a;
29
+		if (a.line > b.line)
30
+			return b;
31
+		if (a.offset < b.offset)
32
+			return a;
33
+		if (a.offset > b.offset)
34
+			return b;
35
+
36
+		return a;
37
+	}
38
+	
39
+	public static SourcePosition max(SourcePosition a, SourcePosition b) {
40
+		if (a.line < b.line)
41
+			return b;
42
+		if (a.line > b.line)
43
+			return a;
44
+		if (a.offset < b.offset)
45
+			return b;
46
+		if (a.offset > b.offset)
47
+			return a;
48
+
49
+		return b;
50
+	}
51
+
52
+	public boolean equals(SourcePosition other) {
53
+		return line == other.line && offset == other.offset;
54
+	}
55
+	
56
+	public TokenModel.Position asTokenPosition() {
57
+		if (position == null || positionVersion != tokens.getVersion()) {
58
+			positionVersion = tokens.getVersion();
59
+			position = tokens.getPosition(line, offset);
60
+		}
61
+		
62
+		return position;
63
+	}
64
+}

+ 1
- 1
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/TokenLine.java Voir le fichier

@@ -40,7 +40,7 @@ public final class TokenLine {
40 40
 	}
41 41
 	
42 42
 	public ZSToken getLastToken() {
43
-		return tokens.get(tokens.size() - 1);
43
+		return tokens.isEmpty() ? null : tokens.get(tokens.size() - 1);
44 44
 	}
45 45
 	
46 46
 	public String getIndent() {

+ 94
- 20
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/TokenModel.java Voir le fichier

@@ -26,6 +26,7 @@ public class TokenModel {
26 26
 	private final String filename;
27 27
 	private final int spacesPerTab;
28 28
 	private final List<TokenLine> lines = new ArrayList<>();
29
+	private long version = 0;
29 30
 	
30 31
 	public TokenModel(String filename, int spacesPerTab) {
31 32
 		this.filename = filename;
@@ -52,6 +53,42 @@ public class TokenModel {
52 53
 		return lines.get(line).length();
53 54
 	}
54 55
 	
56
+	public ZSToken getTokenAt(Position position) {
57
+		TokenLine line = lines.get(position.line);
58
+		return position.token >= line.getTokenCount() ? null : line.getToken(position.token);
59
+	}
60
+	
61
+	/**
62
+	 * Version can be used to check if the token model has been updated. If the
63
+	 * version is unchanged, the contents of the token model will be unchanged
64
+	 * too.
65
+	 * 
66
+	 * @return version number
67
+	 */
68
+	public long getVersion() {
69
+		return version;
70
+	}
71
+	
72
+	public Position getPosition(int line, int offset) {
73
+		if (line < 0)
74
+			line = 0;
75
+		if (line >= lines.size())
76
+			return new Position(lines.size(), lines.get(lines.size() - 1).getTokenCount(), 0);
77
+		
78
+		int token = 0;
79
+		int tokenOffset = 0;
80
+		TokenLine tokenLine = getLine(line);
81
+		while (token < tokenLine.getTokenCount()) {
82
+			ZSToken t = tokenLine.getToken(token);
83
+			if (tokenOffset + t.content.length() > offset)
84
+				return new Position(line, token, offset - tokenOffset);
85
+			
86
+			tokenOffset += t.content.length();
87
+			token++;
88
+		}
89
+		return new Position(line, tokenLine.getTokenCount(), 0);
90
+	}
91
+	
55 92
 	public void set(Iterator<ZSToken> tokens) {
56 93
 		lines.clear();
57 94
 		lines.add(new TokenLine());
@@ -64,6 +101,7 @@ public class TokenModel {
64 101
 		merge(line, lineIndex, getLine(lineIndex + 1));
65 102
 		lines.remove(lineIndex + 1);
66 103
 		
104
+		version++;
67 105
 		listeners.accept(listener -> listener.onLineDeleted(lineIndex + 1));
68 106
 	}
69 107
 	
@@ -75,48 +113,67 @@ public class TokenModel {
75 113
 			if (tokenOffset + token.content.length() > offset) {
76 114
 				if (token.content.length() == 1) {
77 115
 					line.remove(i);
78
-					i--; // make sure previous token is reparsed too
116
+					if (i > 0)
117
+						i--; // make sure previous token is reparsed too
79 118
 				} else {
80 119
 					token = token.delete(offset - tokenOffset, 1);
81 120
 					line.replace(i, token);
82 121
 				}
83 122
 
84
-				reparse(lineIndex, i, lineIndex, i + 1);
123
+				relex(lineIndex, i, lineIndex, i + 1);
85 124
 				return;
86 125
 			}
87 126
 			tokenOffset += token.content.length();
88 127
 		}
89 128
 	}
90 129
 	
130
+	public void delete(int fromLineIndex, int fromOffset, int toLineIndex, int toOffset) {
131
+		Position from = getPosition(fromLineIndex, fromOffset);
132
+		Position to = getPosition(toLineIndex, toOffset);
133
+		
134
+		ZSToken fromToken = getTokenAt(from);
135
+		ZSToken toToken = getTokenAt(to);
136
+		
137
+		String remainder = "";
138
+		if (fromToken != null)
139
+			remainder = fromToken.content.substring(0, from.offset);
140
+		if (toToken != null && to.offset > 0)
141
+			remainder += toToken.content.substring(to.offset);
142
+		
143
+		removeTokens(from.line, from.token, to.line, to.offset > 0 ? to.token + 1 : to.token);
144
+		if (!remainder.isEmpty()) {
145
+			getLine(from.line).insert(from.token, new ZSToken(ZSTokenType.INVALID, remainder));
146
+			relex(from.line, Math.max(0, from.token - 1), from.line, from.token + 1);
147
+		}
148
+	}
149
+	
91 150
 	public void insert(int lineIndex, int offset, String value) {
92 151
 		TokenLine line = lines.get(lineIndex);
93 152
 		int tokenOffset = 0;
94
-		if (line.isEmpty()) {
95
-			line.add(new ZSToken(ZSTokenType.INVALID, value));
96
-			reparse(lineIndex, 0, lineIndex, 1);
97
-			return;
98
-		}
99
-
100 153
 		for (int i = 0; i < line.getTokenCount(); i++) {
101 154
 			ZSToken token = line.getToken(i);
102 155
 			if (tokenOffset + token.content.length() > offset) {
103 156
 				token = token.insert(offset - tokenOffset, value);
104 157
 				line.replace(i, token);
105
-				reparse(lineIndex, i, lineIndex, i + 1);
158
+				relex(lineIndex, i, lineIndex, i + 1);
106 159
 				return;
107 160
 			}
108 161
 			tokenOffset += token.content.length();
109 162
 		}
110 163
 
111 164
 		ZSToken token = line.getLastToken();
112
-		token = new ZSToken(token.type, token.content + value);
113
-		line.replace(line.getTokenCount() - 1, token);
114
-		reparse(lineIndex, line.getTokenCount() - 1, lineIndex, line.getTokenCount());
165
+		if (token == null) {
166
+			line.add(new ZSToken(ZSTokenType.INVALID, value));
167
+		} else {
168
+			token = new ZSToken(token.type, token.content + value);
169
+			line.replace(line.getTokenCount() - 1, token);
170
+		}
171
+		relex(lineIndex, line.getTokenCount() - 1, lineIndex, line.getTokenCount());
115 172
 	}
116 173
 	
117
-	private void reparse(int fromLine, int fromToken, int toLine, int toToken) {
118
-		TokenReparser reparser = new TokenReparser(filename, lines, fromLine, fromToken, toLine, toToken, spacesPerTab);
119
-		List<ZSToken> tokens = reparser.reparse();
174
+	private void relex(int fromLine, int fromToken, int toLine, int toToken) {
175
+		TokenRelexer reparser = new TokenRelexer(filename, lines, fromLine, fromToken, toLine, toToken, spacesPerTab);
176
+		List<ZSToken> tokens = reparser.relex();
120 177
 		replaceTokens(fromLine, fromToken, reparser.getLine(), reparser.getToken(), tokens);
121 178
 	}
122 179
 	
@@ -130,19 +187,22 @@ public class TokenModel {
130 187
 			TokenLine fromLineObject = lines.get(fromLine);
131 188
 			fromLineObject.removeRange(fromToken, fromLineObject.getTokenCount());
132 189
 			
133
-			listeners.accept(listener -> listener.onLineChanged(fromLine));
134
-			
135 190
 			TokenLine toLineObject = lines.get(toLine);
136 191
 			for (int i = toToken - 1; i >= 0; i--)
137 192
 				toLineObject.remove(i);
138 193
 			
139
-			listeners.accept(listener -> listener.onLineChanged(toLine));
140
-			
141 194
 			merge(lines.get(fromLine), fromLine, lines.remove(toLine));
195
+			
196
+			version++;
197
+			listeners.accept(listener -> listener.onLineChanged(fromLine));
198
+			listeners.accept(listener -> listener.onLineDeleted(toLine));
199
+			
142 200
 			for (int i = toLine - 1; i > fromLine; i--) {
143 201
 				lines.remove(i);
144 202
 				
145 203
 				int ix = i;
204
+				
205
+				version++;
146 206
 				listeners.accept(listener -> listener.onLineDeleted(ix));
147 207
 			}
148 208
 		} else {
@@ -150,6 +210,7 @@ public class TokenModel {
150 210
 			for (int i = toToken - 1; i >= fromToken; i--)
151 211
 				line.remove(i);
152 212
 			
213
+			version++;
153 214
 			listeners.accept(listener -> listener.onLineChanged(fromLine));
154 215
 		}
155 216
 	}
@@ -195,6 +256,7 @@ public class TokenModel {
195 256
 			}
196 257
 		}
197 258
 		
259
+		version++;
198 260
 		listeners.accept(listener -> {
199 261
 			for (Integer inserted : insertedLines)
200 262
 				listener.onLineInserted(inserted);
@@ -228,7 +290,19 @@ public class TokenModel {
228 290
 		int fromToken = line.getTokenCount() - 1;
229 291
 		int toToken = !other.isEmpty() ? fromToken + 2 : fromToken + 1;
230 292
 		line.addAll(other.getTokens());
231
-		reparse(lineIndex, fromToken, lineIndex, toToken);
293
+		relex(lineIndex, fromToken, lineIndex, toToken);
294
+	}
295
+	
296
+	public static class Position {
297
+		public final int line;
298
+		public final int token;
299
+		public final int offset;
300
+		
301
+		public Position(int line, int token, int offset) {
302
+			this.line = line;
303
+			this.token = token;
304
+			this.offset = offset;
305
+		}
232 306
 	}
233 307
 	
234 308
 	public interface Listener {

IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/TokenReparser.java → IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/TokenRelexer.java Voir le fichier

@@ -18,7 +18,7 @@ import org.openzen.zenscript.lexer.ZSTokenType;
18 18
  *
19 19
  * @author Hoofdgebruiker
20 20
  */
21
-public class TokenReparser {
21
+public class TokenRelexer {
22 22
 	private final List<TokenLine> lines;
23 23
 	private final String filename;
24 24
 	private final int toLine;
@@ -28,7 +28,7 @@ public class TokenReparser {
28 28
 	private int lineIndex;
29 29
 	private int token;
30 30
 	
31
-	public TokenReparser(String filename, List<TokenLine> lines, int fromLine, int fromToken, int toLine, int toToken, int spacesPerTab) {
31
+	public TokenRelexer(String filename, List<TokenLine> lines, int fromLine, int fromToken, int toLine, int toToken, int spacesPerTab) {
32 32
 		if (fromToken < 0 || toToken < 0)
33 33
 			throw new IllegalArgumentException("fromToken or toToken cannot be < 0");
34 34
 		
@@ -42,8 +42,8 @@ public class TokenReparser {
42 42
 		this.spacesPerTab = spacesPerTab;
43 43
 	}
44 44
 	
45
-	public List<ZSToken> reparse() {
46
-		ReparseCharReader reader = new ReparseCharReader();
45
+	public List<ZSToken> relex() {
46
+		RelexCharReader reader = new RelexCharReader();
47 47
 		TokenParser<ZSToken, ZSTokenType> reparser = ZSTokenParser.createRaw(filename, reader, spacesPerTab);
48 48
 		List<ZSToken> result = new ArrayList<>();
49 49
 		while ((lineIndex < toLine || token < toToken || !reader.isAtTokenBoundary()) && reparser.hasNext())
@@ -79,11 +79,11 @@ public class TokenReparser {
79 79
 		return token;
80 80
 	}
81 81
 	
82
-	private class ReparseCharReader implements CharReader {
82
+	private class RelexCharReader implements CharReader {
83 83
 		private String token;
84 84
 		private int tokenOffset;
85 85
 		
86
-		public ReparseCharReader() {
86
+		public RelexCharReader() {
87 87
 			token = get();
88 88
 		}
89 89
 		

+ 0
- 4
Parser/src/main/java/org/openzen/zenscript/lexer/ZSToken.java Voir le fichier

@@ -5,10 +5,6 @@
5 5
  */
6 6
 package org.openzen.zenscript.lexer;
7 7
 
8
-import java.util.HashMap;
9
-import java.util.Map;
10
-import org.openzen.zenscript.shared.StringUtils;
11
-
12 8
 /**
13 9
  *
14 10
  * @author Hoofdgebruiker

Loading…
Annuler
Enregistrer