diff --git a/.appveyor.yml b/.appveyor.yml index d36227e29..55d07c22e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,4 +1,4 @@ -version: 0.9.14.{build} +version: 0.9.15.{build} os: Windows Server 2012 R2 environment: diff --git a/.authors.yml b/.authors.yml index d0afb6132..a52cc8667 100644 --- a/.authors.yml +++ b/.authors.yml @@ -68,7 +68,7 @@ github: gforsyth - name: Morten Enemark Lund email: melund@gmail.com - num_commits: 486 + num_commits: 490 first_commit: 2015-07-10 07:54:10 github: melund - name: Ned Letcher @@ -384,7 +384,7 @@ email: lalochcz@gmail.com aliases: - laloch - num_commits: 43 + num_commits: 47 first_commit: 2018-07-26 13:51:50 github: laloch - name: Nico Lehmann @@ -434,7 +434,7 @@ github: funkyfuture - name: Anthony Scopatz email: scopatz@gmail.com - num_commits: 2587 + num_commits: 2593 first_commit: 2015-01-21 17:04:13 github: scopatz - name: anatoly techtonik @@ -1016,7 +1016,7 @@ first_commit: 2018-09-16 22:50:24 - name: Gyuri Horak email: dyuri@horak.hu - num_commits: 1 + num_commits: 2 first_commit: 2019-06-18 12:00:16 github: dyuri - name: Ke Zhang @@ -1092,12 +1092,14 @@ email: drmikecrowe@gmail.com aliases: - drmikecrowe - num_commits: 1 + num_commits: 6 first_commit: 2020-01-28 09:44:58 github: drmikecrowe - name: anki-code email: anki-code@users.noreply.github.com - num_commits: 14 + alternate_emails: + - anki-code + num_commits: 21 first_commit: 2019-10-15 18:20:58 - name: Sylvain Corlay email: sylvain.corlay@gmail.com @@ -1118,3 +1120,26 @@ num_commits: 1 first_commit: 2020-02-05 13:38:52 github: marciomazza +- name: Noortheen Raja + email: jnoortheen@gmail.com + num_commits: 2 + first_commit: 2020-03-15 10:13:56 + github: jnoortheen +- name: Samuel Lotz + email: samuel.lotz@salotz.info + num_commits: 3 + first_commit: 2020-03-03 11:30:33 + github: salotz +- name: Jerzy Drozdz + email: jerzy.drozdz@gmail.com + num_commits: 1 + first_commit: 2020-03-01 11:56:23 +- name: Gabriel Vogel + email: gabriel.vogel@online.de + num_commits: 2 + first_commit: 2020-03-10 19:06:59 + github: Gobbel2000 +- name: anki + email: anki@code.email + num_commits: 2 + first_commit: 2020-03-02 04:59:17 diff --git a/.mailmap b/.mailmap index 309f002de..8af097c83 100644 --- a/.mailmap +++ b/.mailmap @@ -27,8 +27,8 @@ Jean-Benoist Leger Jean-Benoist Leger Jean-Benoist Leger Jean-Benoist Leger christopher Klaus Alexander Seistrup Klaus Alexander Seistrup -Leonardo Santagada David Strobach laloch +Leonardo Santagada Burak Yiğit Kaya Burak Yigit Kaya Aaron Griffin Rob Brewer Robert W. Brewer @@ -40,6 +40,7 @@ Guillaume Leclerc Guillaume Leclerc Travis Shirk Joel Gerber +anki-code anki-code vaaaaanquish <6syun9@gmail.com> @vaaaaanquish <6syun9@gmail.com> vaaaaanquish <6syun9@gmail.com> vaaaaanquish <6syun9@gmail.com> vaaaaanquish <6syun9@gmail.com> 6syun9 <6syun9@gmail.com> @@ -53,7 +54,6 @@ Carmen Bianca Bakker Frank Sachsenheim Frank Sachsenheim Kurtis Rader cryzed -anki-code Brian Visel Andrew Hundt Jonathan Slenders @@ -82,6 +82,7 @@ Raphael Gaschignard Nathan Goldbaum Nathan Goldbaum mel Jared Crawford +Mike Crowe drmikecrowe JuanPablo Ollie Terrance Marcel Bollmann @@ -139,6 +140,7 @@ Niklas Hambüchen Sébastien Pierre shadow-light <42055707+shadow-light@users.noreply.github.com> Jan Chren +Samuel Lotz Mark Wiebe Nathan Hoad Eric Dill @@ -161,11 +163,15 @@ Owen Campbell Steven Kryskalla cclauss Eddie Peters +Gyuri Horak Ke Zhang ke-zhang-rd László Vaskó Allan Crooks micimize Edmund Miller +Noortheen Raja +Gabriel Vogel +anki Dan Allan Ned Letcher Zach Crownover @@ -222,16 +228,15 @@ stonebig Ronny Pfannschmidt Troy de Freitas <9503857+ntdef@users.noreply.github.com> Rodrigo Oliveira -Gyuri Horak Daniel Smith Nils ANDRÉ-CHANG chengxuncc nedsociety fanosta David Kalliecharan -Mike Crowe drmikecrowe Sylvain Corlay Chris Lasher Marcio Mazza +Jerzy Drozdz goodboy Atsushi Morimoto diff --git a/AUTHORS.rst b/AUTHORS.rst index 6840b589b..03c388dcb 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -13,8 +13,8 @@ Authors are sorted by number of commits. * Jean-Benoist Leger * christopher * Klaus Alexander Seistrup -* Leonardo Santagada * David Strobach +* Leonardo Santagada * Burak Yiğit Kaya * Aaron Griffin * Rob Brewer @@ -26,6 +26,7 @@ Authors are sorted by number of commits. * Gordon Ball * Travis Shirk * Joel Gerber +* anki-code * vaaaaanquish * Bernardas Ališauskas * Derek Thomas @@ -37,7 +38,6 @@ Authors are sorted by number of commits. * Frank Sachsenheim * Kurtis Rader * cryzed -* anki-code * Brian Visel * Andrew Hundt * Jonathan Slenders @@ -66,6 +66,7 @@ Authors are sorted by number of commits. * Nathan Goldbaum * mel * Jared Crawford +* Mike Crowe * JuanPablo * Ollie Terrance * Marcel Bollmann @@ -123,6 +124,7 @@ Authors are sorted by number of commits. * Sébastien Pierre * shadow-light * Jan Chren +* Samuel Lotz * Mark Wiebe * Nathan Hoad * Eric Dill @@ -145,11 +147,15 @@ Authors are sorted by number of commits. * Steven Kryskalla * cclauss * Eddie Peters +* Gyuri Horak * Ke Zhang * László Vaskó * Allan Crooks * micimize * Edmund Miller +* Noortheen Raja +* Gabriel Vogel +* anki * Dan Allan * Ned Letcher * Zach Crownover @@ -206,16 +212,15 @@ Authors are sorted by number of commits. * Ronny Pfannschmidt * Troy de Freitas * Rodrigo Oliveira -* Gyuri Horak * Daniel Smith * Nils ANDRÉ-CHANG * chengxuncc * nedsociety * fanosta * David Kalliecharan -* Mike Crowe * Sylvain Corlay * Chris Lasher * Marcio Mazza +* Jerzy Drozdz * goodboy * Atsushi Morimoto diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d238fa267..147dd48b9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,51 @@ Xonsh Change Log .. current developments +v0.9.15 +==================== + +**Added:** + +* Adds documentation for how to setup an emacs editing mode for xonsh. +* New ``$XONSH_TRACE_SUBPROC`` environment variable. +* Added ``-l``, ``-c`` and ``-a`` options to ``xexec``, works now like ``exec`` + in bash/zsh +* **$HISTCONTROL** - *errordups* support for history-sqlite backend + +**Changed:** + +* ``-l`` switch works like bash, loads environment in non-interactive shell +* The xonsh pytest plugin no longer messes up the test order for pytest. Xsh test + are still executed first to avoid a bug were other tests would prevent ``test_*.xsh`` + files to run correctly. +* New repo name for xxh + +**Fixed:** + +* Correctly follow symlinks when using dot-dot paths with cd -P. +* ``execx`` does not require the input string to be newline-terminated. +* ``evalx`` accepts newline-terminated input string. +* Fixed issue where negative exit codes (such as those produced + by core dumps) where treated as logical successes when chaining + processes with other boolean expressions. +* Fixed XONSH_TRACE_SUBPROC for pipeline command. +* updated CONTRIBUTING.rst about running pylint for changed files + +**Authors:** + +* Anthony Scopatz +* Morten Enemark Lund +* David Strobach +* anki-code +* Samuel Lotz +* Gyuri Horak +* Noortheen Raja +* Gabriel Vogel +* anki +* Jerzy Drozdz + + + v0.9.14 ==================== diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2c9ddd270..acbe0e57d 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -119,7 +119,7 @@ as mispelled variable names, using pylint. If you're using Anaconda you'll need to run "conda install pylint" once. You can easily run pylint on the edited files in your uncommited git change:: - $ pylint $(git status -s | awk '/\.py$$/ { print $$2 }' | sort) + $ git status -s | awk '/\.py$$/ { print $2 }' | xargs pylint If you want to lint the entire code base run:: diff --git a/news/readme_xxh.rst b/news/abbrevs.rst similarity index 84% rename from news/readme_xxh.rst rename to news/abbrevs.rst index d1f232996..d0bf0212a 100644 --- a/news/readme_xxh.rst +++ b/news/abbrevs.rst @@ -1,10 +1,10 @@ **Added:** -* +* Added ``abbrevs`` xontrib. **Changed:** -* New repo name for xxh +* **Deprecated:** diff --git a/news/cd_symlinks.rst b/news/add-xontrib-pyenv.rst similarity index 61% rename from news/cd_symlinks.rst rename to news/add-xontrib-pyenv.rst index c0c5e4ec5..ff41d5539 100644 --- a/news/cd_symlinks.rst +++ b/news/add-xontrib-pyenv.rst @@ -1,6 +1,6 @@ **Added:** -* +* Added `xontrib-pyenv `_ to list of registered xontribs. **Changed:** @@ -16,7 +16,7 @@ **Fixed:** -* Correctly follow symlinks when using dot-dot paths with cd -P. +* **Security:** diff --git a/news/emacs-mode.rst b/news/emacs-mode.rst deleted file mode 100644 index 678ff89b1..000000000 --- a/news/emacs-mode.rst +++ /dev/null @@ -1,23 +0,0 @@ -**Added:** - -* Adds documentation for how to setup an emacs editing mode for xonsh. - -**Changed:** - -* - -**Deprecated:** - -* - -**Removed:** - -* - -**Fixed:** - -* - -**Security:** - -* diff --git a/news/env_trace_subproc.rst b/news/env_trace_subproc.rst deleted file mode 100644 index f90a834be..000000000 --- a/news/env_trace_subproc.rst +++ /dev/null @@ -1,23 +0,0 @@ -**Added:** - -* New ``$XONSH_TRACE_SUBPROC`` environment variable. - -**Changed:** - -* - -**Deprecated:** - -* - -**Removed:** - -* - -**Fixed:** - -* - -**Security:** - -* diff --git a/news/exitcode.rst b/news/exitcode.rst deleted file mode 100644 index 1e7893fac..000000000 --- a/news/exitcode.rst +++ /dev/null @@ -1,25 +0,0 @@ -**Added:** - -* - -**Changed:** - -* - -**Deprecated:** - -* - -**Removed:** - -* - -**Fixed:** - -* Fixed issue where negative exit codes (such as those produced - by core dumps) where treated as logical successes when chaining - processes with other boolean expressions. - -**Security:** - -* diff --git a/news/extended-exec.rst b/news/extended-exec.rst deleted file mode 100644 index 7f9be2186..000000000 --- a/news/extended-exec.rst +++ /dev/null @@ -1,24 +0,0 @@ -**Added:** - -* Added ``-l``, ``-c`` and ``-a`` options to ``xexec``, works now like ``exec`` - in bash/zsh - -**Changed:** - -* ``-l`` switch works like bash, loads environment in non-interactive shell - -**Deprecated:** - -* - -**Removed:** - -* - -**Fixed:** - -* - -**Security:** - -* diff --git a/news/pytest_order.rst b/news/pytest_order.rst deleted file mode 100644 index a63cadbc4..000000000 --- a/news/pytest_order.rst +++ /dev/null @@ -1,25 +0,0 @@ -**Added:** - -* - -**Changed:** - -* The xonsh pytest plugin no longer messes up the test order for pytest. Xsh test - are still executed first to avoid a bug were other tests would prevent ``test_*.xsh`` - files to run correctly. - -**Deprecated:** - -* - -**Removed:** - -* - -**Fixed:** - -* - -**Security:** - -* diff --git a/news/trace_subproc_fix_pipeline.rst b/news/trace_subproc_fix_pipeline.rst deleted file mode 100644 index c7169740d..000000000 --- a/news/trace_subproc_fix_pipeline.rst +++ /dev/null @@ -1,23 +0,0 @@ -**Added:** - -* - -**Changed:** - -* - -**Deprecated:** - -* - -**Removed:** - -* - -**Fixed:** - -* Fixed XONSH_TRACE_SUBPROC for pipeline command. - -**Security:** - -* diff --git a/tests/test_environ.py b/tests/test_environ.py index 6aeafd779..9a043f5e3 100644 --- a/tests/test_environ.py +++ b/tests/test_environ.py @@ -101,6 +101,14 @@ def test_histcontrol_ignoreerr_ignoredups(): assert "ignoredups" in env["HISTCONTROL"] +def test_histcontrol_ignoreerr_ignoredups_erase_dups(): + env = Env(HISTCONTROL="ignoreerr,ignoredups,ignoreerr,erasedups") + assert len(env["HISTCONTROL"]) == 3 + assert "ignoreerr" in env["HISTCONTROL"] + assert "ignoredups" in env["HISTCONTROL"] + assert "erasedups" in env["HISTCONTROL"] + + def test_swap(): env = Env(VAR="wakka") assert env["VAR"] == "wakka" diff --git a/tests/test_execer.py b/tests/test_execer.py index 22e612a6d..d74ccf9a1 100644 --- a/tests/test_execer.py +++ b/tests/test_execer.py @@ -2,7 +2,13 @@ """Tests the xonsh lexer.""" import os -from tools import check_eval, check_parse, skip_if_on_unix, skip_if_on_windows +from tools import ( + check_eval, + check_exec, + check_parse, + skip_if_on_unix, + skip_if_on_windows, +) import pytest @@ -138,3 +144,12 @@ def test_echo_line_cont(): ) def test_two_echo_line_cont(code): assert check_parse(code) + + +def test_eval_eol(): + assert check_eval("0") and check_eval("0\n") + + +def test_exec_eol(): + locs = dict() + assert check_exec("a=0", locs=locs) and check_exec("a=0\n", locs=locs) diff --git a/tests/test_history_sqlite.py b/tests/test_history_sqlite.py index 5b8b3d932..762f36132 100644 --- a/tests/test_history_sqlite.py +++ b/tests/test_history_sqlite.py @@ -169,6 +169,27 @@ def test_histcontrol(hist, xonsh_builtins): assert -1 == hist.rtns[-1] +def test_histcontrol_erase_dup(hist, xonsh_builtins): + """Test HISTCONTROL=erasedups""" + + xonsh_builtins.__xonsh__.env["HISTCONTROL"] = "erasedups" + assert len(hist) == 0 + + hist.append({"inp": "ls foo", "rtn": 2}) + hist.append({"inp": "ls foobazz", "rtn": 0}) + hist.append({"inp": "ls foo", "rtn": 0}) + hist.append({"inp": "ls foobazz", "rtn": 0}) + hist.append({"inp": "ls foo", "rtn": 0}) + assert len(hist) == 2 + assert len(hist.inps) == 5 + + items = list(hist.items()) + assert "ls foo" == items[-1]["inp"] + assert "ls foobazz" == items[-2]["inp"] + assert items[-2]["frequency"] == 2 + assert items[-1]["frequency"] == 3 + + @pytest.mark.parametrize( "index, exp", [ diff --git a/tests/tools.py b/tests/tools.py index 0b9de84a8..767887f96 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -174,8 +174,6 @@ class DummyEnv(MutableMapping): def check_exec(input, **kwargs): - if not input.endswith("\n"): - input += "\n" builtins.__xonsh__.execer.exec(input, **kwargs) return True diff --git a/xonsh/__init__.py b/xonsh/__init__.py index cf2a4e5c9..0055d433d 100644 --- a/xonsh/__init__.py +++ b/xonsh/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.9.14" +__version__ = "0.9.15" # amalgamate exclude jupyter_kernel parser_table parser_test_table pyghooks diff --git a/xonsh/environ.py b/xonsh/environ.py index 59f3b3df8..c9e100b99 100644 --- a/xonsh/environ.py +++ b/xonsh/environ.py @@ -1004,8 +1004,10 @@ def DEFAULT_DOCS(): "that determine what commands are saved to the history list. By " "default all commands are saved. The option ``ignoredups`` will not " "save the command if it matches the previous command. The option " - "'ignoreerr' will cause any commands that fail (i.e. return non-zero " - "exit status) to not be added to the history list.", + "``ignoreerr`` will cause any commands that fail (i.e. return non-zero " + "exit status) to not be added to the history list. The option " + "``erasedups`` will remove all previous commands that matches and updates the frequency. " + "Note: ``erasedups`` is supported only in sqlite backend).", store_as_str=True, ), "IGNOREEOF": VarDocs("Prevents Ctrl-D from exiting the shell."), diff --git a/xonsh/execer.py b/xonsh/execer.py index 2d113ee58..b4daef78c 100644 --- a/xonsh/execer.py +++ b/xonsh/execer.py @@ -145,6 +145,7 @@ class Execer(object): if isinstance(input, types.CodeType): code = input else: + input = input.rstrip("\n") if filename is None: filename = self.filename code = self.compile( @@ -174,6 +175,8 @@ class Execer(object): if isinstance(input, types.CodeType): code = input else: + if not input.endswith("\n"): + input += "\n" if filename is None: filename = self.filename code = self.compile( diff --git a/xonsh/history/sqlite.py b/xonsh/history/sqlite.py index 430e32a8b..130d08dd9 100644 --- a/xonsh/history/sqlite.py +++ b/xonsh/history/sqlite.py @@ -12,6 +12,10 @@ import time from xonsh.history.base import History import xonsh.tools as xt +XH_SQLITE_CACHE = threading.local() +XH_SQLITE_TABLE_NAME = "xonsh_history" +XH_SQLITE_CREATED_SQL_TBL = "CREATED_SQL_TABLE" + def _xh_sqlite_get_file_name(): envs = builtins.__xonsh__.env @@ -33,35 +37,84 @@ def _xh_sqlite_create_history_table(cursor): Columns: info - JSON formatted, reserved for future extension. + frequency - in case of HISTCONTROL=erasedups, + it tracks the frequency of the inputs. helps in sorting autocompletion """ + if not getattr(XH_SQLITE_CACHE, XH_SQLITE_CREATED_SQL_TBL, False): + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS {} + (inp TEXT, + rtn INTEGER, + tsb REAL, + tse REAL, + sessionid TEXT, + out TEXT, + info TEXT, + frequency INTEGER default 1 + ) + """.format( + XH_SQLITE_TABLE_NAME + ) + ) + # add frequency column if not exists for backward compatibility + try: + cursor.execute( + "ALTER TABLE " + + XH_SQLITE_TABLE_NAME + + " ADD COLUMN frequency INTEGER default 1" + ) + except sqlite3.OperationalError: + pass + + # mark that this function ran for this session + setattr(XH_SQLITE_CACHE, XH_SQLITE_CREATED_SQL_TBL, True) + + +def _xh_sqlite_get_frequency(cursor, input): + # type: (sqlite3.Cursor, str) -> int + sql = "SELECT sum(frequency) FROM {} WHERE inp=?".format(XH_SQLITE_TABLE_NAME) + cursor.execute(sql, (input,)) + return cursor.fetchone()[0] or 0 + + +def _xh_sqlite_erase_dups(cursor, input): + freq = _xh_sqlite_get_frequency(cursor, input) + sql = "DELETE FROM {} WHERE inp=?".format(XH_SQLITE_TABLE_NAME) + cursor.execute(sql, (input,)) + return freq + + +def _sql_insert(cursor, values): + # type: (sqlite3.Cursor, dict) -> None + """handy function to run insert query""" + sql = "INSERT INTO {} ({}) VALUES ({});" + fields = ", ".join(values) + marks = ", ".join(["?"] * len(values)) cursor.execute( - """ - CREATE TABLE IF NOT EXISTS xonsh_history - (inp TEXT, - rtn INTEGER, - tsb REAL, - tse REAL, - sessionid TEXT, - out TEXT, - info TEXT - ) - """ + sql.format(XH_SQLITE_TABLE_NAME, fields, marks), tuple(values.values()) ) -def _xh_sqlite_insert_command(cursor, cmd, sessionid, store_stdout): - sql = "INSERT INTO xonsh_history (inp, rtn, tsb, tse, sessionid" +def _xh_sqlite_insert_command(cursor, cmd, sessionid, store_stdout, remove_duplicates): tss = cmd.get("ts", [None, None]) - params = [cmd["inp"].rstrip(), cmd["rtn"], tss[0], tss[1], sessionid] + values = collections.OrderedDict( + [ + ("inp", cmd["inp"].rstrip()), + ("rtn", cmd["rtn"]), + ("tsb", tss[0]), + ("tse", tss[1]), + ("sessionid", sessionid), + ] + ) if store_stdout and "out" in cmd: - sql += ", out" - params.append(cmd["out"]) + values["out"] = cmd["out"] if "info" in cmd: - sql += ", info" info = json.dumps(cmd["info"]) - params.append(info) - sql += ") VALUES (" + ("?, " * len(params)).rstrip(", ") + ")" - cursor.execute(sql, tuple(params)) + values["info"] = info + if remove_duplicates: + values["frequency"] = _xh_sqlite_erase_dups(cursor, values["inp"]) + 1 + _sql_insert(cursor, values) def _xh_sqlite_get_count(cursor, sessionid=None): @@ -75,7 +128,7 @@ def _xh_sqlite_get_count(cursor, sessionid=None): def _xh_sqlite_get_records(cursor, sessionid=None, limit=None, newest_first=False): - sql = "SELECT inp, tsb, rtn FROM xonsh_history " + sql = "SELECT inp, tsb, rtn, frequency FROM xonsh_history " params = [] if sessionid is not None: sql += "WHERE sessionid = ? " @@ -103,11 +156,13 @@ def _xh_sqlite_delete_records(cursor, size_to_keep): return result.rowcount -def xh_sqlite_append_history(cmd, sessionid, store_stdout, filename=None): +def xh_sqlite_append_history( + cmd, sessionid, store_stdout, filename=None, remove_duplicates=False +): with _xh_sqlite_get_conn(filename=filename) as conn: c = conn.cursor() _xh_sqlite_create_history_table(c) - _xh_sqlite_insert_command(c, cmd, sessionid, store_stdout) + _xh_sqlite_insert_command(c, cmd, sessionid, store_stdout, remove_duplicates) conn.commit() @@ -181,9 +236,11 @@ class SqliteHistory(History): self.outs = [] self.tss = [] + # during init rerun create command + setattr(XH_SQLITE_CACHE, XH_SQLITE_CREATED_SQL_TBL, False) + def append(self, cmd): envs = builtins.__xonsh__.env - opts = envs.get("HISTCONTROL") inp = cmd["inp"].rstrip() self.inps.append(inp) store_stdout = envs.get("XONSH_STORE_STDOUT", False) @@ -203,22 +260,23 @@ class SqliteHistory(History): return self._last_hist_inp = inp xh_sqlite_append_history( - cmd, str(self.sessionid), store_stdout, filename=self.filename + cmd, + str(self.sessionid), + store_stdout, + filename=self.filename, + remove_duplicates=("erasedups" in opts), ) - def all_items(self, newest_first=False): + def all_items(self, newest_first=False, session_id=None): """Display all history items.""" - for item in xh_sqlite_items(filename=self.filename, newest_first=newest_first): - yield {"inp": item[0], "ts": item[1], "rtn": item[2]} + for inp, ts, rtn, freq in xh_sqlite_items( + filename=self.filename, newest_first=newest_first, sessionid=session_id + ): + yield {"inp": inp, "ts": ts, "rtn": rtn, "frequency": freq} def items(self, newest_first=False): """Display history items of current session.""" - for item in xh_sqlite_items( - sessionid=str(self.sessionid), - filename=self.filename, - newest_first=newest_first, - ): - yield {"inp": item[0], "ts": item[1], "rtn": item[2]} + yield from self.all_items(newest_first, session_id=str(self.sessionid)) def info(self): data = collections.OrderedDict() diff --git a/xonsh/xontribs.json b/xonsh/xontribs.json index 8a96ee8d7..2d8f88e74 100644 --- a/xonsh/xontribs.json +++ b/xonsh/xontribs.json @@ -1,4 +1,17 @@ {"xontribs": [ + {"name": "abbrevs", + "package": "xonsh", + "url": "http://xon.sh", + "description": [ + "Adds ``abbrevs`` dictionary to hold user-defined command abbreviations. ", + "The dictionary is searched as you type and the matching words are replaced ", + "at the command line by the corresponding dictionary contents once you hit ", + "'Space' or 'Return' key. For instance a frequently used command such as ", + "``git status`` can be abbreviated to ``gst`` as follows::\n\n", + " $ xontrib load abbrevs\n", + " $ abbrevs['gst'] = 'git status'\n", + " $ gst # Once you hit or , 'gst' gets expanded to 'git status'.\n\n"] + }, {"name": "apt_tabcomplete", "package": "xonsh-apt-tabcomplete", "url": "https://github.com/DangerOnTheRanger/xonsh-apt-tabcomplete", @@ -146,11 +159,16 @@ "url": "https://github.com/t184256/xontrib-prompt-vi-mode", "description": ["vi-mode status formatter for xonsh prompt"] }, + {"name": "pyenv", + "package": "xontrib-pyenv", + "url": "https://github.com/dyuri/xontrib-pyenv", + "description": ["pyenv integration for xonsh."] + }, {"name": "readable-traceback", "package": "xontrib-readable-traceback", "url": "https://github.com/6syun9/xontrib-readable-traceback", "description": ["Make traceback easier to see for xonsh."] - }, + }, {"name": "schedule", "package": "xontrib-schedule", "url": "https://github.com/astronouth7303/xontrib-schedule", @@ -299,6 +317,13 @@ "pip": "xpip install xontrib-kitty" } }, + "xontrib-powerline": { + "license": "MIT", + "url": "https://github.com/santagada/xontrib-powerline", + "install": { + "pip": "xpip install xontrib-powerline" + } + }, "xontrib-prompt-ret-code": { "license": "MIT", "url": "https://github.com/Siecje/xontrib-prompt-ret-code", @@ -313,11 +338,11 @@ "pip": "xpip install xontrib-prompt-vi-mode" } }, - "xontrib-powerline": { + "xontrib-pyenv": { "license": "MIT", - "url": "https://github.com/santagada/xontrib-powerline", + "url": "https://github.com/dyuri/xontrib-pyenv", "install": { - "pip": "xpip install xontrib-powerline" + "pip": "xpip install xontrib-pyenv" } }, "xontrib-readable-traceback": { diff --git a/xontrib/abbrevs.py b/xontrib/abbrevs.py new file mode 100644 index 000000000..5fa2518c6 --- /dev/null +++ b/xontrib/abbrevs.py @@ -0,0 +1,65 @@ +""" +Command abbreviations. + +This expands input words from `abbrevs` disctionary as you type. +""" + +import builtins +from prompt_toolkit.filters import IsMultiline +from prompt_toolkit.keys import Keys +from xonsh.built_ins import DynamicAccessProxy +from xonsh.platform import ptk_shell_type +from xonsh.tools import check_for_partial_string + +__all__ = () + +builtins.__xonsh__.abbrevs = dict() +proxy = DynamicAccessProxy("abbrevs", "__xonsh__.abbrevs") +setattr(builtins, "abbrevs", proxy) + + +def expand_abbrev(buffer): + abbrevs = getattr(builtins, "abbrevs", None) + if abbrevs is None: + return + document = buffer.document + word = document.get_word_before_cursor(WORD=True) + if word in abbrevs.keys(): + partial = document.text[: document.cursor_position] + startix, endix, quote = check_for_partial_string(partial) + if startix is not None and endix is None: + return + buffer.delete_before_cursor(count=len(word)) + buffer.insert_text(abbrevs[word]) + + +@events.on_ptk_create +def custom_keybindings(bindings, **kw): + + if ptk_shell_type() == "prompt_toolkit2": + from xonsh.ptk2.key_bindings import carriage_return + from prompt_toolkit.filters import EmacsInsertMode, ViInsertMode + + handler = bindings.add + insert_mode = ViInsertMode() | EmacsInsertMode() + else: + from xonsh.ptk.key_bindings import carriage_return + from prompt_toolkit.filters import to_filter + + handler = bindings.registry.add_binding + insert_mode = to_filter(True) + + @handler(" ", filter=IsMultiline() & insert_mode) + def handle_space(event): + buffer = event.app.current_buffer + expand_abbrev(buffer) + buffer.insert_text(" ") + + @handler(Keys.ControlJ, filter=IsMultiline() & insert_mode) + @handler(Keys.ControlM, filter=IsMultiline() & insert_mode) + def multiline_carriage_return(event): + buffer = event.app.current_buffer + current_char = buffer.document.current_char + if not current_char or current_char.isspace(): + expand_abbrev(buffer) + carriage_return(buffer, event.cli)