Skip to content

Commit 540e8f7

Browse files
authored
Merge pull request #2947 from 1c-syntax/feature/analyze-project-on-open
2 parents 6848e67 + 0a62232 commit 540e8f7

File tree

20 files changed

+446
-69
lines changed

20 files changed

+446
-69
lines changed

docs/features/ConfigurationFile.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ BSL Language Server предоставляет возможность измен
1616
|      `cyclomaticComplexity`|`Булево` или `JSON-Объект`|Включает отображение значения [цикломатической сложности](../diagnostics/CyclomaticComplexity.md) метода. По умолчанию настройка установлена в `true`. Доступные параметры линзы: `complexityThreshold` - порог, после которого линза начинает срабатывать. Значение параметра по умолчанию - `-1`.|
1717
|`diagnostics`|`JSON-Объект`|Содержит настройки диагностик|
1818
|&nbsp;&nbsp;&nbsp;`computeTrigger`|`Строка`|С помощью этого параметра можно указать событие, при котором будет вызвана процедура анализа кода для диагностирования замечаний. Возможные значения:<br/>* `onType` - при редактировании файла (онлайн) ***на больших файлах может ЗНАЧИТЕЛЬНО замедлять редактирование***<br/>* `onSave` - при сохранении файла (*используется по умолчанию*)<br/>* `never` - анализ выполняться не будет|
19-
|&nbsp;&nbsp;&nbsp;`ordinaryAppSupport`|`Булево>`|Поддержка обычного клиента. Диагностики будут требовать учитывать особенности обычного приложения. Возможные значения:<br/>* `true` - конфигурация разрабатывается с поддержкой обычного клиента *(установлен по умолчанию)* <br/>* `false` - игнорировать предупреждения связанные с особенностями обычного клиента|
19+
|&nbsp;&nbsp;&nbsp;`ordinaryAppSupport`|`Булево`|Поддержка обычного клиента. Диагностики будут требовать учитывать особенности обычного приложения. Возможные значения:<br/>* `true` - конфигурация разрабатывается с поддержкой обычного клиента *(установлен по умолчанию)* <br/>* `false` - игнорировать предупреждения связанные с особенностями обычного клиента|
2020
|&nbsp;&nbsp;&nbsp;`skipSupport`|`Строка`|Этим параметром настраивается режим пропуска файлов *(т.е. файлы не анализируются на предмет наличия замечаний)* **конфигурации 1С**, находящихся "на поддержке" конфигурации поставщика. Возможные значения:<br/>* `withSupport` - пропускаются все модули, находящиеся "на поддержке" *(все виды "замков")*<br/>* `withSupportLocked` - пропускаются только модули, находящиеся "на поддержке" с запретом изменений *("желтый закрытый замок")*<br/>* `never` - режим поддержки не анализируется и модули не пропускаются *(установлен по умолчанию)*|
2121
|&nbsp;&nbsp;&nbsp;`mode`|`Строка`|Настройка для управления режимом учета настроек диагностик. Возможные варианты:<br/>* `OFF` - Все диагностики считаются выключенными, вне зависимости от их настроек <br/>* `ON` - Все диагностики включенные по умолчанию считаются включенными, остальные - в зависимости от личных настроек <br/>* `EXCEPT` - Все диагностистики, кроме указанных, считаются включенными <br/>* `ONLY` - Только указанные диагностики считаются включенными <br/>* `ALL` - Все диагностики считаются включенными|
2222
|⤷&nbsp;&nbsp;&nbsp;`parameters`|`JSON-Объект`|Параметр представляет собой коллекцию настроек диагностик. Элементами коллекции являются json-объекты следующей структуры:<br/>* *ключ объекта* - строка, являющаяся ключом диагностики<br/>* *значение объекта* - может принимать либо булево значение, и тогда интерпретируется как отключение диагностики (`false`) или ее включение с параметрами по умолчанию (`true`), либо значение типа `json-объект`, представляющего собой набор настроек диагностики.<br/><br/>Ключ, включена ли по умолчанию, а также описание возможных параметров и примеры для конфигурационного файла представлены на странице с описанием каждой диагностики.|
2323
|&nbsp;&nbsp;&nbsp;`subsystemsFilter`|`JSON-Объект`|Фильтр по подсистемам конфигурации|
24+
|&nbsp;&nbsp;&nbsp;`analyzeOnStart`|`Булево`|Запустить анализ всего проекта при запуске сервера. Если включено, после построения контекста на клиента будет отправлена информация о диагностиках во всех файлах проекта.|
2425
|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`include`|`Массив` `Строка`|Список имен подсистем по объектам которых выполняется анализ, включая подчиненные подсистемы|
2526
|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`exclude`|`Массив` `Строка`|Список имен подсистем исключенных из анализа, включая подчиненные подсистемы|
2627
|`documentLink`|`JSON-Объект`|Содержит настройки ссылок на документацию|
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright (c) 2018-2022
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;
23+
24+
import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration;
25+
import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
26+
import com.github._1c_syntax.bsl.languageserver.context.events.ServerContextPopulatedEvent;
27+
import com.github._1c_syntax.bsl.languageserver.providers.DiagnosticProvider;
28+
import com.github._1c_syntax.bsl.languageserver.utils.Resources;
29+
import lombok.RequiredArgsConstructor;
30+
import org.springframework.context.event.EventListener;
31+
import org.springframework.scheduling.annotation.Async;
32+
import org.springframework.stereotype.Component;
33+
34+
import java.util.concurrent.ExecutionException;
35+
import java.util.concurrent.ForkJoinPool;
36+
37+
/**
38+
* Перехватчик события заполнения контекста сервера, запускающий анализ всех файлов контекста.
39+
*/
40+
@Component
41+
@RequiredArgsConstructor
42+
public class AnalyzeProjectOnStart {
43+
44+
private final LanguageServerConfiguration configuration;
45+
private final DiagnosticProvider diagnosticProvider;
46+
private final LanguageClientHolder languageClientHolder;
47+
private final WorkDoneProgressHelper workDoneProgressHelper;
48+
49+
@EventListener
50+
@Async
51+
public void handleEvent(ServerContextPopulatedEvent event) {
52+
if (!configuration.getDiagnosticsOptions().isAnalyzeOnStart()) {
53+
return;
54+
}
55+
56+
if (!languageClientHolder.isConnected()) {
57+
return;
58+
}
59+
60+
var serverContext = event.getSource();
61+
62+
var documentContexts = serverContext.getDocuments().values();
63+
var progress = workDoneProgressHelper.createProgress(documentContexts.size(), getMessage("filesSuffix"));
64+
progress.beginProgress(getMessage("analyzeProject"));
65+
66+
var executorService = new ForkJoinPool(ForkJoinPool.getCommonPoolParallelism());
67+
68+
try {
69+
executorService.submit(() ->
70+
documentContexts.parallelStream().forEach((DocumentContext documentContext) -> {
71+
progress.tick();
72+
73+
serverContext.rebuildDocument(documentContext);
74+
diagnosticProvider.computeAndPublishDiagnostics(documentContext);
75+
76+
serverContext.tryClearDocument(documentContext);
77+
})
78+
).get();
79+
80+
} catch (ExecutionException e) {
81+
throw new RuntimeException("Can't analyze project on start", e);
82+
} catch (InterruptedException e) {
83+
Thread.currentThread().interrupt();
84+
throw new RuntimeException("Interrupted while analyzing project on start", e);
85+
} finally {
86+
executorService.shutdown();
87+
}
88+
89+
progress.endProgress(getMessage("projectAnalyzed"));
90+
}
91+
92+
private String getMessage(String key) {
93+
return Resources.getResourceString(configuration.getLanguage(), getClass(), key);
94+
}
95+
96+
}

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
import org.eclipse.lsp4j.services.TextDocumentService;
9393
import org.springframework.stereotype.Component;
9494

