Use server-provided completion suggestions

Co-authored-by: Chris Henn <chris.henn@influxdata.com>
Co-authored-by: Andrew Watkins <andrew.watkinz@gmail.com>
pull/3583/head
Christopher Henn 2018-06-05 14:23:20 -07:00
parent 1bcd89151a
commit 7187f64d4c
No known key found for this signature in database
GPG Key ID: 909E48D5E1C526FA
4 changed files with 110 additions and 10 deletions

View File

@ -109,6 +109,7 @@ class TimeMachine extends PureComponent<Props> {
status={status}
script={script}
visibility={visibility}
suggestions={suggestions}
onChangeScript={onChangeScript}
onSubmitScript={onSubmitScript}
/>

View File

@ -1,9 +1,10 @@
import React, {PureComponent} from 'react'
import {Controlled as CodeMirror, IInstance} from 'react-codemirror2'
import {Controlled as ReactCodeMirror, IInstance} from 'react-codemirror2'
import {EditorChange} from 'codemirror'
import 'src/external/codemirror'
import {ShowHintOptions} from 'src/types/codemirror'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {OnChangeScript, OnSubmitScript} from 'src/types/flux'
import {OnChangeScript, OnSubmitScript, Suggestion} from 'src/types/flux'
import {getFluxCompletions} from 'src/flux/helpers/autoComplete'
interface Gutter {
line: number
@ -21,10 +22,11 @@ interface Props {
status: Status
onChangeScript: OnChangeScript
onSubmitScript: OnSubmitScript
suggestions: Suggestion[]
}
interface EditorInstance extends IInstance {
showHint: (options?: any) => void
showHint: (options?: ShowHintOptions) => void
}
@ErrorHandling
@ -36,19 +38,21 @@ class TimeMachineEditor extends PureComponent<Props> {
}
public componentDidUpdate(prevProps) {
if (this.props.status.type === 'error') {
const {status, visibility} = this.props
if (status.type === 'error') {
this.makeError()
}
if (this.props.status.type !== 'error') {
if (status.type !== 'error') {
this.editor.clearGutter('error-gutter')
}
if (prevProps.visibility === this.props.visibility) {
if (prevProps.visibility === visibility) {
return
}
if (this.props.visibility === 'visible') {
if (visibility === 'visible') {
setTimeout(() => this.editor.refresh(), 60)
}
}
@ -61,7 +65,6 @@ class TimeMachineEditor extends PureComponent<Props> {
theme: 'time-machine',
tabIndex: 1,
readonly: false,
extraKeys: {'Ctrl-Space': 'autocomplete'},
completeSingle: false,
autoRefresh: true,
mode: 'flux',
@ -70,7 +73,7 @@ class TimeMachineEditor extends PureComponent<Props> {
return (
<div className="time-machine-editor">
<CodeMirror
<ReactCodeMirror
autoFocus={true}
autoCursor={true}
value={script}
@ -79,6 +82,7 @@ class TimeMachineEditor extends PureComponent<Props> {
onTouchStart={this.onTouchStart}
editorDidMount={this.handleMount}
onBlur={this.handleBlur}
onKeyUp={this.handleKeyUp}
/>
</div>
)
@ -128,6 +132,17 @@ class TimeMachineEditor extends PureComponent<Props> {
private onTouchStart = () => {}
private handleKeyUp = (editor: EditorInstance, e: KeyboardEvent) => {
const {suggestions} = this.props
const space = ' '
if (e.ctrlKey && e.key === space) {
editor.showHint({
hint: () => getFluxCompletions(this.editor, suggestions),
})
}
}
private updateCode = (
_: IInstance,
__: EditorChange,

View File

@ -0,0 +1,49 @@
import CodeMirror from 'codemirror'
import {IInstance} from 'react-codemirror2'
import {Suggestion} from 'src/types/flux'
import {Hints} from 'src/types/codemirror'
export const getFluxCompletions = (
editor: IInstance,
list: Suggestion[]
): Hints => {
const cursor = editor.getCursor()
const currentLine = editor.getLine(cursor.line)
const trailingWhitespace = /[\w$]+/
let start = cursor.ch
let end = start
// Move end marker until a space or end of line is reached
while (
end < currentLine.length &&
trailingWhitespace.test(currentLine.charAt(end))
) {
end += 1
}
// Move start marker until a space or the beginning of line is reached
while (start && trailingWhitespace.test(currentLine.charAt(start - 1))) {
start -= 1
}
// If not completing inside a current word, return list of all possible suggestions
if (start === end) {
return {
from: CodeMirror.Pos(cursor.line, start),
to: CodeMirror.Pos(cursor.line, end),
list: list.map(s => s.name),
}
}
const currentWord = currentLine.slice(start, end)
const listFilter = new RegExp(`^${currentWord}`, 'i')
// Otherwise return suggestions that contain the current word as a substring
return {
from: CodeMirror.Pos(cursor.line, start),
to: CodeMirror.Pos(cursor.line, end),
list: list.filter(s => s.name.match(listFilter)).map(s => s.name),
}
}

View File

@ -0,0 +1,35 @@
import {Editor, Position} from 'codemirror'
export interface Hints {
from: Position
to: Position
list: Array<Hint | string>
}
// Interface used by showHint.js Codemirror add-on
// When completions aren't simple strings, they should be objects with the following properties:
export interface Hint {
text: string
className?: string
displayText?: string
from?: Position
/** Called if a completion is picked. If provided *you* are responsible for applying the completion */
hint?: (cm: Editor, data: Hints, cur: Hint) => void
render?: (element: HTMLLIElement, data: Hints, cur: Hint) => void
to?: Position
}
type HintFunction = (cm: Editor) => Hints
interface AsyncHintFunction {
(cm: Editor, callback: (hints: Hints) => any): any
async?: boolean
}
export interface ShowHintOptions {
completeSingle?: boolean
hint: HintFunction | AsyncHintFunction
}
export type ShowHint = (cm: Editor, options: ShowHintOptions) => void