提交 cf6b4b56 编写于 作者: A Alex Dima

Remove the rest of vs/languages

上级 d7397bca
......@@ -47,9 +47,6 @@ const indentationFilter = [
'!**/package.json',
'!**/npm-shrinkwrap.json',
'!**/octicons/**',
'!**/vs/languages/sass/test/common/example.scss',
'!**/vs/languages/less/common/parser/less.grammar.txt',
'!**/vs/languages/css/common/buildscripts/css-schema.xml',
'!**/vs/base/common/marked/raw.marked.js',
'!**/vs/base/common/winjs.base.raw.js',
'!**/vs/base/node/terminateProcess.sh',
......@@ -89,7 +86,6 @@ const tslintFilter = [
'!src/vs/base/**/*.test.ts',
'!extensions/typescript/test/colorize-fixtures/**',
'!extensions/vscode-api-tests/testWorkspace/**',
'!src/vs/languages/**/*.test.ts',
'!src/vs/workbench/**/*.test.ts',
'!extensions/**/*.test.ts'
];
......
......@@ -5,7 +5,7 @@
exports.base = require('./vs/base/buildfile').collectModules();
exports.editor = require('./vs/editor/buildfile').collectModules();
exports.languages = require('./vs/languages/buildfile').collectModules();
exports.languages = [];
exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.main']);
exports.code = require('./vs/code/buildfile').collectModules();
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/languages/html/common/html.contribution';
import { TestInstantiationService } from 'vs/test/utils/instantiationTestUtils';
import * as assert from 'assert';
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
suite('Editor Modes - Modes Registry', () => {
let instantiationService: TestInstantiationService;
setup(() => {
instantiationService= new TestInstantiationService();
instantiationService.stub(IExtensionService);
});
test('Bug 12104: [f12] createModel not successfully handling mime type list?', () => {
let modeService = instantiationService.createInstance(ModeServiceImpl);
assert.equal(modeService.getModeId('text/html,text/plain'), 'html');
});
});
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
var EntryPoint = (function() {
function toArray(param) {
if (!param) {
return [];
}
if (!Array.isArray(param)) {
return [param];
}
return param;
}
function EntryPoint(result, modules) {
this.result = result;
this.modules = toArray(modules);
}
EntryPoint.prototype.define = function(moduleId, excludes) {
excludes = toArray(excludes);
this.result.push({
name: moduleId,
exclude: ['vs/css', 'vs/nls'].concat(this.modules).concat(excludes)
});
return new EntryPoint(this.result, this.modules.concat([moduleId].concat(excludes)));
};
EntryPoint.prototype.combine = function(other) {
return new EntryPoint(this.result, this.modules.concat(other.modules));
};
return EntryPoint;
})();
exports.collectModules = function(args) {
var result = [];
// var common = new EntryPoint(result, 'vs/editor/common/languages.common');
// var worker = new EntryPoint(result, ['vs/editor/common/languages.common', 'vs/base/common/worker/workerServer', 'vs/editor/common/worker/editorWorkerServer']);
// ---- beautify-html (shared btw html and xml) -----------------------------
// worker.define('vs/languages/lib/common/beautify-html');
// // ---- html ----------------------------------
// common.define('vs/languages/html/common/html')
// .combine(worker)
// .define('vs/languages/html/common/htmlWorker', ['vs/languages/lib/common/beautify-html']);
return result;
};
\ No newline at end of file
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "HTML 5.1 W3C Working Draft",
"version": "08 October 2015",
"license": "W3C Document License",
"repositoryURL": "http://www.w3.org/TR/2015/WD-html51-20151008/",
"licenseDetail": [
"Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang). This software or document includes material copied ",
"from or derived from HTML 5.1 W3C Working Draft (http://www.w3.org/TR/2015/WD-html51-20151008/.)",
"",
"THIS DOCUMENT IS PROVIDED \"AS IS,\" AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT ",
"NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF ",
"THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY ",
"PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.",
"",
"COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE ",
"DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS THEREOF.",
"",
"The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to this document or its contents",
"without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders."
]
},
{
"name": "Ionic documentation",
"version": "1.2.4",
"license": "Apache2",
"repositoryURL": "https://github.com/driftyco/ionic-site",
"licenseDetail": [
"Copyright Drifty Co. http://drifty.com/.",
"",
"Apache License",
"",
"Version 2.0, January 2004",
"",
"http://www.apache.org/licenses/",
"",
"TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION",
"",
"1. Definitions.",
"",
"\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.",
"",
"\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.",
"",
"\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.",
"",
"\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.",
"",
"\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.",
"",
"\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.",
"",
"\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).",
"",
"\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.",
"",
"\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"",
"",
"\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.",
"",
"2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.",
"",
"3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.",
"",
"4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:",
"",
"You must give any other recipients of the Work or Derivative Works a copy of this License; and",
"",
"You must cause any modified files to carry prominent notices stating that You changed the files; and",
"",
"You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and",
"",
"If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.",
"",
"5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.",
"",
"6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.",
"",
"7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.",
"",
"8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.",
"",
"9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.",
"",
"END OF TERMS AND CONDITIONS"
]
}
]
/*---------------------------------------------------------------------------------------------
* 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 {ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
import nls = require('vs/nls');
import platform = require('vs/platform/platform');
import ConfigurationRegistry = require('vs/platform/configuration/common/configurationRegistry');
ModesRegistry.registerCompatMode({
id: 'html',
extensions: ['.html', '.htm', '.shtml', '.xhtml', '.mdoc', '.jsp', '.asp', '.aspx', '.jshtm'],
aliases: ['HTML', 'htm', 'html', 'xhtml'],
mimetypes: ['text/html', 'text/x-jshtm', 'text/template', 'text/ng-template'],
moduleId: 'vs/languages/html/common/html',
ctorName: 'HTMLMode',
deps: ['text/css', 'text/javascript']
});
var configurationRegistry = <ConfigurationRegistry.IConfigurationRegistry>platform.Registry.as(ConfigurationRegistry.Extensions.Configuration);
export interface IHTMLFormatConfiguration {
wrapLineLength: number;
unformatted: string;
indentInnerHtml: boolean;
preserveNewLines: boolean;
maxPreserveNewLines: number;
indentHandlebars: boolean;
endWithNewline: boolean;
extraLiners: string;
}
export interface IHTMLConfiguration {
format: IHTMLFormatConfiguration;
suggest: {[providerId:string]:boolean};
}
configurationRegistry.registerConfiguration({
'id': 'html',
'order': 20,
'type': 'object',
'title': nls.localize('htmlConfigurationTitle', "HTML"),
'properties': {
'html.format.wrapLineLength': {
'type': 'integer',
'default': 120,
'description': nls.localize('format.wrapLineLength', "Maximum amount of characters per line (0 = disable)."),
},
'html.format.unformatted': {
'type': ['string', 'null'],
'default': 'a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, q, samp, script, select, small, span, strong, sub, sup, textarea, tt, var',
'description': nls.localize('format.unformatted', "List of tags, comma separated, that shouldn't be reformatted. 'null' defaults to all tags listed at https://www.w3.org/TR/html5/dom.html#phrasing-content."),
},
'html.format.indentInnerHtml': {
'type': 'boolean',
'default': false,
'description': nls.localize('format.indentInnerHtml', "Indent <head> and <body> sections."),
},
'html.format.preserveNewLines': {
'type': 'boolean',
'default': true,
'description': nls.localize('format.preserveNewLines', "Whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text."),
},
'html.format.maxPreserveNewLines': {
'type': ['number', 'null'],
'default': null,
'description': nls.localize('format.maxPreserveNewLines', "Maximum number of line breaks to be preserved in one chunk. Use 'null' for unlimited."),
},
'html.format.indentHandlebars': {
'type': 'boolean',
'default': false,
'description': nls.localize('format.indentHandlebars', "Format and indent {{#foo}} and {{/foo}}."),
},
'html.format.endWithNewline': {
'type': 'boolean',
'default': false,
'description': nls.localize('format.endWithNewline', "End with a newline."),
},
'html.format.extraLiners': {
'type': ['string', 'null'],
'default': 'head, body, /html',
'description': nls.localize('format.extraLiners', "List of tags, comma separated, that should have an extra newline before them. 'null' defaults to \"head, body, /html\"."),
},
'html.suggest.angular1': {
'type': ['boolean'],
'default': true,
'description': nls.localize('suggest.angular1', "Configures if the built-in HTML language support suggests Angular V1 tags and properties."),
},
'html.suggest.ionic': {
'type': ['boolean'],
'default': true,
'description': nls.localize('suggest.ionic', "Configures if the built-in HTML language support suggests Ionic tags, properties and values."),
},
'html.suggest.html5': {
'type': ['boolean'],
'default': true,
'description': nls.localize('suggest.html5', "Configures if the built-in HTML language support suggests HTML5 tags, properties and values."),
},
}
});
/*---------------------------------------------------------------------------------------------
* 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 URI from 'vs/base/common/uri';
import winjs = require('vs/base/common/winjs.base');
import editorCommon = require('vs/editor/common/editorCommon');
import modes = require('vs/editor/common/modes');
import htmlWorker = require('vs/languages/html/common/htmlWorker');
import { CompatMode, createWordRegExp, ModeWorkerManager } from 'vs/editor/common/modes/abstractMode';
import { AbstractState } from 'vs/editor/common/modes/abstractState';
import {IModeService} from 'vs/editor/common/services/modeService';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import * as htmlTokenTypes from 'vs/languages/html/common/htmlTokenTypes';
import {EMPTY_ELEMENTS} from 'vs/languages/html/common/htmlEmptyTagsShared';
import {LanguageConfigurationRegistry, LanguageConfiguration} from 'vs/editor/common/modes/languageConfigurationRegistry';
import {TokenizationSupport, IModeLocator, ILeavingNestedModeData, ITokenizationCustomization} from 'vs/editor/common/modes/supports/tokenizationSupport';
import {wireCancellationToken} from 'vs/base/common/async';
import {ICompatWorkerService, CompatWorkerAttr} from 'vs/editor/common/services/compatWorkerService';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {IHTMLConfiguration} from 'vs/languages/html/common/html.contribution';
import {CharCode} from 'vs/base/common/charCode';
export { htmlTokenTypes }; // export to be used by Razor. We are the main module, so Razor should get it from us.
export { EMPTY_ELEMENTS }; // export to be used by Razor. We are the main module, so Razor should get it from us.
export enum States {
Content,
OpeningStartTag,
OpeningEndTag,
WithinDoctype,
WithinTag,
WithinComment,
WithinEmbeddedContent,
AttributeName,
AttributeValue
}
// list of elements that embed other content
var tagsEmbeddingContent:string[] = ['script', 'style'];
export class State extends AbstractState {
public kind:States;
public lastTagName:string;
public lastAttributeName:string;
public embeddedContentType:string;
public attributeValueQuote:string;
public attributeValueLength:number;
constructor(modeId:string, kind:States, lastTagName:string, lastAttributeName:string, embeddedContentType:string, attributeValueQuote:string, attributeValueLength:number) {
super(modeId);
this.kind = kind;
this.lastTagName = lastTagName;
this.lastAttributeName = lastAttributeName;
this.embeddedContentType = embeddedContentType;
this.attributeValueQuote = attributeValueQuote;
this.attributeValueLength = attributeValueLength;
}
static escapeTagName(s:string):string {
return htmlTokenTypes.getTag(s.replace(/[:_.]/g, '-'));
}
public makeClone():State {
return new State(this.getModeId(), this.kind, this.lastTagName, this.lastAttributeName, this.embeddedContentType, this.attributeValueQuote, this.attributeValueLength);
}
public equals(other:modes.IState):boolean {
if (other instanceof State) {
return (
super.equals(other) &&
this.kind === other.kind &&
this.lastTagName === other.lastTagName &&
this.lastAttributeName === other.lastAttributeName &&
this.embeddedContentType === other.embeddedContentType &&
this.attributeValueQuote === other.attributeValueQuote &&
this.attributeValueLength === other.attributeValueLength
);
}
return false;
}
private nextElementName(stream:modes.IStream):string {
return stream.advanceIfRegExp(/^[_:\w][_:\w-.\d]*/).toLowerCase();
}
private nextAttributeName(stream:modes.IStream):string {
return stream.advanceIfRegExp(/^[^\s"'>/=\x00-\x0F\x7F\x80-\x9F]*/).toLowerCase();
}
public tokenize(stream:modes.IStream) : modes.ITokenizationResult {
switch(this.kind){
case States.WithinComment:
if (stream.advanceUntilString2('-->', false)) {
return { type: htmlTokenTypes.COMMENT};
} else if(stream.advanceIfString2('-->')) {
this.kind = States.Content;
return { type: htmlTokenTypes.DELIM_COMMENT, dontMergeWithPrev: true };
}
break;
case States.WithinDoctype:
if (stream.advanceUntilString2('>', false)) {
return { type: htmlTokenTypes.DOCTYPE};
} else if(stream.advanceIfString2('>')) {
this.kind = States.Content;
return { type: htmlTokenTypes.DELIM_DOCTYPE, dontMergeWithPrev: true };
}
break;
case States.Content:
if (stream.advanceIfCharCode2(CharCode.LessThan)) {
if (!stream.eos() && stream.peek() === '!') {
if (stream.advanceIfString2('!--')) {
this.kind = States.WithinComment;
return { type: htmlTokenTypes.DELIM_COMMENT, dontMergeWithPrev: true };
}
if (stream.advanceIfStringCaseInsensitive2('!DOCTYPE')) {
this.kind = States.WithinDoctype;
return { type: htmlTokenTypes.DELIM_DOCTYPE, dontMergeWithPrev: true };
}
}
if (stream.advanceIfCharCode2(CharCode.Slash)) {
this.kind = States.OpeningEndTag;
return { type: htmlTokenTypes.DELIM_END, dontMergeWithPrev: true };
}
this.kind = States.OpeningStartTag;
return { type: htmlTokenTypes.DELIM_START, dontMergeWithPrev: true };
}
break;
case States.OpeningEndTag:
var tagName = this.nextElementName(stream);
if (tagName.length > 0){
return {
type: State.escapeTagName(tagName),
};
} else if (stream.advanceIfString2('>')) {
this.kind = States.Content;
return { type: htmlTokenTypes.DELIM_END, dontMergeWithPrev: true };
} else {
stream.advanceUntilString2('>', false);
return { type: '' };
}
case States.OpeningStartTag:
this.lastTagName = this.nextElementName(stream);
if (this.lastTagName.length > 0) {
this.lastAttributeName = null;
if ('script' === this.lastTagName || 'style' === this.lastTagName) {
this.lastAttributeName = null;
this.embeddedContentType = null;
}
this.kind = States.WithinTag;
return {
type: State.escapeTagName(this.lastTagName),
};
}
break;
case States.WithinTag:
if (stream.skipWhitespace2() || stream.eos()) {
this.lastAttributeName = ''; // remember that we have seen a whitespace
return { type: '' };
} else {
if (this.lastAttributeName === '') {
var name = this.nextAttributeName(stream);
if (name.length > 0) {
this.lastAttributeName = name;
this.kind = States.AttributeName;
return { type: htmlTokenTypes.ATTRIB_NAME };
}
}
if (stream.advanceIfString2('/>')) {
this.kind = States.Content;
return { type: htmlTokenTypes.DELIM_START, dontMergeWithPrev: true };
}
if (stream.advanceIfCharCode2(CharCode.GreaterThan)) {
if (tagsEmbeddingContent.indexOf(this.lastTagName) !== -1) {
this.kind = States.WithinEmbeddedContent;
return { type: htmlTokenTypes.DELIM_START, dontMergeWithPrev: true };
} else {
this.kind = States.Content;
return { type: htmlTokenTypes.DELIM_START, dontMergeWithPrev: true };
}
} else {
stream.next2();
return { type: '' };
}
}
case States.AttributeName:
if (stream.skipWhitespace2() || stream.eos()){
return { type: '' };
}
if (stream.advanceIfCharCode2(CharCode.Equals)) {
this.kind = States.AttributeValue;
return { type: htmlTokenTypes.DELIM_ASSIGN };
} else {
this.kind = States.WithinTag;
this.lastAttributeName = '';
return this.tokenize(stream); // no advance yet - jump to WithinTag
}
case States.AttributeValue:
if (stream.eos()) {
return { type: '' };
}
if(stream.skipWhitespace2()) {
if (this.attributeValueQuote === '"' || this.attributeValueQuote === '\'') {
// We are inside the quotes of an attribute value
return { type: htmlTokenTypes.ATTRIB_VALUE };
}
return { type: '' };
}
// We are in a attribute value
if (this.attributeValueQuote === '"' || this.attributeValueQuote === '\'') {
if (this.attributeValueLength === 1 && ('script' === this.lastTagName || 'style' === this.lastTagName) && 'type' === this.lastAttributeName) {
let attributeValue = stream.advanceUntilString(this.attributeValueQuote, true);
if (attributeValue.length > 0) {
this.embeddedContentType = this.unquote(attributeValue);
this.kind = States.WithinTag;
this.attributeValueLength = 0;
this.attributeValueQuote = '';
return { type: htmlTokenTypes.ATTRIB_VALUE };
}
} else {
if (stream.advanceIfCharCode2(this.attributeValueQuote.charCodeAt(0))) {
this.kind = States.WithinTag;
this.attributeValueLength = 0;
this.attributeValueQuote = '';
this.lastAttributeName = null;
} else {
stream.next();
this.attributeValueLength++;
}
return { type: htmlTokenTypes.ATTRIB_VALUE };
}
} else {
let attributeValue = stream.advanceIfRegExp(/^[^\s"'`=<>]+/);
if (attributeValue.length > 0) {
this.kind = States.WithinTag;
this.lastAttributeName = null;
return { type: htmlTokenTypes.ATTRIB_VALUE };
}
var ch = stream.peek();
if (ch === '\'' || ch === '"') {
this.attributeValueQuote = ch;
this.attributeValueLength = 1;
stream.next2();
return { type: htmlTokenTypes.ATTRIB_VALUE };
} else {
this.kind = States.WithinTag;
this.lastAttributeName = null;
return this.tokenize(stream); // no advance yet - jump to WithinTag
}
}
}
stream.next2();
this.kind = States.Content;
return { type: '' };
}
private unquote(value:string):string {
var start = 0;
var end = value.length;
if ('"' === value[0]) {
start++;
}
if ('"' === value[end - 1]) {
end--;
}
return value.substring(start, end);
}
}
export class HTMLMode<W extends htmlWorker.HTMLWorker> extends CompatMode implements ITokenizationCustomization {
public static LANG_CONFIG:LanguageConfiguration = {
wordPattern: createWordRegExp('#-?%'),
comments: {
blockComment: ['<!--', '-->']
},
brackets: [
['<!--', '-->'],
['<', '>'],
],
__electricCharacterSupport: {
embeddedElectricCharacters: ['*', '}', ']', ')']
},
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: '\'', close: '\'' }
],
surroundingPairs: [
{ open: '"', close: '"' },
{ open: '\'', close: '\'' }
],
onEnterRules: [
{
beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>$/i,
action: { indentAction: modes.IndentAction.IndentOutdent }
},
{
beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
action: { indentAction: modes.IndentAction.Indent }
}
],
};
protected _modeService:IModeService;
private _modeWorkerManager: ModeWorkerManager<W>;
constructor(
descriptor:modes.IModeDescriptor,
@IInstantiationService instantiationService: IInstantiationService,
@IModeService modeService: IModeService,
@ICompatWorkerService compatWorkerService: ICompatWorkerService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IConfigurationService private configurationService: IConfigurationService
) {
super(descriptor.id, compatWorkerService);
this._modeWorkerManager = this._createModeWorkerManager(descriptor, instantiationService);
this._modeService = modeService;
if (this.compatWorkerService && this.compatWorkerService.isInMainThread) {
let updateConfiguration = () => {
let opts = configurationService.getConfiguration<IHTMLConfiguration>('html');
this._configureWorker(opts);
};
configurationService.onDidUpdateConfiguration((e) => updateConfiguration());
updateConfiguration();
}
this._registerSupports();
}
protected _registerSupports(): void {
if (this.getId() !== 'html') {
throw new Error('This method must be overwritten!');
}
modes.SuggestRegistry.register(this.getId(), {
triggerCharacters: ['.', ':', '<', '"', '=', '/'],
provideCompletionItems: (model, position, token): Thenable<modes.ISuggestResult> => {
return wireCancellationToken(token, this._provideCompletionItems(model.uri, position));
}
}, true);
modes.DocumentHighlightProviderRegistry.register(this.getId(), {
provideDocumentHighlights: (model, position, token): Thenable<modes.DocumentHighlight[]> => {
return wireCancellationToken(token, this._provideDocumentHighlights(model.uri, position));
}
}, true);
modes.DocumentRangeFormattingEditProviderRegistry.register(this.getId(), {
provideDocumentRangeFormattingEdits: (model, range, options, token): Thenable<editorCommon.ISingleEditOperation[]> => {
return wireCancellationToken(token, this._provideDocumentRangeFormattingEdits(model.uri, range, options));
}
}, true);
modes.LinkProviderRegistry.register(this.getId(), {
provideLinks: (model, token): Thenable<modes.ILink[]> => {
return wireCancellationToken(token, this.provideLinks(model.uri));
}
}, true);
LanguageConfigurationRegistry.register(this.getId(), HTMLMode.LANG_CONFIG);
modes.TokenizationRegistry.register(this.getId(), new TokenizationSupport(this._modeService, this.getId(), this, true));
}
protected _createModeWorkerManager(descriptor:modes.IModeDescriptor, instantiationService: IInstantiationService): ModeWorkerManager<W> {
return new ModeWorkerManager<W>(descriptor, 'vs/languages/html/common/htmlWorker', 'HTMLWorker', null, instantiationService);
}
private _worker<T>(runner:(worker:W)=>winjs.TPromise<T>): winjs.TPromise<T> {
return this._modeWorkerManager.worker(runner);
}
// TokenizationSupport
public getInitialState():modes.IState {
return new State(this.getId(), States.Content, '', '', '', '', 0);
}
public enterNestedMode(state:modes.IState):boolean {
return state instanceof State && (<State>state).kind === States.WithinEmbeddedContent;
}
public getNestedMode(state:modes.IState, locator:IModeLocator): modes.IMode {
let htmlState:State = <State>state;
if (htmlState.embeddedContentType !== null) {
return locator.getMode(htmlState.embeddedContentType);
}
if ('script' === htmlState.lastTagName) {
return locator.getMode('text/javascript');
}
if ('style' === htmlState.lastTagName) {
return locator.getMode('text/css');
}
return null;
}
public getLeavingNestedModeData(line:string, state:modes.IState):ILeavingNestedModeData {
var tagName = (<State>state).lastTagName;
var regexp = new RegExp('<\\/' + tagName + '\\s*>', 'i');
var match:any = regexp.exec(line);
if (match !== null) {
return {
nestedModeBuffer: line.substring(0, match.index),
bufferAfterNestedMode: line.substring(match.index),
stateAfterNestedMode: new State(this.getId(), States.Content, '', '', '', '', 0)
};
}
return null;
}
static $_configureWorker = CompatWorkerAttr(HTMLMode, HTMLMode.prototype._configureWorker);
private _configureWorker(options:any): winjs.TPromise<void> {
return this._worker((w) => w._doConfigure(options));
}
protected provideLinks(resource:URI):winjs.TPromise<modes.ILink[]> {
let workspace = this.workspaceContextService.getWorkspace();
let workspaceResource = workspace ? workspace.resource : null;
return this._provideLinks(resource, workspaceResource);
}
static $_provideLinks = CompatWorkerAttr(HTMLMode, HTMLMode.prototype._provideLinks);
private _provideLinks(resource:URI, workspaceResource:URI):winjs.TPromise<modes.ILink[]> {
return this._worker((w) => w.provideLinks(resource, workspaceResource));
}
static $_provideDocumentRangeFormattingEdits = CompatWorkerAttr(HTMLMode, HTMLMode.prototype._provideDocumentRangeFormattingEdits);
private _provideDocumentRangeFormattingEdits(resource:URI, range:editorCommon.IRange, options:modes.FormattingOptions):winjs.TPromise<editorCommon.ISingleEditOperation[]> {
return this._worker((w) => w.provideDocumentRangeFormattingEdits(resource, range, options));
}
static $_provideDocumentHighlights = CompatWorkerAttr(HTMLMode, HTMLMode.prototype._provideDocumentHighlights);
protected _provideDocumentHighlights(resource:URI, position:editorCommon.IPosition, strict:boolean = false): winjs.TPromise<modes.DocumentHighlight[]> {
return this._worker((w) => w.provideDocumentHighlights(resource, position, strict));
}
static $_provideCompletionItems = CompatWorkerAttr(HTMLMode, HTMLMode.prototype._provideCompletionItems);
protected _provideCompletionItems(resource:URI, position:editorCommon.IPosition):winjs.TPromise<modes.ISuggestResult> {
return this._worker((w) => w.provideCompletionItems(resource, position));
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import arrays = require('vs/base/common/arrays');
export const EMPTY_ELEMENTS:string[] = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];
export function isEmptyElement(e: string) : boolean {
return arrays.binarySearch(EMPTY_ELEMENTS, e,(s1: string, s2: string) => s1.localeCompare(s2)) >= 0;
}
/*---------------------------------------------------------------------------------------------
* 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 {isTag, DELIM_END, DELIM_START, DELIM_ASSIGN, ATTRIB_NAME, ATTRIB_VALUE} from 'vs/languages/html/common/htmlTokenTypes';
import EditorCommon = require('vs/editor/common/editorCommon');
export interface IHTMLScanner {
getTokenType(): string;
isOpenBrace(): boolean;
isAtTokenStart(): boolean;
isAtTokenEnd(): boolean;
getTokenContent(): string;
scanBack() : boolean;
scanForward() : boolean;
getTokenPosition(): EditorCommon.IPosition;
getTokenRange(): EditorCommon.IRange;
getModel(): EditorCommon.ITokenizedModel;
}
function isDelimiter(tokenType: string) {
switch (tokenType) {
case DELIM_START:
case DELIM_END:
case DELIM_ASSIGN:
return true;
}
return false;
}
function isInterestingToken(tokenType: string) {
switch (tokenType) {
case DELIM_START:
case DELIM_END:
case DELIM_ASSIGN:
case ATTRIB_NAME:
case ATTRIB_VALUE:
return true;
}
return isTag(tokenType);
}
export function getScanner(model: EditorCommon.ITokenizedModel, position:EditorCommon.IPosition) : IHTMLScanner {
var lineOffset = position.column - 1;
var currentLine = position.lineNumber;
var tokens = model.getLineTokens(currentLine);
var lineContent = model.getLineContent(currentLine);
var tokenIndex = tokens.findTokenIndexAtOffset(lineOffset);
var tokensOnLine = tokens.getTokenCount();
var tokenType = tokens.getTokenType(tokenIndex);
var tokenStart = tokens.getTokenStartOffset(tokenIndex);
var tokenEnd = tokens.getTokenEndOffset(tokenIndex);
if ((tokenType === '' || isDelimiter(tokenType)) && tokenStart === lineOffset) {
tokenIndex--;
if (tokenIndex >= 0) {
// we are at the end of a different token
tokenType = tokens.getTokenType(tokenIndex);
tokenStart = tokens.getTokenStartOffset(tokenIndex);
tokenEnd = tokens.getTokenEndOffset(tokenIndex);
} else {
tokenType = '';
tokenStart = tokenEnd = 0;
}
}
return {
getTokenType: () => tokenType,
isAtTokenEnd: () => lineOffset === tokenEnd,
isAtTokenStart: () => lineOffset === tokenStart,
getTokenContent: () => lineContent.substring(tokenStart, tokenEnd),
isOpenBrace: () => tokenStart < tokenEnd && lineContent.charAt(tokenStart) === '<',
getTokenPosition: () => <EditorCommon.IPosition> { lineNumber: currentLine, column: tokenStart + 1 },
getTokenRange: () => <EditorCommon.IRange> { startLineNumber: currentLine, startColumn: tokenStart + 1, endLineNumber: currentLine, endColumn: tokenEnd + 1 },
getModel: () => model,
scanBack: () => {
if (currentLine <= 0) {
return false;
}
tokenIndex--;
do {
while (tokenIndex >= 0) {
tokenType = tokens.getTokenType(tokenIndex);
tokenStart = tokens.getTokenStartOffset(tokenIndex);
tokenEnd = tokens.getTokenEndOffset(tokenIndex);
if (isInterestingToken(tokenType)) {
return true;
}
tokenIndex--;
}
currentLine--;
if (currentLine > 0) {
tokens = model.getLineTokens(currentLine);
lineContent = model.getLineContent(currentLine);
tokensOnLine = tokens.getTokenCount();
tokenIndex = tokensOnLine - 1;
}
} while (currentLine > 0);
tokens = null;
tokenType = lineContent = '';
tokenStart = tokenEnd = tokensOnLine = 0;
return false;
},
scanForward: () => {
if (currentLine > model.getLineCount()) {
return false;
}
tokenIndex++;
do {
while (tokenIndex < tokensOnLine) {
tokenType = tokens.getTokenType(tokenIndex);
tokenStart = tokens.getTokenStartOffset(tokenIndex);
tokenEnd = tokens.getTokenEndOffset(tokenIndex);
if (isInterestingToken(tokenType)) {
return true;
}
tokenIndex++;
}
currentLine++;
tokenIndex = 0;
if (currentLine <= model.getLineCount()) {
tokens = model.getLineTokens(currentLine);
lineContent = model.getLineContent(currentLine);
tokensOnLine = tokens.getTokenCount();
}
} while (currentLine <= model.getLineCount());
tokenType = lineContent = '';
tokenStart = tokenEnd = tokensOnLine = 0;
return false;
}
};
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*!
BEGIN THIRD PARTY
*/
/*--------------------------------------------------------------------------------------------
* This file is based on or incorporates material from the projects listed below (Third Party IP).
* The original copyright notice and the license under which Microsoft received such Third Party IP,
* are set forth below. Such licenses and notices are provided for informational purposes only.
* Microsoft licenses the Third Party IP to you under the licensing terms for the Microsoft product.
* Microsoft reserves all other rights not expressly granted under this agreement, whether by implication,
* estoppel or otherwise.
*--------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------
* Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang). This software or document includes includes material copied
* from or derived from HTML 5.1 W3C Working Draft (http://www.w3.org/TR/2015/WD-html51-20151008/.)"
*--------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------
* Ionic Main Site (https://github.com/driftyco/ionic-site).
* Copyright Drifty Co. http://drifty.com/.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
* MERCHANTABLITY OR NON-INFRINGEMENT.
*
* See the Apache Version 2.0 License for specific language governing permissions
* and limitations under the License.
*--------------------------------------------------------------------------------------------*/
import strings = require('vs/base/common/strings');
import nls = require('vs/nls');
export interface IHTMLTagProvider {
getId(): string;
collectTags(collector: (tag: string, label: string) => void): void;
collectAttributes(tag: string, collector: (attribute: string, type: string) => void): void;
collectValues(tag: string, attribute: string, collector: (value: string) => void): void;
}
export interface ITagSet {
[tag: string]: HTMLTagSpecification;
}
export class HTMLTagSpecification {
constructor(public label: string, public attributes: string[] = []) { }
}
interface IValueSets {
[tag: string]: string[];
}
// HTML tag information sourced from http://www.w3.org/TR/2015/WD-html51-20151008/
export const HTML_TAGS: ITagSet = {
// The root element
html: new HTMLTagSpecification(
nls.localize('tags.html', 'The html element represents the root of an HTML document.'),
['manifest']),
// Document metadata
head: new HTMLTagSpecification(
nls.localize('tags.head', 'The head element represents a collection of metadata for the Document.')),
title: new HTMLTagSpecification(
nls.localize('tags.title', 'The title element represents the document\'s title or name. Authors should use titles that identify their documents even when they are used out of context, for example in a user\'s history or bookmarks, or in search results. The document\'s title is often different from its first heading, since the first heading does not have to stand alone when taken out of context.')),
base: new HTMLTagSpecification(
nls.localize('tags.base', 'The base element allows authors to specify the document base URL for the purposes of resolving relative URLs, and the name of the default browsing context for the purposes of following hyperlinks. The element does not represent any content beyond this information.'),
['href', 'target']),
link: new HTMLTagSpecification(
nls.localize('tags.link', 'The link element allows authors to link their document to other resources.'),
['href', 'crossorigin:xo', 'rel', 'media', 'hreflang', 'type', 'sizes']),
meta: new HTMLTagSpecification(
nls.localize('tags.meta', 'The meta element represents various kinds of metadata that cannot be expressed using the title, base, link, style, and script elements.'),
['name', 'http-equiv', 'content', 'charset']),
style: new HTMLTagSpecification(
nls.localize('tags.style', 'The style element allows authors to embed style information in their documents. The style element is one of several inputs to the styling processing model. The element does not represent content for the user.'),
['media', 'nonce', 'type', 'scoped:v']),
// Sections
body: new HTMLTagSpecification(
nls.localize('tags.body', 'The body element represents the content of the document.'),
['onafterprint', 'onbeforeprint', 'onbeforeunload', 'onhashchange', 'onlanguagechange', 'onmessage', 'onoffline', 'ononline', 'onpagehide', 'onpageshow', 'onpopstate', 'onstorage', 'onunload']),
article: new HTMLTagSpecification(
nls.localize('tags.article', 'The article element represents a complete, or self-contained, composition in a document, page, application, or site and that is, in principle, independently distributable or reusable, e.g. in syndication. This could be a forum post, a magazine or newspaper article, a blog entry, a user-submitted comment, an interactive widget or gadget, or any other independent item of content. Each article should be identified, typically by including a heading (h1–h6 element) as a child of the article element.')),
section: new HTMLTagSpecification(
nls.localize('tags.section', 'The section element represents a generic section of a document or application. A section, in this context, is a thematic grouping of content. Each section should be identified, typically by including a heading ( h1- h6 element) as a child of the section element.')),
nav: new HTMLTagSpecification(
nls.localize('tags.nav', 'The nav element represents a section of a page that links to other pages or to parts within the page: a section with navigation links.')),
aside: new HTMLTagSpecification(
nls.localize('tags.aside', 'The aside element represents a section of a page that consists of content that is tangentially related to the content around the aside element, and which could be considered separate from that content. Such sections are often represented as sidebars in printed typography.')),
h1: new HTMLTagSpecification(
nls.localize('tags.h1', 'The h1 element represents a section heading.')),
h2: new HTMLTagSpecification(
nls.localize('tags.h2', 'The h2 element represents a section heading.')),
h3: new HTMLTagSpecification(
nls.localize('tags.h3', 'The h3 element represents a section heading.')),
h4: new HTMLTagSpecification(
nls.localize('tags.h4', 'The h4 element represents a section heading.')),
h5: new HTMLTagSpecification(
nls.localize('tags.h5', 'The h5 element represents a section heading.')),
h6: new HTMLTagSpecification(
nls.localize('tags.h6', 'The h6 element represents a section heading.')),
header: new HTMLTagSpecification(
nls.localize('tags.header', 'The header element represents introductory content for its nearest ancestor sectioning content or sectioning root element. A header typically contains a group of introductory or navigational aids. When the nearest ancestor sectioning content or sectioning root element is the body element, then it applies to the whole page.')),
footer: new HTMLTagSpecification(
nls.localize('tags.footer', 'The footer element represents a footer for its nearest ancestor sectioning content or sectioning root element. A footer typically contains information about its section such as who wrote it, links to related documents, copyright data, and the like.')),
address: new HTMLTagSpecification(
nls.localize('tags.address', 'The address element represents the contact information for its nearest article or body element ancestor. If that is the body element, then the contact information applies to the document as a whole.')),
// Grouping content
p: new HTMLTagSpecification(
nls.localize('tags.p', 'The p element represents a paragraph.')),
hr: new HTMLTagSpecification(
nls.localize('tags.hr', 'The hr element represents a paragraph-level thematic break, e.g. a scene change in a story, or a transition to another topic within a section of a reference book.')),
pre: new HTMLTagSpecification(
nls.localize('tags.pre', 'The pre element represents a block of preformatted text, in which structure is represented by typographic conventions rather than by elements.')),
blockquote: new HTMLTagSpecification(
nls.localize('tags.blockquote', 'The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a footer or cite element, and optionally with in-line changes such as annotations and abbreviations.'),
['cite']),
ol: new HTMLTagSpecification(
nls.localize('tags.ol', 'The ol element represents a list of items, where the items have been intentionally ordered, such that changing the order would change the meaning of the document.'),
['reversed:v', 'start', 'type:lt']),
ul: new HTMLTagSpecification(
nls.localize('tags.ul', 'The ul element represents a list of items, where the order of the items is not important — that is, where changing the order would not materially change the meaning of the document.')),
li: new HTMLTagSpecification(
nls.localize('tags.li', 'The li element represents a list item. If its parent element is an ol, ul, or menu element, then the element is an item of the parent element\'s list, as defined for those elements. Otherwise, the list item has no defined list-related relationship to any other li element.'),
['value']),
dl: new HTMLTagSpecification(
nls.localize('tags.dl', 'The dl element represents an association list consisting of zero or more name-value groups (a description list). A name-value group consists of one or more names (dt elements) followed by one or more values (dd elements), ignoring any nodes other than dt and dd elements. Within a single dl element, there should not be more than one dt element for each name.')),
dt: new HTMLTagSpecification(
nls.localize('tags.dt', 'The dt element represents the term, or name, part of a term-description group in a description list (dl element).')),
dd: new HTMLTagSpecification(
nls.localize('tags.dd', 'The dd element represents the description, definition, or value, part of a term-description group in a description list (dl element).')),
figure: new HTMLTagSpecification(
nls.localize('tags.figure', 'The figure element represents some flow content, optionally with a caption, that is self-contained (like a complete sentence) and is typically referenced as a single unit from the main flow of the document.')),
figcaption: new HTMLTagSpecification(
nls.localize('tags.figcaption', 'The figcaption element represents a caption or legend for the rest of the contents of the figcaption element\'s parent figure element, if any.')),
main: new HTMLTagSpecification(
nls.localize('tags.main', 'The main element represents the main content of the body of a document or application. The main content area consists of content that is directly related to or expands upon the central topic of a document or central functionality of an application.')),
div: new HTMLTagSpecification(
nls.localize('tags.div', 'The div element has no special meaning at all. It represents its children. It can be used with the class, lang, and title attributes to mark up semantics common to a group of consecutive elements.')),
// Text-level semantics
a: new HTMLTagSpecification(
nls.localize('tags.a', 'If the a element has an href attribute, then it represents a hyperlink (a hypertext anchor) labeled by its contents.'),
['href', 'target', 'download', 'ping', 'rel', 'hreflang', 'type']),
em: new HTMLTagSpecification(
nls.localize('tags.em', 'The em element represents stress emphasis of its contents.')),
strong: new HTMLTagSpecification(
nls.localize('tags.strong', 'The strong element represents strong importance, seriousness, or urgency for its contents.')),
small: new HTMLTagSpecification(
nls.localize('tags.small', 'The small element represents side comments such as small print.')),
s: new HTMLTagSpecification(
nls.localize('tags.s', 'The s element represents contents that are no longer accurate or no longer relevant.')),
cite: new HTMLTagSpecification(
nls.localize('tags.cite', 'The cite element represents a reference to a creative work. It must include the title of the work or the name of the author(person, people or organization) or an URL reference, or a reference in abbreviated form as per the conventions used for the addition of citation metadata.')),
q: new HTMLTagSpecification(
nls.localize('tags.q', 'The q element represents some phrasing content quoted from another source.'),
['cite']),
dfn: new HTMLTagSpecification(
nls.localize('tags.dfn', 'The dfn element represents the defining instance of a term. The paragraph, description list group, or section that is the nearest ancestor of the dfn element must also contain the definition(s) for the term given by the dfn element.')),
abbr: new HTMLTagSpecification(
nls.localize('tags.abbr', 'The abbr element represents an abbreviation or acronym, optionally with its expansion. The title attribute may be used to provide an expansion of the abbreviation. The attribute, if specified, must contain an expansion of the abbreviation, and nothing else.')),
ruby: new HTMLTagSpecification(
nls.localize('tags.ruby', 'The ruby element allows one or more spans of phrasing content to be marked with ruby annotations. Ruby annotations are short runs of text presented alongside base text, primarily used in East Asian typography as a guide for pronunciation or to include other annotations. In Japanese, this form of typography is also known as furigana. Ruby text can appear on either side, and sometimes both sides, of the base text, and it is possible to control its position using CSS. A more complete introduction to ruby can be found in the Use Cases & Exploratory Approaches for Ruby Markup document as well as in CSS Ruby Module Level 1. [RUBY-UC] [CSSRUBY]')),
rb: new HTMLTagSpecification(
nls.localize('tags.rb', 'The rb element marks the base text component of a ruby annotation. When it is the child of a ruby element, it doesn\'t represent anything itself, but its parent ruby element uses it as part of determining what it represents.')),
rt: new HTMLTagSpecification(
nls.localize('tags.rt', 'The rt element marks the ruby text component of a ruby annotation. When it is the child of a ruby element or of an rtc element that is itself the child of a ruby element, it doesn\'t represent anything itself, but its ancestor ruby element uses it as part of determining what it represents.')),
// <rtc> is not yet supported by 2+ browsers
//rtc: new HTMLTagSpecification(
// nls.localize('tags.rtc', 'The rtc element marks a ruby text container for ruby text components in a ruby annotation. When it is the child of a ruby element it doesn\'t represent anything itself, but its parent ruby element uses it as part of determining what it represents.')),
rp: new HTMLTagSpecification(
nls.localize('tags.rp', 'The rp element is used to provide fallback text to be shown by user agents that don\'t support ruby annotations. One widespread convention is to provide parentheses around the ruby text component of a ruby annotation.')),
// <data> is not yet supported by 2+ browsers
//data: new HTMLTagSpecification(
// nls.localize('tags.data', 'The data element represents its contents, along with a machine-readable form of those contents in the value attribute.')),
time: new HTMLTagSpecification(
nls.localize('tags.time', 'The time element represents its contents, along with a machine-readable form of those contents in the datetime attribute. The kind of content is limited to various kinds of dates, times, time-zone offsets, and durations, as described below.'),
['datetime']),
code: new HTMLTagSpecification(
nls.localize('tags.code', 'The code element represents a fragment of computer code. This could be an XML element name, a file name, a computer program, or any other string that a computer would recognize.')),
var: new HTMLTagSpecification(
nls.localize('tags.var', 'The var element represents a variable. This could be an actual variable in a mathematical expression or programming context, an identifier representing a constant, a symbol identifying a physical quantity, a function parameter, or just be a term used as a placeholder in prose.')),
samp: new HTMLTagSpecification(
nls.localize('tags.samp', 'The samp element represents sample or quoted output from another program or computing system.')),
kbd: new HTMLTagSpecification(
nls.localize('tags.kbd', 'The kbd element represents user input (typically keyboard input, although it may also be used to represent other input, such as voice commands).')),
sub: new HTMLTagSpecification(
nls.localize('tags.sub', 'The sub element represents a subscript.')),
sup: new HTMLTagSpecification(
nls.localize('tags.sup', 'The sup element represents a superscript.')),
i: new HTMLTagSpecification(
nls.localize('tags.i', 'The i element represents a span of text in an alternate voice or mood, or otherwise offset from the normal prose in a manner indicating a different quality of text, such as a taxonomic designation, a technical term, an idiomatic phrase from another language, transliteration, a thought, or a ship name in Western texts.')),
b: new HTMLTagSpecification(
nls.localize('tags.b', 'The b element represents a span of text to which attention is being drawn for utilitarian purposes without conveying any extra importance and with no implication of an alternate voice or mood, such as key words in a document abstract, product names in a review, actionable words in interactive text-driven software, or an article lede.')),
u: new HTMLTagSpecification(
nls.localize('tags.u', 'The u element represents a span of text with an unarticulated, though explicitly rendered, non-textual annotation, such as labeling the text as being a proper name in Chinese text (a Chinese proper name mark), or labeling the text as being misspelt.')),
mark: new HTMLTagSpecification(
nls.localize('tags.mark', 'The mark element represents a run of text in one document marked or highlighted for reference purposes, due to its relevance in another context. When used in a quotation or other block of text referred to from the prose, it indicates a highlight that was not originally present but which has been added to bring the reader\'s attention to a part of the text that might not have been considered important by the original author when the block was originally written, but which is now under previously unexpected scrutiny. When used in the main prose of a document, it indicates a part of the document that has been highlighted due to its likely relevance to the user\'s current activity.')),
bdi: new HTMLTagSpecification(
nls.localize('tags.bdi', 'The bdi element represents a span of text that is to be isolated from its surroundings for the purposes of bidirectional text formatting. [BIDI]')),
bdo: new HTMLTagSpecification(
nls.localize('tags.dbo', 'The bdo element represents explicit text directionality formatting control for its children. It allows authors to override the Unicode bidirectional algorithm by explicitly specifying a direction override. [BIDI]')),
span: new HTMLTagSpecification(
nls.localize('tags.span', 'The span element doesn\'t mean anything on its own, but can be useful when used together with the global attributes, e.g. class, lang, or dir. It represents its children.')),
br: new HTMLTagSpecification(
nls.localize('tags.br', 'The br element represents a line break.')),
wbr: new HTMLTagSpecification(
nls.localize('tags.wbr', 'The wbr element represents a line break opportunity.')),
// Edits
ins: new HTMLTagSpecification(
nls.localize('tags.ins', 'The ins element represents an addition to the document.')),
del: new HTMLTagSpecification(
nls.localize('tags.del', 'The del element represents a removal from the document.'),
['cite', 'datetime']),
// Embedded content
picture: new HTMLTagSpecification(
nls.localize('tags.picture', 'The picture element is a container which provides multiple sources to its contained img element to allow authors to declaratively control or give hints to the user agent about which image resource to use, based on the screen pixel density, viewport size, image format, and other factors. It represents its children.')),
img: new HTMLTagSpecification(
nls.localize('tags.img', 'An img element represents an image.'),
['alt', 'src', 'srcset', 'crossorigin:xo', 'usemap', 'ismap:v', 'width', 'height']),
iframe: new HTMLTagSpecification(
nls.localize('tags.iframe', 'The iframe element represents a nested browsing context.'),
['src', 'srcdoc', 'name', 'sandbox:sb', 'seamless:v', 'allowfullscreen:v', 'width', 'height']),
embed: new HTMLTagSpecification(
nls.localize('tags.embed', 'The embed element provides an integration point for an external (typically non-HTML) application or interactive content.'),
['src', 'type', 'width', 'height']),
object: new HTMLTagSpecification(
nls.localize('tags.object', 'The object element can represent an external resource, which, depending on the type of the resource, will either be treated as an image, as a nested browsing context, or as an external resource to be processed by a plugin.'),
['data', 'type', 'typemustmatch:v', 'name', 'usemap', 'form', 'width', 'height']),
param: new HTMLTagSpecification(
nls.localize('tags.param', 'The param element defines parameters for plugins invoked by object elements. It does not represent anything on its own.'),
['name', 'value']),
video: new HTMLTagSpecification(
nls.localize('tags.video', 'A video element is used for playing videos or movies, and audio files with captions.'),
['src', 'crossorigin:xo', 'poster', 'preload:pl', 'autoplay:v', 'mediagroup', 'loop:v', 'muted:v', 'controls:v', 'width', 'height']),
audio: new HTMLTagSpecification(
nls.localize('tags.audio', 'An audio element represents a sound or audio stream.'),
['src', 'crossorigin:xo', 'preload:pl', 'autoplay:v', 'mediagroup', 'loop:v', 'muted:v', 'controls:v']),
source: new HTMLTagSpecification(
nls.localize('tags.source', 'The source element allows authors to specify multiple alternative media resources for media elements. It does not represent anything on its own.'),
// 'When the source element has a parent that is a picture element, the source element allows authors to specify multiple alternative source sets for img elements.'
['src', 'type']),
track: new HTMLTagSpecification(
nls.localize('tags.track', 'The track element allows authors to specify explicit external timed text tracks for media elements. It does not represent anything on its own.'),
['default:v', 'kind:tk', 'label', 'src', 'srclang']),
map: new HTMLTagSpecification(
nls.localize('tags.map', 'The map element, in conjunction with an img element and any area element descendants, defines an image map. The element represents its children.'),
['name']),
area: new HTMLTagSpecification(
nls.localize('tags.area', 'The area element represents either a hyperlink with some text and a corresponding area on an image map, or a dead area on an image map.'),
['alt', 'coords', 'shape:sh', 'href', 'target', 'download', 'ping', 'rel', 'hreflang', 'type']),
// Tabular data
table: new HTMLTagSpecification(
nls.localize('tags.table', 'The table element represents data with more than one dimension, in the form of a table.'),
['sortable:v', 'border']),
caption: new HTMLTagSpecification(
nls.localize('tags.caption', 'The caption element represents the title of the table that is its parent, if it has a parent and that is a table element.')),
colgroup: new HTMLTagSpecification(
nls.localize('tags.colgroup', 'The colgroup element represents a group of one or more columns in the table that is its parent, if it has a parent and that is a table element.'),
['span']),
col: new HTMLTagSpecification(
nls.localize('tags.col', 'If a col element has a parent and that is a colgroup element that itself has a parent that is a table element, then the col element represents one or more columns in the column group represented by that colgroup.'),
['span']),
tbody: new HTMLTagSpecification(
nls.localize('tags.tbody', 'The tbody element represents a block of rows that consist of a body of data for the parent table element, if the tbody element has a parent and it is a table.')),
thead: new HTMLTagSpecification(
nls.localize('tags.thead', 'The thead element represents the block of rows that consist of the column labels (headers) for the parent table element, if the thead element has a parent and it is a table.')),
tfoot: new HTMLTagSpecification(
nls.localize('tags.tfoot', 'The tfoot element represents the block of rows that consist of the column summaries (footers) for the parent table element, if the tfoot element has a parent and it is a table.')),
tr: new HTMLTagSpecification(
nls.localize('tags.tr', 'The tr element represents a row of cells in a table.')),
td: new HTMLTagSpecification(
nls.localize('tags.td', 'The td element represents a data cell in a table.'),
['colspan', 'rowspan', 'headers']),
th: new HTMLTagSpecification(
nls.localize('tags.th', 'The th element represents a header cell in a table.'),
['colspan', 'rowspan', 'headers', 'scope:s', 'sorted', 'abbr']),
// Forms
form: new HTMLTagSpecification(
nls.localize('tags.form', 'The form element represents a collection of form-associated elements, some of which can represent editable values that can be submitted to a server for processing.'),
['accept-charset', 'action', 'autocomplete:o', 'enctype:et', 'method:m', 'name', 'novalidate:v', 'target']),
label: new HTMLTagSpecification(
nls.localize('tags.label', 'The label element represents a caption in a user interface. The caption can be associated with a specific form control, known as the label element\'s labeled control, either using the for attribute, or by putting the form control inside the label element itself.'),
['form', 'for']),
input: new HTMLTagSpecification(
nls.localize('tags.input', 'The input element represents a typed data field, usually with a form control to allow the user to edit the data.'),
['accept', 'alt', 'autocomplete:inputautocomplete', 'autofocus:v', 'checked:v', 'dirname', 'disabled:v', 'form', 'formaction', 'formenctype:et', 'formmethod:fm', 'formnovalidate:v', 'formtarget', 'height', 'inputmode:im', 'list', 'max', 'maxlength', 'min', 'minlength', 'multiple:v', 'name', 'pattern', 'placeholder', 'readonly:v', 'required:v', 'size', 'src', 'step', 'type:t', 'value', 'width']),
button: new HTMLTagSpecification(
nls.localize('tags.button', 'The button element represents a button labeled by its contents.'),
['autofocus:v', 'disabled:v', 'form', 'formaction', 'formenctype:et', 'formmethod:fm', 'formnovalidate:v', 'formtarget', 'name', 'type:bt', 'value']),
select: new HTMLTagSpecification(
nls.localize('tags.select', 'The select element represents a control for selecting amongst a set of options.'),
['autocomplete:inputautocomplete', 'autofocus:v', 'disabled:v', 'form', 'multiple:v', 'name', 'required:v', 'size']),
datalist: new HTMLTagSpecification(
nls.localize('tags.datalist', 'The datalist element represents a set of option elements that represent predefined options for other controls. In the rendering, the datalist element represents nothing and it, along with its children, should be hidden.')),
optgroup: new HTMLTagSpecification(
nls.localize('tags.optgroup', 'The optgroup element represents a group of option elements with a common label.'),
['disabled:v', 'label']),
option: new HTMLTagSpecification(
nls.localize('tags.option', 'The option element represents an option in a select element or as part of a list of suggestions in a datalist element.'),
['disabled:v', 'label', 'selected:v', 'value']),
textarea: new HTMLTagSpecification(
nls.localize('tags.textarea', 'The textarea element represents a multiline plain text edit control for the element\'s raw value. The contents of the control represent the control\'s default value.'),
['autocomplete:inputautocomplete', 'autofocus:v', 'cols', 'dirname', 'disabled:v', 'form', 'inputmode:im', 'maxlength', 'minlength', 'name', 'placeholder', 'readonly:v', 'required:v', 'rows', 'wrap:w']),
output: new HTMLTagSpecification(
nls.localize('tags.output', 'The output element represents the result of a calculation performed by the application, or the result of a user action.'),
['for', 'form', 'name']),
progress: new HTMLTagSpecification(
nls.localize('tags.progress', 'The progress element represents the completion progress of a task. The progress is either indeterminate, indicating that progress is being made but that it is not clear how much more work remains to be done before the task is complete (e.g. because the task is waiting for a remote host to respond), or the progress is a number in the range zero to a maximum, giving the fraction of work that has so far been completed.'),
['value', 'max']),
meter: new HTMLTagSpecification(
nls.localize('tags.meter', 'The meter element represents a scalar measurement within a known range, or a fractional value; for example disk usage, the relevance of a query result, or the fraction of a voting population to have selected a particular candidate.'),
['value', 'min', 'max', 'low', 'high', 'optimum']),
fieldset: new HTMLTagSpecification(
nls.localize('tags.fieldset', 'The fieldset element represents a set of form controls optionally grouped under a common name.'),
['disabled:v', 'form', 'name']),
legend: new HTMLTagSpecification(
nls.localize('tags.legend', 'The legend element represents a caption for the rest of the contents of the legend element\'s parent fieldset element, if any.')),
// Interactive elements
details: new HTMLTagSpecification(
nls.localize('tags.details', 'The details element represents a disclosure widget from which the user can obtain additional information or controls.'),
['open:v']),
summary: new HTMLTagSpecification(
nls.localize('tags.summary', 'The summary element represents a summary, caption, or legend for the rest of the contents of the summary element\'s parent details element, if any.')),
// <menu> and <menuitem> are not yet supported by 2+ browsers
//menu: new HTMLTagSpecification(
// nls.localize('tags.menu', 'The menu element represents a list of commands.'),
// ['type:mt', 'label']),
//menuitem: new HTMLTagSpecification(
// nls.localize('tags.menuitem', 'The menuitem element represents a command that the user can invoke from a popup menu (either a context menu or the menu of a menu button).')),
dialog: new HTMLTagSpecification(
nls.localize('tags.dialog', 'The dialog element represents a part of an application that a user interacts with to perform a task, for example a dialog box, inspector, or window.')),
// Scripting
script: new HTMLTagSpecification(
nls.localize('tags.script', 'The script element allows authors to include dynamic script and data blocks in their documents. The element does not represent content for the user.'),
['src', 'type', 'charset', 'async:v', 'defer:v', 'crossorigin:xo', 'nonce']),
noscript: new HTMLTagSpecification(
nls.localize('tags.noscript', 'The noscript element represents nothing if scripting is enabled, and represents its children if scripting is disabled. It is used to present different markup to user agents that support scripting and those that don\'t support scripting, by affecting how the document is parsed.')),
template: new HTMLTagSpecification(
nls.localize('tags.template', 'The template element is used to declare fragments of HTML that can be cloned and inserted in the document by script.')),
canvas: new HTMLTagSpecification(
nls.localize('tags.canvas', 'The canvas element provides scripts with a resolution-dependent bitmap canvas, which can be used for rendering graphs, game graphics, art, or other visual images on the fly.'),
['width', 'height'])
};
// Ionic tag information sourced from Ionic main website (https://github.com/driftyco/ionic-site)
export const IONIC_TAGS: ITagSet = {
'ion-checkbox': new HTMLTagSpecification(nls.localize('tags.ion.checkbox', 'The checkbox is no different than the HTML checkbox input, except it\'s styled differently. The checkbox behaves like any AngularJS checkbox.'),
['name', 'ng-false-value', 'ng-model', 'ng-true-value']),
'ion-content': new HTMLTagSpecification(nls.localize('tags.ion.content', 'The ionContent directive provides an easy to use content area that can be configured to use Ionic\'s custom Scroll View, or the built-in overflow scrolling of the browser.'),
['delegate-handle', 'direction:scrolldir', 'has-bouncing:b', 'locking:b', 'on-scroll', 'on-scroll-complete', 'overflow-scroll:b', 'padding:b', 'scroll:b', 'scrollbar-x:b', 'scrollbar-y:b', 'start-x', 'start-y']),
'ion-delete-button': new HTMLTagSpecification(nls.localize('tags.ion.deletebutton', 'Child of ionItem'),
[]),
'ion-footer-bar': new HTMLTagSpecification(nls.localize('tags.ion.footerbar', 'Adds a fixed footer bar below some content. Can also be a subfooter (higher up) if the "bar-subfooter" class is applied.'),
['align-title:align', 'keyboard-attach:v']),
'ion-header-bar': new HTMLTagSpecification(nls.localize('tags.ion.headerbar', 'Adds a fixed header bar above some content. Can also be a subheader (lower down) if the "bar-subheader" class is applied.'),
['align-title:align', 'no-tap-scroll:b']),
'ion-infinite-scroll': new HTMLTagSpecification(nls.localize('tags.ion.infinitescroll', 'Child of ionContent or ionScroll. The ionInfiniteScroll directive allows you to call a function whenever the user gets to the bottom of the page or near the bottom of the page.'),
['distance', 'icon', 'immediate-check:b', 'on-infinite', 'spinner']),
'ion-input': new HTMLTagSpecification(nls.localize('tags.ion.input', 'ionInput is meant for text type inputs only. Ionic uses an actual <input type="text"> HTML element within the component, with Ionic wrapping to better handle the user experience and interactivity.'),
['type:inputtype', 'clearInput:v']),
'ion-item': new HTMLTagSpecification(nls.localize('tags.ion.item', 'Child of ionList.'),
[]),
'ion-list': new HTMLTagSpecification(nls.localize('tags.ion.list', 'The List is a widely used interface element in almost any mobile app, and can include content ranging from basic text all the way to buttons, toggles, icons, and thumbnails.'),
['can-swipe:b', 'delegate-handle', 'show-delete:b', 'show-reorder:b', 'type:listtype']),
'ion-modal-view': new HTMLTagSpecification(nls.localize('tags.ion.modalview', 'The Modal is a content pane that can go over the user\'s main view temporarily. Usually used for making a choice or editing an item.'),
[]),
'ion-nav-back-button': new HTMLTagSpecification(nls.localize('tags.ion.navbackbutton', 'Child of ionNavBar. Creates a back button inside an ionNavBar. The back button will appear when the user is able to go back in the current navigation stack.'),
[]),
'ion-nav-bar': new HTMLTagSpecification(nls.localize('tags.ion.navbar', 'If you have an ionNavView directive, you can also create an <ion-nav-bar>, which will create a topbar that updates as the application state changes.'),
['align-title:align', 'delegate-handle', 'no-tap-scroll:b']),
'ion-nav-buttons': new HTMLTagSpecification(nls.localize('tags.ion.navbuttons', 'Child of ionNavView. Use ionNavButtons to set the buttons on your ionNavBar from within an ionView.'),
['side:navsides']),
'ion-nav-title': new HTMLTagSpecification(nls.localize('tags.ion.navtitle', 'Child of ionNavView. The ionNavTitle directive replaces an ionNavBar title text with custom HTML from within an ionView template.'),
[]),
'ion-nav-view': new HTMLTagSpecification(nls.localize('tags.ion.navview', 'The ionNavView directive is used to render templates in your application. Each template is part of a state. States are usually mapped to a url, and are defined programatically using angular-ui-router.'),
['name']),
'ion-option-button': new HTMLTagSpecification(nls.localize('tags.ion.optionbutton', 'Child of ionItem. Creates an option button inside a list item, that is visible when the item is swiped to the left by the user.'),
[]),
'ion-pane': new HTMLTagSpecification(nls.localize('tags.ion.pane', 'A simple container that fits content, with no side effects. Adds the "pane" class to the element.'),
[]),
'ion-popover-view': new HTMLTagSpecification(nls.localize('tags.ion.popoverview', 'The Popover is a view that floats above an app\'s content. Popovers provide an easy way to present or gather information from the user.'),
[]),
'ion-radio': new HTMLTagSpecification(nls.localize('tags.ion.radio', 'The radio ionRirective is no different than the HTML radio input, except it\'s styled differently. The ionRadio behaves like AngularJS radio input.'),
['disabled:b', 'icon', 'name', 'ng-disabled:b', 'ng-model', 'ng-value', 'value']),
'ion-refresher': new HTMLTagSpecification(nls.localize('tags.ion.refresher', 'Child of ionContent or ionScroll. Allows you to add pull-to-refresh to a scrollView. Place it as the first child of your ionContent or ionScroll element.'),
['disable-pulling-rotation:b', 'on-pulling', 'on-refresh', 'pulling-icon', 'pulling-text', 'refreshing-icon', 'spinner']),
'ion-reorder-button': new HTMLTagSpecification(nls.localize('tags.ion.reorderbutton', 'Child of ionItem.'),
['on-reorder']),
'ion-scroll': new HTMLTagSpecification(nls.localize('tags.ion.scroll', 'Creates a scrollable container for all content inside.'),
['delegate-handle', 'direction:scrolldir', 'has-bouncing:b', 'locking:b', 'max-zoom', 'min-zoom', 'on-refresh', 'on-scroll', 'paging:b', 'scrollbar-x:b', 'scrollbar-y:b', 'zooming:b']),
'ion-side-menu': new HTMLTagSpecification(nls.localize('tags.ion.sidemenu', 'Child of ionSideMenus. A container for a side menu, sibling to an ionSideMenuContent directive.'),
['is-enabled:b', 'expose-aside-when', 'side:navsides', 'width']),
'ion-side-menu-content': new HTMLTagSpecification(nls.localize('tags.ion.sidemenucontent', 'Child of ionSideMenus. A container for the main visible content, sibling to one or more ionSideMenu directives.'),
['drag-content:b', 'edge-drag-threshold']),
'ion-side-menus': new HTMLTagSpecification(nls.localize('tags.ion.sidemenus', 'A container element for side menu(s) and the main content. Allows the left and/or right side menu to be toggled by dragging the main content area side to side.'),
['delegate-handle', 'enable-menu-with-back-views:b']),
'ion-slide': new HTMLTagSpecification(nls.localize('tags.ion.slide', 'Child of ionSlideBox. Displays a slide inside of a slidebox.'),
[]),
'ion-slide-box': new HTMLTagSpecification(nls.localize('tags.ion.slidebox', 'The Slide Box is a multi-page container where each page can be swiped or dragged between.'),
['active-slide', 'auto-play:b', 'delegate-handle', 'does-continue:b', 'on-slide-changed', 'pager-click', 'show-pager:b', 'slide-interval']),
'ion-spinner': new HTMLTagSpecification(nls.localize('tags.ion.spinner', 'The ionSpinner directive provides a variety of animated spinners.'),
['icon']),
'ion-tab': new HTMLTagSpecification(nls.localize('tags.ion.tab', 'Child of ionTabs. Contains a tab\'s content. The content only exists while the given tab is selected.'),
['badge', 'badge-style', 'disabled', 'hidden', 'href', 'icon', 'icon-off', 'icon-on', 'ng-click', 'on-deselect', 'on-select', 'title']),
'ion-tabs': new HTMLTagSpecification(nls.localize('tags.ion.tabs', 'Powers a multi-tabbed interface with a tab bar and a set of "pages" that can be tabbed through.'),
['delegate-handle']),
'ion-title': new HTMLTagSpecification(nls.localize('tags.ion.title', 'ion-title is a component that sets the title of the ionNavbar'),
[]),
'ion-toggle': new HTMLTagSpecification(nls.localize('tags.ion.toggle', 'A toggle is an animated switch which binds a given model to a boolean. Allows dragging of the switch\'s nub. The toggle behaves like any AngularJS checkbox otherwise.'),
['name', 'ng-false-value', 'ng-model', 'ng-true-value', 'toggle-class']),
'ion-view ': new HTMLTagSpecification(nls.localize('tags.ion.view', 'Child of ionNavView. A container for view content and any navigational and header bar information.'),
['cache-view:b', 'can-swipe-back:b', 'hide-back-button:b', 'hide-nav-bar:b', 'view-title'])
};
export function getHTML5TagProvider(): IHTMLTagProvider {
var globalAttributes = [
'aria-activedescendant', 'aria-atomic:b', 'aria-autocomplete:autocomplete', 'aria-busy:b', 'aria-checked:tristate', 'aria-colcount', 'aria-colindex', 'aria-colspan', 'aria-controls', 'aria-current:current', 'aria-describedat',
'aria-describedby', 'aria-disabled:b', 'aria-dropeffect:dropeffect', 'aria-errormessage', 'aria-expanded:u', 'aria-flowto', 'aria-grabbed:u', 'aria-haspopup:b', 'aria-hidden:b', 'aria-invalid:invalid', 'aria-kbdshortcuts',
'aria-label', 'aria-labelledby', 'aria-level', 'aria-live:live', 'aria-modal:b', 'aria-multiline:b', 'aria-multiselectable:b', 'aria-orientation:orientation', 'aria-owns', 'aria-placeholder', 'aria-posinset', 'aria-pressed:tristate',
'aria-readonly:b', 'aria-relevant:relevant', 'aria-required:b', 'aria-roledescription', 'aria-rowcount', 'aria-rowindex', 'aria-rowspan', 'aria-selected:u', 'aria-setsize', 'aria-sort:sort', 'aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext',
'accesskey', 'class', 'contenteditable:b', 'contextmenu', 'dir:d', 'draggable:b', 'dropzone', 'hidden:v', 'id', 'itemid', 'itemprop', 'itemref', 'itemscope:v', 'itemtype', 'lang', 'role:roles', 'spellcheck:b', 'style', 'tabindex',
'title', 'translate:y'];
var eventHandlers = ['onabort', 'onblur', 'oncanplay', 'oncanplaythrough', 'onchange', 'onclick', 'oncontextmenu', 'ondblclick', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart',
'ondrop', 'ondurationchange', 'onemptied', 'onended', 'onerror', 'onfocus', 'onformchange', 'onforminput', 'oninput', 'oninvalid', 'onkeydown', 'onkeypress', 'onkeyup', 'onload', 'onloadeddata', 'onloadedmetadata',
'onloadstart', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onpause', 'onplay', 'onplaying', 'onprogress', 'onratechange', 'onreset', 'onresize', 'onreadystatechange', 'onscroll',
'onseeked', 'onseeking', 'onselect', 'onshow', 'onstalled', 'onsubmit', 'onsuspend', 'ontimeupdate', 'onvolumechange', 'onwaiting'];
var valueSets: IValueSets = {
b: ['true', 'false'],
u: ['true', 'false', 'undefined'],
o: ['on', 'off'],
y: ['yes', 'no'],
w: ['soft', 'hard'],
d: ['ltr', 'rtl', 'auto'],
m: ['GET', 'POST', 'dialog'],
fm: ['GET', 'POST'],
s: ['row', 'col', 'rowgroup', 'colgroup'],
t: ['hidden', 'text', 'search', 'tel', 'url', 'email', 'password', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'range', 'color', 'checkbox', 'radio', 'file', 'submit', 'image', 'reset', 'button'],
im: ['verbatim', 'latin', 'latin-name', 'latin-prose', 'full-width-latin', 'kana', 'kana-name', 'katakana', 'numeric', 'tel', 'email', 'url'],
bt: ['button', 'submit', 'reset', 'menu'],
lt: ['1', 'a', 'A', 'i', 'I'],
mt: ['context', 'toolbar'],
mit: ['command', 'checkbox', 'radio'],
et: ['application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain'],
tk: ['subtitles', 'captions', 'descriptions', 'chapters', 'metadata'],
pl: ['none', 'metadata', 'auto'],
sh: ['circle', 'default', 'poly', 'rect'],
xo: ['anonymous', 'use-credentials'],
sb: ['allow-forms', 'allow-modals', 'allow-pointer-lock', 'allow-popups', 'allow-popups-to-escape-sandbox', 'allow-same-origin', 'allow-scripts', 'allow-top-navigation'],
tristate: ['true', 'false', 'mixed', 'undefined'],
inputautocomplete: ['additional-name', 'address-level1', 'address-level2', 'address-level3', 'address-level4', 'address-line1', 'address-line2', 'address-line3', 'bday', 'bday-year', 'bday-day', 'bday-month', 'billing', 'cc-additional-name', 'cc-csc', 'cc-exp', 'cc-exp-month', 'cc-exp-year', 'cc-family-name', 'cc-given-name', 'cc-name', 'cc-number', 'cc-type', 'country', 'country-name', 'current-password', 'email', 'family-name', 'fax', 'given-name', 'home', 'honorific-prefix', 'honorific-suffix', 'impp', 'language', 'mobile', 'name', 'new-password', 'nickname', 'organization', 'organization-title', 'pager', 'photo', 'postal-code', 'sex', 'shipping', 'street-address', 'tel-area-code', 'tel', 'tel-country-code', 'tel-extension', 'tel-local', 'tel-local-prefix', 'tel-local-suffix', 'tel-national', 'transaction-amount', 'transaction-currency', 'url', 'username', 'work'],
autocomplete: ['inline', 'list', 'both', 'none'],
current: ['page', 'step', 'location', 'date', 'time', 'true', 'false'],
dropeffect: ['copy', 'move', 'link', 'execute', 'popup', 'none'],
invalid: ['grammar', 'false', 'spelling', 'true'],
live: ['off', 'polite', 'assertive'],
orientation: ['vertical', 'horizontal', 'undefined'],
relevant: ['additions', 'removals', 'text', 'all', 'additions text'],
sort: ['ascending', 'descending', 'none', 'other'],
roles: ['alert', 'alertdialog', 'button', 'checkbox', 'dialog', 'gridcell', 'link', 'log', 'marquee', 'menuitem', 'menuitemcheckbox', 'menuitemradio', 'option', 'progressbar', 'radio', 'scrollbar', 'searchbox', 'slider',
'spinbutton', 'status', 'switch', 'tab', 'tabpanel', 'textbox', 'timer', 'tooltip', 'treeitem', 'combobox', 'grid', 'listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid',
'application', 'article', 'cell', 'columnheader', 'definition', 'directory', 'document', 'feed', 'figure', 'group', 'heading', 'img', 'list', 'listitem', 'math', 'none', 'note', 'presentation', 'region', 'row', 'rowgroup',
'rowheader', 'separator', 'table', 'term', 'text', 'toolbar',
'banner', 'complementary', 'contentinfo', 'form', 'main', 'navigation', 'region', 'search']
};
return {
getId: () => 'html5',
collectTags: (collector: (tag: string, label: string) => void) => collectTagsDefault(collector, HTML_TAGS),
collectAttributes: (tag: string, collector: (attribute: string, type: string) => void) => {
collectAttributesDefault(tag, collector, HTML_TAGS, globalAttributes);
eventHandlers.forEach(handler => {
collector(handler, 'event');
});
},
collectValues: (tag: string, attribute: string, collector: (value: string) => void) => collectValuesDefault(tag, attribute, collector, HTML_TAGS, globalAttributes, valueSets)
};
}
export function getAngularTagProvider(): IHTMLTagProvider {
var customTags: { [tag: string]: string[] } = {
input: ['ng-model', 'ng-required', 'ng-minlength', 'ng-maxlength', 'ng-pattern', 'ng-trim'],
select: ['ng-model'],
textarea: ['ng-model', 'ng-required', 'ng-minlength', 'ng-maxlength', 'ng-pattern', 'ng-trim']
};
var globalAttributes = ['ng-app', 'ng-bind', 'ng-bind-html', 'ng-bind-template', 'ng-blur', 'ng-change', 'ng-checked', 'ng-class', 'ng-class-even', 'ng-class-odd',
'ng-click', 'ng-cloak', 'ng-controller', 'ng-copy', 'ng-csp', 'ng-cut', 'ng-dblclick', 'ng-disabled', 'ng-focus', 'ng-form', 'ng-hide', 'ng-href', 'ng-if',
'ng-include', 'ng-init', 'ng-jq', 'ng-keydown', 'ng-keypress', 'ng-keyup', 'ng-list', 'ng-model-options', 'ng-mousedown', 'ng-mouseenter', 'ng-mouseleave',
'ng-mousemove', 'ng-mouseover', 'ng-mouseup', 'ng-non-bindable', 'ng-open', 'ng-options', 'ng-paste', 'ng-pluralize', 'ng-readonly', 'ng-repeat', 'ng-selected',
'ng-show', 'ng-src', 'ng-srcset', 'ng-style', 'ng-submit', 'ng-switch', 'ng-transclude', 'ng-value'
];
return {
getId: () => 'angular1',
collectTags: (collector: (tag: string) => void) => {
// no extra tags
},
collectAttributes: (tag: string, collector: (attribute: string, type: string) => void) => {
if (tag) {
var attributes = customTags[tag];
if (attributes) {
attributes.forEach((a) => {
collector(a, null);
collector('data-' + a, null);
});
}
}
globalAttributes.forEach((a) => {
collector(a, null);
collector('data-' + a, null);
});
},
collectValues: (tag: string, attribute: string, collector: (value: string) => void) => {
// no values
}
};
}
export function getIonicTagProvider(): IHTMLTagProvider {
var customTags: { [tag: string]: string[] } = {
a: ['nav-direction:navdir', 'nav-transition:trans'],
button: ['menu-toggle:menusides']
};
var globalAttributes = ['collection-repeat', 'force-refresh-images:b', 'ion-stop-event', 'item-height', 'item-render-buffer', 'item-width', 'menu-close:v',
'on-double-tap', 'on-drag', 'on-drag-down', 'on-drag-left', 'on-drag-right', 'on-drag-up', 'on-hold', 'on-release', 'on-swipe', 'on-swipe-down', 'on-swipe-left',
'on-swipe-right', 'on-swipe-up', 'on-tap', 'on-touch'];
var valueSets: IValueSets = {
align: ['center', 'left', 'right'],
b: ['true', 'false'],
inputtype: ['email', 'number', 'password', 'search', 'tel', 'text', 'url'],
listtype: ['card', 'list-inset'],
menusides: ['left', 'right'],
navdir: ['back', 'enter', 'exit', 'forward', 'swap'],
navsides: ['left', 'primary', 'right', 'secondary'],
scrolldir: ['x', 'xy', 'y'],
trans: ['android', 'ios', 'none']
};
return {
getId: () => 'ionic',
collectTags: (collector: (tag: string, label: string) => void) => collectTagsDefault(collector, IONIC_TAGS),
collectAttributes: (tag: string, collector: (attribute: string, type: string) => void) => {
collectAttributesDefault(tag, collector, IONIC_TAGS, globalAttributes);
if (tag) {
var attributes = customTags[tag];
if (attributes) {
attributes.forEach((a) => {
var segments = a.split(':');
collector(segments[0], segments[1]);
});
}
}
},
collectValues: (tag: string, attribute: string, collector: (value: string) => void) => collectValuesDefault(tag, attribute, collector, IONIC_TAGS, globalAttributes, valueSets, customTags)
};
}
function collectTagsDefault(collector: (tag: string, label: string) => void, tagSet: ITagSet): void {
for (var tag in tagSet) {
collector(tag, tagSet[tag].label);
}
}
function collectAttributesDefault(tag: string, collector: (attribute: string, type: string) => void, tagSet: ITagSet, globalAttributes: string[]): void {
globalAttributes.forEach(attr => {
var segments = attr.split(':');
collector(segments[0], segments[1]);
});
if (tag) {
var tags = tagSet[tag];
if (tags) {
var attributes = tags.attributes;
if (attributes) {
attributes.forEach(attr => {
var segments = attr.split(':');
collector(segments[0], segments[1]);
});
}
}
}
}
function collectValuesDefault(tag: string, attribute: string, collector: (value: string) => void, tagSet: ITagSet, globalAttributes: string[], valueSets: IValueSets, customTags?: { [tag: string]: string[] }): void {
var prefix = attribute + ':';
var processAttributes = (attributes: string[]) => {
attributes.forEach((attr) => {
if (attr.length > prefix.length && strings.startsWith(attr, prefix)) {
var typeInfo = attr.substr(prefix.length);
if (typeInfo === 'v') {
collector(attribute);
} else {
var values = valueSets[typeInfo];
if (values) {
values.forEach(collector);
}
}
}
});
};
if (tag) {
var tags = tagSet[tag];
if (tags) {
var attributes = tags.attributes;
if (attributes) {
processAttributes(attributes);
}
}
}
processAttributes(globalAttributes);
if (customTags) {
var customTagAttributes = customTags[tag];
if (customTagAttributes) {
processAttributes(customTagAttributes);
}
}
}
/*!
END THIRD PARTY
*/
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 strings = require('vs/base/common/strings');
export const DELIM_END = 'punctuation.definition.meta.tag.end.html';
export const DELIM_START = 'punctuation.definition.meta.tag.begin.html';
export const DELIM_ASSIGN = 'meta.tag.assign.html';
export const ATTRIB_NAME = 'entity.other.attribute-name.html';
export const ATTRIB_VALUE = 'string.html';
export const COMMENT = 'comment.html.content';
export const DELIM_COMMENT = 'comment.html';
export const DOCTYPE = 'entity.other.attribute-name.html';
export const DELIM_DOCTYPE = 'entity.name.tag.html';
const TAG_PREFIX = 'entity.name.tag.tag-';
export function isTag(tokenType: string) {
return strings.startsWith(tokenType, TAG_PREFIX);
}
export function getTag(name: string) {
return TAG_PREFIX + name;
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 URI from 'vs/base/common/uri';
import winjs = require('vs/base/common/winjs.base');
import beautifyHTML = require('vs/languages/lib/common/beautify-html');
import htmlTags = require('vs/languages/html/common/htmlTags');
import network = require('vs/base/common/network');
import editorCommon = require('vs/editor/common/editorCommon');
import modes = require('vs/editor/common/modes');
import strings = require('vs/base/common/strings');
import {IResourceService, ICompatMirrorModel} from 'vs/editor/common/services/resourceService';
import {getScanner, IHTMLScanner} from 'vs/languages/html/common/htmlScanner';
import {isTag, DELIM_END, DELIM_START, DELIM_ASSIGN, ATTRIB_NAME, ATTRIB_VALUE} from 'vs/languages/html/common/htmlTokenTypes';
import {isEmptyElement} from 'vs/languages/html/common/htmlEmptyTagsShared';
import {filterSuggestions} from 'vs/editor/common/modes/supports/suggestSupport';
import paths = require('vs/base/common/paths');
import {IHTMLConfiguration, IHTMLFormatConfiguration} from 'vs/languages/html/common/html.contribution';
import {LineTokens} from 'vs/editor/common/core/lineTokens';
enum LinkDetectionState {
LOOKING_FOR_HREF_OR_SRC = 1,
AFTER_HREF_OR_SRC = 2
}
interface IColorRange {
range:editorCommon.IRange;
value:string;
}
export class HTMLWorker {
private resourceService:IResourceService;
private _modeId: string;
private _tagProviders: htmlTags.IHTMLTagProvider[];
private _formatSettings: IHTMLFormatConfiguration;
private _providerConfiguration: {[providerId:string]:boolean};
constructor(
modeId: string,
@IResourceService resourceService: IResourceService
) {
this._modeId = modeId;
this.resourceService = resourceService;
this._tagProviders = [];
this._tagProviders.push(htmlTags.getHTML5TagProvider());
this.addCustomTagProviders(this._tagProviders);
this._providerConfiguration = null;
}
protected addCustomTagProviders(providers: htmlTags.IHTMLTagProvider[]): void {
providers.push(htmlTags.getAngularTagProvider());
providers.push(htmlTags.getIonicTagProvider());
}
private getTagProviders(): htmlTags.IHTMLTagProvider[] {
if (this._modeId !== 'html' || !this._providerConfiguration) {
return this._tagProviders;
}
return this._tagProviders.filter(p => !!this._providerConfiguration[p.getId()]);
}
public provideDocumentRangeFormattingEdits(resource: URI, range: editorCommon.IRange, options: modes.FormattingOptions): winjs.TPromise<editorCommon.ISingleEditOperation[]> {
return this.formatHTML(resource, range, options);
}
private formatHTML(resource: URI, range: editorCommon.IRange, options: modes.FormattingOptions): winjs.TPromise<editorCommon.ISingleEditOperation[]> {
let model = this.resourceService.get(resource);
let value = range ? model.getValueInRange(range) : model.getValue();
let htmlOptions : beautifyHTML.IBeautifyHTMLOptions = {
indent_size: options.insertSpaces ? options.tabSize : 1,
indent_char: options.insertSpaces ? ' ' : '\t',
wrap_line_length: this.getFormatOption('wrapLineLength', 120),
unformatted: this.getTagsFormatOption('unformatted', void 0),
indent_inner_html: this.getFormatOption('indentInnerHtml', false),
preserve_newlines: this.getFormatOption('preserveNewLines', false),
max_preserve_newlines: this.getFormatOption('maxPreserveNewLines', void 0),
indent_handlebars: this.getFormatOption('indentHandlebars', false),
end_with_newline: this.getFormatOption('endWithNewline', false),
extra_liners: this.getTagsFormatOption('extraLiners', void 0),
};
let result = beautifyHTML.html_beautify(value, htmlOptions);
return winjs.TPromise.as([{
range: range,
text: result
}]);
}
private getFormatOption(key: string, dflt: any): any {
if (this._formatSettings && this._formatSettings.hasOwnProperty(key)) {
let value = this._formatSettings[key];
if (value !== null) {
return value;
}
}
return dflt;
}
private getTagsFormatOption(key: string, dflt: string[]): string[] {
let list = <string> this.getFormatOption(key, null);
if (typeof list === 'string') {
if (list.length > 0) {
return list.split(',').map(t => t.trim().toLowerCase());
}
return [];
}
return dflt;
}
_doConfigure(options: IHTMLConfiguration): winjs.TPromise<void> {
this._formatSettings = options && options.format;
if (options && options.suggest) {
this._providerConfiguration = options.suggest;
}
return winjs.TPromise.as(null);
}
private findMatchingOpenTag(scanner: IHTMLScanner) : string {
let closedTags : { [name:string]: number } = {};
let tagClosed = false;
while (scanner.scanBack()) {
if (isTag(scanner.getTokenType()) && !tagClosed) {
let tag = scanner.getTokenContent();
scanner.scanBack();
if (scanner.getTokenType() === DELIM_END) {
closedTags[tag] = (closedTags[tag] || 0) + 1;
} else if (!isEmptyElement(tag)) {
if (closedTags[tag]) {
closedTags[tag]--;
} else {
return tag;
}
}
} else if (scanner.getTokenType() === DELIM_START) {
tagClosed = scanner.getTokenContent() === '/>';
}
}
return null;
}
private collectTagSuggestions(scanner: IHTMLScanner, position: editorCommon.IPosition, suggestions: modes.ISuggestResult): void {
let model = scanner.getModel();
let currentLine = model.getLineContent(position.lineNumber);
let contentAfter = currentLine.substr(position.column - 1);
let closeTag = isWhiteSpace(contentAfter) || strings.startsWith(contentAfter, '<') ? '>' : '';
let collectClosingTagSuggestion = (overwriteBefore: number) => {
let endPosition = scanner.getTokenPosition();
let matchingTag = this.findMatchingOpenTag(scanner);
if (matchingTag) {
let suggestion : modes.ISuggestion = {
label: '/' + matchingTag,
insertText: '/' + matchingTag + closeTag,
overwriteBefore: overwriteBefore,
type: 'property'
};
suggestions.suggestions.push(suggestion);
// use indent from start tag
let startPosition = scanner.getTokenPosition();
if (endPosition.lineNumber !== startPosition.lineNumber) {
let startIndent = model.getLineContent(startPosition.lineNumber).substring(0, startPosition.column - 1);
let endIndent = model.getLineContent(endPosition.lineNumber).substring(0, endPosition.column - 1);
if (isWhiteSpace(startIndent) && isWhiteSpace(endIndent)) {
suggestion.overwriteBefore = position.column - 1; // replace from start of line
suggestion.insertText = startIndent + '</' + matchingTag + closeTag;
suggestion.filterText = endIndent + '</' + matchingTag + closeTag;
}
}
return true;
}
return false;
};
if (scanner.getTokenType() === DELIM_END && scanner.getTokenRange().endColumn === position.column) {
let hasClose = collectClosingTagSuggestion(suggestions.currentWord.length + 1);
if (!hasClose) {
this.getTagProviders().forEach((provider) => {
provider.collectTags((tag, label) => {
suggestions.suggestions.push({
label: '/' + tag,
overwriteBefore: suggestions.currentWord.length + 1,
insertText: '/' + tag + closeTag,
type: 'property',
documentation: label,
filterText: '</' + tag + closeTag
});
});
});
}
} else {
collectClosingTagSuggestion(suggestions.currentWord.length);
this.getTagProviders().forEach((provider) => {
provider.collectTags((tag, label) => {
suggestions.suggestions.push({
label: tag,
insertText: tag,
type: 'property',
documentation: label,
overwriteBefore: suggestions.currentWord.length
});
});
});
}
}
private collectContentSuggestions(suggestions: modes.ISuggestResult): void {
// disable the simple snippets in favor of the emmet templates
}
private collectAttributeSuggestions(scanner: IHTMLScanner, suggestions: modes.ISuggestResult): void {
let parentTag: string = null;
do {
if (isTag(scanner.getTokenType())) {
parentTag = scanner.getTokenContent();
break;
}
if (scanner.getTokenType() === DELIM_START) {
break;
}
} while (scanner.scanBack());
this.getTagProviders().forEach((provider) => {
provider.collectAttributes(parentTag,(attribute, type) => {
let codeSnippet = attribute;
if (type !== 'v') {
codeSnippet = codeSnippet + '="{{}}"';
}
suggestions.suggestions.push({
label: attribute,
insertText: codeSnippet,
type: type === 'handler' ? 'function' : 'value',
overwriteBefore: suggestions.currentWord.length
});
});
});
}
private collectAttributeValueSuggestions(scanner: IHTMLScanner, suggestions: modes.ISuggestResult): void {
let needsQuotes = scanner.getTokenType() === DELIM_ASSIGN;
let attribute: string = null;
let parentTag: string = null;
while (scanner.scanBack()) {
if (scanner.getTokenType() === ATTRIB_NAME) {
attribute = scanner.getTokenContent();
break;
}
}
while (scanner.scanBack()) {
if (isTag(scanner.getTokenType())) {
parentTag = scanner.getTokenContent();
break;
}
if (scanner.getTokenType() === DELIM_START) {
return;
}
}
this.getTagProviders().forEach((provider) => {
provider.collectValues(parentTag, attribute,(value) => {
suggestions.suggestions.push({
label: value,
insertText: needsQuotes ? '"' + value + '"' : value,
type: 'unit',
overwriteBefore: suggestions.currentWord.length
});
});
});
}
public provideCompletionItems(resource:URI, position:editorCommon.IPosition):winjs.TPromise<modes.ISuggestResult> {
let model = this.resourceService.get(resource);
let modeIdAtPosition = model.getModeIdAtPosition(position.lineNumber, position.column);
if (modeIdAtPosition === this._modeId) {
return this.suggestHTML(resource, position);
}
}
private suggestHTML(resource:URI, position:editorCommon.IPosition):winjs.TPromise<modes.ISuggestResult> {
return this.doSuggest(resource, position).then(value => filterSuggestions(value));
}
private doSuggest(resource: URI, position: editorCommon.IPosition): winjs.TPromise<modes.ISuggestResult> {
let model = this.resourceService.get(resource),
currentWord = model.getWordUntilPosition(position).word;
let suggestions: modes.ISuggestResult = {
currentWord: currentWord,
suggestions: [],
};
let scanner = getScanner(model, position);
switch (scanner.getTokenType()) {
case DELIM_START:
case DELIM_END:
if (scanner.isOpenBrace()) {
this.collectTagSuggestions(scanner, position, suggestions);
} else {
this.collectContentSuggestions(suggestions);
}
break;
case ATTRIB_NAME:
this.collectAttributeSuggestions(scanner, suggestions);
break;
case ATTRIB_VALUE:
this.collectAttributeValueSuggestions(scanner, suggestions);
break;
case DELIM_ASSIGN:
if (scanner.isAtTokenEnd()) {
this.collectAttributeValueSuggestions(scanner, suggestions);
}
break;
case '':
if (isWhiteSpace(scanner.getTokenContent()) && scanner.scanBack()) { // go one back
switch (scanner.getTokenType()) {
case ATTRIB_VALUE:
case ATTRIB_NAME:
this.collectAttributeSuggestions(scanner, suggestions);
break;
case DELIM_ASSIGN:
this.collectAttributeValueSuggestions(scanner, suggestions);
break;
case DELIM_START:
case DELIM_END:
if (scanner.isOpenBrace()) {
this.collectTagSuggestions(scanner, position, suggestions);
} else {
this.collectContentSuggestions(suggestions);
}
break;
default:
if (isTag(scanner.getTokenType())) {
this.collectAttributeSuggestions(scanner, suggestions);
}
}
} else {
this.collectContentSuggestions(suggestions);
}
break;
default:
if (isTag(scanner.getTokenType())) {
scanner.scanBack(); // one back to the end/start bracket
this.collectTagSuggestions(scanner, position, suggestions);
}
}
return winjs.TPromise.as(suggestions);
}
private findMatchingBracket(tagname: string, scanner: IHTMLScanner) : editorCommon.IRange {
if (isEmptyElement(tagname)) {
return null;
}
let tagCount = 0;
scanner.scanBack(); // one back to the end/start bracket
if (scanner.getTokenType() === DELIM_END) {
// find the opening tag
let tagClosed = false;
while (scanner.scanBack()) {
if (isTag(scanner.getTokenType()) && scanner.getTokenContent() === tagname && !tagClosed) {
let range = scanner.getTokenRange();
scanner.scanBack(); // one back to the end/start bracket
if (scanner.getTokenType() === DELIM_START) {
if (tagCount === 0) {
return range;
} else {
tagCount--;
}
} else {
tagCount++;
}
} else if (scanner.getTokenType() === DELIM_START) {
tagClosed = scanner.getTokenContent() === '/>';
}
}
} else {
let isTagEnd = false;
while (scanner.scanForward()) {
if (isTag(scanner.getTokenType()) && scanner.getTokenContent() === tagname) {
if (!isTagEnd) {
scanner.scanForward();
if (scanner.getTokenType() === DELIM_START && scanner.getTokenContent() === '/>') {
if (tagCount <= 0) {
return null;
}
} else {
tagCount++;
}
} else {
tagCount--;
if (tagCount <= 0) {
return scanner.getTokenRange();
}
}
} else if (scanner.getTokenType() === DELIM_START) {
isTagEnd = false;
} else if (scanner.getTokenType() === DELIM_END) {
isTagEnd = true;
}
}
}
return null;
}
public provideDocumentHighlights(resource:URI, position:editorCommon.IPosition, strict:boolean = false): winjs.TPromise<modes.DocumentHighlight[]> {
let model = this.resourceService.get(resource),
wordAtPosition = model.getWordAtPosition(position),
result:modes.DocumentHighlight[] = [];
let scanner = getScanner(model, position);
if (isTag(scanner.getTokenType())) {
let tagname = scanner.getTokenContent();
result.push({
range: scanner.getTokenRange(),
kind: modes.DocumentHighlightKind.Read
});
let range = this.findMatchingBracket(tagname, scanner);
if (range) {
result.push({
range: range,
kind: modes.DocumentHighlightKind.Read
});
}
} else {
if (wordAtPosition) {
let results = model.findMatches(wordAtPosition.word, false, false, true, true);
for (let i = 0, len = results.length; i < len; i++) {
result.push({
range: results[i],
kind: modes.DocumentHighlightKind.Read
});
}
}
}
return winjs.TPromise.as(result);
}
private static _stripQuotes(url: string): string {
return url
.replace(/^'([^']+)'$/,(substr, match1) => match1)
.replace(/^"([^"]+)"$/,(substr, match1) => match1);
}
public static _getWorkspaceUrl(modelAbsoluteUri: URI, rootAbsoluteUri: URI, tokenContent: string): string {
tokenContent = HTMLWorker._stripQuotes(tokenContent);
if (/^\s*javascript\:/i.test(tokenContent) || /^\s*\#/i.test(tokenContent)) {
return null;
}
if (/^\s*https?:\/\//i.test(tokenContent) || /^\s*file:\/\//i.test(tokenContent)) {
// Absolute link that needs no treatment
return tokenContent.replace(/^\s*/g, '');
}
if (/^\s*\/\//i.test(tokenContent)) {
// Absolute link (that does not name the protocol)
let pickedScheme = network.Schemas.http;
if (modelAbsoluteUri.scheme === network.Schemas.https) {
pickedScheme = network.Schemas.https;
}
return pickedScheme + ':' + tokenContent.replace(/^\s*/g, '');
}
let modelPath = paths.dirname(modelAbsoluteUri.path);
let alternativeResultPath: string = null;
if (tokenContent.length > 0 && tokenContent.charAt(0) === '/') {
alternativeResultPath = tokenContent;
} else {
alternativeResultPath = paths.join(modelPath, tokenContent);
alternativeResultPath = alternativeResultPath.replace(/^(\/\.\.)+/, '');
}
let potentialResult = modelAbsoluteUri.with({ path: alternativeResultPath }).toString();
let rootAbsoluteUrlStr = (rootAbsoluteUri ? rootAbsoluteUri.toString() : null);
if (rootAbsoluteUrlStr && strings.startsWith(modelAbsoluteUri.toString(), rootAbsoluteUrlStr)) {
// The `rootAbsoluteUrl` is set and matches our current model
// We need to ensure that this `potentialResult` does not escape `rootAbsoluteUrl`
let commonPrefixLength = strings.commonPrefixLength(rootAbsoluteUrlStr, potentialResult);
if (strings.endsWith(rootAbsoluteUrlStr, '/')) {
commonPrefixLength = potentialResult.lastIndexOf('/', commonPrefixLength) + 1;
}
return rootAbsoluteUrlStr + potentialResult.substr(commonPrefixLength);
}
return potentialResult;
}
private createLink(modelAbsoluteUrl: URI, rootAbsoluteUrl: URI, tokenContent: string, lineNumber: number, startColumn: number, endColumn: number): modes.ILink {
let workspaceUrl = HTMLWorker._getWorkspaceUrl(modelAbsoluteUrl, rootAbsoluteUrl, tokenContent);
if (!workspaceUrl) {
return null;
}
// console.info('workspaceUrl: ' + workspaceUrl);
return {
range: {
startLineNumber: lineNumber,
startColumn: startColumn,
endLineNumber: lineNumber,
endColumn: endColumn
},
url: workspaceUrl
};
}
private _computeHTMLLinks(model: ICompatMirrorModel, modelAbsoluteUrl:URI, workspaceResource:URI): modes.ILink[] {
let lineCount = model.getLineCount(),
newLinks: modes.ILink[] = [],
state: LinkDetectionState = LinkDetectionState.LOOKING_FOR_HREF_OR_SRC,
lineNumber: number,
lineContent: string,
lineContentLength: number,
tokens: LineTokens,
tokenType: string,
tokensLength: number,
i: number,
nextTokenEndIndex: number,
tokenContent: string,
link: modes.ILink;
let rootAbsoluteUrl: URI = null;
if (workspaceResource) {
// The workspace can be null in the no folder opened case
let strRootAbsoluteUrl = String(workspaceResource);
if (strRootAbsoluteUrl.charAt(strRootAbsoluteUrl.length - 1) === '/') {
rootAbsoluteUrl = URI.parse(strRootAbsoluteUrl);
} else {
rootAbsoluteUrl = URI.parse(strRootAbsoluteUrl + '/');
}
}
for (lineNumber = 1; lineNumber <= lineCount; lineNumber++) {
lineContent = model.getLineContent(lineNumber);
lineContentLength = lineContent.length;
tokens = model.getLineTokens(lineNumber);
for (i = 0, tokensLength = tokens.getTokenCount(); i < tokensLength; i++) {
tokenType = tokens.getTokenType(i);
switch (tokenType) {
case DELIM_ASSIGN:
case '':
break;
case ATTRIB_NAME:
nextTokenEndIndex = tokens.getTokenEndOffset(i);
tokenContent = lineContent.substring(tokens.getTokenStartOffset(i), nextTokenEndIndex).toLowerCase();
if (tokenContent === 'src' || tokenContent === 'href') {
state = LinkDetectionState.AFTER_HREF_OR_SRC;
} else {
state = LinkDetectionState.LOOKING_FOR_HREF_OR_SRC;
}
break;
case ATTRIB_VALUE:
if (state === LinkDetectionState.AFTER_HREF_OR_SRC) {
nextTokenEndIndex = tokens.getTokenEndOffset(i);
tokenContent = lineContent.substring(tokens.getTokenStartOffset(i), nextTokenEndIndex);
link = this.createLink(modelAbsoluteUrl, rootAbsoluteUrl, tokenContent, lineNumber, tokens.getTokenStartOffset(i) + 2, nextTokenEndIndex);
if (link) {
newLinks.push(link);
}
state = LinkDetectionState.LOOKING_FOR_HREF_OR_SRC;
}
default:
if (isTag(tokenType)) {
state = LinkDetectionState.LOOKING_FOR_HREF_OR_SRC;
} else if (state === LinkDetectionState.AFTER_HREF_OR_SRC) {
state = LinkDetectionState.LOOKING_FOR_HREF_OR_SRC;
}
}
}
}
return newLinks;
}
public provideLinks(resource: URI, workspaceResource:URI): winjs.TPromise<modes.ILink[]> {
let model = this.resourceService.get(resource);
return winjs.TPromise.as(this._computeHTMLLinks(model, resource, workspaceResource));
}
}
function isWhiteSpace(s:string) : boolean {
return /^\s*$/.test(s);
}
/*---------------------------------------------------------------------------------------------
* 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 assert = require('assert');
import mm = require('vs/editor/common/model/compatMirrorModel');
import htmlWorker = require('vs/languages/html/common/htmlWorker');
import URI from 'vs/base/common/uri';
import ResourceService = require('vs/editor/common/services/resourceServiceImpl');
import Modes = require('vs/editor/common/modes');
import WinJS = require('vs/base/common/winjs.base');
import {HTMLMode} from 'vs/languages/html/common/html';
import {MockModeService} from 'vs/editor/test/common/mocks/mockModeService';
import {TextModel} from 'vs/editor/common/model/textModel';
function createTestMirrorModelFromString(value:string, mode:Modes.IMode, associatedResource:URI): mm.CompatMirrorModel {
return new mm.CompatMirrorModel(0, TextModel.toRawText(value, TextModel.DEFAULT_CREATION_OPTIONS), mode.getId(), associatedResource);
}
suite('HTML - worker', () => {
var mode: Modes.IMode;
(function() {
mode = new HTMLMode<htmlWorker.HTMLWorker>(
{ id: 'html' },
null,
new MockModeService(),
null,
null,
null
);
})();
var mockHtmlWorkerEnv = function (url: URI, content: string): { worker: htmlWorker.HTMLWorker; model: mm.CompatMirrorModel; } {
var resourceService = new ResourceService.ResourceService();
var model = createTestMirrorModelFromString(content, mode, url);
resourceService.insert(url, model);
var worker = new htmlWorker.HTMLWorker(mode.getId(), resourceService);
return { worker: worker, model: model };
};
var testSuggestionsFor = function(value:string):WinJS.TPromise<Modes.ISuggestResult> {
var idx = value.indexOf('|');
var content = value.substr(0, idx) + value.substr(idx + 1);
var url = URI.parse('test://1');
var env = mockHtmlWorkerEnv(url, content);
var position = env.model.getPositionAt(idx);
return env.worker.provideCompletionItems(url, position);
};
var assertSuggestion = function(completion: Modes.ISuggestResult, label: string, type?: string, codeSnippet?: string) {
var proposalsFound = completion.suggestions.filter(function(suggestion: Modes.ISuggestion) {
return suggestion.label === label && (!type || suggestion.type === type) && (!codeSnippet || suggestion.insertText === codeSnippet);
});
if (proposalsFound.length !== 1) {
assert.fail('Suggestion not found: ' + label + ', has ' + completion.suggestions.map(s => s.label).join(', '));
}
};
test('Intellisense', function(testDone): any {
WinJS.Promise.join([
testSuggestionsFor('<|').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'iframe');
assertSuggestion(completion, 'h1');
assertSuggestion(completion, 'div');
}),
testSuggestionsFor('< |').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'iframe');
assertSuggestion(completion, 'h1');
assertSuggestion(completion, 'div');
}),
testSuggestionsFor('<h|').then((completion) => {
assert.equal(completion.currentWord, 'h');
assertSuggestion(completion, 'html');
assertSuggestion(completion, 'h1');
assertSuggestion(completion, 'header');
}),
testSuggestionsFor('<input|').then((completion) => {
assert.equal(completion.currentWord, 'input');
assertSuggestion(completion, 'input');
}),
testSuggestionsFor('<input |').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'type');
assertSuggestion(completion, 'style');
assertSuggestion(completion, 'onmousemove');
}),
testSuggestionsFor('<input t|').then((completion) => {
assert.equal(completion.currentWord, 't');
assertSuggestion(completion, 'type');
assertSuggestion(completion, 'tabindex');
}),
testSuggestionsFor('<input type="text" |').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'style');
assertSuggestion(completion, 'type');
assertSuggestion(completion, 'size');
}),
testSuggestionsFor('<input type="text" s|').then((completion) => {
assert.equal(completion.currentWord, 's');
assertSuggestion(completion, 'style');
assertSuggestion(completion, 'src');
assertSuggestion(completion, 'size');
}),
testSuggestionsFor('<input di| type="text"').then((completion) => {
assert.equal(completion.currentWord, 'di');
assertSuggestion(completion, 'disabled', null, 'disabled');
assertSuggestion(completion, 'dir', null, 'dir="{{}}"');
}),
testSuggestionsFor('<input disabled | type="text"').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'dir', null, 'dir="{{}}"');
assertSuggestion(completion, 'style');
}),
testSuggestionsFor('<input type=|').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'text', null, '"text"');
assertSuggestion(completion, 'checkbox', null, '"checkbox"');
}),
testSuggestionsFor('<input type="c|').then((completion) => {
assert.equal(completion.currentWord, 'c');
assertSuggestion(completion, 'color', null, 'color');
assertSuggestion(completion, 'checkbox', null, 'checkbox');
}),
testSuggestionsFor('<input type= |').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'color', null, '"color"');
assertSuggestion(completion, 'checkbox', null, '"checkbox"');
}),
testSuggestionsFor('<input src="c" type="color|" ').then((completion) => {
assert.equal(completion.currentWord, 'color');
assertSuggestion(completion, 'color', null, 'color');
}),
testSuggestionsFor('<input src="c" type=color| ').then((completion) => {
assert.equal(completion.currentWord, 'color');
assertSuggestion(completion, 'color', null, 'color');
}),
testSuggestionsFor('<div dir=|></div>').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'ltr', null, '"ltr"');
assertSuggestion(completion, 'rtl', null, '"rtl"');
}),
testSuggestionsFor('<ul><|>').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/ul', null, '/ul');
assertSuggestion(completion, 'li', null, 'li');
}),
testSuggestionsFor('<ul><li><|').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/li', null, '/li>');
assertSuggestion(completion, 'a', null, 'a');
}),
testSuggestionsFor('<table></|>').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/table', null, '/table');
}),
testSuggestionsFor('<foo></f|').then((completion) => {
assert.equal(completion.currentWord, 'f');
assertSuggestion(completion, '/foo', null, '/foo>');
}),
testSuggestionsFor('<ul></ |>').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/ul', null, '/ul');
}),
testSuggestionsFor('<span></ |').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/span', null, '/span>');
}),
testSuggestionsFor('<li><br></ |>').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/li', null, '/li');
}),
testSuggestionsFor('<li><br/></ |>').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/li', null, '/li');
}),
testSuggestionsFor('<li><div/></|').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/li', null, '/li>');
}),
testSuggestionsFor('<li><br/|>').then((completion) => {
assert.equal(completion.currentWord, '');
assert.equal(completion.suggestions.length, 0);
}),
testSuggestionsFor('<li><br>a/|').then((completion) => {
assert.equal(completion.currentWord, '');
assert.equal(completion.suggestions.length, 0);
}),
testSuggestionsFor('<a><div></div></| ').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/a', null, '/a>');
}),
testSuggestionsFor('<body><div><div></div></div></| >').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/body', null, '/body');
}),
testSuggestionsFor(['<body>', ' <div>', ' </|'].join('\n')).then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, '/div', null, ' </div>');
})
]).done(() => testDone(), (errors:any[]) => {
testDone(errors.reduce((e1, e2) => e1 || e2));
});
});
test('Intellisense aria', function(testDone): any {
function assertAriaAttributes(completion) {
assertSuggestion(completion, 'aria-activedescendant');
assertSuggestion(completion, 'aria-atomic');
assertSuggestion(completion, 'aria-autocomplete');
assertSuggestion(completion, 'aria-busy');
assertSuggestion(completion, 'aria-checked');
assertSuggestion(completion, 'aria-colcount');
assertSuggestion(completion, 'aria-colindex');
assertSuggestion(completion, 'aria-colspan');
assertSuggestion(completion, 'aria-controls');
assertSuggestion(completion, 'aria-current');
assertSuggestion(completion, 'aria-describedat');
assertSuggestion(completion, 'aria-describedby');
assertSuggestion(completion, 'aria-disabled');
assertSuggestion(completion, 'aria-dropeffect');
assertSuggestion(completion, 'aria-errormessage');
assertSuggestion(completion, 'aria-expanded');
assertSuggestion(completion, 'aria-flowto');
assertSuggestion(completion, 'aria-grabbed');
assertSuggestion(completion, 'aria-haspopup');
assertSuggestion(completion, 'aria-hidden');
assertSuggestion(completion, 'aria-invalid');
assertSuggestion(completion, 'aria-kbdshortcuts');
assertSuggestion(completion, 'aria-label');
assertSuggestion(completion, 'aria-labelledby');
assertSuggestion(completion, 'aria-level');
assertSuggestion(completion, 'aria-live');
assertSuggestion(completion, 'aria-modal');
assertSuggestion(completion, 'aria-multiline');
assertSuggestion(completion, 'aria-multiselectable');
assertSuggestion(completion, 'aria-orientation');
assertSuggestion(completion, 'aria-owns');
assertSuggestion(completion, 'aria-placeholder');
assertSuggestion(completion, 'aria-posinset');
assertSuggestion(completion, 'aria-pressed');
assertSuggestion(completion, 'aria-readonly');
assertSuggestion(completion, 'aria-relevant');
assertSuggestion(completion, 'aria-required');
assertSuggestion(completion, 'aria-roledescription');
assertSuggestion(completion, 'aria-rowcount');
assertSuggestion(completion, 'aria-rowindex');
assertSuggestion(completion, 'aria-rowspan');
assertSuggestion(completion, 'aria-selected');
assertSuggestion(completion, 'aria-setsize');
assertSuggestion(completion, 'aria-sort');
assertSuggestion(completion, 'aria-valuemax');
assertSuggestion(completion, 'aria-valuemin');
assertSuggestion(completion, 'aria-valuenow');
assertSuggestion(completion, 'aria-valuetext');
}
WinJS.Promise.join([
testSuggestionsFor('<div |> </div >').then((completion) => {
assert.equal(completion.currentWord, '');
assertAriaAttributes(completion);
}),
testSuggestionsFor('<span |> </span >').then((completion) => {
assert.equal(completion.currentWord, '');
assertAriaAttributes(completion);
}),
testSuggestionsFor('<input |> </input >').then((completion) => {
assert.equal(completion.currentWord, '');
assertAriaAttributes(completion);
})
]).done(() => testDone(), (errors:any[]) => {
testDone(errors.reduce((e1, e2) => e1 || e2));
});
});
test('Intellisense Angular', function(testDone): any {
WinJS.Promise.join([
testSuggestionsFor('<body |> </body >').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'ng-controller');
assertSuggestion(completion, 'data-ng-controller');
}),
testSuggestionsFor('<li |> </li >').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'ng-repeat');
assertSuggestion(completion, 'data-ng-repeat');
}),
testSuggestionsFor('<input |> </input >').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'ng-model');
assertSuggestion(completion, 'data-ng-model');
})
]).done(() => testDone(), (errors:any[]) => {
testDone(errors.reduce((e1, e2) => e1 || e2));
});
});
test('Intellisense Ionic', function(testDone): any {
WinJS.Promise.join([
// Try some Ionic tags
testSuggestionsFor('<|').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'ion-checkbox');
assertSuggestion(completion, 'ion-content');
assertSuggestion(completion, 'ion-nav-title');
}),
testSuggestionsFor('<ion-re|').then((completion) => {
assert.equal(completion.currentWord, 'ion-re');
assertSuggestion(completion, 'ion-refresher');
assertSuggestion(completion, 'ion-reorder-button');
}),
// Try some global attributes (1 with value suggestions, 1 without value suggestions, 1 void)
testSuggestionsFor('<ion-checkbox |').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'force-refresh-images');
assertSuggestion(completion, 'collection-repeat');
assertSuggestion(completion, 'menu-close');
}),
// Try some tag-specific attributes (1 with value suggestions, 1 void)
testSuggestionsFor('<ion-footer-bar |').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'align-title');
assertSuggestion(completion, 'keyboard-attach');
}),
// Try the extended attributes of an existing HTML 5 tag
testSuggestionsFor('<a |').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'nav-direction');
assertSuggestion(completion, 'nav-transition');
assertSuggestion(completion, 'href');
assertSuggestion(completion, 'hreflang');
}),
// Try value suggestion for a tag-specific attribute
testSuggestionsFor('<ion-side-menu side="|').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'left');
assertSuggestion(completion, 'primary');
assertSuggestion(completion, 'right');
assertSuggestion(completion, 'secondary');
}),
// Try a value suggestion for a global attribute
testSuggestionsFor('<img force-refresh-images="|').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'false');
assertSuggestion(completion, 'true');
}),
// Try a value suggestion for an extended attribute of an existing HTML 5 tag
testSuggestionsFor('<a nav-transition="|').then((completion) => {
assert.equal(completion.currentWord, '');
assertSuggestion(completion, 'android');
assertSuggestion(completion, 'ios');
assertSuggestion(completion, 'none');
})
]).done(() => testDone(), (errors:any[]) => {
testDone(errors.reduce((e1, e2) => e1 || e2));
});
});
function testLinkCreation(modelUrl:string, rootUrl:string, tokenContent:string, expected:string): void {
var _modelUrl = URI.parse(modelUrl);
var _rootUrl = rootUrl === null ? null : URI.parse(rootUrl);
var actual = htmlWorker.HTMLWorker._getWorkspaceUrl(_modelUrl, _rootUrl, tokenContent);
var _actual = actual === null ? null : URI.parse(actual);
var _expected = expected === null ? null : URI.parse(expected);
assert.equal(String(_actual), String(_expected));
}
test('Link creation', () => {
testLinkCreation('inmemory://model/1', null, 'javascript:void;', null);
testLinkCreation('inmemory://model/1', null, ' \tjavascript:alert(7);', null);
testLinkCreation('inmemory://model/1', null, ' #relative', null);
testLinkCreation('inmemory://model/1', null, 'file:///C:\\Alex\\src\\path\\to\\file.txt', 'file:///C:\\Alex\\src\\path\\to\\file.txt');
testLinkCreation('inmemory://model/1', null, 'http://www.microsoft.com/', 'http://www.microsoft.com/');
testLinkCreation('inmemory://model/1', null, 'https://www.microsoft.com/', 'https://www.microsoft.com/');
testLinkCreation('inmemory://model/1', null, '//www.microsoft.com/', 'http://www.microsoft.com/');
testLinkCreation('inmemory://model/1', null, '../../a.js', 'inmemory://model/a.js');
testLinkCreation('inmemory://model/1', 'inmemory://model/', 'javascript:void;', null);
testLinkCreation('inmemory://model/1', 'inmemory://model/', ' \tjavascript:alert(7);', null);
testLinkCreation('inmemory://model/1', 'inmemory://model/', ' #relative', null);
testLinkCreation('inmemory://model/1', 'inmemory://model/', 'file:///C:\\Alex\\src\\path\\to\\file.txt', 'file:///C:\\Alex\\src\\path\\to\\file.txt');
testLinkCreation('inmemory://model/1', 'inmemory://model/', 'http://www.microsoft.com/', 'http://www.microsoft.com/');
testLinkCreation('inmemory://model/1', 'inmemory://model/', 'https://www.microsoft.com/', 'https://www.microsoft.com/');
testLinkCreation('inmemory://model/1', 'inmemory://model/', ' //www.microsoft.com/', 'http://www.microsoft.com/');
testLinkCreation('inmemory://model/1', 'inmemory://model/', '../../a.js', 'inmemory://model/a.js');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, 'javascript:void;', null);
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, ' \tjavascript:alert(7);', null);
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, ' #relative', null);
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, 'file:///C:\\Alex\\src\\path\\to\\file.txt', 'file:///C:\\Alex\\src\\path\\to\\file.txt');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, 'http://www.microsoft.com/', 'http://www.microsoft.com/');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, 'https://www.microsoft.com/', 'https://www.microsoft.com/');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, ' //www.microsoft.com/', 'http://www.microsoft.com/');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, 'a.js', 'file:///C:/Alex/src/path/to/a.js');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, '/a.js', 'file:///a.js');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', 'javascript:void;', null);
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', ' \tjavascript:alert(7);', null);
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', ' #relative', null);
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', null, 'file:///C:\\Alex\\src\\path\\to\\file.txt', 'file:///C:\\Alex\\src\\path\\to\\file.txt');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', 'http://www.microsoft.com/', 'http://www.microsoft.com/');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', 'https://www.microsoft.com/', 'https://www.microsoft.com/');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', 'https://www.microsoft.com/?q=1#h', 'https://www.microsoft.com/?q=1#h');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', ' //www.microsoft.com/', 'http://www.microsoft.com/');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', 'a.js', 'file:///C:/Alex/src/path/to/a.js');
testLinkCreation('file:///C:/Alex/src/path/to/file.txt', 'file:///C:/Alex/src/', '/a.js', 'file:///C:/Alex/src/a.js');
testLinkCreation('https://www.test.com/path/to/file.txt', null, 'file:///C:\\Alex\\src\\path\\to\\file.txt', 'file:///C:\\Alex\\src\\path\\to\\file.txt');
testLinkCreation('https://www.test.com/path/to/file.txt', null, '//www.microsoft.com/', 'https://www.microsoft.com/');
testLinkCreation('https://www.test.com/path/to/file.txt', 'https://www.test.com', '//www.microsoft.com/', 'https://www.microsoft.com/');
// invalid uris don't throw
testLinkCreation('https://www.test.com/path/to/file.txt', 'https://www.test.com', '%', 'https://www.test.com/path/to/%25');
// Bug #18314: Ctrl + Click does not open existing file if folder's name starts with 'c' character
testLinkCreation('file:///c:/Alex/working_dir/18314-link-detection/test.html', 'file:///c:/Alex/working_dir/18314-link-detection/', '/class/class.js', 'file:///c:/Alex/working_dir/18314-link-detection/class/class.js');
});
});
/*---------------------------------------------------------------------------------------------
* 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 assert = require('assert');
import Modes = require('vs/editor/common/modes');
import modesUtil = require('vs/editor/test/common/modesUtil');
import {Model} from 'vs/editor/common/model/model';
import {getTag, DELIM_END, DELIM_START, DELIM_ASSIGN, ATTRIB_NAME, ATTRIB_VALUE, COMMENT, DELIM_COMMENT, DELIM_DOCTYPE, DOCTYPE} from 'vs/languages/html/common/htmlTokenTypes';
import {TextModelWithTokens} from 'vs/editor/common/model/textModelWithTokens';
import {TextModel} from 'vs/editor/common/model/textModel';
import {Range} from 'vs/editor/common/core/range';
import {MockModeService} from 'vs/editor/test/common/mocks/mockModeService';
import {HTMLMode} from 'vs/languages/html/common/html';
import htmlWorker = require('vs/languages/html/common/htmlWorker');
import {MockTokenizingMode} from 'vs/editor/test/common/mocks/mockMode';
import {LanguageConfigurationRegistry} from 'vs/editor/common/modes/languageConfigurationRegistry';
class MockJSMode extends MockTokenizingMode {
constructor() {
super('mock-js');
LanguageConfigurationRegistry.register(this.getId(), {
brackets: [
['(', ')'],
['{', '}'],
['[', ']']
],
onEnterRules: [
{
// e.g. /** | */
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
afterText: /^\s*\*\/$/,
action: { indentAction: Modes.IndentAction.IndentOutdent, appendText: ' * ' }
},
{
// e.g. /** ...|
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
action: { indentAction: Modes.IndentAction.None, appendText: ' * ' }
},
{
// e.g. * ...|
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: { indentAction: Modes.IndentAction.None, appendText: '* ' }
},
{
// e.g. */|
beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
action: { indentAction: Modes.IndentAction.None, removeText: 1 }
},
{
// e.g. *-----*/|
beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/,
action: { indentAction: Modes.IndentAction.None, removeText: 1 }
}
]
});
}
}
class HTMLMockModeService extends MockModeService {
private _mockJSMode = new MockJSMode();
isRegisteredMode(mimetypeOrModeId: string): boolean {
if (mimetypeOrModeId === 'text/javascript') {
return true;
}
if (mimetypeOrModeId === 'text/plain') {
return false;
}
throw new Error('Not implemented');
}
getModeId(mimetypeOrModeId: string): string {
if (mimetypeOrModeId === 'text/javascript') {
return 'js-mode-id';
}
throw new Error('Not implemented');
}
getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): Modes.IMode {
if (commaSeparatedMimetypesOrCommaSeparatedIds === 'js-mode-id') {
return this._mockJSMode;
}
throw new Error('Not implemented');
}
}
suite('Colorizing - HTML', () => {
let tokenizationSupport: Modes.ITokenizationSupport;
let _mode: Modes.IMode;
let onEnterSupport: Modes.IRichEditOnEnter;
suiteSetup(function() {
_mode = new HTMLMode<htmlWorker.HTMLWorker>(
{ id: 'html' },
null,
new HTMLMockModeService(),
null,
null,
null
);
tokenizationSupport = Modes.TokenizationRegistry.get(_mode.getId());
onEnterSupport = LanguageConfigurationRegistry.getOnEnterSupport(_mode.getId());
});
test('Open Start Tag #1', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') }
]}
]);
});
test('Open Start Tag #2', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<input',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('input') }
]}
]);
});
test('Open Start Tag with Invalid Tag', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '< abc',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: '' }
]}
]);
});
test('Open Start Tag #3', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '< abc>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: '' }
]}
]);
});
test('Open Start Tag #4', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: 'i <len;',
tokens: [
{ startIndex:0, type: '' },
{ startIndex:2, type: DELIM_START },
{ startIndex:3, type: getTag('len') },
{ startIndex:6, type: '' }
]}
]);
});
test('Open Start Tag #5', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<',
tokens: [
{ startIndex:0, type: DELIM_START }
]}
]);
});
test('Open End Tag', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '</a',
tokens: [
{ startIndex:0, type: DELIM_END },
{ startIndex:2, type: getTag('a') }
]}
]);
});
test('Complete Start Tag', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: DELIM_START }
]}
]);
});
test('Complete Start Tag with Whitespace', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc >',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: DELIM_START }
]}
]);
});
test('bug 9809 - Complete Start Tag with Namespaceprefix', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<foo:bar>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('foo-bar') },
{ startIndex:8, type: DELIM_START }
]}
]);
});
test('Complete End Tag', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '</abc>',
tokens: [
{ startIndex:0, type: DELIM_END },
{ startIndex:2, type: getTag('abc') },
{ startIndex:5, type: DELIM_END }
]}
]);
});
test('Complete End Tag with Whitespace', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '</abc >',
tokens: [
{ startIndex:0, type: DELIM_END },
{ startIndex:2, type: getTag('abc') },
{ startIndex:5, type: '' },
{ startIndex:7, type: DELIM_END }
]}
]);
});
test('Empty Tag', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc />',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: DELIM_START }
]}
]);
});
test('Embedded Content #1', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/javascript">var i= 10;</script>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: ATTRIB_NAME },
{ startIndex:12, type: DELIM_ASSIGN },
{ startIndex:13, type: ATTRIB_VALUE },
{ startIndex:30, type: DELIM_START },
{ startIndex:31, type: 'mock-js' },
{ startIndex:41, type: DELIM_END },
{ startIndex:43, type: getTag('script') },
{ startIndex:49, type: DELIM_END }
]}
]);
});
test('Embedded Content #2', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/javascript">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: ATTRIB_NAME },
{ startIndex:12, type: DELIM_ASSIGN },
{ startIndex:13, type: ATTRIB_VALUE },
{ startIndex:30, type: DELIM_START }
]}, {
line: 'var i= 10;',
tokens: [
{ startIndex:0, type: 'mock-js' },
]}, {
line: '</script>',
tokens: [
{ startIndex:0, type: DELIM_END },
{ startIndex:2, type: getTag('script') },
{ startIndex:8, type: DELIM_END }
]}
]);
});
test('Embedded Content #3', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/javascript">var i= 10;',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: ATTRIB_NAME },
{ startIndex:12, type: DELIM_ASSIGN },
{ startIndex:13, type: ATTRIB_VALUE },
{ startIndex:30, type: DELIM_START },
{ startIndex:31, type: 'mock-js' },
]}, {
line: '</script>',
tokens: [
{ startIndex:0, type: DELIM_END },
{ startIndex:2, type: getTag('script') },
{ startIndex:8, type: DELIM_END }
]}
]);
});
test('Embedded Content #4', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/javascript">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: ATTRIB_NAME },
{ startIndex:12, type: DELIM_ASSIGN },
{ startIndex:13, type: ATTRIB_VALUE },
{ startIndex:30, type: DELIM_START }
]}, {
line: 'var i= 10;</script>',
tokens: [
{ startIndex:0, type: 'mock-js' },
{ startIndex:10, type: DELIM_END },
{ startIndex:12, type: getTag('script') },
{ startIndex:18, type: DELIM_END }
]}
]);
});
test('Embedded Content #5', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/plain">a\n<a</script>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: ATTRIB_NAME },
{ startIndex:12, type: DELIM_ASSIGN },
{ startIndex:13, type: ATTRIB_VALUE },
{ startIndex:25, type: DELIM_START },
{ startIndex:26, type: '' },
{ startIndex:30, type: DELIM_END },
{ startIndex:32, type: getTag('script') },
{ startIndex:38, type: DELIM_END }
]}
]);
});
test('Embedded Content #6', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script>a</script><script>b</script>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: DELIM_START },
{ startIndex:8, type: 'mock-js' },
{ startIndex:9, type: DELIM_END },
{ startIndex:11, type: getTag('script') },
{ startIndex:17, type: DELIM_END },
{ startIndex:18, type: DELIM_START },
{ startIndex:19, type: getTag('script') },
{ startIndex:25, type: DELIM_START },
{ startIndex:26, type: 'mock-js' },
{ startIndex:27, type: DELIM_END },
{ startIndex:29, type: getTag('script') },
{ startIndex:35, type: DELIM_END }
]}
]);
});
test('Embedded Content #7', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/javascript"></script>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: ATTRIB_NAME },
{ startIndex:12, type: DELIM_ASSIGN },
{ startIndex:13, type: ATTRIB_VALUE },
{ startIndex:30, type: DELIM_START },
{ startIndex:31, type: DELIM_END },
{ startIndex:33, type: getTag('script') },
{ startIndex:39, type: DELIM_END }
]}
]);
});
test('Embedded Content #8', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script>var i= 10;</script>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: DELIM_START },
{ startIndex:8, type: 'mock-js' },
{ startIndex:18, type: DELIM_END },
{ startIndex:20, type: getTag('script') },
{ startIndex:26, type: DELIM_END }
]}
]);
});
test('Embedded Content #9', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<script type="text/javascript" src="main.js"></script>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('script') },
{ startIndex:7, type: '' },
{ startIndex:8, type: ATTRIB_NAME },
{ startIndex:12, type: DELIM_ASSIGN },
{ startIndex:13, type: ATTRIB_VALUE },
{ startIndex:30, type: '' },
{ startIndex:31, type: ATTRIB_NAME },
{ startIndex:34, type: DELIM_ASSIGN },
{ startIndex:35, type: ATTRIB_VALUE },
{ startIndex:44, type: DELIM_START },
{ startIndex:45, type: DELIM_END },
{ startIndex:47, type: getTag('script') },
{ startIndex:53, type: DELIM_END }
]}
]);
});
test('Tag with Attribute', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo="bar">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: DELIM_ASSIGN },
{ startIndex:9, type: ATTRIB_VALUE },
{ startIndex:14, type: DELIM_START }
]}
]);
});
test('Tag with Empty Attribute Value', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo=\'bar\'>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: DELIM_ASSIGN },
{ startIndex:9, type: ATTRIB_VALUE },
{ startIndex:14, type: DELIM_START }
]}
]);
});
test('Tag with empty attributes', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo="">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: DELIM_ASSIGN },
{ startIndex:9, type: ATTRIB_VALUE },
{ startIndex:11, type: DELIM_START }
]}
]);
});
test('Tag with Attributes', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo="bar" bar=\'foo\'>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: DELIM_ASSIGN },
{ startIndex:9, type: ATTRIB_VALUE },
{ startIndex:14, type: '' },
{ startIndex:15, type: ATTRIB_NAME },
{ startIndex:18, type: DELIM_ASSIGN },
{ startIndex:19, type: ATTRIB_VALUE },
{ startIndex:24, type: DELIM_START }
]}
]);
});
test('Tag with Attributes, no quotes', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo=bar bar=help-me>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: DELIM_ASSIGN },
{ startIndex:9, type: ATTRIB_VALUE },
{ startIndex:12, type: '' },
{ startIndex:13, type: ATTRIB_NAME },
{ startIndex:16, type: DELIM_ASSIGN },
{ startIndex:17, type: ATTRIB_VALUE },
{ startIndex:24, type: DELIM_START }
]}
]);
});
test('Tag with Attribute And Whitespace', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo= "bar">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: DELIM_ASSIGN },
{ startIndex:9, type: '' },
{ startIndex:11, type: ATTRIB_VALUE },
{ startIndex:16, type: DELIM_START }
]}
]);
});
test('Tag with Attribute And Whitespace #2', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo = "bar">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: '' },
{ startIndex:9, type: DELIM_ASSIGN },
{ startIndex:10, type: '' },
{ startIndex:11, type: ATTRIB_VALUE },
{ startIndex:16, type: DELIM_START }
]}
]);
});
test('Tag with Name-Only-Attribute #1', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: DELIM_START }
]}
]);
});
test('Tag with Name-Only-Attribute #2', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo bar>',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: '' },
{ startIndex:9, type: ATTRIB_NAME },
{ startIndex:12, type: DELIM_START }
]}
]);
});
test('Tag with Interesting Attribute Name', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo!@#="bar">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:11, type: DELIM_ASSIGN },
{ startIndex:12, type: ATTRIB_VALUE },
{ startIndex:17, type: DELIM_START }
]}
]);
});
test('Tag with Angular Attribute Name', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc #myinput (click)="bar" [value]="someProperty" *ngIf="someCondition">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:13, type: '' },
{ startIndex:14, type: ATTRIB_NAME },
{ startIndex:21, type: DELIM_ASSIGN },
{ startIndex:22, type: ATTRIB_VALUE },
{ startIndex:27, type: '' },
{ startIndex:28, type: ATTRIB_NAME },
{ startIndex:35, type: DELIM_ASSIGN },
{ startIndex:36, type: ATTRIB_VALUE },
{ startIndex:50, type: '' },
{ startIndex:51, type: ATTRIB_NAME },
{ startIndex:56, type: DELIM_ASSIGN },
{ startIndex:57, type: ATTRIB_VALUE },
{ startIndex:72, type: DELIM_START }
]}
]);
});
test('Tag with Invalid Attribute Value', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<abc foo=">',
tokens: [
{ startIndex:0, type: DELIM_START },
{ startIndex:1, type: getTag('abc') },
{ startIndex:4, type: '' },
{ startIndex:5, type: ATTRIB_NAME },
{ startIndex:8, type: DELIM_ASSIGN },
{ startIndex:9, type: ATTRIB_VALUE }
]}
]);
});
test('Simple Comment 1', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<!--a-->',
tokens: [
{ startIndex:0, type: DELIM_COMMENT },
{ startIndex:4, type: COMMENT },
{ startIndex:5, type: DELIM_COMMENT }
]}
]);
});
test('Simple Comment 2', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<!--a>foo bar</a -->',
tokens: [
{ startIndex:0, type: DELIM_COMMENT },
{ startIndex:4, type: COMMENT },
{ startIndex:17, type: DELIM_COMMENT }
]}
]);
});
test('Multiline Comment', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<!--a>\nfoo \nbar</a -->',
tokens: [
{ startIndex:0, type: DELIM_COMMENT },
{ startIndex:4, type: COMMENT },
{ startIndex:19, type: DELIM_COMMENT }
]}
]);
});
test('Simple Doctype', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<!DOCTYPE a>',
tokens: [
{ startIndex:0, type: DELIM_DOCTYPE },
{ startIndex:9, type: DOCTYPE },
{ startIndex:11, type: DELIM_DOCTYPE }
]}
]);
});
test('Simple Doctype #2', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<!doctype a>',
tokens: [
{ startIndex:0, type: DELIM_DOCTYPE },
{ startIndex:9, type: DOCTYPE },
{ startIndex:11, type: DELIM_DOCTYPE }
]}
]);
});
test('Simple Doctype #4', () => {
modesUtil.assertTokenization(tokenizationSupport, [{
line: '<!DOCTYPE a\n"foo" \'bar\'>',
tokens: [
{ startIndex:0, type: DELIM_DOCTYPE },
{ startIndex:9, type: DOCTYPE },
{ startIndex:23, type: DELIM_DOCTYPE }
]}
]);
});
test('onEnter 1', function() {
var model = Model.createFromString('<script type=\"text/javascript\">function f() { foo(); }', undefined, _mode.getId());
var actual = onEnterSupport.onEnter(model, {
lineNumber: 1,
column: 46
});
assert.equal(actual.indentAction, Modes.IndentAction.Indent);
model.dispose();
});
test('onEnter 2', function() {
function onEnter(line:string, offset:number): Modes.EnterAction {
let model = new TextModelWithTokens([], TextModel.toRawText(line, TextModel.DEFAULT_CREATION_OPTIONS), _mode.getId());
let result = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, 1, offset + 1);
model.dispose();
return result;
}
function assertOnEnter(text:string, offset:number, expected: Modes.IndentAction): void {
let _actual = onEnter(text, offset);
let actual = _actual ? _actual.indentAction : null;
let actualStr = actual ? Modes.IndentAction[actual] : null;
let expectedStr = expected ? Modes.IndentAction[expected] : null;
assert.equal(actualStr, expectedStr, 'TEXT: <<' + text + '>>, OFFSET: <<' + offset + '>>');
}
assertOnEnter('', 0, null);
assertOnEnter('>', 1, null);
assertOnEnter('span>', 5, null);
assertOnEnter('</span>', 7, null);
assertOnEnter('<img />', 7, null);
assertOnEnter('<span>', 6, Modes.IndentAction.Indent);
assertOnEnter('<p>', 3, Modes.IndentAction.Indent);
assertOnEnter('<span><span>', 6, Modes.IndentAction.Indent);
assertOnEnter('<p><span>', 3, Modes.IndentAction.Indent);
assertOnEnter('<span></SPan>', 6, Modes.IndentAction.IndentOutdent);
assertOnEnter('<span></span>', 6, Modes.IndentAction.IndentOutdent);
assertOnEnter('<p></p>', 3, Modes.IndentAction.IndentOutdent);
assertOnEnter('<span>a</span>', 6, Modes.IndentAction.Indent);
assertOnEnter('<span>a</span>', 7, Modes.IndentAction.IndentOutdent);
assertOnEnter('<span> </span>', 6, Modes.IndentAction.Indent);
assertOnEnter('<span> </span>', 7, Modes.IndentAction.IndentOutdent);
});
test('matchBracket', () => {
function toString(brackets:[Range, Range]): [string,string] {
if (!brackets) {
return null;
}
brackets.sort(Range.compareRangesUsingStarts);
return [brackets[0].toString(), brackets[1].toString()];
}
function assertBracket(lines:string[], lineNumber:number, column:number, expected:[Range, Range]): void {
let model = new TextModelWithTokens([], TextModel.toRawText(lines.join('\n'), TextModel.DEFAULT_CREATION_OPTIONS), _mode.getId());
// force tokenization
model.getLineContext(model.getLineCount());
let actual = model.matchBracket({
lineNumber: lineNumber,
column: column
});
assert.deepEqual(toString(actual), toString(expected), 'TEXT <<' + lines.join('\n') + '>>, POS: ' + lineNumber + ', ' + column);
}
assertBracket(['<p></p>'], 1, 1, [new Range(1, 1, 1, 2), new Range(1, 3, 1, 4)]);
assertBracket(['<p></p>'], 1, 2, [new Range(1, 1, 1, 2), new Range(1, 3, 1, 4)]);
assertBracket(['<p></p>'], 1, 3, [new Range(1, 1, 1, 2), new Range(1, 3, 1, 4)]);
assertBracket(['<p></p>'], 1, 4, [new Range(1, 4, 1, 5), new Range(1, 7, 1, 8)]);
assertBracket(['<p></p>'], 1, 5, [new Range(1, 4, 1, 5), new Range(1, 7, 1, 8)]);
assertBracket(['<p></p>'], 1, 6, null);
assertBracket(['<p></p>'], 1, 7, [new Range(1, 4, 1, 5), new Range(1, 7, 1, 8)]);
assertBracket(['<p></p>'], 1, 8, [new Range(1, 4, 1, 5), new Range(1, 7, 1, 8)]);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 10, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 11, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 22, null);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 23, null);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 33, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a[a<script>a]a'], 1, 34, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 10, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 11, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 22, null);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 23, null);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 33, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
assertBracket(['<script>a[a</script>a]a<script>a]a'], 1, 34, [new Range(1, 10, 1, 11), new Range(1, 33, 1, 34)]);
});
});
// June 2012
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: THEY ARE CLEARED ONLY FOR LIMITED USE BY MONACO FOR THE MONACO PRODUCT. DO NOT USE OR SHARE THIS CODE WITHOUT APPROVAL PURSUANT TO THE MICROSOFT OPEN SOURCE SOFTWARE APPROVAL POLICY.APPROVAL
[{
"name": "js-beautify",
"version": "1.6.2",
"license": "MIT",
"repositoryURL": "https://github.com/beautify-web/js-beautify"
}]
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IBeautifyCSSOptions {
indent_size?: number; // (4) — indentation size,
indent_char?: string; // (space) — character to indent with,
selector_separator_newline?: boolean; // (true) - separate selectors with newline or not (e.g. "a,\nbr" or "a, br")
end_with_newline?: boolean; // (false) - end with a newline
newline_between_rules?: boolean; // (true) - add a new line after every css rule
}
export interface IBeautifyCSS {
(value:string, options:IBeautifyCSSOptions): string;
}
export declare var css_beautify:IBeautifyCSS;
\ No newline at end of file
// copied https://raw.githubusercontent.com/beautify-web/js-beautify/master/js/lib/beautify-css.js
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
CSS Beautifier
---------------
Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)
Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
http://jsbeautifier.org/
Usage:
css_beautify(source_text);
css_beautify(source_text, options);
The options are (default in brackets):
indent_size (4) — indentation size,
indent_char (space) — character to indent with,
selector_separator_newline (true) - separate selectors with newline or
not (e.g. "a,\nbr" or "a, br")
end_with_newline (false) - end with a newline
newline_between_rules (true) - add a new line after every css rule
e.g
css_beautify(css_source_text, {
'indent_size': 1,
'indent_char': '\t',
'selector_separator': ' ',
'end_with_newline': false,
'newline_between_rules': true
});
*/
// http://www.w3.org/TR/CSS21/syndata.html#tokenization
// http://www.w3.org/TR/css3-syntax/
(function() {
function css_beautify(source_text, options) {
options = options || {};
source_text = source_text || '';
// HACK: newline parsing inconsistent. This brute force normalizes the input.
source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n')
var indentSize = options.indent_size || 4;
var indentCharacter = options.indent_char || ' ';
var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
var eol = options.eol ? options.eol : '\n';
// compatibility
if (typeof indentSize === "string") {
indentSize = parseInt(indentSize, 10);
}
if(options.indent_with_tabs){
indentCharacter = '\t';
indentSize = 1;
}
eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
// tokenizer
var whiteRe = /^\s+$/;
var wordRe = /[\w$\-_]/;
var pos = -1,
ch;
var parenLevel = 0;
function next() {
ch = source_text.charAt(++pos);
return ch || '';
}
function peek(skipWhitespace) {
var result = '';
var prev_pos = pos;
if (skipWhitespace) {
eatWhitespace();
}
result = source_text.charAt(pos + 1) || '';
pos = prev_pos - 1;
next();
return result;
}
function eatString(endChars) {
var start = pos;
while (next()) {
if (ch === "\\") {
next();
} else if (endChars.indexOf(ch) !== -1) {
break;
} else if (ch === "\n") {
break;
}
}
return source_text.substring(start, pos + 1);
}
function peekString(endChar) {
var prev_pos = pos;
var str = eatString(endChar);
pos = prev_pos - 1;
next();
return str;
}
function eatWhitespace() {
var result = '';
while (whiteRe.test(peek())) {
next();
result += ch;
}
return result;
}
function skipWhitespace() {
var result = '';
if (ch && whiteRe.test(ch)) {
result = ch;
}
while (whiteRe.test(next())) {
result += ch;
}
return result;
}
function eatComment(singleLine) {
var start = pos;
singleLine = peek() === "/";
next();
while (next()) {
if (!singleLine && ch === "*" && peek() === "/") {
next();
break;
} else if (singleLine && ch === "\n") {
return source_text.substring(start, pos);
}
}
return source_text.substring(start, pos) + ch;
}
function lookBack(str) {
return source_text.substring(pos - str.length, pos).toLowerCase() ===
str;
}
// Nested pseudo-class if we are insideRule
// and the next special character found opens
// a new block
function foundNestedPseudoClass() {
var openParen = 0;
for (var i = pos + 1; i < source_text.length; i++) {
var ch = source_text.charAt(i);
if (ch === "{") {
return true;
} else if (ch === '(') {
// pseudoclasses can contain ()
openParen += 1;
} else if (ch === ')') {
if (openParen == 0) {
return false;
}
openParen -= 1;
} else if (ch === ";" || ch === "}") {
return false;
}
}
return false;
}
// printer
var basebaseIndentString = source_text.match(/^[\t ]*/)[0];
var singleIndent = new Array(indentSize + 1).join(indentCharacter);
var indentLevel = 0;
var nestedLevel = 0;
function indent() {
indentLevel++;
basebaseIndentString += singleIndent;
}
function outdent() {
indentLevel--;
basebaseIndentString = basebaseIndentString.slice(0, -indentSize);
}
var print = {};
print["{"] = function(ch) {
print.singleSpace();
output.push(ch);
print.newLine();
};
print["}"] = function(ch) {
print.newLine();
output.push(ch);
print.newLine();
};
print._lastCharWhitespace = function() {
return whiteRe.test(output[output.length - 1]);
};
print.newLine = function(keepWhitespace) {
if (output.length) {
if (!keepWhitespace && output[output.length - 1] !== '\n') {
print.trim();
}
output.push('\n');
if (basebaseIndentString) {
output.push(basebaseIndentString);
}
}
};
print.singleSpace = function() {
if (output.length && !print._lastCharWhitespace()) {
output.push(' ');
}
};
print.preserveSingleSpace = function() {
if (isAfterSpace) {
print.singleSpace();
}
};
print.trim = function() {
while (print._lastCharWhitespace()) {
output.pop();
}
};
var output = [];
/*_____________________--------------------_____________________*/
var insideRule = false;
var insidePropertyValue = false;
var enteringConditionalGroup = false;
var top_ch = '';
var last_top_ch = '';
while (true) {
var whitespace = skipWhitespace();
var isAfterSpace = whitespace !== '';
var isAfterNewline = whitespace.indexOf('\n') !== -1;
last_top_ch = top_ch;
top_ch = ch;
if (!ch) {
break;
} else if (ch === '/' && peek() === '*') { /* css comment */
var header = indentLevel === 0;
if (isAfterNewline || header) {
print.newLine();
}
output.push(eatComment());
print.newLine();
if (header) {
print.newLine(true);
}
} else if (ch === '/' && peek() === '/') { // single line comment
if (!isAfterNewline && last_top_ch !== '{' ) {
print.trim();
}
print.singleSpace();
output.push(eatComment());
print.newLine();
} else if (ch === '@') {
print.preserveSingleSpace();
output.push(ch);
// strip trailing space, if present, for hash property checks
var variableOrRule = peekString(": ,;{}()[]/='\"");
if (variableOrRule.match(/[ :]$/)) {
// we have a variable or pseudo-class, add it and insert one space before continuing
next();
variableOrRule = eatString(": ").replace(/\s$/, '');
output.push(variableOrRule);
print.singleSpace();
}
variableOrRule = variableOrRule.replace(/\s$/, '')
// might be a nesting at-rule
if (variableOrRule in css_beautify.NESTED_AT_RULE) {
nestedLevel += 1;
if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) {
enteringConditionalGroup = true;
}
}
} else if (ch === '#' && peek() === '{') {
print.preserveSingleSpace();
output.push(eatString('}'));
} else if (ch === '{') {
if (peek(true) === '}') {
eatWhitespace();
next();
print.singleSpace();
output.push("{}");
print.newLine();
if (newline_between_rules && indentLevel === 0) {
print.newLine(true);
}
} else {
indent();
print["{"](ch);
// when entering conditional groups, only rulesets are allowed
if (enteringConditionalGroup) {
enteringConditionalGroup = false;
insideRule = (indentLevel > nestedLevel);
} else {
// otherwise, declarations are also allowed
insideRule = (indentLevel >= nestedLevel);
}
}
} else if (ch === '}') {
outdent();
print["}"](ch);
insideRule = false;
insidePropertyValue = false;
if (nestedLevel) {
nestedLevel--;
}
if (newline_between_rules && indentLevel === 0) {
print.newLine(true);
}
} else if (ch === ":") {
eatWhitespace();
if ((insideRule || enteringConditionalGroup) &&
!(lookBack("&") || foundNestedPseudoClass())) {
// 'property: value' delimiter
// which could be in a conditional group query
insidePropertyValue = true;
output.push(':');
print.singleSpace();
} else {
// sass/less parent reference don't use a space
// sass nested pseudo-class don't use a space
if (peek() === ":") {
// pseudo-element
next();
output.push("::");
} else {
// pseudo-class
output.push(':');
}
}
} else if (ch === '"' || ch === '\'') {
print.preserveSingleSpace();
output.push(eatString(ch));
} else if (ch === ';') {
insidePropertyValue = false;
output.push(ch);
print.newLine();
} else if (ch === '(') { // may be a url
if (lookBack("url")) {
output.push(ch);
eatWhitespace();
if (next()) {
if (ch !== ')' && ch !== '"' && ch !== '\'') {
output.push(eatString(')'));
} else {
pos--;
}
}
} else {
parenLevel++;
print.preserveSingleSpace();
output.push(ch);
eatWhitespace();
}
} else if (ch === ')') {
output.push(ch);
parenLevel--;
} else if (ch === ',') {
output.push(ch);
eatWhitespace();
if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
print.newLine();
} else {
print.singleSpace();
}
} else if (ch === ']') {
output.push(ch);
} else if (ch === '[') {
print.preserveSingleSpace();
output.push(ch);
} else if (ch === '=') { // no whitespace before or after
eatWhitespace()
ch = '=';
output.push(ch);
} else {
print.preserveSingleSpace();
output.push(ch);
}
}
var sweetCode = '';
if (basebaseIndentString) {
sweetCode += basebaseIndentString;
}
sweetCode += output.join('').replace(/[\r\n\t ]+$/, '');
// establish end_with_newline
if (end_with_newline) {
sweetCode += '\n';
}
if (eol != '\n') {
sweetCode = sweetCode.replace(/[\n]/g, eol);
}
return sweetCode;
}
// https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
css_beautify.NESTED_AT_RULE = {
"@page": true,
"@font-face": true,
"@keyframes": true,
// also in CONDITIONAL_GROUP_RULE below
"@media": true,
"@supports": true,
"@document": true
};
css_beautify.CONDITIONAL_GROUP_RULE = {
"@media": true,
"@supports": true,
"@document": true
};
/*global define */
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define([], function() {
return {
css_beautify: css_beautify
};
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var html_beautify = require("beautify").html_beautify`.
exports.css_beautify = css_beautify;
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.css_beautify = css_beautify;
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.css_beautify = css_beautify;
}
}());
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IBeautifyHTMLOptions {
/**
* indent <head> and <body> sections
* default false
*/
indent_inner_html?: boolean;
/**
* indentation size
* default 4
*/
indent_size?: number; // indentation size,
/**
* character to indent with
* default space
*/
indent_char?: string; // character to indent with,
/**
* maximum amount of characters per line (0 = disable)
* default 250
*/
wrap_line_length?: number;
/**
* put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line, or attempt to keep them where they are.
* "collapse" | "expand" | "end-expand" | "none"
* default "collapse"
*/
brace_style?: string;
/**
* list of tags, that shouldn't be reformatted
* defaults to inline tags
*/
unformatted?: string[];
/**
* "keep"|"separate"|"normal"
* default normal
*/
indent_scripts?: string;
/**
* whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.
* default true
*/
preserve_newlines?: boolean;
/**
* maximum number of line breaks to be preserved in one chunk
* default unlimited
*/
max_preserve_newlines?: number;
/**
* format and indent {{#foo}} and {{/foo}}
* default false
*/
indent_handlebars?: boolean;
/**
* end with a newline
* default false
*/
end_with_newline?: boolean;
/**
* List of tags that should have an extra newline before them.
* default [head,body,/html]
*/
extra_liners?: string[];
}
export interface IBeautifyHTML {
(value:string, options:IBeautifyHTMLOptions): string;
}
export declare var html_beautify:IBeautifyHTML;
\ No newline at end of file
// copied from https://raw.githubusercontent.com/beautify-web/js-beautify/master/js/lib/beautify-html.js
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Style HTML
---------------
Written by Nochum Sossonko, (nsossonko@hotmail.com)
Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
http://jsbeautifier.org/
Usage:
style_html(html_source);
style_html(html_source, options);
The options are:
indent_inner_html (default false) — indent <head> and <body> sections,
indent_size (default 4) — indentation size,
indent_char (default space) — character to indent with,
wrap_line_length (default 250) - maximum amount of characters per line (0 = disable)
brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none"
put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line, or attempt to keep them where they are.
unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
indent_scripts (default normal) - "keep"|"separate"|"normal"
preserve_newlines (default true) - whether existing line breaks before elements should be preserved
Only works before elements, not inside tags or for text.
max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk
indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}}
end_with_newline (false) - end with a newline
extra_liners (default [head,body,/html]) -List of tags that should have an extra newline before them.
e.g.
style_html(html_source, {
'indent_inner_html': false,
'indent_size': 2,
'indent_char': ' ',
'wrap_line_length': 78,
'brace_style': 'expand',
'preserve_newlines': true,
'max_preserve_newlines': 5,
'indent_handlebars': false,
'extra_liners': ['/html']
});
*/
(function() {
function trim(s) {
return s.replace(/^\s+|\s+$/g, '');
}
function ltrim(s) {
return s.replace(/^\s+/g, '');
}
function rtrim(s) {
return s.replace(/\s+$/g,'');
}
function style_html(html_source, options, js_beautify, css_beautify) {
//Wrapper function to invoke all the necessary constructors and deal with the output.
var multi_parser,
indent_inner_html,
indent_size,
indent_character,
wrap_line_length,
brace_style,
unformatted,
preserve_newlines,
max_preserve_newlines,
indent_handlebars,
wrap_attributes,
wrap_attributes_indent_size,
end_with_newline,
extra_liners,
eol;
options = options || {};
// backwards compatibility to 1.3.4
if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
(options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
options.wrap_line_length = options.max_char;
}
indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
unformatted = options.unformatted || [
// https://www.w3.org/TR/html5/dom.html#phrasing-content
'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite',
'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img',
'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark', 'math', 'meter', 'noscript',
'object', 'output', 'progress', 'q', 'ruby', 's', 'samp', /* 'script', */ 'select', 'small',
'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var',
'video', 'wbr', 'text',
// prexisting - not sure of full effect of removing, leaving in
'acronym', 'address', 'big', 'dt', 'ins', 'small', 'strike', 'tt',
'pre',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
];
preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
max_preserve_newlines = preserve_newlines ?
(isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10))
: 0;
indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes;
wrap_attributes_indent_size = (isNaN(parseInt(options.wrap_attributes_indent_size, 10))) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10);
end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
extra_liners = (typeof options.extra_liners == 'object') && options.extra_liners ?
options.extra_liners.concat() : (typeof options.extra_liners === 'string') ?
options.extra_liners.split(',') : 'head,body,/html'.split(',');
eol = options.eol ? options.eol : '\n';
if(options.indent_with_tabs){
indent_character = '\t';
indent_size = 1;
}
eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
function Parser() {
this.pos = 0; //Parser position
this.token = '';
this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
parent: 'parent1',
parentcount: 1,
parent1: ''
};
this.tag_type = '';
this.token_text = this.last_token = this.last_text = this.token_type = '';
this.newlines = 0;
this.indent_content = indent_inner_html;
this.Utils = { //Uilities made available to the various functions
whitespace: "\n\r\t ".split(''),
single_token: [
// HTLM void elements - aka self-closing tags - aka singletons
// https://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr',
// NOTE: Optional tags - are not understood.
// https://www.w3.org/TR/html5/syntax.html#optional-tags
// The rules for optional tags are too complex for a simple list
// Also, the content of these tags should still be indented in many cases.
// 'li' is a good exmple.
// Doctype and xml elements
'!doctype', '?xml',
// ?php tag
'?php',
// other tags that were in this list, keeping just in case
'basefont', 'isindex'
],
extra_liners: extra_liners, //for tags that need a line of whitespace before them
in_array: function(what, arr) {
for (var i = 0; i < arr.length; i++) {
if (what === arr[i]) {
return true;
}
}
return false;
}
};
// Return true if the given text is composed entirely of whitespace.
this.is_whitespace = function(text) {
for (var n = 0; n < text.length; n++) {
if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) {
return false;
}
}
return true;
};
this.traverse_whitespace = function() {
var input_char = '';
input_char = this.input.charAt(this.pos);
if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
this.newlines = 0;
while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) {
this.newlines += 1;
}
this.pos++;
input_char = this.input.charAt(this.pos);
}
return true;
}
return false;
};
// Append a space to the given content (string array) or, if we are
// at the wrap_line_length, append a newline/indentation.
// return true if a newline was added, false if a space was added
this.space_or_wrap = function(content) {
if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
this.print_newline(false, content);
this.print_indentation(content);
return true;
} else {
this.line_char_count++;
content.push(' ');
return false;
}
};
this.get_content = function() { //function to capture regular content between tags
var input_char = '',
content = [],
space = false; //if a space is needed
while (this.input.charAt(this.pos) !== '<') {
if (this.pos >= this.input.length) {
return content.length ? content.join('') : ['', 'TK_EOF'];
}
if (this.traverse_whitespace()) {
this.space_or_wrap(content);
continue;
}
if (indent_handlebars) {
// Handlebars parsing is complicated.
// {{#foo}} and {{/foo}} are formatted tags.
// {{something}} should get treated as content, except:
// {{else}} specifically behaves like {{#if}} and {{/if}}
var peek3 = this.input.substr(this.pos, 3);
if (peek3 === '{{#' || peek3 === '{{/') {
// These are tags and not content.
break;
} else if (peek3 === '{{!') {
return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT'];
} else if (this.input.substr(this.pos, 2) === '{{') {
if (this.get_tag(true) === '{{else}}') {
break;
}
}
}
input_char = this.input.charAt(this.pos);
this.pos++;
this.line_char_count++;
content.push(input_char); //letter at-a-time (or string) inserted to an array
}
return content.length ? content.join('') : '';
};
this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
if (this.pos === this.input.length) {
return ['', 'TK_EOF'];
}
var input_char = '';
var content = '';
var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
reg_match.lastIndex = this.pos;
var reg_array = reg_match.exec(this.input);
var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
if (this.pos < end_script) { //get everything in between the script tags
content = this.input.substring(this.pos, end_script);
this.pos = end_script;
}
return content;
};
this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
if (this.tags[tag + 'count']) { //check for the existence of this tag type
this.tags[tag + 'count']++;
this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
} else { //otherwise initialize this tag type
this.tags[tag + 'count'] = 1;
this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
}
this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
};
this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
var temp_parent = this.tags.parent; //check to see if it's a closable tag.
while (temp_parent) { //till we reach '' (the initial value);
if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
break;
}
temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
}
if (temp_parent) { //if we caught something
this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
}
delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
if (this.tags[tag + 'count'] === 1) {
delete this.tags[tag + 'count'];
} else {
this.tags[tag + 'count']--;
}
}
};
this.indent_to_tag = function(tag) {
// Match the indentation level to the last use of this tag, but don't remove it.
if (!this.tags[tag + 'count']) {
return;
}
var temp_parent = this.tags.parent;
while (temp_parent) {
if (tag + this.tags[tag + 'count'] === temp_parent) {
break;
}
temp_parent = this.tags[temp_parent + 'parent'];
}
if (temp_parent) {
this.indent_level = this.tags[tag + this.tags[tag + 'count']];
}
};
this.get_tag = function(peek) { //function to get a full tag and parse its type
var input_char = '',
content = [],
comment = '',
space = false,
first_attr = true,
tag_start, tag_end,
tag_start_char,
orig_pos = this.pos,
orig_line_char_count = this.line_char_count;
peek = peek !== undefined ? peek : false;
do {
if (this.pos >= this.input.length) {
if (peek) {
this.pos = orig_pos;
this.line_char_count = orig_line_char_count;
}
return content.length ? content.join('') : ['', 'TK_EOF'];
}
input_char = this.input.charAt(this.pos);
this.pos++;
if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
space = true;
continue;
}
if (input_char === "'" || input_char === '"') {
input_char += this.get_unformatted(input_char);
space = true;
}
if (input_char === '=') { //no space before =
space = false;
}
if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) {
//no space after = or before >
var wrapped = this.space_or_wrap(content);
var indentAttrs = wrapped && input_char !== '/' && wrap_attributes !== 'force';
space = false;
if (!first_attr && wrap_attributes === 'force' && input_char !== '/') {
this.print_newline(false, content);
this.print_indentation(content);
indentAttrs = true;
}
if (indentAttrs) {
//indent attributes an auto or forced line-wrap
for (var count = 0; count < wrap_attributes_indent_size; count++) {
content.push(indent_character);
}
}
for (var i = 0; i < content.length; i++) {
if (content[i] === ' ') {
first_attr = false;
break;
}
}
}
if (indent_handlebars && tag_start_char === '<') {
// When inside an angle-bracket tag, put spaces around
// handlebars not inside of strings.
if ((input_char + this.input.charAt(this.pos)) === '{{') {
input_char += this.get_unformatted('}}');
if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
input_char = ' ' + input_char;
}
space = true;
}
}
if (input_char === '<' && !tag_start_char) {
tag_start = this.pos - 1;
tag_start_char = '<';
}
if (indent_handlebars && !tag_start_char) {
if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] === '{') {
if (input_char === '#' || input_char === '/' || input_char === '!') {
tag_start = this.pos - 3;
} else {
tag_start = this.pos - 2;
}
tag_start_char = '{';
}
}
this.line_char_count++;
content.push(input_char); //inserts character at-a-time (or string)
if (content[1] && (content[1] === '!' || content[1] === '?' || content[1] === '%')) { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
content = [this.get_comment(tag_start)];
break;
}
if (indent_handlebars && content[1] && content[1] === '{' && content[2] && content[2] === '!') { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
content = [this.get_comment(tag_start)];
break;
}
if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') {
break;
}
} while (input_char !== '>');
var tag_complete = content.join('');
var tag_index;
var tag_offset;
if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends
tag_index = tag_complete.indexOf(' ');
} else if (tag_complete.charAt(0) === '{') {
tag_index = tag_complete.indexOf('}');
} else { //otherwise go with the tag ending
tag_index = tag_complete.indexOf('>');
}
if (tag_complete.charAt(0) === '<' || !indent_handlebars) {
tag_offset = 1;
} else {
tag_offset = tag_complete.charAt(2) === '#' ? 3 : 2;
}
var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
if (!peek) {
this.tag_type = 'SINGLE';
}
} else if (indent_handlebars && tag_complete.charAt(0) === '{' && tag_check === 'else') {
if (!peek) {
this.indent_to_tag('if');
this.tag_type = 'HANDLEBARS_ELSE';
this.indent_content = true;
this.traverse_whitespace();
}
} else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
content.push(comment);
tag_end = this.pos - 1;
this.tag_type = 'SINGLE';
} else if (tag_check === 'script' &&
(tag_complete.search('type') === -1 ||
(tag_complete.search('type') > -1 &&
tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json)/) > -1))) {
if (!peek) {
this.record_tag(tag_check);
this.tag_type = 'SCRIPT';
}
} else if (tag_check === 'style' &&
(tag_complete.search('type') === -1 ||
(tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
if (!peek) {
this.record_tag(tag_check);
this.tag_type = 'STYLE';
}
} else if (tag_check.charAt(0) === '!') { //peek for <! comment
// for comments content is already correct.
if (!peek) {
this.tag_type = 'SINGLE';
this.traverse_whitespace();
}
} else if (!peek) {
if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
this.tag_type = 'END';
} else { //otherwise it's a start-tag
this.record_tag(tag_check); //push it on the tag stack
if (tag_check.toLowerCase() !== 'html') {
this.indent_content = true;
}
this.tag_type = 'START';
}
// Allow preserving of newlines after a start or end tag
if (this.traverse_whitespace()) {
this.space_or_wrap(content);
}
if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
this.print_newline(false, this.output);
if (this.output.length && this.output[this.output.length - 2] !== '\n') {
this.print_newline(true, this.output);
}
}
}
if (peek) {
this.pos = orig_pos;
this.line_char_count = orig_line_char_count;
}
return content.join(''); //returns fully formatted tag
};
this.get_comment = function(start_pos) { //function to return comment content in its entirety
// this is will have very poor perf, but will work for now.
var comment = '',
delimiter = '>',
matched = false;
this.pos = start_pos;
var input_char = this.input.charAt(this.pos);
this.pos++;
while (this.pos <= this.input.length) {
comment += input_char;
// only need to check for the delimiter if the last chars match
if (comment.charAt(comment.length - 1) === delimiter.charAt(delimiter.length - 1) &&
comment.indexOf(delimiter) !== -1) {
break;
}
// only need to search for custom delimiter for the first few characters
if (!matched && comment.length < 10) {
if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
delimiter = '<![endif]>';
matched = true;
} else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
delimiter = ']]>';
matched = true;
} else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
delimiter = ']>';
matched = true;
} else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
delimiter = '-->';
matched = true;
} else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment
delimiter = '}}';
matched = true;
} else if (comment.indexOf('<?') === 0) { // {{! handlebars comment
delimiter = '?>';
matched = true;
} else if (comment.indexOf('<%') === 0) { // {{! handlebars comment
delimiter = '%>';
matched = true;
}
}
input_char = this.input.charAt(this.pos);
this.pos++;
}
return comment;
};
function tokenMatcher(delimiter) {
var token = '';
var add = function (str) {
var newToken = token + str.toLowerCase();
token = newToken.length <= delimiter.length ? newToken : newToken.substr(newToken.length - delimiter.length, delimiter.length);
};
var doesNotMatch = function () {
return token.indexOf(delimiter) === -1;
};
return {
add: add,
doesNotMatch: doesNotMatch
};
}
this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety
if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) {
return '';
}
var input_char = '';
var content = '';
var space = true;
var delimiterMatcher = tokenMatcher(delimiter);
do {
if (this.pos >= this.input.length) {
return content;
}
input_char = this.input.charAt(this.pos);
this.pos++;
if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
if (!space) {
this.line_char_count--;
continue;
}
if (input_char === '\n' || input_char === '\r') {
content += '\n';
/* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
for (var i=0; i<this.indent_level; i++) {
content += this.indent_string;
}
space = false; //...and make sure other indentation is erased
*/
this.line_char_count = 0;
continue;
}
}
content += input_char;
delimiterMatcher.add(input_char);
this.line_char_count++;
space = true;
if (indent_handlebars && input_char === '{' && content.length && content.charAt(content.length - 2) === '{') {
// Handlebars expressions in strings should also be unformatted.
content += this.get_unformatted('}}');
// Don't consider when stopping for delimiters.
}
} while (delimiterMatcher.doesNotMatch());
return content;
};
this.get_token = function() { //initial handler for token-retrieval
var token;
if (this.last_token === 'TK_TAG_SCRIPT' || this.last_token === 'TK_TAG_STYLE') { //check if we need to format javascript
var type = this.last_token.substr(7);
token = this.get_contents_to(type);
if (typeof token !== 'string') {
return token;
}
return [token, 'TK_' + type];
}
if (this.current_mode === 'CONTENT') {
token = this.get_content();
if (typeof token !== 'string') {
return token;
} else {
return [token, 'TK_CONTENT'];
}
}
if (this.current_mode === 'TAG') {
token = this.get_tag();
if (typeof token !== 'string') {
return token;
} else {
var tag_name_type = 'TK_TAG_' + this.tag_type;
return [token, tag_name_type];
}
}
};
this.get_full_indent = function(level) {
level = this.indent_level + level || 0;
if (level < 1) {
return '';
}
return Array(level + 1).join(this.indent_string);
};
this.is_unformatted = function(tag_check, unformatted) {
//is this an HTML5 block-level link?
if (!this.Utils.in_array(tag_check, unformatted)) {
return false;
}
if (tag_check.toLowerCase() !== 'a' || !this.Utils.in_array('a', unformatted)) {
return true;
}
//at this point we have an tag; is its first child something we want to remain
//unformatted?
var next_tag = this.get_tag(true /* peek. */ );
// test next_tag to see if it is just html tag (no external content)
var tag = (next_tag || "").match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);
// if next_tag comes back but is not an isolated tag, then
// let's treat the 'a' tag as having content
// and respect the unformatted option
if (!tag || this.Utils.in_array(tag, unformatted)) {
return true;
} else {
return false;
}
};
this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
this.input = js_source || ''; //gets the input for the Parser
// HACK: newline parsing inconsistent. This brute force normalizes the input.
this.input = this.input.replace(/\r\n|[\r\u2028\u2029]/g, '\n')
this.output = [];
this.indent_character = indent_character;
this.indent_string = '';
this.indent_size = indent_size;
this.brace_style = brace_style;
this.indent_level = 0;
this.wrap_line_length = wrap_line_length;
this.line_char_count = 0; //count to see if wrap_line_length was exceeded
for (var i = 0; i < this.indent_size; i++) {
this.indent_string += this.indent_character;
}
this.print_newline = function(force, arr) {
this.line_char_count = 0;
if (!arr || !arr.length) {
return;
}
if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
if ((arr[arr.length - 1] !== '\n')) {
arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
}
arr.push('\n');
}
};
this.print_indentation = function(arr) {
for (var i = 0; i < this.indent_level; i++) {
arr.push(this.indent_string);
this.line_char_count += this.indent_string.length;
}
};
this.print_token = function(text) {
// Avoid printing initial whitespace.
if (this.is_whitespace(text) && !this.output.length) {
return;
}
if (text || text !== '') {
if (this.output.length && this.output[this.output.length - 1] === '\n') {
this.print_indentation(this.output);
text = ltrim(text);
}
}
this.print_token_raw(text);
};
this.print_token_raw = function(text) {
// If we are going to print newlines, truncate trailing
// whitespace, as the newlines will represent the space.
if (this.newlines > 0) {
text = rtrim(text);
}
if (text && text !== '') {
if (text.length > 1 && text.charAt(text.length - 1) === '\n') {
// unformatted tags can grab newlines as their last character
this.output.push(text.slice(0, -1));
this.print_newline(false, this.output);
} else {
this.output.push(text);
}
}
for (var n = 0; n < this.newlines; n++) {
this.print_newline(n > 0, this.output);
}
this.newlines = 0;
};
this.indent = function() {
this.indent_level++;
};
this.unindent = function() {
if (this.indent_level > 0) {
this.indent_level--;
}
};
};
return this;
}
/*_____________________--------------------_____________________*/
multi_parser = new Parser(); //wrapping functions Parser
multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
while (true) {
var t = multi_parser.get_token();
multi_parser.token_text = t[0];
multi_parser.token_type = t[1];
if (multi_parser.token_type === 'TK_EOF') {
break;
}
switch (multi_parser.token_type) {
case 'TK_TAG_START':
multi_parser.print_newline(false, multi_parser.output);
multi_parser.print_token(multi_parser.token_text);
if (multi_parser.indent_content) {
multi_parser.indent();
multi_parser.indent_content = false;
}
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_STYLE':
case 'TK_TAG_SCRIPT':
multi_parser.print_newline(false, multi_parser.output);
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_END':
//Print new line only if the tag has no content and has child
if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
var tag_name = multi_parser.token_text.match(/\w+/)[0];
var tag_extracted_from_last_output = null;
if (multi_parser.output.length) {
tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
}
if (tag_extracted_from_last_output === null ||
(tag_extracted_from_last_output[1] !== tag_name && !multi_parser.Utils.in_array(tag_extracted_from_last_output[1], unformatted))) {
multi_parser.print_newline(false, multi_parser.output);
}
}
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_SINGLE':
// Don't add a newline before elements that should remain unformatted.
var tag_check = multi_parser.token_text.match(/^\s*<([a-z-]+)/i);
if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
multi_parser.print_newline(false, multi_parser.output);
}
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_HANDLEBARS_ELSE':
// Don't add a newline if opening {{#if}} tag is on the current line
var foundIfOnCurrentLine = false;
for (var lastCheckedOutput=multi_parser.output.length-1; lastCheckedOutput>=0; lastCheckedOutput--) {
if (multi_parser.output[lastCheckedOutput] === '\n') {
break;
} else {
if (multi_parser.output[lastCheckedOutput].match(/{{#if/)) {
foundIfOnCurrentLine = true;
break;
}
}
}
if (!foundIfOnCurrentLine) {
multi_parser.print_newline(false, multi_parser.output);
}
multi_parser.print_token(multi_parser.token_text);
if (multi_parser.indent_content) {
multi_parser.indent();
multi_parser.indent_content = false;
}
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_HANDLEBARS_COMMENT':
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'TAG';
break;
case 'TK_CONTENT':
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'TAG';
break;
case 'TK_STYLE':
case 'TK_SCRIPT':
if (multi_parser.token_text !== '') {
multi_parser.print_newline(false, multi_parser.output);
var text = multi_parser.token_text,
_beautifier,
script_indent_level = 1;
if (multi_parser.token_type === 'TK_SCRIPT') {
_beautifier = typeof js_beautify === 'function' && js_beautify;
} else if (multi_parser.token_type === 'TK_STYLE') {
_beautifier = typeof css_beautify === 'function' && css_beautify;
}
if (options.indent_scripts === "keep") {
script_indent_level = 0;
} else if (options.indent_scripts === "separate") {
script_indent_level = -multi_parser.indent_level;
}
var indentation = multi_parser.get_full_indent(script_indent_level);
if (_beautifier) {
// call the Beautifier if avaliable
var Child_options = function() {
this.eol = '\n';
};
Child_options.prototype = options;
var child_options = new Child_options();
text = _beautifier(text.replace(/^\s*/, indentation), child_options);
} else {
// simply indent the string otherwise
var white = text.match(/^\s*/)[0];
var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
var reindent = multi_parser.get_full_indent(script_indent_level - _level);
text = text.replace(/^\s*/, indentation)
.replace(/\r\n|\r|\n/g, '\n' + reindent)
.replace(/\s+$/, '');
}
if (text) {
multi_parser.print_token_raw(text);
multi_parser.print_newline(true, multi_parser.output);
}
}
multi_parser.current_mode = 'TAG';
break;
default:
// We should not be getting here but we don't want to drop input on the floor
// Just output the text and move on
if (multi_parser.token_text !== '') {
multi_parser.print_token(multi_parser.token_text);
}
break;
}
multi_parser.last_token = multi_parser.token_type;
multi_parser.last_text = multi_parser.token_text;
}
var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');
// establish end_with_newline
if (end_with_newline) {
sweet_code += '\n';
}
if (eol != '\n') {
sweet_code = sweet_code.replace(/[\n]/g, eol);
}
return sweet_code;
}
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define(["require", "./beautify", "./beautify-css"], function(requireamd) {
var js_beautify = requireamd("./beautify");
var css_beautify = requireamd("./beautify-css");
return {
html_beautify: function(html_source, options) {
return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
}
};
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var html_beautify = require("beautify").html_beautify`.
var js_beautify = require('./beautify.js');
var css_beautify = require('./beautify-css.js');
exports.html_beautify = function(html_source, options) {
return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
};
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.html_beautify = function(html_source, options) {
return style_html(html_source, options, window.js_beautify, window.css_beautify);
};
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.html_beautify = function(html_source, options) {
return style_html(html_source, options, global.js_beautify, global.css_beautify);
};
}
}());
\ No newline at end of file
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*
* Mock for the JS formatter. Ignore formatting of JS content in HTML.
*/
export function js_beautify(js_source_text: string, options: any) {
// no formatting
return js_source_text;
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 stream = require('stream');
var DefaultSize:number = 8192;
var ContentLength:string = 'Content-Length: ';
var ContentLengthSize:number = Buffer.byteLength(ContentLength, 'utf8');
var Blank:number = new Buffer(' ', 'utf8')[0];
var BackslashR:number = new Buffer('\r', 'utf8')[0];
var BackslashN:number = new Buffer('\n', 'utf8')[0];
class ProtocolBuffer {
private index:number;
private buffer:Buffer;
constructor() {
this.index = 0;
this.buffer = new Buffer(DefaultSize);
}
public append(data:string | Buffer):void {
var toAppend:Buffer = null;
if (Buffer.isBuffer(data)) {
toAppend = <Buffer>data;
} else {
toAppend = new Buffer(<string>data, 'utf8');
}
if (this.buffer.length - this.index >= toAppend.length) {
toAppend.copy(this.buffer, this.index, 0, toAppend.length);
} else {
var newSize = (Math.ceil((this.index + toAppend.length) / DefaultSize) + 1) * DefaultSize;
if (this.index === 0) {
this.buffer = new Buffer(newSize);
toAppend.copy(this.buffer, 0, 0, toAppend.length);
} else {
this.buffer = Buffer.concat([this.buffer.slice(0, this.index), toAppend], newSize);
}
}
this.index+= toAppend.length;
}
public tryReadContentLength():number {
var result = -1;
var current = 0;
// we are utf8 encoding...
while (current < this.index && (this.buffer[current] === Blank || this.buffer[current] === BackslashR || this.buffer[current] === BackslashN)) {
current++;
}
if (this.index < current + ContentLengthSize) {
return result;
}
current += ContentLengthSize;
var start = current;
while(current < this.index && this.buffer[current] !== BackslashR) {
current++;
}
if (current + 3 >= this.index || this.buffer[current + 1] !== BackslashN || this.buffer[current + 2] !== BackslashR || this.buffer[current + 3] !== BackslashN) {
return result;
}
var data = this.buffer.toString('utf8', start, current);
result = parseInt(data);
this.buffer = this.buffer.slice(current + 4);
this.index = this.index - (current + 4);
return result;
}
public tryReadContent(length:number):string {
if (this.index < length) {
return null;
}
var result = this.buffer.toString('utf8', 0, length);
var sourceStart = length;
while(sourceStart < this.index && (this.buffer[sourceStart] === BackslashR || this.buffer[sourceStart] === BackslashN)) {
sourceStart++;
}
this.buffer.copy(this.buffer, 0, sourceStart);
this.index = this.index - sourceStart;
return result;
}
public tryReadLine():string {
var end:number = 0;
while (end < this.index && this.buffer[end] !== BackslashR && this.buffer[end] !== BackslashN) {
end++;
}
if (end >= this.index) {
return null;
}
var result = this.buffer.toString('utf8', 0, end);
while (end < this.index && (this.buffer[end] === BackslashR || this.buffer[end] === BackslashN)) {
end++;
}
if (this.index === end) {
this.index = 0;
} else {
this.buffer.copy(this.buffer, 0, end);
this.index = this.index - end;
}
return result;
}
public get numberOfBytes():number {
return this.index;
}
}
export enum ReaderType {
Length = 0,
Line = 1
}
export interface ICallback<T> {
(data:T):void;
}
export class Reader<T> {
private readable:stream.Readable;
private callback:ICallback<T>;
private buffer:ProtocolBuffer;
private nextMessageLength:number;
public constructor(readable:stream.Readable, callback:ICallback<T>, type:ReaderType = ReaderType.Length) {
this.readable = readable;
this.buffer = new ProtocolBuffer();
this.callback = callback;
this.nextMessageLength = -1;
if (type === ReaderType.Length) {
this.readable.on('data', (data:Buffer) => {
this.onLengthData(data);
});
} else if (type === ReaderType.Line) {
this.readable.on('data', (data:Buffer) => {
this.onLineData(data);
});
}
}
private onLengthData(data:Buffer):void {
this.buffer.append(data);
while(true) {
if (this.nextMessageLength === -1) {
this.nextMessageLength = this.buffer.tryReadContentLength();
if (this.nextMessageLength === -1) {
return;
}
}
var msg = this.buffer.tryReadContent(this.nextMessageLength);
if (msg === null) {
return;
}
this.nextMessageLength = -1;
var json = JSON.parse(msg);
this.callback(json);
}
}
private onLineData(data:Buffer):void {
this.buffer.append(data);
while(true) {
var msg = this.buffer.tryReadLine();
if (msg === null) {
return;
}
this.callback(JSON.parse(msg));
}
}
}
export class Writer<T> {
private writable:stream.Writable;
public constructor(writable:stream.Writable) {
this.writable = writable;
}
public write(msg:T):void {
var json = JSON.stringify(msg);
var buffer:string[] = [
ContentLength,
Buffer.byteLength(json, 'utf8').toString(),
'\r\n\r\n',
json,
'\r\n'
];
this.writable.write(buffer.join(''), 'utf8');
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册