forked from microsoft/vscode-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
238 lines (219 loc) · 8.6 KB
/
index.ts
File metadata and controls
238 lines (219 loc) · 8.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
'use strict';
import { nbformat } from '@jupyterlab/coreutils/lib/nbformat';
import { noop } from '../../client/common/utils/misc';
const SingleQuoteMultiline = "'''";
const DoubleQuoteMultiline = '"""';
function concatMultilineString(str: nbformat.MultilineString, trim: boolean): string {
const nonLineFeedWhiteSpaceTrim = /(^[\t\f\v\r ]+|[\t\f\v\r ]+$)/g; // Local var so don't have to reset the lastIndex.
if (Array.isArray(str)) {
let result = '';
for (let i = 0; i < str.length; i += 1) {
const s = str[i];
if (i < str.length - 1 && !s.endsWith('\n')) {
result = result.concat(`${s}\n`);
} else {
result = result.concat(s);
}
}
// Just trim whitespace. Leave \n in place
return trim ? result.replace(nonLineFeedWhiteSpaceTrim, '') : result;
}
return trim ? str.toString().replace(nonLineFeedWhiteSpaceTrim, '') : str.toString();
}
export function concatMultilineStringOutput(str: nbformat.MultilineString): string {
return concatMultilineString(str, true);
}
export function concatMultilineStringInput(str: nbformat.MultilineString): string {
return concatMultilineString(str, false);
}
export function splitMultilineString(source: nbformat.MultilineString): string[] {
// Make sure a multiline string is back the way Jupyter expects it
if (Array.isArray(source)) {
return source as string[];
}
const str = source.toString();
if (str.length > 0) {
// Each line should be a separate entry, but end with a \n if not last entry
const arr = str.split('\n');
return arr
.map((s, i) => {
if (i < arr.length - 1) {
return `${s}\n`;
}
return s;
})
.filter(s => s.length > 0); // Skip last one if empty (it's the only one that could be length 0)
}
return [];
}
export function removeLinesFromFrontAndBack(code: string): string {
const lines = code.splitLines({ trim: false, removeEmptyEntries: false });
let foundNonEmptyLine = false;
let lastNonEmptyLine = -1;
let result: string[] = [];
parseForComments(
lines,
(_s, i) => {
result.push(lines[i]);
lastNonEmptyLine = i;
},
(s, i) => {
const trimmed = s.trim();
if (foundNonEmptyLine || trimmed) {
result.push(lines[i]);
foundNonEmptyLine = true;
}
if (trimmed) {
lastNonEmptyLine = i;
}
}
);
// Remove empty lines off the bottom too
if (lastNonEmptyLine < lines.length - 1) {
result = result.slice(0, result.length - (lines.length - 1 - lastNonEmptyLine));
}
return result.join('\n');
}
// Strip out comment lines from code
export function stripComments(str: string): string {
let result: string = '';
parseForComments(
str.splitLines({ trim: false, removeEmptyEntries: false }),
_s => noop,
s => (result = result.concat(`${s}\n`))
);
return result;
}
// Took this from jupyter/notebook
// https://github.com/jupyter/notebook/blob/b8b66332e2023e83d2ee04f83d8814f567e01a4e/notebook/static/base/js/utils.js
// Remove characters that are overridden by backspace characters
function fixBackspace(txt: string) {
let tmp = txt;
do {
txt = tmp;
// Cancel out anything-but-newline followed by backspace
tmp = txt.replace(/[^\n]\x08/gm, '');
} while (tmp.length < txt.length);
return txt;
}
// Using our own version for fixCarriageReturn. The jupyter version seems to not work.
function fixCarriageReturn(str: string): string {
// Go through the string, looking for \r's that are not followed by \n. This is
// a special case that means replace the string before. This is necessary to
// get an html display of this string to behave correctly.
// Note: According to this:
// https://jsperf.com/javascript-concat-vs-join/2.
// Concat is way faster than array join for building up a string.
let result = '';
let previousLinePos = 0;
for (let i = 0; i < str.length; i += 1) {
if (str[i] === '\r') {
// See if this is a line feed. If so, leave alone. This is goofy windows \r\n
if (i < str.length - 1 && str[i + 1] === '\n') {
// This line is legit, output it and convert to '\n' only.
result += str.substr(previousLinePos, i - previousLinePos);
result += '\n';
previousLinePos = i + 2;
i += 1;
} else {
// This line should replace the previous one. Skip our \r
previousLinePos = i + 1;
}
} else if (str[i] === '\n') {
// This line is legit, output it. (Single linefeed)
result += str.substr(previousLinePos, i - previousLinePos + 1);
previousLinePos = i + 1;
}
}
result += str.substr(previousLinePos, str.length - previousLinePos);
return result;
}
export function formatStreamText(str: string): string {
// Do the same thing jupyter is doing
return fixCarriageReturn(fixBackspace(str));
}
export function appendLineFeed(arr: string[], modifier?: (s: string) => string) {
return arr.map((s: string, i: number) => {
const out = modifier ? modifier(s) : s;
return i === arr.length - 1 ? `${out}` : `${out}\n`;
});
}
export function generateMarkdownFromCodeLines(lines: string[]) {
// Generate markdown by stripping out the comments and markdown header
return appendLineFeed(extractComments(lines.slice(1)));
}
// tslint:disable-next-line: cyclomatic-complexity
export function parseForComments(
lines: string[],
foundCommentLine: (s: string, i: number) => void,
foundNonCommentLine: (s: string, i: number) => void
) {
// Check for either multiline or single line comments
let insideMultilineComment: string | undefined;
let insideMultilineQuote: string | undefined;
let pos = 0;
for (const l of lines) {
const trim = l.trim();
// Multiline is triple quotes of either kind
const isMultilineComment = trim.startsWith(SingleQuoteMultiline)
? SingleQuoteMultiline
: trim.startsWith(DoubleQuoteMultiline)
? DoubleQuoteMultiline
: undefined;
const isMultilineQuote = trim.includes(SingleQuoteMultiline)
? SingleQuoteMultiline
: trim.includes(DoubleQuoteMultiline)
? DoubleQuoteMultiline
: undefined;
// Check for ending quotes of multiline string
if (insideMultilineQuote) {
if (insideMultilineQuote === isMultilineQuote) {
insideMultilineQuote = undefined;
}
foundNonCommentLine(l, pos);
// Not inside quote, see if inside a comment
} else if (insideMultilineComment) {
if (insideMultilineComment === isMultilineComment) {
insideMultilineComment = undefined;
}
if (insideMultilineComment) {
foundCommentLine(l, pos);
}
// Not inside either, see if starting a quote
} else if (isMultilineQuote && !isMultilineComment) {
// Make sure doesn't begin and end on the same line.
const beginQuote = trim.indexOf(isMultilineQuote);
const endQuote = trim.lastIndexOf(isMultilineQuote);
insideMultilineQuote = endQuote !== beginQuote ? undefined : isMultilineQuote;
foundNonCommentLine(l, pos);
// Not starting a quote, might be starting a comment
} else if (isMultilineComment) {
// See if this line ends the comment too or not
const endIndex = trim.indexOf(isMultilineComment, 3);
insideMultilineComment = endIndex >= 0 ? undefined : isMultilineComment;
// Might end with text too
if (trim.length > 3) {
foundCommentLine(trim.slice(3, endIndex >= 0 ? endIndex : undefined), pos);
}
} else {
// Normal line
if (trim.startsWith('#')) {
foundCommentLine(trim.slice(1), pos);
} else {
foundNonCommentLine(l, pos);
}
}
pos += 1;
}
}
function extractComments(lines: string[]): string[] {
const result: string[] = [];
parseForComments(
lines,
s => result.push(s),
_s => noop()
);
return result;
}