@lvce-editor/shared-process 0.11.6 → 0.11.8
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/builtinCommands.json +1 -1
- package/config/defaultKeyBindings.json +5 -1
- package/config/defaultSettings.json +2 -0
- package/extensions/builtin.language-basics-cpp/src/tokenizeCpp.js +29 -6
- package/extensions/builtin.language-basics-css/src/tokenizeCss.js +176 -25
- package/extensions/builtin.language-basics-desktop/src/tokenizeDesktop.js +49 -5
- package/extensions/builtin.language-basics-gn/README.md +14 -0
- package/extensions/builtin.language-basics-gn/extension.json +12 -0
- package/extensions/builtin.language-basics-gn/src/tokenizeGn.js +178 -0
- package/extensions/builtin.language-basics-javascript/src/tokenizeJavaScript.js +5 -0
- package/extensions/builtin.language-basics-json5/README.md +16 -0
- package/extensions/builtin.language-basics-json5/extension.json +12 -0
- package/extensions/builtin.language-basics-json5/src/tokenizeJson5.js +321 -0
- package/extensions/builtin.language-basics-python/extension.json +2 -1
- package/extensions/builtin.language-basics-python/src/tokenizePython.js +45 -35
- package/extensions/builtin.language-basics-shellscript/extension.json +1 -0
- package/extensions/builtin.language-basics-typescript/src/tokenizeTypeScript.js +197 -15
- package/extensions/builtin.language-basics-xml/src/tokenizeXml.js +1 -1
- package/extensions/builtin.theme-ayu/color-theme.json +2 -0
- package/extensions/builtin.theme-slime/color-theme.json +6 -0
- package/index.js +5 -2
- package/package.json +5 -5
- package/src/parts/Command/Command.js +38 -40
- package/src/parts/ErrorCodes/ErrorCodes.js +2 -0
- package/src/parts/ExportStatic/ExportStatic.js +78 -2
- package/src/parts/ExtensionLink/ExtensionLink.js +2 -3
- package/src/parts/ExtensionManagement/ExtensionManagement.js +1 -1
- package/src/parts/ExtensionManifest/ExtensionManifest.js +2 -2
- package/src/parts/GetResponse/GetResponse.js +2 -2
- package/src/parts/Module/Module.js +2 -2
- package/src/parts/ModuleMap/ModuleMap.js +1 -1
- package/src/parts/PrettyError/PrettyError.js +55 -5
- package/src/parts/RipGrep/RipGrep.js +30 -0
- package/src/parts/SearchFile/SearchFile.js +12 -16
- package/src/parts/TextSearch/TextSearch.ipc.js +7 -0
- package/src/parts/TextSearch/TextSearch.js +138 -0
- package/src/parts/TextSearchResultType/TextSearchResultType.js +2 -0
- package/src/parts/VError/VError.js +44 -0
- package/src/parts/Search/Search.ipc.js +0 -7
- package/src/parts/Search/Search.js +0 -107
|
@@ -637,11 +637,76 @@ const addExtension = async ({
|
|
|
637
637
|
})
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
+
const generateTestOverviewHtml = (dirents) => {
|
|
641
|
+
const pre = `<!DOCTYPE html>
|
|
642
|
+
<html lang="en">
|
|
643
|
+
<head>
|
|
644
|
+
<meta charset="UTF-8" />
|
|
645
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
646
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
647
|
+
<title>Tests</title>
|
|
648
|
+
</head>
|
|
649
|
+
<body>
|
|
650
|
+
<h1>Tests</h1>
|
|
651
|
+
<p>Available Tests</p>
|
|
652
|
+
<ul>
|
|
653
|
+
`
|
|
654
|
+
let middle = ``
|
|
655
|
+
// TODO properly escape name
|
|
656
|
+
for (const dirent of dirents) {
|
|
657
|
+
const name = dirent
|
|
658
|
+
middle += ` <li><a href="./${name}.html">${name}</a></li>
|
|
659
|
+
`
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const post = ` </ul>
|
|
663
|
+
</body>
|
|
664
|
+
</html>
|
|
665
|
+
`
|
|
666
|
+
return pre + middle + post
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const getName = (name) => {
|
|
670
|
+
return name.slice(0, -'.js'.length)
|
|
671
|
+
}
|
|
672
|
+
const isTestFile = (file) => {
|
|
673
|
+
return file !== '_all.js'
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const getTestFiles = (testFilesRaw) => {
|
|
677
|
+
return testFilesRaw.map(getName).filter(isTestFile)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const addTestFiles = async ({ testPath, commitHash, root, pathPrefix }) => {
|
|
681
|
+
await FileSystem.copy(
|
|
682
|
+
`${root}/${testPath}/src`,
|
|
683
|
+
`${root}/dist/${commitHash}/packages/extension-host-worker-tests/src`
|
|
684
|
+
)
|
|
685
|
+
const testFilesRaw = await FileSystem.readDir(`${root}/${testPath}/src`)
|
|
686
|
+
const testFiles = getTestFiles(testFilesRaw)
|
|
687
|
+
await FileSystem.mkdir(`${root}/dist/${commitHash}/tests`)
|
|
688
|
+
await FileSystem.mkdir(`${root}/dist/tests`)
|
|
689
|
+
console.log({ testFiles })
|
|
690
|
+
for (const testFile of testFiles) {
|
|
691
|
+
await FileSystem.copyFile(
|
|
692
|
+
`${root}/dist/index.html`,
|
|
693
|
+
`${root}/dist/tests/${testFile}.html`
|
|
694
|
+
)
|
|
695
|
+
}
|
|
696
|
+
const testOverviewHtml = generateTestOverviewHtml(testFiles)
|
|
697
|
+
await FileSystem.writeFile(`${root}/dist/tests/index.html`, testOverviewHtml)
|
|
698
|
+
}
|
|
699
|
+
|
|
640
700
|
/**
|
|
641
701
|
*
|
|
642
|
-
* @param {{root:string, pathPrefix:string , extensionPath:string
|
|
702
|
+
* @param {{root:string, pathPrefix:string , extensionPath:string, testPath:string }} param0
|
|
643
703
|
*/
|
|
644
|
-
export const exportStatic = async ({
|
|
704
|
+
export const exportStatic = async ({
|
|
705
|
+
root,
|
|
706
|
+
pathPrefix,
|
|
707
|
+
extensionPath,
|
|
708
|
+
testPath,
|
|
709
|
+
}) => {
|
|
645
710
|
if (pathPrefix === 'auto') {
|
|
646
711
|
const extensionJson = await readExtensionManifest(
|
|
647
712
|
Path.join(extensionPath, 'extension.json')
|
|
@@ -679,4 +744,15 @@ export const exportStatic = async ({ root, pathPrefix, extensionPath }) => {
|
|
|
679
744
|
root,
|
|
680
745
|
})
|
|
681
746
|
console.timeEnd('addExtension')
|
|
747
|
+
|
|
748
|
+
if (testPath) {
|
|
749
|
+
console.time('addTestFiles')
|
|
750
|
+
await addTestFiles({
|
|
751
|
+
testPath,
|
|
752
|
+
commitHash,
|
|
753
|
+
pathPrefix,
|
|
754
|
+
root,
|
|
755
|
+
})
|
|
756
|
+
console.timeEnd('addTestFiles')
|
|
757
|
+
}
|
|
682
758
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
2
2
|
import * as ExtensionManifest from '../ExtensionManifest/ExtensionManifest.js'
|
|
3
3
|
import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
|
|
4
4
|
import * as FileSystem from '../FileSystem/FileSystem.js'
|
|
5
|
-
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
6
5
|
import * as Path from '../Path/Path.js'
|
|
7
6
|
import * as Platform from '../Platform/Platform.js'
|
|
8
7
|
import * as SymLink from '../SymLink/SymLink.js'
|
|
8
|
+
import { VError } from '../VError/VError.js'
|
|
9
9
|
|
|
10
10
|
const linkFallBack = async (path) => {
|
|
11
11
|
try {
|
|
@@ -19,7 +19,6 @@ const linkFallBack = async (path) => {
|
|
|
19
19
|
await FileSystem.remove(to)
|
|
20
20
|
await SymLink.createSymLink(path, to)
|
|
21
21
|
} catch (error) {
|
|
22
|
-
console.log({ error })
|
|
23
22
|
throw new VError(error, `Failed to link extension`)
|
|
24
23
|
}
|
|
25
24
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir, rename, rm } from 'node:fs/promises'
|
|
2
|
-
import VError from '
|
|
2
|
+
import { VError } from '../VError/VError.js'
|
|
3
3
|
import * as Debug from '../Debug/Debug.js'
|
|
4
4
|
import * as ExtensionManifestInputType from '../ExtensionManifestInputType/ExtensionManifestInputType.js'
|
|
5
5
|
import * as ExtensionManifests from '../ExtensionManifests/ExtensionManifests.js'
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import isObject from 'is-object'
|
|
2
|
-
import
|
|
2
|
+
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
3
3
|
import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
|
|
4
4
|
import * as ReadJson from '../JsonFile/JsonFile.js'
|
|
5
5
|
import * as Path from '../Path/Path.js'
|
|
6
|
-
import
|
|
6
|
+
import { VError } from '../VError/VError.js'
|
|
7
7
|
|
|
8
8
|
const RE_EXTENSION_FRAGMENT = /.+(\/|\\)(.+)$/
|
|
9
9
|
|
|
@@ -7,8 +7,8 @@ import { requiresSocket } from '../RequiresSocket/RequiresSocket.js'
|
|
|
7
7
|
export const getResponse = async (message, handle) => {
|
|
8
8
|
try {
|
|
9
9
|
const result = requiresSocket(message.method)
|
|
10
|
-
? await Command.
|
|
11
|
-
: await Command.
|
|
10
|
+
? await Command.execute(message.method, handle, ...message.params)
|
|
11
|
+
: await Command.execute(message.method, ...message.params)
|
|
12
12
|
|
|
13
13
|
return {
|
|
14
14
|
jsonrpc: JsonRpc.Version,
|
|
@@ -28,14 +28,14 @@ export const load = (moduleId) => {
|
|
|
28
28
|
return import('../Preferences/Preferences.ipc.js')
|
|
29
29
|
case ModuleId.RecentlyOpened:
|
|
30
30
|
return import('../RecentlyOpened/RecentlyOpened.ipc.js')
|
|
31
|
-
case ModuleId.Search:
|
|
32
|
-
return import('../Search/Search.ipc.js')
|
|
33
31
|
case ModuleId.SearchFile:
|
|
34
32
|
return import('../SearchFile/SearchFile.ipc.js')
|
|
35
33
|
case ModuleId.Terminal:
|
|
36
34
|
return import('../Terminal/Terminal.ipc.js')
|
|
37
35
|
case ModuleId.TextDocument:
|
|
38
36
|
return import('../TextDocument/TextDocument.ipc.js')
|
|
37
|
+
case ModuleId.Search:
|
|
38
|
+
return import('../TextSearch/TextSearch.ipc.js')
|
|
39
39
|
case ModuleId.WebSocketServer:
|
|
40
40
|
return import('../WebSocketServer/WebSocketServer.ipc.js')
|
|
41
41
|
case ModuleId.Workspace:
|
|
@@ -120,7 +120,7 @@ export const getModuleId = (commandId) => {
|
|
|
120
120
|
return ModuleId.Preferences
|
|
121
121
|
case 'RecentlyOpened.addPath':
|
|
122
122
|
return ModuleId.RecentlyOpened
|
|
123
|
-
case '
|
|
123
|
+
case 'TextSearch.search':
|
|
124
124
|
return ModuleId.Search
|
|
125
125
|
case 'SearchFile.searchFile':
|
|
126
126
|
return ModuleId.SearchFile
|
|
@@ -3,6 +3,8 @@ import cleanStack from 'clean-stack'
|
|
|
3
3
|
import { LinesAndColumns } from 'lines-and-columns'
|
|
4
4
|
import { readFileSync } from 'node:fs'
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
7
|
+
import * as Json from '../Json/Json.js'
|
|
6
8
|
import * as SplitLines from '../SplitLines/SplitLines.js'
|
|
7
9
|
|
|
8
10
|
const getActualPath = (fileUri) => {
|
|
@@ -12,7 +14,59 @@ const getActualPath = (fileUri) => {
|
|
|
12
14
|
return fileUri
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
const RE_MODULE_NOT_FOUND_STACK =
|
|
18
|
+
/Cannot find package '([^']+)' imported from (.+)$/
|
|
19
|
+
|
|
20
|
+
const prepareModuleNotFoundError = (error) => {
|
|
21
|
+
const message = error.message
|
|
22
|
+
const match = message.match(RE_MODULE_NOT_FOUND_STACK)
|
|
23
|
+
if (!match) {
|
|
24
|
+
return {
|
|
25
|
+
message,
|
|
26
|
+
stack: error.stack,
|
|
27
|
+
codeFrame: '',
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const notFoundModule = match[1]
|
|
31
|
+
const importedFrom = match[2]
|
|
32
|
+
const rawLines = readFileSync(importedFrom, 'utf-8')
|
|
33
|
+
let line = 0
|
|
34
|
+
let column = 0
|
|
35
|
+
const splittedLines = rawLines.split('\n')
|
|
36
|
+
for (let i = 0; i < splittedLines.length; i++) {
|
|
37
|
+
const splittedLine = splittedLines[i]
|
|
38
|
+
const index = splittedLine.indexOf(notFoundModule)
|
|
39
|
+
if (index !== -1) {
|
|
40
|
+
line = i + 1
|
|
41
|
+
column = index
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const location = {
|
|
46
|
+
start: {
|
|
47
|
+
line,
|
|
48
|
+
column,
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
const codeFrame = codeFrameColumns(rawLines, location)
|
|
52
|
+
const stackLines = SplitLines.splitLines(error.stack)
|
|
53
|
+
const newStackLines = [
|
|
54
|
+
stackLines[0],
|
|
55
|
+
` at ${importedFrom}:${line}:${column}`,
|
|
56
|
+
...stackLines.slice(1),
|
|
57
|
+
]
|
|
58
|
+
const newStack = newStackLines.join('\n')
|
|
59
|
+
return {
|
|
60
|
+
message,
|
|
61
|
+
stack: newStack,
|
|
62
|
+
codeFrame,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
15
66
|
export const prepare = (error) => {
|
|
67
|
+
if (error && error.code === ErrorCodes.ERR_MODULE_NOT_FOUND) {
|
|
68
|
+
return prepareModuleNotFoundError(error)
|
|
69
|
+
}
|
|
16
70
|
const message = error.message
|
|
17
71
|
if (error && error.cause) {
|
|
18
72
|
const cause = error.cause()
|
|
@@ -57,12 +111,8 @@ const fixBackslashes = (string) => {
|
|
|
57
111
|
return string.replaceAll('\\\\', '\\')
|
|
58
112
|
}
|
|
59
113
|
|
|
60
|
-
const stringifyJson = (json) => {
|
|
61
|
-
return JSON.stringify(json, null, 2) + '\n'
|
|
62
|
-
}
|
|
63
|
-
|
|
64
114
|
export const prepareJsonError = (json, property, message) => {
|
|
65
|
-
const string = fixBackslashes(
|
|
115
|
+
const string = fixBackslashes(Json.stringify(json))
|
|
66
116
|
const stringifiedPropertyName = `"${property}"`
|
|
67
117
|
const index = string.indexOf(stringifiedPropertyName) // TODO this could be wrong in some cases, find a better way
|
|
68
118
|
console.log({ string, index })
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as NodeChildProcess from 'node:child_process'
|
|
2
|
+
import * as Assert from '../Assert/Assert.js'
|
|
3
|
+
import * as Exec from '../Exec/Exec.js'
|
|
4
|
+
import * as RgPath from '../RgPath/RgPath.js'
|
|
5
|
+
|
|
6
|
+
export const ripGrepPath = process.env.RIP_GREP_PATH || RgPath.rgPath
|
|
7
|
+
|
|
8
|
+
export const spawn = (args, options) => {
|
|
9
|
+
const childProcess = NodeChildProcess.spawn(RgPath.rgPath, args, options)
|
|
10
|
+
return {
|
|
11
|
+
on(event, listener) {
|
|
12
|
+
childProcess.on(event, listener)
|
|
13
|
+
},
|
|
14
|
+
once(event, listener) {
|
|
15
|
+
childProcess.once(event, listener)
|
|
16
|
+
},
|
|
17
|
+
stdout: childProcess.stdout,
|
|
18
|
+
stderr: childProcess.stderr,
|
|
19
|
+
kill() {
|
|
20
|
+
childProcess.kill()
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const exec = async (args, options) => {
|
|
26
|
+
Assert.array(args)
|
|
27
|
+
Assert.object(options)
|
|
28
|
+
const { stdout, stderr } = await Exec.exec(ripGrepPath, args, options)
|
|
29
|
+
return { stdout, stderr }
|
|
30
|
+
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as Assert from '../Assert/Assert.js'
|
|
2
2
|
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
3
|
-
import * as Exec from '../Exec/Exec.js'
|
|
4
3
|
import * as LimitString from '../LimitString/LimitString.js'
|
|
5
|
-
import * as
|
|
6
|
-
|
|
7
|
-
const ripGrepPath = process.env.RIP_GREP_PATH || RgPath.rgPath
|
|
4
|
+
import * as RipGrep from '../RipGrep/RipGrep.js'
|
|
5
|
+
import * as Logger from '../Logger/Logger.js'
|
|
8
6
|
|
|
9
7
|
const isEnoentErrorLinux = (error) => {
|
|
10
8
|
return error.code === ErrorCodes.ENOENT
|
|
@@ -32,25 +30,23 @@ export const searchFile = async (path, searchTerm, limit) => {
|
|
|
32
30
|
Assert.string(path)
|
|
33
31
|
Assert.string(searchTerm)
|
|
34
32
|
Assert.number(limit)
|
|
35
|
-
const { stdout, stderr } = await
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
cwd: path,
|
|
40
|
-
}
|
|
41
|
-
)
|
|
33
|
+
const { stdout, stderr } = await RipGrep.exec(['--files', '--sort-files'], {
|
|
34
|
+
cwd: path,
|
|
35
|
+
})
|
|
42
36
|
return LimitString.limitString(stdout, limit)
|
|
43
37
|
} catch (error) {
|
|
44
38
|
// @ts-ignore
|
|
45
39
|
if (isEnoentError(error)) {
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
Logger.info(
|
|
41
|
+
`[shared-process] ripgrep could not be found at "${RipGrep.ripGrepPath}"`
|
|
42
|
+
)
|
|
43
|
+
return ``
|
|
48
44
|
}
|
|
49
45
|
// @ts-ignore
|
|
50
46
|
if (error && error.stderr === '') {
|
|
51
|
-
return
|
|
47
|
+
return ``
|
|
52
48
|
}
|
|
53
|
-
|
|
54
|
-
return
|
|
49
|
+
Logger.error(error)
|
|
50
|
+
return ``
|
|
55
51
|
}
|
|
56
52
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import * as RipGrep from '../RipGrep/RipGrep.js'
|
|
2
|
+
import * as RipGrepParsedLineType from '../RipGrepParsedLineType/RipGrepParsedLineType.js'
|
|
3
|
+
import * as TextSearchResultType from '../TextSearchResultType/TextSearchResultType.js'
|
|
4
|
+
|
|
5
|
+
const MAX_SEARCH_RESULTS = 300
|
|
6
|
+
|
|
7
|
+
const CHARS_BEFORE = 20
|
|
8
|
+
const CHARS_AFTER = 50
|
|
9
|
+
|
|
10
|
+
const toSearchResult = (parsedLine) => {
|
|
11
|
+
const results = []
|
|
12
|
+
const lines = parsedLine.data.lines.text
|
|
13
|
+
const lineNumber = parsedLine.data.line_number
|
|
14
|
+
for (const submatch of parsedLine.data.submatches) {
|
|
15
|
+
const previewStart = Math.max(submatch.start - CHARS_BEFORE, 0)
|
|
16
|
+
const previewEnd = Math.min(submatch.end + CHARS_AFTER, lines.length)
|
|
17
|
+
const previewText = lines.slice(previewStart, previewEnd)
|
|
18
|
+
results.push({
|
|
19
|
+
type: TextSearchResultType.Match,
|
|
20
|
+
start: submatch.start - previewStart,
|
|
21
|
+
end: submatch.end - previewStart,
|
|
22
|
+
lineNumber,
|
|
23
|
+
text: previewText,
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
return results
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// TODO update vscode-ripgrep when https://github.com/mhinz/vim-grepper/issues/244, https://github.com/BurntSushi/ripgrep/issues/1892 is fixed
|
|
30
|
+
|
|
31
|
+
// need to use '.' as last argument for ripgrep
|
|
32
|
+
// issue 1 https://github.com/nvim-telescope/telescope.nvim/pull/908/files
|
|
33
|
+
// issue 2 https://github.com/BurntSushi/ripgrep/issues/1892
|
|
34
|
+
// remove workaround when ripgrep is fixed
|
|
35
|
+
|
|
36
|
+
// TODO stats flag might not be necessary
|
|
37
|
+
// TODO update client
|
|
38
|
+
// TODO not always run nice, maybe configure nice via flag/options
|
|
39
|
+
|
|
40
|
+
export const search = async (searchDir, searchString, { threads = 1 } = {}) => {
|
|
41
|
+
// TODO reject promise when ripgrep search fails
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const ripGrepArgs = [
|
|
44
|
+
'--smart-case',
|
|
45
|
+
'--stats',
|
|
46
|
+
'--json',
|
|
47
|
+
'--threads',
|
|
48
|
+
`${threads}`,
|
|
49
|
+
'--fixed-strings',
|
|
50
|
+
searchString,
|
|
51
|
+
'.',
|
|
52
|
+
]
|
|
53
|
+
const childProcess = RipGrep.spawn(ripGrepArgs, {
|
|
54
|
+
cwd: searchDir,
|
|
55
|
+
})
|
|
56
|
+
const allSearchResults = Object.create(null)
|
|
57
|
+
let buffer = ''
|
|
58
|
+
let stats = {}
|
|
59
|
+
let limitHit = false
|
|
60
|
+
let numberOfResults = 0
|
|
61
|
+
// TODO use pipeline / transform stream maybe
|
|
62
|
+
|
|
63
|
+
const handleLine = (line) => {
|
|
64
|
+
const parsedLine = JSON.parse(line)
|
|
65
|
+
switch (parsedLine.type) {
|
|
66
|
+
case RipGrepParsedLineType.Begin:
|
|
67
|
+
allSearchResults[parsedLine.data.path.text] = [
|
|
68
|
+
{
|
|
69
|
+
type: TextSearchResultType.File,
|
|
70
|
+
start: 0,
|
|
71
|
+
end: 0,
|
|
72
|
+
lineNumber: 0,
|
|
73
|
+
text: parsedLine.data.path.text,
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
break
|
|
77
|
+
case RipGrepParsedLineType.Match:
|
|
78
|
+
numberOfResults++
|
|
79
|
+
allSearchResults[parsedLine.data.path.text].push(
|
|
80
|
+
...toSearchResult(parsedLine)
|
|
81
|
+
)
|
|
82
|
+
break
|
|
83
|
+
case RipGrepParsedLineType.Summary:
|
|
84
|
+
stats = parsedLine.data
|
|
85
|
+
break
|
|
86
|
+
default:
|
|
87
|
+
break
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let total = 0
|
|
92
|
+
const handleData = (chunk) => {
|
|
93
|
+
let newLineIndex = chunk.indexOf('\n')
|
|
94
|
+
const dataString = buffer + chunk
|
|
95
|
+
if (newLineIndex === -1) {
|
|
96
|
+
buffer = dataString
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
total += chunk.length
|
|
100
|
+
newLineIndex += buffer.length
|
|
101
|
+
let previousIndex = 0
|
|
102
|
+
while (newLineIndex >= 0) {
|
|
103
|
+
const line = dataString.slice(previousIndex, newLineIndex)
|
|
104
|
+
handleLine(line)
|
|
105
|
+
previousIndex = newLineIndex + 1
|
|
106
|
+
newLineIndex = dataString.indexOf('\n', previousIndex)
|
|
107
|
+
}
|
|
108
|
+
buffer = dataString.slice(previousIndex)
|
|
109
|
+
|
|
110
|
+
if (numberOfResults > MAX_SEARCH_RESULTS) {
|
|
111
|
+
limitHit = true
|
|
112
|
+
childProcess.kill()
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const handleClose = () => {
|
|
117
|
+
const results = Object.values(allSearchResults).flat(1)
|
|
118
|
+
resolve({
|
|
119
|
+
results,
|
|
120
|
+
stats,
|
|
121
|
+
limitHit,
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
const handleError = (error) => {
|
|
125
|
+
// TODO check type of error
|
|
126
|
+
console.error(error)
|
|
127
|
+
resolve({
|
|
128
|
+
results: [],
|
|
129
|
+
stats,
|
|
130
|
+
limitHit,
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
childProcess.stdout.setEncoding('utf8')
|
|
134
|
+
childProcess.stdout.on('data', handleData)
|
|
135
|
+
childProcess.once('close', handleClose)
|
|
136
|
+
childProcess.once('error', handleError)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const getCombinedMessage = (error, message) => {
|
|
2
|
+
let stringifiedError = `${error}`
|
|
3
|
+
if (stringifiedError.startsWith('Error: ')) {
|
|
4
|
+
stringifiedError = stringifiedError.slice(`Error: `.length)
|
|
5
|
+
} else if (stringifiedError.startsWith('VError: ')) {
|
|
6
|
+
stringifiedError = stringifiedError.slice(`VError: `.length)
|
|
7
|
+
}
|
|
8
|
+
if (message) {
|
|
9
|
+
return `${message}: ${stringifiedError}`
|
|
10
|
+
}
|
|
11
|
+
return stringifiedError
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const mergeStacks = (parent, child) => {
|
|
15
|
+
if (!child) {
|
|
16
|
+
return parent
|
|
17
|
+
}
|
|
18
|
+
const parentNewLineIndex = parent.indexOf('\n')
|
|
19
|
+
const childNewLineIndex = child.indexOf('\n')
|
|
20
|
+
const parentFirstLine = parent.slice(0, parentNewLineIndex)
|
|
21
|
+
const childRest = child.slice(childNewLineIndex)
|
|
22
|
+
const childFirstLine = child.slice(0, childNewLineIndex)
|
|
23
|
+
if (parentFirstLine.includes(childFirstLine)) {
|
|
24
|
+
return parentFirstLine + childRest
|
|
25
|
+
}
|
|
26
|
+
return child
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class VError extends Error {
|
|
30
|
+
constructor(error, message) {
|
|
31
|
+
const combinedMessage = getCombinedMessage(error, message)
|
|
32
|
+
super(combinedMessage)
|
|
33
|
+
this.name = 'VError'
|
|
34
|
+
if (error instanceof Error) {
|
|
35
|
+
this.stack = mergeStacks(this.stack, error.stack)
|
|
36
|
+
}
|
|
37
|
+
if (error.codeFrame) {
|
|
38
|
+
this.codeFrame = error.codeFrame
|
|
39
|
+
}
|
|
40
|
+
if (error.code) {
|
|
41
|
+
this.code = error.code
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process'
|
|
2
|
-
import * as Platform from '../Platform/Platform.js'
|
|
3
|
-
import * as RgPath from '../RgPath/RgPath.js'
|
|
4
|
-
import * as RipGrepParsedLineType from '../RipGrepParsedLineType/RipGrepParsedLineType.js'
|
|
5
|
-
import * as SplitLines from '../SplitLines/SplitLines.js'
|
|
6
|
-
|
|
7
|
-
const MAX_SEARCH_RESULTS = 300
|
|
8
|
-
|
|
9
|
-
const toSearchResult = (parsedLine) => {
|
|
10
|
-
return {
|
|
11
|
-
preview: parsedLine.data.lines.text,
|
|
12
|
-
absoluteOffset: parsedLine.data.absolute_offset,
|
|
13
|
-
lineNumber: parsedLine.data.line_number - 1,
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// TODO update vscode-ripgrep when https://github.com/mhinz/vim-grepper/issues/244, https://github.com/BurntSushi/ripgrep/issues/1892 is fixed
|
|
18
|
-
|
|
19
|
-
// need to use '.' as last argument for ripgrep
|
|
20
|
-
// issue 1 https://github.com/nvim-telescope/telescope.nvim/pull/908/files
|
|
21
|
-
// issue 2 https://github.com/BurntSushi/ripgrep/issues/1892
|
|
22
|
-
// remove workaround when ripgrep is fixed
|
|
23
|
-
|
|
24
|
-
// TODO no function call at toplevel!
|
|
25
|
-
const useNice = !Platform.isWindows
|
|
26
|
-
// TODO stats flag might not be necessary
|
|
27
|
-
// TODO update client
|
|
28
|
-
// TODO not always run nice, maybe configure nice via flag/options
|
|
29
|
-
|
|
30
|
-
export const search = async (searchDir, searchString) => {
|
|
31
|
-
// TODO reject promise when ripgrep search fails
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
const ripGrepArgs = [
|
|
34
|
-
'--smart-case',
|
|
35
|
-
'--stats',
|
|
36
|
-
'--json',
|
|
37
|
-
'--fixed-strings',
|
|
38
|
-
searchString,
|
|
39
|
-
'.',
|
|
40
|
-
]
|
|
41
|
-
const childProcess = useNice
|
|
42
|
-
? spawn('nice', ['-20', RgPath.rgPath, ...ripGrepArgs], {
|
|
43
|
-
cwd: searchDir,
|
|
44
|
-
})
|
|
45
|
-
: spawn(RgPath.rgPath, ripGrepArgs, {
|
|
46
|
-
cwd: searchDir,
|
|
47
|
-
})
|
|
48
|
-
const allSearchResults = Object.create(null)
|
|
49
|
-
let buffer = ''
|
|
50
|
-
let stats = {}
|
|
51
|
-
let limitHit = false
|
|
52
|
-
let numberOfResults = 0
|
|
53
|
-
// TODO use pipeline / transform stream maybe
|
|
54
|
-
|
|
55
|
-
const handleData = (chunk) => {
|
|
56
|
-
buffer += chunk
|
|
57
|
-
const lines = SplitLines.splitLines(buffer)
|
|
58
|
-
// @ts-ignore
|
|
59
|
-
buffer = lines.pop()
|
|
60
|
-
for (const line of lines) {
|
|
61
|
-
const parsedLine = JSON.parse(line)
|
|
62
|
-
console.log(parsedLine)
|
|
63
|
-
switch (parsedLine.type) {
|
|
64
|
-
case RipGrepParsedLineType.Begin: {
|
|
65
|
-
allSearchResults[parsedLine.data.path.text] = []
|
|
66
|
-
break
|
|
67
|
-
}
|
|
68
|
-
case RipGrepParsedLineType.Match:
|
|
69
|
-
numberOfResults++
|
|
70
|
-
allSearchResults[parsedLine.data.path.text].push(
|
|
71
|
-
toSearchResult(parsedLine)
|
|
72
|
-
)
|
|
73
|
-
break
|
|
74
|
-
case RipGrepParsedLineType.Summary:
|
|
75
|
-
stats = parsedLine.data
|
|
76
|
-
break
|
|
77
|
-
default:
|
|
78
|
-
break
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (numberOfResults > MAX_SEARCH_RESULTS) {
|
|
82
|
-
limitHit = true
|
|
83
|
-
childProcess.kill()
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const handleClose = () => {
|
|
88
|
-
resolve({
|
|
89
|
-
results: Object.entries(allSearchResults),
|
|
90
|
-
stats,
|
|
91
|
-
limitHit,
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
const handleError = (error) => {
|
|
95
|
-
// TODO check type of error
|
|
96
|
-
console.error(error)
|
|
97
|
-
resolve({
|
|
98
|
-
results: [],
|
|
99
|
-
stats,
|
|
100
|
-
limitHit,
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
childProcess.stdout.on('data', handleData)
|
|
104
|
-
childProcess.once('close', handleClose)
|
|
105
|
-
childProcess.once('error', handleError)
|
|
106
|
-
})
|
|
107
|
-
}
|