virt2/api/soft/CodeMirror/comment/test/test-comment.ts

196 lines
7.0 KiB
TypeScript
Raw Normal View History

import ist from "ist"
import { SelectionRange, EditorState, EditorSelection, Extension, StateCommand } from "@codemirror/next/state"
import { Text } from "@codemirror/next/text"
import { toggleLineComment, getLinesInRange, CommentTokens, toggleBlockComment } from "@codemirror/next/comment"
import { html } from "@codemirror/next/lang-html"
describe("comment", () => {
it("get lines across range", () => {
// 0 1 2 3
// 0123456 7890123 4567890 1234567 8901234 5
let doc = Text.of("Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n\n".split("\n"))
const t = (from: number, to: number, expectedLinesNo: number[]) => {
let lines = getLinesInRange(doc, new SelectionRange(from, to))
ist(lines.map(l => l.start).join(","), expectedLinesNo.join(","))
}
t(0, 0, [0])
t(7, 7, [7])
t(16, 16, [14])
t(0, 35, [0, 7, 14, 21, 28, 35])
t(0, 6, [0])
t(4, 8, [0, 7])
t(3, 17, [0, 7, 14])
})
const defaultConfig: CommentTokens = {line: "//", block: {open: "/*", close: "*/"}}
/// Creates a new `EditorState` using `doc` as the document text.
/// The selection ranges in the returned state can be specified
/// within the `doc` argument:
/// The character `|` is used a marker to indicate both the
/// start and the end of a `SelectionRange`, *e.g.*,
///
/// ```typescript
/// s("line 1\nlin|e 2\nline 3")
/// ```
function s(doc: string, config: CommentTokens = defaultConfig, extensions: readonly Extension[] = []): EditorState {
let markers = [], pos
while ((pos = doc.indexOf("|", 0)) >= 0) {
markers.push(pos)
doc = doc.slice(0, pos) + doc.slice(pos + 1)
}
const ranges: SelectionRange[] = []
if (markers.length == 1) {
ranges.push(new SelectionRange(markers[0]))
} else if (markers.length % 2 != 0) {
throw "Markers for multiple selections need to be even.";
} else {
for (let i = 0; i < markers.length; i += 2)
ranges.push(new SelectionRange(markers[i], markers[i + 1]))
if (ranges.length == 0) ranges.push(new SelectionRange(0))
}
return EditorState.create({
doc,
selection: EditorSelection.create(ranges),
extensions: [EditorState.allowMultipleSelections.of(true),
EditorState.addLanguageData.of({commentTokens: config})].concat(extensions)
})
}
function same(actualState: EditorState, expectedState: EditorState) {
ist(actualState.doc.toString(), expectedState.doc.toString())
ist(JSON.stringify(actualState.selection), JSON.stringify(expectedState.selection))
}
function checkToggleChain(toggle: StateCommand, config: CommentTokens, docs: string[]) {
let state = s(docs[0], config)
for (let i = 1; i <= docs.length; i++) {
toggle({state, dispatch(tr) { state = tr.apply() }})
same(state, s(docs[i == docs.length ? docs.length - 2 : i], config))
}
}
// Runs all tests for the given line-comment token, `k`.
function runLineCommentTests(k: string) {
function check(...docs: string[]) {
checkToggleChain(toggleLineComment, {line: k}, docs)
}
describe(`Line comments ('${k}')`, () => {
it("toggles in an empty single selection", () => {
check(`\nline 1\n ${k} ${k} ${k} ${k}line| 2\nline 3\n`,
`\nline 1\n ${k} ${k} ${k}line| 2\nline 3\n`,
`\nline 1\n ${k} ${k}line| 2\nline 3\n`,
`\nline 1\n ${k}line| 2\nline 3\n`,
`\nline 1\n line| 2\nline 3\n`,
`\nline 1\n ${k} line| 2\nline 3\n`)
check(`\nline 1\n ${k}line 2|\nline 3\n`,
`\nline 1\n line 2|\nline 3\n`,
`\nline 1\n ${k} line 2|\nline 3\n`)
check(`\nline 1\n| ${k}line 2\nline 3\n`,
`\nline 1\n| line 2\nline 3\n`,
`\nline 1\n| ${k} line 2\nline 3\n`)
check(`\nline 1\n|${k}\nline 3\n`,
`\nline 1\n|\nline 3\n`,
`\nline 1\n|${k} \nline 3\n`)
check(`\nline 1\n line 2\nline 3\n|${k}`,
`\nline 1\n line 2\nline 3\n|`,
`\nline 1\n line 2\nline 3\n|${k} `)
})
it("toggles comments in a single line when the cursor is at the beginning", () => {
check(`line 1\n |line 2\nline 3\n`,
`line 1\n |${k} line 2\nline 3\n`)
})
it("toggles comments in a single line selection", () => {
check(`line 1\n ${k}li|ne |2\nline 3\n`,
`line 1\n li|ne |2\nline 3\n`,
`line 1\n ${k} li|ne |2\nline 3\n`)
})
it("toggles comments in a multi-line selection", () => {
check(`\n ${k}lin|e 1\n ${k} line 2\n ${k} line |3\n`,
`\n lin|e 1\n line 2\n line |3\n`,
`\n ${k} lin|e 1\n ${k} line 2\n ${k} line |3\n`)
check(`\n ${k}lin|e 1\n ${k} line 2\n line 3\n ${k} li|ne 4\n`,
`\n ${k} ${k}lin|e 1\n ${k} ${k} line 2\n ${k} line 3\n ${k} ${k} li|ne 4\n`)
check(`\n ${k} lin|e 1\n\n ${k} line |3\n`,
`\n lin|e 1\n\n line |3\n`)
check(`\n ${k} lin|e 1\n \n ${k} line |3\n`,
`\n lin|e 1\n \n line |3\n`)
check(`\n|\n ${k} line 2\n | \n`,
`\n|\n line 2\n | \n`)
check(`\n|\n\n | \n`,
`\n|\n\n | \n`)
})
it("toggles comments in a multi-line multi-range selection", () => {
check(`\n lin|e 1\n line |2\n line 3\n l|ine 4\n line| 5\n`,
`\n ${k} lin|e 1\n ${k} line |2\n line 3\n ${k} l|ine 4\n ${k} line| 5\n`)
})
})
}
/// Runs all tests for the given block-comment tokens.
function runBlockCommentTests(o: string, c: string) {
describe(`Block comments ('${o} ${c}')`, () => {
function check(...docs: string[]) {
checkToggleChain(toggleBlockComment, {block: {open: o, close: c}}, docs)
}
it("toggles block comment in multi-line selection", () => {
check(`\n lin|e 1\n line 2\n line 3\n line |4\n line 5\n`,
`\n lin${o} |e 1\n line 2\n line 3\n line | ${c}4\n line 5\n`)
})
it("toggles block comment in multi-line multi-range selection", () => {
check(`\n lin|e 1\n line |2\n l|ine 3\n line 4\n line |5\n`,
`\n lin${o} |e 1\n line | ${c}2\n l${o} |ine 3\n line 4\n line | ${c}5\n`)
})
it("can toggle comments inside the selection", () => {
check(`|${o} one\ntwo ${c}| three`,
`|one\ntwo| three`,
`${o} |one\ntwo| ${c} three`)
})
})
}
runLineCommentTests("//")
runLineCommentTests("#")
runBlockCommentTests("/*", "*/")
runBlockCommentTests("<!--", "-->")
it("toggles line comment in multi-language doc", () => {
let state = s(`<script>
// This is a |line comment
console.log("Hello");
</script>
<!-- HTML only provides block comments -->`, undefined, [html()])
toggleLineComment({state, dispatch(tr) { state = tr.apply() }})
same(state, s(`<script>
This is a |line comment
console.log("Hello");
</script>
<!-- HTML only provides block comments -->`))
})
})