Skip to content

Commit

Permalink
renpyWarp.jumpToLabel
Browse files Browse the repository at this point in the history
fixes #46
  • Loading branch information
furudean committed Aug 18, 2024
1 parent c715efd commit 4574428
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 73 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to this project will be documented in this file.

## Unreleased

- Adds a new command, `renpyWarp.jumpToLabel`, which allows you to jump to a
label in your Ren'Py project

## 1.16.0 - 2024-08-18

- Now focuses window and warp in parallel, which should make it more
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ Launch and sync your Ren'Py game at the current line in Visual Studio Code.

## Commands

This extension adds the following commands:

| Command | Description | Shortcut | Shortcut (Mac) |
| ------------------------------ | --------------------------------------------------- | -------------------------------------------- | ------------------------------------------ |
| `renpyWarp.warpToLine` | Open Ren'Py at the current line | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>E</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>E</kbd> |
| `renpyWarp.warpToFile` | Open Ren'Py at the current file | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>F</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>F</kbd> |
| `renpyWarp.launch` | Launch the Ren'Py project | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>L</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>L</kbd> |
| `renpyWarp.toggleFollowCursor` | Toggle: Warp to selected line as cursor moves | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>C</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>C</kbd> |
| `renpyWarp.killAll` | Kill running Ren'Py instances | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>K</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>K</kbd> |
| `renpyWarp.installRpe` | Install the Ren'Py extension in the current project | | |
The extension provides many commands to interact with Ren'Py. You probably want
to know about the following:

| Command | Shortcut | Shortcut (Mac) |
| --------------------------------------------- | -------------------------------------------- | ------------------------------------------ |
| Start Ren'Py project | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>L</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>L</kbd> |
| Open Ren'Py at the current line | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>E</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>E</kbd> |
| Open Ren'Py at the current file | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>F</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>F</kbd> |
| Open Ren'Py at label | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>J</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>J</kbd> |
| Toggle: Warp to selected line as cursor moves | <kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>C</kbd> | <kbd>⌘</kbd>+<kbd>Shift</kbd>+<kbd>C</kbd> |

## Triggers

