Simplify file handling: give up trying to execute examples in their folders, just copy all files to /. Breaks autocomplete
This commit is contained in:
parent
bcebd5ae00
commit
51b481866f
8 changed files with 90 additions and 52 deletions
|
|
@ -11,7 +11,7 @@ import { buildUrlForStateParams } from '../state/fragment-state';
|
|||
import { blankProjectState } from '../state/initial-state';
|
||||
import { ModelContext, FSContext } from './contexts';
|
||||
import FilePicker, { } from './FilePicker';
|
||||
import { isFileWritable } from '../state/model';
|
||||
// import { isFileWritable } from '../state/model';
|
||||
|
||||
// import "primereact/resources/themes/lara-light-indigo/theme.css";
|
||||
// import "primereact/resources/primereact.min.css";
|
||||
|
|
@ -186,7 +186,7 @@ export default function EditorPanel({className, style}: {className?: string, sty
|
|||
onMount={onMount} // TODO: This looks a bit silly, does it trigger a re-render??
|
||||
options={{
|
||||
...openscadEditorOptions,
|
||||
readOnly: !isFileWritable(state.params.sourcePath)
|
||||
// readOnly: !isFileWritable(state.params.sourcePath)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { CSSProperties, useContext } from 'react';
|
|||
import { TreeSelect } from 'primereact/treeselect';
|
||||
import TreeNode from 'primereact/treenode';
|
||||
import { ModelContext, FSContext } from './contexts';
|
||||
import { isFileWritable } from '../state/model';
|
||||
// import { isFileWritable } from '../state/model';
|
||||
import { join } from '../fs/filesystem';
|
||||
|
||||
function listFilesAsNodes(fs: FS, path: string, accept?: (path: string) => boolean): TreeNode[] {
|
||||
const files: [string, string][] = []
|
||||
|
|
@ -13,7 +14,7 @@ function listFilesAsNodes(fs: FS, path: string, accept?: (path: string) => boole
|
|||
if (name.startsWith('.')) {
|
||||
continue;
|
||||
}
|
||||
const childPath = `${path}/${name}`;
|
||||
const childPath = join(path, name);//`${path}/${name}`;
|
||||
if (accept && !accept(childPath)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -35,7 +36,8 @@ function listFilesAsNodes(fs: FS, path: string, accept?: (path: string) => boole
|
|||
}
|
||||
nodes.push({
|
||||
// icon: path == '/home' ? 'pi-home' : ...
|
||||
icon: isDirectory ? 'pi pi-folder' : isFileWritable(path) ? 'pi pi-file' : 'pi pi-lock',
|
||||
// icon: isDirectory ? 'pi pi-folder' : isFileWritable(path) ? 'pi pi-file' : 'pi pi-lock',
|
||||
icon: isDirectory ? 'pi pi-folder' : 'pi pi-file',
|
||||
label: name,
|
||||
data: path,
|
||||
key: path,
|
||||
|
|
@ -54,7 +56,24 @@ export default function FilePicker({className, style}: {className?: string, styl
|
|||
|
||||
const fs = useContext(FSContext);
|
||||
|
||||
const fsItems = fs && listFilesAsNodes(fs, '/home')
|
||||
const fsItems = fs && //listFilesAsNodes(fs, '/home')
|
||||
[
|
||||
{
|
||||
icon: 'pi pi-home',
|
||||
label: 'User files',
|
||||
key: '/',
|
||||
children: listFilesAsNodes(fs, '/'),//
|
||||
// children: listFilesAsNodes(fs, '/', f => f != librariesFolder && !f.startsWith(`${librariesFolder}/`)),
|
||||
selectable: false
|
||||
},
|
||||
{
|
||||
icon: 'pi pi-database',
|
||||
label: 'Builtin libraries',
|
||||
key: '/libraries',
|
||||
children: listFilesAsNodes(fs, '/libraries'),
|
||||
selectable: false
|
||||
},
|
||||
] || [];
|
||||
|
||||
return (
|
||||
<TreeSelect
|
||||
|
|
|
|||
|
|
@ -10,13 +10,22 @@ export type FSMounts = {
|
|||
|
||||
export type Symlinks = {[alias: string]: string};
|
||||
|
||||
export const getParentDir = (path: string) => path.split('/').slice(0, -1).join('/');
|
||||
export const getParentDir = (path: string) => {
|
||||
let d = path.split('/').slice(0, -1).join('/');
|
||||
return d === '' ? (path.startsWith('/') ? '/' : '.') : d;
|
||||
}
|
||||
export const getFileName = (path: string) => path.split('/').splice(-1)[0];
|
||||
|
||||
export function readDirAsArray(fs: FS, path: string): Promise<string[] | undefined> {
|
||||
return new Promise((res, rej) => fs.readdir(path, (err, files) => err ? rej(err) : res(files)));
|
||||
}
|
||||
|
||||
export function join(a: string, b: string): string {
|
||||
if (a === '.') return b;
|
||||
if (a.endsWith('/')) return join(a.substring(0, a.length - 1), b);
|
||||
return b === '.' ? a : `${a}/${b}`;
|
||||
}
|
||||
|
||||
export async function getBrowserFSLibrariesMounts(archiveNames: string[]) {
|
||||
const Buffer = BrowserFS.BFSRequire('buffer').Buffer;
|
||||
const fetchData = async (url: string) => (await fetch(url)).arrayBuffer();
|
||||
|
|
@ -38,7 +47,11 @@ export async function getBrowserFSLibrariesMounts(archiveNames: string[]) {
|
|||
export async function symlinkLibraries(archiveNames: string[], fs: FS, prefix='/libraries', cwd='/tmp') {
|
||||
const createSymlink = async (target: string, source: string) => {
|
||||
// console.log('symlink', target, source);
|
||||
await fs.symlink(target, source);
|
||||
try {
|
||||
await fs.symlink(target, source);
|
||||
} catch (e) {
|
||||
console.error(`symlink(${target}, ${source}) failed: `, e);
|
||||
}
|
||||
// await symlink(target, source);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,13 +22,8 @@ if (process.env.NODE_ENV !== 'production') {
|
|||
|
||||
(async () => {
|
||||
|
||||
const workingDir = '/home';
|
||||
const fs = await createEditorFS(workingDir)!;
|
||||
await registerOpenSCADLanguage(fs, workingDir, zipArchives);
|
||||
|
||||
// type Mode = State['view']['layout']['mode'];
|
||||
// const mode: Mode = window.matchMedia("(min-width: 768px)").matches
|
||||
// ? 'multi' : 'single';
|
||||
const fs = await createEditorFS('/libraries/');
|
||||
await registerOpenSCADLanguage(fs, '/', zipArchives);
|
||||
|
||||
const initialState = createInitialState(fs, readStateFromFragment());
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING.
|
||||
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
import { getParentDir } from '../fs/filesystem';
|
||||
import { getFileName, getParentDir } from '../fs/filesystem';
|
||||
import { spawnOpenSCAD } from "./openscad-runner";
|
||||
import { processMergedOutputs } from "./output-parser";
|
||||
import { AbortablePromise, turnIntoDelayableExecution } from '../utils';
|
||||
|
|
@ -14,10 +14,12 @@ export const checkSyntax =
|
|||
// const timestamp = Date.now();
|
||||
|
||||
source = '$preview=true;\n' + source;
|
||||
sourcePath = getFileName(sourcePath);
|
||||
|
||||
const job = spawnOpenSCAD({
|
||||
inputs: [[sourcePath, source + '\n']],
|
||||
args: [sourcePath, "-o", "out.ast"],
|
||||
// workingDir: sourcePath.startsWith('/') ? getParentDir(sourcePath) : '/home'
|
||||
});
|
||||
|
||||
return AbortablePromise<SyntaxCheckOutput>((res, rej) => {
|
||||
|
|
@ -49,26 +51,29 @@ export type RenderArgs = {
|
|||
isPreview: boolean
|
||||
}
|
||||
export const render =
|
||||
turnIntoDelayableExecution(renderDelay, (params: RenderArgs) => {
|
||||
const args = [
|
||||
params.sourcePath,
|
||||
"-o", "out.stl",
|
||||
"--export-format=binstl",
|
||||
...(params.features ?? []).map(f => `--enable=${f}`),
|
||||
...(params.extraArgs ?? [])
|
||||
]
|
||||
turnIntoDelayableExecution(renderDelay, ({sourcePath, source, isPreview, features, extraArgs}: RenderArgs) => {
|
||||
|
||||
const prefixLines: string[] = [];
|
||||
if (params.isPreview) {
|
||||
if (isPreview) {
|
||||
prefixLines.push('$preview=true;');
|
||||
}
|
||||
const source = [...prefixLines, params.source].join('\n');
|
||||
source = [...prefixLines, source].join('\n');
|
||||
sourcePath = getFileName(sourcePath);
|
||||
|
||||
const args = [
|
||||
sourcePath,
|
||||
"-o", "out.stl",
|
||||
"--export-format=binstl",
|
||||
...(features ?? []).map(f => `--enable=${f}`),
|
||||
...(extraArgs ?? [])
|
||||
]
|
||||
|
||||
const job = spawnOpenSCAD({
|
||||
// wasmMemory,
|
||||
inputs: [[params.sourcePath, source]],
|
||||
inputs: [[sourcePath, source]],
|
||||
args,
|
||||
outputPaths: ['out.stl']
|
||||
outputPaths: ['out.stl'],
|
||||
// workingDir: sourcePath.startsWith('/') ? getParentDir(sourcePath) : '/home'
|
||||
});
|
||||
|
||||
return AbortablePromise<RenderOutput>((resolve, reject) => {
|
||||
|
|
@ -79,7 +84,7 @@ export const render =
|
|||
|
||||
const {logText, markers} = processMergedOutputs(result.mergedOutputs, {
|
||||
shiftSourceLines: {
|
||||
sourcePath: params.sourcePath,
|
||||
sourcePath: sourcePath,
|
||||
skipLines: prefixLines.length
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import OpenSCAD from "../wasm/openscad.js";
|
||||
|
||||
import { createEditorFS, getBrowserFSLibrariesMounts, symlinkLibraries } from "../fs/filesystem";
|
||||
import { createEditorFS, getBrowserFSLibrariesMounts, getParentDir, symlinkLibraries } from "../fs/filesystem";
|
||||
import { OpenSCADInvocation, OpenSCADInvocationResults } from "./openscad-runner";
|
||||
import { zipArchives } from "../fs/zip-archives";
|
||||
declare var BrowserFS: BrowserFSInterface
|
||||
|
|
@ -33,41 +33,43 @@ addEventListener('message', async (e) => {
|
|||
console.debug('stderr: ' + text);
|
||||
mergedOutputs.push({ stderr: text })
|
||||
},
|
||||
// ENV: {
|
||||
// OPENSCADPATH: '/home'
|
||||
// }
|
||||
});
|
||||
|
||||
// const librariesFolder = '/home/.local/share/OpenSCAD/libraries'
|
||||
const fs = await createEditorFS();
|
||||
await symlinkLibraries(allArchiveNames, fs, librariesFolder, '/home');//'/home', '/home');
|
||||
|
||||
// This will mount lots of libraries' ZIP archives under /libraries/<name> -> <name>.zip
|
||||
await createEditorFS('');
|
||||
|
||||
instance.FS.mkdir('/libraries');
|
||||
|
||||
// https://github.com/emscripten-core/emscripten/issues/10061
|
||||
const BFS = new BrowserFS.EmscriptenFS(
|
||||
instance.FS,
|
||||
instance.PATH ?? {
|
||||
join2: (a: string, b: string) => `${a}/${b}`,
|
||||
join: (...args: string[]) => args.join('/'),
|
||||
}, instance.ERRNO_CODES ?? {});
|
||||
instance.FS.mount(BFS, {root: '/home'}, '/home');
|
||||
},
|
||||
instance.ERRNO_CODES ?? {}
|
||||
);
|
||||
|
||||
instance.FS.mount(BFS, {root: '/'}, '/libraries');
|
||||
|
||||
//await symlinkLibraries(allArchiveNames, instance.FS, '/home/libraries', '/home');
|
||||
await symlinkLibraries(allArchiveNames, instance.FS, '/libraries', "/");
|
||||
|
||||
instance.FS.chdir('/home');
|
||||
// Fonts are seemingly resolved from $(cwd)/fonts
|
||||
instance.FS.chdir("/");
|
||||
|
||||
if (inputs) {
|
||||
for (const [path, content] of inputs) {
|
||||
try {
|
||||
// const parent = getParentDir(path);
|
||||
// instance.FS.writeFile(path, content);
|
||||
fs.writeFile(path, content);
|
||||
instance.FS.writeFile(path, content);
|
||||
// fs.writeFile(path, content);
|
||||
} catch (e) {
|
||||
console.error(`Error while trying to write ${path}`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Invoking OpenSCAD from ', workingDir, args)
|
||||
console.log('Invoking OpenSCAD with: ', args)
|
||||
const start = performance.now();
|
||||
const exitCode = instance.callMain(args);
|
||||
const end = performance.now();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import defaultScad from './default-scad';
|
||||
import { State } from './app-state';
|
||||
|
||||
export const defaultSourcePath = 'playground.scad';
|
||||
export const defaultSourcePath = '/playground.scad';
|
||||
|
||||
export const blankProjectState: State = {
|
||||
params: {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import { formatBytes, formatMillis } from '../utils'
|
|||
import { getParentDir } from "../fs/filesystem";
|
||||
|
||||
// export const isFileWritable = (path: string) => !path.startsWith('/');
|
||||
export const isFileWritable = (path: string) => getParentDir(path) === '/home';
|
||||
// export const isFileWritable = (path: string) => !path.startsWith(librariesFolder + '/');
|
||||
// export const isFileWritable = (path: string) => getParentDir(path) === '/home';
|
||||
// export const isFileWritable = (path: string) => !path.startsWith('/libraries/');
|
||||
|
||||
export class Model {
|
||||
constructor(private fs: FS, public state: State, private setStateCallback?: (state: State) => void) {
|
||||
|
|
@ -96,7 +96,11 @@ export class Model {
|
|||
// alert(`TODO: open ${path}`);
|
||||
if (this.mutate(s => {
|
||||
s.params.source = new TextDecoder("utf-8").decode(this.fs.readFileSync(path));
|
||||
s.params.sourcePath = path;
|
||||
if (s.params.sourcePath != path) {
|
||||
s.params.sourcePath = path;
|
||||
s.lastCheckerRun = undefined;
|
||||
s.output = undefined;
|
||||
}
|
||||
})) {
|
||||
this.processSource();
|
||||
}
|
||||
|
|
@ -110,10 +114,10 @@ export class Model {
|
|||
|
||||
private processSource() {
|
||||
const params = this.state.params;
|
||||
if (isFileWritable(params.sourcePath)) {
|
||||
const absolutePath = `/home/${params.sourcePath}`;
|
||||
this.fs.writeFile(absolutePath, params.source);
|
||||
}
|
||||
// if (isFileWritable(params.sourcePath)) {
|
||||
// const absolutePath = params.sourcePath.startsWith('/') ? params.sourcePath : `/${params.sourcePath}`;
|
||||
this.fs.writeFile(params.sourcePath, params.source);
|
||||
// }
|
||||
this.checkSyntax();
|
||||
this.render({isPreview: true, now: false});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue