@peteanderson/ansi 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,7 +16,8 @@ const ansi = require("@peteanderson/ansi");
16
16
  console.log(ansi.fg.red + "error: something went wrong" + ansi.fg.default);
17
17
  console.log(ansi.bg.blue + ansi.fg.white + "highlighted" + ansi.fg.default + ansi.bg.default);
18
18
  console.log(ansi.erase.screen);
19
- console.log(ansi.caret.show);
19
+ console.log(ansi.slice(coloredText, 0, 10)); // slice by visible characters
20
+ console.log(ansi.strip(coloredText)); // remove ANSI codes
20
21
  ```
21
22
 
22
23
  ## API
@@ -55,6 +56,11 @@ Raw sequences:
55
56
 
56
57
  Removes ANSI CSI sequences from a string.
57
58
 
59
+ ### `slice(text, start, end)`
60
+
61
+ Slices a string by visible characters, ignoring ANSI sequences. Works like `String.prototype.slice`
62
+ but counts only visible characters.
63
+
58
64
  ## License
59
65
 
60
66
  MIT
package/dist/ansi.d.ts CHANGED
@@ -62,3 +62,5 @@ export namespace caret {
62
62
  }
63
63
  export let scrollUp: (lines: number) => string;
64
64
  export let scrollDown: (lines: number) => string;
65
+ export let strip: (text: string) => string;
66
+ export function slice(text: string, start: number, end: number): string;
package/dist/ansi.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ const rxCSI = /\x1b\[[0-?]*[ -/]*[@-~]/g;
2
3
  /** @type {(r: number, g: number, b: number, code: number) => string} */
3
4
  function rgb(r, g, b, code) {
4
5
  if (r === g && g === b) {
@@ -71,5 +72,49 @@ const ansi = {
71
72
  scrollUp: lines => `\x1b[${lines}S`,
72
73
  /** @type {(lines: number) => string} */
73
74
  scrollDown: lines => `\x1b[${lines}T`,
75
+ /** @type {(text: string) => string} */
76
+ strip: text => text.replace(rxCSI, ''),
77
+ /** @type {(text: string, start: number, end: number) => string} */
78
+ slice(text, start, end) {
79
+ if (start < 0 || end < 0) {
80
+ const visibleLength = ansi.strip(text).length;
81
+ if (start < 0)
82
+ start = Math.max(0, visibleLength + start);
83
+ if (end < 0)
84
+ end = Math.max(0, visibleLength + end);
85
+ }
86
+ if (start > end)
87
+ return "";
88
+ let visibleIndex = 0;
89
+ let startIndex = 0;
90
+ while (startIndex < text.length && visibleIndex < start) {
91
+ if (text[startIndex] === "\x1b") {
92
+ rxCSI.lastIndex = startIndex;
93
+ const match = rxCSI.exec(text);
94
+ if (match?.index === startIndex) {
95
+ startIndex += match[0].length;
96
+ continue;
97
+ }
98
+ }
99
+ startIndex++;
100
+ visibleIndex++;
101
+ }
102
+ let endIndex = startIndex;
103
+ let hasAnsi = false;
104
+ while (endIndex < text.length && visibleIndex < end) {
105
+ if (text[endIndex] === "\x1b") {
106
+ rxCSI.lastIndex = endIndex;
107
+ const match = rxCSI.exec(text);
108
+ if (match?.index === endIndex) {
109
+ endIndex += match[0].length;
110
+ hasAnsi = true;
111
+ continue;
112
+ }
113
+ }
114
+ endIndex++;
115
+ visibleIndex++;
116
+ }
117
+ return text.slice(startIndex, endIndex) + (hasAnsi ? ansi.reset : "");
118
+ },
74
119
  };
75
120
  module.exports = ansi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name" : "@peteanderson/ansi",
3
- "version" : "0.1.0",
3
+ "version" : "0.1.1",
4
4
  "description" : "ANSI escape sequence generation with automatic feature detection",
5
5
  "author" : {
6
6
  "name" : "Pete Anderson",
package/src/ansi.js CHANGED
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ const rxCSI = /\x1b\[[0-?]*[ -/]*[@-~]/g;
4
+
3
5
  /** @type {(r: number, g: number, b: number, code: number) => string} */
4
6
  function rgb(r, g, b, code) {
5
7
  if (r === g && g === b) {
@@ -84,6 +86,58 @@ const ansi = {
84
86
  scrollUp : lines => `\x1b[${lines}S`,
85
87
  /** @type {(lines: number) => string} */
86
88
  scrollDown : lines => `\x1b[${lines}T`,
89
+
90
+ /** @type {(text: string) => string} */
91
+ strip: text => text.replace(rxCSI, ''),
92
+
93
+ /** @type {(text: string, start: number, end: number) => string} */
94
+ slice(text, start, end) {
95
+ if (start < 0 || end < 0) {
96
+ const visibleLength = ansi.strip(text).length;
97
+ if (start < 0)
98
+ start = Math.max(0, visibleLength + start);
99
+ if (end < 0)
100
+ end = Math.max(0, visibleLength + end);
101
+ }
102
+ if (start > end)
103
+ return "";
104
+
105
+ let visibleIndex = 0;
106
+ let startIndex = 0;
107
+
108
+ while (startIndex < text.length && visibleIndex < start) {
109
+ if (text[startIndex] === "\x1b") {
110
+ rxCSI.lastIndex = startIndex;
111
+ const match = rxCSI.exec(text);
112
+ if (match?.index === startIndex) {
113
+ startIndex += match[0].length;
114
+ continue;
115
+ }
116
+ }
117
+
118
+ startIndex++;
119
+ visibleIndex++;
120
+ }
121
+
122
+ let endIndex = startIndex;
123
+ let hasAnsi = false;
124
+ while (endIndex < text.length && visibleIndex < end) {
125
+ if (text[endIndex] === "\x1b") {
126
+ rxCSI.lastIndex = endIndex;
127
+ const match = rxCSI.exec(text);
128
+ if (match?.index === endIndex) {
129
+ endIndex += match[0].length;
130
+ hasAnsi = true;
131
+ continue;
132
+ }
133
+ }
134
+
135
+ endIndex++;
136
+ visibleIndex++;
137
+ }
138
+
139
+ return text.slice(startIndex, endIndex) + (hasAnsi ? ansi.reset : "");
140
+ },
87
141
  };
88
142
 
89
143
  module.exports = ansi;