Repository: geopandas/geopandas

Issue:
ENH: DOC: add source button for functions in docs
#### Is your feature request related to a problem?

Adding a source button for functions in the docs will make it easier to navigate the codebase.

#### Describe the solution you'd like

Match pandas documentation e.g. https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.assign.html compared to https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoSeries.bounds.html

![Screen Shot 2022-08-11 at 4 57 43 PM](https://user-images.githubusercontent.com/17162724/184241054-0cc74428-ed06-4359-8ea5-53c3df94d8de.png)





Discussion:

Resolving patch:
diff --git a/doc/source/conf.py b/doc/source/conf.py
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -14,6 +14,10 @@
 import sys, os
 import warnings
 
+sys.path.insert(0, os.path.abspath("../.."))
+
+import geopandas  # noqa
+
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
@@ -33,6 +37,7 @@
     "sphinx.ext.autosummary",
     "sphinx.ext.intersphinx",
     "sphinx.ext.autodoc",
+    "sphinx.ext.linkcode",
     "myst_parser",
     "nbsphinx",
     "numpydoc",
@@ -85,8 +90,8 @@ def setup(app):
 master_doc = "index"
 
 # General information about the project.
-project = u"GeoPandas"
-copyright = u"2013–2022, GeoPandas developers"
+project = "GeoPandas"
+copyright = "2013–2022, GeoPandas developers"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -260,7 +265,7 @@ def setup(app):
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-    ("index", "GeoPandas.tex", u"GeoPandas Documentation", u"Kelsey Jordahl", "manual")
+    ("index", "GeoPandas.tex", "GeoPandas Documentation", "Kelsey Jordahl", "manual")
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -288,7 +293,7 @@ def setup(app):
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [("index", "geopandas", u"GeoPandas Documentation", [u"Kelsey Jordahl"], 1)]
+man_pages = [("index", "geopandas", "GeoPandas Documentation", ["Kelsey Jordahl"], 1)]
 
 # If true, show URL addresses after external links.
 # man_show_urls = False
@@ -303,8 +308,8 @@ def setup(app):
     (
         "index",
         "GeoPandas",
-        u"GeoPandas Documentation",
-        u"Kelsey Jordahl",
+        "GeoPandas Documentation",
+        "Kelsey Jordahl",
         "GeoPandas",
         "One line description of project.",
         "Miscellaneous",
@@ -418,3 +423,68 @@ def setup(app):
         "https://xyzservices.readthedocs.io/en/stable/objects.inv",
     ),
 }
+
+
+# based on pandas implementation with added support of properties
+def linkcode_resolve(domain, info):
+    """
+    Determine the URL corresponding to Python object
+    """
+    import inspect
+
+    if domain != "py":
+        return None
+
+    modname = info["module"]
+    fullname = info["fullname"]
+
+    submod = sys.modules.get(modname)
+    if submod is None:
+        return None
+
+    obj = submod
+    for part in fullname.split("."):
+        try:
+            with warnings.catch_warnings():
+                # Accessing deprecated objects will generate noisy warnings
+                warnings.simplefilter("ignore", FutureWarning)
+                obj = getattr(obj, part)
+        except AttributeError:
+            return None
+
+    try:
+        fn = inspect.getsourcefile(inspect.unwrap(obj))
+    except TypeError:
+        try:  # property
+            fn = inspect.getsourcefile(inspect.unwrap(obj.fget))
+        except AttributeError:
+            fn = None
+    if not fn:
+        return None
+
+    try:
+        source, lineno = inspect.getsourcelines(obj)
+    except TypeError:
+        try:  # property
+            source, lineno = inspect.getsourcelines(obj.fget)
+        except AttributeError:
+            lineno = None
+    except OSError:
+        lineno = None
+
+    if lineno:
+        linespec = f"#L{lineno}-L{lineno + len(source) - 1}"
+    else:
+        linespec = ""
+
+    fn = os.path.relpath(fn, start=os.path.dirname(geopandas.__file__))
+
+    if "+" in geopandas.__version__:
+        return (
+            f"https://github.com/geopandas/geopandas/blob/main/geopandas/{fn}{linespec}"
+        )
+    else:
+        return (
+            f"https://github.com/geopandas/geopandas/blob/"
+            f"v{geopandas.__version__}/geopandas/{fn}{linespec}"
+        )


Test patch: