Skip to content

Commit

Permalink
fix: solve document parsing issue (fixes #3)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fevol committed Mar 11, 2023
1 parent b951342 commit e14da9b
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 90 deletions.
33 changes: 17 additions & 16 deletions src/editor/commands.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Editor, MarkdownView } from 'obsidian';

import { treeParser } from './tree-parser';

import type { ChangeSpec } from '@codemirror/state';
import { EditorSelection } from '@codemirror/state';
import { EditorSelection, EditorState } from '@codemirror/state';
import type { Tree } from '@lezer/common';

import { criticmarkupLanguage } from './parser';

import type { CommandI } from '../../types';

import { addBracket, unwrapBracket, wrapBracket } from '../util';
Expand All @@ -14,7 +14,7 @@ import { ltEP, minEP, maxEP, nodesInSelection, selectionToRange } from './editor

function changeSelectionType(editor: Editor, view: MarkdownView, type: string) {
// @ts-ignore
const tree: Tree = criticmarkupLanguage.parser.parse(editor.getValue(), []);
const tree: Tree = editor.cm.state.field(treeParser).tree;

const selection = editor.listSelections()[0];
if (!selection) return;
Expand All @@ -27,6 +27,7 @@ function changeSelectionType(editor: Editor, view: MarkdownView, type: string) {

const nodes = nodesInSelection(tree, selection_left, selection_right);

// TODO: Replace editor.replaceSelection with CM equivalents

// CASE 0: Selection is empty
if (selection_left === selection_right) {
Expand Down Expand Up @@ -218,9 +219,11 @@ function changeSelectionType(editor: Editor, view: MarkdownView, type: string) {
}


export function acceptAllSuggestions(text: string, from?: number, to?: number): ChangeSpec[] {
export function acceptAllSuggestions(state: EditorState, from?: number, to?: number): ChangeSpec[] {
// @ts-ignore
const tree: Tree = criticmarkupLanguage.parser.parse(text, []);
const tree: Tree = state.field(treeParser).tree;
const text = state.doc.toString();

const nodes = nodesInSelection(tree, from, to);
const changes: ChangeSpec[] = [];
for (const node of nodes) {
Expand All @@ -235,9 +238,11 @@ export function acceptAllSuggestions(text: string, from?: number, to?: number):
}


export function rejectAllSuggestions(text: string, from?: number, to?: number): ChangeSpec[] {
export function rejectAllSuggestions(state: EditorState, from?: number, to?: number): ChangeSpec[] {
// @ts-ignore
const tree: Tree = criticmarkupLanguage.parser.parse(text, []);
const tree: Tree = state.field(treeParser).tree;
const text = state.doc.toString();

const nodes = nodesInSelection(tree, from, to).reverse();
const changes: ChangeSpec[] = [];
for (const node of nodes) {
Expand Down Expand Up @@ -270,9 +275,8 @@ export const commands: Array<CommandI> = [...suggestion_commands,
icon: 'check-check',
editor_context: true,
callback: async (editor: Editor, view: MarkdownView) => {
// @ts-ignore (editor.cm.dispatch exists)
editor.cm.dispatch(editor.cm.state.update({
changes: acceptAllSuggestions(editor.getValue()),
changes: acceptAllSuggestions(editor.cm.state),
}));
},
}, {
Expand All @@ -281,9 +285,8 @@ export const commands: Array<CommandI> = [...suggestion_commands,
icon: 'cross',
editor_context: true,
callback: async (editor: Editor, view: MarkdownView) => {
// @ts-ignore (editor.cm.dispatch exists)
editor.cm.dispatch(editor.cm.state.update({
changes: rejectAllSuggestions(editor.getValue()),
changes: rejectAllSuggestions(editor.cm.state),
}));
},
},
Expand All @@ -294,9 +297,8 @@ export const commands: Array<CommandI> = [...suggestion_commands,
editor_context: true,
callback: async (editor: Editor, view: MarkdownView) => {
const [from, to] = selectionToRange(editor);
// @ts-ignore (editor.cm.dispatch exists)
editor.cm.dispatch(editor.cm.state.update({
changes: acceptAllSuggestions(editor.getValue(), from, to),
changes: acceptAllSuggestions(editor.cm.state, from, to),
}));
},
},
Expand All @@ -307,9 +309,8 @@ export const commands: Array<CommandI> = [...suggestion_commands,
editor_context: true,
callback: async (editor: Editor, view: MarkdownView) => {
const [from, to] = selectionToRange(editor);
// @ts-ignore (editor.cm.dispatch exists)
editor.cm.dispatch(editor.cm.state.update({
changes: rejectAllSuggestions(editor.getValue(), from, to),
changes: rejectAllSuggestions(editor.cm.state, from, to),
}));
},
},
Expand Down
4 changes: 2 additions & 2 deletions src/editor/context-menu-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const change_suggestions: EventRef =
const [from, to] = selectionToRange(editor);
// @ts-ignore (editor.cm.dispatch exists)
editor.cm.dispatch(editor.cm.state.update({
changes: acceptAllSuggestions(editor.getValue(), from, to),
changes: acceptAllSuggestions(editor.cm.state, from, to),
}));
});
});
Expand All @@ -27,7 +27,7 @@ export const change_suggestions: EventRef =
const [from, to] = selectionToRange(editor);
// @ts-ignore (editor.cm.dispatch exists)
editor.cm.dispatch(editor.cm.state.update({
changes: rejectAllSuggestions(editor.getValue(), from, to),
changes: rejectAllSuggestions(editor.cm.state, from, to),
}));
});
});
Expand Down
20 changes: 12 additions & 8 deletions src/editor/criticmarkup-gutter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Menu } from 'obsidian';

