13 type CommandWithResult,
14 } from "./commands/command.mjs";
15 import NewProjectCommand from "./commands/newProject.mjs";
16 import Logger from "./logger.mjs";
18 cmakeGetSelectedToolchainAndSDKVersions,
20 } from "./utils/cmakeUtil.mjs";
25 } from "./settings.mjs";
26 import UI from "./ui.mjs";
27 import SwitchSDKCommand from "./commands/switchSDK.mjs";
28 import { existsSync, readFileSync } from "fs";
29 import { basename, join } from "path";
30 import CompileProjectCommand from "./commands/compileProject.mjs";
31 import LaunchTargetPathCommand from "./commands/launchTargetPath.mjs";
35 } from "./commands/getPaths.mjs";
37 downloadAndInstallCmake,
38 downloadAndInstallNinja,
39 downloadAndInstallSDK,
40 downloadAndInstallToolchain,
41 downloadAndInstallTools,
43 } from "./utils/download.mjs";
44 import { SDK_REPOSITORY_URL } from "./utils/githubREST.mjs";
45 import { getSupportedToolchains } from "./utils/toolchainUtil.mjs";
49 } from "./webview/newProjectPanel.mjs";
50 import GithubApiCache from "./utils/githubApiCache.mjs";
51 import ClearGithubApiCacheCommand from "./commands/clearGithubApiCache.mjs";
52 import { ContextKeys } from "./contextKeys.mjs";
53 import { PicoProjectActivityBar } from "./webview/activityBar.mjs";
54 import ConditionalDebuggingCommand from "./commands/conditionalDebugging.mjs";
55 import DebugLayoutCommand from "./commands/debugLayout.mjs";
56 import OpenSdkDocumentationCommand from "./commands/openSdkDocumentation.mjs";
57 import ConfigureCmakeCommand from "./commands/configureCmake.mjs";
58 import ImportProjectCommand from "./commands/importProject.mjs";
59 import { homedir } from "os";
60 import VersionBundlesLoader from "./utils/versionBundles.mjs";
61 import { pyenvInstallPython, setupPyenv } from "./utils/pyenvUtil.mjs";
62 import NewExampleProjectCommand from "./commands/newExampleProject.mjs";
64 const CMAKE_DO_NOT_EDIT_HEADER_PREFIX =
65 // eslint-disable-next-line max-len
66 "== DO NEVER EDIT THE NEXT LINES for Raspberry Pi Pico VS Code Extension to work ==";
68 export async function activate(context: ExtensionContext): Promise<void> {
69 Logger.log("Extension activated.");
71 const settings = Settings.createInstance(
72 context.workspaceState,
74 context.extension.packageJSON as PackageJSON
76 GithubApiCache.createInstance(context);
78 const picoProjectActivityBarProvider = new PicoProjectActivityBar();
79 const ui = new UI(picoProjectActivityBarProvider);
82 const COMMANDS: Array<Command | CommandWithResult<string> | CommandWithArgs> =
84 new NewProjectCommand(context.extensionUri),
85 new SwitchSDKCommand(ui, context.extensionUri),
86 new LaunchTargetPathCommand(),
87 new GetPythonPathCommand(),
88 new GetEnvPathCommand(),
89 new CompileProjectCommand(),
90 new ClearGithubApiCacheCommand(),
91 new ConditionalDebuggingCommand(),
92 new DebugLayoutCommand(),
93 new OpenSdkDocumentationCommand(context.extensionUri),
94 new ConfigureCmakeCommand(),
95 new ImportProjectCommand(context.extensionUri),
96 new NewExampleProjectCommand(context.extensionUri),
99 // register all command handlers
100 COMMANDS.forEach(command => {
101 context.subscriptions.push(command.register());
104 context.subscriptions.push(
105 window.registerWebviewPanelSerializer(NewProjectPanel.viewType, {
106 // eslint-disable-next-line @typescript-eslint/require-await
107 async deserializeWebviewPanel(webviewPanel: WebviewPanel): Promise<void> {
108 // Reset the webview options so we use latest uri for `localResourceRoots`.
109 webviewPanel.webview.options = getWebviewOptions(context.extensionUri);
110 NewProjectPanel.revive(webviewPanel, context.extensionUri, false);
115 context.subscriptions.push(
116 window.registerTreeDataProvider(
117 PicoProjectActivityBar.viewType,
118 picoProjectActivityBarProvider
122 const workspaceFolder = workspace.workspaceFolders?.[0];
124 // check if is a pico project
126 workspaceFolder === undefined ||
127 !existsSync(join(workspaceFolder.uri.fsPath, "pico_sdk_import.cmake"))
130 Logger.log("No workspace folder or pico project found.");
131 await commands.executeCommand(
133 ContextKeys.isPicoProject,
140 const cmakeListsFilePath = join(workspaceFolder.uri.fsPath, "CMakeLists.txt");
141 if (!existsSync(cmakeListsFilePath)) {
142 Logger.log("No CMakeLists.txt found in workspace folder.");
143 await commands.executeCommand(
145 ContextKeys.isPicoProject,
152 // check if it has .vscode folder and cmake donotedit header in CMakelists.txt
154 !existsSync(join(workspaceFolder.uri.fsPath, ".vscode")) ||
155 !readFileSync(cmakeListsFilePath)
157 .includes(CMAKE_DO_NOT_EDIT_HEADER_PREFIX)
160 "No .vscode folder and/or cmake " +
161 '"DO NOT EDIT"-header in CMakelists.txt found.'
163 await commands.executeCommand(
165 ContextKeys.isPicoProject,
168 const wantToImport = await window.showInformationMessage(
169 "Do you want to import this project as Raspberry Pi Pico project?",
173 if (wantToImport === "Yes") {
174 void commands.executeCommand(
175 `${extensionName}.${ImportProjectCommand.id}`,
183 await commands.executeCommand("setContext", ContextKeys.isPicoProject, true);
185 // get sdk selected in the project
186 const selectedToolchainAndSDKVersions =
187 cmakeGetSelectedToolchainAndSDKVersions(
188 join(workspaceFolder.uri.fsPath, "CMakeLists.txt")
190 if (selectedToolchainAndSDKVersions === null) {
194 // get all available toolchains for download link of current selected one
195 const toolchains = await getSupportedToolchains();
196 const selectedToolchain = toolchains.find(
197 toolchain => toolchain.version === selectedToolchainAndSDKVersions[1]
202 !(await downloadAndInstallSDK(
203 selectedToolchainAndSDKVersions[0],
206 !(await downloadAndInstallTools(
207 selectedToolchainAndSDKVersions[0],
208 process.platform === "win32"
212 "Failed to install project SDK " +
213 `version: ${selectedToolchainAndSDKVersions[0]}.` +
214 "Make sure all requirements are met."
217 void window.showErrorMessage("Failed to install project SDK version.");
222 "Found/installed project SDK " +
223 `version: ${selectedToolchainAndSDKVersions[0]}`
229 selectedToolchain === undefined ||
230 !(await downloadAndInstallToolchain(selectedToolchain))
233 "Failed to install project toolchain " +
234 `version: ${selectedToolchainAndSDKVersions[1]}`
237 void window.showErrorMessage(
238 "Failed to install project toolchain version."
244 "Found/installed project toolchain " +
245 `version: ${selectedToolchainAndSDKVersions[1]}`
249 // TODO: move this ninja, cmake and python installs auto of extension.mts
250 const ninjaPath = settings.getString(SettingsKey.ninjaPath);
251 if (ninjaPath && ninjaPath.includes("/.pico-sdk/ninja")) {
252 // check if ninja path exists
253 if (!existsSync(ninjaPath.replace(HOME_VAR, homedir()))) {
255 "Ninja path in settings does not exist. " +
256 "Installing ninja to default path."
258 const ninjaVersion = /\/\.pico-sdk\/ninja\/([v.0-9]+)\//.exec(
261 if (ninjaVersion === undefined) {
262 Logger.log("Failed to get ninja version from path.");
263 await commands.executeCommand(
265 ContextKeys.isPicoProject,
273 await window.withProgress(
275 location: ProgressLocation.Notification,
276 title: "Download and installing Ninja. This may take a while...",
280 result = await downloadAndInstallNinja(ninjaVersion);
288 Logger.log("Failed to install ninja.");
289 await commands.executeCommand(
291 ContextKeys.isPicoProject,
297 Logger.log("Installed selected ninja for project.");
301 const cmakePath = settings.getString(SettingsKey.cmakePath);
302 if (cmakePath && cmakePath.includes("/.pico-sdk/cmake")) {
303 // check if cmake path exists
304 if (!existsSync(cmakePath.replace(HOME_VAR, homedir()))) {
306 "CMake path in settings does not exist. " +
307 "Installing cmake to default path."
310 const cmakeVersion = /\/\.pico-sdk\/cmake\/([v.0-9A-Za-z-]+)\//.exec(
313 if (cmakeVersion === undefined) {
314 Logger.log("Failed to get cmake version from path.");
315 await commands.executeCommand(
317 ContextKeys.isPicoProject,
325 await window.withProgress(
327 location: ProgressLocation.Notification,
328 title: "Download and installing CMake. This may take a while...",
332 result = await downloadAndInstallCmake(cmakeVersion);
340 Logger.log("Failed to install cmake.");
341 await commands.executeCommand(
343 ContextKeys.isPicoProject,
349 Logger.log("Installed selected cmake for project.");
353 const pythonPath = settings.getString(SettingsKey.python3Path);
354 if (pythonPath && pythonPath.includes("/.pico-sdk/python")) {
355 // check if python path exists
356 if (!existsSync(pythonPath.replace(HOME_VAR, homedir()))) {
358 "Python path in settings does not exist. " +
359 "Installing python to default path."
361 const pythonVersion = /\/\.pico-sdk\/python\/([.0-9]+)\//.exec(
364 if (pythonVersion === undefined) {
365 Logger.log("Failed to get python version from path.");
366 await commands.executeCommand(
368 ContextKeys.isPicoProject,
375 let result: string | undefined;
376 await window.withProgress(
378 location: ProgressLocation.Notification,
380 "Download and installing Python. This may take a long while...",
384 if (process.platform === "win32") {
385 const versionBundle = await new VersionBundlesLoader(
387 ).getPythonWindowsAmd64Url(pythonVersion);
389 if (versionBundle === undefined) {
390 Logger.log("Failed to get python url from version bundle.");
391 await commands.executeCommand(
393 ContextKeys.isPicoProject,
400 // ! because data.pythonMode === 0 => versionBundle !== undefined
401 result = await downloadEmbedPython(versionBundle);
402 } else if (process.platform === "darwin") {
403 const result1 = await setupPyenv();
411 const result2 = await pyenvInstallPython(pythonVersion);
413 if (result2 !== null) {
418 "Automatic Python installation is only " +
419 "supported on Windows and macOS."
422 await window.showErrorMessage(
423 "Automatic Python installation is only " +
424 "supported on Windows and macOS."
433 if (result === undefined) {
434 Logger.log("Failed to install python.");
435 await commands.executeCommand(
437 ContextKeys.isPicoProject,
446 ui.showStatusBarItems();
447 ui.updateSDKVersion(selectedToolchainAndSDKVersions[0]);
449 // auto project configuration with cmake
450 if (settings.getBoolean(SettingsKey.cmakeAutoConfigure)) {
451 //run `cmake -G Ninja -B ./build ` in the root folder
452 await configureCmakeNinja(workspaceFolder.uri);
454 workspace.onDidChangeTextDocument(event => {
455 // Check if the changed document is the file you are interested in
456 if (basename(event.document.fileName) === "CMakeLists.txt") {
457 // File has changed, do something here
458 // TODO: rerun configure project
459 // TODO: maybe conflicts with cmake extension which also does this
460 console.log("File changed:", event.document.fileName);
465 "No workspace folder for configuration found " +
466 "or cmakeAutoConfigure disabled."
471 export function deactivate(): void {
472 void commands.executeCommand("setContext", ContextKeys.isPicoProject, true);
474 Logger.log("Extension deactivated.");