Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*/
package org.rascalmpl.vscode.lsp;

import com.google.gson.JsonPrimitive;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
Expand All @@ -37,6 +38,7 @@
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.eclipse.lsp4j.ClientCapabilities;
import org.eclipse.lsp4j.CreateFilesParams;
import org.eclipse.lsp4j.DeleteFilesParams;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
Expand All @@ -45,7 +47,6 @@
import org.eclipse.lsp4j.FileDelete;
import org.eclipse.lsp4j.FileOperationFilter;
import org.eclipse.lsp4j.FileOperationOptions;
import org.eclipse.lsp4j.FileOperationPattern;
import org.eclipse.lsp4j.FileOperationsServerCapabilities;
import org.eclipse.lsp4j.RenameFilesParams;
import org.eclipse.lsp4j.ServerCapabilities;
Expand All @@ -56,7 +57,6 @@
import org.eclipse.lsp4j.services.LanguageClientAware;
import org.eclipse.lsp4j.services.WorkspaceService;
import org.rascalmpl.uri.URIUtil;
import com.google.gson.JsonPrimitive;

public abstract class BaseWorkspaceService implements WorkspaceService, LanguageClientAware {
private static final Logger logger = LogManager.getLogger(BaseWorkspaceService.class);
Expand All @@ -71,10 +71,10 @@ public abstract class BaseWorkspaceService implements WorkspaceService, Language

private final IBaseTextDocumentService documentService;
private final CopyOnWriteArrayList<WorkspaceFolder> workspaceFolders = new CopyOnWriteArrayList<>();
private final List<FileOperationPattern> interestedInFiles;
private final List<FileOperationFilter> interestedInFiles;


protected BaseWorkspaceService(ExecutorService exec, IBaseTextDocumentService documentService, List<FileOperationPattern> interestedInFiles) {
protected BaseWorkspaceService(ExecutorService exec, IBaseTextDocumentService documentService, List<FileOperationFilter> interestedInFiles) {
this.documentService = documentService;
this.ownExecuter = exec;
this.interestedInFiles = interestedInFiles;
Expand All @@ -98,10 +98,7 @@ public void initialize(ClientCapabilities clientCap, @Nullable List<WorkspaceFol
}

var fileOperationCapabilities = new FileOperationsServerCapabilities();
var whichFiles = new FileOperationOptions(interestedInFiles.stream()
.map(FileOperationFilter::new)
.collect(Collectors.toList())
);
var whichFiles = new FileOperationOptions(interestedInFiles);
boolean watchesSet = false;
if (clientWorkspaceCap.getFileOperations().getDidRename().booleanValue()) {
fileOperationCapabilities.setDidRename(whichFiles);
Expand Down Expand Up @@ -165,13 +162,17 @@ public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) {
}
}

@Override
public void didCreateFiles(CreateFilesParams params) {
logger.debug("workspace/didCreateFiles: {}", params.getFiles());
ownExecuter.submit(() -> documentService.didCreateFiles(params));
}

@Override
public void didRenameFiles(RenameFilesParams params) {
logger.debug("workspace/didRenameFiles: {}", params.getFiles());

ownExecuter.submit(() -> {
documentService.didRenameFiles(params, workspaceFolders());
});
ownExecuter.submit(() -> documentService.didRenameFiles(params, workspaceFolders()));

ownExecuter.submit(() -> {
// cleanup the old files (we do not get a `didDelete` event)
Expand Down Expand Up @@ -204,7 +205,7 @@ public CompletableFuture<Object> executeCommand(ExecuteCommandParams params) {
return CompletableFuture.supplyAsync(() -> params.getCommand() + " was ignored.", ownExecuter);
}

protected final ExecutorService getExecuter() {
protected final ExecutorService getExecutor() {
return ownExecuter;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.eclipse.lsp4j.CreateFilesParams;
import org.eclipse.lsp4j.DeleteFilesParams;
import org.eclipse.lsp4j.RenameFilesParams;
import org.eclipse.lsp4j.ServerCapabilities;
Expand Down Expand Up @@ -65,6 +66,7 @@ public interface IBaseTextDocumentService extends TextDocumentService {

boolean isManagingFile(ISourceLocation file);

void didCreateFiles(CreateFilesParams params);
void didRenameFiles(RenameFilesParams params, List<WorkspaceFolder> workspaceFolders);
void didDeleteFiles(DeleteFilesParams params);
void cancelProgress(String progressId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.eclipse.lsp4j.CodeLensOptions;
import org.eclipse.lsp4j.CodeLensParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CreateFilesParams;
import org.eclipse.lsp4j.DefinitionParams;
import org.eclipse.lsp4j.DeleteFilesParams;
import org.eclipse.lsp4j.Diagnostic;
Expand Down Expand Up @@ -500,6 +501,12 @@ private static MessageParams setMessageParams(IConstructor message) {
return params;
}

@Override
public void didCreateFiles(CreateFilesParams params) {
// This is fine, as long as `ParametricWorkspaceService` doet not claim to support the `didCreateFiles` capability.
throw new UnsupportedOperationException("Unimplemented method 'didCreateFiles'");
}

@Override
public void didRenameFiles(RenameFilesParams params, List<WorkspaceFolder> workspaceFolders) {
Map<ILanguageContributions, List<FileRename>> byContrib = bundleRenamesByContribution(params.getFiles());
Expand All @@ -516,7 +523,7 @@ public void didRenameFiles(RenameFilesParams params, List<WorkspaceFolder> works
var client = availableClient();
showMessages(client, messages);

if (edits.size() == 0) {
if (edits.isEmpty()) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@

import java.util.List;
import java.util.concurrent.ExecutorService;

import org.eclipse.lsp4j.FileOperationFilter;
import org.eclipse.lsp4j.FileOperationPattern;
import org.rascalmpl.vscode.lsp.BaseWorkspaceService;
import org.rascalmpl.vscode.lsp.IBaseTextDocumentService;

public class ParametricWorkspaceService extends BaseWorkspaceService {
ParametricWorkspaceService(ExecutorService exec, IBaseTextDocumentService docService) {
super(exec, docService, List.of(new FileOperationPattern("**/*")));
super(exec, docService, List.of(new FileOperationFilter(new FileOperationPattern("**/*"))));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
Expand Down Expand Up @@ -92,7 +91,7 @@ public class RascalLanguageServices {
private static final IValueFactory VF = IRascalValueFactory.getInstance();
private static final Logger logger = LogManager.getLogger(RascalLanguageServices.class);

private final CompletableFuture<Evaluator> documentSymbolEvaluator;
private final CompletableFuture<Evaluator> shortRunningTaskEvaluator;
private final CompletableFuture<Evaluator> semanticEvaluator;
private final CompletableFuture<Evaluator> compilerEvaluator;

Expand Down Expand Up @@ -123,7 +122,7 @@ public RascalLanguageServices(RascalTextDocumentService docService, BaseWorkspac

var context = new LSPContext(exec, docService, workspaceService, client);

documentSymbolEvaluator = makeFutureEvaluator(context, "Rascal document symbols", monitor, pcfg, "lang::rascal::lsp::DocumentSymbols");
shortRunningTaskEvaluator = makeFutureEvaluator(context, "Rascal tasks", monitor, pcfg, "lang::rascal::lsp::DocumentSymbols", "lang::rascal::lsp::Templates");
semanticEvaluator = makeFutureEvaluator(context, "Rascal semantics", monitor, compilerPcfg, "lang::rascalcore::check::Summary", "lang::rascal::lsp::refactor::Rename", "lang::rascal::lsp::Actions");
compilerEvaluator = makeFutureEvaluator(context, "Rascal compiler", monitor, compilerPcfg, "lang::rascal::lsp::IDECheckerWrapper");
actionStore = semanticEvaluator.thenApply(e -> ((ModuleEnvironment) e.getModule("lang::rascal::lsp::Actions")).getStore());
Expand Down Expand Up @@ -223,7 +222,7 @@ public InterruptibleFuture<IList> getDocumentSymbols(IConstructor module) {
});
}

return runEvaluator("Rascal Document Symbols", documentSymbolEvaluator, eval -> (IList) eval.call("documentRascalSymbols", module),
return runEvaluator("Rascal Document Symbols", shortRunningTaskEvaluator, eval -> (IList) eval.call("documentRascalSymbols", module),
VF.list(), exec, false, client);
}

Expand Down Expand Up @@ -264,6 +263,16 @@ public InterruptibleFuture<ITuple> getModuleRenames(IList fileRenames, Set<ISour
, emptyResult, exec, false, client);
}

public InterruptibleFuture<IList> newModuleTemplates(IList newFiles) {
return EvaluatorUtil.runEvaluator("Rascal new module", shortRunningTaskEvaluator,
eval -> {
try {
return (IList) eval.call("newModuleTemplates", newFiles, makePathConfigGetter(eval));
} catch (Throw e) {
throw new RuntimeException(e.getMessage());
}
}, VF.list(), exec, false, client);
}

public CompletableFuture<ITree> parseSourceFile(ISourceLocation loc, String input) {
return CompletableFuture.supplyAsync(() -> RascalServices.parseRascalModule(loc, input.toCharArray()), exec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,24 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
import org.eclipse.lsp4j.ApplyWorkspaceEditResponse;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.CodeLensOptions;
import org.eclipse.lsp4j.CodeLensParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CreateFilesParams;
import org.eclipse.lsp4j.DefinitionParams;
import org.eclipse.lsp4j.DeleteFilesParams;
import org.eclipse.lsp4j.Diagnostic;
Expand All @@ -61,6 +63,7 @@
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.ExecuteCommandOptions;
import org.eclipse.lsp4j.FileCreate;
import org.eclipse.lsp4j.FoldingRange;
import org.eclipse.lsp4j.FoldingRangeRequestParams;
import org.eclipse.lsp4j.Hover;
Expand Down Expand Up @@ -493,9 +496,22 @@ public CompletableFuture<List<FoldingRange>> foldingRange(FoldingRangeRequestPar
}

@Override
public void didRenameFiles(RenameFilesParams params, List<WorkspaceFolder> workspaceFolders) {
logger.debug("workspace/didRenameFiles: {}", params.getFiles());
public void didCreateFiles(CreateFilesParams params) {
var newFiles = params.getFiles()
.stream()
.map(FileCreate::getUri)
.map(URIUtil::assumeCorrectLocation)
.collect(VF.listWriter());

var edits = availableRascalServices().newModuleTemplates(newFiles).get();
applyDocumentEdits("Auto-insert module headers", edits, res -> {
logger.error("Applying new module template failed{}", failureReason(res));
return null;
});
}

@Override
public void didRenameFiles(RenameFilesParams params, List<WorkspaceFolder> workspaceFolders) {
Set<ISourceLocation> folders = workspaceFolders.stream()
.map(f -> Locations.toLoc(f.getUri()))
.collect(Collectors.toSet());
Expand All @@ -504,38 +520,26 @@ public void didRenameFiles(RenameFilesParams params, List<WorkspaceFolder> works
.map(r -> VF.tuple(URIUtil.assumeCorrectLocation(r.getOldUri()), URIUtil.assumeCorrectLocation(r.getNewUri())))
.collect(VF.listWriter());

availableRascalServices().getModuleRenames(renames, folders)
.thenAccept(res -> {
var rascalEdits = availableRascalServices().getModuleRenames(renames, folders)
.get()
.thenApply(res -> {
var edits = (IList) res.get(0);
var messages = (ISet) res.get(1);
showMessages(messages);

if (edits.size() == 0) {
return;
}

var changes = DocumentChanges.translateDocumentChanges(this, edits);
availableClient().applyEdit(new ApplyWorkspaceEditParams(changes, "Rename files")).thenAccept(editResponse -> {
if (!editResponse.isApplied()) {
throw new RuntimeException("Applying module rename failed" + (editResponse.getFailureReason() != null ? (": " + editResponse.getFailureReason()) : ""));
}
});
})
.get()
.exceptionally(e -> {
var cause = e.getCause();
logger.catching(Level.ERROR, cause);
String message = "unkown error";
if (cause != null && cause.getMessage() != null) {
message= cause.getMessage();
}
availableClient().showMessage(new MessageParams(MessageType.Error, message));
return null; // Return of type `Void` is unused, but required
return edits;
});

applyDocumentEdits("Module rename", rascalEdits, res -> {
throw new RuntimeException("Applying module rename failed" + failureReason(res));
});
}

// Private utility methods

private String failureReason(ApplyWorkspaceEditResponse res) {
return res.getFailureReason() != null ? (": " + res.getFailureReason()) : "";
}

private static <T> T last(List<T> l) {
return l.get(l.size() - 1);
}
Expand Down Expand Up @@ -605,6 +609,28 @@ public CompletableFuture<List<SelectionRange>> selectionRange(SelectionRangePara
.collect(Collectors.toList())));
}

private <T> CompletableFuture<@Nullable T> applyDocumentEdits(String task, CompletableFuture<IList> rascalEdits, Function<ApplyWorkspaceEditResponse, T> notApplied) {
return rascalEdits.thenApply(edits -> !edits.isEmpty() ? DocumentChanges.translateDocumentChanges(this, edits) : null) // pass null all the way through if our list of edits is empty
.thenCompose(edits -> edits != null ? availableClient().applyEdit(new ApplyWorkspaceEditParams(edits, task)) : null)
.thenApply(res -> {
if (res != null && !res.isApplied()) {
logger.trace("Could not apply workspace edits: {}", res.getFailureReason());
return notApplied.apply(res);
}
return null;
})
.exceptionally(e -> {
var cause = e.getCause();
logger.catching(Level.ERROR, cause);
String message = "unkown error";
if (cause != null && cause.getMessage() != null) {
message = cause.getMessage();
}
availableClient().showMessage(new MessageParams(MessageType.Error, String.format("Error during '%s': %s", task, message)));
return null; // Return of type `Void` is unused, but required
});
}

@Override
public void registerLanguage(LanguageParameter lang) {
throw new UnsupportedOperationException("registering language is a feature of the language parametric server, not of the Rascal server");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,32 @@

import java.util.List;
import java.util.concurrent.ExecutorService;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.eclipse.lsp4j.ClientCapabilities;
import org.eclipse.lsp4j.FileOperationFilter;
import org.eclipse.lsp4j.FileOperationOptions;
import org.eclipse.lsp4j.FileOperationPattern;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.WorkspaceFolder;
import org.rascalmpl.vscode.lsp.BaseWorkspaceService;
import org.rascalmpl.vscode.lsp.IBaseTextDocumentService;

public class RascalWorkspaceService extends BaseWorkspaceService {

private static final List<FileOperationFilter> fileFilters = List.of(new FileOperationFilter(new FileOperationPattern("**/*.rsc")));

RascalWorkspaceService(ExecutorService exec, IBaseTextDocumentService documentService) {
super(exec, documentService, List.of(new FileOperationPattern("**/*.rsc")));
super(exec, documentService, fileFilters);
}

@Override
public void initialize(ClientCapabilities clientCap, @Nullable List<WorkspaceFolder> currentWorkspaceFolders,
ServerCapabilities capabilities) {
super.initialize(clientCap, currentWorkspaceFolders, capabilities);
var workspaceCapabilities = capabilities.getWorkspace();
if (clientCap.getWorkspace().getFileOperations().getDidCreate().booleanValue()) {
workspaceCapabilities.getFileOperations().setDidCreate(new FileOperationOptions(fileFilters));
}
}

}
Loading
Loading