mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-30 20:35:38 +02:00
Extensive rewrite of Add Include. Bugs 236530 and 255952.
This commit is contained in:
parent
1580548a45
commit
deeb5db9ae
8 changed files with 691 additions and 388 deletions
|
@ -29,6 +29,7 @@ import org.eclipse.cdt.core.index.IIndexFileSet;
|
|||
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
|
||||
import org.eclipse.cdt.internal.core.dom.Linkage;
|
||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
|
||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
|
||||
import org.eclipse.cdt.internal.core.parser.ParserMessages;
|
||||
import org.eclipse.core.runtime.PlatformObject;
|
||||
|
||||
|
@ -230,7 +231,7 @@ public class ProblemBinding extends PlatformObject implements IProblemBinding, I
|
|||
}
|
||||
|
||||
public IBinding getOwner() throws DOMException {
|
||||
return null;
|
||||
return node instanceof IASTName ? CPPVisitor.findNameOwner((IASTName) node, true) : null;
|
||||
}
|
||||
|
||||
public void setASTNode(IASTName name) {
|
||||
|
|
|
@ -2010,10 +2010,17 @@ public class CPPVisitor extends ASTQueries {
|
|||
while (node instanceof IASTName) {
|
||||
if (node instanceof ICPPASTQualifiedName) {
|
||||
IASTName[] qn= ((ICPPASTQualifiedName) node).getNames();
|
||||
if (qn.length < 2)
|
||||
return null;
|
||||
return qn[qn.length - 2].resolveBinding();
|
||||
int i = qn.length;
|
||||
while (--i >= 0) {
|
||||
if (qn[i] == name) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (--i < 0)
|
||||
break;
|
||||
return qn[i].resolveBinding();
|
||||
}
|
||||
name = (IASTName) node;
|
||||
node= node.getParent();
|
||||
}
|
||||
return findDeclarationOwner(node, allowFunction);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
* QNX Software Systems - initial API and implementation
|
||||
* Markus Schorn (Wind River Systems)
|
||||
* Ed Swartz (Nokia)
|
||||
* Sergey Prigogin (Google)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.utils;
|
||||
|
||||
|
@ -23,6 +24,7 @@ import org.eclipse.core.resources.IResource;
|
|||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.Assert;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
|
||||
|
@ -65,8 +67,8 @@ public class PathUtil {
|
|||
IWorkspaceRoot workspaceRoot = getWorkspaceRoot();
|
||||
if (workspaceRoot != null) {
|
||||
IPath workspaceLocation = workspaceRoot.getLocation();
|
||||
if (workspaceLocation != null && workspaceLocation.isPrefixOf(fullPath)) {
|
||||
int segments = fullPath.matchingFirstSegments(workspaceLocation);
|
||||
if (workspaceLocation != null && isPrefix(workspaceLocation, fullPath)) {
|
||||
int segments = matchingFirstSegments(fullPath, workspaceLocation);
|
||||
IPath relPath = fullPath.setDevice(null).removeFirstSegments(segments);
|
||||
return new Path("").addTrailingSeparator().append(relPath); //$NON-NLS-1$
|
||||
}
|
||||
|
@ -76,11 +78,11 @@ public class PathUtil {
|
|||
|
||||
public static IPath getProjectRelativePath(IPath fullPath, IProject project) {
|
||||
IPath projectPath = project.getFullPath();
|
||||
if (projectPath.isPrefixOf(fullPath)) {
|
||||
if (isPrefix(projectPath, fullPath)) {
|
||||
return fullPath.removeFirstSegments(projectPath.segmentCount());
|
||||
}
|
||||
projectPath = project.getLocation();
|
||||
if (projectPath.isPrefixOf(fullPath)) {
|
||||
if (isPrefix(projectPath, fullPath)) {
|
||||
return fullPath.removeFirstSegments(projectPath.segmentCount());
|
||||
}
|
||||
return getWorkspaceRelativePath(fullPath);
|
||||
|
@ -94,7 +96,7 @@ public class PathUtil {
|
|||
IWorkspaceRoot workspaceRoot = getWorkspaceRoot();
|
||||
if (workspaceRoot != null && wsRelativePath != null) {
|
||||
IPath workspaceLocation = workspaceRoot.getLocation();
|
||||
if (workspaceLocation != null && !workspaceLocation.isPrefixOf(wsRelativePath)) {
|
||||
if (workspaceLocation != null && !isPrefix(workspaceLocation, wsRelativePath)) {
|
||||
return workspaceLocation.append(wsRelativePath);
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +104,7 @@ public class PathUtil {
|
|||
}
|
||||
|
||||
public static IPath makeRelativePath(IPath path, IPath relativeTo) {
|
||||
int segments = relativeTo.matchingFirstSegments(path);
|
||||
int segments = matchingFirstSegments(relativeTo, path);
|
||||
if (segments > 0) {
|
||||
IPath prefix = relativeTo.removeFirstSegments(segments);
|
||||
IPath suffix = path.removeFirstSegments(segments);
|
||||
|
@ -131,8 +133,8 @@ public class PathUtil {
|
|||
int mostSegments = 0;
|
||||
for (int i = 0; i < includePaths.length; ++i) {
|
||||
IPath includePath = new Path(includePaths[i]);
|
||||
if (includePath.isPrefixOf(fullPath)) {
|
||||
int segments = includePath.matchingFirstSegments(fullPath);
|
||||
if (isPrefix(includePath, fullPath)) {
|
||||
int segments = includePath.segmentCount();
|
||||
if (segments > mostSegments) {
|
||||
relativePath = fullPath.removeFirstSegments(segments).setDevice(null);
|
||||
mostSegments = segments;
|
||||
|
@ -171,4 +173,104 @@ public class PathUtil {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether path1 is the same as path2.
|
||||
* @return <code>true</code> if path1 is the same as path2, and <code>false</code> otherwise
|
||||
*
|
||||
* Similar to IPath.equals(Object obj), but takes case sensitivity of the file system
|
||||
* into account.
|
||||
* @since 5.1
|
||||
*/
|
||||
public boolean equal(IPath path1, IPath path2) {
|
||||
// Check leading separators
|
||||
if (path1.isAbsolute() != path2.isAbsolute() || path1.isUNC() != path2.isUNC()) {
|
||||
return false;
|
||||
}
|
||||
int i = path1.segmentCount();
|
||||
// Check segment count
|
||||
if (i != path2.segmentCount())
|
||||
return false;
|
||||
// Check segments in reverse order - later segments more likely to differ
|
||||
while (--i >= 0) {
|
||||
if (!path1.segment(i).equals(path2.segment(i)))
|
||||
return false;
|
||||
}
|
||||
// Check device last (least likely to differ)
|
||||
if (path1.getDevice() == null) {
|
||||
return path2.getDevice() == null;
|
||||
} else {
|
||||
return path1.getDevice().equalsIgnoreCase(path2.getDevice());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether path1 is a prefix of path2. To be a prefix, path1's segments
|
||||
* must appear in path1 in the same order, and their device ids must match.
|
||||
* <p>
|
||||
* An empty path is a prefix of all paths with the same device; a root path is a prefix of
|
||||
* all absolute paths with the same device.
|
||||
* </p>
|
||||
* @return <code>true</code> if path1 is a prefix of path2, and <code>false</code> otherwise
|
||||
*
|
||||
* Similar to IPath.isPrefixOf(IPath anotherPath), but takes case sensitivity of the file system
|
||||
* into account.
|
||||
* @since 5.1
|
||||
*/
|
||||
public static boolean isPrefix(IPath path1, IPath path2) {
|
||||
if (path1.getDevice() == null) {
|
||||
if (path2.getDevice() != null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!path1.getDevice().equalsIgnoreCase(path2.getDevice())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (path1.isEmpty() || (path1.isRoot() && path2.isAbsolute())) {
|
||||
return true;
|
||||
}
|
||||
int len1 = path1.segmentCount();
|
||||
if (len1 > path2.segmentCount()) {
|
||||
return false;
|
||||
}
|
||||
boolean caseSensitive = !isWindowsFileSystem();
|
||||
for (int i = 0; i < len1; i++) {
|
||||
if (!(caseSensitive ?
|
||||
path1.segment(i).equals(path2.segment(i)) :
|
||||
path1.segment(i).equalsIgnoreCase(path2.segment(i)))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of segments which match in path1 and path2
|
||||
* (device ids are ignored), comparing in increasing segment number order.
|
||||
*
|
||||
* @return the number of matching segments
|
||||
|
||||
* Similar to IPath.matchingFirstSegments(IPath anotherPath), but takes case sensitivity
|
||||
* of the file system into account.
|
||||
* @since 5.1
|
||||
*/
|
||||
public static int matchingFirstSegments(IPath path1, IPath path2) {
|
||||
Assert.isNotNull(path1);
|
||||
Assert.isNotNull(path2);
|
||||
int len1 = path1.segmentCount();
|
||||
int len2 = path2.segmentCount();
|
||||
int max = Math.min(len1, len2);
|
||||
int count = 0;
|
||||
boolean caseSensitive = !isWindowsFileSystem();
|
||||
for (int i = 0; i < max; i++) {
|
||||
if (!(caseSensitive ?
|
||||
path1.segment(i).equals(path2.segment(i)) :
|
||||
path1.segment(i).equalsIgnoreCase(path2.segment(i)))) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* Contributors:
|
||||
* IBM Corporation - initial API and implementation
|
||||
* QNX Software Systems
|
||||
* Sergey Prigogin (Google)
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.internal.corext.codemanipulation;
|
||||
|
@ -24,6 +25,8 @@ import org.eclipse.core.runtime.NullProgressMonitor;
|
|||
import org.eclipse.core.runtime.jobs.ISchedulingRule;
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.jface.text.Region;
|
||||
|
||||
import org.eclipse.cdt.core.model.CModelException;
|
||||
import org.eclipse.cdt.core.model.IBuffer;
|
||||
|
@ -43,26 +46,27 @@ import org.eclipse.cdt.internal.ui.editor.CEditorMessages;
|
|||
* If the translation unit is open in an editor, be sure to pass over its working copy.
|
||||
*/
|
||||
public class AddIncludesOperation implements IWorkspaceRunnable {
|
||||
|
||||
private ITranslationUnit fTranslationUnit;
|
||||
private IRequiredInclude[] fIncludes;
|
||||
private String[] fUsings;
|
||||
private final String fNewLine;
|
||||
private IRegion insertedIncludes;
|
||||
|
||||
/**
|
||||
* Generate include statements for the passed java elements
|
||||
*/
|
||||
public AddIncludesOperation(ITranslationUnit tu, IRequiredInclude[] includes, boolean save) {
|
||||
this (tu, includes, null, save);
|
||||
this(tu, includes, null, save);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate include statements for the passed c elements
|
||||
*/
|
||||
public AddIncludesOperation(ITranslationUnit tu, IRequiredInclude[] includes, String[] using, boolean save) {
|
||||
public AddIncludesOperation(ITranslationUnit tu, IRequiredInclude[] includes, String[] usings,
|
||||
boolean save) {
|
||||
super();
|
||||
fIncludes= includes;
|
||||
fUsings = using;
|
||||
fUsings = usings;
|
||||
fTranslationUnit = tu;
|
||||
fNewLine= getNewLine(tu);
|
||||
}
|
||||
|
@ -82,10 +86,10 @@ public class AddIncludesOperation implements IWorkspaceRunnable {
|
|||
} catch (CModelException e) {
|
||||
} catch (BadLocationException e) {
|
||||
}
|
||||
return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$ }
|
||||
return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$
|
||||
}
|
||||
|
||||
public void executeIncludes(IProgressMonitor monitor) throws CoreException {
|
||||
private void insertIncludes(IProgressMonitor monitor) throws CoreException {
|
||||
// Sanity
|
||||
if (fIncludes == null || fIncludes.length == 0) {
|
||||
return;
|
||||
|
@ -96,122 +100,118 @@ public class AddIncludesOperation implements IWorkspaceRunnable {
|
|||
|
||||
monitor.beginTask(CEditorMessages.AddIncludesOperation_description, 2);
|
||||
|
||||
List<?> elements = fTranslationUnit.getChildrenOfType(ICElement.C_INCLUDE);
|
||||
for (int i = 0; i < fIncludes.length; ++i) {
|
||||
String name = fIncludes[i].getIncludeName();
|
||||
List<ICElement> elements = fTranslationUnit.getChildrenOfType(ICElement.C_INCLUDE);
|
||||
for (IRequiredInclude include : fIncludes) {
|
||||
String name = include.getIncludeName();
|
||||
boolean found = false;
|
||||
for (int j = 0; j < elements.size(); ++j) {
|
||||
IInclude include = (IInclude)elements.get(j);
|
||||
if (name.equals(include.getElementName())) {
|
||||
for (ICElement element : elements) {
|
||||
if (name.equals(element.getElementName())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
toAdd.add(fIncludes[i]);
|
||||
toAdd.add(include);
|
||||
}
|
||||
}
|
||||
|
||||
if (toAdd.size() > 0) {
|
||||
if (!toAdd.isEmpty()) {
|
||||
// So we have our list. Now insert.
|
||||
StringBuffer insert = new StringBuffer(""); //$NON-NLS-1$
|
||||
for(int j = 0; j < toAdd.size(); j++) {
|
||||
IRequiredInclude req = toAdd.get(j);
|
||||
if (req.isStandard()) {
|
||||
insert.append("#include <" + req.getIncludeName() + ">").append(fNewLine); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (IRequiredInclude include : toAdd) {
|
||||
if (include.isStandard()) {
|
||||
buf.append("#include <" + include.getIncludeName() + ">").append(fNewLine); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
} else {
|
||||
insert.append("#include \"" + req.getIncludeName() + "\"").append(fNewLine); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
buf.append("#include \"" + include.getIncludeName() + "\"").append(fNewLine); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
}
|
||||
|
||||
int pos;
|
||||
if (elements.size() > 0) {
|
||||
IInclude lastInclude = (IInclude)elements.get(elements.size() - 1);
|
||||
int pos = 0;
|
||||
if (!elements.isEmpty()) {
|
||||
IInclude lastInclude = (IInclude) elements.get(elements.size() - 1);
|
||||
ISourceRange range = lastInclude.getSourceRange();
|
||||
pos = range.getStartPos() + range.getLength();
|
||||
} else {
|
||||
pos = 0;
|
||||
}
|
||||
monitor.worked(1);
|
||||
replace(pos, insert.toString());
|
||||
replace(pos, buf.toString());
|
||||
insertedIncludes = new Region(pos, buf.length());
|
||||
monitor.worked(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void executeUsings(IProgressMonitor monitor) throws CoreException {
|
||||
private void insertUsings(IProgressMonitor monitor) throws CoreException {
|
||||
// Sanity
|
||||
if (fUsings == null || fUsings.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fTranslationUnit != null) {
|
||||
ArrayList<String> toAdd = new ArrayList<String>();
|
||||
|
||||
ArrayList<String> toAdd = new ArrayList<String>(fUsings.length);
|
||||
|
||||
monitor.beginTask(CEditorMessages.AddIncludesOperation_description, 2);
|
||||
|
||||
List<?> elements = fTranslationUnit.getChildrenOfType(ICElement.C_USING);
|
||||
for (int i = 0; i < fUsings.length; ++i) {
|
||||
String name = fUsings[i];
|
||||
|
||||
List<ICElement> elements = fTranslationUnit.getChildrenOfType(ICElement.C_USING);
|
||||
for (String name : fUsings) {
|
||||
boolean found = false;
|
||||
for (int j = 0; j < elements.size(); ++j) {
|
||||
IUsing using = (IUsing)elements.get(j);
|
||||
if (name.equals(using.getElementName())) {
|
||||
for (ICElement element : elements) {
|
||||
if (name.equals(element.getElementName())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
toAdd.add(fUsings[i]);
|
||||
toAdd.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (toAdd.size() > 0) {
|
||||
|
||||
if (!toAdd.isEmpty()) {
|
||||
// So we have our list. Now insert.
|
||||
StringBuffer insert = new StringBuffer(""); //$NON-NLS-1$
|
||||
for(int j = 0; j < toAdd.size(); j++) {
|
||||
String using = toAdd.get(j);
|
||||
insert.append("using namespace " + using + ";").append(fNewLine); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (String using : toAdd) {
|
||||
buf.append("using ").append(using).append(';').append(fNewLine); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
int pos;
|
||||
List<?> includes = fTranslationUnit.getChildrenOfType(ICElement.C_INCLUDE);
|
||||
if (includes.size() > 0) {
|
||||
IInclude lastInclude = (IInclude)includes.get(includes.size() - 1);
|
||||
ISourceRange range = lastInclude.getSourceRange();
|
||||
pos = range.getStartPos() + range.getLength();
|
||||
} else if (elements.size() > 0) {
|
||||
IUsing lastUsing = (IUsing)includes.get(includes.size() - 1);
|
||||
|
||||
int pos = 0;
|
||||
if (!elements.isEmpty()) {
|
||||
IUsing lastUsing = (IUsing) elements.get(elements.size() - 1);
|
||||
ISourceRange range = lastUsing.getSourceRange();
|
||||
pos = range.getStartPos() + range.getLength();
|
||||
} else {
|
||||
pos = 0;
|
||||
List<ICElement> includes = fTranslationUnit.getChildrenOfType(ICElement.C_INCLUDE);
|
||||
if (!includes.isEmpty()) {
|
||||
IInclude lastInclude = (IInclude) includes.get(includes.size() - 1);
|
||||
ISourceRange range = lastInclude.getSourceRange();
|
||||
pos = range.getStartPos() + range.getLength();
|
||||
}
|
||||
if (!includes.isEmpty() || insertedIncludes != null) {
|
||||
buf.insert(0, fNewLine);
|
||||
}
|
||||
}
|
||||
|
||||
if (insertedIncludes != null && pos >= insertedIncludes.getOffset()) {
|
||||
pos += insertedIncludes.getLength();
|
||||
}
|
||||
|
||||
monitor.worked(1);
|
||||
replace(pos, insert.toString());
|
||||
replace(pos, buf.toString());
|
||||
monitor.worked(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void replace(int pos, String s) {
|
||||
try {
|
||||
IBuffer buffer = fTranslationUnit.getBuffer();
|
||||
// Now find the next newline and insert after that
|
||||
if (pos > 0) {
|
||||
while (buffer.getChar(pos) != '\n') {
|
||||
pos++;
|
||||
}
|
||||
if (buffer.getChar(pos) == '\r') {
|
||||
pos++;
|
||||
}
|
||||
private void replace(int pos, String s) throws CModelException {
|
||||
IBuffer buffer = fTranslationUnit.getBuffer();
|
||||
// Now find the next newline and insert after that
|
||||
if (pos > 0) {
|
||||
while (buffer.getChar(pos) != '\n') {
|
||||
pos++;
|
||||
}
|
||||
buffer.replace(pos, 0, s);
|
||||
} catch (Exception e) {
|
||||
// ignore; should we log ?
|
||||
if (buffer.getChar(pos) == '\r') {
|
||||
pos++;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
buffer.replace(pos, 0, s);
|
||||
}
|
||||
|
||||
public void run(IProgressMonitor monitor) throws CoreException {
|
||||
|
@ -219,8 +219,8 @@ public class AddIncludesOperation implements IWorkspaceRunnable {
|
|||
monitor= new NullProgressMonitor();
|
||||
}
|
||||
try {
|
||||
executeUsings(monitor);
|
||||
executeIncludes(monitor);
|
||||
insertIncludes(monitor);
|
||||
insertUsings(monitor);
|
||||
} finally {
|
||||
monitor.done();
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ public class AddIncludesOperation implements IWorkspaceRunnable {
|
|||
/**
|
||||
* @return Returns the scheduling rule for this operation
|
||||
*/
|
||||
public ISchedulingRule getScheduleRule() {
|
||||
public ISchedulingRule getSchedulingRule() {
|
||||
return ResourcesPlugin.getWorkspace().getRoot();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2000, 2008 IBM Corporation and others.
|
||||
* Copyright (c) 2000, 2009 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
|
||||
|
@ -9,341 +9,406 @@
|
|||
* IBM Corporation - initial API and implementation
|
||||
* QNX Software Systems
|
||||
* Markus Schorn (Wind River Systems)
|
||||
* Sergey Prigogin (Google)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.ui.editor;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.jface.action.Action;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.content.IContentType;
|
||||
import org.eclipse.jface.operation.IRunnableWithProgress;
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.IRegion;
|
||||
import org.eclipse.jface.text.ITextSelection;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.LabelProvider;
|
||||
import org.eclipse.jface.window.Window;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
|
||||
import org.eclipse.ui.texteditor.ITextEditor;
|
||||
import org.eclipse.ui.texteditor.IUpdate;
|
||||
import org.eclipse.ui.texteditor.TextEditorAction;
|
||||
|
||||
import org.eclipse.cdt.core.CCorePlugin;
|
||||
import org.eclipse.cdt.core.browser.IQualifiedTypeName;
|
||||
import org.eclipse.cdt.core.browser.QualifiedTypeName;
|
||||
import org.eclipse.cdt.core.dom.ast.DOMException;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||
import org.eclipse.cdt.core.dom.ast.IBinding;
|
||||
import org.eclipse.cdt.core.dom.ast.ICompositeType;
|
||||
import org.eclipse.cdt.core.dom.ast.IEnumeration;
|
||||
import org.eclipse.cdt.core.dom.ast.IFunction;
|
||||
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
|
||||
import org.eclipse.cdt.core.dom.ast.IType;
|
||||
import org.eclipse.cdt.core.dom.ast.ITypedef;
|
||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
|
||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
|
||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
|
||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
|
||||
import org.eclipse.cdt.core.index.IIndex;
|
||||
import org.eclipse.cdt.core.index.IIndexBinding;
|
||||
import org.eclipse.cdt.core.index.IIndexManager;
|
||||
import org.eclipse.cdt.core.index.IIndexFile;
|
||||
import org.eclipse.cdt.core.index.IIndexInclude;
|
||||
import org.eclipse.cdt.core.index.IIndexMacro;
|
||||
import org.eclipse.cdt.core.index.IIndexName;
|
||||
import org.eclipse.cdt.core.index.IndexFilter;
|
||||
import org.eclipse.cdt.core.model.ILanguage;
|
||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||
import org.eclipse.cdt.core.parser.IScannerInfo;
|
||||
import org.eclipse.cdt.core.parser.IScannerInfoProvider;
|
||||
import org.eclipse.cdt.ui.CUIPlugin;
|
||||
import org.eclipse.cdt.ui.IFunctionSummary;
|
||||
import org.eclipse.cdt.ui.IRequiredInclude;
|
||||
import org.eclipse.cdt.ui.browser.typeinfo.TypeInfoLabelProvider;
|
||||
import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
|
||||
import org.eclipse.cdt.ui.text.SharedASTJob;
|
||||
import org.eclipse.cdt.utils.PathUtil;
|
||||
|
||||
import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding;
|
||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
|
||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
|
||||
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
|
||||
import org.eclipse.cdt.internal.corext.codemanipulation.AddIncludesOperation;
|
||||
|
||||
import org.eclipse.cdt.internal.ui.CHelpProviderManager;
|
||||
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
|
||||
import org.eclipse.cdt.internal.ui.actions.WorkbenchRunnableAdapter;
|
||||
import org.eclipse.cdt.internal.ui.text.CWordFinder;
|
||||
import org.eclipse.cdt.internal.ui.util.ExceptionHandler;
|
||||
|
||||
// TODO this is a big TODO.
|
||||
public class AddIncludeOnSelectionAction extends Action implements IUpdate {
|
||||
|
||||
private ITextEditor fEditor;
|
||||
/**
|
||||
* Adds an include statement and, optionally, a 'using' declaration for the currently
|
||||
* selected name.
|
||||
*/
|
||||
public class AddIncludeOnSelectionAction extends TextEditorAction {
|
||||
private ITranslationUnit fTu;
|
||||
private IProject fProject;
|
||||
private String[] fIncludePath;
|
||||
private IRequiredInclude[] fRequiredIncludes;
|
||||
private String[] fUsings;
|
||||
|
||||
class RequiredIncludes implements IRequiredInclude {
|
||||
String name;
|
||||
boolean isStandard;
|
||||
|
||||
RequiredIncludes(String n) {
|
||||
name = n;
|
||||
isStandard = true;
|
||||
}
|
||||
|
||||
RequiredIncludes(String n, boolean isStandard) {
|
||||
name = n;
|
||||
this.isStandard = isStandard;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.ui.IRequiredInclude#getIncludeName()
|
||||
*/
|
||||
public String getIncludeName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.ui.IRequiredInclude#isStandard()
|
||||
*/
|
||||
public boolean isStandard() {
|
||||
return isStandard;
|
||||
}
|
||||
}
|
||||
private String[] fUsingDeclarations;
|
||||
|
||||
public AddIncludeOnSelectionAction(ITextEditor editor) {
|
||||
super(CEditorMessages.AddIncludeOnSelection_label);
|
||||
setToolTipText(CEditorMessages.AddIncludeOnSelection_tooltip);
|
||||
setDescription(CEditorMessages.AddIncludeOnSelection_description);
|
||||
super(CEditorMessages.getBundleForConstructedKeys(), "AddIncludeOnSelection.", editor); //$NON-NLS-1$
|
||||
|
||||
fEditor= editor;
|
||||
CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this, ICHelpContextIds.ADD_INCLUDE_ON_SELECTION_ACTION);
|
||||
CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this,
|
||||
ICHelpContextIds.ADD_INCLUDE_ON_SELECTION_ACTION);
|
||||
}
|
||||
|
||||
private void addInclude(ITranslationUnit tu) {
|
||||
AddIncludesOperation op= new AddIncludesOperation(tu, fRequiredIncludes, fUsings, false);
|
||||
private void insertInclude(IRequiredInclude[] includes, String[] usings) {
|
||||
AddIncludesOperation op= new AddIncludesOperation(fTu, includes, usings, false);
|
||||
try {
|
||||
PlatformUI.getWorkbench().getProgressService().runInUI(
|
||||
PlatformUI.getWorkbench().getProgressService(),
|
||||
new WorkbenchRunnableAdapter(op), op.getScheduleRule());
|
||||
new WorkbenchRunnableAdapter(op), op.getSchedulingRule());
|
||||
} catch (InvocationTargetException e) {
|
||||
ExceptionHandler.handle(e, getShell(), CEditorMessages.AddIncludeOnSelection_error_message1, null);
|
||||
ExceptionHandler.handle(e, getShell(), CEditorMessages.AddIncludeOnSelection_error_title,
|
||||
CEditorMessages.AddIncludeOnSelection_insertion_failed);
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing. Operation has been canceled.
|
||||
}
|
||||
}
|
||||
|
||||
protected ITranslationUnit getTranslationUnit () {
|
||||
ITranslationUnit unit = null;
|
||||
if (fEditor != null) {
|
||||
IEditorInput editorInput= fEditor.getEditorInput();
|
||||
unit = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editorInput);
|
||||
private static ITranslationUnit getTranslationUnit(ITextEditor editor) {
|
||||
if (editor == null) {
|
||||
return null;
|
||||
}
|
||||
return unit;
|
||||
return CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput());
|
||||
}
|
||||
|
||||
private Shell getShell() {
|
||||
return fEditor.getSite().getShell();
|
||||
return getTextEditor().getSite().getShell();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ITranslationUnit tu= getTranslationUnit();
|
||||
if (tu == null) {
|
||||
fTu = getTranslationUnit(getTextEditor());
|
||||
if (fTu == null) {
|
||||
return;
|
||||
}
|
||||
IIndex index;
|
||||
fProject = fTu.getCProject().getProject();
|
||||
IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(fProject);
|
||||
fIncludePath = null;
|
||||
if (provider != null) {
|
||||
IScannerInfo info = provider.getScannerInformation(fTu.getResource());
|
||||
if (info != null) {
|
||||
fIncludePath = info.getIncludePaths();
|
||||
}
|
||||
}
|
||||
if (fIncludePath == null) {
|
||||
fIncludePath = new String[0];
|
||||
}
|
||||
|
||||
try {
|
||||
index = CCorePlugin.getIndexManager().getIndex(tu.getCProject(), IIndexManager.ADD_DEPENDENCIES);
|
||||
index.acquireReadLock();
|
||||
try {
|
||||
extractIncludes(fEditor, index);
|
||||
addInclude(tu);
|
||||
} finally {
|
||||
index.releaseReadLock();
|
||||
final ISelection selection= getTextEditor().getSelectionProvider().getSelection();
|
||||
if (selection.isEmpty() || !(selection instanceof ITextSelection)) {
|
||||
return;
|
||||
}
|
||||
if (!validateEditorInputState()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] lookupName = new String[1];
|
||||
|
||||
SharedASTJob job = new SharedASTJob(CEditorMessages.AddIncludeOnSelection_label, fTu) {
|
||||
@Override
|
||||
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
|
||||
deduceInclude((ITextSelection) selection, ast, lookupName);
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
};
|
||||
job.schedule();
|
||||
job.join();
|
||||
|
||||
if (fRequiredIncludes == null || fRequiredIncludes.length == 0 && lookupName[0].length() > 0) {
|
||||
// Try contribution from plugins.
|
||||
IFunctionSummary fs = findContribution(lookupName[0]);
|
||||
if (fs != null) {
|
||||
fRequiredIncludes = fs.getIncludes();
|
||||
String ns = fs.getNamespace();
|
||||
if (ns != null && ns.length() > 0) {
|
||||
fUsingDeclarations = new String[] { fs.getNamespace() };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (fRequiredIncludes != null && fRequiredIncludes.length >= 0) {
|
||||
insertInclude(fRequiredIncludes, fUsingDeclarations);
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
CUIPlugin.log(e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
fUsings = null;
|
||||
fRequiredIncludes = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used by ElementListSelectionDialog for user to choose which declarations/
|
||||
* definitions for "add include" when there are more than one to choose from.
|
||||
*/
|
||||
private static class DisplayName extends Object {
|
||||
private IIndexName name;
|
||||
private IIndexBinding binding;
|
||||
|
||||
public DisplayName(IIndexName name, IIndexBinding binding) {
|
||||
this.name = name;
|
||||
this.binding= binding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
if (binding != null) {
|
||||
return getBindingQualifiedName(binding) + " - " + name.getFileLocation().getFileName(); //$NON-NLS-1$
|
||||
}
|
||||
return null;
|
||||
} catch (CoreException e) {
|
||||
CUIPlugin.log(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IIndexName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public IIndexBinding getBinding() {
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the includes for the given selection. This can be both used to perform
|
||||
* the work as well as being invoked when there is a change. The actual results
|
||||
* can and should be cached as the lookup process could be potentially costly.
|
||||
* the work as well as being invoked when there is a change.
|
||||
* @param index
|
||||
*/
|
||||
private void extractIncludes(ITextEditor editor, IIndex index) {
|
||||
if (editor == null) {
|
||||
private void deduceInclude(ITextSelection selection, IASTTranslationUnit ast, String[] lookupName)
|
||||
throws CoreException {
|
||||
IASTNodeSelector selector = ast.getNodeSelector(fTu.getLocation().toOSString());
|
||||
IASTName name = selector.findEnclosingName(selection.getOffset(), selection.getLength());
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
char[] nameChars = name.toCharArray();
|
||||
IBinding binding = name.resolveBinding();
|
||||
try {
|
||||
if (binding instanceof ICPPVariable) {
|
||||
IType type = ((ICPPVariable) binding).getType();
|
||||
type = SemanticUtil.getNestedType(type,
|
||||
SemanticUtil.CVQ | SemanticUtil.PTR | SemanticUtil.ARRAY | SemanticUtil.REF);
|
||||
if (type instanceof IBinding) {
|
||||
binding = (IBinding) type;
|
||||
nameChars = binding.getNameCharArray();
|
||||
}
|
||||
}
|
||||
} catch (DOMException e) {
|
||||
CUIPlugin.log(e);
|
||||
}
|
||||
if (nameChars.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
IIndex index = ast.getIndex();
|
||||
final IndexFilter filter = IndexFilter.getDeclaredBindingFilter(ast.getLinkage().getLinkageID(), false);
|
||||
IIndexBinding[] bindings= index.findBindings(nameChars, false, filter, new NullProgressMonitor());
|
||||
final Map<String, IncludeCandidate> candidatesMap= new HashMap<String, IncludeCandidate>();
|
||||
for (IIndexBinding indexBinding : bindings) {
|
||||
IIndexName[] definitions= null;
|
||||
// class, struct, union, enum
|
||||
if (indexBinding instanceof ICompositeType || indexBinding instanceof IEnumeration) {
|
||||
definitions= index.findDefinitions(indexBinding);
|
||||
} else if (indexBinding instanceof ITypedef ||
|
||||
(indexBinding instanceof IFunction && !(indexBinding instanceof ICPPMethod))) {
|
||||
definitions= index.findDeclarations(indexBinding);
|
||||
}
|
||||
if (definitions != null) {
|
||||
for (IIndexName definition : definitions) {
|
||||
considerForInclusion(definition, indexBinding, index, candidatesMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
IIndexMacro[] macros = index.findMacros(nameChars, filter, new NullProgressMonitor());
|
||||
for (IIndexMacro macro : macros) {
|
||||
IIndexName definition = macro.getDefinition();
|
||||
considerForInclusion(definition, macro, index, candidatesMap);
|
||||
}
|
||||
|
||||
ISelection s= editor.getSelectionProvider().getSelection();
|
||||
IDocument doc= editor.getDocumentProvider().getDocument(editor.getEditorInput());
|
||||
|
||||
if (s.isEmpty() || !(s instanceof ITextSelection) || doc == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ITextSelection selection= (ITextSelection) s;
|
||||
try {
|
||||
IRegion region = CWordFinder.findWord(doc, selection.getOffset());
|
||||
if (region == null || region.getLength() == 0) {
|
||||
return;
|
||||
}
|
||||
String name = doc.get(region.getOffset(), region.getLength());
|
||||
if (name.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try contribution from plugins.
|
||||
IFunctionSummary fs = findContribution(name);
|
||||
if (fs != null) {
|
||||
fRequiredIncludes = fs.getIncludes();
|
||||
String ns = fs.getNamespace();
|
||||
if (ns != null && ns.length() > 0) {
|
||||
fUsings = new String[] { fs.getNamespace() };
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
IndexFilter filter= IndexFilter.ALL_DECLARED_OR_IMPLICIT;
|
||||
IIndexBinding[] bindings= index.findBindings(name.toCharArray(), false, filter,
|
||||
new NullProgressMonitor());
|
||||
ArrayList<DisplayName> pdomNames= new ArrayList<DisplayName>();
|
||||
for (int i = 0; i < bindings.length; ++i) {
|
||||
IIndexBinding binding= bindings[i];
|
||||
IIndexName[] defs= null;
|
||||
// class, struct union, enumeration
|
||||
if (binding instanceof ICompositeType || binding instanceof IEnumeration) {
|
||||
defs= index.findDefinitions(binding);
|
||||
} else if (binding instanceof ITypedef || binding instanceof IFunction) {
|
||||
defs= index.findDeclarations(binding);
|
||||
}
|
||||
if (defs != null) {
|
||||
for (IIndexName def : defs) {
|
||||
pdomNames.add(new DisplayName(def, binding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pdomNames.size() > 1) {
|
||||
ElementListSelectionDialog dialog= new ElementListSelectionDialog(getShell(), new TypeInfoLabelProvider(TypeInfoLabelProvider.SHOW_NAME_ONLY));
|
||||
dialog.setElements(pdomNames.toArray());
|
||||
final ArrayList<IncludeCandidate> candidates = new ArrayList<IncludeCandidate>(candidatesMap.values());
|
||||
if (candidates.size() > 1) {
|
||||
runInUIThread(new Runnable() {
|
||||
public void run() {
|
||||
ElementListSelectionDialog dialog=
|
||||
new ElementListSelectionDialog(getShell(), new LabelProvider());
|
||||
dialog.setElements(candidates.toArray());
|
||||
dialog.setTitle(CEditorMessages.AddIncludeOnSelection_label);
|
||||
dialog.setMessage(CEditorMessages.AddIncludeOnSelection_description);
|
||||
if (dialog.open() == Window.OK) {
|
||||
//get selection
|
||||
Object[] selects = dialog.getResult();
|
||||
|
||||
fRequiredIncludes = new IRequiredInclude[selects.length];
|
||||
List<String> usings = new ArrayList<String>(selects.length);
|
||||
for (int i = 0; i < fRequiredIncludes.length; i++) {
|
||||
IRequiredInclude include = getRequiredInclude(
|
||||
((DisplayName)selects[i]).getName().getFileLocation().getFileName(), getTranslationUnit());
|
||||
if (include != null) {
|
||||
fRequiredIncludes[i] = include;
|
||||
IIndexBinding binding = ((DisplayName)selects[i]).getBinding();
|
||||
if (binding instanceof ICPPBinding)
|
||||
{
|
||||
//find the enclosing namespace, if there's one
|
||||
IQualifiedTypeName qualifiedName = new QualifiedTypeName(getBindingQualifiedName(binding));
|
||||
String qualifiedEnclosingName = (new QualifiedTypeName(qualifiedName.getEnclosingNames())).getFullyQualifiedName();
|
||||
if (!qualifiedEnclosingName.equals("")) //$NON-NLS-1$
|
||||
usings.add(qualifiedEnclosingName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (usings.size() > 0)
|
||||
{
|
||||
fUsings = new String[usings.size()];
|
||||
for (int i = 0; i < usings.size(); i++)
|
||||
{
|
||||
fUsings[i] = usings.get(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (pdomNames.size() == 1) {
|
||||
// we should use the IIndexName.getLocation here rather than getFileLocation
|
||||
String fileName = (pdomNames.get(0)).getName().getFileLocation().getFileName();
|
||||
fRequiredIncludes = new IRequiredInclude[] {getRequiredInclude(fileName, getTranslationUnit())};
|
||||
IIndexBinding binding = (pdomNames.get(0)).getBinding();
|
||||
|
||||
if (binding instanceof ICPPBinding) {
|
||||
//find the enclosing namespace, if there's one
|
||||
IQualifiedTypeName qualifiedName = new QualifiedTypeName(getBindingQualifiedName(binding));
|
||||
String qualifiedEnclosingName = new QualifiedTypeName(qualifiedName.getEnclosingNames()).getFullyQualifiedName();
|
||||
if (!qualifiedEnclosingName.equals("")) //$NON-NLS-1$
|
||||
fUsings = new String[] {qualifiedEnclosingName};
|
||||
candidates.clear();
|
||||
candidates.add((IncludeCandidate) dialog.getFirstResult());
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Try the type caching.
|
||||
if (fRequiredIncludes == null && fUsings == null) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Do a full search
|
||||
if (fRequiredIncludes == null && fUsings == null) {
|
||||
fRequiredIncludes = null;
|
||||
fUsingDeclarations = null;
|
||||
if (candidates.size() == 1) {
|
||||
IncludeCandidate candidate = candidates.get(0);
|
||||
fRequiredIncludes = new IRequiredInclude[] { candidate.getInclude() };
|
||||
IIndexBinding indexBinding = candidate.getBinding();
|
||||
|
||||
if (indexBinding instanceof ICPPBinding && !(indexBinding instanceof IIndexMacro)) {
|
||||
// Decide what 'using' declaration should be added along with the include.
|
||||
if (binding == null) {
|
||||
binding = new ProblemBinding(name, IProblemBinding.SEMANTIC_NAME_NOT_FOUND);
|
||||
}
|
||||
String usingDeclaration = deduceUsingDeclaration(binding, indexBinding);
|
||||
if (usingDeclaration != null)
|
||||
fUsingDeclarations = new String[] { usingDeclaration };
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
MessageDialog.openError(getShell(), CEditorMessages.AddIncludeOnSelection_error_message3,
|
||||
CEditorMessages.AddIncludeOnSelection_error_message4 + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private IFunctionSummary findContribution (final String name) {
|
||||
/**
|
||||
* Adds an include candidate to the <code>candidates</code> map if the file containing
|
||||
* the definition is suitable for inclusion.
|
||||
*/
|
||||
private void considerForInclusion(IIndexName definition, IIndexBinding binding,
|
||||
IIndex index, Map<String, IncludeCandidate> candidates) throws CoreException {
|
||||
IIndexFile file = definition.getFile();
|
||||
// Consider the file for inclusion only if it is not a source file,
|
||||
// or a source file that was already included by some other file.
|
||||
if (!isSource(getPath(file)) || index.findIncludedBy(file, 0).length > 0) {
|
||||
IIndexFile representativeFile = getRepresentativeFile(file, index);
|
||||
IRequiredInclude include = getRequiredInclude(representativeFile, index);
|
||||
IncludeCandidate candidate = new IncludeCandidate(binding, include);
|
||||
if (!candidates.containsKey(candidate.toString())) {
|
||||
candidates.put(candidate.toString(), candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String deduceUsingDeclaration(IBinding source, IBinding target) {
|
||||
if (source.equals(target)) {
|
||||
return null; // No using declaration is needed.
|
||||
}
|
||||
ArrayList<String> targetChain = getUsingChain(target);
|
||||
if (targetChain.size() <= 1) {
|
||||
return null; // Target is not in a namespace
|
||||
}
|
||||
ArrayList<String> sourceChain = getUsingChain(source);
|
||||
if (sourceChain.size() >= targetChain.size()) {
|
||||
int j = targetChain.size();
|
||||
for (int i = sourceChain.size(); --j >= 1 && --i >= 1;) {
|
||||
if (!sourceChain.get(i).equals(targetChain.get(j))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j <= 0) {
|
||||
return null; // Source is in the target's namespace
|
||||
}
|
||||
}
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = targetChain.size(); --i >= 0;) {
|
||||
if (buf.length() > 0) {
|
||||
buf.append("::"); //$NON-NLS-1$
|
||||
}
|
||||
buf.append(targetChain.get(i));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private ArrayList<String> getUsingChain(IBinding binding) {
|
||||
ArrayList<String> chain = new ArrayList<String>(4);
|
||||
try {
|
||||
for (; binding != null; binding = binding.getOwner()) {
|
||||
String name = binding.getName();
|
||||
if (binding instanceof ICPPNamespace) {
|
||||
if (name.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
chain.clear();
|
||||
}
|
||||
chain.add(name);
|
||||
}
|
||||
} catch (DOMException e) {
|
||||
CUIPlugin.log(e);
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a header file, decides if this header file should be included directly or
|
||||
* through another header file. For example, <code>bits/stl_map.h</code> is not supposed
|
||||
* to be included directly, but should be represented by <code>map</code>.
|
||||
* @return the header file to include.
|
||||
*/
|
||||
private IIndexFile getRepresentativeFile(IIndexFile headerFile, IIndex index) {
|
||||
try {
|
||||
if (ResourceLookup.findFilesForLocationURI(headerFile.getLocation().getURI()).length > 0) {
|
||||
return headerFile;
|
||||
}
|
||||
// TODO(sprigogin): Change to ArrayDeque when Java 5 support is no longer needed.
|
||||
LinkedList<IIndexFile> front = new LinkedList<IIndexFile>();
|
||||
front.add(headerFile);
|
||||
HashSet<IIndexFile> processed = new HashSet<IIndexFile>();
|
||||
processed.add(headerFile);
|
||||
while (!front.isEmpty()) {
|
||||
IIndexFile file = front.remove();
|
||||
// A header without an extension is a good candidate for inclusion into a C++ source file.
|
||||
if (fTu.isCXXLanguage() && !hasExtension(getPath(file))) {
|
||||
return file;
|
||||
}
|
||||
IIndexInclude[] includes = index.findIncludedBy(file, 0);
|
||||
for (IIndexInclude include : includes) {
|
||||
IIndexFile includer = include.getIncludedBy();
|
||||
if (!processed.contains(includer)) {
|
||||
URI uri = includer.getLocation().getURI();
|
||||
if (isSource(uri.getPath()) || ResourceLookup.findFilesForLocationURI(uri).length > 0) {
|
||||
return file;
|
||||
}
|
||||
front.add(includer);
|
||||
processed.add(includer);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
CUIPlugin.log(e);
|
||||
}
|
||||
return headerFile;
|
||||
}
|
||||
|
||||
private boolean hasExtension(String path) {
|
||||
return path.indexOf('.', path.lastIndexOf('/') + 1) >= 0;
|
||||
}
|
||||
|
||||
private IFunctionSummary findContribution(final String name) {
|
||||
final IFunctionSummary[] fs = new IFunctionSummary[1];
|
||||
IRunnableWithProgress op = new IRunnableWithProgress() {
|
||||
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
|
||||
ICHelpInvocationContext context = new ICHelpInvocationContext() {
|
||||
|
||||
public IProject getProject() {
|
||||
ITranslationUnit u = getTranslationUnit();
|
||||
if (u != null) {
|
||||
return u.getCProject().getProject();
|
||||
}
|
||||
return null;
|
||||
return fProject;
|
||||
}
|
||||
|
||||
public ITranslationUnit getTranslationUnit() {
|
||||
return AddIncludeOnSelectionAction.this.getTranslationUnit();
|
||||
return fTu;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -353,91 +418,204 @@ public class AddIncludeOnSelectionAction extends Action implements IUpdate {
|
|||
try {
|
||||
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(op);
|
||||
} catch (InvocationTargetException e) {
|
||||
ExceptionHandler.handle(e, getShell(), CEditorMessages.AddIncludeOnSelection_error_message1, null);
|
||||
ExceptionHandler.handle(e, getShell(), CEditorMessages.AddIncludeOnSelection_error_title,
|
||||
CEditorMessages.AddIncludeOnSelection_help_provider_error);
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing. Operation has been canceled.
|
||||
}
|
||||
return fs[0];
|
||||
}
|
||||
|
||||
public void setContentEditor(ITextEditor editor) {
|
||||
fEditor= editor;
|
||||
private void runInUIThread(Runnable runnable) {
|
||||
if (Display.getCurrent() != null) {
|
||||
runnable.run();
|
||||
} else {
|
||||
Display.getDefault().syncExec(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
setEnabled(getTranslationUnit() != null);
|
||||
ITextEditor editor = getTextEditor();
|
||||
setEnabled(editor != null && getTranslationUnit(editor) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file is a source file (.c, .cpp, .cc, etc). Header files are not considered source files.
|
||||
* @return Returns <code>true</code> if the the file is a source file.
|
||||
*/
|
||||
private boolean isSource(String filename) {
|
||||
IContentType ct= CCorePlugin.getContentType(fProject, filename);
|
||||
if (ct != null) {
|
||||
String id = ct.getId();
|
||||
if (CCorePlugin.CONTENT_TYPE_CSOURCE.equals(id) || CCorePlugin.CONTENT_TYPE_CXXSOURCE.equals(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String getPath(IIndexFile file) throws CoreException {
|
||||
return file.getLocation().getURI().getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the RequiredInclude object to be added to the include list
|
||||
* @param path - the full path of the file to include
|
||||
* @param tu - the translation unit which requires the include
|
||||
* @return the required include
|
||||
* @throws CoreException
|
||||
*/
|
||||
private IRequiredInclude getRequiredInclude(String path, ITranslationUnit tu) {
|
||||
if (path != null) {
|
||||
IPath typeLocation = new Path(path);
|
||||
IProject project = tu.getCProject().getProject();
|
||||
IPath projectLocation = project.getLocation();
|
||||
IPath workspaceLocation = project.getWorkspace().getRoot().getLocation();
|
||||
IPath headerLocation = tu.getResource().getLocation();
|
||||
boolean isSystemIncludePath = false;
|
||||
private IRequiredInclude getRequiredInclude(IIndexFile file, IIndex index) throws CoreException {
|
||||
IIndexInclude[] includes;
|
||||
includes = index.findIncludedBy(file);
|
||||
if (includes.length > 0) {
|
||||
// Let the existing includes vote. To be eligible to vote, an include
|
||||
// has to be resolvable in the context of the current translation unit.
|
||||
int systemIncludeVotes = 0;
|
||||
String[] ballotBox = new String[includes.length];
|
||||
for (int i = 0; i < includes.length; i++) {
|
||||
IIndexInclude include = includes[i];
|
||||
if (isResolvable(include)) {
|
||||
ballotBox[i] = include.getFullName();
|
||||
if (include.isSystemInclude()) {
|
||||
systemIncludeVotes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Arrays.sort(ballotBox);
|
||||
String contender = ballotBox[0];
|
||||
int votes = 1;
|
||||
String winner = contender;
|
||||
int winnerVotes = votes;
|
||||
for (int i = 1; i < ballotBox.length; i++) {
|
||||
if (!ballotBox[i].equals(contender)) {
|
||||
contender = ballotBox[i];
|
||||
votes = 1;
|
||||
}
|
||||
votes++;
|
||||
if (votes > winnerVotes) {
|
||||
winner = contender;
|
||||
winnerVotes = votes;
|
||||
}
|
||||
}
|
||||
return new RequiredInclude(winner, systemIncludeVotes * 2 >= includes.length);
|
||||
}
|
||||
|
||||
IPath includePath = makeRelativePathToProjectIncludes(typeLocation, tu);
|
||||
if (includePath != null && !projectLocation.isPrefixOf(typeLocation)) {
|
||||
isSystemIncludePath = true;
|
||||
}
|
||||
//create a relative path - the include file is in the same project as the file we're currently at
|
||||
else if (projectLocation.isPrefixOf(typeLocation)
|
||||
&& projectLocation.isPrefixOf(headerLocation)) {
|
||||
includePath = PathUtil.makeRelativePath(typeLocation, headerLocation.removeLastSegments(1));
|
||||
}
|
||||
//create a relative path - the include file is in the same workspace as the file we're currently at
|
||||
else if (workspaceLocation.isPrefixOf(typeLocation))
|
||||
{
|
||||
includePath = PathUtil.makeRelativePath(typeLocation, projectLocation);
|
||||
}
|
||||
if (includePath == null)
|
||||
includePath = typeLocation; //the full path
|
||||
return new RequiredIncludes(includePath.toString(), isSystemIncludePath);
|
||||
// The file has never been included before.
|
||||
IPath targetLocation = PathUtil.getCanonicalPath(new Path(file.getLocation().getURI().getPath()));
|
||||
IPath sourceLocation = PathUtil.getCanonicalPath(fTu.getResource().getLocation());
|
||||
boolean isSystemIncludePath = false;
|
||||
|
||||
IPath path = PathUtil.makeRelativePathToIncludes(targetLocation, fIncludePath);
|
||||
if (path != null &&
|
||||
ResourceLookup.findFilesForLocationURI(URI.create(targetLocation.toString())).length == 0) {
|
||||
// A header file in the include path but outside the workspace is included with angle brackets.
|
||||
isSystemIncludePath = true;
|
||||
}
|
||||
return null;
|
||||
if (path == null) {
|
||||
IPath sourceDirectory = sourceLocation.removeLastSegments(1);
|
||||
if (PathUtil.isPrefix(sourceDirectory, targetLocation)) {
|
||||
path = targetLocation.removeFirstSegments(sourceDirectory.segmentCount());
|
||||
} else {
|
||||
path = targetLocation;
|
||||
}
|
||||
}
|
||||
return new RequiredInclude(path.toString(), isSystemIncludePath);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a relative path to the project includes.
|
||||
* @param fullPath the full path to the project
|
||||
* @param tu a translation unit in the project
|
||||
* @return IPath corresponding to a relative path to the project includes
|
||||
* Returns <code>true</code> if the given include can be resolved in the context of
|
||||
* the current translation unit.
|
||||
*/
|
||||
private static IPath makeRelativePathToProjectIncludes(IPath fullPath, ITranslationUnit tu) {
|
||||
IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(tu.getCProject().getProject());
|
||||
if (provider != null) {
|
||||
IScannerInfo info = provider.getScannerInformation(tu.getResource());
|
||||
if (info != null) {
|
||||
return PathUtil.makeRelativePathToIncludes(fullPath, info.getIncludePaths());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isResolvable(IIndexInclude include) {
|
||||
try {
|
||||
File target = new File(include.getIncludesLocation().getURI().getPath());
|
||||
String includeName = include.getFullName();
|
||||
for (String dir : fIncludePath) {
|
||||
if (target.equals(new File(dir, includeName))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (include.isSystemInclude()) {
|
||||
return false;
|
||||
}
|
||||
return target.equals(new File(new File(fTu.getLocationURI().getPath()).getParent(), includeName));
|
||||
} catch (CoreException e) {
|
||||
CUIPlugin.log(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully qualified name for a given PDOMBinding
|
||||
* @param pdomBinding
|
||||
* Get the fully qualified name for a given index binding.
|
||||
* @param binding
|
||||
* @return binding's fully qualified name
|
||||
* @throws CoreException
|
||||
*/
|
||||
private static String getBindingQualifiedName(IIndexBinding binding) throws CoreException {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
String[] qname= binding.getQualifiedName();
|
||||
for (int i = 0; i < qname.length; i++) {
|
||||
if (i > 0) {
|
||||
buf.append("::"); //$NON-NLS-1$
|
||||
}
|
||||
buf.append(qname[i]);
|
||||
return CPPVisitor.renderQualifiedName(qname);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used by ElementListSelectionDialog for user to choose which declarations/
|
||||
* definitions for "add include" when there are more than one to choose from.
|
||||
*/
|
||||
private static class IncludeCandidate {
|
||||
private final IIndexBinding binding;
|
||||
private final IRequiredInclude include;
|
||||
private final String label;
|
||||
|
||||
public IncludeCandidate(IIndexBinding binding, IRequiredInclude include) throws CoreException {
|
||||
this.binding = binding;
|
||||
this.include = include;
|
||||
this.label = getBindingQualifiedName(binding) + " - " + include.toString(); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public IIndexBinding getBinding() {
|
||||
return binding;
|
||||
}
|
||||
|
||||
public IRequiredInclude getInclude() {
|
||||
return include;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RequiredInclude implements IRequiredInclude {
|
||||
final String includeName;
|
||||
final boolean isSystem;
|
||||
|
||||
RequiredInclude(String includeName, boolean isSystem) {
|
||||
this.includeName = includeName;
|
||||
this.isSystem = isSystem;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.ui.IRequiredInclude#getIncludeName()
|
||||
*/
|
||||
public String getIncludeName() {
|
||||
return includeName;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.ui.IRequiredInclude#isStandard()
|
||||
*/
|
||||
public boolean isStandard() {
|
||||
return isSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(includeName.length() + 2);
|
||||
buf.append(isSystem ? '<' : '"');
|
||||
buf.append(includeName);
|
||||
buf.append(isSystem ? '>' : '"');
|
||||
return buf.toString();
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,22 +10,36 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.ui.editor;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.eclipse.osgi.util.NLS;
|
||||
|
||||
public final class CEditorMessages extends NLS {
|
||||
private static final String BUNDLE_FOR_CONSTRUCTED_KEYS=
|
||||
"org.eclipse.cdt.internal.ui.editor.ConstructedCEditorMessages"; //$NON-NLS-1$
|
||||
private static ResourceBundle fgBundleForConstructedKeys= ResourceBundle.getBundle(BUNDLE_FOR_CONSTRUCTED_KEYS);
|
||||
|
||||
private static final String BUNDLE_NAME = "org.eclipse.cdt.internal.ui.editor.CEditorMessages";//$NON-NLS-1$
|
||||
/**
|
||||
* Returns the message bundle which contains constructed keys.
|
||||
*
|
||||
* @return the message bundle
|
||||
* @since 5.1
|
||||
*/
|
||||
public static ResourceBundle getBundleForConstructedKeys() {
|
||||
return fgBundleForConstructedKeys;
|
||||
}
|
||||
|
||||
private static final String BUNDLE_NAME = CEditorMessages.class.getName();
|
||||
|
||||
private CEditorMessages() {
|
||||
// Do not instantiate
|
||||
}
|
||||
|
||||
public static String AddIncludeOnSelection_description;
|
||||
public static String AddIncludeOnSelection_error_message1;
|
||||
public static String AddIncludeOnSelection_error_message3;
|
||||
public static String AddIncludeOnSelection_error_message4;
|
||||
public static String AddIncludeOnSelection_label;
|
||||
public static String AddIncludeOnSelection_tooltip;
|
||||
public static String AddIncludeOnSelection_description;
|
||||
public static String AddIncludeOnSelection_error_title;
|
||||
public static String AddIncludeOnSelection_insertion_failed;
|
||||
public static String AddIncludeOnSelection_help_provider_error;
|
||||
public static String AddIncludesOperation_description;
|
||||
public static String ShowInCView_description;
|
||||
public static String ShowInCView_label;
|
||||
|
|
|
@ -10,14 +10,14 @@
|
|||
# QNX Software System
|
||||
# Anton Leherbauer (Wind River Systems)
|
||||
# Markus Schorn (Wind River Systems)
|
||||
# Sergey Prigogin (Google)
|
||||
#########################################
|
||||
|
||||
AddIncludeOnSelection_description=Add include statement on selection
|
||||
AddIncludeOnSelection_error_message1=Adding include statements failed
|
||||
AddIncludeOnSelection_error_message3=Error
|
||||
AddIncludeOnSelection_error_message4=BadLocationException:
|
||||
AddIncludeOnSelection_label=Add Include
|
||||
AddIncludeOnSelection_tooltip=Add Include Statement on Selection
|
||||
AddIncludeOnSelection_description=Add include statement for selected name
|
||||
AddIncludeOnSelection_error_title=Error Adding Include
|
||||
AddIncludeOnSelection_insertion_failed=Adding include statements failed
|
||||
AddIncludeOnSelection_help_provider_error=Help provider error
|
||||
AddIncludesOperation_description=Adding include statement
|
||||
|
||||
ShowInCView_description=Show the current resource in the C/C++ Projects view
|
||||
|
|
|
@ -10,11 +10,12 @@
|
|||
# QNX Software System
|
||||
# Anton Leherbauer (Wind River Systems)
|
||||
# Markus Schorn (Wind River Systems)
|
||||
# Sergey Prigogin (Google)
|
||||
#########################################
|
||||
|
||||
AddIncludeOnSelection.description=Add include statement on selection
|
||||
AddIncludeOnSelection.description=Add include statement for selected name
|
||||
AddIncludeOnSelection.label=Add Include
|
||||
AddIncludeOnSelection.tooltip=Add Include Statement on Selection
|
||||
AddIncludeOnSelection.tooltip=Add Include Statement for Selected Name
|
||||
|
||||
OpenOutline.label= Quick Out&line
|
||||
OpenOutline.tooltip= Shows the Quick Outline of Editor Input
|
||||
|
|
Loading…
Add table
Reference in a new issue