mdast#Paragraph TypeScript Examples

The following examples show how to use mdast#Paragraph. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
static isParagraph(node: Node): node is Paragraph {
    return node.type === DendronASTTypes.PARAGRAPH;
  }
Example #2
Source File: toc.ts    From website-docs with MIT License 6 votes vote down vote up
export function mdxAstToToc(ast: ListItem[], config: PathConfig): RepoNav {
  return ast.map(node => {
    const content = node.children as [Paragraph, List | undefined]
    if (content.length > 0 && content.length <= 2) {
      const ret = getContentFromLink(content[0], config)

      if (content[1]) {
        const list = content[1]
        if (list.type !== 'list') {
          throw new Error(`incorrect listitem in TOC.md`)
        }

        ret.children = mdxAstToToc(list.children, config)
      }

      return ret
    }

    throw new Error(`incorrect format in TOC.md`)
  })
}
Example #3
Source File: utils.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
/** Extract all blocks from the note which could be referenced by a block anchor.
   *
   * If those blocks already have anchors (or if they are a header), this will also find that anchor.
   *
   * @param note The note from which blocks will be extracted.
   */
  static async extractBlocks({
    note,
    engine,
  }: {
    note: NoteProps;
    engine: DEngineClient;
  }): Promise<NoteBlock[]> {
    const proc = MDUtilsV5.procRemarkFull({
      engine,
      vault: note.vault,
      fname: note.fname,
      dest: DendronASTDest.MD_DENDRON,
    });
    const slugger = getSlugger();

    // Read and parse the note
    const noteText = NoteUtils.serialize(note);
    const noteAST = proc.parse(noteText);
    // @ts-ignore
    if (_.isUndefined(noteAST.children)) return [];
    // @ts-ignore
    const nodesToSearch = _.filter(noteAST.children as Node[], (node) =>
      _.includes(NODE_TYPES_TO_EXTRACT, node.type)
    );

    // Extract the blocks
    const blocks: NoteBlock[] = [];
    for (const node of nodesToSearch) {
      // Block anchors at top level refer to the blocks before them
      if (node.type === DendronASTTypes.PARAGRAPH) {
        // These look like a paragraph...
        const parent = node as Paragraph;
        if (parent.children.length === 1) {
          // ... that has only a block anchor in it ...
          const child = parent.children[0] as Node;
          if (child.type === DendronASTTypes.BLOCK_ANCHOR) {
            // ... in which case this block anchor refers to the previous block, if any
            const previous = _.last(blocks);
            if (!_.isUndefined(previous))
              [, previous.anchor] =
                AnchorUtils.anchorNode2anchor(child as BlockAnchor, slugger) ||
                [];
            // Block anchors themselves are not blocks, don't extract them
            continue;
          }
        }
      }

      // Extract list items out of lists. We also extract them from nested lists,
      // because block anchors can't refer to nested lists, only items inside of them
      if (node.type === DendronASTTypes.LIST) {
        visit(node, [DendronASTTypes.LIST_ITEM], (listItem: ListItem) => {
          // The list item might have a block anchor inside of it.
          let anchor: DNoteAnchorPositioned | undefined;
          visit(
            listItem,
            [DendronASTTypes.BLOCK_ANCHOR, DendronASTTypes.LIST],
            (inListItem) => {
              // Except if we hit a nested list, because then the block anchor refers to the item in the nested list
              if (inListItem.type === DendronASTTypes.LIST) return "skip";
              [, anchor] =
                AnchorUtils.anchorNode2anchor(
                  inListItem as BlockAnchor,
                  slugger
                ) || [];
              return;
            }
          );

          blocks.push({
            text: proc.stringify(listItem),
            anchor,
            // position can only be undefined for generated nodes, not for parsed ones
            position: listItem.position!,
            type: listItem.type,
          });
        });
      }

      // extract the anchor for this block, if it exists
      let anchor: DNoteAnchorPositioned | undefined;
      if (node.type === DendronASTTypes.HEADING) {
        // Headings are anchors themselves
        [, anchor] =
          AnchorUtils.anchorNode2anchor(node as Heading, slugger) || [];
      } else if (node.type !== DendronASTTypes.LIST) {
        // Other nodes might have block anchors inside them
        // Except lists, because anchors inside lists only refer to specific list items
        visit(node, [DendronASTTypes.BLOCK_ANCHOR], (child) => {
          [, anchor] =
            AnchorUtils.anchorNode2anchor(child as BlockAnchor, slugger) || [];
        });
      }

      // extract the block
      blocks.push({
        text: proc.stringify(node),
        anchor,
        // position can only be undefined for generated nodes, not for parsed ones
        position: node.position!,
        type: node.type,
      });
    }

    return blocks;
  }
