Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented error recovery support in parametric language servers #511

Open
wants to merge 8 commits into
base: error-recovery/rascal
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@
import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageClientAware;
import org.rascalmpl.exceptions.Throw;
import org.rascalmpl.parser.gtd.exception.ParseError;
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.values.IRascalValueFactory;
import org.rascalmpl.values.parsetrees.ITree;
Expand All @@ -107,7 +105,6 @@
import org.rascalmpl.vscode.lsp.parametric.model.ParametricSummary.SummaryLookup;
import org.rascalmpl.vscode.lsp.terminal.ITerminalIDEServer.LanguageParameter;
import org.rascalmpl.vscode.lsp.util.CodeActions;
import org.rascalmpl.vscode.lsp.util.Diagnostics;
import org.rascalmpl.vscode.lsp.util.FoldingRanges;
import org.rascalmpl.vscode.lsp.util.DocumentSymbols;
import org.rascalmpl.vscode.lsp.util.SemanticTokenizer;
Expand Down Expand Up @@ -227,7 +224,8 @@ public void connect(LanguageClient client) {
@Override
public void didOpen(DidOpenTextDocumentParams params) {
logger.debug("Did Open file: {}", params.getTextDocument());
handleParsingErrors(open(params.getTextDocument()));
TextDocumentState file = open(params.getTextDocument());
handleParsingErrors(file, file.getCurrentDiagnosticsAsync()); // No debounce
triggerAnalyzer(params.getTextDocument(), Duration.ofMillis(800));
}

Expand Down Expand Up @@ -279,37 +277,15 @@ private TextDocumentState updateContents(VersionedTextDocumentIdentifier doc, St
TextDocumentState file = getFile(doc);
logger.trace("New contents for {}", doc);
file.update(doc.getVersion(), newContents);
handleParsingErrors(file, file.getCurrentTreeAsync()); // Warning: Might be a later version (when a concurrent update happened)
handleParsingErrors(file, file.getCurrentDiagnosticsAsync(Duration.ofMillis(800))); // Warning: Might be a later version (when a concurrent update happened)
return file;
}

private void handleParsingErrors(TextDocumentState file, CompletableFuture<Versioned<ITree>> futureTree) {
var version = file.getCurrentContent().version();
futureTree.handle((tree, excp) -> {
Diagnostic newParseError = null;
if (excp instanceof CompletionException) {
excp = excp.getCause();
}

if (excp instanceof Throw) {
Throw thrown = (Throw) excp;
newParseError = Diagnostics.translateRascalParseError(thrown.getException(), columns);
}
else if (excp instanceof ParseError) {
newParseError = Diagnostics.translateDiagnostic((ParseError)excp, columns);
}
else if (excp != null) {
logger.error("Parsing crashed", excp);
newParseError = new Diagnostic(
new Range(new Position(0,0), new Position(0,1)),
"Parsing failed: " + excp.getMessage(),
DiagnosticSeverity.Error,
"Rascal Parser");
}
logger.trace("Finished parsing tree, reporting new parse error: {} for: {}", newParseError, file.getLocation());
facts(file.getLocation()).reportParseErrors(file.getLocation(), version,
newParseError == null ? Collections.emptyList() : Collections.singletonList(newParseError));
return null;
private void handleParsingErrors(TextDocumentState file, CompletableFuture<Versioned<List<Diagnostic>>> diagnosticsAsync) {
diagnosticsAsync.thenAccept(diagnostics -> {
List<Diagnostic> parseErrors = diagnostics.get();
logger.trace("Finished parsing tree, reporting new parse errors: {} for: {}", parseErrors, file.getLocation());
facts(file.getLocation()).reportParseErrors(file.getLocation(), diagnostics.version(), parseErrors);
});
}

Expand Down Expand Up @@ -382,12 +358,6 @@ private CodeLens locCommandTupleToCodeLense(String languageName, IValue v) {
return new CodeLens(Locations.toRange(loc, columns), CodeActions.constructorToCommand(dedicatedLanguageName, languageName, command), null);
}



private void handleParsingErrors(TextDocumentState file) {
handleParsingErrors(file, file.getCurrentTreeAsync());
}

private static <T> T last(List<T> l) {
return l.get(l.size() - 1);
}
Expand Down
10 changes: 5 additions & 5 deletions rascal-lsp/src/main/rascal/demo/lang/pico/LanguageServer.rsc
PieterOlivier marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Here we group all services such that the LSP server can link them
with the ((Language)) definition later.
}
set[LanguageService] picoLanguageServer() = {
parsing(parser(#start[Program])),
parsing(parser(#start[Program], allowRecovery=true, allowAmbiguity=false)),
documentSymbol(picoDocumentSymbolService),
codeLens(picoCodeLenseService),
execution(picoExecutionService),
Expand All @@ -62,7 +62,7 @@ such that quicky loaded features can be made available while slower to load
tools come in later.
}
set[LanguageService] picoLanguageServerSlowSummary() = {
parsing(parser(#start[Program])),
parsing(parser(#start[Program], allowRecovery=true, allowAmbiguity=false)),
analysis(picoAnalysisService, providesImplementations = false),
build(picoBuildService)
};
Expand All @@ -74,7 +74,7 @@ symbol search in the editor.
}
list[DocumentSymbol] picoDocumentSymbolService(start[Program] input)
= [symbol("<input.src>", DocumentSymbolKind::\file(), input.src, children=[
*[symbol("<var.id>", \variable(), var.src) | /IdType var := input]
*[symbol("<var.id>", \variable(), var.src) | /IdType var := input, var has id]
PieterOlivier marked this conversation as resolved.
Show resolved Hide resolved
])];

@synopsis{The analyzer maps pico syntax trees to error messages and references}
Expand Down Expand Up @@ -195,7 +195,7 @@ void main() {
pathConfig(),
"Pico",
{"pico", "pico-new"},
"demo::lang::pico::NewLanguageServer",
"demo::lang::pico::LanguageServer",
PieterOlivier marked this conversation as resolved.
Show resolved Hide resolved
"picoLanguageServer"
)
);
Expand All @@ -204,7 +204,7 @@ void main() {
pathConfig(),
"Pico",
{"pico", "pico-new"},
"demo::lang::pico::NewLanguageServer",
"demo::lang::pico::LanguageServer",
"picoLanguageServerSlowSummary"
)
);
Expand Down
Loading