Allow to change indent options on models

This commit is contained in:
Alex Dima
2016-03-08 21:29:02 +01:00
parent ad0dcb254b
commit 70db9378ce
7 changed files with 153 additions and 42 deletions
+11 -1
View File
@@ -1255,8 +1255,14 @@ export interface ITextModelCreationOptions {
defaultEOL: DefaultEndOfLine;
}
export interface ITextModelUpdateOptions {
tabSize?: number;
insertSpaces?: boolean;
}
export interface IModelOptionsChangedEvent {
// TODO@Alex TODO@indent
tabSize: boolean;
insertSpaces: boolean;
}
/**
@@ -1710,6 +1716,10 @@ export interface IEditableTextModel extends ITextModelWithMarkers {
getOneIndent(): string;
updateOptions(newOpts:ITextModelUpdateOptions): void;
detectIndentation(defaultInsertSpaces:boolean, defaultTabSize:number): void;
/**
* Push a stack element onto the undo stack. This acts as an undo/redo point.
* The idea is to use `pushEditOperations` to edit the model and then to
+37 -1
View File
@@ -38,7 +38,7 @@ export class TextModel extends OrderGuaranteeEventEmitter implements editorCommo
private _BOM:string;
constructor(allowedEventTypes:string[], rawText:editorCommon.IRawText) {
allowedEventTypes.push(editorCommon.EventType.ModelContentChanged);
allowedEventTypes.push(editorCommon.EventType.ModelContentChanged, editorCommon.EventType.ModelOptionsChanged);
super(allowedEventTypes);
this._options = rawText.options;
@@ -56,6 +56,42 @@ export class TextModel extends OrderGuaranteeEventEmitter implements editorCommo
return this._options;
}
public updateOptions(newOpts:editorCommon.ITextModelUpdateOptions): void {
let somethingChanged = false;
let changed:editorCommon.IModelOptionsChangedEvent = {
tabSize: false,
insertSpaces: false
};
if (typeof newOpts.insertSpaces !== 'undefined') {
if (this._options.insertSpaces !== newOpts.insertSpaces) {
somethingChanged = true;
changed.insertSpaces = true;
this._options.insertSpaces = newOpts.insertSpaces;
}
}
if (typeof newOpts.tabSize !== 'undefined') {
if (this._options.tabSize !== newOpts.tabSize) {
somethingChanged = true;
changed.tabSize = true;
this._options.tabSize = newOpts.tabSize;
}
}
if (somethingChanged) {
this.emit(editorCommon.EventType.ModelOptionsChanged, changed);
}
}
public detectIndentation(defaultInsertSpaces:boolean, defaultTabSize:number): void {
let lines = this._lines.map(line => line.text);
let guessedIndentation = guessIndentation(lines, defaultTabSize, defaultInsertSpaces);
this.updateOptions({
insertSpaces: guessedIndentation.insertSpaces,
tabSize: guessedIndentation.tabSize
});
}
private _normalizeIndentationFromWhitespace(str:string): string {
let tabSize = this._options.tabSize;
let insertSpaces = this._options.insertSpaces;
@@ -8,7 +8,7 @@ import Event from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import {TPromise} from 'vs/base/common/winjs.base';
import {ServiceIdentifier, createDecorator} from 'vs/platform/instantiation/common/instantiation';
import {IModel} from 'vs/editor/common/editorCommon';
import {IModel, ITextModelCreationOptions} from 'vs/editor/common/editorCommon';
import {IMode} from 'vs/editor/common/modes';
export var IModelService = createDecorator<IModelService>('modelService');
@@ -22,6 +22,8 @@ export interface IModelService {
getModels(): IModel[];
getCreationOptions(): ITextModelCreationOptions;
getModel(resource: URI): IModel;
onModelAdded: Event<IModel>;
@@ -170,6 +170,17 @@ class ModelMarkerHandler {
}
}
interface IRawConfig {
files?: {
eol?: any;
};
editor?: {
tabSize?: any;
insertSpaces?: any;
detectIndentation?: any;
};
}
export class ModelServiceImpl implements IModelService {
public serviceId = IModelService;
@@ -210,20 +221,20 @@ export class ModelServiceImpl implements IModelService {
this._workerHelper = this._threadService.getRemotable(ModelServiceWorkerHelper);
this._configurationService = configurationService;
let readDefaultEOL = (config:any) => {
let readConfig = (config:IRawConfig) => {
const eol = config.files && config.files.eol;
let newTabSize = DEFAULT_INDENTATION.tabSize;
let tabSize = DEFAULT_INDENTATION.tabSize;
if (config.editor && typeof config.editor.tabSize !== 'undefined') {
let parsedTabSize = parseInt(config.editor.tabSize, 10);
if (!isNaN(parsedTabSize)) {
newTabSize = parsedTabSize;
tabSize = parsedTabSize;
}
}
let newInsertSpaces = DEFAULT_INDENTATION.insertSpaces;
let insertSpaces = DEFAULT_INDENTATION.insertSpaces;
if (config.editor && typeof config.editor.insertSpaces !== 'undefined') {
newInsertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces));
insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces));
}
let newDefaultEOL = this._modelCreationOptions.defaultEOL;
@@ -234,19 +245,23 @@ export class ModelServiceImpl implements IModelService {
}
let detectIndentation = DEFAULT_INDENTATION.detectIndentation;
if (config.editor && typeof config.editor.detectIndentation !== 'undefined') {
detectIndentation = (config.editor.detectIndentation === 'false' ? false : Boolean(config.editor.detectIndentation));
}
this._modelCreationOptions = {
tabSize: newTabSize,
insertSpaces: newInsertSpaces,
this._setModelOptions({
tabSize: tabSize,
insertSpaces: insertSpaces,
detectIndentation: detectIndentation,
defaultEOL: newDefaultEOL
};
});
};
this._configurationServiceSubscription = this._configurationService.addListener2(ConfigurationServiceEventTypes.UPDATED, (e: IConfigurationServiceEvent) => {
readDefaultEOL(e.config);
readConfig(e.config);
});
this._configurationService.loadConfiguration().then((config) => {
readDefaultEOL(config);
readConfig(config);
});
this._models = {};
@@ -260,6 +275,26 @@ export class ModelServiceImpl implements IModelService {
}
}
public getCreationOptions(): editorCommon.ITextModelCreationOptions {
return this._modelCreationOptions;
}
private _setModelOptions(newOpts: editorCommon.ITextModelCreationOptions): void {
if (
(this._modelCreationOptions.detectIndentation === newOpts.detectIndentation)
&& (this._modelCreationOptions.insertSpaces === newOpts.insertSpaces)
&& (this._modelCreationOptions.tabSize === newOpts.tabSize)
) {
// Same indent opts, no need to touch created models
this._modelCreationOptions = newOpts;
return;
}
this._modelCreationOptions = newOpts;
// TODO@Alex TODO@indent
// console.log('I NEED TO UPDATE EXISTING MODELS!!!');
}
public dispose(): void {
if(this._markerServiceSubscription) {
this._markerServiceSubscription.dispose();
@@ -10,6 +10,7 @@ import {ICommonCodeEditor, IEditorActionDescriptorData} from 'vs/editor/common/e
import {CommonEditorRegistry, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions';
import {IndentationToSpacesCommand, IndentationToTabsCommand} from 'vs/editor/contrib/indentation/common/indentationCommands';
import {IQuickOpenService} from 'vs/workbench/services/quickopen/common/quickOpenService';
import {IModelService} from 'vs/editor/common/services/modelService';
export class IndentationToSpacesAction extends EditorAction {
static ID = 'editor.action.indentationToSpaces';
@@ -26,10 +27,9 @@ export class IndentationToSpacesAction extends EditorAction {
let modelOpts = model.getOptions();
const command = new IndentationToSpacesCommand(this.editor.getSelection(), modelOpts.tabSize);
this.editor.executeCommands(this.id, [command]);
// TODO@Alex TODO@indent
// model.updateOptions({
// insertSpaces: true
// });
model.updateOptions({
insertSpaces: true
});
return TPromise.as(true);
}
@@ -50,10 +50,9 @@ export class IndentationToTabsAction extends EditorAction {
let modelOpts = model.getOptions();
const command = new IndentationToTabsCommand(this.editor.getSelection(), modelOpts.tabSize);
this.editor.executeCommands(this.id, [command]);
// TODO@Alex TODO@indent
// model.updateOptions({
// insertSpaces: false
// });
model.updateOptions({
insertSpaces: false
});
return TPromise.as(true);
}
@@ -63,7 +62,8 @@ export class ChangeIndentationSizeAction extends EditorAction {
constructor(descriptor: IEditorActionDescriptorData, editor: ICommonCodeEditor,
private insertSpaces: boolean,
private quickOpenService: IQuickOpenService
private quickOpenService: IQuickOpenService,
private modelService:IModelService
) {
super(descriptor, editor);
}
@@ -73,22 +73,22 @@ export class ChangeIndentationSizeAction extends EditorAction {
if (!model) {
return;
}
let modelOpts = model.getOptions();
let creationOpts = this.modelService.getCreationOptions();
const picks = [1, 2, 3, 4, 5, 6, 7, 8].map(n => ({
id: n.toString(),
label: n.toString(),
description: n === modelOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : null
description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : null
}));
const autoFocusIndex = Math.min(modelOpts.tabSize - 1, 7);
const autoFocusIndex = Math.min(creationOpts.tabSize - 1, 7);
return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).then(() =>
this.quickOpenService.pick(picks, { placeHolder: nls.localize('selectTabWidth', "Select Tab Size for Current File"), autoFocus: { autoFocusIndex } }).then(pick => {
if (pick) {
// TODO@Alex TODO@indent
// model.updateOptions({
// tabSize: parseInt(pick.label)
// insertSpaces: this.insertSpaces
// });
model.updateOptions({
tabSize: parseInt(pick.label, 10),
insertSpaces: this.insertSpaces
});
}
return true;
@@ -101,10 +101,13 @@ export class IndentUsingTabs extends ChangeIndentationSizeAction {
static ID = 'editor.action.indentUsingTabs';
constructor(descriptor: IEditorActionDescriptorData, editor: ICommonCodeEditor,
@IQuickOpenService quickOpenService: IQuickOpenService
constructor(
descriptor: IEditorActionDescriptorData,
editor: ICommonCodeEditor,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IModelService modelService:IModelService
) {
super(descriptor, editor, false, quickOpenService);
super(descriptor, editor, false, quickOpenService, modelService);
}
}
@@ -112,10 +115,36 @@ export class IndentUsingSpaces extends ChangeIndentationSizeAction {
static ID = 'editor.action.indentUsingSpaces';
constructor(descriptor: IEditorActionDescriptorData, editor: ICommonCodeEditor,
@IQuickOpenService quickOpenService: IQuickOpenService
constructor(
descriptor: IEditorActionDescriptorData,
editor: ICommonCodeEditor,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IModelService modelService:IModelService
) {
super(descriptor, editor, true, quickOpenService);
super(descriptor, editor, true, quickOpenService, modelService);
}
}
export class DetectIndentation extends EditorAction {
static ID = 'editor.action.detectIndentation';
constructor(
descriptor: IEditorActionDescriptorData,
editor: ICommonCodeEditor,
@IModelService private modelService:IModelService
) {
super(descriptor, editor);
}
public run(): TPromise<boolean> {
let model = this.editor.getModel();
if (!model) {
return;
}
let creationOpts = this.modelService.getCreationOptions();
model.detectIndentation(creationOpts.insertSpaces, creationOpts.tabSize);
}
}
@@ -140,4 +169,5 @@ CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(Indentation
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(IndentationToTabsAction, IndentationToTabsAction.ID, nls.localize('indentationToTabs', "Convert Indentation to Tabs")));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(IndentUsingSpaces, IndentUsingSpaces.ID, nls.localize('indentUsingSpaces', "Indent Using Spaces")));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(IndentUsingTabs, IndentUsingTabs.ID, nls.localize('indentUsingTabs', "Indent Using Tabs")));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(DetectIndentation, DetectIndentation.ID, nls.localize('detectIndentation', "Detect Indentation from Content")));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(ToggleRenderWhitespaceAction, ToggleRenderWhitespaceAction.ID, nls.localize('toggleRenderWhitespace', "Toggle Render Whitespace")));
@@ -19,7 +19,6 @@ export interface ITextEditorConfigurationUpdate {
insertSpaces?: boolean | string;
}
export interface IResolvedTextEditorConfiguration {
IResolvedTextEditorConfiguration: any; // TODO@Alex TODO@indent
tabSize: number;
insertSpaces: boolean;
}
@@ -199,7 +198,6 @@ export class MainThreadTextEditor {
private static _readConfiguration(model:EditorCommon.IModel): IResolvedTextEditorConfiguration {
let indent = model.getOptions();
return {
IResolvedTextEditorConfiguration: null, // TODO@Alex TODO@indent
insertSpaces: indent.insertSpaces,
tabSize: indent.tabSize
};
@@ -24,7 +24,7 @@ import {ICommonCodeEditor} from 'vs/editor/common/editorCommon';
import {OpenGlobalSettingsAction} from 'vs/workbench/browser/actions/openSettings';
import {ICodeEditor, IDiffEditor} from 'vs/editor/browser/editorBrowser';
import {EndOfLineSequence, ITokenizedModel, EditorType, IEditorSelection, ITextModel, IDiffEditorModel, IEditor} from 'vs/editor/common/editorCommon';
import {IndentUsingSpaces, IndentUsingTabs, IndentationToSpacesAction, IndentationToTabsAction} from 'vs/editor/contrib/indentation/common/indentation';
import {IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction} from 'vs/editor/contrib/indentation/common/indentation';
import {EventType, ResourceEvent, EditorEvent, TextEditorSelectionEvent} from 'vs/workbench/common/events';
import {BaseTextEditor} from 'vs/workbench/browser/parts/editor/textEditor';
import {IEditor as IBaseEditor} from 'vs/platform/editor/common/editor';
@@ -732,9 +732,9 @@ class ChangeIndentationAction extends Action {
}
const control = <ICommonCodeEditor>activeEditor.getControl();
const picks = [control.getAction(IndentUsingSpaces.ID), control.getAction(IndentUsingTabs.ID), control.getAction(IndentationToSpacesAction.ID), control.getAction(IndentationToTabsAction.ID)];
const picks = [control.getAction(IndentUsingSpaces.ID), control.getAction(IndentUsingTabs.ID), control.getAction(DetectIndentation.ID), control.getAction(IndentationToSpacesAction.ID), control.getAction(IndentationToTabsAction.ID)];
(<IPickOpenEntry>picks[0]).separator = { label: nls.localize('indentView', "change view") };
(<IPickOpenEntry>picks[2]).separator = { label: nls.localize('indentConvert', "convert file"), border: true };
(<IPickOpenEntry>picks[3]).separator = { label: nls.localize('indentConvert', "convert file"), border: true };
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action") }).then(action => action && action.run());
}