irongit

A Regex file extension that makes it way easier to edit large regex.

regex-file/src/highlighter.ts
184 lines5.1 KBJavaScript
1import * as vscode from "vscode";
2import { output } from "./output";
3import {
4 editorsContainRegexFile,
5 getNonRegexEditors,
6 getRegexEditor,
7 timeit,
8} from "./utils";
9import { regexButtons } from "./regexButtons";
10import decorationSelector from "./decorationSelector";
11import { getConfig } from "./global";
12
13type HighlightGroup = {
14 decorations: vscode.DecorationOptions[];
15 decorationType: vscode.TextEditorDecorationType;
16};
17
18class Highlighter {
19 context: vscode.ExtensionContext | undefined;
20 highlightGroups: { [key: string]: HighlightGroup } = {};
21 forceStop = false;
22
23 constructor() {}
24
25 activate(context: vscode.ExtensionContext) {
26 // output.log("Highlighter activated");
27 this.context = context;
28 }
29
30 getDecorationTypes() {
31 return Object.values(this.highlightGroups).map(
32 (group) => group.decorationType
33 );
34 }
35
36 @timeit
37 reset() {
38 output.clear();
39 this.removeHighlights();
40 this.highlightGroups = {};
41 decorationSelector.reset();
42 }
43
44 clearRanges() {
45 for (const key of Object.keys(this.highlightGroups)) {
46 this.highlightGroups[key].decorations = [];
47 }
48 }
49
50 updateOrCreateHighlightGroup(key: string, range: vscode.Range) {
51 const decoration = { range, hoverMessage: key };
52 // output.log(`Highlighting ${key}`);
53 if (this.highlightGroups[key]) {
54 this.highlightGroups[key].decorations.push(decoration);
55 } else {
56 const decorationType = decorationSelector.select();
57 this.highlightGroups[key] = {
58 decorations: [decoration],
59 decorationType: decorationType,
60 };
61 }
62 }
63
64 applyXFlag(regex: string) {
65 return regex.replace(/(?<!\\)#.*$/gm, "").replace(/\s+/g, "");
66 }
67
68 matchOnEditor(regex: string, document: vscode.TextDocument) {
69 this.forceStop = false;
70 const flags = regexButtons.getFlagString();
71 const timeout = getConfig().get("timeout") as number;
72 const startTime = Date.now();
73 if (regexButtons.hasXFlag()) {
74 regex = this.applyXFlag(regex);
75 }
76 try {
77 new RegExp(regex, flags);
78 } catch (err) {
79 output.log(`Invalid regex: ${regex}`);
80 return {};
81 }
82 const re = new RegExp(regex, flags);
83 const matches = document.getText().matchAll(re);
84 let i = 0;
85 const maxMatches = getConfig().get("maxMatches") as number;
86 for (const match of matches) {
87 if (
88 i >= maxMatches ||
89 Date.now() - startTime > timeout ||
90 this.forceStop
91 ) {
92 this.forceStop = false;
93 break;
94 }
95 if (match.index === undefined) {
96 continue;
97 }
98 const start = document.positionAt(match.index);
99 const end = document.positionAt(match.index + match[0].length);
100 this.updateOrCreateHighlightGroup("match", new vscode.Range(start, end));
101
102 for (const groupName in match.groups) {
103 const anyMatch = match as any;
104 if (
105 anyMatch.indices &&
106 anyMatch.indices.groups &&
107 anyMatch.indices.groups[groupName]
108 ) {
109 const groupIndices = anyMatch.indices.groups[groupName];
110 const start = document.positionAt(groupIndices[0]);
111 const end = document.positionAt(groupIndices[1]);
112 this.updateOrCreateHighlightGroup(
113 groupName,
114 new vscode.Range(start, end)
115 );
116 } else {
117 // output.log(groupName);
118 }
119 }
120 i++;
121 }
122 output.log(`Found ${i} matches`);
123 }
124
125 matchRegexGroups(document: vscode.TextDocument) {
126 const re = /(?<=\(\?<).+?(?=\>)/g;
127 const matches = document.getText().matchAll(re);
128 let i = 0;
129 const maxMatches = getConfig().get("maxMatches") as number;
130 for (const match of matches) {
131 if (i >= maxMatches) {
132 break;
133 }
134 if (match.index === undefined) {
135 continue;
136 }
137 const start = document.positionAt(match.index);
138 const end = document.positionAt(match.index + match[0].length);
139 this.updateOrCreateHighlightGroup(match[0], new vscode.Range(start, end));
140 i++;
141 }
142 }
143
144 @timeit
145 highlightMatches(editor: vscode.TextEditor) {
146 for (const key of Object.keys(this.highlightGroups)) {
147 const group = this.highlightGroups[key];
148 editor.setDecorations(group.decorationType, group.decorations);
149 }
150 }
151
152 removeHighlights() {
153 for (const editor of vscode.window.visibleTextEditors) {
154 for (const key of Object.keys(this.highlightGroups)) {
155 const group = this.highlightGroups[key];
156 editor.setDecorations(group.decorationType, []);
157 }
158 }
159 }
160
161 run() {
162 this.reset();
163 const regexEditor = getRegexEditor();
164 if (!regexEditor) {
165 return;
166 }
167 this.updateOrCreateHighlightGroup("match", new vscode.Range(0, 0, 0, 0));
168 this.matchRegexGroups(regexEditor.document);
169 this.highlightMatches(regexEditor);
170 this.clearRanges();
171 const nonRegexEditors = getNonRegexEditors();
172 output.log(`Highlighting ${nonRegexEditors.length} editors`);
173 for (const editor of nonRegexEditors) {
174 this.matchOnEditor(regexEditor.document.getText(), editor.document);
175 this.highlightMatches(editor);
176 }
177 }
178
179 dispose() {
180 this.reset();
181 }
182}
183
184export const highlighter = new Highlighter();