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/frontend/codemirror/extensions/sagews.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45// I think the extensions are all used to support Sage worksheets...67import * as CodeMirror from "codemirror";8import { defaults, required } from "@cocalc/util/misc";9import { IS_MOBILE } from "../../feature";10declare var $;1112// Apply a CodeMirror changeObj to this editing buffer.13CodeMirror.defineExtension("apply_changeObj", function (changeObj) {14// @ts-ignore15const editor = this;16editor.replaceRange(changeObj.text, changeObj.from, changeObj.to);17if (changeObj.next != null) {18return editor.apply_changeObj(changeObj.next);19}20});2122// This is an improved rewrite of simple-hint.js from the CodeMirror3 distribution.23// It is used only by sage worksheets and nothing else, currently.24CodeMirror.defineExtension("showCompletions", function (opts: {25from: CodeMirror.Position;26to: CodeMirror.Position;27completions: string[];28target: string;29completions_size?: number;30}): void {31const { from, to, completions, target, completions_size } = defaults(opts, {32from: required,33to: required,34completions: required,35target: required,36completions_size: 20,37});3839if (completions.length === 0) {40return;41}4243// @ts-ignore44const editor = this;45const start_cursor_pos = editor.getCursor();46const insert = function (str: string): void {47const pos = editor.getCursor();48from.line = pos.line;49to.line = pos.line;50const shift = pos.ch - start_cursor_pos.ch;51from.ch += shift;52to.ch += shift;53editor.replaceRange(str, from, to);54};5556if (completions.length === 1) {57// do not include target in appended completion if it has a '*'58if (target.indexOf("*") === -1) {59insert(target + completions[0]);60} else {61insert(completions[0]);62}63return;64}6566const sel = $("<select>").css("width", "auto");67const complete = $("<div>").addClass("webapp-completions").append(sel);68for (let c of completions) {69// do not include target in appended completion if it has a '*'70if (target.indexOf("*") === -1) {71sel.append($("<option>").text(target + c));72} else {73sel.append($("<option>").text(c));74}75}76sel.find(":first").attr("selected", true);77sel.attr("size", Math.min(completions_size, completions.length));78const pos = editor.cursorCoords(from);7980complete.css({81left: pos.left + "px",82top: pos.bottom + "px",83});84$("body").append(complete);85// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.86const winW =87window.innerWidth ||88Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);89if (winW - pos.left < sel.attr("clientWidth")) {90complete.css({ left: pos.left - sel.attr("clientWidth") + "px" });91}92// Hide scrollbar93if (completions.length <= completions_size) {94complete.css({ width: sel.attr("clientWidth") - 1 + "px" });95}9697let done = false;9899const close = function () {100if (done) {101return;102}103done = true;104complete.remove();105};106107const pick = function () {108insert(sel.val());109close();110if (!IS_MOBILE) {111return setTimeout(() => editor.focus(), 50);112}113};114115sel.blur(pick);116sel.dblclick(pick);117if (!IS_MOBILE) {118// do not do this on mobile, since it makes it unusable!119sel.click(pick);120}121sel.keydown(function (event) {122const code = event.keyCode;123switch (code) {124case 13: // enter125pick();126return false;127case 27:128close();129editor.focus();130return false;131default:132if (133code !== 38 &&134code !== 40 &&135code !== 33 &&136code !== 34 &&137!(CodeMirror as any).isModifierKey(event)138) {139close();140editor.focus();141// Pass to CodeMirror (e.g., backspace)142return editor.triggerOnKeyDown(event);143}144}145});146sel.focus();147});148149function get_inspect_dialog(editor) {150const dialog = $(`\151<div class="webapp-codemirror-introspect modal"152data-backdrop="static" tabindex="-1" role="dialog" aria-hidden="true">153<div class="modal-dialog" style="width:90%">154<div class="modal-content">155<div class="modal-header">156<button type="button" class="close" aria-hidden="true">157<span style="font-size:20pt;">×</span>158</button>159<h4><div class="webapp-codemirror-introspect-title"></div></h4>160</div>161162<div class="webapp-codemirror-introspect-content-source-code cm-s-default">163</div>164<div class="webapp-codemirror-introspect-content-docstring cm-s-default">165</div>166167168<div class="modal-footer">169<button class="btn btn-close btn-default">Close</button>170</div>171</div>172</div>173</div>\174`);175dialog.modal();176dialog.data("editor", editor);177178dialog.find("button").click(function () {179dialog.modal("hide");180dialog.remove(); // also remove; we no longer have any use for this element!181});182183// see http://stackoverflow.com/questions/8363802/bind-a-function-to-twitter-bootstrap-modal-close184dialog.on("hidden.bs.modal", function () {185dialog.data("editor").focus?.();186dialog.data("editor", 0);187});188189return dialog;190}191192CodeMirror.defineExtension("showIntrospect", function (opts: {193from: CodeMirror.Position;194content: string;195type: string;196target: string;197}): void {198opts = defaults(opts, {199from: required,200content: required,201type: required, // 'docstring', 'source-code' -- FUTURE:202target: required,203});204// @ts-ignore205const editor = this;206if (typeof opts.content !== "string") {207// If for some reason the content isn't a string (e.g., undefined or an object or something else),208// convert it a string, which will display fine.209opts.content = `${JSON.stringify(opts.content)}`;210}211const element = get_inspect_dialog(editor);212element.find(".webapp-codemirror-introspect-title").text(opts.target);213element.show();214let elt;215if (opts.type === "source-code") {216elt = element.find(".webapp-codemirror-introspect-content-source-code")[0];217if (elt != null) {218// see https://github.com/sagemathinc/cocalc/issues/1993219(CodeMirror as any).runMode(opts.content, "python", elt);220}221} else {222elt = element.find(".webapp-codemirror-introspect-content-docstring")[0];223if (elt != null) {224// see https://github.com/sagemathinc/cocalc/issues/1993225(CodeMirror as any).runMode(opts.content, "text/x-rst", elt);226}227}228});229230231232233