Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/jupyter/util/cell-utils.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Misc utility functions for manipulating and working wth cells.7*/89import { List, Map } from "immutable";10import { field_cmp, len } from "@cocalc/util/misc";1112export function positions_between(13before_pos: number | undefined,14after_pos: number | undefined,15num: number16) {17// Return an array of num equally spaced positions starting after18// before_pos and ending before after_pos, so19// [before_pos+delta, before_pos+2*delta, ..., after_pos-delta]20// where delta is a function of the endpoints and num.21let delta: number, pos: number;22if (before_pos != null && after_pos != null && before_pos > after_pos) {23[before_pos, after_pos] = [after_pos, before_pos];24}25if (before_pos == null) {26if (after_pos == null) {27pos = 0;28delta = 1;29} else {30pos = after_pos - num;31delta = 1;32}33} else {34if (after_pos == null) {35pos = before_pos + 1;36delta = 1;37} else {38delta = (after_pos - before_pos) / (num + 1);39pos = before_pos + delta;40}41}42const v: number[] = [];43for (44let i = 0, end = num, asc = 0 <= end;45asc ? i < end : i > end;46asc ? i++ : i--47) {48v.push(pos);49pos += delta;50}51return v;52}5354export function sorted_cell_list(cells: Map<string, any>): List<string> {55// Given an immutable Map from id's to cells, returns an immutable List whose56// entries are the id's in the correct order, as defined by the pos field (a float).57if (cells == null) {58return List([]);59}60return cells61.map((record, id) => ({ id, pos: record.get("pos", -1) }))62.filter((x) => x.id != null)63.sort(field_cmp("pos"))64.map((x) => x.id)65.toList();66}6768export function ensure_positions_are_unique(cells?: Map<string, any>) {69// Verify that pos's of cells are distinct. If not70// return map from id's to new unique positions.71if (cells == null) {72return;73}74const v: any = {};75let all_unique = true;76cells.forEach((cell) => {77const pos = cell.get("pos");78if (pos == null || v[pos]) {79// dup! (or not defined)80all_unique = false;81return false;82}83v[pos] = true;84});85if (all_unique) {86return;87}88let pos = 0;89const new_pos: { [id: string]: number } = {};90sorted_cell_list(cells).forEach((id) => {91new_pos[id] = pos;92pos += 1;93});94return new_pos;95}9697export function new_cell_pos(98cells: Map<string, any>,99cell_list: List<string>,100cur_id: string,101delta: -1 | 1102): number {103/*104Returns pos for a new cell whose position105is relative to the cell with cur_id.106107cells = immutable map id --> pos108cell_list = immutable sorted list of id's (derived from cells)109cur_id = one of the ids110delta = -1 (above) or +1 (below)111112Returned undefined whenever don't really know what to do; then caller113just makes up a pos, and it'll get sorted out.114*/115let cell_list_0: List<string>;116if (cell_list == null) {117cell_list_0 = sorted_cell_list(cells)!;118} else {119cell_list_0 = cell_list;120}121let adjacent_id: string | undefined;122cell_list_0.forEach((id, i) => {123if (id === cur_id) {124const j = i + delta;125if (j >= 0 && j < cell_list_0.size) {126adjacent_id = cell_list_0.get(j);127}128return false; // break iteration129}130});131const adjacent_pos = cells.getIn([adjacent_id, "pos"]) as number | undefined;132const current_pos = cells.getIn([cur_id, "pos"]) as number;133let pos: number;134if (adjacent_pos != null) {135// there is a cell after (or before) cur_id cell136pos = (adjacent_pos + current_pos) / 2;137} else {138// no cell after (or before)139pos = current_pos + delta;140}141return pos;142}143144export function move_selected_cells(145v?: string[],146selected?: { [id: string]: true },147delta?: number148) {149/*150- v = ordered js array of all cell id's151- selected = js map from ids to true152- delta = integer153154Returns new ordered js array of all cell id's or undefined if nothing to do.155*/156if (v == null || selected == null || !delta || len(selected) === 0) {157return; // nothing to do158}159const w: string[] = [];160// put selected cells in their proper new positions161for (let i = 0; i < v.length; i++) {162if (selected[v[i]]) {163const n = i + delta;164if (n < 0 || n >= v.length) {165// would move cells out of document, so nothing to do166return;167}168w[n] = v[i];169}170}171// now put non-selected in remaining places172let k = 0;173for (let i = 0; i < v.length; i++) {174if (!selected[v[i]]) {175while (w[k] != null) {176k += 1;177}178w[k] = v[i];179}180}181return w;182}183184export function moveCell({185oldIndex,186newIndex,187getPos,188size,189}: {190oldIndex: number;191newIndex: number;192getPos: (index: number) => number;193size: number;194}): number {195if (oldIndex == newIndex) {196// no-op -- better to not call in this case.197return getPos(newIndex);198}199const pos = getPos(newIndex);200if (newIndex == 0) {201// easy special case: to beginning202return pos - 1;203} else if (newIndex >= size - 1) {204// the end205return pos + 1;206} else {207const pos1 = getPos(oldIndex < newIndex ? newIndex + 1 : newIndex - 1);208return (pos + pos1) / 2;209}210}211212213