-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathextension.js
More file actions
113 lines (100 loc) · 3.15 KB
/
extension.js
File metadata and controls
113 lines (100 loc) · 3.15 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
const vscode = require('vscode');
const cp = require('child_process');
/**
* Activate the extension.
*
* @param {vscode.ExtensionContext} context
*/
function activate(context) {
// Register a formatter for the `testscript` language (txtar files)
const disposable = vscode.languages.registerDocumentFormattingEditProvider('testscript', {
provideDocumentFormattingEdits(document, options, token) {
return formatDocument(document);
},
});
context.subscriptions.push(disposable);
}
/**
* Deactivate the extension.
*/
function deactivate() {
// nothing to clean up
}
/**
* Format a txtar document by running `gofmt` over any embedded Go source files.
*
* @param {vscode.TextDocument} document
* @returns {vscode.TextEdit[]}
*/
function formatDocument(document) {
const edits = [];
const text = document.getText();
const lines = text.split(/\r?\n/);
// Regex that matches file delimiters like: `-- hello.go --`
const headerRE = /^--\s+(.+?)\s+--$/;
let currentFilename = null;
let blockStart = null; // line index where the file content starts
/**
* Flush a block (if any) and append edits.
* @param {number} endLine - index of the line BEFORE the next header (or EOF)
*/
function flushBlock(endLine) {
if (currentFilename && currentFilename.endsWith('.go') && blockStart !== null) {
const originalRange = new vscode.Range(
blockStart,
0,
endLine,
lines[endLine] ? lines[endLine].length : 0,
);
const originalText = document.getText(originalRange);
const formattedText = runGofmt(originalText);
if (formattedText !== null && formattedText !== originalText) {
// Replace the original block with the formatted version.
edits.push(vscode.TextEdit.replace(originalRange, formattedText.replace(/\r?\n$/, '\n')));
}
}
// reset state
currentFilename = null;
blockStart = null;
}
for (let i = 0; i < lines.length; i++) {
const m = lines[i].match(headerRE);
if (m) {
// When we hit a new header, flush any active block first.
if (blockStart !== null) {
flushBlock(i - 1);
}
// Start new block
currentFilename = m[1];
blockStart = i + 1; // content starts on next line
}
}
// flush the final block to EOF if needed
if (blockStart !== null) {
flushBlock(lines.length - 1);
}
return edits;
}
/**
* Run `go fmt` on a Go source string.
* Returns formatted source, or null on error.
* @param {string} source
* @returns {string|null}
*/
function runGofmt(source) {
try {
const result = cp.execFileSync('gofmt', [], {
input: source,
encoding: 'utf8',
});
return result;
} catch (err) {
// If gofmt fails (syntax errors, not installed, etc.) ignore formatting.
console.error('gofmt failed:', err.message || err);
return null;
}
}
module.exports = {
activate,
deactivate,
};