Skip to content

Commit

Permalink
Refactor and rename
Browse files Browse the repository at this point in the history
The implementation wasn't fundamentally changed! Mainly, I shifted code
closer to whatever was referencing it.
  • Loading branch information
start committed Oct 5, 2020
1 parent 9b0ac17 commit 67b2baf
Show file tree
Hide file tree
Showing 29 changed files with 267 additions and 312 deletions.
12 changes: 6 additions & 6 deletions src/Implementation/NormalizedSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,27 +126,27 @@ export namespace NormalizedSettings {
private _table: Keyword = []
private _video: Keyword = []

get audio(): Keyword {
audio(): Keyword {
return distinct('audio', ...this._audio)
}

get image(): Keyword {
image(): Keyword {
return distinct('image', 'img', ...this._image)
}

get revealable(): Keyword {
revealable(): Keyword {
return distinct('spoiler', 'nsfw', 'nsfl', 'revealable', ...this._revealable)
}

get sectionLink(): Keyword {
sectionLink(): Keyword {
return distinct('section', 'topic', ...this._sectionLink)
}

get table(): Keyword {
table(): Keyword {
return distinct('table', ...this._table)
}

get video(): Keyword {
video(): Keyword {
return distinct('video', 'vid', ...this._video)
}

Expand Down
6 changes: 3 additions & 3 deletions src/Implementation/Parsing/Inline/MediaConventions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import { TokenRole } from './TokenRole'


export const AUDIO: MediaConvention = {
keyword: keywords => keywords.audio,
keyword: keywords => keywords.audio(),
SyntaxNodeType: Audio,
tokenRoleForStartAndDescription: TokenRole.AudioStartAndDescription
}

export const IMAGE: MediaConvention = {
keyword: keywords => keywords.image,
keyword: keywords => keywords.image(),
SyntaxNodeType: Image,
tokenRoleForStartAndDescription: TokenRole.ImageStartAndDescription
}

export const VIDEO: MediaConvention = {
keyword: keywords => keywords.video,
keyword: keywords => keywords.video(),
SyntaxNodeType: Video,
tokenRoleForStartAndDescription: TokenRole.VideoStartAndDescription
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
import { ConventionContext } from './ConventionContext'
import { OpenConvention } from './OpenConvention'
import { ConventionVariation } from './ConventionVariation'


// We use this class to keep track of which conventions we've been forced to backtrack.
export class BacktrackedConventionHelper {
private failedConventionsByMarkupIndex: FailedConventionsByTextIndex = {}
private failedConventionsByMarkupIndex: {
[markupIndex: number]: ConventionVariation[]
} = {}

registerFailure(contextOfFailedConvention: ConventionContext): void {
const { convention, snapshot } = contextOfFailedConvention
const { markupIndex } = snapshot
registerFailure(failure: OpenConvention): void {
const { markupIndex } = failure.tokenizerSnapshotWhenOpening

if (!this.failedConventionsByMarkupIndex[markupIndex]) {
this.failedConventionsByMarkupIndex[markupIndex] = []
}

this.failedConventionsByMarkupIndex[markupIndex].push(convention)
this.failedConventionsByMarkupIndex[markupIndex] ??= []
this.failedConventionsByMarkupIndex[markupIndex].push(failure.convention)
}

hasFailed(convention: ConventionVariation, markupIndex: number): boolean {
const failedConventions = (this.failedConventionsByMarkupIndex[markupIndex] ?? [])
return failedConventions.some(failedConvention => failedConvention === convention)
}
}


interface FailedConventionsByTextIndex {
[markupIndex: number]: ConventionVariation[]
}
53 changes: 0 additions & 53 deletions src/Implementation/Parsing/Inline/Tokenizing/ConventionContext.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { patternIgnoringCapitalizationAndStartingWith, patternStartingWith } from '../../../PatternHelpers'
import { TokenRole } from '../TokenRole'
import { ConventionContext } from './ConventionContext'
import { OpenConvention } from './OpenConvention'


// The start/end delimiters of a convention variation are ultimately represented by
Expand Down Expand Up @@ -28,30 +28,30 @@ export class ConventionVariation {
isCutShortByWhitespace?: boolean
canConsistSolelyOfWhitespace?: boolean
flushesBufferToTextTokenBeforeOpening?: boolean
whenOpening?: (match: string, charAfterMatch: string, ...captures: string[]) => void
whenOpening?: (...captures: string[]) => void
insteadOfClosingOuterConventionsWhileOpen?: () => void
insteadOfOpeningRegularConventionsWhileOpen?: () => void
failsIfWhitespaceIsEnounteredBeforeClosing?: boolean
beforeClosingItFlushesNonEmptyBufferTo?: TokenRole
beforeClosingItAlwaysFlushesBufferTo?: TokenRole
whenClosingItAlsoClosesInnerConventions?: boolean
mustBeDirectlyFollowedBy?: ConventionVariation[]
whenClosing?: (context: ConventionContext) => void
whenClosing?: (thisOpenConvention: OpenConvention) => void
insteadOfFailingWhenLeftUnclosed?: () => void

constructor(args: ConventionVariationArgs) {
// First, let's blindly copy everything from `args` to `this`.
//
// Alas! This also copies the string `startsWith`/`endsWith` fields, too! We'll
// overwrite those below.
// overwrite those below and convert them into anchored RegExp patterns.
Object.assign(this, args)
const { startsWith, endsWith } = args

this.startsWith =
// Some of our start delimiters can contain terms. As a rule, terms are
// case-insensitive.
//
// Let's determine whether we need to worry about case sensitivity.
// Here, we determine whether we need to worry about case sensitivity.
startsWith.toLowerCase() != startsWith.toUpperCase()
? patternIgnoringCapitalizationAndStartingWith(startsWith)
: patternStartingWith(startsWith)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
export class StartDelimiter {
export class ActiveStartDelimiter {
constructor(
public tokenIndex: number,
public delimiterText: string,
public remainingLength = delimiterText.length) {
}

get isUnused(): boolean {
isUnused(): boolean {
return this.remainingLength === this.delimiterText.length
}

get isFullyExhausted(): boolean {
isFullyExhausted(): boolean {
return this.remainingLength <= 0
}

shortenBy(length: number): void {
this.remainingLength -= length
}

clone(): StartDelimiter {
return new StartDelimiter(this.tokenIndex, this.delimiterText, this.remainingLength)
clone(): ActiveStartDelimiter {
return new ActiveStartDelimiter(this.tokenIndex, this.delimiterText, this.remainingLength)
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { atLeast, escapeForRegex, patternStartingWith } from '../../../../PatternHelpers'
import { StartDelimiter } from './StartDelimiter'
import { ActiveStartDelimiter } from './ActiveStartDelimiter'

// For a given delimiter character (`delimiterChar`), this class matchds end delimiters with start
// delimiters, even if they aren't perfectly balanced.
export class ForgivingConventionHandler {
delimiterPattern: RegExp
private startDelimiters: StartDelimiter[] = []
private startDelimiters: ActiveStartDelimiter[] = []

constructor(
// We save `options` as a field to make it easier to clone this object.
Expand Down Expand Up @@ -42,45 +42,42 @@ export class ForgivingConventionHandler {
}
) {
const delimiterChar = escapeForRegex(options.delimiterChar)
const minDelimiterLength = options.minDelimiterLength || 1
const minDelimiterLength = options.minDelimiterLength ?? 1

this.delimiterPattern = patternStartingWith(
atLeast(minDelimiterLength, delimiterChar))
}

get unusedStartDelimiters(): StartDelimiter[] {
return this.startDelimiters.filter(startDelimiter => startDelimiter.isUnused)
unusedStartDelimiters(): ActiveStartDelimiter[] {
return this.startDelimiters.filter(startDelimiter => startDelimiter.isUnused())
}

addStartDelimiter(startDelimiterText: string, tokenIndex: number): void {
// Start delimiters are stored from most-to-least recent!
this.startDelimiters.unshift(
new StartDelimiter(tokenIndex, startDelimiterText))
new ActiveStartDelimiter(tokenIndex, startDelimiterText))
}

tryToCloseAnyOpenStartDelimiters(endDelimiterText: string): boolean {
if (!this.openStartDelimiters.length) {
tryToCloseAnyOpenStartDelimiters(endDelimiterLength: number): boolean {
if (!this.openStartDelimiters().length) {
return false
}

const { options } = this
let endDelimiterLength = endDelimiterText.length

const { isPerfectMatch } = options
const { isPerfectMatch } = this.options

if (isPerfectMatch) {
const isDelimiterPerfectMatch =
(startDelimiter: StartDelimiter) =>
(startDelimiter: ActiveStartDelimiter) =>
isPerfectMatch(startDelimiter.remainingLength, endDelimiterLength)

const perfectStartDelimiter =
this.openStartDelimiters.find(isDelimiterPerfectMatch)
this.openStartDelimiters().find(isDelimiterPerfectMatch)

if (perfectStartDelimiter) {
const lengthInCommon =
Math.min(perfectStartDelimiter.remainingLength, endDelimiterLength)

options.whenDelimitersEnclose(perfectStartDelimiter.tokenIndex, lengthInCommon)
this.options.whenDelimitersEnclose(perfectStartDelimiter.tokenIndex, lengthInCommon)
perfectStartDelimiter.shortenBy(lengthInCommon)

return true
Expand All @@ -89,15 +86,15 @@ export class ForgivingConventionHandler {

// From here on out, the end delimiter will "cancel out" as many remaining characters as
// it can with each start delimiter.
for (const startDelimiter of this.openStartDelimiters) {
for (const startDelimiter of this.openStartDelimiters()) {
if (!endDelimiterLength) {
return true
}

const lengthInCommon =
Math.min(startDelimiter.remainingLength, endDelimiterLength)

options.whenDelimitersEnclose(startDelimiter.tokenIndex, lengthInCommon)
this.options.whenDelimitersEnclose(startDelimiter.tokenIndex, lengthInCommon)

startDelimiter.shortenBy(lengthInCommon)
endDelimiterLength -= lengthInCommon
Expand All @@ -107,14 +104,14 @@ export class ForgivingConventionHandler {
}

registerTokenInsertion(atIndex: number): void {
for (const startDelimiter of this.openStartDelimiters) {
for (const startDelimiter of this.openStartDelimiters()) {
if (atIndex < startDelimiter.tokenIndex) {
startDelimiter.tokenIndex += 1
}
}
}

// Like the `ConventionContext` class, this class needs to be clonable in order to properly
// Like the `OpenConvention` class, this class needs to be clonable in order to properly
// handle backtracking.
clone(): ForgivingConventionHandler {
const clone = new ForgivingConventionHandler(this.options)
Expand All @@ -124,7 +121,7 @@ export class ForgivingConventionHandler {
}

// Returns open start delimiters from most-to-least recent.
private get openStartDelimiters(): StartDelimiter[] {
return this.startDelimiters.filter(delimiter => !delimiter.isFullyExhausted)
private openStartDelimiters(): ActiveStartDelimiter[] {
return this.startDelimiters.filter(delimiter => !delimiter.isFullyExhausted())
}
}
25 changes: 25 additions & 0 deletions src/Implementation/Parsing/Inline/Tokenizing/OpenConvention.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ConventionVariation } from './ConventionVariation'
import { ForgivingConventionHandler } from './ForgivingConventions/ForgivingConventionHandler'
import { Token } from './Token'


export class OpenConvention {
constructor(
public convention: ConventionVariation,
public tokenizerSnapshotWhenOpening: {
markupIndex: number
markupIndexThatLastOpenedAConvention?: number
bufferedContent: string
tokens: Token[]
openConventions: OpenConvention[]
forgivingConventionHandlers: ForgivingConventionHandler[]
},
public startTokenIndex = tokenizerSnapshotWhenOpening.tokens.length) { }

clone(): OpenConvention {
return new OpenConvention(
this.convention,
this.tokenizerSnapshotWhenOpening,
this.startTokenIndex)
}
}
Loading

0 comments on commit 67b2baf

Please sign in to comment.