1
0
Fork 0
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:
Anton Leherbauer 2008-03-06 13:51:43 +00:00
parent 9d9607ee05
commit e10f67a94a
3 changed files with 209 additions and 70 deletions

View file

@ -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();
}
}

View file

@ -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());

View file

@ -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);
}
@ -226,13 +178,49 @@ public class CFormatter {
throw new BadLocationException(); // fall back to indenting
edit.apply(doc, TextEdit.UPDATE_REGIONS);
}
}
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);
}