/** - * Copyright (c) 2013-2016 Angelo ZERR. * 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: * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation */ package tern.eclipse.ide.ui.views; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.navigator.CommonViewer; import org.eclipse.ui.texteditor.ITextEditor; import tern.eclipse.ide.internal.ui.TernUIMessages; import tern.eclipse.ide.ui.utils.EditorUtils; import tern.server.protocol.outline.IJSNode; import tern.utils.StringUtils; /** * Job which is executed when there is a selection inside JavaScript Editor to * select the well node in the tern outline. * */ class UpdateSelectionJob extends Job implements ISelectionChangedListener { private static final int UPDATE_DELAY = 100; private final AbstractTernOutlineView view; private ISelection selection; private ITextEditor textEditor; public UpdateSelectionJob(AbstractTernOutlineView view) { super(TernUIMessages.Link_With_Editor_Job_); super.setSystem(true); super.setPriority(Job.SHORT); this.view = view; } @Override protected IStatus run(IProgressMonitor monitor) { BestNode bestNode = findBestNode(selection); if (bestNode != null) { try { view.ignoreSelectionChanged = true; final IJSNode node = bestNode.node; final CommonViewer viewer = bestNode.viewer; Display.getDefault().syncExec(new Runnable() { @Override public void run() { viewer.setSelection(new StructuredSelection(node)); } }); } finally { view.ignoreSelectionChanged = false; } } return Status.OK_STATUS; } @Override public void selectionChanged(SelectionChangedEvent event) { selectInTreeview(event.getSelection()); } public void setCurrentPart(IWorkbenchPart part) { if (this.textEditor != null) { uninstall(this.textEditor.getSelectionProvider()); } if (part instanceof ITextEditor) { this.textEditor = (ITextEditor) part; ISelectionProvider provider = this.textEditor.getSelectionProvider(); selectInTreeview(provider.getSelection()); install(provider); } else { this.textEditor = null; } } private void selectInTreeview(ISelection selection) { if (view.ignoreEditorActivation) { return; } this.selection = selection; super.schedule(UPDATE_DELAY); } private void install(ISelectionProvider selectionProvider) { if (selectionProvider == null) { return; } if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.addPostSelectionChangedListener(this); } else { selectionProvider.addSelectionChangedListener(this); } } private void uninstall(ISelectionProvider selectionProvider) { if (selectionProvider == null) { return; } if (selectionProvider instanceof IPostSelectionProvider) { IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.removePostSelectionChangedListener(this); } else { selectionProvider.removeSelectionChangedListener(this); } } private class BestNode { public final IJSNode node; public final CommonViewer viewer; public BestNode(IJSNode node, CommonViewer viewer) { this.node = node; this.viewer = viewer; } } private BestNode findBestNode(ISelection selection) { if (!view.isLinkingEnabled() /* && !ignoreSelectionChanged */ ) { return null; } CommonViewer viewer = view.getCurrentViewer(); if (viewer == null) { return null; } IStructuredContentProvider contentProvider = (IStructuredContentProvider) viewer.getContentProvider(); if (contentProvider == null || selection == null || selection.isEmpty() || !(selection instanceof ITextSelection)) { return null; } Object[] elements = contentProvider.getElements(viewer.getInput()); IFile currentFile = view.getCurrentTernPage().getCurrentFile(); if (elements != null) { Object elt = null; IJSNode bestNode = null; for (int i = 0; i < elements.length; i++) { elt = elements[i]; if (elt instanceof IJSNode) { bestNode = findBestNode((IJSNode) elt, (ITextSelection) selection, currentFile); if (bestNode != null) { return new BestNode(bestNode, viewer); } } } } return null; } private IJSNode findBestNode(IJSNode node, ITextSelection selection, IFile currentFile) { int start = selection.getOffset(), end = start + selection.getLength(); if (node.getStart() != null) { if (node.getStart() <= start && node.getEnd() >= end) { if (!StringUtils.isEmpty(node.getFile()) && currentFile != null && !currentFile.equals(EditorUtils.getFile(node))) { // doesn't match the file return null; } // node is found, check if node is a container (like a function) if (node.isContainer()) { // check if there is a child which match the selection IJSNode child = findBestNodeInChildren(node, selection, currentFile); if (child != null) { return child; } } return node; } } return findBestNodeInChildren(node, selection, currentFile); } protected IJSNode findBestNodeInChildren(IJSNode node, ITextSelection selection, IFile currentFile) { if (node.hasChidren()) { for (IJSNode child : node.getChildren()) { IJSNode c = findBestNode(child, selection, currentFile); if (c != null) { return c; } } } return null; } public void dispose() { if (this.textEditor != null) { uninstall(this.textEditor.getSelectionProvider()); } super.cancel(); } }