editorStatus.ts 28.1 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8 9
/*---------------------------------------------------------------------------------------------
 *  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 'vs/css!./media/editorstatus';
import nls = require('vs/nls');
10
import {TPromise} from 'vs/base/common/winjs.base';
A
Alex Dima 已提交
11
import { emmet as $, append } from 'vs/base/browser/dom';
E
Erich Gamma 已提交
12 13 14 15 16 17 18
import encoding = require('vs/base/common/bits/encoding');
import strings = require('vs/base/common/strings');
import types = require('vs/base/common/types');
import uri from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import {IStatusbarItem} from 'vs/workbench/browser/parts/statusbar/statusbar';
import {Action} from 'vs/base/common/actions';
19
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
B
Benjamin Pasero 已提交
20 21
import {IFileEditorInput, EncodingMode, IEncodingSupport, asFileEditorInput, getUntitledOrFileResource} from 'vs/workbench/common/editor';
import {IDisposable, combinedDispose} from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
22
import {ICommonCodeEditor} from 'vs/editor/common/editorCommon';
23
import {ICodeEditor, IDiffEditor} from 'vs/editor/browser/editorBrowser';
E
Erich Gamma 已提交
24
import {EndOfLineSequence, ITokenizedModel, EditorType, IEditorSelection, ITextModel, IDiffEditorModel, IEditor} from 'vs/editor/common/editorCommon';
25
import {ChangeIndentationSizeAction, IndentationToSpacesAction, IndentationToTabsAction} from 'vs/editor/contrib/indentation/common/indentation';
26
import {EventType, ResourceEvent, EditorEvent, TextEditorSelectionEvent} from 'vs/workbench/common/events';
E
Erich Gamma 已提交
27
import {BaseTextEditor} from 'vs/workbench/browser/parts/editor/textEditor';
28
import {IEditor as IBaseEditor} from 'vs/platform/editor/common/editor';
E
Erich Gamma 已提交
29
import {IWorkbenchEditorService}  from 'vs/workbench/services/editor/common/editorService';
30
import {IQuickOpenService, IPickOpenEntry} from 'vs/workbench/services/quickopen/common/quickOpenService';
E
Erich Gamma 已提交
31 32 33 34 35
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {IEventService} from 'vs/platform/event/common/event';
import {IFilesConfiguration} from 'vs/platform/files/common/files';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IModeService} from 'vs/editor/common/services/modeService';
A
Alex Dima 已提交
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
import {StyleMutator} from 'vs/base/browser/styleMutator';

function getCodeEditor(e: IBaseEditor): ICommonCodeEditor {
	if (e instanceof BaseTextEditor) {
		let editorWidget = e.getControl();
		if (editorWidget.getEditorType() === EditorType.IDiffEditor) {
			return (<IDiffEditor>editorWidget).getModifiedEditor();
		}
		if (editorWidget.getEditorType() === EditorType.ICodeEditor) {
			return (<ICodeEditor>editorWidget);
		}
		return null;
	}
	return null;
}
E
Erich Gamma 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

function getTextModel(editorWidget: IEditor): ITextModel {
	let textModel: ITextModel;

	// Support for diff
	let model = editorWidget.getModel();
	if (model && !!(<IDiffEditorModel>model).modified) {
		textModel = (<IDiffEditorModel>model).modified;
	}

	// Normal editor
	else {
		textModel = <ITextModel>model;
	}

	return textModel;
}

69 70 71 72 73 74 75 76
function asFileOrUntitledEditorInput(input: any): UntitledEditorInput|IFileEditorInput {
	if (input instanceof UntitledEditorInput) {
		return input;
	}

	return asFileEditorInput(input, true /* support diff editor */);
}

E
Erich Gamma 已提交
77 78 79 80 81
interface IEditorSelectionStatus {
	selections?: IEditorSelection[];
	charactersSelected?: number;
}

82
interface IStateChange {
I
isidor 已提交
83
	indentation: boolean;
84 85 86 87
	selectionStatus: boolean;
	mode: boolean;
	encoding: boolean;
	EOL: boolean;
E
Erich Gamma 已提交
88 89 90
	tabFocusMode: boolean;
}

