untitledEditorService.ts 5.2 KB
Newer Older
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  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 URI from 'vs/base/common/uri';
8
import {createDecorator, IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
9 10 11
import {EventType} from 'vs/base/common/events';
import arrays = require('vs/base/common/arrays');
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
12

B
Benjamin Pasero 已提交
13
export const IUntitledEditorService = createDecorator<IUntitledEditorService>('untitledEditorService');
14 15 16

export interface IUntitledEditorService {

17
	_serviceBrand: any;
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

	/**
	 * Returns the untitled editor input matching the provided resource.
	 */
	get(resource: URI): UntitledEditorInput;

	/**
	 * Returns all untitled editor inputs.
	 */
	getAll(resources?: URI[]): UntitledEditorInput[];

	/**
	 * Returns dirty untitled editors as resource URIs.
	 */
	getDirty(): URI[];

	/**
	 * Returns true iff the provided resource is dirty.
	 */
	isDirty(resource: URI): boolean;

39 40 41 42 43
	/**
	 * Reverts the untitled resources if found.
	 */
	revertAll(resources?: URI[]): URI[];

44 45 46 47 48 49 50 51 52 53 54 55 56
	/**
	 * Creates a new untitled input with the optional resource URI or returns an existing one
	 * if the provided resource exists already as untitled input.
	 *
	 * It is valid to pass in a file resource. In that case the path will be used as identifier.
	 * The use case is to be able to create a new file with a specific path with VSCode.
	 */
	createOrGet(resource?: URI, modeId?: string): UntitledEditorInput;

	/**
	 * A check to find out if a untitled resource has a file path associated or not.
	 */
	hasAssociatedFilePath(resource: URI): boolean;
57 58 59
}

export class UntitledEditorService implements IUntitledEditorService {
60
	public _serviceBrand: any;
61 62 63 64

	private static CACHE: { [resource: string]: UntitledEditorInput } = Object.create(null);
	private static KNOWN_ASSOCIATED_FILE_PATHS: { [resource: string]: boolean } = Object.create(null);

65
	constructor(@IInstantiationService private instantiationService: IInstantiationService) {
66 67 68 69 70 71 72 73 74 75 76 77 78 79
	}

	public get(resource: URI): UntitledEditorInput {
		return UntitledEditorService.CACHE[resource.toString()];
	}

	public getAll(resources?: URI[]): UntitledEditorInput[] {
		if (resources) {
			return arrays.coalesce(resources.map((r) => this.get(r)));
		}

		return Object.keys(UntitledEditorService.CACHE).map((key) => UntitledEditorService.CACHE[key]);
	}

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
	public revertAll(resources?: URI[], force?: boolean): URI[] {
		const reverted: URI[] = [];

		const untitledInputs = this.getAll(resources);
		untitledInputs.forEach(input => {
			if (input) {
				input.revert();
				input.dispose();

				reverted.push(input.getResource());
			}
		});

		return reverted;
	}

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
	public isDirty(resource: URI): boolean {
		let input = this.get(resource);

		return input && input.isDirty();
	}

	public getDirty(): URI[] {
		return Object.keys(UntitledEditorService.CACHE)
			.map((key) => UntitledEditorService.CACHE[key])
			.filter((i) => i.isDirty())
			.map((i) => i.getResource());
	}

	public createOrGet(resource?: URI, modeId?: string): UntitledEditorInput {
		let hasAssociatedFilePath = false;
		if (resource) {
			hasAssociatedFilePath = (resource.scheme === 'file');
			resource = this.resourceToUntitled(resource); // ensure we have the right scheme

			if (hasAssociatedFilePath) {
				UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS[resource.toString()] = true; // remember for future lookups
			}
		}

		// Return existing instance if asked for it
		if (resource && UntitledEditorService.CACHE[resource.toString()]) {
			return UntitledEditorService.CACHE[resource.toString()];
		}

		// Create new otherwise
		return this.doCreate(resource, hasAssociatedFilePath, modeId);
	}

	private doCreate(resource?: URI, hasAssociatedFilePath?: boolean, modeId?: string): UntitledEditorInput {
		if (!resource) {

			// Create new taking a resource URI that is not already taken
			let counter = Object.keys(UntitledEditorService.CACHE).length + 1;
			do {
135
				resource = URI.from({ scheme: UntitledEditorInput.SCHEMA, path: 'Untitled-' + counter });
136 137 138 139 140 141 142
				counter++;
			} while (Object.keys(UntitledEditorService.CACHE).indexOf(resource.toString()) >= 0);
		}

		let input = this.instantiationService.createInstance(UntitledEditorInput, resource, hasAssociatedFilePath, modeId);

		// Remove from cache on dispose
A
Alex Dima 已提交
143
		input.addOneTimeDisposableListener(EventType.DISPOSE, () => {
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
			delete UntitledEditorService.CACHE[input.getResource().toString()];
			delete UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS[input.getResource().toString()];
		});

		// Add to cache
		UntitledEditorService.CACHE[resource.toString()] = input;

		return input;
	}

	private resourceToUntitled(resource: URI): URI {
		if (resource.scheme === UntitledEditorInput.SCHEMA) {
			return resource;
		}

159
		return URI.from({ scheme: UntitledEditorInput.SCHEMA, path: resource.fsPath });
160 161 162 163 164
	}

	public hasAssociatedFilePath(resource: URI): boolean {
		return !!UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS[resource.toString()];
	}
165
}