From 0b807ce01b7e25a7c911d7de9331a04e96747ce3 Mon Sep 17 00:00:00 2001 From: Rodrigo Fernandes Date: Sat, 30 Aug 2014 03:54:52 +0100 Subject: [PATCH] initial lines --- LICENSE | 20 +++ README.md | 38 ++++++ diff2html.js | 364 ++++++++++++++++++++++++++------------------------ style.css | 115 +++++++++++++--- template.html | 54 +++++++- 5 files changed, 395 insertions(+), 196 deletions(-) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f676a81 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright 2014 Rodrigo Fernandes https://rtfpessoa.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bafba7d --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# Diff to Html by [rtfpessoa](https://github.com/rtfpessoa) + +Diff to Html generates pretty HTML diffs from regular command line tools output. + +### Inspiration + +This project is inspired in [pretty-diff](https://github.com/scottgonzalez/pretty-diff) by [Scott González](https://github.com/scottgonzalez). +This project started with some code from `pretty-diff` but it was then re-written and is now a +browser library. + +## Features + +* line-by-line diff (not side-by-side) + +* new and old line numbers + +* GitHub like style + +## Setup + +> Simply import the diff2html.js invoke: + + var diff2Html = Diff2Html.getInstance(); + diff2Html.generatePrettyDiff(exInput); + +> Check out the `template.html` for a complete example. + +## Contribution + +All the contributions are welcome. + +To contribute just send a pull request with your feature,fix,... and it will be reviewed asap. + +## License + +Copyright 2014 Rodrigo Fernandes. Released under the terms of the MIT license. + +--- diff --git a/diff2html.js b/diff2html.js index 57c6b23..0a1756c 100644 --- a/diff2html.js +++ b/diff2html.js @@ -1,188 +1,210 @@ /* * - * Diff(git) to HTML + * Diff to HTML (diff2html.js) * Author: rtfpessoa * Date: Friday 29 August 2014 * */ -var exInput = 'diff --git a/conf/load-analysis.conf b/conf/load-analysis.conf\n' + -'new file mode 100644\n' + -'index 0000000..b170d2b\n' + -'--- /dev/null\n' + -'+++ b/conf/load-analysis.conf\n' + -'@@ -0,0 +1,5 @@\n' + -'+include "load-master-analysis.conf"\n' + -'+\n' + -'+\n' + -'+# if should manage tasks\n' + -'+codacy.isTaskManager=false\n' + -'diff --git a/conf/load-master-analysis.conf b/conf/load-master-analysis.conf\n' + -'new file mode 100644\n' + -'index 0000000..53e049f\n' + -'--- /dev/null\n' + -'+++ b/conf/load-master-analysis.conf\n' + -'@@ -0,0 +1,26 @@\n' + -'+include "load.conf"\n' + -'+\n' + -'+# Sockets\n' + -'+# ~~~~~\n' + -'+codacy.sockets.remote=true\n' + -'+codacy.sockets.url="http://load.codacy.com/socket/notify"\n' + -'+\n' + -'+# enables the analysis server to run\n' + -'+codacy.isAnalysisServer=true\n' + -'+# if should manage analysis server work\n' + -'+codacy.isServerManager.active=false\n' + -'+# if should manage tasks\n' + -'+codacy.isTaskManager=true\n' + -'+\n' + -'+\n' + -'+# enables the repositoryListener to run\n' + -'+codacy.isRepositoryListener=true\n' + -'+#enables the payment system pooling for stripe payments\n' + -'+codacy.payments.active=false\n' + -'+\n' + -'+#third party\n' + -'+codacy.thirdPartyNotification.akka.active=true\n' + -'+codacy.thirdPartyNotification.users.active=false\n' + -'+\n' + -'+# DB values\n' + -'+db.default.hikaricp.file="conf/database/hikaricp.production-server.properties"\n' + -'diff --git a/conf/load-website.conf b/conf/load-website.conf\n' + -'new file mode 100644\n' + -'index 0000000..aa43adb\n' + -'--- /dev/null\n' + -'+++ b/conf/load-website.conf\n' + -'@@ -0,0 +1,26 @@\n' + -'+include "load.conf"\n' + -'+\n' + -'+# Sockets\n' + -'+# ~~~~~\n' + -'+codacy.sockets.remote=false\n' + -'+codacy.sockets.url=""\n' + -'+\n' + -'+# enables the analysis server to run\n' + -'+codacy.isAnalysisServer=false\n' + -'+# if should manage analysis server work\n' + -'+codacy.isServerManager.active=true\n' + -'+# if should manage tasks\n' + -'+codacy.isTaskManager=false\n' + -'+\n' + -'+\n' + -'+# enables the repositoryListener to run\n' + -'+codacy.isRepositoryListener=false\n' + -'+#enables the payment system pooling for stripe payments\n' + -'+codacy.payments.active=true\n' + -'+\n' + -'+#third party\n' + -'+codacy.thirdPartyNotification.akka.active=false\n' + -'+codacy.thirdPartyNotification.users.active=true\n' + -'+\n' + -'+#DB values\n' + -'+db.default.hikaricp.file="conf/database/hikaricp.production-website.properties"\n' + -'diff --git a/conf/load.conf b/conf/load.conf\n' + -'new file mode 100644\n' + -'index 0000000..8463b41\n' + -'--- /dev/null\n' + -'+++ b/conf/load.conf\n' + -'@@ -0,0 +1,27 @@\n' + -'+include "application.conf"\n' + -'+include "auth/integration.conf"\n' + -'+include "payments/test.conf"\n' + -'+include "actors/production.conf"\n' + -'+\n' + -'+# Application configuration\n' + -'+# ~~~~~~~~~~~~~~~~~~~~~~~~~\n' + -'+application.mode=prod\n' + -'+\n' + -'+#Prevents integration from sending emails. DO NOT CHANGE\n' + -'+mail.debug=true\n' + -'+\n' + -'+# Codacy\n' + -'+# ~~~~~\n' + -'+codacy.js.extension=".js"\n' + -'+codacy.keys.location="/data/codacy/keys/"\n' + -'+codacy.repository.location="/data/codacy/repos/"\n' + -'+codacy.secure=false\n' + -'+codacy.algorithms.location="public/javascripts/_engine/_algos/"\n' + -'+\n' + -'+codacy.url="http://load.codacy.com"\n' + -'+\n' + -'+db.default.maxActive=20\n' + -'+\n' + -'+db.default.url="jdbc:postgresql://loaddb.codacy.com:5432/codacy"\n' + -'+db.default.user="codacy"\n' + -'+db.default.password="codacy"'; +(function($, window) { + var ClassVariable; -function generatePrettyDiff( diffInput ) { - var diffHtml = ""; - var diffFiles = splitByFile( diffInput ); + ClassVariable = (function() { - for ( var file in diffFiles ) { - diffHtml += "

