1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-07 09:46:02 +02:00

Bug 323971 - [C++0x] Editor support for raw string literals

This commit is contained in:
Anton Leherbauer 2010-09-01 14:59:43 +00:00
parent ed2d483f12
commit 53b2b45d96
8 changed files with 443 additions and 121 deletions

View file

@ -19,6 +19,7 @@ import java.util.List;
import java.util.Stack; import java.util.Stack;
import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException; import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException;
import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration; import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator; import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator;
@ -95,7 +96,7 @@ import org.eclipse.cdt.core.dom.ast.c.ICASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.c.ICASTDesignatedInitializer; import org.eclipse.cdt.core.dom.ast.c.ICASTDesignatedInitializer;
import org.eclipse.cdt.core.dom.ast.c.ICASTDesignator; import org.eclipse.cdt.core.dom.ast.c.ICASTDesignator;
import org.eclipse.cdt.core.dom.ast.c.ICASTTypeIdInitializerExpression; import org.eclipse.cdt.core.dom.ast.c.ICASTTypeIdInitializerExpression;
import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor; import org.eclipse.cdt.core.dom.ast.c.ICASTVisitor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCastExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCastExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
@ -128,6 +129,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisitor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTWhileStatement; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTWhileStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier;
import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
@ -152,7 +154,7 @@ import org.eclipse.text.edits.TextEdit;
* *
* @since 4.0 * @since 4.0
*/ */
public class CodeFormatterVisitor extends CPPASTVisitor { public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor, ICASTVisitor {
private static boolean DEBUG = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.core/debug/formatter")); //$NON-NLS-1$ //$NON-NLS-2$ private static boolean DEBUG = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.core/debug/formatter")); //$NON-NLS-1$ //$NON-NLS-2$
@ -2201,11 +2203,11 @@ public class CodeFormatterVisitor extends CPPASTVisitor {
final int line= scribe.line; final int line= scribe.line;
boolean indented= false; boolean indented= false;
try { try {
int[] stringLiterals = { Token.tSTRING, Token.tLSTRING }; int[] stringLiterals = { Token.tSTRING, Token.tLSTRING, Token.tRSTRING };
while (true) { while (true) {
scribe.printNextToken(stringLiterals, needSpace); scribe.printNextToken(stringLiterals, needSpace);
token= peekNextToken(); token= peekNextToken();
if (token != Token.tSTRING && token != Token.tLSTRING) { if (token != Token.tSTRING && token != Token.tLSTRING && token != Token.tRSTRING) {
break; break;
} }
scribe.printCommentPreservingNewLines(); scribe.printCommentPreservingNewLines();

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2004, 2009 IBM Corporation and others. * Copyright (c) 2004, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -243,10 +243,14 @@ public class SimpleScanner {
} while ((c == ' ') || (c == '\r') || (c == '\t') || (c == '\n')); } while ((c == ' ') || (c == '\r') || (c == '\t') || (c == '\n'));
ungetChar(c); ungetChar(c);
return newToken(Token.tWHITESPACE); return newToken(Token.tWHITESPACE);
} else if (c == '"' || (c == 'L' && !madeMistake)) {
boolean wideString = false; } else if (c == '"') {
if (c == 'L') {
matchStringLiteral();
return newToken(Token.tSTRING);
} else if (c == 'L' && !madeMistake) {
int oldChar = c; int oldChar = c;
c = getChar(); c = getChar();
if (c != '"') { if (c != '"') {
@ -255,14 +259,25 @@ public class SimpleScanner {
c = oldChar; c = oldChar;
madeMistake = true; madeMistake = true;
continue; continue;
} else {
wideString = true;
}
} }
matchStringLiteral(); matchStringLiteral();
int type = wideString ? Token.tLSTRING : Token.tSTRING; return newToken(Token.tLSTRING);
return newToken(type);
} else if (c == 'R' && !madeMistake) {
int oldChar = c;
c = getChar();
if (c != '"') {
// we have made a mistake
ungetChar(c);
c = oldChar;
madeMistake = true;
continue;
}
matchRawStringLiteral();
return newToken(Token.tRSTRING);
} else if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || (c == '_') || (c > 255 && Character.isUnicodeIdentifierStart(c))) { } else if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || (c == '_') || (c > 255 && Character.isUnicodeIdentifierStart(c))) {
@ -669,6 +684,40 @@ public class SimpleScanner {
} }
} }
private void matchRawStringLiteral() {
// raw-string R"<delim-opt>(string)<delim-opt>";
int c = getChar(false);
StringBuilder delim = new StringBuilder(12);
while (c != '(') {
if (c == EOFCHAR) {
return;
}
delim.append((char) c);
c = getChar(false);
}
int delimLen = delim.length();
c = getChar(false);
LOOP:
for (;;) {
if (c == EOFCHAR)
break;
if (c == ')') {
c = getChar(false);
int idx = 0;
while (idx < delimLen) {
if (c != delim.charAt(idx)) {
continue LOOP;
}
++idx;
c = getChar(false);
}
if (c == '"')
break;
}
c = getChar(false);
}
}
/** /**
* Matches a preprocesser directive. * Matches a preprocesser directive.
* *

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2004, 2008 IBM Corporation and others. * Copyright (c) 2004, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -487,6 +487,7 @@ public class Token {
static public final int tFLOATINGPT = 130; static public final int tFLOATINGPT = 130;
static public final int tLSTRING = 131; static public final int tLSTRING = 131;
static public final int tCHAR = 132; static public final int tCHAR = 132;
static public final int tRSTRING = 133;
static public final int t_restrict = 136; static public final int t_restrict = 136;
static public final int t_interface = 200; static public final int t_interface = 200;
static public final int t_import = 201; static public final int t_import = 201;

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others. * Copyright (c) 2000, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -1164,4 +1164,242 @@ public class CPartitionerTest extends TestCase {
assertTrue(false); assertTrue(false);
} }
} }
public void testString1() {
try {
fDocument.replace(0, fDocument.getLength(), "\"[string]\"");
ITypedRegion[] result= fDocument.computePartitioning(0, fDocument.getLength());
TypedRegion[] expectation= {
new TypedRegion(0, fDocument.getLength(), ICPartitions.C_STRING)
};
checkPartitioning(expectation, result);
fDocument.replace(0, fDocument.getLength(), "\"string1\" \"string2\"");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 9, ICPartitions.C_STRING),
new TypedRegion(9, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(10, 9, ICPartitions.C_STRING)
};
checkPartitioning(expectation, result);
} catch (BadLocationException x) {
assertTrue(false);
}
}
public void testRawString1() {
try {
fDocument.replace(0, fDocument.getLength(), "R\"(line 1\n/*line 2*/\nline 3\n)\"");
ITypedRegion[] result= fDocument.computePartitioning(0, fDocument.getLength());
TypedRegion[] expectation= {
new TypedRegion(0, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(1, fDocument.getLength() - 1, ICPartitions.C_STRING)
};
checkPartitioning(expectation, result);
fDocument.replace(0, fDocument.getLength(), "R\"()\"//comment");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(1, fDocument.getLength() - 10, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 9, 9, ICPartitions.C_SINGLE_LINE_COMMENT),
};
checkPartitioning(expectation, result);
fDocument.replace(0, fDocument.getLength(), "R\"delimiter(line 1\n()delimitex\nline 3\n)delimiter\"");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(1, fDocument.getLength() - 1, ICPartitions.C_STRING)
};
checkPartitioning(expectation, result);
} catch (BadLocationException x) {
assertTrue(false);
}
}
public void testRawString2() {
try {
fDocument.replace(0, fDocument.getLength(), "/***/R\"(line 1\nline 2\nline 3\n)\"");
ITypedRegion[] result= fDocument.computePartitioning(0, fDocument.getLength());
TypedRegion[] expectation= {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 6, ICPartitions.C_STRING)
};
checkPartitioning(expectation, result);
fDocument.replace(0, fDocument.getLength(), "#define X x\nR\"delimiter(line 1\n()delimitex\nline 3\n)delimiter\"");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 12, ICPartitions.C_PREPROCESSOR),
new TypedRegion(12, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(13, fDocument.getLength() - 13, ICPartitions.C_STRING)
};
checkPartitioning(expectation, result);
} catch (BadLocationException x) {
assertTrue(false);
}
}
public void testRawString3() {
try {
fDocument.replace(0, fDocument.getLength(), "/***/R\"(line 1\nline 2\nline 3\n)\" \"str\"");
ITypedRegion[] result= fDocument.computePartitioning(0, fDocument.getLength());
TypedRegion[] expectation= {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 12, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 6, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
fDocument.replace(0, fDocument.getLength(), "#define X x\nR\"delimiter(line 1\n()delimitex\nline 3\n)del)delimiter\" \"str\"");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 12, ICPartitions.C_PREPROCESSOR),
new TypedRegion(12, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(13, fDocument.getLength() - 19, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 6, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
} catch (BadLocationException x) {
assertTrue(false);
}
}
public void testEditingRawString1() {
try {
fDocument.replace(0, fDocument.getLength(), "/***/R\"(line 1\nline 2\nline 3\n)\" \"str\"");
ITypedRegion[] result= fDocument.computePartitioning(0, fDocument.getLength());
TypedRegion[] expectation= {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 12, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 6, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
// insert line
fDocument.replace(8, 0, "line 0\n");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 12, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 6, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
// delete text
fDocument.replace(12, 8, "");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 12, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 6, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
} catch (BadLocationException x) {
assertTrue(false);
}
}
public void testEditingRawString2() {
try {
fDocument.replace(0, fDocument.getLength(), "/***/R\"(line 1\nline 2\nline 3\n)\" \"str\"");
ITypedRegion[] result= fDocument.computePartitioning(0, fDocument.getLength());
TypedRegion[] expectation= {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 12, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 6, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
// insert opening delimiter
fDocument.replace(7, 0, "***");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 6, ICPartitions.C_STRING)
};
checkPartitioning(expectation, result);
// insert closing delimiter
fDocument.replace(fDocument.getLength() - 7, 0, "***");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 12, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 6, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
// invalidate closing delimiter
fDocument.replace(fDocument.getLength() - 7, 1, "");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 6, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
} catch (BadLocationException x) {
assertTrue(false);
}
}
public void testEditingRawString3() {
try {
fDocument.replace(0, fDocument.getLength(), "/***/R\"(line 1\nline 2\nline 3\n)\" \"str\"");
ITypedRegion[] result= fDocument.computePartitioning(0, fDocument.getLength());
TypedRegion[] expectation= {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 12, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 6, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
// insert text after closing quote
fDocument.replace(fDocument.getLength() - 6, 0, " ");
result= fDocument.computePartitioning(0, fDocument.getLength());
expectation= new TypedRegion[] {
new TypedRegion(0, 5, ICPartitions.C_MULTI_LINE_COMMENT),
new TypedRegion(5, 1, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(6, fDocument.getLength() - 13, ICPartitions.C_STRING),
new TypedRegion(fDocument.getLength() - 7, 2, IDocument.DEFAULT_CONTENT_TYPE),
new TypedRegion(fDocument.getLength() - 5, 5, ICPartitions.C_STRING),
};
checkPartitioning(expectation, result);
} catch (BadLocationException x) {
assertTrue(false);
}
}
} }

View file

@ -40,9 +40,10 @@ public class PartitionTokenScannerTest extends TestCase {
super(name); super(name);
} }
@Override
protected void setUp() { protected void setUp() {
fReference= new CPartitionScanner(); fReference= new CPartitionScanner();
fTestee= new FastCPartitionScanner(true, null); fTestee= new FastCPartitionScanner();
} }
// read sample C file // read sample C file
@ -51,7 +52,7 @@ public class PartitionTokenScannerTest extends TestCase {
InputStream stream= getClass().getResourceAsStream(name); InputStream stream= getClass().getResourceAsStream(name);
BufferedReader reader= new BufferedReader(new InputStreamReader(stream)); BufferedReader reader= new BufferedReader(new InputStreamReader(stream));
StringBuffer buffer= new StringBuffer(); StringBuilder buffer= new StringBuilder();
String line= reader.readLine(); String line= reader.readLine();
while (line != null) { while (line != null) {
buffer.append(line); buffer.append(line);
@ -67,7 +68,7 @@ public class PartitionTokenScannerTest extends TestCase {
private static IDocument getRandomDocument(int size) { private static IDocument getRandomDocument(int size) {
final char[] characters= {'/', '*', '\'', '"', '\r', '\n', '\\'}; final char[] characters= {'/', '*', '\'', '"', '\r', '\n', '\\'};
final StringBuffer buffer= new StringBuffer(); final StringBuilder buffer= new StringBuilder();
for (int i= 0; i < size; i++) { for (int i= 0; i < size; i++) {
final int randomIndex= (int) (Math.random() * characters.length); final int randomIndex= (int) (Math.random() * characters.length);
@ -142,7 +143,7 @@ public class PartitionTokenScannerTest extends TestCase {
} }
private void testConformance(final IDocument document) { private void testConformance(final IDocument document) {
final StringBuffer message= new StringBuffer(); final StringBuilder message= new StringBuilder();
fReference.setRange(document, 0, document.getLength()); fReference.setRange(document, 0, document.getLength());
fTestee.setRange(document, 0, document.getLength()); fTestee.setRange(document, 0, document.getLength());
@ -201,7 +202,7 @@ public class PartitionTokenScannerTest extends TestCase {
} }
private static String extractString(IDocument document, int offset) { private static String extractString(IDocument document, int offset) {
final StringBuffer buffer= new StringBuffer(); final StringBuilder buffer= new StringBuilder();
try { try {
IRegion region= document.getLineInformationOfOffset(offset); IRegion region= document.getLineInformationOfOffset(offset);
@ -229,7 +230,7 @@ public class PartitionTokenScannerTest extends TestCase {
* Escapes CR, LF and TAB in a string. * Escapes CR, LF and TAB in a string.
*/ */
private static String escape(String string) { private static String escape(String string) {
final StringBuffer buffer= new StringBuffer(); final StringBuilder buffer= new StringBuilder();
final int length= string.length(); final int length= string.length();
for (int i= 0; i < length; i++) { for (int i= 0; i < length; i++) {

View file

@ -214,6 +214,9 @@ public class IndentAction extends TextEditorAction {
indent= computeCommentIndent(document, line, scanner, startingPartition); indent= computeCommentIndent(document, line, scanner, startingPartition);
} else if (startingPartition.getType().equals(ICPartitions.C_PREPROCESSOR)) { } else if (startingPartition.getType().equals(ICPartitions.C_PREPROCESSOR)) {
indent= computePreprocessorIndent(document, line, startingPartition); indent= computePreprocessorIndent(document, line, startingPartition);
} else if (startingPartition.getType().equals(ICPartitions.C_STRING) && offset > startingPartition.getOffset()) {
// don't indent inside (raw-)string
indent = ""; //$NON-NLS-1$
} else if (!fIsTabAction && startingPartition.getOffset() == offset && startingPartition.getType().equals(ICPartitions.C_SINGLE_LINE_COMMENT)) { } else if (!fIsTabAction && startingPartition.getOffset() == offset && startingPartition.getType().equals(ICPartitions.C_SINGLE_LINE_COMMENT)) {
// line comment starting at position 0 -> indent inside // line comment starting at position 0 -> indent inside
if (indentInsideLineComments()) { if (indentInsideLineComments()) {

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others. * Copyright (c) 2000, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -141,6 +141,9 @@ public class CStringAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy
if (command.offset == offset + length && document.getChar(offset + length - 1) == '\"') if (command.offset == offset + length && document.getChar(offset + length - 1) == '\"')
return; return;
if (offset > 0 && document.getChar(offset - 1) == 'R') // raw string
return;
CHeuristicScanner scanner = new CHeuristicScanner(document); CHeuristicScanner scanner = new CHeuristicScanner(document);
CIndenter indenter = new CIndenter(document, scanner, fProject); CIndenter indenter = new CIndenter(document, scanner, fProject);
StringBuilder indentation = indenter.computeContinuationLineIndentation(offset); StringBuilder indentation = indenter.computeContinuationLineIndentation(offset);

View file

@ -37,11 +37,21 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
private static final int MULTI_LINE_COMMENT= 2; private static final int MULTI_LINE_COMMENT= 2;
private static final int CHARACTER= 3; private static final int CHARACTER= 3;
private static final int STRING= 4; private static final int STRING= 4;
private static final int PREPROCESSOR= 5; private static final int RAW_STRING= 5;
private static final int PREPROCESSOR_MULTI_LINE_COMMENT= 6; private static final int PREPROCESSOR= 6;
private static final int PREPROCESSOR_STRING= 7; private static final int PREPROCESSOR_MULTI_LINE_COMMENT= 7;
private static final int SINGLE_LINE_DOC_COMMENT= 8; private static final int PREPROCESSOR_STRING= 8;
private static final int MULTI_LINE_DOC_COMMENT= 9; private static final int SINGLE_LINE_DOC_COMMENT= 9;
private static final int MULTI_LINE_DOC_COMMENT= 10;
/**
* Sub state for raw strings.
*/
private enum RawStringState {
OPEN_DELIMITER,
CONTENT,
CLOSE_DELIMITER
}
// beginning of prefixes and postfixes // beginning of prefixes and postfixes
private static final int NONE= 0; private static final int NONE= 0;
@ -52,6 +62,7 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
private static final int CARRIAGE_RETURN=5; // postfix for STRING, CHARACTER and SINGLE_LINE_COMMENT private static final int CARRIAGE_RETURN=5; // postfix for STRING, CHARACTER and SINGLE_LINE_COMMENT
private static final int BACKSLASH_CR= 6; // postfix for STRING, CHARACTER and SINGLE_LINE_COMMENT private static final int BACKSLASH_CR= 6; // postfix for STRING, CHARACTER and SINGLE_LINE_COMMENT
private static final int BACKSLASH_BACKSLASH= 7; // postfix for STRING, CHARACTER private static final int BACKSLASH_BACKSLASH= 7; // postfix for STRING, CHARACTER
private static final int RAW_STRING_R= 8; // prefix for RAW_STRING
/** The scanner. */ /** The scanner. */
private final BufferedDocumentScanner fScanner= new BufferedDocumentScanner(1000); // faster implementation private final BufferedDocumentScanner fScanner= new BufferedDocumentScanner(1000); // faster implementation
@ -69,13 +80,9 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
private int fPrefixLength; private int fPrefixLength;
/** Indicate whether current char is first non-whitespace char on the line*/ /** Indicate whether current char is first non-whitespace char on the line*/
private boolean fFirstCharOnLine= true; private boolean fFirstCharOnLine= true;
/** An optional (possibly null) comment owner for detecting documentation-comments **/ /** An optional (possibly null) comment owner for detecting documentation-comments */
private IDocCommentOwner fOwner; private final IDocCommentOwner fOwner;
// emulate CPartitionScanner
private final boolean fEmulate;
private int fCCodeOffset;
private int fCCodeLength;
private IDocument fDocument; private IDocument fDocument;
private final IToken[] fTokens= new IToken[] { private final IToken[] fTokens= new IToken[] {
@ -84,6 +91,7 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
new Token(C_MULTI_LINE_COMMENT), new Token(C_MULTI_LINE_COMMENT),
new Token(C_CHARACTER), new Token(C_CHARACTER),
new Token(C_STRING), new Token(C_STRING),
new Token(C_STRING),
new Token(C_PREPROCESSOR), new Token(C_PREPROCESSOR),
new Token(C_MULTI_LINE_COMMENT), new Token(C_MULTI_LINE_COMMENT),
new Token(C_PREPROCESSOR), new Token(C_PREPROCESSOR),
@ -91,17 +99,14 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
new Token(C_MULTI_LINE_DOC_COMMENT) new Token(C_MULTI_LINE_DOC_COMMENT)
}; };
public FastCPartitionScanner(boolean emulate, IDocCommentOwner owner) { private final StringBuilder fRawStringDelimiter = new StringBuilder(12);
fEmulate= emulate;
fOwner= owner;
}
public FastCPartitionScanner(IDocCommentOwner owner) { public FastCPartitionScanner(IDocCommentOwner owner) {
this(false, owner); fOwner = owner;
} }
public FastCPartitionScanner() { public FastCPartitionScanner() {
this(false, null); this(null);
} }
/* /*
@ -109,52 +114,43 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
*/ */
public IToken nextToken() { public IToken nextToken() {
// emulate CPartitionScanner
if (fEmulate) {
if (fCCodeOffset != -1 && fTokenOffset + fTokenLength != fCCodeOffset + fCCodeLength) {
fTokenOffset += fTokenLength;
return fTokens[CCODE];
} else {
fCCodeOffset= -1;
fCCodeLength= 0;
}
}
fTokenOffset += fTokenLength; fTokenOffset += fTokenLength;
fTokenLength= fPrefixLength; fTokenLength= fPrefixLength;
RawStringState rawStringState = RawStringState.OPEN_DELIMITER;
int rawStringDelimiterIdx = 0;
while (true) { while (true) {
final int ch= fScanner.read(); final int ch= fScanner.read();
final boolean isFirstCharOnLine= fFirstCharOnLine; final boolean isFirstCharOnLine= fFirstCharOnLine;
if (fFirstCharOnLine && ch != ' ' && ch != '\t') { if (isFirstCharOnLine && ch != ' ' && ch != '\t') {
fFirstCharOnLine= false; fFirstCharOnLine= false;
} }
// characters // characters
switch (ch) { switch (ch) {
case ICharacterScanner.EOF: case ICharacterScanner.EOF:
if (fTokenLength > 0) {
fLast= NONE; // ignore last fLast= NONE; // ignore last
if (fTokenLength > 0) {
return preFix(fState, CCODE, NONE, 0); return preFix(fState, CCODE, NONE, 0);
} else { } else {
fLast= NONE;
fPrefixLength= 0; fPrefixLength= 0;
return Token.EOF; return Token.EOF;
} }
case '\r': case '\r':
fFirstCharOnLine= true; fFirstCharOnLine= true;
if (!fEmulate && fLast == BACKSLASH || fLast == BACKSLASH_BACKSLASH) { if (fLast == BACKSLASH || fLast == BACKSLASH_BACKSLASH) {
fLast= BACKSLASH_CR; fLast= BACKSLASH_CR;
fTokenLength++; fTokenLength++;
continue; continue;
} else if (!fEmulate && fLast != CARRIAGE_RETURN) { } else if (fLast != CARRIAGE_RETURN) {
fLast= CARRIAGE_RETURN; fLast= CARRIAGE_RETURN;
fTokenLength++; fTokenLength++;
continue; continue;
} else { } else {
// fEmulate || fLast == CARRIAGE_RETURN // fLast == CARRIAGE_RETURN
switch (fState) { switch (fState) {
case SINGLE_LINE_COMMENT: case SINGLE_LINE_COMMENT:
case CHARACTER: case CHARACTER:
@ -162,16 +158,8 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
case PREPROCESSOR: case PREPROCESSOR:
if (fTokenLength > 0) { if (fTokenLength > 0) {
IToken token= fTokens[fState]; IToken token= fTokens[fState];
// emulate CPartitionScanner
if (fEmulate) {
fTokenLength++;
fLast= NONE;
fPrefixLength= 0;
} else {
fLast= CARRIAGE_RETURN; fLast= CARRIAGE_RETURN;
fPrefixLength= 1; fPrefixLength= 1;
}
fState= CCODE; fState= CCODE;
return token; return token;
@ -222,8 +210,15 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
continue; continue;
} }
case 'R':
if (fState == CCODE) {
fLast = RAW_STRING_R;
}
fTokenLength++;
continue;
default: default:
if (!fEmulate && fLast == CARRIAGE_RETURN) { if (fLast == CARRIAGE_RETURN) {
switch (fState) { switch (fState) {
case SINGLE_LINE_COMMENT: case SINGLE_LINE_COMMENT:
case CHARACTER: case CHARACTER:
@ -349,18 +344,24 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
} }
case '"': case '"':
int newState = STRING;
if (fLast == RAW_STRING_R) {
newState = RAW_STRING;
rawStringState = RawStringState.OPEN_DELIMITER;
fRawStringDelimiter.setLength(0);
}
fLast= NONE; // ignore fLast fLast= NONE; // ignore fLast
if (fTokenLength > 0 ) { if (fTokenLength > 0 ) {
return preFix(CCODE, STRING, NONE, 1); return preFix(CCODE, newState, NONE, 1);
} else { } else {
preFix(CCODE, STRING, NONE, 1); preFix(CCODE, newState, NONE, 1);
fTokenOffset += fTokenLength; fTokenOffset += fTokenLength;
fTokenLength= fPrefixLength; fTokenLength= fPrefixLength;
break; break;
} }
case '#': case '#':
if (!fEmulate && isFirstCharOnLine) { if (isFirstCharOnLine) {
int column= fScanner.getColumn() - 1; int column= fScanner.getColumn() - 1;
fTokenLength -= column; fTokenLength -= column;
if (fTokenLength > 0) { if (fTokenLength > 0) {
@ -372,7 +373,6 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
break; break;
} }
} }
// fallthrough
consume(); consume();
break; break;
default: default:
@ -398,7 +398,7 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
break; break;
} }
} else { } else {
consume(); fTokenLength++;
fLast= SLASH; fLast= SLASH;
} }
break; break;
@ -493,7 +493,6 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
case '\"': case '\"':
if (fLast != BACKSLASH) { if (fLast != BACKSLASH) {
return postFix(STRING); return postFix(STRING);
} else { } else {
consume(); consume();
break; break;
@ -505,12 +504,47 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
} }
break; break;
case RAW_STRING:
switch (rawStringState) {
case OPEN_DELIMITER:
if (ch == '(') {
rawStringState = RawStringState.CONTENT;
} else {
fRawStringDelimiter.append((char) ch);
}
consume();
break;
case CONTENT:
if (ch == ')') {
rawStringState = RawStringState.CLOSE_DELIMITER;
rawStringDelimiterIdx = 0;
}
consume();
break;
case CLOSE_DELIMITER:
if (ch == ')') {
rawStringDelimiterIdx = 0;
} else if (rawStringDelimiterIdx < fRawStringDelimiter.length()) {
if (fRawStringDelimiter.charAt(rawStringDelimiterIdx) != ch) {
rawStringState = RawStringState.CONTENT;
} else {
++rawStringDelimiterIdx;
}
} else if (ch == '"') {
return postFix(RAW_STRING);
} else {
rawStringState = RawStringState.CONTENT;
}
consume();
break;
}
break;
case CHARACTER: case CHARACTER:
switch (ch) { switch (ch) {
case '\'': case '\'':
if (fLast != BACKSLASH) { if (fLast != BACKSLASH) {
return postFix(CHARACTER); return postFix(CHARACTER);
} else { } else {
consume(); consume();
break; break;
@ -537,6 +571,7 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
case BACKSLASH: case BACKSLASH:
case SLASH: case SLASH:
case STAR: case STAR:
case RAW_STRING_R:
return 1; return 1;
case SLASH_STAR: case SLASH_STAR:
@ -566,25 +601,12 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
private final IToken preFix(int state, int newState, int last, int prefixLength) { private final IToken preFix(int state, int newState, int last, int prefixLength) {
// emulate CPartitionScanner
if (fEmulate && state == CCODE && (fTokenLength - getLastLength(fLast) > 0)) {
fTokenLength -= getLastLength(fLast);
fCCodeOffset= fTokenOffset;
fCCodeLength= fTokenLength;
fTokenLength= 1;
fState= newState;
fPrefixLength= prefixLength;
fLast= last;
return fTokens[interceptTokenState(state)];
} else {
fTokenLength -= getLastLength(fLast); fTokenLength -= getLastLength(fLast);
fLast= last; fLast= last;
fPrefixLength= prefixLength; fPrefixLength= prefixLength;
fState= newState; fState= newState;
return fTokens[interceptTokenState(state)]; return fTokens[interceptTokenState(state)];
} }
}
private static int getState(String contentType) { private static int getState(String contentType) {
@ -627,11 +649,26 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
fPrefixLength= offset - partitionOffset; fPrefixLength= offset - partitionOffset;
fLast= NONE; fLast= NONE;
fState= getState(contentType);
if (fState == STRING) {
// raw string is a special case: need to restart from partition offset
try {
if (partitionOffset > 0 && fDocument.getChar(partitionOffset - 1) == 'R') {
fState = RAW_STRING;
int endOffset = offset + length;
offset = partitionOffset + 1;
length = endOffset - offset;
fScanner.setRange(document, offset, length);
fPrefixLength = offset - partitionOffset;
fRawStringDelimiter.setLength(0);
}
} catch (BadLocationException exc) {
// cannot happen
}
}
if (offset == partitionOffset) { if (offset == partitionOffset) {
// restart at beginning of partition // restart at beginning of partition
fState= CCODE; fState= CCODE;
} else {
fState= getState(contentType);
} }
try { try {
@ -640,12 +677,6 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
} catch (BadLocationException exc) { } catch (BadLocationException exc) {
fFirstCharOnLine= true; fFirstCharOnLine= true;
} }
// emulate CPartitionScanner
if (fEmulate) {
fCCodeOffset= -1;
fCCodeLength= 0;
}
} }
/* /*
@ -666,12 +697,6 @@ public final class FastCPartitionScanner implements IPartitionTokenScanner, ICPa
} catch (BadLocationException exc) { } catch (BadLocationException exc) {
fFirstCharOnLine= true; fFirstCharOnLine= true;
} }
// emulate CPartitionScanner
if (fEmulate) {
fCCodeOffset= -1;
fCCodeLength= 0;
}
} }
/* /*