Skip to content

Commit

Permalink
feat(eclipse): support to collect additional context used for code co…
Browse files Browse the repository at this point in the history
…mpletion. (#2971)

* feat(eclipse): impl git provider lsp client.

* feat(eclipse): add client side language support based on lsp4e.

* chore(eclipse): bump build number.
  • Loading branch information
icycodes authored Aug 23, 2024
1 parent 2eafea6 commit 74c78dd
Show file tree
Hide file tree
Showing 13 changed files with 371 additions and 10 deletions.
4 changes: 2 additions & 2 deletions clients/eclipse/feature/feature.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<feature
id="com.tabbyml.features.tabby4eclipse"
label="Tabby"
version="0.0.1.13"
version="0.0.1.14"
provider-name="com.tabbyml">

<description url="http://www.example.com/description">
Expand All @@ -19,6 +19,6 @@

<plugin
id="com.tabbyml.tabby4eclipse"
version="0.0.1.13"/>
version="0.0.1.14"/>

</feature>
2 changes: 1 addition & 1 deletion clients/eclipse/plugin/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tabby Plugin for Eclipse
Bundle-SymbolicName: com.tabbyml.tabby4eclipse;singleton:=true
Bundle-Version: 0.0.1.13
Bundle-Version: 0.0.1.14
Bundle-Activator: com.tabbyml.tabby4eclipse.Activator
Bundle-Vendor: com.tabbyml
Require-Bundle: org.eclipse.ui,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import com.tabbyml.tabby4eclipse.lsp.StatusInfoHolder;
import com.tabbyml.tabby4eclipse.lsp.protocol.Config;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitRepository;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitRepositoryParams;
import com.tabbyml.tabby4eclipse.lsp.protocol.ILanguageServer;
import com.tabbyml.tabby4eclipse.lsp.protocol.IStatusService;
import com.tabbyml.tabby4eclipse.lsp.protocol.StatusInfo;
Expand Down Expand Up @@ -234,8 +235,9 @@ private void reloadContentForStatus(String status, boolean force) {
} else {
// Load main
Config.ServerConfig config = serverConfigHolder.getConfig().getServer();
if (force || currentConfig == null || currentConfig.getEndpoint() != config.getEndpoint()
|| currentConfig.getToken() != config.getToken()) {
if (config != null
&& (force || currentConfig == null || currentConfig.getEndpoint() != config.getEndpoint()
|| currentConfig.getToken() != config.getToken())) {
showMessage("Connecting to Tabby server...");
showChatPanel(false);
currentConfig = config;
Expand Down Expand Up @@ -504,7 +506,8 @@ private FileContext getActiveContext() {
IFile file = ResourceUtil.getFile(activeTextEditor.getEditorInput());
URI fileUri = file.getLocationURI();
if (file != null) {
GitRepository gitInfo = GitProvider.getRepository(fileUri);
GitRepository gitInfo = GitProvider.getInstance()
.getRepository(new GitRepositoryParams(fileUri.toString()));
IProject project = file.getProject();
if (gitInfo != null) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
package com.tabbyml.tabby4eclipse.git;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.net.URI;
import java.util.List;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.RemoteConfig;

import com.tabbyml.tabby4eclipse.Logger;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitDiffParams;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitDiffResult;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitRepository;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitRepositoryParams;

public class GitProvider {
private static Logger logger = new Logger("GitProvider");
public static GitProvider getInstance() {
return LazyHolder.INSTANCE;
}

private static class LazyHolder {
private static final GitProvider INSTANCE = new GitProvider();
}

public static GitRepository getRepository(URI uri) {
private Logger logger = new Logger("GitProvider");

public GitRepository getRepository(GitRepositoryParams params) {
try {
URI uri = new URI(params.getUri());
File file = new File(uri);
FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repository = builder.findGitDir(file).build();
Expand Down Expand Up @@ -48,7 +62,32 @@ public static GitRepository getRepository(URI uri) {
return null;
}
} catch (Exception e) {
logger.info("Failed to get repository for: " + uri);
logger.error("Failed to get repository for: " + params.getUri(), e);
return null;
}
}

public GitDiffResult getDiff(GitDiffParams params) {
try {
URI repoPath = new URI(params.getRepository());
File repoDir = new File(repoPath);
FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repository = builder.findGitDir(repoDir).build();
if (repository != null) {
try (Git git = new Git(repository)) {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
git.diff().setCached(params.getCached()).setOutputStream(outStream).call();
String diff = outStream.toString("UTF-8");

GitDiffResult result = new GitDiffResult(diff);
outStream.close();
return result;
}
} else {
return null;
}
} catch (Exception e) {
logger.error("Failed to get diff for: " + params.getRepository(), e);
return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ private ClientCapabilities getClientCapabilities() {
TabbyClientCapabilities tabbyClientCapabilities = new TabbyClientCapabilities();
tabbyClientCapabilities.setConfigDidChangeListener(true);
tabbyClientCapabilities.setStatusDidChangeListener(true);
tabbyClientCapabilities.setWorkspaceFileSystem(true);
tabbyClientCapabilities.setGitProvider(true);
tabbyClientCapabilities.setLanguageSupport(true);

ClientCapabilities clientCapabilities = new ClientCapabilities();
clientCapabilities.setTextDocument(textDocumentClientCapabilities);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,92 @@
package com.tabbyml.tabby4eclipse.lsp;

import java.net.URI;
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SemanticTokensRangeParams;
import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServers;
import org.eclipse.lsp4j.DeclarationParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;

import com.tabbyml.tabby4eclipse.git.GitProvider;
import com.tabbyml.tabby4eclipse.lsp.protocol.Config;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitDiffParams;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitDiffResult;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitRepository;
import com.tabbyml.tabby4eclipse.lsp.protocol.GitRepositoryParams;
import com.tabbyml.tabby4eclipse.lsp.protocol.ReadFileParams;
import com.tabbyml.tabby4eclipse.lsp.protocol.ReadFileResult;
import com.tabbyml.tabby4eclipse.lsp.protocol.SemanticTokensRangeResult;
import com.tabbyml.tabby4eclipse.lsp.protocol.StatusInfo;

public class LanguageClientImpl extends org.eclipse.lsp4e.LanguageClientImpl {
@JsonNotification("tabby/status/didChange")
void statusDidChange(StatusInfo params) {
StatusInfoHolder.getInstance().setStatusInfo(params);
}

@JsonNotification("tabby/config/didChange")
void statusDidChange(Config params) {
ServerConfigHolder.getInstance().setConfig(params);
}

@JsonRequest("tabby/workspaceFileSystem/readFile")
CompletableFuture<ReadFileResult> workspaceReadFile(ReadFileParams params) {
if (params.getFormat().equals("text")) {
try {
URI uri = new URI(params.getUri());
IDocument document = LSPEclipseUtils.getDocument(uri);
Range range = params.getRange();

ReadFileResult result = new ReadFileResult();
if (range != null) {
int start = LSPEclipseUtils.toOffset(range.getStart(), document);
int end = LSPEclipseUtils.toOffset(range.getEnd(), document);
result.setText(document.get(start, end - start));
} else {
result.setText(document.get());
}
return CompletableFuture.completedFuture(result);
} catch (Exception e) {
return CompletableFuture.completedFuture(null);
}
} else {
return CompletableFuture.completedFuture(null);
}
}

@JsonRequest("tabby/git/repository")
CompletableFuture<GitRepository> gitRepository(GitRepositoryParams params) {
CompletableFuture<GitRepository> future = new CompletableFuture<>();
GitRepository result = GitProvider.getInstance().getRepository(params);
future.complete(result);
return future;
}

@JsonRequest("tabby/git/diff")
CompletableFuture<GitDiffResult> gitDiff(GitDiffParams params) {
CompletableFuture<GitDiffResult> future = new CompletableFuture<>();
GitDiffResult result = GitProvider.getInstance().getDiff(params);
future.complete(result);
return future;
}

@JsonRequest("tabby/languageSupport/textDocument/declaration")
CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> languageSupportDeclaration(
DeclarationParams params) {
return LanguageSupportProvider.getInstance().languageSupportDeclaration(params);
}

@JsonRequest("tabby/languageSupport/textDocument/semanticTokens/range")
CompletableFuture<SemanticTokensRangeResult> languageSupportSemanticTokensRange(SemanticTokensRangeParams params) {
return LanguageSupportProvider.getInstance().languageSupportSemanticTokensRange(params);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.tabbyml.tabby4eclipse.lsp;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.net.URI;

import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServers;
import org.eclipse.lsp4j.DeclarationParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.SemanticTokensLegend;
import org.eclipse.lsp4j.SemanticTokensRangeParams;
import org.eclipse.lsp4j.jsonrpc.messages.Either;

import com.tabbyml.tabby4eclipse.Logger;
import com.tabbyml.tabby4eclipse.lsp.protocol.SemanticTokensRangeResult;

public class LanguageSupportProvider {
public static LanguageSupportProvider getInstance() {
return LazyHolder.INSTANCE;
}

private static class LazyHolder {
private static final LanguageSupportProvider INSTANCE = new LanguageSupportProvider();
}

private Logger logger = new Logger("LanguageSupportProvider");

CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> languageSupportDeclaration(
DeclarationParams params) {
try {
URI uri = new URI(params.getTextDocument().getUri());
IDocument document = LSPEclipseUtils.getDocument(uri);

CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> future = LanguageServers
.forDocument(document).withFilter((serverCapabilities) -> {
return serverCapabilities.getDeclarationProvider() != null;
}).computeFirst((wrapper, server) -> {
return server.getTextDocumentService().declaration(params);
}).thenApply((result) -> {
if (result.isPresent()) {
return result.get();
} else {
return null;
}
});
return future;
} catch (Exception e) {
logger.error("Error while processing languageSupport/declaration.", e);
return CompletableFuture.completedFuture(null);
}
}

CompletableFuture<SemanticTokensRangeResult> languageSupportSemanticTokensRange(SemanticTokensRangeParams params) {
try {
URI uri = new URI(params.getTextDocument().getUri());
IDocument document = LSPEclipseUtils.getDocument(uri);

CompletableFuture<SemanticTokensRangeResult> future = LanguageServers.forDocument(document)
.withFilter((serverCapabilities) -> {
return serverCapabilities.getSelectionRangeProvider() != null;
}).computeFirst((wrapper, server) -> {
return server.getTextDocumentService().semanticTokensRange(params).thenApply((tokens) -> {
SemanticTokensRangeResult result = new SemanticTokensRangeResult();
SemanticTokensLegend legend = wrapper.getServerCapabilities().getSemanticTokensProvider()
.getLegend();
result.setLegend(legend);
result.setTokens(tokens);
return result;
});
}).thenApply((result) -> {
if (result.isPresent()) {
return result.get();
} else {
return null;
}
});
return future;
} catch (Exception e) {
logger.error("Error while processing languageSupport/semanticTokens/range.", e);
return CompletableFuture.completedFuture(null);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.tabbyml.tabby4eclipse.lsp.protocol;

public class GitDiffParams {
private String repository;
private boolean cached;

public GitDiffParams() {
}

public GitDiffParams(String repository) {
this.repository = repository;
this.cached = false;
}

public GitDiffParams(String repository, boolean cached) {
this.repository = repository;
this.cached = cached;
}

public String getRepository() {
return repository;
}

public void setRepository(String repository) {
this.repository = repository;
}

public boolean getCached() {
return cached;
}

public void setCached(boolean cached) {
this.cached = cached;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.tabbyml.tabby4eclipse.lsp.protocol;

public class GitDiffResult {
private String diff;

public GitDiffResult() {
}

public GitDiffResult(String diff) {
this.diff = diff;
}

public String getDiff() {
return diff;
}

public void setDiff(String diff) {
this.diff = diff;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.tabbyml.tabby4eclipse.lsp.protocol;

public class GitRepositoryParams {
private String uri;

public GitRepositoryParams(String uri) {
this.uri = uri;
}

public String getUri() {
return uri;
}

public void setUri(String uri) {
this.uri = uri;
}
}
Loading

0 comments on commit 74c78dd

Please sign in to comment.