| /******************************************************************************* |
| * Copyright (c) 2007, 2017 Dakshinamurthy Karra, 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 |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Dakshinamurthy Karra (Jalian Systems) - Templates View - https://bugs.eclipse.org/bugs/show_bug.cgi?id=69581 |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.ui.editor; |
| |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.internal.ui.preferences.ScriptSourcePreviewerUpdater; |
| import org.eclipse.dltk.ui.DLTKPluginImages; |
| import org.eclipse.dltk.ui.DLTKUILanguageManager; |
| import org.eclipse.dltk.ui.IDLTKUILanguageToolkit; |
| import org.eclipse.dltk.ui.preferences.EditTemplateDialog; |
| import org.eclipse.dltk.ui.templates.ScriptTemplateContextType; |
| import org.eclipse.dltk.ui.text.ScriptSourceViewerConfiguration; |
| import org.eclipse.dltk.ui.text.ScriptTextTools; |
| import org.eclipse.dltk.ui.text.templates.ITemplateAccess; |
| import org.eclipse.dltk.ui.text.templates.TemplateVariableProcessor; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.ITextViewerExtension; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.TextSelection; |
| import org.eclipse.jface.text.source.ISourceViewer; |
| import org.eclipse.jface.text.source.SourceViewer; |
| import org.eclipse.jface.text.templates.ContextTypeRegistry; |
| import org.eclipse.jface.text.templates.DocumentTemplateContext; |
| import org.eclipse.jface.text.templates.Template; |
| import org.eclipse.jface.text.templates.TemplateContextType; |
| import org.eclipse.jface.text.templates.TemplateProposal; |
| import org.eclipse.jface.text.templates.persistence.TemplateStore; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.ui.texteditor.templates.AbstractTemplatesPage; |
| |
| /** |
| * The templates page for the Script editor. |
| * |
| * @since 3.0 |
| */ |
| public class ScriptTemplatesPage extends AbstractTemplatesPage { |
| |
| private final TemplateVariableProcessor fTemplateProcessor; |
| private final ScriptEditor fScriptEditor; |
| private final ITemplateAccess fTemplateAccess; |
| |
| /** |
| * Create a new AbstractTemplatesPage for the JavaEditor |
| * |
| * @param scriptEditor |
| * the java editor |
| */ |
| public ScriptTemplatesPage(ScriptEditor scriptEditor, |
| ITemplateAccess templateAccess) { |
| super(scriptEditor, scriptEditor.getViewer()); |
| fScriptEditor = scriptEditor; |
| fTemplateProcessor = new TemplateVariableProcessor(); |
| fTemplateAccess = templateAccess; |
| } |
| |
| @Override |
| protected void insertTemplate(Template template, IDocument document) { |
| if (!fScriptEditor.validateEditorInputState()) |
| return; |
| |
| ISourceViewer contextViewer = fScriptEditor.getViewer(); |
| ITextSelection textSelection = (ITextSelection) contextViewer |
| .getSelectionProvider().getSelection(); |
| if (!isValidTemplate(document, template, textSelection.getOffset(), |
| textSelection.getLength())) |
| return; |
| beginCompoundChange(contextViewer); |
| /* |
| * The Editor checks whether a completion for a word exists before it |
| * allows for the template to be applied. We pickup the current text at |
| * the selection position and replace it with the first char of the |
| * template name for this to succeed. Another advantage by this method |
| * is that the template replaces the selected text provided the |
| * selection by itself is not used in the template pattern. |
| */ |
| String savedText; |
| try { |
| savedText = document.get(textSelection.getOffset(), |
| textSelection.getLength()); |
| if (savedText.length() == 0) { |
| String prefix = getIdentifierPart(document, template, |
| textSelection.getOffset(), textSelection.getLength()); |
| if (prefix.length() > 0 |
| && !template.getName().startsWith(prefix.toString())) { |
| return; |
| } |
| if (prefix.length() > 0) { |
| contextViewer.setSelectedRange( |
| textSelection.getOffset() - prefix.length(), |
| prefix.length()); |
| textSelection = (ITextSelection) contextViewer |
| .getSelectionProvider().getSelection(); |
| } |
| } |
| document.replace(textSelection.getOffset(), |
| textSelection.getLength(), |
| template.getName().substring(0, 1)); |
| } catch (BadLocationException e) { |
| endCompoundChange(contextViewer); |
| return; |
| } |
| Position position = new Position(textSelection.getOffset() + 1, 0); |
| Region region = new Region(textSelection.getOffset() + 1, 0); |
| contextViewer.getSelectionProvider() |
| .setSelection(new TextSelection(textSelection.getOffset(), 1)); |
| ISourceModule compilationUnit = EditorUtility |
| .getEditorInputModelElement(fScriptEditor, true); |
| |
| TemplateContextType type = getContextTypeRegistry() |
| .getContextType(template.getContextTypeId()); |
| DocumentTemplateContext context = ((ScriptTemplateContextType) type) |
| .createContext(document, position, compilationUnit); |
| context.setVariable("selection", savedText); //$NON-NLS-1$ |
| if (context.getKey().length() == 0) { |
| try { |
| document.replace(textSelection.getOffset(), 1, savedText); |
| } catch (BadLocationException e) { |
| endCompoundChange(contextViewer); |
| return; |
| } |
| } |
| TemplateProposal proposal = new TemplateProposal(template, context, |
| region, null); |
| fScriptEditor.getSite().getPage().activate(fScriptEditor); |
| proposal.apply(fScriptEditor.getViewer(), ' ', 0, region.getOffset()); |
| endCompoundChange(contextViewer); |
| } |
| |
| @Override |
| protected ContextTypeRegistry getContextTypeRegistry() { |
| return fTemplateAccess.getContextTypeRegistry(); |
| } |
| |
| @Override |
| protected IPreferenceStore getTemplatePreferenceStore() { |
| return fTemplateAccess.getTemplatePreferenceStore(); |
| } |
| |
| @Override |
| public TemplateStore getTemplateStore() { |
| return fTemplateAccess.getTemplateStore(); |
| } |
| |
| @Override |
| protected boolean isValidTemplate(IDocument document, Template template, |
| int offset, int length) { |
| String[] contextIds = getContextTypeIds(document, offset); |
| for (int i = 0; i < contextIds.length; i++) { |
| if (contextIds[i].equals(template.getContextTypeId())) { |
| DocumentTemplateContext context = getContext(document, template, |
| offset, length); |
| return context.canEvaluate(template) |
| || isTemplateAllowed(context, template); |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| protected SourceViewer createPatternViewer(Composite parent) { |
| IDocument document = new Document(); |
| ScriptTextTools tools = fScriptEditor.getTextTools(); |
| tools.setupDocumentPartitioner(document); |
| IPreferenceStore store = uiToolkit().getCombinedPreferenceStore(); |
| ScriptSourceViewer viewer = new ScriptSourceViewer(parent, null, null, |
| false, SWT.V_SCROLL | SWT.H_SCROLL, store); |
| |
| ScriptSourceViewerConfiguration configuration = uiToolkit() |
| .createSourceViewerConfiguration(); |
| viewer.configure(configuration); |
| viewer.setEditable(false); |
| viewer.setDocument(document); |
| |
| Font font = JFaceResources.getFont(fScriptEditor.getSymbolicFontName()); |
| viewer.getTextWidget().setFont(font); |
| new ScriptSourcePreviewerUpdater(viewer, configuration, store); |
| |
| Control control = viewer.getControl(); |
| GridData data = new GridData( |
| GridData.HORIZONTAL_ALIGN_FILL | GridData.FILL_VERTICAL); |
| control.setLayoutData(data); |
| |
| viewer.setEditable(false); |
| return viewer; |
| } |
| |
| @Override |
| protected Image getImage(Template template) { |
| return DLTKPluginImages.get(DLTKPluginImages.IMG_OBJS_TEMPLATE); |
| } |
| |
| @Override |
| protected Template editTemplate(Template template, boolean edit, |
| boolean isNameModifiable) { |
| EditTemplateDialog dialog = new EditTemplateDialog(uiToolkit(), |
| getSite().getShell(), template, edit, isNameModifiable, true, |
| getContextTypeRegistry()); |
| if (dialog.open() == Window.OK) |
| return dialog.getTemplate(); |
| return null; |
| } |
| |
| protected IDLTKUILanguageToolkit uiToolkit() { |
| return DLTKUILanguageManager |
| .getLanguageToolkit(fScriptEditor.getLanguageToolkit()); |
| } |
| |
| @Override |
| protected void updatePatternViewer(Template template) { |
| if (template == null) { |
| getPatternViewer().getDocument().set(""); //$NON-NLS-1$ |
| return; |
| } |
| String contextId = template.getContextTypeId(); |
| TemplateContextType type = getContextTypeRegistry() |
| .getContextType(contextId); |
| fTemplateProcessor.setContextType(type); |
| |
| IDocument doc = getPatternViewer().getDocument(); |
| |
| String start = null; |
| if ("javadoc".equals(contextId)) { //$NON-NLS-1$ |
| start = "/**" + doc.getLegalLineDelimiters()[0]; //$NON-NLS-1$ |
| } else |
| start = ""; //$NON-NLS-1$ |
| |
| doc.set(start + template.getPattern()); |
| int startLen = start.length(); |
| getPatternViewer().setDocument(doc, startLen, |
| doc.getLength() - startLen); |
| } |
| |
| @Override |
| protected String getPreferencePageId() { |
| return uiToolkit().getEditorTemplatesPreferencePageId(); |
| } |
| |
| /** |
| * Undomanager - end compound change |
| * |
| * @param viewer |
| * the viewer |
| */ |
| private void endCompoundChange(ISourceViewer viewer) { |
| if (viewer instanceof ITextViewerExtension) |
| ((ITextViewerExtension) viewer).getRewriteTarget() |
| .endCompoundChange(); |
| } |
| |
| /** |
| * Undomanager - begin a compound change |
| * |
| * @param viewer |
| * the viewer |
| */ |
| private void beginCompoundChange(ISourceViewer viewer) { |
| if (viewer instanceof ITextViewerExtension) |
| ((ITextViewerExtension) viewer).getRewriteTarget() |
| .beginCompoundChange(); |
| } |
| |
| /** |
| * Check whether the template is allowed even though the context can't |
| * evaluate it. This is needed because the Dropping of a template is more |
| * lenient than Ctrl-space invoked code assist. |
| * |
| * @param context |
| * the template context |
| * @param template |
| * the template |
| * @return true if the template is allowed |
| */ |
| private boolean isTemplateAllowed(DocumentTemplateContext context, |
| Template template) { |
| int offset; |
| try { |
| // if (template.getContextTypeId().equals(JavaDocContextType.ID)) { |
| // return (offset = context.getCompletionOffset()) > 0 |
| // && Character.isWhitespace(context.getDocument() |
| // .getChar(offset - 1)); |
| // } else { |
| return ((offset = context.getCompletionOffset()) > 0 |
| && !isTemplateNamePart( |
| context.getDocument().getChar(offset - 1))); |
| // } |
| } catch (BadLocationException e) { |
| } |
| return false; |
| } |
| |
| /** |
| * Checks whether the character is a valid character in Java template names |
| * |
| * @param ch |
| * the character |
| * @return <code>true</code> if the character is part of a template name |
| */ |
| private boolean isTemplateNamePart(char ch) { |
| return !Character.isWhitespace(ch) && ch != '(' && ch != ')' |
| && ch != '{' && ch != '}' && ch != ';'; |
| } |
| |
| /** |
| * Get context |
| * |
| * @param document |
| * the document |
| * @param template |
| * the template |
| * @param offset |
| * the offset |
| * @param length |
| * the length |
| * @return the context |
| */ |
| private DocumentTemplateContext getContext(IDocument document, |
| Template template, final int offset, int length) { |
| final ScriptTemplateContextType contextType = (ScriptTemplateContextType) getContextTypeRegistry() |
| .getContextType(template.getContextTypeId()); |
| final ISourceModule module = EditorUtility |
| .getEditorInputModelElement(fScriptEditor, true); |
| return contextType.createContext(document, offset, length, module); |
| } |
| |
| /** |
| * Get the active contexts for the given position in the document. |
| * <p> |
| * FIXME: should trigger code assist to get the context. |
| * </p> |
| * |
| * @param document |
| * the document |
| * @param offset |
| * the offset |
| * @return an array of valid context id |
| */ |
| @Override |
| protected String[] getContextTypeIds(IDocument document, int offset) { |
| final Set<String> ids = new HashSet<String>(); |
| final Iterator<TemplateContextType> i = getContextTypeRegistry() |
| .contextTypes(); |
| while (i.hasNext()) { |
| ids.add(i.next().getId()); |
| } |
| return ids.toArray(new String[ids.size()]); |
| // try { |
| // String partition = TextUtilities.getContentType(document, |
| // IJavaPartitions.JAVA_PARTITIONING, offset, true); |
| // String[] ids = new String[] { JavaContextType.ID_ALL, |
| // JavaContextType.ID_MEMBERS, JavaContextType.ID_STATEMENTS, |
| // SWTContextType.ID_ALL, SWTContextType.ID_STATEMENTS, |
| // SWTContextType.ID_MEMBERS }; |
| // if (partition.equals(IJavaPartitions.JAVA_DOC)) |
| // ids = new String[] { JavaDocContextType.ID }; |
| // return ids; |
| // } catch (BadLocationException e) { |
| // return new String[0]; |
| // } |
| } |
| |
| /** |
| * Get the Java identifier terminated at the given offset |
| * |
| * @param document |
| * the document |
| * @param template |
| * the template |
| * @param offset |
| * the offset |
| * @param length |
| * the length |
| * @return the identifier part the Java identifier |
| */ |
| private String getIdentifierPart(IDocument document, Template template, |
| int offset, int length) { |
| return getContext(document, template, offset, length).getKey(); |
| } |
| } |