/*--------------------------------------------------------------------------------------------- * 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 WinJS = require('vs/base/common/winjs.base'); import URI from 'vs/base/common/uri'; import Types = require('vs/editor/common/modes/monarch/monarchTypes'); import Compile = require('vs/editor/common/modes/monarch/monarchCompile'); import Modes = require('vs/editor/common/modes'); import MarkdownWorker = require('vs/languages/markdown/common/markdownWorker'); import {OneWorkerAttr, AllWorkersAttr} from 'vs/platform/thread/common/threadService'; import {htmlTokenTypes} from 'vs/languages/html/common/html'; import markdownTokenTypes = require('vs/languages/markdown/common/markdownTokenTypes'); import {IModeService} from 'vs/editor/common/services/modeService'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IThreadService} from 'vs/platform/thread/common/thread'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService'; import {AbstractMode, ModeWorkerManager} from 'vs/editor/common/modes/abstractMode'; import {createTokenizationSupport} from 'vs/editor/common/modes/monarch/monarchLexer'; import {LanguageConfigurationRegistry, IRichLanguageConfiguration} from 'vs/editor/common/modes/languageConfigurationRegistry'; import {wireCancellationToken} from 'vs/base/common/async'; export const language = { defaultToken: '', tokenPostfix: '.md', // escape codes control: /[\\`*_\[\]{}()#+\-\.!]/, noncontrol: /[^\\`*_\[\]{}()#+\-\.!]/, escapes: /\\(?:@control)/, // escape codes for javascript/CSS strings jsescapes: /\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/, // non matched elements empty: [ 'area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param' ], tokenizer: { root: [ // headers (with #) [/^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/, ['white', markdownTokenTypes.TOKEN_HEADER_LEAD, markdownTokenTypes.TOKEN_HEADER, markdownTokenTypes.TOKEN_HEADER]], // headers (with =) [/^\s*(=+|\-+)\s*$/, markdownTokenTypes.TOKEN_EXT_HEADER], // headers (with ***) [/^\s*((\*[ ]?)+)\s*$/, markdownTokenTypes.TOKEN_SEPARATOR], // quote [/^\s*>+/, markdownTokenTypes.TOKEN_QUOTE], // list (starting with * or number) [/^\s*([\*\-+:]|\d+\.)\s/, markdownTokenTypes.TOKEN_LIST], // code block (4 spaces indent) [/^(\t|[ ]{4})[^ ].*$/, markdownTokenTypes.TOKEN_BLOCK], // code block (3 tilde) [/^\s*~{3}\s*((?:\w|[\/\-#])+)?\s*$/, { token: markdownTokenTypes.TOKEN_BLOCK, next: '@codeblock' }], // github style code blocks (with backticks and language) [/^\s*```\s*((?:\w|[\/\-#])+)\s*$/, { token: markdownTokenTypes.TOKEN_BLOCK, next: '@codeblockgh', nextEmbedded: '$1' }], // github style code blocks (with backticks but no language) [/^\s*`{3}\s*$/, { token: markdownTokenTypes.TOKEN_BLOCK, next: '@codeblock' }], // markup within lines { include: '@linecontent' }, ], codeblock: [ [/^\s*~{3}\s*$/, { token: markdownTokenTypes.TOKEN_BLOCK, next: '@pop' }], [/^\s*`{3}\s*$/, { token: markdownTokenTypes.TOKEN_BLOCK, next: '@pop' }], [/.*$/, markdownTokenTypes.TOKEN_BLOCK_CODE], ], // github style code blocks codeblockgh: [ [/```\s*$/, { token: '@rematch', switchTo: '@codeblockghend', nextEmbedded: '@pop' }], [/[^`]*$/, markdownTokenTypes.TOKEN_BLOCK_CODE], ], codeblockghend: [ [/\s*```/, { token: markdownTokenTypes.TOKEN_BLOCK_CODE, next: '@pop' }], [/./, '@rematch', '@pop'], ], linecontent: [ // escapes [/&\w+;/, 'string.escape'], [/@escapes/, 'escape'], // various markup [/\b__([^\\_]|@escapes|_(?!_))+__\b/, 'strong'], [/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/, 'strong'], [/\b_[^_]+_\b/, 'emphasis'], [/\*([^\\*]|@escapes)+\*/, 'emphasis'], [/`([^\\`]|@escapes)+`/, 'variable'], // links [/\{[^}]+\}/, 'string.target'], [/(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/, ['string.link', '', 'string.link']], [/(!?\[)((?:[^\]\\]|@escapes)*)(\])/, 'string.link'], // or html { include: 'html' }, ], // Note: it is tempting to rather switch to the real HTML mode instead of building our own here // but currently there is a limitation in Monarch that prevents us from doing it: The opening // '<' would start the HTML mode, however there is no way to jump 1 character back to let the // HTML mode also tokenize the opening angle bracket. Thus, even though we could jump to HTML, // we cannot correctly tokenize it in that mode yet. html: [ // html tags [/<(\w+)\/>/, htmlTokenTypes.getTag('$1')], [/<(\w+)/, { cases: { '@empty': { token: htmlTokenTypes.getTag('$1'), next: '@tag.$1' }, '@default': { token: htmlTokenTypes.getTag('$1'), bracket: '@open', next: '@tag.$1' } } }], [/<\/(\w+)\s*>/, { token: htmlTokenTypes.getTag('$1'), bracket: '@close' }], [//, 'comment', '@pop'], [/',] }, brackets: [['{','}'], ['[',']'], ['(',')'], ['<','>']], autoClosingPairs: [] }; public emitOutputSupport: Modes.IEmitOutputSupport; public configSupport:Modes.IConfigurationSupport; public tokenizationSupport: Modes.ITokenizationSupport; private _modeWorkerManager: ModeWorkerManager; private _threadService:IThreadService; constructor( descriptor: Modes.IModeDescriptor, @IInstantiationService instantiationService: IInstantiationService, @IThreadService threadService: IThreadService, @IModeService modeService: IModeService, @IEditorWorkerService editorWorkerService: IEditorWorkerService, @IConfigurationService configurationService: IConfigurationService ) { super(descriptor.id); let lexer = Compile.compile(descriptor.id, language); this._modeWorkerManager = new ModeWorkerManager(descriptor, 'vs/languages/markdown/common/markdownWorker', 'MarkdownWorker', null, instantiationService); this._threadService = threadService; this.emitOutputSupport = this; this.configSupport = this; this.tokenizationSupport = createTokenizationSupport(modeService, this, lexer); LanguageConfigurationRegistry.register(this.getId(), MarkdownMode.LANG_CONFIG); Modes.SuggestRegistry.register(this.getId(), { triggerCharacters: [], shouldAutotriggerSuggest: false, provideCompletionItems: (model, position, token) => { return wireCancellationToken(token, editorWorkerService.textualSuggest(model.uri, position)); } }, true); } private _worker(runner:(worker:MarkdownWorker.MarkdownWorker)=>WinJS.TPromise): WinJS.TPromise { return this._modeWorkerManager.worker(runner); } public configure(options:any): WinJS.TPromise { if (this._threadService.isInMainThread) { return this._configureWorkers(options); } else { return this._worker((w) => w._doConfigure(options)); } } static $_configureWorkers = AllWorkersAttr(MarkdownMode, MarkdownMode.prototype._configureWorkers); private _configureWorkers(options:any): WinJS.TPromise { return this._worker((w) => w._doConfigure(options)); } static $getEmitOutput = OneWorkerAttr(MarkdownMode, MarkdownMode.prototype.getEmitOutput); public getEmitOutput(resource: URI, absoluteWorkerResourcesPath?: string): WinJS.TPromise { // TODO@Ben technical debt: worker cannot resolve paths absolute return this._worker((w) => w.getEmitOutput(resource, absoluteWorkerResourcesPath)); } }