blob: 62953a953105d9cc3561d17d772c9d2b04baa023 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2021 Christoph Läubrich 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:
* Christoph Läubrich - Inital API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.genericeditor;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.swt.widgets.Shell;
/**
* Extension of the ContentAssistant that supports the following additional
* features:
* <ul>
* <li>#updateTokens refresh the registration of
* {@link IContentAssistProcessor}s on document changes</li>
* <li>Using a ContentTypeRelatedExtensionTracker to dynamically track
* IContentAssistProcessor in the OSGi service registry
* </ul>
*
* @author christoph
*
*/
public class GenericEditorContentAssistant extends ContentAssistant {
private static final DefaultContentAssistProcessor DEFAULT_CONTENT_ASSIST_PROCESSOR = new DefaultContentAssistProcessor();
private ContentTypeRelatedExtensionTracker<IContentAssistProcessor> contentAssistProcessorTracker;
private Set<IContentType> types;
private List<IContentAssistProcessor> processors;
/**
* Creates a new GenericEditorContentAssistant instance for the given content
* types and contentAssistProcessorTracker
*
* @param contentAssistProcessorTracker the tracker to use for tracking
* additional
* {@link IContentAssistProcessor}s in the
* OSGi service factory
* @param processors the static processor list
* @param types the {@link IContentType} that are used
* to filter appropriate candidates from
* the registry
*/
public GenericEditorContentAssistant(
ContentTypeRelatedExtensionTracker<IContentAssistProcessor> contentAssistProcessorTracker,
List<IContentAssistProcessor> processors, Set<IContentType> types) {
super(true);
this.contentAssistProcessorTracker = contentAssistProcessorTracker;
this.processors = Objects.requireNonNullElseGet(processors, () -> Collections.emptyList());
this.types = types;
setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_BELOW);
setProposalPopupOrientation(IContentAssistant.PROPOSAL_REMOVE);
setAutoActivationDelay(0);
enableColoredLabels(true);
enableAutoActivation(true);
setInformationControlCreator(new AbstractReusableInformationControlCreator() {
@Override
protected IInformationControl doCreateInformationControl(Shell parent) {
return new DefaultInformationControl(parent);
}
});
}
/**
* Updates the {@link IContentAssistProcessor} registrations according to the
* documents content-type tokens
*
* @param document the document to use for updating the tokens
*/
public void updateTokens(IDocument document) {
updateProcessors(document);
contentAssistProcessorTracker.getTracked().stream().filter(s -> s.isPresent()).map(s -> s.get())
.forEach(p -> updateProcessorToken(p, document));
}
private void updateProcessors(IDocument iDocument) {
if (processors.isEmpty()) {
updateProcessorToken(DEFAULT_CONTENT_ASSIST_PROCESSOR, iDocument);
} else {
for (IContentAssistProcessor processor : processors) {
updateProcessorToken(processor, iDocument);
}
}
}
private void updateProcessorToken(IContentAssistProcessor processor, IDocument document) {
removeContentAssistProcessor(processor);
addContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE);
if (document != null) {
for (String contentType : document.getLegalContentTypes()) {
addContentAssistProcessor(processor, contentType);
}
}
if (processor != DEFAULT_CONTENT_ASSIST_PROCESSOR) {
removeContentAssistProcessor(DEFAULT_CONTENT_ASSIST_PROCESSOR);
}
}
@Override
public void uninstall() {
contentAssistProcessorTracker.stopTracking();
super.uninstall();
}
@Override
public void install(ITextViewer textViewer) {
super.install(textViewer);
updateProcessors(textViewer.getDocument());
contentAssistProcessorTracker.onAdd(added -> {
if (types.contains(added.getContentType())) {
IContentAssistProcessor processor = added.get();
if (processor != null) {
updateProcessorToken(processor, textViewer.getDocument());
}
}
});
contentAssistProcessorTracker.onRemove(removed -> {
if (removed.isPresent()) {
removeContentAssistProcessor(removed.get());
}
});
contentAssistProcessorTracker.startTracking();
}
}