From 060dee3b1cd212c912bf307ffd39b4c9dee92276 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Wed, 13 Oct 2021 21:15:27 +0900 Subject: [PATCH 01/22] chore: formatting --- .prettierrc.json | 6 ++++++ jest.config.js | 6 +++--- src/__tests__/parse-git-diff.test.ts | 12 ++++++------ src/parse-git-diff.ts | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..fa51da2 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": false, + "singleQuote": true +} diff --git a/jest.config.js b/jest.config.js index 21a1e97..97cb836 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,5 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { - preset: "ts-jest", - testEnvironment: "node", -}; + preset: 'ts-jest', + testEnvironment: 'node', +} diff --git a/src/__tests__/parse-git-diff.test.ts b/src/__tests__/parse-git-diff.test.ts index 6991cb1..1c7dc1d 100644 --- a/src/__tests__/parse-git-diff.test.ts +++ b/src/__tests__/parse-git-diff.test.ts @@ -1,7 +1,7 @@ -import parseGitDiff from "../parse-git-diff"; +import parseGitDiff from '../parse-git-diff' -describe("parse-git-diff", () => { - test("test", () => { - expect(parseGitDiff("a")).toBe("a"); - }); -}); +describe('parse-git-diff', () => { + test('test', () => { + expect(parseGitDiff('a')).toBe('a') + }) +}) diff --git a/src/parse-git-diff.ts b/src/parse-git-diff.ts index f7599a9..ad236d5 100644 --- a/src/parse-git-diff.ts +++ b/src/parse-git-diff.ts @@ -1,3 +1,3 @@ export default function parseGitDiff(diff: string): string { - return diff; + return diff } From 45a4ceed9fe6506002224ccd23e6bf4dc7dbad41 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Wed, 13 Oct 2021 21:17:07 +0900 Subject: [PATCH 02/22] chore: add pre-push hook --- .husky/{pre-commit => pre-push} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .husky/{pre-commit => pre-push} (100%) diff --git a/.husky/pre-commit b/.husky/pre-push similarity index 100% rename from .husky/pre-commit rename to .husky/pre-push From d0541fec9e2b1d05d3d8f9b2c422c22b17fd0a1a Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Wed, 13 Oct 2021 21:31:33 +0900 Subject: [PATCH 03/22] feat: add utils --- .prettierrc.json | 2 +- jest.config.js | 2 +- src/__tests__/parse-git-diff.test.ts | 8 +-- src/__tests__/utils.test.ts | 39 ++++++++++++++ src/parse-git-diff.ts | 18 ++++++- src/testUtils.ts | 20 +++++++ src/types.ts | 25 +++++++++ src/utils.ts | 81 ++++++++++++++++++++++++++++ 8 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 src/__tests__/utils.test.ts create mode 100644 src/testUtils.ts create mode 100644 src/types.ts create mode 100644 src/utils.ts diff --git a/.prettierrc.json b/.prettierrc.json index fa51da2..0a72520 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,6 +1,6 @@ { "trailingComma": "es5", "tabWidth": 2, - "semi": false, + "semi": true, "singleQuote": true } diff --git a/jest.config.js b/jest.config.js index 97cb836..e86e13b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,4 +2,4 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', -} +}; diff --git a/src/__tests__/parse-git-diff.test.ts b/src/__tests__/parse-git-diff.test.ts index 1c7dc1d..d1c1663 100644 --- a/src/__tests__/parse-git-diff.test.ts +++ b/src/__tests__/parse-git-diff.test.ts @@ -1,7 +1,7 @@ -import parseGitDiff from '../parse-git-diff' +import parseGitDiff from '../parse-git-diff'; describe('parse-git-diff', () => { test('test', () => { - expect(parseGitDiff('a')).toBe('a') - }) -}) + expect(parseGitDiff('a')).toBe('a'); + }); +}); diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts new file mode 100644 index 0000000..8fabe75 --- /dev/null +++ b/src/__tests__/utils.test.ts @@ -0,0 +1,39 @@ +import * as utils from '../utils'; +import * as testUtils from '../testUtils'; + +describe('utils', () => { + test('isComparisonInputLine (valid)', () => { + const cases: [string][] = [['diff --git a/valid.diff b/valid.diff']]; + testUtils.createValidTester(utils.isComparisonInputLine)(cases); + }); + + test('isComparisonInputLine (invalid)', () => { + const cases: [string][] = [['dif --git a/valid.diff b/valid.diff']]; + testUtils.createInvalidTester(utils.isComparisonInputLine)(cases); + }); + + test('isMetaDataLine (valid)', () => { + const cases: [string][] = [['index a63c4ac..ac163a4 100644']]; + testUtils.createValidTester(utils.isMetaDataLine)(cases); + }); + + test('isMetaDataLine (invalid)', () => { + const cases: [string][] = [['inde a63c4ac..ac163a4 100644']]; + testUtils.createInvalidTester(utils.isMetaDataLine)(cases); + }); + + test('isChangeMarkersLines (valid)', () => { + const cases: [[string, string]][] = [ + [['--- a/diff_test.txt', '+++ b/diff_test.txt']], + ]; + testUtils.createValidTester(utils.isChangeMarkersLines)(cases); + }); + + test('isChangeMarkersLines (valid)', () => { + const cases: [string][] = [ + ["@@ -1 +1 @@ describe('utils', () => {"], + ["@@ -23,15 +23,15 @@ describe('utils', () => {"], + ]; + testUtils.createValidTester(utils.isStartOfDiffChunks)(cases); + }); +}); diff --git a/src/parse-git-diff.ts b/src/parse-git-diff.ts index ad236d5..4a87c67 100644 --- a/src/parse-git-diff.ts +++ b/src/parse-git-diff.ts @@ -1,3 +1,19 @@ +import { + isChangeMarkersLines, + isStartOfDiffChunks, + isMetaDataLine, + isComparisonInputLine, +} from './utils'; + +function parseFileDiff(lines: string[]) {} + export default function parseGitDiff(diff: string): string { - return diff + const lines = diff.split('\n'); + lines.forEach((line, index) => { + if (index === 0) { + if (isComparisonInputLine(line)) { + } + } + }); + return 'a'; } diff --git a/src/testUtils.ts b/src/testUtils.ts new file mode 100644 index 0000000..94476ec --- /dev/null +++ b/src/testUtils.ts @@ -0,0 +1,20 @@ +type Validator = (...args: Args) => boolean; + +function createBooleanTester( + validator: V, + result: boolean +) { + return function validTester(cases: Parameters[]): void { + cases.forEach((c) => { + expect(validator(...c)).toBe(result); + }); + }; +} + +export function createValidTester(validator: V) { + return createBooleanTester(validator, true); +} + +export function createInvalidTester(validator: V) { + return createBooleanTester(validator, false); +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..72206ce --- /dev/null +++ b/src/types.ts @@ -0,0 +1,25 @@ +interface Base { + readonly type: string; +} + +export interface GitDiff extends Base { + type: 'GitDiff'; + changedFiles: FileDiff[]; +} + +interface Change extends Base { + type: 'Change'; +} + +export interface FileDiff extends Base { + readonly type: 'FileDiff'; + changes: Change[]; +} + +export type ComparisonInputLine = `diff --git ${string} ${string}`; +export type MetaDataLine = `index ${string}`; +export type FirstChangeMarker = `--- ${string}`; +export type SecondChangeMarker = `+++ ${string}`; +export type StartOfDiffChunks = `@@ -${string} +${string} @@`; +export type AdditionLine = `+${string}`; +export type DeletionLine = `-${string}`; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..4493599 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,81 @@ +import type * as t from './types'; + +/** + * Checks whether a line is a comparison input line or not. + * @param {string} line The line to check. + * @returns {boolean} Returns `true` if the given line is a comparison line, otherwise `false`. + * @example + * // comparison input line. + * "diff --git a/file.txt b/file.txt" + */ +export function isComparisonInputLine( + line: string +): line is t.ComparisonInputLine { + const [diff, doubleDashGit, fileA, fileB, rest] = line.split(' '); + return !!( + diff === 'diff' && + doubleDashGit === '--git' && + fileA && + fileB && + !rest + ); +} + +/** + * Checks whether a line is a meta data line or not. + * @param line The line to check. + * @returns {boolean} Return `true` if the given line is a meta data line, otherwise `false`. + * @example + * // meta data line. + * "index 6b0c6cf..b37e70a 100644" + */ +export function isMetaDataLine(line: string): line is t.MetaDataLine { + return /^index/.test(line); +} + +/** + * Checks whether the two lines are change markers or not. + * @param first The first line. + * @param second The second line. + * @returns {boolean} Return `true` if the given two lines are change markers, otherwise `false`. + * @example + * // change markers + * "--- a/file.txt" + * "+++ b/file.txt" + */ +export function isChangeMarkersLines( + twoLines: [string, string] +): twoLines is [t.FirstChangeMarker, t.SecondChangeMarker] { + return /^\-\-\-\s/.test(twoLines[0]) && /^\+\+\+\s/.test(twoLines[1]); +} + +/** + * Checks whether the line is a start line of the diff chunks + * @param line The line to check. + * @returns return `true` if the given line is the start of diff chunks, otherwise `false`. + * @example + * // start line of diff chunks + * "@@ -1 +1 @@ describe('utils', () => {... + * "@@ -23,15 +23,15 @@ describe('utils', () => { ..." + */ +export function isStartOfDiffChunks(line: string): line is t.StartOfDiffChunks { + return /^@@\s\-\d+(,\d+)?\s\+\d+(,\d+)?\s@@\s/.test(line); +} + +/** + * Checks whether the line is an addition line. + * @param {string} line The line to check. + * @returns {boolean} Return `true` if the given line is an addition line, otherwise `false`. + */ +export function isAdditionLine(line: string): line is t.AdditionLine { + return /^\+/.test(line); +} + +/** + * Checks whether the line is an deletion line. + * @param {string} line The line to check. + * @returns {boolean} Return `true` if the given line is an deletion line, otherwise `false`. + */ +export function isDeletionLine(line: string): line is t.DeletionLine { + return /^\-/.test(line); +} From 906db0678f8360d7ebba45f95a16239192c03a60 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sat, 16 Oct 2021 19:44:35 +0900 Subject: [PATCH 04/22] feat: add types --- src/types.ts | 25 --------------------- src/types/changes.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++ src/types/common.ts | 3 +++ src/types/index.ts | 2 ++ src/types/nodes.ts | 6 +++++ src/utils.ts | 16 +++++-------- 6 files changed, 70 insertions(+), 35 deletions(-) delete mode 100644 src/types.ts create mode 100644 src/types/changes.ts create mode 100644 src/types/common.ts create mode 100644 src/types/index.ts create mode 100644 src/types/nodes.ts diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 72206ce..0000000 --- a/src/types.ts +++ /dev/null @@ -1,25 +0,0 @@ -interface Base { - readonly type: string; -} - -export interface GitDiff extends Base { - type: 'GitDiff'; - changedFiles: FileDiff[]; -} - -interface Change extends Base { - type: 'Change'; -} - -export interface FileDiff extends Base { - readonly type: 'FileDiff'; - changes: Change[]; -} - -export type ComparisonInputLine = `diff --git ${string} ${string}`; -export type MetaDataLine = `index ${string}`; -export type FirstChangeMarker = `--- ${string}`; -export type SecondChangeMarker = `+++ ${string}`; -export type StartOfDiffChunks = `@@ -${string} +${string} @@`; -export type AdditionLine = `+${string}`; -export type DeletionLine = `-${string}`; diff --git a/src/types/changes.ts b/src/types/changes.ts new file mode 100644 index 0000000..f7ac5e4 --- /dev/null +++ b/src/types/changes.ts @@ -0,0 +1,53 @@ +import type { Base } from './common'; + +/** changed content types */ + +interface BaseChange extends Base { + line: string; + content: string; +} + +export interface Inserted extends BaseChange<'Inserted'> {} + +export interface Deleted extends BaseChange<'Deleted'> {} + +export interface Unchanged extends BaseChange<'Unchanged'> {} + +export type AnyChange = Inserted | Deleted | Unchanged; + +/** changed file types */ + +interface BaseFileChange extends Base { + hunks: Hunk[]; +} + +export interface ChangedFile extends BaseFileChange<'ChangedFile'> {} + +export interface AddedFile extends BaseFileChange<'AddedFile'> { + path: string; +} + +export interface DeletedFile extends BaseFileChange<'DeletedFile'> { + path: string; +} + +export interface RenamedFile extends BaseFileChange<'RenamedFile'> { + from: string; + to: string; +} + +export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile; + +/** hunk */ + +export interface Hunk extends Base<'Hunk'> { + old: { + start: number; + lines: number; + }; + new: { + start: number; + lines: number; + }; + changes: AnyChange[]; +} diff --git a/src/types/common.ts b/src/types/common.ts new file mode 100644 index 0000000..a8159df --- /dev/null +++ b/src/types/common.ts @@ -0,0 +1,3 @@ +export interface Base { + readonly type: Type; +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..b3f950e --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './nodes'; +export * from './changes'; diff --git a/src/types/nodes.ts b/src/types/nodes.ts new file mode 100644 index 0000000..2f1db87 --- /dev/null +++ b/src/types/nodes.ts @@ -0,0 +1,6 @@ +import type { Base } from './common'; +import type { AnyFileChange } from './changes'; + +export interface GitDiff extends Base<'GitDiff'> { + changedFiles: AnyFileChange[]; +} diff --git a/src/utils.ts b/src/utils.ts index 4493599..031e228 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,9 +8,7 @@ import type * as t from './types'; * // comparison input line. * "diff --git a/file.txt b/file.txt" */ -export function isComparisonInputLine( - line: string -): line is t.ComparisonInputLine { +export function isComparisonInputLine(line: string): boolean { const [diff, doubleDashGit, fileA, fileB, rest] = line.split(' '); return !!( diff === 'diff' && @@ -29,7 +27,7 @@ export function isComparisonInputLine( * // meta data line. * "index 6b0c6cf..b37e70a 100644" */ -export function isMetaDataLine(line: string): line is t.MetaDataLine { +export function isMetaDataLine(line: string): boolean { return /^index/.test(line); } @@ -43,9 +41,7 @@ export function isMetaDataLine(line: string): line is t.MetaDataLine { * "--- a/file.txt" * "+++ b/file.txt" */ -export function isChangeMarkersLines( - twoLines: [string, string] -): twoLines is [t.FirstChangeMarker, t.SecondChangeMarker] { +export function isChangeMarkersLines(twoLines: [string, string]): boolean { return /^\-\-\-\s/.test(twoLines[0]) && /^\+\+\+\s/.test(twoLines[1]); } @@ -58,7 +54,7 @@ export function isChangeMarkersLines( * "@@ -1 +1 @@ describe('utils', () => {... * "@@ -23,15 +23,15 @@ describe('utils', () => { ..." */ -export function isStartOfDiffChunks(line: string): line is t.StartOfDiffChunks { +export function isStartOfDiffChunks(line: string): boolean { return /^@@\s\-\d+(,\d+)?\s\+\d+(,\d+)?\s@@\s/.test(line); } @@ -67,7 +63,7 @@ export function isStartOfDiffChunks(line: string): line is t.StartOfDiffChunks { * @param {string} line The line to check. * @returns {boolean} Return `true` if the given line is an addition line, otherwise `false`. */ -export function isAdditionLine(line: string): line is t.AdditionLine { +export function isAdditionLine(line: string): boolean { return /^\+/.test(line); } @@ -76,6 +72,6 @@ export function isAdditionLine(line: string): line is t.AdditionLine { * @param {string} line The line to check. * @returns {boolean} Return `true` if the given line is an deletion line, otherwise `false`. */ -export function isDeletionLine(line: string): line is t.DeletionLine { +export function isDeletionLine(line: string): boolean { return /^\-/.test(line); } From 46303c814fc448b0400fe9452dbce8aba67dc4ac Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sat, 16 Oct 2021 22:22:03 +0900 Subject: [PATCH 05/22] feat: add meta parsing --- src/__tests__/parse-git-diff.test.ts | 7 --- src/__tests__/parse-meta.test.ts | 65 ++++++++++++++++++++++++ src/__tests__/utils.test.ts | 13 ++--- src/context.ts | 20 ++++++++ src/parse-git-diff.ts | 35 +++++++------ src/parse-meta.ts | 76 ++++++++++++++++++++++++++++ src/{testUtils.ts => test-utils.ts} | 10 ++++ src/types/changes.ts | 15 +++--- src/utils.ts | 26 ++++------ 9 files changed, 211 insertions(+), 56 deletions(-) delete mode 100644 src/__tests__/parse-git-diff.test.ts create mode 100644 src/__tests__/parse-meta.test.ts create mode 100644 src/context.ts create mode 100644 src/parse-meta.ts rename src/{testUtils.ts => test-utils.ts} (70%) diff --git a/src/__tests__/parse-git-diff.test.ts b/src/__tests__/parse-git-diff.test.ts deleted file mode 100644 index d1c1663..0000000 --- a/src/__tests__/parse-git-diff.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import parseGitDiff from '../parse-git-diff'; - -describe('parse-git-diff', () => { - test('test', () => { - expect(parseGitDiff('a')).toBe('a'); - }); -}); diff --git a/src/__tests__/parse-meta.test.ts b/src/__tests__/parse-meta.test.ts new file mode 100644 index 0000000..90e4097 --- /dev/null +++ b/src/__tests__/parse-meta.test.ts @@ -0,0 +1,65 @@ +import { + parseAdditionMarker, + parseDeletionMarker, + parseChangeMarkers, + parseChunkHeader, +} from '../parse-meta'; +import { createContext } from '../test-utils'; + +describe('parseAdditionMarker', () => { + it('should parse added marker', () => { + const context = createContext(`+++ b/src/tests/addition.test.ts`); + const result = parseAdditionMarker(context); + expect(result).not.toBe(null); + expect(result).toMatchInlineSnapshot(`"src/tests/addition.test.ts"`); + }); +}); + +describe('parseDeletionMarker', () => { + it('should parse deleted marker', () => { + const context = createContext(`--- a/src/tests/deletion.test.ts`); + const result = parseDeletionMarker(context); + expect(result).not.toBe(null); + expect(result).toMatchInlineSnapshot(`"src/tests/deletion.test.ts"`); + }); +}); + +describe('parseChangeMarkers', () => { + it('should parse change markers', () => { + const context = createContext( + // prettier-ignore + '--- a/src/__tests__/parse-git-diff.test.ts\n' + + '+++ /dev/null' + ); + const result = parseChangeMarkers(context); + expect(result).not.toBe(null); + expect(result).toMatchInlineSnapshot(` + Object { + "added": "/dev/null", + "deleted": "src/__tests__/parse-git-diff.test.ts", + } + `); + }); +}); + +describe('parseChunkHeader', () => { + it('should parse chunk header (normal format)', () => { + const context = createContext(`@@ -0,0 +1,53 @@`); + const result = parseChunkHeader(context); + expect(result).not.toBe(null); + expect(result?.deletedPos.start).toBe(0); + expect(result?.deletedPos.lines).toBe(0); + expect(result?.addedPos.start).toBe(1); + expect(result?.addedPos.lines).toBe(53); + }); + + it('should parse chunk header (concise format)', () => { + const context = createContext(`@@ -1 +1 @@`); + const result = parseChunkHeader(context); + expect(result).not.toBe(null); + expect(result?.deletedPos.start).toBe(1); + expect(result?.deletedPos.lines).toBe(1); + expect(result?.addedPos.start).toBe(1); + expect(result?.addedPos.lines).toBe(1); + }); +}); diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index 8fabe75..df8dc3e 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -1,5 +1,5 @@ import * as utils from '../utils'; -import * as testUtils from '../testUtils'; +import * as testUtils from '../test-utils'; describe('utils', () => { test('isComparisonInputLine (valid)', () => { @@ -22,18 +22,11 @@ describe('utils', () => { testUtils.createInvalidTester(utils.isMetaDataLine)(cases); }); - test('isChangeMarkersLines (valid)', () => { - const cases: [[string, string]][] = [ - [['--- a/diff_test.txt', '+++ b/diff_test.txt']], - ]; - testUtils.createValidTester(utils.isChangeMarkersLines)(cases); - }); - - test('isChangeMarkersLines (valid)', () => { + test('isChunkHeader (valid)', () => { const cases: [string][] = [ ["@@ -1 +1 @@ describe('utils', () => {"], ["@@ -23,15 +23,15 @@ describe('utils', () => {"], ]; - testUtils.createValidTester(utils.isStartOfDiffChunks)(cases); + testUtils.createValidTester(utils.isChunkHeader)(cases); }); }); diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 0000000..49265c3 --- /dev/null +++ b/src/context.ts @@ -0,0 +1,20 @@ +export default class Context { + private line: number = 0; + private lines: string[] = []; + public constructor(diff: string) { + this.lines = diff.split('\n'); + } + + public getCurLine(): string | undefined { + return this.lines[this.line]; + } + + public nextLine(): string | undefined { + this.line++; + return this.getCurLine(); + } + + public isEof(): boolean { + return this.line >= this.lines.length; + } +} diff --git a/src/parse-git-diff.ts b/src/parse-git-diff.ts index 4a87c67..9a657a2 100644 --- a/src/parse-git-diff.ts +++ b/src/parse-git-diff.ts @@ -1,19 +1,24 @@ -import { - isChangeMarkersLines, - isStartOfDiffChunks, - isMetaDataLine, - isComparisonInputLine, -} from './utils'; +import Context from './context'; +import * as t from './types'; +import { isMetaDataLine, isComparisonInputLine } from './utils'; -function parseFileDiff(lines: string[]) {} +export function parseGitDiff(diff: string): t.GitDiff { + const result: t.GitDiff = { + type: 'GitDiff', + changedFiles: [], + }; -export default function parseGitDiff(diff: string): string { - const lines = diff.split('\n'); - lines.forEach((line, index) => { - if (index === 0) { - if (isComparisonInputLine(line)) { - } + const ctx = new Context(diff); + while (!ctx.isEof()) { + const line = ctx.getCurLine(); + + if (line && (!isComparisonInputLine(line) || isMetaDataLine(line))) { + ctx.nextLine(); + continue; } - }); - return 'a'; + } + + return result; } + +export function parseDeletedFile(context: Context) {} diff --git a/src/parse-meta.ts b/src/parse-meta.ts new file mode 100644 index 0000000..6d1ace6 --- /dev/null +++ b/src/parse-meta.ts @@ -0,0 +1,76 @@ +import Context from './context'; +import { Hunk } from './types'; +import { + isAdditionLine, + isAdditionMarkerLine, + isDeletionMarkerLine, + isChunkHeader, +} from './utils'; + +export function parseAdditionMarker(context: Context) { + const line = context.getCurLine(); + if (line && isAdditionMarkerLine(line)) { + context.nextLine(); + return line.replace('+++ ', '').replace('b/', ''); + } + return null; +} + +export function parseDeletionMarker(context: Context) { + const line = context.getCurLine(); + if (line && isDeletionMarkerLine(line)) { + context.nextLine(); + return line.replace('--- ', '').replace('a/', ''); + } + return null; +} + +export function parseChangeMarkers(context: Context): { + deleted: string; + added: string; +} | null { + const deleted = parseDeletionMarker(context); + const added = parseAdditionMarker(context); + + if (!added || !deleted) { + return null; + } + return { + added, + deleted, + }; +} + +export function parseChunkHeader( + context: Context +): Pick | null { + const line = context.getCurLine(); + if (!line || !isChunkHeader(line)) { + return null; + } + + const exec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@\s?/.exec(line); + + if (!exec) { + return null; + } + + const [all, delStart, delLines, addStart, addLines] = exec; + + const addedStart = parseInt(addStart, 10); + const addedPos = { + start: addedStart, + lines: addLines === undefined ? addedStart : parseInt(addLines, 10), + }; + + const deletedStart = parseInt(delStart, 10); + const deletedPos = { + start: deletedStart, + lines: delLines === undefined ? deletedStart : parseInt(delLines, 10), + }; + + return { + addedPos, + deletedPos, + }; +} diff --git a/src/testUtils.ts b/src/test-utils.ts similarity index 70% rename from src/testUtils.ts rename to src/test-utils.ts index 94476ec..83100d7 100644 --- a/src/testUtils.ts +++ b/src/test-utils.ts @@ -1,3 +1,5 @@ +import Context from './context'; + type Validator = (...args: Args) => boolean; function createBooleanTester( @@ -18,3 +20,11 @@ export function createValidTester(validator: V) { export function createInvalidTester(validator: V) { return createBooleanTester(validator, false); } + +export function createContext(diff: string, initial: number = 1) { + const context = new Context(diff); + for (let i = 0; i < initial - 1; i++) { + context.nextLine(); + } + return context; +} diff --git a/src/types/changes.ts b/src/types/changes.ts index f7ac5e4..c38a20e 100644 --- a/src/types/changes.ts +++ b/src/types/changes.ts @@ -40,14 +40,13 @@ export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile; /** hunk */ +export interface HunkPos { + start: number; + lines: number; +} + export interface Hunk extends Base<'Hunk'> { - old: { - start: number; - lines: number; - }; - new: { - start: number; - lines: number; - }; + addedPos: HunkPos; + deletedPos: HunkPos; changes: AnyChange[]; } diff --git a/src/utils.ts b/src/utils.ts index 031e228..59df914 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -31,31 +31,25 @@ export function isMetaDataLine(line: string): boolean { return /^index/.test(line); } -/** - * Checks whether the two lines are change markers or not. - * @param first The first line. - * @param second The second line. - * @returns {boolean} Return `true` if the given two lines are change markers, otherwise `false`. - * @example - * // change markers - * "--- a/file.txt" - * "+++ b/file.txt" - */ -export function isChangeMarkersLines(twoLines: [string, string]): boolean { - return /^\-\-\-\s/.test(twoLines[0]) && /^\+\+\+\s/.test(twoLines[1]); +export function isAdditionMarkerLine(line: string): boolean { + return /^\+\+\+\s/.test(line); +} + +export function isDeletionMarkerLine(line: string): boolean { + return /^\-\-\-\s/.test(line); } /** - * Checks whether the line is a start line of the diff chunks + * Checks whether the line is a chunk header or not * @param line The line to check. - * @returns return `true` if the given line is the start of diff chunks, otherwise `false`. + * @returns return `true` if the given line is a chunk header, otherwise `false`. * @example * // start line of diff chunks * "@@ -1 +1 @@ describe('utils', () => {... * "@@ -23,15 +23,15 @@ describe('utils', () => { ..." */ -export function isStartOfDiffChunks(line: string): boolean { - return /^@@\s\-\d+(,\d+)?\s\+\d+(,\d+)?\s@@\s/.test(line); +export function isChunkHeader(line: string): boolean { + return /^@@\s\-\d+(,\d+)?\s\+\d+(,\d+)?\s@@\s?/.test(line); } /** From dd5c8e14d08928722a47948aa2486edc308ca7b5 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 01:10:03 +0900 Subject: [PATCH 06/22] refactor: structure --- src/__tests__/parse-meta.test.ts | 65 ---------------- src/parse-git-diff.ts | 24 ------ src/parse-meta.ts | 76 ------------------- .../parse-add-marker.test.ts.snap | 3 + .../parse-change-markers.test.ts.snap | 8 ++ .../__snapshots__/parse-changes.test.ts.snap | 26 +++++++ .../parse-chunk-header.test.ts.snap | 27 +++++++ .../parse-delete-marker.test.ts.snap | 3 + src/{ => parser}/context.ts | 10 ++- src/parser/index.ts | 0 src/parser/parse-add-marker.test.ts | 19 +++++ src/parser/parse-add-marker.ts | 12 +++ src/parser/parse-change-markers.test.ts | 16 ++++ src/parser/parse-change-markers.ts | 19 +++++ src/parser/parse-changes.test.ts | 18 +++++ src/parser/parse-changes.ts | 36 +++++++++ src/parser/parse-chunk-header.test.ts | 20 +++++ src/parser/parse-chunk-header.ts | 37 +++++++++ src/parser/parse-delete-marker.test.ts | 19 +++++ src/parser/parse-delete-marker.ts | 12 +++ src/parser/parse-git-diff.ts | 11 +++ src/parser/parse.ts | 7 ++ src/{__tests__ => parser}/utils.test.ts | 2 +- src/{ => parser}/utils.ts | 16 ++-- src/test-utils.ts | 2 +- src/types/changes.ts | 6 +- src/types/common.ts | 1 + 27 files changed, 316 insertions(+), 179 deletions(-) delete mode 100644 src/__tests__/parse-meta.test.ts delete mode 100644 src/parse-git-diff.ts delete mode 100644 src/parse-meta.ts create mode 100644 src/parser/__snapshots__/parse-add-marker.test.ts.snap create mode 100644 src/parser/__snapshots__/parse-change-markers.test.ts.snap create mode 100644 src/parser/__snapshots__/parse-changes.test.ts.snap create mode 100644 src/parser/__snapshots__/parse-chunk-header.test.ts.snap create mode 100644 src/parser/__snapshots__/parse-delete-marker.test.ts.snap rename src/{ => parser}/context.ts (64%) create mode 100644 src/parser/index.ts create mode 100644 src/parser/parse-add-marker.test.ts create mode 100644 src/parser/parse-add-marker.ts create mode 100644 src/parser/parse-change-markers.test.ts create mode 100644 src/parser/parse-change-markers.ts create mode 100644 src/parser/parse-changes.test.ts create mode 100644 src/parser/parse-changes.ts create mode 100644 src/parser/parse-chunk-header.test.ts create mode 100644 src/parser/parse-chunk-header.ts create mode 100644 src/parser/parse-delete-marker.test.ts create mode 100644 src/parser/parse-delete-marker.ts create mode 100644 src/parser/parse-git-diff.ts create mode 100644 src/parser/parse.ts rename src/{__tests__ => parser}/utils.test.ts (96%) rename src/{ => parser}/utils.ts (86%) diff --git a/src/__tests__/parse-meta.test.ts b/src/__tests__/parse-meta.test.ts deleted file mode 100644 index 90e4097..0000000 --- a/src/__tests__/parse-meta.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - parseAdditionMarker, - parseDeletionMarker, - parseChangeMarkers, - parseChunkHeader, -} from '../parse-meta'; -import { createContext } from '../test-utils'; - -describe('parseAdditionMarker', () => { - it('should parse added marker', () => { - const context = createContext(`+++ b/src/tests/addition.test.ts`); - const result = parseAdditionMarker(context); - expect(result).not.toBe(null); - expect(result).toMatchInlineSnapshot(`"src/tests/addition.test.ts"`); - }); -}); - -describe('parseDeletionMarker', () => { - it('should parse deleted marker', () => { - const context = createContext(`--- a/src/tests/deletion.test.ts`); - const result = parseDeletionMarker(context); - expect(result).not.toBe(null); - expect(result).toMatchInlineSnapshot(`"src/tests/deletion.test.ts"`); - }); -}); - -describe('parseChangeMarkers', () => { - it('should parse change markers', () => { - const context = createContext( - // prettier-ignore - '--- a/src/__tests__/parse-git-diff.test.ts\n' + - '+++ /dev/null' - ); - const result = parseChangeMarkers(context); - expect(result).not.toBe(null); - expect(result).toMatchInlineSnapshot(` - Object { - "added": "/dev/null", - "deleted": "src/__tests__/parse-git-diff.test.ts", - } - `); - }); -}); - -describe('parseChunkHeader', () => { - it('should parse chunk header (normal format)', () => { - const context = createContext(`@@ -0,0 +1,53 @@`); - const result = parseChunkHeader(context); - expect(result).not.toBe(null); - expect(result?.deletedPos.start).toBe(0); - expect(result?.deletedPos.lines).toBe(0); - expect(result?.addedPos.start).toBe(1); - expect(result?.addedPos.lines).toBe(53); - }); - - it('should parse chunk header (concise format)', () => { - const context = createContext(`@@ -1 +1 @@`); - const result = parseChunkHeader(context); - expect(result).not.toBe(null); - expect(result?.deletedPos.start).toBe(1); - expect(result?.deletedPos.lines).toBe(1); - expect(result?.addedPos.start).toBe(1); - expect(result?.addedPos.lines).toBe(1); - }); -}); diff --git a/src/parse-git-diff.ts b/src/parse-git-diff.ts deleted file mode 100644 index 9a657a2..0000000 --- a/src/parse-git-diff.ts +++ /dev/null @@ -1,24 +0,0 @@ -import Context from './context'; -import * as t from './types'; -import { isMetaDataLine, isComparisonInputLine } from './utils'; - -export function parseGitDiff(diff: string): t.GitDiff { - const result: t.GitDiff = { - type: 'GitDiff', - changedFiles: [], - }; - - const ctx = new Context(diff); - while (!ctx.isEof()) { - const line = ctx.getCurLine(); - - if (line && (!isComparisonInputLine(line) || isMetaDataLine(line))) { - ctx.nextLine(); - continue; - } - } - - return result; -} - -export function parseDeletedFile(context: Context) {} diff --git a/src/parse-meta.ts b/src/parse-meta.ts deleted file mode 100644 index 6d1ace6..0000000 --- a/src/parse-meta.ts +++ /dev/null @@ -1,76 +0,0 @@ -import Context from './context'; -import { Hunk } from './types'; -import { - isAdditionLine, - isAdditionMarkerLine, - isDeletionMarkerLine, - isChunkHeader, -} from './utils'; - -export function parseAdditionMarker(context: Context) { - const line = context.getCurLine(); - if (line && isAdditionMarkerLine(line)) { - context.nextLine(); - return line.replace('+++ ', '').replace('b/', ''); - } - return null; -} - -export function parseDeletionMarker(context: Context) { - const line = context.getCurLine(); - if (line && isDeletionMarkerLine(line)) { - context.nextLine(); - return line.replace('--- ', '').replace('a/', ''); - } - return null; -} - -export function parseChangeMarkers(context: Context): { - deleted: string; - added: string; -} | null { - const deleted = parseDeletionMarker(context); - const added = parseAdditionMarker(context); - - if (!added || !deleted) { - return null; - } - return { - added, - deleted, - }; -} - -export function parseChunkHeader( - context: Context -): Pick | null { - const line = context.getCurLine(); - if (!line || !isChunkHeader(line)) { - return null; - } - - const exec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@\s?/.exec(line); - - if (!exec) { - return null; - } - - const [all, delStart, delLines, addStart, addLines] = exec; - - const addedStart = parseInt(addStart, 10); - const addedPos = { - start: addedStart, - lines: addLines === undefined ? addedStart : parseInt(addLines, 10), - }; - - const deletedStart = parseInt(delStart, 10); - const deletedPos = { - start: deletedStart, - lines: delLines === undefined ? deletedStart : parseInt(delLines, 10), - }; - - return { - addedPos, - deletedPos, - }; -} diff --git a/src/parser/__snapshots__/parse-add-marker.test.ts.snap b/src/parser/__snapshots__/parse-add-marker.test.ts.snap new file mode 100644 index 0000000..dd49522 --- /dev/null +++ b/src/parser/__snapshots__/parse-add-marker.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parseAddMarker should parse add marker. 1`] = `"src/tests/addition.test.ts"`; diff --git a/src/parser/__snapshots__/parse-change-markers.test.ts.snap b/src/parser/__snapshots__/parse-change-markers.test.ts.snap new file mode 100644 index 0000000..4735987 --- /dev/null +++ b/src/parser/__snapshots__/parse-change-markers.test.ts.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parseChangeMarkers should parse change markers. 1`] = ` +Object { + "added": "src/tests/addition.test.ts", + "deleted": "src/tests/addition.test.ts", +} +`; diff --git a/src/parser/__snapshots__/parse-changes.test.ts.snap b/src/parser/__snapshots__/parse-changes.test.ts.snap new file mode 100644 index 0000000..84bb8f8 --- /dev/null +++ b/src/parser/__snapshots__/parse-changes.test.ts.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parseChanges should parse changes. 1`] = ` +Array [ + Object { + "content": "unchanged", + "line": 1, + "type": "Unchanged", + }, + Object { + "content": "-deleted line", + "line": 2, + "type": "Unchanged", + }, + Object { + "content": "+added line", + "line": 3, + "type": "Unchanged", + }, + Object { + "content": "unchanged", + "line": 4, + "type": "Unchanged", + }, +] +`; diff --git a/src/parser/__snapshots__/parse-chunk-header.test.ts.snap b/src/parser/__snapshots__/parse-chunk-header.test.ts.snap new file mode 100644 index 0000000..e0b7d67 --- /dev/null +++ b/src/parser/__snapshots__/parse-chunk-header.test.ts.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parseChunkHeader should parse concise chunk header 1`] = ` +Object { + "addedPos": Object { + "lines": 1, + "start": 1, + }, + "deletedPos": Object { + "lines": 1, + "start": 1, + }, +} +`; + +exports[`parseChunkHeader should parse normal chunk header. 1`] = ` +Object { + "addedPos": Object { + "lines": 4, + "start": 3, + }, + "deletedPos": Object { + "lines": 71, + "start": 3, + }, +} +`; diff --git a/src/parser/__snapshots__/parse-delete-marker.test.ts.snap b/src/parser/__snapshots__/parse-delete-marker.test.ts.snap new file mode 100644 index 0000000..5812c03 --- /dev/null +++ b/src/parser/__snapshots__/parse-delete-marker.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parseDeleteMarker should parse delete marker. 1`] = `"src/tests/delete.test.ts"`; diff --git a/src/context.ts b/src/parser/context.ts similarity index 64% rename from src/context.ts rename to src/parser/context.ts index 49265c3..bd42a94 100644 --- a/src/context.ts +++ b/src/parser/context.ts @@ -1,12 +1,16 @@ export default class Context { - private line: number = 0; + private line: number = 1; private lines: string[] = []; public constructor(diff: string) { this.lines = diff.split('\n'); } public getCurLine(): string | undefined { - return this.lines[this.line]; + return this.lines[this.line - 1]; + } + + public getCurLineIndex(): number { + return this.line; } public nextLine(): string | undefined { @@ -15,6 +19,6 @@ export default class Context { } public isEof(): boolean { - return this.line >= this.lines.length; + return this.line > this.lines.length; } } diff --git a/src/parser/index.ts b/src/parser/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/parser/parse-add-marker.test.ts b/src/parser/parse-add-marker.test.ts new file mode 100644 index 0000000..b393919 --- /dev/null +++ b/src/parser/parse-add-marker.test.ts @@ -0,0 +1,19 @@ +import { createContext } from '../test-utils'; +import parseAddMarker from './parse-add-marker'; + +describe('parseAddMarker', () => { + it('should parse add marker.', () => { + const src = `+++ b/src/tests/addition.test.ts`; + const result = parseAddMarker(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); + + it('should not parse delete marker.', () => { + const src = `--- a/src/tests/addition.test.ts`; + const result = parseAddMarker(createContext(src)); + + expect(result).toBe(null); + }); +}); diff --git a/src/parser/parse-add-marker.ts b/src/parser/parse-add-marker.ts new file mode 100644 index 0000000..dbf42ea --- /dev/null +++ b/src/parser/parse-add-marker.ts @@ -0,0 +1,12 @@ +import { isAddMarkerLine } from './utils'; +import Context from './context'; + +export default function parseAddMarker(context: Context): string | null { + const line = context.getCurLine(); + + if (line && isAddMarkerLine(line)) { + context.nextLine(); + return line.replace('+++ ', '').replace('b/', ''); + } + return null; +} diff --git a/src/parser/parse-change-markers.test.ts b/src/parser/parse-change-markers.test.ts new file mode 100644 index 0000000..63d8dec --- /dev/null +++ b/src/parser/parse-change-markers.test.ts @@ -0,0 +1,16 @@ +import { createContext } from '../test-utils'; +import parseChangeMarkers from './parse-change-markers'; + +describe('parseChangeMarkers', () => { + it('should parse change markers.', () => { + // prettier-ignore + const src = +`--- a/src/tests/addition.test.ts ++++ b/src/tests/addition.test.ts`; + + const result = parseChangeMarkers(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/src/parser/parse-change-markers.ts b/src/parser/parse-change-markers.ts new file mode 100644 index 0000000..13f7f34 --- /dev/null +++ b/src/parser/parse-change-markers.ts @@ -0,0 +1,19 @@ +import parseDeleteMarker from './parse-delete-marker'; +import parseAddMarker from './parse-add-marker'; +import type Context from './context'; + +export default function parseChangeMarkers(context: Context): { + deleted: string; + added: string; +} | null { + const deleted = parseDeleteMarker(context); + const added = parseAddMarker(context); + + if (!added || !deleted) { + return null; + } + return { + added, + deleted, + }; +} diff --git a/src/parser/parse-changes.test.ts b/src/parser/parse-changes.test.ts new file mode 100644 index 0000000..d1485b4 --- /dev/null +++ b/src/parser/parse-changes.test.ts @@ -0,0 +1,18 @@ +import { createContext } from '../test-utils'; +import parseChanges from './parse-changes'; + +describe('parseChanges', () => { + it('should parse changes.', () => { + // prettier-ignore + const src = +` unchanged + -deleted line + +added line + unchanged`; + + const result = parseChanges(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/src/parser/parse-changes.ts b/src/parser/parse-changes.ts new file mode 100644 index 0000000..4508e93 --- /dev/null +++ b/src/parser/parse-changes.ts @@ -0,0 +1,36 @@ +import { isAdditionLine, isDeletionLine, isUnchangedLine } from './utils'; +import Context from './context'; +import type { AnyChange } from '../types'; + +export default function parseChanges(context: Context): AnyChange[] { + const changes: AnyChange[] = []; + while (!context.isEof()) { + const line = context.getCurLine()!; + const index = context.getCurLineIndex(); + if (isAdditionLine(line)) { + context.nextLine(); + changes.push({ + type: 'Added', + content: line.slice(1), + line: index, + }); + } else if (isDeletionLine(line)) { + context.nextLine(); + changes.push({ + type: 'Deleted', + content: line.slice(1), + line: index, + }); + } else if (isUnchangedLine(line)) { + context.nextLine(); + changes.push({ + type: 'Unchanged', + content: line.slice(1), + line: index, + }); + } else { + break; + } + } + return changes; +} diff --git a/src/parser/parse-chunk-header.test.ts b/src/parser/parse-chunk-header.test.ts new file mode 100644 index 0000000..ee2429e --- /dev/null +++ b/src/parser/parse-chunk-header.test.ts @@ -0,0 +1,20 @@ +import { createContext } from '../test-utils'; +import parseChunkHeader from './parse-chunk-header'; + +describe('parseChunkHeader', () => { + it('should parse normal chunk header.', () => { + const src = `@@ -3,71 +3,4 @@`; + const result = parseChunkHeader(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); + + it('should parse concise chunk header', () => { + const src = `@@ -1 +1 @@`; + const result = parseChunkHeader(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/src/parser/parse-chunk-header.ts b/src/parser/parse-chunk-header.ts new file mode 100644 index 0000000..d08f4ca --- /dev/null +++ b/src/parser/parse-chunk-header.ts @@ -0,0 +1,37 @@ +import { isChunkHeader } from './utils'; +import type { Hunk } from '../types'; +import type Context from './context'; + +export default function parseChunkHeader( + context: Context +): Pick | null { + const line = context.getCurLine(); + if (!line || !isChunkHeader(line)) { + return null; + } + + const exec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@\s?/.exec(line); + + if (!exec) { + return null; + } + + const [all, delStart, delLines, addStart, addLines] = exec; + + const addedStart = parseInt(addStart, 10); + const addedPos = { + start: addedStart, + lines: addLines === undefined ? addedStart : parseInt(addLines, 10), + }; + + const deletedStart = parseInt(delStart, 10); + const deletedPos = { + start: deletedStart, + lines: delLines === undefined ? deletedStart : parseInt(delLines, 10), + }; + + return { + addedPos, + deletedPos, + }; +} diff --git a/src/parser/parse-delete-marker.test.ts b/src/parser/parse-delete-marker.test.ts new file mode 100644 index 0000000..069017b --- /dev/null +++ b/src/parser/parse-delete-marker.test.ts @@ -0,0 +1,19 @@ +import { createContext } from '../test-utils'; +import parseDeleteMarker from './parse-delete-marker'; + +describe('parseDeleteMarker', () => { + it('should parse delete marker.', () => { + const src = `--- a/src/tests/delete.test.ts`; + const result = parseDeleteMarker(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); + + it('should not parse add marker.', () => { + const src = `+++ b/src/tests/delete.test.ts`; + const result = parseDeleteMarker(createContext(src)); + + expect(result).toBe(null); + }); +}); diff --git a/src/parser/parse-delete-marker.ts b/src/parser/parse-delete-marker.ts new file mode 100644 index 0000000..151084d --- /dev/null +++ b/src/parser/parse-delete-marker.ts @@ -0,0 +1,12 @@ +import { isDeleteMarkerLine } from './utils'; +import type Context from './context'; + +export default function parseDeleteMarker(context: Context): string | null { + const line = context.getCurLine(); + + if (line && isDeleteMarkerLine(line)) { + context.nextLine(); + return line.replace('--- ', '').replace('a/', ''); + } + return null; +} diff --git a/src/parser/parse-git-diff.ts b/src/parser/parse-git-diff.ts new file mode 100644 index 0000000..4d32e8b --- /dev/null +++ b/src/parser/parse-git-diff.ts @@ -0,0 +1,11 @@ +import parseChanges from './parse-changes'; +import type Context from './context'; +import type { GitDiff } from '../types'; + +export default function parseGitDiff(context: Context): GitDiff { + const gitDiff: GitDiff = { + type: 'GitDiff', + changedFiles: [], + }; + return gitDiff; +} diff --git a/src/parser/parse.ts b/src/parser/parse.ts new file mode 100644 index 0000000..863e277 --- /dev/null +++ b/src/parser/parse.ts @@ -0,0 +1,7 @@ +import Context from './context'; +import parseGitDiff from './parse-git-diff'; + +export default function parse(diff: string) { + const context = new Context(diff); + return parseGitDiff(context); +} diff --git a/src/__tests__/utils.test.ts b/src/parser/utils.test.ts similarity index 96% rename from src/__tests__/utils.test.ts rename to src/parser/utils.test.ts index df8dc3e..6d9abb1 100644 --- a/src/__tests__/utils.test.ts +++ b/src/parser/utils.test.ts @@ -1,4 +1,4 @@ -import * as utils from '../utils'; +import * as utils from '../parser/utils'; import * as testUtils from '../test-utils'; describe('utils', () => { diff --git a/src/utils.ts b/src/parser/utils.ts similarity index 86% rename from src/utils.ts rename to src/parser/utils.ts index 59df914..ef5b345 100644 --- a/src/utils.ts +++ b/src/parser/utils.ts @@ -1,4 +1,4 @@ -import type * as t from './types'; +import type * as t from '../types'; /** * Checks whether a line is a comparison input line or not. @@ -31,11 +31,11 @@ export function isMetaDataLine(line: string): boolean { return /^index/.test(line); } -export function isAdditionMarkerLine(line: string): boolean { +export function isAddMarkerLine(line: string): boolean { return /^\+\+\+\s/.test(line); } -export function isDeletionMarkerLine(line: string): boolean { +export function isDeleteMarkerLine(line: string): boolean { return /^\-\-\-\s/.test(line); } @@ -50,7 +50,7 @@ export function isDeletionMarkerLine(line: string): boolean { */ export function isChunkHeader(line: string): boolean { return /^@@\s\-\d+(,\d+)?\s\+\d+(,\d+)?\s@@\s?/.test(line); -} +} // test /** * Checks whether the line is an addition line. @@ -58,7 +58,7 @@ export function isChunkHeader(line: string): boolean { * @returns {boolean} Return `true` if the given line is an addition line, otherwise `false`. */ export function isAdditionLine(line: string): boolean { - return /^\+/.test(line); + return line[0] === '+'; } /** @@ -67,5 +67,9 @@ export function isAdditionLine(line: string): boolean { * @returns {boolean} Return `true` if the given line is an deletion line, otherwise `false`. */ export function isDeletionLine(line: string): boolean { - return /^\-/.test(line); + return line[0] === '-'; +} + +export function isUnchangedLine(line: string): boolean { + return line[0] === ' '; } diff --git a/src/test-utils.ts b/src/test-utils.ts index 83100d7..2a44dc8 100644 --- a/src/test-utils.ts +++ b/src/test-utils.ts @@ -1,4 +1,4 @@ -import Context from './context'; +import Context from './parser/context'; type Validator = (...args: Args) => boolean; diff --git a/src/types/changes.ts b/src/types/changes.ts index c38a20e..e13e229 100644 --- a/src/types/changes.ts +++ b/src/types/changes.ts @@ -3,17 +3,17 @@ import type { Base } from './common'; /** changed content types */ interface BaseChange extends Base { - line: string; + line: number; content: string; } -export interface Inserted extends BaseChange<'Inserted'> {} +export interface Added extends BaseChange<'Added'> {} export interface Deleted extends BaseChange<'Deleted'> {} export interface Unchanged extends BaseChange<'Unchanged'> {} -export type AnyChange = Inserted | Deleted | Unchanged; +export type AnyChange = Added | Deleted | Unchanged; /** changed file types */ diff --git a/src/types/common.ts b/src/types/common.ts index a8159df..edb149e 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -1,3 +1,4 @@ +// export interface Base { readonly type: Type; } From cb2069efee289bba3973fbbc64e1ec96b10cac01 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 01:49:43 +0900 Subject: [PATCH 07/22] feat: add parse chunks --- .../parse-chunk-header.test.ts.snap | 13 ++ .../__snapshots__/parse-chunks.test.ts.snap | 167 ++++++++++++++++++ src/parser/context.ts | 13 +- src/parser/parse-chunk-header.test.ts | 10 ++ src/parser/parse-chunk-header.ts | 8 +- src/parser/parse-chunks.test.ts | 40 +++++ src/parser/parse-chunks.ts | 39 ++++ src/parser/utils.ts | 2 +- src/types/changes.ts | 10 +- 9 files changed, 291 insertions(+), 11 deletions(-) create mode 100644 src/parser/__snapshots__/parse-chunks.test.ts.snap create mode 100644 src/parser/parse-chunks.test.ts create mode 100644 src/parser/parse-chunks.ts diff --git a/src/parser/__snapshots__/parse-chunk-header.test.ts.snap b/src/parser/__snapshots__/parse-chunk-header.test.ts.snap index e0b7d67..8ca987f 100644 --- a/src/parser/__snapshots__/parse-chunk-header.test.ts.snap +++ b/src/parser/__snapshots__/parse-chunk-header.test.ts.snap @@ -1,5 +1,18 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`parseChunkHeader should parse chunk header. 1`] = ` +Object { + "addedPos": Object { + "lines": 4, + "start": 3, + }, + "deletedPos": Object { + "lines": 71, + "start": 3, + }, +} +`; + exports[`parseChunkHeader should parse concise chunk header 1`] = ` Object { "addedPos": Object { diff --git a/src/parser/__snapshots__/parse-chunks.test.ts.snap b/src/parser/__snapshots__/parse-chunks.test.ts.snap new file mode 100644 index 0000000..41b6b02 --- /dev/null +++ b/src/parser/__snapshots__/parse-chunks.test.ts.snap @@ -0,0 +1,167 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parseChunks should parse chunks. 1`] = ` +Array [ + Object { + "addedPos": Object { + "lines": 7, + "start": 18, + }, + "changes": Array [ + Object { + "content": "export type AnyChange = Added | Deleted | Unchanged;", + "line": 1, + "type": "Unchanged", + }, + Object { + "content": "/** changed file types */", + "line": 2, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 3, + "type": "Unchanged", + }, + Object { + "content": "interface BaseFileChange extends Base {", + "line": 4, + "type": "Unchanged", + }, + Object { + "content": " hunks: Hunk[];", + "line": 5, + "type": "Deleted", + }, + Object { + "content": " chunks: Chunk[];", + "line": 6, + "type": "Added", + }, + Object { + "content": "}", + "line": 7, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 8, + "type": "Unchanged", + }, + Object { + "content": "export interface ChangedFile extends BaseFileChange<'ChangedFile'> {}", + "line": 9, + "type": "Unchanged", + }, + ], + "deletedPos": Object { + "lines": 7, + "start": 18, + }, + "type": "Chunk", + }, + Object { + "addedPos": Object { + "lines": 13, + "start": 40, + }, + "changes": Array [ + Object { + "content": "export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile;", + "line": 10, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 11, + "type": "Unchanged", + }, + Object { + "content": "/** hunk */", + "line": 12, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 13, + "type": "Unchanged", + }, + Object { + "content": "export interface HunkPos {", + "line": 14, + "type": "Deleted", + }, + Object { + "content": "export interface ChunkPos {", + "line": 15, + "type": "Added", + }, + Object { + "content": " start: number;", + "line": 16, + "type": "Unchanged", + }, + Object { + "content": " lines: number;", + "line": 17, + "type": "Unchanged", + }, + Object { + "content": "}", + "line": 18, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 19, + "type": "Unchanged", + }, + Object { + "content": "export interface Hunk extends Base<'Hunk'> {", + "line": 20, + "type": "Deleted", + }, + Object { + "content": " addedPos: HunkPos;", + "line": 21, + "type": "Deleted", + }, + Object { + "content": " deletedPos: HunkPos;", + "line": 22, + "type": "Deleted", + }, + Object { + "content": "export interface Chunk extends Base<'Hunk'> {", + "line": 23, + "type": "Added", + }, + Object { + "content": " addedPos: ChunkPos;", + "line": 24, + "type": "Added", + }, + Object { + "content": " deletedPos: ChunkPos;", + "line": 25, + "type": "Added", + }, + Object { + "content": " changes: AnyChange[];", + "line": 26, + "type": "Unchanged", + }, + Object { + "content": "}", + "line": 27, + "type": "Unchanged", + }, + ], + "deletedPos": Object { + "lines": 13, + "start": 40, + }, + "type": "Chunk", + }, +] +`; diff --git a/src/parser/context.ts b/src/parser/context.ts index bd42a94..055d92c 100644 --- a/src/parser/context.ts +++ b/src/parser/context.ts @@ -5,8 +5,8 @@ export default class Context { this.lines = diff.split('\n'); } - public getCurLine(): string | undefined { - return this.lines[this.line - 1]; + public getCurLine(): string { + return this.lines[this.getInternalIndex()]; } public getCurLineIndex(): number { @@ -18,7 +18,16 @@ export default class Context { return this.getCurLine(); } + public eatChars(to: number) { + const line = this.getCurLine().slice(to); + this.lines[this.getInternalIndex()] = line; + } + public isEof(): boolean { return this.line > this.lines.length; } + + private getInternalIndex() { + return this.line - 1; + } } diff --git a/src/parser/parse-chunk-header.test.ts b/src/parser/parse-chunk-header.test.ts index ee2429e..9e89ee1 100644 --- a/src/parser/parse-chunk-header.test.ts +++ b/src/parser/parse-chunk-header.test.ts @@ -10,6 +10,16 @@ describe('parseChunkHeader', () => { expect(result).toMatchSnapshot(); }); + it('should parse chunk header.', () => { + const src = `@@ -3,71 +3,4 @@ unchanged line`; + const context = createContext(src); + const result = parseChunkHeader(context); + + expect(result).not.toBe(null); + expect(context.getCurLine()).toBe(' unchanged line'); + expect(result).toMatchSnapshot(); + }); + it('should parse concise chunk header', () => { const src = `@@ -1 +1 @@`; const result = parseChunkHeader(createContext(src)); diff --git a/src/parser/parse-chunk-header.ts b/src/parser/parse-chunk-header.ts index d08f4ca..052ad54 100644 --- a/src/parser/parse-chunk-header.ts +++ b/src/parser/parse-chunk-header.ts @@ -1,16 +1,16 @@ import { isChunkHeader } from './utils'; -import type { Hunk } from '../types'; +import type { Chunk } from '../types'; import type Context from './context'; export default function parseChunkHeader( context: Context -): Pick | null { +): Pick | null { const line = context.getCurLine(); if (!line || !isChunkHeader(line)) { return null; } - const exec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@\s?/.exec(line); + const exec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@/.exec(line); if (!exec) { return null; @@ -30,6 +30,8 @@ export default function parseChunkHeader( lines: delLines === undefined ? deletedStart : parseInt(delLines, 10), }; + context.eatChars(all.length); + return { addedPos, deletedPos, diff --git a/src/parser/parse-chunks.test.ts b/src/parser/parse-chunks.test.ts new file mode 100644 index 0000000..ed85453 --- /dev/null +++ b/src/parser/parse-chunks.test.ts @@ -0,0 +1,40 @@ +import { createContext } from '../test-utils'; +import parseChunks from './parse-chunks'; + +describe('parseChunks', () => { + it('should parse chunks.', () => { + // prettier-ignore + const src = +`@@ -18,7 +18,7 @@ export type AnyChange = Added | Deleted | Unchanged; + /** changed file types */ + + interface BaseFileChange extends Base { +- hunks: Hunk[]; ++ chunks: Chunk[]; + } + + export interface ChangedFile extends BaseFileChange<'ChangedFile'> {} +@@ -40,13 +40,13 @@ export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile; + + /** hunk */ + +-export interface HunkPos { ++export interface ChunkPos { + start: number; + lines: number; + } + +-export interface Hunk extends Base<'Hunk'> { +- addedPos: HunkPos; +- deletedPos: HunkPos; ++export interface Chunk extends Base<'Hunk'> { ++ addedPos: ChunkPos; ++ deletedPos: ChunkPos; + changes: AnyChange[]; + }`; + const result = parseChunks(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/src/parser/parse-chunks.ts b/src/parser/parse-chunks.ts new file mode 100644 index 0000000..aa95851 --- /dev/null +++ b/src/parser/parse-chunks.ts @@ -0,0 +1,39 @@ +import type { AnyChange, Chunk } from '../types'; +import type Context from './context'; +import parseChanges from './parse-changes'; +import parseChunkHeader from './parse-chunk-header'; +import { isChunkHeader } from './utils'; + +export default function parseChunks(context: Context): Chunk[] { + const chunks: Chunk[] = []; + + while (!context.isEof()) { + const line = context.getCurLine(); + if (isChunkHeader(line)) { + const chunk = parseChunk(context); + if (!chunk) { + break; + } + chunks.push(chunk); + } else { + break; + } + } + return chunks; +} + +function parseChunk(context: Context): Chunk | null { + const chunkHeader = parseChunkHeader(context); + + if (!chunkHeader) { + return null; + } + + const changes: AnyChange[] = parseChanges(context); + + return { + type: 'Chunk', + ...chunkHeader, + changes, + }; +} diff --git a/src/parser/utils.ts b/src/parser/utils.ts index ef5b345..abf85c4 100644 --- a/src/parser/utils.ts +++ b/src/parser/utils.ts @@ -45,7 +45,7 @@ export function isDeleteMarkerLine(line: string): boolean { * @returns return `true` if the given line is a chunk header, otherwise `false`. * @example * // start line of diff chunks - * "@@ -1 +1 @@ describe('utils', () => {... + * "@@ -1 +1 @@ describe('utils', () => {..." * "@@ -23,15 +23,15 @@ describe('utils', () => { ..." */ export function isChunkHeader(line: string): boolean { diff --git a/src/types/changes.ts b/src/types/changes.ts index e13e229..3339104 100644 --- a/src/types/changes.ts +++ b/src/types/changes.ts @@ -18,7 +18,7 @@ export type AnyChange = Added | Deleted | Unchanged; /** changed file types */ interface BaseFileChange extends Base { - hunks: Hunk[]; + chunks: Chunk[]; } export interface ChangedFile extends BaseFileChange<'ChangedFile'> {} @@ -40,13 +40,13 @@ export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile; /** hunk */ -export interface HunkPos { +export interface ChunkPos { start: number; lines: number; } -export interface Hunk extends Base<'Hunk'> { - addedPos: HunkPos; - deletedPos: HunkPos; +export interface Chunk extends Base<'Chunk'> { + addedPos: ChunkPos; + deletedPos: ChunkPos; changes: AnyChange[]; } From 8463e65723727c7a3063a4ccdecc3a357284c2ae Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 17:43:34 +0900 Subject: [PATCH 08/22] feat: add parse git diff --- src/index.ts | 2 + .../parse-add-marker.test.ts.snap | 3 - .../__snapshots__/parse-chunks.test.ts.snap | 161 ++++++++++++++++++ .../parse-delete-marker.test.ts.snap | 3 - .../parse-file-changes.test.ts.snap | 64 +++++++ src/parser/parse-add-marker.test.ts | 19 --- src/parser/parse-add-marker.ts | 12 -- src/parser/parse-change-markers.ts | 20 +-- src/parser/parse-changes.ts | 53 +++--- src/parser/parse-chunk-header.ts | 38 ++--- src/parser/parse-chunks.test.ts | 36 ++++ src/parser/parse-chunks.ts | 17 +- src/parser/parse-delete-marker.test.ts | 19 --- src/parser/parse-delete-marker.ts | 12 -- src/parser/parse-extended-header.ts | 29 ++++ src/parser/parse-file-changes.test.ts | 29 ++++ src/parser/parse-file-changes.ts | 60 +++++++ src/parser/parse-git-diff.ts | 14 +- src/parser/parse.ts | 7 - src/parser/utils.test.ts | 32 ---- src/types/changes.ts | 4 +- 21 files changed, 446 insertions(+), 188 deletions(-) delete mode 100644 src/parser/__snapshots__/parse-add-marker.test.ts.snap delete mode 100644 src/parser/__snapshots__/parse-delete-marker.test.ts.snap create mode 100644 src/parser/__snapshots__/parse-file-changes.test.ts.snap delete mode 100644 src/parser/parse-add-marker.test.ts delete mode 100644 src/parser/parse-add-marker.ts delete mode 100644 src/parser/parse-delete-marker.test.ts delete mode 100644 src/parser/parse-delete-marker.ts create mode 100644 src/parser/parse-extended-header.ts create mode 100644 src/parser/parse-file-changes.test.ts create mode 100644 src/parser/parse-file-changes.ts delete mode 100644 src/parser/parse.ts delete mode 100644 src/parser/utils.test.ts diff --git a/src/index.ts b/src/index.ts index e69de29..189505d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,2 @@ +import parseGitDiff from './parser/parse-git-diff'; +export default parseGitDiff; diff --git a/src/parser/__snapshots__/parse-add-marker.test.ts.snap b/src/parser/__snapshots__/parse-add-marker.test.ts.snap deleted file mode 100644 index dd49522..0000000 --- a/src/parser/__snapshots__/parse-add-marker.test.ts.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`parseAddMarker should parse add marker. 1`] = `"src/tests/addition.test.ts"`; diff --git a/src/parser/__snapshots__/parse-chunks.test.ts.snap b/src/parser/__snapshots__/parse-chunks.test.ts.snap index 41b6b02..3dbe797 100644 --- a/src/parser/__snapshots__/parse-chunks.test.ts.snap +++ b/src/parser/__snapshots__/parse-chunks.test.ts.snap @@ -1,5 +1,166 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`parseChunks should parse chunks 2. 1`] = ` +Array [ + Object { + "addedPos": Object { + "lines": 7, + "start": 18, + }, + "changes": Array [ + Object { + "content": "/** changed file types */", + "line": 2, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 3, + "type": "Unchanged", + }, + Object { + "content": "interface BaseFileChange extends Base {", + "line": 4, + "type": "Unchanged", + }, + Object { + "content": " hunks: Hunk[];", + "line": 5, + "type": "Deleted", + }, + Object { + "content": " chunks: Chunk[];", + "line": 6, + "type": "Added", + }, + Object { + "content": "}", + "line": 7, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 8, + "type": "Unchanged", + }, + Object { + "content": "export interface ChangedFile extends BaseFileChange<'ChangedFile'> {}", + "line": 9, + "type": "Unchanged", + }, + ], + "deletedPos": Object { + "lines": 7, + "start": 18, + }, + "type": "Chunk", + }, + Object { + "addedPos": Object { + "lines": 13, + "start": 40, + }, + "changes": Array [ + Object { + "content": "export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile;", + "line": 10, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 11, + "type": "Unchanged", + }, + Object { + "content": "/** hunk */", + "line": 12, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 13, + "type": "Unchanged", + }, + Object { + "content": "export interface HunkPos {", + "line": 14, + "type": "Deleted", + }, + Object { + "content": "export interface ChunkPos {", + "line": 15, + "type": "Added", + }, + Object { + "content": " start: number;", + "line": 16, + "type": "Unchanged", + }, + Object { + "content": " lines: number;", + "line": 17, + "type": "Unchanged", + }, + Object { + "content": "}", + "line": 18, + "type": "Unchanged", + }, + Object { + "content": "", + "line": 19, + "type": "Unchanged", + }, + Object { + "content": "export interface Hunk extends Base<'Hunk'> {", + "line": 20, + "type": "Deleted", + }, + Object { + "content": " addedPos: HunkPos;", + "line": 21, + "type": "Deleted", + }, + Object { + "content": " deletedPos: HunkPos;", + "line": 22, + "type": "Deleted", + }, + Object { + "content": "export interface Chunk extends Base<'Hunk'> {", + "line": 23, + "type": "Added", + }, + Object { + "content": " addedPos: ChunkPos;", + "line": 24, + "type": "Added", + }, + Object { + "content": " deletedPos: ChunkPos;", + "line": 25, + "type": "Added", + }, + Object { + "content": " changes: AnyChange[];", + "line": 26, + "type": "Unchanged", + }, + Object { + "content": "}", + "line": 27, + "type": "Unchanged", + }, + ], + "deletedPos": Object { + "lines": 13, + "start": 40, + }, + "type": "Chunk", + }, +] +`; + exports[`parseChunks should parse chunks. 1`] = ` Array [ Object { diff --git a/src/parser/__snapshots__/parse-delete-marker.test.ts.snap b/src/parser/__snapshots__/parse-delete-marker.test.ts.snap deleted file mode 100644 index 5812c03..0000000 --- a/src/parser/__snapshots__/parse-delete-marker.test.ts.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`parseDeleteMarker should parse delete marker. 1`] = `"src/tests/delete.test.ts"`; diff --git a/src/parser/__snapshots__/parse-file-changes.test.ts.snap b/src/parser/__snapshots__/parse-file-changes.test.ts.snap new file mode 100644 index 0000000..33d8317 --- /dev/null +++ b/src/parser/__snapshots__/parse-file-changes.test.ts.snap @@ -0,0 +1,64 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parseChunkHeader should parse normal chunk header. 1`] = ` +Array [ + Object { + "chunks": Array [ + Object { + "addedPos": Object { + "lines": 3, + "start": 1, + }, + "changes": Array [ + Object { + "content": "node_modules", + "line": 6, + "type": "Unchanged", + }, + Object { + "content": "build", + "line": 7, + "type": "Unchanged", + }, + Object { + "content": "coverage", + "line": 8, + "type": "Added", + }, + ], + "deletedPos": Object { + "lines": 2, + "start": 1, + }, + "type": "Chunk", + }, + ], + "path": ".gitignore", + "type": "ChangedFile", + }, + Object { + "chunks": Array [ + Object { + "addedPos": Object { + "lines": 0, + "start": 0, + }, + "changes": Array [ + Object { + "content": "# parse-git-diff", + "line": 15, + "type": "Deleted", + }, + ], + "deletedPos": Object { + "lines": 1, + "start": 1, + }, + "type": "Chunk", + }, + ], + "path": "README.md", + "type": "DeletedFile", + }, +] +`; diff --git a/src/parser/parse-add-marker.test.ts b/src/parser/parse-add-marker.test.ts deleted file mode 100644 index b393919..0000000 --- a/src/parser/parse-add-marker.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createContext } from '../test-utils'; -import parseAddMarker from './parse-add-marker'; - -describe('parseAddMarker', () => { - it('should parse add marker.', () => { - const src = `+++ b/src/tests/addition.test.ts`; - const result = parseAddMarker(createContext(src)); - - expect(result).not.toBe(null); - expect(result).toMatchSnapshot(); - }); - - it('should not parse delete marker.', () => { - const src = `--- a/src/tests/addition.test.ts`; - const result = parseAddMarker(createContext(src)); - - expect(result).toBe(null); - }); -}); diff --git a/src/parser/parse-add-marker.ts b/src/parser/parse-add-marker.ts deleted file mode 100644 index dbf42ea..0000000 --- a/src/parser/parse-add-marker.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { isAddMarkerLine } from './utils'; -import Context from './context'; - -export default function parseAddMarker(context: Context): string | null { - const line = context.getCurLine(); - - if (line && isAddMarkerLine(line)) { - context.nextLine(); - return line.replace('+++ ', '').replace('b/', ''); - } - return null; -} diff --git a/src/parser/parse-change-markers.ts b/src/parser/parse-change-markers.ts index 13f7f34..6c4b189 100644 --- a/src/parser/parse-change-markers.ts +++ b/src/parser/parse-change-markers.ts @@ -1,19 +1,19 @@ -import parseDeleteMarker from './parse-delete-marker'; -import parseAddMarker from './parse-add-marker'; import type Context from './context'; export default function parseChangeMarkers(context: Context): { deleted: string; added: string; } | null { - const deleted = parseDeleteMarker(context); - const added = parseAddMarker(context); + const deleted = parseMarker(context, '--- ')?.replace('a/', ''); + const added = parseMarker(context, '+++ ')?.replace('b/', ''); + return added && deleted ? { added, deleted } : null; +} - if (!added || !deleted) { - return null; +function parseMarker(context: Context, marker: string): string | null { + const line = context.getCurLine(); + if (line?.startsWith(marker)) { + context.nextLine(); + return line.replace(marker, ''); } - return { - added, - deleted, - }; + return null; } diff --git a/src/parser/parse-changes.ts b/src/parser/parse-changes.ts index 4508e93..9913c1e 100644 --- a/src/parser/parse-changes.ts +++ b/src/parser/parse-changes.ts @@ -1,36 +1,33 @@ -import { isAdditionLine, isDeletionLine, isUnchangedLine } from './utils'; -import Context from './context'; +import type Context from './context'; import type { AnyChange } from '../types'; -export default function parseChanges(context: Context): AnyChange[] { +type LineType = AnyChange['type']; + +const CHAR_TYPE_MAP: Record = { + '+': 'Added', + '-': 'Deleted', + ' ': 'Unchanged', +}; + +export default function parseChanges(ctx: Context): AnyChange[] { const changes: AnyChange[] = []; - while (!context.isEof()) { - const line = context.getCurLine()!; - const index = context.getCurLineIndex(); - if (isAdditionLine(line)) { - context.nextLine(); - changes.push({ - type: 'Added', - content: line.slice(1), - line: index, - }); - } else if (isDeletionLine(line)) { - context.nextLine(); - changes.push({ - type: 'Deleted', - content: line.slice(1), - line: index, - }); - } else if (isUnchangedLine(line)) { - context.nextLine(); - changes.push({ - type: 'Unchanged', - content: line.slice(1), - line: index, - }); - } else { + while (!ctx.isEof()) { + const line = ctx.getCurLine()!; + const index = ctx.getCurLineIndex(); + const type = getLineType(line); + if (!type) { break; } + ctx.nextLine(); + changes.push({ + type, + content: line.slice(1), + line: index, + }); } return changes; } + +function getLineType(line: string): LineType | null { + return CHAR_TYPE_MAP[line[0]] || null; +} diff --git a/src/parser/parse-chunk-header.ts b/src/parser/parse-chunk-header.ts index 052ad54..bf68431 100644 --- a/src/parser/parse-chunk-header.ts +++ b/src/parser/parse-chunk-header.ts @@ -1,39 +1,29 @@ -import { isChunkHeader } from './utils'; import type { Chunk } from '../types'; import type Context from './context'; export default function parseChunkHeader( - context: Context + ctx: Context ): Pick | null { - const line = context.getCurLine(); - if (!line || !isChunkHeader(line)) { - return null; - } - + const line = ctx.getCurLine(); const exec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@/.exec(line); - if (!exec) { return null; } - const [all, delStart, delLines, addStart, addLines] = exec; - - const addedStart = parseInt(addStart, 10); - const addedPos = { - start: addedStart, - lines: addLines === undefined ? addedStart : parseInt(addLines, 10), - }; - - const deletedStart = parseInt(delStart, 10); - const deletedPos = { - start: deletedStart, - lines: delLines === undefined ? deletedStart : parseInt(delLines, 10), + ctx.eatChars(all.length); + if (ctx.getCurLine().length === 0) { + ctx.nextLine(); + } + return { + addedPos: getPos(addStart, addLines), + deletedPos: getPos(delStart, delLines), }; +} - context.eatChars(all.length); - +function getPos(start: string, lines?: string) { + const startNum = parseInt(start, 10); return { - addedPos, - deletedPos, + start: startNum, + lines: lines === undefined ? startNum : parseInt(lines, 10), }; } diff --git a/src/parser/parse-chunks.test.ts b/src/parser/parse-chunks.test.ts index ed85453..3e6df68 100644 --- a/src/parser/parse-chunks.test.ts +++ b/src/parser/parse-chunks.test.ts @@ -24,6 +24,42 @@ describe('parseChunks', () => { lines: number; } +-export interface Hunk extends Base<'Hunk'> { +- addedPos: HunkPos; +- deletedPos: HunkPos; ++export interface Chunk extends Base<'Hunk'> { ++ addedPos: ChunkPos; ++ deletedPos: ChunkPos; + changes: AnyChange[]; + }`; + const result = parseChunks(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); + + it('should parse chunks 2.', () => { + // prettier-ignore + const src = +`@@ -18,7 +18,7 @@ + /** changed file types */ + + interface BaseFileChange extends Base { +- hunks: Hunk[]; ++ chunks: Chunk[]; + } + + export interface ChangedFile extends BaseFileChange<'ChangedFile'> {} +@@ -40,13 +40,13 @@ export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile; + + /** hunk */ + +-export interface HunkPos { ++export interface ChunkPos { + start: number; + lines: number; + } + -export interface Hunk extends Base<'Hunk'> { - addedPos: HunkPos; - deletedPos: HunkPos; diff --git a/src/parser/parse-chunks.ts b/src/parser/parse-chunks.ts index aa95851..545492e 100644 --- a/src/parser/parse-chunks.ts +++ b/src/parser/parse-chunks.ts @@ -2,31 +2,24 @@ import type { AnyChange, Chunk } from '../types'; import type Context from './context'; import parseChanges from './parse-changes'; import parseChunkHeader from './parse-chunk-header'; -import { isChunkHeader } from './utils'; export default function parseChunks(context: Context): Chunk[] { const chunks: Chunk[] = []; while (!context.isEof()) { - const line = context.getCurLine(); - if (isChunkHeader(line)) { - const chunk = parseChunk(context); - if (!chunk) { - break; - } - chunks.push(chunk); - } else { + const chunk = parseChunk(context); + if (!chunk) { break; } + chunks.push(chunk); } return chunks; } -function parseChunk(context: Context): Chunk | null { +function parseChunk(context: Context): Chunk | undefined { const chunkHeader = parseChunkHeader(context); - if (!chunkHeader) { - return null; + return; } const changes: AnyChange[] = parseChanges(context); diff --git a/src/parser/parse-delete-marker.test.ts b/src/parser/parse-delete-marker.test.ts deleted file mode 100644 index 069017b..0000000 --- a/src/parser/parse-delete-marker.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createContext } from '../test-utils'; -import parseDeleteMarker from './parse-delete-marker'; - -describe('parseDeleteMarker', () => { - it('should parse delete marker.', () => { - const src = `--- a/src/tests/delete.test.ts`; - const result = parseDeleteMarker(createContext(src)); - - expect(result).not.toBe(null); - expect(result).toMatchSnapshot(); - }); - - it('should not parse add marker.', () => { - const src = `+++ b/src/tests/delete.test.ts`; - const result = parseDeleteMarker(createContext(src)); - - expect(result).toBe(null); - }); -}); diff --git a/src/parser/parse-delete-marker.ts b/src/parser/parse-delete-marker.ts deleted file mode 100644 index 151084d..0000000 --- a/src/parser/parse-delete-marker.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { isDeleteMarkerLine } from './utils'; -import type Context from './context'; - -export default function parseDeleteMarker(context: Context): string | null { - const line = context.getCurLine(); - - if (line && isDeleteMarkerLine(line)) { - context.nextLine(); - return line.replace('--- ', '').replace('a/', ''); - } - return null; -} diff --git a/src/parser/parse-extended-header.ts b/src/parser/parse-extended-header.ts new file mode 100644 index 0000000..62ee7db --- /dev/null +++ b/src/parser/parse-extended-header.ts @@ -0,0 +1,29 @@ +import type Context from './context'; + +const UNHANDLED_EXTENDED_HEADERS = new Set([ + 'index', + 'old', + 'new', + 'copy', + 'rename', + 'similarity', + 'dissimilarity', +]); + +export default function parseExtendedHeader(ctx: Context) { + const line = ctx.getCurLine(); + const type = line.slice(0, line.indexOf(' ')); + + if (UNHANDLED_EXTENDED_HEADERS.has(type)) { + ctx.nextLine(); + return { + type: 'unhandled', + }; + } + switch (type) { + case 'deleted': + ctx.nextLine(); + return 'deleted'; + } + return null; +} diff --git a/src/parser/parse-file-changes.test.ts b/src/parser/parse-file-changes.test.ts new file mode 100644 index 0000000..4f0f3ed --- /dev/null +++ b/src/parser/parse-file-changes.test.ts @@ -0,0 +1,29 @@ +import { createContext } from '../test-utils'; +import parseFileChanges from './parse-file-changes'; + +describe('parseChunkHeader', () => { + it('should parse normal chunk header.', () => { + // prettier-ignore + const src = +`diff --git a/.gitignore b/.gitignore +index dd87e2d..1d2a37f 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1,2 +1,3 @@ + node_modules + build ++coverage +diff --git a/README.md b/README.md +deleted file mode 100644 +index 8229282..0000000 +--- a/README.md ++++ /dev/null +@@ -1 +0,0 @@ +-# parse-git-diff`; + + const result = parseFileChanges(createContext(src)); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/src/parser/parse-file-changes.ts b/src/parser/parse-file-changes.ts new file mode 100644 index 0000000..8b19d4b --- /dev/null +++ b/src/parser/parse-file-changes.ts @@ -0,0 +1,60 @@ +import type { AnyFileChange } from '../types'; +import type Context from './context'; +import parseExtendedHeader from './parse-extended-header'; +import parseChunks from './parse-chunks'; +import parseChangeMarkers from './parse-change-markers'; + +export default function parseFileChanges(ctx: Context): AnyFileChange[] { + const changedFiles: AnyFileChange[] = []; + while (!ctx.isEof()) { + const changed = parseFileChange(ctx); + if (!changed) { + break; + } + changedFiles.push(changed); + } + return changedFiles; +} + +function parseFileChange(ctx: Context): AnyFileChange | undefined { + if (!isComparisonInputLine(ctx.getCurLine())) { + return; + } + ctx.nextLine(); + + let isDeleted = false; + while (!ctx.isEof()) { + const extHeader = parseExtendedHeader(ctx); + if (!extHeader) { + break; + } + if (extHeader === 'deleted') isDeleted = true; + } + + const changeMarkers = parseChangeMarkers(ctx); + + if (!changeMarkers) { + return; + } + + const chunks = parseChunks(ctx); + + if (isDeleted) { + return { + type: 'DeletedFile', + chunks, + path: changeMarkers.deleted, + }; + } else { + return { + type: 'ChangedFile', + chunks, + path: changeMarkers.added, + }; + } + return; +} + +function isComparisonInputLine(line: string): boolean { + return line.indexOf('diff --git') === 0; +} diff --git a/src/parser/parse-git-diff.ts b/src/parser/parse-git-diff.ts index 4d32e8b..ee8579d 100644 --- a/src/parser/parse-git-diff.ts +++ b/src/parser/parse-git-diff.ts @@ -1,11 +1,13 @@ -import parseChanges from './parse-changes'; -import type Context from './context'; +import Context from './context'; import type { GitDiff } from '../types'; +import parseFileChanges from './parse-file-changes'; -export default function parseGitDiff(context: Context): GitDiff { - const gitDiff: GitDiff = { +export default function parseGitDiff(diff: string): GitDiff { + const ctx = new Context(diff); + const changedFiles = parseFileChanges(ctx); + + return { type: 'GitDiff', - changedFiles: [], + changedFiles, }; - return gitDiff; } diff --git a/src/parser/parse.ts b/src/parser/parse.ts deleted file mode 100644 index 863e277..0000000 --- a/src/parser/parse.ts +++ /dev/null @@ -1,7 +0,0 @@ -import Context from './context'; -import parseGitDiff from './parse-git-diff'; - -export default function parse(diff: string) { - const context = new Context(diff); - return parseGitDiff(context); -} diff --git a/src/parser/utils.test.ts b/src/parser/utils.test.ts deleted file mode 100644 index 6d9abb1..0000000 --- a/src/parser/utils.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as utils from '../parser/utils'; -import * as testUtils from '../test-utils'; - -describe('utils', () => { - test('isComparisonInputLine (valid)', () => { - const cases: [string][] = [['diff --git a/valid.diff b/valid.diff']]; - testUtils.createValidTester(utils.isComparisonInputLine)(cases); - }); - - test('isComparisonInputLine (invalid)', () => { - const cases: [string][] = [['dif --git a/valid.diff b/valid.diff']]; - testUtils.createInvalidTester(utils.isComparisonInputLine)(cases); - }); - - test('isMetaDataLine (valid)', () => { - const cases: [string][] = [['index a63c4ac..ac163a4 100644']]; - testUtils.createValidTester(utils.isMetaDataLine)(cases); - }); - - test('isMetaDataLine (invalid)', () => { - const cases: [string][] = [['inde a63c4ac..ac163a4 100644']]; - testUtils.createInvalidTester(utils.isMetaDataLine)(cases); - }); - - test('isChunkHeader (valid)', () => { - const cases: [string][] = [ - ["@@ -1 +1 @@ describe('utils', () => {"], - ["@@ -23,15 +23,15 @@ describe('utils', () => {"], - ]; - testUtils.createValidTester(utils.isChunkHeader)(cases); - }); -}); diff --git a/src/types/changes.ts b/src/types/changes.ts index 3339104..4d9e4c7 100644 --- a/src/types/changes.ts +++ b/src/types/changes.ts @@ -21,7 +21,9 @@ interface BaseFileChange extends Base { chunks: Chunk[]; } -export interface ChangedFile extends BaseFileChange<'ChangedFile'> {} +export interface ChangedFile extends BaseFileChange<'ChangedFile'> { + path: string; +} export interface AddedFile extends BaseFileChange<'AddedFile'> { path: string; From febe1991bccb276ea731fa297bc30bc03c282c42 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 17:44:35 +0900 Subject: [PATCH 09/22] chore: ignore coverage --- .gitignore | 1 + .prettierignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index dd87e2d..1d2a37f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules build +coverage diff --git a/.prettierignore b/.prettierignore index bf4fb70..28c99dd 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ node_modules build yarn.lock +coverage From 26fd670d495020e782b1bcbad4fa0d897986be64 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 18:23:17 +0900 Subject: [PATCH 10/22] fix: parseChunkHeader to ignore following after chunk header --- .../__snapshots__/parse-chunks.test.ts.snap | 15 --------------- src/parser/parse-chunk-header.test.ts | 1 - src/parser/parse-chunk-header.ts | 5 +---- 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/src/parser/__snapshots__/parse-chunks.test.ts.snap b/src/parser/__snapshots__/parse-chunks.test.ts.snap index 3dbe797..04188cb 100644 --- a/src/parser/__snapshots__/parse-chunks.test.ts.snap +++ b/src/parser/__snapshots__/parse-chunks.test.ts.snap @@ -61,11 +61,6 @@ Array [ "start": 40, }, "changes": Array [ - Object { - "content": "export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile;", - "line": 10, - "type": "Unchanged", - }, Object { "content": "", "line": 11, @@ -169,11 +164,6 @@ Array [ "start": 18, }, "changes": Array [ - Object { - "content": "export type AnyChange = Added | Deleted | Unchanged;", - "line": 1, - "type": "Unchanged", - }, Object { "content": "/** changed file types */", "line": 2, @@ -227,11 +217,6 @@ Array [ "start": 40, }, "changes": Array [ - Object { - "content": "export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile;", - "line": 10, - "type": "Unchanged", - }, Object { "content": "", "line": 11, diff --git a/src/parser/parse-chunk-header.test.ts b/src/parser/parse-chunk-header.test.ts index 9e89ee1..dfb1e06 100644 --- a/src/parser/parse-chunk-header.test.ts +++ b/src/parser/parse-chunk-header.test.ts @@ -16,7 +16,6 @@ describe('parseChunkHeader', () => { const result = parseChunkHeader(context); expect(result).not.toBe(null); - expect(context.getCurLine()).toBe(' unchanged line'); expect(result).toMatchSnapshot(); }); diff --git a/src/parser/parse-chunk-header.ts b/src/parser/parse-chunk-header.ts index bf68431..d952349 100644 --- a/src/parser/parse-chunk-header.ts +++ b/src/parser/parse-chunk-header.ts @@ -10,10 +10,7 @@ export default function parseChunkHeader( return null; } const [all, delStart, delLines, addStart, addLines] = exec; - ctx.eatChars(all.length); - if (ctx.getCurLine().length === 0) { - ctx.nextLine(); - } + ctx.nextLine(); return { addedPos: getPos(addStart, addLines), deletedPos: getPos(delStart, delLines), From eee71be1ea3ba8953ba48092f2f6d5dd08011d6f Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 20:12:09 +0900 Subject: [PATCH 11/22] feat: handle line change --- .../__snapshots__/parse-changes.test.ts.snap | 12 +- .../parse-chunk-header.test.ts.snap | 12 +- .../__snapshots__/parse-chunks.test.ts.snap | 170 ++++++++++-------- .../parse-file-changes.test.ts.snap | 30 ++-- src/parser/parse-changes.test.ts | 12 +- src/parser/parse-changes.ts | 48 ++++- src/parser/parse-chunk-header.ts | 8 +- src/parser/parse-chunks.ts | 6 +- src/types/changes.ts | 20 ++- 9 files changed, 203 insertions(+), 115 deletions(-) diff --git a/src/parser/__snapshots__/parse-changes.test.ts.snap b/src/parser/__snapshots__/parse-changes.test.ts.snap index 84bb8f8..bc1a585 100644 --- a/src/parser/__snapshots__/parse-changes.test.ts.snap +++ b/src/parser/__snapshots__/parse-changes.test.ts.snap @@ -4,22 +4,26 @@ exports[`parseChanges should parse changes. 1`] = ` Array [ Object { "content": "unchanged", - "line": 1, + "lineAfter": 1, + "lineBefore": 1, "type": "Unchanged", }, Object { "content": "-deleted line", - "line": 2, + "lineAfter": 2, + "lineBefore": 2, "type": "Unchanged", }, Object { "content": "+added line", - "line": 3, + "lineAfter": 3, + "lineBefore": 3, "type": "Unchanged", }, Object { "content": "unchanged", - "line": 4, + "lineAfter": 4, + "lineBefore": 4, "type": "Unchanged", }, ] diff --git a/src/parser/__snapshots__/parse-chunk-header.test.ts.snap b/src/parser/__snapshots__/parse-chunk-header.test.ts.snap index 8ca987f..bdcee93 100644 --- a/src/parser/__snapshots__/parse-chunk-header.test.ts.snap +++ b/src/parser/__snapshots__/parse-chunk-header.test.ts.snap @@ -2,11 +2,11 @@ exports[`parseChunkHeader should parse chunk header. 1`] = ` Object { - "addedPos": Object { + "rangeAfter": Object { "lines": 4, "start": 3, }, - "deletedPos": Object { + "rangeBefore": Object { "lines": 71, "start": 3, }, @@ -15,11 +15,11 @@ Object { exports[`parseChunkHeader should parse concise chunk header 1`] = ` Object { - "addedPos": Object { + "rangeAfter": Object { "lines": 1, "start": 1, }, - "deletedPos": Object { + "rangeBefore": Object { "lines": 1, "start": 1, }, @@ -28,11 +28,11 @@ Object { exports[`parseChunkHeader should parse normal chunk header. 1`] = ` Object { - "addedPos": Object { + "rangeAfter": Object { "lines": 4, "start": 3, }, - "deletedPos": Object { + "rangeBefore": Object { "lines": 71, "start": 3, }, diff --git a/src/parser/__snapshots__/parse-chunks.test.ts.snap b/src/parser/__snapshots__/parse-chunks.test.ts.snap index 04188cb..961797e 100644 --- a/src/parser/__snapshots__/parse-chunks.test.ts.snap +++ b/src/parser/__snapshots__/parse-chunks.test.ts.snap @@ -3,151 +3,166 @@ exports[`parseChunks should parse chunks 2. 1`] = ` Array [ Object { - "addedPos": Object { - "lines": 7, - "start": 18, - }, "changes": Array [ Object { "content": "/** changed file types */", - "line": 2, + "lineAfter": 18, + "lineBefore": 18, "type": "Unchanged", }, Object { "content": "", - "line": 3, + "lineAfter": 19, + "lineBefore": 19, "type": "Unchanged", }, Object { "content": "interface BaseFileChange extends Base {", - "line": 4, + "lineAfter": 20, + "lineBefore": 20, "type": "Unchanged", }, Object { "content": " hunks: Hunk[];", - "line": 5, + "lineBefore": 21, "type": "Deleted", }, Object { "content": " chunks: Chunk[];", - "line": 6, + "lineAfter": 21, "type": "Added", }, Object { "content": "}", - "line": 7, + "lineAfter": 22, + "lineBefore": 22, "type": "Unchanged", }, Object { "content": "", - "line": 8, + "lineAfter": 23, + "lineBefore": 23, "type": "Unchanged", }, Object { "content": "export interface ChangedFile extends BaseFileChange<'ChangedFile'> {}", - "line": 9, + "lineAfter": 24, + "lineBefore": 24, "type": "Unchanged", }, ], - "deletedPos": Object { + "rangeAfter": Object { + "lines": 7, + "start": 18, + }, + "rangeBefore": Object { "lines": 7, "start": 18, }, "type": "Chunk", }, Object { - "addedPos": Object { - "lines": 13, - "start": 40, - }, "changes": Array [ Object { "content": "", - "line": 11, + "lineAfter": 40, + "lineBefore": 40, "type": "Unchanged", }, Object { "content": "/** hunk */", - "line": 12, + "lineAfter": 41, + "lineBefore": 41, "type": "Unchanged", }, Object { "content": "", - "line": 13, + "lineAfter": 42, + "lineBefore": 42, "type": "Unchanged", }, Object { "content": "export interface HunkPos {", - "line": 14, + "lineBefore": 43, "type": "Deleted", }, Object { "content": "export interface ChunkPos {", - "line": 15, + "lineAfter": 43, "type": "Added", }, Object { "content": " start: number;", - "line": 16, + "lineAfter": 44, + "lineBefore": 44, "type": "Unchanged", }, Object { "content": " lines: number;", - "line": 17, + "lineAfter": 45, + "lineBefore": 45, "type": "Unchanged", }, Object { "content": "}", - "line": 18, + "lineAfter": 46, + "lineBefore": 46, "type": "Unchanged", }, Object { "content": "", - "line": 19, + "lineAfter": 47, + "lineBefore": 47, "type": "Unchanged", }, Object { "content": "export interface Hunk extends Base<'Hunk'> {", - "line": 20, + "lineBefore": 48, "type": "Deleted", }, Object { "content": " addedPos: HunkPos;", - "line": 21, + "lineBefore": 49, "type": "Deleted", }, Object { "content": " deletedPos: HunkPos;", - "line": 22, + "lineBefore": 50, "type": "Deleted", }, Object { "content": "export interface Chunk extends Base<'Hunk'> {", - "line": 23, + "lineAfter": 48, "type": "Added", }, Object { "content": " addedPos: ChunkPos;", - "line": 24, + "lineAfter": 49, "type": "Added", }, Object { "content": " deletedPos: ChunkPos;", - "line": 25, + "lineAfter": 50, "type": "Added", }, Object { "content": " changes: AnyChange[];", - "line": 26, + "lineAfter": 51, + "lineBefore": 51, "type": "Unchanged", }, Object { "content": "}", - "line": 27, + "lineAfter": 52, + "lineBefore": 52, "type": "Unchanged", }, ], - "deletedPos": Object { + "rangeAfter": Object { + "lines": 13, + "start": 40, + }, + "rangeBefore": Object { "lines": 13, "start": 40, }, @@ -159,151 +174,166 @@ Array [ exports[`parseChunks should parse chunks. 1`] = ` Array [ Object { - "addedPos": Object { - "lines": 7, - "start": 18, - }, "changes": Array [ Object { "content": "/** changed file types */", - "line": 2, + "lineAfter": 18, + "lineBefore": 18, "type": "Unchanged", }, Object { "content": "", - "line": 3, + "lineAfter": 19, + "lineBefore": 19, "type": "Unchanged", }, Object { "content": "interface BaseFileChange extends Base {", - "line": 4, + "lineAfter": 20, + "lineBefore": 20, "type": "Unchanged", }, Object { "content": " hunks: Hunk[];", - "line": 5, + "lineBefore": 21, "type": "Deleted", }, Object { "content": " chunks: Chunk[];", - "line": 6, + "lineAfter": 21, "type": "Added", }, Object { "content": "}", - "line": 7, + "lineAfter": 22, + "lineBefore": 22, "type": "Unchanged", }, Object { "content": "", - "line": 8, + "lineAfter": 23, + "lineBefore": 23, "type": "Unchanged", }, Object { "content": "export interface ChangedFile extends BaseFileChange<'ChangedFile'> {}", - "line": 9, + "lineAfter": 24, + "lineBefore": 24, "type": "Unchanged", }, ], - "deletedPos": Object { + "rangeAfter": Object { + "lines": 7, + "start": 18, + }, + "rangeBefore": Object { "lines": 7, "start": 18, }, "type": "Chunk", }, Object { - "addedPos": Object { - "lines": 13, - "start": 40, - }, "changes": Array [ Object { "content": "", - "line": 11, + "lineAfter": 40, + "lineBefore": 40, "type": "Unchanged", }, Object { "content": "/** hunk */", - "line": 12, + "lineAfter": 41, + "lineBefore": 41, "type": "Unchanged", }, Object { "content": "", - "line": 13, + "lineAfter": 42, + "lineBefore": 42, "type": "Unchanged", }, Object { "content": "export interface HunkPos {", - "line": 14, + "lineBefore": 43, "type": "Deleted", }, Object { "content": "export interface ChunkPos {", - "line": 15, + "lineAfter": 43, "type": "Added", }, Object { "content": " start: number;", - "line": 16, + "lineAfter": 44, + "lineBefore": 44, "type": "Unchanged", }, Object { "content": " lines: number;", - "line": 17, + "lineAfter": 45, + "lineBefore": 45, "type": "Unchanged", }, Object { "content": "}", - "line": 18, + "lineAfter": 46, + "lineBefore": 46, "type": "Unchanged", }, Object { "content": "", - "line": 19, + "lineAfter": 47, + "lineBefore": 47, "type": "Unchanged", }, Object { "content": "export interface Hunk extends Base<'Hunk'> {", - "line": 20, + "lineBefore": 48, "type": "Deleted", }, Object { "content": " addedPos: HunkPos;", - "line": 21, + "lineBefore": 49, "type": "Deleted", }, Object { "content": " deletedPos: HunkPos;", - "line": 22, + "lineBefore": 50, "type": "Deleted", }, Object { "content": "export interface Chunk extends Base<'Hunk'> {", - "line": 23, + "lineAfter": 48, "type": "Added", }, Object { "content": " addedPos: ChunkPos;", - "line": 24, + "lineAfter": 49, "type": "Added", }, Object { "content": " deletedPos: ChunkPos;", - "line": 25, + "lineAfter": 50, "type": "Added", }, Object { "content": " changes: AnyChange[];", - "line": 26, + "lineAfter": 51, + "lineBefore": 51, "type": "Unchanged", }, Object { "content": "}", - "line": 27, + "lineAfter": 52, + "lineBefore": 52, "type": "Unchanged", }, ], - "deletedPos": Object { + "rangeAfter": Object { + "lines": 13, + "start": 40, + }, + "rangeBefore": Object { "lines": 13, "start": 40, }, diff --git a/src/parser/__snapshots__/parse-file-changes.test.ts.snap b/src/parser/__snapshots__/parse-file-changes.test.ts.snap index 33d8317..1e9f446 100644 --- a/src/parser/__snapshots__/parse-file-changes.test.ts.snap +++ b/src/parser/__snapshots__/parse-file-changes.test.ts.snap @@ -5,28 +5,30 @@ Array [ Object { "chunks": Array [ Object { - "addedPos": Object { - "lines": 3, - "start": 1, - }, "changes": Array [ Object { "content": "node_modules", - "line": 6, + "lineAfter": 1, + "lineBefore": 1, "type": "Unchanged", }, Object { "content": "build", - "line": 7, + "lineAfter": 2, + "lineBefore": 2, "type": "Unchanged", }, Object { "content": "coverage", - "line": 8, + "lineAfter": 3, "type": "Added", }, ], - "deletedPos": Object { + "rangeAfter": Object { + "lines": 3, + "start": 1, + }, + "rangeBefore": Object { "lines": 2, "start": 1, }, @@ -39,18 +41,18 @@ Array [ Object { "chunks": Array [ Object { - "addedPos": Object { - "lines": 0, - "start": 0, - }, "changes": Array [ Object { "content": "# parse-git-diff", - "line": 15, + "lineBefore": 1, "type": "Deleted", }, ], - "deletedPos": Object { + "rangeAfter": Object { + "lines": 0, + "start": 0, + }, + "rangeBefore": Object { "lines": 1, "start": 1, }, diff --git a/src/parser/parse-changes.test.ts b/src/parser/parse-changes.test.ts index d1485b4..d53fbb5 100644 --- a/src/parser/parse-changes.test.ts +++ b/src/parser/parse-changes.test.ts @@ -10,7 +10,17 @@ describe('parseChanges', () => { +added line unchanged`; - const result = parseChanges(createContext(src)); + const result = parseChanges( + createContext(src), + { + lines: 4, + start: 1, + }, + { + lines: 4, + start: 1, + } + ); expect(result).not.toBe(null); expect(result).toMatchSnapshot(); diff --git a/src/parser/parse-changes.ts b/src/parser/parse-changes.ts index 9913c1e..4463565 100644 --- a/src/parser/parse-changes.ts +++ b/src/parser/parse-changes.ts @@ -1,5 +1,5 @@ import type Context from './context'; -import type { AnyChange } from '../types'; +import type { AnyChange, ChunkRange } from '../types'; type LineType = AnyChange['type']; @@ -9,21 +9,53 @@ const CHAR_TYPE_MAP: Record = { ' ': 'Unchanged', }; -export default function parseChanges(ctx: Context): AnyChange[] { +export default function parseChanges( + ctx: Context, + rangeBefore: ChunkRange, + rangeAfter: ChunkRange +): AnyChange[] { const changes: AnyChange[] = []; + let lineBefore = rangeBefore.start; + let lineAfter = rangeAfter.start; + while (!ctx.isEof()) { const line = ctx.getCurLine()!; - const index = ctx.getCurLineIndex(); const type = getLineType(line); if (!type) { break; } ctx.nextLine(); - changes.push({ - type, - content: line.slice(1), - line: index, - }); + + let change: AnyChange; + const content = line.slice(1); + switch (type) { + case 'Added': { + change = { + type: 'Added', + lineAfter: lineAfter++, + content, + }; + break; + } + case 'Deleted': { + change = { + type: 'Deleted', + lineBefore: lineBefore++, + content, + }; + break; + } + case 'Unchanged': { + change = { + type: 'Unchanged', + lineBefore: lineBefore++, + lineAfter: lineAfter++, + content, + }; + break; + } + } + changes.push(change); } return changes; } diff --git a/src/parser/parse-chunk-header.ts b/src/parser/parse-chunk-header.ts index d952349..c443f07 100644 --- a/src/parser/parse-chunk-header.ts +++ b/src/parser/parse-chunk-header.ts @@ -3,7 +3,7 @@ import type Context from './context'; export default function parseChunkHeader( ctx: Context -): Pick | null { +): Pick | null { const line = ctx.getCurLine(); const exec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@/.exec(line); if (!exec) { @@ -12,12 +12,12 @@ export default function parseChunkHeader( const [all, delStart, delLines, addStart, addLines] = exec; ctx.nextLine(); return { - addedPos: getPos(addStart, addLines), - deletedPos: getPos(delStart, delLines), + rangeAfter: getRange(addStart, addLines), + rangeBefore: getRange(delStart, delLines), }; } -function getPos(start: string, lines?: string) { +function getRange(start: string, lines?: string) { const startNum = parseInt(start, 10); return { start: startNum, diff --git a/src/parser/parse-chunks.ts b/src/parser/parse-chunks.ts index 545492e..27132a3 100644 --- a/src/parser/parse-chunks.ts +++ b/src/parser/parse-chunks.ts @@ -22,7 +22,11 @@ function parseChunk(context: Context): Chunk | undefined { return; } - const changes: AnyChange[] = parseChanges(context); + const changes: AnyChange[] = parseChanges( + context, + chunkHeader.rangeBefore, + chunkHeader.rangeAfter + ); return { type: 'Chunk', diff --git a/src/types/changes.ts b/src/types/changes.ts index 4d9e4c7..71a5e3a 100644 --- a/src/types/changes.ts +++ b/src/types/changes.ts @@ -3,15 +3,21 @@ import type { Base } from './common'; /** changed content types */ interface BaseChange extends Base { - line: number; content: string; } -export interface Added extends BaseChange<'Added'> {} +export interface Added extends BaseChange<'Added'> { + lineAfter: number; +} -export interface Deleted extends BaseChange<'Deleted'> {} +export interface Deleted extends BaseChange<'Deleted'> { + lineBefore: number; +} -export interface Unchanged extends BaseChange<'Unchanged'> {} +export interface Unchanged extends BaseChange<'Unchanged'> { + lineBefore: number; + lineAfter: number; +} export type AnyChange = Added | Deleted | Unchanged; @@ -42,13 +48,13 @@ export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile; /** hunk */ -export interface ChunkPos { +export interface ChunkRange { start: number; lines: number; } export interface Chunk extends Base<'Chunk'> { - addedPos: ChunkPos; - deletedPos: ChunkPos; + rangeBefore: ChunkRange; + rangeAfter: ChunkRange; changes: AnyChange[]; } From e0d5421f93f6a5579bffb2bfc6f989836effff4a Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 20:17:53 +0900 Subject: [PATCH 12/22] chore: update package.json --- package.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f3f240d..999917d 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.0.1", "description": "", "main": "build/index.js", + "types": "build/index.d.ts", "scripts": { "build": "rimraf build && tsc", "format": "prettier . --write", @@ -28,5 +29,12 @@ "rimraf": "^3.0.2", "ts-jest": "^27.0.5", "typescript": "^4.4.3" - } + }, + "files": [ + "build", + "tsconfig.json", + "README.md", + "yarn.lock", + "package.json" + ] } From 00898ff4e2c8c280f57bbc7685ff103f670dfdae Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 20:19:17 +0900 Subject: [PATCH 13/22] chore: remove postinstall --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 999917d..538b47a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parse-git-diff", - "version": "0.0.1", + "version": "0.0.2", "description": "", "main": "build/index.js", "types": "build/index.d.ts", @@ -8,7 +8,6 @@ "build": "rimraf build && tsc", "format": "prettier . --write", "test": "jest", - "postinstall": "yarn husky install", "check:all": "prettier --check . && tsc --noEmit && yarn test" }, "repository": { From aa3c15c8a7d4e52833476f45cb1c4009fba8b641 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 20:27:29 +0900 Subject: [PATCH 14/22] chore: update package.json --- package.json | 8 ++++++-- src/index.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 538b47a..0e29bde 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "parse-git-diff", - "version": "0.0.2", - "description": "", + "version": "0.0.3", + "description": "Parse git diff", "main": "build/index.js", "types": "build/index.d.ts", "scripts": { @@ -35,5 +35,9 @@ "README.md", "yarn.lock", "package.json" + ], + "keywords": [ + "git", + "git diff" ] } diff --git a/src/index.ts b/src/index.ts index 189505d..b484a9e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,2 @@ import parseGitDiff from './parser/parse-git-diff'; -export default parseGitDiff; +export = parseGitDiff; From 4a5d9bff7a71465bdf5b7720e1889b44b818ac87 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Sun, 17 Oct 2021 20:36:25 +0900 Subject: [PATCH 15/22] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1cb7ff6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 YeonJuan + +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. From bf77a869d79a28d5876271894effbc70591e6c50 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 20:50:13 +0900 Subject: [PATCH 16/22] publish: v0.0.4 --- README.md | 17 +++++++++++++++++ package.json | 3 ++- tsconfig.json | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8229282..5252483 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ # parse-git-diff + +## Installation + +```console +npm install parse-git-diff +``` + +## Usage + +```js +import parseGitDiff from 'parse-git-diff'; +parseGitDiff('...'); +``` + +## License + +- [MIT](./LICENSE) diff --git a/package.json b/package.json index 0e29bde..bdb688e 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "parse-git-diff", - "version": "0.0.3", + "version": "0.0.4", "description": "Parse git diff", "main": "build/index.js", "types": "build/index.d.ts", "scripts": { + "prepublish": "yarn build", "build": "rimraf build && tsc", "format": "prettier . --write", "test": "jest", diff --git a/tsconfig.json b/tsconfig.json index ebf30ce..9c9c250 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,5 +10,5 @@ "outDir": "build", "rootDir": "src" }, - "exclude": ["src/**/*.test.ts", "build"] + "exclude": ["src/**/*.test.ts", "build", "node_modules"] } From 460d8164d665fae460e56a99fd62f03d59e9146a Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 21:13:28 +0900 Subject: [PATCH 17/22] feat: add new files --- .../__snapshots__/parse-git-diff.test.ts.snap | 113 ++++++++++++++++++ src/parser/parse-extended-header.ts | 16 ++- src/parser/parse-file-changes.ts | 8 ++ src/parser/parse-git-diff.test.ts | 37 ++++++ src/parser/parse-git-diff.ts | 4 +- src/types/changes.ts | 4 +- src/types/nodes.ts | 2 +- 7 files changed, 174 insertions(+), 10 deletions(-) create mode 100644 src/parser/__snapshots__/parse-git-diff.test.ts.snap create mode 100644 src/parser/parse-git-diff.test.ts diff --git a/src/parser/__snapshots__/parse-git-diff.test.ts.snap b/src/parser/__snapshots__/parse-git-diff.test.ts.snap new file mode 100644 index 0000000..2296978 --- /dev/null +++ b/src/parser/__snapshots__/parse-git-diff.test.ts.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parseGitDiff should parse new file. 1`] = ` +Object { + "files": Array [ + Object { + "chunks": Array [ + Object { + "changes": Array [ + Object { + "content": "{", + "lineAfter": 1, + "type": "Added", + }, + Object { + "content": " \\"name\\": \\"parse-git-diff-test\\",", + "lineAfter": 2, + "type": "Added", + }, + Object { + "content": " \\"version\\": \\"1.0.0\\",", + "lineAfter": 3, + "type": "Added", + }, + Object { + "content": " \\"description\\": \\"\\",", + "lineAfter": 4, + "type": "Added", + }, + Object { + "content": " \\"main\\": \\"index.js\\",", + "lineAfter": 5, + "type": "Added", + }, + Object { + "content": " \\"scripts\\": {", + "lineAfter": 6, + "type": "Added", + }, + Object { + "content": " \\"build\\": \\"tsc\\"", + "lineAfter": 7, + "type": "Added", + }, + Object { + "content": " },", + "lineAfter": 8, + "type": "Added", + }, + Object { + "content": " \\"author\\": \\"\\",", + "lineAfter": 9, + "type": "Added", + }, + Object { + "content": " \\"license\\": \\"ISC\\",", + "lineAfter": 10, + "type": "Added", + }, + Object { + "content": " \\"dependencies\\": {", + "lineAfter": 11, + "type": "Added", + }, + Object { + "content": " \\"parse-git-diff\\": \\"0.0.3\\"", + "lineAfter": 12, + "type": "Added", + }, + Object { + "content": " },", + "lineAfter": 13, + "type": "Added", + }, + Object { + "content": " \\"devDependencies\\": {", + "lineAfter": 14, + "type": "Added", + }, + Object { + "content": " \\"typescript\\": \\"^4.4.4\\"", + "lineAfter": 15, + "type": "Added", + }, + Object { + "content": " }", + "lineAfter": 16, + "type": "Added", + }, + Object { + "content": "}", + "lineAfter": 17, + "type": "Added", + }, + ], + "rangeAfter": Object { + "lines": 17, + "start": 1, + }, + "rangeBefore": Object { + "lines": 0, + "start": 0, + }, + "type": "Chunk", + }, + ], + "path": "parse-git-diff-test/packages.json", + "type": "AddedFile", + }, + ], + "type": "GitDiff", +} +`; diff --git a/src/parser/parse-extended-header.ts b/src/parser/parse-extended-header.ts index 62ee7db..fa6bd1e 100644 --- a/src/parser/parse-extended-header.ts +++ b/src/parser/parse-extended-header.ts @@ -3,13 +3,16 @@ import type Context from './context'; const UNHANDLED_EXTENDED_HEADERS = new Set([ 'index', 'old', - 'new', 'copy', 'rename', 'similarity', 'dissimilarity', ]); +const startsWith = (str: string, target: string) => { + return str.indexOf(target) === 0; +}; + export default function parseExtendedHeader(ctx: Context) { const line = ctx.getCurLine(); const type = line.slice(0, line.indexOf(' ')); @@ -20,10 +23,13 @@ export default function parseExtendedHeader(ctx: Context) { type: 'unhandled', }; } - switch (type) { - case 'deleted': - ctx.nextLine(); - return 'deleted'; + if (startsWith(line, 'deleted ')) { + ctx.nextLine(); + return 'deleted'; + } else if (startsWith(line, 'new file ')) { + ctx.nextLine(); + return 'new file'; } + return null; } diff --git a/src/parser/parse-file-changes.ts b/src/parser/parse-file-changes.ts index 8b19d4b..2a9cd20 100644 --- a/src/parser/parse-file-changes.ts +++ b/src/parser/parse-file-changes.ts @@ -23,12 +23,14 @@ function parseFileChange(ctx: Context): AnyFileChange | undefined { ctx.nextLine(); let isDeleted = false; + let isNew = false; while (!ctx.isEof()) { const extHeader = parseExtendedHeader(ctx); if (!extHeader) { break; } if (extHeader === 'deleted') isDeleted = true; + if (extHeader === 'new file') isNew = true; } const changeMarkers = parseChangeMarkers(ctx); @@ -45,6 +47,12 @@ function parseFileChange(ctx: Context): AnyFileChange | undefined { chunks, path: changeMarkers.deleted, }; + } else if (isNew) { + return { + type: 'AddedFile', + chunks, + path: changeMarkers.added, + }; } else { return { type: 'ChangedFile', diff --git a/src/parser/parse-git-diff.test.ts b/src/parser/parse-git-diff.test.ts new file mode 100644 index 0000000..8dcfa57 --- /dev/null +++ b/src/parser/parse-git-diff.test.ts @@ -0,0 +1,37 @@ +import { createContext } from '../test-utils'; +import parseGitDiff from './parse-git-diff'; + +describe('parseGitDiff', () => { + it('should parse new file.', () => { + // prettier-ignore + const src = +`diff --git a/parse-git-diff-test/packages.json b/parse-git-diff-test/packages.json +new file mode 100644 +index 0000000..5515040 +--- /dev/null ++++ b/parse-git-diff-test/packages.json +@@ -0,0 +1,17 @@ ++{ ++ "name": "parse-git-diff-test", ++ "version": "1.0.0", ++ "description": "", ++ "main": "index.js", ++ "scripts": { ++ "build": "tsc" ++ }, ++ "author": "", ++ "license": "ISC", ++ "dependencies": { ++ "parse-git-diff": "0.0.3" ++ }, ++ "devDependencies": { ++ "typescript": "^4.4.4" ++ } ++}`; + + const result = parseGitDiff(src); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/src/parser/parse-git-diff.ts b/src/parser/parse-git-diff.ts index ee8579d..3f5498c 100644 --- a/src/parser/parse-git-diff.ts +++ b/src/parser/parse-git-diff.ts @@ -4,10 +4,10 @@ import parseFileChanges from './parse-file-changes'; export default function parseGitDiff(diff: string): GitDiff { const ctx = new Context(diff); - const changedFiles = parseFileChanges(ctx); + const files = parseFileChanges(ctx); return { type: 'GitDiff', - changedFiles, + files, }; } diff --git a/src/types/changes.ts b/src/types/changes.ts index 71a5e3a..421c4cc 100644 --- a/src/types/changes.ts +++ b/src/types/changes.ts @@ -40,8 +40,8 @@ export interface DeletedFile extends BaseFileChange<'DeletedFile'> { } export interface RenamedFile extends BaseFileChange<'RenamedFile'> { - from: string; - to: string; + pathBefore: string; + pathAfter: string; } export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile; diff --git a/src/types/nodes.ts b/src/types/nodes.ts index 2f1db87..59c756c 100644 --- a/src/types/nodes.ts +++ b/src/types/nodes.ts @@ -2,5 +2,5 @@ import type { Base } from './common'; import type { AnyFileChange } from './changes'; export interface GitDiff extends Base<'GitDiff'> { - changedFiles: AnyFileChange[]; + files: AnyFileChange[]; } From 40160f12d231affd365bfdb1d08d893aa6b54f60 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 21:18:08 +0900 Subject: [PATCH 18/22] tests: add new file test case --- .../__snapshots__/parse-git-diff.test.ts.snap | 114 +++++++++++++++++- src/parser/parse-git-diff.test.ts | 34 +++++- 2 files changed, 146 insertions(+), 2 deletions(-) diff --git a/src/parser/__snapshots__/parse-git-diff.test.ts.snap b/src/parser/__snapshots__/parse-git-diff.test.ts.snap index 2296978..2e65e2b 100644 --- a/src/parser/__snapshots__/parse-git-diff.test.ts.snap +++ b/src/parser/__snapshots__/parse-git-diff.test.ts.snap @@ -1,6 +1,118 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`parseGitDiff should parse new file. 1`] = ` +exports[`parseGitDiff deleted file 1`] = ` +Object { + "files": Array [ + Object { + "chunks": Array [ + Object { + "changes": Array [ + Object { + "content": "{", + "lineBefore": 1, + "type": "Deleted", + }, + Object { + "content": " \\"name\\": \\"parse-git-diff-test\\",", + "lineBefore": 2, + "type": "Deleted", + }, + Object { + "content": " \\"version\\": \\"1.0.0\\",", + "lineBefore": 3, + "type": "Deleted", + }, + Object { + "content": " \\"description\\": \\"\\",", + "lineBefore": 4, + "type": "Deleted", + }, + Object { + "content": " \\"main\\": \\"index.js\\",", + "lineBefore": 5, + "type": "Deleted", + }, + Object { + "content": " \\"scripts\\": {", + "lineBefore": 6, + "type": "Deleted", + }, + Object { + "content": " \\"build\\": \\"tsc\\"", + "lineBefore": 7, + "type": "Deleted", + }, + Object { + "content": " },", + "lineBefore": 8, + "type": "Deleted", + }, + Object { + "content": " \\"author\\": \\"\\",", + "lineBefore": 9, + "type": "Deleted", + }, + Object { + "content": " \\"license\\": \\"ISC\\",", + "lineBefore": 10, + "type": "Deleted", + }, + Object { + "content": " \\"dependencies\\": {", + "lineBefore": 11, + "type": "Deleted", + }, + Object { + "content": " \\"parse-git-diff\\": \\"0.0.3\\"", + "lineBefore": 12, + "type": "Deleted", + }, + Object { + "content": " },", + "lineBefore": 13, + "type": "Deleted", + }, + Object { + "content": " \\"devDependencies\\": {", + "lineBefore": 14, + "type": "Deleted", + }, + Object { + "content": " \\"typescript\\": \\"^4.4.4\\"", + "lineBefore": 15, + "type": "Deleted", + }, + Object { + "content": " }", + "lineBefore": 16, + "type": "Deleted", + }, + Object { + "content": "}", + "lineBefore": 17, + "type": "Deleted", + }, + ], + "rangeAfter": Object { + "lines": 0, + "start": 0, + }, + "rangeBefore": Object { + "lines": 17, + "start": 1, + }, + "type": "Chunk", + }, + ], + "path": "parse-git-diff-test/package.json", + "type": "DeletedFile", + }, + ], + "type": "GitDiff", +} +`; + +exports[`parseGitDiff new file. 1`] = ` Object { "files": Array [ Object { diff --git a/src/parser/parse-git-diff.test.ts b/src/parser/parse-git-diff.test.ts index 8dcfa57..a21616d 100644 --- a/src/parser/parse-git-diff.test.ts +++ b/src/parser/parse-git-diff.test.ts @@ -2,7 +2,7 @@ import { createContext } from '../test-utils'; import parseGitDiff from './parse-git-diff'; describe('parseGitDiff', () => { - it('should parse new file.', () => { + it('new file.', () => { // prettier-ignore const src = `diff --git a/parse-git-diff-test/packages.json b/parse-git-diff-test/packages.json @@ -34,4 +34,36 @@ index 0000000..5515040 expect(result).not.toBe(null); expect(result).toMatchSnapshot(); }); + + it('deleted file', () => { + // prettier-ignore + const src = + `diff --git a/parse-git-diff-test/package.json b/parse-git-diff-test/package.json +deleted file mode 100644 +index 5515040..0000000 +--- a/parse-git-diff-test/package.json ++++ /dev/null +@@ -1,17 +0,0 @@ +-{ +- "name": "parse-git-diff-test", +- "version": "1.0.0", +- "description": "", +- "main": "index.js", +- "scripts": { +- "build": "tsc" +- }, +- "author": "", +- "license": "ISC", +- "dependencies": { +- "parse-git-diff": "0.0.3" +- }, +- "devDependencies": { +- "typescript": "^4.4.4" +- } +-}`; + const result = parseGitDiff(src); + + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); }); From fe6d410af631eda2157d3189dc44deac10efc626 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 21:36:03 +0900 Subject: [PATCH 19/22] feat: support rename --- README.md | 2 +- .../__snapshots__/parse-git-diff.test.ts.snap | 220 +----------------- src/parser/parse-extended-header.ts | 23 +- src/parser/parse-file-changes.ts | 33 ++- src/parser/parse-git-diff.test.ts | 104 +++++---- 5 files changed, 113 insertions(+), 269 deletions(-) diff --git a/README.md b/README.md index 5252483..7d4a6e3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Installation -```console +```bash npm install parse-git-diff ``` diff --git a/src/parser/__snapshots__/parse-git-diff.test.ts.snap b/src/parser/__snapshots__/parse-git-diff.test.ts.snap index 2e65e2b..65defb5 100644 --- a/src/parser/__snapshots__/parse-git-diff.test.ts.snap +++ b/src/parser/__snapshots__/parse-git-diff.test.ts.snap @@ -2,222 +2,26 @@ exports[`parseGitDiff deleted file 1`] = ` Object { - "files": Array [ - Object { - "chunks": Array [ - Object { - "changes": Array [ - Object { - "content": "{", - "lineBefore": 1, - "type": "Deleted", - }, - Object { - "content": " \\"name\\": \\"parse-git-diff-test\\",", - "lineBefore": 2, - "type": "Deleted", - }, - Object { - "content": " \\"version\\": \\"1.0.0\\",", - "lineBefore": 3, - "type": "Deleted", - }, - Object { - "content": " \\"description\\": \\"\\",", - "lineBefore": 4, - "type": "Deleted", - }, - Object { - "content": " \\"main\\": \\"index.js\\",", - "lineBefore": 5, - "type": "Deleted", - }, - Object { - "content": " \\"scripts\\": {", - "lineBefore": 6, - "type": "Deleted", - }, - Object { - "content": " \\"build\\": \\"tsc\\"", - "lineBefore": 7, - "type": "Deleted", - }, - Object { - "content": " },", - "lineBefore": 8, - "type": "Deleted", - }, - Object { - "content": " \\"author\\": \\"\\",", - "lineBefore": 9, - "type": "Deleted", - }, - Object { - "content": " \\"license\\": \\"ISC\\",", - "lineBefore": 10, - "type": "Deleted", - }, - Object { - "content": " \\"dependencies\\": {", - "lineBefore": 11, - "type": "Deleted", - }, - Object { - "content": " \\"parse-git-diff\\": \\"0.0.3\\"", - "lineBefore": 12, - "type": "Deleted", - }, - Object { - "content": " },", - "lineBefore": 13, - "type": "Deleted", - }, - Object { - "content": " \\"devDependencies\\": {", - "lineBefore": 14, - "type": "Deleted", - }, - Object { - "content": " \\"typescript\\": \\"^4.4.4\\"", - "lineBefore": 15, - "type": "Deleted", - }, - Object { - "content": " }", - "lineBefore": 16, - "type": "Deleted", - }, - Object { - "content": "}", - "lineBefore": 17, - "type": "Deleted", - }, - ], - "rangeAfter": Object { - "lines": 0, - "start": 0, - }, - "rangeBefore": Object { - "lines": 17, - "start": 1, - }, - "type": "Chunk", - }, - ], - "path": "parse-git-diff-test/package.json", - "type": "DeletedFile", - }, - ], + "files": Array [], "type": "GitDiff", } `; exports[`parseGitDiff new file. 1`] = ` +Object { + "files": Array [], + "type": "GitDiff", +} +`; + +exports[`parseGitDiff rename 1`] = ` Object { "files": Array [ Object { - "chunks": Array [ - Object { - "changes": Array [ - Object { - "content": "{", - "lineAfter": 1, - "type": "Added", - }, - Object { - "content": " \\"name\\": \\"parse-git-diff-test\\",", - "lineAfter": 2, - "type": "Added", - }, - Object { - "content": " \\"version\\": \\"1.0.0\\",", - "lineAfter": 3, - "type": "Added", - }, - Object { - "content": " \\"description\\": \\"\\",", - "lineAfter": 4, - "type": "Added", - }, - Object { - "content": " \\"main\\": \\"index.js\\",", - "lineAfter": 5, - "type": "Added", - }, - Object { - "content": " \\"scripts\\": {", - "lineAfter": 6, - "type": "Added", - }, - Object { - "content": " \\"build\\": \\"tsc\\"", - "lineAfter": 7, - "type": "Added", - }, - Object { - "content": " },", - "lineAfter": 8, - "type": "Added", - }, - Object { - "content": " \\"author\\": \\"\\",", - "lineAfter": 9, - "type": "Added", - }, - Object { - "content": " \\"license\\": \\"ISC\\",", - "lineAfter": 10, - "type": "Added", - }, - Object { - "content": " \\"dependencies\\": {", - "lineAfter": 11, - "type": "Added", - }, - Object { - "content": " \\"parse-git-diff\\": \\"0.0.3\\"", - "lineAfter": 12, - "type": "Added", - }, - Object { - "content": " },", - "lineAfter": 13, - "type": "Added", - }, - Object { - "content": " \\"devDependencies\\": {", - "lineAfter": 14, - "type": "Added", - }, - Object { - "content": " \\"typescript\\": \\"^4.4.4\\"", - "lineAfter": 15, - "type": "Added", - }, - Object { - "content": " }", - "lineAfter": 16, - "type": "Added", - }, - Object { - "content": "}", - "lineAfter": 17, - "type": "Added", - }, - ], - "rangeAfter": Object { - "lines": 17, - "start": 1, - }, - "rangeBefore": Object { - "lines": 0, - "start": 0, - }, - "type": "Chunk", - }, - ], - "path": "parse-git-diff-test/packages.json", - "type": "AddedFile", + "chunks": Array [], + "pathAfter": "parse-git-diff-test/ts.json", + "pathBefore": "parse-git-diff-test/tsconfig.json", + "type": "RenamedFile", }, ], "type": "GitDiff", diff --git a/src/parser/parse-extended-header.ts b/src/parser/parse-extended-header.ts index fa6bd1e..99985e2 100644 --- a/src/parser/parse-extended-header.ts +++ b/src/parser/parse-extended-header.ts @@ -4,7 +4,6 @@ const UNHANDLED_EXTENDED_HEADERS = new Set([ 'index', 'old', 'copy', - 'rename', 'similarity', 'dissimilarity', ]); @@ -21,14 +20,30 @@ export default function parseExtendedHeader(ctx: Context) { ctx.nextLine(); return { type: 'unhandled', - }; + } as const; } if (startsWith(line, 'deleted ')) { ctx.nextLine(); - return 'deleted'; + return { + type: 'deleted', + } as const; } else if (startsWith(line, 'new file ')) { ctx.nextLine(); - return 'new file'; + return { + type: 'new file', + } as const; + } else if (startsWith(line, 'rename from ')) { + ctx.nextLine(); + return { + type: 'rename from', + path: line.slice('rename from '.length), + } as const; + } else if (startsWith(line, 'rename to ')) { + ctx.nextLine(); + return { + type: 'rename to', + path: line.slice('rename to '.length), + } as const; } return null; diff --git a/src/parser/parse-file-changes.ts b/src/parser/parse-file-changes.ts index 2a9cd20..a3425ea 100644 --- a/src/parser/parse-file-changes.ts +++ b/src/parser/parse-file-changes.ts @@ -24,36 +24,49 @@ function parseFileChange(ctx: Context): AnyFileChange | undefined { let isDeleted = false; let isNew = false; + let isRename = false; + let pathBefore = ''; + let pathAfter = ''; while (!ctx.isEof()) { const extHeader = parseExtendedHeader(ctx); if (!extHeader) { break; } - if (extHeader === 'deleted') isDeleted = true; - if (extHeader === 'new file') isNew = true; + if (extHeader.type === 'deleted') isDeleted = true; + if (extHeader.type === 'new file') isNew = true; + if (extHeader.type === 'rename from') { + isRename = true; + pathBefore = extHeader.path; + } + if (extHeader.type === 'rename to') { + isRename = true; + pathAfter = extHeader.path; + } } const changeMarkers = parseChangeMarkers(ctx); - - if (!changeMarkers) { - return; - } - const chunks = parseChunks(ctx); - if (isDeleted) { + if (isDeleted && changeMarkers) { return { type: 'DeletedFile', chunks, path: changeMarkers.deleted, }; - } else if (isNew) { + } else if (isNew && changeMarkers) { return { type: 'AddedFile', chunks, path: changeMarkers.added, }; - } else { + } else if (isRename) { + return { + type: 'RenamedFile', + pathAfter, + pathBefore, + chunks, + }; + } else if (changeMarkers) { return { type: 'ChangedFile', chunks, diff --git a/src/parser/parse-git-diff.test.ts b/src/parser/parse-git-diff.test.ts index a21616d..591431a 100644 --- a/src/parser/parse-git-diff.test.ts +++ b/src/parser/parse-git-diff.test.ts @@ -5,29 +5,29 @@ describe('parseGitDiff', () => { it('new file.', () => { // prettier-ignore const src = -`diff --git a/parse-git-diff-test/packages.json b/parse-git-diff-test/packages.json -new file mode 100644 -index 0000000..5515040 ---- /dev/null -+++ b/parse-git-diff-test/packages.json -@@ -0,0 +1,17 @@ -+{ -+ "name": "parse-git-diff-test", -+ "version": "1.0.0", -+ "description": "", -+ "main": "index.js", -+ "scripts": { -+ "build": "tsc" -+ }, -+ "author": "", -+ "license": "ISC", -+ "dependencies": { -+ "parse-git-diff": "0.0.3" -+ }, -+ "devDependencies": { -+ "typescript": "^4.4.4" -+ } -+}`; + `diff --git a/parse-git-diff-test/packages.json b/parse-git-diff-test/packages.json + new file mode 100644 + index 0000000..5515040 + --- /dev/null + +++ b/parse-git-diff-test/packages.json + @@ -0,0 +1,17 @@ + +{ + + "name": "parse-git-diff-test", + + "version": "1.0.0", + + "description": "", + + "main": "index.js", + + "scripts": { + + "build": "tsc" + + }, + + "author": "", + + "license": "ISC", + + "dependencies": { + + "parse-git-diff": "0.0.3" + + }, + + "devDependencies": { + + "typescript": "^4.4.4" + + } + +}`; const result = parseGitDiff(src); @@ -38,32 +38,44 @@ index 0000000..5515040 it('deleted file', () => { // prettier-ignore const src = - `diff --git a/parse-git-diff-test/package.json b/parse-git-diff-test/package.json -deleted file mode 100644 -index 5515040..0000000 ---- a/parse-git-diff-test/package.json -+++ /dev/null -@@ -1,17 +0,0 @@ --{ -- "name": "parse-git-diff-test", -- "version": "1.0.0", -- "description": "", -- "main": "index.js", -- "scripts": { -- "build": "tsc" -- }, -- "author": "", -- "license": "ISC", -- "dependencies": { -- "parse-git-diff": "0.0.3" -- }, -- "devDependencies": { -- "typescript": "^4.4.4" -- } --}`; + `diff --git a/parse-git-diff-test/package.json b/parse-git-diff-test/package.json + deleted file mode 100644 + index 5515040..0000000 + --- a/parse-git-diff-test/package.json + +++ /dev/null + @@ -1,17 +0,0 @@ + -{ + - "name": "parse-git-diff-test", + - "version": "1.0.0", + - "description": "", + - "main": "index.js", + - "scripts": { + - "build": "tsc" + - }, + - "author": "", + - "license": "ISC", + - "dependencies": { + - "parse-git-diff": "0.0.3" + - }, + - "devDependencies": { + - "typescript": "^4.4.4" + - } + -}`; const result = parseGitDiff(src); expect(result).not.toBe(null); expect(result).toMatchSnapshot(); }); + + it('rename', () => { + // prettier-ignore + const src = +`diff --git a/parse-git-diff-test/tsconfig.json b/parse-git-diff-test/ts.json +similarity index 100% +rename from parse-git-diff-test/tsconfig.json +rename to parse-git-diff-test/ts.json`; + const result = parseGitDiff(src); + expect(result).not.toBe(null); + expect(result).toMatchSnapshot(); + }); }); From 47e5adac8d216a21e1238c45ded46f6961930da9 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Sun, 17 Oct 2021 23:05:30 +0900 Subject: [PATCH 20/22] feat: add travis --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..88de8f2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - 12 +before_script: + - yarn +script: + - yarn check:all From c245e7711da141d13f1ba4869ee35fc2fc0bc1cb Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Mon, 18 Oct 2021 22:26:10 +0900 Subject: [PATCH 21/22] feat: change type name --- .../__snapshots__/parse-changes.test.ts.snap | 8 +- .../__snapshots__/parse-chunks.test.ts.snap | 100 +++++++++--------- .../parse-file-changes.test.ts.snap | 8 +- src/parser/parse-changes.ts | 28 ++--- src/parser/parse-chunks.ts | 4 +- src/types/changes.ts | 10 +- 6 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/parser/__snapshots__/parse-changes.test.ts.snap b/src/parser/__snapshots__/parse-changes.test.ts.snap index bc1a585..2059f74 100644 --- a/src/parser/__snapshots__/parse-changes.test.ts.snap +++ b/src/parser/__snapshots__/parse-changes.test.ts.snap @@ -6,25 +6,25 @@ Array [ "content": "unchanged", "lineAfter": 1, "lineBefore": 1, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "-deleted line", "lineAfter": 2, "lineBefore": 2, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "+added line", "lineAfter": 3, "lineBefore": 3, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "unchanged", "lineAfter": 4, "lineBefore": 4, - "type": "Unchanged", + "type": "UnchangedLine", }, ] `; diff --git a/src/parser/__snapshots__/parse-chunks.test.ts.snap b/src/parser/__snapshots__/parse-chunks.test.ts.snap index 961797e..96ebb33 100644 --- a/src/parser/__snapshots__/parse-chunks.test.ts.snap +++ b/src/parser/__snapshots__/parse-chunks.test.ts.snap @@ -8,47 +8,47 @@ Array [ "content": "/** changed file types */", "lineAfter": 18, "lineBefore": 18, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "", "lineAfter": 19, "lineBefore": 19, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "interface BaseFileChange extends Base {", "lineAfter": 20, "lineBefore": 20, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": " hunks: Hunk[];", "lineBefore": 21, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": " chunks: Chunk[];", "lineAfter": 21, - "type": "Added", + "type": "AddedLine", }, Object { "content": "}", "lineAfter": 22, "lineBefore": 22, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "", "lineAfter": 23, "lineBefore": 23, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "export interface ChangedFile extends BaseFileChange<'ChangedFile'> {}", "lineAfter": 24, "lineBefore": 24, - "type": "Unchanged", + "type": "UnchangedLine", }, ], "rangeAfter": Object { @@ -67,95 +67,95 @@ Array [ "content": "", "lineAfter": 40, "lineBefore": 40, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "/** hunk */", "lineAfter": 41, "lineBefore": 41, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "", "lineAfter": 42, "lineBefore": 42, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "export interface HunkPos {", "lineBefore": 43, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": "export interface ChunkPos {", "lineAfter": 43, - "type": "Added", + "type": "AddedLine", }, Object { "content": " start: number;", "lineAfter": 44, "lineBefore": 44, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": " lines: number;", "lineAfter": 45, "lineBefore": 45, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "}", "lineAfter": 46, "lineBefore": 46, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "", "lineAfter": 47, "lineBefore": 47, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "export interface Hunk extends Base<'Hunk'> {", "lineBefore": 48, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": " addedPos: HunkPos;", "lineBefore": 49, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": " deletedPos: HunkPos;", "lineBefore": 50, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": "export interface Chunk extends Base<'Hunk'> {", "lineAfter": 48, - "type": "Added", + "type": "AddedLine", }, Object { "content": " addedPos: ChunkPos;", "lineAfter": 49, - "type": "Added", + "type": "AddedLine", }, Object { "content": " deletedPos: ChunkPos;", "lineAfter": 50, - "type": "Added", + "type": "AddedLine", }, Object { "content": " changes: AnyChange[];", "lineAfter": 51, "lineBefore": 51, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "}", "lineAfter": 52, "lineBefore": 52, - "type": "Unchanged", + "type": "UnchangedLine", }, ], "rangeAfter": Object { @@ -179,47 +179,47 @@ Array [ "content": "/** changed file types */", "lineAfter": 18, "lineBefore": 18, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "", "lineAfter": 19, "lineBefore": 19, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "interface BaseFileChange extends Base {", "lineAfter": 20, "lineBefore": 20, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": " hunks: Hunk[];", "lineBefore": 21, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": " chunks: Chunk[];", "lineAfter": 21, - "type": "Added", + "type": "AddedLine", }, Object { "content": "}", "lineAfter": 22, "lineBefore": 22, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "", "lineAfter": 23, "lineBefore": 23, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "export interface ChangedFile extends BaseFileChange<'ChangedFile'> {}", "lineAfter": 24, "lineBefore": 24, - "type": "Unchanged", + "type": "UnchangedLine", }, ], "rangeAfter": Object { @@ -238,95 +238,95 @@ Array [ "content": "", "lineAfter": 40, "lineBefore": 40, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "/** hunk */", "lineAfter": 41, "lineBefore": 41, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "", "lineAfter": 42, "lineBefore": 42, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "export interface HunkPos {", "lineBefore": 43, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": "export interface ChunkPos {", "lineAfter": 43, - "type": "Added", + "type": "AddedLine", }, Object { "content": " start: number;", "lineAfter": 44, "lineBefore": 44, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": " lines: number;", "lineAfter": 45, "lineBefore": 45, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "}", "lineAfter": 46, "lineBefore": 46, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "", "lineAfter": 47, "lineBefore": 47, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "export interface Hunk extends Base<'Hunk'> {", "lineBefore": 48, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": " addedPos: HunkPos;", "lineBefore": 49, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": " deletedPos: HunkPos;", "lineBefore": 50, - "type": "Deleted", + "type": "DeletedLine", }, Object { "content": "export interface Chunk extends Base<'Hunk'> {", "lineAfter": 48, - "type": "Added", + "type": "AddedLine", }, Object { "content": " addedPos: ChunkPos;", "lineAfter": 49, - "type": "Added", + "type": "AddedLine", }, Object { "content": " deletedPos: ChunkPos;", "lineAfter": 50, - "type": "Added", + "type": "AddedLine", }, Object { "content": " changes: AnyChange[];", "lineAfter": 51, "lineBefore": 51, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "}", "lineAfter": 52, "lineBefore": 52, - "type": "Unchanged", + "type": "UnchangedLine", }, ], "rangeAfter": Object { diff --git a/src/parser/__snapshots__/parse-file-changes.test.ts.snap b/src/parser/__snapshots__/parse-file-changes.test.ts.snap index 1e9f446..98ac5b2 100644 --- a/src/parser/__snapshots__/parse-file-changes.test.ts.snap +++ b/src/parser/__snapshots__/parse-file-changes.test.ts.snap @@ -10,18 +10,18 @@ Array [ "content": "node_modules", "lineAfter": 1, "lineBefore": 1, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "build", "lineAfter": 2, "lineBefore": 2, - "type": "Unchanged", + "type": "UnchangedLine", }, Object { "content": "coverage", "lineAfter": 3, - "type": "Added", + "type": "AddedLine", }, ], "rangeAfter": Object { @@ -45,7 +45,7 @@ Array [ Object { "content": "# parse-git-diff", "lineBefore": 1, - "type": "Deleted", + "type": "DeletedLine", }, ], "rangeAfter": Object { diff --git a/src/parser/parse-changes.ts b/src/parser/parse-changes.ts index 4463565..9879004 100644 --- a/src/parser/parse-changes.ts +++ b/src/parser/parse-changes.ts @@ -1,20 +1,20 @@ import type Context from './context'; -import type { AnyChange, ChunkRange } from '../types'; +import type { AnyLineChange, ChunkRange } from '../types'; -type LineType = AnyChange['type']; +type LineType = AnyLineChange['type']; const CHAR_TYPE_MAP: Record = { - '+': 'Added', - '-': 'Deleted', - ' ': 'Unchanged', + '+': 'AddedLine', + '-': 'DeletedLine', + ' ': 'UnchangedLine', }; export default function parseChanges( ctx: Context, rangeBefore: ChunkRange, rangeAfter: ChunkRange -): AnyChange[] { - const changes: AnyChange[] = []; +): AnyLineChange[] { + const changes: AnyLineChange[] = []; let lineBefore = rangeBefore.start; let lineAfter = rangeAfter.start; @@ -26,28 +26,28 @@ export default function parseChanges( } ctx.nextLine(); - let change: AnyChange; + let change: AnyLineChange; const content = line.slice(1); switch (type) { - case 'Added': { + case 'AddedLine': { change = { - type: 'Added', + type: 'AddedLine', lineAfter: lineAfter++, content, }; break; } - case 'Deleted': { + case 'DeletedLine': { change = { - type: 'Deleted', + type: 'DeletedLine', lineBefore: lineBefore++, content, }; break; } - case 'Unchanged': { + case 'UnchangedLine': { change = { - type: 'Unchanged', + type: 'UnchangedLine', lineBefore: lineBefore++, lineAfter: lineAfter++, content, diff --git a/src/parser/parse-chunks.ts b/src/parser/parse-chunks.ts index 27132a3..ed29a7e 100644 --- a/src/parser/parse-chunks.ts +++ b/src/parser/parse-chunks.ts @@ -1,4 +1,4 @@ -import type { AnyChange, Chunk } from '../types'; +import type { AnyLineChange, Chunk } from '../types'; import type Context from './context'; import parseChanges from './parse-changes'; import parseChunkHeader from './parse-chunk-header'; @@ -22,7 +22,7 @@ function parseChunk(context: Context): Chunk | undefined { return; } - const changes: AnyChange[] = parseChanges( + const changes: AnyLineChange[] = parseChanges( context, chunkHeader.rangeBefore, chunkHeader.rangeAfter diff --git a/src/types/changes.ts b/src/types/changes.ts index 421c4cc..215d847 100644 --- a/src/types/changes.ts +++ b/src/types/changes.ts @@ -6,20 +6,20 @@ interface BaseChange extends Base { content: string; } -export interface Added extends BaseChange<'Added'> { +export interface AddedLine extends BaseChange<'AddedLine'> { lineAfter: number; } -export interface Deleted extends BaseChange<'Deleted'> { +export interface DeletedLine extends BaseChange<'DeletedLine'> { lineBefore: number; } -export interface Unchanged extends BaseChange<'Unchanged'> { +export interface UnchangedLine extends BaseChange<'UnchangedLine'> { lineBefore: number; lineAfter: number; } -export type AnyChange = Added | Deleted | Unchanged; +export type AnyLineChange = AddedLine | DeletedLine | UnchangedLine; /** changed file types */ @@ -56,5 +56,5 @@ export interface ChunkRange { export interface Chunk extends Base<'Chunk'> { rangeBefore: ChunkRange; rangeAfter: ChunkRange; - changes: AnyChange[]; + changes: AnyLineChange[]; } From 949eac826a68c5e11e8988a271d066fa479a9d8f Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Mon, 18 Oct 2021 22:36:05 +0900 Subject: [PATCH 22/22] tests: add fixtures --- src/__fixtures__/delete-line-diff | 7 +++++++ src/__fixtures__/deleted-file-diff | 7 +++++++ src/__fixtures__/new-file-diff | 7 +++++++ src/__fixtures__/new-line-diff | 7 +++++++ src/__fixtures__/rename-file-diff | 4 ++++ 5 files changed, 32 insertions(+) create mode 100644 src/__fixtures__/delete-line-diff create mode 100644 src/__fixtures__/deleted-file-diff create mode 100644 src/__fixtures__/new-file-diff create mode 100644 src/__fixtures__/new-line-diff create mode 100644 src/__fixtures__/rename-file-diff diff --git a/src/__fixtures__/delete-line-diff b/src/__fixtures__/delete-line-diff new file mode 100644 index 0000000..c298f39 --- /dev/null +++ b/src/__fixtures__/delete-line-diff @@ -0,0 +1,7 @@ +diff --git a/rename.md b/rename.md +index 0e05564..aa39060 100644 +--- a/rename.md ++++ b/rename.md +@@ -1,2 +1 @@ + newfile +-newline \ No newline at end of file diff --git a/src/__fixtures__/deleted-file-diff b/src/__fixtures__/deleted-file-diff new file mode 100644 index 0000000..e214d78 --- /dev/null +++ b/src/__fixtures__/deleted-file-diff @@ -0,0 +1,7 @@ +diff --git a/newfile.md b/newfile.md +deleted file mode 100644 +index aa39060..0000000 +--- a/newfile.md ++++ /dev/null +@@ -1 +0,0 @@ +-newfile \ No newline at end of file diff --git a/src/__fixtures__/new-file-diff b/src/__fixtures__/new-file-diff new file mode 100644 index 0000000..5b52f6a --- /dev/null +++ b/src/__fixtures__/new-file-diff @@ -0,0 +1,7 @@ +diff --git a/newfile.md b/newfile.md +new file mode 100644 +index 0000000..aa39060 +--- /dev/null ++++ b/newfile.md +@@ -0,0 +1 @@ ++newfile \ No newline at end of file diff --git a/src/__fixtures__/new-line-diff b/src/__fixtures__/new-line-diff new file mode 100644 index 0000000..cb376d5 --- /dev/null +++ b/src/__fixtures__/new-line-diff @@ -0,0 +1,7 @@ +diff --git a/rename.md b/rename.md +index aa39060..0e05564 100644 +--- a/rename.md ++++ b/rename.md +@@ -1 +1,2 @@ + newfile ++newline \ No newline at end of file diff --git a/src/__fixtures__/rename-file-diff b/src/__fixtures__/rename-file-diff new file mode 100644 index 0000000..63e2d7f --- /dev/null +++ b/src/__fixtures__/rename-file-diff @@ -0,0 +1,4 @@ +diff --git a/newfile.md b/rename.md +similarity index 100% +rename from newfile.md +rename to rename.md \ No newline at end of file