91 92 93 94 95
interface StateDelta {
	selectionStatus?: string;
	mode?: string;
	encoding?: string;
	EOL?: string;
I
isidor 已提交
96
	indentation?: string;
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	tabFocusMode?: boolean;
}

class State {
	private _selectionStatus: string;
	public get selectionStatus(): string { return this._selectionStatus; }

	private _mode: string;
	public get mode(): string { return this._mode; }

	private _encoding: string;
	public get encoding(): string { return this._encoding; }

	private _EOL: string;
	public get EOL(): string { return this._EOL; }

I
isidor 已提交
113 114 115
	private _indentation: string;
	public get indentation(): string { return this._indentation; }

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
	private _tabFocusMode: boolean;
	public get tabFocusMode(): boolean { return this._tabFocusMode; }

	constructor() {
		this._selectionStatus = null;
		this._mode = null;
		this._encoding = null;
		this._EOL = null;
		this._tabFocusMode = false;
	}

	public update(update:StateDelta): IStateChange {
		let e = {
			selectionStatus: false,
			mode: false,
			encoding: false,
			EOL: false,
I
isidor 已提交
133 134
			tabFocusMode: false,
			indentation: false
135 136 137 138 139 140 141 142 143 144
		};
		let somethingChanged = false;

		if (typeof update.selectionStatus !== 'undefined') {
			if (this._selectionStatus !== update.selectionStatus) {
				this._selectionStatus = update.selectionStatus;
				somethingChanged = true;
				e.selectionStatus = true;
			}
		}
I
isidor 已提交
145 146 147 148 149 150 151
		if (typeof update.indentation !== 'undefined') {
			if (this._indentation !== update.indentation) {
				this._indentation = update.indentation;
				somethingChanged = true;
				e.indentation = true;
			}
		}
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
		if (typeof update.mode !== 'undefined') {
			if (this._mode !== update.mode) {
				this._mode = update.mode;
				somethingChanged = true;
				e.mode = true;
			}
		}
		if (typeof update.encoding !== 'undefined') {
			if (this._encoding !== update.encoding) {
				this._encoding = update.encoding;
				somethingChanged = true;
				e.encoding = true;
			}
		}
		if (typeof update.EOL !== 'undefined') {
			if (this._EOL !== update.EOL) {
				this._EOL = update.EOL;
				somethingChanged = true;
				e.EOL = true;
			}
		}
		if (typeof update.tabFocusMode !== 'undefined') {
			if (this._tabFocusMode !== update.tabFocusMode) {
				this._tabFocusMode = update.tabFocusMode;
				somethingChanged = true;
				e.tabFocusMode = true;
			}
		}

		if (somethingChanged) {
			return e;
		}
		return null;
	}
}

188 189 190 191 192 193
const nlsSingleSelectionRange = nls.localize('singleSelectionRange', "Ln {0}, Col {1} ({2} selected)");
const nlsSingleSelection = nls.localize('singleSelection', "Ln {0}, Col {1}");
const nlsMultiSelectionRange = nls.localize('multiSelectionRange', "{0} selections ({1} characters selected)");
const nlsMultiSelection = nls.localize('multiSelection', "{0} selections");
const nlsEOLLF = nls.localize('endOfLineLineFeed', "LF");
const nlsEOLCRLF = nls.localize('endOfLineCarriageReturnLineFeed', "CRLF");
194
const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab moves focus");
E
Erich Gamma 已提交
195

A
Alex Dima 已提交
196 197 198 199 200 201 202
function show(el:HTMLElement): void {
	StyleMutator.setDisplay(el, '');
}
function hide(el:HTMLElement): void {
	StyleMutator.setDisplay(el, 'none');
}

