
org.fife.ui.rsyntaxtextarea.modes.CSSTokenMaker.flex Maven / Gradle / Ivy
Show all versions of rsyntaxtextarea Show documentation
/*
* 09/03/2005
*
* CSSTokenMaker.java - Token maker for CSS 3 files.
*
* This library is distributed under a modified BSD license. See the included
* LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea.modes;
import java.io.*;
import javax.swing.text.Segment;
import org.fife.ui.rsyntaxtextarea.*;
/**
* This class splits up text into tokens representing a CSS 3 file. It's
* written with a few extra internal states so that it can easily be copy
* and pasted into HTML, PHP, and JSP TokenMarkers when it is updated.
*
* This implementation was created using
* JFlex 1.4.1; however, the generated file
* was modified for performance. Memory allocation needs to be almost
* completely removed to be competitive with the handwritten lexers (subclasses
* of AbstractTokenMaker
), so this class has been modified so that
* Strings are never allocated (via yytext()), and the scanner never has to
* worry about refilling its buffer (needlessly copying chars around).
* We can achieve this because RText always scans exactly 1 line of tokens at a
* time, and hands the scanner this line as an array of characters (a Segment
* really). Since tokens contain pointers to char arrays instead of Strings
* holding their contents, there is no need for allocating new memory for
* Strings.
*
* The actual algorithm generated for scanning has, of course, not been
* modified.
*
* If you wish to regenerate this file yourself, keep in mind the following:
*
* - The generated
CSSTokenMaker.java
file will contain 2
* definitions of both zzRefill
and yyreset
.
* You should hand-delete the second of each definition (the ones
* generated by the lexer), as these generated methods modify the input
* buffer, which we'll never have to do.
* - You should also change the declaration/definition of zzBuffer to NOT
* be initialized. This is a needless memory allocation for us since we
* will be pointing the array somewhere else anyway.
* - You should NOT call
yylex()
on the generated scanner
* directly; rather, you should use getTokenList
as you would
* with any other TokenMaker
instance.
*
*
* @author Robert Futrell
* @version 0.5
*
*/
%%
%public
%class CSSTokenMaker
%extends AbstractJFlexCTokenMaker
%unicode
%type org.fife.ui.rsyntaxtextarea.Token
%{
/**
* Internal type denoting a line ending in a CSS property.
*/
public static final int INTERNAL_CSS_PROPERTY = -1;
/**
* Internal type denoting a line ending in a CSS property value.
*/
public static final int INTERNAL_CSS_VALUE = -2;
/**
* Internal type denoting line ending in a CSS double-quote string.
* The state to return to is embedded in the actual end token type.
*/
public static final int INTERNAL_CSS_STRING = -(1<<11);
/**
* Internal type denoting line ending in a CSS single-quote string.
* The state to return to is embedded in the actual end token type.
*/
public static final int INTERNAL_CSS_CHAR = -(2<<11);
/**
* Internal type denoting line ending in a CSS multi-line comment.
* The state to return to is embedded in the actual end token type.
*/
public static final int INTERNAL_CSS_MLC = -(3<<11);
/**
* The state previous CSS-related state we were in before going into a CSS
* string, multi-line comment, etc.
*/
private int cssPrevState;
/**
* Whether we are highlighting less instead of CSS.
*/
private boolean highlightingLess;
/**
* Constructor. This must be here because JFlex does not generate a
* no-parameter constructor.
*/
public CSSTokenMaker() {
super();
}
/**
* Adds the token specified to the current linked list of tokens as an
* "end token;" that is, at zzMarkedPos
.
*
* @param tokenType The token's type.
*/
private void addEndToken(int tokenType) {
addToken(zzMarkedPos,zzMarkedPos, tokenType);
}
/**
* Adds the token specified to the current linked list of tokens.
*
* @param tokenType The token's type.
* @see #addToken(int, int, int)
*/
private void addHyperlinkToken(int start, int end, int tokenType) {
int so = start + offsetShift;
addToken(zzBuffer, start,end, tokenType, so, true);
}
/**
* Adds the token specified to the current linked list of tokens.
*
* @param tokenType The token's type.
*/
private void addToken(int tokenType) {
addToken(zzStartRead, zzMarkedPos-1, tokenType);
}
/**
* Adds the token specified to the current linked list of tokens.
*
* @param tokenType The token's type.
*/
private void addToken(int start, int end, int tokenType) {
int so = start + offsetShift;
addToken(zzBuffer, start,end, tokenType, so);
}
/**
* Adds the token specified to the current linked list of tokens.
*
* @param array The character array.
* @param start The starting offset in the array.
* @param end The ending offset in the array.
* @param tokenType The token's type.
* @param startOffset The offset in the document at which this token
* occurs.
*/
@Override
public void addToken(char[] array, int start, int end, int tokenType, int startOffset) {
super.addToken(array, start,end, tokenType, startOffset);
zzStartRead = zzMarkedPos;
}
/**
* Returns the closest {@link TokenTypes "standard" token type} for a given
* "internal" token type (e.g. one whose value is < 0
).
*/
@Override
public int getClosestStandardTokenTypeForInternalType(int type) {
switch (type) {
case INTERNAL_CSS_STRING:
case INTERNAL_CSS_CHAR:
return TokenTypes.LITERAL_STRING_DOUBLE_QUOTE;
case INTERNAL_CSS_MLC:
return TokenTypes.COMMENT_MULTILINE;
}
return type;
}
@Override
public String[] getLineCommentStartAndEnd(int languageIndex) {
return new String[] { "/*", "*/" };
}
@Override
public boolean getMarkOccurrencesOfTokenType(int type) {
return type==Token.RESERVED_WORD; // Used for CSS keys
}
@Override
public boolean getShouldIndentNextLineAfter(Token t) {
if (t!=null && t.length()==1) {
char ch = t.charAt(0);
return ch=='{' || ch=='(';
}
return false;
}
/**
* Returns the first token in the linked list of tokens generated
* from text
. This method must be implemented by
* subclasses so they can correctly implement syntax highlighting.
*
* @param text The text from which to get tokens.
* @param initialTokenType The token type we should start with.
* @param startOffset The offset into the document at which
* text
starts.
* @return The first Token
in a linked list representing
* the syntax highlighted text.
*/
public Token getTokenList(Segment text, int initialTokenType, int startOffset) {
resetTokenList();
this.offsetShift = -text.offset + startOffset;
cssPrevState = YYINITIAL; // Shouldn't be necessary
// Start off in the proper state.
int state;
switch (initialTokenType) {
case INTERNAL_CSS_PROPERTY:
state = CSS_PROPERTY;
break;
case INTERNAL_CSS_VALUE:
state = CSS_VALUE;
break;
default:
if (initialTokenType<-1024) {
int main = -(-initialTokenType & 0xffffff00);
switch (main) {
default: // Should never happen
case INTERNAL_CSS_STRING:
state = CSS_STRING;
break;
case INTERNAL_CSS_CHAR:
state = CSS_CHAR_LITERAL;
break;
case INTERNAL_CSS_MLC:
state = CSS_C_STYLE_COMMENT;
break;
}
cssPrevState = -initialTokenType&0xff;
}
else {
state = YYINITIAL;
}
}
start = text.offset;
s = text;
try {
yyreset(zzReader);
yybegin(state);
return yylex();
} catch (IOException ioe) {
ioe.printStackTrace();
return new TokenImpl();
}
}
/**
* Overridden to accept letters, digits, underscores, and hyphens.
*/
@Override
public boolean isIdentifierChar(int languageIndex, char ch) {
return Character.isLetterOrDigit(ch) || ch=='-' || ch=='.' || ch=='_';
}
/**
* Toggles whether we're highlighting less instead of just CSS.
*
* @param highlightingLess Whether we're highlighting less.
*/
public void setHighlightingLess(boolean highlightingLess) {
this.highlightingLess = highlightingLess;
}
/**
* Refills the input buffer.
*
* @return true
if EOF was reached, otherwise
* false
.
*/
private boolean zzRefill() {
return zzCurrentPos>=s.offset+s.count;
}
/**
* Resets the scanner to read from a new input stream.
* Does not close the old reader.
*
* All internal variables are reset, the old input stream
* cannot be reused (internal buffer is discarded and lost).
* Lexical state is set to YY_INITIAL.
*
* @param reader the new input stream
*/
public final void yyreset(java.io.Reader reader) {
// 's' has been updated.
zzBuffer = s.array;
/*
* We replaced the line below with the two below it because zzRefill
* no longer "refills" the buffer (since the way we do it, it's always
* "full" the first time through, since it points to the segment's
* array). So, we assign zzEndRead here.
*/
//zzStartRead = zzEndRead = s.offset;
zzStartRead = s.offset;
zzEndRead = zzStartRead + s.count - 1;
zzCurrentPos = zzMarkedPos = zzPushbackPos = s.offset;
zzLexicalState = YYINITIAL;
zzReader = reader;
zzAtBOL = true;
zzAtEOF = false;
}
%}
Digit = ([0-9])
Letter = ([A-Za-z])
LetterOrUnderscore = ({Letter}|[_])
LetterOrUnderscoreOrDash = ({LetterOrUnderscore}|[\-])
CSS_SelectorPiece = (("*"|"."|{LetterOrUnderscoreOrDash})({LetterOrUnderscoreOrDash}|"."|{Digit})*)
CSS_PseudoClass = (":"("root"|"nth-child"|"nth-last-child"|"nth-of-type"|"nth-last-of-type"|"first-child"|"last-child"|"first-of-type"|"last-of-type"|"only-child"|"only-of-type"|"empty"|"link"|"visited"|"active"|"hover"|"focus"|"target"|"lang"|"enabled"|"disabled"|"checked"|":first-line"|":first-letter"|":before"|":after"|"not"))
CSS_AtRule = ("@"(charset|import|namespace|media|document|page|font-face|keyframes|viewport))
CSS_Less_Var = ("@"({LetterOrUnderscoreOrDash})({LetterOrUnderscoreOrDash}|"."|{Digit})*)
CSS_Id = ("#"{CSS_SelectorPiece})
CSS_Separator = ([;\(\)\[\]])
WhiteSpace = ([ \t]+)
MlcStart = ("/*")
MlcEnd = ("*/")
CSS_Property = ([\*]?{LetterOrUnderscoreOrDash}({LetterOrUnderscoreOrDash}|{Digit})*(:[\w]+)?)
CSS_ValueChar = ({LetterOrUnderscoreOrDash}|[\\/])
CSS_Value = ({CSS_ValueChar}*)
CSS_Function = ({CSS_Value}\()
CSS_Digits = ([\-]?{Digit}+([0-9\.]+)?(pt|pc|in|mm|cm|em|ex|px|ms|s|%)?)
CSS_Hex = ("#"[0-9a-fA-F]+)
CSS_Number = ({CSS_Digits}|{CSS_Hex})
// Less_Nested_Selector includes pseudo-classes for ease of highlighting, even
// though it is not as detailed as standard CSS selector highlighting.
Less_Nested_Selector_With_Pseudo = ({CSS_SelectorPiece}{CSS_PseudoClass}*)
Less_Selector_ParentRef = ("&"{CSS_SelectorPiece})
Less_LineCommentBegin = "//"
URLGenDelim = ([:\/\?#\[\]@])
URLSubDelim = ([\!\$&'\(\)\*\+,;=])
URLUnreserved = ({LetterOrUnderscore}|{Digit}|[\-\.\~])
URLCharacter = ({URLGenDelim}|{URLSubDelim}|{URLUnreserved}|[%])
URLCharacters = ({URLCharacter}*)
URLEndCharacter = ([\/\$]|{Letter}|{Digit})
URL = (((https?|f(tp|ile))"://"|"www.")({URLCharacters}{URLEndCharacter})?)
%state CSS_PROPERTY
%state CSS_VALUE
%state CSS_STRING
%state CSS_CHAR_LITERAL
%state CSS_C_STYLE_COMMENT
%state LESS_EOL_COMMENT
%%
{
{CSS_SelectorPiece} { addToken(Token.DATA_TYPE); }
{CSS_PseudoClass} { addToken(Token.RESERVED_WORD); }
":" { /* Unknown pseudo class */ addToken(Token.DATA_TYPE); }
{CSS_AtRule} { addToken(Token.REGEX); }
{CSS_Less_Var} { addToken(highlightingLess ? Token.VARIABLE : Token.REGEX); }
{CSS_Number} { addToken(highlightingLess ? Token.LITERAL_NUMBER_DECIMAL_INT : Token.IDENTIFIER); }
{CSS_Id} { addToken(highlightingLess ? Token.ANNOTATION : Token.VARIABLE); }
"{" { addToken(Token.SEPARATOR); yybegin(CSS_PROPERTY); }
[,] { addToken(Token.IDENTIFIER); }
\" { start = zzMarkedPos-1; cssPrevState = zzLexicalState; yybegin(CSS_STRING); }
\' { start = zzMarkedPos-1; cssPrevState = zzLexicalState; yybegin(CSS_CHAR_LITERAL); }
"}" { addToken(highlightingLess ? Token.SEPARATOR : Token.IDENTIFIER); }
[+>~\^$\|=] { addToken(Token.OPERATOR); }
{CSS_Separator} { addToken(Token.SEPARATOR); }
{WhiteSpace} { addToken(Token.WHITESPACE); }
{MlcStart} { start = zzMarkedPos-2; cssPrevState = zzLexicalState; yybegin(CSS_C_STYLE_COMMENT); }
{Less_LineCommentBegin} {
if (highlightingLess) {
start = zzMarkedPos-2; yybegin(LESS_EOL_COMMENT);
}
else { // Highlight the "//" as an identifier and continue on
int temp = zzStartRead + 2;
addToken(zzStartRead, zzStartRead + 1, Token.IDENTIFIER);
zzStartRead = temp;
}
}
. { /*System.out.println("yyinitial: " + yytext());*/ addToken(Token.IDENTIFIER); }
"\n" |
<> { addNullToken(); return firstToken; }
}
{
{CSS_Property} { addToken(Token.RESERVED_WORD); }
{CSS_Less_Var} { addToken(highlightingLess ? Token.VARIABLE : Token.IDENTIFIER); }
{Less_Nested_Selector_With_Pseudo} { addToken(highlightingLess ? Token.RESERVED_WORD : Token.IDENTIFIER); }
{Less_Selector_ParentRef} {
if (highlightingLess) {
// Unfortunately, as we're sharing the CSS and Less
// syntax highlighting, we do not color nested selectors
// properly. For uniformity, color this the same as
// CSS_Property
addToken(Token.RESERVED_WORD);
}
else {
addToken(Token.IDENTIFIER);
}
}
"{" { addToken(Token.SEPARATOR); /* helps with auto-closing curlies when editing CSS */ }
"}" { addToken(Token.SEPARATOR); yybegin(YYINITIAL); }
":" { addToken(Token.OPERATOR); yybegin(CSS_VALUE); }
{WhiteSpace} { addToken(Token.WHITESPACE); }
{MlcStart} { start = zzMarkedPos-2; cssPrevState = zzLexicalState; yybegin(CSS_C_STYLE_COMMENT); }
{Less_LineCommentBegin} {
if (highlightingLess) {
cssPrevState = zzLexicalState;
start = zzMarkedPos-2; yybegin(LESS_EOL_COMMENT);
}
else { // Highlight the "//" as an identifier and continue on
int temp = zzStartRead + 2;
addToken(zzStartRead, zzStartRead + 1, Token.IDENTIFIER);
zzStartRead = temp;
}
}
. { /*System.out.println("css_property: " + yytext());*/ addToken(Token.IDENTIFIER); }
"\n" |
<> { addEndToken(INTERNAL_CSS_PROPERTY); return firstToken; }
}
{
{Less_LineCommentBegin} {
// This must be before {CSS_Value} since "//" is a valid CSS value
if (highlightingLess) {
cssPrevState = zzLexicalState;
start = zzMarkedPos-2; yybegin(LESS_EOL_COMMENT);
}
else { // Highlight the "//" as an identifier and continue on
int temp = zzStartRead + 2;
addToken(zzStartRead, zzStartRead + 1, Token.IDENTIFIER);
zzStartRead = temp;
}
}
{CSS_Value} { addToken(Token.IDENTIFIER); }
"!important" { addToken(Token.PREPROCESSOR); }
{CSS_Function} { int temp = zzMarkedPos - 2;
addToken(zzStartRead, temp, Token.FUNCTION);
addToken(zzMarkedPos-1, zzMarkedPos-1, Token.SEPARATOR);
zzStartRead = zzCurrentPos = zzMarkedPos;
}
{CSS_Number} { addToken(Token.LITERAL_NUMBER_DECIMAL_INT); }
{CSS_Less_Var} { addToken(highlightingLess ? Token.VARIABLE : Token.IDENTIFIER); }
\" { start = zzMarkedPos-1; cssPrevState = zzLexicalState; yybegin(CSS_STRING); }
\' { start = zzMarkedPos-1; cssPrevState = zzLexicalState; yybegin(CSS_CHAR_LITERAL); }
")" { /* End of a function */ addToken(Token.SEPARATOR); }
[;] { addToken(Token.OPERATOR); yybegin(CSS_PROPERTY); }
[,\.] { addToken(Token.IDENTIFIER); }
"}" { addToken(Token.SEPARATOR); yybegin(YYINITIAL); }
{WhiteSpace} { addToken(Token.WHITESPACE); }
{MlcStart} { start = zzMarkedPos-2; cssPrevState = zzLexicalState; yybegin(CSS_C_STYLE_COMMENT); }
. { /*System.out.println("css_value: " + yytext());*/ addToken(Token.IDENTIFIER); }
"\n" |
<> { addEndToken(INTERNAL_CSS_VALUE); return firstToken; }
}
{
[^\n\\\"]+ {}
\\.? { /* Skip escaped chars. */ }
\" { addToken(start,zzStartRead, Token.LITERAL_STRING_DOUBLE_QUOTE); yybegin(cssPrevState); }
\n |
<> { addToken(start,zzStartRead-1, Token.LITERAL_STRING_DOUBLE_QUOTE); addEndToken(INTERNAL_CSS_STRING - cssPrevState); return firstToken; }
}
{
[^\n\\\']+ {}
\\.? { /* Skip escaped chars. */ }
\' { addToken(start,zzStartRead, Token.LITERAL_CHAR); yybegin(cssPrevState); }
\n |
<> { addToken(start,zzStartRead-1, Token.LITERAL_CHAR); addEndToken(INTERNAL_CSS_CHAR - cssPrevState); return firstToken; }
}
{
[^hwf\n\*]+ {}
{URL} { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_MULTILINE); addHyperlinkToken(temp,zzMarkedPos-1, Token.COMMENT_MULTILINE); start = zzMarkedPos; }
[hwf] {}
{MlcEnd} { addToken(start,zzStartRead+1, Token.COMMENT_MULTILINE); yybegin(cssPrevState); }
\* {}
\n |
<> { addToken(start,zzStartRead-1, Token.COMMENT_MULTILINE); addEndToken(INTERNAL_CSS_MLC - cssPrevState); return firstToken; }
}
{
[^hwf\n]+ {}
{URL} { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_EOL); addHyperlinkToken(temp,zzMarkedPos-1, Token.COMMENT_EOL); start = zzMarkedPos; }
[hwf] {}
\n |
<> {
addToken(start,zzStartRead-1, Token.COMMENT_EOL);
switch (cssPrevState) {
case CSS_PROPERTY:
addEndToken(INTERNAL_CSS_PROPERTY);
break;
case CSS_VALUE:
addEndToken(INTERNAL_CSS_VALUE);
break;
default: // Should be YYINITIAL
addNullToken();
break;
}
return firstToken;
}
}