Skip to content

Commit

Permalink
Resolve #3: Automatically delete hidden braces when escaping from an …
Browse files Browse the repository at this point in the history
…inline math
  • Loading branch information
RyotaUshio committed Nov 24, 2023
1 parent 85d81e8 commit 6feb0fb
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 38 deletions.
102 changes: 75 additions & 27 deletions src/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChangeSpec, EditorState } from '@codemirror/state';
import { ChangeSpec, EditorState, EditorSelection, ChangeSet } from '@codemirror/state';
import { syntaxTree } from '@codemirror/language';

import { isInlineMathBegin, isInlineMathEnd, printNode } from './utils';
Expand All @@ -15,21 +15,12 @@ export function getChangesForDeletion(state: EditorState): ChangeSpec {
const to = range.to;
const text = state.sliceDoc(from, to)
const index = text.lastIndexOf("$");
// console.log(
// `range: ${range.from}-${range.to}\n`
// + `from = ${from} \ to = ${to}\n`
// + `text = "${text}"\n`
// + `index = ${index}`
// );
if (index == -1) {
continue;
}

const indexNextDollar = doc.indexOf("$", from + index + 1);
const indexPrevDollar = doc.lastIndexOf("$", from);
// console.log(
// `!! ${indexPrevDollar}:${indexNextDollar}: "${state.sliceDoc(indexPrevDollar, indexNextDollar)}"`
// );

tree.iterate({
from: indexPrevDollar,
Expand All @@ -50,31 +41,88 @@ export function getChangesForDeletion(state: EditorState): ChangeSpec {
return changes;
}

export function getChangesForInsertion(state: EditorState): ChangeSpec {
export function getChangesForInsertion(state: EditorState, changes: ChangeSet): ChangeSpec {
const tree = syntaxTree(state);
const doc = state.doc.toString();
let changes: ChangeSpec[] = [];
let changesToAdd: ChangeSpec[] = [];

const changesWithLeadingWhitespace: Map<number, boolean> = new Map();
changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
if (inserted.toString().startsWith(' ')) {
changesWithLeadingWhitespace.set(fromA, true);
}
});

for (const range of state.selection.ranges) {
if (range.from >= 1) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf#parameters
// "If position is less than 0, the behavior is the same as for 0"
const indexPrevDollar = doc.lastIndexOf("$", range.from - 1);

if (indexPrevDollar >= 0) {
const node = tree.cursorAt(indexPrevDollar, 1).node;
if (isInlineMathBegin(node, state)) {
if (indexPrevDollar === range.from - 1 && changesWithLeadingWhitespace.has(range.from)) {
changesToAdd.push({ from: indexPrevDollar, to: range.from, insert: "${} " });
continue;
}

if (state.sliceDoc(node.to, node.to + 3) !== "{} ") {
changesToAdd.push({ from: node.to, insert: "{} " });
}
}
else if (isInlineMathEnd(node, state) && state.sliceDoc(node.from - 3, node.from) === " {}") {
const openIndex = doc.lastIndexOf("${} ", node.from - 3);
changesToAdd.push({ from: openIndex + 1, to: node.from, insert: doc.slice(openIndex + 4, node.from - 3).trim() });
}
}
}

const indexNextDollar = doc.indexOf("$", range.to);
if (indexNextDollar >= 0) {
const node = tree.cursorAt(indexNextDollar, 1).node;
if (isInlineMathEnd(node, state)) {
if (state.sliceDoc(node.from - 3, node.from) !== " {}") {
changesToAdd.push({ from: node.from, insert: " {}" });
}
}
else if (isInlineMathBegin(node, state) && state.sliceDoc(node.to, node.to + 3) === "{} ") {
const closeIndex = doc.indexOf(" {}$", node.to + 3);
if (closeIndex >= 0) {
changesToAdd.push({ from: node.to, to: closeIndex + 3, insert: doc.slice(node.to + 3, closeIndex).trim() });
}
}
}
}

return changesToAdd;
}

export function getChangesForSelection(state: EditorState, newSelection: EditorSelection): ChangeSpec {
const tree = syntaxTree(state);
const doc = state.doc.toString();
let changes: ChangeSpec[] = [];

for (const range of newSelection.ranges) {
const indexNextDollar = doc.indexOf("$", range.to);
const indexPrevDollar = doc.lastIndexOf("$", range.from);
const indexPrevDollar = doc.lastIndexOf("$", range.from - 1);

if (indexPrevDollar >= 0) {
const node = tree.cursorAt(indexPrevDollar, 1).node;
if (isInlineMathEnd(node, state) && state.sliceDoc(node.from - 3, node.from) === " {}") {
const openIndex = doc.lastIndexOf("${} ", node.from - 3);
changes.push({ from: openIndex + 1, to: node.from, insert: doc.slice(openIndex + 4, node.from - 3).trim() });
}
}

if (indexNextDollar >= 0) {
tree.iterate({
from: indexPrevDollar,
to: indexNextDollar + 1,
enter(node) {
if (isInlineMathBegin(node, state)) {
if (!(state.sliceDoc(node.to, node.to + 3) == "{} ")) {
changes.push({ from: node.to, insert: "{} " });
}
} else if (isInlineMathEnd(node, state)) {
if (!(state.sliceDoc(node.from - 3, node.from) == " {}")) {
changes.push({ from: node.from, insert: " {}" });
}
}
const node = tree.cursorAt(indexNextDollar, 1).node;
if (isInlineMathBegin(node, state) && state.sliceDoc(node.to, node.to + 3) === "{} ") {
const closeIndex = doc.indexOf(" {}$", node.to + 3);
if (closeIndex >= 0) {
changes.push({ from: node.to, to: closeIndex + 3, insert: doc.slice(node.to + 3, closeIndex).trim() });
}
});
}
}
}

Expand Down
31 changes: 20 additions & 11 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { EditorState, Transaction } from '@codemirror/state';
import { EditorState, Transaction, ChangeSet, TransactionSpec } from '@codemirror/state';
import { MarkdownView, Plugin } from 'obsidian';
import { Extension } from '@codemirror/state';

import { DEFAULT_SETTINGS, NoMoreFlickerSettingTab, NoMoreFlickerSettings } from './settings';
import { getChangesForDeletion, getChangesForInsertion } from './handlers';
import { getChangesForDeletion, getChangesForInsertion, getChangesForSelection } from './handlers';
import { cleanerCallback } from 'cleaner';
import { createViewPlugin } from 'decoration_and_atomic-range';
import { selectionSatisfies } from 'utils';
Expand All @@ -16,7 +16,7 @@ export default class NoMoreFlicker extends Plugin {
async onload() {

/** Settings */

await this.loadSettings();
await this.saveSettings();
this.addSettingTab(new NoMoreFlickerSettingTab(this.app, this));
Expand All @@ -30,7 +30,7 @@ export default class NoMoreFlicker extends Plugin {


/** Clean-up commands */

this.addCommand({
id: "clean",
name: "Clean up braces in this note",
Expand All @@ -57,13 +57,22 @@ export default class NoMoreFlicker extends Plugin {
if (this.shouldIgnore(tr.startState)) {
return tr;
}
const userEvent = tr.annotation(Transaction.userEvent);
if (userEvent?.split('.')[0] == 'input') {
const changes = getChangesForInsertion(tr.startState);
return [tr, { changes }];
} else if (userEvent?.split('.')[0] == 'delete') {
const changes = getChangesForDeletion(tr.startState);
return [tr, { changes }];
const userEvent = tr.annotation(Transaction.userEvent)?.split('.')[0];
if (userEvent) {
if (userEvent === 'input') {
const changes = getChangesForInsertion(tr.startState, tr.changes);
return [tr, { changes }];
}
else if (userEvent === 'select' && tr.selection) {
// Even if we cannot access to the new state (tr.state),
// we can still access to the new selection (tr.selection)!!
const changes = getChangesForSelection(tr.startState, tr.selection);
return [tr, { changes }];
}
else if (userEvent === 'delete') {
const changes = getChangesForDeletion(tr.startState);
return [tr, { changes }];
}
}
return tr;
});
Expand Down

0 comments on commit 6feb0fb

Please sign in to comment.