203
export class EditorStatus implements IStatusbarItem {
E
Erich Gamma 已提交
204

205
	private state: State;
206 207
	private element: HTMLElement;
	private tabFocusModeElement: HTMLElement;
I
isidor 已提交
208
	private indentationElement: HTMLElement;
209 210 211 212
	private selectionElement: HTMLElement;
	private encodingElement: HTMLElement;
	private eolElement: HTMLElement;
	private modeElement: HTMLElement;
E
Erich Gamma 已提交
213 214
	private toDispose: IDisposable[];

215 216 217 218
	constructor(
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
		@IQuickOpenService private quickOpenService: IQuickOpenService,
		@IInstantiationService private instantiationService: IInstantiationService,
219 220
		@IEventService private eventService: IEventService,
		@IModeService private modeService: IModeService
221 222
	) {
		this.toDispose = [];
223
		this.state = new State();
E
Erich Gamma 已提交
224 225
	}

226 227 228 229 230 231 232
	public render(container: HTMLElement): IDisposable {
		this.element = append(container, $('.editor-statusbar-item'));

		this.tabFocusModeElement = append(this.element, $('a.editor-status-tabfocusmode'));
		this.tabFocusModeElement.title = nls.localize('disableTabMode', "Disable Accessibility Mode");
		this.tabFocusModeElement.onclick = () => this.onTabFocusModeClick();
		this.tabFocusModeElement.textContent = nlsTabFocusMode;
233
		hide(this.tabFocusModeElement);
234

I
isidor 已提交
235 236 237 238 239
		this.indentationElement = append(this.element, $('a.editor-status-indentation'));
		this.indentationElement.title = nls.localize('indentation', "Indentation");
		this.indentationElement.onclick = () => this.onIndentationClick();
		hide(this.indentationElement);

240 241 242
		this.selectionElement = append(this.element, $('a.editor-status-selection'));
		this.selectionElement.title = nls.localize('gotoLine', "Go to Line");
		this.selectionElement.onclick = () => this.onSelectionClick();
243
		hide(this.selectionElement);
244 245 246 247

		this.encodingElement = append(this.element, $('a.editor-status-encoding'));
		this.encodingElement.title = nls.localize('selectEncoding', "Select Encoding");
		this.encodingElement.onclick = () => this.onEncodingClick();
248
		hide(this.encodingElement);
249 250 251 252

		this.eolElement = append(this.element, $('a.editor-status-eol'));
		this.eolElement.title = nls.localize('selectEOL', "Select End of Line Sequence");
		this.eolElement.onclick = () => this.onEOLClick();
253
		hide(this.eolElement);
254 255 256 257

		this.modeElement = append(this.element, $('a.editor-status-mode'));
		this.modeElement.title = nls.localize('selectLanguageMode', "Select Language Mode");
		this.modeElement.onclick = () => this.onModeClick();
258
		hide(this.modeElement);
259 260 261 262 263 264 265

		this.toDispose.push(
			this.eventService.addListener2(EventType.EDITOR_INPUT_CHANGED, (e: EditorEvent) => this.onEditorInputChange(e.editor)),
			this.eventService.addListener2(EventType.RESOURCE_ENCODING_CHANGED, (e: ResourceEvent) => this.onResourceEncodingChange(e.resource)),
			this.eventService.addListener2(EventType.TEXT_EDITOR_SELECTION_CHANGED, (e: TextEditorSelectionEvent) => this.onSelectionChange(e.editor)),
			this.eventService.addListener2(EventType.TEXT_EDITOR_MODE_CHANGED, (e: EditorEvent) => this.onModeChange(e.editor)),
			this.eventService.addListener2(EventType.TEXT_EDITOR_CONTENT_CHANGED, (e: EditorEvent) => this.onEOLChange(e.editor)),
I
isidor 已提交
266 267
			this.eventService.addListener2(EventType.TEXT_EDITOR_CONFIGURATION_CHANGED, (e: EditorEvent) => this.onTabFocusModeChange(e.editor)),
			this.eventService.addListener2(EventType.TEXT_EDITOR_CONFIGURATION_CHANGED, (e: EditorEvent) => this.onIndentationChange(e.editor))
268
		);
E
Erich Gamma 已提交
269

270 271 272
		return combinedDispose(...this.toDispose);
	}

273 274 275 276 277
	private updateState(update: StateDelta): void {
		let changed = this.state.update(update);
		if (!changed) {
			// Nothing really changed
			return;
E
Erich Gamma 已提交
278 279
		}

280 281 282 283 284 285
		if (changed.tabFocusMode) {
			if (this.state.tabFocusMode && this.state.tabFocusMode === true) {
				show(this.tabFocusModeElement);
			} else {
				hide(this.tabFocusModeElement);
			}
E
Erich Gamma 已提交
286 287
		}

I
isidor 已提交
288 289 290 291 292 293 294 295 296
		if (changed.indentation) {
			if (this.state.indentation) {
				this.indentationElement.textContent = this.state.indentation;
				show(this.indentationElement);
			} else {
				hide(this.indentationElement);
			}
		}

297 298 299 300 301 302 303
		if (changed.selectionStatus) {
			if (this.state.selectionStatus) {
				this.selectionElement.textContent = this.state.selectionStatus;
				show(this.selectionElement);
			} else {
				hide(this.selectionElement);
			}
E
Erich Gamma 已提交
304 305
		}

306 307 308 309 310 311 312
		if (changed.encoding) {
			if (this.state.encoding) {
				this.encodingElement.textContent = this.state.encoding;
				show(this.encodingElement);
			} else {
				hide(this.encodingElement);
			}
E
Erich Gamma 已提交
313 314
		}

315 316 317 318 319 320 321
		if (changed.EOL) {
			if (this.state.EOL) {
				this.eolElement.textContent = this.state.EOL === '\r\n' ? nlsEOLCRLF : nlsEOLLF;
				show(this.eolElement);
			} else {
				hide(this.eolElement);
			}
E
Erich Gamma 已提交
322 323
		}

324 325 326 327 328 329 330 331
		if (changed.mode) {
			if (this.state.mode) {
				this.modeElement.textContent = this.state.mode;
				show(this.modeElement);
			} else {
				hide(this.modeElement);
			}
		}
E
Erich Gamma 已提交
332 333
	}

334
	private getSelectionLabel(info:IEditorSelectionStatus): string {
E
Erich Gamma 已提交
335 336 337 338 339 340
		if (!info || !info.selections) {
			return null;
		}

		if (info.selections.length === 1) {
			if (info.charactersSelected) {
341
				return strings.format(nlsSingleSelectionRange, info.selections[0].positionLineNumber, info.selections[0].positionColumn, info.charactersSelected);
E
Erich Gamma 已提交
342
			} else {
343
				return strings.format(nlsSingleSelection, info.selections[0].positionLineNumber, info.selections[0].positionColumn);
E
Erich Gamma 已提交
344 345 346
			}
		} else {
			if (info.charactersSelected) {
347
				return strings.format(nlsMultiSelectionRange, info.selections.length, info.charactersSelected);
E
Erich Gamma 已提交
348
			} else {
349
				return strings.format(nlsMultiSelection, info.selections.length);
E
Erich Gamma 已提交
350 351 352 353
			}
		}
	}

354 355 356 357 358 359 360
	private onModeClick(): void {
		let action = this.instantiationService.createInstance(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL);

		action.run().done(null, errors.onUnexpectedError);
		action.dispose();
	}

I
isidor 已提交
361 362 363 364 365 366
	private onIndentationClick(): void {
		const action = this.instantiationService.createInstance(ChangeIndentationAction, ChangeIndentationAction.ID, ChangeIndentationAction.LABEL);
		action.run().done(null, errors.onUnexpectedError);
		action.dispose();
	}

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
	private onSelectionClick(): void {
		this.quickOpenService.show(':'); // "Go to line"
	}

