Skip to content

Commit 21062fb

Browse files
authored
Better autocomplete (#1154)
1 parent 3caf327 commit 21062fb

4 files changed

Lines changed: 807 additions & 92 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/JassDocService.java

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.Map;
3535
import java.util.Optional;
3636
import java.util.concurrent.ConcurrentHashMap;
37+
import java.util.concurrent.atomic.AtomicBoolean;
3738
import java.util.function.Function;
3839
import java.util.stream.Stream;
3940

@@ -100,6 +101,9 @@ public int hashCode() {
100101
private volatile boolean triedInit;
101102
private volatile boolean initFailed;
102103
private volatile boolean missingSourceWarningShown;
104+
private final AtomicBoolean initRequested = new AtomicBoolean(false);
105+
private volatile @Nullable Connection sharedConnection;
106+
private volatile @Nullable Path sharedConnectionPath;
103107
private static volatile @Nullable Function<LookupKey, @Nullable String> testLookup;
104108

105109
private static final class CachedDb {
@@ -140,25 +144,70 @@ public static void setTestLookup(@Nullable Function<LookupKey, @Nullable String>
140144
return documentationFor(f.getName(), SymbolKind.FUNCTION, f.getSource().getFile());
141145
}
142146

147+
public @Nullable String documentationForFunctionQuick(FunctionDefinition f) {
148+
return documentationForQuick(f.getName(), SymbolKind.FUNCTION, f.getSource().getFile());
149+
}
150+
143151
public @Nullable String documentationForVariable(NameDef n) {
144152
return documentationFor(n.getName(), SymbolKind.VARIABLE, n.getSource().getFile());
145153
}
146154

155+
public @Nullable String documentationForVariableQuick(NameDef n) {
156+
return documentationForQuick(n.getName(), SymbolKind.VARIABLE, n.getSource().getFile());
157+
}
158+
147159
public @Nullable String documentationFor(String symbolName, SymbolKind symbolKind, String sourceFile) {
160+
Function<LookupKey, @Nullable String> override = testLookup;
161+
LookupKey key = new LookupKey(symbolName, symbolKind, sourceFile);
162+
if (override != null) {
163+
Optional<String> computed = Optional.ofNullable(trimToNull(override.apply(key)));
164+
Optional<String> prev = lookupCache.putIfAbsent(key, computed);
165+
return (prev != null ? prev : computed).orElse(null);
166+
}
148167
if (!isJassBuiltinSource(sourceFile)) {
149168
return null;
150169
}
151-
LookupKey key = new LookupKey(symbolName, symbolKind, sourceFile);
152170
Optional<String> cached = lookupCache.computeIfAbsent(key, this::lookupDocumentation);
153171
return cached.orElse(null);
154172
}
155173

174+
/**
175+
* Non-blocking lookup for latency-sensitive paths (autocomplete):
176+
* if DB is not ready, triggers async init and returns null immediately.
177+
*/
178+
public @Nullable String documentationForQuick(String symbolName, SymbolKind symbolKind, String sourceFile) {
179+
LookupKey key = new LookupKey(symbolName, symbolKind, sourceFile);
180+
Function<LookupKey, @Nullable String> override = testLookup;
181+
if (override != null) {
182+
Optional<String> computed = Optional.ofNullable(trimToNull(override.apply(key)));
183+
Optional<String> prev = lookupCache.putIfAbsent(key, computed);
184+
return (prev != null ? prev : computed).orElse(null);
185+
}
186+
if (!isJassBuiltinSource(sourceFile)) {
187+
return null;
188+
}
189+
Optional<String> cached = lookupCache.get(key);
190+
if (cached != null) {
191+
return cached.orElse(null);
192+
}
193+
CachedDb db = cachedDb;
194+
if (db == null) {
195+
triggerAsyncInit();
196+
return null;
197+
}
198+
Optional<String> computed = Optional.ofNullable(lookupFromDb(db, key));
199+
Optional<String> prev = lookupCache.putIfAbsent(key, computed);
200+
return (prev != null ? prev : computed).orElse(null);
201+
}
202+
156203
public void clearCacheForTests() {
157204
lookupCache.clear();
205+
closeSharedConnection();
158206
cachedDb = null;
159207
triedInit = false;
160208
initFailed = false;
161209
missingSourceWarningShown = false;
210+
initRequested.set(false);
162211
}
163212

164213
private Optional<String> lookupDocumentation(LookupKey key) {
@@ -216,7 +265,8 @@ private Optional<String> lookupDocumentation(LookupKey key) {
216265
}
217266

218267
private @Nullable String lookupFromDb(CachedDb db, LookupKey key) {
219-
try (Connection conn = open(db.dbPath)) {
268+
try {
269+
Connection conn = getSharedConnection(db.dbPath);
220270
String legacyDoc = lookupFromLegacyJassdocTables(conn, key);
221271
if (legacyDoc != null) {
222272
return legacyDoc;
@@ -258,6 +308,52 @@ private Optional<String> lookupDocumentation(LookupKey key) {
258308
}
259309
}
260310

311+
private Connection getSharedConnection(Path dbPath) throws SQLException {
312+
synchronized (this) {
313+
if (sharedConnection != null) {
314+
try {
315+
if (!sharedConnection.isClosed() && dbPath.equals(sharedConnectionPath)) {
316+
return sharedConnection;
317+
}
318+
} catch (SQLException ignored) {
319+
// reconnect below
320+
}
321+
closeSharedConnection();
322+
}
323+
sharedConnection = open(dbPath);
324+
sharedConnectionPath = dbPath;
325+
return sharedConnection;
326+
}
327+
}
328+
329+
private void closeSharedConnection() {
330+
synchronized (this) {
331+
if (sharedConnection != null) {
332+
try {
333+
sharedConnection.close();
334+
} catch (SQLException ignored) {
335+
}
336+
}
337+
sharedConnection = null;
338+
sharedConnectionPath = null;
339+
}
340+
}
341+
342+
private void triggerAsyncInit() {
343+
if (!initRequested.compareAndSet(false, true)) {
344+
return;
345+
}
346+
Thread t = new Thread(() -> {
347+
try {
348+
getOrInitDb();
349+
} finally {
350+
initRequested.set(false);
351+
}
352+
}, "JassDocInit");
353+
t.setDaemon(true);
354+
t.start();
355+
}
356+
261357
private @Nullable String lookupFromLegacyJassdocTables(Connection conn, LookupKey key) throws SQLException {
262358
if (!tableExists(conn, "parameters")) {
263359
return null;

0 commit comments

Comments
 (0)