Merge branch 'master' into javadoc

# Conflicts:
#	vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/StsValueHint.java
This commit is contained in:
BoykoAlex
2016-11-18 20:42:43 -05:00
59 changed files with 1105 additions and 298 deletions

1
concourse/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
credentials.yml

3
concourse/destroy-pipeline.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
fly -t tools destroy-pipeline -p sts4

View File

@@ -0,0 +1,15 @@
FROM ubuntu:16.04
ADD npmrc /root/.npmrc
RUN apt-get update && apt-get install -y \
build-essential \
git \
openjdk-8-jdk \
maven \
curl
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \
&& apt-get install -y nodejs
CMD /bin/bash

1
concourse/docker/npmrc Normal file
View File

@@ -0,0 +1 @@
unsafe-perm=true

50
concourse/pipeline.yml Normal file
View File

@@ -0,0 +1,50 @@
resources:
- name: docker-git
type: git
source:
uri: git@github.com:spring-projects/sts4.git
branch: master
username: kdvolder
private_key: {{rsa_id}}
paths:
- concourse/docker
- name: sts4-git
type: git
source:
uri: git@github.com:spring-projects/sts4.git
branch: master
private_key: {{rsa_id}}
- name: docker-image
type: docker-image
source:
username: {{docker_hub_username}}
email: {{docker_hub_email}}
password: {{docker_hub_password}}
repository: kdvolder/sts4-build-env
jobs:
- name: build-docker-image
serial: true
plan:
- get: docker-git
trigger: true
- put: docker-image
params:
build: docker-git/concourse/docker
get_params:
skip_download: true
- name: build-vsix
plan:
- get: sts4-git
trigger: true
- task: build-vscode-extensions
config:
inputs:
- name: sts4-git
platform: linux
image_resource:
type: docker-image
source:
repository: kdvolder/sts4-build-env
run:
path: "./build-all.sh"
dir: "sts4-git/vscode-extensions"

2
concourse/set-pipeline.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
fly -t tools set-pipeline --load-vars-from ${HOME}/.sts4-concourse-credentials.yml -p sts4 -c pipeline.yml

23
vscode-extensions/build-all.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
set -e
base_dir=`pwd`
cd ${base_dir}/commons-vscode
npm install
cd $base_dir
for i in vscode-application-properties vscode-application-yaml vscode-manifest-yaml ; do
cd ${base_dir}/${i}
echo "***************************************************************************************"
echo "***************************************************************************************"
echo "***************************************************************************************"
echo "***** BUILDING: " $i
echo "***************************************************************************************"
echo "***************************************************************************************"
echo "***************************************************************************************"
npm install ../commons-vscode
npm install
npm run vsce-package
done

View File

