How to get and replace the word on the left of the cursor

497 Views Asked by At

For a word add-ins in javascript, a simple use case is to get the word on the left of the cursor and to replace it in upper case.

For example, if | is the cursor:

  • Hello world| will become Hello WORLD|
  • Hello| world will become HELLO| world

Is it possible to perform this example with the Word.Range class? For example, to expand the range until a space like this fictive code:

    Word.run(function (context) {
        var selection = context.document.getSelection();
        var cursor = selection.getRange('Start');

        // Fictive: how to expand the range to the left until a space?
        var range = cursor.expandToLeftUntil(' ');
        range.load("text");
        var html = range.getHtml();
        await context.sync();
        var textToReplace = html.value.toUpperCase();


        // Replace the text
        range.insertText(textToReplace, 'Replace');
        await context.sync();
    });

Or is there any other solution?

2

There are 2 best solutions below

0
On

A possible strategy is to use the search method to get a RangeCollection of all the words in the document (or body or paragraph, etc.). Then get a reference to the current selected range (where the cursor is). Then loop through the collection and call the Range.compareLocationWith method to find the range that is "AdjacentBefore" the currently selected range.

0
On

I was trying to do a similar thing. At least, when a selection is empty get the word nearby the cursor. I hope their would be some API function, but that's not the case.

I started out with the answer/idea of Rick Kirkham (thanks!). I couldn't get to search method to work to get a list of words. Using split on a space worked fine though.

Instead of select like I do you could modify the the text. If you don't want to get close-by but only after you should alter the function to check 'InsideStart' (in that scenario you would like to go to the previous word, so i-1).

Word.run(async (context) => {

    let cursorOrSelection = context.document.getSelection();
    cursorOrSelection.load();
    
    await context.sync();

    // if the cursor is empty we make a selection of the Word close-by
    // this behaviour is done automatically when you insert a comment in Word
    if (cursorOrSelection.isEmpty) {
        
        console.log("Empty selection, cursor.");
        
        // get the paragraph closest to the cursor. 
        const paragraph = cursorOrSelection.paragraphs.getFirst(); 
        const allWordsInParagraph = paragraph.split([" "], true /* trimDelimiters*/, true /* trimSpaces */);
        
        allWordsInParagraph.load();
        
        await context.sync();

        // compare the cursorRange with the ranges of individual words in the paragraph. 
        let compareRanges = [];
        
        allWordsInParagraph.items.forEach( item => {
            compareRanges.push({
                compare: cursorOrSelection.compareLocationWith(item),
                range: item
            });
        });

        await context.sync();

        // walk through all the words and compare the location relation with the cursor
        // were the location relation changes, the word is near the cursor.

        let previousLocationRelation = null;
        let wordClosestToCursorRange = null;

        for (let i = 0; i < compareRanges.length; i++) {
            const locationRelation = compareRanges[i].compare.value;
            
            console.log(locationRelation);

            // if first entry is Before, we are at the beginning
            if(i==0 && locationRelation === 'Before') {
                wordClosestToCursorRange = compareRanges[i].range;

                // jump out
                break;
            } 
            else {

                if(previousLocationRelation && locationRelation != previousLocationRelation) {
                    // first "edge" we find. 
                    // console.log('-- edge');
                    
                    // if first edge we encounter is Before
                    // we need the previous one (could be after)
                    if(locationRelation === 'Before') {
                        wordClosestToCursorRange = compareRanges[i-1].range;
                    }
                    else {
                        // we are inside the word or end of the word
                        // Inside, InsideStart, InsideEnd
                        wordClosestToCursorRange = compareRanges[i].range;
                    }

                    // jump out we are only interested in the first edge
                    break;
                }

            }

            previousLocationRelation = locationRelation;
            
        }

        wordClosestToCursorRange.select();
    }

    return context.sync();
})
.catch(function (error) {
    console.log(error.message)
})