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 => + ` + ${messages[locale]['copy_to_clipboard']} + ` + 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}