feat: implement base autocomplete implementation for codemirror along with preRequest autocompletion

This commit is contained in:
Andrew Bastin
2021-09-07 22:12:38 +05:30
committed by liyasthomas
parent dc5f52cc0d
commit b9fc0175e7
4 changed files with 105 additions and 0 deletions

View File

@@ -8,6 +8,7 @@ import "codemirror/mode/javascript/javascript"
import { ref, watch } from "@nuxtjs/composition-api"
import { useCodemirror } from "~/helpers/editor/codemirror"
import { LinterDefinition } from "~/helpers/editor/linting/linter"
import { Completer } from "~/helpers/editor/completion"
const props = withDefaults(
defineProps<{
@@ -16,11 +17,13 @@ const props = withDefaults(
placeholder?: string
wrap?: boolean
linter: LinterDefinition | null
completer: Completer | null
}>(),
{
placeholder: "",
wrap: true,
linter: null as any,
completer: null as any,
}
)
@@ -45,5 +48,6 @@ useCodemirror(editor, value, {
lineWrapping: props.wrap,
},
linter: props.linter,
completer: props.completer,
})
</script>

View File

@@ -7,6 +7,7 @@ import "codemirror/theme/3024-night.css"
import "codemirror/lib/codemirror.css"
import "codemirror/addon/lint/lint.css"
import "codemirror/addon/dialog/dialog.css"
import "codemirror/addon/hint/show-hint.css"
import "codemirror/addon/fold/foldgutter.css"
import "codemirror/addon/fold/foldgutter"
@@ -15,6 +16,7 @@ import "codemirror/addon/fold/comment-fold"
import "codemirror/addon/fold/indent-fold"
import "codemirror/addon/display/autorefresh"
import "codemirror/addon/lint/lint"
import "codemirror/addon/hint/show-hint"
import "codemirror/addon/display/placeholder"
import "codemirror/addon/edit/closebrackets"
import "codemirror/addon/search/search"
@@ -24,10 +26,12 @@ import "codemirror/addon/dialog/dialog"
import { watch, onMounted, ref, Ref, useContext } from "@nuxtjs/composition-api"
import { LinterDefinition } from "./linting/linter"
import { Completer } from "./completion"
type CodeMirrorOptions = {
extendedEditorConfig: Omit<CodeMirror.EditorConfiguration, "value">
linter: LinterDefinition | null
completer: Completer | null
}
const DEFAULT_EDITOR_CONFIG: CodeMirror.EditorConfiguration = {
@@ -76,6 +80,31 @@ export function useCodemirror(
}
}
const updateCompleterConfig = () => {
if (options.completer) {
cm.value?.setOption("hintOptions", {
completeSingle: false,
hint: async (editor: CodeMirror.Editor) => {
const pos = editor.getCursor()
const text = editor.getValue()
const result = await options.completer!(text, pos)
console.log("complete!")
console.log(result)
return <CodeMirror.Hints>{
from: result.start,
to: result.end,
list: result.completions
.sort((a, b) => a.score - b.score)
.map((x) => x.text),
}
},
})
}
}
// Boot-up CodeMirror, set the value and listeners
onMounted(() => {
cm.value = CodeMirror(el.value!, DEFAULT_EDITOR_CONFIG)
@@ -83,6 +112,7 @@ export function useCodemirror(
setTheme()
updateEditorConfig()
updateLinterConfig()
updateCompleterConfig()
cm.value.on("change", (instance) => {
// External update propagation (via watchers) should be ignored
@@ -90,6 +120,13 @@ export function useCodemirror(
value.value = instance.getValue()
}
})
/* TODO: Show autocomplete on typing (this is just for testing) */
cm.value.on("keyup", (instance, event) => {
if (!instance.state.completionActive && event.key !== "Enter") {
instance.showHint()
}
})
})
const setTheme = () => {
@@ -120,6 +157,7 @@ export function useCodemirror(
deep: true,
})
watch(() => options.linter, updateLinterConfig, { immediate: true })
watch(() => options.completer, updateCompleterConfig, { immediate: true })
// Watch value updates
watch(value, (newVal) => {

View File

@@ -0,0 +1,33 @@
export type CompletionEntry = {
text: string
meta: string
score: number
}
export type CompleterResult = {
/**
* List of completions to display
*/
completions: CompletionEntry[]
/**
* Start of the completion position
* (on completion the start..end region is replaced)
*/
start: { line: number; ch: number }
/**
* End of the completion position
* (on completion the start..end region is replaced)
*/
end: { line: number; ch: number }
}
export type Completer = (
/**
* The contents of the editor
*/
text: string,
/**
* Position where the completer is fired
*/
completePos: { line: number; ch: number }
) => Promise<CompleterResult>

View File

@@ -0,0 +1,30 @@
import { convertIndexToLineCh } from "../utils"
import { Completer, CompletionEntry } from "."
import { getPreRequestScriptCompletions } from "~/helpers/tern"
const completer: Completer = async (text, completePos) => {
const results = await getPreRequestScriptCompletions(
text,
completePos.line,
completePos.ch
)
const start = convertIndexToLineCh(text, results.start)
const end = convertIndexToLineCh(text, results.end)
const completions = results.completions.map((completion: any, i: number) => {
return <CompletionEntry>{
text: completion.name,
meta: completion.isKeyword ? "keyword" : completion.type,
score: results.completions.length - i,
}
})
return {
start,
end,
completions,
}
}
export default completer