/*=============================================================================# # Copyright (c) 2014-2016 Stephan Wahlbrink (WalWare.de) 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: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.docmlet.wikitext.internal.ui.sourceediting; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; import org.eclipse.jface.viewers.StyledString; import org.eclipse.swt.graphics.Point; import de.walware.jcommons.collections.ImList; import de.walware.ecommons.ltk.core.model.INameAccessSet; import de.walware.ecommons.ltk.ui.sourceediting.ISourceEditor; import de.walware.ecommons.ltk.ui.sourceediting.assist.AssistInvocationContext; import de.walware.ecommons.ltk.ui.sourceediting.assist.AssistProposalCollector; import de.walware.ecommons.ltk.ui.sourceediting.assist.ContentAssist; import de.walware.ecommons.ltk.ui.sourceediting.assist.IAssistCompletionProposal; import de.walware.ecommons.ltk.ui.sourceediting.assist.IContentAssistComputer; import de.walware.ecommons.ltk.ui.sourceediting.assist.SimpleCompletionProposal; import de.walware.docmlet.wikitext.core.ast.WikitextAstNode; import de.walware.docmlet.wikitext.core.markup.IMarkupLanguage; import de.walware.docmlet.wikitext.core.model.IWikidocModelInfo; import de.walware.docmlet.wikitext.core.model.IWikitextSourceElement; import de.walware.docmlet.wikitext.core.model.WikitextNameAccess; import de.walware.docmlet.wikitext.core.source.MarkupLanguageDocumentSetupParticipant; import de.walware.docmlet.wikitext.ui.WikitextUI; import de.walware.docmlet.wikitext.ui.sourceediting.IMarkupCompletionExtension; public class MarkupLabelCompletionComputer implements IContentAssistComputer, IMarkupCompletionExtension { private static class LabelCompletionProposal extends SimpleCompletionProposal implements ICompletionProposalExtension6 { private final IWikitextSourceElement refElement; public LabelCompletionProposal(final AssistInvocationContext context, final String replacementString, final int replacementOffset, final IWikitextSourceElement refElement) { super(context, replacementString, replacementOffset); this.refElement= refElement; } @Override protected String getPluginId() { return WikitextUI.PLUGIN_ID; } @Override public StyledString getStyledDisplayString() { final StyledString styledString= new StyledString(getDisplayString()); if (this.refElement != null ) { final String name= this.refElement.getElementName().getDisplayName(); if (name != null) { styledString.append(" (", StyledString.DECORATIONS_STYLER); //$NON-NLS-1$ if ((this.refElement.getElementType() & IWikitextSourceElement.MASK_C2) == IWikitextSourceElement.C2_SECTIONING) { styledString.append("h:" + (this.refElement.getElementType() & 0xf) + " "); } styledString.append(name, StyledString.DECORATIONS_STYLER); styledString.append(')', StyledString.DECORATIONS_STYLER); } } return styledString; } @Override public boolean isAutoInsertable() { return false; } @Override protected int computeReplacementLength(final int replacementOffset, final Point selection, final int caretOffset, final boolean overwrite) throws BadLocationException { int end= Math.max(caretOffset, selection.x + selection.y); if (overwrite) { final IDocument document= getInvocationContext().getDocument(); end--; while (++end < document.getLength()) { final char c= document.getChar(end); if (Character.isLetterOrDigit(c) || c == '-' || c == '_' || c == ':' || c == '.') { continue; } break; } } return (end - replacementOffset); } } private IMarkupLanguage markupLanguage; private IMarkupCompletionExtension completionExtension; @Override public void sessionStarted(final ISourceEditor editor, final ContentAssist assist) { this.markupLanguage= MarkupLanguageDocumentSetupParticipant.getMarkupLanguage( editor.getViewer().getDocument(), editor.getDocumentContentInfo().getPartitioning() ); if (this.markupLanguage != null) { this.completionExtension= (IMarkupCompletionExtension) Platform.getAdapterManager().getAdapter(this.markupLanguage, IMarkupCompletionExtension.class); } } @Override public void sessionEnded() { this.markupLanguage= null; this.completionExtension= null; } protected IMarkupLanguage getMarkupLanguage() { return this.markupLanguage; } @Override public IStatus computeCompletionProposals(final AssistInvocationContext context, final int mode, final AssistProposalCollector proposals, final IProgressMonitor monitor) { final IMarkupLanguage markupLanguage= this.markupLanguage; final IMarkupCompletionExtension ext= (this.completionExtension != null) ? this.completionExtension : this; final IWikidocModelInfo modelInfo= (IWikidocModelInfo) context.getModelInfo(); { final CompletionType type= ext.getLinkAnchorLabel(context, markupLanguage); if (type != null) { addLabelProposals(context, type, modelInfo.getLinkAnchorLabels(), proposals); } } { final CompletionType type= ext.getLinkRefLabel(context, markupLanguage); if (type != null) { addLabelProposals(context, type, modelInfo.getLinkRefLabels(), proposals); } } return Status.OK_STATUS; } @Override public IStatus computeInformationProposals(final AssistInvocationContext context, final AssistProposalCollector proposals, final IProgressMonitor monitor) { return null; } private void addLabelProposals(final AssistInvocationContext context, final CompletionType type, final INameAccessSet<WikitextNameAccess> labels, final AssistProposalCollector proposals) { for (final String label : labels.getNames()) { if (label.startsWith(type.getLookupPrefix())) { final ImList<WikitextNameAccess> accessList= labels.getAllInUnit(label); if (accessList.size() == 1 && isCurrent(accessList.get(0).getNameNode(), context.getInvocationOffset()) ) { continue; } WikitextNameAccess defAccess= null; IWikitextSourceElement defElement= null; ITER_ACCESS: for (final WikitextNameAccess access : accessList) { if (access.isWriteAccess()) { for (final Object attachment : access.getNode().getAttachments()) { if (attachment instanceof IWikitextSourceElement) { defAccess= access; defElement= (IWikitextSourceElement) attachment; break ITER_ACCESS; } } if (defAccess == null) { defAccess= access; } } } if (defAccess == null) { defAccess= accessList.get(0); } proposals.add(createProposal(defAccess.getDisplayName(), context, type.getSourcePrefix(), defElement )); } } } private boolean isCurrent(final WikitextAstNode node, final int offset) { return (node != null && node.getOffset() <= offset && node.getEndOffset() >= offset); } protected IAssistCompletionProposal createProposal(final String name, final AssistInvocationContext context, final String prefix, final IWikitextSourceElement element) { return new LabelCompletionProposal(context, name, context.getInvocationOffset() - prefix.length(), element ); } @Override public CompletionType getLinkAnchorLabel(final AssistInvocationContext context, final IMarkupLanguage markupLanguage) { try { final IDocument document= context.getDocument(); final int endOffset= context.getOffset(); int beginOffset= endOffset; while (beginOffset > 0) { final char c= document.getChar(--beginOffset); if (Character.isLetterOrDigit(c) || c == '-' || c == '_' || c == ':' || c == '.') { continue; } if (c == '#') { beginOffset++; return new CompletionType(document.get(beginOffset, endOffset - beginOffset)); } } } catch (final BadLocationException e) {} return null; } @Override public CompletionType getLinkRefLabel(final AssistInvocationContext context, final IMarkupLanguage markupLanguage) { return null; } }