@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.
Files changed (40) hide show
  1. package/config/builtinCommands.json +1 -1
  2. package/config/defaultKeyBindings.json +5 -1
  3. package/config/defaultSettings.json +2 -0
  4. package/extensions/builtin.language-basics-cpp/src/tokenizeCpp.js +29 -6
  5. package/extensions/builtin.language-basics-css/src/tokenizeCss.js +176 -25
  6. package/extensions/builtin.language-basics-desktop/src/tokenizeDesktop.js +49 -5
  7. package/extensions/builtin.language-basics-gn/README.md +14 -0
  8. package/extensions/builtin.language-basics-gn/extension.json +12 -0
  9. package/extensions/builtin.language-basics-gn/src/tokenizeGn.js +178 -0
  10. package/extensions/builtin.language-basics-javascript/src/tokenizeJavaScript.js +5 -0
  11. package/extensions/builtin.language-basics-json5/README.md +16 -0
  12. package/extensions/builtin.language-basics-json5/extension.json +12 -0
  13. package/extensions/builtin.language-basics-json5/src/tokenizeJson5.js +321 -0
  14. package/extensions/builtin.language-basics-python/extension.json +2 -1
  15. package/extensions/builtin.language-basics-python/src/tokenizePython.js +45 -35
  16. package/extensions/builtin.language-basics-shellscript/extension.json +1 -0
  17. package/extensions/builtin.language-basics-typescript/src/tokenizeTypeScript.js +197 -15
  18. package/extensions/builtin.language-basics-xml/src/tokenizeXml.js +1 -1
  19. package/extensions/builtin.theme-ayu/color-theme.json +2 -0
  20. package/extensions/builtin.theme-slime/color-theme.json +6 -0
  21. package/index.js +5 -2
  22. package/package.json +5 -5
  23. package/src/parts/Command/Command.js +38 -40
  24. package/src/parts/ErrorCodes/ErrorCodes.js +2 -0
  25. package/src/parts/ExportStatic/ExportStatic.js +78 -2
  26. package/src/parts/ExtensionLink/ExtensionLink.js +2 -3
  27. package/src/parts/ExtensionManagement/ExtensionManagement.js +1 -1
  28. package/src/parts/ExtensionManifest/ExtensionManifest.js +2 -2
  29. package/src/parts/GetResponse/GetResponse.js +2 -2
  30. package/src/parts/Module/Module.js +2 -2
  31. package/src/parts/ModuleMap/ModuleMap.js +1 -1
  32. package/src/parts/PrettyError/PrettyError.js +55 -5
  33. package/src/parts/RipGrep/RipGrep.js +30 -0
  34. package/src/parts/SearchFile/SearchFile.js +12 -16
  35. package/src/parts/TextSearch/TextSearch.ipc.js +7 -0
  36. package/src/parts/TextSearch/TextSearch.js +138 -0
  37. package/src/parts/TextSearchResultType/TextSearchResultType.js +2 -0
  38. package/src/parts/VError/VError.js +44 -0
  39. package/src/parts/Search/Search.ipc.js +0 -7
  40. 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 }} param0
702
+ * @param {{root:string, pathPrefix:string , extensionPath:string, testPath:string }} param0
643
703
  */
644
- export const exportStatic = async ({ root, pathPrefix, extensionPath }) => {
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 VError from 'verror'
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 'verror'
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 VError from 'verror'
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 * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
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.invoke(message.method, handle, ...message.params)
11
- : await Command.invoke(message.method, ...message.params)
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 'Search.search':
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(stringifyJson(json))
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 RgPath from '../RgPath/RgPath.js'
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 Exec.exec(
36
- ripGrepPath,
37
- ['--files', '--sort-files'],
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
- console.info(`[info] ripgrep could not be found at "${ripGrepPath}"`)
47
- return []
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
- console.error(error)
54
- return []
49
+ Logger.error(error)
50
+ return ``
55
51
  }
56
52
  }
@@ -0,0 +1,7 @@
1
+ import * as TextSearch from './TextSearch.js'
2
+
3
+ export const name = 'TextSearch'
4
+
5
+ export const Commands = {
6
+ search: TextSearch.search,
7
+ }
@@ -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,2 @@
1
+ export const File = 1
2
+ export const Match = 2
@@ -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,7 +0,0 @@
1
- import * as Search from './Search.js'
2
-
3
- export const name = 'Search'
4
-
5
- export const Commands = {
6
- search: Search.search,
7
- }
@@ -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
- }