| /* |
| * Copyright 2000-2014 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.editor; |
| |
| import com.intellij.codeInsight.editorActions.StringLiteralCopyPasteProcessor; |
| import com.intellij.lang.ASTNode; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.RawText; |
| import com.intellij.openapi.editor.SelectionModel; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.util.text.LineTokenizer; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.tree.IElementType; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; |
| import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil; |
| |
| /** |
| * @author peter |
| */ |
| public class GroovyLiteralCopyPasteProcessor extends StringLiteralCopyPasteProcessor { |
| |
| private static final Logger LOG = Logger.getInstance(GroovyLiteralCopyPasteProcessor.class); |
| |
| @Override |
| protected boolean isCharLiteral(@NotNull PsiElement token) { |
| return false; |
| } |
| |
| @Override |
| protected boolean isStringLiteral(@NotNull PsiElement token) { |
| ASTNode node = token.getNode(); |
| return node != null && |
| (TokenSets.STRING_LITERALS.contains(node.getElementType()) || |
| node.getElementType() == GroovyElementTypes.GSTRING_INJECTION || |
| node.getElementType() == GroovyElementTypes.GSTRING_CONTENT); |
| } |
| |
| @Override |
| @Nullable |
| protected PsiElement findLiteralTokenType(PsiFile file, int selectionStart, int selectionEnd) { |
| PsiElement elementAtSelectionStart = file.findElementAt(selectionStart); |
| if (elementAtSelectionStart == null) { |
| return null; |
| } |
| IElementType elementType = elementAtSelectionStart.getNode().getElementType(); |
| if ((elementType == GroovyTokenTypes.mREGEX_END || elementType == GroovyTokenTypes.mDOLLAR_SLASH_REGEX_END || elementType == |
| GroovyTokenTypes.mGSTRING_END) && |
| elementAtSelectionStart.getTextOffset() == selectionStart) { |
| elementAtSelectionStart = elementAtSelectionStart.getPrevSibling(); |
| if (elementAtSelectionStart == null) return null; |
| elementType = elementAtSelectionStart.getNode().getElementType(); |
| } |
| if (elementType == GroovyTokenTypes.mDOLLAR) { |
| elementAtSelectionStart = elementAtSelectionStart.getParent(); |
| elementType = elementAtSelectionStart.getNode().getElementType(); |
| } |
| |
| if (!isStringLiteral(elementAtSelectionStart)) { |
| return null; |
| } |
| |
| if (elementAtSelectionStart.getTextRange().getEndOffset() < selectionEnd) { |
| final PsiElement elementAtSelectionEnd = file.findElementAt(selectionEnd); |
| if (elementAtSelectionEnd == null) { |
| return null; |
| } |
| if (elementAtSelectionEnd.getNode().getElementType() == elementType && |
| elementAtSelectionEnd.getTextRange().getStartOffset() < selectionEnd) { |
| return elementAtSelectionStart; |
| } |
| } |
| |
| final TextRange textRange = elementAtSelectionStart.getTextRange(); |
| |
| //content elements don't have quotes, so they are shorter than whole string literals |
| if (elementType == GroovyTokenTypes.mREGEX_CONTENT || |
| elementType == GroovyTokenTypes.mGSTRING_CONTENT || |
| elementType == GroovyTokenTypes.mDOLLAR_SLASH_REGEX_CONTENT || |
| elementType == GroovyElementTypes.GSTRING_INJECTION) { |
| selectionStart++; |
| selectionEnd--; |
| } |
| if (textRange.getLength() > 0 && (selectionStart <= textRange.getStartOffset() || selectionEnd >= textRange.getEndOffset())) { |
| return null; |
| } |
| |
| if (elementType == GroovyElementTypes.GSTRING_CONTENT) { |
| elementAtSelectionStart = elementAtSelectionStart.getFirstChild(); |
| } |
| |
| return elementAtSelectionStart; |
| } |
| |
| |
| @Override |
| protected String getLineBreaker(@NotNull PsiElement token) { |
| PsiElement parent = GrStringUtil.findContainingLiteral(token); |
| final String text = parent.getText(); |
| if (text.contains("'''") || text.contains("\"\"\"")) { |
| return "\n"; |
| } |
| |
| final IElementType type = token.getNode().getElementType(); |
| if (type == GroovyTokenTypes.mGSTRING_LITERAL || type == GroovyTokenTypes.mGSTRING_CONTENT) { |
| return super.getLineBreaker(token); |
| } |
| if (type == GroovyTokenTypes.mSTRING_LITERAL) { |
| return super.getLineBreaker(token).replace('"', '\''); |
| } |
| |
| return "\n"; |
| |
| } |
| |
| @NotNull |
| @Override |
| public String preprocessOnPaste(Project project, PsiFile file, Editor editor, String text, RawText rawText) { |
| final Document document = editor.getDocument(); |
| PsiDocumentManager.getInstance(project).commitDocument(document); |
| final SelectionModel selectionModel = editor.getSelectionModel(); |
| |
| // pastes in block selection mode (column mode) are not handled by a CopyPasteProcessor |
| final int selectionStart = selectionModel.getSelectionStart(); |
| final int selectionEnd = selectionModel.getSelectionEnd(); |
| PsiElement token = findLiteralTokenType(file, selectionStart, selectionEnd); |
| if (token == null) { |
| return text; |
| } |
| |
| if (isStringLiteral(token)) { |
| StringBuilder buffer = new StringBuilder(text.length()); |
| @NonNls String breaker = getLineBreaker(token); |
| final String[] lines = LineTokenizer.tokenize(text.toCharArray(), false, true); |
| for (int i = 0; i < lines.length; i++) { |
| buffer.append(escapeCharCharacters(lines[i], token)); |
| if (i != lines.length - 1 || "\n".equals(breaker) && text.endsWith("\n")) { |
| buffer.append(breaker); |
| } |
| } |
| text = buffer.toString(); |
| } |
| return text; |
| } |
| |
| @NotNull |
| @Override |
| protected String escapeCharCharacters(@NotNull String s, @NotNull PsiElement token) { |
| if (s.isEmpty()) return s; |
| IElementType tokenType = token.getNode().getElementType(); |
| |
| if (tokenType == GroovyTokenTypes.mREGEX_CONTENT || tokenType == GroovyTokenTypes.mREGEX_LITERAL) { |
| return GrStringUtil.escapeSymbolsForSlashyStrings(s); |
| } |
| |
| if (tokenType == GroovyTokenTypes.mDOLLAR_SLASH_REGEX_CONTENT || tokenType == GroovyTokenTypes.mDOLLAR_SLASH_REGEX_LITERAL) { |
| return GrStringUtil.escapeSymbolsForDollarSlashyStrings(s); |
| } |
| |
| if (tokenType == GroovyTokenTypes.mGSTRING_CONTENT || tokenType == GroovyTokenTypes.mGSTRING_LITERAL || tokenType == GroovyElementTypes.GSTRING_INJECTION) { |
| boolean singleLine = !GrStringUtil.findContainingLiteral(token).getText().contains("\"\"\""); |
| StringBuilder b = new StringBuilder(); |
| GrStringUtil.escapeStringCharacters(s.length(), s, singleLine ? "\"" : "", singleLine, true, b); |
| GrStringUtil.unescapeCharacters(b, singleLine ? "'" : "'\"", true); |
| LOG.assertTrue(b.length() > 0, "s=" + s); |
| for (int i = b.length() - 2; i >= 0; i--) { |
| if (b.charAt(i) == '$') { |
| final char next = b.charAt(i + 1); |
| if (next != '{' && !Character.isLetter(next)) { |
| b.insert(i, '\\'); |
| } |
| } |
| } |
| if (b.charAt(b.length() - 1) == '$') { |
| b.insert(b.length() - 1, '\\'); |
| } |
| return b.toString(); |
| } |
| |
| if (tokenType == GroovyTokenTypes.mSTRING_LITERAL) { |
| return GrStringUtil.escapeSymbolsForString(s, !token.getText().contains("'''"), false); |
| } |
| |
| return super.escapeCharCharacters(s, token); |
| } |
| |
| @NotNull |
| @Override |
| protected String unescape(String s, PsiElement token) { |
| final IElementType tokenType = token.getNode().getElementType(); |
| |
| if (tokenType == GroovyTokenTypes.mREGEX_CONTENT || tokenType == GroovyTokenTypes.mREGEX_LITERAL) { |
| return GrStringUtil.unescapeSlashyString(s); |
| } |
| |
| if (tokenType == GroovyTokenTypes.mDOLLAR_SLASH_REGEX_CONTENT || tokenType == GroovyTokenTypes.mDOLLAR_SLASH_REGEX_LITERAL) { |
| return GrStringUtil.unescapeDollarSlashyString(s); |
| } |
| |
| if (tokenType == GroovyTokenTypes.mGSTRING_CONTENT || tokenType == GroovyTokenTypes.mGSTRING_LITERAL) { |
| return GrStringUtil.unescapeString(s); |
| } |
| |
| if (tokenType == GroovyTokenTypes.mSTRING_LITERAL) { |
| return GrStringUtil.unescapeString(s); |
| } |
| |
| return super.unescape(s, token); |
| } |
| |
| } |