sillyp
April 24, 2021, 2:48pm
1
Hey,
Is it possible to hide some parts of the syntax when a line is not in focus?
For example:
display the string ‘lambda’ as ‘λ’ or
“!=” as “≠”
This is similar to Vim’s conceal feature.
I have had some luck doing this with replace widgets with but I am not able to remove the decoration when the line is in focus. Also, it feels like I’m not approaching it correct.
Any guidance will be greatly appreciated
Thank you so much
1 Like
marijn
April 24, 2021, 3:26pm
2
Replacing widget decorations would be the way to go for this, yes. Make sure you skip the line with the selection head in it, and update (probably a full rebuild of the widgets inside the viewport is fine) when the selection, document, or viewport changes.
sillyp
April 25, 2021, 9:57am
3
Thank you so much it worked!
Instead of skipping the complete line, I am skipping any range that overlaps with the selection range.
For anybody who finds this post, this is how I implemented the feature
class ConcealWidget extends WidgetType {
constructor(readonly symbol: string) {
super()
}
eq(other: ConcealWidget) {
return (other.symbol == this.symbol)
}
toDOM() {
let span = document.createElement("span")
span.className = "cm-concealed-sym" /* Formatting to be taken care of*/
span.textContent = this.symbol
return span;
}
ignoreEvent() {
return false
}
}
function selectionAndRangeOverlap(selection: EditorSelection, rangeFrom:
number, rangeTo: number) {
return (selection.main.from <= rangeTo) &&
(selection.main.to) >= rangeFrom;
}
function conceal(view: EditorView) {
const concealMap = {
"!=": "≠",
"<=": "≤",
">=": "≥",
// and so on...
}
let widgets: any = []
for (let { from, to } of view.visibleRanges) {
syntaxTree(view.state).iterate({
from, to, enter: (type, from, to) => {
const toSkip: Boolean = selectionAndRangeOverlap(
view.state.selection, from, to)
if (
(type.name == "CompareOp" || type.name = "LogicOp")
&& !toSkip
) {
const s: string = view.state.doc.sliceString(from, to)
if ( !concealMap.hasOwnProperty(s) ) {
return
}
widgets.push(Decoration.replace({
widget: new ConcealWidget(),
inclusive: false,
block: false,
}).range(from, to))
}
}
})
}
return Decoration.set(widgets, true)
}
export const concealPlugin = ViewPlugin.fromClass(class {
decorations: DecorationSet
constructor(view: EditorView) {
this.decorations = conceal(view)
}
update(update: ViewUpdate) {
if (update.docChanged || update.viewportChanged || update.selectionSet)
this.decorations = conceal(update.view)
}
}, { decorations: v => v.decorations, })
Will check if the code has any issues but looks good so far
2 Likes
qbane
April 25, 2021, 4:18pm
4
Nice work. A small typo, in concealPlugin
, images
should be conceal
instead.
sillyp
April 25, 2021, 4:40pm
5
Thanks for the catch! I have updated the post
1 Like