Skip to content

Commit bc17566

Browse files
authored
implement different performance improvements for large files (#733)
* implement different performance improvements for large files * apply formatting changes --------- Co-authored-by: Logende <[email protected]>
1 parent 16e895f commit bc17566

8 files changed

Lines changed: 141 additions & 12 deletions

File tree

meta_configurator/src/components/panels/code-editor/AceEditor.vue

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Synchronized with file data from the store.
44
-->
55
<script setup lang="ts">
6-
import {onMounted, watch} from 'vue';
6+
import {computed, onMounted, type Ref, ref, watch} from 'vue';
77
import type {Editor} from 'brace';
88
import * as ace from 'brace';
99
import 'brace/mode/javascript';
@@ -21,6 +21,7 @@ import {useSettings} from '@/settings/useSettings';
2121
import {modeToDocumentTypeDescription, SessionMode} from '@/store/sessionMode';
2222
import {setupAceMode, setupAceProperties} from '@/components/panels/shared-components/aceUtils';
2323
import Message from 'primevue/message';
24+
import {sizeOf} from '@/utility/sizeOf';
2425
2526
const props = defineProps<{
2627
sessionMode: SessionMode;
@@ -30,27 +31,29 @@ const settings = useSettings();
3031
3132
// random id is used to enable multiple Ace Editors of same sessionMode on the same page
3233
const editor_id = 'code-editor-' + props.sessionMode + '-' + Math.random();
34+
let editor: Ref<Editor | undefined> = ref(undefined);
3335
3436
onMounted(() => {
35-
const editor: Editor = ace.edit(editor_id);
36-
setupAceMode(editor, settings.value);
37-
setupAceProperties(editor, settings.value);
37+
editor.value = ace.edit(editor_id);
38+
setupAceMode(editor.value, settings.value);
39+
setupAceProperties(editor.value, settings.value);
3840
39-
setupLinkToData(editor, props.sessionMode);
40-
setupLinkToCurrentSelection(editor, props.sessionMode);
41-
setupAnnotationsFromValidationErrors(editor, props.sessionMode);
41+
setupLinkToData(editor.value, props.sessionMode);
42+
setupLinkToCurrentSelection(editor.value, props.sessionMode);
43+
setupAnnotationsFromValidationErrors(editor.value, props.sessionMode);
4244
4345
if (isEditorReadOnly()) {
44-
editor.setReadOnly(true);
46+
editor.value.setReadOnly(true);
4547
}
4648
});
4749
4850
// watch for changes in the data format and update the editor accordingly
4951
watch(
5052
() => settings.value.dataFormat,
5153
_ => {
52-
const editor: Editor = ace.edit(editor_id);
53-
editor.setReadOnly(isEditorReadOnly());
54+
if (editor.value) {
55+
editor.value.setReadOnly(isEditorReadOnly());
56+
}
5457
}
5558
);
5659
@@ -63,6 +66,18 @@ function isEditorReadOnly(): boolean {
6366
(mode === SessionMode.SchemaEditor || mode === SessionMode.Settings) && dataFormat === 'xml'
6467
);
6568
}
69+
70+
const featuresDisabledForPerformance = computed(() => {
71+
if (!editor.value) {
72+
return false;
73+
}
74+
const performanceSettings = settings.value.performance;
75+
const editorContentSize = sizeOf(editor.value.getValue());
76+
return (
77+
editorContentSize > performanceSettings.maxDocumentSizeForValidation ||
78+
editorContentSize > performanceSettings.maxDocumentSizeForCursorSynchronization
79+
);
80+
});
6681
</script>
6782

6883
<template>
@@ -71,6 +86,10 @@ function isEditorReadOnly(): boolean {
7186
underlying JSON {{ modeToDocumentTypeDescription(props.sessionMode) }} document, because of
7287
ambiguity and technical restrictions in XML to JSON conversion.</Message
7388
>
89+
<Message v-if="featuresDisabledForPerformance" severity="warn"
90+
>Some editor features are disabled for performance reasons due to the large size of the
91+
document.</Message
92+
>
7493
<div class="h-full" :id="editor_id" />
7594
</template>
7695

meta_configurator/src/components/panels/code-editor/setupLinkToSelectionAndData.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
import type {SessionMode} from '@/store/sessionMode';
1111
import {getDataForMode, getSessionForMode} from '@/data/useDataLink';
1212
import {watchImmediate} from '@vueuse/core/index';
13+
import {useSettings} from '@/settings/useSettings';
14+
import {sizeOf} from '@/utility/sizeOf';
1315

1416
// variables to prevent updating functions to trigger each other
1517
let selectionChangeFromOutside = false;
@@ -42,6 +44,14 @@ function setupCursorPositionToSelectedPath(editor: Editor, mode: SessionMode) {
4244
// do not attempt to determine the path when the text does not have valid syntax
4345
return;
4446
}
47+
if (
48+
sizeOf(editor.getValue()) >
49+
useSettings().value.performance.maxDocumentSizeForCursorSynchronization
50+
) {
51+
// do not determine the path when the document is exceeding the maximum size
52+
return;
53+
}
54+
4555
try {
4656
const newPath = determinePath(editor);
4757
const session = getSessionForMode(mode);
@@ -70,6 +80,14 @@ function setupSelectedPathToCursorPosition(editor: Editor, mode: SessionMode) {
7080
return;
7181
}
7282
selectionChangeFromOutside = true;
83+
84+
if (
85+
sizeof(editor.getValue()) >
86+
useSettings().value.performance.maxDocumentSizeForCursorSynchronization
87+
) {
88+
// do not determine and update the cursor position when the document is exceeding the maximum size
89+
return;
90+
}
7391
updateCursorPositionBasedOnPath(editor, newSelectedElement);
7492
}
7593
);

meta_configurator/src/components/panels/gui-editor/configTreeNodeResolver.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ export class ConfigTreeNodeResolver {
195195
this.createObjectChildrenTreeNodes(mode, {absolutePath, relativePath, schema, depth})
196196
);
197197
}
198+
199+
if (children.length > settings.value.performance.maximumShownChildrenInGuiEditor) {
200+
children = children.slice(0, settings.value.performance.maximumShownChildrenInGuiEditor);
201+
}
202+
198203
return children;
199204
}
200205

