/******************************************************************************* * Copyright (c) 2014 Bruno Medeiros and other Contributors. * 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: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package dtool.engine.operations; import static melnorme.utilbox.core.Assert.AssertNamespace.assertEquals; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import melnorme.lang.tooling.ast.ASTNodeFinder; import melnorme.lang.tooling.ast.INamedElementNode; import melnorme.lang.tooling.ast.SourceRange; import melnorme.lang.tooling.context.ISemanticContext; import melnorme.lang.tooling.engine.ErrorElement; import melnorme.lang.tooling.symbols.INamedElement; import melnorme.utilbox.collections.Collection2; import melnorme.utilbox.core.CommonException; import melnorme.utilbox.misc.Location; import dtool.ast.definitions.DefSymbol; import dtool.ast.definitions.Module; import dtool.ast.references.CommonQualifiedReference; import dtool.ast.references.NamedReference; import dtool.ast.references.Reference; import dtool.engine.ResolvedModule; import dtool.engine.SemanticManager; import dtool.engine.operations.FindDefinitionResult.FindDefinitionResultEntry; public class FindDefinitionOperation extends AbstractDToolOperation { public static final String FIND_DEF_PickedElementAlreadyADefinition = "Element next to cursor is already a definition, not a reference."; public static final String FIND_DEF_NoReferenceFoundAtCursor = "No reference found next to cursor."; public static final String FIND_DEF_MISSING_REFERENCE_AT_CURSOR = FIND_DEF_NoReferenceFoundAtCursor; public static final String FIND_DEF_NoNameReferenceAtCursor = "No name reference found next to cursor."; public static final String FIND_DEF_ReferenceResolveFailed = "Definition not found for reference: "; public FindDefinitionOperation(SemanticManager semanticManager, Path filePath, int offset, Path compilerPath, String dubPath) throws CommonException { super(semanticManager, filePath, offset, compilerPath, dubPath); } public FindDefinitionResult findDefinition() throws CommonException { final ResolvedModule resolvedModule = getResolvedModule(fileLoc); final ISemanticContext mr = resolvedModule.getSemanticContext(); Module module = resolvedModule.getModuleNode(); assertEquals(module.compilationUnitPath, fileLoc.path); return findDefinition(module, offset, mr); } public static FindDefinitionResult findDefinition(Module module, final int offset, final ISemanticContext mr) { ASTNodeFinder nodeFinder = new ASTNodeFinder(module, offset, true); if(nodeFinder.matchOnLeft instanceof NamedReference) { NamedReference namedReference = (NamedReference) nodeFinder.matchOnLeft; return doFindDefinition(namedReference, mr); } else if(nodeFinder.match instanceof Reference) { Reference reference = (Reference) nodeFinder.match; return doFindDefinition(reference, mr); } else if(nodeFinder.match instanceof DefSymbol){ return new FindDefinitionResult(FIND_DEF_PickedElementAlreadyADefinition); } return new FindDefinitionResult(FIND_DEF_NoReferenceFoundAtCursor); } public static FindDefinitionResult doFindDefinition(Reference reference, final ISemanticContext mr) { if(reference instanceof NamedReference) { NamedReference namedReference = (NamedReference) reference; if(namedReference.isMissingCoreReference()) { return new FindDefinitionResult(FIND_DEF_MISSING_REFERENCE_AT_CURSOR, namedReference); } if(namedReference instanceof CommonQualifiedReference) { // Then the cursor is not actually next to an identifier. return new FindDefinitionResult(FIND_DEF_NoNameReferenceAtCursor); } else { return doFindDefinitionForRef(namedReference, mr); } } else { return new FindDefinitionResult(FIND_DEF_NoNameReferenceAtCursor); } } public static FindDefinitionResult doFindDefinitionForRef(Reference ref, ISemanticContext context) { INamedElement resolveResult = ref.resolveTargetElement(context); if(resolveResult instanceof ErrorElement) { return new FindDefinitionResult(FIND_DEF_ReferenceResolveFailed + ref.toStringAsCode(), ref); } // TODO need to refactor use of OverloadedNamedElement Collection2<INamedElement> namedElements = Reference.resolveResultToCollection(resolveResult); List<FindDefinitionResultEntry> results = new ArrayList<>(); for (INamedElement namedElement : namedElements) { final INamedElementNode node = namedElement.resolveUnderlyingNode(); Location compilationUnitPath = null; SourceRange sourceRange = null; if(node != null) { // This can happen with intrinsic elements compilationUnitPath = Location.createValidOrNull(node.getModuleNode().getCompilationUnitPath()); sourceRange = node.getNameSourceRangeOrNull(); } results.add(new FindDefinitionResultEntry( namedElement.getExtendedName(), namedElement.isBuiltinElement(), compilationUnitPath, sourceRange)); } return new FindDefinitionResult(results, ref, namedElements); } }