Dynamic CA for github repo uris in concourse resource definitions
This commit is contained in:
@@ -37,7 +37,7 @@ public abstract class ScoreableProposal implements ICompletionProposal {
|
||||
if (p1 instanceof ScoreableProposal && p2 instanceof ScoreableProposal) {
|
||||
double s1 = ((ScoreableProposal)p1).getScore();
|
||||
double s2 = ((ScoreableProposal)p2).getScore();
|
||||
if (Math.abs(s1-s2)<1E-5) {
|
||||
if (s1==s2) {
|
||||
String name1 = ((ScoreableProposal)p1).getLabel();
|
||||
String name2 = ((ScoreableProposal)p2).getLabel();
|
||||
return name1.compareTo(name2);
|
||||
@@ -45,7 +45,13 @@ public abstract class ScoreableProposal implements ICompletionProposal {
|
||||
return Double.compare(s2, s1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
if (p1 instanceof ScoreableProposal) {
|
||||
return -1;
|
||||
}
|
||||
if (p2 instanceof ScoreableProposal) {
|
||||
return +1;
|
||||
}
|
||||
return p1.getLabel().compareTo(p2.getLabel());
|
||||
}
|
||||
};
|
||||
public abstract double getBaseScore();
|
||||
|
||||
@@ -8,11 +8,10 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
package org.springframework.ide.vscode.commons.languageserver.completion;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
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.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
@@ -20,13 +19,13 @@ public class SimpleCompletionFactory {
|
||||
|
||||
|
||||
public static class SimpleProposal implements ICompletionProposal{
|
||||
|
||||
|
||||
private DocumentEdits edits;
|
||||
private CompletionItemKind kind;
|
||||
private Renderable info;
|
||||
private String detail;
|
||||
private String label;
|
||||
|
||||
|
||||
public SimpleProposal(DocumentEdits edits, CompletionItemKind kind, Renderable info,
|
||||
String detail, String label) {
|
||||
this.edits = edits;
|
||||
@@ -66,9 +65,17 @@ public class SimpleCompletionFactory {
|
||||
return detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SimpleProposal("+label+")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static SimpleProposal simpleProposal(DocumentRegion query, CompletionItemKind kind, String value, String detail, Renderable info) {
|
||||
return simpleProposal(query.getDocument(), query.getEnd(), query.toString(), kind, value, detail, info);
|
||||
}
|
||||
|
||||
public static SimpleProposal simpleProposal(IDocument doc, int offset, String query, CompletionItemKind kind, String value, String detail, Renderable info) {
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(offset-query.length(), offset, value);
|
||||
@@ -181,6 +181,10 @@ public class DocumentRegion implements CharSequence, IRegion {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int indexOf(char c) {
|
||||
return indexOf(c, 0);
|
||||
}
|
||||
|
||||
public DocumentRegion[] split(char c) {
|
||||
List<DocumentRegion> pieces = new ArrayList<>();
|
||||
int start = 0;
|
||||
@@ -338,5 +342,4 @@ public class DocumentRegion implements CharSequence, IRegion {
|
||||
return getStart();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -24,7 +24,7 @@ public class FuzzyMatcher {
|
||||
* 'score' when it does. The higher the score, the better the match is considered to
|
||||
* be.
|
||||
*/
|
||||
public static double matchScore(String pattern, String data) {
|
||||
public static double matchScore(CharSequence pattern, String data) {
|
||||
int ppos = 0; //pos of next char in pattern to look for
|
||||
int dpos = 0; //pos of next char in data not yet matched
|
||||
int gaps = 0; //number of 'gaps' in the match. A gap is any non-empty run of consecutive characters in the data that are not used by the match
|
||||
@@ -61,7 +61,7 @@ public class FuzzyMatcher {
|
||||
return score(gaps, skips, pattern);
|
||||
}
|
||||
|
||||
private static double score(int gaps, int skips, String pattern) {
|
||||
private static double score(int gaps, int skips, CharSequence pattern) {
|
||||
if (gaps==0) {
|
||||
//gaps == 0 means a prefix match, ignore 'skips' at end of String and just sort
|
||||
// alphabetic (see STS-4049)
|
||||
|
||||
@@ -55,21 +55,25 @@ public abstract class AbstractYamlAssistContext implements YamlAssistContext {
|
||||
if (keyNode.isInValue(offset)) {
|
||||
int valueStart = keyNode.getColonOffset()+1;
|
||||
int valueEnd = keyNode.getNodeEnd(); // assumes we only look at the current line, good enough for now
|
||||
return new DocumentRegion(doc.getDocument(), valueStart, valueEnd);
|
||||
DocumentRegion region = new DocumentRegion(doc.getDocument(), valueStart, valueEnd);
|
||||
if (region.startsWith(" ")) {
|
||||
region = region.subSequence(1);
|
||||
}
|
||||
return region;
|
||||
}
|
||||
}
|
||||
return null; // TODO Reaching here might mean support for calling the custom assistant isn't
|
||||
// implemented for this kind of context yet. It will have to be expanded upon
|
||||
// as the need for it arises in real use-cases.
|
||||
}
|
||||
|
||||
|
||||
private static PrefixFinder prefixfinder = new PrefixFinder() {
|
||||
@Override
|
||||
protected boolean isPrefixChar(char c) {
|
||||
return !Character.isWhitespace(c);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
protected String getPrefix(YamlDocument doc, SNode node, int offset) {
|
||||
//For value completions... in general we would like to determine the whole text
|
||||
// corresponding to the value, so a simplistic backwards scan isn't good enough.
|
||||
|
||||
@@ -37,7 +37,19 @@
|
||||
<artifactId>commons-yaml</artifactId>
|
||||
<version>${dependencies.version}</version>
|
||||
</dependency>
|
||||
<!-- github client -->
|
||||
<dependency>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>github-api</artifactId>
|
||||
<version>1.90</version>
|
||||
</dependency>
|
||||
<!-- Test harness -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>${mockito-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-test-harness</artifactId>
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
import org.springframework.ide.vscode.commons.yaml.snippet.SchemaBasedSnippetGenerator;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@@ -90,10 +91,10 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
|
||||
}
|
||||
}
|
||||
|
||||
public ConcourseLanguageServer(YamlCompletionEngineOptions completionOptions) {
|
||||
public ConcourseLanguageServer(YamlCompletionEngineOptions completionOptions, GithubInfoProvider github) {
|
||||
super("vscode-concourse");
|
||||
this.COMPLETION_OPTIONS = completionOptions;
|
||||
PipelineYmlSchema pipelineSchema = new PipelineYmlSchema(models);
|
||||
PipelineYmlSchema pipelineSchema = new PipelineYmlSchema(models, github);
|
||||
enableSnippets(pipelineSchema, true);
|
||||
this.yamlQuickfixes = new YamlQuickfixes(getQuickfixRegistry(), documents, structureProvider);
|
||||
|
||||
|
||||
@@ -16,13 +16,19 @@ import java.io.IOException;
|
||||
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
import org.springframework.ide.vscode.concourse.github.DefaultGithubInfoProvider;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp.STANDALONE_STARTUP;
|
||||
|
||||
public class Main {
|
||||
private static final YamlCompletionEngineOptions OPTIONS = YamlCompletionEngineOptions.DEFAULT;
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
String serverName = "concourse-language-server";
|
||||
LogRedirect.redirectToFile(serverName);
|
||||
LaunguageServerApp.start(serverName, () -> new ConcourseLanguageServer(OPTIONS));
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
String serverName = "concourse-language-server";
|
||||
if (!Boolean.getBoolean(STANDALONE_STARTUP)) {
|
||||
LogRedirect.redirectToFile(serverName);
|
||||
}
|
||||
LaunguageServerApp.start(serverName, () -> new ConcourseLanguageServer(OPTIONS, new DefaultGithubInfoProvider()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
|
||||
import org.springframework.ide.vscode.concourse.ConcourseModel.ResourceModel;
|
||||
import org.springframework.ide.vscode.concourse.ConcourseModel.StepModel;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubRepoContentAssistant;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
|
||||
@@ -160,7 +162,10 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
|
||||
private List<YType> definitionTypes = new ArrayList<>();
|
||||
|
||||
public PipelineYmlSchema(ConcourseModel models) {
|
||||
private GithubInfoProvider github;
|
||||
|
||||
public PipelineYmlSchema(ConcourseModel models, GithubInfoProvider github) {
|
||||
this.github = github;
|
||||
this.models = models;
|
||||
this.asts = models.getAstCache();
|
||||
models.setResourceTypeRegistry(resourceTypes);
|
||||
@@ -417,8 +422,11 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
private void initializeDefaultResourceTypes() {
|
||||
// git :
|
||||
{
|
||||
AbstractType t_git_repo_uri = f.yatomic("GitRepoUri");
|
||||
t_git_repo_uri.setCustomContentAssistant(new GithubRepoContentAssistant(github));
|
||||
|
||||
AbstractType source = f.ybean("GitSource");
|
||||
addProp(source, "uri", t_ne_string).isPrimary(true);
|
||||
addProp(source, "uri", t_git_repo_uri).isPrimary(true);
|
||||
addProp(source, "branch", t_ne_string); //It's more complicated than that! Its only required in 'put' step. So we'll check this as a contrain in put steps!
|
||||
addProp(source, "private_key", t_ne_string);
|
||||
addProp(source, "username", t_ne_string);
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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.concourse.github;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.kohsuke.github.GHPerson;
|
||||
import org.kohsuke.github.GHRepository;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class DefaultGithubInfoProvider implements GithubInfoProvider {
|
||||
|
||||
//TODO: we only try to connect to github once and cache the connection.
|
||||
//This means that, if creating the connection fails we won't try again.
|
||||
//It would be nice to cache connection only for a limited amount of time
|
||||
//especially when connecting fails. So that user may try to address the
|
||||
//issue and try again.
|
||||
|
||||
private GitHub github;
|
||||
private IOException connectionError;
|
||||
private Collection<String> owners;
|
||||
|
||||
private Cache<String, Collection<String>> reposByOwner = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
{
|
||||
try {
|
||||
checkConfiguration();
|
||||
github = GitHub.connect();
|
||||
} catch (IOException e) {
|
||||
connectionError = e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getOwners() throws Exception {
|
||||
if (connectionError!=null) {
|
||||
throw connectionError;
|
||||
}
|
||||
if (github!=null) {
|
||||
if (owners==null) {
|
||||
ImmutableSet.Builder<String> owners = ImmutableSet.builder();
|
||||
for (GHRepository repo : github.getMyself().listRepositories()) {
|
||||
owners.add(repo.getOwnerName());
|
||||
}
|
||||
this.owners = owners.build();
|
||||
}
|
||||
return owners;
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private void checkConfiguration() throws IOException {
|
||||
File configFile = new File(System.getProperty("user.home"));
|
||||
configFile = new File(configFile, ".github");
|
||||
if (!configFile.isFile()) {
|
||||
throw new IOException("`~/.github` doesn't exist: You will get better content assist for github " +
|
||||
"repos if you create a file at `~/.github` containing your github login and password:\n"+
|
||||
"\n" +
|
||||
" login=...username...\n" +
|
||||
" password=...password...\n"+
|
||||
"\n" +
|
||||
"Note: Github connection data is cached indefinitely, so the editor will need to be restarted for " +
|
||||
"this to take effect."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getReposForOwner(String ownerName) throws Exception {
|
||||
if (connectionError!=null) {
|
||||
throw connectionError;
|
||||
}
|
||||
try {
|
||||
if (github!=null) {
|
||||
return reposByOwner.get(ownerName, () -> {
|
||||
GHPerson owner = getOwner(ownerName);
|
||||
return Flux.fromIterable(owner.listRepositories())
|
||||
.filter(repo -> repo.getOwnerName().equals(ownerName))
|
||||
.map(GHRepository::getName)
|
||||
.collect(CollectorUtil.toImmutableSet())
|
||||
.block();
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private GHPerson getOwner(String ownerName) throws IOException {
|
||||
try {
|
||||
return github.getUser(ownerName);
|
||||
} catch (IOException e) {
|
||||
return github.getOrganization(ownerName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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.concourse.github;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface GithubInfoProvider {
|
||||
|
||||
/**
|
||||
* Retrieves a list of owners that can be suggested as hints in completions of github repo urls.
|
||||
* <p>
|
||||
* Note: since github has millions of users and fetching all of them isn't really an option, the owners
|
||||
* are expected to be somehow limited based on the user credentials (i.e. returning a list that is
|
||||
* deemed relevant to the current logged in user, rather than an exhaustive list of every org and user name
|
||||
* on github.
|
||||
*/
|
||||
Collection<String> getOwners() throws Exception;
|
||||
Collection<String> getReposForOwner(String owner) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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.concourse.github;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.SimpleCompletionFactory;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.CompletionFactory;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.ISubCompletionEngine;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class GithubRepoContentAssistant implements ISubCompletionEngine {
|
||||
|
||||
private String[] uriPrefixes = {
|
||||
"git@github.com:",
|
||||
"https://github.com/"
|
||||
};
|
||||
private GithubInfoProvider github;
|
||||
|
||||
public GithubRepoContentAssistant(GithubInfoProvider github) {
|
||||
this.github = github;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ICompletionProposal> getCompletions(CompletionFactory f, DocumentRegion region, int offset) {
|
||||
DocumentRegion query = region.subSequence(0, offset);
|
||||
//If uri prefix is already there, we provide CA for owner / repo
|
||||
for (String uriPrefix : uriPrefixes) {
|
||||
if (query.startsWith(uriPrefix)) {
|
||||
return getOwnerOrRepoCompletions(f, query.subSequence(uriPrefix.length()));
|
||||
}
|
||||
}
|
||||
//If uri prefix is not yet there, maybe we can suggest it (if it matches the query)
|
||||
List<ICompletionProposal> proposals = new ArrayList<>(uriPrefixes.length);
|
||||
for (String uriPrefix : uriPrefixes) {
|
||||
if (FuzzyMatcher.matchScore(query, uriPrefix)!=0.0) {
|
||||
proposals.add(SimpleCompletionFactory.simpleProposal(query, CompletionItemKind.Text, uriPrefix, null, null));
|
||||
}
|
||||
}
|
||||
return proposals;
|
||||
}
|
||||
|
||||
private List<ICompletionProposal> getOwnerOrRepoCompletions(CompletionFactory f, DocumentRegion ownerAndRepoRegion) {
|
||||
try {
|
||||
int slash = ownerAndRepoRegion.indexOf('/');
|
||||
if (slash>=0) {
|
||||
DocumentRegion owner = ownerAndRepoRegion.subSequence(0, slash);
|
||||
return getRepoCompletions(f, owner, ownerAndRepoRegion.subSequence(slash+1));
|
||||
} else {
|
||||
Collection<String> owners = github.getOwners();
|
||||
DocumentRegion query = ownerAndRepoRegion;
|
||||
if (!owners.isEmpty()) {
|
||||
List<ICompletionProposal> proposals = new ArrayList<>(owners.size());
|
||||
for (String owner : owners) {
|
||||
if (FuzzyMatcher.matchScore(query, owner)!=0.0) {
|
||||
proposals.add(SimpleCompletionFactory.simpleProposal(query, CompletionItemKind.Text, owner+"/", null, null));
|
||||
}
|
||||
}
|
||||
return proposals;
|
||||
} else {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return ImmutableList.of(f.errorMessage(ownerAndRepoRegion.toString(), ExceptionUtil.getMessageNoAppendedInformation(e)));
|
||||
}
|
||||
}
|
||||
|
||||
private List<ICompletionProposal> getRepoCompletions(CompletionFactory f, DocumentRegion owner, DocumentRegion query) {
|
||||
try {
|
||||
Collection<String> repos = github.getReposForOwner(owner.toString());
|
||||
if (repos!=null && !repos.isEmpty()) {
|
||||
List<ICompletionProposal> proposals = new ArrayList<>(repos.size());
|
||||
for (String repo : repos) {
|
||||
if (FuzzyMatcher.matchScore(query, repo)!=0.0) {
|
||||
proposals.add(SimpleCompletionFactory.simpleProposal(query, CompletionItemKind.Text, repo, null, null));
|
||||
}
|
||||
}
|
||||
return proposals;
|
||||
} else {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return ImmutableList.of(f.errorMessage(query.toString(), ExceptionUtil.getMessageNoAppendedInformation(e)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,8 +12,12 @@ package org.springframework.ide.vscode.concourse;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.Editor.INDENTED_COMPLETION;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.Editor.PLAIN_COMPLETION;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -31,12 +35,15 @@ import org.springframework.ide.vscode.commons.util.Unicodes;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.CodeAction;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.SynchronizationPoint;
|
||||
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.Editor.*;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class ConcourseEditorTest {
|
||||
|
||||
@@ -45,9 +52,11 @@ public class ConcourseEditorTest {
|
||||
private static final String CURSOR = "<*>";
|
||||
LanguageServerHarness harness;
|
||||
|
||||
private GithubInfoProvider github= Mockito.mock(GithubInfoProvider.class);
|
||||
|
||||
@Before public void setup() throws Exception {
|
||||
harness = new LanguageServerHarness(() -> {
|
||||
return new ConcourseLanguageServer(OPTIONS)
|
||||
return new ConcourseLanguageServer(OPTIONS, github)
|
||||
.setMaxCompletions(100);
|
||||
},
|
||||
LanguageId.CONCOURSE_PIPELINE
|
||||
@@ -4373,7 +4382,107 @@ public class ConcourseEditorTest {
|
||||
"publish-snapshot|'publish-snapshot' belongs to no group",
|
||||
"publish-release|'publish-release' belongs to no group"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void githubCompletionsUriTypes() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- name: my-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" uri: <*>"
|
||||
);
|
||||
editor.assertContextualCompletions("<*>",
|
||||
"git@github.com:<*>",
|
||||
"https://github.com/<*>"
|
||||
);
|
||||
|
||||
editor.assertContextualCompletions("@<*>",
|
||||
"git@github.com:<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void githubCompletionsOwners() throws Exception {
|
||||
when(github.getOwners()).thenReturn(ImmutableList.of(
|
||||
"kdvolder", "spring-projects", "spring-guides"
|
||||
));
|
||||
Editor editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- name: my-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" uri: <*>"
|
||||
);
|
||||
|
||||
editor.assertContextualCompletions("git@github.com:<*>",
|
||||
"git@github.com:kdvolder/<*>",
|
||||
"git@github.com:spring-guides/<*>",
|
||||
"git@github.com:spring-projects/<*>"
|
||||
);
|
||||
|
||||
editor.assertContextualCompletions("git@github.com:vol<*>",
|
||||
"git@github.com:kdvolder/<*>"
|
||||
);
|
||||
editor.assertContextualCompletions("https://github.com/<*>",
|
||||
"https://github.com/kdvolder/<*>",
|
||||
"https://github.com/spring-guides/<*>",
|
||||
"https://github.com/spring-projects/<*>"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test public void githubCompletionsRepos() throws Exception {
|
||||
when(github.getReposForOwner("the-owner")).thenReturn(ImmutableList.of(
|
||||
"nice-repo", "cool-project", "good-stuff"
|
||||
));
|
||||
Editor editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- name: my-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" uri: <*>"
|
||||
);
|
||||
editor.assertContextualCompletions("https://github.com/the-owner/<*>",
|
||||
"https://github.com/the-owner/cool-project<*>",
|
||||
"https://github.com/the-owner/good-stuff<*>",
|
||||
"https://github.com/the-owner/nice-repo<*>"
|
||||
);
|
||||
|
||||
editor.assertContextualCompletions("https://github.com/the-owner/proj<*>",
|
||||
"https://github.com/the-owner/cool-project<*>"
|
||||
);
|
||||
|
||||
editor.assertContextualCompletions("git@github.com:the-owner/<*>",
|
||||
"git@github.com:the-owner/cool-project<*>",
|
||||
"git@github.com:the-owner/good-stuff<*>",
|
||||
"git@github.com:the-owner/nice-repo<*>"
|
||||
);
|
||||
|
||||
editor.assertContextualCompletions("git@github.com:the-owner/proj<*>",
|
||||
"git@github.com:the-owner/cool-project<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void githubCompletionErrors() throws Exception {
|
||||
when(github.getReposForOwner("the-owner")).thenThrow(new IOException("Explain some stuff"));
|
||||
when(github.getOwners()).thenThrow(new IOException("Explain some stuff"));
|
||||
Editor editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- name: my-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" uri: git@github.com:the-owner/<*>"
|
||||
);
|
||||
editor.assertCompletionLabels("Explain some stuff");
|
||||
|
||||
editor.setText(
|
||||
"resources:\n" +
|
||||
"- name: my-repo\n" +
|
||||
" type: git\n" +
|
||||
" source:\n" +
|
||||
" uri: git@github.com:<*>"
|
||||
);
|
||||
editor.assertCompletionLabels("Explain some stuff");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -20,7 +20,9 @@ import java.nio.file.Paths;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.TextDocumentSyncKind;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.ide.vscode.concourse.ConcourseLanguageServer;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
import static org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions.*;
|
||||
|
||||
@@ -32,7 +34,8 @@ public class ConcourseLanguageServerTest {
|
||||
|
||||
@Test
|
||||
public void createAndInitializeServerWithWorkspace() throws Exception {
|
||||
LanguageServerHarness harness = new LanguageServerHarness(() -> new ConcourseLanguageServer(TEST_DEFAULT));
|
||||
LanguageServerHarness harness = new LanguageServerHarness(() ->
|
||||
new ConcourseLanguageServer(TEST_DEFAULT, Mockito.mock(GithubInfoProvider.class)));
|
||||
File workspaceRoot = getTestResource("/workspace/");
|
||||
assertExpectedInitResult(harness.intialize(workspaceRoot));
|
||||
}
|
||||
@@ -40,7 +43,8 @@ public class ConcourseLanguageServerTest {
|
||||
@Test
|
||||
public void createAndInitializeServerWithoutWorkspace() throws Exception {
|
||||
File workspaceRoot = null;
|
||||
LanguageServerHarness harness = new LanguageServerHarness(() -> new ConcourseLanguageServer(TEST_DEFAULT));
|
||||
LanguageServerHarness harness = new LanguageServerHarness(() ->
|
||||
new ConcourseLanguageServer(TEST_DEFAULT, Mockito.mock(GithubInfoProvider.class)));
|
||||
assertExpectedInitResult(harness.intialize(workspaceRoot));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ function error(msg : string) {
|
||||
export function activate(context: VSCode.ExtensionContext) {
|
||||
let options : commons.ActivatorOptions = {
|
||||
DEBUG : false,
|
||||
CONNECT_TO_LS: false,
|
||||
CONNECT_TO_LS: true,
|
||||
extensionId: 'vscode-concourse',
|
||||
launcher: (context: VSCode.ExtensionContext) => Path.resolve(context.extensionPath, 'jars/language-server.jar'),
|
||||
jvmHeap: "48m",
|
||||
|
||||
Reference in New Issue
Block a user