Skip to content

Commit

Permalink
Драфт внешнего вычислителя типов
Browse files Browse the repository at this point in the history
  • Loading branch information
nixel2007 committed Apr 24, 2024
1 parent a1604eb commit d789d6f
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration;
import com.github._1c_syntax.bsl.languageserver.context.symbol.VariableSymbol;
import com.github._1c_syntax.bsl.languageserver.context.symbol.variable.VariableDescription;
import com.github._1c_syntax.bsl.languageserver.types.Type;
import com.github._1c_syntax.bsl.languageserver.types.TypeResolver;
import com.github._1c_syntax.bsl.languageserver.utils.MdoRefBuilder;
import com.github._1c_syntax.bsl.languageserver.utils.Resources;
import lombok.RequiredArgsConstructor;
Expand All @@ -32,7 +34,9 @@
import org.eclipse.lsp4j.SymbolKind;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
Expand All @@ -41,6 +45,7 @@ public class VariableSymbolMarkupContentBuilder implements MarkupContentBuilder<
private static final String VARIABLE_KEY = "var";
private static final String EXPORT_KEY = "export";

private final TypeResolver typeResolver;
private final LanguageServerConfiguration configuration;

@Override
Expand Down Expand Up @@ -69,6 +74,10 @@ public MarkupContent getContent(VariableSymbol symbol) {
.map(VariableDescription::getPurposeDescription)
.ifPresent(trailingDescription -> addSectionIfNotEmpty(markupBuilder, trailingDescription));

var types = typeResolver.findTypes(symbol);
var typeDescription = getTypeDescription(types);
addSectionIfNotEmpty(markupBuilder, typeDescription);

String content = markupBuilder.toString();

return new MarkupContent(MarkupKind.MARKDOWN, content);
Expand Down Expand Up @@ -112,6 +121,18 @@ private static String getLocation(VariableSymbol symbol) {
);
}

private static String getTypeDescription(List<Type> types) {
var typeDescription = types.stream()
.map(Type::getName)
.collect(Collectors.joining(" | "));

if (!typeDescription.isEmpty()) {
typeDescription = "`" + typeDescription + "`";
}

return typeDescription;
}

