bustle / mobiledoc-kit

A toolkit for building WYSIWYG editors with Mobiledoc
https://bustle.github.io/mobiledoc-kit/demo/
MIT License
1.55k stars 156 forks source link

How do I do a find and replace using the API? #517

Closed dannyb closed 7 years ago

dannyb commented 7 years ago

I am attempting to use the API to find a string and then replace it with a string or string with markups (e.g. if the replacement was an tag link).

I have been going through the documentation and the code and can't seem to find how to do this.

Thanks

mixonic commented 7 years ago

Howdy @dannyb. Because documents aren't simply text but are represented as an abstract tree, simply doing a regex over the body of the document isn't possible. The more you can describe your use-case the more helpful we can be, for example the editor provides hooks that can implement functionality such as converting * on its own in to a list item which is a form of you what described but very specialized.

In the language of the Mobiledoc-Kit API, what you're looking to do is discover a range (an object with head and tail position objects), delete that range from the document, then insert a new marker.

Finding "a string of text" is easy as long as you don't plan to cross sections (which I expect would involve some newline stuff). There will be some decisions you need to make: If you replace text, do you mean to keep the markup on it? What if the text you are replacing starts with a markup but that markup isn't applied at the end of the replaced string: Should the replacement have that markup or not?

This simple example would snip text and add new text that has no markup:

let searchString = 'blum';

let range = null;
editor.post.walkMarkerableSections(editor.post.toRange(), section => {
  if (range) { return; /* Skip after range is found */ }
  let index = section.text.indexOf(searchString);
  if (index !== -1) {
    let head = section.toPosition(index);
    let tail = section.toPosition(index + searchString.length);
    range = head.toRange(tail);
  }
});

editor.run(postEditor => {
  let position = postEditor.deleteRange(range);
  postEditor.insertText(position, 'fwowwee');
});

insertText can easily be replaced with any other API that inserts content (for example insertTextWithMarkup). insertMarkers is probably the lowest level API you would need to look at for inserting.

dannyb commented 7 years ago

@mixonic Thanks! Yeah I was having trouble figuring out just how to do the search. The example you gave me makes sense and now I can see a way to do what i'm trying to do. I don't believe the text i'm looking for will span sections. I will go try implementing this and I will let you know. Thanks again for the quick response!

mixonic commented 7 years ago

@dannyb FYI I used a couple APIs there (the Position and Range classes) that really aren't exposed to the public API. I just editing the example to use the public API mechanisms that achieve the same thing (toPosition, toRange).