1*082815dcSEvan Baconexport interface CodeBlock {
2*082815dcSEvan Bacon  start: number;
3*082815dcSEvan Bacon  end: number;
4*082815dcSEvan Bacon  code: string;
5*082815dcSEvan Bacon}
6*082815dcSEvan Bacon
7*082815dcSEvan Bacon/**
8*082815dcSEvan Bacon * Insert contents at given offset
9*082815dcSEvan Bacon * @param srcContents source contents
10*082815dcSEvan Bacon * @param insertion content to insert
11*082815dcSEvan Bacon * @param offset `srcContents` offset to insert `insertion`
12*082815dcSEvan Bacon * @returns updated contents
13*082815dcSEvan Bacon */
14*082815dcSEvan Baconexport function insertContentsAtOffset(
15*082815dcSEvan Bacon  srcContents: string,
16*082815dcSEvan Bacon  insertion: string,
17*082815dcSEvan Bacon  offset: number
18*082815dcSEvan Bacon): string {
19*082815dcSEvan Bacon  const srcContentsLength = srcContents.length;
20*082815dcSEvan Bacon  if (offset < 0 || offset > srcContentsLength) {
21*082815dcSEvan Bacon    throw new Error('Invalid parameters.');
22*082815dcSEvan Bacon  }
23*082815dcSEvan Bacon  if (offset === 0) {
24*082815dcSEvan Bacon    return `${insertion}${srcContents}`;
25*082815dcSEvan Bacon  } else if (offset === srcContentsLength) {
26*082815dcSEvan Bacon    return `${srcContents}${insertion}`;
27*082815dcSEvan Bacon  }
28*082815dcSEvan Bacon
29*082815dcSEvan Bacon  const prefix = srcContents.substring(0, offset);
30*082815dcSEvan Bacon  const suffix = srcContents.substring(offset);
31*082815dcSEvan Bacon  return `${prefix}${insertion}${suffix}`;
32*082815dcSEvan Bacon}
33*082815dcSEvan Bacon
34*082815dcSEvan Bacon/**
35*082815dcSEvan Bacon * Replace contents at given start and end offset
36*082815dcSEvan Bacon *
37*082815dcSEvan Bacon * @param contents source contents
38*082815dcSEvan Bacon * @param replacement new contents to place in [startOffset:endOffset]
39*082815dcSEvan Bacon * @param startOffset `contents` start offset for replacement
40*082815dcSEvan Bacon * @param endOffset `contents` end offset for replacement
41*082815dcSEvan Bacon * @returns updated contents
42*082815dcSEvan Bacon */
43*082815dcSEvan Baconexport function replaceContentsWithOffset(
44*082815dcSEvan Bacon  contents: string,
45*082815dcSEvan Bacon  replacement: string,
46*082815dcSEvan Bacon  startOffset: number,
47*082815dcSEvan Bacon  endOffset: number
48*082815dcSEvan Bacon): string {
49*082815dcSEvan Bacon  const contentsLength = contents.length;
50*082815dcSEvan Bacon  if (
51*082815dcSEvan Bacon    startOffset < 0 ||
52*082815dcSEvan Bacon    endOffset < 0 ||
53*082815dcSEvan Bacon    startOffset >= contentsLength ||
54*082815dcSEvan Bacon    endOffset >= contentsLength ||
55*082815dcSEvan Bacon    startOffset > endOffset
56*082815dcSEvan Bacon  ) {
57*082815dcSEvan Bacon    throw new Error('Invalid parameters.');
58*082815dcSEvan Bacon  }
59*082815dcSEvan Bacon  const prefix = contents.substring(0, startOffset);
60*082815dcSEvan Bacon  const suffix = contents.substring(endOffset + 1);
61*082815dcSEvan Bacon  return `${prefix}${replacement}${suffix}`;
62*082815dcSEvan Bacon}
63*082815dcSEvan Bacon
64*082815dcSEvan Bacon/**
65*082815dcSEvan Bacon * String.prototype.search() with offset support
66*082815dcSEvan Bacon *
67*082815dcSEvan Bacon * @param source source string to search
68*082815dcSEvan Bacon * @param regexp RegExp pattern to search
69*082815dcSEvan Bacon * @param offset start offset of `source` to search `regexp` pattern
70*082815dcSEvan Bacon * @returns The index of the first match between the regular expression and the given string, or -1 if no match was found.
71*082815dcSEvan Bacon */
72*082815dcSEvan Baconexport function searchFromOffset(source: string, regexp: RegExp, offset: number): number {
73*082815dcSEvan Bacon  const target = source.substring(offset);
74*082815dcSEvan Bacon  const matchedIndex = target.search(regexp);
75*082815dcSEvan Bacon  return matchedIndex < 0 ? matchedIndex : matchedIndex + offset;
76*082815dcSEvan Bacon}
77