mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-20 00:18:53 +03:00
Merge branch 'main' into mk-localization
This commit is contained in:
115
.github/scripts/request_review.py
vendored
Normal file
115
.github/scripts/request_review.py
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.9"
|
||||
# dependencies = [
|
||||
# "githubkit",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import re
|
||||
from itertools import chain
|
||||
|
||||
from githubkit import GitHub
|
||||
|
||||
ORG_NAME = "ghostty-org"
|
||||
REPO_NAME = "ghostty"
|
||||
ALLOWED_PARENT_TEAM = "localization"
|
||||
LOCALIZATION_TEAM_NAME_PATTERN = re.compile(r"[a-z]{2}_[A-Z]{2}")
|
||||
|
||||
gh = GitHub(os.environ["GITHUB_TOKEN"])
|
||||
|
||||
|
||||
async def fetch_and_parse_codeowners() -> dict[str, str]:
|
||||
content = (
|
||||
await gh.rest.repos.async_get_content(
|
||||
ORG_NAME,
|
||||
REPO_NAME,
|
||||
"CODEOWNERS",
|
||||
headers={"Accept": "application/vnd.github.raw+json"},
|
||||
)
|
||||
).text
|
||||
|
||||
codeowners: dict[str, str] = {}
|
||||
for line in content.splitlines():
|
||||
if not line or line.lstrip().startswith("#"):
|
||||
continue
|
||||
# This assumes that all entries only list one owner
|
||||
# and that this owner is a team (ghostty-org/foobar)
|
||||
path, owner = line.split()
|
||||
codeowners[path.lstrip("/")] = owner.removeprefix(f"@{ORG_NAME}/")
|
||||
return codeowners
|
||||
|
||||
|
||||
async def get_team_members(team_name: str) -> list[str]:
|
||||
team = (await gh.rest.teams.async_get_by_name(ORG_NAME, team_name)).parsed_data
|
||||
if team.parent and team.parent.slug == ALLOWED_PARENT_TEAM:
|
||||
members = (
|
||||
await gh.rest.teams.async_list_members_in_org(ORG_NAME, team_name)
|
||||
).parsed_data
|
||||
return [m.login for m in members]
|
||||
return []
|
||||
|
||||
|
||||
async def get_changed_files(pr_number: int) -> list[str]:
|
||||
diff_entries = (
|
||||
await gh.rest.pulls.async_list_files(
|
||||
ORG_NAME,
|
||||
REPO_NAME,
|
||||
pr_number,
|
||||
per_page=3000,
|
||||
headers={"Accept": "application/vnd.github+json"},
|
||||
)
|
||||
).parsed_data
|
||||
return [d.filename for d in diff_entries]
|
||||
|
||||
|
||||
async def request_review(pr_number: int, pr_author: str, *users: str) -> None:
|
||||
await asyncio.gather(
|
||||
*(
|
||||
gh.rest.pulls.async_request_reviewers(
|
||||
ORG_NAME,
|
||||
REPO_NAME,
|
||||
pr_number,
|
||||
headers={"Accept": "application/vnd.github+json"},
|
||||
data={"reviewers": [user]},
|
||||
)
|
||||
for user in users
|
||||
if user != pr_author
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def is_localization_team(team_name: str) -> bool:
|
||||
return LOCALIZATION_TEAM_NAME_PATTERN.fullmatch(team_name) is not None
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
pr_number = int(os.environ["PR_NUMBER"])
|
||||
changed_files = await get_changed_files(pr_number)
|
||||
pr_author = (
|
||||
await gh.rest.pulls.async_get(ORG_NAME, REPO_NAME, pr_number)
|
||||
).parsed_data.user.login
|
||||
localization_codewners = {
|
||||
path: owner
|
||||
for path, owner in (await fetch_and_parse_codeowners()).items()
|
||||
if is_localization_team(owner)
|
||||
}
|
||||
|
||||
found_owners = set[str]()
|
||||
for file in changed_files:
|
||||
for path, owner in localization_codewners.items():
|
||||
if file.startswith(path):
|
||||
break
|
||||
else:
|
||||
continue
|
||||
found_owners.add(owner)
|
||||
|
||||
member_lists = await asyncio.gather(
|
||||
*(get_team_members(owner) for owner in found_owners)
|
||||
)
|
||||
await request_review(pr_number, pr_author, *chain.from_iterable(member_lists))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
37
.github/workflows/review.yml
vendored
Normal file
37
.github/workflows/review.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: Request Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
|
||||
env:
|
||||
PY_COLORS: 1
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Request Localization Review
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_REVIEW_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: nix develop -c uv run .github/scripts/request_review.py
|
@ -158,9 +158,18 @@
|
||||
# Localization
|
||||
/po/README_TRANSLATORS.md @ghostty-org/localization
|
||||
/po/com.mitchellh.ghostty.pot @ghostty-org/localization
|
||||
/po/ca_ES.UTF-8.po @ghostty-org/ca_ES
|
||||
/po/de_DE.UTF-8.po @ghostty-org/de_DE
|
||||
/po/es_BO.UTF-8.po @ghostty-org/es_BO
|
||||
/po/fr_FR.UTF-8.po @ghostty-org/fr_FR
|
||||
/po/id_ID.UTF-8.po @ghostty-org/id_ID
|
||||
/po/ja_JP.UTF-8.po @ghostty-org/ja_JP
|
||||
/po/nb_NO.UTF-8.po @ghostty-org/nb_NO
|
||||
/po/nl_NL.UTF-8.po @ghostty-org/nl_NL
|
||||
/po/pl_PL.UTF-8.po @ghostty-org/pl_PL
|
||||
/po/pt_BR.UTF-8.po @ghostty-org/pt_BR
|
||||
/po/ru_RU.UTF-8.po @ghostty-org/ru_RU
|
||||
/po/tr_TR.UTF-8.po @ghostty-org/tr_TR
|
||||
/po/uk_UA.UTF-8.po @ghostty-org/uk_UA
|
||||
/po/zh_CN.UTF-8.po @ghostty-org/zh_CN
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
.{
|
||||
.name = .ghostty,
|
||||
.version = "1.1.3",
|
||||
.version = "1.1.4",
|
||||
.paths = .{""},
|
||||
.fingerprint = 0x64407a2a0b4147e5,
|
||||
.dependencies = .{
|
||||
@ -14,8 +14,8 @@
|
||||
},
|
||||
.vaxis = .{
|
||||
// rockorager/libvaxis
|
||||
.url = "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447",
|
||||
.hash = "vaxis-0.1.0-BWNV_MHyCAA0rNbPTr50Z44PyEdNP9zQSnHcXBXoo3Ti",
|
||||
.url = "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23",
|
||||
.hash = "vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn",
|
||||
.lazy = true,
|
||||
},
|
||||
.z2d = .{
|
||||
@ -48,8 +48,8 @@
|
||||
},
|
||||
.zf = .{
|
||||
// natecraddock/zf
|
||||
.url = "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz",
|
||||
.hash = "zf-0.10.3-OIRy8bKIAADhjqtdjVaDfONRuI7RVl5gMbhCoOwiBWV5",
|
||||
.url = "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz",
|
||||
.hash = "zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9",
|
||||
.lazy = true,
|
||||
},
|
||||
.gobject = .{
|
||||
@ -103,8 +103,8 @@
|
||||
// Other
|
||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||
.iterm2_themes = .{
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz",
|
||||
.hash = "N-V-__8AADk6LwSAbK3OMyGiadf6aeyztHNV4-zKaLy6IZa6",
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4c57d8c11d352a4aeda6928b65d78794c28883a5.tar.gz",
|
||||
.hash = "N-V-__8AAEH8MwQaEsARbyV42-bSZGcu1am8xtg2h67wTFC3",
|
||||
.lazy = true,
|
||||
},
|
||||
},
|
||||
|
24
build.zig.zon.json
generated
24
build.zig.zon.json
generated
@ -54,10 +54,10 @@
|
||||
"url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
||||
},
|
||||
"N-V-__8AADk6LwSAbK3OMyGiadf6aeyztHNV4-zKaLy6IZa6": {
|
||||
"N-V-__8AAEH8MwQaEsARbyV42-bSZGcu1am8xtg2h67wTFC3": {
|
||||
"name": "iterm2_themes",
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz",
|
||||
"hash": "sha256-nOkH31MQQd2PPdjVpRxBxNQWfR9Exg6nRF/KHgSz3cM="
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4c57d8c11d352a4aeda6928b65d78794c28883a5.tar.gz",
|
||||
"hash": "sha256-c+twvkEPiz1DaULYlnGXLxis19Q2h+TgBJxoARMasjU="
|
||||
},
|
||||
"N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD": {
|
||||
"name": "libpng",
|
||||
@ -104,10 +104,10 @@
|
||||
"url": "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz",
|
||||
"hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8="
|
||||
},
|
||||
"vaxis-0.1.0-BWNV_MHyCAA0rNbPTr50Z44PyEdNP9zQSnHcXBXoo3Ti": {
|
||||
"vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn": {
|
||||
"name": "vaxis",
|
||||
"url": "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447",
|
||||
"hash": "sha256-iONEySjPeD0WYJ93fw5mxT+0pVfUO/m6008J/LXjQkA="
|
||||
"url": "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23",
|
||||
"hash": "sha256-bNZ3oveT6vPChjimPJ/GGfcdivlAeJdl/xfWM+S/MHY="
|
||||
},
|
||||
"N-V-__8AAKrHGAAs2shYq8UkE6bGcR1QJtLTyOE_lcosMn6t": {
|
||||
"name": "wayland",
|
||||
@ -129,10 +129,10 @@
|
||||
"url": "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz",
|
||||
"hash": "sha256-PEKVSUZ6teRbDyhFPWSiuBSe40pgr0kVRivIY8Cn8HQ="
|
||||
},
|
||||
"zf-0.10.3-OIRy8bKIAADhjqtdjVaDfONRuI7RVl5gMbhCoOwiBWV5": {
|
||||
"zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9": {
|
||||
"name": "zf",
|
||||
"url": "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz",
|
||||
"hash": "sha256-xVva07TAYlVv4E4PKe2wUj86a6Ky2YC30YBgtbvNKvw="
|
||||
"url": "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz",
|
||||
"hash": "sha256-3nulNQd/4rZ4paeXJYXwAliNNyRNsIOX/q3z1JB8C7I="
|
||||
},
|
||||
"zg-0.13.4-AAAAAGiZ7QLz4pvECFa_wG4O4TP4FLABHHbemH2KakWM": {
|
||||
"name": "zg",
|
||||
@ -154,10 +154,10 @@
|
||||
"url": "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz",
|
||||
"hash": "sha256-E77GZ15APYbbO1WzmuJi8eG9/iQFbc2CgkNBxjCLUhk="
|
||||
},
|
||||
"zigimg-0.1.0-lly-O4heEADSRxoTwJwrD3TBfUob9052sIgb9SL8Iz-A": {
|
||||
"zigimg-0.1.0-lly-O6N2EABOxke8dqyzCwhtUCAafqP35zC7wsZ4Ddxj": {
|
||||
"name": "zigimg",
|
||||
"url": "git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0",
|
||||
"hash": "sha256-Rr+mAfbLOoaxHOwCug+0cWCmW9gDhjhnaO2J/Oik9HI="
|
||||
"url": "git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d",
|
||||
"hash": "sha256-oblfr2FIzuqq0FLo/RrzCwUX1NJJuT53EwD3nP3KwN0="
|
||||
},
|
||||
"ziglyph-0.11.2-AAAAAHPtHwB4Mbzn1KvOV7Wpjo82NYEc_v0WC8oCLrkf": {
|
||||
"name": "ziglyph",
|
||||
|
24
build.zig.zon.nix
generated
24
build.zig.zon.nix
generated
@ -170,11 +170,11 @@ in
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AADk6LwSAbK3OMyGiadf6aeyztHNV4-zKaLy6IZa6";
|
||||
name = "N-V-__8AAEH8MwQaEsARbyV42-bSZGcu1am8xtg2h67wTFC3";
|
||||
path = fetchZigArtifact {
|
||||
name = "iterm2_themes";
|
||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz";
|
||||
hash = "sha256-nOkH31MQQd2PPdjVpRxBxNQWfR9Exg6nRF/KHgSz3cM=";
|
||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4c57d8c11d352a4aeda6928b65d78794c28883a5.tar.gz";
|
||||
hash = "sha256-c+twvkEPiz1DaULYlnGXLxis19Q2h+TgBJxoARMasjU=";
|
||||
};
|
||||
}
|
||||
{
|
||||
@ -250,11 +250,11 @@ in
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "vaxis-0.1.0-BWNV_MHyCAA0rNbPTr50Z44PyEdNP9zQSnHcXBXoo3Ti";
|
||||
name = "vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn";
|
||||
path = fetchZigArtifact {
|
||||
name = "vaxis";
|
||||
url = "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447";
|
||||
hash = "sha256-iONEySjPeD0WYJ93fw5mxT+0pVfUO/m6008J/LXjQkA=";
|
||||
url = "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23";
|
||||
hash = "sha256-bNZ3oveT6vPChjimPJ/GGfcdivlAeJdl/xfWM+S/MHY=";
|
||||
};
|
||||
}
|
||||
{
|
||||
@ -290,11 +290,11 @@ in
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "zf-0.10.3-OIRy8bKIAADhjqtdjVaDfONRuI7RVl5gMbhCoOwiBWV5";
|
||||
name = "zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9";
|
||||
path = fetchZigArtifact {
|
||||
name = "zf";
|
||||
url = "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz";
|
||||
hash = "sha256-xVva07TAYlVv4E4PKe2wUj86a6Ky2YC30YBgtbvNKvw=";
|
||||
url = "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz";
|
||||
hash = "sha256-3nulNQd/4rZ4paeXJYXwAliNNyRNsIOX/q3z1JB8C7I=";
|
||||
};
|
||||
}
|
||||
{
|
||||
@ -330,11 +330,11 @@ in
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "zigimg-0.1.0-lly-O4heEADSRxoTwJwrD3TBfUob9052sIgb9SL8Iz-A";
|
||||
name = "zigimg-0.1.0-lly-O6N2EABOxke8dqyzCwhtUCAafqP35zC7wsZ4Ddxj";
|
||||
path = fetchZigArtifact {
|
||||
name = "zigimg";
|
||||
url = "git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0";
|
||||
hash = "sha256-Rr+mAfbLOoaxHOwCug+0cWCmW9gDhjhnaO2J/Oik9HI=";
|
||||
url = "git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d";
|
||||
hash = "sha256-oblfr2FIzuqq0FLo/RrzCwUX1NJJuT53EwD3nP3KwN0=";
|
||||
};
|
||||
}
|
||||
{
|
||||
|
8
build.zig.zon.txt
generated
8
build.zig.zon.txt
generated
@ -1,6 +1,6 @@
|
||||
git+https://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc
|
||||
git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0
|
||||
git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447
|
||||
git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d
|
||||
git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23
|
||||
https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz
|
||||
https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz
|
||||
https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz
|
||||
@ -27,8 +27,8 @@ https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.
|
||||
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
|
||||
https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.tar.gz
|
||||
https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-18-21-1/ghostty-gobject-0.14.0-2025-03-18-21-1.tar.zst
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4c57d8c11d352a4aeda6928b65d78794c28883a5.tar.gz
|
||||
https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz
|
||||
https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz
|
||||
https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz
|
||||
https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz
|
||||
https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz
|
||||
|
@ -51,6 +51,7 @@
|
||||
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
||||
zig = zig.packages.${system}."0.14.0";
|
||||
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
||||
uv = pkgs-unstable.uv;
|
||||
# remove once blueprint-compiler 0.16.0 is in the stable nixpkgs
|
||||
blueprint-compiler = pkgs-unstable.blueprint-compiler;
|
||||
zon2nix = zon2nix;
|
||||
|
@ -91,6 +91,12 @@ class TerminalController: BaseTerminalController {
|
||||
name: Ghostty.Notification.didEqualizeSplits,
|
||||
object: nil
|
||||
)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onCloseWindow),
|
||||
name: .ghosttyCloseWindow,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@ -842,6 +848,12 @@ class TerminalController: BaseTerminalController {
|
||||
closeTab(self)
|
||||
}
|
||||
|
||||
@objc private func onCloseWindow(notification: SwiftUI.Notification) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard surfaceTree?.contains(view: target) ?? false else { return }
|
||||
closeWindow(self)
|
||||
}
|
||||
|
||||
@objc private func onResetWindowSize(notification: SwiftUI.Notification) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard surfaceTree?.contains(view: target) ?? false else { return }
|
||||
|
@ -107,7 +107,7 @@ extension Ghostty {
|
||||
deinit {
|
||||
// This will force the didSet callbacks to run which free.
|
||||
self.app = nil
|
||||
|
||||
|
||||
#if os(macOS)
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
#endif
|
||||
@ -451,6 +451,9 @@ extension Ghostty {
|
||||
case GHOSTTY_ACTION_CLOSE_TAB:
|
||||
closeTab(app, target: target)
|
||||
|
||||
case GHOSTTY_ACTION_CLOSE_WINDOW:
|
||||
closeWindow(app, target: target)
|
||||
|
||||
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
||||
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
|
||||
|
||||
@ -686,6 +689,26 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
private static func closeWindow(_ app: ghostty_app_t, target: ghostty_target_s) {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("close window does nothing with an app target")
|
||||
return
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyCloseWindow,
|
||||
object: surfaceView
|
||||
)
|
||||
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
private static func toggleFullscreen(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
|
@ -248,6 +248,9 @@ extension Notification.Name {
|
||||
/// Close tab
|
||||
static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab")
|
||||
|
||||
/// Close window
|
||||
static let ghosttyCloseWindow = Notification.Name("com.mitchellh.ghostty.closeWindow")
|
||||
|
||||
/// Resize the window to a default size.
|
||||
static let ghosttyResetWindowSize = Notification.Name("com.mitchellh.ghostty.resetWindowSize")
|
||||
}
|
||||
|
@ -201,7 +201,14 @@ extension Ghostty {
|
||||
self.eventMonitor = NSEvent.addLocalMonitorForEvents(
|
||||
matching: [
|
||||
// We need keyUp because command+key events don't trigger keyUp.
|
||||
.keyUp
|
||||
.keyUp,
|
||||
|
||||
// We need leftMouseDown to determine if we should focus ourselves
|
||||
// when the app/window isn't in focus. We do this instead of
|
||||
// "acceptsFirstMouse" because that forces us to also handle the
|
||||
// event and encode the event to the pty which we want to avoid.
|
||||
// (Issue 2595)
|
||||
.leftMouseDown,
|
||||
]
|
||||
) { [weak self] event in self?.localEventHandler(event) }
|
||||
|
||||
@ -450,11 +457,40 @@ extension Ghostty {
|
||||
case .keyUp:
|
||||
localEventKeyUp(event)
|
||||
|
||||
case .leftMouseDown:
|
||||
localEventLeftMouseDown(event)
|
||||
|
||||
default:
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
private func localEventLeftMouseDown(_ event: NSEvent) -> NSEvent? {
|
||||
// We only want to process events that are on this window.
|
||||
guard let window,
|
||||
event.window != nil,
|
||||
window == event.window else { return event }
|
||||
|
||||
// The clicked location in this window should be this view.
|
||||
let location = convert(event.locationInWindow, from: nil)
|
||||
guard hitTest(location) == self else { return event }
|
||||
|
||||
// We only want to grab focus if either our app or window was
|
||||
// not focused.
|
||||
guard !NSApp.isActive || !window.isKeyWindow else { return event }
|
||||
|
||||
// If we're already focused we do nothing
|
||||
guard !focused else { return event }
|
||||
|
||||
// Make ourselves the first responder
|
||||
window.makeFirstResponder(self)
|
||||
|
||||
// We have to keep processing the event so that AppKit can properly
|
||||
// focus the window and dispatch events. If you return nil here then
|
||||
// nobody gets a windowDidBecomeKey event and so on.
|
||||
return event
|
||||
}
|
||||
|
||||
private func localEventKeyUp(_ event: NSEvent) -> NSEvent? {
|
||||
// We only care about events with "command" because all others will
|
||||
// trigger the normal responder chain.
|
||||
@ -620,14 +656,6 @@ extension Ghostty {
|
||||
ghostty_surface_draw(surface);
|
||||
}
|
||||
|
||||
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
|
||||
// "Override this method in a subclass to allow instances to respond to
|
||||
// click-through. This allows the user to click on a view in an inactive
|
||||
// window, activating the view with one click, instead of clicking first
|
||||
// to make the window active and then clicking the view."
|
||||
return true
|
||||
}
|
||||
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
guard let surface = self.surface else { return }
|
||||
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||
@ -884,6 +912,11 @@ extension Ghostty {
|
||||
nil
|
||||
}
|
||||
|
||||
// If we are in a keyDown then we don't need to redispatch a command-modded
|
||||
// key event (see docs for this field) so reset this to nil because
|
||||
// `interpretKeyEvents` may dispach it.
|
||||
self.lastPerformKeyEvent = nil
|
||||
|
||||
self.interpretKeyEvents([translationEvent])
|
||||
|
||||
// If our keyboard changed from this we just assume an input method
|
||||
@ -922,6 +955,34 @@ extension Ghostty {
|
||||
_ = keyAction(GHOSTTY_ACTION_RELEASE, event: event)
|
||||
}
|
||||
|
||||
/// Records the timestamp of the last event to performKeyEquivalent that we need to save.
|
||||
/// We currently save all commands with command or control set.
|
||||
///
|
||||
/// For command+key inputs, the AppKit input stack calls performKeyEquivalent to give us a chance
|
||||
/// to handle them first. If we return "false" then it goes through the standard AppKit responder chain.
|
||||
/// For an NSTextInputClient, that may redirect some commands _before_ our keyDown gets called.
|
||||
/// Concretely: Command+Period will do: performKeyEquivalent, doCommand ("cancel:"). In doCommand,
|
||||
/// we need to know that we actually want to handle that in keyDown, so we send it back through the
|
||||
/// event dispatch system and use this timestamp as an identity to know to actually send it to keyDown.
|
||||
///
|
||||
/// Why not send it to keyDown always? Because if the user rebinds a command to something we
|
||||
/// actually handle then we do want the standard response chain to handle the key input. Unfortunately,
|
||||
/// we can't know what a command is bound to at a system level until we let it flow through the system.
|
||||
/// That's the crux of the problem.
|
||||
///
|
||||
/// So, we have to send it back through if we didn't handle it.
|
||||
///
|
||||
/// The next part of the problem is comparing NSEvent identity seems pretty nasty. I couldn't
|
||||
/// find a good way to do it. I originally stored a weak ref and did identity comparison but that
|
||||
/// doesn't work and for reasons I couldn't figure out the value gets mangled (fields don't match
|
||||
/// before/after the assignment). I suspect it has something to do with the fact an NSEvent is wrapping
|
||||
/// a lower level event pointer and its just not surviving the Swift runtime somehow. I don't know.
|
||||
///
|
||||
/// The best thing I could find was to store the event timestamp which has decent granularity
|
||||
/// and compare that. To further complicate things, some events are synthetic and have a zero
|
||||
/// timestamp so we have to protect against that. Fun!
|
||||
var lastPerformKeyEvent: TimeInterval?
|
||||
|
||||
/// Special case handling for some control keys
|
||||
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
||||
switch (event.type) {
|
||||
@ -975,15 +1036,42 @@ extension Ghostty {
|
||||
|
||||
equivalent = "\r"
|
||||
|
||||
case ".":
|
||||
if (!event.modifierFlags.contains(.command)) {
|
||||
default:
|
||||
// It looks like some part of AppKit sometimes generates synthetic NSEvents
|
||||
// with a zero timestamp. We never process these at this point. Concretely,
|
||||
// this happens for me when pressing Cmd+period with default bindings. This
|
||||
// binds to "cancel" which goes through AppKit to produce a synthetic "escape".
|
||||
//
|
||||
// Question: should we be ignoring all synthetic events? Should we be finding
|
||||
// synthetic escape and ignoring it? I feel like Cmd+period could map to a
|
||||
// escape binding by accident, but it hasn't happened yet...
|
||||
if event.timestamp == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
equivalent = "."
|
||||
// All of this logic here re: lastCommandEvent is to workaround some
|
||||
// nasty behavior. See the docs for lastCommandEvent for more info.
|
||||
|
||||
default:
|
||||
// Ignore other events
|
||||
// Ignore all other non-command events. This lets the event continue
|
||||
// through the AppKit event systems.
|
||||
if (!event.modifierFlags.contains(.command) &&
|
||||
!event.modifierFlags.contains(.control)) {
|
||||
// Reset since we got a non-command event.
|
||||
lastPerformKeyEvent = nil
|
||||
return false
|
||||
}
|
||||
|
||||
// If we have a prior command binding and the timestamp matches exactly
|
||||
// then we pass it through to keyDown for encoding.
|
||||
if let lastPerformKeyEvent {
|
||||
self.lastPerformKeyEvent = nil
|
||||
if lastPerformKeyEvent == event.timestamp {
|
||||
equivalent = event.characters ?? ""
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lastPerformKeyEvent = event.timestamp
|
||||
return false
|
||||
}
|
||||
|
||||
@ -1480,9 +1568,19 @@ extension Ghostty.SurfaceView: NSTextInputClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// This function needs to exist for two reasons:
|
||||
/// 1. Prevents an audible NSBeep for unimplemented actions.
|
||||
/// 2. Allows us to properly encode super+key input events that we don't handle
|
||||
override func doCommand(by selector: Selector) {
|
||||
// This currently just prevents NSBeep from interpretKeyEvents but in the future
|
||||
// we may want to make some of this work.
|
||||
// If we are being processed by performKeyEquivalent with a command binding,
|
||||
// we send it back through the event system so it can be encoded.
|
||||
if let lastPerformKeyEvent,
|
||||
let current = NSApp.currentEvent,
|
||||
lastPerformKeyEvent == current.timestamp
|
||||
{
|
||||
NSApp.sendEvent(current)
|
||||
return
|
||||
}
|
||||
|
||||
print("SEL: \(selector)")
|
||||
}
|
||||
|
@ -57,6 +57,7 @@
|
||||
pandoc,
|
||||
hyperfine,
|
||||
typos,
|
||||
uv,
|
||||
wayland,
|
||||
wayland-scanner,
|
||||
wayland-protocols,
|
||||
@ -109,6 +110,9 @@ in
|
||||
# Localization
|
||||
gettext
|
||||
|
||||
# CI
|
||||
uv
|
||||
|
||||
# We need these GTK-related deps on all platform so we can build
|
||||
# dist tarballs.
|
||||
blueprint-compiler
|
||||
|
@ -39,7 +39,7 @@
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "ghostty";
|
||||
version = "1.1.3";
|
||||
version = "1.1.4";
|
||||
|
||||
# We limit source like this to try and reduce the amount of rebuilds as possible
|
||||
# thus we only provide the source that is needed for the build
|
||||
|
269
po/ca_ES.UTF-8.po
Normal file
269
po/ca_ES.UTF-8.po
Normal file
@ -0,0 +1,269 @@
|
||||
# Catalan translations for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Francesc Arpi <francesc.arpi@gmail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:28-0700\n"
|
||||
"PO-Revision-Date: 2025-03-20 08:07+0100\n"
|
||||
"Last-Translator: Francesc Arpi <francesc.arpi@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: ca\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Canvia el títol del terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Deixa en blanc per restaurar el títol per defecte."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel·la"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "D'acord"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Errors de configuració"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"S'han trobat un o més errors de configuració. Si us plau, revisa els errors a "
|
||||
"continuació i torna a carregar la configuració o ignora aquests errors."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Ignora"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Carrega la configuració"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Copia"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Enganxa"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Neteja"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Reinicia"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Divideix"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Canvia el títol…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Divideix cap amunt"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Divideix cap avall"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Divideix a l'esquerra"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Divideix a la dreta"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Pestanya"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "Nova pestanya"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Tanca la pestanya"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Finestra"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Nova finestra"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Tanca la finestra"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Configuració"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "Obre la configuració"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Inspector de terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "Sobre Ghostty"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "Surt"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Autoritza l'accés al porta-retalls"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Una aplicació està intentant llegir del porta-retalls. El contingut actual "
|
||||
"del porta-retalls es mostra a continuació."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Denegar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "Permet"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Una aplicació està intentant escriure al porta-retalls. El contingut actual "
|
||||
"del porta-retalls es mostra a continuació."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Avís: Enganxament potencialment insegur"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Enganxar aquest text al terminal pot ser perillós, ja que sembla que es "
|
||||
"podrien executar algunes ordres."
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: Inspector de terminal"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Copiat al porta-retalls"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "Tanca"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Surt de Ghostty?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "Tanca la finestra?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "Tanca la pestanya?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Tanca la divisió?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Totes les sessions del terminal es tancaran."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Totes les sessions del terminal en aquesta finestra es tancaran."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Totes les sessions del terminal en aquesta pestanya es tancaran."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "El procés actualment en execució en aquesta divisió es tancarà."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "Menú principal"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Mostra les pestanyes obertes"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr ""
|
||||
"⚠️ Estàs executant una versió de depuració de Ghostty! El rendiment es "
|
||||
"veurà afectat."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "S'ha tornat a carregar la configuració"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Desenvolupadors de Ghostty"
|
267
po/es_BO.UTF-8.po
Normal file
267
po/es_BO.UTF-8.po
Normal file
@ -0,0 +1,267 @@
|
||||
# Spanish translations for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Miguel Peredo <miguelp@quientienemail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:54-0700\n"
|
||||
"PO-Revision-Date: 2025-03-28 17:46+0200\n"
|
||||
"Last-Translator: Miguel Peredo <miguelp@quientienemail.com>\n"
|
||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||
"Language: es_BO\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Cambiar el título de la terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Dejar en blanco para restaurar el título predeterminado."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "Aceptar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Errores de configuración"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Se encontraron uno o más errores de configuración. Por favor revise los errores a continuación, "
|
||||
"y recargue su configuración o ignore estos errores."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Ignorar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Recargar configuración"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Copiar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Pegar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Limpiar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Reiniciar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Dividir"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Cambiar título…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Dividir arriba"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Dividir abajo"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Dividir a la izquierda"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Dividir a la derecha"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Pestaña"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "Nueva pestaña"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Cerrar pestaña"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Ventana"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Nueva ventana"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Cerrar ventana"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Configuración"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "Abrir configuración"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Inspector de la terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "Acerca de Ghostty"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "Salir"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Autorizar acceso al portapapeles"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Una aplicación está intentando leer desde el portapapeles. El contenido "
|
||||
"actual del portapapeles se muestra a continuación."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Denegar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "Permitir"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Una aplicación está intentando escribir en el portapapeles. El contenido "
|
||||
"actual del portapapeles se muestra a continuación."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Advertencia: Pegado potencialmente inseguro"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Pegar este texto en la terminal puede ser peligroso ya que parece que "
|
||||
"algunos comandos podrían ejecutarse."
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: Inspector de la terminal"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Copiado al portapapeles"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "Cerrar"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "¿Salir de Ghostty?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "¿Cerrar ventana?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "¿Cerrar pestaña?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "¿Cerrar división?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Todas las sesiones de terminal serán terminadas."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Todas las sesiones de terminal en esta ventana serán terminadas."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Todas las sesiones de terminal en esta pestaña serán terminadas."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "El proceso actualmente en ejecución en esta división será terminado."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "Menú principal"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Ver pestañas abiertas"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr "⚠️ Está ejecutando una versión de depuración de Ghostty. El rendimiento no será óptimo."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "Configuración recargada"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Desarrolladores de Ghostty"
|
268
po/fr_FR.UTF-8.po
Normal file
268
po/fr_FR.UTF-8.po
Normal file
@ -0,0 +1,268 @@
|
||||
# French translations for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Kirwiisp <swiip__@hotmail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:28-0700\n"
|
||||
"PO-Revision-Date: 2025-03-22 09:31+0100\n"
|
||||
"Last-Translator: Kirwiisp <swiip__@hotmail.com>\n"
|
||||
"Language-Team: French <traduc@traduc.org>\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Changer le nom du terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Laisser vide pour restaurer le titre par défaut."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Erreurs de configuration"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Une ou plusieurs erreurs de configuration ont été trouvées. Veuillez lire les erreurs ci-dessous,"
|
||||
"et recharger votre configuration ou bien ignorer ces erreurs."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Ignorer"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Recharger la configuration"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Copier"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Coller"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Tout effacer"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Réinitialiser"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Créer panneau"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Changer le titre…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Panneau en haut"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Panneau en bas"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Panneau à gauche"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Panneau à droite"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Onglet"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "Nouvel onglet"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Fermer onglet"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Fenêtre"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Nouvelle fenêtre"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Fermer la fenêtre"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Config"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "Ouvrir la configuration"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Inspecteur de terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "À propos de Ghostty"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "Quitter"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Autoriser l'accès au presse-papiers"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Une application essaie de lire depuis le presse-papiers."
|
||||
"Le contenu actuel du presse-papiers est affiché ci-dessous."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Refuser"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "Autoriser"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Une application essaie d'écrire dans le presse-papiers."
|
||||
"Le contenu actuel du presse-papiers est affiché ci-dessous."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Attention: Collage potentiellement dangereux"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Coller ce texte dans le terminal pourrait être dangereux, "
|
||||
"il semblerait que certaines commandes pourraient être exécutées."
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: Inspecteur"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Copié dans le presse-papiers"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "Fermer"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Quitter Ghostty ?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "Fermer la fenêtre ?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "Fermer l'onglet ?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Fermer le panneau ?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Toutes les sessions vont être arrêtées."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Toutes les sessions de cette fenêtre vont être arrêtées."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Toutes les sessions de cet onglet vont être arrêtées."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "Le processus en cours dans ce panneau va être arrêté."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "Menu principal"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Voir les onglets ouverts"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr ""
|
||||
"⚠️ Vous utilisez une version de débogage de Ghostty ! Les performances seront dégradées."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "Recharger la configuration"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Les développeurs de Ghostty"
|
266
po/id_ID.UTF-8.po
Normal file
266
po/id_ID.UTF-8.po
Normal file
@ -0,0 +1,266 @@
|
||||
# Indonesian translations for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Satrio Bayu Aji <halosatrio@gmail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:28-0700\n"
|
||||
"PO-Revision-Date: 2025-03-20 15:19+0700\n"
|
||||
"Last-Translator: Satrio Bayu Aji <halosatrio@gmail.com>\n"
|
||||
"Language-Team: Indonesian <translation-team-id@lists.sourceforge.net>\n"
|
||||
"Language: id\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Ubah judul terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Biarkan kosong untuk mengembalikan judul bawaan."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Batal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Kesalahan konfigurasi"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Ditemukan satu atau lebih kesalahan konfigurasi. Silakan tinjau kesalahan di bawah ini, "
|
||||
"dan muat ulang konfigurasi anda atau abaikan kesalahan ini."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Abaikan"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Muat ulang konfigurasi"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Salin"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Tempel"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Hapus"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Atur ulang"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Belah"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Ubah judul…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Belah atas"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Belah bawah"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Belah kiri"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Belah kanan"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Tab"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "Tab baru"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Tutup tab"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Jendela"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Jendela baru"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Tutup jendela"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Konfigurasi"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "Buka konfigurasi"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Inspektur terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "Tentang Ghostty"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "Keluar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Mengesahkan akses papan klip"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Aplikasi sedang mencoba membaca dari papan klip. Isi papan klip "
|
||||
"saat ini ditampilkan di bawah ini."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Menyangkal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "Izinkan"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Aplikasi sedang mencoba menulis ke papan klip. Isi papan klip "
|
||||
"saat ini ditampilkan di bawah ini."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Peringatan: Tempelan yang berpotensi tidak aman"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Menempelkan teks ini ke terminal mungkin berbahaya karena sepertinya "
|
||||
"beberapa perintah mungkin dijalankan."
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: Inspektur terminal"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Disalin ke papan klip"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "Tutup"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Keluar dari Ghostty?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "Tutup jendela?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "Tutup tab?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Tutup belahan?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Semua sesi terminal akan diakhiri."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Semua sesi terminal di jendela ini akan diakhiri."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Semua sesi terminal di tab ini akan diakhiri."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "Proses yang sedang berjalan dalam belahan ini akan diakhiri."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "Menu utama"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Lihat tab terbuka"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr "⚠️ Anda sedang menjalankan versi debug dari Ghostty! Performa akan menurun."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "Memuat ulang konfigurasi"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Pengembang Ghostty"
|
268
po/ja_JP.UTF-8.po
Normal file
268
po/ja_JP.UTF-8.po
Normal file
@ -0,0 +1,268 @@
|
||||
# Japanese translations for com.mitchellh.ghostty package
|
||||
# com.mitchellh.ghostty パッケージに対する英訳.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Lon Sagisawa <lon@sagisawa.me>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:28-0700\n"
|
||||
"PO-Revision-Date: 2025-03-21 00:08+0900\n"
|
||||
"Last-Translator: Lon Sagisawa <lon@sagisawa.me>\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "ターミナルのタイトルを変更する"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "空白にした場合、デフォルトのタイトルを使用します。"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "設定エラー"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"設定ファイルにエラーがあります。以下のエラーを確認し、"
|
||||
"設定ファイルの再読み込みをするか、無視してください。"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "無視"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "設定ファイルの再読み込み"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "コピー"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "貼り付け"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "クリア"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "リセット"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "タイトルを変更…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "上に分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "下に分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "左に分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "右に分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "タブ"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "新しいタブ"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "タブを閉じる"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "ウィンドウ"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "新しいウィンドウ"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "ウィンドウを閉じる"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "設定"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "設定ファイルを開く"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "端末インスペクター"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "Ghostty について"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "終了"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "クリップボードへのアクセスを承認"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"アプリケーションがクリップボードを読み取ろうとしています。"
|
||||
"現在のクリップボードの内容は以下の通りです。"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "拒否"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "許可"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"アプリケーションがクリップボードに書き込もうとしています。"
|
||||
"現在のクリップボードの内容は以下の通りです。"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "警告: 危険な可能性のあるペースト"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"このテキストには実行可能なコマンドが含まれており、"
|
||||
"ターミナルに貼り付けるのは危険な可能性があります。"
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: 端末インスペクター"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "クリップボードにコピーしました"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "閉じる"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Ghostty を終了しますか?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "ウィンドウを閉じますか?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "タブを閉じますか?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "分割ウィンドウを閉じますか?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "すべてのターミナルセッションが終了されます。"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "ウィンドウ内のすべてのターミナルセッションが終了されます。"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "タブ内のすべてのターミナルセッションが終了されます。"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "分割ウィンドウ内のすべてのターミナルセッションが終了されます。"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "メインメニュー"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "開いているすべてのタブを表示"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr "⚠️ Ghostty のデバッグビルドを実行しています! パフォーマンスが低下しています。"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "設定を再読み込みしました"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Ghostty 開発者"
|
268
po/nl_NL.UTF-8.po
Normal file
268
po/nl_NL.UTF-8.po
Normal file
@ -0,0 +1,268 @@
|
||||
# Dutch translations for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Nico Geesink <geesinknico@gmail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:28-0700\n"
|
||||
"PO-Revision-Date: 2025-03-24 15:00+0100\n"
|
||||
"Last-Translator: Nico Geesink <geesinknico@gmail.com>\n"
|
||||
"Language-Team: Dutch <vertaling@vrijschrift.org>\n"
|
||||
"Language: nl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Titel van de terminal wijzigen"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Laat leeg om de standaard titel te herstellen."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Annuleren"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Configuratiefouten"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Er zijn één of meer configuratiefouten gevonden. Bekijk de onderstaande fouten "
|
||||
"en herlaad je configuratie of negeer deze fouten."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Negeer"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Herlaad configuratie"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Kopiëren"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Plakken"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Leegmaken"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Herstellen"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Splitsen"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Wijzig titel…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Splits naar boven"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Splits naar beneden"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Splits naar links"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Splits naar rechts"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Tabblad"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "Nieuw tabblad"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Sluit tabblad"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Venster"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Nieuw venster"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Sluit venster"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Configuratie"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "Open configuratie"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Terminal inspecteur"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "Over Ghostty"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "Afsluiten"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Verleen toegang tot klembord"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Een applicatie probeert de inhoud van het klembord te lezen. De huidige "
|
||||
"inhoud van het klembord wordt hieronder weergegeven."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Weigeren"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "Toestaan"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Een applicatie probeert de inhoud van het klembord te wijzigen. De huidige "
|
||||
"inhoud van het klembord wordt hieronder weergegeven."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Waarschuwing: mogelijk onveilige plakactie"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Het plakken van deze tekst in de terminal is mogelijk gevaarlijk, omdat "
|
||||
"het lijkt op een commando dat uitgevoerd kan worden."
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: terminal inspecteur"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Gekopieerd naar klembord"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "Afsluiten"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Wil je Ghostty afsluiten?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "Wil je dit venster afsluiten?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "Wil je dit tabblad afsluiten?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Wil je deze splitsing afsluiten?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Alle terminalsessies zullen worden beëindigd."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Alle terminalsessies binnen dit venster zullen worden beëindigd."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Alle terminalsessies binnen dit tabblad zullen worden beëindigd."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "Alle processen die nu draaien in deze splitsing zullen worden beëindigd."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "Hoofdmenu"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Open tabbladen bekijken"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr ""
|
||||
"⚠️ Je draait een debug versie van Ghostty! Prestaties zullen minder zijn dan normaal."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "De configuratie is herladen"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Ghostty ontwikkelaars"
|
269
po/pt_BR.UTF-8.po
Normal file
269
po/pt_BR.UTF-8.po
Normal file
@ -0,0 +1,269 @@
|
||||
# Portuguese translations for com.mitchellh.ghostty package
|
||||
# Traduções em português brasileiro para o pacote com.mitchellh.ghostty.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Gustavo Peres <gsodevel@gmail.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:54-0700\n"
|
||||
"PO-Revision-Date: 2025-03-28 11:04-0300\n"
|
||||
"Last-Translator: Gustavo Peres <gsodevel@gmail.com>\n"
|
||||
"Language-Team: Brazilian Portuguese <ldpbr-"
|
||||
"translation@lists.sourceforge.net>\n"
|
||||
"Language: pt_BR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Mudar título do Terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Deixe em branco para restaurar o título original."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Erros de configuração"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Um ou mais erros de configuração encontrados. Por favor revise os erros abaixo, "
|
||||
"e ou recarregue sua configuração, ou ignore esses erros."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Ignorar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Recarregar configuração"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Copiar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Colar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Limpar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Reiniciar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Dividir"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Mudar título…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Dividir para cima"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Dividir para baixo"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Dividir à esquerda"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Dividir à direita"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Aba"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "Nova aba"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Fechar aba"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Janela"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Nova janela"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Fechar janela"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Configurar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "Abrir configuração"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Inspetor de terminal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "Sobre o Ghostty"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "Sair"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Autorizar acesso à área de transferência"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Uma aplicação está tentando ler da área de transferência. O conteúdo "
|
||||
"atual da área de transferência está sendo exibido abaixo."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Negar"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "Permitir"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Uma aplicação está tentando escrever na área de transferência. O conteúdo "
|
||||
"atual da área de transferência está aparecendo abaixo."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Aviso: Conteúdo potencialmente inseguro"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Colar esse texto em um terminal pode ser perigoso, pois parece que alguns "
|
||||
"comandos podem ser executados."
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: Inspetor de terminal"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Copiado para a área de transferência"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "Fechar"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Fechar Ghostty?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "Fechar janela?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "Fechar aba?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Fechar divisão?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Todas as sessões de terminal serão finalizadas."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Todas as sessões de terminal nessa janela serão finalizadas."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Todas as sessões de terminal nessa aba serão finalizadas."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "O processo atual rodando nessa divisão será finalizado."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "Menu Principal"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Visualizar abas abertas"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr "⚠️ Você está rodando uma build de debug do Ghostty! O desempenho será afetado."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "Configuração recarregada"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Desenvolvedores Ghostty"
|
270
po/ru_RU.UTF-8.po
Normal file
270
po/ru_RU.UTF-8.po
Normal file
@ -0,0 +1,270 @@
|
||||
# Russian translations for com.mitchellh.ghostty package
|
||||
# Русские переводы для пакета com.mitchellh.ghostty.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# blackzeshi <sergey_zhuzhgov@mail.ru>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:28-0700\n"
|
||||
"PO-Revision-Date: 2025-03-24 00:01+0500\n"
|
||||
"Last-Translator: blackzeshi <sergey_zhuzhgov@mail.ru>\n"
|
||||
"Language-Team: Russian <gnu@d07.ru>\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Изменить заголовок терминала"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Оставьте пустым, чтобы восстановить исходный заголовок."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "ОК"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Ошибки конфигурации"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Конфигурация содержит ошибки. Проверьте их ниже, а затем"
|
||||
"либо перезагрузите конфигурацию, либо проигнорируйте ошибки."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Игнорировать"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Обновить конфигурацию"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Копировать"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Вставить"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Очистить"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Сброс"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Сплит"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Изменить заголовок…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Сплит вверх"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Сплит вниз"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Сплит влево"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Сплит вправо"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Вкладка"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "Новая вкладка"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Закрыть вкладку"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Окно"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Новое окно"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Закрыть окно"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Конфигурация"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "Открыть конфигурационный файл"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Инспектор терминала"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "О Ghostty"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "Выход"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Разрешить доступ к буферу обмена"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Приложение пытается прочитать данные из буфера обмена. Эти данные "
|
||||
"отображены ниже."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Отклонить"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "Разрешить"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Приложение пытается записать данные в буфер обмена. Эти данные "
|
||||
"показаны ниже."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Внимание! Вставляемые данные могут нанести вред вашей системе"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Вставка этого текста в терминал может быть опасной. Это выглядит "
|
||||
"как команды, которые могут быть исполнены."
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: инспектор терминала"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Скопировано в буфер обмена"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "Закрыть"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Закрыть Ghostty?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "Закрыть окно?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "Закрыть вкладку?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Закрыть сплит-режим?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Все сессии терминала будут остановлены."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Все сессии терминала в этом окне будут остановлены."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Все сессии терминала в этой вкладке будут остановлены."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "Процесс, работающий в этой сплит-области, будет остановлен."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "Главное меню"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Просмотреть открытые вкладки"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr ""
|
||||
"⚠️ Вы запустили отладочную сборку Ghostty! Это может влиять на производительность."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "Конфигурация была обновлена"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Разработчики Ghostty"
|
270
po/tr_TR.UTF-8.po
Normal file
270
po/tr_TR.UTF-8.po
Normal file
@ -0,0 +1,270 @@
|
||||
# Turkish translations for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Emir SARI <emir_sari@icloud.com>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-03-19 08:54-0700\n"
|
||||
"PO-Revision-Date: 2025-03-24 22:01+0300\n"
|
||||
"Last-Translator: Emir SARI <emir_sari@icloud.com>\n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Language: tr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "Uçbirim Başlığını Değiştir"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "Öntanımlı başlığı geri yüklemek için boş bırakın."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "İptal"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "Tamam"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "Yapılandırma Hataları"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"Bir veya daha fazla yapılandırma hatası bulundu. Lütfen aşağıdaki hataları "
|
||||
"gözden geçirin ve ardından ya yapılandırmanızı yeniden yükleyin ya da bu "
|
||||
"hataları yok sayın."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "Yok Say"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Reload Configuration"
|
||||
msgstr "Yapılandırmayı Yeniden Yükle"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "Kopyala"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "Yapıştır"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "Temizle"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "Sıfırla"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "Böl"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "Başlığı Değiştir…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "Yukarı Doğru Böl"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "Aşağı Doğru Böl"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "Sola Doğru Böl"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "Sağa Doğru Böl"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "Sekme"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:246
|
||||
msgid "New Tab"
|
||||
msgstr "Yeni Sekme"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "Sekmeyi Kapat"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "Pencere"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "Yeni Pencere"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "Pencereyi Kapat"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "Yapılandırma"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Open Configuration"
|
||||
msgstr "Yapılandırmayı Aç"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "Uçbirim Denetçisi"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||
#: src/apprt/gtk/Window.zig:960
|
||||
msgid "About Ghostty"
|
||||
msgstr "Ghostty Hakkında"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
msgid "Quit"
|
||||
msgstr "Çık"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "Pano Erişimine İzin Ver"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Bir uygulama panodan okumaya çalışıyor. Geçerli pano içeriği aşağıda "
|
||||
"gösterilmektedir."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "Reddet"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "İzin Ver"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"Bir uygulama panoya yazmaya çalışıyor. Geçerli pano içeriği aşağıda "
|
||||
"gösterilmektedir."
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "Uyarı: Tehlikeli Olabilecek Yapıştırma"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"Bu metni uçbirime yapıştırmak tehlikeli olabilir; çünkü bir komut "
|
||||
"yürütülebilecekmiş gibi duruyor."
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty: Uçbirim Denetçisi"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1243
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Panoya kopyalandı"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47
|
||||
msgid "Close"
|
||||
msgstr "Kapat"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "Ghostty’den Çık?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "Pencereyi Kapat?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "Sekmeyi Kapat?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "Bölmeyi Kapat?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "Tüm uçbirim oturumları sonlandırılacaktır."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "Bu penceredeki tüm uçbirim oturumları sonlandırılacaktır."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "Bu sekmedeki tüm uçbirim oturumları sonlandırılacaktır."
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "Bu bölmedeki şu anda çalışan süreç sonlandırılacaktır."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:200
|
||||
msgid "Main Menu"
|
||||
msgstr "Ana Menü"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:221
|
||||
msgid "View Open Tabs"
|
||||
msgstr "Açık Sekmeleri Görüntüle"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:295
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr ""
|
||||
"⚠️ Ghostty’nin hata ayıklama amaçlı yapılmış bir sürümünü kullanıyorsunuz! "
|
||||
"Başarım normale göre daha düşük olacaktır."
|
||||
|
||||
#: src/apprt/gtk/Window.zig:725
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "Yapılandırma yeniden yüklendi"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:941
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Ghostty Geliştiricileri"
|
@ -33,14 +33,17 @@ const EnvMap = std.process.EnvMap;
|
||||
|
||||
const PreExecFn = fn (*Command) void;
|
||||
|
||||
/// Path to the command to run. This must be an absolute path. This
|
||||
/// library does not do PATH lookup.
|
||||
path: []const u8,
|
||||
/// Path to the command to run. This doesn't have to be an absolute path,
|
||||
/// because use exec functions that search the PATH, if necessary.
|
||||
///
|
||||
/// This field is null-terminated to avoid a copy for the sake of
|
||||
/// adding a null terminator since POSIX systems are so common.
|
||||
path: [:0]const u8,
|
||||
|
||||
/// Command-line arguments. It is the responsibility of the caller to set
|
||||
/// args[0] to the command. If args is empty then args[0] will automatically
|
||||
/// be set to equal path.
|
||||
args: []const []const u8,
|
||||
args: []const [:0]const u8,
|
||||
|
||||
/// Environment variables for the child process. If this is null, inherits
|
||||
/// the environment variables from this process. These are the exact
|
||||
@ -129,9 +132,8 @@ pub fn start(self: *Command, alloc: Allocator) !void {
|
||||
|
||||
fn startPosix(self: *Command, arena: Allocator) !void {
|
||||
// Null-terminate all our arguments
|
||||
const pathZ = try arena.dupeZ(u8, self.path);
|
||||
const argsZ = try arena.allocSentinel(?[*:0]u8, self.args.len, null);
|
||||
for (self.args, 0..) |arg, i| argsZ[i] = (try arena.dupeZ(u8, arg)).ptr;
|
||||
const argsZ = try arena.allocSentinel(?[*:0]const u8, self.args.len, null);
|
||||
for (self.args, 0..) |arg, i| argsZ[i] = arg.ptr;
|
||||
|
||||
// Determine our env vars
|
||||
const envp = if (self.env) |env_map|
|
||||
@ -184,7 +186,9 @@ fn startPosix(self: *Command, arena: Allocator) !void {
|
||||
if (self.pre_exec) |f| f(self);
|
||||
|
||||
// Finally, replace our process.
|
||||
_ = posix.execveZ(pathZ, argsZ, envp) catch null;
|
||||
// Note: we must use the "p"-variant of exec here because we
|
||||
// do not guarantee our command is looked up already in the path.
|
||||
_ = posix.execvpeZ(self.path, argsZ, envp) catch null;
|
||||
|
||||
// If we are executing this code, the exec failed. In that scenario,
|
||||
// we return a very specific error that can be detected to determine
|
||||
|
@ -518,7 +518,7 @@ pub fn init(
|
||||
};
|
||||
|
||||
// The command we're going to execute
|
||||
const command: ?[]const u8 = if (app.first)
|
||||
const command: ?configpkg.Command = if (app.first)
|
||||
config.@"initial-command" orelse config.command
|
||||
else
|
||||
config.command;
|
||||
@ -650,21 +650,19 @@ pub fn init(
|
||||
// title to the command being executed. This allows window managers
|
||||
// to set custom styling based on the command being executed.
|
||||
const v = command orelse break :xdg;
|
||||
if (v.len > 0) {
|
||||
const title = alloc.dupeZ(u8, v) catch |err| {
|
||||
log.warn(
|
||||
"error copying command for title, title will not be set err={}",
|
||||
.{err},
|
||||
);
|
||||
break :xdg;
|
||||
};
|
||||
defer alloc.free(title);
|
||||
_ = try rt_app.performAction(
|
||||
.{ .surface = self },
|
||||
.set_title,
|
||||
.{ .title = title },
|
||||
const title = v.string(alloc) catch |err| {
|
||||
log.warn(
|
||||
"error copying command for title, title will not be set err={}",
|
||||
.{err},
|
||||
);
|
||||
}
|
||||
break :xdg;
|
||||
};
|
||||
defer alloc.free(title);
|
||||
_ = try rt_app.performAction(
|
||||
.{ .surface = self },
|
||||
.set_title,
|
||||
.{ .title = title },
|
||||
);
|
||||
}
|
||||
|
||||
// We are no longer the first surface
|
||||
|
@ -311,7 +311,7 @@ pub const Action = union(Key) {
|
||||
|
||||
break :cvalue @Type(.{ .@"union" = .{
|
||||
.layout = .@"extern",
|
||||
.tag_type = Key,
|
||||
.tag_type = null,
|
||||
.fields = &union_fields,
|
||||
.decls = &.{},
|
||||
} });
|
||||
@ -323,6 +323,13 @@ pub const Action = union(Key) {
|
||||
value: CValue,
|
||||
};
|
||||
|
||||
comptime {
|
||||
// For ABI compatibility, we expect that this is our union size.
|
||||
// At the time of writing, we don't promise ABI compatibility
|
||||
// so we can change this but I want to be aware of it.
|
||||
assert(@sizeOf(CValue) == 16);
|
||||
}
|
||||
|
||||
/// Returns the value type for the given key.
|
||||
pub fn Value(comptime key: Key) type {
|
||||
inline for (@typeInfo(Action).@"union".fields) |field| {
|
||||
|
@ -636,6 +636,11 @@ pub const Surface = struct {
|
||||
/// The command to run in the new surface. If this is set then
|
||||
/// the "wait-after-command" option is also automatically set to true,
|
||||
/// since this is used for scripting.
|
||||
///
|
||||
/// This command always run in a shell (e.g. via `/bin/sh -c`),
|
||||
/// despite Ghostty allowing directly executed commands via config.
|
||||
/// This is a legacy thing and we should probably change it in the
|
||||
/// future once we have a concrete use case.
|
||||
command: [*:0]const u8 = "",
|
||||
};
|
||||
|
||||
@ -696,7 +701,7 @@ pub const Surface = struct {
|
||||
// If we have a command from the options then we set it.
|
||||
const cmd = std.mem.sliceTo(opts.command, 0);
|
||||
if (cmd.len > 0) {
|
||||
config.command = cmd;
|
||||
config.command = .{ .shell = cmd };
|
||||
config.@"wait-after-command" = true;
|
||||
}
|
||||
|
||||
|
@ -314,8 +314,8 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||
.prefer_dark;
|
||||
},
|
||||
.system => .prefer_light,
|
||||
.dark => .prefer_dark,
|
||||
.light => .force_dark,
|
||||
.dark => .force_dark,
|
||||
.light => .force_light,
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -281,6 +281,15 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
.detail = "is-active",
|
||||
},
|
||||
);
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
self.window,
|
||||
*Window,
|
||||
gtkWindowUpdateScaleFactor,
|
||||
self,
|
||||
.{
|
||||
.detail = "scale-factor",
|
||||
},
|
||||
);
|
||||
|
||||
// If Adwaita is enabled and is older than 1.4.0 we don't have the tab overview and so we
|
||||
// need to stick the headerbar into the content box.
|
||||
@ -473,11 +482,13 @@ pub fn syncAppearance(self: *Window) !void {
|
||||
if (self.isQuickTerminal()) break :visible false;
|
||||
|
||||
// Unconditionally disable the header bar when fullscreened.
|
||||
if (self.config.fullscreen) break :visible false;
|
||||
if (self.window.as(gtk.Window).isFullscreen() != 0)
|
||||
break :visible false;
|
||||
|
||||
// *Conditionally* disable the header bar when maximized,
|
||||
// and gtk-titlebar-hide-when-maximized is set
|
||||
if (self.config.maximize and self.config.gtk_titlebar_hide_when_maximized)
|
||||
if (self.window.as(gtk.Window).isMaximized() != 0 and
|
||||
self.config.gtk_titlebar_hide_when_maximized)
|
||||
break :visible false;
|
||||
|
||||
break :visible self.config.gtk_titlebar;
|
||||
@ -672,7 +683,7 @@ pub fn toggleTabOverview(self: *Window) void {
|
||||
|
||||
/// Toggle the maximized state for this window.
|
||||
pub fn toggleMaximize(self: *Window) void {
|
||||
if (self.config.maximize) {
|
||||
if (self.window.as(gtk.Window).isMaximized() != 0) {
|
||||
self.window.as(gtk.Window).unmaximize();
|
||||
} else {
|
||||
self.window.as(gtk.Window).maximize();
|
||||
@ -683,7 +694,7 @@ pub fn toggleMaximize(self: *Window) void {
|
||||
|
||||
/// Toggle fullscreen for this window.
|
||||
pub fn toggleFullscreen(self: *Window) void {
|
||||
if (self.config.fullscreen) {
|
||||
if (self.window.as(gtk.Window).isFullscreen() != 0) {
|
||||
self.window.as(gtk.Window).unfullscreen();
|
||||
} else {
|
||||
self.window.as(gtk.Window).fullscreen();
|
||||
@ -754,7 +765,6 @@ fn gtkWindowNotifyMaximized(
|
||||
_: *gobject.ParamSpec,
|
||||
self: *Window,
|
||||
) callconv(.c) void {
|
||||
self.config.maximize = self.window.as(gtk.Window).isMaximized() != 0;
|
||||
self.syncAppearance() catch |err| {
|
||||
log.err("failed to sync appearance={}", .{err});
|
||||
};
|
||||
@ -765,7 +775,6 @@ fn gtkWindowNotifyFullscreened(
|
||||
_: *gobject.ParamSpec,
|
||||
self: *Window,
|
||||
) callconv(.c) void {
|
||||
self.config.fullscreen = self.window.as(gtk.Window).isFullscreen() != 0;
|
||||
self.syncAppearance() catch |err| {
|
||||
log.err("failed to sync appearance={}", .{err});
|
||||
};
|
||||
@ -784,6 +793,24 @@ fn gtkWindowNotifyIsActive(
|
||||
}
|
||||
}
|
||||
|
||||
fn gtkWindowUpdateScaleFactor(
|
||||
_: *adw.ApplicationWindow,
|
||||
_: *gobject.ParamSpec,
|
||||
self: *Window,
|
||||
) callconv(.c) void {
|
||||
// On some platforms (namely X11) we need to refresh our appearance when
|
||||
// the scale factor changes. In theory this could be more fine-grained as
|
||||
// a full refresh could be expensive, but a) this *should* be rare, and
|
||||
// b) quite noticeable visual bugs would occur if this is not present.
|
||||
self.winproto.syncAppearance() catch |err| {
|
||||
log.err(
|
||||
"failed to sync appearance after scale factor has been updated={}",
|
||||
.{err},
|
||||
);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
// Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab
|
||||
// sends an undefined value.
|
||||
fn gtkTabNewClick(_: *gtk.Button, self: *Window) callconv(.c) void {
|
||||
|
@ -219,13 +219,12 @@ pub const Window = struct {
|
||||
|
||||
pub fn resizeEvent(self: *Window) !void {
|
||||
// The blur region must update with window resizes
|
||||
const gtk_widget = self.gtk_window.as(gtk.Widget);
|
||||
self.blur_region.width = gtk_widget.getWidth();
|
||||
self.blur_region.height = gtk_widget.getHeight();
|
||||
try self.syncBlur();
|
||||
}
|
||||
|
||||
pub fn syncAppearance(self: *Window) !void {
|
||||
// The user could have toggled between CSDs and SSDs,
|
||||
// therefore we need to recalculate the blur region offset.
|
||||
self.blur_region = blur: {
|
||||
// NOTE(pluiedev): CSDs are a f--king mistake.
|
||||
// Please, GNOME, stop this nonsense of making a window ~30% bigger
|
||||
@ -236,6 +235,11 @@ pub const Window = struct {
|
||||
|
||||
self.gtk_window.as(gtk.Native).getSurfaceTransform(&x, &y);
|
||||
|
||||
// Transform surface coordinates to device coordinates.
|
||||
const scale: f64 = @floatFromInt(self.gtk_window.as(gtk.Widget).getScaleFactor());
|
||||
x *= scale;
|
||||
y *= scale;
|
||||
|
||||
break :blur .{
|
||||
.x = @intFromFloat(x),
|
||||
.y = @intFromFloat(y),
|
||||
@ -265,6 +269,13 @@ pub const Window = struct {
|
||||
// and I think it's not really noticeable enough to justify the effort.
|
||||
// (Wayland also has this visual artifact anyway...)
|
||||
|
||||
const gtk_widget = self.gtk_window.as(gtk.Widget);
|
||||
|
||||
// Transform surface coordinates to device coordinates.
|
||||
const scale = self.gtk_window.as(gtk.Widget).getScaleFactor();
|
||||
self.blur_region.width = gtk_widget.getWidth() * scale;
|
||||
self.blur_region.height = gtk_widget.getHeight() * scale;
|
||||
|
||||
const blur = self.config.background_blur;
|
||||
log.debug("set blur={}, window xid={}, region={}", .{
|
||||
blur,
|
||||
|
@ -19,7 +19,7 @@ const GitVersion = @import("GitVersion.zig");
|
||||
/// TODO: When Zig 0.14 is released, derive this from build.zig.zon directly.
|
||||
/// Until then this MUST match build.zig.zon and should always be the
|
||||
/// _next_ version to release.
|
||||
const app_version: std.SemanticVersion = .{ .major = 1, .minor = 1, .patch = 3 };
|
||||
const app_version: std.SemanticVersion = .{ .major = 1, .minor = 1, .patch = 4 };
|
||||
|
||||
/// Standard build configuration options.
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
|
@ -14,6 +14,7 @@ pub const formatEntry = formatter.formatEntry;
|
||||
|
||||
// Field types
|
||||
pub const ClipboardAccess = Config.ClipboardAccess;
|
||||
pub const Command = Config.Command;
|
||||
pub const ConfirmCloseSurface = Config.ConfirmCloseSurface;
|
||||
pub const CopyOnSelect = Config.CopyOnSelect;
|
||||
pub const CustomShaderAnimation = Config.CustomShaderAnimation;
|
||||
|
@ -22,7 +22,6 @@ const inputpkg = @import("../input.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const cli = @import("../cli.zig");
|
||||
const Command = @import("../Command.zig");
|
||||
|
||||
const conditional = @import("conditional.zig");
|
||||
const Conditional = conditional.Conditional;
|
||||
@ -34,6 +33,7 @@ const KeyValue = @import("key.zig").Value;
|
||||
const ErrorList = @import("ErrorList.zig");
|
||||
const MetricModifier = fontpkg.Metrics.Modifier;
|
||||
const help_strings = @import("help_strings");
|
||||
pub const Command = @import("command.zig").Command;
|
||||
const RepeatableStringMap = @import("RepeatableStringMap.zig");
|
||||
pub const Path = @import("path.zig").Path;
|
||||
pub const RepeatablePath = @import("path.zig").RepeatablePath;
|
||||
@ -691,8 +691,17 @@ palette: Palette = .{},
|
||||
/// * `passwd` entry (user information)
|
||||
///
|
||||
/// This can contain additional arguments to run the command with. If additional
|
||||
/// arguments are provided, the command will be executed using `/bin/sh -c`.
|
||||
/// Ghostty does not do any shell command parsing.
|
||||
/// arguments are provided, the command will be executed using `/bin/sh -c`
|
||||
/// to offload shell argument expansion.
|
||||
///
|
||||
/// To avoid shell expansion altogether, prefix the command with `direct:`,
|
||||
/// e.g. `direct:nvim foo`. This will avoid the roundtrip to `/bin/sh` but will
|
||||
/// also not support any shell parsing such as arguments with spaces, filepaths
|
||||
/// with `~`, globs, etc.
|
||||
///
|
||||
/// You can also explicitly prefix the command with `shell:` to always
|
||||
/// wrap the command in a shell. This can be used to ensure our heuristics
|
||||
/// to choose the right mode are not used in case they are wrong.
|
||||
///
|
||||
/// This command will be used for all new terminal surfaces, i.e. new windows,
|
||||
/// tabs, etc. If you want to run a command only for the first terminal surface
|
||||
@ -702,7 +711,7 @@ palette: Palette = .{},
|
||||
/// arguments. For example, `ghostty -e fish --with --custom --args`.
|
||||
/// This flag sets the `initial-command` configuration, see that for more
|
||||
/// information.
|
||||
command: ?[]const u8 = null,
|
||||
command: ?Command = null,
|
||||
|
||||
/// This is the same as "command", but only applies to the first terminal
|
||||
/// surface created when Ghostty starts. Subsequent terminal surfaces will use
|
||||
@ -718,6 +727,10 @@ command: ?[]const u8 = null,
|
||||
/// fish --with --custom --args`. The `-e` flag automatically forces some
|
||||
/// other behaviors as well:
|
||||
///
|
||||
/// * Disables shell expansion since the input is expected to already
|
||||
/// be shell-expanded by the upstream (e.g. the shell used to type in
|
||||
/// the `ghostty -e` command).
|
||||
///
|
||||
/// * `gtk-single-instance=false` - This ensures that a new instance is
|
||||
/// launched and the CLI args are respected.
|
||||
///
|
||||
@ -735,7 +748,7 @@ command: ?[]const u8 = null,
|
||||
/// name your binary appropriately or source the shell integration script
|
||||
/// manually.
|
||||
///
|
||||
@"initial-command": ?[]const u8 = null,
|
||||
@"initial-command": ?Command = null,
|
||||
|
||||
/// Extra environment variables to pass to commands launched in a terminal
|
||||
/// surface. The format is `env=KEY=VALUE`.
|
||||
@ -826,7 +839,7 @@ env: RepeatableStringMap = .{},
|
||||
link: RepeatableLink = .{},
|
||||
|
||||
/// Enable URL matching. URLs are matched on hover with control (Linux) or
|
||||
/// super (macOS) pressed and open using the default system application for
|
||||
/// command (macOS) pressed and open using the default system application for
|
||||
/// the linked URL.
|
||||
///
|
||||
/// The URL matcher is always lowest priority of any configured links (see
|
||||
@ -2564,21 +2577,17 @@ pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
|
||||
|
||||
// Next, take all remaining args and use that to build up
|
||||
// a command to execute.
|
||||
var command = std.ArrayList(u8).init(arena_alloc);
|
||||
errdefer command.deinit();
|
||||
var builder = std.ArrayList([:0]const u8).init(arena_alloc);
|
||||
errdefer builder.deinit();
|
||||
for (args) |arg_raw| {
|
||||
const arg = std.mem.sliceTo(arg_raw, 0);
|
||||
try self._replay_steps.append(
|
||||
arena_alloc,
|
||||
.{ .arg = try arena_alloc.dupe(u8, arg) },
|
||||
);
|
||||
|
||||
try command.appendSlice(arg);
|
||||
try command.append(' ');
|
||||
const copy = try arena_alloc.dupeZ(u8, arg);
|
||||
try self._replay_steps.append(arena_alloc, .{ .arg = copy });
|
||||
try builder.append(copy);
|
||||
}
|
||||
|
||||
self.@"_xdg-terminal-exec" = true;
|
||||
self.@"initial-command" = command.items[0 .. command.items.len - 1];
|
||||
self.@"initial-command" = .{ .direct = try builder.toOwnedSlice() };
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -3023,7 +3032,7 @@ pub fn finalize(self: *Config) !void {
|
||||
// We don't do this in flatpak because SHELL in Flatpak is always
|
||||
// set to /bin/sh.
|
||||
if (self.command) |cmd|
|
||||
log.info("shell src=config value={s}", .{cmd})
|
||||
log.info("shell src=config value={}", .{cmd})
|
||||
else shell_env: {
|
||||
// Flatpak always gets its shell from outside the sandbox
|
||||
if (internal_os.isFlatpak()) break :shell_env;
|
||||
@ -3035,7 +3044,9 @@ pub fn finalize(self: *Config) !void {
|
||||
|
||||
if (std.process.getEnvVarOwned(alloc, "SHELL")) |value| {
|
||||
log.info("default shell source=env value={s}", .{value});
|
||||
self.command = value;
|
||||
|
||||
const copy = try alloc.dupeZ(u8, value);
|
||||
self.command = .{ .shell = copy };
|
||||
|
||||
// If we don't need the working directory, then we can exit now.
|
||||
if (!wd_home) break :command;
|
||||
@ -3046,7 +3057,7 @@ pub fn finalize(self: *Config) !void {
|
||||
.windows => {
|
||||
if (self.command == null) {
|
||||
log.warn("no default shell found, will default to using cmd", .{});
|
||||
self.command = "cmd.exe";
|
||||
self.command = .{ .shell = "cmd.exe" };
|
||||
}
|
||||
|
||||
if (wd_home) {
|
||||
@ -3063,7 +3074,7 @@ pub fn finalize(self: *Config) !void {
|
||||
if (self.command == null) {
|
||||
if (pw.shell) |sh| {
|
||||
log.info("default shell src=passwd value={s}", .{sh});
|
||||
self.command = sh;
|
||||
self.command = .{ .shell = sh };
|
||||
}
|
||||
}
|
||||
|
||||
@ -3145,13 +3156,13 @@ pub fn parseManuallyHook(
|
||||
|
||||
// Build up the command. We don't clean this up because we take
|
||||
// ownership in our allocator.
|
||||
var command = std.ArrayList(u8).init(alloc);
|
||||
var command: std.ArrayList([:0]const u8) = .init(alloc);
|
||||
errdefer command.deinit();
|
||||
|
||||
while (iter.next()) |param| {
|
||||
try self._replay_steps.append(alloc, .{ .arg = try alloc.dupe(u8, param) });
|
||||
try command.appendSlice(param);
|
||||
try command.append(' ');
|
||||
const copy = try alloc.dupeZ(u8, param);
|
||||
try self._replay_steps.append(alloc, .{ .arg = copy });
|
||||
try command.append(copy);
|
||||
}
|
||||
|
||||
if (command.items.len == 0) {
|
||||
@ -3167,9 +3178,8 @@ pub fn parseManuallyHook(
|
||||
return false;
|
||||
}
|
||||
|
||||
self.@"initial-command" = command.items[0 .. command.items.len - 1];
|
||||
|
||||
// See "command" docs for the implied configurations and why.
|
||||
self.@"initial-command" = .{ .direct = command.items };
|
||||
self.@"gtk-single-instance" = .false;
|
||||
self.@"quit-after-last-window-closed" = true;
|
||||
self.@"quit-after-last-window-closed-delay" = null;
|
||||
@ -3184,7 +3194,7 @@ pub fn parseManuallyHook(
|
||||
// Keep track of our input args for replay
|
||||
try self._replay_steps.append(
|
||||
alloc,
|
||||
.{ .arg = try alloc.dupe(u8, arg) },
|
||||
.{ .arg = try alloc.dupeZ(u8, arg) },
|
||||
);
|
||||
|
||||
// If we didn't find a special case, continue parsing normally
|
||||
@ -3377,6 +3387,16 @@ fn equalField(comptime T: type, old: T, new: T) bool {
|
||||
[:0]const u8,
|
||||
=> return std.mem.eql(u8, old, new),
|
||||
|
||||
[]const [:0]const u8,
|
||||
=> {
|
||||
if (old.len != new.len) return false;
|
||||
for (old, new) |a, b| {
|
||||
if (!std.mem.eql(u8, a, b)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -3412,6 +3432,8 @@ fn equalField(comptime T: type, old: T, new: T) bool {
|
||||
},
|
||||
|
||||
.@"union" => |info| {
|
||||
if (@hasDecl(T, "equal")) return old.equal(new);
|
||||
|
||||
const tag_type = info.tag_type.?;
|
||||
const old_tag = std.meta.activeTag(old);
|
||||
const new_tag = std.meta.activeTag(new);
|
||||
@ -3441,7 +3463,7 @@ fn equalField(comptime T: type, old: T, new: T) bool {
|
||||
const Replay = struct {
|
||||
const Step = union(enum) {
|
||||
/// An argument to parse as if it came from the CLI or file.
|
||||
arg: []const u8,
|
||||
arg: [:0]const u8,
|
||||
|
||||
/// A base path to expand relative paths against.
|
||||
expand: []const u8,
|
||||
@ -3481,7 +3503,7 @@ const Replay = struct {
|
||||
return switch (self) {
|
||||
.@"-e" => self,
|
||||
.diagnostic => |v| .{ .diagnostic = try v.clone(alloc) },
|
||||
.arg => |v| .{ .arg = try alloc.dupe(u8, v) },
|
||||
.arg => |v| .{ .arg = try alloc.dupeZ(u8, v) },
|
||||
.expand => |v| .{ .expand = try alloc.dupe(u8, v) },
|
||||
.conditional_arg => |v| conditional: {
|
||||
var conds = try alloc.alloc(Conditional, v.conditions.len);
|
||||
@ -6620,7 +6642,11 @@ test "parse e: command only" {
|
||||
|
||||
var it: TestIterator = .{ .data = &.{"foo"} };
|
||||
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
||||
try testing.expectEqualStrings("foo", cfg.@"initial-command".?);
|
||||
|
||||
const cmd = cfg.@"initial-command".?;
|
||||
try testing.expect(cmd == .direct);
|
||||
try testing.expectEqual(cmd.direct.len, 1);
|
||||
try testing.expectEqualStrings(cmd.direct[0], "foo");
|
||||
}
|
||||
|
||||
test "parse e: command and args" {
|
||||
@ -6631,7 +6657,13 @@ test "parse e: command and args" {
|
||||
|
||||
var it: TestIterator = .{ .data = &.{ "echo", "foo", "bar baz" } };
|
||||
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
||||
try testing.expectEqualStrings("echo foo bar baz", cfg.@"initial-command".?);
|
||||
|
||||
const cmd = cfg.@"initial-command".?;
|
||||
try testing.expect(cmd == .direct);
|
||||
try testing.expectEqual(cmd.direct.len, 3);
|
||||
try testing.expectEqualStrings(cmd.direct[0], "echo");
|
||||
try testing.expectEqualStrings(cmd.direct[1], "foo");
|
||||
try testing.expectEqualStrings(cmd.direct[2], "bar baz");
|
||||
}
|
||||
|
||||
test "clone default" {
|
||||
|
322
src/config/command.zig
Normal file
322
src/config/command.zig
Normal file
@ -0,0 +1,322 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const formatterpkg = @import("formatter.zig");
|
||||
|
||||
/// A command to execute (argv0 and args).
|
||||
///
|
||||
/// A command is specified as a simple string such as "nvim a b c".
|
||||
/// By default, we expect the downstream to do some sort of shell expansion
|
||||
/// on this string.
|
||||
///
|
||||
/// If a command is already expanded and the user does NOT want to do
|
||||
/// shell expansion (because this usually requires a round trip into
|
||||
/// /bin/sh or equivalent), specify a `direct:`-prefix. e.g.
|
||||
/// `direct:nvim a b c`.
|
||||
///
|
||||
/// The whitespace before or around the prefix is ignored. For example,
|
||||
/// ` direct:nvim a b c` and `direct: nvim a b c` are equivalent.
|
||||
///
|
||||
/// If the command is not absolute, it'll be looked up via the PATH.
|
||||
/// For the shell-expansion case, we let the shell do this. For the
|
||||
/// direct case, we do this directly.
|
||||
pub const Command = union(enum) {
|
||||
const Self = @This();
|
||||
|
||||
/// Execute a command directly, e.g. via `exec`. The format here
|
||||
/// is already structured to be ready to passed directly to `exec`
|
||||
/// with index zero being the command to execute.
|
||||
///
|
||||
/// Index zero is not guaranteed to be an absolute path, and may require
|
||||
/// PATH lookup. It is up to the downstream to do this, usually via
|
||||
/// delegation to something like `execvp`.
|
||||
direct: []const [:0]const u8,
|
||||
|
||||
/// Execute a command via shell expansion. This provides the command
|
||||
/// as a single string that is expected to be expanded in some way
|
||||
/// (up to the downstream). Usually `/bin/sh -c`.
|
||||
shell: [:0]const u8,
|
||||
|
||||
pub fn parseCLI(
|
||||
self: *Self,
|
||||
alloc: Allocator,
|
||||
input_: ?[]const u8,
|
||||
) !void {
|
||||
// Input is required. Whitespace on the edges isn't needed.
|
||||
// Commands must be non-empty.
|
||||
const input = input_ orelse return error.ValueRequired;
|
||||
const trimmed = std.mem.trim(u8, input, " ");
|
||||
if (trimmed.len == 0) return error.ValueRequired;
|
||||
|
||||
// If we have a `:` then we MIGHT have a prefix to specify what
|
||||
// tag we should use.
|
||||
const tag: std.meta.Tag(Self), const str: []const u8 = tag: {
|
||||
if (std.mem.indexOfScalar(u8, trimmed, ':')) |idx| {
|
||||
const prefix = trimmed[0..idx];
|
||||
if (std.mem.eql(u8, prefix, "direct")) {
|
||||
break :tag .{ .direct, trimmed[idx + 1 ..] };
|
||||
} else if (std.mem.eql(u8, prefix, "shell")) {
|
||||
break :tag .{ .shell, trimmed[idx + 1 ..] };
|
||||
}
|
||||
}
|
||||
|
||||
break :tag .{ .shell, trimmed };
|
||||
};
|
||||
|
||||
switch (tag) {
|
||||
.shell => {
|
||||
// We have a shell command, so we can just dupe it.
|
||||
const copy = try alloc.dupeZ(u8, std.mem.trim(u8, str, " "));
|
||||
self.* = .{ .shell = copy };
|
||||
},
|
||||
|
||||
.direct => {
|
||||
// We're not shell expanding, so the arguments are naively
|
||||
// split on spaces.
|
||||
var builder: std.ArrayListUnmanaged([:0]const u8) = .empty;
|
||||
var args = std.mem.splitScalar(
|
||||
u8,
|
||||
std.mem.trim(u8, str, " "),
|
||||
' ',
|
||||
);
|
||||
while (args.next()) |arg| {
|
||||
const copy = try alloc.dupeZ(u8, arg);
|
||||
try builder.append(alloc, copy);
|
||||
}
|
||||
|
||||
self.* = .{ .direct = try builder.toOwnedSlice(alloc) };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a command as a single string, joining arguments as
|
||||
/// necessary with spaces. Its not guaranteed that this is a valid
|
||||
/// command; it is only meant to be human readable.
|
||||
pub fn string(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
) Allocator.Error![:0]const u8 {
|
||||
return switch (self.*) {
|
||||
.shell => |v| try alloc.dupeZ(u8, v),
|
||||
.direct => |v| try std.mem.joinZ(alloc, " ", v),
|
||||
};
|
||||
}
|
||||
|
||||
/// Get an iterator over the arguments array. This may allocate
|
||||
/// depending on the active tag of the command.
|
||||
///
|
||||
/// For direct commands, this is very cheap and just iterates over
|
||||
/// the array. There is no allocation.
|
||||
///
|
||||
/// For shell commands, this will use Zig's ArgIteratorGeneral as
|
||||
/// a best effort shell string parser. This is not guaranteed to be
|
||||
/// 100% accurate, but it works for common cases. This requires allocation.
|
||||
pub fn argIterator(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
) Allocator.Error!ArgIterator {
|
||||
return switch (self.*) {
|
||||
.direct => |v| .{ .direct = .{ .args = v } },
|
||||
.shell => |v| .{ .shell = try .init(alloc, v) },
|
||||
};
|
||||
}
|
||||
|
||||
/// Iterates over each argument in the command.
|
||||
pub const ArgIterator = union(enum) {
|
||||
shell: std.process.ArgIteratorGeneral(.{}),
|
||||
direct: struct {
|
||||
i: usize = 0,
|
||||
args: []const [:0]const u8,
|
||||
},
|
||||
|
||||
/// Return the next argument. This may or may not be a copy
|
||||
/// depending on the active tag. If you want to ensure that every
|
||||
/// argument is a copy, use the `clone` method first.
|
||||
pub fn next(self: *ArgIterator) ?[:0]const u8 {
|
||||
return switch (self.*) {
|
||||
.shell => |*v| v.next(),
|
||||
.direct => |*v| {
|
||||
if (v.i >= v.args.len) return null;
|
||||
defer v.i += 1;
|
||||
return v.args[v.i];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ArgIterator) void {
|
||||
switch (self.*) {
|
||||
.shell => |*v| v.deinit(),
|
||||
.direct => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn clone(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
) Allocator.Error!Self {
|
||||
return switch (self.*) {
|
||||
.shell => |v| .{ .shell = try alloc.dupeZ(u8, v) },
|
||||
.direct => |v| direct: {
|
||||
const copy = try alloc.alloc([:0]const u8, v.len);
|
||||
for (v, 0..) |arg, i| copy[i] = try alloc.dupeZ(u8, arg);
|
||||
break :direct .{ .direct = copy };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
switch (self) {
|
||||
.shell => |v| try formatter.formatEntry([]const u8, v),
|
||||
|
||||
.direct => |v| {
|
||||
var buf: [4096]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
const writer = fbs.writer();
|
||||
writer.writeAll("direct:") catch return error.OutOfMemory;
|
||||
for (v) |arg| {
|
||||
writer.writeAll(arg) catch return error.OutOfMemory;
|
||||
writer.writeByte(' ') catch return error.OutOfMemory;
|
||||
}
|
||||
|
||||
const written = fbs.getWritten();
|
||||
try formatter.formatEntry(
|
||||
[]const u8,
|
||||
written[0..@intCast(written.len - 1)],
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test "Command: parseCLI errors" {
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var v: Self = undefined;
|
||||
try testing.expectError(error.ValueRequired, v.parseCLI(alloc, null));
|
||||
try testing.expectError(error.ValueRequired, v.parseCLI(alloc, ""));
|
||||
try testing.expectError(error.ValueRequired, v.parseCLI(alloc, " "));
|
||||
}
|
||||
|
||||
test "Command: parseCLI shell expanded" {
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var v: Self = undefined;
|
||||
try v.parseCLI(alloc, "echo hello");
|
||||
try testing.expect(v == .shell);
|
||||
try testing.expectEqualStrings(v.shell, "echo hello");
|
||||
|
||||
// Spaces are stripped
|
||||
try v.parseCLI(alloc, " echo hello ");
|
||||
try testing.expect(v == .shell);
|
||||
try testing.expectEqualStrings(v.shell, "echo hello");
|
||||
}
|
||||
|
||||
test "Command: parseCLI direct" {
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var v: Self = undefined;
|
||||
try v.parseCLI(alloc, "direct:echo hello");
|
||||
try testing.expect(v == .direct);
|
||||
try testing.expectEqual(v.direct.len, 2);
|
||||
try testing.expectEqualStrings(v.direct[0], "echo");
|
||||
try testing.expectEqualStrings(v.direct[1], "hello");
|
||||
|
||||
// Spaces around the prefix
|
||||
try v.parseCLI(alloc, " direct: echo hello");
|
||||
try testing.expect(v == .direct);
|
||||
try testing.expectEqual(v.direct.len, 2);
|
||||
try testing.expectEqualStrings(v.direct[0], "echo");
|
||||
try testing.expectEqualStrings(v.direct[1], "hello");
|
||||
}
|
||||
|
||||
test "Command: argIterator shell" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var v: Self = .{ .shell = "echo hello world" };
|
||||
var it = try v.argIterator(alloc);
|
||||
defer it.deinit();
|
||||
|
||||
try testing.expectEqualStrings(it.next().?, "echo");
|
||||
try testing.expectEqualStrings(it.next().?, "hello");
|
||||
try testing.expectEqualStrings(it.next().?, "world");
|
||||
try testing.expect(it.next() == null);
|
||||
}
|
||||
|
||||
test "Command: argIterator direct" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var v: Self = .{ .direct = &.{ "echo", "hello world" } };
|
||||
var it = try v.argIterator(alloc);
|
||||
defer it.deinit();
|
||||
|
||||
try testing.expectEqualStrings(it.next().?, "echo");
|
||||
try testing.expectEqualStrings(it.next().?, "hello world");
|
||||
try testing.expect(it.next() == null);
|
||||
}
|
||||
|
||||
test "Command: string shell" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var v: Self = .{ .shell = "echo hello world" };
|
||||
const str = try v.string(alloc);
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str, "echo hello world");
|
||||
}
|
||||
|
||||
test "Command: string direct" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var v: Self = .{ .direct = &.{ "echo", "hello world" } };
|
||||
const str = try v.string(alloc);
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str, "echo hello world");
|
||||
}
|
||||
|
||||
test "Command: formatConfig shell" {
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
var v: Self = undefined;
|
||||
try v.parseCLI(alloc, "echo hello");
|
||||
try v.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = echo hello\n", buf.items);
|
||||
}
|
||||
|
||||
test "Command: formatConfig direct" {
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
var v: Self = undefined;
|
||||
try v.parseCLI(alloc, "direct: echo hello");
|
||||
try v.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = direct:echo hello\n", buf.items);
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
_ = Command;
|
||||
}
|
@ -23,13 +23,28 @@ const log = std.log.scoped(.i18n);
|
||||
///
|
||||
/// 3. Most preferred locale for a language without a country code.
|
||||
///
|
||||
/// Note for "most common" locales, this is subjective and based on
|
||||
/// the perceived userbase of Ghostty, which may not be representative
|
||||
/// of general populations or global language distribution. Also note
|
||||
/// that ordering may be weird when we first merge a new locale since
|
||||
/// we don't have a good way to determine this. We can always reorder
|
||||
/// with some data.
|
||||
pub const locales = [_][:0]const u8{
|
||||
"de_DE.UTF-8",
|
||||
"zh_CN.UTF-8",
|
||||
"de_DE.UTF-8",
|
||||
"fr_FR.UTF-8",
|
||||
"ja_JP.UTF-8",
|
||||
"nl_NL.UTF-8",
|
||||
"nb_NO.UTF-8",
|
||||
"ru_RU.UTF-8",
|
||||
"uk_UA.UTF-8",
|
||||
"pl_PL.UTF-8",
|
||||
"mk_MK.UTF-8",
|
||||
"tr_TR.UTF-8",
|
||||
"id_ID.UTF-8",
|
||||
"es_BO.UTF-8",
|
||||
"pt_BR.UTF-8",
|
||||
"ca_ES.UTF-8",
|
||||
};
|
||||
|
||||
/// Set for faster membership lookup of locales.
|
||||
@ -108,6 +123,9 @@ pub fn canonicalizeLocale(
|
||||
buf: []u8,
|
||||
locale: []const u8,
|
||||
) error{NoSpaceLeft}![:0]const u8 {
|
||||
// Fix zh locales for macOS
|
||||
if (fixZhLocale(locale)) |fixed| return fixed;
|
||||
|
||||
// Buffer must be 16 or at least as long as the locale and null term
|
||||
if (buf.len < @max(16, locale.len + 1)) return error.NoSpaceLeft;
|
||||
|
||||
@ -126,6 +144,30 @@ pub fn canonicalizeLocale(
|
||||
return buf[0..slice.len :0];
|
||||
}
|
||||
|
||||
/// Handles some zh locales canonicalization because internal libintl
|
||||
/// canonicalization function doesn't handle correctly in these cases.
|
||||
fn fixZhLocale(locale: []const u8) ?[:0]const u8 {
|
||||
var it = std.mem.splitScalar(u8, locale, '-');
|
||||
const name = it.next() orelse return null;
|
||||
if (!std.mem.eql(u8, name, "zh")) return null;
|
||||
|
||||
const script = it.next() orelse return null;
|
||||
const region = it.next() orelse return null;
|
||||
|
||||
if (std.mem.eql(u8, script, "Hans")) {
|
||||
if (std.mem.eql(u8, region, "SG")) return "zh_SG";
|
||||
return "zh_CN";
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, script, "Hant")) {
|
||||
if (std.mem.eql(u8, region, "MO")) return "zh_MO";
|
||||
if (std.mem.eql(u8, region, "HK")) return "zh_HK";
|
||||
return "zh_TW";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// This can be called at any point a compile-time-known locale is
|
||||
/// available. This will use comptime to verify the locale is supported.
|
||||
pub fn staticLocale(comptime v: [*:0]const u8) [*:0]const u8 {
|
||||
@ -160,6 +202,12 @@ test "canonicalizeLocale darwin" {
|
||||
try testing.expectEqualStrings("zh_CN", try canonicalizeLocale(&buf, "zh-Hans"));
|
||||
try testing.expectEqualStrings("zh_TW", try canonicalizeLocale(&buf, "zh-Hant"));
|
||||
|
||||
try testing.expectEqualStrings("zh_CN", try canonicalizeLocale(&buf, "zh-Hans-CN"));
|
||||
try testing.expectEqualStrings("zh_SG", try canonicalizeLocale(&buf, "zh-Hans-SG"));
|
||||
try testing.expectEqualStrings("zh_TW", try canonicalizeLocale(&buf, "zh-Hant-TW"));
|
||||
try testing.expectEqualStrings("zh_HK", try canonicalizeLocale(&buf, "zh-Hant-HK"));
|
||||
try testing.expectEqualStrings("zh_MO", try canonicalizeLocale(&buf, "zh-Hant-MO"));
|
||||
|
||||
// This is just an edge case I want to make sure we're aware of:
|
||||
// canonicalizeLocale does not handle encodings and will turn them into
|
||||
// underscores. We should parse them out before calling this function.
|
||||
|
@ -25,9 +25,9 @@ const c = if (builtin.os.tag != .windows) @cImport({
|
||||
// Entry that is retrieved from the passwd API. This only contains the fields
|
||||
// we care about.
|
||||
pub const Entry = struct {
|
||||
shell: ?[]const u8 = null,
|
||||
home: ?[]const u8 = null,
|
||||
name: ?[]const u8 = null,
|
||||
shell: ?[:0]const u8 = null,
|
||||
home: ?[:0]const u8 = null,
|
||||
name: ?[:0]const u8 = null,
|
||||
};
|
||||
|
||||
/// Get the passwd entry for the currently executing user.
|
||||
@ -117,30 +117,27 @@ pub fn get(alloc: Allocator) !Entry {
|
||||
|
||||
// Shell and home are the last two entries
|
||||
var it = std.mem.splitBackwardsScalar(u8, std.mem.trimRight(u8, output, " \r\n"), ':');
|
||||
result.shell = it.next() orelse null;
|
||||
result.home = it.next() orelse null;
|
||||
result.shell = if (it.next()) |v| try alloc.dupeZ(u8, v) else null;
|
||||
result.home = if (it.next()) |v| try alloc.dupeZ(u8, v) else null;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (pw.pw_shell) |ptr| {
|
||||
const source = std.mem.sliceTo(ptr, 0);
|
||||
const sh = try alloc.alloc(u8, source.len);
|
||||
@memcpy(sh, source);
|
||||
result.shell = sh;
|
||||
const value = try alloc.dupeZ(u8, source);
|
||||
result.shell = value;
|
||||
}
|
||||
|
||||
if (pw.pw_dir) |ptr| {
|
||||
const source = std.mem.sliceTo(ptr, 0);
|
||||
const dir = try alloc.alloc(u8, source.len);
|
||||
@memcpy(dir, source);
|
||||
result.home = dir;
|
||||
const value = try alloc.dupeZ(u8, source);
|
||||
result.home = value;
|
||||
}
|
||||
|
||||
if (pw.pw_name) |ptr| {
|
||||
const source = std.mem.sliceTo(ptr, 0);
|
||||
const name = try alloc.alloc(u8, source.len);
|
||||
@memcpy(name, source);
|
||||
result.name = name;
|
||||
const value = try alloc.dupeZ(u8, source);
|
||||
result.name = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -23,6 +23,8 @@ pub fn ShellEscapeWriter(comptime T: type) type {
|
||||
'?',
|
||||
' ',
|
||||
'|',
|
||||
'(',
|
||||
')',
|
||||
=> &[_]u8{ '\\', byte },
|
||||
else => &[_]u8{byte},
|
||||
};
|
||||
@ -93,3 +95,12 @@ test "shell escape 6" {
|
||||
try writer.writeAll("a\"c");
|
||||
try testing.expectEqualStrings("a\\\"c", fmt.getWritten());
|
||||
}
|
||||
|
||||
test "shell escape 7" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var fmt = std.io.fixedBufferStream(&buf);
|
||||
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
|
||||
const writer = shell.writer();
|
||||
try writer.writeAll("a(1)");
|
||||
try testing.expectEqualStrings("a\\(1\\)", fmt.getWritten());
|
||||
}
|
||||
|
@ -425,11 +425,19 @@ vertex CellTextVertexOut cell_text_vertex(
|
||||
// If we're constrained then we need to scale the glyph.
|
||||
if (in.mode == MODE_TEXT_CONSTRAINED) {
|
||||
float max_width = uniforms.cell_size.x * in.constraint_width;
|
||||
// If this glyph is wider than the constraint width,
|
||||
// fit it to the width and remove its horizontal offset.
|
||||
if (size.x > max_width) {
|
||||
float new_y = size.y * (max_width / size.x);
|
||||
offset.y += (size.y - new_y) / 2;
|
||||
offset.x = 0;
|
||||
size.y = new_y;
|
||||
size.x = max_width;
|
||||
} else if (max_width - size.x > offset.x) {
|
||||
// However, if it does fit in the constraint width, make
|
||||
// sure the offset is small enough to not push it over the
|
||||
// right edge of the constraint width.
|
||||
offset.x = max_width - size.x;
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,7 +507,11 @@ fragment float4 cell_text_fragment(
|
||||
constexpr sampler textureSampler(
|
||||
coord::pixel,
|
||||
address::clamp_to_edge,
|
||||
filter::nearest
|
||||
// TODO(qwerasd): This can be changed back to filter::nearest when
|
||||
// we move the constraint logic out of the GPU code
|
||||
// which should once again guarantee pixel perfect
|
||||
// sizing.
|
||||
filter::linear
|
||||
);
|
||||
|
||||
switch (in.mode) {
|
||||
|
@ -70,7 +70,7 @@ if [ -n "$GHOSTTY_BASH_INJECT" ]; then
|
||||
fi
|
||||
|
||||
# Sudo
|
||||
if [[ "$GHOSTTY_SHELL_INTEGRATION_NO_SUDO" != "1" && -n "$TERMINFO" ]]; then
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" == *"sudo"* && -n "$TERMINFO" ]]; then
|
||||
# Wrap `sudo` command to ensure Ghostty terminfo is preserved.
|
||||
#
|
||||
# This approach supports wrapping a `sudo` alias, but the alias definition
|
||||
@ -124,13 +124,13 @@ function __ghostty_precmd() {
|
||||
fi
|
||||
|
||||
# Cursor
|
||||
if test "$GHOSTTY_SHELL_INTEGRATION_NO_CURSOR" != "1"; then
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
|
||||
PS1=$PS1'\[\e[5 q\]'
|
||||
PS0=$PS0'\[\e[0 q\]'
|
||||
fi
|
||||
|
||||
# Title (working directory)
|
||||
if [[ "$GHOSTTY_SHELL_INTEGRATION_NO_TITLE" != 1 ]]; then
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
|
||||
PS1=$PS1'\[\e]2;\w\a\]'
|
||||
fi
|
||||
fi
|
||||
@ -161,7 +161,7 @@ function __ghostty_preexec() {
|
||||
PS2="$_GHOSTTY_SAVE_PS2"
|
||||
|
||||
# Title (current command)
|
||||
if [[ -n $cmd && "$GHOSTTY_SHELL_INTEGRATION_NO_TITLE" != 1 ]]; then
|
||||
if [[ -n $cmd && "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
|
||||
builtin printf "\e]2;%s\a" "${cmd//[[:cntrl:]]}"
|
||||
fi
|
||||
|
||||
|
@ -36,6 +36,8 @@
|
||||
}
|
||||
|
||||
{
|
||||
use str
|
||||
|
||||
# helper used by `mark-*` functions
|
||||
fn set-prompt-state {|new| set-env __ghostty_prompt_state $new }
|
||||
|
||||
@ -73,7 +75,8 @@
|
||||
}
|
||||
|
||||
fn report-pwd {
|
||||
printf "\e]7;file://%s%s\a" (hostname) (pwd)
|
||||
use platform
|
||||
printf "\e]7;kitty-shell-cwd://%s%s\a" (platform:hostname) $pwd
|
||||
}
|
||||
|
||||
fn sudo-with-terminfo {|@args|
|
||||
@ -104,20 +107,18 @@
|
||||
set edit:after-readline = (conj $edit:after-readline $mark-output-start~)
|
||||
set edit:after-command = (conj $edit:after-command $mark-output-end~)
|
||||
|
||||
var no-title = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_TITLE)
|
||||
var no-cursor = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_CURSOR)
|
||||
var no-sudo = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_SUDO)
|
||||
var features = [(str:split ',' $E:GHOSTTY_SHELL_FEATURES)]
|
||||
|
||||
if (not $no-title) {
|
||||
if (has-value $features title) {
|
||||
set after-chdir = (conj $after-chdir {|_| report-pwd })
|
||||
}
|
||||
if (not $no-cursor) {
|
||||
if (has-value $features cursor) {
|
||||
fn beam { printf "\e[5 q" }
|
||||
fn block { printf "\e[0 q" }
|
||||
set edit:before-readline = (conj $edit:before-readline $beam~)
|
||||
set edit:after-readline = (conj $edit:after-readline {|_| block })
|
||||
}
|
||||
if (and (not $no-sudo) (not-eq "" $E:TERMINFO) (has-external sudo)) {
|
||||
if (and (has-value $features sudo) (not-eq "" $E:TERMINFO) (has-external sudo)) {
|
||||
edit:add-var sudo~ $sudo-with-terminfo~
|
||||
}
|
||||
}
|
||||
|
@ -49,10 +49,9 @@ status --is-interactive || ghostty_exit
|
||||
function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
|
||||
functions -e __ghostty_setup
|
||||
|
||||
# Check if we are setting cursors
|
||||
set --local no_cursor "$GHOSTTY_SHELL_INTEGRATION_NO_CURSOR"
|
||||
set --local features (string split , $GHOSTTY_SHELL_FEATURES)
|
||||
|
||||
if test -z $no_cursor
|
||||
if contains cursor $features
|
||||
# Change the cursor to a beam on prompt.
|
||||
function __ghostty_set_cursor_beam --on-event fish_prompt -d "Set cursor shape"
|
||||
echo -en "\e[5 q"
|
||||
@ -62,13 +61,9 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
|
||||
end
|
||||
end
|
||||
|
||||
# Check if we are setting sudo
|
||||
set --local no_sudo "$GHOSTTY_SHELL_INTEGRATION_NO_SUDO"
|
||||
|
||||
# When using sudo shell integration feature, ensure $TERMINFO is set
|
||||
# and `sudo` is not already a function or alias
|
||||
if test -z $no_sudo
|
||||
and test -n "$TERMINFO"; and test "file" = (type -t sudo 2> /dev/null; or echo "x")
|
||||
if contains sudo $features; and test -n "$TERMINFO"; and test "file" = (type -t sudo 2> /dev/null; or echo "x")
|
||||
# Wrap `sudo` command to ensure Ghostty terminfo is preserved
|
||||
function sudo -d "Wrap sudo to preserve terminfo"
|
||||
set --function sudo_has_sudoedit_flags "no"
|
||||
@ -125,7 +120,7 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
|
||||
set --global fish_handle_reflow 1
|
||||
|
||||
# Initial calls for first prompt
|
||||
if test -z $no_cursor
|
||||
if contains cursor $features
|
||||
__ghostty_set_cursor_beam
|
||||
end
|
||||
__ghostty_mark_prompt_start
|
||||
|
@ -194,7 +194,7 @@ _ghostty_deferred_init() {
|
||||
_ghostty_report_pwd"
|
||||
_ghostty_report_pwd
|
||||
|
||||
if [[ "$GHOSTTY_SHELL_INTEGRATION_NO_TITLE" != 1 ]]; then
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
|
||||
# Enable terminal title changes.
|
||||
functions[_ghostty_precmd]+="
|
||||
builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
|
||||
@ -202,7 +202,7 @@ _ghostty_deferred_init() {
|
||||
builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${(V)1}\"\$'\\a'"
|
||||
fi
|
||||
|
||||
if [[ "$GHOSTTY_SHELL_INTEGRATION_NO_CURSOR" != 1 ]]; then
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
|
||||
# Enable cursor shape changes depending on the current keymap.
|
||||
# This implementation leaks blinking block cursor into external commands
|
||||
# executed from zle. For example, users of fzf-based widgets may find
|
||||
@ -221,7 +221,7 @@ _ghostty_deferred_init() {
|
||||
fi
|
||||
|
||||
# Sudo
|
||||
if [[ "$GHOSTTY_SHELL_INTEGRATION_NO_SUDO" != "1" ]] && [[ -n "$TERMINFO" ]]; then
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" == *"sudo"* ]] && [[ -n "$TERMINFO" ]]; then
|
||||
# Wrap `sudo` command to ensure Ghostty terminfo is preserved
|
||||
sudo() {
|
||||
builtin local sudo_has_sudoedit_flags="no"
|
||||
|
@ -98,6 +98,12 @@ pub const Parser = struct {
|
||||
self.state = .control_value;
|
||||
},
|
||||
|
||||
// This can be encountered if we have a sequence with no
|
||||
// control data, only payload data (i.e. "\x1b_G;<data>").
|
||||
//
|
||||
// Kitty treats this as valid so we do as well.
|
||||
';' => self.state = .data,
|
||||
|
||||
else => try self.accumulateValue(c, .control_key_ignore),
|
||||
},
|
||||
|
||||
@ -1053,6 +1059,21 @@ test "delete command" {
|
||||
try testing.expectEqual(@as(u32, 4), dv.y);
|
||||
}
|
||||
|
||||
test "no control data" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
var p = Parser.init(alloc);
|
||||
defer p.deinit();
|
||||
|
||||
const input = ";QUFBQQ";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .transmit);
|
||||
try testing.expectEqualStrings("AAAA", command.data);
|
||||
}
|
||||
|
||||
test "ignore unknown keys (long)" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
@ -24,6 +24,7 @@ const SegmentedPool = @import("../datastruct/main.zig").SegmentedPool;
|
||||
const ptypkg = @import("../pty.zig");
|
||||
const Pty = ptypkg.Pty;
|
||||
const EnvMap = std.process.EnvMap;
|
||||
const PasswdEntry = internal_os.passwd.Entry;
|
||||
const windows = internal_os.windows;
|
||||
|
||||
const log = std.log.scoped(.io_exec);
|
||||
@ -725,7 +726,7 @@ pub const ThreadData = struct {
|
||||
};
|
||||
|
||||
pub const Config = struct {
|
||||
command: ?[]const u8 = null,
|
||||
command: ?configpkg.Command = null,
|
||||
env: EnvMap,
|
||||
env_override: configpkg.RepeatableStringMap = .{},
|
||||
shell_integration: configpkg.Config.ShellIntegration = .detect,
|
||||
@ -746,7 +747,7 @@ const Subprocess = struct {
|
||||
arena: std.heap.ArenaAllocator,
|
||||
cwd: ?[]const u8,
|
||||
env: ?EnvMap,
|
||||
args: [][]const u8,
|
||||
args: []const [:0]const u8,
|
||||
grid_size: renderer.GridSize,
|
||||
screen_size: renderer.ScreenSize,
|
||||
pty: ?Pty = null,
|
||||
@ -892,18 +893,29 @@ const Subprocess = struct {
|
||||
env.remove("VTE_VERSION");
|
||||
|
||||
// Setup our shell integration, if we can.
|
||||
const integrated_shell: ?shell_integration.Shell, const shell_command: []const u8 = shell: {
|
||||
const default_shell_command = cfg.command orelse switch (builtin.os.tag) {
|
||||
.windows => "cmd.exe",
|
||||
else => "sh",
|
||||
};
|
||||
const shell_command: configpkg.Command = shell: {
|
||||
const default_shell_command: configpkg.Command =
|
||||
cfg.command orelse .{ .shell = switch (builtin.os.tag) {
|
||||
.windows => "cmd.exe",
|
||||
else => "sh",
|
||||
} };
|
||||
|
||||
const force: ?shell_integration.Shell = switch (cfg.shell_integration) {
|
||||
.none => {
|
||||
// Even if shell integration is none, we still want to set up the feature env vars
|
||||
try shell_integration.setupFeatures(&env, cfg.shell_integration_features);
|
||||
break :shell .{ null, default_shell_command };
|
||||
// Even if shell integration is none, we still want to
|
||||
// set up the feature env vars
|
||||
try shell_integration.setupFeatures(
|
||||
&env,
|
||||
cfg.shell_integration_features,
|
||||
);
|
||||
|
||||
// This is a source of confusion for users despite being
|
||||
// opt-in since it results in some Ghostty features not
|
||||
// working. We always want to log it.
|
||||
log.info("shell integration disabled by configuration", .{});
|
||||
break :shell default_shell_command;
|
||||
},
|
||||
|
||||
.detect => null,
|
||||
.bash => .bash,
|
||||
.elvish => .elvish,
|
||||
@ -911,9 +923,9 @@ const Subprocess = struct {
|
||||
.zsh => .zsh,
|
||||
};
|
||||
|
||||
const dir = cfg.resources_dir orelse break :shell .{
|
||||
null,
|
||||
default_shell_command,
|
||||
const dir = cfg.resources_dir orelse {
|
||||
log.warn("no resources dir set, shell integration disabled", .{});
|
||||
break :shell default_shell_command;
|
||||
};
|
||||
|
||||
const integration = try shell_integration.setup(
|
||||
@ -923,19 +935,18 @@ const Subprocess = struct {
|
||||
&env,
|
||||
force,
|
||||
cfg.shell_integration_features,
|
||||
) orelse break :shell .{ null, default_shell_command };
|
||||
) orelse {
|
||||
log.warn("shell could not be detected, no automatic shell integration will be injected", .{});
|
||||
break :shell default_shell_command;
|
||||
};
|
||||
|
||||
break :shell .{ integration.shell, integration.command };
|
||||
};
|
||||
|
||||
if (integrated_shell) |shell| {
|
||||
log.info(
|
||||
"shell integration automatically injected shell={}",
|
||||
.{shell},
|
||||
.{integration.shell},
|
||||
);
|
||||
} else if (cfg.shell_integration != .none) {
|
||||
log.warn("shell could not be detected, no automatic shell integration will be injected", .{});
|
||||
}
|
||||
|
||||
break :shell integration.command;
|
||||
};
|
||||
|
||||
// Add the environment variables that override any others.
|
||||
{
|
||||
@ -947,134 +958,29 @@ const Subprocess = struct {
|
||||
}
|
||||
|
||||
// Build our args list
|
||||
const args = args: {
|
||||
const cap = 9; // the most we'll ever use
|
||||
var args = try std.ArrayList([]const u8).initCapacity(alloc, cap);
|
||||
defer args.deinit();
|
||||
const args: []const [:0]const u8 = execCommand(
|
||||
alloc,
|
||||
shell_command,
|
||||
internal_os.passwd,
|
||||
) catch |err| switch (err) {
|
||||
// If we fail to allocate space for the command we want to
|
||||
// execute, we'd still like to try to run something so
|
||||
// Ghostty can launch (and maybe the user can debug this further).
|
||||
// Realistically, if you're getting OOM, I think other stuff is
|
||||
// about to crash, but we can try.
|
||||
error.OutOfMemory => oom: {
|
||||
log.warn("failed to allocate space for command args, falling back to basic shell", .{});
|
||||
|
||||
// If we're on macOS, we have to use `login(1)` to get all of
|
||||
// the proper environment variables set, a login shell, and proper
|
||||
// hushlogin behavior.
|
||||
if (comptime builtin.target.os.tag.isDarwin()) darwin: {
|
||||
const passwd = internal_os.passwd.get(alloc) catch |err| {
|
||||
log.warn("failed to read passwd, not using a login shell err={}", .{err});
|
||||
break :darwin;
|
||||
// The comptime here is important to ensure the full slice
|
||||
// is put into the binary data and not the stack.
|
||||
break :oom comptime switch (builtin.os.tag) {
|
||||
.windows => &.{"cmd.exe"},
|
||||
else => &.{"/bin/sh"},
|
||||
};
|
||||
},
|
||||
|
||||
const username = passwd.name orelse {
|
||||
log.warn("failed to get username, not using a login shell", .{});
|
||||
break :darwin;
|
||||
};
|
||||
|
||||
const hush = if (passwd.home) |home| hush: {
|
||||
var dir = std.fs.openDirAbsolute(home, .{}) catch |err| {
|
||||
log.warn(
|
||||
"failed to open home dir, not checking for hushlogin err={}",
|
||||
.{err},
|
||||
);
|
||||
break :hush false;
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
break :hush if (dir.access(".hushlogin", .{})) true else |_| false;
|
||||
} else false;
|
||||
|
||||
const cmd = try std.fmt.allocPrint(
|
||||
alloc,
|
||||
"exec -l {s}",
|
||||
.{shell_command},
|
||||
);
|
||||
|
||||
// The reason for executing login this way is unclear. This
|
||||
// comment will attempt to explain but prepare for a truly
|
||||
// unhinged reality.
|
||||
//
|
||||
// The first major issue is that on macOS, a lot of users
|
||||
// put shell configurations in ~/.bash_profile instead of
|
||||
// ~/.bashrc (or equivalent for another shell). This file is only
|
||||
// loaded for a login shell so macOS users expect all their terminals
|
||||
// to be login shells. No other platform behaves this way and its
|
||||
// totally braindead but somehow the entire dev community on
|
||||
// macOS has cargo culted their way to this reality so we have to
|
||||
// do it...
|
||||
//
|
||||
// To get a login shell, you COULD just prepend argv0 with a `-`
|
||||
// but that doesn't fully work because `getlogin()` C API will
|
||||
// return the wrong value, SHELL won't be set, and various
|
||||
// other login behaviors that macOS users expect.
|
||||
//
|
||||
// The proper way is to use `login(1)`. But login(1) forces
|
||||
// the working directory to change to the home directory,
|
||||
// which we may not want. If we specify "-l" then we can avoid
|
||||
// this behavior but now the shell isn't a login shell.
|
||||
//
|
||||
// There is another issue: `login(1)` on macOS 14.3 and earlier
|
||||
// checked for ".hushlogin" in the working directory. This means
|
||||
// that if we specify "-l" then we won't get hushlogin honored
|
||||
// if its in the home directory (which is standard). To get
|
||||
// around this, we check for hushlogin ourselves and if present
|
||||
// specify the "-q" flag to login(1).
|
||||
//
|
||||
// So to get all the behaviors we want, we specify "-l" but
|
||||
// execute "bash" (which is built-in to macOS). We then use
|
||||
// the bash builtin "exec" to replace the process with a login
|
||||
// shell ("-l" on exec) with the command we really want.
|
||||
//
|
||||
// We use "bash" instead of other shells that ship with macOS
|
||||
// because as of macOS Sonoma, we found with a microbenchmark
|
||||
// that bash can `exec` into the desired command ~2x faster
|
||||
// than zsh.
|
||||
//
|
||||
// To figure out a lot of this logic I read the login.c
|
||||
// source code in the OSS distribution Apple provides for
|
||||
// macOS.
|
||||
//
|
||||
// Awesome.
|
||||
try args.append("/usr/bin/login");
|
||||
if (hush) try args.append("-q");
|
||||
try args.append("-flp");
|
||||
|
||||
// We execute bash with "--noprofile --norc" so that it doesn't
|
||||
// load startup files so that (1) our shell integration doesn't
|
||||
// break and (2) user configuration doesn't mess this process
|
||||
// up.
|
||||
try args.append(username);
|
||||
try args.append("/bin/bash");
|
||||
try args.append("--noprofile");
|
||||
try args.append("--norc");
|
||||
try args.append("-c");
|
||||
try args.append(cmd);
|
||||
break :args try args.toOwnedSlice();
|
||||
}
|
||||
|
||||
if (comptime builtin.os.tag == .windows) {
|
||||
// We run our shell wrapped in `cmd.exe` so that we don't have
|
||||
// to parse the command line ourselves if it has arguments.
|
||||
|
||||
// Note we don't free any of the memory below since it is
|
||||
// allocated in the arena.
|
||||
const windir = try std.process.getEnvVarOwned(alloc, "WINDIR");
|
||||
const cmd = try std.fs.path.join(alloc, &[_][]const u8{
|
||||
windir,
|
||||
"System32",
|
||||
"cmd.exe",
|
||||
});
|
||||
|
||||
try args.append(cmd);
|
||||
try args.append("/C");
|
||||
} else {
|
||||
// We run our shell wrapped in `/bin/sh` so that we don't have
|
||||
// to parse the command line ourselves if it has arguments.
|
||||
// Additionally, some environments (NixOS, I found) use /bin/sh
|
||||
// to setup some environment variables that are important to
|
||||
// have set.
|
||||
try args.append("/bin/sh");
|
||||
if (internal_os.isFlatpak()) try args.append("-l");
|
||||
try args.append("-c");
|
||||
}
|
||||
|
||||
try args.append(shell_command);
|
||||
break :args try args.toOwnedSlice();
|
||||
// This logs on its own, this is a bad error.
|
||||
error.SystemError => return err,
|
||||
};
|
||||
|
||||
// We have to copy the cwd because there is no guarantee that
|
||||
@ -1562,3 +1468,320 @@ pub const ReadThread = struct {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Builds the argv array for the process we should exec for the
|
||||
/// configured command. This isn't as straightforward as it seems since
|
||||
/// we deal with shell-wrapping, macOS login shells, etc.
|
||||
///
|
||||
/// The passwdpkg comptime argument is expected to have a single function
|
||||
/// `get(Allocator)` that returns a passwd entry. This is used by macOS
|
||||
/// to determine the username and home directory for the login shell.
|
||||
/// It is unused on other platforms.
|
||||
///
|
||||
/// Memory ownership:
|
||||
///
|
||||
/// The allocator should be an arena, since the returned value may or
|
||||
/// may not be allocated and args may or may not be allocated (or copied).
|
||||
/// Pointers in the return value may point to pointers in the command
|
||||
/// struct.
|
||||
fn execCommand(
|
||||
alloc: Allocator,
|
||||
command: configpkg.Command,
|
||||
comptime passwdpkg: type,
|
||||
) (Allocator.Error || error{SystemError})![]const [:0]const u8 {
|
||||
// If we're on macOS, we have to use `login(1)` to get all of
|
||||
// the proper environment variables set, a login shell, and proper
|
||||
// hushlogin behavior.
|
||||
if (comptime builtin.target.os.tag.isDarwin()) darwin: {
|
||||
const passwd = passwdpkg.get(alloc) catch |err| {
|
||||
log.warn("failed to read passwd, not using a login shell err={}", .{err});
|
||||
break :darwin;
|
||||
};
|
||||
|
||||
const username = passwd.name orelse {
|
||||
log.warn("failed to get username, not using a login shell", .{});
|
||||
break :darwin;
|
||||
};
|
||||
|
||||
const hush = if (passwd.home) |home| hush: {
|
||||
var dir = std.fs.openDirAbsolute(home, .{}) catch |err| {
|
||||
log.warn(
|
||||
"failed to open home dir, not checking for hushlogin err={}",
|
||||
.{err},
|
||||
);
|
||||
break :hush false;
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
break :hush if (dir.access(".hushlogin", .{})) true else |_| false;
|
||||
} else false;
|
||||
|
||||
// If we made it this far we're going to start building
|
||||
// the actual command.
|
||||
var args: std.ArrayList([:0]const u8) = try .initCapacity(
|
||||
alloc,
|
||||
|
||||
// This capacity is chosen based on what we'd need to
|
||||
// execute a shell command (very common). We can/will
|
||||
// grow if necessary for a longer command (uncommon).
|
||||
9,
|
||||
);
|
||||
defer args.deinit();
|
||||
|
||||
// The reason for executing login this way is unclear. This
|
||||
// comment will attempt to explain but prepare for a truly
|
||||
// unhinged reality.
|
||||
//
|
||||
// The first major issue is that on macOS, a lot of users
|
||||
// put shell configurations in ~/.bash_profile instead of
|
||||
// ~/.bashrc (or equivalent for another shell). This file is only
|
||||
// loaded for a login shell so macOS users expect all their terminals
|
||||
// to be login shells. No other platform behaves this way and its
|
||||
// totally braindead but somehow the entire dev community on
|
||||
// macOS has cargo culted their way to this reality so we have to
|
||||
// do it...
|
||||
//
|
||||
// To get a login shell, you COULD just prepend argv0 with a `-`
|
||||
// but that doesn't fully work because `getlogin()` C API will
|
||||
// return the wrong value, SHELL won't be set, and various
|
||||
// other login behaviors that macOS users expect.
|
||||
//
|
||||
// The proper way is to use `login(1)`. But login(1) forces
|
||||
// the working directory to change to the home directory,
|
||||
// which we may not want. If we specify "-l" then we can avoid
|
||||
// this behavior but now the shell isn't a login shell.
|
||||
//
|
||||
// There is another issue: `login(1)` on macOS 14.3 and earlier
|
||||
// checked for ".hushlogin" in the working directory. This means
|
||||
// that if we specify "-l" then we won't get hushlogin honored
|
||||
// if its in the home directory (which is standard). To get
|
||||
// around this, we check for hushlogin ourselves and if present
|
||||
// specify the "-q" flag to login(1).
|
||||
//
|
||||
// So to get all the behaviors we want, we specify "-l" but
|
||||
// execute "bash" (which is built-in to macOS). We then use
|
||||
// the bash builtin "exec" to replace the process with a login
|
||||
// shell ("-l" on exec) with the command we really want.
|
||||
//
|
||||
// We use "bash" instead of other shells that ship with macOS
|
||||
// because as of macOS Sonoma, we found with a microbenchmark
|
||||
// that bash can `exec` into the desired command ~2x faster
|
||||
// than zsh.
|
||||
//
|
||||
// To figure out a lot of this logic I read the login.c
|
||||
// source code in the OSS distribution Apple provides for
|
||||
// macOS.
|
||||
//
|
||||
// Awesome.
|
||||
try args.append("/usr/bin/login");
|
||||
if (hush) try args.append("-q");
|
||||
try args.append("-flp");
|
||||
try args.append(username);
|
||||
|
||||
switch (command) {
|
||||
// Direct args can be passed directly to login, since
|
||||
// login uses execvp we don't need to worry about PATH
|
||||
// searching.
|
||||
.direct => |v| try args.appendSlice(v),
|
||||
|
||||
.shell => |v| {
|
||||
// Use "exec" to replace the bash process with
|
||||
// our intended command so we don't have a parent
|
||||
// process hanging around.
|
||||
const cmd = try std.fmt.allocPrintZ(
|
||||
alloc,
|
||||
"exec -l {s}",
|
||||
.{v},
|
||||
);
|
||||
|
||||
// We execute bash with "--noprofile --norc" so that it doesn't
|
||||
// load startup files so that (1) our shell integration doesn't
|
||||
// break and (2) user configuration doesn't mess this process
|
||||
// up.
|
||||
try args.append("/bin/bash");
|
||||
try args.append("--noprofile");
|
||||
try args.append("--norc");
|
||||
try args.append("-c");
|
||||
try args.append(cmd);
|
||||
},
|
||||
}
|
||||
|
||||
return try args.toOwnedSlice();
|
||||
}
|
||||
|
||||
return switch (command) {
|
||||
.direct => |v| v,
|
||||
|
||||
.shell => |v| shell: {
|
||||
var args: std.ArrayList([:0]const u8) = try .initCapacity(alloc, 4);
|
||||
defer args.deinit();
|
||||
|
||||
if (comptime builtin.os.tag == .windows) {
|
||||
// We run our shell wrapped in `cmd.exe` so that we don't have
|
||||
// to parse the command line ourselves if it has arguments.
|
||||
|
||||
// Note we don't free any of the memory below since it is
|
||||
// allocated in the arena.
|
||||
const windir = std.process.getEnvVarOwned(
|
||||
alloc,
|
||||
"WINDIR",
|
||||
) catch |err| {
|
||||
log.warn("failed to get WINDIR, cannot run shell command err={}", .{err});
|
||||
return error.SystemError;
|
||||
};
|
||||
const cmd = try std.fs.path.joinZ(alloc, &[_][]const u8{
|
||||
windir,
|
||||
"System32",
|
||||
"cmd.exe",
|
||||
});
|
||||
|
||||
try args.append(cmd);
|
||||
try args.append("/C");
|
||||
} else {
|
||||
// We run our shell wrapped in `/bin/sh` so that we don't have
|
||||
// to parse the command line ourselves if it has arguments.
|
||||
// Additionally, some environments (NixOS, I found) use /bin/sh
|
||||
// to setup some environment variables that are important to
|
||||
// have set.
|
||||
try args.append("/bin/sh");
|
||||
if (internal_os.isFlatpak()) try args.append("-l");
|
||||
try args.append("-c");
|
||||
}
|
||||
|
||||
try args.append(v);
|
||||
break :shell try args.toOwnedSlice();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test "execCommand darwin: shell command" {
|
||||
if (comptime !builtin.os.tag.isDarwin()) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const result = try execCommand(alloc, .{ .shell = "foo bar baz" }, struct {
|
||||
fn get(_: Allocator) !PasswdEntry {
|
||||
return .{
|
||||
.name = "testuser",
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
try testing.expectEqual(8, result.len);
|
||||
try testing.expectEqualStrings(result[0], "/usr/bin/login");
|
||||
try testing.expectEqualStrings(result[1], "-flp");
|
||||
try testing.expectEqualStrings(result[2], "testuser");
|
||||
try testing.expectEqualStrings(result[3], "/bin/bash");
|
||||
try testing.expectEqualStrings(result[4], "--noprofile");
|
||||
try testing.expectEqualStrings(result[5], "--norc");
|
||||
try testing.expectEqualStrings(result[6], "-c");
|
||||
try testing.expectEqualStrings(result[7], "exec -l foo bar baz");
|
||||
}
|
||||
|
||||
test "execCommand darwin: direct command" {
|
||||
if (comptime !builtin.os.tag.isDarwin()) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const result = try execCommand(alloc, .{ .direct = &.{
|
||||
"foo",
|
||||
"bar baz",
|
||||
} }, struct {
|
||||
fn get(_: Allocator) !PasswdEntry {
|
||||
return .{
|
||||
.name = "testuser",
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
try testing.expectEqual(5, result.len);
|
||||
try testing.expectEqualStrings(result[0], "/usr/bin/login");
|
||||
try testing.expectEqualStrings(result[1], "-flp");
|
||||
try testing.expectEqualStrings(result[2], "testuser");
|
||||
try testing.expectEqualStrings(result[3], "foo");
|
||||
try testing.expectEqualStrings(result[4], "bar baz");
|
||||
}
|
||||
|
||||
test "execCommand: shell command, empty passwd" {
|
||||
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const result = try execCommand(
|
||||
alloc,
|
||||
.{ .shell = "foo bar baz" },
|
||||
struct {
|
||||
fn get(_: Allocator) !PasswdEntry {
|
||||
// Empty passwd entry means we can't construct a macOS
|
||||
// login command and falls back to POSIX behavior.
|
||||
return .{};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
try testing.expectEqual(3, result.len);
|
||||
try testing.expectEqualStrings(result[0], "/bin/sh");
|
||||
try testing.expectEqualStrings(result[1], "-c");
|
||||
try testing.expectEqualStrings(result[2], "foo bar baz");
|
||||
}
|
||||
|
||||
test "execCommand: shell command, error passwd" {
|
||||
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const result = try execCommand(
|
||||
alloc,
|
||||
.{ .shell = "foo bar baz" },
|
||||
struct {
|
||||
fn get(_: Allocator) !PasswdEntry {
|
||||
// Failed passwd entry means we can't construct a macOS
|
||||
// login command and falls back to POSIX behavior.
|
||||
return error.Fail;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
try testing.expectEqual(3, result.len);
|
||||
try testing.expectEqualStrings(result[0], "/bin/sh");
|
||||
try testing.expectEqualStrings(result[1], "-c");
|
||||
try testing.expectEqualStrings(result[2], "foo bar baz");
|
||||
}
|
||||
|
||||
test "execCommand: direct command, error passwd" {
|
||||
if (comptime builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const result = try execCommand(alloc, .{
|
||||
.direct = &.{
|
||||
"foo",
|
||||
"bar baz",
|
||||
},
|
||||
}, struct {
|
||||
fn get(_: Allocator) !PasswdEntry {
|
||||
// Failed passwd entry means we can't construct a macOS
|
||||
// login command and falls back to POSIX behavior.
|
||||
return error.Fail;
|
||||
}
|
||||
});
|
||||
|
||||
try testing.expectEqual(2, result.len);
|
||||
try testing.expectEqualStrings(result[0], "foo");
|
||||
try testing.expectEqualStrings(result[1], "bar baz");
|
||||
}
|
||||
|
@ -27,10 +27,10 @@ pub const ShellIntegration = struct {
|
||||
/// bash in particular it may be different.
|
||||
///
|
||||
/// The memory is allocated in the arena given to setup.
|
||||
command: []const u8,
|
||||
command: config.Command,
|
||||
};
|
||||
|
||||
/// Setup the command execution environment for automatic
|
||||
/// Set up the command execution environment for automatic
|
||||
/// integrated shell integration and return a ShellIntegration
|
||||
/// struct describing the integration. If integration fails
|
||||
/// (shell type couldn't be detected, etc.), this will return null.
|
||||
@ -41,7 +41,7 @@ pub const ShellIntegration = struct {
|
||||
pub fn setup(
|
||||
alloc_arena: Allocator,
|
||||
resource_dir: []const u8,
|
||||
command: []const u8,
|
||||
command: config.Command,
|
||||
env: *EnvMap,
|
||||
force_shell: ?Shell,
|
||||
features: config.ShellIntegrationFeatures,
|
||||
@ -51,14 +51,24 @@ pub fn setup(
|
||||
.elvish => "elvish",
|
||||
.fish => "fish",
|
||||
.zsh => "zsh",
|
||||
} else exe: {
|
||||
// The command can include arguments. Look for the first space
|
||||
// and use the basename of the first part as the command's exe.
|
||||
const idx = std.mem.indexOfScalar(u8, command, ' ') orelse command.len;
|
||||
break :exe std.fs.path.basename(command[0..idx]);
|
||||
} else switch (command) {
|
||||
.direct => |v| std.fs.path.basename(v[0]),
|
||||
.shell => |v| exe: {
|
||||
// Shell strings can include spaces so we want to only
|
||||
// look up to the space if it exists. No shell that we integrate
|
||||
// has spaces.
|
||||
const idx = std.mem.indexOfScalar(u8, v, ' ') orelse v.len;
|
||||
break :exe std.fs.path.basename(v[0..idx]);
|
||||
},
|
||||
};
|
||||
|
||||
const result = try setupShell(alloc_arena, resource_dir, command, env, exe);
|
||||
const result = try setupShell(
|
||||
alloc_arena,
|
||||
resource_dir,
|
||||
command,
|
||||
env,
|
||||
exe,
|
||||
);
|
||||
|
||||
// Setup our feature env vars
|
||||
try setupFeatures(env, features);
|
||||
@ -69,7 +79,7 @@ pub fn setup(
|
||||
fn setupShell(
|
||||
alloc_arena: Allocator,
|
||||
resource_dir: []const u8,
|
||||
command: []const u8,
|
||||
command: config.Command,
|
||||
env: *EnvMap,
|
||||
exe: []const u8,
|
||||
) !?ShellIntegration {
|
||||
@ -83,7 +93,10 @@ fn setupShell(
|
||||
// we're using Apple's Bash because /bin is non-writable
|
||||
// on modern macOS due to System Integrity Protection.
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
if (std.mem.eql(u8, "/bin/bash", command)) {
|
||||
if (std.mem.eql(u8, "/bin/bash", switch (command) {
|
||||
.direct => |v| v[0],
|
||||
.shell => |v| v,
|
||||
})) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -104,7 +117,7 @@ fn setupShell(
|
||||
try setupXdgDataDirs(alloc_arena, resource_dir, env);
|
||||
return .{
|
||||
.shell = .elvish,
|
||||
.command = try alloc_arena.dupe(u8, command),
|
||||
.command = try command.clone(alloc_arena),
|
||||
};
|
||||
}
|
||||
|
||||
@ -112,7 +125,7 @@ fn setupShell(
|
||||
try setupXdgDataDirs(alloc_arena, resource_dir, env);
|
||||
return .{
|
||||
.shell = .fish,
|
||||
.command = try alloc_arena.dupe(u8, command),
|
||||
.command = try command.clone(alloc_arena),
|
||||
};
|
||||
}
|
||||
|
||||
@ -120,7 +133,7 @@ fn setupShell(
|
||||
try setupZsh(resource_dir, env);
|
||||
return .{
|
||||
.shell = .zsh,
|
||||
.command = try alloc_arena.dupe(u8, command),
|
||||
.command = try command.clone(alloc_arena),
|
||||
};
|
||||
}
|
||||
|
||||
@ -139,20 +152,41 @@ test "force shell" {
|
||||
|
||||
inline for (@typeInfo(Shell).@"enum".fields) |field| {
|
||||
const shell = @field(Shell, field.name);
|
||||
const result = try setup(alloc, ".", "sh", &env, shell, .{});
|
||||
const result = try setup(
|
||||
alloc,
|
||||
".",
|
||||
.{ .shell = "sh" },
|
||||
&env,
|
||||
shell,
|
||||
.{},
|
||||
);
|
||||
try testing.expectEqual(shell, result.?.shell);
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup shell integration feature environment variables without
|
||||
/// performing full shell integration setup.
|
||||
/// Set up the shell integration features environment variable.
|
||||
pub fn setupFeatures(
|
||||
env: *EnvMap,
|
||||
features: config.ShellIntegrationFeatures,
|
||||
) !void {
|
||||
if (!features.cursor) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR", "1");
|
||||
if (!features.sudo) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_SUDO", "1");
|
||||
if (!features.title) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_TITLE", "1");
|
||||
const fields = @typeInfo(@TypeOf(features)).@"struct".fields;
|
||||
const capacity: usize = capacity: {
|
||||
comptime var n: usize = fields.len - 1; // commas
|
||||
inline for (fields) |field| n += field.name.len;
|
||||
break :capacity n;
|
||||
};
|
||||
var buffer = try std.BoundedArray(u8, capacity).init(0);
|
||||
|
||||
inline for (fields) |field| {
|
||||
if (@field(features, field.name)) {
|
||||
if (buffer.len > 0) try buffer.append(',');
|
||||
try buffer.appendSlice(field.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.len > 0) {
|
||||
try env.put("GHOSTTY_SHELL_FEATURES", buffer.slice());
|
||||
}
|
||||
}
|
||||
|
||||
test "setup features" {
|
||||
@ -162,15 +196,13 @@ test "setup features" {
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
// Test: all features enabled (no environment variables should be set)
|
||||
// Test: all features enabled
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
try setupFeatures(&env, .{ .cursor = true, .sudo = true, .title = true });
|
||||
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR") == null);
|
||||
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO") == null);
|
||||
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE") == null);
|
||||
try testing.expectEqualStrings("cursor,sudo,title", env.get("GHOSTTY_SHELL_FEATURES").?);
|
||||
}
|
||||
|
||||
// Test: all features disabled
|
||||
@ -179,9 +211,7 @@ test "setup features" {
|
||||
defer env.deinit();
|
||||
|
||||
try setupFeatures(&env, .{ .cursor = false, .sudo = false, .title = false });
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR").?);
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO").?);
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE").?);
|
||||
try testing.expect(env.get("GHOSTTY_SHELL_FEATURES") == null);
|
||||
}
|
||||
|
||||
// Test: mixed features
|
||||
@ -190,9 +220,7 @@ test "setup features" {
|
||||
defer env.deinit();
|
||||
|
||||
try setupFeatures(&env, .{ .cursor = false, .sudo = true, .title = false });
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR").?);
|
||||
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO") == null);
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE").?);
|
||||
try testing.expectEqualStrings("sudo", env.get("GHOSTTY_SHELL_FEATURES").?);
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,25 +235,21 @@ test "setup features" {
|
||||
/// enables the integration or null if integration failed.
|
||||
fn setupBash(
|
||||
alloc: Allocator,
|
||||
command: []const u8,
|
||||
command: config.Command,
|
||||
resource_dir: []const u8,
|
||||
env: *EnvMap,
|
||||
) !?[]const u8 {
|
||||
// Accumulates the arguments that will form the final shell command line.
|
||||
// We can build this list on the stack because we're just temporarily
|
||||
// referencing other slices, but we can fall back to heap in extreme cases.
|
||||
var args_alloc = std.heap.stackFallback(1024, alloc);
|
||||
var args = try std.ArrayList([]const u8).initCapacity(args_alloc.get(), 2);
|
||||
) !?config.Command {
|
||||
var args = try std.ArrayList([:0]const u8).initCapacity(alloc, 2);
|
||||
defer args.deinit();
|
||||
|
||||
// Iterator that yields each argument in the original command line.
|
||||
// This will allocate once proportionate to the command line length.
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(alloc, command);
|
||||
var iter = try command.argIterator(alloc);
|
||||
defer iter.deinit();
|
||||
|
||||
// Start accumulating arguments with the executable and `--posix` mode flag.
|
||||
if (iter.next()) |exe| {
|
||||
try args.append(exe);
|
||||
try args.append(try alloc.dupeZ(u8, exe));
|
||||
} else return null;
|
||||
try args.append("--posix");
|
||||
|
||||
@ -259,17 +283,17 @@ fn setupBash(
|
||||
if (std.mem.indexOfScalar(u8, arg, 'c') != null) {
|
||||
return null;
|
||||
}
|
||||
try args.append(arg);
|
||||
try args.append(try alloc.dupeZ(u8, arg));
|
||||
} else if (std.mem.eql(u8, arg, "-") or std.mem.eql(u8, arg, "--")) {
|
||||
// All remaining arguments should be passed directly to the shell
|
||||
// command. We shouldn't perform any further option processing.
|
||||
try args.append(arg);
|
||||
try args.append(try alloc.dupeZ(u8, arg));
|
||||
while (iter.next()) |remaining_arg| {
|
||||
try args.append(remaining_arg);
|
||||
try args.append(try alloc.dupeZ(u8, remaining_arg));
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
try args.append(arg);
|
||||
try args.append(try alloc.dupeZ(u8, arg));
|
||||
}
|
||||
}
|
||||
try env.put("GHOSTTY_BASH_INJECT", inject.slice());
|
||||
@ -302,30 +326,36 @@ fn setupBash(
|
||||
);
|
||||
try env.put("ENV", integ_dir);
|
||||
|
||||
// Join the accumulated arguments to form the final command string.
|
||||
return try std.mem.join(alloc, " ", args.items);
|
||||
// Since we built up a command line, we don't need to wrap it in
|
||||
// ANOTHER shell anymore and can do a direct command.
|
||||
return .{ .direct = try args.toOwnedSlice() };
|
||||
}
|
||||
|
||||
test "bash" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
const command = try setupBash(alloc, .{ .shell = "bash" }, ".", &env);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("./shell-integration/bash/ghostty.bash", env.get("ENV").?);
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
|
||||
test "bash: unsupported options" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const cmdlines = [_][]const u8{
|
||||
const cmdlines = [_][:0]const u8{
|
||||
"bash --posix",
|
||||
"bash --rcfile script.sh --posix",
|
||||
"bash --init-file script.sh --posix",
|
||||
@ -337,7 +367,7 @@ test "bash: unsupported options" {
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
try testing.expect(try setupBash(alloc, cmdline, ".", &env) == null);
|
||||
try testing.expect(try setupBash(alloc, .{ .shell = cmdline }, ".", &env) == null);
|
||||
try testing.expect(env.get("GHOSTTY_BASH_INJECT") == null);
|
||||
try testing.expect(env.get("GHOSTTY_BASH_RCFILE") == null);
|
||||
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
||||
@ -346,17 +376,20 @@ test "bash: unsupported options" {
|
||||
|
||||
test "bash: inject flags" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
// bash --norc
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash --norc", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
const command = try setupBash(alloc, .{ .shell = "bash --norc" }, ".", &env);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("1 --norc", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
|
||||
@ -365,52 +398,55 @@ test "bash: inject flags" {
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash --noprofile", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
const command = try setupBash(alloc, .{ .shell = "bash --noprofile" }, ".", &env);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("1 --noprofile", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
}
|
||||
|
||||
test "bash: rcfile" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
// bash --rcfile
|
||||
{
|
||||
const command = try setupBash(alloc, "bash --rcfile profile.sh", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
const command = try setupBash(alloc, .{ .shell = "bash --rcfile profile.sh" }, ".", &env);
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
||||
}
|
||||
|
||||
// bash --init-file
|
||||
{
|
||||
const command = try setupBash(alloc, "bash --init-file profile.sh", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix", command.?);
|
||||
const command = try setupBash(alloc, .{ .shell = "bash --init-file profile.sh" }, ".", &env);
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
||||
}
|
||||
}
|
||||
|
||||
test "bash: HISTFILE" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
// HISTFILE unset
|
||||
{
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
const command = try setupBash(alloc, "bash", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
_ = try setupBash(alloc, .{ .shell = "bash" }, ".", &env);
|
||||
try testing.expect(std.mem.endsWith(u8, env.get("HISTFILE").?, ".bash_history"));
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE").?);
|
||||
}
|
||||
@ -422,9 +458,7 @@ test "bash: HISTFILE" {
|
||||
|
||||
try env.put("HISTFILE", "my_history");
|
||||
|
||||
const command = try setupBash(alloc, "bash", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
_ = try setupBash(alloc, .{ .shell = "bash" }, ".", &env);
|
||||
try testing.expectEqualStrings("my_history", env.get("HISTFILE").?);
|
||||
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
||||
}
|
||||
@ -432,25 +466,35 @@ test "bash: HISTFILE" {
|
||||
|
||||
test "bash: additional arguments" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var env = EnvMap.init(alloc);
|
||||
defer env.deinit();
|
||||
|
||||
// "-" argument separator
|
||||
{
|
||||
const command = try setupBash(alloc, "bash - --arg file1 file2", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix - --arg file1 file2", command.?);
|
||||
const command = try setupBash(alloc, .{ .shell = "bash - --arg file1 file2" }, ".", &env);
|
||||
try testing.expectEqual(6, command.?.direct.len);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("-", command.?.direct[2]);
|
||||
try testing.expectEqualStrings("--arg", command.?.direct[3]);
|
||||
try testing.expectEqualStrings("file1", command.?.direct[4]);
|
||||
try testing.expectEqualStrings("file2", command.?.direct[5]);
|
||||
}
|
||||
|
||||
// "--" argument separator
|
||||
{
|
||||
const command = try setupBash(alloc, "bash -- --arg file1 file2", ".", &env);
|
||||
defer if (command) |c| alloc.free(c);
|
||||
|
||||
try testing.expectEqualStrings("bash --posix -- --arg file1 file2", command.?);
|
||||
const command = try setupBash(alloc, .{ .shell = "bash -- --arg file1 file2" }, ".", &env);
|
||||
try testing.expectEqual(6, command.?.direct.len);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("--", command.?.direct[2]);
|
||||
try testing.expectEqualStrings("--arg", command.?.direct[3]);
|
||||
try testing.expectEqualStrings("file1", command.?.direct[4]);
|
||||
try testing.expectEqualStrings("file2", command.?.direct[5]);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user