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
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ export class LSAndTSDocResolver {
// Open it immediately to reduce rebuilds in the startup
// where multiple files and their dependencies
// being loaded in a short period of times
docManager.on('documentOpen', handleDocumentChange);
docManager.on('documentOpen', (document) => {
handleDocumentChange(document);
docManager.lockDocument(document.uri);
});
}

/**
Expand Down Expand Up @@ -121,17 +124,26 @@ export class LSAndTSDocResolver {
/**
* Updates snapshot path in all existing ts services and retrieves snapshot
*/
async updateSnapshotPath(oldPath: string, newPath: string): Promise<DocumentSnapshot> {
await this.deleteSnapshot(oldPath);
return this.getSnapshot(newPath);
async updateSnapshotPath(oldPath: string, newPath: string): Promise<void> {
for (const snapshot of this.globalSnapshotsManager.getByPrefix(oldPath)) {
await this.deleteSnapshot(snapshot.filePath);
}
// This may not be a file but a directory, still try
await this.getSnapshot(newPath);
}

/**
* Deletes snapshot in all existing ts services
*/
async deleteSnapshot(filePath: string) {
await forAllServices((service) => service.deleteSnapshot(filePath));
this.docManager.releaseDocument(pathToUrl(filePath));
const uri = pathToUrl(filePath);
if (this.docManager.get(uri)) {
// Guard this call, due to race conditions it may already have been closed;
// also this may not be a Svelte file
this.docManager.closeDocument(uri);
}
this.docManager.releaseDocument(uri);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ export class GlobalSnapshotsManager {
return this.documents.get(fileName);
}

getByPrefix(path: string) {
path = normalizePath(path);
return Array.from(this.documents.entries())
.filter((doc) => doc[0].startsWith(path))
.map((doc) => doc[1]);
}

set(fileName: string, document: DocumentSnapshot) {
fileName = normalizePath(fileName);
this.documents.set(fileName, document);
Expand Down Expand Up @@ -112,6 +119,7 @@ export class SnapshotManager {
// and set them "manually" in the set/update methods.
if (!document) {
this.documents.delete(fileName);
this.projectFiles = this.projectFiles.filter((s) => s !== fileName);
} else if (this.documents.has(fileName)) {
this.documents.set(fileName, document);
}
Expand Down Expand Up @@ -169,7 +177,6 @@ export class SnapshotManager {

delete(fileName: string): void {
fileName = normalizePath(fileName);
this.projectFiles = this.projectFiles.filter((s) => s !== fileName);
this.globalSnapshotsManager.delete(fileName);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ import {
scriptElementKindToCompletionItemKind
} from '../utils';
import { getJsDocTemplateCompletion } from './getJsDocTemplateCompletion';
import { findContainingNode, getComponentAtPosition, isPartOfImportStatement } from './utils';
import {
findContainingNode,
getComponentAtPosition,
isKitTypePath,
isPartOfImportStatement
} from './utils';

export interface CompletionEntryWithIdentifier extends ts.CompletionEntry, TextDocumentIdentifier {
position: Position;
Expand Down Expand Up @@ -271,7 +276,7 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
if (basename(filePath).startsWith('+')) {
const $typeImports = new Map<string, CompletionItem>();
for (const c of completionItems) {
if (c.data?.source?.includes('.svelte-kit/types')) {
if (isKitTypePath(c.data?.source)) {
$typeImports.set(c.label, c);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path';
import {
OptionalVersionedTextDocumentIdentifier,
TextDocumentEdit,
Expand All @@ -9,7 +10,7 @@ import { urlToPath } from '../../../utils';
import { FileRename, UpdateImportsProvider } from '../../interfaces';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { convertRange } from '../utils';
import { SnapshotMap } from './utils';
import { isKitTypePath, SnapshotMap } from './utils';

export class UpdateImportsProviderImpl implements UpdateImportsProvider {
constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {}
Expand All @@ -24,17 +25,51 @@ export class UpdateImportsProviderImpl implements UpdateImportsProvider {

const ls = await this.getLSForPath(newPath);
// `getEditsForFileRename` might take a while
const fileChanges = ls.getEditsForFileRename(oldPath, newPath, {}, {});
const fileChanges = ls
.getEditsForFileRename(oldPath, newPath, {}, {})
// Assumption: Updating imports will not create new files, and to make sure just filter those out
// who - for whatever reason - might be new ones.
.filter((change) => !change.isNewFile || change.fileName === oldPath);

await this.lsAndTsDocResolver.updateSnapshotPath(oldPath, newPath);

const editInOldPath = fileChanges.find(
(change) =>
change.fileName.startsWith(oldPath) &&
(oldPath.includes(newPath) || !change.fileName.startsWith(newPath))
);
const editInNewPath = fileChanges.find(
(change) =>
change.fileName.startsWith(newPath) &&
(newPath.includes(oldPath) || !change.fileName.startsWith(oldPath))
);
const updateImportsChanges = fileChanges
// Assumption: Updating imports will not create new files, and to make sure just filter those out
// who - for whatever reason - might be new ones.
.filter((change) => !change.isNewFile || change.fileName === oldPath)
// The language service might want to do edits to the old path, not the new path -> rewire it.
// If there is a better solution for this, please file a PR :)
.filter((change) => {
if (isKitTypePath(change.fileName)) {
// These types are generated from the route files, so we don't want to update them
return false;
}
if (!editInOldPath || !editInNewPath) {
return true;
}
// If both present, take the one that has more text changes to it (more likely to be the correct one)
return editInOldPath.textChanges.length > editInNewPath.textChanges.length
? change !== editInNewPath
: change !== editInOldPath;
})
.map((change) => {
change.fileName = change.fileName.replace(oldPath, newPath);
if (change === editInOldPath) {
// The language service might want to do edits to the old path, not the new path -> rewire it.
// If there is a better solution for this, please file a PR :)
change.fileName = change.fileName.replace(oldPath, newPath);
}
change.textChanges = change.textChanges.filter(
(textChange) =>
// Filter out changes to './$type' imports for Kit route files,
// you'll likely want these to stay as-is
!isKitTypePath(textChange.newText) ||
!path.basename(change.fileName).startsWith('+')
);
return change;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,7 @@ function gatherDescendants<T extends ts.Node>(
}

export const gatherIdentifiers = (node: ts.Node) => gatherDescendants(node, ts.isIdentifier);

export function isKitTypePath(path?: string): boolean {
return !!path?.includes('.svelte-kit/types');
}