📄 rsyntaxdocument.java
字号:
/*
* 10/16/2004
*
* RSyntaxDocument.java - A document capable of syntax highlighting, used by
* RSyntaxTextArea.
* Copyright (C) 2004 Robert Futrell
* email@address.com
* www.website.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.event.*;
import javax.swing.text.*;
import org.fife.ui.rsyntaxtextarea.modes.*;
import org.fife.ui.rtextarea.RTextAreaDocument;
import org.fife.util.DynamicIntArray;
/**
* The document used by {@link org.fife.ui.rsyntaxtextarea.RSyntaxTextArea}.
* This document is like <code>javax.swing.text.PlainDocument</code> except that
* it also keeps track of syntax highlighting in the document. It has a "style"
* attribute associated with it that determines how syntax highlighting is done
* (i.e., what language is being highlighted).<p>
*
* Instances of <code>RSyntaxTextArea</code> will only accept instances of
* <code>RSyntaxDocument</code>, since it is this document that keeps
* track of syntax highlighting. All others will cause an exception to be
* thrown.<p>
*
* To change the language being syntax highlighted at any time, you merely have
* to call {@link #setSyntaxStyle}. Other than that, this document can be
* treated like any other save one caveat: all <code>DocumentEvent</code>s of
* type <code>CHANGE</code> use their offset and length values to represent the
* first and last lines, respectively, that have had their syntax coloring
* change. This is really a hack to increase the speed of the painting code
* and should really be corrected, but oh well.
*
* @author Robert Futrell
* @version 0.1
*/
public class RSyntaxDocument extends RTextAreaDocument
implements SyntaxConstants {
/**
*
*/
private static final long serialVersionUID = -6255404347412937685L;
private TokenMaker tokenMaker;
/**
* Array of values representing the "last token type" on each line. This
* is used in cases such as multiline comments: if the previous line
* ended with an (unclosed) multiline comment, we can use this knowledge
* and start the current line's syntax highlighting in multiline comment
* state.
*/
protected DynamicIntArray lastTokensOnLines;
private transient Segment s;
/*****************************************************************************/
/**
* Constructs a plain text document. A default root element is created,
* and the tab size set to 5.
*
* @param syntaxStyle The syntax highlighting scheme to use.
*/
public RSyntaxDocument(int syntaxStyle) {
super(new GapContent());
putProperty(tabSizeAttribute, new Integer(5));
lastTokensOnLines = new DynamicIntArray(500);
lastTokensOnLines.add(Token.NULL); // Initial (empty) line.
s = new Segment();
setSyntaxStyle(syntaxStyle);
}
/*****************************************************************************/
/**
* Creates a token element. Line elements contain these, representing
* syntax-highlighting tokens.
*/
protected Element createTokenElement(Element parent, int p0,int p1,
int type) {
return new TokenElement(parent, p0, p1, type);
}
/*****************************************************************************/
/**
* Alerts all listeners to this document of an insertion. This is
* overridden so we can update our syntax highlighting stuff.<p>
* The syntax highlighting stuff has to be here instead of in
* <code>insertUpdate</code> because <code>insertUpdate</code> is not
* called by the undo/redo actions, but this method is.
*
* @param e The change.
*/
protected void fireInsertUpdate(DocumentEvent e) {
/*
* Now that the text is actually inserted into the content and
* element structure, we can update our token elements and "last
* tokens on lines" structure.
*/
Element lineMap = getDefaultRootElement();
DocumentEvent.ElementChange change = e.getChange(lineMap);
Element[] added = change==null ? null : change.getChildrenAdded();
int numLines = lineMap.getElementCount();
int line = lineMap.getElementIndex(e.getOffset());
int previousLine = line - 1;
int previousTokenType = (previousLine>-1 ?
lastTokensOnLines.get(previousLine) : Token.NULL);
// If entire lines were added...
if (added!=null && added.length>0) {
Element[] removed = change.getChildrenRemoved();
int numRemoved = removed!=null ? removed.length : 0;
int endBefore = line + added.length - numRemoved;
//System.err.println("... adding lines: " + line + " - " + (endBefore-1));
//System.err.println("... ... added: " + added.length + ", removed:" + numRemoved);
for (int i=line; i<endBefore; i++) {
setSharedSegment(i); // Loads line i's text into s.
int tokenType = tokenMaker.getLastTokenTypeOnLine(s, previousTokenType);
lastTokensOnLines.add(i, tokenType);
//System.err.println("--------- lastTokensOnLines.size() == " + lastTokensOnLines.getSize());
previousTokenType = tokenType;
} // End of for (int i=line; i<endBefore; i++).
// Update last tokens for lines below until they stop changing.
updateLastTokensBelow(endBefore, numLines, previousTokenType);
} // End of if (added!=null && added.length>0).
// Otherwise, text was inserted on a single line...
else {
// Update last tokens for lines below until they stop changing.
updateLastTokensBelow(line, numLines, previousTokenType);
} // End of else.
// Let all listeners know about the insertion.
super.fireInsertUpdate(e);
}
/*****************************************************************************/
/**
* This method is called AFTER the content has been inserted into the
* document and the element structure has been updated.<p>
* The syntax-highlighting updates need to be done here (as opposed to
* an override of <code>postRemoveUpdate</code>) as this method is called
* in response to undo/redo events, whereas <code>postRemoveUpdate</code>
* is not.<p>
* Now that the text is actually inserted into the content and element
* structure, we can update our token elements and "last tokens on
* lines" structure.
*
* @param chng The change that occured.
* @see #removeUpdate
*/
protected void fireRemoveUpdate(DocumentEvent chng) {
Element lineMap = getDefaultRootElement();
int numLines = lineMap.getElementCount();
DocumentEvent.ElementChange change = chng.getChange(lineMap);
Element[] removed = change==null ? null : change.getChildrenRemoved();
// If entire lines were removed...
if (removed!=null && removed.length>0) {
int line = change.getIndex(); // First line entirely removed.
int previousLine = line - 1; // Line before that.
int previousTokenType = (previousLine>-1 ? lastTokensOnLines.get(previousLine) : Token.NULL);
Element[] added = change.getChildrenAdded();
int numAdded = added==null ? 0 : added.length;
// Remove the cached last-token values for the removed lines.
int endBefore = line + removed.length - numAdded;
//System.err.println("... removing lines: " + line + " - " + (endBefore-1));
//System.err.println("... added: " + numAdded + ", removed: " + removed.length);
lastTokensOnLines.removeRange(line, endBefore); // Removing values for lines [line-(endBefore-1)].
//System.err.println("--------- lastTokensOnLines.size() == " + lastTokensOnLines.getSize());
// Update last tokens for lines below until they've stopped changing.
updateLastTokensBelow(line, numLines, previousTokenType);
} // End of if (removed!=null && removed.size()>0).
// Otherwise, text was removed from just one line...
else {
int line = lineMap.getElementIndex(chng.getOffset());
if (line>=lastTokensOnLines.getSize())
return; // If we're editing the last line in a document...
int previousLine = line - 1;
int previousTokenType = (previousLine>-1 ? lastTokensOnLines.get(previousLine) : Token.NULL);
//System.err.println("previousTokenType for line : " + previousLine + " is " + previousTokenType);
// Update last tokens for lines below until they've stopped changing.
updateLastTokensBelow(line, numLines, previousTokenType);
}
// Let all of our listeners know about the removal.
super.fireRemoveUpdate(chng);
}
/*****************************************************************************/
/**
* Returns the token type of the last token on the given line.
*
* @param line The line to inspect.
* @return The token type of the last token on the specified line. If
* the line is invalid, an exception is thrown.
*/
public int getLastTokenTypeOnLine(int line) {
return lastTokensOnLines.get(line);
}
/*****************************************************************************/
/**
* Returns a token list for the specified segment of text representing
* the specified line number. This method is basically a wrapper for
* <code>tokenMaker.getTokenList</code> that takes into account the last
* token on the previous line to assure token accuracy.
*
* @param line The line number of <code>text</code> in the document, >= 0.
* @return A token list representing the specified line.
*/
public final Token getTokenListForLine(int line) {
// FIXME: This is where we would use the "token elements" instead of calling
// getTokenList(). But for the time being, we'll do ig the old-fashioned way.
// When ready, remove the old version below, uncomment these lines of code, and
// implement the rest.
/*
Element map = getDefaultRootElement();
LineElement elem = (LineElement)map.getElement(line);
int startOffset = elem.getStartOffset();
int endOffset = elem.getEndOffset() - 1; // Why always "-1"?
try {
getText(startOffset,endOffset-startOffset, s);
} catch (BadLocationException ble) {
ble.printStackTrace();
return null;
}
Enumeration e = elem.children();
if (e!=null) {
Token tokenList = null;
Token t2 = null;
Token t3 = null;
while (e.hasMoreElements()) {
TokenElement te = (TokenElement)e.nextElement();
int so = te.getStartOffset();
int eo = te.getEndOffset();
System.err.println(so + "-" + eo + ", " + te.getType());
if (tokenList==null) {
tokenList = new DefaultToken(s, s.offset+(so-startOffset),
s.offset+(eo-startOffset),
so, te.getType());
t2 = tokenList;
}
else {
t3 = new DefaultToken(s, s.offset+(so-startOffset),
s.offset+(eo-startOffset),
so, te.getType());
t2.nextToken = t3;
t2 = t3;
}
}
return tokenList;
}
else
return new DefaultToken();
*/
Element map = getDefaultRootElement();
Element elem = map.getElement(line);
int startOffset = elem.getStartOffset();
//int endOffset = (line==map.getElementCount()-1 ? elem.getEndOffset() - 1:
// elem.getEndOffset() - 1);
int endOffset = elem.getEndOffset() - 1; // Why always "-1"?
try {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -