mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd98225b3c | ||
|
|
3f1ae18ea5 | ||
|
|
f184e6f28c | ||
|
|
1998bd09c1 | ||
|
|
2d2dbeb79a | ||
|
|
2207546541 | ||
|
|
e08d7687aa | ||
|
|
932eaf8315 | ||
|
|
d0a5c1304a | ||
|
|
e44b3bf2f6 | ||
|
|
567cd9ae3b | ||
|
|
85d3d834d0 | ||
|
|
09dece1cec | ||
|
|
e3c653cd0d | ||
|
|
48057948c2 | ||
|
|
ce7107285d | ||
|
|
a43355425f | ||
|
|
4e2e7e0a55 | ||
|
|
1aea7c19b6 | ||
|
|
144904db24 | ||
|
|
4d0f1a1a43 | ||
|
|
9c6f6c53c5 | ||
|
|
de0958add0 | ||
|
|
7b60aabb08 | ||
|
|
e7ac881fab | ||
|
|
ec9b9de142 | ||
|
|
974f003a23 | ||
|
|
c96e383018 | ||
|
|
5db15936f7 | ||
|
|
dc0d245d04 | ||
|
|
666982b34d |
+16
-3
@@ -29,6 +29,8 @@ import io.cloudbeaver.server.servlets.WebStatusServlet;
|
||||
import io.cloudbeaver.server.websockets.CBEventsLongPollingServlet;
|
||||
import io.cloudbeaver.server.websockets.CBEventsWebSocket;
|
||||
import io.cloudbeaver.server.websockets.CBWebSocketServerConfigurator;
|
||||
import io.cloudbeaver.server.websockets.lsp.LSPWebSocketConstants;
|
||||
import io.cloudbeaver.server.websockets.lsp.LSPWebSocketEndpoint;
|
||||
import io.cloudbeaver.service.DBWServiceBindingServlet;
|
||||
import io.cloudbeaver.service.DBWServiceBindingWebSocket;
|
||||
import jakarta.websocket.server.ServerEndpointConfig;
|
||||
@@ -174,13 +176,24 @@ public class CBJettyServer {
|
||||
|
||||
JakartaWebSocketServletContainerInitializer.configure(servletContextHandler, (context, container) -> {
|
||||
// Add echo endpoint to server container
|
||||
ServerEndpointConfig eventWsEnpoint = ServerEndpointConfig.Builder
|
||||
CBWebSocketServerConfigurator configurator =
|
||||
new CBWebSocketServerConfigurator(application.getSessionManager());
|
||||
|
||||
ServerEndpointConfig eventWsEndpoint = ServerEndpointConfig.Builder
|
||||
.create(
|
||||
CBEventsWebSocket.class,
|
||||
serverConfiguration.getServicesURI() + "ws"
|
||||
).configurator(new CBWebSocketServerConfigurator(application.getSessionManager()))
|
||||
).configurator(configurator)
|
||||
.build();
|
||||
container.addEndpoint(eventWsEnpoint);
|
||||
container.addEndpoint(eventWsEndpoint);
|
||||
|
||||
ServerEndpointConfig lspWsEndpoint = ServerEndpointConfig.Builder
|
||||
.create(
|
||||
LSPWebSocketEndpoint.class,
|
||||
serverConfiguration.getServicesURI() + LSPWebSocketConstants.ENDPOINT_SUFFIX
|
||||
).configurator(configurator)
|
||||
.build();
|
||||
container.addEndpoint(lspWsEndpoint);
|
||||
});
|
||||
|
||||
JettyUtils.initSessionManager(
|
||||
|
||||
@@ -11,6 +11,7 @@ Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
|
||||
org.apache.commons.jexl,
|
||||
org.jkiss.utils;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.sql;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.lsp,
|
||||
org.jkiss.dbeaver.data.gis,
|
||||
org.jkiss.bundle.jetty.server;visibility:=reexport,
|
||||
com.google.gson;visibility:=reexport,
|
||||
@@ -19,7 +20,8 @@ Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
|
||||
org.jkiss.dbeaver.net.ssh,
|
||||
io.cloudbeaver.model;visibility:=reexport,
|
||||
io.cloudbeaver.model.cli,
|
||||
io.cloudbeaver.service.security;visibility:=reexport
|
||||
io.cloudbeaver.service.security;visibility:=reexport,
|
||||
org.eclipse.lsp4j.websocket.jakarta
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.app,
|
||||
@@ -33,9 +35,11 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.server.jobs,
|
||||
io.cloudbeaver.server.servlets,
|
||||
io.cloudbeaver.server.websockets,
|
||||
io.cloudbeaver.server.websockets.lsp,
|
||||
io.cloudbeaver.service,
|
||||
io.cloudbeaver.service.navigator,
|
||||
io.cloudbeaver.service.sql
|
||||
Import-Package: org.slf4j
|
||||
Import-Package: org.slf4j,
|
||||
jakarta.websocket
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.server
|
||||
|
||||
+3
-3
@@ -30,12 +30,12 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class CBJettyWebSocketManager {
|
||||
private static final Log log = Log.getLog(CBJettyWebSocketManager.class);
|
||||
private static final Map<String, List<CBEventsWebSocket>> socketBySessionId = new ConcurrentHashMap<>();
|
||||
private static final Map<String, List<CBAbstractWebSocket>> socketBySessionId = new ConcurrentHashMap<>();
|
||||
|
||||
private CBJettyWebSocketManager() {
|
||||
}
|
||||
|
||||
public static void registerWebSocket(@NotNull String webSessionId, @NotNull CBEventsWebSocket webSocket) {
|
||||
public static void registerWebSocket(@NotNull String webSessionId, @NotNull CBAbstractWebSocket webSocket) {
|
||||
socketBySessionId.computeIfAbsent(webSessionId, key -> new CopyOnWriteArrayList<>()).add(webSocket);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class CBJettyWebSocketManager {
|
||||
entry -> {
|
||||
var sessionId = entry.getKey();
|
||||
var webSockets = entry.getValue();
|
||||
for (CBEventsWebSocket webSocket : webSockets) {
|
||||
for (CBAbstractWebSocket webSocket : webSockets) {
|
||||
try {
|
||||
webSocket.getSession().getBasicRemote().sendPing(
|
||||
ByteBuffer.wrap("cb-ping".getBytes(StandardCharsets.UTF_8))
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp
|
||||
*
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
package io.cloudbeaver.server.websockets.lsp;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.impl.AbstractSessionPersistent;
|
||||
import org.jkiss.dbeaver.model.lsp.DBLServerSessionProvider;
|
||||
|
||||
public class LSPWebServerSessionProvider implements DBLServerSessionProvider {
|
||||
|
||||
@NotNull
|
||||
private final BaseWebSession session;
|
||||
|
||||
public LSPWebServerSessionProvider(@NotNull BaseWebSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AbstractSessionPersistent getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBPWorkspace getWorkspace() {
|
||||
return session.getWorkspace();
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp
|
||||
*
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
package io.cloudbeaver.server.websockets.lsp;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class LSPWebSocketConstants {
|
||||
|
||||
public static final String ENDPOINT_SUFFIX = "ws/lsp";
|
||||
public static final Duration IDLE_TIMEOUT = Duration.ofMinutes(5);
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp
|
||||
*
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
package io.cloudbeaver.server.websockets.lsp;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.server.websockets.CBAbstractWebSocket;
|
||||
import io.cloudbeaver.server.websockets.CBJettyWebSocketManager;
|
||||
import io.cloudbeaver.server.websockets.CBWebSocketServerConfigurator;
|
||||
import jakarta.websocket.CloseReason;
|
||||
import jakarta.websocket.EndpointConfig;
|
||||
import jakarta.websocket.Session;
|
||||
import org.eclipse.lsp4j.jsonrpc.Launcher;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import org.eclipse.lsp4j.websocket.jakarta.WebSocketLauncherBuilder;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.lsp.DBLServer;
|
||||
|
||||
public class LSPWebSocketEndpoint extends CBAbstractWebSocket {
|
||||
private static final Log log = Log.getLog(LSPWebSocketEndpoint.class);
|
||||
|
||||
public LSPWebSocketEndpoint() {}
|
||||
|
||||
@Override
|
||||
public void onOpen(Session wsSession, EndpointConfig endpointConfig) {
|
||||
BaseWebSession webSession = (BaseWebSession) wsSession.getUserProperties()
|
||||
.get(CBWebSocketServerConfigurator.PROP_WEB_SESSION);
|
||||
CBJettyWebSocketManager.registerWebSocket(webSession.getSessionId(), this);
|
||||
|
||||
wsSession.setMaxIdleTimeout(LSPWebSocketConstants.IDLE_TIMEOUT.toMillis());
|
||||
wsSession.setMaxTextMessageBufferSize(Integer.MAX_VALUE);
|
||||
wsSession.setMaxBinaryMessageBufferSize(Integer.MAX_VALUE);
|
||||
|
||||
LSPWebServerSessionProvider sessionProvider = new LSPWebServerSessionProvider(webSession);
|
||||
DBLServer server = new DBLServer(sessionProvider);
|
||||
Launcher<LanguageClient> launcher = new WebSocketLauncherBuilder<LanguageClient>()
|
||||
.setSession(wsSession)
|
||||
.setLocalService(server)
|
||||
.setRemoteInterface(LanguageClient.class)
|
||||
.create();
|
||||
server.connect(launcher.getRemoteProxy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, CloseReason closeReason) {
|
||||
log.debug("Closing websocket session: " + session.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Session session, Throwable thr) {
|
||||
log.error("LSP WebSocket error", thr);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import { CachedMapAllKey, CachedMapResource, type ResourceKey, resourceKeyList,
|
||||
import { GraphQLService, type SqlDialectInfo } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import type { IConnectionInfoParams } from './CONNECTION_INFO_PARAM_SCHEMA.js';
|
||||
import type { IConnectionExecutionContextInfo } from './ConnectionExecutionContext/ConnectionExecutionContextResource.js';
|
||||
import {
|
||||
ConnectionInfoActiveProjectKey,
|
||||
ConnectionInfoProjectKey,
|
||||
@@ -42,17 +41,6 @@ export class ConnectionDialectResource extends CachedMapResource<IConnectionInfo
|
||||
this.before(ExecutorInterrupter.interrupter(key => !connectionInfoResource.isConnected(key)));
|
||||
}
|
||||
|
||||
async formatScript(context: IConnectionExecutionContextInfo, query: string): Promise<string> {
|
||||
const result = await this.graphQLService.sdk.formatSqlQuery({
|
||||
projectId: context.projectId,
|
||||
connectionId: context.connectionId,
|
||||
contextId: context.id,
|
||||
query,
|
||||
});
|
||||
|
||||
return result.query;
|
||||
}
|
||||
|
||||
protected async loader(
|
||||
originalKey: ResourceKey<IConnectionInfoParams>,
|
||||
includes: string[],
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
query formatSqlQuery(
|
||||
$projectId: ID!
|
||||
$connectionId: ID!
|
||||
$contextId: ID!
|
||||
$query: String!
|
||||
) {
|
||||
query: sqlFormatQuery(
|
||||
projectId: $projectId
|
||||
connectionId: $connectionId
|
||||
contextId: $contextId
|
||||
query: $query
|
||||
)
|
||||
}
|
||||
@@ -35,6 +35,7 @@
|
||||
"@codemirror/lang-sql": "^6",
|
||||
"@codemirror/lang-xml": "^6",
|
||||
"@codemirror/language": "^6",
|
||||
"@codemirror/lsp-client": "^6",
|
||||
"@codemirror/merge": "^6",
|
||||
"@codemirror/search": "^6",
|
||||
"@codemirror/state": "^6",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +24,7 @@ export * from './Hyperlink/useHyperlink.js';
|
||||
export * from '@codemirror/view';
|
||||
export * from '@codemirror/state';
|
||||
export * from '@codemirror/autocomplete';
|
||||
export * from '@codemirror/lsp-client';
|
||||
export * from './highlightNewLine.js';
|
||||
|
||||
export { html as HTML_EDITOR } from '@codemirror/lang-html';
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
"@cloudbeaver/plugin-settings-panel": "workspace:*",
|
||||
"@cloudbeaver/plugin-sql-async-task-confirmation": "workspace:^",
|
||||
"@cloudbeaver/plugin-sql-editor": "workspace:*",
|
||||
"@cloudbeaver/plugin-sql-editor-codemirror": "workspace:*",
|
||||
"@cloudbeaver/plugin-sql-editor-navigation-tab": "workspace:*",
|
||||
"@cloudbeaver/plugin-sql-editor-navigation-tab-script": "workspace:*",
|
||||
"@cloudbeaver/plugin-sql-editor-new": "workspace:*",
|
||||
|
||||
@@ -90,6 +90,7 @@ import pluginSettingsPanel from '@cloudbeaver/plugin-settings-panel/module';
|
||||
import pluginSqlEditor from '@cloudbeaver/plugin-sql-editor/module';
|
||||
import pluginSqlEditorNavigationTab from '@cloudbeaver/plugin-sql-editor-navigation-tab/module';
|
||||
import pluginSqlEditorNavigationTabScript from '@cloudbeaver/plugin-sql-editor-navigation-tab-script/module';
|
||||
import pluginSqlEditorCodemirror from '@cloudbeaver/plugin-sql-editor-codemirror/module';
|
||||
import pluginSqlEditorNew from '@cloudbeaver/plugin-sql-editor-new/module';
|
||||
import pluginSqlEditorScreen from '@cloudbeaver/plugin-sql-editor-screen/module';
|
||||
import pluginSqlGenerator from '@cloudbeaver/plugin-sql-generator/module';
|
||||
@@ -175,6 +176,7 @@ export const commonSet = [
|
||||
pluginSqlEditorNavigationTab,
|
||||
pluginSqlEditorScreen,
|
||||
pluginSqlEditorNew,
|
||||
pluginSqlEditorCodemirror,
|
||||
pluginSqlGenerator,
|
||||
pluginUserProfile,
|
||||
pluginUserProfileAdministration,
|
||||
|
||||
@@ -274,6 +274,9 @@
|
||||
{
|
||||
"path": "../plugin-sql-editor"
|
||||
},
|
||||
{
|
||||
"path": "../plugin-sql-editor-codemirror"
|
||||
},
|
||||
{
|
||||
"path": "../plugin-sql-editor-navigation-tab"
|
||||
},
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@cloudbeaver/core-blocks": "workspace:*",
|
||||
"@cloudbeaver/core-di": "workspace:*",
|
||||
"@cloudbeaver/core-sdk": "workspace:*",
|
||||
"@cloudbeaver/core-utils": "workspace:*",
|
||||
"@cloudbeaver/plugin-codemirror6": "workspace:*",
|
||||
"mobx": "^6",
|
||||
"mobx-react-lite": "^4",
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { GlobalConstants } from '@cloudbeaver/core-utils';
|
||||
import { type Transport, LSPClient, languageServerExtensions } from '@cloudbeaver/plugin-codemirror6';
|
||||
|
||||
const LSP_ENDPOINT = 'ws/lsp';
|
||||
const RECONNECT_BASE_DELAY = 1000;
|
||||
const MAX_RECONNECT_ATTEMPTS = 5;
|
||||
|
||||
interface IReconnectingTransport extends Transport {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
interface ILSPPosition {
|
||||
line: number;
|
||||
character: number;
|
||||
}
|
||||
|
||||
interface ILSPTextEdit {
|
||||
range: { start: ILSPPosition; end: ILSPPosition };
|
||||
newText: string;
|
||||
}
|
||||
|
||||
interface IDocumentFormattingParams {
|
||||
textDocument: { uri: string };
|
||||
options: { tabSize: number; insertSpaces: boolean };
|
||||
}
|
||||
|
||||
function createReconnectingTransport(uri: string): { ready: Promise<IReconnectingTransport>; dispose(): void } {
|
||||
let handlers: ((value: string) => void)[] = [];
|
||||
let sock: WebSocket | null = null;
|
||||
let reconnectAttempts = 0;
|
||||
let reconnectTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let disposed = false;
|
||||
let connected = false;
|
||||
|
||||
const { promise: ready, resolve: resolveReady, reject: rejectReady } = Promise.withResolvers<IReconnectingTransport>();
|
||||
|
||||
const transport: IReconnectingTransport = {
|
||||
send(message: string) {
|
||||
if (sock?.readyState === WebSocket.OPEN) {
|
||||
sock.send(message);
|
||||
}
|
||||
},
|
||||
subscribe(handler) {
|
||||
handlers.push(handler);
|
||||
},
|
||||
unsubscribe(handler) {
|
||||
handlers = handlers.filter(h => h !== handler);
|
||||
},
|
||||
dispose() {
|
||||
disposed = true;
|
||||
|
||||
if (reconnectTimeout !== null) {
|
||||
clearTimeout(reconnectTimeout);
|
||||
}
|
||||
|
||||
if (sock) {
|
||||
sock.onopen = null;
|
||||
sock.onmessage = null;
|
||||
sock.onclose = null;
|
||||
sock.onerror = null;
|
||||
sock.close();
|
||||
sock = null;
|
||||
}
|
||||
|
||||
handlers = [];
|
||||
},
|
||||
};
|
||||
|
||||
function connect() {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
sock = new WebSocket(uri);
|
||||
|
||||
sock.onopen = () => {
|
||||
reconnectAttempts = 0;
|
||||
|
||||
if (!connected) {
|
||||
connected = true;
|
||||
resolveReady(transport);
|
||||
}
|
||||
};
|
||||
|
||||
sock.onmessage = e => {
|
||||
handlers.forEach(h => h(e.data as string));
|
||||
};
|
||||
|
||||
sock.onclose = () => {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
||||
reconnectAttempts++;
|
||||
const delay = RECONNECT_BASE_DELAY * 2 ** (reconnectAttempts - 1);
|
||||
console.warn(`[LSP] WebSocket closed, reconnecting in ${delay}ms (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);
|
||||
reconnectTimeout = setTimeout(connect, delay);
|
||||
} else {
|
||||
console.error('[LSP] Max reconnect attempts reached');
|
||||
rejectReady(new Error('LSP WebSocket connection failed after max reconnect attempts'));
|
||||
}
|
||||
};
|
||||
|
||||
sock.onerror = e => {
|
||||
console.error('[LSP] WebSocket error:', e);
|
||||
};
|
||||
}
|
||||
|
||||
connect();
|
||||
|
||||
return { ready, dispose: transport.dispose };
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class LSPConnectionService {
|
||||
private client: LSPClient | null = null;
|
||||
private disposeTransport: (() => void) | null = null;
|
||||
private refCount = 0;
|
||||
|
||||
acquire(): LSPClient {
|
||||
if (!this.client) {
|
||||
this.client = new LSPClient({
|
||||
extensions: languageServerExtensions(),
|
||||
});
|
||||
|
||||
const url = GlobalConstants.absoluteServiceWSUrl(LSP_ENDPOINT);
|
||||
const { ready, dispose } = createReconnectingTransport(url);
|
||||
this.disposeTransport = dispose;
|
||||
|
||||
ready
|
||||
.then(transport => {
|
||||
if (this.client) {
|
||||
this.client.connect(transport);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
this.refCount++;
|
||||
return this.client;
|
||||
}
|
||||
|
||||
async formatDocument(documentUri: string): Promise<string | null> {
|
||||
if (!this.client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.client.sync();
|
||||
|
||||
const result = await this.client.request<IDocumentFormattingParams, ILSPTextEdit[] | null>('textDocument/formatting', {
|
||||
textDocument: { uri: documentUri },
|
||||
options: { tabSize: 4, insertSpaces: true },
|
||||
});
|
||||
|
||||
if (!result?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result[0]!.newText;
|
||||
}
|
||||
|
||||
release(): void {
|
||||
this.refCount--;
|
||||
|
||||
if (this.refCount <= 0) {
|
||||
this.client = null;
|
||||
this.refCount = 0;
|
||||
this.disposeTransport?.();
|
||||
this.disposeTransport = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,5 +9,7 @@
|
||||
export * from './SQLCodeEditor/SQLCodeEditorLoader.js';
|
||||
export * from './SQLCodeEditor/useSQLCodeEditor.js';
|
||||
export * from './useSqlDialectExtension.js';
|
||||
export * from './useLSPExtension.js';
|
||||
export * from './LSPConnectionService.js';
|
||||
export * from './ACTIVE_QUERY_EXTENSION.js';
|
||||
export * from './QUERY_STATUS_GUTTER_EXTENSION.js';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { ModuleRegistry } from '@cloudbeaver/core-di';
|
||||
|
||||
import { LSPConnectionService } from './LSPConnectionService.js';
|
||||
|
||||
export default ModuleRegistry.add({
|
||||
name: '@cloudbeaver/plugin-sql-editor-codemirror',
|
||||
|
||||
configure: serviceCollection => {
|
||||
serviceCollection.addSingleton(LSPConnectionService);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { createLazyLoader, useLazyImport } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { type Compartment, type Extension } from '@cloudbeaver/plugin-codemirror6';
|
||||
|
||||
import { LSPConnectionService } from './LSPConnectionService.js';
|
||||
|
||||
const codemirrorPluginLoader = createLazyLoader(() => import('@cloudbeaver/plugin-codemirror6'));
|
||||
|
||||
export interface ILSPExtensionOptions {
|
||||
documentUri: string | null | undefined;
|
||||
}
|
||||
|
||||
export function useLSPExtension(options: ILSPExtensionOptions): [Compartment, Extension] | null {
|
||||
const { documentUri } = options;
|
||||
const codemirror = useLazyImport(codemirrorPluginLoader);
|
||||
const lspConnectionService = useService(LSPConnectionService);
|
||||
|
||||
const LSP_COMPARTMENT = useMemo(() => {
|
||||
if (!codemirror) {
|
||||
return null;
|
||||
}
|
||||
return new codemirror.Compartment();
|
||||
}, [codemirror]);
|
||||
|
||||
const client = useMemo(() => lspConnectionService.acquire(), [lspConnectionService]);
|
||||
|
||||
useEffect(() => () => lspConnectionService.release(), [lspConnectionService]);
|
||||
|
||||
return useMemo(() => {
|
||||
if (!LSP_COMPARTMENT || !client || !codemirror || !documentUri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [LSP_COMPARTMENT, client.plugin(documentUri)];
|
||||
}, [LSP_COMPARTMENT, client, codemirror, documentUri]);
|
||||
}
|
||||
@@ -13,9 +13,15 @@
|
||||
{
|
||||
"path": "../core-cli"
|
||||
},
|
||||
{
|
||||
"path": "../core-di"
|
||||
},
|
||||
{
|
||||
"path": "../core-sdk"
|
||||
},
|
||||
{
|
||||
"path": "../core-utils"
|
||||
},
|
||||
{
|
||||
"path": "../plugin-codemirror6"
|
||||
}
|
||||
|
||||
+9
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -73,6 +73,14 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
return key.projectId;
|
||||
}
|
||||
|
||||
override get lspDocumentUri(): string | null {
|
||||
if (!this.resourceKey || !this.projectId) {
|
||||
return null;
|
||||
}
|
||||
const { path } = getRmResourceKey(this.resourceKey);
|
||||
return `lsp://${this.projectId}/${path}`;
|
||||
}
|
||||
|
||||
get executionContext(): IConnectionExecutionContextInfo | undefined {
|
||||
const executionContext = this.state?.executionContext;
|
||||
if (!executionContext || !this.connectionInfoResource.has(createConnectionParam(executionContext.projectId, executionContext.connectionId))) {
|
||||
|
||||
+11
@@ -22,6 +22,7 @@ import {
|
||||
SQLCodeEditor,
|
||||
useSQLCodeEditor,
|
||||
useSqlDialectExtension,
|
||||
useLSPExtension,
|
||||
} from '@cloudbeaver/plugin-sql-editor-codemirror';
|
||||
import { useHighlightExtensions } from '../useHighlightExtensions.js';
|
||||
import { useSqlDialectAutocompletion } from '../useSqlDialectAutocompletion.js';
|
||||
@@ -46,6 +47,12 @@ export const SQLCodeEditorPanel: TabContainerPanelComponent<ISqlEditorModeProps>
|
||||
const sqlDialect = useSqlDialectExtension(data.dialect);
|
||||
const highlightExtensions = useHighlightExtensions(sqlEditorSettingsService.highlightWhitespace);
|
||||
|
||||
const lspDocumentUri =
|
||||
data.model.dataSource?.lspDocumentUri ??
|
||||
(data.model.dataSource?.projectId ? `lsp://${data.model.dataSource.projectId}/${data.model.state?.editorId}` : null);
|
||||
|
||||
const lspExtension = useLSPExtension({ documentUri: lspDocumentUri });
|
||||
|
||||
if (autocompletion) {
|
||||
extensions.set(...autocompletion);
|
||||
}
|
||||
@@ -60,6 +67,10 @@ export const SQLCodeEditorPanel: TabContainerPanelComponent<ISqlEditorModeProps>
|
||||
});
|
||||
}
|
||||
|
||||
if (lspExtension) {
|
||||
extensions.set(...lspExtension);
|
||||
}
|
||||
|
||||
const dndBox = useDNDBox({
|
||||
canDrop: context => context.has(DATA_CONTEXT_NAV_NODE),
|
||||
onDrop: async (context, mouse) => {
|
||||
|
||||
@@ -77,6 +77,10 @@ export abstract class BaseSqlDataSource<TDataSource extends QueryDataSource = Qu
|
||||
return this.executionContext?.projectId ?? null;
|
||||
}
|
||||
|
||||
get lspDocumentUri(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
readonly icon: string;
|
||||
readonly history: ISqlDataSourceHistory;
|
||||
readonly onUpdate: ISyncExecutor;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,6 +39,7 @@ export interface ISqlDataSource<TDataSource extends QueryDataSource = QueryDataS
|
||||
|
||||
readonly sourceKey: string;
|
||||
readonly projectId: string | null;
|
||||
readonly lspDocumentUri: string | null;
|
||||
|
||||
readonly script: string;
|
||||
readonly cursor: ISqlEditorCursor;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { ConnectionDialectResource, type IConnectionExecutionContextInfo } from '@cloudbeaver/core-connections';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
|
||||
@injectable(() => [ConnectionDialectResource, NotificationService])
|
||||
export class SqlDialectInfoService {
|
||||
constructor(
|
||||
private readonly connectionDialectResource: ConnectionDialectResource,
|
||||
private readonly notificationService: NotificationService,
|
||||
) {}
|
||||
|
||||
async formatScript(context: IConnectionExecutionContextInfo, query: string): Promise<string> {
|
||||
try {
|
||||
return await this.connectionDialectResource.formatScript(context, query);
|
||||
} catch (error: any) {
|
||||
this.notificationService.logException(error, 'Failed to format script');
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import { createLastPromiseGetter, type LastPromiseGetter } from '@cloudbeaver/co
|
||||
import type { ISqlEditorTabState } from '../ISqlEditorTabState.js';
|
||||
import { ESqlDataSourceFeatures } from '../SqlDataSource/ESqlDataSourceFeatures.js';
|
||||
import type { ISqlEditorCursor } from '../SqlDataSource/ISqlDataSource.js';
|
||||
import { SqlDialectInfoService } from '../SqlDialectInfoService.js';
|
||||
import { LSPConnectionService } from '@cloudbeaver/plugin-sql-editor-codemirror';
|
||||
import { SqlEditorService } from '../SqlEditorService.js';
|
||||
import { type ISQLScriptSegment } from '../SQLParser.js';
|
||||
import { SqlExecutionPlanService } from '../SqlResultTabs/ExecutionPlan/SqlExecutionPlanService.js';
|
||||
@@ -31,7 +31,7 @@ import { SqlEditorSettingsService } from '../SqlEditorSettingsService.js';
|
||||
import { SqlEditorModelService } from '../SqlEditorModel/SqlEditorModelService.js';
|
||||
|
||||
interface ISQLEditorDataPrivate extends ISQLEditorData {
|
||||
readonly sqlDialectInfoService: SqlDialectInfoService;
|
||||
readonly lspConnectionService: LSPConnectionService;
|
||||
readonly connectionExecutionContextService: ConnectionExecutionContextService;
|
||||
readonly sqlQueryService: SqlQueryService;
|
||||
readonly sqlEditorService: SqlEditorService;
|
||||
@@ -56,7 +56,7 @@ const MAX_HINTS_LIMIT = 200;
|
||||
export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
|
||||
const connectionExecutionContextService = useService(ConnectionExecutionContextService);
|
||||
const sqlQueryService = useService(SqlQueryService);
|
||||
const sqlDialectInfoService = useService(SqlDialectInfoService);
|
||||
const lspConnectionService = useService(LSPConnectionService);
|
||||
const sqlEditorService = useService(SqlEditorService);
|
||||
const notificationService = useService(NotificationService);
|
||||
const sqlExecutionPlanService = useService(SqlExecutionPlanService);
|
||||
@@ -155,26 +155,30 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
|
||||
},
|
||||
|
||||
async formatScript(): Promise<void> {
|
||||
if (this.isDisabled || this.isScriptEmpty || !this.model.dataSource?.executionContext) {
|
||||
if (this.isDisabled || this.isScriptEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
const script = await this.model.getResolvedSegment();
|
||||
const documentUri =
|
||||
this.model.dataSource?.lspDocumentUri ??
|
||||
(this.model.dataSource?.projectId ? `lsp://${this.model.dataSource.projectId}/${this.state.editorId}` : null);
|
||||
|
||||
if (!script) {
|
||||
if (!documentUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onExecute.execute(true);
|
||||
try {
|
||||
this.readonlyState = true;
|
||||
const formatted = await this.sqlDialectInfoService.formatScript(this.model.dataSource.executionContext, script.query);
|
||||
const formatted = await this.lspConnectionService.formatDocument(documentUri);
|
||||
|
||||
const cursorAnchor = this.model.cursor.anchor;
|
||||
this.setScript(this.value.substring(0, script.begin) + formatted + this.value.substring(script.end), 'format', {
|
||||
anchor: cursorAnchor,
|
||||
head: cursorAnchor,
|
||||
});
|
||||
if (formatted !== null) {
|
||||
const cursorAnchor = this.model.cursor.anchor;
|
||||
this.setScript(formatted, 'format', {
|
||||
anchor: cursorAnchor,
|
||||
head: cursorAnchor,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
this.readonlyState = false;
|
||||
}
|
||||
@@ -352,9 +356,9 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
|
||||
state,
|
||||
model,
|
||||
dialect: connectionDialectLoader.tryGetData,
|
||||
lspConnectionService,
|
||||
connectionExecutionContextService,
|
||||
sqlQueryService,
|
||||
sqlDialectInfoService,
|
||||
sqlEditorService,
|
||||
sqlExecutionPlanService,
|
||||
sqlResultTabsService,
|
||||
|
||||
@@ -46,7 +46,6 @@ export * from './SqlEditorModel/SqlEditorModelService.js';
|
||||
export * from './DATA_CONTEXT_SQL_EDITOR_STATE.js';
|
||||
export * from './getSqlEditorName.js';
|
||||
export * from './QueryDataSource.js';
|
||||
export * from './SqlDialectInfoService.js';
|
||||
export * from './ISqlEditorTabState.js';
|
||||
export * from './SQLEditorLoader.js';
|
||||
export * from './SqlEditorModeService.js';
|
||||
|
||||
@@ -20,7 +20,6 @@ import { SqlEditorSettingsService } from './SqlEditorSettingsService.js';
|
||||
import { SqlEditorService } from './SqlEditorService.js';
|
||||
import { SqlEditorModeService } from './SqlEditorModeService.js';
|
||||
import { SqlEditorGroupTabsBootstrap } from './SqlEditorGroupTabsBootstrap.js';
|
||||
import { SqlDialectInfoService } from './SqlDialectInfoService.js';
|
||||
import { SqlDataSourceService } from './SqlDataSource/SqlDataSourceService.js';
|
||||
import { LocalStorageSqlDataSourceBootstrap } from './SqlDataSource/LocalStorage/LocalStorageSqlDataSourceBootstrap.js';
|
||||
import { MenuBootstrap } from './MenuBootstrap.js';
|
||||
@@ -54,7 +53,6 @@ export default ModuleRegistry.add({
|
||||
.addSingleton(SqlEditorService)
|
||||
.addSingleton(SqlEditorModeService)
|
||||
.addSingleton(SqlEditorGroupTabsBootstrap)
|
||||
.addSingleton(SqlDialectInfoService)
|
||||
.addSingleton(SqlDataSourceService)
|
||||
.addSingleton(LocalStorageSqlDataSourceBootstrap)
|
||||
.addSingleton(SqlEditorModelService)
|
||||
|
||||
+58
-5
@@ -2595,6 +2595,7 @@ __metadata:
|
||||
"@codemirror/lang-sql": "npm:^6"
|
||||
"@codemirror/lang-xml": "npm:^6"
|
||||
"@codemirror/language": "npm:^6"
|
||||
"@codemirror/lsp-client": "npm:^6"
|
||||
"@codemirror/merge": "npm:^6"
|
||||
"@codemirror/search": "npm:^6"
|
||||
"@codemirror/state": "npm:^6"
|
||||
@@ -3953,6 +3954,7 @@ __metadata:
|
||||
"@cloudbeaver/plugin-settings-panel": "workspace:*"
|
||||
"@cloudbeaver/plugin-sql-async-task-confirmation": "workspace:^"
|
||||
"@cloudbeaver/plugin-sql-editor": "workspace:*"
|
||||
"@cloudbeaver/plugin-sql-editor-codemirror": "workspace:*"
|
||||
"@cloudbeaver/plugin-sql-editor-navigation-tab": "workspace:*"
|
||||
"@cloudbeaver/plugin-sql-editor-navigation-tab-script": "workspace:*"
|
||||
"@cloudbeaver/plugin-sql-editor-new": "workspace:*"
|
||||
@@ -4115,7 +4117,9 @@ __metadata:
|
||||
dependencies:
|
||||
"@cloudbeaver/core-blocks": "workspace:*"
|
||||
"@cloudbeaver/core-cli": "workspace:*"
|
||||
"@cloudbeaver/core-di": "workspace:*"
|
||||
"@cloudbeaver/core-sdk": "workspace:*"
|
||||
"@cloudbeaver/core-utils": "workspace:*"
|
||||
"@cloudbeaver/plugin-codemirror6": "workspace:*"
|
||||
"@cloudbeaver/tsconfig": "workspace:*"
|
||||
"@types/react": "npm:^19"
|
||||
@@ -4735,7 +4739,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@codemirror/autocomplete@npm:^6, @codemirror/autocomplete@npm:^6.0.0":
|
||||
"@codemirror/autocomplete@npm:^6, @codemirror/autocomplete@npm:^6.0.0, @codemirror/autocomplete@npm:^6.20.0":
|
||||
version: 6.20.1
|
||||
resolution: "@codemirror/autocomplete@npm:6.20.1"
|
||||
dependencies:
|
||||
@@ -4842,7 +4846,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/language@npm:^6, @codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.4.0, @codemirror/language@npm:^6.6.0":
|
||||
"@codemirror/language@npm:^6, @codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.11.0, @codemirror/language@npm:^6.4.0, @codemirror/language@npm:^6.6.0":
|
||||
version: 6.12.2
|
||||
resolution: "@codemirror/language@npm:6.12.2"
|
||||
dependencies:
|
||||
@@ -4856,7 +4860,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/lint@npm:^6.0.0":
|
||||
"@codemirror/lint@npm:^6.0.0, @codemirror/lint@npm:^6.8.5":
|
||||
version: 6.9.5
|
||||
resolution: "@codemirror/lint@npm:6.9.5"
|
||||
dependencies:
|
||||
@@ -4867,6 +4871,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/lsp-client@npm:^6":
|
||||
version: 6.2.2
|
||||
resolution: "@codemirror/lsp-client@npm:6.2.2"
|
||||
dependencies:
|
||||
"@codemirror/autocomplete": "npm:^6.20.0"
|
||||
"@codemirror/language": "npm:^6.11.0"
|
||||
"@codemirror/lint": "npm:^6.8.5"
|
||||
"@codemirror/state": "npm:^6.5.2"
|
||||
"@codemirror/view": "npm:^6.37.0"
|
||||
"@lezer/highlight": "npm:^1.2.1"
|
||||
marked: "npm:^15.0.12"
|
||||
vscode-languageserver-protocol: "npm:^3.17.5"
|
||||
checksum: 10c0/909759d953cc189444ce679c6c64859aa1503272510718cb28dea2f7934aa9e8f3985f2f824eac670d1bb211d273753f28b244b4bb0af313f2da058cbbd3cad8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/merge@npm:^6":
|
||||
version: 6.12.0
|
||||
resolution: "@codemirror/merge@npm:6.12.0"
|
||||
@@ -4891,7 +4911,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/state@npm:^6, @codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.4.0, @codemirror/state@npm:^6.5.0":
|
||||
"@codemirror/state@npm:^6, @codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.4.0, @codemirror/state@npm:^6.5.0, @codemirror/state@npm:^6.5.2":
|
||||
version: 6.5.4
|
||||
resolution: "@codemirror/state@npm:6.5.4"
|
||||
dependencies:
|
||||
@@ -6913,7 +6933,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lezer/highlight@npm:^1, @lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3":
|
||||
"@lezer/highlight@npm:^1, @lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3, @lezer/highlight@npm:^1.2.1":
|
||||
version: 1.2.3
|
||||
resolution: "@lezer/highlight@npm:1.2.3"
|
||||
dependencies:
|
||||
@@ -14912,6 +14932,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"marked@npm:^15.0.12":
|
||||
version: 15.0.12
|
||||
resolution: "marked@npm:15.0.12"
|
||||
bin:
|
||||
marked: bin/marked.js
|
||||
checksum: 10c0/e09da211544b787ecfb25fed07af206060bf7cd6d9de6cb123f15c496a57f83b7aabea93340aaa94dae9c94e097ae129377cad6310abc16009590972e85f4212
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"math-intrinsics@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "math-intrinsics@npm:1.1.0"
|
||||
@@ -19701,6 +19730,30 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vscode-jsonrpc@npm:8.2.0":
|
||||
version: 8.2.0
|
||||
resolution: "vscode-jsonrpc@npm:8.2.0"
|
||||
checksum: 10c0/0789c227057a844f5ead55c84679206227a639b9fb76e881185053abc4e9848aa487245966cc2393fcb342c4541241b015a1a2559fddd20ac1e68945c95344e6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vscode-languageserver-protocol@npm:^3.17.5":
|
||||
version: 3.17.5
|
||||
resolution: "vscode-languageserver-protocol@npm:3.17.5"
|
||||
dependencies:
|
||||
vscode-jsonrpc: "npm:8.2.0"
|
||||
vscode-languageserver-types: "npm:3.17.5"
|
||||
checksum: 10c0/5f38fd80da9868d706eaa4a025f4aff9c3faad34646bcde1426f915cbd8d7e8b6c3755ce3fef6eebd256ba3145426af1085305f8a76e34276d2e95aaf339a90b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vscode-languageserver-types@npm:3.17.5":
|
||||
version: 3.17.5
|
||||
resolution: "vscode-languageserver-types@npm:3.17.5"
|
||||
checksum: 10c0/1e1260de79a2cc8de3e46f2e0182cdc94a7eddab487db5a3bd4ee716f67728e685852707d72c059721ce500447be9a46764a04f0611e94e4321ffa088eef36f8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"w3c-keyname@npm:^2.2.4":
|
||||
version: 2.2.8
|
||||
resolution: "w3c-keyname@npm:2.2.8"
|
||||
|
||||
Reference in New Issue
Block a user