Expand Down
77 changes: 45 additions & 32 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,27 @@
},
"contributes": {
"commands": [
{
"command": "renpyWarp.launch",
"title": "Start Ren'Py project",
"category": "Ren'Py Launch",
"icon": "$(play)"
},
{
"command": "renpyWarp.warpToLine",
"title": "Open Ren'Py at line",
"title": "Open Ren'Py to current line",
"category": "Ren'Py Launch",
"icon": "$(play)"
},
{
"command": "renpyWarp.warpToFile",
"title": "Open Ren'Py at file",
"title": "Open Ren'Py to current file",
"category": "Ren'Py Launch",
"icon": "$(play)"
},
{
"command": "renpyWarp.launch",
"title": "Launch project in Ren'Py",
"command": "renpyWarp.jumpToLabel",
"title": "Open Ren'Py at label",
"category": "Ren'Py Launch",
"icon": "$(play)"
},
Expand Down Expand Up @@ -189,6 +195,12 @@
]
},
"keybindings": [
{
"command": "renpyWarp.launch",
"key": "alt+shift+l",
"mac": "cmd+shift+l",
"when": "workbenchState == 'folder'"
},
{
"command": "renpyWarp.warpToLine",
"key": "alt+shift+e",
Expand All @@ -202,9 +214,9 @@
"when": "editorTextFocus && resourceExtname == '.rpy'"
},
{
"command": "renpyWarp.launch",
"key": "alt+shift+l",
"mac": "cmd+shift+l",
"command": "renpyWarp.jumpToLabel",
"key": "alt+shift+j",
"mac": "cmd+shift+j",
"when": "workbenchState == 'folder'"
},
{
Expand Down Expand Up @@ -245,6 +257,18 @@
"Kill the existing instance and launch a new one."
]
},
"renpyWarp.renpyExtensionsEnabled": {
"type": "string",
"enum": [
"Not set",
"Enabled",
"Disabled"
],
"default": "Not set",
"markdownDescription": "Enable Ren'Py Extension (.rpe) support",
"order": 1,
"scope": "machine-overridable"
},
"renpyWarp.followCursorMode": {
"type": "string",
"order": 2,
Expand All @@ -264,42 +288,31 @@
"renpyWarp.followCursorOnLaunch": {
"type": "boolean",
"default": false,
"order": 3,
"order": 2,
"markdownDescription": "Automatically enter _Follow Cursor_ mode when launching Ren'Py. This is ignored if `#renpyWarp.renpyExtensionsEnabled#` is set to _Disabled_."
},
"renpyWarp.focusWindowOnWarp": {
"type": "boolean",
"default": false,
"order": 4,
"markdownDescription": "Focus the Ren'Py window after a warp command is executed.\n\nNot supported on Linux. Requires accessibility permissions to be granted to Visual Studio Code on macOS."
},
"renpyWarp.editor": {
"type": "string",
"default": "launcher/Visual Studio Code (System).edit.py",
"markdownDescription": "The `Editor` class to supply to `renpy.sh`. Should be a relative or absolute path to an `.edit.py` file.\n\nThis setting is equivalent to the environment variable [`RENPY_EDIT_PY`](https://www.renpy.org/doc/html/editor.html).\n\nRelative paths are resolved from `#renpyWarp.sdkPath#`. For example, a valid value would be [`launcher/System Editor.edit.py`](https://github.com/renpy/renpy/blob/master/launcher/System%20Editor.edit.py).\n\n"
},
"renpyWarp.renpyExtensionsEnabled": {
"type": "string",
"enum": [
"Not set",
"Enabled",
"Disabled"
],
"default": "Not set",
"markdownDescription": "Enable Ren'Py Extension (.rpe) support",
"scope": "machine-overridable"
},
"renpyWarp.setAutoReloadOnSave": {
"type": "boolean",
"default": false,
"order": 2,
"order": 3,
"markdownDescription": "Enable autoreload in the Ren'Py process when a file is saved"
},
"renpyWarp.autoStartSocketServer": {
"type": "boolean",
"default": true,
"order": 2,
"order": 3,
"markdownDescription": "Automatically start the socket server when in a Ren'Py project"
},
"renpyWarp.focusWindowOnWarp": {
"type": "boolean",
"default": false,
"order": 3,
"markdownDescription": "Focus the Ren'Py window after a warp command is executed.\n\nNot supported on Linux. Requires accessibility permissions to be granted to Visual Studio Code on macOS."
},
"renpyWarp.editor": {
"type": "string",
"default": "launcher/Visual Studio Code (System).edit.py",
"markdownDescription": "The `Editor` class to supply to `renpy.sh`. Should be a relative or absolute path to an `.edit.py` file.\n\nThis setting is equivalent to the environment variable [`RENPY_EDIT_PY`](https://www.renpy.org/doc/html/editor.html).\n\nRelative paths are resolved from `#renpyWarp.sdkPath#`. For example, a valid value would be [`launcher/System Editor.edit.py`](https://github.com/renpy/renpy/blob/master/launcher/System%20Editor.edit.py).\n\n"
}
}
}
Expand Down
70 changes: 70 additions & 0 deletions src/lib/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,76 @@ export function get_commands(
}
},

'renpyWarp.jumpToLabel': async () => {
if (get_config('renpyExtensionsEnabled') !== 'Enabled') {
vscode.window.showErrorMessage(
"Ren'Py extensions must be enabled to use this feature",
'OK'
)
return
}

let process = pm.at(-1)

if (process === undefined) {
process = await launch_renpy({
pm,
status_bar,
follow_cursor,
context,
extra_environment: {
RENPY_SKIP_SPLASHSCREEN: '1',
},
})
if (process === undefined) return
await process.wait_for_labels(500)
}

if (process.labels === undefined) {
vscode.window.showErrorMessage(
"Ren'Py has not reported any labels",
'OK'
)
return
}

// https://www.renpy.org/doc/html/label.html#special-labels
const renpy_special_labels = [
// 'start',
'quit',
'after_load',
'splashscreen',
'before_main_menu',
// 'main_menu',
'after_warp',
'hide_windows',
]

const filtered_labels = process.labels
.filter(
(label) =>
!label.startsWith('_') &&
!label.endsWith('_screen') &&
!renpy_special_labels.includes(label)
)
.sort()

const selection = await vscode.window.showQuickPick(
filtered_labels,
{
placeHolder: 'Select a label to jump to',
title: "Jump to Ren'Py label",
}
)

if (selection === undefined) return

await process.jump_to_label(selection)
status_bar.notify(
`$(debug-line-by-line) Jumped to label '${selection}'`
)
},

