Consider currently written path when opening file browser (#10076)

We pass the current path as default to show(Open|Save)FileDialog.

# Important Notes
There is `wrapper` method of Ast, I assumed it is suitable also for Groups.
This commit is contained in:
Adam Obuchowicz 2024-05-30 09:25:25 +02:00 committed by GitHub
parent ca8d715d5a
commit 65737b34f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 53 additions and 23 deletions

1
app/gui2/env.d.ts vendored
View File

@ -29,6 +29,7 @@ interface FileBrowserApi {
*/
readonly openFileBrowser: (
kind: 'file' | 'directory' | 'default' | 'filePath',
defaultPath?: string,
) => Promise<string[] | undefined>
}

View File

@ -1766,6 +1766,10 @@ export class Group extends Ast {
return this.module.get(this.fields.get('expression')?.node)
}
wrappedExpression(): Ast | undefined {
return this.expression
}
*concreteChildren(_verbatim?: boolean): IterableIterator<RawNodeChild> {
const { open, expression, close } = getAll(this.fields)
if (open) yield open

View File

@ -8,7 +8,9 @@ import { Score, WidgetInput, defineWidget, widgetProps } from '@/providers/widge
import { useGraphStore } from '@/stores/graph'
import type { RequiredImport } from '@/stores/graph/imports'
import { Ast } from '@/util/ast'
import { Pattern } from '@/util/ast/match'
import { ArgumentInfoKey } from '@/util/callTree'
import { TextLiteral } from 'shared/ast'
import { computed } from 'vue'
const props = defineProps(widgetProps(widgetDefinition))
@ -51,8 +53,22 @@ const label = computed(() => {
}
})
const FILE_CONSTRUCTOR = FILE_TYPE + '.new'
const FILE_SHORT_CONSTRUCTOR = 'File.new'
const fileConPattern = Pattern.parse(`${FILE_TYPE}.new __`)
const fileShortConPattern = Pattern.parse(`File.new __`)
const currentPath = computed(() => {
if (typeof props.input.value === 'string') {
return props.input.value
} else if (props.input.value) {
const expression = props.input.value.innerExpression()
const match = fileShortConPattern.match(expression) ?? fileConPattern.match(expression)
const pathAst =
match && match[0] ? expression.module.get(match[0]).innerExpression() : expression
if (pathAst instanceof TextLiteral) {
return pathAst.rawTextContent
}
}
return undefined
})
function makeValue(edit: Ast.MutableModule, useFileConstructor: boolean, path: string): Ast.Owned {
if (useFileConstructor) {
@ -63,29 +79,32 @@ function makeValue(edit: Ast.MutableModule, useFileConstructor: boolean, path: s
import: 'File',
} as RequiredImport
const conflicts = graph.addMissingImports(edit, [requiredImport])
const constructor = conflicts != null ? FILE_CONSTRUCTOR : FILE_SHORT_CONSTRUCTOR
const constructorAst = Ast.PropertyAccess.tryParse(constructor, edit)
if (constructorAst == null) {
throw new Error(`Failed to parse constructor as AST: ${constructor}`)
}
return Ast.App.new(edit, constructorAst, undefined, arg)
const pattern = conflicts ? fileConPattern : fileShortConPattern
return pattern.instantiate(edit, [arg])
} else {
return Ast.TextLiteral.new(path, edit)
}
}
const onClick = async () => {
const selected = await window.fileBrowserApi.openFileBrowser(dialogKind.value)
if (selected != null && selected[0] != null) {
const edit = graph.startEdit()
const value = makeValue(edit, insertAsFileConstructor.value, selected[0])
props.onUpdate({
edit,
portUpdate: {
value,
origin: props.input.portId,
},
})
if (!window.fileBrowserApi) {
console.error('File browser not supported!')
} else {
const selected = await window.fileBrowserApi.openFileBrowser(
dialogKind.value,
currentPath.value,
)
if (selected != null && selected[0] != null) {
const edit = graph.startEdit()
const value = makeValue(edit, insertAsFileConstructor.value, selected[0])
props.onUpdate({
edit,
portUpdate: {
value,
origin: props.input.portId,
},
})
}
}
}

View File

@ -445,13 +445,18 @@ class App {
)
electron.ipcMain.handle(
ipc.Channel.openFileBrowser,
async (_event, kind: 'default' | 'directory' | 'file' | 'filePath') => {
logger.log('Request for opening browser for ', kind)
async (
_event,
kind: 'default' | 'directory' | 'file' | 'filePath',
defaultPath?: string
) => {
logger.log('Request for opening browser for ', kind, defaultPath)
let retval = null
if (kind === 'filePath') {
// "Accept", as the file won't be created immediately.
const { canceled, filePath } = await electron.dialog.showSaveDialog({
buttonLabel: 'Accept',
...(defaultPath != null ? { defaultPath } : {}),
})
if (!canceled) {
retval = [filePath]
@ -469,6 +474,7 @@ class App {
: ['openFile']
const { canceled, filePaths } = await electron.dialog.showOpenDialog({
properties,
...(defaultPath != null ? { defaultPath } : {}),
})
if (!canceled) {
retval = filePaths

View File

@ -173,8 +173,8 @@ const AUTHENTICATION_API = {
electron.contextBridge.exposeInMainWorld(AUTHENTICATION_API_KEY, AUTHENTICATION_API)
const FILE_BROWSER_API = {
openFileBrowser: (kind: 'any' | 'directory' | 'file' | 'filePath') =>
electron.ipcRenderer.invoke(ipc.Channel.openFileBrowser, kind),
openFileBrowser: (kind: 'any' | 'directory' | 'file' | 'filePath', defaultPath?: string) =>
electron.ipcRenderer.invoke(ipc.Channel.openFileBrowser, kind, defaultPath),
}
electron.contextBridge.exposeInMainWorld(FILE_BROWSER_API_KEY, FILE_BROWSER_API)