@lvce-editor/shared-process 0.16.3 → 0.16.4
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/package.json +4 -4
- package/src/parts/CollectTextSearchStdout/CollectTextSearchStdout.js +103 -0
- package/src/parts/IncrementalTextSearch/IncrementalTextSearch.js +49 -0
- package/src/parts/IncrementalTextSearch/IncremetalTextSearch.ipc.js +7 -0
- package/src/parts/LeftCut/LeftCut.js +20 -0
- package/src/parts/Module/Module.js +2 -0
- package/src/parts/ModuleId/ModuleId.js +1 -0
- package/src/parts/ModuleMap/ModuleMap.js +2 -0
- package/src/parts/ParseRipGrepLines/ParseRipGrepLines.js +11 -0
- package/src/parts/Platform/Platform.js +3 -3
- package/src/parts/RequiresSocket/RequiresSocket.js +1 -0
- package/src/parts/TextSearch/TextSearch.js +3 -93
- package/src/parts/ToTextSearchResult/ToTextSearchResult.js +2 -43
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lvce-editor/shared-process",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.4",
|
|
4
4
|
"description": "Utility package for @lvce-editor/server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@babel/code-frame": "^7.22.5",
|
|
20
|
-
"@lvce-editor/extension-host": "0.16.
|
|
21
|
-
"@lvce-editor/extension-host-helper-process": "0.16.
|
|
22
|
-
"@lvce-editor/pty-host": "0.16.
|
|
20
|
+
"@lvce-editor/extension-host": "0.16.4",
|
|
21
|
+
"@lvce-editor/extension-host-helper-process": "0.16.4",
|
|
22
|
+
"@lvce-editor/pty-host": "0.16.4",
|
|
23
23
|
"debug": "^4.3.4",
|
|
24
24
|
"execa": "^7.1.1",
|
|
25
25
|
"exit-hook": "^3.2.0",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Writable } from 'node:stream'
|
|
2
|
+
import { pipeline } from 'node:stream/promises'
|
|
3
|
+
import * as EncodingType from '../EncodingType/EncodingType.js'
|
|
4
|
+
import * as GetNewLineIndex from '../GetNewLineIndex/GetNewLineIndex.js'
|
|
5
|
+
import * as RipGrepParsedLineType from '../RipGrepParsedLineType/RipGrepParsedLineType.js'
|
|
6
|
+
import * as TextSearchResultType from '../TextSearchResultType/TextSearchResultType.js'
|
|
7
|
+
import * as ToTextSearchResult from '../ToTextSearchResult/ToTextSearchResult.js'
|
|
8
|
+
// TODO update vscode-ripgrep when https://github.com/mhinz/vim-grepper/issues/244, https://github.com/BurntSushi/ripgrep/issues/1892 is fixed
|
|
9
|
+
|
|
10
|
+
// need to use '.' as last argument for ripgrep
|
|
11
|
+
// issue 1 https://github.com/nvim-telescope/telescope.nvim/pull/908/files
|
|
12
|
+
// issue 2 https://github.com/BurntSushi/ripgrep/issues/1892
|
|
13
|
+
// remove workaround when ripgrep is fixed
|
|
14
|
+
|
|
15
|
+
// TODO stats flag might not be necessary
|
|
16
|
+
// TODO update client
|
|
17
|
+
// TODO not always run nice, maybe configure nice via flag/options
|
|
18
|
+
|
|
19
|
+
export const collectStdout = async (childProcess, maxSearchResults, charsBefore, charsAfter) => {
|
|
20
|
+
const allSearchResults = Object.create(null)
|
|
21
|
+
let buffer = ''
|
|
22
|
+
let stats = {}
|
|
23
|
+
let limitHit = false
|
|
24
|
+
let numberOfResults = 0
|
|
25
|
+
|
|
26
|
+
childProcess.stdout.setEncoding(EncodingType.Utf8)
|
|
27
|
+
|
|
28
|
+
const handleLine = (line) => {
|
|
29
|
+
const parsedLine = JSON.parse(line)
|
|
30
|
+
const { type, data } = parsedLine
|
|
31
|
+
switch (type) {
|
|
32
|
+
case RipGrepParsedLineType.Begin:
|
|
33
|
+
allSearchResults[data.path.text] = [
|
|
34
|
+
{
|
|
35
|
+
type: TextSearchResultType.File,
|
|
36
|
+
start: 0,
|
|
37
|
+
end: 0,
|
|
38
|
+
lineNumber: 0,
|
|
39
|
+
text: data.path.text,
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
break
|
|
43
|
+
case RipGrepParsedLineType.Match:
|
|
44
|
+
const remaining = maxSearchResults - numberOfResults
|
|
45
|
+
const matches = ToTextSearchResult.toTextSearchResult(parsedLine, remaining, charsBefore, charsAfter)
|
|
46
|
+
numberOfResults += matches.length
|
|
47
|
+
allSearchResults[data.path.text].push(...matches)
|
|
48
|
+
break
|
|
49
|
+
case RipGrepParsedLineType.Summary:
|
|
50
|
+
stats = data
|
|
51
|
+
break
|
|
52
|
+
default:
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const handleData = (chunk) => {
|
|
58
|
+
let newLineIndex = GetNewLineIndex.getNewLineIndex(chunk)
|
|
59
|
+
const dataString = buffer + chunk
|
|
60
|
+
if (newLineIndex === -1) {
|
|
61
|
+
buffer = dataString
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
newLineIndex += buffer.length
|
|
65
|
+
let previousIndex = 0
|
|
66
|
+
while (newLineIndex >= 0) {
|
|
67
|
+
const line = dataString.slice(previousIndex, newLineIndex)
|
|
68
|
+
handleLine(line)
|
|
69
|
+
previousIndex = newLineIndex + 1
|
|
70
|
+
newLineIndex = GetNewLineIndex.getNewLineIndex(dataString, previousIndex)
|
|
71
|
+
}
|
|
72
|
+
buffer = dataString.slice(previousIndex)
|
|
73
|
+
|
|
74
|
+
if (numberOfResults > maxSearchResults) {
|
|
75
|
+
limitHit = true
|
|
76
|
+
childProcess.kill()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await pipeline(
|
|
81
|
+
childProcess.stdout,
|
|
82
|
+
new Writable({
|
|
83
|
+
decodeStrings: false,
|
|
84
|
+
construct(callback) {
|
|
85
|
+
callback()
|
|
86
|
+
},
|
|
87
|
+
write(chunk, encoding, callback) {
|
|
88
|
+
try {
|
|
89
|
+
handleData(chunk)
|
|
90
|
+
callback()
|
|
91
|
+
} catch (error) {
|
|
92
|
+
callback(error)
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
const results = Object.values(allSearchResults).flat(1)
|
|
98
|
+
return {
|
|
99
|
+
results,
|
|
100
|
+
stats,
|
|
101
|
+
limitHit,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as RipGrep from '../RipGrep/RipGrep.js'
|
|
2
|
+
|
|
3
|
+
const connectProcess = (ipc, id, childProcess) => {
|
|
4
|
+
let count = 0
|
|
5
|
+
const cleanup = () => {
|
|
6
|
+
childProcess.kill()
|
|
7
|
+
}
|
|
8
|
+
childProcess.stdout.on('data', (data) => {
|
|
9
|
+
count++
|
|
10
|
+
if (count > 20) {
|
|
11
|
+
cleanup()
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
// console.log({ data: data.toString() })
|
|
15
|
+
ipc.send({
|
|
16
|
+
jsonrpc: '2.0',
|
|
17
|
+
method: 'IncrementalTextSearch.handleData',
|
|
18
|
+
params: [id, data.toString()],
|
|
19
|
+
})
|
|
20
|
+
// emitter.dispatchEvent(new Event('data', x))
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
childProcess.on('exit', () => {
|
|
24
|
+
ipc.send({
|
|
25
|
+
jsonrpc: '2.0',
|
|
26
|
+
method: 'IncrementalTextSearch.handleExit',
|
|
27
|
+
params: [id],
|
|
28
|
+
})
|
|
29
|
+
// emitter.dispatchEvent(new Event('exit'))
|
|
30
|
+
})
|
|
31
|
+
childProcess.on('error', () => {
|
|
32
|
+
ipc.send({
|
|
33
|
+
jsonrpc: '2.0',
|
|
34
|
+
method: 'IncrementalTextSearch.handleError',
|
|
35
|
+
params: [id],
|
|
36
|
+
})
|
|
37
|
+
// emitter.dispatchEvent(new Event('error'))
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const start = async (ipc, id, { searchDir = '', maxSearchResults = 20_000, ripGrepArgs = [] } = {}) => {
|
|
42
|
+
console.log({ ipc })
|
|
43
|
+
const emitter = new EventTarget()
|
|
44
|
+
const childProcess = RipGrep.spawn(ripGrepArgs, {
|
|
45
|
+
cwd: searchDir,
|
|
46
|
+
})
|
|
47
|
+
console.log('start', { searchDir, ripGrepArgs, id })
|
|
48
|
+
connectProcess(ipc, id, childProcess)
|
|
49
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const RE_WORD = /\b/g
|
|
2
|
+
|
|
3
|
+
export const leftCut = (text, final, charsBefore) => {
|
|
4
|
+
if (text.length < charsBefore) {
|
|
5
|
+
return text.length
|
|
6
|
+
}
|
|
7
|
+
let i = 0
|
|
8
|
+
while (RE_WORD.test(text)) {
|
|
9
|
+
if (final - RE_WORD.lastIndex < charsBefore) {
|
|
10
|
+
break
|
|
11
|
+
}
|
|
12
|
+
i = RE_WORD.lastIndex
|
|
13
|
+
RE_WORD.lastIndex++
|
|
14
|
+
}
|
|
15
|
+
RE_WORD.lastIndex = 0
|
|
16
|
+
if (final - i > charsBefore + 10) {
|
|
17
|
+
return final - charsBefore
|
|
18
|
+
}
|
|
19
|
+
return i
|
|
20
|
+
}
|
|
@@ -72,6 +72,8 @@ export const load = (moduleId) => {
|
|
|
72
72
|
return import('../HandleCliArgs/HandleCliArgs.ipc.js')
|
|
73
73
|
case ModuleId.ListProcessesWithMemoryUsage:
|
|
74
74
|
return import('../ListProcessesWithMemoryUsage/ListProcessesWithMemoryUsage.ipc.js')
|
|
75
|
+
case ModuleId.IncrementalTextSearch:
|
|
76
|
+
return import('../IncrementalTextSearch/IncremetalTextSearch.ipc.js')
|
|
75
77
|
default:
|
|
76
78
|
throw new Error(`module ${moduleId} not found`)
|
|
77
79
|
}
|
|
@@ -185,6 +185,8 @@ export const getModuleId = (commandId) => {
|
|
|
185
185
|
return ModuleId.HandleCliArgs
|
|
186
186
|
case 'ListProcessesWithMemoryUsage.listProcessesWithMemoryUsage':
|
|
187
187
|
return ModuleId.ListProcessesWithMemoryUsage
|
|
188
|
+
case 'IncrementalTextSearch.start':
|
|
189
|
+
return ModuleId.IncrementalTextSearch
|
|
188
190
|
default:
|
|
189
191
|
throw new CommandNotFoundError(commandId)
|
|
190
192
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as EncodingType from '../EncodingType/EncodingType.js'
|
|
2
|
+
|
|
3
|
+
export const parseRipGrepLines = (parsedLineData) => {
|
|
4
|
+
if (parsedLineData.lines.text) {
|
|
5
|
+
return parsedLineData.lines.text
|
|
6
|
+
}
|
|
7
|
+
if (parsedLineData.lines.bytes) {
|
|
8
|
+
return Buffer.from(parsedLineData.lines.bytes, EncodingType.Base64).toString()
|
|
9
|
+
}
|
|
10
|
+
throw new Error(`unable to parse line data`)
|
|
11
|
+
}
|
|
@@ -165,11 +165,11 @@ export const getSetupName = () => {
|
|
|
165
165
|
return 'Lvce-Setup'
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
export const version = '0.16.
|
|
168
|
+
export const version = '0.16.4'
|
|
169
169
|
|
|
170
|
-
export const commit = '
|
|
170
|
+
export const commit = '4ec07e5'
|
|
171
171
|
|
|
172
|
-
export const date = '2023-07-
|
|
172
|
+
export const date = '2023-07-04T18:03:04.000Z'
|
|
173
173
|
|
|
174
174
|
export const getVersion = () => {
|
|
175
175
|
return version
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { pipeline } from 'node:stream/promises'
|
|
3
|
-
import * as EncodingType from '../EncodingType/EncodingType.js'
|
|
4
|
-
import * as GetNewLineIndex from '../GetNewLineIndex/GetNewLineIndex.js'
|
|
1
|
+
import * as CollectTextSearchStdout from '../CollectTextSearchStdout/CollectTextSearchStdout.js'
|
|
5
2
|
import * as ProcessExitEventType from '../ProcessExitEventType/ProcessExitEventType.js'
|
|
6
3
|
import * as RipGrep from '../RipGrep/RipGrep.js'
|
|
7
|
-
import * as RipGrepParsedLineType from '../RipGrepParsedLineType/RipGrepParsedLineType.js'
|
|
8
4
|
import { TextSearchError } from '../TextSearchError/TextSearchError.js'
|
|
9
|
-
import * as TextSearchResultType from '../TextSearchResultType/TextSearchResultType.js'
|
|
10
|
-
import * as ToTextSearchResult from '../ToTextSearchResult/ToTextSearchResult.js'
|
|
11
5
|
import * as WaitForProcessToExit from '../WaitForProcessToExit/WaitForProcessToExit.js'
|
|
6
|
+
|
|
12
7
|
// TODO update vscode-ripgrep when https://github.com/mhinz/vim-grepper/issues/244, https://github.com/BurntSushi/ripgrep/issues/1892 is fixed
|
|
13
8
|
|
|
14
9
|
// need to use '.' as last argument for ripgrep
|
|
@@ -20,98 +15,13 @@ import * as WaitForProcessToExit from '../WaitForProcessToExit/WaitForProcessToE
|
|
|
20
15
|
// TODO update client
|
|
21
16
|
// TODO not always run nice, maybe configure nice via flag/options
|
|
22
17
|
|
|
23
|
-
const collectStdout = async (childProcess, maxSearchResults, charsBefore, charsAfter) => {
|
|
24
|
-
const allSearchResults = Object.create(null)
|
|
25
|
-
let buffer = ''
|
|
26
|
-
let stats = {}
|
|
27
|
-
let limitHit = false
|
|
28
|
-
let numberOfResults = 0
|
|
29
|
-
|
|
30
|
-
childProcess.stdout.setEncoding(EncodingType.Utf8)
|
|
31
|
-
|
|
32
|
-
const handleLine = (line) => {
|
|
33
|
-
const parsedLine = JSON.parse(line)
|
|
34
|
-
switch (parsedLine.type) {
|
|
35
|
-
case RipGrepParsedLineType.Begin:
|
|
36
|
-
allSearchResults[parsedLine.data.path.text] = [
|
|
37
|
-
{
|
|
38
|
-
type: TextSearchResultType.File,
|
|
39
|
-
start: 0,
|
|
40
|
-
end: 0,
|
|
41
|
-
lineNumber: 0,
|
|
42
|
-
text: parsedLine.data.path.text,
|
|
43
|
-
},
|
|
44
|
-
]
|
|
45
|
-
break
|
|
46
|
-
case RipGrepParsedLineType.Match:
|
|
47
|
-
const remaining = maxSearchResults - numberOfResults
|
|
48
|
-
const matches = ToTextSearchResult.toTextSearchResult(parsedLine, remaining, charsBefore, charsAfter)
|
|
49
|
-
numberOfResults += matches.length
|
|
50
|
-
allSearchResults[parsedLine.data.path.text].push(...matches)
|
|
51
|
-
break
|
|
52
|
-
case RipGrepParsedLineType.Summary:
|
|
53
|
-
stats = parsedLine.data
|
|
54
|
-
break
|
|
55
|
-
default:
|
|
56
|
-
break
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const handleData = (chunk) => {
|
|
61
|
-
let newLineIndex = GetNewLineIndex.getNewLineIndex(chunk)
|
|
62
|
-
const dataString = buffer + chunk
|
|
63
|
-
if (newLineIndex === -1) {
|
|
64
|
-
buffer = dataString
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
newLineIndex += buffer.length
|
|
68
|
-
let previousIndex = 0
|
|
69
|
-
while (newLineIndex >= 0) {
|
|
70
|
-
const line = dataString.slice(previousIndex, newLineIndex)
|
|
71
|
-
handleLine(line)
|
|
72
|
-
previousIndex = newLineIndex + 1
|
|
73
|
-
newLineIndex = GetNewLineIndex.getNewLineIndex(dataString, previousIndex)
|
|
74
|
-
}
|
|
75
|
-
buffer = dataString.slice(previousIndex)
|
|
76
|
-
|
|
77
|
-
if (numberOfResults > maxSearchResults) {
|
|
78
|
-
limitHit = true
|
|
79
|
-
childProcess.kill()
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
await pipeline(
|
|
84
|
-
childProcess.stdout,
|
|
85
|
-
new Writable({
|
|
86
|
-
decodeStrings: false,
|
|
87
|
-
construct(callback) {
|
|
88
|
-
callback()
|
|
89
|
-
},
|
|
90
|
-
write(chunk, encoding, callback) {
|
|
91
|
-
try {
|
|
92
|
-
handleData(chunk)
|
|
93
|
-
callback()
|
|
94
|
-
} catch (error) {
|
|
95
|
-
callback(error)
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
})
|
|
99
|
-
)
|
|
100
|
-
const results = Object.values(allSearchResults).flat(1)
|
|
101
|
-
return {
|
|
102
|
-
results,
|
|
103
|
-
stats,
|
|
104
|
-
limitHit,
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
18
|
export const search = async ({ searchDir = '', maxSearchResults = 20_000, ripGrepArgs = [] } = {}) => {
|
|
109
19
|
const charsBefore = 26
|
|
110
20
|
const charsAfter = 50
|
|
111
21
|
const childProcess = RipGrep.spawn(ripGrepArgs, {
|
|
112
22
|
cwd: searchDir,
|
|
113
23
|
})
|
|
114
|
-
const pipeLinePromise = collectStdout(childProcess, maxSearchResults, charsBefore, charsAfter)
|
|
24
|
+
const pipeLinePromise = CollectTextSearchStdout.collectStdout(childProcess, maxSearchResults, charsBefore, charsAfter)
|
|
115
25
|
const closePromise = WaitForProcessToExit.waitForProcessToExit(childProcess)
|
|
116
26
|
const [pipeLineResult, exitResult] = await Promise.all([pipeLinePromise, closePromise])
|
|
117
27
|
if (exitResult.type === ProcessExitEventType.Error) {
|
|
@@ -1,57 +1,16 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as ParseRipGrepLines from '../ParseRipGrepLines/ParseRipGrepLines.js'
|
|
2
2
|
import * as TextSearchResultType from '../TextSearchResultType/TextSearchResultType.js'
|
|
3
3
|
|
|
4
|
-
const getLines = (parsedLineData) => {
|
|
5
|
-
if (parsedLineData.lines.text) {
|
|
6
|
-
return parsedLineData.lines.text
|
|
7
|
-
}
|
|
8
|
-
if (parsedLineData.lines.bytes) {
|
|
9
|
-
return Buffer.from(parsedLineData.lines.bytes, EncodingType.Base64).toString()
|
|
10
|
-
}
|
|
11
|
-
throw new Error(`unable to parse line data`)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const getRemainingSubMatches = (submatches, remaining) => {
|
|
15
|
-
if (submatches.length < remaining) {
|
|
16
|
-
return submatches
|
|
17
|
-
}
|
|
18
|
-
return submatches.slice(0, remaining)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const RE_WORD = /\b/g
|
|
22
|
-
|
|
23
|
-
const leftCut = (text, final, charsBefore) => {
|
|
24
|
-
if (text.length < charsBefore) {
|
|
25
|
-
return text.length
|
|
26
|
-
}
|
|
27
|
-
let i = 0
|
|
28
|
-
while (RE_WORD.test(text)) {
|
|
29
|
-
if (final - RE_WORD.lastIndex < charsBefore) {
|
|
30
|
-
break
|
|
31
|
-
}
|
|
32
|
-
i = RE_WORD.lastIndex
|
|
33
|
-
RE_WORD.lastIndex++
|
|
34
|
-
}
|
|
35
|
-
RE_WORD.lastIndex = 0
|
|
36
|
-
if (final - i > charsBefore + 10) {
|
|
37
|
-
return final - charsBefore
|
|
38
|
-
}
|
|
39
|
-
return i
|
|
40
|
-
}
|
|
41
|
-
|
|
42
4
|
export const toTextSearchResult = (parsedLine, remaining, charsBefore, charsAfter) => {
|
|
43
5
|
const results = []
|
|
44
6
|
const parsedLineData = parsedLine.data
|
|
45
|
-
const lines =
|
|
7
|
+
const lines = ParseRipGrepLines.parseRipGrepLines(parsedLineData)
|
|
46
8
|
const lineNumber = parsedLineData.line_number
|
|
47
9
|
const submatches = parsedLineData.submatches
|
|
48
10
|
const linesLength = lines.length
|
|
49
11
|
for (const submatch of submatches) {
|
|
50
12
|
const previewStart = Math.max(submatch.start - charsBefore, 0)
|
|
51
13
|
let actualStart = previewStart
|
|
52
|
-
if (actualStart > 0) {
|
|
53
|
-
actualStart = leftCut(lines, submatch.start, charsBefore)
|
|
54
|
-
}
|
|
55
14
|
const previewEnd = Math.min(submatch.end + charsAfter, linesLength)
|
|
56
15
|
const previewText = lines.slice(actualStart, previewEnd)
|
|
57
16
|
results.push({
|