mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 22:52:11 +02:00
Bug 414694 - Forward declarations inserted by Organize Includes have to
honor formatting preferences
This commit is contained in:
parent
f99f12f4ed
commit
73e87bf305
14 changed files with 496 additions and 345 deletions
|
@ -91,7 +91,7 @@ Export-Package: org.eclipse.cdt.core,
|
||||||
org.eclipse.cdt.internal.core.settings.model;x-internal:=true,
|
org.eclipse.cdt.internal.core.settings.model;x-internal:=true,
|
||||||
org.eclipse.cdt.internal.core.util;x-internal:=true,
|
org.eclipse.cdt.internal.core.util;x-internal:=true,
|
||||||
org.eclipse.cdt.internal.errorparsers;x-internal:=true,
|
org.eclipse.cdt.internal.errorparsers;x-internal:=true,
|
||||||
org.eclipse.cdt.internal.formatter;x-internal:=true,
|
org.eclipse.cdt.internal.formatter;x-friends:="org.eclipse.cdt.ui",
|
||||||
org.eclipse.cdt.internal.formatter.align;x-internal:=true,
|
org.eclipse.cdt.internal.formatter.align;x-internal:=true,
|
||||||
org.eclipse.cdt.internal.formatter.scanner;x-friends:="org.eclipse.cdt.ui",
|
org.eclipse.cdt.internal.formatter.scanner;x-friends:="org.eclipse.cdt.ui",
|
||||||
org.eclipse.cdt.utils,
|
org.eclipse.cdt.utils,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2008, 2012 Institute for Software, HSR Hochschule fuer Technik
|
* Copyright (c) 2008, 2013 Institute for Software, HSR Hochschule fuer Technik
|
||||||
* Rapperswil, University of applied sciences and others
|
* Rapperswil, University of applied sciences 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
|
||||||
|
@ -14,15 +14,12 @@
|
||||||
package org.eclipse.cdt.internal.core.dom.rewrite.changegenerator;
|
package org.eclipse.cdt.internal.core.dom.rewrite.changegenerator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import org.eclipse.cdt.core.CCorePlugin;
|
|
||||||
import org.eclipse.cdt.core.ToolFactory;
|
|
||||||
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
|
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
|
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
|
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
|
||||||
|
@ -46,9 +43,6 @@ import org.eclipse.cdt.core.dom.ast.IASTTypeId;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
|
||||||
import org.eclipse.cdt.core.formatter.CodeFormatter;
|
|
||||||
import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants;
|
|
||||||
import org.eclipse.cdt.core.model.ICProject;
|
|
||||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||||
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification;
|
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification;
|
||||||
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind;
|
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind;
|
||||||
|
@ -59,20 +53,14 @@ import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ASTWriter;
|
||||||
import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ContainerNode;
|
import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ContainerNode;
|
||||||
import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ProblemRuntimeException;
|
import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ProblemRuntimeException;
|
||||||
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
|
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
|
||||||
|
import org.eclipse.cdt.internal.formatter.ChangeFormatter;
|
||||||
import org.eclipse.core.resources.IFile;
|
import org.eclipse.core.resources.IFile;
|
||||||
import org.eclipse.core.runtime.Assert;
|
import org.eclipse.core.runtime.Assert;
|
||||||
import org.eclipse.jface.text.BadLocationException;
|
|
||||||
import org.eclipse.jface.text.Document;
|
|
||||||
import org.eclipse.jface.text.IDocument;
|
|
||||||
import org.eclipse.jface.text.IRegion;
|
|
||||||
import org.eclipse.jface.text.Region;
|
|
||||||
import org.eclipse.jface.text.TextUtilities;
|
|
||||||
import org.eclipse.ltk.core.refactoring.Change;
|
import org.eclipse.ltk.core.refactoring.Change;
|
||||||
import org.eclipse.ltk.core.refactoring.CompositeChange;
|
import org.eclipse.ltk.core.refactoring.CompositeChange;
|
||||||
import org.eclipse.ltk.core.refactoring.TextFileChange;
|
import org.eclipse.ltk.core.refactoring.TextFileChange;
|
||||||
import org.eclipse.text.edits.DeleteEdit;
|
import org.eclipse.text.edits.DeleteEdit;
|
||||||
import org.eclipse.text.edits.InsertEdit;
|
import org.eclipse.text.edits.InsertEdit;
|
||||||
import org.eclipse.text.edits.MalformedTreeException;
|
|
||||||
import org.eclipse.text.edits.MultiTextEdit;
|
import org.eclipse.text.edits.MultiTextEdit;
|
||||||
import org.eclipse.text.edits.ReplaceEdit;
|
import org.eclipse.text.edits.ReplaceEdit;
|
||||||
import org.eclipse.text.edits.TextEdit;
|
import org.eclipse.text.edits.TextEdit;
|
||||||
|
@ -125,7 +113,7 @@ public class ChangeGenerator extends ASTVisitor {
|
||||||
IASTTranslationUnit ast = rootNode.getTranslationUnit();
|
IASTTranslationUnit ast = rootNode.getTranslationUnit();
|
||||||
String source = ast.getRawSignature();
|
String source = ast.getRawSignature();
|
||||||
ITranslationUnit tu = ast.getOriginatingTranslationUnit();
|
ITranslationUnit tu = ast.getOriginatingTranslationUnit();
|
||||||
formatChangedCode(source, tu);
|
rootEdit = ChangeFormatter.formatChangedCode(source, tu, rootEdit);
|
||||||
TextFileChange subchange= ASTRewriteAnalyzer.createCTextFileChange((IFile) tu.getResource());
|
TextFileChange subchange= ASTRewriteAnalyzer.createCTextFileChange((IFile) tu.getResource());
|
||||||
subchange.setEdit(rootEdit);
|
subchange.setEdit(rootEdit);
|
||||||
change.add(subchange);
|
change.add(subchange);
|
||||||
|
@ -317,195 +305,6 @@ public class ChangeGenerator extends ASTVisitor {
|
||||||
processedOffset = edit.getExclusiveEnd();
|
processedOffset = edit.getExclusiveEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextEdit clippedEdit(TextEdit edit, IRegion region) {
|
|
||||||
if ((edit.getOffset() < region.getOffset() && edit.getExclusiveEnd() <= region.getOffset()) ||
|
|
||||||
edit.getOffset() >= endOffset(region)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int offset = Math.max(edit.getOffset(), region.getOffset());
|
|
||||||
int length = Math.min(endOffset(edit), endOffset(region)) - offset;
|
|
||||||
if (offset == edit.getOffset() && length == edit.getLength()) {
|
|
||||||
// InsertEdit always satisfies the above condition.
|
|
||||||
return edit;
|
|
||||||
}
|
|
||||||
if (edit instanceof DeleteEdit) {
|
|
||||||
return new DeleteEdit(offset, length);
|
|
||||||
} if (edit instanceof ReplaceEdit) {
|
|
||||||
String replacement = ((ReplaceEdit) edit).getText();
|
|
||||||
int start = Math.max(offset - edit.getOffset(), 0);
|
|
||||||
int end = Math.min(endOffset(region) - offset, replacement.length());
|
|
||||||
if (end <= start) {
|
|
||||||
return new DeleteEdit(offset, length);
|
|
||||||
}
|
|
||||||
return new ReplaceEdit(offset, length, replacement.substring(start, end));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Unexpected edit type: " + edit.getClass().getSimpleName()); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies the C++ code formatter to the code affected by refactoring.
|
|
||||||
*
|
|
||||||
* @param code The code being modified.
|
|
||||||
* @param tu The translation unit containing the code.
|
|
||||||
*/
|
|
||||||
private void formatChangedCode(String code, ITranslationUnit tu) {
|
|
||||||
IDocument document = new Document(code);
|
|
||||||
try {
|
|
||||||
TextEdit edit = rootEdit.copy();
|
|
||||||
// Apply refactoring changes to a temporary document.
|
|
||||||
edit.apply(document, TextEdit.UPDATE_REGIONS);
|
|
||||||
|
|
||||||
// Expand regions affected by the changes to cover complete lines. We calculate two
|
|
||||||
// sets of regions, reflecting the state of the document before and after
|
|
||||||
// the refactoring changes.
|
|
||||||
TextEdit[] appliedEdits = edit.getChildren();
|
|
||||||
TextEdit[] edits = rootEdit.removeChildren();
|
|
||||||
IRegion[] regions = new IRegion[appliedEdits.length];
|
|
||||||
int numRegions = 0;
|
|
||||||
int prevEnd = -1;
|
|
||||||
for (int i = 0; i < appliedEdits.length; i++) {
|
|
||||||
edit = appliedEdits[i];
|
|
||||||
int offset = edit.getOffset();
|
|
||||||
int end = offset + edit.getLength();
|
|
||||||
int newOffset = document.getLineInformationOfOffset(offset).getOffset();
|
|
||||||
edit = edits[i];
|
|
||||||
int originalEnd = edit.getExclusiveEnd();
|
|
||||||
// Expand to the end of the line unless the end of the edit region is at
|
|
||||||
// the beginning of line both, before and after the change.
|
|
||||||
int newEnd = (originalEnd == 0 || code.charAt(originalEnd - 1) == '\n') && end == newOffset ?
|
|
||||||
end : endOffset(document.getLineInformationOfOffset(end));
|
|
||||||
if (newOffset <= prevEnd) {
|
|
||||||
numRegions--;
|
|
||||||
newOffset = regions[numRegions].getOffset();
|
|
||||||
}
|
|
||||||
prevEnd = newEnd;
|
|
||||||
regions[numRegions] = new Region(newOffset, newEnd - newOffset);
|
|
||||||
numRegions++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numRegions < regions.length) {
|
|
||||||
regions = Arrays.copyOf(regions, numRegions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate formatting changes for the regions after the refactoring changes.
|
|
||||||
ICProject project = tu.getCProject();
|
|
||||||
Map<String, Object> options = new HashMap<String, Object>(project.getOptions(true));
|
|
||||||
options.put(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT, tu);
|
|
||||||
// Allow all comments to be indented.
|
|
||||||
options.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_NEVER_INDENT_LINE_COMMENTS_ON_FIRST_COLUMN,
|
|
||||||
DefaultCodeFormatterConstants.FALSE);
|
|
||||||
CodeFormatter formatter = ToolFactory.createCodeFormatter(options);
|
|
||||||
code = document.get();
|
|
||||||
TextEdit[] formatEdits = formatter.format(CodeFormatter.K_TRANSLATION_UNIT, code,
|
|
||||||
regions, TextUtilities.getDefaultLineDelimiter(document));
|
|
||||||
|
|
||||||
TextEdit combinedFormatEdit = new MultiTextEdit();
|
|
||||||
for (TextEdit formatEdit : formatEdits) {
|
|
||||||
combinedFormatEdit = TextEditUtil.merge(combinedFormatEdit, formatEdit);
|
|
||||||
}
|
|
||||||
formatEdits = TextEditUtil.flatten(combinedFormatEdit).removeChildren();
|
|
||||||
|
|
||||||
MultiTextEdit result = new MultiTextEdit();
|
|
||||||
int delta = 0;
|
|
||||||
TextEdit edit1 = null;
|
|
||||||
TextEdit edit2 = null;
|
|
||||||
int i = 0;
|
|
||||||
int j = 0;
|
|
||||||
while (true) {
|
|
||||||
if (edit1 == null && i < edits.length)
|
|
||||||
edit1 = edits[i++];
|
|
||||||
if (edit2 == null && j < formatEdits.length)
|
|
||||||
edit2 = formatEdits[j++];
|
|
||||||
if (edit1 == null) {
|
|
||||||
if (edit2 == null)
|
|
||||||
break;
|
|
||||||
edit2.moveTree(-delta);
|
|
||||||
result.addChild(edit2);
|
|
||||||
edit2 = null;
|
|
||||||
} else if (edit2 == null) {
|
|
||||||
delta += TextEditUtil.delta(edit1);
|
|
||||||
result.addChild(edit1);
|
|
||||||
edit1 = null;
|
|
||||||
} else {
|
|
||||||
if (edit2.getExclusiveEnd() - delta <= edit1.getOffset()) {
|
|
||||||
edit2.moveTree(-delta);
|
|
||||||
result.addChild(edit2);
|
|
||||||
edit2 = null;
|
|
||||||
} else {
|
|
||||||
TextEdit piece = clippedEdit(edit2, new Region(-1, edit1.getOffset() + delta));
|
|
||||||
if (piece != null) {
|
|
||||||
piece.moveTree(-delta);
|
|
||||||
result.addChild(piece);
|
|
||||||
}
|
|
||||||
int d = TextEditUtil.delta(edit1);
|
|
||||||
Region region = new Region(edit1.getOffset() + delta, edit1.getLength() + d);
|
|
||||||
int end = endOffset(region);
|
|
||||||
MultiTextEdit format = new MultiTextEdit();
|
|
||||||
while ((piece = clippedEdit(edit2, region)) != null) {
|
|
||||||
format.addChild(piece);
|
|
||||||
// The warning "The variable edit2 may be null at this location" is bogus.
|
|
||||||
// Make the compiler happy:
|
|
||||||
if (edit2 != null) {
|
|
||||||
if (edit2.getExclusiveEnd() >= end || j >= formatEdits.length) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
edit2 = formatEdits[j++];
|
|
||||||
}
|
|
||||||
if (format.hasChildren()) {
|
|
||||||
format.moveTree(-delta);
|
|
||||||
edit1 = applyEdit(format, edit1);
|
|
||||||
}
|
|
||||||
delta += d;
|
|
||||||
result.addChild(edit1);
|
|
||||||
edit1 = null;
|
|
||||||
|
|
||||||
edit2 = clippedEdit(edit2, new Region(end, Integer.MAX_VALUE - end));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rootEdit = result;
|
|
||||||
} catch (MalformedTreeException e) {
|
|
||||||
CCorePlugin.log(e);
|
|
||||||
} catch (BadLocationException e) {
|
|
||||||
CCorePlugin.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies source edit to the target one and returns the combined edit.
|
|
||||||
*/
|
|
||||||
private TextEdit applyEdit(TextEdit source, TextEdit target)
|
|
||||||
throws MalformedTreeException, BadLocationException {
|
|
||||||
source.moveTree(-target.getOffset());
|
|
||||||
String text;
|
|
||||||
if (target instanceof InsertEdit) {
|
|
||||||
text = ((InsertEdit) target).getText();
|
|
||||||
} else if (target instanceof ReplaceEdit) {
|
|
||||||
text = ((ReplaceEdit) target).getText();
|
|
||||||
} else {
|
|
||||||
text = ""; //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
IDocument document = new Document(text);
|
|
||||||
source.apply(document, TextEdit.NONE);
|
|
||||||
text = document.get();
|
|
||||||
if (target.getLength() == 0) {
|
|
||||||
return new InsertEdit(target.getOffset(), text);
|
|
||||||
} else {
|
|
||||||
return new ReplaceEdit(target.getOffset(), target.getLength(), text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int endOffset(IRegion region) {
|
|
||||||
return region.getOffset() + region.getLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int endOffset(TextEdit edit) {
|
|
||||||
return edit.getOffset() + edit.getLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int endOffset(IASTFileLocation nodeLocation) {
|
private int endOffset(IASTFileLocation nodeLocation) {
|
||||||
return nodeLocation.getNodeOffset() + nodeLocation.getNodeLength();
|
return nodeLocation.getNodeOffset() + nodeLocation.getNodeLength();
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class ASTNodes {
|
||||||
* Returns the offset of the beginning of the next line after the node, or the end-of-file
|
* Returns the offset of the beginning of the next line after the node, or the end-of-file
|
||||||
* offset if there is no line delimiter after the node.
|
* offset if there is no line delimiter after the node.
|
||||||
*/
|
*/
|
||||||
public static int skipToNextLineAfterNode(char[] text, IASTNode node) {
|
public static int skipToNextLineAfterNode(String text, IASTNode node) {
|
||||||
return TextUtil.skipToNextLine(text, getNodeEndOffset(node));
|
return TextUtil.skipToNextLine(text, getNodeEndOffset(node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ public class TextUtil {
|
||||||
* Returns the offset of the beginning of the next line after the given offset,
|
* Returns the offset of the beginning of the next line after the given offset,
|
||||||
* or the end-of-file offset if there is no line delimiter after the given offset.
|
* or the end-of-file offset if there is no line delimiter after the given offset.
|
||||||
*/
|
*/
|
||||||
public static int skipToNextLine(char[] text, int offset) {
|
public static int skipToNextLine(String text, int offset) {
|
||||||
while (offset < text.length) {
|
while (offset < text.length()) {
|
||||||
if (text[offset++] == '\n')
|
if (text.charAt(offset++) == '\n')
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return offset;
|
return offset;
|
||||||
|
@ -32,21 +32,9 @@ public class TextUtil {
|
||||||
/**
|
/**
|
||||||
* Returns the offset of the beginning of the line containing the given offset.
|
* Returns the offset of the beginning of the line containing the given offset.
|
||||||
*/
|
*/
|
||||||
public static int getLineStart(char[] text, int offset) {
|
public static int getLineStart(String text, int offset) {
|
||||||
while (--offset >= 0) {
|
while (--offset >= 0) {
|
||||||
if (text[offset] == '\n')
|
if (text.charAt(offset) == '\n')
|
||||||
break;
|
|
||||||
}
|
|
||||||
return offset + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skips whitespace characters to the left of the given offset without leaving the current line.
|
|
||||||
*/
|
|
||||||
public static int skipWhitespaceToTheLeft(char[] text, int offset) {
|
|
||||||
while (--offset >= 0) {
|
|
||||||
char c = text[offset];
|
|
||||||
if (c == '\n' || !Character.isWhitespace(c))
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
|
@ -56,13 +44,13 @@ public class TextUtil {
|
||||||
* Returns {@code true} the line prior to the line corresponding to the given {@code offset}
|
* Returns {@code true} the line prior to the line corresponding to the given {@code offset}
|
||||||
* does not contain non-whitespace characters.
|
* does not contain non-whitespace characters.
|
||||||
*/
|
*/
|
||||||
public static boolean isPreviousLineBlank(char[] text, int offset) {
|
public static boolean isPreviousLineBlank(String text, int offset) {
|
||||||
while (--offset >= 0) {
|
while (--offset >= 0) {
|
||||||
if (text[offset] == '\n')
|
if (text.charAt(offset) == '\n')
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
while (--offset >= 0) {
|
while (--offset >= 0) {
|
||||||
char c = text[offset];
|
char c = text.charAt(offset);
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
return true;
|
return true;
|
||||||
if (!Character.isWhitespace(c))
|
if (!Character.isWhitespace(c))
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Google, 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:
|
||||||
|
* Sergey Prigogin (Google) - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.internal.formatter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.core.CCorePlugin;
|
||||||
|
import org.eclipse.cdt.core.ToolFactory;
|
||||||
|
import org.eclipse.cdt.core.formatter.CodeFormatter;
|
||||||
|
import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants;
|
||||||
|
import org.eclipse.cdt.core.model.ICProject;
|
||||||
|
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||||
|
import org.eclipse.cdt.internal.core.dom.rewrite.changegenerator.TextEditUtil;
|
||||||
|
import org.eclipse.jface.text.BadLocationException;
|
||||||
|
import org.eclipse.jface.text.Document;
|
||||||
|
import org.eclipse.jface.text.IDocument;
|
||||||
|
import org.eclipse.jface.text.IRegion;
|
||||||
|
import org.eclipse.jface.text.Region;
|
||||||
|
import org.eclipse.jface.text.TextUtilities;
|
||||||
|
import org.eclipse.text.edits.DeleteEdit;
|
||||||
|
import org.eclipse.text.edits.InsertEdit;
|
||||||
|
import org.eclipse.text.edits.MalformedTreeException;
|
||||||
|
import org.eclipse.text.edits.MultiTextEdit;
|
||||||
|
import org.eclipse.text.edits.ReplaceEdit;
|
||||||
|
import org.eclipse.text.edits.TextEdit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the C++ code formatter to the code affected by refactoring.
|
||||||
|
*/
|
||||||
|
public class ChangeFormatter {
|
||||||
|
/**
|
||||||
|
* Applies the C++ code formatter to the code affected by refactoring.
|
||||||
|
*
|
||||||
|
* @param code The code being modified.
|
||||||
|
* @param tu The translation unit containing the code.
|
||||||
|
*/
|
||||||
|
public static MultiTextEdit formatChangedCode(String code, ITranslationUnit tu, MultiTextEdit rootEdit) {
|
||||||
|
IDocument document = new Document(code);
|
||||||
|
try {
|
||||||
|
TextEdit edit = rootEdit.copy();
|
||||||
|
// Apply refactoring changes to a temporary document.
|
||||||
|
edit.apply(document, TextEdit.UPDATE_REGIONS);
|
||||||
|
|
||||||
|
// Expand regions affected by the changes to cover complete lines. We calculate two
|
||||||
|
// sets of regions, reflecting the state of the document before and after
|
||||||
|
// the refactoring changes.
|
||||||
|
TextEdit[] appliedEdits = edit.getChildren();
|
||||||
|
TextEdit[] edits = rootEdit.copy().removeChildren();
|
||||||
|
IRegion[] regions = new IRegion[appliedEdits.length];
|
||||||
|
int numRegions = 0;
|
||||||
|
int prevEnd = -1;
|
||||||
|
for (int i = 0; i < appliedEdits.length; i++) {
|
||||||
|
edit = appliedEdits[i];
|
||||||
|
int offset = edit.getOffset();
|
||||||
|
int end = offset + edit.getLength();
|
||||||
|
int newOffset = document.getLineInformationOfOffset(offset).getOffset();
|
||||||
|
edit = edits[i];
|
||||||
|
int originalEnd = edit.getExclusiveEnd();
|
||||||
|
// Expand to the end of the line unless the end of the edit region is at
|
||||||
|
// the beginning of line both, before and after the change.
|
||||||
|
IRegion lineInfo = document.getLineInformationOfOffset(end);
|
||||||
|
int newEnd = lineInfo.getOffset();
|
||||||
|
newEnd = (originalEnd == 0 || code.charAt(originalEnd - 1) == '\n') && end == newEnd ?
|
||||||
|
end : endOffset(lineInfo);
|
||||||
|
if (newOffset <= prevEnd) {
|
||||||
|
numRegions--;
|
||||||
|
newOffset = regions[numRegions].getOffset();
|
||||||
|
}
|
||||||
|
prevEnd = newEnd;
|
||||||
|
regions[numRegions] = new Region(newOffset, newEnd - newOffset);
|
||||||
|
numRegions++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numRegions < regions.length) {
|
||||||
|
regions = Arrays.copyOf(regions, numRegions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate formatting changes for the regions after the refactoring changes.
|
||||||
|
ICProject project = tu.getCProject();
|
||||||
|
Map<String, Object> options = new HashMap<String, Object>(project.getOptions(true));
|
||||||
|
options.put(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT, tu);
|
||||||
|
// Allow all comments to be indented.
|
||||||
|
options.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_NEVER_INDENT_LINE_COMMENTS_ON_FIRST_COLUMN,
|
||||||
|
DefaultCodeFormatterConstants.FALSE);
|
||||||
|
CodeFormatter formatter = ToolFactory.createCodeFormatter(options);
|
||||||
|
code = document.get();
|
||||||
|
TextEdit[] formatEdits = formatter.format(CodeFormatter.K_TRANSLATION_UNIT, code,
|
||||||
|
regions, TextUtilities.getDefaultLineDelimiter(document));
|
||||||
|
|
||||||
|
TextEdit combinedFormatEdit = new MultiTextEdit();
|
||||||
|
for (TextEdit formatEdit : formatEdits) {
|
||||||
|
combinedFormatEdit = TextEditUtil.merge(combinedFormatEdit, formatEdit);
|
||||||
|
}
|
||||||
|
formatEdits = TextEditUtil.flatten(combinedFormatEdit).removeChildren();
|
||||||
|
|
||||||
|
MultiTextEdit result = new MultiTextEdit();
|
||||||
|
int delta = 0;
|
||||||
|
TextEdit edit1 = null;
|
||||||
|
TextEdit edit2 = null;
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
while (true) {
|
||||||
|
if (edit1 == null && i < edits.length)
|
||||||
|
edit1 = edits[i++];
|
||||||
|
if (edit2 == null && j < formatEdits.length)
|
||||||
|
edit2 = formatEdits[j++];
|
||||||
|
if (edit1 == null) {
|
||||||
|
if (edit2 == null)
|
||||||
|
break;
|
||||||
|
edit2.moveTree(-delta);
|
||||||
|
result.addChild(edit2);
|
||||||
|
edit2 = null;
|
||||||
|
} else if (edit2 == null) {
|
||||||
|
delta += TextEditUtil.delta(edit1);
|
||||||
|
result.addChild(edit1);
|
||||||
|
edit1 = null;
|
||||||
|
} else {
|
||||||
|
if (edit2.getExclusiveEnd() - delta <= edit1.getOffset()) {
|
||||||
|
edit2.moveTree(-delta);
|
||||||
|
result.addChild(edit2);
|
||||||
|
edit2 = null;
|
||||||
|
} else {
|
||||||
|
TextEdit piece = clippedEdit(edit2, new Region(-1, edit1.getOffset() + delta));
|
||||||
|
if (piece != null) {
|
||||||
|
piece.moveTree(-delta);
|
||||||
|
result.addChild(piece);
|
||||||
|
}
|
||||||
|
int d = TextEditUtil.delta(edit1);
|
||||||
|
Region region = new Region(edit1.getOffset() + delta, edit1.getLength() + d);
|
||||||
|
int end = endOffset(region);
|
||||||
|
MultiTextEdit format = new MultiTextEdit();
|
||||||
|
while ((piece = clippedEdit(edit2, region)) != null) {
|
||||||
|
format.addChild(piece);
|
||||||
|
// The warning "The variable edit2 may be null at this location" is bogus.
|
||||||
|
// Make the compiler happy:
|
||||||
|
if (edit2 != null) {
|
||||||
|
if (edit2.getExclusiveEnd() >= end || j >= formatEdits.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
edit2 = formatEdits[j++];
|
||||||
|
}
|
||||||
|
if (format.hasChildren()) {
|
||||||
|
format.moveTree(-delta);
|
||||||
|
edit1 = applyEdit(format, edit1);
|
||||||
|
}
|
||||||
|
delta += d;
|
||||||
|
result.addChild(edit1);
|
||||||
|
edit1 = null;
|
||||||
|
|
||||||
|
edit2 = clippedEdit(edit2, new Region(end, Integer.MAX_VALUE - end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (MalformedTreeException e) {
|
||||||
|
CCorePlugin.log(e);
|
||||||
|
} catch (BadLocationException e) {
|
||||||
|
CCorePlugin.log(e);
|
||||||
|
}
|
||||||
|
return rootEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextEdit clippedEdit(TextEdit edit, IRegion region) {
|
||||||
|
if ((edit.getOffset() < region.getOffset() && edit.getExclusiveEnd() <= region.getOffset()) ||
|
||||||
|
edit.getOffset() >= endOffset(region)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int offset = Math.max(edit.getOffset(), region.getOffset());
|
||||||
|
int length = Math.min(endOffset(edit), endOffset(region)) - offset;
|
||||||
|
if (offset == edit.getOffset() && length == edit.getLength()) {
|
||||||
|
// InsertEdit always satisfies the above condition.
|
||||||
|
return edit;
|
||||||
|
}
|
||||||
|
if (edit instanceof DeleteEdit) {
|
||||||
|
return new DeleteEdit(offset, length);
|
||||||
|
} if (edit instanceof ReplaceEdit) {
|
||||||
|
String replacement = ((ReplaceEdit) edit).getText();
|
||||||
|
int start = Math.max(offset - edit.getOffset(), 0);
|
||||||
|
int end = Math.min(endOffset(region) - offset, replacement.length());
|
||||||
|
if (end <= start) {
|
||||||
|
return new DeleteEdit(offset, length);
|
||||||
|
}
|
||||||
|
return new ReplaceEdit(offset, length, replacement.substring(start, end));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unexpected edit type: " + edit.getClass().getSimpleName()); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies source edit to the target one and returns the combined edit.
|
||||||
|
*/
|
||||||
|
private static TextEdit applyEdit(TextEdit source, TextEdit target)
|
||||||
|
throws MalformedTreeException, BadLocationException {
|
||||||
|
source.moveTree(-target.getOffset());
|
||||||
|
String text;
|
||||||
|
if (target instanceof InsertEdit) {
|
||||||
|
text = ((InsertEdit) target).getText();
|
||||||
|
} else if (target instanceof ReplaceEdit) {
|
||||||
|
text = ((ReplaceEdit) target).getText();
|
||||||
|
} else {
|
||||||
|
text = ""; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
IDocument document = new Document(text);
|
||||||
|
source.apply(document, TextEdit.NONE);
|
||||||
|
text = document.get();
|
||||||
|
if (target.getLength() == 0) {
|
||||||
|
return new InsertEdit(target.getOffset(), text);
|
||||||
|
} else {
|
||||||
|
return new ReplaceEdit(target.getOffset(), target.getLength(), text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int endOffset(TextEdit edit) {
|
||||||
|
return edit.getOffset() + edit.getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int endOffset(IRegion region) {
|
||||||
|
return region.getOffset() + region.getLength();
|
||||||
|
}
|
||||||
|
}
|
|
@ -580,7 +580,7 @@ public class SimpleScanner {
|
||||||
c = getChar();
|
c = getChar();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '/': {
|
case '/': {
|
||||||
matchSinglelineComment();
|
matchSinglelineComment(true);
|
||||||
return newToken(Token.tLINECOMMENT);
|
return newToken(Token.tLINECOMMENT);
|
||||||
}
|
}
|
||||||
case '*': {
|
case '*': {
|
||||||
|
@ -749,7 +749,7 @@ public class SimpleScanner {
|
||||||
ungetChar(c);
|
ungetChar(c);
|
||||||
result= newPreprocessorToken();
|
result= newPreprocessorToken();
|
||||||
} else {
|
} else {
|
||||||
matchSinglelineComment();
|
matchSinglelineComment(false);
|
||||||
result= newToken(Token.tLINECOMMENT);
|
result= newToken(Token.tLINECOMMENT);
|
||||||
}
|
}
|
||||||
fPreprocessorToken= 0;
|
fPreprocessorToken= 0;
|
||||||
|
@ -804,7 +804,7 @@ public class SimpleScanner {
|
||||||
int next = getChar();
|
int next = getChar();
|
||||||
if (next == '/') {
|
if (next == '/') {
|
||||||
// single line comment
|
// single line comment
|
||||||
matchSinglelineComment();
|
matchSinglelineComment(false);
|
||||||
break;
|
break;
|
||||||
} else if (next == '*') {
|
} else if (next == '*') {
|
||||||
// multiline comment
|
// multiline comment
|
||||||
|
@ -828,12 +828,12 @@ public class SimpleScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void matchSinglelineComment() {
|
private void matchSinglelineComment(boolean includeNewline) {
|
||||||
int c = getChar();
|
int c = getChar();
|
||||||
while (c != '\n' && c != EOFCHAR) {
|
while (c != '\n' && c != EOFCHAR) {
|
||||||
c = getChar();
|
c = getChar();
|
||||||
}
|
}
|
||||||
if (c == EOFCHAR) {
|
if (c == EOFCHAR || !includeNewline) {
|
||||||
ungetChar(c);
|
ungetChar(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -852,10 +852,11 @@ public class SimpleScanner {
|
||||||
state = 1;
|
state = 1;
|
||||||
break;
|
break;
|
||||||
case 1 :
|
case 1 :
|
||||||
if (c == '/')
|
if (c == '/') {
|
||||||
state = 2;
|
state = 2;
|
||||||
else if (c != '*')
|
} else if (c != '*') {
|
||||||
state = 0;
|
state = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
c = getChar();
|
c = getChar();
|
||||||
|
|
|
@ -199,7 +199,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
|
||||||
public void testFunctionCallWithPointerParameter_1() throws Exception {
|
public void testFunctionCallWithPointerParameter_1() throws Exception {
|
||||||
getPreferenceStore().setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true);
|
getPreferenceStore().setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true);
|
||||||
assertDefined();
|
assertDefined();
|
||||||
assertDeclared("f", "g");
|
assertDeclared("f", "A", "g");
|
||||||
}
|
}
|
||||||
|
|
||||||
// typedef int A;
|
// typedef int A;
|
||||||
|
@ -210,7 +210,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
|
||||||
// }
|
// }
|
||||||
public void testFunctionCallWithPointerParameter_2() throws Exception {
|
public void testFunctionCallWithPointerParameter_2() throws Exception {
|
||||||
getPreferenceStore().setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true);
|
getPreferenceStore().setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true);
|
||||||
assertDefined();
|
assertDefined("A");
|
||||||
assertDeclared("f");
|
assertDeclared("f");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
|
||||||
public void testFunctionCallWithReferenceParameter() throws Exception {
|
public void testFunctionCallWithReferenceParameter() throws Exception {
|
||||||
getPreferenceStore().setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true);
|
getPreferenceStore().setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true);
|
||||||
assertDefined();
|
assertDefined();
|
||||||
assertDeclared("f", "g");
|
assertDeclared("f", "A", "g");
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct A {
|
// struct A {
|
||||||
|
@ -240,7 +240,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
|
||||||
// A header declaring the function is responsible for defining the parameter type that
|
// A header declaring the function is responsible for defining the parameter type that
|
||||||
// provides constructor that can be used for implicit conversion.
|
// provides constructor that can be used for implicit conversion.
|
||||||
assertDefined();
|
assertDefined();
|
||||||
assertDeclared("f");
|
assertDeclared("f", "A");
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct A {};
|
// struct A {};
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
package org.eclipse.cdt.ui.tests.refactoring.includes;
|
package org.eclipse.cdt.ui.tests.refactoring.includes;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import junit.framework.Test;
|
import junit.framework.Test;
|
||||||
|
|
||||||
|
@ -19,7 +18,6 @@ import org.eclipse.jface.preference.IPreferenceStore;
|
||||||
import org.eclipse.jface.text.Document;
|
import org.eclipse.jface.text.Document;
|
||||||
import org.eclipse.jface.text.IDocument;
|
import org.eclipse.jface.text.IDocument;
|
||||||
import org.eclipse.text.edits.MultiTextEdit;
|
import org.eclipse.text.edits.MultiTextEdit;
|
||||||
import org.eclipse.text.edits.TextEdit;
|
|
||||||
|
|
||||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||||
import org.eclipse.cdt.ui.PreferenceConstants;
|
import org.eclipse.cdt.ui.PreferenceConstants;
|
||||||
|
@ -74,14 +72,9 @@ public class IncludeOrganizerTest extends IncludesTestBase {
|
||||||
private String organizeIncludes(ITranslationUnit tu) throws Exception {
|
private String organizeIncludes(ITranslationUnit tu) throws Exception {
|
||||||
IHeaderChooser headerChooser = new FirstHeaderChooser();
|
IHeaderChooser headerChooser = new FirstHeaderChooser();
|
||||||
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, LINE_DELIMITER, headerChooser);
|
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, LINE_DELIMITER, headerChooser);
|
||||||
List<TextEdit> edits = organizer.organizeIncludes(ast);
|
MultiTextEdit edit = organizer.organizeIncludes(ast);
|
||||||
IDocument document = new Document(new String(tu.getContents()));
|
IDocument document = new Document(new String(tu.getContents()));
|
||||||
if (!edits.isEmpty()) {
|
edit.apply(document);
|
||||||
// Apply text edits.
|
|
||||||
MultiTextEdit edit = new MultiTextEdit();
|
|
||||||
edit.addChildren(edits.toArray(new TextEdit[edits.size()]));
|
|
||||||
edit.apply(document);
|
|
||||||
}
|
|
||||||
return document.get();
|
return document.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,4 +436,49 @@ public class IncludeOrganizerTest extends IncludesTestBase {
|
||||||
public void testSymbolToDeclareIsDefinedInIncludedHeader() throws Exception {
|
public void testSymbolToDeclareIsDefinedInIncludedHeader() throws Exception {
|
||||||
assertExpectedResults();
|
assertExpectedResults();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//h1.h
|
||||||
|
//namespace ns3 {
|
||||||
|
//class C {};
|
||||||
|
//namespace ns2 {
|
||||||
|
//class A {};
|
||||||
|
//class B {};
|
||||||
|
//namespace ns1 {
|
||||||
|
//C* f(const A& a, B* b) { return nullptr; }
|
||||||
|
//} // ns1
|
||||||
|
//} // ns2
|
||||||
|
//} // ns3
|
||||||
|
|
||||||
|
//source.cpp
|
||||||
|
//#include "h1.h"
|
||||||
|
//void test(ns3::ns2::A& a) {
|
||||||
|
// ns3::C* c = ns3::ns2::ns1::f(a, nullptr);
|
||||||
|
//}
|
||||||
|
//====================
|
||||||
|
//namespace ns3 {
|
||||||
|
//class C;
|
||||||
|
//namespace ns2 {
|
||||||
|
//class A;
|
||||||
|
//class B;
|
||||||
|
//} /* namespace ns2 */
|
||||||
|
//} /* namespace ns3 */
|
||||||
|
//namespace ns3 {
|
||||||
|
//namespace ns2 {
|
||||||
|
//namespace ns1 {
|
||||||
|
//C * f(const A &a, B *b);
|
||||||
|
//} /* namespace ns1 */
|
||||||
|
//} /* namespace ns2 */
|
||||||
|
//} /* namespace ns3 */
|
||||||
|
//
|
||||||
|
//void test(ns3::ns2::A& a) {
|
||||||
|
// ns3::C* c = ns3::ns2::ns1::f(a, nullptr);
|
||||||
|
//}
|
||||||
|
public void testForwardDeclarations() throws Exception {
|
||||||
|
// TODO(sprigogin): Move ns1 outside of other namespaces after IncludeOrganizer starts using ASTWriter.
|
||||||
|
IPreferenceStore preferenceStore = getPreferenceStore();
|
||||||
|
preferenceStore.setValue(PreferenceConstants.INCLUDES_UNUSED_STATEMENTS_DISPOSITION,
|
||||||
|
UnusedStatementDisposition.REMOVE.toString());
|
||||||
|
preferenceStore.setValue(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS, true);
|
||||||
|
assertExpectedResults();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -818,6 +818,17 @@ public class CodeFormatterTest extends BaseUITestCase {
|
||||||
assertFormatterResult();
|
assertFormatterResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#include "header.h" // comment
|
||||||
|
//
|
||||||
|
//class C;
|
||||||
|
|
||||||
|
//#include "header.h" // comment
|
||||||
|
//
|
||||||
|
//class C;
|
||||||
|
public void testPreserveBlankLineAfterInclude() throws Exception {
|
||||||
|
assertFormatterResult();
|
||||||
|
}
|
||||||
|
|
||||||
//void f() { throw 42; }
|
//void f() { throw 42; }
|
||||||
|
|
||||||
//void f() {
|
//void f() {
|
||||||
|
|
|
@ -11,9 +11,6 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.internal.ui.editor;
|
package org.eclipse.cdt.internal.ui.editor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IStatus;
|
import org.eclipse.core.runtime.IStatus;
|
||||||
import org.eclipse.core.runtime.Status;
|
import org.eclipse.core.runtime.Status;
|
||||||
|
@ -22,7 +19,6 @@ import org.eclipse.jface.text.BadLocationException;
|
||||||
import org.eclipse.jface.text.IDocument;
|
import org.eclipse.jface.text.IDocument;
|
||||||
import org.eclipse.text.edits.MalformedTreeException;
|
import org.eclipse.text.edits.MalformedTreeException;
|
||||||
import org.eclipse.text.edits.MultiTextEdit;
|
import org.eclipse.text.edits.MultiTextEdit;
|
||||||
import org.eclipse.text.edits.TextEdit;
|
|
||||||
import org.eclipse.text.undo.DocumentUndoManagerRegistry;
|
import org.eclipse.text.undo.DocumentUndoManagerRegistry;
|
||||||
import org.eclipse.text.undo.IDocumentUndoManager;
|
import org.eclipse.text.undo.IDocumentUndoManager;
|
||||||
import org.eclipse.ui.IEditorInput;
|
import org.eclipse.ui.IEditorInput;
|
||||||
|
@ -70,7 +66,7 @@ public class OrganizeIncludesAction extends TextEditorAction {
|
||||||
final IHeaderChooser headerChooser = new InteractiveHeaderChooser(
|
final IHeaderChooser headerChooser = new InteractiveHeaderChooser(
|
||||||
CEditorMessages.OrganizeIncludes_label, editor.getSite().getShell());
|
CEditorMessages.OrganizeIncludes_label, editor.getSite().getShell());
|
||||||
final String lineDelimiter = getLineDelimiter(editor);
|
final String lineDelimiter = getLineDelimiter(editor);
|
||||||
final List<TextEdit> edits = new ArrayList<TextEdit>();
|
final MultiTextEdit[] holder = new MultiTextEdit[1];
|
||||||
SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) {
|
SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) {
|
||||||
@Override
|
@Override
|
||||||
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
|
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
|
||||||
|
@ -79,7 +75,7 @@ public class OrganizeIncludesAction extends TextEditorAction {
|
||||||
try {
|
try {
|
||||||
index.acquireReadLock();
|
index.acquireReadLock();
|
||||||
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, lineDelimiter, headerChooser);
|
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, lineDelimiter, headerChooser);
|
||||||
edits.addAll(organizer.organizeIncludes(ast));
|
holder[0] = organizer.organizeIncludes(ast);
|
||||||
return Status.OK_STATUS;
|
return Status.OK_STATUS;
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
return Status.CANCEL_STATUS;
|
return Status.CANCEL_STATUS;
|
||||||
|
@ -90,10 +86,9 @@ public class OrganizeIncludesAction extends TextEditorAction {
|
||||||
};
|
};
|
||||||
IStatus status = BusyCursorJobRunner.execute(job);
|
IStatus status = BusyCursorJobRunner.execute(job);
|
||||||
if (status.isOK()) {
|
if (status.isOK()) {
|
||||||
if (!edits.isEmpty()) {
|
MultiTextEdit edit = holder[0];
|
||||||
// Apply text edits.
|
if (edit.hasChildren()) {
|
||||||
MultiTextEdit edit = new MultiTextEdit();
|
// Apply the text edit.
|
||||||
edit.addChildren(edits.toArray(new TextEdit[edits.size()]));
|
|
||||||
IEditorInput editorInput = editor.getEditorInput();
|
IEditorInput editorInput = editor.getEditorInput();
|
||||||
IDocument document = editor.getDocumentProvider().getDocument(editorInput);
|
IDocument document = editor.getDocumentProvider().getDocument(editorInput);
|
||||||
IDocumentUndoManager manager= DocumentUndoManagerRegistry.getDocumentUndoManager(document);
|
IDocumentUndoManager manager= DocumentUndoManagerRegistry.getDocumentUndoManager(document);
|
||||||
|
|
|
@ -63,6 +63,7 @@ import org.eclipse.cdt.core.dom.ast.IASTStatement;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
|
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTWhileStatement;
|
import org.eclipse.cdt.core.dom.ast.IASTWhileStatement;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IBasicType;
|
||||||
import org.eclipse.cdt.core.dom.ast.IBinding;
|
import org.eclipse.cdt.core.dom.ast.IBinding;
|
||||||
import org.eclipse.cdt.core.dom.ast.ICompositeType;
|
import org.eclipse.cdt.core.dom.ast.ICompositeType;
|
||||||
import org.eclipse.cdt.core.dom.ast.IEnumeration;
|
import org.eclipse.cdt.core.dom.ast.IEnumeration;
|
||||||
|
@ -162,6 +163,7 @@ public class BindingClassifier {
|
||||||
* comparing the declared parameters with the actual arguments.
|
* comparing the declared parameters with the actual arguments.
|
||||||
*/
|
*/
|
||||||
private void processFunctionParameters(IFunction function, IASTInitializerClause[] arguments) {
|
private void processFunctionParameters(IFunction function, IASTInitializerClause[] arguments) {
|
||||||
|
boolean functionIsDefined = fProcessedDefinedBindings.contains(function);
|
||||||
IParameter[] parameters = function.getParameters();
|
IParameter[] parameters = function.getParameters();
|
||||||
for (int i = 0; i < parameters.length && i < arguments.length; i++) {
|
for (int i = 0; i < parameters.length && i < arguments.length; i++) {
|
||||||
IType parameterType = parameters[i].getType();
|
IType parameterType = parameters[i].getType();
|
||||||
|
@ -172,6 +174,9 @@ public class BindingClassifier {
|
||||||
// A declaration is sufficient if the argument type matches the parameter type.
|
// A declaration is sufficient if the argument type matches the parameter type.
|
||||||
// We don't need to provide a declaration of the parameter type since it is
|
// We don't need to provide a declaration of the parameter type since it is
|
||||||
// a responsibility of the header declaring the function.
|
// a responsibility of the header declaring the function.
|
||||||
|
if (!functionIsDefined) {
|
||||||
|
declareType(parameterType);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +190,8 @@ public class BindingClassifier {
|
||||||
fAst.getDeclarationsInAST(function).length != 0 ||
|
fAst.getDeclarationsInAST(function).length != 0 ||
|
||||||
!hasConvertingConstructor((ICPPClassType) parameterType, argument)) {
|
!hasConvertingConstructor((ICPPClassType) parameterType, argument)) {
|
||||||
defineTypeExceptTypedefOrNonFixedEnum(parameterType);
|
defineTypeExceptTypedefOrNonFixedEnum(parameterType);
|
||||||
|
} else if (!functionIsDefined) {
|
||||||
|
declareType(parameterType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,7 +254,10 @@ public class BindingClassifier {
|
||||||
if (!constructor.isExplicit()) {
|
if (!constructor.isExplicit()) {
|
||||||
ICPPParameter[] parameters = constructor.getParameters();
|
ICPPParameter[] parameters = constructor.getParameters();
|
||||||
if (parameters.length != 0 && CPPFunction.getRequiredArgumentCount(parameters) <= 1) {
|
if (parameters.length != 0 && CPPFunction.getRequiredArgumentCount(parameters) <= 1) {
|
||||||
IType type = getNestedType(parameters[0].getType(), REF | ALLCVQ);
|
IType type = parameters[0].getType();
|
||||||
|
if (type instanceof IBasicType && ((IBasicType) type).getKind() == IBasicType.Kind.eVoid)
|
||||||
|
continue;
|
||||||
|
type = getNestedType(type, REF | ALLCVQ);
|
||||||
if (!classType.isSameType(type))
|
if (!classType.isSameType(type))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -341,6 +351,12 @@ public class BindingClassifier {
|
||||||
return bindings;
|
return bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void declareType(IType type) {
|
||||||
|
IBinding binding = getTypeBinding(type);
|
||||||
|
if (binding != null)
|
||||||
|
declareBinding(binding);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given binding to the list of bindings which have to be forward declared.
|
* Adds the given binding to the list of bindings which have to be forward declared.
|
||||||
*
|
*
|
||||||
|
|
|
@ -37,6 +37,7 @@ public class IncludeCreationContext extends InclusionContext {
|
||||||
private final Set<IPath> fHeadersToInclude;
|
private final Set<IPath> fHeadersToInclude;
|
||||||
private final Set<IPath> fHeadersAlreadyIncluded;
|
private final Set<IPath> fHeadersAlreadyIncluded;
|
||||||
private final Set<IPath> fHeadersIncludedPreviously;
|
private final Set<IPath> fHeadersIncludedPreviously;
|
||||||
|
private String fSourceContents;
|
||||||
|
|
||||||
public IncludeCreationContext(ITranslationUnit tu, IIndex index) {
|
public IncludeCreationContext(ITranslationUnit tu, IIndex index) {
|
||||||
super(tu);
|
super(tu);
|
||||||
|
@ -46,8 +47,11 @@ public class IncludeCreationContext extends InclusionContext {
|
||||||
fHeadersIncludedPreviously = new HashSet<IPath>();
|
fHeadersIncludedPreviously = new HashSet<IPath>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public char[] getSourceContents() {
|
public String getSourceContents() {
|
||||||
return getTranslationUnit().getContents();
|
if (fSourceContents == null) {
|
||||||
|
fSourceContents = new String(getTranslationUnit().getContents());
|
||||||
|
}
|
||||||
|
return fSourceContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IIndex getIndex() {
|
public IIndex getIndex() {
|
||||||
|
|
|
@ -230,7 +230,7 @@ public class IncludeCreator {
|
||||||
List<UsingDeclaration> usingDeclarations, IASTTranslationUnit ast,
|
List<UsingDeclaration> usingDeclarations, IASTTranslationUnit ast,
|
||||||
ITextSelection selection) {
|
ITextSelection selection) {
|
||||||
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
|
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
|
||||||
char[] contents = fContext.getSourceContents();
|
String contents = fContext.getSourceContents();
|
||||||
IRegion includeRegion =
|
IRegion includeRegion =
|
||||||
IncludeOrganizer.getSafeIncludeReplacementRegion(contents, ast, commentedNodeMap);
|
IncludeOrganizer.getSafeIncludeReplacementRegion(contents, ast, commentedNodeMap);
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ public class IncludeCreator {
|
||||||
if (previousNode != null) {
|
if (previousNode != null) {
|
||||||
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
|
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
|
||||||
flushEditBuffer(offset, text, rootEdit);
|
flushEditBuffer(offset, text, rootEdit);
|
||||||
if (contents[offset - 1] != '\n')
|
if (contents.charAt(offset - 1) != '\n')
|
||||||
text.append(fLineDelimiter);
|
text.append(fLineDelimiter);
|
||||||
}
|
}
|
||||||
if (include.getStyle().isBlankLineNeededAfter(previousInclude.getStyle(), preferences.includeStyles))
|
if (include.getStyle().isBlankLineNeededAfter(previousInclude.getStyle(), preferences.includeStyles))
|
||||||
|
@ -365,7 +365,7 @@ public class IncludeCreator {
|
||||||
if (previousNode != null) {
|
if (previousNode != null) {
|
||||||
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
|
offset = ASTNodes.skipToNextLineAfterNode(contents, previousNode);
|
||||||
flushEditBuffer(offset, text, rootEdit);
|
flushEditBuffer(offset, text, rootEdit);
|
||||||
if (contents[offset - 1] != '\n')
|
if (contents.charAt(offset - 1) != '\n')
|
||||||
text.append(fLineDelimiter);
|
text.append(fLineDelimiter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ import org.eclipse.jface.text.IRegion;
|
||||||
import org.eclipse.jface.text.Region;
|
import org.eclipse.jface.text.Region;
|
||||||
import org.eclipse.text.edits.DeleteEdit;
|
import org.eclipse.text.edits.DeleteEdit;
|
||||||
import org.eclipse.text.edits.InsertEdit;
|
import org.eclipse.text.edits.InsertEdit;
|
||||||
|
import org.eclipse.text.edits.MultiTextEdit;
|
||||||
import org.eclipse.text.edits.ReplaceEdit;
|
import org.eclipse.text.edits.ReplaceEdit;
|
||||||
import org.eclipse.text.edits.TextEdit;
|
|
||||||
|
|
||||||
import com.ibm.icu.text.Collator;
|
import com.ibm.icu.text.Collator;
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ import org.eclipse.cdt.core.parser.Keywords;
|
||||||
import org.eclipse.cdt.core.parser.util.CharArrayIntMap;
|
import org.eclipse.cdt.core.parser.util.CharArrayIntMap;
|
||||||
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
|
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
|
||||||
import org.eclipse.cdt.ui.CUIPlugin;
|
import org.eclipse.cdt.ui.CUIPlugin;
|
||||||
|
import org.eclipse.cdt.ui.CodeGeneration;
|
||||||
import org.eclipse.cdt.utils.PathUtil;
|
import org.eclipse.cdt.utils.PathUtil;
|
||||||
|
|
||||||
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
|
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
|
||||||
|
@ -92,6 +93,7 @@ import org.eclipse.cdt.internal.core.parser.scanner.IncludeGuardDetection;
|
||||||
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
|
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
|
||||||
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
|
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
|
||||||
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
|
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
|
||||||
|
import org.eclipse.cdt.internal.formatter.ChangeFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Organizes the include directives and forward declarations of a source or header file.
|
* Organizes the include directives and forward declarations of a source or header file.
|
||||||
|
@ -129,6 +131,54 @@ public class IncludeOrganizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static enum DeclarationType { TYPE, FUNCTION, VARIABLE, NAMESPACE }
|
||||||
|
|
||||||
|
private static class ForwardDeclarationNode implements Comparable<ForwardDeclarationNode> {
|
||||||
|
final String name;
|
||||||
|
final String declaration;
|
||||||
|
final DeclarationType type;
|
||||||
|
final List<ForwardDeclarationNode> children;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a namespace node.
|
||||||
|
*/
|
||||||
|
ForwardDeclarationNode(String name) {
|
||||||
|
this.name = name;
|
||||||
|
this.declaration = null;
|
||||||
|
this.type = DeclarationType.NAMESPACE;
|
||||||
|
this.children = new ArrayList<ForwardDeclarationNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a declaration node.
|
||||||
|
*/
|
||||||
|
ForwardDeclarationNode(String name, String declaration, DeclarationType type) {
|
||||||
|
this.name = name;
|
||||||
|
this.declaration = declaration;
|
||||||
|
this.type = type;
|
||||||
|
this.children = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ForwardDeclarationNode findOrAddChild(ForwardDeclarationNode node) {
|
||||||
|
int i = Collections.binarySearch(children, node);
|
||||||
|
if (i >= 0)
|
||||||
|
return children.get(i);
|
||||||
|
children.add(-(i + 1), node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ForwardDeclarationNode other) {
|
||||||
|
int c = type.ordinal() - other.type.ordinal();
|
||||||
|
if (c != 0)
|
||||||
|
return c;
|
||||||
|
c = COLLATOR.compare(name, other.name);
|
||||||
|
if (declaration == null || c != 0)
|
||||||
|
return c;
|
||||||
|
return COLLATOR.compare(declaration, other.declaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final IHeaderChooser fHeaderChooser;
|
private final IHeaderChooser fHeaderChooser;
|
||||||
private final IncludeCreationContext fContext;
|
private final IncludeCreationContext fContext;
|
||||||
private final String fLineDelimiter;
|
private final String fLineDelimiter;
|
||||||
|
@ -145,7 +195,7 @@ public class IncludeOrganizer {
|
||||||
* @param ast The AST translation unit to process.
|
* @param ast The AST translation unit to process.
|
||||||
* @throws CoreException
|
* @throws CoreException
|
||||||
*/
|
*/
|
||||||
public List<TextEdit> organizeIncludes(IASTTranslationUnit ast) throws CoreException {
|
public MultiTextEdit organizeIncludes(IASTTranslationUnit ast) throws CoreException {
|
||||||
// Process the given translation unit with the inclusion resolver.
|
// Process the given translation unit with the inclusion resolver.
|
||||||
BindingClassifier bindingClassifier = new BindingClassifier(fContext);
|
BindingClassifier bindingClassifier = new BindingClassifier(fContext);
|
||||||
bindingClassifier.classifyNodeContents(ast);
|
bindingClassifier.classifyNodeContents(ast);
|
||||||
|
@ -195,7 +245,7 @@ public class IncludeOrganizer {
|
||||||
IncludePreferences preferences = fContext.getPreferences();
|
IncludePreferences preferences = fContext.getPreferences();
|
||||||
boolean allowReordering = preferences.allowReordering || existingIncludes.length == 0;
|
boolean allowReordering = preferences.allowReordering || existingIncludes.length == 0;
|
||||||
|
|
||||||
List<TextEdit> edits = new ArrayList<TextEdit>();
|
MultiTextEdit rootEdit = new MultiTextEdit();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<IncludePrototype>[] groupedPrototypes =
|
List<IncludePrototype>[] groupedPrototypes =
|
||||||
|
@ -218,10 +268,10 @@ public class IncludeOrganizer {
|
||||||
&& isContainedInRegion(prototype.getExistingInclude(), includeReplacementRegion)) {
|
&& isContainedInRegion(prototype.getExistingInclude(), includeReplacementRegion)) {
|
||||||
switch (preferences.unusedStatementsDisposition) {
|
switch (preferences.unusedStatementsDisposition) {
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
createDelete(prototype.getExistingInclude(), edits);
|
createDelete(prototype.getExistingInclude(), rootEdit);
|
||||||
break;
|
break;
|
||||||
case COMMENT_OUT:
|
case COMMENT_OUT:
|
||||||
createCommentOut(prototype.getExistingInclude(), edits);
|
createCommentOut(prototype.getExistingInclude(), rootEdit);
|
||||||
break;
|
break;
|
||||||
case KEEP:
|
case KEEP:
|
||||||
break;
|
break;
|
||||||
|
@ -262,13 +312,6 @@ public class IncludeOrganizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stores the forward declarations for composite types and enumerations as text.
|
|
||||||
List<String> typeForwardDeclarations = new ArrayList<String>();
|
|
||||||
// Stores the forward declarations for C-style functions as text.
|
|
||||||
List<String> functionForwardDeclarations = new ArrayList<String>();
|
|
||||||
|
|
||||||
createForwardDeclarations(ast, bindingClassifier, typeForwardDeclarations, functionForwardDeclarations);
|
|
||||||
|
|
||||||
// Create the source code to insert into the editor.
|
// Create the source code to insert into the editor.
|
||||||
|
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
|
@ -277,50 +320,41 @@ public class IncludeOrganizer {
|
||||||
buf.append(fLineDelimiter);
|
buf.append(fLineDelimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf.length() != 0 && !typeForwardDeclarations.isEmpty())
|
|
||||||
buf.append(fLineDelimiter);
|
|
||||||
for (String declaration : typeForwardDeclarations) {
|
|
||||||
buf.append(declaration);
|
|
||||||
buf.append(fLineDelimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf.length() != 0 && !functionForwardDeclarations.isEmpty())
|
|
||||||
buf.append(fLineDelimiter);
|
|
||||||
for (String declaration : functionForwardDeclarations) {
|
|
||||||
buf.append(declaration);
|
|
||||||
buf.append(fLineDelimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
int offset = includeReplacementRegion.getOffset();
|
int offset = includeReplacementRegion.getOffset();
|
||||||
int length = includeReplacementRegion.getLength();
|
int length = includeReplacementRegion.getLength();
|
||||||
if (allowReordering) {
|
if (allowReordering) {
|
||||||
if (buf.length() != 0) {
|
if (buf.length() != 0) {
|
||||||
if (offset != 0 && !TextUtil.isPreviousLineBlank(fContext.getSourceContents(), offset))
|
if (offset != 0 && !TextUtil.isPreviousLineBlank(fContext.getSourceContents(), offset))
|
||||||
buf.insert(0, fLineDelimiter); // Blank line before.
|
buf.insert(0, fLineDelimiter); // Blank line before.
|
||||||
if (!isBlankLineOrEndOfFile(offset + length))
|
|
||||||
buf.append(fLineDelimiter); // Blank line after.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String text = buf.toString();
|
String text = buf.toString();
|
||||||
// TODO(sprigogin): Add a diff algorithm and produce narrower replacements.
|
// TODO(sprigogin): Add a diff algorithm and produce narrower replacements.
|
||||||
if (!CharArrayUtils.equals(fContext.getSourceContents(), offset, length, text)) {
|
if (text.length() != length ||
|
||||||
edits.add(new ReplaceEdit(offset, length, text));
|
!fContext.getSourceContents().regionMatches(offset, text, 0, length)) {
|
||||||
|
rootEdit.addChild(new ReplaceEdit(offset, length, text));
|
||||||
}
|
}
|
||||||
} else if (buf.length() != 0) {
|
} else if (buf.length() != 0) {
|
||||||
offset += length;
|
offset += length;
|
||||||
if (!isBlankLineOrEndOfFile(offset))
|
rootEdit.addChild(new InsertEdit(offset, buf.toString()));
|
||||||
buf.append(fLineDelimiter); // Blank line after.
|
|
||||||
edits.add(new InsertEdit(offset, buf.toString()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return edits;
|
createForwardDeclarations(ast, bindingClassifier,
|
||||||
|
includeReplacementRegion.getOffset() + includeReplacementRegion.getLength(),
|
||||||
|
buf.length() != 0, rootEdit);
|
||||||
|
|
||||||
|
return ChangeFormatter.formatChangedCode(new String(fContext.getSourceContents()), fContext.getTranslationUnit(), rootEdit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates forward declarations by examining the list of bindings which have to be declared.
|
* Creates forward declarations by examining the list of bindings which have to be declared.
|
||||||
|
* @param pendingBlankLine
|
||||||
*/
|
*/
|
||||||
private void createForwardDeclarations(IASTTranslationUnit ast, BindingClassifier classifier,
|
private void createForwardDeclarations(IASTTranslationUnit ast, BindingClassifier classifier,
|
||||||
List<String> forwardDeclarations, List<String> functionForwardDeclarations) throws CoreException {
|
int offset, boolean pendingBlankLine, MultiTextEdit rootEdit) throws CoreException {
|
||||||
|
ForwardDeclarationNode typeDeclarationsRoot = new ForwardDeclarationNode(""); //$NON-NLS-1$
|
||||||
|
ForwardDeclarationNode nonTypeDeclarationsRoot = new ForwardDeclarationNode(""); //$NON-NLS-1$
|
||||||
|
|
||||||
IIndexFileSet reachableHeaders = ast.getIndexFileSet();
|
IIndexFileSet reachableHeaders = ast.getIndexFileSet();
|
||||||
Set<IBinding> bindings =
|
Set<IBinding> bindings =
|
||||||
removeBindingsDefinedInIncludedHeaders(ast, classifier.getBindingsToDeclare(), reachableHeaders);
|
removeBindingsDefinedInIncludedHeaders(ast, classifier.getBindingsToDeclare(), reachableHeaders);
|
||||||
|
@ -328,32 +362,10 @@ public class IncludeOrganizer {
|
||||||
// Create the text of the forward declaration of this binding.
|
// Create the text of the forward declaration of this binding.
|
||||||
StringBuilder declarationText = new StringBuilder();
|
StringBuilder declarationText = new StringBuilder();
|
||||||
|
|
||||||
// Consider the namespace(s) of the binding.
|
DeclarationType declarationType;
|
||||||
List<IName> scopeNames = new ArrayList<IName>();
|
|
||||||
try {
|
|
||||||
IScope scope = binding.getScope();
|
|
||||||
while (scope != null && scope.getKind() == EScopeKind.eNamespace) {
|
|
||||||
IName scopeName = scope.getScopeName();
|
|
||||||
if (scopeName != null) {
|
|
||||||
scopeNames.add(scopeName);
|
|
||||||
}
|
|
||||||
scope = scope.getParent();
|
|
||||||
}
|
|
||||||
} catch (DOMException e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.reverse(scopeNames);
|
|
||||||
for (IName scopeName : scopeNames) {
|
|
||||||
declarationText.append("namespace "); //$NON-NLS-1$
|
|
||||||
declarationText.append(scopeName.toString());
|
|
||||||
declarationText.append(" { "); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the list which should be used to store the declaration.
|
|
||||||
List<String> forwardDeclarationListToUse = forwardDeclarations;
|
|
||||||
|
|
||||||
// Check the type of the binding and create a corresponding forward declaration text.
|
// Check the type of the binding and create a corresponding forward declaration text.
|
||||||
if (binding instanceof ICompositeType) {
|
if (binding instanceof ICompositeType) {
|
||||||
|
declarationType = DeclarationType.TYPE;
|
||||||
// Forward declare a composite type.
|
// Forward declare a composite type.
|
||||||
ICompositeType compositeType = (ICompositeType) binding;
|
ICompositeType compositeType = (ICompositeType) binding;
|
||||||
|
|
||||||
|
@ -404,16 +416,19 @@ public class IncludeOrganizer {
|
||||||
// Append the semicolon.
|
// Append the semicolon.
|
||||||
declarationText.append(';');
|
declarationText.append(';');
|
||||||
} else if (binding instanceof IEnumeration) {
|
} else if (binding instanceof IEnumeration) {
|
||||||
|
declarationType = DeclarationType.TYPE;
|
||||||
// Forward declare an enumeration class (C++11 syntax).
|
// Forward declare an enumeration class (C++11 syntax).
|
||||||
declarationText.append("enum class "); //$NON-NLS-1$
|
declarationText.append("enum class "); //$NON-NLS-1$
|
||||||
declarationText.append(binding.getName());
|
declarationText.append(binding.getName());
|
||||||
declarationText.append(';');
|
declarationText.append(';');
|
||||||
} else if (binding instanceof IFunction && !(binding instanceof ICPPMethod)) {
|
} else if (binding instanceof IFunction && !(binding instanceof ICPPMethod)) {
|
||||||
|
declarationType = DeclarationType.FUNCTION;
|
||||||
// Forward declare a C-style function.
|
// Forward declare a C-style function.
|
||||||
IFunction function = (IFunction) binding;
|
IFunction function = (IFunction) binding;
|
||||||
|
|
||||||
// Append return type and function name.
|
// Append return type and function name.
|
||||||
IFunctionType functionType = function.getType();
|
IFunctionType functionType = function.getType();
|
||||||
|
// TODO(sprigogin): Switch to ASTWriter since ASTTypeUtil doesn't properly handle namespaces.
|
||||||
declarationText.append(ASTTypeUtil.getType(functionType.getReturnType(), false));
|
declarationText.append(ASTTypeUtil.getType(functionType.getReturnType(), false));
|
||||||
declarationText.append(' ');
|
declarationText.append(' ');
|
||||||
declarationText.append(function.getName());
|
declarationText.append(function.getName());
|
||||||
|
@ -436,10 +451,8 @@ public class IncludeOrganizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
declarationText.append(");"); //$NON-NLS-1$
|
declarationText.append(");"); //$NON-NLS-1$
|
||||||
|
|
||||||
// Add this forward declaration to the separate function forward declaration list.
|
|
||||||
forwardDeclarationListToUse = functionForwardDeclarations;
|
|
||||||
} else if (binding instanceof IVariable) {
|
} else if (binding instanceof IVariable) {
|
||||||
|
declarationType = DeclarationType.VARIABLE;
|
||||||
IVariable variable = (IVariable) binding;
|
IVariable variable = (IVariable) binding;
|
||||||
IType variableType = variable.getType();
|
IType variableType = variable.getType();
|
||||||
declarationText.append("extern "); //$NON-NLS-1$
|
declarationText.append("extern "); //$NON-NLS-1$
|
||||||
|
@ -451,41 +464,95 @@ public class IncludeOrganizer {
|
||||||
CUIPlugin.log(new IllegalArgumentException(
|
CUIPlugin.log(new IllegalArgumentException(
|
||||||
"Unexpected type of binding " + binding.getName() + //$NON-NLS-1$
|
"Unexpected type of binding " + binding.getName() + //$NON-NLS-1$
|
||||||
" - " + binding.getClass().getSimpleName())); //$NON-NLS-1$
|
" - " + binding.getClass().getSimpleName())); //$NON-NLS-1$
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the closing curly brackets from the namespaces (if any).
|
// Consider the namespace(s) of the binding.
|
||||||
for (int i = 0; i < scopeNames.size(); i++) {
|
List<String> namespaces = new ArrayList<String>();
|
||||||
declarationText.append(" }"); //$NON-NLS-1$
|
try {
|
||||||
|
IScope scope = binding.getScope();
|
||||||
|
while (scope != null && scope.getKind() == EScopeKind.eNamespace) {
|
||||||
|
IName scopeName = scope.getScopeName();
|
||||||
|
if (scopeName != null) {
|
||||||
|
namespaces.add(new String(scopeName.getSimpleID()));
|
||||||
|
}
|
||||||
|
scope = scope.getParent();
|
||||||
|
}
|
||||||
|
} catch (DOMException e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the forward declaration to the corresponding list.
|
ForwardDeclarationNode parentNode = declarationType == DeclarationType.TYPE ?
|
||||||
forwardDeclarationListToUse.add(declarationText.toString());
|
typeDeclarationsRoot : nonTypeDeclarationsRoot;
|
||||||
|
|
||||||
|
Collections.reverse(namespaces);
|
||||||
|
for (String ns : namespaces) {
|
||||||
|
ForwardDeclarationNode node = new ForwardDeclarationNode(ns);
|
||||||
|
parentNode = parentNode.findOrAddChild(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
ForwardDeclarationNode node =
|
||||||
|
new ForwardDeclarationNode(binding.getName(), declarationText.toString(), declarationType);
|
||||||
|
parentNode.findOrAddChild(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(forwardDeclarations, COLLATOR);
|
StringBuilder buf = new StringBuilder();
|
||||||
Collections.sort(functionForwardDeclarations, COLLATOR);
|
|
||||||
|
for (ForwardDeclarationNode node : typeDeclarationsRoot.children) {
|
||||||
|
if (pendingBlankLine) {
|
||||||
|
buf.append(fLineDelimiter);
|
||||||
|
pendingBlankLine = false;
|
||||||
|
}
|
||||||
|
printNode(node, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ForwardDeclarationNode node : nonTypeDeclarationsRoot.children) {
|
||||||
|
if (pendingBlankLine) {
|
||||||
|
buf.append(fLineDelimiter);
|
||||||
|
pendingBlankLine = false;
|
||||||
|
}
|
||||||
|
printNode(node, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pendingBlankLine || buf.length() != 0) && !isBlankLineOrEndOfFile(offset))
|
||||||
|
buf.append(fLineDelimiter);
|
||||||
|
|
||||||
|
if (buf.length() != 0)
|
||||||
|
rootEdit.addChild(new InsertEdit(offset, buf.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCommentOut(IASTPreprocessorIncludeStatement include, List<TextEdit> edits) {
|
private void printNode(ForwardDeclarationNode node, StringBuilder buf) throws CoreException {
|
||||||
|
if (node.declaration == null) {
|
||||||
|
buf.append(CodeGeneration.getNamespaceBeginContent(fContext.getTranslationUnit(), node.name, fLineDelimiter));
|
||||||
|
for (ForwardDeclarationNode child : node.children) {
|
||||||
|
printNode(child, buf);
|
||||||
|
}
|
||||||
|
buf.append(CodeGeneration.getNamespaceEndContent(fContext.getTranslationUnit(), node.name, fLineDelimiter));
|
||||||
|
} else {
|
||||||
|
buf.append(node.declaration);
|
||||||
|
}
|
||||||
|
buf.append(fLineDelimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCommentOut(IASTPreprocessorIncludeStatement include, MultiTextEdit rootEdit) {
|
||||||
IASTFileLocation location = include.getFileLocation();
|
IASTFileLocation location = include.getFileLocation();
|
||||||
int offset = location.getNodeOffset();
|
int offset = location.getNodeOffset();
|
||||||
if (fContext.getTranslationUnit().isCXXLanguage()) {
|
if (fContext.getTranslationUnit().isCXXLanguage()) {
|
||||||
offset = TextUtil.getLineStart(fContext.getSourceContents(), offset);
|
offset = TextUtil.getLineStart(fContext.getSourceContents(), offset);
|
||||||
edits.add(new InsertEdit(offset, "//")); //$NON-NLS-1$
|
rootEdit.addChild(new InsertEdit(offset, "//")); //$NON-NLS-1$
|
||||||
} else {
|
} else {
|
||||||
edits.add(new InsertEdit(offset, "/*")); //$NON-NLS-1$
|
rootEdit.addChild(new InsertEdit(offset, "/*")); //$NON-NLS-1$
|
||||||
int endOffset = offset + location.getNodeLength();
|
int endOffset = offset + location.getNodeLength();
|
||||||
edits.add(new InsertEdit(endOffset, "*/")); //$NON-NLS-1$
|
rootEdit.addChild(new InsertEdit(endOffset, "*/")); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDelete(IASTPreprocessorIncludeStatement include, List<TextEdit> edits) {
|
private void createDelete(IASTPreprocessorIncludeStatement include, MultiTextEdit rootEdit) {
|
||||||
IASTFileLocation location = include.getFileLocation();
|
IASTFileLocation location = include.getFileLocation();
|
||||||
int offset = location.getNodeOffset();
|
int offset = location.getNodeOffset();
|
||||||
int endOffset = offset + location.getNodeLength();
|
int endOffset = offset + location.getNodeLength();
|
||||||
offset = TextUtil.getLineStart(fContext.getSourceContents(), offset);
|
offset = TextUtil.getLineStart(fContext.getSourceContents(), offset);
|
||||||
endOffset = TextUtil.skipToNextLine(fContext.getSourceContents(), endOffset);
|
endOffset = TextUtil.skipToNextLine(fContext.getSourceContents(), endOffset);
|
||||||
edits.add(new DeleteEdit(offset, endOffset - offset));
|
rootEdit.addChild(new DeleteEdit(offset, endOffset - offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateIncludePrototypes(Map<IncludePrototype, IncludePrototype> includePrototypes,
|
private void updateIncludePrototypes(Map<IncludePrototype, IncludePrototype> includePrototypes,
|
||||||
|
@ -498,7 +565,7 @@ public class IncludeOrganizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static IRegion getSafeIncludeReplacementRegion(char[] contents, IASTTranslationUnit ast,
|
static IRegion getSafeIncludeReplacementRegion(String contents, IASTTranslationUnit ast,
|
||||||
NodeCommentMap commentMap) {
|
NodeCommentMap commentMap) {
|
||||||
int maxSafeOffset = ast.getFileLocation().getNodeLength();
|
int maxSafeOffset = ast.getFileLocation().getNodeLength();
|
||||||
IASTDeclaration[] declarations = ast.getDeclarations(true);
|
IASTDeclaration[] declarations = ast.getDeclarations(true);
|
||||||
|
@ -592,9 +659,9 @@ public class IncludeOrganizer {
|
||||||
* {@code offset} and the end of the line.
|
* {@code offset} and the end of the line.
|
||||||
*/
|
*/
|
||||||
private boolean isBlankLineOrEndOfFile(int offset) {
|
private boolean isBlankLineOrEndOfFile(int offset) {
|
||||||
char[] contents = fContext.getSourceContents();
|
String contents = fContext.getSourceContents();
|
||||||
while (offset < contents.length) {
|
while (offset < contents.length()) {
|
||||||
char c = contents[offset++];
|
char c = contents.charAt(offset++);
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
return true;
|
return true;
|
||||||
if (!Character.isWhitespace(c))
|
if (!Character.isWhitespace(c))
|
||||||
|
@ -610,20 +677,20 @@ public class IncludeOrganizer {
|
||||||
private String getPrecedingWhitespace(IASTNode node) {
|
private String getPrecedingWhitespace(IASTNode node) {
|
||||||
int offset = getNodeOffset(node);
|
int offset = getNodeOffset(node);
|
||||||
if (offset >= 0) {
|
if (offset >= 0) {
|
||||||
char[] contents = fContext.getSourceContents();
|
String contents = fContext.getSourceContents();
|
||||||
int i = offset;
|
int i = offset;
|
||||||
while (--i >= 0) {
|
while (--i >= 0) {
|
||||||
char c = contents[i];
|
char c = contents.charAt(i);
|
||||||
if (c == '\n' || !Character.isWhitespace(c))
|
if (c == '\n' || !Character.isWhitespace(c))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
return new String(contents, i, offset - i);
|
return contents.substring(i, offset);
|
||||||
}
|
}
|
||||||
return ""; //$NON-NLS-1$
|
return ""; //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int skipStandaloneCommentBlock(char[] contents, int offset, int endOffset,
|
private static int skipStandaloneCommentBlock(String contents, int offset, int endOffset,
|
||||||
IASTComment[] comments, NodeCommentMap commentMap) {
|
IASTComment[] comments, NodeCommentMap commentMap) {
|
||||||
Map<IASTComment, IASTNode> inverseLeadingMap = new HashMap<IASTComment, IASTNode>();
|
Map<IASTComment, IASTNode> inverseLeadingMap = new HashMap<IASTComment, IASTNode>();
|
||||||
for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getLeadingMap().entrySet()) {
|
for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getLeadingMap().entrySet()) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue