Skip to content

Commit d789d6f

Browse files
committed
Драфт внешнего вычислителя типов
1 parent a1604eb commit d789d6f

File tree

6 files changed

+364
-2
lines changed

6 files changed

+364
-2
lines changed

src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/VariableSymbolMarkupContentBuilder.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration;
2525
import com.github._1c_syntax.bsl.languageserver.context.symbol.VariableSymbol;
2626
import com.github._1c_syntax.bsl.languageserver.context.symbol.variable.VariableDescription;
27+
import com.github._1c_syntax.bsl.languageserver.types.Type;
28+
import com.github._1c_syntax.bsl.languageserver.types.TypeResolver;
2729
import com.github._1c_syntax.bsl.languageserver.utils.MdoRefBuilder;
2830
import com.github._1c_syntax.bsl.languageserver.utils.Resources;
2931
import lombok.RequiredArgsConstructor;
@@ -32,7 +34,9 @@
3234
import org.eclipse.lsp4j.SymbolKind;
3335
import org.springframework.stereotype.Component;
3436

37+
import java.util.List;
3538
import java.util.StringJoiner;
39+
import java.util.stream.Collectors;
3640

3741
@Component
3842
@RequiredArgsConstructor
@@ -41,6 +45,7 @@ public class VariableSymbolMarkupContentBuilder implements MarkupContentBuilder<
4145
private static final String VARIABLE_KEY = "var";
4246
private static final String EXPORT_KEY = "export";
4347

48+
private final TypeResolver typeResolver;
4449
private final LanguageServerConfiguration configuration;
4550

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

77+
var types = typeResolver.findTypes(symbol);
78+
var typeDescription = getTypeDescription(types);
79+
addSectionIfNotEmpty(markupBuilder, typeDescription);
80+
7281
String content = markupBuilder.toString();
7382

7483
return new MarkupContent(MarkupKind.MARKDOWN, content);
@@ -112,6 +121,18 @@ private static String getLocation(VariableSymbol symbol) {
112121
);
113122
}
114123

