@lvce-editor/shared-process 0.0.40 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/defaultKeyBindings.json +5 -0
- package/extensions/builtin.language-basics-docker/extension.json +13 -3
- package/extensions/builtin.language-basics-json/extension.json +2 -1
- package/extensions/builtin.language-basics-json/src/tokenizeJson.js +6 -2
- package/extensions/builtin.language-basics-markdown/src/tokenizeMarkdown.js +288 -11
- package/extensions/builtin.language-basics-perl/README.md +14 -0
- package/extensions/builtin.language-basics-perl/extension.json +12 -0
- package/extensions/builtin.language-basics-perl/src/tokenizePerl.js +31 -0
- package/extensions/builtin.language-basics-toml/extension.json +10 -2
- package/extensions/builtin.theme-slime/color-theme.json +4 -0
- package/package.json +6 -6
- package/src/parts/Command/Command.js +5 -18
- package/src/parts/DirentType/DirentType.js +13 -0
- package/src/parts/Download/Download.js +6 -8
- package/src/parts/ExtensionManagement/ExtensionManagement.js +16 -41
- package/src/parts/ExtensionManagement/ExtensionManagementColorTheme.js +4 -6
- package/src/parts/ExtensionManagement/ExtensionManagementIconTheme.js +4 -6
- package/src/parts/ExtensionManifestStatus/ExtensionManifestStatus.js +3 -0
- package/src/parts/FileSystem/FileSystem.js +33 -74
- package/src/parts/Json/Json.js +7 -55
- package/src/parts/JsonError/JsonError.js +70 -0
- package/src/parts/JsonFile/JsonFile.js +5 -5
- package/src/parts/Native/Native.js +2 -6
- package/src/parts/Preferences/Preferences.js +1 -1
- package/src/parts/RecentlyOpened/RecentlyOpened.ipc.js +5 -0
- package/src/parts/RecentlyOpened/RecentlyOpened.js +52 -0
- package/src/parts/Search/Search.js +9 -3
- package/src/parts/Electron/Electron.ipc.js +0 -18
- package/src/parts/Electron/Electron.js +0 -110
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import isObject from 'is-object'
|
|
1
2
|
import { mkdir, readdir, rename, rm } from 'node:fs/promises'
|
|
2
|
-
import { performance } from 'node:perf_hooks'
|
|
3
3
|
import path from 'node:path'
|
|
4
|
-
import
|
|
4
|
+
import { performance } from 'node:perf_hooks'
|
|
5
5
|
import VError from 'verror'
|
|
6
6
|
import * as Debug from '../Debug/Debug.js'
|
|
7
|
-
import * as Error from '../Error/Error.js'
|
|
8
7
|
import * as ReadJson from '../JsonFile/JsonFile.js'
|
|
9
8
|
import * as Path from '../Path/Path.js'
|
|
10
9
|
import * as Platform from '../Platform/Platform.js'
|
|
11
10
|
import * as Queue from '../Queue/Queue.js'
|
|
11
|
+
import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
|
|
12
12
|
|
|
13
13
|
const isLanguageBasics = (extension) => {
|
|
14
14
|
if (extension && extension.id) {
|
|
@@ -61,11 +61,7 @@ export const install = async (id) => {
|
|
|
61
61
|
// type: 'marketplace',
|
|
62
62
|
// })
|
|
63
63
|
} catch (error) {
|
|
64
|
-
throw new
|
|
65
|
-
cause: error,
|
|
66
|
-
code: 'E_EXT_INSTALL_FAILED',
|
|
67
|
-
message: `Failed to install extension "${id}"`,
|
|
68
|
-
})
|
|
64
|
+
throw new VError(error, `Failed to install extension "${id}"`)
|
|
69
65
|
}
|
|
70
66
|
}
|
|
71
67
|
|
|
@@ -78,11 +74,7 @@ export const uninstall = async (id) => {
|
|
|
78
74
|
// if (error.code === 'ENOENT') {
|
|
79
75
|
// return
|
|
80
76
|
// }
|
|
81
|
-
throw new
|
|
82
|
-
cause: error,
|
|
83
|
-
code: 'E_EXT_UNINSTALL_FAILED',
|
|
84
|
-
message: `Failed to uninstall extension "${id}"`,
|
|
85
|
-
})
|
|
77
|
+
throw new VError(error, `Failed to uninstall extension "${id}"`)
|
|
86
78
|
}
|
|
87
79
|
}
|
|
88
80
|
|
|
@@ -97,11 +89,7 @@ export const enable = async (id) => {
|
|
|
97
89
|
Path.join(extensionsPath, id)
|
|
98
90
|
)
|
|
99
91
|
} catch (error) {
|
|
100
|
-
throw new
|
|
101
|
-
cause: error,
|
|
102
|
-
message: `Failed to enable extension "${id}"`,
|
|
103
|
-
code: 'E_EXTENSION_ENABLING_FAILED',
|
|
104
|
-
})
|
|
92
|
+
throw new VError(error, `Failed to enable extension "${id}"`)
|
|
105
93
|
}
|
|
106
94
|
}
|
|
107
95
|
|
|
@@ -116,11 +104,7 @@ export const disable = async (id) => {
|
|
|
116
104
|
Path.join(disabledExtensionsPath, id)
|
|
117
105
|
)
|
|
118
106
|
} catch (error) {
|
|
119
|
-
throw new
|
|
120
|
-
cause: error,
|
|
121
|
-
code: 'E_EXTENSION_DISABLING_FAILED',
|
|
122
|
-
message: `Failed to disable extension ${id}`,
|
|
123
|
-
})
|
|
107
|
+
throw new VError(error, `Failed to disable extension ${id}`)
|
|
124
108
|
}
|
|
125
109
|
}
|
|
126
110
|
|
|
@@ -160,6 +144,8 @@ const isIncludedBuiltinExtension = (dirent) => {
|
|
|
160
144
|
return !SKIPPED.includes(dirent)
|
|
161
145
|
}
|
|
162
146
|
|
|
147
|
+
// TODO pass builtin extensions path from renderer-worker
|
|
148
|
+
// only negative: more messages sent between renderer-worker and shared process on startup
|
|
163
149
|
const getBuiltinExtensionPaths = async () => {
|
|
164
150
|
try {
|
|
165
151
|
const builtinExtensionsPath = Platform.getBuiltinExtensionsPath()
|
|
@@ -192,11 +178,7 @@ const getInstalledExtensionPaths = async () => {
|
|
|
192
178
|
await mkdir(Platform.getExtensionsPath(), { recursive: true })
|
|
193
179
|
return []
|
|
194
180
|
}
|
|
195
|
-
throw new
|
|
196
|
-
cause: error,
|
|
197
|
-
code: 'E_FAILED_TO_LOAD_INSTALLED_EXTENSIONS',
|
|
198
|
-
message: 'Failed to get installed extensions',
|
|
199
|
-
})
|
|
181
|
+
throw new VError(error, 'Failed to get installed extensions')
|
|
200
182
|
}
|
|
201
183
|
}
|
|
202
184
|
|
|
@@ -222,28 +204,21 @@ const getExtensionManifest = async (path) => {
|
|
|
222
204
|
return {
|
|
223
205
|
...json,
|
|
224
206
|
path,
|
|
225
|
-
status:
|
|
207
|
+
status: ExtensionManifestStatus.Resolved,
|
|
226
208
|
}
|
|
227
209
|
} catch (error) {
|
|
228
210
|
const id = inferExtensionId(path)
|
|
229
211
|
return {
|
|
230
212
|
path,
|
|
231
|
-
status:
|
|
232
|
-
reason: new
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// @ts-ignore
|
|
237
|
-
stack: error.stack,
|
|
238
|
-
}),
|
|
213
|
+
status: ExtensionManifestStatus.Rejected,
|
|
214
|
+
reason: new VError(
|
|
215
|
+
error,
|
|
216
|
+
`Failed to load extension "${id}": Failed to load extension manifest`
|
|
217
|
+
),
|
|
239
218
|
}
|
|
240
219
|
}
|
|
241
220
|
}
|
|
242
221
|
|
|
243
|
-
const isRejected = (promise) => {
|
|
244
|
-
return promise.status === 'rejected'
|
|
245
|
-
}
|
|
246
|
-
|
|
247
222
|
/**
|
|
248
223
|
* @param {string[]} paths
|
|
249
224
|
*/
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import VError from 'verror'
|
|
1
2
|
import * as Error from '../Error/Error.js'
|
|
2
3
|
import * as ReadJson from '../JsonFile/JsonFile.js'
|
|
4
|
+
import * as Path from '../Path/Path.js'
|
|
3
5
|
import * as ExtensionManagement from './ExtensionManagement.js'
|
|
4
6
|
|
|
5
7
|
// TODO test this function
|
|
@@ -14,16 +16,12 @@ export const getColorThemeJson = async (colorThemeId) => {
|
|
|
14
16
|
if (colorTheme.id !== colorThemeId) {
|
|
15
17
|
continue
|
|
16
18
|
}
|
|
17
|
-
const absolutePath =
|
|
19
|
+
const absolutePath = Path.join(extension.path, colorTheme.path)
|
|
18
20
|
try {
|
|
19
21
|
const json = await ReadJson.readJson(absolutePath)
|
|
20
22
|
return json
|
|
21
23
|
} catch (error) {
|
|
22
|
-
throw new
|
|
23
|
-
cause: error,
|
|
24
|
-
code: 'E_COLOR_THEME_COULD_NOT_BE_LOADED',
|
|
25
|
-
message: `Failed to load color theme "${colorThemeId}"`,
|
|
26
|
-
})
|
|
24
|
+
throw new VError(error, `Failed to load color theme "${colorThemeId}"`)
|
|
27
25
|
}
|
|
28
26
|
}
|
|
29
27
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import VError from 'verror'
|
|
1
2
|
import * as Error from '../Error/Error.js'
|
|
2
3
|
import * as ReadJson from '../JsonFile/JsonFile.js'
|
|
4
|
+
import * as Path from '../Path/Path.js'
|
|
3
5
|
import * as ExtensionManagement from './ExtensionManagement.js'
|
|
4
6
|
|
|
5
7
|
export const getIconTheme = async (iconThemeId) => {
|
|
@@ -12,7 +14,7 @@ export const getIconTheme = async (iconThemeId) => {
|
|
|
12
14
|
if (iconTheme.id !== iconThemeId) {
|
|
13
15
|
continue
|
|
14
16
|
}
|
|
15
|
-
const absolutePath =
|
|
17
|
+
const absolutePath = Path.join(extension.path, iconTheme.path)
|
|
16
18
|
try {
|
|
17
19
|
const json = await ReadJson.readJson(absolutePath)
|
|
18
20
|
return {
|
|
@@ -20,11 +22,7 @@ export const getIconTheme = async (iconThemeId) => {
|
|
|
20
22
|
json,
|
|
21
23
|
}
|
|
22
24
|
} catch (error) {
|
|
23
|
-
throw new
|
|
24
|
-
cause: error,
|
|
25
|
-
code: 'E_ICON_THEME_COULD_NOT_BE_LOADED',
|
|
26
|
-
message: `Failed to load icon theme "${iconThemeId}"`,
|
|
27
|
-
})
|
|
25
|
+
throw new VError(error, `Failed to load icon theme "${iconThemeId}"`)
|
|
28
26
|
}
|
|
29
27
|
}
|
|
30
28
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// TODO lazyload chokidar and trash (but doesn't work currently because of bug with jest)
|
|
2
2
|
import * as fs from 'node:fs/promises'
|
|
3
3
|
import * as os from 'node:os'
|
|
4
|
-
import
|
|
5
|
-
import * as
|
|
4
|
+
import VError from 'verror'
|
|
5
|
+
import * as DirentType from '../DirentType/DirentType.js'
|
|
6
6
|
import * as Path from '../Path/Path.js'
|
|
7
|
-
import * as Trash from '../Trash/Trash.js'
|
|
8
7
|
import * as Platform from '../Platform/Platform.js'
|
|
9
|
-
import
|
|
8
|
+
import * as Trash from '../Trash/Trash.js'
|
|
10
9
|
|
|
11
10
|
export const state = {
|
|
12
11
|
watcherMap: Object.create(null),
|
|
@@ -27,26 +26,26 @@ export const copy = async (source, target) => {
|
|
|
27
26
|
`Failed to copy "${source}" to "${target}": src and dest cannot be the same`
|
|
28
27
|
)
|
|
29
28
|
}
|
|
30
|
-
throw new
|
|
31
|
-
cause: error,
|
|
32
|
-
code: 'E_FILE_SYSTEM_ERROR',
|
|
33
|
-
message: `Failed to copy "${source}" to "${target}"`,
|
|
34
|
-
category: 'File System Error',
|
|
35
|
-
})
|
|
29
|
+
throw new VError(error, `Failed to copy "${source}" to "${target}"`)
|
|
36
30
|
}
|
|
37
31
|
}
|
|
38
32
|
|
|
33
|
+
// TODO extensions should only be accessed once on startup
|
|
39
34
|
export const readFile = async (path) => {
|
|
35
|
+
// console.info('[shared-process] read file', path)
|
|
40
36
|
try {
|
|
37
|
+
// const start = performance.now()
|
|
38
|
+
// console.time(`read ${path}`)
|
|
41
39
|
const content = await fs.readFile(path, 'utf8')
|
|
40
|
+
// const end = performance.now()
|
|
41
|
+
// console.log('read', path, 'took', (end - start).toFixed(2), 'ms')
|
|
42
|
+
// console.timeEnd(`read ${path}`)
|
|
42
43
|
return content
|
|
43
44
|
} catch (error) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
category: 'File System Error',
|
|
49
|
-
})
|
|
45
|
+
if (error && error.code === 'ENOENT') {
|
|
46
|
+
throw new VError(`File not found '${path}'`)
|
|
47
|
+
}
|
|
48
|
+
throw new VError(error, `Failed to read file "${path}"`)
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -56,12 +55,7 @@ export const writeFile = async (path, content) => {
|
|
|
56
55
|
// Queue.add(`writeFile/${path}`, () =>
|
|
57
56
|
await fs.writeFile(path, content)
|
|
58
57
|
} catch (error) {
|
|
59
|
-
throw new
|
|
60
|
-
cause: error,
|
|
61
|
-
code: 'E_FILE_SYSTEM_ERROR',
|
|
62
|
-
message: `Failed to write to file "${path}"`,
|
|
63
|
-
category: 'File System Error',
|
|
64
|
-
})
|
|
58
|
+
throw new VError(error, `Failed to write to file "${path}"`)
|
|
65
59
|
}
|
|
66
60
|
}
|
|
67
61
|
|
|
@@ -70,12 +64,7 @@ export const ensureFile = async (path, content) => {
|
|
|
70
64
|
await fs.mkdir(Path.dirname(path), { recursive: true })
|
|
71
65
|
await fs.writeFile(path, content)
|
|
72
66
|
} catch (error) {
|
|
73
|
-
throw new
|
|
74
|
-
cause: error,
|
|
75
|
-
code: 'E_FILE_SYSTEM_ERROR',
|
|
76
|
-
message: `Failed to write to file "${path}"`,
|
|
77
|
-
category: 'File System Error',
|
|
78
|
-
})
|
|
67
|
+
throw new VError(error, `Failed to write to file "${path}"`)
|
|
79
68
|
}
|
|
80
69
|
}
|
|
81
70
|
|
|
@@ -83,12 +72,7 @@ export const createFile = async (path) => {
|
|
|
83
72
|
try {
|
|
84
73
|
await fs.writeFile(path, '', { flag: 'wx' })
|
|
85
74
|
} catch (error) {
|
|
86
|
-
throw new
|
|
87
|
-
cause: error,
|
|
88
|
-
code: 'E_FILE_SYSTEM_ERROR',
|
|
89
|
-
message: `Failed to create file "${path}"`,
|
|
90
|
-
category: 'File System Error',
|
|
91
|
-
})
|
|
75
|
+
throw new VError(error, `Failed to create file "${path}"`)
|
|
92
76
|
}
|
|
93
77
|
}
|
|
94
78
|
|
|
@@ -96,12 +80,7 @@ export const createFolder = async (path, options) => {
|
|
|
96
80
|
try {
|
|
97
81
|
await fs.mkdir(path, options)
|
|
98
82
|
} catch (error) {
|
|
99
|
-
throw new
|
|
100
|
-
cause: error,
|
|
101
|
-
code: 'E_FILE_SYSTEM_ERROR',
|
|
102
|
-
message: `Failed to create folder "${path}"`,
|
|
103
|
-
category: 'File System Error',
|
|
104
|
-
})
|
|
83
|
+
throw new VError(error, `Failed to create folder "${path}"`)
|
|
105
84
|
}
|
|
106
85
|
}
|
|
107
86
|
|
|
@@ -129,12 +108,7 @@ export const remove = async (path) => {
|
|
|
129
108
|
try {
|
|
130
109
|
await Trash.trash(path)
|
|
131
110
|
} catch (error) {
|
|
132
|
-
throw new
|
|
133
|
-
cause: error,
|
|
134
|
-
code: 'E_FILE_REMOVE_SYSTEM_ERROR',
|
|
135
|
-
message: `Failed to remove "${path}"`,
|
|
136
|
-
category: 'File System Error',
|
|
137
|
-
})
|
|
111
|
+
throw new VError(error, `Failed to remove "${path}"`)
|
|
138
112
|
}
|
|
139
113
|
}
|
|
140
114
|
|
|
@@ -152,24 +126,24 @@ export const exists = async (path) => {
|
|
|
152
126
|
*/
|
|
153
127
|
const getType = (dirent) => {
|
|
154
128
|
if (dirent.isFile()) {
|
|
155
|
-
return
|
|
129
|
+
return DirentType.File
|
|
156
130
|
}
|
|
157
131
|
if (dirent.isDirectory()) {
|
|
158
|
-
return
|
|
132
|
+
return DirentType.Direcory
|
|
159
133
|
}
|
|
160
134
|
if (dirent.isSymbolicLink()) {
|
|
161
|
-
return
|
|
135
|
+
return DirentType.Symlink
|
|
162
136
|
}
|
|
163
137
|
if (dirent.isSocket()) {
|
|
164
|
-
return
|
|
138
|
+
return DirentType.Socket
|
|
165
139
|
}
|
|
166
140
|
if (dirent.isBlockDevice()) {
|
|
167
|
-
return
|
|
141
|
+
return DirentType.BlockDevice
|
|
168
142
|
}
|
|
169
143
|
if (dirent.isCharacterDevice()) {
|
|
170
|
-
return
|
|
144
|
+
return DirentType.CharacterDevice
|
|
171
145
|
}
|
|
172
|
-
return
|
|
146
|
+
return DirentType.Unknown
|
|
173
147
|
}
|
|
174
148
|
|
|
175
149
|
/**
|
|
@@ -188,25 +162,15 @@ export const readDirWithFileTypes = async (path) => {
|
|
|
188
162
|
const prettyDirents = dirents.map(toPrettyDirent)
|
|
189
163
|
return prettyDirents
|
|
190
164
|
} catch (error) {
|
|
191
|
-
throw new
|
|
192
|
-
cause: error,
|
|
193
|
-
code: 'E_FILE_READ_DIR_SYSTEM_ERROR',
|
|
194
|
-
message: `Failed to read directory "${path}"`,
|
|
195
|
-
category: 'File System Error',
|
|
196
|
-
})
|
|
165
|
+
throw new VError(error, `Failed to read directory "${path}"`)
|
|
197
166
|
}
|
|
198
167
|
}
|
|
199
168
|
|
|
200
169
|
export const mkdir = async (path) => {
|
|
201
170
|
try {
|
|
202
|
-
await fs.mkdir(path)
|
|
171
|
+
await fs.mkdir(path, { recursive: true })
|
|
203
172
|
} catch (error) {
|
|
204
|
-
throw new
|
|
205
|
-
cause: error,
|
|
206
|
-
code: 'E_CREATE_DIRECTORY_FAILED',
|
|
207
|
-
message: `Failed to create directory "${path}"`,
|
|
208
|
-
category: 'File System Error',
|
|
209
|
-
})
|
|
173
|
+
throw new VError(error, `Failed to create directory "${path}"`)
|
|
210
174
|
}
|
|
211
175
|
}
|
|
212
176
|
|
|
@@ -214,18 +178,13 @@ export const rename = async (oldPath, newPath) => {
|
|
|
214
178
|
try {
|
|
215
179
|
await fs.rename(oldPath, newPath)
|
|
216
180
|
} catch (error) {
|
|
217
|
-
throw new
|
|
218
|
-
cause: error,
|
|
219
|
-
code: 'E_RENAME_FAILED',
|
|
220
|
-
message: `Failed to rename "${oldPath}" to "${newPath}"`,
|
|
221
|
-
category: 'File System Error',
|
|
222
|
-
})
|
|
181
|
+
throw new VError(error, `Failed to rename "${oldPath}" to "${newPath}"`)
|
|
223
182
|
}
|
|
224
183
|
}
|
|
225
184
|
|
|
226
|
-
export const watch = (path, options) => {
|
|
185
|
+
export const watch = async (path, options) => {
|
|
227
186
|
// let state = 'loading'
|
|
228
|
-
|
|
187
|
+
const chokidar = await import('chokidar')
|
|
229
188
|
const watcher = chokidar.watch(`${path}`, {
|
|
230
189
|
ignoreInitial: true,
|
|
231
190
|
})
|
package/src/parts/Json/Json.js
CHANGED
|
@@ -1,65 +1,17 @@
|
|
|
1
|
-
import fallback from 'json-parse-even-better-errors'
|
|
2
|
-
import { codeFrameColumns } from '@babel/code-frame'
|
|
3
|
-
import { LinesAndColumns } from 'lines-and-columns'
|
|
4
|
-
|
|
5
1
|
// parsing error handling based on https://github.com/sindresorhus/parse-json/blob/main/index.js
|
|
6
2
|
|
|
7
3
|
export const parse = async (string, filePath) => {
|
|
8
4
|
try {
|
|
9
|
-
|
|
10
|
-
return JSON.parse(string)
|
|
11
|
-
} catch (error) {
|
|
12
|
-
fallback(string)
|
|
13
|
-
throw error
|
|
14
|
-
}
|
|
5
|
+
return JSON.parse(string)
|
|
15
6
|
} catch (error) {
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (topMessage.startsWith('Unexpected token')) {
|
|
23
|
-
topMessage = 'JSON parsing error:'
|
|
24
|
-
}
|
|
25
|
-
if (bottomMessage.includes('while parsing "{')) {
|
|
26
|
-
bottomMessage = bottomMessage.slice(
|
|
27
|
-
0,
|
|
28
|
-
bottomMessage.indexOf('while parsing "{')
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
if (bottomMessage.includes(' (0x7D) ')) {
|
|
32
|
-
bottomMessage = bottomMessage.replace(' (0x7D) ', ' ')
|
|
33
|
-
}
|
|
34
|
-
bottomMessage = bottomMessage.trim()
|
|
35
|
-
const jsonError = new Error(topMessage)
|
|
36
|
-
if (indexMatch && indexMatch.length > 0) {
|
|
37
|
-
const lines = new LinesAndColumns(string)
|
|
38
|
-
const index = Number(indexMatch[1])
|
|
39
|
-
const location = lines.locationForIndex(index)
|
|
40
|
-
const codeFrame = codeFrameColumns(
|
|
41
|
-
string,
|
|
42
|
-
{ start: { line: location.line + 1, column: location.column + 1 } },
|
|
43
|
-
{ highlightCode: true }
|
|
44
|
-
)
|
|
45
|
-
jsonError.codeFrame = codeFrame
|
|
46
|
-
}
|
|
47
|
-
// console.log({ bottomMessage, message: error.message })
|
|
48
|
-
jsonError.stack = `${bottomMessage}\n at ${filePath}`
|
|
7
|
+
const JsonError = await import('../JsonError/JsonError.js')
|
|
8
|
+
const errorProps = JsonError.getErrorPropsFromError(error, string, filePath)
|
|
9
|
+
const jsonError = new Error(errorProps.message)
|
|
10
|
+
jsonError.stack = errorProps.stack
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
jsonError.codeFrame = errorProps.codeFrame
|
|
49
13
|
throw jsonError
|
|
50
14
|
}
|
|
51
|
-
|
|
52
|
-
// try {
|
|
53
|
-
// return JSON.parse(json)
|
|
54
|
-
// } catch (error) {
|
|
55
|
-
// const parseJsonBetterErrors = await import('parse-json')
|
|
56
|
-
// try {
|
|
57
|
-
// return parseJsonBetterErrors.default(json, filePath)
|
|
58
|
-
// } catch (error) {
|
|
59
|
-
// console.log({ frame: error.codeFrame })
|
|
60
|
-
// throw error
|
|
61
|
-
// }
|
|
62
|
-
// }
|
|
63
15
|
}
|
|
64
16
|
|
|
65
17
|
export const stringify = (value) => {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { codeFrameColumns } from '@babel/code-frame'
|
|
2
|
+
import { LinesAndColumns } from 'lines-and-columns'
|
|
3
|
+
|
|
4
|
+
// parsing error handling based on https://github.com/sindresorhus/parse-json/blob/main/index.js
|
|
5
|
+
|
|
6
|
+
const emptyError = {
|
|
7
|
+
message: '',
|
|
8
|
+
stack: '',
|
|
9
|
+
codeFrame: '',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const getErrorPropsFromError = (error, string, filePath) => {
|
|
13
|
+
const indexMatch = error.message.match(/in JSON at position (\d+)/)
|
|
14
|
+
if (indexMatch && indexMatch.length > 0) {
|
|
15
|
+
const lines = new LinesAndColumns(string)
|
|
16
|
+
const index = Number(indexMatch[1])
|
|
17
|
+
const location = lines.locationForIndex(index)
|
|
18
|
+
if (location) {
|
|
19
|
+
const codeFrame = codeFrameColumns(
|
|
20
|
+
string,
|
|
21
|
+
{ start: { line: location.line + 1, column: location.column + 1 } },
|
|
22
|
+
{ highlightCode: false }
|
|
23
|
+
)
|
|
24
|
+
return {
|
|
25
|
+
codeFrame,
|
|
26
|
+
message: 'Json Parsing Error',
|
|
27
|
+
stack: `at ${filePath}`,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (string.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
codeFrame: ``,
|
|
34
|
+
message: 'Json Parsing Error: Cannot parse empty string',
|
|
35
|
+
stack: `at ${filePath}`,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (error.message === 'Unexpected end of JSON input') {
|
|
40
|
+
const lines = new LinesAndColumns(string)
|
|
41
|
+
const index = string.length - 1
|
|
42
|
+
const location = lines.locationForIndex(index)
|
|
43
|
+
if (location) {
|
|
44
|
+
const codeFrame = codeFrameColumns(
|
|
45
|
+
string,
|
|
46
|
+
{ start: { line: location.line + 1, column: location.column + 1 } },
|
|
47
|
+
{ highlightCode: false }
|
|
48
|
+
)
|
|
49
|
+
return {
|
|
50
|
+
codeFrame,
|
|
51
|
+
message: 'Json Parsing Error',
|
|
52
|
+
stack: `at ${filePath}`,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
codeFrame: ``,
|
|
58
|
+
message: 'Json Parsing Error',
|
|
59
|
+
stack: `at ${filePath}`,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const getErrorProps = (string, filePath) => {
|
|
64
|
+
try {
|
|
65
|
+
JSON.parse(string)
|
|
66
|
+
return emptyError
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return getErrorPropsFromError(error, string, filePath)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as Path from '../Path/Path.js'
|
|
1
|
+
import * as FileSystem from '../FileSystem/FileSystem.js'
|
|
3
2
|
import * as Json from '../Json/Json.js'
|
|
3
|
+
import * as Path from '../Path/Path.js'
|
|
4
4
|
|
|
5
5
|
export const readJson = async (absolutePath) => {
|
|
6
|
-
const content = await readFile(absolutePath
|
|
6
|
+
const content = await FileSystem.readFile(absolutePath)
|
|
7
7
|
const json = await Json.parse(content, absolutePath)
|
|
8
8
|
return json
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const writeJson = async (absolutePath, value) => {
|
|
12
|
-
await mkdir(Path.dirname(absolutePath)
|
|
13
|
-
await writeFile(absolutePath, Json.stringify(value))
|
|
12
|
+
await FileSystem.mkdir(Path.dirname(absolutePath))
|
|
13
|
+
await FileSystem.writeFile(absolutePath, Json.stringify(value))
|
|
14
14
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import open from 'open'
|
|
2
|
-
import
|
|
2
|
+
import VError from 'verror'
|
|
3
3
|
|
|
4
4
|
export const openFolder = async (path) => {
|
|
5
5
|
try {
|
|
6
6
|
await open(path)
|
|
7
7
|
} catch (error) {
|
|
8
|
-
throw new
|
|
9
|
-
cause: error,
|
|
10
|
-
code: 'E_OPEN_SYSTEM_ERROR',
|
|
11
|
-
message: `Failed to open ${path}`,
|
|
12
|
-
})
|
|
8
|
+
throw new VError(error, `Failed to open ${path}`)
|
|
13
9
|
}
|
|
14
10
|
}
|
|
@@ -11,7 +11,7 @@ export const getUserPreferences = async () => {
|
|
|
11
11
|
try {
|
|
12
12
|
json = await JsonFile.readJson(userSettingsPath)
|
|
13
13
|
} catch (error) {
|
|
14
|
-
if (error && error.
|
|
14
|
+
if (error && error.message.includes('File not found')) {
|
|
15
15
|
return {}
|
|
16
16
|
}
|
|
17
17
|
throw error
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import VError from 'verror'
|
|
2
|
+
import * as Assert from '../Assert/Assert.js'
|
|
3
|
+
import * as FileSystem from '../FileSystem/FileSystem.js'
|
|
4
|
+
import * as Json from '../Json/Json.js'
|
|
5
|
+
import * as Platform from '../Platform/Platform.js'
|
|
6
|
+
|
|
7
|
+
const isValid = (recentlyOpened) => {
|
|
8
|
+
return recentlyOpened && Array.isArray(recentlyOpened)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const addToArrayUnique = (recentlyOpened, path) => {
|
|
12
|
+
const index = recentlyOpened.indexOf(path)
|
|
13
|
+
if (index === -1) {
|
|
14
|
+
return [path, ...recentlyOpened]
|
|
15
|
+
}
|
|
16
|
+
return [
|
|
17
|
+
path,
|
|
18
|
+
...recentlyOpened.slice(0, index),
|
|
19
|
+
...recentlyOpened.slice(index + 1),
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getRecentlyOpened = async (recentlyOpenedPath) => {
|
|
24
|
+
try {
|
|
25
|
+
const content = await FileSystem.readFile(recentlyOpenedPath)
|
|
26
|
+
const parsed = await Json.parse(content, recentlyOpenedPath)
|
|
27
|
+
return parsed
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// TODO should check for error.code
|
|
30
|
+
if (error.message.includes('File not found')) {
|
|
31
|
+
// ignore
|
|
32
|
+
} else if (error.message.includes('Json Parsing Error')) {
|
|
33
|
+
// ignore
|
|
34
|
+
} else {
|
|
35
|
+
throw new VError(error, `Failed to read recently opened`)
|
|
36
|
+
}
|
|
37
|
+
return []
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const setRecentlyOpened = async (recentlyOpenedPath, newRecentlyOpened) => {
|
|
42
|
+
const stringified = Json.stringify(newRecentlyOpened)
|
|
43
|
+
await FileSystem.writeFile(recentlyOpenedPath, stringified)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const addPath = async (path) => {
|
|
47
|
+
Assert.string(path)
|
|
48
|
+
const recentlyOpenedPath = Platform.getRecentlyOpenedPath()
|
|
49
|
+
const parsed = await getRecentlyOpened(recentlyOpenedPath)
|
|
50
|
+
const newRecentlyOpened = addToArrayUnique(parsed, path)
|
|
51
|
+
await setRecentlyOpened(recentlyOpenedPath, newRecentlyOpened)
|
|
52
|
+
}
|
|
@@ -22,6 +22,12 @@ const useNice = !Platform.isWindows()
|
|
|
22
22
|
// TODO update client
|
|
23
23
|
// TODO not always run nice, maybe configure nice via flag/options
|
|
24
24
|
|
|
25
|
+
const ParsedLineType = {
|
|
26
|
+
Begin: 'begin',
|
|
27
|
+
Match: 'match',
|
|
28
|
+
Summary: 'summary',
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
export const search = async (searchDir, searchString) => {
|
|
26
32
|
// TODO reject promise when ripgrep search fails
|
|
27
33
|
return new Promise((resolve, reject) => {
|
|
@@ -61,18 +67,18 @@ export const search = async (searchDir, searchString) => {
|
|
|
61
67
|
for (const line of lines) {
|
|
62
68
|
const parsedLine = JSON.parse(line)
|
|
63
69
|
switch (parsedLine.type) {
|
|
64
|
-
case
|
|
70
|
+
case ParsedLineType.Begin: {
|
|
65
71
|
allSearchResults[parsedLine.data.path.text] = []
|
|
66
72
|
break
|
|
67
73
|
}
|
|
68
|
-
case
|
|
74
|
+
case ParsedLineType.Match: {
|
|
69
75
|
numberOfResults++
|
|
70
76
|
allSearchResults[parsedLine.data.path.text].push(
|
|
71
77
|
toSearchResult(parsedLine)
|
|
72
78
|
)
|
|
73
79
|
break
|
|
74
80
|
}
|
|
75
|
-
case
|
|
81
|
+
case ParsedLineType.Summary:
|
|
76
82
|
stats = parsedLine.data
|
|
77
83
|
break
|
|
78
84
|
default:
|