	private onEOLClick(): void {
		let action = this.instantiationService.createInstance(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL);

		action.run().done(null, errors.onUnexpectedError);
		action.dispose();
	}

	private onEncodingClick(): void {
		let action = this.instantiationService.createInstance(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL);

		action.run().done(null, errors.onUnexpectedError);
		action.dispose();
	}

	private onTabFocusModeClick(): void {
		let activeEditor = this.editorService.getActiveEditor();
		if (activeEditor instanceof BaseTextEditor && isCodeEditorWithTabFocusMode(activeEditor)) {
			(<ICodeEditor>activeEditor.getControl()).updateOptions({ tabFocusMode: false });
		}
	}

392
	private onEditorInputChange(e: IBaseEditor): void {
E
Erich Gamma 已提交
393 394 395 396 397
		this.onSelectionChange(e);
		this.onModeChange(e);
		this.onEOLChange(e);
		this.onEncodingChange(e);
		this.onTabFocusModeChange(e);
I
isidor 已提交
398
		this.onIndentationChange(e);
E
Erich Gamma 已提交
399 400
	}

401
	private onModeChange(e: IBaseEditor): void {
E
Erich Gamma 已提交
402 403 404 405
		if (e && !this.isActiveEditor(e)) {
			return;
		}

406
		let info: StateDelta = { mode: null };
E
Erich Gamma 已提交
407 408 409 410 411 412 413 414 415 416

		// We only support text based editors
		if (e instanceof BaseTextEditor) {
			let editorWidget = e.getControl();
			let textModel = getTextModel(editorWidget);
			if (textModel) {
				// Compute mode
				if (!!(<ITokenizedModel>textModel).getMode) {
					let mode = (<ITokenizedModel>textModel).getMode();
					if (mode) {
417
						info = { mode: this.modeService.getLanguageName(mode.getId()) };
E
Erich Gamma 已提交
418 419 420 421 422 423 424 425
					}
				}
			}
		}

		this.updateState(info);
	}

I
isidor 已提交
426
	private onIndentationChange(e: IBaseEditor): void {
427 428 429 430 431
		if (e && !this.isActiveEditor(e)) {
			return;
		}

		const update: StateDelta = { indentation: null };
I
isidor 已提交
432
		if (e instanceof BaseTextEditor) {
433 434 435 436 437 438 439 440 441
			let editorWidget = e.getControl();
			if (editorWidget) {
				if (editorWidget.getEditorType() === EditorType.IDiffEditor) {
					editorWidget = (<IDiffEditor>editorWidget).getModifiedEditor();
				}
				const options = (<ICommonCodeEditor>editorWidget).getIndentationOptions();
				update.indentation = options.insertSpaces ? nls.localize('spacesSize', "Spaces: {0}", options.tabSize) :
					nls.localize('tabSize', "Tab Size: {0}", options.tabSize);
			}
I
isidor 已提交
442
		}
443

I
isidor 已提交
444 445 446
		this.updateState(update);
	}

447
	private onSelectionChange(e: IBaseEditor): void {
E
Erich Gamma 已提交
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
		if (e && !this.isActiveEditor(e)) {
			return;
		}

		let info: IEditorSelectionStatus = {};

		// We only support text based editors
		if (e instanceof BaseTextEditor) {
			let editorWidget = e.getControl();

			// Compute selection(s)
			info.selections = editorWidget.getSelections() || [];

			// Compute selection length
			info.charactersSelected = 0;
			let textModel = getTextModel(editorWidget);
			if (textModel) {
				info.selections.forEach((selection) => {
					info.charactersSelected += textModel.getValueLengthInRange(selection);
				});
			}

			// Compute the visible column for one selection. This will properly handle tabs and their configured widths
			if (info.selections.length === 1) {
				let visibleColumn = editorWidget.getVisibleColumnFromPosition(editorWidget.getPosition());

				let selectionClone = info.selections[0].clone(); // do not modify the original position we got from the editor
				selectionClone.positionColumn = visibleColumn;

				info.selections[0] = selectionClone;
			}
		}

481
		this.updateState({ selectionStatus: this.getSelectionLabel(info) });
E
Erich Gamma 已提交
482 483
	}

484
	private onEOLChange(e: IBaseEditor): void {
E
Erich Gamma 已提交
485 486 487 488
		if (e && !this.isActiveEditor(e)) {
			return;
		}

A
Alex Dima 已提交
489 490 491 492
		let codeEditor = getCodeEditor(e);
		if (!codeEditor) {
			return;
		}
E
Erich Gamma 已提交
493

A
Alex Dima 已提交
494 495 496 497
		let info: StateDelta = { EOL: null };
		if (!codeEditor.getConfiguration().readOnly) {
			let codeEditorModel = codeEditor.getModel();
			info.EOL = codeEditorModel.getEOL();
E
Erich Gamma 已提交
498 499 500 501 502
		}

		this.updateState(info);
	}

503
	private onEncodingChange(e: IBaseEditor): void {
E
Erich Gamma 已提交
504 505 506 507
		if (e && !this.isActiveEditor(e)) {
			return;
		}

508
		let info: StateDelta = { encoding: null };
E
Erich Gamma 已提交
509 510 511

		// We only support text based editors
		if (e instanceof BaseTextEditor) {
512
			let encodingSupport: IEncodingSupport = <any>asFileOrUntitledEditorInput(e.input);
E
Erich Gamma 已提交
513 514 515 516 517 518 519 520 521 522 523 524 525 526
			if (encodingSupport && types.isFunction(encodingSupport.getEncoding)) {
				let rawEncoding = encodingSupport.getEncoding();
				let encodingInfo = encoding.SUPPORTED_ENCODINGS[rawEncoding];
				if (encodingInfo) {
					info.encoding = encodingInfo.labelShort; // if we have a label, take it from there
				} else {
					info.encoding = rawEncoding; // otherwise use it raw
				}
			}
		}

		this.updateState(info);
	}

527 528 529 530 531
	private onResourceEncodingChange(resource: uri): void {
		let activeEditor = this.editorService.getActiveEditor();
		if (activeEditor) {
			let activeResource = getUntitledOrFileResource(activeEditor.input, true);
			if (activeResource && activeResource.toString() === resource.toString()) {
532
				return this.onEncodingChange(<IBaseEditor>activeEditor); // only update if the encoding changed for the active resource
533 534
			}
		}
E
Erich Gamma 已提交
535 536
	}

537
	private onTabFocusModeChange(e: IBaseEditor): void {
E
Erich Gamma 已提交
538 539 540 541
		if (e && !this.isActiveEditor(e)) {
			return;
		}

542
		let info: StateDelta = { tabFocusMode: false };
E
Erich Gamma 已提交
543 544 545 546 547 548 549 550 551

		// We only support text based editors
		if (e instanceof BaseTextEditor && isCodeEditorWithTabFocusMode(e)) {
			info = { tabFocusMode: true };
		}

		this.updateState(info);
	}

552
	private isActiveEditor(e: IBaseEditor): boolean {
553
		let activeEditor = this.editorService.getActiveEditor();
E
Erich Gamma 已提交
554 555 556 557 558 559 560

		return activeEditor && e && activeEditor === e;
	}
}

