Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into wip/jtulach/StateI…
Browse files Browse the repository at this point in the history
…nContext7115
  • Loading branch information
JaroslavTulach committed Feb 6, 2025
2 parents 952125c + a829bdb commit 84da6c6
Show file tree
Hide file tree
Showing 32 changed files with 726 additions and 381 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
- [When editing cells or header names in Table Editor Widget, `tab` and `enter`
keys jumps to next cell/ next row respectively.][12129]
- [Fixed bugs occurring after renaming project from within graph editor][12106].
- [Users having "Team" plan or above may now access shared directories in Cloud
File Browser][12208]
- [Added support for rendering numbered and nested lists][12190].
- [Removed `#` from default colum name][12222]

Expand All @@ -29,6 +31,7 @@
[12064]: https://github.com/enso-org/enso/pull/12064
[12129]: https://github.com/enso-org/enso/pull/12129
[12106]: https://github.com/enso-org/enso/pull/12106
[12208]: https://github.com/enso-org/enso/pull/12208
[12190]: https://github.com/enso-org/enso/pull/12190
[12222]: https://github.com/enso-org/enso/pull/12222

Expand All @@ -39,11 +42,17 @@
- [Reducing helper methods in `Standard.Base.Meta`.][12031]
- [Added Table.Offset][12071]
- [Added Column.Offset][12092]
- [When reading a Delimited file, if a row with more columns than expected is
encountered, extra columns can be added to the result.][12231]
- In `Delimited` format, the `keep_invalid_rows` setting has been renamed to
`on_invalid_rows`. The default behaviour was also changed to add any extra
columns instead of discarding them.

[11926]: https://github.com/enso-org/enso/pull/11926
[12031]: https://github.com/enso-org/enso/pull/12031
[12071]: https://github.com/enso-org/enso/pull/12071
[12092]: https://github.com/enso-org/enso/pull/12092
[12231]: https://github.com/enso-org/enso/pull/12231

#### Enso Language & Runtime

Expand Down
15 changes: 15 additions & 0 deletions app/gui/src/project-view/assets/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,18 @@
--code-editor-default-height: 30%;
--scrollbar-scrollable-opacity: 100%;
}

