├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── ask-for-help.md
│ ├── bug_report.md
│ └── feature_request.md
└── templates.md
├── .gitignore
├── .jsbeautifyrc
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── _config.yml
├── icons
├── activity_bar_icon.svg
└── favorites.png
├── images
├── add_dark.svg
├── add_light.svg
├── collapse_dark.svg
├── collapse_light.svg
├── collapsevs_dark.svg
├── delete_all_dark.svg
├── delete_all_light.svg
├── group_dark.svg
├── group_light.svg
├── icon-small.png
├── refresh_dark.svg
├── refresh_light.svg
├── sort_dark_az.svg
├── sort_dark_za.svg
├── sort_light_az.svg
└── sort_light_za.svg
├── media
└── icon.svg
├── package-lock.json
├── package.json
├── preview
├── adding-favorite.jpg
├── adding.jpg
├── browsing.gif
├── operations.jpg
├── preview-promo.gif
├── selectRegistry.jpg
├── statusRegistry.jpg
└── using.jpg
├── src
├── class
│ ├── clipboard.ts
│ ├── dataProvider.ts
│ ├── exclude-check.ts
│ ├── favorites-browser.ts
│ ├── favorites-navigator.ts
│ ├── favorites.ts
│ ├── filesystem.ts
│ ├── fs-watcher.ts
│ ├── global.ts
│ ├── group-color.ts
│ ├── storage.ts
│ ├── tree.ts
│ ├── view-item.ts
│ └── workspace.ts
├── command
│ └── index.ts
├── index.ts
└── types
│ ├── index.ts
│ └── trash.d.ts
├── tsconfig.json
├── tslint.json
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 4
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/ask-for-help.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Ask for help
3 | about: Get help using extension
4 |
5 | ---
6 |
7 | **Describe your use case.**
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | Description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | What you expected to happen.
19 |
20 | **Screenshots**
21 | If it will help describe behaviour, add screenshots to help explain your problem.
22 |
23 | **System info (please complete the following information):**
24 | - OS version: [e.g. Windows 10, Debian 9, MacOS 10.14.1]
25 | - Favorites extension version
26 | - vscode version
27 |
28 | **Additional info**
29 | Add any other info about the problem here.
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
--------------------------------------------------------------------------------
/.github/templates.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/.github/templates.md
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | packed
3 | node_modules
4 | .DS_Store
5 | promo/**
6 | svg-generated/**
7 | .favorites.json
8 |
--------------------------------------------------------------------------------
/.jsbeautifyrc:
--------------------------------------------------------------------------------
1 | {
2 | "indent_inner_html": true,
3 | "indent_size": 4,
4 | "css": {
5 | "indent_size": 4,
6 | "newline_between_rules": true
7 | },
8 | "html": {
9 | "wrap_attributes": "force-aligned"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | {
3 | "version": "0.1.0",
4 | "configurations": [
5 | {
6 | "name": "Extension",
7 | "type": "extensionHost",
8 | "request": "launch",
9 | "runtimeExecutable": "${execPath}",
10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"],
11 | "stopOnEntry": false,
12 | "sourceMaps": true,
13 | "outFiles": ["${workspaceRoot}/packed/**/*.js"],
14 | // "preLaunchTask": "npm: webpack"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "**/.git": true,
5 | "**/.svn": true,
6 | "**/.hg": true,
7 | "**/CVS": true,
8 | "**/.DS_Store": true,
9 | // "out": true,
10 | // "node_modules": true,
11 | ".vscode": true
12 | }
13 | // "typescript.referencesCodeLens.enabled": true
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/.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": "$tsc-watch",
10 | "isBackground": true,
11 | "presentation": {
12 | "reveal": "never"
13 | },
14 | "group": {
15 | "kind": "build",
16 | "isDefault": true
17 | }
18 | },
19 | {
20 | "type": "npm",
21 | "script": "webpack",
22 | "problemMatcher": []
23 | },
24 | {
25 | "type": "npm",
26 | "script": "webpack-dev",
27 | "problemMatcher": [
28 | "$tsc-watch"
29 | ],
30 | "isBackground": true
31 | }
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | .github/**
3 | node_modules
4 | !node_modules/arch
5 | !node_modules/clipboardy
6 | !node_modules/cross-spawn
7 | !node_modules/execa
8 | !node_modules/get-stream
9 | !node_modules/is-stream
10 | !node_modules/isexe
11 | !node_modules/lru-cache
12 | !node_modules/nice-try
13 | !node_modules/npm-run-path
14 | !node_modules/p-finally
15 | !node_modules/path-key
16 | !node_modules/pseudomap
17 | !node_modules/shebang-command
18 | !node_modules/shebang-regex
19 | !node_modules/signal-exit
20 | !node_modules/strip-eof
21 | !node_modules/semver
22 | !node_modules/which
23 | !node_modules/yallist
24 | out/**
25 | test/**
26 | src/**
27 | promo/**
28 | preview/**
29 | svg-generated/**
30 | **/*.map
31 | .gitignore
32 | tsconfig.json
33 | .jsbeautifyrc
34 | .editorconfig
35 | tslint.json
36 | media/**
37 | webpack.config.js
38 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 2.4.6 | 2022/09/15
2 |
3 | - enabled the "Add to Favorites" and "Add to Favorites Group" context menu items in additional context menus such as when right clicking an open file tab, or in the text of an open editor.
4 |
5 |
6 | ## 2.4.5 | 2018/11/29
7 |
8 | - fixed vulnerability issue
9 |
10 | ## 2.4.4 | 2018/11/25
11 |
12 | - repo rename
13 |
14 | ## 2.4.3 | 2018/11/18
15 |
16 | - added browsing using keyboard only (mouseless) of favorites via palette `Favorites: Browse`. [#31](https://github.com/kdcro101/vscode-favorites/issues/31)
17 |
18 | ## 2.3.4 | 2018/11/16
19 |
20 | - fixed `Copy path` not working with webpack
21 |
22 | ## 2.3.2 | 2018/11/16
23 |
24 | - extension packed using `webpack.js` for faster loading
25 | - fixed `duplicate` file when inside group
26 |
27 | ## 2.3.1 | 2018/11/04
28 |
29 | - README
30 |
31 | ## 2.3.0 | 2018/11/03
32 |
33 | - added support for multiple/switchable favorite storage per workspace [#28](https://github.com/kdcro101/vscode-favorites/issues/28)
34 | - fixed adding file from editor using command `Favorites: Add to group of favorites` from palette [#26](https://github.com/kdcro101/vscode-favorites/issues/26)
35 |
36 | ## 2.2.5 | 2018/10/29
37 |
38 | - merging update observables
39 |
40 | ## 2.2.4 | 2018/10/27
41 |
42 | - unsubscribe observables on extension deactivation
43 |
44 | ## 2.2.2 | 2018/10/26
45 |
46 | - fixed creating `.vscode/setting.json` when folder gets opened [#25](https://github.com/kdcro101/vscode-favorites/issues/25)
47 |
48 | ## 2.2.1 | 2018/10/26
49 |
50 | - Favorites refresh on FileSystemWatcher events [#24](https://github.com/kdcro101/vscode-favorites/issues/24)
51 | - added `refresh` button
52 |
53 | ## 2.2.0 | 2018/10/24
54 |
55 | - fixed "Remove everything from favorites"
56 | - added configuration `favorites.useWorkspace`
57 | - added workspace folder selection for storage of favorites
58 |
59 | ## 2.1.5 | 2018/10/22
60 |
61 | - fixed `Path must be string. Received null` error [#20](https://github.com/kdcro101/vscode-favorites/issues/20)
62 |
63 | ## 2.1.2 | 2018/10/21
64 |
65 | - added `Copy Name` to copy file name
66 | - added support for `files.exclude`
67 |
68 | ## 2.0.5 | 2018/10/18
69 |
70 | - Icon + Marketplace banner
71 | - Favorites items are stored, by default, in workspace root as `favorites.json` In case of multiroot workspace, file is located in first workspace. [#13](https://github.com/kdcro101/vscode-favorites/issues/13) [#18](https://github.com/kdcro101/vscode-favorites/issues/18) [#19](https://github.com/kdcro101/vscode-favorites/issues/19)
72 | - added configuration item `favorites.storageFilePath` to overrride storage file path relative to workspace. Default is `.favorites.json`
73 |
74 | ## 2.0.1 | 2018/10/17
75 |
76 | - Favorites items are stored in `.vscode/favorites.json`. In case of multiroot workspace, file is located in first workspace. [#13](https://github.com/kdcro101/vscode-favorites/issues/13) [#18](https://github.com/kdcro101/vscode-favorites/issues/18)
77 |
78 | ## 2.0.0 | 2018/10/17
79 |
80 | - Favorites items are stored in workspace root as file `.favorites.json`. In case of multiroot workspace, file is located in first workspace. [#13](https://github.com/kdcro101/vscode-favorites/issues/13)
81 |
82 |
83 | ## 1.9.10 | 2018/10/15
84 |
85 | - Marketplace icon + activity bar icon
86 |
87 | ## 1.9.9 | 2018/10/11
88 |
89 | - Fix favorites explorer focusing on acitve editor change. [#16](https://github.com/kdcro101/vscode-favorites/issues/16)
90 |
91 | ## 1.9.8 | 2018/10/09
92 |
93 | - README
94 | - `Add to favorites` executed from command palette adds active document to favorites root
95 |
96 | ## 1.9.7 | 2018/09/25
97 |
98 | - favorites.useTrash -> README
99 | - remove obsolete code
100 |
101 | ## 1.9.6 | 2018/09/19
102 |
103 | - Colored icons for groups are generated when needed if not exists
104 |
105 | ## 1.9.5 | 2018/09/18
106 |
107 | - activity bar icon name change (caching problem - full restart was required)
108 | - README
109 |
110 | ## 1.9.3 | 2018/09/18
111 |
112 | - remove unicode characters from context menu when adding to favorites [#11](https://github.com/kdcro101/vscode-favorites/issues/11)
113 | - change activity bar icon
114 | - README
115 |
116 | ## 1.9.2 | 2018/09/15
117 |
118 | - change prompt to include `PERMANENTLY` when deleting from file system
119 |
120 | ## 1.9.1 | 2018/09/12
121 |
122 | - Fix reporting of "Unable to identify path" when adding file/directory that is already in favorites
123 |
124 | ## 1.9.0 | 2018/09/08
125 |
126 | - Copy Path
127 |
128 | ## 1.8.0 | 2018/08/30
129 |
130 | - Trash support
131 |
132 | ## 1.7.3 | 2018/08/21
133 |
134 | - view/item context menu fix
135 |
136 | ## 1.7.2 | 2018/08/18
137 |
138 | - code cleanup
139 |
140 | ## 1.7.1 | 2018/08/16
141 |
142 | - file uri fix for external resources
143 |
144 | ## 1.7.0 | 2018/08/15
145 |
146 | - minor bugs
147 | - create group in root from context menu (right click on empty area)
148 | - external resources (files/directories out of workspace) can be added
149 | - minor context menu reordering
150 |
151 | ## 1.6.0 | 2018/08/08
152 |
153 | - reveal file in "favorites" view (if visible) when active editor changes
154 |
155 | ## 1.5.5 | 2018/08/06
156 |
157 | - fs.move -> fs.rename
158 |
159 | ## 1.5.4 | 2018/08/06
160 |
161 | - minor bugs
162 |
163 | ## 1.5.3 | 2018/08/06
164 |
165 | - fs-extra 7.0.0 upgrade
166 |
167 | ## 1.5.2 | 2018/07/13
168 |
169 | - "window.titleBarStyle": "custom" - temporary fix
170 |
171 | ## 1.5.1 | 2018/07/10
172 |
173 | - minor bugs
174 |
175 | ## 1.5.0 | 2018/07/10
176 |
177 | - multiple selection can be added to favorites
178 |
179 | ## 1.4.4 | 2018/05/25
180 |
181 | - fixed file creation bug when user cancel file name input
182 |
183 | ## 1.4.3 | 2018/05/09
184 |
185 | - activationEvents
186 | - fixed command labels
187 |
188 | ## 1.4.2 | 2018/05/09
189 |
190 | - icon
191 | - readme update
192 |
193 | ## 1.4.1 | 2018/05/09
194 |
195 | - fixed bug that may corrupt current favorites data
196 | - remove all icon changed
197 | - missing command labels fixed
198 |
199 | ## 1.4.0 | 2018/05/09
200 |
201 | - groups colors can be changed
202 | - filesystem operations in favorites view
203 | - copy/cut/paste operations in favorites view
204 |
205 | ## 1.3.1 | 2018/05/08
206 |
207 | - minor bugs
208 | - README update
209 |
210 | ## 1.3.0 | 2018/05/08
211 |
212 | - renaming group
213 | - alias for favorite items (custom label)
214 | - activity bar contribution
215 | - minimum vscode version = 1.23.0
216 |
217 | ## 1.2.1 | 2018/05/07
218 |
219 | - minor bugs
220 |
221 | ## 1.2.0 | 2018/05/02
222 |
223 | - added subgroup support
224 |
225 |
226 | ## 1.0.3 | 2018/03/30
227 |
228 | - icon change
229 |
230 | ## 1.0.2 | 2018/03/29
231 |
232 | - minor bugs
233 | - README
234 |
235 | ## 1.0.0 | 2018/03/24
236 |
237 | - added option to delete everything
238 |
239 | ## 0.1.8 | 2018/03/22
240 |
241 | - fixed `delete item` menu showing on file system item
242 |
243 | ## 0.1.4 | 2018/03/21
244 |
245 | ## 0.1.3 | 2018/03/21
246 |
247 | - reduce extension size
248 |
249 | ## 0.1.2 | 2018/03/20
250 |
251 | - update minimum engine to `^1.13.0`
252 |
253 | ## 0.1.1 | 2018/03/19
254 |
255 | - fixed badges link
256 |
257 | ## 0.1.0 | 2018/03/19
258 |
259 | - added ability to group favorites
260 |
261 |
262 | ## 0.0.5 | 2018/03/19
263 |
264 | - initial commit
265 | - collapse function
266 | - fixed sorting
267 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018
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 | # Favorites
2 |
3 | [](https://marketplace.visualstudio.com/items?itemName=kdcro101.favorites)
4 | [](https://marketplace.visualstudio.com/items?itemName=kdcro101.favorites)
5 | [](https://marketplace.visualstudio.com/items?itemName=kdcro101.favorites#review-details)
6 |
7 |
8 | Add files and directories to **workspace** favorites. You can create groups (and subgroups) of favorite items with files and folders.
9 | Time saver for complex projects.
10 |
11 |
12 | ## Features
13 |
14 | - [browsing using keyboard](#keyboard-browsing) only via `Favorites: Browse` palette command
15 | - add resources within workspace
16 | - add external resources (files or directories out of workspace)
17 | - organize favorites in groups and subgroups (nesting not limited)
18 | - have [multiple sets](#multiple-sets) of favorites, per workspace, depending of context, using setting `favorites.storageRegistry`
19 | - group icons can have their color changed
20 | - basic file system operations within Favorites explorer:
21 | - copy/cut -> paste
22 | - create file
23 | - delete file/directory
24 | - rename file/directory
25 | - duplicate file/directory
26 | - favorite items can have alias (different label)
27 | - items are accesible via activity bar and, optionally, as File explorer subview (see [Using Favorites explorer section](#using))
28 | - `files.exclude` supported (see [configuration](#configuration))
29 | - language independent
30 |
31 |
32 | ## Adding to favorites
33 |
34 |
35 |
36 |
37 | - to add file or directory to favorites, right-click item in ***File explorer*** and select:
38 | - `add to favorites` - to add item to root of favorites tree.
39 | - `add to group of favorites` - to add item to group of favorites you previously created.
40 |
41 |
42 |
43 | ## Using Favorites explorer
44 |
45 |
46 |
47 |
48 |
49 | you can **turn off** subview in **File explorer** by clicking on its header and deselecting it.
50 |
51 |
52 | ## Install
53 |
54 | Open Visual Studio Code press CTRL+p and type or copy and paste:
55 |
56 | `ext install kdcro101.favorites`
57 |
58 |
59 | ## Configuration
60 |
61 | `favorites.useWorkspace` : number - default is `0`
62 | - index of workspace to use as root when composing storage file path
63 |
64 | `favorites.useFilesExclude` : boolean
65 | - should `files.exclude` setting be used. Default is `true`
66 |
67 | `favorites.storageFilePath` : string
68 | - overrride storage file path relative to workspace. Default is `.favorites.json`
69 |
70 | `favorites.storageRegistry` : string[]
71 | - List of storage file paths relative to workspace to make available for switching using command `Favorites: Select alternative storage from registry`. ([multiple sets](#multiple-sets)). Default is `[]`
72 |
73 | `favorites.groupsFirst` : boolean
74 | - if set to `true`, groups will be listed before directories and files, if `false`, groups will appear after directories and files.
75 |
76 | `favorites.sortDirection ` : string, `ASC` or `DESC`
77 |
78 | `favorites.useTrash`: boolean (default `false`)
79 | - if set to `true`, extension will try to use system trash when resource (file or directory is deleted)
80 |
81 | `favorites.includeInDocumentBodyContextMenu` : boolean (default `false`)
82 | - if set to `true`, the two "Add to * favorites" commands will be included in the editor context menu that appears when right-clicking the body of an open document.
83 |
84 | `favorites.includeInEditorTabContextMenu` : boolean (default `true`)
85 | - if set to `true`, the two "Add to * favorites" commands will be included in the context menu for the tab of a specific file (e.g. the menu that appears when right-clicking the tab of an open document).
86 |
87 | ## Keyboard browsing
88 |
89 | You can browse favorites using **keyboard only** by executing command `Favorites: Browse` command from palette. Assign keyboard shortcut if needed.
90 |
91 |
92 |
93 |
94 |
95 | ## Usage
96 |
97 | #### Adding to favorites
98 | Right-click item in File explorer, an open file tab, or the background of an open editor and select `Add to favorites`.
99 | #### Adding to favorites group or subgroup
100 | Right-click item in File explorer, an open file tab, or the background of an open editor and select `Add to favorites group`, then select group from list.
101 | #### Removing from favorites
102 | Right-click item in Favorites view and select `Remove from favorites`
103 | #### Create favorites group
104 | Right-click on empty area and select `Create group`
105 | #### Create favorites subgroup
106 | Right-click on group item and choose `Create group`
107 | #### Delete favorites group
108 | Right-click group item in Favorites view and select `Remove group`
109 | #### Remove everything from favorites
110 | Click on trash bin icon on Favorites view title, type "yes" to confirm
111 |
112 | ## Multiple sets
113 |
114 | You can have multiple sets of favorites per workspace. This allows you to build independent set of favorites, depending on context.
115 | To achieve this you need to setup storage registry.
116 |
117 | Add favorites.storageRegistry to your workspace settings, for example:
118 | ```ts
119 | // paths are relative to workspace
120 | "favorites.storageRegistry": [
121 | "favorites/system.json",
122 | "favorites/classes.json",
123 | "favorites/services.json",
124 | ],
125 | ```
126 |
127 | Select active storage file from registry by clicking status bar element  or by executing command `Favorites: Select alternative storage from registry` from command palette and then selecting item from list:
128 |
129 | 
130 |
131 | All `add favorite` operations will be written in currently selected storage file.
132 |
133 |
134 | ## LICENSE
135 |
136 | [GPL v3 License](https://raw.githubusercontent.com/kdcro101/vscode-favorites/master/LICENSE)
137 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/icons/activity_bar_icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/icons/favorites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/icons/favorites.png
--------------------------------------------------------------------------------
/images/add_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | image/svg+xml
27 |
--------------------------------------------------------------------------------
/images/add_light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | image/svg+xml
--------------------------------------------------------------------------------
/images/collapse_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/images/collapse_light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/images/collapsevs_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/images/delete_all_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/images/delete_all_light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/images/group_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/images/group_light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/images/icon-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/images/icon-small.png
--------------------------------------------------------------------------------
/images/refresh_dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/images/refresh_light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/images/sort_dark_az.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Layer 1
5 |
6 |
7 |
--------------------------------------------------------------------------------
/images/sort_dark_za.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/images/sort_light_az.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Layer 1
5 |
6 |
7 |
--------------------------------------------------------------------------------
/images/sort_light_za.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/media/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
42 |
49 |
56 |
63 |
70 |
77 |
78 |
80 |
81 |
83 | image/svg+xml
84 |
86 |
87 |
88 |
89 |
90 |
95 |
99 |
106 |
111 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "favorites",
3 | "displayName": "Favorites",
4 | "description": "Add files and directories to workspace favorites. Create groups of directories and files. Time saver for complex projects.",
5 | "version": "2.4.6",
6 | "categories": [
7 | "Other"
8 | ],
9 | "keywords": [
10 | "bookmarks",
11 | "favorites",
12 | "groups",
13 | "favorite files",
14 | "favorite directories"
15 | ],
16 | "publisher": "kdcro101",
17 | "engines": {
18 | "vscode": "^1.29.0"
19 | },
20 | "galleryBanner": {
21 | "color": "#283593",
22 | "theme": "dark"
23 | },
24 | "scripts": {
25 | "vscode:prepublish": "webpack --mode production",
26 | "webpack": "webpack --mode development",
27 | "webpack-dev": "webpack --mode development --watch",
28 | "postinstall": "node ./node_modules/vscode/bin/install"
29 | },
30 | "icon": "icons/favorites.png",
31 | "bugs": {
32 | "url": "https://github.com/kdcro101/vscode-favorites/issues"
33 | },
34 | "homepage": "https://github.com/kdcro101/vscode-favorites/blob/master/README.md",
35 | "repository": {
36 | "type": "git",
37 | "url": "https://github.com/kdcro101/vscode-favorites.git"
38 | },
39 | "activationEvents": [
40 | "*"
41 | ],
42 | "license": "GPL-3.0",
43 | "main": "./packed/",
44 | "contributes": {
45 | "keybindings": [
46 | {
47 | "command": "workbench.view.extension.favorites-explorer",
48 | "key": "ctrl+alt+f",
49 | "mac": "cmd+alt+f",
50 | "when": "editorTextFocus"
51 | }
52 | ],
53 | "configuration": {
54 | "type": "object",
55 | "title": "Favorite-items configuration",
56 | "properties": {
57 | "favorites.useTrash": {
58 | "type": "boolean",
59 | "default": false,
60 | "description": "Move to trash when resource gets deleted"
61 | },
62 | "favorites.useWorkspace": {
63 | "type": "number",
64 | "default": 0,
65 | "description": "Index of workspace where to look for storage file"
66 | },
67 | "favorites.useFilesExclude": {
68 | "type": "boolean",
69 | "default": true,
70 | "description": "Should 'files.exclude' setting be used "
71 | },
72 | "favorites.storageFilePath": {
73 | "type": "string",
74 | "default": ".favorites.json",
75 | "description": "Storage file path relative to workspace"
76 | },
77 | "favorites.storageRegistry": {
78 | "type": "array",
79 | "default": [],
80 | "description": "List of alternative storage paths relative to workspace",
81 | "items": {
82 | "type": "string",
83 | "title": "Path",
84 | "description": "Path to alternative storage .json file"
85 | }
86 | },
87 | "favorites.groupsFirst": {
88 | "type": "boolean",
89 | "default": true,
90 | "description": "Display favorite groups before (above) favorite files and directories"
91 | },
92 | "favorites.sortDirection": {
93 | "type": "string",
94 | "enum": [
95 | "ASC",
96 | "DESC"
97 | ],
98 | "default": "ASC",
99 | "description": "Specify an order for all favorites"
100 | },
101 | "favorites.includeInDocumentBodyContextMenu": {
102 | "type": "boolean",
103 | "default": false,
104 | "description": "Display the 'Favorites: Add to * favorites' commands in the editor context menu"
105 | },
106 | "favorites.includeInEditorTabContextMenu": {
107 | "type": "boolean",
108 | "default": true,
109 | "description": "Display the 'Favorites: Add to * favorites' commands in the context menu for the tab of a specific file"
110 | }
111 | }
112 | },
113 | "viewsContainers": {
114 | "activitybar": [
115 | {
116 | "id": "favorites-explorer",
117 | "title": "Favorites",
118 | "icon": "icons/activity_bar_icon.svg"
119 | }
120 | ]
121 | },
122 | "views": {
123 | "favorites-explorer": [
124 | {
125 | "id": "favoritesActivity",
126 | "name": "Explorer"
127 | }
128 | ],
129 | "explorer": [
130 | {
131 | "id": "favorites",
132 | "name": "Favorites"
133 | }
134 | ]
135 | },
136 | "menus": {
137 | "explorer/context": [
138 | {
139 | "command": "favorites.addToFavorites",
140 | "group": "favorites"
141 | },
142 | {
143 | "command": "favorites.addToFavoritesGroup",
144 | "group": "favorites"
145 | }
146 | ],
147 | "editor/title": [
148 | {
149 | "command": "favorites.addToFavorites",
150 | "group": "favorites"
151 | },
152 | {
153 | "command": "favorites.addToFavoritesGroup",
154 | "group": "favorites"
155 | }
156 | ],
157 | "editor/title/context": [
158 | {
159 | "when": "config.favorites.includeInEditorTabContextMenu",
160 | "command": "favorites.addToFavorites",
161 | "group": "favorites"
162 | },
163 | {
164 | "when": "config.favorites.includeInEditorTabContextMenu",
165 | "command": "favorites.addToFavoritesGroup",
166 | "group": "favorites"
167 | }
168 | ],
169 | "editor/context": [
170 | {
171 | "when": "config.favorites.includeInDocumentBodyContextMenu && resourceScheme == file",
172 | "command": "favorites.addFileToFavorites",
173 | "group": "favorites"
174 | },
175 | {
176 | "when": "config.favorites.includeInDocumentBodyContextMenu && resourceScheme == file",
177 | "command": "favorites.addFileToFavoritesGroup",
178 | "group": "favorites"
179 | }
180 | ],
181 | "view/title": [
182 | {
183 | "command": "favorites.collapse",
184 | "when": "view == favorites",
185 | "group": "navigation@4"
186 | },
187 | {
188 | "command": "favorites.refresh",
189 | "when": "view == favorites",
190 | "group": "navigation@3"
191 | },
192 | {
193 | "command": "favorites.nav.sort.az",
194 | "when": "view == favorites && sort==DESC",
195 | "group": "navigation@2"
196 | },
197 | {
198 | "command": "favorites.nav.sort.za",
199 | "when": "view == favorites && sort==ASC",
200 | "group": "navigation@2"
201 | },
202 | {
203 | "command": "favorites.group.create",
204 | "when": "view == favorites",
205 | "group": "navigation@1"
206 | },
207 | {
208 | "command": "favorites.delete.all",
209 | "when": "view == favorites",
210 | "group": "navigation@0"
211 | },
212 | {
213 | "command": "favorites.collapse.activity",
214 | "when": "view == favoritesActivity",
215 | "group": "navigation@4"
216 | },
217 | {
218 | "command": "favorites.refresh",
219 | "when": "view == favoritesActivity",
220 | "group": "navigation@3"
221 | },
222 | {
223 | "command": "favorites.nav.sort.az",
224 | "when": "view == favoritesActivity && sort==DESC",
225 | "group": "navigation@2"
226 | },
227 | {
228 | "command": "favorites.nav.sort.za",
229 | "when": "view == favoritesActivity && sort==ASC",
230 | "group": "navigation@2"
231 | },
232 | {
233 | "command": "favorites.group.create",
234 | "when": "view == favoritesActivity",
235 | "group": "navigation@1"
236 | },
237 | {
238 | "command": "favorites.delete.all",
239 | "when": "view == favoritesActivity",
240 | "group": "navigation@0"
241 | }
242 | ],
243 | "view/item/context": [
244 | {
245 | "command": "favorites.deleteFavorite",
246 | "when": "viewItem == FAVORITE_FILE",
247 | "group": "operation"
248 | },
249 | {
250 | "command": "favorites.deleteFavorite",
251 | "when": "viewItem == FAVORITE_DIRECTORY",
252 | "group": "operation"
253 | },
254 | {
255 | "command": "favorites.alias.modify",
256 | "when": "viewItem == FAVORITE_FILE",
257 | "group": "operation"
258 | },
259 | {
260 | "command": "favorites.alias.modify",
261 | "when": "viewItem == FAVORITE_DIRECTORY",
262 | "group": "operation"
263 | },
264 | {
265 | "command": "favorites.alias.remove",
266 | "when": "viewItem == FAVORITE_FILE",
267 | "group": "operation"
268 | },
269 | {
270 | "command": "favorites.alias.remove",
271 | "when": "viewItem == FAVORITE_DIRECTORY",
272 | "group": "operation"
273 | },
274 | {
275 | "command": "favorites.group.delete",
276 | "when": "viewItem == FAVORITE_GROUP",
277 | "group": "1_modification"
278 | },
279 | {
280 | "command": "favorites.group.rename",
281 | "when": "viewItem == FAVORITE_GROUP",
282 | "group": "1_modification"
283 | },
284 | {
285 | "command": "favorites.group.color.set",
286 | "when": "viewItem == FAVORITE_GROUP",
287 | "group": "6_modification"
288 | },
289 | {
290 | "command": "favorites.group.create",
291 | "when": "viewItem == FAVORITE_GROUP",
292 | "group": "1_modification"
293 | },
294 | {
295 | "command": "favorites.group.create",
296 | "when": "view == favoritesActivity && viewItem != FAVORITE_GROUP && viewItem != FAVORITE_DIRECTORY && viewItem != FAVORITE_FILE && viewItem != FS_FILE && viewItem != FS_DIRECTORY",
297 | "group": "1_modification"
298 | },
299 | {
300 | "command": "favorites.group.create",
301 | "when": "view == favorites && viewItem != FAVORITE_GROUP && viewItem != FAVORITE_DIRECTORY && viewItem != FAVORITE_FILE && viewItem != FS_FILE && viewItem != FS_DIRECTORY",
302 | "group": "1_modification"
303 | },
304 | {
305 | "command": "filesystem.create.file",
306 | "when": "viewItem == FS_DIRECTORY",
307 | "group": "1_modification"
308 | },
309 | {
310 | "command": "filesystem.create.directory",
311 | "when": "viewItem == FS_DIRECTORY",
312 | "group": "1_modification"
313 | },
314 | {
315 | "command": "filesystem.duplicate",
316 | "when": "viewItem == FS_DIRECTORY",
317 | "group": "1_modification"
318 | },
319 | {
320 | "command": "filesystem.copy",
321 | "when": "viewItem == FS_DIRECTORY",
322 | "group": "9_cutcopypaste"
323 | },
324 | {
325 | "command": "filesystem.cut",
326 | "when": "viewItem == FS_DIRECTORY",
327 | "group": "9_cutcopypaste"
328 | },
329 | {
330 | "command": "filesystem.paste",
331 | "when": "viewItem == FS_DIRECTORY && clipboard == true",
332 | "group": "9_cutcopypaste"
333 | },
334 | {
335 | "command": "filesystem.delete",
336 | "when": "viewItem == FS_DIRECTORY",
337 | "group": "7_modification"
338 | },
339 | {
340 | "command": "filesystem.rename",
341 | "when": "viewItem == FS_DIRECTORY",
342 | "group": "7_modification"
343 | },
344 | {
345 | "command": "filesystem.duplicate",
346 | "when": "viewItem == FS_FILE",
347 | "group": "1_modification"
348 | },
349 | {
350 | "command": "filesystem.copy",
351 | "when": "viewItem == FS_FILE",
352 | "group": "9_cutcopypaste"
353 | },
354 | {
355 | "command": "filesystem.cut",
356 | "when": "viewItem == FS_FILE",
357 | "group": "9_cutcopypaste"
358 | },
359 | {
360 | "command": "filesystem.delete",
361 | "when": "viewItem == FS_FILE",
362 | "group": "7_modification"
363 | },
364 | {
365 | "command": "filesystem.rename",
366 | "when": "viewItem == FS_FILE",
367 | "group": "7_modification"
368 | },
369 | {
370 | "command": "filesystem.duplicate",
371 | "when": "viewItem == FAVORITE_FILE",
372 | "group": "1_modification"
373 | },
374 | {
375 | "command": "filesystem.copy",
376 | "when": "viewItem == FAVORITE_FILE",
377 | "group": "9_cutcopypaste"
378 | },
379 | {
380 | "command": "filesystem.cut",
381 | "when": "viewItem == FAVORITE_FILE",
382 | "group": "9_cutcopypaste"
383 | },
384 | {
385 | "command": "filesystem.delete",
386 | "when": "viewItem == FAVORITE_FILE",
387 | "group": "7_modification"
388 | },
389 | {
390 | "command": "filesystem.rename",
391 | "when": "viewItem == FAVORITE_FILE",
392 | "group": "7_modification"
393 | },
394 | {
395 | "command": "filesystem.create.file",
396 | "when": "viewItem == FAVORITE_DIRECTORY",
397 | "group": "1_modification"
398 | },
399 | {
400 | "command": "filesystem.create.directory",
401 | "when": "viewItem == FAVORITE_DIRECTORY",
402 | "group": "1_modification"
403 | },
404 | {
405 | "command": "filesystem.duplicate",
406 | "when": "viewItem == FAVORITE_DIRECTORY",
407 | "group": "1_modification"
408 | },
409 | {
410 | "command": "filesystem.copy",
411 | "when": "viewItem == FAVORITE_DIRECTORY",
412 | "group": "9_cutcopypaste"
413 | },
414 | {
415 | "command": "filesystem.cut",
416 | "when": "viewItem == FAVORITE_DIRECTORY",
417 | "group": "9_cutcopypaste"
418 | },
419 | {
420 | "command": "filesystem.paste",
421 | "when": "viewItem == FAVORITE_DIRECTORY && clipboard == true",
422 | "group": "9_cutcopypaste"
423 | },
424 | {
425 | "command": "filesystem.delete",
426 | "when": "viewItem == FAVORITE_DIRECTORY",
427 | "group": "7_modification"
428 | },
429 | {
430 | "command": "filesystem.rename",
431 | "when": "viewItem == FAVORITE_DIRECTORY",
432 | "group": "7_modification"
433 | },
434 | {
435 | "command": "favorites.addExternal",
436 | "when": "view == favoritesActivity && viewItem != FAVORITE_DIRECTORY && viewItem != FAVORITE_FILE && viewItem != FS_FILE && viewItem != FS_DIRECTORY",
437 | "group": "7_modification"
438 | },
439 | {
440 | "command": "favorites.addExternal",
441 | "when": "view == favorites && viewItem != FAVORITE_DIRECTORY && viewItem != FAVORITE_FILE && viewItem != FS_FILE && viewItem != FS_DIRECTORY",
442 | "group": "7_modification"
443 | },
444 | {
445 | "command": "favorites.copy.path",
446 | "when": "viewItem == FAVORITE_FILE",
447 | "group": "8_copyNameOrPath"
448 | },
449 | {
450 | "command": "favorites.copy.path",
451 | "when": "viewItem == FS_FILE",
452 | "group": "8_copyNameOrPath"
453 | },
454 | {
455 | "command": "favorites.copy.path",
456 | "when": "viewItem == FAVORITE_DIRECTORY",
457 | "group": "8_copyNameOrPath"
458 | },
459 | {
460 | "command": "favorites.copy.path",
461 | "when": "viewItem == FS_DIRECTORY",
462 | "group": "8_copyNameOrPath"
463 | },
464 | {
465 | "command": "favorites.copy.name",
466 | "when": "viewItem == FAVORITE_FILE",
467 | "group": "8_copyNameOrPath"
468 | },
469 | {
470 | "command": "favorites.copy.name",
471 | "when": "viewItem == FS_FILE",
472 | "group": "8_copyNameOrPath"
473 | },
474 | {
475 | "command": "favorites.copy.name",
476 | "when": "viewItem == FAVORITE_DIRECTORY",
477 | "group": "8_copyNameOrPath"
478 | },
479 | {
480 | "command": "favorites.copy.name",
481 | "when": "viewItem == FS_DIRECTORY",
482 | "group": "8_copyNameOrPath"
483 | }
484 | ]
485 | },
486 | "commands": [
487 | {
488 | "command": "favorites.selectWorkspace",
489 | "title": "Favorites: Select Workspace for Favorites"
490 | },
491 | {
492 | "command": "favorites.selectFromRegistry",
493 | "title": "Favorites: Select alternative storage from registry"
494 | },
495 | {
496 | "command": "favorites.nav.sort.az",
497 | "title": "Sort ascending",
498 | "icon": {
499 | "dark": "images/sort_dark_za.svg",
500 | "light": "images/sort_light_za.svg"
501 | }
502 | },
503 | {
504 | "command": "favorites.nav.sort.za",
505 | "title": "Sort descending",
506 | "icon": {
507 | "dark": "images/sort_dark_az.svg",
508 | "light": "images/sort_light_az.svg"
509 | }
510 | },
511 | {
512 | "command": "favorites.collapse",
513 | "title": "Collapse all",
514 | "icon": {
515 | "dark": "images/collapse_dark.svg",
516 | "light": "images/collapse_light.svg"
517 | }
518 | },
519 | {
520 | "command": "favorites.collapse.activity",
521 | "title": "Collapse all",
522 | "icon": {
523 | "dark": "images/collapse_dark.svg",
524 | "light": "images/collapse_light.svg"
525 | }
526 | },
527 | {
528 | "command": "favorites.addToFavorites",
529 | "title": "Favorites: Add to favorites"
530 | },
531 | {
532 | "command": "favorites.addToFavoritesGroup",
533 | "title": "Favorites: Add to group of favorites"
534 | },
535 | {
536 | "command": "favorites.addFileToFavorites",
537 | "title": "Favorites: Add file to favorites"
538 | },
539 | {
540 | "command": "favorites.addFileToFavoritesGroup",
541 | "title": "Favorites: Add file to group of favorites"
542 | },
543 | {
544 | "command": "favorites.deleteFavorite",
545 | "title": "Remove from favorites"
546 | },
547 | {
548 | "command": "favorites.group.rename",
549 | "title": "Rename group"
550 | },
551 | {
552 | "command": "favorites.alias.modify",
553 | "title": "Modify alias"
554 | },
555 | {
556 | "command": "favorites.alias.remove",
557 | "title": "Remove alias"
558 | },
559 | {
560 | "command": "favorites.group.create",
561 | "title": "Create group",
562 | "icon": {
563 | "dark": "images/add_dark.svg",
564 | "light": "images/add_light.svg"
565 | }
566 | },
567 | {
568 | "command": "favorites.delete.all",
569 | "title": "Remove everything from favorites",
570 | "icon": {
571 | "dark": "images/delete_all_dark.svg",
572 | "light": "images/delete_all_light.svg"
573 | }
574 | },
575 | {
576 | "command": "favorites.refresh",
577 | "title": "Refresh list",
578 | "icon": {
579 | "dark": "images/refresh_dark.svg",
580 | "light": "images/refresh_light.svg"
581 | }
582 | },
583 | {
584 | "command": "favorites.group.delete",
585 | "title": "Remove group"
586 | },
587 | {
588 | "command": "favorites.group.color.set",
589 | "title": "Group color"
590 | },
591 | {
592 | "command": "filesystem.create.file",
593 | "title": "Create file"
594 | },
595 | {
596 | "command": "filesystem.create.directory",
597 | "title": "Create directory"
598 | },
599 | {
600 | "command": "filesystem.copy",
601 | "title": "Copy"
602 | },
603 | {
604 | "command": "filesystem.cut",
605 | "title": "Cut"
606 | },
607 | {
608 | "command": "filesystem.paste",
609 | "title": "Paste"
610 | },
611 | {
612 | "command": "filesystem.delete",
613 | "title": "Delete"
614 | },
615 | {
616 | "command": "filesystem.rename",
617 | "title": "Rename"
618 | },
619 | {
620 | "command": "filesystem.duplicate",
621 | "title": "Duplicate"
622 | },
623 | {
624 | "command": "favorites.addExternal",
625 | "title": "Add external resource"
626 | },
627 | {
628 | "command": "favorites.copy.path",
629 | "title": "Copy Path"
630 | },
631 | {
632 | "command": "favorites.copy.name",
633 | "title": "Copy Name"
634 | },
635 | {
636 | "command": "favorites.browse",
637 | "title": "Favorites: Browse"
638 | }
639 | ]
640 | },
641 | "devDependencies": {
642 | "@types/clipboardy": "^1.1.0",
643 | "@types/color": "^3.0.0",
644 | "@types/fs-extra": "^5.0.2",
645 | "@types/lodash": "^4.14.108",
646 | "@types/lodash-es": "^4.17.0",
647 | "@types/md5": "^2.1.32",
648 | "@types/minimatch": "^3.0.3",
649 | "@types/nconf": "0.0.37",
650 | "@types/node": "^9.4.7",
651 | "del": "^3.0.0",
652 | "gulp": "^3.9.1",
653 | "gulp-rename": "^1.2.2",
654 | "ts-loader": "^5.3.0",
655 | "tslint": "^5.9.1",
656 | "typescript": "^2.7.2",
657 | "vscode": "^1.1.13",
658 | "vscode-nls-dev": "^2.1.6",
659 | "webpack": "^4.25.1",
660 | "webpack-cli": "^3.1.2"
661 | },
662 | "dependencies": {
663 | "clipboardy": "^1.2.3",
664 | "color": "^3.0.0",
665 | "cryptiles": "^4.1.2",
666 | "fs-extra": "^7.0.0",
667 | "gulp-remote-src-vscode": "^0.5.1",
668 | "html-colors": "0.0.6",
669 | "lodash": "^4.17.5",
670 | "lodash-es": "^4.17.10",
671 | "md5": "^2.2.1",
672 | "minimatch": "^3.0.4",
673 | "nconf": "^0.10.0",
674 | "rxjs": "^6.2.2",
675 | "spawn-sync": "^2.0.0",
676 | "trash": "^4.3.0",
677 | "url-parse": "^1.4.3"
678 | }
679 | }
680 |
--------------------------------------------------------------------------------
/preview/adding-favorite.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/preview/adding-favorite.jpg
--------------------------------------------------------------------------------
/preview/adding.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/preview/adding.jpg
--------------------------------------------------------------------------------
/preview/browsing.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/preview/browsing.gif
--------------------------------------------------------------------------------
/preview/operations.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/preview/operations.jpg
--------------------------------------------------------------------------------
/preview/preview-promo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/preview/preview-promo.gif
--------------------------------------------------------------------------------
/preview/selectRegistry.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/preview/selectRegistry.jpg
--------------------------------------------------------------------------------
/preview/statusRegistry.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/preview/statusRegistry.jpg
--------------------------------------------------------------------------------
/preview/using.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdcro101/vscode-favorites/bbf4ca0ead1ef60ea28c4e08868437271b6bbc83/preview/using.jpg
--------------------------------------------------------------------------------
/src/class/clipboard.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from "vscode";
2 | import { ClipboardBuffer } from "../types";
3 | import { ViewItem } from "./view-item";
4 |
5 | export class Clipboard {
6 | private static buffer: ClipboardBuffer = null;
7 |
8 | public copy(item: ViewItem) {
9 | Clipboard.buffer = {
10 | operation: "copy",
11 | item,
12 | };
13 | vscode.commands.executeCommand("setContext", "clipboard", true);
14 | }
15 | public cut(item: ViewItem) {
16 | Clipboard.buffer = {
17 | operation: "cut",
18 | item,
19 | };
20 | vscode.commands.executeCommand("setContext", "clipboard", true);
21 | }
22 | public reset() {
23 | Clipboard.buffer = null;
24 | vscode.commands.executeCommand("setContext", "clipboard", false);
25 | }
26 | public get(): ClipboardBuffer {
27 | return Clipboard.buffer;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/class/dataProvider.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from "vscode";
2 | import { Favorites } from "./favorites";
3 | import { FavoritesBrowser } from "./favorites-browser";
4 | import { ViewItem } from "./view-item";
5 |
6 | export class DataProvider implements vscode.TreeDataProvider {
7 | public onDidChangeTreeDataEmmiter = new vscode.EventEmitter();
8 | public readonly onDidChangeTreeData: vscode.Event = this.onDidChangeTreeDataEmmiter.event;
9 | public returnEmpty: boolean = false;
10 | private browser: FavoritesBrowser = null;
11 |
12 | constructor(private favorites: Favorites) {
13 | this.browser = new FavoritesBrowser(favorites);
14 | }
15 |
16 | public getParent(item: ViewItem): Thenable {
17 | return this.browser.getParent(item);
18 | }
19 | public refresh(): void {
20 | this.onDidChangeTreeDataEmmiter.fire();
21 | }
22 | public getTreeItem(item: ViewItem): vscode.TreeItem {
23 | return item;
24 | }
25 |
26 | public getChildren(item?: ViewItem): Thenable {
27 | if (this.returnEmpty) {
28 | return Promise.resolve([]);
29 | }
30 | return this.browser.getChildren(item);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/class/exclude-check.ts:
--------------------------------------------------------------------------------
1 | //
2 | import * as minimatch from "minimatch";
3 | import { Workspace } from "./workspace";
4 |
5 | export class ExcludeCheck {
6 | private excludeList: string[] = [];
7 | private useExclude: boolean = true;
8 |
9 | constructor(private workspace: Workspace) {
10 | this.collectExcludeList();
11 | this.useExclude = workspace.get("useFilesExclude");
12 | }
13 |
14 | public isExcluded(fsPath: string): boolean {
15 |
16 | if (!this.useExclude) {
17 | return false;
18 | }
19 |
20 | let relative = this.workspace.workspacePath(fsPath);
21 | if (!relative) {
22 | relative = fsPath;
23 | }
24 |
25 | const excluded = this.excludeList.reduce((acc, cur) => {
26 | const res = minimatch(relative, cur, {
27 | dot: true,
28 | });
29 | if (res) {
30 | acc = true;
31 | return acc;
32 | }
33 | return acc;
34 | }, false);
35 |
36 | return excluded;
37 | }
38 | private collectExcludeList() {
39 | const filesExclude = this.workspace.getGlobal("files.exclude");
40 | const filesExcludeList = Object.keys(filesExclude);
41 |
42 | this.excludeList = filesExcludeList.reduce((acc, cur, i) => {
43 | const val = filesExclude[cur];
44 | if (val === true) {
45 | acc.push(cur);
46 | return acc;
47 | }
48 | return acc;
49 | }, []);
50 |
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/class/favorites-browser.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 | import * as path from "path";
3 | import * as vscode from "vscode";
4 | import { FilesystemResource, ResourceType } from "../types";
5 | import { Favorites } from "./favorites";
6 | import { ViewItem } from "./view-item";
7 | import workspace from "./workspace";
8 |
9 | export class FavoritesBrowser {
10 | constructor(private favorites: Favorites) {
11 |
12 | }
13 | public getChildren(item: ViewItem): Promise {
14 | return new Promise((resolve, reject) => {
15 |
16 | if (!item) {
17 | this.favorites.groupViewItems(null)
18 | .then((result) => {
19 | resolve(result);
20 | }).catch((e) => {
21 | reject(e);
22 | });
23 | return;
24 | }
25 |
26 | if (item.resourceType === ResourceType.Group) {
27 | this.favorites.groupViewItems(item)
28 | .then((result) => {
29 | resolve(result);
30 | })
31 | .catch((e) => {
32 | reject(e);
33 | });
34 | return;
35 | }
36 | if (item.resourceType === ResourceType.Directory) {
37 | this.getDirectoryItems(item)
38 | .then((result) => {
39 | resolve(result);
40 | })
41 | .catch((e) => {
42 | reject(e);
43 | });
44 |
45 | return;
46 | }
47 |
48 | resolve([]);
49 |
50 | });
51 | }
52 | public getParent(item: ViewItem): Promise {
53 | if (item == null) {
54 | return null;
55 | }
56 | return item.getParent();
57 | }
58 | private getDirectoryItems(parentItem: ViewItem): Promise {
59 | return new Promise((resolve, reject) => {
60 | const fsPath = parentItem.resourceName;
61 | const sortDirection = workspace.get("sortDirection");
62 |
63 | fs.readdir(workspace.pathResolve(fsPath), (err, items) => {
64 | if (err) {
65 | resolve([]);
66 | return;
67 | }
68 |
69 | const resolved = items.map((i) => workspace.pathResolve(path.join(fsPath, i)))
70 | .filter((item) => {
71 | return !workspace.excludeCheck.isExcluded(item);
72 | });
73 |
74 | Promise.all(resolved.map((i) => this.favorites.identify(i)))
75 | .then((result) => {
76 |
77 | const typed = resolved.map((p, i) => {
78 | const o: FilesystemResource = {
79 | path: p,
80 | type: result[i],
81 | };
82 | return o;
83 | });
84 |
85 | const dirs = typed.filter((i) => i.type === ResourceType.Directory);
86 | const files = typed.filter((i) => i.type === ResourceType.File);
87 |
88 | const dirsAZ = dirs.sort((a, b) => {
89 | const aBasename = path.basename(a.path).toLocaleLowerCase();
90 | const bBasename = path.basename(b.path).toLocaleLowerCase();
91 | if (aBasename < bBasename) { return -1; }
92 | if (aBasename === bBasename) { return 0; }
93 | if (aBasename > bBasename) { return 1; }
94 |
95 | });
96 | const filesAZ = files.sort((a, b) => {
97 | const aBasename = path.basename(a.path).toLocaleLowerCase();
98 | const bBasename = path.basename(b.path).toLocaleLowerCase();
99 | if (aBasename < bBasename) { return -1; }
100 | if (aBasename === bBasename) { return 0; }
101 | if (aBasename > bBasename) { return 1; }
102 |
103 | });
104 | let fsItems: FilesystemResource[];
105 | if (sortDirection === "ASC") {
106 | fsItems = dirsAZ.concat(filesAZ);
107 | } else {
108 | fsItems = dirsAZ.reverse().concat(filesAZ.reverse());
109 | }
110 |
111 | Promise.all(fsItems.map((i) => this.favorites.viewItemForPath(i.path)))
112 | .then((views) => {
113 | resolve(views);
114 | return;
115 | })
116 | .catch((e) => {
117 | reject(e);
118 | });
119 |
120 | })
121 | .catch((e) => {
122 | reject(e);
123 | });
124 |
125 | });
126 |
127 | });
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/class/favorites-navigator.ts:
--------------------------------------------------------------------------------
1 | import { from } from "rxjs";
2 | import { map } from "rxjs/operators";
3 | import * as vscode from "vscode";
4 | import { ResourceType } from "../types";
5 | import { Favorites } from "./favorites";
6 | import { FavoritesBrowser } from "./favorites-browser";
7 | import { ViewItem } from "./view-item";
8 |
9 | export interface FavoritesNavigatorQuickPickItem extends vscode.QuickPickItem {
10 | viewItem: ViewItem;
11 | parent?: ViewItem;
12 | upLevel: boolean;
13 | }
14 |
15 | export class FavoritesNavigator {
16 | private browser: FavoritesBrowser = null;
17 | constructor(private favorites: Favorites) {
18 | this.browser = new FavoritesBrowser(this.favorites);
19 | }
20 |
21 | public build(item: ViewItem): Promise {
22 | return new Promise((resolve, reject) => {
23 |
24 | from(Promise.all([
25 | this.browser.getChildren(item),
26 | this.browser.getParent(item),
27 | ])).pipe(
28 | map<[ViewItem[], ViewItem], FavoritesNavigatorQuickPickItem[]>((result) => {
29 |
30 | const listItems = result[0] ? result[0] : [];
31 | const parent = result[1];
32 |
33 | let pickItems: FavoritesNavigatorQuickPickItem[] = item ? [{
34 | label: "↰ ..",
35 | viewItem: null,
36 | upLevel: true,
37 | parent,
38 | }] : [];
39 |
40 | const contents = listItems.map((i) => {
41 | let label = "";
42 | let desc = "";
43 | if (i.resourceType === ResourceType.Group) {
44 | label = `■ ${i.label}`;
45 | desc = "group";
46 | }
47 | if (i.resourceType === ResourceType.Directory) {
48 | label = `□ ${i.label}`;
49 | desc = "directory";
50 | }
51 | if (i.resourceType === ResourceType.File) {
52 | label = `${i.label}`;
53 | desc = "file";
54 | }
55 |
56 | const out: FavoritesNavigatorQuickPickItem = {
57 | label,
58 | description: desc,
59 | viewItem: i,
60 | upLevel: false,
61 | };
62 | return out;
63 | });
64 |
65 | pickItems = pickItems.concat(contents);
66 |
67 | return pickItems;
68 | }),
69 | ).subscribe((list) => {
70 | resolve(list);
71 | }, (e) => {
72 | reject(e);
73 | });
74 |
75 | });
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/class/favorites.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 | import * as _ from "lodash";
3 | import * as path from "path";
4 | import { from, ReplaySubject, zip } from "rxjs";
5 | import { tap } from "rxjs/operators";
6 | import * as vscode from "vscode";
7 | import { GroupQuickPick, ResourceType, StoredResource } from "../types/index";
8 | import { GroupColor } from "./group-color";
9 | import { FavoriteStorage } from "./storage";
10 | import { ViewItem } from "./view-item";
11 | import workspace from "./workspace";
12 |
13 | export class Favorites {
14 | public stateList = new ReplaySubject(1);
15 | public groupColor: GroupColor;
16 |
17 | constructor(private context: vscode.ExtensionContext, private storage: FavoriteStorage) {
18 | this.groupColor = new GroupColor(this, context);
19 | this.get()
20 | .then((result) => {
21 | console.log(`kdcro101.favorites > loaded ${result.length} stored items`);
22 | }).catch((e) => {
23 | console.log(e);
24 | });
25 | }
26 |
27 | public updateWithPath(id: string, absPath: string): Promise {
28 | return new Promise((resolve, reject) => {
29 | Promise.all([
30 | this.get(),
31 | ]).then((results) => {
32 | const all = results[0];
33 | const i = all.findIndex((d) => d.id === id);
34 |
35 | if (i < 0) {
36 | reject("no_item");
37 | return;
38 | }
39 | all[i].workspacePath = workspace.pathForWorkspace(absPath);
40 |
41 | return this.save(all);
42 |
43 | }).then(() => {
44 | resolve();
45 | }).catch((e) => {
46 | reject(e);
47 | });
48 | });
49 | }
50 | public duplicateWithPath(id: string, absPath: string): Promise {
51 | return new Promise((resolve, reject) => {
52 | Promise.all([
53 | this.get(),
54 | ]).then((results) => {
55 | const all = results[0];
56 | const i = all.findIndex((d) => d.id === id);
57 |
58 | if (i < 0) {
59 | reject("no_item");
60 | return;
61 | }
62 | const o = all[i];
63 |
64 | const workspaceRoot = workspace.workspaceRoot(absPath);
65 | if (workspaceRoot) {
66 | return this.addPathToGroup(o.parent_id, absPath);
67 | } else {
68 | return this.addExternalPathToGroup(o.parent_id, absPath);
69 | }
70 |
71 | }).then(() => {
72 | resolve();
73 | }).catch((e) => {
74 | reject(e);
75 | });
76 | });
77 | }
78 | public generateGroupQuickPickList(): Promise {
79 | return new Promise((resolve, reject) => {
80 |
81 | const out: GroupQuickPick[] = [];
82 |
83 | Promise.all([
84 | this.get(),
85 | ]).then((result) => {
86 | const all = result[0];
87 | const root = all.filter((i) => i.type === ResourceType.Group && i.parent_id == null);
88 |
89 | const addChildren = (lastDepth: number, parentId: string) => {
90 | const children = all.filter((i) => i.type === ResourceType.Group && i.parent_id === parentId);
91 | const paddingLen: number = lastDepth + 1;
92 | const padding = "".padStart(paddingLen * 2, " ") + "▹";
93 |
94 | children.forEach((c, i) => {
95 | const label = `${padding} ${c.name}`;
96 | const o: GroupQuickPick = {
97 | id: c.id,
98 | label,
99 | description: "",
100 | };
101 | out.push(o);
102 | addChildren(lastDepth + 1, c.id);
103 | });
104 | };
105 |
106 | root.forEach((g, i) => {
107 | const o: GroupQuickPick = {
108 | id: g.id,
109 | label: g.name,
110 | description: "",
111 | };
112 | out.push(o);
113 | addChildren(0, g.id);
114 | });
115 |
116 | resolve(out);
117 | });
118 | });
119 | }
120 | public removeResource(id: string) {
121 | this.get()
122 | .then((result) => {
123 |
124 | const toDelete: string[] = [];
125 | toDelete.push(id);
126 |
127 | const collectChildren = (resourceId: string) => {
128 | toDelete.push(resourceId);
129 | const cc = result.filter((i) => i.parent_id === resourceId);
130 | cc.forEach((sc) => {
131 | collectChildren(sc.id);
132 | });
133 | };
134 |
135 | const c = result.filter((i) => i.parent_id === id);
136 |
137 | c.forEach((e) => {
138 | collectChildren(e.id);
139 | });
140 |
141 | const final = result.filter((i) => toDelete.find((x) => x === i.id) == null);
142 |
143 | this.save(final);
144 |
145 | })
146 | .catch((e) => {
147 | console.log(e);
148 | });
149 | }
150 | public addExternalPathToGroup(groupId: string, itemPath: string) {
151 | return new Promise((resolve, reject) => {
152 |
153 | let all: StoredResource[] = null;
154 | Promise.all([
155 | this.get(),
156 | ]).then((result) => {
157 | all = result[0];
158 |
159 | const groupContents = all.filter((i) => i.parent_id === groupId);
160 | const hasPath = groupContents.filter((i) => i.type !== ResourceType.Group && i.name === itemPath);
161 |
162 | if (hasPath.length > 0) {
163 | resolve();
164 | return;
165 | }
166 |
167 | return this.identify(itemPath);
168 |
169 | }).then((t) => {
170 |
171 | if (t == null) {
172 | vscode.window.showErrorMessage(`Unable to identify type of ${itemPath}`);
173 | reject("identify_error");
174 | return;
175 | }
176 |
177 | const o = this.createStoredResource(groupId, itemPath, t, true);
178 | all.push(o);
179 | return this.save(all);
180 |
181 | }).then(() => {
182 | resolve();
183 | }).catch((e) => {
184 | reject(e);
185 | });
186 |
187 | });
188 | }
189 | public addPathToGroup(groupId: string, itemPath: string) {
190 | return new Promise((resolve, reject) => {
191 |
192 | let all: StoredResource[] = null;
193 | Promise.all([
194 | this.get(),
195 | ]).then((result) => {
196 | all = result[0];
197 | const isMultiRoot = workspace.isMultiRootWorkspace();
198 | const rPath = isMultiRoot ? itemPath : itemPath.substr(workspace.getSingleRootPath().length + 1);
199 | const groupContents = all.filter((i) => i.parent_id === groupId);
200 | const hasPath = groupContents.filter((i) => i.type !== ResourceType.Group && i.name === itemPath);
201 |
202 | if (hasPath.length > 0) {
203 | return Promise.resolve("exists");
204 | }
205 |
206 | return this.identify(itemPath);
207 |
208 | }).then((t: ResourceType) => {
209 |
210 | if ((t as string) === "exists") {
211 | vscode.window.showWarningMessage(`${itemPath} already in favorites`);
212 | resolve();
213 | return;
214 | }
215 |
216 | if (t == null) {
217 | vscode.window.showErrorMessage(`Unable to identify type of ${itemPath}`);
218 | reject("identify_error");
219 | return;
220 | }
221 |
222 | const o = this.createStoredResource(groupId, itemPath, t);
223 | all.push(o);
224 | return this.save(all);
225 |
226 | }).then(() => {
227 | resolve();
228 | }).catch((e) => {
229 | reject(e);
230 | });
231 |
232 | });
233 | }
234 | public labelModify(id: string, name: string) {
235 | return new Promise((resolve, reject) => {
236 |
237 | Promise.all([
238 | this.get(),
239 | ]).then((result) => {
240 | const all = result[0];
241 | const i = all.findIndex((r) => r.id === id);
242 | if (i === -1) {
243 | resolve();
244 | }
245 | all[i].label = name;
246 | return this.save(all);
247 |
248 | }).then(() => {
249 | resolve();
250 | }).catch((e) => {
251 | reject(e);
252 | });
253 |
254 | });
255 | }
256 | public groupRename(id: string, name: string) {
257 | return new Promise((resolve, reject) => {
258 |
259 | Promise.all([
260 | this.get(),
261 | ]).then((result) => {
262 | const all = result[0];
263 | const i = all.findIndex((r) => r.id === id);
264 | if (i === -1) {
265 | resolve();
266 | }
267 | all[i].name = name;
268 | return this.save(all);
269 |
270 | }).then(() => {
271 | resolve();
272 | }).catch((e) => {
273 | reject(e);
274 | });
275 |
276 | });
277 | }
278 | public addGroup(parent_id: string, name: string) {
279 | return new Promise((resolve, reject) => {
280 |
281 | Promise.all([
282 | this.get(),
283 | ]).then((result) => {
284 | const all = result[0];
285 |
286 | const o = this.createStoredResource(parent_id, name, ResourceType.Group);
287 | const newList: StoredResource[] = all.concat([o]);
288 | return this.save(newList);
289 |
290 | }).then(() => {
291 | resolve();
292 | }).catch((e) => {
293 | reject(e);
294 | });
295 |
296 | });
297 | }
298 | public get(): Promise {
299 | return new Promise((resolve, reject) => {
300 |
301 | from(this.storage.get()).pipe(
302 | tap((list) => this.stateList.next(list)),
303 | ).subscribe((list) => {
304 | resolve(list);
305 | }, (e) => {
306 | console.log(e);
307 | });
308 |
309 | });
310 | }
311 |
312 | public save(list: StoredResource[]): Promise {
313 | this.stateList.next(list);
314 | return this.storage.save(list);
315 | }
316 | public identify(itemPath: string): Promise {
317 | return new Promise((resolve, reject) => {
318 | fs.stat(workspace.pathResolve(itemPath), (err, stat: fs.Stats) => {
319 |
320 | if (err) {
321 | resolve(null);
322 | return;
323 | }
324 |
325 | const isDir = stat.isDirectory();
326 | const isFile = stat.isFile();
327 |
328 | if (isDir) {
329 | resolve(ResourceType.Directory);
330 | return;
331 | }
332 | if (isFile) {
333 | resolve(ResourceType.File);
334 | return;
335 | }
336 |
337 | resolve(null);
338 |
339 | });
340 | });
341 | }
342 | public groupViewItems(parentItem: ViewItem): Promise {
343 |
344 | const parentId = parentItem == null ? null : parentItem.id;
345 |
346 | return new Promise((resolve, reject) => {
347 | Promise.all([
348 | this.get(),
349 | ]).then((result) => {
350 | const all = result[0];
351 | // tslint:disable-next-line:triple-equals
352 | const list: StoredResource[] = all.filter((i) => i.parent_id == parentId)
353 | .filter((item) => {
354 | if (item.type === ResourceType.Group) {
355 | return true;
356 | }
357 | const checkPath = item.fsPath || path.join(item.workspaceRoot, item.workspacePath);
358 | return !workspace.excludeCheck.isExcluded(checkPath);
359 | });
360 |
361 | this.sortStoredResources(list)
362 | .then((sorted) => {
363 | Promise.all(sorted.map((i) => this.asViewItem(i)))
364 | .then((views) => {
365 | resolve(views);
366 | })
367 | .catch((e) => {
368 | reject(e);
369 | });
370 | })
371 | .catch((e) => {
372 | reject(e);
373 | });
374 |
375 | }).catch((e) => {
376 | reject(e);
377 | });
378 | });
379 | }
380 | public sortStoredResources(list: StoredResource[]): Promise {
381 | return new Promise((resolve, reject) => {
382 | try {
383 |
384 | const dirs = list.filter((i) => i.type === ResourceType.Directory);
385 | const files = list.filter((i) => i.type === ResourceType.File);
386 | const groups = list.filter((i) => i.type === ResourceType.Group);
387 |
388 | const sortDirection = workspace.get("sortDirection");
389 | const groupsFirst = workspace.get("groupsFirst");
390 |
391 | const groupsAZ = groups.sort((a, b) => {
392 | const aBasename = a.name;
393 | const bBasename = b.name;
394 | if (aBasename < bBasename) { return -1; }
395 | if (aBasename === bBasename) { return 0; }
396 | if (aBasename > bBasename) { return 1; }
397 | });
398 |
399 | const dirsAZ = dirs.sort((a, b) => {
400 | const aBasename = path.basename(a.name).toLocaleLowerCase();
401 | const bBasename = path.basename(b.name).toLocaleLowerCase();
402 | if (aBasename < bBasename) { return -1; }
403 | if (aBasename === bBasename) { return 0; }
404 | if (aBasename > bBasename) { return 1; }
405 | });
406 |
407 | const filesAZ = files.sort((a, b) => {
408 | const aBasename = path.basename(a.name).toLocaleLowerCase();
409 | const bBasename = path.basename(b.name).toLocaleLowerCase();
410 | if (aBasename < bBasename) { return -1; }
411 | if (aBasename === bBasename) { return 0; }
412 | if (aBasename > bBasename) { return 1; }
413 |
414 | });
415 |
416 | let fsItems: StoredResource[];
417 | let groupsPrepared: StoredResource[];
418 |
419 | if (sortDirection === "ASC") {
420 | fsItems = dirsAZ.concat(filesAZ);
421 | groupsPrepared = groupsAZ;
422 | } else {
423 | fsItems = dirsAZ.reverse().concat(filesAZ.reverse());
424 | groupsPrepared = groupsAZ.reverse();
425 | }
426 | let final: StoredResource[];
427 |
428 | if (groupsFirst) {
429 | final = groupsPrepared.concat(fsItems);
430 | } else {
431 | final = fsItems.concat(groupsPrepared);
432 | }
433 |
434 | resolve(final);
435 | } catch (e) {
436 | reject(e);
437 | }
438 |
439 | });
440 | }
441 |
442 | public viewItemForPath(fsPath: string): Promise {
443 | return new Promise((resolve, reject) => {
444 | const enablePreview = vscode.workspace.getConfiguration("workbench.editor").get("enablePreview") as boolean;
445 | Promise.all([this.identify(fsPath)])
446 | .then((result) => {
447 | let o: ViewItem;
448 | switch (result[0]) {
449 | case ResourceType.File:
450 | const fUri = workspace.pathAsUri(fsPath);
451 | o = new ViewItem(
452 | path.basename(fsPath),
453 | vscode.TreeItemCollapsibleState.None,
454 | fsPath,
455 | "FS_FILE",
456 | fsPath,
457 | ResourceType.File
458 | , null,
459 | {
460 | command: "vscode.open",
461 | title: "",
462 | arguments: [fUri, { preview: enablePreview }],
463 | },
464 | );
465 | break;
466 | case ResourceType.Directory:
467 | o = new ViewItem(
468 | path.basename(fsPath),
469 | vscode.TreeItemCollapsibleState.Collapsed,
470 | fsPath,
471 | "FS_DIRECTORY",
472 | fsPath,
473 | ResourceType.Directory
474 | , null);
475 | break;
476 | }
477 |
478 | resolve(o);
479 | })
480 | .catch((e) => {
481 | reject(e);
482 | });
483 |
484 | });
485 | }
486 | public asViewItem(i: StoredResource): ViewItem {
487 | const enablePreview = vscode.workspace.getConfiguration("workbench.editor").get("enablePreview") as boolean;
488 |
489 | let o: ViewItem = null;
490 | switch (i.type) {
491 | case ResourceType.File:
492 |
493 | if (i.fsPath == null) {
494 |
495 | // const fPath = workspace.pathAbsolute(i.workspacePath);
496 | const fPath = path.join(i.workspaceRoot, i.workspacePath);
497 | const fUri = workspace.pathAsUri(fPath);
498 | o = new ViewItem(
499 | (i.label != null) ? i.label : path.basename(i.workspacePath),
500 | vscode.TreeItemCollapsibleState.None,
501 | fPath,
502 | "FAVORITE_FILE",
503 | fPath,
504 | i.type,
505 | null, // NO ICON
506 | {
507 | command: "vscode.open",
508 | title: "",
509 | arguments: [fUri, { preview: enablePreview }],
510 | },
511 | i.id,
512 | i.parent_id,
513 | (i.label != null) ? `[alias] ${path.basename(i.name)}` : null,
514 | );
515 | } else {
516 | const fPath = i.fsPath;
517 | const fUri = vscode.Uri.file(fPath);
518 | o = new ViewItem(
519 | (i.label != null) ? i.label : path.basename(fPath),
520 | vscode.TreeItemCollapsibleState.None,
521 | fPath,
522 | "FAVORITE_FILE",
523 | fPath,
524 | i.type,
525 | null, // NO ICON
526 | {
527 | command: "vscode.open",
528 | title: "",
529 | arguments: [fUri, { preview: enablePreview }],
530 | },
531 | i.id,
532 | i.parent_id,
533 | (i.label != null) ? `[alias] ${path.basename(i.name)}` : null,
534 | );
535 | }
536 |
537 | break;
538 | case ResourceType.Directory:
539 |
540 | if (i.fsPath == null) {
541 | // const wPath = workspace.pathAbsolute(i.workspacePath);
542 | const wPath = path.join(i.workspaceRoot, i.workspacePath);
543 | o = new ViewItem(
544 | (i.label != null) ? i.label : path.basename(wPath),
545 | vscode.TreeItemCollapsibleState.Collapsed,
546 | wPath,
547 | "FAVORITE_DIRECTORY",
548 | wPath,
549 | i.type,
550 | null,
551 | null,
552 | i.id,
553 | i.parent_id,
554 | (i.label != null) ? `[alias] ${i.name}` : null,
555 | );
556 |
557 | } else {
558 | const fsPath = i.fsPath;
559 | o = new ViewItem(
560 | (i.label != null) ? i.label : path.basename(fsPath),
561 | vscode.TreeItemCollapsibleState.Collapsed,
562 | fsPath,
563 | "FAVORITE_DIRECTORY",
564 | fsPath,
565 | i.type,
566 | null,
567 | null,
568 | i.id,
569 | i.parent_id,
570 | (i.label != null) ? `[alias] ${i.name}` : null,
571 | );
572 | }
573 |
574 | break;
575 | case ResourceType.Group:
576 | const iconLight: string = i.iconColor == null ?
577 | // this.context.asAbsolutePath(path.join("images", "group_light.svg")) : i.iconPath;
578 | this.context.asAbsolutePath(path.join("images", "group_light.svg")) : this.groupColor.getIconPath(i.iconColor);
579 | const iconDark: string = i.iconColor == null ?
580 | // this.context.asAbsolutePath(path.join("images", "group_dark.svg")) : i.iconPath;
581 | this.context.asAbsolutePath(path.join("images", "group_dark.svg")) : this.groupColor.getIconPath(i.iconColor);
582 |
583 | o = new ViewItem(
584 | i.name,
585 | vscode.TreeItemCollapsibleState.Collapsed,
586 | i.name,
587 | "FAVORITE_GROUP",
588 | i.name,
589 | i.type,
590 | {
591 | light: iconLight,
592 | dark: iconDark,
593 | },
594 | null,
595 | i.id,
596 | i.parent_id,
597 |
598 | );
599 | break;
600 | }
601 |
602 | // o.parentViewItem = parentItem;
603 | return o;
604 | }
605 |
606 | public isPartOfFavorites(fsPath: string): Promise {
607 | return new Promise((resolve, reject) => {
608 |
609 | if (!fsPath) {
610 | resolve(false);
611 | return;
612 | }
613 |
614 | zip(this.stateList, this.identify(fsPath))
615 | .subscribe((result) => {
616 | const list = result[0];
617 | const type = result[1];
618 |
619 | if (!fsPath || !list || list.length === 0) {
620 | resolve(false);
621 | return;
622 | }
623 |
624 | let dir: string = "";
625 |
626 | if (type === ResourceType.Directory) {
627 | dir = fsPath;
628 | }
629 | if (type === ResourceType.File) {
630 | dir = path.dirname(fsPath);
631 | }
632 |
633 | const splits = dir.split(path.sep);
634 | const findDirect = list.find((sr) => sr.name === dir && sr.type === ResourceType.Directory);
635 |
636 | if (findDirect) {
637 | resolve(true);
638 | }
639 | let currentDir = dir;
640 | for (let i = 0; i < splits.length; i++) {
641 | const p = path.join(currentDir, "..");
642 | const f = list.find((sr) => sr.name === p && sr.type === ResourceType.Directory);
643 | if (f) {
644 | resolve(true);
645 | }
646 | currentDir = p;
647 | }
648 |
649 | resolve(false);
650 |
651 | }, (e) => {
652 | reject(e);
653 | });
654 |
655 | });
656 | }
657 |
658 | private createStoredResource(parent_id: string, name: string, type: ResourceType, external: boolean = false): StoredResource {
659 | let o: StoredResource = null;
660 | switch (type) {
661 | case ResourceType.Group:
662 | o = {
663 | type,
664 | name,
665 | parent_id,
666 | workspaceRoot: null,
667 | workspacePath: null,
668 | id: this.generateId(),
669 | };
670 | break;
671 | case ResourceType.Directory:
672 | if (external) {
673 | o = {
674 | type,
675 | name,
676 | parent_id,
677 | fsPath: name,
678 | workspaceRoot: null,
679 | workspacePath: null,
680 | id: this.generateId(),
681 | };
682 | } else {
683 | o = {
684 | type,
685 | name,
686 | parent_id,
687 | // workspacePath: workspace.pathForWorkspace(name),
688 | workspaceRoot: workspace.workspaceRoot(name),
689 | workspacePath: workspace.workspacePath(name),
690 | id: this.generateId(),
691 | };
692 |
693 | }
694 | break;
695 | case ResourceType.File:
696 | if (external) {
697 | o = {
698 | type,
699 | name,
700 | parent_id,
701 | fsPath: name,
702 | workspaceRoot: null,
703 | workspacePath: null,
704 | id: this.generateId(),
705 | };
706 | } else {
707 | o = {
708 | type,
709 | name,
710 | parent_id,
711 | // workspacePath: workspace.pathForWorkspace(name),
712 | workspaceRoot: workspace.workspaceRoot(name),
713 | workspacePath: workspace.workspacePath(name),
714 | id: this.generateId(),
715 | };
716 |
717 | }
718 | break;
719 | }
720 |
721 | return o;
722 | }
723 | private generateId(): string {
724 | return _.sampleSize("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 16).join("");
725 | }
726 | }
727 |
--------------------------------------------------------------------------------
/src/class/filesystem.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs-extra";
2 | import * as path from "path";
3 | import { from } from "rxjs";
4 | import { concatMap } from "rxjs/operators";
5 | import * as vscode from "vscode";
6 | import { Favorites } from "./favorites";
7 | import { ViewItem } from "./view-item";
8 | import workspace from "./workspace";
9 |
10 | import trash = require("trash");
11 |
12 | export class FilesystemUtils {
13 | constructor(private favorites: Favorites) {
14 |
15 | }
16 | public duplicate(item: ViewItem) {
17 |
18 | return new Promise((resolve, reject) => {
19 | const isFav = item.isFavorite;
20 | const aPath = item.value;
21 | const base = path.basename(aPath);
22 | const dir = path.dirname(aPath);
23 |
24 | Promise.all([])
25 | .then(() => {
26 |
27 | vscode.window.showInputBox({
28 | prompt: "Enter name for duplicate",
29 | placeHolder: "Type duplicate name",
30 | value: base,
31 | }).then((val) => {
32 | if (val == null || val.trim() === "") {
33 | resolve();
34 | return;
35 | }
36 | if (val === base) {
37 | vscode.window.showWarningMessage("New name must be different");
38 | reject("not_different");
39 | return;
40 | }
41 | const newBase = val.trim();
42 | const newPath = path.join(dir, newBase);
43 | fs.copy(aPath, newPath)
44 | .then(() => {
45 | return isFav ? this.favorites.duplicateWithPath(item.id, newPath) : Promise.resolve();
46 | }).then((result) => {
47 | vscode.window.showInformationMessage(`Duplication successful`);
48 | resolve();
49 | }).catch((e) => {
50 | vscode.window.showErrorMessage(`Error duplicating ${base}`);
51 | reject(e);
52 | });
53 |
54 | });
55 | })
56 | .catch((e) => {
57 | reject(e);
58 | });
59 | });
60 | }
61 | public delete(item: ViewItem) {
62 | return new Promise((resolve, reject) => {
63 | const useTrash = workspace.get("useTrash") as boolean;
64 | const aPath = item.value;
65 | const base = path.basename(aPath);
66 |
67 | vscode.window.showQuickPick(
68 | ["Yes", "No"], {
69 | placeHolder: `Are you sure you want to PERMANENTLY delete '${base}' ?`,
70 | },
71 | ).then((val) => {
72 | if (val == null || val.toLocaleLowerCase() === "no") {
73 | resolve();
74 | return;
75 | }
76 |
77 | const p = useTrash ? this.deleteToTrash(item) : this.deletePermanently(item);
78 |
79 | p.then((result) => {
80 | resolve();
81 | }).catch((e) => {
82 | vscode.window.showErrorMessage(`Error deleting ${base}`);
83 | reject(e);
84 | });
85 |
86 | });
87 | });
88 | }
89 | public deletePermanently(item: ViewItem) {
90 | return new Promise((resolve, reject) => {
91 |
92 | const isFav = item.isFavorite;
93 | const aPath = item.value;
94 | const base = path.basename(aPath);
95 |
96 | fs.remove(aPath).then((result) => {
97 | return isFav ? this.favorites.removeResource(item.id) : Promise.resolve();
98 | }).then((result) => {
99 | resolve();
100 | }).catch((e) => {
101 | vscode.window.showErrorMessage(`Error deleting ${base}`);
102 | reject(e);
103 | });
104 |
105 | });
106 |
107 | }
108 | public deleteToTrash(item: ViewItem): Promise {
109 | return new Promise((resolve, reject) => {
110 | const isFav = item.isFavorite;
111 | const resourcePath = item.value;
112 | const base = path.basename(resourcePath);
113 |
114 | from(trash([resourcePath])).pipe(
115 | concatMap(() => {
116 | return isFav ? Promise.resolve(this.favorites.removeResource(item.id)) : Promise.resolve();
117 | }),
118 | ).subscribe(() => {
119 | resolve();
120 | }, (e) => {
121 | vscode.window.showQuickPick(
122 | ["Yes", "No"],
123 | {
124 | placeHolder: `Trash is not accessible. Do you want to permanently delete '${base}' ?`,
125 | },
126 | ).then((response) => {
127 |
128 | if (response == null || response === "No") {
129 | resolve();
130 | return;
131 | }
132 | fs.remove(resourcePath)
133 | .then(() => {
134 | return isFav ? Promise.resolve(this.favorites.removeResource(item.id)) : Promise.resolve();
135 | })
136 | .then(() => {
137 | resolve();
138 | }).catch((fsError) => {
139 | reject(fsError);
140 | });
141 |
142 | });
143 |
144 | });
145 |
146 | });
147 | }
148 | public rename(item: ViewItem) {
149 | return new Promise((resolve, reject) => {
150 | const isFav = item.isFavorite;
151 | const aPath = item.value;
152 | const base = path.basename(aPath);
153 | const dir = path.dirname(aPath);
154 | vscode.window.showInputBox({
155 | prompt: "Enter new name",
156 | placeHolder: "Type new name",
157 | value: base,
158 | }).then((val) => {
159 | if (val == null || val.trim() === "") {
160 | return;
161 | }
162 | if (val === base) {
163 | vscode.window.showWarningMessage("New name must be different");
164 | reject("not_different");
165 | return;
166 | }
167 | const newBase = val.trim();
168 | const newPath = path.join(dir, newBase);
169 | console.log(`renaming: [${aPath}]->[${newPath}]`);
170 | fs.rename(aPath, newPath)
171 | .then(() => {
172 | return isFav ? this.favorites.updateWithPath(item.id, newPath) : Promise.resolve();
173 | }).then(() => {
174 | resolve();
175 | }).catch((e) => {
176 | console.log(e);
177 | console.error(e);
178 | vscode.window.showErrorMessage(`Error renaming ${base}`);
179 | reject(e);
180 | });
181 |
182 | });
183 | });
184 | }
185 | public createFile(item: ViewItem) {
186 | return new Promise((resolve, reject) => {
187 | const aPath = item.value;
188 | let newPath: string = null;
189 | let newName: string = null;
190 |
191 | vscode.window.showInputBox({
192 | prompt: "Enter file name",
193 | placeHolder: "Type file name",
194 | }).then((val) => {
195 | if (val == null || val.trim() === "") {
196 | newPath = null;
197 | resolve();
198 | return;
199 | }
200 | newName = val;
201 | newPath = path.join(aPath, val);
202 | return fs.pathExists(newPath);
203 | }).then((b) => {
204 | if (newPath == null) {
205 | // do nothing
206 | return;
207 | }
208 |
209 | if (b === true) {
210 | vscode.window.showWarningMessage("File exists");
211 | reject("exists");
212 | return;
213 | }
214 |
215 | fs.createFile(newPath)
216 | .then((result) => {
217 | resolve();
218 | })
219 | .catch((e) => {
220 | vscode.window.showErrorMessage(`Error creating file ${newName}`);
221 | reject(e);
222 | });
223 |
224 | });
225 |
226 | });
227 | }
228 | public createDirectory(item: ViewItem) {
229 | return new Promise((resolve, reject) => {
230 | const aPath = item.value;
231 | let newPath: string = null;
232 | let newName: string = null;
233 |
234 | vscode.window.showInputBox({
235 | prompt: "Enter directory name",
236 | placeHolder: "Type directory name",
237 | }).then((val) => {
238 | if (val == null || val.trim() === "") {
239 | resolve();
240 | return;
241 | }
242 | newName = val;
243 | newPath = path.join(aPath, val);
244 | return fs.pathExists(newPath);
245 | }).then((b) => {
246 |
247 | if (b === true) {
248 | vscode.window.showWarningMessage("Directory exists");
249 | reject("exists");
250 | return;
251 | }
252 |
253 | fs.ensureDir(newPath)
254 | .then((result) => {
255 | resolve();
256 | })
257 | .catch((e) => {
258 | vscode.window.showErrorMessage(`Error creating directory ${newName}`);
259 | reject(e);
260 | });
261 |
262 | });
263 |
264 | });
265 | }
266 | public copy(clipboardItem: ViewItem, destination: ViewItem) {
267 | return new Promise((resolve, reject) => {
268 | const sPath = clipboardItem.value;
269 | const sBase = path.basename(clipboardItem.value);
270 | const dDir = destination.value;
271 | const dPath = path.join(dDir, sBase);
272 |
273 | Promise.all([
274 | fs.pathExists(dPath),
275 | ]).then((results) => {
276 |
277 | const exists = results[0];
278 |
279 | if (exists) {
280 | return vscode.window.showQuickPick(
281 | ["Yes", "No"], {
282 | placeHolder: `Destination exists! Overwrite? '${dPath}'`,
283 | },
284 | );
285 | } else {
286 | return Promise.resolve("Yes");
287 | }
288 |
289 | }).then((shouldCopy: string) => {
290 |
291 | if (shouldCopy !== "Yes") {
292 | resolve();
293 | return;
294 | }
295 |
296 | fs.copy(sPath, dPath, {
297 | overwrite: true,
298 | }).then((result) => {
299 | resolve();
300 | }).catch((e) => {
301 | vscode.window.showErrorMessage(`Error copying ${sPath}`);
302 | reject(e);
303 | });
304 |
305 | });
306 |
307 | });
308 | }
309 | public move(clipboardItem: ViewItem, destination: ViewItem) {
310 |
311 | return new Promise((resolve, reject) => {
312 | const isFav = clipboardItem.isFavorite;
313 | const sPath = clipboardItem.value;
314 | const sBase = path.basename(clipboardItem.value);
315 | const dDir = destination.value;
316 | const dPath = path.join(dDir, sBase);
317 |
318 | Promise.all([
319 | fs.pathExists(dPath),
320 | ]).then((results) => {
321 |
322 | const exists = results[0];
323 |
324 | if (exists) {
325 | return vscode.window.showQuickPick(
326 | ["Yes", "No"], {
327 | placeHolder: `Destination exists! Overwrite? '${dPath}'`,
328 | },
329 | );
330 | } else {
331 | return Promise.resolve("Yes");
332 | }
333 |
334 | }).then((shouldMove: string) => {
335 |
336 | if (shouldMove !== "Yes") {
337 | resolve();
338 | return;
339 | }
340 |
341 | fs.move(sPath, dPath, {
342 | overwrite: true,
343 | }).then((result) => {
344 | return isFav ? this.favorites.removeResource(clipboardItem.id) : Promise.resolve();
345 | }).then((result) => {
346 | resolve();
347 | }).catch((e) => {
348 | vscode.window.showErrorMessage(`Error moving ${sPath}`);
349 | reject(e);
350 | });
351 |
352 | });
353 |
354 | });
355 |
356 | }
357 |
358 | }
359 |
--------------------------------------------------------------------------------
/src/class/fs-watcher.ts:
--------------------------------------------------------------------------------
1 | import { fromEventPattern, merge, Subject } from "rxjs";
2 | import { catchError, debounceTime, filter, map, takeUntil } from "rxjs/operators";
3 | import * as vscode from "vscode";
4 | import { Global } from "./global";
5 | import { FavoriteStorage } from "./storage";
6 |
7 | export interface FileSystemEvent {
8 | type: "change" | "create" | "delete";
9 | fsPath: string;
10 | }
11 |
12 | export class FsWatcher {
13 | public eventFs = new Subject();
14 | private watcher: vscode.FileSystemWatcher = null;
15 | constructor(private storage: FavoriteStorage) {
16 |
17 | this.watcher = vscode.workspace.createFileSystemWatcher("**/*.*", false, false, false);
18 | merge(
19 | fromEventPattern((f) => {
20 | return this.watcher.onDidCreate(f as any);
21 | }, (f: any, d: vscode.Disposable) => {
22 | d.dispose();
23 | }).pipe(map((e) => {
24 | return {
25 | fsPath: e.fsPath,
26 | type: "create",
27 | } as FileSystemEvent;
28 | })),
29 | fromEventPattern((f) => {
30 | return this.watcher.onDidDelete(f as any);
31 | }, (f: any, d: vscode.Disposable) => {
32 | d.dispose();
33 | }).pipe(map((e) => {
34 | return {
35 | fsPath: e.fsPath,
36 | type: "delete",
37 | } as FileSystemEvent;
38 | })),
39 | fromEventPattern((f) => {
40 | return this.watcher.onDidChange(f as any);
41 | }, (f: any, d: vscode.Disposable) => {
42 | d.dispose();
43 | }).pipe(map((e) => {
44 | return {
45 | fsPath: e.fsPath,
46 | type: "change",
47 | } as FileSystemEvent;
48 | })),
49 | ).pipe(
50 | takeUntil(Global.eventDeactivate),
51 | filter((e) => e.fsPath !== this.storage.storageFilePath),
52 | debounceTime(1000),
53 | catchError((e, o) => o),
54 | ).subscribe((e) => {
55 | this.eventFs.next();
56 | });
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/class/global.ts:
--------------------------------------------------------------------------------
1 | import { Subject } from "rxjs";
2 | import * as vscode from "vscode";
3 |
4 | export class Global {
5 | public static eventDeactivate = new Subject();
6 | public static statusRegistry: vscode.StatusBarItem = null;
7 | }
8 |
--------------------------------------------------------------------------------
/src/class/group-color.ts:
--------------------------------------------------------------------------------
1 | import * as Color from "color";
2 | import * as fs from "fs-extra";
3 | import * as colors from "html-colors";
4 | import * as md5 from "md5";
5 | import * as path from "path";
6 | import { ExtensionContext } from "vscode";
7 | import { HtmlColor } from "../types";
8 | import { Favorites } from "./favorites";
9 |
10 | const STORAGE_DIRECTORY: string = "svg-generated";
11 |
12 | export class GroupColor {
13 | public colorList: HtmlColor = colors.all();
14 | public storagePath: string = null;
15 | constructor(private favorites: Favorites, private context: ExtensionContext) {
16 | this.storagePath = path.join(this.context.extensionPath, STORAGE_DIRECTORY);
17 | }
18 | public getIconPath(normalizedColor: string) {
19 | const ip = this.iconPath(normalizedColor);
20 | const exists = fs.pathExistsSync(ip);
21 |
22 | if (exists) {
23 | return ip;
24 | }
25 | const svg = this.build(normalizedColor);
26 | const dir = path.dirname(ip);
27 | fs.mkdirpSync(dir);
28 | fs.writeFileSync(ip, svg);
29 | return ip;
30 |
31 | }
32 | public iconPath(normalizedColor: string): string {
33 | const h = md5(normalizedColor);
34 | const p = path.join(this.storagePath, `icon_${h}.svg`);
35 | return p;
36 | }
37 | public setColor(id: string, colorDef: string) {
38 | return new Promise((resolve, reject) => {
39 |
40 | const isColor = this.isColor(colorDef);
41 | if (!isColor) {
42 | reject("invalid_color");
43 | return;
44 | }
45 |
46 | const normColor = this.normalizeColor(colorDef);
47 | const iconPath = this.iconPath(normColor);
48 | const iconData = this.build(normColor);
49 |
50 | fs.outputFile(iconPath, iconData)
51 | .then(() => {
52 | return this.groupIconSet(id, normColor);
53 | }).then(() => {
54 | resolve();
55 | }).catch((e) => {
56 | reject(e);
57 | });
58 | });
59 | }
60 | public isColor(colorDef: string): boolean {
61 | try {
62 | const c = Color(colorDef);
63 | const rgb = c.rgb().string(4);
64 | return true;
65 | } catch (e) {
66 |
67 | return false;
68 | }
69 |
70 | // colorDef.match(new RegExp(/#[0-9a-f]{6}/, "i"));
71 | }
72 | public normalizeColor(color: string): string {
73 | try {
74 | const c = Color(color);
75 | const rgb = c.rgb().string(4);
76 | return rgb;
77 | } catch (e) {
78 | return null;
79 | }
80 | }
81 | public removeColor(id: string) {
82 | return this.groupIconRemove(id);
83 | }
84 | private groupIconRemove(id: string) {
85 | return new Promise((resolve, reject) => {
86 |
87 | Promise.all([
88 | this.favorites.get(),
89 | ]).then((results) => {
90 | const all = results[0];
91 | const i = all.findIndex((d) => d.id === id);
92 |
93 | if (i < 0) {
94 | reject("no_item");
95 | return;
96 | }
97 |
98 | delete all[i].iconColor;
99 |
100 | return this.favorites.save(all);
101 |
102 | }).then(() => {
103 | resolve();
104 | }).catch((e) => {
105 | reject(e);
106 | });
107 |
108 | });
109 | }
110 | private groupIconSet(id: string, normalizedColor: string) {
111 | return new Promise((resolve, reject) => {
112 |
113 | Promise.all([
114 | this.favorites.get(),
115 | ]).then((results) => {
116 | const all = results[0];
117 | const i = all.findIndex((d) => d.id === id);
118 |
119 | if (i < 0) {
120 | reject("no_item");
121 | return;
122 | }
123 |
124 | all[i].iconColor = normalizedColor;
125 |
126 | return this.favorites.save(all);
127 |
128 | }).then(() => {
129 | resolve();
130 | }).catch((e) => {
131 | reject(e);
132 | });
133 |
134 | });
135 | }
136 | private build(fillColor: string) {
137 | const o = `
138 |
139 |
142 |
143 |
147 |
151 |
152 |
153 |
154 | `;
155 | return o;
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/class/storage.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs-extra";
2 | import * as path from "path";
3 | import * as vscode from "vscode";
4 |
5 | import { from, Subject } from "rxjs";
6 | import { concatMap, tap } from "rxjs/operators";
7 | import { ResourceType, StoredResource } from "../types";
8 | import workspace from "./workspace";
9 |
10 | export class FavoriteStorage {
11 | public defaultRelativePath = ".favorites.json";
12 | public storageFilePath: string = null;
13 | public eventChange = new Subject();
14 | private eventDestroy = new Subject();
15 |
16 | constructor(context: vscode.ExtensionContext) {
17 |
18 | this.reloadStoragePath();
19 |
20 | }
21 | public reloadStoragePath() {
22 | const configRelativePath = workspace.get("storageFilePath");
23 | const execRelativePath = configRelativePath || this.defaultRelativePath;
24 | this.storageFilePath = path.join(workspace.getConfigurationRoot(), execRelativePath);
25 | }
26 | public get(): Promise {
27 | return new Promise((resolve, reject) => {
28 |
29 | if (!fs.existsSync(this.storageFilePath)) {
30 | resolve([]);
31 | return;
32 | }
33 |
34 | from(fs.readJson(this.storageFilePath)).pipe(
35 | concatMap((result) => this.___fixItems(result)),
36 | ).subscribe((result) => {
37 | resolve(result);
38 | }, (e) => {
39 | console.log(e);
40 | resolve([] as StoredResource[]);
41 | });
42 |
43 | });
44 | }
45 | public storageName(fromPath?: string) {
46 | const sp = fromPath == null ? this.storageFilePath : fromPath;
47 | const e = path.extname(sp);
48 | const b = path.basename(sp);
49 | const rx = new RegExp(`${e}$`, "i");
50 | const out = b.replace(rx, "");
51 |
52 | return out;
53 | }
54 | public save(list: StoredResource[], triggerChange: boolean = true): Promise {
55 | return new Promise((resolve, reject) => {
56 |
57 | const data = !list ? [] : list;
58 |
59 | const dir = path.dirname(this.storageFilePath);
60 |
61 | from(fs.mkdirp(dir)).pipe(
62 | concatMap(() => fs.writeJson(this.storageFilePath, data, {
63 | spaces: 4,
64 | })),
65 | tap(() => {
66 | if (triggerChange) {
67 | this.eventChange.next();
68 | }
69 | }),
70 | ).subscribe(() => {
71 | resolve();
72 | }, (e) => {
73 | console.log(e);
74 | reject(e);
75 | });
76 |
77 | });
78 | }
79 | public destroy() {
80 | this.eventDestroy.next();
81 | }
82 | private convertLegacy(list: StoredResource[]) {
83 | fs.writeJsonSync(this.storageFilePath, list, {
84 | spaces: 4,
85 | });
86 | }
87 | private ___fixItems(list: StoredResource[]): Promise {
88 | return new Promise((resolve, reject) => {
89 |
90 | const result = list.map((item) => {
91 |
92 | const wRoot = workspace.workspaceRoot(item.name);
93 | const wPath = workspace.workspacePath(item.name);
94 |
95 | if (item.fsPath || item.type === ResourceType.Group) {
96 | item.workspaceRoot = null;
97 | item.workspacePath = null;
98 | return item;
99 | }
100 | if (wRoot && item.workspaceRoot == null && item.workspacePath == null && item.fsPath == null) {
101 | item.workspaceRoot = wRoot;
102 | item.workspacePath = wPath;
103 | return item;
104 | }
105 | if (!wRoot && item.workspaceRoot == null && item.workspacePath == null && item.fsPath == null) {
106 | item.fsPath = item.name;
107 | item.workspacePath = null;
108 | item.workspaceRoot = null;
109 | return item;
110 | }
111 |
112 | if (item.workspaceRoot == null && item.fsPath == null) {
113 | const itemFsPath = workspace.pathAbsolute(item.workspacePath || item.name);
114 | item.workspaceRoot = workspace.workspaceRoot(itemFsPath);
115 | item.workspacePath = workspace.workspacePath(itemFsPath);
116 | return item;
117 |
118 | }
119 |
120 | return item;
121 |
122 | });
123 |
124 | this.save(result, false)
125 | .then(() => {
126 | resolve(result);
127 | }).catch((e) => {
128 | reject(e);
129 | });
130 |
131 | });
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/src/class/tree.ts:
--------------------------------------------------------------------------------
1 | import { fromEventPattern, merge, Subject, zip } from "rxjs";
2 | import { filter, takeUntil } from "rxjs/operators";
3 | import * as vscode from "vscode";
4 | import { ResourceType } from "../types/index";
5 | import { DataProvider } from "./dataProvider";
6 | import { Favorites } from "./favorites";
7 | import { Global } from "./global";
8 | import { ViewItem } from "./view-item";
9 | import workspace from "./workspace";
10 |
11 | export class TreeViewManager {
12 | public visible: boolean = false;
13 | public activeEditor: vscode.TextEditor = null;
14 | private eventVisibility = new Subject();
15 | private eventActiveEditor = new Subject();
16 |
17 | constructor(
18 | private treeView: vscode.TreeView,
19 | private context: vscode.ExtensionContext,
20 | private favorites: Favorites,
21 | private treeProvider: DataProvider,
22 | ) {
23 |
24 | this.visible = this.treeView.visible;
25 | this.activeEditor = vscode.window.activeTextEditor;
26 |
27 | fromEventPattern((f: (e: any) => any) => {
28 | return this.treeView.onDidChangeVisibility(f, null, context.subscriptions);
29 | }, (f: any, d: vscode.Disposable) => {
30 | d.dispose();
31 | }).pipe(
32 | takeUntil(Global.eventDeactivate),
33 | ).subscribe((m) => {
34 | this.visible = m.visible;
35 | this.eventVisibility.next(m.visible);
36 | });
37 |
38 | fromEventPattern((f: (e: any) => any) => {
39 | return vscode.window.onDidChangeActiveTextEditor(f, null, context.subscriptions);
40 | }, (f: any, d: vscode.Disposable) => {
41 | d.dispose();
42 | }).pipe(
43 | takeUntil(Global.eventDeactivate),
44 | ).subscribe((m) => {
45 | this.activeEditor = m;
46 | this.eventActiveEditor.next(m);
47 | });
48 |
49 | merge(
50 | this.eventActiveEditor.pipe(),
51 | this.eventVisibility.pipe(),
52 | ).pipe(
53 | takeUntil(Global.eventDeactivate),
54 | filter(() => {
55 | return (
56 | this.activeEditor != null &&
57 | this.activeEditor.document != null &&
58 | this.activeEditor.document.uri.fsPath != null
59 | );
60 | }),
61 | filter(() => this.visible === true),
62 | ).subscribe((editor) => {
63 | this.reveal(this.activeEditor.document.uri.fsPath);
64 | });
65 |
66 | this.eventActiveEditor.next(this.activeEditor);
67 | }
68 | public reveal(fsPath: string) {
69 |
70 | if (workspace.excludeCheck.isExcluded(fsPath)) {
71 | return;
72 | }
73 |
74 | zip(
75 | this.favorites.identify(fsPath),
76 | this.favorites.stateList,
77 | this.favorites.isPartOfFavorites(fsPath),
78 | ).subscribe((result) => {
79 | const type = result[0];
80 | const list = result[1];
81 | const isPart = result[2];
82 |
83 | // try to locate in favorite items! directly added
84 | const fileItem = list.find((f) => f.name === fsPath && f.type === ResourceType.File);
85 |
86 | // if file and in root!
87 | if (fileItem && type === ResourceType.File) {
88 | // it is file in root!
89 | const viewItem = this.favorites.asViewItem(fileItem);
90 | this.treeView.reveal(viewItem, { select: true, focus: false });
91 | return;
92 | }
93 |
94 | if (isPart) {
95 | this.favorites.viewItemForPath(fsPath)
96 | .then((item) => {
97 | this.treeView.reveal(item, { select: true, focus: false });
98 | }).catch((e) => {
99 | console.log(e);
100 | });
101 |
102 | }
103 |
104 | }, (e) => {
105 | console.log(e);
106 | });
107 |
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/class/view-item.ts:
--------------------------------------------------------------------------------
1 |
2 | import * as path from "path";
3 | import { take } from "rxjs/operators";
4 | import * as vscode from "vscode";
5 | import { ResourceType } from "../types/index";
6 | import { Favorites } from "./favorites";
7 |
8 | export type ViewItemContextValue = "FAVORITE_GROUP" | "FAVORITE_DIRECTORY" | "FAVORITE_FILE" | "FS_DIRECTORY" | "FS_FILE";
9 |
10 | export class ViewItem extends vscode.TreeItem {
11 | public static favorites: Favorites;
12 | public static context: vscode.ExtensionContext;
13 |
14 | public resourceUri: vscode.Uri;
15 | public groupName: string;
16 | public parentViewItem: ViewItem = null;
17 |
18 | constructor(
19 | public label: string,
20 | public collapsibleState: vscode.TreeItemCollapsibleState,
21 | public value: string,
22 | public contextValue: ViewItemContextValue,
23 | public resourceName: string,
24 | public resourceType: ResourceType,
25 | public icon?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri },
26 | public command?: vscode.Command,
27 | public id?: string,
28 | public parentId?: string,
29 | public tooltipText?: string,
30 | ) {
31 | super(label, collapsibleState);
32 |
33 | this.resourceUri = vscode.Uri.file(value);
34 | this.tooltip = value;
35 | this.iconPath = icon;
36 | this.tooltip = tooltipText;
37 | }
38 | public get isFavorite() {
39 | return this.contextValue === "FAVORITE_DIRECTORY" || this.contextValue === "FAVORITE_FILE";
40 | }
41 | public getParent(): Promise {
42 |
43 | if (
44 | this.contextValue === "FAVORITE_DIRECTORY" ||
45 | this.contextValue === "FAVORITE_FILE" ||
46 | this.contextValue === "FAVORITE_GROUP"
47 | ) {
48 |
49 | if (this.parentId == null) {
50 | return null;
51 | }
52 |
53 | return this.getParentForFavorite(this.parentId);
54 |
55 | }
56 |
57 | return new Promise((resolve, reject) => {
58 |
59 | ViewItem.favorites.get()
60 | .then((stored) => {
61 |
62 | this.getParentForFs(this.resourceUri.fsPath)
63 | .then((result) => {
64 |
65 | return Promise.all([
66 | ViewItem.favorites.isPartOfFavorites(result.resourceUri.fsPath),
67 | result,
68 | ]);
69 |
70 | }).then((res) => {
71 |
72 | const check = res[0];
73 | const item = res[1];
74 |
75 | if (check) {
76 | resolve(item);
77 | } else {
78 | resolve(null);
79 | }
80 |
81 | }).catch((e) => {
82 | reject(e);
83 | });
84 |
85 | }).catch((e) => {
86 | reject(e);
87 | });
88 |
89 | });
90 | }
91 | private getParentForFs(fsPath: string): Promise {
92 | return new Promise((resolve, reject) => {
93 | ViewItem.favorites.stateList.pipe(
94 | take(1),
95 | ).subscribe((list) => {
96 |
97 | const dir = path.dirname(fsPath);
98 | const sr = list.find((i) => i.name === dir && i.type === ResourceType.Directory);
99 |
100 | if (sr != null) {
101 | const vi = ViewItem.favorites.asViewItem(sr);
102 | resolve(vi);
103 | return;
104 | }
105 |
106 | const pvi = ViewItem.favorites.viewItemForPath(dir);
107 | resolve(pvi);
108 |
109 | }, (e) => {
110 | reject(e);
111 | });
112 | });
113 | }
114 | private getParentForFavorite(parentId: string): Promise {
115 | return new Promise((resolve, reject) => {
116 | ViewItem.favorites.stateList.pipe(
117 | take(1),
118 | ).subscribe((list) => {
119 |
120 | const p = list.find((item) => item.id === parentId);
121 | const vi = ViewItem.favorites.asViewItem(p);
122 | resolve(vi);
123 | }, (e) => {
124 | reject(e);
125 | });
126 | });
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/class/workspace.ts:
--------------------------------------------------------------------------------
1 | import * as os from "os";
2 | import * as path from "path";
3 | import { fromEventPattern, Subject } from "rxjs";
4 | import { takeUntil } from "rxjs/operators";
5 | import * as vscode from "vscode";
6 | import { WorkspaceConfiguration } from "../types/index";
7 | import { ExcludeCheck } from "./exclude-check";
8 | import { Global } from "./global";
9 |
10 | export class Workspace {
11 |
12 | public eventConfigurationChange = new Subject();
13 | public excludeCheck = new ExcludeCheck(this);
14 |
15 | public constructor() {
16 |
17 | fromEventPattern((f) => {
18 | return vscode.workspace.onDidChangeConfiguration(f as any);
19 | }, (f: any, d: vscode.Disposable) => {
20 | d.dispose();
21 | }).pipe(
22 | takeUntil(Global.eventDeactivate),
23 | ).subscribe((e) => {
24 | this.excludeCheck = new ExcludeCheck(this);
25 | this.eventConfigurationChange.next();
26 | });
27 | }
28 |
29 | public get(key: T): WorkspaceConfiguration[T] {
30 | const config = vscode.workspace.getConfiguration("favorites");
31 | return config.get(key) as T;
32 | }
33 | public getGlobal(key: string): any {
34 | const config = vscode.workspace.getConfiguration();
35 | return config.get(key);
36 | }
37 |
38 | public save(key: T, value: WorkspaceConfiguration[T]): Promise {
39 | const config = vscode.workspace.getConfiguration("favorites");
40 |
41 | config.update(key, value, false);
42 | return Promise.resolve();
43 |
44 | }
45 | public getConfigurationRoot() {
46 | const workspaceIndex = this.get("useWorkspace");
47 | const rootPath = vscode.workspace.workspaceFolders[workspaceIndex] != null
48 | ? vscode.workspace.workspaceFolders[workspaceIndex].uri.fsPath
49 | : vscode.workspace.workspaceFolders[0].uri.fsPath;
50 |
51 | return rootPath;
52 | }
53 | public getSingleRootPath(): string {
54 | return vscode.workspace.workspaceFolders[0].uri.fsPath;
55 | }
56 |
57 | public isMultiRootWorkspace(): boolean {
58 | return vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1;
59 | }
60 |
61 | public pathResolve(filePath: string) {
62 | if (this.isMultiRootWorkspace()) {
63 | return filePath;
64 | }
65 | return path.resolve(vscode.workspace.workspaceFolders[0].uri.fsPath, filePath);
66 | }
67 | public pathAsUri(fsPath: string): vscode.Uri {
68 | let uri = vscode.Uri.parse(`file://${this.pathResolve(fsPath)}`);
69 | if (os.platform().startsWith("win")) {
70 | uri = vscode.Uri.parse(`file:///${this.pathResolve(fsPath)}`.replace(/\\/g, "/"));
71 | }
72 |
73 | return uri;
74 | }
75 |
76 | public pathForWorkspace(fsPath: string) {
77 | const isMultiRoot = this.isMultiRootWorkspace();
78 | const wp = isMultiRoot ? fsPath : this.pathResolve(fsPath).substr(this.getSingleRootPath().length + 1);
79 | return wp;
80 | }
81 | public pathAbsolute(workspacePath: string) {
82 | if (this.isMultiRootWorkspace()) {
83 | return workspacePath;
84 | }
85 | return path.resolve(vscode.workspace.workspaceFolders[0].uri.fsPath, workspacePath);
86 | }
87 | // -----------------------------------------------------------------------
88 | public workspaceRoot(fsPath): string {
89 | const roots = vscode.workspace.workspaceFolders.map((i) => i.uri.fsPath);
90 | for (let i = 0; i < roots.length; i++) {
91 | const p = roots[i];
92 | const r = fsPath.search(p);
93 | if (r === 0) {
94 | return p;
95 | }
96 | }
97 |
98 | return null;
99 |
100 | }
101 | public workspacePath(fsPath): string {
102 | const w = this.workspaceRoot(fsPath);
103 |
104 | if (w) {
105 | return path.relative(w, fsPath);
106 | }
107 |
108 | return null;
109 | }
110 |
111 | }
112 |
113 | export default new Workspace();
114 |
--------------------------------------------------------------------------------
/src/command/index.ts:
--------------------------------------------------------------------------------
1 | import * as clipboardy from "clipboardy";
2 | import * as path from "path";
3 | import { of } from "rxjs";
4 | import { concatMap } from "rxjs/operators";
5 | import * as vscode from "vscode";
6 | import { QuickPickItem } from "vscode";
7 | import { Clipboard } from "../class/clipboard";
8 | import { Favorites } from "../class/favorites";
9 | import { FavoritesNavigator, FavoritesNavigatorQuickPickItem } from "../class/favorites-navigator";
10 | import { FilesystemUtils } from "../class/filesystem";
11 | import { GroupColor } from "../class/group-color";
12 | import { FavoriteStorage } from "../class/storage";
13 | import { ViewItem } from "../class/view-item";
14 | import workspace from "../class/workspace";
15 | import { RegistryQuickPickItem, ResourceType, TreeProviders, WorkspaceQuickPickItem } from "../types/index";
16 |
17 | export class Commands {
18 | private clipboard = new Clipboard();
19 | private filesystem: FilesystemUtils = null;
20 | private groupColor: GroupColor = null;
21 |
22 | constructor(
23 | private context: vscode.ExtensionContext,
24 | public providers: TreeProviders,
25 | private favorites: Favorites,
26 | private storage: FavoriteStorage,
27 | ) {
28 |
29 | this.filesystem = new FilesystemUtils(favorites);
30 | this.groupColor = new GroupColor(favorites, context);
31 |
32 | context.subscriptions.push(this.selectRegistryItem());
33 | context.subscriptions.push(this.favoritesRefresh());
34 | context.subscriptions.push(this.selectWorkspace());
35 | context.subscriptions.push(this.addExternal());
36 | context.subscriptions.push(this.addToFavorites());
37 | context.subscriptions.push(this.addToFavoritesGroup());
38 | context.subscriptions.push(this.addFileToFavorites());
39 | context.subscriptions.push(this.addFileToFavoritesGroup());
40 | context.subscriptions.push(this.deleteFavorite());
41 | context.subscriptions.push(this.setSortAsc());
42 | context.subscriptions.push(this.setSortDesc());
43 | context.subscriptions.push(this.collapse());
44 | context.subscriptions.push(this.collapseActivityView());
45 | context.subscriptions.push(this.createGroup());
46 | context.subscriptions.push(this.deleteGroup());
47 | context.subscriptions.push(this.deleteAllFavorites());
48 | context.subscriptions.push(this.groupRename());
49 | context.subscriptions.push(this.aliasModify());
50 | context.subscriptions.push(this.aliasRemove());
51 |
52 | context.subscriptions.push(this.copyPath());
53 | context.subscriptions.push(this.copyName());
54 | context.subscriptions.push(this.fsCopy());
55 | context.subscriptions.push(this.fsCut());
56 | context.subscriptions.push(this.fsPaste());
57 | context.subscriptions.push(this.fsCreateDirectory());
58 | context.subscriptions.push(this.fsCreateFile());
59 | context.subscriptions.push(this.fsDuplicate());
60 | context.subscriptions.push(this.fsDelete());
61 | context.subscriptions.push(this.fsRename());
62 | context.subscriptions.push(this.groupColorSet());
63 | context.subscriptions.push(this.favoritesBrowse());
64 | }
65 | public favoritesBrowse = () => {
66 | return vscode.commands.registerCommand("favorites.browse",
67 | () => {
68 |
69 | Promise.all([])
70 | .then(async () => {
71 |
72 | const navigator = new FavoritesNavigator(this.favorites);
73 | const editor = vscode.window.activeTextEditor;
74 | const editorFsPath = editor ? path.dirname(editor.document.uri.fsPath) : null;
75 | const isPart = await this.favorites.isPartOfFavorites(editorFsPath);
76 | const item = isPart ? await this.favorites.viewItemForPath(editorFsPath) : null;
77 | const viewColumn = editor ? editor.viewColumn : null;
78 |
79 | const display = (startItem: ViewItem) => {
80 |
81 | of(true).pipe(
82 | concatMap(() => navigator.build(startItem)),
83 | ).subscribe((list) => {
84 | vscode.window.showQuickPick(list, {
85 | canPickMany: false,
86 | placeHolder: "Type to filter",
87 | }).then((result) => {
88 | if (!result) {
89 | return;
90 | }
91 |
92 | if (result.upLevel) {
93 | display(result.parent);
94 | return;
95 | }
96 |
97 | const selItem = result.viewItem;
98 | switch (selItem.resourceType) {
99 | case ResourceType.Group:
100 | display(selItem);
101 | break;
102 | case ResourceType.Directory:
103 | display(selItem);
104 | break;
105 | case ResourceType.File:
106 | vscode.window.showTextDocument(selItem.resourceUri, {
107 | viewColumn,
108 | }).then(() => {
109 |
110 | }, (e) => {
111 | console.log(e);
112 | });
113 | break;
114 | }
115 |
116 | });
117 | });
118 |
119 | };
120 |
121 | display(item);
122 |
123 | }).catch((e) => {
124 | console.log(e);
125 | });
126 |
127 | });
128 | }
129 | public favoritesRefresh = () => {
130 | return vscode.commands.registerCommand("favorites.refresh",
131 | () => {
132 | this.providers.refresh();
133 | });
134 | }
135 | public selectRegistryItem = () => {
136 | return vscode.commands.registerCommand("favorites.selectFromRegistry",
137 | () => {
138 |
139 | const list: RegistryQuickPickItem[] = workspace.get("storageRegistry").map((p, i) => {
140 | const out: RegistryQuickPickItem = {
141 | index: i,
142 | label: `${this.storage.storageName(p)}`,
143 | description: `${p}`,
144 | relativePath: p,
145 |
146 | };
147 | return out;
148 | });
149 |
150 | vscode.window.showQuickPick(list, {
151 | matchOnDescription: true,
152 | matchOnDetail: true,
153 | placeHolder: "Select alternative storage from registry",
154 | }).then((result) => {
155 | if (!result) {
156 | return;
157 | }
158 |
159 | workspace.save("storageFilePath", result.relativePath);
160 | return this.providers.refresh();
161 | }).then(() => {
162 | console.log("Active storageFilePath changed");
163 | });
164 |
165 | });
166 | }
167 | public selectWorkspace = () => {
168 | return vscode.commands.registerCommand("favorites.selectWorkspace",
169 | () => {
170 |
171 | const list: WorkspaceQuickPickItem[] = vscode.workspace.workspaceFolders.map((wf, i) => {
172 | const out: WorkspaceQuickPickItem = {
173 | index: i,
174 | label: `${wf.uri.fsPath}`,
175 |
176 | };
177 | return out;
178 | });
179 |
180 | vscode.window.showQuickPick(list, {
181 | matchOnDescription: true,
182 | matchOnDetail: true,
183 | placeHolder: "Select workspace folder for Favorites",
184 | }).then((result) => {
185 | if (!result) {
186 | return;
187 | }
188 |
189 | return workspace.save("useWorkspace", result.index);
190 | }).then(() => {
191 | console.log("Active folder changed");
192 | });
193 |
194 | });
195 | }
196 |
197 | public copyPath = () => {
198 | return vscode.commands.registerCommand("favorites.copy.path",
199 | (value: ViewItem) => {
200 | if (value == null) {
201 | return;
202 | }
203 | const fsPath = value.resourceUri.fsPath;
204 | console.log(path);
205 | try {
206 | clipboardy.writeSync(fsPath);
207 | } catch (e) {
208 | console.log(e);
209 | }
210 |
211 | });
212 | }
213 | public copyName = () => {
214 | return vscode.commands.registerCommand("favorites.copy.name",
215 | (value: ViewItem) => {
216 | if (value == null) {
217 | return;
218 | }
219 | const name = path.basename(value.resourceUri.fsPath);
220 | console.log(path);
221 | clipboardy.writeSync(name);
222 |
223 | });
224 | }
225 | public addExternal = () => {
226 | //
227 | return vscode.commands.registerCommand("favorites.addExternal",
228 | (value: ViewItem) => {
229 | console.log("favorites.addExternal");
230 | console.log(value);
231 | const shouldExit = (value == null || value.contextValue === "FAVORITE_GROUP") ? false : true;
232 |
233 | if (shouldExit) {
234 | return;
235 | }
236 |
237 | const parentId = value == null ? null : value.id;
238 |
239 | Promise.all([vscode.window.showInputBox({
240 | prompt: "New external resource",
241 | placeHolder: "Enter file or directory path",
242 | })]).then((args) => {
243 |
244 | const pathToAdd = args[0];
245 |
246 | if (pathToAdd == null || pathToAdd.trim() === "") {
247 | // vscode.window.showWarningMessage("Invalid path");
248 | return;
249 | }
250 |
251 | return this.favorites.addExternalPathToGroup(parentId, pathToAdd);
252 |
253 | }).catch((e) => {
254 | vscode.window.showErrorMessage(e);
255 | });
256 |
257 | });
258 | }
259 |
260 | public groupColorSet = () => {
261 | return vscode.commands.registerCommand("favorites.group.color.set",
262 | (value: ViewItem) => {
263 |
264 | const list: QuickPickItem[] = [
265 | {
266 | label: "None",
267 | description: "Remove color from group",
268 | },
269 | {
270 | label: "Custom",
271 | description: "Use custom color (hex or rgb/rgba)",
272 | },
273 | ];
274 |
275 | const colors: QuickPickItem[] = Object.keys(this.groupColor.colorList).map((k) => {
276 | const o: QuickPickItem = {
277 | label: k,
278 | description: this.groupColor.colorList[k],
279 | };
280 | return o;
281 | });
282 | const all = list.concat(colors);
283 |
284 | vscode.window.showQuickPick(all, {
285 | matchOnDescription: true,
286 | matchOnDetail: true,
287 | }).then((result) => {
288 |
289 | if (result == null) {
290 | return Promise.resolve(null);
291 | }
292 | if (result.label === "None") {
293 | return this.groupColor.removeColor(value.id);
294 | }
295 | if (result.label === "Custom") {
296 | return vscode.window.showInputBox({
297 | prompt: "New group color",
298 | placeHolder: "Enter hex or rgb/rgba value",
299 | });
300 | }
301 | // then desc = #hex
302 | return Promise.resolve(result.description);
303 |
304 | }).then((input: string) => {
305 | if (input == null) {
306 | // no action
307 | return Promise.resolve();
308 | }
309 |
310 | return this.groupColor.setColor(value.id, input);
311 | }).then(() => {
312 | console.log("all ok");
313 | });
314 | });
315 | }
316 | public fsCreateFile = () => {
317 | return vscode.commands.registerCommand("filesystem.create.file",
318 | (value: ViewItem) => {
319 | if (value == null) {
320 | return;
321 | }
322 | this.filesystem.createFile(value)
323 | .then((result) => {
324 | this.providers.refresh();
325 | })
326 | .catch((e) => {
327 | this.providers.refresh();
328 | console.log(e);
329 | });
330 | });
331 | }
332 | public fsCreateDirectory = () => {
333 | return vscode.commands.registerCommand("filesystem.create.directory",
334 | (value: ViewItem) => {
335 | this.filesystem.createDirectory(value)
336 | .then((result) => {
337 | this.providers.refresh();
338 | })
339 | .catch((e) => {
340 | this.providers.refresh();
341 | console.log(e);
342 | });
343 | });
344 | }
345 | public fsCopy = () => {
346 | return vscode.commands.registerCommand("filesystem.copy",
347 | // tslint:disable-next-line:no-empty
348 | (value: ViewItem) => {
349 | this.clipboard.copy(value);
350 | });
351 | }
352 | public fsCut = () => {
353 | return vscode.commands.registerCommand("filesystem.cut",
354 | // tslint:disable-next-line:no-empty
355 | (value: ViewItem) => {
356 | this.clipboard.cut(value);
357 | });
358 | }
359 | public fsPaste = () => {
360 | return vscode.commands.registerCommand("filesystem.paste",
361 | // tslint:disable-next-line:no-empty
362 | (value: ViewItem) => {
363 | const state = this.clipboard.get();
364 | if (state == null) {
365 | return;
366 | }
367 | switch (state.operation) {
368 | case "copy":
369 | this.filesystem.copy(state.item, value)
370 | .then((result) => {
371 | this.providers.refresh();
372 | // this.clipboard.reset();
373 | })
374 | .catch((e) => {
375 | console.log(e);
376 | });
377 | break;
378 | case "cut":
379 | this.filesystem.move(state.item, value)
380 | .then((result) => {
381 | this.providers.refresh();
382 | this.clipboard.reset();
383 | })
384 | .catch((e) => {
385 | console.log(e);
386 | });
387 | break;
388 | }
389 |
390 | });
391 | }
392 | public fsDelete = () => {
393 | return vscode.commands.registerCommand("filesystem.delete",
394 | // tslint:disable-next-line:no-empty
395 | (value: ViewItem) => {
396 | this.filesystem.delete(value)
397 | .then((result) => {
398 | this.providers.refresh();
399 | })
400 | .catch((e) => {
401 | this.providers.refresh();
402 | console.log(e);
403 | });
404 | });
405 | }
406 | public fsRename = () => {
407 | return vscode.commands.registerCommand("filesystem.rename",
408 | // tslint:disable-next-line:no-empty
409 | (value: ViewItem) => {
410 | this.filesystem.rename(value)
411 | .then((result) => {
412 | this.providers.refresh();
413 | })
414 | .catch((e) => {
415 | this.providers.refresh();
416 | console.log(e);
417 | });
418 | });
419 | }
420 | public fsDuplicate = () => {
421 | return vscode.commands.registerCommand("filesystem.duplicate",
422 | // tslint:disable-next-line:no-empty
423 | (value: ViewItem) => {
424 | this.filesystem.duplicate(value)
425 | .then((result) => {
426 | this.providers.refresh();
427 | })
428 | .catch((e) => {
429 | this.providers.refresh();
430 | console.log(e);
431 | });
432 | });
433 | }
434 | public aliasRemove = () => {
435 | return vscode.commands.registerCommand("favorites.alias.remove",
436 | (value: ViewItem) => {
437 |
438 | this.favorites.labelModify(value.id, null)
439 | .then((result) => {
440 | this.providers.refresh();
441 | })
442 | .catch((e) => {
443 | console.log(e);
444 | });
445 |
446 | });
447 |
448 | }
449 | public aliasModify = () => {
450 | return vscode.commands.registerCommand("favorites.alias.modify",
451 | (value: ViewItem) => {
452 |
453 | this.favorites.get()
454 | .then((result) => {
455 | const item = result.find((r) => r.id === value.id);
456 | const oldVal = (item != null && item.label != null) ? item.label : "";
457 | vscode.window.showInputBox({ prompt: "Enter alias", value: oldVal })
458 | .then((name) => {
459 | if (!name || name.trim().length === 0) {
460 | return;
461 | }
462 | const tname = name.trim();
463 | this.favorites.labelModify(value.id, tname);
464 | this.providers.refresh();
465 | });
466 |
467 | })
468 | .catch((e) => {
469 | console.log(e);
470 | });
471 |
472 | });
473 |
474 | }
475 | public groupRename = () => {
476 | return vscode.commands.registerCommand("favorites.group.rename",
477 | (value: ViewItem) => {
478 | vscode.window.showInputBox({ prompt: "Enter new group name" })
479 | .then((name) => {
480 | if (!name || name.trim().length === 0) {
481 | return;
482 | }
483 | const tname = name.trim();
484 | this.favorites.groupRename(value.id, tname);
485 | this.providers.refresh();
486 | });
487 |
488 | });
489 |
490 | }
491 | addToFavorites = () => {
492 | return vscode.commands.registerCommand("favorites.addToFavorites", (fileUri: vscode.Uri, list: any[]) => {
493 |
494 | const isList = Array.isArray(list);
495 | const isFile = (fileUri && fileUri.fsPath) != null ? true : false;
496 | const isActiveEditor = vscode.window.activeTextEditor != null ? true : false;
497 |
498 | if (!isList && isFile) {
499 | list = [fileUri];
500 | }
501 | if (!isList && !isFile && isActiveEditor) {
502 | list = [vscode.window.activeTextEditor.document.uri];
503 | }
504 |
505 | const run = async () => {
506 |
507 | for (const uri of list) {
508 | const itemPath = uri.fsPath;
509 | const workspacePath = workspace.workspaceRoot(itemPath);
510 | if (workspacePath) {
511 | await this.favorites.addPathToGroup(null, itemPath);
512 | } else {
513 | await this.favorites.addExternalPathToGroup(null, itemPath);
514 | }
515 | }
516 | };
517 |
518 | run();
519 |
520 | });
521 | }
522 | addToFavoritesGroup = () => {
523 | return vscode.commands.registerCommand("favorites.addToFavoritesGroup", (fileUri: vscode.Uri, list: any[]) => {
524 |
525 | const isList = Array.isArray(list);
526 | const isFile = (fileUri && fileUri.fsPath) != null ? true : false;
527 | const isActiveEditor = vscode.window.activeTextEditor != null ? true : false;
528 |
529 | if (!isList && isFile) {
530 | list = [fileUri];
531 | }
532 | if (!isList && !isFile && isActiveEditor) {
533 | list = [vscode.window.activeTextEditor.document.uri];
534 | }
535 |
536 | const run = async (group_id: string) => {
537 |
538 | for (const uri of list) {
539 | const itemPath = uri.fsPath;
540 | const workspacePath = workspace.workspaceRoot(itemPath);
541 |
542 | if (workspacePath) {
543 | await this.favorites.addPathToGroup(group_id, itemPath);
544 | } else {
545 | await this.favorites.addExternalPathToGroup(group_id, itemPath);
546 | }
547 | }
548 | };
549 |
550 | this.favorites.generateGroupQuickPickList()
551 | .then((result) => {
552 |
553 | if (result.length === 0) {
554 | vscode.window.showWarningMessage("No group definition found. Create group first.");
555 | return;
556 | }
557 | vscode.window.showQuickPick(result)
558 | .then((pickedItem) => {
559 | if (pickedItem == null) {
560 | // canceled
561 | return;
562 | }
563 | run(pickedItem.id);
564 | });
565 |
566 | })
567 | .catch((e) => {
568 | console.log(e);
569 | });
570 |
571 | });
572 | }
573 |
574 | addFileToFavorites = () => {
575 | // Note implementation is duplicated verbatim from addToFavorites. TODO: figure out how to call same code to prevent needless code duplication.
576 | return vscode.commands.registerCommand("favorites.addFileToFavorites", (fileUri: vscode.Uri, list: any[]) => {
577 |
578 | const isList = Array.isArray(list);
579 | const isFile = (fileUri && fileUri.fsPath) != null ? true : false;
580 | const isActiveEditor = vscode.window.activeTextEditor != null ? true : false;
581 |
582 | if (!isList && isFile) {
583 | list = [fileUri];
584 | }
585 | if (!isList && !isFile && isActiveEditor) {
586 | list = [vscode.window.activeTextEditor.document.uri];
587 | }
588 |
589 | const run = async () => {
590 |
591 | for (const uri of list) {
592 | const itemPath = uri.fsPath;
593 | const workspacePath = workspace.workspaceRoot(itemPath);
594 | if (workspacePath) {
595 | await this.favorites.addPathToGroup(null, itemPath);
596 | } else {
597 | await this.favorites.addExternalPathToGroup(null, itemPath);
598 | }
599 | }
600 | };
601 |
602 | run();
603 |
604 | });
605 | }
606 | addFileToFavoritesGroup = () => {
607 | // Note implementation is duplicated verbatim from addToFavoritesGroup. TODO: figure out how to call same code to prevent needless code duplication.
608 | return vscode.commands.registerCommand("favorites.addFileToFavoritesGroup", (fileUri: vscode.Uri, list: any[]) => {
609 |
610 | const isList = Array.isArray(list);
611 | const isFile = (fileUri && fileUri.fsPath) != null ? true : false;
612 | const isActiveEditor = vscode.window.activeTextEditor != null ? true : false;
613 |
614 | if (!isList && isFile) {
615 | list = [fileUri];
616 | }
617 | if (!isList && !isFile && isActiveEditor) {
618 | list = [vscode.window.activeTextEditor.document.uri];
619 | }
620 |
621 | const run = async (group_id: string) => {
622 |
623 | for (const uri of list) {
624 | const itemPath = uri.fsPath;
625 | const workspacePath = workspace.workspaceRoot(itemPath);
626 |
627 | if (workspacePath) {
628 | await this.favorites.addPathToGroup(group_id, itemPath);
629 | } else {
630 | await this.favorites.addExternalPathToGroup(group_id, itemPath);
631 | }
632 | }
633 | };
634 |
635 | this.favorites.generateGroupQuickPickList()
636 | .then((result) => {
637 |
638 | if (result.length === 0) {
639 | vscode.window.showWarningMessage("No group definition found. Create group first.");
640 | return;
641 | }
642 | vscode.window.showQuickPick(result)
643 | .then((pickedItem) => {
644 | if (pickedItem == null) {
645 | // canceled
646 | return;
647 | }
648 | run(pickedItem.id);
649 | });
650 |
651 | })
652 | .catch((e) => {
653 | console.log(e);
654 | });
655 |
656 | });
657 | }
658 | collapse = () => {
659 | return vscode.commands.registerCommand("favorites.collapse", (v) => {
660 |
661 | this.providers.explorer.returnEmpty = true;
662 | this.providers.refresh();
663 |
664 | setTimeout(() => {
665 | this.providers.explorer.returnEmpty = false;
666 | this.providers.refresh();
667 |
668 | }, 400);
669 |
670 | console.log(v);
671 |
672 | });
673 | }
674 | collapseActivityView = () => {
675 | return vscode.commands.registerCommand("favorites.collapse.activity", (v) => {
676 |
677 | this.providers.activity.returnEmpty = true;
678 | this.providers.activity.refresh();
679 |
680 | setTimeout(() => {
681 | this.providers.activity.returnEmpty = false;
682 | this.providers.activity.refresh();
683 |
684 | }, 400);
685 |
686 | console.log(v);
687 |
688 | });
689 | }
690 | deleteFavorite = () => {
691 | return vscode.commands.registerCommand("favorites.deleteFavorite",
692 | (value: ViewItem) => {
693 |
694 | this.favorites.removeResource(value.id);
695 | this.providers.refresh();
696 | });
697 | }
698 | setSortAsc = () => {
699 | return vscode.commands.registerCommand("favorites.nav.sort.az", (value: ViewItem) => {
700 | const config = vscode.workspace.getConfiguration("favorites");
701 |
702 | vscode.commands.executeCommand("setContext", "sort", "ASC");
703 | return config.update("sortDirection", "ASC", false);
704 |
705 | });
706 | }
707 | setSortDesc = () => {
708 | return vscode.commands.registerCommand("favorites.nav.sort.za", (value: ViewItem) => {
709 | const config = vscode.workspace.getConfiguration("favorites");
710 |
711 | vscode.commands.executeCommand("setContext", "sort", "DESC");
712 | config.update("sortDirection", "DESC", false);
713 |
714 | });
715 | }
716 | createGroup = () => {
717 | return vscode.commands.registerCommand("favorites.group.create", (value: ViewItem) => {
718 |
719 | vscode.window.showInputBox({ prompt: "Enter group name" }).then((name) => {
720 | console.log(name);
721 | if (!name || name.trim().length === 0) {
722 | return;
723 | }
724 | const tname = name.trim();
725 | const parentId = value == null ? null : value.id;
726 | this.favorites.addGroup(parentId, tname);
727 |
728 | });
729 | });
730 | }
731 | deleteGroup = () => {
732 | return vscode.commands.registerCommand("favorites.group.delete",
733 | (value: ViewItem) => {
734 | this.favorites.removeResource(value.id);
735 | });
736 | }
737 | deleteAllFavorites = () => {
738 | return vscode.commands.registerCommand("favorites.delete.all", (value: any) => {
739 |
740 | vscode.window.showInputBox({
741 | prompt: "Do you want to remove ALL favorites (including groups)?",
742 | placeHolder: "type 'yes' to remove everything",
743 | })
744 | .then((val) => {
745 | if (val === "yes") {
746 | this.storage.save([])
747 | .then(() => {
748 | vscode.window.showInformationMessage(`All favorites are removed`);
749 |
750 | }).catch((e) => {
751 | console.log(e);
752 | });
753 |
754 | }
755 | });
756 | //
757 |
758 | });
759 | }
760 | }
761 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { merge } from "rxjs";
2 | import { takeUntil, tap } from "rxjs/operators";
3 | import * as vscode from "vscode";
4 | import { DataProvider } from "./class/dataProvider";
5 | import { Favorites } from "./class/favorites";
6 | import { FsWatcher } from "./class/fs-watcher";
7 | import { Global } from "./class/global";
8 | import { FavoriteStorage } from "./class/storage";
9 | import { TreeViewManager } from "./class/tree";
10 | import { ViewItem } from "./class/view-item";
11 | import workspace from "./class/workspace";
12 | import { Commands } from "./command";
13 | import { TreeProviders } from "./types";
14 |
15 | export function activate(context: vscode.ExtensionContext) {
16 |
17 | if (vscode.workspace.workspaceFolders == null || vscode.workspace.workspaceFolders.length === 0) {
18 | return;
19 | }
20 |
21 | const configSort = workspace.get("sortDirection");
22 | let registryList = workspace.get("storageRegistry");
23 | const sort = (configSort === "DESC" || configSort === "ASC") ? configSort : "ASC";
24 |
25 | vscode.commands.executeCommand("setContext", "sort", sort);
26 |
27 | const storage = new FavoriteStorage(context);
28 | const favorites = new Favorites(context, storage);
29 |
30 | ViewItem.favorites = favorites;
31 |
32 | const provider = new DataProvider(favorites);
33 | const providerActivity = new DataProvider(favorites);
34 |
35 | const providers: TreeProviders = {
36 | explorer: provider,
37 | activity: providerActivity,
38 | refresh: () => {
39 | provider.refresh();
40 | providerActivity.refresh();
41 | },
42 | };
43 |
44 | const treeExplorer = vscode.window.createTreeView("favorites", { treeDataProvider: providers.explorer });
45 | const treeActivity = vscode.window.createTreeView("favoritesActivity", { treeDataProvider: providers.activity });
46 |
47 | const managerExplorer = new TreeViewManager(treeExplorer, context, favorites, providers.explorer);
48 | const managerActivity = new TreeViewManager(treeActivity, context, favorites, providers.activity);
49 | const fsWatcher = new FsWatcher(storage);
50 |
51 | const commands = new Commands(context, providers, favorites, storage);
52 |
53 | if (registryList.length > 0) {
54 | statusRegistryCreate();
55 | refreshStatus(storage);
56 | }
57 |
58 | workspace.eventConfigurationChange.pipe(
59 | takeUntil(Global.eventDeactivate),
60 | tap(() => {
61 | registryList = workspace.get("storageRegistry");
62 | if (registryList.length > 0) {
63 | statusRegistryCreate();
64 | } else {
65 | statusRegistryDestroy();
66 | }
67 | }),
68 | ).subscribe(() => {
69 | providers.refresh();
70 | storage.reloadStoragePath();
71 | refreshStatus(storage);
72 | });
73 |
74 | merge(storage.eventChange, fsWatcher.eventFs).pipe(
75 | takeUntil(Global.eventDeactivate),
76 | ).subscribe(() => {
77 | providers.refresh();
78 | });
79 |
80 | }
81 |
82 | function statusRegistryCreate() {
83 |
84 | const status = !Global.statusRegistry ? vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1) : Global.statusRegistry;
85 | Global.statusRegistry = status;
86 |
87 | status.command = "favorites.selectFromRegistry";
88 | status.show();
89 | }
90 | function statusRegistryDestroy() {
91 |
92 | const status = Global.statusRegistry;
93 |
94 | if (!status) {
95 | return;
96 | }
97 |
98 | status.hide();
99 | status.dispose();
100 | Global.statusRegistry = null;
101 | }
102 |
103 | function refreshStatus(storage: FavoriteStorage) {
104 | const status = Global.statusRegistry;
105 |
106 | if (!status) {
107 | return;
108 | }
109 |
110 | const name = storage.storageName();
111 | status.text = `$(heart) ${name}`;
112 | status.tooltip = `${storage.storageFilePath}`;
113 | }
114 |
115 | export function deactivate() {
116 | Global.eventDeactivate.next();
117 | }
118 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import { QuickPickItem } from "vscode";
2 | import { DataProvider } from "../class/dataProvider";
3 | import { ViewItem } from "../class/view-item";
4 |
5 | export enum ResourceType {
6 | File = "File",
7 | Group = "Group",
8 | Directory = "Directory",
9 | }
10 |
11 | export interface WorkspaceQuickPickItem extends QuickPickItem {
12 | index: number;
13 | }
14 | export interface RegistryQuickPickItem extends QuickPickItem {
15 | index: number;
16 | relativePath: string;
17 | }
18 |
19 | export interface WorkspaceConfiguration {
20 | useTrash: boolean;
21 | useWorkspace: number;
22 | useFilesExclude: boolean;
23 | storageFilePath: string;
24 | storageRegistry: string[];
25 | groupsFirst: boolean;
26 | sortDirection: "ASC" | "DESC";
27 | includeInDocumentBodyContextMenu: boolean;
28 | includeInEditorTabContextMenu: boolean;
29 | }
30 |
31 | export interface StoredResource {
32 | id?: string;
33 | name: string;
34 | type: ResourceType;
35 | parent_id?: string;
36 | label?: string;
37 | workspaceRoot: string;
38 | workspacePath: string;
39 | fsPath?: string;
40 | iconColor?: string;
41 | }
42 |
43 | export interface FilesystemResource {
44 | path: string;
45 | type: ResourceType;
46 | }
47 |
48 | export interface GroupQuickPick extends QuickPickItem {
49 | id: string;
50 | }
51 |
52 | export interface TreeProviders {
53 | explorer: DataProvider;
54 | activity: DataProvider;
55 | refresh: () => void;
56 | }
57 |
58 | export interface ClipboardBuffer {
59 | item: ViewItem;
60 | operation: "copy" | "cut";
61 | }
62 |
63 | export interface HtmlColor {
64 | [key: string]: string;
65 | }
66 |
--------------------------------------------------------------------------------
/src/types/trash.d.ts:
--------------------------------------------------------------------------------
1 |
2 | // declare module "renderjson" {
3 |
4 | // export default function (data: any);
5 | // export function set_icons(collapse: string, uncollapse: string);
6 | // // set_max_string_length: function()
7 | // // set_property_list: function()
8 | // // set_replacer: function()
9 | // // set_show_by_default: function()
10 | // // set_show_to_level: function()
11 | // // set_sort_objects: function()
12 |
13 | // }
14 |
15 | declare module "trash" {
16 | interface TrashOptions {
17 | glob: boolean;
18 | }
19 | function trash (iterable: Iterable, opts?: TrashOptions): Promise;
20 | export = trash;
21 | }
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "outDir": "./out",
5 | "moduleResolution": "node",
6 | "module": "commonjs",
7 | "emitDecoratorMetadata": true,
8 | "experimentalDecorators": true,
9 | "allowSyntheticDefaultImports": true,
10 | "allowJs": true,
11 | "lib": [
12 | "es2017"
13 | ],
14 | "sourceMap": true,
15 | "rootDir": "src",
16 | "typeRoots": [
17 | "./node_modules/@types"
18 | ]
19 | },
20 | "exclude": [
21 | "node_modules",
22 | "out",
23 | "packed",
24 | "webpack.config.js"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint:recommended",
3 | "rules": {
4 | "directive-selector": [
5 | false,
6 | "attribute",
7 | "foo",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | false,
12 | "element",
13 | "foo",
14 | "kebab-case"
15 | ],
16 | "quotemark": [
17 | true,
18 | "double"
19 | ],
20 | "no-console": [
21 | false,
22 | "debug",
23 | "info",
24 | "time",
25 | "timeEnd",
26 | "trace"
27 | ],
28 | "no-inferrable-types": [
29 | false,
30 | "ignore-params"
31 | ],
32 | "no-input-rename": false,
33 | "import-spacing": false,
34 | "no-output-on-prefix": false,
35 | "no-output-rename": false,
36 | "object-literal-sort-keys": false,
37 | "max-line-length": [
38 | true,
39 | 140
40 | ],
41 | "interface-name": false,
42 | "angular-whitespace": [
43 | false
44 | ],
45 | "no-consecutive-blank-lines": true,
46 | "member-access": false,
47 | "variable-name": false,
48 | "semicolon": [
49 | true,
50 | "always"
51 | ],
52 | "no-empty":false,
53 | "prefer-for-of":false
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 |
2 | /*---------------------------------------------------------------------------------------------
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 |
7 | //@ts-check
8 |
9 | 'use strict';
10 |
11 | const path = require('path');
12 |
13 | /**@type {import('webpack').Configuration}*/
14 | const config = {
15 | target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
16 |
17 | entry: './src/index.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
18 | output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
19 | path: path.resolve(__dirname, 'packed'),
20 | filename: 'index.js',
21 | libraryTarget: "commonjs2",
22 | devtoolModuleFilenameTemplate: "../[resource-path]",
23 | },
24 | devtool: 'source-map',
25 | externals: {
26 | 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/
27 | clipboardy: "clipboardy"
28 | },
29 | resolve: { // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
30 | extensions: ['.ts', '.js']
31 | },
32 | module: {
33 | rules: [{
34 | test: /\.ts$/,
35 | exclude: /node_modules/,
36 | use: [{
37 | loader: 'ts-loader',
38 | }]
39 | }]
40 | },
41 | }
42 |
43 | module.exports = config;
44 |
--------------------------------------------------------------------------------