Skip to content

Commit

Permalink
feat: add flux editor errors
Browse files Browse the repository at this point in the history
  • Loading branch information
bthesorceror authored and jsteenb2 committed Mar 19, 2020
1 parent 51b0b9f commit b84f5db
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 72 deletions.
10 changes: 10 additions & 0 deletions ui/cypress/e2e/explorer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,16 @@ describe('DataExplorer', () => {
.click()
})

it('shows flux errors', () => {
cy.getByTestID('time-machine--bottom').then(() => {
cy.getByTestID('flux-editor').within(() => {
cy.get('textarea').type('foo |> bar', {force: true})

cy.get('.squiggly-error').should('be.visible')
})
})
})

it('enables the submit button when a query is typed', () => {
cy.getByTestID('time-machine-submit-button').should('be.disabled')

Expand Down
3 changes: 2 additions & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@
},
"dependencies": {
"@influxdata/clockface": "2.0.2",
"@influxdata/flux-lsp-browser": "0.3.1",
"@influxdata/flux-lsp-browser": "^0.4.0",
"@influxdata/flux-parser": "^0.3.0",
"@influxdata/flux": "^0.4.0",
"@influxdata/giraffe": "0.17.6",
"@influxdata/influx": "0.5.5",
"@influxdata/influxdb-templates": "0.9.0",
Expand Down
32 changes: 1 addition & 31 deletions ui/src/external/monaco.flux.completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,39 +33,9 @@ export function registerCompletion(
...context,
triggerKind: CompletionTriggerKind.TriggerCharacter,
})

return p2m.asCompletionResult(items, defaultRange)
},
triggerCharacters: [
'.',
':',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
],
triggerCharacters: ['.', ':', '('],
}
)

Expand Down
4 changes: 2 additions & 2 deletions ui/src/external/monaco.flux.messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ interface Message {
jsonrpc: string
}

