@typespec/compiler
Version:
TypeSpec compiler and standard library
138 lines • 5.94 kB
JavaScript
import { pathToFileURL } from "url";
import { FileChangeType } from "vscode-languageserver";
import { TextDocument } from "vscode-languageserver-textdocument";
import { parse, visitChildren } from "../core/parser.js";
import { resolvePath } from "../core/path-utils.js";
import { SyntaxKind } from "../core/types.js";
import { createClientConfigProvider } from "../server/client-config-provider.js";
import { createServer } from "../server/index.js";
import { createStringMap } from "../utils/misc.js";
import { createTestFileSystem } from "./fs.js";
import { StandardTestLibrary } from "./test-compiler-host.js";
import { resolveVirtualPath } from "./test-utils.js";
export async function createTestServerHost(options) {
const logMessages = [];
const documents = createStringMap(!!options?.caseInsensitiveFileSystem);
const diagnostics = createStringMap(!!options?.caseInsensitiveFileSystem);
const fileSystem = await createTestFileSystem({ ...options, excludeTestLib: true });
await fileSystem.addTypeSpecLibrary(StandardTestLibrary);
const serverHost = {
...fileSystem,
throwInternalErrors: true,
server: undefined, // initialized later due to cycle
logMessages,
getOpenDocumentByURL(url) {
return documents.get(url);
},
getOpenDocument(path) {
return this.getOpenDocumentByURL(this.getURL(path));
},
addOrUpdateDocument(path, content) {
const url = this.getURL(path);
let version = 1;
const oldDocument = documents.get(url);
if (oldDocument) {
version = oldDocument.version;
if (oldDocument.getText() !== content) {
version++;
}
}
const document = TextDocument.create(url, "typespec", version, content);
documents.set(url, document);
fileSystem.addTypeSpecFile(path, ""); // force virtual file system to create directory where document lives.
if (!oldDocument) {
server.documentOpened({ document });
}
server.checkChange({ document });
return document;
},
openDocument(path) {
const content = fileSystem.fs.get(resolveVirtualPath(path)) ?? "";
return this.addOrUpdateDocument(path, content);
},
getDiagnostics(path) {
return diagnostics.get(this.getURL(path)) ?? [];
},
async sendDiagnostics(params) {
if (params.version && documents.get(params.uri)?.version !== params.version) {
return;
}
diagnostics.set(params.uri, params.diagnostics);
},
log(log) {
logMessages.push(`[${log.level}] ${log.message}`);
},
getURL(path) {
if (path.startsWith("untitled:")) {
return path;
}
return pathToFileURL(resolveVirtualPath(path)).href;
},
async applyEdit(paramOrEdit) {
if ("changes" in paramOrEdit) {
const changes = paramOrEdit.changes || {};
for (const uri in changes) {
const path = resolvePath(this.compilerHost.fileURLToPath(uri));
const document = this.fs.get(path);
if (document) {
const lines = document.split("\n");
changes[uri].map((edit) => {
const curLineIdx = edit.range.start.line;
lines[curLineIdx] =
lines[curLineIdx].slice(0, edit.range.start.character) +
edit.newText +
lines[curLineIdx].slice(edit.range.end.character);
this.fs.set(path, lines.join("\n"));
});
}
server.watchedFilesChanged({
changes: [{ uri, type: FileChangeType.Changed }],
});
}
}
return Promise.resolve({ applied: true });
},
getDocumentUpdateDebounceDelay: () => 0,
};
const workspaceDir = options?.workspaceDir ?? "./";
const rootUri = serverHost.getURL(workspaceDir);
const clientConfigProvider = createClientConfigProvider();
const server = createServer(serverHost, clientConfigProvider);
await server.initialize({
rootUri: options?.caseInsensitiveFileSystem ? rootUri.toUpperCase() : rootUri,
capabilities: {},
processId: null,
workspaceFolders: null,
});
server.initialized({});
const serverCompile = server.compile;
server.compile = async (document, additionalOptions, serverCompileOptions) => {
// Wrap the compile to report diagnostics after compile in test to make sure existing tests
// that rely on diagnostics to work without having to change each of them to call reportDiagnostics explicitly.
const result = await serverCompile(document, additionalOptions, serverCompileOptions);
if (result) {
await server.reportDiagnostics(result);
}
return result;
};
serverHost.server = server;
return serverHost;
}
/**
* Extracts all identifiers marked with trailing empty comments from source
*/
export function getTestIdentifiers(source) {
const identifiers = [];
const ast = parse(source);
visitChildren(ast, function visit(node) {
if (node.kind === SyntaxKind.Identifier) {
if (source.substring(node.end, node.end + "/**/".length) === "/**/") {
identifiers.push(node);
}
}
visitChildren(node, visit);
});
identifiers.sort((x, y) => x.pos - y.pos);
return identifiers;
}
//# sourceMappingURL=test-server-host.js.map