Skip to content

Commit bed499b

Browse files
committed
Refactor ProjectConfig to use PlatformIO Core native parser
1 parent 837edcd commit bed499b

File tree

4 files changed

+50
-176
lines changed

4 files changed

+50
-176
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
},
4949
"homepage": "https://platformio.org",
5050
"dependencies": {
51-
"glob": "~8.1.0",
5251
"global-agent": "^3.0.0",
5352
"got": "^11.8.6",
5453
"jsonrpc-lite": "~2.1.1",

src/project/config.js

Lines changed: 29 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -6,159 +6,48 @@
66
* the root directory of this source tree.
77
*/
88

9-
import { promises as fs } from 'fs';
10-
import glob from 'glob';
9+
import * as core from '../core';
1110

1211
export default class ProjectConfig {
13-
reLines = /[\r\n]+/g;
14-
reComment = /(^\s*;|\s+;|^\s*#).+/;
15-
reSection = /^\[([^\]]+)\]/;
16-
reOptionValue = /^([^=]+)=(.*)/;
17-
reMultiLineValue = /^\s+(.*)/;
18-
reInterpolation = /\$\{([^\.\}]+)\.([^\}]+)\}/g;
19-
20-
static parse_multi_values(items) {
21-
const result = [];
22-
if (!items) {
23-
return result;
24-
}
25-
if (typeof items == 'string') {
26-
items = items.split(items.includes('\n') ? '\n' : ', ');
27-
}
28-
for (let item of items) {
29-
item = item.trim();
30-
if (item) {
31-
result.push(item);
32-
}
33-
}
34-
return result;
35-
}
36-
37-
constructor() {
38-
this._parsed = [];
39-
this._data = {};
12+
constructor(projectDir) {
13+
this.projectDir = projectDir;
14+
this._data = undefined;
4015
}
4116

42-
async read(path) {
43-
if (!path) {
44-
return;
45-
}
46-
if (this._parsed.includes(path)) {
47-
return;
48-
}
49-
this._parsed.push(path);
50-
let section = null;
51-
let option = null;
52-
for (let line of (await fs.readFile(path, { encoding: 'utf-8' })).split(
53-
this.reLines
54-
)) {
55-
// Remove comments
56-
line = line.replace(this.reComment, '');
57-
if (!line) {
58-
continue;
59-
}
17+
async read() {
18+
const script = `
19+
import json
20+
from platformio.public import ProjectConfig
6021
61-
// Section
62-
const mSection = line.match(this.reSection);
63-
if (mSection) {
64-
section = mSection[1];
65-
if (!this._data[section]) {
66-
this._data[section] = {};
67-
}
68-
option = null;
69-
continue;
70-
}
22+
config = ProjectConfig()
23+
envs = config.envs()
7124
72-
// Option and value
73-
const mOptionValue = line.match(this.reOptionValue);
74-
if (section && mOptionValue) {
75-
option = mOptionValue[1].trim();
76-
this._data[section][option] = mOptionValue[2].trim();
77-
continue;
78-
}
79-
80-
// Multi-line value
81-
const mMultiLineValue = line.match(this.reMultiLineValue);
82-
if (option && mMultiLineValue) {
83-
this._data[section][option] += '\n' + mMultiLineValue[0];
84-
}
85-
}
86-
87-
for (const pattern of this.getlist('platformio', 'extra_configs')) {
88-
for (const item of glob.sync(pattern)) {
89-
await this.read(item);
90-
}
91-
}
25+
print(json.dumps(dict(
26+
envs=envs,
27+
default_envs=config.default_envs(),
28+
default_env=config.get_default_env(),
29+
env_platforms={env:config.get(f"env:{env}", "platform", default=None) for env in envs}
30+
)))
31+
`;
32+
const output = await core.getCorePythonCommandOutput(['-c', script], {
33+
projectDir: this.projectDir,
34+
});
35+
this._data = JSON.parse(output.trim());
9236
}
9337

94-
getraw(section, option) {
95-
if (!this._data[section]) {
96-
throw `NoSectionError: ${section}`;
97-
}
98-
let value = null;
99-
if (option in this._data[section]) {
100-
value = this._data[section][option];
101-
} else {
102-
if ('extends' in this._data[section]) {
103-
for (const ext of ProjectConfig.parse_multi_values(
104-
this._data[section]['extends']
105-
)) {
106-
try {
107-
value = this.getraw(ext, option);
108-
break;
109-
} catch {}
110-
}
111-
}
112-
if (!value && section.startsWith('env:')) {
113-
try {
114-
value = this.getraw('env', option);
115-
} catch {}
116-
}
117-
}
118-
if (!value) {
119-
throw `NoOptionError: ${section} -> ${option}`;
120-
}
121-
if (!value.includes('${') || !value.includes('}')) {
122-
return value;
123-
}
124-
return value.replace(this.reInterpolation, (_, _section, _option) =>
125-
this._reInterpolationHandler(section, _section, _option)
126-
);
127-
}
128-
129-
_reInterpolationHandler(parentSection, section, option) {
130-
if (section === 'sysenv') {
131-
return process.env[option] || '';
132-
}
133-
// handle ${this.*}
134-
if (section === 'this') {
135-
section = parentSection;
136-
if (option === '__env__') {
137-
return parentSection.substring(4);
138-
}
139-
}
140-
return this.get(section, option);
141-
}
142-
143-
sections() {
144-
return Object.keys(this._data);
38+
envs() {
39+
return this._data.envs;
14540
}
14641

147-
envs() {
148-
return this.sections()
149-
.filter((item) => item.startsWith('env:'))
150-
.map((item) => item.substring(4));
42+
defaultEnvs() {
43+
return this._data.default_envs;
15144
}
15245

153-
get(section, option, default_ = undefined) {
154-
try {
155-
return this.getraw(section, option);
156-
} catch (err) {
157-
return default_;
158-
}
46+
defaultEnv() {
47+
return this._data.default_env;
15948
}
16049

161-
getlist(section, option, default_ = undefined) {
162-
return ProjectConfig.parse_multi_values(this.get(section, option, default_));
50+
getEnvPlatform(env) {
51+
return this._data.env_platforms[env];
16352
}
16453
}

src/project/observer.js

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default class ProjectObserver {
2525
this.dirWatchSubscriptions = [];
2626

2727
this._cache = new Map();
28+
this._config = undefined;
2829
this._indexer = undefined;
2930
this._projectTasks = new ProjectTasks(this.projectDir, this.options.ide);
3031
this._updateDirWatchersTimeout = undefined;
@@ -46,6 +47,7 @@ export default class ProjectObserver {
4647
if (this._indexer) {
4748
this._indexer.dispose();
4849
}
50+
this.resetCache();
4951
}
5052

5153
activate() {
@@ -64,6 +66,14 @@ export default class ProjectObserver {
6466
this._cache.clear();
6567
}
6668

69+
async getConfig() {
70+
if (!this._config) {
71+
this._config = new ProjectConfig(this.projectDir);
72+
await this._config.read();
73+
}
74+
return this._config;
75+
}
76+
6777
rebuildIndex({ force = false, delayed = false } = {}) {
6878
if (!force && !this.getSetting('autoRebuild')) {
6979
return;
@@ -75,7 +85,7 @@ export default class ProjectObserver {
7585
}
7686

7787
async switchProjectEnv(name, { delayedRebuildIndex = false } = {}) {
78-
const validNames = (await this.getProjectEnvs()).map((item) => item.name);
88+
const validNames = (await this.getConfig()).envs();
7989
if (!validNames.includes(name)) {
8090
name = undefined;
8191
}
@@ -90,32 +100,12 @@ export default class ProjectObserver {
90100
return this._selectedEnv;
91101
}
92102

93-
async getProjectEnvs() {
94-
if (this._cache.has('projectEnvs')) {
95-
return this._cache.get('projectEnvs');
103+
async revealActiveEnvironment() {
104+
if (this._selectedEnv) {
105+
return this._selectedEnv;
96106
}
97-
const result = [];
98-
const prevCWD = process.cwd();
99-
process.chdir(this.projectDir);
100-
try {
101-
const config = new ProjectConfig();
102-
await config.read(path.join(this.projectDir, 'platformio.ini'));
103-
for (const name of config.envs()) {
104-
const platform = config.get(`env:${name}`, 'platform');
105-
if (!platform) {
106-
continue;
107-
}
108-
result.push({ name, platform });
109-
}
110-
} catch (err) {
111-
console.warn(
112-
`Could not parse "platformio.ini" file in ${this.projectDir}: ${err}`
113-
);
114-
}
115-
// restore original CWD
116-
process.chdir(prevCWD);
117-
this._cache.set('projectEnvs', result);
118-
return result;
107+
const config = await this.getConfig();
108+
return config.defaultEnv();
119109
}
120110

121111
async getDefaultTasks() {
@@ -131,7 +121,7 @@ export default class ProjectObserver {
131121
options.preload ||
132122
this.getSetting('autoPreloadEnvTasks') ||
133123
this._selectedEnv === name ||
134-
(await this.getProjectEnvs()).length === 1;
124+
(await this.getConfig()).envs().length === 1;
135125
if (!lazyLoading) {
136126
return undefined;
137127
}
@@ -154,6 +144,7 @@ export default class ProjectObserver {
154144
}
155145

156146
onDidChangeProjectConfig() {
147+
this._config = undefined;
157148
// reset to `undefined` if env was removed from conf
158149
this.resetCache();
159150
// rebuildIndex
@@ -218,12 +209,7 @@ export default class ProjectObserver {
218209
async fetchLibDirs() {
219210
const script = `
220211
import json
221-
222-
try:
223-
from platformio.public import get_project_watch_lib_dirs
224-
except ImportError:
225-
from platformio.project.helpers import get_project_all_lib_dirs as get_project_watch_lib_dirs
226-
212+
from platformio.public import get_project_watch_lib_dirs
227213
print(json.dumps(get_project_watch_lib_dirs()))
228214
`;
229215
const output = await core.getCorePythonCommandOutput(['-c', script], {

src/project/pool.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ export default class ProjectPool {
4040
return observer;
4141
}
4242

43-
switch(projectDir) {
43+
async switch(projectDir) {
4444
this._activeProjectDir = projectDir;
4545
console.info('Switching project to', projectDir);
4646
this._observers
4747
.filter((observer) => observer.projectDir !== projectDir)
4848
.forEach((observer) => observer.deactivate());
4949
const observer = this.getObserver(projectDir);
50-
observer.activate();
50+
await observer.activate();
5151
return observer;
5252
}
5353

0 commit comments

Comments
 (0)