Skip to content

Commit a4b5eca

Browse files
authored
feat(tabs): add pin/unpin support for editor and non-editor tabs (#1984)
1 parent 61aca01 commit a4b5eca

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+494
-168
lines changed

src/cm/commandRegistry.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ function registerCoreCommands() {
211211
return true;
212212
},
213213
});
214+
addCommand({
215+
name: "togglePinnedTab",
216+
description: "Pin or unpin current tab",
217+
readOnly: true,
218+
requiresView: false,
219+
run() {
220+
acode.exec("toggle-pin-tab");
221+
return true;
222+
},
223+
});
214224
addCommand({
215225
name: "newFile",
216226
description: "Create new file",

src/components/terminal/terminalManager.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class TerminalManager {
7373
sessions.push({
7474
pid: entry,
7575
name: `Terminal ${entry}`,
76+
pinned: false,
7677
});
7778
changed = true;
7879
continue;
@@ -88,12 +89,13 @@ class TerminalManager {
8889
typeof entry.name === "string" && entry.name.trim()
8990
? entry.name.trim()
9091
: `Terminal ${pid}`;
92+
const pinned = entry.pinned === true;
9193

92-
if (entry.pid !== pid || entry.name !== name) {
94+
if (entry.pid !== pid || entry.name !== name || entry.pinned !== pinned) {
9395
changed = true;
9496
}
9597

96-
sessions.push({ pid, name });
98+
sessions.push({ pid, name, pinned });
9799
}
98100

99101
for (const session of sessions) {
@@ -109,6 +111,7 @@ class TerminalManager {
109111
typeof session.name === "string" && session.name.trim()
110112
? session.name.trim()
111113
: `Terminal ${pid}`,
114+
pinned: session.pinned === true,
112115
});
113116
}
114117

@@ -174,7 +177,7 @@ class TerminalManager {
174177
}
175178
}
176179

177-
async persistTerminalSession(pid, name) {
180+
async persistTerminalSession(pid, name, pinned = false) {
178181
if (!pid) return;
179182

180183
const pidStr = String(pid);
@@ -185,6 +188,7 @@ class TerminalManager {
185188
const sessionData = {
186189
pid: pidStr,
187190
name: name || `Terminal ${pidStr}`,
191+
pinned: pinned === true,
188192
};
189193

190194
if (existingIndex >= 0) {
@@ -228,6 +232,7 @@ class TerminalManager {
228232
const instance = await this.createServerTerminal({
229233
pid: session.pid,
230234
name: session.name,
235+
pinned: session.pinned === true,
231236
reconnecting: true,
232237
render: false,
233238
});
@@ -266,7 +271,8 @@ class TerminalManager {
266271
*/
267272
async createTerminal(options = {}) {
268273
try {
269-
const { render, serverMode, reconnecting, ...terminalOptions } = options;
274+
const { render, serverMode, reconnecting, pinned, ...terminalOptions } =
275+
options;
270276
const shouldRender = render !== false;
271277
const isServerMode = serverMode !== false;
272278
const isReconnecting = reconnecting === true;
@@ -317,6 +323,7 @@ class TerminalManager {
317323
type: "terminal",
318324
content: terminalContainer,
319325
tabIcon: "licons terminal",
326+
pinned,
320327
render: shouldRender,
321328
});
322329

@@ -363,6 +370,7 @@ class TerminalManager {
363370
await this.persistTerminalSession(
364371
terminalComponent.pid,
365372
terminalName,
373+
terminalFile.pinned,
366374
);
367375
}
368376
resolve(instance);
@@ -382,7 +390,7 @@ class TerminalManager {
382390
try {
383391
// Force remove the tab without confirmation
384392
terminalFile._skipTerminalCloseConfirm = true;
385-
terminalFile.remove(true);
393+
terminalFile.remove(true, { ignorePinned: true });
386394
} catch (removeError) {
387395
console.error("Error removing terminal tab:", removeError);
388396
}
@@ -613,10 +621,22 @@ class TerminalManager {
613621
terminalFile.onclose = () => {
614622
this.closeTerminal(terminalId);
615623
};
624+
terminalFile.onpinstatechange = (pinned) => {
625+
if (!terminalComponent.serverMode || !terminalComponent.pid) return;
626+
void this.persistTerminalSession(
627+
terminalComponent.pid,
628+
terminalFile.filename,
629+
pinned,
630+
);
631+
};
616632

617633
terminalFile._skipTerminalCloseConfirm = false;
618634
const originalRemove = terminalFile.remove.bind(terminalFile);
619-
terminalFile.remove = async (force = false) => {
635+
terminalFile.remove = async (force = false, options = {}) => {
636+
if (terminalFile.pinned && !options?.ignorePinned) {
637+
return originalRemove(force, options);
638+
}
639+
620640
if (
621641
!terminalFile._skipTerminalCloseConfirm &&
622642
this.shouldConfirmTerminalClose()
@@ -627,7 +647,7 @@ class TerminalManager {
627647
}
628648

629649
terminalFile._skipTerminalCloseConfirm = false;
630-
return originalRemove(force);
650+
return originalRemove(force, options);
631651
};
632652

633653
// Enhanced resize handling with debouncing
@@ -717,6 +737,7 @@ class TerminalManager {
717737
await this.persistTerminalSession(
718738
terminalComponent.pid,
719739
formattedTitle,
740+
terminalFile.pinned,
720741
);
721742
}
722743

@@ -744,7 +765,7 @@ class TerminalManager {
744765

745766
this.closeTerminal(terminalId);
746767
terminalFile._skipTerminalCloseConfirm = true;
747-
terminalFile.remove(true);
768+
terminalFile.remove(true, { ignorePinned: true });
748769
toast(message);
749770
};
750771

@@ -823,7 +844,7 @@ class TerminalManager {
823844
if (removeTab && terminal.file) {
824845
try {
825846
terminal.file._skipTerminalCloseConfirm = true;
826-
terminal.file.remove(true);
847+
terminal.file.remove(true, { ignorePinned: true });
827848
} catch (removeError) {
828849
console.error("Error removing terminal tab:", removeError);
829850
}

src/handlers/editorFileTab.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ function getClientPos(e) {
283283
* @param {HTMLElement} $parent
284284
*/
285285
function updateFileList($parent) {
286+
const pinnedCount = editorManager.files.filter((file) => file.pinned).length;
286287
const children = [...$parent.children];
287288
const newFileList = [];
288289
for (let el of children) {
@@ -295,6 +296,25 @@ function updateFileList($parent) {
295296
}
296297

297298
editorManager.files = newFileList;
299+
300+
const draggedFile = newFileList.find((file) => file.tab === $tab);
301+
if (draggedFile) {
302+
const draggedIndex = newFileList.indexOf(draggedFile);
303+
let nextPinnedState;
304+
305+
if (!draggedFile.pinned && draggedIndex < pinnedCount) {
306+
nextPinnedState = true;
307+
} else if (draggedFile.pinned && draggedIndex >= pinnedCount) {
308+
nextPinnedState = false;
309+
}
310+
311+
if (nextPinnedState !== undefined) {
312+
draggedFile.setPinnedState(nextPinnedState, { reorder: false });
313+
if (typeof editorManager.normalizePinnedTabOrder === "function") {
314+
editorManager.normalizePinnedTabOrder(editorManager.files);
315+
}
316+
}
317+
}
298318
}
299319

300320
/**

src/lang/ar-ye.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,5 +699,9 @@
699699
"settings-note-formatter-settings": "Assign a formatter to each language. Install formatter plugins to unlock more options.",
700700
"settings-note-lsp-settings": "Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.",
701701
"search result label singular": "result",
702-
"search result label plural": "results"
702+
"search result label plural": "results",
703+
"pin tab": "Pin tab",
704+
"unpin tab": "Unpin tab",
705+
"pinned tab": "Pinned tab",
706+
"unpin tab before closing": "Unpin the tab before closing it."
703707
}

src/lang/be-by.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,5 +701,9 @@
701701
"settings-note-formatter-settings": "Assign a formatter to each language. Install formatter plugins to unlock more options.",
702702
"settings-note-lsp-settings": "Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.",
703703
"search result label singular": "result",
704-
"search result label plural": "results"
704+
"search result label plural": "results",
705+
"pin tab": "Pin tab",
706+
"unpin tab": "Unpin tab",
707+
"pinned tab": "Pinned tab",
708+
"unpin tab before closing": "Unpin the tab before closing it."
705709
}

src/lang/bn-bd.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,5 +700,9 @@
700700
"settings-note-formatter-settings": "Assign a formatter to each language. Install formatter plugins to unlock more options.",
701701
"settings-note-lsp-settings": "Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.",
702702
"search result label singular": "result",
703-
"search result label plural": "results"
703+
"search result label plural": "results",
704+
"pin tab": "Pin tab",
705+
"unpin tab": "Unpin tab",
706+
"pinned tab": "Pinned tab",
707+
"unpin tab before closing": "Unpin the tab before closing it."
704708
}

src/lang/cs-cz.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,5 +700,9 @@
700700
"settings-note-formatter-settings": "Assign a formatter to each language. Install formatter plugins to unlock more options.",
701701
"settings-note-lsp-settings": "Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.",
702702
"search result label singular": "result",
703-
"search result label plural": "results"
703+
"search result label plural": "results",
704+
"pin tab": "Pin tab",
705+
"unpin tab": "Unpin tab",
706+
"pinned tab": "Pinned tab",
707+
"unpin tab before closing": "Unpin the tab before closing it."
704708
}

src/lang/de-de.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,5 +700,9 @@
700700
"settings-note-formatter-settings": "Assign a formatter to each language. Install formatter plugins to unlock more options.",
701701
"settings-note-lsp-settings": "Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.",
702702
"search result label singular": "result",
703-
"search result label plural": "results"
703+
"search result label plural": "results",
704+
"pin tab": "Pin tab",
705+
"unpin tab": "Unpin tab",
706+
"pinned tab": "Pinned tab",
707+
"unpin tab before closing": "Unpin the tab before closing it."
704708
}

src/lang/en-us.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,5 +700,9 @@
700700
"settings-note-formatter-settings": "Assign a formatter to each language. Install formatter plugins to unlock more options.",
701701
"settings-note-lsp-settings": "Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.",
702702
"search result label singular": "result",
703-
"search result label plural": "results"
703+
"search result label plural": "results",
704+
"pin tab": "Pin tab",
705+
"unpin tab": "Unpin tab",
706+
"pinned tab": "Pinned tab",
707+
"unpin tab before closing": "Unpin the tab before closing it."
704708
}

src/lang/es-sv.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,5 +700,9 @@
700700
"settings-note-formatter-settings": "Assign a formatter to each language. Install formatter plugins to unlock more options.",
701701
"settings-note-lsp-settings": "Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.",
702702
"search result label singular": "result",
703-
"search result label plural": "results"
703+
"search result label plural": "results",
704+
"pin tab": "Pin tab",
705+
"unpin tab": "Unpin tab",
706+
"pinned tab": "Pinned tab",
707+
"unpin tab before closing": "Unpin the tab before closing it."
704708
}

0 commit comments

Comments
 (0)