function isCodeEditorWithTabFocusMode(e: BaseTextEditor): boolean {
	let editorWidget = e.getControl();
561 562 563
	if (editorWidget.getEditorType() === EditorType.IDiffEditor) {
		editorWidget = (<IDiffEditor>editorWidget).getModifiedEditor();
	}
564 565 566
	if (editorWidget.getEditorType() !== EditorType.ICodeEditor) {
		return false;
	}
567

568 569
	let editorConfig = (<ICodeEditor>editorWidget).getConfiguration();
	return editorConfig.tabFocusMode && !editorConfig.readOnly;
E
Erich Gamma 已提交
570 571 572 573
}

function isWritableCodeEditor(e: BaseTextEditor): boolean {
	let editorWidget = e.getControl();
574 575 576 577
	if (editorWidget.getEditorType() === EditorType.IDiffEditor) {
		editorWidget = (<IDiffEditor>editorWidget).getModifiedEditor();
	}

E
Erich Gamma 已提交
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
	return (editorWidget.getEditorType() === EditorType.ICodeEditor &&
		!(<ICodeEditor>editorWidget).getConfiguration().readOnly);
}

export class ChangeModeAction extends Action {

	public static ID = 'workbench.action.editor.changeLanguageMode';
	public static LABEL = nls.localize('changeMode', "Change Language Mode");

