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
parent
1bcd89151a
commit
7187f64d4c
|
@ -109,6 +109,7 @@ class TimeMachine extends PureComponent<Props> {
|
|||
status={status}
|
||||
script={script}
|
||||
visibility={visibility}
|
||||
suggestions={suggestions}
|
||||
onChangeScript={onChangeScript}
|
||||
onSubmitScript={onSubmitScript}
|
||||
/>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue