You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
5.8 KiB
186 lines
5.8 KiB
'use strict' |
|
|
|
var chunkedPush = require('../util/chunked-push.js') |
|
var chunkedSplice = require('../util/chunked-splice.js') |
|
var classifyCharacter = require('../util/classify-character.js') |
|
var movePoint = require('../util/move-point.js') |
|
var resolveAll = require('../util/resolve-all.js') |
|
var shallow = require('../util/shallow.js') |
|
|
|
var attention = { |
|
name: 'attention', |
|
tokenize: tokenizeAttention, |
|
resolveAll: resolveAllAttention |
|
} |
|
|
|
function resolveAllAttention(events, context) { |
|
var index = -1 |
|
var open |
|
var group |
|
var text |
|
var openingSequence |
|
var closingSequence |
|
var use |
|
var nextEvents |
|
var offset // Walk through all events. |
|
// |
|
// Note: performance of this is fine on an mb of normal markdown, but it’s |
|
// a bottleneck for malicious stuff. |
|
|
|
while (++index < events.length) { |
|
// Find a token that can close. |
|
if ( |
|
events[index][0] === 'enter' && |
|
events[index][1].type === 'attentionSequence' && |
|
events[index][1]._close |
|
) { |
|
open = index // Now walk back to find an opener. |
|
|
|
while (open--) { |
|
// Find a token that can open the closer. |
|
if ( |
|
events[open][0] === 'exit' && |
|
events[open][1].type === 'attentionSequence' && |
|
events[open][1]._open && // If the markers are the same: |
|
context.sliceSerialize(events[open][1]).charCodeAt(0) === |
|
context.sliceSerialize(events[index][1]).charCodeAt(0) |
|
) { |
|
// If the opening can close or the closing can open, |
|
// and the close size *is not* a multiple of three, |
|
// but the sum of the opening and closing size *is* multiple of three, |
|
// then don’t match. |
|
if ( |
|
(events[open][1]._close || events[index][1]._open) && |
|
(events[index][1].end.offset - events[index][1].start.offset) % 3 && |
|
!( |
|
(events[open][1].end.offset - |
|
events[open][1].start.offset + |
|
events[index][1].end.offset - |
|
events[index][1].start.offset) % |
|
3 |
|
) |
|
) { |
|
continue |
|
} // Number of markers to use from the sequence. |
|
|
|
use = |
|
events[open][1].end.offset - events[open][1].start.offset > 1 && |
|
events[index][1].end.offset - events[index][1].start.offset > 1 |
|
? 2 |
|
: 1 |
|
openingSequence = { |
|
type: use > 1 ? 'strongSequence' : 'emphasisSequence', |
|
start: movePoint(shallow(events[open][1].end), -use), |
|
end: shallow(events[open][1].end) |
|
} |
|
closingSequence = { |
|
type: use > 1 ? 'strongSequence' : 'emphasisSequence', |
|
start: shallow(events[index][1].start), |
|
end: movePoint(shallow(events[index][1].start), use) |
|
} |
|
text = { |
|
type: use > 1 ? 'strongText' : 'emphasisText', |
|
start: shallow(events[open][1].end), |
|
end: shallow(events[index][1].start) |
|
} |
|
group = { |
|
type: use > 1 ? 'strong' : 'emphasis', |
|
start: shallow(openingSequence.start), |
|
end: shallow(closingSequence.end) |
|
} |
|
events[open][1].end = shallow(openingSequence.start) |
|
events[index][1].start = shallow(closingSequence.end) |
|
nextEvents = [] // If there are more markers in the opening, add them before. |
|
|
|
if (events[open][1].end.offset - events[open][1].start.offset) { |
|
nextEvents = chunkedPush(nextEvents, [ |
|
['enter', events[open][1], context], |
|
['exit', events[open][1], context] |
|
]) |
|
} // Opening. |
|
|
|
nextEvents = chunkedPush(nextEvents, [ |
|
['enter', group, context], |
|
['enter', openingSequence, context], |
|
['exit', openingSequence, context], |
|
['enter', text, context] |
|
]) // Between. |
|
|
|
nextEvents = chunkedPush( |
|
nextEvents, |
|
resolveAll( |
|
context.parser.constructs.insideSpan.null, |
|
events.slice(open + 1, index), |
|
context |
|
) |
|
) // Closing. |
|
|
|
nextEvents = chunkedPush(nextEvents, [ |
|
['exit', text, context], |
|
['enter', closingSequence, context], |
|
['exit', closingSequence, context], |
|
['exit', group, context] |
|
]) // If there are more markers in the closing, add them after. |
|
|
|
if (events[index][1].end.offset - events[index][1].start.offset) { |
|
offset = 2 |
|
nextEvents = chunkedPush(nextEvents, [ |
|
['enter', events[index][1], context], |
|
['exit', events[index][1], context] |
|
]) |
|
} else { |
|
offset = 0 |
|
} |
|
|
|
chunkedSplice(events, open - 1, index - open + 3, nextEvents) |
|
index = open + nextEvents.length - offset - 2 |
|
break |
|
} |
|
} |
|
} |
|
} // Remove remaining sequences. |
|
|
|
index = -1 |
|
|
|
while (++index < events.length) { |
|
if (events[index][1].type === 'attentionSequence') { |
|
events[index][1].type = 'data' |
|
} |
|
} |
|
|
|
return events |
|
} |
|
|
|
function tokenizeAttention(effects, ok) { |
|
var before = classifyCharacter(this.previous) |
|
var marker |
|
return start |
|
|
|
function start(code) { |
|
effects.enter('attentionSequence') |
|
marker = code |
|
return sequence(code) |
|
} |
|
|
|
function sequence(code) { |
|
var token |
|
var after |
|
var open |
|
var close |
|
|
|
if (code === marker) { |
|
effects.consume(code) |
|
return sequence |
|
} |
|
|
|
token = effects.exit('attentionSequence') |
|
after = classifyCharacter(code) |
|
open = !after || (after === 2 && before) |
|
close = !before || (before === 2 && after) |
|
token._open = marker === 42 ? open : open && (before || !close) |
|
token._close = marker === 42 ? close : close && (after || !open) |
|
return ok(code) |
|
} |
|
} |
|
|
|
module.exports = attention
|
|
|