forked from elektronikworkshop/vscode-arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
completionProvider.ts
129 lines (115 loc) · 4.96 KB
/
completionProvider.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
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as fs from "fs";
import * as path from "path";
import * as vscode from "vscode";
import * as constants from "../common/constants";
import * as util from "../common/util";
import { VscodeSettings } from "../arduino/vscodeSettings";
import ArduinoActivator from "../arduinoActivator";
import ArduinoContext from "../arduinoContext";
import { ArduinoWorkspace } from "../common/workspace";
/**
* Provides completions for library header includes.
*
* NOTE: With the new IntelliSense auto-configuration this doesn't make
* much sense in its current state, since it tries to fetch includes
* from the wrongly guessed include paths. And it tries to fetch includes
* from the c_cpp_properties which is now automatically generated from the
* files already included -> therefore the user already included the header
* and doesn't need a completion. Furthermore IntelliSense knows the location
* as well and can complete it too.
*
* To make this useful it has to parse the actual library folders and then
* it makes only sense if it reads the library information and checks if
* the individual libraries are actually compatible with the current board
* before offering a completion.
*
* EW, 2020-02-17
*/
export class CompletionProvider implements vscode.CompletionItemProvider {
private _headerFiles = new Set<string>();
private _libPaths = new Set<string>();
private _watcher: vscode.FileSystemWatcher;
private _cppConfigFile: string;
private _activated: boolean = false;
constructor() {
if (vscode.workspace && ArduinoWorkspace.rootPath) {
this._cppConfigFile = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE);
this._watcher = vscode.workspace.createFileSystemWatcher(this._cppConfigFile);
this._watcher.onDidCreate(() => this.updateLibList());
this._watcher.onDidChange(() => this.updateLibList());
this._watcher.onDidDelete(() => this.updateLibList());
}
}
public async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position):
Promise<vscode.CompletionItem[]> {
if (VscodeSettings.getInstance().skipHeaderProvider) {
return [];
}
if (!ArduinoContext.initialized) {
await ArduinoActivator.activate();
}
if (!this._activated) {
this._activated = true;
this.updateLibList();
}
// Check if we are currently inside an include statement.
const text = document.lineAt(position.line).text.substr(0, position.character);
const match = text.match(/^\s*#\s*include\s*(<[^>]*|"[^"]*)$/);
if (match) {
const result = [];
this._headerFiles.forEach((headerFile) => {
result.push(new vscode.CompletionItem(headerFile, vscode.CompletionItemKind.File));
});
return result;
}
}
private updateLibList(): void {
if (!this._activated) {
return;
}
this._libPaths.clear();
this._headerFiles.clear();
if (fs.existsSync(this._cppConfigFile)) {
const deviceConfig = util.tryParseJSON(fs.readFileSync(this._cppConfigFile, "utf8"));
if (deviceConfig) {
if (deviceConfig.sketch) {
const appFolder = path.dirname(deviceConfig.sketch);
if (util.directoryExistsSync(appFolder)) {
this._libPaths.add(path.normalize(appFolder));
}
}
if (deviceConfig.configurations) {
deviceConfig.configurations.forEach((configSection) => {
if (configSection.name === constants.C_CPP_PROPERTIES_CONFIG_NAME && Array.isArray(configSection.includePath)) {
configSection.includePath.forEach((includePath) => {
this._libPaths.add(path.normalize(includePath));
});
}
});
}
}
}
this._libPaths.forEach((includePath) => {
this.addLibFiles(includePath);
});
}
private addLibFiles(libPath: string): void {
if (!util.directoryExistsSync(libPath)) {
return;
}
const subItems = fs.readdirSync(libPath);
subItems.forEach((item) => {
try {
const state = fs.statSync(path.join(libPath, item));
if (state.isFile() && item.endsWith(".h")) {
this._headerFiles.add(item);
} else if (state.isDirectory()) {
this.addLibFiles(path.join(libPath, item));
}
} catch (ex) {
}
});
}
}