Skip to content

Commit

Permalink
add tabTracker, add JSDoc, organize and format the code (#17)
Browse files Browse the repository at this point in the history
* add tabTracker, add JSDoc, organize and format the code, add info to changelog, increase the version to 1.1.2

* delete changelog, decrease the version to 1.1.1

* oj dylan dylan
  • Loading branch information
Megaemce authored Oct 21, 2024
1 parent 2ea2e55 commit b6b850f
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 46 deletions.
16 changes: 12 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
# Change Log
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.

All notable changes to the "lru-tabs" extension will be documented in this file.
## [1.1.2] - 2024-10-14

Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
### Added

- Removing the least active tab based on the timestamp not order,

## [1.1.1] - 2024-10-12

### Added

- Option to disabled automatic tab sorting

## [Unreleased]

- Initial release
- Initial release
60 changes: 47 additions & 13 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,53 @@
import * as vscode from 'vscode';
import { removeLRUTabs, reorderTabs } from './functions';
import * as vscode from "vscode";
import { removeLRUTabs, reorderTabs } from "./functions";
import { tabTracker } from "./tabTracker";

/**
* Activates the extension
* @param {vscode.ExtensionContext} context - The context in which the extension runs
*/
export function activate(context: vscode.ExtensionContext) {
// Initialize tabTracker (this will set timestamps for existing tabs)
tabTracker;

// when a tab is changed or deselected
let listener = vscode.window.onDidChangeActiveTextEditor(() => {
const configuration = vscode.workspace.getConfiguration();
if(configuration.lrutabs.reorderTabs === true){
reorderTabs(vscode.window.tabGroups.activeTabGroup);
}
if(configuration.lrutabs.closeTabs === true){
removeLRUTabs(vscode.window.tabGroups.activeTabGroup, configuration.lrutabs.maxTabs || 7);
}
});
context.subscriptions.push(listener);
/**
* Event listener for changes in the active text editor
* @type {vscode.Disposable}
*/
let listener: vscode.Disposable = vscode.window.onDidChangeActiveTextEditor(
(editor) => {
const configuration = vscode.workspace.getConfiguration();

if (editor) {
const activeTab =
vscode.window.tabGroups.activeTabGroup.activeTab;
if (activeTab) {
// Update the timestamp for the newly active tab
tabTracker.updateTimestamp(activeTab);
}
}

// Reorder tabs if the configuration option is enabled
if (configuration.lrutabs.reorderTabs === true) {
reorderTabs(vscode.window.tabGroups.activeTabGroup);
}

// Close least recently used tabs if the configuration option is enabled
if (configuration.lrutabs.closeTabs === true) {
removeLRUTabs(
vscode.window.tabGroups.activeTabGroup,
configuration.lrutabs.maxTabs || 7
);
}
}
);

// Add the listener to the extension's subscriptions
context.subscriptions.push(listener);
}

/**
* Deactivates the extension
* This function is empty as there's no cleanup required
*/
export function deactivate() {}
80 changes: 51 additions & 29 deletions src/functions.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,75 @@
// this file defines the main functions of the extension
import * as vscode from 'vscode';
import * as vscode from "vscode";
import { tabTracker } from "./tabTracker";

const getNumberOfTabs = (group: vscode.TabGroup): number => {
return group.tabs.length;
};

// reorder the tabs in a tab group based on the currently active tab. The most recently active tabs will be closest to the left
// a separate copy of tabs is kept to track the order
/**
* Reorders the tabs in a tab group based on the currently active tab.
* The most recently active tabs will be closest to the left.
*
* @param {vscode.TabGroup} group - The tab group to reorder
*/
const reorderTabs = (group: vscode.TabGroup): void => {
const activeTab = group.tabs.find(tab => tab.isActive);
const numberOfPinnedTabs = group.tabs.filter(tab => tab.isPinned).length;
// this occurs if there is no tab
if(activeTab === undefined){
const activeTab = group.tabs.find((tab) => tab.isActive);
const numberOfPinnedTabs = group.tabs.filter((tab) => tab.isPinned).length;

// This occurs if there is no active tab
if (activeTab === undefined) {
return;
}

// don't touch pinned tabs
if(activeTab.isPinned){
// Don't move pinned tabs
if (activeTab.isPinned) {
return;
}
const index = group.tabs.indexOf(activeTab);

// check if tab is already in the correct position (this happens when it's called twice sometimes)
var targetIndex = numberOfPinnedTabs;
if(targetIndex === index){
// Check if tab is already in the correct position (this happens when it's called twice sometimes)
if (numberOfPinnedTabs === index) {
return;
}
// Move current tab position to the start, not before pinned tabs
vscode.commands.executeCommand("moveActiveEditor", {
to: "left",
by: "tab",
value: index - numberOfPinnedTabs,
});

// move current tab position to the start, but not before pinned tabs
vscode.commands.executeCommand('moveActiveEditor', { to: 'left', by: 'tab', value: index-numberOfPinnedTabs });
vscode.commands.executeCommand('workbench.action.unpinEditor');
vscode.commands.executeCommand("workbench.action.unpinEditor");
};

// remove least recently used tabs (but only if they aren't dirty and not pinned, we don't want to touch those dirty tabs otherwise we could get a disease right?)
/**
* Removes least recently used tabs, but only if they aren't dirty and not pinned.
* We don't want to touch those dirty tabs otherwise we could get a disease, right?
*
* @param {vscode.TabGroup} group - The tab group to remove tabs from
* @param {number} maxTabs - The maximum number of tabs to keep open
*/
const removeLRUTabs = (group: vscode.TabGroup, maxTabs: number): void => {
if(group.tabs.length <= maxTabs){
const tabsToClose: vscode.Tab[] = [];

if (group.tabs.length <= maxTabs) {
return;
}

const tabsToClose: vscode.Tab[] = [];
group.tabs.slice(0).reverse().forEach(tab => {
if(!tab.isDirty && !tab.isPinned && !tab.isPreview && (group.tabs.length - tabsToClose.length) > maxTabs){
tabsToClose.push(tab);
}
// Sort tabs based on their last active timestamp
const sortedTabs = group.tabs.slice().sort((a, b) => {
return tabTracker.getTimestamp(a) - tabTracker.getTimestamp(b);
});

// actually close the tabs here
tabsToClose.forEach(tab => vscode.window.tabGroups.close(tab));
sortedTabs.forEach((tab) => {
// Don't close the active tab, dirty tabs, pinned tabs, or preview tabs
if (
!tab.isActive &&
!tab.isDirty &&
!tab.isPinned &&
!tab.isPreview &&
group.tabs.length - tabsToClose.length > maxTabs
) {
tabsToClose.push(tab);
}
});

// Actually close the tabs here
tabsToClose.forEach((tab) => vscode.window.tabGroups.close(tab));
};

export { reorderTabs, removeLRUTabs };
70 changes: 70 additions & 0 deletions src/tabTracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as vscode from "vscode";

/**
* TabTracker class to manage timestamps for VSCode tabs
*/
class TabTracker {
/**
* Map to store timestamps for each tab
* @private
*/
private tabTimestamps: Map<string, number> = new Map();

/**
* Constructor for TabTracker
* Initializes timestamps for existing tabs when the extension is activated
*/
constructor() {
this.initializeExistingTabs();
}

/**
* Initializes timestamps for existing tabs
* @private
*/
private initializeExistingTabs() {
vscode.window.tabGroups.all.forEach((group) => {
group.tabs.forEach((tab, index: number) => {
const tabId = this.getTabId(tab);
// Set timestamps for existing tabs, giving a slight preference to tabs on the left
this.tabTimestamps.set(tabId, Date.now() - index);
});
});
}

/**
* Updates the timestamp for a given tab
* @param {vscode.Tab} tab - The tab to update
*/
updateTimestamp(tab: vscode.Tab) {
const tabId = this.getTabId(tab);
this.tabTimestamps.set(tabId, Date.now());
}

/**
* Gets the timestamp for a given tab
* @param {vscode.Tab} tab - The tab to get the timestamp for
* @returns {number} The timestamp of the tab, or 0 if not found
*/
getTimestamp(tab: vscode.Tab): number {
const tabId = this.getTabId(tab);
return this.tabTimestamps.get(tabId) || 0;
}

/**
* Gets a unique identifier for a tab
* @param {vscode.Tab} tab - The tab to get the ID for
* @returns {string} The unique identifier for the tab
* @private
*/
private getTabId(tab: vscode.Tab): string {
return tab.input instanceof vscode.TabInputText
? tab.input.uri.toString()
: "";
}
}

/**
* Exported instance of TabTracker
*/
export const tabTracker = new TabTracker();

0 comments on commit b6b850f

Please sign in to comment.