svelte-language-server
Version:
A language server for Svelte
238 lines • 10.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ignoredBuildDirectories = exports.SnapshotManager = exports.GlobalSnapshotsManager = void 0;
const typescript_1 = __importDefault(require("typescript"));
const DocumentSnapshot_1 = require("./DocumentSnapshot");
const logger_1 = require("../../logger");
const utils_1 = require("../../utils");
const events_1 = require("events");
const fileCollection_1 = require("../../lib/documents/fileCollection");
/**
* Every snapshot corresponds to a unique file on disk.
* A snapshot can be part of multiple projects, but for a given file path
* there can be only one snapshot.
*/
class GlobalSnapshotsManager {
constructor(tsSystem) {
this.tsSystem = tsSystem;
this.emitter = new events_1.EventEmitter();
this.documents = new fileCollection_1.FileMap(tsSystem.useCaseSensitiveFileNames);
this.getCanonicalFileName = (0, utils_1.createGetCanonicalFileName)(tsSystem.useCaseSensitiveFileNames);
}
get(fileName) {
fileName = (0, utils_1.normalizePath)(fileName);
return this.documents.get(fileName);
}
getByPrefix(path) {
path = this.getCanonicalFileName((0, utils_1.normalizePath)(path));
return Array.from(this.documents.entries())
.filter((doc) => doc[0].startsWith(path))
.map((doc) => doc[1]);
}
set(fileName, document) {
fileName = (0, utils_1.normalizePath)(fileName);
this.documents.set(fileName, document);
this.emitter.emit('change', fileName, document);
}
delete(fileName) {
fileName = (0, utils_1.normalizePath)(fileName);
this.documents.delete(fileName);
this.emitter.emit('change', fileName, undefined);
}
updateTsOrJsFile(fileName, changes) {
fileName = (0, utils_1.normalizePath)(fileName);
const previousSnapshot = this.get(fileName);
if (changes) {
if (!(previousSnapshot instanceof DocumentSnapshot_1.JSOrTSDocumentSnapshot)) {
return;
}
previousSnapshot.update(changes);
this.emitter.emit('change', fileName, previousSnapshot);
return previousSnapshot;
}
else {
const newSnapshot = DocumentSnapshot_1.DocumentSnapshot.fromNonSvelteFilePath(fileName, this.tsSystem);
if (previousSnapshot) {
newSnapshot.version = previousSnapshot.version + 1;
}
else {
// ensure it's greater than initial version
// so that ts server picks up the change
newSnapshot.version += 1;
}
this.set(fileName, newSnapshot);
return newSnapshot;
}
}
onChange(listener) {
this.emitter.on('change', listener);
}
removeChangeListener(listener) {
this.emitter.off('change', listener);
}
}
exports.GlobalSnapshotsManager = GlobalSnapshotsManager;
/**
* Should only be used by `service.ts`
*/
class SnapshotManager {
constructor(globalSnapshotsManager, fileSpec, workspaceRoot, tsSystem, projectFiles, wildcardDirectories) {
this.globalSnapshotsManager = globalSnapshotsManager;
this.fileSpec = fileSpec;
this.workspaceRoot = workspaceRoot;
this.tsSystem = tsSystem;
this.lastLogged = new Date(new Date().getTime() - 60_001);
this.watchExtensions = [
typescript_1.default.Extension.Dts,
typescript_1.default.Extension.Dcts,
typescript_1.default.Extension.Dmts,
typescript_1.default.Extension.Js,
typescript_1.default.Extension.Cjs,
typescript_1.default.Extension.Mjs,
typescript_1.default.Extension.Jsx,
typescript_1.default.Extension.Ts,
typescript_1.default.Extension.Mts,
typescript_1.default.Extension.Cts,
typescript_1.default.Extension.Tsx,
typescript_1.default.Extension.Json,
'.svelte'
];
this.onSnapshotChange = this.onSnapshotChange.bind(this);
this.globalSnapshotsManager.onChange(this.onSnapshotChange);
this.documents = new fileCollection_1.FileMap(tsSystem.useCaseSensitiveFileNames);
this.projectFileToOriginalCasing = new Map();
this.getCanonicalFileName = (0, utils_1.createGetCanonicalFileName)(tsSystem.useCaseSensitiveFileNames);
projectFiles.forEach((originalCasing) => this.projectFileToOriginalCasing.set(this.getCanonicalFileName(originalCasing), originalCasing));
this.watchingCanonicalDirectories = new Map(Object.entries(wildcardDirectories ?? {}).map(([dir, flags]) => [
this.getCanonicalFileName(dir),
flags
]));
}
onSnapshotChange(fileName, document) {
// Only delete/update snapshots, don't add new ones,
// as they could be from another TS service and this
// snapshot manager can't reach this file.
// For these, instead wait on a `get` method invocation
// and set them "manually" in the set/update methods.
if (!document) {
this.documents.delete(fileName);
this.projectFileToOriginalCasing.delete(this.getCanonicalFileName(fileName));
}
else if (this.documents.has(fileName)) {
this.documents.set(fileName, document);
}
}
areIgnoredFromNewFileWatch(watcherNewFiles) {
const { include } = this.fileSpec;
// Since we default to not include anything,
// just don't waste time on this
if (include?.length === 0 || !this.watchingCanonicalDirectories) {
return true;
}
for (const newFile of watcherNewFiles) {
const path = this.getCanonicalFileName((0, utils_1.normalizePath)(newFile));
if (this.projectFileToOriginalCasing.has(path)) {
continue;
}
for (const [dir, flags] of this.watchingCanonicalDirectories) {
if (path.startsWith(dir)) {
if (!(flags & typescript_1.default.WatchDirectoryFlags.Recursive)) {
const relative = path.slice(dir.length);
if (relative.includes('/')) {
continue;
}
}
return false;
}
}
}
return true;
}
updateProjectFiles() {
const { include, exclude } = this.fileSpec;
if (include?.length === 0) {
return;
}
const projectFiles = this.tsSystem
.readDirectory(this.workspaceRoot, this.watchExtensions, exclude, include)
.map(utils_1.normalizePath);
projectFiles.forEach((projectFile) => this.projectFileToOriginalCasing.set(this.getCanonicalFileName(projectFile), projectFile));
}
updateTsOrJsFile(fileName, changes) {
const snapshot = this.globalSnapshotsManager.updateTsOrJsFile(fileName, changes);
// This isn't duplicated logic to the listener, because this could
// be a new snapshot which the listener wouldn't add.
if (snapshot) {
this.documents.set((0, utils_1.normalizePath)(fileName), snapshot);
}
}
has(fileName) {
fileName = (0, utils_1.normalizePath)(fileName);
return (this.projectFileToOriginalCasing.has(this.getCanonicalFileName(fileName)) ||
this.documents.has(fileName));
}
set(fileName, snapshot) {
this.globalSnapshotsManager.set(fileName, snapshot);
// This isn't duplicated logic to the listener, because this could
// be a new snapshot which the listener wouldn't add.
this.documents.set((0, utils_1.normalizePath)(fileName), snapshot);
this.logStatistics();
}
get(fileName) {
fileName = (0, utils_1.normalizePath)(fileName);
let snapshot = this.documents.get(fileName);
if (!snapshot) {
snapshot = this.globalSnapshotsManager.get(fileName);
if (snapshot) {
this.documents.set(fileName, snapshot);
}
}
return snapshot;
}
delete(fileName) {
fileName = (0, utils_1.normalizePath)(fileName);
this.globalSnapshotsManager.delete(fileName);
}
getClientFileNames() {
return Array.from(this.documents.values())
.filter((doc) => doc.isOpenedInClient())
.map((doc) => doc.filePath);
}
getProjectFileNames() {
return Array.from(this.projectFileToOriginalCasing.values());
}
isProjectFile(fileName) {
fileName = (0, utils_1.normalizePath)(fileName);
return this.projectFileToOriginalCasing.has(this.getCanonicalFileName(fileName));
}
logStatistics() {
const date = new Date();
// Don't use setInterval because that will keep tests running forever
if (date.getTime() - this.lastLogged.getTime() > 60_000) {
this.lastLogged = date;
const allFiles = Array.from(new Set([...this.projectFileToOriginalCasing.keys(), ...this.documents.keys()]));
logger_1.Logger.log('SnapshotManager File Statistics:\n' +
`Project files: ${this.projectFileToOriginalCasing.size}\n` +
`Svelte files: ${allFiles.filter((name) => name.endsWith('.svelte')).length}\n` +
`From node_modules: ${allFiles.filter((name) => name.includes('node_modules')).length}\n` +
`Total: ${allFiles.length}`);
}
}
allFilesAreJsOrDts() {
for (const doc of this.documents.values()) {
if (doc.scriptKind === typescript_1.default.ScriptKind.TS || doc.scriptKind === typescript_1.default.ScriptKind.TSX) {
return false;
}
}
return true;
}
dispose() {
this.globalSnapshotsManager.removeChangeListener(this.onSnapshotChange);
}
}
exports.SnapshotManager = SnapshotManager;
exports.ignoredBuildDirectories = ['__sapper__', '.svelte-kit'];
//# sourceMappingURL=SnapshotManager.js.map