diff --git a/src/components/TheEditor.vue b/src/components/TheEditor.vue index e740c7db8..dcd593119 100644 --- a/src/components/TheEditor.vue +++ b/src/components/TheEditor.vue @@ -31,6 +31,16 @@ {{ mdiHelp }} {{ $t('Editor.ConfigReference') }} + + {{ mdiFormatListCheckbox }} + {{ $t('Editor.FileStructure') }} + - +
+
+ + + + +
+
+
+ +
@@ -139,9 +183,11 @@ import { mdiHelpCircle, mdiRestart, mdiUsb, + mdiFormatListCheckbox, } from '@mdi/js' import type Codemirror from '@/components/inputs/Codemirror.vue' import DevicesDialog from '@/components/dialogs/DevicesDialog.vue' +import { ConfigFileSection } from '@/store/files/types' @Component({ components: { DevicesDialog, Panel, CodemirrorAsync }, @@ -149,6 +195,9 @@ import DevicesDialog from '@/components/dialogs/DevicesDialog.vue' export default class TheEditor extends Mixins(BaseMixin) { dialogConfirmChange = false dialogDevices = false + fileStructureSidebar = true + structureActive: number[] = [] + structureOpen: number[] = [] formatFilesize = formatFilesize @@ -164,6 +213,7 @@ export default class TheEditor extends Mixins(BaseMixin) { mdiFileDocumentEditOutline = mdiFileDocumentEditOutline mdiFileDocumentOutline = mdiFileDocumentOutline mdiUsb = mdiUsb + mdiFormatListCheckbox = mdiFormatListCheckbox declare $refs: { editor: Codemirror @@ -305,6 +355,44 @@ export default class TheEditor extends Mixins(BaseMixin) { return url } + get configFileStructure() { + if (['conf', 'cfg'].includes(this.fileExtension)) { + const sourcecode = this.sourcecode + const lines = sourcecode.split(/\n/gi) + const regex = /^[^#\S]*?(\[(?
.*?)]|(?\w+)\s*?[:=])/gim + let section = null + let name = null + let structure: ConfigFileSection[] = [] + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + const matches = [...line.matchAll(regex)] + if (matches.length > 0) { + const match = matches[0] + if (match['groups']['section']) { + section = match['groups']['section'] + structure.push({ + name: section, + type: 'section', + line: i + 1, + children: [], + }) + } else if (match['groups']['name']) { + name = match['groups']['name'] + structure[structure.length - 1]['children'].push({ + name: name, + type: 'item', + line: i + 1, + }) + } + } + } + this.fileStructureSidebar = true + return structure + } + this.fileStructureSidebar = false + return null + } + cancelDownload() { this.$store.dispatch('editor/cancelLoad') } @@ -337,6 +425,29 @@ export default class TheEditor extends Mixins(BaseMixin) { }) } + showFileStructure() { + this.fileStructureSidebar = !this.fileStructureSidebar + } + + activeChanges(key: any) { + this.$refs.editor.gotoLine(key) + } + + lineChanges(line: number) { + this.configFileStructure?.map((item) => { + if (item.line == line) { + this.structureActive = [line] + } else { + item.children?.map((child) => { + if (child.line == line) { + this.structureActive = [line] + if (!this.structureOpen.includes(item.line)) this.structureOpen.push(item.line) + } + }) + } + }) + } + @Watch('changed') changedChanged(newVal: boolean) { if (!this.confirmUnsavedChanges) return @@ -398,4 +509,15 @@ export default class TheEditor extends Mixins(BaseMixin) { background-color: var(--color-primary); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E %3Cpath d='M15.88 8.29L10 14.17l-1.88-1.88a.996.996 0 1 0-1.41 1.41l2.59 2.59c.39.39 1.02.39 1.41 0L17.3 9.7a.996.996 0 0 0 0-1.41c-.39-.39-1.03-.39-1.42 0z' fill='%23fffff'/%3E %3C/svg%3E"); } + +@media screen and (min-width: 960px) { + .structure { + margin-right: 300px + } + .structure-sidebar { + width: 300px; + overflow-y: auto; + } +} + diff --git a/src/components/inputs/Codemirror.vue b/src/components/inputs/Codemirror.vue index 9c54459ec..7f6cd3e38 100644 --- a/src/components/inputs/Codemirror.vue +++ b/src/components/inputs/Codemirror.vue @@ -79,7 +79,6 @@ export default class Codemirror extends Mixins(BaseMixin) { setCmValue(content: string) { this.cminstance?.setState(EditorState.create({ doc: content, extensions: this.cmExtensions })) } - get cmExtensions() { const extensions = [ EditorView.theme({}, { dark: true }), @@ -88,6 +87,10 @@ export default class Codemirror extends Mixins(BaseMixin) { indentUnit.of(' '.repeat(this.tabSize)), keymap.of([indentWithTab]), EditorView.updateListener.of((update) => { + if(update.selectionSet) { + const line = this.cminstance?.state?.doc.lineAt(this.cminstance?.state?.selection.main.head).number + this.$emit('lineChange', line) + } this.content = update.state?.doc.toString() if (this.$emit) { this.$emit('input', this.content) @@ -110,5 +113,13 @@ export default class Codemirror extends Mixins(BaseMixin) { get tabSize() { return this.$store.state.gui.editor.tabSize || 2 } + + gotoLine(line:number){ + const l:any = this.cminstance?.state?.doc.line(line); + this.cminstance?.dispatch({ + selection: { head: l.from, anchor: l.to }, + scrollIntoView: true + }); + } } diff --git a/src/locales/en.json b/src/locales/en.json index 5c403574b..04205db9b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -203,7 +203,8 @@ "UnsavedChanges": "Unsaved Changes", "UnsavedChangesMessage": "Do you want to save your changes made to {filename}?", "UnsavedChangesSubMessage": "Your changes will be lost if you don't save them. You can disable this message in the editor settings.", - "Uploading": "Uploading" + "Uploading": "Uploading", + "FileStructure": "File Structure" }, "EmergencyStopDialog": { "AreYouSure": "Are you sure?", @@ -375,7 +376,6 @@ "EntryNextPerform": "Next perform:", "EntryPerformedAt": "Performed at {date}.", "EntrySince": "Used since:", - "EstimatedFilament": "Estimated Filament", "EstimatedFilamentWeight": "Estimated Filament Weight", "EstimatedTime": "Estimated Time", "FilamentBasedReminder": "Filament", @@ -426,7 +426,6 @@ "SelectedJobs": "Selected Jobs", "SelectedPrinttime": "Selected Print Time", "Slicer": "Slicer", - "SlicerVersion": "Slicer Version", "StartTime": "Start Time", "Statistics": "Statistics", "Status": "Status", diff --git a/src/locales/zh.json b/src/locales/zh.json index 9ff1fadbf..2722244f3 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -199,7 +199,8 @@ "UnsavedChanges": "有未保存的更改", "UnsavedChangesMessage": "是否保存对{filename}的更改?", "UnsavedChangesSubMessage": "如果不保存,您的更改将丢失。您可以在编辑器设置中关闭此提示。", - "Uploading": "正在上传" + "Uploading": "正在上传", + "FileStructure": "结构" }, "EmergencyStopDialog": { "AreYouSure": "确定要执行此操作吗?", diff --git a/src/store/files/types.ts b/src/store/files/types.ts index 55197c159..468025733 100644 --- a/src/store/files/types.ts +++ b/src/store/files/types.ts @@ -97,3 +97,13 @@ export interface ApiGetDirectoryReturnFile { filename: string permissions: string } + +export interface ConfigFileKey{ + name: string, + type: string, + line: number +} + +export interface ConfigFileSection extends ConfigFileKey{ + children: ConfigFileKey[] +}