	constructor(
		actionId: string,
		actionLabel: string,
		@IModeService private modeService: IModeService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
		@IQuickOpenService private quickOpenService: IQuickOpenService
	) {
		super(actionId, actionLabel);
	}

A
Alex Dima 已提交
597
	public run(): TPromise<any> {
598
		let languages = this.modeService.getRegisteredLanguageNames();
E
Erich Gamma 已提交
599 600 601 602 603 604 605 606 607 608 609 610 611
		let activeEditor = this.editorService.getActiveEditor();
		if (!(activeEditor instanceof BaseTextEditor)) {
			return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
		}

		let editorWidget = (<BaseTextEditor>activeEditor).getControl();
		let textModel = getTextModel(editorWidget);

		// Compute mode
		let currentModeId: string;
		if (!!(<ITokenizedModel>textModel).getMode) {
			let mode = (<ITokenizedModel>textModel).getMode();
			if (mode) {
612
				currentModeId = this.modeService.getLanguageName(mode.getId());
E
Erich Gamma 已提交
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
			}
		}

		// All languages are valid picks
		let selectedIndex: number;
		let picks: IPickOpenEntry[] = languages.sort().map((lang, index) => {
			if (currentModeId === lang) {
				selectedIndex = index;
			}

			return {
				label: lang
			};
		});

		// Offer to "Auto Detect" if we have a file open
		let autoDetectMode: IPickOpenEntry = {
			label: nls.localize('autoDetect', "Auto Detect")
		};

		if (asFileEditorInput(activeEditor.input, true)) {
			picks.unshift(autoDetectMode); // first entry
			selectedIndex++; // pushes selected index down
		}

		return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode"), autoFocus: { autoFocusIndex: selectedIndex } }).then((language) => {
			if (language) {
				activeEditor = this.editorService.getActiveEditor();
				if (activeEditor instanceof BaseTextEditor) {
					let editorWidget = activeEditor.getControl();
					let textModel = getTextModel(editorWidget);

					// Change mode
					if (!!(<ITokenizedModel>textModel).getMode) {
						if (language === autoDetectMode) {
							let fileResource = asFileEditorInput(activeEditor.input, true).getResource();
							(<ITokenizedModel>textModel).setMode(this.modeService.getOrCreateModeByFilenameOrFirstLine(fileResource.fsPath, textModel.getLineContent(1)));
						} else {
							(<ITokenizedModel>textModel).setMode(this.modeService.getOrCreateModeByLanguageName(language.label));
						}
					}
				}
			}
		});
	}
}

export interface IChangeEOLEntry extends IPickOpenEntry {
	eol: EndOfLineSequence;
}

I
isidor 已提交
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
export class ChangeIndentationAction extends Action {

	public static ID = 'workbench.action.editor.changeIndentation';
	public static LABEL = nls.localize('changeIndentation', "Change Indentation");

	constructor(
		actionId: string,
		actionLabel: string,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
		@IQuickOpenService private quickOpenService: IQuickOpenService
	) {
		super(actionId, actionLabel);
	}

	public run(): TPromise<any> {
		const activeEditor = this.editorService.getActiveEditor();
		if (!(activeEditor instanceof BaseTextEditor)) {
			return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
		}
		if (!isWritableCodeEditor(<BaseTextEditor>activeEditor)) {
			return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
		}
686
		const control = <ICommonCodeEditor>activeEditor.getControl();
I
isidor 已提交
687

688
		return this.quickOpenService.pick([control.getAction(ChangeIndentationSizeAction.ID), control.getAction(IndentationToSpacesAction.ID), control.getAction(IndentationToTabsAction.ID)], {
689
			placeHolder: nls.localize('pickAction', "Select Action")
690
		}).then(action => action && action.run());
I
isidor 已提交
691 692 693
	}
}

E
Erich Gamma 已提交
694 695 696 697 698 699 700 701 702 703 704 705 706 707
export class ChangeEOLAction extends Action {

	public static ID = 'workbench.action.editor.changeEOL';
	public static LABEL = nls.localize('changeEndOfLine', "Change End of Line Sequence");

	constructor(
		actionId: string,
		actionLabel: string,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
		@IQuickOpenService private quickOpenService: IQuickOpenService
	) {
		super(actionId, actionLabel);
	}

A
Alex Dima 已提交
708
	public run(): TPromise<any> {
E
Erich Gamma 已提交
709 710 711 712 713 714 715 716 717 718 719 720 721 722

		let activeEditor = this.editorService.getActiveEditor();
		if (!(activeEditor instanceof BaseTextEditor)) {
			return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
		}

		if (!isWritableCodeEditor(<BaseTextEditor>activeEditor)) {
			return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
		}

		let editorWidget = (<BaseTextEditor>activeEditor).getControl();
		let textModel = getTextModel(editorWidget);

		let EOLOptions: IChangeEOLEntry[] = [
723 724
			{ label: nlsEOLLF, eol: EndOfLineSequence.LF },
			{ label: nlsEOLCRLF, eol: EndOfLineSequence.CRLF },
E
Erich Gamma 已提交
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
		];

		let selectedIndex = (textModel.getEOL() === '\n') ? 0 : 1;

		return this.quickOpenService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), autoFocus: { autoFocusIndex: selectedIndex } }).then((eol) => {
			if (eol) {
				activeEditor = this.editorService.getActiveEditor();
				if (activeEditor instanceof BaseTextEditor && isWritableCodeEditor(activeEditor)) {
					let editorWidget = activeEditor.getControl();
					let textModel = getTextModel(editorWidget);
					textModel.setEOL(eol.eol);
				}
			}
		});
	}
}

