Skip to content
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
3 changes: 2 additions & 1 deletion apps/zui/src/app/core/models/active-query.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {QueryModel} from "src/js/models/query-model"
import {QueryVersion} from "src/js/state/QueryVersions/types"
import {EditorSnapshot} from "src/models/editor-snapshot"

export class ActiveQuery {
constructor(
Expand Down Expand Up @@ -60,6 +61,6 @@ export class ActiveQuery {
}

toZed() {
return QueryModel.versionToZed(this.version)
return new EditorSnapshot(this.version).toQueryText()
}
}
17 changes: 17 additions & 0 deletions apps/zui/src/components/zed-editor-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export class ZedEditorHandler {
constructor(public monaco, public editor) {}

focus() {
if (!this.mounted) return
setTimeout(() => this.editor.focus())
}

setErrors(markers) {
if (!this.mounted) return
this.monaco.editor.setModelMarkers(this.editor.getModel(), "zed", markers)
}

private get mounted() {
return !!this.monaco && !!this.editor
}
}
41 changes: 20 additions & 21 deletions apps/zui/src/components/zed-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {Editor} from "@monaco-editor/react"
import {useEffect, useRef} from "react"
import {Editor, useMonaco} from "@monaco-editor/react"
import {useEffect, useMemo, useRef} from "react"
import {useSelector} from "react-redux"
import {cmdOrCtrl} from "src/app/core/utils/keyboard"
import Config from "src/js/state/Config"
import {Marker} from "src/js/state/Editor/types"
import {useColorScheme} from "src/util/hooks/use-color-scheme"
import {ZedEditorHandler} from "./zed-editor-handler"

/**
*
Expand All @@ -30,31 +32,32 @@ export function ZedEditor(props: {
value: string
onChange: (value: string | undefined, ev: any) => void
autoFocus?: boolean
markers?: Marker[]
}) {
const ref = useRef<any>()
const {isDark} = useColorScheme()
const monaco = useMonaco()
const handler = useMemo(
() => new ZedEditorHandler(monaco, ref.current),
[monaco, ref.current]
)

// Keep this thing in focus as much as possible.
// Probably want to move this into parent.
useEffect(() => {
setTimeout(() => {
if (ref.current) {
ref.current.focus()
}
})
}, [props.path, props.value])
useEffect(() => handler.focus(), [props.path, props.value, handler])
useEffect(() => handler.setErrors(props.markers), [props.markers, handler])

return (
<Editor
wrapperProps={{
"data-testid": props.testId,
}}
height="100%"
width="100%"
value={props.value}
onChange={props.onChange}
language="zed"
onChange={props.onChange}
onMount={(editor) => (ref.current = editor)}
path={props.path}
theme={isDark ? "vs-dark" : "vs-light"}
value={props.value}
width="100%"
wrapperProps={{
"data-testid": props.testId,
}}
options={{
minimap: {enabled: false},
renderLineHighlight: "none",
Expand All @@ -64,10 +67,6 @@ export function ZedEditor(props: {
fontVariations: "inherit",
lineNumbersMinChars: 4,
}}
onMount={(editor) => {
ref.current = editor
}}
path={props.path}
/>
)
}
2 changes: 1 addition & 1 deletion apps/zui/src/domain/editor/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const describe = createOperation(
async (ctx, string, pool?) => {
try {
const resp = await lake.client.describeQuery(string, pool)
return resp.toJS()
return resp.error ? {error: resp} : resp
} catch (error) {
return {error: error.toString()}
}
Expand Down
26 changes: 1 addition & 25 deletions apps/zui/src/js/models/query-model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {Query} from "src/js/state/Queries/types"
import {isEmpty, last} from "lodash"
import {QueryPinInterface} from "../state/Editor/types"
import buildPin from "src/js/state/Editor/models/build-pin"
import {last} from "lodash"
import {QueryVersion} from "src/js/state/QueryVersions/types"
import {QuerySource} from "src/js/api/queries/types"

Expand Down Expand Up @@ -59,26 +57,4 @@ export class QueryModel implements Query {
isReadOnly: this.isReadOnly,
}
}

static versionToZed(version: QueryVersion): string {
let pinS = []
if (!isEmpty(version?.pins))
pinS = version.pins
.filter((p) => !p.disabled)
.map<QueryPinInterface>(buildPin)
.map((p) => p.toZed())
let s = pinS
.concat(version?.value ?? "")
.filter((s) => s.trim() !== "")
.join(" | ")
.trim()

if (isEmpty(s)) s = "*"

return s
}

toString(): string {
return QueryModel.versionToZed(this.current)
}
}
3 changes: 2 additions & 1 deletion apps/zui/src/js/state/Current/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import lake from "src/js/models/lake"
import {defaultLake} from "src/js/initializers/initLakeParams"
import {getActive} from "../Tabs/selectors"
import QueryInfo from "../QueryInfo"
import {EditorSnapshot} from "src/models/editor-snapshot"

export const getHistory = (
state,
Expand Down Expand Up @@ -54,7 +55,7 @@ export const getVersion = (state: State): QueryVersion => {
}

export const getQueryText = createSelector(getVersion, (version) => {
return QueryModel.versionToZed(version)
return new EditorSnapshot(version).toQueryText()
})

const getRawSession = (state: State) => {
Expand Down
6 changes: 5 additions & 1 deletion apps/zui/src/js/state/Editor/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {createSlice, PayloadAction} from "@reduxjs/toolkit"
import buildPin from "./models/build-pin"
import {FromQueryPin, QueryPin, TimeRangeQueryPin} from "./types"
import {FromQueryPin, QueryPin, TimeRangeQueryPin, Marker} from "./types"

const slice = createSlice({
name: "TAB_EDITOR",
Expand All @@ -9,6 +9,7 @@ const slice = createSlice({
pins: [] as QueryPin[],
pinEditIndex: null as null | number,
pinHoverIndex: null as null | number,
markers: [] as Marker[],
},
reducers: {
setValue(s, a: PayloadAction<string>) {
Expand Down Expand Up @@ -113,6 +114,9 @@ const slice = createSlice({
})
}
},
setMarkers(s, a: PayloadAction<Marker[]>) {
s.markers = a.payload
},
},
})

Expand Down
4 changes: 4 additions & 0 deletions apps/zui/src/js/state/Editor/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ export const getSnapshot = activeTabSelect((tab) => {
export const isEmpty = createSelector(getValue, getPins, (value, pins) => {
return value.trim() === "" && pins.length === 0
})

export const getMarkers = activeTabSelect((tab) => {
return tab.editor.markers
})
8 changes: 8 additions & 0 deletions apps/zui/src/js/state/Editor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ export interface QueryPinInterface {
empty(): boolean
toZed(): string
}

export type Marker = {
message: string
startLineNumber: number
startColumn: number
endLineNumber: number
endColumn: number
}
2 changes: 1 addition & 1 deletion apps/zui/src/js/state/QueryInfo/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const get = activeTabSelect((tab) => {
return tab.queryInfo
})

export const getParseError = createSelector(get, (info) => info.error)
export const getParseError = createSelector(get, (info) => info.error?.error)
export const getIsParsed = createSelector(get, (info) => info.isParsed)
export const getPoolName = createSelector(get, (info) => {
let source = find(info.sources || [], {kind: "Pool"})
Expand Down
2 changes: 1 addition & 1 deletion apps/zui/src/models/active.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {Session} from "./session"
import Current from "src/js/state/Current"
import {EditorSnapshot} from "./editor-snapshot"
import {BrowserTab} from "./browser-tab"
import Editor from "src/js/state/Editor"
import {Frame} from "./frame"
import {getActiveTab} from "src/js/state/Tabs/selectors"
import Editor from "src/js/state/Editor"

export class Active extends DomainModel {
static get tab() {
Expand Down
32 changes: 31 additions & 1 deletion apps/zui/src/models/editor-snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import {nanoid} from "@reduxjs/toolkit"
import {isEqual} from "lodash"
import {queryPath} from "src/app/router/utils/paths"
import {DomainModel} from "src/core/domain-model"
import {QueryPin} from "src/js/state/Editor/types"
import buildPin from "src/js/state/Editor/models/build-pin"
import {QueryPin, QueryPinInterface} from "src/js/state/Editor/types"
import QueryVersions from "src/js/state/QueryVersions"
import {SourceSet} from "./editor-snapshot/source-set"
import {Validator} from "./editor-snapshot/validator"

type Attrs = {
version: string
Expand All @@ -14,6 +17,8 @@ type Attrs = {
}

export class EditorSnapshot extends DomainModel<Attrs> {
validator = new Validator()

constructor(attrs: Partial<Attrs> = {}) {
super({
version: nanoid(),
Expand Down Expand Up @@ -50,6 +55,23 @@ export class EditorSnapshot extends DomainModel<Attrs> {
return this.attrs.parentId
}

activePins() {
return this.attrs.pins
.filter((pin) => !pin.disabled)
.map<QueryPinInterface>((attrs) => buildPin(attrs))
}

toSourceSet() {
return new SourceSet(
this.activePins().map((pin) => pin.toZed()),
this.attrs.value
)
}

toQueryText() {
return this.toSourceSet().contents
}

equals(other: EditorSnapshot) {
return (
isEqual(this.attrs.pins, other.attrs.pins) &&
Expand All @@ -65,4 +87,12 @@ export class EditorSnapshot extends DomainModel<Attrs> {
clone(attrs: Partial<Attrs>) {
return new EditorSnapshot({...this.attrs, ...attrs})
}

async isValid() {
return this.validator.validate(this)
}

get errors() {
return this.validator.errors
}
}
37 changes: 37 additions & 0 deletions apps/zui/src/models/editor-snapshot/compilation-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {Marker} from "src/js/state/Editor/types"
import {Source} from "./source"
import {SourceSet} from "./source-set"

export class CompilationError {
constructor(
public message: string,
public offset: number,
public end: number
) {}

toMarker(sourceSet: SourceSet) {
const source = sourceSet.sourceOf(this.offset)
const start = this.getStartPosition(source)
const end = this.getEndPosition(source)

return {
message: this.message,
startLineNumber: start.lineNumber,
startColumn: start.column,
endLineNumber: end ? end.lineNumber : start.lineNumber,
endColumn: end ? end.column : start.column + 1,
} as Marker
}

private hasRange() {
return this.end >= 0
}

private getStartPosition(source: Source) {
return source.position(this.offset)
}

private getEndPosition(source: Source) {
return this.hasRange() ? source.position(this.end) : null
}
}
28 changes: 28 additions & 0 deletions apps/zui/src/models/editor-snapshot/source-set.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {Source} from "./source"

export class SourceSet {
sources: Source[] = []
contents: string = ""

constructor(prefixes: string[], mainText: string) {
prefixes
.concat(mainText)
.filter((snippet) => snippet.trim().length)
.forEach((snippet) => this.appendSource(snippet))

if (!this.sources.length) this.appendSource("*")
}

appendSource(text: string) {
const isFirst = this.contents.length === 0
if (!isFirst) this.contents += " | "
this.sources.push(new Source(this.contents.length, text))
this.contents += text
}

sourceOf(pos: number) {
return this.sources.find(
(s) => pos >= s.start && pos < s.start + s.length + 1
)
}
}
33 changes: 33 additions & 0 deletions apps/zui/src/models/editor-snapshot/source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {sortedIndex} from "lodash"

export type Position = {
column: number
lineNumber: number
}

export class Source {
start: number
length: number
lines: number[]

constructor(start: number, text: string) {
this.start = start
this.length = text.length
this.lines = [0]

for (let k = 0; k < text.length; k++) {
if (text[k] === "\n") {
this.lines.push(k + 1)
}
}
}

position(pos: number): Position {
let offset = pos - this.start
let i = sortedIndex(this.lines, offset) - 1
return {
column: offset - this.lines[i] + 1,
lineNumber: i + 1,
}
}
}
Loading