Skip to content

Commit

Permalink
draft of completion provider
Browse files Browse the repository at this point in the history
  • Loading branch information
nixel2007 committed Jun 4, 2024
1 parent 243c8ff commit 3cb6bbf
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.eclipse.lsp4j.CodeActionOptions;
import org.eclipse.lsp4j.CodeLensOptions;
import org.eclipse.lsp4j.ColorProviderOptions;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.DefinitionOptions;
import org.eclipse.lsp4j.DocumentFormattingOptions;
import org.eclipse.lsp4j.DocumentLinkOptions;
Expand Down Expand Up @@ -123,6 +124,7 @@ public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
capabilities.setRenameProvider(getRenameProvider(params));
capabilities.setInlayHintProvider(getInlayHintProvider());
capabilities.setExecuteCommandProvider(getExecuteCommandProvider());
capabilities.setCompletionProvider(getCompletionProvider());

var result = new InitializeResult(capabilities, serverInfo);

Expand Down Expand Up @@ -336,4 +338,13 @@ private ExecuteCommandOptions getExecuteCommandProvider() {
executeCommandOptions.setWorkDoneProgress(Boolean.FALSE);
return executeCommandOptions;
}

private CompletionOptions getCompletionProvider() {
var completionOptions = new CompletionOptions();
completionOptions.setTriggerCharacters(List.of("."));
completionOptions.setResolveProvider(Boolean.FALSE);
completionOptions.setWorkDoneProgress(Boolean.FALSE);

return completionOptions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.github._1c_syntax.bsl.languageserver.providers.CodeActionProvider;
import com.github._1c_syntax.bsl.languageserver.providers.CodeLensProvider;
import com.github._1c_syntax.bsl.languageserver.providers.ColorProvider;
import com.github._1c_syntax.bsl.languageserver.providers.CompletionProvider;
import com.github._1c_syntax.bsl.languageserver.providers.DefinitionProvider;
import com.github._1c_syntax.bsl.languageserver.providers.DiagnosticProvider;
import com.github._1c_syntax.bsl.languageserver.providers.DocumentLinkProvider;
Expand Down Expand Up @@ -60,6 +61,9 @@
import org.eclipse.lsp4j.ColorPresentation;
import org.eclipse.lsp4j.ColorPresentationParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionParams;
import org.eclipse.lsp4j.DefinitionParams;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
Expand Down Expand Up @@ -125,6 +129,7 @@ public class BSLTextDocumentService implements TextDocumentService, ProtocolExte
private final ColorProvider colorProvider;
private final RenameProvider renameProvider;
private final InlayHintProvider inlayHintProvider;
private final CompletionProvider completionProvider;

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

Expand Down Expand Up @@ -370,6 +375,19 @@ public CompletableFuture<List<InlayHint>> inlayHint(InlayHintParams params) {
);
}

@Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) {
var documentContext = context.getDocument(params.getTextDocument().getUri());
if (documentContext == null) {
return CompletableFuture.completedFuture(null);
}

return CompletableFuture.supplyAsync(
() -> completionProvider.getCompletions(documentContext, params),
executorService
);
}

