CloudConfig 'manual network' schema
This commit is contained in:
@@ -15,7 +15,9 @@ import java.util.Collection;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.ide.vscode.bosh.models.BoshModels;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
import org.springframework.ide.vscode.commons.util.Lazy;
|
||||
import org.springframework.ide.vscode.commons.util.PartialCollection;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParsers;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
|
||||
@@ -24,40 +26,63 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType;
|
||||
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.YTypeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
|
||||
public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema {
|
||||
|
||||
private final YBeanType toplevelType;
|
||||
private final YType t_az_ref;
|
||||
private final YType t_az_def;
|
||||
private final YType t_vm_type_ref;
|
||||
private final YType t_vm_type_def;
|
||||
private final YType t_network_ref;
|
||||
private AbstractType t_network_def;
|
||||
private Collection<YType> definitionTypes;
|
||||
private Collection<Pair<YType, YType>> defAndRefTypes;
|
||||
private final Lazy<Collection<Pair<YType, YType>>> defAndRefTypes = new Lazy<>();
|
||||
private AbstractType t_any;
|
||||
private AbstractType t_ne_string;
|
||||
private YAtomicType t_boolean;
|
||||
private AbstractType t_pos_integer;
|
||||
private YType t_params;
|
||||
private YType t_ip_address;
|
||||
private YType t_ip_range;
|
||||
private YType t_ip_address_or_range;
|
||||
|
||||
public BoshCloudConfigSchema(YTypeFactory f, BoshModels models) {
|
||||
super(f);
|
||||
|
||||
AbstractType t_any = f.yany("Object");
|
||||
AbstractType t_ne_string = f.yatomic("String")
|
||||
t_any = f.yany("Object");
|
||||
t_ne_string = f.yatomic("String")
|
||||
.parseWith(ValueParsers.NE_STRING);
|
||||
YAtomicType t_boolean = f.yenum("boolean", "true", "false");
|
||||
AbstractType t_pos_integer = f.yatomic("Positive Integer")
|
||||
t_boolean = f.yenum("boolean", "true", "false");
|
||||
t_pos_integer = f.yatomic("Positive Integer")
|
||||
.parseWith(ValueParsers.POS_INTEGER);
|
||||
YType t_params = f.ymap(t_ne_string, t_any);
|
||||
t_params = f.ymap(t_ne_string, t_any);
|
||||
|
||||
t_az_ref = t_ne_string;
|
||||
t_az_def = f.yatomic("AZName").parseWith(ValueParsers.NE_STRING);
|
||||
t_az_ref = f.yenumFromDynamicValues("AZName", (dc) -> PartialCollection.compute(() ->
|
||||
models.astTypes.getDefinedNames(dc, t_az_def)
|
||||
));
|
||||
t_vm_type_def = f.yatomic("VMTypeName").parseWith(ValueParsers.NE_STRING);
|
||||
t_vm_type_ref = f.yenumFromDynamicValues("VMTypeName", (dc) -> PartialCollection.compute(() ->
|
||||
models.astTypes.getDefinedNames(dc, t_vm_type_def)
|
||||
));
|
||||
t_network_def = f.yatomic("NetworkName").parseWith(ValueParsers.NE_STRING);
|
||||
t_network_ref = t_ne_string;
|
||||
t_ip_address = t_ne_string; //TODO? Syntax for ip addresses like 192.168.1.22 ?
|
||||
t_ip_range = t_ne_string; //TODO? Syntax for ip ranges like 10.10.0.0/22 ?
|
||||
t_ip_address_or_range = t_ne_string; //TODO?
|
||||
|
||||
YType t_network = createNetworkBlockSchema(models);
|
||||
|
||||
YBeanType t_az = f.ybean("AZ");
|
||||
addProp(t_az, "name", t_az_def).isRequired(true);
|
||||
addProp(t_az, "cloud_properties", t_params);
|
||||
|
||||
YBeanType t_compilation = f.ybean("Compilation");
|
||||
addProp(t_compilation, "workers", t_pos_integer).isRequired(true);
|
||||
@@ -71,8 +96,8 @@ public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema {
|
||||
addProp(t_vm_type, "cloud_properties", t_params);
|
||||
|
||||
this.toplevelType = f.ybean("CloudConfig");
|
||||
addProp(toplevelType, "azs", t_any).isRequired(true);
|
||||
addProp(toplevelType, "networks", t_any).isRequired(true);
|
||||
addProp(toplevelType, "azs", f.yseq(t_az)).isRequired(true);
|
||||
addProp(toplevelType, "networks", f.yseq(t_network)).isRequired(true);
|
||||
addProp(toplevelType, "vm_types", f.yseq(t_vm_type)).isRequired(true);
|
||||
addProp(toplevelType, "vm_extensions", t_any);
|
||||
addProp(toplevelType, "disk_types", t_any).isRequired(true);
|
||||
@@ -85,18 +110,73 @@ public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema {
|
||||
|
||||
}
|
||||
|
||||
private YType createNetworkBlockSchema(BoshModels models) {
|
||||
AbstractType t_manual_nw = f.ybean("ManualNetwork");
|
||||
{
|
||||
YBeanType t_subnet = f.ybean("Subnet[Manual]");
|
||||
addProp(t_subnet, "range", t_ip_range).isRequired(true);
|
||||
addProp(t_subnet, "gateway", t_ip_address).isRequired(true);
|
||||
addProp(t_subnet, "dns", f.yseq(t_ip_address));
|
||||
addProp(t_subnet, "reserved", f.yseq(t_ip_address_or_range));
|
||||
addProp(t_subnet, "static", f.yseq(t_ip_address_or_range));
|
||||
addProp(t_subnet, "az", t_az_ref);
|
||||
addProp(t_subnet, "azs", f.yseq(t_az_ref));
|
||||
addProp(t_subnet, "cloud_properties", t_params);
|
||||
|
||||
addProp(t_manual_nw, "subnets", f.yseq(t_subnet)).isRequired(true);
|
||||
}
|
||||
|
||||
AbstractType t_vip_nw = f.ybean("VipNetwork");
|
||||
addProp(t_vip_nw, "cloud_properties", t_params);
|
||||
|
||||
AbstractType t_dynamic_nw = f.ybean("DynamicNetwork");
|
||||
addProp(t_dynamic_nw, "dns", f.yseq(t_ip_address));
|
||||
addProp(t_dynamic_nw, "cloud_properties", t_params);
|
||||
|
||||
t_dynamic_nw.require(Constraints.mutuallyExclusive("dns", "subnets"));
|
||||
t_dynamic_nw.require(Constraints.mutuallyExclusive("cloud_properties", "subnets"));
|
||||
|
||||
AbstractType t_network = f.contextAware("Network", dc -> {
|
||||
String type = models.getTypeTag(dc);
|
||||
if (StringUtil.hasText(type)) {
|
||||
switch (type) {
|
||||
case "manual":
|
||||
return t_manual_nw;
|
||||
case "dynamic":
|
||||
return t_dynamic_nw;
|
||||
case "vip":
|
||||
return t_vip_nw;
|
||||
default:
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.treatAsBean();
|
||||
|
||||
//Add shared properties to the 'super' type.
|
||||
addProp(t_network, "name", t_network_def).isPrimary(true);
|
||||
addProp(t_network, "type", f.yenum("NetworkType", "manual", "dynamic", "vip")).isRequired(true);
|
||||
|
||||
//Add shared properties to all 'sub' types.
|
||||
for (AbstractType subtype : ImmutableList.of(t_dynamic_nw, t_manual_nw, t_vip_nw)) {
|
||||
for (YTypedProperty sharedProp : t_network.getProperties()) {
|
||||
subtype.addProperty(sharedProp);
|
||||
}
|
||||
}
|
||||
return t_network;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Pairs of types. Each pair contains a 'def' type and a 'ref' type. Nodes with the ref-type
|
||||
* shall be interpreted as reference to a corresponding node with the 'def' type if the def and ref node
|
||||
* contain the same scalar value.
|
||||
*/
|
||||
public Collection<Pair<YType, YType>> getDefAndRefTypes() {
|
||||
if (defAndRefTypes==null) {
|
||||
defAndRefTypes = ImmutableList.of(
|
||||
Pair.of(t_vm_type_def, t_vm_type_ref)
|
||||
);
|
||||
}
|
||||
return defAndRefTypes;
|
||||
return defAndRefTypes.load(() -> ImmutableList.of(
|
||||
Pair.of(t_vm_type_def, t_vm_type_ref),
|
||||
Pair.of(t_network_def, t_network_ref),
|
||||
Pair.of(t_az_def, t_az_ref)
|
||||
));
|
||||
}
|
||||
|
||||
public Collection<YType> getDefinitionTypes() {
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.bosh.models;
|
||||
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
|
||||
public class BoshModels {
|
||||
public final YamlAstCache asts = new YamlAstCache();
|
||||
@@ -28,4 +31,12 @@ public class BoshModels {
|
||||
this.releasesProvider = releasesProvider;
|
||||
}
|
||||
|
||||
public String getTypeTag(DynamicSchemaContext dc) {
|
||||
YamlFileAST ast = asts.getSafeAst(dc.getDocument(), true);
|
||||
if (ast!=null) {
|
||||
return NodeUtil.asScalar(dc.getPath().thenValAt("type").traverseToNode(ast));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. Lists subnets in this network.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. Name used to reference this network configuration.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. The type of configuration. Should be one of `manual`, `dynamic` or `vip`.
|
||||
@@ -0,0 +1 @@
|
||||
AZ associated with this subnet (should only be used when using [first class AZs](https://bosh.io/docs/azs.html)). Example: `z1`. Available in v241+.
|
||||
@@ -0,0 +1 @@
|
||||
List of AZs associated with this subnet (should only be used when using [first class AZs](https://bosh.io/docs/azs.html)). Example: `[z1, z2]`. Available in v241+.
|
||||
@@ -0,0 +1,11 @@
|
||||
Describes any IaaS-specific properties for the subnet. Default is {} (empty Hash).
|
||||
|
||||
See:
|
||||
|
||||
- [AWS CPI network cloud properties](https://bosh.io/docs/aws-cpi.html#networks)
|
||||
- [Azure CPI network cloud properties](https://bosh.io/docs/azure-cpi.html#networks)
|
||||
- [OpenStack CPI network cloud properties](https://bosh.io/docs/openstack-cpi.html#networks)
|
||||
- [Softlayer CPI network cloud properties](https://bosh.io/docs/softlayer-cpi.html#networks)
|
||||
- [Google Cloud Platform CPI network cloud properties](https://bosh.io/docs/google-cpi.html#networks)
|
||||
- [vSphere CPI network cloud properties](https://bosh.io/docs/vsphere-cpi.html#networks)
|
||||
- [vCloud CPI network cloud properties](https://bosh.io/docs/vcloud-cpi.html#networks)
|
||||
@@ -0,0 +1 @@
|
||||
DNS IP addresses for this subnet.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. Subnet gateway IP.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. Subnet IP range that includes all IPs from this subnet.
|
||||
@@ -0,0 +1 @@
|
||||
Array of reserved IPs and/or IP ranges. BOSH does not assign IPs from this range to any VM.
|
||||
@@ -0,0 +1 @@
|
||||
Array of static IPs and/or IP ranges. BOSH assigns IPs from this range to jobs requesting static IPs. Only IPs specified here can be used for static IP reservations.
|
||||
@@ -11,6 +11,7 @@
|
||||
package org.springframework.ide.vscode.bosh;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -1915,13 +1916,8 @@ public class BoshEditorTest {
|
||||
"compilation:\n" +
|
||||
" <*>"
|
||||
);
|
||||
editor.assertContextualCompletions(PLAIN_COMPLETION.or(SNIPPET_COMPLETION),
|
||||
editor.assertContextualCompletions(PLAIN_COMPLETION.and(SNIPPET_COMPLETION.negate()),
|
||||
"<*>"
|
||||
, // ==>
|
||||
"workers: $1\n" +
|
||||
" az: $2\n" +
|
||||
" vm_type: $3\n" +
|
||||
" network: $4<*>"
|
||||
,
|
||||
"az: <*>"
|
||||
,
|
||||
@@ -1933,6 +1929,14 @@ public class BoshEditorTest {
|
||||
,
|
||||
"workers: <*>"
|
||||
);
|
||||
editor.assertContextualCompletions(PLAIN_COMPLETION.and(SNIPPET_COMPLETION),
|
||||
"<*>"
|
||||
, // ==>
|
||||
"workers: $1\n" +
|
||||
" az: $2\n" +
|
||||
" vm_type: $3\n" +
|
||||
" network: $4<*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"compilation:\n" +
|
||||
@@ -1946,6 +1950,7 @@ public class BoshEditorTest {
|
||||
editor.assertProblems(
|
||||
"not-int|NumberFormatException",
|
||||
"not-bool|unknown 'boolean'",
|
||||
"z1|unknown 'AZName'",
|
||||
"default|unknown 'VMTypeName'"
|
||||
);
|
||||
editor.assertHoverContains("workers", "The maximum number of compilation VMs");
|
||||
@@ -2037,7 +2042,9 @@ public class BoshEditorTest {
|
||||
" network: private"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(/*NONE*/);
|
||||
editor.assertProblems(
|
||||
"z1|unknown 'AZName'"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"vm_types:\n" +
|
||||
@@ -2057,7 +2064,167 @@ public class BoshEditorTest {
|
||||
" network: private"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems("woot-vm|unknown 'VMTypeName'");
|
||||
editor.assertProblems(
|
||||
"z1|unknown 'AZName'",
|
||||
"woot-vm|unknown 'VMTypeName'"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void cloudconfig_network_ca() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"networks:\n" +
|
||||
"- <*>"
|
||||
);
|
||||
editor.assertCompletions(SNIPPET_COMPLETION.negate(),
|
||||
"networks:\n" +
|
||||
"- name: <*>"
|
||||
);
|
||||
editor.assertCompletions(SNIPPET_COMPLETION,
|
||||
"networks:\n" +
|
||||
"- name: $1\n" +
|
||||
" type: $2<*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"networks:\n" +
|
||||
"- name: foo\n" +
|
||||
" <*>"
|
||||
);
|
||||
editor.assertCompletions(PLAIN_COMPLETION,
|
||||
"networks:\n" +
|
||||
"- name: foo\n" +
|
||||
" type: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"networks:\n" +
|
||||
"- name: foo\n" +
|
||||
" type: <*>"
|
||||
);
|
||||
editor.assertContextualCompletions(PLAIN_COMPLETION,
|
||||
"<*>"
|
||||
, // =>
|
||||
"dynamic<*>",
|
||||
"manual<*>",
|
||||
"vip<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void cloudconfig_manual_network_ca() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"networks:\n" +
|
||||
"- name: foo\n" +
|
||||
" type: manual\n" +
|
||||
" <*>"
|
||||
);
|
||||
editor.assertContextualCompletions(PLAIN_COMPLETION,
|
||||
"<*>"
|
||||
, // =>
|
||||
"subnets:\n" + //non-snippet
|
||||
" - <*>"
|
||||
,
|
||||
"subnets:\n" + //snippet
|
||||
" - range: $1\n" +
|
||||
" gateway: $2<*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"networks:\n" +
|
||||
"- name: foo\n" +
|
||||
" type: manual\n" +
|
||||
" subnets:\n" +
|
||||
" - <*>"
|
||||
);
|
||||
editor.assertContextualCompletions(PLAIN_COMPLETION.and(SNIPPET_COMPLETION.negate()),
|
||||
"<*>"
|
||||
, // ==>
|
||||
"az: <*>"
|
||||
,
|
||||
"azs:\n" +
|
||||
" - <*>"
|
||||
,
|
||||
"cloud_properties:\n" +
|
||||
" <*>"
|
||||
,
|
||||
"dns:\n" +
|
||||
" - <*>"
|
||||
,
|
||||
"gateway: <*>"
|
||||
,
|
||||
"range: <*>"
|
||||
,
|
||||
"reserved:\n" +
|
||||
" - <*>"
|
||||
,
|
||||
"static:\n" +
|
||||
" - <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"azs:\n" +
|
||||
"- name: zone-1\n" +
|
||||
"- name: zone-2\n" +
|
||||
"- name: zone-3\n" +
|
||||
"networks:\n" +
|
||||
"- name: foo\n" +
|
||||
" type: manual\n" +
|
||||
" subnets:\n" +
|
||||
" - az: <*>"
|
||||
);
|
||||
editor.assertContextualCompletions("<*>", "zone-1<*>", "zone-2<*>", "zone-3<*>");
|
||||
editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"azs:\n" +
|
||||
"- name: zone-1\n" +
|
||||
"- name: zone-2\n" +
|
||||
"- name: zone-3\n" +
|
||||
"networks:\n" +
|
||||
"- name: foo\n" +
|
||||
" type: manual\n" +
|
||||
" subnets:\n" +
|
||||
" - azs:\n" +
|
||||
" - <*>"
|
||||
);
|
||||
editor.assertContextualCompletions("<*>", "zone-1<*>", "zone-2<*>", "zone-3<*>");
|
||||
|
||||
}
|
||||
|
||||
@Test public void cloudconfig_manual_network_hovers() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG,
|
||||
"networks:\n" +
|
||||
"- name: my-network\n" +
|
||||
" type: manual\n" +
|
||||
"\n" +
|
||||
" subnets:\n" +
|
||||
" - range: 10.10.0.0/24\n" +
|
||||
" gateway: 10.10.0.1\n" +
|
||||
" dns: [10.10.0.2]\n" +
|
||||
"\n" +
|
||||
" # IPs that will not be used for anything\n" +
|
||||
" reserved: [10.10.0.2-10.10.0.10]\n" +
|
||||
" az: z1\n" +
|
||||
"\n" +
|
||||
" cloud_properties: {subnet: subnet-9be6c3f7}\n" +
|
||||
"\n" +
|
||||
" - range: 10.10.1.0/24\n" +
|
||||
" gateway: 10.10.1.1\n" +
|
||||
" dns: [10.10.1.2]\n" +
|
||||
"\n" +
|
||||
" static: [10.10.1.11-10.10.1.20]\n" +
|
||||
"\n" +
|
||||
" azs: [z2, z3]\n" +
|
||||
" cloud_properties: {subnet: subnet-9be6c6gh}"
|
||||
);
|
||||
editor.assertHoverContains("name", "Name used to reference this network configuration");
|
||||
editor.assertHoverContains("type", "The type of configuration. Should be one of `manual`, `dynamic` or `vip`");
|
||||
editor.assertHoverContains("subnets", "Lists subnets in this network");
|
||||
editor.assertHoverContains("range", "Subnet IP range that includes all IPs from this subnet");
|
||||
editor.assertHoverContains("gateway", "Subnet gateway IP");
|
||||
editor.assertHoverContains("dns", "DNS IP addresses for this subnet");
|
||||
editor.assertHoverContains("reserved", "Array of reserved IPs and/or IP ranges. BOSH does not assign IPs from this range to any VM");
|
||||
editor.assertHoverContains("static", "Array of static IPs and/or IP ranges.");
|
||||
editor.assertHoverContains("az", "AZ associated with this subnet");
|
||||
editor.assertHoverContains("azs", "List of AZs associated with this subnet");
|
||||
editor.assertHoverContains("cloud_properties", "Describes any IaaS-specific properties for the subnet");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,10 +61,10 @@ public class Editor {
|
||||
|| c.getLabel().startsWith(Unicodes.LEFT_ARROW+" ")
|
||||
|| c.getLabel().startsWith(Unicodes.RIGHT_ARROW+" ")
|
||||
;
|
||||
public static final Predicate<CompletionItem> PLAIN_COMPLETION = c -> !RELAXED_COMPLETION.test(c);
|
||||
public static final Predicate<CompletionItem> SNIPPET_COMPLETION = c -> c.getLabel().endsWith("Snippet");
|
||||
public static final Predicate<CompletionItem> PLAIN_COMPLETION = RELAXED_COMPLETION.negate();
|
||||
public static final Predicate<CompletionItem> DEDENTED_COMPLETION = c -> c.getLabel().startsWith(Unicodes.LEFT_ARROW+" ");
|
||||
public static final Predicate<CompletionItem> INDENTED_COMPLETION = c -> c.getLabel().startsWith(Unicodes.RIGHT_ARROW+" ");
|
||||
public static final Predicate<CompletionItem> SNIPPET_COMPLETION = c -> c.getLabel().endsWith("Snippet");
|
||||
|
||||
static class EditorState {
|
||||
String documentContents;
|
||||
|
||||
Reference in New Issue
Block a user