Gridarta Editor
TokenMarker.java
Go to the documentation of this file.
1 /*
2  * TokenMarker.java - Generic token marker
3  * Copyright (C) 1998, 1999 Slava Pestov
4  * Copyright (C) 2000-2015 The Gridarta Developers.
5  *
6  * You may use and modify this package for any purpose. Redistribution is
7  * permitted, in both source and binary form, provided that this notice
8  * remains intact in all source distributions of this package.
9  */
10 
11 package net.sf.gridarta.textedit.textarea.tokenmarker;
12 
13 import java.util.LinkedList;
14 import java.util.List;
15 import javax.swing.text.Segment;
17 import org.jetbrains.annotations.Nullable;
18 
31 public abstract class TokenMarker {
32 
36  private final List<Token> tokens = new LinkedList<>();
37 
43  private LineInfo[] lineInfo;
44 
49  private int length;
50 
54  private int lastLine;
55 
59  private boolean nextLineRequested;
60 
68  public List<Token> markTokens(final Segment line, final int lineIndex) {
69  if (lineIndex >= length) {
70  throw new IllegalArgumentException("Tokenizing invalid line: " + lineIndex);
71  }
72 
73  tokens.clear();
74 
75  final LineInfo info = lineInfo[lineIndex];
76  @Nullable final LineInfo prev;
77  if (lineIndex == 0) {
78  prev = null;
79  } else {
80  prev = lineInfo[lineIndex - 1];
81  }
82 
83  final byte oldToken = info.getToken();
84  final byte token = markTokensImpl(prev == null ? Token.NULL : prev.getToken(), line);
85 
86  info.setToken(token);
87 
88  /*
89  * This is a foul hack. It stops nextLineRequested
90  * from being cleared if the same line is marked twice.
91  *
92  * Why is this necessary? It's all JEditTextArea's fault.
93  * When something is inserted into the text, firing a
94  * document event, the insertUpdate() method shifts the
95  * caret (if necessary) by the amount inserted.
96  *
97  * All caret movement is handled by the select() method,
98  * which eventually pipes the new position to scrollTo()
99  * and calls repaint().
100  *
101  * Note that at this point in time, the new line hasn't
102  * yet been painted; the caret is moved first.
103  *
104  * scrollTo() calls offsetToX(), which tokenizes the line
105  * unless it is being called on the last line painted
106  * (in which case it uses the text area's painter cached
107  * token list). What scrollTo() does next is irrelevant.
108  *
109  * After scrollTo() has done it's job, repaint() is
110  * called, and eventually we end up in paintLine(), whose
111  * job is to paint the changed line. It, too, calls
112  * markTokens().
113  *
114  * The problem was that if the line started a multi.line
115  * token, the first markTokens() (done in offsetToX())
116  * would set nextLineRequested (because the line end
117  * token had changed) but the second would clear it
118  * (because the line was the same that time) and therefore
119  * paintLine() would never know that it needed to repaint
120  * subsequent lines.
121  *
122  * This bug took me ages to track down, that's why I wrote
123  * all the relevant info down so that others wouldn't
124  * duplicate it.
125  */
126  if (!(lastLine == lineIndex && nextLineRequested)) {
127  nextLineRequested = oldToken != token;
128  }
129 
130  lastLine = lineIndex;
131 
132  return tokens;
133  }
134 
147  protected abstract byte markTokensImpl(byte token, Segment line);
148 
155  public void insertLines(final int index, final int lines) {
156  if (lines <= 0) {
157  return;
158  }
159  length += lines;
160  ensureCapacity(length);
161  final int len = index + lines;
162  System.arraycopy(lineInfo, index, lineInfo, len, lineInfo.length - len);
163 
164  for (int i = index + lines - 1; i >= index; i--) {
165  lineInfo[i] = new LineInfo();
166  }
167  }
168 
175  public void deleteLines(final int index, final int lines) {
176  if (lines <= 0) {
177  return;
178  }
179  final int len = index + lines;
180  length -= lines;
181  System.arraycopy(lineInfo, len, lineInfo, index, lineInfo.length - len);
182  }
183 
189  public boolean isNextLineRequested() {
190  return nextLineRequested;
191  }
192 
197  protected TokenMarker() {
198  lastLine = -1;
199  }
200 
211  private void ensureCapacity(final int index) {
212  if (lineInfo == null) {
213  lineInfo = new LineInfo[index + 1];
214  } else if (lineInfo.length <= index) {
215  final LineInfo[] lineInfoN = new LineInfo[(index + 1) * 2];
216  System.arraycopy(lineInfo, 0, lineInfoN, 0, lineInfo.length);
217  lineInfo = lineInfoN;
218  }
219  }
220 
226  protected void addToken(final int length, final byte id) {
227  if (id >= Token.INTERNAL_FIRST && id <= Token.INTERNAL_LAST) {
228  throw new InternalError("Invalid id: " + id);
229  }
230 
231  if (length == 0) {
232  return;
233  }
234 
235  tokens.add(new Token(length, id));
236  }
237 
238 }
int length
The number of lines in the model being tokenized.
void insertLines(final int index, final int lines)
Informs the token marker that lines have been inserted into the document.
void deleteLines(final int index, final int lines)
Informs the token marker that line have been deleted from the document.
static final byte INTERNAL_FIRST
The first id that can be used for internal state in a token marker.
Definition: Token.java:94
Stores information about tokenized lines.
Definition: LineInfo.java:27
final List< Token > tokens
The collected tokens.
boolean nextLineRequested
True if the next line should be painted.
void addToken(final int length, final byte id)
Adds a token to the token list.
Base package of all Gridarta classes.
void ensureCapacity(final int index)
Ensures that the.
LineInfo [] lineInfo
An array for storing information about lines.
A linked list of tokens.
Definition: Token.java:21
abstract byte markTokensImpl(byte token, Segment line)
An abstract method that splits a line up into tokens.
This package contains the other part of the script editor.
List< Token > markTokens(final Segment line, final int lineIndex)
A wrapper for the lower-level.
static final byte NULL
Normal text token id.
Definition: Token.java:26
A token marker that splits lines of text into tokens.
static final byte INTERNAL_LAST
The last id that can be used for internal state in a token marker.
Definition: Token.java:99
boolean isNextLineRequested()
Returns true if the next line should be repainted.