UNPKG

parse-diff

Version:
164 lines (133 loc) 3.58 kB
# parses unified diff # http://www.gnu.org/software/diffutils/manual/diffutils.html#Unified-Format module.exports = (input) -> return [] if not input return [] if input.match /^\s+$/ lines = input.split '\n' return [] if lines.length == 0 files = [] file = null ln_del = 0 ln_add = 0 current = null start = (line) -> file = chunks: [] deletions: 0 additions: 0 files.push file if not file.to and not file.from fileNames = parseFile line if fileNames file.from = fileNames[0] file.to = fileNames[1] restart = -> start() if not file || file.chunks.length new_file = -> restart() file.new = true file.from = '/dev/null' deleted_file = -> restart() file.deleted = true file.to = '/dev/null' index = (line) -> restart() file.index = line.split(' ').slice(1) from_file = (line) -> restart() file.from = parseFileFallback line to_file = (line) -> restart() file.to = parseFileFallback line chunk = (line, match) -> ln_del = oldStart = +match[1] oldLines = +(match[2] || 0) ln_add = newStart = +match[3] newLines = +(match[4] || 0) current = { content: line, changes: [], oldStart, oldLines, newStart, newLines } file.chunks.push current del = (line) -> return unless current current.changes.push {type:'del', del:true, ln:ln_del++, content:line} file.deletions++ add = (line) -> return unless current current.changes.push {type:'add', add:true, ln:ln_add++, content:line} file.additions++ normal = (line) -> return unless current current.changes.push { type: 'normal' normal: true ln1: ln_del++ ln2: ln_add++ content: line } eof = (line) -> [..., recentChange] = current.changes current.changes.push { type: recentChange.type "#{recentChange.type}": true ln1: recentChange.ln1 ln2: recentChange.ln2 ln: recentChange.ln content: line } schema = [ # todo beter regexp to avoid detect normal line starting with diff [/^\s+/, normal], [/^diff\s/, start], [/^new file mode \d+$/, new_file], [/^deleted file mode \d+$/, deleted_file], [/^index\s[\da-zA-Z]+\.\.[\da-zA-Z]+(\s(\d+))?$/, index], [/^---\s/, from_file], [/^\+\+\+\s/, to_file], [/^@@\s+\-(\d+),?(\d+)?\s+\+(\d+),?(\d+)?\s@@/, chunk], [/^-/, del], [/^\+/, add], [/^\\ No newline at end of file$/, eof] ] parse = (line) -> for p in schema m = line.match p[0] if m p[1](line, m) return true return false for line in lines parse line return files parseFile = (s) -> return if not s fileNames = s.match(/a\/.*(?= b)|b\/.*$/g) fileNames.map (fileName, i) -> fileNames[i] = fileName.replace(/^(a|b)\//, '') return fileNames # fallback function to overwrite file.from and file.to if executed parseFileFallback = (s) -> s = ltrim s, '-' s = ltrim s, '+' s = s.trim() # ignore possible time stamp t = (/\t.*|\d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d(.\d+)?\s(\+|-)\d\d\d\d/).exec(s) s = s.substring(0, t.index).trim() if t # ignore git prefixes a/ or b/ if s.match (/^(a|b)\//) then s.substr(2) else s ltrim = (s, chars) -> s = makeString(s) return trimLeft.call(s) if !chars and trimLeft chars = defaultToWhiteSpace(chars) return s.replace(new RegExp('^' + chars + '+'), '') makeString = (s) -> if s == null then '' else s + '' trimLeft = String.prototype.trimLeft defaultToWhiteSpace = (chars) -> return '\\s' if chars == null return chars.source if chars.source return '[' + escapeRegExp(chars) + ']' escapeRegExp = (s) -> makeString(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1')