mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
macos: open URLs with NSWorkspace APIs instead of open
Fixes #5256 This updates the macOS apprt to implement the `OPEN_URL` apprt action to use the NSWorkspace APIs instead of the `open` command line utility. As part of this, we removed the `ghostty_config_open` libghostty API and instead introduced a new `ghostty_config_open_path` API that returns the path to open, and then we use the `NSWorkspace` APIs to open it (same function as the `OPEN_URL` action).
This commit is contained in:
@ -350,6 +350,11 @@ typedef struct {
|
|||||||
const char* message;
|
const char* message;
|
||||||
} ghostty_diagnostic_s;
|
} ghostty_diagnostic_s;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* ptr;
|
||||||
|
uintptr_t len;
|
||||||
|
} ghostty_string_s;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
double tl_px_x;
|
double tl_px_x;
|
||||||
double tl_px_y;
|
double tl_px_y;
|
||||||
@ -797,6 +802,7 @@ int ghostty_init(uintptr_t, char**);
|
|||||||
void ghostty_cli_try_action(void);
|
void ghostty_cli_try_action(void);
|
||||||
ghostty_info_s ghostty_info(void);
|
ghostty_info_s ghostty_info(void);
|
||||||
const char* ghostty_translate(const char*);
|
const char* ghostty_translate(const char*);
|
||||||
|
void ghostty_string_free(ghostty_string_s);
|
||||||
|
|
||||||
ghostty_config_t ghostty_config_new();
|
ghostty_config_t ghostty_config_new();
|
||||||
void ghostty_config_free(ghostty_config_t);
|
void ghostty_config_free(ghostty_config_t);
|
||||||
@ -811,7 +817,7 @@ ghostty_input_trigger_s ghostty_config_trigger(ghostty_config_t,
|
|||||||
uintptr_t);
|
uintptr_t);
|
||||||
uint32_t ghostty_config_diagnostics_count(ghostty_config_t);
|
uint32_t ghostty_config_diagnostics_count(ghostty_config_t);
|
||||||
ghostty_diagnostic_s ghostty_config_get_diagnostic(ghostty_config_t, uint32_t);
|
ghostty_diagnostic_s ghostty_config_get_diagnostic(ghostty_config_t, uint32_t);
|
||||||
void ghostty_config_open();
|
ghostty_string_s ghostty_config_open_path(void);
|
||||||
|
|
||||||
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s*,
|
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s*,
|
||||||
ghostty_config_t);
|
ghostty_config_t);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
9351BE8E3D22937F003B3499 /* nvim in Resources */ = {isa = PBXBuildFile; fileRef = 9351BE8E2D22937F003B3499 /* nvim */; };
|
9351BE8E3D22937F003B3499 /* nvim in Resources */ = {isa = PBXBuildFile; fileRef = 9351BE8E2D22937F003B3499 /* nvim */; };
|
||||||
A50297352DFA0F3400B4E924 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50297342DFA0F3300B4E924 /* Double+Extension.swift */; };
|
A50297352DFA0F3400B4E924 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50297342DFA0F3300B4E924 /* Double+Extension.swift */; };
|
||||||
A505D21D2E1A2FA20018808F /* FileHandle+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A505D21C2E1A2F9E0018808F /* FileHandle+Extension.swift */; };
|
A505D21D2E1A2FA20018808F /* FileHandle+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A505D21C2E1A2F9E0018808F /* FileHandle+Extension.swift */; };
|
||||||
|
A505D21F2E1B6DE00018808F /* NSWorkspace+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A505D21E2E1B6DDC0018808F /* NSWorkspace+Extension.swift */; };
|
||||||
A511940F2E050595007258CC /* CloseTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A511940E2E050590007258CC /* CloseTerminalIntent.swift */; };
|
A511940F2E050595007258CC /* CloseTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A511940E2E050590007258CC /* CloseTerminalIntent.swift */; };
|
||||||
A51194112E05A483007258CC /* QuickTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51194102E05A480007258CC /* QuickTerminalIntent.swift */; };
|
A51194112E05A483007258CC /* QuickTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51194102E05A480007258CC /* QuickTerminalIntent.swift */; };
|
||||||
A51194132E05D006007258CC /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51194122E05D003007258CC /* Optional+Extension.swift */; };
|
A51194132E05D006007258CC /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51194122E05D003007258CC /* Optional+Extension.swift */; };
|
||||||
@ -160,6 +161,7 @@
|
|||||||
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = "../zig-out/share/nvim"; sourceTree = "<group>"; };
|
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = "../zig-out/share/nvim"; sourceTree = "<group>"; };
|
||||||
A50297342DFA0F3300B4E924 /* Double+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = "<group>"; };
|
A50297342DFA0F3300B4E924 /* Double+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A505D21C2E1A2F9E0018808F /* FileHandle+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileHandle+Extension.swift"; sourceTree = "<group>"; };
|
A505D21C2E1A2F9E0018808F /* FileHandle+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileHandle+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
A505D21E2E1B6DDC0018808F /* NSWorkspace+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWorkspace+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A511940E2E050590007258CC /* CloseTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseTerminalIntent.swift; sourceTree = "<group>"; };
|
A511940E2E050590007258CC /* CloseTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseTerminalIntent.swift; sourceTree = "<group>"; };
|
||||||
A51194102E05A480007258CC /* QuickTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalIntent.swift; sourceTree = "<group>"; };
|
A51194102E05A480007258CC /* QuickTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalIntent.swift; sourceTree = "<group>"; };
|
||||||
A51194122E05D003007258CC /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
|
A51194122E05D003007258CC /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
|
||||||
@ -531,6 +533,7 @@
|
|||||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||||
A5874D9C2DAD785F00E83852 /* NSWindow+Extension.swift */,
|
A5874D9C2DAD785F00E83852 /* NSWindow+Extension.swift */,
|
||||||
|
A505D21E2E1B6DDC0018808F /* NSWorkspace+Extension.swift */,
|
||||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
||||||
A58636722DF4813000E04A10 /* UndoManager+Extension.swift */,
|
A58636722DF4813000E04A10 /* UndoManager+Extension.swift */,
|
||||||
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
||||||
@ -819,6 +822,7 @@
|
|||||||
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
||||||
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
||||||
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
||||||
|
A505D21F2E1B6DE00018808F /* NSWorkspace+Extension.swift in Sources */,
|
||||||
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
|
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
|
||||||
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */,
|
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */,
|
||||||
A511940F2E050595007258CC /* CloseTerminalIntent.swift in Sources */,
|
A511940F2E050595007258CC /* CloseTerminalIntent.swift in Sources */,
|
||||||
|
@ -932,7 +932,7 @@ class AppDelegate: NSObject,
|
|||||||
//MARK: - IB Actions
|
//MARK: - IB Actions
|
||||||
|
|
||||||
@IBAction func openConfig(_ sender: Any?) {
|
@IBAction func openConfig(_ sender: Any?) {
|
||||||
ghostty.openConfig()
|
Ghostty.App.openConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func reloadConfig(_ sender: Any?) {
|
@IBAction func reloadConfig(_ sender: Any?) {
|
||||||
|
@ -40,4 +40,34 @@ extension Ghostty.Action {
|
|||||||
self.amount = c.amount
|
self.amount = c.amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct OpenURL {
|
||||||
|
enum Kind {
|
||||||
|
case unknown
|
||||||
|
case text
|
||||||
|
|
||||||
|
init(_ c: ghostty_action_open_url_kind_e) {
|
||||||
|
switch c {
|
||||||
|
case GHOSTTY_ACTION_OPEN_URL_KIND_TEXT:
|
||||||
|
self = .text
|
||||||
|
default:
|
||||||
|
self = .unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind: Kind
|
||||||
|
let url: String
|
||||||
|
|
||||||
|
init(c: ghostty_action_open_url_s) {
|
||||||
|
self.kind = Kind(c.kind)
|
||||||
|
|
||||||
|
if let urlCString = c.url {
|
||||||
|
let data = Data(bytes: urlCString, count: Int(c.len))
|
||||||
|
self.url = String(data: data, encoding: .utf8) ?? ""
|
||||||
|
} else {
|
||||||
|
self.url = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,9 +114,21 @@ extension Ghostty {
|
|||||||
ghostty_app_tick(app)
|
ghostty_app_tick(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
func openConfig() {
|
static func openConfig() {
|
||||||
guard let app = self.app else { return }
|
let str = Ghostty.AllocatedString(ghostty_config_open_path()).string
|
||||||
ghostty_app_open_config(app)
|
guard !str.isEmpty else { return }
|
||||||
|
#if os(macOS)
|
||||||
|
let fileURL = URL(fileURLWithPath: str).absoluteString
|
||||||
|
var action = ghostty_action_open_url_s()
|
||||||
|
action.kind = GHOSTTY_ACTION_OPEN_URL_KIND_TEXT
|
||||||
|
fileURL.withCString { cStr in
|
||||||
|
action.url = cStr
|
||||||
|
action.len = UInt(fileURL.count)
|
||||||
|
_ = openURL(action)
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fatalError("Unsupported platform for opening config file")
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reload the configuration.
|
/// Reload the configuration.
|
||||||
@ -488,7 +500,7 @@ extension Ghostty {
|
|||||||
pwdChanged(app, target: target, v: action.action.pwd)
|
pwdChanged(app, target: target, v: action.action.pwd)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_OPEN_CONFIG:
|
case GHOSTTY_ACTION_OPEN_CONFIG:
|
||||||
ghostty_config_open()
|
openConfig()
|
||||||
|
|
||||||
case GHOSTTY_ACTION_FLOAT_WINDOW:
|
case GHOSTTY_ACTION_FLOAT_WINDOW:
|
||||||
toggleFloatWindow(app, target: target, mode: action.action.float_window)
|
toggleFloatWindow(app, target: target, mode: action.action.float_window)
|
||||||
@ -547,6 +559,9 @@ extension Ghostty {
|
|||||||
case GHOSTTY_ACTION_CHECK_FOR_UPDATES:
|
case GHOSTTY_ACTION_CHECK_FOR_UPDATES:
|
||||||
checkForUpdates(app)
|
checkForUpdates(app)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_OPEN_URL:
|
||||||
|
return openURL(action.action.open_url)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_UNDO:
|
case GHOSTTY_ACTION_UNDO:
|
||||||
return undo(app, target: target)
|
return undo(app, target: target)
|
||||||
|
|
||||||
@ -599,6 +614,34 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func openURL(
|
||||||
|
_ v: ghostty_action_open_url_s
|
||||||
|
) -> Bool {
|
||||||
|
let action = Ghostty.Action.OpenURL(c: v)
|
||||||
|
|
||||||
|
// Convert the URL string to a URL object
|
||||||
|
guard let url = URL(string: action.url) else {
|
||||||
|
Ghostty.logger.warning("invalid URL for open URL action: \(action.url)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch action.kind {
|
||||||
|
case .text:
|
||||||
|
// Open with the default text editor
|
||||||
|
if let textEditor = NSWorkspace.shared.defaultTextEditor {
|
||||||
|
NSWorkspace.shared.open([url], withApplicationAt: textEditor, configuration: NSWorkspace.OpenConfiguration())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case .unknown:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open with the default application for the URL
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private static func undo(_ app: ghostty_app_t, target: ghostty_target_s) -> Bool {
|
private static func undo(_ app: ghostty_app_t, target: ghostty_target_s) -> Bool {
|
||||||
let undoManager: UndoManager?
|
let undoManager: UndoManager?
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
|
@ -73,6 +73,26 @@ extension Ghostty {
|
|||||||
|
|
||||||
// MARK: Swift Types for C Types
|
// MARK: Swift Types for C Types
|
||||||
|
|
||||||
|
extension Ghostty {
|
||||||
|
class AllocatedString {
|
||||||
|
private let cString: ghostty_string_s
|
||||||
|
|
||||||
|
init(_ c: ghostty_string_s) {
|
||||||
|
self.cString = c
|
||||||
|
}
|
||||||
|
|
||||||
|
var string: String {
|
||||||
|
guard let ptr = cString.ptr else { return "" }
|
||||||
|
let data = Data(bytes: ptr, count: Int(cString.len))
|
||||||
|
return String(data: data, encoding: .utf8) ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
ghostty_string_free(cString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Ghostty {
|
extension Ghostty {
|
||||||
enum SetFloatWIndow {
|
enum SetFloatWIndow {
|
||||||
case on
|
case on
|
||||||
|
29
macos/Sources/Helpers/Extensions/NSWorkspace+Extension.swift
Normal file
29
macos/Sources/Helpers/Extensions/NSWorkspace+Extension.swift
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import AppKit
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
extension NSWorkspace {
|
||||||
|
/// Returns the URL of the default text editor application.
|
||||||
|
/// - Returns: The URL of the default text editor, or nil if no default text editor is found.
|
||||||
|
var defaultTextEditor: URL? {
|
||||||
|
defaultApplicationURL(forContentType: UTType.plainText.identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the URL of the default application for opening files with the specified content type.
|
||||||
|
/// - Parameter contentType: The content type identifier (UTI) to find the default application for.
|
||||||
|
/// - Returns: The URL of the default application, or nil if no default application is found.
|
||||||
|
func defaultApplicationURL(forContentType contentType: String) -> URL? {
|
||||||
|
return LSCopyDefaultApplicationURLForContentType(
|
||||||
|
contentType as CFString,
|
||||||
|
.all,
|
||||||
|
nil
|
||||||
|
)?.takeRetainedValue() as? URL
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the URL of the default application for opening files with the specified file extension.
|
||||||
|
/// - Parameter ext: The file extension to find the default application for.
|
||||||
|
/// - Returns: The URL of the default application, or nil if no default application is found.
|
||||||
|
func defaultApplicationURL(forExtension ext: String) -> URL? {
|
||||||
|
guard let uti = UTType(filenameExtension: ext) else { return nil}
|
||||||
|
return defaultApplicationURL(forContentType: uti.identifier)
|
||||||
|
}
|
||||||
|
}
|
@ -496,7 +496,7 @@ pub fn performAction(
|
|||||||
.resize_split => self.resizeSplit(target, value),
|
.resize_split => self.resizeSplit(target, value),
|
||||||
.equalize_splits => self.equalizeSplits(target),
|
.equalize_splits => self.equalizeSplits(target),
|
||||||
.goto_split => return self.gotoSplit(target, value),
|
.goto_split => return self.gotoSplit(target, value),
|
||||||
.open_config => try configpkg.edit.open(self.core_app.alloc),
|
.open_config => return self.openConfig(),
|
||||||
.config_change => self.configChange(target, value.config),
|
.config_change => self.configChange(target, value.config),
|
||||||
.reload_config => try self.reloadConfig(target, value),
|
.reload_config => try self.reloadConfig(target, value),
|
||||||
.inspector => self.controlInspector(target, value),
|
.inspector => self.controlInspector(target, value),
|
||||||
@ -1759,7 +1759,22 @@ fn initActions(self: *App) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openUrl(
|
fn openConfig(self: *App) !bool {
|
||||||
|
// Get the config file path
|
||||||
|
const alloc = self.core_app.alloc;
|
||||||
|
const path = configpkg.edit.openPath(alloc) catch |err| {
|
||||||
|
log.warn("error getting config file path: {}", .{err});
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
defer alloc.free(path);
|
||||||
|
|
||||||
|
// Open it using openURL. "path" isn't actually a URL but
|
||||||
|
// at the time of writing that works just fine for GTK.
|
||||||
|
self.openUrl(.{ .kind = .text, .url = path });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn openUrl(
|
||||||
app: *App,
|
app: *App,
|
||||||
value: apprt.action.OpenUrl,
|
value: apprt.action.OpenUrl,
|
||||||
) void {
|
) void {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
const cli = @import("../cli.zig");
|
const cli = @import("../cli.zig");
|
||||||
const inputpkg = @import("../input.zig");
|
const inputpkg = @import("../input.zig");
|
||||||
const global = &@import("../global.zig").state;
|
const state = &@import("../global.zig").state;
|
||||||
|
const c = @import("../main_c.zig");
|
||||||
|
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
const c_get = @import("c_get.zig");
|
const c_get = @import("c_get.zig");
|
||||||
@ -12,14 +14,14 @@ const log = std.log.scoped(.config);
|
|||||||
|
|
||||||
/// Create a new configuration filled with the initial default values.
|
/// Create a new configuration filled with the initial default values.
|
||||||
export fn ghostty_config_new() ?*Config {
|
export fn ghostty_config_new() ?*Config {
|
||||||
const result = global.alloc.create(Config) catch |err| {
|
const result = state.alloc.create(Config) catch |err| {
|
||||||
log.err("error allocating config err={}", .{err});
|
log.err("error allocating config err={}", .{err});
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
result.* = Config.default(global.alloc) catch |err| {
|
result.* = Config.default(state.alloc) catch |err| {
|
||||||
log.err("error creating config err={}", .{err});
|
log.err("error creating config err={}", .{err});
|
||||||
global.alloc.destroy(result);
|
state.alloc.destroy(result);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,20 +31,20 @@ export fn ghostty_config_new() ?*Config {
|
|||||||
export fn ghostty_config_free(ptr: ?*Config) void {
|
export fn ghostty_config_free(ptr: ?*Config) void {
|
||||||
if (ptr) |v| {
|
if (ptr) |v| {
|
||||||
v.deinit();
|
v.deinit();
|
||||||
global.alloc.destroy(v);
|
state.alloc.destroy(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deep clone the configuration.
|
/// Deep clone the configuration.
|
||||||
export fn ghostty_config_clone(self: *Config) ?*Config {
|
export fn ghostty_config_clone(self: *Config) ?*Config {
|
||||||
const result = global.alloc.create(Config) catch |err| {
|
const result = state.alloc.create(Config) catch |err| {
|
||||||
log.err("error allocating config err={}", .{err});
|
log.err("error allocating config err={}", .{err});
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
result.* = self.clone(global.alloc) catch |err| {
|
result.* = self.clone(state.alloc) catch |err| {
|
||||||
log.err("error cloning config err={}", .{err});
|
log.err("error cloning config err={}", .{err});
|
||||||
global.alloc.destroy(result);
|
state.alloc.destroy(result);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ export fn ghostty_config_clone(self: *Config) ?*Config {
|
|||||||
|
|
||||||
/// Load the configuration from the CLI args.
|
/// Load the configuration from the CLI args.
|
||||||
export fn ghostty_config_load_cli_args(self: *Config) void {
|
export fn ghostty_config_load_cli_args(self: *Config) void {
|
||||||
self.loadCliArgs(global.alloc) catch |err| {
|
self.loadCliArgs(state.alloc) catch |err| {
|
||||||
log.err("error loading config err={}", .{err});
|
log.err("error loading config err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -60,7 +62,7 @@ export fn ghostty_config_load_cli_args(self: *Config) void {
|
|||||||
/// is usually done first. The default file locations are locations
|
/// is usually done first. The default file locations are locations
|
||||||
/// such as the home directory.
|
/// such as the home directory.
|
||||||
export fn ghostty_config_load_default_files(self: *Config) void {
|
export fn ghostty_config_load_default_files(self: *Config) void {
|
||||||
self.loadDefaultFiles(global.alloc) catch |err| {
|
self.loadDefaultFiles(state.alloc) catch |err| {
|
||||||
log.err("error loading config err={}", .{err});
|
log.err("error loading config err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -69,7 +71,7 @@ export fn ghostty_config_load_default_files(self: *Config) void {
|
|||||||
/// file locations in the previously loaded configuration. This will
|
/// file locations in the previously loaded configuration. This will
|
||||||
/// recursively continue to load up to a built-in limit.
|
/// recursively continue to load up to a built-in limit.
|
||||||
export fn ghostty_config_load_recursive_files(self: *Config) void {
|
export fn ghostty_config_load_recursive_files(self: *Config) void {
|
||||||
self.loadRecursiveFiles(global.alloc) catch |err| {
|
self.loadRecursiveFiles(state.alloc) catch |err| {
|
||||||
log.err("error loading config err={}", .{err});
|
log.err("error loading config err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -122,10 +124,13 @@ export fn ghostty_config_get_diagnostic(self: *Config, idx: u32) Diagnostic {
|
|||||||
return .{ .message = message.ptr };
|
return .{ .message = message.ptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn ghostty_config_open() void {
|
export fn ghostty_config_open_path() c.String {
|
||||||
edit.open(global.alloc) catch |err| {
|
const path = edit.openPath(state.alloc) catch |err| {
|
||||||
log.err("error opening config in editor err={}", .{err});
|
log.err("error opening config in editor err={}", .{err});
|
||||||
|
return .empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return .fromSlice(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sync with ghostty_diagnostic_s
|
/// Sync with ghostty_diagnostic_s
|
||||||
|
@ -5,18 +5,19 @@ const Allocator = std.mem.Allocator;
|
|||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
const internal_os = @import("../os/main.zig");
|
const internal_os = @import("../os/main.zig");
|
||||||
|
|
||||||
/// Open the configuration in the OS default editor according to the default
|
/// The path to the configuration that should be opened for editing.
|
||||||
/// paths the main config file could be in.
|
|
||||||
///
|
///
|
||||||
/// On Linux, this will open the file at the XDG config path. This is the
|
/// On Linux, this will use the file at the XDG config path. This is the
|
||||||
/// only valid path for Linux so we don't need to check for other paths.
|
/// only valid path for Linux so we don't need to check for other paths.
|
||||||
///
|
///
|
||||||
/// On macOS, both XDG and AppSupport paths are valid. Because Ghostty
|
/// On macOS, both XDG and AppSupport paths are valid. Because Ghostty
|
||||||
/// prioritizes AppSupport over XDG, we will open AppSupport if it exists,
|
/// prioritizes AppSupport over XDG, we will use AppSupport if it exists,
|
||||||
/// followed by XDG if it exists, and finally AppSupport if neither exist.
|
/// followed by XDG if it exists, and finally AppSupport if neither exist.
|
||||||
/// For the existence check, we also prefer non-empty files over empty
|
/// For the existence check, we also prefer non-empty files over empty
|
||||||
/// files.
|
/// files.
|
||||||
pub fn open(alloc_gpa: Allocator) !void {
|
///
|
||||||
|
/// The returned value is allocated using the provided allocator.
|
||||||
|
pub fn openPath(alloc_gpa: Allocator) ![:0]const u8 {
|
||||||
// Use an arena to make memory management easier in here.
|
// Use an arena to make memory management easier in here.
|
||||||
var arena = ArenaAllocator.init(alloc_gpa);
|
var arena = ArenaAllocator.init(alloc_gpa);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
@ -41,7 +42,7 @@ pub fn open(alloc_gpa: Allocator) !void {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try internal_os.open(alloc_gpa, .text, config_path);
|
return try alloc_gpa.dupeZ(u8, config_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the config path to use for open for the current OS.
|
/// Returns the config path to use for open for the current OS.
|
||||||
|
@ -19,7 +19,12 @@ const internal_os = @import("os/main.zig");
|
|||||||
|
|
||||||
// Some comptime assertions that our C API depends on.
|
// Some comptime assertions that our C API depends on.
|
||||||
comptime {
|
comptime {
|
||||||
|
// We allow tests to reference this file because we unit test
|
||||||
|
// some of the C API. At runtime though we should never get these
|
||||||
|
// functions unless we are building libghostty.
|
||||||
|
if (!builtin.is_test) {
|
||||||
assert(apprt.runtime == apprt.embedded);
|
assert(apprt.runtime == apprt.embedded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Global options so we can log. This is identical to main.
|
/// Global options so we can log. This is identical to main.
|
||||||
@ -29,7 +34,9 @@ comptime {
|
|||||||
// These structs need to be referenced so the `export` functions
|
// These structs need to be referenced so the `export` functions
|
||||||
// are truly exported by the C API lib.
|
// are truly exported by the C API lib.
|
||||||
_ = @import("config.zig").CAPI;
|
_ = @import("config.zig").CAPI;
|
||||||
|
if (@hasDecl(apprt.runtime, "CAPI")) {
|
||||||
_ = apprt.runtime.CAPI;
|
_ = apprt.runtime.CAPI;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ghostty_info_s
|
/// ghostty_info_s
|
||||||
@ -46,6 +53,24 @@ const Info = extern struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// ghostty_string_s
|
||||||
|
pub const String = extern struct {
|
||||||
|
ptr: ?[*]const u8,
|
||||||
|
len: usize,
|
||||||
|
|
||||||
|
pub const empty: String = .{
|
||||||
|
.ptr = null,
|
||||||
|
.len = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn fromSlice(slice: []const u8) String {
|
||||||
|
return .{
|
||||||
|
.ptr = slice.ptr,
|
||||||
|
.len = slice.len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Initialize ghostty global state.
|
/// Initialize ghostty global state.
|
||||||
export fn ghostty_init(argc: usize, argv: [*][*:0]u8) c_int {
|
export fn ghostty_init(argc: usize, argv: [*][*:0]u8) c_int {
|
||||||
assert(builtin.link_libc);
|
assert(builtin.link_libc);
|
||||||
@ -95,3 +120,8 @@ export fn ghostty_info() Info {
|
|||||||
export fn ghostty_translate(msgid: [*:0]const u8) [*:0]const u8 {
|
export fn ghostty_translate(msgid: [*:0]const u8) [*:0]const u8 {
|
||||||
return internal_os.i18n._(msgid);
|
return internal_os.i18n._(msgid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Free a string allocated by Ghostty.
|
||||||
|
export fn ghostty_string_free(str: String) void {
|
||||||
|
state.alloc.free(str.ptr.?[0..str.len]);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user