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/latex-code-folding.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45// LaTeX code folding (isn't included in CodeMirror)67import * as CodeMirror from "codemirror";8import { startswith } from "@cocalc/util/misc";9import { trimStart } from "lodash";1011function get_latex_environ(s: string): string | undefined {12const i = s.indexOf("{");13const j = s.indexOf("}");14if (i !== -1 && j !== -1) {15return s.slice(i + 1, j).trim();16} else {17return undefined;18}19}2021CodeMirror.registerHelper("fold", "stex", function (cm, start) {22let line = trimStart(cm.getLine(start.line));23const find_close = function () {24const BEGIN = "\\begin";25if (startswith(line, BEGIN)) {26// \begin{foo}27// ...28// \end{foo}29// find environment close30const environ = get_latex_environ(line.slice(BEGIN.length));31if (environ == null) {32return [undefined, undefined];33}34// find environment close35const END = "\\end";36let level = 0;37let begin, end;38try {39begin = new RegExp(`\\\\begin\\s*{${environ}}`);40end = new RegExp(`\\\\end\\s*{${environ}}`);41} catch (_err) {42// This can happen, e.g., if somebody puts something totally wrong for the environment.43// See https://github.com/sagemathinc/cocalc/issues/579444// Here's a reasonable fallback:45return [undefined, undefined];46}47for (let i = start.line; i <= cm.lastLine(); i++) {48const cur = cm.getLine(i);49const m = cur.search(begin);50const j = cur.search(end);51if (m !== -1 && (j === -1 || m < j)) {52level += 1;53}54if (j !== -1) {55level -= 1;56if (level === 0) {57return [i, j + END.length - 1];58}59}60}61} else if (startswith(line, "\\[")) {62for (let i = start.line + 1; i <= cm.lastLine(); i++) {63if (startswith(trimStart(cm.getLine(i)), "\\]")) {64return [i, 0];65}66}67} else if (startswith(line, "\\(")) {68for (let i = start.line + 1; i <= cm.lastLine(); i++) {69if (startswith(trimStart(cm.getLine(i)), "\\)")) {70return [i, 0];71}72}73} else if (startswith(line, "\\documentclass")) {74// pre-amble75for (let i = start.line + 1; i <= cm.lastLine(); i++) {76if (startswith(trimStart(cm.getLine(i)), "\\begin{document}")) {77return [i - 1, 0];78}79}80} else if (startswith(line, "\\chapter")) {81// book chapter82for (let i = start.line + 1; i <= cm.lastLine(); i++) {83if (84startswith(trimStart(cm.getLine(i)), ["\\chapter", "\\end{document}"])85) {86return [i - 1, 0];87}88}89return [cm.lastLine(), 0];90} else if (startswith(line, "\\section")) {91// article section92for (let i = start.line + 1; i <= cm.lastLine(); i++) {93if (94startswith(trimStart(cm.getLine(i)), [95"\\chapter",96"\\section",97"\\end{document}",98])99) {100return [i - 1, 0];101}102}103return [cm.lastLine(), 0];104} else if (startswith(line, "\\subsection")) {105// article subsection106for (let i = start.line + 1; i <= cm.lastLine(); i++) {107if (108startswith(trimStart(cm.getLine(i)), [109"\\chapter",110"\\section",111"\\subsection",112"\\end{document}",113])114) {115return [i - 1, 0];116}117}118return [cm.lastLine(), 0];119} else if (startswith(line, "\\subsubsection")) {120// article subsubsection121for (let i = start.line + 1; i <= cm.lastLine(); i++) {122if (123startswith(trimStart(cm.getLine(i)), [124"\\chapter",125"\\section",126"\\subsection",127"\\subsubsection",128"\\end{document}",129])130) {131return [i - 1, 0];132}133}134return [cm.lastLine(), 0];135} else if (startswith(line, "\\subsubsubsection")) {136// article subsubsubsection137for (let i = start.line + 1; i <= cm.lastLine(); i++) {138if (139startswith(trimStart(cm.getLine(i)), [140"\\chapter",141"\\section",142"\\subsection",143"\\subsubsection",144"\\subsubsubsection",145"\\end{document}",146])147) {148return [i - 1, 0];149}150}151return [cm.lastLine(), 0];152} else if (startswith(line, "%\\begin{}")) {153// support what texmaker supports for custom folding -- http://tex.stackexchange.com/questions/44022/code-folding-in-latex154for (let i = start.line + 1; i <= cm.lastLine(); i++) {155if (startswith(trimStart(cm.getLine(i)), "%\\end{}")) {156return [i, 0];157}158}159}160161return [undefined, undefined]; // no folding here...162};163164const [i, j] = find_close();165if (i != null) {166line = cm.getLine(start.line);167let k = line.indexOf("}");168if (k === -1) {169k = line.length;170}171const range = {172from: CodeMirror.Pos(start.line, k + 1),173to: CodeMirror.Pos(i, j),174};175return range;176} else {177// nothing to fold178return undefined;179}180});181182183