Browse Source

- Fixed various bugs in the source editor

- Added ability to remove a selected block of text
Stan Hebben 6 years ago
parent
commit
cccd95ba21

+ 2
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DDrawingContext.java View File

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

+ 17
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/DTimerHandle.java View File

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 View File

14
 import org.openzen.drawablegui.DFontMetrics;
14
 import org.openzen.drawablegui.DFontMetrics;
15
 import org.openzen.drawablegui.DIRectangle;
15
 import org.openzen.drawablegui.DIRectangle;
16
 import org.openzen.drawablegui.DMouseEvent;
16
 import org.openzen.drawablegui.DMouseEvent;
17
+import org.openzen.drawablegui.DTimerHandle;
17
 import org.openzen.drawablegui.listeners.ListenerHandle;
18
 import org.openzen.drawablegui.listeners.ListenerHandle;
18
 import org.openzen.drawablegui.live.LiveInt;
19
 import org.openzen.drawablegui.live.LiveInt;
19
 import org.openzen.drawablegui.live.LiveObject;
20
 import org.openzen.drawablegui.live.LiveObject;
248
 			if (y + height > offsetY.getValue() + bounds.height)
249
 			if (y + height > offsetY.getValue() + bounds.height)
249
 				offsetY.setValue(y + height - bounds.height);
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
 	private class ScrollListener implements LiveInt.Listener {
259
 	private class ScrollListener implements LiveInt.Listener {

+ 9
- 0
DrawableGui/src/main/java/org/openzen/drawablegui/swing/SwingGraphicsContext.java View File

9
 import java.awt.Graphics;
9
 import java.awt.Graphics;
10
 import java.awt.geom.GeneralPath;
10
 import java.awt.geom.GeneralPath;
11
 import java.util.WeakHashMap;
11
 import java.util.WeakHashMap;
12
+import javax.swing.Timer;
12
 import org.openzen.drawablegui.DComponent;
13
 import org.openzen.drawablegui.DComponent;
13
 import org.openzen.drawablegui.DPath;
14
 import org.openzen.drawablegui.DPath;
14
 import org.openzen.drawablegui.DDrawingContext;
15
 import org.openzen.drawablegui.DDrawingContext;
15
 import org.openzen.drawablegui.DFont;
16
 import org.openzen.drawablegui.DFont;
16
 import org.openzen.drawablegui.DFontMetrics;
17
 import org.openzen.drawablegui.DFontMetrics;
17
 import org.openzen.drawablegui.DPathTracer;
18
 import org.openzen.drawablegui.DPathTracer;
19
+import org.openzen.drawablegui.DTimerHandle;
18
 
20
 
19
 /**
21
 /**
20
  *
22
  *
101
 	public void scrollInView(int x, int y, int width, int height) {
103
 	public void scrollInView(int x, int y, int width, int height) {
102
 		// not in a scrollable context
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
 	private class PathTracer implements DPathTracer {
114
 	private class PathTracer implements DPathTracer {
106
 		private final GeneralPath path;
115
 		private final GeneralPath path;

+ 72
- 117
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/SourceEditor.java View File

6
 package org.openzen.zenscript.ide.ui.view.editor;
6
 package org.openzen.zenscript.ide.ui.view.editor;
7
 
7
 
8
 import java.io.IOException;
8
 import java.io.IOException;
9
-import java.util.Timer;
10
-import java.util.TimerTask;
11
 import org.openzen.drawablegui.DCanvas;
9
 import org.openzen.drawablegui.DCanvas;
12
 import org.openzen.drawablegui.DComponent;
10
 import org.openzen.drawablegui.DComponent;
13
 import org.openzen.drawablegui.DDimensionPreferences;
11
 import org.openzen.drawablegui.DDimensionPreferences;
52
 	private int selectionLineHeight;
50
 	private int selectionLineHeight;
53
 	
51
 	
54
 	private int lineBarWidth;
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
 	private boolean cursorBlink = true;
56
 	private boolean cursorBlink = true;
60
 	
57
 	
61
 	private int mouseDownX = -1;
58
 	private int mouseDownX = -1;
67
 		tokens = new TokenModel(sourceFile.getName(), tab.length());
64
 		tokens = new TokenModel(sourceFile.getName(), tab.length());
68
 		tokenListener = tokens.addListener(new TokenListener());
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
 		try {
67
 		try {
78
 			TokenParser<ZSToken, ZSTokenType> parser = ZSTokenParser.createRaw(sourceFile.getName(), new ReaderCharReader(sourceFile.read()), tab.length());
68
 			TokenParser<ZSToken, ZSTokenType> parser = ZSTokenParser.createRaw(sourceFile.getName(), new ReaderCharReader(sourceFile.read()), tab.length());
79
 			tokens.set(parser);
69
 			tokens.set(parser);
91
 		selectionLineHeight = textLineHeight + style.selectionPaddingTop + style.selectionPaddingBottom;
81
 		selectionLineHeight = textLineHeight + style.selectionPaddingTop + style.selectionPaddingBottom;
92
 		
82
 		
93
 		dimensionPreferences.setValue(new DDimensionPreferences(0, fullLineHeight * tokens.getLineCount()));
83
 		dimensionPreferences.setValue(new DDimensionPreferences(0, fullLineHeight * tokens.getLineCount()));
84
+		context.setTimer(300, this::blink);
94
 	}
85
 	}
95
 
86
 
96
 	@Override
87
 	@Override
126
 		
117
 		
127
 		if (cursorStart != null && !cursorStart.equals(cursorEnd)) {
118
 		if (cursorStart != null && !cursorStart.equals(cursorEnd)) {
128
 			if (cursorStart.line == cursorEnd.line) {
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
 				int fromX = Math.min(x1, x2);
123
 				int fromX = Math.min(x1, x2);
133
 				int toX = Math.max(x1, x2);
124
 				int toX = Math.max(x1, x2);
134
 				canvas.fillRectangle(fromX, y, toX - fromX, selectionLineHeight, style.selectionColor);
125
 				canvas.fillRectangle(fromX, y, toX - fromX, selectionLineHeight, style.selectionColor);
135
 			} else {
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
 				for (int i = from.line + 1; i < to.line; i++) {
133
 				for (int i = from.line + 1; i < to.line; i++) {
143
 					canvas.fillRectangle(x, lineToY(i), bounds.width - x, selectionLineHeight, style.selectionColor);
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
 		}
160
 		}
170
 		
161
 		
171
 		if (cursorEnd != null && cursorBlink) {
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
 			canvas.fillRectangle(cursorX, cursorY, 2, selectionLineHeight, 0xFF000000);
165
 			canvas.fillRectangle(cursorX, cursorY, 2, selectionLineHeight, 0xFF000000);
175
 		}
166
 		}
176
 	}
167
 	}
189
 	public void onMouseClick(DMouseEvent e) {
180
 	public void onMouseClick(DMouseEvent e) {
190
 		context.focus(this);
181
 		context.focus(this);
191
 		
182
 		
192
-		CursorPosition position = getPositionAt(e.x, e.y);
183
+		SourcePosition position = getPositionAt(e.x, e.y);
193
 		if (e.isDoubleClick()) {
184
 		if (e.isDoubleClick()) {
194
 			// select entire word
185
 			// select entire word
195
-			TokenPosition token = getTokenAt(position);
186
+			TokenModel.Position tokenPosition = tokens.getPosition(position.line, position.offset);
196
 			setCursor(
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
 		} else if (e.isTripleClick()) {
190
 		} else if (e.isTripleClick()) {
200
 			setCursor(
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
 		} else {
194
 		} else {
204
 			setCursor(position, position);
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
 		if (cursorStart != null)
200
 		if (cursorStart != null)
210
 			repaint(cursorStart, cursorEnd);
201
 			repaint(cursorStart, cursorEnd);
211
 		
202
 		
231
 	
222
 	
232
 	@Override
223
 	@Override
233
 	public void onMouseDrag(DMouseEvent e) {
224
 	public void onMouseDrag(DMouseEvent e) {
234
-		CursorPosition start = cursorStart;
225
+		SourcePosition start = cursorStart;
235
 		if (!dragging)
226
 		if (!dragging)
236
 			start = getPositionAt(mouseDownX, mouseDownY);
227
 			start = getPositionAt(mouseDownX, mouseDownY);
237
 		
228
 		
248
 				
239
 				
249
 				{
240
 				{
250
 					int line = cursorEnd.line - 1;
241
 					int line = cursorEnd.line - 1;
251
-					CursorPosition position = new CursorPosition(
242
+					SourcePosition position = new SourcePosition(
243
+							tokens,
252
 							line,
244
 							line,
253
 							Math.min(tokens.getLineLength(line), cursorEnd.offset));
245
 							Math.min(tokens.getLineLength(line), cursorEnd.offset));
254
 					setCursor(shift ? cursorStart : position, position);
246
 					setCursor(shift ? cursorStart : position, position);
260
 				
252
 				
261
 				{
253
 				{
262
 					int line = cursorEnd.line + 1;
254
 					int line = cursorEnd.line + 1;
263
-					CursorPosition position = new CursorPosition(
255
+					SourcePosition position = new SourcePosition(
256
+							tokens,
264
 							line,
257
 							line,
265
 							Math.min(tokens.getLineLength(line), cursorEnd.offset));
258
 							Math.min(tokens.getLineLength(line), cursorEnd.offset));
266
 					setCursor(shift ? cursorStart : position, position);
259
 					setCursor(shift ? cursorStart : position, position);
271
 					return;
264
 					return;
272
 				
265
 				
273
 				{
266
 				{
274
-					CursorPosition position;
267
+					SourcePosition position;
275
 					if (cursorEnd.offset == 0) {
268
 					if (cursorEnd.offset == 0) {
276
 						int line = cursorEnd.line - 1;
269
 						int line = cursorEnd.line - 1;
277
-						position = new CursorPosition(line, tokens.getLineLength(line));
270
+						position = new SourcePosition(tokens, line, tokens.getLineLength(line));
278
 					} else {
271
 					} else {
279
-						position = new CursorPosition(cursorEnd.line, cursorEnd.offset - 1);
272
+						position = new SourcePosition(tokens, cursorEnd.line, cursorEnd.offset - 1);
280
 					}
273
 					}
281
 					setCursor(shift ? cursorStart : position, position);
274
 					setCursor(shift ? cursorStart : position, position);
282
 				}
275
 				}
286
 					return;
279
 					return;
287
 				
280
 				
288
 				{
281
 				{
289
-					CursorPosition position;
282
+					SourcePosition position;
290
 					if (cursorEnd.offset == tokens.getLineLength(cursorEnd.line)) {
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
 					} else {
285
 					} else {
293
-						position = new CursorPosition(cursorEnd.line, cursorEnd.offset + 1);
286
+						position = new SourcePosition(tokens, cursorEnd.line, cursorEnd.offset + 1);
294
 					}
287
 					}
295
 					setCursor(shift ? cursorStart : position, position);
288
 					setCursor(shift ? cursorStart : position, position);
296
 				}
289
 				}
344
 		}
337
 		}
345
 		
338
 		
346
 		tokens.deleteCharacter(cursorEnd.line, cursorEnd.offset);
339
 		tokens.deleteCharacter(cursorEnd.line, cursorEnd.offset);
347
-		repaintLine(cursorEnd.line);
348
 	}
340
 	}
349
 	
341
 	
350
 	private void backspace() {
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
 		if (cursorEnd.offset == 0) {
351
 		if (cursorEnd.offset == 0) {
352
 			if (cursorEnd.line == 0)
352
 			if (cursorEnd.line == 0)
353
 				return;
353
 				return;
355
 			int length = tokens.getLineLength(cursorEnd.line - 1);
355
 			int length = tokens.getLineLength(cursorEnd.line - 1);
356
 			tokens.deleteNewline(cursorEnd.line - 1);
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
 			setCursor(position, position);
359
 			setCursor(position, position);
360
 			return;
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
 	private void type(String value) {
372
 	private void type(String value) {
369
 		tokens.insert(cursorEnd.line, cursorEnd.offset, value);
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
 		setCursor(position, position);
375
 		setCursor(position, position);
372
 	}
376
 	}
373
 	
377
 	
374
 	private void newline() {
378
 	private void newline() {
375
 		String indent = tokens.getLine(cursorEnd.line).getIndent();
379
 		String indent = tokens.getLine(cursorEnd.line).getIndent();
376
 		tokens.insert(cursorEnd.line, cursorEnd.offset, "\n" + indent);
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
 		setCursor(position, position);
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
 		if (from.line == to.line) {
386
 		if (from.line == to.line) {
405
 			int y = lineToY(from.line);
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
 			context.repaint(fromX, y, toX - fromX, selectionLineHeight);
392
 			context.repaint(fromX, y, toX - fromX, selectionLineHeight);
409
 		} else {
393
 		} else {
410
 			int fromY = lineToY(Math.min(from.line, to.line));
394
 			int fromY = lineToY(Math.min(from.line, to.line));
420
 		context.repaint(bounds.x, lineToY(line), bounds.width, selectionLineHeight);
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
 	private void blink() {
411
 	private void blink() {
433
 		}
415
 		}
434
 	}
416
 	}
435
 	
417
 	
436
-	private CursorPosition getPositionAt(int x, int y) {
418
+	public SourcePosition getPositionAt(int x, int y) {
437
 		int line = yToLine(y);
419
 		int line = yToLine(y);
438
 		int offset = xToOffset(line, x);
420
 		int offset = xToOffset(line, x);
439
 		
421
 		
442
 		if (line >= tokens.getLineCount())
424
 		if (line >= tokens.getLineCount())
443
 			line = tokens.getLineCount() - 1;
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
 	private int xToOffset(int lineIndex, int x) {
430
 	private int xToOffset(int lineIndex, int x) {
493
 		return startY + line * fullLineHeight;
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
 		int x = bounds.x + lineBarWidth + 10;
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
 		return x;
488
 		return x;
516
 	}
489
 	}
517
 	
490
 	
491
+	public int getY(SourcePosition position) {
492
+		return lineToY(position.line);
493
+	}
494
+	
518
 	private String getDisplayContent(ZSToken token) {
495
 	private String getDisplayContent(ZSToken token) {
519
 		return token.type == ZSTokenType.T_WHITESPACE_TAB ? tab : token.content;
496
 		return token.type == ZSTokenType.T_WHITESPACE_TAB ? tab : token.content;
520
 	}
497
 	}
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 View File

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 View File

40
 	}
40
 	}
41
 	
41
 	
42
 	public ZSToken getLastToken() {
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
 	public String getIndent() {
46
 	public String getIndent() {

+ 94
- 20
IDE/src/main/java/org/openzen/zenscript/ide/ui/view/editor/TokenModel.java View File

26
 	private final String filename;
26
 	private final String filename;
27
 	private final int spacesPerTab;
27
 	private final int spacesPerTab;
28
 	private final List<TokenLine> lines = new ArrayList<>();
28
 	private final List<TokenLine> lines = new ArrayList<>();
29
+	private long version = 0;
29
 	
30
 	
30
 	public TokenModel(String filename, int spacesPerTab) {
31
 	public TokenModel(String filename, int spacesPerTab) {
31
 		this.filename = filename;
32
 		this.filename = filename;
52
 		return lines.get(line).length();
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
 	public void set(Iterator<ZSToken> tokens) {
92
 	public void set(Iterator<ZSToken> tokens) {
56
 		lines.clear();
93
 		lines.clear();
57
 		lines.add(new TokenLine());
94
 		lines.add(new TokenLine());
64
 		merge(line, lineIndex, getLine(lineIndex + 1));
101
 		merge(line, lineIndex, getLine(lineIndex + 1));
65
 		lines.remove(lineIndex + 1);
102
 		lines.remove(lineIndex + 1);
66
 		
103
 		
104
+		version++;
67
 		listeners.accept(listener -> listener.onLineDeleted(lineIndex + 1));
105
 		listeners.accept(listener -> listener.onLineDeleted(lineIndex + 1));
68
 	}
106
 	}
69
 	
107
 	
75
 			if (tokenOffset + token.content.length() > offset) {
113
 			if (tokenOffset + token.content.length() > offset) {
76
 				if (token.content.length() == 1) {
114
 				if (token.content.length() == 1) {
77
 					line.remove(i);
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
 				} else {
118
 				} else {
80
 					token = token.delete(offset - tokenOffset, 1);
119
 					token = token.delete(offset - tokenOffset, 1);
81
 					line.replace(i, token);
120
 					line.replace(i, token);
82
 				}
121
 				}
83
 
122
 
84
-				reparse(lineIndex, i, lineIndex, i + 1);
123
+				relex(lineIndex, i, lineIndex, i + 1);
85
 				return;
124
 				return;
86
 			}
125
 			}
87
 			tokenOffset += token.content.length();
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
 	public void insert(int lineIndex, int offset, String value) {
150
 	public void insert(int lineIndex, int offset, String value) {
92
 		TokenLine line = lines.get(lineIndex);
151
 		TokenLine line = lines.get(lineIndex);
93
 		int tokenOffset = 0;
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
 		for (int i = 0; i < line.getTokenCount(); i++) {
153
 		for (int i = 0; i < line.getTokenCount(); i++) {
101
 			ZSToken token = line.getToken(i);
154
 			ZSToken token = line.getToken(i);
102
 			if (tokenOffset + token.content.length() > offset) {
155
 			if (tokenOffset + token.content.length() > offset) {
103
 				token = token.insert(offset - tokenOffset, value);
156
 				token = token.insert(offset - tokenOffset, value);
104
 				line.replace(i, token);
157
 				line.replace(i, token);
105
-				reparse(lineIndex, i, lineIndex, i + 1);
158
+				relex(lineIndex, i, lineIndex, i + 1);
106
 				return;
159
 				return;
107
 			}
160
 			}
108
 			tokenOffset += token.content.length();
161
 			tokenOffset += token.content.length();
109
 		}
162
 		}
110
 
163
 
111
 		ZSToken token = line.getLastToken();
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
 		replaceTokens(fromLine, fromToken, reparser.getLine(), reparser.getToken(), tokens);
177
 		replaceTokens(fromLine, fromToken, reparser.getLine(), reparser.getToken(), tokens);
121
 	}
178
 	}
122
 	
179
 	
130
 			TokenLine fromLineObject = lines.get(fromLine);
187
 			TokenLine fromLineObject = lines.get(fromLine);
131
 			fromLineObject.removeRange(fromToken, fromLineObject.getTokenCount());
188
 			fromLineObject.removeRange(fromToken, fromLineObject.getTokenCount());
132
 			
189
 			
133
-			listeners.accept(listener -> listener.onLineChanged(fromLine));
134
-			
135
 			TokenLine toLineObject = lines.get(toLine);
190
 			TokenLine toLineObject = lines.get(toLine);
136
 			for (int i = toToken - 1; i >= 0; i--)
191
 			for (int i = toToken - 1; i >= 0; i--)
137
 				toLineObject.remove(i);
192
 				toLineObject.remove(i);
138
 			
193
 			
139
-			listeners.accept(listener -> listener.onLineChanged(toLine));
140
-			
141
 			merge(lines.get(fromLine), fromLine, lines.remove(toLine));
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
 			for (int i = toLine - 1; i > fromLine; i--) {
200
 			for (int i = toLine - 1; i > fromLine; i--) {
143
 				lines.remove(i);
201
 				lines.remove(i);
144
 				
202
 				
145
 				int ix = i;
203
 				int ix = i;
204
+				
205
+				version++;
146
 				listeners.accept(listener -> listener.onLineDeleted(ix));
206
 				listeners.accept(listener -> listener.onLineDeleted(ix));
147
 			}
207
 			}
148
 		} else {
208
 		} else {
150
 			for (int i = toToken - 1; i >= fromToken; i--)
210
 			for (int i = toToken - 1; i >= fromToken; i--)
151
 				line.remove(i);
211
 				line.remove(i);
152
 			
212
 			
213
+			version++;
153
 			listeners.accept(listener -> listener.onLineChanged(fromLine));
214
 			listeners.accept(listener -> listener.onLineChanged(fromLine));
154
 		}
215
 		}
155
 	}
216
 	}
195
 			}
256
 			}
196
 		}
257
 		}
197
 		
258
 		
259
+		version++;
198
 		listeners.accept(listener -> {
260
 		listeners.accept(listener -> {
199
 			for (Integer inserted : insertedLines)
261
 			for (Integer inserted : insertedLines)
200
 				listener.onLineInserted(inserted);
262
 				listener.onLineInserted(inserted);
228
 		int fromToken = line.getTokenCount() - 1;
290
 		int fromToken = line.getTokenCount() - 1;
229
 		int toToken = !other.isEmpty() ? fromToken + 2 : fromToken + 1;
291
 		int toToken = !other.isEmpty() ? fromToken + 2 : fromToken + 1;
230
 		line.addAll(other.getTokens());
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
 	public interface Listener {
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 View File

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

+ 0
- 4
Parser/src/main/java/org/openzen/zenscript/lexer/ZSToken.java View File

5
  */
5
  */
6
 package org.openzen.zenscript.lexer;
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
  * @author Hoofdgebruiker
10
  * @author Hoofdgebruiker

Loading…
Cancel
Save