Skip to content

Commit

Permalink
Feature: multiple extensions for single language (#344)
Browse files Browse the repository at this point in the history
fixes #343
  • Loading branch information
DavyLandman authored Dec 12, 2023
1 parent 00e43f0 commit 7c21c26
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) {
@Override
public CompletableFuture<Object> executeCommand(ExecuteCommandParams params) {
if (params.getCommand().startsWith(RASCAL_META_COMMAND)) {
String extension = ((JsonPrimitive) params.getArguments().get(0)).getAsString();
String languageName = ((JsonPrimitive) params.getArguments().get(0)).getAsString();
String command = ((JsonPrimitive) params.getArguments().get(1)).getAsString();
return documentService.executeCommand(extension, command).thenApply(v -> v);
return documentService.executeCommand(languageName, command).thenApply(v -> v);
}
return CompletableFuture.supplyAsync(() -> params.getCommand() + " was ignored.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ public interface IBaseTextDocumentService extends TextDocumentService {
void pair(BaseWorkspaceService workspaceService);
void registerLanguage(LanguageParameter lang);
void unregisterLanguage(LanguageParameter lang);
CompletableFuture<IValue> executeCommand(String extension, String command);
CompletableFuture<IValue> executeCommand(String languageName, String command);
LineColumnOffsetMap getColumnMap(ISourceLocation file);
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,30 +140,12 @@ private ISourceLocation buildProjectChildLoc(WorkspaceFolder folder, ISourceLoca

@Override
public void registerLanguage(IConstructor language) {
LanguageParameter param = new LanguageParameter(
language.get(0).toString(),
((IString) language.get(1)).getValue(),
((IString) language.get(2)).getValue(),
((IString) language.get(3)).getValue(),
((IString) language.get(4)).getValue(),
null
);

languageClient.receiveRegisterLanguage(param);
languageClient.receiveRegisterLanguage(LanguageParameter.fromRascalValue(language));
}

@Override
public void unregisterLanguage(IConstructor language) {
LanguageParameter param = new LanguageParameter(
language.get(0).toString(),
((IString) language.get(1)).getValue(),
((IString) language.get(2)).getValue(),
((IString) language.get(3)).getValue(),
((IString) language.get(4)).getValue(),
null
);

languageClient.receiveUnregisterLanguage(param);
languageClient.receiveUnregisterLanguage(LanguageParameter.fromRascalValue(language));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@

public interface ILanguageContributions {
public String getName();
public String getExtension();
public CompletableFuture<ITree> parseSourceFile(ISourceLocation loc, String input);
public InterruptibleFuture<IList> outline(ITree input);
public InterruptibleFuture<IConstructor> summarize(ISourceLocation loc, ITree input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
Expand All @@ -54,7 +53,6 @@
import org.rascalmpl.vscode.lsp.terminal.ITerminalIDEServer.LanguageParameter;
import org.rascalmpl.vscode.lsp.util.EvaluatorUtil;
import org.rascalmpl.vscode.lsp.util.concurrent.InterruptibleFuture;

import io.usethesource.vallang.IBool;
import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.IList;
Expand All @@ -73,7 +71,6 @@ public class InterpretedLanguageContributions implements ILanguageContributions
private final ExecutorService exec;

private final String name;
private final String extension;
private final String mainModule;

private final CompletableFuture<Evaluator> eval;
Expand Down Expand Up @@ -178,7 +175,6 @@ public void warning(String message, ISourceLocation src) {
public InterpretedLanguageContributions(LanguageParameter lang, IBaseTextDocumentService docService, BaseWorkspaceService workspaceService, IBaseLanguageClient client, ExecutorService exec) {
this.name = lang.getName();
this.mainModule = lang.getMainModule();
extension = lang.getExtension();
this.exec = exec;

try {
Expand Down Expand Up @@ -281,11 +277,6 @@ public String getName() {
return name;
}

@Override
public String getExtension() {
return extension;
}

@Override
public CompletableFuture<ITree> parseSourceFile(ISourceLocation loc, String input) {
return parser.thenApplyAsync(p -> p.call(VF.string(input), loc), exec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public class LanguageContributionsMultiplexer implements ILanguageContributions

private final ExecutorService ownExecuter;
private final String name;
private final String extension;

private static final <T> CompletableFuture<T> failedInitialization() {
return CompletableFuture.failedFuture(new RuntimeException("No contributions registered"));
Expand Down Expand Up @@ -77,9 +76,8 @@ private static final <T> CompletableFuture<T> failedInitialization() {
private volatile CompletableFuture<Boolean> askSummaryForReferences = failedInitialization();
private volatile CompletableFuture<Boolean> askSummaryForImplementations = failedInitialization();

public LanguageContributionsMultiplexer(String name, String extension, ExecutorService ownService) {
public LanguageContributionsMultiplexer(String name, ExecutorService ownService) {
this.name = name;
this.extension = extension;
this.ownExecuter = ownService;
}

Expand Down Expand Up @@ -210,12 +208,6 @@ public String getName() {
return name;
}

@Override
public String getExtension() {
return extension;
}


@Override
public CompletableFuture<ITree> parseSourceFile(ISourceLocation loc, String input) {
var p = parser;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,11 @@ public class ParametricTextDocumentService implements IBaseTextDocumentService,
private final Map<ISourceLocation, TextDocumentState> files;
private final ColumnMaps columns;

/** extension to language */
private final Map<String, String> registeredExtensions = new ConcurrentHashMap<>();
/** language to facts */
private final Map<String, ParametricFileFacts> facts = new ConcurrentHashMap<>();
/** language to contribution */
private final Map<String, LanguageContributionsMultiplexer> contributions = new ConcurrentHashMap<>();

private final @Nullable LanguageParameter dedicatedLanguage;
Expand Down Expand Up @@ -291,7 +295,7 @@ public CompletableFuture<List<? extends CodeLens>> codeLens(CodeLensParams param
.thenApply(contrib::lenses)
.thenCompose(InterruptibleFuture::get)
.thenApply(s -> s.stream()
.map(e -> locCommandTupleToCodeLense(contrib.getExtension(), e))
.map(e -> locCommandTupleToCodeLense(contrib.getName(), e))
.collect(Collectors.toList())
), () -> null);
}
Expand Down Expand Up @@ -341,18 +345,18 @@ private InlayHint rowToInlayHint(IValue v) {
return result;
}

private CodeLens locCommandTupleToCodeLense(String extension, IValue v) {
private CodeLens locCommandTupleToCodeLense(String languageName, IValue v) {
ITuple t = (ITuple) v;
ISourceLocation loc = (ISourceLocation) t.get(0);
IConstructor command = (IConstructor) t.get(1);

return new CodeLens(Locations.toRange(loc, columns), constructorToCommand(extension, command), null);
return new CodeLens(Locations.toRange(loc, columns), constructorToCommand(languageName, command), null);
}

private Command constructorToCommand(String extension, IConstructor command) {
private Command constructorToCommand(String languageName, IConstructor command) {
IWithKeywordParameters<?> kw = command.asWithKeywordParameters();

return new Command(kw.hasParameter("title") ? ((IString) kw.getParameter("title")).getValue() : command.toString(), getRascalMetaCommandName(), Arrays.asList(extension, command.toString()));
return new Command(kw.hasParameter("title") ? ((IString) kw.getParameter("title")).getValue() : command.toString(), getRascalMetaCommandName(), Arrays.asList(languageName, command.toString()));
}

private void handleParsingErrors(TextDocumentState file) {
Expand All @@ -372,10 +376,13 @@ private ILanguageContributions contributions(TextDocumentItem doc) {
}

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

if (contrib != null) {
return contrib;
if (contrib != null) {
return contrib;
}
}

throw new UnsupportedOperationException("Rascal Parametric LSP has no support for this file: " + doc);
Expand All @@ -398,9 +405,12 @@ private ParametricFileFacts facts(ISourceLocation doc) {
}

private ParametricFileFacts facts(String doc) {
ParametricFileFacts fact = facts.get(extension(doc));
if (fact != null) {
return fact;
String language = registeredExtensions.get(extension(doc));
if (language != null) {
ParametricFileFacts fact = facts.get(language);
if (fact != null) {
return fact;
}
}
throw new UnsupportedOperationException("Rascal Parametric LSP has no support for this file: " + doc);
}
Expand Down Expand Up @@ -527,19 +537,23 @@ public CompletableFuture<List<FoldingRange>> foldingRange(FoldingRangeRequestPar
public void registerLanguage(LanguageParameter lang) {
logger.info("registerLanguage({})", lang.getName());

for (var extension: lang.getExtensions()) {
this.registeredExtensions.put(extension, lang.getName());
}

var multiplexer = contributions.computeIfAbsent(lang.getExtension(),
t -> new LanguageContributionsMultiplexer(lang.getName(), lang.getExtension(), ownExecuter)
var multiplexer = contributions.computeIfAbsent(lang.getName(),
t -> new LanguageContributionsMultiplexer(lang.getName(), ownExecuter)
);
var fact = facts.computeIfAbsent(lang.getExtension(), t ->
var fact = facts.computeIfAbsent(lang.getName(), t ->
new ParametricFileFacts(multiplexer, this::getFile, columns, ownExecuter)
);

if (lang.getPrecompiledParser() != null) {
try {
var location = lang.getPrecompiledParser().getParserLocation();
if (URIResolverRegistry.getInstance().exists(location)) {
logger.debug("Got precompiled definition: {}", lang.getPrecompiledParser());
multiplexer.addContributor(buildContributionKey(lang) + "$parser", new ParserOnlyContribution(lang.getName(), lang.getExtension(), lang.getPrecompiledParser()));
multiplexer.addContributor(buildContributionKey(lang) + "$parser", new ParserOnlyContribution(lang.getName(), lang.getPrecompiledParser()));
}
else {
logger.error("Defined precompiled parser ({}) does not exist", lang.getPrecompiledParser());
Expand All @@ -565,34 +579,38 @@ private static String buildContributionKey(LanguageParameter lang) {

@Override
public void unregisterLanguage(LanguageParameter lang) {
var extension = lang.getExtension();
if (lang.getMainModule() == null || lang.getMainModule().isEmpty()) {
// clear the whole language
logger.trace("unregisterLanguage({}) completly", lang.getName());
facts.remove(extension);
contributions.remove(extension);
return;
}
logger.trace("unregisterLanguage({}) only {}", lang.getName(), lang.getMainModule());
if (!contributions.get(extension).removeContributor(buildContributionKey(lang))) {
logger.error("unregisterLanguage cleared everything, so removing all");
facts.remove(extension);
contributions.remove(extension);
boolean removeAll = lang.getMainModule() == null || lang.getMainModule().isEmpty();
if (!removeAll) {
if (!contributions.get(lang.getName()).removeContributor(buildContributionKey(lang))) {
logger.error("unregisterLanguage cleared everything, so removing all");
// ok, so it was a clear after all
removeAll = true;
}
else {
facts.get(lang.getName()).reloadContributions();
}
}
else {
facts.get(extension).reloadContributions();
if (removeAll) {
// clear the whole language
logger.trace("unregisterLanguage({}) completely", lang.getName());

for (var extension : lang.getExtensions()) {
this.registeredExtensions.remove(extension);
}
facts.remove(lang.getName());
contributions.remove(lang.getName());
}
}

@Override
public CompletableFuture<IValue> executeCommand(String extension, String command) {
ILanguageContributions contribs = contributions.get(extension);
public CompletableFuture<IValue> executeCommand(String languageName, String command) {
ILanguageContributions contribs = contributions.get(languageName);

if (contribs != null) {
return contribs.executeCommand(command).get();
}
else {
logger.warn("ignoring command execution (no contributor configured for this extension): {}, {} ", extension, command);
logger.warn("ignoring command execution (no contributor configured for this language): {}, {} ", languageName, command);
return CompletableFuture.completedFuture(null);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,11 @@ public class ParserOnlyContribution implements ILanguageContributions {
private static final Logger logger = LogManager.getLogger(ParserOnlyContribution.class);
private static final IValueFactory VF = IRascalValueFactory.getInstance();
private final String name;
private final String extension;
private final @Nullable Exception loadingParserError;
private final @Nullable IFunction parser;

public ParserOnlyContribution(String name, String extension, ParserSpecification spec) {
public ParserOnlyContribution(String name, ParserSpecification spec) {
this.name = name;
this.extension = extension;

// we use an entry and a single initialization function to make sure that parser and loadingParserError can be `final`:
Either<IFunction,Exception> result = loadParser(spec);
Expand All @@ -76,11 +74,6 @@ public String getName() {
return name;
}

@Override
public String getExtension() {
return extension;
}

@Override
public CompletableFuture<ITree> parseSourceFile(ISourceLocation loc, String input) {
if (loadingParserError != null || parser == null) {
Expand Down
Loading

0 comments on commit 7c21c26

Please sign in to comment.