From 40335a9f9fb1863f43bba95c878cbd19b3f0bfd1 Mon Sep 17 00:00:00 2001 From: Anton Leherbauer Date: Fri, 5 Mar 2010 10:57:28 +0000 Subject: [PATCH] Bug 264140 - [templates] Add support for Templates view Patch from Marc-Andre Laperle --- .../internal/corext/template/c/CContext.java | 6 +- .../cdt/internal/ui/editor/CEditor.java | 11 +- .../internal/ui/editor/CTemplatesPage.java | 352 ++++++++++++++++++ .../preferences/CSourcePreviewerUpdater.java | 4 +- .../ui/preferences/EditTemplateDialog.java | 12 +- 5 files changed, 373 insertions(+), 12 deletions(-) create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CTemplatesPage.java diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/template/c/CContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/template/c/CContext.java index 42f0266b54b..d33107da398 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/template/c/CContext.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/template/c/CContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2008 IBM Corporation and others. + * Copyright (c) 2005, 2010 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 @@ -30,7 +30,7 @@ import org.eclipse.cdt.ui.PreferenceConstants; /** - * A context for C/C++ + * A context for C/C++. */ public class CContext extends TranslationUnitContext { @@ -75,7 +75,7 @@ public class CContext extends TranslationUnitContext { int start= getCompletionOffset(); int end= getCompletionOffset() + getCompletionLength(); - while (start != 0 && !Character.isWhitespace(document.getChar(start - 1))) + while (start != 0 && Character.isUnicodeIdentifierPart(document.getChar(start - 1))) start--; while (start != end && Character.isWhitespace(document.getChar(start))) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java index d3d620ae1e4..703af01b0aa 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2009 IBM Corporation and others. + * Copyright (c) 2005, 2010 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 @@ -160,6 +160,7 @@ import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.eclipse.ui.texteditor.TextNavigationAction; import org.eclipse.ui.texteditor.TextOperationAction; import org.eclipse.ui.texteditor.link.EditorLinkedModeUI; +import org.eclipse.ui.texteditor.templates.ITemplatesPage; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import com.ibm.icu.text.BreakIterator; @@ -1306,6 +1307,8 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IC */ private volatile boolean fIsReconciling; + private CTemplatesPage fTemplatesPage; + private static final Set angularIntroducers = new HashSet(); static { angularIntroducers.add("template"); //$NON-NLS-1$ @@ -1539,6 +1542,12 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IC IGotoMarker gotoMarker= new GotoMarkerAdapter(); return gotoMarker; } + else if (ITemplatesPage.class.equals(required)) { + if (fTemplatesPage == null) { + fTemplatesPage = new CTemplatesPage(this); + return fTemplatesPage; + } + } return super.getAdapter(required); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CTemplatesPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CTemplatesPage.java new file mode 100644 index 00000000000..3553387d5f6 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CTemplatesPage.java @@ -0,0 +1,352 @@ +/* + * /******************************************************************************* + * Copyright (c) 2007, 2010 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 + * Marc-Andre Laperle - CDT version + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + +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.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.window.Window; + +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.ui.texteditor.templates.AbstractTemplatesPage; + +import org.eclipse.cdt.core.model.ITranslationUnit; + +import org.eclipse.cdt.internal.corext.template.c.CContextType; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; +import org.eclipse.cdt.ui.text.ICPartitions; + +import org.eclipse.cdt.internal.corext.template.c.CContext; + +import org.eclipse.cdt.internal.ui.preferences.CSourcePreviewerUpdater; +import org.eclipse.cdt.internal.ui.preferences.EditTemplateDialog; + +import org.eclipse.cdt.internal.ui.CPluginImages; +import org.eclipse.cdt.internal.ui.text.CTextTools; +import org.eclipse.cdt.internal.ui.text.SimpleCSourceViewerConfiguration; +import org.eclipse.cdt.internal.ui.text.template.TemplateVariableProcessor; +import org.eclipse.cdt.internal.ui.util.EditorUtility; + +/** + * The templates page for the C editor. + * + * @since 5.2 + */ +public class CTemplatesPage extends AbstractTemplatesPage { + + private static final String PREFERENCE_PAGE_ID= "org.eclipse.cdt.ui.preferences.TemplatePreferencePage"; //$NON-NLS-1$ + private static final TemplateStore TEMPLATE_STORE= CUIPlugin.getDefault().getTemplateStore(); + private static final IPreferenceStore PREFERENCE_STORE= CUIPlugin.getDefault().getPreferenceStore(); + private static final ContextTypeRegistry TEMPLATE_CONTEXT_REGISTRY= CUIPlugin.getDefault().getTemplateContextRegistry(); + + private TemplateVariableProcessor fTemplateProcessor; + private CEditor fCEditor; + + /** + * Create a new AbstractTemplatesPage for the CEditor + * + * @param cEditor the C editor + */ + public CTemplatesPage(CEditor cEditor) { + super(cEditor, cEditor.getViewer()); + fCEditor= cEditor; + fTemplateProcessor= new TemplateVariableProcessor(); + } + + /* + * @see org.eclipse.ui.texteditor.templates.AbstractTemplatesPage#insertTemplate(org.eclipse.jface.text.templates.Template, org.eclipse.jface.text.IDocument) + */ + @Override + protected void insertTemplate(Template template, IDocument document) { + if (!fCEditor.validateEditorInputState()) + return; + + ISourceViewer contextViewer= fCEditor.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)); + ITranslationUnit compilationUnit= (ITranslationUnit) EditorUtility.getEditorInputCElement(fCEditor); + + TemplateContextType type= getContextTypeRegistry().getContextType(template.getContextTypeId()); + DocumentTemplateContext context= ((CContextType) 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); + fCEditor.getSite().getPage().activate(fCEditor); + proposal.apply(fCEditor.getViewer(), ' ', 0, region.getOffset()); + endCompoundChange(contextViewer); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.templates.AbstractTemplatesPage#getContextTypeRegistry() + */ + @Override + protected ContextTypeRegistry getContextTypeRegistry() { + return TEMPLATE_CONTEXT_REGISTRY; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.templates.AbstractTemplatesPage#getTemplatePreferenceStore() + */ + @Override + protected IPreferenceStore getTemplatePreferenceStore() { + return PREFERENCE_STORE; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.templates.AbstractTemplatesPage#getTemplateStore() + */ + @Override + public TemplateStore getTemplateStore() { + return TEMPLATE_STORE; + } + + /* + * @see org.eclipse.ui.texteditor.templates.TextEditorTemplatesPage#isValidTemplate(org.eclipse.jface.text.templates.Template, int, int) + */ + @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; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.templates.TextEditorTemplatesPage#createPatternViewer(org.eclipse.swt.widgets.Composite) + */ + @Override + protected SourceViewer createPatternViewer(Composite parent) { + IDocument document= new Document(); + CTextTools tools= CUIPlugin.getDefault().getTextTools(); + tools.setupCDocumentPartitioner(document, ICPartitions.C_PARTITIONING, null); + IPreferenceStore store= CUIPlugin.getDefault().getCombinedPreferenceStore(); + CSourceViewer viewer= new CSourceViewer(parent, null, null, false, SWT.V_SCROLL | SWT.H_SCROLL, store); + SimpleCSourceViewerConfiguration configuration= new SimpleCSourceViewerConfiguration(tools.getColorManager(), store, null, ICPartitions.C_PARTITIONING, false); + viewer.configure(configuration); + viewer.setEditable(false); + viewer.setDocument(document); + + Font font= JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT); + viewer.getTextWidget().setFont(font); + new CSourcePreviewerUpdater(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; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.templates.AbstractTemplatesPage#getImageForTemplate(org.eclipse.jface.text.templates.Template) + */ + @Override + protected Image getImage(Template template) { + return CPluginImages.get(CPluginImages.IMG_OBJS_TEMPLATE); + } + + /* + * @see org.eclipse.ui.texteditor.templates.TextEditorTemplatesPage#editTemplate(org.eclipse.jface.text.templates.Template, boolean, boolean) + */ + @Override + protected Template editTemplate(Template template, boolean edit, boolean isNameModifiable) { + EditTemplateDialog dialog= new EditTemplateDialog(getSite().getShell(), template, edit, isNameModifiable, false, getContextTypeRegistry()); + if (dialog.open() == Window.OK) + return dialog.getTemplate(); + return null; + } + + /* + * @see org.eclipse.ui.texteditor.templates.TextEditorTemplatesPage#updatePatternViewer(org.eclipse.jface.text.templates.Template) + */ + @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= ""; //$NON-NLS-1$ + doc.set(start + template.getPattern()); + int startLen= start.length(); + getPatternViewer().setDocument(doc, startLen, doc.getLength() - startLen); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.texteditor.templates.AbstractTemplatesPage#getPreferencePageId() + */ + @Override + protected String getPreferencePageId() { + return PREFERENCE_PAGE_ID; + } + + /** + * 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 ctl-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 { + 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 C template names + * + * @param ch the character + * @return true 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) { + DocumentTemplateContext context= new CContext(getContextTypeRegistry().getContextType(template.getContextTypeId()), document, new Position(offset, length), (ITranslationUnit) EditorUtility.getEditorInputCElement( + fCEditor)); + return context; + } + + /** + * Get the active contexts for the given position in the document. + *

+ * FIXME: should trigger code assist to get the context. + *

+ * + * @param document the document + * @param offset the offset + * @return an array of valid context id + */ + @Override + protected String[] getContextTypeIds(IDocument document, int offset) { + return new String[] { CContextType.ID }; + } + + /** + * Get the C 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 C identifier + */ + private String getIdentifierPart(IDocument document, Template template, int offset, int length) { + return getContext(document, template, offset, length).getKey(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CSourcePreviewerUpdater.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CSourcePreviewerUpdater.java index 80091981dbd..80212e91bb1 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CSourcePreviewerUpdater.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CSourcePreviewerUpdater.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 QNX Software Systems and others. + * Copyright (c) 2000, 2010 QNX Software Systems 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 @@ -34,7 +34,7 @@ public class CSourcePreviewerUpdater { * @param configuration * @param store */ - public CSourcePreviewerUpdater(SourceViewer viewer, CodeTemplateSourceViewerConfiguration configuration, IPreferenceStore store) { + public CSourcePreviewerUpdater(SourceViewer viewer, CSourceViewerConfiguration configuration, IPreferenceStore store) { registerPreviewer(viewer, configuration, store); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/EditTemplateDialog.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/EditTemplateDialog.java index e457989f0af..f3402254ba9 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/EditTemplateDialog.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/EditTemplateDialog.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2010 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 @@ -73,14 +73,13 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.ActiveShellExpression; +import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.handlers.IHandlerActivation; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.IUpdate; -import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; - import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.ui.PreferenceConstants; import org.eclipse.cdt.ui.text.ICPartitions; @@ -94,8 +93,9 @@ import org.eclipse.cdt.internal.ui.text.template.TemplateVariableProcessor; /** * Dialog to edit a template. + * @since 5.2 */ -class EditTemplateDialog extends StatusDialog { +public class EditTemplateDialog extends StatusDialog { private static class TextViewerAction extends Action implements IUpdate { @@ -492,12 +492,12 @@ class EditTemplateDialog extends StatusDialog { TextViewerAction action= new TextViewerAction(fPatternEditor, ITextOperationTarget.UNDO); action.setText(PreferencesMessages.EditTemplateDialog_undo); fGlobalActions.put(ITextEditorActionConstants.UNDO, action); - handlerActivations.add(handlerService.activateHandler(IWorkbenchActionDefinitionIds.UNDO, new ActionHandler(action), expression)); + handlerActivations.add(handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_UNDO, new ActionHandler(action), expression)); action= new TextViewerAction(fPatternEditor, ITextOperationTarget.REDO); action.setText(PreferencesMessages.EditTemplateDialog_redo); fGlobalActions.put(ITextEditorActionConstants.REDO, action); - handlerActivations.add(handlerService.activateHandler(IWorkbenchActionDefinitionIds.REDO, new ActionHandler(action), expression)); + handlerActivations.add(handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_REDO, new ActionHandler(action), expression)); action= new TextViewerAction(fPatternEditor, ITextOperationTarget.CUT); action.setText(PreferencesMessages.EditTemplateDialog_cut);