Compare commits

...

25 Commits

Author SHA1 Message Date
Kovid Goyal
73db717a0c version 0.19.1 2020-10-06 14:03:25 +05:30
Kovid Goyal
f1552c8354 Avoid alias related complications with completion delegation for hyperlinked_grep 2020-10-06 13:53:41 +05:30
Kovid Goyal
fb87fc32f0 Fix a regression that caused a segfault when using scrollback_pager_history_size
Fixes #3011
2020-10-06 13:28:48 +05:30
Kovid Goyal
b04b0c670d Fix #3010
Probably ancient version of Python
2020-10-06 12:09:47 +05:30
Kovid Goyal
b33845df8a Update Changelog 2020-10-06 06:50:29 +05:30
Kovid Goyal
12c9d8d8f8 Delay load the ipaddress module 2020-10-06 06:50:17 +05:30
Kovid Goyal
fe97bbcbbf Merge branch 'master' of https://github.com/Sighery/kitty into master 2020-10-06 06:45:05 +05:30
Sighery
10533c3eba Hints kitten: validate IPs with ipaddress
On the initial commit of this feature, IPs were just matched with a
very simple regex that prioritised simplicity/readability over
accuracy.

This commit adds a postprocessor for ip matches that makes use of
Python's `ipaddress` in the standard library to validate all the IP
matches.

This way we don't need huge and complex regex patterns to match _and_
validate the IPs, and we can just use `ipaddress` to abstract us from
implementing all the validation logic into the regex pattern.
2020-10-05 20:11:25 +02:00
Sighery
b4415c90f9 Hints kitten: add support for IPs (v4 and v6)
It selects v4 and v6 IPs with a simple regex that doesn't actually
check for the validity of the IPs.
2020-10-05 20:11:22 +02:00
Kovid Goyal
2970bbdf6f Update GA tags
Also auto gen docs if running publish only for website
2020-10-05 20:04:32 +05:30
Kovid Goyal
95a420534d Remove the old asciidoc files
By now hopefully the kitty website is well established
2020-10-05 19:34:02 +05:30
Kovid Goyal
be56a21574 ... 2020-10-05 19:33:09 +05:30
Kovid Goyal
3741a235fe Merge branch 'patch-1' of https://github.com/Nudin/kitty into master 2020-10-05 19:28:22 +05:30
Michael F. Schönitzer
523b6c4c2d Fix wrong kittenname in documentation
It's called `hyperlinked_grep` not `hyperlink_grep`.
2020-10-05 15:54:11 +02:00
Kovid Goyal
c3b2300310 Merge branch 'remove-outdated-check' of https://github.com/Luflosi/kitty into master 2020-10-05 18:16:06 +05:30
Luflosi
1c4a20d86f Remove outdated Python version check
The Python version is already checked at the top of the file. That check was added in 81a58186c6.
2020-10-05 14:41:07 +02:00
Kovid Goyal
f5af475012 Merge branch 'notification_fix' of https://github.com/nertpinx/kitty into master 2020-10-05 18:09:35 +05:30
Martin Kletzander
f149b74332 Fix repeated version notifications
Without this fix the file with the list of notifications looks like this:
```
0.19.0,1601899026.4373078,<built-in method count of Notification object at 0x7f8c582193b0>
```
which ends up notifying about a new version all the time.
2020-10-05 14:27:47 +02:00
Kovid Goyal
8cd51386cb Fix some typos in the OSC 99 spec 2020-10-05 17:27:41 +05:30
Kovid Goyal
fb2cca88df Merge branch 'pip3-in-ci' of https://github.com/Luflosi/kitty into master 2020-10-04 21:23:29 +05:30
Luflosi
031d4dc85a Use pip3 instead of pip for CI
There was a warning when the CI executed the "macOS Brew" job:
```
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
```
To fix this, use pip3 instead of pip.
2020-10-04 17:47:41 +02:00
Kovid Goyal
bf66d1c0f5 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty into master 2020-10-04 21:13:48 +05:30
Luflosi
0edfa88755 Reduce the difference of GLFW to upstream 2020-10-04 17:34:51 +02:00
Kovid Goyal
dba1f3bcbc Merge branch 'add-comment' of https://github.com/Luflosi/kitty into master 2020-10-04 21:01:12 +05:30
Luflosi
e204cc8f12 Add comment on how to call send_test_notification() 2020-10-04 17:23:50 +02:00
18 changed files with 119 additions and 36 deletions

View File

@@ -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():

View File

@@ -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]
------------------- -------------------

View File

@@ -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>`.

View File

@@ -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.

View File

@@ -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)

View File

@@ -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)

View File

@@ -1 +0,0 @@
See https://sw.kovidgoyal.net/kitty/graphics-protocol.html

View File

@@ -1 +0,0 @@
See https://sw.kovidgoyal.net/kitty/key-encoding.html

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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())

View File

@@ -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)

View File

@@ -1 +0,0 @@
See https://sw.kovidgoyal.net/kitty/protocol-extensions.html

View File

@@ -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()

View File

@@ -1 +0,0 @@
See https://sw.kovidgoyal.net/kitty/remote-control.html

View File

@@ -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)