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

marekdedic / prosemirror-remark / 14286002573

05 Apr 2025 09:34PM UTC coverage: 73.113% (-8.5%) from 81.604%
14286002573

Pull #509

github

marekdedic
Migrated syntax extension tests to vitest
Pull Request #509: Switched to vitest

54 of 125 branches covered (43.2%)

310 of 424 relevant lines covered (73.11%)

14.41 hits per line

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

66.67
/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";
4✔
10
import { type InputRule, textblockTypeInputRule } from "prosemirror-inputrules";
4✔
11
import {
4✔
12
  type Command,
13
  type EditorState,
14
  Selection,
15
  type Transaction,
16
} from "prosemirror-state";
17
import {
4✔
18
  createProseMirrorNode,
19
  type Extension,
20
  NodeExtension,
21
} from "prosemirror-unified";
22

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

25
/**
26
 * @public
27
 */
28
export class CodeBlockExtension extends NodeExtension<Code> {
4✔
29
  private static liftOutOfCodeBlock() {
30
    return (
8✔
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
  }
66

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

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

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

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

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

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

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

131
  public override unistNodeToProseMirrorNodes(
132
    node: Code,
133
    proseMirrorSchema: Schema<string, string>,
134
  ): Array<ProseMirrorNode> {
135
    return createProseMirrorNode(
4✔
136
      this.proseMirrorNodeName(),
137
      proseMirrorSchema,
138
      [proseMirrorSchema.text(node.value)],
139
    );
140
  }
141
}
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

© 2026 Coveralls, Inc