Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17e4995e93 | ||
|
|
e161b5a4de | ||
|
|
52b643b6c6 | ||
|
|
9bdb647454 | ||
|
|
0cabc3e109 | ||
|
|
d06d6d7646 | ||
|
|
f1dc072045 | ||
|
|
9adc474e3c | ||
|
|
370aa3aaa6 | ||
|
|
bed4f33be8 | ||
|
|
8ce80d8962 | ||
|
|
27ae9104ac | ||
|
|
331f1b7f2b | ||
|
|
df1a99a974 | ||
|
|
7ea4270c88 | ||
|
|
a8480a4ca6 | ||
|
|
783bfb2823 | ||
|
|
a88164e3a2 | ||
|
|
a7cbe3776d | ||
|
|
78d0cc40a3 | ||
|
|
01720a8d4f | ||
|
|
1d45cf4f91 | ||
|
|
a9da57d9b3 | ||
|
|
a280328731 | ||
|
|
17d2315d0c | ||
|
|
e27920527c | ||
|
|
960f5ff065 | ||
|
|
8fe936882d | ||
|
|
682428fb54 | ||
|
|
1c6bae636b | ||
|
|
c201bac900 | ||
|
|
5eaa935ede | ||
|
|
a73f09cf89 | ||
|
|
092dc3d01f | ||
|
|
5c0d477a18 | ||
|
|
414ca86e3f | ||
|
|
fbbfb25702 | ||
|
|
6ea812679f | ||
|
|
5a997a5f7a | ||
|
|
47641456da | ||
|
|
077f71cad5 | ||
|
|
8f71f6112a | ||
|
|
8bdd4d0596 | ||
|
|
df45a4e759 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -5,7 +5,7 @@ env:
|
||||
ASAN_OPTIONS: leak_check_at_exit=0
|
||||
LC_ALL: en_US.UTF-8
|
||||
LANG: en_US.UTF-8
|
||||
GO_INSTALL_VERSION: ">=1.19.0"
|
||||
GO_INSTALL_VERSION: ">=1.20.0"
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ">=1.19.0"
|
||||
go-version: ">=1.20.0"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -22,7 +22,8 @@ simply re-run the command.
|
||||
.. warning::
|
||||
**Do not** copy the kitty binary out of the installation folder. If you want
|
||||
to add it to your :envvar:`PATH`, create a symlink in :file:`~/.local/bin` or
|
||||
:file:`/usr/bin` or wherever.
|
||||
:file:`/usr/bin` or wherever. You should create a symlink for the :file:`kitten`
|
||||
binary as well.
|
||||
|
||||
|
||||
Manually installing
|
||||
@@ -46,9 +47,9 @@ particular desktop, but it should work for most major desktop environments.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# Create a symbolic link to add kitty to PATH (assuming ~/.local/bin is in
|
||||
# Create symbolic links to add kitty and kitten to PATH (assuming ~/.local/bin is in
|
||||
# your system-wide PATH)
|
||||
ln -s ~/.local/kitty.app/bin/kitty ~/.local/bin/
|
||||
ln -sf ~/.local/kitty.app/bin/kitty ~/.local/kitty.app/bin/kitten ~/.local/bin/
|
||||
# Place the kitty.desktop file somewhere it can be found by the OS
|
||||
cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications/
|
||||
# If you want to open text files and images in kitty via your file manager also add the kitty-open.desktop file
|
||||
|
||||
@@ -46,7 +46,7 @@ Run-time dependencies:
|
||||
Build-time dependencies:
|
||||
|
||||
* ``gcc`` or ``clang``
|
||||
* ``go >= 1.19`` (see :file:`go.mod` for go packages used during building)
|
||||
* ``go >= 1.20`` (see :file:`go.mod` for go packages used during building)
|
||||
* ``pkg-config``
|
||||
* For building on Linux in addition to the above dependencies you might also
|
||||
need to install the following packages, if they are not already installed by
|
||||
|
||||
@@ -35,6 +35,25 @@ mouse anywhere in the current command to move the cursor there. See
|
||||
Detailed list of changes
|
||||
-------------------------------------
|
||||
|
||||
0.27.1 [2023-02-07]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Fix :opt:`modify_font` not working for strikethrough position (:iss:`5946`)
|
||||
|
||||
- Fix a regression causing the ``edit-in-kitty`` command not working if :file:`kitten` is not added
|
||||
to PATH (:iss:`5956`)
|
||||
|
||||
- icat kitten: Fix a regression that broke display of animated GIFs over SSH (:iss:`5958`)
|
||||
|
||||
- Wayland GNOME: Fix for ibus not working when using XWayland (:iss:`5967`)
|
||||
|
||||
- Fix regression in previous release that caused incorrect entries in terminfo for modifier+F3 key combinations (:pull:`5970`)
|
||||
|
||||
- Bring back the deprecated and removed ``kitty +complete`` and delegate it to :program:`kitten` for backward compatibility (:pull:`5977`)
|
||||
|
||||
- Bump the version of Go needed to build kitty to ``1.20`` so we can use the Go stdlib ecdh package for crypto.
|
||||
|
||||
|
||||
0.27.0 [2023-01-31]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -67,7 +86,7 @@ Detailed list of changes
|
||||
|
||||
- A new option :opt:`undercurl_style` to control the rendering of undercurls (:pull:`5883`)
|
||||
|
||||
- Bash integration: Fix ``clone-in-kitty`` not working on bash >= 5.2 if an environment variable values contain newlines or other special characters (:iss:`5629`)
|
||||
- Bash integration: Fix ``clone-in-kitty`` not working on bash >= 5.2 if environment variable values contain newlines or other special characters (:iss:`5629`)
|
||||
|
||||
- A new :ac:`sleep` action useful in combine based mappings to make kitty sleep before executing the next action
|
||||
|
||||
|
||||
@@ -68,6 +68,11 @@ Sample kitty.conf
|
||||
pre-existing :file:`kitty.conf`, then that will be used instead, delete it to
|
||||
see the sample file.
|
||||
|
||||
A default configuration file can also be generated by running::
|
||||
|
||||
kitty +runpy 'from kitty.config import *; print(commented_out_default_config())'
|
||||
|
||||
This will print the commented out default config file to :file:`STDOUT`.
|
||||
|
||||
All mappable actions
|
||||
------------------------
|
||||
|
||||
@@ -330,12 +330,13 @@ sequence of escape codes to the terminal emulator::
|
||||
<ESC>_Gm=0;<encoded pixel data last chunk><ESC>\
|
||||
|
||||
Note that only the first escape code needs to have the full set of control
|
||||
codes such as width, height, format etc. Subsequent chunks **must** have
|
||||
only the ``m`` key. The client **must** finish sending all chunks for a single image
|
||||
before sending any other graphics related escape codes. Note that the cursor
|
||||
position used to display the image **must** be the position when the final chunk is
|
||||
received. Finally, terminals must not display anything, until the entire sequence is
|
||||
received and validated.
|
||||
codes such as width, height, format, etc. Subsequent chunks **must** have only
|
||||
the ``m`` and optionally ``q`` keys. When sending animation frame data, subsequent
|
||||
chunks **must** also specify the ``a=f`` key. The client **must** finish sending
|
||||
all chunks for a single image before sending any other graphics related escape
|
||||
codes. Note that the cursor position used to display the image **must** be the
|
||||
position when the final chunk is received. Finally, terminals must not display
|
||||
anything, until the entire sequence is received and validated.
|
||||
|
||||
|
||||
Querying support and available transmission mediums
|
||||
|
||||
@@ -1581,26 +1581,33 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||
// Support services receiving "public.utf8-plain-text" and "NSStringPboardType"
|
||||
- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
|
||||
{
|
||||
if ([sendType isEqual:NSPasteboardTypeString] || [sendType isEqual:@"NSStringPboardType"]) {
|
||||
return self;
|
||||
if (
|
||||
(!sendType || [sendType isEqual:NSPasteboardTypeString] || [sendType isEqual:@"NSStringPboardType"]) &&
|
||||
(!returnType || [returnType isEqual:NSPasteboardTypeString] || [returnType isEqual:@"NSStringPboardType"])
|
||||
) {
|
||||
if (_glfw.callbacks.has_current_selection && _glfw.callbacks.has_current_selection()) return self;
|
||||
}
|
||||
return nil;
|
||||
return [super validRequestorForSendType:sendType returnType:returnType];
|
||||
}
|
||||
|
||||
// Selected text as input to be sent to Services
|
||||
- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types
|
||||
{
|
||||
NSString *text = [self accessibilitySelectedText];
|
||||
if (text && [text length] > 0) {
|
||||
if (!_glfw.callbacks.get_current_selection) return NO;
|
||||
char *text = _glfw.callbacks.get_current_selection();
|
||||
if (!text) return NO;
|
||||
BOOL ans = NO;
|
||||
if (text[0]) {
|
||||
if ([types containsObject:NSPasteboardTypeString] == YES) {
|
||||
[pboard declareTypes:@[NSPasteboardTypeString] owner:self];
|
||||
return [pboard setString:text forType:NSPasteboardTypeString];
|
||||
ans = [pboard setString:@(text) forType:NSPasteboardTypeString];
|
||||
} else if ([types containsObject:@"NSStringPboardType"] == YES) {
|
||||
[pboard declareTypes:@[@"NSStringPboardType"] owner:self];
|
||||
return [pboard setString:text forType:NSPasteboardTypeString];
|
||||
ans = [pboard setString:@(text) forType:@"NSStringPboardType"];
|
||||
}
|
||||
free(text);
|
||||
}
|
||||
return NO;
|
||||
return ans;
|
||||
}
|
||||
|
||||
// Service output to be handled
|
||||
@@ -1625,7 +1632,7 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||
_glfwInputKeyboard(window, &glfw_keyevent);
|
||||
}
|
||||
debug_key("Sending text received in readSelectionFromPasteboard as key event\n");
|
||||
GLFWkeyevent glfw_keyevent = {.text=utf8};
|
||||
GLFWkeyevent glfw_keyevent = {.text=utf8, .ime_state=GLFW_IME_COMMIT_TEXT};
|
||||
_glfwInputKeyboard(window, &glfw_keyevent);
|
||||
return YES;
|
||||
}
|
||||
|
||||
2
glfw/glfw3.h
vendored
2
glfw/glfw3.h
vendored
@@ -1719,6 +1719,7 @@ typedef void (* GLFWtickcallback)(void*);
|
||||
typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data);
|
||||
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin);
|
||||
typedef char* (* GLFWcurrentselectionfun)(void);
|
||||
typedef bool (* GLFWhascurrentselectionfun)(void);
|
||||
typedef void (* GLFWclipboarddatafreefun)(void* data);
|
||||
typedef struct GLFWDataChunk {
|
||||
const char *data;
|
||||
@@ -1889,6 +1890,7 @@ GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval,
|
||||
GLFWAPI void glfwRemoveTimer(unsigned long long);
|
||||
GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function);
|
||||
GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback);
|
||||
GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback);
|
||||
|
||||
/*! @brief Terminates the GLFW library.
|
||||
*
|
||||
|
||||
40
glfw/ibus_glfw.c
vendored
40
glfw/ibus_glfw.c
vendored
@@ -283,29 +283,35 @@ static const char*
|
||||
get_ibus_address_file_name(void) {
|
||||
const char *addr;
|
||||
static char ans[PATH_MAX];
|
||||
static char display[64] = {0};
|
||||
addr = getenv("IBUS_ADDRESS");
|
||||
int offset = 0;
|
||||
if (addr && addr[0]) {
|
||||
memcpy(ans, addr, GLFW_MIN(strlen(addr), sizeof(ans)));
|
||||
return ans;
|
||||
}
|
||||
|
||||
const char *de = getenv("DISPLAY");
|
||||
if (!de || !de[0]) de = ":0.0";
|
||||
char *display = _glfw_strdup(de);
|
||||
const char *host = display;
|
||||
char *disp_num = strrchr(display, ':');
|
||||
char *screen_num = strrchr(display, '.');
|
||||
|
||||
if (!disp_num) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as DISPLAY env var has no colon");
|
||||
free(display);
|
||||
return NULL;
|
||||
const char* disp_num = NULL;
|
||||
const char *host = "unix";
|
||||
// See https://github.com/ibus/ibus/commit/8ce25208c3f4adfd290a032c6aa739d2b7580eb1 for why we need this dance.
|
||||
const char *de = getenv("WAYLAND_DISPLAY");
|
||||
if (de) {
|
||||
disp_num = de;
|
||||
} else {
|
||||
const char *de = getenv("DISPLAY");
|
||||
if (!de || !de[0]) de = ":0.0";
|
||||
strncpy(display, de, sizeof(display) - 1);
|
||||
char *dnum = strrchr(display, ':');
|
||||
if (!dnum) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as DISPLAY env var has no colon");
|
||||
return NULL;
|
||||
}
|
||||
char *screen_num = strrchr(display, '.');
|
||||
*dnum = 0;
|
||||
dnum++;
|
||||
if (screen_num) *screen_num = 0;
|
||||
if (*display) host = display;
|
||||
disp_num = dnum;
|
||||
}
|
||||
*disp_num = 0;
|
||||
disp_num++;
|
||||
if (screen_num) *screen_num = 0;
|
||||
if (!*host) host = "unix";
|
||||
|
||||
memset(ans, 0, sizeof(ans));
|
||||
const char *conf_env = getenv("XDG_CONFIG_HOME");
|
||||
@@ -315,7 +321,6 @@ get_ibus_address_file_name(void) {
|
||||
conf_env = getenv("HOME");
|
||||
if (!conf_env || !conf_env[0]) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as no HOME env var is set");
|
||||
free(display);
|
||||
return NULL;
|
||||
}
|
||||
offset = snprintf(ans, sizeof(ans), "%s/.config", conf_env);
|
||||
@@ -323,7 +328,6 @@ get_ibus_address_file_name(void) {
|
||||
char *key = dbus_get_local_machine_id();
|
||||
snprintf(ans + offset, sizeof(ans) - offset, "/ibus/bus/%s-%s-%s", key, host, disp_num);
|
||||
dbus_free(key);
|
||||
free(display);
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
7
glfw/init.c
vendored
7
glfw/init.c
vendored
@@ -395,3 +395,10 @@ GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselec
|
||||
_GLFW_SWAP_POINTERS(_glfw.callbacks.get_current_selection, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun cbfun)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
1
glfw/internal.h
vendored
1
glfw/internal.h
vendored
@@ -634,6 +634,7 @@ struct _GLFWlibrary
|
||||
GLFWapplicationclosefun application_close;
|
||||
GLFWdrawtextfun draw_text;
|
||||
GLFWcurrentselectionfun get_current_selection;
|
||||
GLFWhascurrentselectionfun has_current_selection;
|
||||
} callbacks;
|
||||
|
||||
|
||||
|
||||
13
go.mod
13
go.mod
@@ -1,6 +1,6 @@
|
||||
module kitty
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/ALTree/bigfloat v0.0.0-20220102081255-38c8b72a9924
|
||||
@@ -8,11 +8,10 @@ require (
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f
|
||||
github.com/seancfoley/ipaddress-go v1.2.1
|
||||
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7
|
||||
golang.org/x/exp v0.0.0-20220921164117-439092de6870
|
||||
golang.org/x/image v0.2.0
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
|
||||
github.com/seancfoley/ipaddress-go v1.5.3
|
||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
|
||||
golang.org/x/image v0.3.0
|
||||
golang.org/x/sys v0.4.0
|
||||
)
|
||||
|
||||
require github.com/seancfoley/bintree v1.1.0 // indirect
|
||||
require github.com/seancfoley/bintree v1.2.1 // indirect
|
||||
|
||||
24
go.sum
24
go.sum
@@ -8,20 +8,18 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f h1:Ko4+g6K16vSyUrtd/pPXuQnWsiHe5BYptEtTxfwYwCc=
|
||||
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f/go.mod h1:eHzfhOKbTGJEGPSdMHzU6jft192tHHt2Bu2vIZArvC0=
|
||||
github.com/seancfoley/bintree v1.1.0 h1:6J0rj9hLNLIcWSsfYdZ4ZHkMHokaK/PHkak8qyBO/mc=
|
||||
github.com/seancfoley/bintree v1.1.0/go.mod h1:CtE6qO6/n9H3V2CAGEC0lpaYr6/OijhNaMG/dt7P70c=
|
||||
github.com/seancfoley/ipaddress-go v1.2.1 h1:yEZxnyC6NQEDDPflyQm4KkWozffx1vHWsx+knKBr/n0=
|
||||
github.com/seancfoley/ipaddress-go v1.2.1/go.mod h1:/UEVHyrBg1ASVap2ffdY2cq5UMYIX9f3QW3uWSVqpbo=
|
||||
github.com/seancfoley/bintree v1.2.1 h1:Z/iNjRKkXnn0CTW7jDQYtjW5fz2GH1yWvOTJ4MrMvdo=
|
||||
github.com/seancfoley/bintree v1.2.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=
|
||||
github.com/seancfoley/ipaddress-go v1.5.3 h1:fLnn4nsatd2rp3IJsVWriXv5gXn2Qiy8uxjxe4iZtTg=
|
||||
github.com/seancfoley/ipaddress-go v1.5.3/go.mod h1:fpvVPC+Jso+YEhNcNiww8HQmBgKP8T4T6BTp1SLxxIo=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7 h1:WJywXQVIb56P2kAvXeMGTIgQ1ZHQxR60+F9dLsodECc=
|
||||
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20220921164117-439092de6870 h1:j8b6j9gzSigH28O5SjSpQSSh9lFd6f5D/q0aHjNTulc=
|
||||
golang.org/x/exp v0.0.0-20220921164117-439092de6870/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b h1:EqBVA+nNsObCwQoBEHy4wLU0pi7i8a4AL3pbItPdPkE=
|
||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ=
|
||||
golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI=
|
||||
golang.org/x/image v0.3.0 h1:HTDXbdK9bjfSWkPzDJIw89W8CAtfFGduujWs33NLLsg=
|
||||
golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
@@ -33,14 +31,14 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
||||
@@ -1985,6 +1985,12 @@ class Boss:
|
||||
return w.text_for_selection()
|
||||
return None
|
||||
|
||||
def has_active_selection(self) -> bool:
|
||||
w = self.active_window
|
||||
if w is not None and not w.destroyed:
|
||||
return w.has_selection()
|
||||
return False
|
||||
|
||||
@ac('cp', '''
|
||||
Copy the selection from the active window to the specified buffer
|
||||
|
||||
|
||||
@@ -888,7 +888,7 @@ Change to the specified directory when launching.
|
||||
--detach
|
||||
type=bool-set
|
||||
condition=not is_macos
|
||||
Detach from the controlling terminal, if any.
|
||||
Detach from the controlling terminal, if any. Not available on macOS.
|
||||
|
||||
|
||||
--session
|
||||
|
||||
@@ -22,7 +22,7 @@ class Version(NamedTuple):
|
||||
|
||||
appname: str = 'kitty'
|
||||
kitty_face = '🐱'
|
||||
version: Version = Version(0, 27, 0)
|
||||
version: Version = Version(0, 27, 1)
|
||||
str_version: str = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos: bool = 'darwin' in _plat
|
||||
@@ -79,6 +79,7 @@ def kitty_exe() -> str:
|
||||
return os.path.join(rpath, 'kitty')
|
||||
|
||||
|
||||
@run_once
|
||||
def kitten_exe() -> str:
|
||||
return os.path.join(os.path.dirname(kitty_exe()), 'kitten')
|
||||
|
||||
|
||||
@@ -31,6 +31,19 @@ def hold(args: List[str]) -> None:
|
||||
os.execvp(kitten_exe(), args)
|
||||
|
||||
|
||||
def complete(args: List[str]) -> None:
|
||||
# Delegate to kitten to maintain backward compatibility
|
||||
if len(args) < 2 or args[1] not in ('setup', 'zsh', 'fish2', 'bash'):
|
||||
raise SystemExit(1)
|
||||
if args[1] == 'fish2':
|
||||
args[1:1] = ['fish', '_legacy_completion=fish2']
|
||||
elif len(args) >= 3 and args [1:3] == ['setup', 'fish2']:
|
||||
args[2] = 'fish'
|
||||
from kitty.constants import kitten_exe
|
||||
args = ['kitten', '__complete__'] + args[1:]
|
||||
os.execvp(kitten_exe(), args)
|
||||
|
||||
|
||||
def open_urls(args: List[str]) -> None:
|
||||
setattr(sys, 'cmdline_args_for_open', True)
|
||||
sys.argv = ['kitty'] + args[1:]
|
||||
@@ -144,6 +157,7 @@ entry_points = {
|
||||
}
|
||||
namespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}
|
||||
namespaced_entry_points['hold'] = hold
|
||||
namespaced_entry_points['complete'] = complete
|
||||
namespaced_entry_points['runpy'] = runpy
|
||||
namespaced_entry_points['launch'] = launch
|
||||
namespaced_entry_points['open'] = open_urls
|
||||
|
||||
@@ -387,7 +387,7 @@ calc_cell_metrics(FontGroup *fg) {
|
||||
int adjustment = baseline - baseline_before;
|
||||
baseline = adjust_ypos(baseline_before, cell_height, adjustment);
|
||||
underline_position = adjust_ypos(underline_position, cell_height, adjustment);
|
||||
strikethrough_position = adjust_ypos(underline_position, cell_height, adjustment);
|
||||
strikethrough_position = adjust_ypos(strikethrough_position, cell_height, adjustment);
|
||||
}
|
||||
|
||||
underline_position = MIN(cell_height - 1, underline_position);
|
||||
|
||||
3
kitty/glfw-wrapper.c
generated
3
kitty/glfw-wrapper.c
generated
@@ -41,6 +41,9 @@ load_glfw(const char* path) {
|
||||
*(void **) (&glfwSetCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetCurrentSelectionCallback");
|
||||
if (glfwSetCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetCurrentSelectionCallback with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetHasCurrentSelectionCallback");
|
||||
if (glfwSetHasCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetHasCurrentSelectionCallback with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwTerminate_impl) = dlsym(handle, "glfwTerminate");
|
||||
if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror());
|
||||
|
||||
|
||||
5
kitty/glfw-wrapper.h
generated
5
kitty/glfw-wrapper.h
generated
@@ -1457,6 +1457,7 @@ typedef void (* GLFWtickcallback)(void*);
|
||||
typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data);
|
||||
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin);
|
||||
typedef char* (* GLFWcurrentselectionfun)(void);
|
||||
typedef bool (* GLFWhascurrentselectionfun)(void);
|
||||
typedef void (* GLFWclipboarddatafreefun)(void* data);
|
||||
typedef struct GLFWDataChunk {
|
||||
const char *data;
|
||||
@@ -1662,6 +1663,10 @@ typedef GLFWcurrentselectionfun (*glfwSetCurrentSelectionCallback_func)(GLFWcurr
|
||||
GFW_EXTERN glfwSetCurrentSelectionCallback_func glfwSetCurrentSelectionCallback_impl;
|
||||
#define glfwSetCurrentSelectionCallback glfwSetCurrentSelectionCallback_impl
|
||||
|
||||
typedef GLFWhascurrentselectionfun (*glfwSetHasCurrentSelectionCallback_func)(GLFWhascurrentselectionfun);
|
||||
GFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl;
|
||||
#define glfwSetHasCurrentSelectionCallback glfwSetHasCurrentSelectionCallback_impl
|
||||
|
||||
typedef void (*glfwTerminate_func)(void);
|
||||
GFW_EXTERN glfwTerminate_func glfwTerminate_impl;
|
||||
#define glfwTerminate glfwTerminate_impl
|
||||
|
||||
15
kitty/glfw.c
15
kitty/glfw.c
@@ -179,7 +179,7 @@ log_event(const char *format, ...) {
|
||||
// callbacks {{{
|
||||
|
||||
void
|
||||
update_os_window_references() {
|
||||
update_os_window_references(void) {
|
||||
for (size_t i = 0; i < global_state.num_os_windows; i++) {
|
||||
OSWindow *w = global_state.os_windows + i;
|
||||
if (w->handle) glfwSetWindowUserPointer(w->handle, w);
|
||||
@@ -496,6 +496,16 @@ get_current_selection(void) {
|
||||
return ans;
|
||||
}
|
||||
|
||||
static bool
|
||||
has_current_selection(void) {
|
||||
if (!global_state.boss) return false;
|
||||
PyObject *ret = PyObject_CallMethod(global_state.boss, "has_active_selection", NULL);
|
||||
if (!ret) { PyErr_Print(); return false; }
|
||||
bool ans = ret == Py_True;
|
||||
Py_DECREF(ret);
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
static void get_window_dpi(GLFWwindow *w, double *x, double *y);
|
||||
|
||||
@@ -800,6 +810,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
|
||||
if (OPT(hide_window_decorations) & 1) glfwWindowHint(GLFW_DECORATED, false);
|
||||
glfwSetApplicationCloseCallback(application_close_requested_callback);
|
||||
glfwSetCurrentSelectionCallback(get_current_selection);
|
||||
glfwSetHasCurrentSelectionCallback(has_current_selection);
|
||||
#ifdef __APPLE__
|
||||
cocoa_set_activation_policy(OPT(macos_hide_from_tasks));
|
||||
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);
|
||||
@@ -1433,7 +1444,7 @@ swap_window_buffers(OSWindow *os_window) {
|
||||
}
|
||||
|
||||
void
|
||||
wakeup_main_loop() {
|
||||
wakeup_main_loop(void) {
|
||||
glfwPostEmptyEvent();
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ Where to launch the child process:
|
||||
:code:`tab`
|
||||
A new :term:`tab` in the current OS window
|
||||
|
||||
:code:`os-window <os_window>`
|
||||
:code:`os-window`
|
||||
A new :term:`operating system window <os_window>`
|
||||
|
||||
:code:`overlay`
|
||||
|
||||
@@ -23,6 +23,7 @@ from .constants import (
|
||||
glfw_path,
|
||||
is_macos,
|
||||
is_wayland,
|
||||
kitten_exe,
|
||||
kitty_exe,
|
||||
logo_png_file,
|
||||
running_in_kitty,
|
||||
@@ -382,6 +383,15 @@ def ensure_kitty_in_path() -> None:
|
||||
os.environ['PATH'] = prepend_if_not_present(rpath, env_path)
|
||||
|
||||
|
||||
def ensure_kitten_in_path() -> None:
|
||||
correct_kitten = kitten_exe()
|
||||
existing = shutil.which('kitten')
|
||||
if existing and safe_samefile(existing, correct_kitten):
|
||||
return
|
||||
env_path = os.environ.get('PATH', '')
|
||||
os.environ['PATH'] = prepend_if_not_present(os.path.dirname(correct_kitten), env_path)
|
||||
|
||||
|
||||
def setup_manpath(env: Dict[str, str]) -> None:
|
||||
# Ensure kitty manpages are available in frozen builds
|
||||
if not getattr(sys, 'frozen', False):
|
||||
@@ -407,6 +417,7 @@ def setup_environment(opts: Options, cli_opts: CLIOptions) -> None:
|
||||
cli_opts.listen_on = expand_listen_on(cli_opts.listen_on, from_config_file)
|
||||
env = opts.env.copy()
|
||||
ensure_kitty_in_path()
|
||||
ensure_kitten_in_path()
|
||||
kitty_path = shutil.which('kitty')
|
||||
if kitty_path:
|
||||
child_path = env.get('PATH')
|
||||
|
||||
@@ -2785,7 +2785,12 @@ screen_detect_url(Screen *screen, unsigned int x, unsigned int y) {
|
||||
#define WRAP2B(name) static PyObject* name(Screen *self, PyObject *args) { unsigned int a, b; int p; if(!PyArg_ParseTuple(args, "IIp", &a, &b, &p)) return NULL; screen_##name(self, a, b, (bool)p); Py_RETURN_NONE; }
|
||||
|
||||
WRAP0(garbage_collect_hyperlink_pool)
|
||||
WRAP0x(has_selection)
|
||||
|
||||
static PyObject*
|
||||
has_selection(Screen *self, PyObject *a UNUSED) {
|
||||
if (screen_has_selection(self)) Py_RETURN_TRUE;
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
hyperlinks_as_list(Screen *self, PyObject *args UNUSED) {
|
||||
@@ -4076,7 +4081,7 @@ static PyMethodDef methods[] = {
|
||||
MND(cursor_down1, METH_VARARGS)
|
||||
MND(cursor_forward, METH_VARARGS)
|
||||
{"index", (PyCFunction)xxx_index, METH_VARARGS, ""},
|
||||
{"has_selection", (PyCFunction)xxx_has_selection, METH_VARARGS, ""},
|
||||
{"has_selection", (PyCFunction)has_selection, METH_VARARGS, ""},
|
||||
MND(set_pending_timeout, METH_O)
|
||||
MND(as_text, METH_VARARGS)
|
||||
MND(as_text_non_visual, METH_VARARGS)
|
||||
|
||||
@@ -284,11 +284,18 @@ string_capabilities = {
|
||||
}
|
||||
|
||||
string_capabilities.update({
|
||||
f'kf{offset + n}':
|
||||
encode_keystring(modify_key_bytes(b'\033' + value, mod))
|
||||
for offset, mod in {0: 0, 12: 2, 24: 5, 36: 6, 48: 3, 60: 4}.items()
|
||||
f'kf{n}':
|
||||
encode_keystring(modify_key_bytes(b'\033' + value, 0))
|
||||
for n, value in zip(range(1, 13),
|
||||
b'OP OQ OR OS [15~ [17~ [18~ [19~ [20~ [21~ [23~ [24~'.split())
|
||||
})
|
||||
|
||||
string_capabilities.update({
|
||||
f'kf{offset + n}':
|
||||
encode_keystring(modify_key_bytes(b'\033' + value, mod))
|
||||
for offset, mod in {12: 2, 24: 5, 36: 6, 48: 3, 60: 4}.items()
|
||||
for n, value in zip(range(1, 13),
|
||||
b'OP OQ [13~ OS [15~ [17~ [18~ [19~ [20~ [21~ [23~ [24~'.split())
|
||||
if offset + n < 64
|
||||
})
|
||||
|
||||
|
||||
@@ -1354,6 +1354,9 @@ class Window:
|
||||
lines = self.screen.text_for_selection(as_ansi, strip_trailing_spaces)
|
||||
return ''.join(lines)
|
||||
|
||||
def has_selection(self) -> bool:
|
||||
return self.screen.has_selection()
|
||||
|
||||
def call_watchers(self, which: Iterable[Watcher], data: Dict[str, Any]) -> None:
|
||||
boss = get_boss()
|
||||
for w in which:
|
||||
|
||||
@@ -13,7 +13,7 @@ if [[ -n "$KITTY_BASH_INJECT" ]]; then
|
||||
if [[ -z "$KITTY_BASH_ETC_LOCATION" ]]; then KITTY_BASH_ETC_LOCATION="/etc"; fi
|
||||
|
||||
_ksi_sourceable() {
|
||||
[[ -f "$1" && -r "$1" ]] && return 0; return 1;
|
||||
[[ -f "$1" && -r "$1" ]] && builtin return 0; builtin return 1;
|
||||
}
|
||||
|
||||
if [[ "$kitty_bash_inject" == *"posix"* ]]; then
|
||||
@@ -209,7 +209,8 @@ _ksi_main() {
|
||||
_ksi_prompt[ps0]+="\[\e]133;C\a\]"
|
||||
fi
|
||||
|
||||
alias edit-in-kitty="kitten edit-in-kitty"
|
||||
builtin alias edit-in-kitty="kitten edit-in-kitty"
|
||||
|
||||
if [[ "${_ksi_prompt[complete]}" == "y" ]]; then
|
||||
_ksi_completions() {
|
||||
builtin local src
|
||||
@@ -272,8 +273,8 @@ _ksi_main() {
|
||||
builtin local venv="${VIRTUAL_ENV}/bin/activate"
|
||||
builtin local sourced=""
|
||||
_ksi_s_is_ok() {
|
||||
[[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && return 0
|
||||
return 1
|
||||
[[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && builtin return 0
|
||||
builtin return 1
|
||||
}
|
||||
|
||||
if _ksi_s_is_ok "venv" && [ -n "${VIRTUAL_ENV}" -a -r "$venv" ]; then
|
||||
@@ -339,7 +340,7 @@ clone-in-kitty() {
|
||||
"") break;;
|
||||
-h|--help)
|
||||
builtin printf "%s\n\n%s\n" "Clone the current bash session into a new kitty window." "For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#clone-shell"
|
||||
return
|
||||
builtin return
|
||||
;;
|
||||
*) data="$data,a=$(builtin printf "%s" "$1" | builtin command base64)";;
|
||||
esac
|
||||
|
||||
@@ -156,7 +156,7 @@ function __ksi_schedule --on-event fish_prompt -d "Setup kitty integration after
|
||||
end
|
||||
end
|
||||
|
||||
function edit-in-kitty --wraps "kitten edit-in-kitty"
|
||||
function edit-in-kitty --wraps "kitten edit-in-kitty" -d "Edit the specified file in a kitty overlay window with your locally installed editor"
|
||||
kitten edit-in-kitty $argv
|
||||
end
|
||||
|
||||
|
||||
@@ -177,11 +177,6 @@ fi' > "$sh_script"
|
||||
}
|
||||
|
||||
install_kitty_bootstrap() {
|
||||
case "$(command uname)" in
|
||||
Linux) ;;
|
||||
Darwin) ;;
|
||||
*) return ;;
|
||||
esac
|
||||
kitty_exists="n"
|
||||
command -v kitty 2> /dev/null > /dev/null && kitty_exists="y"
|
||||
if [ "$kitty_remote" = "yes" -o "$kitty_remote-$kitty_exists" = "if-needed-n" ]; then
|
||||
|
||||
@@ -261,8 +261,6 @@ def exec_with_shell_integration():
|
||||
|
||||
def install_kitty_bootstrap():
|
||||
kitty_remote = os.environ.pop('KITTY_REMOTE', '')
|
||||
if os.uname().sysname not in ('Linux', 'Darwin'):
|
||||
return
|
||||
kitty_exists = shutil.which('kitty')
|
||||
if kitty_remote == 'yes' or (kitty_remote == 'if-needed' and not kitty_exists):
|
||||
kitty_dir = os.path.join(data_dir, 'kitty', 'bin')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#compdef kitty
|
||||
|
||||
(( ${+commands[kitty]} )) || builtin return
|
||||
(( ${+commands[kitten]} )) || builtin return
|
||||
builtin local src cmd=${(F)words:0:$CURRENT}
|
||||
# Send all words up to the word the cursor is currently on.
|
||||
src=$(builtin command kitten __complete__ zsh "_matcher=$_matcher" <<<$cmd) || builtin return
|
||||
|
||||
@@ -362,8 +362,8 @@ _ksi_deferred_init() {
|
||||
builtin local venv="${VIRTUAL_ENV}/bin/activate"
|
||||
builtin local sourced=""
|
||||
_ksi_s_is_ok() {
|
||||
[[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && return 0
|
||||
return 1
|
||||
[[ -z "$sourced" && "$KITTY_CLONE_SOURCE_STRATEGIES" == *",$1,"* ]] && builtin return 0
|
||||
builtin return 1
|
||||
}
|
||||
|
||||
if _ksi_s_is_ok "venv" && [[ -n "${VIRTUAL_ENV}" && -r "$venv" ]]; then
|
||||
@@ -386,7 +386,7 @@ _ksi_deferred_init() {
|
||||
fi
|
||||
builtin unset KITTY_IS_CLONE_LAUNCH KITTY_CLONE_SOURCE_STRATEGIES
|
||||
|
||||
alias edit-in-kitty="kitten edit-in-kitty"
|
||||
builtin alias edit-in-kitty="kitten edit-in-kitty"
|
||||
|
||||
# Map alt+left/right to move by word if not already mapped. This is expected behavior on macOS and I am tired
|
||||
# of answering questions about it.
|
||||
@@ -421,7 +421,7 @@ clone-in-kitty() {
|
||||
"") break;;
|
||||
-h|--help)
|
||||
builtin printf "%s\n\n%s\n" "Clone the current zsh session into a new kitty window." "For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#clone-shell"
|
||||
return
|
||||
builtin return
|
||||
;;
|
||||
*) data="$data,a=$(builtin printf "%s" "$1" | builtin command base64)";;
|
||||
esac
|
||||
|
||||
@@ -1 +1 @@
|
||||
xterm-kitty|KovIdTTY:5i:NP:am:cc:hs:km:mi:ms:xn:Co#256:co#80:it#8:li#24:pa#32767:#2=\E[1;2H:#3=\E[2;2~:#4=\E[1;2D:%1=:%c=\E[6;2~:%e=\E[5;2~:%i=\E[1;2C:&8=:&9=\E[1;2E:*4=\E[3;2~:*7=\E[1;2F:@1=\EOE:@7=\EOF:AB=\E[48;5;%dm:AF=\E[38;5;%dm:AL=\E[%dL:DC=\E[%dP:DL=\E[%dM:DO=\E[%dB:F1=\E[23~:F2=\E[24~:F3=\E[1;2P:F4=\E[1;2Q:F5=\E[1;2R:F6=\E[1;2S:F7=\E[15;2~:F8=\E[17;2~:F9=\E[18;2~:FA=\E[19;2~:FB=\E[20;2~:FC=\E[21;2~:FD=\E[23;2~:FE=\E[24;2~:FF=\E[1;5P:FG=\E[1;5Q:FH=\E[1;5R:FI=\E[1;5S:FJ=\E[15;5~:FK=\E[17;5~:FL=\E[18;5~:FM=\E[19;5~:FN=\E[20;5~:FO=\E[21;5~:FP=\E[23;5~:FQ=\E[24;5~:FR=\E[1;6P:FS=\E[1;6Q:FT=\E[1;6R:FU=\E[1;6S:FV=\E[15;6~:FW=\E[17;6~:FX=\E[18;6~:FY=\E[19;6~:FZ=\E[20;6~:Fa=\E[21;6~:Fb=\E[23;6~:Fc=\E[24;6~:Fd=\E[1;3P:Fe=\E[1;3Q:Ff=\E[1;3R:Fg=\E[1;3S:Fh=\E[15;3~:Fi=\E[17;3~:Fj=\E[18;3~:Fk=\E[19;3~:Fl=\E[20;3~:Fm=\E[21;3~:Fn=\E[23;3~:Fo=\E[24;3~:Fp=\E[1;4P:Fq=\E[1;4Q:Fr=\E[1;4R:IC=\E[%d@:..Ic=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\:K1=:K3=:K4=:K5=:Km=\E[M:LE=\E[%dD:RA=\E[?7l:RI=\E[%dC:SA=\E[?7h:SF=\E[%dS:SR=\E[%dT:UP=\E[%dA:ZH=\E[3m:ZR=\E[23m:ac=++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~:ae=\E(B:al=\E[L:as=\E(0:bl=^G:bt=\E[Z:cb=\E[1K:cd=\E[J:ce=\E[K:ch=\E[%i%dG:cl=\E[H\E[2J:cm=\E[%i%d;%dH:cr=\r:cs=\E[%i%d;%dr:ct=\E[3g:cv=\E[%i%dd:dc=\E[P:dl=\E[M:do=\n:ds=\E]2;\007:ec=\E[%dX:ei=\E[4l:fs=^G:ho=\E[H:im=\E[4h:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:k5=\E[15~:k6=\E[17~:k7=\E[18~:k8=\E[19~:k9=\E[20~:k;=\E[21~:kB=\E[Z:kD=\E[3~:kF=\E[1;2B:kI=\E[2~:kN=\E[6~:kP=\E[5~:kR=\E[1;2A:kb=\177:kd=\EOB:ke=\E[?1l:kh=\EOH:kl=\EOD:kr=\EOC:ks=\E[?1h:ku=\EOA:le=^H:md=\E[1m:me=\E[0m:mh=\E[2m:mr=\E[7m:nd=\E[C:oc=\E]104\007:op=\E[39;49m:r1=\E]\E\\\Ec:rc=\E8:..rp=%p1%c\E[%p2%{1}%-%db:..sa=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m:sc=\E7:se=\E[27m:sf=\n:so=\E[7m:sr=\EM:st=\EH:ta=^I:te=\E[?1049l:ti=\E[?1049h:ts=\E]2;:u6=\E[%i%d;%dR:u7=\E[6n:..u8=\E[?%[;0123456789]c:u9=\E[c:ue=\E[24m:up=\E[A:us=\E[4m:vb=\E[?5h\E[?5l:ve=\E[?12h\E[?25h:vi=\E[?25l:vs=\E[?12;25h:
|
||||
xterm-kitty|KovIdTTY:5i:NP:am:cc:hs:km:mi:ms:xn:Co#256:co#80:it#8:li#24:pa#32767:#2=\E[1;2H:#3=\E[2;2~:#4=\E[1;2D:%1=:%c=\E[6;2~:%e=\E[5;2~:%i=\E[1;2C:&8=:&9=\E[1;2E:*4=\E[3;2~:*7=\E[1;2F:@1=\EOE:@7=\EOF:AB=\E[48;5;%dm:AF=\E[38;5;%dm:AL=\E[%dL:DC=\E[%dP:DL=\E[%dM:DO=\E[%dB:F1=\E[23~:F2=\E[24~:F3=\E[1;2P:F4=\E[1;2Q:F5=\E[13;2~:F6=\E[1;2S:F7=\E[15;2~:F8=\E[17;2~:F9=\E[18;2~:FA=\E[19;2~:FB=\E[20;2~:FC=\E[21;2~:FD=\E[23;2~:FE=\E[24;2~:FF=\E[1;5P:FG=\E[1;5Q:FH=\E[13;5~:FI=\E[1;5S:FJ=\E[15;5~:FK=\E[17;5~:FL=\E[18;5~:FM=\E[19;5~:FN=\E[20;5~:FO=\E[21;5~:FP=\E[23;5~:FQ=\E[24;5~:FR=\E[1;6P:FS=\E[1;6Q:FT=\E[13;6~:FU=\E[1;6S:FV=\E[15;6~:FW=\E[17;6~:FX=\E[18;6~:FY=\E[19;6~:FZ=\E[20;6~:Fa=\E[21;6~:Fb=\E[23;6~:Fc=\E[24;6~:Fd=\E[1;3P:Fe=\E[1;3Q:Ff=\E[13;3~:Fg=\E[1;3S:Fh=\E[15;3~:Fi=\E[17;3~:Fj=\E[18;3~:Fk=\E[19;3~:Fl=\E[20;3~:Fm=\E[21;3~:Fn=\E[23;3~:Fo=\E[24;3~:Fp=\E[1;4P:Fq=\E[1;4Q:Fr=\E[13;4~:IC=\E[%d@:..Ic=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\:K1=:K3=:K4=:K5=:Km=\E[M:LE=\E[%dD:RA=\E[?7l:RI=\E[%dC:SA=\E[?7h:SF=\E[%dS:SR=\E[%dT:UP=\E[%dA:ZH=\E[3m:ZR=\E[23m:ac=++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~:ae=\E(B:al=\E[L:as=\E(0:bl=^G:bt=\E[Z:cb=\E[1K:cd=\E[J:ce=\E[K:ch=\E[%i%dG:cl=\E[H\E[2J:cm=\E[%i%d;%dH:cr=\r:cs=\E[%i%d;%dr:ct=\E[3g:cv=\E[%i%dd:dc=\E[P:dl=\E[M:do=\n:ds=\E]2;\007:ec=\E[%dX:ei=\E[4l:fs=^G:ho=\E[H:im=\E[4h:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:k5=\E[15~:k6=\E[17~:k7=\E[18~:k8=\E[19~:k9=\E[20~:k;=\E[21~:kB=\E[Z:kD=\E[3~:kF=\E[1;2B:kI=\E[2~:kN=\E[6~:kP=\E[5~:kR=\E[1;2A:kb=\177:kd=\EOB:ke=\E[?1l:kh=\EOH:kl=\EOD:kr=\EOC:ks=\E[?1h:ku=\EOA:le=^H:md=\E[1m:me=\E[0m:mh=\E[2m:mr=\E[7m:nd=\E[C:oc=\E]104\007:op=\E[39;49m:r1=\E]\E\\\Ec:rc=\E8:..rp=%p1%c\E[%p2%{1}%-%db:..sa=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m:sc=\E7:se=\E[27m:sf=\n:so=\E[7m:sr=\EM:st=\EH:ta=^I:te=\E[?1049l:ti=\E[?1049h:ts=\E]2;:u6=\E[%i%d;%dR:u7=\E[6n:..u8=\E[?%[;0123456789]c:u9=\E[c:ue=\E[24m:up=\E[A:us=\E[4m:vb=\E[?5h\E[?5l:ve=\E[?12h\E[?25h:vi=\E[?25l:vs=\E[?12;25h:
|
||||
@@ -144,7 +144,7 @@ xterm-kitty|KovIdTTY,
|
||||
kf12=\E[24~,
|
||||
kf13=\E[1;2P,
|
||||
kf14=\E[1;2Q,
|
||||
kf15=\E[1;2R,
|
||||
kf15=\E[13;2~,
|
||||
kf16=\E[1;2S,
|
||||
kf17=\E[15;2~,
|
||||
kf18=\E[17;2~,
|
||||
@@ -157,7 +157,7 @@ xterm-kitty|KovIdTTY,
|
||||
kf24=\E[24;2~,
|
||||
kf25=\E[1;5P,
|
||||
kf26=\E[1;5Q,
|
||||
kf27=\E[1;5R,
|
||||
kf27=\E[13;5~,
|
||||
kf28=\E[1;5S,
|
||||
kf29=\E[15;5~,
|
||||
kf3=\EOR,
|
||||
@@ -170,7 +170,7 @@ xterm-kitty|KovIdTTY,
|
||||
kf36=\E[24;5~,
|
||||
kf37=\E[1;6P,
|
||||
kf38=\E[1;6Q,
|
||||
kf39=\E[1;6R,
|
||||
kf39=\E[13;6~,
|
||||
kf4=\EOS,
|
||||
kf40=\E[1;6S,
|
||||
kf41=\E[15;6~,
|
||||
@@ -184,7 +184,7 @@ xterm-kitty|KovIdTTY,
|
||||
kf49=\E[1;3P,
|
||||
kf5=\E[15~,
|
||||
kf50=\E[1;3Q,
|
||||
kf51=\E[1;3R,
|
||||
kf51=\E[13;3~,
|
||||
kf52=\E[1;3S,
|
||||
kf53=\E[15;3~,
|
||||
kf54=\E[17;3~,
|
||||
@@ -197,7 +197,7 @@ xterm-kitty|KovIdTTY,
|
||||
kf60=\E[24;3~,
|
||||
kf61=\E[1;4P,
|
||||
kf62=\E[1;4Q,
|
||||
kf63=\E[1;4R,
|
||||
kf63=\E[13;4~,
|
||||
kf7=\E[18~,
|
||||
kf8=\E[19~,
|
||||
kf9=\E[20~,
|
||||
|
||||
Binary file not shown.
@@ -11,6 +11,25 @@ import (
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
func bash_completion_script(commands []string) (string, error) {
|
||||
return `_ksi_completions() {
|
||||
builtin local src
|
||||
builtin local limit
|
||||
# Send all words up to the word the cursor is currently on
|
||||
builtin let limit=1+$COMP_CWORD
|
||||
src=$(builtin printf "%s\n" "${COMP_WORDS[@]:0:$limit}" | builtin command kitten __complete__ bash)
|
||||
if [[ $? == 0 ]]; then
|
||||
builtin eval "${src}"
|
||||
fi
|
||||
}
|
||||
|
||||
builtin complete -F _ksi_completions kitty
|
||||
builtin complete -F _ksi_completions edit-in-kitty
|
||||
builtin complete -F _ksi_completions clone-in-kitty
|
||||
builtin complete -F _ksi_completions kitten
|
||||
`, nil
|
||||
}
|
||||
|
||||
func bash_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {
|
||||
output := strings.Builder{}
|
||||
f := func(format string, args ...any) { fmt.Fprintf(&output, format+"\n", args...) }
|
||||
@@ -51,6 +70,7 @@ func bash_init_completions(completions *Completions) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
completion_scripts["bash"] = bash_completion_script
|
||||
input_parsers["bash"] = shell_input_parser
|
||||
output_serializers["bash"] = bash_output_serializer
|
||||
init_completions["bash"] = bash_init_completions
|
||||
|
||||
@@ -520,28 +520,24 @@ func (self *Command) GetOptionValues(pointer_to_options_struct any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Command) Exec(args ...string) {
|
||||
func (self *Command) ExecArgs(args []string) (exit_code int) {
|
||||
root := self
|
||||
for root.Parent != nil {
|
||||
root = root.Parent
|
||||
}
|
||||
if len(args) == 0 {
|
||||
args = os.Args
|
||||
}
|
||||
cmd, err := root.ParseArgs(args)
|
||||
if err != nil {
|
||||
ShowError(err)
|
||||
os.Exit(1)
|
||||
return 1
|
||||
}
|
||||
help_opt := cmd.option_map["Help"]
|
||||
version_opt := root.option_map["Version"]
|
||||
exit_code := 0
|
||||
if help_opt != nil && help_opt.parsed_value().(bool) {
|
||||
cmd.ShowHelp()
|
||||
os.Exit(exit_code)
|
||||
return
|
||||
} else if version_opt != nil && version_opt.parsed_value().(bool) {
|
||||
root.ShowVersion()
|
||||
os.Exit(exit_code)
|
||||
return
|
||||
} else if cmd.Run != nil {
|
||||
exit_code, err = cmd.Run(cmd, cmd.Args)
|
||||
if err != nil {
|
||||
@@ -551,7 +547,14 @@ func (self *Command) Exec(args ...string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
os.Exit(exit_code)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Command) Exec(args ...string) {
|
||||
if len(args) == 0 {
|
||||
args = os.Args
|
||||
}
|
||||
os.Exit(self.ExecArgs(args))
|
||||
}
|
||||
|
||||
func (self *Command) GetCompletions(argv []string, init_completions func(*Completions)) *Completions {
|
||||
|
||||
@@ -30,9 +30,11 @@ func json_output_serializer(completions []*Completions, shell_state map[string]s
|
||||
return json.Marshal(completions)
|
||||
}
|
||||
|
||||
type completion_script_func func(commands []string) (string, error)
|
||||
type parser_func func(data []byte, shell_state map[string]string) ([][]string, error)
|
||||
type serializer_func func(completions []*Completions, shell_state map[string]string) ([]byte, error)
|
||||
|
||||
var completion_scripts = make(map[string]completion_script_func, 4)
|
||||
var input_parsers = make(map[string]parser_func, 4)
|
||||
var output_serializers = make(map[string]serializer_func, 4)
|
||||
var init_completions = make(map[string]func(*Completions), 4)
|
||||
@@ -61,6 +63,22 @@ func GenerateCompletions(args []string) error {
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
if output_type == "setup" {
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("The shell must be specified")
|
||||
}
|
||||
shell_name := args[0]
|
||||
args = args[1:]
|
||||
completion_script := completion_scripts[shell_name]
|
||||
if completion_script == nil {
|
||||
return fmt.Errorf("Unsupported shell: %s", shell_name)
|
||||
}
|
||||
output, err := completion_script(args)
|
||||
if err == nil {
|
||||
_, err = os.Stdout.WriteString(output)
|
||||
}
|
||||
return err
|
||||
}
|
||||
shell_state := make(map[string]string, n)
|
||||
for _, arg := range args {
|
||||
k, v, found := utils.Cut(arg, "=")
|
||||
|
||||
@@ -12,12 +12,51 @@ import (
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
func fish_completion_script(commands []string) (string, error) {
|
||||
// One command in fish requires one completion script.
|
||||
// Usage: kitten __complete__ setup fish [kitty|kitten|clone-in-kitty]
|
||||
all_commands := map[string]bool{
|
||||
"kitty": true,
|
||||
"clone-in-kitty": true,
|
||||
"kitten": true,
|
||||
}
|
||||
if len(commands) == 0 {
|
||||
commands = append(commands, utils.Keys(all_commands)...)
|
||||
}
|
||||
script := strings.Builder{}
|
||||
script.WriteString(`function __ksi_completions
|
||||
set --local ct (commandline --current-token)
|
||||
set --local tokens (commandline --tokenize --cut-at-cursor --current-process)
|
||||
printf "%s\n" $tokens $ct | command kitten __complete__ fish | source -
|
||||
end
|
||||
|
||||
`)
|
||||
for _, cmd := range commands {
|
||||
if all_commands[cmd] {
|
||||
fmt.Fprintf(&script, "complete -f -c %s -a \"(__ksi_completions)\"\n", cmd)
|
||||
} else if strings.Contains(cmd, "=") {
|
||||
// Reserved for `setup SHELL [KEY=VALUE ...]`, not used now.
|
||||
continue
|
||||
} else {
|
||||
return "", fmt.Errorf("No fish completion script for command: %s", cmd)
|
||||
}
|
||||
}
|
||||
return script.String(), nil
|
||||
}
|
||||
|
||||
func fish_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {
|
||||
output := strings.Builder{}
|
||||
f := func(format string, args ...any) { fmt.Fprintf(&output, format+"\n", args...) }
|
||||
n := completions[0].Delegate.NumToRemove
|
||||
fm := markup.New(false) // fish freaks out if there are escape codes in the description strings
|
||||
if n > 0 {
|
||||
legacy_completion := shell_state["_legacy_completion"]
|
||||
if legacy_completion == "fish2" {
|
||||
for _, mg := range completions[0].Groups {
|
||||
for _, m := range mg.Matches {
|
||||
f("%s", strings.ReplaceAll(m.Word+"\t"+fm.Prettify(m.Description), "\n", " "))
|
||||
}
|
||||
}
|
||||
} else if n > 0 {
|
||||
words := make([]string, len(completions[0].AllWords)-n+1)
|
||||
words[0] = completions[0].Delegate.Command
|
||||
copy(words[1:], completions[0].AllWords[n:])
|
||||
@@ -40,6 +79,7 @@ func fish_output_serializer(completions []*Completions, shell_state map[string]s
|
||||
}
|
||||
|
||||
func init() {
|
||||
completion_scripts["fish"] = fish_completion_script
|
||||
input_parsers["fish"] = shell_input_parser
|
||||
output_serializers["fish"] = fish_output_serializer
|
||||
}
|
||||
|
||||
@@ -15,6 +15,25 @@ import (
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
func zsh_completion_script(commands []string) (string, error) {
|
||||
return `#compdef kitty
|
||||
|
||||
_kitty() {
|
||||
(( ${+commands[kitten]} )) || builtin return
|
||||
builtin local src cmd=${(F)words:0:$CURRENT}
|
||||
# Send all words up to the word the cursor is currently on.
|
||||
src=$(builtin command kitten __complete__ zsh "_matcher=$_matcher" <<<$cmd) || builtin return
|
||||
builtin eval "$src"
|
||||
}
|
||||
|
||||
if (( $+functions[compdef] )); then
|
||||
compdef _kitty kitty
|
||||
compdef _kitty clone-in-kitty
|
||||
compdef _kitty kitten
|
||||
fi
|
||||
`, nil
|
||||
}
|
||||
|
||||
func shell_input_parser(data []byte, shell_state map[string]string) ([][]string, error) {
|
||||
raw := string(data)
|
||||
new_word := strings.HasSuffix(raw, "\n\n")
|
||||
@@ -150,6 +169,7 @@ func zsh_output_serializer(completions []*Completions, shell_state map[string]st
|
||||
}
|
||||
|
||||
func init() {
|
||||
completion_scripts["zsh"] = zsh_completion_script
|
||||
input_parsers["zsh"] = zsh_input_parser
|
||||
output_serializers["zsh"] = zsh_output_serializer
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ var ProtocolVersion [3]int = [3]int{0, 26, 0}
|
||||
type GlobalOptions struct {
|
||||
to_network, to_address, password string
|
||||
to_address_is_from_env_var bool
|
||||
already_setup bool
|
||||
}
|
||||
|
||||
var global_options GlobalOptions
|
||||
@@ -367,6 +368,9 @@ func register_at_cmd(f func(*cli.Command) *cli.Command) {
|
||||
}
|
||||
|
||||
func setup_global_options(cmd *cli.Command) (err error) {
|
||||
if global_options.already_setup {
|
||||
return nil
|
||||
}
|
||||
err = cmd.GetOptionValues(&rc_global_opts)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -385,6 +389,7 @@ func setup_global_options(cmd *cli.Command) (err error) {
|
||||
}
|
||||
q, err := get_password(rc_global_opts.Password, rc_global_opts.PasswordFile, rc_global_opts.PasswordEnv, rc_global_opts.UsePassword)
|
||||
global_options.password = q
|
||||
global_options.already_setup = true
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -110,6 +109,8 @@ func show_basic_help() {
|
||||
fmt.Fprintln(&output, " ", sc.ShortDescription)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(&output, " ", formatter.Green("help"))
|
||||
fmt.Fprintln(&output, " ", "Show this help")
|
||||
fmt.Fprintln(&output, " ", formatter.Green("exit"))
|
||||
fmt.Fprintln(&output, " ", "Exit this shell")
|
||||
cli.ShowHelpInPager(output.String())
|
||||
@@ -159,26 +160,12 @@ func exec_command(at_root_command *cli.Command, rl *readline.Readline, cmdline s
|
||||
fmt.Fprintln(os.Stderr, "No command named", formatter.BrightRed(parsed_cmdline[0])+". Type help for a list of commands")
|
||||
return true
|
||||
}
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
exe, err = exec.LookPath("kitten")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Could not find the kitten executable")
|
||||
return false
|
||||
}
|
||||
}
|
||||
cmdline := []string{"kitten", "@"}
|
||||
cmdline = append(cmdline, parsed_cmdline...)
|
||||
cmd := exec.Cmd{Path: exe, Args: cmdline, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr}
|
||||
err = cmd.Run()
|
||||
root := cli.NewRootCommand()
|
||||
EntryPoint(root)
|
||||
hi.ExitCode = root.ExecArgs(cmdline)
|
||||
hi.Duration = time.Now().Sub(hi.Timestamp)
|
||||
hi.ExitCode = 0
|
||||
if err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
hi.ExitCode = exitError.ExitCode()
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
rl.AddHistoryItem(hi)
|
||||
}
|
||||
return true
|
||||
@@ -212,6 +199,10 @@ func completions(before_cursor, after_cursor string) (ans *cli.Completions) {
|
||||
}
|
||||
|
||||
func shell_main(cmd *cli.Command, args []string) (int, error) {
|
||||
err := setup_global_options(cmd)
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
formatter = markup.New(true)
|
||||
fmt.Println("Welcome to the kitty shell!")
|
||||
fmt.Println("Use", formatter.Green("help"), "for assistance or", formatter.Green("exit"), "to quit.")
|
||||
|
||||
@@ -87,7 +87,7 @@ func EntryPoint(tool_root *cli.Command) {
|
||||
Name: "__complete__", Hidden: true,
|
||||
Usage: "output_type [shell state...]",
|
||||
ShortDescription: "Generate completions for kitty commands",
|
||||
HelpText: "Generate completion candidates for kitty commands. The command line is read from STDIN. output_type can be one of the supported shells or 'json' for JSON output.",
|
||||
HelpText: "Generate completion candidates for kitty commands. The command line is read from STDIN. output_type can be one of the supported shells: :code:`zsh`, :code:`fish`, :code:`bash`, or :code:`setup` for completion setup script following with the shell name, or :code:`json` for JSON output.",
|
||||
Run: func(cmd *cli.Command, args []string) (ret int, err error) {
|
||||
return ret, cli.GenerateCompletions(args)
|
||||
},
|
||||
|
||||
@@ -235,7 +235,7 @@ type Options struct {
|
||||
func EntryPoint(parent *cli.Command) *cli.Command {
|
||||
sc := parent.AddSubCommand(&cli.Command{
|
||||
Name: "edit-in-kitty",
|
||||
Usage: "edit-in-kitty [options] file-to-edit",
|
||||
Usage: "[options] file-to-edit",
|
||||
ShortDescription: "Edit a file in a kitty overlay window",
|
||||
HelpText: "Edit the specified file in a kitty overlay window. Works over SSH as well.\n\n" +
|
||||
"For usage instructions see: https://sw.kovidgoyal.net/kitty/shell-integration/#edit-file",
|
||||
|
||||
@@ -4,11 +4,13 @@ package icat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
not_rand "math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -202,6 +204,19 @@ func place_cursor(imgd *image_data) {
|
||||
}
|
||||
}
|
||||
|
||||
func next_random() (ans uint32) {
|
||||
for ans == 0 {
|
||||
b := make([]byte, 4)
|
||||
_, err := rand.Read(b)
|
||||
if err == nil {
|
||||
ans = binary.LittleEndian.Uint32(b[:])
|
||||
} else {
|
||||
ans = not_rand.Uint32()
|
||||
}
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
func transmit_image(imgd *image_data) {
|
||||
defer func() {
|
||||
for _, frame := range imgd.frames {
|
||||
@@ -239,7 +254,7 @@ func transmit_image(imgd *image_data) {
|
||||
}
|
||||
if len(imgd.frames) > 1 {
|
||||
for imgd.image_number == 0 {
|
||||
imgd.image_number = rand.Uint32()
|
||||
imgd.image_number = next_random()
|
||||
}
|
||||
}
|
||||
place_cursor(imgd)
|
||||
|
||||
@@ -18,7 +18,7 @@ var _ = fmt.Print
|
||||
|
||||
func KittyToolEntryPoints(root *cli.Command) {
|
||||
root.Add(cli.OptionSpec{
|
||||
Name: "--version", Type: "bool-set", Help: "The current kitty version."})
|
||||
Name: "--version", Type: "bool-set", Help: "The current kitten version."})
|
||||
// @
|
||||
at.EntryPoint(root)
|
||||
// update-self
|
||||
|
||||
@@ -71,7 +71,7 @@ func update_self(version string) (err error) {
|
||||
func EntryPoint(root *cli.Command) *cli.Command {
|
||||
sc := root.AddSubCommand(&cli.Command{
|
||||
Name: "update-self",
|
||||
Usage: "update-self [options ...]",
|
||||
Usage: "[options]",
|
||||
ShortDescription: "Update this kitten binary",
|
||||
HelpText: "Update this kitten binary in place to the latest available version.",
|
||||
Run: func(cmd *cli.Command, args []string) (ret int, err error) {
|
||||
@@ -89,7 +89,7 @@ func EntryPoint(root *cli.Command) *cli.Command {
|
||||
sc.Add(cli.OptionSpec{
|
||||
Name: "--fetch-version",
|
||||
Default: "latest",
|
||||
Help: "The version to fetch. The special words :code:`latest` and :code:`nightly` fetch the latest stable and nightly release respectively. Other values can be, for example: 0.27.1.",
|
||||
Help: fmt.Sprintf("The version to fetch. The special words :code:`latest` and :code:`nightly` fetch the latest stable and nightly release respectively. Other values can be, for example: :code:`%s`.", kitty.VersionString),
|
||||
})
|
||||
return sc
|
||||
}
|
||||
|
||||
@@ -6,27 +6,39 @@ package crypto
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdh"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/jamesruan/go-rfc1924/base85"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"kitty/tools/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
func curve25519_key_pair() (private_key []byte, public_key []byte, err error) {
|
||||
private_key = make([]byte, 32)
|
||||
_, err = rand.Read(private_key)
|
||||
curve := ecdh.X25519()
|
||||
privkey, err := curve.GenerateKey(rand.Reader)
|
||||
if err == nil {
|
||||
public_key, err = curve25519.X25519(private_key[:], curve25519.Basepoint)
|
||||
pubkey := privkey.PublicKey()
|
||||
return privkey.Bytes(), pubkey.Bytes(), nil
|
||||
}
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
func curve25519_derive_shared_secret(private_key []byte, public_key []byte) (secret []byte, err error) {
|
||||
secret, err = curve25519.X25519(private_key[:], public_key[:])
|
||||
prkey, err := ecdh.X25519().NewPrivateKey(private_key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid X25519 private key: %w", err)
|
||||
}
|
||||
pubkey, err := ecdh.X25519().NewPublicKey(public_key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid X25519 public key: %w", err)
|
||||
}
|
||||
secret, err = prkey.ECDH(pubkey)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to perform ECDH shared secret derivation: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -544,11 +544,12 @@ func (self *GraphicsCommand) WriteWithPayloadTo(o io.StringWriter, payload []byt
|
||||
return self.serialize_to(o, base64.StdEncoding.EncodeToString(payload))
|
||||
}
|
||||
gc := *self
|
||||
compressed := compress_with_zlib(payload)
|
||||
if len(compressed) < len(payload) {
|
||||
gc.SetCompression(GRT_compression_zlib)
|
||||
gc.SetDataSize(uint64(len(payload)))
|
||||
payload = compressed
|
||||
if self.Format() != GRT_format_png {
|
||||
compressed := compress_with_zlib(payload)
|
||||
if len(compressed) < len(payload) {
|
||||
gc.SetCompression(GRT_compression_zlib)
|
||||
payload = compressed
|
||||
}
|
||||
}
|
||||
data := base64.StdEncoding.EncodeToString(payload)
|
||||
for len(data) > 0 && err == nil {
|
||||
@@ -565,9 +566,10 @@ func (self *GraphicsCommand) WriteWithPayloadTo(o io.StringWriter, payload []byt
|
||||
gc.m = GRT_more_nomore
|
||||
}
|
||||
err = gc.serialize_to(o, chunk)
|
||||
if gc.DataSize() > 0 {
|
||||
gc = GraphicsCommand{}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gc = GraphicsCommand{q: self.q, a: self.a}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -93,6 +93,10 @@ func (self KeyModifiers) String() string {
|
||||
return strings.Join(ans, "+")
|
||||
}
|
||||
|
||||
func (self KeyModifiers) HasCapsLock() bool {
|
||||
return self&CAPS_LOCK != 0
|
||||
}
|
||||
|
||||
type KeyEvent struct {
|
||||
Type KeyEventType
|
||||
Mods KeyModifiers
|
||||
@@ -121,6 +125,10 @@ func (self *KeyEvent) String() string {
|
||||
return ans + "}"
|
||||
}
|
||||
|
||||
func (self *KeyEvent) HasCapsLock() bool {
|
||||
return self.Mods.HasCapsLock()
|
||||
}
|
||||
|
||||
func KeyEventFromCSI(csi string) *KeyEvent {
|
||||
if len(csi) == 0 {
|
||||
return nil
|
||||
|
||||
@@ -26,10 +26,28 @@ func ReadPassword(prompt string, kill_if_signaled bool) (password string, err er
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
capspress_was_locked := false
|
||||
has_caps_lock := false
|
||||
|
||||
redraw_prompt := func() {
|
||||
text := prompt + shadow
|
||||
lp.QueueWriteString("\r")
|
||||
lp.ClearToEndOfLine()
|
||||
if has_caps_lock {
|
||||
lp.QueueWriteString("\x1b[31m[CapsLock on!]\x1b[39m ")
|
||||
}
|
||||
lp.QueueWriteString(text)
|
||||
}
|
||||
|
||||
lp.OnInitialize = func() (string, error) {
|
||||
lp.QueueWriteString(prompt)
|
||||
return "\r\n", nil
|
||||
lp.SetCursorShape(loop.BAR_CURSOR, true)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
lp.OnFinalize = func() string {
|
||||
lp.SetCursorShape(loop.BLOCK_CURSOR, true)
|
||||
return "\r\n"
|
||||
}
|
||||
|
||||
lp.OnText = func(text string, from_key_event bool, in_bracketed_paste bool) error {
|
||||
@@ -45,6 +63,22 @@ func ReadPassword(prompt string, kill_if_signaled bool) (password string, err er
|
||||
}
|
||||
|
||||
lp.OnKeyEvent = func(event *loop.KeyEvent) error {
|
||||
has_caps := false
|
||||
if strings.ToLower(event.Key) == "caps_lock" {
|
||||
if event.Type == loop.RELEASE {
|
||||
has_caps = !capspress_was_locked
|
||||
capspress_was_locked = false
|
||||
} else {
|
||||
capspress_was_locked = event.HasCapsLock()
|
||||
has_caps = true
|
||||
}
|
||||
} else {
|
||||
has_caps = event.HasCapsLock()
|
||||
}
|
||||
if has_caps_lock != has_caps {
|
||||
has_caps_lock = has_caps
|
||||
redraw_prompt()
|
||||
}
|
||||
if event.MatchesPressOrRepeat("backspace") || event.MatchesPressOrRepeat("delete") {
|
||||
event.Handled = true
|
||||
if len(password) > 0 {
|
||||
@@ -80,7 +114,7 @@ func ReadPassword(prompt string, kill_if_signaled bool) (password string, err er
|
||||
}
|
||||
|
||||
lp.OnResumeFromStop = func() error {
|
||||
lp.QueueWriteString("\r" + prompt + shadow)
|
||||
redraw_prompt()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
package shm
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
not_rand "math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -42,8 +44,12 @@ func prefix_and_suffix(pattern string) (prefix, suffix string, err error) {
|
||||
}
|
||||
|
||||
func next_random() string {
|
||||
num := rand.Uint32()
|
||||
return strconv.FormatUint(uint64(num), 16)
|
||||
b := make([]byte, 8)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return strconv.FormatUint(uint64(not_rand.Uint32()), 16)
|
||||
}
|
||||
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b)
|
||||
}
|
||||
|
||||
type MMap interface {
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
package shm
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"math/rand"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -21,6 +21,5 @@ func UnsafeStringToBytes(s string) (b []byte) {
|
||||
// If you modify b, then s will also be modified. This violates the
|
||||
// property that strings are immutable.
|
||||
func UnsafeBytesToString(b []byte) (s string) {
|
||||
unsafe.String(unsafe.SliceData(b), len(b))
|
||||
return s
|
||||
return unsafe.String(unsafe.SliceData(b), len(b))
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||
//go:build !go1.20
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// stringHeader is the runtime representation of a string.
|
||||
// It should be identical to reflect.StringHeader
|
||||
type stringHeader struct {
|
||||
data unsafe.Pointer
|
||||
stringLen int
|
||||
}
|
||||
|
||||
// sliceHeader is the runtime representation of a slice.
|
||||
// It should be identical to reflect.sliceHeader
|
||||
type sliceHeader struct {
|
||||
data unsafe.Pointer
|
||||
sliceLen int
|
||||
sliceCap int
|
||||
}
|
||||
|
||||
// Unsafely converts s into a byte slice.
|
||||
// If you modify b, then s will also be modified. This violates the
|
||||
// property that strings are immutable.
|
||||
func UnsafeStringToBytes(s string) (b []byte) {
|
||||
stringHeader := (*stringHeader)(unsafe.Pointer(&s))
|
||||
sliceHeader := (*sliceHeader)(unsafe.Pointer(&b))
|
||||
sliceHeader.data = stringHeader.data
|
||||
sliceHeader.sliceLen = len(s)
|
||||
sliceHeader.sliceCap = len(s)
|
||||
return b
|
||||
}
|
||||
|
||||
// Unsafely converts b into a string.
|
||||
// If you modify b, then s will also be modified. This violates the
|
||||
// property that strings are immutable.
|
||||
func UnsafeBytesToString(b []byte) (s string) {
|
||||
sliceHeader := (*sliceHeader)(unsafe.Pointer(&b))
|
||||
stringHeader := (*stringHeader)(unsafe.Pointer(&s))
|
||||
stringHeader.data = sliceHeader.data
|
||||
stringHeader.stringLen = len(b)
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user