@lvce-editor/shared-process 0.11.5 → 0.11.7

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.
@@ -326,7 +326,7 @@
326
326
  "label": "File: Open Recent"
327
327
  },
328
328
  {
329
- "id": "Dialog.showAbout",
329
+ "id": "About.showAbout",
330
330
  "label": "Help: About"
331
331
  },
332
332
  {
@@ -149,7 +149,7 @@
149
149
  },
150
150
  {
151
151
  "key": "ctrl+Enter",
152
- "command": "SourceControl.acceptInput",
152
+ "command": "Source Control.acceptInput",
153
153
  "when": "focus.sourceControlInput"
154
154
  },
155
155
  {
@@ -4,6 +4,9 @@
4
4
  export const State = {
5
5
  TopLevelContent: 1,
6
6
  InsideLineComment: 2,
7
+ AfterPropertyName: 3,
8
+ AfterPropertyNameAfterEqualSign: 4,
9
+ InsideString: 5,
7
10
  }
8
11
 
9
12
  export const StateMap = {
@@ -23,6 +26,10 @@ export const TokenType = {
23
26
  Comment: 885,
24
27
  Query: 886,
25
28
  Text: 887,
29
+ PropertyName: 12,
30
+ PropertyValueString: 14,
31
+ Punctuation: 13,
32
+ LanguageConstant: 11,
26
33
  }
27
34
 
28
35
  export const TokenMap = {
@@ -34,13 +41,19 @@ export const TokenMap = {
34
41
  [TokenType.Comment]: 'Comment',
35
42
  [TokenType.Query]: 'Query',
36
43
  [TokenType.Text]: 'Text',
44
+ [TokenType.Punctuation]: 'Punctuation',
45
+ [TokenType.LanguageConstant]: 'LanguageConstant',
46
+ [TokenType.PropertyName]: 'JsonPropertyName',
47
+ [TokenType.LanguageConstant]: 'LanguageConstant',
48
+ [TokenType.Punctuation]: 'Punctuation',
49
+ [TokenType.PropertyValueString]: 'YamlPropertyValueString',
37
50
  }
38
51
 
39
52
  const RE_LINE_COMMENT_START = /^#/
40
53
  const RE_WHITESPACE = /^ +/
41
54
  const RE_CURLY_OPEN = /^\{/
42
55
  const RE_CURLY_CLOSE = /^\}/
43
- const RE_PROPERTY_NAME = /^[a-zA-Z\-]+\b/
56
+ const RE_PROPERTY_NAME = /^[a-zA-Z\-\_\d]+\b(?=\s*=)/
44
57
  const RE_COLON = /^:/
45
58
  const RE_PROPERTY_VALUE = /^[^;\}]+/
46
59
  const RE_SEMICOLON = /^;/
@@ -62,6 +75,11 @@ const RE_STAR = /^\*/
62
75
  const RE_QUERY_NAME = /^[a-z\-]+/
63
76
  const RE_QUERY_CONTENT = /^[^\)]+/
64
77
  const RE_COMBINATOR = /^[\+\>\~]/
78
+ const RE_EQUAL_SIGN = /^=/
79
+ const RE_QUOTE_DOUBLE = /^"/
80
+ const RE_STRING_DOUBLE_QUOTE_CONTENT = /^[^"]+/
81
+ const RE_LINE_COMMENT = /^#.*/s
82
+ const RE_LANGUAGE_CONSTANT = /^(?:true|false)\b/
65
83
 
66
84
  export const initialLineState = {
67
85
  state: State.TopLevelContent,
@@ -85,9 +103,12 @@ export const tokenizeLine = (line, lineState) => {
85
103
  const part = line.slice(index)
86
104
  switch (state) {
87
105
  case State.TopLevelContent:
88
- if ((next = part.match(RE_LINE_COMMENT_START))) {
106
+ if ((next = part.match(RE_PROPERTY_NAME))) {
107
+ token = TokenType.PropertyName
108
+ state = State.AfterPropertyName
109
+ } else if ((next = part.match(RE_LINE_COMMENT))) {
89
110
  token = TokenType.Comment
90
- state = State.InsideLineComment
111
+ state = State.TopLevelContent
91
112
  } else if ((next = part.match(RE_WHITESPACE))) {
92
113
  token = TokenType.Whitespace
93
114
  state = State.TopLevelContent
@@ -99,10 +120,33 @@ export const tokenizeLine = (line, lineState) => {
99
120
  throw new Error('no')
100
121
  }
101
122
  break
102
- case State.InsideLineComment:
103
- if ((next = part.match(RE_ANYTHING))) {
123
+ case State.AfterPropertyName:
124
+ if ((next = part.match(RE_EQUAL_SIGN))) {
125
+ token = TokenType.Punctuation
126
+ state = State.AfterPropertyNameAfterEqualSign
127
+ } else if ((next = part.match(RE_WHITESPACE))) {
128
+ token = TokenType.Whitespace
129
+ state = State.AfterPropertyName
130
+ } else {
131
+ throw new Error('no')
132
+ }
133
+ break
134
+ case State.AfterPropertyNameAfterEqualSign:
135
+ if ((next = part.match(RE_WHITESPACE))) {
136
+ token = TokenType.Whitespace
137
+ state = State.AfterPropertyNameAfterEqualSign
138
+ } else if ((next = part.match(RE_LANGUAGE_CONSTANT))) {
139
+ token = TokenType.LanguageConstant
140
+ state = State.TopLevelContent
141
+ } else if ((next = part.match(RE_LINE_COMMENT))) {
104
142
  token = TokenType.Comment
105
143
  state = State.TopLevelContent
144
+ } else if ((next = part.match(RE_QUOTE_DOUBLE))) {
145
+ token = TokenType.Punctuation
146
+ state = State.InsideString
147
+ } else if ((next = part.match(RE_ANYTHING))) {
148
+ token = TokenType.PropertyValueString
149
+ state = State.TopLevelContent
106
150
  } else {
107
151
  throw new Error('no')
108
152
  }
@@ -37,6 +37,7 @@
37
37
  "fileNames": [
38
38
  "APKBUILD",
39
39
  "PKGBUILD",
40
+ ".INSTALL",
40
41
  ".envrc",
41
42
  ".hushlogin",
42
43
  "zshrc",
@@ -79,7 +79,7 @@ const RE_SLASH = /^\//
79
79
  const RE_STRING_DOUBLE_QUOTE_CONTENT = /^[^"]+/
80
80
  const RE_STRING_SINGLE_QUOTE_CONTENT = /^[^']+/
81
81
  const RE_TAG_TEXT = /^[^\s>]+/
82
- const RE_TAGNAME = /^[!\w]+/
82
+ const RE_TAGNAME = /^[!\w\-]+/
83
83
  const RE_TEXT = /^[^<>\n]+/
84
84
  const RE_WHITESPACE = /^\s+/
85
85
  const RE_WORD = /^[^\s]+/
package/index.js CHANGED
@@ -5,12 +5,15 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
5
5
 
6
6
  export const sharedProcessPath = join(__dirname, 'src', 'sharedProcessMain.js')
7
7
 
8
- export const exportStatic = async ({ extensionPath = process.cwd() } = {}) => {
8
+ export const exportStatic = async ({
9
+ extensionPath = process.cwd(),
10
+ testPath = '',
11
+ } = {}) => {
9
12
  const fn = await import('./src/parts/ExportStatic/ExportStatic.js')
10
13
  const root = process.cwd()
11
14
  if (extensionPath !== root) {
12
15
  extensionPath = join(root, extensionPath)
13
16
  }
14
17
  const pathPrefix = process.env.PATH_PREFIX || ''
15
- await fn.exportStatic({ root, pathPrefix, extensionPath })
18
+ await fn.exportStatic({ root, pathPrefix, extensionPath, testPath })
16
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/shared-process",
3
- "version": "0.11.5",
3
+ "version": "0.11.7",
4
4
  "description": "Utility package for @lvce-editor/server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -17,12 +17,12 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@babel/code-frame": "^7.18.6",
20
- "@lvce-editor/extension-host": "0.11.5",
21
- "@lvce-editor/extension-host-helper-process": "0.11.5",
22
- "@lvce-editor/pty-host": "0.11.5",
20
+ "@lvce-editor/extension-host": "0.11.7",
21
+ "@lvce-editor/extension-host-helper-process": "0.11.7",
22
+ "@lvce-editor/pty-host": "0.11.7",
23
23
  "debug": "^4.3.4",
24
24
  "execa": "^6.1.0",
25
- "exit-hook": "^3.1.2",
25
+ "exit-hook": "^3.1.4",
26
26
  "got": "^12.5.3",
27
27
  "is-object": "^1.0.2",
28
28
  "lines-and-columns": "^2.0.3",
@@ -4,6 +4,7 @@
4
4
 
5
5
  import VError from 'verror'
6
6
  import * as Exec from '../Exec/Exec.js'
7
+ import * as SplitLines from '../SplitLines/SplitLines.js'
7
8
 
8
9
  const removePrefix = (file) => {
9
10
  if (file.startsWith('file://')) {
@@ -35,7 +36,7 @@ export const readFiles = async () => {
35
36
  }
36
37
  throw error
37
38
  }
38
- const [type, ...files] = result.stdout.split('\n')
39
+ const [type, ...files] = SplitLines.splitLines(result.stdout)
39
40
  const actualFiles = files.map(removePrefix)
40
41
  return {
41
42
  source: 'gnomeCopiedFiles',
@@ -2,6 +2,7 @@ import { createWriteStream, writeFileSync } from 'node:fs'
2
2
  import { performance } from 'node:perf_hooks'
3
3
  import * as ExtensionHost from '../ExtensionHost/ExtensionHost.js'
4
4
  import * as Process from '../Process/Process.js'
5
+ import * as Timeout from '../Timeout/Timeout.js'
5
6
 
6
7
  export const measureLatencyBetweenExtensionHostAndSharedProcess = async (
7
8
  socket,
@@ -77,7 +78,7 @@ export const allocateMemory = () => {
77
78
 
78
79
  /* istanbul ignore next */
79
80
  export const crashSharedProcess = () => {
80
- setTimeout(() => {
81
+ Timeout.setTimeout(() => {
81
82
  throw new Error('oops')
82
83
  }, 0)
83
84
  }
@@ -113,7 +114,7 @@ export const createProfile = async () => {
113
114
  session.post('Profiler.start', () => {
114
115
  // Invoke business logic under measurement here...
115
116
 
116
- setTimeout(() => {
117
+ Timeout.setTimeout(() => {
117
118
  session.post('Profiler.stop', (error, { profile }) => {
118
119
  // Write profile to disk, upload, etc.
119
120
  if (!error) {
@@ -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,7 +1,7 @@
1
1
  import VError from 'verror'
2
- import * as Assert from '../Assert/Assert.js'
3
2
  import * as Extract from '../Extract/Extract.js'
4
3
  import * as FileSystem from '../FileSystem/FileSystem.js'
4
+ import * as JsonFile from '../JsonFile/JsonFile.js'
5
5
  import * as Path from '../Path/Path.js'
6
6
  import * as Platform from '../Platform/Platform.js'
7
7
 
@@ -24,8 +24,7 @@ export const install = async ({ path }) => {
24
24
  await Extract.extractTarBr(path, cachedExtensionPath)
25
25
  const extensionsPath = Platform.getExtensionsPath()
26
26
  const manifestPath = Path.join(cachedExtensionPath, 'extension.json')
27
- const manifestContent = await FileSystem.readFile(manifestPath)
28
- const manifestJson = JSON.parse(manifestContent)
27
+ const manifestJson = await JsonFile.readJson(manifestPath)
29
28
  const id = manifestJson.id
30
29
  if (!id) {
31
30
  throw new Error('missing id in extension manifest')
@@ -1,6 +1,7 @@
1
1
  import VError from 'verror'
2
2
  import * as DownloadAndExtract from '../DownloadAndExtract/DownloadAndExtract.js'
3
3
  import * as FileSystem from '../FileSystem/FileSystem.js'
4
+ import * as JsonFile from '../JsonFile/JsonFile.js'
4
5
  import * as Path from '../Path/Path.js'
5
6
  import * as Platform from '../Platform/Platform.js'
6
7
 
@@ -19,8 +20,7 @@ export const install = async ({ user, repo, branch }) => {
19
20
  })
20
21
  const extensionsPath = Platform.getExtensionsPath()
21
22
  const manifestPath = Path.join(cachedExtensionPath, 'extension.json')
22
- const manifestContent = await FileSystem.readFile(manifestPath)
23
- const manifestJson = JSON.parse(manifestContent)
23
+ const manifestJson = await JsonFile.readJson(manifestPath)
24
24
  const id = manifestJson.id
25
25
  if (!id) {
26
26
  throw new Error('missing id in extension manifest')
@@ -1,11 +1,11 @@
1
- import { readFile, rename, rm } from 'fs/promises'
1
+ import { rename, rm } from 'fs/promises'
2
2
  import { join } from 'path'
3
3
  import VError from 'verror'
4
4
  import * as Download from '../Download/Download.js'
5
5
  import * as Extract from '../Extract/Extract.js'
6
+ import * as JsonFile from '../JsonFile/JsonFile.js'
6
7
  import * as Platform from '../Platform/Platform.js'
7
8
  import * as TmpFile from '../TmpFile/TmpFile.js'
8
- import * as EncodingType from '../EncodingType/EncodingType.js'
9
9
 
10
10
  export const install = async ({ url }) => {
11
11
  try {
@@ -18,8 +18,7 @@ export const install = async ({ url }) => {
18
18
  const tmpDir = await TmpFile.getTmpDir()
19
19
  await Extract.extractTarBr(tmpFile, tmpDir)
20
20
  const manifestPath = join(tmpDir, 'extension.json')
21
- const manifestContent = await readFile(manifestPath, EncodingType.Utf8)
22
- const manifestJson = JSON.parse(manifestContent)
21
+ const manifestJson = await JsonFile.readJson(manifestPath)
23
22
  const id = manifestJson.id
24
23
  if (!id) {
25
24
  throw new Error('missing id in extension manifest')
@@ -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
 
@@ -32,6 +32,7 @@ export const getResponse = async (message, handle) => {
32
32
  },
33
33
  }
34
34
  }
35
+ // @ts-ignore
35
36
  if (error && error instanceof Error && error.code === ErrorCodes.ENOENT) {
36
37
  return {
37
38
  jsonrpc: JsonRpc.Version,
@@ -43,7 +44,7 @@ export const getResponse = async (message, handle) => {
43
44
  }
44
45
  }
45
46
  const prettyError = PrettyError.prepare(error)
46
- PrettyError.print(prettyError)
47
+ PrettyError.print(prettyError, `[shared-process] `)
47
48
  return {
48
49
  jsonrpc: JsonRpc.Version,
49
50
  id: message.id,
@@ -1,8 +1,9 @@
1
- import { readFileSync } from 'node:fs'
2
- import { fileURLToPath } from 'node:url'
3
1
  import { codeFrameColumns } from '@babel/code-frame'
4
2
  import cleanStack from 'clean-stack'
5
3
  import { LinesAndColumns } from 'lines-and-columns'
4
+ import { readFileSync } from 'node:fs'
5
+ import { fileURLToPath } from 'node:url'
6
+ import * as SplitLines from '../SplitLines/SplitLines.js'
6
7
 
7
8
  const getActualPath = (fileUri) => {
8
9
  if (fileUri.startsWith('file://')) {
@@ -20,7 +21,7 @@ export const prepare = (error) => {
20
21
  }
21
22
  }
22
23
  const cleanedStack = cleanStack(error.stack)
23
- const lines = cleanedStack.split('\n')
24
+ const lines = SplitLines.splitLines(cleanedStack)
24
25
  const file = lines[1]
25
26
  let codeFrame = ''
26
27
  if (error.codeFrame) {
@@ -82,8 +83,8 @@ export const prepareJsonError = (json, property, message) => {
82
83
  return jsonError
83
84
  }
84
85
 
85
- export const print = (prettyError) => {
86
+ export const print = (prettyError, prefix = '') => {
86
87
  console.error(
87
- `Error: ${prettyError.message}\n\n${prettyError.codeFrame}\n\n${prettyError.stack}`
88
+ `${prefix}Error: ${prettyError.message}\n\n${prettyError.codeFrame}\n\n${prettyError.stack}`
88
89
  )
89
90
  }
@@ -4,6 +4,7 @@ import * as Assert from '../Assert/Assert.js'
4
4
  import { FileNotFoundError } from '../Error/FileNotFoundError.js'
5
5
  import * as FileSystem from '../FileSystem/FileSystem.js'
6
6
  import * as Json from '../Json/Json.js'
7
+ import * as JsonFile from '../JsonFile/JsonFile.js'
7
8
  import * as Platform from '../Platform/Platform.js'
8
9
 
9
10
  const isValid = (recentlyOpened) => {
@@ -24,8 +25,7 @@ const addToArrayUnique = (recentlyOpened, path) => {
24
25
 
25
26
  const getRecentlyOpened = async (recentlyOpenedPath) => {
26
27
  try {
27
- const content = await FileSystem.readFile(recentlyOpenedPath)
28
- const parsed = await Json.parse(content, recentlyOpenedPath)
28
+ const parsed = await JsonFile.readJson(recentlyOpenedPath)
29
29
  return parsed
30
30
  } catch (error) {
31
31
  // TODO should check for error.code
@@ -0,0 +1,4 @@
1
+ export const Begin = 'begin'
2
+ export const Match = 'match'
3
+ export const Summary = 'summary'
4
+ export const Context = 'context'
@@ -1,15 +1,31 @@
1
1
  import { spawn } from 'node:child_process'
2
- import * as RgPath from '../RgPath/RgPath.js'
3
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 TextSearchResultType from '../TextSearchResultType/TextSearchResultType.js'
4
6
 
5
7
  const MAX_SEARCH_RESULTS = 300
6
8
 
9
+ const BEFORE = 20
10
+ const AFTER = 20
11
+
7
12
  const toSearchResult = (parsedLine) => {
8
- return {
9
- preview: parsedLine.data.lines.text,
10
- absoluteOffset: parsedLine.data.absolute_offset,
11
- lineNumber: parsedLine.data.line_number - 1,
13
+ const results = []
14
+ const lines = parsedLine.data.lines.text
15
+ const lineNumber = parsedLine.data.line_number
16
+ for (const submatch of parsedLine.data.submatches) {
17
+ const previewStart = Math.max(submatch.start - BEFORE, 0)
18
+ const previewEnd = Math.min(submatch.end + AFTER, lines.length)
19
+ const previewText = lines.slice(previewStart, previewEnd)
20
+ results.push({
21
+ type: TextSearchResultType.Match,
22
+ start: submatch.start - previewStart,
23
+ end: submatch.end - previewStart,
24
+ lineNumber,
25
+ text: previewText,
26
+ })
12
27
  }
28
+ return results
13
29
  }
14
30
 
15
31
  // TODO update vscode-ripgrep when https://github.com/mhinz/vim-grepper/issues/244, https://github.com/BurntSushi/ripgrep/issues/1892 is fixed
@@ -25,79 +41,90 @@ const useNice = !Platform.isWindows
25
41
  // TODO update client
26
42
  // TODO not always run nice, maybe configure nice via flag/options
27
43
 
28
- const ParsedLineType = {
29
- Begin: 'begin',
30
- Match: 'match',
31
- Summary: 'summary',
32
- }
33
-
34
44
  export const search = async (searchDir, searchString) => {
35
45
  // TODO reject promise when ripgrep search fails
36
46
  return new Promise((resolve, reject) => {
47
+ const ripGrepArgs = [
48
+ '--smart-case',
49
+ '--stats',
50
+ '--json',
51
+ '--fixed-strings',
52
+ searchString,
53
+ '.',
54
+ ]
37
55
  const childProcess = useNice
38
- ? spawn(
39
- 'nice',
40
- [
41
- '-20',
42
- RgPath.rgPath,
43
- '--smart-case',
44
- '--stats',
45
- '--json',
46
- searchString,
47
- '.',
48
- ],
49
- {
50
- cwd: searchDir,
51
- }
52
- )
53
- : spawn(
54
- RgPath.rgPath,
55
- ['--smart-case', '--stats', '--json', searchString, '.'],
56
- {
57
- cwd: searchDir,
58
- }
59
- )
56
+ ? spawn('nice', ['-20', RgPath.rgPath, ...ripGrepArgs], {
57
+ cwd: searchDir,
58
+ })
59
+ : spawn(RgPath.rgPath, ripGrepArgs, {
60
+ cwd: searchDir,
61
+ })
60
62
  const allSearchResults = Object.create(null)
61
63
  let buffer = ''
62
64
  let stats = {}
65
+ let limitHit = false
63
66
  let numberOfResults = 0
64
67
  // TODO use pipeline / transform stream maybe
65
68
 
69
+ const handleLine = (line) => {
70
+ const parsedLine = JSON.parse(line)
71
+ switch (parsedLine.type) {
72
+ case RipGrepParsedLineType.Begin:
73
+ allSearchResults[parsedLine.data.path.text] = [
74
+ {
75
+ type: TextSearchResultType.File,
76
+ start: 0,
77
+ end: 0,
78
+ lineNumber: 0,
79
+ text: parsedLine.data.path.text,
80
+ },
81
+ ]
82
+ break
83
+ case RipGrepParsedLineType.Match:
84
+ numberOfResults++
85
+ allSearchResults[parsedLine.data.path.text].push(
86
+ ...toSearchResult(parsedLine)
87
+ )
88
+ break
89
+ case RipGrepParsedLineType.Summary:
90
+ stats = parsedLine.data
91
+ break
92
+ default:
93
+ break
94
+ }
95
+ }
96
+
97
+ let total = 0
66
98
  const handleData = (chunk) => {
67
- buffer += chunk
68
- const lines = buffer.split('\n')
69
- // @ts-ignore
70
- buffer = lines.pop()
71
- for (const line of lines) {
72
- const parsedLine = JSON.parse(line)
73
- switch (parsedLine.type) {
74
- case ParsedLineType.Begin: {
75
- allSearchResults[parsedLine.data.path.text] = []
76
- break
77
- }
78
- case ParsedLineType.Match: {
79
- numberOfResults++
80
- allSearchResults[parsedLine.data.path.text].push(
81
- toSearchResult(parsedLine)
82
- )
83
- break
84
- }
85
- case ParsedLineType.Summary:
86
- stats = parsedLine.data
87
- break
88
- default:
89
- break
90
- }
99
+ let newLineIndex = chunk.indexOf('\n')
100
+ const dataString = buffer + chunk
101
+ if (newLineIndex === -1) {
102
+ buffer = dataString
103
+ return
91
104
  }
105
+ total += chunk.length
106
+ newLineIndex += buffer.length
107
+ let previousIndex = 0
108
+ while (newLineIndex >= 0) {
109
+ const line = dataString.slice(previousIndex, newLineIndex)
110
+ handleLine(line)
111
+ previousIndex = newLineIndex + 1
112
+ newLineIndex = dataString.indexOf('\n', previousIndex)
113
+ }
114
+ buffer = dataString.slice(previousIndex)
115
+
92
116
  if (numberOfResults > MAX_SEARCH_RESULTS) {
117
+ limitHit = true
93
118
  childProcess.kill()
94
119
  }
95
120
  }
96
121
 
97
122
  const handleClose = () => {
123
+ const results = Object.values(allSearchResults).flat(1)
98
124
  resolve({
99
- results: Object.entries(allSearchResults),
125
+ results,
100
126
  stats,
127
+ limitHit,
101
128
  })
102
129
  }
103
130
  const handleError = (error) => {
@@ -106,8 +133,10 @@ export const search = async (searchDir, searchString) => {
106
133
  resolve({
107
134
  results: [],
108
135
  stats,
136
+ limitHit,
109
137
  })
110
138
  }
139
+ childProcess.stdout.setEncoding('utf8')
111
140
  childProcess.stdout.on('data', handleData)
112
141
  childProcess.once('close', handleClose)
113
142
  childProcess.once('error', handleError)
@@ -0,0 +1,3 @@
1
+ export const splitLines = (lines) => {
2
+ return lines.split('\n')
3
+ }
@@ -1,9 +1,9 @@
1
1
  // parse ps output based on vscode https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/node/ps.ts (License MIT)
2
2
 
3
3
  import * as Exec from '../Exec/Exec.js'
4
+ import * as SplitLines from '../SplitLines/SplitLines.js'
4
5
 
5
- const PID_CMD =
6
- /^\s*(\d+)\s+(\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(.+)$/
6
+ const PID_CMD = /^\s*(\d+)\s+(\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(.+)$/
7
7
 
8
8
  const parsePsOutputLine = (line) => {
9
9
  const matches = PID_CMD.exec(line.trim())
@@ -18,7 +18,7 @@ const parsePsOutputLine = (line) => {
18
18
  }
19
19
  }
20
20
  const parsePsOutput = (stdout, rootPid) => {
21
- const lines = stdout.split('\n')
21
+ const lines = SplitLines.splitLines(stdout)
22
22
  return lines.map(parsePsOutputLine)
23
23
  }
24
24
 
@@ -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
+ }