<?xml version="1.0" encoding="UTF-8" ?> <!-- <![CDATA[ Psalm XML to HTML renderer See - https://github.com/Roave/psalm-html-output ]]> --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="report"> <html> <head> <title>Psalm Report</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/themes/prism.css" rel="stylesheet" /> <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet" /> <style> html { font-family: sans-serif; } pre > code.language-php { font-size: 1em; } code[class*="language-"] .psalm-errored-code { background-color: #ce5353; color: white; text-shadow: none; } ul.tags { padding: 0; margin: 0; font-size: 0.9em; } ul.tags li { display: inline; background-color: #e5e6d9; padding: 3px 5px; border-radius: 5px; text-shadow: 0 1px white; margin-right: 10px; } .severity { text-transform: lowercase; } .severity.error { color: #ff0000; } .severity.warning { color: #ff6e00; } .severity.info { color: #0079ff; } ul#psalmErrors li.hidden { display: none; } </style> </head> <body> <section> <h1>Total violations: <xsl:value-of select="count(//report/item)" /></h1> </section> <form> <label> <select class="filterable" data-filter-type="severity"> <option value="">-- show all --</option> </select> <select class="filterable" data-filter-type="psalm-type"> <option value="">-- show all --</option> </select> </label> </form> <ul id="psalmErrors"> <xsl:apply-templates /> </ul> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/prism.min.js" /> <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/plugins/autoloader/prism-autoloader.min.js" />--> <!-- <script>Prism.plugins.autoloader.languages_path = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/components/'</script>--> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/components/prism-markup-templating.min.js" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/components/prism-php.min.js" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/plugins/line-numbers/prism-line-numbers.min.js" /> <script type="text/javascript"> <![CDATA[ Prism.hooks.add('before-highlight', function (env) { // Replace "//" temporarily inside strings as it seems to confuse Prism and causes some // /** comments later in the line to be treated as operators instead of comments env.code = env.code.replace(/('[^']*)\/\/([^']*')/gs, '$1/**/**/$2'); }) Prism.hooks.add('before-insert', function(env) { const magicTagMatch = /\/\*\*##\(##\*\*\/([\w\W]*)\/\*\*##\)##\*\*\//gs; const matchedCodeToBecomeRed = magicTagMatch.exec(env.highlightedCode); let codeToBecomeRed = matchedCodeToBecomeRed[1]; // Fairly naive assumption that PrismJS only uses </span> and <span class="..."> to highlight... codeToBecomeRed = codeToBecomeRed.replace(/(<\/span>)/g, '</span>$1'); codeToBecomeRed = codeToBecomeRed.replace(/(<span class="[^"]*">)/g, '$1<span class="psalm-errored-code">'); codeToBecomeRed = '<span class="psalm-errored-code">' + codeToBecomeRed + '</span>'; env.highlightedCode = env.highlightedCode .replace(magicTagMatch, codeToBecomeRed) .replace(/\/\*\*\/\*\*\//gs, '//'); }); function subtractUnmatchedItems(filterType, filterValue) { var items = document.querySelectorAll('#psalmErrors > li'); for (var i = 0; i < items.length; i++) { var itemTag = items[i].getAttribute('data-' + filterType); if (filterValue !== '' && itemTag !== filterValue) { items[i].setAttribute('class', 'hidden'); } } } function showAllItems() { var items = document.querySelectorAll('#psalmErrors > li'); for (var i = 0; i < items.length; i++) { items[i].setAttribute('class', ''); } } document.querySelectorAll('.filterable').forEach(function (itemToApplyClickHandlerTo) { var foundItems = []; var filterType = itemToApplyClickHandlerTo.getAttribute('data-filter-type'); var items = document.querySelectorAll('#psalmErrors > li'); var uniqueCounts = []; for (var i = 0; i < items.length; i++) { var itemAttributeValue = items[i].getAttribute('data-' + filterType); if (itemAttributeValue in uniqueCounts) { uniqueCounts[itemAttributeValue]++; } else { uniqueCounts[itemAttributeValue] = 1; } foundItems.push(itemAttributeValue); } var uniqueFoundItems = foundItems.filter(function (value, index, self) { return self.indexOf(value) === index; }); for (var i = 0; i < uniqueFoundItems.length; i++) { var newOpt = document.createElement('option'); newOpt.value = uniqueFoundItems[i]; newOpt.innerHTML = uniqueFoundItems[i] + ' (' + uniqueCounts[uniqueFoundItems[i]] + ')'; itemToApplyClickHandlerTo.appendChild(newOpt); } itemToApplyClickHandlerTo.onclick = function () { showAllItems(); document.querySelectorAll('.filterable').forEach(function (filteringDropdown) { subtractUnmatchedItems( filteringDropdown.getAttribute('data-filter-type'), filteringDropdown.options[filteringDropdown.selectedIndex].value ); }); }; }); ]]> </script> </body> </html> </xsl:template> <xsl:template match="item"> <li data-severity="{severity}" data-psalm-type="{type}"> <h3> <xsl:value-of select="file_name"/>:<xsl:value-of select="line_from"/> </h3> <ul class="tags"> <li class="severity {severity}"><xsl:value-of select="severity"/></li> <li><xsl:value-of select="type"/></li> </ul> <p><xsl:value-of select="message"/></p> <pre data-start='{line_from}' class="line-numbersx"> <code class="language-php"><xsl:value-of select="substring(snippet, 0, from - snippet_from + 1)" />/**##(##**/<xsl:value-of select="substring(snippet, from - snippet_from + 1, to - from)" />/**##)##**/<xsl:value-of select="substring(snippet, to - snippet_from + 1)" /></code> </pre> </li> </xsl:template> </xsl:stylesheet>