mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
⚡ Improve autocomplete and a few other changes
This commit is contained in:
@@ -17,14 +17,29 @@
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
|
||||
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||
|
||||
import mixins from 'vue-typed-mixins';
|
||||
import { IExecutionResponse } from '@/Interface';
|
||||
import { INodeExecutionData } from 'n8n-workflow';
|
||||
import { IExecutionResponse, INodeUi } from '@/Interface';
|
||||
import {
|
||||
IBinaryKeyData,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
IRunExecutionData,
|
||||
IWorkflowDataProxyAdditionalKeys,
|
||||
WorkflowDataProxy,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export default mixins(genericHelpers).extend({
|
||||
import {
|
||||
PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
|
||||
} from '@/constants';
|
||||
|
||||
export default mixins(
|
||||
genericHelpers,
|
||||
workflowHelpers,
|
||||
).extend({
|
||||
name: 'CodeEdit',
|
||||
props: ['dialogVisible', 'parameter', 'value'],
|
||||
props: ['codeAutocomplete', 'dialogVisible', 'parameter', 'type', 'value'],
|
||||
data() {
|
||||
return {
|
||||
monacoInstance: null as monaco.editor.IStandaloneCodeEditor | null,
|
||||
@@ -35,7 +50,9 @@ export default mixins(genericHelpers).extend({
|
||||
setTimeout(this.loadEditor);
|
||||
},
|
||||
destroyed() {
|
||||
this.monacoLibrary!.dispose();
|
||||
if (this.monacoLibrary) {
|
||||
this.monacoLibrary.dispose();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeDialog() {
|
||||
@@ -45,19 +62,50 @@ export default mixins(genericHelpers).extend({
|
||||
return false;
|
||||
},
|
||||
|
||||
createSimpleRepresentation(inputData: object | null | undefined | boolean | string | number | boolean[] | string[] | number[] | object[]): object | null | undefined | boolean | string | number | boolean[] | string[] | number[] | object[] {
|
||||
if (inputData === null || inputData === undefined) {
|
||||
return inputData;
|
||||
} else if (typeof inputData === 'string') {
|
||||
return '';
|
||||
} else if (typeof inputData === 'boolean') {
|
||||
return true;
|
||||
} else if (typeof inputData === 'number') {
|
||||
return 1;
|
||||
} else if (Array.isArray(inputData)) {
|
||||
return inputData.map(value => this.createSimpleRepresentation(value));
|
||||
} else if (typeof inputData === 'object') {
|
||||
const returnData: { [key: string]: object } = {};
|
||||
Object.keys(inputData).forEach(key => {
|
||||
// @ts-ignore
|
||||
returnData[key] = this.createSimpleRepresentation(inputData[key]);
|
||||
});
|
||||
return returnData;
|
||||
}
|
||||
return inputData;
|
||||
},
|
||||
|
||||
loadEditor() {
|
||||
if (!this.$refs.code) return;
|
||||
|
||||
this.monacoInstance = monaco.editor.create(this.$refs.code as HTMLElement, {
|
||||
value: this.value,
|
||||
language: 'javascript',
|
||||
language: this.type === 'code' ? 'javascript' : 'json',
|
||||
tabSize: 2,
|
||||
wordBasedSuggestions: false,
|
||||
readOnly: this.isReadOnly,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
this.monacoInstance.onDidChangeModelContent(() => {
|
||||
const model = this.monacoInstance!.getModel();
|
||||
if (model) {
|
||||
this.$emit('valueChanged', model.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
monaco.editor.defineTheme('n8nCustomTheme', {
|
||||
base: 'vs',
|
||||
inherit: true,
|
||||
@@ -68,45 +116,112 @@ export default mixins(genericHelpers).extend({
|
||||
});
|
||||
monaco.editor.setTheme('n8nCustomTheme');
|
||||
|
||||
this.monacoInstance.onDidChangeModelContent(() => {
|
||||
const model = this.monacoInstance!.getModel();
|
||||
if (this.type === 'code') {
|
||||
// As wordBasedSuggestions: false does not have any effect does it however seem
|
||||
// to remove all all suggestions from the editor if I do this
|
||||
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
|
||||
allowNonTsExtensions: true,
|
||||
});
|
||||
|
||||
if (model) {
|
||||
this.$emit('valueChanged', model.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
this.loadAutocompleteData();
|
||||
this.loadAutocompleteData();
|
||||
} else if (this.type === 'json') {
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loadAutocompleteData(): void {
|
||||
const executedWorkflow: IExecutionResponse | null = this.$store.getters.getWorkflowExecution;
|
||||
if (['function', 'functionItem'].includes(this.codeAutocomplete)) {
|
||||
const executedWorkflow: IExecutionResponse | null = this.$store.getters.getWorkflowExecution;
|
||||
const workflow = this.getWorkflow();
|
||||
const activeNode: INodeUi | null = this.$store.getters.activeNode;
|
||||
const inputIndex = 0;
|
||||
const itemIndex = 0;
|
||||
const inputName = 'main';
|
||||
const mode = 'manual';
|
||||
const runIndex = 0;
|
||||
|
||||
let autocompleteData: INodeExecutionData[] = [];
|
||||
const autocompleteData: string[] = [];
|
||||
|
||||
if (executedWorkflow) {
|
||||
const lastNodeExecuted = executedWorkflow.data.resultData.lastNodeExecuted;
|
||||
const executionData = this.$store.getters.getWorkflowExecution as IExecutionResponse | null;
|
||||
|
||||
if (lastNodeExecuted) {
|
||||
const data = executedWorkflow.data.resultData.runData[lastNodeExecuted];
|
||||
|
||||
// @ts-ignore
|
||||
autocompleteData = data[0].data!.main[0];
|
||||
let runExecutionData: IRunExecutionData;
|
||||
if (executionData === null) {
|
||||
runExecutionData = {
|
||||
resultData: {
|
||||
runData: {},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
runExecutionData = executionData.data;
|
||||
}
|
||||
const parentNode = workflow.getParentNodes(activeNode!.name, inputName, 1);
|
||||
|
||||
const connectionInputData = this.connectionInputData(parentNode, inputName, runIndex, inputIndex);
|
||||
|
||||
const additionalProxyKeys: IWorkflowDataProxyAdditionalKeys = {
|
||||
$executionId: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
|
||||
$resumeWebhookUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
|
||||
};
|
||||
|
||||
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, activeNode!.name, connectionInputData || [], {}, mode, additionalProxyKeys);
|
||||
const proxy = dataProxy.getDataProxy();
|
||||
|
||||
const autoCompleteItems = [
|
||||
`function $evaluateExpression(expression: string, itemIndex?: number): any {};`,
|
||||
`function getNodeParameter(parameterName: string, itemIndex: number, fallbackValue?: any): any {};`,
|
||||
`function getWorkflowStaticData(type: string): object {};`,
|
||||
`function $item(itemIndex: number, runIndex?: number) {};`,
|
||||
`function $items(nodeName?: string, outputIndex?: number, runIndex?: number) {};`,
|
||||
];
|
||||
|
||||
const baseKeys = ['$env', '$executionId', '$mode', '$parameter', '$position', '$resumeWebhookUrl', '$workflow'];
|
||||
const additionalKeys = ['$json', '$binary'];
|
||||
if (executedWorkflow) {
|
||||
baseKeys.push(...additionalKeys);
|
||||
} else {
|
||||
additionalKeys.forEach(key => {
|
||||
autoCompleteItems.push(`const ${key} = {}`);
|
||||
});
|
||||
}
|
||||
|
||||
for (const key of baseKeys) {
|
||||
autoCompleteItems.push(`const ${key} = ${JSON.stringify(this.createSimpleRepresentation(proxy[key]))}`);
|
||||
}
|
||||
|
||||
// Add the nodes and their simplified data
|
||||
const nodes: {
|
||||
[key: string]: INodeExecutionData;
|
||||
} = {};
|
||||
for (const [nodeName, node] of Object.entries(workflow.nodes)) {
|
||||
// To not load to much data create a simple representation.
|
||||
nodes[nodeName] = {
|
||||
json: {} as IDataObject,
|
||||
parameter: this.createSimpleRepresentation(proxy.$node[nodeName].parameter) as IDataObject,
|
||||
};
|
||||
|
||||
try {
|
||||
nodes[nodeName]!.json = this.createSimpleRepresentation(proxy.$node[nodeName].json) as IDataObject;
|
||||
nodes[nodeName]!.context = this.createSimpleRepresentation(proxy.$node[nodeName].context) as IDataObject;
|
||||
nodes[nodeName]!.runIndex = proxy.$node[nodeName].runIndex;
|
||||
if (Object.keys(proxy.$node[nodeName].binary).length) {
|
||||
nodes[nodeName]!.binary = this.createSimpleRepresentation(proxy.$node[nodeName].binary) as IBinaryKeyData;
|
||||
}
|
||||
} catch(error) {}
|
||||
}
|
||||
autoCompleteItems.push(`const $node = ${JSON.stringify(nodes)}`);
|
||||
|
||||
if (this.codeAutocomplete === 'function') {
|
||||
autoCompleteItems.push(`const items = ${JSON.stringify(this.createSimpleRepresentation(connectionInputData))}`);
|
||||
} else if (this.codeAutocomplete === 'functionItem') {
|
||||
autoCompleteItems.push(`const item = $json`);
|
||||
}
|
||||
|
||||
this.monacoLibrary = monaco.languages.typescript.javascriptDefaults.addExtraLib(
|
||||
autoCompleteItems.join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
const autoCompleteItems = [
|
||||
`/**\n\`\`\`\nconst items = ${JSON.stringify(autocompleteData, null, 2)}\n\`\`\`\n*/`,
|
||||
`const items = ${JSON.stringify(autocompleteData)}`,
|
||||
];
|
||||
|
||||
if (autocompleteData.length) {
|
||||
autoCompleteItems.push(`const $json = ${JSON.stringify(autocompleteData[0].json)}`);
|
||||
}
|
||||
|
||||
this.monacoLibrary = monaco.languages.typescript.javascriptDefaults.addExtraLib(
|
||||
autoCompleteItems.join('\n'),
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user