diff --git a/README.md b/README.md
index e5d6dba..dd67610 100644
--- a/README.md
+++ b/README.md
@@ -81,7 +81,7 @@ Show a horizontal line between the brackets? Enabled by default
>`"bracket-pair-colorizer-2.scopeLineRelativePosition"`
Disable this to show the vertical line in column 0
![Scope Line](images/no-relative.png "Gutter Brackets Example")
-
+
>`"bracket-pair-colorizer-2.scopeLineCSS"`
Choose a border style to highlight the active scope. Use `{color}` to match the existing bracket color
@@ -97,6 +97,28 @@ Choose a border style to highlight the active scope. Use `{color}` to match the
>`"bracket-pair-colorizer-2.excludedLanguages"`
Exclude a language from being colorized
+>`"bracket-pair-colorizer-2.htmlStyleTagsLanguages"`
+Languages which should colorize HTML-style tags e.g. `
` matches to `
`. This will not affect the behavior of other types of brackets
+![HTML-Style Tags](images/htmlTags.png "HTML-Style Tags Example")
+
+```json
+"bracket-pair-colorizer-2.htmlStyleTagsLanguages": [
+ "html",
+ "xml"
+]
+```
+Exclude a language from being colorized
+
+>`"bracket-pair-colorizer-2.htmlIgnoredTags"`
+Tags which do not have a corresponding close and therefore should be ignored by the HTML colorization e.g. `
`
+
+```json
+"bracket-pair-colorizer-2.htmlIgnoredTags": [
+ "br",
+ "hr"
+]
+```
+
### Commands
These commands will expand/undo the cursor selection to the next scope
diff --git a/images/htmlTags.png b/images/htmlTags.png
new file mode 100644
index 0000000..2269958
Binary files /dev/null and b/images/htmlTags.png differ
diff --git a/package.json b/package.json
index 14d0351..453621e 100644
--- a/package.json
+++ b/package.json
@@ -159,6 +159,24 @@
"default": [],
"description": "Don't colorize files of these languages",
"scope": "window"
+ },
+ "bracket-pair-colorizer-2.htmlStyleTagsLanguages": {
+ "type": "array",
+ "default": [
+ "html",
+ "xml"
+ ],
+ "description": "Languages which should colorize HTML-style tags e.g. matches to
. This will not affect the behavior of other types of brackets",
+ "scope": "window"
+ },
+ "bracket-pair-colorizer-2.htmlIgnoredTags": {
+ "type": "array",
+ "default": [
+ "br",
+ "hr"
+ ],
+ "description": "Tags which do not have a corresponding close therefore should be ignored by the HTML colorization e.g.
",
+ "scope": "window"
}
}
}
diff --git a/src/bracketUtil.ts b/src/bracketUtil.ts
index 7bc1573..de35d98 100644
--- a/src/bracketUtil.ts
+++ b/src/bracketUtil.ts
@@ -13,7 +13,7 @@ export function getRegexForBrackets(input: ISimpleInternalBracket[]): RegExp {
return createBracketOrRegExp(pieces);
}
-function createBracketOrRegExp(pieces: string[]): RegExp {
+export function createBracketOrRegExp(pieces: string[]): RegExp {
const regexStr = `(${pieces.map(prepareBracketForRegExp).join(")|(")})`;
return createRegExp(regexStr, true, { global: true });
}
diff --git a/src/documentDecoration.ts b/src/documentDecoration.ts
index 8b45db6..0b5eb6e 100644
--- a/src/documentDecoration.ts
+++ b/src/documentDecoration.ts
@@ -8,6 +8,9 @@ import Settings from "./settings";
import TextLine from "./textLine";
import { ignoreBracketsInToken, LineTokens } from "./vscodeFiles";
import { TextDocumentContentChangeEvent } from "vscode";
+import { createBracketOrRegExp } from "./bracketUtil";
+
+type Match = { content: string, index: number };
export default class DocumentDecoration {
public readonly settings: Settings;
@@ -320,7 +323,9 @@ export default class DocumentDecoration {
const tokens = tokenized.tokens;
const lineTokens = new LineTokens(tokens, newText);
- const matches = new Array<{ content: string, index: number }>();
+ const matches = new Array();
+ const htmlMatches = { open: new Array(), close: new Array(), ignore: new Array() };
+
const count = lineTokens.getCount();
for (let i = 0; i < count; i++) {
const tokenType = lineTokens.getStandardTokenType(i);
@@ -330,24 +335,68 @@ export default class DocumentDecoration {
const currentTokenText = newText.substring(searchStartOffset, searchEndOffset);
- let result: RegExpExecArray | null;
- // tslint:disable-next-line:no-conditional-assignment
- while ((result = this.languageConfig.regex.exec(currentTokenText)) !== null) {
- matches.push({ content: result[0], index: result.index + searchStartOffset });
+ this.pushMatches(currentTokenText, this.languageConfig.regex, searchStartOffset, matches);
+ if (this.languageConfig.colorHtmlStyleTags) {
+ this.pushMatches(
+ currentTokenText, createBracketOrRegExp(["", "/>"]), searchStartOffset, htmlMatches.close
+ );
+ this.pushMatches(
+ currentTokenText, createBracketOrRegExp(["<"]), searchStartOffset, htmlMatches.open
+ );
+ this.pushMatches(
+ currentTokenText,
+ createBracketOrRegExp(
+ this.settings.htmlIgnoredTags.map((value: string) => { return "<" + value })
+ ), searchStartOffset, htmlMatches.ignore
+ );
}
}
}
- const newLine = new TextLine(tokenized.ruleStack, previousLineState, index);
+ // Filter out the overlap between the open tags and the other tags
+ // (also protects against a potential <> in languageConfig)
+ const htmlCloseIgnoreIndexes = matches.concat(htmlMatches.close, htmlMatches.ignore)
+ .map((value: { index: number }) => { return value.index });
+ htmlMatches.open = htmlMatches.open.filter(function (value: { index: number }) {
+ return htmlCloseIgnoreIndexes.indexOf(value.index) == -1
+ });
+
+ // Collate all matches
+ let tokensToAdd = new Array<{ content: string, index: number, key: number, open: boolean }>();
for (const match of matches) {
const lookup = this.languageConfig.bracketToId.get(match.content);
if (lookup) {
- newLine.AddToken(match.content, match.index, lookup.key, lookup.open);
+ tokensToAdd.push({ content: match.content, index: match.index, key: lookup.key, open: lookup.open });
}
}
+ tokensToAdd = tokensToAdd.concat(
+ htmlMatches.open.map((match: Match) => {
+ return { content: match.content, index: match.index, key: this.languageConfig.htmlKey, open: true }
+ }),
+ htmlMatches.close.map((match: Match) => {
+ return { content: match.content, index: match.index, key: this.languageConfig.htmlKey, open: false }
+ })
+ );
+
+ // Force index order so that the bracket stack is created correctly
+ tokensToAdd.sort((a, b) => a.index - b.index);
+
+ const newLine = new TextLine(tokenized.ruleStack, previousLineState, index);
+ for (const token of tokensToAdd) {
+ newLine.AddToken(token.content, token.index, token.key, token.open);
+ }
+
return newLine;
}
+ private pushMatches(text: string, regex: RegExp, indexOffset: number, matches: Array) {
+ let result: RegExpExecArray | null;
+ // tslint:disable-next-line:no-conditional-assignment
+ while ((result = regex.exec(text)) !== null) {
+ matches.push({ content: result[0], index: result.index + indexOffset });
+ }
+ }
+
private setOverLineDecoration(
bracket: Bracket,
event: vscode.TextEditorSelectionChangeEvent,
diff --git a/src/languageConfig.ts b/src/languageConfig.ts
index b481c8a..22a4418 100644
--- a/src/languageConfig.ts
+++ b/src/languageConfig.ts
@@ -4,10 +4,23 @@ export default class LanguageConfig {
public readonly grammar: IGrammar;
public readonly regex: RegExp;
public readonly bracketToId: Map;
+ // Key to be used for html brackets
+ public readonly htmlKey: number;
+ public readonly colorHtmlStyleTags: boolean;
- constructor(grammar: IGrammar, regex: RegExp, bracketToId: Map) {
+ constructor(
+ grammar: IGrammar, regex: RegExp, bracketToId: Map,
+ colorHtmlStyleTags: boolean
+ ) {
this.grammar = grammar;
this.regex = regex;
this.bracketToId = bracketToId;
+ let htmlKey = -1;
+ if (colorHtmlStyleTags) {
+ bracketToId.forEach((value: { key: number }) => { if (value.key > htmlKey) { htmlKey = value.key } });
+ htmlKey++;
+ }
+ this.htmlKey = htmlKey;
+ this.colorHtmlStyleTags = colorHtmlStyleTags;
}
}
diff --git a/src/multipleIndexes.ts b/src/multipleIndexes.ts
index 340bed9..cc0846a 100644
--- a/src/multipleIndexes.ts
+++ b/src/multipleIndexes.ts
@@ -32,6 +32,10 @@ export default class MultipleBracketGroups implements IBracketManager {
this.allLinesOpenBracketStack[value.key] = [];
this.previousOpenBracketColorIndexes[value.key] = 0;
}
+ if (languageConfig.colorHtmlStyleTags) {
+ this.allLinesOpenBracketStack[languageConfig.htmlKey] = [];
+ this.previousOpenBracketColorIndexes[languageConfig.htmlKey] = 0;
+ }
}
}
diff --git a/src/settings.ts b/src/settings.ts
index c853f35..10649f4 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -5,7 +5,7 @@ import TextMateLoader from "./textMateLoader";
import { ThemeColor } from "vscode";
export default class Settings {
- public readonly TextMateLoader = new TextMateLoader();
+ public readonly TextMateLoader: TextMateLoader;
public readonly bracketDecorations: Map;
public readonly colorMode: ColorMode;
public readonly contextualParsing: boolean;
@@ -22,6 +22,7 @@ export default class Settings {
public readonly colors: string[];
public readonly unmatchedScopeColor: string;
public readonly excludedLanguages: Set;
+ public readonly htmlIgnoredTags: string[];
public isDisposed = false;
private readonly gutterIcons: GutterIconManager;
private readonly activeBracketCSSElements: string[][];
@@ -129,6 +130,7 @@ export default class Settings {
}
this.colors = configuration.get("colors") as string[];
+
if (!Array.isArray(this.colors)) {
throw new Error("colors is not an array");
}
@@ -142,6 +144,20 @@ export default class Settings {
}
this.excludedLanguages = new Set(excludedLanguages);
+
+ this.htmlIgnoredTags = configuration.get("htmlIgnoredTags") as string[];
+
+ if (!Array.isArray(this.htmlIgnoredTags)) {
+ throw new Error("htmlIgnoredTags is not an array");
+ }
+
+ let htmlStyleTagsLanguages = configuration.get("htmlStyleTagsLanguages") as string[];
+
+ if (!Array.isArray(htmlStyleTagsLanguages)) {
+ throw new Error("htmlStyleTagsLanguages is not an array");
+ }
+
+ this.TextMateLoader = new TextMateLoader(htmlStyleTagsLanguages);
}
public dispose() {
diff --git a/src/textMateLoader.ts b/src/textMateLoader.ts
index 04a1bdf..29d0168 100644
--- a/src/textMateLoader.ts
+++ b/src/textMateLoader.ts
@@ -15,10 +15,12 @@ export class TextMateLoader {
private readonly vsctm: any;
private readonly oniguruma: any;
private readonly languageConfigs = new Map();
- constructor() {
+ private readonly htmlStyleTagsLanguages: string[];
+ constructor(htmlStyleTagsLanguages: string[] = []) {
this.initializeGrammars();
this.vsctm = this.loadTextMate();
this.oniguruma = this.loadOniguruma();
+ this.htmlStyleTagsLanguages = htmlStyleTagsLanguages;
}
public tryGetLanguageConfig(languageID: string) {
@@ -105,7 +107,10 @@ export class TextMateLoader {
}
const regex = getRegexForBrackets(mappedBrackets);
- this.languageConfigs.set(languageID, new LanguageConfig(grammar, regex, bracketToId));
+ const colorHtmlStyleTags = this.htmlStyleTagsLanguages.indexOf(languageID) > -1;
+ this.languageConfigs.set(
+ languageID, new LanguageConfig(grammar, regex, bracketToId, colorHtmlStyleTags)
+ );
}
}
return grammar;