Skip to content

Commit 23906d9

Browse files
author
Akos Kitta
committed
fix: encoding when reading a cloud sketch
Closes #449 Closes #634 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent f9c5888 commit 23906d9

File tree

5 files changed

+50
-57
lines changed

5 files changed

+50
-57
lines changed

arduino-ide-extension/src/browser/create/create-api.ts

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,66 +2,24 @@ import { MaybePromise } from '@theia/core/lib/common/types';
22
import { inject, injectable } from '@theia/core/shared/inversify';
33
import { fetch } from 'cross-fetch';
44
import { SketchesService } from '../../common/protocol';
5+
import { unit8ArrayToString } from '../../common/utils';
56
import { ArduinoPreferences } from '../arduino-preferences';
67
import { AuthenticationClientService } from '../auth/authentication-client-service';
78
import { SketchCache } from '../widgets/cloud-sketchbook/cloud-sketch-cache';
89
import * as createPaths from './create-paths';
910
import { posix } from './create-paths';
1011
import { Create, CreateError } from './typings';
1112

12-
export interface ResponseResultProvider {
13+
interface ResponseResultProvider {
1314
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1415
(response: Response): Promise<any>;
1516
}
16-
export namespace ResponseResultProvider {
17+
namespace ResponseResultProvider {
1718
export const NOOP: ResponseResultProvider = async () => undefined;
1819
export const TEXT: ResponseResultProvider = (response) => response.text();
1920
export const JSON: ResponseResultProvider = (response) => response.json();
2021
}
2122

22-
// TODO: check if this is still needed: https://github.com/electron/electron/issues/18733
23-
// The original issue was reported for Electron 5.x and 6.x. Theia uses 15.x
24-
export function Utf8ArrayToStr(array: Uint8Array): string {
25-
let out, i, c;
26-
let char2, char3;
27-
28-
out = '';
29-
const len = array.length;
30-
i = 0;
31-
while (i < len) {
32-
c = array[i++];
33-
switch (c >> 4) {
34-
case 0:
35-
case 1:
36-
case 2:
37-
case 3:
38-
case 4:
39-
case 5:
40-
case 6:
41-
case 7:
42-
// 0xxxxxxx
43-
out += String.fromCharCode(c);
44-
break;
45-
case 12:
46-
case 13:
47-
// 110x xxxx 10xx xxxx
48-
char2 = array[i++];
49-
out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
50-
break;
51-
case 14:
52-
// 1110 xxxx 10xx xxxx 10xx xxxx
53-
char2 = array[i++];
54-
char3 = array[i++];
55-
out += String.fromCharCode(
56-
((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
57-
);
58-
break;
59-
}
60-
}
61-
62-
return out;
63-
}
64-
6523
type ResourceType = 'f' | 'd';
6624

6725
@injectable()
@@ -330,10 +288,9 @@ export class CreateApi {
330288
if (sketch) {
331289
const url = new URL(`${this.domain()}/sketches/${sketch.id}`);
332290
const headers = await this.headers();
333-
334291
// parse the secret file
335292
const secrets = (
336-
typeof content === 'string' ? content : Utf8ArrayToStr(content)
293+
typeof content === 'string' ? content : unit8ArrayToString(content)
337294
)
338295
.split(/\r?\n/)
339296
.reduce((prev, curr) => {
@@ -397,7 +354,7 @@ export class CreateApi {
397354
const headers = await this.headers();
398355

399356
let data: string =
400-
typeof content === 'string' ? content : Utf8ArrayToStr(content);
357+
typeof content === 'string' ? content : unit8ArrayToString(content);
401358
data = await this.toggleSecretsInclude(posixPath, data, 'remove');
402359

403360
const payload = { data: btoa(data) };

arduino-ide-extension/src/browser/create/create-fs-provider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { CreateUri } from './create-uri';
2929
import { SketchesService } from '../../common/protocol';
3030
import { ArduinoPreferences } from '../arduino-preferences';
3131
import { Create } from './typings';
32+
import { stringToUint8Array } from '../../common/utils';
3233

3334
@injectable()
3435
export class CreateFsProvider
@@ -154,7 +155,7 @@ export class CreateFsProvider
154155

155156
async readFile(uri: URI): Promise<Uint8Array> {
156157
const content = await this.getCreateApi.readFile(uri.path.toString());
157-
return new TextEncoder().encode(content);
158+
return stringToUint8Array(content);
158159
}
159160

160161
async writeFile(

arduino-ide-extension/src/browser/theia/core/connection-status-service.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ import {
1717
} from '@theia/core/shared/inversify';
1818
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
1919
import { ArduinoDaemon } from '../../../common/protocol';
20+
import { assertUnreachable } from '../../../common/utils';
21+
import { CreateFeatures } from '../../create/create-features';
2022
import { NotificationCenter } from '../../notification-center';
2123
import debounce = require('lodash.debounce');
2224
import isOnline = require('is-online');
23-
import { CreateFeatures } from '../../create/create-features';
2425

2526
@injectable()
2627
export class IsOnline implements FrontendApplicationContribution {
@@ -321,8 +322,3 @@ function getOfflineTooltip(statusType: OfflineConnectionStatus): string {
321322
assertUnreachable(statusType);
322323
}
323324
}
324-
325-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
326-
function assertUnreachable(_: never): never {
327-
throw new Error();
328-
}

arduino-ide-extension/src/browser/widgets/cloud-sketchbook/cloud-sketchbook-tree.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,19 @@ import {
2525
import { CloudSketchbookCommands } from './cloud-sketchbook-commands';
2626
import { DoNotAskAgainConfirmDialog } from '../../dialogs/do-not-ask-again-dialog';
2727
import { SketchbookTree } from '../sketchbook/sketchbook-tree';
28-
import { firstToUpperCase } from '../../../common/utils';
28+
import { assertUnreachable } from '../../../common/utils';
2929
import { FileStat } from '@theia/filesystem/lib/common/files';
3030
import { WorkspaceNode } from '@theia/navigator/lib/browser/navigator-tree';
3131
import { posix, splitSketchPath } from '../../create/create-paths';
3232
import { Create } from '../../create/typings';
3333
import { nls } from '@theia/core/lib/common';
3434
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
3535
import { ExecuteWithProgress } from '../../../common/protocol/progressible';
36+
import {
37+
synchronizingSketchbook,
38+
pullingSketch,
39+
pushingSketch,
40+
} from '../../contributions/cloud-contribution';
3641

3742
const MESSAGE_TIMEOUT = 5 * 1000;
3843
const deepmerge = require('deepmerge').default;
@@ -327,7 +332,7 @@ export class CloudSketchbookTree extends SketchbookTree {
327332
): Promise<T> {
328333
const name = node.uri.path.name;
329334
return ExecuteWithProgress.withProgress(
330-
`${firstToUpperCase(state)} '${name}'`,
335+
this.taskMessage(state, name),
331336
this.messageService,
332337
async (progress) => {
333338
progress.report({ work: { done: 0, total: NaN } });
@@ -336,6 +341,22 @@ export class CloudSketchbookTree extends SketchbookTree {
336341
);
337342
}
338343

344+
private taskMessage(
345+
state: CloudSketchbookTree.CloudSketchDirNode.State,
346+
input: string
347+
): string {
348+
switch (state) {
349+
case 'syncing':
350+
return synchronizingSketchbook;
351+
case 'pulling':
352+
return pullingSketch(input);
353+
case 'pushing':
354+
return pushingSketch(input);
355+
default:
356+
assertUnreachable(state);
357+
}
358+
}
359+
339360
private async sync(source: URI, dest: URI): Promise<void> {
340361
const { filesToWrite, filesToDelete } = await this.treeDiff(source, dest);
341362
await Promise.all(

arduino-ide-extension/src/common/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,21 @@ export function startsWithUpperCase(what: string): boolean {
2020
export function isNullOrUndefined(what: unknown): what is undefined | null {
2121
return what === undefined || what === null;
2222
}
23+
24+
// Use it for and exhaustive `switch` statements
25+
// https://stackoverflow.com/a/39419171/5529090
26+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
27+
export function assertUnreachable(_: never): never {
28+
throw new Error();
29+
}
30+
31+
// Text encoder can crash in electron browser: https://github.com/arduino/arduino-ide/issues/634#issuecomment-1440039171
32+
export function unit8ArrayToString(uint8Array: Uint8Array): string {
33+
return uint8Array.reduce(
34+
(text, byte) => text + String.fromCharCode(byte),
35+
''
36+
);
37+
}
38+
export function stringToUint8Array(text: string): Uint8Array {
39+
return Uint8Array.from(text, (char) => char.charCodeAt(0));
40+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy