mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Fix markdown link diagnostics not updated when directories are renamed / deleted (#157956)
Fix markdown link diagnostics not updated when directories are renamed/deleted Turns our that `createFileSystemWatcher` will not fire if a parent dir is renamed / deleted. See #60813 To fix this, I believe we have to create watchers for all parent directories too (or watch everything in the entire workspace)
This commit is contained in:
parent
8c2fd550a3
commit
720a61fc28
|
@ -14,7 +14,7 @@ export const fs_readFile = new RequestType<{ uri: string }, number[], any>('mark
|
|||
export const fs_readDirectory = new RequestType<{ uri: string }, [string, { isDirectory: boolean }][], any>('markdown/fs/readDirectory');
|
||||
export const fs_stat = new RequestType<{ uri: string }, { isDirectory: boolean } | undefined, any>('markdown/fs/stat');
|
||||
|
||||
export const fs_watcher_create = new RequestType<{ id: number; uri: string; options: md.FileWatcherOptions }, void, any>('markdown/fs/watcher/create');
|
||||
export const fs_watcher_create = new RequestType<{ id: number; uri: string; options: md.FileWatcherOptions; watchParentDirs: boolean }, void, any>('markdown/fs/watcher/create');
|
||||
export const fs_watcher_delete = new RequestType<{ id: number }, void, any>('markdown/fs/watcher/delete');
|
||||
|
||||
export const findMarkdownFilesInWorkspace = new RequestType<{}, string[], any>('markdown/findMarkdownFilesInWorkspace');
|
||||
|
|
|
@ -236,6 +236,7 @@ export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching {
|
|||
id,
|
||||
uri: resource.toString(),
|
||||
options,
|
||||
watchParentDirs: true,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -5,10 +5,14 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import { BaseLanguageClient, LanguageClientOptions, NotebookDocumentSyncRegistrationType } from 'vscode-languageclient';
|
||||
import { disposeAll, IDisposable } from 'vscode-markdown-languageservice/out/util/dispose';
|
||||
import { ResourceMap } from 'vscode-markdown-languageservice/out/util/resourceMap';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Utils } from 'vscode-uri';
|
||||
import { IMdParser } from './markdownEngine';
|
||||
import * as proto from './protocol';
|
||||
import { looksLikeMarkdownPath, markdownFileExtensions } from './util/file';
|
||||
import { Schemes } from './util/schemes';
|
||||
import { IMdWorkspace } from './workspace';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
@ -92,19 +96,24 @@ export async function startClient(factory: LanguageClientConstructor, workspace:
|
|||
return (await vscode.workspace.findFiles(mdFileGlob, '**/node_modules/**')).map(x => x.toString());
|
||||
});
|
||||
|
||||
const watchers = new Map<number, vscode.FileSystemWatcher>();
|
||||
const watchers = new FileWatcherManager();
|
||||
|
||||
client.onRequest(proto.fs_watcher_create, async (params): Promise<void> => {
|
||||
const id = params.id;
|
||||
const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.Uri.parse(params.uri), '*'), params.options.ignoreCreate, params.options.ignoreChange, params.options.ignoreDelete);
|
||||
watchers.set(id, watcher);
|
||||
watcher.onDidCreate(() => { client.sendRequest(proto.fs_watcher_onChange, { id, uri: params.uri, kind: 'create' }); });
|
||||
watcher.onDidChange(() => { client.sendRequest(proto.fs_watcher_onChange, { id, uri: params.uri, kind: 'change' }); });
|
||||
watcher.onDidDelete(() => { client.sendRequest(proto.fs_watcher_onChange, { id, uri: params.uri, kind: 'delete' }); });
|
||||
const uri = vscode.Uri.parse(params.uri);
|
||||
|
||||
const sendWatcherChange = (kind: 'create' | 'change' | 'delete') => {
|
||||
client.sendRequest(proto.fs_watcher_onChange, { id, uri: params.uri, kind });
|
||||
};
|
||||
|
||||
watchers.create(id, uri, params.watchParentDirs, {
|
||||
create: params.options.ignoreCreate ? undefined : () => sendWatcherChange('create'),
|
||||
change: params.options.ignoreChange ? undefined : () => sendWatcherChange('change'),
|
||||
delete: params.options.ignoreDelete ? undefined : () => sendWatcherChange('delete'),
|
||||
});
|
||||
});
|
||||
|
||||
client.onRequest(proto.fs_watcher_delete, async (params): Promise<void> => {
|
||||
watchers.get(params.id)?.dispose();
|
||||
watchers.delete(params.id);
|
||||
});
|
||||
|
||||
|
@ -112,3 +121,91 @@ export async function startClient(factory: LanguageClientConstructor, workspace:
|
|||
|
||||
return client;
|
||||
}
|
||||
|
||||
type DirWatcherEntry = {
|
||||
readonly uri: vscode.Uri;
|
||||
readonly listeners: IDisposable[];
|
||||
};
|
||||
|
||||
class FileWatcherManager {
|
||||
|
||||
private readonly fileWatchers = new Map<number, {
|
||||
readonly watcher: vscode.FileSystemWatcher;
|
||||
readonly dirWatchers: DirWatcherEntry[];
|
||||
}>();
|
||||
|
||||
private readonly dirWatchers = new ResourceMap<{
|
||||
readonly watcher: vscode.FileSystemWatcher;
|
||||
refCount: number;
|
||||
}>();
|
||||
|
||||
create(id: number, uri: vscode.Uri, watchParentDirs: boolean, listeners: { create?: () => void; change?: () => void; delete?: () => void }): void {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*'), !listeners.create, !listeners.change, !listeners.delete);
|
||||
const parentDirWatchers: DirWatcherEntry[] = [];
|
||||
this.fileWatchers.set(id, { watcher, dirWatchers: parentDirWatchers });
|
||||
|
||||
if (listeners.create) { watcher.onDidCreate(listeners.create); }
|
||||
if (listeners.change) { watcher.onDidChange(listeners.change); }
|
||||
if (listeners.delete) { watcher.onDidDelete(listeners.delete); }
|
||||
|
||||
if (watchParentDirs && uri.scheme !== Schemes.untitled) {
|
||||
// We need to watch the parent directories too for when these are deleted / created
|
||||
for (let dirUri = Utils.dirname(uri); dirUri.path.length > 1; dirUri = Utils.dirname(dirUri)) {
|
||||
const dirWatcher: DirWatcherEntry = { uri: dirUri, listeners: [] };
|
||||
|
||||
let parentDirWatcher = this.dirWatchers.get(dirUri);
|
||||
if (!parentDirWatcher) {
|
||||
const glob = new vscode.RelativePattern(Utils.dirname(dirUri), Utils.basename(dirUri));
|
||||
const parentWatcher = vscode.workspace.createFileSystemWatcher(glob, !listeners.create, true, !listeners.delete);
|
||||
parentDirWatcher = { refCount: 0, watcher: parentWatcher };
|
||||
this.dirWatchers.set(dirUri, parentDirWatcher);
|
||||
}
|
||||
parentDirWatcher.refCount++;
|
||||
|
||||
if (listeners.create) {
|
||||
dirWatcher.listeners.push(parentDirWatcher.watcher.onDidCreate(async () => {
|
||||
// Just because the parent dir was created doesn't mean our file was created
|
||||
try {
|
||||
const stat = await vscode.workspace.fs.stat(uri);
|
||||
if (stat.type === vscode.FileType.File) {
|
||||
listeners.create!();
|
||||
}
|
||||
} catch {
|
||||
// Noop
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (listeners.delete) {
|
||||
// When the parent dir is deleted, consider our file deleted too
|
||||
|
||||
// TODO: this fires if the file previously did not exist and then the parent is deleted
|
||||
dirWatcher.listeners.push(parentDirWatcher.watcher.onDidDelete(listeners.delete));
|
||||
}
|
||||
|
||||
parentDirWatchers.push(dirWatcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete(id: number): void {
|
||||
const entry = this.fileWatchers.get(id);
|
||||
if (entry) {
|
||||
for (const dirWatcher of entry.dirWatchers) {
|
||||
disposeAll(dirWatcher.listeners);
|
||||
|
||||
const dirWatcherEntry = this.dirWatchers.get(dirWatcher.uri);
|
||||
if (dirWatcherEntry) {
|
||||
if (--dirWatcherEntry.refCount <= 0) {
|
||||
dirWatcherEntry.watcher.dispose();
|
||||
this.dirWatchers.delete(dirWatcher.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entry.watcher.dispose();
|
||||
}
|
||||
|
||||
this.fileWatchers.delete(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import Token = require('markdown-it/lib/token');
|
||||
import type Token = require('markdown-it/lib/token');
|
||||
import { RequestType } from 'vscode-languageclient';
|
||||
import type * as lsp from 'vscode-languageserver-types';
|
||||
import type * as md from 'vscode-markdown-languageservice';
|
||||
|
@ -15,7 +15,7 @@ export const fs_readFile = new RequestType<{ uri: string }, number[], any>('mark
|
|||
export const fs_readDirectory = new RequestType<{ uri: string }, [string, { isDirectory: boolean }][], any>('markdown/fs/readDirectory');
|
||||
export const fs_stat = new RequestType<{ uri: string }, { isDirectory: boolean } | undefined, any>('markdown/fs/stat');
|
||||
|
||||
export const fs_watcher_create = new RequestType<{ id: number; uri: string; options: md.FileWatcherOptions }, void, any>('markdown/fs/watcher/create');
|
||||
export const fs_watcher_create = new RequestType<{ id: number; uri: string; options: md.FileWatcherOptions; watchParentDirs: boolean }, void, any>('markdown/fs/watcher/create');
|
||||
export const fs_watcher_delete = new RequestType<{ id: number }, void, any>('markdown/fs/watcher/delete');
|
||||
|
||||
export const findMarkdownFilesInWorkspace = new RequestType<{}, string[], any>('markdown/findMarkdownFilesInWorkspace');
|
||||
|
|
Loading…
Reference in a new issue