瀏覽代碼

feat: add autocomplete in javascript block

Ahmad Kholid 3 年之前
父節點
當前提交
a708c2414a

+ 8 - 0
src/components/newtab/shared/SharedCodemirror.vue

@@ -31,6 +31,10 @@ const props = defineProps({
     type: Boolean,
     default: true,
   },
+  extensions: {
+    type: [Object, Array],
+    default: () => [],
+  },
 });
 const emit = defineEmits(['change', 'update:modelValue']);
 
@@ -48,6 +52,9 @@ const updateListener = EditorView.updateListener.of((event) => {
   }
 });
 
+const customExtension = Array.isArray(props.extensions)
+  ? props.extensions
+  : [props.extensions];
 const state = EditorState.create({
   doc: props.modelValue,
   extensions: [
@@ -58,6 +65,7 @@ const state = EditorState.create({
     keymap.of([indentWithTab]),
     EditorState.readOnly.of(props.readonly),
     props.lang === 'javascript' ? javascript() : json(),
+    ...customExtension,
   ],
 });
 

+ 78 - 0
src/components/newtab/workflow/edit/EditJavascriptCode.vue

@@ -40,6 +40,7 @@
         <ui-tab-panel value="code" class="h-full">
           <shared-codemirror
             v-model="state.code"
+            :extensions="codemirrorExts"
             class="overflow-auto"
             style="height: 87%"
           />
@@ -92,8 +93,12 @@
 <script setup>
 import { watch, reactive } from 'vue';
 import { useI18n } from 'vue-i18n';
+import { syntaxTree } from '@codemirror/language';
+import { autocompletion, snippet } from '@codemirror/autocomplete';
+import * as anu from '@codemirror/autocomplete';
 import SharedCodemirror from '@/components/newtab/shared/SharedCodemirror.vue';
 
+console.log(anu);
 const props = defineProps({
   data: {
     type: Object,
@@ -123,6 +128,79 @@ function updateData(value) {
 function addScript() {
   state.preloadScripts.push({ src: '', removeAfterExec: true });
 }
+const dontCompleteIn = [
+  'String',
+  'TemplateString',
+  'LineComment',
+  'BlockComment',
+  'VariableDefinition',
+  'PropertyDefinition',
+];
+/* eslint-disable no-template-curly-in-string */
+function automaFuncsCompletion(context) {
+  const word = context.matchBefore(/\w*/);
+  const nodeBefore = syntaxTree(context.state).resolveInner(context.pos, -1);
+
+  if (
+    (word.from === word.to && !context.explicit) ||
+    dontCompleteIn.includes(nodeBefore.name)
+  )
+    return null;
+
+  return {
+    from: word.from,
+    options: [
+      {
+        label: 'automaNextBlock',
+        type: 'function',
+        apply: snippet('automaNextBlock(${data})'),
+        info: () => {
+          const container = document.createElement('div');
+
+          container.innerHTML = `
+            <code>automaNextBlock(<i>data</i>)</code>
+            <p class="mt-2">
+              Execute the next block
+              <a href="https://github.com/Kholid060/automa/wiki/Blocks#automanextblockdata" target="_blank" class="underline">
+                Read more
+              </a>
+            </p>
+          `;
+
+          return container;
+        },
+      },
+      {
+        label: 'automaRefData',
+        type: 'function',
+        apply: snippet("automaRefData('${keyword}', '${path}')"),
+        info: () => {
+          const container = document.createElement('div');
+
+          container.innerHTML = `
+            <code>automaRefData(<i>keyword</i>, <i>path</i>)</code>
+            <p class="mt-2">
+              Use this function to
+              <a href="https://github.com/Kholid060/automa/wiki/Features#reference-data" target="_blank" class="underline">
+                reference data
+              </a>
+            </p>
+          `;
+
+          return container;
+        },
+      },
+      {
+        label: 'automaResetTimeout',
+        type: 'function',
+        info: 'Reset javascript execution timeout',
+        apply: 'automaResetTimeout()',
+      },
+    ],
+  };
+}
+
+const codemirrorExts = [autocompletion({ override: [automaFuncsCompletion] })];
 
 watch(
   () => state.code,

+ 1 - 1
src/utils/helper.js

@@ -96,7 +96,7 @@ export function toCamelCase(str) {
 }
 
 export function isObject(obj) {
-  return typeof obj === 'object' && obj !== null;
+  return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
 }
 
 export function objectHasKey(obj, key) {

+ 7 - 6
src/utils/reference-data/index.js

@@ -4,19 +4,20 @@ import { objectHasKey } from '@/utils/helper';
 import mustacheReplacer from './mustache-replacer';
 
 export const funcs = {
-  date: (...params) => {
+  date(...args) {
     let date = new Date();
     let dateFormat = 'DD-MM-YYYY';
 
     const getDateFormat = (value) =>
       value ? value?.replace(/['"]/g, '') : dateFormat;
 
-    if (params.length === 1) {
-      dateFormat = getDateFormat(params[0][0]);
-    } else if (params.length >= 2) {
-      date = new Date(params[0]);
-      dateFormat = getDateFormat(params[1][0]);
+    if (args.length === 1) {
+      dateFormat = getDateFormat(args[0]);
+    } else if (args.length >= 2) {
+      date = new Date(args[0]);
+      dateFormat = getDateFormat(args[1]);
     }
+    console.log(this, 'anu');
 
     /* eslint-disable-next-line */
     const isValidDate = date instanceof Date && !isNaN(date);

+ 1 - 1
src/utils/reference-data/mustache-replacer.js

@@ -22,7 +22,7 @@ export default function (str, data) {
     const funcRef = extractStrFunction(key);
 
     if (funcRef && data.funcs[funcRef.name]) {
-      return data.funcs[funcRef.name]?.(funcRef.params);
+      return data.funcs[funcRef.name]?.apply({ refData: data }, funcRef.params);
     }
 
     const { dataKey, path } = keyParser(key);