@setoelkahfi / svara / commits / 1aba36b

Tab system rewrite and restructure

mellobacon committed Oct 31, 2022 at 13:40 UTC 1aba36bd1993dabb53251dc208ae95d448b17898
13 files changed +193 -138
src/components/Content/Editor/Editor.svelte
+2 -1
@@ -1,6 +1,7 @@
1 <script lang="ts">
2 import EditorTabs from "./EditorTabs.svelte";
3 - import { hidden } from "./scripts/Tabs";
3 + //import { hidden } from "./scripts/Tabs";
4 + import { hidden } from "./scripts/Tab";
5 </script>
6
7 <EditorTabs />
src/components/Content/Editor/EditorTabs.svelte
+1 -2
@@ -2,8 +2,7 @@
2 import { Sortable } from "sortablejs";
3 import { onMount } from "svelte";
4 import Tab from "./Tab.svelte";
5 - import { tabs, hidden } from "./scripts/Tabs";
6 -
5 + import { hidden, tabs } from "./scripts/Tab";
6 let tabcontainer;
7 onMount(() => {
8 Sortable.create(tabcontainer, {
src/components/Content/Editor/Tab.svelte
+2 -1
@@ -1,6 +1,7 @@
1 <script lang="ts">
2 + import { closeTab, setActive } from "./scripts/Tab";
3 import TabMenu from "./TabMenu.svelte";
3 - import { setActive, closeTab } from "./scripts/Tabs";
4 + //import { setActive, closeTab } from "./scripts/Tabs";
5 export let id = 0;
6 export let label = "Untitled-1";
7 export let path = "";
src/components/Content/Editor/TabMenu.svelte
+3 -3
@@ -4,11 +4,11 @@
4 import CopyFile from "carbon-icons-svelte/lib/CopyFile.svelte";
5 import Cut from "carbon-icons-svelte/lib/Cut.svelte";
6 import { clipboard, path, invoke } from "@tauri-apps/api";
7 - import { closeTab, setActive } from "./scripts/Tabs";
7 + import { closeTab, setActive } from "./scripts/Tab";
8 export let id: number;
9 export let target;
10 - export let filename;
11 - export let filepath;
10 + export let filename: string;
11 + export let filepath: string;
12
13 async function copyToClipboard(input) {
14 await clipboard.writeText(input);
src/components/Content/Editor/scripts/Tab.ts renamed
+92 -100
@@ -1,42 +1,52 @@
1 -import { dialog } from '@tauri-apps/api';
2 -import { writeFile, writeTextFile } from '@tauri-apps/api/fs';
3 -import { sep, videoDir } from '@tauri-apps/api/path';
1 import { writable } from 'svelte/store';
5 -import { createFile, loadFile } from '../../../FileTree/scripts/TreeData';
2 +import { EditorFile, getFileData } from '../../../../scripts/EditorFile
3 import CodeMirrorEditor from '../CodeMirrorEditor.svelte';
4 +import { writeFile } from '@tauri-apps/api/fs';
5 import { getLang, getLangMode } from './Editor';
8 -export let tabs = writable([]);
6 +
7 +let id = 0;
8 +let activeid;
9 +
10 export let file_language = writable("");
11 export let linefeed = writable("");
12 +export let tablist: Tab[] = [];
13 +export let tabs = writable([]);
14 export let hidden = writable(true);
15 +export let isfile = writable(false);
16
17 class Tab {
18 + id: number;
19 label: string;
20 + active: boolean;
21 + constructor(id: number, label: string = "") {
22 + this.id = id;
23 + this.label = label === "" ? `Untitled-${id}` : label;
24 + }
25 +}
26 +
27 +export class FileTab extends Tab {
28 path: string;
29 language: string = "Plain Text";
30 linefeed: string;
18 - id: number;
19 - active: boolean = false;
31 + saved: boolean = true;
32 editor: CodeMirrorEditor | null;
33 editorcontent: string;
22 - saved: boolean = true;
23 - constructor(id: number, file , editor = null, saved: boolean = true) {
24 - this.id = id;
25 - this.label = file.filename === "" ? `Untitled-${id}` : file.filename;
34 + constructor(id: number, file: EditorFile, editor, saved: boolean = true) {
35 + super(id, file.filename);
36 this.path = file.path;
37 + this.saved = saved;
38 this.editor = editor;
39 this.editorcontent = file.content;
29 - this.saved = saved;
40
41 let filepath = file.path.split(".");
42 let extension = "txt";
43 if (filepath.length !== 1) {
44 extension = filepath.at(-1);
45 }
36 - this.language = getLang(extension);
46
47 + this.language = getLang(extension)
48 this.linefeed = file.linefeed;
39 -
49 +
50 let _ = undefined;
51 this.editor.$on("input", (e) => {
52 clearTimeout(_);
@@ -47,7 +57,7 @@ class Tab {
57 console.log(`${this.label} saved`);
58 if (!this.saved) {
59 this.saved = true;
50 - setActive(id);
60 + setActive(this.id);
61 }
62 }
63 else {
@@ -55,14 +65,17 @@ class Tab {
65 }
66 }, 1000)
67 })
58 - }
59 -
68 + }
69 +
70 async setLanguage(lang: string) {
71 let mode = await getLangMode(lang);
72 this.editor.setLanguageMode(mode);
73 +
74 + file_language.set(lang);
75 + this.language = lang;
76 }
64 - setFile(file) {
65 - this.label = file.filename === "" ? `Untitled-${id}` : file.filename;
77 + setFile(file: EditorFile) {
78 + this.label = file.filename === "" ? `Untitled-${this.id}` : file.filename;
79 this.path = file.path;
80 let filepath = file.path.split(".");
81 let extension = "txt";
@@ -73,96 +86,59 @@ class Tab {
86
87 this.linefeed = file.linefeed;
88 }
76 -}
77 -
78 -let id = 0;
79 -let activeid;
80 -let tablist: Tab[] = [];
81 -export async function addTab(f: string) {
82 - if (tablist.find(file => file.path === f)) {
83 - setActive(tablist.find(file => file.path === f).id);
84 - return;
89 + focusTab() {
90 + file_language.set(this.language);
91 + linefeed.set(this.linefeed);
92 + this.editor.focus();
93 }
86 - let file = await loadFile(f);
87 - let editor = new CodeMirrorEditor({ target: document.getElementById("tabview"), props: { content: file.content } });
88 - let tab = new Tab(id, file, editor);
89 - await tab.setLanguage(tab.language);
90 -
91 - tablist = [...tablist, tab];
92 - if (tablist.length > 0) {
93 - hidden.set(false);
94 + updateEditorVisibility(id: number) {
95 + this.editor.$set({ hidden: !(this.id === id) })
96 }
95 -
96 - tabs.set(tablist);
97 - setActive(id);
98 - id++;
97 }
100 -
101 -export async function addNewFileTab() {
102 - let content = "";
103 - let editor = new CodeMirrorEditor({ target: document.getElementById("tabview"), props: { content: content } });
104 - let file = createFile(); // create empty file
105 - let tab = new Tab(id, file, editor, false);
106 - tablist = [...tablist, tab];
107 - if (tablist.length > 0) {
108 - hidden.set(false);
98 +export class SettingsTab extends Tab {
99 + content: string;
100 + constructor(id: number, label: string, content: string) {
101 + super(id, label);
102 + this.content = content;
103 }
110 - tabs.set(tablist);
111 - setActive(id);
112 - id++;
104 }
105
115 -export async function saveFile() {
116 - for (let tab of tablist) {
117 - if (tab.active) {
118 - if (tab.path === "") {
119 - const filePath = await dialog.save({
120 - defaultPath: `${tab.label}.txt`
121 - }) as string;
122 - if (filePath == undefined) return;
123 - let file = createFile(filePath.split(sep).pop(), filePath);
124 - tab.setFile(file);
125 - tab.saved = true;
126 - setActive(tab.id); // refresh active tab to load file data in status bar
127 - }
128 - else {
129 - writeFile(tab.path, tab.editorcontent);
130 - }
131 - break;
106 +export async function addFileTab(path = "") {
107 + for (let t of tablist) {
108 + if ((t as FileTab).path === path) {
109 + setActive(t.id);
110 }
111 }
134 -}
112 + let file = await getFileData(path);
113 + let editor = new CodeMirrorEditor({ target: document.getElementById("tabview"), props: { content: file.content } });
114 + let tab = new FileTab(id, file, editor, path !== "");
115 + await tab.setLanguage(tab.language);
116 + tablist = [...tablist, tab];
117
136 -export function renameFile(label, path) {
137 - let tab = tablist.find(t => t.active);
138 - tab.label = label;
139 - tab.path = path;
140 - setActive(tab.id);
118 + refreshTabs();
119 }
120
143 -export function setLanguage(language) {
144 - for (let tab of tablist) {
145 - if (tab.active) {
146 - file_language.set(language);
147 - tab.language = language;
148 - tab.setLanguage(language);
149 - break;
150 - }
151 - }
152 -}
121 +export function addSettingsTab() {
122 + let content = "";
123 + let tab = new SettingsTab(id, "Settings", content);
124 + tablist = [...tablist, tab];
125
154 -export function isSaved(id) {
155 - return tablist.find(t => t.id == id).saved;
126 + refreshTabs();
127 }
128
158 -export function setActive(id) {
129 +export function setActive(id: number) {
130 for (let tab of tablist) {
131 if (tab.id === id) {
132 activeid = id;
133 tab.active = true;
163 - file_language.set(tab.language);
164 - linefeed.set(tab.linefeed);
165 - tab.editor.focus();
134 + if (tab instanceof FileTab) {
135 + let t = tab as FileTab;
136 + isfile.set(true);
137 + t.focusTab();
138 + }
139 + else {
140 + isfile.set(false);
141 + }
142 }
143 else {
144 tab.active = false;
@@ -172,12 +148,6 @@ export function setActive(id) {
148 updateEditorVisibility();
149 }
150
175 -function updateEditorVisibility() {
176 - for (let tab of tablist) {
177 - tab.editor.$set({ hidden: !(tab.id === activeid) })
178 - }
179 -}
180 -
151 export function closeTab(tabid: number) {
152 if (activeid === tabid) {
153 for (let i = 0; i <= tablist.length - 1; i++) {
@@ -191,14 +161,36 @@ export function closeTab(tabid: number) {
161 }
162 }
163 }
194 -
195 - tablist.find(t => t.id === tabid).editor.$destroy();
164 +
165 + for (let tab of tablist) {
166 + if (tab.id === tabid) {
167 + (tab as FileTab).editor.$destroy();
168 + break;
169 + }
170 + }
171 tablist = tablist.filter(t => t.id !== tabid);
172 tabs.set(tablist);
173
174 updateEditorVisibility();
175 if (tablist.length === 0) {
176 hidden.set(true);
177 + isfile.set(false);
178 id = 0;
179 }
204 -}
\ No newline at end of file
180 +}
181 +
182 +function refreshTabs() {
183 + if (tablist.length > 0) {
184 + hidden.set(false);
185 + isfile.set(true);
186 + }
187 + tabs.set(tablist);
188 + setActive(id);
189 + id++;
190 +}
191 +
192 +function updateEditorVisibility() {
193 + for (let tab of tablist) {
194 + (tab as FileTab).updateEditorVisibility(activeid);
195 + }
196 +}
src/components/FileTree/TreeViewNode.svelte
+3 -2
@@ -41,7 +41,8 @@
41 export let icon = undefined;
42
43 import { afterUpdate, getContext } from "svelte";
44 - import { addTab } from "../Content/Editor/scripts/Tabs";
44 + //import { addTab } from "../Content/Editor/scripts/Tabs";
45 + import { addFileTab } from "../Content/Editor/scripts/Tab";
46 import RenameModel from "../Modal/RenameModel.svelte";
47 import LeafNodeMenu from "./LeafNodeMenu.svelte";
48
@@ -90,7 +91,7 @@
91 clickNode(node);
92 }}
93 on:dblclick={async () => {
93 - await addTab(path);
94 + await addFileTab(path);
95 }}
96 on:keydown={(e) => {
97 if (
src/components/FileTree/scripts/TreeData.ts
+3 -14
@@ -1,6 +1,7 @@
1 import { dialog, fs } from "@tauri-apps/api";
2 import { readTextFile } from "@tauri-apps/api/fs";
3 import { sep } from "@tauri-apps/api/path";
4 +import { EditorFile } from "../../../scripts/EditorFile";
5 import { filetree } from "./TreeStore";
6 let parentname: string;
7 let dir;
@@ -11,27 +12,15 @@ export async function data() {
12 return await loadTree();
13 }
14
14 -class File {
15 - filename: string;
16 - path: string;
17 - content: string;
18 - linefeed: string;
19 - constructor(filename: string, path: string, linefeed: string, content) {
20 - this.filename = filename;
21 - this.path = path;
22 - this.linefeed = linefeed;
23 - this.content = content;
24 - }
25 -}
15 export function createFile(filename = "", path = "") {
27 - return new File(filename, path, getLF(""), "");
16 + return new EditorFile(filename, path, getLF(""), "");
17 }
18 export async function loadFile(path: string) {
19 if (path === null) return;
20 let filename = path.split(sep).pop();
21 let content = await readTextFile(path);
22 let linefeed = getLF(content);
34 - return new File(filename, path, linefeed, content);
23 + return new EditorFile(filename, path, linefeed, content);
24 }
25
26 function getLF(file) {
src/components/Footer/Footer.svelte
+5 -3
@@ -1,6 +1,8 @@
1 <script lang="ts">
2 - import { Terminal } from "carbon-icons-svelte";
3 - import { hidden, file_language, linefeed } from "../Content/Editor/scripts/Tabs";
2 + import { Terminal } from "carbon-icons-svelte"
3 + import { isfile } from "../Content/Editor/scripts/Tab";
4 + import {file_language, linefeed} from "../Content/Editor/scripts/Tab";
5 +
6 import { line_info } from "../Content/Editor/scripts/Editor";
7 import { invoke } from "@tauri-apps/api/tauri";
8 import { homeDir } from '@tauri-apps/api/path';
@@ -33,7 +35,7 @@
35 invoke("open_terminal", {path: userpath});
36 }}><Terminal /> <span class="toolname">Terminal</span></span>
37 </div>
36 - {#if !$hidden}
38 + {#if $isfile}
39 <div id="codeinfo">
40 <span title="End of Line Sequence">{$linefeed}</span>
41 <span>{$line_info.line} : {$line_info.col}</span>
src/components/Footer/LanguageList.svelte
+2 -2
@@ -1,6 +1,6 @@
1 <script lang="ts">
2 import { Modal, Search } from "carbon-components-svelte";
3 - import { setLanguage } from "../Content/Editor/scripts/Tabs";
3 + import { setFileLanguage } from "../../scripts/EditorFile";
4
5 export let showlangs = false;
6 export let langs;
@@ -35,7 +35,7 @@
35 </div>
36 <div id="selected">Selected: {selectedlang}</div>
37 <div id="button" on:click={() => {
38 - setLanguage(selectedlang);
38 + setFileLanguage(selectedlang);
39 showlangs = false;
40 }}>
41 Select
src/components/Menu/Menu.svelte
+1 -1
@@ -6,7 +6,7 @@
6 </script>
7
8 <OverflowMenu style="width: auto;" size="sm">
9 - <div slot="menu" style="padding: 1rem; color: white;">{name}</div>
9 + <div slot="menu" style="padding: 0 1rem; color: white;">{name}</div>
10 {#each options as menuoption}
11 <OverflowMenuItem on:click={menuoption.onclick} hasDivider={menuoption.divider}>
12 <span class="option_name">{menuoption.option}</span>
src/components/Menu/menu.ts
+6 -6
@@ -1,16 +1,16 @@
1 import { filetree, workspacename } from "../FileTree/scripts/TreeStore";
2 import { data, workspace } from "../FileTree/scripts/TreeData";
3 -import { addNewFileTab, addTab, saveFile } from "../Content/Editor/scripts/Tabs";
4 -import { dialog } from "@tauri-apps/api";
3 import { appWindow } from "@tauri-apps/api/window";
4 +import { addFileTab } from "../Content/Editor/scripts/Tab";
5 +import { openFile, saveFile } from "../../scripts/EditorFile";
6 export const filemenu = [
7 { option: "New File...", shortcut: "Ctrl + N", onclick: async () => {
8 - await addNewFileTab();
8 + await addFileTab();
9 } },
10 { option: "Open File...", shortcut: "Ctrl + O", onclick: async () => {
11 - let f = await dialog.open() as string;
12 - if (f === undefined) return;
13 - await addTab(f);
11 + let path = await openFile();
12 + if (path === undefined) return;
13 + await addFileTab(path);
14 } },
15 { option: "Open Folder", shortcut: "Ctrl + K", onclick: async () => {
16 let treedata = await data();
src/components/Modal/RenameModel.svelte
+2 -3
@@ -5,8 +5,7 @@
5 import { updateTree } from "../FileTree/scripts/TreeData";
6 import langlist from "../../scripts/languages/languages.json";
7 import { sep } from "@tauri-apps/api/path";
8 - import { renameFile, setLanguage } from "../Content/Editor/scripts/Tabs";
9 - import { getLang } from "../Content/Editor/scripts/Editor";
8 + import { renameFile, setFileLanguage } from "../../scripts/EditorFile";
9 export let open = false;
10 export let filename = "";
11 export let path = "/";
@@ -94,7 +93,7 @@
93
94 await fs.renameFile(oldpath, path);
95 renameFile(filenameinput, path);
97 - setLanguage(extinput);
96 + setFileLanguage(extinput);
97 await updateTree();
98 open = false;
99 }}
src/scripts/EditorFile.ts new
+71
@@ -0,0 +1,71 @@
1 +import { readTextFile, writeFile } from "@tauri-apps/api/fs";
2 +import { sep } from "@tauri-apps/api/path";
3 +import { dialog } from "@tauri-apps/api";
4 +import { setActive, tablist } from "../components/Content/Editor/scripts/Tab";
5 +import type { FileTab } from "../components/Content/Editor/scripts/Tab";
6 +
7 +export class EditorFile {
8 + filename: string;
9 + path: string;
10 + content: string;
11 + linefeed: string;
12 + constructor(filename: string, path: string, linefeed: string, content: string) {
13 + this.filename = filename;
14 + this.path = path;
15 + this.linefeed = linefeed;
16 + this.content = content;
17 + }
18 +}
19 +
20 +export async function openFile() {
21 + let path = await dialog.open() as string;
22 + if (path === undefined) return;
23 + return path;
24 +}
25 +export async function getFileData(path = "", label = "") {
26 + if (path === null) return;
27 + if (path === "") {
28 + return new EditorFile(label, path, getLF(""), "");
29 + }
30 + let filename = path.split(sep).pop();
31 + let content = await readTextFile(path);
32 + let linefeed = getLF(content);
33 + return new EditorFile(filename, path, linefeed, content);
34 +}
35 +
36 +export async function saveFile() {
37 + for (let tab of tablist) {
38 + if (tab.active) {
39 + let t = tab as FileTab;
40 + if (t.path === "") {
41 + const filePath = await dialog.save({
42 + defaultPath: `${tab.label}.txt`
43 + }) as string;
44 + if (filePath == undefined) return;
45 +
46 + let file = new EditorFile(filePath.split(sep).pop(), filePath, getLF(""), "");
47 + t.setFile(file);
48 + t.saved = true;
49 + setActive(tab.id);
50 + }
51 + else {
52 + writeFile(t.path, t.editorcontent);
53 + }
54 + }
55 + }
56 +}
57 +
58 +export function renameFile(label, path) {
59 + let tab = tablist.find(t => t.active);
60 + tab.label = label;
61 + (tab as FileTab).path = path;
62 + setActive(tab.id);
63 +}
64 +
65 +export function setFileLanguage(lang) {
66 + let tab = tablist.find(t => t.active) as FileTab;
67 + tab.setLanguage(lang);
68 +}
69 +function getLF(content: string) {
70 + return content.includes('\r\n') ? 'CRLF' : 'LF'
71 +}
\ No newline at end of file