private static void addSectionIfNotEmpty(StringJoiner markupBuilder, String newContent) {
if (!newContent.isEmpty()) {
markupBuilder.add(newContent);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* This file is a part of BSL Language Server.
*
* Copyright (c) 2018-2024
* Alexey Sosnoviy <[email protected]>, Nikita Fedkin <[email protected]> and contributors
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* BSL Language Server is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* BSL Language Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BSL Language Server.
*/
package com.github._1c_syntax.bsl.languageserver.types;

import lombok.Value;

@Value
public class Type {
private final String name;

Check warning on line 28 in src/main/java/com/github/_1c_syntax/bsl/languageserver/types/Type.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

@Value modifiers

@value already marks non-static fields final.

Check warning on line 28 in src/main/java/com/github/_1c_syntax/bsl/languageserver/types/Type.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

@Value modifiers

@value already marks non-static, package-local fields private.

Check warning

Code scanning / QDJVMC

@Value modifiers Warning

@Value already marks non-static fields final.

Check warning

Code scanning / QDJVMC

@Value modifiers Warning

@Value already marks non-static, package-local fields private.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* This file is a part of BSL Language Server.
*
* Copyright (c) 2018-2024
* Alexey Sosnoviy <[email protected]>, Nikita Fedkin <[email protected]> and contributors
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* BSL Language Server is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* BSL Language Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BSL Language Server.
*/
package com.github._1c_syntax.bsl.languageserver.types;

import com.github._1c_syntax.bsl.languageserver.context.ServerContext;
import com.github._1c_syntax.bsl.languageserver.context.symbol.Describable;
import com.github._1c_syntax.bsl.languageserver.context.symbol.SourceDefinedSymbol;
import com.github._1c_syntax.bsl.languageserver.context.symbol.variable.VariableDescription;
import com.github._1c_syntax.bsl.languageserver.references.ReferenceIndex;
import com.github._1c_syntax.bsl.languageserver.references.ReferenceResolver;
import com.github._1c_syntax.bsl.languageserver.references.model.OccurrenceType;
import com.github._1c_syntax.bsl.languageserver.references.model.Reference;
import com.github._1c_syntax.bsl.languageserver.utils.Trees;
import com.github._1c_syntax.bsl.languageserver.utils.bsl.Constructors;
import com.github._1c_syntax.bsl.parser.BSLParser;
import lombok.RequiredArgsConstructor;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.eclipse.lsp4j.Position;
import org.springframework.stereotype.Component;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

@Component
@RequiredArgsConstructor
public class TypeResolver {

private final ServerContext serverContext;
private final ReferenceResolver referenceResolver;
private final ReferenceIndex referenceIndex;

// TODO: Create LRU cache for calculated types.

// TODO: Use reference instead of symbol. Refactor hover provider to pass references to markup content builders.
public List<Type> findTypes(SourceDefinedSymbol symbol) {
return calculateTypes(symbol);
}

public List<Type> findTypes(URI uri, Position position) {
return referenceResolver.findReference(uri, position)
.stream()
.flatMap(reference -> calculateTypes(uri, reference).stream())
.distinct()
.toList();
}

private List<Type> calculateTypes(SourceDefinedSymbol symbol) {

// variable description resolver
if (symbol instanceof Describable describableSymbol) {
var maybeDescription = describableSymbol.getDescription();
if (maybeDescription.isPresent()) {
var description = maybeDescription.get();
if (description instanceof VariableDescription variableDescription) {

Check warning on line 76 in src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolver.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Statement with empty body

`if` statement has empty body

Check warning

Code scanning / QDJVMC

Statement with empty body Warning

if statement has empty body
// TODO: extract types from type description and return.
}
}
}

// reference-based type resolver
var ast = symbol.getOwner().getAst();
var position = symbol.getSelectionRange().getStart();

var typesOfCurrentReference = calculateTypes(ast, position);

var typesOfOtherReferences = referenceIndex.getReferencesTo(symbol).stream()
.filter(referenceTo -> referenceTo.getOccurrenceType() == OccurrenceType.DEFINITION)
.map(referenceTo -> calculateTypes(ast, referenceTo.getSelectionRange().getStart()))
.flatMap(Collection::stream)
.toList();

return Stream.concat(typesOfCurrentReference.stream(), typesOfOtherReferences.stream())
.distinct()
.toList();
}

private List<Type> calculateTypes(URI uri, Reference reference) {

// source defined symbol resolver
if (reference.isSourceDefinedSymbolReference()) {
return calculateTypes(reference.getSourceDefinedSymbol().orElseThrow());
}

// expression tree resolver
if (reference.getOccurrenceType() == OccurrenceType.DEFINITION) {
var document = serverContext.getDocument(uri);
var ast = document.getAst();

Check warning on line 109 in src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolver.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Nullability and data flow problems

Method invocation `getAst` may produce `NullPointerException`

Check warning

Code scanning / QDJVMC

Nullability and data flow problems Warning

Method invocation getAst may produce NullPointerException
var position = reference.getSelectionRange().getStart();
return calculateTypes(ast, position);
}

// no-op
return Collections.emptyList();
}

private List<Type> calculateTypes(BSLParser.FileContext ast, Position position) {
return Trees.findTerminalNodeContainsPosition(ast, position)
.map(TerminalNode::getParent)
.map(ruleNode -> Trees.getRootParent(ruleNode, BSLParser.RULE_assignment))
.map(BSLParser.AssignmentContext.class::cast)
.map(BSLParser.AssignmentContext::expression)
.map(this::calculateTypes)
.orElseGet(Collections::emptyList);
}

private List<Type> calculateTypes(BSLParser.ExpressionContext expression) {

// only simple cases for now. Use ExpressionTree in the future.
if (!expression.operation().isEmpty()) {
return Collections.emptyList();
}

// new-resolver
var typeName = typeName(expression);
if (!typeName.isEmpty()) {
Type type = new Type(typeName);
return List.of(type);
}

// const-value resolver
var constValueContext = expression.member(0).constValue();
if (constValueContext == null) {
return Collections.emptyList();
}

Type type = null;
if (constValueContext.DATETIME() != null) {
type = new Type("Дата");
} else if (constValueContext.FALSE() != null || constValueContext.TRUE() != null) {
type = new Type("Булево");
} else if (constValueContext.NULL() != null) {
type = new Type("null");
} else if (constValueContext.numeric() != null) {
type = new Type("Число");
} else if (constValueContext.string() != null) {
type = new Type("Строка");
} else if (constValueContext.UNDEFINED() != null) {
type = new Type("Неопределено");
}

if (type != null) {
return List.of(type);
}

return Collections.emptyList();

}

private String typeName(BSLParser.ExpressionContext ctx) {
var typeName = "";
var newCtx = Trees.getNextNode(ctx, ctx, BSLParser.RULE_newExpression);
if (newCtx instanceof BSLParser.NewExpressionContext newExpression) {
typeName = Constructors.typeName(newExpression).orElse("");
}
return typeName;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Tree;
import org.eclipse.lsp4j.Position;
Expand Down Expand Up @@ -286,14 +287,14 @@ public static BSLParserRuleContext getRootParent(BSLParserRuleContext tnc) {
* @return tnc - если родитель не найден, вернет null
*/
@Nullable
public static BSLParserRuleContext getRootParent(BSLParserRuleContext tnc, int ruleindex) {
public static BSLParserRuleContext getRootParent(RuleNode tnc, int ruleindex) {
final var parent = tnc.getParent();
if (parent == null) {
return null;
}

if (getRuleIndex(parent) == ruleindex) {
return parent;
return (BSLParserRuleContext) parent;
} else {
return getRootParent(parent, ruleindex);
}
Expand Down
Loading

0 comments on commit d789d6f

Please sign in to comment.