foldNodeProp not being applied(?)

i’ve created a custom parser that’s kinda-sorta based on @lezer/markdown (which you can find here if you’re interested and if you can excuse my colourful commit messages).

what i’m trying to do is add a foldNodeProp to certain syntax nodes; first by defining my node types…

let nodeTypes = [NodeType.none];
for (let i = 1, name; (name = Type[i]); i++) {
	let properties = [];
	if (name === "Scene") {
		properties.push(
			foldNodeProp.add({
				Scene: foldInside
			}))
	}
	nodeTypes[i] = NodeType.define({
		id: i,
		name,
		props: properties,
	});
}

… and then finally attaching them to the parser like this:

const parser = new FountainParser(
	new NodeSet(nodeTypes),
	Object.keys(DefaultBlockParsers).map((n) => DefaultBlockParsers[n]),
	Object.keys(DefaultBlockParsers).map((n) => DefaultLeafBlocks[n]),
	Object.keys(DefaultBlockParsers),
	DefaultEndLeaf,
	DefaultSkipMarkup,
	Object.keys(DefaultInline).map((n) => DefaultInline[n]),
	Object.keys(DefaultInline),
	[]
);
const data = defineLanguageFacet({ BoneYard: { open: "/*", close: "*/" } });

export function ftn(exts?: Extension[]) {
	return new LanguageSupport(new Language(data, parser, exts));
}

(Type is an enum containing the names of my node types.)

when i searched through the forum earlier, i found this – foldNodeProp does not seem to work for ATX heading

foldNodeProp is used to make the extent of certain syntax nodes foldable. Since a heading node only spans the actual heading, if you’re expecting this to fold that heading plus any content below it, that won’t work.

The folding system will only call this for nodes that span across the end of the line that’s being folded.

however, i’ve made sure that the nodes i’m trying to fold span across/include newlines.

could i be doing something wrong?

At a glance, that looks correct. Have you verified whether the prop exists on the node type (sceneNodeType.prop(foldInside))? And is the shape of the Scene node such that it has an opening and closing delimiting child node at its start and end, so that foldInside actually works on it?

i just tried that – is it supposed to return undefined?

i’m pretty sure it is (see below).

