Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
01fa185
Keep track of opened files with unknown extension.
toinehartman Oct 1, 2025
ec1363b
Check for valid state before getting files.
toinehartman Oct 1, 2025
2d7b93c
Use dummy parser instead of separate open file bookkeeping.
toinehartman Oct 2, 2025
ce5aee8
Reset parser.
toinehartman Oct 2, 2025
ab6e7c6
Simplify updating open files.
toinehartman Oct 6, 2025
1a0da53
Re-use newly introduced language helpers.
toinehartman Oct 6, 2025
8d8b675
Merge remote-tracking branch 'origin/main' into fix/795-open-register…
toinehartman Oct 6, 2025
65bd32f
Replace state instead of updating parser.
toinehartman Oct 6, 2025
e9f791a
Exception instead of incomplete future.
toinehartman Oct 9, 2025
bee0669
Merge remote-tracking branch 'origin/main' into fix/795-open-register…
toinehartman Oct 9, 2025
37c816c
Prevent 'no such file' errors using dummy contribution.
toinehartman Oct 13, 2025
da179cf
Clean up declarations & imports.
toinehartman Oct 13, 2025
eaa412f
Prevent 'no such file' errors on analysis.
toinehartman Oct 13, 2025
80b9fd3
Register extension after contibution registration is done.
toinehartman Oct 13, 2025
efaa03c
Merge remote-tracking branch 'origin/main' into fix/795-open-register…
toinehartman Oct 13, 2025
261bf55
Extract updating file state to functions.
toinehartman Oct 13, 2025
cfebe65
Properly log analyzer triggers.
toinehartman Oct 13, 2025
b6380dd
Reset language ID to re-trigger contributions.
toinehartman Oct 14, 2025
eb973f0
Rewrite optional mapping.
toinehartman Oct 14, 2025
74b8451
Merge remote-tracking branch 'origin/main' into fix/795-open-register…
toinehartman Oct 14, 2025
252608f
Log port number when server fails.
toinehartman Oct 14, 2025
edb3302
Use precise replace to prevent races.
toinehartman Oct 14, 2025
82b5b98
Bump log level.
toinehartman Oct 14, 2025
73a479e
Document NoContributions.
toinehartman Oct 14, 2025
6485415
Fix SQ issues.
toinehartman Oct 14, 2025
c050228
Handle case where didClose or registerLanguage races with parser update.
toinehartman Oct 15, 2025
f9b8582
Fixes from review by @DavyLandman.
toinehartman Oct 15, 2025
4781d0c
Associate empty contributions with extension.
toinehartman Oct 15, 2025
27a6410
Throw exceptions on methods that are not intended to be called.
toinehartman Oct 15, 2025
756bb02
Merge remote-tracking branch 'origin/main' into fix/795-open-register…
toinehartman Oct 15, 2025
e20ddde
Refactor and fix SQ warnings.
toinehartman Oct 15, 2025
0e1ac0d
Revert "Throw exceptions on methods that are not intended to be called."
toinehartman Oct 15, 2025
6e9be85
Do not wrap exceptions in futures.
toinehartman Oct 15, 2025
61108eb
Merge remote-tracking branch 'origin/main' into fix/795-open-register…
toinehartman Oct 20, 2025
eb1f73b
Make sure to always complete tree future.
toinehartman Oct 20, 2025
93bd8ad
Revert changes to state computation.
toinehartman Oct 20, 2025
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 @@ -60,7 +60,7 @@ public class TextDocumentState {
private static final Logger logger = LogManager.getLogger(TextDocumentState.class);
private static final ParseErrorRecovery RECOVERY = new ParseErrorRecovery(IRascalValueFactory.getInstance());

private final BiFunction<ISourceLocation, String, CompletableFuture<ITree>> parser;
private BiFunction<ISourceLocation, String, CompletableFuture<ITree>> parser;
private final ISourceLocation location;

private final AtomicReference<Versioned<Update>> current;
Expand All @@ -81,6 +81,11 @@ public TextDocumentState(
this.last = new AtomicReference<>();
}

public void setParser(BiFunction<ISourceLocation, String, CompletableFuture<ITree>> parser) {
this.parser = parser;
this.unpackCurrent().parse();
}

public ISourceLocation getLocation() {
return location;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@

import java.io.IOException;
import java.io.Reader;
import java.net.URI;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -316,7 +317,7 @@ public void didDeleteFiles(DeleteFilesParams params) {
ownExecuter.submit(() -> {
// if a file is deleted, and we were tracking it, we remove our diagnostics
for (var f : params.getFiles()) {
if (registeredExtensions.containsKey(extension(URIUtil.assumeCorrectLocation(f.getUri())))) {
if (isLanguageRegistered(URIUtil.assumeCorrectLocation(f.getUri()))) {
client.publishDiagnostics(new PublishDiagnosticsParams(f.getUri(), List.of()));
}
}
Expand All @@ -327,12 +328,19 @@ public void didDeleteFiles(DeleteFilesParams params) {
private void triggerAnalyzer(TextDocumentItem doc, Duration delay) {
triggerAnalyzer(new VersionedTextDocumentIdentifier(doc.getUri(), doc.getVersion()), delay);
}

private void triggerAnalyzer(VersionedTextDocumentIdentifier doc, Duration delay) {
logger.trace("Triggering analyzer for {}", doc.getUri());
var location = Locations.toLoc(doc);
triggerAnalyzer(location, doc.getVersion(), delay);
}

private void triggerAnalyzer(ISourceLocation location, int version, Duration delay) {
logger.trace("Triggering analyzer for {}", location);
var fileFacts = facts(location);
fileFacts.invalidateAnalyzer(location);
fileFacts.calculateAnalyzer(location, getFile(location).getCurrentTreeAsync(true), doc.getVersion(), delay);
if (isLanguageRegistered(location)) {
fileFacts.invalidateAnalyzer(location);
fileFacts.calculateAnalyzer(location, getFile(location).getCurrentTreeAsync(true), version, delay);
}
}

private void triggerBuilder(TextDocumentIdentifier doc) {
Expand All @@ -343,12 +351,11 @@ private void triggerBuilder(TextDocumentIdentifier doc) {
fileFacts.calculateBuilder(location, getFile(location).getCurrentTreeAsync(true));
}

private TextDocumentState updateContents(VersionedTextDocumentIdentifier doc, String newContents, long timestamp) {
TextDocumentState file = getFile(Locations.toLoc(doc));
private void updateContents(VersionedTextDocumentIdentifier doc, String newContents, long timestamp) {
logger.trace("New contents for {}", doc);
TextDocumentState file = getFile(Locations.toLoc(doc));
columns.clear(file.getLocation());
handleParsingErrors(file, file.update(doc.getVersion(), newContents, timestamp));
return file;
}

private void handleParsingErrors(TextDocumentState file, CompletableFuture<Versioned<List<Diagnostics.Template>>> diagnosticsAsync) {
Expand Down Expand Up @@ -522,8 +529,9 @@ public void didRenameFiles(RenameFilesParams params, List<WorkspaceFolder> works
private Map<ILanguageContributions, List<FileRename>> bundleRenamesByContribution(List<FileRename> allRenames) {
Map<ILanguageContributions, List<FileRename>> bundled = new HashMap<>();
for (FileRename rename : allRenames) {
String language = registeredExtensions.get(extension(URIUtil.assumeCorrectLocation(rename.getNewUri())));
if (language != null) {
var l = URIUtil.assumeCorrectLocation(rename.getNewUri());
if (isLanguageRegistered(l)) {
var language = language(l);
ILanguageContributions contrib = contributions.get(language);
if (contrib != null) {
bundled.computeIfAbsent(contrib, k -> new ArrayList<>()).add(rename);
Expand Down Expand Up @@ -597,38 +605,53 @@ private static <T> T last(List<T> l) {
return l.get(l.size() - 1);
}

private boolean isLanguageRegistered(ISourceLocation loc) {
return registeredExtensions.containsKey(extension(loc));
}

private String language(ISourceLocation loc) {
var ext = extension(loc);
var language = registeredExtensions.get(ext);
if (language == null) {
throw new UnsupportedOperationException(String.format("Rascal Parametric LSP has no support for this file, since no language is registered for extension '%s': %s", ext, loc));
}
return language;
}

private ILanguageContributions contributions(ISourceLocation doc) {
String language = registeredExtensions.get(extension(doc));
if (language != null) {
ILanguageContributions contrib = contributions.get(language);
ILanguageContributions contrib = contributions.get(language(doc));

if (contrib != null) {
return contrib;
}
if (contrib == null) {
throw new UnsupportedOperationException("Rascal Parametric LSP has no support for this file: " + doc);
}

throw new UnsupportedOperationException("Rascal Parametric LSP has no support for this file: " + doc);
return contrib;
}

private static String extension(ISourceLocation doc) {
return URIUtil.getExtension(doc);
}

private ParametricFileFacts facts(ISourceLocation doc) {
String language = registeredExtensions.get(extension(doc));
if (language != null) {
ParametricFileFacts fact = facts.get(language);
if (fact != null) {
return fact;
}
ParametricFileFacts fact = facts.get(language(doc));

if (fact == null) {
throw new UnsupportedOperationException("Rascal Parametric LSP has no support for this file: " + doc);
}
throw new UnsupportedOperationException("Rascal Parametric LSP has no support for this file: " + doc);

return fact;
}

private TextDocumentState open(TextDocumentItem doc, long timestamp) {
return files.computeIfAbsent(Locations.toLoc(doc),
l -> new TextDocumentState(contributions(l)::parsing, l, doc.getVersion(), doc.getText(), timestamp)
);
var loc = Locations.toLoc(doc);
BiFunction<ISourceLocation, String, CompletableFuture<ITree>> parser
= isLanguageRegistered(loc)
? contributions(loc)::parsing
// Language not (yet) registered; plug in an incomplete parse future
: (l, s) -> new CompletableFuture<>();

return files.computeIfAbsent(loc,
l -> new TextDocumentState(parser, l, doc.getVersion(), doc.getText(), timestamp));
}

private TextDocumentState getFile(ISourceLocation loc) {
Expand Down Expand Up @@ -851,6 +874,20 @@ public synchronized void registerLanguage(LanguageParameter lang) {
if (client != null) {
fact.setClient(client);
}

// If we opened any files with this extension before, now associate them with contributions
var extensions = Arrays.asList(lang.getExtensions());
for (var e : files.entrySet()) {
var f = e.getKey();
var state = e.getValue();
if (extensions.contains(extension(e.getKey()))) {
logger.trace("Open file of language {} - updating state: {}", lang.getName(), f);
state.setParser(contributions(f)::parsing);
// Update open editor
triggerAnalyzer(f, state.getCurrentContent().version(), NORMAL_DEBOUNCE);
handleParsingErrors(state, state.getCurrentDiagnosticsAsync());
}
}
}

private static String buildContributionKey(LanguageParameter lang) {
Expand Down
Loading