@lvce-editor/shared-process 0.15.15 → 0.15.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/builtinCommands.json +4 -0
- package/config/defaultSettings.json +2 -0
- package/extensions/builtin.language-basics-python/extension.json +1 -1
- package/extensions/builtin.language-basics-xml/extension.json +2 -1
- package/extensions/builtin.language-basics-xml/src/tokenizeXml.js +1 -1
- package/extensions/builtin.theme-atom-one-dark/color-theme.json +4 -0
- package/package.json +4 -4
- package/src/parts/ChildProcessError/ChildProcessError.js +4 -1
- package/src/parts/ElectronInitialize/ElectronInitialize.ipc.js +8 -0
- package/src/parts/ElectronInitialize/ElectronInitialize.js +40 -0
- package/src/parts/ErrorCodes/ErrorCodes.js +2 -0
- package/src/parts/ExportStatic/ExportStatic.js +5 -0
- package/src/parts/FirstWebSocketEventType/FirstWebSocketEventType.js +2 -0
- package/src/parts/GetErrorResponse/GetErrorResponse.js +1 -0
- package/src/parts/GetHelpfulChildProcessError/GetHelpfulChildProcessError.js +13 -3
- package/src/parts/HandleIpc/HandleIpc.js +30 -0
- package/src/parts/HandleWebSocket/HandleWebSocket.ipc.js +7 -0
- package/src/parts/HandleWebSocket/HandleWebSocket.js +25 -0
- package/src/parts/HandleWebSocketForExtensionHost/HandleWebSocketForExtensionHost.js +15 -0
- package/src/parts/HandleWebSocketForExtensionHostHelperProcess/HandleWebSocketForExtensionHostHelperProcess.js +6 -0
- package/src/parts/HandleWebSocketForSharedProcess/HandleWebSocketForSharedProcess.js +16 -0
- package/src/parts/HandleWebSocketForTerminalProcess/HandleWebSocketForTerminalProcess.js +22 -0
- package/src/parts/HandleWebSocketForUnknown/HandleWebSocketForUnknown.js +10 -0
- package/src/parts/HandleWebSocketModule/HandleWebSocketModule.js +20 -0
- package/src/parts/IpcChild/IpcChild.js +3 -1
- package/src/parts/IpcChildModule/IpcChildModule.js +2 -0
- package/src/parts/IpcChildType/IpcChildType.js +4 -0
- package/src/parts/IpcChildWithElectronUtilityProcess/IpcChildWithElectronUtilityProcess.js +27 -0
- package/src/parts/IpcChildWithNodeForkedProcess/IpcChildWithNodeForkedProcess.js +4 -0
- package/src/parts/IpcParentWithNodeForkedProcess/IpcParentWithNodeForkedProcess.js +7 -3
- package/src/parts/IsElectron/IsElectron.js +3 -0
- package/src/parts/JsonRpc/JsonRpc.js +16 -0
- package/src/parts/Module/Module.js +6 -0
- package/src/parts/ModuleId/ModuleId.js +3 -0
- package/src/parts/ModuleMap/ModuleMap.js +6 -0
- package/src/parts/ParentIpc/ParentIpc.js +10 -188
- package/src/parts/PrettyError/PrettyError.js +1 -0
- package/src/parts/ProtocolType/ProtocolType.js +2 -1
- package/src/parts/PtyHost/PtyHost.js +81 -0
- package/src/parts/RebuildNodePty/RebuildNodePty.ipc.js +7 -0
- package/src/parts/RebuildNodePty/RebuildNodePty.js +48 -0
- package/src/parts/RequiresSocket/RequiresSocket.js +1 -0
- package/src/parts/Terminal/Terminal.js +36 -127
- package/src/parts/WebSocketServer/WebSocketServer.js +6 -25
- package/src/sharedProcessMain.js +1 -1
- package/extensions/builtin.language-basics-json5/README.md +0 -16
- package/extensions/builtin.language-basics-json5/extension.json +0 -12
- package/extensions/builtin.language-basics-json5/src/tokenizeJson5.js +0 -321
- /package/src/parts/{GetWindowsNsisDOwnloadUrl → GetWindowsNsisDownloadUrl}/GetWindowsNsisDownloadUrl.js +0 -0
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
4
|
-
|
|
5
|
-
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
6
|
-
import * as ExtensionHostHelperProcessIpc from '../ExtensionHostHelperProcessIpc/ExtensionHostHelperProcessIpc.js'
|
|
7
|
-
import * as ExtensionHostIpc from '../ExtensionHostIpc/ExtensionHostIpc.js'
|
|
8
|
-
import * as ExtensionHostRpc from '../ExtensionHostRpc/ExtensionHostRpc.js'
|
|
9
|
-
import * as GetResponse from '../GetResponse/GetResponse.js'
|
|
10
|
-
import * as Logger from '../Logger/Logger.js'
|
|
11
|
-
import * as ProtocolType from '../ProtocolType/ProtocolType.js'
|
|
12
|
-
import { VError } from '../VError/VError.js'
|
|
1
|
+
import * as HandleIpc from '../HandleIpc/HandleIpc.js'
|
|
2
|
+
import * as IpcChild from '../IpcChild/IpcChild.js'
|
|
3
|
+
import * as IpcChildType from '../IpcChildType/IpcChildType.js'
|
|
4
|
+
|
|
13
5
|
// TODO add tests for this
|
|
14
6
|
|
|
15
7
|
// TODO handle structure: one shared process multiple extension hosts
|
|
@@ -20,181 +12,11 @@ export const state = {
|
|
|
20
12
|
electronPortMap: new Map(),
|
|
21
13
|
}
|
|
22
14
|
|
|
23
|
-
// TODO apparent there are multiple ways for listening to ipc
|
|
24
|
-
// 1. parentPort
|
|
25
|
-
// 2. process.send
|
|
26
|
-
// instead of if/else use interface and abstraction: ParentPortIpc.listen(), ProcessIpc.listen(), ParentPortIpc.send(message), ProcessIpc.send(message)
|
|
27
|
-
|
|
28
|
-
export const electronSend = (message) => {
|
|
29
|
-
if (parentPort) {
|
|
30
|
-
// @ts-ignore
|
|
31
|
-
// process.send(message)
|
|
32
|
-
parentPort.postMessage(message)
|
|
33
|
-
} else if (process.send) {
|
|
34
|
-
process.send(message)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const handleWebSocketSharedProcess = (message, handle) => {
|
|
39
|
-
// TODO when it is an extension host websocket, spawn extension host
|
|
40
|
-
handle.on('error', (error) => {
|
|
41
|
-
if (error && error.code === ErrorCodes.ECONNRESET) {
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
console.info('[info shared process: handle error]', error)
|
|
45
|
-
})
|
|
46
|
-
Command.execute(/* WebSocketServer.handleUpgrade */ 'WebSocketServer.handleUpgrade', /* message */ message, /* handle */ handle)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// TODO lazyload modules for spawning extension host, they are only needed later
|
|
50
|
-
const handleWebSocketExtensionHost = async (message, handle) => {
|
|
51
|
-
// console.log('[shared-process] received extension host websocket', message)
|
|
52
|
-
const ipc = await ExtensionHostIpc.create()
|
|
53
|
-
console.info('[sharedprocess] creating extension ipc')
|
|
54
|
-
const rpc = await ExtensionHostRpc.create(ipc, handle)
|
|
55
|
-
console.info('[sharedprocess] created extension host rpc')
|
|
56
|
-
ipc._process.send(message, handle)
|
|
57
|
-
// rpc.send(message)
|
|
58
|
-
// console.log('spawned extension host')
|
|
59
|
-
// console.log(rpc)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const handleWebSocketExtensionHostHelperProcess = async (message, handle) => {
|
|
63
|
-
const ipc = await ExtensionHostHelperProcessIpc.create()
|
|
64
|
-
ipc._process.send(message, handle)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const handleWebSocketUnknown = (message, handle, protocol) => {
|
|
68
|
-
Logger.warn(`[shared-process] unsupported sec-websocket-procotol ${protocol}`)
|
|
69
|
-
try {
|
|
70
|
-
handle.destroy()
|
|
71
|
-
} catch {
|
|
72
|
-
// ignore
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const handleWebSocket = (message, handle) => {
|
|
77
|
-
const headers = message.headers
|
|
78
|
-
if (!headers) {
|
|
79
|
-
throw new VError('missing websocket headers')
|
|
80
|
-
}
|
|
81
|
-
const protocol = headers['sec-websocket-protocol']
|
|
82
|
-
if (!protocol) {
|
|
83
|
-
throw new VError('missing sec websocket protocol header')
|
|
84
|
-
}
|
|
85
|
-
switch (protocol) {
|
|
86
|
-
case ProtocolType.SharedProcess:
|
|
87
|
-
return handleWebSocketSharedProcess(message, handle)
|
|
88
|
-
case ProtocolType.ExtensionHost:
|
|
89
|
-
return handleWebSocketExtensionHost(message, handle)
|
|
90
|
-
case ProtocolType.ExtensionHostHelperProcess:
|
|
91
|
-
return handleWebSocketExtensionHostHelperProcess(message, handle)
|
|
92
|
-
default:
|
|
93
|
-
return handleWebSocketUnknown(message, handle, protocol)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const handleJsonRpcResult = (message) => {
|
|
98
|
-
Callback.resolve(message.id, message.result)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const handleJsonRpcMessage = async (message, handle) => {
|
|
102
|
-
if (message.id) {
|
|
103
|
-
const response = await GetResponse.getResponse(message, handle)
|
|
104
|
-
electronSend(response)
|
|
105
|
-
} else {
|
|
106
|
-
// TODO handle error
|
|
107
|
-
Command.execute(message.method, null, ...message.params)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const handleMessageFromParentProcess = async (message, handle) => {
|
|
112
|
-
if (handle) {
|
|
113
|
-
return handleWebSocket(message, handle)
|
|
114
|
-
}
|
|
115
|
-
if (message.result) {
|
|
116
|
-
return handleJsonRpcResult(message)
|
|
117
|
-
}
|
|
118
|
-
if (message.method) {
|
|
119
|
-
return handleJsonRpcMessage(message, handle)
|
|
120
|
-
}
|
|
121
|
-
console.warn('unknown message', message)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
*
|
|
126
|
-
* @param {{initialize:{port: MessagePort, folder:string}}} initializeMessage
|
|
127
|
-
*/
|
|
128
|
-
const electronInitialize = (initializeMessage) => {
|
|
129
|
-
const port = initializeMessage.initialize.port
|
|
130
|
-
// TODO handle error
|
|
131
|
-
const fakeSocket = {
|
|
132
|
-
send: port.postMessage.bind(port),
|
|
133
|
-
on(event, listener) {
|
|
134
|
-
switch (event) {
|
|
135
|
-
case 'close':
|
|
136
|
-
port.on('close', listener)
|
|
137
|
-
break
|
|
138
|
-
case 'message':
|
|
139
|
-
port.on('message', listener)
|
|
140
|
-
break
|
|
141
|
-
default:
|
|
142
|
-
console.warn('socket event not implemented', event, listener)
|
|
143
|
-
break
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
}
|
|
147
|
-
const handleOtherMessagesFromMessagePort = async (message) => {
|
|
148
|
-
// console.log('got port message', message)
|
|
149
|
-
if (message.result) {
|
|
150
|
-
Callback.resolve(message.id, message.result)
|
|
151
|
-
} else if (message.method) {
|
|
152
|
-
if (message.id) {
|
|
153
|
-
const response = await GetResponse.getResponse(message, fakeSocket)
|
|
154
|
-
port.postMessage(response)
|
|
155
|
-
} else {
|
|
156
|
-
console.warn(`[shared process] sending messages without id is deprecated: ${message.method}`)
|
|
157
|
-
Command.execute(message.method, fakeSocket, ...message.params)
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
console.warn('unknown message', message)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
port.on('message', handleOtherMessagesFromMessagePort)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const handleMessageFromParentProcessElectron = async (message) => {
|
|
167
|
-
if (message.initialize) {
|
|
168
|
-
electronInitialize(message)
|
|
169
|
-
return
|
|
170
|
-
}
|
|
171
|
-
if ('result' in message) {
|
|
172
|
-
Callback.resolve(message.id, message.result)
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
console.log('unknown message from electron', message)
|
|
177
|
-
console.log({ message })
|
|
178
|
-
}
|
|
179
|
-
|
|
180
15
|
// TODO maybe rename to hydrate
|
|
181
|
-
export const listen = () => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// @ts-ignore
|
|
188
|
-
parentPort.on('message', handleMessageFromParentProcessElectron)
|
|
189
|
-
electronSend('ready')
|
|
190
|
-
} else {
|
|
191
|
-
Debug.debug('is not electron')
|
|
192
|
-
// otherwise listen to web process ipc
|
|
193
|
-
// and when a socket is transferred,
|
|
194
|
-
// listen to socket messages also
|
|
195
|
-
process.on('message', handleMessageFromParentProcess)
|
|
196
|
-
if (process.send) {
|
|
197
|
-
process.send('ready')
|
|
198
|
-
}
|
|
199
|
-
}
|
|
16
|
+
export const listen = async () => {
|
|
17
|
+
const method = IpcChildType.Auto()
|
|
18
|
+
const ipc = await IpcChild.listen({
|
|
19
|
+
method,
|
|
20
|
+
})
|
|
21
|
+
HandleIpc.handleIpc(ipc)
|
|
200
22
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export const SharedProcess = 'lvce.shared-process'
|
|
2
1
|
export const ExtensionHost = 'lvce.extension-host'
|
|
3
2
|
export const ExtensionHostHelperProcess = 'lvce.extension-host-helper-process'
|
|
3
|
+
export const SharedProcess = 'lvce.shared-process'
|
|
4
|
+
export const TerminalProcess = 'lvce.terminal-process'
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import exitHook from 'exit-hook'
|
|
2
|
+
import * as Debug from '../Debug/Debug.js'
|
|
3
|
+
import * as IpcParent from '../IpcParent/IpcParent.js'
|
|
4
|
+
import * as IpcParentType from '../IpcParentType/IpcParentType.js'
|
|
5
|
+
import * as PtyHostPath from '../PtyHostPath/PtyHostPath.js'
|
|
6
|
+
|
|
7
|
+
export const state = {
|
|
8
|
+
/**
|
|
9
|
+
* @type {any}
|
|
10
|
+
*/
|
|
11
|
+
ptyHost: undefined,
|
|
12
|
+
/**
|
|
13
|
+
* @type {any}
|
|
14
|
+
*/
|
|
15
|
+
ptyHostPromise: undefined,
|
|
16
|
+
/**
|
|
17
|
+
* @type {any[]}
|
|
18
|
+
*/
|
|
19
|
+
pendingMessages: [],
|
|
20
|
+
send(message) {
|
|
21
|
+
state.pendingMessages.push(message)
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const cleanUpAll = () => {
|
|
26
|
+
if (state.ptyHost) {
|
|
27
|
+
state.ptyHost.dispose()
|
|
28
|
+
state.ptyHost = undefined
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const handleProcessExit = () => {
|
|
33
|
+
Debug.debug('shared process exit, clean terminals')
|
|
34
|
+
cleanUpAll()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const createPtyHost = async () => {
|
|
38
|
+
exitHook(handleProcessExit)
|
|
39
|
+
const ptyHostPath = await PtyHostPath.getPtyHostPath()
|
|
40
|
+
const ptyHost = await IpcParent.create({
|
|
41
|
+
method: IpcParentType.NodeForkedProcess,
|
|
42
|
+
path: ptyHostPath,
|
|
43
|
+
argv: [],
|
|
44
|
+
stdio: 'inherit',
|
|
45
|
+
name: 'Terminal Process',
|
|
46
|
+
})
|
|
47
|
+
const handleClose = () => {
|
|
48
|
+
ptyHost.off('close', handleClose)
|
|
49
|
+
state.ptyHost = undefined
|
|
50
|
+
state.ptyHostPromise = undefined
|
|
51
|
+
}
|
|
52
|
+
ptyHost.on('close', handleClose)
|
|
53
|
+
state.ptyHost = ptyHost
|
|
54
|
+
return ptyHost
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const getOrCreate = () => {
|
|
58
|
+
Debug.debug('creating pty host')
|
|
59
|
+
if (!state.ptyHostPromise) {
|
|
60
|
+
state.ptyHostPromise = createPtyHost()
|
|
61
|
+
}
|
|
62
|
+
return state.ptyHostPromise
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const getCurrentInstance = () => {
|
|
66
|
+
return state.ptyHost
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const disposeAll = () => {
|
|
70
|
+
if (state.ptyHost) {
|
|
71
|
+
state.ptyHost.removeAllListeners()
|
|
72
|
+
if (state.ptyHost) {
|
|
73
|
+
state.ptyHost.dispose()
|
|
74
|
+
state.ptyHost = undefined
|
|
75
|
+
}
|
|
76
|
+
state.ptyHostState = /* None */ 0
|
|
77
|
+
state.send = (message) => {
|
|
78
|
+
state.pendingMessages.push(message)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import * as IsElectron from '../IsElectron/IsElectron.js'
|
|
3
|
+
import * as Path from '../Path/Path.js'
|
|
4
|
+
import * as Root from '../Root/Root.js'
|
|
5
|
+
import { VError } from '../VError/VError.js'
|
|
6
|
+
|
|
7
|
+
const getElectronRebuildPath = () => {
|
|
8
|
+
return Path.join(Root.root, 'packages', 'main-process', 'node_modules', '.bin', 'electron-rebuild')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const getPtyHostPath = () => {
|
|
12
|
+
return Path.join(Root.root, 'packages', 'pty-host')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} cwd
|
|
17
|
+
*/
|
|
18
|
+
const rebuildNodePtyElectron = async (cwd) => {
|
|
19
|
+
const electronRebuildPath = getElectronRebuildPath()
|
|
20
|
+
const childProcess = spawn(electronRebuildPath, [], {
|
|
21
|
+
cwd,
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
})
|
|
24
|
+
// TODO wait for child process to be finished
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} cwd
|
|
29
|
+
*/
|
|
30
|
+
const rebuildNodePtyNode = async (cwd) => {
|
|
31
|
+
const childProcess = spawn('npm', ['rebuild'], {
|
|
32
|
+
cwd,
|
|
33
|
+
stdio: 'inherit',
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const rebuildNodePty = async () => {
|
|
38
|
+
try {
|
|
39
|
+
const ptyHostPath = getPtyHostPath()
|
|
40
|
+
if (IsElectron.isElectron()) {
|
|
41
|
+
await rebuildNodePtyElectron(ptyHostPath)
|
|
42
|
+
} else {
|
|
43
|
+
await rebuildNodePtyNode(ptyHostPath)
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
throw new VError(error, `Failed to rebuild node-pty`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,138 +1,53 @@
|
|
|
1
|
-
import exitHook from 'exit-hook'
|
|
2
1
|
import * as Assert from '../Assert/Assert.js'
|
|
3
|
-
import * as Debug from '../Debug/Debug.js'
|
|
4
|
-
import * as IpcParent from '../IpcParent/IpcParent.js'
|
|
5
|
-
import * as IpcParentType from '../IpcParentType/IpcParentType.js'
|
|
6
2
|
import * as JsonRpcVersion from '../JsonRpcVersion/JsonRpcVersion.js'
|
|
7
|
-
import * as
|
|
3
|
+
import * as PtyHost from '../PtyHost/PtyHost.js'
|
|
4
|
+
import { VError } from '../VError/VError.js'
|
|
8
5
|
|
|
9
6
|
export const state = {
|
|
10
|
-
|
|
11
|
-
* @type {any}
|
|
12
|
-
*/
|
|
13
|
-
ptyHost: undefined,
|
|
14
|
-
ptyHostState: /* None */ 0,
|
|
15
|
-
/**
|
|
16
|
-
* @type {any[]}
|
|
17
|
-
*/
|
|
18
|
-
pendingMessages: [],
|
|
19
|
-
send(message) {
|
|
20
|
-
state.pendingMessages.push(message)
|
|
21
|
-
},
|
|
7
|
+
socketMap: Object.create(null),
|
|
22
8
|
}
|
|
23
9
|
|
|
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 = async () => {
|
|
49
|
-
exitHook(handleProcessExit)
|
|
50
|
-
const ptyHostPath = await PtyHostPath.getPtyHostPath()
|
|
51
|
-
const ptyHost = await IpcParent.create({
|
|
52
|
-
method: IpcParentType.NodeForkedProcess,
|
|
53
|
-
path: ptyHostPath,
|
|
54
|
-
argv: ['--ipc-type=node-forked-process'],
|
|
55
|
-
stdio: 'inherit',
|
|
56
|
-
name: 'Terminal Process',
|
|
57
|
-
})
|
|
58
|
-
return ptyHost
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// const send = (method, ...params) => {
|
|
62
|
-
|
|
63
|
-
// state.ptyHost.send({
|
|
64
|
-
// jsonrpc: '2.0',
|
|
65
|
-
// method,
|
|
66
|
-
// params,
|
|
67
|
-
// })
|
|
68
|
-
// }
|
|
69
|
-
|
|
70
10
|
export const create = async (socket, id, cwd) => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
state.ptyHostState = /* Creating */ 1
|
|
79
|
-
const ptyHost = await createPtyHost()
|
|
11
|
+
try {
|
|
12
|
+
Assert.object(socket)
|
|
13
|
+
Assert.number(id)
|
|
14
|
+
Assert.string(cwd)
|
|
15
|
+
// TODO dispose entry
|
|
16
|
+
state.socketMap[id] = socket
|
|
17
|
+
const ptyHost = await PtyHost.getOrCreate()
|
|
80
18
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const handleMessage = (message) => {
|
|
84
|
-
const data = message.params[1]
|
|
85
|
-
socket.send({
|
|
86
|
-
jsonrpc: JsonRpcVersion.Two,
|
|
87
|
-
method: 'Viewlet.send',
|
|
88
|
-
params: ['Terminal', 'handleData', data],
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
ptyHost.on('message', handleMessage)
|
|
92
|
-
state.send = (message) => {
|
|
93
|
-
ptyHost.send(message)
|
|
94
|
-
}
|
|
95
|
-
while (state.pendingMessages.length > 0) {
|
|
96
|
-
state.send(state.pendingMessages.shift())
|
|
97
|
-
}
|
|
98
|
-
state.ptyHost = ptyHost
|
|
99
|
-
break
|
|
100
|
-
}
|
|
101
|
-
case /* Creating */ 1: {
|
|
102
|
-
break
|
|
19
|
+
const handleMessage = (message) => {
|
|
20
|
+
socket.send(message)
|
|
103
21
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
break
|
|
109
|
-
}
|
|
110
|
-
state.send({
|
|
111
|
-
jsonrpc: JsonRpcVersion.Two,
|
|
112
|
-
method: 'Terminal.create',
|
|
113
|
-
params: [id, cwd],
|
|
114
|
-
})
|
|
115
|
-
const handleClose = () => {
|
|
116
|
-
if (state.ptyHost) {
|
|
117
|
-
state.ptyHost.dispose()
|
|
118
|
-
state.ptyHost = undefined
|
|
119
|
-
state.ptyHostState = /* None */ 0
|
|
120
|
-
state.send = (message) => {
|
|
121
|
-
state.pendingMessages.push(message)
|
|
122
|
-
}
|
|
22
|
+
const handleClose = () => {
|
|
23
|
+
// socket.off('close', handleClose)
|
|
24
|
+
ptyHost.off('message', handleMessage)
|
|
25
|
+
ptyHost.dispose()
|
|
123
26
|
}
|
|
27
|
+
ptyHost.on('message', handleMessage)
|
|
28
|
+
socket.on('close', handleClose)
|
|
29
|
+
|
|
30
|
+
// TODO use invoke
|
|
31
|
+
ptyHost.send({
|
|
32
|
+
jsonrpc: JsonRpcVersion.Two,
|
|
33
|
+
method: 'Terminal.create',
|
|
34
|
+
params: [id, cwd],
|
|
35
|
+
})
|
|
36
|
+
} catch (error) {
|
|
37
|
+
throw new VError(error, `Failed to create terminal`)
|
|
124
38
|
}
|
|
125
|
-
socket.on('close', handleClose)
|
|
126
39
|
}
|
|
127
40
|
|
|
128
41
|
export const write = (id, data) => {
|
|
129
42
|
Assert.number(id)
|
|
130
43
|
Assert.string(data)
|
|
131
|
-
|
|
44
|
+
const ptyHost = PtyHost.getCurrentInstance()
|
|
45
|
+
if (!ptyHost) {
|
|
132
46
|
console.log('[shared-process] pty host not ready')
|
|
133
47
|
return
|
|
134
48
|
}
|
|
135
|
-
|
|
49
|
+
// TODO should use invoke
|
|
50
|
+
ptyHost.send({
|
|
136
51
|
jsonrpc: JsonRpcVersion.Two,
|
|
137
52
|
method: 'Terminal.write',
|
|
138
53
|
params: [id, data],
|
|
@@ -143,8 +58,10 @@ export const resize = (state, columns, rows) => {
|
|
|
143
58
|
// state.pty.resize(columns, rows)
|
|
144
59
|
}
|
|
145
60
|
|
|
146
|
-
export const dispose = (id) => {
|
|
147
|
-
|
|
61
|
+
export const dispose = async (id) => {
|
|
62
|
+
const ptyHost = await PtyHost.getOrCreate()
|
|
63
|
+
// TODO use invoke
|
|
64
|
+
ptyHost.send({
|
|
148
65
|
jsonrpc: JsonRpcVersion.Two,
|
|
149
66
|
method: 'Terminal.dispose',
|
|
150
67
|
params: [id],
|
|
@@ -152,13 +69,5 @@ export const dispose = (id) => {
|
|
|
152
69
|
}
|
|
153
70
|
|
|
154
71
|
export const disposeAll = () => {
|
|
155
|
-
|
|
156
|
-
state.ptyHost.removeAllListeners()
|
|
157
|
-
state.ptyHost.kill()
|
|
158
|
-
state.ptyHost = undefined
|
|
159
|
-
state.ptyHostState = /* None */ 0
|
|
160
|
-
state.send = (message) => {
|
|
161
|
-
state.pendingMessages.push(message)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
72
|
+
PtyHost.disposeAll()
|
|
164
73
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer'
|
|
2
2
|
import * as _ws from 'ws'
|
|
3
|
-
import * as Socket from '../Socket/Socket.js'
|
|
4
3
|
|
|
5
4
|
// workaround for jest or node bug
|
|
6
5
|
const WebSocketServer = _ws.WebSocketServer
|
|
@@ -8,13 +7,6 @@ const WebSocketServer = _ws.WebSocketServer
|
|
|
8
7
|
: // @ts-ignore
|
|
9
8
|
_ws.default.WebSocketServer
|
|
10
9
|
|
|
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
10
|
const webSocketServer = new WebSocketServer({
|
|
19
11
|
noServer: true,
|
|
20
12
|
|
|
@@ -22,22 +14,11 @@ const webSocketServer = new WebSocketServer({
|
|
|
22
14
|
// perMessageDeflate: true
|
|
23
15
|
})
|
|
24
16
|
|
|
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
17
|
export const handleUpgrade = (request, socket) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
request,
|
|
39
|
-
|
|
40
|
-
Buffer.alloc(0),
|
|
41
|
-
upgradeCallback
|
|
42
|
-
)
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const upgradeCallback = (ws) => {
|
|
20
|
+
resolve(ws)
|
|
21
|
+
}
|
|
22
|
+
webSocketServer.handleUpgrade(request, socket, Buffer.alloc(0), upgradeCallback)
|
|
23
|
+
})
|
|
43
24
|
}
|
package/src/sharedProcessMain.js
CHANGED
|
@@ -37,7 +37,7 @@ const main = async () => {
|
|
|
37
37
|
process.on('SIGTERM', handleSigTerm)
|
|
38
38
|
|
|
39
39
|
process.on('uncaughtExceptionMonitor', ErrorHandling.handleUncaughtExceptionMonitor)
|
|
40
|
-
ParentIpc.listen()
|
|
40
|
+
await ParentIpc.listen()
|
|
41
41
|
|
|
42
42
|
// ExtensionHost.start() // TODO start on demand, e.g. not when extensions should be disabled
|
|
43
43
|
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Language Basics JSON5
|
|
2
|
-
|
|
3
|
-
Provides syntax highlighting in JSON5 files.
|
|
4
|
-
|
|
5
|
-
## Contributing
|
|
6
|
-
|
|
7
|
-
```sh
|
|
8
|
-
git clone git@github.com:lvce-editor/language-basics-json5.git &&
|
|
9
|
-
cd language-basics-json5 &&
|
|
10
|
-
npm ci &&
|
|
11
|
-
npm test
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Gitpod
|
|
15
|
-
|
|
16
|
-
[](https://gitpod.io/#https://github.com/lvce-editor/language-basics-json5)
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "builtin.language-basics-json5",
|
|
3
|
-
"name": "Language Basics JSON5",
|
|
4
|
-
"description": "Provides syntax highlighting and bracket matching in JSON5 files.",
|
|
5
|
-
"languages": [
|
|
6
|
-
{
|
|
7
|
-
"id": "json5",
|
|
8
|
-
"extensions": [".json5"],
|
|
9
|
-
"tokenize": "src/tokenizeJson5.js"
|
|
10
|
-
}
|
|
11
|
-
]
|
|
12
|
-
}
|