diff options
-rw-r--r-- | examples/bun/openInEditor.js | 23 | ||||
-rw-r--r-- | src/javascript/jsc/api/bun.zig | 81 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.zig | 9 | ||||
-rw-r--r-- | src/open.zig | 31 |
4 files changed, 131 insertions, 13 deletions
diff --git a/examples/bun/openInEditor.js b/examples/bun/openInEditor.js new file mode 100644 index 000000000..59282c098 --- /dev/null +++ b/examples/bun/openInEditor.js @@ -0,0 +1,23 @@ +import { resolve } from "path"; +import { parse } from "querystring"; + +export default { + fetch(req) { + if (req.url === "/favicon.ico") + return new Response("nooo dont open favicon in editor", { status: 404 }); + + var pathname = req.url.substring(1); + const q = pathname.indexOf("?"); + var { editor } = parse(pathname.substring(q + 1)) || {}; + + if (q > 0) { + pathname = pathname.substring(0, q); + } + + Bun.openInEditor(resolve(pathname), { + editor, + }); + + return new Response(`Opened ${req.url}`); + }, +}; diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig index 6c3729f0d..f1a6e6c36 100644 --- a/src/javascript/jsc/api/bun.zig +++ b/src/javascript/jsc/api/bun.zig @@ -75,6 +75,7 @@ const URL = @import("../../../url.zig").URL; const Transpiler = @import("./transpiler.zig"); const VirtualMachine = @import("../javascript.zig").VirtualMachine; const IOTask = JSC.IOTask; + const is_bindgen = JSC.is_bindgen; threadlocal var css_imports_list_strings: [512]ZigString = undefined; @@ -540,6 +541,82 @@ pub fn getRouteNames( return ref; } +const Editor = @import("../../../open.zig").Editor; +pub fn openInEditor( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + args: []const js.JSValueRef, + exception: js.ExceptionRef, +) js.JSValueRef { + var edit = &VirtualMachine.vm.rareData().editor_context; + + var arguments = JSC.Node.ArgumentsSlice.from(args); + var path: string = ""; + var editor_choice: ?Editor = null; + var line: ?string = null; + var column: ?string = null; + + if (arguments.nextEat()) |file_path_| { + path = file_path_.toSlice(ctx.ptr(), bun.default_allocator).slice(); + } + + if (arguments.nextEat()) |opts| { + if (!opts.isUndefinedOrNull()) { + if (opts.getTruthy(ctx.ptr(), "editor")) |editor_val| { + var sliced = editor_val.toSlice(ctx.ptr(), bun.default_allocator); + var prev_name = edit.name; + + if (!strings.eqlLong(prev_name, sliced.slice(), true)) { + var prev = edit.*; + edit.name = sliced.slice(); + edit.detectEditor(VirtualMachine.vm.bundler.env); + editor_choice = edit.editor; + if (editor_choice == null) { + edit.* = prev; + JSError(getAllocator(ctx), "Could not find editor \"{s}\"", .{sliced.slice()}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } else if (edit.name.ptr == edit.path.ptr) { + edit.name = bun.default_allocator.dupe(u8, edit.path) catch unreachable; + edit.path = edit.path; + } + } + } + + if (opts.getTruthy(ctx.ptr(), "line")) |line_| { + line = line_.toSlice(ctx.ptr(), bun.default_allocator).slice(); + } + + if (opts.getTruthy(ctx.ptr(), "column")) |column_| { + column = column_.toSlice(ctx.ptr(), bun.default_allocator).slice(); + } + } + } + + const editor = editor_choice orelse edit.editor orelse brk: { + edit.autoDetectEditor(VirtualMachine.vm.bundler.env); + if (edit.editor == null) { + JSC.JSError(bun.default_allocator, "Failed to auto-detect editor", .{}, ctx, exception); + return null; + } + + break :brk edit.editor.?; + }; + + if (path.len == 0) { + JSError(getAllocator(ctx), "No file path specified", .{}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + + editor.open(edit.path, path, line, column, bun.default_allocator) catch |err| { + JSC.JSError(bun.default_allocator, "Opening editor failed {s}", .{@errorName(err)}, ctx, exception); + return null; + }; + + return JSC.JSValue.jsUndefined().asObjectRef(); +} + pub fn readFileAsBytes( _: void, ctx: js.JSContextRef, @@ -950,6 +1027,10 @@ pub const Class = NewClass( .rfn = Bun.shrink, .ts = d.ts{}, }, + .openInEditor = .{ + .rfn = Bun.openInEditor, + .ts = d.ts{}, + }, .readAllStdinSync = .{ .rfn = Bun.readAllStdinSync, .ts = d.ts{}, diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 0ddd11862..05298ea27 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -2256,6 +2256,15 @@ pub const JSValue = enum(u64) { return if (@enumToInt(value) != 0) value else return null; } + pub fn getTruthy(this: JSValue, global: *JSGlobalObject, property: []const u8) ?JSValue { + if (get(this, global, property)) |prop| { + if (@enumToInt(prop) == 0 or prop.isUndefinedOrNull()) return null; + return prop; + } + + return null; + } + /// Alias for getIfPropertyExists pub const getIfPropertyExists = get; diff --git a/src/open.zig b/src/open.zig index f129dfc54..26b980a66 100644 --- a/src/open.zig +++ b/src/open.zig @@ -219,12 +219,14 @@ pub const Editor = enum(u8) { file: []const u8, line: ?string, column: ?string, - allocator: std.mem.Allocator, + _: std.mem.Allocator, ) !void { - var file_path_buf: [bun.MAX_PATH_BYTES + 1024]u8 = undefined; - var file_path_buf_stream = std.io.fixedBufferStream(&file_path_buf); + var spawned = try default_allocator.create(SpawnedEditorContext); + spawned.* = .{}; + var file_path_buf_stream = std.io.fixedBufferStream(&spawned.file_path_buf); var file_path_buf_writer = file_path_buf_stream.writer(); - var args_buf: [10]string = undefined; + var args_buf = &spawned.buf; + errdefer default_allocator.destroy(spawned); var i: usize = 0; @@ -309,19 +311,22 @@ pub const Editor = enum(u8) { }, } - var child_process = try std.ChildProcess.init(args_buf[0..i], allocator); - child_process.stderr_behavior = .Pipe; - child_process.stdin_behavior = .Ignore; - child_process.stdout_behavior = .Pipe; - try child_process.spawn(); - var thread = try std.Thread.spawn(.{}, autoClose, .{child_process}); + spawned.child_process = try std.ChildProcess.init(args_buf[0..i], default_allocator); + var thread = try std.Thread.spawn(.{}, autoClose, .{spawned}); thread.detach(); } + const SpawnedEditorContext = struct { + file_path_buf: [1024 + bun.MAX_PATH_BYTES]u8 = undefined, + buf: [10]string = undefined, + child_process: *std.ChildProcess = undefined, + }; + + fn autoClose(spawned: *SpawnedEditorContext) void { + defer bun.default_allocator.destroy(spawned); - fn autoClose(child_process: *std.ChildProcess) void { Global.setThreadName("Open Editor"); - _ = child_process.wait() catch {}; - child_process.deinit(); + spawned.child_process.spawn() catch return; + _ = spawned.child_process.wait() catch {}; } }; |