# Some pointers in creating a casing plugin

**URL:** https://discuss.prosemirror.net/t/some-pointers-in-creating-a-casing-plugin/2805
**Category:** Uncategorized
**Created:** [May 24, 2020, 10:30pm UTC](https://discuss.prosemirror.net/t/some-pointers-in-creating-a-casing-plugin/2805 "2020-05-24T22:30:55Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![samuelgoldenbaum](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/samuelgoldenbaum/32/1491_2.png) [@samuelgoldenbaum](https://discuss.prosemirror.net/u/samuelgoldenbaum)
#### Post date: [May 24, 2020, 10:30pm UTC](https://discuss.prosemirror.net/t/some-pointers-in-creating-a-casing-plugin/2805/1 "2020-05-24T22:30:55Z")

</div>

A little lost in ProseMirror at the moment and trying to understand the correct way to replace the text of the current selection. I want to be able to toggle the case between lower/upper case.

`state.selection.content();` will return slice of the relevant nodes under the selection, and the content that is selected, but does not return the range within each node that would need to replaced.

I assume I would need to create a new text node to replace the range within each selected node something like:

```auto
const updatedText = node.textContent.toUpperCase();
const textNode = state.schema.text(updatedText);
transaction = transaction.replaceWith(startPos, startPos + node.nodeSize, textNode);

```

How do I get the range to replace within each node?

Unfortunately, I cannot find a suitable example or existing plugin to review. Very good chance I am approaching this incorrectly too!

---

<div class="post-metadata">

### Author: ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)
#### Post date: [May 25, 2020, 7:13am UTC](https://discuss.prosemirror.net/t/some-pointers-in-creating-a-casing-plugin/2805/2 "2020-05-25T07:13:10Z")

</div>

I think calling [`nodesBetween`](https://prosemirror.net/docs/ref/#model.Node.nodesBetween) on the selected range and replacing each text node you find with an uppercased variant (keeping marks per node, so something like `schema.text(textNode.text.toUpperCase(), textNode.marks)`) would be the most straightforward way to do this.

---

<div class="post-metadata">

### Author: ![samuelgoldenbaum](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/samuelgoldenbaum/32/1491_2.png) [@samuelgoldenbaum](https://discuss.prosemirror.net/u/samuelgoldenbaum)
#### Post date: [June 3, 2020, 9:07pm UTC](https://discuss.prosemirror.net/t/some-pointers-in-creating-a-casing-plugin/2805/3 "2020-06-03T21:07:38Z")

</div>

Possibly better ways to do this, but sharing the following for future users:

```
const execute = (casing, state, dispatch) => {
    // grab the current transaction and selection
    let tr = state.tr;
    const selection = tr.selection;

    // check we will actually need a to dispatch transaction
    let shouldUpdate = false;

    state.doc.nodesBetween(selection.from, selection.to, (node, position) => {
        // we only processing text, must be a selection
        if (!node.isTextblock || selection.from === selection.to) return;

        // calculate the section to replace
        const startPosition = Math.max(position + 1, selection.from);
        const endPosition = Math.min(position + node.nodeSize, selection.to);

        // grab the content
        const substringFrom = Math.max(0, selection.from - position - 1);
        const substringTo = Math.max(0, selection.to - position - 1);
        const updatedText = node.textContent.substring(substringFrom, substringTo);

        // set the casing
        const textNode = (casing === 'uppercase')
            ? state.schema.text(updatedText.toUpperCase(), node.marks)
            : state.schema.text(updatedText.toLocaleLowerCase(), node.marks);

        // replace
        tr = tr.replaceWith(startPosition, endPosition, textNode);
        shouldUpdate = true;
    });

    if (dispatch && shouldUpdate) {
        dispatch(tr.scrollIntoView());
    }
}
```
