docs: Update Sphinx; remove Markdown; add CSS

This completes the update of Sphinx and RST by removing the legacy
Markdown support. It also adds custom CSS skeleton.

Change-Id: I398a21208772cf2d2dd32dee0adac768630dd575
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/40621
Commit-Queue: Keir Mierle <keir@google.com>
Pigweed-Auto-Submit: Keir Mierle <keir@google.com>
Reviewed-by: Anthony DiGirolamo <tonymd@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index 43859c6..f0f9aa2 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -26,6 +26,9 @@
     "images/pw_watch_on_device_demo.gif",
     "images/pw_watch_test_demo.gif",
     "images/stm32f429i-disc1_connected.jpg",
+
+    # TODO(pwbug/368): This should be in the pw_doc_gen target instead of here.
+    "_static/css/pigweed.css",
   ]
   sources = [
     "code_of_conduct.rst",
@@ -140,6 +143,8 @@
 pw_doc_gen("docs") {
   conf = "conf.py"
   sources = [
+    # Note: These must use the "docs" prefix for links and image references. In
+    # contrast, the pw_doc_group above should not use the docs prefix.
     "build_system.rst",
     "index.rst",
     "module_guides.rst",
diff --git a/docs/_static/css/pigweed.css b/docs/_static/css/pigweed.css
new file mode 100644
index 0000000..030b89a
--- /dev/null
+++ b/docs/_static/css/pigweed.css
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021 The Pigweed Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/********** Top left logo & search bar ***********/
+
+/* Hide the version string since we're not yet using it. */
+.wy-side-nav-search div.version {
+  display: none;
+}
+
+/* Hide the little house icon */
+.wy-side-nav-search a.icon.icon-home::before {
+  display: none;
+}
+
+/* Make the "Pigweed" logo text. One day, this will be an image. */
+.wy-side-nav-search a {
+  font-size: 2em;
+  font-family: 'Inconsolata', monospace;
+  letter-spacing: 0.1em;
+  text-transform: uppercase;
+}
+
+/* Make the logo background more amaranth-like */
+.wy-side-nav-search {
+  background-color: #e815a5;
+}
+
+/* The input box has a subtle outline; make it match the background */
+.wy-side-nav-search input[type=text] {
+  border-color: #b529aa;
+}
+
+/********** General document coloring ***********/
+
+/* Re-color the fixed width text away from the red color to something more
+ * gentle, that aligns with the Pigweed colored theme. */
+.rst-content code.literal, .rst-content tt.literal {
+    color: #b756bd;
+}
+
diff --git a/docs/conf.py b/docs/conf.py
index 7eaf374..3568fea 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -16,7 +16,7 @@
 import sphinx_rtd_theme
 
 # The suffix of source filenames.
-source_suffix = ['.rst', '.md']
+source_suffix = ['.rst']
 
 # The master toctree document.
 master_doc = 'index'
@@ -40,7 +40,6 @@
 extensions = [
     'sphinx.ext.autodoc',  # Automatic documentation for Python code
     'sphinx.ext.napoleon',  # Parses Google-style docstrings
-    'm2r',  # Converts Markdown to reStructuredText
 
     # Blockdiag suite of diagram generators.
     'sphinxcontrib.blockdiag',
@@ -96,6 +95,18 @@
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
 html_show_sphinx = False
 
+# These folders are copied to the documentation's HTML output
+html_static_path = ['docs/_static']
+
+# These paths are either relative to html_static_path
+# or fully qualified paths (eg. https://...)
+html_css_files = [
+    'css/pigweed.css',
+
+    # Needed for Inconsolata font.
+    'https://fonts.googleapis.com/css2?family=Inconsolata&display=swap',
+]
+
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'Pigweeddoc'
 
@@ -111,17 +122,20 @@
      'Miscellaneous'),
 ]
 
-# Markdown files imported using m2r aren't marked as "referenced," so exclude
-# them from the error reference checking.
-exclude_patterns = ['README.md']
-
 
 def do_not_skip_init(app, what, name, obj, would_skip, options):
     if name == "__init__":
         return False  # never skip __init__ functions
-
     return would_skip
 
 
+# Problem: CSS files aren't copied after modifying them. Solution:
+# https://github.com/sphinx-doc/sphinx/issues/2090#issuecomment-572902572
+def env_get_outdated(app, env, added, changed, removed):
+    return ['index']
+
+
 def setup(app):
+    app.add_css_file('css/pigweed.css')
+    app.connect('env-get-outdated', env_get_outdated)
     app.connect("autodoc-skip-member", do_not_skip_init)
diff --git a/pw_docgen/py/setup.py b/pw_docgen/py/setup.py
index e14a315..45d273a 100644
--- a/pw_docgen/py/setup.py
+++ b/pw_docgen/py/setup.py
@@ -25,10 +25,9 @@
     package_data={'pw_docgen': ['py.typed']},
     zip_safe=False,
     install_requires=[
-        'sphinx>=2, <3',  # Restrict version for compatibility with m2r plugin
+        'sphinx >3',
         'sphinx-rtd-theme',
-        # Markdown to REST for documentation.
-        'm2r',
+
         # Diagram generation modules.
         'sphinxcontrib-actdiag',
         'sphinxcontrib-blockdiag',
diff --git a/pw_interrupt/docs.rst b/pw_interrupt/docs.rst
index 2f1b40b..1bc7d21 100644
--- a/pw_interrupt/docs.rst
+++ b/pw_interrupt/docs.rst
@@ -7,6 +7,7 @@
 whether one is currently executing in an interrupt context (IRQ or NMI) or not.
 
 .. c:function:: bool InInterruptContext()
+
   Returns true if currently executing within an interrupt service routine
   handling an IRQ or NMI.:w!
 
diff --git a/pw_log/docs.rst b/pw_log/docs.rst
index c36490b..2a2b0a9 100644
--- a/pw_log/docs.rst
+++ b/pw_log/docs.rst
@@ -202,7 +202,7 @@
        PW_LOG_WARN("This is above INFO level, and will display");
      }
 
-.. c:function:: PW_LOG_ENABLE_IF(level, flags)
+.. c:macro:: PW_LOG_ENABLE_IF(level, flags)
 
    Filters logs by an arbitrary expression based on ``level`` and ``flags``.
    Source files that define ``PW_LOG_ENABLE_IF(level, flags)`` will display if
diff --git a/pw_log_basic/docs.rst b/pw_log_basic/docs.rst
index 985ec50..7bdf8c5 100644
--- a/pw_log_basic/docs.rst
+++ b/pw_log_basic/docs.rst
@@ -13,7 +13,7 @@
 
 .. cpp:namespace:: pw::log_basic
 
-.. cpp:function:: void SetOutput(void (\*log_output)(std::string_view))
+.. cpp:function:: void SetOutput(void (*log_output)(std::string_view))
 
   Set the log output function, which defaults ``pw_sys_io::WriteLine``. This
   function is called with each formatted log message.
diff --git a/pw_log_tokenized/docs.rst b/pw_log_tokenized/docs.rst
index 3c100ed..206c87e 100644
--- a/pw_log_tokenized/docs.rst
+++ b/pw_log_tokenized/docs.rst
@@ -46,7 +46,19 @@
 ``PW_LOG_TOKENIZED_ENCODE_MESSAGE`` config macro. This macro should take
 arguments equivalent to ``PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD``:
 
-  .. c:function:: PW_LOG_TOKENIZED_ENCODE_MESSAGE(pw_tokenizer_Payload log_metadata, const char* message, ...)
+.. c:macro:: PW_LOG_TOKENIZED_ENCODE_MESSAGE(log_metadata, message, ...)
+
+  :param log_metadata:
+
+    Packed metadata for the log message. See the Metadata_ class for how to
+    unpack the details.
+
+  :type log_metadata: pw_tokenizer_Payload
+
+  :param message: The log message format string (untokenized)
+  :type message: :c:texpr:`const char*`
+
+  .. _Metadata: https://cs.opensource.google/pigweed/pigweed/+/master:pw_log_tokenized/public/pw_log_tokenized/log_tokenized.h;l=113
 
 For instructions on how to implement a custom tokenization macro, see
 :ref:`module-pw_tokenizer-custom-macro`.
diff --git a/pw_preprocessor/docs.rst b/pw_preprocessor/docs.rst
index 070a2bd..3f5b13c 100644
--- a/pw_preprocessor/docs.rst
+++ b/pw_preprocessor/docs.rst
@@ -18,7 +18,7 @@
 Defines macros for handling variadic arguments to function-like macros. Macros
 include the following:
 
-.. c:function:: PW_DELEGATE_BY_ARG_COUNT(name, ...)
+.. c:macro:: PW_DELEGATE_BY_ARG_COUNT(name, ...)
 
   Selects and invokes a macro based on the number of arguments provided. Expands
   to ``<name><arg_count>(...)``. For example,
@@ -45,7 +45,7 @@
       ARG_PRINT("a", "b");       // Outputs: 2 args: a, b
       ARG_PRINT("a", "b", "c");  // Outputs: 3 args: a, b, c
 
-.. c:function:: PW_COMMA_ARGS(...)
+.. c:macro:: PW_COMMA_ARGS(...)
 
   Expands to a comma followed by the arguments if any arguments are provided.
   Otherwise, expands to nothing. If the final argument is empty, it is omitted.
diff --git a/pw_watch/py/pw_watch/watch.py b/pw_watch/py/pw_watch/watch.py
index 87acd7f..7909162 100755
--- a/pw_watch/py/pw_watch/watch.py
+++ b/pw_watch/py/pw_watch/watch.py
@@ -330,6 +330,7 @@
     '*.bloaty',
     '*.c',
     '*.cc',
+    '*.css',
     '*.cpp',
     '*.cmake',
     'CMakeLists.txt',