lezer-tree-visualizer output
Screenplay:
├╴TitlePage:
│ ├╴TitlePageField: Title: Out of Phase
│ ├╴TitlePageField: Author: The Tablet ❀
│ ├╴TitlePageField: Source: a reanimation/reinterpretation of Diamond Head's Out of Phase music video
│ └╴TitlePageField: Copyright: 2022
├╴TitlePage: ---
├╴Scene:
│ ├╴SceneHeading:
│ │ └╴SceneNumber: #1-1#
│ ├╴Synopsis: = establishing shot
│ ├╴Lyrics: ~You never listen in to what they know
│ └╴Transition: FADE TO:
├╴Scene:
│ ├╴SceneHeading:
│ │ └╴SceneNumber: #1-2#
│ ├╴Lyrics: ~they only play the songs you've always known
│ └╴Action: merchants and guards mill around. Brian hurriedly weaves his way around the people to the front of the shot. he has the “magical box of rock n roll” tucked under his arm.
├╴Scene:
│ ├╴SceneHeading:
│ │ └╴SceneNumber: #1-3#
│ ├╴Lyrics: ~can't replace the roll of ‘69
│ ├╴Note:
│ │ ├╴OpenNote: [[
│ │ └╴CloseNote: ]]
│ ├╴Action: Sean is visibly bored and disinterested:
│ ├╴Action: • he's leaning forward on the table with his elbow
│ ├╴Action:     • head propped up on his hand
│ ├╴Action: • his eyes are drooping closed
│ ├╴Action: Sean's eyes roam right -
│ ├╴Action: - then left.
│ ├╴Lyrics: ~as you are —
│ ├╴Action: he fully turns his head, upon seeing Brian amongst the crowd.
│ ├╴Action: his cheeks turn pink.
│ └╴Transition: JUMP CUT TO:
├╴Scene:
│ ├╴SceneHeading:
│ │ └╴SceneNumber: #1-4#
│ ├╴Note:
│ │ ├╴OpenNote: [[
│ │ └╴CloseNote: ]]
│ ├╴Lyrics: ~— and always were in love that time
│ ├╴Action: his eyes catch Sean staring, and he pauses.
│ ├╴Action: he turns to Sean and smiles.
│ ├╴Transition: JUMP CUT TO:
│ ├╴Note:
│ │ ├╴OpenNote: [[
│ │ └╴CloseNote: ]]
│ ├╴Action: Sean looks away and blushes even redder
│ ├╴Transition: JUMP CUT TO:
│ ├╴Note:
│ │ ├╴OpenNote: [[
│ │ └╴CloseNote: ]]
│ ├╴Action: Brian smirks devilishly.
│ ├╴Action: he turns his head forward and approaches the table with a slight swagger.
│ ├╴Transition: JUMP CUT TO:
│ ├╴Note:
│ │ ├╴OpenNote: [[
│ │ └╴CloseNote: ]]
│ ├╴Lyrics: ~You're unsure of the marriage vow
│ ├╴Action: the king looks up from a scroll and cocks his brow.
│ ├╴Action: at the top of the scroll is a heading that says "PROCLAMATION".
│ ├╴Transition: JUMP CUT TO:
│ ├╴Note:
│ │ ├╴OpenNote: [[
│ │ └╴CloseNote: ]]
│ ├╴Lyrics: ~Lived it once and in it hide you now
│ ├╴Action: Brian is now in front of the table, facing the king.
│ ├╴Synopsis: = the king is a shadow (something something composition)
│ ├╴Action: Brian places the box on the table
│ ├╴Transition: > MOTION BLUR (↑)
│ ├╴Lyrics: ~the morning ash, the fire of ‘59
│ ├╴Action: Brian animatedly explains what's in the box, gesticulating especially with his hands.
│ ├╴Synopsis: = he exudes an aura of confidence — show that in body language!
│ ├╴Transition: > MOTION BLUR (↓)
│ ├╴Action: close up of box...
│ ├╴Lyrics: ~Will remain with you,
│ ├╴Action: Brian opens the box
│ └╴Lyrics: ~In hope for better times...
├╴Scene:
│ ├╴SceneHeading:
│ │ └╴SceneNumber: #2-1#
│ ├╴Lyrics: ~ (out of phase)
│ ├╴Lyrics: ~ will you always be!
│ ├╴Synopsis: ===
│ ├╴Synopsis: = ...bridge...
│ ├╴Note:
│ │ ├╴OpenNote: [[
│ │ └╴CloseNote: ]]
│ ├╴Lyrics: ~ looking around me
│ └╴Transition: > MOTION BLUR (↑)
└╴Scene:
  ├╴SceneHeading:
  │ └╴SceneNumber: #3-1#
  ├╴Note:
  │ ├╴OpenNote: [[
  │ └╴CloseNote: ]]
  ├╴Lyrics: ~ you're not the only one
  ├╴Action: the king slams the box shut.
  ├╴Action: his face is contorted in a disgusted/angry grimace.
  ├╴DialogueBlock:
  │ ├╴Character: THE KING
  │ ├╴Parenthetical: (soundlessly)
  │ └╴Dialogue:
  │   └╴Underline:
  │     ├╴UnderlineMark: _
  │     ├╴Italic:
  │     │ ├╴ItalicMark: *
  │     │ └╴ItalicMark: *
  │     └╴UnderlineMark: _
  ├╴Transition: JUMP CUT TO:
  ├╴Note:
  │ ├╴OpenNote: [[
  │ └╴CloseNote: ]]
  ├╴Lyrics: ~ looking for something old
  ├╴Action: guards close in around Brian
  ├╴Action: Brian turns around partially
  ├╴Note:
  │ ├╴OpenNote: [[
  │ └╴CloseNote: ]]
  ├╴Lyrics: ~ looking for something done
  ├╴Transition: JUMP CUT TO:
  ├╴Note:
  │ ├╴OpenNote: [[
  │ └╴CloseNote: ]]
  ├╴Action: Brian whips out his sword
  ├╴Transition: JUMP CUT TO:
  ├╴Note:
  │ ├╴OpenNote: [[
  │ └╴CloseNote: ]]
  ├╴Lyrics: ~out of phase,
  ├╴Lyrics: ~out of phase,
  ├╴Lyrics: ~out of phase,
  ├╴Lyrics: ~out of phase...
  └╴Sequence: ## fight scene.

as well, i’ve tried passing my own function to foldNodeProp.add(), but nothing shows up in the fold gutter.

foldNodeProp.add((type) => {
	if(!type.is("Scene")) return null
		return (node, state) => {
			let ret = {
				from: node.from,
				to: node.to,
			};
			return ret;
	};
});

No. But I gave the wrong code, I meant sceneNodeType.prop(foldNodeProp).

image

does this return value look right?

Yes. Is your function being called? Does it actually return a range that crosses the end of the line?

you’re not going to believe this but… after changing how i passed the language support extension to EditorState.create(), folding worked.

originally i was doing this:

const exts = [
	theme, 
	ftn().support, 
	inlinePlugin(), 
	EditorView.lineWrapping,
]
this.state = EditorState.create({
	extensions: exts
})

changing exts to

const exts = [
	theme, 
	ftn().extension, 
	inlinePlugin(), 
	EditorView.lineWrapping,
	...basicSetup
]

made things work! :exploding_head:

thank you for your time and help, @marijn, it’s much appreciated :smiley:

Just passing ftn() should also work.