interface ResponseMessage extends Message {
export interface ResponseMessage extends Message {
id: number | string | null
result?: string | number | boolean | object | null
}

interface NotificationMessage extends Message {
export interface NotificationMessage extends Message {
method: string
params?: object[] | object
}
Expand Down
127 changes: 97 additions & 30 deletions ui/src/external/monaco.flux.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ import {
completion,
didOpen,
didChange,
NotificationMessage,
} from 'src/external/monaco.flux.messages'
import {registerCompletion} from './monaco.flux.completions'
import {AppState, LocalStorage} from 'src/types'
import {getVariableAssignments} from '../timeMachine/selectors'
import {buildVarsOption} from '../variables/utils/buildVarsOption'

import {store} from '../index'

import {Store} from 'redux'
import {
CompletionItem,
CompletionContext,
Position,
Diagnostic,
} from 'monaco-languageclient/lib/services'
import {Server} from '@influxdata/flux-lsp-browser'

Expand All @@ -23,19 +31,19 @@ export interface WASMServer extends Server {
register_buckets_callback: (BucketCallback) => void
}

const queue = []
let server = null,
loading = false
import {format_from_js_file} from '@influxdata/flux'

export class LSPServer {
private server: WASMServer
private messageID: number = 0
private buckets: string[] = []
private documentVersions: {[key: string]: number} = {}
public store: Store<AppState & LocalStorage>

constructor(server: WASMServer) {
constructor(server: WASMServer, reduxStore = store) {
this.server = server
this.server.register_buckets_callback(this.getBuckets)
this.store = reduxStore
}

getBuckets = () => {
Expand All @@ -46,14 +54,6 @@ export class LSPServer {
this.buckets = buckets
}

async send(message: LSPMessage): Promise<LSPResponse> {
const body = JSON.stringify(message)
const fullMessage = `Content-Length: ${body.length}\r\n\r\n${body}`
const response = await this.server.process(fullMessage)

return parseResponse(response)
}

initialize() {
return this.send(initialize(this.currentMessageID))
}
Expand All @@ -63,9 +63,19 @@ export class LSPServer {
position: Position,
context: CompletionContext
): Promise<CompletionItem[]> {
await this.sendPrelude(uri)

try {
const response = (await this.send(
completion(this.currentMessageID, uri, position, context)
completion(
this.currentMessageID,
uri,
{
...position,
line: position.line,
},
context
)
)) as {result?: {items?: []}}

return (response.result || {}).items || []
Expand All @@ -75,44 +85,101 @@ export class LSPServer {
}

async didOpen(uri: string, script: string) {
await this.send(didOpen(this.currentMessageID, uri, script, 1))
await this.sendPrelude(uri)
const response = await this.send(
didOpen(this.currentMessageID, uri, script, 1)
)
this.documentVersions[uri] = 1

return this.parseDiagnostics(response as NotificationMessage)
}

async didChange(uri: string, script: string) {
await this.sendPrelude(uri)
const version = this.documentVersions[uri] || 1
await this.send(didChange(this.currentMessageID, uri, script, version + 1))
const response = await this.send(
didChange(this.currentMessageID, uri, script, version + 1)
)
this.documentVersions[uri] = version + 1

return this.parseDiagnostics(response as NotificationMessage)
}

private parseDiagnostics(response: NotificationMessage): Diagnostic[] {
if (
response.method === 'textDocument/publishDiagnostics' &&
response.params
) {
const {diagnostics} = response.params as {diagnostics: Diagnostic[]}

return diagnostics || []
}

return []
}

private get currentMessageID(): number {
const result = this.messageID
this.messageID++
return result
}
}

export default async function loader(): Promise<LSPServer> {
if (server) {
return server
private async send(message: LSPMessage): Promise<LSPResponse> {
const body = JSON.stringify(message)
const fullMessage = `Content-Length: ${body.length}\r\n\r\n${body}`
const response = await this.server.process(fullMessage)

return parseResponse(response)
}

const deferred = new Deferred<LSPServer>()
private async sendPrelude(uri: string): Promise<void> {
const state = this.store.getState()
const variableAssignments = getVariableAssignments(state)
const file = buildVarsOption(variableAssignments)

if (loading) {
queue.push(deferred)
return await deferred.promise
const parts = uri.split('/')
parts.pop()
const dir = parts.join('/')
const path = `${dir}/prelude.flux`

const prelude = format_from_js_file(file)
await this.send(didOpen(this.currentMessageID, path, prelude, 0))
}
}

class LSPLoader {
private server: LSPServer
private queue: Deferred<LSPServer>[] = []
private loading: boolean = false

async load() {
if (this.server) {
return this.server
}

const deferred = new Deferred<LSPServer>()

loading = true
if (this.loading) {
this.queue.push(deferred)
return await deferred.promise
}

const {Server} = await import('@influxdata/flux-lsp-browser')
server = new LSPServer(new Server(false) as WASMServer)
registerCompletion(window.monaco, server)
this.loading = true

await server.initialize()
const {Server} = await import('@influxdata/flux-lsp-browser')
this.server = new LSPServer(new Server(false, true) as WASMServer)
registerCompletion(window.monaco, this.server)

queue.forEach(d => d.resolve(server))
await this.server.initialize()

return server
this.queue.forEach(d => d.resolve(this.server))

return this.server
}
}

const serverLoader = new LSPLoader()

export default async function loader(): Promise<LSPServer> {
return await serverLoader.load()
}
22 changes: 19 additions & 3 deletions ui/src/shared/components/FluxMonacoEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Libraries
import React, {FC, useRef, useState} from 'react'
import {ProtocolToMonacoConverter} from 'monaco-languageclient/lib/monaco-converter'

// Components
import MonacoEditor from 'react-monaco-editor'
Expand All @@ -17,7 +18,10 @@ import {OnChangeScript} from 'src/types/flux'
import {EditorType, ResourceType} from 'src/types'

import './FluxMonacoEditor.scss'
import {editor as monacoEditor} from 'monaco-editor'
import {Diagnostic} from 'monaco-languageclient/lib/services'

const p2m = new ProtocolToMonacoConverter()
interface Props {
script: string
onChangeScript: OnChangeScript
Expand All @@ -32,14 +36,24 @@ const FluxEditorMonaco: FC<Props> = ({
setEditorInstance,
}) => {
const lspServer = useRef<LSPServer>(null)
const [editorInst, seteditorInst] = useState<EditorType | null>(null)
const [docVersion, setdocVersion] = useState(2)
const [docURI, setDocURI] = useState('')

const updateDiagnostics = (diagnostics: Diagnostic[]) => {
if (editorInst) {
const results = p2m.asDiagnostics(diagnostics)
monacoEditor.setModelMarkers(editorInst.getModel(), 'default', results)
}
}

const editorDidMount = async (editor: EditorType) => {
if (setEditorInstance) {
setEditorInstance(editor)
}

seteditorInst(editor)

const uri = editor.getModel().uri.toString()

setDocURI(uri)
Expand All @@ -55,16 +69,18 @@ const FluxEditorMonaco: FC<Props> = ({

try {
lspServer.current = await loadServer()
lspServer.current.didOpen(uri, script)
const diagnostics = await lspServer.current.didOpen(uri, script)
updateDiagnostics(diagnostics)
} catch (e) {
// TODO: notify user that lsp failed
}
}

const onChange = (text: string) => {
const onChange = async (text: string) => {
onChangeScript(text)
try {
lspServer.current.didChange(docURI, text)
const diagnostics = await lspServer.current.didChange(docURI, text)
updateDiagnostics(diagnostics)
setdocVersion(docVersion + 1)
} catch (e) {
// TODO: notify user that lsp failed
Expand Down
6 changes: 5 additions & 1 deletion ui/webpack.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ module.exports = {
type: 'webassembly/experimental',
},
{
test: /^((?!flux_parser_bg|flux-lsp-browser_bg).)*.wasm$/,
test: /flux_bg.wasm$/,
type: 'webassembly/experimental',
},
{
test: /^((?!flux_parser_bg|flux-lsp-browser_bg|flux_bg).)*.wasm$/,
loader: 'file-loader',
type: 'javascript/auto',
},
Expand Down
13 changes: 9 additions & 4 deletions ui/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1023,16 +1023,21 @@
resolved "https://registry.yarnpkg.com/@influxdata/clockface/-/clockface-2.0.2.tgz#7dff9d99b984717bd540920fda15584e07a198d2"
integrity sha512-t5XmAEpjP9u5S+LeZ34k+Zm3iklpmWS5MfmSlUQ0lhfxxmpK7PHXsRYHvvXsw65h5tvj6Qdeks5ISwBxghxeWA==

"@influxdata/flux-lsp-browser@0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@influxdata/flux-lsp-browser/-/flux-lsp-browser-0.3.1.tgz#472bdd4b82b239955776133c299eea3643237041"
integrity sha512-G813h+ytOnSxjjdNhAjwz+6iL+nZn425eMsCR08fajvMNztbk3GpY5X7LLA7f8JXIsVg1SCczkmSpGkK3lWKSg==
"@influxdata/flux-lsp-browser@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@influxdata/flux-lsp-browser/-/flux-lsp-browser-0.4.0.tgz#84fde999bbd49948b2f7a86153cafaec22edfaa4"
integrity sha512-fKHBkLDbw4MLERuY5r7tSIeJUS7qafsWIQYmTrc4xDTDuW0gTiaspG8jyx/p/BtSB0GwaWCtZb2O9gkGMhfMzQ==

"@influxdata/flux-parser@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@influxdata/flux-parser/-/flux-parser-0.3.0.tgz#b63123ac814ad32c65e46a4097ba3d8b959416a5"
integrity sha512-nsm801l60kXFulcSWA2YH2YRz9oSsMlTK9Evn6Og9BoQnQMcwUsSUEug8mQRIUljnkNYV58JSs0W0mP8h7Y/ZQ==

"@influxdata/flux@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@influxdata/flux/-/flux-0.4.0.tgz#7780f1344175c3dc784fb935e7224c6758d44846"
integrity sha512-m678aFy2fUMlBFwbZ8dG1yawuLUAzOEJm6jPdZR6ezUW/Z07mLk1XwMEbCMk7vFkx1f7cbZ0Yxe/sDt0uwRcxA==

"@influxdata/giraffe@0.17.6":
version "0.17.6"
resolved "https://registry.yarnpkg.com/@influxdata/giraffe/-/giraffe-0.17.6.tgz#33e94a98dea81601f2759488605e206493a34555"
Expand Down

0 comments on commit b84f5db

Please sign in to comment.