@Override
public void didOpen(DidOpenTextDocumentParams params) {
var textDocumentItem = params.getTextDocument();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.github._1c_syntax.bsl.languageserver.providers;

import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
import com.github._1c_syntax.bsl.languageserver.context.symbol.SourceDefinedSymbol;
import com.github._1c_syntax.bsl.languageserver.context.symbol.Symbol;
import com.github._1c_syntax.bsl.languageserver.types.KnownTypes;
import com.github._1c_syntax.bsl.languageserver.types.TypeResolver;
import com.github._1c_syntax.bsl.languageserver.utils.Ranges;
import com.github._1c_syntax.bsl.languageserver.utils.Trees;
import com.github._1c_syntax.bsl.parser.BSLLexer;
import lombok.RequiredArgsConstructor;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionParams;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

@Component
@RequiredArgsConstructor
public class CompletionProvider {

private final TypeResolver typeResolver;
private final KnownTypes knownTypes;

public Either<List<CompletionItem>, CompletionList> getCompletions(DocumentContext documentContext, CompletionParams params) {

var completionList = new CompletionList();

var position = params.getPosition();
var terminalNode = Trees.findTerminalNodeContainsPosition(documentContext.getAst(), position).orElseThrow();

if (terminalNode.getSymbol().getType() != BSLLexer.DOT) {
return Either.forRight(completionList);
}

var previousToken = Trees.getPreviousTokenFromDefaultChannel(documentContext.getTokens(), terminalNode.getSymbol().getTokenIndex() - 1);
var completionItems = previousToken
.map(Ranges::create)
.map(Range::getStart)
.map(previousTokenPosition -> typeResolver.findTypes(documentContext.getUri(), previousTokenPosition))
.stream()
.flatMap(Collection::stream)
.map(knownTypes::getSymbolByType)
.filter(Optional::isPresent)
.map(Optional::get)
.flatMap(symbol -> getChildren(symbol).stream())
.map(symbol -> {
var completionItem = new CompletionItem();
completionItem.setLabel(symbol.getName());
completionItem.setKind(getCompletionItemKind(symbol.getSymbolKind()));

return completionItem;
})
.toList();

completionList.setItems(completionItems);

return Either.forRight(completionList);
}

private CompletionItemKind getCompletionItemKind(SymbolKind symbolKind) {
return switch (symbolKind) {
case Class -> CompletionItemKind.Class;
case Method -> CompletionItemKind.Method;
case Variable -> CompletionItemKind.Variable;
case Module -> CompletionItemKind.Module;
default -> CompletionItemKind.Text;
};
}

private List<? extends Symbol> getChildren(Symbol symbol) {
if (!(symbol instanceof SourceDefinedSymbol sourceDefinedSymbol)) {
return Collections.emptyList();
}

return sourceDefinedSymbol.getChildren();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.github._1c_syntax.bsl.languageserver.types;

import com.github._1c_syntax.bsl.languageserver.context.events.DocumentContextContentChangedEvent;
import com.github._1c_syntax.bsl.languageserver.context.symbol.Symbol;
import org.apache.commons.io.FilenameUtils;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class KnownTypes {

private final Map<Type, Symbol> knownTypes = new ConcurrentHashMap<>();

public void addType(Type type, Symbol symbol) {
knownTypes.put(type, symbol);
}

public Optional<Symbol> getSymbolByType(Type type) {
return Optional.ofNullable(knownTypes.get(type));
}

public void clear() {
knownTypes.clear();
}

@EventListener
public void handleEvent(DocumentContextContentChangedEvent event) {
var documentContext = event.getSource();
// TODO: this logic should be moved to somewhere else. It will break for BSL files.
var typeName = FilenameUtils.getBaseName(documentContext.getUri().getPath());
var module = documentContext.getSymbolTree().getModule();

addType(new Type(typeName), module);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.github._1c_syntax.bsl.languageserver.providers;

import com.github._1c_syntax.bsl.languageserver.context.ServerContext;
import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterClass;
import com.github._1c_syntax.bsl.languageserver.util.TestUtils;
import jakarta.annotation.PostConstruct;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.CompletionParams;
import org.eclipse.lsp4j.Position;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.nio.file.Paths;

import static com.github._1c_syntax.bsl.languageserver.util.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;

@SpringBootTest
@CleanupContextBeforeClassAndAfterClass
class CompletionProviderTest {

@Autowired
private CompletionProvider completionProvider;

@Autowired
private ServerContext serverContext;

private static final String PATH_TO_FILE = "./src/test/resources/providers/completion.os";

@PostConstruct
void prepareServerContext() {
serverContext.setConfigurationRoot(Paths.get("src/test/resources/metadata/oslib"));
serverContext.populateContext();
}

@Test
void completionAfterDotOnOSClass() {
// given
var documentContext = TestUtils.getDocumentContextFromFile(PATH_TO_FILE);

var params = new CompletionParams();
params.setPosition(new Position(3, 13));

// when
var completions = completionProvider.getCompletions(documentContext, params);

// then
assertTrue(completions.isRight());

var completionList = completions.getRight();
assertThat(completionList.getItems()).hasSize(1);
assertThat(completionList.getItems().get(0).getLabel()).isEqualTo("NewObject");
assertThat(completionList.getItems().get(0).getKind()).isEqualTo(CompletionItemKind.Method);
}

}
17 changes: 17 additions & 0 deletions src/test/resources/metadata/oslib/Классы/МойКласс.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Перем ЭкспортнаяПеременная Экспорт;
Перем НеЭкспортнаяПеременная;

Функция ЭкспортнаяФункция() Экспорт
Возврат "";
КонецФункции

Функция НеЭкспортнаяФункция()
Возврат "";
КонецФункции

Процедура ЭкспортнаяПроцедура() Экспорт
КонецПроцедуры

Процедура НеЭкспортнаяПроцедура()
КонецПроцедуры

4 changes: 4 additions & 0 deletions src/test/resources/providers/completion.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#Использовать "metadata/oslib"

КакойТоКласс = Новый МойКласс();
КакойТоКласс.

0 comments on commit 3cb6bbf

Please sign in to comment.