123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- var labelEnd = {
- name: 'labelEnd',
- tokenize: tokenizeLabelEnd,
- resolveTo: resolveToLabelEnd,
- resolveAll: resolveAllLabelEnd
- }
- export default labelEnd
-
- import assert from 'assert'
- import codes from '../character/codes.mjs'
- import markdownLineEndingOrSpace from '../character/markdown-line-ending-or-space.mjs'
- import constants from '../constant/constants.mjs'
- import types from '../constant/types.mjs'
- import chunkedPush from '../util/chunked-push.mjs'
- import chunkedSplice from '../util/chunked-splice.mjs'
- import normalizeIdentifier from '../util/normalize-identifier.mjs'
- import resolveAll from '../util/resolve-all.mjs'
- import shallow from '../util/shallow.mjs'
- import destinationFactory from './factory-destination.mjs'
- import labelFactory from './factory-label.mjs'
- import titleFactory from './factory-title.mjs'
- import whitespaceFactory from './factory-whitespace.mjs'
-
- var resourceConstruct = {tokenize: tokenizeResource}
- var fullReferenceConstruct = {tokenize: tokenizeFullReference}
- var collapsedReferenceConstruct = {tokenize: tokenizeCollapsedReference}
-
- function resolveAllLabelEnd(events) {
- var index = -1
- var token
-
- while (++index < events.length) {
- token = events[index][1]
-
- if (
- !token._used &&
- (token.type === types.labelImage ||
- token.type === types.labelLink ||
- token.type === types.labelEnd)
- ) {
- // Remove the marker.
- events.splice(index + 1, token.type === types.labelImage ? 4 : 2)
- token.type = types.data
- index++
- }
- }
-
- return events
- }
-
- function resolveToLabelEnd(events, context) {
- var index = events.length
- var offset = 0
- var group
- var label
- var text
- var token
- var open
- var close
- var media
-
- // Find an opening.
- while (index--) {
- token = events[index][1]
-
- if (open) {
- // If we see another link, or inactive link label, we’ve been here before.
- if (
- token.type === types.link ||
- (token.type === types.labelLink && token._inactive)
- ) {
- break
- }
-
- // Mark other link openings as inactive, as we can’t have links in
- // links.
- if (events[index][0] === 'enter' && token.type === types.labelLink) {
- token._inactive = true
- }
- } else if (close) {
- if (
- events[index][0] === 'enter' &&
- (token.type === types.labelImage || token.type === types.labelLink) &&
- !token._balanced
- ) {
- open = index
-
- if (token.type !== types.labelLink) {
- offset = 2
- break
- }
- }
- } else if (token.type === types.labelEnd) {
- close = index
- }
- }
-
- group = {
- type: events[open][1].type === types.labelLink ? types.link : types.image,
- start: shallow(events[open][1].start),
- end: shallow(events[events.length - 1][1].end)
- }
-
- label = {
- type: types.label,
- start: shallow(events[open][1].start),
- end: shallow(events[close][1].end)
- }
-
- text = {
- type: types.labelText,
- start: shallow(events[open + offset + 2][1].end),
- end: shallow(events[close - 2][1].start)
- }
-
- media = [
- ['enter', group, context],
- ['enter', label, context]
- ]
-
- // Opening marker.
- media = chunkedPush(media, events.slice(open + 1, open + offset + 3))
-
- // Text open.
- media = chunkedPush(media, [['enter', text, context]])
-
- // Between.
- media = chunkedPush(
- media,
- resolveAll(
- context.parser.constructs.insideSpan.null,
- events.slice(open + offset + 4, close - 3),
- context
- )
- )
-
- // Text close, marker close, label close.
- media = chunkedPush(media, [
- ['exit', text, context],
- events[close - 2],
- events[close - 1],
- ['exit', label, context]
- ])
-
- // Reference, resource, or so.
- media = chunkedPush(media, events.slice(close + 1))
-
- // Media close.
- media = chunkedPush(media, [['exit', group, context]])
-
- chunkedSplice(events, open, events.length, media)
-
- return events
- }
-
- function tokenizeLabelEnd(effects, ok, nok) {
- var self = this
- var index = self.events.length
- var labelStart
- var defined
-
- // Find an opening.
- while (index--) {
- if (
- (self.events[index][1].type === types.labelImage ||
- self.events[index][1].type === types.labelLink) &&
- !self.events[index][1]._balanced
- ) {
- labelStart = self.events[index][1]
- break
- }
- }
-
- return start
-
- function start(code) {
- assert(code === codes.rightSquareBracket, 'expected `]`')
-
- if (!labelStart) {
- return nok(code)
- }
-
- // It’s a balanced bracket, but contains a link.
- if (labelStart._inactive) return balanced(code)
- defined =
- self.parser.defined.indexOf(
- normalizeIdentifier(
- self.sliceSerialize({start: labelStart.end, end: self.now()})
- )
- ) > -1
- effects.enter(types.labelEnd)
- effects.enter(types.labelMarker)
- effects.consume(code)
- effects.exit(types.labelMarker)
- effects.exit(types.labelEnd)
- return afterLabelEnd
- }
-
- function afterLabelEnd(code) {
- // Resource: `[asd](fgh)`.
- if (code === codes.leftParenthesis) {
- return effects.attempt(
- resourceConstruct,
- ok,
- defined ? ok : balanced
- )(code)
- }
-
- // Collapsed (`[asd][]`) or full (`[asd][fgh]`) reference?
- if (code === codes.leftSquareBracket) {
- return effects.attempt(
- fullReferenceConstruct,
- ok,
- defined
- ? effects.attempt(collapsedReferenceConstruct, ok, balanced)
- : balanced
- )(code)
- }
-
- // Shortcut reference: `[asd]`?
- return defined ? ok(code) : balanced(code)
- }
-
- function balanced(code) {
- labelStart._balanced = true
- return nok(code)
- }
- }
-
- function tokenizeResource(effects, ok, nok) {
- return start
-
- function start(code) {
- assert.equal(code, codes.leftParenthesis, 'expected left paren')
- effects.enter(types.resource)
- effects.enter(types.resourceMarker)
- effects.consume(code)
- effects.exit(types.resourceMarker)
- return whitespaceFactory(effects, open)
- }
-
- function open(code) {
- if (code === codes.rightParenthesis) {
- return end(code)
- }
-
- return destinationFactory(
- effects,
- destinationAfter,
- nok,
- types.resourceDestination,
- types.resourceDestinationLiteral,
- types.resourceDestinationLiteralMarker,
- types.resourceDestinationRaw,
- types.resourceDestinationString,
- constants.linkResourceDestinationBalanceMax
- )(code)
- }
-
- function destinationAfter(code) {
- return markdownLineEndingOrSpace(code)
- ? whitespaceFactory(effects, between)(code)
- : end(code)
- }
-
- function between(code) {
- if (
- code === codes.quotationMark ||
- code === codes.apostrophe ||
- code === codes.leftParenthesis
- ) {
- return titleFactory(
- effects,
- whitespaceFactory(effects, end),
- nok,
- types.resourceTitle,
- types.resourceTitleMarker,
- types.resourceTitleString
- )(code)
- }
-
- return end(code)
- }
-
- function end(code) {
- if (code === codes.rightParenthesis) {
- effects.enter(types.resourceMarker)
- effects.consume(code)
- effects.exit(types.resourceMarker)
- effects.exit(types.resource)
- return ok
- }
-
- return nok(code)
- }
- }
-
- function tokenizeFullReference(effects, ok, nok) {
- var self = this
-
- return start
-
- function start(code) {
- assert.equal(code, codes.leftSquareBracket, 'expected left bracket')
- return labelFactory.call(
- self,
- effects,
- afterLabel,
- nok,
- types.reference,
- types.referenceMarker,
- types.referenceString
- )(code)
- }
-
- function afterLabel(code) {
- return self.parser.defined.indexOf(
- normalizeIdentifier(
- self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1)
- )
- ) < 0
- ? nok(code)
- : ok(code)
- }
- }
-
- function tokenizeCollapsedReference(effects, ok, nok) {
- return start
-
- function start(code) {
- assert.equal(code, codes.leftSquareBracket, 'expected left bracket')
- effects.enter(types.reference)
- effects.enter(types.referenceMarker)
- effects.consume(code)
- effects.exit(types.referenceMarker)
- return open
- }
-
- function open(code) {
- if (code === codes.rightSquareBracket) {
- effects.enter(types.referenceMarker)
- effects.consume(code)
- effects.exit(types.referenceMarker)
- effects.exit(types.reference)
- return ok
- }
-
- return nok(code)
- }
- }
|