diff --git a/gulpfile.js b/gulpfile.js index ac4be384..1b8564dc 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -56,6 +56,7 @@ gulp.task('release', ['clean-release','compile'], function() { bundleOne('src/jade'), bundleOne('src/java'), bundleOne('src/lua'), + bundleOne('src/markdown'), bundleOne('src/objective-c'), bundleOne('src/powershell'), bundleOne('src/python'), diff --git a/src/markdown.ts b/src/markdown.ts new file mode 100644 index 00000000..9e4199c9 --- /dev/null +++ b/src/markdown.ts @@ -0,0 +1,215 @@ +/*--------------------------------------------------------------------------------------------- + * 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 IRichLanguageConfiguration = monaco.languages.LanguageConfiguration; +import ILanguage = monaco.languages.IMonarchLanguage; + +const TOKEN_HEADER_LEAD = 'entity.name.tag'; +const TOKEN_HEADER = 'entity.name.tag'; +const TOKEN_EXT_HEADER = 'entity.other.attribute-name'; +const TOKEN_SEPARATOR = 'meta.separator'; +const TOKEN_QUOTE = 'comment'; +const TOKEN_LIST = 'keyword'; +const TOKEN_BLOCK = 'string'; +const TOKEN_BLOCK_CODE = 'variable.source'; + +const DELIM_END = 'punctuation.definition.meta.tag.end.html'; +const DELIM_START = 'punctuation.definition.meta.tag.begin.html'; +const DELIM_ASSIGN = 'meta.tag.assign.html'; +const ATTRIB_NAME = 'entity.other.attribute-name.html'; +const ATTRIB_VALUE = 'string.html'; +const COMMENT = 'comment.html.content'; +const DELIM_COMMENT = 'comment.html'; +const DOCTYPE = 'entity.other.attribute-name.html'; +const DELIM_DOCTYPE = 'entity.name.tag.html'; + +const TAG_PREFIX = 'entity.name.tag.tag-'; + +function getTag(name: string) { + return TAG_PREFIX + name; +} + +export var conf: IRichLanguageConfiguration = { + comments: { + blockComment: ['',] + }, + brackets: [['{', '}'], ['[', ']'], ['(', ')'], ['<', '>']], + autoClosingPairs: [] +}; + +export var 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', TOKEN_HEADER_LEAD, TOKEN_HEADER, TOKEN_HEADER]], + + // headers (with =) + [/^\s*(=+|\-+)\s*$/, TOKEN_EXT_HEADER], + + // headers (with ***) + [/^\s*((\*[ ]?)+)\s*$/, TOKEN_SEPARATOR], + + // quote + [/^\s*>+/, TOKEN_QUOTE], + + // list (starting with * or number) + [/^\s*([\*\-+:]|\d+\.)\s/, TOKEN_LIST], + + // code block (4 spaces indent) + [/^(\t|[ ]{4})[^ ].*$/, TOKEN_BLOCK], + + // code block (3 tilde) + [/^\s*~{3}\s*((?:\w|[\/\-#])+)?\s*$/, { token: TOKEN_BLOCK, next: '@codeblock' }], + + // github style code blocks (with backticks and language) + [/^\s*```\s*((?:\w|[\/\-#])+)\s*$/, { token: TOKEN_BLOCK, next: '@codeblockgh', nextEmbedded: '$1' }], + + // github style code blocks (with backticks but no language) + [/^\s*`{3}\s*$/, { token: TOKEN_BLOCK, next: '@codeblock' }], + + // markup within lines + { include: '@linecontent' }, + ], + + codeblock: [ + [/^\s*~{3}\s*$/, { token: TOKEN_BLOCK, next: '@pop' }], + [/^\s*`{3}\s*$/, { token: TOKEN_BLOCK, next: '@pop' }], + [/.*$/, TOKEN_BLOCK_CODE], + ], + + // github style code blocks + codeblockgh: [ + [/```\s*$/, { token: '@rematch', switchTo: '@codeblockghend', nextEmbedded: '@pop' }], + [/[^`]*$/, TOKEN_BLOCK_CODE], + ], + + codeblockghend: [ + [/\s*```/, { token: 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+)\/>/, getTag('$1')], + [/<(\w+)/, { + cases: { + '@empty': { token: getTag('$1'), next: '@tag.$1' }, + '@default': { token: getTag('$1'), bracket: '@open', next: '@tag.$1' } + } + }], + [/<\/(\w+)\s*>/, { token: getTag('$1'), bracket: '@close' }], + + [//, 'comment', '@pop'], + [/