forked from FINAKON/HelpProject
1. Initial Commit - a boiler plate code and POC to realize the concept of context sensitive help 2. Frontend code written in ReactJS 3. Backend code written in Java, Spring Boot Framework 4. Frontend Start: pre-requisites : node, npm npm run dev ==> to start the frontend vite server 5. Backend Start: pre-requisites : java, mvn mvn spring-boot:run ==> to start the backend server 6. Visit http://localhost:5173/ for basic demo of help, press F1 in textboxes 7. Visit http://localhost:5173/editor and enter "admin123" to add/modify texts. Happy Coding !!! Thank you, Bhargava.
301 lines
8.0 KiB
JavaScript
301 lines
8.0 KiB
JavaScript
/**
|
||
* @typedef {import('mdast').InlineCode} InlineCode
|
||
* @typedef {import('mdast').Table} Table
|
||
* @typedef {import('mdast').TableCell} TableCell
|
||
* @typedef {import('mdast').TableRow} TableRow
|
||
*
|
||
* @typedef {import('markdown-table').Options} MarkdownTableOptions
|
||
*
|
||
* @typedef {import('mdast-util-from-markdown').CompileContext} CompileContext
|
||
* @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension
|
||
* @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle
|
||
*
|
||
* @typedef {import('mdast-util-to-markdown').Options} ToMarkdownExtension
|
||
* @typedef {import('mdast-util-to-markdown').Handle} ToMarkdownHandle
|
||
* @typedef {import('mdast-util-to-markdown').State} State
|
||
* @typedef {import('mdast-util-to-markdown').Info} Info
|
||
*/
|
||
|
||
/**
|
||
* @typedef Options
|
||
* Configuration.
|
||
* @property {boolean | null | undefined} [tableCellPadding=true]
|
||
* Whether to add a space of padding between delimiters and cells (default:
|
||
* `true`).
|
||
* @property {boolean | null | undefined} [tablePipeAlign=true]
|
||
* Whether to align the delimiters (default: `true`).
|
||
* @property {MarkdownTableOptions['stringLength'] | null | undefined} [stringLength]
|
||
* Function to detect the length of table cell content, used when aligning
|
||
* the delimiters between cells (optional).
|
||
*/
|
||
|
||
import {ok as assert} from 'devlop'
|
||
import {markdownTable} from 'markdown-table'
|
||
import {defaultHandlers} from 'mdast-util-to-markdown'
|
||
|
||
/**
|
||
* Create an extension for `mdast-util-from-markdown` to enable GFM tables in
|
||
* markdown.
|
||
*
|
||
* @returns {FromMarkdownExtension}
|
||
* Extension for `mdast-util-from-markdown` to enable GFM tables.
|
||
*/
|
||
export function gfmTableFromMarkdown() {
|
||
return {
|
||
enter: {
|
||
table: enterTable,
|
||
tableData: enterCell,
|
||
tableHeader: enterCell,
|
||
tableRow: enterRow
|
||
},
|
||
exit: {
|
||
codeText: exitCodeText,
|
||
table: exitTable,
|
||
tableData: exit,
|
||
tableHeader: exit,
|
||
tableRow: exit
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @this {CompileContext}
|
||
* @type {FromMarkdownHandle}
|
||
*/
|
||
function enterTable(token) {
|
||
const align = token._align
|
||
assert(align, 'expected `_align` on table')
|
||
this.enter(
|
||
{
|
||
type: 'table',
|
||
align: align.map(function (d) {
|
||
return d === 'none' ? null : d
|
||
}),
|
||
children: []
|
||
},
|
||
token
|
||
)
|
||
this.data.inTable = true
|
||
}
|
||
|
||
/**
|
||
* @this {CompileContext}
|
||
* @type {FromMarkdownHandle}
|
||
*/
|
||
function exitTable(token) {
|
||
this.exit(token)
|
||
this.data.inTable = undefined
|
||
}
|
||
|
||
/**
|
||
* @this {CompileContext}
|
||
* @type {FromMarkdownHandle}
|
||
*/
|
||
function enterRow(token) {
|
||
this.enter({type: 'tableRow', children: []}, token)
|
||
}
|
||
|
||
/**
|
||
* @this {CompileContext}
|
||
* @type {FromMarkdownHandle}
|
||
*/
|
||
function exit(token) {
|
||
this.exit(token)
|
||
}
|
||
|
||
/**
|
||
* @this {CompileContext}
|
||
* @type {FromMarkdownHandle}
|
||
*/
|
||
function enterCell(token) {
|
||
this.enter({type: 'tableCell', children: []}, token)
|
||
}
|
||
|
||
// Overwrite the default code text data handler to unescape escaped pipes when
|
||
// they are in tables.
|
||
/**
|
||
* @this {CompileContext}
|
||
* @type {FromMarkdownHandle}
|
||
*/
|
||
function exitCodeText(token) {
|
||
let value = this.resume()
|
||
|
||
if (this.data.inTable) {
|
||
value = value.replace(/\\([\\|])/g, replace)
|
||
}
|
||
|
||
const node = this.stack[this.stack.length - 1]
|
||
assert(node.type === 'inlineCode')
|
||
node.value = value
|
||
this.exit(token)
|
||
}
|
||
|
||
/**
|
||
* @param {string} $0
|
||
* @param {string} $1
|
||
* @returns {string}
|
||
*/
|
||
function replace($0, $1) {
|
||
// Pipes work, backslashes don’t (but can’t escape pipes).
|
||
return $1 === '|' ? $1 : $0
|
||
}
|
||
|
||
/**
|
||
* Create an extension for `mdast-util-to-markdown` to enable GFM tables in
|
||
* markdown.
|
||
*
|
||
* @param {Options | null | undefined} [options]
|
||
* Configuration.
|
||
* @returns {ToMarkdownExtension}
|
||
* Extension for `mdast-util-to-markdown` to enable GFM tables.
|
||
*/
|
||
export function gfmTableToMarkdown(options) {
|
||
const settings = options || {}
|
||
const padding = settings.tableCellPadding
|
||
const alignDelimiters = settings.tablePipeAlign
|
||
const stringLength = settings.stringLength
|
||
const around = padding ? ' ' : '|'
|
||
|
||
return {
|
||
unsafe: [
|
||
{character: '\r', inConstruct: 'tableCell'},
|
||
{character: '\n', inConstruct: 'tableCell'},
|
||
// A pipe, when followed by a tab or space (padding), or a dash or colon
|
||
// (unpadded delimiter row), could result in a table.
|
||
{atBreak: true, character: '|', after: '[\t :-]'},
|
||
// A pipe in a cell must be encoded.
|
||
{character: '|', inConstruct: 'tableCell'},
|
||
// A colon must be followed by a dash, in which case it could start a
|
||
// delimiter row.
|
||
{atBreak: true, character: ':', after: '-'},
|
||
// A delimiter row can also start with a dash, when followed by more
|
||
// dashes, a colon, or a pipe.
|
||
// This is a stricter version than the built in check for lists, thematic
|
||
// breaks, and setex heading underlines though:
|
||
// <https://github.com/syntax-tree/mdast-util-to-markdown/blob/51a2038/lib/unsafe.js#L57>
|
||
{atBreak: true, character: '-', after: '[:|-]'}
|
||
],
|
||
handlers: {
|
||
inlineCode: inlineCodeWithTable,
|
||
table: handleTable,
|
||
tableCell: handleTableCell,
|
||
tableRow: handleTableRow
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @type {ToMarkdownHandle}
|
||
* @param {Table} node
|
||
*/
|
||
function handleTable(node, _, state, info) {
|
||
return serializeData(handleTableAsData(node, state, info), node.align)
|
||
}
|
||
|
||
/**
|
||
* This function isn’t really used normally, because we handle rows at the
|
||
* table level.
|
||
* But, if someone passes in a table row, this ensures we make somewhat sense.
|
||
*
|
||
* @type {ToMarkdownHandle}
|
||
* @param {TableRow} node
|
||
*/
|
||
function handleTableRow(node, _, state, info) {
|
||
const row = handleTableRowAsData(node, state, info)
|
||
const value = serializeData([row])
|
||
// `markdown-table` will always add an align row
|
||
return value.slice(0, value.indexOf('\n'))
|
||
}
|
||
|
||
/**
|
||
* @type {ToMarkdownHandle}
|
||
* @param {TableCell} node
|
||
*/
|
||
function handleTableCell(node, _, state, info) {
|
||
const exit = state.enter('tableCell')
|
||
const subexit = state.enter('phrasing')
|
||
const value = state.containerPhrasing(node, {
|
||
...info,
|
||
before: around,
|
||
after: around
|
||
})
|
||
subexit()
|
||
exit()
|
||
return value
|
||
}
|
||
|
||
/**
|
||
* @param {Array<Array<string>>} matrix
|
||
* @param {Array<string | null | undefined> | null | undefined} [align]
|
||
*/
|
||
function serializeData(matrix, align) {
|
||
return markdownTable(matrix, {
|
||
align,
|
||
// @ts-expect-error: `markdown-table` types should support `null`.
|
||
alignDelimiters,
|
||
// @ts-expect-error: `markdown-table` types should support `null`.
|
||
padding,
|
||
// @ts-expect-error: `markdown-table` types should support `null`.
|
||
stringLength
|
||
})
|
||
}
|
||
|
||
/**
|
||
* @param {Table} node
|
||
* @param {State} state
|
||
* @param {Info} info
|
||
*/
|
||
function handleTableAsData(node, state, info) {
|
||
const children = node.children
|
||
let index = -1
|
||
/** @type {Array<Array<string>>} */
|
||
const result = []
|
||
const subexit = state.enter('table')
|
||
|
||
while (++index < children.length) {
|
||
result[index] = handleTableRowAsData(children[index], state, info)
|
||
}
|
||
|
||
subexit()
|
||
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* @param {TableRow} node
|
||
* @param {State} state
|
||
* @param {Info} info
|
||
*/
|
||
function handleTableRowAsData(node, state, info) {
|
||
const children = node.children
|
||
let index = -1
|
||
/** @type {Array<string>} */
|
||
const result = []
|
||
const subexit = state.enter('tableRow')
|
||
|
||
while (++index < children.length) {
|
||
// Note: the positional info as used here is incorrect.
|
||
// Making it correct would be impossible due to aligning cells?
|
||
// And it would need copy/pasting `markdown-table` into this project.
|
||
result[index] = handleTableCell(children[index], node, state, info)
|
||
}
|
||
|
||
subexit()
|
||
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* @type {ToMarkdownHandle}
|
||
* @param {InlineCode} node
|
||
*/
|
||
function inlineCodeWithTable(node, parent, state) {
|
||
let value = defaultHandlers.inlineCode(node, parent, state)
|
||
|
||
if (state.stack.includes('tableCell')) {
|
||
value = value.replace(/\|/g, '\\$&')
|
||
}
|
||
|
||
return value
|
||
}
|
||
}
|