├── .eslintrc.json ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── images ├── logo.png ├── preview.gif └── settings-preview.png ├── package-lock.json ├── package.json ├── src ├── configuration.ts ├── constants.ts ├── extension.ts ├── interfaces.ts ├── sorter.ts ├── test │ ├── runTest.ts │ └── suite │ │ ├── extension.test.ts │ │ └── index.ts └── utils.ts ├── tsconfig.json └── webpack.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | }, 19 | "ignorePatterns": [ 20 | "out", 21 | "dist", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | .vscode-test/ 5 | *.vsix 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher"] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/dist/**/*.js" 17 | ], 18 | "preLaunchTask": "${defaultBuildTask}" 19 | }, 20 | { 21 | "name": "Extension Tests", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "args": [ 25 | "--extensionDevelopmentPath=${workspaceFolder}", 26 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 27 | ], 28 | "outFiles": [ 29 | "${workspaceFolder}/out/**/*.js", 30 | "${workspaceFolder}/dist/**/*.js" 31 | ], 32 | "preLaunchTask": "tasks: watch-tests" 33 | }, 34 | { 35 | "name": "Run Web Extension in VS Code", 36 | "type": "pwa-extensionHost", 37 | "debugWebWorkerHost": true, 38 | "request": "launch", 39 | "args": [ 40 | "--extensionDevelopmentPath=${workspaceFolder}", 41 | "--extensionDevelopmentKind=web" 42 | ], 43 | "outFiles": ["${workspaceFolder}/dist/web/**/*.js"], 44 | "preLaunchTask": "npm: watch-web" 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false, // set this to true to hide the "out" folder with the compiled JS files 5 | "dist": false // set this to true to hide the "dist" folder with the compiled JS files 6 | }, 7 | "search.exclude": { 8 | "out": true, // set this to false to include "out" folder in search results 9 | "dist": true // set this to false to include "dist" folder in search results 10 | }, 11 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 12 | "typescript.tsc.autoDetect": "off" 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": [ 10 | "$ts-webpack-watch", 11 | "$tslint-webpack-watch" 12 | ], 13 | "isBackground": true, 14 | "presentation": { 15 | "reveal": "never", 16 | "group": "watchers" 17 | }, 18 | "group": { 19 | "kind": "build", 20 | "isDefault": true 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "watch-tests", 26 | "problemMatcher": "$tsc-watch", 27 | "isBackground": true, 28 | "presentation": { 29 | "reveal": "never", 30 | "group": "watchers" 31 | }, 32 | "group": "build" 33 | }, 34 | { 35 | "label": "tasks: watch-tests", 36 | "dependsOn": [ 37 | "npm: watch", 38 | "npm: watch-tests" 39 | ], 40 | "problemMatcher": [] 41 | }, 42 | { 43 | "type": "npm", 44 | "script": "watch-web", 45 | "group": "build", 46 | "isBackground": true, 47 | "problemMatcher": ["$ts-webpack-watch"] 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/** 4 | node_modules/** 5 | src/** 6 | .gitignore 7 | .yarnrc 8 | webpack.config.js 9 | vsc-extension-quickstart.md 10 | **/tsconfig.json 11 | **/.eslintrc.json 12 | **/*.map 13 | **/*.ts 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.1 (January 17, 2023) 2 | 3 | - Improved error handling and logging (Issue #2) 4 | 5 | 6 | ## 2.0.0 (June 12, 2022) 7 | 8 | - [Web Extensions](https://code.visualstudio.com/api/extension-guides/web-extensions) enabled 9 | - Performance improvements 10 | 11 | 12 | ## 1.0.0 (June 5, 2022) 13 | 14 | - Initial release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 aswinkumar863 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sort Editors for Visual Studio Code 2 | 3 | [![Latest Release](https://vsmarketplacebadges.dev/version-short/aswinkumar863.sort-editors.svg)](https://marketplace.visualstudio.com/items?itemName=aswinkumar863.sort-editors) 4 | [![Installs](https://vsmarketplacebadges.dev/installs/aswinkumar863.sort-editors.svg)](https://marketplace.visualstudio.com/items?itemName=aswinkumar863.sort-editors) 5 | [![Rating](https://vsmarketplacebadges.dev/rating-short/aswinkumar863.sort-editors.svg)](https://marketplace.visualstudio.com/items?itemName=aswinkumar863.sort-editors#review-details) 6 | [![Trending Weekly](https://vsmarketplacebadges.dev/trending-weekly/aswinkumar863.sort-editors.svg)](https://marketplace.visualstudio.com/items?itemName=aswinkumar863.sort-editors) 7 | ![GitHub closed issues](https://img.shields.io/github/issues-closed/aswinkumar863/sort-editors-vscode?color=blue) 8 | 9 | This extension will automatically sort the editors/tabs when they are opened. 10 | 11 | ## Features 12 | 13 | * Sorts automatically when the new editor is opened. 14 | * Supports `alphabetical` and `fullpath` modes of sorting. 15 | * Supports reordering of existing editors. 16 | * Supports sorting in multiple tab groups. 17 | * Safely ignores pinned editors. 18 | 19 | ## How it looks like 20 | 21 | ![Settings](images/preview.gif) 22 | 23 | 24 | ## Extension Settings 25 | 26 | 27 | ![Settings](images/settings-preview.png) 28 | 29 | * **`sortEditors.sortEditorsAutomatically`**: Enable to automatically sort the editors when they are opened. 30 | * **`sortEditors.sortEditorsOrder`**: Controls the sorting order of opened editors. 31 | 32 | **For example:** 33 | 34 | ```jsonc 35 | "sortEditors.sortEditorsAutomatically": true, 36 | "sortEditors.sortEditorsOrder": "fullpath" 37 | ``` 38 | 39 | ## Limitations 40 | Due to limitations in the VScode Extensions API, reordering tabs/editors takes place one by one. That might feels a bit slow. 41 | 42 | ## Issues 43 | 44 | Submit the [issues](https://github.com/aswinkumar863/sort-editors-vscode/issues) if you find any bug or have any suggestion. 45 | 46 | ## Release Notes 47 | 48 | Detailed release notes are available [here](CHANGELOG.md). 49 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aswinkumar863/sort-editors-vscode/35bc9ec83af18f580a3c3a1e63da149a11936e52/images/logo.png -------------------------------------------------------------------------------- /images/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aswinkumar863/sort-editors-vscode/35bc9ec83af18f580a3c3a1e63da149a11936e52/images/preview.gif -------------------------------------------------------------------------------- /images/settings-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aswinkumar863/sort-editors-vscode/35bc9ec83af18f580a3c3a1e63da149a11936e52/images/settings-preview.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sort-editors", 3 | "displayName": "Sort Editors", 4 | "description": "Automatically sorts the editors when they are opened", 5 | "author": { 6 | "name": "Aswin Kumar", 7 | "email": "aswinkumar863@gmail.com" 8 | }, 9 | "license": "MIT", 10 | "version": "2.0.1", 11 | "publisher": "aswinkumar863", 12 | "icon": "images/logo.png", 13 | "galleryBanner": { 14 | "color": "#B8163D", 15 | "theme": "dark" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/aswinkumar863/sort-editors-vscode.git" 20 | }, 21 | "categories": [ 22 | "Other" 23 | ], 24 | "keywords": [ 25 | "Sort Editors", 26 | "Sort Open Editors", 27 | "Alphabetically Sort Editors", 28 | "Sort Tabs", 29 | "Sort Files" 30 | ], 31 | "engines": { 32 | "vscode": "^1.67.0" 33 | }, 34 | "activationEvents": [ 35 | "*" 36 | ], 37 | "markdown": "github", 38 | "main": "./dist/extension.js", 39 | "browser": "./dist/web/extension.js", 40 | "contributes": { 41 | "configuration": { 42 | "title": "Sort Editors", 43 | "properties": { 44 | "sortEditors.sortEditorsAutomatically": { 45 | "type": "boolean", 46 | "default": true, 47 | "title": "Sort Editors Automatically", 48 | "description": "Enable to automatically sort the editors when they are opened." 49 | }, 50 | "sortEditors.sortEditorsOrder": { 51 | "type": "string", 52 | "default": "fullpath", 53 | "title": "Sort Order", 54 | "description": "Controls the sorting order of opened editors.", 55 | "enum": [ 56 | "fullpath", 57 | "alphabetical" 58 | ], 59 | "enumDescriptions": [ 60 | "Editors are ordered alphabetically by fullpath inside each editor group.", 61 | "Editors are ordered alphabetically by tab name inside each editor group." 62 | ] 63 | } 64 | } 65 | }, 66 | "commands": [ 67 | { 68 | "command": "sortEditors.enableAutoSorting", 69 | "title": "Enable Automatic Sorting", 70 | "category": "Sort Editors", 71 | "enablement": "!config.sortEditors.sortEditorsAutomatically" 72 | }, 73 | { 74 | "command": "sortEditors.disableAutoSorting", 75 | "title": "Disable Automatic Sorting", 76 | "category": "Sort Editors", 77 | "enablement": "config.sortEditors.sortEditorsAutomatically" 78 | }, 79 | { 80 | "command": "sortEditors.sortActiveEditor", 81 | "title": "Sort Active Editor", 82 | "category": "Sort Editors", 83 | "enablement": "activeEditor && !activeEditorIsPinned" 84 | }, 85 | { 86 | "command": "sortEditors.sortActiveTabEditors", 87 | "title": "Sort Opened Editors", 88 | "category": "Sort Editors", 89 | "enablement": "!activeEditorGroupEmpty" 90 | }, 91 | { 92 | "command": "sortEditors.sortAllEditors", 93 | "title": "Sort Opened Editors in All Groups", 94 | "category": "Sort Editors", 95 | "enablement": "multipleEditorGroups" 96 | } 97 | ], 98 | "menus": { 99 | "editor/title": [ 100 | { 101 | "command": "sortEditors.sortActiveTabEditors", 102 | "group": "sortEditors", 103 | "when": "editorFocus" 104 | } 105 | ] 106 | } 107 | }, 108 | "scripts": { 109 | "vscode:prepublish": "npm run package", 110 | "compile": "webpack", 111 | "watch": "webpack --watch", 112 | "package": "webpack --mode production --devtool hidden-source-map", 113 | "compile-tests": "tsc -p . --outDir out", 114 | "watch-tests": "tsc -p . -w --outDir out", 115 | "pretest": "npm run compile-tests && npm run compile && npm run lint", 116 | "lint": "eslint src --ext ts", 117 | "test": "node ./out/test/runTest.js", 118 | "vscode:prepublish-web": "npm run package-web", 119 | "compile-web": "webpack", 120 | "watch-web": "webpack --watch", 121 | "package-web": "webpack --mode production --devtool hidden-source-map" 122 | }, 123 | "devDependencies": { 124 | "@types/glob": "^7.2.0", 125 | "@types/mocha": "^9.1.1", 126 | "@types/node": "14.x", 127 | "@types/vscode": "^1.67.0", 128 | "@typescript-eslint/eslint-plugin": "^5.21.0", 129 | "@typescript-eslint/parser": "^5.21.0", 130 | "@vscode/test-electron": "^2.1.3", 131 | "eslint": "^8.14.0", 132 | "glob": "^8.0.1", 133 | "mocha": "^9.2.2", 134 | "path-browserify": "^1.0.1", 135 | "process": "^0.11.10", 136 | "ts-loader": "^9.2.8", 137 | "typescript": "^4.6.4", 138 | "webpack": "^5.70.0", 139 | "webpack-cli": "^4.9.2" 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/configuration.ts: -------------------------------------------------------------------------------- 1 | import { workspace } from 'vscode'; 2 | import { Configuration } from './interfaces'; 3 | 4 | export const CONFIG: Configuration = {}; 5 | 6 | export function setConfiguration(): void { 7 | const getConfig = workspace.getConfiguration('sortEditors'); 8 | 9 | Object.assign(CONFIG, { 10 | sortEditorsAutomatically: getConfig.get('sortEditorsAutomatically'), 11 | sortEditorsOrder: getConfig.get('sortEditorsOrder') 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const lastSeenVersionKey = 'lastSeenVersion'; 2 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import { 2 | commands, ConfigurationTarget, Disposable, ExtensionContext, TabInputText, window, workspace 3 | } from 'vscode'; 4 | 5 | import { CONFIG, setConfiguration } from './configuration'; 6 | import { sortActiveEditor, sortAllOpenedEditors } from './sorter'; 7 | import { promptToShowReleaseNotes } from './utils'; 8 | 9 | let autoSortDisposable: Disposable | undefined; 10 | 11 | // this method is called when your extension is activated 12 | export function activate(context: ExtensionContext) { 13 | 14 | // Set configuration 15 | setConfiguration(); 16 | 17 | applyAutoSorting(); 18 | 19 | function applyAutoSorting() { 20 | const config = CONFIG.sortEditorsAutomatically; 21 | if (config === true) { 22 | autoSortDisposable = window.tabGroups.onDidChangeTabs(({ opened }) => { 23 | const openedEditors = opened.filter(t => t.input instanceof TabInputText).map(t => (t.input as TabInputText).uri.path); 24 | openedEditors.length && sortActiveEditor(openedEditors); 25 | }); 26 | context.subscriptions.push(autoSortDisposable); 27 | } else if (autoSortDisposable) { 28 | autoSortDisposable.dispose(); 29 | } 30 | } 31 | 32 | context.subscriptions.push(...[ 33 | commands.registerCommand('sortEditors.sortActiveEditor', () => { 34 | window.activeTextEditor && sortActiveEditor([window.activeTextEditor.document.uri.path]); 35 | }), 36 | 37 | commands.registerCommand('sortEditors.sortActiveTabEditors', () => { 38 | sortAllOpenedEditors([window.tabGroups.activeTabGroup]); 39 | }), 40 | 41 | commands.registerCommand('sortEditors.sortAllEditors', () => { 42 | sortAllOpenedEditors(window.tabGroups.all); 43 | }), 44 | 45 | commands.registerCommand('sortEditors.enableAutoSorting', () => { 46 | toggleAutoSorting(true); 47 | }), 48 | 49 | commands.registerCommand('sortEditors.disableAutoSorting', () => { 50 | toggleAutoSorting(false); 51 | }) 52 | ]); 53 | 54 | function toggleAutoSorting(value: boolean) { 55 | const config = workspace.getConfiguration('sortEditors'); 56 | config.update('sortEditorsAutomatically', value, ConfigurationTarget.Global); 57 | } 58 | 59 | // Subscribe to configuration change 60 | workspace.onDidChangeConfiguration(e => { 61 | if (e.affectsConfiguration('sortEditors.sortEditorsAutomatically')) { 62 | setConfiguration(); 63 | applyAutoSorting(); 64 | autoSortingToggled(); 65 | } else if (e.affectsConfiguration('sortEditors.sortEditorsOrder')) { 66 | setConfiguration(); 67 | sortingOrderChanged(); 68 | } 69 | }); 70 | 71 | function autoSortingToggled() { 72 | const config = CONFIG.sortEditorsAutomatically; 73 | if (config === true && window.tabGroups.activeTabGroup.tabs.length > 0) { 74 | window.showInformationMessage('Automatic Sorting is Enabled. Do you want to sort opened editors?', 'Yes', 'No') 75 | .then(answer => { answer === 'Yes' && sortAllOpenedEditors([window.tabGroups.activeTabGroup]); }); 76 | return; 77 | } 78 | 79 | window.showInformationMessage(`Automatic Sorting is ${config ? 'Enabled' : 'Disabled'}`); 80 | } 81 | 82 | function sortingOrderChanged() { 83 | const sortOrder = CONFIG.sortEditorsOrder; 84 | if (window.tabGroups.activeTabGroup.tabs.length > 0) { 85 | window.showInformationMessage(`Sorting order changed to ${sortOrder}. Do you want to sort opened editors?`, 'Yes', 'No') 86 | .then(answer => { answer === 'Yes' && sortAllOpenedEditors([window.tabGroups.activeTabGroup]); }); 87 | return; 88 | } 89 | 90 | window.showInformationMessage(`Sorting order changed to ${sortOrder}`); 91 | } 92 | 93 | // Prompt to show release notes on extension updated 94 | promptToShowReleaseNotes(context); 95 | } 96 | 97 | // this method is called when your extension is deactivated 98 | export function deactivate() { 99 | if (autoSortDisposable) { 100 | autoSortDisposable.dispose(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface Configuration { 2 | sortEditorsAutomatically?: boolean, 3 | sortEditorsOrder?: string 4 | } 5 | -------------------------------------------------------------------------------- /src/sorter.ts: -------------------------------------------------------------------------------- 1 | import { basename } from 'path'; 2 | import { 3 | commands, ProgressLocation, ProgressOptions, TabGroup, TabInputText, window, workspace 4 | } from 'vscode'; 5 | 6 | import { CONFIG } from './configuration'; 7 | 8 | const collator = new Intl.Collator(undefined, { 9 | numeric: true, 10 | sensitivity: 'base', 11 | ignorePunctuation: true 12 | }); 13 | 14 | const progressOptions: ProgressOptions = { 15 | location: ProgressLocation.Notification, 16 | title: 'Sorting editors', 17 | cancellable: true 18 | }; 19 | 20 | function sortTabGroupEditors(tabGroups: readonly TabGroup[]) { 21 | const formatPath = (a: string) => CONFIG.sortEditorsOrder === 'alphabetical' ? basename(a) : a; 22 | 23 | return tabGroups.flatMap(g => { 24 | return g.tabs 25 | .filter(t => t.input instanceof TabInputText) 26 | .map(t => ({ ...t, uri: (t.input as TabInputText).uri })) 27 | .sort((a, b) => b.isPinned ? 1 : collator.compare(formatPath(a.uri.path), formatPath(b.uri.path))); 28 | }); 29 | } 30 | 31 | export async function sortActiveEditor(openedEditors: Array) { 32 | const sortedEditors = sortTabGroupEditors([window.tabGroups.activeTabGroup]); 33 | 34 | for (let i = 0; i < sortedEditors.length; i++) { 35 | if (openedEditors.includes(sortedEditors[i].uri.path)) { 36 | try { 37 | await commands.executeCommand('moveActiveEditor', { to: 'position', value: i + 1 }); 38 | } catch (error: any) { 39 | window.showErrorMessage(error.message ?? 'Unknown Exception'); 40 | } 41 | break; 42 | } 43 | } 44 | } 45 | 46 | export async function sortAllOpenedEditors(tabGroups: readonly TabGroup[]) { 47 | const sortedEditors = sortTabGroupEditors(tabGroups); 48 | const lastActiveEditor = window.activeTextEditor; 49 | const increment = 100 / sortedEditors.length; 50 | 51 | await window.withProgress(progressOptions, async (progress, cancellationToken) => { 52 | for (let i = 0; i < sortedEditors.length; i++) { 53 | if (cancellationToken.isCancellationRequested) { 54 | break; 55 | } 56 | 57 | progress.report({ message: `${i + 1}/${sortedEditors.length}` }); 58 | 59 | if (sortedEditors[i].isPinned === false) { 60 | try { 61 | await window.showTextDocument(sortedEditors[i].uri, { preview: false, viewColumn: sortedEditors[i].group.viewColumn }); 62 | await commands.executeCommand('moveActiveEditor', { to: 'position', value: i + 1 }); 63 | } catch (error: any) { 64 | window.showErrorMessage(error.message ?? 'Unknown Exception'); 65 | } 66 | } 67 | 68 | progress.report({ increment }); 69 | } 70 | }); 71 | 72 | if (lastActiveEditor) { 73 | try { 74 | await window.showTextDocument(lastActiveEditor.document, { viewColumn: lastActiveEditor.viewColumn }); 75 | } catch (error: any) { 76 | window.showErrorMessage(error.message ?? 'Unknown Exception'); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from '@vscode/test-electron'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { env, ExtensionContext, Uri, window } from 'vscode'; 2 | import * as CONSTANT from './constants'; 3 | 4 | const packageJson = require('../package.json'); 5 | 6 | export function promptToShowReleaseNotes(context: ExtensionContext): void { 7 | const currentVersion = getExtensionVersion(); 8 | const lastSeenVersion = context.globalState.get(CONSTANT.lastSeenVersionKey, ''); 9 | 10 | if (!versionIsNewer(lastSeenVersion, currentVersion)) { 11 | return; 12 | } 13 | 14 | context.globalState.update(CONSTANT.lastSeenVersionKey, currentVersion); 15 | 16 | window.showInformationMessage( 17 | `Sort Editors extension has been updated to v${currentVersion}. Please check the changelog and star on Github`, 18 | `Visit Github`, 19 | ).then(() => { 20 | const uri = Uri.parse(packageJson.repository.url); 21 | env.openExternal(uri); 22 | }); 23 | } 24 | 25 | export function versionIsNewer(oldVersion: string, newVersion: string): boolean { 26 | return !!newVersion.localeCompare(oldVersion, undefined, { 27 | numeric: true, 28 | sensitivity: 'base' 29 | }); 30 | } 31 | 32 | export function getExtensionVersion(): string { 33 | return packageJson.version; 34 | } 35 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2020", 5 | "lib": [ 6 | "ES2020" 7 | ], 8 | "sourceMap": true, 9 | "rootDir": "src", 10 | "strict": true /* enable all strict type-checking options */ 11 | /* Additional Checks */ 12 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 13 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 14 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | const webpack = require('webpack'); 7 | 8 | //@ts-check 9 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 10 | 11 | /** @type WebpackConfig */ 12 | const extensionConfig = { 13 | target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 14 | mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 15 | 16 | entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 17 | output: { 18 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 19 | path: path.resolve(__dirname, 'dist'), 20 | filename: 'extension.js', 21 | libraryTarget: 'commonjs2' 22 | }, 23 | externals: { 24 | vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 25 | // modules added here also need to be added in the .vscodeignore file 26 | }, 27 | resolve: { 28 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 29 | extensions: ['.ts', '.js'] 30 | }, 31 | module: { 32 | rules: [ 33 | { 34 | test: /\.ts$/, 35 | exclude: /node_modules/, 36 | use: [ 37 | { 38 | loader: 'ts-loader' 39 | } 40 | ] 41 | } 42 | ] 43 | }, 44 | devtool: 'nosources-source-map', 45 | infrastructureLogging: { 46 | level: "log", // enables logging required for problem matchers 47 | }, 48 | }; 49 | 50 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 51 | /** @type WebpackConfig */ 52 | const webExtensionConfig = { 53 | mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 54 | target: 'webworker', // extensions run in a webworker context 55 | entry: { 56 | extension: './src/extension.ts' 57 | }, 58 | output: { 59 | filename: '[name].js', 60 | path: path.join(__dirname, './dist/web'), 61 | libraryTarget: 'commonjs', 62 | devtoolModuleFilenameTemplate: '../../[resource-path]' 63 | }, 64 | resolve: { 65 | mainFields: ['browser', 'module', 'main'], // look for `browser` entry point in imported node modules 66 | extensions: ['.ts', '.js'], // support ts-files and js-files 67 | alias: { 68 | // provides alternate implementation for node module and source files 69 | }, 70 | fallback: { 71 | // Webpack 5 no longer polyfills Node.js core modules automatically. 72 | // see https://webpack.js.org/configuration/resolve/#resolvefallback 73 | // for the list of Node.js core module polyfills. 74 | assert: require.resolve('assert'), 75 | path: require.resolve('path-browserify'), 76 | } 77 | }, 78 | module: { 79 | rules: [ 80 | { 81 | test: /\.ts$/, 82 | exclude: /node_modules/, 83 | use: [ 84 | { 85 | loader: 'ts-loader' 86 | } 87 | ] 88 | } 89 | ] 90 | }, 91 | plugins: [ 92 | new webpack.ProvidePlugin({ 93 | process: 'process/browser' // provide a shim for the global `process` variable 94 | }) 95 | ], 96 | externals: { 97 | vscode: 'commonjs vscode' // ignored because it doesn't exist 98 | }, 99 | performance: { 100 | hints: false 101 | }, 102 | devtool: 'nosources-source-map' // create a source map that points to the original source file 103 | }; 104 | 105 | module.exports = [extensionConfig, webExtensionConfig]; --------------------------------------------------------------------------------