mirror of
https://github.com/esp8266/Arduino.git
synced 2025-07-30 16:24:09 +03:00
Initial Arduino IDE based on Processing.
This commit is contained in:
274
app/syntax/CTokenMarker.java
Normal file
274
app/syntax/CTokenMarker.java
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* CTokenMarker.java - C token marker
|
||||
* Copyright (C) 1998, 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
/**
|
||||
* C token marker.
|
||||
*
|
||||
* @author Slava Pestov
|
||||
* @version $Id: CTokenMarker.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
|
||||
*/
|
||||
public class CTokenMarker extends TokenMarker
|
||||
{
|
||||
public CTokenMarker()
|
||||
{
|
||||
this(true,getKeywords());
|
||||
}
|
||||
|
||||
public CTokenMarker(boolean cpp, KeywordMap keywords)
|
||||
{
|
||||
this.cpp = cpp;
|
||||
this.keywords = keywords;
|
||||
}
|
||||
|
||||
public byte markTokensImpl(byte token, Segment line, int lineIndex)
|
||||
{
|
||||
char[] array = line.array;
|
||||
int offset = line.offset;
|
||||
lastOffset = offset;
|
||||
lastKeyword = offset;
|
||||
int mlength = line.count + offset;
|
||||
boolean backslash = false;
|
||||
|
||||
loop: for(int i = offset; i < mlength; i++)
|
||||
{
|
||||
int i1 = (i+1);
|
||||
|
||||
char c = array[i];
|
||||
if(c == '\\')
|
||||
{
|
||||
backslash = !backslash;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(token)
|
||||
{
|
||||
case Token.NULL:
|
||||
switch(c)
|
||||
{
|
||||
case '#':
|
||||
if(backslash)
|
||||
backslash = false;
|
||||
else if(cpp)
|
||||
{
|
||||
if(doKeyword(line,i,c))
|
||||
break;
|
||||
addToken(i - lastOffset,token);
|
||||
addToken(mlength - i,Token.KEYWORD2);
|
||||
lastOffset = lastKeyword = mlength;
|
||||
break loop;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
doKeyword(line,i,c);
|
||||
if(backslash)
|
||||
backslash = false;
|
||||
else
|
||||
{
|
||||
addToken(i - lastOffset,token);
|
||||
token = Token.LITERAL1;
|
||||
lastOffset = lastKeyword = i;
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
doKeyword(line,i,c);
|
||||
if(backslash)
|
||||
backslash = false;
|
||||
else
|
||||
{
|
||||
addToken(i - lastOffset,token);
|
||||
token = Token.LITERAL2;
|
||||
lastOffset = lastKeyword = i;
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
if(lastKeyword == offset)
|
||||
{
|
||||
if(doKeyword(line,i,c))
|
||||
break;
|
||||
backslash = false;
|
||||
addToken(i1 - lastOffset,Token.LABEL);
|
||||
lastOffset = lastKeyword = i1;
|
||||
}
|
||||
else if(doKeyword(line,i,c))
|
||||
break;
|
||||
break;
|
||||
case '/':
|
||||
backslash = false;
|
||||
doKeyword(line,i,c);
|
||||
if(mlength - i > 1)
|
||||
{
|
||||
switch(array[i1])
|
||||
{
|
||||
case '*':
|
||||
addToken(i - lastOffset,token);
|
||||
lastOffset = lastKeyword = i;
|
||||
if(mlength - i > 2 && array[i+2] == '*')
|
||||
token = Token.COMMENT2;
|
||||
else
|
||||
token = Token.COMMENT1;
|
||||
break;
|
||||
case '/':
|
||||
addToken(i - lastOffset,token);
|
||||
addToken(mlength - i,Token.COMMENT1);
|
||||
lastOffset = lastKeyword = mlength;
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
backslash = false;
|
||||
if(!Character.isLetterOrDigit(c)
|
||||
&& c != '_')
|
||||
doKeyword(line,i,c);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Token.COMMENT1:
|
||||
case Token.COMMENT2:
|
||||
backslash = false;
|
||||
if(c == '*' && mlength - i > 1)
|
||||
{
|
||||
if(array[i1] == '/')
|
||||
{
|
||||
i++;
|
||||
addToken((i+1) - lastOffset,token);
|
||||
token = Token.NULL;
|
||||
lastOffset = lastKeyword = i+1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Token.LITERAL1:
|
||||
if(backslash)
|
||||
backslash = false;
|
||||
else if(c == '"')
|
||||
{
|
||||
addToken(i1 - lastOffset,token);
|
||||
token = Token.NULL;
|
||||
lastOffset = lastKeyword = i1;
|
||||
}
|
||||
break;
|
||||
case Token.LITERAL2:
|
||||
if(backslash)
|
||||
backslash = false;
|
||||
else if(c == '\'')
|
||||
{
|
||||
addToken(i1 - lastOffset,Token.LITERAL1);
|
||||
token = Token.NULL;
|
||||
lastOffset = lastKeyword = i1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InternalError("Invalid state: "
|
||||
+ token);
|
||||
}
|
||||
}
|
||||
|
||||
if(token == Token.NULL)
|
||||
doKeyword(line,mlength,'\0');
|
||||
|
||||
switch(token)
|
||||
{
|
||||
case Token.LITERAL1:
|
||||
case Token.LITERAL2:
|
||||
addToken(mlength - lastOffset,Token.INVALID);
|
||||
token = Token.NULL;
|
||||
break;
|
||||
case Token.KEYWORD2:
|
||||
addToken(mlength - lastOffset,token);
|
||||
if (!backslash) token = Token.NULL;
|
||||
addToken(mlength - lastOffset,token);
|
||||
break;
|
||||
default:
|
||||
addToken(mlength - lastOffset,token);
|
||||
break;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
public static KeywordMap getKeywords()
|
||||
{
|
||||
if(cKeywords == null)
|
||||
{
|
||||
cKeywords = new KeywordMap(false);
|
||||
cKeywords.add("char",Token.KEYWORD3);
|
||||
cKeywords.add("double",Token.KEYWORD3);
|
||||
cKeywords.add("enum",Token.KEYWORD3);
|
||||
cKeywords.add("float",Token.KEYWORD3);
|
||||
cKeywords.add("int",Token.KEYWORD3);
|
||||
cKeywords.add("long",Token.KEYWORD3);
|
||||
cKeywords.add("short",Token.KEYWORD3);
|
||||
cKeywords.add("signed",Token.KEYWORD3);
|
||||
cKeywords.add("struct",Token.KEYWORD3);
|
||||
cKeywords.add("typedef",Token.KEYWORD3);
|
||||
cKeywords.add("union",Token.KEYWORD3);
|
||||
cKeywords.add("unsigned",Token.KEYWORD3);
|
||||
cKeywords.add("void",Token.KEYWORD3);
|
||||
cKeywords.add("auto",Token.KEYWORD1);
|
||||
cKeywords.add("const",Token.KEYWORD1);
|
||||
cKeywords.add("extern",Token.KEYWORD1);
|
||||
cKeywords.add("register",Token.KEYWORD1);
|
||||
cKeywords.add("static",Token.KEYWORD1);
|
||||
cKeywords.add("volatile",Token.KEYWORD1);
|
||||
cKeywords.add("break",Token.KEYWORD1);
|
||||
cKeywords.add("case",Token.KEYWORD1);
|
||||
cKeywords.add("continue",Token.KEYWORD1);
|
||||
cKeywords.add("default",Token.KEYWORD1);
|
||||
cKeywords.add("do",Token.KEYWORD1);
|
||||
cKeywords.add("else",Token.KEYWORD1);
|
||||
cKeywords.add("for",Token.KEYWORD1);
|
||||
cKeywords.add("goto",Token.KEYWORD1);
|
||||
cKeywords.add("if",Token.KEYWORD1);
|
||||
cKeywords.add("return",Token.KEYWORD1);
|
||||
cKeywords.add("sizeof",Token.KEYWORD1);
|
||||
cKeywords.add("switch",Token.KEYWORD1);
|
||||
cKeywords.add("while",Token.KEYWORD1);
|
||||
cKeywords.add("asm",Token.KEYWORD2);
|
||||
cKeywords.add("asmlinkage",Token.KEYWORD2);
|
||||
cKeywords.add("far",Token.KEYWORD2);
|
||||
cKeywords.add("huge",Token.KEYWORD2);
|
||||
cKeywords.add("inline",Token.KEYWORD2);
|
||||
cKeywords.add("near",Token.KEYWORD2);
|
||||
cKeywords.add("pascal",Token.KEYWORD2);
|
||||
cKeywords.add("true",Token.LITERAL2);
|
||||
cKeywords.add("false",Token.LITERAL2);
|
||||
cKeywords.add("NULL",Token.LITERAL2);
|
||||
}
|
||||
return cKeywords;
|
||||
}
|
||||
|
||||
// private members
|
||||
private static KeywordMap cKeywords;
|
||||
|
||||
private boolean cpp;
|
||||
private KeywordMap keywords;
|
||||
private int lastOffset;
|
||||
private int lastKeyword;
|
||||
|
||||
private boolean doKeyword(Segment line, int i, char c)
|
||||
{
|
||||
int i1 = i+1;
|
||||
|
||||
int len = i - lastKeyword;
|
||||
byte id = keywords.lookup(line,lastKeyword,len);
|
||||
if(id != Token.NULL)
|
||||
{
|
||||
if(lastKeyword != lastOffset)
|
||||
addToken(lastKeyword - lastOffset,Token.NULL);
|
||||
addToken(len,id);
|
||||
lastOffset = i;
|
||||
}
|
||||
lastKeyword = i1;
|
||||
return false;
|
||||
}
|
||||
}
|
374
app/syntax/DefaultInputHandler.java
Normal file
374
app/syntax/DefaultInputHandler.java
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
* DefaultInputHandler.java - Default implementation of an input handler
|
||||
* Copyright (C) 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
import java.awt.event.*;
|
||||
import java.awt.Toolkit;
|
||||
import java.util.Hashtable;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* The default input handler. It maps sequences of keystrokes into actions
|
||||
* and inserts key typed events into the text area.
|
||||
* @author Slava Pestov
|
||||
* @version $Id: DefaultInputHandler.java,v 1.2 2005/05/11 08:34:16 benfry Exp $
|
||||
*/
|
||||
public class DefaultInputHandler extends InputHandler
|
||||
{
|
||||
/**
|
||||
* Creates a new input handler with no key bindings defined.
|
||||
*/
|
||||
public DefaultInputHandler()
|
||||
{
|
||||
bindings = currentBindings = new Hashtable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the default key bindings.
|
||||
*/
|
||||
public void addDefaultKeyBindings()
|
||||
{
|
||||
addKeyBinding("BACK_SPACE",BACKSPACE);
|
||||
addKeyBinding("C+BACK_SPACE",BACKSPACE_WORD);
|
||||
addKeyBinding("DELETE",DELETE);
|
||||
addKeyBinding("C+DELETE",DELETE_WORD);
|
||||
|
||||
addKeyBinding("ENTER",INSERT_BREAK);
|
||||
addKeyBinding("TAB",INSERT_TAB);
|
||||
|
||||
addKeyBinding("INSERT",OVERWRITE);
|
||||
addKeyBinding("C+\\",TOGGLE_RECT);
|
||||
|
||||
addKeyBinding("HOME",HOME);
|
||||
addKeyBinding("END",END);
|
||||
addKeyBinding("S+HOME",SELECT_HOME);
|
||||
addKeyBinding("S+END",SELECT_END);
|
||||
addKeyBinding("C+HOME",DOCUMENT_HOME);
|
||||
addKeyBinding("C+END",DOCUMENT_END);
|
||||
addKeyBinding("CS+HOME",SELECT_DOC_HOME);
|
||||
addKeyBinding("CS+END",SELECT_DOC_END);
|
||||
|
||||
addKeyBinding("PAGE_UP",PREV_PAGE);
|
||||
addKeyBinding("PAGE_DOWN",NEXT_PAGE);
|
||||
addKeyBinding("S+PAGE_UP",SELECT_PREV_PAGE);
|
||||
addKeyBinding("S+PAGE_DOWN",SELECT_NEXT_PAGE);
|
||||
|
||||
addKeyBinding("LEFT",PREV_CHAR);
|
||||
addKeyBinding("S+LEFT",SELECT_PREV_CHAR);
|
||||
addKeyBinding("C+LEFT",PREV_WORD);
|
||||
addKeyBinding("CS+LEFT",SELECT_PREV_WORD);
|
||||
addKeyBinding("RIGHT",NEXT_CHAR);
|
||||
addKeyBinding("S+RIGHT",SELECT_NEXT_CHAR);
|
||||
addKeyBinding("C+RIGHT",NEXT_WORD);
|
||||
addKeyBinding("CS+RIGHT",SELECT_NEXT_WORD);
|
||||
addKeyBinding("UP",PREV_LINE);
|
||||
addKeyBinding("S+UP",SELECT_PREV_LINE);
|
||||
addKeyBinding("DOWN",NEXT_LINE);
|
||||
addKeyBinding("S+DOWN",SELECT_NEXT_LINE);
|
||||
|
||||
addKeyBinding("C+ENTER",REPEAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a key binding to this input handler. The key binding is
|
||||
* a list of white space separated key strokes of the form
|
||||
* <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
|
||||
* or S for Shift, and key is either a character (a-z) or a field
|
||||
* name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
|
||||
* @param keyBinding The key binding
|
||||
* @param action The action
|
||||
*/
|
||||
public void addKeyBinding(String keyBinding, ActionListener action)
|
||||
{
|
||||
Hashtable current = bindings;
|
||||
|
||||
StringTokenizer st = new StringTokenizer(keyBinding);
|
||||
while(st.hasMoreTokens())
|
||||
{
|
||||
KeyStroke keyStroke = parseKeyStroke(st.nextToken());
|
||||
if(keyStroke == null)
|
||||
return;
|
||||
|
||||
if(st.hasMoreTokens())
|
||||
{
|
||||
Object o = current.get(keyStroke);
|
||||
if(o instanceof Hashtable)
|
||||
current = (Hashtable)o;
|
||||
else
|
||||
{
|
||||
o = new Hashtable();
|
||||
current.put(keyStroke,o);
|
||||
current = (Hashtable)o;
|
||||
}
|
||||
}
|
||||
else
|
||||
current.put(keyStroke,action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a key binding from this input handler. This is not yet
|
||||
* implemented.
|
||||
* @param keyBinding The key binding
|
||||
*/
|
||||
public void removeKeyBinding(String keyBinding)
|
||||
{
|
||||
throw new InternalError("Not yet implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all key bindings from this input handler.
|
||||
*/
|
||||
public void removeAllKeyBindings()
|
||||
{
|
||||
bindings.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this input handler that shares the same
|
||||
* key bindings. Setting key bindings in the copy will also
|
||||
* set them in the original.
|
||||
*/
|
||||
public InputHandler copy()
|
||||
{
|
||||
return new DefaultInputHandler(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a key pressed event. This will look up the binding for
|
||||
* the key stroke and execute it.
|
||||
*/
|
||||
public void keyPressed(KeyEvent evt)
|
||||
{
|
||||
int keyCode = evt.getKeyCode();
|
||||
int modifiers = evt.getModifiers();
|
||||
|
||||
// moved this earlier so it doesn't get random meta clicks
|
||||
if (keyCode == KeyEvent.VK_CONTROL ||
|
||||
keyCode == KeyEvent.VK_SHIFT ||
|
||||
keyCode == KeyEvent.VK_ALT ||
|
||||
keyCode == KeyEvent.VK_META) {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't get command-s or other menu key equivs on mac
|
||||
// unless it's something that's specifically bound (cmd-left or right)
|
||||
//if ((modifiers & KeyEvent.META_MASK) != 0) return;
|
||||
if ((modifiers & KeyEvent.META_MASK) != 0) {
|
||||
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers);
|
||||
if (currentBindings.get(keyStroke) == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
char keyChar = evt.getKeyChar();
|
||||
System.out.println("code=" + keyCode + " char=" + keyChar +
|
||||
" charint=" + ((int)keyChar));
|
||||
System.out.println("other codes " + KeyEvent.VK_ALT + " " +
|
||||
KeyEvent.VK_META);
|
||||
*/
|
||||
|
||||
if((modifiers & ~KeyEvent.SHIFT_MASK) != 0
|
||||
|| evt.isActionKey()
|
||||
|| keyCode == KeyEvent.VK_BACK_SPACE
|
||||
|| keyCode == KeyEvent.VK_DELETE
|
||||
|| keyCode == KeyEvent.VK_ENTER
|
||||
|| keyCode == KeyEvent.VK_TAB
|
||||
|| keyCode == KeyEvent.VK_ESCAPE)
|
||||
{
|
||||
if(grabAction != null)
|
||||
{
|
||||
handleGrabAction(evt);
|
||||
return;
|
||||
}
|
||||
|
||||
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
|
||||
modifiers);
|
||||
Object o = currentBindings.get(keyStroke);
|
||||
if(o == null)
|
||||
{
|
||||
// Don't beep if the user presses some
|
||||
// key we don't know about unless a
|
||||
// prefix is active. Otherwise it will
|
||||
// beep when caps lock is pressed, etc.
|
||||
if(currentBindings != bindings)
|
||||
{
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
// F10 should be passed on, but C+e F10
|
||||
// shouldn't
|
||||
repeatCount = 0;
|
||||
repeat = false;
|
||||
evt.consume();
|
||||
}
|
||||
currentBindings = bindings;
|
||||
return;
|
||||
}
|
||||
else if(o instanceof ActionListener)
|
||||
{
|
||||
currentBindings = bindings;
|
||||
|
||||
executeAction(((ActionListener)o),
|
||||
evt.getSource(),null);
|
||||
|
||||
evt.consume();
|
||||
return;
|
||||
}
|
||||
else if(o instanceof Hashtable)
|
||||
{
|
||||
currentBindings = (Hashtable)o;
|
||||
evt.consume();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a key typed event. This inserts the key into the text area.
|
||||
*/
|
||||
public void keyTyped(KeyEvent evt)
|
||||
{
|
||||
int modifiers = evt.getModifiers();
|
||||
char c = evt.getKeyChar();
|
||||
|
||||
// this is the apple/cmd key on macosx.. so menu commands
|
||||
// were being passed through as legit keys.. added this line
|
||||
// in an attempt to prevent.
|
||||
if ((modifiers & KeyEvent.META_MASK) != 0) return;
|
||||
|
||||
if (c != KeyEvent.CHAR_UNDEFINED) // &&
|
||||
// (modifiers & KeyEvent.ALT_MASK) == 0)
|
||||
{
|
||||
if(c >= 0x20 && c != 0x7f)
|
||||
{
|
||||
KeyStroke keyStroke = KeyStroke.getKeyStroke(
|
||||
Character.toUpperCase(c));
|
||||
Object o = currentBindings.get(keyStroke);
|
||||
|
||||
if(o instanceof Hashtable)
|
||||
{
|
||||
currentBindings = (Hashtable)o;
|
||||
return;
|
||||
}
|
||||
else if(o instanceof ActionListener)
|
||||
{
|
||||
currentBindings = bindings;
|
||||
executeAction((ActionListener)o,
|
||||
evt.getSource(),
|
||||
String.valueOf(c));
|
||||
return;
|
||||
}
|
||||
|
||||
currentBindings = bindings;
|
||||
|
||||
if(grabAction != null)
|
||||
{
|
||||
handleGrabAction(evt);
|
||||
return;
|
||||
}
|
||||
|
||||
// 0-9 adds another 'digit' to the repeat number
|
||||
if(repeat && Character.isDigit(c))
|
||||
{
|
||||
repeatCount *= 10;
|
||||
repeatCount += (c - '0');
|
||||
return;
|
||||
}
|
||||
|
||||
executeAction(INSERT_CHAR,evt.getSource(),
|
||||
String.valueOf(evt.getKeyChar()));
|
||||
|
||||
repeatCount = 0;
|
||||
repeat = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to a keystroke. The string should be of the
|
||||
* form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
|
||||
* is any combination of A for Alt, C for Control, S for Shift
|
||||
* or M for Meta, and <i>shortcut</i> is either a single character,
|
||||
* or a keycode name from the <code>KeyEvent</code> class, without
|
||||
* the <code>VK_</code> prefix.
|
||||
* @param keyStroke A string description of the key stroke
|
||||
*/
|
||||
public static KeyStroke parseKeyStroke(String keyStroke)
|
||||
{
|
||||
if(keyStroke == null)
|
||||
return null;
|
||||
int modifiers = 0;
|
||||
int index = keyStroke.indexOf('+');
|
||||
if(index != -1)
|
||||
{
|
||||
for(int i = 0; i < index; i++)
|
||||
{
|
||||
switch(Character.toUpperCase(keyStroke
|
||||
.charAt(i)))
|
||||
{
|
||||
case 'A':
|
||||
modifiers |= InputEvent.ALT_MASK;
|
||||
break;
|
||||
case 'C':
|
||||
modifiers |= InputEvent.CTRL_MASK;
|
||||
break;
|
||||
case 'M':
|
||||
modifiers |= InputEvent.META_MASK;
|
||||
break;
|
||||
case 'S':
|
||||
modifiers |= InputEvent.SHIFT_MASK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
String key = keyStroke.substring(index + 1);
|
||||
if(key.length() == 1)
|
||||
{
|
||||
char ch = Character.toUpperCase(key.charAt(0));
|
||||
if(modifiers == 0)
|
||||
return KeyStroke.getKeyStroke(ch);
|
||||
else
|
||||
return KeyStroke.getKeyStroke(ch,modifiers);
|
||||
}
|
||||
else if(key.length() == 0)
|
||||
{
|
||||
System.err.println("Invalid key stroke: " + keyStroke);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ch;
|
||||
|
||||
try
|
||||
{
|
||||
ch = KeyEvent.class.getField("VK_".concat(key))
|
||||
.getInt(null);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
System.err.println("Invalid key stroke: "
|
||||
+ keyStroke);
|
||||
return null;
|
||||
}
|
||||
|
||||
return KeyStroke.getKeyStroke(ch,modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
// private members
|
||||
private Hashtable bindings;
|
||||
private Hashtable currentBindings;
|
||||
|
||||
private DefaultInputHandler(DefaultInputHandler copy)
|
||||
{
|
||||
bindings = currentBindings = copy.bindings;
|
||||
}
|
||||
}
|
1071
app/syntax/InputHandler.java
Normal file
1071
app/syntax/InputHandler.java
Normal file
File diff suppressed because it is too large
Load Diff
2264
app/syntax/JEditTextArea.java
Normal file
2264
app/syntax/JEditTextArea.java
Normal file
File diff suppressed because it is too large
Load Diff
140
app/syntax/KeywordMap.java
Normal file
140
app/syntax/KeywordMap.java
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* KeywordMap.java - Fast keyword->id map
|
||||
* Copyright (C) 1998, 1999 Slava Pestov
|
||||
* Copyright (C) 1999 Mike Dillon
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
/**
|
||||
* A <code>KeywordMap</code> is similar to a hashtable in that it maps keys
|
||||
* to values. However, the `keys' are Swing segments. This allows lookups of
|
||||
* text substrings without the overhead of creating a new string object.
|
||||
* <p>
|
||||
* This class is used by <code>CTokenMarker</code> to map keywords to ids.
|
||||
*
|
||||
* @author Slava Pestov, Mike Dillon
|
||||
* @version $Id: KeywordMap.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
|
||||
*/
|
||||
public class KeywordMap
|
||||
{
|
||||
/**
|
||||
* Creates a new <code>KeywordMap</code>.
|
||||
* @param ignoreCase True if keys are case insensitive
|
||||
*/
|
||||
public KeywordMap(boolean ignoreCase)
|
||||
{
|
||||
this(ignoreCase, 52);
|
||||
this.ignoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>KeywordMap</code>.
|
||||
* @param ignoreCase True if the keys are case insensitive
|
||||
* @param mapLength The number of `buckets' to create.
|
||||
* A value of 52 will give good performance for most maps.
|
||||
*/
|
||||
public KeywordMap(boolean ignoreCase, int mapLength)
|
||||
{
|
||||
this.mapLength = mapLength;
|
||||
this.ignoreCase = ignoreCase;
|
||||
map = new Keyword[mapLength];
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a key.
|
||||
* @param text The text segment
|
||||
* @param offset The offset of the substring within the text segment
|
||||
* @param length The length of the substring
|
||||
*/
|
||||
public byte lookup(Segment text, int offset, int length)
|
||||
{
|
||||
if(length == 0)
|
||||
return Token.NULL;
|
||||
Keyword k = map[getSegmentMapKey(text, offset, length)];
|
||||
while(k != null)
|
||||
{
|
||||
if(length != k.keyword.length)
|
||||
{
|
||||
k = k.next;
|
||||
continue;
|
||||
}
|
||||
if(SyntaxUtilities.regionMatches(ignoreCase,text,offset,
|
||||
k.keyword))
|
||||
return k.id;
|
||||
k = k.next;
|
||||
}
|
||||
return Token.NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a key-value mapping.
|
||||
* @param keyword The key
|
||||
* @Param id The value
|
||||
*/
|
||||
public void add(String keyword, byte id)
|
||||
{
|
||||
int key = getStringMapKey(keyword);
|
||||
map[key] = new Keyword(keyword.toCharArray(),id,map[key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the keyword map is set to be case insensitive,
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean getIgnoreCase()
|
||||
{
|
||||
return ignoreCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the keyword map should be case insensitive.
|
||||
* @param ignoreCase True if the keyword map should be case
|
||||
* insensitive, false otherwise
|
||||
*/
|
||||
public void setIgnoreCase(boolean ignoreCase)
|
||||
{
|
||||
this.ignoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
// protected members
|
||||
protected int mapLength;
|
||||
|
||||
protected int getStringMapKey(String s)
|
||||
{
|
||||
return (Character.toUpperCase(s.charAt(0)) +
|
||||
Character.toUpperCase(s.charAt(s.length()-1)))
|
||||
% mapLength;
|
||||
}
|
||||
|
||||
protected int getSegmentMapKey(Segment s, int off, int len)
|
||||
{
|
||||
return (Character.toUpperCase(s.array[off]) +
|
||||
Character.toUpperCase(s.array[off + len - 1]))
|
||||
% mapLength;
|
||||
}
|
||||
|
||||
// private members
|
||||
class Keyword
|
||||
{
|
||||
public Keyword(char[] keyword, byte id, Keyword next)
|
||||
{
|
||||
this.keyword = keyword;
|
||||
this.id = id;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public char[] keyword;
|
||||
public byte id;
|
||||
public Keyword next;
|
||||
}
|
||||
|
||||
private Keyword[] map;
|
||||
private boolean ignoreCase;
|
||||
}
|
122
app/syntax/PdeKeywords.java
Normal file
122
app/syntax/PdeKeywords.java
Normal file
@ -0,0 +1,122 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
PdeKeywords - handles text coloring and links to html reference
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Copyright (c) 2004-05 Ben Fry and Casey Reas
|
||||
Copyright (c) 2001-04 Massachusetts Institute of Technology
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import processing.app.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class PdeKeywords extends CTokenMarker {
|
||||
|
||||
// lookup table for the TokenMarker subclass, handles coloring
|
||||
static KeywordMap keywordColoring;
|
||||
|
||||
// lookup table that maps keywords to their html reference pages
|
||||
static Hashtable keywordToReference;
|
||||
|
||||
|
||||
public PdeKeywords() {
|
||||
super(false, getKeywords());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles loading of keywords file.
|
||||
* <P>
|
||||
* Uses getKeywords() method because that's part of the
|
||||
* TokenMarker classes.
|
||||
* <P>
|
||||
* It is recommended that a # sign be used for comments
|
||||
* inside keywords.txt.
|
||||
*/
|
||||
static public KeywordMap getKeywords() {
|
||||
if (keywordColoring == null) {
|
||||
try {
|
||||
keywordColoring = new KeywordMap(false);
|
||||
keywordToReference = new Hashtable();
|
||||
|
||||
InputStream input = Base.getStream("keywords.txt");
|
||||
InputStreamReader isr = new InputStreamReader(input);
|
||||
BufferedReader reader = new BufferedReader(isr);
|
||||
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
//System.out.println("line is " + line);
|
||||
// in case there's any garbage on the line
|
||||
//if (line.trim().length() == 0) continue;
|
||||
|
||||
String pieces[] = Base.split(line, '\t');
|
||||
if (pieces.length >= 2) {
|
||||
//int tab = line.indexOf('\t');
|
||||
// any line with no tab is ignored
|
||||
// meaning that a comment is any line without a tab
|
||||
//if (tab == -1) continue;
|
||||
|
||||
String keyword = pieces[0].trim();
|
||||
//String keyword = line.substring(0, tab).trim();
|
||||
//String second = line.substring(tab + 1);
|
||||
//tab = second.indexOf('\t');
|
||||
//String coloring = second.substring(0, tab).trim();
|
||||
//String htmlFilename = second.substring(tab + 1).trim();
|
||||
String coloring = pieces[1].trim();
|
||||
|
||||
if (coloring.length() > 0) {
|
||||
// text will be KEYWORD or LITERAL
|
||||
boolean isKey = (coloring.charAt(0) == 'K');
|
||||
// KEYWORD1 -> 0, KEYWORD2 -> 1, etc
|
||||
int num = coloring.charAt(coloring.length() - 1) - '1';
|
||||
byte id = (byte)
|
||||
((isKey ? Token.KEYWORD1 : Token.LITERAL1) + num);
|
||||
//System.out.println("got " + (isKey ? "keyword" : "literal") +
|
||||
// (num+1) + " for " + keyword);
|
||||
keywordColoring.add(keyword, id);
|
||||
}
|
||||
if (pieces.length >= 3) {
|
||||
String htmlFilename = pieces[2].trim();
|
||||
if (htmlFilename.length() > 0) {
|
||||
keywordToReference.put(keyword, htmlFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
|
||||
} catch (Exception e) {
|
||||
Base.showError("Problem loading keywords",
|
||||
"Could not load keywords.txt,\n" +
|
||||
"please re-install Arduino.", e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
return keywordColoring;
|
||||
}
|
||||
|
||||
|
||||
static public String getReference(String keyword) {
|
||||
return (String) keywordToReference.get(keyword);
|
||||
}
|
||||
}
|
162
app/syntax/PdeTextAreaDefaults.java
Normal file
162
app/syntax/PdeTextAreaDefaults.java
Normal file
@ -0,0 +1,162 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
PdeTextAreaDefaults - grabs font/color settings for the editor
|
||||
Part of the Processing project - http://Proce55ing.net
|
||||
|
||||
Except where noted, code is written by Ben Fry
|
||||
Copyright (c) 2001-03 Massachusetts Institute of Technology
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import processing.app.*;
|
||||
|
||||
|
||||
public class PdeTextAreaDefaults extends TextAreaDefaults {
|
||||
|
||||
public PdeTextAreaDefaults() {
|
||||
|
||||
inputHandler = new DefaultInputHandler();
|
||||
inputHandler.addDefaultKeyBindings();
|
||||
|
||||
// use option on mac for things that are ctrl on windows/linux
|
||||
String mod = Base.isMacOS() ? "A" : "C";
|
||||
|
||||
inputHandler.addKeyBinding("S+BACK_SPACE", InputHandler.BACKSPACE);
|
||||
inputHandler.addKeyBinding("S+DELETE", InputHandler.DELETE);
|
||||
|
||||
inputHandler.addKeyBinding("BACK_SPACE", InputHandler.BACKSPACE);
|
||||
inputHandler.addKeyBinding("C+BACK_SPACE", InputHandler.BACKSPACE_WORD);
|
||||
inputHandler.addKeyBinding("DELETE", InputHandler.DELETE);
|
||||
inputHandler.addKeyBinding("C+DELETE", InputHandler.DELETE_WORD);
|
||||
|
||||
inputHandler.addKeyBinding("ENTER", InputHandler.INSERT_BREAK);
|
||||
inputHandler.addKeyBinding("TAB", InputHandler.INSERT_TAB);
|
||||
|
||||
inputHandler.addKeyBinding("INSERT", InputHandler.OVERWRITE);
|
||||
inputHandler.addKeyBinding("C+\\", InputHandler.TOGGLE_RECT);
|
||||
|
||||
// beginning and ending of the current line
|
||||
inputHandler.addKeyBinding("HOME", InputHandler.HOME);
|
||||
inputHandler.addKeyBinding("END", InputHandler.END);
|
||||
|
||||
if (Base.isMacOS()) {
|
||||
inputHandler.addKeyBinding("M+LEFT", InputHandler.HOME);
|
||||
inputHandler.addKeyBinding("M+RIGHT", InputHandler.END);
|
||||
}
|
||||
|
||||
inputHandler.addKeyBinding("S+HOME", InputHandler.SELECT_HOME);
|
||||
inputHandler.addKeyBinding("S+END", InputHandler.SELECT_END);
|
||||
inputHandler.addKeyBinding(mod + "+HOME", InputHandler.DOCUMENT_HOME);
|
||||
inputHandler.addKeyBinding(mod + "+END", InputHandler.DOCUMENT_END);
|
||||
inputHandler.addKeyBinding(mod + "S+HOME", InputHandler.SELECT_DOC_HOME);
|
||||
inputHandler.addKeyBinding(mod + "S+END", InputHandler.SELECT_DOC_END);
|
||||
|
||||
inputHandler.addKeyBinding("PAGE_UP", InputHandler.PREV_PAGE);
|
||||
inputHandler.addKeyBinding("PAGE_DOWN", InputHandler.NEXT_PAGE);
|
||||
inputHandler.addKeyBinding("S+PAGE_UP", InputHandler.SELECT_PREV_PAGE);
|
||||
inputHandler.addKeyBinding("S+PAGE_DOWN", InputHandler.SELECT_NEXT_PAGE);
|
||||
|
||||
inputHandler.addKeyBinding("LEFT", InputHandler.PREV_CHAR);
|
||||
inputHandler.addKeyBinding("S+LEFT", InputHandler.SELECT_PREV_CHAR);
|
||||
inputHandler.addKeyBinding(mod + "+LEFT", InputHandler.PREV_WORD);
|
||||
inputHandler.addKeyBinding(mod + "S+LEFT", InputHandler.SELECT_PREV_WORD);
|
||||
inputHandler.addKeyBinding("RIGHT", InputHandler.NEXT_CHAR);
|
||||
inputHandler.addKeyBinding("S+RIGHT", InputHandler.SELECT_NEXT_CHAR);
|
||||
inputHandler.addKeyBinding(mod + "+RIGHT", InputHandler.NEXT_WORD);
|
||||
inputHandler.addKeyBinding(mod + "S+RIGHT", InputHandler.SELECT_NEXT_WORD);
|
||||
inputHandler.addKeyBinding("UP", InputHandler.PREV_LINE);
|
||||
inputHandler.addKeyBinding(mod + "+UP", InputHandler.PREV_LINE); // p5
|
||||
inputHandler.addKeyBinding("S+UP", InputHandler.SELECT_PREV_LINE);
|
||||
inputHandler.addKeyBinding("DOWN", InputHandler.NEXT_LINE);
|
||||
inputHandler.addKeyBinding(mod + "+DOWN", InputHandler.NEXT_LINE); // p5
|
||||
inputHandler.addKeyBinding("S+DOWN", InputHandler.SELECT_NEXT_LINE);
|
||||
|
||||
inputHandler.addKeyBinding(mod + "+ENTER", InputHandler.REPEAT);
|
||||
|
||||
document = new SyntaxDocument();
|
||||
editable = true;
|
||||
electricScroll = 3;
|
||||
|
||||
cols = 80;
|
||||
rows = 15;
|
||||
|
||||
|
||||
// moved from SyntaxUtilities
|
||||
//DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles();
|
||||
|
||||
styles = new SyntaxStyle[Token.ID_COUNT];
|
||||
|
||||
// comments
|
||||
styles[Token.COMMENT1] = Preferences.getStyle("comment1");
|
||||
styles[Token.COMMENT2] = Preferences.getStyle("comment2");
|
||||
|
||||
// abstract, final, private
|
||||
styles[Token.KEYWORD1] = Preferences.getStyle("keyword1");
|
||||
|
||||
// beginShape, point, line
|
||||
styles[Token.KEYWORD2] = Preferences.getStyle("keyword2");
|
||||
|
||||
// byte, char, short, color
|
||||
styles[Token.KEYWORD3] = Preferences.getStyle("keyword3");
|
||||
|
||||
// constants: null, true, this, RGB, TWO_PI
|
||||
styles[Token.LITERAL1] = Preferences.getStyle("literal1");
|
||||
|
||||
// p5 built in variables: mouseX, width, pixels
|
||||
styles[Token.LITERAL2] = Preferences.getStyle("literal2");
|
||||
|
||||
// ??
|
||||
styles[Token.LABEL] = Preferences.getStyle("label");
|
||||
|
||||
// + - = /
|
||||
styles[Token.OPERATOR] = Preferences.getStyle("operator");
|
||||
|
||||
// area that's not in use by the text (replaced with tildes)
|
||||
styles[Token.INVALID] = Preferences.getStyle("invalid");
|
||||
|
||||
|
||||
// moved from TextAreaPainter
|
||||
|
||||
font = Preferences.getFont("editor.font");
|
||||
|
||||
fgcolor = Preferences.getColor("editor.fgcolor");
|
||||
bgcolor = Preferences.getColor("editor.bgcolor");
|
||||
|
||||
caretVisible = true;
|
||||
caretBlinks = Preferences.getBoolean("editor.caret.blink");
|
||||
caretColor = Preferences.getColor("editor.caret.color");
|
||||
|
||||
selectionColor = Preferences.getColor("editor.selection.color");
|
||||
|
||||
lineHighlight =
|
||||
Preferences.getBoolean("editor.linehighlight");
|
||||
lineHighlightColor =
|
||||
Preferences.getColor("editor.linehighlight.color");
|
||||
|
||||
bracketHighlight =
|
||||
Preferences.getBoolean("editor.brackethighlight");
|
||||
bracketHighlightColor =
|
||||
Preferences.getColor("editor.brackethighlight.color");
|
||||
|
||||
eolMarkers = Preferences.getBoolean("editor.eolmarkers");
|
||||
eolMarkerColor = Preferences.getColor("editor.eolmarkers.color");
|
||||
|
||||
paintInvalid = Preferences.getBoolean("editor.invalid");
|
||||
}
|
||||
}
|
166
app/syntax/SyntaxDocument.java
Normal file
166
app/syntax/SyntaxDocument.java
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* SyntaxDocument.java - Document that can be tokenized
|
||||
* Copyright (C) 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.text.*;
|
||||
import javax.swing.undo.UndoableEdit;
|
||||
|
||||
/**
|
||||
* A document implementation that can be tokenized by the syntax highlighting
|
||||
* system.
|
||||
*
|
||||
* @author Slava Pestov
|
||||
* @version $Id: SyntaxDocument.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
|
||||
*/
|
||||
public class SyntaxDocument extends PlainDocument
|
||||
{
|
||||
/**
|
||||
* Returns the token marker that is to be used to split lines
|
||||
* of this document up into tokens. May return null if this
|
||||
* document is not to be colorized.
|
||||
*/
|
||||
public TokenMarker getTokenMarker()
|
||||
{
|
||||
return tokenMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the token marker that is to be used to split lines of
|
||||
* this document up into tokens. May throw an exception if
|
||||
* this is not supported for this type of document.
|
||||
* @param tm The new token marker
|
||||
*/
|
||||
public void setTokenMarker(TokenMarker tm)
|
||||
{
|
||||
tokenMarker = tm;
|
||||
if(tm == null)
|
||||
return;
|
||||
tokenMarker.insertLines(0,getDefaultRootElement()
|
||||
.getElementCount());
|
||||
tokenizeLines();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reparses the document, by passing all lines to the token
|
||||
* marker. This should be called after the document is first
|
||||
* loaded.
|
||||
*/
|
||||
public void tokenizeLines()
|
||||
{
|
||||
tokenizeLines(0,getDefaultRootElement().getElementCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reparses the document, by passing the specified lines to the
|
||||
* token marker. This should be called after a large quantity of
|
||||
* text is first inserted.
|
||||
* @param start The first line to parse
|
||||
* @param len The number of lines, after the first one to parse
|
||||
*/
|
||||
public void tokenizeLines(int start, int len)
|
||||
{
|
||||
if(tokenMarker == null || !tokenMarker.supportsMultilineTokens())
|
||||
return;
|
||||
|
||||
Segment lineSegment = new Segment();
|
||||
Element map = getDefaultRootElement();
|
||||
|
||||
len += start;
|
||||
|
||||
try
|
||||
{
|
||||
for(int i = start; i < len; i++)
|
||||
{
|
||||
Element lineElement = map.getElement(i);
|
||||
int lineStart = lineElement.getStartOffset();
|
||||
getText(lineStart,lineElement.getEndOffset()
|
||||
- lineStart - 1,lineSegment);
|
||||
tokenMarker.markTokens(lineSegment,i);
|
||||
}
|
||||
}
|
||||
catch(BadLocationException bl)
|
||||
{
|
||||
bl.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a compound edit that can be undone in one operation.
|
||||
* Subclasses that implement undo should override this method;
|
||||
* this class has no undo functionality so this method is
|
||||
* empty.
|
||||
*/
|
||||
public void beginCompoundEdit() {}
|
||||
|
||||
/**
|
||||
* Ends a compound edit that can be undone in one operation.
|
||||
* Subclasses that implement undo should override this method;
|
||||
* this class has no undo functionality so this method is
|
||||
* empty.
|
||||
*/
|
||||
public void endCompoundEdit() {}
|
||||
|
||||
/**
|
||||
* Adds an undoable edit to this document's undo list. The edit
|
||||
* should be ignored if something is currently being undone.
|
||||
* @param edit The undoable edit
|
||||
*
|
||||
* @since jEdit 2.2pre1
|
||||
*/
|
||||
public void addUndoableEdit(UndoableEdit edit) {}
|
||||
|
||||
// protected members
|
||||
protected TokenMarker tokenMarker;
|
||||
|
||||
/**
|
||||
* We overwrite this method to update the token marker
|
||||
* state immediately so that any event listeners get a
|
||||
* consistent token marker.
|
||||
*/
|
||||
protected void fireInsertUpdate(DocumentEvent evt)
|
||||
{
|
||||
if(tokenMarker != null)
|
||||
{
|
||||
DocumentEvent.ElementChange ch = evt.getChange(
|
||||
getDefaultRootElement());
|
||||
if(ch != null)
|
||||
{
|
||||
tokenMarker.insertLines(ch.getIndex() + 1,
|
||||
ch.getChildrenAdded().length -
|
||||
ch.getChildrenRemoved().length);
|
||||
}
|
||||
}
|
||||
|
||||
super.fireInsertUpdate(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* We overwrite this method to update the token marker
|
||||
* state immediately so that any event listeners get a
|
||||
* consistent token marker.
|
||||
*/
|
||||
protected void fireRemoveUpdate(DocumentEvent evt)
|
||||
{
|
||||
if(tokenMarker != null)
|
||||
{
|
||||
DocumentEvent.ElementChange ch = evt.getChange(
|
||||
getDefaultRootElement());
|
||||
if(ch != null)
|
||||
{
|
||||
tokenMarker.deleteLines(ch.getIndex() + 1,
|
||||
ch.getChildrenRemoved().length -
|
||||
ch.getChildrenAdded().length);
|
||||
}
|
||||
}
|
||||
|
||||
super.fireRemoveUpdate(evt);
|
||||
}
|
||||
}
|
137
app/syntax/SyntaxStyle.java
Normal file
137
app/syntax/SyntaxStyle.java
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* SyntaxStyle.java - A simple text style class
|
||||
* Copyright (C) 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* A simple text style class. It can specify the color, italic flag,
|
||||
* and bold flag of a run of text.
|
||||
* @author Slava Pestov
|
||||
* @version $Id: SyntaxStyle.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
|
||||
*/
|
||||
public class SyntaxStyle
|
||||
{
|
||||
/**
|
||||
* Creates a new SyntaxStyle.
|
||||
* @param color The text color
|
||||
* @param italic True if the text should be italics
|
||||
* @param bold True if the text should be bold
|
||||
*/
|
||||
public SyntaxStyle(Color color, boolean italic, boolean bold)
|
||||
{
|
||||
this.color = color;
|
||||
this.italic = italic;
|
||||
this.bold = bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color specified in this style.
|
||||
*/
|
||||
public Color getColor()
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if no font styles are enabled.
|
||||
*/
|
||||
public boolean isPlain()
|
||||
{
|
||||
return !(bold || italic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if italics is enabled for this style.
|
||||
*/
|
||||
public boolean isItalic()
|
||||
{
|
||||
return italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if boldface is enabled for this style.
|
||||
*/
|
||||
public boolean isBold()
|
||||
{
|
||||
return bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified font, but with the style's bold and
|
||||
* italic flags applied.
|
||||
*/
|
||||
public Font getStyledFont(Font font)
|
||||
{
|
||||
if(font == null)
|
||||
throw new NullPointerException("font param must not"
|
||||
+ " be null");
|
||||
if(font.equals(lastFont))
|
||||
return lastStyledFont;
|
||||
lastFont = font;
|
||||
lastStyledFont = new Font(font.getFamily(),
|
||||
(bold ? Font.BOLD : 0)
|
||||
| (italic ? Font.ITALIC : 0),
|
||||
font.getSize());
|
||||
return lastStyledFont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the font metrics for the styled font.
|
||||
*/
|
||||
public FontMetrics getFontMetrics(Font font)
|
||||
{
|
||||
if(font == null)
|
||||
throw new NullPointerException("font param must not"
|
||||
+ " be null");
|
||||
if(font.equals(lastFont) && fontMetrics != null)
|
||||
return fontMetrics;
|
||||
lastFont = font;
|
||||
lastStyledFont = new Font(font.getFamily(),
|
||||
(bold ? Font.BOLD : 0)
|
||||
| (italic ? Font.ITALIC : 0),
|
||||
font.getSize());
|
||||
fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(
|
||||
lastStyledFont);
|
||||
return fontMetrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the foreground color and font of the specified graphics
|
||||
* context to that specified in this style.
|
||||
* @param gfx The graphics context
|
||||
* @param font The font to add the styles to
|
||||
*/
|
||||
public void setGraphicsFlags(Graphics gfx, Font font)
|
||||
{
|
||||
Font _font = getStyledFont(font);
|
||||
gfx.setFont(_font);
|
||||
gfx.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this object.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return getClass().getName() + "[color=" + color +
|
||||
(italic ? ",italic" : "") +
|
||||
(bold ? ",bold" : "") + "]";
|
||||
}
|
||||
|
||||
// private members
|
||||
private Color color;
|
||||
private boolean italic;
|
||||
private boolean bold;
|
||||
private Font lastFont;
|
||||
private Font lastStyledFont;
|
||||
private FontMetrics fontMetrics;
|
||||
}
|
163
app/syntax/SyntaxUtilities.java
Normal file
163
app/syntax/SyntaxUtilities.java
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* SyntaxUtilities.java - Utility functions used by syntax colorizing
|
||||
* Copyright (C) 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import javax.swing.text.*;
|
||||
import java.awt.*;
|
||||
|
||||
|
||||
/**
|
||||
* Class with several utility functions used by jEdit's syntax colorizing
|
||||
* subsystem.
|
||||
*
|
||||
* @author Slava Pestov
|
||||
* @version $Id: SyntaxUtilities.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
|
||||
*/
|
||||
public class SyntaxUtilities
|
||||
{
|
||||
/**
|
||||
* Checks if a subregion of a <code>Segment</code> is equal to a
|
||||
* string.
|
||||
* @param ignoreCase True if case should be ignored, false otherwise
|
||||
* @param text The segment
|
||||
* @param offset The offset into the segment
|
||||
* @param match The string to match
|
||||
*/
|
||||
public static boolean regionMatches(boolean ignoreCase, Segment text,
|
||||
int offset, String match)
|
||||
{
|
||||
int length = offset + match.length();
|
||||
char[] textArray = text.array;
|
||||
if(length > text.offset + text.count)
|
||||
return false;
|
||||
for(int i = offset, j = 0; i < length; i++, j++)
|
||||
{
|
||||
char c1 = textArray[i];
|
||||
char c2 = match.charAt(j);
|
||||
if(ignoreCase)
|
||||
{
|
||||
c1 = Character.toUpperCase(c1);
|
||||
c2 = Character.toUpperCase(c2);
|
||||
}
|
||||
if(c1 != c2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a subregion of a <code>Segment</code> is equal to a
|
||||
* character array.
|
||||
* @param ignoreCase True if case should be ignored, false otherwise
|
||||
* @param text The segment
|
||||
* @param offset The offset into the segment
|
||||
* @param match The character array to match
|
||||
*/
|
||||
public static boolean regionMatches(boolean ignoreCase, Segment text,
|
||||
int offset, char[] match)
|
||||
{
|
||||
int length = offset + match.length;
|
||||
char[] textArray = text.array;
|
||||
if(length > text.offset + text.count)
|
||||
return false;
|
||||
for(int i = offset, j = 0; i < length; i++, j++)
|
||||
{
|
||||
char c1 = textArray[i];
|
||||
char c2 = match[j];
|
||||
if(ignoreCase)
|
||||
{
|
||||
c1 = Character.toUpperCase(c1);
|
||||
c2 = Character.toUpperCase(c2);
|
||||
}
|
||||
if(c1 != c2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default style table. This can be passed to the
|
||||
* <code>setStyles()</code> method of <code>SyntaxDocument</code>
|
||||
* to use the default syntax styles.
|
||||
*/
|
||||
public static SyntaxStyle[] getDefaultSyntaxStyles()
|
||||
{
|
||||
SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT];
|
||||
|
||||
styles[Token.COMMENT1] = new SyntaxStyle(Color.black,true,false);
|
||||
styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033),true,false);
|
||||
styles[Token.KEYWORD1] = new SyntaxStyle(Color.black,false,true);
|
||||
styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta,false,false);
|
||||
styles[Token.KEYWORD3] = new SyntaxStyle(new Color(0x009600),false,false);
|
||||
styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x650099),false,false);
|
||||
styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x650099),false,true);
|
||||
styles[Token.LABEL] = new SyntaxStyle(new Color(0x990033),false,true);
|
||||
styles[Token.OPERATOR] = new SyntaxStyle(Color.black,false,true);
|
||||
styles[Token.INVALID] = new SyntaxStyle(Color.red,false,true);
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Paints the specified line onto the graphics context. Note that this
|
||||
* method munges the offset and count values of the segment.
|
||||
* @param line The line segment
|
||||
* @param tokens The token list for the line
|
||||
* @param styles The syntax style list
|
||||
* @param expander The tab expander used to determine tab stops. May
|
||||
* be null
|
||||
* @param gfx The graphics context
|
||||
* @param x The x co-ordinate
|
||||
* @param y The y co-ordinate
|
||||
* @return The x co-ordinate, plus the width of the painted string
|
||||
*/
|
||||
public static int paintSyntaxLine(Segment line, Token tokens,
|
||||
SyntaxStyle[] styles,
|
||||
TabExpander expander, Graphics gfx,
|
||||
int x, int y)
|
||||
{
|
||||
Font defaultFont = gfx.getFont();
|
||||
Color defaultColor = gfx.getColor();
|
||||
|
||||
int offset = 0;
|
||||
for(;;)
|
||||
{
|
||||
byte id = tokens.id;
|
||||
if(id == Token.END)
|
||||
break;
|
||||
|
||||
int length = tokens.length;
|
||||
if(id == Token.NULL)
|
||||
{
|
||||
if(!defaultColor.equals(gfx.getColor()))
|
||||
gfx.setColor(defaultColor);
|
||||
if(!defaultFont.equals(gfx.getFont()))
|
||||
gfx.setFont(defaultFont);
|
||||
}
|
||||
else
|
||||
styles[id].setGraphicsFlags(gfx,defaultFont);
|
||||
|
||||
line.count = length;
|
||||
x = Utilities.drawTabbedText(line,x,y,gfx,expander,0);
|
||||
line.offset += length;
|
||||
offset += length;
|
||||
|
||||
tokens = tokens.next;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
// private members
|
||||
private SyntaxUtilities() {}
|
||||
}
|
90
app/syntax/TextAreaDefaults.java
Normal file
90
app/syntax/TextAreaDefaults.java
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* TextAreaDefaults.java - Encapsulates default values for various settings
|
||||
* Copyright (C) 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import java.awt.*;
|
||||
//import javax.swing.JPopupMenu;
|
||||
|
||||
/**
|
||||
* Encapsulates default settings for a text area. This can be passed
|
||||
* to the constructor once the necessary fields have been filled out.
|
||||
* The advantage of doing this over calling lots of set() methods after
|
||||
* creating the text area is that this method is faster.
|
||||
*/
|
||||
public class TextAreaDefaults
|
||||
{
|
||||
private static TextAreaDefaults DEFAULTS;
|
||||
|
||||
public InputHandler inputHandler;
|
||||
public SyntaxDocument document;
|
||||
public boolean editable;
|
||||
|
||||
public boolean caretVisible;
|
||||
public boolean caretBlinks;
|
||||
public boolean blockCaret;
|
||||
public int electricScroll;
|
||||
|
||||
public int cols;
|
||||
public int rows;
|
||||
public SyntaxStyle[] styles;
|
||||
public Color caretColor;
|
||||
public Color selectionColor;
|
||||
public Color lineHighlightColor;
|
||||
public boolean lineHighlight;
|
||||
public Color bracketHighlightColor;
|
||||
public boolean bracketHighlight;
|
||||
public Color eolMarkerColor;
|
||||
public boolean eolMarkers;
|
||||
public boolean paintInvalid;
|
||||
|
||||
|
||||
// moved from TextAreaPainter [fry]
|
||||
public Font font;
|
||||
public Color fgcolor;
|
||||
public Color bgcolor;
|
||||
|
||||
//public JPopupMenu popup;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new TextAreaDefaults object with the default values filled
|
||||
* in.
|
||||
*/
|
||||
public static TextAreaDefaults getDefaults()
|
||||
{
|
||||
if (DEFAULTS == null) {
|
||||
DEFAULTS = new TextAreaDefaults();
|
||||
|
||||
DEFAULTS.inputHandler = new DefaultInputHandler();
|
||||
DEFAULTS.inputHandler.addDefaultKeyBindings();
|
||||
DEFAULTS.document = new SyntaxDocument();
|
||||
DEFAULTS.editable = true;
|
||||
|
||||
DEFAULTS.caretVisible = true;
|
||||
DEFAULTS.caretBlinks = true;
|
||||
DEFAULTS.electricScroll = 3;
|
||||
|
||||
DEFAULTS.cols = 80;
|
||||
DEFAULTS.rows = 25;
|
||||
DEFAULTS.styles = SyntaxUtilities.getDefaultSyntaxStyles();
|
||||
DEFAULTS.caretColor = Color.red;
|
||||
DEFAULTS.selectionColor = new Color(0xccccff);
|
||||
DEFAULTS.lineHighlightColor = new Color(0xe0e0e0);
|
||||
DEFAULTS.lineHighlight = true;
|
||||
DEFAULTS.bracketHighlightColor = Color.black;
|
||||
DEFAULTS.bracketHighlight = true;
|
||||
DEFAULTS.eolMarkerColor = new Color(0x009999);
|
||||
DEFAULTS.eolMarkers = true;
|
||||
DEFAULTS.paintInvalid = true;
|
||||
}
|
||||
|
||||
return DEFAULTS;
|
||||
}
|
||||
}
|
688
app/syntax/TextAreaPainter.java
Normal file
688
app/syntax/TextAreaPainter.java
Normal file
@ -0,0 +1,688 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
* TextAreaPainter.java - Paints the text area
|
||||
* Copyright (C) 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import processing.app.*;
|
||||
|
||||
import javax.swing.ToolTipManager;
|
||||
import javax.swing.text.*;
|
||||
import javax.swing.JComponent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* The text area repaint manager. It performs double buffering and paints
|
||||
* lines of text.
|
||||
* @author Slava Pestov
|
||||
* @version $Id: TextAreaPainter.java,v 1.3 2005/05/10 01:17:21 benfry Exp $
|
||||
*/
|
||||
public class TextAreaPainter extends JComponent implements TabExpander
|
||||
{
|
||||
/**
|
||||
* Creates a new repaint manager. This should be not be called
|
||||
* directly.
|
||||
*/
|
||||
public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults)
|
||||
{
|
||||
this.textArea = textArea;
|
||||
|
||||
setAutoscrolls(true);
|
||||
setDoubleBuffered(true);
|
||||
setOpaque(true);
|
||||
|
||||
ToolTipManager.sharedInstance().registerComponent(this);
|
||||
|
||||
currentLine = new Segment();
|
||||
currentLineIndex = -1;
|
||||
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
|
||||
|
||||
setFont(defaults.font);
|
||||
setForeground(defaults.fgcolor);
|
||||
setBackground(defaults.bgcolor);
|
||||
|
||||
blockCaret = defaults.blockCaret;
|
||||
styles = defaults.styles;
|
||||
cols = defaults.cols;
|
||||
rows = defaults.rows;
|
||||
caretColor = defaults.caretColor;
|
||||
selectionColor = defaults.selectionColor;
|
||||
lineHighlightColor = defaults.lineHighlightColor;
|
||||
lineHighlight = defaults.lineHighlight;
|
||||
bracketHighlightColor = defaults.bracketHighlightColor;
|
||||
bracketHighlight = defaults.bracketHighlight;
|
||||
paintInvalid = defaults.paintInvalid;
|
||||
eolMarkerColor = defaults.eolMarkerColor;
|
||||
eolMarkers = defaults.eolMarkers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this component can be traversed by pressing the
|
||||
* Tab key. This returns false.
|
||||
*/
|
||||
public final boolean isManagingFocus()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the syntax styles used to paint colorized text. Entry <i>n</i>
|
||||
* will be used to paint tokens with id = <i>n</i>.
|
||||
* @see org.gjt.sp.jedit.syntax.Token
|
||||
*/
|
||||
public final SyntaxStyle[] getStyles()
|
||||
{
|
||||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the syntax styles used to paint colorized text. Entry <i>n</i>
|
||||
* will be used to paint tokens with id = <i>n</i>.
|
||||
* @param styles The syntax styles
|
||||
* @see org.gjt.sp.jedit.syntax.Token
|
||||
*/
|
||||
public final void setStyles(SyntaxStyle[] styles)
|
||||
{
|
||||
this.styles = styles;
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caret color.
|
||||
*/
|
||||
public final Color getCaretColor()
|
||||
{
|
||||
return caretColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the caret color.
|
||||
* @param caretColor The caret color
|
||||
*/
|
||||
public final void setCaretColor(Color caretColor)
|
||||
{
|
||||
this.caretColor = caretColor;
|
||||
invalidateSelectedLines();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selection color.
|
||||
*/
|
||||
public final Color getSelectionColor()
|
||||
{
|
||||
return selectionColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selection color.
|
||||
* @param selectionColor The selection color
|
||||
*/
|
||||
public final void setSelectionColor(Color selectionColor)
|
||||
{
|
||||
this.selectionColor = selectionColor;
|
||||
invalidateSelectedLines();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the line highlight color.
|
||||
*/
|
||||
public final Color getLineHighlightColor()
|
||||
{
|
||||
return lineHighlightColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line highlight color.
|
||||
* @param lineHighlightColor The line highlight color
|
||||
*/
|
||||
public final void setLineHighlightColor(Color lineHighlightColor)
|
||||
{
|
||||
this.lineHighlightColor = lineHighlightColor;
|
||||
invalidateSelectedLines();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if line highlight is enabled, false otherwise.
|
||||
*/
|
||||
public final boolean isLineHighlightEnabled()
|
||||
{
|
||||
return lineHighlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables current line highlighting.
|
||||
* @param lineHighlight True if current line highlight
|
||||
* should be enabled, false otherwise
|
||||
*/
|
||||
public final void setLineHighlightEnabled(boolean lineHighlight)
|
||||
{
|
||||
this.lineHighlight = lineHighlight;
|
||||
invalidateSelectedLines();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bracket highlight color.
|
||||
*/
|
||||
public final Color getBracketHighlightColor()
|
||||
{
|
||||
return bracketHighlightColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bracket highlight color.
|
||||
* @param bracketHighlightColor The bracket highlight color
|
||||
*/
|
||||
public final void setBracketHighlightColor(Color bracketHighlightColor)
|
||||
{
|
||||
this.bracketHighlightColor = bracketHighlightColor;
|
||||
invalidateLine(textArea.getBracketLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if bracket highlighting is enabled, false otherwise.
|
||||
* When bracket highlighting is enabled, the bracket matching the
|
||||
* one before the caret (if any) is highlighted.
|
||||
*/
|
||||
public final boolean isBracketHighlightEnabled()
|
||||
{
|
||||
return bracketHighlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables bracket highlighting.
|
||||
* When bracket highlighting is enabled, the bracket matching the
|
||||
* one before the caret (if any) is highlighted.
|
||||
* @param bracketHighlight True if bracket highlighting should be
|
||||
* enabled, false otherwise
|
||||
*/
|
||||
public final void setBracketHighlightEnabled(boolean bracketHighlight)
|
||||
{
|
||||
this.bracketHighlight = bracketHighlight;
|
||||
invalidateLine(textArea.getBracketLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the caret should be drawn as a block, false otherwise.
|
||||
*/
|
||||
public final boolean isBlockCaretEnabled()
|
||||
{
|
||||
return blockCaret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the caret should be drawn as a block, false otherwise.
|
||||
* @param blockCaret True if the caret should be drawn as a block,
|
||||
* false otherwise.
|
||||
*/
|
||||
public final void setBlockCaretEnabled(boolean blockCaret)
|
||||
{
|
||||
this.blockCaret = blockCaret;
|
||||
invalidateSelectedLines();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the EOL marker color.
|
||||
*/
|
||||
public final Color getEOLMarkerColor()
|
||||
{
|
||||
return eolMarkerColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the EOL marker color.
|
||||
* @param eolMarkerColor The EOL marker color
|
||||
*/
|
||||
public final void setEOLMarkerColor(Color eolMarkerColor)
|
||||
{
|
||||
this.eolMarkerColor = eolMarkerColor;
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if EOL markers are drawn, false otherwise.
|
||||
*/
|
||||
public final boolean getEOLMarkersPainted()
|
||||
{
|
||||
return eolMarkers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if EOL markers are to be drawn.
|
||||
* @param eolMarkers True if EOL markers should be drawn, false otherwise
|
||||
*/
|
||||
public final void setEOLMarkersPainted(boolean eolMarkers)
|
||||
{
|
||||
this.eolMarkers = eolMarkers;
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if invalid lines are painted as red tildes (~),
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean getInvalidLinesPainted()
|
||||
{
|
||||
return paintInvalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if invalid lines are to be painted as red tildes.
|
||||
* @param paintInvalid True if invalid lines should be drawn, false otherwise
|
||||
*/
|
||||
public void setInvalidLinesPainted(boolean paintInvalid)
|
||||
{
|
||||
this.paintInvalid = paintInvalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom highlight painter.
|
||||
* @param highlight The highlight
|
||||
*/
|
||||
public void addCustomHighlight(Highlight highlight)
|
||||
{
|
||||
highlight.init(textArea,highlights);
|
||||
highlights = highlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight interface.
|
||||
*/
|
||||
public interface Highlight
|
||||
{
|
||||
/**
|
||||
* Called after the highlight painter has been added.
|
||||
* @param textArea The text area
|
||||
* @param next The painter this one should delegate to
|
||||
*/
|
||||
void init(JEditTextArea textArea, Highlight next);
|
||||
|
||||
/**
|
||||
* This should paint the highlight and delgate to the
|
||||
* next highlight painter.
|
||||
* @param gfx The graphics context
|
||||
* @param line The line number
|
||||
* @param y The y co-ordinate of the line
|
||||
*/
|
||||
void paintHighlight(Graphics gfx, int line, int y);
|
||||
|
||||
/**
|
||||
* Returns the tool tip to display at the specified
|
||||
* location. If this highlighter doesn't know what to
|
||||
* display, it should delegate to the next highlight
|
||||
* painter.
|
||||
* @param evt The mouse event
|
||||
*/
|
||||
String getToolTipText(MouseEvent evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tool tip to display at the specified location.
|
||||
* @param evt The mouse event
|
||||
*/
|
||||
public String getToolTipText(MouseEvent evt)
|
||||
{
|
||||
if(highlights != null)
|
||||
return highlights.getToolTipText(evt);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the font metrics used by this component.
|
||||
*/
|
||||
public FontMetrics getFontMetrics()
|
||||
{
|
||||
return fm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the font for this component. This is overridden to update the
|
||||
* cached font metrics and to recalculate which lines are visible.
|
||||
* @param font The font
|
||||
*/
|
||||
public void setFont(Font font)
|
||||
{
|
||||
super.setFont(font);
|
||||
fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
|
||||
textArea.recalculateVisibleLines();
|
||||
}
|
||||
|
||||
/**
|
||||
* Repaints the text.
|
||||
* @param g The graphics context
|
||||
*/
|
||||
public void paint(Graphics gfx)
|
||||
{
|
||||
if (Base.isMacOS()) {
|
||||
Graphics2D g2 = (Graphics2D) gfx;
|
||||
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
|
||||
}
|
||||
|
||||
tabSize = fm.charWidth(' ') * ((Integer)textArea.getDocument().getProperty(PlainDocument.tabSizeAttribute)).intValue();
|
||||
|
||||
Rectangle clipRect = gfx.getClipBounds();
|
||||
|
||||
gfx.setColor(getBackground());
|
||||
gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
|
||||
|
||||
// We don't use yToLine() here because that method doesn't
|
||||
// return lines past the end of the document
|
||||
int height = fm.getHeight();
|
||||
int firstLine = textArea.getFirstLine();
|
||||
int firstInvalid = firstLine + clipRect.y / height;
|
||||
// Because the clipRect's height is usually an even multiple
|
||||
// of the font height, we subtract 1 from it, otherwise one
|
||||
// too many lines will always be painted.
|
||||
int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height;
|
||||
|
||||
try {
|
||||
TokenMarker tokenMarker = textArea.getDocument().getTokenMarker();
|
||||
int x = textArea.getHorizontalOffset();
|
||||
|
||||
for (int line = firstInvalid; line <= lastInvalid; line++) {
|
||||
paintLine(gfx,tokenMarker,line,x);
|
||||
}
|
||||
|
||||
if (tokenMarker != null && tokenMarker.isNextLineRequested()) {
|
||||
int h = clipRect.y + clipRect.height;
|
||||
repaint(0,h,getWidth(),getHeight() - h);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error repainting line"
|
||||
+ " range {" + firstInvalid + ","
|
||||
+ lastInvalid + "}:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a line as needing a repaint.
|
||||
* @param line The line to invalidate
|
||||
*/
|
||||
public final void invalidateLine(int line)
|
||||
{
|
||||
repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(),
|
||||
getWidth(),fm.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a range of lines as needing a repaint.
|
||||
* @param firstLine The first line to invalidate
|
||||
* @param lastLine The last line to invalidate
|
||||
*/
|
||||
public final void invalidateLineRange(int firstLine, int lastLine)
|
||||
{
|
||||
repaint(0,textArea.lineToY(firstLine) +
|
||||
fm.getMaxDescent() + fm.getLeading(),
|
||||
getWidth(),(lastLine - firstLine + 1) * fm.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Repaints the lines containing the selection.
|
||||
*/
|
||||
public final void invalidateSelectedLines()
|
||||
{
|
||||
invalidateLineRange(textArea.getSelectionStartLine(),
|
||||
textArea.getSelectionEndLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of TabExpander interface. Returns next tab stop after
|
||||
* a specified point.
|
||||
* @param x The x co-ordinate
|
||||
* @param tabOffset Ignored
|
||||
* @return The next tab stop after <i>x</i>
|
||||
*/
|
||||
public float nextTabStop(float x, int tabOffset)
|
||||
{
|
||||
int offset = textArea.getHorizontalOffset();
|
||||
int ntabs = ((int)x - offset) / tabSize;
|
||||
return (ntabs + 1) * tabSize + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the painter's preferred size.
|
||||
*/
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
Dimension dim = new Dimension();
|
||||
dim.width = fm.charWidth('w') * cols;
|
||||
dim.height = fm.getHeight() * rows;
|
||||
return dim;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the painter's minimum size.
|
||||
*/
|
||||
public Dimension getMinimumSize()
|
||||
{
|
||||
return getPreferredSize();
|
||||
}
|
||||
|
||||
// package-private members
|
||||
int currentLineIndex;
|
||||
Token currentLineTokens;
|
||||
Segment currentLine;
|
||||
|
||||
// protected members
|
||||
protected JEditTextArea textArea;
|
||||
|
||||
protected SyntaxStyle[] styles;
|
||||
protected Color caretColor;
|
||||
protected Color selectionColor;
|
||||
protected Color lineHighlightColor;
|
||||
protected Color bracketHighlightColor;
|
||||
protected Color eolMarkerColor;
|
||||
|
||||
protected boolean blockCaret;
|
||||
protected boolean lineHighlight;
|
||||
protected boolean bracketHighlight;
|
||||
protected boolean paintInvalid;
|
||||
protected boolean eolMarkers;
|
||||
protected int cols;
|
||||
protected int rows;
|
||||
|
||||
protected int tabSize;
|
||||
protected FontMetrics fm;
|
||||
|
||||
protected Highlight highlights;
|
||||
|
||||
protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
|
||||
int line, int x)
|
||||
{
|
||||
Font defaultFont = getFont();
|
||||
Color defaultColor = getForeground();
|
||||
|
||||
currentLineIndex = line;
|
||||
int y = textArea.lineToY(line);
|
||||
|
||||
if (line < 0 || line >= textArea.getLineCount()) {
|
||||
if (paintInvalid) {
|
||||
paintHighlight(gfx,line,y);
|
||||
styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont);
|
||||
gfx.drawString("~",0,y + fm.getHeight());
|
||||
}
|
||||
} else if(tokenMarker == null) {
|
||||
paintPlainLine(gfx,line,defaultFont,defaultColor,x,y);
|
||||
} else {
|
||||
paintSyntaxLine(gfx,tokenMarker,line,defaultFont,
|
||||
defaultColor,x,y);
|
||||
}
|
||||
}
|
||||
|
||||
protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
|
||||
Color defaultColor, int x, int y)
|
||||
{
|
||||
paintHighlight(gfx,line,y);
|
||||
textArea.getLineText(line,currentLine);
|
||||
|
||||
gfx.setFont(defaultFont);
|
||||
gfx.setColor(defaultColor);
|
||||
|
||||
y += fm.getHeight();
|
||||
x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0);
|
||||
|
||||
if (eolMarkers) {
|
||||
gfx.setColor(eolMarkerColor);
|
||||
gfx.drawString(".",x,y);
|
||||
}
|
||||
}
|
||||
|
||||
protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
|
||||
int line, Font defaultFont,
|
||||
Color defaultColor, int x, int y)
|
||||
{
|
||||
textArea.getLineText(currentLineIndex,currentLine);
|
||||
currentLineTokens = tokenMarker.markTokens(currentLine,
|
||||
currentLineIndex);
|
||||
|
||||
paintHighlight(gfx,line,y);
|
||||
|
||||
gfx.setFont(defaultFont);
|
||||
gfx.setColor(defaultColor);
|
||||
y += fm.getHeight();
|
||||
x = SyntaxUtilities.paintSyntaxLine(currentLine,
|
||||
currentLineTokens,
|
||||
styles, this, gfx, x, y);
|
||||
|
||||
if (eolMarkers) {
|
||||
gfx.setColor(eolMarkerColor);
|
||||
gfx.drawString(".",x,y);
|
||||
}
|
||||
}
|
||||
|
||||
protected void paintHighlight(Graphics gfx, int line, int y)
|
||||
{
|
||||
if (line >= textArea.getSelectionStartLine()
|
||||
&& line <= textArea.getSelectionEndLine())
|
||||
paintLineHighlight(gfx,line,y);
|
||||
|
||||
if (highlights != null)
|
||||
highlights.paintHighlight(gfx,line,y);
|
||||
|
||||
if (bracketHighlight && line == textArea.getBracketLine())
|
||||
paintBracketHighlight(gfx,line,y);
|
||||
|
||||
if (line == textArea.getCaretLine())
|
||||
paintCaret(gfx,line,y);
|
||||
}
|
||||
|
||||
protected void paintLineHighlight(Graphics gfx, int line, int y)
|
||||
{
|
||||
int height = fm.getHeight();
|
||||
y += fm.getLeading() + fm.getMaxDescent();
|
||||
|
||||
int selectionStart = textArea.getSelectionStart();
|
||||
int selectionEnd = textArea.getSelectionEnd();
|
||||
|
||||
if (selectionStart == selectionEnd) {
|
||||
if (lineHighlight) {
|
||||
gfx.setColor(lineHighlightColor);
|
||||
gfx.fillRect(0,y,getWidth(),height);
|
||||
}
|
||||
} else {
|
||||
gfx.setColor(selectionColor);
|
||||
|
||||
int selectionStartLine = textArea.getSelectionStartLine();
|
||||
int selectionEndLine = textArea.getSelectionEndLine();
|
||||
int lineStart = textArea.getLineStartOffset(line);
|
||||
|
||||
int x1, x2;
|
||||
if (textArea.isSelectionRectangular()) {
|
||||
int lineLen = textArea.getLineLength(line);
|
||||
x1 = textArea._offsetToX(line,Math.min(lineLen, selectionStart - textArea.getLineStartOffset(selectionStartLine)));
|
||||
x2 = textArea._offsetToX(line,Math.min(lineLen, selectionEnd - textArea.getLineStartOffset(selectionEndLine)));
|
||||
if (x1 == x2)
|
||||
x2++;
|
||||
} else if(selectionStartLine == selectionEndLine) {
|
||||
x1 = textArea._offsetToX(line, selectionStart - lineStart);
|
||||
x2 = textArea._offsetToX(line, selectionEnd - lineStart);
|
||||
} else if(line == selectionStartLine) {
|
||||
x1 = textArea._offsetToX(line, selectionStart - lineStart);
|
||||
x2 = getWidth();
|
||||
} else if(line == selectionEndLine) {
|
||||
//x1 = 0;
|
||||
// hack from stendahl to avoid doing weird side selection thing
|
||||
x1 = textArea._offsetToX(line, 0);
|
||||
// attempt at getting the gutter too, but doesn't seem to work
|
||||
//x1 = textArea._offsetToX(line, -textArea.getHorizontalOffset());
|
||||
x2 = textArea._offsetToX(line, selectionEnd - lineStart);
|
||||
} else {
|
||||
//x1 = 0;
|
||||
// hack from stendahl to avoid doing weird side selection thing
|
||||
x1 = textArea._offsetToX(line, 0);
|
||||
// attempt at getting the gutter too, but doesn't seem to work
|
||||
//x1 = textArea._offsetToX(line, -textArea.getHorizontalOffset());
|
||||
x2 = getWidth();
|
||||
}
|
||||
|
||||
// "inlined" min/max()
|
||||
gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ?
|
||||
(x1 - x2) : (x2 - x1),height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void paintBracketHighlight(Graphics gfx, int line, int y)
|
||||
{
|
||||
int position = textArea.getBracketPosition();
|
||||
if(position == -1)
|
||||
return;
|
||||
y += fm.getLeading() + fm.getMaxDescent();
|
||||
int x = textArea._offsetToX(line,position);
|
||||
gfx.setColor(bracketHighlightColor);
|
||||
// Hack!!! Since there is no fast way to get the character
|
||||
// from the bracket matching routine, we use ( since all
|
||||
// brackets probably have the same width anyway
|
||||
gfx.drawRect(x,y,fm.charWidth('(') - 1,
|
||||
fm.getHeight() - 1);
|
||||
}
|
||||
|
||||
protected void paintCaret(Graphics gfx, int line, int y)
|
||||
{
|
||||
//System.out.println("painting caret " + line + " " + y);
|
||||
if (textArea.isCaretVisible()) {
|
||||
//System.out.println("caret is visible");
|
||||
int offset =
|
||||
textArea.getCaretPosition() - textArea.getLineStartOffset(line);
|
||||
int caretX = textArea._offsetToX(line, offset);
|
||||
int caretWidth = ((blockCaret ||
|
||||
textArea.isOverwriteEnabled()) ?
|
||||
fm.charWidth('w') : 1);
|
||||
y += fm.getLeading() + fm.getMaxDescent();
|
||||
int height = fm.getHeight();
|
||||
|
||||
//System.out.println("caretX, width = " + caretX + " " + caretWidth);
|
||||
|
||||
gfx.setColor(caretColor);
|
||||
|
||||
if (textArea.isOverwriteEnabled()) {
|
||||
gfx.fillRect(caretX,y + height - 1, caretWidth,1);
|
||||
|
||||
} else {
|
||||
// some machines don't like the drawRect for the single
|
||||
// pixel caret.. this caused a lot of hell because on that
|
||||
// minority of machines, the caret wouldn't show up past
|
||||
// the first column. the fix is to use drawLine() in
|
||||
// those cases, as a workaround.
|
||||
if (caretWidth == 1) {
|
||||
gfx.drawLine(caretX, y, caretX, y + height - 1);
|
||||
} else {
|
||||
gfx.drawRect(caretX, y, caretWidth - 1, height - 1);
|
||||
}
|
||||
//gfx.drawRect(caretX, y, caretWidth, height - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
184
app/syntax/TextUtilities.java
Normal file
184
app/syntax/TextUtilities.java
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* TextUtilities.java - Utility functions used by the text area classes
|
||||
* Copyright (C) 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import javax.swing.text.*;
|
||||
|
||||
/**
|
||||
* Class with several utility functions used by the text area component.
|
||||
* @author Slava Pestov
|
||||
* @version $Id: TextUtilities.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
|
||||
*/
|
||||
public class TextUtilities
|
||||
{
|
||||
/**
|
||||
* Returns the offset of the bracket matching the one at the
|
||||
* specified offset of the document, or -1 if the bracket is
|
||||
* unmatched (or if the character is not a bracket).
|
||||
* @param doc The document
|
||||
* @param offset The offset
|
||||
* @exception BadLocationException If an out-of-bounds access
|
||||
* was attempted on the document text
|
||||
*/
|
||||
public static int findMatchingBracket(Document doc, int offset)
|
||||
throws BadLocationException
|
||||
{
|
||||
if(doc.getLength() == 0)
|
||||
return -1;
|
||||
char c = doc.getText(offset,1).charAt(0);
|
||||
char cprime; // c` - corresponding character
|
||||
boolean direction; // true = back, false = forward
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case '(': cprime = ')'; direction = false; break;
|
||||
case ')': cprime = '('; direction = true; break;
|
||||
case '[': cprime = ']'; direction = false; break;
|
||||
case ']': cprime = '['; direction = true; break;
|
||||
case '{': cprime = '}'; direction = false; break;
|
||||
case '}': cprime = '{'; direction = true; break;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
int count;
|
||||
|
||||
// How to merge these two cases is left as an exercise
|
||||
// for the reader.
|
||||
|
||||
// Go back or forward
|
||||
if(direction)
|
||||
{
|
||||
// Count is 1 initially because we have already
|
||||
// `found' one closing bracket
|
||||
count = 1;
|
||||
|
||||
// Get text[0,offset-1];
|
||||
String text = doc.getText(0,offset);
|
||||
|
||||
// Scan backwards
|
||||
for(int i = offset - 1; i >= 0; i--)
|
||||
{
|
||||
// If text[i] == c, we have found another
|
||||
// closing bracket, therefore we will need
|
||||
// two opening brackets to complete the
|
||||
// match.
|
||||
char x = text.charAt(i);
|
||||
if(x == c)
|
||||
count++;
|
||||
|
||||
// If text[i] == cprime, we have found a
|
||||
// opening bracket, so we return i if
|
||||
// --count == 0
|
||||
else if(x == cprime)
|
||||
{
|
||||
if(--count == 0)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Count is 1 initially because we have already
|
||||
// `found' one opening bracket
|
||||
count = 1;
|
||||
|
||||
// So we don't have to + 1 in every loop
|
||||
offset++;
|
||||
|
||||
// Number of characters to check
|
||||
int len = doc.getLength() - offset;
|
||||
|
||||
// Get text[offset+1,len];
|
||||
String text = doc.getText(offset,len);
|
||||
|
||||
// Scan forwards
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
// If text[i] == c, we have found another
|
||||
// opening bracket, therefore we will need
|
||||
// two closing brackets to complete the
|
||||
// match.
|
||||
char x = text.charAt(i);
|
||||
|
||||
if(x == c)
|
||||
count++;
|
||||
|
||||
// If text[i] == cprime, we have found an
|
||||
// closing bracket, so we return i if
|
||||
// --count == 0
|
||||
else if(x == cprime)
|
||||
{
|
||||
if(--count == 0)
|
||||
return i + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the start of the word at the specified position.
|
||||
* @param line The text
|
||||
* @param pos The position
|
||||
*/
|
||||
public static int findWordStart(String line, int pos, String noWordSep)
|
||||
{
|
||||
char ch = line.charAt(pos - 1);
|
||||
|
||||
if(noWordSep == null)
|
||||
noWordSep = "";
|
||||
boolean selectNoLetter = (!Character.isLetterOrDigit(ch)
|
||||
&& noWordSep.indexOf(ch) == -1);
|
||||
|
||||
int wordStart = 0;
|
||||
for(int i = pos - 1; i >= 0; i--)
|
||||
{
|
||||
ch = line.charAt(i);
|
||||
if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) &&
|
||||
noWordSep.indexOf(ch) == -1))
|
||||
{
|
||||
wordStart = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return wordStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the end of the word at the specified position.
|
||||
* @param line The text
|
||||
* @param pos The position
|
||||
*/
|
||||
public static int findWordEnd(String line, int pos, String noWordSep)
|
||||
{
|
||||
char ch = line.charAt(pos);
|
||||
|
||||
if(noWordSep == null)
|
||||
noWordSep = "";
|
||||
boolean selectNoLetter = (!Character.isLetterOrDigit(ch)
|
||||
&& noWordSep.indexOf(ch) == -1);
|
||||
|
||||
int wordEnd = line.length();
|
||||
for(int i = pos; i < line.length(); i++)
|
||||
{
|
||||
ch = line.charAt(i);
|
||||
if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) &&
|
||||
noWordSep.indexOf(ch) == -1))
|
||||
{
|
||||
wordEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return wordEnd;
|
||||
}
|
||||
}
|
149
app/syntax/Token.java
Normal file
149
app/syntax/Token.java
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Token.java - Generic token
|
||||
* Copyright (C) 1998, 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
/**
|
||||
* A linked list of tokens. Each token has three fields - a token
|
||||
* identifier, which is a byte value that can be looked up in the
|
||||
* array returned by <code>SyntaxDocument.getColors()</code>
|
||||
* to get a color value, a length value which is the length of the
|
||||
* token in the text, and a pointer to the next token in the list.
|
||||
*
|
||||
* @author Slava Pestov
|
||||
* @version $Id: Token.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
|
||||
*/
|
||||
public class Token
|
||||
{
|
||||
/**
|
||||
* Normal text token id. This should be used to mark
|
||||
* normal text.
|
||||
*/
|
||||
public static final byte NULL = 0;
|
||||
|
||||
/**
|
||||
* Comment 1 token id. This can be used to mark a comment.
|
||||
*/
|
||||
public static final byte COMMENT1 = 1;
|
||||
|
||||
/**
|
||||
* Comment 2 token id. This can be used to mark a comment.
|
||||
*/
|
||||
public static final byte COMMENT2 = 2;
|
||||
|
||||
|
||||
/**
|
||||
* Literal 1 token id. This can be used to mark a string
|
||||
* literal (eg, C mode uses this to mark "..." literals)
|
||||
*/
|
||||
public static final byte LITERAL1 = 3;
|
||||
|
||||
/**
|
||||
* Literal 2 token id. This can be used to mark an object
|
||||
* literal (eg, Java mode uses this to mark true, false, etc)
|
||||
*/
|
||||
public static final byte LITERAL2 = 4;
|
||||
|
||||
/**
|
||||
* Label token id. This can be used to mark labels
|
||||
* (eg, C mode uses this to mark ...: sequences)
|
||||
*/
|
||||
public static final byte LABEL = 5;
|
||||
|
||||
/**
|
||||
* Keyword 1 token id. This can be used to mark a
|
||||
* keyword. This should be used for general language
|
||||
* constructs.
|
||||
*/
|
||||
public static final byte KEYWORD1 = 6;
|
||||
|
||||
/**
|
||||
* Keyword 2 token id. This can be used to mark a
|
||||
* keyword. This should be used for preprocessor
|
||||
* commands, or variables.
|
||||
*/
|
||||
public static final byte KEYWORD2 = 7;
|
||||
|
||||
/**
|
||||
* Keyword 3 token id. This can be used to mark a
|
||||
* keyword. This should be used for data types.
|
||||
*/
|
||||
public static final byte KEYWORD3 = 8;
|
||||
|
||||
/**
|
||||
* Operator token id. This can be used to mark an
|
||||
* operator. (eg, SQL mode marks +, -, etc with this
|
||||
* token type)
|
||||
*/
|
||||
public static final byte OPERATOR = 9;
|
||||
|
||||
/**
|
||||
* Invalid token id. This can be used to mark invalid
|
||||
* or incomplete tokens, so the user can easily spot
|
||||
* syntax errors.
|
||||
*/
|
||||
public static final byte INVALID = 10;
|
||||
|
||||
/**
|
||||
* The total number of defined token ids.
|
||||
*/
|
||||
public static final byte ID_COUNT = 11;
|
||||
|
||||
/**
|
||||
* The first id that can be used for internal state
|
||||
* in a token marker.
|
||||
*/
|
||||
public static final byte INTERNAL_FIRST = 100;
|
||||
|
||||
/**
|
||||
* The last id that can be used for internal state
|
||||
* in a token marker.
|
||||
*/
|
||||
public static final byte INTERNAL_LAST = 126;
|
||||
|
||||
/**
|
||||
* The token type, that along with a length of 0
|
||||
* marks the end of the token list.
|
||||
*/
|
||||
public static final byte END = 127;
|
||||
|
||||
/**
|
||||
* The length of this token.
|
||||
*/
|
||||
public int length;
|
||||
|
||||
/**
|
||||
* The id of this token.
|
||||
*/
|
||||
public byte id;
|
||||
|
||||
/**
|
||||
* The next token in the linked list.
|
||||
*/
|
||||
public Token next;
|
||||
|
||||
/**
|
||||
* Creates a new token.
|
||||
* @param length The length of the token
|
||||
* @param id The id of the token
|
||||
*/
|
||||
public Token(int length, byte id)
|
||||
{
|
||||
this.length = length;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this token.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return "[id=" + id + ",length=" + length + "]";
|
||||
}
|
||||
}
|
345
app/syntax/TokenMarker.java
Normal file
345
app/syntax/TokenMarker.java
Normal file
@ -0,0 +1,345 @@
|
||||
/*
|
||||
* TokenMarker.java - Generic token marker
|
||||
* Copyright (C) 1998, 1999 Slava Pestov
|
||||
*
|
||||
* You may use and modify this package for any purpose. Redistribution is
|
||||
* permitted, in both source and binary form, provided that this notice
|
||||
* remains intact in all source distributions of this package.
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A token marker that splits lines of text into tokens. Each token carries
|
||||
* a length field and an indentification tag that can be mapped to a color
|
||||
* for painting that token.<p>
|
||||
*
|
||||
* For performance reasons, the linked list of tokens is reused after each
|
||||
* line is tokenized. Therefore, the return value of <code>markTokens</code>
|
||||
* should only be used for immediate painting. Notably, it cannot be
|
||||
* cached.
|
||||
*
|
||||
* @author Slava Pestov
|
||||
* @version $Id: TokenMarker.java,v 1.1 2005/04/09 02:30:37 benfry Exp $
|
||||
*
|
||||
* @see org.gjt.sp.jedit.syntax.Token
|
||||
*/
|
||||
public abstract class TokenMarker
|
||||
{
|
||||
/**
|
||||
* A wrapper for the lower-level <code>markTokensImpl</code> method
|
||||
* that is called to split a line up into tokens.
|
||||
* @param line The line
|
||||
* @param lineIndex The line number
|
||||
*/
|
||||
public Token markTokens(Segment line, int lineIndex)
|
||||
{
|
||||
if(lineIndex >= length)
|
||||
{
|
||||
throw new IllegalArgumentException("Tokenizing invalid line: "
|
||||
+ lineIndex);
|
||||
}
|
||||
|
||||
lastToken = null;
|
||||
|
||||
LineInfo info = lineInfo[lineIndex];
|
||||
LineInfo prev;
|
||||
if(lineIndex == 0)
|
||||
prev = null;
|
||||
else
|
||||
prev = lineInfo[lineIndex - 1];
|
||||
|
||||
byte oldToken = info.token;
|
||||
byte token = markTokensImpl(prev == null ?
|
||||
Token.NULL : prev.token,line,lineIndex);
|
||||
|
||||
info.token = token;
|
||||
|
||||
/*
|
||||
* This is a foul hack. It stops nextLineRequested
|
||||
* from being cleared if the same line is marked twice.
|
||||
*
|
||||
* Why is this necessary? It's all JEditTextArea's fault.
|
||||
* When something is inserted into the text, firing a
|
||||
* document event, the insertUpdate() method shifts the
|
||||
* caret (if necessary) by the amount inserted.
|
||||
*
|
||||
* All caret movement is handled by the select() method,
|
||||
* which eventually pipes the new position to scrollTo()
|
||||
* and calls repaint().
|
||||
*
|
||||
* Note that at this point in time, the new line hasn't
|
||||
* yet been painted; the caret is moved first.
|
||||
*
|
||||
* scrollTo() calls offsetToX(), which tokenizes the line
|
||||
* unless it is being called on the last line painted
|
||||
* (in which case it uses the text area's painter cached
|
||||
* token list). What scrollTo() does next is irrelevant.
|
||||
*
|
||||
* After scrollTo() has done it's job, repaint() is
|
||||
* called, and eventually we end up in paintLine(), whose
|
||||
* job is to paint the changed line. It, too, calls
|
||||
* markTokens().
|
||||
*
|
||||
* The problem was that if the line started a multiline
|
||||
* token, the first markTokens() (done in offsetToX())
|
||||
* would set nextLineRequested (because the line end
|
||||
* token had changed) but the second would clear it
|
||||
* (because the line was the same that time) and therefore
|
||||
* paintLine() would never know that it needed to repaint
|
||||
* subsequent lines.
|
||||
*
|
||||
* This bug took me ages to track down, that's why I wrote
|
||||
* all the relevant info down so that others wouldn't
|
||||
* duplicate it.
|
||||
*/
|
||||
if(!(lastLine == lineIndex && nextLineRequested))
|
||||
nextLineRequested = (oldToken != token);
|
||||
|
||||
lastLine = lineIndex;
|
||||
|
||||
addToken(0,Token.END);
|
||||
|
||||
return firstToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract method that splits a line up into tokens. It
|
||||
* should parse the line, and call <code>addToken()</code> to
|
||||
* add syntax tokens to the token list. Then, it should return
|
||||
* the initial token type for the next line.<p>
|
||||
*
|
||||
* For example if the current line contains the start of a
|
||||
* multiline comment that doesn't end on that line, this method
|
||||
* should return the comment token type so that it continues on
|
||||
* the next line.
|
||||
*
|
||||
* @param token The initial token type for this line
|
||||
* @param line The line to be tokenized
|
||||
* @param lineIndex The index of the line in the document,
|
||||
* starting at 0
|
||||
* @return The initial token type for the next line
|
||||
*/
|
||||
protected abstract byte markTokensImpl(byte token, Segment line,
|
||||
int lineIndex);
|
||||
|
||||
/**
|
||||
* Returns if the token marker supports tokens that span multiple
|
||||
* lines. If this is true, the object using this token marker is
|
||||
* required to pass all lines in the document to the
|
||||
* <code>markTokens()</code> method (in turn).<p>
|
||||
*
|
||||
* The default implementation returns true; it should be overridden
|
||||
* to return false on simpler token markers for increased speed.
|
||||
*/
|
||||
public boolean supportsMultilineTokens()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the token marker that lines have been inserted into
|
||||
* the document. This inserts a gap in the <code>lineInfo</code>
|
||||
* array.
|
||||
* @param index The first line number
|
||||
* @param lines The number of lines
|
||||
*/
|
||||
public void insertLines(int index, int lines)
|
||||
{
|
||||
if(lines <= 0)
|
||||
return;
|
||||
length += lines;
|
||||
ensureCapacity(length);
|
||||
int len = index + lines;
|
||||
System.arraycopy(lineInfo,index,lineInfo,len,
|
||||
lineInfo.length - len);
|
||||
|
||||
for(int i = index + lines - 1; i >= index; i--)
|
||||
{
|
||||
lineInfo[i] = new LineInfo();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the token marker that line have been deleted from
|
||||
* the document. This removes the lines in question from the
|
||||
* <code>lineInfo</code> array.
|
||||
* @param index The first line number
|
||||
* @param lines The number of lines
|
||||
*/
|
||||
public void deleteLines(int index, int lines)
|
||||
{
|
||||
if (lines <= 0)
|
||||
return;
|
||||
int len = index + lines;
|
||||
length -= lines;
|
||||
System.arraycopy(lineInfo,len,lineInfo,
|
||||
index,lineInfo.length - len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of lines in this token marker.
|
||||
*/
|
||||
public int getLineCount()
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the next line should be repainted. This
|
||||
* will return true after a line has been tokenized that starts
|
||||
* a multiline token that continues onto the next line.
|
||||
*/
|
||||
public boolean isNextLineRequested()
|
||||
{
|
||||
return nextLineRequested;
|
||||
}
|
||||
|
||||
// protected members
|
||||
|
||||
/**
|
||||
* The first token in the list. This should be used as the return
|
||||
* value from <code>markTokens()</code>.
|
||||
*/
|
||||
protected Token firstToken;
|
||||
|
||||
/**
|
||||
* The last token in the list. New tokens are added here.
|
||||
* This should be set to null before a new line is to be tokenized.
|
||||
*/
|
||||
protected Token lastToken;
|
||||
|
||||
/**
|
||||
* An array for storing information about lines. It is enlarged and
|
||||
* shrunk automatically by the <code>insertLines()</code> and
|
||||
* <code>deleteLines()</code> methods.
|
||||
*/
|
||||
protected LineInfo[] lineInfo;
|
||||
|
||||
/**
|
||||
* The number of lines in the model being tokenized. This can be
|
||||
* less than the length of the <code>lineInfo</code> array.
|
||||
*/
|
||||
protected int length;
|
||||
|
||||
/**
|
||||
* The last tokenized line.
|
||||
*/
|
||||
protected int lastLine;
|
||||
|
||||
/**
|
||||
* True if the next line should be painted.
|
||||
*/
|
||||
protected boolean nextLineRequested;
|
||||
|
||||
/**
|
||||
* Creates a new <code>TokenMarker</code>. This DOES NOT create
|
||||
* a lineInfo array; an initial call to <code>insertLines()</code>
|
||||
* does that.
|
||||
*/
|
||||
protected TokenMarker()
|
||||
{
|
||||
lastLine = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the <code>lineInfo</code> array can contain the
|
||||
* specified index. This enlarges it if necessary. No action is
|
||||
* taken if the array is large enough already.<p>
|
||||
*
|
||||
* It should be unnecessary to call this under normal
|
||||
* circumstances; <code>insertLine()</code> should take care of
|
||||
* enlarging the line info array automatically.
|
||||
*
|
||||
* @param index The array index
|
||||
*/
|
||||
protected void ensureCapacity(int index)
|
||||
{
|
||||
if(lineInfo == null)
|
||||
lineInfo = new LineInfo[index + 1];
|
||||
else if(lineInfo.length <= index)
|
||||
{
|
||||
LineInfo[] lineInfoN = new LineInfo[(index + 1) * 2];
|
||||
System.arraycopy(lineInfo,0,lineInfoN,0,
|
||||
lineInfo.length);
|
||||
lineInfo = lineInfoN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a token to the token list.
|
||||
* @param length The length of the token
|
||||
* @param id The id of the token
|
||||
*/
|
||||
protected void addToken(int length, byte id)
|
||||
{
|
||||
if(id >= Token.INTERNAL_FIRST && id <= Token.INTERNAL_LAST)
|
||||
throw new InternalError("Invalid id: " + id);
|
||||
|
||||
if(length == 0 && id != Token.END)
|
||||
return;
|
||||
|
||||
if(firstToken == null)
|
||||
{
|
||||
firstToken = new Token(length,id);
|
||||
lastToken = firstToken;
|
||||
}
|
||||
else if(lastToken == null)
|
||||
{
|
||||
lastToken = firstToken;
|
||||
firstToken.length = length;
|
||||
firstToken.id = id;
|
||||
}
|
||||
else if(lastToken.next == null)
|
||||
{
|
||||
lastToken.next = new Token(length,id);
|
||||
lastToken = lastToken.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastToken = lastToken.next;
|
||||
lastToken.length = length;
|
||||
lastToken.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class for storing information about tokenized lines.
|
||||
*/
|
||||
public class LineInfo
|
||||
{
|
||||
/**
|
||||
* Creates a new LineInfo object with token = Token.NULL
|
||||
* and obj = null.
|
||||
*/
|
||||
public LineInfo()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new LineInfo object with the specified
|
||||
* parameters.
|
||||
*/
|
||||
public LineInfo(byte token, Object obj)
|
||||
{
|
||||
this.token = token;
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the last token of the line.
|
||||
*/
|
||||
public byte token;
|
||||
|
||||
/**
|
||||
* This is for use by the token marker implementations
|
||||
* themselves. It can be used to store anything that
|
||||
* is an object and that needs to exist on a per-line
|
||||
* basis.
|
||||
*/
|
||||
public Object obj;
|
||||
}
|
||||
}
|
46
app/syntax/readme.txt
Normal file
46
app/syntax/readme.txt
Normal file
@ -0,0 +1,46 @@
|
||||
OLDSYNTAX PACKAGE README
|
||||
|
||||
I am placing the jEdit 2.2.1 syntax highlighting package in the public
|
||||
domain. This means it can be integrated into commercial programs, etc.
|
||||
|
||||
This package requires at least Java 1.1 and Swing 1.1. Syntax
|
||||
highlighting for the following file types is supported:
|
||||
|
||||
- C++, C
|
||||
- CORBA IDL
|
||||
- Eiffel
|
||||
- HTML
|
||||
- Java
|
||||
- Java properties
|
||||
- JavaScript
|
||||
- MS-DOS INI
|
||||
- MS-DOS batch files
|
||||
- Makefile
|
||||
- PHP
|
||||
- Perl
|
||||
- Python
|
||||
- TeX
|
||||
- Transact-SQL
|
||||
- Unix patch/diff
|
||||
- Unix shell script
|
||||
- XML
|
||||
|
||||
This package is undocumented; read the source (start by taking a look at
|
||||
JEditTextArea.java) to find out how to use it; it's really simple. Feel
|
||||
free to e-mail questions, queries, etc. to me, but keep in mind that
|
||||
this code is very old and I no longer maintain it. So if you find a bug,
|
||||
don't bother me about it; fix it yourself.
|
||||
|
||||
* Copyright
|
||||
|
||||
The jEdit 2.2.1 syntax highlighting package contains code that is
|
||||
Copyright 1998-1999 Slava Pestov, Artur Biesiadowski, Clancy Malcolm,
|
||||
Jonathan Revusky, Juha Lindfors and Mike Dillon.
|
||||
|
||||
You may use and modify this package for any purpose. Redistribution is
|
||||
permitted, in both source and binary form, provided that this notice
|
||||
remains intact in all source distributions of this package.
|
||||
|
||||
-- Slava Pestov
|
||||
25 September 2000
|
||||
<sp@gjt.org>
|
Reference in New Issue
Block a user