mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-11-03 13:11:46 +08:00
671 lines
21 KiB
JavaScript
671 lines
21 KiB
JavaScript
export const org_cycle = (cm) => {
|
||
let pos = cm.getCursor();
|
||
isFold(cm, pos) ? unfold(cm, pos) : fold(cm, pos);
|
||
};
|
||
|
||
|
||
let state = {
|
||
stab: 'CONTENT'
|
||
};
|
||
export const org_set_fold = (cm) => {
|
||
const cursor = cm.getCursor();
|
||
set_folding_mode(cm, state.stab);
|
||
cm.setCursor(cursor);
|
||
return state.stab;
|
||
};
|
||
/*
|
||
* DONE: Global visibility cycling
|
||
* TODO: or move to previous table field.
|
||
*/
|
||
export const org_shifttab = (cm) => {
|
||
if(state.stab === "SHOW_ALL"){
|
||
state.stab = 'OVERVIEW';
|
||
}else if(state.stab === "OVERVIEW"){
|
||
state.stab = 'CONTENT';
|
||
}else if(state.stab === "CONTENT"){
|
||
state.stab = 'SHOW_ALL';
|
||
}
|
||
set_folding_mode(cm, state.stab);
|
||
return state.stab;
|
||
};
|
||
|
||
|
||
function set_folding_mode(cm, mode){
|
||
if(mode === "OVERVIEW"){
|
||
folding_mode_overview(cm);
|
||
}else if(mode === "SHOW_ALL"){
|
||
folding_mode_all(cm);
|
||
}else if(mode === "CONTENT"){
|
||
folding_mode_content(cm);
|
||
}
|
||
cm.refresh();
|
||
|
||
function folding_mode_overview(cm){
|
||
cm.operation(function() {
|
||
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++){
|
||
fold(cm, CodeMirror.Pos(i, 0));
|
||
}
|
||
});
|
||
}
|
||
function folding_mode_content(cm){
|
||
cm.operation(function() {
|
||
let previous_header = null;
|
||
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++){
|
||
fold(cm, CodeMirror.Pos(i, 0));
|
||
if(/header/.test(cm.getTokenTypeAt(CodeMirror.Pos(i, 0))) === true){
|
||
const level = cm.getLine(i).replace(/^(\*+).*/, "$1").length;
|
||
if(previous_header && level > previous_header.level){
|
||
unfold(cm, CodeMirror.Pos(previous_header.line, 0));
|
||
}
|
||
previous_header = {
|
||
line: i,
|
||
level: level
|
||
};
|
||
}
|
||
}
|
||
});
|
||
}
|
||
function folding_mode_all(cm){
|
||
cm.operation(function() {
|
||
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++){
|
||
if(/header/.test(cm.getTokenTypeAt(CodeMirror.Pos(i, 0))) === true){
|
||
unfold(cm, CodeMirror.Pos(i, 0));
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Promote heading or move table column to left.
|
||
*/
|
||
export const org_metaleft = (cm) => {
|
||
const line = cm.getCursor().line;
|
||
_metaleft(cm, line);
|
||
};
|
||
function _metaleft(cm, line){
|
||
let p = null;
|
||
if(p = isTitle(cm, line)){
|
||
if(p['level'] > 1) cm.replaceRange('', {line: p.start, ch: 0}, {line: p.start, ch: 1});
|
||
}else if(p = isItemList(cm, line)){
|
||
for(let i=p.start; i<=p.end; i++){
|
||
if(p['level'] > 0) cm.replaceRange('', {line: i, ch: 0}, {line: i, ch: 2});
|
||
}
|
||
}else if(p = isNumberedList(cm, line)){
|
||
for(let i=p.start; i<=p.end; i++){
|
||
if(p['level'] > 0) cm.replaceRange('', {line: i, ch: 0}, {line: i, ch: 3});
|
||
}
|
||
rearrange_list(cm, line);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Demote a subtree, a list item or move table column to right.
|
||
* In front of a drawer or a block keyword, indent it correctly.
|
||
*/
|
||
export const org_metaright = (cm) => {
|
||
const line = cm.getCursor().line;
|
||
_metaright(cm, line);
|
||
};
|
||
|
||
function _metaright(cm, line){
|
||
let p = null, tmp = null;
|
||
if(p = isTitle(cm, line)){
|
||
cm.replaceRange('*', {line: p.start, ch: 0});
|
||
}else if(p = isItemList(cm, line)){
|
||
if(tmp = isItemList(cm, p.start - 1)){
|
||
if(p.level < tmp.level + 1){
|
||
for(let i=p.start; i<=p.end; i++){
|
||
cm.replaceRange(' ', {line: i, ch: 0});
|
||
}
|
||
}
|
||
}
|
||
}else if(p = isNumberedList(cm, line)){
|
||
if(tmp = isNumberedList(cm, p.start - 1)){
|
||
if(p.level < tmp.level + 1){
|
||
for(let i=p.start; i<=p.end; i++){
|
||
cm.replaceRange(' ', {line: i, ch: 0});
|
||
}
|
||
rearrange_list(cm, p.start);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Insert a new heading or wrap a region in a table
|
||
*/
|
||
export const org_meta_return = (cm) => {
|
||
const line = cm.getCursor().line,
|
||
content = cm.getLine(line);
|
||
let p = null;
|
||
|
||
if(p = isItemList(cm, line)){
|
||
const level = p.level;
|
||
cm.replaceRange('\n'+" ".repeat(level*2)+'- ', {line: p.end, ch: cm.getLine(p.end).length});
|
||
cm.setCursor({line: p.end+1, ch: level*2+2});
|
||
}else if(p = isNumberedList(cm, line)){
|
||
const level = p.level;
|
||
cm.replaceRange('\n'+" ".repeat(level*3)+(p.n+1)+'. ', {line: p.end, ch: cm.getLine(p.end).length});
|
||
cm.setCursor({line: p.end+1, ch: level*3+3});
|
||
rearrange_list(cm, line);
|
||
}else if(p = isTitle(cm, line)){
|
||
const tmp = previousOfType(cm, 'title', line);
|
||
const level = tmp && tmp.level || 1;
|
||
cm.replaceRange('\n'+'*'.repeat(level)+' ', {line: line, ch: content.length});
|
||
cm.setCursor({line: line+1, ch: level+1});
|
||
}else if(content.trim() === ""){
|
||
cm.replaceRange('* ', {line: line, ch: 0});
|
||
cm.setCursor({line: line, ch: 2});
|
||
}else{
|
||
cm.replaceRange('\n\n* ', {line: line, ch: content.length});
|
||
cm.setCursor({line: line + 2, ch: 2});
|
||
}
|
||
};
|
||
|
||
|
||
const TODO_CYCLES = ["TODO", "DONE", ""];
|
||
/*
|
||
* Cycle the thing at point or in the current line, depending on context.
|
||
* Depending on context, this does one of the following:
|
||
* - TODO: switch a timestamp at point one day into the past
|
||
* - DONE: on a headline, switch to the previous TODO keyword.
|
||
* - TODO: on an item, switch entire list to the previous bullet type
|
||
* - TODO: on a property line, switch to the previous allowed value
|
||
* - TODO: on a clocktable definition line, move time block into the past
|
||
*/
|
||
export const org_shiftleft = (cm) => {
|
||
const cycles = [].concat(TODO_CYCLES.slice(0).reverse(), TODO_CYCLES.slice(-1)),
|
||
line = cm.getCursor().line,
|
||
content = cm.getLine(line),
|
||
params = isTitle(cm, line);
|
||
|
||
if(params === null) return;
|
||
params['status'] = cycles[cycles.indexOf(params['status']) + 1];
|
||
cm.replaceRange(makeTitle(params), {line: line, ch: 0}, {line: line, ch: content.length});
|
||
};
|
||
/*
|
||
* Cycle the thing at point or in the current line, depending on context.
|
||
* Depending on context, this does one of the following:
|
||
* - TODO: switch a timestamp at point one day into the future
|
||
* - DONE: on a headline, switch to the next TODO keyword.
|
||
* - TODO: on an item, switch entire list to the next bullet type
|
||
* - TODO: on a property line, switch to the next allowed value
|
||
* - TODO: on a clocktable definition line, move time block into the future
|
||
*/
|
||
export const org_shiftright = (cm) => {
|
||
cm.operation(() => {
|
||
const cycles = [].concat(TODO_CYCLES, [TODO_CYCLES[0]]),
|
||
line = cm.getCursor().line,
|
||
content = cm.getLine(line),
|
||
params = isTitle(cm, line);
|
||
|
||
if(params === null) return;
|
||
params['status'] = cycles[cycles.indexOf(params['status']) + 1];
|
||
cm.replaceRange(makeTitle(params), {line: line, ch: 0}, {line: line, ch: content.length});
|
||
});
|
||
};
|
||
|
||
export const org_insert_todo_heading = (cm) => {
|
||
cm.operation(() => {
|
||
const line = cm.getCursor().line,
|
||
content = cm.getLine(line);
|
||
|
||
let p = null;
|
||
if(p = isItemList(cm, line)){
|
||
const level = p.level;
|
||
cm.replaceRange('\n'+" ".repeat(level*2)+'- [ ] ', {line: p.end, ch: cm.getLine(p.end).length});
|
||
cm.setCursor({line: line+1, ch: 6+level*2});
|
||
}else if(p = isNumberedList(cm, line)){
|
||
const level = p.level;
|
||
cm.replaceRange('\n'+" ".repeat(level*3)+(p.n+1)+'. [ ] ', {line: p.end, ch: cm.getLine(p.end).length});
|
||
cm.setCursor({line: p.end+1, ch: level*3+7});
|
||
rearrange_list(cm, line);
|
||
}else if(p = isTitle(cm, line)){
|
||
const level = p && p.level || 1;
|
||
cm.replaceRange('\n'+"*".repeat(level)+' TODO ', {line: line, ch: content.length});
|
||
cm.setCursor({line: line+1, ch: level+6});
|
||
}else if(content.trim() === ""){
|
||
cm.replaceRange('* TODO ', {line: line, ch: 0});
|
||
cm.setCursor({line: line, ch: 7});
|
||
}else{
|
||
cm.replaceRange('\n\n* TODO ', {line: line, ch: content.length});
|
||
cm.setCursor({line: line + 2, ch: 7});
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
/*
|
||
* Move subtree up or move table row up.
|
||
* Calls ‘org-move-subtree-up’ or ‘org-table-move-row’ or
|
||
* ‘org-move-item-up’, depending on context
|
||
*/
|
||
export const org_metaup = (cm) => {
|
||
cm.operation(() => {
|
||
const line = cm.getCursor().line;
|
||
let p = null;
|
||
|
||
if(p = isItemList(cm, line)){
|
||
let a = isItemList(cm, p.start - 1);
|
||
if(a){
|
||
swap(cm, [p.start, p.end], [a.start, a.end]);
|
||
rearrange_list(cm, line);
|
||
}
|
||
}else if(p = isNumberedList(cm, line)){
|
||
let a = isNumberedList(cm, p.start - 1);
|
||
if(a){
|
||
swap(cm, [p.start, p.end], [a.start, a.end]);
|
||
rearrange_list(cm, line);
|
||
}
|
||
}else if(p = isTitle(cm, line)){
|
||
let _line = line,
|
||
a;
|
||
do{
|
||
_line -= 1;
|
||
if(a = isTitle(cm, _line, p.level)){
|
||
break;
|
||
}
|
||
}while(_line > 0);
|
||
|
||
if(a){
|
||
swap(cm, [p.start, p.end], [a.start, a.end]);
|
||
org_set_fold(cm);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/*
|
||
* Move subtree down or move table row down.
|
||
* Calls ‘org-move-subtree-down’ or ‘org-table-move-row’ or
|
||
* ‘org-move-item-down’, depending on context
|
||
*/
|
||
export const org_metadown = (cm) => {
|
||
cm.operation(() => {
|
||
const line = cm.getCursor().line;
|
||
let p = null;
|
||
|
||
if(p = isItemList(cm, line)){
|
||
let a = isItemList(cm, p.end + 1);
|
||
if(a){
|
||
swap(cm, [p.start, p.end], [a.start, a.end]);
|
||
}
|
||
}else if(p = isNumberedList(cm, line)){
|
||
let a = isNumberedList(cm, p.end + 1);
|
||
if(a){
|
||
swap(cm, [p.start, p.end], [a.start, a.end]);
|
||
}
|
||
rearrange_list(cm, line);
|
||
}else if(p = isTitle(cm, line)){
|
||
let a = isTitle(cm, p.end + 1, p.level);
|
||
if(a){
|
||
swap(cm, [p.start, p.end], [a.start, a.end]);
|
||
org_set_fold(cm);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
|
||
export const org_shiftmetaright = function(cm){
|
||
cm.operation(() => {
|
||
const line = cm.getCursor().line;
|
||
let p = null;
|
||
if(p = isTitle(cm, line)){
|
||
_metaright(cm, line);
|
||
for(let i=p.start + 1; i<=p.end; i++){
|
||
if(isTitle(cm, i)){
|
||
_metaright(cm, i);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
export const org_shiftmetaleft = function(cm){
|
||
cm.operation(() => {
|
||
const line = cm.getCursor().line;
|
||
let p = null;
|
||
if(p = isTitle(cm, line)){
|
||
if(p.level === 1) return;
|
||
_metaleft(cm, line);
|
||
for(let i=p.start + 1; i<=p.end; i++){
|
||
if(isTitle(cm, i)){
|
||
_metaleft(cm, i);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
|
||
|
||
function makeTitle(p){
|
||
let content = "*".repeat(p['level'])+" ";
|
||
if(p['status']){
|
||
content += p['status']+" ";
|
||
}
|
||
content += p['content'];
|
||
return content;
|
||
}
|
||
|
||
function previousOfType(cm, type, line){
|
||
let content, tmp, i;
|
||
for(i=line - 1; i>0; i--){
|
||
if(type === 'list' || type === null){
|
||
tmp = isItemList(cm, line);
|
||
}else if(type === 'numbered' || type === null){
|
||
tmp = isNumberedList(cm, line);
|
||
}else if(type === 'title' || type === null){
|
||
tmp = isTitle(cm, line);
|
||
}
|
||
if(tmp !== null){
|
||
return tmp;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function isItemList(cm, line){
|
||
const rootLineItem = findRootLine(cm, line);
|
||
if(rootLineItem === null) return null;
|
||
line = rootLineItem;
|
||
const content = cm.getLine(line);
|
||
|
||
if(content && (content.trimLeft()[0] !== "-" || content.trimLeft()[1] !== " ")) return null;
|
||
const padding = content.replace(/^(\s*).*$/, "$1").length;
|
||
if(padding % 2 !== 0) return null;
|
||
return {
|
||
type: 'list',
|
||
level: padding / 2,
|
||
content: content.trimLeft().replace(/^\s*\-\s(.*)$/, '$1'),
|
||
start: line,
|
||
end: function(_cm, _line){
|
||
let line_candidate = _line,
|
||
content = null;
|
||
do{
|
||
_line += 1;
|
||
content = _cm.getLine(_line);
|
||
if(content === undefined || content.trimLeft()[0] === "-"){
|
||
break;
|
||
}else if(/^\s+/.test(content)){
|
||
line_candidate = _line;
|
||
continue;
|
||
}else{
|
||
break;
|
||
}
|
||
}while(_line <= _cm.lineCount())
|
||
return line_candidate;
|
||
}(cm, line)
|
||
};
|
||
|
||
function findRootLine(_cm, _line){
|
||
let content;
|
||
do{
|
||
content = _cm.getLine(_line);
|
||
if(/^\s*\-/.test(content)) return _line;
|
||
else if(/^\s+/.test(content) === false){
|
||
break;
|
||
}
|
||
_line -= 1;
|
||
}while(_line >= 0);
|
||
return null;
|
||
}
|
||
|
||
}
|
||
function isNumberedList(cm, line){
|
||
const rootLineItem = findRootLine(cm, line);
|
||
if(rootLineItem === null) return null;
|
||
line = rootLineItem;
|
||
const content = cm.getLine(line);
|
||
|
||
if(/^[0-9]+[\.\)]\s.*$/.test(content && content.trimLeft()) === false) return null;
|
||
const padding = content.replace(/^(\s*)[0-9]+.*$/, "$1").length;
|
||
if(padding % 3 !== 0) return null;
|
||
return {
|
||
type: 'numbered',
|
||
level: padding / 3,
|
||
content: content.trimLeft().replace(/^[0-9]+[\.\)]\s(.*)$/, '$1'),
|
||
start: line,
|
||
end: function(_cm, _line){
|
||
let line_candidate = _line,
|
||
content = null;
|
||
do{
|
||
_line += 1;
|
||
content = _cm.getLine(_line);
|
||
if(content === undefined || /^[0-9]+[\.\)]/.test(content.trimLeft())){
|
||
break;
|
||
}else if(/^\s+/.test(content)){
|
||
line_candidate = _line;
|
||
continue;
|
||
}else{
|
||
break;
|
||
}
|
||
}while(_line <= _cm.lineCount())
|
||
return line_candidate;
|
||
}(cm, line),
|
||
// specific
|
||
n: parseInt(content.trimLeft().replace(/^([0-9]+).*$/, "$1")),
|
||
separator: content.trimLeft().replace(/^[0-9]+([\.\)]).*$/, '$1')
|
||
};
|
||
|
||
|
||
function findRootLine(_cm, _line){
|
||
let content;
|
||
do{
|
||
content = _cm.getLine(_line);
|
||
if(/^\s*[0-9]+[\.\)]\s/.test(content)) return _line;
|
||
else if(/^\s+/.test(content) === false){
|
||
break;
|
||
}
|
||
_line -= 1;
|
||
}while(_line >= 0);
|
||
return null;
|
||
}
|
||
}
|
||
function isTitle(cm, line, level){
|
||
const content = cm.getLine(line);
|
||
if(/^\*+\s/.test(content) === false) return null;
|
||
const match = content.match(/^(\*+)([\sA-Z]*)\s(.*)$/);
|
||
const reference_level = match[1].length;
|
||
if(level !== undefined && level !== reference_level){ return null; }
|
||
if(match === null) return null;
|
||
return {
|
||
type: 'title',
|
||
level: reference_level,
|
||
content: match[3],
|
||
start: line,
|
||
end: function(_cm, _line){
|
||
let line_candidate = _line,
|
||
content = null;
|
||
do{
|
||
_line += 1;
|
||
content = _cm.getLine(_line);
|
||
if(content === undefined) break;
|
||
let match = content.match(/^(\*+)\s.*/);
|
||
if(match && match[1] && ( match[1].length === reference_level || match[1].length < reference_level)){
|
||
break;
|
||
}else{
|
||
line_candidate = _line;
|
||
continue;
|
||
}
|
||
}while(_line <= _cm.lineCount())
|
||
return line_candidate;
|
||
}(cm, line),
|
||
// specific
|
||
status: match[2].trim()
|
||
};
|
||
}
|
||
|
||
function rearrange_list(cm, line){
|
||
const line_inferior = find_limit_inferior(cm, line);
|
||
const line_superior = find_limit_superior(cm, line);
|
||
|
||
let last_p = null, p;
|
||
|
||
for(let i=line_inferior; i<=line_superior; i++){
|
||
if(p = isNumberedList(cm, i)){
|
||
// rearrange numbers on the numbered list
|
||
if(last_p){
|
||
if(p.level === last_p.level){
|
||
const tmp = findLastAtLevel(cm, p.start, line_inferior, p.level);
|
||
if(tmp && p.n !== tmp.n + 1) setNumber(cm, p.start, tmp.n + 1);
|
||
}else if(p.level > last_p.level){
|
||
if(p.n !== 1){
|
||
setNumber(cm, p.start, 1);
|
||
}
|
||
}else if(p.level < last_p.level){
|
||
const tmp = findLastAtLevel(cm, p.start, line_inferior, p.level);
|
||
if(tmp && p.n !== tmp.n + 1) setNumber(cm, p.start, tmp.n + 1);
|
||
}
|
||
}else{
|
||
if(p.n !== 1){ setNumber(cm, p.start, 1); }
|
||
}
|
||
}
|
||
|
||
|
||
if(p = (isNumberedList(cm, i) || isItemList(cm, i))){
|
||
// rearrange spacing levels in list
|
||
if(last_p){
|
||
if(p.level > last_p.level){
|
||
if(p.level !== last_p.level + 1){
|
||
setLevel(cm, [p.start, p.end], last_p.level + 1, p.type);
|
||
}
|
||
}
|
||
}else{
|
||
if(p.level !== 0){
|
||
setLevel(cm, [p.start, p.end], 0, p.type);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
last_p = p;
|
||
// we can process content block instead of line
|
||
if(p){
|
||
i += (p.end - p.start);
|
||
}
|
||
}
|
||
|
||
function findLastAtLevel(_cm, line, line_limit_inf, level){
|
||
let p;
|
||
do{
|
||
line -= 1;
|
||
if((p = isNumberedList(_cm, line)) && p.level === level)
|
||
return p;
|
||
}while(line > line_limit_inf);
|
||
|
||
return null;
|
||
}
|
||
|
||
function setLevel(_cm, range, level, type){
|
||
let content, i;
|
||
for(i=range[0]; i<=range[1]; i++){
|
||
content = cm.getLine(i).trimLeft();
|
||
const n_spaces = function(_level, _line, _type){
|
||
let spaces = _level * 3;
|
||
if(_line > 0){
|
||
spaces += _type === 'numbered' ? 3 : 2;
|
||
}
|
||
return spaces;
|
||
}(level, i - range[0], type)
|
||
|
||
content = " ".repeat(n_spaces) + content;
|
||
cm.replaceRange(content, {line: i, ch: 0}, {line: i, ch: _cm.getLine(i).length});
|
||
}
|
||
}
|
||
|
||
function setNumber(_cm, line, level){
|
||
const content = _cm.getLine(line);
|
||
const new_content = content.replace(/[0-9]+\./, level+".");
|
||
cm.replaceRange(new_content, {line: line, ch: 0}, {line: line, ch: content.length});
|
||
}
|
||
|
||
function find_limit_inferior(_cm, _line){
|
||
let content, p, match, line_candidate = _line;
|
||
do{
|
||
content = _cm.getLine(_line);
|
||
p = isNumberedList(_cm, _line);
|
||
match = /(\s+).*$/.exec(content);
|
||
if(p){ line_candidate = _line;}
|
||
if(!p || !match) break;
|
||
_line -= 1;
|
||
}while(_line >= 0);
|
||
return line_candidate;
|
||
}
|
||
function find_limit_superior(_cm, _line){
|
||
let content, p, match, line_candidate = _line;
|
||
do{
|
||
content = _cm.getLine(_line);
|
||
p = isNumberedList(_cm, _line);
|
||
match = /(\s+).*$/.exec(content);
|
||
if(p){ line_candidate = _line;}
|
||
if(!p || !match) break;
|
||
_line += 1;
|
||
}while(_line < _cm.lineCount());
|
||
return line_candidate;
|
||
}
|
||
}
|
||
|
||
function swap(cm, from, to){
|
||
const from_content = cm.getRange({line: from[0], ch: 0}, {line: from[1], ch: cm.getLine(from[1]).length}),
|
||
to_content = cm.getRange({line: to[0], ch: 0}, {line: to[1], ch: cm.getLine(to[1]).length}),
|
||
cursor = cm.getCursor();
|
||
|
||
if(to[0] > from[0]){
|
||
// moving down
|
||
cm.replaceRange(
|
||
from_content,
|
||
{line: to[0], ch:0},
|
||
{line: to[1], ch: cm.getLine(to[1]).length}
|
||
);
|
||
cm.replaceRange(
|
||
to_content,
|
||
{line: from[0], ch:0},
|
||
{line: from[1], ch: cm.getLine(from[1]).length}
|
||
);
|
||
cm.setCursor({
|
||
line: cursor.line + (to[1] - to[0] + 1),
|
||
ch: cursor.ch
|
||
});
|
||
}else{
|
||
// moving up
|
||
cm.replaceRange(
|
||
to_content,
|
||
{line: from[0], ch:0},
|
||
{line: from[1], ch: cm.getLine(from[1]).length}
|
||
);
|
||
cm.replaceRange(
|
||
from_content,
|
||
{line: to[0], ch:0},
|
||
{line: to[1], ch: cm.getLine(to[1]).length}
|
||
);
|
||
cm.setCursor({
|
||
line: cursor.line - (to[1] - to[0] + 1),
|
||
ch: cursor.ch
|
||
});
|
||
}
|
||
}
|
||
|
||
|
||
function isEmpty(content){
|
||
return content.trim() === "";
|
||
}
|
||
|
||
export function fold(cm, start){
|
||
cm.foldCode(start, null, "fold");
|
||
}
|
||
export function unfold(cm, start){
|
||
cm.foldCode(start, null, "unfold");
|
||
}
|
||
export function isFold(cm, start){
|
||
const line = start.line;
|
||
const marks = cm.findMarks(CodeMirror.Pos(line, 0), CodeMirror.Pos(line + 1, 0));
|
||
for (let i = 0; i < marks.length; ++i)
|
||
if (marks[i].__isFold && marks[i].find().from.line == line) return marks[i];
|
||
return false;
|
||
}
|