highlighting for slashy strings

This commit is contained in:
Maxim.Medvedev
2011-12-12 12:55:42 +04:00
parent dd477bf1ad
commit 4fa0d72f6c
10 changed files with 145 additions and 68 deletions
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrRegex;
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
import java.util.List;
@@ -39,30 +40,29 @@ public class GroovyLiteralSelectioner extends GroovyBasicSelectioner {
}
private static boolean isLiteral(PsiElement element) {
if (element instanceof GrListOrMap) {
return true;
}
if (element instanceof GrListOrMap) return true;
if (!(element instanceof GrLiteral)) return false;
if (element instanceof GrRegex && ((GrRegex)element).getInjections().length == 0) return true;
ASTNode node = element.getNode();
if (node == null) return false;
ASTNode firstNode = node.getFirstChildNode();
final IElementType type = firstNode.getElementType();
return firstNode == node.getLastChildNode() && (type == mSTRING_LITERAL || type == mGSTRING_LITERAL || type == mREGEX_LITERAL);
return firstNode == node.getLastChildNode() && (type == mSTRING_LITERAL || type == mGSTRING_LITERAL);
}
public List<TextRange> select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) {
List<TextRange> result = super.select(e, editorText, cursorOffset, editor);
if (e instanceof GrListOrMap) {
return result;
}
if (e instanceof GrListOrMap) return result;
int startOffset = -1;
int endOffset = -1;
final String text = e.getText();
final int stringOffset = e.getTextOffset();
if (e.getNode().getElementType() == mGSTRING_CONTENT) {
final IElementType elementType = e.getNode().getElementType();
if (elementType == mGSTRING_CONTENT || elementType == mREGEX_CONTENT || elementType == mDOLLAR_SLASH_REGEX_CONTENT) {
int cur;
int index = -1;
while (true) {
@@ -46,7 +46,10 @@ public class GroovyWordSelectionFilter implements Condition<PsiElement> {
type == mGDOC_TAG_VALUE_TOKEN ||
type == mREGEX_BEGIN ||
type == mREGEX_CONTENT ||
type == mREGEX_END) {
type == mREGEX_END ||
type == mDOLLAR_SLASH_REGEX_BEGIN ||
type == mDOLLAR_SLASH_REGEX_CONTENT ||
type == mDOLLAR_SLASH_REGEX_END) {
return true;
}
@@ -49,8 +49,6 @@ public class DefaultHighlighter {
@NonNls
static final String STRING_ID = "String";
@NonNls
static final String REGEXP_ID = "Regular expression";
@NonNls
static final String BRACES_ID = "Braces";
@NonNls
static final String BRACKETS_ID = "Brackets";
@@ -150,9 +148,6 @@ public class DefaultHighlighter {
public static TextAttributesKey STRING = TextAttributesKey.createTextAttributesKey(STRING_ID,
SyntaxHighlighterColors.STRING.getDefaultAttributes());
public static TextAttributesKey REGEXP = TextAttributesKey.createTextAttributesKey(REGEXP_ID,
SyntaxHighlighterColors.VALID_STRING_ESCAPE.getDefaultAttributes());
public static TextAttributesKey BRACES = TextAttributesKey.createTextAttributesKey(BRACES_ID,
SyntaxHighlighterColors.BRACES.getDefaultAttributes());
@@ -19,14 +19,18 @@ package org.jetbrains.plugins.groovy.highlighter;
import com.intellij.lang.BracePair;
import com.intellij.lang.PairedBraceMatcher;
import com.intellij.psi.PsiFile;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.groovydoc.lexer.GroovyDocTokenTypes;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
import static com.intellij.psi.TokenType.WHITE_SPACE;
import static org.jetbrains.plugins.groovy.GroovyFileType.GROOVY_LANGUAGE;
import static org.jetbrains.plugins.groovy.lang.groovydoc.lexer.GroovyDocTokenTypes.mGDOC_INLINE_TAG_END;
import static org.jetbrains.plugins.groovy.lang.groovydoc.lexer.GroovyDocTokenTypes.mGDOC_INLINE_TAG_START;
import static org.jetbrains.plugins.groovy.lang.groovydoc.lexer.GroovyDocTokenTypes.mGDOC_TAG_VALUE_LPAREN;
import static org.jetbrains.plugins.groovy.lang.groovydoc.lexer.GroovyDocTokenTypes.mGDOC_TAG_VALUE_RPAREN;
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
import static org.jetbrains.plugins.groovy.lang.lexer.TokenSets.COMMENT_SET;
/**
* Brace matcher for Groovy language
@@ -36,32 +40,35 @@ import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
public class GroovyBraceMatcher implements PairedBraceMatcher {
private static final BracePair[] PAIRS = {
new BracePair(GroovyTokenTypes.mLPAREN, GroovyTokenTypes.mRPAREN, false),
new BracePair(GroovyTokenTypes.mLBRACK, GroovyTokenTypes.mRBRACK, false),
new BracePair(GroovyTokenTypes.mLCURLY, GroovyTokenTypes.mRCURLY, true),
new BracePair(mLPAREN, mRPAREN, false),
new BracePair(mLBRACK, mRBRACK, false),
new BracePair(mLCURLY, mRCURLY, true),
new BracePair(GroovyDocTokenTypes.mGDOC_INLINE_TAG_START, GroovyDocTokenTypes.mGDOC_INLINE_TAG_END, true),
new BracePair(GroovyDocTokenTypes.mGDOC_TAG_VALUE_LPAREN, GroovyDocTokenTypes.mGDOC_TAG_VALUE_RPAREN, false),
new BracePair(mGDOC_INLINE_TAG_START, mGDOC_INLINE_TAG_END, true),
new BracePair(mGDOC_TAG_VALUE_LPAREN, mGDOC_TAG_VALUE_RPAREN, false),
new BracePair(GroovyTokenTypes.mGSTRING_BEGIN, GroovyTokenTypes.mGSTRING_END, false),
new BracePair(GroovyTokenTypes.mREGEX_BEGIN, GroovyTokenTypes.mREGEX_END, false)
new BracePair(mGSTRING_BEGIN, mGSTRING_END, false),
new BracePair(mREGEX_BEGIN, mREGEX_END, false),
new BracePair(mDOLLAR_SLASH_REGEX_BEGIN, mDOLLAR_SLASH_REGEX_END, false),
};
public BracePair[] getPairs() {
return PAIRS;
}
public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType ibraceType, @Nullable IElementType tokenType) {
public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType braceType, @Nullable IElementType tokenType) {
return tokenType == null
|| TokenType.WHITE_SPACE == tokenType
|| TokenSets.COMMENT_SET.contains(tokenType)
|| tokenType == GroovyTokenTypes.mSEMI
|| tokenType == GroovyTokenTypes.mCOMMA
|| tokenType == GroovyTokenTypes.mRPAREN
|| tokenType == GroovyTokenTypes.mRBRACK
|| tokenType == GroovyTokenTypes.mRCURLY
|| tokenType == GroovyTokenTypes.mGSTRING_BEGIN
|| tokenType.getLanguage() != GroovyFileType.GROOVY_LANGUAGE;
|| tokenType == WHITE_SPACE
|| tokenType == mSEMI
|| tokenType == mCOMMA
|| tokenType == mRPAREN
|| tokenType == mRBRACK
|| tokenType == mRCURLY
|| tokenType == mGSTRING_BEGIN
|| tokenType == mREGEX_BEGIN
|| tokenType == mDOLLAR_SLASH_REGEX_BEGIN
|| COMMENT_SET.contains(tokenType)
|| tokenType.getLanguage() != GROOVY_LANGUAGE;
}
public int getCodeConstructStart(PsiFile file, int openingBraceOffset) {
@@ -58,7 +58,6 @@ public class GroovyColorsAndFontsPage implements ColorSettingsPage {
new AttributesDescriptor("Number", DefaultHighlighter.NUMBER),
new AttributesDescriptor("GString", DefaultHighlighter.GSTRING),
new AttributesDescriptor("String", DefaultHighlighter.STRING),
new AttributesDescriptor("Regular expression", DefaultHighlighter.REGEXP),
new AttributesDescriptor("Braces", DefaultHighlighter.BRACES),
new AttributesDescriptor("Brackets", DefaultHighlighter.BRACKETS),
new AttributesDescriptor("Parentheses", DefaultHighlighter.PARENTHESES),
@@ -0,0 +1,98 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.highlighter;
import com.intellij.lexer.LexerBase;
import com.intellij.psi.StringEscapesTokenTypes;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
/**
* @author Max Medvedev
*/
public class GroovySlashyStringLexer extends LexerBase {
private CharSequence myBuffer;
private int myStart;
private int myBufferEnd;
private IElementType myTokenType;
private int myEnd;
public GroovySlashyStringLexer() {
}
@Override
public void start(CharSequence buffer, int startOffset, int endOffset, int initialState) {
myBuffer = buffer;
myEnd = startOffset;
myBufferEnd = endOffset;
myTokenType = locateToken();
}
@Nullable
private IElementType locateToken() {
if (myEnd >= myBufferEnd) return null;
myStart = myEnd;
if (checkForEscape(myStart)) {
myEnd = myStart + 2;
return StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN;
}
while (myEnd < myBufferEnd && !checkForEscape(myEnd)) myEnd++;
return GroovyTokenTypes.mREGEX_CONTENT;
}
private boolean checkForEscape(int start) {
return myBuffer.charAt(start) == '\\' && start + 1 < myBufferEnd && myBuffer.charAt(start + 1) == '/';
}
@Override
public int getState() {
return 0;
}
@Override
public IElementType getTokenType() {
return myTokenType;
}
@Override
public int getTokenStart() {
return myStart;
}
@Override
public int getTokenEnd() {
return myEnd;
}
@Override
public void advance() {
myTokenType = locateToken();
}
@Override
public CharSequence getBufferSequence() {
return myBuffer;
}
@Override
public int getBufferEnd() {
return myBufferEnd;
}
}
@@ -62,10 +62,6 @@ public class GroovySyntaxHighlighter extends SyntaxHighlighterBase implements Gr
mWRONG
);
static final TokenSet tWRONG_REGEX = TokenSet.create(
mWRONG_REGEX_LITERAL
);
static final TokenSet tGSTRINGS = TokenSet.create(
mGSTRING_BEGIN,
mGSTRING_CONTENT,
@@ -77,15 +73,6 @@ public class GroovySyntaxHighlighter extends SyntaxHighlighterBase implements Gr
mSTRING_LITERAL
);
static final TokenSet tREGEXP = TokenSet.create(
mREGEX_LITERAL,
mREGEX_BEGIN,
mREGEX_CONTENT,
mREGEX_END
);
static final TokenSet tBRACES = TokenSet.create(
mLCURLY,
mRCURLY
@@ -210,8 +197,7 @@ public class GroovySyntaxHighlighter extends SyntaxHighlighterBase implements Gr
fillMap(ATTRIBUTES, tNUMBERS, DefaultHighlighter.NUMBER);
fillMap(ATTRIBUTES, tGSTRINGS, DefaultHighlighter.GSTRING);
fillMap(ATTRIBUTES, tSTRINGS, DefaultHighlighter.STRING);
fillMap(ATTRIBUTES, tREGEXP, DefaultHighlighter.REGEXP);
fillMap(ATTRIBUTES, tWRONG_REGEX, DefaultHighlighter.REGEXP);
fillMap(ATTRIBUTES, DefaultHighlighter.STRING, mREGEX_BEGIN, mREGEX_CONTENT, mREGEX_END, mDOLLAR_SLASH_REGEX_BEGIN, mDOLLAR_SLASH_REGEX_CONTENT, mDOLLAR_SLASH_REGEX_END);
fillMap(ATTRIBUTES, tBRACES, DefaultHighlighter.BRACES);
fillMap(ATTRIBUTES, tBRACKETS, DefaultHighlighter.BRACKETS);
fillMap(ATTRIBUTES, tPARENTHESES, DefaultHighlighter.PARENTHESES);
@@ -234,6 +220,8 @@ public class GroovySyntaxHighlighter extends SyntaxHighlighterBase implements Gr
new IElementType[]{GroovyTokenTypes.mGSTRING_LITERAL}, IElementType.EMPTY_ARRAY);
registerSelfStoppingLayer(new StringLiteralLexer(StringLiteralLexer.NO_QUOTE_CHAR, GroovyTokenTypes.mGSTRING_CONTENT, true, "$"),
new IElementType[]{GroovyTokenTypes.mGSTRING_CONTENT}, IElementType.EMPTY_ARRAY);
registerSelfStoppingLayer(new GroovySlashyStringLexer(), new IElementType[]{GroovyTokenTypes.mREGEX_CONTENT},
IElementType.EMPTY_ARRAY);
}
}
@@ -111,10 +111,7 @@ public class GroovyLiteralCopyPasteProcessor extends StringLiteralCopyPasteProce
protected String escapeCharCharacters(@NotNull String s, @NotNull PsiElement token, boolean escapeSlashes) {
IElementType tokenType = token.getNode().getElementType();
if (tokenType == mREGEX_CONTENT ||
tokenType == mREGEX_LITERAL ||
tokenType == mDOLLAR_SLASH_REGEX_CONTENT ||
tokenType == mDOLLAR_SLASH_REGEX_LITERAL) {
if (tokenType == mREGEX_CONTENT || tokenType == mDOLLAR_SLASH_REGEX_CONTENT) {
if (escapeSlashes) {
return StringUtil.escapeSlashes(s);
}
@@ -149,10 +146,7 @@ public class GroovyLiteralCopyPasteProcessor extends StringLiteralCopyPasteProce
protected String unescape(String text, PsiElement token) {
final IElementType tokenType = token.getNode().getElementType();
if (tokenType == mREGEX_CONTENT ||
tokenType == mREGEX_LITERAL ||
tokenType == mDOLLAR_SLASH_REGEX_CONTENT ||
tokenType == mDOLLAR_SLASH_REGEX_LITERAL) {
if (tokenType == mREGEX_CONTENT || tokenType == mDOLLAR_SLASH_REGEX_CONTENT) {
return StringUtil.unescapeSlashes(text);
}
@@ -73,9 +73,6 @@ public interface GroovyTokenTypes extends GroovyDocElementTypes {
IElementType mGSTRING_CONTENT = new GroovyElementType("Gstring content");
IElementType mGSTRING_END = new GroovyElementType("Gstring end");
IElementType mREGEX_LITERAL = new GroovyElementType("regexp");
IElementType mDOLLAR_SLASH_REGEX_LITERAL = new GroovyElementType("$/ regexp");
IElementType mREGEX_BEGIN = new GroovyElementType("regex begin");
IElementType mREGEX_CONTENT = new GroovyElementType("regex content");
IElementType mREGEX_END = new GroovyElementType("regex end");
@@ -68,9 +68,7 @@ public abstract class TokenSets {
kFALSE,
kNULL,
mSTRING_LITERAL,
mGSTRING_LITERAL,
mREGEX_LITERAL,
mDOLLAR_SLASH_REGEX_LITERAL
mGSTRING_LITERAL
);
public static final TokenSet BUILT_IN_TYPE = TokenSet.create(
@@ -123,10 +121,8 @@ public abstract class TokenSets {
public static TokenSet STRING_LITERALS = TokenSet.create(
mSTRING_LITERAL,
mREGEX_LITERAL,
mREGEX_CONTENT,
mDOLLAR_SLASH_REGEX_CONTENT,
mDOLLAR_SLASH_REGEX_LITERAL,
mGSTRING_LITERAL,
mGSTRING_CONTENT,
mGSTRING_BEGIN,