Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73db717a0c | ||
|
|
f1552c8354 | ||
|
|
fb87fc32f0 | ||
|
|
b04b0c670d | ||
|
|
b33845df8a | ||
|
|
12c9d8d8f8 | ||
|
|
fe97bbcbbf | ||
|
|
10533c3eba | ||
|
|
b4415c90f9 | ||
|
|
2970bbdf6f | ||
|
|
95a420534d | ||
|
|
be56a21574 | ||
|
|
3741a235fe | ||
|
|
523b6c4c2d | ||
|
|
c3b2300310 | ||
|
|
1c4a20d86f | ||
|
|
f5af475012 | ||
|
|
f149b74332 | ||
|
|
8cd51386cb | ||
|
|
fb2cca88df | ||
|
|
031d4dc85a | ||
|
|
bf66d1c0f5 | ||
|
|
0edfa88755 | ||
|
|
dba1f3bcbc | ||
|
|
e204cc8f12 |
2
.github/workflows/ci.py
vendored
2
.github/workflows/ci.py
vendored
@@ -40,7 +40,7 @@ def install_deps():
|
|||||||
if is_bundle:
|
if is_bundle:
|
||||||
install_bundle()
|
install_bundle()
|
||||||
else:
|
else:
|
||||||
run('pip install Pillow pygments')
|
run('pip3 install Pillow pygments')
|
||||||
|
|
||||||
|
|
||||||
def build_kitty():
|
def build_kitty():
|
||||||
|
|||||||
@@ -4,6 +4,18 @@ Changelog
|
|||||||
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||||
To update |kitty|, :doc:`follow the instructions <binary>`.
|
To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||||
|
|
||||||
|
0.19.1 [2020-10-06]
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- hints kitten: Add an ``ip`` type for easy selection of IP addresses
|
||||||
|
(:pull:`3009`)
|
||||||
|
|
||||||
|
- Fix a regression that caused a segfault when using
|
||||||
|
:opt:`scrollback_pager_history_size` and it needs to be expanded (:iss:`3011`)
|
||||||
|
|
||||||
|
- Fix update available notifications repeating (:pull:`3006`)
|
||||||
|
|
||||||
|
|
||||||
0.19.0 [2020-10-04]
|
0.19.0 [2020-10-04]
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ you use some editor other than vim, you should adjust the
|
|||||||
|
|
||||||
Finally, add an alias to your shell's rc files to invoke the kitten as ``hg``::
|
Finally, add an alias to your shell's rc files to invoke the kitten as ``hg``::
|
||||||
|
|
||||||
alias hg='kitty +kitten hyperlink_grep'
|
alias hg='kitty +kitten hyperlinked_grep'
|
||||||
|
|
||||||
|
|
||||||
You can now run searches with::
|
You can now run searches with::
|
||||||
@@ -42,10 +42,17 @@ You can now run searches with::
|
|||||||
hg some-search-term
|
hg some-search-term
|
||||||
|
|
||||||
If you want to enable completion, for the kitten, you can delegate completion
|
If you want to enable completion, for the kitten, you can delegate completion
|
||||||
to rg. For ZSH, you do that with::
|
to rg. For that, instead of using an alias create a simple wrapper script named
|
||||||
|
:file:`hg` somewhere in your ``PATH``:
|
||||||
|
|
||||||
compdef _rg kitty +kitten hyperlinked_grep
|
.. code-block:: sh
|
||||||
|
|
||||||
|
#!/bin/sh
|
||||||
|
exec kitty +kitten hyperlinked_grep "$@"
|
||||||
|
|
||||||
|
Then, for example, for ZSH, add the following to :file:`.zshrc`::
|
||||||
|
|
||||||
|
compdef _rg hg
|
||||||
|
|
||||||
To learn more about kitty's powerful framework for customizing URL click
|
To learn more about kitty's powerful framework for customizing URL click
|
||||||
actions, :doc:`see here <../open_actions>`.
|
actions, :doc:`see here <../open_actions>`.
|
||||||
|
|||||||
@@ -250,12 +250,13 @@ The escape code has the form::
|
|||||||
|
|
||||||
<OSC> 99 ; metadata ; payload <terminator>
|
<OSC> 99 ; metadata ; payload <terminator>
|
||||||
|
|
||||||
Here ``<OSC>`` is :code:`<ESC>]` and ``<terminator>`` is :code:`<ESC><backslash>`. The
|
Here ``<OSC>`` is :code:`<ESC>]` and ``<terminator>`` is
|
||||||
metadata is a section of colon separated :code:`key=value` pairs. Every key
|
:code:`<ESC><backslash>`. The metadata is a section of colon separated
|
||||||
must be a single character from the set :code:`[a-zA-Z]` and every value must
|
:code:`key=value` pairs. Every key must be a single character from the set
|
||||||
be a character from the set :code:`a-zA-Z0-9-_/\+.,(){}[]*&^%$#@!`~`. The
|
:code:`a-zA-Z` and every value must be a word consisting of characters from
|
||||||
payload must be interpreted based on the metadata section. The two semi-colons
|
the set :code:`a-zA-Z0-9-_/\+.,(){}[]*&^%$#@!`~`. The payload must be
|
||||||
*must* always be present even when no metadata is present.
|
interpreted based on the metadata section. The two semi-colons *must* always be
|
||||||
|
present even when no metadata is present.
|
||||||
|
|
||||||
Before going into details, lets see how one can display a simple, single line
|
Before going into details, lets see how one can display a simple, single line
|
||||||
notification from a shell script::
|
notification from a shell script::
|
||||||
@@ -297,7 +298,7 @@ was activated. This is controlled by the ``a`` key which takes a comma
|
|||||||
separated set of values, ``report`` and ``focus``. The value ``focus`` means
|
separated set of values, ``report`` and ``focus``. The value ``focus`` means
|
||||||
focus the window from which the notification was issued and is the default.
|
focus the window from which the notification was issued and is the default.
|
||||||
``report`` means send an escape code back to the application. The format of the
|
``report`` means send an escape code back to the application. The format of the
|
||||||
escape code is::
|
returned escape code is::
|
||||||
|
|
||||||
<OSC> 99 ; i=identifier ; <terminator>
|
<OSC> 99 ; i=identifier ; <terminator>
|
||||||
|
|
||||||
@@ -322,7 +323,7 @@ to display it based on what it does understand.
|
|||||||
allow displaying custom icons on a notification, at all, it was decided to
|
allow displaying custom icons on a notification, at all, it was decided to
|
||||||
leave it out of the spec for the time being.
|
leave it out of the spec for the time being.
|
||||||
|
|
||||||
Similarly, features such a scheduled notifications could be added in future
|
Similarly, features such as scheduled notifications could be added in future
|
||||||
revisions.
|
revisions.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -529,7 +529,6 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
|
|||||||
*width = (int)frameRect.size.width;
|
*width = (int)frameRect.size.width;
|
||||||
if (height)
|
if (height)
|
||||||
*height = (int)frameRect.size.height;
|
*height = (int)frameRect.size.height;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
|
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
|
||||||
|
|||||||
@@ -1595,10 +1595,10 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
|||||||
|
|
||||||
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
|
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
|
||||||
{
|
{
|
||||||
if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE)
|
if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
|
||||||
[window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)];
|
|
||||||
else
|
|
||||||
[window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)];
|
[window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)];
|
||||||
|
else
|
||||||
|
[window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr, int heightincr)
|
void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr, int heightincr)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
See https://sw.kovidgoyal.net/kitty/graphics-protocol.html
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
See https://sw.kovidgoyal.net/kitty/key-encoding.html
|
|
||||||
@@ -236,6 +236,11 @@ def postprocessor(func: PostprocessorFunc) -> PostprocessorFunc:
|
|||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidMatch(Exception):
|
||||||
|
"""Raised when a match turns out to be invalid."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@postprocessor
|
@postprocessor
|
||||||
def url(text: str, s: int, e: int) -> Tuple[int, int]:
|
def url(text: str, s: int, e: int) -> Tuple[int, int]:
|
||||||
if s > 4 and text[s - 5:s] == 'link:': # asciidoc URLs
|
if s > 4 and text[s - 5:s] == 'link:': # asciidoc URLs
|
||||||
@@ -280,11 +285,29 @@ def quotes(text: str, s: int, e: int) -> Tuple[int, int]:
|
|||||||
return s, e
|
return s, e
|
||||||
|
|
||||||
|
|
||||||
|
@postprocessor
|
||||||
|
def ip(text: str, s: int, e: int) -> Tuple[int, int]:
|
||||||
|
from ipaddress import ip_address
|
||||||
|
# Check validity of IPs (or raise InvalidMatch)
|
||||||
|
ip = text[s:e]
|
||||||
|
|
||||||
|
try:
|
||||||
|
ip_address(ip)
|
||||||
|
except Exception:
|
||||||
|
raise InvalidMatch("Invalid IP")
|
||||||
|
|
||||||
|
return s, e
|
||||||
|
|
||||||
|
|
||||||
def mark(pattern: str, post_processors: Iterable[PostprocessorFunc], text: str, args: HintsCLIOptions) -> Generator[Mark, None, None]:
|
def mark(pattern: str, post_processors: Iterable[PostprocessorFunc], text: str, args: HintsCLIOptions) -> Generator[Mark, None, None]:
|
||||||
pat = re.compile(pattern)
|
pat = re.compile(pattern)
|
||||||
for idx, (s, e, groupdict) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
|
for idx, (s, e, groupdict) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
|
||||||
for func in post_processors:
|
try:
|
||||||
s, e = func(text, s, e)
|
for func in post_processors:
|
||||||
|
s, e = func(text, s, e)
|
||||||
|
except InvalidMatch:
|
||||||
|
continue
|
||||||
|
|
||||||
mark_text = text[s:e].replace('\n', '').replace('\0', '')
|
mark_text = text[s:e].replace('\n', '').replace('\0', '')
|
||||||
yield Mark(idx, s, e, mark_text, groupdict)
|
yield Mark(idx, s, e, mark_text, groupdict)
|
||||||
|
|
||||||
@@ -325,6 +348,15 @@ def functions_for(args: HintsCLIOptions) -> Tuple[str, List[PostprocessorFunc]]:
|
|||||||
pattern = '(?m)^\\s*(.+)[\\s\0]*$'
|
pattern = '(?m)^\\s*(.+)[\\s\0]*$'
|
||||||
elif args.type == 'hash':
|
elif args.type == 'hash':
|
||||||
pattern = '[0-9a-f]{7,128}'
|
pattern = '[0-9a-f]{7,128}'
|
||||||
|
elif args.type == 'ip':
|
||||||
|
pattern = (
|
||||||
|
# # IPv4 with no validation
|
||||||
|
r"((?:\d{1,3}\.){3}\d{1,3}"
|
||||||
|
r"|"
|
||||||
|
# # IPv6 with no validation
|
||||||
|
r"(?:[a-fA-F0-9]{0,4}:){2,7}[a-fA-F0-9]{1,4})"
|
||||||
|
)
|
||||||
|
post_processors.append(ip)
|
||||||
elif args.type == 'word':
|
elif args.type == 'word':
|
||||||
chars = args.word_characters
|
chars = args.word_characters
|
||||||
if chars is None:
|
if chars is None:
|
||||||
@@ -402,7 +434,7 @@ def process_hyperlinks(text: str) -> Tuple[str, Tuple[Mark, ...]]:
|
|||||||
active_hyperlink_start_offset = 0
|
active_hyperlink_start_offset = 0
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|
||||||
def process_hyperlink(m: re.Match) -> str:
|
def process_hyperlink(m: 're.Match') -> str:
|
||||||
nonlocal removed_size, active_hyperlink_url, active_hyperlink_id, active_hyperlink_start_offset
|
nonlocal removed_size, active_hyperlink_url, active_hyperlink_id, active_hyperlink_start_offset
|
||||||
raw = m.group()
|
raw = m.group()
|
||||||
start = m.start() - removed_size
|
start = m.start() - removed_size
|
||||||
@@ -482,7 +514,7 @@ programs.
|
|||||||
|
|
||||||
--type
|
--type
|
||||||
default=url
|
default=url
|
||||||
choices=url,regex,path,line,hash,word,linenum,hyperlink
|
choices=url,regex,path,line,hash,word,linenum,hyperlink,ip
|
||||||
The type of text to search for. A value of :code:`linenum` is special, it looks
|
The type of text to search for. A value of :code:`linenum` is special, it looks
|
||||||
for error messages using the pattern specified with :option:`--regex`, which
|
for error messages using the pattern specified with :option:`--regex`, which
|
||||||
must have the named groups, :code:`path` and :code:`line`. If not specified,
|
must have the named groups, :code:`path` and :code:`line`. If not specified,
|
||||||
|
|||||||
@@ -1563,6 +1563,7 @@ class Boss:
|
|||||||
for os_window_id in os_windows:
|
for os_window_id in os_windows:
|
||||||
self.default_bg_changed_for(os_window_id)
|
self.default_bg_changed_for(os_window_id)
|
||||||
|
|
||||||
|
# Can be called with kitty -o "map f1 send_test_notification"
|
||||||
def send_test_notification(self) -> None:
|
def send_test_notification(self) -> None:
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Version(NamedTuple):
|
|||||||
|
|
||||||
|
|
||||||
appname: str = 'kitty'
|
appname: str = 'kitty'
|
||||||
version: Version = Version(0, 19, 0)
|
version: Version = Version(0, 19, 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
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ free_pagerhist(HistoryBuf *self) {
|
|||||||
static inline bool
|
static inline bool
|
||||||
pagerhist_extend(PagerHistoryBuf *ph, size_t minsz) {
|
pagerhist_extend(PagerHistoryBuf *ph, size_t minsz) {
|
||||||
if (ph->buffer_size >= ph->max_sz) return false;
|
if (ph->buffer_size >= ph->max_sz) return false;
|
||||||
size_t newsz = ph->buffer_size + MAX(1024u * 1024u, minsz);
|
size_t newsz = MIN(ph->max_sz, ph->buffer_size + MAX(1024u * 1024u, minsz));
|
||||||
uint8_t *newbuf = PyMem_Malloc(MIN(ph->buffer_size + minsz, ph->max_sz));
|
uint8_t *newbuf = PyMem_Malloc(newsz);
|
||||||
if (!newbuf) return false;
|
if (!newbuf) return false;
|
||||||
size_t copied = MIN(ph->length, ph->buffer_size - ph->start);
|
size_t copied = MIN(ph->length, ph->buffer_size - ph->start);
|
||||||
if (copied) memcpy(newbuf, ph->buffer + ph->start, copied);
|
if (copied) memcpy(newbuf, ph->buffer + ph->start, copied);
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ def save_notification(version: Version) -> None:
|
|||||||
for version in sorted(notified_versions):
|
for version in sorted(notified_versions):
|
||||||
n = notified_versions[version]
|
n = notified_versions[version]
|
||||||
lines.append('{},{},{}'.format(
|
lines.append('{},{},{}'.format(
|
||||||
'.'.join(map(str, n.version)), n.time_of_last_notification, n.count))
|
'.'.join(map(str, n.version)),
|
||||||
|
n.time_of_last_notification,
|
||||||
|
n.notification_count))
|
||||||
atomic_save('\n'.join(lines).encode('utf-8'), version_notification_log())
|
atomic_save('\n'.join(lines).encode('utf-8'), version_notification_log())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,3 +30,31 @@ class TestHints(BaseTest):
|
|||||||
t('link:{}[xxx]'.format(u), u)
|
t('link:{}[xxx]'.format(u), u)
|
||||||
t('`xyz <{}>`_.'.format(u), u)
|
t('`xyz <{}>`_.'.format(u), u)
|
||||||
t('<a href="{}">moo'.format(u), u)
|
t('<a href="{}">moo'.format(u), u)
|
||||||
|
|
||||||
|
def test_ip_hints(self):
|
||||||
|
from kittens.hints.main import parse_hints_args, functions_for, mark, convert_text
|
||||||
|
args = parse_hints_args(['--type', 'ip'])[0]
|
||||||
|
pattern, post_processors = functions_for(args)
|
||||||
|
|
||||||
|
def create_marks(text, cols=60):
|
||||||
|
text = convert_text(text, cols)
|
||||||
|
return tuple(mark(pattern, post_processors, text, args))
|
||||||
|
|
||||||
|
testcases = (
|
||||||
|
('100.64.0.0', ['100.64.0.0']),
|
||||||
|
('2001:0db8:0000:0000:0000:ff00:0042:8329', ['2001:0db8:0000:0000:0000:ff00:0042:8329']),
|
||||||
|
('2001:db8:0:0:0:ff00:42:8329', ['2001:db8:0:0:0:ff00:42:8329']),
|
||||||
|
('2001:db8::ff00:42:8329', ['2001:db8::ff00:42:8329']),
|
||||||
|
('2001:DB8::FF00:42:8329', ['2001:DB8::FF00:42:8329']),
|
||||||
|
('0000:0000:0000:0000:0000:0000:0000:0001', ['0000:0000:0000:0000:0000:0000:0000:0001']),
|
||||||
|
('::1', ['::1']),
|
||||||
|
# Invalid IPs won't match
|
||||||
|
('255.255.255.256', []),
|
||||||
|
(':1', []),
|
||||||
|
)
|
||||||
|
|
||||||
|
for testcase, expected in testcases:
|
||||||
|
with self.subTest(testcase=testcase, expected=expected):
|
||||||
|
marks = create_marks(testcase)
|
||||||
|
ips = [m.text for m in marks]
|
||||||
|
self.ae(ips, expected)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
See https://sw.kovidgoyal.net/kitty/protocol-extensions.html
|
|
||||||
19
publish.py
19
publish.py
@@ -68,14 +68,15 @@ def run_html(args: Any) -> None:
|
|||||||
|
|
||||||
def add_analytics() -> None:
|
def add_analytics() -> None:
|
||||||
analytics = '''
|
analytics = '''
|
||||||
<!-- Google Analytics -->
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20736318-2"></script>
|
||||||
<script>
|
<script>
|
||||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
window.dataLayer = window.dataLayer || [];
|
||||||
ga('create', 'UA-20736318-2', 'auto');
|
function gtag(){dataLayer.push(arguments);}
|
||||||
ga('send', 'pageview');
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'UA-20736318-2');
|
||||||
</script>
|
</script>
|
||||||
<script async="async" src='https://www.google-analytics.com/analytics.js'></script>
|
|
||||||
<!-- End Google Analytics -->\
|
|
||||||
'''
|
'''
|
||||||
for dirpath, firnames, filenames in os.walk(publish_dir):
|
for dirpath, firnames, filenames in os.walk(publish_dir):
|
||||||
for fname in filenames:
|
for fname in filenames:
|
||||||
@@ -87,6 +88,10 @@ ga('send', 'pageview');
|
|||||||
f.write(html.encode('utf-8'))
|
f.write(html.encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
|
def run_docs(args: Any) -> None:
|
||||||
|
subprocess.check_call(['make', 'docs'])
|
||||||
|
|
||||||
|
|
||||||
def run_website(args: Any) -> None:
|
def run_website(args: Any) -> None:
|
||||||
if os.path.exists(publish_dir):
|
if os.path.exists(publish_dir):
|
||||||
shutil.rmtree(publish_dir)
|
shutil.rmtree(publish_dir)
|
||||||
@@ -384,6 +389,8 @@ def main() -> None:
|
|||||||
ans = 'n'
|
ans = 'n'
|
||||||
if ans.lower() != 'y':
|
if ans.lower() != 'y':
|
||||||
return
|
return
|
||||||
|
if actions == ['website']:
|
||||||
|
actions.insert(0, 'docs')
|
||||||
for action in actions:
|
for action in actions:
|
||||||
print('Running', action)
|
print('Running', action)
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
See https://sw.kovidgoyal.net/kitty/remote-control.html
|
|
||||||
2
setup.py
2
setup.py
@@ -1176,8 +1176,6 @@ def option_parser() -> argparse.ArgumentParser: # {{{
|
|||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
global verbose
|
global verbose
|
||||||
if sys.version_info < (3, 5):
|
|
||||||
raise SystemExit('python >= 3.5 required')
|
|
||||||
args = option_parser().parse_args(namespace=Options())
|
args = option_parser().parse_args(namespace=Options())
|
||||||
verbose = args.verbose > 0
|
verbose = args.verbose > 0
|
||||||
args.prefix = os.path.abspath(args.prefix)
|
args.prefix = os.path.abspath(args.prefix)
|
||||||
|
|||||||
Reference in New Issue
Block a user