/* FIXME: Due to some bug in vue, when a component is used both as web components and normally,
It's styles are applied only on the former. Therefore additional "global" definitions must
be added here. */
.LoadingSpinner {
border-radius: 50%;
border: 4px solid;
border-color: rgba(0, 0, 0, 30%) #0000;
animation: s1 0.8s infinite;
}
@keyframes s1 {
to {
transform: rotate(0.5turn);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,8 @@ export const widgetDefinition = defineWidget(
position: absolute;
}
.LoadingSpinner {
border: 4px solid;
border-radius: 100%;
border-color: rgba(255, 255, 255, 90%) #0000;
animation: s1 0.8s infinite;
}
@keyframes s1 {
to {
transform: rotate(0.5turn);
}
}
.v-enter-active,
.v-leave-active {
Expand Down
103 changes: 81 additions & 22 deletions app/gui/src/project-view/components/widgets/FileBrowserWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,56 @@ import LoadingSpinner from '@/components/shared/LoadingSpinner.vue'
import SvgButton from '@/components/SvgButton.vue'
import SvgIcon from '@/components/SvgIcon.vue'
import { useBackend } from '@/composables/backend'
import { injectBackend } from '@/providers/backend'
import type { ToValue } from '@/util/reactivity'
import { useToast } from '@/util/toast'
import type {
DatalinkAsset,
DatalinkId,
DirectoryAsset,
DirectoryId,
FileAsset,
FileId,
} from 'enso-common/src/services/Backend'
import Backend, { assetIsDirectory, assetIsFile } from 'enso-common/src/services/Backend'
import Backend, {
assetIsDatalink,
assetIsDirectory,
assetIsFile,
} from 'enso-common/src/services/Backend'
import { computed, ref, toValue, watch } from 'vue'
import { Err, Ok, Result } from 'ydoc-shared/util/data/result'
const emit = defineEmits<{
pathSelected: [path: string]
}>()
const { query, ensureQueryData } = useBackend('remote')
const { query, fetch, ensureQueryData } = useBackend('remote')
const { remote: backend } = injectBackend()
const errorToast = useToast.error()
// === Current Directory ===
interface Directory {
id: DirectoryId | null
id: DirectoryId
title: string
}
const directoryStack = ref<Directory[]>([
{
id: null,
title: 'Cloud',
},
])
const currentDirectory = computed(() => directoryStack.value[directoryStack.value.length - 1]!)
const currentUser = query('usersMe', [])
const currentPath = computed(
() =>
currentUser.data.value &&
`enso://Users/${currentUser.data.value.name}${Array.from(directoryStack.value.slice(1), (frame) => '/' + frame.title).join()}`,
)
const currentOrganization = query('getOrganization', [])
const directoryStack = ref<Directory[]>([])
const isDirectoryStackInitializing = computed(() => directoryStack.value.length === 0)
const currentDirectory = computed(() => directoryStack.value[directoryStack.value.length - 1])
const currentPath = computed(() => {
if (!currentUser.data.value) return
let root = backend?.rootPath(currentUser.data.value) ?? 'enso://'
if (!root.endsWith('/')) root += '/'
return `${root}${directoryStack.value
.slice(1)
.map((dir) => `${dir.title}/`)
.join('')}`
})
// === Directory Contents ===
Expand All @@ -65,17 +79,19 @@ const { isPending, isError, data, error } = query(
)
const compareTitle = (a: { title: string }, b: { title: string }) => a.title.localeCompare(b.title)
const directories = computed(
() => data.value && data.value.filter<DirectoryAsset>(assetIsDirectory).sort(compareTitle),
() => data.value && data.value.filter((asset) => assetIsDirectory(asset)).sort(compareTitle),
)
const files = computed(
() => data.value && data.value.filter<FileAsset>(assetIsFile).sort(compareTitle),
() =>
data.value &&
data.value.filter((asset) => assetIsFile(asset) || assetIsDatalink(asset)).sort(compareTitle),
)
const isEmpty = computed(() => directories.value?.length === 0 && files.value?.length === 0)
// === Selected File ===
interface File {
id: FileId
id: FileId | DatalinkId
title: string
}
Expand All @@ -97,16 +113,27 @@ function enterDir(dir: DirectoryAsset) {
directoryStack.value.push(dir)
}
class DirNotFoundError {
constructor(public dirName: string) {}
toString() {
return `Directory "${this.dirName}" not found`
}
}
function popTo(index: number) {
directoryStack.value.splice(index + 1)
}
function chooseFile(file: FileAsset) {
function chooseFile(file: FileAsset | DatalinkAsset) {
selectedFile.value = file
}
const isBusy = computed(
() => isPending.value || (selectedFile.value && currentUser.isPending.value),
() =>
isDirectoryStackInitializing.value ||
isPending.value ||
(selectedFile.value && currentUser.isPending.value),
)
const anyError = computed(() =>
Expand All @@ -117,12 +144,44 @@ const anyError = computed(() =>
const selectedFilePath = computed(
() =>
selectedFile.value && currentPath.value && `${currentPath.value}/${selectedFile.value.title}`,
selectedFile.value && currentPath.value && `${currentPath.value}${selectedFile.value.title}`,
)
watch(selectedFilePath, (path) => {
if (path) emit('pathSelected', path)
})
// === Initialization ===
async function enterDirByName(name: string, stack: Directory[]): Promise<Result> {
const currentDir = stack[stack.length - 1]
if (currentDir == null) return Err('Stack is empty')
const content = await fetch('listDirectory', listDirectoryArgs(currentDir))
const nextDir = content.find(
(asset): asset is DirectoryAsset => assetIsDirectory(asset) && asset.title === name,
)
if (!nextDir) return Err(new DirNotFoundError(name))
stack.push(nextDir)
return Ok()
}
Promise.all([currentUser.promise.value, currentOrganization.promise.value]).then(
async ([user, organization]) => {
if (!user) {
errorToast.show('Cannot load file list: not logged in.')
return
}
const rootDirectoryId =
backend?.rootDirectoryId(user, organization, null) ?? user.rootDirectoryId
const stack = [{ id: rootDirectoryId, title: 'Cloud' }]
if (rootDirectoryId != user.rootDirectoryId) {
let result = await enterDirByName('Users', stack)
result = result.ok ? await enterDirByName(user.name, stack) : result
if (!result.ok) errorToast.reportError(result.error, 'Cannot enter home directory')
}
directoryStack.value = stack
},
)
</script>

<template>
Expand All @@ -143,7 +202,7 @@ watch(selectedFilePath, (path) => {
<div v-if="isBusy" class="centerContent contents"><LoadingSpinner /></div>
<div v-else-if="anyError" class="centerContent contents">Error: {{ anyError }}</div>
<div v-else-if="isEmpty" class="centerContent contents">Directory is empty</div>
<div v-else :key="currentDirectory.id ?? 'root'" class="listing contents">
<div v-else :key="currentDirectory?.id ?? 'root'" class="listing contents">
<TransitionGroup>
<div v-for="entry in directories" :key="entry.id">
<SvgButton :label="entry.title" name="folder" class="entry" @click="enterDir(entry)" />
Expand Down
9 changes: 8 additions & 1 deletion app/gui/src/project-view/composables/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ export function useBackend(which: 'remote' | 'project') {
return useQuery(backendQueryOptions(method, args, backend))
}

function fetch<Method extends BackendMethods>(
method: Method,
args: ToValue<Parameters<Backend[Method]> | undefined>,
): Promise<Awaited<ReturnType<Backend[Method]>>> {
return queryClient.fetchQuery(backendQueryOptions(method, args, backend))
}

/** Enable prefetching of the specified query. */
function prefetch<Method extends BackendMethods>(
method: Method,
Expand All @@ -67,5 +74,5 @@ export function useBackend(which: 'remote' | 'project') {
return queryClient.ensureQueryData(backendQueryOptions(method, args, backend))
}

return { query, prefetch, ensureQueryData }
return { query, fetch, prefetch, ensureQueryData }
}
2 changes: 2 additions & 0 deletions distribution/lib/Standard/AWS/0.0.0-dev/src/AWS.enso
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type AWS
- region_service: The region and service to use for signing the request.
Defaults to the region and service parsed from the URI.
@uri (Text_Input display=..Always)
@method HTTP_Method.default_fetch_widget
@headers Header.default_widget
@format File_Format.default_widget
@credentials AWS_Credential.default_widget
Expand Down Expand Up @@ -71,6 +72,7 @@ type AWS
- region_service: The region and service to use for signing the request.
Defaults to the region and service parsed from the URI.
@uri (Text_Input display=..Always)
@method HTTP_Method.default_post_widget
@headers Header.default_widget
@format File_Format.default_widget
@credentials AWS_Credential.default_widget
Expand Down
3 changes: 3 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data.enso
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ list (directory=enso_project.root) (name_filter:Text="") recursive:Boolean=False
file = enso_project.data / "spreadsheet.xls"
Data.fetch URL . body . write file
@uri (Text_Input display=..Always)
@method HTTP_Method.default_fetch_widget
@format Data_Read_Helpers.format_widget_with_raw_response
@headers Header.default_widget
fetch : (URI | Text) -> HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Cache_Policy -> Any ! Request_Error | HTTP_Error
Expand Down Expand Up @@ -427,6 +428,7 @@ fetch (uri:(URI | Text)=(Missing_Argument.throw "uri")) (method:HTTP_Method=..Ge
form_data = Dictionary.from_vector [["key", "val"], ["a_file", test_file]]
response = Data.post url_post (Request_Body.Form_Data form_data url_encoded=True)
@uri (Text_Input display=..Always)
@method HTTP_Method.default_post_widget
@headers Header.default_widget
@response_format Data_Read_Helpers.format_widget_with_raw_response
post : (URI | Text) -> Request_Body -> HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Any ! Request_Error | HTTP_Error
Expand Down Expand Up @@ -456,6 +458,7 @@ post (uri:(URI | Text)=(Missing_Argument.throw "uri")) (body:Request_Body=..Empt
Defaults to `HTTP_Method.Get`.
- headers: The headers to send with the request. Defaults to an empty vector.
@uri (Text_Input display=..Always)
@method HTTP_Method.default_fetch_widget
@headers Header.default_widget
download : (URI | Text) -> Writable_File -> Download_Mode -> HTTP_Method -> Vector (Header | Pair Text Text) -> File ! Request_Error | HTTP_Error
download (uri:(URI | Text)=(Missing_Argument.throw "uri")) file:Writable_File (replace_existing:Download_Mode=..If_Not_Exists) (method:HTTP_Method=..Get) (headers:(Vector (Header | Pair Text Text))=[]) =
Expand Down
31 changes: 8 additions & 23 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso
Original file line number Diff line number Diff line change
Expand Up @@ -474,41 +474,26 @@ type Missing_Argument
## PRIVATE
Indicates that an expression cannot be evaluated because somewhere within
it, a function does not have all required arguments provided.
Error (argument_name : Text) (function_name : Text | Nothing = Nothing) (call_location : Source_Location | Nothing = Nothing)
Error (argument_name : Text) (function_name : Text | Nothing = Nothing) (call_location : Source_Location | Nothing = Nothing) (message : Text = "Provide a value for the argument `" + argument_name + "`.")

## PRIVATE
to_display_text : Text
to_display_text self = case self.function_name of
Nothing -> "Provide a value for the argument `" + self.argument_name + "`."
_ -> "Missing required argument `" + self.argument_name + "` in function `" + self.function_name + "`."

## PRIVATE
Throws an error saying that a required argument has not been provided.
throw : Text -> Nothing ! Missing_Argument
throw argument_name:Text =
Error.throw (Missing_Argument.Error argument_name)
to_display_text self = self.message

## PRIVATE
Throws an error saying that a required argument has not been provided.

This function is supposed to be used as a default value for arguments
that are supposed to be required and should prevent errors stemming from
a not-fully-applied function being passed around. Instead, this error is
raised and propagated to the top, so that the IDE can see that the node
still has some missing arguments within it.

> Example

my_function (arg1 = Missing_Argument.ensure_present "arg1") (arg2 = 100) = arg1+arg2
ensure_present : Text -> Nothing ! Missing_Argument
ensure_present (argument_name : Text = Missing_Argument.ensure_present "argument_name") =
throw : Text -> Text | Nothing -> Nothing ! Missing_Argument
throw argument_name:Text message_override:Text|Nothing=Nothing =
stack_trace = Runtime.get_stack_trace
function_frame = stack_trace . at 1
caller_frame = stack_trace . at 2

function_name = function_frame.name
call_location = caller_frame.source_location
Error.throw (Missing_Argument.Error argument_name function_name call_location)

error = if message_override.is_nothing then Missing_Argument.Error argument_name function_name call_location else
Missing_Argument.Error argument_name function_name call_location message_override
Error.throw error

## Warning when additional warnings occurred.
@Builtin_Type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Text.to_uri self = URI.parse self
Defaults to `Auto_Detect`. If `Raw_Response` is selected or if the format
cannot be determined automatically, a raw HTTP `Response` will be returned.
@format Data_Read_Helpers.format_widget_with_raw_response
@method HTTP_Method.default_fetch_widget
@headers Header.default_widget
URI.fetch : HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Any
URI.fetch self (method:HTTP_Method=..Get) headers=[] format=Auto_Detect =
Expand Down Expand Up @@ -87,6 +88,7 @@ URI.fetch self (method:HTTP_Method=..Get) headers=[] format=Auto_Detect =
- File: shorthand for `Request_Body.Binary that_file`.
- Any other Enso object: shorthand for `Request_Body.Json that_object`.
@headers Header.default_widget
@method HTTP_Method.default_post_widget
@response_format Data_Read_Helpers.format_widget_with_raw_response
URI.post : Request_Body -> HTTP_Method -> Vector (Header | Pair Text Text) -> File_Format -> Any
URI.post self (body:Request_Body=..Empty) (method:HTTP_Method=..Post) (headers:(Vector (Header | Pair Text Text))=[]) (response_format = Auto_Detect) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ private _resolve_form_body (form_data:(Dictionary Text (Text | File))) (url_enco
## PRIVATE
if_fetch_method : HTTP_Method -> Function -> Any -> Any ! Illegal_Argument
if_fetch_method method:HTTP_Method ~action ~if_not=(Error.throw (Illegal_Argument.Error ("Unsupported method " + method.to_display_text))) =
if [HTTP_Method.Get, HTTP_Method.Head, HTTP_Method.Options].contains method then action else
if [HTTP_Method.Get, HTTP_Method.Head, HTTP_Method.Options, HTTP_Method.Trace].contains method then action else
if_not

## PRIVATE
Expand Down
Loading

0 comments on commit 84da6c6

Please sign in to comment.