mirror of
https://github.com/processing/processing4.git
synced 2026-02-03 21:59:20 +01:00
cvs cleanup
This commit is contained in:
@@ -1,123 +0,0 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
PdeCompilerJavac - compiler interface to kjc.. someday this will go away
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Except where noted, code is written by Ben Fry and
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
// this class is incomplete. it was added in an attempt to support
|
||||
// mac os 9, but the javac for jdk 1.1 doesn't seem to like the
|
||||
// file format of the classes produced by jikes, which is revision
|
||||
// 57 code instead of rev 55.
|
||||
|
||||
// using jikes -target 1.1 doesn't seem to help. the next attempt
|
||||
// was to use javac from the 1.1 jdk, which i attempted to hack
|
||||
// into the bagel build scripts, but javac actually crashed (not
|
||||
// just and exception, but an application crash) whenever used
|
||||
// (especially if called on long paths/from another directory).
|
||||
|
||||
// there are plenty of ways to address this, but for the time being,
|
||||
// it's not worth the time so we'll just suspend macos9 support.
|
||||
|
||||
public class PdeCompilerJavac extends PdeCompiler {
|
||||
|
||||
public PdeCompilerJavac(String buildPath, String className,
|
||||
File includeFolder, PdeEditor editor) {
|
||||
super(buildPath, className, includeFolder, editor);
|
||||
}
|
||||
|
||||
public boolean compileJava(PrintStream leechErr) {
|
||||
|
||||
// probably not needed with javac
|
||||
//System.setErr(leechErr); // redirect stderr to our leech filter
|
||||
|
||||
int argc = 0;
|
||||
String args[] = new String[4];
|
||||
args[argc++] = "-d";
|
||||
args[argc++] = buildPath;
|
||||
args[argc++] = "-nowarn";
|
||||
args[argc++] = buildPath + File.separator + className + ".java";
|
||||
//System.out.println("args = " + args[0] + " " + args[1]);
|
||||
|
||||
System.out.println("compiling with javac");
|
||||
return new sun.tools.javac.Main(leechErr, "javac").compile(args);
|
||||
|
||||
// probably not needed with javac
|
||||
//System.setErr(PdeEditorConsole.consoleErr);
|
||||
|
||||
//System.err.println("success = " + success);
|
||||
//return success;
|
||||
}
|
||||
|
||||
// part of the PdeMessageConsumer interface
|
||||
//
|
||||
public void message(String s) {
|
||||
System.out.println("javac msg: " + s);
|
||||
|
||||
// as in: lib\build\Temporary_5476_6442.java:88: caution:Assignment of an expression to itself [KOPI]
|
||||
if (s.indexOf("caution") != -1) return;
|
||||
|
||||
//
|
||||
//System.out.println("leech2: " + new String(b, offset, length));
|
||||
//String s = new String(b, offset, length);
|
||||
//if (s.indexOf(tempFilename) == 0) {
|
||||
String fullTempFilename = buildPath + File.separator + className + ".java";
|
||||
if (s.indexOf(fullTempFilename) == 0) {
|
||||
String s1 = s.substring(fullTempFilename.length() + 1);
|
||||
int colon = s1.indexOf(':');
|
||||
int lineNumber = Integer.parseInt(s1.substring(0, colon));
|
||||
//System.out.println("pde / line number: " + lineNumber);
|
||||
|
||||
//String s2 = s1.substring(colon + 2);
|
||||
int err = s1.indexOf("error:");
|
||||
if (err != -1) {
|
||||
//err += "error:".length();
|
||||
String description = s1.substring(err + "error:".length());
|
||||
description = description.trim();
|
||||
|
||||
// as in: ...error:Constructor setup must be named Temporary_5362_2548 [JL1 8.6]
|
||||
if(description.indexOf("Constructor setup must be named") != -1) {
|
||||
description = "Missing function return type, or constructor does not match class name";
|
||||
}
|
||||
//exception = new PdeException(description, lineNumber-2);
|
||||
exception = new PdeException(description, lineNumber-1);
|
||||
editor.error(exception);
|
||||
|
||||
} else {
|
||||
System.err.println("i suck: " + s);
|
||||
}
|
||||
|
||||
} else {
|
||||
//System.err.println("don't understand: " + s);
|
||||
exception = new PdeException(SUPER_BADNESS);
|
||||
editor.error(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
9
app/preproc/.cvsignore
Normal file
9
app/preproc/.cvsignore
Normal file
@@ -0,0 +1,9 @@
|
||||
*Lexer.java
|
||||
*Recognizer.java
|
||||
*TokenTypes.java
|
||||
*TokenTypes.txt
|
||||
*TreeParser.java
|
||||
*TreeParserTokenTypes.java
|
||||
*TreeParserTokenTypes.txt
|
||||
expanded*.g
|
||||
|
||||
132
app/preproc/ExtendedCommonASTWithHiddenTokens.java
Normal file
132
app/preproc/ExtendedCommonASTWithHiddenTokens.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package antlr;
|
||||
|
||||
/* ANTLR Translator Generator
|
||||
* Project led by Terence Parr at http://www.jGuru.com
|
||||
* Software rights: http://www.antlr.org/RIGHTS.html
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import antlr.*;
|
||||
import antlr.collections.*;
|
||||
import antlr.collections.impl.*;
|
||||
|
||||
/** A CommonAST whose initialization copies hidden token
|
||||
* information from the Token used to create a node.
|
||||
*/
|
||||
public class ExtendedCommonASTWithHiddenTokens
|
||||
extends CommonASTWithHiddenTokens {
|
||||
|
||||
public ExtendedCommonASTWithHiddenTokens() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ExtendedCommonASTWithHiddenTokens(Token tok) {
|
||||
super(tok);
|
||||
}
|
||||
|
||||
public void initialize(AST ast) {
|
||||
ExtendedCommonASTWithHiddenTokens a =
|
||||
(ExtendedCommonASTWithHiddenTokens)ast;
|
||||
super.initialize(a);
|
||||
hiddenBefore = a.getHiddenBefore();
|
||||
hiddenAfter = a.getHiddenAfter();
|
||||
}
|
||||
|
||||
public String getHiddenAfterString() {
|
||||
|
||||
CommonHiddenStreamToken t;
|
||||
StringBuffer hiddenAfterString = new StringBuffer(100);
|
||||
|
||||
for ( t = hiddenAfter ; t != null ; t = t.getHiddenAfter() ) {
|
||||
hiddenAfterString.append(t.getText());
|
||||
}
|
||||
|
||||
return hiddenAfterString.toString();
|
||||
}
|
||||
|
||||
public String getHiddenBeforeString() {
|
||||
|
||||
antlr.CommonHiddenStreamToken
|
||||
child = null,
|
||||
parent = hiddenBefore;
|
||||
|
||||
// if there aren't any hidden tokens here, quietly return
|
||||
//
|
||||
if (parent == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// traverse back to the head of the list of tokens before this node
|
||||
do {
|
||||
child = parent;
|
||||
parent = child.getHiddenBefore();
|
||||
} while (parent != null);
|
||||
|
||||
// dump that list
|
||||
|
||||
StringBuffer hiddenBeforeString = new StringBuffer(100);
|
||||
|
||||
for ( CommonHiddenStreamToken t = child; t != null ;
|
||||
t = t.getHiddenAfter() ) {
|
||||
hiddenBeforeString.append(t.getText());
|
||||
}
|
||||
|
||||
return hiddenBeforeString.toString();
|
||||
}
|
||||
|
||||
public void xmlSerializeNode(Writer out)
|
||||
throws IOException {
|
||||
StringBuffer buf = new StringBuffer(100);
|
||||
buf.append("<");
|
||||
buf.append(getClass().getName() + " ");
|
||||
|
||||
buf.append("hiddenBeforeString=\"" +
|
||||
encode(getHiddenBeforeString()) +
|
||||
"\" text=\"" + encode(getText()) + "\" type=\"" +
|
||||
getType() + "\" hiddenAfterString=\"" +
|
||||
encode(getHiddenAfterString()) + "\"/>");
|
||||
out.write(buf.toString());
|
||||
}
|
||||
|
||||
public void xmlSerializeRootOpen(Writer out)
|
||||
throws IOException {
|
||||
StringBuffer buf = new StringBuffer(100);
|
||||
buf.append("<");
|
||||
buf.append(getClass().getName() + " ");
|
||||
buf.append("hiddenBeforeString=\"" +
|
||||
encode(getHiddenBeforeString()) +
|
||||
"\" text=\"" + encode(getText()) + "\" type=\"" +
|
||||
getType() + "\" hiddenAfterString=\"" +
|
||||
encode(getHiddenAfterString()) + "\">\n");
|
||||
out.write(buf.toString());
|
||||
}
|
||||
|
||||
public void xmlSerializeRootClose(Writer out)
|
||||
throws IOException {
|
||||
out.write("</" + getClass().getName() + ">\n");
|
||||
}
|
||||
|
||||
public void xmlSerialize(Writer out) throws IOException {
|
||||
// print out this node and all siblings
|
||||
for (AST node = this;
|
||||
node != null;
|
||||
node = node.getNextSibling()) {
|
||||
if (node.getFirstChild() == null) {
|
||||
// print guts (class name, attributes)
|
||||
((BaseAST)node).xmlSerializeNode(out);
|
||||
}
|
||||
else {
|
||||
((BaseAST)node).xmlSerializeRootOpen(out);
|
||||
|
||||
// print children
|
||||
((BaseAST)node.getFirstChild()).xmlSerialize(out);
|
||||
|
||||
// print end tag
|
||||
((BaseAST)node).xmlSerializeRootClose(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
922
app/preproc/PdeEmitter.java
Normal file
922
app/preproc/PdeEmitter.java
Normal file
@@ -0,0 +1,922 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
package processing.app.preproc;
|
||||
|
||||
import processing.app.*;
|
||||
|
||||
|
||||
/* Based on original code copyright (c) 2003 Andy Tripp <atripp@comcast.net>.
|
||||
* shipped under GPL with permission.
|
||||
*/
|
||||
|
||||
import antlr.*;
|
||||
import antlr.collections.*;
|
||||
import antlr.collections.impl.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* PDEEmitter: A class that can take an ANTLR Java AST and produce
|
||||
* reasonably formatted Java code from it. To use it, create a
|
||||
* PDEEmitter object, call setOut() if you want to print to something
|
||||
* other than System.out, and then call print(), passing the
|
||||
* AST. Typically, the AST node that you pass would be the root of a
|
||||
* tree - the ROOT_ID node that represents a Java file.
|
||||
*/
|
||||
|
||||
public class PdeEmitter implements PdeTokenTypes
|
||||
{
|
||||
private PrintStream out = System.out;
|
||||
private PrintStream debug = System.err;
|
||||
private static int ALL = -1;
|
||||
private java.util.Stack stack = new java.util.Stack();
|
||||
private static String[] tokenNames;
|
||||
private final static int ROOT_ID = 0;
|
||||
static {
|
||||
setupTokenNames();
|
||||
}
|
||||
|
||||
/*
|
||||
private static Hashtable publicMethods;
|
||||
private static final String publicMethodList[] = {
|
||||
"setup", "draw", //"loop",
|
||||
"mousePressed", "mouseReleased", "mouseClicked",
|
||||
"mouseEntered", "mouseExited",
|
||||
"mouseMoved", "mouseDragged",
|
||||
"keyPressed", "keyReleased", "keyTyped"
|
||||
};
|
||||
|
||||
static {
|
||||
publicMethods = new Hashtable();
|
||||
for (int i = 0; i < publicMethodList.length; i++) {
|
||||
publicMethods.put(publicMethodList[i], new Object());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Map each AST token type to a String
|
||||
private static void setupTokenNames() {
|
||||
tokenNames = new String[200];
|
||||
for (int i=0; i<tokenNames.length; i++) {
|
||||
tokenNames[i] = "ERROR:" + i;
|
||||
}
|
||||
|
||||
tokenNames[POST_INC]="++";
|
||||
tokenNames[POST_DEC]="--";
|
||||
tokenNames[UNARY_MINUS]="-";
|
||||
tokenNames[UNARY_PLUS]="+";
|
||||
tokenNames[STAR]="*";
|
||||
tokenNames[ASSIGN]="=";
|
||||
tokenNames[PLUS_ASSIGN]="+=";
|
||||
tokenNames[MINUS_ASSIGN]="-=";
|
||||
tokenNames[STAR_ASSIGN]="*=";
|
||||
tokenNames[DIV_ASSIGN]="/=";
|
||||
tokenNames[MOD_ASSIGN]="%=";
|
||||
tokenNames[SR_ASSIGN]=">>=";
|
||||
tokenNames[BSR_ASSIGN]=">>>=";
|
||||
tokenNames[SL_ASSIGN]="<<=";
|
||||
tokenNames[BAND_ASSIGN]="&=";
|
||||
tokenNames[BXOR_ASSIGN]="^=";
|
||||
tokenNames[BOR_ASSIGN]="|=";
|
||||
tokenNames[QUESTION]="?";
|
||||
tokenNames[LOR]="||";
|
||||
tokenNames[LAND]="&&";
|
||||
tokenNames[BOR]="|";
|
||||
tokenNames[BXOR]="^";
|
||||
tokenNames[BAND]="&";
|
||||
tokenNames[NOT_EQUAL]="!=";
|
||||
tokenNames[EQUAL]="==";
|
||||
tokenNames[LT]="<";
|
||||
tokenNames[GT]=">";
|
||||
tokenNames[LE]="<=";
|
||||
tokenNames[GE]=">=";
|
||||
tokenNames[SL]="<<";
|
||||
tokenNames[SR]=">>";
|
||||
tokenNames[BSR]=">>>";
|
||||
tokenNames[PLUS]="+";
|
||||
tokenNames[MINUS]="-";
|
||||
tokenNames[DIV]="/";
|
||||
tokenNames[MOD]="%";
|
||||
tokenNames[INC]="++";
|
||||
tokenNames[DEC]="--";
|
||||
tokenNames[BNOT]="~";
|
||||
tokenNames[LNOT]="!";
|
||||
tokenNames[FINAL]="final";
|
||||
tokenNames[ABSTRACT]="abstract";
|
||||
tokenNames[LITERAL_package]="package";
|
||||
tokenNames[LITERAL_import]="import";
|
||||
tokenNames[LITERAL_void]="void";
|
||||
tokenNames[LITERAL_boolean]="boolean";
|
||||
tokenNames[LITERAL_byte]="byte";
|
||||
tokenNames[LITERAL_char]="char";
|
||||
tokenNames[LITERAL_short]="short";
|
||||
tokenNames[LITERAL_int]="int";
|
||||
tokenNames[LITERAL_float]="float";
|
||||
tokenNames[LITERAL_long]="long";
|
||||
tokenNames[LITERAL_double]="double";
|
||||
tokenNames[LITERAL_private]="private";
|
||||
tokenNames[LITERAL_public]="public";
|
||||
tokenNames[LITERAL_protected]="protected";
|
||||
tokenNames[LITERAL_static]="static";
|
||||
tokenNames[LITERAL_transient]="transient";
|
||||
tokenNames[LITERAL_native]="native";
|
||||
tokenNames[LITERAL_threadsafe]="threadsafe";
|
||||
tokenNames[LITERAL_synchronized]="synchronized";
|
||||
tokenNames[LITERAL_volatile]="volatile";
|
||||
tokenNames[LITERAL_class]="class";
|
||||
tokenNames[LITERAL_extends]="extends";
|
||||
tokenNames[LITERAL_interface]="interface";
|
||||
tokenNames[LITERAL_implements]="implements";
|
||||
tokenNames[LITERAL_throws]="throws";
|
||||
tokenNames[LITERAL_if]="if";
|
||||
tokenNames[LITERAL_else]="else";
|
||||
tokenNames[LITERAL_for]="for";
|
||||
tokenNames[LITERAL_while]="while";
|
||||
tokenNames[LITERAL_do]="do";
|
||||
tokenNames[LITERAL_break]="break";
|
||||
tokenNames[LITERAL_continue]="continue";
|
||||
tokenNames[LITERAL_return]="return";
|
||||
tokenNames[LITERAL_switch]="switch";
|
||||
tokenNames[LITERAL_throw]="throw";
|
||||
tokenNames[LITERAL_case]="case";
|
||||
tokenNames[LITERAL_default]="default";
|
||||
tokenNames[LITERAL_try]="try";
|
||||
tokenNames[LITERAL_finally]="finally";
|
||||
tokenNames[LITERAL_catch]="catch";
|
||||
tokenNames[LITERAL_instanceof]="instanceof";
|
||||
tokenNames[LITERAL_this]="this";
|
||||
tokenNames[LITERAL_super]="super";
|
||||
tokenNames[LITERAL_true]="true";
|
||||
tokenNames[LITERAL_false]="false";
|
||||
tokenNames[LITERAL_null]="null";
|
||||
tokenNames[LITERAL_new]="new";
|
||||
tokenNames[LITERAL_color]="int"; // PDE specific alias
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a PrintStream to print to. System.out is the default.
|
||||
* @param out the PrintStream to print to
|
||||
*/
|
||||
public void setOut(PrintStream out) {
|
||||
this.out = out;
|
||||
}
|
||||
private String name(AST ast) {
|
||||
return tokenNames[ast.getType()];
|
||||
}
|
||||
private String name(int type) {
|
||||
return tokenNames[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a child of the given AST that has the given type
|
||||
* @returns a child AST of the given type. If it can't find a child of the
|
||||
* given type, return null.
|
||||
*/
|
||||
private AST getChild(AST ast, int childType) {
|
||||
AST child = ast.getFirstChild();
|
||||
while (child != null) {
|
||||
if (child.getType() == childType) {
|
||||
// debug.println("getChild: found:" + name(ast));
|
||||
return child;
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the list of hidden tokens linked to after the AST node passed in.
|
||||
* Most hidden tokens are dumped from this function.
|
||||
*/
|
||||
private void dumpHiddenAfter(AST ast) {
|
||||
dumpHiddenTokens(((antlr.CommonASTWithHiddenTokens)ast).getHiddenAfter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the list of hidden tokens linked to before the AST node passed in.
|
||||
* The only time hidden tokens need to be dumped with this function is when
|
||||
* dealing parts of the tree where automatic tree construction was
|
||||
* turned off with the ! operator in the grammar file and the nodes were
|
||||
* manually constructed in such a way that the usual tokens don't have the
|
||||
* necessary hiddenAfter links.
|
||||
*/
|
||||
private void dumpHiddenBefore(AST ast) {
|
||||
|
||||
antlr.CommonHiddenStreamToken
|
||||
child = null,
|
||||
parent = ((antlr.CommonASTWithHiddenTokens)ast).getHiddenBefore();
|
||||
|
||||
// if there aren't any hidden tokens here, quietly return
|
||||
//
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// traverse back to the head of the list of tokens before this node
|
||||
do {
|
||||
child = parent;
|
||||
parent = child.getHiddenBefore();
|
||||
} while (parent != null);
|
||||
|
||||
// dump that list
|
||||
dumpHiddenTokens(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the list of hidden tokens linked to from the token passed in.
|
||||
*/
|
||||
private void dumpHiddenTokens(antlr.CommonHiddenStreamToken t) {
|
||||
for ( ; t != null ; t=PdePreprocessor.filter.getHiddenAfter(t) ) {
|
||||
out.print(t.getText());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the children of the given AST
|
||||
* @param ast The AST to print
|
||||
* @returns true iff anything was printed
|
||||
*/
|
||||
private boolean printChildren(AST ast) throws PdeException {
|
||||
boolean ret = false;
|
||||
AST child = ast.getFirstChild();
|
||||
while (child != null) {
|
||||
ret = true;
|
||||
print(child);
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether an AST has any children or not.
|
||||
* @return true iff the AST has at least one child
|
||||
*/
|
||||
private boolean hasChildren(AST ast) {
|
||||
return (ast.getFirstChild() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the best node in the subtree for printing. This really means
|
||||
* the next node which could potentially have hiddenBefore data. It's
|
||||
* usually the first printable leaf, but not always.
|
||||
*
|
||||
* @param includeThisNode Should this node be included in the search?
|
||||
* If false, only descendants are searched.
|
||||
*
|
||||
* @return the first printable leaf node in an AST
|
||||
*/
|
||||
private AST getBestPrintableNode(AST ast, boolean includeThisNode) {
|
||||
AST child;
|
||||
|
||||
if (includeThisNode) {
|
||||
child = ast;
|
||||
} else {
|
||||
child = ast.getFirstChild();
|
||||
}
|
||||
|
||||
if (child != null) {
|
||||
|
||||
switch (child.getType()) {
|
||||
|
||||
// the following node types are printing nodes that print before
|
||||
// any children, but then also recurse over children. So they
|
||||
// may have hiddenBefore chains that need to be printed first. Many
|
||||
// statements and all unary expression types qualify. Return these
|
||||
// nodes directly
|
||||
case CLASS_DEF:
|
||||
case LITERAL_if:
|
||||
case LITERAL_for:
|
||||
case LITERAL_while:
|
||||
case LITERAL_do:
|
||||
case LITERAL_break:
|
||||
case LITERAL_continue:
|
||||
case LITERAL_return:
|
||||
case LITERAL_switch:
|
||||
case LITERAL_try:
|
||||
case LITERAL_throw:
|
||||
case LITERAL_synchronized:
|
||||
case LITERAL_assert:
|
||||
case BNOT:
|
||||
case LNOT:
|
||||
case INC:
|
||||
case DEC:
|
||||
case UNARY_MINUS:
|
||||
case UNARY_PLUS:
|
||||
return child;
|
||||
|
||||
// Some non-terminal node types (at the moment, I only know of
|
||||
// MODIFIERS, but there may be other such types), can be
|
||||
// leaves in the tree but not have any children. If this is
|
||||
// such a node, move on to the next sibling.
|
||||
case MODIFIERS:
|
||||
if (child.getFirstChild() == null ) {
|
||||
return getBestPrintableNode(child.getNextSibling(), false);
|
||||
}
|
||||
// new jikes doesn't like fallthrough, so just duplicated here:
|
||||
return getBestPrintableNode(child, false);
|
||||
|
||||
default:
|
||||
return getBestPrintableNode(child, false);
|
||||
}
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a binary operator
|
||||
*/
|
||||
private void printBinaryOperator(AST ast) throws PdeException {
|
||||
print(ast.getFirstChild());
|
||||
out.print(name(ast));
|
||||
dumpHiddenAfter(ast);
|
||||
print(ast.getFirstChild().getNextSibling());
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the given AST. Call this function to print your PDE code.
|
||||
*
|
||||
* It works by making recursive calls to print children.
|
||||
* So the code below is one big "switch" statement on the passed AST type.
|
||||
*/
|
||||
public void print (AST ast) throws PdeException {
|
||||
if (ast == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AST parent = null;
|
||||
if (!stack.isEmpty()) {
|
||||
parent = (AST) stack.peek();
|
||||
}
|
||||
stack.push(ast);
|
||||
|
||||
AST child1 = ast.getFirstChild();
|
||||
AST child2 = null;
|
||||
AST child3 = null;
|
||||
if (child1 != null) {
|
||||
child2 = child1.getNextSibling();
|
||||
if (child2 != null) {
|
||||
child3 = child2.getNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
switch(ast.getType()) {
|
||||
// The top of the tree looks like this:
|
||||
// ROOT_ID "Whatever.java"
|
||||
// package
|
||||
// imports
|
||||
// class definition
|
||||
case ROOT_ID:
|
||||
dumpHiddenTokens(PdePreprocessor.filter.getInitialHiddenToken());
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
// supporting a "package" statement in a PDE program has
|
||||
// a bunch of issues with it that need to dealt in the compilation
|
||||
// code too, so this isn't actually tested.
|
||||
case PACKAGE_DEF:
|
||||
out.print("package");
|
||||
dumpHiddenAfter(ast);
|
||||
print(ast.getFirstChild());
|
||||
break;
|
||||
|
||||
// IMPORT has exactly one child
|
||||
case IMPORT:
|
||||
out.print("import");
|
||||
dumpHiddenAfter(ast);
|
||||
print(ast.getFirstChild());
|
||||
break;
|
||||
|
||||
case CLASS_DEF:
|
||||
case INTERFACE_DEF:
|
||||
print(getChild(ast, MODIFIERS));
|
||||
if (ast.getType() == CLASS_DEF) {
|
||||
out.print("class");
|
||||
} else {
|
||||
out.print("interface");
|
||||
}
|
||||
dumpHiddenBefore(getChild(ast, IDENT));
|
||||
print(getChild(ast, IDENT));
|
||||
print(getChild(ast, EXTENDS_CLAUSE));
|
||||
print(getChild(ast, IMPLEMENTS_CLAUSE));
|
||||
print(getChild(ast, OBJBLOCK));
|
||||
break;
|
||||
|
||||
case EXTENDS_CLAUSE:
|
||||
if (hasChildren(ast)) {
|
||||
out.print("extends");
|
||||
dumpHiddenBefore(getBestPrintableNode(ast, false));
|
||||
printChildren(ast);
|
||||
}
|
||||
break;
|
||||
|
||||
case IMPLEMENTS_CLAUSE:
|
||||
if (hasChildren(ast)) {
|
||||
out.print("implements");
|
||||
dumpHiddenBefore(getBestPrintableNode(ast, false));
|
||||
printChildren(ast);
|
||||
}
|
||||
break;
|
||||
|
||||
// DOT always has exactly two children.
|
||||
case DOT:
|
||||
print(child1);
|
||||
out.print(".");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child2);
|
||||
break;
|
||||
|
||||
case MODIFIERS:
|
||||
case OBJBLOCK:
|
||||
case CTOR_DEF:
|
||||
//case METHOD_DEF:
|
||||
case PARAMETERS:
|
||||
case PARAMETER_DEF:
|
||||
case VARIABLE_DEF:
|
||||
case TYPE:
|
||||
case SLIST:
|
||||
case ELIST:
|
||||
case ARRAY_DECLARATOR:
|
||||
case TYPECAST:
|
||||
case EXPR:
|
||||
case ARRAY_INIT:
|
||||
case FOR_INIT:
|
||||
case FOR_CONDITION:
|
||||
case FOR_ITERATOR:
|
||||
case METHOD_CALL:
|
||||
case INSTANCE_INIT:
|
||||
case INDEX_OP:
|
||||
case SUPER_CTOR_CALL:
|
||||
case CTOR_CALL:
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case METHOD_DEF:
|
||||
// kids seem to be: MODIFIERS TYPE setup PARAMETERS
|
||||
//AST parent = (AST) stack.peek();
|
||||
AST modifiersChild = ast.getFirstChild();
|
||||
AST typeChild = modifiersChild.getNextSibling();
|
||||
AST methodNameChild = typeChild.getNextSibling();
|
||||
AST parametersChild = methodNameChild.getNextSibling();
|
||||
|
||||
// to output, use print(child) on each of the four
|
||||
|
||||
/*
|
||||
// 1. figure out if this is setup, draw, or loop
|
||||
String methodName = methodNameChild.getText();
|
||||
if (publicMethods.get(methodName) != null) {
|
||||
// make sure this feller is public
|
||||
boolean foundPublic = false;
|
||||
AST child = modifiersChild.getFirstChild();
|
||||
while (child != null) {
|
||||
if (child.getText().equals("public")) {
|
||||
foundPublic = true;
|
||||
child = null;
|
||||
} else {
|
||||
//out.print("." + child.getText() + ".");
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
}
|
||||
if (!foundPublic) {
|
||||
out.print("public ");
|
||||
}
|
||||
*/
|
||||
|
||||
// if this method doesn't have a specifier, make it public
|
||||
// (useful for setup/keyPressed/etc)
|
||||
boolean foundSpecifier = false;
|
||||
AST child = modifiersChild.getFirstChild();
|
||||
while (child != null) {
|
||||
String childText = child.getText();
|
||||
if (childText.equals("public") ||
|
||||
childText.equals("protected") ||
|
||||
childText.equals("private")) {
|
||||
foundSpecifier = true;
|
||||
child = null;
|
||||
} else {
|
||||
//out.print("." + child.getText() + ".");
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
}
|
||||
if (!foundSpecifier) {
|
||||
out.print("public ");
|
||||
}
|
||||
printChildren(ast); // everything is fine
|
||||
break;
|
||||
|
||||
// if we have two children, it's of the form "a=0"
|
||||
// if just one child, it's of the form "=0" (where the
|
||||
// lhs is above this AST).
|
||||
case ASSIGN:
|
||||
if (child2 != null) {
|
||||
print(child1);
|
||||
out.print("=");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child2);
|
||||
}
|
||||
else {
|
||||
out.print("=");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
}
|
||||
break;
|
||||
|
||||
// binary operators:
|
||||
case PLUS:
|
||||
case MINUS:
|
||||
case DIV:
|
||||
case MOD:
|
||||
case NOT_EQUAL:
|
||||
case EQUAL:
|
||||
case LT:
|
||||
case GT:
|
||||
case LE:
|
||||
case GE:
|
||||
case LOR:
|
||||
case LAND:
|
||||
case BOR:
|
||||
case BXOR:
|
||||
case BAND:
|
||||
case SL:
|
||||
case SR:
|
||||
case BSR:
|
||||
case LITERAL_instanceof:
|
||||
case PLUS_ASSIGN:
|
||||
case MINUS_ASSIGN:
|
||||
case STAR_ASSIGN:
|
||||
case DIV_ASSIGN:
|
||||
case MOD_ASSIGN:
|
||||
case SR_ASSIGN:
|
||||
case BSR_ASSIGN:
|
||||
case SL_ASSIGN:
|
||||
case BAND_ASSIGN:
|
||||
case BXOR_ASSIGN:
|
||||
case BOR_ASSIGN:
|
||||
printBinaryOperator(ast);
|
||||
break;
|
||||
|
||||
|
||||
case LITERAL_for:
|
||||
out.print(name(ast));
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case POST_INC:
|
||||
case POST_DEC:
|
||||
print(child1);
|
||||
out.print(name(ast));
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
// unary operators:
|
||||
case BNOT:
|
||||
case LNOT:
|
||||
case INC:
|
||||
case DEC:
|
||||
case UNARY_MINUS:
|
||||
case UNARY_PLUS:
|
||||
out.print(name(ast));
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
break;
|
||||
|
||||
case LITERAL_new:
|
||||
out.print("new");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
print(child2);
|
||||
// "new String[] {...}": the stuff in {} is child3
|
||||
if (child3 != null) {
|
||||
print(child3);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_return:
|
||||
out.print("return");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
break;
|
||||
|
||||
case STATIC_INIT:
|
||||
out.print("static");
|
||||
dumpHiddenBefore(getBestPrintableNode(ast, false));
|
||||
print(child1);
|
||||
break;
|
||||
|
||||
case LITERAL_switch:
|
||||
out.print("switch");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case CASE_GROUP:
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_case:
|
||||
out.print("case");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_default:
|
||||
out.print("default");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case NUM_INT:
|
||||
case CHAR_LITERAL:
|
||||
case STRING_LITERAL:
|
||||
case NUM_FLOAT:
|
||||
out.print(ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_private:
|
||||
case LITERAL_public:
|
||||
case LITERAL_protected:
|
||||
case LITERAL_static:
|
||||
case LITERAL_transient:
|
||||
case LITERAL_native:
|
||||
case LITERAL_threadsafe:
|
||||
case LITERAL_synchronized:
|
||||
case LITERAL_volatile:
|
||||
case FINAL:
|
||||
case ABSTRACT:
|
||||
case LITERAL_package:
|
||||
case LITERAL_void:
|
||||
case LITERAL_boolean:
|
||||
case LITERAL_byte:
|
||||
case LITERAL_char:
|
||||
case LITERAL_short:
|
||||
case LITERAL_int:
|
||||
case LITERAL_float:
|
||||
case LITERAL_long:
|
||||
case LITERAL_double:
|
||||
case LITERAL_true:
|
||||
case LITERAL_false:
|
||||
case LITERAL_null:
|
||||
case SEMI:
|
||||
case LITERAL_this:
|
||||
case LITERAL_super:
|
||||
case LITERAL_continue:
|
||||
case LITERAL_break:
|
||||
out.print(name(ast));
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
case EMPTY_STAT:
|
||||
case EMPTY_FIELD:
|
||||
break;
|
||||
|
||||
// yuck: Distinguish between "import x.y.*" and "x = 1 * 3"
|
||||
case STAR:
|
||||
if (hasChildren(ast)) { // the binary mult. operator
|
||||
printBinaryOperator(ast);
|
||||
}
|
||||
else { // the special "*" in import:
|
||||
out.print("*");
|
||||
dumpHiddenAfter(ast);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_throws:
|
||||
out.print("throws");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_if:
|
||||
out.print("if");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1); // the "if" condition: an EXPR
|
||||
print(child2); // the "then" clause is an SLIST
|
||||
if (child3 != null) {
|
||||
out.print("else");
|
||||
dumpHiddenBefore(getBestPrintableNode(child3, true));
|
||||
print(child3); // optional "else" clause: an SLIST
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_while:
|
||||
out.print("while");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_do:
|
||||
out.print("do");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1); // an SLIST
|
||||
out.print("while");
|
||||
dumpHiddenBefore(getBestPrintableNode(child2, false));
|
||||
print(child2); // an EXPR
|
||||
break;
|
||||
|
||||
case LITERAL_try:
|
||||
out.print("try");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_catch:
|
||||
out.print("catch");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
// the first child is the "try" and the second is the SLIST
|
||||
case LITERAL_finally:
|
||||
out.print("finally");
|
||||
dumpHiddenAfter(ast);
|
||||
printChildren(ast);
|
||||
break;
|
||||
|
||||
case LITERAL_throw:
|
||||
out.print("throw");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child1);
|
||||
break;
|
||||
|
||||
// the dreaded trinary operator
|
||||
case QUESTION:
|
||||
print(child1);
|
||||
out.print("?");
|
||||
dumpHiddenAfter(ast);
|
||||
print(child2);
|
||||
print(child3);
|
||||
break;
|
||||
|
||||
// pde specific or modified tokens start here
|
||||
|
||||
// Image -> BImage, Font -> BFont as appropriate
|
||||
case IDENT:
|
||||
/*
|
||||
if (ast.getText().equals("Image") &&
|
||||
PdePreferences.getBoolean("preproc.substitute_image")) { //, true)) {
|
||||
out.print("BImage");
|
||||
} else if (ast.getText().equals("Font") &&
|
||||
PdePreferences.getBoolean("preproc.substitute_font")) { //, true)) {
|
||||
out.print("BFont");
|
||||
} else {
|
||||
*/
|
||||
out.print(ast.getText());
|
||||
//}
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
// the color datatype is just an alias for int
|
||||
case LITERAL_color:
|
||||
out.print("int");
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
case WEBCOLOR_LITERAL:
|
||||
if (ast.getText().length() != 6) {
|
||||
System.err.println("Internal error: incorrect length of webcolor " +
|
||||
"literal should have been detected sooner.");
|
||||
break;
|
||||
}
|
||||
out.print("0xff" + ast.getText());
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
// allow for stuff like int(43.2).
|
||||
case CONSTRUCTOR_CAST:
|
||||
|
||||
AST nonTerminalTypeNode = child1;
|
||||
AST terminalTypeNode = child1.getFirstChild();
|
||||
AST exprToCast = child2;
|
||||
|
||||
/*
|
||||
// if this is a string type, add .valueOf()
|
||||
if (nonTerminalTypeNode.getType() == PdeRecognizer.TYPE &&
|
||||
terminalTypeNode.getText().equals("String")) {
|
||||
|
||||
out.print(terminalTypeNode.getText() + ".valueOf");
|
||||
dumpHiddenAfter(terminalTypeNode);
|
||||
print(exprToCast);
|
||||
|
||||
// if the expresion to be cast is a string literal, try and parse it.
|
||||
//
|
||||
// ideally, we'd be able to do this for all expressions with a
|
||||
// string type, not just string literals. however, the parser
|
||||
// doesn't currently track expression type, and for full
|
||||
// functionality, we'd need to do semantic analysis to handle
|
||||
// imports so that we could know the return types of method calls.
|
||||
//
|
||||
} else if (exprToCast.getFirstChild().getType() == STRING_LITERAL ) {
|
||||
|
||||
switch (terminalTypeNode.getType()) {
|
||||
|
||||
case PdeRecognizer.LITERAL_byte:
|
||||
out.print("Byte.parseByte");
|
||||
dumpHiddenAfter(terminalTypeNode);
|
||||
print(exprToCast);
|
||||
break;
|
||||
|
||||
case PdeRecognizer.LITERAL_double:
|
||||
out.print("(new Double");
|
||||
dumpHiddenAfter(terminalTypeNode);
|
||||
out.print(exprToCast.getFirstChild().getText() + ").doubleValue()");
|
||||
dumpHiddenAfter(exprToCast.getFirstChild());
|
||||
break;
|
||||
|
||||
case PdeRecognizer.LITERAL_float:
|
||||
out.print("(new Float");
|
||||
dumpHiddenAfter(terminalTypeNode);
|
||||
out.print(exprToCast.getFirstChild().getText() + ").floatValue()");
|
||||
dumpHiddenAfter(exprToCast.getFirstChild());
|
||||
break;
|
||||
|
||||
case PdeRecognizer.LITERAL_int:
|
||||
case PdeRecognizer.LITERAL_color:
|
||||
out.print("Integer.parseInt");
|
||||
dumpHiddenAfter(terminalTypeNode);
|
||||
print(exprToCast);
|
||||
break;
|
||||
|
||||
case PdeRecognizer.LITERAL_long:
|
||||
out.print("Long.parseLong");
|
||||
break;
|
||||
|
||||
case PdeRecognizer.LITERAL_short:
|
||||
out.print("Short.parseShort");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new PdeException(PdeCompiler.SUPER_BADNESS);
|
||||
}
|
||||
|
||||
// for builtin types, use regular casting syntax
|
||||
} else {
|
||||
*/
|
||||
|
||||
// result of below is (int)(4.0
|
||||
//out.print("(");
|
||||
//out.print(terminalTypeNode.getText() + ")"); // typename
|
||||
//dumpHiddenAfter(terminalTypeNode);
|
||||
//print(exprToCast);
|
||||
//}
|
||||
|
||||
//out.print("(");
|
||||
String pooType = terminalTypeNode.getText();
|
||||
out.print("PApplet.to" +
|
||||
Character.toUpperCase(pooType.charAt(0)) +
|
||||
pooType.substring(1));
|
||||
dumpHiddenAfter(terminalTypeNode); // the left paren
|
||||
print(exprToCast);
|
||||
//out.print("x)");
|
||||
|
||||
break;
|
||||
|
||||
|
||||
// making floating point literals default to floats, not doubles
|
||||
case NUM_DOUBLE:
|
||||
out.print(ast.getText());
|
||||
if (PdePreferences.getBoolean("preproc.substitute_floats")) { //, true) ) {
|
||||
out.print("f");
|
||||
}
|
||||
dumpHiddenAfter(ast);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
debug.println("Invalid type:" + ast.getType());
|
||||
break;
|
||||
|
||||
|
||||
/* The following are tokens, but I don't think JavaRecognizer
|
||||
ever produces an AST with one of these types:
|
||||
case COMMA:
|
||||
case LITERAL_implements:
|
||||
case LITERAL_class:
|
||||
case LITERAL_extends:
|
||||
case EOF:
|
||||
case NULL_TREE_LOOKAHEAD:
|
||||
case BLOCK:
|
||||
case LABELED_STAT: // refuse to implement on moral grounds :)
|
||||
case LITERAL_import:
|
||||
case LBRACK:
|
||||
case RBRACK:
|
||||
case LCURLY:
|
||||
case RCURLY:
|
||||
case LPAREN:
|
||||
case RPAREN:
|
||||
case LITERAL_else: // else is a child of "if" AST
|
||||
case COLON: // part of the trinary operator
|
||||
case WS: // whitespace
|
||||
case ESC:
|
||||
case HEX_DIGIT:
|
||||
case VOCAB:
|
||||
|
||||
case EXPONENT: // exponents and float suffixes are left in the NUM_FLOAT
|
||||
case FLOAT_SUFFIX
|
||||
*/
|
||||
}
|
||||
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
PdePreprocessor - wrapper for default ANTLR-generated parser
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Except where noted, code is written by Ben Fry and
|
||||
Copyright (c) 2001-03 Massachusetts Institute of Technology
|
||||
Copyright (c) 2004-05 Ben Fry and Casey Reas
|
||||
Copyright (c) 2001-04 Massachusetts Institute of Technology
|
||||
|
||||
ANTLR-generated parser and several supporting classes written
|
||||
by Dan Mosedale via funding from the Interaction Institute IVREA.
|
||||
@@ -25,6 +25,9 @@
|
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
package processing.app.preproc;
|
||||
|
||||
import processing.app.*;
|
||||
import processing.core.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -48,17 +51,17 @@ public class PdePreprocessor {
|
||||
// might be at the end instead of .* whcih would make trouble
|
||||
// other classes using this can lop of the . and anything after
|
||||
// it to produce a package name consistently.
|
||||
String extraImports[];
|
||||
public String extraImports[];
|
||||
|
||||
// imports just from the code folder, treated differently
|
||||
// than the others, since the imports are auto-generated.
|
||||
String codeFolderImports[];
|
||||
public String codeFolderImports[];
|
||||
|
||||
static final int STATIC = 0; // formerly BEGINNER
|
||||
static final int ACTIVE = 1; // formerly INTERMEDIATE
|
||||
static final int JAVA = 2; // formerly ADVANCED
|
||||
static public final int STATIC = 0; // formerly BEGINNER
|
||||
static public final int ACTIVE = 1; // formerly INTERMEDIATE
|
||||
static public final int JAVA = 2; // formerly ADVANCED
|
||||
// static to make it easier for the antlr preproc to get at it
|
||||
static int programType = -1;
|
||||
static public int programType = -1;
|
||||
|
||||
Reader programReader;
|
||||
String buildPath;
|
||||
106
app/preproc/README.txt
Normal file
106
app/preproc/README.txt
Normal file
@@ -0,0 +1,106 @@
|
||||
The PDE Preprocessor is based on the Java Grammar that comes with
|
||||
ANTLR 2.7.2. Moving it forward to a new version of the grammar
|
||||
shouldn't be too difficult.
|
||||
|
||||
Here's some info about the various files in this directory:
|
||||
|
||||
java.g: this is the ANTLR grammar for Java 1.3/1.4 from the ANTLR
|
||||
distribution. It is in the public domain. The only change to this
|
||||
file from the original this file is the uncommenting of the clauses
|
||||
required to support assert().
|
||||
|
||||
java.tree.g: this describes the Abstract Syntax Tree (AST) generated
|
||||
by java.g. It is only here as a reference for coders hacking on the
|
||||
preprocessor, it is not built or used at all. Note that pde.g
|
||||
overrides some of the java.g rules so that in PDE ASTs, there are a
|
||||
few minor differences. Also in the public domain.
|
||||
|
||||
pde.g: this is the grammar and lexer for the PDE language itself. It
|
||||
subclasses the java.g grammar and lexer. There are a couple of
|
||||
overrides to java.g that I hope to convince the ANTLR folks to fold
|
||||
back into their grammar, but most of this file is highly specific to
|
||||
PDE itself.
|
||||
|
||||
PdeEmitter.java: this class traverses the AST generated by the PDE
|
||||
Recognizer, and emits it as Java code, doing any necessary
|
||||
transformations along the way. It is based on JavaEmitter.java,
|
||||
available from antlr.org, written by Andy Tripp <atripp@comcast.net>,
|
||||
who has given permission for it to be distributed under the GPL.
|
||||
|
||||
ExtendedCommonASTWithHiddenTokens.java: this adds a necessary
|
||||
initialize() method, as well as a number of methods to allow for XML
|
||||
serialization of the parse tree in a such a way that the hidden tokens
|
||||
are visible. Much of the code is taken from the original
|
||||
CommonASTWithHiddenTokens class. I hope to convince the ANTLR folks
|
||||
to fold these changes back into that class so that this file will be
|
||||
unnecessary.
|
||||
|
||||
TokenStreamCopyingHiddenTokenFilter.java: this class provides
|
||||
TokenStreamHiddenTokenFilters with the concept of tokens which can be
|
||||
copied so that they are seen by both the hidden token stream as well
|
||||
as the parser itself. This is useful when one wants to use an
|
||||
existing parser (like the Java parser included with ANTLR) that throws
|
||||
away some tokens to create a parse tree which can be used to spit out
|
||||
a copy of the code with only minor modifications. Partially derived
|
||||
from ANTLR code. I hope to convince the ANTLR folks to fold this
|
||||
functionality back into ANTLR proper as well.
|
||||
|
||||
whitespace_test.pde: a torture test to ensure that the preprocessor is
|
||||
correctly preserving whitespace, comments, and other hidden tokens
|
||||
correctly. See the comments in the code for details about how to run
|
||||
the test.
|
||||
|
||||
All other files in this directory are generated at build time by ANTLR
|
||||
itself. The ANTLR manual goes into a fair amount of detail about the
|
||||
what each type of file is for.
|
||||
|
||||
|
||||
....
|
||||
|
||||
|
||||
Current Preprocessor Subsitutions:
|
||||
|
||||
"compiler.substitute_floats" (currently "substitute_f")
|
||||
- treat doubles as floats, i.e. 12.3 becomes 12.3f so that people
|
||||
don't have to add f after their numbers all the time. this is
|
||||
confusing for beginners.
|
||||
|
||||
"compiler.enhanced_casting"
|
||||
- byte(), char(), int(), float() works for casting. this is basic in
|
||||
the current implementation, but should be expanded as described
|
||||
above. color() works similarly to int(), however there is also a
|
||||
*function* called color(r, g, b) in p5. will this cause trouble?
|
||||
|
||||
"compiler.color_datattype"
|
||||
- 'color' is aliased to 'int' as a datatype to represent ARGB packed
|
||||
into a single int, commonly used in p5 for pixels[] and other color
|
||||
operations. this is just a search/replace type thing, and it can be
|
||||
used interchangeably with int.
|
||||
|
||||
"compiler.web_colors" (currently "inline_web_colors")
|
||||
- color c = #cc0080; should unpack to 0xffcc0080 (the ff at the top is
|
||||
so that the color is opaque), which is just an int.
|
||||
|
||||
Other preprocessor functionality
|
||||
|
||||
- detects what 'mode' the program is in: static (no function brackets
|
||||
at all, just assumes everything is in draw), active (setup plus draw
|
||||
or loop), and java mode (full java support).
|
||||
http://proce55ing.net/reference/environment/index.html
|
||||
|
||||
- size and background are pulled from draw mode programs and placed
|
||||
into setup(). this has a problem if size() is based on a variable,
|
||||
which we try to avoid people doing, but would like to be able to
|
||||
support it (perhaps by requiring the size() to be final?)
|
||||
|
||||
- currently does a godawful scrambling of the comments so that the
|
||||
substitution doesn't try to run on them. this also causes lots of
|
||||
bizarro bugs.
|
||||
|
||||
Possible?
|
||||
|
||||
- would be nice to just type code wherever, mixing a 'static' style
|
||||
app with a few functions. would be simpler for starting out. but it
|
||||
seems that the declarations would have to be pulled out, but that
|
||||
all seems problematic. or maybe it could all be inside a static { }
|
||||
block. but that wouldn't seem to work either.
|
||||
221
app/preproc/TokenStreamCopyingHiddenTokenFilter.java
Normal file
221
app/preproc/TokenStreamCopyingHiddenTokenFilter.java
Normal file
@@ -0,0 +1,221 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
package antlr;
|
||||
//package processing.app.preproc;
|
||||
|
||||
|
||||
import antlr.*;
|
||||
import antlr.collections.impl.BitSet;
|
||||
|
||||
/**
|
||||
* This class provides TokenStreamHiddenTokenFilters with the concept of
|
||||
* tokens which can be copied so that they are seen by both the hidden token
|
||||
* stream as well as the parser itself. This is useful when one wants to use
|
||||
* an existing parser (like the Java parser included with ANTLR) that throws
|
||||
* away some tokens to create a parse tree which can be used to spit out
|
||||
* a copy of the code with only minor modifications.
|
||||
*
|
||||
* This code is partially derived from the public domain ANLTR TokenStream
|
||||
*/
|
||||
public class TokenStreamCopyingHiddenTokenFilter
|
||||
extends TokenStreamHiddenTokenFilter
|
||||
implements TokenStream {
|
||||
|
||||
protected BitSet copyMask;
|
||||
CommonHiddenStreamToken hiddenCopy = null;
|
||||
|
||||
public TokenStreamCopyingHiddenTokenFilter(TokenStream input) {
|
||||
super(input);
|
||||
copyMask = new BitSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that all tokens of type tokenType should be copied. The copy
|
||||
* is put in the stream of hidden tokens, and the original is returned in the
|
||||
* stream of normal tokens.
|
||||
*
|
||||
* @param tokenType integer representing the token type to copied
|
||||
*/
|
||||
public void copy(int tokenType) {
|
||||
copyMask.add(tokenType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of the important parts of the given token. Note that this
|
||||
* does NOT copy the hiddenBefore and hiddenAfter fields.
|
||||
*
|
||||
* @param t token to partially clone
|
||||
* @return newly created partial clone
|
||||
*/
|
||||
public CommonHiddenStreamToken partialCloneToken(CommonHiddenStreamToken t) {
|
||||
|
||||
CommonHiddenStreamToken u = new CommonHiddenStreamToken(t.getType(),
|
||||
t.getText());
|
||||
u.setColumn(t.getColumn());
|
||||
u.setLine(t.getLine());
|
||||
u.setFilename(t.getFilename());
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
public void linkAndCopyToken(CommonHiddenStreamToken prev,
|
||||
CommonHiddenStreamToken monitored) {
|
||||
// create a copy of the token in the lookahead for use as hidden token
|
||||
hiddenCopy = partialCloneToken(LA(1));
|
||||
|
||||
// attach copy to the previous token, whether hidden or monitored
|
||||
prev.setHiddenAfter(hiddenCopy);
|
||||
|
||||
// if previous token was hidden, set the hiddenBefore pointer of the
|
||||
// copy to point back to it
|
||||
if (prev != monitored) {
|
||||
hiddenCopy.setHiddenBefore(prev);
|
||||
}
|
||||
|
||||
// we don't want the non-hidden copy to link back to the hidden
|
||||
// copy on the next pass through this function, so we leave
|
||||
// lastHiddenToken alone
|
||||
|
||||
//System.err.println("hidden copy: " + hiddenCopy.toString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void consumeFirst() throws TokenStreamException {
|
||||
consume(); // get first token of input stream
|
||||
|
||||
// Handle situation where hidden or discarded tokens
|
||||
// appear first in input stream
|
||||
CommonHiddenStreamToken p=null;
|
||||
|
||||
// while hidden, copied, or discarded scarf tokens
|
||||
while ( hideMask.member(LA(1).getType()) ||
|
||||
discardMask.member(LA(1).getType()) ||
|
||||
copyMask.member(LA(1).getType()) ) {
|
||||
|
||||
// if we've hit one of the tokens that needs to be copied, we copy it
|
||||
// and then break out of the loop, because the parser needs to see it
|
||||
// too
|
||||
//
|
||||
if (copyMask.member(LA(1).getType())) {
|
||||
|
||||
// copy the token in the lookahead
|
||||
hiddenCopy = partialCloneToken(LA(1));
|
||||
|
||||
// if there's an existing token before this, link that and the
|
||||
// copy together
|
||||
if (p != null) {
|
||||
p.setHiddenAfter(hiddenCopy);
|
||||
hiddenCopy.setHiddenBefore(p); // double-link
|
||||
}
|
||||
|
||||
lastHiddenToken = hiddenCopy;
|
||||
if (firstHidden == null) {
|
||||
firstHidden = hiddenCopy;
|
||||
}
|
||||
|
||||
// we don't want to consume this token, because it also needs to
|
||||
// be passed through to the parser, so break out of the while look
|
||||
// entirely
|
||||
//
|
||||
break;
|
||||
} else if (hideMask.member(LA(1).getType())) {
|
||||
if (p != null) {
|
||||
p.setHiddenAfter(LA(1));
|
||||
LA(1).setHiddenBefore(p); // double-link
|
||||
}
|
||||
p = LA(1);
|
||||
|
||||
lastHiddenToken = p;
|
||||
if (firstHidden == null) {
|
||||
firstHidden = p; // record hidden token if first
|
||||
}
|
||||
}
|
||||
consume();
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the next monitored token.
|
||||
* Test the token following the monitored token.
|
||||
* If following is another monitored token, save it
|
||||
* for the next invocation of nextToken (like a single
|
||||
* lookahead token) and return it then.
|
||||
* If following is unmonitored, nondiscarded (hidden)
|
||||
* channel token, add it to the monitored token.
|
||||
*
|
||||
* Note: EOF must be a monitored Token.
|
||||
*/
|
||||
public Token nextToken() throws TokenStreamException {
|
||||
// handle an initial condition; don't want to get lookahead
|
||||
// token of this splitter until first call to nextToken
|
||||
if (LA(1) == null) {
|
||||
consumeFirst();
|
||||
}
|
||||
|
||||
//System.err.println();
|
||||
|
||||
// we always consume hidden tokens after monitored, thus,
|
||||
// upon entry LA(1) is a monitored token.
|
||||
CommonHiddenStreamToken monitored = LA(1);
|
||||
|
||||
// point to hidden tokens found during last invocation
|
||||
monitored.setHiddenBefore(lastHiddenToken);
|
||||
lastHiddenToken = null;
|
||||
|
||||
// Look for hidden tokens, hook them into list emanating
|
||||
// from the monitored tokens.
|
||||
consume();
|
||||
CommonHiddenStreamToken prev = monitored;
|
||||
|
||||
// deal with as many not-purely-monitored tokens as possible
|
||||
while ( hideMask.member(LA(1).getType()) ||
|
||||
discardMask.member(LA(1).getType()) ||
|
||||
copyMask.member(LA(1).getType()) ) {
|
||||
|
||||
if (copyMask.member(LA(1).getType())) {
|
||||
|
||||
// copy the token and link it backwards
|
||||
if (hiddenCopy != null) {
|
||||
linkAndCopyToken(hiddenCopy, monitored);
|
||||
} else {
|
||||
linkAndCopyToken(prev, monitored);
|
||||
}
|
||||
|
||||
// we now need to parse it as a monitored token, so we return, which
|
||||
// avoids the consume() call at the end of this loop. the next call
|
||||
// will parse it as a monitored token.
|
||||
//System.err.println("returned: " + monitored.toString());
|
||||
return monitored;
|
||||
|
||||
} else if (hideMask.member(LA(1).getType())) {
|
||||
|
||||
// attach the hidden token to the monitored in a chain
|
||||
// link forwards
|
||||
prev.setHiddenAfter(LA(1));
|
||||
|
||||
// link backwards
|
||||
if (prev != monitored) { //hidden cannot point to monitored tokens
|
||||
LA(1).setHiddenBefore(prev);
|
||||
} else if (hiddenCopy != null) {
|
||||
hiddenCopy.setHiddenAfter(LA(1));
|
||||
LA(1).setHiddenBefore(hiddenCopy);
|
||||
hiddenCopy = null;
|
||||
}
|
||||
|
||||
//System.err.println("hidden: " + prev.getHiddenAfter().toString() + "\" after: " + prev.toString());
|
||||
prev = lastHiddenToken = LA(1);
|
||||
}
|
||||
|
||||
consume();
|
||||
}
|
||||
|
||||
// remember the last hidden token for next time around
|
||||
if (hiddenCopy != null) {
|
||||
lastHiddenToken = hiddenCopy;
|
||||
hiddenCopy = null;
|
||||
}
|
||||
|
||||
//System.err.println("returned: " + monitored.toString());
|
||||
return monitored;
|
||||
}
|
||||
}
|
||||
11
app/preproc/clean.sh
Executable file
11
app/preproc/clean.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -f *Lexer.java
|
||||
rm -f *Recognizer.java
|
||||
rm -f *TokenTypes.java
|
||||
rm -f *TokenTypes.txt
|
||||
rm -f *TreeParser.java
|
||||
rm -f *TreeParserTokenTypes.java
|
||||
rm -f *TreeParserTokenTypes.txt
|
||||
rm -f expanded*.g
|
||||
|
||||
1276
app/preproc/java.g
Normal file
1276
app/preproc/java.g
Normal file
File diff suppressed because it is too large
Load Diff
326
app/preproc/java.tree.g
Normal file
326
app/preproc/java.tree.g
Normal file
@@ -0,0 +1,326 @@
|
||||
package antlr;
|
||||
|
||||
|
||||
/** Java 1.3 AST Recognizer Grammar
|
||||
*
|
||||
* Author: (see java.g preamble)
|
||||
*
|
||||
* This grammar is in the PUBLIC DOMAIN
|
||||
*/
|
||||
class JavaTreeParser extends TreeParser;
|
||||
|
||||
options {
|
||||
importVocab = Java;
|
||||
}
|
||||
|
||||
compilationUnit
|
||||
: (packageDefinition)?
|
||||
(importDefinition)*
|
||||
(typeDefinition)*
|
||||
;
|
||||
|
||||
packageDefinition
|
||||
: #( PACKAGE_DEF identifier )
|
||||
;
|
||||
|
||||
importDefinition
|
||||
: #( IMPORT identifierStar )
|
||||
;
|
||||
|
||||
typeDefinition
|
||||
: #(CLASS_DEF modifiers IDENT extendsClause implementsClause objBlock )
|
||||
| #(INTERFACE_DEF modifiers IDENT extendsClause interfaceBlock )
|
||||
;
|
||||
|
||||
typeSpec
|
||||
: #(TYPE typeSpecArray)
|
||||
;
|
||||
|
||||
typeSpecArray
|
||||
: #( ARRAY_DECLARATOR typeSpecArray )
|
||||
| type
|
||||
;
|
||||
|
||||
type: identifier
|
||||
| builtInType
|
||||
;
|
||||
|
||||
builtInType
|
||||
: "void"
|
||||
| "boolean"
|
||||
| "byte"
|
||||
| "char"
|
||||
| "short"
|
||||
| "int"
|
||||
| "float"
|
||||
| "long"
|
||||
| "double"
|
||||
;
|
||||
|
||||
modifiers
|
||||
: #( MODIFIERS (modifier)* )
|
||||
;
|
||||
|
||||
modifier
|
||||
: "private"
|
||||
| "public"
|
||||
| "protected"
|
||||
| "static"
|
||||
| "transient"
|
||||
| "final"
|
||||
| "abstract"
|
||||
| "native"
|
||||
| "threadsafe"
|
||||
| "synchronized"
|
||||
| "const"
|
||||
| "volatile"
|
||||
| "strictfp"
|
||||
;
|
||||
|
||||
extendsClause
|
||||
: #(EXTENDS_CLAUSE (identifier)* )
|
||||
;
|
||||
|
||||
implementsClause
|
||||
: #(IMPLEMENTS_CLAUSE (identifier)* )
|
||||
;
|
||||
|
||||
|
||||
interfaceBlock
|
||||
: #( OBJBLOCK
|
||||
( methodDecl
|
||||
| variableDef
|
||||
)*
|
||||
)
|
||||
;
|
||||
|
||||
objBlock
|
||||
: #( OBJBLOCK
|
||||
( ctorDef
|
||||
| methodDef
|
||||
| variableDef
|
||||
| typeDefinition
|
||||
| #(STATIC_INIT slist)
|
||||
| #(INSTANCE_INIT slist)
|
||||
)*
|
||||
)
|
||||
;
|
||||
|
||||
ctorDef
|
||||
: #(CTOR_DEF modifiers methodHead (slist)?)
|
||||
;
|
||||
|
||||
methodDecl
|
||||
: #(METHOD_DEF modifiers typeSpec methodHead)
|
||||
;
|
||||
|
||||
methodDef
|
||||
: #(METHOD_DEF modifiers typeSpec methodHead (slist)?)
|
||||
;
|
||||
|
||||
variableDef
|
||||
: #(VARIABLE_DEF modifiers typeSpec variableDeclarator varInitializer)
|
||||
;
|
||||
|
||||
parameterDef
|
||||
: #(PARAMETER_DEF modifiers typeSpec IDENT )
|
||||
;
|
||||
|
||||
objectinitializer
|
||||
: #(INSTANCE_INIT slist)
|
||||
;
|
||||
|
||||
variableDeclarator
|
||||
: IDENT
|
||||
| LBRACK variableDeclarator
|
||||
;
|
||||
|
||||
varInitializer
|
||||
: #(ASSIGN initializer)
|
||||
|
|
||||
;
|
||||
|
||||
initializer
|
||||
: expression
|
||||
| arrayInitializer
|
||||
;
|
||||
|
||||
arrayInitializer
|
||||
: #(ARRAY_INIT (initializer)*)
|
||||
;
|
||||
|
||||
methodHead
|
||||
: IDENT #( PARAMETERS (parameterDef)* ) (throwsClause)?
|
||||
;
|
||||
|
||||
throwsClause
|
||||
: #( "throws" (identifier)* )
|
||||
;
|
||||
|
||||
identifier
|
||||
: IDENT
|
||||
| #( DOT identifier IDENT )
|
||||
;
|
||||
|
||||
identifierStar
|
||||
: IDENT
|
||||
| #( DOT identifier (STAR|IDENT) )
|
||||
;
|
||||
|
||||
slist
|
||||
: #( SLIST (stat)* )
|
||||
;
|
||||
|
||||
stat: typeDefinition
|
||||
| variableDef
|
||||
| expression
|
||||
| #(LABELED_STAT IDENT stat)
|
||||
| #("if" expression stat (stat)? )
|
||||
| #( "for"
|
||||
#(FOR_INIT (variableDef | elist)?)
|
||||
#(FOR_CONDITION (expression)?)
|
||||
#(FOR_ITERATOR (elist)?)
|
||||
stat
|
||||
)
|
||||
| #("while" expression stat)
|
||||
| #("do" stat expression)
|
||||
| #("break" (IDENT)? )
|
||||
| #("continue" (IDENT)? )
|
||||
| #("return" (expression)? )
|
||||
| #("switch" expression (caseGroup)*)
|
||||
| #("throw" expression)
|
||||
| #("synchronized" expression stat)
|
||||
| tryBlock
|
||||
| slist // nested SLIST
|
||||
// uncomment to make assert JDK 1.4 stuff work
|
||||
| #("assert" expression (expression)?)
|
||||
| EMPTY_STAT
|
||||
;
|
||||
|
||||
caseGroup
|
||||
: #(CASE_GROUP (#("case" expression) | "default")+ slist)
|
||||
;
|
||||
|
||||
tryBlock
|
||||
: #( "try" slist (handler)* (#("finally" slist))? )
|
||||
;
|
||||
|
||||
handler
|
||||
: #( "catch" parameterDef slist )
|
||||
;
|
||||
|
||||
elist
|
||||
: #( ELIST (expression)* )
|
||||
;
|
||||
|
||||
expression
|
||||
: #(EXPR expr)
|
||||
;
|
||||
|
||||
expr: #(QUESTION expr expr expr) // trinary operator
|
||||
| #(ASSIGN expr expr) // binary operators...
|
||||
| #(PLUS_ASSIGN expr expr)
|
||||
| #(MINUS_ASSIGN expr expr)
|
||||
| #(STAR_ASSIGN expr expr)
|
||||
| #(DIV_ASSIGN expr expr)
|
||||
| #(MOD_ASSIGN expr expr)
|
||||
| #(SR_ASSIGN expr expr)
|
||||
| #(BSR_ASSIGN expr expr)
|
||||
| #(SL_ASSIGN expr expr)
|
||||
| #(BAND_ASSIGN expr expr)
|
||||
| #(BXOR_ASSIGN expr expr)
|
||||
| #(BOR_ASSIGN expr expr)
|
||||
| #(LOR expr expr)
|
||||
| #(LAND expr expr)
|
||||
| #(BOR expr expr)
|
||||
| #(BXOR expr expr)
|
||||
| #(BAND expr expr)
|
||||
| #(NOT_EQUAL expr expr)
|
||||
| #(EQUAL expr expr)
|
||||
| #(LT expr expr)
|
||||
| #(GT expr expr)
|
||||
| #(LE expr expr)
|
||||
| #(GE expr expr)
|
||||
| #(SL expr expr)
|
||||
| #(SR expr expr)
|
||||
| #(BSR expr expr)
|
||||
| #(PLUS expr expr)
|
||||
| #(MINUS expr expr)
|
||||
| #(DIV expr expr)
|
||||
| #(MOD expr expr)
|
||||
| #(STAR expr expr)
|
||||
| #(INC expr)
|
||||
| #(DEC expr)
|
||||
| #(POST_INC expr)
|
||||
| #(POST_DEC expr)
|
||||
| #(BNOT expr)
|
||||
| #(LNOT expr)
|
||||
| #("instanceof" expr expr)
|
||||
| #(UNARY_MINUS expr)
|
||||
| #(UNARY_PLUS expr)
|
||||
| primaryExpression
|
||||
;
|
||||
|
||||
primaryExpression
|
||||
: IDENT
|
||||
| #( DOT
|
||||
( expr
|
||||
( IDENT
|
||||
| arrayIndex
|
||||
| "this"
|
||||
| "class"
|
||||
| #( "new" IDENT elist )
|
||||
| "super"
|
||||
)
|
||||
| #(ARRAY_DECLARATOR typeSpecArray)
|
||||
| builtInType ("class")?
|
||||
)
|
||||
)
|
||||
| arrayIndex
|
||||
| #(METHOD_CALL primaryExpression elist)
|
||||
| ctorCall
|
||||
| #(TYPECAST typeSpec expr)
|
||||
| newExpression
|
||||
| constant
|
||||
| "super"
|
||||
| "true"
|
||||
| "false"
|
||||
| "this"
|
||||
| "null"
|
||||
| typeSpec // type name used with instanceof
|
||||
;
|
||||
|
||||
ctorCall
|
||||
: #( CTOR_CALL elist )
|
||||
| #( SUPER_CTOR_CALL
|
||||
( elist
|
||||
| primaryExpression elist
|
||||
)
|
||||
)
|
||||
;
|
||||
|
||||
arrayIndex
|
||||
: #(INDEX_OP expr expression)
|
||||
;
|
||||
|
||||
constant
|
||||
: NUM_INT
|
||||
| CHAR_LITERAL
|
||||
| STRING_LITERAL
|
||||
| NUM_FLOAT
|
||||
| NUM_DOUBLE
|
||||
| NUM_LONG
|
||||
;
|
||||
|
||||
newExpression
|
||||
: #( "new" type
|
||||
( newArrayDeclarator (arrayInitializer)?
|
||||
| elist (objBlock)?
|
||||
)
|
||||
)
|
||||
|
||||
;
|
||||
|
||||
newArrayDeclarator
|
||||
: #( ARRAY_DECLARATOR (newArrayDeclarator)? (expression)? )
|
||||
;
|
||||
3
app/preproc/make.sh
Executable file
3
app/preproc/make.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
java -cp ../../build/macosx/work/lib/antlr.jar antlr.Tool java.g
|
||||
# now build the pde stuff that extends the java classes
|
||||
java -cp ../../build/macosx/work/lib/antlr.jar antlr.Tool -glib java.g pde.g
|
||||
304
app/preproc/pde.g
Normal file
304
app/preproc/pde.g
Normal file
@@ -0,0 +1,304 @@
|
||||
/* -*- mode: antlr; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
header {
|
||||
package processing.app.preproc;
|
||||
|
||||
import processing.app.*;
|
||||
}
|
||||
|
||||
class PdeRecognizer extends JavaRecognizer;
|
||||
|
||||
options {
|
||||
importVocab = Java;
|
||||
exportVocab = PdePartial;
|
||||
|
||||
codeGenMakeSwitchThreshold=10; // this is set high for debugging
|
||||
codeGenBitsetTestThreshold=10; // this is set high for debugging
|
||||
|
||||
// developers may to want to set this to true for better
|
||||
// debugging messages, however, doing so disables highlighting errors
|
||||
// in the editor.
|
||||
defaultErrorHandler = false; //true;
|
||||
}
|
||||
|
||||
tokens {
|
||||
CONSTRUCTOR_CAST; EMPTY_FIELD;
|
||||
}
|
||||
|
||||
pdeProgram
|
||||
// only java mode programs will have their own public classes or
|
||||
// imports (and they must have at least one)
|
||||
: ( "public" "class" | "import" ) => javaProgram
|
||||
{ PdePreprocessor.programType = PdePreprocessor.JAVA; }
|
||||
|
||||
// the syntactic predicate here looks for any minimal (thus
|
||||
// the non-greedy qualifier) number of fields, followed by
|
||||
// the tokens that represent the definition of loop() or
|
||||
// some other member function. java mode programs may have such
|
||||
// definitions, but they won't reach this point, having already been
|
||||
// selected in the previous alternative. static mode programs
|
||||
// don't have member functions.
|
||||
//
|
||||
| ( ( options {greedy=false;}: possiblyEmptyField)* "void" IDENT LPAREN )
|
||||
=> activeProgram
|
||||
{ PdePreprocessor.programType = PdePreprocessor.ACTIVE; }
|
||||
|
||||
| staticProgram
|
||||
{ PdePreprocessor.programType = PdePreprocessor.STATIC; }
|
||||
;
|
||||
|
||||
// advanced mode is really just a normal java file
|
||||
javaProgram
|
||||
: compilationUnit
|
||||
;
|
||||
|
||||
activeProgram
|
||||
: (possiblyEmptyField)+
|
||||
;
|
||||
|
||||
staticProgram
|
||||
: (statement)*
|
||||
;
|
||||
|
||||
// copy of the java.g rule with WEBCOLOR_LITERAL added
|
||||
constant
|
||||
: NUM_INT
|
||||
| CHAR_LITERAL
|
||||
| STRING_LITERAL
|
||||
| NUM_FLOAT
|
||||
| NUM_LONG
|
||||
| NUM_DOUBLE
|
||||
| webcolor_literal
|
||||
;
|
||||
|
||||
// of the form #cc008f in PDE
|
||||
webcolor_literal
|
||||
: w:WEBCOLOR_LITERAL
|
||||
{ PdePreferences.getBoolean("preproc.web_colors") &&
|
||||
w.getText().length() == 6 }? // must be exactly 6 hex digits
|
||||
;
|
||||
|
||||
// copy of the java.g builtInType rule
|
||||
builtInConsCastType
|
||||
: "void"
|
||||
| "boolean"
|
||||
| "byte"
|
||||
| "char"
|
||||
| "short"
|
||||
| "int"
|
||||
| "float"
|
||||
| "long"
|
||||
| "double"
|
||||
;
|
||||
|
||||
// our types include the java types and "color". this is separated into two
|
||||
// rules so that constructor casts can just use the original typelist, since
|
||||
// we don't want to support the color type as a constructor cast.
|
||||
//
|
||||
builtInType
|
||||
: builtInConsCastType
|
||||
| "color" // aliased to an int in PDE
|
||||
{ PdePreferences.getBoolean("preproc.color_datatype") }?
|
||||
;
|
||||
|
||||
// constructor style casts.
|
||||
constructorCast!
|
||||
: t:consCastTypeSpec[true]
|
||||
LPAREN!
|
||||
e:expression
|
||||
RPAREN!
|
||||
// if this is a string literal, make sure the type we're trying to cast
|
||||
// to is one of the supported ones
|
||||
//
|
||||
{ #e.getType() != STRING_LITERAL ||
|
||||
( #t.getType() == LITERAL_byte ||
|
||||
#t.getType() == LITERAL_double ||
|
||||
#t.getType() == LITERAL_float ||
|
||||
#t.getType() == LITERAL_int ||
|
||||
#t.getType() == LITERAL_long ||
|
||||
#t.getType() == LITERAL_short ) }?
|
||||
// create the node
|
||||
//
|
||||
{#constructorCast = #(#[CONSTRUCTOR_CAST,"CONSTRUCTOR_CAST"], t, e);}
|
||||
;
|
||||
|
||||
// A list of types that be used as the destination type in a constructor-style
|
||||
// cast. Ideally, this would include all class types, not just "String".
|
||||
// Unfortunately, it's not possible to tell whether Foo(5) is supposed to be
|
||||
// a method call or a constructor cast without have a table of all valid
|
||||
// types or methods, which requires semantic analysis (eg processing of import
|
||||
// statements). So we accept the set of built-in types plus "String".
|
||||
//
|
||||
consCastTypeSpec[boolean addImagNode]
|
||||
// : stringTypeSpec[addImagNode]
|
||||
// | builtInConsCastTypeSpec[addImagNode]
|
||||
: builtInConsCastTypeSpec[addImagNode]
|
||||
// trying to remove String() cast [fry]
|
||||
;
|
||||
|
||||
//stringTypeSpec[boolean addImagNode]
|
||||
// : id:IDENT { #id.getText().equals("String") }?
|
||||
// {
|
||||
// if ( addImagNode ) {
|
||||
// #stringTypeSpec = #(#[TYPE,"TYPE"],
|
||||
// #stringTypeSpec);
|
||||
// }
|
||||
// }
|
||||
// ;
|
||||
|
||||
builtInConsCastTypeSpec[boolean addImagNode]
|
||||
: builtInConsCastType
|
||||
{
|
||||
if ( addImagNode ) {
|
||||
#builtInConsCastTypeSpec = #(#[TYPE,"TYPE"],
|
||||
#builtInConsCastTypeSpec);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
// Since "color" tokens are lexed as LITERAL_color now, we need to have a rule
|
||||
// that can generate a method call from an expression that starts with this
|
||||
// token
|
||||
//
|
||||
colorMethodCall
|
||||
: c:"color" {#c.setType(IDENT);} // this would default to LITERAL_color
|
||||
lp:LPAREN^ {#lp.setType(METHOD_CALL);}
|
||||
argList
|
||||
RPAREN!
|
||||
;
|
||||
|
||||
// copy of the java.g rule with added constructorCast and colorMethodCall
|
||||
// alternatives
|
||||
primaryExpression
|
||||
: (consCastTypeSpec[false] LPAREN) => constructorCast
|
||||
{ PdePreferences.getBoolean("preproc.enhanced_casting") }?
|
||||
| identPrimary ( options {greedy=true;} : DOT^ "class" )?
|
||||
| constant
|
||||
| "true"
|
||||
| "false"
|
||||
| "null"
|
||||
| newExpression
|
||||
| "this"
|
||||
| "super"
|
||||
| LPAREN! assignmentExpression RPAREN!
|
||||
| colorMethodCall
|
||||
// look for int.class and int[].class
|
||||
| builtInType
|
||||
( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )*
|
||||
DOT^ "class"
|
||||
;
|
||||
|
||||
// the below variable rule hacks are needed so that it's possible for the
|
||||
// emitter to correctly output variable declarations of the form "float a, b"
|
||||
// from the AST. This means that our AST has a somewhat different form in
|
||||
// these rules than the java one does, and this new form may have its own
|
||||
// semantic issues. But it seems to fix the comma declaration issues.
|
||||
//
|
||||
variableDefinitions![AST mods, AST t]
|
||||
: vd:variableDeclarator[getASTFactory().dupTree(mods),
|
||||
getASTFactory().dupTree(t)]
|
||||
{#variableDefinitions = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods,
|
||||
t, vd);}
|
||||
;
|
||||
variableDeclarator[AST mods, AST t]
|
||||
: ( id:IDENT (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
|
||||
v:varInitializer (COMMA!)? )+
|
||||
;
|
||||
|
||||
// java.g builds syntax trees with an inconsistent structure. override one of
|
||||
// the rules there to fix this.
|
||||
//
|
||||
explicitConstructorInvocation!
|
||||
: t:"this" LPAREN a1:argList RPAREN SEMI
|
||||
{#explicitConstructorInvocation = #(#[CTOR_CALL, "CTOR_CALL"],
|
||||
#t, #a1);}
|
||||
| s:"super" LPAREN a2:argList RPAREN SEMI
|
||||
{#explicitConstructorInvocation = #(#[SUPER_CTOR_CALL,
|
||||
"SUPER_CTOR_CALL"],
|
||||
#s, #a2);}
|
||||
;
|
||||
|
||||
// quick-n-dirty hack to the get the advanced class name. we should
|
||||
// really be getting it from the AST and not forking this rule from
|
||||
// the java.g copy at all. Since this is a recursive descent parser, we get
|
||||
// the last class name in the file so that we don't end up with the classname
|
||||
// of an inner class. If there is more than one "outer" class in a file,
|
||||
// this heuristic will fail.
|
||||
//
|
||||
classDefinition![AST modifiers]
|
||||
: "class" i:IDENT
|
||||
// it _might_ have a superclass...
|
||||
sc:superClassClause
|
||||
// it might implement some interfaces...
|
||||
ic:implementsClause
|
||||
// now parse the body of the class
|
||||
cb:classBlock
|
||||
{#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"],
|
||||
modifiers,i,sc,ic,cb);
|
||||
PdePreprocessor.advClassName = i.getText();}
|
||||
;
|
||||
|
||||
possiblyEmptyField
|
||||
: field
|
||||
| s:SEMI {#s.setType(EMPTY_FIELD);}
|
||||
;
|
||||
|
||||
class PdeLexer extends JavaLexer;
|
||||
|
||||
options {
|
||||
importVocab=PdePartial;
|
||||
exportVocab=Pde;
|
||||
}
|
||||
|
||||
// We need to preserve whitespace and commentary instead of ignoring
|
||||
// like the supergrammar does. Otherwise Jikes won't be able to give
|
||||
// us error messages that point to the equivalent PDE code.
|
||||
|
||||
// WS, SL_COMMENT, ML_COMMENT are copies of the original productions,
|
||||
// but with the SKIP assigment removed.
|
||||
|
||||
WS : ( ' '
|
||||
| '\t'
|
||||
| '\f'
|
||||
// handle newlines
|
||||
| ( options {generateAmbigWarnings=false;}
|
||||
: "\r\n" // Evil DOS
|
||||
| '\r' // Macintosh
|
||||
| '\n' // Unix (the right way)
|
||||
)
|
||||
{ newline(); }
|
||||
)+
|
||||
;
|
||||
|
||||
// Single-line comments
|
||||
SL_COMMENT
|
||||
: "//"
|
||||
(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
|
||||
{newline();}
|
||||
;
|
||||
|
||||
// multiple-line comments
|
||||
ML_COMMENT
|
||||
: "/*"
|
||||
( /* '\r' '\n' can be matched in one alternative or by matching
|
||||
'\r' in one iteration and '\n' in another. I am trying to
|
||||
handle any flavor of newline that comes in, but the language
|
||||
that allows both "\r\n" and "\r" and "\n" to all be valid
|
||||
newline is ambiguous. Consequently, the resulting grammar
|
||||
must be ambiguous. I'm shutting this warning off.
|
||||
*/
|
||||
options {
|
||||
generateAmbigWarnings=false;
|
||||
}
|
||||
:
|
||||
{ LA(2)!='/' }? '*'
|
||||
| '\r' '\n' {newline();}
|
||||
| '\r' {newline();}
|
||||
| '\n' {newline();}
|
||||
| ~('*'|'\n'|'\r')
|
||||
)*
|
||||
"*/"
|
||||
;
|
||||
|
||||
WEBCOLOR_LITERAL
|
||||
: '#'! (HEX_DIGIT)+
|
||||
;
|
||||
150
app/preproc/whitespace_test.pde
Normal file
150
app/preproc/whitespace_test.pde
Normal file
@@ -0,0 +1,150 @@
|
||||
// this is a whitespace and other invisible token torture test for the ANTLR-based
|
||||
// preprocessor. edit pde.properties and set "editor.save_build_files" to true.
|
||||
// then, build this in processing. next, use
|
||||
//
|
||||
// diff -u --strip-trailing-cr \
|
||||
// work/sketchbook/default/whitespace_test/whitespace_test.pde \
|
||||
// work/lib/build/MyDemo.java
|
||||
//
|
||||
// to compare the files before and after preprocessing. There should not be
|
||||
// any differences.
|
||||
|
||||
import // comment test
|
||||
java.io.*;
|
||||
|
||||
// comment 1
|
||||
public class // post-class comment
|
||||
|
||||
// comment2
|
||||
|
||||
MyDemo extends BApplet implements // foo
|
||||
java.lang. // bar
|
||||
Cloneable {
|
||||
|
||||
//argh
|
||||
|
||||
public // foo
|
||||
String // bar
|
||||
fff = /*rheet */ "stuff";
|
||||
|
||||
static /*a*/ {
|
||||
/*foo*/
|
||||
/*bar*/
|
||||
six = 6;
|
||||
} /* b*/
|
||||
|
||||
static /*a*/ final /*b*/ int six;
|
||||
|
||||
void setup()
|
||||
{
|
||||
size(200, 200);
|
||||
background(255);
|
||||
|
||||
this . fff = /* ook */ (String)/*foo*/"yo";
|
||||
rectMode(CENTER_DIAMETER); // comment 1a
|
||||
noStroke();
|
||||
fill(255, 204, 0);
|
||||
|
||||
int q = /*a*/ - /*b*/ 1;
|
||||
|
||||
boolean c = /*a*/ ! /*b*/ true;
|
||||
}
|
||||
|
||||
int foo() /*a*/ throws /*b*/ java.lang.Exception /*c*/
|
||||
{
|
||||
int b = 7;
|
||||
switch /*a*/ ( /*b*/ b /*c*/ ) {
|
||||
case /*d*/ 1 /*e*/: /*f*/
|
||||
int c=9;
|
||||
/*g*/
|
||||
break; /*h*/
|
||||
default /*i*/ :
|
||||
int d=9;
|
||||
break;
|
||||
}
|
||||
|
||||
try { /* qq */
|
||||
loop(); /* rr */
|
||||
} catch /*ss*/ (java.lang.Exception ex) /*tt*/ {
|
||||
b = 8; /*tut*/
|
||||
throw /*utu*/ ex;
|
||||
} /*uu*/ finally /*vv*/ {
|
||||
b = 9;
|
||||
} /*ww*/
|
||||
|
||||
b /*aaa*/ = /*bbb*/ true /*ccc*/ ? /*ddd*/ 0 /*eee*/
|
||||
: /* fff*/ 1 /*ggg*/;
|
||||
return /*a*/ 5 /*b*/;
|
||||
}
|
||||
|
||||
// comment 2
|
||||
void loop()
|
||||
{
|
||||
|
||||
int arr1 /* VVV */ [ /* XXX */] /*YYY*/ ;
|
||||
int[] arr2 = { /*a*/ 2, 3 /*b*/ } /*c*/ ;
|
||||
|
||||
for /*a*/ (/*b*/ int j=0 /*c*/; /*d*/ j<2/*e*/ ; /*f*/ j++ /*g*/)
|
||||
/*h*/
|
||||
arr2[1] = 6;
|
||||
|
||||
/*foo*/
|
||||
;
|
||||
/*bar*/
|
||||
rect(width-mouseX, height-mouseY, 50, 50);
|
||||
rect(mouseX, mouseY, 50, 50);
|
||||
|
||||
if (/*a*/ arr2[1] == 6/*b*/) {
|
||||
/*c*/
|
||||
int d=7;
|
||||
} /*d*/else /*e*/{
|
||||
int e=8;
|
||||
/*f*/
|
||||
}
|
||||
|
||||
int f;
|
||||
if (/*aa*/ arr2[1] ==6 /*bb*/ )
|
||||
/*cc*/
|
||||
f=8; /*dd*/
|
||||
else /*ee*/
|
||||
f=10; /*ff*/
|
||||
|
||||
while ( /*aaa*/ f < 15) /*bbb*/ {
|
||||
f ++;
|
||||
} /*ggg*/
|
||||
|
||||
do /* aaaa */ {
|
||||
f++;
|
||||
} /*bbbb*/ while /*cccc*/ ( /*a*/ - /*b*/ 20 > f) /*dddd*/;
|
||||
|
||||
f = 2 * 3 + 4;
|
||||
|
||||
f = ( 2 * 3 ) + 4 + /*aa*/ -/*bb*/1;
|
||||
|
||||
f = 2 * ( 3 + 4 ) ;
|
||||
|
||||
fff = /*a*/ new /*b*/ String(/*c*/"foo"/*d*/) /*e*/;
|
||||
|
||||
int arr3[] = /*a*/ new /*b*/ int/*c*/[/*d*/] /*e*/ {1/*f*/,2};
|
||||
int arr4[][] = new /*a*/int/*b*/[1][2]/*c*/;
|
||||
|
||||
}
|
||||
|
||||
class Shoe
|
||||
{
|
||||
Shoe(String brand)
|
||||
{
|
||||
println(brand);
|
||||
}
|
||||
}
|
||||
|
||||
class NikeAir extends Shoe
|
||||
{
|
||||
NikeAir()
|
||||
{
|
||||
/*a*/ super /*b*/ ( /*c*/ "Nike" /*d*/ ) /*e*/ ;
|
||||
|
||||
/*aa*/ ( /*bb*/ new /*cc*/ MyDemo /*dd*/ (/*ee*/)/*ff*/)/*gg*/./*hh*/super/*ii*/(/*jj*/5/*kk*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
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$
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
365
app/syntax/DefaultInputHandler.java
Normal file
365
app/syntax/DefaultInputHandler.java
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* 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$
|
||||
*/
|
||||
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();
|
||||
|
||||
// don't get command-s or other menu key equivs on mac
|
||||
if ((modifiers & KeyEvent.META_MASK) != 0) 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(keyCode == KeyEvent.VK_CONTROL ||
|
||||
keyCode == KeyEvent.VK_SHIFT ||
|
||||
keyCode == KeyEvent.VK_ALT ||
|
||||
keyCode == KeyEvent.VK_META)
|
||||
return;
|
||||
|
||||
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
2227
app/syntax/JEditTextArea.java
Normal file
2227
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$
|
||||
*/
|
||||
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;
|
||||
}
|
||||
170
app/syntax/PdeEditorListener.java
Normal file
170
app/syntax/PdeEditorListener.java
Normal file
@@ -0,0 +1,170 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
PdeEditorListener - filter key events for tab expansion/indent/etc
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Except where noted, code is written by Ben Fry and
|
||||
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.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
|
||||
public class PdeEditorListener {
|
||||
PdeEditor editor;
|
||||
JEditTextArea textarea;
|
||||
|
||||
boolean externalEditor;
|
||||
boolean expandTabs;
|
||||
String tabString;
|
||||
boolean autoIndent;
|
||||
|
||||
int selectionStart, selectionEnd;
|
||||
int position;
|
||||
|
||||
|
||||
public PdeEditorListener(PdeEditor editor, JEditTextArea textarea) {
|
||||
this.editor = editor;
|
||||
this.textarea = textarea;
|
||||
|
||||
// let him know that i'm leechin'
|
||||
textarea.editorListener = this;
|
||||
|
||||
applyPreferences();
|
||||
}
|
||||
|
||||
|
||||
public void applyPreferences() {
|
||||
expandTabs = PdePreferences.getBoolean("editor.tabs.expand");
|
||||
int tabSize = PdePreferences.getInteger("editor.tabs.size");
|
||||
tabString = PdeEditor.EMPTY.substring(0, tabSize);
|
||||
autoIndent = PdePreferences.getBoolean("editor.indent");
|
||||
externalEditor = PdePreferences.getBoolean("editor.external");
|
||||
}
|
||||
|
||||
|
||||
//public void setExternalEditor(boolean externalEditor) {
|
||||
//this.externalEditor = externalEditor;
|
||||
//}
|
||||
|
||||
|
||||
// called by JEditTextArea inside processKeyEvent
|
||||
public boolean keyPressed(KeyEvent event) {
|
||||
// don't do things if the textarea isn't editable
|
||||
if (externalEditor) return false;
|
||||
|
||||
//deselect(); // this is for paren balancing
|
||||
char c = event.getKeyChar();
|
||||
int code = event.getKeyCode();
|
||||
|
||||
//System.out.println(c + " " + code + " " + event);
|
||||
//System.out.println();
|
||||
|
||||
if ((event.getModifiers() & KeyEvent.META_MASK) != 0) {
|
||||
//event.consume(); // does nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO i don't like these accessors. clean em up later.
|
||||
if (!editor.sketch.current.modified) {
|
||||
if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB) ||
|
||||
(code == KeyEvent.VK_ENTER) || ((c >= 32) && (c < 128))) {
|
||||
editor.sketch.setModified();
|
||||
}
|
||||
}
|
||||
|
||||
switch ((int) c) {
|
||||
|
||||
case 9: // expand tabs
|
||||
if (expandTabs) {
|
||||
//tc.replaceSelection(tabString);
|
||||
textarea.setSelectedText(tabString);
|
||||
event.consume();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 10: // auto-indent
|
||||
case 13:
|
||||
if (autoIndent) {
|
||||
char contents[] = textarea.getText().toCharArray();
|
||||
|
||||
// this is the position of the caret, if the textarea
|
||||
// only used a single kind of line ending
|
||||
int origIndex = textarea.getCaretPosition() - 1;
|
||||
|
||||
// walk through the array to the current caret position,
|
||||
// and count how many weirdo windows line endings there are,
|
||||
// which would be throwing off the caret position number
|
||||
int offset = 0;
|
||||
int realIndex = origIndex;
|
||||
for (int i = 0; i < realIndex-1; i++) {
|
||||
if ((contents[i] == 13) && (contents[i+1] == 10)) {
|
||||
offset++;
|
||||
realIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// back up until \r \r\n or \n.. @#($* cross platform
|
||||
|
||||
//System.out.println(origIndex + " offset = " + offset);
|
||||
origIndex += offset; // ARGH!#(* WINDOWS#@($*
|
||||
|
||||
int index = origIndex;
|
||||
int spaceCount = 0;
|
||||
boolean finished = false;
|
||||
while ((index != -1) && (!finished)) {
|
||||
if ((contents[index] == 10) ||
|
||||
(contents[index] == 13)) {
|
||||
finished = true;
|
||||
index++; // maybe ?
|
||||
} else {
|
||||
index--; // new
|
||||
}
|
||||
}
|
||||
while ((index < contents.length) && (index >= 0) &&
|
||||
(contents[index++] == ' ')) {
|
||||
spaceCount++;
|
||||
}
|
||||
|
||||
// seems that \r is being inserted anyway
|
||||
// so no need to insert the platform's line separator
|
||||
String insertion = "\n" + PdeEditor.EMPTY.substring(0, spaceCount);
|
||||
//tc.replaceSelection(insertion);
|
||||
textarea.setSelectedText(insertion);
|
||||
// microsoft vm version:
|
||||
//tc.setCaretPosition(oldCarrot + insertion.length() - 1);
|
||||
// sun vm version:
|
||||
// tc.setCaretPosition(oldCarrot + insertion.length());
|
||||
event.consume();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
/*
|
||||
PdeKeywords - handles text coloring and links to html reference
|
||||
Part of the Processing project - http://Proce55ing.net
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Except where noted, code is written by Ben Fry and
|
||||
Copyright (c) 2001-03 Massachusetts Institute of Technology
|
||||
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
|
||||
@@ -22,6 +22,10 @@
|
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
package processing.app.syntax;
|
||||
|
||||
import processing.app.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
106
app/syntax/PdeTextAreaDefaults.java
Normal file
106
app/syntax/PdeTextAreaDefaults.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/* -*- 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();
|
||||
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] = PdePreferences.getStyle("comment1");
|
||||
styles[Token.COMMENT2] = PdePreferences.getStyle("comment2");
|
||||
|
||||
// abstract, final, private
|
||||
styles[Token.KEYWORD1] = PdePreferences.getStyle("keyword1");
|
||||
|
||||
// beginShape, point, line
|
||||
styles[Token.KEYWORD2] = PdePreferences.getStyle("keyword2");
|
||||
|
||||
// byte, char, short, color
|
||||
styles[Token.KEYWORD3] = PdePreferences.getStyle("keyword3");
|
||||
|
||||
// constants: null, true, this, RGB, TWO_PI
|
||||
styles[Token.LITERAL1] = PdePreferences.getStyle("literal1");
|
||||
|
||||
// p5 built in variables: mouseX, width, pixels
|
||||
styles[Token.LITERAL2] = PdePreferences.getStyle("literal2");
|
||||
|
||||
// ??
|
||||
styles[Token.LABEL] = PdePreferences.getStyle("label");
|
||||
|
||||
// + - = /
|
||||
styles[Token.OPERATOR] = PdePreferences.getStyle("operator");
|
||||
|
||||
// area that's not in use by the text (replaced with tildes)
|
||||
styles[Token.INVALID] = PdePreferences.getStyle("invalid");
|
||||
|
||||
|
||||
// moved from TextAreaPainter
|
||||
|
||||
font = PdePreferences.getFont("editor.font");
|
||||
|
||||
fgcolor = PdePreferences.getColor("editor.fgcolor");
|
||||
bgcolor = PdePreferences.getColor("editor.bgcolor");
|
||||
|
||||
caretVisible = true;
|
||||
caretBlinks = PdePreferences.getBoolean("editor.caret.blink");
|
||||
caretColor = PdePreferences.getColor("editor.caret.color");
|
||||
|
||||
selectionColor = PdePreferences.getColor("editor.selection.color");
|
||||
|
||||
lineHighlight =
|
||||
PdePreferences.getBoolean("editor.linehighlight");
|
||||
lineHighlightColor =
|
||||
PdePreferences.getColor("editor.linehighlight.color");
|
||||
|
||||
bracketHighlight =
|
||||
PdePreferences.getBoolean("editor.brackethighlight");
|
||||
bracketHighlightColor =
|
||||
PdePreferences.getColor("editor.brackethighlight.color");
|
||||
|
||||
eolMarkers = PdePreferences.getBoolean("editor.eolmarkers");
|
||||
eolMarkerColor = PdePreferences.getColor("editor.eolmarkers.color");
|
||||
|
||||
paintInvalid = PdePreferences.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$
|
||||
*/
|
||||
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$
|
||||
*/
|
||||
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$
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
680
app/syntax/TextAreaPainter.java
Normal file
680
app/syntax/TextAreaPainter.java
Normal file
@@ -0,0 +1,680 @@
|
||||
/* -*- 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$
|
||||
*/
|
||||
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 (PdeBase.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;
|
||||
x2 = textArea._offsetToX(line, selectionEnd - lineStart);
|
||||
} else {
|
||||
x1 = 0;
|
||||
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$
|
||||
*/
|
||||
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$
|
||||
*/
|
||||
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$
|
||||
*
|
||||
* @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>
|
||||
@@ -1,11 +1,11 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
PdeFontBuilder - gui interface to font creation heaven/hell
|
||||
CreateFont - gui interface to font creation heaven/hell
|
||||
Part of the Processing project - http://processing.org
|
||||
|
||||
Except where noted, code is written by Ben Fry and
|
||||
Copyright (c) 2001-03 Massachusetts Institute of Technology
|
||||
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
|
||||
@@ -22,6 +22,9 @@
|
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
package processing.app.tools;
|
||||
|
||||
import processing.app.*;
|
||||
import processing.core.*;
|
||||
|
||||
import java.awt.*;
|
||||
@@ -34,7 +37,7 @@ import javax.swing.border.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
|
||||
public class PdeFontBuilder extends JFrame {
|
||||
public class CreateFont extends JFrame {
|
||||
File targetFolder;
|
||||
|
||||
Dimension windowSize;
|
||||
@@ -62,9 +65,11 @@ public class PdeFontBuilder extends JFrame {
|
||||
};
|
||||
|
||||
|
||||
public PdeFontBuilder() {
|
||||
public CreateFont(PdeEditor editor) {
|
||||
super("Create Font");
|
||||
|
||||
targetFolder = editor.sketch.dataFolder;
|
||||
|
||||
Container paine = getContentPane();
|
||||
paine.setLayout(new BorderLayout()); //10, 10));
|
||||
|
||||
@@ -347,10 +352,12 @@ public class PdeFontBuilder extends JFrame {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public void show(File targetFolder) {
|
||||
this.targetFolder = targetFolder;
|
||||
show();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
public void update() {
|
||||
Reference in New Issue
Block a user