clide:// deep link handler — open files at a line (T-56)
clide://open?path=&line= translates (in parseArgv) to editor.open, so an OS scheme invocation routes through the existing CLI→IPC path into the running window — single-instance for free, no new code path. Registered the scheme: linux/clide.desktop MimeType x-scheme-handler/clide (Exec already passes %U) and macOS Info.plist CFBundleURLTypes. Parser validates the action/path/line. Linux works end to end (the OS passes the URL as argv). macOS URL DELIVERY (the AppDelegate openURLs callback → forward into this path) is a follow-up — the scheme is registered, but the native hook needs a real macOS machine to verify, so it's not shipped blind. Also drops a stray import in clide_markdown_test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,12 @@ ArgvParseResult parseArgv(List<String> argv, {required String requestId}) {
|
||||
}
|
||||
|
||||
final first = argv[0];
|
||||
// A clide:// deep link (T-56): the OS scheme handler invokes
|
||||
// `clide clide://open?path=…&line=…`, which lands here as the first arg and
|
||||
// routes through the same IPC path to the running app (no second instance).
|
||||
if (first.startsWith('clide://')) {
|
||||
return _deepLinkToRequest(first, requestId);
|
||||
}
|
||||
// Umbrella commands: single-token name, no verb required.
|
||||
if (_umbrellaCommands.contains(first)) {
|
||||
final tail = argv.sublist(1);
|
||||
@@ -98,6 +104,38 @@ ArgvParseResult parseArgv(List<String> argv, {required String requestId}) {
|
||||
));
|
||||
}
|
||||
|
||||
/// Translate a `clide://` deep link into an [IpcRequest] (T-56). Only the
|
||||
/// `open` action is defined today: `clide://open?path=<abs|repo-rel>&line=<n>`
|
||||
/// maps to `editor.open` (which jumps the selection to the 1-based line). The
|
||||
/// path is passed through verbatim — `editor.open` resolves it against the
|
||||
/// workspace and applies the usual `files.read` allow-list (D-80).
|
||||
ArgvParseResult _deepLinkToRequest(String url, String requestId) {
|
||||
final uri = Uri.tryParse(url);
|
||||
if (uri == null || uri.scheme != 'clide') {
|
||||
return ArgvError(_err(requestId, 'malformed clide:// link: $url'));
|
||||
}
|
||||
switch (uri.host) {
|
||||
case 'open':
|
||||
final path = uri.queryParameters['path'];
|
||||
if (path == null || path.isEmpty) {
|
||||
return ArgvError(_err(requestId, 'clide://open requires a ?path='));
|
||||
}
|
||||
final positional = <String>[path];
|
||||
final lineRaw = uri.queryParameters['line'];
|
||||
if (lineRaw != null && lineRaw.isNotEmpty) {
|
||||
final line = int.tryParse(lineRaw);
|
||||
if (line == null || line < 1) {
|
||||
return ArgvError(_err(requestId, 'clide://open: line must be a positive integer, got "$lineRaw"'));
|
||||
}
|
||||
positional.add('$line');
|
||||
}
|
||||
// Mirror the CLI's positional form so the editor.open schema maps them.
|
||||
return ArgvParsed(IpcRequest(id: requestId, cmd: 'editor.open', args: {'positional': positional}));
|
||||
default:
|
||||
return ArgvError(_err(requestId, 'unknown clide:// action "${uri.host}" (expected: open)'));
|
||||
}
|
||||
}
|
||||
|
||||
// -- internals --------------------------------------------------------------
|
||||
|
||||
sealed class _TailParseResult {
|
||||
|
||||
Reference in New Issue
Block a user