ptrharmonic / Datacamp Select Next Underscore

// ==UserScript==
// @name     Datacamp Select Next Underscore
// @version  1.2
// @grant    unsafeWindow
// @include  https://campus.datacamp.com/courses/*
// @license MIT
// @updateURL https://openuserjs.org/meta/ptrharmonic/Datacamp_Select_Next_Underscore.meta.js
// @downloadURL https://openuserjs.org/install/ptrharmonic/Datacamp_Select_Next_Underscore.user.js
// @copyright 2023, ptrharmonic (https://openuserjs.org/users/ptrharmonic)
// ==/UserScript==


// Adapted from here https://stackoverflow.com/a/39165137
function FindReact(dom, traverseUp = 0) {
  	const key = Object.keys(dom.wrappedJSObject).find(key=>{
        return key.startsWith("__reactFiber$") // react 17+
            || key.startsWith("__reactInternalInstance$"); // react <17
    });
    const domFiber = dom.wrappedJSObject[key];
    if (domFiber == null) return null;
  
    // react <16
    if (domFiber._currentElement) {
        let compFiber = domFiber._currentElement._owner;
        for (let i = 0; i < traverseUp; i++) {
            compFiber = compFiber._currentElement._owner;
        }
        return compFiber._instance;
    }

    // react 16+
    const GetCompFiber = fiber=>{
        //return fiber._debugOwner; // this also works, but is __DEV__ only
        let parentFiber = fiber.return;
        while (typeof parentFiber.type == "string") {
            parentFiber = parentFiber.return;
        }
        return parentFiber;
    };
    let compFiber = GetCompFiber(domFiber);
    for (let i = 0; i < traverseUp; i++) {
        compFiber = GetCompFiber(compFiber);
    }
    return compFiber.stateNode;
}

let editor = null;
const getEditor = () => {
  if (editor != null) {
//   	console.log(editor.getDomNode());
    if (editor.getDomNode() != null) {
      return;
    }
  }
  
	let elem = document.getElementsByClassName("react-monaco-editor-container")[0];
  editor = FindReact(elem).editor;
//   console.log(editor);
  let currentPos = editor.getPosition();
  let text = editor.getModel().getLinesContent()
  
  
  console.log(currentPos, text);
}

const getNextUnderscore = () => {
	let currentPos = editor.getPosition();
  let text = editor.getModel().getLinesContent();
  let startLine = currentPos.lineNumber - 1;
  for (let i=startLine;i<text.length;i++) {
  	if (i == startLine) {
    	let index = text[i].indexOf("____", currentPos.column - 1);
      if (index != -1) {
      	return new unsafeWindow.monaco.Position(i + 1, index + 1);
      }
    } else {
    	let index = text[i].indexOf("____");
      if (index != -1) {
      	return new unsafeWindow.monaco.Position(i + 1, index + 1);
      }
    }
  }
  return null;
}

function reverseString(str) {
    return str.split("").reverse().join("");
}

const getPrevUnderscore = () => {
	let currentPos = editor.getPosition();
  let text = editor.getModel().getLinesContent();
  let startLine = currentPos.lineNumber - 1;
  for (let i=startLine;i>0;i--) {
    // reverse the string so we can search in the opposite direction
    let line = reverseString(text[i]);    
  	if (i == startLine) {
      // If the cursor is to the left of some underlines, skip it
      let add = 0;
      let subst = text[i].substring(currentPos.column - 1 - 4, currentPos.column - 1);
      console.log(subst, subst === "____");
      if (subst === "____") {
      	add = 4;
      }
      
    	let index = line.indexOf("____", line.length - currentPos.column + add);
      if (index != -1) {
      	return new unsafeWindow.monaco.Position(i + 1, line.length - index + 1 - 4);
      }
    } else {
    	let index = line.indexOf("____");
      if (index != -1) {
      	return new unsafeWindow.monaco.Position(i + 1, line.length - index + 1 - 4);
      }
    }
  }
  return null;
}




const next = (prev) => {
  getEditor();
	let nextPos;
  
  if (prev) {
//     console.log("prev");
    nextPos = getPrevUnderscore();
  } else {
    nextPos = getNextUnderscore();
  }
//   editor.setPosition(nextPos);
  editor.setSelection(new unsafeWindow.monaco.Range(nextPos.lineNumber, nextPos.column, nextPos.lineNumber, nextPos.column + 4));
}

window.addEventListener("keydown", (event) => {
  let e = event;
//   e.preventDefault();
//   console.log(event, event.key, e.ctrlKey);
	if (e.key.toLowerCase() == "u" && e.ctrlKey) {
//   	console.log("next!")
    e.preventDefault();
    next(e.shiftKey);
  }
});