Compare commits
79 Commits
v0.28.0
...
bold_is_br
| Author | SHA1 | Date | |
|---|---|---|---|
| 129186761c | |||
|
|
491297ea1d | ||
|
|
c101a6acb0 | ||
|
|
65f8bb7397 | ||
|
|
5b8b91b6a3 | ||
|
|
6a2edfa847 | ||
|
|
28b84a2d5b | ||
|
|
c247fe2336 | ||
|
|
c883a024ba | ||
|
|
0cc38e1086 | ||
|
|
1777b87c45 | ||
|
|
e72975cc98 | ||
|
|
8f15654985 | ||
|
|
2408ccb635 | ||
|
|
a0cf4214df | ||
|
|
07203c67ca | ||
|
|
a36fe45181 | ||
|
|
061c444f20 | ||
|
|
a1d791083b | ||
|
|
454acd4f5c | ||
|
|
71189aee9f | ||
|
|
23d7494e3a | ||
|
|
404f83a277 | ||
|
|
474244268c | ||
|
|
79cd6f38fe | ||
|
|
b7c3946f8f | ||
|
|
537cabca71 | ||
|
|
79c19562b5 | ||
|
|
52afc79476 | ||
|
|
877d8d7008 | ||
|
|
ce70320a62 | ||
|
|
3eb18a416a | ||
|
|
8ba7258db9 | ||
|
|
a502e94950 | ||
|
|
ea5634b3fd | ||
|
|
87943079fb | ||
|
|
a77b2b20c2 | ||
|
|
8f96395f74 | ||
|
|
1fc4e53bea | ||
|
|
f6ccd2ad2c | ||
|
|
bc2af4acf9 | ||
|
|
07dbfaa297 | ||
|
|
8020d5823b | ||
|
|
73f10aaf43 | ||
|
|
59c4d4a4bd | ||
|
|
ef999c9024 | ||
|
|
514888a274 | ||
|
|
09ebdcd809 | ||
|
|
8ebe4084cc | ||
|
|
9f41183628 | ||
|
|
289957ef1c | ||
|
|
920b350ac9 | ||
|
|
d14655f644 | ||
|
|
29583411e6 | ||
|
|
019359b219 | ||
|
|
7b6d11fd1e | ||
|
|
bb33c66570 | ||
|
|
c2fc4eadc8 | ||
|
|
a7b4d07601 | ||
|
|
6a07435bb0 | ||
|
|
93a5107e79 | ||
|
|
6cc8e67580 | ||
|
|
ccdb951716 | ||
|
|
07bcc5ba61 | ||
|
|
6e90bc1996 | ||
|
|
6269f78ed2 | ||
|
|
dd0e1cce9e | ||
|
|
92e68a6e0c | ||
|
|
e4baca6d97 | ||
|
|
a09464dee9 | ||
|
|
b966013a2b | ||
|
|
046fbb860b | ||
|
|
91700b3e42 | ||
|
|
b314303787 | ||
|
|
176cfe771c | ||
|
|
3b57acf03c | ||
|
|
77e2572c5a | ||
|
|
39eff0fe8c | ||
|
|
12efff6d08 |
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@ -1,4 +1,2 @@
|
|||||||
github: kovidgoyal
|
custom: https://my.fsf.org/donate
|
||||||
patreon: kovidgoyal
|
|
||||||
liberapay: kovidgoyal
|
|
||||||
custom: https://sw.kovidgoyal.net/kitty/support.html
|
custom: https://sw.kovidgoyal.net/kitty/support.html
|
||||||
|
|||||||
@ -58,6 +58,7 @@ Action Shortcut
|
|||||||
New window :sc:`new_window` (also :kbd:`⌘+↩` on macOS)
|
New window :sc:`new_window` (also :kbd:`⌘+↩` on macOS)
|
||||||
New OS window :sc:`new_os_window` (also :kbd:`⌘+n` on macOS)
|
New OS window :sc:`new_os_window` (also :kbd:`⌘+n` on macOS)
|
||||||
Close window :sc:`close_window` (also :kbd:`⇧+⌘+d` on macOS)
|
Close window :sc:`close_window` (also :kbd:`⇧+⌘+d` on macOS)
|
||||||
|
Resize window :sc:`start_resizing_window` (also :kbd:`⌘+r` on macOS)
|
||||||
Next window :sc:`next_window`
|
Next window :sc:`next_window`
|
||||||
Previous window :sc:`previous_window`
|
Previous window :sc:`previous_window`
|
||||||
Move window forward :sc:`move_window_forward`
|
Move window forward :sc:`move_window_forward`
|
||||||
|
|||||||
@ -31,7 +31,7 @@ Manually installing
|
|||||||
|
|
||||||
If something goes wrong or you simply do not want to run the installer, you can
|
If something goes wrong or you simply do not want to run the installer, you can
|
||||||
manually download and install |kitty| from the `GitHub releases page
|
manually download and install |kitty| from the `GitHub releases page
|
||||||
<https://github.com/kovidgoyal/kitty/releases>`__. If you are on macOS, download
|
<https://gitea.rexy712.xyz/KittyPatch/kitty/releases>`__. If you are on macOS, download
|
||||||
the :file:`.dmg` and install as normal. If you are on Linux, download the
|
the :file:`.dmg` and install as normal. If you are on Linux, download the
|
||||||
tarball and extract it into a directory. The |kitty| executable will be in the
|
tarball and extract it into a directory. The |kitty| executable will be in the
|
||||||
:file:`bin` sub-directory.
|
:file:`bin` sub-directory.
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
Build from source
|
Build from source
|
||||||
==================
|
==================
|
||||||
|
|
||||||
.. image:: https://github.com/kovidgoyal/kitty/workflows/CI/badge.svg
|
.. image:: https://gitea.rexy712.xyz/KittyPatch/kitty/workflows/CI/badge.svg
|
||||||
:alt: Build status
|
:alt: Build status
|
||||||
:target: https://github.com/kovidgoyal/kitty/actions?query=workflow%3ACI
|
:target: https://gitea.rexy712.xyz/KittyPatch/kitty/actions?query=workflow%3ACI
|
||||||
|
|
||||||
.. highlight:: sh
|
.. highlight:: sh
|
||||||
|
|
||||||
@ -61,6 +61,7 @@ Build-time dependencies:
|
|||||||
- ``libfontconfig-dev``
|
- ``libfontconfig-dev``
|
||||||
- ``libx11-xcb-dev``
|
- ``libx11-xcb-dev``
|
||||||
- ``liblcms2-dev``
|
- ``liblcms2-dev``
|
||||||
|
- ``libssl-dev``
|
||||||
- ``libpython3-dev``
|
- ``libpython3-dev``
|
||||||
- ``librsync-dev``
|
- ``librsync-dev``
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ Install and run from source
|
|||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
git clone https://github.com/kovidgoyal/kitty && cd kitty
|
git clone https://gitea.rexy712.xyz/KittyPatch/kitty && cd kitty
|
||||||
|
|
||||||
Now build the native code parts of |kitty| with the following command::
|
Now build the native code parts of |kitty| with the following command::
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ dependencies you might have to rebuild the app.
|
|||||||
.. note::
|
.. note::
|
||||||
The released :file:`kitty.dmg` includes all dependencies, unlike the
|
The released :file:`kitty.dmg` includes all dependencies, unlike the
|
||||||
:file:`kitty.app` built above and is built automatically by using the
|
:file:`kitty.app` built above and is built automatically by using the
|
||||||
`bypy framework <https://github.com/kovidgoyal/bypy>`__ however, that is
|
`bypy framework <https://gitea.rexy712.xyz/KittyPatch/bypy>`__ however, that is
|
||||||
designed to run on Linux and is not for the faint of heart.
|
designed to run on Linux and is not for the faint of heart.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
@ -154,7 +155,7 @@ Notes for Linux/macOS packagers
|
|||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
The released |kitty| source code is available as a `tarball`_ from
|
The released |kitty| source code is available as a `tarball`_ from
|
||||||
`the GitHub releases page <https://github.com/kovidgoyal/kitty/releases>`__.
|
`the GitHub releases page <https://gitea.rexy712.xyz/KittyPatch/kitty/releases>`__.
|
||||||
|
|
||||||
While |kitty| does use Python, it is not a traditional Python package, so please
|
While |kitty| does use Python, it is not a traditional Python package, so please
|
||||||
do not install it in site-packages.
|
do not install it in site-packages.
|
||||||
|
|||||||
@ -35,6 +35,45 @@ mouse anywhere in the current command to move the cursor there. See
|
|||||||
Detailed list of changes
|
Detailed list of changes
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
|
0.28.2 [future]
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- A new escape code ``<ESC>[22J`` that moves the current contents of the screen into the scrollback before clearing it
|
||||||
|
|
||||||
|
- unicode_input kitten: Fix a regression in 0.28.0 that caused the order of recent and favorites entries to not be respected (:iss:`6214`)
|
||||||
|
|
||||||
|
- unicode_input kitten: Fix a regression in 0.28.0 that caused editing of favorites to sometimes hang
|
||||||
|
|
||||||
|
- clipboard kitten: Fix a bug causing the last MIME type available on the clipboard not being recognized when pasting
|
||||||
|
|
||||||
|
- Fix regression in 0.28.0 causing color fringing when rendering in transparent windows on light backgrounds (:iss:`6209`)
|
||||||
|
|
||||||
|
- show_key kitten: In kitty mode show the actual bytes sent by the terminal rather than a re-encoding of the parsed key event
|
||||||
|
|
||||||
|
- hints kitten: Fix a regression in 0.28.0 that broke using sub-groups in regexp captures (:iss:`6228`)
|
||||||
|
|
||||||
|
- hints kitten: Fix a regression in 0.28.0 that broke using lookahead/lookbehind in regexp captures (:iss:`6265`)
|
||||||
|
|
||||||
|
- diff kitten: Fix a regression in 0.28.0 that broke using relative paths as arguments to the kitten (:iss:`6325`)
|
||||||
|
|
||||||
|
- Fix re-using the image id of an animated image for a still image causing a crash (:iss:`6244`)
|
||||||
|
|
||||||
|
- kitty +open: Ask for permission before executing script files that are not marked as executable. This prevents accidental execution
|
||||||
|
of script files via MIME type association from programs that unconditionally "open" attachments/downloaded files
|
||||||
|
|
||||||
|
- edit-in-kitty: Fix running edit-in-kitty with elevated privileges to edit a restricted file not working (:disc:`6245`)
|
||||||
|
|
||||||
|
- ssh kitten: Fix a regression in 0.28.0 that caused interrupt during setup to not be handled gracefully (:iss:`6254`)
|
||||||
|
|
||||||
|
0.28.1 [2023-04-21]
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Fix a regression in the previous release that broke the remote file kitten (:iss:`6186`)
|
||||||
|
|
||||||
|
- Fix a regression in the previous release that broke handling of some keyboard shortcuts in some kittens on some keyboard layouts (:iss:`6189`)
|
||||||
|
|
||||||
|
- Fix a regression in the previous release that broke usage of custom themes (:iss:`6191`)
|
||||||
|
|
||||||
0.28.0 [2023-04-15]
|
0.28.0 [2023-04-15]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
@ -33,8 +33,8 @@ from kitty.constants import str_version, website_url # noqa
|
|||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'kitty'
|
project = 'kitty'
|
||||||
copyright = time.strftime('%Y, Kovid Goyal')
|
copyright = time.strftime('%Y, Kovid Goyal, KittyPatch')
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal, KittyPatch'
|
||||||
building_man_pages = 'man' in sys.argv
|
building_man_pages = 'man' in sys.argv
|
||||||
|
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
@ -100,7 +100,7 @@ exclude_patterns = [
|
|||||||
rst_prolog = '''
|
rst_prolog = '''
|
||||||
.. |kitty| replace:: *kitty*
|
.. |kitty| replace:: *kitty*
|
||||||
.. |version| replace:: VERSION
|
.. |version| replace:: VERSION
|
||||||
.. _tarball: https://github.com/kovidgoyal/kitty/releases/download/vVERSION/kitty-VERSION.tar.xz
|
.. _tarball: https://gitea.rexy712.xyz/KittyPatch/kitty/releases/download/vVERSION/kitty-VERSION.tar.xz
|
||||||
.. role:: italic
|
.. role:: italic
|
||||||
|
|
||||||
'''.replace('VERSION', str_version)
|
'''.replace('VERSION', str_version)
|
||||||
@ -215,7 +215,7 @@ def commit_role(
|
|||||||
f'GitHub commit id "{text}" not recognized.', line=lineno)
|
f'GitHub commit id "{text}" not recognized.', line=lineno)
|
||||||
prb = inliner.problematic(rawtext, rawtext, msg)
|
prb = inliner.problematic(rawtext, rawtext, msg)
|
||||||
return [prb], [msg]
|
return [prb], [msg]
|
||||||
url = f'https://github.com/kovidgoyal/kitty/commit/{commit_id}'
|
url = f'https://gitea.rexy712.xyz/KittyPatch/kitty/commit/{commit_id}'
|
||||||
set_classes(options)
|
set_classes(options)
|
||||||
short_id = subprocess.check_output(
|
short_id = subprocess.check_output(
|
||||||
f'git rev-list --max-count=1 --abbrev-commit --skip=# {commit_id}'.split()).decode('utf-8').strip()
|
f'git rev-list --max-count=1 --abbrev-commit --skip=# {commit_id}'.split()).decode('utf-8').strip()
|
||||||
|
|||||||
@ -58,7 +58,8 @@ Getting the window size
|
|||||||
|
|
||||||
In order to know what size of images to display and how to position them, the
|
In order to know what size of images to display and how to position them, the
|
||||||
client must be able to get the window size in pixels and the number of cells
|
client must be able to get the window size in pixels and the number of cells
|
||||||
per row and column. This can be done by using the ``TIOCGWINSZ`` ioctl. Some
|
per row and column. The cell width is then simply the window size divided by the
|
||||||
|
number of rows. This can be done by using the ``TIOCGWINSZ`` ioctl. Some
|
||||||
code to demonstrate its use
|
code to demonstrate its use
|
||||||
|
|
||||||
.. tab:: C
|
.. tab:: C
|
||||||
@ -99,6 +100,20 @@ code to demonstrate its use
|
|||||||
fmt.Println("rows: %v columns: %v width: %v height %v", sz.Row, sz.Col, sz.Xpixel, sz.Ypixel)
|
fmt.Println("rows: %v columns: %v width: %v height %v", sz.Row, sz.Col, sz.Xpixel, sz.Ypixel)
|
||||||
|
|
||||||
|
|
||||||
|
.. tab:: Bash
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This uses the kitten standalone binary from kitty to get the pixel sizes
|
||||||
|
# since we cant do IOCTLs directly. Fortunately, kitten is a static exe
|
||||||
|
# pre-built for every Unix like OS under the sun.
|
||||||
|
|
||||||
|
builtin read -r rows cols < <(command stty size)
|
||||||
|
IFS=x builtin read -r width height < <(command kitten icat --print-window-size); builtin unset IFS
|
||||||
|
builtin echo "number of rows: $rows number of columns: $cols screen width: $width screen height: $height"
|
||||||
|
|
||||||
|
|
||||||
Note that some terminals return ``0`` for the width and height values. Such
|
Note that some terminals return ``0`` for the width and height values. Such
|
||||||
terminals should be modified to return the correct values. Examples of
|
terminals should be modified to return the correct values. Examples of
|
||||||
|
|||||||
@ -80,6 +80,11 @@ base application that uses kitty's graphics protocol for images.
|
|||||||
A text mode WWW browser that supports kitty's graphics protocol to display
|
A text mode WWW browser that supports kitty's graphics protocol to display
|
||||||
images.
|
images.
|
||||||
|
|
||||||
|
`awrit <https://github.com/chase/awrit>`__
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
A full Chromium based web browser running in the terminal using kitty's
|
||||||
|
graphics protocol.
|
||||||
|
|
||||||
.. _tool_mpv:
|
.. _tool_mpv:
|
||||||
|
|
||||||
`mpv <https://github.com/mpv-player/mpv/commit/874e28f4a41a916bb567a882063dd2589e9234e1>`_
|
`mpv <https://github.com/mpv-player/mpv/commit/874e28f4a41a916bb567a882063dd2589e9234e1>`_
|
||||||
|
|||||||
@ -44,7 +44,8 @@ You can also create your own themes as :file:`.conf` files. Put them in the
|
|||||||
usually, :file:`~/.config/kitty/themes`. The kitten will automatically add them
|
usually, :file:`~/.config/kitty/themes`. The kitten will automatically add them
|
||||||
to the list of themes. You can use this to modify the builtin themes, by giving
|
to the list of themes. You can use this to modify the builtin themes, by giving
|
||||||
the conf file the name :file:`Some theme name.conf` to override the builtin
|
the conf file the name :file:`Some theme name.conf` to override the builtin
|
||||||
theme of that name. Note that after doing so you have to run the kitten and
|
theme of that name. Here, ``Some theme name`` is the actual builtin theme name, not
|
||||||
|
its file name. Note that after doing so you have to run the kitten and
|
||||||
choose that theme once for your changes to be applied.
|
choose that theme once for your changes to be applied.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,21 @@
|
|||||||
|
A message from us at KittyPatch
|
||||||
|
===============================
|
||||||
|
|
||||||
|
KittyPatch was created as a home for useful features that are unavailable
|
||||||
|
in the kitty main branch. To this end, we do not accept donations directly.
|
||||||
|
|
||||||
|
If you wish to support KittyPatch: share your ideas and spread the word.
|
||||||
|
|
||||||
|
If you still wish to donate, please `support the Free Software Foundation
|
||||||
|
<https://my.fsf.org/donate>`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
A message from the maintainer of Kitty
|
||||||
|
======================================
|
||||||
Support kitty development ❤️
|
Support kitty development ❤️
|
||||||
==============================
|
==============================
|
||||||
|
>>>>>>> upstream/master
|
||||||
|
|
||||||
My goal with |kitty| is to move the stagnant terminal ecosystem forward. To that
|
My goal with |kitty| is to move the stagnant terminal ecosystem forward. To that
|
||||||
end kitty has many foundational features, such as: :doc:`image support
|
end kitty has many foundational features, such as: :doc:`image support
|
||||||
|
|||||||
@ -1014,7 +1014,7 @@ static pthread_t main_thread;
|
|||||||
static NSLock *tick_lock = NULL;
|
static NSLock *tick_lock = NULL;
|
||||||
|
|
||||||
|
|
||||||
void _glfwDispatchTickCallback() {
|
void _glfwDispatchTickCallback(void) {
|
||||||
if (tick_lock && tick_callback) {
|
if (tick_lock && tick_callback) {
|
||||||
[tick_lock lock];
|
[tick_lock lock];
|
||||||
while(tick_callback_requested) {
|
while(tick_callback_requested) {
|
||||||
@ -1026,7 +1026,7 @@ void _glfwDispatchTickCallback() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
request_tick_callback() {
|
request_tick_callback(void) {
|
||||||
if (!tick_callback_requested) {
|
if (!tick_callback_requested) {
|
||||||
tick_callback_requested = true;
|
tick_callback_requested = true;
|
||||||
[NSApp performSelectorOnMainThread:@selector(tick_callback) withObject:nil waitUntilDone:NO];
|
[NSApp performSelectorOnMainThread:@selector(tick_callback) withObject:nil waitUntilDone:NO];
|
||||||
|
|||||||
@ -323,7 +323,7 @@ static double getFallbackRefreshRate(CGDirectDisplayID displayID)
|
|||||||
////// GLFW internal API //////
|
////// GLFW internal API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void _glfwClearDisplayLinks() {
|
void _glfwClearDisplayLinks(void) {
|
||||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||||
if (_glfw.ns.displayLinks.entries[i].displayLink) {
|
if (_glfw.ns.displayLinks.entries[i].displayLink) {
|
||||||
CVDisplayLinkStop(_glfw.ns.displayLinks.entries[i].displayLink);
|
CVDisplayLinkStop(_glfw.ns.displayLinks.entries[i].displayLink);
|
||||||
|
|||||||
19
go.mod
19
go.mod
@ -5,24 +5,25 @@ go 1.20
|
|||||||
require (
|
require (
|
||||||
github.com/ALTree/bigfloat v0.0.0-20220102081255-38c8b72a9924
|
github.com/ALTree/bigfloat v0.0.0-20220102081255-38c8b72a9924
|
||||||
github.com/alecthomas/chroma/v2 v2.7.0
|
github.com/alecthomas/chroma/v2 v2.7.0
|
||||||
github.com/bmatcuk/doublestar v1.3.4
|
github.com/bmatcuk/doublestar/v4 v4.6.0
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
|
github.com/dlclark/regexp2 v1.9.0
|
||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f
|
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f
|
||||||
github.com/seancfoley/ipaddress-go v1.5.3
|
github.com/seancfoley/ipaddress-go v1.5.4
|
||||||
github.com/shirou/gopsutil/v3 v3.23.1
|
github.com/shirou/gopsutil/v3 v3.23.3
|
||||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||||
golang.org/x/image v0.5.0
|
golang.org/x/image v0.7.0
|
||||||
golang.org/x/sys v0.4.0
|
golang.org/x/sys v0.7.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||||
github.com/seancfoley/bintree v1.2.1 // indirect
|
github.com/seancfoley/bintree v1.2.1 // indirect
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.5 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
|||||||
51
go.sum
51
go.sum
@ -4,15 +4,15 @@ github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2
|
|||||||
github.com/alecthomas/chroma/v2 v2.7.0 h1:hm1rY6c/Ob4eGclpQ7X/A3yhqBOZNUTk9q+yhyLIViI=
|
github.com/alecthomas/chroma/v2 v2.7.0 h1:hm1rY6c/Ob4eGclpQ7X/A3yhqBOZNUTk9q+yhyLIViI=
|
||||||
github.com/alecthomas/chroma/v2 v2.7.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw=
|
github.com/alecthomas/chroma/v2 v2.7.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw=
|
||||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||||
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
|
github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc=
|
||||||
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
github.com/dlclark/regexp2 v1.9.0 h1:pTK/l/3qYIKaRXuHnEnIf7Y5NxfRPfpb7dis6/gdlVI=
|
||||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.9.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
@ -23,25 +23,32 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
github.com/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f h1:Ko4+g6K16vSyUrtd/pPXuQnWsiHe5BYptEtTxfwYwCc=
|
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/jamesruan/go-rfc1924 v0.0.0-20170108144916-2767ca7c638f/go.mod h1:eHzfhOKbTGJEGPSdMHzU6jft192tHHt2Bu2vIZArvC0=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/seancfoley/bintree v1.2.1 h1:Z/iNjRKkXnn0CTW7jDQYtjW5fz2GH1yWvOTJ4MrMvdo=
|
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/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.4 h1:ZdjewWC1J2y5ruQjWHwK6rA1tInWB6mz1ftz6uTm+Uw=
|
||||||
github.com/seancfoley/ipaddress-go v1.5.3/go.mod h1:fpvVPC+Jso+YEhNcNiww8HQmBgKP8T4T6BTp1SLxxIo=
|
github.com/seancfoley/ipaddress-go v1.5.4/go.mod h1:fpvVPC+Jso+YEhNcNiww8HQmBgKP8T4T6BTp1SLxxIo=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
|
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA=
|
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||||
|
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
|
||||||
|
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||||
@ -51,17 +58,20 @@ github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPR
|
|||||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b h1:EqBVA+nNsObCwQoBEHy4wLU0pi7i8a4AL3pbItPdPkE=
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/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.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
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=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -70,17 +80,22 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||||
|
golang.org/x/sys v0.7.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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
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.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@ -60,7 +60,7 @@ func extra_for(width, screen_width int) int {
|
|||||||
return utils.Max(0, screen_width-width)/2 + 1
|
return utils.Max(0, screen_width-width)/2 + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func choices(o *Options) (response string, err error) {
|
func GetChoices(o *Options) (response string, err error) {
|
||||||
response = ""
|
response = ""
|
||||||
lp, err := loop.New()
|
lp, err := loop.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -33,7 +33,7 @@ func main(_ *cli.Command, o *Options, args []string) (rc int, err error) {
|
|||||||
}
|
}
|
||||||
switch o.Type {
|
switch o.Type {
|
||||||
case "yesno", "choices":
|
case "yesno", "choices":
|
||||||
result.Response, err = choices(o)
|
result.Response, err = GetChoices(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -343,7 +343,7 @@ func run_get_loop(opts *Options, args []string) (err error) {
|
|||||||
if reading_available_mimes {
|
if reading_available_mimes {
|
||||||
switch metadata["status"] {
|
switch metadata["status"] {
|
||||||
case "DATA":
|
case "DATA":
|
||||||
available_mimes = strings.Split(utils.UnsafeBytesToString(payload), " ")
|
available_mimes = utils.Map(strings.TrimSpace, strings.Split(utils.UnsafeBytesToString(payload), " "))
|
||||||
case "OK":
|
case "OK":
|
||||||
case "DONE":
|
case "DONE":
|
||||||
reading_available_mimes = false
|
reading_available_mimes = false
|
||||||
|
|||||||
@ -258,6 +258,10 @@ func resolve_remote_name(path, defval string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func walk(base string, patterns []string, names *utils.Set[string], pmap, path_name_map map[string]string) error {
|
func walk(base string, patterns []string, names *utils.Set[string], pmap, path_name_map map[string]string) error {
|
||||||
|
base, err := filepath.Abs(base)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return filepath.WalkDir(base, func(path string, d fs.DirEntry, err error) error {
|
return filepath.WalkDir(base, func(path string, d fs.DirEntry, err error) error {
|
||||||
is_allowed := allowed(path, patterns...)
|
is_allowed := allowed(path, patterns...)
|
||||||
if !is_allowed {
|
if !is_allowed {
|
||||||
|
|||||||
@ -90,7 +90,8 @@ for the operating system. Various special values are supported:
|
|||||||
copy the match to the specified buffer, e.g. :code:`@a`
|
copy the match to the specified buffer, e.g. :code:`@a`
|
||||||
|
|
||||||
:code:`default`
|
:code:`default`
|
||||||
run the default open program.
|
run the default open program. Note that when using the hyperlink :code:`--type`
|
||||||
|
the default is to use the kitty :doc:`hyperlink handling </open_actions>` facilities.
|
||||||
|
|
||||||
:code:`launch`
|
:code:`launch`
|
||||||
run :doc:`/launch` to open the program in a new kitty tab, window, overlay, etc.
|
run :doc:`/launch` to open the program in a new kitty tab, window, overlay, etc.
|
||||||
@ -349,8 +350,9 @@ def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int,
|
|||||||
else:
|
else:
|
||||||
from kitty.conf.utils import to_cmdline
|
from kitty.conf.utils import to_cmdline
|
||||||
cwd = data['cwd']
|
cwd = data['cwd']
|
||||||
program = get_options().open_url_with if program == 'default' else program
|
is_default_program = program == 'default'
|
||||||
if text_type == 'hyperlink':
|
program = get_options().open_url_with if is_default_program else program
|
||||||
|
if text_type == 'hyperlink' and is_default_program:
|
||||||
w = boss.window_id_map.get(target_window_id)
|
w = boss.window_id_map.get(target_window_id)
|
||||||
for m in matches:
|
for m in matches:
|
||||||
if w is not None:
|
if w is not None:
|
||||||
|
|||||||
@ -13,9 +13,12 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/dlclark/regexp2"
|
||||||
"github.com/seancfoley/ipaddress-go/ipaddr"
|
"github.com/seancfoley/ipaddress-go/ipaddr"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
@ -252,15 +255,6 @@ func functions_for(opts *Options) (pattern string, post_processors []PostProcess
|
|||||||
// IPv6 with no validation
|
// IPv6 with no validation
|
||||||
`(?:[a-fA-F0-9]{0,4}:){2,7}[a-fA-F0-9]{1,4})`)
|
`(?:[a-fA-F0-9]{0,4}:){2,7}[a-fA-F0-9]{1,4})`)
|
||||||
post_processors = append(post_processors, PostProcessorMap()["ip"])
|
post_processors = append(post_processors, PostProcessorMap()["ip"])
|
||||||
case "word":
|
|
||||||
chars := opts.WordCharacters
|
|
||||||
if chars == "" {
|
|
||||||
chars = RelevantKittyOpts().Select_by_word_characters
|
|
||||||
}
|
|
||||||
chars = regexp.QuoteMeta(chars)
|
|
||||||
chars = strings.ReplaceAll(chars, "-", "\\-")
|
|
||||||
pattern = fmt.Sprintf(`[%s\pL\pN]{%d,}`, chars, opts.MinimumMatchLength)
|
|
||||||
post_processors = append(post_processors, PostProcessorMap()["brackets"], PostProcessorMap()["quotes"])
|
|
||||||
default:
|
default:
|
||||||
pattern = opts.Regex
|
pattern = opts.Regex
|
||||||
if opts.Type == "linenum" {
|
if opts.Type == "linenum" {
|
||||||
@ -274,11 +268,112 @@ func functions_for(opts *Options) (pattern string, post_processors []PostProcess
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func mark(r *regexp.Regexp, post_processors []PostProcessorFunc, group_processors []GroupProcessorFunc, text string, opts *Options) (ans []Mark) {
|
type Capture struct {
|
||||||
|
Text string
|
||||||
|
Text_as_runes []rune
|
||||||
|
Byte_Offsets struct {
|
||||||
|
Start, End int
|
||||||
|
}
|
||||||
|
Rune_Offsets struct {
|
||||||
|
Start, End int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Capture) String() string {
|
||||||
|
return fmt.Sprintf("Capture(start=%d, end=%d, %#v)", self.Byte_Offsets.Start, self.Byte_Offsets.End, self.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Group struct {
|
||||||
|
Name string
|
||||||
|
IsNamed bool
|
||||||
|
Captures []Capture
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Group) LastCapture() Capture {
|
||||||
|
if len(self.Captures) == 0 {
|
||||||
|
return Capture{}
|
||||||
|
}
|
||||||
|
return self.Captures[len(self.Captures)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Group) String() string {
|
||||||
|
return fmt.Sprintf("Group(name=%#v, captures=%v)", self.Name, self.Captures)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Match struct {
|
||||||
|
Groups []Group
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Match) HasNamedGroups() bool {
|
||||||
|
for _, g := range self.Groups {
|
||||||
|
if g.IsNamed {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func find_all_matches(re *regexp2.Regexp, text string) (ans []Match, err error) {
|
||||||
|
m, err := re.FindStringMatch(text)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rune_to_bytes := utils.RuneOffsetsToByteOffsets(text)
|
||||||
|
get_byte_offset_map := func(groups []regexp2.Group) (ans map[int]int, err error) {
|
||||||
|
ans = make(map[int]int, len(groups)*2)
|
||||||
|
rune_offsets := make([]int, 0, len(groups)*2)
|
||||||
|
for _, g := range groups {
|
||||||
|
for _, c := range g.Captures {
|
||||||
|
if _, found := ans[c.Index]; !found {
|
||||||
|
rune_offsets = append(rune_offsets, c.Index)
|
||||||
|
ans[c.Index] = -1
|
||||||
|
}
|
||||||
|
end := c.Index + c.Length
|
||||||
|
if _, found := ans[end]; !found {
|
||||||
|
rune_offsets = append(rune_offsets, end)
|
||||||
|
ans[end] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slices.Sort(rune_offsets)
|
||||||
|
for _, pos := range rune_offsets {
|
||||||
|
if ans[pos] = rune_to_bytes(pos); ans[pos] < 0 {
|
||||||
|
return nil, fmt.Errorf("Matches are not monotonic cannot map rune offsets to byte offsets")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for m != nil {
|
||||||
|
groups := m.Groups()
|
||||||
|
bom, err := get_byte_offset_map(groups)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
match := Match{Groups: make([]Group, len(groups))}
|
||||||
|
for i, g := range m.Groups() {
|
||||||
|
match.Groups[i].Name = g.Name
|
||||||
|
match.Groups[i].IsNamed = g.Name != "" && g.Name != strconv.Itoa(i)
|
||||||
|
for _, c := range g.Captures {
|
||||||
|
cn := Capture{Text: c.String(), Text_as_runes: c.Runes()}
|
||||||
|
cn.Rune_Offsets.End = c.Index + c.Length
|
||||||
|
cn.Rune_Offsets.Start = c.Index
|
||||||
|
cn.Byte_Offsets.Start, cn.Byte_Offsets.End = bom[c.Index], bom[cn.Rune_Offsets.End]
|
||||||
|
match.Groups[i].Captures = append(match.Groups[i].Captures, cn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ans = append(ans, match)
|
||||||
|
m, _ = re.FindNextMatch(m)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mark(r *regexp2.Regexp, post_processors []PostProcessorFunc, group_processors []GroupProcessorFunc, text string, opts *Options) (ans []Mark) {
|
||||||
sanitize_pat := regexp.MustCompile("[\r\n\x00]")
|
sanitize_pat := regexp.MustCompile("[\r\n\x00]")
|
||||||
names := r.SubexpNames()
|
all_matches, _ := find_all_matches(r, text)
|
||||||
for i, v := range r.FindAllStringSubmatchIndex(text, -1) {
|
for i, m := range all_matches {
|
||||||
match_start, match_end := v[0], v[1]
|
full_capture := m.Groups[0].LastCapture()
|
||||||
|
match_start, match_end := full_capture.Byte_Offsets.Start, full_capture.Byte_Offsets.End
|
||||||
for match_end > match_start+1 && text[match_end-1] == 0 {
|
for match_end > match_start+1 && text[match_end-1] == 0 {
|
||||||
match_end--
|
match_end--
|
||||||
}
|
}
|
||||||
@ -296,14 +391,14 @@ func mark(r *regexp.Regexp, post_processors []PostProcessorFunc, group_processor
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
full_match = sanitize_pat.ReplaceAllLiteralString(text[match_start:match_end], "")
|
full_match = sanitize_pat.ReplaceAllLiteralString(text[match_start:match_end], "")
|
||||||
gd := make(map[string]string, len(names))
|
gd := make(map[string]string, len(m.Groups))
|
||||||
for x, name := range names {
|
for idx, g := range m.Groups {
|
||||||
if name != "" {
|
if idx > 0 && g.IsNamed {
|
||||||
idx := 2 * x
|
c := g.LastCapture()
|
||||||
if s, e := v[idx], v[idx+1]; s > -1 && e > -1 {
|
if s, e := c.Byte_Offsets.Start, c.Byte_Offsets.End; s > -1 && e > -1 {
|
||||||
s = utils.Max(s, match_start)
|
s = utils.Max(s, match_start)
|
||||||
e = utils.Min(e, match_end)
|
e = utils.Min(e, match_end)
|
||||||
gd[name] = sanitize_pat.ReplaceAllLiteralString(text[s:e], "")
|
gd[g.Name] = sanitize_pat.ReplaceAllLiteralString(text[s:e], "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,15 +409,89 @@ func mark(r *regexp.Regexp, post_processors []PostProcessorFunc, group_processor
|
|||||||
for k, v := range gd {
|
for k, v := range gd {
|
||||||
gd2[k] = v
|
gd2[k] = v
|
||||||
}
|
}
|
||||||
|
if opts.Type == "regex" && len(m.Groups) > 1 && !m.HasNamedGroups() {
|
||||||
|
cp := m.Groups[1].LastCapture()
|
||||||
|
ms, me := cp.Byte_Offsets.Start, cp.Byte_Offsets.End
|
||||||
|
match_start = utils.Max(match_start, ms)
|
||||||
|
match_end = utils.Min(match_end, me)
|
||||||
|
full_match = sanitize_pat.ReplaceAllLiteralString(text[match_start:match_end], "")
|
||||||
|
}
|
||||||
|
if full_match != "" {
|
||||||
ans = append(ans, Mark{
|
ans = append(ans, Mark{
|
||||||
Index: i, Start: match_start, End: match_end, Text: full_match, Groupdict: gd2,
|
Index: i, Start: match_start, End: match_end, Text: full_match, Groupdict: gd2,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrNoMatches struct{ Type string }
|
type ErrNoMatches struct{ Type string }
|
||||||
|
|
||||||
|
func is_word_char(ch rune, current_chars []rune) bool {
|
||||||
|
return unicode.IsLetter(ch) || unicode.IsNumber(ch) || (unicode.IsMark(ch) && len(current_chars) > 0 && unicode.IsLetter(current_chars[len(current_chars)-1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func mark_words(text string, opts *Options) (ans []Mark) {
|
||||||
|
left := text
|
||||||
|
var current_run struct {
|
||||||
|
chars []rune
|
||||||
|
start, size int
|
||||||
|
}
|
||||||
|
chars := opts.WordCharacters
|
||||||
|
if chars == "" {
|
||||||
|
chars = RelevantKittyOpts().Select_by_word_characters
|
||||||
|
}
|
||||||
|
allowed_chars := make(map[rune]bool, len(chars))
|
||||||
|
for _, ch := range chars {
|
||||||
|
allowed_chars[ch] = true
|
||||||
|
}
|
||||||
|
pos := 0
|
||||||
|
post_processors := []PostProcessorFunc{PostProcessorMap()["brackets"], PostProcessorMap()["quotes"]}
|
||||||
|
|
||||||
|
commit_run := func() {
|
||||||
|
if len(current_run.chars) >= opts.MinimumMatchLength {
|
||||||
|
match_start, match_end := current_run.start, current_run.start+current_run.size
|
||||||
|
for _, f := range post_processors {
|
||||||
|
match_start, match_end = f(text, match_start, match_end)
|
||||||
|
if match_start < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match_start > -1 && match_end > match_start {
|
||||||
|
full_match := text[match_start:match_end]
|
||||||
|
if len([]rune(full_match)) >= opts.MinimumMatchLength {
|
||||||
|
ans = append(ans, Mark{
|
||||||
|
Index: len(ans), Start: match_start, End: match_end, Text: full_match,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_run.chars = nil
|
||||||
|
current_run.start = 0
|
||||||
|
current_run.size = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
ch, size := utf8.DecodeRuneInString(left)
|
||||||
|
if ch == utf8.RuneError {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if allowed_chars[ch] || is_word_char(ch, current_run.chars) {
|
||||||
|
if len(current_run.chars) == 0 {
|
||||||
|
current_run.start = pos
|
||||||
|
}
|
||||||
|
current_run.chars = append(current_run.chars, ch)
|
||||||
|
current_run.size += size
|
||||||
|
} else {
|
||||||
|
commit_run()
|
||||||
|
}
|
||||||
|
left = left[size:]
|
||||||
|
pos += size
|
||||||
|
}
|
||||||
|
commit_run()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func adjust_python_offsets(text string, marks []Mark) error {
|
func adjust_python_offsets(text string, marks []Mark) error {
|
||||||
// python returns rune based offsets (unicode chars not utf-8 bytes)
|
// python returns rune based offsets (unicode chars not utf-8 bytes)
|
||||||
adjust := utils.RuneOffsetsToByteOffsets(text)
|
adjust := utils.RuneOffsetsToByteOffsets(text)
|
||||||
@ -356,7 +525,7 @@ func find_marks(text string, opts *Options, cli_args ...string) (sanitized_text
|
|||||||
|
|
||||||
run_basic_matching := func() error {
|
run_basic_matching := func() error {
|
||||||
pattern, post_processors, group_processors := functions_for(opts)
|
pattern, post_processors, group_processors := functions_for(opts)
|
||||||
r, err := regexp.Compile(pattern)
|
r, err := regexp2.Compile(pattern, regexp2.RE2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to compile the regex pattern: %#v with error: %w", pattern, err)
|
return fmt.Errorf("Failed to compile the regex pattern: %#v with error: %w", pattern, err)
|
||||||
}
|
}
|
||||||
@ -393,6 +562,8 @@ func find_marks(text string, opts *Options, cli_args ...string) (sanitized_text
|
|||||||
}
|
}
|
||||||
} else if opts.Type == "hyperlink" {
|
} else if opts.Type == "hyperlink" {
|
||||||
ans = hyperlinks
|
ans = hyperlinks
|
||||||
|
} else if opts.Type == "word" {
|
||||||
|
ans = mark_words(text, opts)
|
||||||
} else {
|
} else {
|
||||||
err = run_basic_matching()
|
err = run_basic_matching()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ func TestHintMarking(t *testing.T) {
|
|||||||
for _, m := range marks {
|
for _, m := range marks {
|
||||||
q := strings.NewReplacer("\n", "", "\r", "", "\x00", "").Replace(ptext[m.Start:m.End])
|
q := strings.NewReplacer("\n", "", "\r", "", "\x00", "").Replace(ptext[m.Start:m.End])
|
||||||
if diff := cmp.Diff(m.Text, q); diff != "" {
|
if diff := cmp.Diff(m.Text, q); diff != "" {
|
||||||
t.Fatalf("Mark start and end dont point to correct offset in text for %#v\n%s", text, diff)
|
t.Fatalf("Mark start (%d) and end (%d) dont point to correct offset in text for %#v\n%s", m.Start, m.End, text, diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -107,6 +107,18 @@ func TestHintMarking(t *testing.T) {
|
|||||||
r(`255.255.255.256`)
|
r(`255.255.255.256`)
|
||||||
r(`:1`)
|
r(`:1`)
|
||||||
|
|
||||||
|
reset()
|
||||||
|
opts.Type = "regex"
|
||||||
|
opts.Regex = `(?ms)^[*]?\s(\S+)`
|
||||||
|
r(`* 2b687c2 - test1`, `2b687c2`)
|
||||||
|
opts.Regex = `(?<=got: )sha256.{4}`
|
||||||
|
r(`got: sha256-L8=`, `sha256-L8=`)
|
||||||
|
|
||||||
|
reset()
|
||||||
|
opts.Type = "word"
|
||||||
|
r(`#one (two) 😍 a-1b `, `#one`, `two`, `a-1b`)
|
||||||
|
r("fōtiz час a\u0310b ", `fōtiz`, `час`, "a\u0310b")
|
||||||
|
|
||||||
reset()
|
reset()
|
||||||
tdir := t.TempDir()
|
tdir := t.TempDir()
|
||||||
simple := filepath.Join(tdir, "simple.py")
|
simple := filepath.Join(tdir, "simple.py")
|
||||||
|
|||||||
@ -4,11 +4,33 @@
|
|||||||
import sys
|
import sys
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from ..show_key.kitty_mode import format_mods
|
from kitty.key_encoding import ALT, CAPS_LOCK, CTRL, HYPER, META, NUM_LOCK, SHIFT, SUPER
|
||||||
|
|
||||||
from ..tui.handler import Handler
|
from ..tui.handler import Handler
|
||||||
from ..tui.loop import Loop, MouseEvent
|
from ..tui.loop import Loop, MouseEvent
|
||||||
from ..tui.operations import MouseTracking
|
from ..tui.operations import MouseTracking
|
||||||
|
|
||||||
|
mod_names = {
|
||||||
|
SHIFT: 'Shift',
|
||||||
|
ALT: 'Alt',
|
||||||
|
CTRL: 'Ctrl',
|
||||||
|
SUPER: 'Super',
|
||||||
|
HYPER: 'Hyper',
|
||||||
|
META: 'Meta',
|
||||||
|
NUM_LOCK: 'NumLock',
|
||||||
|
CAPS_LOCK: 'CapsLock',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def format_mods(mods: int) -> str:
|
||||||
|
if not mods:
|
||||||
|
return ''
|
||||||
|
lmods = []
|
||||||
|
for m, name in mod_names.items():
|
||||||
|
if mods & m:
|
||||||
|
lmods.append(name)
|
||||||
|
return '+'.join(lmods)
|
||||||
|
|
||||||
|
|
||||||
class Mouse(Handler):
|
class Mouse(Handler):
|
||||||
mouse_tracking = MouseTracking.full
|
mouse_tracking = MouseTracking.full
|
||||||
|
|||||||
78
kittens/show_key/kitty.go
Normal file
78
kittens/show_key/kitty.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package show_key
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"kitty/tools/cli/markup"
|
||||||
|
"kitty/tools/tui/loop"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func csi(csi string) string {
|
||||||
|
return "CSI " + strings.NewReplacer(":", " : ", ";", " ; ").Replace(csi[:len(csi)-1]) + " " + csi[len(csi)-1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func run_kitty_loop(opts *Options) (err error) {
|
||||||
|
lp, err := loop.New(loop.FullKeyboardProtocol)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx := markup.New(true)
|
||||||
|
|
||||||
|
lp.OnInitialize = func() (string, error) {
|
||||||
|
lp.SetCursorVisible(false)
|
||||||
|
lp.SetWindowTitle("kitty extended keyboard protocol demo")
|
||||||
|
lp.Println("Press any keys - Ctrl+C or Ctrl+D will terminate")
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lp.OnKeyEvent = func(e *loop.KeyEvent) (err error) {
|
||||||
|
e.Handled = true
|
||||||
|
if e.MatchesPressOrRepeat("ctrl+c") || e.MatchesPressOrRepeat("ctrl+d") {
|
||||||
|
lp.Quit(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mods := e.Mods.String()
|
||||||
|
if mods != "" {
|
||||||
|
mods += "+"
|
||||||
|
}
|
||||||
|
etype := e.Type.String()
|
||||||
|
key := e.Key
|
||||||
|
if key == " " {
|
||||||
|
key = "space"
|
||||||
|
}
|
||||||
|
key = mods + key
|
||||||
|
lp.Printf("%s %s %s\r\n", ctx.Green(key), ctx.Yellow(etype), e.Text)
|
||||||
|
lp.Println(ctx.Cyan(csi(e.CSI)))
|
||||||
|
if e.AlternateKey != "" || e.ShiftedKey != "" {
|
||||||
|
if e.ShiftedKey != "" {
|
||||||
|
lp.QueueWriteString(ctx.Dim("Shifted key: "))
|
||||||
|
lp.QueueWriteString(e.ShiftedKey + " ")
|
||||||
|
}
|
||||||
|
if e.AlternateKey != "" {
|
||||||
|
lp.QueueWriteString(ctx.Dim("Alternate key: "))
|
||||||
|
lp.QueueWriteString(e.AlternateKey + " ")
|
||||||
|
}
|
||||||
|
lp.Println()
|
||||||
|
}
|
||||||
|
lp.Println()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = lp.Run()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ds := lp.DeathSignalName()
|
||||||
|
if ds != "" {
|
||||||
|
fmt.Println("Killed by signal: ", ds)
|
||||||
|
lp.KillIfSignalled()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -1,79 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
|
||||||
|
|
||||||
from kittens.tui.handler import Handler
|
|
||||||
from kittens.tui.loop import Loop
|
|
||||||
from kitty.key_encoding import ALT, CAPS_LOCK, CTRL, HYPER, META, NUM_LOCK, PRESS, RELEASE, REPEAT, SHIFT, SUPER, KeyEvent, encode_key_event
|
|
||||||
|
|
||||||
mod_names = {
|
|
||||||
SHIFT: 'Shift',
|
|
||||||
ALT: 'Alt',
|
|
||||||
CTRL: 'Ctrl',
|
|
||||||
SUPER: 'Super',
|
|
||||||
HYPER: 'Hyper',
|
|
||||||
META: 'Meta',
|
|
||||||
NUM_LOCK: 'NumLock',
|
|
||||||
CAPS_LOCK: 'CapsLock',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def format_mods(mods: int) -> str:
|
|
||||||
if not mods:
|
|
||||||
return ''
|
|
||||||
lmods = []
|
|
||||||
for m, name in mod_names.items():
|
|
||||||
if mods & m:
|
|
||||||
lmods.append(name)
|
|
||||||
return '+'.join(lmods)
|
|
||||||
|
|
||||||
|
|
||||||
class KeysHandler(Handler):
|
|
||||||
|
|
||||||
def initialize(self) -> None:
|
|
||||||
self.cmd.set_window_title('kitty extended keyboard protocol demo')
|
|
||||||
self.cmd.set_cursor_visible(False)
|
|
||||||
self.print('Press any keys - Ctrl+C or Ctrl+D will terminate')
|
|
||||||
|
|
||||||
def on_key_event(self, key_event: KeyEvent, in_bracketed_paste: bool = False) -> None:
|
|
||||||
etype = {
|
|
||||||
PRESS: 'PRESS',
|
|
||||||
REPEAT: 'REPEAT',
|
|
||||||
RELEASE: 'RELEASE'
|
|
||||||
}[key_event.type]
|
|
||||||
mods = format_mods(key_event.mods)
|
|
||||||
if mods:
|
|
||||||
mods += '+'
|
|
||||||
kk = key_event.key
|
|
||||||
if kk == ' ':
|
|
||||||
kk = 'SPACE'
|
|
||||||
key = f'{mods}{kk} '
|
|
||||||
self.cmd.colored(key, 'green')
|
|
||||||
self.cmd.colored(etype + ' ', 'yellow')
|
|
||||||
self.cmd.styled(key_event.text, italic=True)
|
|
||||||
self.print()
|
|
||||||
rep = f'CSI {encode_key_event(key_event)[2:]}'
|
|
||||||
rep = rep.replace(';', ' ; ').replace(':', ' : ')[:-1] + ' ' + rep[-1]
|
|
||||||
self.cmd.styled(rep, fg='magenta')
|
|
||||||
if (key_event.shifted_key or key_event.alternate_key):
|
|
||||||
self.print()
|
|
||||||
if key_event.shifted_key:
|
|
||||||
self.cmd.colored('Shifted key: ', 'gray')
|
|
||||||
self.print(key_event.shifted_key + ' ', end='')
|
|
||||||
if key_event.alternate_key:
|
|
||||||
self.cmd.colored('Alternate key: ', 'gray')
|
|
||||||
self.print(key_event.alternate_key + ' ', end='')
|
|
||||||
self.print()
|
|
||||||
self.print()
|
|
||||||
|
|
||||||
def on_interrupt(self) -> None:
|
|
||||||
self.quit_loop(0)
|
|
||||||
|
|
||||||
def on_eot(self) -> None:
|
|
||||||
self.quit_loop(0)
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
loop = Loop()
|
|
||||||
handler = KeysHandler()
|
|
||||||
loop.loop(handler)
|
|
||||||
raise SystemExit(loop.return_code)
|
|
||||||
76
kittens/show_key/legacy.go
Normal file
76
kittens/show_key/legacy.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package show_key
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"kitty/tools/cli/markup"
|
||||||
|
"kitty/tools/tty"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func print_key(buf []byte, ctx *markup.Context) {
|
||||||
|
const ctrl_keys = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
|
||||||
|
unix := ""
|
||||||
|
send_text := ""
|
||||||
|
for _, ch := range buf {
|
||||||
|
switch {
|
||||||
|
case int(ch) < len(ctrl_keys):
|
||||||
|
unix += "^" + ctrl_keys[ch:ch+1]
|
||||||
|
case ch == 127:
|
||||||
|
unix += "^?"
|
||||||
|
default:
|
||||||
|
unix += string(rune(ch))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ch := range string(buf) {
|
||||||
|
q := fmt.Sprintf("%#v", string(ch))
|
||||||
|
send_text += q[1 : len(q)-1]
|
||||||
|
}
|
||||||
|
os.Stdout.WriteString(unix + "\t\t")
|
||||||
|
os.Stdout.WriteString(ctx.Yellow(send_text) + "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func run_legacy_loop(opts *Options) (err error) {
|
||||||
|
term, err := tty.OpenControllingTerm(tty.SetRaw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
term.RestoreAndClose()
|
||||||
|
}()
|
||||||
|
if opts.KeyMode != "unchanged" {
|
||||||
|
os.Stdout.WriteString("\x1b[?1")
|
||||||
|
switch opts.KeyMode {
|
||||||
|
case "normal":
|
||||||
|
os.Stdout.WriteString("l")
|
||||||
|
default:
|
||||||
|
os.Stdout.WriteString("h")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
os.Stdout.WriteString("\x1b[?1l")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
fmt.Print("Press any keys - Ctrl+D will terminate this program\r\n")
|
||||||
|
ctx := markup.New(true)
|
||||||
|
fmt.Print(ctx.Green("UNIX\t\tsend_text\r\n"))
|
||||||
|
buf := make([]byte, 64)
|
||||||
|
for {
|
||||||
|
n, err := term.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print_key(buf[:n], ctx)
|
||||||
|
if n == 1 && buf[0] == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
27
kittens/show_key/main.go
Normal file
27
kittens/show_key/main.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package show_key
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"kitty/tools/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {
|
||||||
|
if opts.KeyMode == "kitty" {
|
||||||
|
err = run_kitty_loop(opts)
|
||||||
|
} else {
|
||||||
|
err = run_legacy_loop(opts)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
rc = 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func EntryPoint(parent *cli.Command) {
|
||||||
|
create_cmd(parent, main)
|
||||||
|
}
|
||||||
@ -2,56 +2,9 @@
|
|||||||
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from kittens.tui.operations import raw_mode, styled
|
|
||||||
from kitty.cli import parse_args
|
|
||||||
from kitty.cli_stub import ShowKeyCLIOptions
|
|
||||||
|
|
||||||
ctrl_keys = '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'
|
|
||||||
|
|
||||||
|
|
||||||
def print_key(raw: bytearray) -> None:
|
|
||||||
unix = ''
|
|
||||||
for ch in raw:
|
|
||||||
if ch < len(ctrl_keys):
|
|
||||||
unix += f'^{ctrl_keys[ch]}'
|
|
||||||
elif ch == 127:
|
|
||||||
unix += '^?'
|
|
||||||
else:
|
|
||||||
unix += chr(ch)
|
|
||||||
print(unix + '\t\t', end='')
|
|
||||||
for ch in raw:
|
|
||||||
x = chr(ch).encode('utf-8')
|
|
||||||
print(styled(repr(x)[2:-1], fg='yellow'), end='')
|
|
||||||
print(end='\r\n', flush=True)
|
|
||||||
|
|
||||||
|
|
||||||
def read_keys() -> None:
|
|
||||||
fd = sys.stdin.fileno()
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
raw = bytearray(os.read(fd, 64))
|
|
||||||
except OSError as err:
|
|
||||||
print(err, file=sys.stderr, flush=True)
|
|
||||||
break
|
|
||||||
if not raw:
|
|
||||||
break
|
|
||||||
print_key(raw)
|
|
||||||
if len(raw) == 1 and raw[0] == 4:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
def legacy_main() -> None:
|
|
||||||
print('Press any keys - Ctrl+D will terminate this program', end='\r\n', flush=True)
|
|
||||||
print(styled('UNIX', italic=True, fg='green'), styled('send_text', italic=True, fg='green'), sep='\t\t', end='\r\n')
|
|
||||||
|
|
||||||
with raw_mode():
|
|
||||||
read_keys()
|
|
||||||
|
|
||||||
|
|
||||||
OPTIONS = r'''
|
OPTIONS = r'''
|
||||||
--key-mode -m
|
--key-mode -m
|
||||||
default=normal
|
default=normal
|
||||||
@ -66,17 +19,7 @@ usage = ''
|
|||||||
|
|
||||||
|
|
||||||
def main(args: List[str]) -> None:
|
def main(args: List[str]) -> None:
|
||||||
cli_opts, items = parse_args(args[1:], OPTIONS, '', help_text, 'kitty +kitten show_key', result_class=ShowKeyCLIOptions)
|
raise SystemExit('This should be reun as kitten show_key')
|
||||||
if cli_opts.key_mode == 'kitty':
|
|
||||||
from .kitty_mode import main as kitty_main
|
|
||||||
return kitty_main()
|
|
||||||
if cli_opts.key_mode != 'unchanged':
|
|
||||||
print(end='\x1b[?1' + ('l' if cli_opts.key_mode == 'normal' else 'h'), flush=True)
|
|
||||||
try:
|
|
||||||
return legacy_main()
|
|
||||||
finally:
|
|
||||||
if cli_opts.key_mode != 'unchanged':
|
|
||||||
print(end='\x1b[?1l', flush=True)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@ -86,3 +29,4 @@ elif __name__ == '__doc__':
|
|||||||
cd['usage'] = usage
|
cd['usage'] = usage
|
||||||
cd['options'] = OPTIONS
|
cd['options'] = OPTIONS
|
||||||
cd['help_text'] = help_text
|
cd['help_text'] = help_text
|
||||||
|
cd['short_desc'] = help_text
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import (
|
|||||||
"kitty/tools/utils/paths"
|
"kitty/tools/utils/paths"
|
||||||
"kitty/tools/utils/shlex"
|
"kitty/tools/utils/shlex"
|
||||||
|
|
||||||
"github.com/bmatcuk/doublestar"
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ func resolve_file_spec(spec string, is_glob bool) ([]string, error) {
|
|||||||
ans = paths_ctx.AbspathFromHome(ans)
|
ans = paths_ctx.AbspathFromHome(ans)
|
||||||
}
|
}
|
||||||
if is_glob {
|
if is_glob {
|
||||||
files, err := doublestar.Glob(ans)
|
files, err := doublestar.FilepathGlob(ans)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s is not a valid glob pattern with error: %w", spec, err)
|
return nil, fmt.Errorf("%s is not a valid glob pattern with error: %w", spec, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,6 +78,9 @@ func TestSSHConfigParsing(t *testing.T) {
|
|||||||
rt(`unset ["a"]`)
|
rt(`unset ["a"]`)
|
||||||
conf = "env LOCAL_ENV=_kitty_copy_env_var_"
|
conf = "env LOCAL_ENV=_kitty_copy_env_var_"
|
||||||
rt(`export ["LOCAL_ENV","LOCAL_VAL",false]`)
|
rt(`export ["LOCAL_ENV","LOCAL_VAL",false]`)
|
||||||
|
conf = "env a=b\nhostname 2\ncolor_scheme xyz"
|
||||||
|
hostname = "2"
|
||||||
|
rt()
|
||||||
|
|
||||||
ci, err := ParseCopyInstruction("--exclude moose --dest=target " + cf)
|
ci, err := ParseCopyInstruction("--exclude moose --dest=target " + cf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -669,6 +670,9 @@ func run_ssh(ssh_args, server_args, found_extra_args []string) (rc int, err erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
|
sigs := make(chan os.Signal, 8)
|
||||||
|
signal.Notify(sigs, unix.SIGINT, unix.SIGTERM)
|
||||||
|
|
||||||
if !cd.request_data {
|
if !cd.request_data {
|
||||||
rq := fmt.Sprintf("id=%s:pwfile=%s:pw=%s", cd.replacements["REQUEST_ID"], cd.replacements["PASSWORD_FILENAME"], cd.replacements["DATA_PASSWORD"])
|
rq := fmt.Sprintf("id=%s:pwfile=%s:pw=%s", cd.replacements["REQUEST_ID"], cd.replacements["PASSWORD_FILENAME"], cd.replacements["DATA_PASSWORD"])
|
||||||
err := term.ApplyOperations(tty.TCSANOW, tty.SetNoEcho)
|
err := term.ApplyOperations(tty.TCSANOW, tty.SetNoEcho)
|
||||||
@ -685,11 +689,22 @@ func run_ssh(ssh_args, server_args, found_extra_args []string) (rc int, err erro
|
|||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
go func() {
|
||||||
|
_ = <-sigs
|
||||||
|
// ignore any interrupt and terminate signals as they will usually be sent to the ssh child process as well
|
||||||
|
// and we are waiting on that.
|
||||||
|
}()
|
||||||
err = c.Wait()
|
err = c.Wait()
|
||||||
drain_potential_tty_garbage(term)
|
drain_potential_tty_garbage(term)
|
||||||
|
signal.Reset(unix.SIGINT, unix.SIGTERM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var exit_err *exec.ExitError
|
var exit_err *exec.ExitError
|
||||||
if errors.As(err, &exit_err) {
|
if errors.As(err, &exit_err) {
|
||||||
|
if state := exit_err.ProcessState.String(); state == "signal: interrupt" {
|
||||||
|
unix.Kill(os.Getpid(), unix.SIGINT)
|
||||||
|
// Give the signal time to be delivered
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
}
|
||||||
return exit_err.ExitCode(), nil
|
return exit_err.ExitCode(), nil
|
||||||
}
|
}
|
||||||
return 1, err
|
return 1, err
|
||||||
|
|||||||
@ -20,7 +20,7 @@ def option_text() -> str:
|
|||||||
--glob
|
--glob
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Interpret file arguments as glob patterns. Globbing is based on
|
Interpret file arguments as glob patterns. Globbing is based on
|
||||||
Based on standard wildcards with the addition that ``/**/`` matches any number of directories.
|
standard wildcards with the addition that ``/**/`` matches any number of directories.
|
||||||
See the :link:`detailed syntax <https://github.com/bmatcuk/doublestar#patterns>`.
|
See the :link:`detailed syntax <https://github.com/bmatcuk/doublestar#patterns>`.
|
||||||
|
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ variables and ~ are not expanded.
|
|||||||
--exclude
|
--exclude
|
||||||
type=list
|
type=list
|
||||||
A glob pattern. Files with names matching this pattern are excluded from being
|
A glob pattern. Files with names matching this pattern are excluded from being
|
||||||
transferred. Useful when adding directories. Can
|
transferred. Only used when copying directories. Can
|
||||||
be specified multiple times, if any of the patterns match the file will be
|
be specified multiple times, if any of the patterns match the file will be
|
||||||
excluded. If the pattern includes a :code:`/` then it will match against the full
|
excluded. If the pattern includes a :code:`/` then it will match against the full
|
||||||
path, not just the filename. In such patterns you can use :code:`/**/` to match zero
|
path, not just the filename. In such patterns you can use :code:`/**/` to match zero
|
||||||
@ -77,7 +77,11 @@ by spaces. The hostname can include an optional username in the form
|
|||||||
first hostname specification is found. Note that matching of hostname is done
|
first hostname specification is found. Note that matching of hostname is done
|
||||||
against the name you specify on the command line to connect to the remote host.
|
against the name you specify on the command line to connect to the remote host.
|
||||||
If you wish to include the same basic configuration for many different hosts,
|
If you wish to include the same basic configuration for many different hosts,
|
||||||
you can do so with the :ref:`include <include>` directive.
|
you can do so with the :ref:`include <include>` directive. In version 0.28.0
|
||||||
|
the behavior of this option was changed slightly, now, when a hostname is encountered
|
||||||
|
all its config values are set to defaults instead of being inherited from a previous
|
||||||
|
matching hostname block. In particular it means hostnames dont inherit configurations,
|
||||||
|
thereby avoiding hard to understand action-at-a-distance.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
opt('interpreter', 'sh', long_text='''
|
opt('interpreter', 'sh', long_text='''
|
||||||
|
|||||||
@ -3,7 +3,11 @@
|
|||||||
package themes
|
package themes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -96,3 +100,48 @@ func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
|
|||||||
func EntryPoint(parent *cli.Command) {
|
func EntryPoint(parent *cli.Command) {
|
||||||
create_cmd(parent, main)
|
create_cmd(parent, main)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parse_theme_metadata() error {
|
||||||
|
raw, err := io.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
paths := utils.Splitlines(utils.UnsafeBytesToString(raw))
|
||||||
|
ans := make([]*themes.ThemeMetadata, 0, len(paths))
|
||||||
|
for _, path := range paths {
|
||||||
|
if path != "" {
|
||||||
|
metadata, _, err := themes.ParseThemeMetadata(path)
|
||||||
|
if metadata.Name == "" {
|
||||||
|
metadata.Name = themes.ThemeNameFromFileName(filepath.Base(path))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ans = append(ans, metadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raw, err = json.Marshal(ans)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = os.Stdout.Write(raw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseEntryPoint(parent *cli.Command) {
|
||||||
|
parent.AddSubCommand(&cli.Command{
|
||||||
|
Name: "__parse_theme_metadata__",
|
||||||
|
Hidden: true,
|
||||||
|
Run: func(cmd *cli.Command, args []string) (rc int, err error) {
|
||||||
|
err = parse_theme_metadata()
|
||||||
|
if err != nil {
|
||||||
|
rc = 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -92,6 +92,7 @@ func serialize_favorites(favs []rune) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var loaded_favorites []rune
|
var loaded_favorites []rune
|
||||||
|
var favorites_loaded_from_user_config bool
|
||||||
|
|
||||||
func favorites_path() string {
|
func favorites_path() string {
|
||||||
return filepath.Join(utils.ConfigDir(), "unicode-input-favorites.conf")
|
return filepath.Join(utils.ConfigDir(), "unicode-input-favorites.conf")
|
||||||
@ -102,8 +103,10 @@ func load_favorites(refresh bool) []rune {
|
|||||||
raw, err := os.ReadFile(favorites_path())
|
raw, err := os.ReadFile(favorites_path())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
loaded_favorites = parse_favorites(utils.UnsafeBytesToString(raw))
|
loaded_favorites = parse_favorites(utils.UnsafeBytesToString(raw))
|
||||||
|
favorites_loaded_from_user_config = true
|
||||||
} else {
|
} else {
|
||||||
loaded_favorites = DEFAULT_SET
|
loaded_favorites = DEFAULT_SET
|
||||||
|
favorites_loaded_from_user_config = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loaded_favorites
|
return loaded_favorites
|
||||||
@ -427,8 +430,9 @@ func (self *handler) handle_favorites_key_event(event *loop.KeyEvent) {
|
|||||||
self.lp.Quit(1)
|
self.lp.Quit(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
raw := serialize_favorites(load_favorites(false))
|
|
||||||
fp := favorites_path()
|
fp := favorites_path()
|
||||||
|
if len(load_favorites(false)) == 0 || !favorites_loaded_from_user_config {
|
||||||
|
raw := serialize_favorites(load_favorites(false))
|
||||||
err = os.MkdirAll(filepath.Dir(fp), 0o755)
|
err = os.MkdirAll(filepath.Dir(fp), 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.err = fmt.Errorf("Failed to create config directory to store favorites in: %w", err)
|
self.err = fmt.Errorf("Failed to create config directory to store favorites in: %w", err)
|
||||||
@ -441,13 +445,8 @@ func (self *handler) handle_favorites_key_event(event *loop.KeyEvent) {
|
|||||||
self.lp.Quit(1)
|
self.lp.Quit(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resume, err := self.lp.Suspend()
|
|
||||||
if err != nil {
|
|
||||||
self.err = err
|
|
||||||
self.lp.Quit(1)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer resume()
|
err = self.lp.SuspendAndRun(func() error {
|
||||||
cmd := exec.Command(exe, "edit-in-kitty", "--type=overlay", fp)
|
cmd := exec.Command(exe, "edit-in-kitty", "--type=overlay", fp)
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
@ -461,6 +460,13 @@ func (self *handler) handle_favorites_key_event(event *loop.KeyEvent) {
|
|||||||
var ln string
|
var ln string
|
||||||
fmt.Scanln(&ln)
|
fmt.Scanln(&ln)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
self.err = err
|
||||||
|
self.lp.Quit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,7 @@ func (self *table) current_codepoint() rune {
|
|||||||
|
|
||||||
func (self *table) set_codepoints(codepoints []rune, mode Mode, current_idx int) {
|
func (self *table) set_codepoints(codepoints []rune, mode Mode, current_idx int) {
|
||||||
self.codepoints = codepoints
|
self.codepoints = codepoints
|
||||||
if self.codepoints != nil {
|
if self.codepoints != nil && mode != FAVORITES && mode != HEX {
|
||||||
slices.Sort(self.codepoints)
|
slices.Sort(self.codepoints)
|
||||||
}
|
}
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#version GLSL_VERSION
|
#version GLSL_VERSION
|
||||||
uniform uvec2 viewport;
|
uniform uvec2 viewport;
|
||||||
uniform uint colors[9];
|
uniform uint colors[9];
|
||||||
uniform float background_opacity;
|
uniform float background_opacity, do_srgb_correction;
|
||||||
uniform float tint_opacity, tint_premult;
|
uniform float tint_opacity, tint_premult;
|
||||||
uniform float gamma_lut[256];
|
uniform float gamma_lut[256];
|
||||||
in vec4 rect; // left, top, right, bottom
|
in vec4 rect; // left, top, right, bottom
|
||||||
@ -22,6 +22,14 @@ const uvec2 pos_map[] = uvec2[4](
|
|||||||
uvec2(LEFT, TOP)
|
uvec2(LEFT, TOP)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
float linear2srgb(float x) {
|
||||||
|
// Linear to sRGB conversion. Needed to match alpha from the cell shader
|
||||||
|
float lower = 12.92 * x;
|
||||||
|
float upper = 1.055 * pow(x, 1.0f / 2.4f) - 0.055f;
|
||||||
|
|
||||||
|
return mix(lower, upper, step(0.0031308f, x));
|
||||||
|
}
|
||||||
|
|
||||||
float to_color(uint c) {
|
float to_color(uint c) {
|
||||||
return gamma_lut[c & FF];
|
return gamma_lut[c & FF];
|
||||||
}
|
}
|
||||||
@ -45,5 +53,6 @@ void main() {
|
|||||||
color3 = is_window_bg * window_bg + (1. - is_window_bg) * color3;
|
color3 = is_window_bg * window_bg + (1. - is_window_bg) * color3;
|
||||||
float final_opacity = is_default_bg * tint_opacity + (1. - is_default_bg) * background_opacity;
|
float final_opacity = is_default_bg * tint_opacity + (1. - is_default_bg) * background_opacity;
|
||||||
float final_premult_opacity = is_default_bg * tint_premult + (1. - is_default_bg) * background_opacity;
|
float final_premult_opacity = is_default_bg * tint_premult + (1. - is_default_bg) * background_opacity;
|
||||||
|
final_opacity = do_srgb_correction * linear2srgb(final_opacity) + (1. - do_srgb_correction) * final_opacity;
|
||||||
color = vec4(color3 * final_premult_opacity, final_opacity);
|
color = vec4(color3 * final_premult_opacity, final_opacity);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import sys
|
|||||||
from contextlib import contextmanager, suppress
|
from contextlib import contextmanager, suppress
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
from gettext import ngettext
|
||||||
from time import monotonic, sleep
|
from time import monotonic, sleep
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
@ -1010,9 +1011,8 @@ class Boss:
|
|||||||
tm = tab.tab_manager_ref()
|
tm = tab.tab_manager_ref()
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
tm.set_active_tab(tab)
|
tm.set_active_tab(tab)
|
||||||
self.confirm(_(
|
self.confirm(ngettext('Are you sure you want to close this tab, it has one window running?',
|
||||||
'Are you sure you want to close this tab, it has {}'
|
'Are you sure you want to close this tab, it has {} windows running?', num).format(num),
|
||||||
' windows running?').format(num),
|
|
||||||
self.handle_close_tab_confirmation, tab.id,
|
self.handle_close_tab_confirmation, tab.id,
|
||||||
window=tab.active_window,
|
window=tab.active_window,
|
||||||
)
|
)
|
||||||
@ -1602,8 +1602,8 @@ class Boss:
|
|||||||
if tm is not None:
|
if tm is not None:
|
||||||
w = tm.active_window
|
w = tm.active_window
|
||||||
self.confirm(
|
self.confirm(
|
||||||
_('Are you sure you want to close this OS window, it has {}'
|
ngettext('Are you sure you want to close this OS window, it has one window running?',
|
||||||
' windows running?').format(num),
|
'Are you sure you want to close this OS window, it has {} windows running', num).format(num),
|
||||||
self.handle_close_os_window_confirmation, os_window_id,
|
self.handle_close_os_window_confirmation, os_window_id,
|
||||||
window=w,
|
window=w,
|
||||||
)
|
)
|
||||||
@ -1642,7 +1642,8 @@ class Boss:
|
|||||||
return
|
return
|
||||||
assert tm is not None
|
assert tm is not None
|
||||||
self.confirm(
|
self.confirm(
|
||||||
_('Are you sure you want to quit kitty, it has {} windows running?').format(num),
|
ngettext('Are you sure you want to quit kitty, it has one window running?',
|
||||||
|
'Are you sure you want to quit kitty, it has {} windows running?', num).format(num),
|
||||||
self.handle_quit_confirmation,
|
self.handle_quit_confirmation,
|
||||||
window=tm.active_window,
|
window=tm.active_window,
|
||||||
)
|
)
|
||||||
@ -1812,7 +1813,8 @@ class Boss:
|
|||||||
'tab', '''
|
'tab', '''
|
||||||
Change the title of the active tab interactively, by typing in the new title.
|
Change the title of the active tab interactively, by typing in the new title.
|
||||||
If you specify an argument to this action then that is used as the title instead of asking for it.
|
If you specify an argument to this action then that is used as the title instead of asking for it.
|
||||||
Use the empty string ("") to reset the title to default. For example::
|
Use the empty string ("") to reset the title to default. Use a space (" ") to indicate that the
|
||||||
|
prompt should not be pre-filled. For example::
|
||||||
|
|
||||||
# interactive usage
|
# interactive usage
|
||||||
map f1 set_tab_title
|
map f1 set_tab_title
|
||||||
@ -1820,19 +1822,24 @@ class Boss:
|
|||||||
map f2 set_tab_title some title
|
map f2 set_tab_title some title
|
||||||
# reset to default
|
# reset to default
|
||||||
map f3 set_tab_title ""
|
map f3 set_tab_title ""
|
||||||
|
# interactive usage without prefilled prompt
|
||||||
|
map f3 set_tab_title " "
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
def set_tab_title(self, title: Optional[str] = None) -> None:
|
def set_tab_title(self, title: Optional[str] = None) -> None:
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
if tab:
|
if tab:
|
||||||
if title is not None:
|
if title is not None and title not in ('" "', "' '"):
|
||||||
if title in ('""', "''"):
|
if title in ('""', "''"):
|
||||||
title = ''
|
title = ''
|
||||||
tab.set_title(title)
|
tab.set_title(title)
|
||||||
return
|
return
|
||||||
|
prefilled = tab.name or tab.title
|
||||||
|
if title in ('" "', "' '"):
|
||||||
|
prefilled = ''
|
||||||
args = [
|
args = [
|
||||||
'--name=tab-title', '--message', _('Enter the new title for this tab below.'),
|
'--name=tab-title', '--message', _('Enter the new title for this tab below.'),
|
||||||
'--default', tab.name or tab.title, 'do_set_tab_title', str(tab.id)]
|
'--default', prefilled, 'do_set_tab_title', str(tab.id)]
|
||||||
self.run_kitten_with_metadata('ask', args)
|
self.run_kitten_with_metadata('ask', args)
|
||||||
|
|
||||||
def do_set_tab_title(self, title: str, tab_id: int) -> None:
|
def do_set_tab_title(self, title: str, tab_id: int) -> None:
|
||||||
@ -2628,7 +2635,7 @@ class Boss:
|
|||||||
where = 'new' if args[0] == 'new-tab' else args[0][4:]
|
where = 'new' if args[0] == 'new-tab' else args[0][4:]
|
||||||
return self._move_window_to(target_tab_id=where)
|
return self._move_window_to(target_tab_id=where)
|
||||||
ct = self.active_tab
|
ct = self.active_tab
|
||||||
items: List[Tuple[Union[str, int], str]] = [(t.id, t.title) for t in self.all_tabs if t is not ct]
|
items: List[Tuple[Union[str, int], str]] = [(t.id, t.effective_title) for t in self.all_tabs if t is not ct]
|
||||||
items.append(('new_tab', 'New tab'))
|
items.append(('new_tab', 'New tab'))
|
||||||
items.append(('new_os_window', 'New OS Window'))
|
items.append(('new_os_window', 'New OS Window'))
|
||||||
target_window = self.active_window
|
target_window = self.active_window
|
||||||
|
|||||||
@ -37,6 +37,22 @@ in float colored_sprite;
|
|||||||
out vec4 final_color;
|
out vec4 final_color;
|
||||||
|
|
||||||
// Util functions {{{
|
// Util functions {{{
|
||||||
|
float linear2srgb(float x) {
|
||||||
|
// Linear to sRGB conversion.
|
||||||
|
float lower = 12.92 * x;
|
||||||
|
float upper = 1.055 * pow(x, 1.0f / 2.4f) - 0.055f;
|
||||||
|
|
||||||
|
return mix(lower, upper, step(0.0031308f, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
float srgb2linear(float x) {
|
||||||
|
// sRGB to linear conversion
|
||||||
|
float lower = x / 12.92;
|
||||||
|
float upper = pow((x + 0.055f) / 1.055f, 2.4f);
|
||||||
|
|
||||||
|
return mix(lower, upper, step(0.04045f, x));
|
||||||
|
}
|
||||||
|
|
||||||
vec4 alpha_blend(vec4 over, vec4 under) {
|
vec4 alpha_blend(vec4 over, vec4 under) {
|
||||||
// Alpha blend two colors returning the resulting color pre-multiplied by its alpha
|
// Alpha blend two colors returning the resulting color pre-multiplied by its alpha
|
||||||
// and its alpha.
|
// and its alpha.
|
||||||
@ -103,20 +119,9 @@ vec4 vec4_premul(vec4 rgba) {
|
|||||||
#ifdef NEEDS_FOREGROUND
|
#ifdef NEEDS_FOREGROUND
|
||||||
// sRGB luminance values
|
// sRGB luminance values
|
||||||
const vec3 Y = vec3(0.2126, 0.7152, 0.0722);
|
const vec3 Y = vec3(0.2126, 0.7152, 0.0722);
|
||||||
const float gamma_factor = 2.2;
|
|
||||||
// Scaling factor for the extra text-alpha adjustment for luminance-difference.
|
// Scaling factor for the extra text-alpha adjustment for luminance-difference.
|
||||||
const float text_gamma_scaling = 0.5;
|
const float text_gamma_scaling = 0.5;
|
||||||
|
|
||||||
float linear2srgb(float x) {
|
|
||||||
// Approximation of linear-to-sRGB conversion
|
|
||||||
return pow(x, 1.0 / gamma_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
float srgb2linear(float x) {
|
|
||||||
// Approximation of sRGB-to-linear conversion
|
|
||||||
return pow(x, gamma_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
float clamp_to_unit_float(float x) {
|
float clamp_to_unit_float(float x) {
|
||||||
// Clamp value to suitable output range
|
// Clamp value to suitable output range
|
||||||
return clamp(x, 0.0f, 1.0f);
|
return clamp(x, 0.0f, 1.0f);
|
||||||
@ -179,6 +184,12 @@ void main() {
|
|||||||
vec4 fg = calculate_foreground(background);
|
vec4 fg = calculate_foreground(background);
|
||||||
#ifdef TRANSPARENT
|
#ifdef TRANSPARENT
|
||||||
final_color = alpha_blend_premul(fg, vec4_premul(background, bg_alpha));
|
final_color = alpha_blend_premul(fg, vec4_premul(background, bg_alpha));
|
||||||
|
|
||||||
|
// Adjust the transparent alpha-channel to account for incorrect
|
||||||
|
// gamma-blending performed by the compositor (true for at least wlroots,
|
||||||
|
// picom, GNOME, MacOS).
|
||||||
|
// This is the last pass:
|
||||||
|
final_color.a = linear2srgb(final_color.a);
|
||||||
#else
|
#else
|
||||||
final_color = alpha_blend_premul(fg, background);
|
final_color = alpha_blend_premul(fg, background);
|
||||||
#endif
|
#endif
|
||||||
@ -193,7 +204,7 @@ void main() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BACKGROUND
|
#ifdef BACKGROUND
|
||||||
#if defined(TRANSPARENT)
|
#ifdef TRANSPARENT
|
||||||
final_color = vec4_premul(background, bg_alpha);
|
final_color = vec4_premul(background, bg_alpha);
|
||||||
#else
|
#else
|
||||||
final_color = vec4(background, draw_bg);
|
final_color = vec4(background, draw_bg);
|
||||||
@ -202,6 +213,10 @@ void main() {
|
|||||||
|
|
||||||
#ifdef FOREGROUND
|
#ifdef FOREGROUND
|
||||||
final_color = calculate_foreground(); // pre-multiplied foreground
|
final_color = calculate_foreground(); // pre-multiplied foreground
|
||||||
|
|
||||||
|
// This is the last pass, adjust alpha to compensate for gamma-incorrect
|
||||||
|
// blending in compositor:
|
||||||
|
final_color.a = linear2srgb(final_color.a);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#define {WHICH_PROGRAM}
|
#define {WHICH_PROGRAM}
|
||||||
#define NOT_TRANSPARENT
|
#define NOT_TRANSPARENT
|
||||||
|
#define BOLD_SHIFT {BOLD_SHIFT}
|
||||||
#define DECORATION_SHIFT {DECORATION_SHIFT}
|
#define DECORATION_SHIFT {DECORATION_SHIFT}
|
||||||
#define REVERSE_SHIFT {REVERSE_SHIFT}
|
#define REVERSE_SHIFT {REVERSE_SHIFT}
|
||||||
#define STRIKE_SHIFT {STRIKE_SHIFT}
|
#define STRIKE_SHIFT {STRIKE_SHIFT}
|
||||||
@ -17,6 +18,7 @@ layout(std140) uniform CellRenderData {
|
|||||||
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg;
|
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg;
|
||||||
|
|
||||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
||||||
|
uint bold_is_bright;
|
||||||
|
|
||||||
uint xnum, ynum, cursor_fg_sprite_idx;
|
uint xnum, ynum, cursor_fg_sprite_idx;
|
||||||
float cursor_x, cursor_y, cursor_w;
|
float cursor_x, cursor_y, cursor_w;
|
||||||
@ -93,6 +95,22 @@ vec3 color_to_vec(uint c) {
|
|||||||
return vec3(gamma_lut[r], gamma_lut[g], gamma_lut[b]);
|
return vec3(gamma_lut[r], gamma_lut[g], gamma_lut[b]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint byte_to_bool(uint n) {
|
||||||
|
uint n1 = (n >> 1) | n;
|
||||||
|
uint n2 = (n1 >> 2) | n1;
|
||||||
|
uint n3 = (n2 >> 4) | n2;
|
||||||
|
return n3 & 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint brighten_color(uint c, uint is_bold) {
|
||||||
|
uint table_idx = (c >> 8) & 0xFFu;
|
||||||
|
uint is_table_color = c & 1u;
|
||||||
|
uint is_rgb_color = byte_to_bool(c & 0xFEu);
|
||||||
|
uint is_8bit_color = byte_to_bool(table_idx & 0xF8u);
|
||||||
|
uint should_brighten = bold_is_bright * is_bold * (1u >> (is_rgb_color + is_8bit_color)) * is_table_color;
|
||||||
|
return c | (0x800u * should_brighten);
|
||||||
|
}
|
||||||
|
|
||||||
uint resolve_color(uint c, uint defval) {
|
uint resolve_color(uint c, uint defval) {
|
||||||
// Convert a cell color to an actual color based on the color table
|
// Convert a cell color to an actual color based on the color table
|
||||||
int t = int(c & BYTE_MASK);
|
int t = int(c & BYTE_MASK);
|
||||||
@ -161,6 +179,7 @@ void main() {
|
|||||||
// set cell color indices {{{
|
// set cell color indices {{{
|
||||||
uvec2 default_colors = uvec2(default_fg, default_bg);
|
uvec2 default_colors = uvec2(default_fg, default_bg);
|
||||||
uint text_attrs = sprite_coords[3];
|
uint text_attrs = sprite_coords[3];
|
||||||
|
uint is_bold = ((text_attrs >> BOLD_SHIFT) & ONE);
|
||||||
uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & ONE);
|
uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & ONE);
|
||||||
uint is_inverted = is_reversed + inverted;
|
uint is_inverted = is_reversed + inverted;
|
||||||
int fg_index = fg_index_map[is_inverted];
|
int fg_index = fg_index_map[is_inverted];
|
||||||
@ -170,10 +189,10 @@ void main() {
|
|||||||
float cell_has_block_cursor = cell_has_cursor * is_block_cursor;
|
float cell_has_block_cursor = cell_has_cursor * is_block_cursor;
|
||||||
int mark = int(text_attrs >> MARK_SHIFT) & MARK_MASK;
|
int mark = int(text_attrs >> MARK_SHIFT) & MARK_MASK;
|
||||||
uint has_mark = uint(step(1, float(mark)));
|
uint has_mark = uint(step(1, float(mark)));
|
||||||
uint bg_as_uint = resolve_color(colors[bg_index], default_colors[bg_index]);
|
uint bg_as_uint = resolve_color(brighten_color(colors[bg_index], is_bold), default_colors[bg_index]);
|
||||||
bg_as_uint = has_mark * color_table[NUM_COLORS + mark] + (ONE - has_mark) * bg_as_uint;
|
bg_as_uint = has_mark * color_table[NUM_COLORS + mark] + (ONE - has_mark) * bg_as_uint;
|
||||||
vec3 bg = color_to_vec(bg_as_uint);
|
vec3 bg = color_to_vec(bg_as_uint);
|
||||||
uint fg_as_uint = resolve_color(colors[fg_index], default_colors[fg_index]);
|
uint fg_as_uint = resolve_color(brighten_color(colors[fg_index], is_bold), default_colors[fg_index]);
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// Foreground {{{
|
// Foreground {{{
|
||||||
|
|||||||
@ -429,7 +429,7 @@ class Child:
|
|||||||
return environ_of_process(pid)
|
return environ_of_process(pid)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def send_signal_for_key(self, key_num: int) -> bool:
|
def send_signal_for_key(self, key_num: bytes) -> bool:
|
||||||
import signal
|
import signal
|
||||||
import termios
|
import termios
|
||||||
if self.child_fd is None:
|
if self.child_fd is None:
|
||||||
|
|||||||
@ -960,7 +960,7 @@ cocoa_set_dock_icon(PyObject UNUSED *self, PyObject *args) {
|
|||||||
static NSSound *beep_sound = nil;
|
static NSSound *beep_sound = nil;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cleanup() {
|
cleanup(void) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
|
|
||||||
if (dockMenu) [dockMenu release];
|
if (dockMenu) [dockMenu release];
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class Version(NamedTuple):
|
|||||||
|
|
||||||
appname: str = 'kitty'
|
appname: str = 'kitty'
|
||||||
kitty_face = '🐱'
|
kitty_face = '🐱'
|
||||||
version: Version = Version(0, 28, 0)
|
version: Version = Version(0, 28, 1)
|
||||||
str_version: str = '.'.join(map(str, version))
|
str_version: str = '.'.join(map(str, version))
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
is_macos: bool = 'darwin' in _plat
|
is_macos: bool = 'darwin' in _plat
|
||||||
|
|||||||
@ -155,7 +155,7 @@ font_descriptor_from_python(PyObject *src) {
|
|||||||
static CTFontCollectionRef all_fonts_collection_data = NULL;
|
static CTFontCollectionRef all_fonts_collection_data = NULL;
|
||||||
|
|
||||||
static CTFontCollectionRef
|
static CTFontCollectionRef
|
||||||
all_fonts_collection() {
|
all_fonts_collection(void) {
|
||||||
if (all_fonts_collection_data == NULL) all_fonts_collection_data = CTFontCollectionCreateFromAvailableFonts(NULL);
|
if (all_fonts_collection_data == NULL) all_fonts_collection_data = CTFontCollectionCreateFromAvailableFonts(NULL);
|
||||||
return all_fonts_collection_data;
|
return all_fonts_collection_data;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ user_cache_dir(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
process_group_map() {
|
process_group_map(void) {
|
||||||
int num_of_processes = proc_listallpids(NULL, 0);
|
int num_of_processes = proc_listallpids(NULL, 0);
|
||||||
size_t bufsize = sizeof(pid_t) * (num_of_processes + 1024);
|
size_t bufsize = sizeof(pid_t) * (num_of_processes + 1024);
|
||||||
FREE_AFTER_FUNCTION pid_t *buf = malloc(bufsize);
|
FREE_AFTER_FUNCTION pid_t *buf = malloc(bufsize);
|
||||||
|
|||||||
@ -94,6 +94,7 @@ def edit(args: List[str]) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def shebang(args: List[str]) -> None:
|
def shebang(args: List[str]) -> None:
|
||||||
|
from kitty.constants import kitten_exe
|
||||||
script_path = args[1]
|
script_path = args[1]
|
||||||
cmd = args[2:]
|
cmd = args[2:]
|
||||||
if cmd == ['__ext__']:
|
if cmd == ['__ext__']:
|
||||||
@ -111,7 +112,7 @@ def shebang(args: List[str]) -> None:
|
|||||||
cmd = line.split(' ')
|
cmd = line.split(' ')
|
||||||
else:
|
else:
|
||||||
cmd = line.split(' ', maxsplit=1)
|
cmd = line.split(' ', maxsplit=1)
|
||||||
os.execvp(cmd[0], cmd + [script_path])
|
os.execvp(kitten_exe(), ['kitten', '__confirm_and_run_shebang__'] + cmd + [script_path])
|
||||||
|
|
||||||
|
|
||||||
def run_kitten(args: List[str]) -> None:
|
def run_kitten(args: List[str]) -> None:
|
||||||
|
|||||||
@ -265,6 +265,7 @@ CELL_FG_PROGRAM: int
|
|||||||
CELL_PROGRAM: int
|
CELL_PROGRAM: int
|
||||||
CELL_SPECIAL_PROGRAM: int
|
CELL_SPECIAL_PROGRAM: int
|
||||||
CSI: int
|
CSI: int
|
||||||
|
BOLD: int
|
||||||
DCS: int
|
DCS: int
|
||||||
DECORATION: int
|
DECORATION: int
|
||||||
DIM: int
|
DIM: int
|
||||||
|
|||||||
@ -543,6 +543,7 @@ START_ALLOW_CASE_RANGE
|
|||||||
case 0xe0b0 ... 0xe0bf: // powerline box drawing
|
case 0xe0b0 ... 0xe0bf: // powerline box drawing
|
||||||
case 0x1fb00 ... 0x1fb8b: // symbols for legacy computing
|
case 0x1fb00 ... 0x1fb8b: // symbols for legacy computing
|
||||||
case 0x1fba0 ... 0x1fbae:
|
case 0x1fba0 ... 0x1fbae:
|
||||||
|
case 0x1fb90: // inverse medium shade
|
||||||
return BOX_FONT;
|
return BOX_FONT;
|
||||||
default:
|
default:
|
||||||
*is_emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
|
*is_emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
|
||||||
@ -585,6 +586,9 @@ START_ALLOW_CASE_RANGE
|
|||||||
return 0x151 + ch - 0x1fba0;
|
return 0x151 + ch - 0x1fba0;
|
||||||
case 0x2800 ... 0x28ff:
|
case 0x2800 ... 0x28ff:
|
||||||
return 0x160 + ch - 0x2800;
|
return 0x160 + ch - 0x2800;
|
||||||
|
case 0x1fb90:
|
||||||
|
// Allocated to allow for 0x1fb8c ... 0x1fb94 eventually
|
||||||
|
return 0x25f + ch - 0x1fb8c;
|
||||||
default:
|
default:
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -617,37 +617,18 @@ def shade(buf: BufType, width: int, height: int, light: bool = False, invert: bo
|
|||||||
square_sz = max(1, width // 12)
|
square_sz = max(1, width // 12)
|
||||||
number_of_rows = height // square_sz
|
number_of_rows = height // square_sz
|
||||||
number_of_cols = width // square_sz
|
number_of_cols = width // square_sz
|
||||||
nums = tuple(range(square_sz))
|
nums = range(square_sz)
|
||||||
|
|
||||||
dest = bytearray(width * height) if invert else buf
|
|
||||||
|
|
||||||
for r in range(number_of_rows):
|
for r in range(number_of_rows):
|
||||||
is_odd = r % 2 != 0
|
for c in range(number_of_cols):
|
||||||
if is_odd:
|
if invert ^ ((r % 2 != c % 2) or (light and r % 2 == 1)):
|
||||||
continue
|
continue
|
||||||
fill_even = r % 4 == 0
|
|
||||||
for yr in nums:
|
for yr in nums:
|
||||||
y = r * square_sz + yr
|
y = r * square_sz + yr
|
||||||
if y >= height:
|
offset = width * y
|
||||||
break
|
|
||||||
off = width * y
|
|
||||||
for c in range(number_of_cols):
|
|
||||||
if light:
|
|
||||||
fill = (c % 4) == (0 if fill_even else 2)
|
|
||||||
else:
|
|
||||||
fill = (c % 2 == 0) == fill_even
|
|
||||||
if fill:
|
|
||||||
for xc in nums:
|
for xc in nums:
|
||||||
x = (c * square_sz) + xc
|
x = c * square_sz + xc
|
||||||
if x >= width:
|
buf[offset + x] = 255
|
||||||
break
|
|
||||||
dest[off + x] = 255
|
|
||||||
if invert:
|
|
||||||
for y in range(height):
|
|
||||||
off = width * y
|
|
||||||
for x in range(width):
|
|
||||||
q = off + x
|
|
||||||
buf[q] = 255 - dest[q]
|
|
||||||
|
|
||||||
|
|
||||||
def quad(buf: BufType, width: int, height: int, x: int = 0, y: int = 0) -> None:
|
def quad(buf: BufType, width: int, height: int, x: int = 0, y: int = 0) -> None:
|
||||||
@ -883,7 +864,8 @@ box_chars: Dict[str, List[Callable[[BufType, int, int], Any]]] = {
|
|||||||
'▐': [p(eight_block, which=(4, 5, 6, 7))],
|
'▐': [p(eight_block, which=(4, 5, 6, 7))],
|
||||||
'░': [p(shade, light=True)],
|
'░': [p(shade, light=True)],
|
||||||
'▒': [shade],
|
'▒': [shade],
|
||||||
'▓': [p(shade, invert=True)],
|
'▓': [p(shade, light=True, invert=True)],
|
||||||
|
'🮐': [p(shade, invert=True)],
|
||||||
'▔': [p(eight_bar, horizontal=True)],
|
'▔': [p(eight_bar, horizontal=True)],
|
||||||
'▕': [p(eight_bar, which=7)],
|
'▕': [p(eight_bar, which=7)],
|
||||||
'▖': [p(quad, y=1)],
|
'▖': [p(quad, y=1)],
|
||||||
|
|||||||
@ -561,6 +561,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||||||
img->root_frame_data_loaded = false;
|
img->root_frame_data_loaded = false;
|
||||||
img->is_drawn = false;
|
img->is_drawn = false;
|
||||||
img->current_frame_shown_at = 0;
|
img->current_frame_shown_at = 0;
|
||||||
|
img->extra_framecnt = 0;
|
||||||
free_image(self, img);
|
free_image(self, img);
|
||||||
*is_dirty = true;
|
*is_dirty = true;
|
||||||
self->layers_dirty = true;
|
self->layers_dirty = true;
|
||||||
|
|||||||
@ -715,7 +715,7 @@ class EditCmd:
|
|||||||
self.is_local_file = False
|
self.is_local_file = False
|
||||||
with suppress(OSError):
|
with suppress(OSError):
|
||||||
st = os.stat(self.file_localpath)
|
st = os.stat(self.file_localpath)
|
||||||
self.is_local_file = (st.st_dev, st.st_ino) == self.file_inode
|
self.is_local_file = (st.st_dev, st.st_ino) == self.file_inode and os.access(self.file_localpath, os.W_OK | os.R_OK)
|
||||||
if not self.is_local_file:
|
if not self.is_local_file:
|
||||||
import tempfile
|
import tempfile
|
||||||
self.tdir = tempfile.mkdtemp()
|
self.tdir = tempfile.mkdtemp()
|
||||||
|
|||||||
@ -332,7 +332,7 @@ exec_kitten(int argc, char *argv[], char *exe_dir) {
|
|||||||
newargv[argc] = 0;
|
newargv[argc] = 0;
|
||||||
newargv[0] = "kitten";
|
newargv[0] = "kitten";
|
||||||
errno = 0;
|
errno = 0;
|
||||||
execv(exe, argv);
|
execv(exe, newargv);
|
||||||
fprintf(stderr, "Failed to execute kitten (%s) with error: %s\n", exe, strerror(errno));
|
fprintf(stderr, "Failed to execute kitten (%s) with error: %s\n", exe, strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ cwd_of_process(PyObject *self UNUSED, PyObject *pid_) {
|
|||||||
|
|
||||||
// Read the maximum argument size for processes
|
// Read the maximum argument size for processes
|
||||||
static int
|
static int
|
||||||
get_argmax() {
|
get_argmax(void) {
|
||||||
int argmax;
|
int argmax;
|
||||||
int mib[] = { CTL_KERN, KERN_ARGMAX };
|
int mib[] = { CTL_KERN, KERN_ARGMAX };
|
||||||
size_t size = sizeof(argmax);
|
size_t size = sizeof(argmax);
|
||||||
|
|||||||
@ -1328,6 +1328,10 @@ color is chosen to match the background color of the neighboring tab.
|
|||||||
)
|
)
|
||||||
egr() # }}}
|
egr() # }}}
|
||||||
|
|
||||||
|
opt('bold_is_bright', 'no',
|
||||||
|
option_type='to_bool', ctype='bool',
|
||||||
|
long_text='Display bold text with bright colors'
|
||||||
|
)
|
||||||
|
|
||||||
# colors {{{
|
# colors {{{
|
||||||
agr('colors', 'Color scheme')
|
agr('colors', 'Color scheme')
|
||||||
@ -3990,18 +3994,31 @@ You can create shortcuts to clear/reset the terminal. For example::
|
|||||||
If you want to operate on all kitty windows instead of just the current one, use
|
If you want to operate on all kitty windows instead of just the current one, use
|
||||||
:italic:`all` instead of :italic:`active`.
|
:italic:`all` instead of :italic:`active`.
|
||||||
|
|
||||||
It is also possible to remap :kbd:`Ctrl+L` to both scroll the current screen
|
Some useful functions that can be defined in the shell rc files to perform various kinds of
|
||||||
|
clearing of the current window:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
clear-only-screen() {
|
||||||
|
printf "\e[H\e[2J"
|
||||||
|
}
|
||||||
|
|
||||||
|
clear-screen-and-scrollback() {
|
||||||
|
printf "\e[H\e[3J"
|
||||||
|
}
|
||||||
|
|
||||||
|
clear-screen-saving-contents-in-scrollback() {
|
||||||
|
printf "\e[H\e[22J"
|
||||||
|
}
|
||||||
|
|
||||||
|
For instance, using these functions, it is possible to remap :kbd:`Ctrl+L` to both scroll the current screen
|
||||||
contents into the scrollback buffer and clear the screen, instead of just
|
contents into the scrollback buffer and clear the screen, instead of just
|
||||||
clearing the screen, for example, for ZSH add the following to :file:`~/.zshrc`:
|
clearing the screen. For ZSH, in :file:`~/.zshrc` after the above functions, add:
|
||||||
|
|
||||||
.. code-block:: zsh
|
.. code-block:: zsh
|
||||||
|
|
||||||
scroll-and-clear-screen() {
|
zle -N clear-screen-saving-contents-in-scrollback
|
||||||
printf '\\n%.0s' {1..$LINES}
|
bindkey '^l' clear-screen-saving-contents-in-scrollback
|
||||||
zle clear-screen
|
|
||||||
}
|
|
||||||
zle -N scroll-and-clear-screen
|
|
||||||
bindkey '^l' scroll-and-clear-screen
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|||||||
3
kitty/options/parse.py
generated
3
kitty/options/parse.py
generated
@ -100,6 +100,9 @@ class Parser:
|
|||||||
def bold_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
def bold_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
ans['bold_font'] = str(val)
|
ans['bold_font'] = str(val)
|
||||||
|
|
||||||
|
def bold_is_bright(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
|
ans['bold_is_bright'] = to_bool(val)
|
||||||
|
|
||||||
def bold_italic_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
def bold_italic_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
ans['bold_italic_font'] = str(val)
|
ans['bold_italic_font'] = str(val)
|
||||||
|
|
||||||
|
|||||||
15
kitty/options/to-c-generated.h
generated
15
kitty/options/to-c-generated.h
generated
@ -837,6 +837,19 @@ convert_from_opts_dim_opacity(PyObject *py_opts, Options *opts) {
|
|||||||
Py_DECREF(ret);
|
Py_DECREF(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
convert_from_python_bold_is_bright(PyObject *val, Options *opts) {
|
||||||
|
opts->bold_is_bright = PyObject_IsTrue(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
convert_from_opts_bold_is_bright(PyObject *py_opts, Options *opts) {
|
||||||
|
PyObject *ret = PyObject_GetAttrString(py_opts, "bold_is_bright");
|
||||||
|
if (ret == NULL) return;
|
||||||
|
convert_from_python_bold_is_bright(ret, opts);
|
||||||
|
Py_DECREF(ret);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
convert_from_python_mark1_foreground(PyObject *val, Options *opts) {
|
convert_from_python_mark1_foreground(PyObject *val, Options *opts) {
|
||||||
opts->mark1_foreground = color_as_int(val);
|
opts->mark1_foreground = color_as_int(val);
|
||||||
@ -1188,6 +1201,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
|
|||||||
if (PyErr_Occurred()) return false;
|
if (PyErr_Occurred()) return false;
|
||||||
convert_from_opts_dim_opacity(py_opts, opts);
|
convert_from_opts_dim_opacity(py_opts, opts);
|
||||||
if (PyErr_Occurred()) return false;
|
if (PyErr_Occurred()) return false;
|
||||||
|
convert_from_opts_bold_is_bright(py_opts, opts);
|
||||||
|
if (PyErr_Occurred()) return false;
|
||||||
convert_from_opts_mark1_foreground(py_opts, opts);
|
convert_from_opts_mark1_foreground(py_opts, opts);
|
||||||
if (PyErr_Occurred()) return false;
|
if (PyErr_Occurred()) return false;
|
||||||
convert_from_opts_mark1_background(py_opts, opts);
|
convert_from_opts_mark1_background(py_opts, opts);
|
||||||
|
|||||||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
@ -71,6 +71,7 @@ option_names = ( # {{{
|
|||||||
'bell_on_tab',
|
'bell_on_tab',
|
||||||
'bell_path',
|
'bell_path',
|
||||||
'bold_font',
|
'bold_font',
|
||||||
|
'bold_is_bright',
|
||||||
'bold_italic_font',
|
'bold_italic_font',
|
||||||
'box_drawing_scale',
|
'box_drawing_scale',
|
||||||
'clear_all_mouse_actions',
|
'clear_all_mouse_actions',
|
||||||
@ -490,6 +491,7 @@ class Options:
|
|||||||
bell_on_tab: str = '🔔 '
|
bell_on_tab: str = '🔔 '
|
||||||
bell_path: typing.Optional[str] = None
|
bell_path: typing.Optional[str] = None
|
||||||
bold_font: str = 'auto'
|
bold_font: str = 'auto'
|
||||||
|
bold_is_bright: bool = False
|
||||||
bold_italic_font: str = 'auto'
|
bold_italic_font: str = 'auto'
|
||||||
box_drawing_scale: typing.Tuple[float, float, float, float] = (0.001, 1.0, 1.5, 2.0)
|
box_drawing_scale: typing.Tuple[float, float, float, float] = (0.001, 1.0, 1.5, 2.0)
|
||||||
clear_all_mouse_actions: bool = False
|
clear_all_mouse_actions: bool = False
|
||||||
|
|||||||
@ -422,6 +422,7 @@ dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
|
|||||||
case 9:
|
case 9:
|
||||||
case 99:
|
case 99:
|
||||||
case 777:
|
case 777:
|
||||||
|
case 1337:
|
||||||
START_DISPATCH
|
START_DISPATCH
|
||||||
DISPATCH_OSC_WITH_CODE(desktop_notify)
|
DISPATCH_OSC_WITH_CODE(desktop_notify)
|
||||||
END_DISPATCH
|
END_DISPATCH
|
||||||
|
|||||||
@ -1628,6 +1628,26 @@ screen_clear_scrollback(Screen *self) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Line* visual_line_(Screen *self, int y_);
|
||||||
|
|
||||||
|
static void
|
||||||
|
screen_move_into_scrollback(Screen *self) {
|
||||||
|
if (self->linebuf != self->main_linebuf || self->margin_top != 0 || self->margin_bottom != self->lines - 1) return;
|
||||||
|
unsigned int num_of_lines_to_move = self->lines;
|
||||||
|
while (num_of_lines_to_move) {
|
||||||
|
Line *line = visual_line_(self, num_of_lines_to_move-1);
|
||||||
|
if (!line_is_empty(line)) break;
|
||||||
|
num_of_lines_to_move--;
|
||||||
|
}
|
||||||
|
if (num_of_lines_to_move) {
|
||||||
|
unsigned int top, bottom;
|
||||||
|
for (; num_of_lines_to_move; num_of_lines_to_move--) {
|
||||||
|
top = 0, bottom = num_of_lines_to_move - 1;
|
||||||
|
INDEX_UP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
||||||
/* Erases display in a specific way.
|
/* Erases display in a specific way.
|
||||||
@ -1640,6 +1660,8 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
|||||||
including cursor position.
|
including cursor position.
|
||||||
* ``2`` -- Erases complete display. All lines are erased
|
* ``2`` -- Erases complete display. All lines are erased
|
||||||
and changed to single-width. Cursor does not move.
|
and changed to single-width. Cursor does not move.
|
||||||
|
* ``22`` -- Copy screen contents into scrollback if in main screen,
|
||||||
|
then do the same as ``2``.
|
||||||
* ``3`` -- Erase complete display and scrollback buffer as well.
|
* ``3`` -- Erase complete display and scrollback buffer as well.
|
||||||
:param bool private: when ``True`` character attributes are left unchanged
|
:param bool private: when ``True`` character attributes are left unchanged
|
||||||
*/
|
*/
|
||||||
@ -1649,6 +1671,10 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
|||||||
a = self->cursor->y + 1; b = self->lines; break;
|
a = self->cursor->y + 1; b = self->lines; break;
|
||||||
case 1:
|
case 1:
|
||||||
a = 0; b = self->cursor->y; break;
|
a = 0; b = self->cursor->y; break;
|
||||||
|
case 22:
|
||||||
|
screen_move_into_scrollback(self);
|
||||||
|
how = 2;
|
||||||
|
/* fallthrough */
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
grman_clear(self->grman, how == 3, self->cell_size);
|
grman_clear(self->grman, how == 3, self->cell_size);
|
||||||
@ -1671,7 +1697,7 @@ screen_erase_in_display(Screen *self, unsigned int how, bool private) {
|
|||||||
self->is_dirty = true;
|
self->is_dirty = true;
|
||||||
clear_selection(&self->selections);
|
clear_selection(&self->selections);
|
||||||
}
|
}
|
||||||
if (how != 2) {
|
if (how < 2) {
|
||||||
screen_erase_in_line(self, how, private);
|
screen_erase_in_line(self, how, private);
|
||||||
if (how == 1) linebuf_clear_attrs_and_dirty(self->linebuf, self->cursor->y);
|
if (how == 1) linebuf_clear_attrs_and_dirty(self->linebuf, self->cursor->y);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -303,6 +303,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
|||||||
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_color, use_cell_for_selection_bg;
|
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_color, use_cell_for_selection_bg;
|
||||||
|
|
||||||
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
||||||
|
GLuint bold_is_bright;
|
||||||
|
|
||||||
GLuint xnum, ynum, cursor_fg_sprite_idx;
|
GLuint xnum, ynum, cursor_fg_sprite_idx;
|
||||||
GLfloat cursor_x, cursor_y, cursor_w;
|
GLfloat cursor_x, cursor_y, cursor_w;
|
||||||
@ -374,6 +375,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
|||||||
rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y;
|
rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y;
|
||||||
rd->inverted = inverted ? 1 : 0;
|
rd->inverted = inverted ? 1 : 0;
|
||||||
rd->background_opacity = os_window->is_semi_transparent ? os_window->background_opacity : 1.0f;
|
rd->background_opacity = os_window->is_semi_transparent ? os_window->background_opacity : 1.0f;
|
||||||
|
rd->bold_is_bright = OPT(bold_is_bright) ? 1 : 0;
|
||||||
|
|
||||||
#undef COLOR
|
#undef COLOR
|
||||||
rd->url_color = OPT(url_color); rd->url_style = OPT(url_style);
|
rd->url_color = OPT(url_color); rd->url_style = OPT(url_style);
|
||||||
@ -434,8 +436,13 @@ draw_bg(OSWindow *w) {
|
|||||||
|
|
||||||
glUniform1i(bgimage_program_layout.image_location, BGIMAGE_UNIT);
|
glUniform1i(bgimage_program_layout.image_location, BGIMAGE_UNIT);
|
||||||
glUniform1f(bgimage_program_layout.opacity_location, OPT(background_opacity));
|
glUniform1f(bgimage_program_layout.opacity_location, OPT(background_opacity));
|
||||||
|
#ifdef __APPLE__
|
||||||
|
int window_width = w->window_width, window_height = w->window_height;
|
||||||
|
#else
|
||||||
|
int window_width = w->viewport_width, window_height = w->viewport_height;
|
||||||
|
#endif
|
||||||
glUniform4f(bgimage_program_layout.sizes_location,
|
glUniform4f(bgimage_program_layout.sizes_location,
|
||||||
(GLfloat)w->viewport_width, (GLfloat)w->viewport_height, (GLfloat)w->bgimage->width, (GLfloat)w->bgimage->height);
|
(GLfloat)window_width, (GLfloat)window_height, (GLfloat)w->bgimage->width, (GLfloat)w->bgimage->height);
|
||||||
glUniform1f(bgimage_program_layout.premult_location, w->is_semi_transparent ? 1.f : 0.f);
|
glUniform1f(bgimage_program_layout.premult_location, w->is_semi_transparent ? 1.f : 0.f);
|
||||||
GLfloat tiled = 0.f;;
|
GLfloat tiled = 0.f;;
|
||||||
GLfloat left = -1.0, top = 1.0, right = 1.0, bottom = -1.0;
|
GLfloat left = -1.0, top = 1.0, right = 1.0, bottom = -1.0;
|
||||||
@ -446,12 +453,12 @@ draw_bg(OSWindow *w) {
|
|||||||
tiled = 0.f; break;
|
tiled = 0.f; break;
|
||||||
case CENTER_CLAMPED:
|
case CENTER_CLAMPED:
|
||||||
tiled = 1.f;
|
tiled = 1.f;
|
||||||
if (w->viewport_width > (int)w->bgimage->width) {
|
if (window_width > (int)w->bgimage->width) {
|
||||||
GLfloat frac = (w->viewport_width - w->bgimage->width) / (GLfloat)w->viewport_width;
|
GLfloat frac = (window_width - w->bgimage->width) / (GLfloat)window_width;
|
||||||
left += frac; right += frac;
|
left += frac; right += frac;
|
||||||
}
|
}
|
||||||
if (w->viewport_height > (int)w->bgimage->height) {
|
if (window_height > (int)w->bgimage->height) {
|
||||||
GLfloat frac = (w->viewport_height - w->bgimage->height) / (GLfloat)w->viewport_height;
|
GLfloat frac = (window_height - w->bgimage->height) / (GLfloat)window_height;
|
||||||
top -= frac; bottom -= frac;
|
top -= frac; bottom -= frac;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -979,7 +986,7 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, float
|
|||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// Borders {{{
|
// Borders {{{
|
||||||
enum BorderUniforms { BORDER_viewport, BORDER_background_opacity, BORDER_tint_opacity, BORDER_tint_premult, BORDER_colors, BORDER_gamma_lut, NUM_BORDER_UNIFORMS };
|
enum BorderUniforms { BORDER_viewport, BORDER_background_opacity, BORDER_tint_opacity, BORDER_tint_premult, BORDER_colors, BORDER_gamma_lut, BORDER_do_srgb_correction, NUM_BORDER_UNIFORMS };
|
||||||
static GLint border_uniform_locations[NUM_BORDER_UNIFORMS] = {0};
|
static GLint border_uniform_locations[NUM_BORDER_UNIFORMS] = {0};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -989,6 +996,7 @@ init_borders_program(void) {
|
|||||||
SET_LOC(background_opacity)
|
SET_LOC(background_opacity)
|
||||||
SET_LOC(tint_opacity)
|
SET_LOC(tint_opacity)
|
||||||
SET_LOC(tint_premult)
|
SET_LOC(tint_premult)
|
||||||
|
SET_LOC(do_srgb_correction)
|
||||||
SET_LOC(colors)
|
SET_LOC(colors)
|
||||||
SET_LOC(gamma_lut)
|
SET_LOC(gamma_lut)
|
||||||
#undef SET_LOC
|
#undef SET_LOC
|
||||||
@ -1012,6 +1020,7 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
|||||||
float background_opacity = w->is_semi_transparent ? w->background_opacity: 1.0f;
|
float background_opacity = w->is_semi_transparent ? w->background_opacity: 1.0f;
|
||||||
float tint_opacity = background_opacity;
|
float tint_opacity = background_opacity;
|
||||||
float tint_premult = background_opacity;
|
float tint_premult = background_opacity;
|
||||||
|
float do_srgb_correction = 1.0f;
|
||||||
if (has_bgimage(w)) {
|
if (has_bgimage(w)) {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
BLEND_ONTO_OPAQUE;
|
BLEND_ONTO_OPAQUE;
|
||||||
@ -1020,6 +1029,7 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
|||||||
background_opacity = 1.0f;
|
background_opacity = 1.0f;
|
||||||
tint_opacity = OPT(background_tint) * OPT(background_tint_gaps);
|
tint_opacity = OPT(background_tint) * OPT(background_tint_gaps);
|
||||||
tint_premult = w->is_semi_transparent ? OPT(background_tint) : 1.0f;
|
tint_premult = w->is_semi_transparent ? OPT(background_tint) : 1.0f;
|
||||||
|
do_srgb_correction = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_border_rects) {
|
if (num_border_rects) {
|
||||||
@ -1041,6 +1051,7 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
|||||||
glUniform1f(border_uniform_locations[BORDER_background_opacity], background_opacity);
|
glUniform1f(border_uniform_locations[BORDER_background_opacity], background_opacity);
|
||||||
glUniform1f(border_uniform_locations[BORDER_tint_opacity], tint_opacity);
|
glUniform1f(border_uniform_locations[BORDER_tint_opacity], tint_opacity);
|
||||||
glUniform1f(border_uniform_locations[BORDER_tint_premult], tint_premult);
|
glUniform1f(border_uniform_locations[BORDER_tint_premult], tint_premult);
|
||||||
|
glUniform1f(border_uniform_locations[BORDER_do_srgb_correction], do_srgb_correction);
|
||||||
glUniform2ui(border_uniform_locations[BORDER_viewport], viewport_width, viewport_height);
|
glUniform2ui(border_uniform_locations[BORDER_viewport], viewport_width, viewport_height);
|
||||||
glUniform1fv(border_uniform_locations[BORDER_gamma_lut], 256, srgb_lut);
|
glUniform1fv(border_uniform_locations[BORDER_gamma_lut], 256, srgb_lut);
|
||||||
if (has_bgimage(w)) {
|
if (has_bgimage(w)) {
|
||||||
|
|||||||
@ -1130,6 +1130,7 @@ PYWRAP1(patch_global_colors) {
|
|||||||
P(background); P(url_color);
|
P(background); P(url_color);
|
||||||
P(mark1_background); P(mark1_foreground); P(mark2_background); P(mark2_foreground);
|
P(mark1_background); P(mark1_foreground); P(mark2_background); P(mark2_foreground);
|
||||||
P(mark3_background); P(mark3_foreground);
|
P(mark3_background); P(mark3_foreground);
|
||||||
|
P(bold_is_bright);
|
||||||
}
|
}
|
||||||
if (PyErr_Occurred()) return NULL;
|
if (PyErr_Occurred()) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
|||||||
@ -39,6 +39,7 @@ typedef struct {
|
|||||||
char_type *select_by_word_characters_forward;
|
char_type *select_by_word_characters_forward;
|
||||||
color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color, tab_bar_background, tab_bar_margin_color;
|
color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color, tab_bar_background, tab_bar_margin_color;
|
||||||
color_type mark1_foreground, mark1_background, mark2_foreground, mark2_background, mark3_foreground, mark3_background;
|
color_type mark1_foreground, mark1_background, mark2_foreground, mark2_background, mark3_foreground, mark3_background;
|
||||||
|
bool bold_is_bright;
|
||||||
monotonic_t repaint_delay, input_delay;
|
monotonic_t repaint_delay, input_delay;
|
||||||
bool focus_follows_mouse;
|
bool focus_follows_mouse;
|
||||||
unsigned int hide_window_decorations;
|
unsigned int hide_window_decorations;
|
||||||
|
|||||||
@ -111,6 +111,10 @@ string_capabilities = {
|
|||||||
'civis': r'\E[?25l',
|
'civis': r'\E[?25l',
|
||||||
# Clear screen
|
# Clear screen
|
||||||
'clear': r'\E[H\E[2J',
|
'clear': r'\E[H\E[2J',
|
||||||
|
# Clear scrollback. This is disabled because the clear program on Linux by default, not as
|
||||||
|
# an option, uses it and nukes the scrollback. What's more this behavior was silently changed
|
||||||
|
# around 2013. Given clear is maintained as part of ncurses this kind of crap is no surprise.
|
||||||
|
# 'E3': r'\E[3J',
|
||||||
# Make cursor appear normal
|
# Make cursor appear normal
|
||||||
'cnorm': r'\E[?12h\E[?25h',
|
'cnorm': r'\E[?12h\E[?25h',
|
||||||
# Carriage return
|
# Carriage return
|
||||||
|
|||||||
@ -49,6 +49,7 @@ from .fast_data_types import (
|
|||||||
CELL_PROGRAM,
|
CELL_PROGRAM,
|
||||||
CELL_SPECIAL_PROGRAM,
|
CELL_SPECIAL_PROGRAM,
|
||||||
CURSOR_BEAM,
|
CURSOR_BEAM,
|
||||||
|
BOLD,
|
||||||
CURSOR_BLOCK,
|
CURSOR_BLOCK,
|
||||||
CURSOR_UNDERLINE,
|
CURSOR_UNDERLINE,
|
||||||
DCS,
|
DCS,
|
||||||
@ -392,6 +393,7 @@ class LoadShaderPrograms:
|
|||||||
STRIKE_SHIFT=STRIKETHROUGH,
|
STRIKE_SHIFT=STRIKETHROUGH,
|
||||||
DIM_SHIFT=DIM,
|
DIM_SHIFT=DIM,
|
||||||
DECORATION_SHIFT=DECORATION,
|
DECORATION_SHIFT=DECORATION,
|
||||||
|
BOLD_SHIFT=BOLD,
|
||||||
MARK_SHIFT=MARK,
|
MARK_SHIFT=MARK,
|
||||||
MARK_MASK=MARK_MASK,
|
MARK_MASK=MARK_MASK,
|
||||||
DECORATION_MASK=DECORATION_MASK,
|
DECORATION_MASK=DECORATION_MASK,
|
||||||
@ -597,6 +599,7 @@ class Window:
|
|||||||
self.default_title = os.path.basename(child.argv[0] or appname)
|
self.default_title = os.path.basename(child.argv[0] or appname)
|
||||||
self.child_title = self.default_title
|
self.child_title = self.default_title
|
||||||
self.title_stack: Deque[str] = deque(maxlen=10)
|
self.title_stack: Deque[str] = deque(maxlen=10)
|
||||||
|
self.user_vars: Dict[str, bytes] = {}
|
||||||
self.id: int = add_window(tab.os_window_id, tab.id, self.title)
|
self.id: int = add_window(tab.os_window_id, tab.id, self.title)
|
||||||
self.clipboard_request_manager = ClipboardRequestManager(self.id)
|
self.clipboard_request_manager = ClipboardRequestManager(self.id)
|
||||||
self.margin = EdgeWidths()
|
self.margin = EdgeWidths()
|
||||||
@ -922,7 +925,27 @@ class Window:
|
|||||||
self.override_title = title or None
|
self.override_title = title or None
|
||||||
self.title_updated()
|
self.title_updated()
|
||||||
|
|
||||||
|
def set_user_var(self, key: str, val: Optional[bytes]) -> None:
|
||||||
|
self.user_vars.pop(key, None) # ensure key will be newest in user_vars even if already present
|
||||||
|
if len(self.user_vars) > 64: # dont store too many user vars
|
||||||
|
oldest_key = next(iter(self.user_vars))
|
||||||
|
self.user_vars.pop(oldest_key)
|
||||||
|
if val is not None:
|
||||||
|
self.user_vars[key] = val
|
||||||
|
|
||||||
|
# screen callbacks {{{
|
||||||
|
|
||||||
|
def osc_1337(self, raw_data: str) -> None:
|
||||||
|
for record in raw_data.split(';'):
|
||||||
|
key, _, val = record.partition('=')
|
||||||
|
if key == 'SetUserVar':
|
||||||
|
from base64 import standard_b64decode
|
||||||
|
ukey, has_equal, uval = val.partition('=')
|
||||||
|
self.set_user_var(ukey, (standard_b64decode(uval) if uval else b'') if has_equal == '=' else None)
|
||||||
|
|
||||||
def desktop_notify(self, osc_code: int, raw_data: str) -> None:
|
def desktop_notify(self, osc_code: int, raw_data: str) -> None:
|
||||||
|
if osc_code == 1337:
|
||||||
|
self.osc_1337(raw_data)
|
||||||
if osc_code == 777:
|
if osc_code == 777:
|
||||||
if not raw_data.startswith('notify;'):
|
if not raw_data.startswith('notify;'):
|
||||||
log_error(f'Ignoring unknown OSC 777: {raw_data}')
|
log_error(f'Ignoring unknown OSC 777: {raw_data}')
|
||||||
@ -932,7 +955,6 @@ class Window:
|
|||||||
if cmd is not None and osc_code == 99:
|
if cmd is not None and osc_code == 99:
|
||||||
self.prev_osc99_cmd = cmd
|
self.prev_osc99_cmd = cmd
|
||||||
|
|
||||||
# screen callbacks {{{
|
|
||||||
def use_utf8(self, on: bool) -> None:
|
def use_utf8(self, on: bool) -> None:
|
||||||
get_boss().child_monitor.set_iutf8_winid(self.id, on)
|
get_boss().child_monitor.set_iutf8_winid(self.id, on)
|
||||||
|
|
||||||
@ -1004,7 +1026,7 @@ class Window:
|
|||||||
'--ssh-connection-data', json.dumps(conn_data)
|
'--ssh-connection-data', json.dumps(conn_data)
|
||||||
)
|
)
|
||||||
|
|
||||||
def send_signal_for_key(self, key_num: int) -> bool:
|
def send_signal_for_key(self, key_num: bytes) -> bool:
|
||||||
try:
|
try:
|
||||||
return self.child.send_signal_for_key(key_num)
|
return self.child.send_signal_for_key(key_num)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 9.6 KiB |
207
logo/kitty.svg
207
logo/kitty.svg
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 12 KiB |
@ -25,7 +25,7 @@ from urllib.parse import urlencode, urlparse
|
|||||||
|
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
docs_dir = os.path.abspath('docs')
|
docs_dir = os.path.abspath('docs')
|
||||||
publish_dir = os.path.abspath(os.path.join('..', 'kovidgoyal.github.io', 'kitty'))
|
publish_dir = os.path.abspath(os.path.join('..', 'kittypatch.github.io', 'kitty'))
|
||||||
building_nightly = False
|
building_nightly = False
|
||||||
with open('kitty/constants.py') as f:
|
with open('kitty/constants.py') as f:
|
||||||
raw = f.read()
|
raw = f.read()
|
||||||
@ -101,7 +101,7 @@ def run_man(args: Any) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def run_html(args: Any) -> None:
|
def run_html(args: Any) -> None:
|
||||||
call('make FAIL_WARN=1 "OPTS=-D analytics_id=G-XTJK3R7GF2" dirhtml', cwd=docs_dir)
|
call('make FAIL_WARN=1 "OPTS=-D analytics_id=G-XXXXXXXXXX" dirhtml', cwd=docs_dir)
|
||||||
add_old_redirects('docs/_build/dirhtml')
|
add_old_redirects('docs/_build/dirhtml')
|
||||||
|
|
||||||
|
|
||||||
@ -154,6 +154,7 @@ def run_website(args: Any) -> None:
|
|||||||
f.write(version)
|
f.write(version)
|
||||||
shutil.copy2(os.path.join(docs_dir, 'installer.sh'), publish_dir)
|
shutil.copy2(os.path.join(docs_dir, 'installer.sh'), publish_dir)
|
||||||
os.chdir(os.path.dirname(publish_dir))
|
os.chdir(os.path.dirname(publish_dir))
|
||||||
|
subprocess.check_call(['optipng', '-o7'] + glob.glob('kitty/_images/social_previews/*.png'))
|
||||||
subprocess.check_call(['git', 'add', 'kitty'])
|
subprocess.check_call(['git', 'add', 'kitty'])
|
||||||
subprocess.check_call(['git', 'commit', '-m', 'kitty website updates'])
|
subprocess.check_call(['git', 'commit', '-m', 'kitty website updates'])
|
||||||
subprocess.check_call(['git', 'push'])
|
subprocess.check_call(['git', 'push'])
|
||||||
@ -492,7 +493,7 @@ def safe_read(path: str) -> str:
|
|||||||
@contextmanager
|
@contextmanager
|
||||||
def change_to_git_master() -> Generator[None, None, None]:
|
def change_to_git_master() -> Generator[None, None, None]:
|
||||||
stash_ref_before = safe_read('.git/refs/stash')
|
stash_ref_before = safe_read('.git/refs/stash')
|
||||||
subprocess.check_call(['git', 'stash'])
|
subprocess.check_call(['git', 'stash', '-u'])
|
||||||
try:
|
try:
|
||||||
branch_before = current_branch()
|
branch_before = current_branch()
|
||||||
if branch_before != 'master':
|
if branch_before != 'master':
|
||||||
@ -547,6 +548,7 @@ def main() -> None:
|
|||||||
with change_to_git_master():
|
with change_to_git_master():
|
||||||
building_nightly = True
|
building_nightly = True
|
||||||
exec_actions(NIGHTLY_ACTIONS, args)
|
exec_actions(NIGHTLY_ACTIONS, args)
|
||||||
|
subprocess.run(['make', 'debug'])
|
||||||
return
|
return
|
||||||
require_git_master()
|
require_git_master()
|
||||||
if args.action == 'all':
|
if args.action == 'all':
|
||||||
|
|||||||
2
setup.py
2
setup.py
@ -816,8 +816,6 @@ def compile_kittens(compilation_database: CompilationDatabase) -> None:
|
|||||||
return kitten, sources, headers, f'kittens/{kitten}/{output}', includes, libraries
|
return kitten, sources, headers, f'kittens/{kitten}/{output}', includes, libraries
|
||||||
|
|
||||||
for kitten, sources, all_headers, dest, includes, libraries in (
|
for kitten, sources, all_headers, dest, includes, libraries in (
|
||||||
files('unicode_input', 'unicode_names'),
|
|
||||||
files('diff', 'diff_speedup'),
|
|
||||||
files('transfer', 'rsync', libraries=('rsync',)),
|
files('transfer', 'rsync', libraries=('rsync',)),
|
||||||
):
|
):
|
||||||
final_env = kenv.copy()
|
final_env = kenv.copy()
|
||||||
|
|||||||
@ -80,7 +80,7 @@ _ksi_prompt=(
|
|||||||
)
|
)
|
||||||
|
|
||||||
_ksi_main() {
|
_ksi_main() {
|
||||||
builtin local ifs="$IFS"
|
builtin local ifs="$IFS" i
|
||||||
IFS=" "
|
IFS=" "
|
||||||
for i in ${KITTY_SHELL_INTEGRATION[@]}; do
|
for i in ${KITTY_SHELL_INTEGRATION[@]}; do
|
||||||
case "$i" in
|
case "$i" in
|
||||||
|
|||||||
@ -24,7 +24,7 @@ exec_kitty() {
|
|||||||
|
|
||||||
|
|
||||||
is_wrapped_kitten() {
|
is_wrapped_kitten() {
|
||||||
wrapped_kittens="clipboard icat hyperlinked_grep ask hints unicode_input ssh themes diff"
|
wrapped_kittens="clipboard icat hyperlinked_grep ask hints unicode_input ssh themes diff show_key"
|
||||||
[ -n "$1" ] && {
|
[ -n "$1" ] && {
|
||||||
case " $wrapped_kittens " in
|
case " $wrapped_kittens " in
|
||||||
*" $1 "*) printf "%s" "$1" ;;
|
*" $1 "*) printf "%s" "$1" ;;
|
||||||
|
|||||||
@ -140,11 +140,12 @@ func (self *Context) Prettify(text string) string {
|
|||||||
return self.ref_hyperlink(val, "envvar-")
|
return self.ref_hyperlink(val, "envvar-")
|
||||||
case "doc":
|
case "doc":
|
||||||
text, target := text_and_target(val)
|
text, target := text_and_target(val)
|
||||||
if text == target {
|
no_title := text == target
|
||||||
target = strings.Trim(target, "/")
|
target = strings.Trim(target, "/")
|
||||||
if title, ok := kitty.DocTitleMap[target]; ok {
|
if title, ok := kitty.DocTitleMap[target]; ok && no_title {
|
||||||
val = title + " <" + target + ">"
|
val = title + " <" + target + ">"
|
||||||
}
|
} else {
|
||||||
|
val = text + " <" + target + ">"
|
||||||
}
|
}
|
||||||
return self.ref_hyperlink(val, "doc-")
|
return self.ref_hyperlink(val, "doc-")
|
||||||
case "iss":
|
case "iss":
|
||||||
|
|||||||
70
tools/cmd/tool/confirm_and_run_shebang.go
Normal file
70
tools/cmd/tool/confirm_and_run_shebang.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package tool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"kitty/kittens/ask"
|
||||||
|
"kitty/tools/cli"
|
||||||
|
"kitty/tools/cli/markup"
|
||||||
|
"kitty/tools/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func ask_for_permission(script_path string) (response string, err error) {
|
||||||
|
opts := &ask.Options{Type: "choices", Default: "n", Choices: []string{"y;green:Yes", "n;red:No", "v;yellow:View", "e;magenta:Edit"}}
|
||||||
|
|
||||||
|
ctx := markup.New(true)
|
||||||
|
opts.Message = ctx.Prettify(fmt.Sprintf(
|
||||||
|
"Attempting to execute the script: :yellow:`%s`\nExecuting untrusted scripts can be dangerous. Proceed anyway?", script_path))
|
||||||
|
response, err = ask.GetChoices(opts)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func confirm_and_run_shebang(args []string) (rc int, err error) {
|
||||||
|
script_path := args[len(args)-1]
|
||||||
|
if unix.Access(script_path, unix.X_OK) != nil {
|
||||||
|
response, err := ask_for_permission(script_path)
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
switch response {
|
||||||
|
default:
|
||||||
|
return 1, fmt.Errorf("Execution of %s was denied by user", script_path)
|
||||||
|
case "v":
|
||||||
|
raw, err := os.ReadFile(script_path)
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
cli.ShowHelpInPager(utils.UnsafeBytesToString(raw))
|
||||||
|
return confirm_and_run_shebang(args)
|
||||||
|
case "e":
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
editor := exec.Command(exe, "edit-in-kitty", script_path)
|
||||||
|
editor.Stdin = os.Stdin
|
||||||
|
editor.Stdout = os.Stdout
|
||||||
|
editor.Stderr = os.Stderr
|
||||||
|
editor.Run()
|
||||||
|
return confirm_and_run_shebang(args)
|
||||||
|
case "y":
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exe := utils.FindExe(args[0])
|
||||||
|
if exe == "" {
|
||||||
|
return 1, fmt.Errorf("Failed to find the script interpreter: %s", args[0])
|
||||||
|
}
|
||||||
|
err = unix.Exec(exe, args, os.Environ())
|
||||||
|
if err != nil {
|
||||||
|
rc = 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"kitty/kittens/hints"
|
"kitty/kittens/hints"
|
||||||
"kitty/kittens/hyperlinked_grep"
|
"kitty/kittens/hyperlinked_grep"
|
||||||
"kitty/kittens/icat"
|
"kitty/kittens/icat"
|
||||||
|
"kitty/kittens/show_key"
|
||||||
"kitty/kittens/ssh"
|
"kitty/kittens/ssh"
|
||||||
"kitty/kittens/themes"
|
"kitty/kittens/themes"
|
||||||
"kitty/kittens/unicode_input"
|
"kitty/kittens/unicode_input"
|
||||||
@ -41,6 +42,8 @@ func KittyToolEntryPoints(root *cli.Command) {
|
|||||||
ssh.EntryPoint(root)
|
ssh.EntryPoint(root)
|
||||||
// unicode_input
|
// unicode_input
|
||||||
unicode_input.EntryPoint(root)
|
unicode_input.EntryPoint(root)
|
||||||
|
// show_key
|
||||||
|
show_key.EntryPoint(root)
|
||||||
// hyperlinked_grep
|
// hyperlinked_grep
|
||||||
hyperlinked_grep.EntryPoint(root)
|
hyperlinked_grep.EntryPoint(root)
|
||||||
// ask
|
// ask
|
||||||
@ -51,6 +54,7 @@ func KittyToolEntryPoints(root *cli.Command) {
|
|||||||
diff.EntryPoint(root)
|
diff.EntryPoint(root)
|
||||||
// themes
|
// themes
|
||||||
themes.EntryPoint(root)
|
themes.EntryPoint(root)
|
||||||
|
themes.ParseEntryPoint(root)
|
||||||
// __pytest__
|
// __pytest__
|
||||||
pytest.EntryPoint(root)
|
pytest.EntryPoint(root)
|
||||||
// __hold_till_enter__
|
// __hold_till_enter__
|
||||||
@ -63,4 +67,13 @@ func KittyToolEntryPoints(root *cli.Command) {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
// __confirm_and_run_shebang__
|
||||||
|
root.AddSubCommand(&cli.Command{
|
||||||
|
Name: "__confirm_and_run_shebang__",
|
||||||
|
Hidden: true,
|
||||||
|
OnlyArgsAllowed: true,
|
||||||
|
Run: func(cmd *cli.Command, args []string) (rc int, err error) {
|
||||||
|
return confirm_and_run_shebang(args)
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -463,7 +463,7 @@ type ThemeMetadata struct {
|
|||||||
Author string `json:"author"`
|
Author string `json:"author"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse_theme_metadata(path string) (*ThemeMetadata, map[string]string, error) {
|
func ParseThemeMetadata(path string) (*ThemeMetadata, map[string]string, error) {
|
||||||
var in_metadata, in_blurb, finished_metadata bool
|
var in_metadata, in_blurb, finished_metadata bool
|
||||||
ans := ThemeMetadata{}
|
ans := ThemeMetadata{}
|
||||||
settings := map[string]string{}
|
settings := map[string]string{}
|
||||||
@ -507,7 +507,9 @@ func parse_theme_metadata(path string) (*ThemeMetadata, map[string]string, error
|
|||||||
val = strings.TrimSpace(val)
|
val = strings.TrimSpace(val)
|
||||||
switch key {
|
switch key {
|
||||||
case "name":
|
case "name":
|
||||||
|
if val != "The name of the theme (if not present, derived from filename)" {
|
||||||
ans.Name = val
|
ans.Name = val
|
||||||
|
}
|
||||||
case "author":
|
case "author":
|
||||||
ans.Author = val
|
ans.Author = val
|
||||||
case "upstream":
|
case "upstream":
|
||||||
@ -536,6 +538,7 @@ type Theme struct {
|
|||||||
settings map[string]string
|
settings map[string]string
|
||||||
zip_reader *zip.File
|
zip_reader *zip.File
|
||||||
is_user_defined bool
|
is_user_defined bool
|
||||||
|
path_for_user_defined_theme string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Theme) Name() string { return self.metadata.Name }
|
func (self *Theme) Name() string { return self.metadata.Name }
|
||||||
@ -558,6 +561,13 @@ func (self *Theme) load_code() (string, error) {
|
|||||||
}
|
}
|
||||||
self.code = utils.UnsafeBytesToString(data)
|
self.code = utils.UnsafeBytesToString(data)
|
||||||
}
|
}
|
||||||
|
if self.is_user_defined && self.path_for_user_defined_theme != "" && self.code == "" {
|
||||||
|
raw, err := os.ReadFile(self.path_for_user_defined_theme)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
self.code = utils.UnsafeBytesToString(raw)
|
||||||
|
}
|
||||||
return self.code, nil
|
return self.code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,7 +784,7 @@ var camel_case_pat = (&utils.Once[*regexp.Regexp]{Run: func() *regexp.Regexp {
|
|||||||
return regexp.MustCompile(`([a-z])([A-Z])`)
|
return regexp.MustCompile(`([a-z])([A-Z])`)
|
||||||
}}).Get
|
}}).Get
|
||||||
|
|
||||||
func theme_name_from_file_name(fname string) string {
|
func ThemeNameFromFileName(fname string) string {
|
||||||
fname = fname[:len(fname)-len(path.Ext(fname))]
|
fname = fname[:len(fname)-len(path.Ext(fname))]
|
||||||
fname = strings.ReplaceAll(fname, "_", " ")
|
fname = strings.ReplaceAll(fname, "_", " ")
|
||||||
fname = camel_case_pat().ReplaceAllString(fname, "$1 $2")
|
fname = camel_case_pat().ReplaceAllString(fname, "$1 $2")
|
||||||
@ -806,14 +816,14 @@ func (self *Themes) Filtered(is_ok func(*Theme) bool) *Themes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Themes) AddFromFile(path string) (*Theme, error) {
|
func (self *Themes) AddFromFile(path string) (*Theme, error) {
|
||||||
m, conf, err := parse_theme_metadata(path)
|
m, conf, err := ParseThemeMetadata(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if m.Name == "" {
|
if m.Name == "" {
|
||||||
m.Name = theme_name_from_file_name(filepath.Base(path))
|
m.Name = ThemeNameFromFileName(filepath.Base(path))
|
||||||
}
|
}
|
||||||
t := Theme{metadata: m, is_user_defined: true, settings: conf}
|
t := Theme{metadata: m, is_user_defined: true, settings: conf, path_for_user_defined_theme: path}
|
||||||
self.name_map[m.Name] = &t
|
self.name_map[m.Name] = &t
|
||||||
return &t, nil
|
return &t, nil
|
||||||
|
|
||||||
@ -829,7 +839,13 @@ func (self *Themes) add_from_dir(dirpath string) error {
|
|||||||
}
|
}
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
if !e.IsDir() && strings.HasSuffix(e.Name(), ".conf") {
|
if !e.IsDir() && strings.HasSuffix(e.Name(), ".conf") {
|
||||||
if _, err = self.AddFromFile(filepath.Join(dirpath, e.Name())); err != nil {
|
path := filepath.Join(dirpath, e.Name())
|
||||||
|
// ignore files if they are the STDOUT of the current processes
|
||||||
|
// allows using kitten theme --dump-theme name > ~/.config/kitty/themes/name.conf
|
||||||
|
if utils.Samefile(path, os.Stdout) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err = self.AddFromFile(path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ func TestThemeCollections(t *testing.T) {
|
|||||||
"mooseCat": "Moose Cat",
|
"mooseCat": "Moose Cat",
|
||||||
"a_bC": "A B C",
|
"a_bC": "A B C",
|
||||||
} {
|
} {
|
||||||
actual := theme_name_from_file_name(fname)
|
actual := ThemeNameFromFileName(fname)
|
||||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||||
t.Fatalf("Unexpected theme name for %s:\n%s", fname, diff)
|
t.Fatalf("Unexpected theme name for %s:\n%s", fname, diff)
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ func TestThemeCollections(t *testing.T) {
|
|||||||
|
|
||||||
pt := func(expected ThemeMetadata, lines ...string) {
|
pt := func(expected ThemeMetadata, lines ...string) {
|
||||||
os.WriteFile(filepath.Join(tdir, "temp.conf"), []byte(strings.Join(lines, "\n")), 0o600)
|
os.WriteFile(filepath.Join(tdir, "temp.conf"), []byte(strings.Join(lines, "\n")), 0o600)
|
||||||
actual, _, err := parse_theme_metadata(filepath.Join(tdir, "temp.conf"))
|
actual, _, err := ParseThemeMetadata(filepath.Join(tdir, "temp.conf"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,8 +66,9 @@ type Loop struct {
|
|||||||
style_ctx style.Context
|
style_ctx style.Context
|
||||||
atomic_update_active bool
|
atomic_update_active bool
|
||||||
|
|
||||||
// Suspend the loop restoring terminal state. Call the return resume function to restore the loop
|
// Suspend the loop restoring terminal state, and run the provided function. When it returns terminal state is
|
||||||
Suspend func() (func() error, error)
|
// put back to what it was before suspending unless the function returns an error or an error occurs saving/restoring state.
|
||||||
|
SuspendAndRun func(func() error) error
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
|
|
||||||
|
|||||||
@ -105,6 +105,9 @@ type KeyEvent struct {
|
|||||||
AlternateKey string
|
AlternateKey string
|
||||||
Text string
|
Text string
|
||||||
Handled bool
|
Handled bool
|
||||||
|
|
||||||
|
// The CSI string this key event was decoded from. Empty if not decoded from CSI.
|
||||||
|
CSI string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *KeyEvent) String() string {
|
func (self *KeyEvent) String() string {
|
||||||
@ -133,6 +136,7 @@ func KeyEventFromCSI(csi string) *KeyEvent {
|
|||||||
if len(csi) == 0 {
|
if len(csi) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
orig_csi := csi
|
||||||
last_char := csi[len(csi)-1:]
|
last_char := csi[len(csi)-1:]
|
||||||
if !strings.Contains("u~ABCDEHFPQRS", last_char) || (last_char == "~" && (csi == "200~" || csi == "201~")) {
|
if !strings.Contains("u~ABCDEHFPQRS", last_char) || (last_char == "~" && (csi == "200~" || csi == "201~")) {
|
||||||
return nil
|
return nil
|
||||||
@ -140,28 +144,32 @@ func KeyEventFromCSI(csi string) *KeyEvent {
|
|||||||
csi = csi[:len(csi)-1]
|
csi = csi[:len(csi)-1]
|
||||||
sections := strings.Split(csi, ";")
|
sections := strings.Split(csi, ";")
|
||||||
|
|
||||||
get_sub_sections := func(section string) []int {
|
get_sub_sections := func(section string, missing int) []int {
|
||||||
p := strings.Split(section, ":")
|
p := strings.Split(section, ":")
|
||||||
ans := make([]int, len(p))
|
ans := make([]int, len(p))
|
||||||
for i, x := range p {
|
for i, x := range p {
|
||||||
|
if x == "" {
|
||||||
|
ans[i] = missing
|
||||||
|
} else {
|
||||||
q, err := strconv.Atoi(x)
|
q, err := strconv.Atoi(x)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ans[i] = q
|
ans[i] = q
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return ans
|
return ans
|
||||||
}
|
}
|
||||||
first_section := get_sub_sections(sections[0])
|
first_section := get_sub_sections(sections[0], 0)
|
||||||
second_section := make([]int, 0)
|
second_section := []int{}
|
||||||
third_section := make([]int, 0)
|
third_section := []int{}
|
||||||
if len(sections) > 1 {
|
if len(sections) > 1 {
|
||||||
second_section = get_sub_sections(sections[1])
|
second_section = get_sub_sections(sections[1], 1)
|
||||||
}
|
}
|
||||||
if len(sections) > 2 {
|
if len(sections) > 2 {
|
||||||
third_section = get_sub_sections(sections[2])
|
third_section = get_sub_sections(sections[2], 0)
|
||||||
}
|
}
|
||||||
var ans = KeyEvent{Type: PRESS}
|
var ans = KeyEvent{Type: PRESS, CSI: orig_csi}
|
||||||
var keynum int
|
var keynum int
|
||||||
if val, ok := letter_trailer_to_csi_number_map[last_char]; ok {
|
if val, ok := letter_trailer_to_csi_number_map[last_char]; ok {
|
||||||
keynum = val
|
keynum = val
|
||||||
|
|||||||
30
tools/tui/loop/key-encoding_test.go
Normal file
30
tools/tui/loop/key-encoding_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package loop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func TestKeyEventFromCSI(t *testing.T) {
|
||||||
|
|
||||||
|
test_text := func(csi string, expected, alternate string) {
|
||||||
|
ev := KeyEventFromCSI(csi)
|
||||||
|
if ev == nil {
|
||||||
|
t.Fatalf("Failed to get parse %#v", csi)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(expected, ev.Text); diff != "" {
|
||||||
|
t.Fatalf("Failed to get text from %#v:\n%s", csi, diff)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(alternate, ev.AlternateKey); diff != "" {
|
||||||
|
t.Fatalf("Failed to get alternate from %#v:\n%s", csi, diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test_text("121;;121u", "y", "")
|
||||||
|
test_text("121::122;;121u", "y", "z")
|
||||||
|
}
|
||||||
@ -271,10 +271,8 @@ func (self *Loop) run() (err error) {
|
|||||||
|
|
||||||
self.keep_going = true
|
self.keep_going = true
|
||||||
self.pending_mouse_events = utils.NewRingBuffer[MouseEvent](4)
|
self.pending_mouse_events = utils.NewRingBuffer[MouseEvent](4)
|
||||||
tty_read_channel := make(chan []byte)
|
|
||||||
tty_write_channel := make(chan *write_msg, 1) // buffered so there is no race between initial queueing and startup of writer thread
|
tty_write_channel := make(chan *write_msg, 1) // buffered so there is no race between initial queueing and startup of writer thread
|
||||||
write_done_channel := make(chan IdType)
|
write_done_channel := make(chan IdType)
|
||||||
tty_reading_done_channel := make(chan byte)
|
|
||||||
self.wakeup_channel = make(chan byte, 256)
|
self.wakeup_channel = make(chan byte, 256)
|
||||||
self.pending_writes = make([]*write_msg, 0, 256)
|
self.pending_writes = make([]*write_msg, 0, 256)
|
||||||
err_channel := make(chan error, 8)
|
err_channel := make(chan error, 8)
|
||||||
@ -286,25 +284,49 @@ func (self *Loop) run() (err error) {
|
|||||||
no_timeout_channel := make(<-chan time.Time)
|
no_timeout_channel := make(<-chan time.Time)
|
||||||
finalizer := ""
|
finalizer := ""
|
||||||
|
|
||||||
w_r, w_w, err := os.Pipe()
|
var r_r, r_w, w_r, w_w *os.File
|
||||||
var r_r, r_w *os.File
|
var tty_reading_done_channel chan byte
|
||||||
if err == nil {
|
var tty_read_channel chan []byte
|
||||||
|
|
||||||
|
start_tty_reader := func() (err error) {
|
||||||
r_r, r_w, err = os.Pipe()
|
r_r, r_w, err = os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w_r.Close()
|
|
||||||
w_w.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
tty_read_channel = make(chan []byte)
|
||||||
|
tty_reading_done_channel = make(chan byte)
|
||||||
|
go read_from_tty(r_r, controlling_term, tty_read_channel, err_channel, tty_reading_done_channel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = start_tty_reader()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
w_r, w_w, err = os.Pipe() // these are closed in the writer thread and the shutdown defer in this thread
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
self.QueueWriteString(self.terminal_options.SetStateEscapeCodes())
|
self.QueueWriteString(self.terminal_options.SetStateEscapeCodes())
|
||||||
needs_reset_escape_codes := true
|
needs_reset_escape_codes := true
|
||||||
|
|
||||||
defer func() {
|
shutdown_tty_reader := func() {
|
||||||
// notify tty reader that we are shutting down
|
// notify tty reader that we are shutting down
|
||||||
|
if r_w != nil {
|
||||||
r_w.Close()
|
r_w.Close()
|
||||||
close(tty_reading_done_channel)
|
close(tty_reading_done_channel)
|
||||||
|
r_w = nil
|
||||||
|
tty_reading_done_channel = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wait_for_tty_reader_to_quit := func() {
|
||||||
|
// wait for tty reader to exit cleanly
|
||||||
|
for range tty_read_channel {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
shutdown_tty_reader()
|
||||||
|
|
||||||
if self.OnFinalize != nil {
|
if self.OnFinalize != nil {
|
||||||
finalizer += self.OnFinalize()
|
finalizer += self.OnFinalize()
|
||||||
@ -318,13 +340,10 @@ func (self *Loop) run() (err error) {
|
|||||||
// flush queued data and wait for it to be written for a timeout, then wait for writer to shutdown
|
// flush queued data and wait for it to be written for a timeout, then wait for writer to shutdown
|
||||||
flush_writer(w_w, tty_write_channel, write_done_channel, self.pending_writes, 2*time.Second)
|
flush_writer(w_w, tty_write_channel, write_done_channel, self.pending_writes, 2*time.Second)
|
||||||
self.pending_writes = nil
|
self.pending_writes = nil
|
||||||
// wait for tty reader to exit cleanly
|
wait_for_tty_reader_to_quit()
|
||||||
for range tty_read_channel {
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go write_to_tty(w_r, controlling_term, tty_write_channel, err_channel, write_done_channel)
|
go write_to_tty(w_r, controlling_term, tty_write_channel, err_channel, write_done_channel)
|
||||||
go read_from_tty(r_r, controlling_term, tty_read_channel, err_channel, tty_reading_done_channel)
|
|
||||||
|
|
||||||
if self.OnInitialize != nil {
|
if self.OnInitialize != nil {
|
||||||
finalizer, err = self.OnInitialize()
|
finalizer, err = self.OnInitialize()
|
||||||
@ -333,27 +352,30 @@ func (self *Loop) run() (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.Suspend = func() (func() error, error) {
|
self.SuspendAndRun = func(run func() error) (err error) {
|
||||||
write_id := self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes())
|
write_id := self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes())
|
||||||
needs_reset_escape_codes = false
|
needs_reset_escape_codes = false
|
||||||
err := self.wait_for_write_to_complete(write_id, tty_write_channel, write_done_channel, 2*time.Second)
|
if err = self.wait_for_write_to_complete(write_id, tty_write_channel, write_done_channel, 2*time.Second); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
shutdown_tty_reader()
|
||||||
|
wait_for_tty_reader_to_quit()
|
||||||
resume, err := controlling_term.Suspend()
|
resume, err := controlling_term.Suspend()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
return func() (err error) {
|
if err = run(); err != nil {
|
||||||
err = resume()
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return
|
if err = start_tty_reader(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = resume(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
write_id = self.QueueWriteString(self.terminal_options.SetStateEscapeCodes())
|
write_id = self.QueueWriteString(self.terminal_options.SetStateEscapeCodes())
|
||||||
needs_reset_escape_codes = true
|
needs_reset_escape_codes = true
|
||||||
return self.wait_for_write_to_complete(write_id, tty_write_channel, write_done_channel, 2*time.Second)
|
return self.wait_for_write_to_complete(write_id, tty_write_channel, write_done_channel, 2*time.Second)
|
||||||
}, nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.on_SIGTSTP = func() error {
|
self.on_SIGTSTP = func() error {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ package tui
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"kitty/tools/utils"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -15,10 +14,16 @@ import (
|
|||||||
|
|
||||||
"github.com/shirou/gopsutil/v3/process"
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"kitty/tools/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
var TmuxExe = (&utils.Once[string]{Run: func() string {
|
||||||
|
return utils.FindExe("tmux")
|
||||||
|
}}).Get
|
||||||
|
|
||||||
func tmux_socket_address() (socket string) {
|
func tmux_socket_address() (socket string) {
|
||||||
socket = os.Getenv("TMUX")
|
socket = os.Getenv("TMUX")
|
||||||
if socket == "" {
|
if socket == "" {
|
||||||
@ -52,14 +57,21 @@ func tmux_socket_address() (socket string) {
|
|||||||
|
|
||||||
var TmuxSocketAddress = (&utils.Once[string]{Run: tmux_socket_address}).Get
|
var TmuxSocketAddress = (&utils.Once[string]{Run: tmux_socket_address}).Get
|
||||||
|
|
||||||
|
func tmux_command(args ...string) (c *exec.Cmd, stderr *strings.Builder) {
|
||||||
|
c = exec.Command(TmuxExe(), args...)
|
||||||
|
stderr = &strings.Builder{}
|
||||||
|
c.Stderr = stderr
|
||||||
|
return c, stderr
|
||||||
|
}
|
||||||
|
|
||||||
func tmux_allow_passthrough() error {
|
func tmux_allow_passthrough() error {
|
||||||
c := exec.Command("tmux", "show", "-Ap", "allow-passthrough")
|
c, stderr := tmux_command("show", "-Ap", "allow-passthrough")
|
||||||
allowed, not_allowed := errors.New("allowed"), errors.New("not allowed")
|
allowed, not_allowed := errors.New("allowed"), errors.New("not allowed")
|
||||||
get_result := make(chan error)
|
get_result := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
output, err := c.Output()
|
output, err := c.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
get_result <- err
|
get_result <- fmt.Errorf("Running %#v failed with error: %w. STDERR: %s", c.Args, err, stderr.String())
|
||||||
} else {
|
} else {
|
||||||
q := strings.TrimSpace(utils.UnsafeBytesToString(output))
|
q := strings.TrimSpace(utils.UnsafeBytesToString(output))
|
||||||
if strings.HasSuffix(q, " on") || strings.HasSuffix(q, " all") {
|
if strings.HasSuffix(q, " on") || strings.HasSuffix(q, " all") {
|
||||||
@ -77,7 +89,12 @@ func tmux_allow_passthrough() error {
|
|||||||
if r != not_allowed {
|
if r != not_allowed {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
return exec.Command("tmux", "set", "-p", "allow-passthrough", "on").Run()
|
c, stderr = tmux_command("set", "-p", "allow-passthrough", "on")
|
||||||
|
err := c.Run()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Running %#v failed with error: %w. STDERR: %s", c.Args, err, stderr.String())
|
||||||
|
}
|
||||||
|
return err
|
||||||
case <-time.After(2 * time.Second):
|
case <-time.After(2 * time.Second):
|
||||||
return fmt.Errorf("Tmux command timed out. This often happens when the version of tmux on your PATH is older than the version of the running tmux server")
|
return fmt.Errorf("Tmux command timed out. This often happens when the version of tmux on your PATH is older than the version of the running tmux server")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
@ -153,3 +154,50 @@ func Memset[T any](dest []T, pattern ...T) []T {
|
|||||||
}
|
}
|
||||||
return dest
|
return dest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type statable interface {
|
||||||
|
Stat() (os.FileInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Samefile(a, b any) bool {
|
||||||
|
var sta, stb os.FileInfo
|
||||||
|
var err error
|
||||||
|
switch v := a.(type) {
|
||||||
|
case string:
|
||||||
|
sta, err = os.Stat(v)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case statable:
|
||||||
|
sta, err = v.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case *os.FileInfo:
|
||||||
|
sta = *v
|
||||||
|
case os.FileInfo:
|
||||||
|
sta = v
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("a must be a string, os.FileInfo or a stat-able object not %T", v))
|
||||||
|
}
|
||||||
|
switch v := b.(type) {
|
||||||
|
case string:
|
||||||
|
stb, err = os.Stat(v)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case statable:
|
||||||
|
stb, err = v.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case *os.FileInfo:
|
||||||
|
stb = *v
|
||||||
|
case os.FileInfo:
|
||||||
|
stb = v
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("b must be a string, os.FileInfo or a stat-able object not %T", v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.SameFile(sta, stb)
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import atexit
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
@ -9,8 +10,6 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import atexit
|
|
||||||
|
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
dmg = sys.argv[-1]
|
dmg = sys.argv[-1]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user