Getting the "word" under the cursor is really, really complicated.
See original GitHub issueProblem
The problem is that I want to be able to get the word under the cursor (collapsed) and the range of that word within a block element. The problem is that slate’s Editor.blah
functions don’t seem sufficient to do it without some crazy logic.
For my use-case a “word” includes the dash and dot (-
,.
) characters.
I’ll use ‘|’ as cursor location.
If you have ‘hello| world’ and call Editor.after
with the word unit, you’ll get the point after world.
If you have ‘hello world|’ and you call Editor.after
with the word unit, you’ll get the first point in the next block.
The same applies to Editor.after
So to actually get the word under the cursor, this is the logic I have:
// Get start and end, modify it as we move along.
let [start, end] = Range.edges(selection);
// Move forward along until I hit a different tree depth
while (true) {
const after = Editor.after(editor, end, {
unit: 'word',
});
const wordAfter =
after && Editor.string(editor, { anchor: end, focus: after });
if (after && wordAfter && wordAfter.length && wordAfter[0] !== ' ') {
end = after;
if (end.offset === 0) { // Means we've wrapped to beginning of another block
break;
}
} else {
break;
}
}
// Move backwards
while (true) {
const before = Editor.before(editor, start, {
unit: 'word',
});
const wordBefore =
before && Editor.string(editor, { anchor: before, focus: start });
if (
before &&
wordBefore &&
wordBefore.length &&
wordBefore[wordBefore.length - 1] !== ' '
) {
start = before;
if (start.offset === 0) { // Means we've wrapped to beginning of another block
break;
}
} else {
break;
}
}
And then I have my word and range:
const wordRange = { anchor: start, focus: end };
const word = Editor.string(editor, wordRange);
Solution
A solution would be to not include “space” as part of word boundaries. Or someway for me to tell the Editor.before/after
APIs to use the word unit but include specific characters and use other characters as terminations: e.g.
Editor.after(editor, selection.anchor, { unit: 'word', include: '-._', terminateOn: ' ' });
Or to allow { edge: 'end' }
in the options so that it doesn’t pass the end of the block?
Context Here’s a screen shot of a slack thread that has more details:
Issue Analytics
- State:
- Created 2 years ago
- Reactions:8
- Comments:8
Top GitHub Comments
For integration of Slate into my product, I also had to write a ridiculously complicated function to get the current word, and I expect many other people have done so as well. I’ll sure mine too, in case anybody finds it helpful when they tackle this issue. This isEqual below is from lodash.
I ended up writing my own stepper which goes character by character and includes options as to which characters to include.
If anyone is interested, here it is. You may have to adjust typings. Credits to @williamstein for parts of it, but it works a little bit different according to my needs (character steps, instead of word steps). It also allows you to pass in a location instead. To adjust this to match the Transforms API, maybe use an “at” property instead. I would be happy to create a PR with this after modifying it to match the rest of the Transforms API.
Include decides whether to include the terminator. Direction allows you to specify which directions to step in.
I have two use cases for this: Emojis and Mentions. You can see how to use it here:
Mentions:
Emojis: