From 61bd7a7635537940bb526bb78db3f9f302ff3d39 Mon Sep 17 00:00:00 2001 From: Alex Komoroske Date: Sat, 16 Dec 2023 16:58:04 -0800 Subject: [PATCH] Add a convert-markdown suggestor. If there are semantic changes to be made by converting markdown, it suggests them. Part of #670. --- src/suggestions.ts | 6 +++ src/suggestions/convert-markdown.ts | 64 +++++++++++++++++++++++++++++ src/types.ts | 3 +- src/util.ts | 15 +++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/suggestions/convert-markdown.ts diff --git a/src/suggestions.ts b/src/suggestions.ts index 34cd1c7f..c03d62c7 100644 --- a/src/suggestions.ts +++ b/src/suggestions.ts @@ -66,6 +66,7 @@ import { import { convertToQuote } from './suggestions/convert-to-quote.js'; +import { convertMarkdown } from './suggestions/convert-markdown.js'; export const makeReferenceSuggestion = (type : SuggestionType, keyCards: CardID | CardID[], otherCards: CardID | CardID[], referenceType : ReferenceType, reverse = false) : Suggestion => { //TODO: it's kind of finicky to have to keep track of which ID is which... shouldn't the actions have a sentinel for each that is overriden before being executed? @@ -196,6 +197,11 @@ export const SUGGESTORS : {[suggestor in SuggestionType]: Suggestor} = { generator: convertToQuote, title: 'Convert to Quote', color: COLORS.NAVY + }, + 'convert-markdown': { + generator: convertMarkdown, + title: 'Convert Markdown', + color: COLORS.ROYAL_BLUE } }; diff --git a/src/suggestions/convert-markdown.ts b/src/suggestions/convert-markdown.ts new file mode 100644 index 00000000..fb1bcfaa --- /dev/null +++ b/src/suggestions/convert-markdown.ts @@ -0,0 +1,64 @@ +import snarkdown from 'snarkdown'; + +import { + SuggestorArgs +} from '../suggestions.js'; + +import { + CardDiff, + Suggestion +} from '../types.js'; + +import { + TEXT_FIELD_CONFIGURATION, + editableFieldsForCardType +} from '../card_fields.js'; + +import { + cardDiffHasChanges +} from '../card_diff.js'; + +import { + TypedObject +} from '../typed_object.js'; + +import { + htmlIsEquivalent +} from '../util.js'; + +//There can be missing concept links when concepts were added after a card was added. +export const convertMarkdown = async (args: SuggestorArgs) : Promise => { + //TODO: isn't there a filter that does this? + + const {type, card, logger} = args; + + const diff : CardDiff = {}; + + for (const cardField of TypedObject.keys(editableFieldsForCardType(card.card_type))) { + const config = TEXT_FIELD_CONFIGURATION[cardField]; + if (!config.html) continue; + const value = card[cardField] as string; + const convertedValue = snarkdown(value); + //The HTML might differ in whitespace. + if (!htmlIsEquivalent(value, convertedValue)) { + logger.info(`Suggesting converting for field ${cardField} ${value} to ${convertedValue}`); + diff[cardField] = convertedValue; + } + + } + + if (!cardDiffHasChanges(diff)) { + logger.info('No changes proposed by markdown'); + return []; + } + + return [{ + type, + keyCards: [card.id], + supportingCards: [], + action: { + keyCards: diff + } + }]; + +}; diff --git a/src/types.ts b/src/types.ts index 1bb165d9..75843552 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1311,7 +1311,8 @@ export type SuggestionType = 'add-see-also' | 'synthesize-cluster' | 'remove-priority' | 'add-concept' - | 'convert-to-quote'; + | 'convert-to-quote' + | 'convert-markdown'; export type Suggestion = { type: SuggestionType, diff --git a/src/util.ts b/src/util.ts index 9ebc98c8..5576ea5e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -305,6 +305,21 @@ export const isURL = (str: string) : boolean => { } }; +export const htmlIsEquivalent = (a : string, b: string) : boolean => { + const doc = getDocument(); + if (!doc) throw new Error('no document'); + const aDiv = doc.createElement('div'); + const bDiv = doc.createElement('div'); + + aDiv.innerHTML = a; + bDiv.innerHTML = b; + + const serializedHTMLA = aDiv.innerHTML.replace(/\s/g, ''); + const serializedHTMLB = bDiv.innerHTML.replace(/\s/g, ''); + + return serializedHTMLA === serializedHTMLB; +}; + //Returns a string with the reason that the proposed card type is not legal for //this card. If the string is '' then it is legal. export const reasonCardTypeNotLegalForCard = (card : Card, proposedCardType : CardType) => {