Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
repos:
# TypeScript compilation check
# Repository validation checks
- repo: local
hooks:
- id: typescript-compile
name: TypeScript Compile Check
entry: npm run compile
- id: npm-precommit
name: Repository Precommit Check
entry: npm run precommit
language: system
files: \.(ts|tsx)$
pass_filenames: false
always_run: true

# Pre-commit hooks for general file checks
- repo: https://github.com/pre-commit/pre-commit-hooks
Expand Down
236 changes: 176 additions & 60 deletions doc/Message_Interfaces.md

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions src/pluginsupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,32 @@ export class SelenePlugin extends BasePlugin {
return true;
}

public async configureFromViewerCache(
version: any,
viewerSeleneYml: string,
): Promise<boolean> {
if (!SelenePlugin.isEnabledHost(this.host)) {
console.warn("Selene plugin not active - skipping configuration");
return false;
}

const basename = `slua_${version}`;
const configPath = await this.host.config.getWorkspaceConfigPath();

const saved = await SelenePlugin.saveSLuaSeleneConfig(
configPath,
basename + `.yml`,
viewerSeleneYml,
this.host,
);

if (saved) {
await SelenePlugin.updateSeleneConfig(configPath, basename, this.host);
}

return saved;
}

private buildSeleneConfig(version: any, defs: LuaTypeDefinitions): string {
const generator = new SeleneYamlGenerator();
const config = {
Expand Down Expand Up @@ -142,6 +168,11 @@ export class LuaLSPPlugin extends BasePlugin {
version: any,
defs: LuaTypeDefinitions,
): Promise<boolean> {
if (!LuaLSPPlugin.isEnabledHost(this.host)) {
console.warn("Lua LSP plugin not active - skipping configuration");
return false;
}

// Implementation for configuring the Lua LSP plugin
let configs = this.buildLuauLSPConfig(defs);

Expand Down Expand Up @@ -264,6 +295,35 @@ export class LuaLSPPlugin extends BasePlugin {
return fullPath;
}

public async configureFromViewerCache(
version: any,
viewerDLuau: string,
viewerDocsJson: string,
): Promise<boolean> {
const configPath = await this.host.config.getWorkspaceConfigPath();

const defsFiles: { [k: string]: string } = {};

defsFiles["sl-slua"] = await this.saveLuauLSPDefs(
configPath,
version,
viewerDLuau,
);

if (this.host.config.getConfig(ConfigKey.PreprocessorConstantsInSLua, false)) {
defsFiles["sl-slua-consts"] = await this.saveLuauLSPConstantDefs(configPath);
}

const docsFileName = await this.saveLuauLSPDocs(
configPath,
version,
viewerDocsJson,
);

await this.restartLuauLSP(defsFiles, docsFileName, this.host);
return true;
}
Comment thread
Rider-Linden marked this conversation as resolved.
Comment on lines +298 to +325

public buildLuauLSPConfig(
defs: LuaTypeDefinitions,
): [string, string] {
Expand Down
42 changes: 42 additions & 0 deletions src/shared/languagerepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { HostInterface, NormalizedPath, normalizeJoinPath } from '../interfaces/hostinterface';
import { LanguageTransformer } from './languagetransformer';
import { JSONRPCInterface } from '../websockclient';
import { SyntaxCacheFile, SyntaxCacheGetRequest, SyntaxCacheList } from '../viewereditwsclient';
import { LSLKeywords } from "./lslkeywords";
import { LuaTypeDefinitions } from "./luadefsinterface";
import { sortObjectKeysRecursive } from '../utils';
Expand All @@ -18,9 +19,12 @@ export interface LanguageInfo {
export interface FetchOptions {
force?: boolean; // bypass cache when true
socket?: JSONRPCInterface; // viewer connection for remote fetch
syntaxCacheSupported?: boolean;
}

export class LanguageRepository {
public syntaxCacheFiles: string[] = [];

constructor(private readonly host: HostInterface) {}

public async getSyntax(version: string, opts: FetchOptions = {}): Promise<LanguageInfo | null> {
Expand Down Expand Up @@ -114,6 +118,44 @@ export class LanguageRepository {
}
}

public async requestSyntaxCacheList(socket: JSONRPCInterface): Promise<string[] | null> {
try {
const result = await socket.call('language.syntax.cache') as SyntaxCacheList;
if (result && result.success === true && Array.isArray(result.files)) {
this.syntaxCacheFiles = result.files;
return this.syntaxCacheFiles;
}
this.syntaxCacheFiles = [];
return null;
} catch (error) {
console.error('Error calling language.syntax.cache:', error);
this.syntaxCacheFiles = [];
return null;
}
}

public async requestSyntaxCacheFile(
socket: JSONRPCInterface,
filename: string,
asJson?: boolean,
): Promise<string | object | null> {
const params: SyntaxCacheGetRequest = {
filename,
...(asJson !== undefined ? { as_json: asJson } : {}),
};

try {
const result = await socket.call('language.syntax.get', params) as SyntaxCacheFile;
if (result && result.success === true && result.content !== undefined) {
return result.content;
}
return null;
} catch (error) {
console.error(`Error calling language.syntax.get for ${filename}:`, error);
return null;
}
}

private async requestLanguageSyntax(socket: JSONRPCInterface, kind: string): Promise<any | null> {
const params = { kind };
try {
Expand Down
68 changes: 65 additions & 3 deletions src/shared/languageservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,21 @@ export class LanguageService implements DisposableLike {
}

//#region Language Info Fetching
public async changeSyntaxVersion(syntaxId: string,
socket?: JSONRPCInterface, force?: boolean): Promise<boolean> {
const syntax = await this.repository.getSyntax(syntaxId, { force, socket });
public async changeSyntaxVersion(
syntaxId: string,
socket?: JSONRPCInterface,
force?: boolean,
syntaxCacheSupported?: boolean,
): Promise<boolean> {
if (syntaxCacheSupported && socket) {
return await this.configureSyntaxFromViewerCache(syntaxId, socket);
}

const syntax = await this.repository.getSyntax(syntaxId, {
force,
socket,
syntaxCacheSupported,
});

if (!syntax) {
console.warn(`No language syntax found for version ${syntaxId}`);
Expand All @@ -134,9 +146,59 @@ export class LanguageService implements DisposableLike {
return true;
}

private async configureSyntaxFromViewerCache(
syntaxId: string,
socket: JSONRPCInterface,
): Promise<boolean> {
const cacheFiles = this.repository.syntaxCacheFiles;

const selene = new SelenePlugin(this.host);
if (cacheFiles.includes("slua_selene.yml")) {
const content = await this.repository.requestSyntaxCacheFile(socket, "slua_selene.yml");
if (typeof content === "string") {
await selene.configureFromViewerCache(syntaxId, content);
} else {
console.warn("syntax_cache: slua_selene.yml missing or invalid, skipping Selene configuration");
}
} else {
console.warn("syntax_cache: slua_selene.yml not in viewer cache, skipping Selene configuration");
}

const luauLSP = new LuaLSPPlugin(this.host);
const hasDLuau = cacheFiles.includes("slua_default.d.luau");
const hasDocs = cacheFiles.includes("slua_default.docs.json");
if (hasDLuau && hasDocs) {
const dLuau = await this.repository.requestSyntaxCacheFile(socket, "slua_default.d.luau");
const docs = await this.repository.requestSyntaxCacheFile(socket, "slua_default.docs.json");
if (typeof dLuau === "string" && typeof docs === "string") {
await luauLSP.configureFromViewerCache(syntaxId, dLuau, docs);
} else {
console.warn("syntax_cache: slua_default.d.luau or slua_default.docs.json missing or invalid, skipping Luau-LSP configuration");
}
} else {
console.warn("syntax_cache: Luau-LSP files not in viewer cache, skipping Luau-LSP configuration");
}
Comment thread
Rider-Linden marked this conversation as resolved.

this.languageVersion = syntaxId;
await ConfigService.getInstance().setConfig<string>(ConfigKey.LastSyntaxID, syntaxId, { target: "global" });
return true;
}

public async requestSyntaxId(socket: JSONRPCInterface): Promise<string | null> {
return await this.repository.requestLanguageSyntaxId(socket);
}

public async requestSyntaxCacheList(socket: JSONRPCInterface): Promise<string[] | null> {
return await this.repository.requestSyntaxCacheList(socket);
}

public async requestSyntaxCacheFile(
socket: JSONRPCInterface,
filename: string,
asJson?: boolean,
): Promise<string | object | null> {
return await this.repository.requestSyntaxCacheFile(socket, filename, asJson);
}
//#endregion

//#region Language definition massaging
Expand Down
45 changes: 39 additions & 6 deletions src/synchservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export class SynchService implements vscode.Disposable {
public viewerVersion?: string;
public viewerLanguages?: string[];
public viewerFeatures?: { [feature: string]: boolean };
public syntaxCacheSupported: boolean = false;
public syntaxId?: string;
public agentId?: string;
public agentName?: string;
Expand Down Expand Up @@ -295,7 +296,7 @@ export class SynchService implements vscode.Disposable {
onHandshake: (message: SessionHandshake): any => this.onHandshake(message),
onHandshakeOk: (): any => this.onHandshakeOk(),
onDisconnect: (message: SessionDisconnect): any => this.onDisconnect(message),
onScriptUnsubscribe: (message: ScriptUnsubscribe): any =>
onUnsubscribe: (message: ScriptUnsubscribe): any =>
this.onScriptUnsubscribe(message),
onSyntaxChange: (message: SyntaxChange): any => this.onSyntaxChange(message),
onCompilationResult: (message: CompilationResult): any => this.onCompilationResult(message),
Expand Down Expand Up @@ -348,6 +349,7 @@ export class SynchService implements vscode.Disposable {
this.viewerLanguages = message.languages;
this.syntaxId = message.syntax_id;
this.viewerFeatures = message.features;
this.syntaxCacheSupported = message.features?.["syntax_cache"] === true;

let challengeResponse: string | undefined = undefined;
if (message.challenge) {
Expand All @@ -359,11 +361,17 @@ export class SynchService implements vscode.Disposable {
});
}

const firstSync = this.activeSync ?? [...this.activeSyncs.values()][0];
const scriptName = firstSync ? path.basename(firstSync.getMasterFilePath()) : undefined;
const scriptLanguage = firstSync ? firstSync.getLanguage() : undefined;

const response: SessionHandshakeResponse = {
client_name: ConfigService.getInstance().getConfig<string>(ConfigKey.ClientName) || "sl-vscode-plugin",
client_version: "1.0",
protocol_version: "1.0",
...maybe("challenge_response", challengeResponse),
...maybe("script_name", scriptName),
...maybe("script_language", scriptLanguage),
languages: ["lsl", "luau"],
features: {
live_sync: true,
Expand All @@ -375,7 +383,7 @@ export class SynchService implements vscode.Disposable {
return response;
}

private onHandshakeOk(): void {
private async onHandshakeOk(): Promise<void> {
// Session established successfully
console.log(
`Session established with viewer ${this.viewerName} v${this.viewerVersion}`,
Expand All @@ -385,10 +393,16 @@ export class SynchService implements vscode.Disposable {
);

const service = LanguageService.getInstance();
await this.refreshSyntaxCacheListIfSupported(service);
if (!this.checkLanguageVersion()) {
Comment thread
Rider-Linden marked this conversation as resolved.
const socket = this.getWebSocket();
if (socket && this.syntaxId) {
const promise = service.changeSyntaxVersion(this.syntaxId, socket);
const promise = service.changeSyntaxVersion(
this.syntaxId,
socket,
false,
this.syntaxCacheSupported,
);
showStatusMessage("Updating to latest language definitions...", promise);
}
}
Expand Down Expand Up @@ -424,20 +438,39 @@ export class SynchService implements vscode.Disposable {
}
}

private onSyntaxChange(params: SyntaxChange): void {
private async onSyntaxChange(params: SyntaxChange): Promise<void> {
if (this.syntaxId !== params.id) {
this.syntaxId = params.id;
const service = LanguageService.getInstance();
await this.refreshSyntaxCacheListIfSupported(service);
if (!this.checkLanguageVersion()) {
const service = LanguageService.getInstance();
const socket = this.getWebSocket();
if (socket) {
const promise = service.changeSyntaxVersion(params.id, socket);
const promise = service.changeSyntaxVersion(
params.id,
socket,
false,
this.syntaxCacheSupported,
);
showStatusMessage("Updating to latest language definitions...", promise);
}
}
}
}
Comment thread
Rider-Linden marked this conversation as resolved.

private async refreshSyntaxCacheListIfSupported(service: LanguageService): Promise<void> {
if (!this.syntaxCacheSupported) {
return;
}

const socket = this.getWebSocket();
if (!socket) {
return;
}

await service.requestSyntaxCacheList(socket);
}

private onCompilationResult(message: CompilationResult): void {
const scriptId = message.script_id;
const sync = this.findSyncByScriptId(scriptId);
Expand Down
Loading
Loading