Added code folding and formatted all files

This commit is contained in:
SuspiciousActivity committed 2018-07-08 14:39:06 +02:00
1 parent 11570d6b95
commit 66cd1dbd3e
233 files changed
+82245 -118134

No files matched your search

@@ -1,10 +1,8 @@
/*
* 01/25/2009
*
* AbstractJFlexCTokenMaker.java - Base class for token makers that use curly
* braces to denote code blocks, such as C, C++, Java, Perl, etc.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -12,38 +10,34 @@ package org.fife.ui.rsyntaxtextarea;
import java.awt.event.ActionEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Action;
import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import org.fife.ui.rtextarea.RTextArea;
/**
* Base class for JFlex-based token makers using C-style syntax. This class
* Base class for JFlex-based token makers using C-style syntax. This class
* knows how to:
*
* <ul>
* <li>Auto-indent after opening braces and parens
* <li>Automatically close multi-line and documentation comments
* <li>Auto-indent after opening braces and parens
* <li>Automatically close multi-line and documentation comments
* </ul>
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
private final Action INSERT_BREAK_ACTION;
private static final Pattern MLC_PATTERN =
Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)");
private static final Pattern MLC_PATTERN = Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)");
protected AbstractJFlexCTokenMaker() {
INSERT_BREAK_ACTION = createInsertBreakAction();
}
/**
* Creates and returns the action to use when the user inserts a newline.
* The default implementation intelligently closes multi-line comments.
@@ -55,8 +49,7 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
protected Action createInsertBreakAction() {
return new CStyleInsertBreakAction();
}
/**
* Returns <code>true</code> always as C-style languages use curly braces
* to denote code blocks.
@@ -67,8 +60,7 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return true;
}
/**
* Returns an action to handle "insert break" key presses (i.e. Enter).
* An action is returned that handles newlines differently in multi-line
@@ -80,94 +72,81 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
public Action getInsertBreakAction() {
return INSERT_BREAK_ACTION;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getMarkOccurrencesOfTokenType(int type) {
return type==Token.IDENTIFIER || type==Token.FUNCTION;
return type == Token.IDENTIFIER || type == Token.FUNCTION;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getShouldIndentNextLineAfter(Token t) {
if (t!=null && t.length()==1) {
if (t != null && t.length() == 1) {
char ch = t.charAt(0);
return ch=='{' || ch=='(';
return ch == '{' || ch == '(';
}
return false;
}
/**
* Returns whether a given token is an internal token type that represents
* an MLC or documentation comment continuing on to the next line. This is
* an MLC or documentation comment continuing on to the next line. This is
* done by languages such as JavaScript that are a little more verbose
* than necessary so that their code can be copy-and-pasted into other
* <code>TokenMaker</code>s that use them as nested languages (such as
* HTML, JSP, etc.).
*
* @param t The token to check. This cannot be <code>null</code>.
* @param t
* The token to check. This cannot be <code>null</code>.
* @return Whether the token is an internal token representing the end of
* a line for an MLC/doc comment continuing on to the next line.
*/
private boolean isInternalEolTokenForMLCs(Token t) {
int type = t.getType();
if (type<0) {
if (type < 0) {
type = getClosestStandardTokenTypeForInternalType(type);
return type==TokenTypes.COMMENT_MULTILINE ||
type==TokenTypes.COMMENT_DOCUMENTATION;
return type == TokenTypes.COMMENT_MULTILINE || type == TokenTypes.COMMENT_DOCUMENTATION;
}
return false;
}
/**
* Action that knows how to special-case inserting a newline in a
* multi-line comment for languages like C and Java.
*/
protected class CStyleInsertBreakAction extends
RSyntaxTextAreaEditorKit.InsertBreakAction {
protected class CStyleInsertBreakAction extends RSyntaxTextAreaEditorKit.InsertBreakAction {
@Override
public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
if (!textArea.isEditable() || !textArea.isEnabled()) {
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
return;
}
RSyntaxTextArea rsta = (RSyntaxTextArea)getTextComponent(e);
RSyntaxDocument doc = (RSyntaxDocument)rsta.getDocument();
RSyntaxTextArea rsta = (RSyntaxTextArea) getTextComponent(e);
RSyntaxDocument doc = (RSyntaxDocument) rsta.getDocument();
int line = textArea.getCaretLineNumber();
int type = doc.getLastTokenTypeOnLine(line);
if (type<0) {
if (type < 0) {
type = doc.getClosestStandardTokenTypeForInternalType(type);
}
// Only in MLC's should we try this
if (type==Token.COMMENT_DOCUMENTATION ||
type==Token.COMMENT_MULTILINE) {
if (type == Token.COMMENT_DOCUMENTATION || type == Token.COMMENT_MULTILINE) {
insertBreakInMLC(e, rsta, line);
}
else {
} else {
handleInsertBreak(rsta, true);
}
}
/**
* Returns whether the MLC token containing <code>offs</code> appears
* to have a "nested" comment (i.e., contains "<code>/*</code>"
* somewhere inside of it). This implies that it is likely a "new" MLC
* and needs to be closed. While not foolproof, this is usually good
* somewhere inside of it). This implies that it is likely a "new" MLC
* and needs to be closed. While not foolproof, this is usually good
* enough of a sign.
*
* @param textArea
@@ -175,45 +154,37 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
* @param offs
* @return Whether a comment appears to be nested inside this one.
*/
private boolean appearsNested(RSyntaxTextArea textArea,
int line, int offs) {
private boolean appearsNested(RSyntaxTextArea textArea, int line, int offs) {
final int firstLine = line; // Remember the line we start at.
while (line<textArea.getLineCount()) {
while (line < textArea.getLineCount()) {
Token t = textArea.getTokenListForLine(line);
int i = 0;
// If examining the first line, start at offs.
if (line++==firstLine) {
if (line++ == firstLine) {
t = RSyntaxUtilities.getTokenAtOffset(t, offs);
if (t==null) { // offs was at end of the line
if (t == null) { // offs was at end of the line
continue;
}
i = t.documentToToken(offs);
}
else {
} else {
i = t.getTextOffset();
}
int textOffset = t.getTextOffset();
while (i<textOffset+t.length()-1) {
if (t.charAt(i-textOffset)=='/' && t.charAt(i-textOffset+1)=='*') {
while (i < textOffset + t.length() - 1) {
if (t.charAt(i - textOffset) == '/' && t.charAt(i - textOffset + 1) == '*') {
return true;
}
i++;
}
// If tokens come after this one on this line, our MLC ended.
if ((t=t.getNextToken())!=null && !isInternalEolTokenForMLCs(t)) {
if ((t = t.getNextToken()) != null && !isInternalEolTokenForMLCs(t)) {
return false;
}
}
return true; // No match - MLC goes to the end of the file
}
private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea,
int line) {
private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea, int line) {
Matcher m = null;
int start = -1;
int end = -1;
@@ -221,39 +192,33 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
try {
start = textArea.getLineStartOffset(line);
end = textArea.getLineEndOffset(line);
text = textArea.getText(start, end-start);
text = textArea.getText(start, end - start);
m = MLC_PATTERN.matcher(text);
} catch (BadLocationException ble) { // Never happens
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
ble.printStackTrace();
return;
}
if (m.lookingAt()) {
String leadingWS = m.group(1);
String mlcMarker = m.group(2);
// If the caret is "inside" any leading whitespace or MLC
// marker, move it to the end of the line.
int dot = textArea.getCaretPosition();
if (dot>=start &&
dot<start+leadingWS.length()+mlcMarker.length()) {
if (dot >= start && dot < start + leadingWS.length() + mlcMarker.length()) {
// If we're in the whitespace before the very start of the
// MLC though, just insert a normal newline
if (mlcMarker.charAt(0)=='/') {
if (mlcMarker.charAt(0) == '/') {
handleInsertBreak(textArea, true);
return;
}
textArea.setCaretPosition(end-1);
}
else {
textArea.setCaretPosition(end - 1);
} else {
// Ensure caret is at the "end" of any whitespace
// immediately after the '*', but before any possible
// non-whitespace chars.
boolean moved = false;
while (dot<end-1 &&
Character.isWhitespace(text.charAt(dot-start))) {
while (dot < end - 1 && Character.isWhitespace(text.charAt(dot - start))) {
moved = true;
dot++;
}
@@ -261,28 +226,18 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
textArea.setCaretPosition(dot);
}
}
boolean firstMlcLine = mlcMarker.charAt(0)=='/';
boolean nested = appearsNested(textArea, line,
start+leadingWS.length()+2);
String header = leadingWS +
(firstMlcLine ? " * " : "*") +
m.group(3);
boolean firstMlcLine = mlcMarker.charAt(0) == '/';
boolean nested = appearsNested(textArea, line, start + leadingWS.length() + 2);
String header = leadingWS + (firstMlcLine ? " * " : "*") + m.group(3);
textArea.replaceSelection("\n" + header);
if (nested) {
dot = textArea.getCaretPosition(); // Has changed
textArea.insert("\n" + leadingWS + " */", dot);
textArea.setCaretPosition(dot);
}
}
else {
} else {
handleInsertBreak(textArea, true);
}
}
}
}
}
@@ -1,20 +1,17 @@
/*
* 03/23/2005
*
* AbstractJFlexTokenMaker.java - Base class for token makers generated from
* programs such as JFlex.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.Segment;
/**
* Base class for JFlex-generated token makers. This class attempts to factor
* out all common code from these classes. Many methods <em>almost</em> could
* Base class for JFlex-generated token makers. This class attempts to factor
* out all common code from these classes. Many methods <em>almost</em> could
* be factored out into this class, but cannot because they reference JFlex
* variables that we cannot access from this class.
*
@@ -22,31 +19,30 @@ import javax.swing.text.Segment;
* @version 0.1
*/
public abstract class AbstractJFlexTokenMaker extends TokenMakerBase {
protected Segment s;
protected int start; // Just for states.
protected int offsetShift; // As parser always starts at 0, but our line doesn't.
protected int start; // Just for states.
protected int offsetShift; // As parser always starts at 0, but our line
// doesn't.
/**
* Declared here so we can define overloads that refer to this method.
*
* @param newState The new JFlex state to enter.
* @param newState
* The new JFlex state to enter.
*/
public abstract void yybegin(int newState);
/**
* Starts a new JFlex state and changes the current language index.
*
* @param state The new JFlex state to enter.
* @param languageIndex The new language index.
* @param state
* The new JFlex state to enter.
* @param languageIndex
* The new language index.
*/
protected void yybegin(int state, int languageIndex) {
yybegin(state);
setLanguageIndex(languageIndex);
}
}
}
@@ -1,19 +1,17 @@
/*
* 11/07/2004
*
* AbstractTokenMaker.java - An abstract implementation of TokenMaker.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* An abstract implementation of the
* {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should
* {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should
* be overridden for every language for which you want to provide
* syntax highlighting.<p>
* syntax highlighting.
* <p>
*
* @see Token
*
@@ -21,7 +19,7 @@ package org.fife.ui.rsyntaxtextarea;
* @version 0.2
*/
public abstract class AbstractTokenMaker extends TokenMakerBase {
/**
* Hash table of words to highlight and what token type they are.
* The keys are the words to highlight, and their values are the
@@ -29,16 +27,14 @@ public abstract class AbstractTokenMaker extends TokenMakerBase {
* <code>Token.FUNCTION</code>.
*/
protected TokenMap wordsToHighlight;
/**
* Constructor.
*/
public AbstractTokenMaker() {
wordsToHighlight = getWordsToHighlight();
}
/**
* Returns the words to highlight for this programming language.
*
@@ -46,22 +42,18 @@ public abstract class AbstractTokenMaker extends TokenMakerBase {
* this programming language.
*/
public abstract TokenMap getWordsToHighlight();
/**
* Removes the token last added from the linked list of tokens. The
* Removes the token last added from the linked list of tokens. The
* programmer should never have to call this directly; it can be called
* by subclasses of <code>TokenMaker</code> if necessary.
*/
public void removeLastToken() {
if (previousToken==null) {
if (previousToken == null) {
firstToken = currentToken = null;
}
else {
} else {
currentToken = previousToken;
currentToken.setNextToken(null);
}
}
}
}
@@ -1,9 +1,7 @@
/*
* 12/14/08
*
* AbstractTokenMakerFactory.java - Base class for TokenMaker implementations.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -12,25 +10,23 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Base class for {@link TokenMakerFactory} implementations. A mapping from
* Base class for {@link TokenMakerFactory} implementations. A mapping from
* language keys to the names of {@link TokenMaker} classes is stored.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
/**
* A mapping from keys to the names of {@link TokenMaker} implementation
* class names. When {@link #getTokenMaker(String)} is called with a key
* class names. When {@link #getTokenMaker(String)} is called with a key
* defined in this map, a <code>TokenMaker</code> of the corresponding type
* is returned.
*/
private Map<String, Object> tokenMakerMap;
/**
* Constructor.
*/
@@ -38,19 +34,19 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
tokenMakerMap = new HashMap<String, Object>();
initTokenMakerMap();
}
/**
* Returns a {@link TokenMaker} for the specified key.
*
* @param key The key.
* @param key
* The key.
* @return The corresponding <code>TokenMaker</code>, or <code>null</code>
* if none matches the specified key.
*/
@Override
protected TokenMaker getTokenMakerImpl(String key) {
TokenMakerCreator tmc = (TokenMakerCreator)tokenMakerMap.get(key);
if (tmc!=null) {
TokenMakerCreator tmc = (TokenMakerCreator) tokenMakerMap.get(key);
if (tmc != null) {
try {
return tmc.create();
} catch (RuntimeException re) { // FindBugs
@@ -61,11 +57,10 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
}
return null;
}
/**
* Populates the mapping from keys to instances of
* <code>TokenMakerCreator</code>s. Subclasses should override this method
* <code>TokenMakerCreator</code>s. Subclasses should override this method
* and call one of the <code>putMapping</code> overloads to register
* {@link TokenMaker}s for syntax constants.
*
@@ -73,8 +68,7 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
* @see #putMapping(String, String, ClassLoader)
*/
protected abstract void initTokenMakerMap();
/**
* {@inheritDoc}
*/
@@ -82,53 +76,52 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
public Set<String> keySet() {
return tokenMakerMap.keySet();
}
/**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
* class name.
*
* @param key The key.
* @param className The <code>TokenMaker</code> class name.
* @param key
* The key.
* @param className
* The <code>TokenMaker</code> class name.
* @see #putMapping(String, String, ClassLoader)
*/
public void putMapping(String key, String className) {
putMapping(key, className, null);
}
/**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
* class name.
*
* @param key The key.
* @param className The <code>TokenMaker</code> class name.
* @param cl The class loader to use when loading the class.
* @param key
* The key.
* @param className
* The <code>TokenMaker</code> class name.
* @param cl
* The class loader to use when loading the class.
* @see #putMapping(String, String)
*/
public void putMapping(String key, String className, ClassLoader cl) {
tokenMakerMap.put(key, new TokenMakerCreator(className, cl));
}
/**
* Wrapper that handles the creation of TokenMaker instances.
*/
private static class TokenMakerCreator {
private String className;
private ClassLoader cl;
public TokenMakerCreator(String className, ClassLoader cl) {
this.className = className;
this.cl = cl!=null ? cl : getClass().getClassLoader();
this.cl = cl != null ? cl : getClass().getClassLoader();
}
public TokenMaker create() throws Exception {
return (TokenMaker)Class.forName(className, true, cl).newInstance();
return (TokenMaker) Class.forName(className, true, cl).newInstance();
}
}
}
}
@@ -1,17 +1,14 @@
/*
* 02/06/2011
*
* ActiveLineRangeEvent.java - Notifies listeners of an "active line range"
* change in an RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.EventObject;
/**
* The event fired by {@link RSyntaxTextArea}s when the active line range
* changes.
@@ -20,27 +17,28 @@ import java.util.EventObject;
* @version 1.0
*/
public class ActiveLineRangeEvent extends EventObject {
private int min;
private int max;
/**
* Constructor.
*
* @param source The text area.
* @param min The first line in the active line range, or
* <code>-1</code> if the line range is being cleared.
* @param max The last line in the active line range, or
* <code>-1</code> if the line range is being cleared.
* @param source
* The text area.
* @param min
* The first line in the active line range, or
* <code>-1</code> if the line range is being cleared.
* @param max
* The last line in the active line range, or
* <code>-1</code> if the line range is being cleared.
*/
public ActiveLineRangeEvent(RSyntaxTextArea source, int min, int max) {
super(source);
this.min = min;
this.max = max;
}
/**
* Returns the last line in the active line range.
*
@@ -51,8 +49,7 @@ public class ActiveLineRangeEvent extends EventObject {
public int getMax() {
return max;
}
/**
* Returns the first line in the active line range.
*
@@ -63,6 +60,4 @@ public class ActiveLineRangeEvent extends EventObject {
public int getMin() {
return min;
}
}
}
@@ -1,44 +1,41 @@
/*
* 02/06/2011
*
* ActiveLineRangeListener.java - Listens for "active line range" changes
* in an RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.EventListener;
/**
* Listens for "active line range" events from an <code>RSyntaxTextArea</code>.
* If a text area contains some semantic knowledge of the programming language
* being edited, it may broadcast {@link ActiveLineRangeEvent}s whenever the
* caret moves into a new "block" of code. Listeners can listen for these
* events and respond accordingly.<p>
* caret moves into a new "block" of code. Listeners can listen for these
* events and respond accordingly.
* <p>
*
* See the <code>RSTALanguageSupport</code> project at
* <a href="http://fifesoft.com">http://fifesoft.com</a> for some
* <code>LanguageSupport</code> implementations that may broadcast these
* events. Note that if an RSTA/LanguageSupport does not support broadcasting
* events. Note that if an RSTA/LanguageSupport does not support broadcasting
* these events, the listener will simply never receive any notifications.
*
* @author Robert Futrell
* @version 1.0
*/
public interface ActiveLineRangeListener extends EventListener {
/**
* Called whenever the "active line range" changes.
*
* @param e Information about the line range change. If there is no longer
* an "active line range," the "minimum" and "maximum" line values
* should both be <code>-1</code>.
* @param e
* Information about the line range change. If there is no longer
* an "active line range," the "minimum" and "maximum" line
* values
* should both be <code>-1</code>.
*/
void activeLineRangeChanged(ActiveLineRangeEvent e);
}
}
@@ -1,9 +1,7 @@
/*
* 02/21/2005
*
* CodeTemplateManager.java - manages code templates.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -30,18 +28,20 @@ import javax.swing.text.Segment;
import org.fife.ui.rsyntaxtextarea.templates.CodeTemplate;
/**
* Manages "code templates."<p>
* Manages "code templates."
* <p>
*
* All methods in this class are synchronized for thread safety, but as a
* best practice, you should probably only modify the templates known to a
* <code>CodeTemplateManager</code> on the EDT. Modifying a
* <code>CodeTemplateManager</code> on the EDT. Modifying a
* <code>CodeTemplate</code> retrieved from a <code>CodeTemplateManager</code>
* while <em>not</em> on the EDT could cause problems.<p>
* while <em>not</em> on the EDT could cause problems.
* <p>
*
* For more flexible boilerplate code insertion, consider using the
* <a href="http://javadoc.fifesoft.com/autocomplete/org/fife/ui/autocomplete/TemplateCompletion.html">
* <a href=
* "http://javadoc.fifesoft.com/autocomplete/org/fife/ui/autocomplete/TemplateCompletion.html">
* TemplateCompletion class</a> in the
* <a href="https://github.com/bobbylight/AutoComplete">AutoComplete
* add-on library</a>.
@@ -50,15 +50,13 @@ import org.fife.ui.rsyntaxtextarea.templates.CodeTemplate;
* @version 1.0
*/
public class CodeTemplateManager {
private int maxTemplateIDLength;
private List<CodeTemplate> templates;
private Segment s;
private TemplateComparator comparator;
private File directory;
/**
* Constructor.
*/
@@ -67,31 +65,32 @@ public class CodeTemplateManager {
comparator = new TemplateComparator();
templates = new ArrayList<CodeTemplate>();
}
/**
* Registers the specified template with this template manager.
*
* @param template The template to register.
* @throws IllegalArgumentException If <code>template</code> is
* <code>null</code>.
* @param template
* The template to register.
* @throws IllegalArgumentException
* If <code>template</code> is
* <code>null</code>.
* @see #removeTemplate(CodeTemplate)
* @see #removeTemplate(String)
*/
public synchronized void addTemplate(CodeTemplate template) {
if (template==null) {
if (template == null) {
throw new IllegalArgumentException("template cannot be null");
}
templates.add(template);
sortTemplates();
}
/**
* Returns the template that should be inserted at the current caret
* position, assuming the trigger character was pressed.
*
* @param textArea The text area that's getting text inserted into it.
* @param textArea
* The text area that's getting text inserted into it.
* @return A template that should be inserted, if appropriate, or
* <code>null</code> if no template should be inserted.
*/
@@ -100,17 +99,16 @@ public class CodeTemplateManager {
int charsToGet = Math.min(caretPos, maxTemplateIDLength);
try {
Document doc = textArea.getDocument();
doc.getText(caretPos-charsToGet, charsToGet, s);
doc.getText(caretPos - charsToGet, charsToGet, s);
@SuppressWarnings("unchecked")
int index = Collections.binarySearch(templates, s, comparator);
return index>=0 ? (CodeTemplate)templates.get(index) : null;
return index >= 0 ? (CodeTemplate) templates.get(index) : null;
} catch (BadLocationException ble) {
ble.printStackTrace();
throw new InternalError("Error in CodeTemplateManager");
}
}
/**
* Returns the number of templates this manager knows about.
*
@@ -119,8 +117,7 @@ public class CodeTemplateManager {
public synchronized int getTemplateCount() {
return templates.size();
}
/**
* Returns the templates currently available.
*
@@ -130,123 +127,113 @@ public class CodeTemplateManager {
CodeTemplate[] temp = new CodeTemplate[templates.size()];
return templates.toArray(temp);
}
/**
* Returns whether the specified character is a valid character for a
* <code>CodeTemplate</code> id.
*
* @param ch The character to check.
* @param ch
* The character to check.
* @return Whether the character is a valid template character.
*/
public static final boolean isValidChar(char ch) {
return RSyntaxUtilities.isLetterOrDigit(ch) || ch=='_';
return RSyntaxUtilities.isLetterOrDigit(ch) || ch == '_';
}
/**
* Returns the specified code template.
*
* @param template The template to remove.
* @param template
* The template to remove.
* @return <code>true</code> if the template was removed, <code>false</code>
* if the template was not in this template manager.
* @throws IllegalArgumentException If <code>template</code> is
* <code>null</code>.
* @throws IllegalArgumentException
* If <code>template</code> is
* <code>null</code>.
* @see #removeTemplate(String)
* @see #addTemplate(CodeTemplate)
*/
public synchronized boolean removeTemplate(CodeTemplate template) {
if (template==null) {
if (template == null) {
throw new IllegalArgumentException("template cannot be null");
}
// TODO: Do a binary search
return templates.remove(template);
}
/**
* Returns the code template with the specified id.
*
* @param id The id to check for.
* @param id
* The id to check for.
* @return The code template that was removed, or <code>null</code> if
* there was no template with the specified ID.
* @throws IllegalArgumentException If <code>id</code> is <code>null</code>.
* @throws IllegalArgumentException
* If <code>id</code> is <code>null</code>.
* @see #removeTemplate(CodeTemplate)
* @see #addTemplate(CodeTemplate)
*/
public synchronized CodeTemplate removeTemplate(String id) {
if (id==null) {
if (id == null) {
throw new IllegalArgumentException("id cannot be null");
}
// TODO: Do a binary search
for (Iterator<CodeTemplate> i=templates.iterator(); i.hasNext();) {
for (Iterator<CodeTemplate> i = templates.iterator(); i.hasNext();) {
CodeTemplate template = i.next();
if (id.equals(template.getID())) {
i.remove();
return template;
}
}
return null;
}
/**
* Replaces the current set of available templates with the ones
* specified.
*
* @param newTemplates The new set of templates. Note that we will
* be taking a shallow copy of these and sorting them.
* @param newTemplates
* The new set of templates. Note that we will
* be taking a shallow copy of these and sorting them.
*/
public synchronized void replaceTemplates(CodeTemplate[] newTemplates) {
templates.clear();
if (newTemplates!=null) {
for (int i=0; i<newTemplates.length; i++) {
if (newTemplates != null) {
for (int i = 0; i < newTemplates.length; i++) {
templates.add(newTemplates[i]);
}
}
sortTemplates(); // Also recomputes maxTemplateIDLength.
}
/**
* Saves all templates as XML files in the current template directory.
*
* @return Whether or not the save was successful.
*/
public synchronized boolean saveTemplates() {
if (templates==null) {
if (templates == null) {
return true;
}
if (directory==null || !directory.isDirectory()) {
if (directory == null || !directory.isDirectory()) {
return false;
}
// Blow away all old XML files to start anew, as some might be from
// templates we're removed from the template manager.
File[] oldXMLFiles = directory.listFiles(new XMLFileFilter());
if (oldXMLFiles==null) {
if (oldXMLFiles == null) {
return false; // Either an IOException or it isn't a directory.
}
int count = oldXMLFiles.length;
for (int i=0; i<count; i++) {
/*boolean deleted = */oldXMLFiles[i].delete();
for (int i = 0; i < count; i++) {
/* boolean deleted = */oldXMLFiles[i].delete();
}
// Save all current templates as XML.
boolean wasSuccessful = true;
for (CodeTemplate template : templates) {
File xmlFile = new File(directory, template.getID() + ".xml");
try {
XMLEncoder e = new XMLEncoder(new BufferedOutputStream(
new FileOutputStream(xmlFile)));
XMLEncoder e = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(xmlFile)));
e.writeObject(template);
e.close();
} catch (IOException ioe) {
@@ -254,48 +241,38 @@ public class CodeTemplateManager {
wasSuccessful = false;
}
}
return wasSuccessful;
}
/**
* Sets the directory in which to look for templates. Calling this
* Sets the directory in which to look for templates. Calling this
* method adds any new templates found in the specified directory to
* the templates already registered.
*
* @param dir The new directory in which to look for templates.
* @param dir
* The new directory in which to look for templates.
* @return The new number of templates in this template manager, or
* <code>-1</code> if the specified directory does not exist.
*/
public synchronized int setTemplateDirectory(File dir) {
if (dir!=null && dir.isDirectory()) {
if (dir != null && dir.isDirectory()) {
this.directory = dir;
File[] files = dir.listFiles(new XMLFileFilter());
int newCount = files==null ? 0 : files.length;
int newCount = files == null ? 0 : files.length;
int oldCount = templates.size();
List<CodeTemplate> temp =
new ArrayList<CodeTemplate>(oldCount+newCount);
List<CodeTemplate> temp = new ArrayList<CodeTemplate>(oldCount + newCount);
temp.addAll(templates);
for (int i=0; i<newCount; i++) {
for (int i = 0; i < newCount; i++) {
try {
XMLDecoder d = new XMLDecoder(new BufferedInputStream(
new FileInputStream(files[i])));
XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream(files[i])));
Object obj = d.readObject();
if (!(obj instanceof CodeTemplate)) {
d.close();
throw new IOException("Not a CodeTemplate: " +
files[i].getAbsolutePath());
throw new IOException("Not a CodeTemplate: " + files[i].getAbsolutePath());
}
temp.add((CodeTemplate)obj);
temp.add((CodeTemplate) obj);
d.close();
} catch (/*IO, NoSuchElement*/Exception e) {
} catch (/* IO, NoSuchElement */Exception e) {
// NoSuchElementException can be thrown when reading
// an XML file not in the format expected by XMLDecoder.
// (e.g. CodeTemplates in an old format).
@@ -304,74 +281,59 @@ public class CodeTemplateManager {
}
templates = temp;
sortTemplates();
return getTemplateCount();
}
return -1;
}
/**
* Removes any null entries in the current set of templates (if
* any), sorts the remaining templates, and computes the new
* maximum template ID length.
*/
private synchronized void sortTemplates() {
// Get the maximum length of a template ID.
maxTemplateIDLength = 0;
// Remove any null entries (should only happen because of
// IOExceptions, etc. when loading from files), and sort
// the remaining list.
for (Iterator<CodeTemplate> i=templates.iterator(); i.hasNext();) {
for (Iterator<CodeTemplate> i = templates.iterator(); i.hasNext();) {
CodeTemplate temp = i.next();
if (temp==null || temp.getID()==null) {
if (temp == null || temp.getID() == null) {
i.remove();
}
else {
maxTemplateIDLength = Math.max(maxTemplateIDLength,
temp.getID().length());
} else {
maxTemplateIDLength = Math.max(maxTemplateIDLength, temp.getID().length());
}
}
Collections.sort(templates);
}
/**
* A comparator that takes a <code>CodeTemplate</code> as its first
* parameter and a <code>Segment</code> as its second, and knows
* to compare the template's ID to the segment's text.
*/
@SuppressWarnings("rawtypes")
private static class TemplateComparator implements Comparator, Serializable{
private static class TemplateComparator implements Comparator, Serializable {
@Override
public int compare(Object template, Object segment) {
// Get template start index (0) and length.
CodeTemplate t = (CodeTemplate)template;
CodeTemplate t = (CodeTemplate) template;
final char[] templateArray = t.getID().toCharArray();
int i = 0;
int len1 = templateArray.length;
// Find "token" part of segment and get its offset and length.
Segment s = (Segment)segment;
Segment s = (Segment) segment;
char[] segArray = s.array;
int len2 = s.count;
int j = s.offset + len2 - 1;
while (j>=s.offset && isValidChar(segArray[j])) {
while (j >= s.offset && isValidChar(segArray[j])) {
j--;
}
j++;
int segShift = j - s.offset;
len2 -= segShift;
int n = Math.min(len1, len2);
while (n-- != 0) {
char c1 = templateArray[i++];
@@ -381,21 +343,17 @@ public class CodeTemplateManager {
}
}
return len1 - len2;
}
}
/**
* A file filter that accepts only XML files.
*/
private static class XMLFileFilter implements FileFilter {
@Override
public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(".xml");
}
}
}
}
@@ -1,10 +1,8 @@
/*
* 03/09/2013
*
* DefaultOccurrenceMarker - Marks occurrences of the current token for most
* languages.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -14,48 +12,40 @@ import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* The default implementation of {@link OccurrenceMarker}. It goes through
* The default implementation of {@link OccurrenceMarker}. It goes through
* the document and marks all instances of the specified token.
*
* @author Robert Futrell
* @version 1.0
*/
class DefaultOccurrenceMarker implements OccurrenceMarker {
/**
* {@inheritDoc}
*/
@Override
public Token getTokenToMark(RSyntaxTextArea textArea) {
// Get the token at the caret position.
int line = textArea.getCaretLineNumber();
Token tokenList = textArea.getTokenListForLine(line);
Caret c = textArea.getCaret();
int dot = c.getDot();
Token t = RSyntaxUtilities.getTokenAtOffset(tokenList, dot);
if (t==null /* EOL */ || !isValidType(textArea, t) ||
RSyntaxUtilities.isNonWordChar(t)) {
if (t == null /* EOL */ || !isValidType(textArea, t) || RSyntaxUtilities.isNonWordChar(t)) {
// Try to the "left" of the caret.
dot--;
try {
if (dot>=textArea.getLineStartOffset(line)) {
if (dot >= textArea.getLineStartOffset(line)) {
t = RSyntaxUtilities.getTokenAtOffset(tokenList, dot);
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
return t;
}
/**
* {@inheritDoc}
*/
@@ -63,41 +53,39 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType());
}
/**
* {@inheritDoc}
*/
@Override
public void markOccurrences(RSyntaxDocument doc, Token t,
RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
public void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
markOccurrencesOfToken(doc, t, h, p);
}
/**
* Highlights all instances of tokens identical to <code>t</code> in the
* specified document.
*
* @param doc The document.
* @param t The document whose relevant occurrences should be marked.
* @param h The highlighter to add the highlights to.
* @param p The painter for the highlights.
* @param doc
* The document.
* @param t
* The document whose relevant occurrences should be marked.
* @param h
* The highlighter to add the highlights to.
* @param p
* The painter for the highlights.
*/
public static final void markOccurrencesOfToken(RSyntaxDocument doc,
Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
public static final void markOccurrencesOfToken(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
char[] lexeme = t.getLexeme().toCharArray();
int type = t.getType();
int lineCount = doc.getDefaultRootElement().getElementCount();
for (int i=0; i<lineCount; i++) {
for (int i = 0; i < lineCount; i++) {
Token temp = doc.getTokenListForLine(i);
while (temp!=null && temp.isPaintable()) {
while (temp != null && temp.isPaintable()) {
if (temp.is(type, lexeme)) {
try {
int end = temp.getEndOffset();
h.addMarkedOccurrenceHighlight(temp.getOffset(),end,p);
h.addMarkedOccurrenceHighlight(temp.getOffset(), end, p);
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
@@ -105,8 +93,5 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
temp = temp.getNextToken();
}
}
}
}
}
@@ -1,90 +1,82 @@
/*
* 10/28/2004
*
* DefaultTokenFactory.java - Default token factory.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.Segment;
/**
* This class generates tokens for a {@link TokenMaker}. This class is here
* This class generates tokens for a {@link TokenMaker}. This class is here
* because it reuses tokens when they aren't needed anymore to prevent
* This class doesn't actually create new tokens every time
* <code>createToken</code> is called. Instead, it internally keeps a stack of
* available already-created tokens. When more tokens are needed to properly
* display a line, more tokens are added to the available stack. This saves
* <code>createToken</code> is called. Instead, it internally keeps a stack of
* available already-created tokens. When more tokens are needed to properly
* display a line, more tokens are added to the available stack. This saves
* from needless repetitive memory allocation. However, it makes it IMPERATIVE
* that users call <code>resetTokenList</code> when creating a new token list so
* that the token maker can keep an accurate list of available tokens.<p>
* that the token maker can keep an accurate list of available tokens.
* <p>
*
* NOTE: This class should only be used by {@link TokenMaker}; nobody else
* NOTE: This class should only be used by {@link TokenMaker}; nobody else
* needs it!
*
* @author Robert Futrell
* @version 0.1
*/
class DefaultTokenFactory implements TokenFactory {
private int size;
private int increment;
private TokenImpl[] tokenList;
private int currentFreeToken;
protected static final int DEFAULT_START_SIZE = 30;
protected static final int DEFAULT_INCREMENT = 10;
protected static final int DEFAULT_START_SIZE = 30;
protected static final int DEFAULT_INCREMENT = 10;
/**
* Constructor.
*/
DefaultTokenFactory() {
this(DEFAULT_START_SIZE, DEFAULT_INCREMENT);
}
/**
* Constructor.
*
* @param size The initial number of tokens in this factory.
* @param increment How many tokens to increment by when the stack gets
* empty.
* @param size
* The initial number of tokens in this factory.
* @param increment
* How many tokens to increment by when the stack gets
* empty.
*/
DefaultTokenFactory(int size, int increment) {
this.size = size;
this.increment = increment;
this.currentFreeToken = 0;
// Give us some tokens to initially work with.
tokenList = new TokenImpl[size];
for (int i=0; i<size; i++) {
for (int i = 0; i < size; i++) {
tokenList[i] = new TokenImpl();
}
}
/**
* Adds tokens to the internal token list. This is called whenever a
* Adds tokens to the internal token list. This is called whenever a
* request is made and no more tokens are available.
*/
private void augmentTokenList() {
TokenImpl[] temp = new TokenImpl[size + increment];
System.arraycopy(tokenList,0, temp,0, size);
System.arraycopy(tokenList, 0, temp, 0, size);
size += increment;
tokenList = temp;
for (int i=0; i<increment; i++) {
tokenList[size-i-1] = new TokenImpl();
for (int i = 0; i < increment; i++) {
tokenList[size - i - 1] = new TokenImpl();
}
//System.err.println("... size up to: " + size);
// System.err.println("... size up to: " + size);
}
/**
* {@inheritDoc}
*/
@@ -96,41 +88,36 @@ class DefaultTokenFactory implements TokenFactory {
token.setOffset(-1);
token.setNextToken(null);
currentFreeToken++;
if (currentFreeToken==size) {
if (currentFreeToken == size) {
augmentTokenList();
}
return token;
}
/**
* {@inheritDoc}
*/
@Override
public TokenImpl createToken(final Segment line, final int beg,
final int end, final int startOffset, final int type) {
return createToken(line.array, beg,end, startOffset, type);
public TokenImpl createToken(final Segment line, final int beg, final int end, final int startOffset, final int type) {
return createToken(line.array, beg, end, startOffset, type);
}
/**
* {@inheritDoc}
*/
@Override
public TokenImpl createToken(final char[] line, final int beg,
final int end, final int startOffset, final int type) {
public TokenImpl createToken(final char[] line, final int beg, final int end, final int startOffset, final int type) {
TokenImpl token = tokenList[currentFreeToken];
token.set(line, beg,end, startOffset, type);
token.set(line, beg, end, startOffset, type);
currentFreeToken++;
if (currentFreeToken==size) {
if (currentFreeToken == size) {
augmentTokenList();
}
return token;
}
/**
* Resets the state of this token maker. This method should be called
* Resets the state of this token maker. This method should be called
* by the <code>TokenMaker</code> every time a token list is generated for
* a new line so the tokens can be reused.
*/
@@ -138,6 +125,4 @@ class DefaultTokenFactory implements TokenFactory {
public void resetAllTokens() {
currentFreeToken = 0;
}
}
}
@@ -1,83 +1,73 @@
/*
* 12/14/2008
*
* DefaultTokenMakerFactory.java - The default TokenMaker factory.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* The default implementation of <code>TokenMakerFactory</code>. This factory
* The default implementation of <code>TokenMakerFactory</code>. This factory
* can create {@link TokenMaker}s for all languages known to
* {@link RSyntaxTextArea}.
*
* @author Robert Futrell
* @version 1.0
*/
class DefaultTokenMakerFactory extends AbstractTokenMakerFactory
implements SyntaxConstants {
class DefaultTokenMakerFactory extends AbstractTokenMakerFactory implements SyntaxConstants {
/**
* {@inheritDoc}
*/
@Override
protected void initTokenMakerMap() {
String pkg = "org.fife.ui.rsyntaxtextarea.modes.";
putMapping(SYNTAX_STYLE_NONE, pkg + "PlainTextTokenMaker");
putMapping(SYNTAX_STYLE_ACTIONSCRIPT, pkg + "ActionScriptTokenMaker");
putMapping(SYNTAX_STYLE_ASSEMBLER_X86, pkg + "AssemblerX86TokenMaker");
putMapping(SYNTAX_STYLE_BBCODE, pkg + "BBCodeTokenMaker");
putMapping(SYNTAX_STYLE_C, pkg + "CTokenMaker");
putMapping(SYNTAX_STYLE_CLOJURE, pkg + "ClojureTokenMaker");
putMapping(SYNTAX_STYLE_CPLUSPLUS, pkg + "CPlusPlusTokenMaker");
putMapping(SYNTAX_STYLE_CSHARP, pkg + "CSharpTokenMaker");
putMapping(SYNTAX_STYLE_CSS, pkg + "CSSTokenMaker");
putMapping(SYNTAX_STYLE_D, pkg + "DTokenMaker");
putMapping(SYNTAX_STYLE_DART, pkg + "DartTokenMaker");
putMapping(SYNTAX_STYLE_DELPHI, pkg + "DelphiTokenMaker");
putMapping(SYNTAX_STYLE_DOCKERFILE, pkg + "DockerTokenMaker");
putMapping(SYNTAX_STYLE_DTD, pkg + "DtdTokenMaker");
putMapping(SYNTAX_STYLE_FORTRAN, pkg + "FortranTokenMaker");
putMapping(SYNTAX_STYLE_GROOVY, pkg + "GroovyTokenMaker");
putMapping(SYNTAX_STYLE_HOSTS, pkg + "HostsTokenMaker");
putMapping(SYNTAX_STYLE_HTACCESS, pkg + "HtaccessTokenMaker");
putMapping(SYNTAX_STYLE_HTML, pkg + "HTMLTokenMaker");
putMapping(SYNTAX_STYLE_INI, pkg + "IniTokenMaker");
putMapping(SYNTAX_STYLE_JAVA, pkg + "JavaTokenMaker");
putMapping(SYNTAX_STYLE_JAVASCRIPT, pkg + "JavaScriptTokenMaker");
putMapping(SYNTAX_STYLE_JSON_WITH_COMMENTS, pkg + "JshintrcTokenMaker");
putMapping(SYNTAX_STYLE_JSON, pkg + "JsonTokenMaker");
putMapping(SYNTAX_STYLE_JSP, pkg + "JSPTokenMaker");
putMapping(SYNTAX_STYLE_LATEX, pkg + "LatexTokenMaker");
putMapping(SYNTAX_STYLE_LESS, pkg + "LessTokenMaker");
putMapping(SYNTAX_STYLE_LISP, pkg + "LispTokenMaker");
putMapping(SYNTAX_STYLE_LUA, pkg + "LuaTokenMaker");
putMapping(SYNTAX_STYLE_MAKEFILE, pkg + "MakefileTokenMaker");
putMapping(SYNTAX_STYLE_MXML, pkg + "MxmlTokenMaker");
putMapping(SYNTAX_STYLE_NSIS, pkg + "NSISTokenMaker");
putMapping(SYNTAX_STYLE_PERL, pkg + "PerlTokenMaker");
putMapping(SYNTAX_STYLE_PHP, pkg + "PHPTokenMaker");
putMapping(SYNTAX_STYLE_PROPERTIES_FILE,pkg + "PropertiesFileTokenMaker");
putMapping(SYNTAX_STYLE_PYTHON, pkg + "PythonTokenMaker");
putMapping(SYNTAX_STYLE_RUBY, pkg + "RubyTokenMaker");
putMapping(SYNTAX_STYLE_SAS, pkg + "SASTokenMaker");
putMapping(SYNTAX_STYLE_SCALA, pkg + "ScalaTokenMaker");
putMapping(SYNTAX_STYLE_SQL, pkg + "SQLTokenMaker");
putMapping(SYNTAX_STYLE_TCL, pkg + "TclTokenMaker");
putMapping(SYNTAX_STYLE_TYPESCRIPT, pkg + "TypeScriptTokenMaker");
putMapping(SYNTAX_STYLE_UNIX_SHELL, pkg + "UnixShellTokenMaker");
putMapping(SYNTAX_STYLE_VISUAL_BASIC, pkg + "VisualBasicTokenMaker");
putMapping(SYNTAX_STYLE_WINDOWS_BATCH, pkg + "WindowsBatchTokenMaker");
putMapping(SYNTAX_STYLE_XML, pkg + "XMLTokenMaker");
putMapping(SYNTAX_STYLE_YAML, pkg + "YamlTokenMaker");
putMapping(SYNTAX_STYLE_NONE, pkg + "PlainTextTokenMaker");
putMapping(SYNTAX_STYLE_ACTIONSCRIPT, pkg + "ActionScriptTokenMaker");
putMapping(SYNTAX_STYLE_ASSEMBLER_X86, pkg + "AssemblerX86TokenMaker");
putMapping(SYNTAX_STYLE_BBCODE, pkg + "BBCodeTokenMaker");
putMapping(SYNTAX_STYLE_C, pkg + "CTokenMaker");
putMapping(SYNTAX_STYLE_CLOJURE, pkg + "ClojureTokenMaker");
putMapping(SYNTAX_STYLE_CPLUSPLUS, pkg + "CPlusPlusTokenMaker");
putMapping(SYNTAX_STYLE_CSHARP, pkg + "CSharpTokenMaker");
putMapping(SYNTAX_STYLE_CSS, pkg + "CSSTokenMaker");
putMapping(SYNTAX_STYLE_D, pkg + "DTokenMaker");
putMapping(SYNTAX_STYLE_DART, pkg + "DartTokenMaker");
putMapping(SYNTAX_STYLE_DELPHI, pkg + "DelphiTokenMaker");
putMapping(SYNTAX_STYLE_DOCKERFILE, pkg + "DockerTokenMaker");
putMapping(SYNTAX_STYLE_DTD, pkg + "DtdTokenMaker");
putMapping(SYNTAX_STYLE_FORTRAN, pkg + "FortranTokenMaker");
putMapping(SYNTAX_STYLE_GROOVY, pkg + "GroovyTokenMaker");
putMapping(SYNTAX_STYLE_HOSTS, pkg + "HostsTokenMaker");
putMapping(SYNTAX_STYLE_HTACCESS, pkg + "HtaccessTokenMaker");
putMapping(SYNTAX_STYLE_HTML, pkg + "HTMLTokenMaker");
putMapping(SYNTAX_STYLE_INI, pkg + "IniTokenMaker");
putMapping(SYNTAX_STYLE_JAVA, pkg + "JavaTokenMaker");
putMapping(SYNTAX_STYLE_JAVASCRIPT, pkg + "JavaScriptTokenMaker");
putMapping(SYNTAX_STYLE_JSON_WITH_COMMENTS, pkg + "JshintrcTokenMaker");
putMapping(SYNTAX_STYLE_JSON, pkg + "JsonTokenMaker");
putMapping(SYNTAX_STYLE_JSP, pkg + "JSPTokenMaker");
putMapping(SYNTAX_STYLE_LATEX, pkg + "LatexTokenMaker");
putMapping(SYNTAX_STYLE_LESS, pkg + "LessTokenMaker");
putMapping(SYNTAX_STYLE_LISP, pkg + "LispTokenMaker");
putMapping(SYNTAX_STYLE_LUA, pkg + "LuaTokenMaker");
putMapping(SYNTAX_STYLE_MAKEFILE, pkg + "MakefileTokenMaker");
putMapping(SYNTAX_STYLE_MXML, pkg + "MxmlTokenMaker");
putMapping(SYNTAX_STYLE_NSIS, pkg + "NSISTokenMaker");
putMapping(SYNTAX_STYLE_PERL, pkg + "PerlTokenMaker");
putMapping(SYNTAX_STYLE_PHP, pkg + "PHPTokenMaker");
putMapping(SYNTAX_STYLE_PROPERTIES_FILE, pkg + "PropertiesFileTokenMaker");
putMapping(SYNTAX_STYLE_PYTHON, pkg + "PythonTokenMaker");
putMapping(SYNTAX_STYLE_RUBY, pkg + "RubyTokenMaker");
putMapping(SYNTAX_STYLE_SAS, pkg + "SASTokenMaker");
putMapping(SYNTAX_STYLE_SCALA, pkg + "ScalaTokenMaker");
putMapping(SYNTAX_STYLE_SQL, pkg + "SQLTokenMaker");
putMapping(SYNTAX_STYLE_TCL, pkg + "TclTokenMaker");
putMapping(SYNTAX_STYLE_TYPESCRIPT, pkg + "TypeScriptTokenMaker");
putMapping(SYNTAX_STYLE_UNIX_SHELL, pkg + "UnixShellTokenMaker");
putMapping(SYNTAX_STYLE_VISUAL_BASIC, pkg + "VisualBasicTokenMaker");
putMapping(SYNTAX_STYLE_WINDOWS_BATCH, pkg + "WindowsBatchTokenMaker");
putMapping(SYNTAX_STYLE_XML, pkg + "XMLTokenMaker");
putMapping(SYNTAX_STYLE_YAML, pkg + "YamlTokenMaker");
}
}
}
@@ -1,9 +1,7 @@
/*
* 03/16/2013
*
* DefaultTokenPainter - Standard implementation of a token painter.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -15,7 +13,6 @@ import java.awt.geom.Rectangle2D;
import javax.swing.text.TabExpander;
/**
* Standard implementation of a token painter.
*
@@ -23,111 +20,101 @@ import javax.swing.text.TabExpander;
* @version 1.0
*/
class DefaultTokenPainter implements TokenPainter {
/**
* Rectangle used for filling token backgrounds.
*/
private Rectangle2D.Float bgRect;
/**
* Micro-optimization; buffer used to compute tab width. If the width is
* Micro-optimization; buffer used to compute tab width. If the width is
* correct it's not re-allocated, to prevent lots of very small garbage.
* Only used when painting tab lines.
*/
private static char[] tabBuf;
DefaultTokenPainter() {
bgRect = new Rectangle2D.Float();
}
/**
* {@inheritDoc}
*/
@Override
public final float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e) {
return paint(token, g, x,y, host, e, 0);
public final float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e) {
return paint(token, g, x, y, host, e, 0);
}
/**
* {@inheritDoc}
*/
@Override
public float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart) {
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart) {
return paintImpl(token, g, x, y, host, e, clipStart, false, false);
}
/**
* {@inheritDoc}
*/
@Override
public float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
boolean paintBG) {
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart, boolean paintBG) {
return paintImpl(token, g, x, y, host, e, clipStart, !paintBG, false);
}
/**
* Paints the background of a token.
*
* @param x The x-coordinate of the token.
* @param y The y-coordinate of the token.
* @param width The width of the token (actually, the width of the part of
* the token to paint).
* @param height The height of the token.
* @param g The graphics context with which to paint.
* @param fontAscent The ascent of the token's font.
* @param host The text area.
* @param color The color with which to paint.
* @param x
* The x-coordinate of the token.
* @param y
* The y-coordinate of the token.
* @param width
* The width of the token (actually, the width of the part of
* the token to paint).
* @param height
* The height of the token.
* @param g
* The graphics context with which to paint.
* @param fontAscent
* The ascent of the token's font.
* @param host
* The text area.
* @param color
* The color with which to paint.
*/
protected void paintBackground(float x, float y, float width, float height,
Graphics2D g, int fontAscent, RSyntaxTextArea host,
Color color) {
protected void paintBackground(float x, float y, float width, float height, Graphics2D g, int fontAscent, RSyntaxTextArea host,
Color color) {
g.setColor(color);
bgRect.setRect(x,y-fontAscent, width,height);
//g.fill(bgRect);
g.fillRect((int)x, (int)(y-fontAscent), (int)width, (int)height);
bgRect.setRect(x, y - fontAscent, width, height);
// g.fill(bgRect);
g.fillRect((int) x, (int) (y - fontAscent), (int) width, (int) height);
}
/**
* Does the dirty-work of actually painting the token.
*/
protected float paintImpl(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
protected float paintImpl(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart,
boolean selected, boolean useSTC) {
int origX = (int)x;
int origX = (int) x;
int textOffs = token.getTextOffset();
char[] text = token.getTextArray();
int end = textOffs + token.length();
float nextX = x;
int flushLen = 0;
int flushIndex = textOffs;
Color fg = useSTC ? host.getSelectedTextColor() :
host.getForegroundForToken(token);
Color fg = useSTC ? host.getSelectedTextColor() : host.getForegroundForToken(token);
Color bg = selected ? null : host.getBackgroundForToken(token);
g.setFont(host.getFontForTokenType(token.getType()));
FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
for (int i=textOffs; i<end; i++) {
for (int i = textOffs; i < end; i++) {
switch (text[i]) {
case '\t':
nextX = e.nextTabStop(
x + fm.charsWidth(text, flushIndex,flushLen), 0);
if (bg!=null) {
paintBackground(x,y, nextX-x,fm.getHeight(),
g, fm.getAscent(), host, bg);
nextX = e.nextTabStop(x + fm.charsWidth(text, flushIndex, flushLen), 0);
if (bg != null) {
paintBackground(x, y, nextX - x, fm.getHeight(), g, fm.getAscent(), host, bg);
}
if (flushLen > 0) {
g.setColor(fg);
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
flushLen = 0;
}
flushIndex = i + 1;
@@ -138,142 +125,131 @@ class DefaultTokenPainter implements TokenPainter {
break;
}
}
nextX = x+fm.charsWidth(text, flushIndex,flushLen);
java.awt.Rectangle r = host.getMatchRectangle();
if (flushLen>0 && nextX>=clipStart) {
if (bg!=null) {
paintBackground(x,y, nextX-x,fm.getHeight(),
g, fm.getAscent(), host, bg);
if (token.length()==1 && r!=null && r.x==x) {
((RSyntaxTextAreaUI)host.getUI()).paintMatchedBracketImpl(
g, host, r);
nextX = x + fm.charsWidth(text, flushIndex, flushLen);
java.awt.Rectangle r = host.getMatchRectangle();
if (flushLen > 0 && nextX >= clipStart) {
if (bg != null) {
paintBackground(x, y, nextX - x, fm.getHeight(), g, fm.getAscent(), host, bg);
if (token.length() == 1 && r != null && r.x == x) {
((RSyntaxTextAreaUI) host.getUI()).paintMatchedBracketImpl(g, host, r);
}
}
g.setColor(fg);
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
}
if (host.getUnderlineForToken(token)) {
g.setColor(fg);
int y2 = (int)(y+1);
g.drawLine(origX,y2, (int)nextX,y2);
int y2 = (int) (y + 1);
g.drawLine(origX, y2, (int) nextX, y2);
}
// Don't check if it's whitespace - some TokenMakers may return types
// other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER).
// This also allows us to paint tab lines for MLC's.
if (host.getPaintTabLines() && origX==host.getMargin().left) {// && isWhitespace()) {
paintTabLines(token, origX, (int)y, (int)nextX, g, e, host);
if (host.getPaintTabLines() && origX == host.getMargin().left) {// &&
// isWhitespace())
// {
paintTabLines(token, origX, (int) y, (int) nextX, g, e, host);
}
return nextX;
}
/**
* {@inheritDoc}
*/
@Override
public float paintSelected(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, boolean useSTC) {
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, boolean useSTC) {
return paintSelected(token, g, x, y, host, e, 0, useSTC);
}
/**
* {@inheritDoc}
*/
@Override
public float paintSelected(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart,
boolean useSTC) {
return paintImpl(token, g, x, y, host, e, clipStart, true, useSTC);
}
/**
* Paints dotted "tab" lines; that is, lines that show where your caret
* would go to on the line if you hit "tab". This visual effect is usually
* would go to on the line if you hit "tab". This visual effect is usually
* done in the leading whitespace token(s) of lines.
*
* @param token The token to render.
* @param x The starting x-offset of this token. It is assumed that this
* is the left margin of the text area (may be non-zero due to
* insets), since tab lines are only painted for leading whitespace.
* @param y The baseline where this token was painted.
* @param endX The ending x-offset of this token.
* @param g The graphics context.
* @param e Used to expand tabs.
* @param host The text area.
* @param token
* The token to render.
* @param x
* The starting x-offset of this token. It is assumed that this
* is the left margin of the text area (may be non-zero due to
* insets), since tab lines are only painted for leading
* whitespace.
* @param y
* The baseline where this token was painted.
* @param endX
* The ending x-offset of this token.
* @param g
* The graphics context.
* @param e
* Used to expand tabs.
* @param host
* The text area.
*/
protected void paintTabLines(Token token, int x, int y, int endX,
Graphics2D g, TabExpander e, RSyntaxTextArea host) {
protected void paintTabLines(Token token, int x, int y, int endX, Graphics2D g, TabExpander e, RSyntaxTextArea host) {
// We allow tab lines to be painted in more than just Token.WHITESPACE,
// i.e. for MLC's and Token.IDENTIFIERS (for TokenMakers that return
// whitespace as identifiers for performance). But we only paint tab
// lines for the leading whitespace in the token. So, if this isn't a
// whitespace as identifiers for performance). But we only paint tab
// lines for the leading whitespace in the token. So, if this isn't a
// WHITESPACE token, figure out the leading whitespace's length.
if (token.getType()!=Token.WHITESPACE) {
if (token.getType() != Token.WHITESPACE) {
int offs = 0;
for (; offs<token.length(); offs++) {
for (; offs < token.length(); offs++) {
if (!RSyntaxUtilities.isWhitespace(token.charAt(offs))) {
break; // MLC text, etc.
}
}
if (offs<2) { // Must be at least two spaces to see tab line
if (offs < 2) { // Must be at least two spaces to see tab line
return;
}
//endX = x + (int)getWidthUpTo(offs, host, e, x);
endX = (int)token.getWidthUpTo(offs, host, e, 0);
// endX = x + (int)getWidthUpTo(offs, host, e, x);
endX = (int) token.getWidthUpTo(offs, host, e, 0);
}
// Get the length of a tab.
FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
int tabSize = host.getTabSize();
if (tabBuf==null || tabBuf.length<tabSize) {
if (tabBuf == null || tabBuf.length < tabSize) {
tabBuf = new char[tabSize];
for (int i=0; i<tabSize; i++) {
for (int i = 0; i < tabSize; i++) {
tabBuf[i] = ' ';
}
}
// Note different token types (MLC's, whitespace) could possibly be
// using different fonts, which means we can't cache the actual width
// of a tab as it may be different per-token-type. We could keep a
// of a tab as it may be different per-token-type. We could keep a
// per-token-type cache, but we'd have to clear it whenever they
// modified token styles.
int tabW = fm.charsWidth(tabBuf, 0, tabSize);
// Draw any tab lines. Here we're assuming that "x" is the left
// Draw any tab lines. Here we're assuming that "x" is the left
// margin of the editor.
g.setColor(host.getTabLineColor());
int x0 = x + tabW;
int y0 = y - fm.getAscent();
if ((y0&1)>0) {
if ((y0 & 1) > 0) {
// Only paint on even y-pixels to prevent doubling up between lines
y0++;
}
// TODO: Go to endX (inclusive) if this token is last token in the line
Token next = token.getNextToken();
if (next==null || !next.isPaintable()) {
if (next == null || !next.isPaintable()) {
endX++;
}
while (x0<endX) {
while (x0 < endX) {
int y1 = y0;
int y2 = y0 + host.getLineHeight();
while (y1<y2) {
while (y1 < y2) {
g.drawLine(x0, y1, x0, y1);
y1 += 2;
}
//g.drawLine(x0,y0, x0,y0+host.getLineHeight());
// g.drawLine(x0,y0, x0,y0+host.getLineHeight());
x0 += tabW;
}
}
}
}
@@ -1,14 +1,11 @@
/*
* 08/11/2009
*
* DocumentRange.java - A range of text in a document.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* A range of text in a document.
*
@@ -16,62 +13,63 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0
*/
public class DocumentRange implements Comparable<DocumentRange> {
private int startOffs;
private int endOffs;
/**
* Constructor.
*
* @param startOffs The starting offset in the document, inclusive.
* @param endOffs The ending offset in the document, exclusive.
* @throws IllegalArgumentException If <code>endOffs</code> is less than
* <code>startOffs</code>, or either argument is less than zero.
* @param startOffs
* The starting offset in the document, inclusive.
* @param endOffs
* The ending offset in the document, exclusive.
* @throws IllegalArgumentException
* If <code>endOffs</code> is less than
* <code>startOffs</code>, or either argument is less than zero.
*/
public DocumentRange(int startOffs, int endOffs) {
set(startOffs, endOffs);
}
/**
* Compares this document range to another.
*
* @param other Another document range.
* @param other
* Another document range.
* @return How the two should be sorted relative to each other.
*/
@Override
public int compareTo(DocumentRange other) {
if (other==null) {
if (other == null) {
return 1;
}
int diff = startOffs - other.startOffs;
if (diff!=0) {
if (diff != 0) {
return diff;
}
return endOffs - other.endOffs;
}
/**
* Returns whether this document range is equal to another one.
*
* @param other Another object, presumably a document range.
* @param other
* Another object, presumably a document range.
* @return Whether <code>other</code> is also a document range, and equal
* to this one.
*/
@Override
public boolean equals(Object other) {
if (other==this) {
if (other == this) {
return true;
}
if (other instanceof DocumentRange) {
return this.compareTo((DocumentRange)other)==0;
return this.compareTo((DocumentRange) other) == 0;
}
return false;
}
/**
* Gets the end offset of the range.
*
@@ -81,8 +79,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int getEndOffset() {
return endOffs;
}
/**
* Gets the starting offset of the range.
*
@@ -92,8 +89,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int getStartOffset() {
return startOffs;
}
/**
* Overridden simply as a best practice, since {@link #equals(Object)} is
* overridden.
@@ -104,10 +100,9 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int hashCode() {
return startOffs + endOffs;
}
/**
* Returns whether this document range has zero length. This can happen,
* Returns whether this document range has zero length. This can happen,
* for example, with regex searches of forms like
* <code>"foo|"</code>, where the right-hand sub-expression matches empty
* strings.
@@ -117,31 +112,29 @@ public class DocumentRange implements Comparable<DocumentRange> {
public boolean isZeroLength() {
return startOffs == endOffs;
}
/**
* Sets the document range.
*
* @param start The new start value, inclusive.
* @param end The new end value, exclusive.
* @throws IllegalArgumentException If <code>end</code> is less than
* <code>start</code>, or either argument is less than zero.
* @param start
* The new start value, inclusive.
* @param end
* The new end value, exclusive.
* @throws IllegalArgumentException
* If <code>end</code> is less than
* <code>start</code>, or either argument is less than zero.
*/
public void set(int start, int end) {
if (start<0 || end<0) {
throw new IllegalArgumentException(
"start and end must be >= 0 (" + start + "-" + end + ")");
if (start < 0 || end < 0) {
throw new IllegalArgumentException("start and end must be >= 0 (" + start + "-" + end + ")");
}
if (end<start) {
throw new IllegalArgumentException(
"'end' cannot be less than 'start' (" +
start + "-" + end + ")");
if (end < start) {
throw new IllegalArgumentException("'end' cannot be less than 'start' (" + start + "-" + end + ")");
}
this.startOffs = start;
this.endOffs = end;
}
/**
* Returns a string representation of this object.
*
@@ -151,12 +144,12 @@ public class DocumentRange implements Comparable<DocumentRange> {
public String toString() {
return "[DocumentRange: " + startOffs + "-" + endOffs + "]";
}
/**
* Translates this document range by a given amount.
*
* @param amount The amount to translate this range by.
* @param amount
* The amount to translate this range by.
* @return This (modified) range.
*/
public DocumentRange translate(int amount) {
@@ -164,6 +157,4 @@ public class DocumentRange implements Comparable<DocumentRange> {
endOffs += amount;
return this;
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,9 +1,7 @@
/*
* 11/13/2008
*
* FileFileLocation.java - The location of a local file.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -15,7 +13,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* The location of a local file.
*
@@ -23,17 +20,17 @@ import java.io.OutputStream;
* @version 1.0
*/
class FileFileLocation extends FileLocation {
/**
* The file. This may or may not actually exist.
* The file. This may or may not actually exist.
*/
private File file;
/**
* Constructor.
*
* @param file The local file.
* @param file
* The local file.
*/
FileFileLocation(File file) {
try {
@@ -43,8 +40,7 @@ class FileFileLocation extends FileLocation {
this.file = file;
}
}
/**
* {@inheritDoc}
*/
@@ -52,8 +48,7 @@ class FileFileLocation extends FileLocation {
protected long getActualLastModified() {
return file.lastModified();
}
/**
* Returns the full path to the file.
*
@@ -64,8 +59,7 @@ class FileFileLocation extends FileLocation {
public String getFileFullPath() {
return file.getAbsolutePath();
}
/**
* {@inheritDoc}
*/
@@ -73,8 +67,7 @@ class FileFileLocation extends FileLocation {
public String getFileName() {
return file.getName();
}
/**
* {@inheritDoc}
*/
@@ -82,8 +75,7 @@ class FileFileLocation extends FileLocation {
protected InputStream getInputStream() throws IOException {
return new FileInputStream(file);
}
/**
* {@inheritDoc}
*/
@@ -91,8 +83,7 @@ class FileFileLocation extends FileLocation {
protected OutputStream getOutputStream() throws IOException {
return new FileOutputStream(file);
}
/**
* Always returns <code>true</code>.
*
@@ -103,8 +94,7 @@ class FileFileLocation extends FileLocation {
public boolean isLocal() {
return true;
}
/**
* Since file locations of this type are guaranteed to be local, this
* method returns whether the file exists.
@@ -116,6 +106,4 @@ class FileFileLocation extends FileLocation {
public boolean isLocalAndExists() {
return file.exists();
}
}
}
@@ -1,67 +1,61 @@
/*
* 11/13/2008
*
* FileLocation.java - Holds the location of a local or remote file.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Holds the location of a local or remote file. This provides a common way
* Holds the location of a local or remote file. This provides a common way
* to read, write, and check properties of both local and remote files.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class FileLocation {
/**
* Creates a {@link FileLocation} instance for the specified local file.
*
* @param fileFullPath The full path to a local file.
* @param fileFullPath
* The full path to a local file.
* @return The file's location.
*/
public static FileLocation create(String fileFullPath) {
if (fileFullPath.startsWith("http://") ||
fileFullPath.startsWith("https://") ||
fileFullPath.startsWith("ftp://")) {
if (fileFullPath.startsWith("http://") || fileFullPath.startsWith("https://") || fileFullPath.startsWith("ftp://")) {
try {
return new URLFileLocation(new URL(fileFullPath));
} catch (MalformedURLException mue) {
throw new IllegalArgumentException(
"Not a valid URL: " + fileFullPath, mue);
throw new IllegalArgumentException("Not a valid URL: " + fileFullPath, mue);
}
}
return new FileFileLocation(new File(fileFullPath));
}
/**
* Creates a {@link FileLocation} instance for the specified local file.
*
* @param file A local file.
* @param file
* A local file.
* @return The file's location.
*/
public static FileLocation create(File file) {
return new FileFileLocation(file);
}
/**
* Creates a {@link FileLocation} instance for the specified file.
*
* @param url The URL of a file.
* @param url
* The URL of a file.
* @return The file's location.
*/
public static FileLocation create(URL url) {
@@ -70,8 +64,7 @@ public abstract class FileLocation {
}
return new URLFileLocation(url);
}
/**
* Returns the last time this file was modified, or
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be
@@ -80,18 +73,16 @@ public abstract class FileLocation {
* @return The last time this file was modified.
*/
protected abstract long getActualLastModified();
/**
* Returns the full path to the file. This will be stripped of
* Returns the full path to the file. This will be stripped of
* sensitive information such as passwords for remote files.
*
* @return The full path to the file.
* @see #getFileName()
*/
public abstract String getFileFullPath();
/**
* Returns the name of the file.
*
@@ -99,27 +90,26 @@ public abstract class FileLocation {
* @see #getFileFullPath()
*/
public abstract String getFileName();
/**
* Opens an input stream for reading from this file.
*
* @return The input stream.
* @throws IOException If the file does not exist, or some other IO error
* occurs.
* @throws IOException
* If the file does not exist, or some other IO error
* occurs.
*/
protected abstract InputStream getInputStream() throws IOException;
/**
* Opens an output stream for writing this file.
*
* @return An output stream.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/
protected abstract OutputStream getOutputStream() throws IOException;
/**
* Returns whether this file location is a local file.
*
@@ -127,8 +117,7 @@ public abstract class FileLocation {
* @see #isLocalAndExists()
*/
public abstract boolean isLocal();
/**
* Returns whether this file location is a local file that already
* exists.
@@ -137,8 +126,7 @@ public abstract class FileLocation {
* @see #isLocal()
*/
public abstract boolean isLocalAndExists();
/**
* Returns whether this file location is a remote location.
*
@@ -147,6 +135,4 @@ public abstract class FileLocation {
public boolean isRemote() {
return !isLocal();
}
}
}
@@ -1,10 +1,8 @@
/*
* 03/07/2012
*
* FoldingAwareIconRowHeader - Icon row header that paints itself correctly
* even when code folding is enabled.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -12,6 +10,7 @@ package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.Icon;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
@@ -21,7 +20,6 @@ import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
import org.fife.ui.rtextarea.GutterIconInfo;
import org.fife.ui.rtextarea.IconRowHeader;
/**
* A row header component that takes code folding into account when painting
* itself.
@@ -30,222 +28,196 @@ import org.fife.ui.rtextarea.IconRowHeader;
* @version 1.0
*/
public class FoldingAwareIconRowHeader extends IconRowHeader {
/**
* Constructor.
*
* @param textArea The parent text area.
* @param textArea
* The parent text area.
*/
public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) {
super(textArea);
}
/**
* {@inheritDoc}
*/
@Override
protected void paintComponent(Graphics g) {
// When line wrap is not enabled, take the faster code path.
if (textArea==null) {
if (textArea == null) {
return;
}
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
FoldManager fm = rsta.getFoldManager();
if (!fm.isCodeFoldingSupportedAndEnabled()) {
super.paintComponent(g);
return;
}
visibleRect = g.getClipBounds(visibleRect);
if (visibleRect==null) { // ???
if (visibleRect == null) { // ???
visibleRect = getVisibleRect();
}
//System.out.println("IconRowHeader repainting: " + visibleRect);
if (visibleRect==null) {
// System.out.println("IconRowHeader repainting: " + visibleRect);
if (visibleRect == null) {
return;
}
paintBackgroundImpl(g, visibleRect);
if (textArea.getLineWrap()) {
paintComponentWrapped(g);
return;
}
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
textAreaInsets = textArea.getInsets(textAreaInsets);
if (visibleRect.y<textAreaInsets.top) {
if (visibleRect.y < textAreaInsets.top) {
visibleRect.height -= (textAreaInsets.top - visibleRect.y);
visibleRect.y = textAreaInsets.top;
}
// Get the first line to paint.
int cellHeight = textArea.getLineHeight();
int topLine = (visibleRect.y-textAreaInsets.top)/cellHeight;
int topLine = (visibleRect.y - textAreaInsets.top) / cellHeight;
// Get where to start painting (top of the row).
// We need to be "scrolled up" up just enough for the missing part of
// the first line.
int y = topLine*cellHeight + textAreaInsets.top;
int y = topLine * cellHeight + textAreaInsets.top;
// AFTER calculating visual offset to paint at, account for folding.
topLine += fm.getHiddenLineCountAbove(topLine, true);
// Paint the active line range.
if (activeLineRangeStart>-1 && activeLineRangeEnd>-1) {
if (activeLineRangeStart > -1 && activeLineRangeEnd > -1) {
Color activeLineRangeColor = getActiveLineRangeColor();
g.setColor(activeLineRangeColor);
try {
int realY1 = rsta.yForLine(activeLineRangeStart);
if (realY1>-1) { // Not in a collapsed fold...
int y1 = realY1;//Math.max(y, realY1);
if (realY1 > -1) { // Not in a collapsed fold...
int y1 = realY1;// Math.max(y, realY1);
int y2 = rsta.yForLine(activeLineRangeEnd);
if (y2==-1) { // In a collapsed fold
if (y2 == -1) { // In a collapsed fold
y2 = y1;
}
y2 += cellHeight - 1;
if (y2<visibleRect.y || y1>visibleRect.y+visibleRect.height) {
//System.out.println("... nothing to paint, bailing...");
if (y2 < visibleRect.y || y1 > visibleRect.y + visibleRect.height) {
// System.out.println("... nothing to paint,
// bailing...");
return;
}
y1 = Math.max(y, realY1);
y2 = Math.min(y2, visibleRect.y+visibleRect.height);
//System.out.println(y1 + "... " + y2 + "; " + realY1 + ", " + visibleRect);
y2 = Math.min(y2, visibleRect.y + visibleRect.height);
// System.out.println(y1 + "... " + y2 + "; " + realY1 + ",
// " + visibleRect);
int j = y1;
while (j<=y2) {
int yEnd = Math.min(y2, j+getWidth());
int xEnd = yEnd-j;
g.drawLine(0,j, xEnd,yEnd);
while (j <= y2) {
int yEnd = Math.min(y2, j + getWidth());
int xEnd = yEnd - j;
g.drawLine(0, j, xEnd, yEnd);
j += 2;
}
int i = 2;
while (i<getWidth()) {
while (i < getWidth()) {
int yEnd = y1 + getWidth() - i;
g.drawLine(i,y1, getWidth(),yEnd);
g.drawLine(i, y1, getWidth(), yEnd);
i += 2;
}
if (realY1>=y && realY1<visibleRect.y+visibleRect.height) {
g.drawLine(0,realY1, getWidth(),realY1);
if (realY1 >= y && realY1 < visibleRect.y + visibleRect.height) {
g.drawLine(0, realY1, getWidth(), realY1);
}
if (y2>=y && y2<visibleRect.y+visibleRect.height) {
g.drawLine(0,y2, getWidth(),y2);
if (y2 >= y && y2 < visibleRect.y + visibleRect.height) {
g.drawLine(0, y2, getWidth(), y2);
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
// Paint icons
if (trackingIcons!=null) {
if (trackingIcons != null) {
int lastLine = textArea.getLineCount() - 1;
for (int i=trackingIcons.size()-1; i>=0; i--) { // Last to first
for (int i = trackingIcons.size() - 1; i >= 0; i--) { // Last to
// first
GutterIconInfo ti = getTrackingIcon(i);
int offs = ti.getMarkedOffset();
if (offs>=0 && offs<=doc.getLength()) {
if (offs >= 0 && offs <= doc.getLength()) {
int line = root.getElementIndex(offs);
if (line<=lastLine && line>=topLine) {
if (line <= lastLine && line >= topLine) {
try {
Icon icon = ti.getIcon();
if (icon!=null) {
if (icon != null) {
int lineY = rsta.yForLine(line);
if (lineY>=y && lineY<=visibleRect.y+visibleRect.height) {
int y2 = lineY + (cellHeight-icon.getIconHeight())/2;
if (lineY >= y && lineY <= visibleRect.y + visibleRect.height) {
int y2 = lineY + (cellHeight - icon.getIconHeight()) / 2;
icon.paintIcon(this, g, 0, y2);
lastLine = line-1; // Paint only 1 icon per line
lastLine = line - 1; // Paint only 1 icon
// per line
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
else if (line<topLine) {
} else if (line < topLine) {
break; // All other lines are above us, so quit now
}
}
}
}
}
/**
* Paints icons when line wrapping is enabled. Note that this does not
* Paints icons when line wrapping is enabled. Note that this does not
* override the parent class's implementation to avoid this version being
* called when line wrapping is disabled.
*/
private void paintComponentWrapped(Graphics g) {
// The variables we use are as follows:
// - visibleRect is the "visible" area of the text area; e.g.
// [0,100, 300,100+(lineCount*cellHeight)-1].
// actualTop.y is the topmost-pixel in the first logical line we
// paint. Note that we may well not paint this part of the logical
// paint. Note that we may well not paint this part of the logical
// line, as it may be broken into many physical lines, with the first
// few physical lines scrolled past. Note also that this is NOT the
// few physical lines scrolled past. Note also that this is NOT the
// visible rect of this line number list; this line number list has
// visible rect == [0,0, insets.left-1,visibleRect.height-1].
// We avoid using modelToView/viewToModel where possible, as these
// methods trigger a parsing of the line into syntax tokens, which is
// costly. It's cheaper to just grab the child views' bounds.
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
// boolean currentLineHighlighted = textArea.getHighlightCurrentLine();
// costly. It's cheaper to just grab the child views' bounds.
RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
// boolean currentLineHighlighted = textArea.getHighlightCurrentLine();
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
int topPosition = textArea.viewToModel(
new Point(visibleRect.x,visibleRect.y));
int topPosition = textArea.viewToModel(new Point(visibleRect.x, visibleRect.y));
int topLine = root.getElementIndex(topPosition);
int topY = visibleRect.y;
int bottomY = visibleRect.y + visibleRect.height;
int cellHeight = textArea.getLineHeight();
// Paint icons
if (trackingIcons!=null) {
if (trackingIcons != null) {
int lastLine = textArea.getLineCount() - 1;
for (int i=trackingIcons.size()-1; i>=0; i--) { // Last to first
for (int i = trackingIcons.size() - 1; i >= 0; i--) { // Last to
// first
GutterIconInfo ti = getTrackingIcon(i);
Icon icon = ti.getIcon();
if (icon!=null) {
if (icon != null) {
int iconH = icon.getIconHeight();
int offs = ti.getMarkedOffset();
if (offs>=0 && offs<=doc.getLength()) {
if (offs >= 0 && offs <= doc.getLength()) {
int line = root.getElementIndex(offs);
if (line<=lastLine && line>=topLine) {
if (line <= lastLine && line >= topLine) {
try {
int lineY = rsta.yForLine(line);
if (lineY<=bottomY && (lineY+iconH>=topY)) {
int y2 = lineY + (cellHeight-iconH)/2;
if (lineY <= bottomY && (lineY + iconH >= topY)) {
int y2 = lineY + (cellHeight - iconH) / 2;
ti.getIcon().paintIcon(this, g, 0, y2);
lastLine = line-1; // Paint only 1 icon per line
lastLine = line - 1; // Paint only 1 icon
// per line
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
else if (line<topLine) {
} else if (line < topLine) {
break; // All other lines are above us, so quit now
}
}
}
}
}
}
}
}
@@ -1,11 +1,9 @@
/*
* 12/01/2014
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -21,181 +19,81 @@ import javax.swing.text.Element;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks occurrences of the current token for HTML. Tags that require a
* Marks occurrences of the current token for HTML. Tags that require a
* closing tag have their "opposite" tag closed.
*
* @author Robert Futrell
* @version 1.0
*/
public class HtmlOccurrenceMarker implements OccurrenceMarker {
private static final char[] CLOSE_TAG_START = { '<', '/' };
private static final char[] TAG_SELF_CLOSE = { '/', '>' };
private static final Set<String> TAGS_REQUIRING_CLOSING =
getRequiredClosingTags();
private static final Set<String> TAGS_REQUIRING_CLOSING = getRequiredClosingTags();
public static final Set<String> getRequiredClosingTags() {
final String[] tags = {
"html",
"head",
"title",
"style",
"script",
"noscript",
"body",
"section",
"nav",
"article",
"aside",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"header",
"footer",
"address",
"pre",
"dialog",
"blockquote",
"ol",
"ul",
"dl",
"a",
"q",
"cite",
"em",
"strong",
"small",
"mark",
"dfn",
"abbr",
"time",
"progress",
"meter",
"code",
"var",
"samp",
"kbd",
"sub",
"sup",
"span",
"i",
"b",
"bdo",
"ruby",
"rt",
"rp",
"ins",
"del",
"figure",
"iframe",
"object",
"video",
"audio",
"canvas",
"map",
"table",
"caption",
"form",
"fieldset",
"label",
"button",
"select",
"datalist",
"textarea",
"output",
"details",
"bb",
"menu",
"legend",
"div",
// Obsolete elements
"acronym",
"applet",
"big",
"blink",
"center",
"dir",
"font",
"frame",
"frameset",
"isindex",
"listing",
"marquee",
"nobr",
"noembed",
"noframes",
"plaintext",
"s",
"spacer",
"strike",
"tt",
"u",
"xmp",
};
final String[] tags = { "html", "head", "title", "style", "script", "noscript", "body", "section", "nav", "article", "aside", "h1",
"h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "pre", "dialog", "blockquote", "ol", "ul", "dl", "a", "q",
"cite", "em", "strong", "small", "mark", "dfn", "abbr", "time", "progress", "meter", "code", "var", "samp", "kbd", "sub",
"sup", "span", "i", "b", "bdo", "ruby", "rt", "rp", "ins", "del", "figure", "iframe", "object", "video", "audio", "canvas",
"map", "table", "caption", "form", "fieldset", "label", "button", "select", "datalist", "textarea", "output", "details",
"bb", "menu", "legend", "div",
// Obsolete elements
"acronym", "applet", "big", "blink", "center", "dir", "font", "frame", "frameset", "isindex", "listing", "marquee", "nobr",
"noembed", "noframes", "plaintext", "s", "spacer", "strike", "tt", "u", "xmp", };
return new HashSet<String>(Arrays.asList(tags));
}
/**
* If the caret is inside of a tag, this method returns the token
* representing the tag name; otherwise, <code>null</code> is returned.<p>
* representing the tag name; otherwise, <code>null</code> is returned.
* <p>
*
* Currently, this method only checks for tag names on the same line as
* the caret, for simplicity. In the future it could check prior lines
* the caret, for simplicity. In the future it could check prior lines
* until the tag name is found.
*
* @param textArea The text area.
* @param occurrenceMarker The occurrence marker.
* @return The token to mark occurrences of. Note that, if the
* @param textArea
* The text area.
* @param occurrenceMarker
* The occurrence marker.
* @return The token to mark occurrences of. Note that, if the
* specified occurrence marker identifies tokens other than
* tag names, these other element types may be returned.
*/
public static final Token getTagNameTokenForCaretOffset(
RSyntaxTextArea textArea, OccurrenceMarker occurrenceMarker) {
public static final Token getTagNameTokenForCaretOffset(RSyntaxTextArea textArea, OccurrenceMarker occurrenceMarker) {
// Get the tag name token.
// For now, we only check for tags on the current line, for simplicity.
int dot = textArea.getCaretPosition();
Token t = textArea.getTokenListForLine(textArea.getCaretLineNumber());
Token toMark = null;
while (t!=null && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_NAME) {
while (t != null && t.isPaintable()) {
if (t.getType() == Token.MARKUP_TAG_NAME) {
toMark = t;
}
// Check for the token containing the caret before checking
// if it's the close token.
if (t.getEndOffset()==dot || t.containsPosition(dot)) {
if (t.getEndOffset() == dot || t.containsPosition(dot)) {
// Some languages, like PHP, mark functions/variables (PHP,
// JavaScirpt) as well as HTML tags.
if (occurrenceMarker.isValidType(textArea, t) &&
t.getType()!=Token.MARKUP_TAG_NAME) {
if (occurrenceMarker.isValidType(textArea, t) && t.getType() != Token.MARKUP_TAG_NAME) {
return t;
}
if (t.containsPosition(dot)) {
break;
}
}
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('>') || t.is(TAG_SELF_CLOSE)) {
toMark = null;
}
}
t = t.getNextToken();
}
return toMark;
}
/**
* {@inheritDoc}
*/
@@ -203,8 +101,7 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
public Token getTokenToMark(RSyntaxTextArea textArea) {
return getTagNameTokenForCaretOffset(textArea, this);
}
/**
* {@inheritDoc}
*/
@@ -212,20 +109,16 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType());
}
/**
* {@inheritDoc}
*/
@Override
public void markOccurrences(RSyntaxDocument doc, Token t,
RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
if (t.getType()!=Token.MARKUP_TAG_NAME) {
public void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
if (t.getType() != Token.MARKUP_TAG_NAME) {
DefaultOccurrenceMarker.markOccurrencesOfToken(doc, t, h, p);
return;
}
String lexemeStr = t.getLexeme();
char[] lexeme = lexemeStr.toCharArray();
lexemeStr = lexemeStr.toLowerCase();
@@ -234,25 +127,23 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
int lineCount = root.getElementCount();
int curLine = root.getElementIndex(t.getOffset());
int depth = 0;
// For now, we only check for tags on the current line, for
// simplicity. Tags spanning multiple lines aren't common anyway.
// simplicity. Tags spanning multiple lines aren't common anyway.
boolean found = false;
boolean forward = true;
t = doc.getTokenListForLine(curLine);
while (t!=null && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('<') && t.getOffset()+1==tokenOffs) {
while (t != null && t.isPaintable()) {
if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('<') && t.getOffset() + 1 == tokenOffs) {
// Don't try to match a tag that is optionally closed (or
// closing is forbidden entirely).
if (TAGS_REQUIRING_CLOSING.contains(lexemeStr)) {
found = true;
}
break;
}
else if (t.is(CLOSE_TAG_START) && t.getOffset()+2==tokenOffs) {
} else if (t.is(CLOSE_TAG_START) && t.getOffset() + 2 == tokenOffs) {
// Searching backward, we assume we can find the opening
// tag. Don't really care if it's valid or not.
// tag. Don't really care if it's valid or not.
found = true;
forward = false;
break;
@@ -260,26 +151,20 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
}
t = t.getNextToken();
}
if (!found) {
return;
}
if (forward) {
t = t.getNextToken().getNextToken();
do {
while (t!=null && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
while (t != null && t.isPaintable()) {
if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
if (t.is(CLOSE_TAG_START)) {
Token match = t.getNextToken();
if (match!=null && match.is(lexeme)) {
if (depth>0) {
if (match != null && match.is(lexeme)) {
if (depth > 0) {
depth--;
}
else {
} else {
try {
int end = match.getOffset() + match.length();
h.addMarkedOccurrenceHighlight(match.getOffset(), end, p);
@@ -291,65 +176,50 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
return; // We're done!
}
}
}
else if (t.isSingleChar('<')) {
} else if (t.isSingleChar('<')) {
t = t.getNextToken();
if (t!=null && t.is(lexeme)) {
if (t != null && t.is(lexeme)) {
depth++;
}
}
}
t = t==null ? null : t.getNextToken();
t = t == null ? null : t.getNextToken();
}
if (++curLine<lineCount) {
if (++curLine < lineCount) {
t = doc.getTokenListForLine(curLine);
}
} while (curLine<lineCount);
}
else { // !forward
} while (curLine < lineCount);
} else { // !forward
// Idea: Get all opening and closing tags of the relevant type on
// the current line. Find the opening tag paired to the closing
// the current line. Find the opening tag paired to the closing
// tag we found originally; if it's not on this line, keep going
// to the previous line.
List<Entry> openCloses = new ArrayList<Entry>();
boolean inPossibleMatch = false;
t = doc.getTokenListForLine(curLine);
final int endBefore = tokenOffs - 2; // Stop before "</".
do {
while (t!=null && t.getOffset()<endBefore && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
while (t != null && t.getOffset() < endBefore && t.isPaintable()) {
if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('<')) {
Token next = t.getNextToken();
if (next!=null) {
if (next != null) {
if (next.is(lexeme)) {
openCloses.add(new Entry(true, next));
inPossibleMatch = true;
}
else {
} else {
inPossibleMatch = false;
}
t = next;
}
}
else if (t.isSingleChar('>')) {
} else if (t.isSingleChar('>')) {
inPossibleMatch = false;
}
else if (inPossibleMatch && t.is(TAG_SELF_CLOSE)) {
openCloses.remove(openCloses.size()-1);
} else if (inPossibleMatch && t.is(TAG_SELF_CLOSE)) {
openCloses.remove(openCloses.size() - 1);
inPossibleMatch = false;
}
else if (t.is(CLOSE_TAG_START)) {
} else if (t.is(CLOSE_TAG_START)) {
Token next = t.getNextToken();
if (next!=null) {
if (next != null) {
// Invalid XML might not have a match
if (next.is(lexeme)) {
openCloses.add(new Entry(false, next));
@@ -360,11 +230,10 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
}
t = t.getNextToken();
}
for (int i=openCloses.size()-1; i>=0; i--) {
for (int i = openCloses.size() - 1; i >= 0; i--) {
Entry entry = openCloses.get(i);
depth += entry.open ? -1 : 1;
if (depth==-1) {
if (depth == -1) {
try {
Token match = entry.t;
int end = match.getOffset() + match.length();
@@ -378,34 +247,25 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
return;
}
}
openCloses.clear();
if (--curLine>=0) {
if (--curLine >= 0) {
t = doc.getTokenListForLine(curLine);
}
} while (curLine>=0);
} while (curLine >= 0);
}
}
/**
* Used internally when searching backward for a matching "open" tag.
*/
private static class Entry {
private boolean open;
private Token t;
Entry(boolean open, Token t) {
this.open = open;
this.t = t;
}
}
}
}
@@ -1,27 +1,25 @@
/*
* 02/16/2012
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* Generates hyperlinks in a document. If one of these is installed on an
* Generates hyperlinks in a document. If one of these is installed on an
* <code>RSyntaxTextArea</code> it is queried when the mouse is moved and
* hyperlinks are enabled. If the user is not hovering over a "real" hyperlink
* hyperlinks are enabled. If the user is not hovering over a "real" hyperlink
* (e.g. "http://www.google.com"), the link generator is asked if a text region
* at the mouse position should be considered a hyperlink. If so, a result
* at the mouse position should be considered a hyperlink. If so, a result
* object is returned, describing exactly what region of text is the link, and
* where it goes to.<p>
* where it goes to.
* <p>
*
* This interface is typically used by applications providing advanced support
* for programming languages, such as IDEs. For example, an implementation of
* for programming languages, such as IDEs. For example, an implementation of
* this class could identify the token under the mouse position as a "variable,"
* and the hyperlink returned would select the variable's declaration in the
* document.
@@ -30,20 +28,19 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0
*/
public interface LinkGenerator {
/**
* If a region of text under the mouse position should be considered a
* hyperlink, a result object is returned. This object describes what
* hyperlink, a result object is returned. This object describes what
* region of text is the link, and what action to perform if the link is
* clicked.
*
* @param textArea The text component.
* @param offs The offset in the document under the mouse position.
* @param textArea
* The text component.
* @param offs
* The offset in the document under the mouse position.
* @return The link information, or <code>null</code> if no link is at the
* specified offset.
*/
LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offs);
}
}
@@ -1,22 +1,19 @@
/*
* 02/16/2012
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.event.HyperlinkEvent;
/**
* A result object from a {@link LinkGenerator}. Implementations of this class
* A result object from a {@link LinkGenerator}. Implementations of this class
* specify what action to execute when the user clicks on the "link" specified
* by the <code>LinkGenerator</code>. Typically, this will do something like
* by the <code>LinkGenerator</code>. Typically, this will do something like
* select another region of text in the document (the declaration of the
* variable at the mouse position), or open another file in the parent
* application, etc.
@@ -26,11 +23,10 @@ import javax.swing.event.HyperlinkEvent;
* @see SelectRegionLinkGeneratorResult
*/
public interface LinkGeneratorResult {
/**
* Executes the action associated with this object. If the result is a
* URL to open, a standard hyperlink event can be returned. Alternatively,
* Executes the action associated with this object. If the result is a
* URL to open, a standard hyperlink event can be returned. Alternatively,
* <code>null</code> can be returned and the action performed in this
* method itself.
*
@@ -39,8 +35,7 @@ public interface LinkGeneratorResult {
* directly.
*/
HyperlinkEvent execute();
/**
* Returns the starting offset of the link specified by the parent
* <code>LinkGenerator</code>.
@@ -48,6 +43,4 @@ public interface LinkGeneratorResult {
* @return The offset.
*/
int getSourceOffset();
}
}
@@ -1,10 +1,8 @@
/*
* 01/06/2009
*
* MarkOccurrencesSupport.java - Handles marking all occurrences of the
* currently selected identifier in a text area.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -20,7 +18,6 @@ import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks all occurrences of the token at the current caret position, if it is
* an identifier.
@@ -30,50 +27,48 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
* @see OccurrenceMarker
*/
class MarkOccurrencesSupport implements CaretListener, ActionListener {
private RSyntaxTextArea textArea;
private Timer timer;
private SmartHighlightPainter p;
/**
* The default color used to mark occurrences.
*/
static final Color DEFAULT_COLOR = new Color(224, 224, 224);
/**
* The default delay.
*/
static final int DEFAULT_DELAY_MS = 1000;
/**
* Constructor. Creates a listener with a 1 second delay.
* Constructor. Creates a listener with a 1 second delay.
*/
MarkOccurrencesSupport() {
this(DEFAULT_DELAY_MS);
}
/**
* Constructor.
*
* @param delay The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
* @param delay
* The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
*/
MarkOccurrencesSupport(int delay) {
this(delay, DEFAULT_COLOR);
}
/**
* Constructor.
*
* @param delay The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
* @param color The color to use to mark the occurrences. This cannot be
* <code>null</code>.
* @param delay
* The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
* @param color
* The color to use to mark the occurrences. This cannot be
* <code>null</code>.
*/
MarkOccurrencesSupport(int delay, Color color) {
timer = new Timer(delay, this);
@@ -81,89 +76,76 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
p = new SmartHighlightPainter();
setColor(color);
}
/**
* Called after the caret has been moved and a fixed time delay has
* elapsed. This locates and highlights all occurrences of the identifier
* at the caret position, if any.<p>
* elapsed. This locates and highlights all occurrences of the identifier
* at the caret position, if any.
* <p>
*
* Callers should not call this method directly, but should rather prefer
* {@link #doMarkOccurrences()} to mark occurrences.
*
* @param e The event.
* @param e
* The event.
* @see #doMarkOccurrences()
*/
@Override
public void actionPerformed(ActionEvent e) {
// Don't do anything if they are selecting text.
Caret c = textArea.getCaret();
if (c.getDot()!=c.getMark()) {
if (c.getDot() != c.getMark()) {
return;
}
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
OccurrenceMarker occurrenceMarker = doc.getOccurrenceMarker();
boolean occurrencesChanged = false;
if (occurrenceMarker!=null) {
if (occurrenceMarker != null) {
doc.readLock();
try {
Token t = occurrenceMarker.getTokenToMark(textArea);
if (t!=null && occurrenceMarker.isValidType(textArea, t) &&
!RSyntaxUtilities.isNonWordChar(t)) {
if (t != null && occurrenceMarker.isValidType(textArea, t) && !RSyntaxUtilities.isNonWordChar(t)) {
clear();
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
occurrenceMarker.markOccurrences(doc, t, h, p);
//textArea.repaint();
// textArea.repaint();
// TODO: Do a textArea.repaint() instead of repainting each
// marker as it's added if count is huge
occurrencesChanged = true;
}
} finally {
doc.readUnlock();
//time = System.currentTimeMillis() - time;
//System.out.println("MarkOccurrencesSupport took: " + time + " ms");
// time = System.currentTimeMillis() - time;
// System.out.println("MarkOccurrencesSupport took: " + time + "
// ms");
}
}
if (occurrencesChanged) {
textArea.fireMarkedOccurrencesChanged();
}
}
/**
* Called when the caret moves in the text area.
*
* @param e The event.
* @param e
* The event.
*/
@Override
public void caretUpdate(CaretEvent e) {
timer.restart();
}
/**
* Removes all highlights added to the text area by this listener.
*/
void clear() {
if (textArea!=null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
if (textArea != null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
h.clearMarkOccurrencesHighlights();
}
}
/**
* Immediately marks all occurrences of the token at the current caret
* position.
@@ -172,8 +154,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
timer.stop();
actionPerformed(null);
}
/**
* Returns the color being used to mark occurrences.
*
@@ -181,10 +162,9 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
* @see #setColor(Color)
*/
public Color getColor() {
return (Color)p.getPaint();
return (Color) p.getPaint();
}
/**
* Returns the delay, in milliseconds.
*
@@ -194,8 +174,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
public int getDelay() {
return timer.getDelay();
}
/**
* Returns whether a border is painted around marked occurrences.
*
@@ -206,84 +185,81 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
public boolean getPaintBorder() {
return p.getPaintBorder();
}
/**
* Installs this listener on a text area. If it is already installed on
* Installs this listener on a text area. If it is already installed on
* another text area, it is uninstalled first.
*
* @param textArea The text area to install on.
* @param textArea
* The text area to install on.
*/
public void install(RSyntaxTextArea textArea) {
if (this.textArea!=null) {
if (this.textArea != null) {
uninstall();
}
this.textArea = textArea;
textArea.addCaretListener(this);
if (textArea.getMarkOccurrencesColor()!=null) {
if (textArea.getMarkOccurrencesColor() != null) {
setColor(textArea.getMarkOccurrencesColor());
}
}
/**
* Sets the color to use when marking occurrences.
*
* @param color The color to use.
* @param color
* The color to use.
* @see #getColor()
* @see #setPaintBorder(boolean)
*/
public void setColor(Color color) {
p.setPaint(color);
if (textArea!=null) {
if (textArea != null) {
clear();
caretUpdate(null); // Force a highlight repaint.
}
}
/**
* Sets the delay between the last caret position change and when the
* text is scanned for matching identifiers. A delay is needed to prevent
* text is scanned for matching identifiers. A delay is needed to prevent
* repeated scanning while the user is typing.
*
* @param delay The new delay.
* @param delay
* The new delay.
* @see #getDelay()
*/
public void setDelay(int delay) {
timer.setInitialDelay(delay);
}
/**
* Toggles whether a border is painted around marked highlights.
*
* @param paint Whether to paint a border.
* @param paint
* Whether to paint a border.
* @see #getPaintBorder()
* @see #setColor(Color)
*/
public void setPaintBorder(boolean paint) {
if (paint!=p.getPaintBorder()) {
if (paint != p.getPaintBorder()) {
p.setPaintBorder(paint);
if (textArea!=null) {
if (textArea != null) {
textArea.repaint();
}
}
}
/**
* Uninstalls this listener from the current text area. Does nothing if
* Uninstalls this listener from the current text area. Does nothing if
* it not currently installed on any text area.
*
* @see #install(RSyntaxTextArea)
*/
public void uninstall() {
if (textArea!=null) {
if (textArea != null) {
clear();
textArea.removeCaretListener(this);
}
}
}
}
@@ -1,7 +1,6 @@
/*
* 07/03/2016
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -33,7 +32,6 @@ import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.focusabletip.TipUtil;
/**
* A tool tip-like popup that shows the line of code containing the bracket
* matched to that at the caret position, if it is scrolled out of the user's
@@ -43,50 +41,38 @@ import org.fife.ui.rsyntaxtextarea.focusabletip.TipUtil;
* @version 1.0
*/
class MatchedBracketPopup extends JWindow {
private RSyntaxTextArea textArea;
private transient Listener listener;
private static final int LEFT_EMPTY_BORDER = 5;
MatchedBracketPopup(Window parent, RSyntaxTextArea textArea, int
offsToRender) {
MatchedBracketPopup(Window parent, RSyntaxTextArea textArea, int offsToRender) {
super(parent);
this.textArea = textArea;
JPanel cp = new JPanel(new BorderLayout());
cp.setBorder(BorderFactory.createCompoundBorder(
TipUtil.getToolTipBorder(),
cp.setBorder(BorderFactory.createCompoundBorder(TipUtil.getToolTipBorder(),
BorderFactory.createEmptyBorder(2, LEFT_EMPTY_BORDER, 5, 5)));
cp.setBackground(TipUtil.getToolTipBackground());
setContentPane(cp);
cp.add(new JLabel(getText(offsToRender)));
installKeyBindings();
listener = new Listener();
setLocation();
}
/**
* Overridden to ensure this popup stays in a specific size range.
*/
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (size!=null) {
if (size != null) {
size.width = Math.min(size.width, 800);
}
return size;
}
private String getText(int offsToRender) {
int line = 0;
try {
line = textArea.getLineOfOffset(offsToRender);
@@ -94,9 +80,7 @@ class MatchedBracketPopup extends JWindow {
ble.printStackTrace(); // Never happens
return null;
}
int lastLine = line + 1;
// Render prior line if the open brace line has no other text on it
if (line > 0) {
try {
@@ -111,41 +95,33 @@ class MatchedBracketPopup extends JWindow {
ble.printStackTrace();
}
}
Font font = textArea.getFontForTokenType(TokenTypes.IDENTIFIER);
StringBuilder sb = new StringBuilder("<html>");
sb.append("<style>body { font-size:\"").append(font.getSize());
sb.append("pt\" }</style><nobr>");
while (line < lastLine) {
Token t = textArea.getTokenListForLine(line);
while (t!=null && t.isPaintable()) {
while (t != null && t.isPaintable()) {
t.appendHTMLRepresentation(sb, textArea, true, true);
t = t.getNextToken();
}
sb.append("<br>");
line++;
}
return sb.toString();
}
/**
* Adds key bindings to this popup.
*/
private void installKeyBindings() {
InputMap im = getRootPane().getInputMap(
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
InputMap im = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap am = getRootPane().getActionMap();
KeyStroke escapeKS = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
im.put(escapeKS, "onEscape");
am.put("onEscape", new EscapeAction());
}
/**
* Positions this popup to be in the top right-hand corner of the parent
* editor.
@@ -156,83 +132,77 @@ class MatchedBracketPopup extends JWindow {
topLeft.y = Math.max(topLeft.y - 24, 0);
setLocation(topLeft.x - LEFT_EMPTY_BORDER, topLeft.y);
}
/**
* Action performed when Escape is pressed in this popup.
*/
private class EscapeAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
listener.uninstallAndHide();
}
}
/**
* Listens for events in this popup.
*/
private class Listener extends WindowAdapter implements ComponentListener {
Listener() {
addWindowFocusListener(this);
// If anything happens to the "parent" window, hide this popup
Window parent = (Window)getParent();
Window parent = (Window) getParent();
parent.addWindowFocusListener(this);
parent.addWindowListener(this);
parent.addComponentListener(this);
}
@Override
public void componentResized(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentMoved(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentShown(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentHidden(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void windowActivated(WindowEvent e) {
checkForParentWindowEvent(e);
}
@Override
public void windowLostFocus(WindowEvent e) {
uninstallAndHide();
}
@Override
public void windowIconified(WindowEvent e) {
checkForParentWindowEvent(e);
}
private boolean checkForParentWindowEvent(WindowEvent e) {
if (e.getSource()==getParent()) {
if (e.getSource() == getParent()) {
uninstallAndHide();
return true;
}
return false;
}
private void uninstallAndHide() {
Window parent = (Window)getParent();
Window parent = (Window) getParent();
parent.removeWindowFocusListener(this);
parent.removeWindowListener(this);
parent.removeComponentListener(this);
@@ -240,6 +210,5 @@ class MatchedBracketPopup extends JWindow {
setVisible(false);
dispose();
}
}
}
}
@@ -1,19 +1,16 @@
/*
* 03/09/2013
*
* OccurrenceMarker - Marks occurrences of the current token.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* An <code>OccurrenceMarker</code> is called when the caret stops moving after
* a short period. If the current {@link TokenMaker} returns an instance of
* a short period. If the current {@link TokenMaker} returns an instance of
* this class, it is told to mark all occurrences of the identifier at the
* caret position.
*
@@ -21,43 +18,44 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
* @version 1.0
*/
public interface OccurrenceMarker {
/**
* Returns the token to mark occurrences, of, provided it matches the
* criteria put forth by {@link #isValidType(RSyntaxTextArea, Token)}.
* For most languages, this method should return the token at the caret
* position.
*
* @param textArea The text area.
* @param textArea
* The text area.
* @return The token to (possibly) mark occurrences of, or
* <code>null</code> if none.
*/
Token getTokenToMark(RSyntaxTextArea textArea);
/**
* Returns whether the specified token is a type that we can do a
* "mark occurrences" of. Typically, this will delegate to
* "mark occurrences" of. Typically, this will delegate to
* {@link RSyntaxTextArea#getMarkOccurrencesOfTokenType(int)}.
*
* @param textArea The text area.
* @param t The token.
* @param textArea
* The text area.
* @param t
* The token.
* @return Whether we should mark all occurrences of this token.
*/
boolean isValidType(RSyntaxTextArea textArea, Token t);
/**
* Called when occurrences of a token should be marked.
*
* @param doc The document.
* @param t The document whose relevant occurrences should be marked.
* @param h The highlighter to add the highlights to.
* @param p The painter for the highlights.
* @param doc
* The document.
* @param t
* The document whose relevant occurrences should be marked.
* @param h
* The highlighter to add the highlights to.
* @param p
* The painter for the highlights.
*/
void markOccurrences(RSyntaxDocument doc, Token t,
RSyntaxTextAreaHighlighter h, SmartHighlightPainter p);
}
void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p);
}
+198 -277
View File
@@ -1,10 +1,8 @@
/*
* 09/26/2005
*
* ParserManager.java - Manages the parsing of an RSyntaxTextArea's document,
* if necessary.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -41,17 +39,14 @@ import org.fife.ui.rsyntaxtextarea.parser.ToolTipInfo;
import org.fife.ui.rtextarea.RDocument;
import org.fife.ui.rtextarea.RTextAreaHighlighter.HighlightInfo;
/**
* Manages running a parser object for an <code>RSyntaxTextArea</code>.
*
* @author Robert Futrell
* @version 0.9
*/
class ParserManager implements DocumentListener, ActionListener,
HyperlinkListener, PropertyChangeListener {
class ParserManager implements DocumentListener, ActionListener, HyperlinkListener, PropertyChangeListener {
private RSyntaxTextArea textArea;
private List<Parser> parsers;
private Timer timer;
@@ -59,58 +54,53 @@ class ParserManager implements DocumentListener, ActionListener,
private Parser parserForTip;
private Position firstOffsetModded;
private Position lastOffsetModded;
/**
* Mapping of notices to their highlights in the editor. Can't use a Map
* Mapping of notices to their highlights in the editor. Can't use a Map
* since parsers could return two <code>ParserNotice</code>s that compare
* equally via <code>equals()</code>. Real-world example: The Perl
* equally via <code>equals()</code>. Real-world example: The Perl
* compiler will return 2+ identical error messages if the same error is
* committed in a single line more than once.
*/
private List<NoticeHighlightPair> noticeHighlightPairs;
/**
* Painter used to underline errors.
*/
private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter =
new SquiggleUnderlineHighlightPainter(Color.RED);
private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter = new SquiggleUnderlineHighlightPainter(Color.RED);
/**
* If this system property is set to <code>true</code>, debug messages
* will be printed to stdout to help diagnose parsing issues.
*/
private static final String PROPERTY_DEBUG_PARSING = "rsta.debugParsing";
/**
* Whether to print debug messages while running parsers.
*/
private static final boolean DEBUG_PARSING;
/**
* The default delay between the last key press and when the document
* is parsed, in milliseconds.
*/
private static final int DEFAULT_DELAY_MS = 1250;
private static final int DEFAULT_DELAY_MS = 1250;
/**
* Constructor.
*
* @param textArea The text area whose document the parser will be
* parsing.
* @param textArea
* The text area whose document the parser will be
* parsing.
*/
ParserManager(RSyntaxTextArea textArea) {
this(DEFAULT_DELAY_MS, textArea);
}
/**
* Constructor.
*
* @param delay The delay between the last key press and when the document
* is parsed.
* @param textArea The text area whose document the parser will be
* parsing.
* @param delay
* The delay between the last key press and when the document
* is parsed.
* @param textArea
* The text area whose document the parser will be
* parsing.
*/
ParserManager(int delay, RSyntaxTextArea textArea) {
this.textArea = textArea;
@@ -121,49 +111,41 @@ class ParserManager implements DocumentListener, ActionListener,
timer.setRepeats(false);
running = true;
}
/**
* Called when the timer fires (e.g. it's time to parse the document).
*
* @param e The event.
* @param e
* The event.
*/
@Override
public void actionPerformed(ActionEvent e) {
// Sanity check - should have >1 parser if event is fired.
int parserCount = getParserCount();
if (parserCount==0) {
if (parserCount == 0) {
return;
}
long begin = 0;
if (DEBUG_PARSING) {
begin = System.currentTimeMillis();
}
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
Element root = doc.getDefaultRootElement();
int firstLine = firstOffsetModded==null ? 0 :
root.getElementIndex(firstOffsetModded.getOffset());
int lastLine = lastOffsetModded==null ? root.getElementCount()-1 :
root.getElementIndex(lastOffsetModded.getOffset());
int firstLine = firstOffsetModded == null ? 0 : root.getElementIndex(firstOffsetModded.getOffset());
int lastLine = lastOffsetModded == null ? root.getElementCount() - 1 : root.getElementIndex(lastOffsetModded.getOffset());
firstOffsetModded = lastOffsetModded = null;
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Minimum lines to parse: " + firstLine + "-" + lastLine);
}
String style = textArea.getSyntaxEditingStyle();
doc.readLock();
try {
for (int i=0; i<parserCount; i++) {
for (int i = 0; i < parserCount; i++) {
Parser parser = getParser(i);
if (parser.isEnabled()) {
ParseResult res = parser.parse(doc, style);
addParserNoticeHighlights(res);
}
else {
} else {
clearParserNoticeHighlights(parser);
}
}
@@ -171,30 +153,28 @@ class ParserManager implements DocumentListener, ActionListener,
} finally {
doc.readUnlock();
}
if (DEBUG_PARSING) {
float time = (System.currentTimeMillis()-begin)/1000f;
float time = (System.currentTimeMillis() - begin) / 1000f;
System.out.println("Total parsing time: " + time + " seconds");
}
}
/**
* Adds a parser for the text area.
*
* @param parser The new parser. If this is <code>null</code>, nothing
* happens.
* @param parser
* The new parser. If this is <code>null</code>, nothing
* happens.
* @see #getParser(int)
* @see #removeParser(Parser)
*/
public void addParser(Parser parser) {
if (parser!=null && !parsers.contains(parser)) {
if (parser != null && !parsers.contains(parser)) {
if (running) {
timer.stop();
}
parsers.add(parser);
if (parsers.size()==1) {
if (parsers.size() == 1) {
// Okay to call more than once.
ToolTipManager.sharedInstance().registerComponent(textArea);
}
@@ -203,40 +183,31 @@ class ParserManager implements DocumentListener, ActionListener,
}
}
}
/**
* Adds highlights for a list of parser notices. Any current notices
* Adds highlights for a list of parser notices. Any current notices
* from the same Parser, in the same parsed range, are removed.
*
* @param res The result of a parsing.
* @param res
* The result of a parsing.
* @see #clearParserNoticeHighlights()
*/
private void addParserNoticeHighlights(ParseResult res) {
// Parsers are supposed to return at least empty ParseResults, but
// we'll be defensive here.
if (res==null) {
if (res == null) {
return;
}
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Adding parser notices from " +
res.getParser());
System.out.println("[DEBUG]: Adding parser notices from " + res.getParser());
}
if (noticeHighlightPairs==null) {
if (noticeHighlightPairs == null) {
noticeHighlightPairs = new ArrayList<NoticeHighlightPair>();
}
removeParserNotices(res);
List<ParserNotice> notices = res.getNotices();
if (notices.size()>0) { // Guaranteed non-null
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
if (notices.size() > 0) { // Guaranteed non-null
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
for (ParserNotice notice : notices) {
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: ... adding: " + notice);
@@ -244,70 +215,60 @@ class ParserManager implements DocumentListener, ActionListener,
try {
HighlightInfo highlight = null;
if (notice.getShowInEditor()) {
highlight = h.addParserHighlight(notice,
parserErrorHighlightPainter);
highlight = h.addParserHighlight(notice, parserErrorHighlightPainter);
}
noticeHighlightPairs.add(new NoticeHighlightPair(notice, highlight));
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
}
}
}
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Done adding parser notices from " +
res.getParser());
System.out.println("[DEBUG]: Done adding parser notices from " + res.getParser());
}
}
/**
* Called when the document is modified.
*
* @param e The document event.
* @param e
* The document event.
*/
@Override
public void changedUpdate(DocumentEvent e) {
}
public void changedUpdate(DocumentEvent e) {}
private void clearParserNoticeHighlights() {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
if (h!=null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
if (h != null) {
h.clearParserHighlights();
}
if (noticeHighlightPairs!=null) {
if (noticeHighlightPairs != null) {
noticeHighlightPairs.clear();
}
}
/**
* Removes all parser notice highlights for a specific parser.
*
* @param parser The parser whose highlights to remove.
* @param parser
* The parser whose highlights to remove.
*/
private void clearParserNoticeHighlights(Parser parser) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
if (h!=null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
if (h != null) {
h.clearParserHighlights(parser);
}
if (noticeHighlightPairs!=null) {
Iterator<NoticeHighlightPair> i=noticeHighlightPairs.iterator();
if (noticeHighlightPairs != null) {
Iterator<NoticeHighlightPair> i = noticeHighlightPairs.iterator();
while (i.hasNext()) {
NoticeHighlightPair pair = i.next();
if (pair.notice.getParser()==parser) {
if (pair.notice.getParser() == parser) {
i.remove();
}
}
}
}
/**
* Removes all parsers and any highlights they have created.
*
@@ -319,32 +280,32 @@ class ParserManager implements DocumentListener, ActionListener,
parsers.clear();
textArea.fireParserNoticesChange();
}
/**
* Forces the given {@link Parser} to re-parse the content of this text
* area.<p>
* area.
* <p>
*
* This method can be useful when a <code>Parser</code> can be configured
* as to what notices it returns. For example, if a Java language parser
* as to what notices it returns. For example, if a Java language parser
* can be configured to set whether no serialVersionUID is a warning,
* error, or ignored, this method can be called after changing the expected
* notice type to have the document re-parsed.
*
* @param parser The index of the <code>Parser</code> to re-run.
* @param parser
* The index of the <code>Parser</code> to re-run.
* @see #getParser(int)
*/
public void forceReparsing(int parser) {
Parser p = getParser(parser);
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
String style = textArea.getSyntaxEditingStyle();
doc.readLock();
try {
if (p.isEnabled()) {
ParseResult res = p.parse(doc, style);
addParserNoticeHighlights(res);
}
else {
} else {
clearParserNoticeHighlights(p);
}
textArea.fireParserNoticesChange();
@@ -352,8 +313,7 @@ class ParserManager implements DocumentListener, ActionListener,
doc.readUnlock();
}
}
/**
* Returns the delay between the last "concurrent" edit and when the
* document is re-parsed.
@@ -364,12 +324,12 @@ class ParserManager implements DocumentListener, ActionListener,
public int getDelay() {
return timer.getDelay();
}
/**
* Returns the specified parser.
*
* @param index The index of the parser.
* @param index
* The index of the parser.
* @return The parser.
* @see #getParserCount()
* @see #addParser(Parser)
@@ -378,8 +338,7 @@ class ParserManager implements DocumentListener, ActionListener,
public Parser getParser(int index) {
return parsers.get(index);
}
/**
* Returns the number of registered parsers.
*
@@ -388,249 +347,230 @@ class ParserManager implements DocumentListener, ActionListener,
public int getParserCount() {
return parsers.size();
}
/**
* Returns a list of the current parser notices for this text area.
* This method (like most Swing methods) should only be called on the
* EDT.
*
* @return The list of notices. This will be an empty list if there are
* @return The list of notices. This will be an empty list if there are
* none.
*/
public List<ParserNotice> getParserNotices() {
List<ParserNotice> notices = new ArrayList<ParserNotice>();
if (noticeHighlightPairs!=null) {
if (noticeHighlightPairs != null) {
for (NoticeHighlightPair pair : noticeHighlightPairs) {
notices.add(pair.notice);
}
}
return notices;
}
/**
* Returns the tool tip to display for a mouse event at the given
* location. This method is overridden to give a registered parser a
* location. This method is overridden to give a registered parser a
* chance to display a tool tip (such as an error description when the
* mouse is over an error highlight).
*
* @param e The mouse event.
* @param e
* The mouse event.
* @return The tool tip to display, and possibly a hyperlink event handler.
*/
public ToolTipInfo getToolTipText(MouseEvent e) {
String tip = null;
HyperlinkListener listener = null;
parserForTip = null;
Point p = e.getPoint();
// try {
int pos = textArea.viewToModel(p);
/*
Highlighter.Highlight[] highlights = textArea.getHighlighter().
getHighlights();
for (int i=0; i<highlights.length; i++) {
Highlighter.Highlight h = highlights[i];
//if (h instanceof ParserNoticeHighlight) {
// ParserNoticeHighlight pnh = (ParserNoticeHighlight)h;
int start = h.getStartOffset();
int end = h.getEndOffset();
if (start<=pos && end>=pos) {
//return pnh.getMessage();
return textArea.getText(start, end-start);
}
//}
}
*/
if (noticeHighlightPairs!=null) {
for (NoticeHighlightPair pair : noticeHighlightPairs) {
ParserNotice notice = pair.notice;
if (noticeContainsPosition(notice, pos) &&
noticeContainsPointInView(notice, p)) {
tip = notice.getToolTipText();
parserForTip = notice.getParser();
if (parserForTip instanceof HyperlinkListener) {
listener = (HyperlinkListener)parserForTip;
}
break;
// try {
int pos = textArea.viewToModel(p);
/*
* Highlighter.Highlight[] highlights = textArea.getHighlighter().
* getHighlights();
* for (int i=0; i<highlights.length; i++) {
* Highlighter.Highlight h = highlights[i];
* //if (h instanceof ParserNoticeHighlight) {
* // ParserNoticeHighlight pnh = (ParserNoticeHighlight)h;
* int start = h.getStartOffset();
* int end = h.getEndOffset();
* if (start<=pos && end>=pos) {
* //return pnh.getMessage();
* return textArea.getText(start, end-start);
* }
* //}
* }
*/
if (noticeHighlightPairs != null) {
for (NoticeHighlightPair pair : noticeHighlightPairs) {
ParserNotice notice = pair.notice;
if (noticeContainsPosition(notice, pos) && noticeContainsPointInView(notice, p)) {
tip = notice.getToolTipText();
parserForTip = notice.getParser();
if (parserForTip instanceof HyperlinkListener) {
listener = (HyperlinkListener) parserForTip;
}
break;
}
}
// } catch (BadLocationException ble) {
// ble.printStackTrace(); // Should never happen.
// }
URL imageBase = parserForTip==null ? null : parserForTip.getImageBase();
}
// } catch (BadLocationException ble) {
// ble.printStackTrace(); // Should never happen.
// }
URL imageBase = parserForTip == null ? null : parserForTip.getImageBase();
return new ToolTipInfo(tip, listener, imageBase);
}
/**
* Called when the document is modified.
*
* @param e The document event.
* @param e
* The document event.
*/
public void handleDocumentEvent(DocumentEvent e) {
if (running && parsers.size()>0) {
if (running && parsers.size() > 0) {
timer.restart();
}
}
/**
* Called when the user clicks a hyperlink in a
* {@link org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip}.
*
* @param e The event.
* @param e
* The event.
*/
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (parserForTip!=null && parserForTip.getHyperlinkListener()!=null) {
if (parserForTip != null && parserForTip.getHyperlinkListener() != null) {
parserForTip.getHyperlinkListener().linkClicked(textArea, e);
}
}
/**
* Called when the document is modified.
*
* @param e The document event.
* @param e
* The document event.
*/
@Override
public void insertUpdate(DocumentEvent e) {
// Keep track of the first and last offset modified. Some parsers are
// Keep track of the first and last offset modified. Some parsers are
// smart and will only re-parse this section of the file.
try {
int offs = e.getOffset();
if (firstOffsetModded==null || offs<firstOffsetModded.getOffset()) {
if (firstOffsetModded == null || offs < firstOffsetModded.getOffset()) {
firstOffsetModded = e.getDocument().createPosition(offs);
}
offs = e.getOffset() + e.getLength();
if (lastOffsetModded==null || offs>lastOffsetModded.getOffset()) {
if (lastOffsetModded == null || offs > lastOffsetModded.getOffset()) {
lastOffsetModded = e.getDocument().createPosition(offs);
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Shouldn't happen
}
handleDocumentEvent(e);
}
/**
* Returns whether a parser notice contains the specified offset.
*
* @param notice The notice.
* @param offs The offset.
* @param notice
* The notice.
* @param offs
* The offset.
* @return Whether the notice contains the offset.
*/
private boolean noticeContainsPosition(ParserNotice notice, int offs){
private boolean noticeContainsPosition(ParserNotice notice, int offs) {
if (notice.getKnowsOffsetAndLength()) {
return notice.containsPosition(offs);
}
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
int line = notice.getLine();
if (line<0) { // Defensive against possible bad user-defined notices.
if (line < 0) { // Defensive against possible bad user-defined notices.
return false;
}
Element elem = root.getElement(line);
return elem != null && offs>=elem.getStartOffset() && offs<elem.getEndOffset();
return elem != null && offs >= elem.getStartOffset() && offs < elem.getEndOffset();
}
/**
* Since <code>viewToModel()</code> returns the <em>closest</em> model
* position, and the position doesn't <em>necessarily</em> contain the
* point passed in as an argument, this method checks whether the point is
* indeed contained in the view rectangle for the specified offset.
*
* @param notice The parser notice.
* @param p The point possibly contained in the view range of the
* parser notice.
* @param notice
* The parser notice.
* @param p
* The point possibly contained in the view range of the
* parser notice.
* @return Whether the parser notice actually contains the specified point
* in the view.
*/
private boolean noticeContainsPointInView(ParserNotice notice,
Point p) {
private boolean noticeContainsPointInView(ParserNotice notice, Point p) {
try {
int start, end;
if (notice.getKnowsOffsetAndLength()) {
start = notice.getOffset();
end = start + notice.getLength() - 1;
}
else {
} else {
Document doc = textArea.getDocument();
Element root = doc.getDefaultRootElement();
int line = notice.getLine();
// Defend against possible bad user-defined notices.
if (line<0) {
if (line < 0) {
return false;
}
Element elem = root.getElement(line);
start = elem.getStartOffset();
end = elem.getEndOffset() - 1;
}
Rectangle r1 = textArea.modelToView(start);
Rectangle r2 = textArea.modelToView(end);
if (r1.y!=r2.y) {
if (r1.y != r2.y) {
// If the notice spans multiple lines, give them the benefit
// of the doubt. This is only "wrong" if the user is in empty
// of the doubt. This is only "wrong" if the user is in empty
// space "to the right" of the error marker when it ends at the
// end of a line anyway.
return true;
}
r1.y--; // Be a tiny bit lenient.
r1.height += 2; // Ditto
return p.x>=r1.x && p.x<(r2.x+r2.width) &&
p.y>=r1.y && p.y<(r1.y+r1.height);
return p.x >= r1.x && p.x < (r2.x + r2.width) && p.y >= r1.y && p.y < (r1.y + r1.height);
} catch (BadLocationException ble) { // Never occurs
// Give them the benefit of the doubt, should 99% of the time be
// true anyway
return true;
}
}
/**
* Called when a property we're interested in changes.
*
* @param e The property change event.
* @param e
* The property change event.
*/
@Override
public void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
if ("document".equals(name)) {
// The document switched out from under us
RDocument old = (RDocument)e.getOldValue();
RDocument old = (RDocument) e.getOldValue();
if (old != null) {
old.removeDocumentListener(this);
}
RDocument newDoc = (RDocument)e.getNewValue();
RDocument newDoc = (RDocument) e.getNewValue();
if (newDoc != null) {
newDoc.addDocumentListener(this);
}
}
}
/**
* Removes a parser.
*
* @param parser The parser to remove.
* @param parser
* The parser to remove.
* @return Whether the parser was found.
* @see #addParser(Parser)
* @see #getParser(int)
@@ -643,91 +583,82 @@ class ParserManager implements DocumentListener, ActionListener,
}
return removed;
}
/**
* Removes all parser notices (and clears highlights in the editor) from
* a particular parser.
*
* @param parser The parser.
* @param parser
* The parser.
*/
private void removeParserNotices(Parser parser) {
if (noticeHighlightPairs!=null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
for (Iterator<NoticeHighlightPair> i=noticeHighlightPairs.iterator(); i.hasNext();) {
if (noticeHighlightPairs != null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
for (Iterator<NoticeHighlightPair> i = noticeHighlightPairs.iterator(); i.hasNext();) {
NoticeHighlightPair pair = i.next();
if (pair.notice.getParser()==parser && pair.highlight!=null) {
if (pair.notice.getParser() == parser && pair.highlight != null) {
h.removeParserHighlight(pair.highlight);
i.remove();
}
}
}
}
/**
* Removes any currently stored notices (and the corresponding highlights
* from the editor) from the same Parser, and in the given line range,
* as in the results.
*
* @param res The results.
* @param res
* The results.
*/
private void removeParserNotices(ParseResult res) {
if (noticeHighlightPairs!=null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
textArea.getHighlighter();
for (Iterator<NoticeHighlightPair> i=noticeHighlightPairs.iterator(); i.hasNext();) {
if (noticeHighlightPairs != null) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
for (Iterator<NoticeHighlightPair> i = noticeHighlightPairs.iterator(); i.hasNext();) {
NoticeHighlightPair pair = i.next();
boolean removed = false;
if (shouldRemoveNotice(pair.notice, res)) {
if (pair.highlight!=null) {
if (pair.highlight != null) {
h.removeParserHighlight(pair.highlight);
}
i.remove();
removed = true;
}
if (DEBUG_PARSING) {
String text = removed ? "[DEBUG]: ... notice removed: " :
"[DEBUG]: ... notice not removed: ";
String text = removed ? "[DEBUG]: ... notice removed: " : "[DEBUG]: ... notice not removed: ";
System.out.println(text + pair.notice);
}
}
}
}
/**
* Called when the document is modified.
*
* @param e The document event.
* @param e
* The document event.
*/
@Override
public void removeUpdate(DocumentEvent e) {
// Keep track of the first and last offset modified. Some parsers are
// smart and will only re-parse this section of the file. Note that
// Keep track of the first and last offset modified. Some parsers are
// smart and will only re-parse this section of the file. Note that
// for removals, only the line at the removal start needs to be
// re-parsed.
try {
int offs = e.getOffset();
if (firstOffsetModded==null || offs<firstOffsetModded.getOffset()) {
if (firstOffsetModded == null || offs < firstOffsetModded.getOffset()) {
firstOffsetModded = e.getDocument().createPosition(offs);
}
if (lastOffsetModded==null || offs>lastOffsetModded.getOffset()) {
if (lastOffsetModded == null || offs > lastOffsetModded.getOffset()) {
lastOffsetModded = e.getDocument().createPosition(offs);
}
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
}
handleDocumentEvent(e);
}
/**
* Restarts parsing the document.
*
@@ -737,14 +668,14 @@ class ParserManager implements DocumentListener, ActionListener,
timer.restart();
running = true;
}
/**
* Sets the delay between the last "concurrent" edit and when the document
* is re-parsed.
*
* @param millis The new delay, in milliseconds. This must be greater
* than <code>0</code>.
* @param millis
* The new delay, in milliseconds. This must be greater
* than <code>0</code>.
* @see #getDelay()
*/
public void setDelay(int millis) {
@@ -757,36 +688,31 @@ class ParserManager implements DocumentListener, ActionListener,
timer.start();
}
}
/**
* Returns whether a parser notice should be removed, based on a parse
* result.
*
* @param notice The notice in question.
* @param res The result.
* @param notice
* The notice in question.
* @param res
* The result.
* @return Whether the notice should be removed.
*/
private boolean shouldRemoveNotice(ParserNotice notice,
ParseResult res) {
private boolean shouldRemoveNotice(ParserNotice notice, ParseResult res) {
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: ... ... shouldRemoveNotice " +
notice + ": " + (notice.getParser()==res.getParser()));
System.out.println("[DEBUG]: ... ... shouldRemoveNotice " + notice + ": " + (notice.getParser() == res.getParser()));
}
// NOTE: We must currently remove all notices for the parser. Parser
// NOTE: We must currently remove all notices for the parser. Parser
// implementors are required to parse the entire document each parsing
// request, as RSTA is not yet sophisticated enough to determine the
// minimum range of text to parse (and ParserNotices' locations aren't
// updated when the Document is mutated, which would be a requirement
// for this as well).
// return same_parser && (in_reparsed_range || in_deleted_end_of_doc)
return notice.getParser()==res.getParser();
return notice.getParser() == res.getParser();
}
/**
* Stops parsing the document.
*
@@ -796,24 +722,21 @@ class ParserManager implements DocumentListener, ActionListener,
timer.stop();
running = false;
}
/**
* Mapping of a parser notice to its highlight in the editor.
*/
private static class NoticeHighlightPair {
private ParserNotice notice;
private HighlightInfo highlight;
NoticeHighlightPair(ParserNotice notice, HighlightInfo highlight) {
this.notice = notice;
this.highlight = highlight;
}
}
static {
boolean debugParsing = false;
try {
@@ -824,6 +747,4 @@ class ParserManager implements DocumentListener, ActionListener,
}
DEBUG_PARSING = debugParsing;
}
}
}
@@ -1,20 +1,17 @@
/*
* 01/11/2011
*
* PopupWindowDecorator.java - Hook allowing hosting applications to decorate
* JWindows created by the AutoComplete library.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.JWindow;
/**
* A hook allowing hosting applications to decorate JWindows created by the
* AutoComplete library. For example, you could use the
* AutoComplete library. For example, you could use the
* <a href="http://jgoodies.com/">JGoodies</a> library to add drop shadows
* to the windows.
*
@@ -22,25 +19,24 @@ import javax.swing.JWindow;
* @version 1.0
*/
public abstract class PopupWindowDecorator {
/**
* The singleton instance of this class.
*/
private static PopupWindowDecorator decorator;
/**
* Callback called whenever an appropriate JWindow is created by the
* AutoComplete library. Implementations can decorate the window however
* AutoComplete library. Implementations can decorate the window however
* they see fit.
*
* @param window The newly-created window.
* @param window
* The newly-created window.
*/
public abstract void decorate(JWindow window);
/**
* Returns the singleton instance of this class. This should only be
* Returns the singleton instance of this class. This should only be
* called on the EDT.
*
* @return The singleton instance of this class, or <code>null</code>
@@ -50,19 +46,17 @@ public abstract class PopupWindowDecorator {
public static PopupWindowDecorator get() {
return decorator;
}
/**
* Sets the singleton instance of this class. This should only be called
* Sets the singleton instance of this class. This should only be called
* on the EDT.
*
* @param decorator The new instance of this class. This may be
* <code>null</code>.
* @param decorator
* The new instance of this class. This may be
* <code>null</code>.
* @see #get()
*/
public static void set(PopupWindowDecorator decorator) {
PopupWindowDecorator.decorator = decorator;
}
}
}
+23 -23
View File
@@ -1,9 +1,7 @@
/*
* 02/10/2009
*
* RSTAView.java - An <code>RSyntaxTextArea</code> view.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -12,7 +10,6 @@ import java.awt.Rectangle;
import javax.swing.text.BadLocationException;
/**
* Utility methods for RSyntaxTextArea's views.
*
@@ -20,43 +17,46 @@ import javax.swing.text.BadLocationException;
* @version 1.0
*/
interface RSTAView {
/**
* Returns the y-coordinate of the specified line.<p>
* Returns the y-coordinate of the specified line.
* <p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
*
* @param alloc The area the text area can render into.
* @param line The line number.
* @param alloc
* The area the text area can render into.
* @param line
* The line number.
* @return The y-coordinate of the top of the line, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException If <code>line</code> isn't a valid line
* number for this document.
* @throws BadLocationException
* If <code>line</code> isn't a valid line
* number for this document.
*/
int yForLine(Rectangle alloc, int line) throws BadLocationException;
/**
* Returns the y-coordinate of the line containing a specified offset.<p>
* Returns the y-coordinate of the line containing a specified offset.
* <p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
*
* @param alloc The area the text area can render into.
* @param offs The offset info the document.
* @param alloc
* The area the text area can render into.
* @param offs
* The offset info the document.
* @return The y-coordinate of the top of the offset, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException If <code>offs</code> isn't a valid offset
* into the document.
* @throws BadLocationException
* If <code>offs</code> isn't a valid offset
* into the document.
*/
int yForLineContaining(Rectangle alloc, int offs)
throws BadLocationException;
}
int yForLineContaining(Rectangle alloc, int offs) throws BadLocationException;
}
@@ -1,10 +1,8 @@
/*
* 10/16/2004
*
* RSyntaxDocument.java - A document capable of syntax highlighting, used by
* RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -51,45 +49,43 @@ import me.ByteEdit.main.JavaAssemblyTokenMaker;
* @version 1.0
*/
public class RSyntaxDocument extends RDocument implements Iterable<Token>, SyntaxConstants {
/**
* Creates a {@link TokenMaker} appropriate for a given programming language.
* Creates a {@link TokenMaker} appropriate for a given programming
* language.
*/
private transient TokenMakerFactory tokenMakerFactory;
/**
* Splits text into tokens for the current programming language.
*/
private transient TokenMaker tokenMaker;
/**
* The current syntax style. Only cached to keep this class serializable.
*/
private String syntaxStyle;
/**
* Array of values representing the "last token type" on each line. This is used
* Array of values representing the "last token type" on each line. This is
* used
* in cases such as multi-line comments: if the previous line ended with an
* (unclosed) multi-line comment, we can use this knowledge and start the
* current line's syntax highlighting in multi-line comment state.
*/
protected transient DynamicIntArray lastTokensOnLines;
private transient int lastLine = -1;
private transient Token cachedTokenList;
private transient int useCacheCount = 0;
private transient int tokenRetrievalCount = 0;
private transient Segment s;
/**
* If this is set to <code>true</code>, debug information about how much token
* If this is set to <code>true</code>, debug information about how much
* token
* caching is helping is printed to stdout.
*/
private static final boolean DEBUG_TOKEN_CACHING = false;
/**
* Constructs a plain text document. A default root element is created, and the
* Constructs a plain text document. A default root element is created, and
* the
* tab size set to 5.
*
* @param syntaxStyle
@@ -98,13 +94,15 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public RSyntaxDocument(String syntaxStyle) {
this(null, syntaxStyle);
}
/**
* Constructs a plain text document. A default root element is created, and the
* Constructs a plain text document. A default root element is created, and
* the
* tab size set to 5.
*
* @param tmf
* The <code>TokenMakerFactory</code> for this document. If this is
* The <code>TokenMakerFactory</code> for this document. If this
* is
* <code>null</code>, a default factory is used.
* @param syntaxStyle
* The syntax highlighting scheme to use.
@@ -117,13 +115,15 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
setTokenMakerFactory(tmf);
setSyntaxStyle(syntaxStyle);
}
/**
* Alerts all listeners to this document of an insertion. This is overridden so
* 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
* <code>insertUpdate</code> because <code>insertUpdate</code> is not called
* by
* the undo/redo actions, but this method is.
*
* @param e
@@ -131,72 +131,59 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
*/
@Override
protected void fireInsertUpdate(DocumentEvent e) {
cachedTokenList = null;
/*
* 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, 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:" +
// 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.
// 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...
// 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.
// Let all listeners know about the insertion.
super.fireInsertUpdate(e);
}
/**
* This method is called AFTER the content has been inserted into the document
* 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.
* 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"
@@ -208,77 +195,71 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
*/
@Override
protected void fireRemoveUpdate(DocumentEvent chng) {
cachedTokenList = null;
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("... removing lines: " + line + " - " +
// (endBefore-1));
// System.err.println("... added: " + numAdded + ", removed: " +
// removed.length);
lastTokensOnLines.removeRange(line, endBefore); // Removing values for lines [line-(endBefore-1)].
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.
// 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...
// 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 " +
// System.err.println("previousTokenType for line : " + previousLine
// + " is " +
// previousTokenType);
// Update last tokens for lines below until they've stopped changing.
// 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 closest {@link TokenTypes "standard" token type} for a given
* "internal" token type (e.g. one whose value is <code>&lt; 0</code>).
*
* @param type
* The token type.
* @return The closest "standard" token type. If a mapping is not defined for
* @return The closest "standard" token type. If a mapping is not defined
* for
* this language, then <code>type</code> is returned.
*/
public int getClosestStandardTokenTypeForInternalType(int type) {
return tokenMaker.getClosestStandardTokenTypeForInternalType(type);
}
/**
* Returns whether closing markup tags should be automatically completed. This
* method only returns <code>true</code> if {@link #getLanguageIsMarkup()} also
* Returns whether closing markup tags should be automatically completed.
* This
* method only returns <code>true</code> if {@link #getLanguageIsMarkup()}
* also
* returns <code>true</code>.
*
* @return Whether markup closing tags should be automatically completed.
@@ -288,14 +269,15 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
// TODO: Remove terrible dependency on AbstractMarkupTokenMaker
return getLanguageIsMarkup() && ((AbstractMarkupTokenMaker) tokenMaker).getCompleteCloseTags();
}
/**
* Returns whether the current programming language uses curly braces
* ('<code>{</code>' and '<code>}</code>') to denote code blocks.
*
* @param languageIndex
* The language index at the offset in question. Since some
* <code>TokenMaker</code>s effectively have nested languages (such
* <code>TokenMaker</code>s effectively have nested languages
* (such
* as JavaScript in HTML), this parameter tells the
* <code>TokenMaker</code> what sub-language to look at.
* @return Whether curly braces denote code blocks.
@@ -303,9 +285,10 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return tokenMaker.getCurlyBracesDenoteCodeBlocks(languageIndex);
}
/**
* Returns whether the current language is a markup language, such as HTML, XML
* Returns whether the current language is a markup language, such as HTML,
* XML
* or PHP.
*
* @return Whether the current language is a markup language.
@@ -313,44 +296,50 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public boolean getLanguageIsMarkup() {
return tokenMaker.isMarkupLanguage();
}
/**
* 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
* @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 the text to place at the beginning and end of a line to "comment" it
* Returns the text to place at the beginning and end of a line to "comment"
* it
* in this programming language.
*
* @return The start and end strings to add to a line to "comment" it out. A
* <code>null</code> value for either means there is no string to add
* for that part. A value of <code>null</code> for the array means this
* <code>null</code> value for either means there is no string to
* add
* for that part. A value of <code>null</code> for the array means
* this
* language does not support commenting/uncommenting lines.
*/
public String[] getLineCommentStartAndEnd(int languageIndex) {
return tokenMaker.getLineCommentStartAndEnd(languageIndex);
}
/**
* Returns whether tokens of the specified type should have "mark occurrences"
* Returns whether tokens of the specified type should have "mark
* occurrences"
* enabled for the current programming language.
*
* @param type
* The token type.
* @return Whether tokens of this type should have "mark occurrences" enabled.
* @return Whether tokens of this type should have "mark occurrences"
* enabled.
*/
boolean getMarkOccurrencesOfTokenType(int type) {
return tokenMaker.getMarkOccurrencesOfTokenType(type);
}
/**
* Returns the occurrence marker for the current language.
*
@@ -359,7 +348,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
OccurrenceMarker getOccurrenceMarker() {
return tokenMaker.getOccurrenceMarker();
}
/**
* This method returns whether auto indentation should be done if Enter is
* pressed at the end of the specified line.
@@ -373,7 +362,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
t = t.getLastNonCommentNonWhitespaceToken();
return tokenMaker.getShouldIndentNextLineAfter(t);
}
/**
* Returns the syntax style being used.
*
@@ -383,11 +372,12 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public String getSyntaxStyle() {
return syntaxStyle;
}
/**
* 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
* <code>tokenMaker.getTokenList</code> that takes into account the last
* token
* on the previous line to assure token accuracy.
*
* @param line
@@ -395,7 +385,6 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
* @return A token list representing the specified line.
*/
public final Token getTokenListForLine(int line) {
tokenRetrievalCount++;
if (line == lastLine && cachedTokenList != null) {
if (DEBUG_TOKEN_CACHING) {
@@ -405,11 +394,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
return cachedTokenList;
}
lastLine = line;
Element map = getDefaultRootElement();
Element elem = map.getElement(line);
int startOffset = elem.getStartOffset();
// int endOffset = (line==map.getElementCount()-1 ? elem.getEndOffset() - 1:
// int endOffset = (line==map.getElementCount()-1 ? elem.getEndOffset()
// - 1:
// elem.getEndOffset() - 1);
int endOffset = elem.getEndOffset() - 1; // Why always "-1"?
try {
@@ -419,13 +408,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
return null;
}
int initialTokenType = line == 0 ? Token.NULL : getLastTokenTypeOnLine(line - 1);
// return tokenMaker.getTokenList(s, initialTokenType, startOffset);
cachedTokenList = tokenMaker.getTokenList(s, initialTokenType, startOffset);
return cachedTokenList;
}
boolean insertBreakSpecialHandling(ActionEvent e) {
Action a = tokenMaker.getInsertBreakAction();
if (a != null) {
@@ -434,10 +421,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
return false;
}
/**
* Returns whether a character could be part of an "identifier" token in a
* specific language. This is used to identify such things as the bounds of the
* specific language. This is used to identify such things as the bounds of
* the
* "word" to select on double-clicking.
*
* @param languageIndex
@@ -449,10 +437,12 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public boolean isIdentifierChar(int languageIndex, char ch) {
return tokenMaker.isIdentifierChar(languageIndex, ch);
}
/**
* Returns an iterator over the paintable tokens in this document. Results are
* undefined if this document is modified while the iterator is being iterated
* Returns an iterator over the paintable tokens in this document. Results
* are
* undefined if this document is modified while the iterator is being
* iterated
* through, so this should only be used on the EDT.
* <p>
*
@@ -465,7 +455,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public Iterator<Token> iterator() {
return new TokenIterator(this);
}
/**
* Deserializes a document.
*
@@ -475,35 +465,31 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
* @throws IOException
*/
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
in.defaultReadObject();
// Install default TokenMakerFactory. To support custom TokenMakers,
// both JVM's should install default TokenMakerFactories that support
// the language they want to use beforehand.
setTokenMakerFactory(null);
// Handle other transient stuff
this.s = new Segment();
int lineCount = getDefaultRootElement().getElementCount();
lastTokensOnLines = new DynamicIntArray(lineCount);
setSyntaxStyle(syntaxStyle); // Actually install (transient) TokenMaker
}
/**
* Makes our private <code>Segment s</code> point to the text in our document
* referenced by the specified element. Note that <code>line</code> MUST be a
* Makes our private <code>Segment s</code> point to the text in our
* document
* referenced by the specified element. Note that <code>line</code> MUST be
* a
* valid line number in the document.
*
* @param line
* The line number you want to get.
*/
private void setSharedSegment(int line) {
Element map = getDefaultRootElement();
// int numLines = map.getElementCount();
Element element = map.getElement(line);
if (element == null) {
throw new InternalError("Invalid line number: " + line);
@@ -517,18 +503,19 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
} catch (BadLocationException ble) {
throw new InternalError("Text range not in document: " + startOffset + "-" + endOffset);
}
}
/**
* Sets the syntax style being used for syntax highlighting in this document.
* Sets the syntax style being used for syntax highlighting in this
* document.
* What styles are supported by a document is determined by its
* {@link TokenMakerFactory}. By default, all <code>RSyntaxDocument</code>s
* support all languages built into <code>RSyntaxTextArea</code>.
*
* @param styleKey
* The new style to use, such as
* {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is not
* {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is
* not
* known or supported by this document, then
* {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
* @see #setSyntaxStyle(TokenMaker)
@@ -543,9 +530,10 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
updateSyntaxHighlightingInformation();
this.syntaxStyle = styleKey;
}
/**
* Sets the syntax style being used for syntax highlighting in this document.
* Sets the syntax style being used for syntax highlighting in this
* document.
* You should call this method if you've created a custom token maker for a
* language not normally supported by <code>RSyntaxTextArea</code>.
*
@@ -558,22 +546,26 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
updateSyntaxHighlightingInformation();
this.syntaxStyle = "text/unknown"; // TODO: Make me public?
}
/**
* Sets the token maker factory used by this document.
*
* @param tmf
* The <code>TokenMakerFactory</code> for this document. If this is
* The <code>TokenMakerFactory</code> for this document. If this
* is
* <code>null</code>, a default factory is used.
*/
public void setTokenMakerFactory(TokenMakerFactory tmf) {
tokenMakerFactory = tmf != null ? tmf : TokenMakerFactory.getDefaultInstance();
}
/**
* Loops through the last-tokens-on-lines array from a specified point onward,
* updating last-token values until they stop changing. This should be called
* when lines are updated/inserted/removed, as doing so may cause lines below to
* Loops through the last-tokens-on-lines array from a specified point
* onward,
* updating last-token values until they stop changing. This should be
* called
* when lines are updated/inserted/removed, as doing so may cause lines
* below to
* change color.
*
* @param line
@@ -581,38 +573,38 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
* @param numLines
* The number of lines in the document.
* @param previousTokenType
* The last-token value of the line just before <code>line</code>.
* The last-token value of the line just before
* <code>line</code>.
* @return The last line that needs repainting.
*/
private int updateLastTokensBelow(int line, int numLines, int previousTokenType) {
int firstLine = line;
// Loop through all lines past our starting point. Update even the last
// line's info, even though there aren't any lines after it that depend
// on it changing for them to be changed, as its state may be used
// elsewhere in the library.
int end = numLines;
// System.err.println("--- end==" + end + " (numLines==" + numLines + ")");
// System.err.println("--- end==" + end + " (numLines==" + numLines +
// ")");
while (line < end) {
setSharedSegment(line); // Sets s's text to that of line 'line' in the document.
setSharedSegment(line); // Sets s's text to that of line 'line' in
// the document.
int oldTokenType = lastTokensOnLines.get(line);
int newTokenType = tokenMaker.getLastTokenTypeOnLine(s, previousTokenType);
// System.err.println("---------------- line " + line + "; oldTokenType==" +
// oldTokenType + ", newTokenType==" + newTokenType + ", s=='" + s + "'");
// System.err.println("---------------- line " + line + ";
// oldTokenType==" +
// oldTokenType + ", newTokenType==" + newTokenType + ", s=='" + s +
// "'");
// If this line's end-token value didn't change, stop here. Note
// that we're saying this line needs repainting; this is because
// the beginning of this line did indeed change color, but the
// end didn't.
if (oldTokenType == newTokenType) {
// System.err.println("... ... ... repainting lines " + firstLine + "-" + line);
// System.err.println("... ... ... repainting lines " +
// firstLine + "-" + line);
fireChangedUpdate(new DefaultDocumentEvent(firstLine, line, DocumentEvent.EventType.CHANGE));
return line;
}
// If the line's end-token value did change, update it and
// keep going.
// NOTE: "setUnsafe" is okay here as the bounds checking was
@@ -620,24 +612,22 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
lastTokensOnLines.setUnsafe(line, newTokenType);
previousTokenType = newTokenType;
line++;
} // End of while (line<numLines).
// If any lines had their token types changed, fire a changed update
// for them. The view will repaint the area covered by the lines.
// FIXME: We currently cheat and send the line range that needs to be
// repainted as the "offset and length" of the change, since this is
// what the view needs. We really should send the actual offset and
// length.
// If any lines had their token types changed, fire a changed update
// for them. The view will repaint the area covered by the lines.
// FIXME: We currently cheat and send the line range that needs to
// be
// repainted as the "offset and length" of the change, since this is
// what the view needs. We really should send the actual offset and
// length.
if (line > firstLine) {
// System.err.println("... ... ... repainting lines " + firstLine + "-" + line);
// System.err.println("... ... ... repainting lines " + firstLine +
// "-" + line);
fireChangedUpdate(new DefaultDocumentEvent(firstLine, line, DocumentEvent.EventType.CHANGE));
}
return line;
}
/**
* Updates internal state information; e.g. the "last tokens on lines" data.
* After this, a changed update is fired to let listeners know that the
@@ -647,7 +637,6 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
* This is called internally whenever the syntax style changes.
*/
private void updateSyntaxHighlightingInformation() {
// Reinitialize the "last token on each line" array. Note that since
// the actual text in the document isn't changing, the number of lines
// is the same.
@@ -659,14 +648,10 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
lastTokenType = tokenMaker.getLastTokenTypeOnLine(s, lastTokenType);
lastTokensOnLines.set(i, lastTokenType);
}
// Clear our token cache to force re-painting
lastLine = -1;
cachedTokenList = null;
// Let everybody know that syntax styles have (probably) changed.
fireChangedUpdate(new DefaultDocumentEvent(0, numLines - 1, DocumentEvent.EventType.CHANGE));
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,10 +1,8 @@
/*
* 10/27/2004
*
* RSyntaxTextAreaDefaultInputMap.java - The default input map for
* RSyntaxTextAreas.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -16,13 +14,12 @@ import javax.swing.KeyStroke;
import org.fife.ui.rtextarea.RTADefaultInputMap;
/**
* The default input map for an <code>RSyntaxTextArea</code>.
* Currently, the new key bindings include:
* <ul>
* <li>Shift+Tab indents the current line or currently selected lines
* to the left.
* <li>Shift+Tab indents the current line or currently selected lines
* to the left.
* </ul>
*
* @author Robert Futrell
@@ -30,50 +27,51 @@ import org.fife.ui.rtextarea.RTADefaultInputMap;
*/
@SuppressWarnings({ "checkstyle:linelength" })
public class RSyntaxTextAreaDefaultInputMap extends RTADefaultInputMap {
/**
* Constructs the default input map for an <code>RSyntaxTextArea</code>.
*/
public RSyntaxTextAreaDefaultInputMap() {
int defaultMod = getDefaultModifier();
//int ctrl = InputEvent.CTRL_MASK;
// int ctrl = InputEvent.CTRL_MASK;
int shift = InputEvent.SHIFT_MASK;
//int alt = InputEvent.ALT_MASK;
int defaultShift = defaultMod|shift;
put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, shift), RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
put(KeyStroke.getKeyStroke('}'), RSyntaxTextAreaEditorKit.rstaCloseCurlyBraceAction);
put(KeyStroke.getKeyStroke('/'), RSyntaxTextAreaEditorKit.rstaCloseMarkupTagAction);
// int alt = InputEvent.ALT_MASK;
int defaultShift = defaultMod | shift;
put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, shift), RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
put(KeyStroke.getKeyStroke('}'), RSyntaxTextAreaEditorKit.rstaCloseCurlyBraceAction);
put(KeyStroke.getKeyStroke('/'), RSyntaxTextAreaEditorKit.rstaCloseMarkupTagAction);
int os = RSyntaxUtilities.getOS();
if (os==RSyntaxUtilities.OS_WINDOWS || os==RSyntaxUtilities.OS_MAC_OSX) {
// *nix causes trouble with CloseMarkupTagAction and ToggleCommentAction.
// It triggers both KEY_PRESSED ctrl+'/' and KEY_TYPED '/' events when the
// user presses ctrl+'/', but Windows and OS X do not. If we try to "move"
// the KEY_TYPED event for '/' to KEY_PRESSED, it'll work for Linux boxes
// with QWERTY keyboard layouts, but non-QUERTY users won't be able to type
// a '/' character at all then (!). Rather than try to hack together a
// solution by trying to detect the IM locale and do different things for
if (os == RSyntaxUtilities.OS_WINDOWS || os == RSyntaxUtilities.OS_MAC_OSX) {
// *nix causes trouble with CloseMarkupTagAction and
// ToggleCommentAction.
// It triggers both KEY_PRESSED ctrl+'/' and KEY_TYPED '/' events
// when the
// user presses ctrl+'/', but Windows and OS X do not. If we try to
// "move"
// the KEY_TYPED event for '/' to KEY_PRESSED, it'll work for Linux
// boxes
// with QWERTY keyboard layouts, but non-QUERTY users won't be able
// to type
// a '/' character at all then (!). Rather than try to hack together
// a
// solution by trying to detect the IM locale and do different
// things for
// different OSes & keyboard layouts, we do the simplest thing and
// (unfortunately) don't have a ToggleCommentAction for *nix out-of-the-box.
// (unfortunately) don't have a ToggleCommentAction for *nix
// out-of-the-box.
// Applications can add one easily enough if they want one.
put(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, defaultMod), RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, defaultMod), RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
}
put(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, defaultMod), RSyntaxTextAreaEditorKit.rstaGoToMatchingBracketAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseFoldAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandFoldAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseAllFoldsAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_MULTIPLY, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandAllFoldsAction);
// NOTE: no modifiers => mapped to keyTyped. If we had "0" as a second
put(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, defaultMod), RSyntaxTextAreaEditorKit.rstaGoToMatchingBracketAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseFoldAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandFoldAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseAllFoldsAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_MULTIPLY, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandAllFoldsAction);
// NOTE: no modifiers => mapped to keyTyped. If we had "0" as a second
// second parameter, we'd get the template action (keyPressed) AND the
// default space action (keyTyped).
//put(KeyStroke.getKeyStroke(' '), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, defaultShift), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
// put(KeyStroke.getKeyStroke(' '),
// RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, defaultShift), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,9 +1,7 @@
/*
* 04/23/2009
*
* RSyntaxTextAreaHighlighter.java - Highlighter for RSyntaxTextAreas.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -27,11 +25,11 @@ import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.fife.ui.rtextarea.RTextAreaHighlighter;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* The highlighter implementation used by {@link RSyntaxTextArea}s. It knows to
* The highlighter implementation used by {@link RSyntaxTextArea}s. It knows to
* always paint "marked occurrences" highlights below selection highlights,
* and squiggle underline highlights above all other highlights.<p>
* and squiggle underline highlights above all other highlights.
* <p>
*
* Most of this code is copied from javax.swing.text.DefaultHighlighter;
* unfortunately, we cannot re-use much of it since it is package private.
@@ -40,34 +38,31 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
* @version 1.0
*/
public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
/**
* Marked occurrences in the document (to be painted separately from
* other highlights).
*/
private List<SyntaxLayeredHighlightInfoImpl> markedOccurrences;
/**
* Highlights from document parsers. These should be painted "on top of"
* Highlights from document parsers. These should be painted "on top of"
* all other highlights to ensure they are always above the selection.
*/
private List<SyntaxLayeredHighlightInfoImpl> parserHighlights;
/**
* The default color used for parser notices when none is specified.
*/
private static final Color DEFAULT_PARSER_NOTICE_COLOR = Color.RED;
private static final Color DEFAULT_PARSER_NOTICE_COLOR = Color.RED;
/**
* Constructor.
*/
public RSyntaxTextAreaHighlighter() {
markedOccurrences = new ArrayList<SyntaxLayeredHighlightInfoImpl>();
parserHighlights = new ArrayList<SyntaxLayeredHighlightInfoImpl>(0); // Often unused
parserHighlights = new ArrayList<SyntaxLayeredHighlightInfoImpl>(0); // Often
// unused
}
/**
* Adds a special "marked occurrence" highlight.
*
@@ -78,8 +73,7 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
* @throws BadLocationException
* @see #clearMarkOccurrencesHighlights()
*/
Object addMarkedOccurrenceHighlight(int start, int end,
SmartHighlightPainter p) throws BadLocationException {
Object addMarkedOccurrenceHighlight(int start, int end, SmartHighlightPainter p) throws BadLocationException {
Document doc = textArea.getDocument();
TextUI mapper = textArea.getUI();
// Always layered highlights for marked occurrences.
@@ -89,43 +83,38 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
// HACK: Use "end-1" to prevent chars the user types at the "end" of
// the highlight to be absorbed into the highlight (default Highlight
// behavior).
i.setEndOffset(doc.createPosition(end-1));
i.setEndOffset(doc.createPosition(end - 1));
markedOccurrences.add(i);
mapper.damageRange(textArea, start, end);
return i;
}
/**
* Adds a highlight from a parser.
*
* @param notice The notice from a {@link Parser}.
* @param notice
* The notice from a {@link Parser}.
* @return A tag with which to reference the highlight.
* @throws BadLocationException
* @see #clearParserHighlights()
* @see #clearParserHighlights(Parser)
*/
HighlightInfo addParserHighlight(ParserNotice notice, HighlightPainter p)
throws BadLocationException {
HighlightInfo addParserHighlight(ParserNotice notice, HighlightPainter p) throws BadLocationException {
Document doc = textArea.getDocument();
TextUI mapper = textArea.getUI();
int start = notice.getOffset();
int end = 0;
if (start==-1) { // Could just define an invalid line number
if (start == -1) { // Could just define an invalid line number
int line = notice.getLine();
Element root = doc.getDefaultRootElement();
if (line>=0 && line<root.getElementCount()) {
if (line >= 0 && line < root.getElementCount()) {
Element elem = root.getElement(line);
start = elem.getStartOffset();
end = elem.getEndOffset();
}
}
else {
} else {
end = start + notice.getLength();
}
// Always layered highlights for parser highlights.
SyntaxLayeredHighlightInfoImpl i = new SyntaxLayeredHighlightInfoImpl();
i.setPainter(p);
@@ -133,16 +122,13 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
// HACK: Use "end-1" to prevent chars the user types at the "end" of
// the highlight to be absorbed into the highlight (default Highlight
// behavior).
i.setEndOffset(doc.createPosition(end-1));
i.notice = notice;//i.color = notice.getColor();
i.setEndOffset(doc.createPosition(end - 1));
i.notice = notice;// i.color = notice.getColor();
parserHighlights.add(i);
mapper.damageRange(textArea, start, end);
return i;
}
/**
* Removes all "marked occurrences" highlights from the view.
*
@@ -156,47 +142,41 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
}
markedOccurrences.clear();
}
/**
* Removes all parser highlights.
*
* @see #addParserHighlight(ParserNotice, javax.swing.text.Highlighter.HighlightPainter)
* @see #addParserHighlight(ParserNotice,
* javax.swing.text.Highlighter.HighlightPainter)
*/
void clearParserHighlights() {
// Don't remove via an iterator; since our List is an ArrayList, this
// implies tons of System.arrayCopy()s
for (int i=0; i<parserHighlights.size(); i++) {
for (int i = 0; i < parserHighlights.size(); i++) {
repaintListHighlight(parserHighlights.get(i));
}
parserHighlights.clear();
}
/**
* Removes all of the highlights for a specific parser.
*
* @param parser The parser.
* @param parser
* The parser.
*/
public void clearParserHighlights(Parser parser) {
Iterator<SyntaxLayeredHighlightInfoImpl> i = parserHighlights.iterator();
while (i.hasNext()) {
SyntaxLayeredHighlightInfoImpl info = i.next();
if (info.notice.getParser()==parser) {
if (info.width > 0 && info.height > 0) {
textArea.repaint(info.x, info.y, info.width, info.height);
}
if (info.notice.getParser() == parser) {
if (info.width > 0 && info.height > 0) {
textArea.repaint(info.x, info.y, info.width, info.height);
}
i.remove();
}
}
}
/**
* {@inheritDoc}
*/
@@ -206,13 +186,12 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
markedOccurrences.clear();
parserHighlights.clear();
}
/**
* Returns a list of "marked occurrences" in the text area. If there are
* Returns a list of "marked occurrences" in the text area. If there are
* no marked occurrences, this will be an empty list.
*
* @return The list of marked occurrences, or an empty list if none. The
* @return The list of marked occurrences, or an empty list if none. The
* contents of this list will be of type {@link DocumentRange}.
*/
public List<DocumentRange> getMarkedOccurrences() {
@@ -231,61 +210,53 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
}
return list;
}
@Override
public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd,
Shape viewBounds, JTextComponent editor, View view) {
paintListLayered(g, lineStart,lineEnd, viewBounds, editor, view, markedOccurrences);
public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor, View view) {
paintListLayered(g, lineStart, lineEnd, viewBounds, editor, view, markedOccurrences);
super.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view);
paintListLayered(g, lineStart,lineEnd, viewBounds, editor, view, parserHighlights);
paintListLayered(g, lineStart, lineEnd, viewBounds, editor, view, parserHighlights);
}
/**
* Removes a parser highlight from this view.
*
* @param tag The reference to the highlight.
* @see #addParserHighlight(ParserNotice, javax.swing.text.Highlighter.HighlightPainter)
* @param tag
* The reference to the highlight.
* @see #addParserHighlight(ParserNotice,
* javax.swing.text.Highlighter.HighlightPainter)
*/
void removeParserHighlight(HighlightInfo tag) {
repaintListHighlight(tag);
parserHighlights.remove(tag);
}
/**
* Highlight info implementation used for parser notices and marked
* occurrences.
*/
private static class SyntaxLayeredHighlightInfoImpl extends
LayeredHighlightInfoImpl {
private ParserNotice notice;//Color color; // Used only by Parser highlights.
private static class SyntaxLayeredHighlightInfoImpl extends LayeredHighlightInfoImpl {
private ParserNotice notice;// Color color; // Used only by Parser
// highlights.
@Override
public Color getColor() {
//return color;
// return color;
Color color = null;
if (notice!=null) {
if (notice != null) {
color = notice.getColor();
if (color==null) {
if (color == null) {
color = DEFAULT_PARSER_NOTICE_COLOR;
}
}
return color;
}
@Override
public String toString() {
return "[SyntaxLayeredHighlightInfoImpl: " +
"startOffs=" + getStartOffset() +
", endOffs=" + getEndOffset() +
", color=" + getColor() +
"]";
return "[SyntaxLayeredHighlightInfoImpl: " + "startOffs=" + getStartOffset() + ", endOffs=" + getEndOffset() + ", color="
+ getColor() + "]";
}
}
}
}
@@ -1,9 +1,7 @@
/*
* 02/24/2004
*
* RSyntaxTextAreaUI.java - UI for an RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -28,40 +26,37 @@ import javax.swing.text.View;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextAreaUI;
/**
* UI used by <code>RSyntaxTextArea</code>. This allows us to implement
* UI used by <code>RSyntaxTextArea</code>. This allows us to implement
* syntax highlighting.
*
* @author Robert Futrell
* @version 0.1
*/
public class RSyntaxTextAreaUI extends RTextAreaUI {
private static final String SHARED_ACTION_MAP_NAME = "RSyntaxTextAreaUI.actionMap";
private static final String SHARED_INPUT_MAP_NAME = "RSyntaxTextAreaUI.inputMap";
private static final EditorKit DEFAULT_KIT = new RSyntaxTextAreaEditorKit();
private static final String SHARED_ACTION_MAP_NAME = "RSyntaxTextAreaUI.actionMap";
private static final String SHARED_INPUT_MAP_NAME = "RSyntaxTextAreaUI.inputMap";
private static final EditorKit DEFAULT_KIT = new RSyntaxTextAreaEditorKit();
public static ComponentUI createUI(JComponent ta) {
return new RSyntaxTextAreaUI(ta);
}
/**
* Constructor.
*/
public RSyntaxTextAreaUI(JComponent rSyntaxTextArea) {
super(rSyntaxTextArea);
}
/**
* Creates the view for an element.
*
* @param elem The element.
* @return The view.
*/
/**
* Creates the view for an element.
*
* @param elem
* The element.
* @return The view.
*/
@Override
public View create(Element elem) {
RTextArea c = getRTextArea();
@@ -70,16 +65,14 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
View v;
if (area.getLineWrap()) {
v = new WrappedSyntaxView(elem);
}
else {
} else {
v = new SyntaxView(elem);
}
return v;
}
return null;
}
/**
* Creates the highlighter to use for syntax text areas.
*
@@ -89,10 +82,9 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
protected Highlighter createHighlighter() {
return new RSyntaxTextAreaHighlighter();
}
/**
* Returns the name to use to cache/fetch the shared action map. This
* Returns the name to use to cache/fetch the shared action map. This
* should be overridden by subclasses if the subclass has its own custom
* editor kit to install, so its actions get picked up.
*
@@ -102,12 +94,12 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
protected String getActionMapName() {
return SHARED_ACTION_MAP_NAME;
}
/**
* Fetches the EditorKit for the UI.
*
* @param tc The text component for which this UI is installed.
* @param tc
* The text component for which this UI is installed.
* @return The editor capabilities.
* @see javax.swing.plaf.TextUI#getEditorKit
*/
@@ -115,10 +107,10 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
public EditorKit getEditorKit(JTextComponent tc) {
return DEFAULT_KIT;
}
/**
* Get the InputMap to use for the UI.<p>
* Get the InputMap to use for the UI.
* <p>
*
* This method is not named <code>getInputMap()</code> because there is
* a package-private method in <code>BasicTextAreaUI</code> with that name.
@@ -129,110 +121,101 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
@Override
protected InputMap getRTextAreaInputMap() {
InputMap map = new InputMapUIResource();
InputMap shared = (InputMap)UIManager.get(SHARED_INPUT_MAP_NAME);
if (shared==null) {
InputMap shared = (InputMap) UIManager.get(SHARED_INPUT_MAP_NAME);
if (shared == null) {
shared = new RSyntaxTextAreaDefaultInputMap();
UIManager.put(SHARED_INPUT_MAP_NAME, shared);
}
//KeyStroke[] keys = shared.allKeys();
//for (int i=0; i<keys.length; i++)
// System.err.println(keys[i] + " -> " + shared.get(keys[i]));
// KeyStroke[] keys = shared.allKeys();
// for (int i=0; i<keys.length; i++)
// System.err.println(keys[i] + " -> " + shared.get(keys[i]));
map.setParent(shared);
return map;
}
@Override
protected void paintEditorAugmentations(Graphics g) {
super.paintEditorAugmentations(g);
paintMatchedBracket(g);
}
/**
* Paints the "matched bracket", if any.
*
* @param g The graphics context.
* @param g
* The graphics context.
*/
protected void paintMatchedBracket(Graphics g) {
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
if (rsta.isBracketMatchingEnabled()) {
Rectangle match = rsta.getMatchRectangle();
if (match!=null) {
if (match != null) {
paintMatchedBracketImpl(g, rsta, match);
}
if (rsta.getPaintMatchedBracketPair()) {
Rectangle dotRect = rsta.getDotRectangle();
if (dotRect!=null) { // should always be true
if (dotRect != null) { // should always be true
paintMatchedBracketImpl(g, rsta, dotRect);
}
}
}
}
protected void paintMatchedBracketImpl(Graphics g, RSyntaxTextArea rsta,
Rectangle r) {
protected void paintMatchedBracketImpl(Graphics g, RSyntaxTextArea rsta, Rectangle r) {
// We must add "-1" to the height because otherwise we'll paint below
// the region that gets invalidated.
if (rsta.getAnimateBracketMatching()) {
Color bg = rsta.getMatchedBracketBGColor();
final int arcWH = 5;
if (bg!=null) {
if (bg != null) {
g.setColor(bg);
g.fillRoundRect(r.x,r.y, r.width,r.height-1, arcWH, arcWH);
g.fillRoundRect(r.x, r.y, r.width, r.height - 1, arcWH, arcWH);
}
g.setColor(rsta.getMatchedBracketBorderColor());
g.drawRoundRect(r.x,r.y, r.width,r.height-1, arcWH, arcWH);
}
else {
g.drawRoundRect(r.x, r.y, r.width, r.height - 1, arcWH, arcWH);
} else {
Color bg = rsta.getMatchedBracketBGColor();
if (bg!=null) {
if (bg != null) {
g.setColor(bg);
g.fillRect(r.x,r.y, r.width,r.height-1);
g.fillRect(r.x, r.y, r.width, r.height - 1);
}
g.setColor(rsta.getMatchedBracketBorderColor());
g.drawRect(r.x,r.y, r.width,r.height-1);
g.drawRect(r.x, r.y, r.width, r.height - 1);
}
}
/**
* Gets called whenever a bound property is changed on this UI's
* <code>RSyntaxTextArea</code>.
*
* @param e The property change event.
* @param e
* The property change event.
*/
@Override
protected void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
// If they change the syntax scheme, we must do this so that
// WrappedSyntaxView(_TEST) updates its child views properly.
if (name.equals(RSyntaxTextArea.SYNTAX_SCHEME_PROPERTY)) {
modelChanged();
}
// Everything else is general to all RTextAreas.
else {
super.propertyChange(e);
}
}
/**
* Updates the view. This should be called when the underlying
* Updates the view. This should be called when the underlying
* <code>RSyntaxTextArea</code> changes its syntax editing style.
*/
public void refreshSyntaxHighlighting() {
modelChanged();
}
/**
* Returns the y-coordinate of the specified line.<p>
* Returns the y-coordinate of the specified line.
* <p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
@@ -241,16 +224,16 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
@Override
public int yForLine(int line) throws BadLocationException {
Rectangle alloc = getVisibleEditorRect();
if (alloc!=null) {
RSTAView view = (RSTAView)getRootView(textArea).getView(0);
if (alloc != null) {
RSTAView view = (RSTAView) getRootView(textArea).getView(0);
return view.yForLine(alloc, line);
}
return -1;
}
/**
* Returns the y-coordinate of the line containing a specified offset.<p>
* Returns the y-coordinate of the line containing a specified offset.
* <p>
*
* This is faster than calling <code>modelToView(offs).y</code>, so it is
* preferred if you do not need the actual bounding box.
@@ -258,12 +241,10 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
@Override
public int yForLineContaining(int offs) throws BadLocationException {
Rectangle alloc = getVisibleEditorRect();
if (alloc!=null) {
RSTAView view = (RSTAView)getRootView(textArea).getView(0);
if (alloc != null) {
RSTAView view = (RSTAView) getRootView(textArea).getView(0);
return view.yForLineContaining(alloc, offs);
}
return -1;
}
}
}
@@ -3,7 +3,7 @@ ContextMenu.Folding=Klappung
Action.ToggleCurrentFold.Name=Aktuellen Block umschalten
Action.ToggleCurrentFold.Mnemonic=F
Action.ToggleCurrentFold.Desc=Schaltet den Block an der Position der Schreibmarke um.
Action.CollapseAllFolds.Name=Action.CollapseAllFolds.Name=Alle Bl\u00f6cke zuklappen
Action.CollapseAllFolds.Name=Alle Bl\u00f6cke zuklappen
Action.CollapseAllFolds.Mnemonic=O
Action.CollapseAllFolds.Desc=Klappt alle Bl\u00f6cke zu.
Action.CollapseCommentFolds.Name=Alle Kommentarbl\u00f6cke zuklappen
File diff suppressed because it is too large. Load diff
+59 -72
View File
@@ -1,9 +1,7 @@
/*
* 07/28/2008
*
* RtfToText.java - Returns the plain text version of RTF documents.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -18,9 +16,9 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
/**
* Gets the plain text version of RTF documents.<p>
* Gets the plain text version of RTF documents.
* <p>
*
* This is used by <code>RtfTransferable</code> to return the plain text
* version of the transferable when the receiver does not support RTF.
@@ -29,18 +27,18 @@ import java.io.StringReader;
* @version 1.0
*/
final class RtfToText {
private Reader r;
private StringBuilder sb;
private StringBuilder controlWord;
private int blockCount;
private boolean inControlWord;
/**
* Private constructor.
*
* @param r The reader to read RTF text from.
* @param r
* The reader to read RTF text from.
*/
private RtfToText(Reader r) {
this.r = r;
@@ -49,79 +47,71 @@ final class RtfToText {
blockCount = 0;
inControlWord = false;
}
/**
* Converts the RTF text read from this converter's <code>Reader</code>
* into plain text. It is the caller's responsibility to close the
* into plain text. It is the caller's responsibility to close the
* reader after this method is called.
*
* @return The plain text.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/
private String convert() throws IOException {
// Skip over first curly brace as the whole file is in '{' and '}'
int i = r.read();
if (i!='{') {
if (i != '{') {
throw new IOException("Invalid RTF file");
}
while ((i=r.read())!=-1) {
char ch = (char)i;
while ((i = r.read()) != -1) {
char ch = (char) i;
switch (ch) {
case '{':
if (inControlWord && controlWord.length()==0) { // "\{"
if (inControlWord && controlWord.length() == 0) { // "\{"
sb.append('{');
controlWord.setLength(0);
inControlWord = false;
}
else {
} else {
blockCount++;
}
break;
case '}':
if (inControlWord && controlWord.length()==0) { // "\}"
if (inControlWord && controlWord.length() == 0) { // "\}"
sb.append('}');
controlWord.setLength(0);
inControlWord = false;
}
else {
} else {
blockCount--;
}
break;
case '\\':
if (blockCount==0) {
if (blockCount == 0) {
if (inControlWord) {
if (controlWord.length()==0) { // "\\"
if (controlWord.length() == 0) { // "\\"
sb.append('\\');
controlWord.setLength(0);
inControlWord = false;
}
else {
} else {
endControlWord();
inControlWord = true;
}
}
else {
} else {
inControlWord = true;
}
}
break;
case ' ':
if (blockCount==0) {
if (blockCount == 0) {
if (inControlWord) {
endControlWord();
}
else {
} else {
sb.append(' ');
}
}
break;
case '\r':
case '\n':
if (blockCount==0) {
if (blockCount == 0) {
if (inControlWord) {
endControlWord();
}
@@ -129,26 +119,21 @@ final class RtfToText {
}
break;
default:
if (blockCount==0) {
if (blockCount == 0) {
if (inControlWord) {
controlWord.append(ch);
}
else {
} else {
sb.append(ch);
}
}
break;
}
}
return sb.toString();
}
/**
* Ends a control word. Checks whether it is a common one that affects
* Ends a control word. Checks whether it is a common one that affects
* the plain text output (such as "<code>par</code>" or "<code>tab</code>")
* and updates the text buffer accordingly.
*/
@@ -156,82 +141,84 @@ final class RtfToText {
String word = controlWord.toString();
if ("par".equals(word)) {
sb.append('\n');
}
else if ("tab".equals(word)) {
} else if ("tab".equals(word)) {
sb.append('\t');
}
controlWord.setLength(0);
inControlWord = false;
}
/**
* Converts the contents of the specified byte array representing
* an RTF document into plain text.
*
* @param rtf The byte array representing an RTF document.
* @param rtf
* The byte array representing an RTF document.
* @return The contents of the RTF document, in plain text.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/
public static String getPlainText(byte[] rtf) throws IOException {
return getPlainText(new ByteArrayInputStream(rtf));
}
/**
* Converts the contents of the specified RTF file to plain text.
*
* @param file The RTF file to convert.
* @param file
* The RTF file to convert.
* @return The contents of the file, in plain text.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/
public static String getPlainText(File file) throws IOException {
return getPlainText(new BufferedReader(new FileReader(file)));
}
/**
* Converts the contents of the specified input stream to plain text.
* The input stream will be closed when this method returns.
*
* @param in The input stream to convert. This will be closed when this
* method returns.
* @param in
* The input stream to convert. This will be closed when this
* method returns.
* @return The contents of the stream, in plain text.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/
public static String getPlainText(InputStream in) throws IOException {
return getPlainText(new InputStreamReader(in, "US-ASCII"));
}
/**
* Converts the contents of the specified <code>Reader</code> to plain text.
*
* @param r The <code>Reader</code>. This will be closed when this method
* returns.
* @param r
* The <code>Reader</code>. This will be closed when this method
* returns.
* @return The contents of the <code>Reader</code>, in plain text.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/
private static String getPlainText(Reader r) throws IOException {
try {
RtfToText converter = new RtfToText(r);
RtfToText converter = new RtfToText(r);
return converter.convert();
} finally {
r.close();
}
}
/**
* Converts the contents of the specified String to plain text.
*
* @param rtf A string whose contents represent an RTF document.
* @param rtf
* A string whose contents represent an RTF document.
* @return The contents of the String, in plain text.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/
public static String getPlainText(String rtf) throws IOException {
return getPlainText(new StringReader(rtf));
}
}
}
+143 -166
View File
@@ -1,9 +1,7 @@
/*
* 07/28/2008
*
* RtfGenerator.java - Generates RTF via a simple Java API.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -16,33 +14,33 @@ import java.util.List;
import org.fife.ui.rtextarea.RTextArea;
/**
* Generates RTF text via a simple Java API.<p>
* Generates RTF text via a simple Java API.
* <p>
*
* The following RTF features are supported:
* <ul>
* <li>Fonts
* <li>Font sizes
* <li>Foreground and background colors
* <li>Bold, italic, and underline
* <li>Fonts
* <li>Font sizes
* <li>Foreground and background colors
* <li>Bold, italic, and underline
* </ul>
*
* The RTF generated isn't really "optimized," but it will do, especially for
* small amounts of text, such as what's common when copy-and-pasting. It
* small amounts of text, such as what's common when copy-and-pasting. It
* tries to be sufficient for the use case of copying syntax highlighted
* code:
* <ul>
* <li>It assumes that tokens changing foreground color often is fairly
* common.
* <li>It assumes that background highlighting is fairly uncommon.
* <li>It assumes that tokens changing foreground color often is fairly
* common.
* <li>It assumes that background highlighting is fairly uncommon.
* </ul>
*
* @author Robert Futrell
* @version 1.0
*/
public class RtfGenerator {
private List<Font> fontList;
private List<Color> colorList;
private StringBuilder document;
@@ -52,22 +50,19 @@ public class RtfGenerator {
private boolean lastBold;
private boolean lastItalic;
private int lastFontSize;
/**
* Java2D assumes a 72 dpi screen resolution, but on Windows the screen
* resolution is either 96 dpi or 120 dpi, depending on your font display
* settings. This is an attempt to make the RTF generated match the
* settings. This is an attempt to make the RTF generated match the
* size of what's displayed in the RSyntaxTextArea.
*/
private int screenRes;
/**
* The default font size for RTF. This is point size, in half
* The default font size for RTF. This is point size, in half
* points.
*/
private static final int DEFAULT_FONT_SIZE = 12;//24;
private static final int DEFAULT_FONT_SIZE = 12;// 24;
/**
* Constructor.
*/
@@ -77,8 +72,7 @@ public class RtfGenerator {
document = new StringBuilder();
reset();
}
/**
* Adds a newline to the RTF document.
*
@@ -89,110 +83,118 @@ public class RtfGenerator {
document.append('\n'); // Just for ease of reading RTF.
lastWasControlWord = false;
}
/**
* Appends styled text to the RTF document being generated.
*
* @param text The text to append.
* @param f The font of the text. If this is <code>null</code>, the
* default font is used.
* @param fg The foreground of the text. If this is <code>null</code>,
* the default foreground color is used.
* @param bg The background color of the text. If this is
* <code>null</code>, the default background color is used.
* @param text
* The text to append.
* @param f
* The font of the text. If this is <code>null</code>, the
* default font is used.
* @param fg
* The foreground of the text. If this is <code>null</code>,
* the default foreground color is used.
* @param bg
* The background color of the text. If this is
* <code>null</code>, the default background color is used.
* @see #appendNewline()
*/
public void appendToDoc(String text, Font f, Color fg, Color bg) {
appendToDoc(text, f, fg, bg, false);
}
/**
* Appends styled text to the RTF document being generated.
*
* @param text The text to append.
* @param f The font of the text. If this is <code>null</code>, the
* default font is used.
* @param bg The background color of the text. If this is
* <code>null</code>, the default background color is used.
* @param underline Whether the text should be underlined.
* @param text
* The text to append.
* @param f
* The font of the text. If this is <code>null</code>, the
* default font is used.
* @param bg
* The background color of the text. If this is
* <code>null</code>, the default background color is used.
* @param underline
* Whether the text should be underlined.
* @see #appendNewline()
*/
public void appendToDocNoFG(String text, Font f, Color bg,
boolean underline) {
public void appendToDocNoFG(String text, Font f, Color bg, boolean underline) {
appendToDoc(text, f, null, bg, underline, false);
}
/**
* Appends styled text to the RTF document being generated.
*
* @param text The text to append.
* @param f The font of the text. If this is <code>null</code>, the
* default font is used.
* @param fg The foreground of the text. If this is <code>null</code>,
* the default foreground color is used.
* @param bg The background color of the text. If this is
* <code>null</code>, the default background color is used.
* @param underline Whether the text should be underlined.
* @param text
* The text to append.
* @param f
* The font of the text. If this is <code>null</code>, the
* default font is used.
* @param fg
* The foreground of the text. If this is <code>null</code>,
* the default foreground color is used.
* @param bg
* The background color of the text. If this is
* <code>null</code>, the default background color is used.
* @param underline
* Whether the text should be underlined.
* @see #appendNewline()
*/
public void appendToDoc(String text, Font f, Color fg, Color bg,
boolean underline) {
public void appendToDoc(String text, Font f, Color fg, Color bg, boolean underline) {
appendToDoc(text, f, fg, bg, underline, true);
}
/**
* Appends styled text to the RTF document being generated.
*
* @param text The text to append.
* @param f The font of the text. If this is <code>null</code>, the
* default font is used.
* @param fg The foreground of the text. If this is <code>null</code>,
* the default foreground color is used.
* @param bg The background color of the text. If this is
* <code>null</code>, the default background color is used.
* @param underline Whether the text should be underlined.
* @param setFG Whether the foreground specified by <code>fg</code> should
* be honored (if it is non-<code>null</code>).
* @param text
* The text to append.
* @param f
* The font of the text. If this is <code>null</code>, the
* default font is used.
* @param fg
* The foreground of the text. If this is <code>null</code>,
* the default foreground color is used.
* @param bg
* The background color of the text. If this is
* <code>null</code>, the default background color is used.
* @param underline
* Whether the text should be underlined.
* @param setFG
* Whether the foreground specified by <code>fg</code> should
* be honored (if it is non-<code>null</code>).
* @see #appendNewline()
*/
public void appendToDoc(String text, Font f, Color fg, Color bg,
boolean underline, boolean setFG) {
if (text!=null) {
public void appendToDoc(String text, Font f, Color fg, Color bg, boolean underline, boolean setFG) {
if (text != null) {
// Set font to use, if different from last addition.
int fontIndex = f==null ? 0 : (getFontIndex(fontList, f)+1);
if (fontIndex!=lastFontIndex) {
int fontIndex = f == null ? 0 : (getFontIndex(fontList, f) + 1);
if (fontIndex != lastFontIndex) {
document.append("\\f").append(fontIndex);
lastFontIndex = fontIndex;
lastWasControlWord = true;
}
// Set styles to use.
if (f!=null) {
int fontSize = fixFontSize(f.getSize2D()*2); // Half points!
if (fontSize!=lastFontSize) {
if (f != null) {
int fontSize = fixFontSize(f.getSize2D() * 2); // Half points!
if (fontSize != lastFontSize) {
document.append("\\fs").append(fontSize);
lastFontSize = fontSize;
lastWasControlWord = true;
}
if (f.isBold()!=lastBold) {
if (f.isBold() != lastBold) {
document.append(lastBold ? "\\b0" : "\\b");
lastBold = !lastBold;
lastWasControlWord = true;
}
if (f.isItalic()!=lastItalic) {
if (f.isItalic() != lastItalic) {
document.append(lastItalic ? "\\i0" : "\\i");
lastItalic = !lastItalic;
lastWasControlWord = true;
}
}
else { // No font specified - assume neither bold nor italic.
if (lastFontSize!=DEFAULT_FONT_SIZE) {
} else { // No font specified - assume neither bold nor italic.
if (lastFontSize != DEFAULT_FONT_SIZE) {
document.append("\\fs").append(DEFAULT_FONT_SIZE);
lastFontSize = DEFAULT_FONT_SIZE;
lastWasControlWord = true;
@@ -212,35 +214,31 @@ public class RtfGenerator {
document.append("\\ul");
lastWasControlWord = true;
}
// Set the foreground color.
if (setFG) {
int fgIndex = 0;
if (fg!=null) { // null => fg color index 0
fgIndex = getColorIndex(colorList, fg)+1;
if (fg != null) { // null => fg color index 0
fgIndex = getColorIndex(colorList, fg) + 1;
}
if (fgIndex!=lastFGIndex) {
if (fgIndex != lastFGIndex) {
document.append("\\cf").append(fgIndex);
lastFGIndex = fgIndex;
lastWasControlWord = true;
}
}
// Set the background color.
if (bg!=null) {
if (bg != null) {
int pos = getColorIndex(colorList, bg);
document.append("\\highlight").append(pos+1);
document.append("\\highlight").append(pos + 1);
lastWasControlWord = true;
}
if (lastWasControlWord) {
document.append(' '); // Delimiter
lastWasControlWord = false;
}
escapeAndAdd(document, text);
// Reset everything that was set for this text fragment.
if (bg!=null) {
if (bg != null) {
document.append("\\highlight0");
lastWasControlWord = true;
}
@@ -248,28 +246,27 @@ public class RtfGenerator {
document.append("\\ul0");
lastWasControlWord = true;
}
}
}
/**
* Appends some text to a buffer, with special care taken for special
* characters as defined by the RTF spec.
*
* <ul>
* <li>All tab characters are replaced with the string
* "<code>\tab</code>"
* <li>'\', '{' and '}' are changed to "\\", "\{" and "\}"
* <li>All tab characters are replaced with the string
* "<code>\tab</code>"
* <li>'\', '{' and '}' are changed to "\\", "\{" and "\}"
* </ul>
*
* @param text The text to append (with tab chars substituted).
* @param sb The buffer to append to.
* @param text
* The text to append (with tab chars substituted).
* @param sb
* The buffer to append to.
*/
private void escapeAndAdd(StringBuilder sb, String text) {
int count = text.length();
for (int i=0; i<count; i++) {
for (int i = 0; i < count; i++) {
char ch = text.charAt(i);
switch (ch) {
case '\t':
@@ -278,7 +275,7 @@ public class RtfGenerator {
// back-to-back at the start of lines, so don't put
// spaces between each "\tab".
sb.append("\\tab");
while ((++i<count) && text.charAt(i)=='\t') {
while ((++i < count) && text.charAt(i) == '\t') {
sb.append("\\tab");
}
sb.append(' ');
@@ -293,61 +290,59 @@ public class RtfGenerator {
if (ch <= 127) {
sb.append(ch);
} else {
sb.append("\\u").append((int)ch);
sb.append("\\u").append((int) ch);
}
break;
}
}
}
/**
* Returns a font point size, adjusted for the current screen resolution.<p>
* Returns a font point size, adjusted for the current screen resolution.
* <p>
*
* Java2D assumes 72 dpi. On systems with larger dpi (Windows, GTK, etc.),
* Java2D assumes 72 dpi. On systems with larger dpi (Windows, GTK, etc.),
* font rendering will appear too small if we simply return a Java "Font"
* object's getSize() value. We need to adjust it for the screen
* object's getSize() value. We need to adjust it for the screen
* resolution.
*
* @param pointSize A Java Font's point size, as returned from
* <code>getSize2D()</code>.
* @param pointSize
* A Java Font's point size, as returned from
* <code>getSize2D()</code>.
* @return The font point size, adjusted for the current screen resolution.
* This will allow other applications to render fonts the same
* size as they appear in the Java application.
*/
private int fixFontSize(float pointSize) {
if (screenRes!=72) { // Java2D assumes 72 dpi
pointSize = Math.round(pointSize*72f/screenRes);
if (screenRes != 72) { // Java2D assumes 72 dpi
pointSize = Math.round(pointSize * 72f / screenRes);
}
return (int)pointSize;
return (int) pointSize;
}
/**
* Returns the index of the specified item in a list. If the item
* Returns the index of the specified item in a list. If the item
* is not in the list, it is added, and its new index is returned.
*
* @param list The list (possibly) containing the item.
* @param item The item to get the index of.
* @param list
* The list (possibly) containing the item.
* @param item
* The item to get the index of.
* @return The index of the item.
*/
private static int getColorIndex(List<Color> list, Color item) {
int pos = list.indexOf(item);
if (pos==-1) {
if (pos == -1) {
list.add(item);
pos = list.size()-1;
pos = list.size() - 1;
}
return pos;
}
private String getColorTableRtf() {
// Example:
// "{\\colortbl ;\\red255\\green0\\blue0;\\red0\\green0\\blue255; }"
StringBuilder sb = new StringBuilder();
sb.append("{\\colortbl ;");
for (Color c : colorList) {
sb.append("\\red").append(c.getRed());
@@ -356,66 +351,58 @@ public class RtfGenerator {
sb.append(';');
}
sb.append("}");
return sb.toString();
}
/**
* Returns the index of the specified font in a list of fonts. This
* Returns the index of the specified font in a list of fonts. This
* method only checks for a font by its family name; its attributes such
* as bold and italic are ignored.<p>
* as bold and italic are ignored.
* <p>
*
* If the font is not in the list, it is added, and its new index is
* returned.
*
* @param list The list (possibly) containing the font.
* @param font The font to get the index of.
* @param list
* The list (possibly) containing the font.
* @param font
* The font to get the index of.
* @return The index of the font.
*/
private static int getFontIndex(List<Font> list, Font font) {
String fontName = font.getFamily();
for (int i=0; i<list.size(); i++) {
for (int i = 0; i < list.size(); i++) {
Font font2 = list.get(i);
if (font2.getFamily().equals(fontName)) {
return i;
}
}
list.add(font);
return list.size()-1;
return list.size() - 1;
}
private String getFontTableRtf() {
// Example:
// "{\\fonttbl{\\f0\\fmodern\\fcharset0 Courier;}}"
StringBuilder sb = new StringBuilder();
// Workaround for text areas using the Java logical font "Monospaced"
// by default. There's no way to know what it's mapped to, so we
// by default. There's no way to know what it's mapped to, so we
// just search for a monospaced font on the system.
String monoFamilyName = getMonospacedFontFamily();
sb.append("{\\fonttbl{\\f0\\fnil\\fcharset0 " + monoFamilyName + ";}");
for (int i=0; i<fontList.size(); i++) {
for (int i = 0; i < fontList.size(); i++) {
Font f = fontList.get(i);
String familyName = f.getFamily();
if (familyName.equals("Monospaced")) {
familyName = monoFamilyName;
}
sb.append("{\\f").append(i+1).append("\\fnil\\fcharset0 ");
sb.append("{\\f").append(i + 1).append("\\fnil\\fcharset0 ");
sb.append(familyName).append(";}");
}
sb.append('}');
return sb.toString();
}
/**
* Returns a good "default" monospaced font to use when Java's logical
* font "Monospaced" is found.
@@ -429,41 +416,33 @@ public class RtfGenerator {
}
return family;
}
/**
* Returns the RTF document created by this generator.
*
* @return The RTF document, as a <code>String</code>.
*/
public String getRtf() {
StringBuilder sb = new StringBuilder();
sb.append("{");
// Header
sb.append("\\rtf1\\ansi\\ansicpg1252");
sb.append("\\deff0"); // First font in font table is the default
sb.append("\\deflang1033");
sb.append("\\viewkind4"); // "Normal" view
sb.append("\\viewkind4"); // "Normal" view
sb.append("\\uc\\pard\\f0");
sb.append("\\fs20"); // Font size in half-points (default 24)
sb.append("\\fs20"); // Font size in half-points (default 24)
sb.append(getFontTableRtf()).append('\n');
sb.append(getColorTableRtf()).append('\n');
// Content
sb.append(document);
sb.append("}");
//System.err.println("*** " + sb.length());
// System.err.println("*** " + sb.length());
return sb.toString();
}
/**
* Resets this generator. All document information and content is
* Resets this generator. All document information and content is
* cleared.
*/
public void reset() {
@@ -478,6 +457,4 @@ public class RtfGenerator {
lastFontSize = DEFAULT_FONT_SIZE;
screenRes = Toolkit.getDefaultToolkit().getScreenResolution();
}
}
}
@@ -1,9 +1,7 @@
/*
* 07/28/2008
*
* RtfTransferable.java - Used during drag-and-drop to represent RTF text.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -15,10 +13,9 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
/**
* Object used during copy/paste and DnD operations to represent RTF text.
* It can return the text being moved as either RTF or plain text. This
* It can return the text being moved as either RTF or plain text. This
* class is basically the same as
* <code>java.awt.datatransfer.StringSelection</code>, except that it can also
* return the text as RTF.
@@ -27,70 +24,56 @@ import java.io.StringReader;
* @version 1.0
*/
class RtfTransferable implements Transferable {
/**
* The RTF data, in bytes (the RTF is 7-bit ascii).
*/
private byte[] data;
/**
* The "flavors" the text can be returned as.
*/
private static final DataFlavor[] FLAVORS = {
new DataFlavor("text/rtf", "RTF"),
DataFlavor.stringFlavor,
DataFlavor.plainTextFlavor // deprecated
private static final DataFlavor[] FLAVORS = { new DataFlavor("text/rtf", "RTF"), DataFlavor.stringFlavor, DataFlavor.plainTextFlavor // deprecated
};
/**
* Constructor.
*
* @param data The RTF data.
* @param data
* The RTF data.
*/
RtfTransferable(byte[] data) {
this.data = data;
}
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (flavor.equals(FLAVORS[0])) { // RTF
return new ByteArrayInputStream(data==null ? new byte[0] : data);
}
else if (flavor.equals(FLAVORS[1])) { // stringFlavor
return data==null ? "" : RtfToText.getPlainText(data);
}
else if (flavor.equals(FLAVORS[2])) { // plainTextFlavor (deprecated)
return new ByteArrayInputStream(data == null ? new byte[0] : data);
} else if (flavor.equals(FLAVORS[1])) { // stringFlavor
return data == null ? "" : RtfToText.getPlainText(data);
} else if (flavor.equals(FLAVORS[2])) { // plainTextFlavor (deprecated)
String text = ""; // Valid if data==null
if (data!=null) {
if (data != null) {
text = RtfToText.getPlainText(data);
}
return new StringReader(text);
}
else {
} else {
throw new UnsupportedFlavorException(flavor);
}
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return FLAVORS.clone();
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
for (int i=0; i<FLAVORS.length; i++) {
for (int i = 0; i < FLAVORS.length; i++) {
if (flavor.equals(FLAVORS[i])) {
return true;
}
}
return false;
}
}
}
@@ -1,18 +1,15 @@
/*
* 02/16/2012
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.event.HyperlinkEvent;
/**
* A link generator result that selects a region of text in the text area.
* This will typically be used by IDE-style applications, to provide support
@@ -23,22 +20,19 @@ import javax.swing.event.HyperlinkEvent;
* @see LinkGenerator
*/
public class SelectRegionLinkGeneratorResult implements LinkGeneratorResult {
private RSyntaxTextArea textArea;
private int sourceOffset;
private int selStart;
private int selEnd;
public SelectRegionLinkGeneratorResult(RSyntaxTextArea textArea,
int sourceOffset, int selStart, int selEnd) {
public SelectRegionLinkGeneratorResult(RSyntaxTextArea textArea, int sourceOffset, int selStart, int selEnd) {
this.textArea = textArea;
this.sourceOffset = sourceOffset;
this.selStart = selStart;
this.selEnd = selEnd;
}
/**
* Selects the text in the text area.
*/
@@ -47,8 +41,7 @@ public class SelectRegionLinkGeneratorResult implements LinkGeneratorResult {
textArea.select(selStart, selEnd);
return null;
}
/**
* {@inheritDoc}
*/
@@ -56,6 +49,4 @@ public class SelectRegionLinkGeneratorResult implements LinkGeneratorResult {
public int getSourceOffset() {
return sourceOffset;
}
}
}
@@ -1,11 +1,9 @@
/*
* 09/13/2005
*
* SquiggleUnderlineHighlightPainter.java - Highlighter that draws a squiggle
* underline under "highlighted" text, similar to error markers in Microsoft
* Visual Studio or Eclipse.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -14,6 +12,7 @@ import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
@@ -21,102 +20,96 @@ import javax.swing.text.View;
import org.fife.ui.rtextarea.ChangeableHighlightPainter;
/**
* Highlight painter that paints a squiggly underline underneath text, similar
* to what popular IDE's such as Visual Studio and Eclipse do to indicate
* errors, warnings, etc.<p>
* errors, warnings, etc.
* <p>
*
* This class must be used as a <code>LayerPainter</code>.
*
* @author Robert Futrell
* @version 1.0
*/
public class SquiggleUnderlineHighlightPainter
extends ChangeableHighlightPainter {
private static final int AMT = 2;
public class SquiggleUnderlineHighlightPainter extends ChangeableHighlightPainter {
private static final int AMT = 2;
/**
* Constructor.
*
* @param color The color of the squiggle. This cannot be
* <code>null</code>.
* @param color
* The color of the squiggle. This cannot be
* <code>null</code>.
*/
public SquiggleUnderlineHighlightPainter(Color color) {
super(color);
setPaint(color);
}
/**
* Paints a portion of a highlight.
*
* @param g the graphics context
* @param offs0 the starting model offset &gt;= 0
* @param offs1 the ending model offset &gt;= offs1
* @param bounds the bounding box of the view, which is not
* necessarily the region to paint.
* @param c the editor
* @param view View painting for
* @param g
* the graphics context
* @param offs0
* the starting model offset &gt;= 0
* @param offs1
* the ending model offset &gt;= offs1
* @param bounds
* the bounding box of the view, which is not
* necessarily the region to paint.
* @param c
* the editor
* @param view
* View painting for
* @return region drawing occurred in
*/
@Override
public Shape paintLayer(Graphics g, int offs0, int offs1,
Shape bounds, JTextComponent c, View view) {
g.setColor((Color)getPaint());
public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
g.setColor((Color) getPaint());
if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) {
// Contained in view, can just use bounds.
Rectangle alloc;
if (bounds instanceof Rectangle) {
alloc = (Rectangle)bounds;
}
else {
alloc = (Rectangle) bounds;
} else {
alloc = bounds.getBounds();
}
paintSquiggle(g, alloc);
return alloc;
}
// Otherwise, should only render part of View.
try {
// --- determine locations ---
Shape shape = view.modelToView(offs0, Position.Bias.Forward,
offs1,Position.Bias.Backward,
bounds);
Rectangle r = (shape instanceof Rectangle) ?
(Rectangle)shape : shape.getBounds();
Shape shape = view.modelToView(offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
Rectangle r = (shape instanceof Rectangle) ? (Rectangle) shape : shape.getBounds();
paintSquiggle(g, r);
return r;
} catch (BadLocationException e) {
e.printStackTrace(); // can't render
}
// Only if exception
return null;
}
/**
* Paints a squiggle underneath text in the specified rectangle.
*
* @param g The graphics context with which to paint.
* @param r The rectangle containing the text.
* @param g
* The graphics context with which to paint.
* @param r
* The rectangle containing the text.
*/
protected void paintSquiggle(Graphics g, Rectangle r) {
int x = r.x;
int y = r.y + r.height - AMT;
int delta = -AMT;
while (x<r.x+r.width) {
g.drawLine(x,y, x+AMT,y+delta);
while (x < r.x + r.width) {
g.drawLine(x, y, x + AMT, y + delta);
y += delta;
delta = -delta;
x += AMT;
}
}
}
}
+57 -65
View File
@@ -1,10 +1,8 @@
/*
* 08/06/2004
*
* Style.java - A set of traits for a particular token type to use while
* painting.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -15,17 +13,16 @@ import java.awt.FontMetrics;
import javax.swing.JPanel;
/**
* The color and style information for a token type. Each token type in an
* The color and style information for a token type. Each token type in an
* <code>RSyntaxTextArea</code> has a corresponding <code>Style</code>; this
* <code>Style</code> tells us the following things:
*
* <ul>
* <li>What foreground color to use for tokens of this type.</li>
* <li>What background color to use.</li>
* <li>The font to use.</li>
* <li>Whether the token should be underlined.</li>
* <li>What foreground color to use for tokens of this type.</li>
* <li>What background color to use.</li>
* <li>The font to use.</li>
* <li>Whether the token should be underlined.</li>
* </ul>
*
* @author Robert Futrell
@@ -33,19 +30,16 @@ import javax.swing.JPanel;
*/
@SuppressWarnings({ "checkstyle:visibilitymodifier" })
public class Style implements Cloneable {
public static final Color DEFAULT_FOREGROUND = Color.BLACK;
public static final Color DEFAULT_BACKGROUND = null;
public static final Font DEFAULT_FONT = null;
public static final Color DEFAULT_FOREGROUND = Color.BLACK;
public static final Color DEFAULT_BACKGROUND = null;
public static final Font DEFAULT_FONT = null;
public Color foreground;
public Color background;
public boolean underline;
public Font font;
public FontMetrics fontMetrics;
/**
* Creates a new style defaulting to black foreground, no
* background, and no styling.
@@ -53,68 +47,74 @@ public class Style implements Cloneable {
public Style() {
this(DEFAULT_FOREGROUND);
}
/**
* Creates a new style with the specified foreground and no styling.
*
* @param fg The foreground color to use.
* @param fg
* The foreground color to use.
*/
public Style(Color fg) {
this(fg, DEFAULT_BACKGROUND);
}
/**
* Creates a new style with the specified colors and no styling.
*
* @param fg The foreground color to use.
* @param bg The background color to use.
* @param fg
* The foreground color to use.
* @param bg
* The background color to use.
*/
public Style(Color fg, Color bg) {
this(fg, bg, DEFAULT_FONT);
}
/**
* Creates a new style.
*
* @param fg The foreground color to use.
* @param bg The background color to use.
* @param font The font for this syntax scheme.
* @param fg
* The foreground color to use.
* @param bg
* The background color to use.
* @param font
* The font for this syntax scheme.
*/
public Style(Color fg, Color bg, Font font) {
this(fg, bg, font, false);
}
/**
* Creates a new style.
*
* @param fg The foreground color to use.
* @param bg The background color to use.
* @param font The font for this syntax scheme.
* @param underline Whether or not to underline tokens with this style.
* @param fg
* The foreground color to use.
* @param bg
* The background color to use.
* @param font
* The font for this syntax scheme.
* @param underline
* Whether or not to underline tokens with this style.
*/
public Style(Color fg, Color bg, Font font, boolean underline) {
foreground = fg;
background = bg;
this.font = font;
this.underline = underline;
this.fontMetrics = font==null ? null :
new JPanel().getFontMetrics(font); // Default, no rendering hints!
this.fontMetrics = font == null ? null : new JPanel().getFontMetrics(font); // Default,
// no
// rendering
// hints!
}
/**
* Returns whether or not two (possibly <code>null</code>) objects are
* equal.
*/
private boolean areEqual(Object o1, Object o2) {
return (o1==null && o2==null) || (o1!=null && o1.equals(o2));
return (o1 == null && o2 == null) || (o1 != null && o1.equals(o2));
}
/**
* Returns a deep copy of this object.
*
@@ -124,7 +124,7 @@ public class Style implements Cloneable {
public Object clone() {
Style clone = null;
try {
clone = (Style)super.clone();
clone = (Style) super.clone();
} catch (CloneNotSupportedException cnse) { // Never happens
cnse.printStackTrace();
return null;
@@ -136,34 +136,31 @@ public class Style implements Cloneable {
clone.fontMetrics = fontMetrics;
return clone;
}
/**
* Returns whether or not two syntax schemes are equal.
*
* @param o2 The object with which to compare this syntax scheme.
* @param o2
* The object with which to compare this syntax scheme.
* @return Whether or not these two syntax schemes represent the same
* scheme.
*/
@Override
public boolean equals(Object o2) {
if (o2 instanceof Style) {
Style ss2 = (Style)o2;
if (this.underline==ss2.underline &&
areEqual(foreground, ss2.foreground) &&
areEqual(background, ss2.background) &&
areEqual(font, ss2.font) &&
areEqual(fontMetrics, ss2.fontMetrics)) {
Style ss2 = (Style) o2;
if (this.underline == ss2.underline && areEqual(foreground, ss2.foreground) && areEqual(background, ss2.background)
&& areEqual(font, ss2.font) && areEqual(fontMetrics, ss2.fontMetrics)) {
return true;
}
}
return false;
}
/**
* Computes the hash code to use when adding this syntax scheme to
* hash tables.<p>
* hash tables.
* <p>
*
* This method is implemented, since {@link #equals(Object)} is implemented,
* to keep FindBugs happy.
@@ -173,16 +170,15 @@ public class Style implements Cloneable {
@Override
public int hashCode() {
int hashCode = underline ? 1 : 0;
if (foreground!=null) {
if (foreground != null) {
hashCode ^= foreground.hashCode();
}
if (background!=null) {
if (background != null) {
hashCode ^= background.hashCode();
}
return hashCode;
}
/**
* Returns a string representation of this style.
*
@@ -190,10 +186,6 @@ public class Style implements Cloneable {
*/
@Override
public String toString() {
return "[Style: foreground: " + foreground +
", background: " + background + ", underline: " +
underline + ", font: " + font + "]";
return "[Style: foreground: " + foreground + ", background: " + background + ", underline: " + underline + ", font: " + font + "]";
}
}
}
@@ -1,25 +1,23 @@
/*
* 03/08/2004
*
* SyntaxConstants.java - Constants used by RSyntaxTextArea and friends.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* Constants that define the different programming languages understood by
* <code>RSyntaxTextArea</code>. These constants are the values you can pass
* <code>RSyntaxTextArea</code>. These constants are the values you can pass
* to {@link RSyntaxTextArea#setSyntaxEditingStyle(String)} to get syntax
* highlighting.<p>
* highlighting.
* <p>
*
* By default, all <code>RSyntaxTextArea</code>s can render all of these
* languages, but this can be changed (the list can be augmented or completely
* overwritten) on a per-text area basis. What languages can be rendered is
* overwritten) on a per-text area basis. What languages can be rendered is
* actually managed by the {@link TokenMakerFactory} installed on the text
* area's {@link RSyntaxDocument}. By default, all
* area's {@link RSyntaxDocument}. By default, all
* <code>RSyntaxDocumenet</code>s have a factory installed capable of handling
* all of these languages.
*
@@ -27,294 +25,198 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0
*/
public interface SyntaxConstants {
/**
* Style meaning don't syntax highlight anything.
*/
String SYNTAX_STYLE_NONE = "text/plain";
String SYNTAX_STYLE_NONE = "text/plain";
/**
* Style for highlighting ActionScript.
*/
String SYNTAX_STYLE_ACTIONSCRIPT = "text/actionscript";
String SYNTAX_STYLE_ACTIONSCRIPT = "text/actionscript";
/**
* Style for highlighting x86 assembler.
*/
String SYNTAX_STYLE_ASSEMBLER_X86 = "text/asm";
String SYNTAX_STYLE_ASSEMBLER_X86 = "text/asm";
/**
* Style for highlighting BBCode.
*/
String SYNTAX_STYLE_BBCODE = "text/bbcode";
String SYNTAX_STYLE_BBCODE = "text/bbcode";
/**
* Style for highlighting C.
*/
String SYNTAX_STYLE_C = "text/c";
String SYNTAX_STYLE_C = "text/c";
/**
* Style for highlighting Clojure.
*/
String SYNTAX_STYLE_CLOJURE = "text/clojure";
String SYNTAX_STYLE_CLOJURE = "text/clojure";
/**
* Style for highlighting C++.
*/
String SYNTAX_STYLE_CPLUSPLUS = "text/cpp";
String SYNTAX_STYLE_CPLUSPLUS = "text/cpp";
/**
* Style for highlighting C#.
*/
String SYNTAX_STYLE_CSHARP = "text/cs";
String SYNTAX_STYLE_CSHARP = "text/cs";
/**
* Style for highlighting CSS.
*/
String SYNTAX_STYLE_CSS = "text/css";
String SYNTAX_STYLE_CSS = "text/css";
/**
* Syntax style for highlighting D.
*/
String SYNTAX_STYLE_D = "text/d";
String SYNTAX_STYLE_D = "text/d";
/**
* Syntax style for highlighting Dockerfiles.
*/
String SYNTAX_STYLE_DOCKERFILE = "text/dockerfile";
String SYNTAX_STYLE_DOCKERFILE = "text/dockerfile";
/**
* Style for highlighting Dart.
*/
String SYNTAX_STYLE_DART = "text/dart";
String SYNTAX_STYLE_DART = "text/dart";
/**
* Style for highlighting Delphi/Pascal.
*/
String SYNTAX_STYLE_DELPHI = "text/delphi";
String SYNTAX_STYLE_DELPHI = "text/delphi";
/**
* Style for highlighting DTD files.
*/
String SYNTAX_STYLE_DTD = "text/dtd";
String SYNTAX_STYLE_DTD = "text/dtd";
/**
* Style for highlighting Fortran.
*/
String SYNTAX_STYLE_FORTRAN = "text/fortran";
String SYNTAX_STYLE_FORTRAN = "text/fortran";
/**
* Style for highlighting Groovy.
*/
String SYNTAX_STYLE_GROOVY = "text/groovy";
String SYNTAX_STYLE_GROOVY = "text/groovy";
/**
* Style for highlighting hosts files.
*/
String SYNTAX_STYLE_HOSTS = "text/hosts";
String SYNTAX_STYLE_HOSTS = "text/hosts";
/**
* Style for highlighting .htaccess files.
*/
String SYNTAX_STYLE_HTACCESS = "text/htaccess";
String SYNTAX_STYLE_HTACCESS = "text/htaccess";
/**
* Style for highlighting HTML.
*/
String SYNTAX_STYLE_HTML = "text/html";
String SYNTAX_STYLE_HTML = "text/html";
/**
* Style for highlighting INI files.
*/
String SYNTAX_STYLE_INI = "text/ini";
String SYNTAX_STYLE_INI = "text/ini";
/**
* Style for highlighting Java.
*/
String SYNTAX_STYLE_JAVA = "text/java";
String SYNTAX_STYLE_JAVA = "text/java";
/**
* Style for highlighting Java.
*/
String SYNTAX_STYLE_JAVA_DISASSEMBLE = "text/javaassembly";
String SYNTAX_STYLE_JAVA_DISASSEMBLE = "text/javaassembly";
/**
* Style for highlighting JavaScript.
*/
String SYNTAX_STYLE_JAVASCRIPT = "text/javascript";
String SYNTAX_STYLE_JAVASCRIPT = "text/javascript";
/**
* Style for highlighting JSON.
*/
String SYNTAX_STYLE_JSON = "text/json";
String SYNTAX_STYLE_JSON = "text/json";
/**
* Style for highlighting .jshintrc files (JSON with comments, so can be
* used for other times when you want this behavior).
*/
String SYNTAX_STYLE_JSON_WITH_COMMENTS = "text/jshintrc";
String SYNTAX_STYLE_JSON_WITH_COMMENTS = "text/jshintrc";
/**
* Style for highlighting JSP.
*/
String SYNTAX_STYLE_JSP = "text/jsp";
String SYNTAX_STYLE_JSP = "text/jsp";
/**
* Style for highlighting LaTeX.
*/
String SYNTAX_STYLE_LATEX = "text/latex";
String SYNTAX_STYLE_LATEX = "text/latex";
/**
* Style for highlighting Less.
*/
String SYNTAX_STYLE_LESS = "text/less";
String SYNTAX_STYLE_LESS = "text/less";
/**
* Style for highlighting Lisp.
*/
String SYNTAX_STYLE_LISP = "text/lisp";
String SYNTAX_STYLE_LISP = "text/lisp";
/**
* Style for highlighting Lua.
*/
String SYNTAX_STYLE_LUA = "text/lua";
String SYNTAX_STYLE_LUA = "text/lua";
/**
* Style for highlighting makefiles.
*/
String SYNTAX_STYLE_MAKEFILE = "text/makefile";
String SYNTAX_STYLE_MAKEFILE = "text/makefile";
/**
* Style for highlighting MXML.
*/
String SYNTAX_STYLE_MXML = "text/mxml";
String SYNTAX_STYLE_MXML = "text/mxml";
/**
* Style for highlighting NSIS install scripts.
*/
String SYNTAX_STYLE_NSIS = "text/nsis";
String SYNTAX_STYLE_NSIS = "text/nsis";
/**
* Style for highlighting Perl.
*/
String SYNTAX_STYLE_PERL = "text/perl";
String SYNTAX_STYLE_PERL = "text/perl";
/**
* Style for highlighting PHP.
*/
String SYNTAX_STYLE_PHP = "text/php";
String SYNTAX_STYLE_PHP = "text/php";
/**
* Style for highlighting properties files.
*/
String SYNTAX_STYLE_PROPERTIES_FILE = "text/properties";
String SYNTAX_STYLE_PROPERTIES_FILE = "text/properties";
/**
* Style for highlighting Python.
*/
String SYNTAX_STYLE_PYTHON = "text/python";
String SYNTAX_STYLE_PYTHON = "text/python";
/**
* Style for highlighting Ruby.
*/
String SYNTAX_STYLE_RUBY = "text/ruby";
String SYNTAX_STYLE_RUBY = "text/ruby";
/**
* Style for highlighting SAS keywords.
*/
String SYNTAX_STYLE_SAS = "text/sas";
String SYNTAX_STYLE_SAS = "text/sas";
/**
* Style for highlighting Scala.
*/
String SYNTAX_STYLE_SCALA = "text/scala";
String SYNTAX_STYLE_SCALA = "text/scala";
/**
* Style for highlighting SQL.
*/
String SYNTAX_STYLE_SQL = "text/sql";
String SYNTAX_STYLE_SQL = "text/sql";
/**
* Style for highlighting Tcl.
*/
String SYNTAX_STYLE_TCL = "text/tcl";
String SYNTAX_STYLE_TCL = "text/tcl";
/**
* Style for highlighting TypeScript.
*/
String SYNTAX_STYLE_TYPESCRIPT = "text/typescript";
String SYNTAX_STYLE_TYPESCRIPT = "text/typescript";
/**
* Style for highlighting UNIX shell keywords.
*/
String SYNTAX_STYLE_UNIX_SHELL = "text/unix";
String SYNTAX_STYLE_UNIX_SHELL = "text/unix";
/**
* Style for highlighting Visual Basic.
*/
String SYNTAX_STYLE_VISUAL_BASIC = "text/vb";
String SYNTAX_STYLE_VISUAL_BASIC = "text/vb";
/**
* Style for highlighting Windows batch files.
*/
String SYNTAX_STYLE_WINDOWS_BATCH = "text/bat";
String SYNTAX_STYLE_WINDOWS_BATCH = "text/bat";
/**
* Style for highlighting XML.
*/
String SYNTAX_STYLE_XML = "text/xml";
String SYNTAX_STYLE_XML = "text/xml";
/**
* Syntax style for highlighting YAML.
*/
String SYNTAX_STYLE_YAML = "text/yaml";
}
String SYNTAX_STYLE_YAML = "text/yaml";
}
+248 -309
View File
@@ -1,10 +1,8 @@
/*
* 02/26/2004
*
* SyntaxScheme.java - The set of colors and tokens used by an RSyntaxTextArea
* to color tokens.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -25,10 +23,10 @@ import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* The set of colors and styles used by an <code>RSyntaxTextArea</code> to
* color tokens.<p>
* color tokens.
* <p>
* You can use this class to programmatically set the fonts and colors used in
* an RSyntaxTextArea, but for more powerful, externalized control, consider
* using {@link Theme}s instead.
@@ -39,19 +37,18 @@ import org.xml.sax.helpers.XMLReaderFactory;
*/
@SuppressWarnings({ "checkstyle:magicnumber" })
public class SyntaxScheme implements Cloneable, TokenTypes {
private Style[] styles;
private static final String VERSION = "*ver1";
private static final String VERSION = "*ver1";
/**
* Creates a color scheme that either has all color values set to
* a default value or set to <code>null</code>.
*
* @param useDefaults If <code>true</code>, all color values will
* be set to default colors; if <code>false</code>, all colors
* will be initially <code>null</code>.
* @param useDefaults
* If <code>true</code>, all color values will
* be set to default colors; if <code>false</code>, all colors
* will be initially <code>null</code>.
*/
public SyntaxScheme(boolean useDefaults) {
styles = new Style[DEFAULT_NUM_TOKEN_TYPES];
@@ -59,62 +56,64 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
restoreDefaults(null);
}
}
/**
* Creates a default color scheme.
*
* @param baseFont The base font to use. Keywords will be a bold version
* of this font, and comments will be an italicized version of this
* font.
* @param baseFont
* The base font to use. Keywords will be a bold version
* of this font, and comments will be an italicized version of
* this
* font.
*/
public SyntaxScheme(Font baseFont) {
this(baseFont, true);
}
/**
* Creates a default color scheme.
*
* @param baseFont The base font to use. Keywords will be a bold version
* of this font, and comments will be an italicized version of this
* font.
* @param fontStyles Whether bold and italic should be used in the scheme
* (vs. all tokens using a plain font).
* @param baseFont
* The base font to use. Keywords will be a bold version
* of this font, and comments will be an italicized version of
* this
* font.
* @param fontStyles
* Whether bold and italic should be used in the scheme
* (vs. all tokens using a plain font).
*/
public SyntaxScheme(Font baseFont, boolean fontStyles) {
styles = new Style[DEFAULT_NUM_TOKEN_TYPES];
restoreDefaults(baseFont, fontStyles);
}
/**
* Changes the "base font" for this syntax scheme. This is called by
* Changes the "base font" for this syntax scheme. This is called by
* <code>RSyntaxTextArea</code> when its font changes via
* <code>setFont()</code>. This looks for tokens that use a derivative of
* <code>setFont()</code>. This looks for tokens that use a derivative of
* the text area's old font (but bolded and/or italicized) and make them
* use the new font with those stylings instead. This is desirable because
* use the new font with those stylings instead. This is desirable because
* most programmers prefer a single font to be used in their text editor,
* but might want bold (say for keywords) or italics.
*
* @param old The old font of the text area.
* @param font The new font of the text area.
* @param old
* The old font of the text area.
* @param font
* The new font of the text area.
*/
void changeBaseFont(Font old, Font font) {
for (int i=0; i<styles.length; i++) {
for (int i = 0; i < styles.length; i++) {
Style style = styles[i];
if (style!=null && style.font!=null) {
if (style.font.getFamily().equals(old.getFamily()) &&
style.font.getSize()==old.getSize()) {
if (style != null && style.font != null) {
if (style.font.getFamily().equals(old.getFamily()) && style.font.getSize() == old.getSize()) {
int s = style.font.getStyle(); // Keep bold or italic
StyleContext sc = StyleContext.getDefaultStyleContext();
style.font= sc.getFont(font.getFamily(), s, font.getSize());
style.font = sc.getFont(font.getFamily(), s, font.getSize());
}
}
}
}
/**
* Returns a deep copy of this color scheme.
*
@@ -124,74 +123,68 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
public Object clone() {
SyntaxScheme shcs = null;
try {
shcs = (SyntaxScheme)super.clone();
shcs = (SyntaxScheme) super.clone();
} catch (CloneNotSupportedException cnse) { // Never happens
cnse.printStackTrace();
return null;
}
shcs.styles = new Style[styles.length];
for (int i=0; i<styles.length; i++) {
for (int i = 0; i < styles.length; i++) {
Style s = styles[i];
if (s!=null) {
shcs.styles[i] = (Style)s.clone();
if (s != null) {
shcs.styles[i] = (Style) s.clone();
}
}
return shcs;
}
/**
* Tests whether this color scheme is the same as another color scheme.
*
* @param otherScheme The color scheme to compare to.
* @param otherScheme
* The color scheme to compare to.
* @return <code>true</code> if this color scheme and
* <code>otherScheme</code> are the same scheme;
* <code>false</code> otherwise.
*/
@Override
public boolean equals(Object otherScheme) {
// No need for null check; instanceof takes care of this for us,
// i.e. "if (!(null instanceof Foo))" evaluates to "true".
if (!(otherScheme instanceof SyntaxScheme)) {
return false;
}
Style[] otherSchemes = ((SyntaxScheme)otherScheme).styles;
Style[] otherSchemes = ((SyntaxScheme) otherScheme).styles;
int length = styles.length;
for (int i=0; i<length; i++) {
if (styles[i]==null) {
if (otherSchemes[i]!=null) {
for (int i = 0; i < length; i++) {
if (styles[i] == null) {
if (otherSchemes[i] != null) {
return false;
}
}
else if (!styles[i].equals(otherSchemes[i])) {
} else if (!styles[i].equals(otherSchemes[i])) {
return false;
}
}
return true;
}
/**
* Returns a hex string representing an RGB color, of the form
* <code>"$rrggbb"</code>.
*
* @param c The color.
* @param c
* The color.
* @return The string representation of the color.
*/
private static String getHexString(Color c) {
return "$" + Integer.toHexString((c.getRGB() & 0xffffff)+0x1000000).
substring(1);
return "$" + Integer.toHexString((c.getRGB() & 0xffffff) + 0x1000000).substring(1);
}
/**
* Returns the specified style.
*
* @param index The index of the style.
* @param index
* The index of the style.
* @return The style.
* @see #setStyle(int, Style)
* @see #getStyleCount()
@@ -199,8 +192,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
public Style getStyle(int index) {
return styles[index];
}
/**
* Returns the number of styles.
*
@@ -210,8 +202,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
public int getStyleCount() {
return styles.length;
}
/**
* Used by third party implementors e.g. SquirreL SQL. Most applications do
* not need to call this method.
@@ -226,8 +217,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
public Style[] getStyles() {
return styles;
}
/**
* This is implemented to be consistent with {@link #equals(Object)}.
* This is a requirement to keep FindBugs happy.
@@ -236,207 +226,190 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
*/
@Override
public int hashCode() {
// Keep me fast. Iterating over *all* syntax schemes contained is
// Keep me fast. Iterating over *all* syntax schemes contained is
// probably much slower than a "bad" hash code here.
int hashCode = 0;
int count = styles.length;
for (int i=0; i<count; i++) {
if (styles[i]!=null) {
for (int i = 0; i < count; i++) {
if (styles[i] != null) {
hashCode ^= styles[i].hashCode();
break;
}
}
return hashCode;
}
/**
* Loads a syntax scheme from an input stream.<p>
* Loads a syntax scheme from an input stream.
* <p>
*
* Consider using the {@link Theme} class for saving and loading RSTA
* styles rather than using this API.
*
* @param baseFont The font to use as the "base" for the syntax scheme.
* If this is <code>null</code>, a default monospaced font is used.
* @param in The stream to load from. It is up to the caller to close this
* stream when they are done.
* @param baseFont
* The font to use as the "base" for the syntax scheme.
* If this is <code>null</code>, a default monospaced font is
* used.
* @param in
* The stream to load from. It is up to the caller to close this
* stream when they are done.
* @return The syntax scheme.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/
public static SyntaxScheme load(Font baseFont, InputStream in)
throws IOException {
if (baseFont==null) {
public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException {
if (baseFont == null) {
baseFont = RSyntaxTextArea.getDefaultFont();
}
return SyntaxSchemeLoader.load(baseFont, in);
}
/**
* Loads a syntax highlighting color scheme from a string created from
* <code>toCommaSeparatedString</code>. This method is useful for saving
* and restoring color schemes.<p>
* <code>toCommaSeparatedString</code>. This method is useful for saving
* and restoring color schemes.
* <p>
*
* Consider using the {@link Theme} class for saving and loading RSTA
* styles rather than using this API.
*
* @param string A string generated from {@link #toCommaSeparatedString()}.
* @param string
* A string generated from {@link #toCommaSeparatedString()}.
* @return A color scheme.
* @see #toCommaSeparatedString()
*/
public static SyntaxScheme loadFromString(String string) {
return loadFromString(string, DEFAULT_NUM_TOKEN_TYPES);
}
/**
* Loads a syntax highlighting color scheme from a string created from
* <code>toCommaSeparatedString</code>. This method is useful for saving
* and restoring color schemes.<p>
* <code>toCommaSeparatedString</code>. This method is useful for saving
* and restoring color schemes.
* <p>
*
* Consider using the {@link Theme} class for saving and loading RSTA
* styles rather than using this API.
*
* @param string A string generated from {@link #toCommaSeparatedString()}.
* @param tokenTypeCount The number of token types saved in this string.
* This should be the number of token types saved by your custom
* SyntaxScheme subclass,
* or {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES} if you used the
* standard implementation (which most people will).
* @param string
* A string generated from {@link #toCommaSeparatedString()}.
* @param tokenTypeCount
* The number of token types saved in this string.
* This should be the number of token types saved by your custom
* SyntaxScheme subclass,
* or {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES} if you used the
* standard implementation (which most people will).
* @return A color scheme.
* @see #loadFromString(String)
* @see #toCommaSeparatedString()
*/
public static SyntaxScheme loadFromString(String string,
int tokenTypeCount) {
public static SyntaxScheme loadFromString(String string, int tokenTypeCount) {
SyntaxScheme scheme = new SyntaxScheme(true);
try {
if (string!=null) {
if (string != null) {
String[] tokens = string.split(",", -1);
// Check the version string, use defaults if incompatible
if (tokens.length==0 || !VERSION.equals(tokens[0])) {
if (tokens.length == 0 || !VERSION.equals(tokens[0])) {
return scheme; // Still set to defaults
}
int tokenCount = tokenTypeCount*7 + 1; // Version string
if (tokens.length!=tokenCount) {
throw new Exception(
"Not enough tokens in packed color scheme: expected " +
tokenCount + ", found " + tokens.length);
int tokenCount = tokenTypeCount * 7 + 1; // Version string
if (tokens.length != tokenCount) {
throw new Exception("Not enough tokens in packed color scheme: expected " + tokenCount + ", found " + tokens.length);
}
// Use StyleContext to create fonts to get composite fonts for
// Asian glyphs.
StyleContext sc = StyleContext.getDefaultStyleContext();
// Loop through each token style. Format:
// Loop through each token style. Format:
// "index,(fg|-),(bg|-),(t|f),((font,style,size)|(-,,))"
for (int i=0; i<tokenTypeCount; i++) {
int pos = i*7 + 1;
for (int i = 0; i < tokenTypeCount; i++) {
int pos = i * 7 + 1;
int integer = Integer.parseInt(tokens[pos]); // == i
if (integer!=i) {
throw new Exception("Expected " + i + ", found " +
integer);
if (integer != i) {
throw new Exception("Expected " + i + ", found " + integer);
}
Color fg = null; String temp = tokens[pos+1];
Color fg = null;
String temp = tokens[pos + 1];
if (!"-".equals(temp)) { // "-" => keep fg as null
fg = stringToColor(temp);
}
Color bg = null; temp = tokens[pos+2];
Color bg = null;
temp = tokens[pos + 2];
if (!"-".equals(temp)) { // "-" => keep bg as null
bg = stringToColor(temp);
}
// Check for "true" or "false" since we don't want to
// accidentally suck in an int representing the next
// packed color, and any string != "true" means false.
temp = tokens[pos+3];
temp = tokens[pos + 3];
if (!"t".equals(temp) && !"f".equals(temp)) {
throw new Exception("Expected 't' or 'f', found " + temp);
}
boolean underline = "t".equals(temp);
Font font = null;
String family = tokens[pos+4];
String family = tokens[pos + 4];
if (!"-".equals(family)) {
font = sc.getFont(family,
Integer.parseInt(tokens[pos+5]), // style
Integer.parseInt(tokens[pos+6])); // size
font = sc.getFont(family, Integer.parseInt(tokens[pos + 5]), // style
Integer.parseInt(tokens[pos + 6])); // size
}
scheme.styles[i] = new Style(fg, bg, font, underline);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return scheme;
}
void refreshFontMetrics(Graphics2D g2d) {
// It is assumed that any rendering hints are already applied to g2d.
for (int i=0; i<styles.length; i++) {
for (int i = 0; i < styles.length; i++) {
Style s = styles[i];
if (s!=null) {
s.fontMetrics = s.font==null ? null :
g2d.getFontMetrics(s.font);
if (s != null) {
s.fontMetrics = s.font == null ? null : g2d.getFontMetrics(s.font);
}
}
}
/**
* Restores all colors and fonts to their default values.
*
* @param baseFont The base font to use when creating this scheme. If
* this is <code>null</code>, then a default monospaced font is
* used.
* @param baseFont
* The base font to use when creating this scheme. If
* this is <code>null</code>, then a default monospaced font is
* used.
*/
public void restoreDefaults(Font baseFont) {
restoreDefaults(baseFont, true);
}
/**
* Restores all colors and fonts to their default values.
*
* @param baseFont The base font to use when creating this scheme. If
* this is <code>null</code>, then a default monospaced font is
* used.
* @param fontStyles Whether bold and italic should be used in the scheme
* (vs. all tokens using a plain font).
* @param baseFont
* The base font to use when creating this scheme. If
* this is <code>null</code>, then a default monospaced font is
* used.
* @param fontStyles
* Whether bold and italic should be used in the scheme
* (vs. all tokens using a plain font).
*/
public void restoreDefaults(Font baseFont, boolean fontStyles) {
// Colors used by tokens.
Color comment = new Color(0,128,0);
Color docComment = new Color(164,0,0);
Color markupComment = new Color(0, 96, 0);
Color keyword = Color.BLUE;
Color dataType = new Color(0,128,128);
Color function = new Color(173,128,0);
Color preprocessor = new Color(128,128,128);
Color operator = new Color(128, 64, 64);
Color regex = new Color(0,128,164);
Color variable = new Color(255,153,0);
Color literalNumber = new Color(100,0,200);
Color literalString = new Color(220,0,156);
Color error = new Color(148,148,0);
Color comment = new Color(0, 128, 0);
Color docComment = new Color(164, 0, 0);
Color markupComment = new Color(0, 96, 0);
Color keyword = Color.BLUE;
Color dataType = new Color(0, 128, 128);
Color function = new Color(173, 128, 0);
Color preprocessor = new Color(128, 128, 128);
Color operator = new Color(128, 64, 64);
Color regex = new Color(0, 128, 164);
Color variable = new Color(255, 153, 0);
Color literalNumber = new Color(100, 0, 200);
Color literalString = new Color(220, 0, 156);
Color error = new Color(148, 148, 0);
// (Possible) special font styles for keywords and comments.
if (baseFont==null) {
if (baseFont == null) {
baseFont = RSyntaxTextArea.getDefaultFont();
}
Font commentFont = baseFont;
@@ -445,194 +418,175 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
// WORKAROUND for Sun JRE bug 6282887 (Asian font bug in 1.4/1.5)
// That bug seems to be hidden now, see 6289072 instead.
StyleContext sc = StyleContext.getDefaultStyleContext();
Font boldFont = sc.getFont(baseFont.getFamily(), Font.BOLD,
baseFont.getSize());
Font italicFont = sc.getFont(baseFont.getFamily(), Font.ITALIC,
baseFont.getSize());
commentFont = italicFont;//baseFont.deriveFont(Font.ITALIC);
keywordFont = boldFont;//baseFont.deriveFont(Font.BOLD);
Font boldFont = sc.getFont(baseFont.getFamily(), Font.BOLD, baseFont.getSize());
Font italicFont = sc.getFont(baseFont.getFamily(), Font.ITALIC, baseFont.getSize());
commentFont = italicFont;// baseFont.deriveFont(Font.ITALIC);
keywordFont = boldFont;// baseFont.deriveFont(Font.BOLD);
}
styles[COMMENT_EOL] = new Style(comment, null, commentFont);
styles[COMMENT_MULTILINE] = new Style(comment, null, commentFont);
styles[COMMENT_DOCUMENTATION] = new Style(docComment, null, commentFont);
styles[COMMENT_KEYWORD] = new Style(new Color(255,152,0), null, commentFont);
styles[COMMENT_MARKUP] = new Style(Color.gray, null, commentFont);
styles[RESERVED_WORD] = new Style(keyword, null, keywordFont);
styles[RESERVED_WORD_2] = new Style(keyword, null, keywordFont);
styles[FUNCTION] = new Style(function);
styles[LITERAL_BOOLEAN] = new Style(literalNumber);
styles[LITERAL_NUMBER_DECIMAL_INT] = new Style(literalNumber);
styles[LITERAL_NUMBER_FLOAT] = new Style(literalNumber);
styles[LITERAL_NUMBER_HEXADECIMAL] = new Style(literalNumber);
styles[LITERAL_STRING_DOUBLE_QUOTE] = new Style(literalString);
styles[LITERAL_CHAR] = new Style(literalString);
styles[LITERAL_BACKQUOTE] = new Style(literalString);
styles[DATA_TYPE] = new Style(dataType, null, keywordFont);
styles[VARIABLE] = new Style(variable);
styles[REGEX] = new Style(regex);
styles[ANNOTATION] = new Style(Color.gray);
styles[IDENTIFIER] = new Style(null);
styles[WHITESPACE] = new Style(Color.gray);
styles[SEPARATOR] = new Style(Color.RED);
styles[OPERATOR] = new Style(operator);
styles[PREPROCESSOR] = new Style(preprocessor);
styles[MARKUP_TAG_DELIMITER] = new Style(Color.RED);
styles[MARKUP_TAG_NAME] = new Style(Color.BLUE);
styles[MARKUP_TAG_ATTRIBUTE] = new Style(new Color(63,127,127));
styles[MARKUP_TAG_ATTRIBUTE_VALUE]= new Style(literalString);
styles[MARKUP_COMMENT] = new Style(markupComment, null, commentFont);
styles[MARKUP_DTD] = new Style(function);
styles[COMMENT_EOL] = new Style(comment, null, commentFont);
styles[COMMENT_MULTILINE] = new Style(comment, null, commentFont);
styles[COMMENT_DOCUMENTATION] = new Style(docComment, null, commentFont);
styles[COMMENT_KEYWORD] = new Style(new Color(255, 152, 0), null, commentFont);
styles[COMMENT_MARKUP] = new Style(Color.gray, null, commentFont);
styles[RESERVED_WORD] = new Style(keyword, null, keywordFont);
styles[RESERVED_WORD_2] = new Style(keyword, null, keywordFont);
styles[FUNCTION] = new Style(function);
styles[LITERAL_BOOLEAN] = new Style(literalNumber);
styles[LITERAL_NUMBER_DECIMAL_INT] = new Style(literalNumber);
styles[LITERAL_NUMBER_FLOAT] = new Style(literalNumber);
styles[LITERAL_NUMBER_HEXADECIMAL] = new Style(literalNumber);
styles[LITERAL_STRING_DOUBLE_QUOTE] = new Style(literalString);
styles[LITERAL_CHAR] = new Style(literalString);
styles[LITERAL_BACKQUOTE] = new Style(literalString);
styles[DATA_TYPE] = new Style(dataType, null, keywordFont);
styles[VARIABLE] = new Style(variable);
styles[REGEX] = new Style(regex);
styles[ANNOTATION] = new Style(Color.gray);
styles[IDENTIFIER] = new Style(null);
styles[WHITESPACE] = new Style(Color.gray);
styles[SEPARATOR] = new Style(Color.RED);
styles[OPERATOR] = new Style(operator);
styles[PREPROCESSOR] = new Style(preprocessor);
styles[MARKUP_TAG_DELIMITER] = new Style(Color.RED);
styles[MARKUP_TAG_NAME] = new Style(Color.BLUE);
styles[MARKUP_TAG_ATTRIBUTE] = new Style(new Color(63, 127, 127));
styles[MARKUP_TAG_ATTRIBUTE_VALUE] = new Style(literalString);
styles[MARKUP_COMMENT] = new Style(markupComment, null, commentFont);
styles[MARKUP_DTD] = new Style(function);
styles[MARKUP_PROCESSING_INSTRUCTION] = new Style(preprocessor);
styles[MARKUP_CDATA] = new Style(new Color(0xcc6600));
styles[MARKUP_CDATA_DELIMITER] = new Style(new Color(0x008080));
styles[MARKUP_ENTITY_REFERENCE] = new Style(dataType);
styles[ERROR_IDENTIFIER] = new Style(error);
styles[ERROR_NUMBER_FORMAT] = new Style(error);
styles[ERROR_STRING_DOUBLE] = new Style(error);
styles[ERROR_CHAR] = new Style(error);
styles[MARKUP_CDATA] = new Style(new Color(0xcc6600));
styles[MARKUP_CDATA_DELIMITER] = new Style(new Color(0x008080));
styles[MARKUP_ENTITY_REFERENCE] = new Style(dataType);
styles[ERROR_IDENTIFIER] = new Style(error);
styles[ERROR_NUMBER_FORMAT] = new Style(error);
styles[ERROR_STRING_DOUBLE] = new Style(error);
styles[ERROR_CHAR] = new Style(error);
// Issue #34: If an application modifies TokenTypes to add new built-in
// token types, we'll get NPEs if not all styles are initialized.
for (int i=0; i<styles.length; i++) {
if (styles[i]==null) {
for (int i = 0; i < styles.length; i++) {
if (styles[i] == null) {
styles[i] = new Style();
}
}
}
/**
* Sets a style to use when rendering a token type.
*
* @param type The token type.
* @param style The style for the token type.
* @param type
* The token type.
* @param style
* The style for the token type.
* @see #getStyle(int)
*/
public void setStyle(int type, Style style) {
styles[type] = style;
}
/**
* Used by third party implementors e.g. SquirreL SQL. Most applications do
* not need to call this method; individual styles can be set via
* {@link #setStyle(int, Style)}.
*
* @param styles The new array of styles to use. Note that this should
* have length of at least
* {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES}.
* @param styles
* The new array of styles to use. Note that this should
* have length of at least
* {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES}.
* @see #setStyle(int, Style)
* @see #getStyles()
*/
public void setStyles(Style[] styles) {
this.styles = styles;
}
/**
* Returns the color represented by a string. If the first char in the
* Returns the color represented by a string. If the first char in the
* string is '<code>$</code>', it is assumed to be in hex, otherwise it is
* assumed to be decimal. So, for example, both of these:
* assumed to be decimal. So, for example, both of these:
*
* <pre>
* "$00ff00"
* "65280"
* </pre>
*
* will return <code>new Color(0, 255, 0)</code>.
*
* @param s The string to evaluate.
* @param s
* The string to evaluate.
* @return The color.
*/
private static Color stringToColor(String s) {
// Check for decimal as well as hex, for backward
// compatibility (fix from GwynEvans on forums)
char ch = s.charAt(0);
return new Color((ch=='$' || ch=='#') ?
Integer.parseInt(s.substring(1),16) :
Integer.parseInt(s));
return new Color((ch == '$' || ch == '#') ? Integer.parseInt(s.substring(1), 16) : Integer.parseInt(s));
}
/**
* Returns this syntax highlighting scheme as a comma-separated list of
* values as follows:
* <ul>
* <li>If a color is non-null, it is added as a 24-bit integer
* of the form <code>((r&lt;*lt;16) | (g&lt;*lt;8) | (b))</code>; if
* it is <code>null</code>, it is added as "<i>-,</i>".
* <li>The font and style (bold/italic) is added as an integer like so:
* "<i>family,</i> <i>style,</i> <i>size</i>".
* <li>The entire syntax highlighting scheme is thus one long string of
* color schemes of the format "<i>i,[fg],[bg],uline,[style]</i>,
* where:
* <ul>
* <li><code>i</code> is the index of the syntax scheme.
* <li><i>fg</i> and <i>bg</i> are the foreground and background
* colors for the scheme, and may be null (represented by
* <code>-</code>).
* <li><code>uline</code> is whether or not the font should be
* underlined, and is either <code>t</code> or <code>f</code>.
* <li><code>style</code> is the <code>family,style,size</code>
* triplet described above.
* </ul>
* <li>If a color is non-null, it is added as a 24-bit integer
* of the form <code>((r&lt;*lt;16) | (g&lt;*lt;8) | (b))</code>; if
* it is <code>null</code>, it is added as "<i>-,</i>".
* <li>The font and style (bold/italic) is added as an integer like so:
* "<i>family,</i> <i>style,</i> <i>size</i>".
* <li>The entire syntax highlighting scheme is thus one long string of
* color schemes of the format "<i>i,[fg],[bg],uline,[style]</i>,
* where:
* <ul>
* <li><code>i</code> is the index of the syntax scheme.
* <li><i>fg</i> and <i>bg</i> are the foreground and background
* colors for the scheme, and may be null (represented by
* <code>-</code>).
* <li><code>uline</code> is whether or not the font should be
* underlined, and is either <code>t</code> or <code>f</code>.
* <li><code>style</code> is the <code>family,style,size</code>
* triplet described above.
* </ul>
* </ul>
*
* @return A string representing the rgb values of the colors.
* @see #loadFromString(String)
*/
public String toCommaSeparatedString() {
StringBuilder sb = new StringBuilder(VERSION);
sb.append(',');
for (int i=0; i<styles.length; i++) {
for (int i = 0; i < styles.length; i++) {
sb.append(i).append(',');
Style ss = styles[i];
if (ss==null) { // Only true for i==0 (NULL token)
if (ss == null) { // Only true for i==0 (NULL token)
sb.append("-,-,f,-,,,");
continue;
}
Color c = ss.foreground;
sb.append(c!=null ? (getHexString(c) + ",") : "-,");
sb.append(c != null ? (getHexString(c) + ",") : "-,");
c = ss.background;
sb.append(c!=null ? (getHexString(c) + ",") : "-,");
sb.append(c != null ? (getHexString(c) + ",") : "-,");
sb.append(ss.underline ? "t," : "f,");
Font font = ss.font;
if (font!=null) {
sb.append(font.getFamily()).append(',').
append(font.getStyle()).append(',').
append(font.getSize()).append(',');
}
else {
if (font != null) {
sb.append(font.getFamily()).append(',').append(font.getStyle()).append(',').append(font.getSize()).append(',');
} else {
sb.append("-,,,");
}
}
return sb.substring(0,sb.length()-1); // Take off final ','.
return sb.substring(0, sb.length() - 1); // Take off final ','.
}
/**
* Loads a <code>SyntaxScheme</code> from an XML file.
*/
private static class SyntaxSchemeLoader extends DefaultHandler {
private Font baseFont;
private SyntaxScheme scheme;
SyntaxSchemeLoader(Font baseFont) {
scheme = new SyntaxScheme(baseFont);
}
public static SyntaxScheme load(Font baseFont, InputStream in)
throws IOException {
public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException {
SyntaxSchemeLoader parser = null;
try {
XMLReader reader = XMLReaderFactory.createXMLReader();
@@ -647,13 +601,10 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
}
return parser.scheme;
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attrs) {
public void startElement(String uri, String localName, String qName, Attributes attrs) {
if ("style".equals(qName)) {
String type = attrs.getValue("token");
Field field = null;
try {
@@ -664,9 +615,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
System.err.println("Invalid token type: " + type);
return;
}
if (field.getType()==int.class) {
if (field.getType() == int.class) {
int index = 0;
try {
index = field.getInt(scheme);
@@ -677,29 +626,26 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
e.printStackTrace();
return;
}
String fgStr = attrs.getValue("fg");
if (fgStr!=null) {
if (fgStr != null) {
Color fg = stringToColor(fgStr);
scheme.styles[index].foreground = fg;
}
String bgStr = attrs.getValue("bg");
if (bgStr!=null) {
if (bgStr != null) {
Color bg = stringToColor(bgStr);
scheme.styles[index].background = bg;
}
boolean styleSpecified = false;
boolean bold = false;
boolean italic = false;
String boldStr = attrs.getValue("bold");
if (boldStr!=null) {
if (boldStr != null) {
bold = Boolean.parseBoolean(boldStr);
styleSpecified = true;
}
String italicStr = attrs.getValue("italic");
if (italicStr!=null) {
if (italicStr != null) {
italic = Boolean.parseBoolean(italicStr);
styleSpecified = true;
}
@@ -713,20 +659,13 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
}
scheme.styles[index].font = baseFont.deriveFont(style);
}
String ulineStr = attrs.getValue("underline");
if (ulineStr!=null) {
boolean uline= Boolean.parseBoolean(ulineStr);
if (ulineStr != null) {
boolean uline = Boolean.parseBoolean(ulineStr);
scheme.styles[index].underline = uline;
}
}
}
}
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,10 +1,8 @@
/*
* 11/25/2008
*
* TextEditorPane.java - A syntax highlighting text area that has knowledge of
* the file it is editing on disk.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -34,33 +32,33 @@ import org.fife.ui.rtextarea.RTextAreaEditorKit;
* that adds information about the file being edited, such as:
*
* <ul>
* <li>Its name and location.
* <li>Is it dirty?
* <li>Is it read-only?
* <li>The last time it was loaded or saved to disk (local files only).
* <li>The file's encoding on disk.
* <li>Easy access to the line separator.
* <li>Its name and location.
* <li>Is it dirty?
* <li>Is it read-only?
* <li>The last time it was loaded or saved to disk (local files only).
* <li>The file's encoding on disk.
* <li>Easy access to the line separator.
* </ul>
*
* Loading and saving is also built into the editor.<p>
* Loading and saving is also built into the editor.
* <p>
*
* When saving UTF-8 files, whether or not a BOM is written is controlled by
* the {@link UnicodeWriter} class.
* Use {@link UnicodeWriter#setWriteUtf8BOM(boolean)} to toggle writing BOMs
* for UTF-8 files.<p>
* for UTF-8 files.
* <p>
*
* Both local and remote files (e.g. ftp) are supported. See the
* Both local and remote files (e.g. ftp) are supported. See the
* {@link FileLocation} class for more information.
*
* @author Robert Futrell
* @version 1.0
* @see FileLocation
*/
public class TextEditorPane extends RSyntaxTextArea implements
DocumentListener {
public class TextEditorPane extends RSyntaxTextArea implements DocumentListener {
private static final long serialVersionUID = 1L;
/**
* Property change event fired when the file path this text area references
* is updated.
@@ -68,94 +66,84 @@ public class TextEditorPane extends RSyntaxTextArea implements
* @see #load(FileLocation, String)
* @see #saveAs(FileLocation)
*/
public static final String FULL_PATH_PROPERTY = "TextEditorPane.fileFullPath";
public static final String FULL_PATH_PROPERTY = "TextEditorPane.fileFullPath";
/**
* Property change event fired when the text area's dirty flag changes.
*
* @see #setDirty(boolean)
*/
public static final String DIRTY_PROPERTY = "TextEditorPane.dirty";
public static final String DIRTY_PROPERTY = "TextEditorPane.dirty";
/**
* Property change event fired when the text area should be treated as
* read-only, and previously it should not, or vice-versa.
*
* @see #setReadOnly(boolean)
*/
public static final String READ_ONLY_PROPERTY = "TextEditorPane.readOnly";
public static final String READ_ONLY_PROPERTY = "TextEditorPane.readOnly";
/**
* Property change event fired when the text area's encoding changes.
*
* @see #setEncoding(String)
*/
public static final String ENCODING_PROPERTY = "TextEditorPane.encoding";
/**
* The location of the file being edited.
*/
private FileLocation loc;
/**
* The charset to use when reading or writing this file.
*/
private String charSet;
/**
* Whether the file should be treated as read-only.
*/
private boolean readOnly;
/**
* Whether the file is dirty.
*/
private boolean dirty;
/**
* The last time this file was modified on disk, for local files.
* For remote files, this value should always be
* {@link #LAST_MODIFIED_UNKNOWN}.
*/
private long lastSaveOrLoadTime;
/**
* The value returned by {@link #getLastSaveOrLoadTime()} for remote files.
*/
public static final long LAST_MODIFIED_UNKNOWN = 0;
public static final long LAST_MODIFIED_UNKNOWN = 0;
/**
* The default name given to files if none is specified in a constructor.
*/
private static final String DEFAULT_FILE_NAME = "Untitled.txt";
/**
* Constructor. The file will be given a default name.
* Constructor. The file will be given a default name.
*/
public TextEditorPane() {
this(INSERT_MODE);
}
/**
* Constructor. The file will be given a default name.
* Constructor. The file will be given a default name.
*
* @param textMode Either <code>INSERT_MODE</code> or
* <code>OVERWRITE_MODE</code>.
* @param textMode
* Either <code>INSERT_MODE</code> or
* <code>OVERWRITE_MODE</code>.
*/
public TextEditorPane(int textMode) {
this(textMode, false);
}
/**
* Creates a new <code>TextEditorPane</code>. The file will be given
* Creates a new <code>TextEditorPane</code>. The file will be given
* a default name.
*
* @param textMode Either <code>INSERT_MODE</code> or
* <code>OVERWRITE_MODE</code>.
* @param wordWrapEnabled Whether or not to use word wrap in this pane.
* @param textMode
* Either <code>INSERT_MODE</code> or
* <code>OVERWRITE_MODE</code>.
* @param wordWrapEnabled
* Whether or not to use word wrap in this pane.
*/
public TextEditorPane(int textMode, boolean wordWrapEnabled) {
super(textMode);
@@ -166,88 +154,92 @@ public class TextEditorPane extends RSyntaxTextArea implements
ioe.printStackTrace();
}
}
/**
* Creates a new <code>TextEditorPane</code>.
*
* @param textMode Either <code>INSERT_MODE</code> or
* <code>OVERWRITE_MODE</code>.
* @param wordWrapEnabled Whether or not to use word wrap in this pane.
* @param loc The location of the text file being edited. If this value
* is <code>null</code>, a file named "Untitled.txt" in the current
* directory is used.
* @throws IOException If an IO error occurs reading the file at
* <code>loc</code>. This of course won't happen if
* <code>loc</code> is <code>null</code>.
* @param textMode
* Either <code>INSERT_MODE</code> or
* <code>OVERWRITE_MODE</code>.
* @param wordWrapEnabled
* Whether or not to use word wrap in this pane.
* @param loc
* The location of the text file being edited. If this value
* is <code>null</code>, a file named "Untitled.txt" in the
* current
* directory is used.
* @throws IOException
* If an IO error occurs reading the file at
* <code>loc</code>. This of course won't happen if
* <code>loc</code> is <code>null</code>.
*/
public TextEditorPane(int textMode, boolean wordWrapEnabled,
FileLocation loc) throws IOException {
public TextEditorPane(int textMode, boolean wordWrapEnabled, FileLocation loc) throws IOException {
this(textMode, wordWrapEnabled, loc, null);
}
/**
* Creates a new <code>TextEditorPane</code>.
*
* @param textMode Either <code>INSERT_MODE</code> or
* <code>OVERWRITE_MODE</code>.
* @param wordWrapEnabled Whether or not to use word wrap in this pane.
* @param loc The location of the text file being edited. If this value
* is <code>null</code>, a file named "Untitled.txt" in the current
* directory is used. This file is displayed as empty even if it
* actually exists.
* @param defaultEnc The default encoding to use when opening the file,
* if the file is not Unicode. If this value is <code>null</code>,
* a system default value is used.
* @throws IOException If an IO error occurs reading the file at
* <code>loc</code>. This of course won't happen if
* <code>loc</code> is <code>null</code>.
* @param textMode
* Either <code>INSERT_MODE</code> or
* <code>OVERWRITE_MODE</code>.
* @param wordWrapEnabled
* Whether or not to use word wrap in this pane.
* @param loc
* The location of the text file being edited. If this value
* is <code>null</code>, a file named "Untitled.txt" in the
* current
* directory is used. This file is displayed as empty even if it
* actually exists.
* @param defaultEnc
* The default encoding to use when opening the file,
* if the file is not Unicode. If this value is
* <code>null</code>,
* a system default value is used.
* @throws IOException
* If an IO error occurs reading the file at
* <code>loc</code>. This of course won't happen if
* <code>loc</code> is <code>null</code>.
*/
public TextEditorPane(int textMode, boolean wordWrapEnabled,
FileLocation loc, String defaultEnc) throws IOException {
public TextEditorPane(int textMode, boolean wordWrapEnabled, FileLocation loc, String defaultEnc) throws IOException {
super(textMode);
setLineWrap(wordWrapEnabled);
init(loc, defaultEnc);
}
/**
* Callback for when styles in the current document change.
* This method is never called.
*
* @param e The document event.
* @param e
* The document event.
*/
@Override
public void changedUpdate(DocumentEvent e) {
}
public void changedUpdate(DocumentEvent e) {}
/**
* Returns the default encoding for this operating system.
*
* @return The default encoding.
*/
private static String getDefaultEncoding() {
// NOTE: The "file.encoding" system property is not guaranteed to be
// NOTE: The "file.encoding" system property is not guaranteed to be
// set by the spec, so we cannot rely on it.
String encoding = Charset.defaultCharset().name();
if (encoding==null) {
if (encoding == null) {
try {
File f = File.createTempFile("rsta", null);
FileWriter w = new FileWriter(f);
encoding = w.getEncoding();
w.close();
f.deleteOnExit();//delete(); Keep FindBugs happy
f.deleteOnExit();// delete(); Keep FindBugs happy
} catch (IOException ioe) {
encoding = "US-ASCII";
}
}
return encoding;
}
/**
* Returns the encoding to use when reading or writing this file.
*
@@ -257,33 +249,31 @@ public class TextEditorPane extends RSyntaxTextArea implements
public String getEncoding() {
return charSet;
}
/**
* Returns the full path to this document.
*
* @return The full path to the document.
*/
public String getFileFullPath() {
return loc==null ? null : loc.getFileFullPath();
return loc == null ? null : loc.getFileFullPath();
}
/**
* Returns the file name of this document.
*
* @return The file name.
*/
public String getFileName() {
return loc==null ? null : loc.getFileName();
return loc == null ? null : loc.getFileName();
}
/**
* Returns the timestamp for when this file was last loaded or saved
* <em>by this editor pane</em>. If the file has been modified on disk by
* <em>by this editor pane</em>. If the file has been modified on disk by
* another process after it was loaded into this editor pane, this method
* will not return the actual file's last modified time.<p>
* will not return the actual file's last modified time.
* <p>
*
* For remote files, this method will always return
* {@link #LAST_MODIFIED_UNKNOWN}.
@@ -296,77 +286,73 @@ public class TextEditorPane extends RSyntaxTextArea implements
public long getLastSaveOrLoadTime() {
return lastSaveOrLoadTime;
}
/**
* Returns the line separator used when writing this file (e.g.
* "<code>\n</code>", "<code>\r\n</code>", or "<code>\r</code>").<p>
* "<code>\n</code>", "<code>\r\n</code>", or "<code>\r</code>").
* <p>
*
* Note that this value is an <code>Object</code> and not a
* <code>String</code> as that is the way the {@link Document} interface
* defines its property values. If you always use
* defines its property values. If you always use
* {@link #setLineSeparator(String)} to modify this value, then the value
* returned from this method will always be a <code>String</code>.
*
* @return The line separator. If this value is <code>null</code>, then
* @return The line separator. If this value is <code>null</code>, then
* the system default line separator is used (usually the value
* of <code>System.getProperty("line.separator")</code>).
* @see #setLineSeparator(String)
* @see #setLineSeparator(String, boolean)
*/
public Object getLineSeparator() {
return getDocument().getProperty(
RTextAreaEditorKit.EndOfLineStringProperty);
return getDocument().getProperty(RTextAreaEditorKit.EndOfLineStringProperty);
}
/**
* Initializes this editor with the specified file location.
*
* @param loc The file location. If this is <code>null</code>, a default
* location is used and an empty file is displayed.
* @param defaultEnc The default encoding to use when opening the file,
* if the file is not Unicode. If this value is <code>null</code>,
* a system default value is used.
* @throws IOException If an IO error occurs reading from <code>loc</code>.
* If <code>loc</code> is <code>null</code>, this cannot happen.
* @param loc
* The file location. If this is <code>null</code>, a default
* location is used and an empty file is displayed.
* @param defaultEnc
* The default encoding to use when opening the file,
* if the file is not Unicode. If this value is
* <code>null</code>,
* a system default value is used.
* @throws IOException
* If an IO error occurs reading from <code>loc</code>.
* If <code>loc</code> is <code>null</code>, this cannot happen.
*/
private void init(FileLocation loc, String defaultEnc) throws IOException {
if (loc==null) {
if (loc == null) {
// Don't call load() just in case Untitled.txt actually exists,
// just to ensure there is no chance of an IOException being thrown
// in the default case.
this.loc = FileLocation.create(DEFAULT_FILE_NAME);
charSet = defaultEnc==null ? getDefaultEncoding() : defaultEnc;
charSet = defaultEnc == null ? getDefaultEncoding() : defaultEnc;
// Ensure that line separator always has a value, even if the file
// does not exist (or is the "default" file). This makes life
// does not exist (or is the "default" file). This makes life
// easier for host applications that want to display this value.
setLineSeparator(System.getProperty("line.separator"));
}
else {
} else {
load(loc, defaultEnc); // Sets this.loc
}
if (this.loc.isLocalAndExists()) {
File file = new File(this.loc.getFileFullPath());
lastSaveOrLoadTime = file.lastModified();
setReadOnly(!file.canWrite());
}
else {
} else {
lastSaveOrLoadTime = LAST_MODIFIED_UNKNOWN;
setReadOnly(false);
}
setDirty(false);
}
/**
* Callback for when text is inserted into the document.
*
* @param e Information on the insertion.
* @param e
* Information on the insertion.
*/
@Override
public void insertUpdate(DocumentEvent e) {
@@ -374,8 +360,7 @@ public class TextEditorPane extends RSyntaxTextArea implements
setDirty(true);
}
}
/**
* Returns whether or not the text in this editor has unsaved changes.
*
@@ -385,8 +370,7 @@ public class TextEditorPane extends RSyntaxTextArea implements
public boolean isDirty() {
return dirty;
}
/**
* Returns whether this file is a local file.
*
@@ -395,8 +379,7 @@ public class TextEditorPane extends RSyntaxTextArea implements
public boolean isLocal() {
return loc.isLocal();
}
/**
* Returns whether this is a local file that already exists.
*
@@ -405,12 +388,12 @@ public class TextEditorPane extends RSyntaxTextArea implements
public boolean isLocalAndExists() {
return loc.isLocalAndExists();
}
/**
* Returns whether the text file has been modified outside of this editor
* since the last load or save operation. Note that if this is a remote
* file, this method will always return <code>false</code>.<p>
* since the last load or save operation. Note that if this is a remote
* file, this method will always return <code>false</code>.
* <p>
*
* This method may be used by applications to implement a reloading
* feature, where the user is prompted to reload a file if it has been
@@ -421,10 +404,9 @@ public class TextEditorPane extends RSyntaxTextArea implements
* @see #getLastSaveOrLoadTime()
*/
public boolean isModifiedOutsideEditor() {
return loc.getActualLastModified()>getLastSaveOrLoadTime();
return loc.getActualLastModified() > getLastSaveOrLoadTime();
}
/**
* Returns whether or not the text area should be treated as read-only.
*
@@ -434,39 +416,39 @@ public class TextEditorPane extends RSyntaxTextArea implements
public boolean isReadOnly() {
return readOnly;
}
/**
* Loads the specified file in this editor. This method fires a property
* Loads the specified file in this editor. This method fires a property
* change event of type {@link #FULL_PATH_PROPERTY}.
*
* @param loc The location of the file to load. This cannot be
* <code>null</code>.
* @param defaultEnc The encoding to use when loading/saving the file.
* This encoding will only be used if the file is not Unicode.
* If this value is <code>null</code>, the system default encoding
* is used.
* @throws IOException If an IO error occurs.
* @param loc
* The location of the file to load. This cannot be
* <code>null</code>.
* @param defaultEnc
* The encoding to use when loading/saving the file.
* This encoding will only be used if the file is not Unicode.
* If this value is <code>null</code>, the system default
* encoding
* is used.
* @throws IOException
* If an IO error occurs.
* @see #save()
* @see #saveAs(FileLocation)
*/
public void load(FileLocation loc, String defaultEnc) throws IOException {
// For new local files, just go with it.
if (loc.isLocal() && !loc.isLocalAndExists()) {
this.charSet = defaultEnc!=null ? defaultEnc : getDefaultEncoding();
this.charSet = defaultEnc != null ? defaultEnc : getDefaultEncoding();
this.loc = loc;
setText(null);
discardAllEdits();
setDirty(false);
return;
}
// Old local files and remote files, load 'em up. UnicodeReader will
// Old local files and remote files, load 'em up. UnicodeReader will
// check for BOMs and handle them correctly in all cases, then pass
// rest of stream down to InputStreamReader.
UnicodeReader ur = new UnicodeReader(loc.getInputStream(), defaultEnc);
// Remove listener so dirty flag doesn't get set when loading a file.
Document doc = getDocument();
doc.removeDocumentListener(this);
@@ -477,7 +459,6 @@ public class TextEditorPane extends RSyntaxTextArea implements
doc.addDocumentListener(this);
r.close();
}
// No IOException thrown, so we can finally change the location.
charSet = ur.getEncoding();
String old = getFileFullPath();
@@ -486,25 +467,26 @@ public class TextEditorPane extends RSyntaxTextArea implements
setCaretPosition(0);
discardAllEdits();
firePropertyChange(FULL_PATH_PROPERTY, old, getFileFullPath());
}
/**
* Reloads this file from disk. The file must exist for this operation
* to not throw an exception.<p>
* Reloads this file from disk. The file must exist for this operation
* to not throw an exception.
* <p>
*
* The file's "dirty" state will be set to <code>false</code> after this
* operation. If this is a local file, its "last modified" time is
* updated to reflect that of the actual file.<p>
* operation. If this is a local file, its "last modified" time is
* updated to reflect that of the actual file.
* <p>
*
* Note that if the file has been modified on disk, and is now a Unicode
* encoding when before it wasn't (or if it is a different Unicode now),
* this will cause this {@link TextEditorPane}'s encoding to change.
* Otherwise, the file's encoding will stay the same.
*
* @throws IOException If the file does not exist, or if an IO error
* occurs reading the file.
* @throws IOException
* If the file does not exist, or if an IO error
* occurs reading the file.
* @see #isLocalAndExists()
*/
public void reload() throws IOException {
@@ -522,12 +504,12 @@ public class TextEditorPane extends RSyntaxTextArea implements
syncLastSaveOrLoadTimeToActualFile();
discardAllEdits(); // Prevent user from being able to undo the reload
}
/**
* Called whenever text is removed from this editor.
*
* @param e The document event.
* @param e
* The document event.
*/
@Override
public void removeUpdate(DocumentEvent e) {
@@ -535,15 +517,16 @@ public class TextEditorPane extends RSyntaxTextArea implements
setDirty(true);
}
}
/**
* Saves the file in its current encoding.<p>
* Saves the file in its current encoding.
* <p>
*
* The text area's "dirty" state is set to <code>false</code>, and if
* this is a local file, its "last modified" time is updated.
*
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
* @see #saveAs(FileLocation)
* @see #load(FileLocation, String)
*/
@@ -552,14 +535,15 @@ public class TextEditorPane extends RSyntaxTextArea implements
setDirty(false);
syncLastSaveOrLoadTimeToActualFile();
}
/**
* Saves this file in a new local location. This method fires a property
* Saves this file in a new local location. This method fires a property
* change event of type {@link #FULL_PATH_PROPERTY}.
*
* @param loc The location to save to.
* @throws IOException If an IO error occurs.
* @param loc
* The location to save to.
* @throws IOException
* If an IO error occurs.
* @see #save()
* @see #load(FileLocation, String)
*/
@@ -572,93 +556,94 @@ public class TextEditorPane extends RSyntaxTextArea implements
lastSaveOrLoadTime = loc.getActualLastModified();
firePropertyChange(FULL_PATH_PROPERTY, old, getFileFullPath());
}
/**
* Saves the text in this editor to the specified location.
*
* @param loc The location to save to.
* @throws IOException If an IO error occurs.
* @param loc
* The location to save to.
* @throws IOException
* If an IO error occurs.
*/
private void saveImpl(FileLocation loc) throws IOException {
OutputStream out = loc.getOutputStream();
BufferedWriter w = new BufferedWriter(
new UnicodeWriter(out, getEncoding()));
BufferedWriter w = new BufferedWriter(new UnicodeWriter(out, getEncoding()));
try {
write(w);
} finally {
w.close();
}
}
/**
* Sets whether or not this text in this editor has unsaved changes.
* This fires a property change event of type {@link #DIRTY_PROPERTY}.<p>
* This fires a property change event of type {@link #DIRTY_PROPERTY}.
* <p>
*
* Applications will usually have no need to call this method directly; the
* only time you might have a need to call this method directly is if you
* have to initialize an instance of TextEditorPane with content that does
* not come from a file. <code>TextEditorPane</code> automatically sets its
* own dirty flag when its content is edited, when its encoding is changed,
* or when its line ending property is changed. It is cleared whenever
* or when its line ending property is changed. It is cleared whenever
* <code>load()</code>, <code>reload()</code>, <code>save()</code>, or
* <code>saveAs()</code> are called.
*
* @param dirty Whether or not the text has been modified.
* @param dirty
* Whether or not the text has been modified.
* @see #isDirty()
*/
public void setDirty(boolean dirty) {
if (this.dirty!=dirty) {
if (this.dirty != dirty) {
this.dirty = dirty;
firePropertyChange(DIRTY_PROPERTY, !dirty, dirty);
}
}
/**
* Sets the document for this editor.
*
* @param doc The new document.
* @param doc
* The new document.
*/
@Override
public void setDocument(Document doc) {
Document old = getDocument();
if (old!=null) {
if (old != null) {
old.removeDocumentListener(this);
}
super.setDocument(doc);
doc.addDocumentListener(this);
}
/**
* Sets the encoding to use when reading or writing this file. This
* Sets the encoding to use when reading or writing this file. This
* method sets the editor's dirty flag when the encoding is changed, and
* fires a property change event of type {@link #ENCODING_PROPERTY}.
*
* @param encoding The new encoding.
* @throws UnsupportedCharsetException If the encoding is not supported.
* @throws NullPointerException If <code>encoding</code> is
* <code>null</code>.
* @param encoding
* The new encoding.
* @throws UnsupportedCharsetException
* If the encoding is not supported.
* @throws NullPointerException
* If <code>encoding</code> is
* <code>null</code>.
* @see #getEncoding()
*/
public void setEncoding(String encoding) {
if (encoding==null) {
if (encoding == null) {
throw new NullPointerException("encoding cannot be null");
}
else if (!Charset.isSupported(encoding)) {
} else if (!Charset.isSupported(encoding)) {
throw new UnsupportedCharsetException(encoding);
}
if (charSet==null || !charSet.equals(encoding)) {
if (charSet == null || !charSet.equals(encoding)) {
String oldEncoding = charSet;
charSet = encoding;
firePropertyChange(ENCODING_PROPERTY, oldEncoding, charSet);
setDirty(true);
}
}
/**
* Sets the line separator sequence to use when this file is saved (e.g.
* "<code>\n</code>", "<code>\r\n</code>" or "<code>\r</code>").
@@ -667,18 +652,21 @@ public class TextEditorPane extends RSyntaxTextArea implements
* <code>getDocument().putProperty()</code> because it sets the editor's
* dirty flag when the line separator is changed.
*
* @param separator The new line separator.
* @throws NullPointerException If <code>separator</code> is
* <code>null</code>.
* @throws IllegalArgumentException If <code>separator</code> is not one
* of "<code>\n</code>", "<code>\r\n</code>" or "<code>\r</code>".
* @param separator
* The new line separator.
* @throws NullPointerException
* If <code>separator</code> is
* <code>null</code>.
* @throws IllegalArgumentException
* If <code>separator</code> is not one
* of "<code>\n</code>", "<code>\r\n</code>" or
* "<code>\r</code>".
* @see #getLineSeparator()
*/
public void setLineSeparator(String separator) {
setLineSeparator(separator, true);
}
/**
* Sets the line separator sequence to use when this file is saved (e.g.
* "<code>\n</code>", "<code>\r\n</code>" or "<code>\r</code>").
@@ -687,55 +675,57 @@ public class TextEditorPane extends RSyntaxTextArea implements
* <code>getDocument().putProperty()</code> because can set the editor's
* dirty flag when the line separator is changed.
*
* @param separator The new line separator.
* @param setDirty Whether the dirty flag should be set if the line
* separator is changed.
* @throws NullPointerException If <code>separator</code> is
* <code>null</code>.
* @throws IllegalArgumentException If <code>separator</code> is not one
* of "<code>\n</code>", "<code>\r\n</code>" or "<code>\r</code>".
* @param separator
* The new line separator.
* @param setDirty
* Whether the dirty flag should be set if the line
* separator is changed.
* @throws NullPointerException
* If <code>separator</code> is
* <code>null</code>.
* @throws IllegalArgumentException
* If <code>separator</code> is not one
* of "<code>\n</code>", "<code>\r\n</code>" or
* "<code>\r</code>".
* @see #getLineSeparator()
*/
public void setLineSeparator(String separator, boolean setDirty) {
if (separator==null) {
if (separator == null) {
throw new NullPointerException("terminator cannot be null");
}
if (!"\r\n".equals(separator) && !"\n".equals(separator) &&
!"\r".equals(separator)) {
if (!"\r\n".equals(separator) && !"\n".equals(separator) && !"\r".equals(separator)) {
throw new IllegalArgumentException("Invalid line terminator");
}
Document doc = getDocument();
Object old = doc.getProperty(
RTextAreaEditorKit.EndOfLineStringProperty);
Object old = doc.getProperty(RTextAreaEditorKit.EndOfLineStringProperty);
if (!separator.equals(old)) {
doc.putProperty(RTextAreaEditorKit.EndOfLineStringProperty,
separator);
doc.putProperty(RTextAreaEditorKit.EndOfLineStringProperty, separator);
if (setDirty) {
setDirty(true);
}
}
}
/**
* Sets whether or not this text area should be treated as read-only.
* This fires a property change event of type {@link #READ_ONLY_PROPERTY}.
*
* @param readOnly Whether or not the document is read-only.
* @param readOnly
* Whether or not the document is read-only.
* @see #isReadOnly()
*/
public void setReadOnly(boolean readOnly) {
if (this.readOnly!=readOnly) {
if (this.readOnly != readOnly) {
this.readOnly = readOnly;
firePropertyChange(READ_ONLY_PROPERTY, !readOnly, readOnly);
}
}
/**
* Syncs this text area's "last saved or loaded" time to that of the file
* being edited, if that file is local and exists. If the file is
* remote or is local but does not yet exist, nothing happens.<p>
* being edited, if that file is local and exists. If the file is
* remote or is local but does not yet exist, nothing happens.
* <p>
*
* You normally do not have to call this method, as the "last saved or
* loaded" time for {@link TextEditorPane}s is kept up-to-date internally
@@ -749,8 +739,7 @@ public class TextEditorPane extends RSyntaxTextArea implements
lastSaveOrLoadTime = loc.getActualLastModified();
}
}
public static void main(String[] args) throws Exception {
try {
TextEditorPane textArea = new TextEditorPane();
@@ -769,4 +758,4 @@ public class TextEditorPane extends RSyntaxTextArea implements
e.printStackTrace();
}
}
}
}
+164 -262
View File
@@ -1,9 +1,7 @@
/*
* 10/30/2011
*
* Theme.java - A color theme for RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -45,22 +43,24 @@ import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/**
* A theme is a set of fonts and colors to use to style RSyntaxTextArea and
* RTextScrollPane. Themes are defined in XML files that are validated against
* <code>org/fife/ui/rsyntaxtextarea/themes/theme.dtd</code>. This provides
* RTextScrollPane. Themes are defined in XML files that are validated against
* <code>org/fife/ui/rsyntaxtextarea/themes/theme.dtd</code>. This provides
* applications and other consumers with an easy way to style RSyntaxTextArea
* without having to use the API.<p>
* without having to use the API.
* <p>
*
* Sample themes are included in the source tree in the
* <code>org.fife.ui.rsyntaxtextarea.themes</code> package, and can be loaded
* via <code>getClass().getResourceAsStream(...)</code>.<p>
* via <code>getClass().getResourceAsStream(...)</code>.
* <p>
*
* All fields are public to facilitate programmatic manipulation, but typically
* you won't need to reference any fields directly, rather using the
* <code>load()</code>, <code>save()</code>, and <code>apply()</code> methods
* for various tasks.<p>
* for various tasks.
* <p>
*
* Note that to save a <code>Theme</code> via {@link #save(OutputStream)},
* you must currently create a <code>Theme</code> from a text area wrapped in
@@ -72,7 +72,7 @@ import org.xml.sax.helpers.DefaultHandler;
*/
@SuppressWarnings({ "checkstyle:visibilitymodifier" })
public class Theme {
public Font baseFont;
public Color bgColor;
public Color caretColor;
@@ -92,9 +92,7 @@ public class Theme {
public boolean matchedBracketAnimate;
public Color hyperlinkFG;
public Color[] secondaryLanguages;
public SyntaxScheme scheme;
public Color gutterBackgroundColor;
public Color gutterBorderColor;
public Color activeLineRangeColor;
@@ -105,31 +103,30 @@ public class Theme {
public Color foldIndicatorFG;
public Color foldBG;
public Color armedFoldBG;
/**
* Private constructor, used when loading from a stream.
*
* @param baseFont The default font to use for any "base font" properties
* not specified in the theme XML. If this is <code>null</code>,
* a default monospaced font will be used.
* @param baseFont
* The default font to use for any "base font" properties
* not specified in the theme XML. If this is <code>null</code>,
* a default monospaced font will be used.
*/
private Theme(Font baseFont) {
// Optional fields that require a default value.
this.baseFont = baseFont!=null ? baseFont : RTextArea.getDefaultFont();
this.baseFont = baseFont != null ? baseFont : RTextArea.getDefaultFont();
secondaryLanguages = new Color[3];
activeLineRangeColor = Gutter.DEFAULT_ACTIVE_LINE_RANGE_COLOR;
}
/**
* Creates a theme from an RSyntaxTextArea. It should be contained in
* Creates a theme from an RSyntaxTextArea. It should be contained in
* an <code>RTextScrollPane</code> to get all gutter color information.
*
* @param textArea The text area.
* @param textArea
* The text area.
*/
public Theme(RSyntaxTextArea textArea) {
baseFont = textArea.getFont();
bgColor = textArea.getBackground();
caretColor = textArea.getCaretColor();
@@ -148,17 +145,14 @@ public class Theme {
matchedBracketHighlightBoth = textArea.getPaintMatchedBracketPair();
matchedBracketAnimate = textArea.getAnimateBracketMatching();
hyperlinkFG = textArea.getHyperlinkForeground();
int count = textArea.getSecondaryLanguageCount();
secondaryLanguages = new Color[count];
for (int i=0; i<count; i++) {
secondaryLanguages[i]= textArea.getSecondaryLanguageBackground(i+1);
for (int i = 0; i < count; i++) {
secondaryLanguages[i] = textArea.getSecondaryLanguageBackground(i + 1);
}
scheme = textArea.getSyntaxScheme();
Gutter gutter = RSyntaxUtilities.getGutter(textArea);
if (gutter!=null) {
if (gutter != null) {
gutterBackgroundColor = gutter.getBackground();
gutterBorderColor = gutter.getBorderColor();
activeLineRangeColor = gutter.getActiveLineRangeColor();
@@ -170,17 +164,15 @@ public class Theme {
foldBG = gutter.getFoldBackground();
armedFoldBG = gutter.getArmedFoldBackground();
}
}
/**
* Applies this theme to a text area.
*
* @param textArea The text area to apply this theme to.
* @param textArea
* The text area to apply this theme to.
*/
public void apply(RSyntaxTextArea textArea) {
textArea.setFont(baseFont);
textArea.setBackground(bgColor);
textArea.setCaretColor(caretColor);
@@ -199,45 +191,37 @@ public class Theme {
textArea.setPaintMatchedBracketPair(matchedBracketHighlightBoth);
textArea.setAnimateBracketMatching(matchedBracketAnimate);
textArea.setHyperlinkForeground(hyperlinkFG);
int count = secondaryLanguages.length;
for (int i=0; i<count; i++) {
textArea.setSecondaryLanguageBackground(i+1, secondaryLanguages[i]);
for (int i = 0; i < count; i++) {
textArea.setSecondaryLanguageBackground(i + 1, secondaryLanguages[i]);
}
textArea.setSyntaxScheme(scheme);
Gutter gutter = RSyntaxUtilities.getGutter(textArea);
if (gutter!=null) {
if (gutter != null) {
gutter.setBackground(gutterBackgroundColor);
gutter.setBorderColor(gutterBorderColor);
gutter.setActiveLineRangeColor(activeLineRangeColor);
gutter.setIconRowHeaderInheritsGutterBackground(iconRowHeaderInheritsGutterBG);
gutter.setLineNumberColor(lineNumberColor);
String fontName = lineNumberFont!=null ? lineNumberFont :
baseFont.getFamily();
int fontSize = lineNumberFontSize>0 ? lineNumberFontSize :
baseFont.getSize();
String fontName = lineNumberFont != null ? lineNumberFont : baseFont.getFamily();
int fontSize = lineNumberFontSize > 0 ? lineNumberFontSize : baseFont.getSize();
Font font = getFont(fontName, Font.PLAIN, fontSize);
gutter.setLineNumberFont(font);
gutter.setFoldIndicatorForeground(foldIndicatorFG);
gutter.setFoldBackground(foldBG);
gutter.setArmedFoldBackground(armedFoldBG);
}
}
private static String colorToString(Color c) {
int color = c.getRGB() & 0xffffff;
StringBuilder stringBuilder = new StringBuilder(Integer.toHexString(color));
while (stringBuilder.length()<6) {
stringBuilder.insert(0, "0");
StringBuilder stringBuilder = new StringBuilder(Integer.toHexString(color));
while (stringBuilder.length() < 6) {
stringBuilder.insert(0, "0");
}
return stringBuilder.toString();
}
/**
* Returns the default selection background color to use if "default" is
* specified in a theme.
@@ -247,20 +231,19 @@ public class Theme {
*/
private static Color getDefaultBG() {
Color c = UIManager.getColor("nimbusLightBackground");
if (c==null) {
if (c == null) {
// Don't search for "text", as Nimbus defines that as the text
// component "text" color, but Basic LAFs use it for text
// component backgrounds! Nimbus also defines TextArea.background
// component backgrounds! Nimbus also defines TextArea.background
// as too dark, not what it actually uses for text area backgrounds
c = UIManager.getColor("TextArea.background");
if (c==null) {
if (c == null) {
c = new ColorUIResource(SystemColor.text);
}
}
return c;
}
/**
* Returns the default selection background color to use if "default" is
* specified in a theme.
@@ -270,19 +253,18 @@ public class Theme {
*/
private static Color getDefaultSelectionBG() {
Color c = UIManager.getColor("TextArea.selectionBackground");
if (c==null) {
if (c == null) {
c = UIManager.getColor("textHighlight");
if (c==null) {
if (c == null) {
c = UIManager.getColor("nimbusSelectionBackground");
if (c==null) {
if (c == null) {
c = new ColorUIResource(SystemColor.textHighlight);
}
}
}
return c;
}
/**
* Returns the default "selected text" color to use if "default" is
* specified in a theme.
@@ -292,25 +274,27 @@ public class Theme {
*/
private static Color getDefaultSelectionFG() {
Color c = UIManager.getColor("TextArea.selectionForeground");
if (c==null) {
if (c == null) {
c = UIManager.getColor("textHighlightText");
if (c==null) {
if (c == null) {
c = UIManager.getColor("nimbusSelectedText");
if (c==null) {
if (c == null) {
c = new ColorUIResource(SystemColor.textHighlightText);
}
}
}
return c;
}
/**
* Returns the specified font.
*
* @param family The font family.
* @param style The style of font.
* @param size The size of the font.
* @param family
* The font family.
* @param style
* The style of font.
* @param size
* The size of the font.
* @return The font.
*/
private static Font getFont(String family, int style, int size) {
@@ -318,187 +302,163 @@ public class Theme {
StyleContext sc = StyleContext.getDefaultStyleContext();
return sc.getFont(family, style, size);
}
/**
* Loads a theme.
*
* @param in The input stream to read from. This will be closed when this
* method returns.
* @param in
* The input stream to read from. This will be closed when this
* method returns.
* @return The theme.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
* @see #save(OutputStream)
*/
public static Theme load(InputStream in) throws IOException {
return load(in, null);
}
/**
* Loads a theme.
*
* @param in The input stream to read from. This will be closed when this
* method returns.
* @param baseFont The default font to use for any "base font" properties
* not specified in the theme XML. If this is <code>null</code>,
* a default monospaced font will be used.
* @param in
* The input stream to read from. This will be closed when this
* method returns.
* @param baseFont
* The default font to use for any "base font" properties
* not specified in the theme XML. If this is <code>null</code>,
* a default monospaced font will be used.
* @return The theme.
* @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
* @see #save(OutputStream)
*/
public static Theme load(InputStream in, Font baseFont) throws IOException {
Theme theme = new Theme(baseFont);
BufferedInputStream bin = new BufferedInputStream(in);
try {
XmlHandler.load(theme, bin);
} finally {
bin.close();
}
return theme;
}
/**
* Saves this theme to an output stream.
*
* @param out The output stream to write to.
* @throws IOException If an IO error occurs.
* @param out
* The output stream to write to.
* @throws IOException
* If an IO error occurs.
* @see #load(InputStream)
*/
public void save(OutputStream out) throws IOException {
BufferedOutputStream bout = new BufferedOutputStream(out);
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().
newDocumentBuilder();
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
DOMImplementation impl = db.getDOMImplementation();
Document doc = impl.createDocument(null, "RSyntaxTheme", null);
Element root = doc.getDocumentElement();
root.setAttribute("version", "1.0");
Element elem = doc.createElement("baseFont");
if (!baseFont.getFamily().equals(
RSyntaxTextArea.getDefaultFont().getFamily())) {
if (!baseFont.getFamily().equals(RSyntaxTextArea.getDefaultFont().getFamily())) {
elem.setAttribute("family", baseFont.getFamily());
}
elem.setAttribute("size", Integer.toString(baseFont.getSize()));
root.appendChild(elem);
elem = doc.createElement("background");
elem.setAttribute("color", colorToString(bgColor));
root.appendChild(elem);
elem = doc.createElement("caret");
elem.setAttribute("color", colorToString(caretColor));
root.appendChild(elem);
elem = doc.createElement("selection");
elem.setAttribute("useFG", Boolean.toString(useSelctionFG));
elem.setAttribute("fg", colorToString(selectionFG));
elem.setAttribute("bg", colorToString(selectionBG));
elem.setAttribute("roundedEdges", Boolean.toString(selectionRoundedEdges));
root.appendChild(elem);
elem = doc.createElement("currentLineHighlight");
elem.setAttribute("color", colorToString(currentLineHighlight));
elem.setAttribute("fade", Boolean.toString(fadeCurrentLineHighlight));
root.appendChild(elem);
elem = doc.createElement("marginLine");
elem.setAttribute("fg", colorToString(marginLineColor));
root.appendChild(elem);
elem = doc.createElement("markAllHighlight");
elem.setAttribute("color", colorToString(markAllHighlightColor));
root.appendChild(elem);
elem = doc.createElement("markOccurrencesHighlight");
elem.setAttribute("color", colorToString(markOccurrencesColor));
elem.setAttribute("border", Boolean.toString(markOccurrencesBorder));
root.appendChild(elem);
elem = doc.createElement("matchedBracket");
elem.setAttribute("fg", colorToString(matchedBracketFG));
elem.setAttribute("bg", colorToString(matchedBracketBG));
elem.setAttribute("highlightBoth", Boolean.toString(matchedBracketHighlightBoth));
elem.setAttribute("animate", Boolean.toString(matchedBracketAnimate));
root.appendChild(elem);
elem = doc.createElement("hyperlinks");
elem.setAttribute("fg", colorToString(hyperlinkFG));
root.appendChild(elem);
elem = doc.createElement("secondaryLanguages");
for (int i=0; i<secondaryLanguages.length; i++) {
for (int i = 0; i < secondaryLanguages.length; i++) {
Color color = secondaryLanguages[i];
Element elem2 = doc.createElement("language");
elem2.setAttribute("index", Integer.toString(i+1));
elem2.setAttribute("bg", color==null ? "":colorToString(color));
elem2.setAttribute("index", Integer.toString(i + 1));
elem2.setAttribute("bg", color == null ? "" : colorToString(color));
elem.appendChild(elem2);
}
root.appendChild(elem);
elem = doc.createElement("gutterBackground");
elem.setAttribute("color", colorToString(gutterBackgroundColor));
root.appendChild(elem);
elem = doc.createElement("gutterBorder");
elem.setAttribute("color", colorToString(gutterBorderColor));
root.appendChild(elem);
elem = doc.createElement("lineNumbers");
elem.setAttribute("fg", colorToString(lineNumberColor));
if (lineNumberFont!=null) {
if (lineNumberFont != null) {
elem.setAttribute("fontFamily", lineNumberFont);
}
if (lineNumberFontSize>0) {
elem.setAttribute("fontSize",
Integer.toString(lineNumberFontSize));
if (lineNumberFontSize > 0) {
elem.setAttribute("fontSize", Integer.toString(lineNumberFontSize));
}
root.appendChild(elem);
elem = doc.createElement("foldIndicator");
elem.setAttribute("fg", colorToString(foldIndicatorFG));
elem.setAttribute("iconBg", colorToString(foldBG));
elem.setAttribute("iconArmedBg", colorToString(armedFoldBG));
root.appendChild(elem);
elem = doc.createElement("iconRowHeader");
elem.setAttribute("activeLineRange", colorToString(activeLineRangeColor));
elem.setAttribute("inheritsGutterBG", Boolean.toString(iconRowHeaderInheritsGutterBG));
root.appendChild(elem);
elem = doc.createElement("tokenStyles");
Field[] fields = TokenTypes.class.getFields();
for (int i=0; i<fields.length; i++) {
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
int value = field.getInt(null);
if (value!=TokenTypes.DEFAULT_NUM_TOKEN_TYPES) {
if (value != TokenTypes.DEFAULT_NUM_TOKEN_TYPES) {
Style style = scheme.getStyle(value);
if (style!=null) {
if (style != null) {
Element elem2 = doc.createElement("style");
elem2.setAttribute("token", field.getName());
Color fg = style.foreground;
if (fg!=null) {
if (fg != null) {
elem2.setAttribute("fg", colorToString(fg));
}
Color bg = style.background;
if (bg!=null) {
if (bg != null) {
elem2.setAttribute("bg", colorToString(bg));
}
Font font = style.font;
if (font!=null) {
if (!font.getFamily().equals(
baseFont.getFamily())) {
if (font != null) {
if (!font.getFamily().equals(baseFont.getFamily())) {
elem2.setAttribute("fontFamily", font.getFamily());
}
if (font.getSize()!=baseFont.getSize()) {
if (font.getSize() != baseFont.getSize()) {
elem2.setAttribute("fontSize", Integer.toString(font.getSize()));
}
if (font.isBold()) {
@@ -516,12 +476,10 @@ public class Theme {
}
}
root.appendChild(elem);
DOMSource source = new DOMSource(doc);
// Use a writer instead of OutputStream to allow pretty printing.
// See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6337981
StreamResult result = new StreamResult(new PrintWriter(
new UnicodeWriter(bout, "UTF-8")));
StreamResult result = new StreamResult(new PrintWriter(new UnicodeWriter(bout, "UTF-8")));
TransformerFactory transFac = TransformerFactory.newInstance();
Transformer transformer = transFac.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
@@ -529,7 +487,6 @@ public class Theme {
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "theme.dtd");
transformer.transform(source, result);
} catch (RuntimeException re) {
throw re; // FindBugs
} catch (Exception e) {
@@ -537,75 +494,78 @@ public class Theme {
} finally {
bout.close();
}
}
/**
* Returns the color represented by a string. The input is expected to
* be a 6-digit hex string, optionally prefixed by a '$'. For example,
* Returns the color represented by a string. The input is expected to
* be a 6-digit hex string, optionally prefixed by a '$'. For example,
* either of the following:
*
* <pre>
* "$00ff00"
* "00ff00"
* </pre>
*
* will return <code>new Color(0, 255, 0)</code>.
*
* @param s The string to evaluate.
* @param s
* The string to evaluate.
* @return The color.
*/
private static Color stringToColor(String s) {
return stringToColor(s, null);
}
/**
* Returns the color represented by a string. The input is expected to
* be a 6-digit hex string, optionally prefixed by a '$'. For example,
* Returns the color represented by a string. The input is expected to
* be a 6-digit hex string, optionally prefixed by a '$'. For example,
* either of the following:
*
* <pre>
* "$00ff00"
* "00ff00"
* </pre>
*
* will return <code>new Color(0, 255, 0)</code>.
*
* @param s The string to evaluate.
* @param defaultVal The color to use if <code>s</code> is
* "<code>default</code>".
* @param s
* The string to evaluate.
* @param defaultVal
* The color to use if <code>s</code> is
* "<code>default</code>".
* @return The color.
*/
private static Color stringToColor(String s, Color defaultVal) {
if (s==null || "default".equalsIgnoreCase(s)) {
if (s == null || "default".equalsIgnoreCase(s)) {
return defaultVal;
}
if (s.length()==6 || s.length()==7) {
if (s.charAt(0)=='$') {
if (s.length() == 6 || s.length() == 7) {
if (s.charAt(0) == '$') {
s = s.substring(1);
}
return new Color(Integer.parseInt(s, 16));
}
return null;
}
/**
* Loads a <code>SyntaxScheme</code> from an XML file.
*/
private static class XmlHandler extends DefaultHandler {
private Theme theme;
@Override
public void error(SAXParseException e) throws SAXException {
throw e;
}
@Override
public void fatalError(SAXParseException e) throws SAXException {
throw e;
}
public static void load(Theme theme, InputStream in) throws IOException {
public static void load(Theme theme, InputStream in) throws IOException {
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setValidating(true);
try {
@@ -620,13 +580,12 @@ public class Theme {
InputSource is = new InputSource(in);
is.setEncoding("UTF-8");
reader.parse(is);
} catch (/*SAX|ParserConfiguration*/Exception se) {
} catch (/* SAX|ParserConfiguration */Exception se) {
throw new IOException(se.toString());
}
}
private static int parseInt(Attributes attrs, String attr,
int def) {
private static int parseInt(Attributes attrs, String attr, int def) {
int value = def;
String temp = attrs.getValue(attr);
if (temp != null) {
@@ -638,117 +597,86 @@ public class Theme {
}
return value;
}
@Override
public InputSource resolveEntity(String publicID,
String systemID) throws SAXException {
return new InputSource(getClass().
getResourceAsStream("themes/theme.dtd"));
public InputSource resolveEntity(String publicID, String systemID) throws SAXException {
return new InputSource(getClass().getResourceAsStream("themes/theme.dtd"));
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attrs) {
public void startElement(String uri, String localName, String qName, Attributes attrs) {
if ("background".equals(qName)) {
String color = attrs.getValue("color");
if (color!=null) {
if (color != null) {
theme.bgColor = stringToColor(color, getDefaultBG());
theme.gutterBackgroundColor = theme.bgColor;
}
else {
} else {
String img = attrs.getValue("image");
if (img!=null) {
if (img != null) {
throw new IllegalArgumentException("Not yet implemented");
}
}
}
// The base font to use in the editor.
else if ("baseFont".equals(qName)) {
int size = theme.baseFont.getSize();
String sizeStr = attrs.getValue("size");
if (sizeStr!=null) {
if (sizeStr != null) {
size = Integer.parseInt(sizeStr);
}
String family = attrs.getValue("family");
if (family!=null) {
if (family != null) {
theme.baseFont = getFont(family, Font.PLAIN, size);
}
else if (sizeStr!=null) {
} else if (sizeStr != null) {
// No family specified, keep original family
theme.baseFont = theme.baseFont.deriveFont(size*1f);
theme.baseFont = theme.baseFont.deriveFont(size * 1f);
}
}
else if ("caret".equals(qName)) {
} else if ("caret".equals(qName)) {
String color = attrs.getValue("color");
theme.caretColor = stringToColor(color);
}
else if ("currentLineHighlight".equals(qName)) {
} else if ("currentLineHighlight".equals(qName)) {
String color = attrs.getValue("color");
theme.currentLineHighlight = stringToColor(color);
String fadeStr = attrs.getValue("fade");
boolean fade = Boolean.parseBoolean(fadeStr);
theme.fadeCurrentLineHighlight = fade;
}
else if ("foldIndicator".equals(qName)) {
} else if ("foldIndicator".equals(qName)) {
String color = attrs.getValue("fg");
theme.foldIndicatorFG = stringToColor(color);
color = attrs.getValue("iconBg");
theme.foldBG = stringToColor(color);
color = attrs.getValue("iconArmedBg");
theme.armedFoldBG = stringToColor(color);
}
else if ("gutterBackground".equals(qName)) {
} else if ("gutterBackground".equals(qName)) {
String color = attrs.getValue("color");
if (color!=null) {
if (color != null) {
theme.gutterBackgroundColor = stringToColor(color);
}
}
else if ("gutterBorder".equals(qName)) {
} else if ("gutterBorder".equals(qName)) {
String color = attrs.getValue("color");
theme.gutterBorderColor = stringToColor(color);
}
else if ("iconRowHeader".equals(qName)) {
} else if ("iconRowHeader".equals(qName)) {
String color = attrs.getValue("activeLineRange");
theme.activeLineRangeColor = stringToColor(color);
String inheritBGStr = attrs.getValue("inheritsGutterBG");
theme.iconRowHeaderInheritsGutterBG =
inheritBGStr==null ? false : Boolean.valueOf(inheritBGStr);
}
else if ("lineNumbers".equals(qName)) {
theme.iconRowHeaderInheritsGutterBG = inheritBGStr == null ? false : Boolean.valueOf(inheritBGStr);
} else if ("lineNumbers".equals(qName)) {
String color = attrs.getValue("fg");
theme.lineNumberColor = stringToColor(color);
theme.lineNumberFont = attrs.getValue("fontFamily");
theme.lineNumberFontSize = parseInt(attrs, "fontSize", -1);
}
else if ("marginLine".equals(qName)) {
} else if ("marginLine".equals(qName)) {
String color = attrs.getValue("fg");
theme.marginLineColor = stringToColor(color);
}
else if ("markAllHighlight".equals(qName)) {
} else if ("markAllHighlight".equals(qName)) {
String color = attrs.getValue("color");
theme.markAllHighlightColor = stringToColor(color);
}
else if ("markOccurrencesHighlight".equals(qName)) {
} else if ("markOccurrencesHighlight".equals(qName)) {
String color = attrs.getValue("color");
theme.markOccurrencesColor = stringToColor(color);
String border = attrs.getValue("border");
theme.markOccurrencesBorder = Boolean.parseBoolean(border);
}
else if ("matchedBracket".equals(qName)) {
} else if ("matchedBracket".equals(qName)) {
String fg = attrs.getValue("fg");
theme.matchedBracketFG = stringToColor(fg);
String bg = attrs.getValue("bg");
@@ -757,44 +685,33 @@ public class Theme {
theme.matchedBracketHighlightBoth = Boolean.parseBoolean(highlightBoth);
String animate = attrs.getValue("animate");
theme.matchedBracketAnimate = Boolean.parseBoolean(animate);
}
else if ("hyperlinks".equals(qName)) {
} else if ("hyperlinks".equals(qName)) {
String fg = attrs.getValue("fg");
theme.hyperlinkFG = stringToColor(fg);
}
else if ("language".equals(qName)) {
} else if ("language".equals(qName)) {
String indexStr = attrs.getValue("index");
int index = Integer.parseInt(indexStr) - 1;
if (theme.secondaryLanguages.length>index) { // Sanity
if (theme.secondaryLanguages.length > index) { // Sanity
Color bg = stringToColor(attrs.getValue("bg"));
theme.secondaryLanguages[index] = bg;
}
}
else if ("selection".equals(qName)) {
} else if ("selection".equals(qName)) {
String useStr = attrs.getValue("useFG");
theme.useSelctionFG = Boolean.parseBoolean(useStr);
String color = attrs.getValue("fg");
theme.selectionFG = stringToColor(color,
getDefaultSelectionFG());
//System.out.println(theme.selectionFG);
theme.selectionFG = stringToColor(color, getDefaultSelectionFG());
// System.out.println(theme.selectionFG);
color = attrs.getValue("bg");
theme.selectionBG = stringToColor(color,
getDefaultSelectionBG());
theme.selectionBG = stringToColor(color, getDefaultSelectionBG());
String roundedStr = attrs.getValue("roundedEdges");
theme.selectionRoundedEdges = Boolean.parseBoolean(roundedStr);
}
// Start of the syntax scheme definition
else if ("tokenStyles".equals(qName)) {
theme.scheme = new SyntaxScheme(theme.baseFont, false);
}
// A style in the syntax scheme
else if ("style".equals(qName)) {
String type = attrs.getValue("token");
Field field = null;
try {
@@ -805,9 +722,7 @@ public class Theme {
System.err.println("Invalid token type: " + type);
return;
}
if (field.getType()==int.class) {
if (field.getType() == int.class) {
int index = 0;
try {
index = field.getInt(theme.scheme);
@@ -818,23 +733,19 @@ public class Theme {
e.printStackTrace();
return;
}
String fgStr = attrs.getValue("fg");
Color fg = stringToColor(fgStr);
theme.scheme.getStyle(index).foreground = fg;
String bgStr = attrs.getValue("bg");
Color bg = stringToColor(bgStr);
theme.scheme.getStyle(index).background = bg;
Font font = theme.baseFont;
String familyName = attrs.getValue("fontFamily");
if (familyName!=null) {
font = getFont(familyName, font.getStyle(),
font.getSize());
if (familyName != null) {
font = getFont(familyName, font.getStyle(), font.getSize());
}
String sizeStr = attrs.getValue("fontSize");
if (sizeStr!=null) {
if (sizeStr != null) {
try {
float size = Float.parseFloat(sizeStr);
size = Math.max(size, 1f);
@@ -844,17 +755,16 @@ public class Theme {
}
}
theme.scheme.getStyle(index).font = font;
boolean styleSpecified = false;
boolean bold = false;
boolean italic = false;
String boldStr = attrs.getValue("bold");
if (boldStr!=null) {
if (boldStr != null) {
bold = Boolean.parseBoolean(boldStr);
styleSpecified = true;
}
String italicStr = attrs.getValue("italic");
if (italicStr!=null) {
if (italicStr != null) {
italic = Boolean.parseBoolean(italicStr);
styleSpecified = true;
}
@@ -867,28 +777,20 @@ public class Theme {
style |= Font.ITALIC;
}
Font orig = theme.scheme.getStyle(index).font;
theme.scheme.getStyle(index).font =
orig.deriveFont(style);
theme.scheme.getStyle(index).font = orig.deriveFont(style);
}
String ulineStr = attrs.getValue("underline");
if (ulineStr!=null) {
boolean uline= Boolean.parseBoolean(ulineStr);
if (ulineStr != null) {
boolean uline = Boolean.parseBoolean(ulineStr);
theme.scheme.getStyle(index).underline = uline;
}
}
}
}
@Override
public void warning(SAXParseException e) throws SAXException {
throw e;
}
}
}
}
+199 -194
View File
@@ -1,9 +1,7 @@
/*
* 02/21/2004
*
* Token.java - A token used in syntax highlighting.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -12,22 +10,27 @@ import java.awt.Rectangle;
import javax.swing.text.TabExpander;
/**
* A generic token that functions as a node in a linked list of syntax
* highlighted tokens for some language.<p>
* highlighted tokens for some language.
* <p>
*
* A <code>Token</code> is a piece of text representing some logical token in
* source code for a programming language. For example, the line of C code:<p>
* source code for a programming language. For example, the line of C code:
* <p>
*
* <pre>
*
* int i = 0;
* </pre>
*
* would be broken into 8 <code>Token</code>s: the first representing
* <code>int</code>, the second whitespace, the third <code>i</code>, the fourth
* whitespace, the fifth <code>=</code>, etc.<p>
* whitespace, the fifth <code>=</code>, etc.
* <p>
*
* <b>Note:</b> The tokens returned by {@link RSyntaxDocument}s are pooled and
* should always be treated as immutable. Modifying tokens you did not create
* should always be treated as immutable. Modifying tokens you did not create
* yourself can and will result in rendering issues and/or runtime exceptions.
* You have been warned!
*
@@ -35,95 +38,97 @@ import javax.swing.text.TabExpander;
* @version 0.3
*/
public interface Token extends TokenTypes {
/**
* Appends HTML code for painting this token, using the given text area's
* color scheme.
*
* @param sb The buffer to append to.
* @param textArea The text area whose color scheme to use.
* @param fontFamily Whether to include the font family in the HTML for
* this token. You can pass <code>false</code> for this parameter
* if, for example, you are making all your HTML be monospaced,
* and don't want any crazy fonts being used in the editor to be
* reflected in your HTML.
* @param sb
* The buffer to append to.
* @param textArea
* The text area whose color scheme to use.
* @param fontFamily
* Whether to include the font family in the HTML for
* this token. You can pass <code>false</code> for this parameter
* if, for example, you are making all your HTML be monospaced,
* and don't want any crazy fonts being used in the editor to be
* reflected in your HTML.
* @return The buffer appended to.
* @see #getHTMLRepresentation(RSyntaxTextArea)
*/
StringBuilder appendHTMLRepresentation(StringBuilder sb,
RSyntaxTextArea textArea,
boolean fontFamily);
StringBuilder appendHTMLRepresentation(StringBuilder sb, RSyntaxTextArea textArea, boolean fontFamily);
/**
* Appends HTML code for painting this token, using the given text area's
* color scheme.
*
* @param sb The buffer to append to.
* @param textArea The text area whose color scheme to use.
* @param fontFamily Whether to include the font family in the HTML for
* this token. You can pass <code>false</code> for this parameter
* if, for example, you are making all your HTML be monospaced,
* and don't want any crazy fonts being used in the editor to be
* reflected in your HTML.
* @param tabsToSpaces Whether to convert tabs into spaces.
* @param sb
* The buffer to append to.
* @param textArea
* The text area whose color scheme to use.
* @param fontFamily
* Whether to include the font family in the HTML for
* this token. You can pass <code>false</code> for this parameter
* if, for example, you are making all your HTML be monospaced,
* and don't want any crazy fonts being used in the editor to be
* reflected in your HTML.
* @param tabsToSpaces
* Whether to convert tabs into spaces.
* @return The buffer appended to.
* @see #getHTMLRepresentation(RSyntaxTextArea)
*/
StringBuilder appendHTMLRepresentation(StringBuilder sb,
RSyntaxTextArea textArea, boolean fontFamily,
boolean tabsToSpaces);
StringBuilder appendHTMLRepresentation(StringBuilder sb, RSyntaxTextArea textArea, boolean fontFamily, boolean tabsToSpaces);
/**
* Returns the character at the specified offset in the token.
*
* @param index The index. This should be in the range
* <code>0-({@link #length()}-1)</code>.
* @param index
* The index. This should be in the range
* <code>0-({@link #length()}-1)</code>.
* @return The character.
* @see #length()
*/
char charAt(int index);
/**
* Returns whether the token straddles the specified position in the
* document.
*
* @param pos The position in the document to check.
* @param pos
* The position in the document to check.
* @return Whether the specified position is straddled by this token.
*/
boolean containsPosition(int pos);
/**
* Returns the position in the token's internal char array corresponding
* to the specified document position.<p>
* to the specified document position.
* <p>
* Note that this method does NOT do any bounds checking; you can pass in
* a document position that does not correspond to a position in this
* token, and you will not receive an Exception or any other notification;
* it is up to the caller to ensure valid input.
*
* @param pos A position in the document that is represented by this token.
* @param pos
* A position in the document that is represented by this token.
* @return The corresponding token position &gt;= <code>textOffset</code>
* and &lt; <code>textOffset+textCount</code>.
* @see #tokenToDocument
*/
int documentToToken(int pos);
/**
* Returns whether this token's lexeme ends with the specified characters.
*
* @param ch The characters.
* @param ch
* The characters.
* @return Whether this token's lexeme ends with the specified characters.
* @see #startsWith(char[])
*/
boolean endsWith(char[] ch);
/**
* Returns the end offset of this token in the document (exclusive). In
* Returns the end offset of this token in the document (exclusive). In
* other words, the token ranges from
* <code>[getOffset(), getEndOffset())</code>.
*
@@ -131,30 +136,28 @@ public interface Token extends TokenTypes {
* @see #getOffset()
*/
int getEndOffset();
/**
* Returns a <code>String</code> containing HTML code for painting this
* token, using the given text area's color scheme.
*
* @param textArea The text area whose color scheme to use.
* @param textArea
* The text area whose color scheme to use.
* @return The HTML representation of the token.
* @see #appendHTMLRepresentation(StringBuilder, RSyntaxTextArea, boolean)
*/
String getHTMLRepresentation(RSyntaxTextArea textArea);
/**
* Returns the language index of this token.
*
* @return The language index. A value of <code>0</code> denotes the
* "main" language, any positive value denotes a specific secondary
* language.
* @return The language index. A value of <code>0</code> denotes the
* "main" language, any positive value denotes a specific secondary
* language.
* @see #setLanguageIndex(int)
*/
int getLanguageIndex();
/**
* Returns the last token in this list that is not whitespace or a
* comment.
@@ -163,8 +166,7 @@ public interface Token extends TokenTypes {
* if there isn't one.
*/
Token getLastNonCommentNonWhitespaceToken();
/**
* Returns the last paintable token in this token list, or <code>null</code>
* if there is no paintable token.
@@ -172,10 +174,10 @@ public interface Token extends TokenTypes {
* @return The last paintable token in this token list.
*/
Token getLastPaintableToken();
/**
* Returns the text of this token, as a string.<p>
* Returns the text of this token, as a string.
* <p>
*
* Note that this method isn't used much by the
* <code>ryntaxtextarea</code> package internally, as it tries to limit
@@ -184,45 +186,47 @@ public interface Token extends TokenTypes {
* @return The text of this token.
*/
String getLexeme();
/**
* Determines the offset into this token list (i.e., into the
* document) that covers pixel location <code>x</code> if the token list
* starts at pixel location <code>x0</code><p>.
* starts at pixel location <code>x0</code>
* <p>
* .
* This method will return the document position "closest" to the
* x-coordinate (i.e., if they click on the "right-half" of the
* <code>w</code> in <code>awe</code>, the caret will be placed in
* between the <code>w</code> and <code>e</code>; similarly, clicking on
* the left-half places the caret between the <code>a</code> and
* <code>w</code>). This makes it useful for methods such as
* <code>w</code>). This makes it useful for methods such as
* <code>viewToModel</code> found in <code>javax.swing.text.View</code>
* subclasses.
*
* @param textArea The text area from which the token list was derived.
* @param e How to expand tabs.
* @param x0 The pixel x-location that is the beginning of
* <code>tokenList</code>.
* @param x The pixel-position for which you want to get the corresponding
* offset.
* @param textArea
* The text area from which the token list was derived.
* @param e
* How to expand tabs.
* @param x0
* The pixel x-location that is the beginning of
* <code>tokenList</code>.
* @param x
* The pixel-position for which you want to get the corresponding
* offset.
* @return The position (in the document, NOT into the token list!) that
* covers the pixel location. If <code>tokenList</code> is
* covers the pixel location. If <code>tokenList</code> is
* <code>null</code> or has type <code>Token.NULL</code>, then
* <code>-1</code> is returned; the caller should recognize this and
* return the actual end position of the (empty) line.
*/
int getListOffset(RSyntaxTextArea textArea, TabExpander e,
float x0, float x);
int getListOffset(RSyntaxTextArea textArea, TabExpander e, float x0, float x);
/**
* Returns the token after this one in the linked list.
*
* @return The next token.
*/
Token getNextToken();
/**
* Returns the offset into the document at which this token resides.
*
@@ -230,8 +234,7 @@ public interface Token extends TokenTypes {
* @see #getEndOffset()
*/
int getOffset();
/**
* Returns the position in the document that represents the last character
* in the token that will fit into <code>endBeforeX-startX</code> pixels.
@@ -239,31 +242,33 @@ public interface Token extends TokenTypes {
* have the token "while" and <code>startX</code> is <code>0</code> and
* <code>endBeforeX</code> is <code>30</code>, this method will return the
* document position of the "i" in "while", because the "i" ends at pixel
* <code>24</code>, while the "l" ends at <code>32</code>. If not even the
* <code>24</code>, while the "l" ends at <code>32</code>. If not even the
* first character fits in <code>endBeforeX-startX</code>, the first
* character's position is still returned so calling methods don't go into
* infinite loops.
*
* @param textArea The text area in which this token is being painted.
* @param e How to expand tabs.
* @param startX The x-coordinate at which the token will be painted. This
* is needed because of tabs.
* @param endBeforeX The x-coordinate for which you want to find the last
* character of <code>t</code> which comes before it.
* @param textArea
* The text area in which this token is being painted.
* @param e
* How to expand tabs.
* @param startX
* The x-coordinate at which the token will be painted. This
* is needed because of tabs.
* @param endBeforeX
* The x-coordinate for which you want to find the last
* character of <code>t</code> which comes before it.
* @return The last document position that will fit in the specified amount
* of pixels.
*/
/*
* @see #getTokenListOffsetBeforeX
* FIXME: This method does not compute correctly! It needs to be abstract
* FIXME: This method does not compute correctly! It needs to be abstract
* and implemented by subclasses.
*/
int getOffsetBeforeX(RSyntaxTextArea textArea, TabExpander e,
float startX, float endBeforeX);
int getOffsetBeforeX(RSyntaxTextArea textArea, TabExpander e, float startX, float endBeforeX);
/**
* Returns the character array backing the lexeme of this token. This
* Returns the character array backing the lexeme of this token. This
* value should be treated as read-only.
*
* @return A character array containing the lexeme of this token.
@@ -271,8 +276,7 @@ public interface Token extends TokenTypes {
* @see #length()
*/
char[] getTextArray();
/**
* Returns the offset into the character array of the lexeme in
* {@link #getTextArray()}.
@@ -281,8 +285,7 @@ public interface Token extends TokenTypes {
* @see #getTextArray()
*/
int getTextOffset();
/**
* Returns the type of this token.
*
@@ -291,43 +294,47 @@ public interface Token extends TokenTypes {
* @see #setType(int)
*/
int getType();
/**
* Returns the width of this token given the specified parameters.
*
* @param textArea The text area in which the token is being painted.
* @param e Describes how to expand tabs. This parameter cannot be
* <code>null</code>.
* @param x0 The pixel-location at which the token begins. This is needed
* because of tabs.
* @param textArea
* The text area in which the token is being painted.
* @param e
* Describes how to expand tabs. This parameter cannot be
* <code>null</code>.
* @param x0
* The pixel-location at which the token begins. This is needed
* because of tabs.
* @return The width of the token, in pixels.
* @see #getWidthUpTo
*/
float getWidth(RSyntaxTextArea textArea, TabExpander e, float x0);
/**
* Returns the width of a specified number of characters in this token.
* For example, for the token "while", specifying a value of <code>3</code>
* here returns the width of the "whi" portion of the token.
*
* @param numChars The number of characters for which to get the width.
* @param textArea The text area in which the token is being painted.
* @param e How to expand tabs. This value cannot be <code>null</code>.
* @param x0 The pixel-location at which this token begins. This is needed
* because of tabs.
* @param numChars
* The number of characters for which to get the width.
* @param textArea
* The text area in which the token is being painted.
* @param e
* How to expand tabs. This value cannot be <code>null</code>.
* @param x0
* The pixel-location at which this token begins. This is needed
* because of tabs.
* @return The width of the specified number of characters in this token.
* @see #getWidth
*/
float getWidthUpTo(int numChars, RSyntaxTextArea textArea,
TabExpander e, float x0);
float getWidthUpTo(int numChars, RSyntaxTextArea textArea, TabExpander e, float x0);
/**
* Returns whether this token's lexeme matches a specific character array.
*
* @param lexeme The lexeme to check for.
* @param lexeme
* The lexeme to check for.
* @return Whether this token has that lexeme.
* @see #is(int, char[])
* @see #is(int, String)
@@ -335,17 +342,19 @@ public interface Token extends TokenTypes {
* @see #startsWith(char[])
*/
boolean is(char[] lexeme);
/**
* Returns whether this token is of the specified type, with the specified
* lexeme.<p>
* lexeme.
* <p>
* This method is preferred over the other overload in performance-critical
* code where this operation may be called frequently, since it does not
* involve any String allocations.
*
* @param type The type to check for.
* @param lexeme The lexeme to check for.
* @param type
* The type to check for.
* @param lexeme
* The lexeme to check for.
* @return Whether this token has that type and lexeme.
* @see #is(int, String)
* @see #is(char[])
@@ -353,25 +362,26 @@ public interface Token extends TokenTypes {
* @see #startsWith(char[])
*/
boolean is(int type, char[] lexeme);
/**
* Returns whether this token is of the specified type, with the specified
* lexeme.<p>
* lexeme.
* <p>
* The other overload of this method is preferred over this one in
* performance-critical code, as this one involves a String allocation
* while the other does not.
*
* @param type The type to check for.
* @param lexeme The lexeme to check for.
* @param type
* The type to check for.
* @param lexeme
* The lexeme to check for.
* @return Whether this token has that type and lexeme.
* @see #is(int, char[])
* @see #isSingleChar(int, char)
* @see #startsWith(char[])
*/
boolean is(int type, String lexeme);
/**
* Returns whether this token is a comment.
*
@@ -380,8 +390,7 @@ public interface Token extends TokenTypes {
* @see #isCommentOrWhitespace()
*/
boolean isComment();
/**
* Returns whether this token is a comment or whitespace.
*
@@ -390,8 +399,7 @@ public interface Token extends TokenTypes {
* @see #isWhitespace()
*/
boolean isCommentOrWhitespace();
/**
* Returns whether this token is a hyperlink.
*
@@ -399,16 +407,14 @@ public interface Token extends TokenTypes {
* @see #setHyperlink(boolean)
*/
boolean isHyperlink();
/**
* Returns whether this token is an identifier.
*
* @return Whether this token is an identifier.
*/
boolean isIdentifier();
/**
* Returns whether this token is a {@link #SEPARATOR} representing a single
* left curly brace.
@@ -417,8 +423,7 @@ public interface Token extends TokenTypes {
* @see #isRightCurly()
*/
boolean isLeftCurly();
/**
* Returns whether this token is a {@link #SEPARATOR} representing a single
* right curly brace.
@@ -427,42 +432,41 @@ public interface Token extends TokenTypes {
* @see #isLeftCurly()
*/
boolean isRightCurly();
/**
* Returns whether or not this token is "paintable;" i.e., whether or not
* the type of this token is one such that it has an associated syntax
* style. What this boils down to is whether the token type is greater
* style. What this boils down to is whether the token type is greater
* than <code>Token.NULL</code>.
*
* @return Whether or not this token is paintable.
*/
boolean isPaintable();
/**
* Returns whether this token is the specified single character.
*
* @param ch The character to check for.
* @param ch
* The character to check for.
* @return Whether this token's lexeme is the single character specified.
* @see #isSingleChar(int, char)
*/
boolean isSingleChar(char ch);
/**
* Returns whether this token is the specified single character, and of a
* specific type.
*
* @param type The token type.
* @param ch The character to check for.
* @param type
* The token type.
* @param ch
* The character to check for.
* @return Whether this token is of the specified type, and with a lexeme
* Equaling the single character specified.
* @see #isSingleChar(char)
*/
boolean isSingleChar(int type, char ch);
/**
* Returns whether or not this token is whitespace.
*
@@ -471,8 +475,7 @@ public interface Token extends TokenTypes {
* @see #isCommentOrWhitespace()
*/
boolean isWhitespace();
/**
* Returns the length of this token.
*
@@ -480,88 +483,90 @@ public interface Token extends TokenTypes {
* @see #getOffset()
*/
int length();
/**
* Returns the bounding box for the specified document location. The
* Returns the bounding box for the specified document location. The
* location must be in the specified token list; if it isn't,
* <code>null</code> is returned.
*
* @param textArea The text area from which the token list was derived.
* @param e How to expand tabs.
* @param pos The position in the document for which to get the bounding
* box in the view.
* @param x0 The pixel x-location that is the beginning of
* <code>tokenList</code>.
* @param rect The rectangle in which we'll be returning the results. This
* object is reused to keep from frequent memory allocations.
* @param textArea
* The text area from which the token list was derived.
* @param e
* How to expand tabs.
* @param pos
* The position in the document for which to get the bounding
* box in the view.
* @param x0
* The pixel x-location that is the beginning of
* <code>tokenList</code>.
* @param rect
* The rectangle in which we'll be returning the results. This
* object is reused to keep from frequent memory allocations.
* @return The bounding box for the specified position in the model.
*/
Rectangle listOffsetToView(RSyntaxTextArea textArea, TabExpander e,
int pos, int x0, Rectangle rect);
Rectangle listOffsetToView(RSyntaxTextArea textArea, TabExpander e, int pos, int x0, Rectangle rect);
/**
* Sets whether this token is a hyperlink.
*
* @param hyperlink Whether this token is a hyperlink.
* @param hyperlink
* Whether this token is a hyperlink.
* @see #isHyperlink()
*/
void setHyperlink(boolean hyperlink);
/**
* Sets the language index for this token. If this value is positive, it
* Sets the language index for this token. If this value is positive, it
* denotes a specific "secondary" language this token represents (such as
* JavaScript code or CSS embedded in an HTML file). If this value is
* JavaScript code or CSS embedded in an HTML file). If this value is
* <code>0</code>, this token is in the "main" language being edited.
* Negative values are invalid and treated as <code>0</code>.
*
* @param languageIndex The new language index. A value of
* <code>0</code> denotes the "main" language, any positive value
* denotes a specific secondary language. Negative values will
* be treated as <code>0</code>.
* @param languageIndex
* The new language index. A value of
* <code>0</code> denotes the "main" language, any positive value
* denotes a specific secondary language. Negative values will
* be treated as <code>0</code>.
* @see #getLanguageIndex()
*/
void setLanguageIndex(int languageIndex);
/**
* Sets the type of this token.
*
* @param type The new token type.
* @param type
* The new token type.
* @see TokenTypes
* @see #getType()
*/
void setType(int type);
/**
* Returns whether this token starts with the specified characters.
*
* @param chars The characters.
* @param chars
* The characters.
* @return Whether this token starts with those characters.
* @see #endsWith(char[])
* @see #is(int, char[])
*/
boolean startsWith(char[] chars);
/**
* Returns the position in the document corresponding to the specified
* position in this token's internal char array (<code>textOffset</code> -
* <code>textOffset+textCount-1</code>).<p>
* <code>textOffset+textCount-1</code>).
* <p>
* Note that this method does NOT do any bounds checking; you can pass in
* an invalid token position, and you will not receive an Exception or any
* other indication that the returned document position is invalid. It is
* other indication that the returned document position is invalid. It is
* up to the user to ensure valid input.
*
* @param pos A position in the token's internal char array
* (<code>textOffset</code> - <code>textOffset+textCount</code>).
* @param pos
* A position in the token's internal char array
* (<code>textOffset</code> - <code>textOffset+textCount</code>).
* @return The corresponding position in the document.
* @see #documentToToken(int)
*/
int tokenToDocument(int pos);
}
}
@@ -1,16 +1,13 @@
/*
* 10/28/2004
*
* TokenFactory.java - Interface for a class that generates tokens of some type.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.Segment;
/**
* Interface for a class that generates tokens somehow.
*
@@ -18,50 +15,52 @@ import javax.swing.text.Segment;
* @version 0.1
*/
interface TokenFactory {
/**
* Returns a null token.
*
* @return A null token.
*/
TokenImpl createToken();
/**
* Returns a token.
*
* @param line The segment from which to get the token's text.
* @param beg The starting offset of the token's text in the segment.
* @param end The ending offset of the token's text in the segment.
* @param startOffset The offset in the document of the token.
* @param type The type of token.
* @param line
* The segment from which to get the token's text.
* @param beg
* The starting offset of the token's text in the segment.
* @param end
* The ending offset of the token's text in the segment.
* @param startOffset
* The offset in the document of the token.
* @param type
* The type of token.
* @return The token.
*/
TokenImpl createToken(final Segment line, final int beg,
final int end, final int startOffset, final int type);
TokenImpl createToken(final Segment line, final int beg, final int end, final int startOffset, final int type);
/**
* Returns a token.
*
* @param line The char array from which to get the token's text.
* @param beg The starting offset of the token's text in the char array.
* @param end The ending offset of the token's text in the char array.
* @param startOffset The offset in the document of the token.
* @param type The type of token.
* @param line
* The char array from which to get the token's text.
* @param beg
* The starting offset of the token's text in the char array.
* @param end
* The ending offset of the token's text in the char array.
* @param startOffset
* The offset in the document of the token.
* @param type
* The type of token.
* @return The token.
*/
TokenImpl createToken(final char[] line, final int beg,
final int end, final int startOffset, final int type);
TokenImpl createToken(final char[] line, final int beg, final int end, final int startOffset, final int type);
/**
* Resets the state of this token maker. This method should be called
* Resets the state of this token maker. This method should be called
* by the <code>TokenMaker</code> every time a token list is generated for
* a new line so the tokens can be reused.
*/
void resetAllTokens();
}
}
File diff suppressed because it is too large. Load diff
@@ -1,16 +1,13 @@
/*
* 08/28/2013
*
* TokenIterator.java - An iterator over the Tokens in an RSyntaxDocument.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.Iterator;
/**
* Allows you to iterate through all paintable tokens in an
* <code>RSyntaxDocument</code>.
@@ -19,33 +16,31 @@ import java.util.Iterator;
* @version 1.0
*/
class TokenIterator implements Iterator<Token> {
private RSyntaxDocument doc;
private int curLine;
private Token token;
/**
* Constructor.
*
* @param doc The document whose tokens we should iterate over.
* @param doc
* The document whose tokens we should iterate over.
*/
TokenIterator(RSyntaxDocument doc) {
this.doc = doc;
loadTokenListForCurLine();
int lineCount = getLineCount();
while ((token==null || !token.isPaintable()) && curLine<lineCount-1) {
while ((token == null || !token.isPaintable()) && curLine < lineCount - 1) {
curLine++;
loadTokenListForCurLine();
}
}
private int getLineCount() {
return doc.getDefaultRootElement().getElementCount();
}
/**
* Returns whether any more paintable tokens are in the document.
*
@@ -54,19 +49,17 @@ class TokenIterator implements Iterator<Token> {
*/
@Override
public boolean hasNext() {
return token!=null;
return token != null;
}
private void loadTokenListForCurLine() {
token = doc.getTokenListForLine(curLine);
if (token!=null && !token.isPaintable()) {
if (token != null && !token.isPaintable()) {
// Common end of document scenario
token = null;
}
}
/**
* Returns the next paintable token in the document.
*
@@ -75,27 +68,22 @@ class TokenIterator implements Iterator<Token> {
*/
@Override
public Token next() {
Token t = token;
boolean tIsCloned = false;
int lineCount = getLineCount();
// Get the next token, going to the next line if necessary.
if (token!=null && token.isPaintable()) {
if (token != null && token.isPaintable()) {
token = token.getNextToken();
}
else if (curLine<lineCount-1) {
} else if (curLine < lineCount - 1) {
t = new TokenImpl(t); // Clone t since tokens are pooled
tIsCloned = true;
curLine++;
loadTokenListForCurLine();
}
else if (token!=null && !token.isPaintable()) {
} else if (token != null && !token.isPaintable()) {
// Ends with a non-paintable token (not sure this ever happens)
token = null;
}
while ((token==null || !token.isPaintable()) && curLine<lineCount-1) {
while ((token == null || !token.isPaintable()) && curLine < lineCount - 1) {
if (!tIsCloned) {
t = new TokenImpl(t); // Clone t since tokens are pooled
tIsCloned = true;
@@ -103,25 +91,21 @@ class TokenIterator implements Iterator<Token> {
curLine++;
loadTokenListForCurLine();
}
if (token!=null && !token.isPaintable() && curLine==lineCount-1) {
if (token != null && !token.isPaintable() && curLine == lineCount - 1) {
token = null;
}
return t;
}
/**
* Always throws {@link UnsupportedOperationException}, as
* <code>Token</code> removal is not supported.
*
* @throws UnsupportedOperationException always.
* @throws UnsupportedOperationException
* always.
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
+71 -72
View File
@@ -1,10 +1,8 @@
/*
* 02/24/2004
*
* TokenMaker.java - An object that can take a chunk of text and return a
* linked list of <code>Token</code>s representing it.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -12,7 +10,6 @@ package org.fife.ui.rsyntaxtextarea;
import javax.swing.Action;
import javax.swing.text.Segment;
/**
* An implementation of <code>TokenMaker</code> is a class that turns text into
* a linked list of <code>Token</code>s for syntax highlighting
@@ -25,90 +22,94 @@ import javax.swing.text.Segment;
* @version 0.2
*/
public interface TokenMaker {
/**
* Adds a null token to the end of the current linked list of tokens.
* This should be put at the end of the linked list whenever the last
* token on the current line is NOT a multi-line token.
*/
public void addNullToken();
/**
* Adds the token specified to the current linked list of tokens.
*
* @param array The character array from which to get the text.
* @param start Start offset in <code>segment</code> of token.
* @param end End offset in <code>segment</code> of token.
* @param tokenType The token's type.
* @param startOffset The offset in the document at which this token
* occurs.
* @param array
* The character array from which to get the text.
* @param start
* Start offset in <code>segment</code> of token.
* @param end
* End offset in <code>segment</code> of token.
* @param tokenType
* The token's type.
* @param startOffset
* The offset in the document at which this token
* occurs.
*/
public void addToken(char[] array, int start, int end, int tokenType,
int startOffset);
public void addToken(char[] array, int start, int end, int tokenType, int startOffset);
/**
* Returns the closest {@link TokenTypes "standard" token type} for a given
* "internal" token type (e.g. one whose value is <code>&lt; 0</code>).
*
* @param type The token type.
* @return The closest "standard" token type. If a mapping is not defined
* @param type
* The token type.
* @return The closest "standard" token type. If a mapping is not defined
* for this language, then <code>type</code> is returned.
*/
public int getClosestStandardTokenTypeForInternalType(int type);
/**
* Returns whether this programming language uses curly braces
* ('<code>{</code>' and '<code>}</code>') to denote code blocks.
*
* @param languageIndex The language index at the offset in question.
* Since some <code>TokenMaker</code>s effectively have nested
* languages (such as JavaScript in HTML), this parameter tells the
* <code>TokenMaker</code> what sub-language to look at.
* @param languageIndex
* The language index at the offset in question.
* Since some <code>TokenMaker</code>s effectively have nested
* languages (such as JavaScript in HTML), this parameter tells
* the
* <code>TokenMaker</code> what sub-language to look at.
* @return Whether curly braces denote code blocks.
*/
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex);
/**
* Returns the last token on this line's type if the token is "unfinished",
* or {@link Token#NULL} if it was finished. For example, if C-style
* or {@link Token#NULL} if it was finished. For example, if C-style
* syntax highlighting is being implemented, and <code>text</code>
* contained a line of code that contained the beginning of a comment but
* no end-comment marker ("*\/"), then this method would return
* {@link Token#COMMENT_MULTILINE} for that line. This is useful
* {@link Token#COMMENT_MULTILINE} for that line. This is useful
* for doing syntax highlighting.
*
* @param text The line of tokens to examine.
* @param initialTokenType The token type to start with (i.e., the value
* of <code>getLastTokenTypeOnLine</code> for the line before
* <code>text</code>).
* @param text
* The line of tokens to examine.
* @param initialTokenType
* The token type to start with (i.e., the value
* of <code>getLastTokenTypeOnLine</code> for the line before
* <code>text</code>).
* @return The last token on this line's type, or {@link Token#NULL}
* if the line was completed.
*/
public int getLastTokenTypeOnLine(Segment text, int initialTokenType);
/**
* Returns the text to place at the beginning and end of a
* line to "comment" it in this programming language.
*
* @param languageIndex The language index at the offset in question.
* Since some <code>TokenMaker</code>s effectively have nested
* languages (such as JavaScript in HTML), this parameter tells the
* <code>TokenMaker</code> what sub-language to look at.
* @param languageIndex
* The language index at the offset in question.
* Since some <code>TokenMaker</code>s effectively have nested
* languages (such as JavaScript in HTML), this parameter tells
* the
* <code>TokenMaker</code> what sub-language to look at.
* @return The start and end strings to add to a line to "comment"
* it out. A <code>null</code> value for either means there
* is no string to add for that part. A value of
* it out. A <code>null</code> value for either means there
* is no string to add for that part. A value of
* <code>null</code> for the array means this language
* does not support commenting/uncommenting lines.
*/
public String[] getLineCommentStartAndEnd(int languageIndex);
/**
* Returns an action to handle "insert break" key presses (i.e. Enter).
*
@@ -116,22 +117,21 @@ public interface TokenMaker {
* be used.
*/
public Action getInsertBreakAction();
/**
* Returns whether tokens of the specified type should have "mark
* occurrences" enabled for the current programming language.
*
* @param type The token type.
* @param type
* The token type.
* @return Whether tokens of this type should have "mark occurrences"
* enabled.
*/
public boolean getMarkOccurrencesOfTokenType(int type);
/**
* Returns the object in charge of marking all occurrences of the token
* at the current caret position, if it is a relevant token. If
* at the current caret position, if it is a relevant token. If
* <code>null</code> is returned, a default <code>OccurrenceMarker</code>
* is used.
*
@@ -139,52 +139,51 @@ public interface TokenMaker {
* for none.
*/
public OccurrenceMarker getOccurrenceMarker();
/**
* If a line ends in the specified token, this method returns whether
* a new line inserted after that line should be indented.
*
* @param token The token the previous line ends with.
* @param token
* The token the previous line ends with.
* @return Whether the next line should be indented.
*/
public boolean getShouldIndentNextLineAfter(Token token);
/**
* Returns the first token in the linked list of tokens generated
* from <code>text</code>. This method must be implemented by
* from <code>text</code>. This method must be implemented by
* subclasses so they can correctly implement syntax highlighting.
*
* @param text The text from which to get tokens.
* @param initialTokenType The token type we should start with.
* @param startOffset The offset into the document at which
* <code>text</code> starts.
* @param text
* The text from which to get tokens.
* @param initialTokenType
* The token type we should start with.
* @param startOffset
* The offset into the document at which
* <code>text</code> starts.
* @return The first <code>Token</code> in a linked list representing
* the syntax highlighted text.
*/
public Token getTokenList(Segment text, int initialTokenType,
int startOffset);
public Token getTokenList(Segment text, int initialTokenType, int startOffset);
/**
* Returns whether a character could be part of an "identifier" token
* in a specific language. This is used to identify such things as the
* in a specific language. This is used to identify such things as the
* bounds of the "word" to select on double-clicking.
*
* @param languageIndex The language index the character was found in.
* @param ch The character.
* @param languageIndex
* The language index the character was found in.
* @param ch
* The character.
* @return Whether the character could be part of an "identifier" token.
*/
public boolean isIdentifierChar(int languageIndex, char ch);
/**
* Returns whether this language is a markup language.
*
* @return Whether this language is markup.
*/
public boolean isMarkupLanguage();
}
}
@@ -1,9 +1,7 @@
/*
* 08/26/2004
*
* TokenMakerBase.java - A base class for token makers.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -11,7 +9,6 @@ package org.fife.ui.rsyntaxtextarea;
import javax.swing.Action;
import javax.swing.text.Segment;
/**
* Base class for token makers.
*
@@ -19,44 +16,38 @@ import javax.swing.text.Segment;
* @version 1.0
*/
public abstract class TokenMakerBase implements TokenMaker {
/**
* The first token in the returned linked list.
*/
protected TokenImpl firstToken;
/**
* Used in the creation of the linked list.
*/
protected TokenImpl currentToken;
/**
* Used in the creation of the linked list.
*/
protected TokenImpl previousToken;
/**
* The factory that gives us our tokens to use.
*/
private TokenFactory tokenFactory;
/**
* Highlights occurrences of the current token in the editor, if it is
* relevant.
*/
private OccurrenceMarker occurrenceMarker;
/**
* "0" implies this is the "main" language being highlighted. Positive
* "0" implies this is the "main" language being highlighted. Positive
* values imply various "secondary" or "embedded" languages, such as CSS
* or JavaScript in HTML. While this value is non-zero, tokens will be
* or JavaScript in HTML. While this value is non-zero, tokens will be
* generated with this language index so they can (possibly) be painted
* differently, so "embedded" languages can be rendered with a special
* background.
*/
private int languageIndex;
/**
* Constructor.
*/
@@ -64,18 +55,16 @@ public abstract class TokenMakerBase implements TokenMaker {
firstToken = currentToken = previousToken = null;
tokenFactory = new DefaultTokenFactory();
}
/**
* {@inheritDoc}
*/
@Override
public void addNullToken() {
if (firstToken==null) {
if (firstToken == null) {
firstToken = tokenFactory.createToken();
currentToken = firstToken;
}
else {
} else {
TokenImpl next = tokenFactory.createToken();
currentToken.setNextToken(next);
previousToken = currentToken;
@@ -83,69 +72,67 @@ public abstract class TokenMakerBase implements TokenMaker {
}
currentToken.setLanguageIndex(languageIndex);
}
/**
* Adds the token specified to the current linked list of tokens.
*
* @param segment <code>Segment</code> to get text from.
* @param start Start offset in <code>segment</code> of token.
* @param end End offset in <code>segment</code> of token.
* @param tokenType The token's type.
* @param startOffset The offset in the document at which this token
* occurs.
* @param segment
* <code>Segment</code> to get text from.
* @param start
* Start offset in <code>segment</code> of token.
* @param end
* End offset in <code>segment</code> of token.
* @param tokenType
* The token's type.
* @param startOffset
* The offset in the document at which this token
* occurs.
*/
public void addToken(Segment segment, int start, int end, int tokenType,
int startOffset) {
addToken(segment.array, start,end, tokenType, startOffset);
public void addToken(Segment segment, int start, int end, int tokenType, int startOffset) {
addToken(segment.array, start, end, tokenType, startOffset);
}
/**
* {@inheritDoc}
*/
@Override
public void addToken(char[] array, int start, int end, int tokenType,
int startOffset) {
public void addToken(char[] array, int start, int end, int tokenType, int startOffset) {
addToken(array, start, end, tokenType, startOffset, false);
}
/**
* Adds the token specified to the current linked list of tokens.
*
* @param array The character array.
* @param start The starting offset in the array.
* @param end The ending offset in the array.
* @param tokenType The token's type.
* @param startOffset The offset in the document at which this token
* occurs.
* @param hyperlink Whether this token is a hyperlink.
* @param array
* The character array.
* @param start
* The starting offset in the array.
* @param end
* The ending offset in the array.
* @param tokenType
* The token's type.
* @param startOffset
* The offset in the document at which this token
* occurs.
* @param hyperlink
* Whether this token is a hyperlink.
*/
public void addToken(char[] array, int start, int end, int tokenType,
int startOffset, boolean hyperlink) {
if (firstToken==null) {
firstToken = tokenFactory.createToken(array, start, end,
startOffset, tokenType);
public void addToken(char[] array, int start, int end, int tokenType, int startOffset, boolean hyperlink) {
if (firstToken == null) {
firstToken = tokenFactory.createToken(array, start, end, startOffset, tokenType);
currentToken = firstToken; // previous token is still null.
}
else {
TokenImpl next = tokenFactory.createToken(array, start,end,
startOffset, tokenType);
} else {
TokenImpl next = tokenFactory.createToken(array, start, end, startOffset, tokenType);
currentToken.setNextToken(next);
previousToken = currentToken;
currentToken = next;
}
currentToken.setLanguageIndex(languageIndex);
currentToken.setHyperlink(hyperlink);
}
/**
* Returns the occurrence marker to use for this token maker. Subclasses
* Returns the occurrence marker to use for this token maker. Subclasses
* can override to use different implementations.
*
* @return The occurrence marker to use.
@@ -153,25 +140,25 @@ public abstract class TokenMakerBase implements TokenMaker {
protected OccurrenceMarker createOccurrenceMarker() {
return new DefaultOccurrenceMarker();
}
/**
* Returns the closest {@link TokenTypes "standard" token type} for a given
* "internal" token type (e.g. one whose value is <code>&lt; 0</code>).<p>
* "internal" token type (e.g. one whose value is <code>&lt; 0</code>).
* <p>
*
* The default implementation returns <code>type</code> always, which
* denotes that a mapping from internal token types to standard token types
* is not defined; subclasses can override.
*
* @param type The token type.
* @param type
* The token type.
* @return The closest "standard" token type.
*/
@Override
public int getClosestStandardTokenTypeForInternalType(int type) {
return type;
}
/**
* Returns whether this programming language uses curly braces
* ('<code>{</code>' and '<code>}</code>') to denote code blocks.
@@ -179,21 +166,22 @@ public abstract class TokenMakerBase implements TokenMaker {
* The default implementation returns <code>false</code>; subclasses can
* override this method if necessary.
*
* @param languageIndex The language index at the offset in question.
* Since some <code>TokenMaker</code>s effectively have nested
* languages (such as JavaScript in HTML), this parameter tells the
* <code>TokenMaker</code> what sub-language to look at.
* @param languageIndex
* The language index at the offset in question.
* Since some <code>TokenMaker</code>s effectively have nested
* languages (such as JavaScript in HTML), this parameter tells
* the
* <code>TokenMaker</code> what sub-language to look at.
* @return Whether curly braces denote code blocks.
*/
@Override
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return false;
}
/**
* Returns an action to handle "insert break" key presses (i.e. Enter).
* The default implementation returns <code>null</code>. Subclasses
* The default implementation returns <code>null</code>. Subclasses
* can override.
*
* @return The default implementation always returns <code>null</code>.
@@ -202,8 +190,7 @@ public abstract class TokenMakerBase implements TokenMaker {
public Action getInsertBreakAction() {
return null;
}
/**
* Returns the current language index.
*
@@ -213,26 +200,20 @@ public abstract class TokenMakerBase implements TokenMaker {
protected int getLanguageIndex() {
return languageIndex;
}
/**
* {@inheritDoc}
*/
@Override
public int getLastTokenTypeOnLine(Segment text, int initialTokenType) {
// Last parameter doesn't matter if we're not painting.
Token t = getTokenList(text, initialTokenType, 0);
while (t.getNextToken()!=null) {
while (t.getNextToken() != null) {
t = t.getNextToken();
}
return t.getType();
}
/**
* {@inheritDoc}
*/
@@ -240,61 +221,58 @@ public abstract class TokenMakerBase implements TokenMaker {
public String[] getLineCommentStartAndEnd(int languageIndex) {
return null;
}
/**
* Returns whether tokens of the specified type should have "mark
* occurrences" enabled for the current programming language. The default
* occurrences" enabled for the current programming language. The default
* implementation returns true if <tt>type</tt> is {@link Token#IDENTIFIER}.
* Subclasses can override this method to support other token types, such
* as {@link Token#VARIABLE}.
*
* @param type The token type.
* @param type
* The token type.
* @return Whether tokens of this type should have "mark occurrences"
* enabled.
*/
@Override
public boolean getMarkOccurrencesOfTokenType(int type) {
return type==Token.IDENTIFIER;
return type == Token.IDENTIFIER;
}
/**
* {@inheritDoc}
*/
@Override
public OccurrenceMarker getOccurrenceMarker() {
if (occurrenceMarker==null) {
if (occurrenceMarker == null) {
occurrenceMarker = createOccurrenceMarker();
}
return occurrenceMarker;
}
/**
* The default implementation returns <code>false</code> always. Languages
* The default implementation returns <code>false</code> always. Languages
* that wish to better support auto-indentation can override this method.
*
* @param token The token the previous line ends with.
* @param token
* The token the previous line ends with.
* @return Whether the next line should be indented.
*/
@Override
public boolean getShouldIndentNextLineAfter(Token token) {
return false;
}
/**
* Returns whether a character could be part of an "identifier" token
* in a specific language. The default implementation returns
* in a specific language. The default implementation returns
* <code>true</code> for letters, numbers, and certain symbols.
*/
@Override
public boolean isIdentifierChar(int languageIndex, char ch) {
return Character.isLetterOrDigit(ch) || ch=='_' || ch=='$';
return Character.isLetterOrDigit(ch) || ch == '_' || ch == '$';
}
/**
* The default implementation returns <code>false</code> always.
* Subclasses that are highlighting a markup language should override this
@@ -306,10 +284,9 @@ public abstract class TokenMakerBase implements TokenMaker {
public boolean isMarkupLanguage() {
return false;
}
/**
* Deletes the linked list of tokens so we can begin anew. This should
* Deletes the linked list of tokens so we can begin anew. This should
* never have to be called by the programmer, as it is automatically
* called whenever the user calls
* {@link #getLastTokenTypeOnLine(Segment, int)} or
@@ -319,22 +296,20 @@ public abstract class TokenMakerBase implements TokenMaker {
firstToken = currentToken = previousToken = null;
tokenFactory.resetAllTokens();
}
/**
* Sets the language index to assign to tokens moving forward. This
* Sets the language index to assign to tokens moving forward. This
* property is used to designate tokens as being in "secondary" languages
* (such as CSS or JavaScript in HTML).
*
* @param languageIndex The new language index. A value of
* <code>0</code> denotes the "main" language, any positive value
* denotes a specific secondary language. Negative values will
* be treated as <code>0</code>.
* @param languageIndex
* The new language index. A value of
* <code>0</code> denotes the "main" language, any positive value
* denotes a specific secondary language. Negative values will
* be treated as <code>0</code>.
* @see #getLanguageIndex()
*/
protected void setLanguageIndex(int languageIndex) {
this.languageIndex = Math.max(0, languageIndex);
}
}
}
@@ -1,9 +1,7 @@
/*
* 12/12/2008
*
* TokenMakerFactory.java - A factory for TokenMakers.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -20,18 +18,18 @@ import org.fife.ui.rsyntaxtextarea.modes.PlainTextTokenMaker;
* @version 1.0
*/
public abstract class TokenMakerFactory {
/**
* If this system property is set, a custom <code>TokenMakerFactory</code> of
* If this system property is set, a custom <code>TokenMakerFactory</code>
* of
* the specified class will be used as the default token maker factory.
*/
public static final String PROPERTY_DEFAULT_TOKEN_MAKER_FACTORY = "TokenMakerFactory";
/**
* The singleton default <code>TokenMakerFactory</code> instance.
*/
private static TokenMakerFactory DEFAULT_INSTANCE;
/**
* Returns the default <code>TokenMakerFactory</code> instance. This is the
* factory used by all {@link RSyntaxDocument}s by default.
@@ -61,7 +59,7 @@ public abstract class TokenMakerFactory {
}
return DEFAULT_INSTANCE;
}
/**
* Returns a {@link TokenMaker} for the specified key.
*
@@ -77,26 +75,28 @@ public abstract class TokenMakerFactory {
}
return tm;
}
/**
* Returns a {@link TokenMaker} for the specified key.
*
* @param key
* The key.
* @return The corresponding <code>TokenMaker</code>, or <code>null</code> if
* @return The corresponding <code>TokenMaker</code>, or <code>null</code>
* if
* none matches the specified key.
*/
protected abstract TokenMaker getTokenMakerImpl(String key);
/**
* Returns the set of keys that this factory maps to token makers.
*
* @return The set of keys.
*/
public abstract Set<String> keySet();
/**
* Sets the default <code>TokenMakerFactory</code> instance. This is the factory
* Sets the default <code>TokenMakerFactory</code> instance. This is the
* factory
* used by all future {@link RSyntaxDocument}s by default.
* <code>RSyntaxDocument</code>s that have already been created are not
* affected.
@@ -113,5 +113,4 @@ public abstract class TokenMakerFactory {
}
DEFAULT_INSTANCE = tmf;
}
}
}
+85 -98
View File
@@ -1,27 +1,25 @@
/*
* 08/26/2004
*
* TokenMap.java - Similar to a Map in Java, only designed specifically for
* org.fife.ui.rsyntaxtextarea.Tokens.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.Segment;
/**
* A hash table for reserved words, etc. defined by a {@link TokenMaker}.
* This class is designed for the quick lookup of tokens, as it can compare
* <code>Segment</code>s without the need to allocate a new string.<p>
* <code>Segment</code>s without the need to allocate a new string.
* <p>
*
* The <code>org.fife.ui.rsyntaxtextarea</code> package uses this class to help
* identify reserved words in programming languages. An instance of
* identify reserved words in programming languages. An instance of
* {@link TokenMaker} will create and initialize an instance of this class
* containing all reserved words, data types, and all other words that need to
* be syntax-highlighted for that particular language. When the token maker
* be syntax-highlighted for that particular language. When the token maker
* parses a line and identifies an individual token, it is looked up in the
* <code>TokenMap</code> to see if it should be syntax-highlighted.
*
@@ -29,124 +27,123 @@ import javax.swing.text.Segment;
* @version 0.6
*/
public class TokenMap {
private int size;
private TokenMapToken[] tokenMap;
private boolean ignoreCase;
private static final int DEFAULT_TOKEN_MAP_SIZE = 52;
/**
* Constructs a new token map that is case-sensitive.
*/
public TokenMap() {
this(DEFAULT_TOKEN_MAP_SIZE);
}
/**
* Constructs a new token map that is case-sensitive.
*
* @param size The size of the token map.
* @param size
* The size of the token map.
*/
public TokenMap(int size) {
this(size, false);
}
/**
* Constructs a new token map.
*
* @param ignoreCase Whether or not this token map should ignore case
* when comparing tokens.
* @param ignoreCase
* Whether or not this token map should ignore case
* when comparing tokens.
*/
public TokenMap(boolean ignoreCase) {
this(DEFAULT_TOKEN_MAP_SIZE, ignoreCase);
}
/**
* Constructs a new token map.
*
* @param size The size of the token map.
* @param ignoreCase Whether or not this token map should ignore case
* when comparing tokens.
* @param size
* The size of the token map.
* @param ignoreCase
* Whether or not this token map should ignore case
* when comparing tokens.
*/
public TokenMap(int size, boolean ignoreCase) {
this.size = size;
tokenMap = new TokenMapToken[size];
this.ignoreCase = ignoreCase;
}
/**
* Adds a token to a specified bucket in the token map.
*
* @param bucket The bucket in which to add the token.
* @param token The token to add.
* @param bucket
* The bucket in which to add the token.
* @param token
* The token to add.
*/
private void addTokenToBucket(int bucket, TokenMapToken token) {
TokenMapToken old = tokenMap[bucket];
token.nextToken = old;
tokenMap[bucket] = token;
}
/**
* Returns the token type associated with the given text, if the given
* text is in this token map. If it isn't, <code>-1</code> is returned.
* text is in this token map. If it isn't, <code>-1</code> is returned.
*
* @param text The segment from which to get the text to compare.
* @param start The starting index in the segment of the text.
* @param end The ending index in the segment of the text.
* @param text
* The segment from which to get the text to compare.
* @param start
* The starting index in the segment of the text.
* @param end
* The ending index in the segment of the text.
* @return The token type associated with the given text, or
* <code>-1</code> if this token was not specified in this map.
*/
public int get(Segment text, int start, int end) {
return get(text.array, start, end);
}
/**
* Returns the token type associated with the given text, if the given
* text is in this token map. If it isn't, <code>-1</code> is returned.
* text is in this token map. If it isn't, <code>-1</code> is returned.
*
* @param array1 An array of characters containing the text.
* @param start The starting index in the array of the text.
* @param end The ending index in the array of the text.
* @param array1
* An array of characters containing the text.
* @param start
* The starting index in the array of the text.
* @param end
* The ending index in the array of the text.
* @return The token type associated with the given text, or
* <code>-1</code> if this token was not specified in this map.
*/
public int get(char[] array1, int start, int end) {
int length1 = end - start + 1;
int hash = getHashCode(array1, start, length1);
TokenMapToken token = tokenMap[hash];
char[] array2;
int offset2;
int offset1;
int length;
/* We check whether or not to ignore case before doing any looping to
* minimize the number of extraneous comparisons we do. This makes
/*
* We check whether or not to ignore case before doing any looping to
* minimize the number of extraneous comparisons we do. This makes
* for slightly redundant code, but it'll be a little more efficient.
*/
// If matches are case-sensitive (C, C++, Java, etc.)...
if (!ignoreCase) {
mainLoop:
while (token!=null) {
if (token.length==length1) {
array2 = token.text;
mainLoop:
while (token != null) {
if (token.length == length1) {
array2 = token.text;
offset2 = token.offset;
offset1 = start;
length = length1;
length = length1;
while (length-- > 0) {
if (array1[offset1++]!=array2[offset2++]) {
if (array1[offset1++] != array2[offset2++]) {
token = token.nextToken;
continue mainLoop;
}
@@ -155,24 +152,20 @@ public class TokenMap {
}
token = token.nextToken;
}
}
// If matches are NOT case-sensitive (HTML)...
// Note that all tokens saved in this map were converted to
// lower-case already.
else {
mainLoop2:
while (token!=null) {
if (token.length==length1) {
array2 = token.text;
mainLoop2:
while (token != null) {
if (token.length == length1) {
array2 = token.text;
offset2 = token.offset;
offset1 = start;
length = length1;
length = length1;
while (length-- > 0) {
if (RSyntaxUtilities.toLowerCase(
array1[offset1++]) != array2[offset2++]) {
if (RSyntaxUtilities.toLowerCase(array1[offset1++]) != array2[offset2++]) {
token = token.nextToken;
continue mainLoop2;
}
@@ -181,29 +174,26 @@ public class TokenMap {
}
token = token.nextToken;
}
}
// Didn't match any of the tokens in the bucket.
return -1;
}
/**
* Returns the hash code for a given string.
*
* @param text The text to hash.
* @param offset The offset into the text at which to start hashing.
* @param length The last character in the text to hash.
* @param text
* The text to hash.
* @param offset
* The offset into the text at which to start hashing.
* @param length
* The last character in the text to hash.
* @return The hash code.
*/
private int getHashCode(char[] text, int offset, int length) {
return (RSyntaxUtilities.toLowerCase(text[offset]) +
RSyntaxUtilities.toLowerCase(text[offset+length-1])) % size;
return (RSyntaxUtilities.toLowerCase(text[offset]) + RSyntaxUtilities.toLowerCase(text[offset + length - 1])) % size;
}
/**
* Returns whether this token map ignores case when checking for tokens.
* This property is set in the constructor and cannot be changed, as this
@@ -214,66 +204,63 @@ public class TokenMap {
protected boolean isIgnoringCase() {
return ignoreCase;
}
/**
* Adds a string to this token map.
*
* @param string The string to add.
* @param tokenType The type of token the string is.
* @param string
* The string to add.
* @param tokenType
* The type of token the string is.
*/
public void put(final String string, final int tokenType) {
if (isIgnoringCase()) {
put(string.toLowerCase().toCharArray(), tokenType);
}
else {
} else {
put(string.toCharArray(), tokenType);
}
}
/**
* Adds a string to this token map. The char array passed-in will be used
* Adds a string to this token map. The char array passed-in will be used
* as the actual data for the token, so it may well be modified (such as
* lower-casing it if <code>ignoreCase</code> is <code>true</code>). This
* lower-casing it if <code>ignoreCase</code> is <code>true</code>). This
* shouldn't be an issue though as this method is only called from the
* public <code>put</code> method, which allocates a new char array.
*
* @param string The string to add.
* @param tokenType The type of token the string is.
* @param string
* The string to add.
* @param tokenType
* The type of token the string is.
*/
private void put(char[] string, int tokenType) {
int hashCode = getHashCode(string, 0, string.length);
addTokenToBucket(hashCode, new TokenMapToken(string, tokenType));
}
/**
* The "token" used by a token map. Note that this isn't the same thing
* The "token" used by a token map. Note that this isn't the same thing
* as the {@link Token} class, but it's basically a 1-1 correspondence
* for reserved words, etc.
*/
private static final class TokenMapToken {
private char[] text;
private int offset;
private int length;
private int tokenType;
private TokenMapToken nextToken;
private TokenMapToken(char[] text, int tokenType) {
this.text = text;
this.offset = 0;
this.length = text.length;
this.tokenType = tokenType;
}
@Override
public String toString() {
return "[TokenMapToken: " + new String(text,offset,length) + "]";
return "[TokenMapToken: " + new String(text, offset, length) + "]";
}
}
}
}
@@ -1,19 +1,17 @@
/*
* 08/06/2004
*
* TokenOrientedView.java - An interface for the syntax-highlighting token-
* oriented views for token-oriented methods.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* An interface for the syntax-highlighting token oriented views for
* token-oriented methods. This way callers won't need to know what specific
* class a view is an instance of to access its tokens.<p>
* token-oriented methods. This way callers won't need to know what specific
* class a view is an instance of to access its tokens.
* <p>
*
* Currently, this interface is only useful for obtaining token lists for
* "physical lines" (i.e., a word-wrapped view's logical lines may be
@@ -24,38 +22,36 @@ package org.fife.ui.rsyntaxtextarea;
* @version 0.1
*/
public interface TokenOrientedView {
/**
* Returns a token list for the <i>physical</i> line above the physical
* line containing the specified offset into the document. Note that for
* line containing the specified offset into the document. Note that for
* a plain (non-wrapped) view, this is simply the token list for the
* logical line above the line containing <code>offset</code>, since lines
* are not wrapped. For a wrapped view, this may or may not be tokens from
* are not wrapped. For a wrapped view, this may or may not be tokens from
* the same line.
*
* @param offset The offset in question.
* @param offset
* The offset in question.
* @return A token list for the physical (and in this view, logical) line
* before this one. If no physical line is above the one
* before this one. If no physical line is above the one
* containing <code>offset</code>, <code>null</code> is returned.
*/
Token getTokenListForPhysicalLineAbove(int offset);
/**
* Returns a token list for the <i>physical</i> line below the physical
* line containing the specified offset into the document. Note that for
* line containing the specified offset into the document. Note that for
* a plain (non-wrapped) view, this is simply the token list for the
* logical line below the line containing <code>offset</code>, since lines
* are not wrapped. For a wrapped view, this may or may not be tokens from
* are not wrapped. For a wrapped view, this may or may not be tokens from
* the same line.
*
* @param offset The offset in question.
* @param offset
* The offset in question.
* @return A token list for the physical (and in this view, logical) line
* after this one. If no physical line is after the one
* after this one. If no physical line is after the one
* containing <code>offset</code>, <code>null</code> is returned.
*/
Token getTokenListForPhysicalLineBelow(int offset);
}
}
@@ -1,9 +1,7 @@
/*
* 03/16/2013
*
* TokenPainter - Renders tokens in an instance of RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -12,9 +10,8 @@ import java.awt.Graphics2D;
import javax.swing.text.TabExpander;
/**
* Renders tokens in an instance of {@link RSyntaxTextArea}. One instance
* Renders tokens in an instance of {@link RSyntaxTextArea}. One instance
* may render tokens "regularly," another may render visible whitespace, for
* example.
*
@@ -22,99 +19,121 @@ import javax.swing.text.TabExpander;
* @version 1.0
*/
interface TokenPainter {
/**
* Paints this token.
*
* @param token The token to render.
* @param g The graphics context in which to paint.
* @param x The x-coordinate at which to paint.
* @param y The y-coordinate at which to paint.
* @param host The text area this token is in.
* @param e How to expand tabs.
* @param token
* The token to render.
* @param g
* The graphics context in which to paint.
* @param x
* The x-coordinate at which to paint.
* @param y
* The y-coordinate at which to paint.
* @param host
* The text area this token is in.
* @param e
* How to expand tabs.
* @return The x-coordinate representing the end of the painted text.
*/
float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e);
float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e);
/**
* Paints this token.
*
* @param token The token to render.
* @param g The graphics context in which to paint.
* @param x The x-coordinate at which to paint.
* @param y The y-coordinate at which to paint.
* @param host The text area this token is in.
* @param e How to expand tabs.
* @param clipStart The left boundary of the clip rectangle in which we're
* painting. This optimizes painting by allowing us to not paint
* paint when this token is "to the left" of the clip rectangle.
* @param token
* The token to render.
* @param g
* The graphics context in which to paint.
* @param x
* The x-coordinate at which to paint.
* @param y
* The y-coordinate at which to paint.
* @param host
* The text area this token is in.
* @param e
* How to expand tabs.
* @param clipStart
* The left boundary of the clip rectangle in which we're
* painting. This optimizes painting by allowing us to not paint
* paint when this token is "to the left" of the clip rectangle.
* @return The x-coordinate representing the end of the painted text.
*/
float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart);
float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart);
/**
* Paints this token.
*
* @param token The token to render.
* @param g The graphics context in which to paint.
* @param x The x-coordinate at which to paint.
* @param y The y-coordinate at which to paint.
* @param host The text area this token is in.
* @param e How to expand tabs.
* @param clipStart The left boundary of the clip rectangle in which we're
* painting. This optimizes painting by allowing us to not paint
* paint when this token is "to the left" of the clip rectangle.
* @param paintBG Whether to paint the background.
* @param token
* The token to render.
* @param g
* The graphics context in which to paint.
* @param x
* The x-coordinate at which to paint.
* @param y
* The y-coordinate at which to paint.
* @param host
* The text area this token is in.
* @param e
* How to expand tabs.
* @param clipStart
* The left boundary of the clip rectangle in which we're
* painting. This optimizes painting by allowing us to not paint
* paint when this token is "to the left" of the clip rectangle.
* @param paintBG
* Whether to paint the background.
* @return The x-coordinate representing the end of the painted text.
*/
float paint(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
boolean paintBG);
float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart, boolean paintBG);
/**
* Paints this token as it should appear in a selected region of text
* (assuming painting with a selection-foreground color is enabled in the
* parent <code>RSyntaxTextArea</code>).
*
* @param token The token to render.
* @param g The graphics context in which to paint.
* @param x The x-coordinate at which to paint.
* @param y The y-coordinate at which to paint.
* @param host The text area this token is in.
* @param e How to expand tabs.
* @param useSTC Whether to use the text area's "selected text color."
* @param token
* The token to render.
* @param g
* The graphics context in which to paint.
* @param x
* The x-coordinate at which to paint.
* @param y
* The y-coordinate at which to paint.
* @param host
* The text area this token is in.
* @param e
* How to expand tabs.
* @param useSTC
* Whether to use the text area's "selected text color."
* @return The x-coordinate representing the end of the painted text.
*/
float paintSelected(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, boolean useSTC);
float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, boolean useSTC);
/**
* Paints this token as it should appear in a selected region of text
* (assuming painting with a selection-foreground color is enabled in the
* parent <code>RSyntaxTextArea</code>).
*
* @param token The token to render.
* @param g The graphics context in which to paint.
* @param x The x-coordinate at which to paint.
* @param y The y-coordinate at which to paint.
* @param host The text area this token is in.
* @param e How to expand tabs.
* @param clipStart The left boundary of the clip rectangle in which we're
* painting. This optimizes painting by allowing us to not paint
* paint when this token is "to the left" of the clip rectangle.
* @param useSTC Whether to use the text area's "selected text color."
* @param token
* The token to render.
* @param g
* The graphics context in which to paint.
* @param x
* The x-coordinate at which to paint.
* @param y
* The y-coordinate at which to paint.
* @param host
* The text area this token is in.
* @param e
* How to expand tabs.
* @param clipStart
* The left boundary of the clip rectangle in which we're
* painting. This optimizes painting by allowing us to not paint
* paint when this token is "to the left" of the clip rectangle.
* @param useSTC
* Whether to use the text area's "selected text color."
* @return The x-coordinate representing the end of the painted text.
*/
float paintSelected(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
boolean useSTC);
}
float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart, boolean useSTC);
}
+44 -63
View File
@@ -1,16 +1,14 @@
/*
* 12/04/2011
*
* TokenTypes.java - All token types supported by RSyntaxTextArea.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
/**
* All token types supported by RSyntaxTextArea.<p>
* All token types supported by RSyntaxTextArea.
* <p>
*
* Note that all valid token types are &gt;= 0, so extensions of the TokenMaker
* class are free to internally use all ints &lt; 0 ONLY for "end-of-line"
@@ -20,66 +18,49 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0
*/
public interface TokenTypes {
/**
* Tokens of type <code>NULL</code> mark the end of lines with no
* multi-line token at the end (such as a block comment in C++).
*/
int NULL = 0;
int COMMENT_EOL = 1;
int COMMENT_MULTILINE = 2;
int COMMENT_DOCUMENTATION = 3;
int COMMENT_KEYWORD = 4;
int COMMENT_MARKUP = 5;
int RESERVED_WORD = 6;
int RESERVED_WORD_2 = 7;
int FUNCTION = 8;
int LITERAL_BOOLEAN = 9;
int LITERAL_NUMBER_DECIMAL_INT = 10;
int LITERAL_NUMBER_FLOAT = 11;
int LITERAL_NUMBER_HEXADECIMAL = 12;
int LITERAL_STRING_DOUBLE_QUOTE = 13;
int LITERAL_CHAR = 14;
int LITERAL_BACKQUOTE = 15;
int DATA_TYPE = 16;
int VARIABLE = 17;
int REGEX = 18;
int ANNOTATION = 19;
int IDENTIFIER = 20;
int WHITESPACE = 21;
int SEPARATOR = 22;
int OPERATOR = 23;
int PREPROCESSOR = 24;
int MARKUP_TAG_DELIMITER = 25;
int MARKUP_TAG_NAME = 26;
int MARKUP_TAG_ATTRIBUTE = 27;
int MARKUP_TAG_ATTRIBUTE_VALUE = 28;
int MARKUP_COMMENT = 29;
int MARKUP_DTD = 30;
int MARKUP_PROCESSING_INSTRUCTION = 31;
int MARKUP_CDATA_DELIMITER = 32;
int MARKUP_CDATA = 33;
int MARKUP_ENTITY_REFERENCE = 34;
int ERROR_IDENTIFIER = 35;
int ERROR_NUMBER_FORMAT = 36;
int ERROR_STRING_DOUBLE = 37;
int ERROR_CHAR = 38;
int NULL = 0;
int COMMENT_EOL = 1;
int COMMENT_MULTILINE = 2;
int COMMENT_DOCUMENTATION = 3;
int COMMENT_KEYWORD = 4;
int COMMENT_MARKUP = 5;
int RESERVED_WORD = 6;
int RESERVED_WORD_2 = 7;
int FUNCTION = 8;
int LITERAL_BOOLEAN = 9;
int LITERAL_NUMBER_DECIMAL_INT = 10;
int LITERAL_NUMBER_FLOAT = 11;
int LITERAL_NUMBER_HEXADECIMAL = 12;
int LITERAL_STRING_DOUBLE_QUOTE = 13;
int LITERAL_CHAR = 14;
int LITERAL_BACKQUOTE = 15;
int DATA_TYPE = 16;
int VARIABLE = 17;
int REGEX = 18;
int ANNOTATION = 19;
int IDENTIFIER = 20;
int WHITESPACE = 21;
int SEPARATOR = 22;
int OPERATOR = 23;
int PREPROCESSOR = 24;
int MARKUP_TAG_DELIMITER = 25;
int MARKUP_TAG_NAME = 26;
int MARKUP_TAG_ATTRIBUTE = 27;
int MARKUP_TAG_ATTRIBUTE_VALUE = 28;
int MARKUP_COMMENT = 29;
int MARKUP_DTD = 30;
int MARKUP_PROCESSING_INSTRUCTION = 31;
int MARKUP_CDATA_DELIMITER = 32;
int MARKUP_CDATA = 33;
int MARKUP_ENTITY_REFERENCE = 34;
int ERROR_IDENTIFIER = 35;
int ERROR_NUMBER_FORMAT = 36;
int ERROR_STRING_DOUBLE = 37;
int ERROR_CHAR = 38;
int DEFAULT_NUM_TOKEN_TYPES = 39;
}
}
+67 -70
View File
@@ -2,7 +2,6 @@ package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.TabExpander;
/**
* Utility methods for dealing with tokens.
*
@@ -10,161 +9,159 @@ import javax.swing.text.TabExpander;
* @version 1.0
*/
public final class TokenUtils {
private TokenUtils() {}
/**
* Modifies the passed-in token list to start at the specified offset.
* For example, if the token list covered positions 20-60 in the document
* (inclusive) like so:
*
* <pre>
* [token1] -&gt; [token2] -&gt; [token3] -&gt; [token4]
* 20 30 31 40 41 50 51 60
* </pre>
*
* and you used this method to make the token list start at position 44,
* then the token list would be modified to be the following:
*
* <pre>
* [part-of-old-token3] -&gt; [token4]
* 44 50 51 60
* </pre>
*
* Tokens that come before the specified position are forever lost, and
* the token containing that position is made to begin at that position if
* necessary. All token types remain the same as they were originally.<p>
* necessary. All token types remain the same as they were originally.
* <p>
*
* This method can be useful if you are only interested in part of a token
* list (i.e., the line it represents), but you don't want to modify the
* token list yourself.
*
* @param tokenList The list to make start at the specified position.
* This parameter is modified.
* @param pos The position at which the new token list is to start. If
* this position is not in the passed-in token list,
* returned token list will either be <code>null</code> or the
* unpaintable token(s) at the end of the passed-in token list.
* @param e How to expand tabs.
* @param textArea The text area from which the token list came.
* @param x0 The initial x-pixel position of the old token list.
* @return Information about the "sub" token list. This will be
* @param tokenList
* The list to make start at the specified position.
* This parameter is modified.
* @param pos
* The position at which the new token list is to start. If
* this position is not in the passed-in token list,
* returned token list will either be <code>null</code> or the
* unpaintable token(s) at the end of the passed-in token list.
* @param e
* How to expand tabs.
* @param textArea
* The text area from which the token list came.
* @param x0
* The initial x-pixel position of the old token list.
* @return Information about the "sub" token list. This will be
* <code>null</code> if <code>pos</code> was not a valid offset
* into the token list.
* @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float, TokenImpl)
* @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float,
* TokenImpl)
*/
public static TokenSubList getSubTokenList(Token tokenList, int pos,
TabExpander e,
final RSyntaxTextArea textArea,
float x0) {
public static TokenSubList getSubTokenList(Token tokenList, int pos, TabExpander e, final RSyntaxTextArea textArea, float x0) {
return getSubTokenList(tokenList, pos, e, textArea, x0, null);
}
/**
* Modifies the passed-in token list to start at the specified offset.
* For example, if the token list covered positions 20-60 in the document
* (inclusive) like so:
*
* <pre>
* [token1] -&gt; [token2] -&gt; [token3] -&gt; [token4]
* 20 30 31 40 41 50 51 60
* </pre>
*
* and you used this method to make the token list start at position 44,
* then the token list would be modified to be the following:
*
* <pre>
* [part-of-old-token3] -&gt; [token4]
* 44 50 51 60
* </pre>
*
* Tokens that come before the specified position are forever lost, and
* the token containing that position is made to begin at that position if
* necessary. All token types remain the same as they were originally.<p>
* necessary. All token types remain the same as they were originally.
* <p>
*
* This method can be useful if you are only interested in part of a token
* list (i.e., the line it represents), but you don't want to modify the
* token list yourself.
*
* @param tokenList The list to make start at the specified position.
* This parameter is modified.
* @param pos The position at which the new token list is to start. If
* this position is not in the passed-in token list,
* returned token list will either be <code>null</code> or the
* unpaintable token(s) at the end of the passed-in token list.
* @param e How to expand tabs.
* @param textArea The text area from which the token list came.
* @param x0 The initial x-pixel position of the old token list.
* @param tempToken A temporary token to use when creating the token list
* result. This may be <code>null</code> but callers can pass in
* a "buffer" token for performance if desired.
* @return Information about the "sub" token list. This will be
* @param tokenList
* The list to make start at the specified position.
* This parameter is modified.
* @param pos
* The position at which the new token list is to start. If
* this position is not in the passed-in token list,
* returned token list will either be <code>null</code> or the
* unpaintable token(s) at the end of the passed-in token list.
* @param e
* How to expand tabs.
* @param textArea
* The text area from which the token list came.
* @param x0
* The initial x-pixel position of the old token list.
* @param tempToken
* A temporary token to use when creating the token list
* result. This may be <code>null</code> but callers can pass in
* a "buffer" token for performance if desired.
* @return Information about the "sub" token list. This will be
* <code>null</code> if <code>pos</code> was not a valid offset
* into the token list.
* @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float)
*/
public static TokenSubList getSubTokenList(Token tokenList, int pos,
TabExpander e,
final RSyntaxTextArea textArea,
float x0,
TokenImpl tempToken) {
if (tempToken==null) {
public static TokenSubList getSubTokenList(Token tokenList, int pos, TabExpander e, final RSyntaxTextArea textArea, float x0,
TokenImpl tempToken) {
if (tempToken == null) {
tempToken = new TokenImpl();
}
Token t = tokenList;
// Loop through the token list until you find the one that contains
// pos. Remember the cumulative width of all of these tokens.
while (t!=null && t.isPaintable() && !t.containsPosition(pos)) {
// pos. Remember the cumulative width of all of these tokens.
while (t != null && t.isPaintable() && !t.containsPosition(pos)) {
x0 += t.getWidth(textArea, e, x0);
t = t.getNextToken();
}
// Make the token that contains pos start at pos.
if (t!=null && t.isPaintable()) {
if (t.getOffset()!=pos) {
if (t != null && t.isPaintable()) {
if (t.getOffset() != pos) {
// Number of chars between p0 and token start.
int difference = pos - t.getOffset();
x0 += t.getWidthUpTo(t.length()-difference+1, textArea, e, x0);
x0 += t.getWidthUpTo(t.length() - difference + 1, textArea, e, x0);
tempToken.copyFrom(t);
tempToken.makeStartAt(pos);
return new TokenSubList(tempToken, x0);
}
else { // t.getOffset()==pos
} else { // t.getOffset()==pos
return new TokenSubList(t, x0);
}
}
// This could be a null token, so we need to just return it.
return new TokenSubList(tokenList, x0);
//return null;
// return null;
}
/**
* A sub-list of tokens.
*/
@SuppressWarnings({ "checkstyle:visibilitymodifier" })
public static class TokenSubList {
/**
* The "sub" token list.
*/
public Token tokenList;
/**
* The width, in pixels, of the part of the token list "removed from
* the front." This way, you know the x-offset of the "new" token list.
* the front." This way, you know the x-offset of the "new" token list.
*/
public float x;
public TokenSubList(Token tokenList, float x) {
this.tokenList = tokenList;
this.x = x;
}
}
}
}
@@ -1,19 +1,16 @@
/*
* 11/13/2008
*
* URLFileLocation.java - The location of a file at a (remote) URL.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
/**
* The location of a file at a (remote) URL.
*
@@ -21,38 +18,35 @@ import java.net.URL;
* @version 1.0
*/
class URLFileLocation extends FileLocation {
/**
* URL of the remote file.
*/
private URL url;
/**
* A prettied-up full path of the URL (password removed, etc.).
*/
private String fileFullPath;
/**
* A prettied-up filename (leading slash, and possibly "<code>%2F</code>",
* removed).
*/
private String fileName;
/**
* Constructor.
*
* @param url The URL of the file.
* @param url
* The URL of the file.
*/
URLFileLocation(URL url) {
this.url = url;
fileFullPath = createFileFullPath();
fileName = createFileName();
}
/**
* Creates a "prettied-up" URL to use. This will be stripped of
* Creates a "prettied-up" URL to use. This will be stripped of
* sensitive information such as passwords.
*
* @return The full path to use.
@@ -62,8 +56,7 @@ class URLFileLocation extends FileLocation {
fullPath = fullPath.replaceFirst("://([^:]+)(?:.+)@", "://$1@");
return fullPath;
}
/**
* Creates the "prettied-up" filename to use.
*
@@ -73,28 +66,25 @@ class URLFileLocation extends FileLocation {
String fileName = url.getPath();
if (fileName.startsWith("/%2F/")) { // Absolute path
fileName = fileName.substring(4);
}
else if (fileName.startsWith("/")) { // All others
} else if (fileName.startsWith("/")) { // All others
fileName = fileName.substring(1);
}
return fileName;
}
/**
* Returns the last time this file was modified, or
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be
* computed (such as for a remote file).
*
* @return The last time this file was modified. This will always be
* @return The last time this file was modified. This will always be
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} for URL's.
*/
@Override
protected long getActualLastModified() {
return TextEditorPane.LAST_MODIFIED_UNKNOWN;
}
/**
* {@inheritDoc}
*/
@@ -102,8 +92,7 @@ class URLFileLocation extends FileLocation {
public String getFileFullPath() {
return fileFullPath;
}
/**
* {@inheritDoc}
*/
@@ -111,8 +100,7 @@ class URLFileLocation extends FileLocation {
public String getFileName() {
return fileName;
}
/**
* {@inheritDoc}
*/
@@ -120,8 +108,7 @@ class URLFileLocation extends FileLocation {
protected InputStream getInputStream() throws IOException {
return url.openStream();
}
/**
* {@inheritDoc}
*/
@@ -129,8 +116,7 @@ class URLFileLocation extends FileLocation {
protected OutputStream getOutputStream() throws IOException {
return url.openConnection().getOutputStream();
}
/**
* Returns whether this file location is a local file.
*
@@ -141,11 +127,10 @@ class URLFileLocation extends FileLocation {
public boolean isLocal() {
return "file".equalsIgnoreCase(url.getProtocol());
}
/**
* Returns whether this file location is a local file and already
* exists. This method always returns <code>false</code> since we
* exists. This method always returns <code>false</code> since we
* cannot check this value easily.
*
* @return <code>false</code> always.
@@ -155,6 +140,4 @@ class URLFileLocation extends FileLocation {
public boolean isLocalAndExists() {
return false;
}
}
}
@@ -1,10 +1,8 @@
/*
* 03/16/2013
*
* VisibleWhitespaceTokenPainter - Renders tokens in an instance of
* RSyntaxTextArea, with special glyphs to denote spaces and tabs.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -12,174 +10,146 @@ package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import javax.swing.text.TabExpander;
/**
* A token painter that visibly renders whitespace (spaces and tabs).<p>
* A token painter that visibly renders whitespace (spaces and tabs).
* <p>
*
* The current implementation paints as follows:
* <ul>
* <li>The first tab or space, if any, is found in the token.</li>
* <li>If a tab was found, all characters up to it are painted as a
* group.</li>
* <li>If a space was found, all characters up to and including it are
* painted (it is painted with a special symbol to denote it as
* a space).</li>
* <li>If neither a tab nor a whitespace was found, all characters in the
* token are painted.</li>
* <li>Repeat until all characters are painted.</li>
* <li>The first tab or space, if any, is found in the token.</li>
* <li>If a tab was found, all characters up to it are painted as a
* group.</li>
* <li>If a space was found, all characters up to and including it are
* painted (it is painted with a special symbol to denote it as
* a space).</li>
* <li>If neither a tab nor a whitespace was found, all characters in the
* token are painted.</li>
* <li>Repeat until all characters are painted.</li>
* </ul>
* This means that rendering hints are applied to all groups of characters
* within a token, excluding whitespace and tabs.<p>
* within a token, excluding whitespace and tabs.
* <p>
*
* A problem with this implementation is that FontMetrics.charsWidth() is still
* used to calculate the width of a group of chars painted. Thus, the group of
* used to calculate the width of a group of chars painted. Thus, the group of
* characters will be painted with the rendering hints specified, but the
* following tab (or group of characters if the current group was the end of a
* token) will not necessarily be painted at the proper x-coordinate (as
* FontMetrics.charsWidth() returns an <code>int</code> and not a
* <code>float</code>). The way around this would be to calculate the token's
* <code>float</code>). The way around this would be to calculate the token's
* width in such a way that a float is returned (Font.getStringBounds()?).
*
* @author Robert Futrell
* @version 1.0
*/
class VisibleWhitespaceTokenPainter extends DefaultTokenPainter {
/**
* {@inheritDoc}
*/
@Override
protected float paintImpl(Token token, Graphics2D g, float x, float y,
RSyntaxTextArea host, TabExpander e, float clipStart,
protected float paintImpl(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart,
boolean selected, boolean useSTC) {
int origX = (int)x;
int origX = (int) x;
int textOffs = token.getTextOffset();
char[] text = token.getTextArray();
int end = textOffs + token.length();
float nextX = x;
int flushLen = 0;
int flushIndex = textOffs;
Color fg = useSTC ? host.getSelectedTextColor() :
host.getForegroundForToken(token);
Color fg = useSTC ? host.getSelectedTextColor() : host.getForegroundForToken(token);
Color bg = selected ? null : host.getBackgroundForToken(token);
g.setFont(host.getFontForTokenType(token.getType()));
FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
int ascent = fm.getAscent();
int height = fm.getHeight();
for (int i=textOffs; i<end; i++) {
for (int i = textOffs; i < end; i++) {
switch (text[i]) {
case '\t':
// Fill in background.
nextX = x+fm.charsWidth(text, flushIndex,flushLen);
nextX = x + fm.charsWidth(text, flushIndex, flushLen);
float nextNextX = e.nextTabStop(nextX, 0);
if (bg!=null) {
paintBackground(x,y, nextNextX-x,height, g,
ascent, host, bg);
if (bg != null) {
paintBackground(x, y, nextNextX - x, height, g, ascent, host, bg);
}
g.setColor(fg);
// Paint chars cached before the tab.
if (flushLen > 0) {
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
flushLen = 0;
}
flushIndex = i + 1;
// Draw an arrow representing the tab.
int halfHeight = height / 2;
int quarterHeight = halfHeight / 2;
int ymid = (int)y - ascent + halfHeight;
g.drawLine((int)nextX,ymid, (int)nextNextX,ymid);
g.drawLine((int)nextNextX,ymid, (int)nextNextX-4,ymid-quarterHeight);
g.drawLine((int)nextNextX,ymid, (int)nextNextX-4,ymid+quarterHeight);
int ymid = (int) y - ascent + halfHeight;
g.drawLine((int) nextX, ymid, (int) nextNextX, ymid);
g.drawLine((int) nextNextX, ymid, (int) nextNextX - 4, ymid - quarterHeight);
g.drawLine((int) nextNextX, ymid, (int) nextNextX - 4, ymid + quarterHeight);
x = nextNextX;
break;
case ' ':
// NOTE: There is a little bit of a "fudge factor"
// NOTE: There is a little bit of a "fudge factor"
// here when "smooth text" is enabled, as "width"
// below may well not be the width given to the space
// by fm.charsWidth() (it depends on how it places the
// space with respect to the preceding character).
// But, we assume the approximation is close enough for
// our drawing a dot for the space.
// "flushLen+1" ensures text is aligned correctly (or,
// aligned the same as in getWidth()).
nextX = x+fm.charsWidth(text, flushIndex,flushLen+1);
nextX = x + fm.charsWidth(text, flushIndex, flushLen + 1);
int width = fm.charWidth(' ');
// Paint background.
if (bg!=null) {
paintBackground(x,y, nextX-x,height, g,
ascent, host, bg);
if (bg != null) {
paintBackground(x, y, nextX - x, height, g, ascent, host, bg);
}
g.setColor(fg);
// Paint chars before space.
if (flushLen>0) {
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
if (flushLen > 0) {
g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
flushLen = 0;
}
// Paint a dot representing the space.
int dotX = (int)(nextX - width/2f); // "2.0f" for FindBugs
int dotY = (int)(y - ascent + height/2f); // Ditto
int dotX = (int) (nextX - width / 2f); // "2.0f" for
// FindBugs
int dotY = (int) (y - ascent + height / 2f); // Ditto
g.drawLine(dotX, dotY, dotX, dotY);
flushIndex = i + 1;
x = nextX;
break;
case '\f':
// ???
// fall-through for now.
default:
flushLen += 1;
break;
}
}
nextX = x+fm.charsWidth(text, flushIndex,flushLen);
if (flushLen>0 && nextX>=clipStart) {
if (bg!=null) {
paintBackground(x,y, nextX-x,height, g,
ascent, host, bg);
nextX = x + fm.charsWidth(text, flushIndex, flushLen);
if (flushLen > 0 && nextX >= clipStart) {
if (bg != null) {
paintBackground(x, y, nextX - x, height, g, ascent, host, bg);
}
g.setColor(fg);
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
}
if (host.getUnderlineForToken(token)) {
g.setColor(fg);
int y2 = (int)(y+1);
g.drawLine(origX,y2, (int)nextX,y2);
int y2 = (int) (y + 1);
g.drawLine(origX, y2, (int) nextX, y2);
}
// Don't check if it's whitespace - some TokenMakers may return types
// other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER).
// This also allows us to paint tab lines for MLC's.
if (host.getPaintTabLines() && origX==host.getMargin().left) {// && isWhitespace()) {
paintTabLines(token, origX, (int)y, (int)nextX, g, e, host);
if (host.getPaintTabLines() && origX == host.getMargin().left) {// &&
// isWhitespace())
// {
paintTabLines(token, origX, (int) y, (int) nextX, g, e, host);
}
return nextX;
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,157 +1,157 @@
package org.fife.ui.rsyntaxtextarea;
//import java.awt.Graphics;
//import java.awt.Rectangle;
//import java.awt.Shape;
// import java.awt.Graphics;
// import java.awt.Rectangle;
// import java.awt.Shape;
//import javax.swing.text.CompositeView;
// import javax.swing.text.CompositeView;
import javax.swing.text.Element;
//import javax.swing.text.View;
// import javax.swing.text.View;
/**
* Replacement for the old <code>WrappedSyntaxView</code> class, designed to
* be faster with large wrapped documents. Heavily based off of
* be faster with large wrapped documents. Heavily based off of
* <code>BoxView</code>, but streamlined to only care about the y-axis, and
* takes code folding into account.<p>
* takes code folding into account.
* <p>
*
* This class is not currently used.
*
* @author Robert Futrell
* @version 1.0
*/
public class WrappedSyntaxView2 {//extends CompositeView {
// private Rectangle tempRect;
// private int[] cachedOffsets;
// private int[] cachedSpans;
// private boolean sizeRequirementsValid;
public class WrappedSyntaxView2 {// extends CompositeView {
// private Rectangle tempRect;
// private int[] cachedOffsets;
// private int[] cachedSpans;
// private boolean sizeRequirementsValid;
public WrappedSyntaxView2(Element root) {
//super(root);
// tempRect = new Rectangle();
// cachedOffsets = new int[0];
// cachedSpans = new int[0];
// sizeRequirementsValid = false;
// super(root);
// tempRect = new Rectangle();
// cachedOffsets = new int[0];
// cachedSpans = new int[0];
// sizeRequirementsValid = false;
}
// protected void childAllocation(int index, Rectangle alloc) {
// alloc.y += getOffset(index);
// alloc.height = getHeight(index);
// }
//
//
// private int getHeight(int childIndex) {
// return cachedSpans[childIndex];
// }
//
//
// private int getOffset(int childIndex) {
// return cachedOffsets[childIndex];
// }
//
//
// protected View getViewAtPoint(int x, int y, Rectangle alloc) {
// // TODO Auto-generated method stub
// return null;
// }
//
//
// /**
// * @param alloc The allocated region; this is the area inside of the insets
// * @return Whether the point lies after the region.
// */
// protected boolean isAfter(int x, int y, Rectangle alloc) {
// return y > (alloc.y + alloc.height);
// }
//
//
// /**
// * @param alloc The allocated region; this is the area inside of the insets
// * @return Whether the point lies before the region.
// */
// protected boolean isBefore(int x, int y, Rectangle alloc) {
// return y < alloc.y;
// }
//
//
// public float getPreferredSpan(int axis) {
// if (axis==X_AXIS) {
// return preferredWidth + getLeftInset() + getRightInset();
// }
// else {
// return preferredHeight + getTopInset() + getBottomInset();
// }
// }
//
//
// public void paint(Graphics g, Shape allocation) {
//
// Rectangle alloc = (allocation instanceof Rectangle) ?
// (Rectangle)allocation : allocation.getBounds();
// int n = getViewCount();
//
// int x = alloc.x + getLeftInset();
// int y = alloc.y + getTopInset();
// Rectangle clip = g.getClipBounds();
// int preferredWidth = (int)getPreferredSpan(X_AXIS);
//
// for (int i = 0; i < n; i++) {
// tempRect.x = x;
// tempRect.y = y + getOffset(i);
// tempRect.width = preferredWidth;
// tempRect.height = getHeight(i);
// if (tempRect.intersects(clip)) {
// paintChild(g, tempRect, i);
// }
// }
//
// }
//
//
// /**
// * Called when a child view's preferred span changes. This invalidates
// * our layout cache and calls the super implementation.
// */
// public void preferenceChanged(View child, boolean widthPreferenceChanged,
// boolean heightPreferenceChanged) {
//
// if (heightPreferenceChanged) {
// sizeRequirementsValid = false;
//// majorAllocValid = false;
// }
//// if (width) {
//// minorReqValid = false;
//// minorAllocValid = false;
//// }
//
// super.preferenceChanged(child, widthPreferenceChanged, heightPreferenceChanged);
//
// }
//
// public void replace(int index, int length, View[] elems) {
//
// super.replace(index, length, elems);
//
// // Invalidate cache
// int insertCount = elems==null ? 0 : elems.length;
// cachedOffsets = updateLayoutArray(cachedOffsets, index, insertCount);
// majorReqValid = false;
// majorAllocValid = false;
//
// }
//
//
// private int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
// int n = getViewCount(); // Called after super.replace() so this is accurate
// int[] newArray = new int[n];
// System.arraycopy(oldArray, 0, newArray, 0, offset);
// System.arraycopy(oldArray, offset,
// newArray, offset + nInserted, n - nInserted - offset);
// return newArray;
// }
//
}
// protected void childAllocation(int index, Rectangle alloc) {
// alloc.y += getOffset(index);
// alloc.height = getHeight(index);
// }
//
//
// private int getHeight(int childIndex) {
// return cachedSpans[childIndex];
// }
//
//
// private int getOffset(int childIndex) {
// return cachedOffsets[childIndex];
// }
//
//
// protected View getViewAtPoint(int x, int y, Rectangle alloc) {
// // TODO Auto-generated method stub
// return null;
// }
//
//
// /**
// * @param alloc The allocated region; this is the area inside of the
// insets
// * @return Whether the point lies after the region.
// */
// protected boolean isAfter(int x, int y, Rectangle alloc) {
// return y > (alloc.y + alloc.height);
// }
//
//
// /**
// * @param alloc The allocated region; this is the area inside of the
// insets
// * @return Whether the point lies before the region.
// */
// protected boolean isBefore(int x, int y, Rectangle alloc) {
// return y < alloc.y;
// }
//
//
// public float getPreferredSpan(int axis) {
// if (axis==X_AXIS) {
// return preferredWidth + getLeftInset() + getRightInset();
// }
// else {
// return preferredHeight + getTopInset() + getBottomInset();
// }
// }
//
//
// public void paint(Graphics g, Shape allocation) {
//
// Rectangle alloc = (allocation instanceof Rectangle) ?
// (Rectangle)allocation : allocation.getBounds();
// int n = getViewCount();
//
// int x = alloc.x + getLeftInset();
// int y = alloc.y + getTopInset();
// Rectangle clip = g.getClipBounds();
// int preferredWidth = (int)getPreferredSpan(X_AXIS);
//
// for (int i = 0; i < n; i++) {
// tempRect.x = x;
// tempRect.y = y + getOffset(i);
// tempRect.width = preferredWidth;
// tempRect.height = getHeight(i);
// if (tempRect.intersects(clip)) {
// paintChild(g, tempRect, i);
// }
// }
//
// }
//
//
// /**
// * Called when a child view's preferred span changes. This invalidates
// * our layout cache and calls the super implementation.
// */
// public void preferenceChanged(View child, boolean widthPreferenceChanged,
// boolean heightPreferenceChanged) {
//
// if (heightPreferenceChanged) {
// sizeRequirementsValid = false;
//// majorAllocValid = false;
// }
//// if (width) {
//// minorReqValid = false;
//// minorAllocValid = false;
//// }
//
// super.preferenceChanged(child, widthPreferenceChanged,
// heightPreferenceChanged);
//
// }
//
// public void replace(int index, int length, View[] elems) {
//
// super.replace(index, length, elems);
//
// // Invalidate cache
// int insertCount = elems==null ? 0 : elems.length;
// cachedOffsets = updateLayoutArray(cachedOffsets, index, insertCount);
// majorReqValid = false;
// majorAllocValid = false;
//
// }
//
//
// private int[] updateLayoutArray(int[] oldArray, int offset, int
// nInserted) {
// int n = getViewCount(); // Called after super.replace() so this is
// accurate
// int[] newArray = new int[n];
// System.arraycopy(oldArray, 0, newArray, 0, offset);
// System.arraycopy(oldArray, offset,
// newArray, offset + nInserted, n - nInserted - offset);
// return newArray;
// }
//
}
@@ -1,9 +1,7 @@
/*
* 03/09/2013
*
* XmlOccurrenceMarker - Marks occurrences of the current token for XML.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -16,7 +14,6 @@ import javax.swing.text.Element;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks occurrences of the current token for XML.
*
@@ -24,21 +21,18 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
* @version 1.0
*/
public class XmlOccurrenceMarker implements OccurrenceMarker {
private static final char[] CLOSE_TAG_START = { '<', '/' };
private static final char[] TAG_SELF_CLOSE = { '/', '>' };
/**
* {@inheritDoc}
*/
@Override
public Token getTokenToMark(RSyntaxTextArea textArea) {
return HtmlOccurrenceMarker.getTagNameTokenForCaretOffset(
textArea, this);
return HtmlOccurrenceMarker.getTagNameTokenForCaretOffset(textArea, this);
}
/**
* {@inheritDoc}
*/
@@ -46,34 +40,29 @@ public class XmlOccurrenceMarker implements OccurrenceMarker {
public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType());
}
/**
* {@inheritDoc}
*/
@Override
public void markOccurrences(RSyntaxDocument doc, Token t,
RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
public void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
char[] lexeme = t.getLexeme().toCharArray();
int tokenOffs = t.getOffset();
Element root = doc.getDefaultRootElement();
int lineCount = root.getElementCount();
int curLine = root.getElementIndex(t.getOffset());
int depth = 0;
// For now, we only check for tags on the current line, for
// simplicity. Tags spanning multiple lines aren't common anyway.
// simplicity. Tags spanning multiple lines aren't common anyway.
boolean found = false;
boolean forward = true;
t = doc.getTokenListForLine(curLine);
while (t!=null && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('<') && t.getOffset()+1==tokenOffs) {
while (t != null && t.isPaintable()) {
if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('<') && t.getOffset() + 1 == tokenOffs) {
found = true;
break;
}
else if (t.is(CLOSE_TAG_START) && t.getOffset()+2==tokenOffs) {
} else if (t.is(CLOSE_TAG_START) && t.getOffset() + 2 == tokenOffs) {
found = true;
forward = false;
break;
@@ -81,26 +70,20 @@ public class XmlOccurrenceMarker implements OccurrenceMarker {
}
t = t.getNextToken();
}
if (!found) {
return;
}
if (forward) {
t = t.getNextToken().getNextToken();
do {
while (t!=null && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
while (t != null && t.isPaintable()) {
if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
if (t.is(CLOSE_TAG_START)) {
Token match = t.getNextToken();
if (match!=null && match.is(lexeme)) {
if (depth>0) {
if (match != null && match.is(lexeme)) {
if (depth > 0) {
depth--;
}
else {
} else {
try {
int end = match.getOffset() + match.length();
h.addMarkedOccurrenceHighlight(match.getOffset(), end, p);
@@ -112,65 +95,50 @@ public class XmlOccurrenceMarker implements OccurrenceMarker {
return; // We're done!
}
}
}
else if (t.isSingleChar('<')) {
} else if (t.isSingleChar('<')) {
t = t.getNextToken();
if (t!=null && t.is(lexeme)) {
if (t != null && t.is(lexeme)) {
depth++;
}
}
}
t = t==null ? null : t.getNextToken();
t = t == null ? null : t.getNextToken();
}
if (++curLine<lineCount) {
if (++curLine < lineCount) {
t = doc.getTokenListForLine(curLine);
}
} while (curLine<lineCount);
}
else { // !forward
} while (curLine < lineCount);
} else { // !forward
// Idea: Get all opening and closing tags of the relevant type on
// the current line. Find the opening tag paired to the closing
// the current line. Find the opening tag paired to the closing
// tag we found originally; if it's not on this line, keep going
// to the previous line.
List<Entry> openCloses = new ArrayList<Entry>();
boolean inPossibleMatch = false;
t = doc.getTokenListForLine(curLine);
final int endBefore = tokenOffs - 2; // Stop before "</".
do {
while (t!=null && t.getOffset()<endBefore && t.isPaintable()) {
if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
while (t != null && t.getOffset() < endBefore && t.isPaintable()) {
if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
if (t.isSingleChar('<')) {
Token next = t.getNextToken();
if (next!=null) {
if (next != null) {
if (next.is(lexeme)) {
openCloses.add(new Entry(true, next));
inPossibleMatch = true;
}
else {
} else {
inPossibleMatch = false;
}
t = next;
}
}
else if (t.isSingleChar('>')) {
} else if (t.isSingleChar('>')) {
inPossibleMatch = false;
}
else if (inPossibleMatch && t.is(TAG_SELF_CLOSE)) {
openCloses.remove(openCloses.size()-1);
} else if (inPossibleMatch && t.is(TAG_SELF_CLOSE)) {
openCloses.remove(openCloses.size() - 1);
inPossibleMatch = false;
}
else if (t.is(CLOSE_TAG_START)) {
} else if (t.is(CLOSE_TAG_START)) {
Token next = t.getNextToken();
if (next!=null) {
if (next != null) {
// Invalid XML might not have a match
if (next.is(lexeme)) {
openCloses.add(new Entry(false, next));
@@ -181,11 +149,10 @@ public class XmlOccurrenceMarker implements OccurrenceMarker {
}
t = t.getNextToken();
}
for (int i=openCloses.size()-1; i>=0; i--) {
for (int i = openCloses.size() - 1; i >= 0; i--) {
Entry entry = openCloses.get(i);
depth += entry.open ? -1 : 1;
if (depth==-1) {
if (depth == -1) {
try {
Token match = entry.t;
int end = match.getOffset() + match.length();
@@ -199,34 +166,25 @@ public class XmlOccurrenceMarker implements OccurrenceMarker {
return;
}
}
openCloses.clear();
if (--curLine>=0) {
if (--curLine >= 0) {
t = doc.getTokenListForLine(curLine);
}
} while (curLine>=0);
} while (curLine >= 0);
}
}
/**
* Used internally when searching backward for a matching "open" tag.
*/
private static class Entry {
private boolean open;
private Token t;
Entry(boolean open, Token t) {
this.open = open;
this.t = t;
}
}
}
}
@@ -1,9 +1,7 @@
/*
* 07/29/2009
*
* FocusableTip.java - A focusable tool tip, like those in Eclipse.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.focusabletip;
@@ -34,53 +32,44 @@ import javax.swing.event.MouseInputAdapter;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* A focusable tool tip, similar to those found in Eclipse. The user
* A focusable tool tip, similar to those found in Eclipse. The user
* can click in the tip and it becomes a "real," resizable window.
*
* @author Robert Futrell
* @version 1.0
*/
public class FocusableTip {
private JTextArea textArea;
private TipWindow tipWindow;
private URL imageBase;
private TextAreaListener textAreaListener;
private HyperlinkListener hyperlinkListener;
private String lastText;
private Dimension maxSize; // null to automatic.
/**
* The screen bounds in which the mouse has to stay for the currently
* displayed tip to stay visible.
*/
private Rectangle tipVisibleBounds;
/**
* Margin from mouse cursor at which to draw focusable tip.
*/
private static final int X_MARGIN = 18;
/**
* Margin from mouse cursor at which to draw focusable tip.
*/
private static final int Y_MARGIN = 12;
private static final ResourceBundle MSG = ResourceBundle.getBundle(
"org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip");
private static final ResourceBundle MSG = ResourceBundle.getBundle("org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip");
public FocusableTip(JTextArea textArea, HyperlinkListener listener) {
setTextArea(textArea);
this.hyperlinkListener = listener;
textAreaListener = new TextAreaListener();
tipVisibleBounds = new Rectangle();
}
/**
* Compute the bounds in which the user can move the mouse without the
* tip window disappearing.
@@ -93,86 +82,73 @@ public class FocusableTip {
Point p = r.getLocation();
SwingUtilities.convertPointFromScreen(p, textArea);
r.setLocation(p);
tipVisibleBounds.setBounds(r.x,r.y-15, r.width,r.height+15*2);
tipVisibleBounds.setBounds(r.x, r.y - 15, r.width, r.height + 15 * 2);
}
private void createAndShowTipWindow(final MouseEvent e, final String text) {
Window owner = SwingUtilities.getWindowAncestor(textArea);
tipWindow = new TipWindow(owner, this, text);
tipWindow.setHyperlinkListener(hyperlinkListener);
// Give apps a chance to decorate us with drop shadows, etc.
PopupWindowDecorator decorator = PopupWindowDecorator.get();
if (decorator!=null) {
if (decorator != null) {
decorator.decorate(tipWindow);
}
// TODO: Position tip window better (handle RTL, edges of screen, etc.).
// Wrap in an invokeLater() to work around a JEditorPane issue where it
// doesn't return its proper preferred size until after it is displayed.
// See http://forums.sun.com/thread.jspa?forumID=57&threadID=574810
// for a discussion.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// If a new FocusableTip is requested while another one is
// *focused* and visible, the focused tip (i.e. "tipWindow")
// will be disposed of. If this Runnable is run after the
// dispose(), tipWindow will be null. All of this is done on
// will be disposed of. If this Runnable is run after the
// dispose(), tipWindow will be null. All of this is done on
// the EDT so no synchronization should be necessary.
if (tipWindow==null) {
if (tipWindow == null) {
return;
}
tipWindow.fixSize();
ComponentOrientation o = textArea.getComponentOrientation();
Point p = e.getPoint();
SwingUtilities.convertPointToScreen(p, textArea);
// Ensure tool tip is in the window bounds.
// Multi-monitor support - make sure the completion window (and
// description window, if applicable) both fit in the same
// window in a multi-monitor environment. To do this, we decide
// window in a multi-monitor environment. To do this, we decide
// which monitor the rectangle "p" is in, and use that one.
Rectangle sb = TipUtil.getScreenBoundsForPoint(p.x, p.y);
//Dimension ss = tipWindow.getToolkit().getScreenSize();
// Dimension ss = tipWindow.getToolkit().getScreenSize();
// Try putting our stuff "below" the mouse first.
int y = p.y + Y_MARGIN;
if (y+tipWindow.getHeight()>=sb.y+sb.height) {
if (y + tipWindow.getHeight() >= sb.y + sb.height) {
y = p.y - Y_MARGIN - tipWindow.getHeight();
}
// Get x-coordinate of completions. Try to align left edge
// Get x-coordinate of completions. Try to align left edge
// with the mouse first (with a slight margin).
int x = p.x - X_MARGIN; // ltr
if (!o.isLeftToRight()) {
x = p.x - tipWindow.getWidth() + X_MARGIN;
}
if (x<sb.x) {
if (x < sb.x) {
x = sb.x;
}
else if (x+tipWindow.getWidth()>sb.x+sb.width) { // completions don't fit
} else if (x + tipWindow.getWidth() > sb.x + sb.width) { // completions
// don't
// fit
x = sb.x + sb.width - tipWindow.getWidth();
}
tipWindow.setLocation(x, y);
tipWindow.setVisible(true);
computeTipVisibleBounds(); // Do after tip is visible
textAreaListener.install(textArea);
lastText = text;
}
});
}
/**
* Returns the base URL to use when loading images in this focusable tip.
*
@@ -182,31 +158,29 @@ public class FocusableTip {
public URL getImageBase() {
return imageBase;
}
/**
* The maximum size for unfocused tool tips.
*
* @return The maximum size for unfocused tool tips. A value of
* @return The maximum size for unfocused tool tips. A value of
* <code>null</code> will use a default size.
* @see #setMaxSize(Dimension)
*/
public Dimension getMaxSize() {
return maxSize;
}
return maxSize;
}
/**
* Returns localized text for the given key.
*
* @param key The key into the resource bundle.
* @param key
* The key into the resource bundle.
* @return The localized text.
*/
static String getString(String key) {
return MSG.getString(key);
}
/**
* Disposes of the focusable tip currently displayed, if any.
*/
@@ -220,67 +194,58 @@ public class FocusableTip {
textArea.requestFocus();
}
}
void removeListeners() {
//System.out.println("DEBUG: Removing text area listeners");
// System.out.println("DEBUG: Removing text area listeners");
textAreaListener.uninstall();
}
/**
* Sets the base URL to use when loading images in this focusable tip.
*
* @param url The base URL to use.
* @param url
* The base URL to use.
* @see #getImageBase()
*/
public void setImageBase(URL url) {
imageBase = url;
}
/**
* Sets the maximum size for unfocused tool tips.
*
* @param maxSize The new maximum size. A value of <code>null</code> will
* cause a default size to be used.
* @param maxSize
* The new maximum size. A value of <code>null</code> will
* cause a default size to be used.
* @see #getMaxSize()
*/
public void setMaxSize(Dimension maxSize) {
this.maxSize = maxSize;
}
this.maxSize = maxSize;
}
private void setTextArea(JTextArea textArea) {
this.textArea = textArea;
// Is okay to do multiple times.
ToolTipManager.sharedInstance().registerComponent(textArea);
}
public void toolTipRequested(MouseEvent e, String text) {
if (text==null || text.length()==0) {
if (text == null || text.length() == 0) {
possiblyDisposeOfTipWindow();
lastText = text;
return;
}
if (lastText==null || text.length()!=lastText.length() ||
!text.equals(lastText)) {
if (lastText == null || text.length() != lastText.length() || !text.equals(lastText)) {
possiblyDisposeOfTipWindow();
createAndShowTipWindow(e, text);
}
}
/**
* Listens for events in a text area.
*/
private class TextAreaListener extends MouseInputAdapter implements
CaretListener, ComponentListener, FocusListener, KeyListener {
private class TextAreaListener extends MouseInputAdapter implements CaretListener, ComponentListener, FocusListener, KeyListener {
@Override
public void caretUpdate(CaretEvent e) {
Object source = e.getSource();
@@ -288,31 +253,30 @@ public class FocusableTip {
possiblyDisposeOfTipWindow();
}
}
@Override
public void componentHidden(ComponentEvent e) {
handleComponentEvent(e);
}
@Override
public void componentMoved(ComponentEvent e) {
handleComponentEvent(e);
}
@Override
public void componentResized(ComponentEvent e) {
handleComponentEvent(e);
}
@Override
public void componentShown(ComponentEvent e) {
handleComponentEvent(e);
}
@Override
public void focusGained(FocusEvent e) {
}
public void focusGained(FocusEvent e) {}
@Override
public void focusLost(FocusEvent e) {
// Only dispose of tip if it wasn't the TipWindow that was clicked
@@ -320,18 +284,16 @@ public class FocusableTip {
// calling SwingUtilities.getWindowAncestor() to guard against an
// NPE.
Component c = e.getOppositeComponent();
boolean tipClicked = (c instanceof TipWindow) ||
(c!=null &&
SwingUtilities.getWindowAncestor(c) instanceof TipWindow);
boolean tipClicked = (c instanceof TipWindow) || (c != null && SwingUtilities.getWindowAncestor(c) instanceof TipWindow);
if (!tipClicked) {
possiblyDisposeOfTipWindow();
}
}
private void handleComponentEvent(ComponentEvent e) {
possiblyDisposeOfTipWindow();
}
public void install(JTextArea textArea) {
textArea.addCaretListener(this);
textArea.addComponentListener(this);
@@ -340,41 +302,37 @@ public class FocusableTip {
textArea.addMouseListener(this);
textArea.addMouseMotionListener(this);
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_ESCAPE) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
possiblyDisposeOfTipWindow();
}
else if (e.getKeyCode()==KeyEvent.VK_F2) {
if (tipWindow!=null && !tipWindow.getFocusableWindowState()) {
} else if (e.getKeyCode() == KeyEvent.VK_F2) {
if (tipWindow != null && !tipWindow.getFocusableWindowState()) {
tipWindow.actionPerformed(null);
e.consume(); // Don't do bookmarking stuff
}
}
}
@Override
public void keyReleased(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {}
@Override
public void keyTyped(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {}
@Override
public void mouseExited(MouseEvent e) {
// possiblyDisposeOfTipWindow();
}
@Override
public void mouseMoved(MouseEvent e) {
if (tipVisibleBounds==null ||
!tipVisibleBounds.contains(e.getPoint())) {
if (tipVisibleBounds == null || !tipVisibleBounds.contains(e.getPoint())) {
possiblyDisposeOfTipWindow();
}
}
public void uninstall() {
textArea.removeCaretListener(this);
textArea.removeComponentListener(this);
@@ -383,7 +341,5 @@ public class FocusableTip {
textArea.removeMouseListener(this);
textArea.removeMouseMotionListener(this);
}
}
}
}
@@ -1,10 +1,8 @@
/*
* 12/23/2008
*
* SizeGrip.java - A size grip component that sits at the bottom of the window,
* allowing the user to easily resize that window.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.focusabletip;
@@ -29,7 +27,6 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.MouseInputAdapter;
/**
* A component that allows its parent window to be resizable, similar to the
* size grip seen on status bars.
@@ -39,34 +36,32 @@ import javax.swing.event.MouseInputAdapter;
*/
@SuppressWarnings({ "checkstyle:magicnumber" })
class SizeGrip extends JPanel {
/**
* The size grip to use if we're on OS X.
*/
private transient Image osxSizeGrip;
SizeGrip() {
MouseHandler adapter = new MouseHandler();
addMouseListener(adapter);
addMouseMotionListener(adapter);
setPreferredSize(new Dimension(16, 16));
}
/**
* Overridden to ensure that the cursor for this component is appropriate
* for the orientation.
*
* @param o The new orientation.
* @param o
* The new orientation.
*/
@Override
public void applyComponentOrientation(ComponentOrientation o) {
possiblyFixCursor(o.isLeftToRight());
super.applyComponentOrientation(o);
}
/**
* Creates and returns the OS X size grip image.
*
@@ -75,7 +70,7 @@ class SizeGrip extends JPanel {
private Image createOSXSizeGrip() {
ClassLoader cl = getClass().getClassLoader();
URL url = cl.getResource("org/fife/ui/rsyntaxtextarea/focusabletip/osx_sizegrip.png");
if (url==null) {
if (url == null) {
// We're not running in a jar - we may be debugging in Eclipse,
// for example
File f = new File("../RSyntaxTextArea/src/org/fife/ui/rsyntaxtextarea/focusabletip/osx_sizegrip.png");
@@ -86,8 +81,7 @@ class SizeGrip extends JPanel {
mue.printStackTrace();
return null;
}
}
else {
} else {
return null; // Can't find resource or image file
}
}
@@ -99,117 +93,107 @@ class SizeGrip extends JPanel {
}
return image;
}
/**
* Paints this panel.
*
* @param g The graphics context.
* @param g
* The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension dim = getSize();
if (osxSizeGrip!=null) {
g.drawImage(osxSizeGrip, dim.width-16, dim.height-16, null);
if (osxSizeGrip != null) {
g.drawImage(osxSizeGrip, dim.width - 16, dim.height - 16, null);
return;
}
Color c1 = UIManager.getColor("Label.disabledShadow");
Color c2 = UIManager.getColor("Label.disabledForeground");
ComponentOrientation orientation = getComponentOrientation();
if (orientation.isLeftToRight()) {
int width = dim.width -= 3;
int width = dim.width -= 3;
int height = dim.height -= 3;
g.setColor(c1);
g.fillRect(width-9,height-1, 3,3);
g.fillRect(width-5,height-1, 3,3);
g.fillRect(width-1,height-1, 3,3);
g.fillRect(width-5,height-5, 3,3);
g.fillRect(width-1,height-5, 3,3);
g.fillRect(width-1,height-9, 3,3);
g.fillRect(width - 9, height - 1, 3, 3);
g.fillRect(width - 5, height - 1, 3, 3);
g.fillRect(width - 1, height - 1, 3, 3);
g.fillRect(width - 5, height - 5, 3, 3);
g.fillRect(width - 1, height - 5, 3, 3);
g.fillRect(width - 1, height - 9, 3, 3);
g.setColor(c2);
g.fillRect(width-9,height-1, 2,2);
g.fillRect(width-5,height-1, 2,2);
g.fillRect(width-1,height-1, 2,2);
g.fillRect(width-5,height-5, 2,2);
g.fillRect(width-1,height-5, 2,2);
g.fillRect(width-1,height-9, 2,2);
}
else {
g.fillRect(width - 9, height - 1, 2, 2);
g.fillRect(width - 5, height - 1, 2, 2);
g.fillRect(width - 1, height - 1, 2, 2);
g.fillRect(width - 5, height - 5, 2, 2);
g.fillRect(width - 1, height - 5, 2, 2);
g.fillRect(width - 1, height - 9, 2, 2);
} else {
int height = dim.height -= 3;
g.setColor(c1);
g.fillRect(10,height-1, 3,3);
g.fillRect(6,height-1, 3,3);
g.fillRect(2,height-1, 3,3);
g.fillRect(6,height-5, 3,3);
g.fillRect(2,height-5, 3,3);
g.fillRect(2,height-9, 3,3);
g.fillRect(10, height - 1, 3, 3);
g.fillRect(6, height - 1, 3, 3);
g.fillRect(2, height - 1, 3, 3);
g.fillRect(6, height - 5, 3, 3);
g.fillRect(2, height - 5, 3, 3);
g.fillRect(2, height - 9, 3, 3);
g.setColor(c2);
g.fillRect(10,height-1, 2,2);
g.fillRect(6,height-1, 2,2);
g.fillRect(2,height-1, 2,2);
g.fillRect(6,height-5, 2,2);
g.fillRect(2,height-5, 2,2);
g.fillRect(2,height-9, 2,2);
g.fillRect(10, height - 1, 2, 2);
g.fillRect(6, height - 1, 2, 2);
g.fillRect(2, height - 1, 2, 2);
g.fillRect(6, height - 5, 2, 2);
g.fillRect(2, height - 5, 2, 2);
g.fillRect(2, height - 9, 2, 2);
}
}
/**
* Ensures that the cursor for this component is appropriate for the
* orientation.
*
* @param ltr Whether the current component orientation is LTR.
* @param ltr
* Whether the current component orientation is LTR.
*/
protected void possiblyFixCursor(boolean ltr) {
int cursor = Cursor.NE_RESIZE_CURSOR;
if (ltr) {
cursor = Cursor.NW_RESIZE_CURSOR;
}
if (cursor!=getCursor().getType()) {
if (cursor != getCursor().getType()) {
setCursor(Cursor.getPredefinedCursor(cursor));
}
}
@Override
public void updateUI() {
super.updateUI();
// TODO: Key off of Aqua LaF, not just OS X, as this size grip looks
// bad on other LaFs on Mac such as Nimbus.
if (System.getProperty("os.name").contains("OS X")) {
if (osxSizeGrip==null) {
if (osxSizeGrip == null) {
osxSizeGrip = createOSXSizeGrip();
}
}
else { // Clear memory in case of runtime LaF change.
} else { // Clear memory in case of runtime LaF change.
osxSizeGrip = null;
}
}
/**
* Listens for mouse events on this panel and resizes the parent window
* appropriately.
*/
private class MouseHandler extends MouseInputAdapter {
/*
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just using
* the locations relative to the corner component because the latter proved
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just
* using
* the locations relative to the corner component because the latter
* proved
* buggy - stretch the window too wide and some kind of arithmetic error
* started happening somewhere - our window would grow way too large.
*/
private Point origPos;
@Override
public void mouseDragged(MouseEvent e) {
Point newPos = e.getPoint();
@@ -217,21 +201,20 @@ class SizeGrip extends JPanel {
int xDelta = newPos.x - origPos.x;
int yDelta = newPos.y - origPos.y;
Window wind = SwingUtilities.getWindowAncestor(SizeGrip.this);
if (wind!=null) { // Should always be true
if (wind != null) { // Should always be true
if (getComponentOrientation().isLeftToRight()) {
int w = wind.getWidth();
if (newPos.x>=wind.getX()) {
if (newPos.x >= wind.getX()) {
w += xDelta;
}
int h = wind.getHeight();
if (newPos.y>=wind.getY()) {
if (newPos.y >= wind.getY()) {
h += yDelta;
}
wind.setSize(w,h);
}
else { // RTL
int newW = Math.max(1, wind.getWidth()-xDelta);
int newH = Math.max(1, wind.getHeight()+yDelta);
wind.setSize(w, h);
} else { // RTL
int newW = Math.max(1, wind.getWidth() - xDelta);
int newH = Math.max(1, wind.getHeight() + yDelta);
wind.setBounds(newPos.x, wind.getY(), newW, newH);
}
// invalidate()/validate() needed pre-1.6.
@@ -240,19 +223,16 @@ class SizeGrip extends JPanel {
}
origPos.setLocation(newPos);
}
@Override
public void mousePressed(MouseEvent e) {
origPos = e.getPoint();
SwingUtilities.convertPointToScreen(origPos, SizeGrip.this);
}
@Override
public void mouseReleased(MouseEvent e) {
origPos = null;
}
}
}
}
@@ -1,9 +1,7 @@
/*
* 08/13/2009
*
* TipUtil.java - Utility methods for homemade tool tips.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.focusabletip;
@@ -25,7 +23,6 @@ import javax.swing.text.html.HTMLDocument;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
/**
* Static utility methods for focusable tips.
*
@@ -33,63 +30,57 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* @version 1.0
*/
public final class TipUtil {
private TipUtil() {
}
private TipUtil() {}
/**
* Returns a hex string for the specified color, suitable for HTML.
*
* @param c The color.
* @param c
* The color.
* @return The string representation, in the form "<code>#rrggbb</code>",
* or <code>null</code> if <code>c</code> is <code>null</code>.
*/
private static String getHexString(Color c) {
if (c==null) {
if (c == null) {
return null;
}
StringBuilder sb = new StringBuilder("#");
int r = c.getRed();
if (r<16) {
if (r < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(r));
int g = c.getGreen();
if (g<16) {
if (g < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(g));
int b = c.getBlue();
if (b<16) {
if (b < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(b));
return sb.toString();
}
/**
* Returns the screen coordinates for the monitor that contains the
* specified point. This is useful for setups with multiple monitors,
* specified point. This is useful for setups with multiple monitors,
* to ensure that popup windows are positioned properly.
*
* @param x The x-coordinate, in screen coordinates.
* @param y The y-coordinate, in screen coordinates.
* @param x
* The x-coordinate, in screen coordinates.
* @param y
* The y-coordinate, in screen coordinates.
* @return The bounds of the monitor that contains the specified point.
*/
public static Rectangle getScreenBoundsForPoint(int x, int y) {
GraphicsEnvironment env = GraphicsEnvironment.
getLocalGraphicsEnvironment();
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] devices = env.getScreenDevices();
for (int i=0; i<devices.length; i++) {
for (int i = 0; i < devices.length; i++) {
GraphicsConfiguration[] configs = devices[i].getConfigurations();
for (int j=0; j<configs.length; j++) {
for (int j = 0; j < configs.length; j++) {
Rectangle gcBounds = configs[j].getBounds();
if (gcBounds.contains(x, y)) {
return gcBounds;
@@ -99,71 +90,59 @@ public final class TipUtil {
// If point is outside all monitors, default to default monitor (?)
return env.getMaximumWindowBounds();
}
/**
* Returns the default background color to use for tool tip windows.
*
* @return The default background color.
*/
public static Color getToolTipBackground() {
Color c = UIManager.getColor("ToolTip.background");
// Tooltip.background is wrong color on Nimbus (!)
boolean isNimbus = isNimbusLookAndFeel();
if (c==null || isNimbus) {
if (c == null || isNimbus) {
c = UIManager.getColor("info"); // Used by Nimbus (and others)
if (c==null || (isNimbus && isDerivedColor(c))) {
if (c == null || (isNimbus && isDerivedColor(c))) {
c = SystemColor.info; // System default
}
}
// Workaround for a bug (?) with Nimbus - calling JLabel.setBackground()
// with a ColorUIResource does nothing, must be a normal Color
if (c instanceof ColorUIResource) {
c = new Color(c.getRGB());
}
return c;
}
/**
* Returns the border used by tool tips in this look and feel.
*
* @return The border.
*/
public static Border getToolTipBorder() {
Border border = UIManager.getBorder("ToolTip.border");
if (border==null || isNimbusLookAndFeel()) {
if (border == null || isNimbusLookAndFeel()) {
border = UIManager.getBorder("nimbusBorder");
if (border==null) {
if (border == null) {
border = BorderFactory.createLineBorder(SystemColor.controlDkShadow);
}
}
return border;
}
/**
* Returns whether a color is a Nimbus DerivedColor, which is troublesome
* in that it doesn't use its RGB values (uses HSB instead?) and so
* querying them is useless.
*
* @param c The color to check.
* @param c
* The color to check.
* @return Whether it is a DerivedColor
*/
private static boolean isDerivedColor(Color c) {
return c!=null && c.getClass().getName().endsWith(".DerivedColor");
return c != null && c.getClass().getName().endsWith(".DerivedColor");
}
/**
* Returns whether the Nimbus Look and Feel is installed.
*
@@ -172,17 +151,16 @@ public final class TipUtil {
private static boolean isNimbusLookAndFeel() {
return UIManager.getLookAndFeel().getName().equals("Nimbus");
}
/**
* Tweaks a <code>JEditorPane</code> so it can be used to render the
* content in a focusable pseudo-tool tip. It is assumed that the editor
* content in a focusable pseudo-tool tip. It is assumed that the editor
* pane is using an <code>HTMLDocument</code>.
*
* @param textArea The editor pane to tweak.
* @param textArea
* The editor pane to tweak.
*/
public static void tweakTipEditorPane(JEditorPane textArea) {
// Jump through a few hoops to get things looking nice in Nimbus
boolean isNimbus = isNimbusLookAndFeel();
if (isNimbus) {
@@ -192,25 +170,20 @@ public final class TipUtil {
textArea.setSelectedTextColor(selFG);
textArea.setSelectionColor(selBG);
}
textArea.setEditable(false); // Required for links to work!
textArea.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
textArea.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// Make selection visible even though we are not (initially) focusable.
textArea.getCaret().setSelectionVisible(true);
// Set the foreground color. Important because when rendering HTML,
// Set the foreground color. Important because when rendering HTML,
// default foreground becomes black, which may not match all LAF's
// (e.g. Substance).
Color fg = UIManager.getColor("Label.foreground");
if (fg==null || (isNimbus && isDerivedColor(fg))) {
if (fg == null || (isNimbus && isDerivedColor(fg))) {
fg = SystemColor.textText;
}
textArea.setForeground(fg);
// Make it use the "tool tip" background color.
textArea.setBackground(TipUtil.getToolTipBackground());
// Force JEditorPane to use a certain font even in HTML.
// All standard LookAndFeels, even Nimbus (!), define Label.font.
Font font = UIManager.getFont("Label.font");
@@ -219,32 +192,27 @@ public final class TipUtil {
}
HTMLDocument doc = (HTMLDocument) textArea.getDocument();
setFont(doc, font, fg);
// Always add link foreground rule. Unfortunately these CSS rules
// Always add link foreground rule. Unfortunately these CSS rules
// stack each time the LaF is changed (how can we overwrite them
// without clearing out the important "standard" ones?).
Color linkFG = RSyntaxUtilities.getHyperlinkForeground();
doc.getStyleSheet().addRule(
"a { color: " + getHexString(linkFG) + "; }");
doc.getStyleSheet().addRule("a { color: " + getHexString(linkFG) + "; }");
}
/**
* Sets the default font for an HTML document (e.g., in a tool tip
* displaying HTML). This is here because when rendering HTML,
* displaying HTML). This is here because when rendering HTML,
* {@code setFont()} is not honored.
*
* @param doc The document to modify.
* @param font The font to use.
* @param fg The default foreground color.
* @param doc
* The document to modify.
* @param font
* The font to use.
* @param fg
* The default foreground color.
*/
public static void setFont(HTMLDocument doc, Font font, Color fg) {
doc.getStyleSheet().addRule(
"body { font-family: " + font.getFamily() +
"; font-size: " + font.getSize() + "pt" +
"; color: " + getHexString(fg) + "; }");
doc.getStyleSheet().addRule("body { font-family: " + font.getFamily() + "; font-size: " + font.getSize() + "pt" + "; color: "
+ getHexString(fg) + "; }");
}
}
}
@@ -1,9 +1,7 @@
/*
* 07/29/2009
*
* TipWindow.java - The actual window component representing the tool tip.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.focusabletip;
@@ -45,7 +43,6 @@ import javax.swing.text.html.HTMLDocument;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
/**
* The actual tool tip component.
*
@@ -53,203 +50,182 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* @version 1.0
*/
class TipWindow extends JWindow implements ActionListener {
private FocusableTip ft;
private JEditorPane textArea;
private String text;
private transient TipListener tipListener;
private transient HyperlinkListener userHyperlinkListener;
private static TipWindow visibleInstance;
/**
* Constructor.
*
* @param owner The parent window.
* @param msg The text of the tool tip. This can be HTML.
* @param owner
* The parent window.
* @param msg
* The text of the tool tip. This can be HTML.
*/
TipWindow(Window owner, FocusableTip ft, String msg) {
super(owner);
this.ft = ft;
// Render plain text tool tips correctly.
if (msg!=null && msg.length()>=6 &&
!msg.substring(0,6).toLowerCase().equals("<html>")) {
if (msg != null && msg.length() >= 6 && !msg.substring(0, 6).toLowerCase().equals("<html>")) {
msg = "<html>" + RSyntaxUtilities.escapeForHtml(msg, "<br>", false);
}
this.text = msg;
tipListener = new TipListener();
JPanel cp = new JPanel(new BorderLayout());
cp.setBorder(TipUtil.getToolTipBorder());
cp.setBackground(TipUtil.getToolTipBackground());
textArea = new JEditorPane("text/html", text);
TipUtil.tweakTipEditorPane(textArea);
if (ft.getImageBase()!=null) { // Base URL for images
((HTMLDocument)textArea.getDocument()).setBase(ft.getImageBase());
if (ft.getImageBase() != null) { // Base URL for images
((HTMLDocument) textArea.getDocument()).setBase(ft.getImageBase());
}
textArea.addMouseListener(tipListener);
textArea.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType()==HyperlinkEvent.EventType.ACTIVATED) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
TipWindow.this.ft.possiblyDisposeOfTipWindow();
}
}
});
cp.add(textArea);
setFocusableWindowState(false);
setContentPane(cp);
setBottomPanel(); // Must do after setContentPane()
pack();
// InputMap/ActionMap combo doesn't work for JWindows (even when
// using the JWindow's JRootPane), so we'll resort to KeyListener
KeyAdapter ka = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_ESCAPE) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
TipWindow.this.ft.possiblyDisposeOfTipWindow();
}
}
};
addKeyListener(ka);
textArea.addKeyListener(ka);
// Ensure only 1 TipWindow is ever visible. If the caller does what
// Ensure only 1 TipWindow is ever visible. If the caller does what
// they're supposed to and only creates these on the EDT, the
// synchronization isn't necessary, but we'll be extra safe.
synchronized (TipWindow.class) {
if (visibleInstance!=null) {
if (visibleInstance != null) {
visibleInstance.dispose();
}
visibleInstance = this;
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (!getFocusableWindowState()) {
setFocusableWindowState(true);
setBottomPanel();
textArea.removeMouseListener(tipListener);
pack();
addWindowFocusListener(new WindowAdapter() {
@Override
public void windowLostFocus(WindowEvent e) {
ft.possiblyDisposeOfTipWindow();
}
});
ft.removeListeners();
if (e==null) { // Didn't get here via our mouseover timer
if (e == null) { // Didn't get here via our mouseover timer
requestFocus();
}
}
}
/**
* Disposes of this window.
*/
@Override
public void dispose() {
//System.out.println("[DEBUG]: Disposing...");
// System.out.println("[DEBUG]: Disposing...");
Container cp = getContentPane();
for (int i=0; i<cp.getComponentCount(); i++) {
for (int i = 0; i < cp.getComponentCount(); i++) {
// Okay if listener is already removed
cp.getComponent(i).removeMouseListener(tipListener);
}
ft.removeListeners();
super.dispose();
}
/**
* Workaround for JEditorPane not returning its proper preferred size
* when rendering HTML until after layout already done. See
* when rendering HTML until after layout already done. See
* http://forums.sun.com/thread.jspa?forumID=57&threadID=574810 for a
* discussion.
*/
void fixSize() {
Dimension d = textArea.getPreferredSize();
Rectangle r = null;
try {
// modelToView call is required for this hack, never remove!
r = textArea.modelToView(textArea.getDocument().getLength()-1);
r = textArea.modelToView(textArea.getDocument().getLength() - 1);
// Ensure the text area doesn't start out too tall or wide.
d = textArea.getPreferredSize();
d.width += 25; // Just a little extra space
final int maxWindowW = ft.getMaxSize() != null ?
ft.getMaxSize().width : 600;
final int maxWindowH = ft.getMaxSize() != null ?
ft.getMaxSize().height : 400;
final int maxWindowW = ft.getMaxSize() != null ? ft.getMaxSize().width : 600;
final int maxWindowH = ft.getMaxSize() != null ? ft.getMaxSize().height : 400;
d.width = Math.min(d.width, maxWindowW);
d.height = Math.min(d.height, maxWindowH);
// Both needed for modelToView() calculation below...
textArea.setPreferredSize(d);
textArea.setSize(d);
// if the new textArea width causes our text to wrap, we must
// compute a new preferred size to get all our physical lines.
r = textArea.modelToView(textArea.getDocument().getLength()-1);
if (r.y+r.height>d.height) {
r = textArea.modelToView(textArea.getDocument().getLength() - 1);
if (r.y + r.height > d.height) {
d.height = r.y + r.height + 5;
if(ft.getMaxSize() != null) {
if (ft.getMaxSize() != null) {
d.height = Math.min(d.height, maxWindowH);
}
textArea.setPreferredSize(d);
}
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
}
pack(); // Must re-pack to calculate proper size.
}
public String getText() {
return text;
}
private void setBottomPanel() {
final JPanel panel = new JPanel(new BorderLayout());
panel.add(new JSeparator(), BorderLayout.NORTH);
boolean focusable = getFocusableWindowState();
if (focusable) {
SizeGrip sg = new SizeGrip();
sg.applyComponentOrientation(sg.getComponentOrientation()); // Workaround
panel.add(sg, BorderLayout.LINE_END);
MouseInputAdapter adapter = new MouseInputAdapter() {
private Point lastPoint;
@Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
SwingUtilities.convertPointToScreen(p, panel);
if (lastPoint==null) {
if (lastPoint == null) {
lastPoint = p;
}
else {
} else {
int dx = p.x - lastPoint.x;
int dy = p.y - lastPoint.y;
setLocation(getX()+dx, getY()+dy);
setLocation(getX() + dx, getY() + dy);
lastPoint = p;
}
}
@Override
public void mousePressed(MouseEvent e) {
lastPoint = e.getPoint();
@@ -259,15 +235,14 @@ class TipWindow extends JWindow implements ActionListener {
panel.addMouseListener(adapter);
panel.addMouseMotionListener(adapter);
// Don't add tipListener to the panel or SizeGrip
}
else {
} else {
panel.setOpaque(false);
JLabel label = new JLabel(FocusableTip.getString("FocusHotkey"));
Color fg = UIManager.getColor("Label.disabledForeground");
Font font = textArea.getFont();
font = font.deriveFont(font.getSize2D() - 1.0f);
label.setFont(font);
if (fg==null) { // Non BasicLookAndFeel-derived Looks
if (fg == null) { // Non BasicLookAndFeel-derived Looks
fg = Color.GRAY;
}
label.setOpaque(true);
@@ -279,10 +254,9 @@ class TipWindow extends JWindow implements ActionListener {
panel.add(label);
panel.addMouseListener(tipListener);
}
// Replace the previous SOUTH Component with the new one.
Container cp = getContentPane();
if (cp.getComponentCount()==2) { // Skip first time through
if (cp.getComponentCount() == 2) { // Skip first time through
Component comp = cp.getComponent(0);
cp.remove(0);
JScrollPane sp = new JScrollPane(comp);
@@ -296,56 +270,49 @@ class TipWindow extends JWindow implements ActionListener {
cp.getComponent(0).removeMouseListener(tipListener);
cp.remove(0);
}
cp.add(panel, BorderLayout.SOUTH);
}
/**
* Sets the listener for hyperlink events in this tip window.
*
* @param listener The new listener. The old listener (if any) is
* removed. A value of <code>null</code> means "no listener."
* @param listener
* The new listener. The old listener (if any) is
* removed. A value of <code>null</code> means "no listener."
*/
public void setHyperlinkListener(HyperlinkListener listener) {
// We've added a separate listener, so remove only the user's.
if (userHyperlinkListener!=null) {
if (userHyperlinkListener != null) {
textArea.removeHyperlinkListener(userHyperlinkListener);
}
userHyperlinkListener = listener;
if (userHyperlinkListener!=null) {
if (userHyperlinkListener != null) {
textArea.addHyperlinkListener(userHyperlinkListener);
}
}
/**
* Listens for events in this window.
*/
private final class TipListener extends MouseAdapter {
private TipListener() {
}
private TipListener() {}
@Override
public void mousePressed(MouseEvent e) {
actionPerformed(null); // Manually create "real" window
}
@Override
public void mouseExited(MouseEvent e) {
// Since we registered this listener on the child components of
// the JWindow, not the JWindow iteself, we have to be careful.
Component source = (Component)e.getSource();
Component source = (Component) e.getSource();
Point p = e.getPoint();
SwingUtilities.convertPointToScreen(p, source);
if (!TipWindow.this.getBounds().contains(p)) {
ft.possiblyDisposeOfTipWindow();
}
}
}
}
}
@@ -1,9 +1,7 @@
/*
* 10/08/2011
*
* CurlyFoldParser.java - Fold parser for languages with C-style syntax.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -16,24 +14,26 @@ import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* A basic fold parser that can be used for languages such as C, that use
* curly braces to denote code blocks. This parser searches for curly brace
* pairs and creates code folds out of them. It can also optionally find
* curly braces to denote code blocks. This parser searches for curly brace
* pairs and creates code folds out of them. It can also optionally find
* C-style multi-line comments ("<code>/* ... *&#47;</code>") and make them
* foldable as well.<p>
* foldable as well.
* <p>
*
* This parser knows nothing about language semantics; it uses
* <code>RSyntaxTextArea</code>'s syntax highlighting tokens to identify
* curly braces. By default, it looks for single-char tokens of type
* curly braces. By default, it looks for single-char tokens of type
* {@link Token#SEPARATOR}, with lexemes '<code>{</code>' or '<code>}</code>'.
* If your {@link org.fife.ui.rsyntaxtextarea.TokenMaker} uses a different token
* type for curly braces, you should override the {@link #isLeftCurly(Token)} and
* {@link #isRightCurly(Token)} methods with your own definitions. In theory,
* type for curly braces, you should override the {@link #isLeftCurly(Token)}
* and
* {@link #isRightCurly(Token)} methods with your own definitions. In theory,
* you could extend this fold parser to parse languages that use completely
* different tokens than curly braces to denote foldable regions by overriding
* those two methods.<p>
* those two methods.
* <p>
*
* Note also that this class may impose somewhat of a performance penalty on
* large source files, since it re-parses the entire document each time folds
@@ -43,28 +43,24 @@ import org.fife.ui.rsyntaxtextarea.Token;
* @version 1.0
*/
public class CurlyFoldParser implements FoldParser {
/**
* Whether to scan for C-style multi-line comments and make them foldable.
*/
private boolean foldableMultiLineComments;
/**
* Whether this parser is folding Java.
*/
private final boolean java;
/**
* Used to find import statements when folding Java code.
*/
private static final char[] KEYWORD_IMPORT = "import".toCharArray();
/**
* Ending of a multi-line comment in C, C++, Java, etc.
*/
protected static final char[] C_MLC_END = "*/".toCharArray();
/**
* Creates a fold parser that identifies foldable regions via curly braces
* as well as C-style multi-line comments.
@@ -72,23 +68,23 @@ public class CurlyFoldParser implements FoldParser {
public CurlyFoldParser() {
this(true, false);
}
/**
* Constructor.
*
* @param cStyleMultiLineComments Whether to scan for C-style multi-line
* comments and make them foldable.
* @param java Whether this parser is folding Java. This adds extra
* parsing rules, such as grouping all import statements into a
* fold section.
* @param cStyleMultiLineComments
* Whether to scan for C-style multi-line
* comments and make them foldable.
* @param java
* Whether this parser is folding Java. This adds extra
* parsing rules, such as grouping all import statements into a
* fold section.
*/
public CurlyFoldParser(boolean cStyleMultiLineComments, boolean java) {
this.foldableMultiLineComments = cStyleMultiLineComments;
this.java = java;
}
/**
* Returns whether multi-line comments are foldable with this parser.
*
@@ -98,16 +94,13 @@ public class CurlyFoldParser implements FoldParser {
public boolean getFoldableMultiLineComments() {
return foldableMultiLineComments;
}
/**
* {@inheritDoc}
*/
@Override
public List<Fold> getFolds(RSyntaxTextArea textArea) {
List<Fold> folds = new ArrayList<Fold>();
Fold currentFold = null;
int lineCount = textArea.getLineCount();
boolean inMLC = false;
@@ -118,106 +111,91 @@ public class CurlyFoldParser implements FoldParser {
int importGroupEndOffs = -1;
int lastRightCurlyLine = -1;
Fold prevFold = null;
try {
for (int line=0; line<lineCount; line++) {
for (int line = 0; line < lineCount; line++) {
Token t = textArea.getTokenListForLine(line);
while (t!=null && t.isPaintable()) {
while (t != null && t.isPaintable()) {
if (getFoldableMultiLineComments() && t.isComment()) {
// Java-specific stuff
if (java) {
if (importStartLine>-1) {
if (lastSeenImportLine>importStartLine) {
if (importStartLine > -1) {
if (lastSeenImportLine > importStartLine) {
Fold fold = null;
// Any imports found *should* be a top-level fold,
// but we're extra lenient here and allow groups
// of them anywhere to keep our parser better-behaved
// if they have random "imports" throughout code.
if (currentFold==null) {
fold = new Fold(FoldType.IMPORTS,
textArea, importGroupStartOffs);
// Any imports found *should* be a top-level
// fold,
// but we're extra lenient here and allow
// groups
// of them anywhere to keep our parser
// better-behaved
// if they have random "imports" throughout
// code.
if (currentFold == null) {
fold = new Fold(FoldType.IMPORTS, textArea, importGroupStartOffs);
folds.add(fold);
}
else {
fold = currentFold.createChild(FoldType.IMPORTS,
importGroupStartOffs);
} else {
fold = currentFold.createChild(FoldType.IMPORTS, importGroupStartOffs);
}
fold.setEndOffset(importGroupEndOffs);
}
importStartLine = lastSeenImportLine =
importGroupStartOffs = importGroupEndOffs = -1;
importStartLine = lastSeenImportLine = importGroupStartOffs = importGroupEndOffs = -1;
}
}
if (inMLC) {
// If we found the end of an MLC that started
// on a previous line...
if (t.endsWith(C_MLC_END)) {
int mlcEnd = t.getEndOffset() - 1;
if (currentFold==null) {
if (currentFold == null) {
currentFold = new Fold(FoldType.COMMENT, textArea, mlcStart);
currentFold.setEndOffset(mlcEnd);
folds.add(currentFold);
currentFold = null;
}
else {
} else {
currentFold = currentFold.createChild(FoldType.COMMENT, mlcStart);
currentFold.setEndOffset(mlcEnd);
currentFold = currentFold.getParent();
}
//System.out.println("Ending MLC at: " + mlcEnd + ", parent==" + currentFold);
// System.out.println("Ending MLC at: " + mlcEnd
// + ", parent==" + currentFold);
inMLC = false;
mlcStart = 0;
}
// Otherwise, this MLC is continuing on to yet
// another line.
}
else {
} else {
// If we're an MLC that ends on a later line...
if (t.getType()!=Token.COMMENT_EOL && !t.endsWith(C_MLC_END)) {
//System.out.println("Starting MLC at: " + t.offset);
if (t.getType() != Token.COMMENT_EOL && !t.endsWith(C_MLC_END)) {
// System.out.println("Starting MLC at: " +
// t.offset);
inMLC = true;
mlcStart = t.getOffset();
}
}
}
else if (isLeftCurly(t)) {
} else if (isLeftCurly(t)) {
// Java-specific stuff
if (java) {
if (importStartLine>-1) {
if (lastSeenImportLine>importStartLine) {
if (importStartLine > -1) {
if (lastSeenImportLine > importStartLine) {
Fold fold = null;
// Any imports found *should* be a top-level fold,
// but we're extra lenient here and allow groups
// of them anywhere to keep our parser better-behaved
// if they have random "imports" throughout code.
if (currentFold==null) {
fold = new Fold(FoldType.IMPORTS,
textArea, importGroupStartOffs);
// Any imports found *should* be a top-level
// fold,
// but we're extra lenient here and allow
// groups
// of them anywhere to keep our parser
// better-behaved
// if they have random "imports" throughout
// code.
if (currentFold == null) {
fold = new Fold(FoldType.IMPORTS, textArea, importGroupStartOffs);
folds.add(fold);
}
else {
fold = currentFold.createChild(FoldType.IMPORTS,
importGroupStartOffs);
} else {
fold = currentFold.createChild(FoldType.IMPORTS, importGroupStartOffs);
}
fold.setEndOffset(importGroupEndOffs);
}
importStartLine = lastSeenImportLine =
importGroupStartOffs = importGroupEndOffs = -1;
importStartLine = lastSeenImportLine = importGroupStartOffs = importGroupEndOffs = -1;
}
}
// If a new fold block starts on the same line as the
// previous one ends, we treat it as one big block
// (e.g. K&R-style "} else {")
@@ -228,30 +206,24 @@ public class CurlyFoldParser implements FoldParser {
// as possible
prevFold = null;
lastRightCurlyLine = -1;
}
else if (currentFold==null) { // A top-level fold
} else if (currentFold == null) { // A top-level fold
currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
folds.add(currentFold);
}
else { // A nested fold
} else { // A nested fold
currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
}
}
else if (isRightCurly(t)) {
if (currentFold!=null) {
} else if (isRightCurly(t)) {
if (currentFold != null) {
currentFold.setEndOffset(t.getOffset());
Fold parentFold = currentFold.getParent();
//System.out.println("... Adding regular fold at " + t.offset + ", parent==" + parentFold);
// System.out.println("... Adding regular fold at "
// + t.offset + ", parent==" + parentFold);
// Don't add fold markers for single-line blocks
if (currentFold.isOnSingleLine()) {
if (!currentFold.removeFromParent()) {
folds.remove(folds.size()-1);
folds.remove(folds.size() - 1);
}
}
else {
} else {
// Remember the end of the last completed fold,
// in case it needs to get merged with the next
// one (e.g. K&R "} else {" style)
@@ -260,79 +232,65 @@ public class CurlyFoldParser implements FoldParser {
}
currentFold = parentFold;
}
}
// Java-specific folding rules
else if (java) {
if (t.is(Token.RESERVED_WORD, KEYWORD_IMPORT)) {
if (importStartLine==-1) {
if (importStartLine == -1) {
importStartLine = line;
importGroupStartOffs = t.getOffset();
importGroupEndOffs = t.getOffset();
}
lastSeenImportLine = line;
}
else if (importStartLine>-1 &&
t.isIdentifier() &&//SEPARATOR &&
} else if (importStartLine > -1 && t.isIdentifier() && // SEPARATOR
// &&
t.isSingleChar(';')) {
importGroupEndOffs = t.getOffset();
}
}
t = t.getNextToken();
}
}
} catch (BadLocationException ble) { // Should never happen
ble.printStackTrace();
}
return folds;
}
/**
* Returns whether the token is a left curly brace. This method exists
* Returns whether the token is a left curly brace. This method exists
* so subclasses can provide their own curly brace definition.
*
* @param t The token.
* @param t
* The token.
* @return Whether it is a left curly brace.
* @see #isRightCurly(Token)
*/
public boolean isLeftCurly(Token t) {
return t.isLeftCurly();
}
/**
* Returns whether the token is a right curly brace. This method exists
* Returns whether the token is a right curly brace. This method exists
* so subclasses can provide their own curly brace definition.
*
* @param t The token.
* @param t
* The token.
* @return Whether it is a right curly brace.
* @see #isLeftCurly(Token)
*/
public boolean isRightCurly(Token t) {
return t.isRightCurly();
}
/**
* Sets whether multi-line comments are foldable with this parser.
*
* @param foldable Whether multi-line comments are foldable.
* @param foldable
* Whether multi-line comments are foldable.
* @see #getFoldableMultiLineComments()
*/
public void setFoldableMultiLineComments(boolean foldable) {
this.foldableMultiLineComments = foldable;
}
}
}
@@ -1,7 +1,6 @@
/*
* 10/08/2011
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -28,18 +27,17 @@ import org.fife.ui.rsyntaxtextarea.parser.ParseResult;
import org.fife.ui.rsyntaxtextarea.parser.Parser;
import org.fife.ui.rtextarea.RDocument;
/**
* The default implementation of a fold manager. Besides keeping track of
* The default implementation of a fold manager. Besides keeping track of
* folds, this class behaves as follows:
*
* <ul>
* <li>If text containing a newline is inserted in a collapsed fold,
* that fold, and any ancestor folds, are expanded. This ensures that
* modified text is always visible to the user.
* <li>If the text area's {@link RSyntaxTextArea#SYNTAX_STYLE_PROPERTY}
* changes, the current fold parser is uninstalled, and one appropriate
* for the new language, if any, is installed.
* <li>If text containing a newline is inserted in a collapsed fold,
* that fold, and any ancestor folds, are expanded. This ensures that
* modified text is always visible to the user.
* <li>If the text area's {@link RSyntaxTextArea#SYNTAX_STYLE_PROPERTY}
* changes, the current fold parser is uninstalled, and one appropriate
* for the new language, if any, is installed.
* </ul>
*
* The folding strategy to use is retrieved from {@link FoldParserManager}.
@@ -48,7 +46,7 @@ import org.fife.ui.rtextarea.RDocument;
* @version 1.0
*/
public class DefaultFoldManager implements FoldManager {
private RSyntaxTextArea textArea;
private Parser rstaParser;
private FoldParser foldParser;
@@ -56,12 +54,12 @@ public class DefaultFoldManager implements FoldManager {
private boolean codeFoldingEnabled;
private PropertyChangeSupport support;
private Listener l;
/**
* Constructor.
*
* @param textArea The text area whose folds we are managing.
* @param textArea
* The text area whose folds we are managing.
*/
public DefaultFoldManager(RSyntaxTextArea textArea) {
this.textArea = textArea;
@@ -73,48 +71,39 @@ public class DefaultFoldManager implements FoldManager {
folds = new ArrayList<Fold>();
updateFoldParser();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
@Override
public void clear() {
folds.clear();
}
@Override
public boolean ensureOffsetNotInClosedFold(int offs) {
boolean foldsOpened = false;
Fold fold = getDeepestFoldContaining(offs);
while (fold!=null) {
while (fold != null) {
if (fold.isCollapsed()) {
fold.setCollapsed(false);
foldsOpened = true;
}
fold = fold.getParent();
}
if (foldsOpened) { // Folds changing state mean gutter is stale
RSyntaxUtilities.possiblyRepaintGutter(textArea);
}
return foldsOpened;
}
@Override
public Fold getDeepestFoldContaining(int offs) {
Fold deepestFold = null;
if (offs>-1) {
for (int i=0; i<folds.size(); i++) {
if (offs > -1) {
for (int i = 0; i < folds.size(); i++) {
Fold fold = getFold(i);
if (fold.containsOffset(offs)) {
deepestFold = fold.getDeepestFoldContaining(offs);
@@ -124,15 +113,12 @@ public class DefaultFoldManager implements FoldManager {
}
return deepestFold;
}
@Override
public Fold getDeepestOpenFoldContaining(int offs) {
Fold deepestFold = null;
if (offs>-1) {
for (int i=0; i<folds.size(); i++) {
if (offs > -1) {
for (int i = 0; i < folds.size(); i++) {
Fold fold = getFold(i);
if (fold.containsOffset(offs)) {
if (fold.isCollapsed()) {
@@ -143,61 +129,48 @@ public class DefaultFoldManager implements FoldManager {
}
}
}
return deepestFold;
}
@Override
public Fold getFold(int index) {
return folds.get(index);
}
@Override
public int getFoldCount() {
return folds.size();
}
@Override
public Fold getFoldForLine(int line) {
return getFoldForLineImpl(null, folds, line);
}
private Fold getFoldForLineImpl(Fold parent, List<Fold> folds, int line) {
int low = 0;
int high = folds.size() - 1;
while (low <= high) {
int mid = (low + high) >> 1;
Fold midFold = folds.get(mid);
int startLine = midFold.getStartLine();
if (line==startLine) {
return midFold;
}
else if (line<startLine) {
high = mid - 1;
}
else {
int endLine = midFold.getEndLine();
if (line>=endLine) {
low = mid + 1;
}
else { // line>startLine && line<=endLine
List<Fold> children = midFold.getChildren();
return children!=null ? getFoldForLineImpl(midFold, children, line) : null;
private Fold getFoldForLineImpl(Fold parent, List<Fold> folds, int line) {
int low = 0;
int high = folds.size() - 1;
while (low <= high) {
int mid = (low + high) >> 1;
Fold midFold = folds.get(mid);
int startLine = midFold.getStartLine();
if (line == startLine) {
return midFold;
} else if (line < startLine) {
high = mid - 1;
} else {
int endLine = midFold.getEndLine();
if (line >= endLine) {
low = mid + 1;
} else { // line>startLine && line<=endLine
List<Fold> children = midFold.getChildren();
return children != null ? getFoldForLineImpl(midFold, children, line) : null;
}
}
}
return null; // No fold for this line
}
return null; // No fold for this line
}
@Override
public int getHiddenLineCount() {
int count = 0;
@@ -206,89 +179,78 @@ private Fold getFoldForLineImpl(Fold parent, List<Fold> folds, int line) {
}
return count;
}
@Override
public int getHiddenLineCountAbove(int line) {
return getHiddenLineCountAbove(line, false);
}
@Override
public int getHiddenLineCountAbove(int line, boolean physical) {
int count = 0;
for (Fold fold : folds) {
int comp = physical ? line+count : line;
if (fold.getStartLine()>=comp) {
int comp = physical ? line + count : line;
if (fold.getStartLine() >= comp) {
break;
}
count += getHiddenLineCountAboveImpl(fold, comp, physical);
}
return count;
}
/**
* Returns the number of lines "hidden" by collapsed folds above the
* specified line.
*
* @param fold The current fold in the recursive algorithm. It and its
* children are examined.
* @param line The line.
* @param physical Whether <code>line</code> is the number of a physical
* line (i.e. visible, not code-folded), or a logical one (i.e. any
* line from the model). If <code>line</code> was determined by a
* raw line calculation (i.e. <code>(visibleTopY / lineHeight)</code>),
* this value should be <code>true</code>. It should be
* <code>false</code> when it was calculated from an offset in the
* document (for example).
* @param fold
* The current fold in the recursive algorithm. It and its
* children are examined.
* @param line
* The line.
* @param physical
* Whether <code>line</code> is the number of a physical
* line (i.e. visible, not code-folded), or a logical one (i.e.
* any
* line from the model). If <code>line</code> was determined by a
* raw line calculation (i.e.
* <code>(visibleTopY / lineHeight)</code>),
* this value should be <code>true</code>. It should be
* <code>false</code> when it was calculated from an offset in
* the
* document (for example).
* @return The number of lines hidden in folds that are descendants of
* <code>fold</code>, or <code>fold</code> itself, above
* <code>line</code>.
*/
private int getHiddenLineCountAboveImpl(Fold fold, int line, boolean physical) {
int count = 0;
if (fold.getEndLine()<line ||
(fold.isCollapsed() && fold.getStartLine()<line)) {
if (fold.getEndLine() < line || (fold.isCollapsed() && fold.getStartLine() < line)) {
count = fold.getCollapsedLineCount();
}
else {
} else {
int childCount = fold.getChildCount();
for (int i=0; i<childCount; i++) {
for (int i = 0; i < childCount; i++) {
Fold child = fold.getChild(i);
int comp = physical ? line+count : line;
if (child.getStartLine()>=comp) {
int comp = physical ? line + count : line;
if (child.getStartLine() >= comp) {
break;
}
count += getHiddenLineCountAboveImpl(child, comp, physical);
}
}
return count;
}
@Override
public int getLastVisibleLine() {
int lastLine = textArea.getLineCount() - 1;
if (isCodeFoldingSupportedAndEnabled()) {
int foldCount = getFoldCount();
if (foldCount>0) {
Fold lastFold = getFold(foldCount-1);
if (foldCount > 0) {
Fold lastFold = getFold(foldCount - 1);
if (lastFold.containsLine(lastLine)) {
if (lastFold.isCollapsed()) {
lastLine = lastFold.getStartLine();
}
else { // Child fold may end on the same line as parent
} else { // Child fold may end on the same line as parent
while (lastFold.getHasChildFolds()) {
lastFold = lastFold.getLastChild();
if (lastFold.containsLine(lastLine)) {
@@ -296,8 +258,7 @@ private Fold getFoldForLineImpl(Fold parent, List<Fold> folds, int line) {
lastLine = lastFold.getStartLine();
break;
}
}
else { // Higher up
} else { // Higher up
break;
}
}
@@ -305,199 +266,165 @@ private Fold getFoldForLineImpl(Fold parent, List<Fold> folds, int line) {
}
}
}
return lastLine;
}
@Override
public int getVisibleLineAbove(int line) {
if (line<=0 || line>=textArea.getLineCount()) {
if (line <= 0 || line >= textArea.getLineCount()) {
return -1;
}
do {
line--;
} while (line>=0 && isLineHidden(line));
} while (line >= 0 && isLineHidden(line));
return line;
}
@Override
public int getVisibleLineBelow(int line) {
int lineCount = textArea.getLineCount();
if (line<0 || line>=lineCount-1) {
if (line < 0 || line >= lineCount - 1) {
return -1;
}
do {
line++;
} while (line<lineCount && isLineHidden(line));
return line==lineCount ? -1 : line;
} while (line < lineCount && isLineHidden(line));
return line == lineCount ? -1 : line;
}
// private static int binaryFindFoldContainingLine(int line) {
//
//List allFolds;
//
// int low = 0;
// int high = allFolds.size() - 1;
//
// while (low <= high) {
// int mid = (low + high) >> 1;
// Fold midVal = (Fold)allFolds.get(mid);
// if (midVal.containsLine(line)) {
// return mid;
// }
// if (line<=midVal.getStartLine()) {
// high = mid - 1;
// }
// else { // line > midVal.getEndLine()
// low = mid + 1;
// }
// }
//
// return -(low + 1); // key not found
//
// }
// private static int binaryFindFoldContainingLine(int line) {
//
// List allFolds;
//
// int low = 0;
// int high = allFolds.size() - 1;
//
// while (low <= high) {
// int mid = (low + high) >> 1;
// Fold midVal = (Fold)allFolds.get(mid);
// if (midVal.containsLine(line)) {
// return mid;
// }
// if (line<=midVal.getStartLine()) {
// high = mid - 1;
// }
// else { // line > midVal.getEndLine()
// low = mid + 1;
// }
// }
//
// return -(low + 1); // key not found
//
// }
@Override
public boolean isCodeFoldingEnabled() {
return codeFoldingEnabled;
}
@Override
public boolean isCodeFoldingSupportedAndEnabled() {
return codeFoldingEnabled && foldParser!=null;
return codeFoldingEnabled && foldParser != null;
}
@Override
public boolean isFoldStartLine(int line) {
return getFoldForLine(line)!=null;
return getFoldForLine(line) != null;
}
@Override
public boolean isLineHidden(int line) {
for (Fold fold : folds) {
if (fold.containsLine(line)) {
if (fold.isCollapsed()) {
return true;
}
else {
} else {
return isLineHiddenImpl(fold, line);
}
}
}
return false;
}
private boolean isLineHiddenImpl(Fold parent, int line) {
for (int i=0; i<parent.getChildCount(); i++) {
for (int i = 0; i < parent.getChildCount(); i++) {
Fold child = parent.getChild(i);
if (child.containsLine(line)) {
if (child.isCollapsed()) {
return true;
}
else {
} else {
return isLineHiddenImpl(child, line);
}
}
}
return false;
}
private void keepFoldState(Fold newFold, List<Fold> oldFolds) {
int previousLoc = Collections.binarySearch(oldFolds, newFold);
//System.out.println(newFold + " => " + previousLoc);
if (previousLoc>=0) {
// System.out.println(newFold + " => " + previousLoc);
if (previousLoc >= 0) {
Fold prevFold = oldFolds.get(previousLoc);
newFold.setCollapsed(prevFold.isCollapsed());
}
else {
//previousLoc = -(insertion point) - 1;
} else {
// previousLoc = -(insertion point) - 1;
int insertionPoint = -(previousLoc + 1);
if (insertionPoint>0) {
Fold possibleParentFold = oldFolds.get(insertionPoint-1);
if (possibleParentFold.containsOffset(
newFold.getStartOffset())) {
if (insertionPoint > 0) {
Fold possibleParentFold = oldFolds.get(insertionPoint - 1);
if (possibleParentFold.containsOffset(newFold.getStartOffset())) {
List<Fold> children = possibleParentFold.getChildren();
if (children!=null) {
if (children != null) {
keepFoldState(newFold, children);
}
}
}
}
}
private void keepFoldStates(List<Fold> newFolds, List<Fold> oldFolds) {
for (Fold newFold : newFolds) {
keepFoldState(newFold, folds);
List<Fold> newChildFolds = newFold.getChildren();
if (newChildFolds!=null) {
if (newChildFolds != null) {
keepFoldStates(newChildFolds, oldFolds);
}
}
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
@Override
public void reparse() {
if (codeFoldingEnabled && foldParser!=null) {
// Re-calculate folds. Keep the fold state of folds that are
if (codeFoldingEnabled && foldParser != null) {
// Re-calculate folds. Keep the fold state of folds that are
// still around.
List<Fold> newFolds = foldParser.getFolds(textArea);
if (newFolds==null) {
if (newFolds == null) {
newFolds = Collections.emptyList();
}
else {
} else {
keepFoldStates(newFolds, folds);
}
folds = newFolds;
// Let folks (gutter, etc.) know that folds have been updated.
support.firePropertyChange(PROPERTY_FOLDS_UPDATED, null, folds);
textArea.repaint();
}
else {
} else {
folds.clear();
}
}
@Override
public void setCodeFoldingEnabled(boolean enabled) {
if (enabled!=codeFoldingEnabled) {
if (enabled != codeFoldingEnabled) {
codeFoldingEnabled = enabled;
if (rstaParser!=null) {
if (rstaParser != null) {
textArea.removeParser(rstaParser);
}
if (enabled) {
rstaParser = new AbstractParser() {
@Override
public ParseResult parse(RSyntaxDocument doc, String style) {
reparse();
@@ -506,46 +433,40 @@ private Fold getFoldForLineImpl(Fold parent, List<Fold> folds, int line) {
};
textArea.addParser(rstaParser);
support.firePropertyChange(PROPERTY_FOLDS_UPDATED, null, null);
//reparse();
}
else {
// reparse();
} else {
folds = Collections.emptyList();
textArea.repaint();
support.firePropertyChange(PROPERTY_FOLDS_UPDATED, null, null);
}
}
}
@Override
public void setFolds(List<Fold> folds) {
this.folds = folds;
}
/**
* Updates the fold parser to be the one appropriate for the language
* currently being highlighted.
*/
private void updateFoldParser() {
foldParser = FoldParserManager.get().getFoldParser(
textArea.getSyntaxEditingStyle());
foldParser = FoldParserManager.get().getFoldParser(textArea.getSyntaxEditingStyle());
}
/**
* Listens for events in the text editor.
*/
private class Listener implements DocumentListener, PropertyChangeListener {
@Override
public void changedUpdate(DocumentEvent e) {
}
public void changedUpdate(DocumentEvent e) {}
@Override
public void insertUpdate(DocumentEvent e) {
// Adding text containing a newline to the visible line of a folded
// Fold causes that Fold to unfold. Check only start offset of
// Fold causes that Fold to unfold. Check only start offset of
// insertion since that's the line that was "modified".
int startOffs = e.getOffset();
int endOffs = startOffs + e.getLength();
@@ -553,60 +474,52 @@ private Fold getFoldForLineImpl(Fold parent, List<Fold> folds, int line) {
Element root = doc.getDefaultRootElement();
int startLine = root.getElementIndex(startOffs);
int endLine = root.getElementIndex(endOffs);
if (startLine!=endLine) { // Inserted text covering > 1 line...
if (startLine != endLine) { // Inserted text covering > 1 line...
Fold fold = getFoldForLine(startLine);
if (fold!=null && fold.isCollapsed()) {
if (fold != null && fold.isCollapsed()) {
fold.toggleCollapsedState();
}
}
}
@Override
public void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
if (RSyntaxTextArea.SYNTAX_STYLE_PROPERTY.equals(name)) {
// Syntax style changed in editor.
updateFoldParser();
reparse(); // Even if no fold parser change, highlighting did
}
else if ("document".equals(name)) {
} else if ("document".equals(name)) {
// The document switched out from under us
RDocument old = (RDocument)e.getOldValue();
RDocument old = (RDocument) e.getOldValue();
if (old != null) {
old.removeDocumentListener(this);
}
RDocument newDoc = (RDocument)e.getNewValue();
RDocument newDoc = (RDocument) e.getNewValue();
if (newDoc != null) {
newDoc.addDocumentListener(this);
}
reparse();
}
}
@Override
public void removeUpdate(DocumentEvent e) {
// Removing text from the visible line of a folded Fold causes that
// Fold to unfold. We only need to check the removal offset since
// Fold to unfold. We only need to check the removal offset since
// that's the new caret position.
int offs = e.getOffset();
try {
int lastLineModified = textArea.getLineOfOffset(offs);
//System.out.println(">>> " + lastLineModified);
// System.out.println(">>> " + lastLineModified);
Fold fold = getFoldForLine(lastLineModified);
//System.out.println("&&& " + fold);
if (fold!=null && fold.isCollapsed()) {
// System.out.println("&&& " + fold);
if (fold != null && fold.isCollapsed()) {
fold.toggleCollapsedState();
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
}
}
}
+126 -158
View File
@@ -1,32 +1,32 @@
/*
* 10/08/2011
*
* Fold.java - A foldable region of text in an RSyntaxTextArea instance.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
import java.util.ArrayList;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.Position;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
/**
* Information about a foldable region.<p>
* Information about a foldable region.
* <p>
*
* A <code>Fold</code> has zero or more children, and <code>Folds</code> thus
* form a hierarchical structure, with "parent" folds containing the info about
* any "child" folds they contain.<p>
* any "child" folds they contain.
* <p>
*
* Fold regions are denoted by a starting and ending offset, but the actual
* folding is done on a per-line basis, so <code>Fold</code> instances provide
* methods for retrieving both starting and ending offsets and lines. The
* methods for retrieving both starting and ending offsets and lines. The
* starting and ending offsets/lines are "sticky" and correctly track their
* positions even as the document is modified.
*
@@ -34,7 +34,7 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
* @version 1.0
*/
public class Fold implements Comparable<Fold> {
private int type;
private RSyntaxTextArea textArea;
private Position startOffs;
@@ -43,134 +43,131 @@ public class Fold implements Comparable<Fold> {
private List<Fold> children;
private boolean collapsed;
private int childCollapsedLineCount;
private int lastStartOffs = -1;
private int cachedStartLine;
private int lastEndOffs = -1;
private int cachedEndLine;
public Fold(int type, RSyntaxTextArea textArea, int startOffs)
throws BadLocationException {
public Fold(int type, RSyntaxTextArea textArea, int startOffs) throws BadLocationException {
this.type = type;
this.textArea = textArea;
this.startOffs = textArea.getDocument().createPosition(startOffs);
}
/**
* Creates a fold that is a child of this one.
*
* @param type The type of fold.
* @param startOffs The starting offset of the fold.
* @param type
* The type of fold.
* @param startOffs
* The starting offset of the fold.
* @return The child fold.
* @throws BadLocationException If <code>startOffs</code> is invalid.
* @throws BadLocationException
* If <code>startOffs</code> is invalid.
* @see FoldType
*/
public Fold createChild(int type, int startOffs) throws BadLocationException {
Fold child = new Fold(type, textArea, startOffs);
child.parent = this;
if (children==null) {
if (children == null) {
children = new ArrayList<Fold>();
}
children.add(child);
return child;
}
/**
* Two folds are considered equal if they start at the same offset.
*
* @param otherFold Another fold to compare this one to.
* @param otherFold
* Another fold to compare this one to.
* @return How this fold compares to the other.
*/
@Override
public int compareTo(Fold otherFold) {
int result = -1;
if (otherFold!=null) {
if (otherFold != null) {
result = startOffs.getOffset() - otherFold.startOffs.getOffset();
//result = getStartLine() - otherFold.getStartLine();
// result = getStartLine() - otherFold.getStartLine();
}
return result;
}
/**
* Returns whether the specified line would be hidden in this fold. Since
* Returns whether the specified line would be hidden in this fold. Since
* RSTA displays the "first" line in a fold, this means that the line must
* must be between <code>(getStartLine()+1)</code> and
* <code>getEndLine()</code>, inclusive.
*
* @param line The line to check.
* @param line
* The line to check.
* @return Whether the line would be hidden if this fold is collapsed.
* @see #containsOffset(int)
* @see #containsOrStartsOnLine(int)
*/
public boolean containsLine(int line) {
return line>getStartLine() && line<=getEndLine();
return line > getStartLine() && line <= getEndLine();
}
/**
* Returns whether the given line is in the range
* <code>[getStartLine(), getEndLine()]</code>, inclusive.
*
* @param line The line to check.
* @param line
* The line to check.
* @return Whether this fold contains, or starts on, the line.
* @see #containsLine(int)
*/
public boolean containsOrStartsOnLine(int line) {
return line>=getStartLine() && line<=getEndLine();
return line >= getStartLine() && line <= getEndLine();
}
/**
* Returns whether the specified offset is "inside" the fold. This method
* Returns whether the specified offset is "inside" the fold. This method
* returns <code>true</code> if the offset is greater than the fold start
* offset, and no further than the last offset of the last folded line.
*
* @param offs The offset to check.
* @param offs
* The offset to check.
* @return Whether the offset is "inside" the fold.
* @see #containsLine(int)
*/
public boolean containsOffset(int offs) {
boolean contained = false;
if (offs>getStartOffset()) {
if (offs > getStartOffset()) {
// Use Elements to avoid BadLocationExceptions
Element root = textArea.getDocument().getDefaultRootElement();
int line = root.getElementIndex(offs);
contained = line<=getEndLine();
contained = line <= getEndLine();
}
return contained;
}
/**
* Two folds are considered equal if they have the same starting offset.
*
* @param otherFold Another fold to compare this one to.
* @param otherFold
* Another fold to compare this one to.
* @return Whether the two folds are equal.
* @see #compareTo(Fold)
*/
@Override
public boolean equals(Object otherFold) {
return otherFold instanceof Fold && compareTo((Fold)otherFold)==0;
return otherFold instanceof Fold && compareTo((Fold) otherFold) == 0;
}
/**
* Returns a specific child fold.
*
* @param index The index of the child fold.
* @param index
* The index of the child fold.
* @return The child fold.
* @see #getChildCount()
*/
public Fold getChild(int index) {
return children.get(index);
}
/**
* Returns the number of child folds.
*
@@ -178,12 +175,11 @@ public class Fold implements Comparable<Fold> {
* @see #getChild(int)
*/
public int getChildCount() {
return children==null ? 0 : children.size();
return children == null ? 0 : children.size();
}
/**
* Returns the array of child folds. This is a shallow copy.
* Returns the array of child folds. This is a shallow copy.
*
* @return The array of child folds, or <code>null</code> if there are
* none.
@@ -191,13 +187,13 @@ public class Fold implements Comparable<Fold> {
List<Fold> getChildren() {
return children;
}
/**
* Returns the number of collapsed lines under this fold. If this fold
* Returns the number of collapsed lines under this fold. If this fold
* is collapsed, this method returns {@link #getLineCount()}, otherwise
* it returns the sum of all collapsed lines of all child folds of this
* one.<p>
* one.
* <p>
*
* The value returned is cached, so this method returns quickly and
* shouldn't affect performance.
@@ -207,21 +203,21 @@ public class Fold implements Comparable<Fold> {
public int getCollapsedLineCount() {
return collapsed ? getLineCount() : childCollapsedLineCount;
}
/**
* Returns the "deepest" fold containing the specified offset. It is
* Returns the "deepest" fold containing the specified offset. It is
* assumed that it's already been verified that <code>offs</code> is indeed
* contained in this fold.
*
* @param offs The offset.
* @param offs
* The offset.
* @return The fold, or <code>null</code> if no child fold also contains
* the offset.
* @see FoldManager#getDeepestFoldContaining(int)
*/
Fold getDeepestFoldContaining(int offs) {
Fold deepestFold = this;
for (int i=0; i<getChildCount(); i++) {
for (int i = 0; i < getChildCount(); i++) {
Fold fold = getChild(i);
if (fold.containsOffset(offs)) {
deepestFold = fold.getDeepestFoldContaining(offs);
@@ -230,23 +226,21 @@ public class Fold implements Comparable<Fold> {
}
return deepestFold;
}
/**
* Returns the "deepest" open fold containing the specified offset. It
* Returns the "deepest" open fold containing the specified offset. It
* is assumed that it's already been verified that <code>offs</code> is
* indeed contained in this fold.
*
* @param offs The offset.
* @param offs
* The offset.
* @return The fold, or <code>null</code> if no open fold contains the
* offset.
* @see FoldManager#getDeepestOpenFoldContaining(int)
*/
Fold getDeepestOpenFoldContaining(int offs) {
Fold deepestFold = this;
for (int i=0; i<getChildCount(); i++) {
for (int i = 0; i < getChildCount(); i++) {
Fold fold = getChild(i);
if (fold.containsOffset(offs)) {
if (fold.isCollapsed()) {
@@ -256,16 +250,14 @@ public class Fold implements Comparable<Fold> {
break;
}
}
return deepestFold;
}
/**
* Returns the end line of this fold. For example, in languages such as
* Returns the end line of this fold. For example, in languages such as
* C and Java, this might be the line containing the closing curly brace of
* a code block.<p>
* a code block.
* <p>
*
* The value returned by this method will automatically update as the
* text area's contents are modified, to track the ending line of the
@@ -277,37 +269,36 @@ public class Fold implements Comparable<Fold> {
*/
public int getEndLine() {
int endOffs = getEndOffset();
if (lastEndOffs==endOffs) {
if (lastEndOffs == endOffs) {
return cachedEndLine;
}
lastEndOffs = endOffs;
Element root = textArea.getDocument().getDefaultRootElement();
return cachedEndLine = root.getElementIndex(endOffs);
}
/**
* Returns the end offset of this fold. For example, in languages such as
* Returns the end offset of this fold. For example, in languages such as
* C and Java, this might be the offset of the closing curly brace of a
* code block.<p>
* code block.
* <p>
*
* The value returned by this method will automatically update as the
* text area's contents are modified, to track the ending offset of the
* code block.
*
* @return The end offset of this code block, or {@link Integer#MAX_VALUE}
* if this fold region isn't closed properly. The latter causes
* if this fold region isn't closed properly. The latter causes
* this fold to collapsed all lines through the end of the file.
* @see #getEndLine()
* @see #getStartOffset()
*/
public int getEndOffset() {
return endOffs!=null ? endOffs.getOffset() : Integer.MAX_VALUE;
return endOffs != null ? endOffs.getOffset() : Integer.MAX_VALUE;
}
/**
* Returns the type of fold this is. This will be one of the values in
* Returns the type of fold this is. This will be one of the values in
* {@link FoldType}, or a user-defined value.
*
* @return The type of fold this is.
@@ -315,8 +306,7 @@ public class Fold implements Comparable<Fold> {
public int getFoldType() {
return type;
}
/**
* Returns whether this fold has any child folds.
*
@@ -324,10 +314,9 @@ public class Fold implements Comparable<Fold> {
* @see #getChildCount()
*/
public boolean getHasChildFolds() {
return getChildCount()>0;
return getChildCount() > 0;
}
/**
* Returns the last child fold.
*
@@ -338,10 +327,9 @@ public class Fold implements Comparable<Fold> {
*/
public Fold getLastChild() {
int childCount = getChildCount();
return childCount==0 ? null : getChild(childCount-1);
return childCount == 0 ? null : getChild(childCount - 1);
}
/**
* Returns the number of lines that are hidden when this fold is
* collapsed.
@@ -353,8 +341,7 @@ public class Fold implements Comparable<Fold> {
public int getLineCount() {
return getEndLine() - getStartLine();
}
/**
* Returns the parent fold of this one.
*
@@ -364,11 +351,11 @@ public class Fold implements Comparable<Fold> {
public Fold getParent() {
return parent;
}
/**
* Returns the starting line of this fold region. This is the only line
* in the fold region that is not hidden when a fold is collapsed.<p>
* Returns the starting line of this fold region. This is the only line
* in the fold region that is not hidden when a fold is collapsed.
* <p>
*
* The value returned by this method will automatically update as the
* text area's contents are modified, to track the starting line of the
@@ -380,19 +367,19 @@ public class Fold implements Comparable<Fold> {
*/
public int getStartLine() {
int startOffs = getStartOffset();
if (lastStartOffs==startOffs) {
if (lastStartOffs == startOffs) {
return cachedStartLine;
}
lastStartOffs = startOffs;
Element root = textArea.getDocument().getDefaultRootElement();
return cachedStartLine = root.getElementIndex(startOffs);
}
/**
* Returns the starting offset of this fold region. For example, for
* Returns the starting offset of this fold region. For example, for
* languages such as C and Java, this would be the offset of the opening
* curly brace of a code block.<p>
* curly brace of a code block.
* <p>
*
* The value returned by this method will automatically update as the
* text area's contents are modified, to track the starting offset of the
@@ -405,14 +392,12 @@ public class Fold implements Comparable<Fold> {
public int getStartOffset() {
return startOffs.getOffset();
}
@Override
public int hashCode() {
return getStartLine();
}
/**
* Returns whether this fold is collapsed.
*
@@ -423,10 +408,9 @@ public class Fold implements Comparable<Fold> {
public boolean isCollapsed() {
return collapsed;
}
/**
* Returns whether this fold is entirely on a single line. In general,
* Returns whether this fold is entirely on a single line. In general,
* a {@link FoldParser} should not remember fold regions all on a single
* line, since there's really nothing to fold.
*
@@ -434,12 +418,11 @@ public class Fold implements Comparable<Fold> {
* @see #removeFromParent()
*/
public boolean isOnSingleLine() {
return getStartLine()==getEndLine();
return getStartLine() == getEndLine();
}
/**
* Removes this fold from its parent. This should only be called by
* Removes this fold from its parent. This should only be called by
* {@link FoldParser} implementations if they determine that a fold is all
* on a single line (and thus shouldn't be remembered) after creating it.
*
@@ -447,45 +430,41 @@ public class Fold implements Comparable<Fold> {
* @see #isOnSingleLine()
*/
public boolean removeFromParent() {
if (parent!=null) {
if (parent != null) {
parent.removeMostRecentChild();
parent = null;
return true;
}
return false;
}
private void removeMostRecentChild() {
children.remove(children.size()-1);
children.remove(children.size() - 1);
}
/**
* Sets whether this <code>Fold</code> is collapsed. Calling this method
* Sets whether this <code>Fold</code> is collapsed. Calling this method
* will update both the text area and all <code>Gutter</code> components.
*
* @param collapsed Whether this fold should be collapsed.
* @param collapsed
* Whether this fold should be collapsed.
* @see #isCollapsed()
* @see #toggleCollapsedState()
*/
public void setCollapsed(boolean collapsed) {
if (collapsed!=this.collapsed) {
if (collapsed != this.collapsed) {
// Change our fold state and cached info about folded line count.
int lineCount = getLineCount();
int linesToCollapse = lineCount - childCollapsedLineCount;
if (!collapsed) { // If we're expanding
linesToCollapse = -linesToCollapse;
}
//System.out.println("Hiding lines: " + linesToCollapse +
// " (" + lineCount + ", " + linesToCollapse + ")");
// System.out.println("Hiding lines: " + linesToCollapse +
// " (" + lineCount + ", " + linesToCollapse + ")");
this.collapsed = collapsed;
if (parent!=null) {
if (parent != null) {
parent.updateChildCollapsedLineCount(linesToCollapse);
}
// If an end point of the selection is being hidden, move the caret
// "out" of the fold.
if (collapsed) {
@@ -495,7 +474,7 @@ public class Fold implements Comparable<Fold> {
boolean updateCaret = containsLine(dotLine);
if (!updateCaret) {
int mark = textArea.getSelectionEnd();
if (mark!=dot) {
if (mark != dot) {
int markLine = root.getElementIndex(mark);
updateCaret = containsLine(markLine);
}
@@ -505,30 +484,27 @@ public class Fold implements Comparable<Fold> {
textArea.setCaretPosition(dot);
}
}
textArea.foldToggled(this);
}
}
/**
* Sets the ending offset of this fold, such as the closing curly brace
* of a code block in C or Java. {@link FoldParser} implementations should
* call this on an existing <code>Fold</code> upon finding its end. If
* call this on an existing <code>Fold</code> upon finding its end. If
* this method isn't called, then this <code>Fold</code> is considered to
* have no end, i.e., it will collapse everything to the end of the file.
*
* @param endOffs The end offset of this fold.
* @throws BadLocationException If <code>endOffs</code> is not a valid
* location in the text area.
* @param endOffs
* The end offset of this fold.
* @throws BadLocationException
* If <code>endOffs</code> is not a valid
* location in the text area.
*/
public void setEndOffset(int endOffs) throws BadLocationException {
this.endOffs = textArea.getDocument().createPosition(endOffs);
}
/**
* Toggles the collapsed state of this fold.
*
@@ -537,19 +513,17 @@ public class Fold implements Comparable<Fold> {
public void toggleCollapsedState() {
setCollapsed(!collapsed);
}
private void updateChildCollapsedLineCount(int count) {
childCollapsedLineCount += count;
//if (childCollapsedLineCount>getLineCount()) {
// Thread.dumpStack();
//}
if (!collapsed && parent!=null) {
// if (childCollapsedLineCount>getLineCount()) {
// Thread.dumpStack();
// }
if (!collapsed && parent != null) {
parent.updateChildCollapsedLineCount(count);
}
}
/**
* Overridden for debugging purposes.
*
@@ -557,12 +531,6 @@ public class Fold implements Comparable<Fold> {
*/
@Override
public String toString() {
return "[Fold: " +
"startOffs=" + getStartOffset() +
", endOffs=" + getEndOffset() +
", collapsed=" + collapsed +
"]";
return "[Fold: " + "startOffs=" + getStartOffset() + ", endOffs=" + getEndOffset() + ", collapsed=" + collapsed + "]";
}
}
}
@@ -1,10 +1,8 @@
/*
* 10/23/2011
*
* FoldCollapser.java - Goes through an RSTA instance and collapses folds of
* specific types, such as comments.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -12,95 +10,91 @@ package org.fife.ui.rsyntaxtextarea.folding;
import java.util.ArrayList;
import java.util.List;
/**
* Collapses folds based on their type. You can create an instance of this
* Collapses folds based on their type. You can create an instance of this
* class to collapse all comment blocks when opening a new file, for example.
*
* @author Robert Futrell
* @version 1.0
*/
public class FoldCollapser {
private List<Integer> typesToCollapse;
/**
* Creates an instance that collapses all comment blocks.
*/
public FoldCollapser() {
this(FoldType.COMMENT);
}
/**
* Creates an instance that collapses all blocks of the specified
* type.
*
* @param typeToCollapse The type to collapse.
* @param typeToCollapse
* The type to collapse.
* @see FoldType
*/
public FoldCollapser(int typeToCollapse) {
typesToCollapse = new ArrayList<Integer>(3);
addTypeToCollapse(typeToCollapse);
}
/**
* Adds a type of fold to collapse.
*
* @param typeToCollapse The type of fold to collapse.
* @param typeToCollapse
* The type of fold to collapse.
*/
public void addTypeToCollapse(int typeToCollapse) {
typesToCollapse.add(Integer.valueOf(typeToCollapse));
}
/**
* Collapses any relevant folds known by the fold manager.
*
* @param fm The fold manager.
* @param fm
* The fold manager.
*/
public void collapseFolds(FoldManager fm) {
for (int i=0; i<fm.getFoldCount(); i++) {
for (int i = 0; i < fm.getFoldCount(); i++) {
Fold fold = fm.getFold(i);
collapseImpl(fold);
}
}
/**
* Collapses the specified fold, and any of its child folds, as
* appropriate.
*
* @param fold The fold to examine.
* @param fold
* The fold to examine.
* @see #getShouldCollapse(Fold)
*/
protected void collapseImpl(Fold fold) {
if (getShouldCollapse(fold)) {
fold.setCollapsed(true);
}
for (int i=0; i<fold.getChildCount(); i++) {
for (int i = 0; i < fold.getChildCount(); i++) {
collapseImpl(fold.getChild(i));
}
}
/**
* Returns whether a specific fold should be collapsed.
*
* @param fold The fold to examine.
* @param fold
* The fold to examine.
* @return Whether the fold should be collapsed.
*/
public boolean getShouldCollapse(Fold fold) {
int type = fold.getFoldType();
for (Integer typeToCollapse : typesToCollapse) {
if (type==typeToCollapse.intValue()) {
if (type == typeToCollapse.intValue()) {
return true;
}
}
return false;
}
}
}
@@ -1,9 +1,7 @@
/*
* 10/08/2011
*
* FoldManager.java - Manages code folding in an RSyntaxTextArea instance.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -11,9 +9,9 @@ package org.fife.ui.rsyntaxtextarea.folding;
import java.beans.PropertyChangeListener;
import java.util.List;
/**
* Manages code folding in an instance of RSyntaxTextArea.<p>
* Manages code folding in an instance of RSyntaxTextArea.
* <p>
*
* By default, {@code RSyntaxTextArea} uses a {@link DefaultFoldManager}, which
* is sufficient for almost all applications.
@@ -23,69 +21,67 @@ import java.util.List;
* @see DefaultFoldManager
*/
public interface FoldManager {
/**
* Property fired when folds have been updated.
*/
String PROPERTY_FOLDS_UPDATED = "FoldsUpdated";
/**
* Adds a property change listener to this fold manager.
*
* @param l The new listener.
* @param l
* The new listener.
* @see #removePropertyChangeListener(PropertyChangeListener)
*/
void addPropertyChangeListener(PropertyChangeListener l);
/**
* Removes all folds.
*/
void clear();
/**
* Ensures that the specified offset is not hidden in a collapsed fold.
* Any folds containing this offset that are collapsed will be expanded.
*
* @param offs The offset.
* @param offs
* The offset.
* @return Whether any folds had to be opened.
* @see #getDeepestFoldContaining(int)
*/
boolean ensureOffsetNotInClosedFold(int offs);
/**
* Returns the "deepest" nested fold containing the specified offset.
*
* @param offs The offset.
* @param offs
* The offset.
* @return The deepest fold containing the offset, or <code>null</code> if
* no fold contains the offset.
*/
Fold getDeepestFoldContaining(int offs);
/**
* Returns the "deepest" open fold containing the specified offset.
*
* @param offs The offset.
* @param offs
* The offset.
* @return The fold, or <code>null</code> if no open fold contains the
* offset.
*/
Fold getDeepestOpenFoldContaining(int offs);
/**
* Returns a specific top-level fold, which may have child folds.
*
* @param index The index of the fold.
* @param index
* The index of the fold.
* @return The fold.
* @see #getFoldCount()
*/
Fold getFold(int index);
/**
* Returns the number of top-level folds.
*
@@ -93,19 +89,18 @@ public interface FoldManager {
* @see #getFold(int)
*/
int getFoldCount();
/**
* Returns the fold region that starts at the specified line.
*
* @param line The line number.
* @param line
* The line number.
* @return The fold, or <code>null</code> if the line is not the start
* of a fold region.
* @see #isFoldStartLine(int)
*/
Fold getFoldForLine(int line);
/**
* Returns the total number of hidden (folded) lines.
*
@@ -113,38 +108,42 @@ public interface FoldManager {
* @see #getHiddenLineCountAbove(int)
*/
int getHiddenLineCount();
/**
* Returns the number of lines "hidden" by collapsed folds above the
* specified line.
*
* @param line The line. This is the line number for a logical line.
* For the line number of a physical line (i.e. visible, not folded),
* use <code>getHiddenLineCountAbove(int, true)</code>.
* @param line
* The line. This is the line number for a logical line.
* For the line number of a physical line (i.e. visible, not
* folded),
* use <code>getHiddenLineCountAbove(int, true)</code>.
* @return The number of lines hidden in folds above <code>line</code>.
* @see #getHiddenLineCountAbove(int, boolean)
*/
int getHiddenLineCountAbove(int line);
/**
* Returns the number of lines "hidden" by collapsed folds above the
* specified line.
*
* @param line The line.
* @param physical Whether <code>line</code> is the number of a physical
* line (i.e. visible, not code-folded), or a logical one (i.e. any
* line from the model). If <code>line</code> was determined by a
* raw line calculation (i.e. <code>(visibleTopY / lineHeight)</code>),
* this value should be <code>true</code>. It should be
* <code>false</code> when it was calculated from an offset in the
* document (for example).
* @param line
* The line.
* @param physical
* Whether <code>line</code> is the number of a physical
* line (i.e. visible, not code-folded), or a logical one (i.e.
* any
* line from the model). If <code>line</code> was determined by a
* raw line calculation (i.e.
* <code>(visibleTopY / lineHeight)</code>),
* this value should be <code>true</code>. It should be
* <code>false</code> when it was calculated from an offset in
* the
* document (for example).
* @return The number of lines hidden in folds above <code>line</code>.
*/
int getHiddenLineCountAbove(int line, boolean physical);
/**
* Returns the last visible line in the text area, taking into account
* folds.
@@ -152,16 +151,13 @@ public interface FoldManager {
* @return The last visible line.
*/
int getLastVisibleLine();
int getVisibleLineAbove(int line);
int getVisibleLineBelow(int line);
/**
* Returns whether code folding is enabled. Note that only certain
/**
* Returns whether code folding is enabled. Note that only certain
* languages support code folding; those that do not will ignore this
* property.
*
@@ -169,8 +165,7 @@ public interface FoldManager {
* @see #setCodeFoldingEnabled(boolean)
*/
boolean isCodeFoldingEnabled();
/**
* Returns <code>true</code> if and only if code folding is enabled for
* this text area, AND folding is supported for the language it is editing.
@@ -182,61 +177,58 @@ public interface FoldManager {
* @see FoldParserManager
*/
boolean isCodeFoldingSupportedAndEnabled();
/**
* Returns whether the specified line contains the start of a fold region.
*
* @param line The line.
* @param line
* The line.
* @return Whether the line contains the start of a fold region.
* @see #getFoldForLine(int)
*/
boolean isFoldStartLine(int line);
/**
* Returns whether a line is hidden in a collapsed fold.
*
* @param line The line to check.
* @param line
* The line to check.
* @return Whether the line is hidden in a collapsed fold.
*/
boolean isLineHidden(int line);
/**
* Removes a property change listener from this fold manager.
*
* @param l The listener to remove.
* @param l
* The listener to remove.
* @see #addPropertyChangeListener(PropertyChangeListener)
*/
void removePropertyChangeListener(PropertyChangeListener l);
/**
* Forces an immediate reparsing for folds, if folding is enabled. This
* Forces an immediate reparsing for folds, if folding is enabled. This
* usually does not need to be called by the programmer, since fold
* parsing is done automatically by RSTA.
*/
void reparse();
/**
* Sets whether code folding is enabled. Note that only certain
* languages will support code folding out of the box. Those languages
* Sets whether code folding is enabled. Note that only certain
* languages will support code folding out of the box. Those languages
* which do not support folding will ignore this property.
*
* @param enabled Whether code folding should be enabled.
* @param enabled
* Whether code folding should be enabled.
* @see #isCodeFoldingEnabled()
*/
void setCodeFoldingEnabled(boolean enabled);
/**
* Sets the folds for this fold manager.
*
* @param folds The new folds. This should not be <code>null</code>.
* @param folds
* The new folds. This should not be <code>null</code>.
*/
void setFolds(List<Fold> folds);
}
}
@@ -1,9 +1,7 @@
/*
* 10/08/2011
*
* FoldParser.java - Locates folds in an RSyntaxTextArea instance.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -12,12 +10,11 @@ import java.util.List;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
/**
* Locates folds in a document. If you are implementing a language that has
* Locates folds in a document. If you are implementing a language that has
* sections of source code that can be logically "folded," you can create an
* instance of this interface that locates those regions and represents them
* as {@link Fold}s. <code>RSyntaxTextArea</code> knows how to take it from
* as {@link Fold}s. <code>RSyntaxTextArea</code> knows how to take it from
* there and implement code folding in the editor.
*
* @author Robert Futrell
@@ -26,16 +23,14 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
* @see XmlFoldParser
*/
public interface FoldParser {
/**
* Returns a list of all folds in the text area.
*
* @param textArea The text area whose contents should be analyzed.
* @return The list of folds. If this method returns <code>null</code>,
* @param textArea
* The text area whose contents should be analyzed.
* @return The list of folds. If this method returns <code>null</code>,
* it is treated as if no folds were found.
*/
List<Fold> getFolds(RSyntaxTextArea textArea);
}
}
@@ -1,10 +1,8 @@
/*
* 10/08/2011
*
* FoldParserManager.java - Used by RSTA to determine what fold parser to use
* for each language it supports.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -14,9 +12,8 @@ import java.util.Map;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
/**
* Manages fold parsers. Instances of <code>RSyntaxTextArea</code> call into
* Manages fold parsers. Instances of <code>RSyntaxTextArea</code> call into
* this class to retrieve fold parsers for whatever language they're editing.
* Folks implementing custom languages can add a {@link FoldParser}
* implementation for their language to this manager and it will be used by
@@ -26,80 +23,73 @@ import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
* @version 1.0
*/
public final class FoldParserManager implements SyntaxConstants {
/**
* Map from syntax styles to fold parsers.
*/
private Map<String, FoldParser> foldParserMap;
private static final FoldParserManager INSTANCE = new FoldParserManager();
/**
* Private constructor to prevent instantiation.
*/
private FoldParserManager() {
foldParserMap = createFoldParserMap();
}
/**
* Adds a mapping from a syntax style to a fold parser. The parser
* Adds a mapping from a syntax style to a fold parser. The parser
* specified will be shared among all RSTA instances editing that language,
* so it should be stateless (which should not be difficult for a fold
* parser). You can also override the fold parser for built-in languages,
* parser). You can also override the fold parser for built-in languages,
* such as <code>SYNTAX_STYLE_JAVA</code>, with your own parser
* implementations.
*
* @param syntaxStyle The syntax style.
* @param parser The parser.
* @param syntaxStyle
* The syntax style.
* @param parser
* The parser.
* @see SyntaxConstants
*/
public void addFoldParserMapping(String syntaxStyle, FoldParser parser) {
foldParserMap.put(syntaxStyle, parser);
}
/**
* Creates the syntax style-to-fold parser mapping for built-in languages.
*
* @return The style-to-fold parser mapping.
*/
private Map<String, FoldParser> createFoldParserMap() {
Map<String, FoldParser> map = new HashMap<String, FoldParser>();
map.put(SYNTAX_STYLE_C, new CurlyFoldParser());
map.put(SYNTAX_STYLE_CPLUSPLUS, new CurlyFoldParser());
map.put(SYNTAX_STYLE_CSHARP, new CurlyFoldParser());
map.put(SYNTAX_STYLE_CLOJURE, new LispFoldParser());
map.put(SYNTAX_STYLE_CSS, new CurlyFoldParser());
map.put(SYNTAX_STYLE_D, new CurlyFoldParser());
map.put(SYNTAX_STYLE_DART, new CurlyFoldParser());
map.put(SYNTAX_STYLE_GROOVY, new CurlyFoldParser());
map.put(SYNTAX_STYLE_HTACCESS, new XmlFoldParser());
map.put(SYNTAX_STYLE_HTML, new HtmlFoldParser(HtmlFoldParser.LANGUAGE_HTML));
map.put(SYNTAX_STYLE_JAVA, new CurlyFoldParser(true, true));
map.put(SYNTAX_STYLE_JAVASCRIPT, new CurlyFoldParser());
map.put(SYNTAX_STYLE_JSON, new JsonFoldParser());
map.put(SYNTAX_STYLE_JSON_WITH_COMMENTS,new JsonFoldParser());
map.put(SYNTAX_STYLE_JSP, new HtmlFoldParser(HtmlFoldParser.LANGUAGE_JSP));
map.put(SYNTAX_STYLE_LATEX, new LatexFoldParser());
map.put(SYNTAX_STYLE_LESS, new CurlyFoldParser());
map.put(SYNTAX_STYLE_LISP, new LispFoldParser());
map.put(SYNTAX_STYLE_MXML, new XmlFoldParser());
map.put(SYNTAX_STYLE_NSIS, new NsisFoldParser());
map.put(SYNTAX_STYLE_PERL, new CurlyFoldParser());
map.put(SYNTAX_STYLE_PHP, new HtmlFoldParser(HtmlFoldParser.LANGUAGE_PHP));
map.put(SYNTAX_STYLE_SCALA, new CurlyFoldParser());
map.put(SYNTAX_STYLE_TYPESCRIPT, new CurlyFoldParser());
map.put(SYNTAX_STYLE_XML, new XmlFoldParser());
map.put(SYNTAX_STYLE_C, new CurlyFoldParser());
map.put(SYNTAX_STYLE_CPLUSPLUS, new CurlyFoldParser());
map.put(SYNTAX_STYLE_CSHARP, new CurlyFoldParser());
map.put(SYNTAX_STYLE_CLOJURE, new LispFoldParser());
map.put(SYNTAX_STYLE_CSS, new CurlyFoldParser());
map.put(SYNTAX_STYLE_D, new CurlyFoldParser());
map.put(SYNTAX_STYLE_DART, new CurlyFoldParser());
map.put(SYNTAX_STYLE_GROOVY, new CurlyFoldParser());
map.put(SYNTAX_STYLE_HTACCESS, new XmlFoldParser());
map.put(SYNTAX_STYLE_HTML, new HtmlFoldParser(HtmlFoldParser.LANGUAGE_HTML));
map.put(SYNTAX_STYLE_JAVA, new CurlyFoldParser(true, true));
map.put(SYNTAX_STYLE_JAVASCRIPT, new CurlyFoldParser());
map.put(SYNTAX_STYLE_JSON, new JsonFoldParser());
map.put(SYNTAX_STYLE_JSON_WITH_COMMENTS, new JsonFoldParser());
map.put(SYNTAX_STYLE_JSP, new HtmlFoldParser(HtmlFoldParser.LANGUAGE_JSP));
map.put(SYNTAX_STYLE_LATEX, new LatexFoldParser());
map.put(SYNTAX_STYLE_LESS, new CurlyFoldParser());
map.put(SYNTAX_STYLE_LISP, new LispFoldParser());
map.put(SYNTAX_STYLE_MXML, new XmlFoldParser());
map.put(SYNTAX_STYLE_NSIS, new NsisFoldParser());
map.put(SYNTAX_STYLE_PERL, new CurlyFoldParser());
map.put(SYNTAX_STYLE_PHP, new HtmlFoldParser(HtmlFoldParser.LANGUAGE_PHP));
map.put(SYNTAX_STYLE_SCALA, new CurlyFoldParser());
map.put(SYNTAX_STYLE_TYPESCRIPT, new CurlyFoldParser());
map.put(SYNTAX_STYLE_XML, new XmlFoldParser());
return map;
}
/**
* Returns the singleton instance of this class.
*
@@ -108,20 +98,18 @@ public final class FoldParserManager implements SyntaxConstants {
public static FoldParserManager get() {
return INSTANCE;
}
/**
* Returns a fold parser to use for an editor highlighting code of a
* specific language.
*
* @param syntaxStyle A value from {@link SyntaxConstants}, such as
* <code>SYNTAX_STYLE_JAVA</code>.
* @param syntaxStyle
* A value from {@link SyntaxConstants}, such as
* <code>SYNTAX_STYLE_JAVA</code>.
* @return A fold parser to use, or <code>null</code> if none is registered
* for the language.
*/
public FoldParser getFoldParser(String syntaxStyle) {
return foldParserMap.get(syntaxStyle);
}
}
}
@@ -1,23 +1,21 @@
/*
* 10/08/2011
*
* FoldType.java - Types of folds found in many programming languages.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
/**
* Constants representing the "type" of a folded region. Implementations of
* Constants representing the "type" of a folded region. Implementations of
* {@link FoldParser} can also define their own folded region types, provided
* they give them values of at least {@link #FOLD_TYPE_USER_DEFINED_MIN}. This
* they give them values of at least {@link #FOLD_TYPE_USER_DEFINED_MIN}. This
* allows you to identify and auto-fold specific regions of source code when
* opening files; for example, a Java editor could identify all import
* statements in a file as a foldable region, and give it a user-defined value
* for fold type. Then, the UI could provide a means for the user to specify
* that they always want the import region folded when opening a new file.<p>
* for fold type. Then, the UI could provide a means for the user to specify
* that they always want the import region folded when opening a new file.
* <p>
*
* The majority of the time, however, code editors won't need to be that fancy,
* and can simply use the standard <code>CODE</code> and <code>COMMENT</code>
@@ -28,32 +26,27 @@ package org.fife.ui.rsyntaxtextarea.folding;
* @see Fold
*/
public interface FoldType {
/**
* Denotes a <code>Fold</code> as being a region of code.
*/
int CODE = 0;
int CODE = 0;
/**
* Denotes a <code>Fold</code> as being a multi-line comment.
*/
int COMMENT = 1;
int COMMENT = 1;
/**
* Denotes a <code>Fold</code> as being a section of import statements
* (Java), include statements (C), etc.
*/
int IMPORTS = 2;
int IMPORTS = 2;
/**
* Users building advanced editors such as IDE's, that want to allow their
* users to auto-expand/collapse foldable regions of a specific type other
* than comments, should define their custom fold types using values
* <code>FOLD_TYPE_USER_DEFINED_MIN + <em>n</em></code>. That way, if
* <code>FOLD_TYPE_USER_DEFINED_MIN + <em>n</em></code>. That way, if
* new default fold types are added to this interface in the future, your
* code won't suddenly break when upgrading to a new version of RSTA.
*/
int FOLD_TYPE_USER_DEFINED_MIN = 1000;
}
int FOLD_TYPE_USER_DEFINED_MIN = 1000;
}
@@ -1,9 +1,7 @@
/*
* 09/30/2012
*
* HtmlFoldParser.java - Fold parser for HTML 5 and PHP.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -19,10 +17,9 @@ import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* Fold parser for HTML 5, PHP and JSP. For HTML, we currently don't fold
* <em>everything</em> possible, just the "big" stuff. For PHP, we only fold
* Fold parser for HTML 5, PHP and JSP. For HTML, we currently don't fold
* <em>everything</em> possible, just the "big" stuff. For PHP, we only fold
* the "big" HTML stuff and PHP regions, not code blocks in the actual PHP.
* For JSP we only fold the "big" HTML stuff and JSP blocks, not anything in
* the actual Java code.
@@ -31,51 +28,41 @@ import org.fife.ui.rsyntaxtextarea.Token;
* @version 1.0
*/
public class HtmlFoldParser implements FoldParser {
/**
* Constant denoting we're folding HTML.
*/
public static final int LANGUAGE_HTML = -1;
public static final int LANGUAGE_HTML = -1;
/**
* Constant denoting we're folding PHP.
*/
public static final int LANGUAGE_PHP = 0;
public static final int LANGUAGE_PHP = 0;
/**
* Constant denoting we're folding JSP.
*/
public static final int LANGUAGE_JSP = 1;
public static final int LANGUAGE_JSP = 1;
/**
* The language we're folding.
*/
private final int language;
/**
* The set of tags we allow to be folded. These are tags that must have
* The set of tags we allow to be folded. These are tags that must have
* explicit close tags in both HTML 4 and HTML 5.
*/
private static final Set<String> FOLDABLE_TAGS;
private static final char[] MARKUP_CLOSING_TAG_START = "</".toCharArray();
//private static final char[] MARKUP_SHORT_TAG_END = "/>".toCharArray();
// private static final char[] MARKUP_SHORT_TAG_END = "/>".toCharArray();
private static final char[] MLC_START = "<!--".toCharArray();
private static final char[] MLC_END = "-->".toCharArray();
private static final char[] MLC_END = "-->".toCharArray();
private static final char[] PHP_START = "<?".toCharArray(); // <? and <?php
private static final char[] PHP_END = "?>".toCharArray();
// Scriptlets, declarations, and expressions all start the same way.
private static final char[] JSP_START = "<%".toCharArray();
private static final char[] JSP_END = "%>".toCharArray();
private static final char[][] LANG_START = { PHP_START, JSP_START };
private static final char[][] LANG_END = { PHP_END, JSP_END };
private static final char[][] LANG_END = { PHP_END, JSP_END };
private static final char[] JSP_COMMENT_START = "<%--".toCharArray();
private static final char[] JSP_COMMENT_END = "--%>".toCharArray();
private static final char[] JSP_COMMENT_END = "--%>".toCharArray();
static {
FOLDABLE_TAGS = new HashSet<String>();
FOLDABLE_TAGS.add("body");
@@ -96,60 +83,50 @@ public class HtmlFoldParser implements FoldParser {
FOLDABLE_TAGS.add("td");
FOLDABLE_TAGS.add("ul");
}
/**
* Constructor.
*
* @param language The language to fold, such as {@link #LANGUAGE_PHP}.
* @param language
* The language to fold, such as {@link #LANGUAGE_PHP}.
*/
public HtmlFoldParser(int language) {
if (language<LANGUAGE_HTML && language>LANGUAGE_JSP) {
if (language < LANGUAGE_HTML && language > LANGUAGE_JSP) {
throw new IllegalArgumentException("Invalid language: " + language);
}
this.language = language;
}
/**
* {@inheritDoc}
*/
@Override
public List<Fold> getFolds(RSyntaxTextArea textArea) {
List<Fold> folds = new ArrayList<Fold>();
Stack<String> tagNameStack = new Stack<String>();
boolean inSublanguage = false;
Fold currentFold = null;
int lineCount = textArea.getLineCount();
boolean inMLC = false;
boolean inJSMLC = false;
TagCloseInfo tci = new TagCloseInfo();
try {
for (int line=0; line<lineCount; line++) {
for (int line = 0; line < lineCount; line++) {
Token t = textArea.getTokenListForLine(line);
while (t!=null && t.isPaintable()) {
// If we're folding PHP. Note that PHP folding can only be
while (t != null && t.isPaintable()) {
// If we're folding PHP. Note that PHP folding can only be
// "one level deep," so our logic here is simple.
if (language>=0 && t.getType()==Token.SEPARATOR) {
if (language >= 0 && t.getType() == Token.SEPARATOR) {
// <?, <?php, <%, <%!, ...
if (t.startsWith(LANG_START[language])) {
if (currentFold==null) {
if (currentFold == null) {
currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
}
inSublanguage = true;
}
// ?> or %>
else if (t.startsWith(LANG_END[language]) && currentFold != null) {
int phpEnd = t.getEndOffset() - 1;
@@ -164,21 +141,19 @@ public class HtmlFoldParser implements FoldParser {
t = t.getNextToken();
continue;
}
}
if (!inSublanguage) {
if (t.getType()==Token.COMMENT_MULTILINE) {
if (t.getType() == Token.COMMENT_MULTILINE) {
// Continuing an MLC from a previous line
if (inMLC) {
// Found the end of the MLC starting on a previous line...
// Found the end of the MLC starting on a
// previous line...
if (t.endsWith(MLC_END)) {
int mlcEnd = t.getEndOffset() - 1;
currentFold.setEndOffset(mlcEnd);
Fold parentFold = currentFold.getParent();
// Don't add fold markers for single-line blocks
// Don't add fold markers for single-line
// blocks
if (currentFold.isOnSingleLine()) {
removeFold(currentFold, folds);
}
@@ -188,15 +163,16 @@ public class HtmlFoldParser implements FoldParser {
// Otherwise, this MLC is continuing on to yet
// another line.
}
// Continuing a JS MLC from a previous line
else if (inJSMLC) {
// Found the end of the MLC starting on a previous line...
// Found the end of the MLC starting on a
// previous line...
if (t.endsWith(JSP_COMMENT_END)) {
int mlcEnd = t.getEndOffset() - 1;
currentFold.setEndOffset(mlcEnd);
Fold parentFold = currentFold.getParent();
// Don't add fold markers for single-line blocks
// Don't add fold markers for single-line
// blocks
if (currentFold.isOnSingleLine()) {
removeFold(currentFold, folds);
}
@@ -206,56 +182,48 @@ public class HtmlFoldParser implements FoldParser {
// Otherwise, this MLC is continuing on to yet
// another line.
}
// Starting a MLC that ends on a later line...
else if (t.startsWith(MLC_START) && !t.endsWith(MLC_END)) {
if (currentFold==null) {
if (currentFold == null) {
currentFold = new Fold(FoldType.COMMENT, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.COMMENT, t.getOffset());
}
inMLC = true;
}
// Starting a JSP comment that ends on a later line...
else if (language==LANGUAGE_JSP &&
t.startsWith(JSP_COMMENT_START) &&
!t.endsWith(JSP_COMMENT_END)) {
if (currentFold==null) {
// Starting a JSP comment that ends on a later
// line...
else if (language == LANGUAGE_JSP && t.startsWith(JSP_COMMENT_START) && !t.endsWith(JSP_COMMENT_END)) {
if (currentFold == null) {
currentFold = new Fold(FoldType.COMMENT, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.COMMENT, t.getOffset());
}
inJSMLC = true;
}
}
// If we're starting a new tag...
else if (t.isSingleChar(Token.MARKUP_TAG_DELIMITER, '<')) {
Token tagStartToken = t;
Token tagNameToken = t.getNextToken();
if (isFoldableTag(tagNameToken)) {
int newLine = getTagCloseInfo(tagNameToken, textArea, line, tci);
if (tci.line==-1) { // EOF reached before end of tag
if (tci.line == -1) { // EOF reached before end
// of tag
return folds;
}
// We have found either ">" or "/>" with tci.
//System.out.println(line + ", "+ tci + ", " + t);
// System.out.println(line + ", "+ tci + ", " +
// t);
Token tagCloseToken = tci.closeToken;
if (tagCloseToken.isSingleChar(Token.MARKUP_TAG_DELIMITER, '>')) {
if (currentFold==null) {
currentFold = new Fold(FoldType.CODE, textArea,
tagStartToken.getOffset());
if (currentFold == null) {
currentFold = new Fold(FoldType.CODE, textArea, tagStartToken.getOffset());
folds.add(currentFold);
}
else {
currentFold = currentFold.createChild(FoldType.CODE,
tagStartToken.getOffset());
} else {
currentFold = currentFold.createChild(FoldType.CODE, tagStartToken.getOffset());
}
tagNameStack.push(tagNameToken.getLexeme());
}
@@ -263,17 +231,16 @@ public class HtmlFoldParser implements FoldParser {
line = newLine;
}
}
// If we've found a closing tag (e.g. "</div>").
else if (t.is(Token.MARKUP_TAG_DELIMITER, MARKUP_CLOSING_TAG_START)) {
if (currentFold!=null) {
if (currentFold != null) {
Token tagNameToken = t.getNextToken();
if (isFoldableTag(tagNameToken) &&
isEndOfLastFold(tagNameStack, tagNameToken)) {
if (isFoldableTag(tagNameToken) && isEndOfLastFold(tagNameStack, tagNameToken)) {
tagNameStack.pop();
currentFold.setEndOffset(t.getOffset());
Fold parentFold = currentFold.getParent();
// Don't add fold markers for single-line blocks
// Don't add fold markers for single-line
// blocks
if (currentFold.isOnSingleLine()) {
removeFold(currentFold, folds);
}
@@ -282,131 +249,111 @@ public class HtmlFoldParser implements FoldParser {
}
}
}
}
t = t.getNextToken();
}
}
} catch (BadLocationException ble) { // Should never happen
ble.printStackTrace();
}
return folds;
}
/**
* Grabs the token representing the closing of a tag (i.e.
* "<code>&gt;</code>" or "<code>/&gt;</code>"). This should only be
* called after a tag name has been parsed, to ensure the "closing" of
* "<code>&gt;</code>" or "<code>/&gt;</code>"). This should only be
* called after a tag name has been parsed, to ensure the "closing" of
* other tags is not identified.
*
* @param tagNameToken The token denoting the name of the tag.
* @param textArea The text area whose contents are being parsed.
* @param line The line we're currently on.
* @param info On return, information about the closing of the tag is
* returned in this object.
* @param tagNameToken
* The token denoting the name of the tag.
* @param textArea
* The text area whose contents are being parsed.
* @param line
* The line we're currently on.
* @param info
* On return, information about the closing of the tag is
* returned in this object.
* @return The line number of the closing tag token.
*/
private int getTagCloseInfo(Token tagNameToken, RSyntaxTextArea textArea,
int line, TagCloseInfo info) {
private int getTagCloseInfo(Token tagNameToken, RSyntaxTextArea textArea, int line, TagCloseInfo info) {
info.reset();
Token t = tagNameToken.getNextToken();
do {
while (t!=null && t.getType()!=Token.MARKUP_TAG_DELIMITER) {
while (t != null && t.getType() != Token.MARKUP_TAG_DELIMITER) {
t = t.getNextToken();
}
if (t!=null) {
if (t != null) {
info.closeToken = t;
info.line = line;
break;
}
} while (++line<textArea.getLineCount() &&
(t=textArea.getTokenListForLine(line))!=null);
} while (++line < textArea.getLineCount() && (t = textArea.getTokenListForLine(line)) != null);
return line;
}
/**
* Returns whether a closing tag ("<code>&lt;/...&gt;</code>") with a
* specific name is the closing tag of our current fold region.
*
* @param tagNameStack The stack of fold regions.
* @param tagNameToken The tag name of the most recently parsed closing
* tag.
* @param tagNameStack
* The stack of fold regions.
* @param tagNameToken
* The tag name of the most recently parsed closing
* tag.
* @return Whether it's the end of the current fold region.
*/
private static boolean isEndOfLastFold(Stack<String> tagNameStack,
Token tagNameToken) {
if (tagNameToken!=null && !tagNameStack.isEmpty()) {
private static boolean isEndOfLastFold(Stack<String> tagNameStack, Token tagNameToken) {
if (tagNameToken != null && !tagNameStack.isEmpty()) {
return tagNameToken.getLexeme().equalsIgnoreCase(tagNameStack.peek());
}
return false;
}
/**
* Returns whether a tag is one we allow as a foldable region.
*
* @param tagNameToken The tag's name token. This may be <code>null</code>.
* @param tagNameToken
* The tag's name token. This may be <code>null</code>.
* @return Whether this tag can be a foldable region.
*/
private static boolean isFoldableTag(Token tagNameToken) {
return tagNameToken!=null &&
FOLDABLE_TAGS.contains(tagNameToken.getLexeme().toLowerCase());
return tagNameToken != null && FOLDABLE_TAGS.contains(tagNameToken.getLexeme().toLowerCase());
}
/**
* If this fold has a parent fold, this method removes it from its parent.
* Otherwise, it's assumed to be the most recent (top-level) fold in the
* <code>folds</code> list, and is removed from that.
*
* @param fold The fold to remove.
* @param folds The list of top-level folds.
* @param fold
* The fold to remove.
* @param folds
* The list of top-level folds.
*/
private static void removeFold(Fold fold, List<Fold> folds) {
if (!fold.removeFromParent()) {
folds.remove(folds.size()-1);
folds.remove(folds.size() - 1);
}
}
/**
* A simple wrapper for the token denoting the closing of a tag (i.e.
* "<code>&gt;</code>" or "<code>/&gt;</code>").
*/
private static class TagCloseInfo {
private Token closeToken;
private int line;
public void reset() {
closeToken = null;
line = -1;
}
@Override
public String toString() {
return "[TagCloseInfo: " +
"closeToken=" + closeToken +
", line=" + line +
"]";
return "[TagCloseInfo: " + "closeToken=" + closeToken + ", line=" + line + "]";
}
}
}
}
@@ -1,9 +1,7 @@
/*
* 12/23/2012
*
* JsonFoldParser.java - Fold parser for JSON.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -18,9 +16,8 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rsyntaxtextarea.TokenTypes;
/**
* The fold parser for JSON. Objects (<code>"{ ... }</code>") and arrays
* The fold parser for JSON. Objects (<code>"{ ... }</code>") and arrays
* (<code>"[ ... ]"</code>) that span multiple lines are considered fold
* regions.
*
@@ -28,136 +25,116 @@ import org.fife.ui.rsyntaxtextarea.TokenTypes;
* @version 1.0
*/
public class JsonFoldParser implements FoldParser {
private static final Object OBJECT_BLOCK = new Object();
private static final Object ARRAY_BLOCK = new Object();
/**
* {@inheritDoc}
*/
@Override
public List<Fold> getFolds(RSyntaxTextArea textArea) {
Stack<Object> blocks = new Stack<Object>();
List<Fold> folds = new ArrayList<Fold>();
Fold currentFold = null;
int lineCount = textArea.getLineCount();
try {
for (int line=0; line<lineCount; line++) {
for (int line = 0; line < lineCount; line++) {
Token t = textArea.getTokenListForLine(line);
while (t!=null && t.isPaintable()) {
while (t != null && t.isPaintable()) {
if (t.isLeftCurly()) {
if (currentFold==null) {
if (currentFold == null) {
currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
}
blocks.push(OBJECT_BLOCK);
}
else if (t.isRightCurly() && popOffTop(blocks, OBJECT_BLOCK)) {
if (currentFold!=null) {
} else if (t.isRightCurly() && popOffTop(blocks, OBJECT_BLOCK)) {
if (currentFold != null) {
currentFold.setEndOffset(t.getOffset());
Fold parentFold = currentFold.getParent();
//System.out.println("... Adding regular fold at " + t.offset + ", parent==" + parentFold);
// System.out.println("... Adding regular fold at "
// + t.offset + ", parent==" + parentFold);
// Don't add fold markers for single-line blocks
if (currentFold.isOnSingleLine()) {
if (!currentFold.removeFromParent()) {
folds.remove(folds.size()-1);
folds.remove(folds.size() - 1);
}
}
currentFold = parentFold;
}
}
else if (isLeftBracket(t)) {
if (currentFold==null) {
} else if (isLeftBracket(t)) {
if (currentFold == null) {
currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
}
blocks.push(ARRAY_BLOCK);
}
else if (isRightBracket(t) && popOffTop(blocks, ARRAY_BLOCK)) {
if (currentFold!=null) {
} else if (isRightBracket(t) && popOffTop(blocks, ARRAY_BLOCK)) {
if (currentFold != null) {
currentFold.setEndOffset(t.getOffset());
Fold parentFold = currentFold.getParent();
//System.out.println("... Adding regular fold at " + t.offset + ", parent==" + parentFold);
// System.out.println("... Adding regular fold at "
// + t.offset + ", parent==" + parentFold);
// Don't add fold markers for single-line blocks
if (currentFold.isOnSingleLine()) {
if (!currentFold.removeFromParent()) {
folds.remove(folds.size()-1);
folds.remove(folds.size() - 1);
}
}
currentFold = parentFold;
}
}
t = t.getNextToken();
}
}
} catch (BadLocationException ble) { // Should never happen
ble.printStackTrace();
}
return folds;
}
/**
* Returns whether a token is the left bracket token.
*
* @param t The token.
* @param t
* The token.
* @return Whether the token is the left bracket token.
* @see #isRightBracket(Token)
*/
private static boolean isLeftBracket(Token t) {
return t.getType()==TokenTypes.SEPARATOR && t.isSingleChar('[');
return t.getType() == TokenTypes.SEPARATOR && t.isSingleChar('[');
}
/**
* Returns whether a token is the right bracket token.
*
* @param t The token.
* @param t
* The token.
* @return Whether the token is the right bracket token.
* @see #isLeftBracket(Token)
*/
private static boolean isRightBracket(Token t) {
return t.getType()==TokenTypes.SEPARATOR && t.isSingleChar(']');
return t.getType() == TokenTypes.SEPARATOR && t.isSingleChar(']');
}
/**
* If the specified value is on top of the stack, pop it off and return
* <code>true</code>. Otherwise, return <code>false</code>.
* <code>true</code>. Otherwise, return <code>false</code>.
*
* @param stack The stack.
* @param value The value to check for.
* @param stack
* The stack.
* @param value
* The value to check for.
* @return Whether the value was found on top of the stack.
*/
private static boolean popOffTop(Stack<Object> stack, Object value) {
if (stack.size()>0 && stack.peek()==value) {
if (stack.size() > 0 && stack.peek() == value) {
stack.pop();
return true;
}
return false;
}
}
}
@@ -1,9 +1,7 @@
/*
* 04/24/2012
*
* LatexFoldParser.java - Fold parser for LaTeX.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -11,77 +9,67 @@ package org.fife.ui.rsyntaxtextarea.folding;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* A fold parser for LaTeX documents. This is likely incomplete and/or not
* A fold parser for LaTeX documents. This is likely incomplete and/or not
* quite right; feedback is appreciated.
*
* @author Robert Futrell
* @version 1.0
*/
public class LatexFoldParser implements FoldParser {
private static final char[] BEGIN = "\\begin".toCharArray();
private static final char[] END = "\\end".toCharArray();
/**
* {@inheritDoc}
*/
@Override
public List<Fold> getFolds(RSyntaxTextArea textArea) {
List<Fold> folds = new ArrayList<Fold>();
Stack<String> expectedStack = new Stack<String>();
Fold currentFold = null;
int lineCount = textArea.getLineCount();
try {
for (int line=0; line<lineCount; line++) {
for (int line = 0; line < lineCount; line++) {
Token t = textArea.getTokenListForLine(line);
while (t!=null && t.isPaintable()) {
while (t != null && t.isPaintable()) {
if (t.is(Token.RESERVED_WORD, BEGIN)) {
Token temp = t.getNextToken();
if (temp!=null && temp.isLeftCurly()) {
if (temp != null && temp.isLeftCurly()) {
temp = temp.getNextToken();
if (temp!=null && temp.getType()==Token.RESERVED_WORD) {
if (currentFold==null) {
if (temp != null && temp.getType() == Token.RESERVED_WORD) {
if (currentFold == null) {
currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
}
expectedStack.push(temp.getLexeme());
t = temp;
}
}
}
else if (t.is(Token.RESERVED_WORD, END) &&
currentFold!=null && !expectedStack.isEmpty()) {
} else if (t.is(Token.RESERVED_WORD, END) && currentFold != null && !expectedStack.isEmpty()) {
Token temp = t.getNextToken();
if (temp!=null && temp.isLeftCurly()) {
if (temp != null && temp.isLeftCurly()) {
temp = temp.getNextToken();
if (temp!=null && temp.getType()==Token.RESERVED_WORD) {
if (temp != null && temp.getType() == Token.RESERVED_WORD) {
String value = temp.getLexeme();
if (expectedStack.peek().equals(value)) {
expectedStack.pop();
currentFold.setEndOffset(t.getOffset());
Fold parentFold = currentFold.getParent();
// Don't add fold markers for single-line blocks
// Don't add fold markers for single-line
// blocks
if (currentFold.isOnSingleLine()) {
if (!currentFold.removeFromParent()) {
folds.remove(folds.size()-1);
folds.remove(folds.size() - 1);
}
}
t = temp;
@@ -90,20 +78,12 @@ public class LatexFoldParser implements FoldParser {
}
}
}
t = t.getNextToken();
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
return folds;
}
}
}
@@ -1,16 +1,13 @@
/*
* 08/08/2012
*
* LispFoldParser.java - Fold parser for Lisp and related languages.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* Fold parser for Lisp and related languages.
*
@@ -18,18 +15,14 @@ import org.fife.ui.rsyntaxtextarea.Token;
* @version 1.0
*/
public class LispFoldParser extends CurlyFoldParser {
@Override
public boolean isLeftCurly(Token t) {
return t.isSingleChar(Token.SEPARATOR, '(');
}
@Override
public boolean isRightCurly(Token t) {
return t.isSingleChar(Token.SEPARATOR, ')');
}
}
}
@@ -1,9 +1,7 @@
/*
* 10/07/2012
*
* NsisFoldParser.java - Fold parser for NSIS.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -17,9 +15,9 @@ import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* A fold parser NSIS.<p>
* A fold parser NSIS.
* <p>
*
* Note that this class may impose somewhat of a performance penalty on large
* source files, since it re-parses the entire document each time folds are
@@ -29,130 +27,101 @@ import org.fife.ui.rsyntaxtextarea.Token;
* @version 1.0
*/
public class NsisFoldParser implements FoldParser {
private static final char[] KEYWORD_FUNCTION = "Function".toCharArray();
private static final char[] KEYWORD_FUNCTION_END = "FunctionEnd".toCharArray();
private static final char[] KEYWORD_SECTION = "Section".toCharArray();
private static final char[] KEYWORD_SECTION_END = "SectionEnd".toCharArray();
private static final char[] KEYWORD_FUNCTION = "Function".toCharArray();
private static final char[] KEYWORD_FUNCTION_END = "FunctionEnd".toCharArray();
private static final char[] KEYWORD_SECTION = "Section".toCharArray();
private static final char[] KEYWORD_SECTION_END = "SectionEnd".toCharArray();
protected static final char[] C_MLC_END = "*/".toCharArray();
private static boolean foundEndKeyword(char[] keyword, Token t,
Stack<char[]> endWordStack) {
return t.is(Token.RESERVED_WORD, keyword) && !endWordStack.isEmpty() &&
keyword==endWordStack.peek();
private static boolean foundEndKeyword(char[] keyword, Token t, Stack<char[]> endWordStack) {
return t.is(Token.RESERVED_WORD, keyword) && !endWordStack.isEmpty() && keyword == endWordStack.peek();
}
/**
* {@inheritDoc}
*/
@Override
public List<Fold> getFolds(RSyntaxTextArea textArea) {
List<Fold> folds = new ArrayList<Fold>();
Fold currentFold = null;
int lineCount = textArea.getLineCount();
boolean inMLC = false;
int mlcStart = 0;
Stack<char[]> endWordStack = new Stack<char[]>();
try {
for (int line=0; line<lineCount; line++) {
for (int line = 0; line < lineCount; line++) {
Token t = textArea.getTokenListForLine(line);
while (t!=null && t.isPaintable()) {
while (t != null && t.isPaintable()) {
if (t.isComment()) {
if (inMLC) {
// If we found the end of an MLC that started
// on a previous line...
if (t.endsWith(C_MLC_END)) {
int mlcEnd = t.getEndOffset() - 1;
if (currentFold==null) {
if (currentFold == null) {
currentFold = new Fold(FoldType.COMMENT, textArea, mlcStart);
currentFold.setEndOffset(mlcEnd);
folds.add(currentFold);
currentFold = null;
}
else {
} else {
currentFold = currentFold.createChild(FoldType.COMMENT, mlcStart);
currentFold.setEndOffset(mlcEnd);
currentFold = currentFold.getParent();
}
//System.out.println("Ending MLC at: " + mlcEnd + ", parent==" + currentFold);
// System.out.println("Ending MLC at: " + mlcEnd
// + ", parent==" + currentFold);
inMLC = false;
mlcStart = 0;
}
// Otherwise, this MLC is continuing on to yet
// another line.
}
else {
} else {
// If we're an MLC that ends on a later line...
if (t.getType()!=Token.COMMENT_EOL && !t.endsWith(C_MLC_END)) {
//System.out.println("Starting MLC at: " + t.offset);
if (t.getType() != Token.COMMENT_EOL && !t.endsWith(C_MLC_END)) {
// System.out.println("Starting MLC at: " +
// t.offset);
inMLC = true;
mlcStart = t.getOffset();
}
}
}
else if (t.is(Token.RESERVED_WORD, KEYWORD_SECTION)) {
if (currentFold==null) {
} else if (t.is(Token.RESERVED_WORD, KEYWORD_SECTION)) {
if (currentFold == null) {
currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
}
endWordStack.push(KEYWORD_SECTION_END);
}
else if (t.is(Token.RESERVED_WORD, KEYWORD_FUNCTION)) {
if (currentFold==null) {
} else if (t.is(Token.RESERVED_WORD, KEYWORD_FUNCTION)) {
if (currentFold == null) {
currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
}
endWordStack.push(KEYWORD_FUNCTION_END);
}
else if (foundEndKeyword(KEYWORD_SECTION_END, t, endWordStack) ||
foundEndKeyword(KEYWORD_FUNCTION_END, t, endWordStack)) {
if (currentFold!=null) {
} else if (foundEndKeyword(KEYWORD_SECTION_END, t, endWordStack)
|| foundEndKeyword(KEYWORD_FUNCTION_END, t, endWordStack)) {
if (currentFold != null) {
currentFold.setEndOffset(t.getOffset());
Fold parentFold = currentFold.getParent();
endWordStack.pop();
// Don't add fold markers for single-line blocks
if (currentFold.isOnSingleLine()) {
if (!currentFold.removeFromParent()) {
folds.remove(folds.size()-1);
folds.remove(folds.size() - 1);
}
}
currentFold = parentFold;
}
}
t = t.getNextToken();
}
}
} catch (BadLocationException ble) { // Should never happen
ble.printStackTrace();
}
return folds;
}
}
}
@@ -1,9 +1,7 @@
/*
* 10/23/2011
*
* XmlFoldParser.java - Fold parser for XML.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
@@ -16,9 +14,8 @@ import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* Fold parser for XML. Any tags that span more than one line, as well as
* Fold parser for XML. Any tags that span more than one line, as well as
* comment regions spanning more than one line, are identified as foldable
* regions.
*
@@ -26,46 +23,38 @@ import org.fife.ui.rsyntaxtextarea.Token;
* @version 1.0
*/
public class XmlFoldParser implements FoldParser {
private static final char[] MARKUP_CLOSING_TAG_START = { '<', '/' };
private static final char[] MARKUP_SHORT_TAG_END = { '/', '>' };
private static final char[] MLC_END = { '-', '-', '>' };
/**
* {@inheritDoc}
*/
@Override
public List<Fold> getFolds(RSyntaxTextArea textArea) {
List<Fold> folds = new ArrayList<Fold>();
Fold currentFold = null;
int lineCount = textArea.getLineCount();
boolean inMLC = false;
int mlcStart = 0;
try {
for (int line=0; line<lineCount; line++) {
for (int line = 0; line < lineCount; line++) {
Token t = textArea.getTokenListForLine(line);
while (t!=null && t.isPaintable()) {
while (t != null && t.isPaintable()) {
if (t.isComment()) {
// Continuing an MLC from a previous line
if (inMLC) {
// Found the end of the MLC starting on a previous line...
// Found the end of the MLC starting on a previous
// line...
if (t.endsWith(MLC_END)) {
int mlcEnd = t.getEndOffset() - 1;
if (currentFold==null) {
if (currentFold == null) {
currentFold = new Fold(FoldType.COMMENT, textArea, mlcStart);
currentFold.setEndOffset(mlcEnd);
folds.add(currentFold);
currentFold = null;
}
else {
} else {
currentFold = currentFold.createChild(FoldType.COMMENT, mlcStart);
currentFold.setEndOffset(mlcEnd);
currentFold = currentFold.getParent();
@@ -75,38 +64,28 @@ public class XmlFoldParser implements FoldParser {
}
// Otherwise, this MLC is continuing on to yet
// another line.
}
else {
} else {
// If we're an MLC that ends on a later line...
if (t.getType()==Token.COMMENT_MULTILINE && !t.endsWith(MLC_END)) {
if (t.getType() == Token.COMMENT_MULTILINE && !t.endsWith(MLC_END)) {
inMLC = true;
mlcStart = t.getOffset();
}
}
}
else if (t.isSingleChar(Token.MARKUP_TAG_DELIMITER, '<')) {
if (currentFold==null) {
} else if (t.isSingleChar(Token.MARKUP_TAG_DELIMITER, '<')) {
if (currentFold == null) {
currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
folds.add(currentFold);
}
else {
} else {
currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
}
}
else if (t.is(Token.MARKUP_TAG_DELIMITER, MARKUP_SHORT_TAG_END)) {
if (currentFold!=null) {
} else if (t.is(Token.MARKUP_TAG_DELIMITER, MARKUP_SHORT_TAG_END)) {
if (currentFold != null) {
Fold parentFold = currentFold.getParent();
removeFold(currentFold, folds);
currentFold = parentFold;
}
}
else if (t.is(Token.MARKUP_TAG_DELIMITER, MARKUP_CLOSING_TAG_START)) {
if (currentFold!=null) {
} else if (t.is(Token.MARKUP_TAG_DELIMITER, MARKUP_CLOSING_TAG_START)) {
if (currentFold != null) {
currentFold.setEndOffset(t.getOffset());
Fold parentFold = currentFold.getParent();
// Don't add fold markers for single-line blocks
@@ -116,35 +95,28 @@ public class XmlFoldParser implements FoldParser {
currentFold = parentFold;
}
}
t = t.getNextToken();
}
}
} catch (BadLocationException ble) { // Should never happen
ble.printStackTrace();
}
return folds;
}
/**
* If this fold has a parent fold, this method removes it from its parent.
* Otherwise, it's assumed to be the most recent (top-level) fold in the
* <code>folds</code> list, and is removed from that.
*
* @param fold The fold to remove.
* @param folds The list of top-level folds.
* @param fold
* The fold to remove.
* @param folds
* The list of top-level folds.
*/
private static void removeFold(Fold fold, List<Fold> folds) {
if (!fold.removeFromParent()) {
folds.remove(folds.size()-1);
folds.remove(folds.size() - 1);
}
}
}
}
@@ -1,17 +1,14 @@
/*
* 10/03/2009
*
* AbstractMarkupTokenMaker.java - Base class for token makers for markup
* languages.
*
* This library is distributed under a modified BSD license. See the included
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.modes;
import org.fife.ui.rsyntaxtextarea.AbstractJFlexTokenMaker;
/**
* Base class for token makers for markup languages.
*
@@ -19,16 +16,14 @@ import org.fife.ui.rsyntaxtextarea.AbstractJFlexTokenMaker;
* @version 1.0
*/
public abstract class AbstractMarkupTokenMaker extends AbstractJFlexTokenMaker {
/**
* Returns whether markup close tags should be completed.
*
* @return Whether closing markup tags are to be completed.
*/
public abstract boolean getCompleteCloseTags();
/**
* {@inheritDoc}
*/
@@ -36,8 +31,7 @@ public abstract class AbstractMarkupTokenMaker extends AbstractJFlexTokenMaker {
public String[] getLineCommentStartAndEnd(int languageIndex) {
return new String[] { "<!--", "-->" };
}
/**
* Overridden to return <code>true</code>.
*
@@ -47,6 +41,4 @@ public abstract class AbstractMarkupTokenMaker extends AbstractJFlexTokenMaker {
public final boolean isMarkupLanguage() {
return true;
}
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
Loaded 100 of 233 files, more files were not shown because too many files have changed in this diff. Show more