Skip to content

Commit

Permalink
Show content
Browse files Browse the repository at this point in the history
  • Loading branch information
pomber committed Feb 4, 2019
1 parent f9f2bab commit 269542d
Show file tree
Hide file tree
Showing 7 changed files with 484 additions and 13 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"diff": "^4.0.1",
"prismjs": "^1.15.0",
"react": "^16.8.0-alpha.1",
"react-dom": "^16.8.0-alpha.1",
"react-scripts": "2.1.3"
"react-scripts": "2.1.3",
"react-use": "^5.2.2"
},
"scripts": {
"start": "react-scripts start",
Expand Down
4 changes: 3 additions & 1 deletion src/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from "react";
import History from "./history";

function App({ commits }) {
return <pre>{JSON.stringify(commits, null, 2)}</pre>;
console.log(commits);
return <History commits={commits} />;
}

export default App;
143 changes: 143 additions & 0 deletions src/differ.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import * as diff from "diff";
import tokenize from "./tokenizer";
const newlineRe = /\r\n|\r|\n/;

function myDiff(oldCode, newCode) {
const changes = diff.diffLines(oldCode || "", newCode);

let oldIndex = -1;
return changes.map(({ value, count, removed, added }) => {
const lines = value.split(newlineRe);
// check if last line is empty, if it is, remove it
const lastLine = lines.pop();
if (lastLine) {
lines.push(lastLine);
}
const result = {
oldIndex,
lines,
count,
removed,
added
};
if (!added) {
oldIndex += count;
}
return result;
});
}

function insert(array, index, elements) {
return array.splice(index, 0, ...elements);
}

function slideDiff(lines, codes, slideIndex) {
const prevLines = lines.filter(l => l.slides.includes(slideIndex - 1));
const prevCode = codes[slideIndex - 1] || "";
const currCode = codes[slideIndex];

const changes = myDiff(prevCode, currCode);

changes.forEach(change => {
if (change.added) {
const prevLine = prevLines[change.oldIndex];
const addAtIndex = lines.indexOf(prevLine) + 1;
const addLines = change.lines.map(content => ({
content,
slides: [slideIndex]
}));
insert(lines, addAtIndex, addLines);
} else if (!change.removed) {
for (let j = 1; j <= change.count; j++) {
prevLines[change.oldIndex + j].slides.push(slideIndex);
}
}
});

const tokenLines = tokenize(currCode);
const currLines = lines.filter(l => l.slides.includes(slideIndex));
currLines.forEach((line, index) => (line.tokens = tokenLines[index]));
}

export function parseLines(codes) {
const lines = [];
for (let slideIndex = 0; slideIndex < codes.length; slideIndex++) {
slideDiff(lines, codes, slideIndex);
}
return lines;
}

export function getSlides(codes) {
const lines = parseLines(codes);
// console.log("lines", lines);
return codes.map((_, slideIndex) => {
return lines
.map((line, lineIndex) => ({
content: line.content,
tokens: line.tokens,
left: line.slides.includes(slideIndex - 1),
middle: line.slides.includes(slideIndex),
right: line.slides.includes(slideIndex + 1),
key: lineIndex
}))
.filter(line => line.middle || line.left || line.right);
});
}

//

function getLines(change) {
return change.value
.trimRight(newlineRe)
.split(newlineRe)
.map(content => ({
content
}));
}

export function tripleDiff(left, middle, right) {
const leftDiff = diff.diffLines(left, middle);
const rightDiff = diff.diffLines(middle, right);

let x = leftDiff
.map(change =>
change.value
.trimRight(newlineRe)
.split(newlineRe)
.map(content => ({
content,
left: !change.added,
middle: !change.removed
}))
)
.flat();
// console.log(JSON.stringify(leftDiff, null, 2));
// console.log(JSON.stringify(x, null, 2));

let i = 0;
// console.log(rightDiff);
rightDiff.forEach(change => {
let mx = x.filter(l => l.middle || l.right);
if (change.added) {
const lines = change.value
.trimRight(newlineRe)
.split(newlineRe)
.map(content => ({
content,
left: false,
middle: false,
right: true
}));
insert(x, i, lines);
} else if (!change.removed) {
// console.log(change);
for (let j = 0; j < change.count; j++) {
mx[i + j].right = true;
}
}
i += change.count;
});
// console.log(JSON.stringify(rightDiff, null, 2));
// console.log(JSON.stringify(x, null, 2));
return x;
}
24 changes: 14 additions & 10 deletions src/github.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ export async function getHistory(repo, sha, path, top = 10) {
`https://api.github.com/repos/${repo}/commits?sha=${sha}&path=${path}`
);
const commitsJson = await commitsResponse.json();
const commits = commitsJson.map(commit => ({
sha: commit.sha,
date: new Date(commit.commit.author.date),
author: {
login: commit.author.login,
avatar: commit.author.avatar_url
},
commitUrl: commit.html_url,
message: commit.commit.message
}));
const commits = commitsJson
.map(commit => ({
sha: commit.sha,
date: new Date(commit.commit.author.date),
author: {
login: commit.author.login,
avatar: commit.author.avatar_url
},
commitUrl: commit.html_url,
message: commit.commit.message
}))
.sort(function(a, b) {
return a.date - b.date;
});

await Promise.all(
commits.slice(0, top).map(async commit => {
Expand Down
34 changes: 34 additions & 0 deletions src/history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { useEffect, useState } from "react";
import { getSlides } from "./differ";
import { useSpring } from "react-use";

export default function History({ commits, language }) {
const codes = commits.map(commit => commit.content);
const slideLines = getSlides(codes);
const [current, target, setTarget] = useSliderSpring(codes.length - 1);
const index = Math.round(current);

const nextSlide = () =>
setTarget(Math.min(Math.round(target + 0.51), slideLines.length - 1));
const prevSlide = () => setTarget(Math.max(Math.round(target - 0.51), 0));
useEffect(() => {
document.body.onkeydown = function(e) {
if (e.keyCode === 39) {
nextSlide();
} else if (e.keyCode === 37) {
prevSlide();
} else if (e.keyCode === 32) {
setTarget(current);
}
};
});
return <pre>{codes[index]}</pre>;
}
function useSliderSpring(initial) {
const [target, setTarget] = useState(initial);
const tension = 0;
const friction = 10;
const value = useSpring(target, tension, friction);

return [Math.round(value * 100) / 100, target, setTarget];
}
57 changes: 57 additions & 0 deletions src/tokenizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Prism from "prismjs";
const newlineRe = /\r\n|\r|\n/;

// Take a list of nested tokens
// (token.content may contain an array of tokens)
// and flatten it so content is always a string
// and type the type of the leaf
function flattenTokens(tokens) {
const flatList = [];
tokens.forEach(token => {
if (Array.isArray(token.content)) {
flatList.push(...flattenTokens(token.content));
} else {
flatList.push(token);
}
});
return flatList;
}

// Convert strings to tokens
function tokenizeStrings(prismTokens) {
return prismTokens.map(pt =>
typeof pt === "string"
? { type: "plain", content: pt }
: {
type: pt.type,
content: Array.isArray(pt.content)
? tokenizeStrings(pt.content)
: pt.content
}
);
}

export default function tokenize(code, language = "javascript") {
const prismTokens = Prism.tokenize(code, Prism.languages[language]);
const nestedTokens = tokenizeStrings(prismTokens);
const tokens = flattenTokens(nestedTokens);

let currentLine = [];
const lines = [currentLine];
tokens.forEach(token => {
const contentLines = token.content.split(newlineRe);

const firstContent = contentLines.shift();
if (firstContent !== "") {
currentLine.push({ type: token.type, content: firstContent });
}
contentLines.forEach(content => {
currentLine = [];
lines.push(currentLine);
if (content !== "") {
currentLine.push({ type: token.type, content });
}
});
});
return lines;
}
Loading

0 comments on commit 269542d

Please sign in to comment.