Example #4
Source File: toc.ts    From website-docs with MIT License 5 votes vote down vote up
function getContentFromLink(
  content: Paragraph,
  config: PathConfig
): RepoNavLink {
  if (content.type !== 'paragraph' || content.children.length === 0) {
    throw new Error(`incorrect format in TOC.md`)
  }

  const child = content.children[0] as Link | Text

  if (child.type === 'link') {
    if (child.children.length === 0) {
      throw new Error(`incorrect link in TOC.md`)
    }

    const content = child.children.map(node => {
      switch (node.type) {
        case 'text':
          return node.value
        case 'inlineCode':
          return { code: true, value: node.value }
        default:
          throw new Error(`unsupported tag ${node.type} in TOC link`)
      }
    })

    if (child.url.startsWith('https://')) {
      return {
        link: child.url,
        content,
      }
    }

    const urlSegs = child.url.split('/')
    const filename = urlSegs[urlSegs.length - 1].replace('.md', '')

    return {
      link: generateUrl(filename, config),
      content,
    }
  } else {
    return {
      content: [child.value],
    }
  }
}
Example #5
Source File: backlinksHover.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
/**
 * Unified processor for rendering text in the backlinks hover control. This
 * processor returns a transformer that does the following:
 * 1. Highlights the backlink text
 * 2. Changes the backlink node away from a wikilink/noteref to prevent the
 *    backlink text from being altered
 * 3. Adds contextual " --- line # ---" information
 * 4. Removes all elements that lie beyond the contextual lines limit of the
 *    backlink
 * @param this
 * @param _opts
 * @returns
 */
