Skip to content

Commit dd85b1d

Browse files
committed
fix: outline display
1 parent 729a308 commit dd85b1d

33 files changed

+530
-391
lines changed

clients/cobol-lsp-vscode-extension/src/test/suite/lsp.spec.test.ts

+28-7
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,39 @@ suite("Integration Test Suite", function () {
191191
test("TC312738 CICS variables and paragraphs support", async () => {
192192
await helper.showDocument("ADSORT.cbl");
193193
let editor = helper.get_editor("ADSORT.cbl");
194-
194+
await helper.waitFor(async () => {
195+
helper.sleep(100);
196+
const result: any[] = await vscode.commands.executeCommand(
197+
"vscode.executeDefinitionProvider",
198+
editor.document.uri,
199+
pos(58, 36),
200+
);
201+
return result.length > 0;
202+
});
195203
const result: any[] = await vscode.commands.executeCommand(
196204
"vscode.executeDefinitionProvider",
197205
editor.document.uri,
198206
pos(58, 36),
199207
);
200-
assert.ok(
201-
result.length === 1 &&
202-
result[0].uri.fsPath === editor.document.fileName &&
203-
result[0].range.start.line === 27 &&
204-
result[0].range.start.character === 7,
205-
"Checks behavior of go to definition action",
208+
assert.strictEqual(
209+
result.length,
210+
1,
211+
"Checks behavior of go to definition action (size)",
212+
);
213+
assert.strictEqual(
214+
result[0].uri.fsPath,
215+
editor.document.fileName,
216+
"Checks behavior of go to definition action (path)",
217+
);
218+
assert.strictEqual(
219+
result[0].range.start.line,
220+
27,
221+
"Checks behavior of go to definition action (line)",
222+
);
223+
assert.strictEqual(
224+
result[0].range.start.character,
225+
7,
226+
"Checks behavior of go to definition action (char)",
206227
);
207228
})
208229
.timeout(helper.TEST_TIMEOUT)

server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/symbols/SymbolsRepository.java

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.inject.Singleton;
2020
import lombok.Synchronized;
2121
import lombok.Value;
22+
import lombok.extern.slf4j.Slf4j;
2223
import org.eclipse.lsp.cobol.common.model.DefinedAndUsedStructure;
2324
import org.eclipse.lsp.cobol.common.model.tree.Node;
2425
import org.eclipse.lsp.cobol.common.model.tree.ProgramNode;
@@ -38,6 +39,7 @@
3839

3940
/** This class is a repository for symbols */
4041
@Singleton
42+
@Slf4j
4143
public class SymbolsRepository {
4244
private final Map<String, SymbolTable> programSymbols;
4345

server/engine/src/main/java/org/eclipse/lsp/cobol/lsp/AsyncAnalysisService.java

+52-17
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
import org.eclipse.lsp.cobol.service.delegates.communications.Communications;
2626

2727
import java.util.*;
28-
import java.util.concurrent.CompletableFuture;
29-
import java.util.concurrent.Executor;
30-
import java.util.concurrent.Executors;
28+
import java.util.concurrent.*;
3129

3230
/**
3331
* Asynchronous analysis
@@ -42,9 +40,10 @@ public class AsyncAnalysisService {
4240
private final Communications communications;
4341

4442
private final Map<String, CompletableFuture<CobolDocumentModel>> analysisResults = Collections.synchronizedMap(new HashMap<>());
45-
private final Map<String, Long> analysisResultsRevisions = Collections.synchronizedMap(new HashMap<>());
43+
private final Map<String, Integer> analysisResultsRevisions = Collections.synchronizedMap(new HashMap<>());
44+
private final Map<String, CompletableFuture<CobolDocumentModel>> lastResults = new ConcurrentHashMap<>();
4645

47-
private final Executor analysisExecutor = Executors.newSingleThreadExecutor();
46+
private final Executor analysisExecutor = Executors.newCachedThreadPool();
4847

4948
@Inject
5049
public AsyncAnalysisService(DocumentModelService documentModelService,
@@ -61,30 +60,47 @@ public AsyncAnalysisService(DocumentModelService documentModelService,
6160
/**
6261
* Fetch last analysis result or wait for it if analysis is in progress.
6362
*
64-
* @param uri URI of the source
63+
* @param documentUri URI of the source
6564
* @return DocumentModel with analysis result or Option.empty() if analysis was not
6665
* triggered at the time of method call.
6766
*/
68-
public Optional<CompletableFuture<CobolDocumentModel>> fetchLastResultOrAnalyzeDocument(String uri) {
69-
CobolDocumentModel documentModel = documentModelService.get(uri);
70-
if (documentModel == null || documentModel.getLastAnalysisResult() == null) {
71-
return Optional.ofNullable(analysisResults.get(makeId(uri, analysisResultsRevisions.get(uri))));
72-
}
73-
return Optional.of(CompletableFuture.completedFuture(documentModel));
67+
public Optional<CompletableFuture<CobolDocumentModel>> fetchLastResultOrAnalyzeDocument(String documentUri) {
68+
return Optional.ofNullable(lastResults.computeIfAbsent(documentUri, uri -> {
69+
CobolDocumentModel documentModel = documentModelService.get(uri);
70+
return documentModel == null || documentModel.getLastAnalysisResult() == null
71+
? analysisResults.get(makeId(uri, analysisResultsRevisions.get(uri)))
72+
: CompletableFuture.completedFuture(documentModel);
73+
}));
7474
}
7575

7676
/**
77-
* Schedule an analysis
77+
* Schedule an analysis last known revision of the file will be assumed or 0 if none
7878
*
7979
* @param uri source URI
8080
* @param text content
8181
* @param open Is document just opened, or it's reanalyse request
8282
* @return document model with analysis result
8383
*/
8484
public synchronized CompletableFuture<CobolDocumentModel> scheduleAnalysis(String uri, String text, boolean open) {
85-
Long currentRevision = analysisResultsRevisions.compute(uri, (k, v) -> (v == null ? 0 : v) + 1);
85+
return scheduleAnalysis(uri, text, analysisResultsRevisions.getOrDefault(uri, 0), open);
86+
}
87+
88+
/**
89+
* Schedule an analysis
90+
*
91+
* @param uri source URI
92+
* @param text content
93+
* @param currentRevision the document currentRevision
94+
* @param open Is document just opened, or it's reanalyse request
95+
* @return document model with analysis result
96+
*/
97+
public synchronized CompletableFuture<CobolDocumentModel> scheduleAnalysis(String uri, String text, Integer currentRevision, boolean open) {
8698
String id = makeId(uri, currentRevision);
99+
analysisResultsRevisions.put(uri, currentRevision);
87100
return analysisResults.computeIfAbsent(id, u -> CompletableFuture.supplyAsync(() -> {
101+
if (currentRevision < analysisResultsRevisions.get(uri)) {
102+
return null;
103+
}
88104
LOG.debug("[analyzeDocumentWithCopybooks] Start analysis: " + uri);
89105

90106
try {
@@ -101,7 +117,7 @@ public synchronized CompletableFuture<CobolDocumentModel> scheduleAnalysis(Strin
101117
}, analysisExecutor));
102118
}
103119

104-
private static String makeId(String uri, Long revision) {
120+
private static String makeId(String uri, Integer revision) {
105121
return revision + "#" + uri;
106122
}
107123

@@ -114,7 +130,7 @@ public void reanalyseOpenedPrograms() {
114130
LOG.info("Cache invalidated");
115131
documentModelService.getAllOpened()
116132
.stream().filter(d -> !analysisService.isCopybook(d.getUri(), d.getText()))
117-
.forEach(doc -> scheduleAnalysis(doc.getUri(), doc.getText(), false));
133+
.forEach(doc -> scheduleAnalysis(doc.getUri(), doc.getText(), analysisResultsRevisions.get(doc.getUri()), false));
118134
}
119135

120136
/**
@@ -124,8 +140,27 @@ public void reanalyseOpenedPrograms() {
124140
*/
125141
public void cancelAnalysis(String uri) {
126142
analysisService.stopAnalysis(uri);
127-
communications.notifyProgressEnd(uri);
128143
LOG.debug("[stopAnalysis] Document " + uri + " publish diagnostic: " + documentModelService.getOpenedDiagnostic());
129144
communications.publishDiagnostics(documentModelService.getOpenedDiagnostic());
130145
}
146+
147+
/**
148+
* Creates LSP Event dependency
149+
*
150+
* @param uri url of document to wait
151+
* @return LspEventDependency object
152+
*/
153+
public LspEventDependency createDependencyOn(String uri) {
154+
return () -> {
155+
CobolDocumentModel doc = documentModelService.get(uri);
156+
if (doc == null) {
157+
return false;
158+
}
159+
if (analysisService.isCopybook(uri, doc.getText())) {
160+
return true;
161+
}
162+
return doc.getLastAnalysisResult() != null;
163+
};
164+
}
165+
131166
}

server/engine/src/main/java/org/eclipse/lsp/cobol/lsp/CobolTextDocumentService.java

+9-8
Original file line numberDiff line numberDiff line change
@@ -95,29 +95,30 @@ public CobolTextDocumentService(
9595
@Override
9696
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(
9797
CompletionParams params) {
98-
return lspMessageDispatcher.publish(() -> completionHandler.completion(params));
98+
return lspMessageDispatcher.publish(completionHandler.createEvent(params));
9999
}
100100

101101
@Override
102102
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>>
103103
definition(DefinitionParams params) {
104-
return lspMessageDispatcher.publish(() -> definitionHandler.definition(params));
104+
return lspMessageDispatcher.publish(definitionHandler.createEvent(params));
105105
}
106106

107107
@Override
108108
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
109-
return lspMessageDispatcher.publish(() -> referencesHandler.references(params));
109+
return lspMessageDispatcher.publish(referencesHandler.createEvent(params));
110110
}
111111

112112
@Override
113113
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(
114114
DocumentHighlightParams params) {
115-
return lspMessageDispatcher.publish(() -> documentHighlightHandler.documentHighlight(params));
115+
return lspMessageDispatcher.publish(documentHighlightHandler.createEvent(params));
116+
116117
}
117118

118119
@Override
119120
public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams params) {
120-
return lspMessageDispatcher.publish(() -> formattingHandler.formatting(params));
121+
return lspMessageDispatcher.publish(formattingHandler.createEvent(params));
121122
}
122123

123124
@Override
@@ -163,16 +164,16 @@ public CompletableFuture<ExtendedApiResult> analysis(@NonNull JsonObject json) {
163164
@Override
164165
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(
165166
DocumentSymbolParams params) {
166-
return lspMessageDispatcher.publish(() -> documentSymbolHandler.documentSymbol(params));
167+
return lspMessageDispatcher.publish(documentSymbolHandler.createEvent(params));
167168
}
168169

169170
@Override
170171
public CompletableFuture<Hover> hover(HoverParams params) {
171-
return lspMessageDispatcher.publish(() -> hoverHandler.hover(params));
172+
return lspMessageDispatcher.publish(hoverHandler.createEvent(params));
172173
}
173174

174175
@Override
175176
public CompletableFuture<List<FoldingRange>> foldingRange(FoldingRangeRequestParams params) {
176-
return lspMessageDispatcher.publish(() -> foldingRangeHandler.foldingRange(params));
177+
return lspMessageDispatcher.publish(foldingRangeHandler.createEvent(params));
177178
}
178179
}

server/engine/src/main/java/org/eclipse/lsp/cobol/lsp/CobolWorkspaceServiceImpl.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,16 @@ public CompletableFuture<Object> executeCommand(@NonNull ExecuteCommandParams pa
7676
*/
7777
@Override
7878
public void didChangeConfiguration(DidChangeConfigurationParams params) {
79-
lspMessageDispatcher.publish(() -> {
80-
didChangeConfigurationHandler.didChangeConfiguration(params);
81-
return null;
82-
});
79+
if (isRelevant(params)) {
80+
lspMessageDispatcher.publish(() -> {
81+
didChangeConfigurationHandler.didChangeConfiguration(params);
82+
return null;
83+
});
84+
}
85+
}
86+
87+
private boolean isRelevant(DidChangeConfigurationParams params) {
88+
return params.getSettings() != null;
8389
}
8490

8591
/**

server/engine/src/main/java/org/eclipse/lsp/cobol/lsp/LspEvent.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,32 @@
1414
*/
1515
package org.eclipse.lsp.cobol.lsp;
1616

17+
import com.google.common.collect.ImmutableList;
18+
19+
import java.util.List;
1720
import java.util.concurrent.ExecutionException;
1821

1922
/**
2023
* Event handler logic container.
24+
*
2125
* @param <T> event processing result type.
2226
*/
2327
@FunctionalInterface
2428
public interface LspEvent<T> {
2529
/**
2630
* Event execute logic.
31+
*
2732
* @return execution result
28-
* @throws ExecutionException forward possible exception.
33+
* @throws ExecutionException forward possible exception.
2934
* @throws InterruptedException forward possible exception.
3035
*/
3136
T execute() throws ExecutionException, InterruptedException;
37+
38+
/**
39+
* Dependency data for the event. Allow to postpone the execution.
40+
* @return Dependencies is any.
41+
*/
42+
default List<LspEventDependency> getDependencies() {
43+
return ImmutableList.of();
44+
}
3245
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2023 Broadcom.
3+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
4+
*
5+
* This program and the accompanying materials are made
6+
* available under the terms of the Eclipse Public License 2.0
7+
* which is available at https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Broadcom, Inc. - initial API and implementation
13+
*
14+
*/
15+
package org.eclipse.lsp.cobol.lsp;
16+
17+
/**
18+
* Contains event handler execution precondition.
19+
*/
20+
@FunctionalInterface
21+
public interface LspEventDependency {
22+
/**
23+
* Chech if it's ok to run event handler.
24+
* @return false is execution should be postponed.
25+
*/
26+
boolean isSatisfied();
27+
}

server/engine/src/main/java/org/eclipse/lsp/cobol/lsp/LspMessageDispatcher.java

+13-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
*/
1515
package org.eclipse.lsp.cobol.lsp;
1616

17-
import com.google.inject.Inject;
1817
import com.google.inject.Singleton;
1918
import lombok.extern.slf4j.Slf4j;
2019

@@ -36,10 +35,6 @@ public class LspMessageDispatcher {
3635

3736
private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
3837

39-
@Inject
40-
public LspMessageDispatcher() {
41-
}
42-
4338
/**
4439
* Starts event loop
4540
*
@@ -68,6 +63,10 @@ private void loop() throws InterruptedException {
6863
continue;
6964
}
7065
try {
66+
if (!nextEven.getDependencies().stream().allMatch(LspEventDependency::isSatisfied)) {
67+
putBack(nextEven, future);
68+
continue;
69+
}
7170
future.complete(nextEven.execute());
7271
} catch (Exception e) {
7372
future.completeExceptionally(e);
@@ -81,6 +80,15 @@ private void loop() throws InterruptedException {
8180
}
8281
}
8382

83+
private void putBack(LspEvent<?> nextEven, CompletableFuture<Object> future) throws InterruptedException {
84+
eventResults.put(nextEven, future);
85+
if (!eventQueue.offer(nextEven)) {
86+
LOG.warn("Event " + nextEven + " dropped");
87+
}
88+
// save the CPU
89+
TimeUnit.MILLISECONDS.sleep(100);
90+
}
91+
8492
/**
8593
* Publish LSP event into event loop
8694
* @param event the event
@@ -105,5 +113,4 @@ public <R> CompletableFuture<R> publish(LspEvent<R> event) {
105113
public void stop() throws InterruptedException {
106114
eventQueue.put(POISON_PILL);
107115
}
108-
109116
}

0 commit comments

Comments
 (0)