Skip to content

[typespec-vscode] Allow lsp disable running emitters #7576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
b0ac596
Merge pull request #1 from microsoft/main
mzhongl524 Nov 22, 2024
3fcbe52
Merge branch 'microsoft:main' into main
mzhongl524 Nov 25, 2024
c109816
Merge branch 'microsoft:main' into main
mzhongl524 Dec 6, 2024
23f0108
Merge branch 'microsoft:main' into main
mzhongl524 Dec 12, 2024
6f04f65
Merge branch 'microsoft:main' into main
mzhongl524 Dec 23, 2024
688ca03
Merge branch 'microsoft:main' into main
mzhongl524 Dec 24, 2024
de3725e
Merge branch 'microsoft:main' into main
mzhongl524 Dec 30, 2024
8130ffb
Merge branch 'microsoft:main' into main
mzhongl524 Jan 3, 2025
beb6433
Merge branch 'microsoft:main' into main
mzhongl524 Jan 15, 2025
8a501f2
Merge branch 'microsoft:main' into main
mzhongl524 Jan 15, 2025
6e1ab2d
Merge branch 'microsoft:main' into main
mzhongl524 Jan 23, 2025
a35052f
Merge branch 'microsoft:main' into main
mzhongl524 Jan 24, 2025
be57c13
Merge branch 'microsoft:main' into main
mzhongl524 Feb 5, 2025
3a9dc7a
Merge branch 'microsoft:main' into main
mzhongl524 Feb 10, 2025
ad7dff7
Merge branch 'microsoft:main' into main
mzhongl524 Feb 10, 2025
f8e8f2f
Merge branch 'microsoft:main' into main
mzhongl524 Feb 12, 2025
7a700c8
Merge branch 'microsoft:main' into main
mzhongl524 Feb 14, 2025
5099583
Merge branch 'microsoft:main' into main
mzhongl524 Feb 18, 2025
a45dbd5
Merge branch 'microsoft:main' into main
mzhongl524 Feb 25, 2025
68cc91d
Merge branch 'microsoft:main' into main
mzhongl524 Feb 25, 2025
48a44ad
Merge branch 'microsoft:main' into main
mzhongl524 Feb 27, 2025
b9283c4
Merge branch 'microsoft:main' into main
mzhongl524 Feb 28, 2025
4286411
Merge branch 'microsoft:main' into main
mzhongl524 Feb 28, 2025
e549035
Merge branch 'microsoft:main' into main
mzhongl524 Mar 5, 2025
9717310
Merge branch 'microsoft:main' into main
mzhongl524 Mar 6, 2025
75260f8
Merge branch 'microsoft:main' into main
mzhongl524 Mar 10, 2025
18b1595
Merge branch 'microsoft:main' into main
mzhongl524 Mar 11, 2025
51078a7
Merge branch 'microsoft:main' into main
mzhongl524 Mar 13, 2025
75ae17a
Merge branch 'microsoft:main' into main
mzhongl524 Mar 17, 2025
da28d95
Merge branch 'microsoft:main' into main
mzhongl524 Mar 18, 2025
4afbb98
Merge branch 'microsoft:main' into main
mzhongl524 Mar 25, 2025
463bdd5
Merge branch 'microsoft:main' into main
mzhongl524 Mar 27, 2025
c105d86
Merge branch 'microsoft:main' into main
mzhongl524 Mar 27, 2025
39dc63e
Merge branch 'microsoft:main' into main
mzhongl524 Mar 31, 2025
ddb0c11
Merge branch 'microsoft:main' into main
mzhongl524 Apr 1, 2025
dccb3dc
Merge branch 'microsoft:main' into main
mzhongl524 Apr 8, 2025
af9903b
Merge branch 'microsoft:main' into main
mzhongl524 Apr 10, 2025
2dddaf5
Merge branch 'microsoft:main' into main
mzhongl524 Apr 15, 2025
857b72b
Merge branch 'microsoft:main' into main
mzhongl524 Apr 16, 2025
41f34d9
Merge branch 'microsoft:main' into main
mzhongl524 Apr 17, 2025
f4dd38f
Merge branch 'microsoft:main' into main
mzhongl524 Apr 18, 2025
4fd48e5
Merge branch 'microsoft:main' into main
mzhongl524 Apr 21, 2025
9870818
Merge branch 'microsoft:main' into main
mzhongl524 Apr 22, 2025
9bf8cdc
Merge branch 'microsoft:main' into main
mzhongl524 Apr 25, 2025
d596efd
Merge branch 'microsoft:main' into main
mzhongl524 May 8, 2025
aed33c0
Merge branch 'microsoft:main' into main
mzhongl524 May 14, 2025
6b49c4b
Merge branch 'microsoft:main' into main
mzhongl524 May 15, 2025
6687f5c
Merge branch 'microsoft:main' into main
mzhongl524 May 15, 2025
10aa654
Merge branch 'microsoft:main' into main
mzhongl524 May 15, 2025
fbf956e
Merge branch 'microsoft:main' into main
mzhongl524 May 15, 2025
0e3d44d
Merge branch 'microsoft:main' into main
mzhongl524 May 15, 2025
6586a10
Merge branch 'microsoft:main' into main
mzhongl524 May 16, 2025
5920ad3
Merge branch 'microsoft:main' into main
mzhongl524 May 16, 2025
2ece297
Merge branch 'microsoft:main' into main
mzhongl524 May 16, 2025
4ad5c5b
Merge branch 'microsoft:main' into main
mzhongl524 May 19, 2025
11fd24f
Merge branch 'microsoft:main' into main
mzhongl524 May 21, 2025
f15e07c
Merge branch 'microsoft:main' into main
mzhongl524 May 22, 2025
7661273
Allow LSP to disable running emitters
mzhongl524 May 28, 2025
b43717a
updated
mzhongl524 Jun 3, 2025
4e395a0
updated
mzhongl524 Jun 6, 2025
2a9bb56
updated
mzhongl524 Jun 11, 2025
64975d6
updated
mzhongl524 Jun 11, 2025
23ab58b
Create allow-lsp-disable-running-emitters-2025-5-11-2-18-42.md
mzhongl524 Jun 11, 2025
a5419df
updated
mzhongl524 Jun 18, 2025
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
140 changes: 140 additions & 0 deletions packages/compiler/src/server/client-config-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { Connection, DidChangeConfigurationNotification } from "vscode-languageserver/node.js";
import { ServerHost } from "./types.js";

