Files
penpot/frontend/text-editor/src/editor/content/Text.js

168 lines
3.5 KiB
JavaScript

/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
/**
* Throws if the passed value is not a valid offset value.
*
* @param {*} offset
* @throws {TypeError}
*/
function tryOffset(offset) {
if (!Number.isInteger(offset) || offset < 0)
throw new TypeError("Invalid offset");
}
/**
* Throws if the passed value is not a valid string.
*
* @param {*} str
* @throws {TypeError}
*/
function tryString(str) {
if (typeof str !== "string") throw new TypeError(`Invalid string ${str}`);
}
/**
* Inserts string into a string.
*
* @param {string} str
* @param {number} offset
* @param {string} text
* @returns {string}
*/
export function insertInto(str, offset, text) {
tryString(str);
tryOffset(offset);
tryString(text);
return str.slice(0, offset) + text + str.slice(offset);
}
/**
* Replaces a part of a string with a string.
*
* @param {string} str
* @param {number} startOffset
* @param {number} endOffset
* @param {string} text
* @returns {string}
*/
export function replaceWith(str, startOffset, endOffset, text) {
tryString(str);
tryOffset(startOffset);
tryOffset(endOffset);
tryString(text);
return str.slice(0, startOffset) + text + str.slice(endOffset);
}
/**
* Removes text backward from specified offset.
*
* @param {string} str
* @param {number} offset
* @returns {string}
*/
export function removeBackward(str, offset) {
tryString(str);
tryOffset(offset);
if (offset === 0) {
return str;
}
return str.slice(0, offset - 1) + str.slice(offset);
}
/**
* Removes text forward from specified offset.
*
* @param {string} str
* @param {number} offset
* @returns {string}
*/
export function removeForward(str, offset) {
tryString(str);
tryOffset(offset);
return str.slice(0, offset) + str.slice(offset + 1);
}
/**
* Removes a slice of text.
*
* @param {string} str
* @param {number} start
* @param {number} end
* @returns {string}
*/
export function removeSlice(str, start, end) {
tryString(str);
tryOffset(start);
tryOffset(end);
return str.slice(0, start) + str.slice(end);
}
/**
* Finds the start of the previous word from the given offset.
* Word boundaries are defined by whitespace and punctuation.
*
* @param {string} str
* @param {number} offset
* @returns {number}
*/
export function findPreviousWordBoundary(str, offset) {
if (str == null) {
return 0;
}
tryString(str);
tryOffset(offset);
if (offset === 0) {
return 0;
}
// Start from the character before the cursor
let pos = offset - 1;
// Skip any whitespace characters
while (pos >= 0 && /\s/.test(str[pos])) {
pos--;
}
// If we're now at a non-word character, skip all non-word characters
if (pos >= 0 && /\W/.test(str[pos]) && !/\s/.test(str[pos])) {
while (pos >= 0 && /\W/.test(str[pos]) && !/\s/.test(str[pos])) {
pos--;
}
}
// Otherwise, skip all word characters
else if (pos >= 0 && /\w/.test(str[pos])) {
while (pos >= 0 && /\w/.test(str[pos])) {
pos--;
}
}
return pos + 1;
}
/**
* Removes a word backward from specified offset.
*
* @param {string} str
* @param {number} offset
* @returns {string}
*/
export function removeWordBackward(str, offset) {
if (str == null) {
return "";
}
tryString(str);
tryOffset(offset);
const wordStart = findPreviousWordBoundary(str, offset);
return str.slice(0, wordStart) + str.slice(offset);
}