Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse Error || Result Error #3065

Merged
merged 11 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps/zui/src/core/state-object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {useState} from "react"

export function useStateObject<T>(init: T) {
const [state, setState] = useState<T>(init)

return {
...state,
set: setState,
setItem: (key: string, value: any) => {
setState((prev) => ({...prev, [key]: value}))
},
}
}

export type StateObject<S> = S & ReturnType<typeof useStateObject>
27 changes: 27 additions & 0 deletions apps/zui/src/core/view-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {Dispatch, State, Store} from "src/js/state/types"
import {ipc} from "src/modules/bullet/view"
import {invoke} from "./invoke"

type Selector = (state: State, ...args: any) => any

export class ViewHandler {
static store: Store
static invoke = invoke
protected invoke = invoke

protected get store() {
return ViewHandler.store
}

protected dispatch(action: Parameters<Dispatch>[0]) {
return this.store.dispatch(action)
}

protected select<T extends Selector>(selector: T): ReturnType<T> {
return selector(this.store.getState())
}

protected request(path: string, params?: object) {
return ipc.request(path, params)
}
}
8 changes: 6 additions & 2 deletions apps/zui/src/domain/editor/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import {createOperation} from "src/core/operations"
import {lake} from "src/zui"

export const parse = createOperation("editor.parse", async (ctx, string) => {
const resp = await lake.client.compile(string)
return resp.toJS()
try {
const resp = await lake.client.compile(string)
return resp.toJS()
} catch (error) {
return {error: error.toString()}
}
})
7 changes: 3 additions & 4 deletions apps/zui/src/domain/editor/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ test("editor.parse", async () => {
})

test("editor.parse error", async () => {
await expect(parse("from source | ;;;(")).rejects.toHaveProperty(
"error",
expect.stringContaining("error parsing Zed at column 15")
)
await expect(parse("from source | ;;;(")).resolves.toEqual({
error: expect.stringContaining("error parsing Zed at column 15"),
})
})
14 changes: 2 additions & 12 deletions apps/zui/src/domain/session/handlers/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,8 @@ export const resetQuery = createHandler("session.resetQuery", () => {
session.navigate(session.snapshot)
})

const fetchAst = createHandler(async ({invoke}, string) => {
let tree
try {
tree = await invoke("editor.parse", string)
} catch (error) {
tree = {error}
}
return tree
})