/**
* Represents configuration value with additional metadata
*/
export interface ConfigurationValue<T = any> {
/** The configuration value */
value: T;
/** The scope where this value was defined (global, workspace, etc.) */
scope?: "default" | "global" | "workspace" | "workspaceFolder";
/** Language specific configuration if applicable */
languageId?: string;
}

/**
* TypeSpec client-side configuration provider
* Inspired by VS Code's WorkspaceConfiguration API for extensibility
*/
export interface ClientConfigProvider {
/**
* Get a configuration value by section key
* @param section The configuration section (e.g., "lsp.emit", "formatting.enabled")
* @param defaultValue Default value if not found
* @returns The configuration value or default
*/
get<T>(section: string): T | undefined;
get<T>(section: string, defaultValue: T): T;

/**
* Check if a configuration section exists
* @param section The configuration section
* @returns True if the section exists
*/
has(section: string): boolean;

/**
* Get detailed information about a configuration value
* @param section The configuration section
* @returns Detailed configuration information
*/
inspect<T>(section: string): ConfigurationValue<T> | undefined;

/**
* Update the entire configuration
* @param configs The new configuration object
*/
update(configs: Record<string, any>): void;

/**
* Initialize client configuration with connection and host
* @param connection Language server connection
* @param host Server host instance
*/
initialize(connection: Connection, host: ServerHost): Promise<void>;
}

export function createClientConfigProvider(): ClientConfigProvider {
let configuration: Record<string, any> = {};

function get<T>(section: string): T | undefined;
function get<T>(section: string, defaultValue: T): T;
function get<T>(section: string, defaultValue?: T): T | undefined {
const keys = section.split(".");
let current: any = configuration;

for (const key of keys) {
if (current && typeof current === "object" && key in current) {
current = current[key];
} else {
return defaultValue;
}
}

return current !== undefined ? current : defaultValue;
}

function has(section: string): boolean {
const keys = section.split(".");
let current: any = configuration;

for (const key of keys) {
if (current && typeof current === "object" && key in current) {
current = current[key];
} else {
return false;
}
}

return current !== undefined;
}

function inspect<T>(section: string): ConfigurationValue<T> | undefined {
if (!has(section)) {
return undefined;
}

const value = get<T>(section);
return {
value: value as T, // Safe cast since we know it exists from has() check
scope: "workspace", // Default scope, could be enhanced to track actual scope
};
}

function update(configs: Record<string, any>): void {
configuration = { ...configs };
}

async function initialize(connection: Connection, host: ServerHost): Promise<void> {
try {
await connection.client.register(DidChangeConfigurationNotification.type, undefined);
const configs = await connection.workspace.getConfiguration("typespec");
host.log({ level: "debug", message: "VSCode settings loaded", detail: configs });

update(configs);

connection.onDidChangeConfiguration(async (params) => {
if (params.settings && params.settings.typespec) {
update(params.settings.typespec);
}

host.log({ level: "debug", message: "Configuration changed", detail: params.settings });
});
} catch (error) {
host.log({
level: "error",
message: "An error occurred while loading the VSCode settings",
detail: error,
});
}
}

return {
get,
has,
inspect,
update,
initialize,
};
}
10 changes: 10 additions & 0 deletions packages/compiler/src/server/compile-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { doIO, loadFile } from "../utils/io.js";
import { resolveTspMain } from "../utils/misc.js";
import { getLocationInYamlScript } from "../yaml/diagnostics.js";
import { parseYaml } from "../yaml/parser.js";
import { ClientConfigProvider } from "./client-config-provider.js";
import { serverOptions } from "./constants.js";
import { FileService } from "./file-service.js";
import { FileSystemCache } from "./file-system-cache.js";
Expand Down Expand Up @@ -78,6 +79,7 @@ export interface CompileServiceOptions {
readonly serverHost: ServerHost;
readonly compilerHost: CompilerHost;
readonly log: (log: ServerLog) => void;
readonly clientConfigsProvider?: ClientConfigProvider;
}

