| /*=============================================================================# |
| # Copyright (c) 2005, 2019 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.ltk.ui.sourceediting.assist; |
| |
| import java.util.List; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.source.ISourceViewer; |
| import org.eclipse.jface.text.templates.DocumentTemplateContext; |
| import org.eclipse.jface.text.templates.GlobalTemplateVariables; |
| import org.eclipse.jface.text.templates.GlobalTemplateVariables.LineSelection; |
| import org.eclipse.jface.text.templates.GlobalTemplateVariables.WordSelection; |
| import org.eclipse.jface.text.templates.Template; |
| import org.eclipse.jface.text.templates.TemplateContextType; |
| import org.eclipse.jface.text.templates.TemplateException; |
| import org.eclipse.jface.text.templates.persistence.TemplateStore; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.text.templates.ContextTypeRegistry; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.text.core.BasicTextRegion; |
| import org.eclipse.statet.jcommons.text.core.TextRegion; |
| |
| import org.eclipse.statet.internal.ltk.ui.LTKUIPlugin; |
| import org.eclipse.statet.ltk.ui.LTKUI; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor; |
| import org.eclipse.statet.ltk.ui.templates.SourceEditorTemplateContext; |
| |
| |
| /** |
| * Content assist computer for editor templates. |
| */ |
| @NonNullByDefault |
| public abstract class TemplateCompletionComputer implements IContentAssistComputer { |
| |
| |
| private static final byte SELECTION_NONE= 0; |
| private static final byte SELECTION_INLINE= 1; |
| private static final byte SELECTION_MULTILINE= 2; |
| |
| private static final Pattern SELECTION_INLINE_PATTERN= Pattern.compile( |
| "\\$\\{" + WordSelection.NAME + "\\}"); //$NON-NLS-1$ //$NON-NLS-2$ |
| private static final Pattern SELECTION_ANY_PATTERN= Pattern.compile( |
| "\\$\\{(?:" + WordSelection.NAME + "|" + LineSelection.NAME + ")\\}"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| |
| |
| protected final TemplateStore templateStore; |
| |
| protected final org.eclipse.text.templates.ContextTypeRegistry typeRegistry; |
| |
| |
| public TemplateCompletionComputer(final TemplateStore templateStore, final ContextTypeRegistry contextTypes) { |
| if (templateStore == null) { |
| throw new NullPointerException("templateStore"); //$NON-NLS-1$ |
| } |
| if (contextTypes == null) { |
| throw new NullPointerException("contextTypes"); //$NON-NLS-1$ |
| } |
| this.templateStore= templateStore; |
| this.typeRegistry= contextTypes; |
| } |
| |
| |
| protected final TemplateStore getTemplateStore() { |
| return this.templateStore; |
| } |
| |
| protected final ContextTypeRegistry getTypeRegistry() { |
| return this.typeRegistry; |
| } |
| |
| |
| @Override |
| public void sessionStarted(final ISourceEditor editor, final ContentAssist assist) { |
| } |
| |
| @Override |
| public void sessionEnded() { |
| } |
| |
| protected boolean handleRequest(final int mode, final String prefix) { |
| return (prefix != null |
| && (prefix.length() > 0 || mode == SPECIFIC_MODE) ); |
| } |
| |
| |
| @Override |
| public @Nullable IStatus computeCompletionProposals(final AssistInvocationContext context, |
| final int mode, final AssistProposalCollector proposals, final IProgressMonitor monitor) { |
| final ISourceViewer viewer= context.getSourceViewer(); |
| final int flags= 0; |
| |
| String prefix= extractPrefix(context); |
| if (!handleRequest(mode, prefix)) { |
| return null; |
| } |
| TextRegion region; |
| if (context.getLength() == 0) { |
| region= new BasicTextRegion(context.getInvocationOffset() - prefix.length(), context.getInvocationOffset()); |
| } |
| else { |
| region= context; |
| } |
| DocumentTemplateContext templateContext= createTemplateContext(context, region, flags); |
| if (templateContext == null) { |
| return null; |
| } |
| |
| int count= 0; |
| if (context.getLength() > 0) { |
| if (prefix.length() == context.getLength()) { |
| count= doComputeProposals(templateContext, prefix, 0, region, proposals); |
| } |
| prefix= ""; // wenn erfolglos, dann ohne prefix //$NON-NLS-1$ |
| if (count != 0) { |
| templateContext= createTemplateContext(context, region, flags); |
| if (templateContext == null) { |
| return null; |
| } |
| } |
| } |
| try { |
| final IDocument document= viewer.getDocument(); |
| final String text= document.get(context.getOffset(), context.getLength()); |
| final int selectionType; |
| if (text.isEmpty()) { |
| selectionType= SELECTION_NONE; |
| } |
| else { |
| selectionType= (text.indexOf('\n') >= 0) ? SELECTION_MULTILINE : SELECTION_INLINE; |
| templateContext.setVariable("text", text); //$NON-NLS-1$ |
| } |
| templateContext.setVariable(GlobalTemplateVariables.SELECTION, text); |
| |
| doComputeProposals(templateContext, prefix, selectionType, region, proposals); |
| } |
| catch (final BadLocationException e) { |
| } |
| return null; |
| } |
| |
| @Override |
| public @Nullable IStatus computeInformationProposals(final AssistInvocationContext context, |
| final AssistProposalCollector tenders, final IProgressMonitor monitor) { |
| return null; |
| } |
| |
| |
| private int doComputeProposals(final DocumentTemplateContext context, final String prefix, |
| final int selectionType, final TextRegion replacementRegion, |
| final AssistProposalCollector proposals) { |
| // Add Templates |
| final int count= 0; |
| final List<Template> templates= getTemplates(context.getContextType().getId()); |
| for (final Template template : templates) { |
| if (include(template, prefix) || isSelectionTemplate(selectionType, template) ) { |
| try { |
| context.getContextType().validate(template.getPattern()); |
| } |
| catch (final TemplateException e) { |
| continue; |
| } |
| |
| proposals.add(createProposal(template, context, prefix, replacementRegion, |
| (template.getName().regionMatches(true, 0, prefix, 0, prefix.length())) ? |
| 20 : -100 )); |
| } |
| } |
| |
| return count; |
| } |
| |
| protected boolean include(final Template template, final String prefix) { |
| return template.getName().regionMatches(true, 0, prefix, 0, prefix.length()); |
| } |
| |
| private boolean isSelectionTemplate(final int selectionType, final Template template) { |
| switch (selectionType) { |
| case SELECTION_INLINE: |
| return SELECTION_INLINE_PATTERN.matcher(template.getPattern()).matches(); |
| case SELECTION_MULTILINE: |
| return SELECTION_ANY_PATTERN.matcher(template.getPattern()).matches(); |
| default: |
| return false; |
| } |
| } |
| |
| |
| protected String extractPrefix(final AssistInvocationContext context) { |
| return context.getIdentifierPrefix(); |
| } |
| |
| protected List<Template> getTemplates(final String contextTypeId) { |
| return ImCollections.newList(this.templateStore.getTemplates(contextTypeId)); |
| } |
| |
| protected abstract @Nullable TemplateContextType getContextType( |
| final AssistInvocationContext context, final TextRegion region); |
| |
| protected @Nullable SourceEditorTemplateContext createTemplateContext( |
| final AssistInvocationContext context, final TextRegion region, |
| final int flags) { |
| final TemplateContextType contextType= getContextType(context, region); |
| if (contextType != null) { |
| return new SourceEditorTemplateContext(contextType, context.getDocument(), |
| region.getStartOffset(), region.getLength(), |
| context.getEditor(), flags ); |
| } |
| return null; |
| } |
| |
| protected TemplateProposal createProposal(final Template template, |
| final DocumentTemplateContext context, final String prefix, final TextRegion region, |
| final int relevance) { |
| return new TemplateProposal(template, context, region, getImage(template), relevance); |
| } |
| |
| protected @Nullable Image getImage(final Template template) { |
| return LTKUIPlugin.getInstance().getImageRegistry().get(LTKUI.OBJ_TEXT_TEMPLATE_IMAGE_ID); |
| } |
| |
| } |