• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

marekdedic / prosemirror-remark / 14286065237

05 Apr 2025 09:44PM UTC coverage: 86.92% (+5.3%) from 81.604%
14286065237

push

github

web-flow
Merge pull request #509 from marekdedic/vitest

Switched to vitest

197 of 211 branches covered (93.36%)

1442 of 1659 relevant lines covered (86.92%)

9.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.04
/src/syntax-extensions/CodeBlockExtension.ts
1
import type { Code, Text } from "mdast";
2
import type {
3
  DOMOutputSpec,
4
  NodeSpec,
5
  Node as ProseMirrorNode,
6
  Schema,
7
} from "prosemirror-model";
8

9
import { setBlockType } from "prosemirror-commands";
1✔
10
import { type InputRule, textblockTypeInputRule } from "prosemirror-inputrules";
1✔
11
import {
1✔
12
  type Command,
13
  type EditorState,
14
  Selection,
15
  type Transaction,
16
} from "prosemirror-state";
17
import {
1✔
18
  createProseMirrorNode,
19
  type Extension,
20
  NodeExtension,
21
} from "prosemirror-unified";
22

23
import { TextExtension } from "./TextExtension";
1✔
24

25
/**
26
 * @public
27
 */
28
export class CodeBlockExtension extends NodeExtension<Code> {
1✔
29
  private static liftOutOfCodeBlock() {
1✔
30
    return (
5✔
31
      state: EditorState,
×
32
      dispatch?: (tr: Transaction) => void,
×
33
    ): boolean => {
×
34
      const { $from, $to } = state.selection;
×
35
      if (
×
36
        // Mustn't be a complex selection
37
        !$from.sameParent($to) ||
×
38
        // Must be in a code block
39
        $from.parent.type.name !== "code_block" ||
×
40
        // Must be at the end of the code block
41
        $from.parentOffset !== $from.parent.content.size ||
×
42
        // There must already be a preceding empty line
43
        !$from.parent.textBetween(0, $from.parentOffset).endsWith("\n\n")
×
44
      ) {
×
45
        return false;
×
46
      }
×
47
      if (dispatch) {
×
48
        const tr = state.tr;
×
49
        dispatch(
×
50
          tr
×
51
            // Delete the preceding empty line
52
            .deleteRange($from.pos - 2, $from.pos)
×
53
            // Insert empty paragraph
54
            .insert(
×
55
              $from.pos - 1,
×
56
              tr.doc.type.schema.nodes["paragraph"].create(),
×
57
            )
×
58
            // Put the cursor into the empty paragraph
59
            .setSelection(Selection.near(tr.doc.resolve($from.pos), 1))
×
60
            .scrollIntoView(),
×
61
        );
×
62
      }
×
63
      return true;
×
64
    };
×
65
  }
5✔
66

67
  public override dependencies(): Array<Extension> {
1✔
68
    return [new TextExtension()];
5✔
69
  }
5✔
70

71
  public override proseMirrorInputRules(
1✔
72
    proseMirrorSchema: Schema<string, string>,
5✔
73
  ): Array<InputRule> {
5✔
74
    return [
5✔
75
      textblockTypeInputRule(
5✔
76
        /^\s{0,3}```$/u,
5✔
77
        proseMirrorSchema.nodes[this.proseMirrorNodeName()],
5✔
78
      ),
5✔
79
      textblockTypeInputRule(
5✔
80
        /^\s{4}$/u,
5✔
81
        proseMirrorSchema.nodes[this.proseMirrorNodeName()],
5✔
82
      ),
5✔
83
    ];
5✔
84
  }
5✔
85

86
  public override proseMirrorKeymap(
1✔
87
    proseMirrorSchema: Schema<string, string>,
5✔
88
  ): Record<string, Command> {
5✔
89
    return {
5✔
90
      Enter: CodeBlockExtension.liftOutOfCodeBlock(),
5✔
91
      "Shift-Mod-\\": setBlockType(
5✔
92
        proseMirrorSchema.nodes[this.proseMirrorNodeName()],
5✔
93
      ),
5✔
94
    };
5✔
95
  }
5✔
96

97
  public override proseMirrorNodeName(): string {
1✔
98
    return "code_block";
126✔
99
  }
126✔
100

101
  public override proseMirrorNodeSpec(): NodeSpec {
1✔
102
    return {
5✔
103
      code: true,
5✔
104
      content: "text*",
5✔
105
      defining: true,
5✔
106
      group: "block",
5✔
107
      marks: "",
5✔
108
      parseDOM: [{ preserveWhitespace: "full", tag: "pre" }],
5✔
109
      toDOM(): DOMOutputSpec {
5✔
110
        return ["pre", ["code", 0]];
3✔
111
      },
3✔
112
    };
5✔
113
  }
5✔
114

115
  public override proseMirrorNodeToUnistNodes(
1✔
116
    _node: ProseMirrorNode,
7✔
117
    convertedChildren: Array<Text>,
7✔
118
  ): Array<Code> {
7✔
119
    return [
7✔
120
      {
7✔
121
        type: this.unistNodeName(),
7✔
122
        value: convertedChildren.map((child) => child.value).join(""),
7✔
123
      },
7✔
124
    ];
7✔
125
  }
7✔
126

127
  public override unistNodeName(): "code" {
1✔
128
    return "code";
53✔
129
  }
53✔
130

131
  public override unistNodeToProseMirrorNodes(
1✔
132
    node: Code,
5✔
133
    proseMirrorSchema: Schema<string, string>,
5✔
134
  ): Array<ProseMirrorNode> {
5✔
135
    return createProseMirrorNode(
5✔
136
      this.proseMirrorNodeName(),
5✔
137
      proseMirrorSchema,
5✔
138
      [proseMirrorSchema.text(node.value)],
5✔
139
    );
5✔
140
  }
5✔
141
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc