Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Показ прогресса иницициализации контекста #2882

Merged
merged 10 commits into from
Nov 19, 2022
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* This file is a part of BSL Language Server.
*
* Copyright (c) 2018-2022
* 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;

import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.eclipse.lsp4j.ClientCapabilities;
import org.eclipse.lsp4j.ProgressParams;
import org.eclipse.lsp4j.WindowClientCapabilities;
import org.eclipse.lsp4j.WorkDoneProgressBegin;
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
import org.eclipse.lsp4j.WorkDoneProgressEnd;
import org.eclipse.lsp4j.WorkDoneProgressReport;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.LanguageClient;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@RequiredArgsConstructor
public class WorkDoneProgressHelper {

private final LanguageClientHolder languageClientHolder;
private final ClientCapabilitiesHolder clientCapabilitiesHolder;

private boolean isWorkDoneProgressSupported;

public WorkDoneProgressReporter createProgress(int size, String messagePostfix) {
isWorkDoneProgressSupported = clientCapabilitiesHolder.getCapabilities()
.map(ClientCapabilities::getWindow)
.map(WindowClientCapabilities::getWorkDoneProgress)
.orElse(false);

if (!isWorkDoneProgressSupported) {
return new WorkDoneProgressReporter("", 0, "");
}

var token = UUID.randomUUID().toString();
var createProgressParams = new WorkDoneProgressCreateParams(Either.forLeft(token));

languageClientHolder.execIfConnected(languageClient ->
languageClient.createProgress(createProgressParams)
);

return new WorkDoneProgressReporter(token, size, messagePostfix);
}


@AllArgsConstructor
public class WorkDoneProgressReporter {
private final String token;
@Setter
private int size;
private final String messagePostfix;

private final AtomicInteger counter = new AtomicInteger();

public void beginProgress(String title) {
if (!isWorkDoneProgressSupported) {
return;
}

languageClientHolder.execIfConnected((LanguageClient languageClient) -> {
var value = new WorkDoneProgressBegin();
value.setTitle(title);

var params = new ProgressParams(Either.forLeft(token), Either.forLeft(value));
languageClient.notifyProgress(params);
});
}

public void tick(String message, int percentage) {
if (!isWorkDoneProgressSupported) {
return;
}

languageClientHolder.execIfConnected((LanguageClient languageClient) -> {
var value = new WorkDoneProgressReport();
value.setMessage(message);
value.setCancellable(false);
value.setPercentage(percentage);

var params = new ProgressParams(Either.forLeft(token), Either.forLeft(value));
languageClient.notifyProgress(params);
});
}

public void tick() {
if (!isWorkDoneProgressSupported) {
return;
}

var currentCounter = counter.incrementAndGet();
var message = String.format("%d/%d%s", currentCounter, size, messagePostfix);
var percentage = currentCounter / size;

tick(message, percentage);
}

public void endProgress(String message) {
if (!isWorkDoneProgressSupported) {
return;
}

languageClientHolder.execIfConnected((LanguageClient languageClient) -> {
var value = new WorkDoneProgressEnd();
value.setMessage(message);

var params = new ProgressParams(Either.forLeft(token), Either.forLeft(value));
languageClient.notifyProgress(params);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -167,7 +166,7 @@ public Integer call() {
var configurationPath = LanguageServerConfiguration.getCustomConfigurationRoot(configuration, srcDir);
context.setConfigurationRoot(configurationPath);

Collection<File> files = FileUtils.listFiles(srcDir.toFile(), new String[]{"bsl", "os"}, true);
var files = (List<File>) FileUtils.listFiles(srcDir.toFile(), new String[]{"bsl", "os"}, true);

context.populateContext(files);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
*/
package com.github._1c_syntax.bsl.languageserver.context;

import com.github._1c_syntax.bsl.languageserver.WorkDoneProgressHelper;
import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration;
import com.github._1c_syntax.bsl.languageserver.utils.MdoRefBuilder;
import com.github._1c_syntax.bsl.languageserver.utils.Resources;
import com.github._1c_syntax.bsl.types.ModuleType;
import com.github._1c_syntax.mdclasses.Configuration;
import com.github._1c_syntax.utils.Absolute;
Expand All @@ -32,18 +35,18 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.eclipse.lsp4j.TextDocumentItem;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Component;

import javax.annotation.CheckForNull;
import java.io.File;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
Expand All @@ -54,7 +57,11 @@
@Slf4j
@Component
@RequiredArgsConstructor
public abstract class ServerContext {
public class ServerContext {
private final ObjectProvider<DocumentContext> documentContextProvider;
private final WorkDoneProgressHelper workDoneProgressHelper;
private final LanguageServerConfiguration languageServerConfiguration;

private final Map<URI, DocumentContext> documents = Collections.synchronizedMap(new HashMap<>());
private final Lazy<Configuration> configurationMetadata = new Lazy<>(this::computeConfigurationMetadata);
@CheckForNull
Expand All @@ -70,29 +77,42 @@ public void populateContext() {
LOGGER.info("Can't populate server context. Configuration root is not defined.");
return;
}

var workDoneProgressReporter = workDoneProgressHelper.createProgress(0, "");
workDoneProgressReporter.beginProgress(getMessage("populateFindFiles"));

LOGGER.debug("Finding files to populate context...");
Collection<File> files = FileUtils.listFiles(
var files = (List<File>) FileUtils.listFiles(
configurationRoot.toFile(),
new String[]{"bsl", "os"},
true
);
workDoneProgressReporter.endProgress("");
populateContext(files);
}

public void populateContext(Collection<File> uris) {
public void populateContext(List<File> files) {
var workDoneProgressReporter = workDoneProgressHelper.createProgress(files.size(), getMessage("populateFilesPostfix"));
workDoneProgressReporter.beginProgress(getMessage("populatePopulatingContext"));

LOGGER.debug("Populating context...");
contextLock.writeLock().lock();

uris.parallelStream().forEach((File file) -> {
files.parallelStream().forEach((File file) -> {

workDoneProgressReporter.tick();

var documentContext = getDocument(file.toURI());
if (documentContext == null) {
documentContext = createDocumentContext(file, 0);
documentContext = createDocumentContext(file);
documentContext.freezeComputedData();
documentContext.clearSecondaryData();
}
});

contextLock.writeLock().unlock();

workDoneProgressReporter.endProgress(getMessage("populateContextPopulated"));
LOGGER.debug("Context populated.");
}

Expand Down Expand Up @@ -146,7 +166,7 @@ public DocumentContext addDocument(TextDocumentItem textDocumentItem) {
}

public void removeDocument(URI uri) {
URI absoluteURI = Absolute.uri(uri);
var absoluteURI = Absolute.uri(uri);
removeDocumentMdoRefByUri(absoluteURI);
documents.remove(absoluteURI);
}
Expand All @@ -162,19 +182,16 @@ public Configuration getConfiguration() {
return configurationMetadata.getOrCompute();
}

@Lookup
protected abstract DocumentContext lookupDocumentContext(URI absoluteURI);

@SneakyThrows
private DocumentContext createDocumentContext(File file, int version) {
String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
return createDocumentContext(file.toURI(), content, version);
private DocumentContext createDocumentContext(File file) {
var content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
return createDocumentContext(file.toURI(), content, 0);
}

private DocumentContext createDocumentContext(URI uri, String content, int version) {
URI absoluteURI = Absolute.uri(uri);
var absoluteURI = Absolute.uri(uri);

var documentContext = lookupDocumentContext(absoluteURI);
var documentContext = documentContextProvider.getObject(absoluteURI);
documentContext.rebuild(content, version);

documents.put(absoluteURI, documentContext);
Expand All @@ -188,6 +205,9 @@ private Configuration computeConfigurationMetadata() {
return Configuration.create();
}

var progress = workDoneProgressHelper.createProgress(0, "");
progress.beginProgress(getMessage("computeConfigurationMetadata"));

Configuration configuration;
var executorService = Executors.newCachedThreadPool();
try {
Expand All @@ -203,6 +223,8 @@ private Configuration computeConfigurationMetadata() {
executorService.shutdown();
}

progress.endProgress(getMessage("computeConfigurationMetadataDone"));

return configuration;
}

Expand All @@ -229,4 +251,8 @@ private void removeDocumentMdoRefByUri(URI uri) {
mdoRefs.remove(uri);
}
}

private String getMessage(String key) {
return Resources.getResourceString(languageServerConfiguration.getLanguage(), getClass(), key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
computeConfigurationMetadata=Computing configuration metadata...
computeConfigurationMetadataDone=Configuration metadata computing is finished.
populateContextPopulated=Context populated.
populateFindFiles=Finding files to populate context...
populatePopulatingContext=Populating context...
populateFilesPostfix=\ files
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
populateContextPopulated=Наполнение контекста завершено.
populateFindFiles=Ищем файлы для наполнения контекста...
populatePopulatingContext=Наполняем контекст...
computeConfigurationMetadata=Рассчитываем данные конфигурации...
computeConfigurationMetadataDone=Расчет метаданных конфигурации завершен.
populateFilesPostfix=\ файлов
Loading