diff --git a/extensions/html/client/src/htmlMain.ts b/extensions/html/client/src/htmlMain.ts
index 303d87b7a2f..5453ea343fd 100644
--- a/extensions/html/client/src/htmlMain.ts
+++ b/extensions/html/client/src/htmlMain.ts
@@ -62,4 +62,19 @@ export function activate(context: ExtensionContext) {
}
],
});
+
+ languages.setLanguageConfiguration('handlebars', {
+ wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|('(?:[^\\\']*(?:\\.)?)*'?)|[^\s<>={}\[\],]+/,
+ onEnterRules:[
+ {
+ beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
+ afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>$/i,
+ action: { indentAction: IndentAction.IndentOutdent }
+ },
+ {
+ beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
+ action: { indentAction: IndentAction.Indent }
+ }
+ ],
+ });
}
\ No newline at end of file
diff --git a/extensions/html/server/src/service/parser/htmlScanner.ts b/extensions/html/server/src/service/parser/htmlScanner.ts
index 2bb5c33ac7b..b6ea9566b5d 100644
--- a/extensions/html/server/src/service/parser/htmlScanner.ts
+++ b/extensions/html/server/src/service/parser/htmlScanner.ts
@@ -206,6 +206,10 @@ export interface Scanner {
getScannerState(): ScannerState;
}
+const htmlScriptContents = {
+ 'text/x-handlebars-template': true
+};
+
export function createScanner(input: string, initialOffset = 0, initialState: ScannerState = ScannerState.WithinContent) : Scanner {
let stream = new MultiLineStream(input, initialOffset);
@@ -216,6 +220,8 @@ export function createScanner(input: string, initialOffset = 0, initialState: Sc
let hasSpaceAfterTag: boolean;
let lastTag: string;
+ let lastAttributeName: string;
+ let lastTypeValue: string;
function nextElementName(): string {
return stream.advanceIfRegExp(/^[_:\w][_:\w-.\d]*/).toLowerCase();
@@ -299,6 +305,8 @@ export function createScanner(input: string, initialOffset = 0, initialState: Sc
break;
case ScannerState.AfterOpeningStartTag:
lastTag = nextElementName();
+ lastTypeValue = null;
+ lastAttributeName = null;
if (lastTag.length > 0) {
hasSpaceAfterTag = false;
state = ScannerState.WithinTag;
@@ -316,8 +324,8 @@ export function createScanner(input: string, initialOffset = 0, initialState: Sc
return finishToken(offset, TokenType.Whitespace);
}
if (hasSpaceAfterTag) {
- let name = nextAttributeName();
- if (name.length > 0) {
+ lastAttributeName = nextAttributeName();
+ if (lastAttributeName.length > 0) {
state = ScannerState.AfterAttributeName;
hasSpaceAfterTag = false;
return finishToken(offset, TokenType.AttributeName);
@@ -329,7 +337,12 @@ export function createScanner(input: string, initialOffset = 0, initialState: Sc
}
if (stream.advanceIfChar(_RAN)) { // >
if (lastTag === 'script') {
- state = ScannerState.WithinScriptContent;
+ if (lastTypeValue && htmlScriptContents[lastTypeValue]) {
+ // stay in html
+ state = ScannerState.WithinContent;
+ } else {
+ state = ScannerState.WithinScriptContent;
+ }
} else if (lastTag === 'style') {
state = ScannerState.WithinStyleContent;
} else {
@@ -357,6 +370,9 @@ export function createScanner(input: string, initialOffset = 0, initialState: Sc
}
let attributeValue = stream.advanceIfRegExp(/^[^\s"'`=<>]+/);
if (attributeValue.length > 0) {
+ if (lastAttributeName === 'type') {
+ lastTypeValue = attributeValue;
+ }
state = ScannerState.WithinTag;
hasSpaceAfterTag = false;
return finishToken(offset, TokenType.AttributeValue);
@@ -367,6 +383,9 @@ export function createScanner(input: string, initialOffset = 0, initialState: Sc
if (stream.advanceUntilChar(ch)) {
stream.advance(1); // consume quote
}
+ if (lastAttributeName === 'type') {
+ lastTypeValue = stream.getSource().substring(offset + 1, stream.pos() - 1);
+ }
state = ScannerState.WithinTag;
hasSpaceAfterTag = false;
return finishToken(offset, TokenType.AttributeValue);
diff --git a/extensions/html/server/src/service/test/completion.test.ts b/extensions/html/server/src/service/test/completion.test.ts
index 04237fe03f3..f369bb3f408 100644
--- a/extensions/html/server/src/service/test/completion.test.ts
+++ b/extensions/html/server/src/service/test/completion.test.ts
@@ -269,6 +269,17 @@ suite('HTML Completion', () => {
], testDone);
});
+ suite('Handlevar Completion', (testDone) => {
+ run([
+
+ testCompletionFor('' , {
+ items: [
+ { label: 'div', resultText: '' },
+ ]
+ })
+ ], testDone);
+ });
+
test('Intellisense aria', function (testDone): any {
let expectedAriaAttributes = [
{ label: 'aria-activedescendant' },
diff --git a/src/vs/editor/editor.main.ts b/src/vs/editor/editor.main.ts
index 0fbf6472d3b..dbbf54ef04e 100644
--- a/src/vs/editor/editor.main.ts
+++ b/src/vs/editor/editor.main.ts
@@ -12,6 +12,7 @@ import 'vs/editor/contrib/quickOpen/browser/quickCommand';
import 'vs/languages/languages.main';
import 'vs/languages/php/common/php.contribution';
import 'vs/languages/html/common/html.contribution';
+import 'vs/languages/handlebars/common/handlebars.contribution';
import {createMonacoBaseAPI} from 'vs/editor/common/standalone/standaloneBase';
import {createMonacoEditorAPI} from 'vs/editor/browser/standalone/standaloneEditor';
diff --git a/src/vs/languages/languages.main.ts b/src/vs/languages/languages.main.ts
index c8d49d4dda4..ecaf274b2af 100644
--- a/src/vs/languages/languages.main.ts
+++ b/src/vs/languages/languages.main.ts
@@ -6,5 +6,4 @@
'use strict';
-import 'vs/languages/handlebars/common/handlebars.contribution';
import 'vs/languages/razor/common/razor.contribution';