Path: blob/master/src/packages/jupyter/util/cell-utils.ts
5837 views
/*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: number,16) {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> | undefined,99cell_list: List<string>,100cur_id: string,101delta: -1 | 1,102): 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*/115if (cells == null) {116return 0;117}118let cell_list_0: List<string>;119if (cell_list == null) {120cell_list_0 = sorted_cell_list(cells)!;121} else {122cell_list_0 = cell_list;123}124let adjacent_id: string | undefined;125cell_list_0.forEach((id, i) => {126if (id === cur_id) {127const j = i + delta;128if (j >= 0 && j < cell_list_0.size) {129adjacent_id = cell_list_0.get(j);130}131return false; // break iteration132}133});134const adjacent_pos = cells.getIn([adjacent_id, "pos"]) as number | undefined;135const current_pos = cells.getIn([cur_id, "pos"]) as number;136let pos: number;137if (adjacent_pos != null) {138// there is a cell after (or before) cur_id cell139pos = (adjacent_pos + current_pos) / 2;140} else {141// no cell after (or before)142pos = current_pos + delta;143}144return pos;145}146147export function move_selected_cells(148v?: string[],149selected?: { [id: string]: true },150delta?: number,151) {152/*153- v = ordered js array of all cell id's154- selected = js map from ids to true155- delta = integer156157Returns new ordered js array of all cell id's or undefined if nothing to do.158*/159if (v == null || selected == null || !delta || len(selected) === 0) {160return; // nothing to do161}162const w: string[] = [];163// put selected cells in their proper new positions164for (let i = 0; i < v.length; i++) {165if (selected[v[i]]) {166const n = i + delta;167if (n < 0 || n >= v.length) {168// would move cells out of document, so nothing to do169return;170}171w[n] = v[i];172}173}174// now put non-selected in remaining places175let k = 0;176for (let i = 0; i < v.length; i++) {177if (!selected[v[i]]) {178while (w[k] != null) {179k += 1;180}181w[k] = v[i];182}183}184return w;185}186187export function moveCell({188oldIndex,189newIndex,190getPos,191size,192}: {193oldIndex: number;194newIndex: number;195getPos: (index: number) => number;196size: number;197}): number {198if (oldIndex == newIndex) {199// no-op -- better to not call in this case.200return getPos(newIndex);201}202const pos = getPos(newIndex);203if (newIndex == 0) {204// easy special case: to beginning205return pos - 1;206} else if (newIndex >= size - 1) {207// the end208return pos + 1;209} else {210const pos1 = getPos(oldIndex < newIndex ? newIndex + 1 : newIndex - 1);211return (pos + pos1) / 2;212}213}214215216