95+
import java.net.URI;
9596
import java.util.Collections;
9697
import java.util.List;
9798
import java.util.concurrent.CompletableFuture;
@@ -305,7 +306,11 @@ public CompletableFuture<List<ColorPresentation>> colorPresentation(ColorPresent
305306

306307
@Override
307308
public void didOpen(DidOpenTextDocumentParams params) {
308-
var documentContext = context.addDocument(params.getTextDocument());
309+
var textDocumentItem = params.getTextDocument();
310+
var documentContext = context.addDocument(URI.create(textDocumentItem.getUri()));
311+
312+
context.openDocument(documentContext, textDocumentItem.getText(), textDocumentItem.getVersion());
313+
309314
if (configuration.getDiagnosticsOptions().getComputeTrigger() != ComputeTrigger.NEVER) {
310315
validate(documentContext);
311316
}
@@ -320,7 +325,11 @@ public void didChange(DidChangeTextDocumentParams params) {
320325
return;
321326
}
322327

323-
documentContext.rebuild(params.getContentChanges().get(0).getText(), params.getTextDocument().getVersion());
328+
context.rebuildDocument(
329+
documentContext,
330+
params.getContentChanges().get(0).getText(),
331+
params.getTextDocument().getVersion()
332+
);
324333

325334
if (configuration.getDiagnosticsOptions().getComputeTrigger() == ComputeTrigger.ONTYPE) {
326335
validate(documentContext);
@@ -334,7 +343,7 @@ public void didClose(DidCloseTextDocumentParams params) {
334343
return;
335344
}
336345

337-
documentContext.clearSecondaryData();
346+
context.closeDocument(documentContext);
338347

339348
diagnosticProvider.publishEmptyDiagnosticList(documentContext);
340349
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/aop/EventPublisherAspect.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration;
2525
import com.github._1c_syntax.bsl.languageserver.configuration.events.LanguageServerConfigurationChangedEvent;
2626
import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
27+
import com.github._1c_syntax.bsl.languageserver.context.ServerContext;
2728
import com.github._1c_syntax.bsl.languageserver.context.events.DocumentContextContentChangedEvent;
29+
import com.github._1c_syntax.bsl.languageserver.context.events.ServerContextPopulatedEvent;
2830
import com.github._1c_syntax.bsl.languageserver.events.LanguageServerInitializeRequestReceivedEvent;
2931
import lombok.NoArgsConstructor;
3032
import lombok.extern.slf4j.Slf4j;
@@ -38,6 +40,8 @@
3840
import org.springframework.context.ApplicationEventPublisherAware;
3941

4042
import javax.annotation.PreDestroy;
43+
import java.io.File;
44+
import java.util.Collection;
4145

4246
/**
4347
* Аспект подсистемы событий.
@@ -74,6 +78,11 @@ public void documentContextRebuild(JoinPoint joinPoint) {
7478
publishEvent(new DocumentContextContentChangedEvent((DocumentContext) joinPoint.getThis()));
7579
}
7680

81+
@AfterReturning("Pointcuts.isServerContext() && Pointcuts.isPopulateContextCall() && args(files)")
82+
public void serverContextPopulated(JoinPoint joinPoint, Collection<File> files) {
83+
publishEvent(new ServerContextPopulatedEvent((ServerContext) joinPoint.getThis()));
84+
}
85+
7786
@AfterReturning("Pointcuts.isLanguageServer() && Pointcuts.isInitializeCall() && args(initializeParams)")
7887
public void languageServerInitialize(JoinPoint joinPoint, InitializeParams initializeParams) {
7988
var event = new LanguageServerInitializeRequestReceivedEvent(

src/main/java/com/github/_1c_syntax/bsl/languageserver/aop/Pointcuts.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ public void isRebuildCall() {
107107
// no-op
108108
}
109109

110+
/**
111+
* Это вызов метода populateContext.
112+
*/
113+
@Pointcut("isBSLLanguageServerScope() && execution(* populateContext(..))")
114+
public void isPopulateContextCall() {
115+
// no-op
116+
}
117+
110118
/**
111119
* Это вызов метода update.
112120
*/

src/main/java/com/github/_1c_syntax/bsl/languageserver/cli/AnalyzeCommand.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.github._1c_syntax.bsl.languageserver.reporters.ReportersAggregator;
2828
import com.github._1c_syntax.bsl.languageserver.reporters.data.AnalysisInfo;
2929
import com.github._1c_syntax.bsl.languageserver.reporters.data.FileInfo;
30+
import com.github._1c_syntax.bsl.types.MdoReference;
3031
import com.github._1c_syntax.mdclasses.mdo.AbstractMDObjectBase;
3132
import com.github._1c_syntax.utils.Absolute;
3233
import lombok.RequiredArgsConstructor;
@@ -41,12 +42,10 @@
4142
import picocli.CommandLine.Command;
4243

4344
import java.io.File;
44-
import java.nio.charset.StandardCharsets;
4545
import java.nio.file.Path;
4646
import java.time.LocalDateTime;
4747
import java.util.ArrayList;
4848
import java.util.List;
49-
import java.util.Optional;
5049
import java.util.concurrent.Callable;
5150
import java.util.stream.Collectors;
5251

@@ -202,23 +201,21 @@ public String[] getReportersOptions() {
202201

203202
@SneakyThrows
204203
private FileInfo getFileInfoFromFile(Path srcDir, File file) {
205-
var textDocumentContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
206-
207-
var documentContext = context.addDocument(file.toURI(), textDocumentContent, 1);
204+
var documentContext = context.addDocument(file.toURI());
205+
context.rebuildDocument(documentContext);
208206

209207
var filePath = srcDir.relativize(Absolute.path(file));
210208
List<Diagnostic> diagnostics = documentContext.getDiagnostics();
211209
MetricStorage metrics = documentContext.getMetrics();
212-
var mdoRef = "";
213-
Optional<AbstractMDObjectBase> mdObjectBase = documentContext.getMdObject();
214-
if (mdObjectBase.isPresent()) {
215-
mdoRef = mdObjectBase.get().getMdoReference().getMdoRef();
216-
}
210+
var mdoRef = documentContext.getMdObject()
211+
.map(AbstractMDObjectBase::getMdoReference)
212+
.map(MdoReference::getMdoRef)
213+
.orElse("");
217214

218215
var fileInfo = new FileInfo(filePath, mdoRef, diagnostics, metrics);
219216

220217
// clean up AST after diagnostic computing to free up RAM.
221-
documentContext.clearSecondaryData();
218+
context.tryClearDocument(documentContext);
222219

223220
return fileInfo;
224221
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/cli/FormatCommand.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import org.springframework.stereotype.Component;
3838

3939
import java.io.File;
40-
import java.net.URI;
4140
import java.nio.charset.StandardCharsets;
4241
import java.nio.file.Path;
4342
import java.util.ArrayList;
@@ -147,13 +146,13 @@ private List<File> findFilesForFormatting(String[] filePaths) {
147146

148147
@SneakyThrows
149148
private void formatFile(File file) {
150-
String textDocumentContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
151-
final URI uri = file.toURI();
149+
var uri = file.toURI();
152150

153-
var documentContext = serverContext.addDocument(uri, textDocumentContent, 1);
151+
var documentContext = serverContext.addDocument(uri);
152+
serverContext.rebuildDocument(documentContext);
154153

155-
DocumentFormattingParams params = new DocumentFormattingParams();
156-
FormattingOptions options = new FormattingOptions();
154+
var params = new DocumentFormattingParams();
155+
var options = new FormattingOptions();
157156
options.setInsertSpaces(false);
158157

159158
params.setOptions(options);

src/main/java/com/github/_1c_syntax/bsl/languageserver/configuration/diagnostics/DiagnosticsOptions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
@JsonIgnoreProperties(ignoreUnknown = true)
4343
public class DiagnosticsOptions {
4444
private ComputeTrigger computeTrigger = ComputeTrigger.ONSAVE;
45+
private boolean analyzeOnStart;
4546
private SkipSupport skipSupport = SkipSupport.NEVER;
4647
private Mode mode = Mode.ON;
4748
private boolean ordinaryAppSupport = true;

src/main/java/com/github/_1c_syntax/bsl/languageserver/context/DocumentContext.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
import lombok.Getter;
4848
import lombok.RequiredArgsConstructor;
4949
import lombok.Setter;
50+
import lombok.extern.slf4j.Slf4j;
5051
import org.antlr.v4.runtime.Token;
52+
import org.apache.commons.io.FileUtils;
5153
import org.apache.commons.io.FilenameUtils;
5254
import org.eclipse.lsp4j.Diagnostic;
5355
import org.eclipse.lsp4j.Position;
@@ -59,7 +61,10 @@
5961

6062
import javax.annotation.Nullable;
6163
import javax.annotation.PostConstruct;
64+
import java.io.File;
65+
import java.io.IOException;
6266
import java.net.URI;
67+
import java.nio.charset.StandardCharsets;
6368
import java.util.Collections;
6469
import java.util.List;
6570
import java.util.Locale;
@@ -75,6 +80,7 @@
7580
@Component
7681
@Scope("prototype")
7782
@RequiredArgsConstructor
83+
@Slf4j
7884
public class DocumentContext {
7985

8086
private static final Pattern CONTENT_SPLIT_PATTERN = Pattern.compile("\r?\n|\r");
@@ -265,13 +271,12 @@ public void unfreezeComputedData() {
265271
isComputedDataFrozen = false;
266272
}
267273

268-
public void rebuild(String content, int version) {
274+
protected void rebuild(String content, int version) {
269275
computeLock.lock();
270276

271277
boolean versionMatches = version == this.version && version != 0;
272-
boolean contentWasCleared = this.content == null;
273278

274-
if (versionMatches && !contentWasCleared) {
279+
if (versionMatches && (this.content != null)) {
275280
clearDependantData();
276281
computeLock.unlock();
277282
return;
@@ -289,7 +294,16 @@ public void rebuild(String content, int version) {
289294
computeLock.unlock();
290295
}
291296

292-
public void clearSecondaryData() {
297+
protected void rebuild() {
298+
try {
299+
var newContent = FileUtils.readFileToString(new File(uri), StandardCharsets.UTF_8);
300+
rebuild(newContent, 0);
301+
} catch (IOException e) {
302+
LOGGER.error("Can't rebuild content from uri", e);
303+
}
304+
}
305+
306+
protected void clearSecondaryData() {
293307
computeLock.lock();
294308

295309
content = null;

0 commit comments

Comments
 (0)