1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3'use strict';
4
5import {
6  sortByKey, shallowCopy, accumulateLengths, splitLines
7} from '../common/util';
8
9import {
10  IDiffEntry, IDiffArrayEntry, IDiffPatch,
11  opAddRange, opRemoveRange, validateSequenceOp
12} from './diffentries';
13
14/**
15 * The indentation to use for JSON stringify.
16 */
17export const JSON_INDENT = '  ';
18
19
20/**
21 * Search the list of diffs for an entry with the given key.
22 *
23 * Returns the first found entry, or null if not entry was found.
24 */
25export
26function getSubDiffByKey(diff: IDiffEntry[] | null, key: string | number) : IDiffEntry[] | null {
27  if (!diff) {
28    return null;
29  }
30  for (let i=0; i < diff.length; ++i) {
31    if (diff[i].key === key) {
32      return (diff[i] as IDiffPatch).diff || null;
33    }
34  }
35  return null;
36}
37
38/**
39 * Search the list of diffs for an entry with the given key.
40 *
41 * Returns the first found entry, or null if not entry was found.
42 */
43export
44function getDiffEntryByKey(diff: IDiffEntry[] | null, key: string | number) : IDiffEntry | null {
45  if (!diff) {
46    return null;
47  }
48  for (let i=0; i < diff.length; ++i) {
49    if (diff[i].key === key) {
50      return diff[i];
51    }
52  }
53  return null;
54}
55
56
57function validateStringDiff(base: string[], entry: IDiffArrayEntry, lineToChar: number[]): void {
58  // First valdiate line ops:
59  validateSequenceOp(base, entry);
60
61  if (entry.op === 'patch') {
62    let line = base[entry.key];
63    let diff = entry.diff;
64    if (diff !== null) {
65      for (let d of diff) {
66        validateSequenceOp(line, d);
67      }
68    }
69  }
70}
71
72/**
73 * Remove the merge source indicator from a diff (returns a copy).
74 */
75export
76function stripSource(diff: IDiffEntry[] | null): IDiffEntry[] | null {
77  if (!diff) {
78    return null;
79  }
80  let ret: IDiffEntry[] = [];
81  for (let e of diff) {
82    if (e.op === 'patch') {
83      ret.push({
84        key: e.key,
85        op: e.op,
86        diff: stripSource(e.diff)
87      });
88    } else {
89      let d = shallowCopy(e);
90      delete d.source;
91      ret.push(d);
92    }
93  }
94  return ret;
95}
96
97
98/**
99 * Translates a diff of strings split by str.splitlines() to a diff of the
100 * joined multiline string
101 */
102export
103function flattenStringDiff(val: string[] | string, diff: IDiffArrayEntry[]): IDiffArrayEntry[] {
104
105  if (typeof val === 'string') {
106    val = splitLines(val);
107  }
108  let lineToChar = [0].concat(accumulateLengths(val));
109  let flattened: IDiffArrayEntry[] = [];
110  for (let e of diff) {
111    // Frist validate op:
112    validateStringDiff(val, e, lineToChar);
113    let lineOffset = lineToChar[e.key];
114    if (e.op === 'patch') {
115      let pdiff = e.diff as IDiffArrayEntry[];
116      if (pdiff !== null) {
117        for (let p of pdiff) {
118          let d = shallowCopy(p);
119          d.key += lineOffset;
120          flattened.push(d);
121        }
122      }
123    } else {
124      // Other ops simply have keys which refer to lines
125      let d: IDiffEntry | null = null;
126      if (e.op === 'addrange') {
127        d = opAddRange(lineOffset,
128                       (e.valuelist as any[]).join(''));
129      } else { // e.op === 'removerange'
130        let idx = e.key + e.length;
131        d = opRemoveRange(lineOffset,
132                          lineToChar[idx] - lineOffset);
133      }
134      d.source = e.source;
135      flattened.push(d);
136    }
137  }
138  // Finally, sort on key (leaving equal items in original order)
139  // This is done since the original diffs are sorted deeper first!
140  return sortByKey(flattened, 'key');
141}
142