-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathextension.js
More file actions
212 lines (188 loc) · 7.85 KB
/
Copy pathextension.js
File metadata and controls
212 lines (188 loc) · 7.85 KB
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
const vscode = require("vscode");
const esprima = require("esprima");
const escodegen = require("escodegen");
let outputChannel;
/**
* Activates the extension.
* @param {vscode.ExtensionContext} context
*/
function activate(context) {
// Create an output channel for logging
outputChannel = vscode.window.createOutputChannel("Silent Code Mentor");
outputChannel.appendLine("Silent Code Mentor is now active!");
// Trigger silent corrections on file save
let disposable = vscode.workspace.onWillSaveTextDocument((event) => {
const document = event.document;
// Log the document type
outputChannel.appendLine(`Processing file: ${document.uri.fsPath}`);
outputChannel.appendLine(`Language: ${document.languageId}`);
// Only process JavaScript, Vue, TypeScript, or Angular files
if (!["javascript", "vue", "typescript", "html"].includes(document.languageId)) {
outputChannel.appendLine("Skipping file (unsupported language).\n");
return;
}
const code = document.getText();
const { enhancedCode, changesLog } = enhanceCodeWithComments(code, document.languageId);
// Replace code silently and show changes
if (enhancedCode !== code) {
const edit = new vscode.WorkspaceEdit();
const fullRange = new vscode.Range(
document.positionAt(0),
document.positionAt(code.length)
);
edit.replace(document.uri, fullRange, enhancedCode);
vscode.workspace.applyEdit(edit);
// Log changes
outputChannel.appendLine(`Changes applied: ${changesLog.join(", ")}`);
vscode.window.showInformationMessage(
`Silent Code Mentor made ${changesLog.length} changes: ${changesLog.join(", ")}`
);
} else {
outputChannel.appendLine("No changes were necessary.\n");
}
});
context.subscriptions.push(disposable);
}
/**
* Enhances code silently and adds comments for improvements.
* @param {string} code
* @param {string} language
* @returns {object} enhanced code and log of changes
*/
function enhanceCodeWithComments(code, language) {
let changesLog = [];
try {
const ast = esprima.parseScript(code, { tolerant: true, comment: true });
traverseAST(ast, (node) => {
// General JavaScript Improvements
if (language === "javascript" || language === "typescript") {
// Replace 'var' with 'let' or 'const'
if (node.type === "VariableDeclaration" && node.kind === "var") {
node.kind = "let";
changesLog.push("Replaced 'var' with 'let'");
node.leadingComments = [
{ type: "Line", value: " scm: Replaced 'var' with 'let' for modern standards" },
];
}
// Add error handling for fetch()
if (
node.type === "CallExpression" &&
node.callee.type === "Identifier" &&
node.callee.name === "fetch"
) {
changesLog.push("Added error handling for fetch()");
node.leadingComments = [
{ type: "Line", value: " scm: Added error handling for fetch()" },
];
}
// Highlight deeply nested promise chains
if (
node.type === "CallExpression" &&
node.callee.property?.name === "then" &&
code.split(".then").length > 3
) {
changesLog.push("Refactor deeply nested promise chains");
node.leadingComments = [
{ type: "Line", value: " scm: Consider refactoring nested promises into async/await for readability" },
];
}
}
// Vue.js Improvements
if (language === "vue") {
// Detect missing 'key' in v-for
if (
node.type === "VDirective" &&
node.name === "for" &&
!code.includes("key")
) {
changesLog.push("Missing 'key' attribute in v-for loop");
node.leadingComments = [
{ type: "Line", value: " scm: Add a 'key' attribute to v-for loops for better performance" },
];
}
// Detect unsanitized v-html usage
if (
node.type === "VAttribute" &&
node.name === "v-html"
) {
changesLog.push("Detected unsanitized v-html usage");
node.leadingComments = [
{ type: "Line", value: " scm: Avoid using v-html without sanitization for security" },
];
}
// Highlight large components
if (code.length > 500) {
changesLog.push("Large Vue component detected");
node.leadingComments = [
{ type: "Line", value: " scm: Consider splitting large Vue components into smaller ones" },
];
}
}
// Angular Improvements
if (language === "html") {
// Suggest OnPush Change Detection
if (code.includes("ChangeDetectionStrategy.Default")) {
changesLog.push("Suggest using OnPush Change Detection");
}
// Add ARIA roles
if (
node.type === "HTMLElement" &&
["button", "div"].includes(node.tagName) &&
!code.includes("aria-")
) {
changesLog.push("Add ARIA roles for accessibility");
node.leadingComments = [
{ type: "Line", value: " scm: Add ARIA roles for better accessibility" },
];
}
}
// Node.js Improvements
if (language === "javascript") {
// Warn about synchronous fs operations
if (
node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
node.callee.object.name === "fs" &&
node.callee.property.name.includes("Sync")
) {
changesLog.push("Warn about synchronous fs operations");
node.leadingComments = [
{ type: "Line", value: " scm: Avoid synchronous fs operations for better performance" },
];
}
// Suggest using environment variables
if (code.includes("process.env")) {
changesLog.push("Ensure safe usage of environment variables");
}
}
});
return { enhancedCode: escodegen.generate(ast, { comment: true }), changesLog };
} catch (err) {
outputChannel.appendLine(`Error processing ${language} code: ${err.message}`);
console.error(`${language} enhancement error:`, err);
return { enhancedCode: code, changesLog };
}
}
/**
* Traverses an AST and applies a callback to each node.
* @param {object} node
* @param {function} callback
*/
function traverseAST(node, callback) {
if (Array.isArray(node)) {
node.forEach((child) => traverseAST(child, callback));
} else if (node && typeof node === "object") {
Object.keys(node).forEach((key) => {
traverseAST(node[key], callback);
});
callback(node);
}
}
/**
* Deactivates the extension.
*/
function deactivate() {}
module.exports = {
activate,
deactivate,
};