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/project/formatters/index.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Use a formatter like prettier to reformat a syncstring.78This very nicely use the in-memory node module to prettyify code, by simply modifying the syncstring9on the backend. This avoids having to send the whole file back and forth, worrying about multiple users10and their cursors, file state etc. -- it just merges in the prettification at a point in time.11Also, by doing this on the backend we don't add 5MB (!) to the webpack frontend bundle, to install12something that is not supported on the frontend anyway.13*/1415declare let require: any;1617import { make_patch } from "@cocalc/sync/editor/generic/util";18import { math_escape, math_unescape } from "@cocalc/util/markdown-utils";19import { filename_extension } from "@cocalc/util/misc";20import { bib_format } from "./bib-format";21import { clang_format } from "./clang-format";22import genericFormat from "./generic-format";23import { gofmt } from "./gofmt";24import { latex_format } from "./latex-format";25import { python_format } from "./python-format";26import { r_format } from "./r-format";27import { rust_format } from "./rust-format";28import { xml_format } from "./xml-format";29// mathjax-utils is from upstream project Jupyter30import { once } from "@cocalc/util/async-utils";31import { remove_math, replace_math } from "@cocalc/util/mathjax-utils";32import { get_prettier } from "./prettier-lib";33import type {34Syntax as FormatterSyntax,35Config,36Options,37} from "@cocalc/util/code-formatter";38export type { Config, Options, FormatterSyntax };3940export async function run_formatter(41client: any,42path: string,43options: Options,44logger: any,45): Promise<object> {46// What we do is edit the syncstring with the given path to be "prettier" if possible...47const syncstring = client.syncdoc({ path });48if (syncstring == null || syncstring.get_state() == "closed") {49return {50status: "error",51error: "document not fully opened",52phase: "format",53};54}55if (syncstring.get_state() != "ready") {56await once(syncstring, "ready");57}58const doc = syncstring.get_doc();59let formatted, math, input0;60let input = (input0 = doc.to_str());61if (options.parser === "markdown") {62[input, math] = remove_math(math_escape(input));63}64try {65formatted = await run_formatter_string(path, input, options, logger);66} catch (err) {67logger.debug(`run_formatter error: ${err.message}`);68return { status: "error", phase: "format", error: err.message };69}70if (options.parser === "markdown") {71formatted = math_unescape(replace_math(formatted, math));72}73// NOTE: the code used to make the change here on the backend.74// See https://github.com/sagemathinc/cocalc/issues/4335 for why75// that leads to confusion.76const patch = make_patch(input0, formatted);77return { status: "ok", patch };78}7980export async function run_formatter_string(81path: string | undefined,82input: string,83options: Options,84logger: any,85): Promise<string> {86let formatted;87logger.debug(`run_formatter options.parser: "${options.parser}"`);88switch (options.parser) {89case "latex":90case "latexindent":91formatted = await latex_format(input, options);92break;93case "python":94case "yapf":95formatted = await python_format(input, options, logger);96break;97case "zig":98formatted = await genericFormat({99command: "zig",100args: (tmp) => ["fmt", tmp],101input,102timeout_s: 30,103});104break;105case "r":106case "formatR":107formatted = await r_format(input, options, logger);108break;109case "xml-tidy":110formatted = await xml_format(input, options, logger);111break;112case "bib-biber":113formatted = await bib_format(input, options, logger);114break;115case "clang-format":116const ext = filename_extension(path != null ? path : "");117formatted = await clang_format(input, ext, options, logger);118break;119case "gofmt":120formatted = await gofmt(input, options, logger);121break;122case "rust":123case "rustfmt":124formatted = await rust_format(input, options, logger);125break;126default:127const prettier = get_prettier();128if (prettier != null) {129formatted = prettier.format(input, options);130} else {131throw Error("Could not load 'prettier'");132}133}134return formatted;135}136137138