Repository: nilearn/nilearn

Issue:
Add link to source code in API pages
<!--Describe your proposed enhancement in detail.-->
I think it would be helpful to users who are interested in nilearn's source code to link back to GitHub from each class or function's documentation. `scikit-learn`'s [`github_link.py`](https://github.com/scikit-learn/scikit-learn/blob/4023a0f94bde429456f45b983c84c5f35475480f/doc/sphinxext/github_link.py) could be adapted and used.

<!--List any pages that would be impacted by the enhancement.-->
### Affected pages
All reference pages (`http://nilearn.github.io/modules/generated/*`)


Discussion:
Interested in this one

Resolving patch:
diff --git a/doc/conf.py b/doc/conf.py
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -37,6 +37,7 @@
 # absolute, like shown here.
 sys.path.insert(0, os.path.abspath('sphinxext'))
 import sphinx_gallery
+from github_link import make_linkcode_resolve
 
 # We also add the directory just above to enable local imports of nilearn
 sys.path.insert(0, os.path.abspath('..'))
@@ -53,6 +54,7 @@
               'sphinx.ext.intersphinx',
               'sphinxcontrib.bibtex',
               'numpydoc',
+              'sphinx.ext.linkcode',
               ]
 
 autosummary_generate = True
@@ -354,3 +356,12 @@ def touch_example_backreferences(app, what, name, obj, options, lines):
 def setup(app):
     app.add_javascript('copybutton.js')
     app.connect('autodoc-process-docstring', touch_example_backreferences)
+
+
+
+# The following is used by sphinx.ext.linkcode to provide links to github
+linkcode_resolve = make_linkcode_resolve('nilearn',
+                                         'https://github.com/nilearn/'
+                                         'nilearn/blob/{revision}/'
+                                         '{package}/{path}#L{lineno}') 
+
diff --git a/doc/sphinxext/github_link.py b/doc/sphinxext/github_link.py
new file mode 100644
--- /dev/null
+++ b/doc/sphinxext/github_link.py
@@ -0,0 +1,89 @@
+from operator import attrgetter
+import inspect
+import subprocess
+import os
+import sys
+from functools import partial
+
+REVISION_CMD = 'git rev-parse --short HEAD'
+
+
+def _get_git_revision():
+    try:
+        revision = subprocess.check_output(REVISION_CMD.split()).strip()
+    except (subprocess.CalledProcessError, OSError):
+        print('Failed to execute git to get revision')
+        return None
+    return revision.decode('utf-8')
+
+
+def _linkcode_resolve(domain, info, package, url_fmt, revision):
+    """Determine a link to online source for a class/method/function
+
+    This is called by sphinx.ext.linkcode
+
+    An example with a long-untouched module that everyone has
+    >>> _linkcode_resolve('py', {'module': 'tty',
+    ...                          'fullname': 'setraw'},
+    ...                   package='tty',
+    ...                   url_fmt='http://hg.python.org/cpython/file/'
+    ...                           '{revision}/Lib/{package}/{path}#L{lineno}',
+    ...                   revision='xxxx')
+    'http://hg.python.org/cpython/file/xxxx/Lib/tty/tty.py#L18'
+    """
+
+    if revision is None:
+        return
+    if domain not in ('py', 'pyx'):
+        return
+    if not info.get('module') or not info.get('fullname'):
+        return
+
+    class_name = info['fullname'].split('.')[0]
+    module = __import__(info['module'], fromlist=[class_name])
+    obj = attrgetter(info['fullname'])(module)
+
+    # Unwrap the object to get the correct source
+    # file in case that is wrapped by a decorator
+    obj = inspect.unwrap(obj)
+
+    try:
+        fn = inspect.getsourcefile(obj)
+    except Exception:
+        fn = None
+    if not fn:
+        try:
+            fn = inspect.getsourcefile(sys.modules[obj.__module__])
+        except Exception:
+            fn = None
+    if not fn:
+        return
+    
+    # Don't include filenames from outside this package's tree
+    if os.path.dirname(__import__(package).__file__) not in fn:
+        return
+
+    fn = os.path.relpath(fn,
+                         start=os.path.dirname(__import__(package).__file__))
+    try:
+        lineno = inspect.getsourcelines(obj)[1]
+    except Exception:
+        lineno = ''
+    return url_fmt.format(revision=revision, package=package,
+                          path=fn, lineno=lineno)
+
+
+def make_linkcode_resolve(package, url_fmt):
+    """Returns a linkcode_resolve function for the given URL format
+
+    revision is a git commit reference (hash or name)
+
+    package is the name of the root module of the package
+
+    url_fmt is along the lines of ('https://github.com/USER/PROJECT/'
+                                   'blob/{revision}/{package}/'
+                                   '{path}#L{lineno}')
+    """
+    revision = _get_git_revision()
+    return partial(_linkcode_resolve, revision=revision, package=package,
+                   url_fmt=url_fmt)


Test patch: