Skip to content

Commit 6ff83aa

Browse files
committed
Detect if firmware is in subdirectory by locating .micropico file as default root
Signed-off-by: paulober <[email protected]>
1 parent 9bc9b2e commit 6ff83aa

File tree

5 files changed

+65
-11
lines changed

5 files changed

+65
-11
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@
347347
"micropico.syncFolder": {
348348
"type": "string",
349349
"scope": "machine-overridable",
350-
"default": "",
350+
"default": null,
351351
"title": "Sync Folder",
352352
"description": "This folder will be uploaded to the pyboard when using the sync button. Leave empty to sync the complete project. (only allows folders within the project). Use a path relative to the project you opened in vscode, without leading or trailing slash",
353353
"order": 4

src/activator.mts

+1
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,7 @@ export default class Activator {
972972

973973
return;
974974
}
975+
this.settings.reload();
975976

976977
const syncDir = await this.settings.requestSyncFolder("Download");
977978

src/osHelper.mts

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { lstat } from "fs";
1+
import { lstat, readdirSync } from "fs";
22
import { readFile, stat, writeFile } from "fs/promises";
3+
import { basename, join } from "path";
34
import { rimrafSync } from "rimraf";
45

56
export async function pathExists(path: string): Promise<boolean> {
@@ -61,3 +62,24 @@ export function removeJunction(junctionPath: string): Promise<boolean> {
6162
});
6263
});
6364
}
65+
66+
/**
67+
* Searches for a file in a directory and its subdirectories.
68+
*
69+
* @param directory The directory to search in.
70+
* @param fileName The name of the file to search for.
71+
* @returns The path to the file if found, otherwise undefined.
72+
*/
73+
export function searchFile(
74+
directory: string,
75+
fileName: string
76+
): string | undefined {
77+
const contents = readdirSync(directory, {
78+
encoding: "utf8",
79+
recursive: true,
80+
});
81+
82+
const file = contents.find(c => basename(c) === fileName);
83+
84+
return file ? join(directory, file) : undefined;
85+
}

src/settings.mts

+40-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { Memento, Uri, WorkspaceConfiguration } from "vscode";
22
import { window, workspace as vsWorkspace } from "vscode";
33
import { extName, getProjectPath, settingsStubsBasePath } from "./api.mjs";
4-
import { join, relative } from "path";
4+
import { dirname, join, relative } from "path";
55
import { PicoMpyCom } from "@paulober/pico-mpy-com";
6+
import { searchFile } from "./osHelper.mjs";
67

78
export enum SettingsKey {
89
autoConnect = "autoConnect",
@@ -75,6 +76,10 @@ export default class Settings {
7576
return this.config.update(key, value, true);
7677
}
7778

79+
public updateWorkspaceFolder<T>(key: string, value: T): Thenable<void> {
80+
return this.config.update(key, value, null);
81+
}
82+
7883
public updatePython<T>(key: string, value: T): Thenable<void> {
7984
return this.pythonConfig.update(key, value, null);
8085
}
@@ -126,22 +131,22 @@ export default class Settings {
126131
* Returns the absolute path to the sync folder. If the sync folder is undefined,
127132
* the project path is returned.
128133
*
129-
* @returns the absolute path to the sync folder
134+
* @returns The absolute path to the sync folder and if the setting is undefined
130135
*/
131-
public getSyncFolderAbsPath(): string | undefined {
136+
public getSyncFolderAbsPath(): [string | undefined, boolean] {
132137
const syncDir = this.getString(SettingsKey.syncFolder);
133138
const projectDir = getProjectPath();
134139

135-
if (syncDir === undefined) {
136-
return projectDir;
140+
if (syncDir === undefined || syncDir.length === 0) {
141+
return [projectDir, true];
137142
}
138143

139144
if (projectDir === undefined) {
140145
// How can this ever happen??!
141-
return undefined;
146+
return [undefined, false];
142147
}
143148

144-
return join(projectDir, syncDir);
149+
return [join(projectDir, syncDir), false];
145150
}
146151

147152
/**
@@ -158,14 +163,41 @@ export default class Settings {
158163
public async requestSyncFolder(
159164
actionTitle: string
160165
): Promise<[string, string] | undefined> {
161-
const syncFolder = this.getSyncFolderAbsPath();
166+
let [syncFolder, syncSettingNotSet] = this.getSyncFolderAbsPath();
162167
const projectDir = getProjectPath();
163168

164169
if (projectDir === undefined) {
165170
// How can this ever happen??!
166171
return;
167172
}
168173

174+
// sync folder setting not set
175+
if (syncSettingNotSet) {
176+
const activationFile = searchFile(projectDir, ".micropico");
177+
const actParent = activationFile ? dirname(activationFile) : undefined;
178+
179+
// check if activation file is not in project root
180+
if (activationFile && actParent && actParent !== projectDir) {
181+
syncFolder = actParent;
182+
183+
// update transparent to the user
184+
await this.updateWorkspaceFolder(
185+
SettingsKey.syncFolder,
186+
relative(projectDir, actParent)
187+
);
188+
189+
void window.showWarningMessage(
190+
`Sync folder has been set to \`${relative(
191+
projectDir,
192+
actParent
193+
)}\` ` +
194+
"because the `.micropico` file was found in a subdirectory " +
195+
"and no sync folder was set. To disable this behavior, " +
196+
"set a sync folder in the settings to `.` for the project root."
197+
);
198+
}
199+
}
200+
169201
let additionalSyncFolders = this.getArray(
170202
SettingsKey.additionalSyncFolders
171203
)?.map(sf => join(projectDir, sf));

src/stubs.mts

-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ export default class Stubs {
191191
};
192192

193193
if (!justUpdate) {
194-
defaultSettings["micropico.syncFolder"] = "";
195194
defaultSettings["micropico.openOnStart"] = true;
196195
}
197196

0 commit comments

Comments
 (0)