Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to VSCode 1.94 #504

Merged
merged 7 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"VSCODE_COMMIT": "readonly"
},
"extends": [
"@codingame"
"@codingame/eslint-config", "prettier"
],
"rules": {
"import/extensions": [
Expand Down
1 change: 1 addition & 0 deletions .ncurc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@vscode/proxy-agent",
"@vscode/ripgrep",
"@vscode/spdlog",
"@vscode/tree-sitter-wasm",
"@vscode/vscode-languagedetection",
"@vscode/windows-process-tree",
"@vscode/windows-registry",
Expand Down
7 changes: 7 additions & 0 deletions .prettierrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
singleQuote: true,
jsxSingleQuote: true,
trailingComma: 'none',
semi: false,
printWidth: 100
}
4 changes: 2 additions & 2 deletions commitlint.config.cjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
extends: ['@codingame/commitlint-config-codingame']
};
extends: ['@codingame/commitlint-config-codingame']
}
2 changes: 1 addition & 1 deletion demo/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"project": "./tsconfig.json"
},
"extends": [
"@codingame"
"@codingame/eslint-config", "prettier"
],
"rules": {
"import/extensions": ["off"],
Expand Down
199 changes: 106 additions & 93 deletions demo/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
"@codingame/monaco-vscode-xml-default-extension": "file:../dist/default-extension-xml",
"@codingame/monaco-vscode-yaml-default-extension": "file:../dist/default-extension-yaml",
"@codingame/monaco-vscode-view-common-service-override": "file:../dist/service-override-view-common",
"@codingame/monaco-vscode-treesitter-service-override": "file:../dist/service-override-treesitter",
"ansi-colors": "^4.1.3",
"dockerode": "^4.0.2",
"express": "^4.21.0",
Expand Down
147 changes: 84 additions & 63 deletions demo/src/debugServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import * as stream from 'stream'
const docker = new Docker()
const image = 'ghcr.io/graalvm/graalvm-ce:21.2.0'

async function createContainer () {
async function createContainer() {
const stream = await docker.pull(image)
await new Promise<void>((resolve, reject) => {
docker.modem.followProgress(stream, err => err == null ? resolve() : reject(err))
docker.modem.followProgress(stream, (err) => (err == null ? resolve() : reject(err)))
})
await fs.promises.mkdir('/tmp/workspace', {
recursive: true
Expand All @@ -23,18 +23,20 @@ async function createContainer () {
Entrypoint: ['sleep', 'infinity'],
HostConfig: {
NetworkMode: 'host',
Mounts: [{
Type: 'bind',
Target: '/workspace',
Source: '/tmp/workspace'
}],
Mounts: [
{
Type: 'bind',
Target: '/workspace',
Source: '/tmp/workspace'
}
],
AutoRemove: true
}
})
return container
}

async function prepareContainer (container: Docker.Container) {
async function prepareContainer(container: Docker.Container) {
await container.start()
// eslint-disable-next-line no-console
console.log('Installing node')
Expand All @@ -47,7 +49,7 @@ async function prepareContainer (container: Docker.Container) {
hijack: true
})
execStream.pipe(process.stdout)
await new Promise(resolve => execStream.on('end', resolve))
await new Promise((resolve) => execStream.on('end', resolve))
// eslint-disable-next-line no-console
console.log('Node installed')
}
Expand All @@ -56,7 +58,7 @@ async function prepareContainer (container: Docker.Container) {
console.log('Pulling image/starting container...')
const containerPromise = createContainer()

async function exitHandler () {
async function exitHandler() {
// eslint-disable-next-line no-console
console.log('Exiting...')
try {
Expand All @@ -83,7 +85,7 @@ class DAPSocket {
private socket: net.Socket
private rawData = Buffer.allocUnsafe(0)
private contentLength = -1
constructor (private onMessage: (message: string) => void) {
constructor(private onMessage: (message: string) => void) {
this.socket = new net.Socket()
this.socket.on('data', this.onData)
}
Expand Down Expand Up @@ -121,12 +123,15 @@ class DAPSocket {
}
}

public connect (port: number) {
public connect(port: number) {
this.socket.connect(port)
}

public sendMessage (message: string) {
this.socket.write(`Content-Length: ${Buffer.byteLength(message, 'utf8')}${TWO_CRLF}${message}`, 'utf8')
public sendMessage(message: string) {
this.socket.write(
`Content-Length: ${Buffer.byteLength(message, 'utf8')}${TWO_CRLF}${message}`,
'utf8'
)
}
}

Expand All @@ -141,8 +146,8 @@ const server = http.createServer(app)

const wss = new WebSocketServer({ server })

async function findPortFree () {
return await new Promise<number>(resolve => {
async function findPortFree() {
return await new Promise<number>((resolve) => {
const srv = net.createServer()
srv.listen(0, () => {
const port = (srv.address() as net.AddressInfo).port
Expand All @@ -151,71 +156,87 @@ async function findPortFree () {
})
}

function sequential<T, P extends unknown[]> (fn: (...params: P) => Promise<T>): (...params: P) => Promise<T> {
function sequential<T, P extends unknown[]>(
fn: (...params: P) => Promise<T>
): (...params: P) => Promise<T> {
let promise = Promise.resolve()
return (...params: P) => {
const result = promise.then(() => {
return fn(...params)
})

promise = result.then(() => {}, () => {})
promise = result.then(
() => {},
() => {}
)
return result
}
}

wss.on('connection', (ws) => {
const socket = new DAPSocket(message => ws.send(message))
const socket = new DAPSocket((message) => ws.send(message))

let initialized = false

ws.on('message', sequential(async (message: string) => {
if (!initialized) {
try {
initialized = true
const init: { main: string, files: Record<string, string> } = JSON.parse(message)
for (const [file, content] of Object.entries(init.files)) {
await fs.promises.writeFile('/tmp/' + file, content)
}
const debuggerPort = await findPortFree()
const exec = await container.exec({
Cmd: ['node', `--dap=${debuggerPort}`, '--dap.WaitAttached', '--dap.Suspend=false', `${init.main}`],
AttachStdout: true,
AttachStderr: true
})

const execStream = await exec.start({
hijack: true
})
const stdout = new stream.PassThrough()
const stderr = new stream.PassThrough()
container.modem.demuxStream(execStream, stdout, stderr)
function sendOutput (category: 'stdout' | 'stderr', output: Buffer) {
ws.send(JSON.stringify({
type: 'event',
event: 'output',
body: {
category,
output: output.toString()
}
}))
}
stdout.on('data', sendOutput.bind(undefined, 'stdout'))
stderr.on('data', sendOutput.bind(undefined, 'stderr'))
ws.on(
'message',
sequential(async (message: string) => {
if (!initialized) {
try {
initialized = true
const init: { main: string; files: Record<string, string> } = JSON.parse(message)
for (const [file, content] of Object.entries(init.files)) {
await fs.promises.writeFile('/tmp/' + file, content)
}
const debuggerPort = await findPortFree()
const exec = await container.exec({
Cmd: [
'node',
`--dap=${debuggerPort}`,
'--dap.WaitAttached',
'--dap.Suspend=false',
`${init.main}`
],
AttachStdout: true,
AttachStderr: true
})

const execStream = await exec.start({
hijack: true
})
const stdout = new stream.PassThrough()
const stderr = new stream.PassThrough()
container.modem.demuxStream(execStream, stdout, stderr)
function sendOutput(category: 'stdout' | 'stderr', output: Buffer) {
ws.send(
JSON.stringify({
type: 'event',
event: 'output',
body: {
category,
output: output.toString()
}
})
)
}
stdout.on('data', sendOutput.bind(undefined, 'stdout'))
stderr.on('data', sendOutput.bind(undefined, 'stderr'))

execStream.on('end', () => {
ws.close()
})
execStream.on('end', () => {
ws.close()
})

await new Promise(resolve => setTimeout(resolve, 1000))
socket.connect(debuggerPort)
await new Promise((resolve) => setTimeout(resolve, 1000))
socket.connect(debuggerPort)

return
} catch (err) {
console.error('Failed to initialize', err)
return
} catch (err) {
console.error('Failed to initialize', err)
}
}
}
socket.sendMessage(message)
}))
socket.sendMessage(message)
})
)
})

server.listen(PORT, () => {
Expand Down
54 changes: 31 additions & 23 deletions demo/src/features/ai.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,45 @@
import { ExtensionHostKind, registerExtension } from 'vscode/extensions'

const { getApi } = registerExtension({
name: 'aiDemo',
publisher: 'codingame',
version: '1.0.0',
engines: {
vscode: '*'
const { getApi } = registerExtension(
{
name: 'aiDemo',
publisher: 'codingame',
version: '1.0.0',
engines: {
vscode: '*'
},
contributes: {
commands: [
{
command: 'aiSuggestedCommand',
title: 'This is a command suggested by the AI'
}
]
},
enabledApiProposals: ['aiRelatedInformation']
},
contributes: {
commands: [{
command: 'aiSuggestedCommand',
title: 'This is a command suggested by the AI'
}]
},
enabledApiProposals: ['aiRelatedInformation']
}, ExtensionHostKind.LocalProcess, {
system: true // to be able to use api proposals
})
ExtensionHostKind.LocalProcess,
{
system: true // to be able to use api proposals
}
)

void getApi().then(async vscode => {
void getApi().then(async (vscode) => {
vscode.commands.registerCommand('aiSuggestedCommand', () => {
void vscode.window.showInformationMessage('Hello', {
detail: 'You just run the AI suggested command',
modal: true
})
})
vscode.ai.registerRelatedInformationProvider(vscode.RelatedInformationType.CommandInformation, {
provideRelatedInformation () {
return [{
type: vscode.RelatedInformationType.CommandInformation,
command: 'aiSuggestedCommand',
weight: 9999
}]
provideRelatedInformation() {
return [
{
type: vscode.RelatedInformationType.CommandInformation,
command: 'aiSuggestedCommand',
weight: 9999
}
]
}
})
})
Loading