" + file + "

" + - "
" + - "
" + - markUpDiff( diffFiles[ file ] ) + - "
" + - "
"; - } + var CSS_STYLES = { + INFO: "info", + CONTEXT: "context", + NEW: "insert", + DELETED: "delete" + }; - return diffHtml; -} + var BLOCK_HEADER_LINE = "..." -function splitByFile( diffInput ) { - var filename, - isEmpty = true; - files = {}; + function Diff2Html() {} - diffInput.split( "\n" ).forEach(function( line, i ) { - // Unmerged paths, and possibly other non-diffable files - // https://github.com/scottgonzalez/pretty-diff/issues/11 - if ( !line || line.charAt( 0 ) === "*") { - return; - } + Diff2Html.prototype.generatePrettyDiff = function(diffInput) { + var diffFiles = splitByFile(diffInput); + var html = generateHtml(diffFiles); - if ( line.charAt( 0 ) === "d" ) { - isEmpty = false; - filename = line.replace( /^diff --git a\/(\S+) b\/(\S+).*$/, "$2" ); - files[ filename ] = []; - } else if ( line.indexOf( "diff --git" ) === 0 || - line.indexOf( "new file mode" ) === 0 || - line.indexOf( "index" ) === 0 || - line.indexOf( "---" ) === 0 || - line.indexOf( "+++" ) === 0) { - return; - } else { - files[ filename ].push( line ); - } - }); + return html; + }; - return isEmpty ? null : files; -} + var splitByFile = function(diffInput) { + var files = [], + currentFile = null, + currentBlock = null, + oldLine = null, + newLine = null; -var markUpDiff = function () { - var diffClasses = { - "d": "file", - "i": "file", - "@": "info", - "-": "delete", - "+": "insert", - " ": "context" - }; + diffInput.split("\n").forEach(function(line) { + // Unmerged paths, and possibly other non-diffable files + // https://github.com/scottgonzalez/pretty-diff/issues/11 + // Also, remove some useless lines + if (!line || line.charAt(0) === "*" || + line.indexOf("new") === 0 || + line.indexOf("index") === 0 || + line.indexOf("---") === 0 || + line.indexOf("+++") === 0) { + return; + } - function escape( str ) { - return str - .replace( /&/g, "&" ) - .replace( //g, ">" ) - .replace( /\t/g, " " ); - } + if (line.indexOf("diff") === 0) { + /* File Diff Line */ - return function( diff ) { - return diff.map(function( line ) { - var type = line.charAt( 0 ); - return "
" + escape( line ) + "
"; - }).join( "\n" ); - }; -}(); + /* add previous block(if exists) before start a new file */ + if (currentBlock) { + currentFile.blocks.push(currentBlock); + currentBlock = null; + } + + /* add previous file(if exists) before start a new one */ + if (currentFile) { + files.push(currentFile); + currentFile = null; + } + + /* create file structure */ + currentFile = {}; + currentFile.blocks = []; + + /* save file paths, before and after the diff */ + var values = /^diff --git a\/(\S+) b\/(\S+).*$/.exec(line); + currentFile.oldName = values[1]; + currentFile.newName = values[2]; + + } else if (line.indexOf("@@") === 0) { + /* Diff Block Header Line */ + + var values = /^(@@ -(\d+),(\d+) \+(\d+),(\d+) @@).*/.exec(line); + + /* add previous block(if exists) before start a new one */ + if (currentBlock) { + currentFile.blocks.push(currentBlock); + currentBlock = null; + } + + /* create block metadata */ + currentBlock = {}; + currentBlock.lines = []; + currentBlock.oldStartLine = oldLine = values[2]; + currentBlock.newStartLine = newLine = values[4]; + currentBlock.deletedLines = values[3]; + currentBlock.addedLines = values[5]; + + /* create block header line */ + var currentLine = {}; + currentLine.type = CSS_STYLES.INFO; + currentLine.content = line; + currentLine.oldNumber = BLOCK_HEADER_LINE; + currentLine.newNumber = BLOCK_HEADER_LINE; + + /* add line to block */ + currentBlock.lines.push(currentLine); + + } else { + /* Regular Diff Line */ + + var currentLine = {}; + currentLine.content = line; + + if (line.indexOf("+") === 0) { + currentLine.type = CSS_STYLES.NEW; + currentLine.oldNumber = null; + currentLine.newNumber = newLine++; + + } else if (line.indexOf("-") === 0) { + currentLine.type = CSS_STYLES.DELETED; + currentLine.oldNumber = oldLine++; + currentLine.newNumber = null; + + } else { + currentLine.type = CSS_STYLES.CONTEXT; + currentLine.oldNumber = oldLine++; + currentLine.newNumber = newLine++; + + } + + /* add line to block */ + currentBlock.lines.push(currentLine); + } + }); + + /* add previous block(if exists) before start a new file */ + if (currentBlock) { + currentFile.blocks.push(currentBlock); + currentBlock = null; + } + + /* add previous file(if exists) before start a new one */ + if (currentFile) { + files.push(currentFile); + currentFile = null; + } + + return files; + }; + + var generateHtml = function( diffFiles ) { + return diffFiles.map(function( file ) { + var fileHeader = file.oldName === file.newName ? file.newName : file.oldName + " -> " + file.newName + + return "
" + + "
" + + "
" + fileHeader + "
" + + "
" + + "
" + + "
" + + " " + + " " + + generateFileHtml(file) + + " " + + "
" + + "
" + + "
" + + "
"; + }); + }; + + var generateFileHtml = function(file) { + return file.blocks.map(function(block) { + return block.lines.map(function(line) { + + var oldLine = line.oldNumber ? line.oldNumber : "", + newLine = line.newNumber ? line.newNumber : ""; + + return "" + + " " + oldLine + "" + + " " + newLine + "" + + " " + escape(line.content) + "" + + ""; + }).join("\n"); + }).join("\n"); + }; + + var escape = function(str) { + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/\t/g, " "); + }; + + /* singleton pattern */ + var instance; + return { + getInstance: function() { + if (instance === undefined) { + instance = new Diff2Html(); + /* Hide the constructor so the returned objected can't be new'd */ + instance.constructor = null; + } + return instance; + } + }; + + })(); + + window.Diff2Html = ClassVariable; + return window.Diff2Html; + +})(jQuery, window); diff --git a/style.css b/style.css index 6207991..875457c 100644 --- a/style.css +++ b/style.css @@ -1,6 +1,8 @@ /* * - * Pretty Diff Style + * Diff to HTML (style.css) + * Author: rtfpessoa + * Date: Friday 29 August 2014 * */ @@ -11,30 +13,35 @@ body { #wrapper { display: inline-block; margin-top: 1em; - min-width: 800px; text-align: left; + width: 920px; } -h2 { - background: #fafafa; - background: -moz-linear-gradient(#fafafa, #eaeaea); - background: -webkit-linear-gradient(#fafafa, #eaeaea); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#fafafa',endColorstr='#eaeaea')"; - border: 1px solid #d8d8d8; - border-bottom: 0; - color: #555; - font: 14px sans-serif; - overflow: hidden; - padding: 10px 6px; - text-shadow: 0 1px 0 white; - margin: 0; +.file-wrapper { + border: 1px solid #ddd; + border-radius: 3px; + margin-bottom: 1em; +} + +.file-header { + padding: 5px 10px; + text-shadow: 0 1px 0 #fff; + border-bottom: 1px solid #d8d8d8; + background-color: #f7f7f7; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + box-sizing: border-box; +} + +.file-name { + font: 13px Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; + line-height: 1.4; + height: 33px; + line-height: 33px; } .file-diff { - border: 1px solid #d8d8d8; - margin-bottom: 1em; overflow: auto; - padding: 0.5em 0; } .file-diff > div { @@ -53,21 +60,89 @@ pre { cursor: pointer; } -.file { - color: #aaa; +.code-wrapper { + overflow-x: auto; + overflow-y: hidden; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} + +.diff-table { + border-collapse: separate; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +tbody { + display: table-row-group; + vertical-align: middle; + border-color: inherit; +} + +tr { + display: table-row; + vertical-align: inherit; + border-color: inherit; +} + +td, th { + padding: 0; +} + +.code-linenumber { + width: 1%; + min-width: 50px; + padding-left: 10px; + padding-right: 10px; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 12px; + line-height: 18px; + color: rgba(0,0,0,0.3); + vertical-align: top; + text-align: right; + border: solid #eeeeee; + border-width: 0 1px 0 0; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.code-line { + position: relative; + padding-left: 10px; + padding-right: 10px; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 12px; + color: #333; + vertical-align: top; + white-space: pre; + overflow: visible; + padding-top: 4px; + padding-bottom: 4px; } .delete { + vertical-align: middle; background-color: #f7c8c8; border-color: #e9aeae; } .insert { + vertical-align: middle; background-color: #ceffce; border-color: #b4e2b4; } .info { background-color: #f8fafd; + vertical-align: middle; color: rgba(0,0,0,0.3); + border-color: #d5e4f2; } diff --git a/template.html b/template.html index 9c53cb9..746cc33 100644 --- a/template.html +++ b/template.html @@ -2,18 +2,62 @@ - Pretty Diff - + Diff to HTML by rtfpessoa + + + + + + -
-
+
+