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