export function createCompileService({
Expand All @@ -86,6 +88,7 @@ export function createCompileService({
fileService,
fileSystemCache,
log,
clientConfigsProvider,
}: CompileServiceOptions): CompileService {
const oldPrograms = new Map<string, Program>();
const eventListeners = new Map<string, (...args: unknown[]) => void>();
Expand Down Expand Up @@ -140,6 +143,13 @@ export function createCompileService({
...(additionalOptions ?? {}),
};

if (additionalOptions?.emit === undefined || additionalOptions?.emit.length === 0) {
const configEmits = clientConfigsProvider?.get<string[]>("lsp.emit");
if (configEmits) {
options.emit = configEmits;
}
}

// add linter rule for unused using if user didn't configure it explicitly
const unusedUsingRule = `${builtInLinterLibraryName}/${builtInLinterRule_UnusedUsing}`;
if (
Expand Down
13 changes: 9 additions & 4 deletions packages/compiler/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from "vscode-languageserver/node.js";
import { NodeHost } from "../core/node-host.js";
import { typespecVersion } from "../manifest.js";
import { createClientConfigProvider } from "./client-config-provider.js";
import { createServer } from "./serverlib.js";
import { CustomRequestName, Server, ServerHost, ServerLog } from "./types.js";

Expand All @@ -26,12 +27,12 @@ let profileSession: inspector.Session | undefined;

process.on("unhandledRejection", fatalError);
try {
main();
await main();
} catch (e) {
fatalError(e);
}

function main() {
async function main() {
// Redirect all console stdout output to stderr since LSP pipe uses stdout
// and writing to stdout for anything other than LSP protocol will break
// things badly.
Expand Down Expand Up @@ -86,7 +87,8 @@ function main() {
},
};

const s = createServer(host);
const clientConfigProvider = createClientConfigProvider();
const s = createServer(host, clientConfigProvider);
server = s;
s.log({ level: `info`, message: `TypeSpec language server v${typespecVersion}` });
s.log({ level: `info`, message: `Module: ${fileURLToPath(import.meta.url)}` });
Expand All @@ -106,10 +108,13 @@ function main() {
return await s.initialize(params);
});

connection.onInitialized((params) => {
connection.onInitialized(async (params) => {
if (clientHasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders(s.workspaceFoldersChanged);
}

// Initialize client configurations
await clientConfigProvider.initialize(connection, host);
s.initialized(params);
});

Expand Down
7 changes: 6 additions & 1 deletion packages/compiler/src/server/serverlib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import { resolveModule, ResolveModuleHost } from "../module-resolver/module-reso
import { listAllFilesInDir } from "../utils/fs-utils.js";
import { getNormalizedRealPath, resolveTspMain } from "../utils/misc.js";
import { getSemanticTokens } from "./classify.js";
import { ClientConfigProvider } from "./client-config-provider.js";
import { createCompileService } from "./compile-service.js";
import { resolveCompletion } from "./completion.js";
import { Commands } from "./constants.js";
Expand Down Expand Up @@ -130,7 +131,10 @@ import {
ServerWorkspaceFolder,
} from "./types.js";

export function createServer(host: ServerHost): Server {
export function createServer(
host: ServerHost,
clientConfigsProvider: ClientConfigProvider,
): Server {
const fileService = createFileService({ serverHost: host });

// Cache all file I/O. Only open documents are sent over the LSP pipe. When
Expand Down Expand Up @@ -158,6 +162,7 @@ export function createServer(host: ServerHost): Server {
compilerHost,
serverHost: host,
log,
clientConfigsProvider,
});
const currentDiagnosticIndex = new Map<number, Diagnostic>();
let diagnosticIdCounter = 0;
Expand Down
4 changes: 3 additions & 1 deletion packages/compiler/src/testing/test-server-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Diagnostic, FileChangeType } from "vscode-languageserver/node.js";
import { parse, visitChildren } from "../core/parser.js";
import { resolvePath } from "../core/path-utils.js";
import { IdentifierNode, SyntaxKind } from "../core/types.js";
import { createClientConfigProvider } from "../server/client-config-provider.js";
import { Server, ServerHost, createServer } from "../server/index.js";
import { createStringMap } from "../utils/misc.js";
import { StandardTestLibrary, TestHostOptions, createTestFileSystem } from "./test-host.js";
Expand Down Expand Up @@ -108,7 +109,8 @@ export async function createTestServerHost(options?: TestHostOptions & { workspa

const workspaceDir = options?.workspaceDir ?? "./";
const rootUri = serverHost.getURL(workspaceDir);
const server = createServer(serverHost);
const clientConfigProvider = createClientConfigProvider();
const server = createServer(serverHost, clientConfigProvider);
await server.initialize({
rootUri: options?.caseInsensitiveFileSystem ? rootUri.toUpperCase() : rootUri,
capabilities: {},
Expand Down
Loading
Loading