Skip to content

Commit

Permalink
Improve completions to suggest available grammars and elements within…
Browse files Browse the repository at this point in the history
… those grammars (#129)

* Improve completions to suggest available grammars and elements within those grammars

* Simplify text interval include implementation

Co-authored-by: Kevin Knight <57677197+kevin-m-knight-gs@users.noreply.github.com>

* Rename method to get line up to length

* Revert unnecessary change

* Rename variable to better align to purpose

---------

Co-authored-by: Kevin Knight <57677197+kevin-m-knight-gs@users.noreply.github.com>
  • Loading branch information
rafaelbey and kevin-m-knight-gs authored Jan 16, 2024
1 parent d5e105c commit b7a2ada
Show file tree
Hide file tree
Showing 22 changed files with 394 additions and 85 deletions.
5 changes: 5 additions & 0 deletions legend-engine-ide-lsp-default-extensions/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
<scope>import</scope>
<version>${legend.engine.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.factory.Maps;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.impl.block.function.checked.ThrowingFunction;
import org.eclipse.collections.impl.utility.Iterate;
import org.eclipse.collections.impl.utility.ListIterate;
import org.finos.legend.engine.ide.lsp.extension.completion.LegendCompletion;
import org.finos.legend.engine.ide.lsp.extension.declaration.LegendDeclaration;
import org.finos.legend.engine.ide.lsp.extension.diagnostic.LegendDiagnostic;
import org.finos.legend.engine.ide.lsp.extension.execution.LegendCommand;
Expand All @@ -44,6 +48,7 @@
import org.finos.legend.engine.ide.lsp.extension.state.SectionState;
import org.finos.legend.engine.ide.lsp.extension.text.GrammarSection;
import org.finos.legend.engine.ide.lsp.extension.text.TextInterval;
import org.finos.legend.engine.ide.lsp.extension.text.TextPosition;
import org.finos.legend.engine.language.pure.compiler.Compiler;
import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
import org.finos.legend.engine.language.pure.compiler.toPureGraph.Warning;
Expand Down Expand Up @@ -388,7 +393,7 @@ protected ParseResult getParseResult(SectionState sectionState)
return sectionState.getProperty(PARSE_RESULT, () -> tryParse(sectionState));
}

protected ParseResult tryParse(SectionState sectionState)
private ParseResult tryParse(SectionState sectionState)
{
MutableList<PackageableElement> elements = Lists.mutable.empty();
try
Expand Down Expand Up @@ -627,6 +632,21 @@ protected static void addChildIfNonNull(LegendDeclaration.Builder builder, Legen
}
}

protected Optional<PackageableElement> getElementAtPosition(SectionState sectionState, TextPosition position)
{
ParseResult parseResult = this.getParseResult(sectionState);
return parseResult
.getElements()
.stream()
.filter(x -> this.isPositionIncludedOnSourceInfo(position, x.sourceInformation))
.findAny();
}

private boolean isPositionIncludedOnSourceInfo(TextPosition position, SourceInformation sourceInformation)
{
return isValidSourceInfo(sourceInformation) && toLocation(sourceInformation).includes(position);
}

/**
* Check if the source information is valid.
*
Expand All @@ -653,6 +673,22 @@ protected static TextInterval toLocation(SourceInformation sourceInfo)
return TextInterval.newInterval(sourceInfo.startLine - 1, sourceInfo.startColumn - 1, sourceInfo.endLine - 1, sourceInfo.endColumn - 1);
}

protected Iterable<LegendCompletion> computeCompletionsForSupportedTypes(SectionState section, TextPosition location, Set<String> supportedTypes)
{
String text = section.getSection().getLineUpTo(location);

if (text.isEmpty())
{
return supportedTypes.stream().map(x -> new LegendCompletion("New " + x, x)).collect(Collectors.toList());
}
else if (this.getElementAtPosition(section, location).isEmpty())
{
return supportedTypes.stream().filter(x -> x.startsWith(text)).map(x -> new LegendCompletion("New " + x, x)).collect(Collectors.toList());
}

return List.of();
}

private static LegendEngineServerClient newEngineServerClient()
{
for (LegendEngineServerClient client : ServiceLoader.load(LegendEngineServerClient.class))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,40 @@

package org.finos.legend.engine.ide.lsp.extension;

import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.Vocabulary;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.eclipse.collections.impl.utility.Iterate;
import org.eclipse.collections.impl.utility.ListIterate;
import org.finos.legend.engine.ide.lsp.extension.completion.LegendCompletion;
import org.finos.legend.engine.ide.lsp.extension.state.SectionState;
import org.finos.legend.engine.ide.lsp.extension.text.TextPosition;
import org.finos.legend.engine.language.pure.grammar.from.ParseTreeWalkerSourceInformation;
import org.finos.legend.engine.language.pure.grammar.from.PureGrammarParserContext;
import org.finos.legend.engine.language.pure.grammar.from.PureGrammarParserUtility;
import org.finos.legend.engine.language.pure.grammar.from.SectionSourceCode;
import org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtension;
import org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtensions;
import org.finos.legend.engine.language.pure.grammar.from.extension.SectionParser;
import org.finos.legend.engine.protocol.pure.v1.model.SourceInformation;
import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.PackageableElement;

import java.util.function.Consumer;
import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException;

abstract class AbstractSectionParserLSPGrammarExtension extends AbstractLSPGrammarExtension
{
protected final SectionParser parser;
private final Set<String> grammarSupportedTypes;

protected AbstractSectionParserLSPGrammarExtension(SectionParser parser)
{
this.parser = parser;
this.grammarSupportedTypes = this.getAntlrExpectedTokens();
}

protected AbstractSectionParserLSPGrammarExtension(String parserName, PureGrammarParserExtension extension)
Expand Down Expand Up @@ -58,4 +76,80 @@ private static SectionParser findSectionParser(String name, PureGrammarParserExt
}
return parser;
}

/**
* The default implementation set the types supported by the grammar as keywords
*
* @return Keywords on this section parser
*/
@Override
public Iterable<? extends String> getKeywords()
{
return this.grammarSupportedTypes;
}

/**
* Implementation that defaults completion suggestions to grammar supported types
*
* @param section grammar section state where completion triggered
* @param location location where completion triggered
* @return Completion suggestion contextual to section and location
*/
@Override
public Iterable<? extends LegendCompletion> getCompletions(SectionState section, TextPosition location)
{
return this.computeCompletionsForSupportedTypes(section, location, this.grammarSupportedTypes);
}

protected Set<String> getAntlrExpectedTokens()
{
Set<String> expectedTokens = Collections.emptySet();

try
{
this.parse(
new SectionSourceCode(
"~bad~code~bad~code~",
this.parser.getSectionTypeName(),
SourceInformation.getUnknownSourceInformation(),
new ParseTreeWalkerSourceInformation.Builder("memory", 0, 0).build()
),
x ->
{
},
new PureGrammarParserContext(PureGrammarParserExtensions.fromAvailableExtensions())
);
}
catch (EngineException e)
{
if (e.getCause() instanceof RecognitionException)
{
RecognitionException re = (RecognitionException) e.getCause();
expectedTokens = getExpectedTokens(re);

}
}
catch (Exception e)
{
e.printStackTrace();
}

return expectedTokens;
}

private Set<String> getExpectedTokens(RecognitionException re)
{
IntervalSet expectedTokensIds = re.getExpectedTokens();
Optional<Vocabulary> vocabulary = Optional.ofNullable(re.getRecognizer())
.map(Recognizer::getVocabulary);
return Optional.ofNullable(expectedTokensIds)
.map(IntervalSet::toList)
.<Set<String>>flatMap(x -> vocabulary.map(v -> ListIterate
.collect(x, v::getLiteralName)
.select(Objects::nonNull)
.collect(t -> PureGrammarParserUtility.fromGrammarString(t, true))
.toSet()
))
.orElse(Collections.emptySet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import org.eclipse.collections.api.factory.Sets;
import org.eclipse.collections.api.list.ListIterable;
import org.eclipse.collections.api.set.MutableSet;
import org.finos.legend.engine.ide.lsp.extension.completion.LegendCompletion;
import org.finos.legend.engine.ide.lsp.extension.execution.LegendExecutionResult;
import org.finos.legend.engine.ide.lsp.extension.execution.LegendExecutionResult.Type;
import org.finos.legend.engine.ide.lsp.extension.state.SectionState;
import org.finos.legend.engine.ide.lsp.extension.text.TextPosition;
import org.finos.legend.engine.language.pure.grammar.from.connection.ConnectionParser;
import org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtensionLoader;
import org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtensions;
Expand Down Expand Up @@ -77,6 +79,12 @@ public Iterable<? extends LegendExecutionResult> execute(SectionState section, S
super.execute(section, entityPath, commandId, executableArgs);
}

@Override
public Iterable<? extends LegendCompletion> getCompletions(SectionState section, TextPosition location)
{
return this.computeCompletionsForSupportedTypes(section, location, this.keywords.toSet());
}

private Iterable<? extends LegendExecutionResult> generateDBFromConnection(SectionState section, String entityPath)
{
if (!isEngineServerConfigured())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@

package org.finos.legend.engine.ide.lsp.extension;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.factory.Sets;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.ListIterable;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.impl.lazy.CompositeIterable;
import org.eclipse.collections.impl.utility.Iterate;
import org.eclipse.collections.impl.utility.ListIterate;
import org.finos.legend.engine.ide.lsp.extension.completion.LegendCompletion;
Expand All @@ -44,13 +52,6 @@
import org.finos.legend.engine.test.runner.mapping.RichMappingTestResult;
import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;

/**
* Extension for the Mapping grammar.
*/
Expand Down Expand Up @@ -206,7 +207,7 @@ private Iterable<? extends LegendExecutionResult> runLegacyMappingTests(SectionS
result.getException().printStackTrace(pw);
}
}
results.add(LegendExecutionResult.newResult(Lists.mutable.of(entityPath, test.name),Type.ERROR, writer.toString()));
results.add(LegendExecutionResult.newResult(Lists.mutable.of(entityPath, test.name), Type.ERROR, writer.toString()));
break;
}
default:
Expand Down Expand Up @@ -247,19 +248,18 @@ private static ListIterable<String> findKeywords()
@Override
public Iterable<? extends LegendCompletion> getCompletions(SectionState section, TextPosition location)
{
String codeLine = section.getSection().getLine(location.getLine()).substring(0, location.getColumn());
String codeLine = section.getSection().getLineUpTo(location);
List<LegendCompletion> legendCompletions = Lists.mutable.empty();

if (codeLine.isEmpty())
{
return BOILERPLATE_SUGGESTIONS.collect(s -> new LegendCompletion("Mapping boilerplate", s.replaceAll("\n",System.lineSeparator())));
BOILERPLATE_SUGGESTIONS.collect(s -> new LegendCompletion("Mapping boilerplate", s.replaceAll("\n", System.lineSeparator())), legendCompletions);
}

if (STORE_OBJECT_TRIGGERS.anySatisfy(codeLine::endsWith))
else if (STORE_OBJECT_TRIGGERS.anySatisfy(codeLine::endsWith))
{
STORE_OBJECT_SUGGESTIONS.collect(s -> new LegendCompletion("Store object type", s), legendCompletions);
}

return legendCompletions;
return CompositeIterable.with(legendCompletions, this.computeCompletionsForSupportedTypes(section, location, Set.of("Mapping")));
}
}
Loading

0 comments on commit b7a2ada

Please sign in to comment.