Mdr in VS Code 插件
我写了个插件,可以方便地运行单文件。在 VS Code 的扩展页面搜索此名称即可下载。
源码:
ts
import * as vscode from "vscode";
export class CodelensProvider implements vscode.CodeLensProvider {
private regex: RegExp;
constructor() {
this.regex = /int\s+main\s*\(/g;
}
public provideCodeLenses(
document: vscode.TextDocument,
token: vscode.CancellationToken,
): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> {
const codeLenses: vscode.CodeLens[] = [];
const regex = new RegExp(this.regex);
const text = document.getText();
let matches;
while ((matches = regex.exec(text)) !== null) {
const line = document.lineAt(document.positionAt(matches.index).line);
const position = new vscode.Position(line.lineNumber, 0);
const range = new vscode.Range(position, position);
if (range) {
const command = {
title: "▶ Mdr!",
command: "c-runner.run",
arguments: [document.uri],
};
codeLenses.push(new vscode.CodeLens(range, command));
}
}
return codeLenses;
}
}
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
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
ts
import * as vscode from "vscode";
import * as path from "path";
import * as fs from "fs";
import { CodelensProvider } from "./CodelensProvider";
import { exec } from "child_process";
import axios from "axios";
import extract = require("extract-zip");
export function activate(context: vscode.ExtensionContext) {
const diagnosticCollection =
vscode.languages.createDiagnosticCollection("c-runner");
context.subscriptions.push(diagnosticCollection);
const gccErrorRegex = /^(.+?):(\d+):(\d+):\s+(?:warning|error):\s+(.+)$/gm;
const C_COMPILER_OPTIONS = [
"-fdiagnostics-color=always",
"-g3",
"-D_DEBUG",
"-Wall",
"-Wextra",
"-Werror",
"-pedantic",
"-pipe",
"-Wshadow",
"-Wconversion",
"-Wfloat-equal",
"-Wpointer-arith",
"-Wpointer-compare",
"-Wcast-align",
"-Wcast-qual",
"-Wwrite-strings",
"-Wimplicit-fallthrough",
"-Wsequence-point",
"-Wswitch-default",
"-Wswitch-enum",
"-Wtautological-compare",
"-Wdangling-else",
"-Wmisleading-indentation",
"-std=c23",
"-lm",
"-lpthread",
"-finput-charset=UTF-8",
"-fexec-charset=UTF-8",
];
const CPP_COMPILER_OPTIONS = [
"-fdiagnostics-color=always",
"-g3",
"-D_DEBUG",
"-Wall",
"-Wextra",
"-Werror",
"-pedantic",
"-pipe",
"-Wshadow",
"-Wconversion",
"-Wfloat-equal",
"-Wpointer-arith",
"-Wpointer-compare",
"-Wcast-align",
"-Wcast-qual",
"-Wwrite-strings",
"-Wimplicit-fallthrough",
"-Wsequence-point",
"-Wswitch-default",
"-Wswitch-enum",
"-Wtautological-compare",
"-Wdangling-else",
"-Wmisleading-indentation",
"-Wnon-virtual-dtor",
"-Woverloaded-virtual",
"-std=c++23",
"-lm",
"-lpthread",
"-finput-charset=UTF-8",
"-fexec-charset=UTF-8",
];
const COMPILER_DOWNLOAD_URL =
"https://github.com/brechtsanders/winlibs_mingw/releases/download/15.2.0posix-13.0.0-ucrt-r1/winlibs-x86_64-posix-seh-gcc-15.2.0-mingw-w64ucrt-13.0.0-r1.zip";
const storagePath = context.globalStorageUri.fsPath;
const compilerInstallDir = path.join(storagePath, "mingw64");
const gccExecutablePath = path.join(compilerInstallDir, "bin", "gcc.exe");
const gppExecutablePath = path.join(compilerInstallDir, "bin", "g++.exe");
function getLanguageConfig(document: vscode.TextDocument) {
if (document.languageId === "cpp") {
return {
compilerPath: gppExecutablePath,
options: CPP_COMPILER_OPTIONS,
compilerName: "g++",
};
}
// 默认为 C
return {
compilerPath: gccExecutablePath,
options: C_COMPILER_OPTIONS,
compilerName: "gcc",
};
}
function compileAndDiagnoseOnSave(document: vscode.TextDocument) {
const config = getLanguageConfig(document);
if (!fs.existsSync(config.compilerPath)) {
return;
}
const backgroundCompileOptions = config.options.filter(
(opt) => opt !== "-fdiagnostics-color=always",
);
const compileCommand = `"${config.compilerPath}" "${document.uri.fsPath}" -fsyntax-only ${backgroundCompileOptions.join(" ")}`;
exec(compileCommand, (error, stdout, stderr) => {
if (error || stderr) {
const diagnostics: vscode.Diagnostic[] = [];
let match;
while ((match = gccErrorRegex.exec(stderr)) !== null) {
const [, , lineStr, columnStr, message] = match;
const line = parseInt(lineStr, 10) - 1;
const column = parseInt(columnStr, 10) - 1;
const range = new vscode.Range(line, column, line, 1000);
const severity = /error:/.test(match[0])
? vscode.DiagnosticSeverity.Error
: vscode.DiagnosticSeverity.Warning;
const diagnostic = new vscode.Diagnostic(range, message, severity);
diagnostics.push(diagnostic);
}
diagnosticCollection.set(document.uri, diagnostics);
}
});
}
const onSaveListener = vscode.workspace.onDidSaveTextDocument((document) => {
if (document.languageId === "c" || document.languageId === "cpp") {
compileAndDiagnoseOnSave(document);
}
});
context.subscriptions.push(onSaveListener);
const codelensProvider = new CodelensProvider();
vscode.languages.registerCodeLensProvider(["c", "cpp"], codelensProvider);
const disposableRun = vscode.commands.registerCommand(
"c-runner.run",
async (fileUri: vscode.Uri) => {
const editor = vscode.window.activeTextEditor;
if (!editor || editor.document.uri.fsPath !== fileUri.fsPath) {
return;
}
const config = getLanguageConfig(editor.document);
if (!fs.existsSync(config.compilerPath)) {
const selection = await vscode.window.showErrorMessage(
"未找到编译器。请选择操作:",
{ modal: true },
"立即下载",
"查看手动放置指南",
);
if (selection === "立即下载") {
vscode.commands.executeCommand("c-runner.downloadCompiler");
}
if (selection === "查看手动放置指南") {
vscode.commands.executeCommand("c-runner.showPath");
}
return;
}
await editor.document.save();
const terminal = vscode.window.createTerminal(`Mdr Runner`);
terminal.show();
terminal.sendText(
"[System.Console]::InputEncoding = [System.Console]::OutputEncoding=[System.Text.Encoding]::GetEncoding(65001)",
);
const filePath = fileUri.fsPath;
const parsedPath = path.parse(filePath);
const executablePath = path.join(
parsedPath.dir,
`${parsedPath.name}.exe`,
);
const compileCommand = `"${config.compilerPath}" "${filePath}" -o "${executablePath}" ${config.options.join(" ")}`;
const compilerBinDir = path.dirname(config.compilerPath);
const runCommand = `cd /d "${parsedPath.dir}" && "${executablePath}"`;
const commandForCmd = `${compileCommand} && ${runCommand}`;
if (editor.document.languageId === "cpp") {
const pathCommand = `$env:PATH = "${compilerBinDir};$env:PATH"`;
terminal.sendText(pathCommand);
}
const finalCommand = `cmd /c "${commandForCmd}"`;
terminal.sendText(finalCommand);
},
);
const disposableDownload = vscode.commands.registerCommand(
"c-runner.downloadCompiler",
async () => {
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: "正在下载 C/C++ 编译器 (MinGW-w64)",
cancellable: true,
},
async (progress, token) => {
if (!fs.existsSync(storagePath)) {
fs.mkdirSync(storagePath, { recursive: true });
}
const zipPath = path.join(storagePath, "compiler.zip");
token.onCancellationRequested(() => {
vscode.window.showInformationMessage("下载已取消。");
});
try {
progress.report({ message: "正在连接...", increment: 5 });
const response = await axios({
method: "get",
url: COMPILER_DOWNLOAD_URL,
responseType: "stream",
});
const totalLength = response.headers["content-length"];
let downloadedLength = 0;
const writer = fs.createWriteStream(zipPath);
response.data.on("data", (chunk: Buffer) => {
downloadedLength += chunk.length;
if (totalLength) {
const percentage = Math.round(
(downloadedLength / totalLength) * 100,
);
progress.report({ message: `已下载 ${percentage}%` });
}
});
response.data.pipe(writer);
await new Promise<void>((resolve, reject) => {
writer.on("finish", resolve);
writer.on("error", reject);
});
progress.report({ message: "下载完成,正在解压..." });
await extract(zipPath, { dir: storagePath });
progress.report({ message: "正在清理临时文件..." });
fs.unlinkSync(zipPath);
progress.report({ message: "安装完成!" });
vscode.window.showInformationMessage("编译器已成功安装!");
} catch (error) {
vscode.window.showErrorMessage(`编译器下载或解压失败:${error}`);
if (fs.existsSync(zipPath)) {
fs.unlinkSync(zipPath);
}
}
},
);
},
);
const disposableShowPath = vscode.commands.registerCommand(
"c-runner.showPath",
() => {
vscode.window.showInformationMessage(
`请将 mingw64 文件夹放入此目录:${storagePath}`,
{ modal: true },
);
},
);
context.subscriptions.push(
disposableRun,
disposableDownload,
disposableShowPath,
);
}
export function deactivate() {}
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270