| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.infoviews; |
| |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| |
| import org.eclipse.core.runtime.Assert; |
| |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| |
| 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.ITextOperationTarget; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.TextViewer; |
| import org.eclipse.jface.text.presentation.IPresentationReconciler; |
| import org.eclipse.jface.text.source.SourceViewer; |
| |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.PlatformUI; |
| |
| import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds; |
| |
| import org.eclipse.jdt.core.ICodeAssist; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.ISourceReference; |
| import org.eclipse.jdt.core.JavaModelException; |
| |
| import org.eclipse.jdt.internal.core.manipulation.StubUtility; |
| import org.eclipse.jdt.internal.core.manipulation.util.Strings; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| |
| import org.eclipse.jdt.ui.IContextMenuConstants; |
| import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds; |
| import org.eclipse.jdt.ui.actions.JdtActionConstants; |
| import org.eclipse.jdt.ui.actions.OpenAction; |
| import org.eclipse.jdt.ui.text.IJavaPartitions; |
| import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration; |
| |
| import org.eclipse.jdt.internal.ui.IJavaHelpContextIds; |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer; |
| import org.eclipse.jdt.internal.ui.text.JavaCodeReader; |
| import org.eclipse.jdt.internal.ui.text.SimpleJavaSourceViewerConfiguration; |
| |
| /** |
| * View which shows source for a given Java element. |
| * |
| * @since 3.0 |
| */ |
| public class SourceView extends AbstractInfoView { |
| |
| /** Symbolic Java editor font name. */ |
| private static final String SYMBOLIC_FONT_NAME= "org.eclipse.jdt.ui.editors.textfont"; //$NON-NLS-1$ |
| |
| /** |
| * Internal property change listener for handling changes in the editor's preferences. |
| * |
| * @since 3.0 |
| */ |
| class PropertyChangeListener implements IPropertyChangeListener { |
| /* |
| * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) |
| */ |
| @Override |
| public void propertyChange(PropertyChangeEvent event) { |
| if (fViewer == null) |
| return; |
| |
| if (fViewerConfiguration.affectsTextPresentation(event)) { |
| fViewerConfiguration.handlePropertyChangeEvent(event); |
| fViewer.invalidateTextPresentation(); |
| } |
| } |
| } |
| |
| /** |
| * Internal property change listener for handling workbench font changes. |
| */ |
| class FontPropertyChangeListener implements IPropertyChangeListener { |
| /* |
| * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) |
| */ |
| @Override |
| public void propertyChange(PropertyChangeEvent event) { |
| if (fViewer == null) |
| return; |
| |
| String property= event.getProperty(); |
| |
| if (SYMBOLIC_FONT_NAME.equals(property)) |
| setViewerFont(); |
| } |
| } |
| |
| /** |
| * The Javadoc view's select all action. |
| */ |
| private static class SelectAllAction extends Action { |
| |
| private TextViewer fTextViewer; |
| |
| /** |
| * Creates the action. |
| * |
| * @param textViewer the text viewer |
| */ |
| public SelectAllAction(TextViewer textViewer) { |
| super("selectAll"); //$NON-NLS-1$ |
| |
| Assert.isNotNull(textViewer); |
| fTextViewer= textViewer; |
| |
| setText(InfoViewMessages.SelectAllAction_label); |
| setToolTipText(InfoViewMessages.SelectAllAction_tooltip); |
| setDescription(InfoViewMessages.SelectAllAction_description); |
| |
| PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION); |
| } |
| |
| /** |
| * Selects all in the viewer. |
| */ |
| @Override |
| public void run() { |
| fTextViewer.doOperation(ITextOperationTarget.SELECT_ALL); |
| } |
| } |
| |
| /** This view's source viewer */ |
| private SourceViewer fViewer; |
| /** The viewers configuration */ |
| private JavaSourceViewerConfiguration fViewerConfiguration; |
| /** Map of configuration kind to source viewer configuration */ |
| private Map<String, JavaSourceViewerConfiguration> fKindToViewerConfiguration= new HashMap<>(); |
| /** The viewer's font properties change listener. */ |
| private IPropertyChangeListener fFontPropertyChangeListener= new FontPropertyChangeListener(); |
| /** |
| * The editor's property change listener. |
| * @since 3.0 |
| */ |
| private IPropertyChangeListener fPropertyChangeListener= new PropertyChangeListener(); |
| /** The open action */ |
| private OpenAction fOpen; |
| /** The number of removed leading comment lines. */ |
| private int fCommentLineCount; |
| /** The select all action. */ |
| private SelectAllAction fSelectAllAction; |
| /** Element opened by the open action. */ |
| private IJavaElement fLastOpenedElement; |
| |
| |
| /* |
| * @see AbstractInfoView#internalCreatePartControl(Composite) |
| */ |
| @Override |
| protected void internalCreatePartControl(Composite parent) { |
| IPreferenceStore store= JavaPlugin.getDefault().getCombinedPreferenceStore(); |
| fViewer= new JavaSourceViewer(parent, null, null, false, SWT.V_SCROLL | SWT.H_SCROLL, store); |
| fViewerConfiguration= new SimpleJavaSourceViewerConfiguration(JavaPlugin.getDefault().getJavaTextTools().getColorManager(), store, null, IJavaPartitions.JAVA_PARTITIONING, false); |
| fKindToViewerConfiguration.put(SimpleJavaSourceViewerConfiguration.STANDARD, fViewerConfiguration); |
| fViewer.configure(fViewerConfiguration); |
| fViewer.setEditable(false); |
| |
| setViewerFont(); |
| JFaceResources.getFontRegistry().addListener(fFontPropertyChangeListener); |
| |
| store.addPropertyChangeListener(fPropertyChangeListener); |
| |
| getViewSite().setSelectionProvider(fViewer); |
| } |
| |
| /* |
| * @see AbstractInfoView#internalCreatePartControl(Composite) |
| */ |
| @Override |
| protected void createActions() { |
| super.createActions(); |
| fSelectAllAction= new SelectAllAction(fViewer); |
| |
| // Setup OpenAction |
| fOpen= new OpenAction(getViewSite()) { |
| |
| /* |
| * @see org.eclipse.jdt.ui.actions.SelectionDispatchAction#getSelection() |
| */ |
| @Override |
| public ISelection getSelection() { |
| return convertToJavaElementSelection(fViewer.getSelection()); |
| } |
| |
| /* |
| * @see org.eclipse.jdt.ui.actions.OpenAction#run(IStructuredSelection) |
| */ |
| @Override |
| public void run(IStructuredSelection selection) { |
| if (selection.isEmpty()) { |
| getShell().getDisplay().beep(); |
| return; |
| } |
| super.run(selection); |
| } |
| |
| /* |
| * @see org.eclipse.jdt.ui.actions.OpenAction#getElementToOpen(Object) |
| */ |
| @Override |
| public Object getElementToOpen(Object object) throws JavaModelException { |
| if (object instanceof IJavaElement) |
| fLastOpenedElement= (IJavaElement)object; |
| else |
| fLastOpenedElement= null; |
| return super.getElementToOpen(object); |
| } |
| |
| /* |
| * @see org.eclipse.jdt.ui.actions.OpenAction#run(Object[]) |
| */ |
| @Override |
| public void run(Object[] elements) { |
| stopListeningForSelectionChanges(); |
| super.run(elements); |
| startListeningForSelectionChanges(); |
| } |
| }; |
| } |
| |
| |
| /* |
| * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getSelectAllAction() |
| * @since 3.0 |
| */ |
| @Override |
| protected IAction getSelectAllAction() { |
| return fSelectAllAction; |
| } |
| |
| /* |
| * @see AbstractInfoView#fillActionBars(IActionBars) |
| */ |
| @Override |
| protected void fillActionBars(IActionBars actionBars) { |
| super.fillActionBars(actionBars); |
| actionBars.setGlobalActionHandler(JdtActionConstants.OPEN, fOpen); |
| fOpen.setActionDefinitionId(IJavaEditorActionDefinitionIds.OPEN_EDITOR); |
| } |
| |
| /* |
| * @see AbstractInfoView#getControl() |
| */ |
| @Override |
| protected Control getControl() { |
| return fViewer.getControl(); |
| } |
| |
| /* |
| * @see AbstractInfoView#menuAboutToShow(IMenuManager) |
| */ |
| @Override |
| public void menuAboutToShow(IMenuManager menu) { |
| super.menuAboutToShow(menu); |
| menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, fOpen); |
| } |
| |
| /* |
| * @see AbstractInfoView#setForeground(Color) |
| */ |
| @Override |
| protected void setForeground(Color color) { |
| fViewer.getTextWidget().setForeground(color); |
| } |
| |
| /* |
| * @see AbstractInfoView#setBackground(Color) |
| */ |
| @Override |
| protected void setBackground(Color color) { |
| fViewer.getTextWidget().setBackground(color); |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getBackgroundColorKey() |
| * @since 3.2 |
| */ |
| @Override |
| protected String getBackgroundColorKey() { |
| return "org.eclipse.jdt.ui.DeclarationView.backgroundColor"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Converts the given selection to a structured selection |
| * containing Java elements. |
| * |
| * @param selection the selection |
| * @return a structured selection with Java elements |
| */ |
| private IStructuredSelection convertToJavaElementSelection(ISelection selection) { |
| |
| if (!(selection instanceof ITextSelection && fCurrentViewInput instanceof ISourceReference)) |
| return StructuredSelection.EMPTY; |
| |
| ITextSelection textSelection= (ITextSelection)selection; |
| |
| Object codeAssist= fCurrentViewInput.getAncestor(IJavaElement.COMPILATION_UNIT); |
| if (codeAssist == null) |
| codeAssist= fCurrentViewInput.getAncestor(IJavaElement.CLASS_FILE); |
| |
| if (codeAssist instanceof ICodeAssist) { |
| IJavaElement[] elements= null; |
| try { |
| ISourceRange range= ((ISourceReference)fCurrentViewInput).getSourceRange(); |
| elements= ((ICodeAssist)codeAssist).codeSelect(range.getOffset() + getOffsetInUnclippedDocument(textSelection), textSelection.getLength()); |
| } catch (JavaModelException e) { |
| return StructuredSelection.EMPTY; |
| } |
| if (elements != null && elements.length > 0) { |
| return new StructuredSelection(elements[0]); |
| } else |
| return StructuredSelection.EMPTY; |
| } |
| |
| return StructuredSelection.EMPTY; |
| } |
| |
| /** |
| * Computes and returns the offset in the unclipped document based on the given text selection |
| * from the clipped document. |
| * |
| * @param textSelection the text selection |
| * @return the offset in the unclipped document or <code>-1</code> if the offset cannot be |
| * computed |
| */ |
| private int getOffsetInUnclippedDocument(ITextSelection textSelection) { |
| IDocument unclippedDocument= null; |
| try { |
| unclippedDocument= new Document(((ISourceReference)fCurrentViewInput).getSource()); |
| } catch (JavaModelException e) { |
| return -1; |
| } |
| IDocument clippedDoc= (IDocument)fViewer.getInput(); |
| try { |
| IRegion unclippedLineInfo= unclippedDocument.getLineInformation(fCommentLineCount + textSelection.getStartLine()); |
| IRegion clippedLineInfo= clippedDoc.getLineInformation(textSelection.getStartLine()); |
| int removedIndentation= unclippedLineInfo.getLength() - clippedLineInfo.getLength(); |
| int relativeLineOffset= textSelection.getOffset() - clippedLineInfo.getOffset(); |
| return unclippedLineInfo.getOffset() + removedIndentation + relativeLineOffset ; |
| } catch (BadLocationException ex) { |
| return -1; |
| } |
| } |
| |
| /* |
| * @see AbstractInfoView#internalDispose() |
| */ |
| @Override |
| protected void internalDispose() { |
| fViewer= null; |
| fViewerConfiguration= null; |
| fKindToViewerConfiguration= null; |
| JFaceResources.getFontRegistry().removeListener(fFontPropertyChangeListener); |
| JavaPlugin.getDefault().getCombinedPreferenceStore().removePropertyChangeListener(fPropertyChangeListener); |
| } |
| |
| /* |
| * @see org.eclipse.ui.part.WorkbenchPart#setFocus() |
| */ |
| @Override |
| public void setFocus() { |
| fViewer.getTextWidget().setFocus(); |
| } |
| |
| /* |
| * @see AbstractInfoView#computeInput(Object) |
| */ |
| @Override |
| protected Object computeInput(Object input) { |
| |
| if (fViewer == null || !(input instanceof ISourceReference)) |
| return null; |
| |
| ISourceReference sourceRef= (ISourceReference)input; |
| |
| if (fLastOpenedElement != null && input instanceof IJavaElement && ((IJavaElement)input).getHandleIdentifier().equals(fLastOpenedElement.getHandleIdentifier())) { |
| fLastOpenedElement= null; |
| return null; |
| } else { |
| fLastOpenedElement= null; |
| } |
| |
| String source; |
| try { |
| source= sourceRef.getSource(); |
| } catch (JavaModelException ex) { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| if (source == null) |
| return ""; //$NON-NLS-1$ |
| |
| source= removeLeadingComments(source); |
| String delim= StubUtility.getLineDelimiterUsed((IJavaElement) input); |
| |
| String[] sourceLines= Strings.convertIntoLines(source); |
| if (sourceLines == null || sourceLines.length == 0) |
| return ""; //$NON-NLS-1$ |
| |
| if (input instanceof IJavaElement) { |
| IWorkbenchPartSite site= getSite(); |
| if (site != null) { |
| Shell shell= site.getShell(); |
| if (!shell.isDisposed()) { |
| Display display= shell.getDisplay(); |
| if (!display.isDisposed()) { |
| display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| updateViewerConfiguration((IJavaElement) input); |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| String firstLine= sourceLines[0]; |
| boolean firstCharNotWhitespace= firstLine != null && firstLine.length() > 0 && !Character.isWhitespace(firstLine.charAt(0)); |
| if (firstCharNotWhitespace) |
| sourceLines[0]= ""; //$NON-NLS-1$ |
| IJavaProject project; |
| if (input instanceof IJavaElement) |
| project= ((IJavaElement) input).getJavaProject(); |
| else |
| project= null; |
| Strings.trimIndentation(sourceLines, project); |
| |
| if (firstCharNotWhitespace) |
| sourceLines[0]= firstLine; |
| |
| return Strings.concatenate(sourceLines, delim); |
| } |
| |
| private void updateViewerConfiguration(IJavaElement input) { |
| if (fKindToViewerConfiguration != null) { |
| JavaSourceViewerConfiguration oldViewerConfiguration= fViewerConfiguration; |
| if (JavaModelUtil.isModule(input)) { |
| fViewerConfiguration= fKindToViewerConfiguration.get(SimpleJavaSourceViewerConfiguration.MODULE); |
| if (fViewerConfiguration == null) { |
| IPreferenceStore store= JavaPlugin.getDefault().getCombinedPreferenceStore(); |
| fViewerConfiguration= new SimpleJavaSourceViewerConfiguration(JavaPlugin.getDefault().getJavaTextTools().getColorManager(), store, null, IJavaPartitions.JAVA_PARTITIONING, false, true); |
| fKindToViewerConfiguration.put(SimpleJavaSourceViewerConfiguration.MODULE, fViewerConfiguration); |
| } |
| } else { |
| fViewerConfiguration= fKindToViewerConfiguration.get(SimpleJavaSourceViewerConfiguration.STANDARD); |
| } |
| if (fViewerConfiguration != null && !fViewerConfiguration.equals(oldViewerConfiguration)) { |
| IPresentationReconciler pReconciler= fViewerConfiguration.getPresentationReconciler(fViewer); |
| pReconciler.install(fViewer); |
| } |
| } |
| } |
| |
| /* |
| * @see AbstractInfoView#doSetInput(Object) |
| */ |
| @Override |
| protected void doSetInput(Object input) { |
| if (input instanceof IDocument) |
| fViewer.setInput(input); |
| else if (input == null) |
| fViewer.setInput(new Document("")); //$NON-NLS-1$ |
| else { |
| IDocument document= new Document(input.toString()); |
| JavaPlugin.getDefault().getJavaTextTools().setupJavaDocumentPartitioner(document, IJavaPartitions.JAVA_PARTITIONING); |
| fViewer.setInput(document); |
| } |
| } |
| |
| /** |
| * Removes the leading comments from the given source. |
| * |
| * @param source the string with the source |
| * @return the source without leading comments |
| */ |
| private String removeLeadingComments(String source) { |
| JavaCodeReader reader= new JavaCodeReader(); |
| IDocument document= new Document(source); |
| int i; |
| try { |
| reader.configureForwardReader(document, 0, document.getLength(), true, false); |
| int c= reader.read(); |
| while (c != -1 && (c == '\r' || c == '\n' || c == '\t')) { |
| c= reader.read(); |
| } |
| i= reader.getOffset(); |
| reader.close(); |
| } catch (IOException ex) { |
| i= 0; |
| } finally { |
| try { |
| reader.close(); |
| } catch (IOException ex) { |
| JavaPlugin.log(ex); |
| } |
| } |
| |
| try { |
| fCommentLineCount= document.getLineOfOffset(i); |
| } catch (BadLocationException e) { |
| fCommentLineCount= 0; |
| } |
| |
| if (i < 0) |
| return source; |
| |
| return source.substring(i); |
| } |
| |
| /** |
| * Sets the font for this viewer sustaining selection and scroll position. |
| */ |
| private void setViewerFont() { |
| Font font= JFaceResources.getFont(SYMBOLIC_FONT_NAME); |
| |
| if (fViewer.getDocument() != null) { |
| |
| Point selection= fViewer.getSelectedRange(); |
| int topIndex= fViewer.getTopIndex(); |
| |
| StyledText styledText= fViewer.getTextWidget(); |
| Control parent= fViewer.getControl(); |
| |
| parent.setRedraw(false); |
| |
| styledText.setFont(font); |
| |
| fViewer.setSelectedRange(selection.x , selection.y); |
| fViewer.setTopIndex(topIndex); |
| |
| if (parent instanceof Composite) { |
| Composite composite= (Composite) parent; |
| composite.layout(true); |
| } |
| |
| parent.setRedraw(true); |
| |
| |
| } else { |
| StyledText styledText= fViewer.getTextWidget(); |
| styledText.setFont(font); |
| } |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getHelpContextId() |
| * @since 3.1 |
| */ |
| @Override |
| protected String getHelpContextId() { |
| return IJavaHelpContextIds.SOURCE_VIEW; |
| } |
| } |