@lvce-editor/shared-process 0.0.2
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/LICENSE +21 -0
- package/README.md +41 -0
- package/bin/sharedProcess.js +3 -0
- package/index.js +6 -0
- package/package.json +96 -0
- package/src/parts/Assert/Assert.js +57 -0
- package/src/parts/Callback/Callback.js +48 -0
- package/src/parts/ChildProcess/ChildProcess.js +98 -0
- package/src/parts/ClipBoard/ClipBoard.ipc.js +7 -0
- package/src/parts/ClipBoard/ClipBoard.js +23 -0
- package/src/parts/ClipBoard/ClipBoardGnome.js +78 -0
- package/src/parts/ClipBoard/ClipBoardNoop.js +14 -0
- package/src/parts/ClipBoard/ClipBoardWindows.js +22 -0
- package/src/parts/Command/Command.js +264 -0
- package/src/parts/Configuration/Configuration.ipc.js +7 -0
- package/src/parts/Configuration/Configuration.js +39 -0
- package/src/parts/Credentials/Credentials.ipc.js +10 -0
- package/src/parts/Credentials/Credentials.js +69 -0
- package/src/parts/Debug/Debug.js +3 -0
- package/src/parts/Developer/Developer.ipc.js +20 -0
- package/src/parts/Developer/Developer.js +132 -0
- package/src/parts/Download/Download.js +33 -0
- package/src/parts/Electron/Electron.ipc.js +19 -0
- package/src/parts/Electron/Electron.js +99 -0
- package/src/parts/Env/Env.js +3 -0
- package/src/parts/Error/Error.js +30 -0
- package/src/parts/ErrorHandling/ErrorHandling.js +40 -0
- package/src/parts/Exec/Exec.js +16 -0
- package/src/parts/Extension/Extension.js +7 -0
- package/src/parts/ExtensionHost/ExtensionHost.ipc.js +80 -0
- package/src/parts/ExtensionHost/ExtensionHost.js +163 -0
- package/src/parts/ExtensionHost/ExtensionHostBraceCompletion.js +14 -0
- package/src/parts/ExtensionHost/ExtensionHostClosingTag.js +14 -0
- package/src/parts/ExtensionHost/ExtensionHostColorTheme.js +60 -0
- package/src/parts/ExtensionHost/ExtensionHostCommand.js +19 -0
- package/src/parts/ExtensionHost/ExtensionHostCompletion.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostDefinition.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostDiagnostic.js +7 -0
- package/src/parts/ExtensionHost/ExtensionHostFileSystem.js +23 -0
- package/src/parts/ExtensionHost/ExtensionHostFormatting.js +4 -0
- package/src/parts/ExtensionHost/ExtensionHostHover.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostIconTheme.js +35 -0
- package/src/parts/ExtensionHost/ExtensionHostImplementation.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostKeyBindings.js +23 -0
- package/src/parts/ExtensionHost/ExtensionHostLanguages.js +84 -0
- package/src/parts/ExtensionHost/ExtensionHostManagement.js +26 -0
- package/src/parts/ExtensionHost/ExtensionHostOutput.js +3 -0
- package/src/parts/ExtensionHost/ExtensionHostQuickPick.js +6 -0
- package/src/parts/ExtensionHost/ExtensionHostReference.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostRename.js +27 -0
- package/src/parts/ExtensionHost/ExtensionHostSemanticTokens.js +10 -0
- package/src/parts/ExtensionHost/ExtensionHostSourceControl.js +15 -0
- package/src/parts/ExtensionHost/ExtensionHostStats.js +3 -0
- package/src/parts/ExtensionHost/ExtensionHostStatusBar.js +7 -0
- package/src/parts/ExtensionHost/ExtensionHostTabCompletion.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostTextDocument.js +35 -0
- package/src/parts/ExtensionHost/ExtensionHostTypeDefinition.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostWorkspace.js +3 -0
- package/src/parts/ExtensionManagement/ExtensionManagement.ipc.js +11 -0
- package/src/parts/ExtensionManagement/ExtensionManagement.js +319 -0
- package/src/parts/FileSystem/FileSystem.ipc.js +32 -0
- package/src/parts/FileSystem/FileSystem.js +228 -0
- package/src/parts/Git/Git.js +58 -0
- package/src/parts/Json/Json.js +67 -0
- package/src/parts/JsonFile/JsonFile.js +14 -0
- package/src/parts/JsonRpc/JsonRpc.js +22 -0
- package/src/parts/Native/Native.ipc.js +6 -0
- package/src/parts/Native/Native.js +14 -0
- package/src/parts/OutputChannel/OutputChannel.ipc.js +51 -0
- package/src/parts/OutputChannel/OutputChannel.js +37 -0
- package/src/parts/ParentIpc/ParentIpc.js +203 -0
- package/src/parts/Path/Path.js +17 -0
- package/src/parts/Platform/Platform.ipc.js +18 -0
- package/src/parts/Platform/Platform.js +165 -0
- package/src/parts/Preferences/Preferences.ipc.js +6 -0
- package/src/parts/Preferences/Preferences.js +75 -0
- package/src/parts/PrettyError/PrettyError.js +86 -0
- package/src/parts/Process/Process.ipc.js +7 -0
- package/src/parts/Process/Process.js +7 -0
- package/src/parts/Queue/Queue.js +35 -0
- package/src/parts/RequiresSocket/RequiresSocket.js +11 -0
- package/src/parts/RgPath/RgPath.js +1 -0
- package/src/parts/Root/Root.js +6 -0
- package/src/parts/Search/Search.ipc.js +6 -0
- package/src/parts/Search/Search.js +101 -0
- package/src/parts/SearchFile/SearchFile.ipc.js +6 -0
- package/src/parts/SearchFile/SearchFile.js +51 -0
- package/src/parts/Socket/Socket.js +132 -0
- package/src/parts/Stats/Stats.js +45 -0
- package/src/parts/Terminal/Terminal.ipc.js +45 -0
- package/src/parts/Terminal/Terminal.js +161 -0
- package/src/parts/TextDocument/TextDocument.ipc.js +6 -0
- package/src/parts/TextDocument/TextDocument.js +37 -0
- package/src/parts/Trace/Trace.js +15 -0
- package/src/parts/Trash/Trash.js +11 -0
- package/src/parts/WebSocketServer/WebSocketServer.ipc.js +6 -0
- package/src/parts/WebSocketServer/WebSocketServer.js +43 -0
- package/src/parts/Workspace/Workspace.ipc.js +7 -0
- package/src/parts/Workspace/Workspace.js +107 -0
- package/src/sharedProcessMain.js +61 -0
- package/test/Callback.test.js +42 -0
- package/test/ClipBoard.test.js +105 -0
- package/test/Credentials.test.js +147 -0
- package/test/Developer.test.js +34 -0
- package/test/Electron.test.js +93 -0
- package/test/Error.test.js +16 -0
- package/test/Exec.test.js +25 -0
- package/test/ExtensionHost.test.js +127 -0
- package/test/ExtensionHostColorTheme.test.js +141 -0
- package/test/ExtensionHostCommand.test.js +27 -0
- package/test/ExtensionHostCompletion.test.js +45 -0
- package/test/ExtensionHostDefinition.test.js +35 -0
- package/test/ExtensionHostDiagnostic.test.js +23 -0
- package/test/ExtensionHostFileSystem.test.js +91 -0
- package/test/ExtensionHostFormatting.test.js +27 -0
- package/test/ExtensionHostIconTheme.test.js +73 -0
- package/test/ExtensionHostLanguages.test.js +212 -0
- package/test/ExtensionHostOutput.test.js +23 -0
- package/test/ExtensionHostRename.test.js +59 -0
- package/test/ExtensionHostSemanticTokens.test.js +26 -0
- package/test/ExtensionHostTabCompletion.test.js +35 -0
- package/test/ExtensionHostTextDocument.test.js +61 -0
- package/test/ExtensionHostTypeDefinition.test.js +35 -0
- package/test/ExtensionHostWorkspace.test.js +21 -0
- package/test/ExtensionManagement.test.js +408 -0
- package/test/FileSystem.test.js +294 -0
- package/test/Json.test.js +25 -0
- package/test/Native.test.js +31 -0
- package/test/OutputChannel.test.js +68 -0
- package/test/Path.test.js +17 -0
- package/test/Platform.test.js +61 -0
- package/test/Preferences.test.js +140 -0
- package/test/Process.test.js +9 -0
- package/test/RgPath.test.js +5 -0
- package/test/Search.test.js +45 -0
- package/test/SearchFile.test.js +80 -0
- package/test/Terminal.test.js +53 -0
- package/test/Trash.test.js +34 -0
- package/test/WebSocketServer.test.js +98 -0
- package/test/fixture-search-1/index.html +10 -0
- package/test/fixture-search-file-1/fileA +0 -0
- package/test/fixture-search-file-1/fileB +0 -0
- package/test/fixture-search-file-1/nested/fileC +0 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import VError from 'verror'
|
|
2
|
+
import * as Command from '../Command/Command.js'
|
|
3
|
+
import * as Error from '../Error/Error.js'
|
|
4
|
+
import { requiresSocket } from '../RequiresSocket/RequiresSocket.js'
|
|
5
|
+
|
|
6
|
+
export const state = {
|
|
7
|
+
/**
|
|
8
|
+
* @type {any}
|
|
9
|
+
*/
|
|
10
|
+
socket: undefined,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const send = (message) => {
|
|
14
|
+
if (!state.socket) {
|
|
15
|
+
console.warn('socket not yet available')
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
state.socket.send(message)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const handleMessage = async (event) => {
|
|
22
|
+
const object = JSON.parse(event.data)
|
|
23
|
+
if (object.id) {
|
|
24
|
+
if (!object.params) {
|
|
25
|
+
console.warn(
|
|
26
|
+
'[shared-process] invalid message 1',
|
|
27
|
+
typeof object,
|
|
28
|
+
object.params,
|
|
29
|
+
object
|
|
30
|
+
)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
let result
|
|
34
|
+
try {
|
|
35
|
+
result = requiresSocket(object.method)
|
|
36
|
+
? await Command.invoke(object.method, state.socket, ...object.params)
|
|
37
|
+
: await Command.invoke(object.method, ...object.params)
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error instanceof Error.OperationalError) {
|
|
40
|
+
// console.log({ error })
|
|
41
|
+
const rawCause = error.cause()
|
|
42
|
+
const errorCause = rawCause ? rawCause.message : ''
|
|
43
|
+
const errorMessage = rawCause
|
|
44
|
+
? error.message.slice(0, error.message.indexOf(errorCause))
|
|
45
|
+
: ''
|
|
46
|
+
|
|
47
|
+
// console.info('expected error', error)
|
|
48
|
+
send({
|
|
49
|
+
jsonrpc: '2.0',
|
|
50
|
+
id: object.id,
|
|
51
|
+
error: {
|
|
52
|
+
code: /* ExpectedError */ -32000,
|
|
53
|
+
message: error.message,
|
|
54
|
+
data: {
|
|
55
|
+
stack: error.originalStack,
|
|
56
|
+
codeFrame: error.originalCodeFrame,
|
|
57
|
+
category: error.category,
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
stderr: error.stderr,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
} else {
|
|
64
|
+
console.error(error)
|
|
65
|
+
// TODO check if socket is active
|
|
66
|
+
send({
|
|
67
|
+
jsonrpc: '2.0',
|
|
68
|
+
id: object.id,
|
|
69
|
+
error: {
|
|
70
|
+
code: /* UnexpectedError */ -32001,
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
message: error.toString(),
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
send({
|
|
79
|
+
jsonrpc: '2.0',
|
|
80
|
+
result,
|
|
81
|
+
id: object.id,
|
|
82
|
+
})
|
|
83
|
+
} else {
|
|
84
|
+
if (!object.params) {
|
|
85
|
+
console.warn('invalid message', typeof object, object.params, object)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
Command.execute(object.method, state.socket, ...object.params)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const handleError = (error) => {
|
|
93
|
+
console.log('EEERRR')
|
|
94
|
+
// console.error(error)
|
|
95
|
+
throw new VError(error, 'Shared Process Socket Error')
|
|
96
|
+
|
|
97
|
+
// console.error('[Shared Process: Socket Error]', event)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// TODO
|
|
101
|
+
const createAbstractSocket = (socket) => {
|
|
102
|
+
return {
|
|
103
|
+
_socket: socket,
|
|
104
|
+
send(message) {
|
|
105
|
+
socket.send(JSON.stringify(message))
|
|
106
|
+
},
|
|
107
|
+
on(event, listener) {
|
|
108
|
+
switch (event) {
|
|
109
|
+
case 'message':
|
|
110
|
+
socket.on('message', listener)
|
|
111
|
+
break
|
|
112
|
+
case 'close':
|
|
113
|
+
socket.on('close', listener)
|
|
114
|
+
break
|
|
115
|
+
default:
|
|
116
|
+
console.warn('socket event not implemented', event, listener)
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const set = (socket) => {
|
|
124
|
+
socket.on('close', () => {
|
|
125
|
+
console.log('[shared-process] socket closed')
|
|
126
|
+
})
|
|
127
|
+
socket.onmessage = handleMessage
|
|
128
|
+
// socket.onerror = handleError
|
|
129
|
+
socket.on('error', handleError)
|
|
130
|
+
const abstractSocket = createAbstractSocket(socket)
|
|
131
|
+
state.socket = abstractSocket
|
|
132
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as Exec from '../Exec/Exec.js'
|
|
2
|
+
|
|
3
|
+
// parse ps output based on vscode https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/node/ps.ts (License MIT)
|
|
4
|
+
|
|
5
|
+
const parsePsOutputLine = (line) => {
|
|
6
|
+
const PID_CMD =
|
|
7
|
+
/^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+\.[0-9]+)\s+([0-9]+\.[0-9]+)\s+(.+)$/
|
|
8
|
+
const matches = PID_CMD.exec(line.trim())
|
|
9
|
+
if (matches && matches.length === 6) {
|
|
10
|
+
return {
|
|
11
|
+
pid: parseInt(matches[1]),
|
|
12
|
+
ppid: parseInt(matches[2]),
|
|
13
|
+
cwd: matches[5],
|
|
14
|
+
load: parseInt(matches[3]),
|
|
15
|
+
mem: parseInt(matches[4]),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const parsePsOutput = (stdout, rootPid) => {
|
|
20
|
+
const lines = stdout.split('\n')
|
|
21
|
+
return lines.map(parsePsOutputLine)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const getPsOutput = async (rootPid) => {
|
|
25
|
+
const { stdout } = await Exec.exec(
|
|
26
|
+
'ps',
|
|
27
|
+
['-g', `${rootPid}`, '-a', '-o', 'pid=,ppid=,pcpu=,pmem=,command='],
|
|
28
|
+
{
|
|
29
|
+
shell: true,
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
return stdout
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const listProcessesWithMemoryUsage = async (rootPid) => {
|
|
36
|
+
const stdout = await getPsOutput(rootPid)
|
|
37
|
+
const parsed = parsePsOutput(stdout, rootPid)
|
|
38
|
+
console.log(parsed)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const main = async () => {
|
|
42
|
+
await listProcessesWithMemoryUsage(process.pid)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
main()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as Command from '../Command/Command.js'
|
|
2
|
+
import * as Terminal from './Terminal.js'
|
|
3
|
+
import * as Assert from '../Assert/Assert.js'
|
|
4
|
+
|
|
5
|
+
const create = (socket, id, cwd) => {
|
|
6
|
+
Assert.object(socket)
|
|
7
|
+
if (!socket) {
|
|
8
|
+
console.log({socket, id, cwd})
|
|
9
|
+
console.warn('socket not available')
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
// terminalMap[id] = Terminal.create({
|
|
13
|
+
// handleData(data) {
|
|
14
|
+
// socket.send(
|
|
15
|
+
// JSON.stringify({
|
|
16
|
+
// jsonrpc: '2.0',
|
|
17
|
+
// method: 2133,
|
|
18
|
+
// params: ['Terminal', 'handleData', data],
|
|
19
|
+
// })
|
|
20
|
+
// )
|
|
21
|
+
// },
|
|
22
|
+
// cwd,
|
|
23
|
+
// })
|
|
24
|
+
Terminal.create(socket, id, cwd)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const write = ( id, input) => {
|
|
28
|
+
console.log({ id, input})
|
|
29
|
+
Terminal.write(id, input)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const resize = (socket, id, columns, rows) => {
|
|
33
|
+
Terminal.resize(id, columns, rows)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const dispose = (socket, id) => {
|
|
37
|
+
Terminal.dispose(id)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const __initialize__ = () => {
|
|
41
|
+
Command.register('Terminal.create', create)
|
|
42
|
+
Command.register('Terminal.dispose', dispose)
|
|
43
|
+
Command.register('Terminal.write', write)
|
|
44
|
+
Command.register('Terminal.resize', resize)
|
|
45
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { ChildProcess, fork } from 'node:child_process'
|
|
2
|
+
import exitHook from 'exit-hook'
|
|
3
|
+
import * as Debug from '../Debug/Debug.js'
|
|
4
|
+
import * as Path from '../Path/Path.js'
|
|
5
|
+
import * as Root from '../Root/Root.js'
|
|
6
|
+
import * as Assert from '../Assert/Assert.js'
|
|
7
|
+
import { ptyHostPath } from '@lvce-editor/pty-host'
|
|
8
|
+
|
|
9
|
+
export const state = {
|
|
10
|
+
/**
|
|
11
|
+
* @type {ChildProcess|undefined}
|
|
12
|
+
*/
|
|
13
|
+
ptyHost: undefined,
|
|
14
|
+
ptyHostState: /* None */ 0,
|
|
15
|
+
/**
|
|
16
|
+
* @type {any[]}
|
|
17
|
+
*/
|
|
18
|
+
pendingMessages: [],
|
|
19
|
+
send(message) {
|
|
20
|
+
state.pendingMessages.push(message)
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// const getSpawnOptions = () => {
|
|
25
|
+
// if (Platform.isWindows) {
|
|
26
|
+
// return {
|
|
27
|
+
// command: 'powershell.exe',
|
|
28
|
+
// args: [],
|
|
29
|
+
// }
|
|
30
|
+
// }
|
|
31
|
+
// return {
|
|
32
|
+
// command: 'bash',
|
|
33
|
+
// args: ['-i'],
|
|
34
|
+
// }
|
|
35
|
+
// }
|
|
36
|
+
|
|
37
|
+
const cleanUpAll = () => {
|
|
38
|
+
if (state.ptyHost) {
|
|
39
|
+
state.ptyHost.kill()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handleProcessExit = () => {
|
|
44
|
+
Debug.debug('shared process exit, clean terminals')
|
|
45
|
+
cleanUpAll()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const createPtyHost = () => {
|
|
49
|
+
exitHook(handleProcessExit)
|
|
50
|
+
const ptyHost = fork(ptyHostPath, { stdio: 'inherit' })
|
|
51
|
+
return ptyHost
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// const send = (method, ...params) => {
|
|
55
|
+
|
|
56
|
+
// state.ptyHost.send({
|
|
57
|
+
// jsonrpc: '2.0',
|
|
58
|
+
// method,
|
|
59
|
+
// params,
|
|
60
|
+
// })
|
|
61
|
+
// }
|
|
62
|
+
|
|
63
|
+
export const create = (socket, id, cwd) => {
|
|
64
|
+
Assert.object(socket)
|
|
65
|
+
Assert.number(id)
|
|
66
|
+
Assert.string(cwd)
|
|
67
|
+
|
|
68
|
+
console.log('create terminal')
|
|
69
|
+
Debug.debug('creating pty host')
|
|
70
|
+
switch (state.ptyHostState) {
|
|
71
|
+
case /* None */ 0: {
|
|
72
|
+
state.ptyHostState = /* Creating */ 1
|
|
73
|
+
const ptyHost = createPtyHost()
|
|
74
|
+
const handleFirstMessage = () => {
|
|
75
|
+
Debug.debug('pty host ready')
|
|
76
|
+
state.ptyHostState = /* Ready */ 2
|
|
77
|
+
const handleMessage = (message) => {
|
|
78
|
+
const data = message.params[1]
|
|
79
|
+
socket.send({
|
|
80
|
+
jsonrpc: '2.0',
|
|
81
|
+
method: 2133,
|
|
82
|
+
params: ['Terminal', 'handleData', data],
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
ptyHost.on('message', handleMessage)
|
|
86
|
+
state.send = (message) => {
|
|
87
|
+
ptyHost.send(message)
|
|
88
|
+
}
|
|
89
|
+
while (state.pendingMessages.length > 0) {
|
|
90
|
+
state.send(state.pendingMessages.shift())
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
ptyHost.once('message', handleFirstMessage)
|
|
94
|
+
state.ptyHost = ptyHost
|
|
95
|
+
break
|
|
96
|
+
}
|
|
97
|
+
case /* Creating */ 1: {
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
case /* Ready */ 2: {
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
default:
|
|
104
|
+
break
|
|
105
|
+
}
|
|
106
|
+
state.send({
|
|
107
|
+
jsonrpc: '2.0',
|
|
108
|
+
method: 'Terminal.create',
|
|
109
|
+
params: [id, cwd],
|
|
110
|
+
})
|
|
111
|
+
const handleClose = () => {
|
|
112
|
+
if (state.ptyHost) {
|
|
113
|
+
state.ptyHost.removeAllListeners()
|
|
114
|
+
state.ptyHost.kill()
|
|
115
|
+
state.ptyHost = undefined
|
|
116
|
+
state.ptyHostState = /* None */ 0
|
|
117
|
+
state.send = (message) => {
|
|
118
|
+
state.pendingMessages.push(message)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
socket.on('close', handleClose)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const write = (id, data) => {
|
|
126
|
+
Assert.number(id)
|
|
127
|
+
Assert.string(data)
|
|
128
|
+
if (!state.ptyHost) {
|
|
129
|
+
console.log('[shared-process] pty host not ready')
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
state.send({
|
|
133
|
+
jsonrpc: '2.0',
|
|
134
|
+
method: 'Terminal.write',
|
|
135
|
+
params: [id, data],
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const resize = (state, columns, rows) => {
|
|
140
|
+
// state.pty.resize(columns, rows)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const dispose = (id) => {
|
|
144
|
+
state.send({
|
|
145
|
+
jsonrpc: '2.0',
|
|
146
|
+
method: 'Terminal.dispose',
|
|
147
|
+
params: [id],
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const disposeAll = () => {
|
|
152
|
+
if (state.ptyHost) {
|
|
153
|
+
state.ptyHost.removeAllListeners()
|
|
154
|
+
state.ptyHost.kill()
|
|
155
|
+
state.ptyHost = undefined
|
|
156
|
+
state.ptyHostState = /* None */ 0
|
|
157
|
+
state.send = (message) => {
|
|
158
|
+
state.pendingMessages.push(message)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const state = {
|
|
2
|
+
textDocuments: Object.create(null),
|
|
3
|
+
/** @type{any[]} */
|
|
4
|
+
listeners: [],
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// const toOffsetBasedEdit = (textDocument, documentEdit) => {
|
|
8
|
+
// let offset = 0
|
|
9
|
+
// let rowIndex = 0
|
|
10
|
+
// while (rowIndex++ < documentEdit.rowIndex) {
|
|
11
|
+
// offset += textDocument.lines[rowIndex].length
|
|
12
|
+
// }
|
|
13
|
+
// offset += documentEdit.columnIndex
|
|
14
|
+
// return {
|
|
15
|
+
// offset,
|
|
16
|
+
// inserted: documentEdit.inserted,
|
|
17
|
+
// deleted: documentEdit.deleted,
|
|
18
|
+
// }
|
|
19
|
+
// }
|
|
20
|
+
|
|
21
|
+
export const applyEdit = (socket, textDocument, documentEdits) => {
|
|
22
|
+
// TODO avoid extra object allocation and anonymous function
|
|
23
|
+
// const offsetBasedEdits = documentEdits.map((e) =>
|
|
24
|
+
// toOffsetBasedEdit(textDocument, e)
|
|
25
|
+
// )
|
|
26
|
+
for (const listener of state.listeners) {
|
|
27
|
+
listener(textDocument, documentEdits)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const getText = (textDocument) => {
|
|
32
|
+
return textDocument.lines.join('\n')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const onChange = (listener) => {
|
|
36
|
+
state.listeners.push(listener)
|
|
37
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer'
|
|
2
|
+
import * as _ws from 'ws'
|
|
3
|
+
import * as Socket from '../Socket/Socket.js'
|
|
4
|
+
|
|
5
|
+
// workaround for jest or node bug
|
|
6
|
+
const WebSocketServer = _ws.WebSocketServer
|
|
7
|
+
? _ws.WebSocketServer
|
|
8
|
+
: // @ts-ignore
|
|
9
|
+
_ws.default.WebSocketServer
|
|
10
|
+
|
|
11
|
+
const encode = (commandId, callbackId) => (commandId << 16) | callbackId
|
|
12
|
+
const decodeCommand = (encodedCommand) => encodedCommand >> 16
|
|
13
|
+
const decodeCallback = (encodedCommand) => encodedCommand & 0x0000FFFF
|
|
14
|
+
|
|
15
|
+
// console.log('ws', _ws)
|
|
16
|
+
// console.log('server', ws.WebSocketServer)
|
|
17
|
+
|
|
18
|
+
const webSocketServer = new WebSocketServer({
|
|
19
|
+
noServer: true,
|
|
20
|
+
|
|
21
|
+
// TODO not sure if ws compress is working at all
|
|
22
|
+
// perMessageDeflate: true
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// TODO should not import command directly -> Websocket server should be independent of business logic
|
|
26
|
+
|
|
27
|
+
const handleSocket = (socket) => {
|
|
28
|
+
Socket.set(socket)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
webSocketServer.on('connection', handleSocket)
|
|
32
|
+
|
|
33
|
+
export const handleUpgrade = (request, socket) => {
|
|
34
|
+
const upgradeCallback = (ws) => {
|
|
35
|
+
webSocketServer.emit('connection', ws, request)
|
|
36
|
+
}
|
|
37
|
+
webSocketServer.handleUpgrade(
|
|
38
|
+
request,
|
|
39
|
+
socket,
|
|
40
|
+
Buffer.alloc(0),
|
|
41
|
+
upgradeCallback
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as Command from '../Command/Command.js'
|
|
2
|
+
import * as Workspace from './Workspace.js'
|
|
3
|
+
|
|
4
|
+
export const __initialize__ = () => {
|
|
5
|
+
Command.register('Workspace.resolveRoot', Workspace.resolveRoot)
|
|
6
|
+
Command.register('Workspace.getHomeDir', Workspace.getHomeDir)
|
|
7
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { homedir } from 'node:os'
|
|
4
|
+
import { join, resolve } from 'node:path'
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
6
|
+
import * as Env from '../Env/Env.js'
|
|
7
|
+
import * as Platform from '../Platform/Platform.js'
|
|
8
|
+
import * as Root from '../Root/Root.js'
|
|
9
|
+
|
|
10
|
+
const RE_ABSOLUTE_URI = /^[a-z]+:\/\//
|
|
11
|
+
|
|
12
|
+
const isAbsoluteUri = (path) => {
|
|
13
|
+
return RE_ABSOLUTE_URI.test(path)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const getAbsolutePath = (path) => {
|
|
17
|
+
if (isAbsoluteUri(path)) {
|
|
18
|
+
return path
|
|
19
|
+
}
|
|
20
|
+
if (path.startsWith('${cwd}')) {
|
|
21
|
+
path = Root.root + path.slice(5)
|
|
22
|
+
} else if (path.startsWith('~')) {
|
|
23
|
+
path = `${homedir()}${path.slice(1)}`
|
|
24
|
+
}
|
|
25
|
+
path = resolve(path)
|
|
26
|
+
return path
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const getWorkspaceStorage = async (workspaceId) => {
|
|
30
|
+
try {
|
|
31
|
+
const workspaceStorage = JSON.parse(
|
|
32
|
+
await readFile(`/tmp/config/${workspaceId}`, 'utf-8')
|
|
33
|
+
)
|
|
34
|
+
return workspaceStorage
|
|
35
|
+
} catch {
|
|
36
|
+
return {}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @deprecated use platform instead
|
|
42
|
+
*/
|
|
43
|
+
export const getHomeDir = () => {
|
|
44
|
+
if (Platform.isWindows) {
|
|
45
|
+
return ''
|
|
46
|
+
}
|
|
47
|
+
const homeDir = homedir()
|
|
48
|
+
return homeDir
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const getWorkspaceId = (absolutePath) => {
|
|
52
|
+
return createHash('sha256').update(absolutePath).digest('hex').slice(0, 16)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const configs = {
|
|
56
|
+
messagePort1: {
|
|
57
|
+
folder: '/tmp',
|
|
58
|
+
},
|
|
59
|
+
messagePort2: {
|
|
60
|
+
folder: '~',
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const toUri = (path) => {
|
|
65
|
+
return pathToFileURL(path).toString()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const resolveRoot = async () => {
|
|
69
|
+
// TODO ask electron, electron can do mapping
|
|
70
|
+
// await
|
|
71
|
+
|
|
72
|
+
// TODO shared process should have no logic, this should probably be somewhere else
|
|
73
|
+
const folder = Env.getFolder()
|
|
74
|
+
if (!folder) {
|
|
75
|
+
const path = join(Root.root, 'playground')
|
|
76
|
+
return {
|
|
77
|
+
path,
|
|
78
|
+
uri: toUri(path),
|
|
79
|
+
workspaceId: getWorkspaceId(path),
|
|
80
|
+
homeDir: Platform.getHomeDir(),
|
|
81
|
+
pathSeparator: Platform.getPathSeparator(),
|
|
82
|
+
source: 'shared-process-env',
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const absolutePath = getAbsolutePath(folder)
|
|
86
|
+
const workspaceId = getWorkspaceId(absolutePath)
|
|
87
|
+
// TODO this slows down startup a lot (~30-50ms)
|
|
88
|
+
// const workspaceStorage = await getWorkspaceStorage(workspaceId)
|
|
89
|
+
return {
|
|
90
|
+
path: absolutePath,
|
|
91
|
+
uri: toUri(absolutePath),
|
|
92
|
+
workspaceId,
|
|
93
|
+
homeDir: Platform.getHomeDir(),
|
|
94
|
+
pathSeparator: Platform.getPathSeparator(),
|
|
95
|
+
source: 'shared-process-default',
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const resolveUri = (uri) => {
|
|
100
|
+
const path = fileURLToPath(uri)
|
|
101
|
+
return {
|
|
102
|
+
path,
|
|
103
|
+
uri,
|
|
104
|
+
homeDir: Platform.getHomeDir(),
|
|
105
|
+
pathSeparator: Platform.getPathSeparator(),
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as ParentIpc from './parts/ParentIpc/ParentIpc.js'
|
|
2
|
+
import * as PrettyError from './parts/PrettyError/PrettyError.js'
|
|
3
|
+
|
|
4
|
+
// TODO handle structure: one shared process multiple extension hosts
|
|
5
|
+
|
|
6
|
+
// TODO use named functions here
|
|
7
|
+
|
|
8
|
+
const handleUncaughtExceptionMonitor = (error, origin) => {
|
|
9
|
+
console.info(`[shared process] uncaught exception: ${error.stack}`)
|
|
10
|
+
console.info('code', error.code)
|
|
11
|
+
if (error && error.code === 'EPIPE' && !process.connected) {
|
|
12
|
+
// parent process is disposed, ignore
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
if (error && error.code === 'ERR_IPC_CHANNEL_CLOSED' && !process.connected) {
|
|
16
|
+
// parent process is disposed, ignore
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
console.log(error)
|
|
20
|
+
const prettyError = PrettyError.prepare(error)
|
|
21
|
+
console.error(prettyError.message)
|
|
22
|
+
console.error(prettyError.codeFrame)
|
|
23
|
+
console.error(prettyError.stack)
|
|
24
|
+
process.exit(1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const handleSigInt = () => {
|
|
28
|
+
console.info('[shared process] sigint')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const handleDisconnect = () => {
|
|
32
|
+
console.info('[shared process] disconnected')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const handleBeforeExit = () => {
|
|
36
|
+
console.info('[shared process] will exit now')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const handleSigTerm = () => {
|
|
40
|
+
console.info('[shared-process] sigterm')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const main = () => {
|
|
44
|
+
console.log('[shared process] started')
|
|
45
|
+
process.on('beforeExit', handleBeforeExit)
|
|
46
|
+
process.on('disconnect', handleDisconnect)
|
|
47
|
+
process.on('SIGINT', handleSigInt)
|
|
48
|
+
process.on('SIGTERM', handleSigTerm)
|
|
49
|
+
|
|
50
|
+
process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
|
|
51
|
+
ParentIpc.listen()
|
|
52
|
+
// ExtensionHost.start() // TODO start on demand, e.g. not when extensions should be disabled
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
main()
|
|
56
|
+
|
|
57
|
+
// TODO when browser reloads or opens in new tab how does the window know which folder to open?
|
|
58
|
+
|
|
59
|
+
// setTimeout(() => {
|
|
60
|
+
// throw new Error('oops')
|
|
61
|
+
// }, 210)
|