@@ -1,9 +1,5 @@
package org.springframework.ide.vscode.application.properties.metadata.hints;
import static org.springframework.ide.vscode.application.properties.metadata.util.DeprecationUtil.*;
import javax.inject.Provider;
import org.springframework.boot.configurationmetadata.Deprecation;
import org.springframework.boot.configurationmetadata.ValueHint;
import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil;
@@ -11,10 +7,13 @@ import org.springframework.ide.vscode.application.properties.metadata.util.Depre
import org.springframework.ide.vscode.commons.java.IJavaElement;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.HtmlSnippet;
import org.springframework.ide.vscode.commons.util.HtmlBuffer;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders;
/**
* Sts version of {@link ValueHint} contains similar data, but accomoates
@@ -28,12 +27,9 @@ import org.springframework.ide.vscode.commons.util.StringUtil;
*/
public class StsValueHint {
private static final HtmlSnippet EMPTY_DESCRIPTION = HtmlSnippet.italic("No description");
private static final Provider<HtmlSnippet> EMPTY_DESCRIPTION_PROVIDER = () -> EMPTY_DESCRIPTION;
private final String value;
private final Provider<HtmlSnippet> description;
private final HoverInfo description;
private final Deprecation deprecation;
/**
@@ -42,7 +38,7 @@ public class StsValueHint {
* This constructor is private. Use one of the provided
* static 'create' methods instead.
*/
private StsValueHint(String value, Provider<HtmlSnippet> description, Deprecation deprecation) {
private StsValueHint(String value, HoverInfo description, Deprecation deprecation) {
this.value = value==null?"null":value.toString();
Assert.isLegal(!this.value.startsWith("StsValueHint"));
this.description = description;
@@ -62,7 +58,7 @@ public class StsValueHint {
}
public static StsValueHint create(String value) {
return new StsValueHint(value, EMPTY_DESCRIPTION_PROVIDER, null);
return new StsValueHint(value, DescriptionProviders.NO_DESCRIPTION, null);
}
public static StsValueHint create(ValueHint hint) {
@@ -96,36 +92,46 @@ public class StsValueHint {
/**
* Create a html snippet from a text snippet.
*/
private static Provider<HtmlSnippet> textSnippet(String description) {
private static HoverInfo textSnippet(String description) {
if (StringUtil.hasText(description)) {
return () -> HtmlSnippet.text(description);
return DescriptionProviders.text(description);
}
return EMPTY_DESCRIPTION_PROVIDER;
return DescriptionProviders.NO_DESCRIPTION;
}
public String getValue() {
return value;
}
public HtmlSnippet getDescription() {
return description.get();
public HoverInfo getDescription() {
return description;
}
public Provider<HtmlSnippet> getDescriptionProvider() {
public HoverInfo getDescriptionProvider() {
return description;
}
public static Provider<HtmlSnippet> javaDocSnippet(IJavaElement je) {
return () -> {
try {
HtmlSnippet jdoc = HtmlSnippet.raw(je.getJavaDoc().html());
if (jdoc!=null) {
return jdoc;
}
} catch (Exception e) {
Log.log(e);
public static HoverInfo javaDocSnippet(IJavaElement je) {
try {
IJavadoc jdoc = je.getJavaDoc();
if (jdoc != null) {
return new HoverInfo() {
@Override
public void renderAsMarkdown(StringBuilder buffer) {
// TODO not correct md
buffer.append(jdoc.markdown());
}
@Override
public void renderAsHtml(HtmlBuffer buffer) {
buffer.raw(jdoc.html());
}
};
}
return EMPTY_DESCRIPTION;
};
} catch (Exception e) {
Log.log(e);
}
return DescriptionProviders.NO_DESCRIPTION;
}
@Override

View File

@@ -3,7 +3,7 @@ package org.springframework.ide.vscode.application.properties.metadata.types;
import javax.inject.Provider;
import org.springframework.boot.configurationmetadata.Deprecation;
import org.springframework.ide.vscode.commons.util.HtmlSnippet;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders;
@@ -27,7 +27,7 @@ public class TypedProperty implements YTypedProperty {
/**
* Provides a description for this property.
*/
private final Provider<HtmlSnippet> descriptionProvider;
private final HoverInfo descriptionProvider;
private final Deprecation deprecation;
@@ -35,7 +35,7 @@ public class TypedProperty implements YTypedProperty {
this(name, type, DescriptionProviders.NO_DESCRIPTION, deprecation);
}
public TypedProperty(String name, Type type, Provider<HtmlSnippet> descriptionProvider, Deprecation deprecation) {
public TypedProperty(String name, Type type, HoverInfo descriptionProvider, Deprecation deprecation) {
this.name = name;
this.type = type;
this.descriptionProvider = descriptionProvider;
@@ -56,14 +56,14 @@ public class TypedProperty implements YTypedProperty {
}
@Override
public HtmlSnippet getDescription() {
public HoverInfo getDescription() {
//TODO: real implementation that somehow gets this from somewhere (i.e. the JavaDoc)
// Note that presently the application.yml and application.properties editor do not actually
// use this description provider but produce hover infos in a different way (so this is only
// used in Schema-based content assist, reconciling and hovering.
//So in that sense putting a good implementation here is kind of pointless right now.
//More refactoring needs to be done to also make use of this.
return descriptionProvider.get();
return descriptionProvider;
}
public static Type typeOf(TypedProperty typedProperty) {

View File

@@ -60,7 +60,11 @@
<artifactId>jackson-datatype-jdk8</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>${reactor-version}</version>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.springframework.ide.vscode</groupId>

View File

@@ -147,6 +147,7 @@ public abstract class LaunguageServerApp {
Function<MessageConsumer, MessageConsumer> wrapper = (MessageConsumer consumer) -> {
return (msg) -> {
try {
LOG.info(""+msg);
consumer.consume(msg);
} catch (UnsupportedOperationException e) {
//log a warning and ignore. We are getting some messages from vsCode the server doesn't know about

View File

@@ -10,15 +10,27 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.hover;
import org.springframework.ide.vscode.commons.util.HtmlBuffer;
/**
* Placeholder. Still need to figure out what exactly we should do with this in vscode.
* Placeholder. Still need to figure out what exactly we should do with this in
* vscode. TODO: rename to Renderable
*/
public interface HoverInfo {
String renderAsText();
void renderAsHtml(HtmlBuffer buffer);
String renderAsHtml();
void renderAsMarkdown(StringBuilder buffer);
String renderAsMarkdown();
default String toMarkdown() {
StringBuilder buffer = new StringBuilder();
renderAsMarkdown(buffer);
return buffer.toString();
}
default String toHtml() {
HtmlBuffer buffer = new HtmlBuffer();
renderAsHtml(buffer);
return buffer.toString();
}
}

View File

@@ -11,9 +11,12 @@
package org.springframework.ide.vscode.commons.languageserver.hover;
import org.springframework.ide.vscode.commons.languageserver.util.IDocument;
import org.springframework.ide.vscode.commons.languageserver.util.IRegion;
public interface IHoverEngine {
import reactor.util.function.Tuple2;
HoverInfo getHover(IDocument document, int offset) throws Exception;
public interface HoverInfoProvider {
Tuple2<HoverInfo, IRegion> getHoverInfo(IDocument document, int offset) throws Exception;
}

View File

@@ -14,23 +14,27 @@ import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.util.IRegion;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.languageserver.util.TextDocument;
import org.springframework.ide.vscode.commons.util.Futures;
import reactor.util.function.Tuple2;
public class VscodeHoverEngineAdapter implements VscodeHoverEngine {
private IHoverEngine engine;
private HoverInfoProvider hoverInfoProvider;
private SimpleLanguageServer server;
final static Logger logger = LoggerFactory.getLogger(VscodeHoverEngineAdapter.class);
public VscodeHoverEngineAdapter(SimpleLanguageServer server, IHoverEngine engine) {
this.engine = engine;
public VscodeHoverEngineAdapter(SimpleLanguageServer server, HoverInfoProvider hoverInfoProvider) {
this.hoverInfoProvider = hoverInfoProvider;
this.server = server;
}
@@ -44,16 +48,17 @@ public class VscodeHoverEngineAdapter implements VscodeHoverEngine {
TextDocument doc = documents.get(params);
if (doc!=null) {
int offset = doc.toOffset(params.getPosition());
HoverInfo hoverInfo = engine.getHover(doc, offset);
if (hoverInfo != null) {
Hover hover = new Hover();
hover.setContents(Collections.singletonList(hoverInfo.renderAsMarkdown()));
Tuple2<HoverInfo, IRegion> hoverTuple = hoverInfoProvider.getHoverInfo(doc, offset);
if (hoverTuple != null) {
HoverInfo hoverInfo = hoverTuple.getT1();
IRegion region = hoverTuple.getT2();
Range range = doc.toRange(region.getOffset(), region.getLength());
Hover hover = new Hover(Collections.singletonList(hoverInfo.toMarkdown()), range);
return Futures.of(hover);
}
else{
return Futures.of(null);
}
}
} catch (Exception e) {
logger.error("error computing hover", e);

View File

@@ -20,12 +20,8 @@ import java.net.URLEncoder;
*/
public class HtmlBuffer {
private StringBuffer buffer = new StringBuffer();
private boolean epilogAdded = false; //to ensure only added once.
private StringBuilder buffer = new StringBuilder();
public HtmlBuffer() {
this.buffer = new StringBuffer();
}
/**
* Append text, applies escaping to the text as needed.
@@ -38,9 +34,6 @@ public class HtmlBuffer {
* Append 'raw' text. Doesn't apply any escaping.
*/
public void raw(String rawText) {
if (epilogAdded) {
throw new IllegalStateException("Can not append more text after epilog was added");
}
buffer.append(rawText);
}
@@ -57,10 +50,7 @@ public class HtmlBuffer {
public String toString() {
if (!epilogAdded && buffer.length()>0) {
epilogAdded = true;
addPrologAndEpilog();
}
return buffer.toString();
}

View File

@@ -12,9 +12,13 @@ package org.springframework.ide.vscode.commons.util;
/**
* A snippet that can be rendered into html.
* <p/>
* Deprecated. Use DescriptionProviders instead.
*
* @author Kris De Volder
*
*/
@Deprecated
public abstract class HtmlSnippet {
public abstract void render(HtmlBuffer html);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* Copyright (c) 2015, 2016 Pivotal, Inc.
* 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
@@ -8,25 +8,14 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.manifest.yaml;
package org.springframework.ide.vscode.commons.util;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
public class HtmlUtil {
public class YamlHoverInfo implements HoverInfo {
@Override
public String renderAsText() {
return null;
}
@Override
public String renderAsHtml() {
return null;
}
@Override
public String renderAsMarkdown() {
return null;
public static String text2html(String s) {
HtmlBuffer buf = new HtmlBuffer();
buf.text(s);
return buf.toString();
}
}

View File

@@ -14,6 +14,8 @@ import java.util.Collection;
import java.util.Collections;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
@@ -49,19 +51,20 @@ public abstract class TopLevelAssistContext implements YamlAssistContext {
return Collections.emptyList();
}
// @Override
// public HoverInfo getHoverInfo() {
// return null;
// }
//
// public HoverInfo getHoverInfo(YamlPathSegment lastSegment) {
// return null;
// }
//
// @Override
// public HoverInfo getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion) {
// return null;
// }
@Override
public HoverInfo getHoverInfo() {
return null;
}
@Override
public HoverInfo getHoverInfo(YamlPathSegment lastSegment) {
return null;
}
@Override
public HoverInfo getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion) {
return null;
}
protected abstract YamlAssistContext getDocumentContext(int documentSelector);
}

View File

@@ -22,8 +22,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.util.CollectionUtil;
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
import org.springframework.ide.vscode.commons.yaml.hover.YPropertyHoverInfo;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.YamlPathSegmentType;
@@ -215,35 +218,42 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
}
// @Override
// public HoverInfo getHoverInfo() {
// if (parent!=null) {
// return parent.getHoverInfo(contextPath.getLastSegment());
// }
// return null;
// }
//
@Override
public HoverInfo getHoverInfo() {
if (parent!=null) {
return parent.getHoverInfo(contextPath.getLastSegment());
}
return null;
}
public YType getType() {
return type;
}
//
// @Override
// public HoverInfo getHoverInfo(YamlPathSegment lastSegment) {
// //Hoverinfo is only attached to YTypedProperties so...
// switch (lastSegment.getType()) {
// case VAL_AT_KEY:
// case KEY_AT_KEY:
// YTypedProperty prop = getProperty(lastSegment.toPropString());
// if (prop!=null) {
// return new YPropertyHoverInfo(contextPath.toPropString(), getType(), prop);
// }
// break;
// default:
// }
// return null;
// }
// private YTypedProperty getProperty(String name) {
// return typeUtil.getPropertiesMap(getType()).get(name);
// }
@Override
public HoverInfo getHoverInfo(YamlPathSegment lastSegment) {
//Hoverinfo is only attached to YTypedProperties so...
switch (lastSegment.getType()) {
case VAL_AT_KEY:
case KEY_AT_KEY:
YTypedProperty prop = getProperty(lastSegment.toPropString());
if (prop!=null) {
return YPropertyHoverInfo.create(contextPath.toPropString(), getType(), prop);
}
break;
default:
}
return null;
}
@Override
public HoverInfo getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion) {
//By default we don't provide value-specific hover, so just show the same hover
// as the assistContext the value is in. This is likely more interesting than showing nothing at all.
return getHoverInfo();
}
private YTypedProperty getProperty(String name) {
return typeUtil.getPropertiesMap(getType()).get(name);
}
}

View File

@@ -13,7 +13,10 @@ package org.springframework.ide.vscode.commons.yaml.completion;
import java.util.Collection;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.yaml.path.YamlNavigable;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
@@ -25,8 +28,8 @@ public interface YamlAssistContext extends YamlNavigable<YamlAssistContext> {
//TODO: conceptually... the right thing would be to only implement the second of these
// two methods and get rid of the first one.
// HoverInfo getHoverInfo();
// HoverInfo getHoverInfo(YamlPathSegment lastSegment);
//
// HoverInfo getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion);
HoverInfo getHoverInfo();
HoverInfo getHoverInfo(YamlPathSegment lastSegment);
HoverInfo getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion);
}

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.yaml.hover;
import static org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders.bold;
import static org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders.concat;
import static org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders.lineBreak;
import static org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders.link;
import static org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders.text;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
/**
* Nicely formatted hover info for a {@link YTypedProperty}
*
* @author Kris De Volder
*/
public class YPropertyHoverInfo {
public static HoverInfo create(String contextProperty, YType contextType, YTypedProperty prop) {
Builder<HoverInfo> html = ImmutableList.builder();
if (StringUtil.hasText(contextProperty)) {
html.add(text(contextProperty));
html.add(text("."));
}
html.add(bold(text(prop.getName())));
html.add(lineBreak());
YType type = prop.getType();
if (type != null) {
html.add(link(type.toString(), /* no URL */ null));
}
HoverInfo description = prop.getDescription();
if (description != null) {
html.add(lineBreak());
html.add(description);
}
return concat(html.build());
}
}

View File

@@ -0,0 +1,117 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.yaml.hover;
import java.util.List;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.languageserver.util.IDocument;
import org.springframework.ide.vscode.commons.languageserver.util.IRegion;
import org.springframework.ide.vscode.commons.languageserver.util.Region;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef;
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContext;
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.scanner.ScannerException;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
/**
* Implements {@link HoverInfoProvider} for Yaml files based on
* {@link YamlAssistContext}.
*
* @author Kris De Volder
*/
public class YamlHoverInfoProvider implements HoverInfoProvider {
private YamlASTProvider astProvider;
private YamlAssistContextProvider assistContextProvider;
private YamlStructureProvider structureProvider;
public YamlHoverInfoProvider(YamlASTProvider astProvider, YamlStructureProvider structureProvider,
YamlAssistContextProvider assistContextProvider) {
Assert.isNotNull(astProvider);
Assert.isNotNull(structureProvider);
Assert.isNotNull(assistContextProvider);
this.astProvider = astProvider;
this.structureProvider = structureProvider;
this.assistContextProvider = assistContextProvider;
}
@Override
public Tuple2<HoverInfo, IRegion> getHoverInfo(IDocument doc, int offset) throws Exception {
YamlFileAST ast = getAst(doc);
if (ast != null) {
IRegion region = getHoverRegion(ast, offset);
YamlDocument ymlDoc = new YamlDocument(doc, structureProvider);
YamlAssistContext assistContext = assistContextProvider.getGlobalAssistContext(ymlDoc);
if (assistContext != null) {
List<NodeRef<?>> astPath = ast.findPath(offset);
final YamlPath path = YamlPath.fromASTPath(astPath);
if (path != null) {
YamlPath assistPath = path;
if (assistPath.pointsAtKey()) {
// When a path points at a key we must tramsform it to a
// 'value-terminating path'
// to be able to reuse the 'getHoverInfo' method on
// YamlAssistContext (as navigation
// into 'key' is not defined for YamlAssistContext.
String key = path.getLastSegment().toPropString();
assistPath = path.dropLast().append(YamlPathSegment.valueAt(key));
}
assistContext = assistPath.traverse(assistContext);
if (assistContext != null) {
if (path.pointsAtValue()) {
HoverInfo info = assistContext.getValueHoverInfo(ymlDoc, new DocumentRegion(doc, region));
return Tuples.of(info, region);
}
HoverInfo info = assistContext.getHoverInfo();
return Tuples.of(info, region);
}
}
}
}
return null;
}
private IRegion getHoverRegion(YamlFileAST ast, int offset) {
if (ast != null) {
Node n = ast.findNode(offset);
if (n != null && n.getNodeId() == NodeId.scalar) {
int start = n.getStartMark().getIndex();
int end = n.getEndMark().getIndex();
return new Region(start, end - start);
}
}
return null;
}
private YamlFileAST getAst(IDocument doc) throws Exception {
try {
return astProvider.getAST(doc);
} catch (ParserException | ScannerException e) {
// ignore, the user just typed some crap
}
return null;
}
}

View File

@@ -21,8 +21,8 @@ import java.util.Set;
import javax.inject.Provider;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.util.EnumValueParser;
import org.springframework.ide.vscode.commons.util.HtmlSnippet;
import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders;
@@ -193,7 +193,7 @@ public class YTypeFactory {
propertyList.add(p);
}
public void addProperty(String name, YType type, Provider<HtmlSnippet> description) {
public void addProperty(String name, YType type, HoverInfo description) {
YTypedPropertyImpl prop;
addProperty(prop = new YTypedPropertyImpl(name, type));
prop.setDescriptionProvider(description);
@@ -314,7 +314,7 @@ public class YTypeFactory {
final private String name;
final private YType type;
private Provider<HtmlSnippet> descriptionProvider = DescriptionProviders.NO_DESCRIPTION;
private HoverInfo description = DescriptionProviders.NO_DESCRIPTION;
private YTypedPropertyImpl(String name, YType type) {
this.name = name;
@@ -337,14 +337,13 @@ public class YTypeFactory {
}
@Override
public HtmlSnippet getDescription() {
return descriptionProvider.get();
public HoverInfo getDescription() {
return description;
}
public void setDescriptionProvider(Provider<HtmlSnippet> descriptionProvider) {
this.descriptionProvider = descriptionProvider;
public void setDescriptionProvider(HoverInfo description) {
this.description = description;
}
}
public YAtomicType yatomic(String name) {

View File

@@ -10,7 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.yaml.schema;
import org.springframework.ide.vscode.commons.util.HtmlSnippet;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
/**
* @author Kris De Volder
@@ -18,5 +18,5 @@ import org.springframework.ide.vscode.commons.util.HtmlSnippet;
public interface YTypedProperty {
String getName();
YType getType();
HtmlSnippet getDescription();
HoverInfo getDescription();
}

View File

@@ -10,26 +10,32 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.yaml.util;
import static org.springframework.ide.vscode.commons.util.HtmlSnippet.*;
import java.io.InputStream;
import java.util.List;
import javax.inject.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.util.HtmlBuffer;
import org.springframework.ide.vscode.commons.util.HtmlSnippet;
import com.google.common.collect.ImmutableList;
/**
* Static methods and convenience constants for creating some 'description providers'.
* Static methods and convenience constants for creating some 'description
* providers'.
*
* @author Kris De Volder
*/
public class DescriptionProviders {
private static final String NO_DESCRIPTION_TEXT = "no description";
final static Logger logger = LoggerFactory.getLogger(DescriptionProviders.class);
public static final Provider<HtmlSnippet> NO_DESCRIPTION = () -> italic(text("no description"));
public static final HoverInfo NO_DESCRIPTION = italic(text(NO_DESCRIPTION_TEXT));
public static Provider<HtmlSnippet> snippet(final HtmlSnippet snippet) {
return new Provider<HtmlSnippet>() {
@@ -37,6 +43,7 @@ public class DescriptionProviders {
public String toString() {
return snippet.toString();
}
@Override
public HtmlSnippet get() {
return snippet;
@@ -44,24 +51,179 @@ public class DescriptionProviders {
};
}
public static Provider<HtmlSnippet> fromClasspath(final Class<?> klass, final String resourcePath) {
return new Provider<HtmlSnippet>() {
public static HoverInfo concat(HoverInfo... pieces) {
return concat(ImmutableList.copyOf(pieces));
}
public static HoverInfo concat(List<HoverInfo> pieces) {
if (pieces == null || pieces.size() == 0) {
throw new IllegalArgumentException("At least one hover information is required for concat");
} else if (pieces.size() == 1) {
return pieces.get(0);
} else {
return new ConcatHoverInfo(pieces);
}
}
public static HoverInfo italic(HoverInfo text) {
return new HoverInfo() {
@Override
public String toString() {
return "DescriptionFromClassPth(class="+klass.getSimpleName()+", "+resourcePath+")";
public void renderAsMarkdown(StringBuilder buffer) {
buffer.append("*");
text.renderAsMarkdown(buffer);
buffer.append("*");
}
@Override
public HtmlSnippet get() {
try {
InputStream stream = klass.getResourceAsStream(resourcePath);
if (stream!=null) {
return HtmlSnippet.text(IOUtil.toString(stream));
}
} catch (Exception e) {
logger.error("Error", e);;
}
return NO_DESCRIPTION.get();
public void renderAsHtml(HtmlBuffer buffer) {
buffer.raw("<i>");
text.renderAsHtml(buffer);
buffer.raw("</i>");
}
};
}
public static HoverInfo link(String text, String url) {
return new HoverInfo() {
@Override
public void renderAsMarkdown(StringBuilder buffer) {
buffer.append('[');
buffer.append(text);
buffer.append(']');
if (url != null) {
buffer.append('(');
buffer.append(url);
buffer.append(')');
}
}
@Override
public void renderAsHtml(HtmlBuffer buffer) {
buffer.raw("<a href=\"");
buffer.url("" + url);
buffer.raw("\">");
buffer.text(text);
buffer.raw("</a>");
}
};
}
public static HoverInfo lineBreak() {
return new HoverInfo() {
@Override
public void renderAsMarkdown(StringBuilder buffer) {
buffer.append("\n\n");
}
@Override
public void renderAsHtml(HtmlBuffer buffer) {
buffer.raw("<br>");
}
};
}
public static HoverInfo bold(HoverInfo text) {
return new HoverInfo() {
@Override
public void renderAsMarkdown(StringBuilder buffer) {
buffer.append("**");
text.renderAsMarkdown(buffer);
buffer.append("**");
}
@Override
public void renderAsHtml(HtmlBuffer buffer) {
buffer.raw("<b>");
text.renderAsHtml(buffer);
buffer.raw("</b>");
}
};
}
public static HoverInfo text(String text) {
return new HoverInfo() {
@Override
public void renderAsMarkdown(StringBuilder buffer) {
// TODO: handle escaping
buffer.append(text);
}
@Override
public void renderAsHtml(HtmlBuffer buffer) {
buffer.text(text);
}
};
}
public static HoverInfo fromClasspath(final Class<?> klass, final String resourcePath) {
return new HoverInfo() {
@Override
public void renderAsMarkdown(StringBuilder buffer) {
String extension = ".md";
String value = getText(klass, resourcePath, extension);
if (value != null) {
buffer.append(value);
} else {
NO_DESCRIPTION.renderAsMarkdown(buffer);
}
}
@Override
public void renderAsHtml(HtmlBuffer buffer) {
String extension = ".html";
String value = getText(klass, resourcePath, extension);
if (value != null) {
buffer.raw(value);
} else {
NO_DESCRIPTION.renderAsHtml(buffer);
}
}
private String getText(final Class<?> klass, final String resourcePath, String extension) {
try {
InputStream stream = klass.getResourceAsStream(resourcePath + extension);
if (stream != null) {
return IOUtil.toString(stream);
}
} catch (Exception e) {
logger.error("Error", e);
}
return null;
}
};
}
private static class ConcatHoverInfo implements HoverInfo {
private HoverInfo[] pieces;
ConcatHoverInfo(HoverInfo[] pieces) {
this.pieces = pieces;
}
public ConcatHoverInfo(List<HoverInfo> pieces) {
this(pieces.toArray(new HoverInfo[pieces.size()]));
}
@Override
public void renderAsHtml(HtmlBuffer buffer) {
for (HoverInfo hoverInfo : pieces) {
hoverInfo.renderAsHtml(buffer);
}
}
@Override
public void renderAsMarkdown(StringBuilder buffer) {
for (HoverInfo hoverInfo : pieces) {
hoverInfo.renderAsMarkdown(buffer);
}
}
}
}

View File

@@ -2,6 +2,7 @@ package org.springframework.ide.vscode.languageserver.testharness;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.*;
import java.util.ArrayList;
import java.util.Collections;
@@ -16,6 +17,7 @@ import javax.swing.text.BadLocationException;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.Range;
@@ -307,13 +309,16 @@ public class Editor {
return document.toPosition(selectionStart);
}
public void assertIsHoverRegion(String string) {
throw new UnsupportedOperationException("Not implemented yet!");
public void assertIsHoverRegion(String string) throws Exception {
int hoverPosition = getRawText().indexOf(string) + string.length() / 2;
Hover hover = harness.getHover(document, document.toPosition(hoverPosition));
assertEquals(string, getText(hover.getRange()));
}
public void assertHoverContains(String string, String string2) {
throw new UnsupportedOperationException("Not implemented yet!");
}
public void assertHoverContains(String hoverOver, String snippet) throws Exception {
int hoverPosition = getRawText().indexOf(hoverOver) + hoverOver.length() / 2;
Hover hover = harness.getHover(document, document.toPosition(hoverPosition));
assertContains(snippet, hover.getContents().toString()); }
public void assertNoHover(String string) {
throw new UnsupportedOperationException("Not implemented yet!");

View File

@@ -24,6 +24,7 @@ import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.MessageParams;
@@ -242,6 +243,15 @@ public class LanguageServerHarness {
return server.getTextDocumentService().completion(params).get();
}
public Hover getHover(TextDocumentInfo document, Position cursor) throws Exception {
TextDocumentPositionParams params = new TextDocumentPositionParams();
params.setPosition(cursor);
params.setTextDocument(document.getId());
return server.getTextDocumentService().hover(params ).get();
}
private CompletionItem resolveCompletionItem(CompletionItem unresolved) {
try {
return server.getTextDocumentService().resolveCompletionItem(unresolved).get();
@@ -313,5 +323,4 @@ public class LanguageServerHarness {
CompletionItem completion = editor.getFirstCompletion();
assertEquals(expected, completion.getLabel());
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.ide.vscode.project.harness;
package org.springframework.ide.vscode.languageserver.testharness;
import static org.junit.Assert.fail;

View File

@@ -89,24 +89,6 @@
<target>1.8</target>
</configuration>
</plugin>
<!-- Generate classpath.txt for VS Code -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.9</version>
<executions>
<execution>
<id>build-classpath</id>
<phase>generate-sources</phase>
<goals>
<goal>build-classpath</goal>
</goals>
</execution>
</executions>
<configuration>
<outputFile>classpath.txt</outputFile>
</configuration>
</plugin>
<!-- Configure fat jar -->
<plugin>
<groupId>org.springframework.boot</groupId>

View File

@@ -13,7 +13,7 @@ package org.springframework.ide.vscode.application.properties.test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.ide.vscode.application.properties.reconcile.ApplicationPropertiesProblemType.PROP_DUPLICATE_KEY;
import static org.springframework.ide.vscode.project.harness.TestAsserts.assertContains;
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains;
import java.nio.charset.Charset;
import java.nio.file.Path;

View File

@@ -101,24 +101,6 @@
<target>1.8</target>
</configuration>
</plugin>
<!-- Generate classpath.txt for VS Code -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.9</version>
<executions>
<execution>
<id>build-classpath</id>
<phase>generate-sources</phase>
<goals>
<goal>build-classpath</goal>
</goals>
</execution>
</executions>
<configuration>
<outputFile>classpath.txt</outputFile>
</configuration>
</plugin>
<!-- Configure fat jar -->
<plugin>
<groupId>org.springframework.boot</groupId>

View File

@@ -38,6 +38,7 @@ import org.springframework.ide.vscode.commons.languageserver.completion.IComplet
import org.springframework.ide.vscode.commons.languageserver.completion.LazyProposalApplier;
import org.springframework.ide.vscode.commons.languageserver.completion.ProposalApplier;
import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.util.CollectionUtil;
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
@@ -373,6 +374,25 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
return type;
}
@Override
public HoverInfo getHoverInfo(YamlPathSegment lastSegment) {
// TODO Auto-generated method stub
return null;
}
@Override
public HoverInfo getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion) {
// TODO Auto-generated method stub
return null;
}
@Override
public HoverInfo getHoverInfo() {
// TODO Auto-generated method stub
return null;
}
}
private static class IndexContext extends ApplicationYamlAssistContext {
@@ -483,6 +503,24 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon
return null;
}
@Override
public HoverInfo getHoverInfo() {
// TODO Auto-generated method stub
return null;
}
@Override
public HoverInfo getHoverInfo(YamlPathSegment lastSegment) {
// TODO Auto-generated method stub
return null;
}
@Override
public HoverInfo getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion) {
// TODO Auto-generated method stub
return null;
}
// @Override
// public HoverInfo getHoverInfo() {
// PropertyInfo prop = indexNav.getExactMatch();

View File

@@ -62,24 +62,6 @@
<target>1.8</target>
</configuration>
</plugin>
<!-- Generate classpath.txt for VS Code -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.9</version>
<executions>
<execution>
<id>build-classpath</id>
<phase>generate-sources</phase>
<goals>
<goal>build-classpath</goal>
</goals>
</execution>
</executions>
<configuration>
<outputFile>classpath.txt</outputFile>
</configuration>
</plugin>
<!-- Configure fat jar -->
<plugin>
<groupId>org.springframework.boot</groupId>

View File

@@ -9,7 +9,7 @@ import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.hover.IHoverEngine;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngine;
import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
@@ -21,6 +21,7 @@ import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
import org.springframework.ide.vscode.commons.yaml.completion.SchemaBasedYamlAssistContextProvider;
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider;
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngine;
import org.springframework.ide.vscode.commons.yaml.hover.YamlHoverInfoProvider;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
@@ -35,21 +36,25 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer {
private Yaml yaml = new Yaml();
private YamlSchema schema = new ManifestYmlSchema(NO_BUILDPACKS);
public ManifestYamlLanguageServer() {
SimpleTextDocumentService documents = getTextDocumentService();
YamlASTProvider parser = new YamlParser(yaml);
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);
YamlCompletionEngine yamlCompletionEngine = new YamlCompletionEngine(structureProvider, contextProvider);
VscodeCompletionEngine completionEngine = new VscodeCompletionEngineAdapter(this, yamlCompletionEngine);
IHoverEngine yamlHoverEngine = new ManifestYmlHoverEngine();
VscodeHoverEngine hoverEngine = new VscodeHoverEngineAdapter(this, yamlHoverEngine );
HoverInfoProvider infoProvider = new YamlHoverInfoProvider(parser, structureProvider, contextProvider);
VscodeHoverEngine hoverEngine = new VscodeHoverEngineAdapter(this, infoProvider);
IReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema);
// SimpleWorkspaceService workspace = getWorkspaceService();
documents.onDidChangeContent(params -> {
TextDocument doc = params.getDocument();
validateWith(doc, getReconcileEngine());
validateWith(doc, engine);
});
// workspace.onDidChangeConfiguraton(settings -> {
@@ -68,17 +73,13 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer {
documents.onHover(hoverEngine ::getHover);
}
protected IReconcileEngine getReconcileEngine() {
YamlASTProvider parser = new YamlParser(yaml);
IReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema);
return engine;
}
@Override
protected ServerCapabilities getServerCapabilities() {
ServerCapabilities c = new ServerCapabilities();
c.setTextDocumentSync(TextDocumentSyncKind.Full);
c.setHoverProvider(true);
CompletionOptions completionProvider = new CompletionOptions();
completionProvider.setResolveProvider(false);

View File

@@ -1,27 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.manifest.yaml;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.languageserver.hover.IHoverEngine;
import org.springframework.ide.vscode.commons.languageserver.util.IDocument;
public class ManifestYmlHoverEngine implements IHoverEngine {
@Override
public HoverInfo getHover(IDocument document, int offset) throws Exception {
// TODO. Create the hover info based on AST
HoverInfo info = new YamlHoverInfo();
return info;
}
}

View File

@@ -15,15 +15,15 @@ import java.util.Set;
import javax.inject.Provider;
import org.springframework.ide.vscode.commons.util.HtmlSnippet;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfo;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YTypedPropertyImpl;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
import org.springframework.ide.vscode.commons.yaml.util.DescriptionProviders;
import com.google.common.collect.ImmutableSet;
@@ -106,11 +106,11 @@ public class ManifestYmlSchema implements YamlSchema {
}
}
private Provider<HtmlSnippet> descriptionFor(String propName) {
return DescriptionProviders.fromClasspath(this.getClass(), "/description-by-prop-name/"+propName+".html");
private HoverInfo descriptionFor(String propName) {
return DescriptionProviders.fromClasspath(this.getClass(), "/description-by-prop-name/"+propName);
}
private Provider<HtmlSnippet> descriptionFor(YTypedPropertyImpl prop) {
private HoverInfo descriptionFor(YTypedPropertyImpl prop) {
return descriptionFor(prop.getName());
}

View File

@@ -0,0 +1,11 @@
If your application requires a custom buildpack, you can use the `buildpack` attribute to specify its URL or name:
```
---
...
buildpack: buildpack_URL
```
**Note**: The `cf buildpacks` command lists the buildpacks that you can refer to by name in a manifest or a command line option.
The command line option that overrides this attribute is `-b`.

View File

@@ -0,0 +1,30 @@
Some languages and frameworks require that you provide a custom command to start an application. Refer to the [buildpack](/buildpacks/) documentation to determine if you need to provide a custom start command.
You can provide the custom start command in your application manifest or on the command line.
To specify the custom start command in your application manifest, add it in the `command: START-COMMAND` format as the following example shows:
```
---
...
command: bundle exec rake VERBOSE=true
```
On the command line, use the `-c` option to specify the custom start command as the following example shows:
```
$ cf push my-app -c "bundle exec rake VERBOSE=true"
```
**Note**: The `-c` option with a value of null forces `cf push` to use the buildpack start command. See [About Starting Applications](./app-startup.html) for more information.
If you override the start command for a Buildpack application, Linux uses `bash -c YOUR-COMMAND` to invoke your application. If you override the start command for a Docker application, Linux uses `sh -c YOUR-COMMAND` to invoke your application. Because of this, if you override a start command, you should prefix `exec` to the final command in your custom composite start command.
`exec` causes the last command to become the root process of your application. The [Cloud Foundry Updates and Your Application](./prepare-to-deploy.html#moving-apps) section of the _Considerations for Designing and Running an Application in the Cloud_ topic explains why your application should handle a `termination signal` during Cloud Foundry updates. Without an `exec` statement, the parent process remains as the implied bash process, and does not propagate signals to your application process.
For example, both of the following composite start commands run database migrations when the first instance of the app starts, then start the app to serve requests, but they behave differently on graceful shutdown.
* `bin/rake cf:on_first_instance db:migrate && bin/rails server -p $PORT -e $RAILS_ENV`: The process tree is `bash -> ruby`, so on graceful shutdown only the `bash` process receives the TERM signal, and not the `ruby` process.
* `bin/rake cf:on_first_instance db:migrate && exec bin/rails server -p $PORT -e $RAILS_ENV`: Because of the `exec` prefix on the final command, the `ruby` process invoked by `rails` takes over the `bash` process managing the execution of the composite command. The process tree is only `ruby`, so the ruby web server receives the TERM signal can shutdown gracefully for 10 seconds.

View File

@@ -0,0 +1,9 @@
Use the `disk_quota` attribute to allocate the disk space for your app instance. This attribute requires a unit of measurement: `M`, `MB`, `G`, or `GB`, in upper case or lower case.
```
---
...
disk_quota: 1024M
```
The command line option that overrides this attribute is `-k`.

View File

@@ -0,0 +1,25 @@
Every `cf push` deploys applications to one particular Cloud Foundry instance. Every Cloud Foundry instance may have a shared domain set by an admin. Unless you specify a domain, Cloud Foundry incorporates that shared domain in the route to your application.
You can use the `domain` attribute when you want your application to be served from a domain other than the default shared domain.
```
---
...
domain: unique-example.com
```
The command line option that overrides this attribute is `-d`.
### The domains attribute
Use the `domains` attribute to provide multiple domains. If you define both `domain` and `domains` attributes, Cloud Foundry creates routes for domains defined in both of these fields.
```
---
...
domains:
- domain-example1.com
- domain-example2.org
```
The command line option that overrides this attribute is `-d`.

View File

@@ -0,0 +1,11 @@
Use the `domains` attribute to provide multiple domains. If you define both `domain` and `domains` attributes, Cloud Foundry creates routes for domains defined in both of these fields.
```
---
...
domains:
- domain-example1.com
- domain-example2.org
```
The command line option that overrides this attribute is `-d`.

View File

@@ -0,0 +1,24 @@
The `env` block consists of a heading, then one or more environment variable/value pairs.
For example:
```
---
...
env:
RAILS_ENV: production
RACK_ENV: production
```
`cf push` deploys the application to a container on the server. The variables belong to the container environment.
While the application is running, Cloud Foundry allows you to operate on environment variables.
* View all variables: `cf env my-app`
* Set an individual variable: `cf set-env my-app my-variable_name my-variable_value`
* Unset an individual variable: `cf unset-env my-app my-variable_name my-variable_value`
Environment variables interact with manifests in the following ways:
* When you deploy an application for the first time, Cloud Foundry reads the variables described in the environment block of the manifest, and adds them to the environment of the container where the application is deployed.
* When you stop and then restart an application, its environment variables persist.

View File

@@ -0,0 +1,9 @@
Use the `host` attribute to provide a hostname, or subdomain, in the form of a string. This segment of a route helps to ensure that the route is unique. If you do not provide a hostname, the URL for the app takes the form of `APP-NAME.DOMAIN`.
```
---
...
host: my-app
```
The command line option that overrides this attribute is `-n`.

View File

@@ -0,0 +1,11 @@
Use the `hosts` attribute to provide multiple hostnames, or subdomains. Each hostname generates a unique route for the app. `hosts` can be used in conjunction with `host`. If you define both attributes, Cloud Foundry creates routes for hostnames defined in both `host` and `hosts`.
```
---
...
hosts:
- app_host1
- app_host2
```
The command line option that overrides this attribute is `-n`.

View File

@@ -0,0 +1,63 @@
A single manifest can describe multiple applications. Another powerful technique is to create multiple manifests with inheritance. Here, manifests have parent-child relationships such that children inherit descriptions from a parent. Children can use inherited descriptions as-is, extend them, or override them.
Content in the child manifest overrides content in the parent manifest, if the two conflict.
This technique helps in these and other scenarios:
* An application has a set of different deployment modes, such as debug, local, and public. Each deployment mode is described in child manifests that extend the settings in a base parent manifest.
* An application is packaged with a basic configuration described by a parent manifest. Users can extend the basic configuration by creating child manifests that add new properties or override those in the parent manifest.
The benefits of multiple manifests with inheritance are similar to those of minimizing duplicated content within single manifests. With inheritance, though, we “promote” content by placing it in the parent manifest.
Every child manifest must contain an “inherit” line that points to the parent manifest. Place the inherit line immediately after the three dashes at the top of the child manifest. For example, every child of a parent manifest called `base-manifest.yml` begins like this:
```
---
...
inherit: base-manifest.yml
```
You do not need to add anything to the parent manifest.
In the simple example below, a parent manifest gives each application minimal resources, while a production child manifest scales them up.
**simple-base-manifest.yml**
```
---
path: .
domain: shared-domain.com
memory: 256M
instances: 1
services:
- singular-backend
# app-specific configuration
applications:
- name: springtock
host: 765shower
path: ./april/build/libs/april-weather.war
- name: wintertick
host: 321flurry
path: ./december/target/december-weather.war
```
**simple-prod-manifest.yml**
```
---
inherit: simple-base-manifest.yml
applications:
- name:springstorm
memory: 512M
instances: 1
host: 765deluge
path: ./april/build/libs/april-weather.war
- name: winterblast
memory: 1G
instances: 2
host: 321blizzard
path: ./december/target/december-weather.war
```
**Note**: Inheritance can add an additional level of complexity to manifest creation and maintenance. Comments that precisely explain how the child manifest extends or overrides the descriptions in the parent manifest can alleviate this complexity.

View File

@@ -0,0 +1,11 @@
Use the `instances` attribute to specify the number of app instances that you want to start upon push:
```
---
...
instances: 2
```
We recommend that you run at least two instances of any apps for which fault tolerance matters.
The command line option that overrides this attribute is `-i`.

View File

@@ -0,0 +1,11 @@
Use the `memory` attribute to specify the memory limit for all instances of an app. This attribute requires a unit of measurement: `M`, `MB`, `G`, or `GB`, in upper case or lower case. For example:
```
---
...
memory: 1024M
```
The default memory limit is 1G. You might want to specify a smaller limit to conserve quota space if you know that your app instances do not require 1G of memory.
The command line option that overrides this attribute is `-m`.

View File

@@ -0,0 +1,9 @@
The `name` attribute is the only required attribute for an application in a manifest file.
This is an example of a minimal manifest:
```
---
applications:
- name: nifty-gui
```

View File

@@ -0,0 +1,9 @@
By default, if you do not provide a hostname, the URL for the app takes the form of `APP-NAME.DOMAIN`. If you want to override this and map the root domain to this app then you can set no-hostname as true.
```
---
...
no-hostname: true
```
The command line option that corresponds to this attribute is `--no-hostname`.

View File

@@ -0,0 +1,16 @@
By default, `cf push` assigns a route to every application. But some applications process data while running in the background, and should not be assigned routes.
You can use the `no-route` attribute with a value of `true` to prevent a route from being created for your application.
```
---
...
no-route: true
```
The command line option that corresponds to this attribute is `--no-route`.
If you find that an application which should not have a route does have one:
1. Remove the route using the `cf unmap-route` command.
2. Push the app again with the `no-route: true` attribute in the manifest or the `--no-route` command line option.

View File

@@ -0,0 +1,9 @@
You can use the `path` attribute to tell Cloud Foundry where to find your application. This is generally not necessary when you run `cf push` from the directory where an application is located.
```
---
...
path: path_to_application_bits
```
The command line option that overrides this attribute is `-p`.

View File

@@ -0,0 +1,9 @@
Use the `random-route` attribute to create a URL that includes the app name and random words. Use this attribute to avoid URL collision when pushing the same app to multiple spaces, or to avoid managing app URLs.
The command line option that corresponds to this attribute is `--random-route`.
```
---
...
random-route: true
```

View File

@@ -0,0 +1,17 @@
Applications can bind to services such as databases, messaging, and key-value stores.
Applications are deployed into App Spaces. An application can only bind to services instances that exist in the target App Space before the application is deployed.
The `services` block consists of a heading, then one or more service instance names.
Whoever creates the service chooses the service instance names. These names can convey logical information, as in `backend_queue`, describe the nature of the service, as in `mysql_5.x`, or do neither, as in the example below.
```
---
...
services:
- instance_ABC
- instance_XYZ
```
Binding to a service instance is a special case of setting an environment variable, namely `VCAP_SERVICES`.

View File

@@ -0,0 +1,11 @@
Use the `stack` attribute to specify which stack to deploy your application to.
To see a list of available stacks, run `cf stacks` from the cf cli.
```
---
...
stack: cflinuxfs2
```
The command line option that overrides this attribute is `-s`.

View File

@@ -0,0 +1,15 @@
The `timeout` attribute defines the number of seconds Cloud Foundry allocates for starting your application.
For example:
```
---
...
timeout: 80
```
You can increase the timeout length for very large apps that require more time to start. The default timeout is 60 seconds with an upper bound of 180 seconds.
**Note**: Administrators can set the upper bound of the `maximum_health_check_timeout` property to any value. Any changes to Cloud Controller properties in the deployment manifest require running `bosh deploy`.
The command line option that overrides the timeout attribute for the shell is `-t`. Manifest values still apply to applications pushed to the deployment.

View File

@@ -11,12 +11,9 @@
package org.springframework.ide.vscode.manifest.yaml;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
import org.springframework.ide.vscode.manifest.yaml.ManifestYamlLanguageServer;
public class ManifestYamlEditorTest {
@@ -365,24 +362,79 @@ public class ManifestYamlEditorTest {
);
}
@Test @Ignore
@Test
public void hoverInfos() throws Exception {
Editor editor = harness.newEditor(
"memory: 1G\n" +
"inherit: base-manifest.yml\n"+
"applications:\n" +
"- buildpack: zbuildpack\n" +
" domain: zdomain\n" +
" name: foo"
" name: foo\n" +
" command: java main.java\n" +
" disk_quota: 1024M\n" +
" domains:\n" +
" - pivotal.io\n" +
" - otherdomain.org\n" +
" env:\n" +
" RAILS_ENV: production\n" +
" RACK_ENV: production\n" +
" host: apppage\n" +
" hosts:\n" +
" - apppage2\n" +
" - appage3\n" +
" instances: 2\n" +
" no-hostname: true\n" +
" no-route: true\n" +
" path: somepath/app.jar\n" +
" random-route: true\n" +
" services:\n" +
" - instance_ABC\n" +
" - instance_XYZ\n" +
" stack: cflinuxfs2\n" +
" timeout: 80\n"
);
editor.assertIsHoverRegion("memory");
editor.assertIsHoverRegion("inherit");
editor.assertIsHoverRegion("applications");
editor.assertIsHoverRegion("buildpack");
editor.assertIsHoverRegion("domain");
editor.assertIsHoverRegion("name");
editor.assertIsHoverRegion("command");
editor.assertIsHoverRegion("disk_quota");
editor.assertIsHoverRegion("domains");
editor.assertIsHoverRegion("env");
editor.assertIsHoverRegion("host");
editor.assertIsHoverRegion("hosts");
editor.assertIsHoverRegion("instances");
editor.assertIsHoverRegion("no-hostname");
editor.assertIsHoverRegion("no-route");
editor.assertIsHoverRegion("path");
editor.assertIsHoverRegion("random-route");
editor.assertIsHoverRegion("services");
editor.assertIsHoverRegion("stack");
editor.assertIsHoverRegion("timeout");
editor.assertHoverContains("memory", "Use the <code>memory</code> attribute to specify the memory limit");
editor.assertHoverContains("1G", "Use the <code>memory</code> attribute to specify the memory limit");
editor.assertHoverContains("buildpack", "use the <code>buildpack</code> attribute to specify its URL or name");
editor.assertHoverContains("memory", "Use the `memory` attribute to specify the memory limit");
editor.assertHoverContains("1G", "Use the `memory` attribute to specify the memory limit");
editor.assertHoverContains("inherit", "For example, every child of a parent manifest called `base-manifest.yml` begins like this");
editor.assertHoverContains("buildpack", "use the `buildpack` attribute to specify its URL or name");
editor.assertHoverContains("name", "The `name` attribute is the only required attribute for an application in a manifest file");
editor.assertHoverContains("command", "On the command line, use the `-c` option to specify the custom start command as the following example shows");
editor.assertHoverContains("disk_quota", "Use the `disk_quota` attribute to allocate the disk space for your app instance");
editor.assertHoverContains("domain", "You can use the `domain` attribute when you want your application to be served");
editor.assertHoverContains("domains", "Use the `domains` attribute to provide multiple domains");
editor.assertHoverContains("env", "The `env` block consists of a heading, then one or more environment variable/value pairs");
editor.assertHoverContains("host", "Use the `host` attribute to provide a hostname, or subdomain, in the form of a string");
editor.assertHoverContains("hosts", "Use the `hosts` attribute to provide multiple hostnames, or subdomains");
editor.assertHoverContains("instances", "Use the `instances` attribute to specify the number of app instances that you want to start upon push");
editor.assertHoverContains("no-hostname", "By default, if you do not provide a hostname, the URL for the app takes the form of `APP-NAME.DOMAIN`");
editor.assertHoverContains("no-route", "You can use the `no-route` attribute with a value of `true` to prevent a route from being created for your application");
editor.assertHoverContains("path", "You can use the `path` attribute to tell Cloud Foundry where to find your application");
editor.assertHoverContains("random-route", "Use the `random-route` attribute to create a URL that includes the app name and random words");
editor.assertHoverContains("services", "The `services` block consists of a heading, then one or more service instance names");
editor.assertHoverContains("stack", "Use the `stack` attribute to specify which stack to deploy your application to.");
editor.assertHoverContains("timeout", "The `timeout` attribute defines the number of seconds Cloud Foundry allocates for starting your application");
}
//////////////////////////////////////////////////////////////////////////////

View File

@@ -112,11 +112,20 @@ public class ManifestYmlSchemaTest {
//////////////////////////////////////////////////////////////////////////////
private void assertHasRealDescription(YTypedProperty p) {
String noDescriptionText = DescriptionProviders.NO_DESCRIPTION.get().toHtml();
String actual = p.getDescription().toHtml();
String msg = "Description missing for '"+p.getName()+"'";
assertTrue(msg, StringUtil.hasText(actual));
assertFalse(msg, noDescriptionText.equals(actual));
{
String noDescriptionText = DescriptionProviders.NO_DESCRIPTION.toHtml();
String actual = p.getDescription().toHtml();
String msg = "Description missing for '"+p.getName()+"'";
assertTrue(msg, StringUtil.hasText(actual));
assertFalse(msg, noDescriptionText.equals(actual));
}
{
String noDescriptionText = DescriptionProviders.NO_DESCRIPTION.toMarkdown();
String actual = p.getDescription().toMarkdown();
String msg = "Description missing for '"+p.getName()+"'";
assertTrue(msg, StringUtil.hasText(actual));
assertFalse(msg, noDescriptionText.equals(actual));
}
}
private List<YTypedProperty> getNestedProps() {

View File

@@ -1,5 +1,31 @@
inherit: base-manifest.yml
applications:
- name: foo
foo: bar
builpack: java
buildpack: java_buildpack
memory: 1G
command: java main.java
disk_quota: 1024M
domain: pivotal.io
domains:
- pivotal.io
- other.org
env:
RAILS_ENV: production
RACK_ENV: production
no-hostname: true
no-route: false
random-route: true
services:
- dbserv1
- redisserv2
path: app.jar
instances: 3
stack: cflinuxfs2
timeout: 80
host: apppage
hosts:
- apppagealt
- anotherhost

View File

@@ -1,25 +0,0 @@
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//
// The module 'assert' provides assertion methods from node
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
import * as myExtension from '../lib/Main';
// Useful link:
// http://ricostacruz.com/cheatsheets/mocha-tdd.html
// Defines a Mocha test suite to group tests of similar kind together
suite("Extension Tests", () => {
// Defines a Mocha unit test
test("My Extension gets activated", () => {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});