提交 66154fa1 编写于 作者: D Daniel Imms

Allow nsfw file watcher to work alongside others

上级 6645b793
......@@ -538,6 +538,7 @@ export interface IFilesConfiguration {
autoSaveDelay: number;
eol: string;
hotExit: string;
useNsfwFileWatcher: boolean;
};
}
......
......@@ -24,6 +24,7 @@ exports.collectModules = function (excludes) {
createModuleDescription('vs/workbench/services/search/node/searchApp', []),
createModuleDescription('vs/workbench/services/search/node/worker/searchWorkerApp', []),
createModuleDescription('vs/workbench/services/files/node/watcher/unix/watcherApp', []),
createModuleDescription('vs/workbench/services/files/node/watcher/nsfw/watcherApp', []),
createModuleDescription('vs/workbench/node/extensionHostProcess', []),
......
......@@ -275,6 +275,11 @@ configurationRegistry.registerConfiguration({
],
'description': nls.localize('hotExit', "Controls whether unsaved files are remembered between sessions, allowing the save prompt when exiting the editor to be skipped.", HotExitConfiguration.ON_EXIT, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE)
},
'files.useNsfwFileWatcher': {
'type': 'boolean',
'default': false,
'description': nls.localize('useNsfwFileWatcher', "Use the new experimental file watcher utilizing the nsfw library.")
},
'files.defaultLanguage': {
'type': 'string',
'description': nls.localize('defaultLanguage', "The default language mode that is assigned to new files.")
......
......@@ -85,11 +85,12 @@ export class FileService implements IFileService {
encodingOverride,
watcherIgnoredPatterns,
verboseLogging: environmentService.verbose,
useNsfwFileWatcher: configuration.files.useNsfwFileWatcher
};
// create service
const workspace = this.contextService.getWorkspace();
this.raw = new NodeFileService(workspace ? workspace.resource.fsPath : void 0, fileServiceConfig);
this.raw = new NodeFileService(workspace ? workspace.resource.fsPath : void 0, fileServiceConfig, contextService);
// Listeners
this.registerListeners();
......
......@@ -26,6 +26,7 @@ import uri from 'vs/base/common/uri';
import nls = require('vs/nls');
import { isWindows, isLinux } from 'vs/base/common/platform';
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import pfs = require('vs/base/node/pfs');
import encoding = require('vs/base/node/encoding');
......@@ -35,6 +36,7 @@ import { FileWatcher as UnixWatcherService } from 'vs/workbench/services/files/n
import { FileWatcher as WindowsWatcherService } from 'vs/workbench/services/files/node/watcher/win32/watcherService';
import { toFileChangesEvent, normalize, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common';
import Event, { Emitter } from 'vs/base/common/event';
import { FileWatcher as NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/watcherService';
export interface IEncodingOverride {
resource: uri;
......@@ -51,6 +53,7 @@ export interface IFileServiceOptions {
watcherIgnoredPatterns?: string[];
disableWatcher?: boolean;
verboseLogging?: boolean;
useNsfwFileWatcher?: boolean;
}
function etag(stat: fs.Stats): string;
......@@ -90,7 +93,11 @@ export class FileService implements IFileService {
private fileChangesWatchDelayer: ThrottledDelayer<void>;
private undeliveredRawFileChangesEvents: IRawFileChange[];
constructor(basePath: string, options: IFileServiceOptions) {
constructor(
basePath: string,
options: IFileServiceOptions,
private contextService: IWorkspaceContextService
) {
this.toDispose = [];
this.basePath = basePath ? paths.normalize(basePath) : void 0;
......@@ -120,10 +127,15 @@ export class FileService implements IFileService {
}
if (this.basePath && !this.options.disableWatcher) {
if (isWindows) {
this.setupWin32WorkspaceWatching();
console.log(this.options);
if (this.options.useNsfwFileWatcher) {
this.setupNsfwWorkspceWatching();
} else {
this.setupUnixWorkspaceWatching();
if (isWindows) {
this.setupWin32WorkspaceWatching();
} else {
this.setupUnixWorkspaceWatching();
}
}
}
......@@ -154,6 +166,11 @@ export class FileService implements IFileService {
this.toDispose.push(toDisposable(new UnixWatcherService(this.basePath, this.options.watcherIgnoredPatterns, e => this._onFileChanges.fire(e), this.options.errorLogger, this.options.verboseLogging).startWatching()));
}
private setupNsfwWorkspceWatching(): void {
const service = new NsfwWatcherService(this.basePath, this.options.watcherIgnoredPatterns, e => this._onFileChanges.fire(e), this.options.errorLogger, this.options.verboseLogging);
this.toDispose.push(toDisposable(service.startWatching()));
}
public resolveFile(resource: uri, options?: IResolveFileOptions): TPromise<IFileStat> {
return this.resolve(resource, options);
}
......
......@@ -6,9 +6,9 @@
import * as glob from 'vs/base/common/glob';
import * as path from 'path';
import * as watcher from 'vs/workbench/services/files/node/watcher/common';
import nsfw = require('nsfw');
import * as nsfw from 'nsfw';
import { IWatcherService, IWatcherRequest } from 'vs/workbench/services/files/node/watcher/unix/watcher';
import { TPromise } from "vs/base/common/winjs.base";
import { TPromise } from 'vs/base/common/winjs.base';
import { ThrottledDelayer } from 'vs/base/common/async';
import { FileChangeType } from 'vs/platform/files/common/files';
......@@ -20,16 +20,11 @@ nsfwActionToRawChangeType[nsfw.actions.DELETED] = FileChangeType.DELETED;
export class NsfwWatcherService implements IWatcherService {
private static FS_EVENT_DELAY = 50; // aggregate and only emit events when changes have stopped for this duration (in ms)
private _ignored: string[];
constructor() {}
public watch(request: IWatcherRequest): TPromise<void> {
if (request.verboseLogging) {
console.log('request', request);
}
this._ignored = request.ignored;
let undeliveredFileEvents: watcher.IRawFileChange[] = [];
const fileEventDelayer = new ThrottledDelayer(NsfwWatcherService.FS_EVENT_DELAY);
......@@ -47,16 +42,16 @@ export class NsfwWatcherService implements IWatcherService {
if (e.action === nsfw.actions.RENAMED) {
// Rename fires when a file's name changes within a single directory
absolutePath = path.join(e.directory, e.oldFile);
if (!this._isPathIgnored(absolutePath)) {
if (!this._isPathIgnored(absolutePath, request.ignored)) {
undeliveredFileEvents.push({ type: FileChangeType.DELETED, path: absolutePath });
}
absolutePath = path.join(e.directory, e.newFile);
if (!this._isPathIgnored(absolutePath)) {
if (!this._isPathIgnored(absolutePath, request.ignored)) {
undeliveredFileEvents.push({ type: FileChangeType.ADDED, path: absolutePath });
}
} else {
absolutePath = path.join(e.directory, e.file);
if (!this._isPathIgnored(absolutePath)) {
if (!this._isPathIgnored(absolutePath, request.ignored)) {
undeliveredFileEvents.push({
type: nsfwActionToRawChangeType[e.action],
path: absolutePath
......@@ -89,7 +84,7 @@ export class NsfwWatcherService implements IWatcherService {
});
}
private _isPathIgnored(absolutePath: string): boolean {
return this._ignored && this._ignored.some(ignore => glob.match(ignore, absolutePath));
private _isPathIgnored(absolutePath: string, ignored: string[]): boolean {
return ignored && ignored.some(ignore => glob.match(ignore, absolutePath));
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
export interface IWatcherRequest {
basePath: string;
ignored: string[];
verboseLogging: boolean;
}
export interface IWatcherService {
watch(request: IWatcherRequest): TPromise<void>;
}
export interface IFileWatcher {
startWatching(): () => void;
addFolder(folder: string): void;
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { WatcherChannel } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc';
import { NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService';
const server = new Server();
const service = new NsfwWatcherService();
const channel = new WatcherChannel(service);
server.registerChannel('watcher', channel);
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IWatcherRequest, IWatcherService } from './watcher';
export interface IWatcherChannel extends IChannel {
call(command: 'watch', request: IWatcherRequest): TPromise<void>;
call(command: string, arg: any): TPromise<any>;
}
export class WatcherChannel implements IWatcherChannel {
constructor(private service: IWatcherService) { }
call(command: string, arg: any): TPromise<any> {
switch (command) {
case 'watch': return this.service.watch(arg);
}
return undefined;
}
}
export class WatcherChannelClient implements IWatcherService {
constructor(private channel: IWatcherChannel) { }
watch(request: IWatcherRequest): TPromise<void> {
return this.channel.call('watch', request);
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc';
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
import uri from 'vs/base/common/uri';
import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common';
import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc';
import { FileChangesEvent } from 'vs/platform/files/common/files';
import { IFileWatcher } from "vs/workbench/services/files/node/watcher/unix/watcher";
export class FileWatcher implements IFileWatcher {
private static MAX_RESTARTS = 5;
private isDisposed: boolean;
private restartCounter: number;
constructor(
private basePath: string,
private ignored: string[],
private onFileChanges: (changes: FileChangesEvent) => void,
private errorLogger: (msg: string) => void,
private verboseLogging: boolean
) {
this.isDisposed = false;
this.restartCounter = 0;
}
public startWatching(): () => void {
const args = ['--type=watcherService'];
const client = new Client(
uri.parse(require.toUrl('bootstrap')).fsPath,
{
serverName: 'Watcher',
args,
env: {
AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/nsfw/watcherApp',
PIPE_LOGGING: 'true',
VERBOSE_LOGGING: this.verboseLogging
}
}
);
const channel = getNextTickChannel(client.getChannel<IWatcherChannel>('watcher'));
const service = new WatcherChannelClient(channel);
// Start watching
service.watch({ basePath: this.basePath, ignored: this.ignored, verboseLogging: this.verboseLogging }).then(null, (err) => {
if (!(err instanceof Error && err.name === 'Canceled' && err.message === 'Canceled')) {
return TPromise.wrapError(err); // the service lib uses the promise cancel error to indicate the process died, we do not want to bubble this up
}
return undefined;
}, (events: IRawFileChange[]) => this.onRawFileEvents(events)).done(() => {
// our watcher app should never be completed because it keeps on watching. being in here indicates
// that the watcher process died and we want to restart it here. we only do it a max number of times
if (!this.isDisposed) {
if (this.restartCounter <= FileWatcher.MAX_RESTARTS) {
this.errorLogger('[FileWatcher] terminated unexpectedly and is restarted again...');
this.restartCounter++;
// TODO: What do we do for multi-root here?
this.startWatching();
} else {
this.errorLogger('[FileWatcher] failed to start after retrying for some time, giving up. Please report this as a bug report!');
}
}
}, this.errorLogger);
return () => {
client.dispose();
this.isDisposed = true;
};
}
public addFolder(folder: string): void {
console.log('addFolder: ' + folder);
}
private onRawFileEvents(events: IRawFileChange[]): void {
// Emit through broadcast service
if (events.length > 0) {
this.onFileChanges(toFileChangesEvent(events));
}
}
}
\ No newline at end of file
......@@ -16,3 +16,8 @@ export interface IWatcherRequest {
export interface IWatcherService {
watch(request: IWatcherRequest): TPromise<void>;
}
export interface IFileWatcher {
startWatching(): () => void;
addFolder(folder: string): void;
}
\ No newline at end of file
......@@ -6,9 +6,9 @@
import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { WatcherChannel } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc';
import { NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService';
import { ChokidarWatcherService } from 'vs/workbench/services/files/node/watcher/unix/chokidarWatcherService';
const server = new Server();
const service = new NsfwWatcherService();
const service = new ChokidarWatcherService();
const channel = new WatcherChannel(service);
server.registerChannel('watcher', channel);
\ No newline at end of file
......@@ -12,8 +12,9 @@ import uri from 'vs/base/common/uri';
import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common';
import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc';
import { FileChangesEvent } from 'vs/platform/files/common/files';
import { IFileWatcher } from "vs/workbench/services/files/node/watcher/unix/watcher";
export class FileWatcher {
export class FileWatcher implements IFileWatcher {
private static MAX_RESTARTS = 5;
private isDisposed: boolean;
......@@ -63,6 +64,7 @@ export class FileWatcher {
if (this.restartCounter <= FileWatcher.MAX_RESTARTS) {
this.errorLogger('[FileWatcher] terminated unexpectedly and is restarted again...');
this.restartCounter++;
// TODO: What do we do for multi-root here?
this.startWatching();
} else {
this.errorLogger('[FileWatcher] failed to start after retrying for some time, giving up. Please report this as a bug report!');
......@@ -76,6 +78,10 @@ export class FileWatcher {
};
}
public addFolder(folder: string): void {
console.log('addFolder: ' + folder);
}
private onRawFileEvents(events: IRawFileChange[]): void {
// Emit through broadcast service
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册