CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/codemirror/extensions/align-assignments.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import * as CodeMirror from "codemirror";
7
import { cm_start_end } from "./util";
8
9
/*
10
Format the selected block (or blocks) of text, so it looks like this:
11
stuff : 'abc'
12
foo : 1
13
more_0 : 'blah'
14
Or
15
stuff = 'abc'
16
foo = 1
17
more_0 = 'blah'
18
The column separate is the first occurence in the first line of
19
one of '=' or ':'. Selected lines that don't contain either symbol
20
are ignored.
21
22
NOTE: This is a nonstandard extension motivated mostly by coffeescript.
23
**Thus consider it on the way to being deprecated.** Prettier and its
24
ilk solves the same sort of problem for all the languages we plan to
25
use longterm in a vastly better way.
26
*/
27
28
CodeMirror.defineExtension("align_assignments", function () {
29
// @ts-ignore
30
const cm : any = this;
31
for (let sel of cm.listSelections()) {
32
const { start_line, end_line } = cm_start_end(sel);
33
let symbol: string | undefined = undefined;
34
let column = 0;
35
// first pass -- figure out what the symbol is and what column we will move it to.
36
for (let n = start_line; n <= end_line; n++) {
37
const x = cm.getLine(n);
38
if (symbol == null) {
39
// we still don't know what the separate symbol is.
40
if (x.indexOf(":") != -1) {
41
symbol = ":";
42
} else if (x.indexOf("=") != -1) {
43
symbol = "=";
44
}
45
}
46
let i = x.indexOf(symbol);
47
if (i === -1) {
48
continue; // no symbol in this line, so skip
49
}
50
// reduce i until x[i-1] is NOT whitespace.
51
while (i > 0 && x[i - 1].trim() === "") {
52
i -= 1;
53
}
54
i += 1;
55
column = Math.max(i, column);
56
}
57
if (symbol == null || !column) {
58
continue; // no symbol in this selection, or no need to move it. Done.
59
}
60
// second pass -- move symbol over by inserting space
61
for (let n = start_line; n <= end_line; n++) {
62
const x = cm.getLine(n);
63
const i = x.indexOf(symbol);
64
if (i !== -1) {
65
// There is a symbol in this line -- put it in the spot where we want it.
66
if (i < column) {
67
// symbol is too early -- add space
68
// column - i spaces
69
let spaces = "";
70
for (let j = 0; j < column - i; j++) {
71
spaces += " ";
72
}
73
// insert spaces in front of the symbol
74
cm.replaceRange(spaces, { line: n, ch: i }, { line: n, ch: i });
75
} else if (i > column) {
76
// symbol is too late -- remove spaces
77
cm.replaceRange("", { line: n, ch: column }, { line: n, ch: i });
78
}
79
// Ensure the right amount of whitespace after the symbol -- exactly one space
80
let j = i + 1; // this will be the next position after x[i] that is not whitespace
81
while (j < x.length && x[j].trim() === "") {
82
j += 1;
83
}
84
if (j - i >= 2) {
85
// remove some spaces
86
cm.replaceRange(
87
"",
88
{ line: n, ch: column + 1 },
89
{ line: n, ch: column + (j - i - 1) }
90
);
91
} else if (j - i === 1) {
92
// insert a space
93
cm.replaceRange(
94
" ",
95
{ line: n, ch: column + 1 },
96
{ line: n, ch: column + 1 }
97
);
98
}
99
}
100
}
101
}
102
});
103
104