diff --git a/Makefile b/Makefile
index b2a5481e90..14fdd8d0c0 100644
--- a/Makefile
+++ b/Makefile
@@ -241,7 +241,7 @@ GEN_DOCS = $(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O
( ! [ "$$GITHUB_SHA" ] || ! [ "$$GITHUB_REPOSITORY" ] || \
find $(BUILDDIR)/html/ -name '*.html' | \
xargs sed -i -e "s;github.com/intel/pmem-csi/\\(deploy/\\S*\\);github.com/$$GITHUB_REPOSITORY/\\1?ref=$$GITHUB_SHA;g" ) && \
- cp docs/html/index.html $(BUILDDIR)/html/index.html
+ cp docs/html/index.html $(BUILDDIR)/html/index.html && cp docs/js/copybutton.js $(BUILDDIR)/html/_static/copybutton.js
vhtml: _work/venv/.stamp
. _work/venv/bin/activate && $(GEN_DOCS)
html:
diff --git a/conf.json b/conf.json
index 5c0e50d727..7f38fce816 100644
--- a/conf.json
+++ b/conf.json
@@ -14,8 +14,10 @@
],
"extensions": [
"recommonmark",
- "sphinx_markdown_tables"
+ "sphinx_markdown_tables",
+ "sphinx_copybutton"
],
+ "copybutton_prompt_text": "$ ",
"html_theme": "sphinx_rtd_theme",
"project": "PMEM-CSI",
"templates_path": [
diff --git a/conf.py b/conf.py
index d83dbd5b6f..2c6b1811e7 100644
--- a/conf.py
+++ b/conf.py
@@ -4,6 +4,20 @@
from os.path import isdir, isfile, join, basename, dirname
from os import makedirs, getenv
from shutil import copyfile
+#support for modified code block
+from pygments.lexers.shell import BashSessionLexer
+from sphinx.highlighting import lexers
+
+#############
+#
+# Add a special lexer to add a class to console lexer
+#
+#############
+
+class copyAllConsole (BashSessionLexer):
+ name = 'ShellSession'
+
+lexers['ShellSession'] = copyAllConsole(startinLine=True)
##############################################################################
#
diff --git a/docs/js/copybutton.js b/docs/js/copybutton.js
new file mode 100644
index 0000000000..3200366d6e
--- /dev/null
+++ b/docs/js/copybutton.js
@@ -0,0 +1,147 @@
+// introduces special behavior for ShellSession
+
+// Localization support
+const messages = {
+ 'en': {
+ 'copy': 'Copy',
+ 'copy_to_clipboard': 'Copy to clipboard',
+ 'copy_success': 'Copied!',
+ 'copy_failure': 'Failed to copy',
+ },
+ 'es' : {
+ 'copy': 'Copiar',
+ 'copy_to_clipboard': 'Copiar al portapapeles',
+ 'copy_success': '¡Copiado!',
+ 'copy_failure': 'Error al copiar',
+ },
+ 'de' : {
+ 'copy': 'Kopieren',
+ 'copy_to_clipboard': 'In die Zwischenablage kopieren',
+ 'copy_success': 'Kopiert!',
+ 'copy_failure': 'Fehler beim Kopieren',
+ }
+}
+
+let locale = 'en'
+if( document.documentElement.lang !== undefined
+ && messages[document.documentElement.lang] !== undefined ) {
+ locale = document.documentElement.lang
+}
+
+/**
+ * Set up copy/paste for code blocks
+ */
+
+const runWhenDOMLoaded = cb => {
+ if (document.readyState != 'loading') {
+ cb()
+ } else if (document.addEventListener) {
+ document.addEventListener('DOMContentLoaded', cb)
+ } else {
+ document.attachEvent('onreadystatechange', function() {
+ if (document.readyState == 'complete') cb()
+ })
+ }
+}
+
+const codeCellId = index => `codecell${index}`
+
+// Clears selected text since ClipboardJS will select the text when copying
+const clearSelection = () => {
+ if (window.getSelection) {
+ window.getSelection().removeAllRanges()
+ } else if (document.selection) {
+ document.selection.empty()
+ }
+}
+
+// Changes tooltip text for two seconds, then changes it back
+const temporarilyChangeTooltip = (el, newText) => {
+ const oldText = el.getAttribute('data-tooltip')
+ el.setAttribute('data-tooltip', newText)
+ setTimeout(() => el.setAttribute('data-tooltip', oldText), 2000)
+}
+
+// Callback when a copy button is clicked. Will be passed the node that was clicked
+// should then grab the text and replace pieces of text that shouldn't be used in output
+var copyTargetText = (trigger) => {
+ var target = document.querySelector(trigger.attributes['data-clipboard-target'].value);
+ var textContent = target.innerText.split('\n');
+ var copybuttonPromptText = '$ '; // Inserted from config
+ var onlyCopyPromptLines = true; // Inserted from config
+ var removePrompts = true; // Inserted from config
+
+ grandParent = target.parentElement.parentElement;
+ blockType = grandParent.classList;
+ if (blockType[0].includes("ShellSession")) {
+ onlyCopyPromptLines = false;
+ }
+
+ // Text content line filtering based on prompts (if a prompt text is given)
+ if (copybuttonPromptText.length > 0) {
+ // If only copying prompt lines, remove all lines that don't start w/ prompt
+ if (onlyCopyPromptLines) {
+ linesWithPrompt = textContent.filter((line) => {
+ return line.startsWith(copybuttonPromptText) || (line.length == 0); // Keep newlines
+ });
+ // Check to make sure we have at least one non-empty line
+ var nonEmptyLines = linesWithPrompt.filter((line) => {return line.length > 0});
+ // If we detected lines w/ prompt, then overwrite textContent w/ those lines
+ if ((linesWithPrompt.length > 0) && (nonEmptyLines.length > 0)) {
+ textContent = linesWithPrompt;
+ }
+ }
+ // Remove the starting prompt from any remaining lines
+ if (removePrompts) {
+ textContent.forEach((line, index) => {
+ if (line.startsWith(copybuttonPromptText)) {
+ textContent[index] = line.slice(copybuttonPromptText.length);
+ }
+ });
+ }
+ }
+ textContent = textContent.join('\n');
+ // Remove a trailing newline to avoid auto-running when pasting
+ if (textContent.endsWith("\n")) {
+ textContent = textContent.slice(0, -1)
+ }
+ return textContent
+}
+
+const addCopyButtonToCodeCells = () => {
+ // If ClipboardJS hasn't loaded, wait a bit and try again. This
+ // happens because we load ClipboardJS asynchronously.
+ if (window.ClipboardJS === undefined) {
+ setTimeout(addCopyButtonToCodeCells, 250)
+ return
+ }
+
+ // Add copybuttons to all of our code cells
+ const codeCells = document.querySelectorAll('div.highlight pre')
+ codeCells.forEach((codeCell, index) => {
+ const id = codeCellId(index)
+ codeCell.setAttribute('id', id)
+ const pre_bg = getComputedStyle(codeCell).backgroundColor;
+
+ const clipboardButton = id =>
+ `
+
+ `
+ codeCell.insertAdjacentHTML('afterend', clipboardButton(id))
+ })
+
+ // Initialize with a callback so we can modify the text before copy
+ const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText})
+
+ // Update UI with error/success messages
+ clipboard.on('success', event => {
+ clearSelection()
+ temporarilyChangeTooltip(event.trigger, messages[locale]['copy_success'])
+ })
+
+ clipboard.on('error', event => {
+ temporarilyChangeTooltip(event.trigger, messages[locale]['copy_failure'])
+ })
+}
+
+runWhenDOMLoaded(addCopyButtonToCodeCells)
\ No newline at end of file
diff --git a/docs/requirements.txt b/docs/requirements.txt
index f699322bcd..bdc8d57374 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,5 @@
sphinx
sphinx_rtd_theme
recommonmark
-sphinx-markdown-tables
\ No newline at end of file
+sphinx-markdown-tables
+sphinx-copybutton
diff --git a/examples/gce.md b/examples/gce.md
index 03cbb43f85..66622f2bcd 100644
--- a/examples/gce.md
+++ b/examples/gce.md
@@ -39,9 +39,9 @@ and check out the source code with `repo sync`.
Before proceeding, apply the following patch:
-```sh
-cd src/overlays
-patch -p1 <
Date: Fri Aug 2 10:46:30 2019 +0200
@@ -269,7 +269,7 @@ kubectl create -f https://raw.githubusercontent.com/intel/pmem-csi/v0.5.0/deploy
It is expected that `my-csi-app-2` will never start because the COS
kernel lacks support for xfs. But `my-csi-app-1` comes up:
-```sh
+```console
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-0c2ebc68-cd77-4c08-9fbb-f8a5d33440b9 4Gi RWO Delete Bound default/pmem-csi-pvc-ext4 pmem-csi-sc-ext4 2m52s
diff --git a/make.bat b/make.bat
index 47017a45a4..4b8b91206f 100644
--- a/make.bat
+++ b/make.bat
@@ -32,8 +32,8 @@ goto end
:html
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
-python.exe .\fix-refs.py
-copy index.html %BUILDDIR%\html\index.html
+copy docs\html\index.html %BUILDDIR%\html\index.html
+copy docs\js\copybutton.js %BUILDDIR%\html\_static\copybutton.js
goto end
:help
diff --git a/tox.ini b/tox.ini
index 145dffed52..6c38ca7b7d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,7 +6,7 @@ platform = mylinux: linux
mywindows: win32
whitelist_externals = make.bat
/usr/bin/make
-deps = -rrequirements.txt
+deps = -rdocs/requirements.txt
commands =
mylinux: make {posargs}
mywindows: make.bat {posargs}