/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.editor.orion.client.signature; import elemental.dom.Element; import elemental.dom.Node; import elemental.events.EventListener; import elemental.html.SpanElement; import com.google.common.base.Optional; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.ide.api.editor.signature.ParameterInfo; import org.eclipse.che.ide.api.editor.signature.SignatureHelp; import org.eclipse.che.ide.api.editor.signature.SignatureInfo; import org.eclipse.che.ide.util.Pair; import org.eclipse.che.ide.util.dom.Elements; import java.util.ArrayList; import java.util.List; /** * Widget for showing signature information * * @author Evgen Vidolob */ public class SignatureHelpView extends PopupPanel { private final SignatureHelpResources resources; private final Element rootElement; private final Element signatures; private final Element overloads; private SignatureHelp signatureHelp; private int activeSignature; private List<Pair<Integer, Integer>> signatureViews; private int x; private int y; @Inject public SignatureHelpView(SignatureHelpResources resources) { super(true); this.resources = resources; rootElement = Elements.createDivElement(resources.css().main(), resources.css().parameterHintsWidget()); Element wrapper = Elements.createDivElement(resources.css().wrapper()); rootElement.appendChild(wrapper); Element buttons = Elements.createDivElement(resources.css().buttons()); wrapper.appendChild(buttons); Element previous = Elements.createDivElement(resources.css().button(), resources.css().previous()); previous.appendChild((Node)resources.arrow().getSvg().getElement()); buttons.appendChild(previous); previous.addEventListener(elemental.events.Event.CLICK, new EventListener() { @Override public void handleEvent(elemental.events.Event evt) { previous(); } }, true); Element next = Elements.createDivElement(resources.css().button(), resources.css().next()); next.appendChild((Node)resources.arrow().getSvg().getElement()); next.addEventListener(elemental.events.Event.CLICK, new EventListener() { @Override public void handleEvent(elemental.events.Event evt) { next(); } }, true); buttons.appendChild(next); overloads = Elements.createDivElement(resources.css().overloads()); wrapper.appendChild(overloads); signatures = Elements.createDivElement(resources.css().signatures()); wrapper.appendChild(signatures); Widget widget = new ElementWidget((com.google.gwt.dom.client.Element)rootElement); setWidget(widget); } private void previous() { if (signatureViews.size() < 2) { return; } activeSignature--; if (activeSignature < 0) { activeSignature = signatureViews.size() - 1; } this.select(activeSignature); } private void next() { if (signatureViews.size() < 2) { return; } activeSignature = (activeSignature + 1) % signatureViews.size(); select(activeSignature); } public void showSignature(Promise<Optional<SignatureHelp>> promise, final int x, final int y) { this.x = x; this.y = y; promise.then(new Operation<Optional<SignatureHelp>>() { @Override public void apply(Optional<SignatureHelp> arg) throws OperationException { if (arg.isPresent() && !arg.get().getSignatures().isEmpty()) { activeSignature = 0; signatureHelp = arg.get(); if (signatureHelp.getActiveSignature().isPresent()) { activeSignature = signatureHelp.getActiveSignature().get(); } show(); render(); select(activeSignature); } } }); } private void select(int position) { Pair<Integer, Integer> signaturePosition = signatureViews.get(position); if (signaturePosition == null) { return; } signatures.getStyle().setHeight(signaturePosition.second + "px"); signatures.setScrollTop(signaturePosition.first); String overloads = "" + (position + 1); if (signatureViews.size() < 10) { overloads += ("/" + signatureViews.size()); } this.overloads.setInnerText(overloads); setPopupPosition(x, y - getElement().getOffsetHeight()); } private void render() { if (signatureHelp.getSignatures().size() > 1) { Elements.addClassName(resources.css().multiple(), rootElement); overloads.getStyle().setDisplay("block"); } else { Elements.removeClassName(resources.css().multiple(), rootElement); overloads.getStyle().setDisplay("none"); } signatures.setInnerHTML(""); signatureViews = new ArrayList<>(); int height = 0; for (SignatureInfo signatureInfo : signatureHelp.getSignatures()) { Element signatureElement = renderSignature(signatures, signatureInfo, signatureHelp.getActiveParameter()); renderDocumentation(signatureElement, signatureInfo, signatureHelp.getActiveParameter()); int signatureHeight = signatureElement.getOffsetHeight(); signatureViews.add(Pair.of(height, signatureHeight)); height += signatureHeight; } } private void renderDocumentation(Element element, SignatureInfo signatureInfo, Optional<Integer> activeParameter) { if (signatureInfo.getDocumentation().isPresent()) { elemental.html.DivElement documentation = Elements.createDivElement(resources.css().documentation()); documentation.setTextContent(signatureInfo.getDocumentation().get()); element.appendChild(documentation); } if (signatureInfo.getParameters().isPresent() && activeParameter.isPresent() && signatureInfo.getParameters().get().size() > activeParameter.get()) { ParameterInfo parameterInfo = signatureInfo.getParameters().get().get(activeParameter.get()); if (parameterInfo.getDocumentation().isPresent()) { elemental.html.DivElement parameter = Elements.createDivElement(resources.css().documentationParameter()); SpanElement label = Elements.createSpanElement(resources.css().documentationParameter()); label.setTextContent(parameterInfo.getLabel()); SpanElement documentation = Elements.createSpanElement(resources.css().documentation()); documentation.setTextContent(parameterInfo.getDocumentation().get()); parameter.appendChild(label); parameter.appendChild(documentation); element.appendChild(parameter); } } } private Element renderSignature(Element signatures, SignatureInfo signatureInfo, Optional<Integer> activeParameter) { Element signatureElement = Elements.createDivElement(); signatures.appendChild(signatureElement); Element code = Elements.createDivElement(); signatureElement.appendChild(code); boolean hasParameters = signatureInfo.getParameters().isPresent() && !signatureInfo.getParameters().get().isEmpty(); if (hasParameters) { renderParameters(code, signatureInfo, activeParameter); } else { Node label = code.appendChild(Elements.createSpanElement()); label.setTextContent(signatureInfo.getLabel()); } return signatureElement; } private void renderParameters(Element parent, SignatureInfo signatureInfo, Optional<Integer> activeParameter) { int end = signatureInfo.getLabel().length(); int idx; Element element; for (int i = signatureInfo.getParameters().get().size() - 1; i >= 0; i--) { ParameterInfo parameterInfo = signatureInfo.getParameters().get().get(i); idx = signatureInfo.getLabel().lastIndexOf(parameterInfo.getLabel(), end); int signatureLabelOffset = 0; int signatureLabelEnd = 0; if (idx >= 0) { signatureLabelOffset = idx; signatureLabelEnd = idx + parameterInfo.getLabel().length(); } element = Elements.createSpanElement(); element.setTextContent(signatureInfo.getLabel().substring(signatureLabelEnd, end)); parent.insertBefore(element, parent.getFirstElementChild()); element = Elements.createSpanElement(resources.css().parameter()); if (activeParameter.isPresent() && i == activeParameter.get()) { Elements.addClassName(resources.css().active(), element); } element.setTextContent(signatureInfo.getLabel().substring(signatureLabelOffset, signatureLabelEnd)); parent.insertBefore(element, parent.getFirstElementChild()); end = signatureLabelOffset; } element = Elements.createSpanElement(); element.setTextContent(signatureInfo.getLabel().substring(0, end)); parent.insertBefore(element, parent.getFirstElementChild()); } /** The method used to hide popup with parameters when user press 'Escape' button. */ @Override protected void onPreviewNativeEvent(Event.NativePreviewEvent event) { super.onPreviewNativeEvent(event); switch (event.getTypeInt()) { case Event.ONKEYDOWN: if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) { hide(); } break; } } }