mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
enter handler for regexes.
This commit is contained in:
+4
-5
@@ -22,10 +22,10 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.GroovyFileType;
|
||||
import org.jetbrains.plugins.groovy.formatter.GroovyBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.editor.actions.GroovyEditorActionUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment;
|
||||
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocTag;
|
||||
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
|
||||
@@ -50,6 +50,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMe
|
||||
*/
|
||||
public abstract class GroovyIndentProcessor implements GroovyElementTypes {
|
||||
public static final int GDOC_COMMENT_INDENT = 1;
|
||||
private static TokenSet GSTRING_TOKENS_INNER = TokenSet.create(mGSTRING_BEGIN,mGSTRING_CONTENT,mGSTRING_END,mDOLLAR);
|
||||
|
||||
/**
|
||||
* Calculates indent, based on code style, between parent block and child node
|
||||
@@ -72,14 +73,12 @@ public abstract class GroovyIndentProcessor implements GroovyElementTypes {
|
||||
return Indent.getContinuationIndent();
|
||||
}
|
||||
|
||||
if (GroovyEditorActionUtil.GSTRING_TOKENS_INNER.contains(child.getElementType()) &&
|
||||
mGSTRING_BEGIN != child.getElementType()) {
|
||||
if (GSTRING_TOKENS_INNER.contains(child.getElementType()) && mGSTRING_BEGIN != child.getElementType()) {
|
||||
return Indent.getAbsoluteNoneIndent();
|
||||
}
|
||||
|
||||
if (psiParent instanceof GrListOrMap) {
|
||||
if (mLBRACK.equals(child.getElementType()) ||
|
||||
mRBRACK.equals(child.getElementType())) {
|
||||
if (mLBRACK.equals(child.getElementType()) || mRBRACK.equals(child.getElementType())) {
|
||||
return Indent.getNoneIndent();
|
||||
} else {
|
||||
return Indent.getContinuationWithoutFirstIndent();
|
||||
|
||||
+5
-19
@@ -23,12 +23,13 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.plugins.groovy.GroovyFileType;
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrString;
|
||||
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mGSTRING_LITERAL;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mSTRING_LITERAL;
|
||||
|
||||
/**
|
||||
* @author ilyas
|
||||
*/
|
||||
@@ -66,7 +67,7 @@ public class GroovyEditorActionUtil {
|
||||
if (child != null && child.getNode() != null) {
|
||||
ASTNode node = child.getNode();
|
||||
assert node != null;
|
||||
return node.getElementType() == GroovyTokenTypes.mSTRING_LITERAL;
|
||||
return node.getElementType() == mSTRING_LITERAL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -76,23 +77,8 @@ public class GroovyEditorActionUtil {
|
||||
if (child != null && child.getNode() != null) {
|
||||
ASTNode node = child.getNode();
|
||||
assert node != null;
|
||||
return node.getElementType() == GroovyTokenTypes.mGSTRING_LITERAL;
|
||||
return node.getElementType() == mGSTRING_LITERAL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TokenSet GSTRING_TOKENS = TokenSet.create(
|
||||
GroovyTokenTypes.mGSTRING_BEGIN,
|
||||
GroovyTokenTypes.mGSTRING_CONTENT,
|
||||
GroovyTokenTypes.mGSTRING_END,
|
||||
GroovyTokenTypes.mGSTRING_LITERAL,
|
||||
GroovyTokenTypes.mDOLLAR
|
||||
);
|
||||
|
||||
public static TokenSet GSTRING_TOKENS_INNER = TokenSet.create(
|
||||
GroovyTokenTypes.mGSTRING_BEGIN,
|
||||
GroovyTokenTypes.mGSTRING_CONTENT,
|
||||
GroovyTokenTypes.mGSTRING_END,
|
||||
GroovyTokenTypes.mDOLLAR
|
||||
);
|
||||
}
|
||||
|
||||
+110
-30
@@ -50,13 +50,64 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
|
||||
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mCLOSABLE_BLOCK_OP;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mDOLLAR;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mDOLLAR_SLASH_REGEX_BEGIN;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mDOLLAR_SLASH_REGEX_CONTENT;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mDOLLAR_SLASH_REGEX_END;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mDOLLAR_SLASH_REGEX_LITERAL;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mGSTRING_BEGIN;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mGSTRING_CONTENT;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mGSTRING_END;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mGSTRING_LITERAL;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mIDENT;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mLBRACK;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mLCURLY;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mNLS;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mRBRACK;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mRCURLY;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mREGEX_BEGIN;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mREGEX_CONTENT;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mREGEX_END;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mREGEX_LITERAL;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mSTRING_LITERAL;
|
||||
import static org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes.*;
|
||||
|
||||
/**
|
||||
* @author ilyas
|
||||
*/
|
||||
public class GroovyEnterHandler extends EnterHandlerDelegateAdapter {
|
||||
|
||||
private static final TokenSet GSTRING_TOKENS = TokenSet.create(mGSTRING_BEGIN, mGSTRING_CONTENT, mGSTRING_END, mGSTRING_LITERAL);
|
||||
|
||||
private static final TokenSet REGEX_TOKENS = TokenSet.create(mREGEX_BEGIN, mREGEX_CONTENT, mREGEX_END, mDOLLAR_SLASH_REGEX_BEGIN,
|
||||
mDOLLAR_SLASH_REGEX_CONTENT, mDOLLAR_SLASH_REGEX_END);
|
||||
|
||||
private static final TokenSet AFTER_DOLLAR = TokenSet.create(mLCURLY, mIDENT, mGSTRING_CONTENT, mDOLLAR, mGSTRING_END, mREGEX_CONTENT,
|
||||
mDOLLAR_SLASH_REGEX_CONTENT, mREGEX_END, mDOLLAR_SLASH_REGEX_END);
|
||||
|
||||
private static final TokenSet ALL_STRINGS = TokenSet.create(mSTRING_LITERAL, mGSTRING_LITERAL, mGSTRING_BEGIN, mGSTRING_END,
|
||||
mGSTRING_CONTENT, mRCURLY, mIDENT, mDOLLAR, mREGEX_BEGIN, mREGEX_CONTENT,
|
||||
mREGEX_END, mDOLLAR_SLASH_REGEX_BEGIN, mDOLLAR_SLASH_REGEX_CONTENT,
|
||||
mDOLLAR_SLASH_REGEX_END, mREGEX_LITERAL, mDOLLAR_SLASH_REGEX_LITERAL);
|
||||
|
||||
private static final TokenSet BEFORE_DOLLAR =TokenSet.create(mGSTRING_BEGIN, mGSTRING_CONTENT, mREGEX_BEGIN, mREGEX_CONTENT,
|
||||
mDOLLAR_SLASH_REGEX_BEGIN, mDOLLAR_SLASH_REGEX_CONTENT);
|
||||
|
||||
private static final TokenSet EXPR_END = TokenSet.create(mRCURLY, mIDENT);
|
||||
|
||||
private static final TokenSet AFTER_EXPR_END = TokenSet.create(mGSTRING_END, mGSTRING_CONTENT, mDOLLAR, mREGEX_END, mREGEX_CONTENT,
|
||||
mDOLLAR_SLASH_REGEX_END, mDOLLAR_SLASH_REGEX_CONTENT);
|
||||
|
||||
private static final TokenSet STRING_END = TokenSet.create(mSTRING_LITERAL, mGSTRING_LITERAL, mGSTRING_END, mREGEX_END,
|
||||
mDOLLAR_SLASH_REGEX_END, mREGEX_LITERAL, mDOLLAR_SLASH_REGEX_LITERAL);
|
||||
|
||||
private static final TokenSet INNER_STRING_TOKENS = TokenSet.create(mGSTRING_BEGIN, mGSTRING_CONTENT, mGSTRING_END, mREGEX_BEGIN,
|
||||
mREGEX_CONTENT, mREGEX_END, mDOLLAR_SLASH_REGEX_BEGIN,
|
||||
mDOLLAR_SLASH_REGEX_CONTENT, mDOLLAR_SLASH_REGEX_END,
|
||||
GSTRING_INJECTION);
|
||||
|
||||
|
||||
public Result preprocessEnter(@NotNull PsiFile file,
|
||||
@NotNull Editor editor,
|
||||
@NotNull Ref<Integer> caretOffset,
|
||||
@@ -204,22 +255,6 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final TokenSet AFTER_DOLLAR = TokenSet.create(mLCURLY, mIDENT, mGSTRING_CONTENT, mDOLLAR, mGSTRING_END);
|
||||
|
||||
private static final TokenSet ALL_STRINGS = TokenSet
|
||||
.create(mSTRING_LITERAL, mGSTRING_LITERAL, mGSTRING_BEGIN, mGSTRING_END, mGSTRING_CONTENT, mRCURLY, mIDENT, mDOLLAR, mREGEX_BEGIN,
|
||||
mREGEX_CONTENT, mREGEX_END, mDOLLAR_SLASH_REGEX_BEGIN, mDOLLAR_SLASH_REGEX_CONTENT, mDOLLAR_SLASH_REGEX_END, mREGEX_LITERAL,
|
||||
mDOLLAR_SLASH_REGEX_LITERAL);
|
||||
|
||||
private static final TokenSet BEFORE_DOLLAR = TokenSet.create(mGSTRING_BEGIN, mGSTRING_CONTENT);
|
||||
|
||||
private static final TokenSet EXPR_END = TokenSet.create(mRCURLY, mIDENT);
|
||||
|
||||
private static final TokenSet AFTER_EXPR_END = TokenSet.create(mGSTRING_END, mGSTRING_CONTENT, mDOLLAR);
|
||||
|
||||
private static final TokenSet STRING_END = TokenSet.create(mSTRING_LITERAL, mGSTRING_LITERAL, mGSTRING_END);
|
||||
|
||||
|
||||
private static boolean handleInString(Editor editor, int caretOffset, DataContext dataContext, EditorActionHandler originalHandler) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(dataContext);
|
||||
if (project == null) return false;
|
||||
@@ -241,6 +276,16 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter {
|
||||
ASTNode node = stringElement.getNode();
|
||||
if (node == null) return false;
|
||||
|
||||
// For expression injection in GString like "abc ${}<caret> abc"
|
||||
if (!INNER_STRING_TOKENS.contains(node.getElementType()) && checkGStringInjection(stringElement)) {
|
||||
stringElement = stringElement.getParent().getParent().getNextSibling();
|
||||
if (stringElement == null) return false;
|
||||
node = stringElement.getNode();
|
||||
if (node == null) return false;
|
||||
}
|
||||
|
||||
boolean isInsertIndent = isInsertIndent(caretOffset, stringElement.getTextRange().getStartOffset(), fileText);
|
||||
|
||||
// For simple String literals like 'abcdef'
|
||||
if (mSTRING_LITERAL == node.getElementType()) {
|
||||
if (GroovyEditorActionUtil.isPlainStringLiteral(node)) {
|
||||
@@ -258,20 +303,13 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter {
|
||||
EditorModificationUtil.insertStringAtCaret(editor, "\n");
|
||||
}
|
||||
else {
|
||||
originalHandler.execute(editor, dataContext);
|
||||
insertLineFeedInString(editor, dataContext, originalHandler, isInsertIndent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// For expression injection in GString like "abc ${}<caret> abc"
|
||||
if (!GroovyEditorActionUtil.GSTRING_TOKENS.contains(node.getElementType()) && checkGStringInnerExpression(stringElement)) {
|
||||
stringElement = stringElement.getParent().getParent().getNextSibling();
|
||||
if (stringElement == null) return false;
|
||||
node = stringElement.getNode();
|
||||
if (node == null) return false;
|
||||
}
|
||||
|
||||
if (GroovyEditorActionUtil.GSTRING_TOKENS.contains(node.getElementType())) {
|
||||
if (GSTRING_TOKENS.contains(node.getElementType()) ||
|
||||
node.getElementType() == mDOLLAR && node.getTreeParent().getTreeParent().getElementType() == GSTRING) {
|
||||
PsiElement parent = stringElement.getParent();
|
||||
if (node.getElementType() == mGSTRING_LITERAL) {
|
||||
parent = stringElement;
|
||||
@@ -296,13 +334,55 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter {
|
||||
}
|
||||
}
|
||||
else {
|
||||
originalHandler.execute(editor, dataContext);
|
||||
insertLineFeedInString(editor, dataContext, originalHandler, isInsertIndent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (REGEX_TOKENS.contains(node.getElementType()) ||
|
||||
node.getElementType() == mDOLLAR && node.getTreeParent().getTreeParent().getElementType() == REGEX) {
|
||||
PsiElement parent = stringElement.getParent();
|
||||
if (node.getElementType() == mREGEX_LITERAL || node.getElementType() == mDOLLAR_SLASH_REGEX_LITERAL) {
|
||||
parent = stringElement;
|
||||
}
|
||||
else {
|
||||
while (parent != null && !(parent instanceof GrLiteral)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
if (parent == null || parent.getLastChild() instanceof PsiErrorElement) return false;
|
||||
PsiElement exprSibling = stringElement.getNextSibling();
|
||||
boolean rightFromDollar = exprSibling instanceof GrExpression && exprSibling.getTextRange().getStartOffset() == caretOffset;
|
||||
if (rightFromDollar) {
|
||||
editor.getCaretModel().moveToOffset(caretOffset - 1);
|
||||
}
|
||||
insertLineFeedInString(editor, dataContext, originalHandler, isInsertIndent);
|
||||
if (rightFromDollar) {
|
||||
editor.getCaretModel().moveCaretRelatively(1, 0, false, false, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void insertLineFeedInString(Editor editor,
|
||||
DataContext dataContext,
|
||||
EditorActionHandler originalHandler,
|
||||
boolean isInsertIndent) {
|
||||
if (isInsertIndent) {
|
||||
originalHandler.execute(editor, dataContext);
|
||||
}
|
||||
else {
|
||||
EditorModificationUtil.insertStringAtCaret(editor, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isInsertIndent(int caret, int stringOffset, String text) {
|
||||
final int i = text.indexOf('\n', stringOffset);
|
||||
return stringOffset < i && i < caret;
|
||||
}
|
||||
|
||||
private static void convertEndToMultiline(int caretOffset, Document document, String fileText) {
|
||||
if (caretOffset < fileText.length() && fileText.charAt(caretOffset) == '\'' ||
|
||||
caretOffset > 0 && fileText.charAt(caretOffset - 1) == '\'') {
|
||||
@@ -339,13 +419,13 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkGStringInnerExpression(PsiElement element) {
|
||||
private static boolean checkGStringInjection(PsiElement element) {
|
||||
if (element != null && (element.getParent() instanceof GrReferenceExpression || element.getParent() instanceof GrClosableBlock)) {
|
||||
final PsiElement parent = element.getParent().getParent();
|
||||
if (!(parent instanceof GrStringInjection)) return false;
|
||||
PsiElement nextSibling = parent.getNextSibling();
|
||||
if (nextSibling == null) return false;
|
||||
return GroovyEditorActionUtil.GSTRING_TOKENS_INNER.contains(nextSibling.getNode().getElementType());
|
||||
return INNER_STRING_TOKENS.contains(nextSibling.getNode().getElementType());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+11
@@ -81,6 +81,7 @@ public class EnterActionTest extends GroovyFormatterTestCase {
|
||||
public void testGstring8() throws Throwable { doTest(); }
|
||||
public void testGstring9() throws Throwable { doTest(); }
|
||||
public void testMultilineIndent() throws Throwable { doTest(); }
|
||||
public void testMultilineIndent2() throws Throwable { doTest(); }
|
||||
public void testSpaces1() throws Throwable { doTest(); }
|
||||
public void testString1() throws Throwable { doTest(); }
|
||||
public void testString2() throws Throwable { doTest(); }
|
||||
@@ -92,6 +93,16 @@ public class EnterActionTest extends GroovyFormatterTestCase {
|
||||
public void testString8() throws Throwable { doTest(); }
|
||||
public void testString9() throws Throwable { doTest(); }
|
||||
public void testString10() throws Throwable { doTest(); }
|
||||
public void testRegex1() {doTest()}
|
||||
public void testRegex2() {doTest()}
|
||||
public void testRegex3() {doTest()}
|
||||
public void testRegex4() {doTest()}
|
||||
public void testRegex5() {doTest()}
|
||||
public void testRegex6() {doTest()}
|
||||
public void testRegex7() {doTest()}
|
||||
public void testRegex8() {doTest()}
|
||||
public void testRegex9() {doTest()}
|
||||
public void testRegex10() {doTest()}
|
||||
|
||||
def doTest(String before, String after) {
|
||||
myFixture.configureByText("a.groovy", before)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
def foo() {
|
||||
'''<caret>
|
||||
class Foo {
|
||||
void foo() {}
|
||||
}
|
||||
'''
|
||||
}
|
||||
-----
|
||||
def foo() {
|
||||
'''
|
||||
<caret>
|
||||
class Foo {
|
||||
void foo() {}
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cl {
|
||||
/a<caret>bc/
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
/a
|
||||
<caret>bc/
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cl {
|
||||
$/a$<caret>{}bc/$
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
$/a
|
||||
$<caret>{}bc/$
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
cl {
|
||||
/a
|
||||
b<caret>bc/
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
/a
|
||||
b
|
||||
<caret>bc/
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cl {
|
||||
/a${}<caret>bc/
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
/a${}
|
||||
<caret>bc/
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cl {
|
||||
/a<caret>${}bc/
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
/a
|
||||
<caret>${}bc/
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cl {
|
||||
/a$<caret>{}bc/
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
/a
|
||||
$<caret>{}bc/
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cl {
|
||||
$/a<caret>bc/$
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
$/a
|
||||
<caret>bc/$
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
cl {
|
||||
$/a
|
||||
b<caret>bc/$
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
$/a
|
||||
b
|
||||
<caret>bc/$
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cl {
|
||||
$/a${}<caret>bc/$
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
$/a${}
|
||||
<caret>bc/$
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
cl {
|
||||
$/a<caret>${}bc/$
|
||||
}
|
||||
-----
|
||||
cl {
|
||||
$/a
|
||||
<caret>${}bc/$
|
||||
}
|
||||
Reference in New Issue
Block a user