'renpyWarp.toggleFollowCursor': () => {
if (follow_cursor.active_process) {
follow_cursor.off()
Expand Down
13 changes: 8 additions & 5 deletions src/lib/launch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface LaunchRenpyOptions {
pm: ProcessManager
status_bar: StatusBar
follow_cursor: FollowCursor
extra_environment?: Record<string, string | undefined>
}

/**
Expand All @@ -50,6 +51,7 @@ export async function launch_renpy({
pm,
status_bar,
follow_cursor,
extra_environment,
}: LaunchRenpyOptions): Promise<ManagedProcess | undefined> {
logger.info('launch_renpy:', { file, line })

Expand Down Expand Up @@ -155,6 +157,7 @@ export async function launch_renpy({
}

const process_env = {
...extra_environment,
WARP_WS_NONCE: nonce.toString(),
// see: https://www.renpy.org/doc/html/editor.html
RENPY_EDIT_PY: await get_editor_path(sdk_path),
Expand All @@ -168,11 +171,11 @@ export async function launch_renpy({
},
async (_, cancel) => {
logger.info(
'spawning process',
cmds.join(' '),
'\n',
'with env',
process_env
'spawning process:',
Object.entries(process_env)
.map(([k, v]) => `${k}="${v}"`)
.join(' '),
cmds.map((k) => `"${k}"`).join(' ')
)

const log_file = path.join(
Expand Down
36 changes: 36 additions & 0 deletions src/lib/process/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class UnmanagedProcess {
project_root: string
socket?: WebSocket
dead: boolean = false
labels: string[] | undefined = undefined

private emitter = new EventEmitter()
emit = this.emitter.emit.bind(this.emitter)
Expand Down Expand Up @@ -118,6 +119,34 @@ export class UnmanagedProcess {
})
}

async wait_for_labels(timeout_ms: number): Promise<void> {
if (this.labels) return

logger.info('waiting for labels from renpy window...')

return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
clearInterval(interval)
reject(new Error('timed out waiting for labels'))
}, timeout_ms)

const interval = setInterval(() => {
if (this.labels || this.dead) {
clearTimeout(timeout)
clearInterval(interval)

if (this.labels) {
resolve()
} else {
reject(
new Error('process died before labels connected')
)
}
}
})
})
}

/** Send a message to the Ren'Py process via WebSocket */
private async ipc(message: SocketMessage): Promise<void> {
await this.wait_for_socket(5000)
Expand Down Expand Up @@ -162,6 +191,13 @@ export class UnmanagedProcess {
type: 'set_autoreload',
})
}

async jump_to_label(label: string) {
return this.ipc({
type: 'jump_to_label',
label,
})
}
}

interface ManagedProcessOptions extends Omit<UnmanagedProcessOptions, 'pid'> {
Expand Down
36 changes: 24 additions & 12 deletions src/lib/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,21 +316,33 @@ export async function ensure_socket_server({
project_root,
context,
async message_handler(process, message) {
if (message.type === 'current_line') {
logger.debug(
`current line reported as ${message.relative_path}:${message.line}`
)
if (follow_cursor.active_process === process) {
const message_path = await realpath(message.path as string)
const messsage_handler: Record<string, () => Promise<void> | void> =
{
async current_line() {
logger.debug(
`current line reported as ${message.relative_path}:${message.line}`
)
if (follow_cursor.active_process === process) {
const message_path = await realpath(
message.path as string
)

await sync_editor_with_renpy({
path: message_path,
relative_path: message.relative_path as string,
line: (message.line as number) - 1,
})
await sync_editor_with_renpy({
path: message_path,
relative_path: message.relative_path as string,
line: (message.line as number) - 1,
})
}
},
async list_labels() {
process.labels = message.labels as string[]
},
}

if (message.type in messsage_handler) {
await messsage_handler[message.type]()
} else {
logger.warn('unhandled message:', message)
logger.error('unhandled socket message:', message)
}
},
})
Expand Down
Loading

0 comments on commit 4574428

Please sign in to comment.