Repository: xonsh/xonsh

Issue:
Added env variable to ignore ./ and ../ in filepath completion
<!--- Thanks for opening a PR on xonsh! Please include a news entry with your PR
to help keep our changelog up to date! There are instructions available here:
https://xon.sh/devguide.html#changelog -->

<!--- If there is specific issue / feature request that this PR is addressing,
please link to the corresponding issue by using the `#issuenumber` syntax.
Thanks again! -->

## For community
**Added:**

* Added environment variable `$IGNORE_DOTS` to ignore ./ and ../ in file path completions.

This addresses issue  #4213 

⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**



Discussion:

Resolving patch:
diff --git a/xonsh/completers/path.py b/xonsh/completers/path.py
--- a/xonsh/completers/path.py
+++ b/xonsh/completers/path.py
@@ -124,10 +124,16 @@ def _env(prefix):
 
 
 def _dots(prefix):
+    complete_dots = builtins.__xonsh__.env.get("COMPLETE_DOTS", "matching").lower()
+    if complete_dots == "never":
+        return ()
     slash = xt.get_sep()
     if slash == "\\":
         slash = ""
-    if prefix in {"", "."}:
+    prefixes = {"."}
+    if complete_dots == "always":
+        prefixes.add("")
+    if prefix in prefixes:
         return ("." + slash, ".." + slash)
     elif prefix == "..":
         return (".." + slash,)
diff --git a/xonsh/environ.py b/xonsh/environ.py
--- a/xonsh/environ.py
+++ b/xonsh/environ.py
@@ -1115,6 +1115,14 @@ class ChangeDirSetting(Xettings):
         False,
         "Whether or not to suppress directory stack manipulation output.",
     )
+    COMPLETE_DOTS = Var.with_default(
+        "matching",
+        doc="Flag to specify how current and previous directories should be "
+        "tab completed  ('./', '../'):"
+        "    - ``always`` Always complete paths with ./ and ../\n"
+        "    - ``never`` Never complete paths with ./ and ../\n"
+        "    - ``matching`` Complete if path starts with . or ..",
+    )
 
 
 class InterpreterSetting(Xettings):


Test patch:
diff --git a/tests/completers/test_base_completer.py b/tests/completers/test_base_completer.py
--- a/tests/completers/test_base_completer.py
+++ b/tests/completers/test_base_completer.py
@@ -26,7 +26,7 @@ def test_empty_line():
         )
     )
     assert completions
-    for exp in ["cool", CUR_DIR, "abs"]:
+    for exp in ["cool", "abs"]:
         assert exp in completions
 
 
@@ -38,6 +38,6 @@ def test_empty_subexpr():
         )
     )
     assert completions
-    for exp in ["cool", CUR_DIR]:
+    for exp in ["cool"]:
         assert exp in completions
     assert "abs" not in completions
diff --git a/tests/completers/test_dir_completers.py b/tests/completers/test_dir_completers.py
--- a/tests/completers/test_dir_completers.py
+++ b/tests/completers/test_dir_completers.py
@@ -16,6 +16,8 @@
     "rmdir": complete_rmdir,
 }
 
+CUR_DIR = "." if ON_WINDOWS else "./"
+PARENT_DIR = ".." if ON_WINDOWS else "../"
 
 @pytest.fixture(autouse=True)
 def setup(xonsh_builtins, xonsh_execer):
@@ -48,6 +50,10 @@ def complete_cmd(cmd, prefix, opening_quote="", closing_quote=""):
     return completions
 
 
+def complete_cmd_dirs(*a, **kw):
+    return [r.value for r in complete_cmd(*a, **kw)]
+
+
 def test_non_dir(cmd):
     with tempfile.NamedTemporaryFile(dir=".", suffix="_dummySuffix") as tmp:
         with pytest.raises(StopIteration):  # tmp is a file
@@ -84,3 +90,26 @@ def test_closing_quotes(cmd, dir_path):
     completion = completions.pop()
     assert isinstance(completion, RichCompletion)
     assert completion.append_closing_quote is False
+
+
+def test_complete_dots(xonsh_builtins):
+    with xonsh_builtins.__xonsh__.env.swap(COMPLETE_DOTS='never'):
+        dirs = complete_cmd_dirs('cd', '')
+        assert CUR_DIR not in dirs and PARENT_DIR not in dirs
+
+        dirs = complete_cmd_dirs('cd', '.')
+        assert CUR_DIR not in dirs and PARENT_DIR not in dirs
+
+    with xonsh_builtins.__xonsh__.env.swap(COMPLETE_DOTS='matching'):
+        dirs = complete_cmd_dirs('cd', '')
+        assert CUR_DIR not in dirs and PARENT_DIR not in dirs
+
+        dirs = complete_cmd_dirs('cd', '.')
+        assert CUR_DIR in dirs and PARENT_DIR in dirs
+
+    with xonsh_builtins.__xonsh__.env.swap(COMPLETE_DOTS='always'):
+        dirs = complete_cmd_dirs('cd', '')
+        assert CUR_DIR in dirs and PARENT_DIR in dirs
+
+        dirs = complete_cmd_dirs('cd', '.')
+        assert CUR_DIR in dirs and PARENT_DIR in dirs