mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-12 10:48:39 +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
|
# Localization
|
||||||
/po/README_TRANSLATORS.md @ghostty-org/localization
|
/po/README_TRANSLATORS.md @ghostty-org/localization
|
||||||
/po/com.mitchellh.ghostty.pot @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/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/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/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/uk_UA.UTF-8.po @ghostty-org/uk_UA
|
||||||
/po/zh_CN.UTF-8.po @ghostty-org/zh_CN
|
/po/zh_CN.UTF-8.po @ghostty-org/zh_CN
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.{
|
.{
|
||||||
.name = .ghostty,
|
.name = .ghostty,
|
||||||
.version = "1.1.3",
|
.version = "1.1.4",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.fingerprint = 0x64407a2a0b4147e5,
|
.fingerprint = 0x64407a2a0b4147e5,
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
@ -14,8 +14,8 @@
|
|||||||
},
|
},
|
||||||
.vaxis = .{
|
.vaxis = .{
|
||||||
// rockorager/libvaxis
|
// rockorager/libvaxis
|
||||||
.url = "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447",
|
.url = "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23",
|
||||||
.hash = "vaxis-0.1.0-BWNV_MHyCAA0rNbPTr50Z44PyEdNP9zQSnHcXBXoo3Ti",
|
.hash = "vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
.z2d = .{
|
.z2d = .{
|
||||||
@ -48,8 +48,8 @@
|
|||||||
},
|
},
|
||||||
.zf = .{
|
.zf = .{
|
||||||
// natecraddock/zf
|
// natecraddock/zf
|
||||||
.url = "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz",
|
.url = "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz",
|
||||||
.hash = "zf-0.10.3-OIRy8bKIAADhjqtdjVaDfONRuI7RVl5gMbhCoOwiBWV5",
|
.hash = "zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
.gobject = .{
|
.gobject = .{
|
||||||
@ -103,8 +103,8 @@
|
|||||||
// Other
|
// Other
|
||||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||||
.iterm2_themes = .{
|
.iterm2_themes = .{
|
||||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz",
|
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4c57d8c11d352a4aeda6928b65d78794c28883a5.tar.gz",
|
||||||
.hash = "N-V-__8AADk6LwSAbK3OMyGiadf6aeyztHNV4-zKaLy6IZa6",
|
.hash = "N-V-__8AAEH8MwQaEsARbyV42-bSZGcu1am8xtg2h67wTFC3",
|
||||||
.lazy = true,
|
.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",
|
"url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||||
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
||||||
},
|
},
|
||||||
"N-V-__8AADk6LwSAbK3OMyGiadf6aeyztHNV4-zKaLy6IZa6": {
|
"N-V-__8AAEH8MwQaEsARbyV42-bSZGcu1am8xtg2h67wTFC3": {
|
||||||
"name": "iterm2_themes",
|
"name": "iterm2_themes",
|
||||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz",
|
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4c57d8c11d352a4aeda6928b65d78794c28883a5.tar.gz",
|
||||||
"hash": "sha256-nOkH31MQQd2PPdjVpRxBxNQWfR9Exg6nRF/KHgSz3cM="
|
"hash": "sha256-c+twvkEPiz1DaULYlnGXLxis19Q2h+TgBJxoARMasjU="
|
||||||
},
|
},
|
||||||
"N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD": {
|
"N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD": {
|
||||||
"name": "libpng",
|
"name": "libpng",
|
||||||
@ -104,10 +104,10 @@
|
|||||||
"url": "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz",
|
"url": "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz",
|
||||||
"hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8="
|
"hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8="
|
||||||
},
|
},
|
||||||
"vaxis-0.1.0-BWNV_MHyCAA0rNbPTr50Z44PyEdNP9zQSnHcXBXoo3Ti": {
|
"vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn": {
|
||||||
"name": "vaxis",
|
"name": "vaxis",
|
||||||
"url": "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447",
|
"url": "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23",
|
||||||
"hash": "sha256-iONEySjPeD0WYJ93fw5mxT+0pVfUO/m6008J/LXjQkA="
|
"hash": "sha256-bNZ3oveT6vPChjimPJ/GGfcdivlAeJdl/xfWM+S/MHY="
|
||||||
},
|
},
|
||||||
"N-V-__8AAKrHGAAs2shYq8UkE6bGcR1QJtLTyOE_lcosMn6t": {
|
"N-V-__8AAKrHGAAs2shYq8UkE6bGcR1QJtLTyOE_lcosMn6t": {
|
||||||
"name": "wayland",
|
"name": "wayland",
|
||||||
@ -129,10 +129,10 @@
|
|||||||
"url": "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz",
|
"url": "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz",
|
||||||
"hash": "sha256-PEKVSUZ6teRbDyhFPWSiuBSe40pgr0kVRivIY8Cn8HQ="
|
"hash": "sha256-PEKVSUZ6teRbDyhFPWSiuBSe40pgr0kVRivIY8Cn8HQ="
|
||||||
},
|
},
|
||||||
"zf-0.10.3-OIRy8bKIAADhjqtdjVaDfONRuI7RVl5gMbhCoOwiBWV5": {
|
"zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9": {
|
||||||
"name": "zf",
|
"name": "zf",
|
||||||
"url": "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz",
|
"url": "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz",
|
||||||
"hash": "sha256-xVva07TAYlVv4E4PKe2wUj86a6Ky2YC30YBgtbvNKvw="
|
"hash": "sha256-3nulNQd/4rZ4paeXJYXwAliNNyRNsIOX/q3z1JB8C7I="
|
||||||
},
|
},
|
||||||
"zg-0.13.4-AAAAAGiZ7QLz4pvECFa_wG4O4TP4FLABHHbemH2KakWM": {
|
"zg-0.13.4-AAAAAGiZ7QLz4pvECFa_wG4O4TP4FLABHHbemH2KakWM": {
|
||||||
"name": "zg",
|
"name": "zg",
|
||||||
@ -154,10 +154,10 @@
|
|||||||
"url": "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz",
|
"url": "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz",
|
||||||
"hash": "sha256-E77GZ15APYbbO1WzmuJi8eG9/iQFbc2CgkNBxjCLUhk="
|
"hash": "sha256-E77GZ15APYbbO1WzmuJi8eG9/iQFbc2CgkNBxjCLUhk="
|
||||||
},
|
},
|
||||||
"zigimg-0.1.0-lly-O4heEADSRxoTwJwrD3TBfUob9052sIgb9SL8Iz-A": {
|
"zigimg-0.1.0-lly-O6N2EABOxke8dqyzCwhtUCAafqP35zC7wsZ4Ddxj": {
|
||||||
"name": "zigimg",
|
"name": "zigimg",
|
||||||
"url": "git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0",
|
"url": "git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d",
|
||||||
"hash": "sha256-Rr+mAfbLOoaxHOwCug+0cWCmW9gDhjhnaO2J/Oik9HI="
|
"hash": "sha256-oblfr2FIzuqq0FLo/RrzCwUX1NJJuT53EwD3nP3KwN0="
|
||||||
},
|
},
|
||||||
"ziglyph-0.11.2-AAAAAHPtHwB4Mbzn1KvOV7Wpjo82NYEc_v0WC8oCLrkf": {
|
"ziglyph-0.11.2-AAAAAHPtHwB4Mbzn1KvOV7Wpjo82NYEc_v0WC8oCLrkf": {
|
||||||
"name": "ziglyph",
|
"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 {
|
path = fetchZigArtifact {
|
||||||
name = "iterm2_themes";
|
name = "iterm2_themes";
|
||||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz";
|
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4c57d8c11d352a4aeda6928b65d78794c28883a5.tar.gz";
|
||||||
hash = "sha256-nOkH31MQQd2PPdjVpRxBxNQWfR9Exg6nRF/KHgSz3cM=";
|
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 {
|
path = fetchZigArtifact {
|
||||||
name = "vaxis";
|
name = "vaxis";
|
||||||
url = "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447";
|
url = "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23";
|
||||||
hash = "sha256-iONEySjPeD0WYJ93fw5mxT+0pVfUO/m6008J/LXjQkA=";
|
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 {
|
path = fetchZigArtifact {
|
||||||
name = "zf";
|
name = "zf";
|
||||||
url = "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz";
|
url = "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz";
|
||||||
hash = "sha256-xVva07TAYlVv4E4PKe2wUj86a6Ky2YC30YBgtbvNKvw=";
|
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 {
|
path = fetchZigArtifact {
|
||||||
name = "zigimg";
|
name = "zigimg";
|
||||||
url = "git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0";
|
url = "git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d";
|
||||||
hash = "sha256-Rr+mAfbLOoaxHOwCug+0cWCmW9gDhjhnaO2J/Oik9HI=";
|
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://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc
|
||||||
git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0
|
git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d
|
||||||
git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447
|
git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23
|
||||||
https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz
|
https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz
|
||||||
https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz
|
https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz
|
||||||
https://deps.files.ghostty.org/fontconfig-2.14.2.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://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
|
||||||
https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.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/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/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz
|
||||||
https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.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
|
https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
||||||
zig = zig.packages.${system}."0.14.0";
|
zig = zig.packages.${system}."0.14.0";
|
||||||
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
||||||
|
uv = pkgs-unstable.uv;
|
||||||
# remove once blueprint-compiler 0.16.0 is in the stable nixpkgs
|
# remove once blueprint-compiler 0.16.0 is in the stable nixpkgs
|
||||||
blueprint-compiler = pkgs-unstable.blueprint-compiler;
|
blueprint-compiler = pkgs-unstable.blueprint-compiler;
|
||||||
zon2nix = zon2nix;
|
zon2nix = zon2nix;
|
||||||
|
@ -91,6 +91,12 @@ class TerminalController: BaseTerminalController {
|
|||||||
name: Ghostty.Notification.didEqualizeSplits,
|
name: Ghostty.Notification.didEqualizeSplits,
|
||||||
object: nil
|
object: nil
|
||||||
)
|
)
|
||||||
|
center.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(onCloseWindow),
|
||||||
|
name: .ghosttyCloseWindow,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -842,6 +848,12 @@ class TerminalController: BaseTerminalController {
|
|||||||
closeTab(self)
|
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) {
|
@objc private func onResetWindowSize(notification: SwiftUI.Notification) {
|
||||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
guard surfaceTree?.contains(view: target) ?? false else { return }
|
guard surfaceTree?.contains(view: target) ?? false else { return }
|
||||||
|
@ -451,6 +451,9 @@ extension Ghostty {
|
|||||||
case GHOSTTY_ACTION_CLOSE_TAB:
|
case GHOSTTY_ACTION_CLOSE_TAB:
|
||||||
closeTab(app, target: target)
|
closeTab(app, target: target)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_CLOSE_WINDOW:
|
||||||
|
closeWindow(app, target: target)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
||||||
toggleFullscreen(app, target: target, mode: action.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(
|
private static func toggleFullscreen(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
|
@ -248,6 +248,9 @@ extension Notification.Name {
|
|||||||
/// Close tab
|
/// Close tab
|
||||||
static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab")
|
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.
|
/// Resize the window to a default size.
|
||||||
static let ghosttyResetWindowSize = Notification.Name("com.mitchellh.ghostty.resetWindowSize")
|
static let ghosttyResetWindowSize = Notification.Name("com.mitchellh.ghostty.resetWindowSize")
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,14 @@ extension Ghostty {
|
|||||||
self.eventMonitor = NSEvent.addLocalMonitorForEvents(
|
self.eventMonitor = NSEvent.addLocalMonitorForEvents(
|
||||||
matching: [
|
matching: [
|
||||||
// We need keyUp because command+key events don't trigger keyUp.
|
// 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) }
|
) { [weak self] event in self?.localEventHandler(event) }
|
||||||
|
|
||||||
@ -450,11 +457,40 @@ extension Ghostty {
|
|||||||
case .keyUp:
|
case .keyUp:
|
||||||
localEventKeyUp(event)
|
localEventKeyUp(event)
|
||||||
|
|
||||||
|
case .leftMouseDown:
|
||||||
|
localEventLeftMouseDown(event)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
event
|
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? {
|
private func localEventKeyUp(_ event: NSEvent) -> NSEvent? {
|
||||||
// We only care about events with "command" because all others will
|
// We only care about events with "command" because all others will
|
||||||
// trigger the normal responder chain.
|
// trigger the normal responder chain.
|
||||||
@ -620,14 +656,6 @@ extension Ghostty {
|
|||||||
ghostty_surface_draw(surface);
|
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) {
|
override func mouseDown(with event: NSEvent) {
|
||||||
guard let surface = self.surface else { return }
|
guard let surface = self.surface else { return }
|
||||||
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||||
@ -884,6 +912,11 @@ extension Ghostty {
|
|||||||
nil
|
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])
|
self.interpretKeyEvents([translationEvent])
|
||||||
|
|
||||||
// If our keyboard changed from this we just assume an input method
|
// If our keyboard changed from this we just assume an input method
|
||||||
@ -922,6 +955,34 @@ extension Ghostty {
|
|||||||
_ = keyAction(GHOSTTY_ACTION_RELEASE, event: event)
|
_ = 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
|
/// Special case handling for some control keys
|
||||||
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
@ -975,15 +1036,42 @@ extension Ghostty {
|
|||||||
|
|
||||||
equivalent = "\r"
|
equivalent = "\r"
|
||||||
|
|
||||||
case ".":
|
default:
|
||||||
if (!event.modifierFlags.contains(.command)) {
|
// 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
|
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 all other non-command events. This lets the event continue
|
||||||
// Ignore other events
|
// 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
|
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) {
|
override func doCommand(by selector: Selector) {
|
||||||
// This currently just prevents NSBeep from interpretKeyEvents but in the future
|
// If we are being processed by performKeyEquivalent with a command binding,
|
||||||
// we may want to make some of this work.
|
// 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)")
|
print("SEL: \(selector)")
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
pandoc,
|
pandoc,
|
||||||
hyperfine,
|
hyperfine,
|
||||||
typos,
|
typos,
|
||||||
|
uv,
|
||||||
wayland,
|
wayland,
|
||||||
wayland-scanner,
|
wayland-scanner,
|
||||||
wayland-protocols,
|
wayland-protocols,
|
||||||
@ -109,6 +110,9 @@ in
|
|||||||
# Localization
|
# Localization
|
||||||
gettext
|
gettext
|
||||||
|
|
||||||
|
# CI
|
||||||
|
uv
|
||||||
|
|
||||||
# We need these GTK-related deps on all platform so we can build
|
# We need these GTK-related deps on all platform so we can build
|
||||||
# dist tarballs.
|
# dist tarballs.
|
||||||
blueprint-compiler
|
blueprint-compiler
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
in
|
in
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
pname = "ghostty";
|
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
|
# 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
|
# 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;
|
const PreExecFn = fn (*Command) void;
|
||||||
|
|
||||||
/// Path to the command to run. This must be an absolute path. This
|
/// Path to the command to run. This doesn't have to be an absolute path,
|
||||||
/// library does not do PATH lookup.
|
/// because use exec functions that search the PATH, if necessary.
|
||||||
path: []const u8,
|
///
|
||||||
|
/// 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
|
/// 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
|
/// args[0] to the command. If args is empty then args[0] will automatically
|
||||||
/// be set to equal path.
|
/// 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
|
/// Environment variables for the child process. If this is null, inherits
|
||||||
/// the environment variables from this process. These are the exact
|
/// 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 {
|
fn startPosix(self: *Command, arena: Allocator) !void {
|
||||||
// Null-terminate all our arguments
|
// Null-terminate all our arguments
|
||||||
const pathZ = try arena.dupeZ(u8, self.path);
|
const argsZ = try arena.allocSentinel(?[*:0]const u8, self.args.len, null);
|
||||||
const argsZ = try arena.allocSentinel(?[*:0]u8, self.args.len, null);
|
for (self.args, 0..) |arg, i| argsZ[i] = arg.ptr;
|
||||||
for (self.args, 0..) |arg, i| argsZ[i] = (try arena.dupeZ(u8, arg)).ptr;
|
|
||||||
|
|
||||||
// Determine our env vars
|
// Determine our env vars
|
||||||
const envp = if (self.env) |env_map|
|
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);
|
if (self.pre_exec) |f| f(self);
|
||||||
|
|
||||||
// Finally, replace our process.
|
// 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,
|
// If we are executing this code, the exec failed. In that scenario,
|
||||||
// we return a very specific error that can be detected to determine
|
// 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
|
// 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
|
config.@"initial-command" orelse config.command
|
||||||
else
|
else
|
||||||
config.command;
|
config.command;
|
||||||
@ -650,8 +650,7 @@ pub fn init(
|
|||||||
// title to the command being executed. This allows window managers
|
// title to the command being executed. This allows window managers
|
||||||
// to set custom styling based on the command being executed.
|
// to set custom styling based on the command being executed.
|
||||||
const v = command orelse break :xdg;
|
const v = command orelse break :xdg;
|
||||||
if (v.len > 0) {
|
const title = v.string(alloc) catch |err| {
|
||||||
const title = alloc.dupeZ(u8, v) catch |err| {
|
|
||||||
log.warn(
|
log.warn(
|
||||||
"error copying command for title, title will not be set err={}",
|
"error copying command for title, title will not be set err={}",
|
||||||
.{err},
|
.{err},
|
||||||
@ -665,7 +664,6 @@ pub fn init(
|
|||||||
.{ .title = title },
|
.{ .title = title },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We are no longer the first surface
|
// We are no longer the first surface
|
||||||
app.first = false;
|
app.first = false;
|
||||||
|
@ -311,7 +311,7 @@ pub const Action = union(Key) {
|
|||||||
|
|
||||||
break :cvalue @Type(.{ .@"union" = .{
|
break :cvalue @Type(.{ .@"union" = .{
|
||||||
.layout = .@"extern",
|
.layout = .@"extern",
|
||||||
.tag_type = Key,
|
.tag_type = null,
|
||||||
.fields = &union_fields,
|
.fields = &union_fields,
|
||||||
.decls = &.{},
|
.decls = &.{},
|
||||||
} });
|
} });
|
||||||
@ -323,6 +323,13 @@ pub const Action = union(Key) {
|
|||||||
value: CValue,
|
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.
|
/// Returns the value type for the given key.
|
||||||
pub fn Value(comptime key: Key) type {
|
pub fn Value(comptime key: Key) type {
|
||||||
inline for (@typeInfo(Action).@"union".fields) |field| {
|
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 command to run in the new surface. If this is set then
|
||||||
/// the "wait-after-command" option is also automatically set to true,
|
/// the "wait-after-command" option is also automatically set to true,
|
||||||
/// since this is used for scripting.
|
/// 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 = "",
|
command: [*:0]const u8 = "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -696,7 +701,7 @@ pub const Surface = struct {
|
|||||||
// If we have a command from the options then we set it.
|
// If we have a command from the options then we set it.
|
||||||
const cmd = std.mem.sliceTo(opts.command, 0);
|
const cmd = std.mem.sliceTo(opts.command, 0);
|
||||||
if (cmd.len > 0) {
|
if (cmd.len > 0) {
|
||||||
config.command = cmd;
|
config.command = .{ .shell = cmd };
|
||||||
config.@"wait-after-command" = true;
|
config.@"wait-after-command" = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,8 +314,8 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
|||||||
.prefer_dark;
|
.prefer_dark;
|
||||||
},
|
},
|
||||||
.system => .prefer_light,
|
.system => .prefer_light,
|
||||||
.dark => .prefer_dark,
|
.dark => .force_dark,
|
||||||
.light => .force_dark,
|
.light => .force_light,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -281,6 +281,15 @@ pub fn init(self: *Window, app: *App) !void {
|
|||||||
.detail = "is-active",
|
.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
|
// 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.
|
// 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;
|
if (self.isQuickTerminal()) break :visible false;
|
||||||
|
|
||||||
// Unconditionally disable the header bar when fullscreened.
|
// 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,
|
// *Conditionally* disable the header bar when maximized,
|
||||||
// and gtk-titlebar-hide-when-maximized is set
|
// 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 false;
|
||||||
|
|
||||||
break :visible self.config.gtk_titlebar;
|
break :visible self.config.gtk_titlebar;
|
||||||
@ -672,7 +683,7 @@ pub fn toggleTabOverview(self: *Window) void {
|
|||||||
|
|
||||||
/// Toggle the maximized state for this window.
|
/// Toggle the maximized state for this window.
|
||||||
pub fn toggleMaximize(self: *Window) void {
|
pub fn toggleMaximize(self: *Window) void {
|
||||||
if (self.config.maximize) {
|
if (self.window.as(gtk.Window).isMaximized() != 0) {
|
||||||
self.window.as(gtk.Window).unmaximize();
|
self.window.as(gtk.Window).unmaximize();
|
||||||
} else {
|
} else {
|
||||||
self.window.as(gtk.Window).maximize();
|
self.window.as(gtk.Window).maximize();
|
||||||
@ -683,7 +694,7 @@ pub fn toggleMaximize(self: *Window) void {
|
|||||||
|
|
||||||
/// Toggle fullscreen for this window.
|
/// Toggle fullscreen for this window.
|
||||||
pub fn toggleFullscreen(self: *Window) void {
|
pub fn toggleFullscreen(self: *Window) void {
|
||||||
if (self.config.fullscreen) {
|
if (self.window.as(gtk.Window).isFullscreen() != 0) {
|
||||||
self.window.as(gtk.Window).unfullscreen();
|
self.window.as(gtk.Window).unfullscreen();
|
||||||
} else {
|
} else {
|
||||||
self.window.as(gtk.Window).fullscreen();
|
self.window.as(gtk.Window).fullscreen();
|
||||||
@ -754,7 +765,6 @@ fn gtkWindowNotifyMaximized(
|
|||||||
_: *gobject.ParamSpec,
|
_: *gobject.ParamSpec,
|
||||||
self: *Window,
|
self: *Window,
|
||||||
) callconv(.c) void {
|
) callconv(.c) void {
|
||||||
self.config.maximize = self.window.as(gtk.Window).isMaximized() != 0;
|
|
||||||
self.syncAppearance() catch |err| {
|
self.syncAppearance() catch |err| {
|
||||||
log.err("failed to sync appearance={}", .{err});
|
log.err("failed to sync appearance={}", .{err});
|
||||||
};
|
};
|
||||||
@ -765,7 +775,6 @@ fn gtkWindowNotifyFullscreened(
|
|||||||
_: *gobject.ParamSpec,
|
_: *gobject.ParamSpec,
|
||||||
self: *Window,
|
self: *Window,
|
||||||
) callconv(.c) void {
|
) callconv(.c) void {
|
||||||
self.config.fullscreen = self.window.as(gtk.Window).isFullscreen() != 0;
|
|
||||||
self.syncAppearance() catch |err| {
|
self.syncAppearance() catch |err| {
|
||||||
log.err("failed to sync appearance={}", .{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
|
// Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab
|
||||||
// sends an undefined value.
|
// sends an undefined value.
|
||||||
fn gtkTabNewClick(_: *gtk.Button, self: *Window) callconv(.c) void {
|
fn gtkTabNewClick(_: *gtk.Button, self: *Window) callconv(.c) void {
|
||||||
|
@ -219,13 +219,12 @@ pub const Window = struct {
|
|||||||
|
|
||||||
pub fn resizeEvent(self: *Window) !void {
|
pub fn resizeEvent(self: *Window) !void {
|
||||||
// The blur region must update with window resizes
|
// 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();
|
try self.syncBlur();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn syncAppearance(self: *Window) !void {
|
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: {
|
self.blur_region = blur: {
|
||||||
// NOTE(pluiedev): CSDs are a f--king mistake.
|
// NOTE(pluiedev): CSDs are a f--king mistake.
|
||||||
// Please, GNOME, stop this nonsense of making a window ~30% bigger
|
// 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);
|
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 .{
|
break :blur .{
|
||||||
.x = @intFromFloat(x),
|
.x = @intFromFloat(x),
|
||||||
.y = @intFromFloat(y),
|
.y = @intFromFloat(y),
|
||||||
@ -265,6 +269,13 @@ pub const Window = struct {
|
|||||||
// and I think it's not really noticeable enough to justify the effort.
|
// and I think it's not really noticeable enough to justify the effort.
|
||||||
// (Wayland also has this visual artifact anyway...)
|
// (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;
|
const blur = self.config.background_blur;
|
||||||
log.debug("set blur={}, window xid={}, region={}", .{
|
log.debug("set blur={}, window xid={}, region={}", .{
|
||||||
blur,
|
blur,
|
||||||
|
@ -19,7 +19,7 @@ const GitVersion = @import("GitVersion.zig");
|
|||||||
/// TODO: When Zig 0.14 is released, derive this from build.zig.zon directly.
|
/// 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
|
/// Until then this MUST match build.zig.zon and should always be the
|
||||||
/// _next_ version to release.
|
/// _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.
|
/// Standard build configuration options.
|
||||||
optimize: std.builtin.OptimizeMode,
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
@ -14,6 +14,7 @@ pub const formatEntry = formatter.formatEntry;
|
|||||||
|
|
||||||
// Field types
|
// Field types
|
||||||
pub const ClipboardAccess = Config.ClipboardAccess;
|
pub const ClipboardAccess = Config.ClipboardAccess;
|
||||||
|
pub const Command = Config.Command;
|
||||||
pub const ConfirmCloseSurface = Config.ConfirmCloseSurface;
|
pub const ConfirmCloseSurface = Config.ConfirmCloseSurface;
|
||||||
pub const CopyOnSelect = Config.CopyOnSelect;
|
pub const CopyOnSelect = Config.CopyOnSelect;
|
||||||
pub const CustomShaderAnimation = Config.CustomShaderAnimation;
|
pub const CustomShaderAnimation = Config.CustomShaderAnimation;
|
||||||
|
@ -22,7 +22,6 @@ const inputpkg = @import("../input.zig");
|
|||||||
const terminal = @import("../terminal/main.zig");
|
const terminal = @import("../terminal/main.zig");
|
||||||
const internal_os = @import("../os/main.zig");
|
const internal_os = @import("../os/main.zig");
|
||||||
const cli = @import("../cli.zig");
|
const cli = @import("../cli.zig");
|
||||||
const Command = @import("../Command.zig");
|
|
||||||
|
|
||||||
const conditional = @import("conditional.zig");
|
const conditional = @import("conditional.zig");
|
||||||
const Conditional = conditional.Conditional;
|
const Conditional = conditional.Conditional;
|
||||||
@ -34,6 +33,7 @@ const KeyValue = @import("key.zig").Value;
|
|||||||
const ErrorList = @import("ErrorList.zig");
|
const ErrorList = @import("ErrorList.zig");
|
||||||
const MetricModifier = fontpkg.Metrics.Modifier;
|
const MetricModifier = fontpkg.Metrics.Modifier;
|
||||||
const help_strings = @import("help_strings");
|
const help_strings = @import("help_strings");
|
||||||
|
pub const Command = @import("command.zig").Command;
|
||||||
const RepeatableStringMap = @import("RepeatableStringMap.zig");
|
const RepeatableStringMap = @import("RepeatableStringMap.zig");
|
||||||
pub const Path = @import("path.zig").Path;
|
pub const Path = @import("path.zig").Path;
|
||||||
pub const RepeatablePath = @import("path.zig").RepeatablePath;
|
pub const RepeatablePath = @import("path.zig").RepeatablePath;
|
||||||
@ -691,8 +691,17 @@ palette: Palette = .{},
|
|||||||
/// * `passwd` entry (user information)
|
/// * `passwd` entry (user information)
|
||||||
///
|
///
|
||||||
/// This can contain additional arguments to run the command with. If additional
|
/// This can contain additional arguments to run the command with. If additional
|
||||||
/// arguments are provided, the command will be executed using `/bin/sh -c`.
|
/// arguments are provided, the command will be executed using `/bin/sh -c`
|
||||||
/// Ghostty does not do any shell command parsing.
|
/// 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,
|
/// 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
|
/// 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`.
|
/// arguments. For example, `ghostty -e fish --with --custom --args`.
|
||||||
/// This flag sets the `initial-command` configuration, see that for more
|
/// This flag sets the `initial-command` configuration, see that for more
|
||||||
/// information.
|
/// information.
|
||||||
command: ?[]const u8 = null,
|
command: ?Command = null,
|
||||||
|
|
||||||
/// This is the same as "command", but only applies to the first terminal
|
/// This is the same as "command", but only applies to the first terminal
|
||||||
/// surface created when Ghostty starts. Subsequent terminal surfaces will use
|
/// 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
|
/// fish --with --custom --args`. The `-e` flag automatically forces some
|
||||||
/// other behaviors as well:
|
/// 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
|
/// * `gtk-single-instance=false` - This ensures that a new instance is
|
||||||
/// launched and the CLI args are respected.
|
/// 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
|
/// name your binary appropriately or source the shell integration script
|
||||||
/// manually.
|
/// manually.
|
||||||
///
|
///
|
||||||
@"initial-command": ?[]const u8 = null,
|
@"initial-command": ?Command = null,
|
||||||
|
|
||||||
/// Extra environment variables to pass to commands launched in a terminal
|
/// Extra environment variables to pass to commands launched in a terminal
|
||||||
/// surface. The format is `env=KEY=VALUE`.
|
/// surface. The format is `env=KEY=VALUE`.
|
||||||
@ -826,7 +839,7 @@ env: RepeatableStringMap = .{},
|
|||||||
link: RepeatableLink = .{},
|
link: RepeatableLink = .{},
|
||||||
|
|
||||||
/// Enable URL matching. URLs are matched on hover with control (Linux) or
|
/// 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 linked URL.
|
||||||
///
|
///
|
||||||
/// The URL matcher is always lowest priority of any configured links (see
|
/// 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
|
// Next, take all remaining args and use that to build up
|
||||||
// a command to execute.
|
// a command to execute.
|
||||||
var command = std.ArrayList(u8).init(arena_alloc);
|
var builder = std.ArrayList([:0]const u8).init(arena_alloc);
|
||||||
errdefer command.deinit();
|
errdefer builder.deinit();
|
||||||
for (args) |arg_raw| {
|
for (args) |arg_raw| {
|
||||||
const arg = std.mem.sliceTo(arg_raw, 0);
|
const arg = std.mem.sliceTo(arg_raw, 0);
|
||||||
try self._replay_steps.append(
|
const copy = try arena_alloc.dupeZ(u8, arg);
|
||||||
arena_alloc,
|
try self._replay_steps.append(arena_alloc, .{ .arg = copy });
|
||||||
.{ .arg = try arena_alloc.dupe(u8, arg) },
|
try builder.append(copy);
|
||||||
);
|
|
||||||
|
|
||||||
try command.appendSlice(arg);
|
|
||||||
try command.append(' ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.@"_xdg-terminal-exec" = true;
|
self.@"_xdg-terminal-exec" = true;
|
||||||
self.@"initial-command" = command.items[0 .. command.items.len - 1];
|
self.@"initial-command" = .{ .direct = try builder.toOwnedSlice() };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3023,7 +3032,7 @@ pub fn finalize(self: *Config) !void {
|
|||||||
// We don't do this in flatpak because SHELL in Flatpak is always
|
// We don't do this in flatpak because SHELL in Flatpak is always
|
||||||
// set to /bin/sh.
|
// set to /bin/sh.
|
||||||
if (self.command) |cmd|
|
if (self.command) |cmd|
|
||||||
log.info("shell src=config value={s}", .{cmd})
|
log.info("shell src=config value={}", .{cmd})
|
||||||
else shell_env: {
|
else shell_env: {
|
||||||
// Flatpak always gets its shell from outside the sandbox
|
// Flatpak always gets its shell from outside the sandbox
|
||||||
if (internal_os.isFlatpak()) break :shell_env;
|
if (internal_os.isFlatpak()) break :shell_env;
|
||||||
@ -3035,7 +3044,9 @@ pub fn finalize(self: *Config) !void {
|
|||||||
|
|
||||||
if (std.process.getEnvVarOwned(alloc, "SHELL")) |value| {
|
if (std.process.getEnvVarOwned(alloc, "SHELL")) |value| {
|
||||||
log.info("default shell source=env value={s}", .{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 we don't need the working directory, then we can exit now.
|
||||||
if (!wd_home) break :command;
|
if (!wd_home) break :command;
|
||||||
@ -3046,7 +3057,7 @@ pub fn finalize(self: *Config) !void {
|
|||||||
.windows => {
|
.windows => {
|
||||||
if (self.command == null) {
|
if (self.command == null) {
|
||||||
log.warn("no default shell found, will default to using cmd", .{});
|
log.warn("no default shell found, will default to using cmd", .{});
|
||||||
self.command = "cmd.exe";
|
self.command = .{ .shell = "cmd.exe" };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wd_home) {
|
if (wd_home) {
|
||||||
@ -3063,7 +3074,7 @@ pub fn finalize(self: *Config) !void {
|
|||||||
if (self.command == null) {
|
if (self.command == null) {
|
||||||
if (pw.shell) |sh| {
|
if (pw.shell) |sh| {
|
||||||
log.info("default shell src=passwd value={s}", .{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
|
// Build up the command. We don't clean this up because we take
|
||||||
// ownership in our allocator.
|
// ownership in our allocator.
|
||||||
var command = std.ArrayList(u8).init(alloc);
|
var command: std.ArrayList([:0]const u8) = .init(alloc);
|
||||||
errdefer command.deinit();
|
errdefer command.deinit();
|
||||||
|
|
||||||
while (iter.next()) |param| {
|
while (iter.next()) |param| {
|
||||||
try self._replay_steps.append(alloc, .{ .arg = try alloc.dupe(u8, param) });
|
const copy = try alloc.dupeZ(u8, param);
|
||||||
try command.appendSlice(param);
|
try self._replay_steps.append(alloc, .{ .arg = copy });
|
||||||
try command.append(' ');
|
try command.append(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.items.len == 0) {
|
if (command.items.len == 0) {
|
||||||
@ -3167,9 +3178,8 @@ pub fn parseManuallyHook(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.@"initial-command" = command.items[0 .. command.items.len - 1];
|
|
||||||
|
|
||||||
// See "command" docs for the implied configurations and why.
|
// See "command" docs for the implied configurations and why.
|
||||||
|
self.@"initial-command" = .{ .direct = command.items };
|
||||||
self.@"gtk-single-instance" = .false;
|
self.@"gtk-single-instance" = .false;
|
||||||
self.@"quit-after-last-window-closed" = true;
|
self.@"quit-after-last-window-closed" = true;
|
||||||
self.@"quit-after-last-window-closed-delay" = null;
|
self.@"quit-after-last-window-closed-delay" = null;
|
||||||
@ -3184,7 +3194,7 @@ pub fn parseManuallyHook(
|
|||||||
// Keep track of our input args for replay
|
// Keep track of our input args for replay
|
||||||
try self._replay_steps.append(
|
try self._replay_steps.append(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .arg = try alloc.dupe(u8, arg) },
|
.{ .arg = try alloc.dupeZ(u8, arg) },
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we didn't find a special case, continue parsing normally
|
// 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,
|
[:0]const u8,
|
||||||
=> return std.mem.eql(u8, old, new),
|
=> 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 => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3412,6 +3432,8 @@ fn equalField(comptime T: type, old: T, new: T) bool {
|
|||||||
},
|
},
|
||||||
|
|
||||||
.@"union" => |info| {
|
.@"union" => |info| {
|
||||||
|
if (@hasDecl(T, "equal")) return old.equal(new);
|
||||||
|
|
||||||
const tag_type = info.tag_type.?;
|
const tag_type = info.tag_type.?;
|
||||||
const old_tag = std.meta.activeTag(old);
|
const old_tag = std.meta.activeTag(old);
|
||||||
const new_tag = std.meta.activeTag(new);
|
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 Replay = struct {
|
||||||
const Step = union(enum) {
|
const Step = union(enum) {
|
||||||
/// An argument to parse as if it came from the CLI or file.
|
/// 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.
|
/// A base path to expand relative paths against.
|
||||||
expand: []const u8,
|
expand: []const u8,
|
||||||
@ -3481,7 +3503,7 @@ const Replay = struct {
|
|||||||
return switch (self) {
|
return switch (self) {
|
||||||
.@"-e" => self,
|
.@"-e" => self,
|
||||||
.diagnostic => |v| .{ .diagnostic = try v.clone(alloc) },
|
.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) },
|
.expand => |v| .{ .expand = try alloc.dupe(u8, v) },
|
||||||
.conditional_arg => |v| conditional: {
|
.conditional_arg => |v| conditional: {
|
||||||
var conds = try alloc.alloc(Conditional, v.conditions.len);
|
var conds = try alloc.alloc(Conditional, v.conditions.len);
|
||||||
@ -6620,7 +6642,11 @@ test "parse e: command only" {
|
|||||||
|
|
||||||
var it: TestIterator = .{ .data = &.{"foo"} };
|
var it: TestIterator = .{ .data = &.{"foo"} };
|
||||||
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
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" {
|
test "parse e: command and args" {
|
||||||
@ -6631,7 +6657,13 @@ test "parse e: command and args" {
|
|||||||
|
|
||||||
var it: TestIterator = .{ .data = &.{ "echo", "foo", "bar baz" } };
|
var it: TestIterator = .{ .data = &.{ "echo", "foo", "bar baz" } };
|
||||||
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
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" {
|
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.
|
/// 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{
|
pub const locales = [_][:0]const u8{
|
||||||
"de_DE.UTF-8",
|
|
||||||
"zh_CN.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",
|
"nb_NO.UTF-8",
|
||||||
|
"ru_RU.UTF-8",
|
||||||
"uk_UA.UTF-8",
|
"uk_UA.UTF-8",
|
||||||
"pl_PL.UTF-8",
|
"pl_PL.UTF-8",
|
||||||
"mk_MK.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.
|
/// Set for faster membership lookup of locales.
|
||||||
@ -108,6 +123,9 @@ pub fn canonicalizeLocale(
|
|||||||
buf: []u8,
|
buf: []u8,
|
||||||
locale: []const u8,
|
locale: []const u8,
|
||||||
) error{NoSpaceLeft}![:0]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
|
// 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;
|
if (buf.len < @max(16, locale.len + 1)) return error.NoSpaceLeft;
|
||||||
|
|
||||||
@ -126,6 +144,30 @@ pub fn canonicalizeLocale(
|
|||||||
return buf[0..slice.len :0];
|
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
|
/// This can be called at any point a compile-time-known locale is
|
||||||
/// available. This will use comptime to verify the locale is supported.
|
/// available. This will use comptime to verify the locale is supported.
|
||||||
pub fn staticLocale(comptime v: [*:0]const u8) [*:0]const u8 {
|
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_CN", try canonicalizeLocale(&buf, "zh-Hans"));
|
||||||
try testing.expectEqualStrings("zh_TW", try canonicalizeLocale(&buf, "zh-Hant"));
|
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:
|
// 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
|
// canonicalizeLocale does not handle encodings and will turn them into
|
||||||
// underscores. We should parse them out before calling this function.
|
// 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
|
// Entry that is retrieved from the passwd API. This only contains the fields
|
||||||
// we care about.
|
// we care about.
|
||||||
pub const Entry = struct {
|
pub const Entry = struct {
|
||||||
shell: ?[]const u8 = null,
|
shell: ?[:0]const u8 = null,
|
||||||
home: ?[]const u8 = null,
|
home: ?[:0]const u8 = null,
|
||||||
name: ?[]const u8 = null,
|
name: ?[:0]const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Get the passwd entry for the currently executing user.
|
/// 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
|
// Shell and home are the last two entries
|
||||||
var it = std.mem.splitBackwardsScalar(u8, std.mem.trimRight(u8, output, " \r\n"), ':');
|
var it = std.mem.splitBackwardsScalar(u8, std.mem.trimRight(u8, output, " \r\n"), ':');
|
||||||
result.shell = it.next() orelse null;
|
result.shell = if (it.next()) |v| try alloc.dupeZ(u8, v) else null;
|
||||||
result.home = it.next() orelse null;
|
result.home = if (it.next()) |v| try alloc.dupeZ(u8, v) else null;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pw.pw_shell) |ptr| {
|
if (pw.pw_shell) |ptr| {
|
||||||
const source = std.mem.sliceTo(ptr, 0);
|
const source = std.mem.sliceTo(ptr, 0);
|
||||||
const sh = try alloc.alloc(u8, source.len);
|
const value = try alloc.dupeZ(u8, source);
|
||||||
@memcpy(sh, source);
|
result.shell = value;
|
||||||
result.shell = sh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pw.pw_dir) |ptr| {
|
if (pw.pw_dir) |ptr| {
|
||||||
const source = std.mem.sliceTo(ptr, 0);
|
const source = std.mem.sliceTo(ptr, 0);
|
||||||
const dir = try alloc.alloc(u8, source.len);
|
const value = try alloc.dupeZ(u8, source);
|
||||||
@memcpy(dir, source);
|
result.home = value;
|
||||||
result.home = dir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pw.pw_name) |ptr| {
|
if (pw.pw_name) |ptr| {
|
||||||
const source = std.mem.sliceTo(ptr, 0);
|
const source = std.mem.sliceTo(ptr, 0);
|
||||||
const name = try alloc.alloc(u8, source.len);
|
const value = try alloc.dupeZ(u8, source);
|
||||||
@memcpy(name, source);
|
result.name = value;
|
||||||
result.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -23,6 +23,8 @@ pub fn ShellEscapeWriter(comptime T: type) type {
|
|||||||
'?',
|
'?',
|
||||||
' ',
|
' ',
|
||||||
'|',
|
'|',
|
||||||
|
'(',
|
||||||
|
')',
|
||||||
=> &[_]u8{ '\\', byte },
|
=> &[_]u8{ '\\', byte },
|
||||||
else => &[_]u8{byte},
|
else => &[_]u8{byte},
|
||||||
};
|
};
|
||||||
@ -93,3 +95,12 @@ test "shell escape 6" {
|
|||||||
try writer.writeAll("a\"c");
|
try writer.writeAll("a\"c");
|
||||||
try testing.expectEqualStrings("a\\\"c", fmt.getWritten());
|
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 we're constrained then we need to scale the glyph.
|
||||||
if (in.mode == MODE_TEXT_CONSTRAINED) {
|
if (in.mode == MODE_TEXT_CONSTRAINED) {
|
||||||
float max_width = uniforms.cell_size.x * in.constraint_width;
|
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) {
|
if (size.x > max_width) {
|
||||||
float new_y = size.y * (max_width / size.x);
|
float new_y = size.y * (max_width / size.x);
|
||||||
offset.y += (size.y - new_y) / 2;
|
offset.y += (size.y - new_y) / 2;
|
||||||
|
offset.x = 0;
|
||||||
size.y = new_y;
|
size.y = new_y;
|
||||||
size.x = max_width;
|
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(
|
constexpr sampler textureSampler(
|
||||||
coord::pixel,
|
coord::pixel,
|
||||||
address::clamp_to_edge,
|
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) {
|
switch (in.mode) {
|
||||||
|
@ -70,7 +70,7 @@ if [ -n "$GHOSTTY_BASH_INJECT" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Sudo
|
# 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.
|
# Wrap `sudo` command to ensure Ghostty terminfo is preserved.
|
||||||
#
|
#
|
||||||
# This approach supports wrapping a `sudo` alias, but the alias definition
|
# This approach supports wrapping a `sudo` alias, but the alias definition
|
||||||
@ -124,13 +124,13 @@ function __ghostty_precmd() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Cursor
|
# Cursor
|
||||||
if test "$GHOSTTY_SHELL_INTEGRATION_NO_CURSOR" != "1"; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
|
||||||
PS1=$PS1'\[\e[5 q\]'
|
PS1=$PS1'\[\e[5 q\]'
|
||||||
PS0=$PS0'\[\e[0 q\]'
|
PS0=$PS0'\[\e[0 q\]'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Title (working directory)
|
# Title (working directory)
|
||||||
if [[ "$GHOSTTY_SHELL_INTEGRATION_NO_TITLE" != 1 ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
|
||||||
PS1=$PS1'\[\e]2;\w\a\]'
|
PS1=$PS1'\[\e]2;\w\a\]'
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@ -161,7 +161,7 @@ function __ghostty_preexec() {
|
|||||||
PS2="$_GHOSTTY_SAVE_PS2"
|
PS2="$_GHOSTTY_SAVE_PS2"
|
||||||
|
|
||||||
# Title (current command)
|
# 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:]]}"
|
builtin printf "\e]2;%s\a" "${cmd//[[:cntrl:]]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -36,6 +36,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
use str
|
||||||
|
|
||||||
# helper used by `mark-*` functions
|
# helper used by `mark-*` functions
|
||||||
fn set-prompt-state {|new| set-env __ghostty_prompt_state $new }
|
fn set-prompt-state {|new| set-env __ghostty_prompt_state $new }
|
||||||
|
|
||||||
@ -73,7 +75,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn report-pwd {
|
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|
|
fn sudo-with-terminfo {|@args|
|
||||||
@ -104,20 +107,18 @@
|
|||||||
set edit:after-readline = (conj $edit:after-readline $mark-output-start~)
|
set edit:after-readline = (conj $edit:after-readline $mark-output-start~)
|
||||||
set edit:after-command = (conj $edit:after-command $mark-output-end~)
|
set edit:after-command = (conj $edit:after-command $mark-output-end~)
|
||||||
|
|
||||||
var no-title = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_TITLE)
|
var features = [(str:split ',' $E:GHOSTTY_SHELL_FEATURES)]
|
||||||
var no-cursor = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_CURSOR)
|
|
||||||
var no-sudo = (eq 1 $E:GHOSTTY_SHELL_INTEGRATION_NO_SUDO)
|
|
||||||
|
|
||||||
if (not $no-title) {
|
if (has-value $features title) {
|
||||||
set after-chdir = (conj $after-chdir {|_| report-pwd })
|
set after-chdir = (conj $after-chdir {|_| report-pwd })
|
||||||
}
|
}
|
||||||
if (not $no-cursor) {
|
if (has-value $features cursor) {
|
||||||
fn beam { printf "\e[5 q" }
|
fn beam { printf "\e[5 q" }
|
||||||
fn block { printf "\e[0 q" }
|
fn block { printf "\e[0 q" }
|
||||||
set edit:before-readline = (conj $edit:before-readline $beam~)
|
set edit:before-readline = (conj $edit:before-readline $beam~)
|
||||||
set edit:after-readline = (conj $edit:after-readline {|_| block })
|
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~
|
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"
|
function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
|
||||||
functions -e __ghostty_setup
|
functions -e __ghostty_setup
|
||||||
|
|
||||||
# Check if we are setting cursors
|
set --local features (string split , $GHOSTTY_SHELL_FEATURES)
|
||||||
set --local no_cursor "$GHOSTTY_SHELL_INTEGRATION_NO_CURSOR"
|
|
||||||
|
|
||||||
if test -z $no_cursor
|
if contains cursor $features
|
||||||
# Change the cursor to a beam on prompt.
|
# Change the cursor to a beam on prompt.
|
||||||
function __ghostty_set_cursor_beam --on-event fish_prompt -d "Set cursor shape"
|
function __ghostty_set_cursor_beam --on-event fish_prompt -d "Set cursor shape"
|
||||||
echo -en "\e[5 q"
|
echo -en "\e[5 q"
|
||||||
@ -62,13 +61,9 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
|
|||||||
end
|
end
|
||||||
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
|
# When using sudo shell integration feature, ensure $TERMINFO is set
|
||||||
# and `sudo` is not already a function or alias
|
# and `sudo` is not already a function or alias
|
||||||
if test -z $no_sudo
|
if contains sudo $features; and test -n "$TERMINFO"; and test "file" = (type -t sudo 2> /dev/null; or echo "x")
|
||||||
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
|
# Wrap `sudo` command to ensure Ghostty terminfo is preserved
|
||||||
function sudo -d "Wrap sudo to preserve terminfo"
|
function sudo -d "Wrap sudo to preserve terminfo"
|
||||||
set --function sudo_has_sudoedit_flags "no"
|
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
|
set --global fish_handle_reflow 1
|
||||||
|
|
||||||
# Initial calls for first prompt
|
# Initial calls for first prompt
|
||||||
if test -z $no_cursor
|
if contains cursor $features
|
||||||
__ghostty_set_cursor_beam
|
__ghostty_set_cursor_beam
|
||||||
end
|
end
|
||||||
__ghostty_mark_prompt_start
|
__ghostty_mark_prompt_start
|
||||||
|
@ -194,7 +194,7 @@ _ghostty_deferred_init() {
|
|||||||
_ghostty_report_pwd"
|
_ghostty_report_pwd"
|
||||||
_ghostty_report_pwd
|
_ghostty_report_pwd
|
||||||
|
|
||||||
if [[ "$GHOSTTY_SHELL_INTEGRATION_NO_TITLE" != 1 ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
|
||||||
# Enable terminal title changes.
|
# Enable terminal title changes.
|
||||||
functions[_ghostty_precmd]+="
|
functions[_ghostty_precmd]+="
|
||||||
builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
|
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'"
|
builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${(V)1}\"\$'\\a'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$GHOSTTY_SHELL_INTEGRATION_NO_CURSOR" != 1 ]]; then
|
if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
|
||||||
# Enable cursor shape changes depending on the current keymap.
|
# Enable cursor shape changes depending on the current keymap.
|
||||||
# This implementation leaks blinking block cursor into external commands
|
# This implementation leaks blinking block cursor into external commands
|
||||||
# executed from zle. For example, users of fzf-based widgets may find
|
# executed from zle. For example, users of fzf-based widgets may find
|
||||||
@ -221,7 +221,7 @@ _ghostty_deferred_init() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Sudo
|
# 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
|
# Wrap `sudo` command to ensure Ghostty terminfo is preserved
|
||||||
sudo() {
|
sudo() {
|
||||||
builtin local sudo_has_sudoedit_flags="no"
|
builtin local sudo_has_sudoedit_flags="no"
|
||||||
|
@ -98,6 +98,12 @@ pub const Parser = struct {
|
|||||||
self.state = .control_value;
|
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),
|
else => try self.accumulateValue(c, .control_key_ignore),
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1053,6 +1059,21 @@ test "delete command" {
|
|||||||
try testing.expectEqual(@as(u32, 4), dv.y);
|
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)" {
|
test "ignore unknown keys (long)" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
@ -24,6 +24,7 @@ const SegmentedPool = @import("../datastruct/main.zig").SegmentedPool;
|
|||||||
const ptypkg = @import("../pty.zig");
|
const ptypkg = @import("../pty.zig");
|
||||||
const Pty = ptypkg.Pty;
|
const Pty = ptypkg.Pty;
|
||||||
const EnvMap = std.process.EnvMap;
|
const EnvMap = std.process.EnvMap;
|
||||||
|
const PasswdEntry = internal_os.passwd.Entry;
|
||||||
const windows = internal_os.windows;
|
const windows = internal_os.windows;
|
||||||
|
|
||||||
const log = std.log.scoped(.io_exec);
|
const log = std.log.scoped(.io_exec);
|
||||||
@ -725,7 +726,7 @@ pub const ThreadData = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Config = struct {
|
pub const Config = struct {
|
||||||
command: ?[]const u8 = null,
|
command: ?configpkg.Command = null,
|
||||||
env: EnvMap,
|
env: EnvMap,
|
||||||
env_override: configpkg.RepeatableStringMap = .{},
|
env_override: configpkg.RepeatableStringMap = .{},
|
||||||
shell_integration: configpkg.Config.ShellIntegration = .detect,
|
shell_integration: configpkg.Config.ShellIntegration = .detect,
|
||||||
@ -746,7 +747,7 @@ const Subprocess = struct {
|
|||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
cwd: ?[]const u8,
|
cwd: ?[]const u8,
|
||||||
env: ?EnvMap,
|
env: ?EnvMap,
|
||||||
args: [][]const u8,
|
args: []const [:0]const u8,
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
screen_size: renderer.ScreenSize,
|
screen_size: renderer.ScreenSize,
|
||||||
pty: ?Pty = null,
|
pty: ?Pty = null,
|
||||||
@ -892,18 +893,29 @@ const Subprocess = struct {
|
|||||||
env.remove("VTE_VERSION");
|
env.remove("VTE_VERSION");
|
||||||
|
|
||||||
// Setup our shell integration, if we can.
|
// Setup our shell integration, if we can.
|
||||||
const integrated_shell: ?shell_integration.Shell, const shell_command: []const u8 = shell: {
|
const shell_command: configpkg.Command = shell: {
|
||||||
const default_shell_command = cfg.command orelse switch (builtin.os.tag) {
|
const default_shell_command: configpkg.Command =
|
||||||
|
cfg.command orelse .{ .shell = switch (builtin.os.tag) {
|
||||||
.windows => "cmd.exe",
|
.windows => "cmd.exe",
|
||||||
else => "sh",
|
else => "sh",
|
||||||
};
|
} };
|
||||||
|
|
||||||
const force: ?shell_integration.Shell = switch (cfg.shell_integration) {
|
const force: ?shell_integration.Shell = switch (cfg.shell_integration) {
|
||||||
.none => {
|
.none => {
|
||||||
// Even if shell integration is none, we still want to set up the feature env vars
|
// Even if shell integration is none, we still want to
|
||||||
try shell_integration.setupFeatures(&env, cfg.shell_integration_features);
|
// set up the feature env vars
|
||||||
break :shell .{ null, default_shell_command };
|
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,
|
.detect => null,
|
||||||
.bash => .bash,
|
.bash => .bash,
|
||||||
.elvish => .elvish,
|
.elvish => .elvish,
|
||||||
@ -911,9 +923,9 @@ const Subprocess = struct {
|
|||||||
.zsh => .zsh,
|
.zsh => .zsh,
|
||||||
};
|
};
|
||||||
|
|
||||||
const dir = cfg.resources_dir orelse break :shell .{
|
const dir = cfg.resources_dir orelse {
|
||||||
null,
|
log.warn("no resources dir set, shell integration disabled", .{});
|
||||||
default_shell_command,
|
break :shell default_shell_command;
|
||||||
};
|
};
|
||||||
|
|
||||||
const integration = try shell_integration.setup(
|
const integration = try shell_integration.setup(
|
||||||
@ -923,19 +935,18 @@ const Subprocess = struct {
|
|||||||
&env,
|
&env,
|
||||||
force,
|
force,
|
||||||
cfg.shell_integration_features,
|
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 .{ integration.shell, integration.command };
|
break :shell default_shell_command;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (integrated_shell) |shell| {
|
|
||||||
log.info(
|
log.info(
|
||||||
"shell integration automatically injected shell={}",
|
"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.
|
// Add the environment variables that override any others.
|
||||||
{
|
{
|
||||||
@ -947,134 +958,29 @@ const Subprocess = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build our args list
|
// Build our args list
|
||||||
const args = args: {
|
const args: []const [:0]const u8 = execCommand(
|
||||||
const cap = 9; // the most we'll ever use
|
|
||||||
var args = try std.ArrayList([]const u8).initCapacity(alloc, cap);
|
|
||||||
defer args.deinit();
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
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,
|
alloc,
|
||||||
"exec -l {s}",
|
shell_command,
|
||||||
.{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", .{});
|
||||||
|
|
||||||
// The reason for executing login this way is unclear. This
|
// The comptime here is important to ensure the full slice
|
||||||
// comment will attempt to explain but prepare for a truly
|
// is put into the binary data and not the stack.
|
||||||
// unhinged reality.
|
break :oom comptime switch (builtin.os.tag) {
|
||||||
//
|
.windows => &.{"cmd.exe"},
|
||||||
// The first major issue is that on macOS, a lot of users
|
else => &.{"/bin/sh"},
|
||||||
// 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
|
// This logs on its own, this is a bad error.
|
||||||
// load startup files so that (1) our shell integration doesn't
|
error.SystemError => return err,
|
||||||
// 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();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// We have to copy the cwd because there is no guarantee that
|
// 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.
|
/// bash in particular it may be different.
|
||||||
///
|
///
|
||||||
/// The memory is allocated in the arena given to setup.
|
/// 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
|
/// integrated shell integration and return a ShellIntegration
|
||||||
/// struct describing the integration. If integration fails
|
/// struct describing the integration. If integration fails
|
||||||
/// (shell type couldn't be detected, etc.), this will return null.
|
/// (shell type couldn't be detected, etc.), this will return null.
|
||||||
@ -41,7 +41,7 @@ pub const ShellIntegration = struct {
|
|||||||
pub fn setup(
|
pub fn setup(
|
||||||
alloc_arena: Allocator,
|
alloc_arena: Allocator,
|
||||||
resource_dir: []const u8,
|
resource_dir: []const u8,
|
||||||
command: []const u8,
|
command: config.Command,
|
||||||
env: *EnvMap,
|
env: *EnvMap,
|
||||||
force_shell: ?Shell,
|
force_shell: ?Shell,
|
||||||
features: config.ShellIntegrationFeatures,
|
features: config.ShellIntegrationFeatures,
|
||||||
@ -51,14 +51,24 @@ pub fn setup(
|
|||||||
.elvish => "elvish",
|
.elvish => "elvish",
|
||||||
.fish => "fish",
|
.fish => "fish",
|
||||||
.zsh => "zsh",
|
.zsh => "zsh",
|
||||||
} else exe: {
|
} else switch (command) {
|
||||||
// The command can include arguments. Look for the first space
|
.direct => |v| std.fs.path.basename(v[0]),
|
||||||
// and use the basename of the first part as the command's exe.
|
.shell => |v| exe: {
|
||||||
const idx = std.mem.indexOfScalar(u8, command, ' ') orelse command.len;
|
// Shell strings can include spaces so we want to only
|
||||||
break :exe std.fs.path.basename(command[0..idx]);
|
// 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
|
// Setup our feature env vars
|
||||||
try setupFeatures(env, features);
|
try setupFeatures(env, features);
|
||||||
@ -69,7 +79,7 @@ pub fn setup(
|
|||||||
fn setupShell(
|
fn setupShell(
|
||||||
alloc_arena: Allocator,
|
alloc_arena: Allocator,
|
||||||
resource_dir: []const u8,
|
resource_dir: []const u8,
|
||||||
command: []const u8,
|
command: config.Command,
|
||||||
env: *EnvMap,
|
env: *EnvMap,
|
||||||
exe: []const u8,
|
exe: []const u8,
|
||||||
) !?ShellIntegration {
|
) !?ShellIntegration {
|
||||||
@ -83,7 +93,10 @@ fn setupShell(
|
|||||||
// we're using Apple's Bash because /bin is non-writable
|
// we're using Apple's Bash because /bin is non-writable
|
||||||
// on modern macOS due to System Integrity Protection.
|
// on modern macOS due to System Integrity Protection.
|
||||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +117,7 @@ fn setupShell(
|
|||||||
try setupXdgDataDirs(alloc_arena, resource_dir, env);
|
try setupXdgDataDirs(alloc_arena, resource_dir, env);
|
||||||
return .{
|
return .{
|
||||||
.shell = .elvish,
|
.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);
|
try setupXdgDataDirs(alloc_arena, resource_dir, env);
|
||||||
return .{
|
return .{
|
||||||
.shell = .fish,
|
.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);
|
try setupZsh(resource_dir, env);
|
||||||
return .{
|
return .{
|
||||||
.shell = .zsh,
|
.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| {
|
inline for (@typeInfo(Shell).@"enum".fields) |field| {
|
||||||
const shell = @field(Shell, field.name);
|
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);
|
try testing.expectEqual(shell, result.?.shell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setup shell integration feature environment variables without
|
/// Set up the shell integration features environment variable.
|
||||||
/// performing full shell integration setup.
|
|
||||||
pub fn setupFeatures(
|
pub fn setupFeatures(
|
||||||
env: *EnvMap,
|
env: *EnvMap,
|
||||||
features: config.ShellIntegrationFeatures,
|
features: config.ShellIntegrationFeatures,
|
||||||
) !void {
|
) !void {
|
||||||
if (!features.cursor) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR", "1");
|
const fields = @typeInfo(@TypeOf(features)).@"struct".fields;
|
||||||
if (!features.sudo) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_SUDO", "1");
|
const capacity: usize = capacity: {
|
||||||
if (!features.title) try env.put("GHOSTTY_SHELL_INTEGRATION_NO_TITLE", "1");
|
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" {
|
test "setup features" {
|
||||||
@ -162,15 +196,13 @@ test "setup features" {
|
|||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const alloc = arena.allocator();
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
// Test: all features enabled (no environment variables should be set)
|
// Test: all features enabled
|
||||||
{
|
{
|
||||||
var env = EnvMap.init(alloc);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
try setupFeatures(&env, .{ .cursor = true, .sudo = true, .title = true });
|
try setupFeatures(&env, .{ .cursor = true, .sudo = true, .title = true });
|
||||||
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR") == null);
|
try testing.expectEqualStrings("cursor,sudo,title", env.get("GHOSTTY_SHELL_FEATURES").?);
|
||||||
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO") == null);
|
|
||||||
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE") == null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: all features disabled
|
// Test: all features disabled
|
||||||
@ -179,9 +211,7 @@ test "setup features" {
|
|||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
try setupFeatures(&env, .{ .cursor = false, .sudo = false, .title = false });
|
try setupFeatures(&env, .{ .cursor = false, .sudo = false, .title = false });
|
||||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR").?);
|
try testing.expect(env.get("GHOSTTY_SHELL_FEATURES") == null);
|
||||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO").?);
|
|
||||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE").?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: mixed features
|
// Test: mixed features
|
||||||
@ -190,9 +220,7 @@ test "setup features" {
|
|||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
try setupFeatures(&env, .{ .cursor = false, .sudo = true, .title = false });
|
try setupFeatures(&env, .{ .cursor = false, .sudo = true, .title = false });
|
||||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_CURSOR").?);
|
try testing.expectEqualStrings("sudo", env.get("GHOSTTY_SHELL_FEATURES").?);
|
||||||
try testing.expect(env.get("GHOSTTY_SHELL_INTEGRATION_NO_SUDO") == null);
|
|
||||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_SHELL_INTEGRATION_NO_TITLE").?);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,25 +235,21 @@ test "setup features" {
|
|||||||
/// enables the integration or null if integration failed.
|
/// enables the integration or null if integration failed.
|
||||||
fn setupBash(
|
fn setupBash(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
command: []const u8,
|
command: config.Command,
|
||||||
resource_dir: []const u8,
|
resource_dir: []const u8,
|
||||||
env: *EnvMap,
|
env: *EnvMap,
|
||||||
) !?[]const u8 {
|
) !?config.Command {
|
||||||
// Accumulates the arguments that will form the final shell command line.
|
var args = try std.ArrayList([:0]const u8).initCapacity(alloc, 2);
|
||||||
// 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);
|
|
||||||
defer args.deinit();
|
defer args.deinit();
|
||||||
|
|
||||||
// Iterator that yields each argument in the original command line.
|
// Iterator that yields each argument in the original command line.
|
||||||
// This will allocate once proportionate to the command line length.
|
// 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();
|
defer iter.deinit();
|
||||||
|
|
||||||
// Start accumulating arguments with the executable and `--posix` mode flag.
|
// Start accumulating arguments with the executable and `--posix` mode flag.
|
||||||
if (iter.next()) |exe| {
|
if (iter.next()) |exe| {
|
||||||
try args.append(exe);
|
try args.append(try alloc.dupeZ(u8, exe));
|
||||||
} else return null;
|
} else return null;
|
||||||
try args.append("--posix");
|
try args.append("--posix");
|
||||||
|
|
||||||
@ -259,17 +283,17 @@ fn setupBash(
|
|||||||
if (std.mem.indexOfScalar(u8, arg, 'c') != null) {
|
if (std.mem.indexOfScalar(u8, arg, 'c') != null) {
|
||||||
return 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, "--")) {
|
} else if (std.mem.eql(u8, arg, "-") or std.mem.eql(u8, arg, "--")) {
|
||||||
// All remaining arguments should be passed directly to the shell
|
// All remaining arguments should be passed directly to the shell
|
||||||
// command. We shouldn't perform any further option processing.
|
// 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| {
|
while (iter.next()) |remaining_arg| {
|
||||||
try args.append(remaining_arg);
|
try args.append(try alloc.dupeZ(u8, remaining_arg));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
try args.append(arg);
|
try args.append(try alloc.dupeZ(u8, arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try env.put("GHOSTTY_BASH_INJECT", inject.slice());
|
try env.put("GHOSTTY_BASH_INJECT", inject.slice());
|
||||||
@ -302,30 +326,36 @@ fn setupBash(
|
|||||||
);
|
);
|
||||||
try env.put("ENV", integ_dir);
|
try env.put("ENV", integ_dir);
|
||||||
|
|
||||||
// Join the accumulated arguments to form the final command string.
|
// Since we built up a command line, we don't need to wrap it in
|
||||||
return try std.mem.join(alloc, " ", args.items);
|
// ANOTHER shell anymore and can do a direct command.
|
||||||
|
return .{ .direct = try args.toOwnedSlice() };
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bash" {
|
test "bash" {
|
||||||
const testing = std.testing;
|
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);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
const command = try setupBash(alloc, "bash", ".", &env);
|
const command = try setupBash(alloc, .{ .shell = "bash" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
|
||||||
|
|
||||||
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("./shell-integration/bash/ghostty.bash", env.get("ENV").?);
|
||||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_INJECT").?);
|
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_INJECT").?);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bash: unsupported options" {
|
test "bash: unsupported options" {
|
||||||
const testing = std.testing;
|
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 --posix",
|
||||||
"bash --rcfile script.sh --posix",
|
"bash --rcfile script.sh --posix",
|
||||||
"bash --init-file script.sh --posix",
|
"bash --init-file script.sh --posix",
|
||||||
@ -337,7 +367,7 @@ test "bash: unsupported options" {
|
|||||||
var env = EnvMap.init(alloc);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
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_INJECT") == null);
|
||||||
try testing.expect(env.get("GHOSTTY_BASH_RCFILE") == null);
|
try testing.expect(env.get("GHOSTTY_BASH_RCFILE") == null);
|
||||||
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
||||||
@ -346,17 +376,20 @@ test "bash: unsupported options" {
|
|||||||
|
|
||||||
test "bash: inject flags" {
|
test "bash: inject flags" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
// bash --norc
|
// bash --norc
|
||||||
{
|
{
|
||||||
var env = EnvMap.init(alloc);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
const command = try setupBash(alloc, "bash --norc", ".", &env);
|
const command = try setupBash(alloc, .{ .shell = "bash --norc" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
|
||||||
|
|
||||||
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").?);
|
try testing.expectEqualStrings("1 --norc", env.get("GHOSTTY_BASH_INJECT").?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,52 +398,55 @@ test "bash: inject flags" {
|
|||||||
var env = EnvMap.init(alloc);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
const command = try setupBash(alloc, "bash --noprofile", ".", &env);
|
const command = try setupBash(alloc, .{ .shell = "bash --noprofile" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
|
||||||
|
|
||||||
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").?);
|
try testing.expectEqualStrings("1 --noprofile", env.get("GHOSTTY_BASH_INJECT").?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bash: rcfile" {
|
test "bash: rcfile" {
|
||||||
const testing = std.testing;
|
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);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
// bash --rcfile
|
// bash --rcfile
|
||||||
{
|
{
|
||||||
const command = try setupBash(alloc, "bash --rcfile profile.sh", ".", &env);
|
const command = try setupBash(alloc, .{ .shell = "bash --rcfile profile.sh" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
try testing.expectEqual(2, command.?.direct.len);
|
||||||
|
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||||
try testing.expectEqualStrings("bash --posix", command.?);
|
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||||
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bash --init-file
|
// bash --init-file
|
||||||
{
|
{
|
||||||
const command = try setupBash(alloc, "bash --init-file profile.sh", ".", &env);
|
const command = try setupBash(alloc, .{ .shell = "bash --init-file profile.sh" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
try testing.expectEqual(2, command.?.direct.len);
|
||||||
|
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||||
try testing.expectEqualStrings("bash --posix", command.?);
|
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||||
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bash: HISTFILE" {
|
test "bash: HISTFILE" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
// HISTFILE unset
|
// HISTFILE unset
|
||||||
{
|
{
|
||||||
var env = EnvMap.init(alloc);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
const command = try setupBash(alloc, "bash", ".", &env);
|
_ = try setupBash(alloc, .{ .shell = "bash" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
|
||||||
|
|
||||||
try testing.expect(std.mem.endsWith(u8, env.get("HISTFILE").?, ".bash_history"));
|
try testing.expect(std.mem.endsWith(u8, env.get("HISTFILE").?, ".bash_history"));
|
||||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE").?);
|
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE").?);
|
||||||
}
|
}
|
||||||
@ -422,9 +458,7 @@ test "bash: HISTFILE" {
|
|||||||
|
|
||||||
try env.put("HISTFILE", "my_history");
|
try env.put("HISTFILE", "my_history");
|
||||||
|
|
||||||
const command = try setupBash(alloc, "bash", ".", &env);
|
_ = try setupBash(alloc, .{ .shell = "bash" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
|
||||||
|
|
||||||
try testing.expectEqualStrings("my_history", env.get("HISTFILE").?);
|
try testing.expectEqualStrings("my_history", env.get("HISTFILE").?);
|
||||||
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
try testing.expect(env.get("GHOSTTY_BASH_UNEXPORT_HISTFILE") == null);
|
||||||
}
|
}
|
||||||
@ -432,25 +466,35 @@ test "bash: HISTFILE" {
|
|||||||
|
|
||||||
test "bash: additional arguments" {
|
test "bash: additional arguments" {
|
||||||
const testing = std.testing;
|
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);
|
var env = EnvMap.init(alloc);
|
||||||
defer env.deinit();
|
defer env.deinit();
|
||||||
|
|
||||||
// "-" argument separator
|
// "-" argument separator
|
||||||
{
|
{
|
||||||
const command = try setupBash(alloc, "bash - --arg file1 file2", ".", &env);
|
const command = try setupBash(alloc, .{ .shell = "bash - --arg file1 file2" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
try testing.expectEqual(6, command.?.direct.len);
|
||||||
|
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||||
try testing.expectEqualStrings("bash --posix - --arg file1 file2", command.?);
|
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
|
// "--" argument separator
|
||||||
{
|
{
|
||||||
const command = try setupBash(alloc, "bash -- --arg file1 file2", ".", &env);
|
const command = try setupBash(alloc, .{ .shell = "bash -- --arg file1 file2" }, ".", &env);
|
||||||
defer if (command) |c| alloc.free(c);
|
try testing.expectEqual(6, command.?.direct.len);
|
||||||
|
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||||
try testing.expectEqualStrings("bash --posix -- --arg file1 file2", command.?);
|
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