export const fetchQueryInfo = createHandler(async (_, query: string) => {
const tree = await fetchAst(query)
export const fetchQueryInfo = createHandler(async ({invoke}, query: string) => {
const tree = await invoke("editor.parse", query)
const ast = new ZedAst(tree, tree.error)
return {
isSummarized: ast.isSummarized,
Expand Down
2 changes: 2 additions & 0 deletions apps/zui/src/js/initializers/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {createWaitForSelector} from "src/app/core/state/create-wait-for-selector
import {initAsyncTasks} from "./init-async-tasks"
import {Renderer} from "src/core/renderer"
import {initDomainModels} from "./init-domain-models"
import {ViewHandler} from "src/core/view-handler"

const getWindowId = () => {
const params = new URLSearchParams(window.location.search)
Expand Down Expand Up @@ -62,6 +63,7 @@ export default async function initialize(
initDomainModels({
store,
})
ViewHandler.store = store
setMenuContext({select: (fn) => fn(store.getState()), api})
initDebugGlobals(store, api)
initAutosave(store)
Expand Down
1 change: 1 addition & 0 deletions apps/zui/src/js/state/QueryInfo/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export const get = activeTabSelect((tab) => {
return tab.queryInfo
})

export const getParseError = createSelector(get, (info) => info.error)
export const getIsParsed = createSelector(get, (info) => info.isParsed)
export const getIsSummarized = createSelector(get, (info) => info.isSummarized)
15 changes: 15 additions & 0 deletions apps/zui/src/modules/bullet/main/application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {ipc} from "./ipc"

class Application {
controllers: any[] = []

config(fn) {
return fn(this)
}

boot() {
ipc.listen()
}
}

export const BulletApplication = new Application()
1 change: 1 addition & 0 deletions apps/zui/src/modules/bullet/main/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./application"
20 changes: 20 additions & 0 deletions apps/zui/src/modules/bullet/main/ipc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {ipcMain} from "electron"
import {camelCase, capitalize} from "lodash"
import {BulletApplication} from "./application"

class MainIpc {
listen() {
ipcMain.handle("bullet:view-request", (e, controllerAction, params) => {
const [shortName, action] = controllerAction.split("#")
const name = capitalize(camelCase(shortName)) + "Controller"
const Controller = BulletApplication.controllers[name]
if (!Controller) {
throw new Error("ControllerNotFound: " + controllerAction)
}
const instance = new Controller()
return instance[action](params)
})
}
}

export const ipc = new MainIpc()
1 change: 1 addition & 0 deletions apps/zui/src/modules/bullet/view/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ipc"
7 changes: 7 additions & 0 deletions apps/zui/src/modules/bullet/view/ipc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class ViewIpc {
request(path: string, params: any) {
return global.zui.invoke("bullet:view-request", path, params)
}
}

export const ipc = new ViewIpc()
4 changes: 2 additions & 2 deletions apps/zui/src/views/application/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {useAppMenu} from "./use-app-menu"
import {WelcomePage} from "src/views/welcome-page"
import {useReleaseNotes} from "./use-release-notes"
import {InitPool, Show} from "src/views/pool-page"
import {SessionRoute} from "src/views/session-page/route"
import Head from "next/head"
import {useTabId} from "src/app/core/hooks/use-tab-id"
import {NoTabsPane} from "src/views/no-tabs-pane"
import {SessionPage} from "../session-page"

function AppRoutes() {
return (
Expand All @@ -27,7 +27,7 @@ function AppRoutes() {
</InitPool>
</Route>
<Route path={routes.query.path}>
<SessionRoute />
<SessionPage />
</Route>
<Route path={routes.welcome.path}>
<WelcomePage />
Expand Down
5 changes: 3 additions & 2 deletions apps/zui/src/views/results-pane/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Results from "src/js/state/Results"
import {RESULTS_QUERY} from "src/views/results-pane/run-results-query"
import {useDataTransition} from "src/util/hooks/use-data-transition"
import useResizeObserver from "use-resize-observer"
import QueryInfo from "src/js/state/QueryInfo"

function useContextValue(parentRef: React.RefObject<HTMLDivElement>) {
const rect = useResizeObserver({ref: parentRef})
Expand All @@ -15,12 +16,12 @@ function useContextValue(parentRef: React.RefObject<HTMLDivElement>) {
const r = useResults(RESULTS_QUERY)
const results = useDataTransition(r, r.data.length === 0 && fetching)
const shapes = useMemo(() => Object.values(results.shapes), [results.shapes])

const parseError = useSelector(QueryInfo.getParseError)
return {
width: rect.width ?? 1000,
height: rect.height ?? 1000,
view: useSelector(Layout.getResultsView),
error: results.error,
error: parseError || results.error,
values: results.data,
shapes,
isSingleShape: shapes.length === 1,
Expand Down
83 changes: 83 additions & 0 deletions apps/zui/src/views/session-page/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {ViewHandler} from "src/core/view-handler"
import {Active} from "src/models/active"
import QueryInfo from "src/js/state/QueryInfo"
import Tabs from "src/js/state/Tabs"
import Notice from "src/js/state/Notice"
import Editor from "src/js/state/Editor"
import {startTransition} from "react"
import {
runResultsCount,
runResultsMain,
} from "../results-pane/run-results-query"
import Layout from "src/js/state/Layout"
import {runHistogramQuery} from "../histogram-pane/run-query"
import {fetchQueryInfo} from "src/domain/session/handlers"
import Current from "src/js/state/Current"
import Pools from "src/js/state/Pools"
import {syncPool} from "src/app/core/pools/sync-pool"

type Props = {
locationKey: string
}

export class SessionPageHandler extends ViewHandler {
constructor(public props: Props) {
super()
}

load() {
this.reset()
this.setEditorValues()
this.fetchResults()
this.parseQueryText()
}

private reset() {
this.dispatch(QueryInfo.reset())
this.dispatch(Tabs.loaded(this.props.locationKey))
this.dispatch(Notice.dismiss()) // This may not be needed any more
}

private setEditorValues() {
const snapshot = Active.session.snapshot
// Give editor a chance to update by scheduling this update
setTimeout(() => {
this.dispatch(Editor.setValue(snapshot.attrs.value ?? ""))
this.dispatch(Editor.setPins(snapshot.attrs.pins || []))
})
}

private fetchResults() {
startTransition(() => {
runResultsMain()
runResultsCount()
if (this.histogramVisible) runHistogramQuery()
})
}

private get histogramVisible() {
return this.select(Layout.getShowHistogram)
}

private async parseQueryText() {
const {session} = Active
const lakeId = this.select(Current.getLakeId)
const program = this.select(Current.getQueryText)
const history = this.select(Current.getHistory)

fetchQueryInfo(program).then((info) => {
const {poolName, error} = info
const pool = this.select(Pools.getByName(lakeId, poolName))

this.dispatch(QueryInfo.set({isParsed: true, ...info}))
this.invoke("updatePluginSessionOp", {poolName, program})
if (pool && !pool.hasSpan()) {
this.dispatch(syncPool(pool.id, lakeId))
}

if (!error && history.action === "PUSH") {
session.pushHistory()
}
})
}
}
15 changes: 15 additions & 0 deletions apps/zui/src/views/session-page/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import {useLayoutEffect} from "react"
import {Editor} from "./editor"
import {Footer} from "./footer"
import {Grid} from "./grid"
import {Pins} from "./pins"
import {Results} from "./results"
import {Toolbar} from "./toolbar"
import {useSelector} from "react-redux"
import Current from "src/js/state/Current"
import Tab from "src/js/state/Tab"
import {SessionPageHandler} from "./handler"

export function SessionPage() {
const locationKey = useSelector(Current.getLocation).key
const tabKey = useSelector(Tab.getLastLocationKey)
const handler = new SessionPageHandler({locationKey})

useLayoutEffect(() => {
// When you switch tabs, the location key changes, but you don't want to reload
const tabHasLoaded = tabKey === locationKey
if (!tabHasLoaded) handler.load()
}, [locationKey, tabKey])

return (
<Grid>
<Toolbar />
Expand Down
68 changes: 0 additions & 68 deletions apps/zui/src/views/session-page/loader.ts

This file was deleted.

Loading
Loading