forked from jonschlinkert/remarkable
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsmartquotes.js
113 lines (90 loc) · 3.14 KB
/
smartquotes.js
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
// Convert straight quotation marks to typographic ones
//
'use strict';
var QUOTE_TEST_RE = /['"]/;
var QUOTE_RE = /['"]/g;
var PUNCT_RE = /[-\s()\[\]]/;
var APOSTROPHE = '’';
// This function returns true if the character at `pos`
// could be inside a word.
function isLetter(str, pos) {
if (pos < 0 || pos >= str.length) { return false; }
return !PUNCT_RE.test(str[pos]);
}
function replaceAt(str, index, ch) {
return str.substr(0, index) + ch + str.substr(index + 1);
}
module.exports = function smartquotes(state) {
/*eslint max-depth:0*/
var i, token, text, t, pos, max, thisLevel, lastSpace, nextSpace, item,
canOpen, canClose, j, isSingle, blkIdx, tokens,
stack;
if (!state.options.typographer) { return; }
stack = [];
for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) {
if (state.tokens[blkIdx].type !== 'inline') { continue; }
tokens = state.tokens[blkIdx].children;
stack.length = 0;
for (i = 0; i < tokens.length; i++) {
token = tokens[i];
if (token.type !== 'text' || QUOTE_TEST_RE.test(token.text)) { continue; }
thisLevel = tokens[i].level;
for (j = stack.length - 1; j >= 0; j--) {
if (stack[j].level <= thisLevel) { break; }
}
stack.length = j + 1;
text = token.content;
pos = 0;
max = text.length;
/*eslint no-labels:0,block-scoped-var:0*/
OUTER:
while (pos < max) {
QUOTE_RE.lastIndex = pos;
t = QUOTE_RE.exec(text);
if (!t) { break; }
lastSpace = !isLetter(text, t.index - 1);
pos = t.index + 1;
isSingle = (t[0] === "'");
nextSpace = !isLetter(text, pos);
if (!nextSpace && !lastSpace) {
// middle of word
if (isSingle) {
token.content = replaceAt(token.content, t.index, APOSTROPHE);
}
continue;
}
canOpen = !nextSpace;
canClose = !lastSpace;
if (canClose) {
// this could be a closing quote, rewind the stack to get a match
for (j = stack.length - 1; j >= 0; j--) {
item = stack[j];
if (stack[j].level < thisLevel) { break; }
if (item.single === isSingle && stack[j].level === thisLevel) {
item = stack[j];
if (isSingle) {
tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, state.options.quotes[2]);
token.content = replaceAt(token.content, t.index, state.options.quotes[3]);
} else {
tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, state.options.quotes[0]);
token.content = replaceAt(token.content, t.index, state.options.quotes[1]);
}
stack.length = j;
continue OUTER;
}
}
}
if (canOpen) {
stack.push({
token: i,
pos: t.index,
single: isSingle,
level: thisLevel
});
} else if (canClose && isSingle) {
token.content = replaceAt(token.content, t.index, APOSTROPHE);
}
}
}
}
};