forked from golang/vscode-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
goLiveErrors.ts
127 lines (112 loc) · 3.95 KB
/
goLiveErrors.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
/* eslint-disable @typescript-eslint/no-explicit-any */
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
'use strict';
import cp = require('child_process');
import path = require('path');
import vscode = require('vscode');
import { getGoConfig } from './config';
import { toolExecutionEnvironment } from './goEnv';
import { promptForMissingTool } from './goInstallTools';
import { buildDiagnosticCollection } from './goMain';
import { isModSupported } from './goModules';
import { getBinPath } from './util';
// Interface for settings configuration for adding and removing tags
interface GoLiveErrorsConfig {
delay: number;
enabled: boolean;
}
let runner: NodeJS.Timer;
export function goLiveErrorsEnabled() {
const goConfig = getGoConfig();
// If the language server is enabled, there is no need for live errors.
if (goConfig['useLanguageServer'] === true) {
return false;
}
const liveErrorsConfig = <GoLiveErrorsConfig>goConfig['liveErrors'];
if (liveErrorsConfig === null || liveErrorsConfig === undefined || !liveErrorsConfig.enabled) {
return false;
}
const files = vscode.workspace.getConfiguration('files', null);
const autoSave = files['autoSave'];
const autoSaveDelay = files['autoSaveDelay'];
if (
autoSave !== null &&
autoSave !== undefined &&
autoSave === 'afterDelay' &&
autoSaveDelay < liveErrorsConfig.delay * 1.5
) {
return false;
}
return liveErrorsConfig.enabled;
}
// parseLiveFile runs the gotype command in live mode to check for any syntactic or
// semantic errors and reports them immediately
export function parseLiveFile(e: vscode.TextDocumentChangeEvent) {
if (e.document.isUntitled) {
return;
}
if (e.document.languageId !== 'go') {
return;
}
if (!goLiveErrorsEnabled()) {
return;
}
if (runner !== null) {
clearTimeout(runner);
}
runner = setTimeout(() => {
processFile(e);
runner = null;
}, getGoConfig(e.document.uri)['liveErrors']['delay']);
}
// processFile does the actual work once the timeout has fired
async function processFile(e: vscode.TextDocumentChangeEvent) {
const isMod = await isModSupported(e.document.uri);
if (isMod) {
return;
}
const gotypeLive = getBinPath('gotype-live');
if (!path.isAbsolute(gotypeLive)) {
return promptForMissingTool('gotype-live');
}
const fileContents = e.document.getText();
const fileName = e.document.fileName;
const args = ['-e', '-a', '-lf=' + fileName, path.dirname(fileName)];
const env = toolExecutionEnvironment();
const p = cp.execFile(gotypeLive, args, { env }, (err, stdout, stderr) => {
if (err && (<any>err).code === 'ENOENT') {
promptForMissingTool('gotype-live');
return;
}
buildDiagnosticCollection.clear();
if (err) {
// we want to take the error path here because the command we are calling
// returns a non-zero exit status if the checks fail
const diagnosticMap: Map<string, vscode.Diagnostic[]> = new Map();
stderr.split('\n').forEach((error) => {
if (error === null || error.length === 0) {
return;
}
// extract the line, column and error message from the gotype output
const [, file, line, column, message] = /^(.+):(\d+):(\d+):\s+(.+)/.exec(error);
// get canonical file path
const canonicalFilePath = vscode.Uri.file(file).toString();
const range = new vscode.Range(+line - 1, +column, +line - 1, +column);
const diagnostic = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error);
diagnostic.source = 'go';
const diagnostics = diagnosticMap.get(canonicalFilePath) || [];
diagnostics.push(diagnostic);
diagnosticMap.set(canonicalFilePath, diagnostics);
});
diagnosticMap.forEach((diagnostics, file) => {
buildDiagnosticCollection.set(vscode.Uri.parse(file), diagnostics);
});
}
});
if (p.pid) {
p.stdin.end(fileContents);
}
}