124+
private static String getTypeDescription(List<Type> types) {
125+
var typeDescription = types.stream()
126+
.map(Type::getName)
127+
.collect(Collectors.joining(" | "));
128+
129+
if (!typeDescription.isEmpty()) {
130+
typeDescription = "`" + typeDescription + "`";
131+
}
132+
133+
return typeDescription;
134+
}
135+
115136
private static void addSectionIfNotEmpty(StringJoiner markupBuilder, String newContent) {
116137
if (!newContent.isEmpty()) {
117138
markupBuilder.add(newContent);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright (c) 2018-2024
5+
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Fedkin <nixel2007@gmail.com> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver.types;
23+
24+
import lombok.Value;
25+
26+
@Value
27+
public class Type {
28+
private final String name;
29+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright (c) 2018-2024
5+
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Fedkin <nixel2007@gmail.com> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver.types;
23+
24+
import com.github._1c_syntax.bsl.languageserver.context.ServerContext;
25+
import com.github._1c_syntax.bsl.languageserver.context.symbol.Describable;
26+
import com.github._1c_syntax.bsl.languageserver.context.symbol.SourceDefinedSymbol;
27+
import com.github._1c_syntax.bsl.languageserver.context.symbol.variable.VariableDescription;
28+
import com.github._1c_syntax.bsl.languageserver.references.ReferenceIndex;
29+
import com.github._1c_syntax.bsl.languageserver.references.ReferenceResolver;
30+
import com.github._1c_syntax.bsl.languageserver.references.model.OccurrenceType;
31+
import com.github._1c_syntax.bsl.languageserver.references.model.Reference;
32+
import com.github._1c_syntax.bsl.languageserver.utils.Trees;
33+
import com.github._1c_syntax.bsl.languageserver.utils.bsl.Constructors;
34+
import com.github._1c_syntax.bsl.parser.BSLParser;
35+
import lombok.RequiredArgsConstructor;
36+
import org.antlr.v4.runtime.tree.TerminalNode;
37+
import org.eclipse.lsp4j.Position;
38+
import org.springframework.stereotype.Component;
39+
40+
import java.net.URI;
41+
import java.util.Collection;
42+
import java.util.Collections;
43+
import java.util.List;
44+
import java.util.stream.Stream;
45+
46+
@Component
47+
@RequiredArgsConstructor
48+
public class TypeResolver {
49+
50+
private final ServerContext serverContext;
51+
private final ReferenceResolver referenceResolver;
52+
private final ReferenceIndex referenceIndex;
53+
54+
// TODO: Create LRU cache for calculated types.
55+
56+
// TODO: Use reference instead of symbol. Refactor hover provider to pass references to markup content builders.
57+
public List<Type> findTypes(SourceDefinedSymbol symbol) {
58+
return calculateTypes(symbol);
59+
}
60+
61+
public List<Type> findTypes(URI uri, Position position) {
62+
return referenceResolver.findReference(uri, position)
63+
.stream()
64+
.flatMap(reference -> calculateTypes(uri, reference).stream())
65+
.distinct()
66+
.toList();
67+
}
68+
69+
private List<Type> calculateTypes(SourceDefinedSymbol symbol) {
70+
71+
// variable description resolver
72+
if (symbol instanceof Describable describableSymbol) {
73+
var maybeDescription = describableSymbol.getDescription();
74+
if (maybeDescription.isPresent()) {
75+
var description = maybeDescription.get();
76+
if (description instanceof VariableDescription variableDescription) {
77+
// TODO: extract types from type description and return.
78+
}
79+
}
80+
}
81+
82+
// reference-based type resolver
83+
var ast = symbol.getOwner().getAst();
84+
var position = symbol.getSelectionRange().getStart();
85+
86+
var typesOfCurrentReference = calculateTypes(ast, position);
87+
88+
var typesOfOtherReferences = referenceIndex.getReferencesTo(symbol).stream()
89+
.filter(referenceTo -> referenceTo.getOccurrenceType() == OccurrenceType.DEFINITION)
90+
.map(referenceTo -> calculateTypes(ast, referenceTo.getSelectionRange().getStart()))
91+
.flatMap(Collection::stream)
92+
.toList();
93+
94+
return Stream.concat(typesOfCurrentReference.stream(), typesOfOtherReferences.stream())
95+
.distinct()
96+
.toList();
97+
}
98+
99+
private List<Type> calculateTypes(URI uri, Reference reference) {
100+
101+
// source defined symbol resolver
102+
if (reference.isSourceDefinedSymbolReference()) {
103+
return calculateTypes(reference.getSourceDefinedSymbol().orElseThrow());
104+
}
105+
106+
// expression tree resolver
107+
if (reference.getOccurrenceType() == OccurrenceType.DEFINITION) {
108+
var document = serverContext.getDocument(uri);
109+
var ast = document.getAst();
110+
var position = reference.getSelectionRange().getStart();
111+
return calculateTypes(ast, position);
112+
}
113+
114+
// no-op
115+
return Collections.emptyList();
116+
}
117+
118+
private List<Type> calculateTypes(BSLParser.FileContext ast, Position position) {
119+
return Trees.findTerminalNodeContainsPosition(ast, position)
120+
.map(TerminalNode::getParent)
121+
.map(ruleNode -> Trees.getRootParent(ruleNode, BSLParser.RULE_assignment))
122+
.map(BSLParser.AssignmentContext.class::cast)
123+
.map(BSLParser.AssignmentContext::expression)
124+
.map(this::calculateTypes)
125+
.orElseGet(Collections::emptyList);
126+
}
127+
128+
private List<Type> calculateTypes(BSLParser.ExpressionContext expression) {
129+
130+
// only simple cases for now. Use ExpressionTree in the future.
131+
if (!expression.operation().isEmpty()) {
132+
return Collections.emptyList();
133+
}
134+
135+
// new-resolver
136+
var typeName = typeName(expression);
137+
if (!typeName.isEmpty()) {
138+
Type type = new Type(typeName);
139+
return List.of(type);
140+
}
141+
142+
// const-value resolver
143+
var constValueContext = expression.member(0).constValue();
144+
if (constValueContext == null) {
145+
return Collections.emptyList();
146+
}
147+
148+
Type type = null;
149+
if (constValueContext.DATETIME() != null) {
150+
type = new Type("Дата");
151+
} else if (constValueContext.FALSE() != null || constValueContext.TRUE() != null) {
152+
type = new Type("Булево");
153+
} else if (constValueContext.NULL() != null) {
154+
type = new Type("null");
155+
} else if (constValueContext.numeric() != null) {
156+
type = new Type("Число");
157+
} else if (constValueContext.string() != null) {
158+
type = new Type("Строка");
159+
} else if (constValueContext.UNDEFINED() != null) {
160+
type = new Type("Неопределено");
161+
}
162+
163+
if (type != null) {
164+
return List.of(type);
165+
}
166+
167+
return Collections.emptyList();
168+
169+
}
170+
171+
private String typeName(BSLParser.ExpressionContext ctx) {
172+
var typeName = "";
173+
var newCtx = Trees.getNextNode(ctx, ctx, BSLParser.RULE_newExpression);
174+
if (newCtx instanceof BSLParser.NewExpressionContext newExpression) {
175+
typeName = Constructors.typeName(newExpression).orElse("");
176+
}
177+
return typeName;
178+
}
179+
180+
181+
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Trees.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.antlr.v4.runtime.ParserRuleContext;
2929
import org.antlr.v4.runtime.Token;
3030
import org.antlr.v4.runtime.tree.ParseTree;
31+
import org.antlr.v4.runtime.tree.RuleNode;
3132
import org.antlr.v4.runtime.tree.TerminalNode;
3233
import org.antlr.v4.runtime.tree.Tree;
3334
import org.eclipse.lsp4j.Position;
@@ -286,14 +287,14 @@ public static BSLParserRuleContext getRootParent(BSLParserRuleContext tnc) {
286287
* @return tnc - если родитель не найден, вернет null
287288
*/
288289
@Nullable
289-
public static BSLParserRuleContext getRootParent(BSLParserRuleContext tnc, int ruleindex) {
290+
public static BSLParserRuleContext getRootParent(RuleNode tnc, int ruleindex) {
290291
final var parent = tnc.getParent();
291292
if (parent == null) {
292293
return null;
293294
}
294295

295296
if (getRuleIndex(parent) == ruleindex) {
296-
return parent;
297+
return (BSLParserRuleContext) parent;
297298
} else {
298299
return getRootParent(parent, ruleindex);
299300
}

0 commit comments

Comments
 (0)