mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
Fix and tests for template indentation adjustment
This commit is contained in:
parent
9d9607ee05
commit
e10f67a94a
3 changed files with 209 additions and 70 deletions
|
@ -0,0 +1,145 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2008 Wind River Systems, Inc. and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Anton Leherbauer (Wind River Systems) - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.ui.tests.text;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
import org.eclipse.jface.text.Document;
|
||||
import org.eclipse.jface.text.templates.DocumentTemplateContext;
|
||||
import org.eclipse.jface.text.templates.GlobalTemplateVariables;
|
||||
import org.eclipse.jface.text.templates.Template;
|
||||
import org.eclipse.jface.text.templates.TemplateBuffer;
|
||||
import org.eclipse.jface.text.templates.TemplateContext;
|
||||
import org.eclipse.jface.text.templates.TemplateContextType;
|
||||
|
||||
import org.eclipse.cdt.core.CCorePlugin;
|
||||
import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants;
|
||||
import org.eclipse.cdt.ui.tests.BaseUITestCase;
|
||||
|
||||
import org.eclipse.cdt.internal.corext.template.c.CFormatter;
|
||||
|
||||
/**
|
||||
* Tests for the template formatter (CFormatter).
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class TemplateFormatterTest extends BaseUITestCase {
|
||||
|
||||
private static class TestTemplateContextType extends TemplateContextType {
|
||||
TestTemplateContextType() {
|
||||
super("test");
|
||||
addResolver(new GlobalTemplateVariables.Cursor());
|
||||
addResolver(new GlobalTemplateVariables.WordSelection());
|
||||
addResolver(new GlobalTemplateVariables.LineSelection());
|
||||
addResolver(new GlobalTemplateVariables.Dollar());
|
||||
addResolver(new GlobalTemplateVariables.Date());
|
||||
addResolver(new GlobalTemplateVariables.Year());
|
||||
addResolver(new GlobalTemplateVariables.Time());
|
||||
addResolver(new GlobalTemplateVariables.User());
|
||||
}
|
||||
}
|
||||
|
||||
public static TestSuite suite() {
|
||||
return suite(TemplateFormatterTest.class, "_");
|
||||
}
|
||||
|
||||
private TemplateContextType fTemplateContextType;
|
||||
private String fSelection;
|
||||
private HashMap fDefaultOptions;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
fTemplateContextType= new TestTemplateContextType();
|
||||
fSelection= "while (true) {\n\tdoSomething();\n}";
|
||||
fDefaultOptions= CCorePlugin.getDefaultOptions();
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception {
|
||||
CCorePlugin.setOptions(fDefaultOptions);
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
private void setOption(String key, String value) {
|
||||
HashMap options= new HashMap(1);
|
||||
options.put(key, value);
|
||||
CCorePlugin.setOptions(options);
|
||||
}
|
||||
|
||||
protected void assertFormatterResult() throws Exception {
|
||||
StringBuffer[] contents= getContentsForTest(2);
|
||||
String before= contents[0].toString().replaceAll("\\r\\n", "\n");
|
||||
String expected= contents[1].toString();
|
||||
final Document document = new Document(before);
|
||||
TemplateContext context= new DocumentTemplateContext(fTemplateContextType, document, 0, document.getLength());
|
||||
context.setVariable(GlobalTemplateVariables.SELECTION, fSelection);
|
||||
Template template= new Template("test", "descr", fTemplateContextType.getId(), before, false);
|
||||
TemplateBuffer buffer= context.evaluate(template);
|
||||
CFormatter formatter= new CFormatter("\n", 0, false, null);
|
||||
formatter.format(buffer, context);
|
||||
assertEquals(expected, buffer.getString());
|
||||
}
|
||||
|
||||
|
||||
//for(int var=0; var<max; var++) {
|
||||
// ${cursor}
|
||||
//}
|
||||
|
||||
//for(int var=0; var<max; var++) {
|
||||
//
|
||||
//}
|
||||
public void testForLoopTemplateDefault() throws Exception {
|
||||
assertFormatterResult();
|
||||
}
|
||||
|
||||
//for(int var=0; var<max; var++) {
|
||||
// ${cursor}
|
||||
//}
|
||||
|
||||
//for(int var=0; var<max; var++) {
|
||||
//
|
||||
//}
|
||||
public void testForLoopTemplateMixedIndent() throws Exception {
|
||||
setOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, DefaultCodeFormatterConstants.MIXED);
|
||||
setOption(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, Integer.toString(8));
|
||||
assertFormatterResult();
|
||||
}
|
||||
|
||||
//for(int var=0; var<max; var++) {
|
||||
// ${line_selection}
|
||||
//}
|
||||
|
||||
//for(int var=0; var<max; var++) {
|
||||
// while (true) {
|
||||
// doSomething();
|
||||
// }
|
||||
//}
|
||||
public void testSourroundWithForLoopTemplateDefault() throws Exception {
|
||||
assertFormatterResult();
|
||||
}
|
||||
|
||||
//for(int var=0; var<max; var++) {
|
||||
// ${line_selection}
|
||||
//}
|
||||
|
||||
//for(int var=0; var<max; var++) {
|
||||
// while (true) {
|
||||
// doSomething();
|
||||
// }
|
||||
//}
|
||||
public void testSourroundWithForLoopTemplateMixedIndent() throws Exception {
|
||||
setOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, DefaultCodeFormatterConstants.MIXED);
|
||||
setOption(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, Integer.toString(8));
|
||||
fSelection= "while (true) {\n doSomething();\n}";
|
||||
assertFormatterResult();
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ public class TextTestSuite extends TestSuite {
|
|||
addTest(ShiftActionTest.suite());
|
||||
addTest(CodeFormatterTest.suite());
|
||||
addTest(CIndenterTest.suite());
|
||||
addTest(TemplateFormatterTest.suite());
|
||||
|
||||
// Break iterator tests.
|
||||
addTest(CBreakIteratorTest.suite());
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2005, 2007 IBM Corporation and others.
|
||||
* Copyright (c) 2005, 2008 IBM Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
|
@ -14,7 +14,6 @@
|
|||
package org.eclipse.cdt.internal.corext.template.c;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -26,10 +25,8 @@ import org.eclipse.jface.text.BadPositionCategoryException;
|
|||
import org.eclipse.jface.text.Document;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.jface.text.Position;
|
||||
import org.eclipse.jface.text.TypedPosition;
|
||||
import org.eclipse.jface.text.rules.FastPartitioner;
|
||||
import org.eclipse.jface.text.source.LineRange;
|
||||
import org.eclipse.jface.text.templates.DocumentTemplateContext;
|
||||
import org.eclipse.jface.text.templates.GlobalTemplateVariables;
|
||||
import org.eclipse.jface.text.templates.TemplateBuffer;
|
||||
|
@ -98,7 +95,6 @@ public class CFormatter {
|
|||
|
||||
internalFormat(document, context, buffer);
|
||||
convertLineDelimiters(document);
|
||||
convertTabs(document);
|
||||
if (isReplacedAreaEmpty(context))
|
||||
trimStart(document);
|
||||
|
||||
|
@ -138,50 +134,6 @@ public class CFormatter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts tabs into spaces if such conversion is required according
|
||||
* to the current code style.
|
||||
*
|
||||
* @param document the document to process.
|
||||
* @throws BadLocationException
|
||||
*/
|
||||
private void convertTabs(IDocument document) throws BadLocationException {
|
||||
int lines= document.getNumberOfLines();
|
||||
if (lines == 0)
|
||||
return;
|
||||
|
||||
String option;
|
||||
if (fProject == null)
|
||||
option= CCorePlugin.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR);
|
||||
else
|
||||
option= fProject.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, true);
|
||||
|
||||
if (!CCorePlugin.SPACE.equals(option))
|
||||
return;
|
||||
|
||||
int tabWidth= CodeFormatterUtil.getTabWidth(fProject);
|
||||
char[] spaces= null;
|
||||
for (int line= 0; line < lines; line++) {
|
||||
IRegion region= document.getLineInformation(line);
|
||||
int offset= region.getOffset();
|
||||
int length = region.getLength();
|
||||
for (int i= 0; i < length; i++) {
|
||||
char c= document.getChar(offset + i);
|
||||
if (c == '\t') {
|
||||
int numSpaces= tabWidth - i % tabWidth;
|
||||
if (spaces == null) {
|
||||
spaces= new char[tabWidth];
|
||||
Arrays.fill(spaces, ' ');
|
||||
}
|
||||
document.replace(offset + i, 1, String.valueOf(spaces, 0, numSpaces));
|
||||
numSpaces--;
|
||||
i += numSpaces;
|
||||
length += numSpaces;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void trimStart(IDocument document) throws BadLocationException {
|
||||
trimAtOffset(0, document);
|
||||
}
|
||||
|
@ -229,10 +181,46 @@ public class CFormatter {
|
|||
}
|
||||
|
||||
private void indent(IDocument document, TemplateBuffer buffer) throws BadLocationException, MalformedTreeException {
|
||||
String indent = CodeFormatterUtil.createIndentString(fInitialIndentLevel, fProject);
|
||||
prefixSelectionLines(document, buffer);
|
||||
int lineCount= document.getNumberOfLines();
|
||||
IndentUtil.indentLines(document, new LineRange(0, lineCount), indent);
|
||||
adjustIndentation(document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert leading tabs to correct indentation and add initial indentation level.
|
||||
*
|
||||
* @param document
|
||||
* @throws BadLocationException
|
||||
*/
|
||||
private void adjustIndentation(IDocument document) throws BadLocationException {
|
||||
int lines= document.getNumberOfLines();
|
||||
if (lines == 0)
|
||||
return;
|
||||
|
||||
String option;
|
||||
if (fProject == null)
|
||||
option= CCorePlugin.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR);
|
||||
else
|
||||
option= fProject.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, true);
|
||||
|
||||
boolean useSpaces= CCorePlugin.SPACE.equals(option);
|
||||
int indentWidth= CodeFormatterUtil.getIndentWidth(fProject);
|
||||
int tabWidth= CodeFormatterUtil.getTabWidth(fProject);
|
||||
|
||||
for (int line= 0; line < lines; ++line) {
|
||||
IRegion lineRegion= document.getLineInformation(line);
|
||||
String indent= IndentUtil.getCurrentIndent(document, line, false);
|
||||
int tabs= 0;
|
||||
for (int i = 0; i < indent.length(); i++) {
|
||||
if (indent.charAt(i) == '\t') {
|
||||
tabs++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int totalIndentWidth= (fInitialIndentLevel + tabs) * indentWidth + IndentUtil.computeVisualLength(indent.substring(tabs), tabWidth);
|
||||
String newIndent= IndentUtil.changePrefix("", totalIndentWidth, tabWidth, useSpaces); //$NON-NLS-1$
|
||||
document.replace(lineRegion.getOffset(), indent.length(), newIndent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -259,13 +247,18 @@ public class CFormatter {
|
|||
String prefix= document.get(startOffset, offset - startOffset);
|
||||
String shift= trimAtOffset(offset, document);
|
||||
delta -= shift.length();
|
||||
if (shift.length() > 0 && startLine < endLine) {
|
||||
if (startLine < endLine) {
|
||||
int shiftWidth= IndentUtil.computeVisualLength(shift, tabWidth);
|
||||
for (int line= startLine + 1; line <= endLine; ++line) {
|
||||
delta -= IndentUtil.cutIndent(document, line, shiftWidth, tabWidth);
|
||||
String currentIndent= IndentUtil.getCurrentIndent(document, line, false);
|
||||
int indentWidth= IndentUtil.computeVisualLength(currentIndent, tabWidth);
|
||||
int newIndentWidth= Math.max(0, indentWidth - shiftWidth);
|
||||
// replace current indent with prefix + spaces
|
||||
String newIndent= IndentUtil.changePrefix(prefix, prefix.length() + newIndentWidth, 1, true);
|
||||
int lineOffset= document.getLineOffset(line);
|
||||
document.replace(lineOffset, 0, prefix);
|
||||
delta += prefix.length();
|
||||
document.replace(lineOffset, currentIndent.length(), newIndent);
|
||||
delta -= currentIndent.length();
|
||||
delta += newIndent.length();
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException exc) {
|
||||
|
@ -286,7 +279,7 @@ public class CFormatter {
|
|||
private static final String CATEGORY= "__template_variables"; //$NON-NLS-1$
|
||||
private Document fDocument;
|
||||
private final TemplateBuffer fBuffer;
|
||||
private List fPositions;
|
||||
private List<TypedPosition> fPositions;
|
||||
|
||||
/**
|
||||
* Creates a new tracker.
|
||||
|
@ -361,11 +354,11 @@ public class CFormatter {
|
|||
return fBuffer;
|
||||
}
|
||||
|
||||
private List createRangeMarkers(TemplateVariable[] variables, IDocument document) throws MalformedTreeException, BadLocationException {
|
||||
Map markerToOriginal= new HashMap();
|
||||
private List<TypedPosition> createRangeMarkers(TemplateVariable[] variables, IDocument document) throws MalformedTreeException, BadLocationException {
|
||||
Map<ReplaceEdit, String> markerToOriginal= new HashMap<ReplaceEdit, String>();
|
||||
|
||||
MultiTextEdit root= new MultiTextEdit(0, document.getLength());
|
||||
List edits= new ArrayList();
|
||||
List<TextEdit> edits= new ArrayList<TextEdit>();
|
||||
boolean hasModifications= false;
|
||||
for (int i= 0; i != variables.length; i++) {
|
||||
final TemplateVariable variable= variables[i];
|
||||
|
@ -396,12 +389,12 @@ public class CFormatter {
|
|||
root.apply(document, TextEdit.UPDATE_REGIONS);
|
||||
}
|
||||
|
||||
List positions= new ArrayList();
|
||||
for (Iterator it= edits.iterator(); it.hasNext();) {
|
||||
TextEdit edit= (TextEdit) it.next();
|
||||
List<TypedPosition> positions= new ArrayList<TypedPosition>();
|
||||
for (Iterator<TextEdit> it= edits.iterator(); it.hasNext();) {
|
||||
TextEdit edit= it.next();
|
||||
try {
|
||||
// abuse TypedPosition to piggy back the original contents of the position
|
||||
final TypedPosition pos= new TypedPosition(edit.getOffset(), edit.getLength(), (String) markerToOriginal.get(edit));
|
||||
final TypedPosition pos= new TypedPosition(edit.getOffset(), edit.getLength(), markerToOriginal.get(edit));
|
||||
document.addPosition(CATEGORY, pos);
|
||||
positions.add(pos);
|
||||
} catch (BadPositionCategoryException x) {
|
||||
|
@ -417,11 +410,11 @@ public class CFormatter {
|
|||
return length == 0 || value.trim().length() == 0;
|
||||
}
|
||||
|
||||
private void removeRangeMarkers(List positions, IDocument document, TemplateVariable[] variables) throws MalformedTreeException, BadLocationException, BadPositionCategoryException {
|
||||
private void removeRangeMarkers(List<TypedPosition> positions, IDocument document, TemplateVariable[] variables) throws MalformedTreeException, BadLocationException, BadPositionCategoryException {
|
||||
|
||||
// revert previous changes
|
||||
for (Iterator it= positions.iterator(); it.hasNext();) {
|
||||
TypedPosition position= (TypedPosition) it.next();
|
||||
for (Iterator<TypedPosition> it= positions.iterator(); it.hasNext();) {
|
||||
TypedPosition position= it.next();
|
||||
// remove and re-add in order to not confuse ExclusivePositionUpdater
|
||||
document.removePosition(CATEGORY, position);
|
||||
final String original= position.getType();
|
||||
|
@ -432,13 +425,13 @@ public class CFormatter {
|
|||
document.addPosition(position);
|
||||
}
|
||||
|
||||
Iterator it= positions.iterator();
|
||||
Iterator<TypedPosition> it= positions.iterator();
|
||||
for (int i= 0; i != variables.length; i++) {
|
||||
TemplateVariable variable= variables[i];
|
||||
|
||||
int[] offsets= new int[variable.getOffsets().length];
|
||||
for (int j= 0; j != offsets.length; j++)
|
||||
offsets[j]= ((Position) it.next()).getOffset();
|
||||
offsets[j]= it.next().getOffset();
|
||||
|
||||
variable.setOffsets(offsets);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue