Skip to content

Commit 3cb6bbf

Browse files
committed
draft of completion provider
1 parent 243c8ff commit 3cb6bbf

File tree

7 files changed

+232
-0
lines changed

7 files changed

+232
-0
lines changed

src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLLanguageServer.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.eclipse.lsp4j.CodeActionOptions;
3838
import org.eclipse.lsp4j.CodeLensOptions;
3939
import org.eclipse.lsp4j.ColorProviderOptions;
40+
import org.eclipse.lsp4j.CompletionOptions;
4041
import org.eclipse.lsp4j.DefinitionOptions;
4142
import org.eclipse.lsp4j.DocumentFormattingOptions;
4243
import org.eclipse.lsp4j.DocumentLinkOptions;
@@ -123,6 +124,7 @@ public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
123124
capabilities.setRenameProvider(getRenameProvider(params));
124125
capabilities.setInlayHintProvider(getInlayHintProvider());
125126
capabilities.setExecuteCommandProvider(getExecuteCommandProvider());
127+
capabilities.setCompletionProvider(getCompletionProvider());
126128

127129
var result = new InitializeResult(capabilities, serverInfo);
128130

@@ -336,4 +338,13 @@ private ExecuteCommandOptions getExecuteCommandProvider() {
336338
executeCommandOptions.setWorkDoneProgress(Boolean.FALSE);
337339
return executeCommandOptions;
338340
}
341+
342+
private CompletionOptions getCompletionProvider() {
343+
var completionOptions = new CompletionOptions();
344+
completionOptions.setTriggerCharacters(List.of("."));
345+
completionOptions.setResolveProvider(Boolean.FALSE);
346+
completionOptions.setWorkDoneProgress(Boolean.FALSE);
347+
348+
return completionOptions;
349+
}
339350
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.github._1c_syntax.bsl.languageserver.providers.CodeActionProvider;
3333
import com.github._1c_syntax.bsl.languageserver.providers.CodeLensProvider;
3434
import com.github._1c_syntax.bsl.languageserver.providers.ColorProvider;
35+
import com.github._1c_syntax.bsl.languageserver.providers.CompletionProvider;
3536
import com.github._1c_syntax.bsl.languageserver.providers.DefinitionProvider;
3637
import com.github._1c_syntax.bsl.languageserver.providers.DiagnosticProvider;
3738
import com.github._1c_syntax.bsl.languageserver.providers.DocumentLinkProvider;
@@ -60,6 +61,9 @@
6061
import org.eclipse.lsp4j.ColorPresentation;
6162
import org.eclipse.lsp4j.ColorPresentationParams;
6263
import org.eclipse.lsp4j.Command;
64+
import org.eclipse.lsp4j.CompletionItem;
65+
import org.eclipse.lsp4j.CompletionList;
66+
import org.eclipse.lsp4j.CompletionParams;
6367
import org.eclipse.lsp4j.DefinitionParams;
6468
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
6569
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
@@ -125,6 +129,7 @@ public class BSLTextDocumentService implements TextDocumentService, ProtocolExte
125129
private final ColorProvider colorProvider;
126130
private final RenameProvider renameProvider;
127131
private final InlayHintProvider inlayHintProvider;
132+
private final CompletionProvider completionProvider;
128133

129134
private final ExecutorService executorService = Executors.newCachedThreadPool(new CustomizableThreadFactory("text-document-service-"));
130135

@@ -370,6 +375,19 @@ public CompletableFuture<List<InlayHint>> inlayHint(InlayHintParams params) {
370375
);
371376
}
372377

378+
@Override
379+
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) {
380+
var documentContext = context.getDocument(params.getTextDocument().getUri());
381+
if (documentContext == null) {
382+
return CompletableFuture.completedFuture(null);
383+
}
384+
385+
return CompletableFuture.supplyAsync(
386+
() -> completionProvider.getCompletions(documentContext, params),
387+
executorService
388+
);
389+
}
390+
373391
@Override
374392
public void didOpen(DidOpenTextDocumentParams params) {
375393
var textDocumentItem = params.getTextDocument();
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.github._1c_syntax.bsl.languageserver.providers;
2+
3+
import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
4+
import com.github._1c_syntax.bsl.languageserver.context.symbol.SourceDefinedSymbol;
5+
import com.github._1c_syntax.bsl.languageserver.context.symbol.Symbol;
6+
import com.github._1c_syntax.bsl.languageserver.types.KnownTypes;
7+
import com.github._1c_syntax.bsl.languageserver.types.TypeResolver;
8+
import com.github._1c_syntax.bsl.languageserver.utils.Ranges;
9+
import com.github._1c_syntax.bsl.languageserver.utils.Trees;
10+
import com.github._1c_syntax.bsl.parser.BSLLexer;
11+
import lombok.RequiredArgsConstructor;
12+
import org.eclipse.lsp4j.CompletionItem;
13+
import org.eclipse.lsp4j.CompletionItemKind;
14+
import org.eclipse.lsp4j.CompletionList;
15+
import org.eclipse.lsp4j.CompletionParams;
16+
import org.eclipse.lsp4j.Range;
17+
import org.eclipse.lsp4j.SymbolKind;
18+
import org.eclipse.lsp4j.jsonrpc.messages.Either;
19+
import org.springframework.stereotype.Component;
20+
21+
import java.util.Collection;
22+
import java.util.Collections;
23+
import java.util.List;
24+
import java.util.Optional;
25+
26+
@Component
27+
@RequiredArgsConstructor
28+
public class CompletionProvider {
29+
30+
private final TypeResolver typeResolver;
31+
private final KnownTypes knownTypes;
32+
33+
public Either<List<CompletionItem>, CompletionList> getCompletions(DocumentContext documentContext, CompletionParams params) {
34+
35+
var completionList = new CompletionList();
36+
37+
var position = params.getPosition();
38+
var terminalNode = Trees.findTerminalNodeContainsPosition(documentContext.getAst(), position).orElseThrow();
39+
40+
if (terminalNode.getSymbol().getType() != BSLLexer.DOT) {
41+
return Either.forRight(completionList);
42+
}
43+
44+
var previousToken = Trees.getPreviousTokenFromDefaultChannel(documentContext.getTokens(), terminalNode.getSymbol().getTokenIndex() - 1);
45+
var completionItems = previousToken
46+
.map(Ranges::create)
47+
.map(Range::getStart)
48+
.map(previousTokenPosition -> typeResolver.findTypes(documentContext.getUri(), previousTokenPosition))
49+
.stream()
50+
.flatMap(Collection::stream)
51+
.map(knownTypes::getSymbolByType)
52+
.filter(Optional::isPresent)
53+
.map(Optional::get)
54+
.flatMap(symbol -> getChildren(symbol).stream())
55+
.map(symbol -> {
56+
var completionItem = new CompletionItem();
57+
completionItem.setLabel(symbol.getName());
58+
completionItem.setKind(getCompletionItemKind(symbol.getSymbolKind()));
59+
60+
return completionItem;
61+
})
62+
.toList();
63+
64+
completionList.setItems(completionItems);
65+
66+
return Either.forRight(completionList);
67+
}
68+
69+
private CompletionItemKind getCompletionItemKind(SymbolKind symbolKind) {
70+
return switch (symbolKind) {
71+
case Class -> CompletionItemKind.Class;
72+
case Method -> CompletionItemKind.Method;
73+
case Variable -> CompletionItemKind.Variable;
74+
case Module -> CompletionItemKind.Module;
75+
default -> CompletionItemKind.Text;
76+
};
77+
}
78+
79+
private List<? extends Symbol> getChildren(Symbol symbol) {
80+
if (!(symbol instanceof SourceDefinedSymbol sourceDefinedSymbol)) {
81+
return Collections.emptyList();
82+
}
83+
84+
return sourceDefinedSymbol.getChildren();
85+
}
86+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.github._1c_syntax.bsl.languageserver.types;
2+
3+
import com.github._1c_syntax.bsl.languageserver.context.events.DocumentContextContentChangedEvent;
4+
import com.github._1c_syntax.bsl.languageserver.context.symbol.Symbol;
5+
import org.apache.commons.io.FilenameUtils;
6+
import org.springframework.context.event.EventListener;
7+
import org.springframework.stereotype.Component;
8+
9+
import java.util.Map;
10+
import java.util.Optional;
11+
import java.util.concurrent.ConcurrentHashMap;
12+
13+
@Component
14+
public class KnownTypes {
15+
16+
private final Map<Type, Symbol> knownTypes = new ConcurrentHashMap<>();
17+
18+
public void addType(Type type, Symbol symbol) {
19+
knownTypes.put(type, symbol);
20+
}
21+
22+
public Optional<Symbol> getSymbolByType(Type type) {
23+
return Optional.ofNullable(knownTypes.get(type));
24+
}
25+
26+
public void clear() {
27+
knownTypes.clear();
28+
}
29+
30+
@EventListener
31+
public void handleEvent(DocumentContextContentChangedEvent event) {
32+
var documentContext = event.getSource();
33+
// TODO: this logic should be moved to somewhere else. It will break for BSL files.
34+
var typeName = FilenameUtils.getBaseName(documentContext.getUri().getPath());
35+
var module = documentContext.getSymbolTree().getModule();
36+
37+
addType(new Type(typeName), module);
38+
}
39+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.github._1c_syntax.bsl.languageserver.providers;
2+
3+
import com.github._1c_syntax.bsl.languageserver.context.ServerContext;
4+
import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterClass;
5+
import com.github._1c_syntax.bsl.languageserver.util.TestUtils;
6+
import jakarta.annotation.PostConstruct;
7+
import org.eclipse.lsp4j.CompletionItemKind;
8+
import org.eclipse.lsp4j.CompletionParams;
9+
import org.eclipse.lsp4j.Position;
10+
import org.junit.jupiter.api.Test;
11+
import org.springframework.beans.factory.annotation.Autowired;
12+
import org.springframework.boot.test.context.SpringBootTest;
13+
14+
import java.nio.file.Paths;
15+
16+
import static com.github._1c_syntax.bsl.languageserver.util.Assertions.assertThat;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
19+
@SpringBootTest
20+
@CleanupContextBeforeClassAndAfterClass
21+
class CompletionProviderTest {
22+
23+
@Autowired
24+
private CompletionProvider completionProvider;
25+
26+
@Autowired
27+
private ServerContext serverContext;
28+
29+
private static final String PATH_TO_FILE = "./src/test/resources/providers/completion.os";
30+
31+
@PostConstruct
32+
void prepareServerContext() {
33+
serverContext.setConfigurationRoot(Paths.get("src/test/resources/metadata/oslib"));
34+
serverContext.populateContext();
35+
}
36+
37+
@Test
38+
void completionAfterDotOnOSClass() {
39+
// given
40+
var documentContext = TestUtils.getDocumentContextFromFile(PATH_TO_FILE);
41+
42+
var params = new CompletionParams();
43+
params.setPosition(new Position(3, 13));
44+
45+
// when
46+
var completions = completionProvider.getCompletions(documentContext, params);
47+
48+
// then
49+
assertTrue(completions.isRight());
50+
51+
var completionList = completions.getRight();
52+
assertThat(completionList.getItems()).hasSize(1);
53+
assertThat(completionList.getItems().get(0).getLabel()).isEqualTo("NewObject");
54+
assertThat(completionList.getItems().get(0).getKind()).isEqualTo(CompletionItemKind.Method);
55+
}
56+
57+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Перем ЭкспортнаяПеременная Экспорт;
2+
Перем НеЭкспортнаяПеременная;
3+
4+
Функция ЭкспортнаяФункция() Экспорт
5+
Возврат "";
6+
КонецФункции
7+
8+
Функция НеЭкспортнаяФункция()
9+
Возврат "";
10+
КонецФункции
11+
12+
Процедура ЭкспортнаяПроцедура() Экспорт
13+
КонецПроцедуры
14+
15+
Процедура НеЭкспортнаяПроцедура()
16+
КонецПроцедуры
17+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#Использовать "metadata/oslib"
2+
3+
КакойТоКласс = Новый МойКласс();
4+
КакойТоКласс.

0 commit comments

Comments
 (0)