export function backlinksHover(
  this: Unified.Processor,
  _opts?: BacklinkOpts
): Transformer {
  function transformer(tree: Node, _file: VFile) {
    if (!_opts) {
      return;
    }

    const backlinkLineNumber = _opts.location.start.line;

    const lowerLineLimit = backlinkLineNumber - _opts.linesOfContext;
    const upperLineLimit = backlinkLineNumber + _opts.linesOfContext;

    /**
     * The last line of the YAML frontmatter counts as line 0.
     */
    let documentBodyStartLine = 0;
    let documentEndLine = 0;

    // In the first visit, set the beginning and end markers of the document.
    visit(tree, [DendronASTTypes.ROOT], (node, _index, _parent) => {
      if (RemarkUtils.isRoot(node)) {
        documentEndLine = node.position?.end.line ?? 0;

        // Count the last line of YAML as the 0 indexed start of the body of the document
        if (RemarkUtils.isYAML(node.children[0])) {
          documentBodyStartLine = node.children[0].position?.end.line ?? 0;
        }
      }
    });

    // In the second visit, modify the wikilink/ref/candidate that is the
    // backlink to highlight it and to change its node type so that it appears
    // in its text form to the user (we don't want to convert a noteref backlink
    // into its reffed contents for example)
    visit(tree, (node, index, parent) => {
      if (!node.position) {
        return;
      }

      // Remove all elements that fall outside of the context boundary limits
      if (
        node.position.end.line < lowerLineLimit ||
        node.position.start.line > upperLineLimit
      ) {
        if (parent) {
          parent.children.splice(index, 1);
          return index;
        }
      }

      // Make special adjustments for preceding and succeeding code blocks that
      // straddle the context boundaries
      if (node.position && node.position.start.line < lowerLineLimit) {
        if (RemarkUtils.isCode(node)) {
          const lines = node.value.split("\n");
          node.value = lines
            .slice(
              Math.max(0, lowerLineLimit - node.position.start.line - 2), // Adjust an offset to account for the code block ``` lines
              lines.length - 1
            )
            .join("\n");
        }
      } else if (node.position && node.position.end.line > upperLineLimit) {
        if (RemarkUtils.isCode(node)) {
          const lines = node.value.split("\n");
          node.value = lines
            .slice(
              0,
              upperLineLimit - node.position.end.line + 1 // Adjust an offset of 1 to account for the code block ``` line
            )
            .join("\n");
        }
      }

      // Do the node replacement for wikilinks, node refs, and text blocks when
      // it's a candidate link
      if (RemarkUtils.isWikiLink(node)) {
        if (
          backlinkLineNumber === node.position?.start.line &&
          node.position.start.column === _opts.location.start.column
        ) {
          let wiklinkText = `${node.value}`;

          if (node.data.anchorHeader) {
            wiklinkText += `#${node.data.anchorHeader}`;
          }

          (node as Node).type = DendronASTTypes.HTML;
          (node as unknown as HTML).value = getHTMLToHighlightText(
            `[[${wiklinkText}]]`
          );
        }
      } else if (RemarkUtils.isNoteRefV2(node)) {
        if (
          backlinkLineNumber === node.position?.start.line &&
          node.position.start.column === _opts.location.start.column
        ) {
          let noteRefText = `${node.value}`;

          if (node.data.link.data.anchorStart) {
            noteRefText += `#${node.data.link.data.anchorStart}`;
          }

          if (node.data.link.data.anchorEnd) {
            noteRefText += `:#${node.data.link.data.anchorEnd}`;
          }

          (node as Node).type = DendronASTTypes.HTML;
          (node as unknown as HTML).value = getHTMLToHighlightText(
            `![[${noteRefText}]]`
          );
        }
      } else if (RemarkUtils.isText(node)) {
        // If the backlink location falls within the range of this text node,
        // then proceed with formatting. Note: a text node can span multiple
        // lines if it ends with a '\n'
        if (
          backlinkLineNumber === node.position?.start.line &&
          (node.position.end.column > _opts.location.start.column ||
            node.position.end.line > _opts.location.start.line) &&
          (node.position.start.column < _opts.location.end.column ||
            node.position.start.line < _opts.location.end.line)
        ) {
          const contents = node.value;
          const prefix = contents.substring(0, _opts.location.start.column - 1);

          const candidate = contents.substring(
            _opts.location.start.column - 1,
            _opts.location.end.column - 1
          );
          const suffix = contents.substring(
            _opts.location.end.column - 1,
            contents.length
          );

          (node as Node).type = DendronASTTypes.HTML;
          (node as unknown as HTML).value = `${prefix}${getHTMLToHighlightText(
            candidate
          )}${suffix}`;

          return index;
        }
      } else if (RemarkUtils.isHashTag(node) || RemarkUtils.isUserTag(node)) {
        if (
          backlinkLineNumber === node.position?.start.line &&
          node.position.start.column === _opts.location.start.column
        ) {
          (node as Node).type = DendronASTTypes.HTML;
          (node as unknown as HTML).value = getHTMLToHighlightText(node.value);
        }
      }
      return;
    });

    // In the third visit, add the contextual line marker information
    visit(tree, [DendronASTTypes.ROOT], (node, _index, _parent) => {
      if (!RemarkUtils.isRoot(node) || !node.position) {
        return;
      }

      const lowerBoundText =
        lowerLineLimit <= documentBodyStartLine
          ? "Start of Note"
          : `Line ${lowerLineLimit - 1}`;

      const lowerBoundParagraph: Paragraph = {
        type: DendronASTTypes.PARAGRAPH,
        children: [
          {
            type: DendronASTTypes.HTML,
            value: `--- <i>${lowerBoundText}</i> ---`,
          },
        ],
      };

      node.children.unshift(lowerBoundParagraph);

      const upperBoundText =
        upperLineLimit >= documentEndLine
          ? "End of Note"
          : `Line ${upperLineLimit + 1}`;

      const upperBoundParagraph: Paragraph = {
        type: DendronASTTypes.PARAGRAPH,
        children: [
          {
            type: DendronASTTypes.HTML,
            value: `--- <i>${upperBoundText}</i> ---`,
          },
        ],
      };

      node.children.push(upperBoundParagraph);
    });
  }
  return transformer;
}