is there anyway to ignore the special character ‘line break’ (\n) in order for the text to be displayed in one line, but without removing this special character
You want the document to be able to contain line breaks, but not display them? I guess you could write an extension that creates replacing decorations on all newlines to ‘fold’ them.
I am writing an extension that would allow you to make text in one line and back at the touch of a button without losing \n. Is there an example of creating a decoration?
Yes, there is.
I don’t understand how to make it so that I process an unformatted value.
class CharacterWidget extends WidgetType {
value: string
constructor(value: string) {
super()
this.value = value
}
toDOM(): HTMLElement {
const span = document.createElement('span')
span.textContent = 'nn'
span.className = 'cm-specialChar'
return span
}
}
const specailCharacterMatcher = new MatchDecorator({
regexp: /\n/g,
decoration: match => Decoration.replace({
widget: new CharacterWidget(match[0]),
}),
})
export const getExtensions = () => [ViewPlugin.fromClass(
class {
specialChars: DecorationSet
constructor(view: EditorView) {
this.specialChars = specailCharacterMatcher.createDeco(view)
}
update(update: ViewUpdate) {
this.specialChars = specailCharacterMatcher.updateDeco(update, this.specialChars)
}
}, {
decorations: instance => instance.specialChars,
provide: plugin => EditorView.atomicRanges.of(view =>
view.plugin(plugin)?.specialChars || Decoration.none
),
}
)]
MatchDecorator
only works inside lines, so that’s not an abstraction you can use for this. But state.doc
already has the document split by line, so you can easily get the location of the line breaks from there, in order to build up your decoration set.
But the state also contains formatted text. Maybe it is possible to codemirror optionally not create lines by \n?
I wrote such a solution, comirror highlights the special character, but the text is still multi-line
export const getExtensions = () => [ViewPlugin.fromClass(
class {
specialChars: DecorationSet
constructor(view: EditorView) {
this.specialChars = this.compute(view)
}
update(update: ViewUpdate) {
this.specialChars = this.compute(update.view)
}
compute(view: EditorView): DecorationSet {
const widgets: Range<Decoration>[] = []
const deco = Decoration.widget({
widget: new CharacterWidget('test'),
})
const sliceDoc = view.state.sliceDoc().split('')
sliceDoc.forEach((char, index) => {
if (char === '\n')
widgets.push(deco.range(index))
})
return Decoration.set(widgets, true)
}
}, {
decorations: instance => instance.specialChars,
provide: plugin => EditorView.atomicRanges.of(view =>
view.plugin(plugin)?.specialChars || Decoration.none
),
}
)]
You could use lineAt
or a document iterator to go through the lines without first stringifying and splitting the entire document.
But more importantly, you’ll want to create a replace decoration that covers the newline, so going from its start index to the next index, or you’re just putting widgets at the end of each line.
Thank you, sir. I decided my problem by statefield define
But, i not understand, how get position \n from document iterator.
function replaceSpecialChar(state: EditorState) {
const widgets: Range<Decoration>[] = []
const deco = Decoration.widget({
widget: new CharacterWidget(),
})
const sliceDoc = state.sliceDoc().split('')
sliceDoc.forEach((char, index) => {
if (char === '\n')
widgets.push(Decoration.replace(deco).range(index, index + 1))
})
return Decoration.set(widgets, true)
}
export const getExtensions = () => [StateField.define<DecorationSet>({
create(state): DecorationSet {
return replaceSpecialChar(state)
},
update(value, transaction) {
if (transaction.docChanged)
return replaceSpecialChar(transaction.state)
return value.map(transaction.changes)
},
provide(field) {
return EditorView.decorations.from(field)
},
})]