export class ChangeEncodingAction extends Action {

	public static ID = 'workbench.action.editor.changeEncoding';
	public static LABEL = nls.localize('changeEncoding', "Change File Encoding");

	constructor(
		actionId: string,
		actionLabel: string,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
		@IQuickOpenService private quickOpenService: IQuickOpenService,
		@IConfigurationService private configurationService: IConfigurationService
	) {
		super(actionId, actionLabel);
	}

A
Alex Dima 已提交
757
	public run(): TPromise<any> {
E
Erich Gamma 已提交
758 759 760 761 762
		let activeEditor = this.editorService.getActiveEditor();
		if (!(activeEditor instanceof BaseTextEditor) || !activeEditor.input) {
			return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
		}

763
		let encodingSupport: IEncodingSupport = <any>asFileOrUntitledEditorInput(activeEditor.input);
E
Erich Gamma 已提交
764 765 766 767 768 769 770 771
		if (!types.areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) {
			return this.quickOpenService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]);
		}

		let pickActionPromise: TPromise<IPickOpenEntry>;
		let saveWithEncodingPick: IPickOpenEntry = { label: nls.localize('saveWithEncoding', "Save with Encoding") };
		let reopenWithEncodingPick: IPickOpenEntry = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding") };

772
		if (encodingSupport instanceof UntitledEditorInput) {
A
Alex Dima 已提交
773
			pickActionPromise = TPromise.as(saveWithEncodingPick);
E
Erich Gamma 已提交
774
		} else if (!isWritableCodeEditor(<BaseTextEditor>activeEditor)) {
A
Alex Dima 已提交
775
			pickActionPromise = TPromise.as(reopenWithEncodingPick);
E
Erich Gamma 已提交
776 777 778 779 780 781 782 783 784
		} else {
			pickActionPromise = this.quickOpenService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action") });
		}

		return pickActionPromise.then((action) => {
			if (!action) {
				return;
			}

785
			return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).then(() => {
E
Erich Gamma 已提交
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
				let isReopenWithEncoding = (action === reopenWithEncodingPick);

				return this.configurationService.loadConfiguration().then((configuration: IFilesConfiguration) => {
					let defaultEncoding = configuration && configuration.files && configuration.files.encoding;
					let selectedIndex: number;

					// All encodings are valid picks
					let picks: IPickOpenEntry[] = Object.keys(encoding.SUPPORTED_ENCODINGS)
						.sort((k1, k2) => {
							if (k1 === defaultEncoding) {
								return -1;
							} else if (k2 === defaultEncoding) {
								return 1;
							}

							return encoding.SUPPORTED_ENCODINGS[k1].order - encoding.SUPPORTED_ENCODINGS[k2].order;
						})
						.map((key, index) => {
							if (key === encodingSupport.getEncoding()) {
								selectedIndex = index;
							}

							return { id: key, label: encoding.SUPPORTED_ENCODINGS[key].labelLong, description: key === defaultEncoding ? nls.localize('defaultEncoding', "Default Encoding") : void 0 };
						});

					return this.quickOpenService.pick(picks, {
						placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
						autoFocus: { autoFocusIndex: selectedIndex }
					}).then((encoding) => {
						if (encoding) {
							activeEditor = this.editorService.getActiveEditor();
817
							encodingSupport = <any>asFileOrUntitledEditorInput(activeEditor.input);
E
Erich Gamma 已提交
818
							if (encodingSupport && types.areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding) && encodingSupport.getEncoding() !== encoding.id) {
819
								encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
E
Erich Gamma 已提交
820 821 822 823 824 825 826 827
							}
						}
					});
				});
			});
		});
	}
}