-
Notifications
You must be signed in to change notification settings - Fork 2
/
extension.ts
184 lines (165 loc) · 7.69 KB
/
extension.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import * as vscode from 'vscode';
// called when vs code is activated
export function activate(context: vscode.ExtensionContext) {
let activeEditor = vscode.window.activeTextEditor;
let timeout: NodeJS.Timer | undefined = undefined;
const decorationType = vscode.window.createTextEditorDecorationType({
borderWidth: '1px',
borderStyle: 'solid',
overviewRulerColor: 'blue',
overviewRulerLane: vscode.OverviewRulerLane.Right,
light: { // used in light color themes
borderColor: 'darkblue'
},
dark: { // used in dark color themes
borderColor: 'lightblue'
},
textDecoration: 'wavy underline'
});
if (activeEditor) {
triggerUpdateDecorations();
}
vscode.window.onDidChangeActiveTextEditor(editor => {
activeEditor = editor;
if (editor) {
triggerUpdateDecorations();
randomIeReminder();
}
}, null, context.subscriptions);
vscode.workspace.onDidChangeTextDocument(event => {
if (activeEditor && event.document === activeEditor.document) {
triggerUpdateDecorations();
}
}, null, context.subscriptions);
function triggerUpdateDecorations() {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
timeout = setTimeout(updateDecorations, 500);
}
function updateDecorations() {
if (!activeEditor) {
return;
}
let rangesToDecorate: vscode.DecorationOptions[] = [];
check_ifIdWithoutNotNull(rangesToDecorate);
check_ifAssignInsteadOfEquals(rangesToDecorate);
check_httpGet(rangesToDecorate);
check_scopeIdentitySQL(rangesToDecorate);
check_rowCountSQL(rangesToDecorate);
check_encryptedSQL(rangesToDecorate);
check_consoleLog(rangesToDecorate);
check_numberOfId(rangesToDecorate);
check_todoComment(rangesToDecorate);
activeEditor.setDecorations(decorationType, rangesToDecorate);
}
function check_ifIdWithoutNotNull(rangesToDecorate: vscode.DecorationOptions[]) {
let regex = /if ?\(([^=)]*?I[dD](?!\.)\b) ?[^=<>\r\n]*?\)/g;
let hoverMessage = 'An ID of 0 would evaluate to falsy. !(0) is truthy. Consider: ${match[1]} != null';
let popupMessage = 'ID of 0 would evaluate to falsy. Consider adding "!= null" for if-statements containing IDs: ${errors.join(", ")}';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function check_ifAssignInsteadOfEquals(rangesToDecorate: vscode.DecorationOptions[]) {
let regex = /if ?\((\w+[^=!<>\r\n])*=([^=\r\n]*)\)/g;
let hoverMessage = 'Should be ${match[1]} == ${match[2]} or ${match[1]} === ${match[2]}';
let popupMessage = '= should be == or === in if-statements: ${errors.join(", ")}';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function check_httpGet(rangesToDecorate: vscode.DecorationOptions[]) {
let regex = /\$http\.get\(/g;
let hoverMessage = 'For IE, use $http.post instead of $http.get, even for GET, and include an empty request object';
let popupMessage = 'Use $http.post instead of $http.get (for IE)';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function check_scopeIdentitySQL(rangesToDecorate: vscode.DecorationOptions[]) {
let regex = /SCOPE_IDENTITY\(\)/g;
let hoverMessage = "SCOPE_IDENTITY() is not thread-safe in SQL. Try using the output of an INSERT, e.g.: INSERT INTO MyTable (letters, numbers) OUTPUT INSERTED.[ID] VALUES ('ABC', 123)";
let popupMessage = 'SCOPE_IDENTITY() is not thread-safe in SQL. Try using OUTPUT INSERTED.[ID] inside an INSERT.';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function check_rowCountSQL(rangesToDecorate: vscode.DecorationOptions[]) {
let regex = /@@ROWCOUNT/g;
let hoverMessage = '@@ROWCOUNT is not very thread-safe in SQL. Try this: NOT EXISTS(SELECT 1 FROM ...)';
let popupMessage = 'Instead of @@ROWCOUNT, try NOT EXISTS(SELECT 1 FROM ...)';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function check_encryptedSQL(rangesToDecorate: vscode.DecorationOptions[]) {
// cover "AS BEGIN"
// cover "AS--WITH ENCRYPTION BEGIN" but missing "--comment: sp_password" after it
let regex = /(AS(?!--WITH ENCRYPTION)[\r\n\s\t]+BEGIN[\r\n\s\t]*|AS *\t?--WITH ENCRYPTION[\r\n\s\t]+BEGIN[\r\n\s\t]*(?! *?\t?-- *?comment: sp_password).+)/g;
let hoverMessage = 'AS BEGIN should have "AS--WITH ENCRYPTION" and "BEGIN -- comment: sp_password"';
let popupMessage = 'AS BEGIN should have "AS--WITH ENCRYPTION" and "BEGIN -- comment: sp_password"';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function check_consoleLog(rangesToDecorate: vscode.DecorationOptions[]) {
let regex = /console\.log/g;
let hoverMessage = 'Remember to remove console log outputs.';
let popupMessage = 'A wild console.log() was found!';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function check_numberOfId(rangesToDecorate: vscode.DecorationOptions[]) {
let regex = /\WNumber\(([^|)]*?I(d|D))\)/g;
let hoverMessage = 'If ${match[1]} is an ID but is empty, then Number(${match[1]}) would evaluate to 0 (e.g. Number("") -> 0).';
let popupMessage = 'Number(someId) evaluates to 0 if someId is empty (e.g. "").';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function check_todoComment(rangesToDecorate: vscode.DecorationOptions[]) {
let regex = /(\/\/|--) ?TODO/g;
let hoverMessage = 'To-do comment detected.';
let popupMessage = 'To-do comment detected.';
genericCheck(regex, hoverMessage, popupMessage, rangesToDecorate);
}
function genericCheck(regex: RegExp = /^$/, hoverMessage: string = '', popupMessage: string = '', rangesToDecorate: vscode.DecorationOptions[] = []) {
if (!activeEditor) {
return;
}
const text = activeEditor.document.getText();
const regexIdVariable = regex; // e.g. /if ?\(([^=)]*[iI][dD](?!\.)\b) ?[^=<>\r\n]*?\)/g;
let match = regexIdVariable.exec(text);
let errors = [];
let firstLineNumber = null;
while (match) {
errors.push(match[1]);
if (firstLineNumber == null) {
firstLineNumber = activeEditor.document.positionAt(match.index).line + 1;
}
const startPos = activeEditor.document.positionAt(match.index);
const endPos = activeEditor.document.positionAt(match.index + match[0].length);
const decoration = {
'range': new vscode.Range(startPos, endPos),
'hoverMessage': hoverMessage.replace(/\$\{match\[1\]\}/g, match[1]).replace(/\$\{match\[2\]\}/g, match[2]) // e.g. `An ID of 0 would evaluate to falsy. Consider: ${match[1]} != null`
};
rangesToDecorate.push(decoration);
match = regexIdVariable.exec(text);
}
if (errors.length > 0) {
// e.g. let popupMessage = `ID of 0 would evaluate to falsy. Consider adding "!= null" for if-statements containing IDs: `;
vscode.window.showInformationMessage('Line ' + firstLineNumber + ': ' + popupMessage.replace(/\$\{errors.join\(", "\)}/g, errors.join(', '))); // e.g. popupMessage + errors.join(', '));
}
}
function randomIeReminder() {
let min = 1;
let max = 140;
let randomNumber = min + Math.floor(Math.random() * max);
let message = ''
if (randomNumber === 1) {
message = 'Random reminder: Did you test in IE?';
} else if (randomNumber === 2) {
message = 'Random reminder from Yoda: Test in IE did you?';
} else if (randomNumber === 3) {
message = "Random reminder from Batman: Test in IE. Your users deserve it.";
} else if (randomNumber === 4) {
message = 'Random reminder from Gandalf: Test in IE. Or it shall not pass.';
} else if (randomNumber === 5) {
message = "Random reminder from Elsa: Did you test in IE? Don't let it go.";
} else if (randomNumber === 6) {
message = "Random reminder from Ash: Always test in IE. Catch all 'em bugs.";
} else if (randomNumber === 7) {
message = "Random reminder from Groot: Test in IE. I am Groot.";
}
if (message) {
vscode.window.showInformationMessage(message);
}
}
}