import type { Tree } from '@lezer/common';
import { RangeSet, RangeSetBuilder } from '@codemirror/state';
import { EditorView, gutter, GutterMarker, PluginValue, ViewPlugin } from '@codemirror/view';
import { EditorView, gutter, GutterMarker } from '@codemirror/view';

import { treeParser } from './tree-parser';

import { acceptAllSuggestions, rejectAllSuggestions } from './commands';

import { nodesInSelection } from './editor-util';


export class CriticMarkupMarker extends GutterMarker {
constructor(readonly from: number, readonly to: number, readonly type: string, readonly top?: boolean, readonly bottom?: boolean) {
super();
Expand All @@ -21,11 +25,11 @@ export class CriticMarkupMarker extends GutterMarker {
}
}

export function buildMarkers(view: EditorView, extension: PluginValue): RangeSet<CriticMarkupMarker> {
function buildMarkers(view: EditorView, tree: Tree): RangeSet<CriticMarkupMarker> {
const builder = new RangeSetBuilder<CriticMarkupMarker>();

// @ts-ignore (Get tree from extension)
let nodes: any[] = nodesInSelection(extension.tree);
let nodes: any[] = nodesInSelection(tree);
nodes = nodes.map(node => {
node.line_start = view.state.doc.lineAt(node.from).number;
node.line_end = view.state.doc.lineAt(node.to).number;
Expand Down Expand Up @@ -53,11 +57,11 @@ export function buildMarkers(view: EditorView, extension: PluginValue): RangeSet
}


export const gutterExtension = (view_plugin: ViewPlugin<PluginValue>) => gutter({
export const gutterExtension = () => gutter({
class: 'criticmarkup-gutter',
markers(view: EditorView) {
// @ts-ignore (Markers exist on viewplugin)
return view.plugin(view_plugin)?.markers ?? RangeSet.empty;
// @ts-ignore (Tree gotten from state field)
return buildMarkers(view, view.state.field(treeParser).tree) ?? RangeSet.empty;
},
domEventHandlers: {
click: (view, line, event: Event) => {
Expand All @@ -67,7 +71,7 @@ export const gutterExtension = (view_plugin: ViewPlugin<PluginValue>) => gutter(
.setIcon('check')
.onClick(() => {
view.dispatch({
changes: acceptAllSuggestions(view.state.doc.toString(), line.from, line.to),
changes: acceptAllSuggestions(view.state, line.from, line.to),
});
});

Expand All @@ -77,7 +81,7 @@ export const gutterExtension = (view_plugin: ViewPlugin<PluginValue>) => gutter(
.setIcon('cross')
.onClick(() => {
view.dispatch({
changes: rejectAllSuggestions(view.state.doc.toString(), line.from, line.to),
changes: rejectAllSuggestions(view.state, line.from, line.to),
});
});

Expand Down
5 changes: 3 additions & 2 deletions src/editor/editor-handlers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EditorView } from '@codemirror/view';
import { ChangeSpec, EditorSelection, EditorState, Prec } from '@codemirror/state';

import { criticmarkupLanguage } from './parser';
import { treeParser } from './tree-parser';

import { CM_Brackets } from '../util';
import { moveEditorCursor, nodeAtCursor } from './editor-util';
Expand Down Expand Up @@ -34,7 +34,8 @@ export const nodeCorrecter = EditorState.transactionFilter.of(tr => {

if (current_selection.main.anchor === current_selection.main.head) {
const text = tr.startState.doc.toString();
const tree = criticmarkupLanguage.parser.parse(text);
// @ts-ignore (Tree is correct)
const tree = tr.startState.field(treeParser).tree;

// @ts-ignore (Tree is correct)
const start_node = nodeAtCursor(tree, previous_selection.main.head);
Expand Down
26 changes: 24 additions & 2 deletions src/editor/editor-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Editor, EditorPosition } from 'obsidian';

import type { Tree } from '@lezer/common';
import { EditorSelection } from '@codemirror/state';
import type { CriticMarkupNode } from '../types';


export function eqEP(a: EditorPosition, b: EditorPosition): boolean {
Expand Down Expand Up @@ -63,7 +64,7 @@ export function nodeAtCursor(tree: Tree, pos: number) {


export function nodesInSelection(tree: Tree, start?: number, end?: number) {
const nodes: { from: number, middle?: number, to: number, type: string }[] = [];
const nodes: CriticMarkupNode[] = [];

tree.iterate({
from: start,
Expand Down Expand Up @@ -92,4 +93,25 @@ export function nodesInSelection(tree: Tree, start?: number, end?: number) {
},
});
return nodes;
}
}

export function nodeAtCursorLocation(nodes: CriticMarkupNode[], pos: number) {
return nodes.find(node => node.from <= pos && node.to >= pos);
}

export function adjacentNode(nodes: CriticMarkupNode[], pos: number, left: boolean) {
if (left)
return nodes.reverse().find(node => node.to <= pos);
return nodes.find(node => node.from >= pos);
}

export function siblingNode(nodes: CriticMarkupNode[], node: CriticMarkupNode, left: boolean) {
const index = nodes.indexOf(node);
if (index === -1)
return undefined;
if (left)
return nodes[index - 1];
return nodes[index + 1];
}


70 changes: 17 additions & 53 deletions src/editor/live-preview.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,23 @@
import type { Tree } from '@lezer/common';
import { TreeFragment } from '@lezer/common';
import { treeParser } from './tree-parser';

import { RangeSet } from '@codemirror/state';
import type { Tree } from '@lezer/common';
import type { Extension, Range } from '@codemirror/state';
import { Decoration, DecorationSet, EditorView, PluginValue, ViewPlugin, ViewUpdate } from '@codemirror/view';

import { buildMarkers, CriticMarkupMarker, gutterExtension } from './criticmarkup-gutter';

import { criticmarkupLanguage } from './parser';

import type { PluginSettings } from '../types';
import { selectionRangeOverlap } from './editor-util';

export function inlinePlugin(settings: PluginSettings): Extension[] {
const view_plugin = ViewPlugin.fromClass(
export function livePreview (settings: PluginSettings): Extension {
return ViewPlugin.fromClass(
class CriticMarkupViewPlugin implements PluginValue {
settings: PluginSettings;
markers: RangeSet<CriticMarkupMarker>;
decorations: DecorationSet;
tree: Tree;
fragments: TreeFragment[] = [];

constructor(view: EditorView) {
this.settings = settings;

// @ts-ignore (Conflicting Tree definitions of node modules in src/editor/parser and ./)
this.tree = criticmarkupLanguage.parser.parse(view.state.doc.toString());
// @ts-ignore
this.fragments = TreeFragment.addTree(this.tree);

this.decorations = (this.settings.live_preview ? this.buildDecorations(view) : null) ?? Decoration.none;

this.markers = (this.settings.editor_gutter ? buildMarkers(view, this) : RangeSet.empty);
const tree = view.state.field(treeParser).tree;
this.settings = settings;
this.decorations = (this.settings.live_preview ? this.buildDecorations(tree, view) : null) ?? Decoration.none;
}

removeBrackets(widgets: Range<Decoration>[], from: number, to: number) {
Expand All @@ -48,11 +34,11 @@ export function inlinePlugin(settings: PluginSettings): Extension[] {
);
}

buildDecorations(view: EditorView): DecorationSet {
buildDecorations(tree: Tree, view: EditorView): DecorationSet {
const widgets: Range<Decoration>[] = [];
const selection = view.state.selection;

const cursor = this.tree.cursor();
const cursor = tree.cursor();
while (cursor.next()) {
const start = cursor.from;
const end = cursor.to;
Expand Down Expand Up @@ -218,45 +204,23 @@ export function inlinePlugin(settings: PluginSettings): Extension[] {
return Decoration.set(widgets, true);
}

update(update: ViewUpdate) {
async update(update: ViewUpdate) {
// @ts-ignore (Returns Tree object)
const tree = update.state.field(treeParser).tree;

// @ts-ignore
const tree = criticmarkupLanguage.parser.parse(update.state.doc.toString(), this.fragments);
if (tree.length < update.view.viewport.to || update.view.composing)
this.decorations = this.decorations.map(update.changes);
// TODO: Figure out how to implement the 'hasEffect' helper function, or determine if it is even necessary
// @ts-ignore
else if (tree != this.tree ||
update.viewportChanged || update.selectionSet /*||
hasEffect(update.transactions, rerenderEffect) ||
hasEffect(update.transactions, addMarks) || hasEffect(update.transactions, filterMarks)*/) {

// @ts-ignore
this.tree = tree;

// @ts-ignore
this.fragments = TreeFragment.addTree(tree, this.fragments);
/* hasEffect(update.transactions, rerenderEffect) || hasEffect(update.transactions, addMarks) ||
hasEffect(update.transactions, filterMarks)*/

// If tree has any CriticMarkup nodes, build decorations
if (this.tree.topNode.firstChild) {
if (this.settings.live_preview)
this.decorations = this.buildDecorations(update.view);
if (this.settings.editor_gutter)
this.markers = buildMarkers(update.view, this);
} else {
this.decorations = Decoration.none;
this.markers = RangeSet.empty;
}
}
else if (update.viewportChanged || update.selectionSet)
this.decorations = tree.topNode.firstChild ? this.buildDecorations(tree, update.view) : Decoration.none;
}
},
{
decorations: (v) => v.decorations,
},
);

if (settings.editor_gutter) {
const gutter_extension = gutterExtension(view_plugin);
return [view_plugin, gutter_extension];
}
return [view_plugin];
}
Loading

0 comments on commit e14da9b

Please sign in to comment.