123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- <template>
- <div class="show-if-initialized" :class="{ initialized }">
- <pre ref="editor" :class="styles"></pre>
- </div>
- </template>
- <script>
- import ace from "ace-builds"
- import "ace-builds/webpack-resolver"
- import "ace-builds/src-noconflict/ext-language_tools"
- import "ace-builds/src-noconflict/mode-graphqlschema"
- import * as esprima from "esprima"
- import { defineComponent } from "@nuxtjs/composition-api"
- import debounce from "~/helpers/utils/debounce"
- import {
- getPreRequestScriptCompletions,
- getTestScriptCompletions,
- performPreRequestLinting,
- performTestLinting,
- } from "~/helpers/tern"
- export default defineComponent({
- props: {
- value: {
- type: String,
- default: "",
- },
- theme: {
- type: String,
- required: false,
- default: null,
- },
- options: {
- type: Object,
- default: () => {},
- },
- styles: {
- type: String,
- default: "",
- },
- completeMode: {
- type: String,
- required: true,
- default: "none",
- },
- },
- data() {
- return {
- initialized: false,
- editor: null,
- cacheValue: "",
- }
- },
- computed: {
- appFontSize() {
- return getComputedStyle(document.documentElement).getPropertyValue(
- "--body-font-size"
- )
- },
- },
- watch: {
- value(value) {
- if (value !== this.cacheValue) {
- this.editor.session.setValue(value, 1)
- this.cacheValue = value
- if (this.lint) this.provideLinting(value)
- }
- },
- theme() {
- this.initialized = false
- this.editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
- this.$nextTick()
- .then(() => {
- this.initialized = true
- })
- .catch(() => {
- // nextTick shouldn't really ever throw but still
- this.initialized = true
- })
- })
- },
- options(value) {
- this.editor.setOptions(value)
- },
- },
- mounted() {
- // const langTools = ace.require("ace/ext/language_tools")
- const editor = ace.edit(this.$refs.editor, {
- mode: `ace/mode/javascript`,
- enableBasicAutocompletion: true,
- enableLiveAutocompletion: true,
- ...this.options,
- })
- // Set the theme and show the editor only after it's been set to prevent FOUC.
- editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
- this.$nextTick()
- .then(() => {
- this.initialized = true
- })
- .catch(() => {
- // nextTIck shouldn't really ever throw but still
- this.initialized = true
- })
- })
- editor.setFontSize(this.appFontSize)
- const completer = {
- getCompletions: (
- editor,
- _session,
- { row, column },
- _prefix,
- callback
- ) => {
- if (this.completeMode === "pre") {
- getPreRequestScriptCompletions(editor.getValue(), row, column)
- .then((res) => {
- callback(
- null,
- res.completions.map((r, index, arr) => ({
- name: r.name,
- value: r.name,
- score: (arr.length - index) / arr.length,
- meta: r.type,
- }))
- )
- })
- .catch(() => callback(null, []))
- } else if (this.completeMode === "test") {
- getTestScriptCompletions(editor.getValue(), row, column)
- .then((res) => {
- callback(
- null,
- res.completions.map((r, index, arr) => ({
- name: r.name,
- value: r.name,
- score: (arr.length - index) / arr.length,
- meta: r.type,
- }))
- )
- })
- .catch(() => callback(null, []))
- }
- },
- }
- editor.completers = [completer]
- if (this.value) editor.setValue(this.value, 1)
- this.editor = editor
- this.cacheValue = this.value
- editor.on("change", () => {
- const content = editor.getValue()
- this.$emit("input", content)
- this.cacheValue = content
- this.provideLinting(content)
- })
- this.provideLinting(this.value)
- },
- destroyed() {
- this.editor.destroy()
- },
- methods: {
- defineTheme() {
- if (this.theme) {
- return this.theme
- }
- const strip = (str) =>
- str.replace(/#/g, "").replace(/ /g, "").replace(/"/g, "")
- return strip(
- window
- .getComputedStyle(document.documentElement)
- .getPropertyValue("--editor-theme")
- )
- },
- provideLinting: debounce(function (code) {
- let results = []
- const lintFunc =
- this.completeMode === "pre"
- ? performPreRequestLinting
- : performTestLinting
- lintFunc(code)
- .then((semanticLints) => {
- results = results.concat(
- semanticLints.map((lint) => ({
- row: lint.from.line,
- column: lint.from.ch,
- text: `[semantic] ${lint.message}`,
- type: "error",
- }))
- )
- try {
- const res = esprima.parseScript(code, { tolerant: true })
- if (res.errors && res.errors.length > 0) {
- results = results.concat(
- res.errors.map((err) => {
- const pos = this.editor.session
- .getDocument()
- .indexToPosition(err.index, 0)
- return {
- row: pos.row,
- column: pos.column,
- text: `[syntax] ${err.description}`,
- type: "error",
- }
- })
- )
- }
- } catch (e) {
- const pos = this.editor.session
- .getDocument()
- .indexToPosition(e.index, 0)
- results = results.concat([
- {
- row: pos.row,
- column: pos.column,
- text: `[syntax] ${e.description}`,
- type: "error",
- },
- ])
- }
- this.editor.session.setAnnotations(results)
- })
- .catch(() => {
- try {
- const res = esprima.parseScript(code, { tolerant: true })
- if (res.errors && res.errors.length > 0) {
- results = results.concat(
- res.errors.map((err) => {
- const pos = this.editor.session
- .getDocument()
- .indexToPosition(err.index, 0)
- return {
- row: pos.row,
- column: pos.column,
- text: `[syntax] ${err.description}`,
- type: "error",
- }
- })
- )
- }
- } catch (e) {
- const pos = this.editor.session
- .getDocument()
- .indexToPosition(e.index, 0)
- results = results.concat([
- {
- row: pos.row,
- column: pos.column,
- text: `[syntax] ${e.description}`,
- type: "error",
- },
- ])
- }
- this.editor.session.setAnnotations(results)
- })
- }, 2000),
- },
- })
- </script>
- <style scoped lang="scss">
- .show-if-initialized {
- &.initialized {
- @apply opacity-100;
- }
- & > * {
- @apply transition-none;
- }
- }
- </style>
|