Repository: theacodes/nox (python)

Issue:
List all sections when a list of sections is set using nox.options
**Describe the bug**
Setting `nox.options.sessions` results in `nox -l` only those sections, not all the sections.

**How to reproduce**
Create a `noxfile.py` with 3 sessions (format, tests, docs) and set `nox.options.sessions = ["format"]`, then run `nox -l`:

```
Available sessions:
* format
```

**Expected behavior**
It would be better if `nox -l` would actually list all the sessions.

---

Related Feature Request: The sessions that are run by default be marked with a `*` while the others won't have a bullet:

```
Available sessions:
* format
  tests
  docs
```



Pull request description:
Make --list list all available sessions, not just the selected ones.
Closes #161

Patch:
diff --git a/nox/__main__.py b/nox/__main__.py
index 24fd899..093d0f7 100644
--- a/nox/__main__.py
+++ b/nox/__main__.py
@@ -57,6 +57,7 @@ class GlobalConfig:
         self.install_only = args.install_only
         self.posargs = args.posargs
         self.report = args.report
+        self.color = not args.nocolor or args.forcecolor
 
         if self.posargs and self.posargs[0] == "--":
             self.posargs.pop(0)
@@ -242,7 +243,7 @@ def main():
         return
 
     global_config = GlobalConfig(args)
-    setup_logging(color=not args.nocolor or args.forcecolor)
+    setup_logging(color=global_config.color)
 
     # Execute the appropriate tasks.
     exit_code = workflow.execute(
diff --git a/nox/manifest.py b/nox/manifest.py
index 534367c..ec29ea8 100644
--- a/nox/manifest.py
+++ b/nox/manifest.py
@@ -94,6 +94,11 @@ class Manifest:
     def __len__(self):
         return len(self._queue) + len(self._consumed)
 
+    def list_all_sessions(self):
+        """Yields all sessions and whether or not they're selected."""
+        for session in self._all_sessions:
+            yield session, session in self._queue
+
     def add_session(self, session):
         """Add the given session to the manifest.
 
diff --git a/nox/tasks.py b/nox/tasks.py
index f392f65..5256b42 100644
--- a/nox/tasks.py
+++ b/nox/tasks.py
@@ -17,6 +17,8 @@ import io
 import json
 import os
 
+from colorlog.escape_codes import parse_colors
+
 from nox import _options
 from nox import registry
 from nox.logger import logger
@@ -132,21 +134,47 @@ def honor_list_request(manifest, global_config):
         Union[~.Manifest,int]: ``0`` if a listing is all that is requested,
             the manifest otherwise (to be sent to the next task).
     """
+    if not global_config.list_sessions:
+        return manifest
+
     # If the user just asked for a list of sessions, print that
     # and be done.
-    if global_config.list_sessions:
-        print("Available sessions:")
-        for session in manifest:
-            output = "* {session}"
-            if session.description is not None:
-                output += " -> {description}"
-            print(
-                output.format(
-                    session=session.friendly_name, description=session.description
-                )
+
+    print("Sessions defined in {noxfile}:\n".format(noxfile=global_config.noxfile))
+
+    reset = parse_colors("reset") if global_config.color else ""
+    selected_color = parse_colors("cyan") if global_config.color else ""
+    skipped_color = parse_colors("white") if global_config.color else ""
+
+    for session, selected in manifest.list_all_sessions():
+        output = "{marker} {color}{session}{reset}"
+
+        if selected:
+            marker = "*"
+            color = selected_color
+        else:
+            marker = "-"
+            color = skipped_color
+
+        if session.description is not None:
+            output += " -> {description}"
+
+        print(
+            output.format(
+                color=color,
+                reset=reset,
+                session=session.friendly_name,
+                description=session.description,
+                marker=marker,
             )
-        return 0
-    return manifest
+        )
+
+    print(
+        "\nsessions marked with {selected_color}*{reset} are selected, sessions marked with {skipped_color}-{reset} are skipped.".format(
+            selected_color=selected_color, skipped_color=skipped_color, reset=reset
+        )
+    )
+    return 0
 
 
 def verify_manifest_nonempty(manifest, global_config):


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
index 5c43652..861d6a8 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -51,6 +51,8 @@ class TestGlobalConfig:
             install_only=False,
             posargs=["a", "b", "c"],
             report=None,
+            nocolor=False,
+            forcecolor=False,
         )
 
     def test_constructor(self):
diff --git a/tests/test_manifest.py b/tests/test_manifest.py
index d679e8f..a5a9513 100644
--- a/tests/test_manifest.py
+++ b/tests/test_manifest.py
@@ -148,6 +148,19 @@ def test_filter_by_keyword():
     assert len(manifest) == 1
 
 
+def test_list_all_sessions_with_filter():
+    sessions = create_mock_sessions()
+    manifest = Manifest(sessions, mock.sentinel.CONFIG)
+    assert len(manifest) == 2
+    manifest.filter_by_keywords("foo")
+    assert len(manifest) == 1
+    all_sessions = list(manifest.list_all_sessions())
+    assert len(all_sessions) == 2
+    # Only one should be marked as selected.
+    assert all_sessions[0][1] is True
+    assert all_sessions[1][1] is False
+
+
 def test_add_session_plain():
     manifest = Manifest({}, mock.sentinel.CONFIG)
     session_func = mock.Mock(spec=(), python=None)
diff --git a/tests/test_tasks.py b/tests/test_tasks.py
index 1252266..c4d32d4 100644
--- a/tests/test_tasks.py
+++ b/tests/test_tasks.py
@@ -127,11 +127,30 @@ def test_honor_list_request_noop():
 
 @pytest.mark.parametrize("description", [None, "bar"])
 def test_honor_list_request(description):
-    config = argparse.Namespace(list_sessions=True)
-    manifest = [argparse.Namespace(friendly_name="foo", description=description)]
+    config = argparse.Namespace(list_sessions=True, noxfile="noxfile.py", color=False)
+    manifest = mock.create_autospec(Manifest)
+    manifest.list_all_sessions.return_value = [
+        (argparse.Namespace(friendly_name="foo", description=description), True)
+    ]
+    return_value = tasks.honor_list_request(manifest, global_config=config)
+    assert return_value == 0
+
+
+def test_honor_list_request_skip_and_selected(capsys):
+    config = argparse.Namespace(list_sessions=True, noxfile="noxfile.py", color=False)
+    manifest = mock.create_autospec(Manifest)
+    manifest.list_all_sessions.return_value = [
+        (argparse.Namespace(friendly_name="foo", description=None), True),
+        (argparse.Namespace(friendly_name="bar", description=None), False),
+    ]
     return_value = tasks.honor_list_request(manifest, global_config=config)
     assert return_value == 0
 
+    out = capsys.readouterr().out
+
+    assert "* foo" in out
+    assert "- bar" in out
+
 
 def test_verify_manifest_empty():
     config = argparse.Namespace(sessions=(), keywords=())