@@ -456,7 +461,12 @@ export class ConfigTreeNodeResolver {
456461
);
457462
});
458463
}
459-
if (this.shouldAddAddItemNode(schema, data)) {
464+
let exceedsChildrenLimit = false;
465+
if (children.length > settings.value.performance.maximumShownChildrenInGuiEditor) {
466+
children = children.slice(0, settings.value.performance.maximumShownChildrenInGuiEditor);
467+
exceedsChildrenLimit = true;
468+
}
469+
if (this.shouldAddAddItemNode(schema, data) && !exceedsChildrenLimit) {
460470
return children.concat(
461471
this.createAddItemTreeNode({absolutePath, relativePath, schema, depth: depth + 1}, children)
462472
);

meta_configurator/src/data/managedValidation.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {computed} from 'vue';
22
import {getDataForMode, getSchemaForMode} from '@/data/useDataLink';
33
import {SessionMode} from '@/store/sessionMode';
4-
import {ValidationService} from '@/schema/validationService';
4+
import {ValidationResult, ValidationService} from '@/schema/validationService';
5+
import {useSettings} from '@/settings/useSettings';
6+
import {sizeOf} from '@/utility/sizeOf';
57

68
export class ManagedValidation {
79
constructor(public mode: SessionMode) {}
@@ -12,6 +14,10 @@ export class ManagedValidation {
1214
});
1315

1416
public currentValidationResult = computed(() => {
17+
const data = getDataForMode(this.mode).data.value;
18+
if (sizeOf(data) > useSettings().value.performance.maxDocumentSizeForValidation) {
19+
return new ValidationResult([]);
20+
}
1521
return this.currentValidationService.value.validate(getDataForMode(this.mode).data.value);
1622
});
1723
}

meta_configurator/src/settings/defaultSettingsData.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ export const SETTINGS_DATA_DEFAULT = {
77
toolbarTitle: 'MetaConfigurator',
88
hideSchemaEditor: false,
99
hideSettings: false,
10+
performance: {
11+
maxDocumentSizeForValidation: 1000000, // 1 MB
12+
maxDocumentSizeForCursorSynchronization: 1000000, // 1 MB
13+
maximumShownChildrenInGuiEditor: 50,
14+
},
1015
codeEditor: {
1116
fontSize: 14,
1217
tabSize: 2,

meta_configurator/src/settings/settingsSchema.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,35 @@ export const SETTINGS_SCHEMA: TopLevelSchema = {
3636
description: 'If set to true, the complete settings view will be hidden.',
3737
default: false,
3838
},
39+
performance: {
40+
type: 'object',
41+
required: ['maxDocumentSizeForValidation', 'maxDocumentSizeForCursorSynchronization'],
42+
additionalProperties: false,
43+
description: 'Performance related settings belong here.',
44+
properties: {
45+
maxDocumentSizeForValidation: {
46+
type: 'integer',
47+
description:
48+
'The maximum size of the document to validate in bytes. If the document is larger, it will not be validated.',
49+
default: 1000000, // 1 MB
50+
minimum: 1000,
51+
},
52+
maxDocumentSizeForCursorSynchronization: {
53+
type: 'integer',
54+
description:
55+
'The maximum size of the document to synchronize the cursor position in bytes. If the document is larger, the cursor position will not be synchronized.',
56+
default: 1000000, // 1 MB
57+
minimum: 1000,
58+
},
59+
maximumShownChildrenInGuiEditor: {
60+
type: 'integer',
61+
description:
62+
'The maximum amount of child nodes to be shown in the GUI editor per parent node. If the document has more children than this value, those will not be shown in the GUI editor, but still exist in the document and can be edited by other panels.',
63+
default: 50,
64+
minimum: 5,
65+
},
66+
},
67+
},
3968
codeEditor: {
4069
type: 'object',
4170
required: ['fontSize'],

meta_configurator/src/settings/settingsTypes.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface SettingsInterfaceRoot {
66
toolbarTitle: string;
77
hideSchemaEditor: boolean;
88
hideSettings: boolean;
9+
performance: SettingsInterfacePerformance;
910
codeEditor: SettingsInterfaceCodeEditor;
1011
guiEditor: SettingsInterfaceGuiEditor;
1112
schemaDiagram: SettingsInterfaceSchemaDiagram;
@@ -17,6 +18,12 @@ export interface SettingsInterfaceRoot {
1718
aiIntegration: SettingsInterfaceAiIntegraton;
1819
}
1920

21+
export interface SettingsInterfacePerformance {
22+
maxDocumentSizeForValidation: number; // in bytes
23+
maxDocumentSizeForCursorSynchronization: number; // in bytes
24+
maximumShownChildrenInGuiEditor: number;
25+
}
26+
2027
export interface SettingsInterfaceCodeEditor {
2128
fontSize: number;
2229
tabSize: number;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// returns a size estimation
2+
// source: https://stackoverflow.com/a/11900218/4743756
3+
export function sizeOf(object: any) {
4+
const objectList: any[] = [];
5+
const stack = [object];
6+
let bytes = 0;
7+
8+
while (stack.length) {
9+
const value = stack.pop();
10+
11+
switch (typeof value) {
12+
case 'boolean':
13+
bytes += 4;
14+
break;
15+
case 'string':
16+
bytes += value.length * 2;
17+
break;
18+
case 'number':
19+
bytes += 8;
20+
break;
21+
case 'object':
22+
if (!objectList.includes(value)) {
23+
objectList.push(value);
24+
for (const prop in value) {
25+
if (value.hasOwnProperty(prop)) {
26+
stack.push(value[prop]);
27+
}
28+
}
29+
}
30+
break;
31+
}
32+
}
33+
34+
return bytes;
35+
}

0 commit comments

Comments
 (0)