From 65463e2d01d7e05c1ddef726c88efd0630421ade Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 8 Feb 2016 17:52:13 +0100 Subject: [PATCH] Consolidate 5 descriptive mode supports into IRichEditSupport --- .../common/controller/cursorCollection.ts | 24 +- src/vs/editor/common/controller/oneCursor.ts | 11 +- src/vs/editor/common/editorCommon.ts | 6 +- .../model/textModelWithTokensHelpers.ts | 19 +- src/vs/editor/common/modes.ts | 211 +++++++++--------- src/vs/editor/common/modes/abstractMode.ts | 40 +--- src/vs/editor/common/modes/monarch/monarch.ts | 14 +- .../common/modes/monarch/monarchDefinition.ts | 58 +++-- src/vs/editor/common/modes/nullMode.ts | 12 +- src/vs/editor/common/modes/supports.ts | 144 ------------ .../common/modes/supports/characterPair.ts | 63 ++++++ .../editor/common/modes/supports/comments.ts | 25 +++ .../modes/supports/electricCharacter.ts | 62 +++++ .../editor/common/modes/supports/onEnter.ts | 18 +- .../common/modes/supports/richEditSupport.ts | 103 +++++++++ .../modes/supports/tokenTypeClassification.ts | 28 +++ src/vs/editor/common/services/modeService.ts | 7 +- .../editor/common/services/modeServiceImpl.ts | 31 +-- .../comment/common/blockCommentCommand.ts | 3 +- .../comment/common/lineCommentCommand.ts | 5 +- src/vs/editor/node/languageConfiguration.ts | 37 +-- .../standalone-languages/test/testUtil.ts | 7 +- .../test/common/commands/shiftCommand.test.ts | 14 +- .../test/common/controller/cursor.test.ts | 28 ++- src/vs/editor/test/common/modesTestUtils.ts | 42 ++-- src/vs/editor/test/common/modesUtil.ts | 8 +- src/vs/editor/test/common/testModes.ts | 13 +- src/vs/languages/css/common/css.ts | 64 +++--- src/vs/languages/css/test/common/css.test.ts | 4 +- .../languages/handlebars/common/handlebars.ts | 47 ++-- src/vs/languages/html/common/html.ts | 84 +++---- .../languages/html/test/common/html.test.ts | 2 +- .../languages/javascript/common/javascript.ts | 64 +++--- .../javascript/test/common/javascript.test.ts | 6 +- src/vs/languages/json/common/json.ts | 67 +++--- .../languages/json/test/common/json.test.ts | 2 +- .../less/test/common/colorizer.test.ts | 2 +- src/vs/languages/markdown/common/markdown.ts | 9 +- src/vs/languages/php/common/php.ts | 52 ++--- src/vs/languages/php/test/common/php.test.ts | 4 +- src/vs/languages/razor/common/razor.ts | 46 +++- .../languages/sass/test/common/sass.test.ts | 2 +- .../typescript/common/typescriptMode.ts | 107 +++++---- .../test/common/tokenization.test.ts | 2 +- src/vs/workbench/api/node/extHost.api.impl.ts | 114 +--------- 45 files changed, 868 insertions(+), 843 deletions(-) create mode 100644 src/vs/editor/common/modes/supports/characterPair.ts create mode 100644 src/vs/editor/common/modes/supports/comments.ts create mode 100644 src/vs/editor/common/modes/supports/electricCharacter.ts create mode 100644 src/vs/editor/common/modes/supports/richEditSupport.ts create mode 100644 src/vs/editor/common/modes/supports/tokenTypeClassification.ts diff --git a/src/vs/editor/common/controller/cursorCollection.ts b/src/vs/editor/common/controller/cursorCollection.ts index 7561a799d47..fe78aafd2d3 100644 --- a/src/vs/editor/common/controller/cursorCollection.ts +++ b/src/vs/editor/common/controller/cursorCollection.ts @@ -307,19 +307,21 @@ export class CursorCollection { } private getModeConfiguration(): IModeConfiguration { - var i: number; + let i: number; - var result: IModeConfiguration = { + let result: IModeConfiguration = { electricChars: {}, autoClosingPairsOpen: {}, autoClosingPairsClose: {}, surroundingPairs: {} }; - var electricChars: string[]; - if (this.model.getMode().electricCharacterSupport) { + let richEditSupport = this.model.getMode().richEditSupport; + + let electricChars: string[]; + if (richEditSupport && richEditSupport.electricCharacter) { try { - electricChars = this.model.getMode().electricCharacterSupport.getElectricCharacters(); + electricChars = richEditSupport.electricCharacter.getElectricCharacters(); } catch(e) { Errors.onUnexpectedError(e); electricChars = null; @@ -331,10 +333,10 @@ export class CursorCollection { } } - var autoClosingPairs: IAutoClosingPair[]; - if (this.model.getMode().characterPairSupport) { + let autoClosingPairs: IAutoClosingPair[]; + if (richEditSupport && richEditSupport.characterPair) { try { - autoClosingPairs = this.model.getMode().characterPairSupport.getAutoClosingPairs(); + autoClosingPairs = richEditSupport.characterPair.getAutoClosingPairs(); } catch(e) { Errors.onUnexpectedError(e); autoClosingPairs = null; @@ -347,10 +349,10 @@ export class CursorCollection { } } - var surroundingPairs: IAutoClosingPair[]; - if (this.model.getMode().characterPairSupport) { + let surroundingPairs: IAutoClosingPair[]; + if (richEditSupport && richEditSupport.characterPair) { try { - surroundingPairs = this.model.getMode().characterPairSupport.getSurroundingPairs(); + surroundingPairs = richEditSupport.characterPair.getSurroundingPairs(); } catch(e) { Errors.onUnexpectedError(e); surroundingPairs = null; diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 44f0e4eb3c3..a43034bb6b4 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -1035,7 +1035,9 @@ export class OneCursorOp { return false; } - if(!cursor.model.getMode().characterPairSupport) { + let richEditSupport = cursor.model.getMode().richEditSupport; + + if(!richEditSupport || !richEditSupport.characterPair) { return false; } @@ -1061,7 +1063,7 @@ export class OneCursorOp { var shouldAutoClosePair = false; try { - shouldAutoClosePair = cursor.model.getMode().characterPairSupport.shouldAutoClosePair(ch, lineContext, position.column - 1); + shouldAutoClosePair = richEditSupport.characterPair.shouldAutoClosePair(ch, lineContext, position.column - 1); } catch(e) { Errors.onUnexpectedError(e); } @@ -1144,9 +1146,10 @@ export class OneCursorOp { var lineContext = cursor.model.getLineContext(position.lineNumber); var electricAction:IElectricAction; - if(cursor.model.getMode().electricCharacterSupport) { + let richEditSupport = cursor.model.getMode().richEditSupport; + if(richEditSupport && richEditSupport.electricCharacter) { try { - electricAction = cursor.model.getMode().electricCharacterSupport.onElectricCharacter(lineContext, position.column - 2); + electricAction = richEditSupport.electricCharacter.onElectricCharacter(lineContext, position.column - 2); } catch(e) { Errors.onUnexpectedError(e); } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 2749f9ff7e5..423b6937226 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -750,13 +750,9 @@ export interface IModeSupportChangedEvent { emitOutputSupport:boolean; linkSupport:boolean; configSupport:boolean; - electricCharacterSupport:boolean; - commentsSupport:boolean; - characterPairSupport:boolean; - tokenTypeClassificationSupport:boolean; quickFixSupport: boolean; codeLensSupport: boolean; - onEnterSupport: boolean; + richEditSupport: boolean; } /** diff --git a/src/vs/editor/common/model/textModelWithTokensHelpers.ts b/src/vs/editor/common/model/textModelWithTokensHelpers.ts index a4991351bbd..40fbc8058ad 100644 --- a/src/vs/editor/common/model/textModelWithTokensHelpers.ts +++ b/src/vs/editor/common/model/textModelWithTokensHelpers.ts @@ -39,14 +39,19 @@ export interface INonWordTokenMap { export class WordHelper { private static _safeGetWordDefinition(mode:Modes.IMode): RegExp { - var result: RegExp = null; + let richEditSupport = mode.richEditSupport; + if (!richEditSupport) { + return null; + } + if (!richEditSupport.tokenTypeClassification) { + return null; + } - if (mode.tokenTypeClassificationSupport) { - try { - result = mode.tokenTypeClassificationSupport.getWordDefinition(); - } catch(e) { - Errors.onUnexpectedError(e); - } + var result: RegExp = null; + try { + result = richEditSupport.tokenTypeClassification.getWordDefinition(); + } catch(e) { + Errors.onUnexpectedError(e); } return result; diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index dd47bd864b0..a066557fa64 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -282,26 +282,6 @@ export interface IMode { */ configSupport?:IConfigurationSupport; - /** - * Optional adapter to support electric characters. - */ - electricCharacterSupport?:IElectricCharacterSupport; - - /** - * Optional adapter to support comment insertion. - */ - commentsSupport?:ICommentsSupport; - - /** - * Optional adapter to support insertion of character pair. - */ - characterPairSupport?:ICharacterPairSupport; - - /** - * Optional adapter to support classification of tokens. - */ - tokenTypeClassificationSupport?:ITokenTypeClassificationSupport; - /** * Optional adapter to support quick fix of typing errors. */ @@ -322,7 +302,10 @@ export interface IMode { */ taskSupport?: ITaskSupport; - onEnterSupport?: IOnEnterSupport; + /** + * Optional adapter to support rich editing. + */ + richEditSupport?: IRichEditSupport; } /** @@ -644,87 +627,6 @@ export interface IConfigurationSupport { configure(options:any):TPromise; } - - - -/** - * Interface used to support electric characters - */ -export interface IElectricAction { - // Only one of the following properties should be defined: - - // The line will be indented at the same level of the line - // which contains the matching given bracket type. - matchBracketType?:string; - - // The text will be appended after the electric character. - appendText?:string; - - // The number of characters to advance the cursor, useful with appendText - advanceCount?:number; -} - -export enum IndentAction { - None, - Indent, - IndentOutdent, - Outdent -} - -/** - * An action the editor executes when 'enter' is being pressed - */ -export interface IEnterAction { - indentAction:IndentAction; - appendText?:string; - removeText?:number; -} - -export interface IElectricCharacterSupport { - getElectricCharacters():string[]; - // Should return opening bracket type to match indentation with - onElectricCharacter(context:ILineContext, offset:number):IElectricAction; - onEnter(context:ILineContext, offset:number):IEnterAction; -} - -export interface IOnEnterSupport { - onEnter(model:EditorCommon.ITokenizedModel, position: EditorCommon.IPosition): IEnterAction; -} - -/** - * Interface used to support insertion of mode specific comments. - */ -export interface ICommentsConfiguration { - lineCommentTokens?:string[]; - blockCommentStartToken?:string; - blockCommentEndToken?:string; -} -export interface ICommentsSupport { - getCommentsConfiguration():ICommentsConfiguration; -} - - - -/** - * Interface used to support insertion of matching characters like brackets and qoutes. - */ -export interface IAutoClosingPair { - open:string; - close:string; -} -export interface ICharacterPairSupport { - getAutoClosingPairs():IAutoClosingPairConditional[]; - shouldAutoClosePair(character:string, context:ILineContext, offset:number):boolean; - getSurroundingPairs():IAutoClosingPair[]; -} - -/** - * Interface used to support the classification of tokens. - */ -export interface ITokenTypeClassificationSupport { - getWordDefinition():RegExp; -} - export interface IResourceEdit { resource: URI; range?: EditorCommon.IRange; @@ -826,4 +728,107 @@ export interface IAutoClosingPairConditional extends IAutoClosingPair { export interface ICharacterPairContribution { autoClosingPairs: IAutoClosingPairConditional[]; surroundingPairs?: IAutoClosingPair[]; -} \ No newline at end of file +} + +/** + * Interface used to support electric characters + */ +export interface IElectricAction { + // Only one of the following properties should be defined: + + // The line will be indented at the same level of the line + // which contains the matching given bracket type. + matchBracketType?:string; + + // The text will be appended after the electric character. + appendText?:string; + + // The number of characters to advance the cursor, useful with appendText + advanceCount?:number; +} + +export enum IndentAction { + None, + Indent, + IndentOutdent, + Outdent +} + +/** + * An action the editor executes when 'enter' is being pressed + */ +export interface IEnterAction { + indentAction:IndentAction; + appendText?:string; + removeText?:number; +} + +export interface IRichEditElectricCharacter { + getElectricCharacters():string[]; + // Should return opening bracket type to match indentation with + onElectricCharacter(context:ILineContext, offset:number):IElectricAction; + onEnter(context:ILineContext, offset:number):IEnterAction; +} + +export interface IRichEditOnEnter { + onEnter(model:EditorCommon.ITokenizedModel, position: EditorCommon.IPosition): IEnterAction; +} + +/** + * Interface used to support insertion of mode specific comments. + */ +export interface ICommentsConfiguration { + lineCommentTokens?:string[]; + blockCommentStartToken?:string; + blockCommentEndToken?:string; +} +export interface IRichEditComments { + getCommentsConfiguration():ICommentsConfiguration; +} + +/** + * Interface used to support insertion of matching characters like brackets and qoutes. + */ +export interface IAutoClosingPair { + open:string; + close:string; +} +export interface IRichEditCharacterPair { + getAutoClosingPairs():IAutoClosingPairConditional[]; + shouldAutoClosePair(character:string, context:ILineContext, offset:number):boolean; + getSurroundingPairs():IAutoClosingPair[]; +} + +/** + * Interface used to support the classification of tokens. + */ +export interface IRichEditTokenTypeClassification { + getWordDefinition():RegExp; +} + +export interface IRichEditSupport { + /** + * Optional adapter for electric characters. + */ + electricCharacter?:IRichEditElectricCharacter; + + /** + * Optional adapter for comment insertion. + */ + comments?:IRichEditComments; + + /** + * Optional adapter for insertion of character pair. + */ + characterPair?:IRichEditCharacterPair; + + /** + * Optional adapter for classification of tokens. + */ + tokenTypeClassification?:IRichEditTokenTypeClassification; + + /** + * Optional adapter for custom Enter handling. + */ + onEnter?: IRichEditOnEnter; +} diff --git a/src/vs/editor/common/modes/abstractMode.ts b/src/vs/editor/common/modes/abstractMode.ts index eb2fa8c27af..fb4775b86e1 100644 --- a/src/vs/editor/common/modes/abstractMode.ts +++ b/src/vs/editor/common/modes/abstractMode.ts @@ -41,8 +41,6 @@ export class AbstractMode implements Modes.IMode { public dirtyDiffSupport:Modes.IDirtyDiffSupport; public linkSupport:Modes.ILinkSupport; public configSupport:Modes.IConfigurationSupport; - public commentsSupport:Modes.ICommentsSupport; - public tokenTypeClassificationSupport:Modes.ITokenTypeClassificationSupport; public codeLensSupport:Modes.ICodeLensSupport; // adapters end @@ -68,8 +66,6 @@ export class AbstractMode implements Modes.IMode { this.dirtyDiffSupport = this; this.linkSupport = this; this.configSupport = this; - this.commentsSupport = this; - this.tokenTypeClassificationSupport = this; this._workerPiecePromise = null; this._simplifiedMode = null; @@ -228,24 +224,12 @@ export class AbstractMode implements Modes.IMode { } // END - - public getWordDefinition():RegExp { - return NullMode.DEFAULT_WORD_REGEXP; - } - - public getCommentsConfiguration():Modes.ICommentsConfiguration { - return null; - } } class SimplifiedMode implements Modes.IMode { tokenizationSupport: Modes.ITokenizationSupport; - electricCharacterSupport: Modes.IElectricCharacterSupport; - commentsSupport: Modes.ICommentsSupport; - characterPairSupport: Modes.ICharacterPairSupport; - tokenTypeClassificationSupport: Modes.ITokenTypeClassificationSupport; - onEnterSupport: Modes.IOnEnterSupport; + richEditSupport: Modes.IRichEditSupport; private _sourceMode: Modes.IMode; private _eventEmitter: EventEmitter; @@ -259,7 +243,7 @@ class SimplifiedMode implements Modes.IMode { if (this._sourceMode.addSupportChangedListener) { this._sourceMode.addSupportChangedListener((e) => { - if (e.tokenizationSupport || e.electricCharacterSupport || e.commentsSupport || e.characterPairSupport || e.tokenTypeClassificationSupport || e.onEnterSupport) { + if (e.tokenizationSupport || e.richEditSupport) { this._assignSupports(); let newEvent = SimplifiedMode._createModeSupportChangedEvent(e); this._eventEmitter.emit('modeSupportChanged', newEvent); @@ -278,11 +262,7 @@ class SimplifiedMode implements Modes.IMode { private _assignSupports(): void { this.tokenizationSupport = this._sourceMode.tokenizationSupport; - this.electricCharacterSupport = this._sourceMode.electricCharacterSupport; - this.commentsSupport = this._sourceMode.commentsSupport; - this.characterPairSupport = this._sourceMode.characterPairSupport; - this.tokenTypeClassificationSupport = this._sourceMode.tokenTypeClassificationSupport; - this.onEnterSupport = this._sourceMode.onEnterSupport; + this.richEditSupport = this._sourceMode.richEditSupport; } private static _createModeSupportChangedEvent(originalModeEvent:EditorCommon.IModeSupportChangedEvent): EditorCommon.IModeSupportChangedEvent { @@ -306,12 +286,8 @@ class SimplifiedMode implements Modes.IMode { emitOutputSupport:false, linkSupport:false, configSupport:false, - electricCharacterSupport: originalModeEvent.electricCharacterSupport, - commentsSupport: originalModeEvent.commentsSupport, - characterPairSupport: originalModeEvent.characterPairSupport, - tokenTypeClassificationSupport: originalModeEvent.tokenTypeClassificationSupport, quickFixSupport:false, - onEnterSupport: originalModeEvent.onEnterSupport + richEditSupport: originalModeEvent.richEditSupport, }; return event; } @@ -392,7 +368,7 @@ export class FrankensteinMode extends AbstractMode { } function _createModeSupportChangedEvent(...changedSupports: string[]): EditorCommon.IModeSupportChangedEvent { - var event = { + var event:EditorCommon.IModeSupportChangedEvent = { codeLensSupport: false, tokenizationSupport:false, occurrencesSupport:false, @@ -412,12 +388,8 @@ function _createModeSupportChangedEvent(...changedSupports: string[]): EditorCom emitOutputSupport:false, linkSupport:false, configSupport:false, - electricCharacterSupport:false, - commentsSupport:false, - characterPairSupport:false, - tokenTypeClassificationSupport:false, quickFixSupport:false, - onEnterSupport: false + richEditSupport: false }; changedSupports.forEach(support => event[support] = true); return event; diff --git a/src/vs/editor/common/modes/monarch/monarch.ts b/src/vs/editor/common/modes/monarch/monarch.ts index 8b1302c747c..4a694691c66 100644 --- a/src/vs/editor/common/modes/monarch/monarch.ts +++ b/src/vs/editor/common/modes/monarch/monarch.ts @@ -21,15 +21,15 @@ import {IInstantiationService} from 'vs/platform/instantiation/common/instantiat import {IThreadService} from 'vs/platform/thread/common/thread'; import {IModeService} from 'vs/editor/common/services/modeService'; import {IModelService} from 'vs/editor/common/services/modelService'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; /** * The MonarchMode creates a Monaco language mode given a certain language description */ export class MonarchMode extends AbstractMode { + public tokenizationSupport: Modes.ITokenizationSupport; - public electricCharacterSupport: Modes.IElectricCharacterSupport; - public characterPairSupport: Modes.ICharacterPairSupport; - public onEnterSupport: Modes.IOnEnterSupport; + public richEditSupport: Modes.IRichEditSupport; constructor( descriptor:Modes.IModeDescriptor, @@ -42,11 +42,9 @@ export class MonarchMode extends AbstractMode { super(descriptor, instantiationService, threadService); this.tokenizationSupport = createTokenizationSupport(modeService, this, lexer); - this.electricCharacterSupport = new Supports.BracketElectricCharacterSupport(this, MonarchDefinition.createBracketElectricCharacterContribution(lexer)); - this.commentsSupport = new Supports.CommentsSupport(MonarchDefinition.createCommentsSupport(lexer)); - this.tokenTypeClassificationSupport = new Supports.TokenTypeClassificationSupport(MonarchDefinition.createTokenTypeClassificationSupportContribution(lexer)); - this.characterPairSupport = new Supports.CharacterPairSupport(this, MonarchDefinition.createCharacterPairContribution(lexer)); + + this.richEditSupport = new RichEditSupport(this.getId(), MonarchDefinition.createRichEditSupport(lexer)); + this.suggestSupport = new Supports.ComposableSuggestSupport(this, MonarchDefinition.createSuggestSupport(modelService, this, lexer)); - this.onEnterSupport = new OnEnterSupport(this.getId(), MonarchDefinition.createOnEnterSupportOptions(lexer)); } } diff --git a/src/vs/editor/common/modes/monarch/monarchDefinition.ts b/src/vs/editor/common/modes/monarch/monarchDefinition.ts index ebf0e0f35c4..2cd84dc394d 100644 --- a/src/vs/editor/common/modes/monarch/monarchDefinition.ts +++ b/src/vs/editor/common/modes/monarch/monarchDefinition.ts @@ -17,35 +17,39 @@ import EditorCommon = require('vs/editor/common/editorCommon'); import {IModelService} from 'vs/editor/common/services/modelService'; import Modes = require('vs/editor/common/modes'); import {IOnEnterSupportOptions} from 'vs/editor/common/modes/supports/onEnter'; +import {CharacterPair, IRichEditConfiguration} from 'vs/editor/common/modes/supports/richEditSupport'; -export function createCommentsSupport(lexer: MonarchCommonTypes.ILexer): Supports.ICommentsSupportContribution { - return { - commentsConfiguration: { - lineCommentTokens: [lexer.lineComment], - blockCommentStartToken: lexer.blockCommentStart, - blockCommentEndToken: lexer.blockCommentEnd - } - }; -} +export function createRichEditSupport(lexer: MonarchCommonTypes.ILexer): IRichEditConfiguration { -export function createBracketElectricCharacterContribution(lexer: MonarchCommonTypes.ILexer): Supports.IBracketElectricCharacterContribution { - return { - brackets: lexer.standardBrackets, - regexBrackets: lexer.enhancedBrackets, - caseInsensitive: lexer.ignoreCase, - embeddedElectricCharacters: lexer.outdentTriggers.split('') - }; -} + function toBracket(input:Modes.IBracketPair): CharacterPair { + return [input.open, input.close]; + } -export function createTokenTypeClassificationSupportContribution(lexer: MonarchCommonTypes.ILexer): Supports.ITokenTypeClassificationSupportContribution { - return { - wordDefinition: lexer.wordDefinition - }; -} + function toBrackets(input:Modes.IBracketPair[]): CharacterPair[] { + return input.map(toBracket); + } -export function createCharacterPairContribution(lexer: MonarchCommonTypes.ILexer): Modes.ICharacterPairContribution { return { - autoClosingPairs: lexer.autoClosingPairs + + wordPattern: lexer.wordDefinition, + + comments: { + lineComment: lexer.lineComment, + blockComment: [lexer.blockCommentStart, lexer.blockCommentEnd] + }, + + brackets: toBrackets(lexer.standardBrackets), + + __electricCharacterSupport: { + brackets: lexer.standardBrackets, + regexBrackets: lexer.enhancedBrackets, + caseInsensitive: lexer.ignoreCase, + embeddedElectricCharacters: lexer.outdentTriggers.split('') + }, + + __characterPairSupport: { + autoClosingPairs: lexer.autoClosingPairs + } }; } @@ -67,12 +71,6 @@ function _addSuggestionsAtPosition(model: EditorCommon.IModel, position:EditorCo return superSuggestions; } -export function createOnEnterSupportOptions(lexer:MonarchCommonTypes.ILexer): IOnEnterSupportOptions { - return { - brackets: lexer.standardBrackets - }; -} - export function createSuggestSupport(modelService: IModelService, mode:Modes.IMode, lexer:MonarchCommonTypes.ILexer): Supports.IComposableSuggestContribution { if (lexer.suggestSupport.textualCompletions && mode instanceof AbstractMode) { return { diff --git a/src/vs/editor/common/modes/nullMode.ts b/src/vs/editor/common/modes/nullMode.ts index 20644133a02..2ace239d324 100644 --- a/src/vs/editor/common/modes/nullMode.ts +++ b/src/vs/editor/common/modes/nullMode.ts @@ -80,10 +80,14 @@ export class NullMode implements Modes.IMode { public static ID = 'vs.editor.modes.nullMode'; - public tokenTypeClassificationSupport: Modes.ITokenTypeClassificationSupport; + public richEditSupport: Modes.IRichEditSupport; constructor() { - this.tokenTypeClassificationSupport = this; + this.richEditSupport = { + tokenTypeClassification: { + getWordDefinition: () => NullMode.DEFAULT_WORD_REGEXP + } + }; } public getId():string { @@ -93,10 +97,6 @@ export class NullMode implements Modes.IMode { public toSimplifiedMode(): Modes.IMode { return this; } - - public getWordDefinition():RegExp { - return NullMode.DEFAULT_WORD_REGEXP; - } } export function nullTokenize(mode: Modes.IMode, buffer:string, state: Modes.IState, deltaOffset:number = 0, stopAtOffset?:number): Modes.ILineTokens { diff --git a/src/vs/editor/common/modes/supports.ts b/src/vs/editor/common/modes/supports.ts index 71bb9bd5f35..67a4644ff44 100644 --- a/src/vs/editor/common/modes/supports.ts +++ b/src/vs/editor/common/modes/supports.ts @@ -503,57 +503,6 @@ export class TokenizationSupport extends AbstractSupport implements Modes.IToken } } -export interface IBracketElectricCharacterContribution { - brackets: Modes.IBracketPair[]; - regexBrackets?: Modes.IRegexBracketPair[]; - docComment?: Modes.IDocComment; - caseInsensitive?: boolean; - embeddedElectricCharacters?: string[]; -} -export class BracketElectricCharacterSupport extends AbstractSupport implements Modes.IElectricCharacterSupport { - - private contribution: IBracketElectricCharacterContribution; - private brackets: Brackets; - - constructor(mode:Modes.IMode, contribution: IBracketElectricCharacterContribution) { - super(mode); - this.contribution = contribution; - this.brackets = new Brackets(contribution.brackets, contribution.regexBrackets, - contribution.docComment, contribution.caseInsensitive); - } - - public getElectricCharacters(): string[]{ - if (Array.isArray(this.contribution.embeddedElectricCharacters)) { - return this.contribution.embeddedElectricCharacters.concat(this.brackets.getElectricCharacters()); - } - return this.brackets.getElectricCharacters(); - } - - public onElectricCharacter(context:Modes.ILineContext, offset:number): Modes.IElectricAction { - return handleEvent(context, offset, (nestedMode:Modes.IMode, context:Modes.ILineContext, offset:number) => { - if (this.mode === nestedMode) { - return this.brackets.onElectricCharacter(context, offset); - } else if (nestedMode.electricCharacterSupport) { - return nestedMode.electricCharacterSupport.onElectricCharacter(context, offset); - } else { - return null; - } - }); - } - - public onEnter(context: Modes.ILineContext, offset: number): Modes.IEnterAction { - return handleEvent(context, offset, (nestedMode:Modes.IMode, context:Modes.ILineContext, offset:number) => { - if (this.mode === nestedMode) { - return this.brackets.onEnter(context, offset); - } else if (nestedMode.electricCharacterSupport) { - return nestedMode.electricCharacterSupport.onEnter(context, offset); - } else { - return null; - } - }); - } -} - // TODO@Alex -> refactor to use `brackets` from language configuration export function getBracketFor(tokenType:string, tokenText:string, mode:Modes.IMode): Modes.Bracket { if (tokenText === '{' || tokenText === '(' || tokenText === '[') { @@ -786,61 +735,6 @@ export class ComposableSuggestSupport extends SuggestSupport { } -export class CharacterPairSupport extends AbstractSupport implements Modes.ICharacterPairSupport { - - private _autoClosingPairs: Modes.IAutoClosingPairConditional[]; - private _surroundingPairs: Modes.IAutoClosingPair[]; - - constructor(mode: Modes.IMode, contribution: Modes.ICharacterPairContribution) { - - super(mode); - this._autoClosingPairs = contribution.autoClosingPairs; - this._surroundingPairs = Array.isArray(contribution.surroundingPairs) ? contribution.surroundingPairs : contribution.autoClosingPairs; - } - - public getAutoClosingPairs(): Modes.IAutoClosingPair[] { - return this._autoClosingPairs; - } - - public shouldAutoClosePair(character:string, context:Modes.ILineContext, offset:number): boolean { - return handleEvent(context, offset, (nestedMode:Modes.IMode, context:Modes.ILineContext, offset:number) => { - if (this.mode === nestedMode) { - - // Always complete on empty line - if (context.getTokenCount() === 0) { - return true; - } - - var tokenIndex = context.findIndexOfOffset(offset - 1); - var tokenType = context.getTokenType(tokenIndex); - - for (var i = 0; i < this._autoClosingPairs.length; ++i) { - if (this._autoClosingPairs[i].open === character) { - if (this._autoClosingPairs[i].notIn) { - for (var notInIndex = 0; notInIndex < this._autoClosingPairs[i].notIn.length; ++notInIndex) { - if (tokenType.indexOf(this._autoClosingPairs[i].notIn[notInIndex]) > -1) { - return false; - } - } - } - break; - } - } - - return true; - } else if (nestedMode.characterPairSupport) { - return nestedMode.characterPairSupport.shouldAutoClosePair(character, context, offset); - } else { - return null; - } - }); - } - - public getSurroundingPairs(): Modes.IAutoClosingPair[]{ - return this._surroundingPairs; - } -} - export interface IReplaceSupportHelper { valueSetReplace(valueSet: string[], value: string, up: boolean): string; valueSetsReplace(valueSets: string[][], value: string, up: boolean): string; @@ -1012,41 +906,3 @@ export class MainInplaceReplaceSupport extends AbstractInplaceReplaceSupport { return this.modelService.getModel(resource); } } - -export interface ICommentsSupportContribution { - commentsConfiguration: Modes.ICommentsConfiguration; -} - -export class CommentsSupport implements Modes.ICommentsSupport { - - private _contribution: ICommentsSupportContribution; - - constructor(contribution:ICommentsSupportContribution) { - this._contribution = contribution; - } - - public getCommentsConfiguration(): Modes.ICommentsConfiguration { - return this._contribution.commentsConfiguration; - } - -} - -export interface ITokenTypeClassificationSupportContribution { - wordDefinition?: RegExp; -} - -export class TokenTypeClassificationSupport implements Modes.ITokenTypeClassificationSupport { - - private _contribution: ITokenTypeClassificationSupportContribution; - - constructor(contribution: ITokenTypeClassificationSupportContribution) { - this._contribution = contribution; - } - - public getWordDefinition(): RegExp { - if (typeof this._contribution.wordDefinition === 'undefined') { - return NullMode.DEFAULT_WORD_REGEXP; - } - return this._contribution.wordDefinition; - } -} \ No newline at end of file diff --git a/src/vs/editor/common/modes/supports/characterPair.ts b/src/vs/editor/common/modes/supports/characterPair.ts new file mode 100644 index 00000000000..406487d4c2f --- /dev/null +++ b/src/vs/editor/common/modes/supports/characterPair.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * 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 {handleEvent} from 'vs/editor/common/modes/supports'; +import * as Modes from 'vs/editor/common/modes'; + +export class CharacterPairSupport implements Modes.IRichEditCharacterPair { + + private _modeId: string; + private _autoClosingPairs: Modes.IAutoClosingPairConditional[]; + private _surroundingPairs: Modes.IAutoClosingPair[]; + + constructor(modeId: string, contribution: Modes.ICharacterPairContribution) { + this._modeId = modeId; + this._autoClosingPairs = contribution.autoClosingPairs; + this._surroundingPairs = Array.isArray(contribution.surroundingPairs) ? contribution.surroundingPairs : contribution.autoClosingPairs; + } + + public getAutoClosingPairs(): Modes.IAutoClosingPair[] { + return this._autoClosingPairs; + } + + public shouldAutoClosePair(character:string, context:Modes.ILineContext, offset:number): boolean { + return handleEvent(context, offset, (nestedMode:Modes.IMode, context:Modes.ILineContext, offset:number) => { + if (this._modeId === nestedMode.getId()) { + + // Always complete on empty line + if (context.getTokenCount() === 0) { + return true; + } + + var tokenIndex = context.findIndexOfOffset(offset - 1); + var tokenType = context.getTokenType(tokenIndex); + + for (var i = 0; i < this._autoClosingPairs.length; ++i) { + if (this._autoClosingPairs[i].open === character) { + if (this._autoClosingPairs[i].notIn) { + for (var notInIndex = 0; notInIndex < this._autoClosingPairs[i].notIn.length; ++notInIndex) { + if (tokenType.indexOf(this._autoClosingPairs[i].notIn[notInIndex]) > -1) { + return false; + } + } + } + break; + } + } + + return true; + } else if (nestedMode.richEditSupport && nestedMode.richEditSupport.characterPair) { + return nestedMode.richEditSupport.characterPair.shouldAutoClosePair(character, context, offset); + } else { + return null; + } + }); + } + + public getSurroundingPairs(): Modes.IAutoClosingPair[]{ + return this._surroundingPairs; + } +} diff --git a/src/vs/editor/common/modes/supports/comments.ts b/src/vs/editor/common/modes/supports/comments.ts new file mode 100644 index 00000000000..92f5fa97d01 --- /dev/null +++ b/src/vs/editor/common/modes/supports/comments.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as Modes from 'vs/editor/common/modes'; + +export interface ICommentsSupportContribution { + commentsConfiguration: Modes.ICommentsConfiguration; +} + +export class CommentsSupport implements Modes.IRichEditComments { + + private _contribution: ICommentsSupportContribution; + + constructor(contribution:ICommentsSupportContribution) { + this._contribution = contribution; + } + + public getCommentsConfiguration(): Modes.ICommentsConfiguration { + return this._contribution.commentsConfiguration; + } + +} diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts new file mode 100644 index 00000000000..936106c97e6 --- /dev/null +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * 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 {Brackets} from 'vs/editor/common/modes/autoIndentation'; +import * as Modes from 'vs/editor/common/modes'; +import {handleEvent} from 'vs/editor/common/modes/supports'; + +export interface IBracketElectricCharacterContribution { + brackets: Modes.IBracketPair[]; + regexBrackets?: Modes.IRegexBracketPair[]; + docComment?: Modes.IDocComment; + caseInsensitive?: boolean; + embeddedElectricCharacters?: string[]; +} + +export class BracketElectricCharacterSupport implements Modes.IRichEditElectricCharacter { + + private _modeId: string; + private contribution: IBracketElectricCharacterContribution; + private brackets: Brackets; + + constructor(modeId: string, contribution: IBracketElectricCharacterContribution) { + this._modeId = modeId; + this.contribution = contribution; + this.brackets = new Brackets(contribution.brackets, contribution.regexBrackets, + contribution.docComment, contribution.caseInsensitive); + } + + public getElectricCharacters(): string[]{ + if (Array.isArray(this.contribution.embeddedElectricCharacters)) { + return this.contribution.embeddedElectricCharacters.concat(this.brackets.getElectricCharacters()); + } + return this.brackets.getElectricCharacters(); + } + + public onElectricCharacter(context:Modes.ILineContext, offset:number): Modes.IElectricAction { + return handleEvent(context, offset, (nestedMode:Modes.IMode, context:Modes.ILineContext, offset:number) => { + if (this._modeId === nestedMode.getId()) { + return this.brackets.onElectricCharacter(context, offset); + } else if (nestedMode.richEditSupport && nestedMode.richEditSupport.electricCharacter) { + return nestedMode.richEditSupport.electricCharacter.onElectricCharacter(context, offset); + } else { + return null; + } + }); + } + + public onEnter(context: Modes.ILineContext, offset: number): Modes.IEnterAction { + return handleEvent(context, offset, (nestedMode:Modes.IMode, context:Modes.ILineContext, offset:number) => { + if (this._modeId === nestedMode.getId()) { + return this.brackets.onEnter(context, offset); + } else if (nestedMode.richEditSupport && nestedMode.richEditSupport.electricCharacter) { + return nestedMode.richEditSupport.electricCharacter.onEnter(context, offset); + } else { + return null; + } + }); + } +} diff --git a/src/vs/editor/common/modes/supports/onEnter.ts b/src/vs/editor/common/modes/supports/onEnter.ts index b4cab652153..d66923273d8 100644 --- a/src/vs/editor/common/modes/supports/onEnter.ts +++ b/src/vs/editor/common/modes/supports/onEnter.ts @@ -5,7 +5,7 @@ 'use strict'; import {handleEvent} from 'vs/editor/common/modes/supports'; -import {IEnterAction, IndentAction, IOnEnterSupport, ILineContext, IMode} from 'vs/editor/common/modes'; +import {IEnterAction, IndentAction, IRichEditOnEnter, ILineContext, IMode} from 'vs/editor/common/modes'; import EditorCommon = require('vs/editor/common/editorCommon'); import Errors = require('vs/base/common/errors'); import Strings = require('vs/base/common/strings'); @@ -40,7 +40,7 @@ interface IProcessedBracketPair extends IBracketPair { closeRegExp: RegExp; } -export class OnEnterSupport implements IOnEnterSupport { +export class OnEnterSupport implements IRichEditOnEnter { private static _INDENT: IEnterAction = { indentAction: IndentAction.Indent }; private static _INDENT_OUTDENT: IEnterAction = { indentAction: IndentAction.IndentOutdent }; @@ -78,8 +78,8 @@ export class OnEnterSupport implements IOnEnterSupport { return handleEvent(context, position.column - 1, (nestedMode:IMode, context:ILineContext, offset:number) => { if (this._modeId === nestedMode.getId()) { return this._onEnter(model, position); - } else if (nestedMode.onEnterSupport) { - return nestedMode.onEnterSupport.onEnter(model, position); + } else if (nestedMode.richEditSupport && nestedMode.richEditSupport.onEnter) { + return nestedMode.richEditSupport.onEnter.onEnter(model, position); } else { return null; } @@ -184,19 +184,21 @@ export class OnEnterSupport implements IOnEnterSupport { export function getRawEnterActionAtPosition(model:EditorCommon.ITokenizedModel, lineNumber:number, column:number): IEnterAction { let enterAction:IEnterAction; - if (model.getMode().onEnterSupport) { + let richEditSupport = model.getMode().richEditSupport; + + if (richEditSupport && richEditSupport.onEnter) { try { - enterAction = model.getMode().onEnterSupport.onEnter(model, new Position(lineNumber, column)); + enterAction = richEditSupport.onEnter.onEnter(model, new Position(lineNumber, column)); } catch (e) { Errors.onUnexpectedError(e); } } if (!enterAction) { - if (model.getMode().electricCharacterSupport) { + if (richEditSupport && richEditSupport.electricCharacter) { let lineContext = model.getLineContext(lineNumber); try { - enterAction = model.getMode().electricCharacterSupport.onEnter(lineContext, column - 1); + enterAction = richEditSupport.electricCharacter.onEnter(lineContext, column - 1); } catch(e) { Errors.onUnexpectedError(e); } diff --git a/src/vs/editor/common/modes/supports/richEditSupport.ts b/src/vs/editor/common/modes/supports/richEditSupport.ts new file mode 100644 index 00000000000..f777cb7dbb4 --- /dev/null +++ b/src/vs/editor/common/modes/supports/richEditSupport.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as Modes from 'vs/editor/common/modes'; +import {OnEnterSupport, IOnEnterSupportOptions, IIndentationRules, IOnEnterRegExpRules} from 'vs/editor/common/modes/supports/onEnter'; +import {CharacterPairSupport} from 'vs/editor/common/modes/supports/characterPair'; +import {BracketElectricCharacterSupport, IBracketElectricCharacterContribution} from 'vs/editor/common/modes/supports/electricCharacter'; +import {TokenTypeClassificationSupport} from 'vs/editor/common/modes/supports/tokenTypeClassification'; +import {CommentsSupport, ICommentsSupportContribution} from 'vs/editor/common/modes/supports/comments'; + +export type CharacterPair = [string, string]; + +export interface CommentRule { + lineComment?: string; + blockComment?: CharacterPair; +} + +export interface IRichEditConfiguration { + comments?: CommentRule; + brackets?: CharacterPair[]; + wordPattern?: RegExp; + indentationRules?: IIndentationRules; + onEnterRules?: IOnEnterRegExpRules[]; + __electricCharacterSupport?: IBracketElectricCharacterContribution; + __characterPairSupport?: Modes.ICharacterPairContribution; +} + +export class RichEditSupport implements Modes.IRichEditSupport { + + public electricCharacter: Modes.IRichEditElectricCharacter; + public comments: Modes.IRichEditComments; + public characterPair: Modes.IRichEditCharacterPair; + public tokenTypeClassification: Modes.IRichEditTokenTypeClassification; + public onEnter: Modes.IRichEditOnEnter; + + constructor(modeId:string, conf:IRichEditConfiguration) { + + this._handleOnEnter(modeId, conf); + + this._handleComments(modeId, conf); + + if (conf.__characterPairSupport) { + this.characterPair = new CharacterPairSupport(modeId, conf.__characterPairSupport); + } + + if (conf.__electricCharacterSupport) { + this.electricCharacter = new BracketElectricCharacterSupport(modeId, conf.__electricCharacterSupport); + } + + this.tokenTypeClassification = new TokenTypeClassificationSupport({ + wordDefinition: conf.wordPattern + }); + } + + private _handleOnEnter(modeId:string, conf:IRichEditConfiguration): void { + // on enter + let onEnter: IOnEnterSupportOptions = {}; + let empty = true; + let {brackets, indentationRules, onEnterRules} = conf; + + if (brackets) { + empty = false; + onEnter.brackets = brackets.map(pair => { + let [open, close] = pair; + return { open, close }; + }); + } + if (indentationRules) { + empty = false; + onEnter.indentationRules = indentationRules; + } + if (onEnterRules) { + empty = false; + onEnter.regExpRules = onEnterRules; + } + + if (!empty) { + this.onEnter = new OnEnterSupport(modeId, onEnter); + } + } + + private _handleComments(modeId:string, conf:IRichEditConfiguration): void { + let {comments} = conf; + + // comment configuration + if (comments) { + let contrib: ICommentsSupportContribution = { commentsConfiguration: {} }; + if (comments.lineComment) { + contrib.commentsConfiguration.lineCommentTokens = [comments.lineComment]; + } + if (comments.blockComment) { + let [blockStart, blockEnd] = comments.blockComment; + contrib.commentsConfiguration.blockCommentStartToken = blockStart; + contrib.commentsConfiguration.blockCommentEndToken = blockEnd; + } + this.comments = new CommentsSupport(contrib); + } + } + +} diff --git a/src/vs/editor/common/modes/supports/tokenTypeClassification.ts b/src/vs/editor/common/modes/supports/tokenTypeClassification.ts new file mode 100644 index 00000000000..516c2a9a028 --- /dev/null +++ b/src/vs/editor/common/modes/supports/tokenTypeClassification.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as Modes from 'vs/editor/common/modes'; +import {NullMode} from 'vs/editor/common/modes/nullMode'; + +export interface ITokenTypeClassificationSupportContribution { + wordDefinition?: RegExp; +} + +export class TokenTypeClassificationSupport implements Modes.IRichEditTokenTypeClassification { + + private _contribution: ITokenTypeClassificationSupportContribution; + + constructor(contribution: ITokenTypeClassificationSupportContribution) { + this._contribution = contribution; + } + + public getWordDefinition(): RegExp { + if (typeof this._contribution.wordDefinition === 'undefined') { + return NullMode.DEFAULT_WORD_REGEXP; + } + return this._contribution.wordDefinition; + } +} diff --git a/src/vs/editor/common/services/modeService.ts b/src/vs/editor/common/services/modeService.ts index 82ad1dd13b5..54f75f85a1d 100644 --- a/src/vs/editor/common/services/modeService.ts +++ b/src/vs/editor/common/services/modeService.ts @@ -11,6 +11,7 @@ import Supports = require ('vs/editor/common/modes/supports'); import MonarchTypes = require('vs/editor/common/modes/monarch/monarchTypes'); import {IOnEnterSupportOptions} from 'vs/editor/common/modes/supports/onEnter'; import {IDisposable} from 'vs/base/common/lifecycle'; +import {IRichEditConfiguration} from 'vs/editor/common/modes/supports/richEditSupport'; export var IModeService = createDecorator('modeService'); @@ -34,11 +35,8 @@ export interface IModeService { getOrCreateModeByLanguageName(languageName: string): TPromise; getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?:string): TPromise; - registerDeclarativeCharacterPairSupport(modeId: string, support: Modes.ICharacterPairContribution): IDisposable; registerCodeLensSupport(modeId: string, support: Modes.ICodeLensSupport): IDisposable; - registerDeclarativeCommentsSupport(modeId: string, support: Supports.ICommentsSupportContribution): IDisposable; registerDeclarativeDeclarationSupport(modeId: string, contribution: Supports.IDeclarationContribution): IDisposable; - registerDeclarativeElectricCharacterSupport(modeId: string, support: Supports.IBracketElectricCharacterContribution): IDisposable; registerExtraInfoSupport(modeId: string, support: Modes.IExtraInfoSupport): IDisposable; registerFormattingSupport(modeId: string, support: Modes.IFormattingSupport): IDisposable; registerInplaceReplaceSupport(modeId: string, support: Modes.IInplaceReplaceSupport): IDisposable; @@ -50,8 +48,7 @@ export interface IModeService { registerRenameSupport(modeId: string, support: Modes.IRenameSupport): IDisposable; registerDeclarativeSuggestSupport(modeId: string, declaration: Supports.ISuggestContribution): IDisposable; registerTokenizationSupport(modeId: string, callback: (mode: Modes.IMode) => Modes.ITokenizationSupport): IDisposable; - registerDeclarativeTokenTypeClassificationSupport(modeId: string, support: Supports.ITokenTypeClassificationSupportContribution): IDisposable; - registerDeclarativeOnEnterSupport(modeId: string, support: IOnEnterSupportOptions): IDisposable; + registerRichEditSupport(modeId: string, support: IRichEditConfiguration): IDisposable; registerMonarchDefinition(modeId:string, language:MonarchTypes.ILanguage): IDisposable; } diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts index 59023c8930d..5733cf2094c 100644 --- a/src/vs/editor/common/services/modeServiceImpl.ts +++ b/src/vs/editor/common/services/modeServiceImpl.ts @@ -25,6 +25,7 @@ import MonarchCommonTypes = require('vs/editor/common/modes/monarch/monarchCommo import {OnEnterSupport, IOnEnterSupportOptions} from 'vs/editor/common/modes/supports/onEnter'; import {IDisposable, combinedDispose, empty as EmptyDisposable} from 'vs/base/common/lifecycle'; import {createAsyncDescriptor0, createAsyncDescriptor1} from 'vs/platform/instantiation/common/descriptors'; +import {RichEditSupport, IRichEditConfiguration} from 'vs/editor/common/modes/supports/richEditSupport'; interface IModeConfigurationMap { [modeId: string]: any; } @@ -263,15 +264,7 @@ export class ModeServiceImpl implements IModeService { return createTokenizationSupport(this, mode, lexer); }), - this.registerDeclarativeCommentsSupport(modeId, MonarchDefinition.createCommentsSupport(lexer)), - - this.registerDeclarativeElectricCharacterSupport(modeId, MonarchDefinition.createBracketElectricCharacterContribution(lexer)), - - this.registerDeclarativeTokenTypeClassificationSupport(modeId, MonarchDefinition.createTokenTypeClassificationSupportContribution(lexer)), - - this.registerDeclarativeCharacterPairSupport(modeId, MonarchDefinition.createCharacterPairContribution(lexer)), - - this.registerDeclarativeOnEnterSupport(modeId, MonarchDefinition.createOnEnterSupportOptions(lexer)) + this.registerRichEditSupport(modeId, MonarchDefinition.createRichEditSupport(lexer)) ); } @@ -280,26 +273,18 @@ export class ModeServiceImpl implements IModeService { return this.doRegisterMonarchDefinition(modeId, lexer); } - public registerDeclarativeCharacterPairSupport(modeId: string, support: Modes.ICharacterPairContribution): IDisposable { - return this.registerModeSupport(modeId, 'characterPairSupport', (mode) => new Supports.CharacterPairSupport(mode, support)); - } - public registerCodeLensSupport(modeId: string, support: Modes.ICodeLensSupport): IDisposable { return this.registerModeSupport(modeId, 'codeLensSupport', (mode) => support); } - public registerDeclarativeCommentsSupport(modeId: string, support: Supports.ICommentsSupportContribution): IDisposable { - return this.registerModeSupport(modeId, 'commentsSupport', (mode) => new Supports.CommentsSupport(support)); + public registerRichEditSupport(modeId: string, support: IRichEditConfiguration): IDisposable { + return this.registerModeSupport(modeId, 'richEditSupport', (mode) => new RichEditSupport(modeId, support)); } public registerDeclarativeDeclarationSupport(modeId: string, contribution: Supports.IDeclarationContribution): IDisposable { return this.registerModeSupport(modeId, 'declarationSupport', (mode) => new Supports.DeclarationSupport(mode, contribution)); } - public registerDeclarativeElectricCharacterSupport(modeId: string, support: Supports.IBracketElectricCharacterContribution): IDisposable { - return this.registerModeSupport(modeId, 'electricCharacterSupport', (mode) => new Supports.BracketElectricCharacterSupport(mode, support)); - } - public registerExtraInfoSupport(modeId: string, support: Modes.IExtraInfoSupport): IDisposable { return this.registerModeSupport(modeId, 'extraInfoSupport', (mode) => support); } @@ -343,14 +328,6 @@ export class ModeServiceImpl implements IModeService { public registerTokenizationSupport(modeId: string, callback: (mode: Modes.IMode) => Modes.ITokenizationSupport): IDisposable { return this.registerModeSupport(modeId, 'tokenizationSupport', callback); } - - public registerDeclarativeTokenTypeClassificationSupport(modeId: string, support: Supports.ITokenTypeClassificationSupportContribution): IDisposable { - return this.registerModeSupport(modeId, 'tokenTypeClassificationSupport', (mode) => new Supports.TokenTypeClassificationSupport(support)); - } - - public registerDeclarativeOnEnterSupport(modeId: string, opts: IOnEnterSupportOptions): IDisposable { - return this.registerModeSupport(modeId, 'onEnterSupport', (mode) => new OnEnterSupport(modeId, opts)); - } } export class MainThreadModeServiceImpl extends ModeServiceImpl { diff --git a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts index a8829b05abb..71c1ca161f1 100644 --- a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts @@ -121,7 +121,8 @@ export class BlockCommentCommand implements EditorCommon.ICommand { var endLineNumber = this._selection.endLineNumber; var endColumn = this._selection.endColumn; - var commentsSupport = model.getModeAtPosition(startLineNumber, startColumn).commentsSupport; + let richEditSupport = model.getModeAtPosition(startLineNumber, startColumn).richEditSupport; + var commentsSupport = richEditSupport? richEditSupport.comments : null; if (!commentsSupport) { // Mode does not support comments return; diff --git a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts index ba4d57a657f..9bd60eba3b2 100644 --- a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts @@ -81,7 +81,7 @@ export class LineCommentCommand implements EditorCommon.ICommand { if (seenModes[modeId]) { commentStr = seenModes[modeId]; } else { - config = (mode.commentsSupport ? mode.commentsSupport.getCommentsConfiguration() : null); + config = (mode.richEditSupport && mode.richEditSupport.comments ? mode.richEditSupport.comments.getCommentsConfiguration() : null); commentStr = (config && config.lineCommentTokens && config.lineCommentTokens.length > 0 ? config.lineCommentTokens[0] : null); if (commentStr === null || commentStr.length === 0) { // Mode does not support line comments @@ -273,7 +273,8 @@ export class LineCommentCommand implements EditorCommon.ICommand { * Given an unsuccessful analysis, delegate to the block comment command */ private _executeBlockComment(model:EditorCommon.ITokenizedModel, builder:EditorCommon.IEditOperationBuilder, s:EditorCommon.IEditorSelection): void { - var commentsSupport = model.getModeAtPosition(s.startLineNumber, s.startColumn).commentsSupport; + let richEditSupport = model.getModeAtPosition(s.startLineNumber, s.startColumn).richEditSupport; + var commentsSupport = richEditSupport ? richEditSupport.comments : null; if (!commentsSupport) { // Mode does not support comments return; diff --git a/src/vs/editor/node/languageConfiguration.ts b/src/vs/editor/node/languageConfiguration.ts index 0ce88b2c185..060a3de6894 100644 --- a/src/vs/editor/node/languageConfiguration.ts +++ b/src/vs/editor/node/languageConfiguration.ts @@ -12,6 +12,7 @@ import Supports = require ('vs/editor/common/modes/supports'); import {IOnEnterSupportOptions} from 'vs/editor/common/modes/supports/onEnter'; import json = require('vs/base/common/json'); import {ICharacterPairContribution} from 'vs/editor/common/modes'; +import {IRichEditConfiguration} from 'vs/editor/common/modes/supports/richEditSupport'; type CharacterPair = [string, string]; @@ -62,47 +63,33 @@ export class LanguageConfigurationFileHandler { } private _handleConfig(modeId:string, configuration:ILanguageConfiguration): void { - if (configuration.comments) { - let comments = configuration.comments; - let contrib: Supports.ICommentsSupportContribution = { commentsConfiguration: {} }; - if (comments.lineComment) { - contrib.commentsConfiguration.lineCommentTokens = [comments.lineComment]; - } - if (comments.blockComment) { - contrib.commentsConfiguration.blockCommentStartToken = comments.blockComment[0]; - contrib.commentsConfiguration.blockCommentEndToken = comments.blockComment[1]; - } + let richEditConfig:IRichEditConfiguration = {}; - this._modeService.registerDeclarativeCommentsSupport(modeId, contrib); + if (configuration.comments) { + richEditConfig.comments = configuration.comments; } if (configuration.brackets) { - let brackets = configuration.brackets; + richEditConfig.brackets = configuration.brackets; - let onEnterContrib: IOnEnterSupportOptions = {}; - onEnterContrib.brackets = brackets.map(pair => { - let [open, close] = pair; - return { open: open, close: close }; - }); - this._modeService.registerDeclarativeOnEnterSupport(modeId, onEnterContrib); - - let characterPairContrib: ICharacterPairContribution = { - autoClosingPairs: brackets.map(pair => { + richEditConfig.__characterPairSupport = { + autoClosingPairs: configuration.brackets.map(pair => { let [open, close] = pair; return { open: open, close: close }; }) - }; - this._modeService.registerDeclarativeCharacterPairSupport(modeId, characterPairContrib); + } } // TMSyntax hard-codes these and tokenizes them as brackets - this._modeService.registerDeclarativeElectricCharacterSupport(modeId, { + richEditConfig.__electricCharacterSupport = { brackets: [ { tokenType:'delimiter.curly.' + modeId, open: '{', close: '}', isElectric: true }, { tokenType:'delimiter.square.' + modeId, open: '[', close: ']', isElectric: true }, { tokenType:'delimiter.paren.' + modeId, open: '(', close: ')', isElectric: true } ] - }); + }; + + this._modeService.registerRichEditSupport(modeId, richEditConfig); } } diff --git a/src/vs/editor/standalone-languages/test/testUtil.ts b/src/vs/editor/standalone-languages/test/testUtil.ts index ee0ca7c0647..e68ae122711 100644 --- a/src/vs/editor/standalone-languages/test/testUtil.ts +++ b/src/vs/editor/standalone-languages/test/testUtil.ts @@ -11,6 +11,7 @@ import modesUtil = require('vs/editor/test/common/modesUtil'); import monarchCompile = require('vs/editor/common/modes/monarch/monarchCompile'); import MonarchDefinition = require('vs/editor/common/modes/monarch/monarchDefinition'); import {OnEnterSupport} from 'vs/editor/common/modes/supports/onEnter'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; export enum Bracket { None = 0, @@ -47,8 +48,8 @@ export function testOnEnter(name:string, language: types.ILanguage, callback:(as suite(language.displayName || name, () => { test('onEnter', () => { var lexer = monarchCompile.compile(language); - var onEnterSupport = new OnEnterSupport('test', MonarchDefinition.createOnEnterSupportOptions(lexer)); - callback(modesUtil.createOnEnterAsserter('test', onEnterSupport)); + var richEditSupport = new RichEditSupport('test', MonarchDefinition.createRichEditSupport(lexer)); + callback(modesUtil.createOnEnterAsserter('test', richEditSupport)); }); }); -} \ No newline at end of file +} diff --git a/src/vs/editor/test/common/commands/shiftCommand.test.ts b/src/vs/editor/test/common/commands/shiftCommand.test.ts index b55658da809..7a2e1035144 100644 --- a/src/vs/editor/test/common/commands/shiftCommand.test.ts +++ b/src/vs/editor/test/common/commands/shiftCommand.test.ts @@ -13,6 +13,7 @@ import {Selection} from 'vs/editor/common/core/selection'; import {Cursor} from 'vs/editor/common/controller/cursor'; import * as Modes from 'vs/editor/common/modes'; import {OnEnterSupport} from 'vs/editor/common/modes/supports/onEnter'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; function testShiftCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { TU.testCommand(lines, null, selection, (sel) => new ShiftCommand(sel, { @@ -32,16 +33,17 @@ function testUnshiftCommand(lines: string[], selection: Selection, expectedLines class DocBlockCommentMode implements Modes.IMode { - public onEnterSupport: Modes.IOnEnterSupport; + public richEditSupport: Modes.IRichEditSupport; constructor() { - this.onEnterSupport = new OnEnterSupport(this.getId(), { + this.richEditSupport = new RichEditSupport(this.getId(), { brackets: [ - { open: '(', close: ')' }, - { open: '{', close: '}' }, - { open: '[', close: ']' } + ['(', ')'], + ['{', '}'], + ['[', ']'] ], - regExpRules: [ + + onEnterRules: [ { // e.g. /** | */ beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, diff --git a/src/vs/editor/test/common/controller/cursor.test.ts b/src/vs/editor/test/common/controller/cursor.test.ts index e0715e8ff74..ffb3e4b46b2 100644 --- a/src/vs/editor/test/common/controller/cursor.test.ts +++ b/src/vs/editor/test/common/controller/cursor.test.ts @@ -15,7 +15,7 @@ import {Handler, EventType, IPosition, ISelection, EndOfLinePreference} from 'vs import {MockConfiguration} from 'vs/editor/test/common/mocks/mockConfiguration'; import {EditOperation} from 'vs/editor/common/core/editOperation'; import {AbstractState} from 'vs/editor/common/modes/abstractState'; -import {CharacterPairSupport} from 'vs/editor/common/modes/supports'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; let H = Handler; @@ -768,28 +768,32 @@ class TestMode { } class SurroundingMode extends TestMode { - public characterPairSupport: Modes.ICharacterPairSupport; + public richEditSupport: Modes.IRichEditSupport; constructor() { super(); - this.characterPairSupport = new CharacterPairSupport(this, { - autoClosingPairs: [{ open: '(', close: ')' }] + this.richEditSupport = new RichEditSupport(this.getId(), { + __characterPairSupport: { + autoClosingPairs: [{ open: '(', close: ')' }] + } }); } } class OnEnterMode extends TestMode { - public electricCharacterSupport: Modes.IElectricCharacterSupport; + public richEditSupport: Modes.IRichEditSupport; constructor(indentAction: Modes.IndentAction) { super(); - this.electricCharacterSupport = { - getElectricCharacters: ():string[] => null, - onElectricCharacter: (context:Modes.ILineContext, offset:number): Modes.IElectricAction => null, - onEnter: (context:Modes.ILineContext, offset:number): Modes.IEnterAction => { - return { - indentAction: indentAction - }; + this.richEditSupport = { + electricCharacter: { + getElectricCharacters: ():string[] => null, + onElectricCharacter: (context:Modes.ILineContext, offset:number): Modes.IElectricAction => null, + onEnter: (context:Modes.ILineContext, offset:number): Modes.IEnterAction => { + return { + indentAction: indentAction + }; + } } }; } diff --git a/src/vs/editor/test/common/modesTestUtils.ts b/src/vs/editor/test/common/modesTestUtils.ts index 06ee9ba0549..28ca0fbafa5 100644 --- a/src/vs/editor/test/common/modesTestUtils.ts +++ b/src/vs/editor/test/common/modesTestUtils.ts @@ -4,47 +4,45 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import modes = require('vs/editor/common/modes'); +import Modes = require('vs/editor/common/modes'); import {Arrays} from 'vs/editor/common/core/arrays'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; -class SimpleTokenTypeClassificationMode implements modes.IMode { +class SimpleTokenTypeClassificationMode implements Modes.IMode { private _id:string; - public tokenTypeClassificationSupport: modes.ITokenTypeClassificationSupport; - constructor(id:string, tokenTypeClassificationSupport: modes.ITokenTypeClassificationSupport) { + public richEditSupport: Modes.IRichEditSupport; + + constructor(id:string, wordRegExp:RegExp) { this._id = id; - this.tokenTypeClassificationSupport = tokenTypeClassificationSupport; + this.richEditSupport = new RichEditSupport(this._id, { + wordPattern: wordRegExp + }); } public getId(): string { return this._id; } - public toSimplifiedMode(): modes.IMode { + public toSimplifiedMode(): Modes.IMode { return this; } } -export function createMockMode(id:string, wordRegExp:RegExp = null):modes.IMode { - var tokenTypeClassificationSupport: modes.ITokenTypeClassificationSupport; - if (wordRegExp) { - tokenTypeClassificationSupport = { - getWordDefinition: () => wordRegExp - }; - } - return new SimpleTokenTypeClassificationMode(id, tokenTypeClassificationSupport); +export function createMockMode(id:string, wordRegExp:RegExp = null):Modes.IMode { + return new SimpleTokenTypeClassificationMode(id, wordRegExp); } export interface TokenText { text: string; type: string; - bracket?: modes.Bracket; + bracket?: Modes.Bracket; } -export function createLineContextFromTokenText(tokens: TokenText[]): modes.ILineContext { +export function createLineContextFromTokenText(tokens: TokenText[]): Modes.ILineContext { var line = ''; - var processedTokens: modes.IToken[] = []; + var processedTokens: Modes.IToken[] = []; var indexSoFar = 0; for (var i = 0; i < tokens.length; ++i){ @@ -56,17 +54,17 @@ export function createLineContextFromTokenText(tokens: TokenText[]): modes.ILine return new TestLineContext(line, processedTokens, null); } -export function createLineContext(line:string, tokens:modes.ILineTokens): modes.ILineContext { +export function createLineContext(line:string, tokens:Modes.ILineTokens): Modes.ILineContext { return new TestLineContext(line, tokens.tokens, tokens.modeTransitions); } -class TestLineContext implements modes.ILineContext { +class TestLineContext implements Modes.ILineContext { - public modeTransitions: modes.IModeTransition[]; + public modeTransitions: Modes.IModeTransition[]; private _line:string; - private _tokens: modes.IToken[]; + private _tokens: Modes.IToken[]; - constructor(line:string, tokens: modes.IToken[], modeTransitions:modes.IModeTransition[]) { + constructor(line:string, tokens: Modes.IToken[], modeTransitions:Modes.IModeTransition[]) { this.modeTransitions = modeTransitions; this._line = line; this._tokens = tokens; diff --git a/src/vs/editor/test/common/modesUtil.ts b/src/vs/editor/test/common/modesUtil.ts index 27e29c0d179..054199f7eb1 100644 --- a/src/vs/editor/test/common/modesUtil.ts +++ b/src/vs/editor/test/common/modesUtil.ts @@ -38,7 +38,7 @@ export function createOnElectricCharacter(mode:modes.IMode): IOnElectricCharacte return function onElectricCharacter(line:string, offset:number, state?:modes.IState): modes.IElectricAction { state = state || mode.tokenizationSupport.getInitialState(); var lineTokens = mode.tokenizationSupport.tokenize(line, state); - return mode.electricCharacterSupport.onElectricCharacter(createLineContext(line, lineTokens), offset); + return mode.richEditSupport.electricCharacter.onElectricCharacter(createLineContext(line, lineTokens), offset); }; } @@ -50,7 +50,7 @@ export function createOnEnter(mode:modes.IMode): IOnEnterFunc { return function onEnter(line:string, offset:number, state?:modes.IState): modes.IEnterAction { state = state || mode.tokenizationSupport.getInitialState(); var lineTokens = mode.tokenizationSupport.tokenize(line, state); - return mode.electricCharacterSupport.onEnter(createLineContext(line, lineTokens), offset); + return mode.richEditSupport.electricCharacter.onEnter(createLineContext(line, lineTokens), offset); }; } @@ -103,13 +103,13 @@ class SimpleMode implements modes.IMode { } } -export function createOnEnterAsserter(modeId:string, onEnterSupport: modes.IOnEnterSupport): IOnEnterAsserter { +export function createOnEnterAsserter(modeId:string, richEditSupport: modes.IRichEditSupport): IOnEnterAsserter { var assertOne = (oneLineAboveText:string, beforeText:string, afterText:string, expected: modes.IndentAction) => { var model = new Model( [ oneLineAboveText, beforeText + afterText ].join('\n'), new SimpleMode(modeId) ); - var actual = onEnterSupport.onEnter(model, { lineNumber: 2, column: beforeText.length + 1 }); + var actual = richEditSupport.onEnter.onEnter(model, { lineNumber: 2, column: beforeText.length + 1 }); if (expected === modes.IndentAction.None) { assert.equal(actual, null, oneLineAboveText + '\\n' + beforeText + '|' + afterText); } else { diff --git a/src/vs/editor/test/common/testModes.ts b/src/vs/editor/test/common/testModes.ts index 5ab4205a356..6a2956481ee 100644 --- a/src/vs/editor/test/common/testModes.ts +++ b/src/vs/editor/test/common/testModes.ts @@ -9,6 +9,7 @@ import supports = require('vs/editor/common/modes/supports'); import {AbstractMode} from 'vs/editor/common/modes/abstractMode'; import {AbstractState} from 'vs/editor/common/modes/abstractState'; import {AbstractModeWorker} from 'vs/editor/common/modes/abstractModeWorker'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; export class CommentState extends AbstractState { @@ -32,21 +33,21 @@ export class CommentState extends AbstractState { export class CommentMode extends AbstractMode { - private commentsConfig:modes.ICommentsConfiguration; - public tokenizationSupport: modes.ITokenizationSupport; + public richEditSupport: modes.IRichEditSupport; constructor(commentsConfig:modes.ICommentsConfiguration) { super({ id: 'tests.commentMode', workerParticipants: [] }, null, null); - this.commentsConfig = commentsConfig; this.tokenizationSupport = new supports.TokenizationSupport(this, { getInitialState: () => new CommentState(this, 0) }, false, false); - } - public getCommentsConfiguration():modes.ICommentsConfiguration { - return this.commentsConfig; + this.richEditSupport = { + comments: { + getCommentsConfiguration: () => commentsConfig + } + }; } } diff --git a/src/vs/languages/css/common/css.ts b/src/vs/languages/css/common/css.ts index cc7d1047f58..a8c696496f8 100644 --- a/src/vs/languages/css/common/css.ts +++ b/src/vs/languages/css/common/css.ts @@ -20,6 +20,7 @@ import {IMarker} from 'vs/platform/markers/common/markers'; import {OnEnterSupport} from 'vs/editor/common/modes/supports/onEnter'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IThreadService} from 'vs/platform/thread/common/thread'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; export enum States { Selector, @@ -279,8 +280,7 @@ export class State extends AbstractState { export class CSSMode extends AbstractMode { public tokenizationSupport: Modes.ITokenizationSupport; - public electricCharacterSupport: Modes.IElectricCharacterSupport; - public characterPairSupport: Modes.ICharacterPairSupport; + public richEditSupport: Modes.IRichEditSupport; public referenceSupport: Modes.IReferenceSupport; public logicalSelectionSupport: Modes.ILogicalSelectionSupport; @@ -290,7 +290,6 @@ export class CSSMode extends AbstractMode { public declarationSupport: Modes.IDeclarationSupport; public suggestSupport: Modes.ISuggestSupport; public quickFixSupport: Modes.IQuickFixSupport; - public onEnterSupport: Modes.IOnEnterSupport; constructor( descriptor:Modes.IModeDescriptor, @@ -302,9 +301,37 @@ export class CSSMode extends AbstractMode { this.tokenizationSupport = new supports.TokenizationSupport(this, { getInitialState: () => new State(this, States.Selector, false, null, false, 0) }, false, false); - this.electricCharacterSupport = new supports.BracketElectricCharacterSupport(this, { brackets: [ - { tokenType: 'punctuation.bracket.css', open: '{', close: '}', isElectric: true } - ] }); + + this.richEditSupport = new RichEditSupport(this.getId(), { + // TODO@Martin: This definition does not work with umlauts for example + wordPattern: /(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g, + + comments: { + blockComment: ['/*', '*/'] + }, + + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ], + + __electricCharacterSupport: { + brackets: [ + { tokenType: 'punctuation.bracket.css', open: '{', close: '}', isElectric: true } + ] + }, + + __characterPairSupport: { + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"', notIn: ['string'] }, + { open: '\'', close: '\'', notIn: ['string'] } + ] + } + }); this.occurrencesSupport = this; this.extraInfoSupport = this; @@ -317,27 +344,11 @@ export class CSSMode extends AbstractMode { tokens: [cssTokenTypes.TOKEN_VALUE + '.css'], findDeclaration: (resource, position) => this.findDeclaration(resource, position)}); - this.characterPairSupport = new supports.CharacterPairSupport(this, { - autoClosingPairs: - [ { open: '{', close: '}' }, - { open: '[', close: ']' }, - { open: '(', close: ')' }, - { open: '"', close: '"', notIn: ['string'] }, - { open: '\'', close: '\'', notIn: ['string'] } - ]}); - this.suggestSupport = new supports.SuggestSupport(this, { triggerCharacters: [' ', ':'], excludeTokens: ['comment.css', 'string.css'], suggest: (resource, position) => this.suggest(resource, position)}); - this.onEnterSupport = new OnEnterSupport(this.getId(), { - brackets: [ - { open: '(', close: ')' }, - { open: '{', close: '}' }, - { open: '[', close: ']' } - ] - }); this.quickFixSupport = this; } @@ -376,15 +387,6 @@ export class CSSMode extends AbstractMode { return this._worker((w) => w.getOutline(resource)); } - public getCommentsConfiguration():Modes.ICommentsConfiguration { - return { blockCommentStartToken: '/*', blockCommentEndToken: '*/' }; - } - - // TODO@Martin: This definition does not work with umlauts for example - public getWordDefinition():RegExp { - return /(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g; - } - static $findColorDeclarations = OneWorkerAttr(CSSMode, CSSMode.prototype.findColorDeclarations); public findColorDeclarations(resource:URI):WinJS.TPromise<{range:EditorCommon.IRange; value:string; }[]> { return this._worker((w) => w.findColorDeclarations(resource)); diff --git a/src/vs/languages/css/test/common/css.test.ts b/src/vs/languages/css/test/common/css.test.ts index 9d737d39f0b..195bdfb3d4e 100644 --- a/src/vs/languages/css/test/common/css.test.ts +++ b/src/vs/languages/css/test/common/css.test.ts @@ -21,8 +21,8 @@ suite('CSS Colorizing', () => { suiteSetup((done) => { modesUtil.load('css').then(mode => { tokenizationSupport = mode.tokenizationSupport; - assertOnEnter = modesUtil.createOnEnterAsserter(mode.getId(), mode.onEnterSupport); - wordDefinition = mode.tokenTypeClassificationSupport.getWordDefinition(); + assertOnEnter = modesUtil.createOnEnterAsserter(mode.getId(), mode.richEditSupport); + wordDefinition = mode.richEditSupport.tokenTypeClassification.getWordDefinition(); done(); }); }); diff --git a/src/vs/languages/handlebars/common/handlebars.ts b/src/vs/languages/handlebars/common/handlebars.ts index af7e37832ad..b993555e3a5 100644 --- a/src/vs/languages/handlebars/common/handlebars.ts +++ b/src/vs/languages/handlebars/common/handlebars.ts @@ -14,6 +14,8 @@ import htmlWorker = require('vs/languages/html/common/htmlWorker'); import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IThreadService} from 'vs/platform/thread/common/thread'; import {IModeService} from 'vs/editor/common/services/modeService'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; +import {createWordRegExp} from 'vs/editor/common/modes/abstractMode'; export enum States { HTML, @@ -115,29 +117,44 @@ export class HandlebarsMode extends htmlMode.HTMLMode { super(descriptor, instantiationService, threadService, modeService); this.formattingSupport = null; - - this.onEnterSupport = new OnEnterSupport(this.getId(), { - brackets: [ - { open: '' }, - { open: '{{', close: '}}' }, - ] - }); } - public asyncCtor(): winjs.Promise { - return super.asyncCtor().then(() => { - var pairs = this.characterPairSupport.getAutoClosingPairs().slice(0).concat([ - { open: '{', close: '}'} - ]); + protected _createRichEditSupport(embeddedAutoClosingPairs: Modes.IAutoClosingPair[]): Modes.IRichEditSupport { + return new RichEditSupport(this.getId(), { - this.characterPairSupport = new supports.CharacterPairSupport(this, { - autoClosingPairs: pairs.slice(0), + wordPattern: createWordRegExp('#-?%'), + + comments: { + blockComment: [''] + }, + + brackets: [ + [''], + ['{{', '}}'] + ], + + __electricCharacterSupport: { + brackets: [], + regexBrackets: [{ + tokenType: htmlMode.htmlTokenTypes.getTag('$1'), + open: new RegExp(`<(?!(?:${htmlMode.EMPTY_ELEMENTS.join("|")}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), + closeComplete: '', + close: /<\/(\w[\w\d]*)\s*>$/i + }], + caseInsensitive: true, + embeddedElectricCharacters: ['*', '}', ']', ')'] + }, + + __characterPairSupport: { + autoClosingPairs: embeddedAutoClosingPairs.slice(0).concat([ + { open: '{', close: '}'} + ]), surroundingPairs: [ { open: '<', close: '>' }, { open: '"', close: '"' }, { open: '\'', close: '\'' } ] - }); + } }); } diff --git a/src/vs/languages/html/common/html.ts b/src/vs/languages/html/common/html.ts index bcfdf9f816f..052a30f841d 100644 --- a/src/vs/languages/html/common/html.ts +++ b/src/vs/languages/html/common/html.ts @@ -25,8 +25,10 @@ import {IInstantiationService} from 'vs/platform/instantiation/common/instantiat import {IThreadService } from 'vs/platform/thread/common/thread'; import * as htmlTokenTypes from 'vs/languages/html/common/htmlTokenTypes'; import {EMPTY_ELEMENTS} from 'vs/languages/html/common/htmlEmptyTagsShared'; +import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; export { htmlTokenTypes }; // export to be used by Razor. We are the main module, so Razor should get ot from use. +export { EMPTY_ELEMENTS }; // export to be used by Razor. We are the main module, so Razor should get ot from use. export enum States { Content, @@ -273,8 +275,7 @@ export class State extends AbstractState { export class HTMLMode extends AbstractMode implements supports.ITokenizationCustomization { public tokenizationSupport: Modes.ITokenizationSupport; - public electricCharacterSupport: Modes.IElectricCharacterSupport; - public characterPairSupport: Modes.ICharacterPairSupport; + public richEditSupport: Modes.IRichEditSupport; public extraInfoSupport:Modes.IExtraInfoSupport; public occurrencesSupport:Modes.IOccurrencesSupport; @@ -283,7 +284,6 @@ export class HTMLMode extends AbstractMode i public formattingSupport: Modes.IFormattingSupport; public parameterHintsSupport: Modes.IParameterHintsSupport; public suggestSupport: Modes.ISuggestSupport; - public onEnterSupport: Modes.IOnEnterSupport; private modeService:IModeService; @@ -298,17 +298,6 @@ export class HTMLMode extends AbstractMode i this.modeService = modeService; this.tokenizationSupport = new supports.TokenizationSupport(this, this, true, true); - this.electricCharacterSupport = new supports.BracketElectricCharacterSupport(this, - { - brackets: [], - regexBrackets:[ - { tokenType: htmlTokenTypes.getTag('$1'), - open: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join("|")}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - closeComplete: '', - close: /<\/(\w[\w\d]*)\s*>$/i }], - caseInsensitive:true, - embeddedElectricCharacters: ['*', '}', ']', ')'] - }); this.formattingSupport = this; this.extraInfoSupport = this; @@ -331,12 +320,6 @@ export class HTMLMode extends AbstractMode i triggerCharacters: ['.', ':', '<', '"', '=', '/'], excludeTokens: ['comment'], suggest: (resource, position) => this.suggest(resource, position)}); - - this.onEnterSupport = new OnEnterSupport(this.getId(), { - brackets: [ - { open: '' } - ] - }); } public asyncCtor(): winjs.Promise { @@ -345,13 +328,42 @@ export class HTMLMode extends AbstractMode i this.modeService.getOrCreateMode('text/css') ]).then((embeddableModes) => { var autoClosingPairs = this._getAutoClosingPairs(embeddableModes); + this.richEditSupport = this._createRichEditSupport(autoClosingPairs); + }); + } + + protected _createRichEditSupport(embeddedAutoClosingPairs: Modes.IAutoClosingPair[]): Modes.IRichEditSupport { + return new RichEditSupport(this.getId(), { - this.characterPairSupport = new supports.CharacterPairSupport(this, { - autoClosingPairs: autoClosingPairs.slice(0), + wordPattern: createWordRegExp('#-?%'), + + comments: { + blockComment: [''] + }, + + brackets: [ + [''] + ], + + __electricCharacterSupport: { + brackets: [], + regexBrackets: [{ + tokenType: htmlTokenTypes.getTag('$1'), + open: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join("|")}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), + closeComplete: '', + close: /<\/(\w[\w\d]*)\s*>$/i + }], + caseInsensitive: true, + embeddedElectricCharacters: ['*', '}', ']', ')'] + }, + + __characterPairSupport: { + autoClosingPairs: embeddedAutoClosingPairs.slice(0), surroundingPairs: [ - { open: '"', close: '"' }, - { open: '\'', close: '\'' } - ]}); + { open: '"', close: '"' }, + { open: '\'', close: '\'' } + ] + } }); } @@ -377,12 +389,13 @@ export class HTMLMode extends AbstractMode i } private _collectAutoClosingPairs(result:{[key:string]:string;}, mode:Modes.IMode): void { - if (mode && mode.characterPairSupport) { - var acp = mode.characterPairSupport.getAutoClosingPairs(); - if (acp !== null) { - for(var i = 0; i < acp.length; i++) { - result[acp[i].open] = acp[i].close; - } + if (!mode || !mode.richEditSupport || !mode.richEditSupport.characterPair) { + return; + } + var acp = mode.richEditSupport.characterPair.getAutoClosingPairs(); + if (acp !== null) { + for(var i = 0; i < acp.length; i++) { + result[acp[i].open] = acp[i].close; } } } @@ -443,15 +456,6 @@ export class HTMLMode extends AbstractMode i return null; } - public static WORD_DEFINITION = createWordRegExp('#-?%'); - public getWordDefinition():RegExp { - return HTMLMode.WORD_DEFINITION; - } - - public getCommentsConfiguration():Modes.ICommentsConfiguration { - return { blockCommentStartToken: '' }; - } - protected _getWorkerDescriptor(): AsyncDescriptor2 { return createAsyncDescriptor2('vs/languages/html/common/htmlWorker', 'HTMLWorker'); } diff --git a/src/vs/languages/html/test/common/html.test.ts b/src/vs/languages/html/test/common/html.test.ts index ed1d87225ff..bc94d892c35 100644 --- a/src/vs/languages/html/test/common/html.test.ts +++ b/src/vs/languages/html/test/common/html.test.ts @@ -609,7 +609,7 @@ suite('Colorizing - HTML', () => { test('onEnter', function() { var model = new Model('