Repository: jquast/blessed

Issue:
API Change: Terminal.move (y, x) => (x, y).
This issue duplicates upstream issue, https://github.com/erikrose/blessings/issues/58
## Problem

Terminal.location is (x, y) but Terminal.move is (y, x).

This causes issues with composability of positional (y, x) parameters. Also causes programmer mistakes.
## Solution

Terminal.move to positional ordering, `(y, x)` => `(x, y)`.


Discussion:

Patch:
diff --git a/bin/colorchart.py b/bin/colorchart.py
index be7aa2a..57d247b 100644
--- a/bin/colorchart.py
+++ b/bin/colorchart.py
@@ -32,7 +32,6 @@ SORTED_COLORS = sort_colors()
 def draw_chart(term):
     """Draw a chart of each X11 color represented as in 24-bit and as down-converted to 256, 16, and
     8 color with the currently configured algorithm."""
-    term.move(0, 0)
     sys.stdout.write(term.home)
     width = term.width
     line = ''
diff --git a/bin/editor.py b/bin/editor.py
index 1fe873d..2963fce 100755
--- a/bin/editor.py
+++ b/bin/editor.py
@@ -63,7 +63,7 @@ def input_filter(keystroke):
 
 def echo_yx(cursor, text):
     """Move to ``cursor`` and display ``text``."""
-    echo(cursor.term.move(cursor.y, cursor.x) + text)
+    echo(cursor.term.move_yx(cursor.y, cursor.x) + text)
 
 
 Cursor = collections.namedtuple('Cursor', ('y', 'x', 'term'))
diff --git a/bin/keymatrix.py b/bin/keymatrix.py
index 4b3d262..18753f3 100755
--- a/bin/keymatrix.py
+++ b/bin/keymatrix.py
@@ -39,13 +39,13 @@ def refresh(term, board, level, score, inps):
     bottom = 0
     for keycode, attr in board.items():
         echo(u''.join((
-            term.move(attr['row'], attr['column']),
+            term.move_yx(attr['row'], attr['column']),
             term.color(level_color),
             (term.reverse if attr['hit'] else term.bold),
             keycode,
             term.normal)))
         bottom = max(bottom, attr['row'])
-    echo(term.move(term.height, 0) + 'level: %s score: %s' % (level, score,))
+    echo(term.move_yx(term.height, 0) + 'level: %s score: %s' % (level, score,))
     if bottom >= (term.height - 5):
         sys.stderr.write(
             ('\n' * (term.height // 2)) +
@@ -54,10 +54,10 @@ def refresh(term, board, level, score, inps):
             term.center("(use a larger screen)") +
             ('\n' * (term.height // 2)))
         sys.exit(1)
-    echo(term.move(bottom + 1, 0))
+    echo(term.move_yx(bottom + 1, 0))
     echo('Press ^C to exit.')
     for row, inp in enumerate(inps[(term.height - (bottom + 3)) * -1:], 1):
-        echo(term.move(bottom + row + 1, 0))
+        echo(term.move_yx(bottom + row + 1, 0))
         echo('{0!r}, {1}, {2}'.format(
             inp.__str__() if inp.is_sequence else inp,
              inp.code,
@@ -128,7 +128,7 @@ def main():
             inps.append(inp)
 
     with term.cbreak():
-        echo(term.move(term.height))
+        echo(term.move_yx(term.height))
         echo(
             u'{term.clear_eol}Your final score was {score} '
             u'at level {level}{term.clear_eol}\n'
diff --git a/bin/plasma.py b/bin/plasma.py
index acf1b66..5adf0b5 100755
--- a/bin/plasma.py
+++ b/bin/plasma.py
@@ -51,14 +51,14 @@ def elapsed_timer():
 
 def show_please_wait(term):
     txt_wait = 'please wait ...'
-    outp = term.move(term.height - 1, 0) + term.clear_eol + term.center(txt_wait)
+    outp = term.move_yx(term.height - 1, 0) + term.clear_eol + term.center(txt_wait)
     print(outp, end='')
     sys.stdout.flush()
 
 
 def show_paused(term):
     txt_paused = 'paused'
-    outp = term.move(term.height - 1, int(term.width / 2 - len(txt_paused) / 2))
+    outp = term.move_yx(term.height - 1, int(term.width / 2 - len(txt_paused) / 2))
     outp += txt_paused
     print(outp, end='')
     sys.stdout.flush()
diff --git a/bin/progress_bar.py b/bin/progress_bar.py
index 1139ac1..e8b0a8d 100755
--- a/bin/progress_bar.py
+++ b/bin/progress_bar.py
@@ -2,10 +2,10 @@
 """
 Example application for the 'blessed' Terminal library for python.
 
-This isn't a real progress bar, just a sample "animated prompt" of sorts that demonstrates the
-separate move_x() and move_y() functions, made mainly to test the `hpa' compatibility for 'screen'
-terminal type which fails to provide one, but blessed recognizes that it actually does, and provides
-a proxy.
+This isn't a real progress bar, just a sample "animated prompt" of sorts that
+demonstrates the separate move_x() and move_yx() capabilities, made mainly to
+test the `hpa' compatibility for 'screen' terminal type which fails to provide
+one, but blessed recognizes that it actually does, and provides a proxy.
 """
 from __future__ import print_function
 
@@ -26,7 +26,7 @@ def main():
     with term.cbreak():
         inp = None
         print("press 'X' to stop.")
-        sys.stderr.write(term.move(term.height, 0) + u'[')
+        sys.stderr.write(term.move_yx(term.height, 0) + u'[')
         sys.stderr.write(term.move_x(term.width - 1) + u']' + term.move_x(1))
         while inp != 'X':
             if col >= (term.width - 2):
diff --git a/bin/worms.py b/bin/worms.py
index 853059e..233a29a 100755
--- a/bin/worms.py
+++ b/bin/worms.py
@@ -191,7 +191,7 @@ def main():
     color_worm = term.yellow_reverse
     color_head = term.red_reverse
     color_bg = term.on_blue
-    echo(term.move(1, 1))
+    echo(term.move_yx(1, 1))
     echo(color_bg(term.clear))
 
     # speed is actually a measure of time; the shorter, the faster.
@@ -199,13 +199,13 @@ def main():
     modifier = 0.93
     inp = None
 
-    echo(term.move(term.height, 0))
+    echo(term.move_yx(term.height, 0))
     with term.hidden_cursor(), term.cbreak(), term.location():
         while inp not in (u'q', u'Q'):
 
             # delete the tail of the worm at worm_length
             if len(worm) > worm_length:
-                echo(term.move(*worm.pop(0)))
+                echo(term.move_yx(*worm.pop(0)))
                 echo(color_bg(u' '))
 
             # compute head location
@@ -229,22 +229,22 @@ def main():
                 # with a worm color for those portions that overlay.
                 for (yloc, xloc) in nibble_locations(*nibble):
                     echo(u''.join((
-                        term.move(yloc, xloc),
+                        term.move_yx(yloc, xloc),
                         (color_worm if (yloc, xloc) == head
                          else color_bg)(u' '),
                         term.normal)))
                 # and draw the new,
-                echo(term.move(*n_nibble.location) + (
+                echo(term.move_yx(*n_nibble.location) + (
                     color_nibble('{}'.format(n_nibble.value))))
 
             # display new worm head
-            echo(term.move(*head) + color_head(head_glyph(direction)))
+            echo(term.move_yx(*head) + color_head(head_glyph(direction)))
 
             # and its old head (now, a body piece)
             if worm:
-                echo(term.move(*(worm[-1])))
+                echo(term.move_yx(*(worm[-1])))
                 echo(color_worm(u' '))
-            echo(term.move(*head))
+            echo(term.move_yx(*head))
 
             # wait for keyboard input, which may indicate
             # a new direction (up/down/left/right)
@@ -271,7 +271,7 @@ def main():
 
     echo(term.normal)
     score = (worm_length - 1) * 100
-    echo(u''.join((term.move(term.height - 1, 1), term.normal)))
+    echo(u''.join((term.move_yx(term.height - 1, 1), term.normal)))
     echo(u''.join((u'\r\n', u'score: {}'.format(score), u'\r\n')))
 
 
diff --git a/bin/x11_colorpicker.py b/bin/x11_colorpicker.py
index acb45c9..ccb24bf 100644
--- a/bin/x11_colorpicker.py
+++ b/bin/x11_colorpicker.py
@@ -35,7 +35,7 @@ def render(term, idx):
         f'{term.number_of_colors} colors - '
         f'{term.color_distance_algorithm}')
 
-    result += term.move(idx // term.width, idx % term.width)
+    result += term.move_yx(idx // term.width, idx % term.width)
     result += term.on_color_rgb(*rgb_color)(' \b')
     return result
 
diff --git a/blessed/terminal.py b/blessed/terminal.py
index 27ba3e8..7e47e8a 100644
--- a/blessed/terminal.py
+++ b/blessed/terminal.py
@@ -667,6 +667,30 @@ class Terminal(object):
         return ParameterizingString(self._foreground_color,
                                     self.normal, 'color')
 
+    def move_xy(self, x, y):
+        """
+        A callable string that moves the cursor to the given ``(x, y)`` screen coordinates.
+
+        :arg int x: horizontal position.
+        :arg int y: vertical position.
+        :rtype: ParameterizingString
+        """
+        # this is just a convenience alias to the built-in, but hidden 'move'
+        # attribute -- we encourage folks to use only (x, y) positional
+        # arguments, or, if they must use (y, x), then use the 'move_yx'
+        # alias.
+        return self.move(y, x)
+
+    def move_yx(self, y, x):
+        """
+        A callable string that moves the cursor to the given ``(y, x)`` screen coordinates.
+
+        :arg int x: horizontal position.
+        :arg int y: vertical position.
+        :rtype: ParameterizingString
+        """
+        return self.move(y, x)
+
     def color_rgb(self, red, green, blue):
         if self.number_of_colors == 1 << 24:
             # "truecolor" 24-bit
diff --git a/docs/history.rst b/docs/history.rst
index 1162348..07a1111 100644
--- a/docs/history.rst
+++ b/docs/history.rst
@@ -1,38 +1,38 @@
 Version History
 ===============
 1.17
-  * introduced: 24-bit color support, detected by ``term.number_of_colors == 1 << 24``,
-    and 24-bit color foreground method :meth:`~Terminal.color_rgb` and background method
-    :meth:`~Terminal.on_color_rgb`.
+  * introduced: 24-bit color support, detected by ``term.number_of_colors == 1 << 24``, and 24-bit
+    color foreground method :meth:`~Terminal.color_rgb` and background method
+    :meth:`~Terminal.on_color_rgb`, as well as 676 common X11 color attribute names are now
+    possible, such as ``term.aquamarine_on_wheat``.
+  * introduced: ``term.move_xy``, recommended over built-in ``move`` capability, as the return
+    positional arguments, ``(x, y)`` match the return value of :meth:`~.Terminal.get_location`,
+    and all other common graphics library calls, :ghissue:`65`.
+    values over the b``term.move``.
   * bugfix: prevent error condition, ``ValueError: underlying buffer has been detached`` in rare
     conditions where sys.__stdout__ has been detached in test frameworks. :ghissue:`126`.
-  * bugfix: off-by-one error in :meth:`~.Terminal.get_location`, now accounts for
-    ``%i`` in cursor_report, :ghissue:`94`.
-  * bugfix :meth:`~Terminal.split_seqs` and related functions failed to match when the
-    color index was greater than 15, :ghissue:`101`.
-  * bugfix: Context Managers, :meth:`~.Terminal.fullscreen`,
-    :meth:`~.Terminal.hidden_cursor`, and :meth:`~Terminal.keypad`
-    now flush the stream after writing their sequences.
-  * bugfix: ``chr(127)``, ``\x7f`` has changed from keycode ``term.DELETE`` to the more
-    common match, ``term.BACKSPACE``, :ghissue:115` by :ghuser:`jwezel`.
-  * deprecated: the direct curses ``move()`` capability is no longer recommended,
-    suggest to use :meth:`~.Terminal.move_xy()`, which matches the return value of
-    :meth:`~.Terminal.get_location`.
-  * deprecated: ``superscript``, ``subscript``, ``shadow``, and ``dim`` are no
-    longer "compoundable" with colors, such as in phrase ``Terminal.blue_subscript('a')``.
-    These attributes are not typically supported, anyway.  Use Unicode text or 256 or
-    24-bit color codes instead.
-  * deprecated: additional key names, such as ``KEY_TAB``, are no longer "injected" into
-    the curses module namespace.
-  * deprecated: :func:`curses.setupterm` is now called with :attr:`os.devnull`
-    as the file descriptor, let us know if this causes any issues. :ghissue:`59`.
-  * deprecated: :meth:`~Terminal.inkey` no longer raises RuntimeError when
-    :attr:`~Terminal.stream` is not a terminal, programs using
-    :meth:`~Terminal.inkey` to block indefinitely if a keyboard is not
-    attached. :ghissue:`69`.
-  * deprecated: using argument ``_intr_continue`` to method
-    :meth:`~Terminal.kbhit`, behavior is as though such value is always True
-    since 1.9.
+  * bugfix: off-by-one error in :meth:`~.Terminal.get_location`, now accounts for ``%i`` in
+    cursor_report, :ghissue:`94`.
+  * bugfix :meth:`~Terminal.split_seqs` and related functions failed to match when the color index
+    was greater than 15, :ghissue:`101`.
+  * bugfix: Context Managers, :meth:`~.Terminal.fullscreen`, :meth:`~.Terminal.hidden_cursor`, and
+    :meth:`~Terminal.keypad` now flush the stream after writing their sequences.
+  * bugfix: ``chr(127)``, ``\x7f`` has changed from keycode ``term.DELETE`` to the more common
+    match, ``term.BACKSPACE``, :ghissue:115` by :ghuser:`jwezel`.
+  * deprecated: the direct curses ``move()`` capability is no longer recommended, suggest to use
+    :meth:`~.Terminal.move_xy()`, which matches the return value of :meth:`~.Terminal.get_location`.
+  * deprecated: ``superscript``, ``subscript``, ``shadow``, and ``dim`` are no longer "compoundable"
+    with colors, such as in phrase ``Terminal.blue_subscript('a')``.  These attributes are not
+    typically supported, anyway.  Use Unicode text or 256 or 24-bit color codes instead.
+  * deprecated: additional key names, such as ``KEY_TAB``, are no longer "injected" into the curses
+    module namespace.
+  * deprecated: :func:`curses.setupterm` is now called with :attr:`os.devnull` as the file
+    descriptor, let us know if this causes any issues. :ghissue:`59`.
+  * deprecated: :meth:`~Terminal.inkey` no longer raises RuntimeError when :attr:`~Terminal.stream`
+    is not a terminal, programs using :meth:`~Terminal.inkey` to block indefinitely if a keyboard is
+    not attached. :ghissue:`69`.
+  * deprecated: using argument ``_intr_continue`` to method :meth:`~Terminal.kbhit`, behavior is as
+    though such value is always True since 1.9.
 
 1.16
   * introduced: Windows support?! :ghissue:`110` by :ghuser:`avylove`.
diff --git a/docs/overview.rst b/docs/overview.rst
index d4224b9..8254456 100644
--- a/docs/overview.rst
+++ b/docs/overview.rst
@@ -184,42 +184,27 @@ demonstration script.
 Moving The Cursor
 -----------------
 
-When you want to move the cursor, you have a few choices:
-
-- ``location(x=None, y=None)`` context manager.
-- ``move(row, col)`` capability.
-- ``move_y(row)`` capability.
-- ``move_x(col)`` capability.
-
-.. warning:: The :meth:`~.Terminal.location` method receives arguments in
-   positional order *(x, y)*, whereas the ``move()`` capability receives
-   arguments in order *(y, x)*.  Please use keyword arguments as a later
-   release may correct the argument order of :meth:`~.Terminal.location`.
-
-Finding The Cursor
-------------------
-
-We can determine the cursor's current position at anytime using
-:meth:`~.get_location`, returning the current (y, x) location.  This uses a
-kind of "answer back" sequence that your terminal emulator responds to.  If
-the terminal may not respond, the :paramref:`~.get_location.timeout` keyword
-argument can be specified to return coordinates (-1, -1) after a blocking
-timeout::
+If you just want to move and aren't worried about returning, do something like
+this::
 
     from blessed import Terminal
 
     term = Terminal()
+    print(term.move_xy(10, 1) + 'Hi, mom!')
 
-    row, col = term.get_location(timeout=5)
+There are three basic movement capabilities:
 
-    if row < term.height:
-        print(term.move_y(term.height) + 'Get down there!')
-
-Moving Temporarily
-~~~~~~~~~~~~~~~~~~
+``move_xy(x, y)``
+  Position cursor at given **x**, **y**.
+``move_x(x)``
+  Position cursor at column **x**.
+``move_y(y)``
+  Position cursor at row **y**.
+``home``
+  Position cursor at (0, 0).
 
 A context manager, :meth:`~.Terminal.location` is provided to move the cursor
-to an *(x, y)* screen position and restore the previous position upon exit::
+to an *(x, y)* screen position and *restore the previous position* on exit::
 
     from blessed import Terminal
 
@@ -236,32 +221,32 @@ keyword arguments::
     with term.location(y=10):
         print('We changed just the row.')
 
-When omitted, it saves the cursor position and restore it upon exit::
+When omitted, it saves the current cursor position, and restore it on exit::
 
     with term.location():
-        print(term.move(1, 1) + 'Hi')
-        print(term.move(9, 9) + 'Mom')
+        print(term.move_xy(1, 1) + 'Hi')
+        print(term.move_xy(9, 9) + 'Mom')
 
 .. note:: calls to :meth:`~.Terminal.location` may not be nested.
 
+Finding The Cursor
+------------------
 
-Moving Permanently
-~~~~~~~~~~~~~~~~~~
-
-If you just want to move and aren't worried about returning, do something like
-this::
+We can determine the cursor's current position at anytime using
+:meth:`~.get_location`, returning the current (y, x) location.  This uses a
+kind of "answer back" sequence that your terminal emulator responds to.  If
+the terminal may not respond, the :paramref:`~.get_location.timeout` keyword
+argument can be specified to return coordinates (-1, -1) after a blocking
+timeout::
 
     from blessed import Terminal
 
     term = Terminal()
-    print(term.move(10, 1) + 'Hi, mom!')
 
-``move(y, x)``
-  Position cursor at given **y**, **x**.
-``move_x(x)``
-  Position cursor at column **x**.
-``move_y(y)``
-  Position cursor at row **y**.
+    row, col = term.get_location(timeout=5)
+
+    if row < term.height:
+        print(term.move_y(term.height) + 'Get down there!')
 
 One-Notch Movement
 ~~~~~~~~~~~~~~~~~~
@@ -309,6 +294,7 @@ changes, you may author a callback for SIGWINCH_ signals::
     # wait for keypress
     term.inkey()
 
+.. note:: This is not compatible with Windows!
 
 Clearing The Screen
 -------------------


Test patch:
diff --git a/tests/accessories.py b/tests/accessories.py
index 9ea6a00..73a6eed 100644
--- a/tests/accessories.py
+++ b/tests/accessories.py
@@ -70,7 +70,6 @@ def init_subproc_coverage(run_note):
         os.path.dirname(__file__),
         os.pardir, 'tox.ini')
     cov = coverage.Coverage(config_file=_coveragerc)
-    cov.set_option("run:note", run_note)
     cov.start()
     return cov
 
diff --git a/tests/test_core.py b/tests/test_core.py
index 9c5a75f..a939aea 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -41,17 +41,17 @@ def test_null_location(all_terms):
     child(all_terms)
 
 
-def test_flipped_location_move(all_terms):
-    """``location()`` and ``move()`` receive counter-example arguments."""
+def test_location_to_move_xy(all_terms):
+    """``location()`` and ``move_xy()`` receive complimentary arguments."""
     @as_subprocess
     def child(kind):
         buf = six.StringIO()
         t = TestTerminal(stream=buf, force_styling=True)
-        y, x = 10, 20
+        x, y = 12, 34
         with t.location(y, x):
-            xy_val = t.move(x, y)
-            yx_val = buf.getvalue()[len(t.sc):]
-            assert xy_val == yx_val
+            xy_val_from_move_xy = t.move_xy(y, x)
+            xy_val_from_location = buf.getvalue()[len(t.sc):]
+            assert xy_val_from_move_xy == xy_val_from_location
 
     child(all_terms)
 
diff --git a/tests/test_length_sequence.py b/tests/test_length_sequence.py
index 964796e..33bea28 100644
--- a/tests/test_length_sequence.py
+++ b/tests/test_length_sequence.py
@@ -377,6 +377,10 @@ def test_sequence_is_movement_true(all_terms):
                 measure_length(term.move(98, 76), term))
         assert (len(term.move(54)) ==
                 measure_length(term.move(54), term))
+        assert (len(term.move_xy(1, 2)) ==
+                measure_length(term.move(1, 2), term))
+        assert (len(term.move_yx(3, 4)) ==
+                measure_length(term.move(3, 4), term))
         assert not term.cud1 or (len(term.cud1) ==
                                  measure_length(term.cud1, term))
         assert not term.cub1 or (len(term.cub1) ==
@@ -406,6 +410,8 @@ def test_termcap_will_move_true(all_terms):
         from blessed.sequences import iter_parse
         term = TestTerminal(kind=kind, force_styling=True)
         assert next(iter_parse(term, term.move(98, 76)))[1].will_move
+        assert next(iter_parse(term, term.move_yx(8, 76)))[1].will_move
+        assert next(iter_parse(term, term.move_xy(98, 7)))[1].will_move
         assert next(iter_parse(term, term.move(54)))[1].will_move
         assert next(iter_parse(term, term.cud1))[1].will_move
         assert next(iter_parse(term, term.cub1))[1].will_move