diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml deleted file mode 100644 index 72af402..0000000 --- a/.github/workflows/eslint.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Lint - -on: - push: - branches: ["main"] - pull_request: - # The branches below must be a subset of the branches above - branches: ["main"] - -env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: write - -jobs: - eslint: - defaults: - run: - working-directory: ./vscode-workspaces - - name: Run eslint scanning - runs-on: ubuntu-latest - permissions: - contents: read - security-events: write - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install ESLint - run: | - npm install - npm ci - npm install @microsoft/eslint-formatter-sarif@2.1.7 - - - name: Run ESLint - run: npm run lint -- - --format @microsoft/eslint-formatter-sarif - --output-file eslint-results.sarif - continue-on-error: true - - - name: Upload analysis results to GitHub - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: eslint-results.sarif - wait-for-processing: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f18052b..e1ebbf3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,3 +71,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: semantic-release + diff --git a/README.md b/README.md index 5a94941..44bd37a 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Navigate to your desired directory and execute following commands in the termina ```bash git clone https://github.com/ZanzyTHEbar/vscode-nautilus.git -cd vscode-nautilus/vscode-workspaces +cd vscode-nautilus/gnome-extension make && make pack && make install ``` diff --git a/.releaserc b/gnome-extension/.releaserc similarity index 97% rename from .releaserc rename to gnome-extension/.releaserc index b5f8a13..00d0b7a 100644 --- a/.releaserc +++ b/gnome-extension/.releaserc @@ -145,12 +145,6 @@ "changelogTitle": "# 📦 Changelog \n[![conventional commits](https://img.shields.io/badge/conventional%20commits-1.0.0-yellow.svg)](https://conventionalcommits.org)\n[![semantic versioning](https://img.shields.io/badge/semantic%20versioning-2.0.0-green.svg)](https://semver.org)\n> All notable changes to this project will be documented in this file" } ], - [ - "@semantic-release/github", - { - "addReleases": "bottom" - } - ], [ "@semantic-release/exec", { diff --git a/gnome-extension/Makefile b/gnome-extension/Makefile index f7e99e0..df16fc3 100644 --- a/gnome-extension/Makefile +++ b/gnome-extension/Makefile @@ -26,6 +26,7 @@ install: $(NAME).zip @touch ~/.local/share/gnome-shell/extensions/$(NAME)@$(DOMAIN) @rm -rf ~/.local/share/gnome-shell/extensions/$(NAME)@$(DOMAIN) @mv dist ~/.local/share/gnome-shell/extensions/$(NAME)@$(DOMAIN) + @echo "Extension installed. Restart GNOME Shell with Alt+F2, then 'r'. Or login again." clean: @rm -rf dist node_modules $(NAME).zip diff --git a/gnome-extension/metadata.json b/gnome-extension/metadata.json index d109c46..f40c561 100644 --- a/gnome-extension/metadata.json +++ b/gnome-extension/metadata.json @@ -1,6 +1,6 @@ { "name": "VSCode Workspaces", - "description": "A VSCode/Codium Workspace management tool-set for GNOME", + "description": "A VSCode/Codium Workspace management tool-set for GNOME - This extension is not affiliated, funded, or in any way associated with Microsoft and vscode software.", "uuid": "vscode-workspaces@prometheontechnologies.com", "url": "https://github.com/ZanzyTHEbar/vscode-nautilus", "settings-schema": "org.gnome.shell.extensions.vscode-workspaces", diff --git a/gnome-extension/src/extension.ts b/gnome-extension/src/extension.ts index 0c624f5..a11f39b 100644 --- a/gnome-extension/src/extension.ts +++ b/gnome-extension/src/extension.ts @@ -1,6 +1,6 @@ import GLib from 'gi://GLib'; import Gio from 'gi://Gio'; -//import Meta from 'gi://Meta'; +//import Mtk from 'gi://Mtk'; //import Shell from 'gi://Shell'; import St from 'gi://St'; import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'; @@ -52,22 +52,32 @@ export default class VSCodeWorkspacesExtension extends Extension { //_notification: MessageTray.Notification | null = null; enable() { + this.gsettings = this.getSettings(); + this._indicator = new PanelMenu.Button(0.0, this.metadata.name, false); + // check if the vscode icon exists, or the code icon exists + // if it doesn't, use the default + let iconName: string = 'vscode'; + + if (!this._iconExists('vscode')) { + iconName = 'code'; + } else if (!this._iconExists('code')) { + iconName = 'vscode'; + } + const icon = new St.Icon({ - icon_name: 'code', + icon_name: iconName, style_class: 'system-status-icon', }); this._indicator.add_child(icon); - this._createMenu(); - Main.panel.addToStatusArea(this.metadata.uuid, this._indicator); this._startRefresh(); - - this.gsettings = this.getSettings(); + this._setSettings(); + this._createMenu(); this.gsettings.connect('changed', () => { this._setSettings(); @@ -75,6 +85,26 @@ export default class VSCodeWorkspacesExtension extends Extension { }); } + disable() { + if (this._refreshTimeout) { + GLib.source_remove(this._refreshTimeout); + this._refreshTimeout = null; + } + if (this._indicator) { + this._indicator.destroy(); + this._indicator = undefined; + } + this.gsettings = undefined; + //this._recentWorkspaces = null; + //this._workspaces = null; + + // clean up the cache + this._workspaces.clear(); + this._recentWorkspaces.clear(); + + this._log(`VSCode Workspaces Extension disabled`); + } + _setSettings() { this._newWindow = this.gsettings!.get_value('new-window').deepUnpack() ?? false; this._vscodeLocation = this.gsettings!.get_value('vscode-location').deepUnpack() ?? 'code'; @@ -88,23 +118,23 @@ export default class VSCodeWorkspacesExtension extends Extension { this._log(`Refresh Interval: ${this._refreshInterval}`); this._log(`Prefer Code Workspace File: ${this._preferCodeWorkspaceFile}`); this._log(`Debug: ${this._debug}`); - } - disable() { - if (this._refreshTimeout) { - GLib.source_remove(this._refreshTimeout); - this._refreshTimeout = null; - } - if (this._indicator) { - this._indicator.destroy(); - this._indicator = undefined; - } - this.gsettings = undefined; + _iconExists(iconName: string): boolean { + const theme = new Gio.Settings({ schema: 'org.gnome.desktop.interface' }); + const iconThemeName = theme.get_string('icon-theme'); + const iconTheme = new Gio.ThemedIcon({ name: iconThemeName }); - this._log(`VSCode Workspaces Extension disabled`); + try { + const iconInfo = iconTheme.get_names().filter(icon => icon === iconName); + return iconInfo !== null; + } catch (error) { + logError(error as object, 'Failed to check if icon exists'); + return false; + } } + _createMenu() { if (!this._indicator) return; @@ -138,7 +168,7 @@ export default class VSCodeWorkspacesExtension extends Extension { _loadRecentWorkspaces() { this._getRecentWorkspaces(); - if (this._recentWorkspaces.size === 0) { + if (this._recentWorkspaces?.size === 0) { this._log('No recent workspaces found'); return; } @@ -161,7 +191,7 @@ export default class VSCodeWorkspacesExtension extends Extension { // Create the PopupMenu for the ComboBox items - this._recentWorkspaces.forEach(workspace => { + this._recentWorkspaces?.forEach(workspace => { const item = new PopupMenu.PopupMenuItem(workspace.name); const trashIcon = new St.Icon({ @@ -262,6 +292,33 @@ export default class VSCodeWorkspacesExtension extends Extension { storeDir: workspaceStoreDir, }; + const pathToWorkspace = Gio.File.new_for_uri(newWorkspace.uri); + + // check if the file exists and remove it from the list if it doesn't + if (!pathToWorkspace.query_exists(null)) { + this._log( + `Workspace does not exist and will be removed from the list: ${pathToWorkspace.get_path()}` + ); + const deleteRes = this._workspaces.delete(newWorkspace); + if (!deleteRes) { + this._log(`Failed to remove workspace: ${newWorkspace.uri} from cache - not in cache or cache is empty`); + } + + // Try to delete the workspace directory itself + // now remove the workspaceStore directory + const trashRes = newWorkspace.storeDir.trash(null); + const workspaceName = GLib.path_get_basename(newWorkspace.uri); + + if (!trashRes) { + this._log(`Failed to move ${workspaceName} to trash`); + return; + } + + this._log(`Workspace Trashed: ${workspaceName}`); + + continue; + } + callback(newWorkspace); // Check if a workspace with the same uri exists const workspaceExists = Array.from(this._workspaces).some(workspace => { @@ -314,15 +371,6 @@ export default class VSCodeWorkspacesExtension extends Extension { const pathToWorkspace = Gio.File.new_for_uri(workspace.uri); - // check if the file exists and remove it from the list if it doesn't - if (!pathToWorkspace.query_exists(null)) { - this._log( - `Workspace does not exist and will be removed from the list: ${pathToWorkspace.get_path()}` - ); - this._workspaces.delete(workspace); - return; - } - // we will only execute the below if the setting is active if (!this._preferCodeWorkspaceFile) { @@ -338,7 +386,7 @@ export default class VSCodeWorkspacesExtension extends Extension { return; } - // Check if there is a file a `.code-workspace` extension in the directory + // Check if there is a file with a `.code-workspace` extension in the directory // look for children, and find one with the `.code-workspace` extension const enumerator = pathToWorkspace.enumerate_children( @@ -409,29 +457,29 @@ export default class VSCodeWorkspacesExtension extends Extension { }); // sort the workspace files by access time - /* this._workspaces = new Set(Array.from(this._workspaces).sort((a, b) => { - + this._workspaces = new Set(Array.from(this._workspaces).sort((a, b) => { + const aInfo = Gio.File.new_for_uri(a.uri).query_info('standard::*,unix::atime', Gio.FileQueryInfoFlags.NONE, null); const bInfo = Gio.File.new_for_uri(b.uri).query_info('standard::*,unix::atime', Gio.FileQueryInfoFlags.NONE, null); - + if (!aInfo || !bInfo) { this._log(`No file info found for ${a} or ${b}`); return 0; } - + const aAccessTime = aInfo.get_attribute_uint64('unix::atime'); const bAccessTime = bInfo.get_attribute_uint64('unix::atime'); - + if (aAccessTime > bAccessTime) { return -1; } - + if (aAccessTime < bAccessTime) { return 1; } - + return 0; - })); */ + })); // this._log the Set of workspaces, given that .values() returns an iterator this._log(`[Workspace Cache]: ${Array.from(this._workspaces).map(workspace => workspace.uri)}`); @@ -618,7 +666,7 @@ export default class VSCodeWorkspacesExtension extends Extension { // Purge the cache this._workspaces.clear(); - this._recentWorkspaces.clear(); + this._recentWorkspaces?.clear(); // Refresh the menu to reflect the changes diff --git a/gnome-extension/src/prefs.ts b/gnome-extension/src/prefs.ts index 58bb7aa..5591bb1 100644 --- a/gnome-extension/src/prefs.ts +++ b/gnome-extension/src/prefs.ts @@ -6,11 +6,9 @@ import { gettext as _, } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; -export default class GnomeRectanglePreferences extends ExtensionPreferences { - _settings?: Gio.Settings; - +export default class VSCodeWorkspacesPreferences extends ExtensionPreferences { fillPreferencesWindow(window: Adw.PreferencesWindow) { - this._settings = this.getSettings(); + const _settings = this.getSettings(); const page = new Adw.PreferencesPage({ title: _('General'), @@ -75,34 +73,34 @@ export default class GnomeRectanglePreferences extends ExtensionPreferences { refreshIntervalGroup.add(refreshGroupEntry); // Bind settings - this._settings!.bind( + _settings.bind( 'new-window', newWindowSwitch, 'active', Gio.SettingsBindFlags.DEFAULT ); - this._settings!.bind( + _settings.bind( 'vscode-location', vscodeLocation, 'text', Gio.SettingsBindFlags.DEFAULT ); - this._settings!.bind( + _settings.bind( 'debug', debug, 'active', Gio.SettingsBindFlags.DEFAULT ); - this._settings!.bind( + _settings.bind( 'prefer-workspace-file', preferWorkspaceFile, 'active', Gio.SettingsBindFlags.DEFAULT ); - this._settings!.bind( + _settings.bind( 'refresh-interval', refreshGroupEntry, 'value', @@ -112,5 +110,9 @@ export default class GnomeRectanglePreferences extends ExtensionPreferences { // Show the window // Add the page to the window window.add(page); + + window.connect('close-request', () => { + _settings.apply(); + }); } }