Skip to content

Commit

Permalink
feat: alternative live preview renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
Fevol committed Aug 1, 2023
1 parent 0b2323a commit af2e03a
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 19 deletions.
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ export const DEFAULT_SETTINGS: PluginSettings = {

post_processor: true,
live_preview: true,
alternative_live_preview: false,
alternative_cursor_movement: true,
};


export const REQUIRES_FULL_RELOAD: Set<string> = new Set([
"preview_mode",
"live_preview",
"alternative_live_preview",
"editor_gutter",

"hide_empty_gutter",
Expand Down
166 changes: 154 additions & 12 deletions src/editor/renderers/live-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,27 @@ import {
WidgetType,
} from '@codemirror/view';

import {
Component, editorEditorField,
editorInfoField,
editorLivePreviewField,
editorViewField,
MarkdownRenderer,
setIcon,
} from 'obsidian';
import { Component, editorLivePreviewField, MarkdownRenderer, setIcon } from 'obsidian';

import type { PluginSettings } from '../../types';
import { NodeType } from '../../types';
import { nodesInSelection, selectionRangeOverlap } from '../editor-util';
import { CriticMarkupNode, SubstitutionNode } from '../criticmarkup-nodes';

export const inlineCommentRenderer = StateField.define<DecorationSet>({
create(state): DecorationSet {
return Decoration.none;
},

update(oldState: DecorationSet, tr: Transaction) {
update(oldSet: DecorationSet, tr: Transaction) {
const is_livepreview = tr.state.field(editorLivePreviewField);
// TODO: Find cleaner way to check if preview was toggled
const preview_changed = is_livepreview !== tr.startState.field(editorLivePreviewField);


// TODO: oldState.size is a bit overkill, since notes without any comment nodes will always parse the document?
if (!tr.docChanged && !preview_changed && oldState.size)
return oldState;
// TODO: oldSet.size is a bit overkill, since notes without any comment nodes will always parse the document?
if (!tr.docChanged && !preview_changed && oldSet.size)
return oldSet;

if (preview_changed && !is_livepreview)
return Decoration.none;
Expand Down Expand Up @@ -137,6 +131,154 @@ class CommentIconWidget extends WidgetType {
}
}

function removeBrackets(decorations: Range<Decoration>[], node: CriticMarkupNode) {
decorations.push(
Decoration.replace({
attributes: { 'data-contents': 'string' },
}).range(node.from, node.from + 3)
);
decorations.push(
Decoration.replace({
attributes: { 'data-contents': 'string' },
}).range(node.to - 3, node.to)
);
}

function removeBracket(decorations: Range<Decoration>[], node: CriticMarkupNode, left: boolean) {
if (left)
decorations.push(
Decoration.replace({
attributes: { 'data-contents': 'string' },
}).range(node.from, node.from + 3)
);
else
decorations.push(
Decoration.replace({
attributes: { 'data-contents': 'string' },
}).range(node.to - 3, node.to)
);
}

function hideNode(decorations: Range<Decoration>[], node: CriticMarkupNode) {
decorations.push(
Decoration.replace({}).range(node.from, node.to)
);
}

function markContents(decorations: Range<Decoration>[], node: CriticMarkupNode, style: string, left: boolean | null = null) {
if (node.type === NodeType.SUBSTITUTION) {
if (left) {
if (!node.part_is_empty(true)) {
decorations.push(
Decoration.mark({
attributes: { 'data-contents': 'string' },
class: style,
}).range(node.from + 3, (node as SubstitutionNode).middle),
);
}
} else {
if (!node.part_is_empty(false)) {
decorations.push(
Decoration.mark({
attributes: { 'data-contents': 'string' },
class: style,
}).range((node as SubstitutionNode).middle + 2, node.to - 3),
);
}
}
} else {
if (!node.empty()) {
decorations.push(
Decoration.mark({
attributes: { 'data-contents': 'string' },
class: style,
}).range(node.from + 3, node.to - 3)
);
}
}
}



export const livePreviewRenderer = (settings: PluginSettings) => StateField.define<DecorationSet>({
create(state): DecorationSet {
return Decoration.none;
},

update(oldSet: DecorationSet, tr: Transaction) {
const is_livepreview = tr.state.field(editorLivePreviewField);
const tree = tr.state.field(treeParser).tree;
const nodes = nodesInSelection(tree);

if (!is_livepreview)
return Decoration.none;

// const builder = new RangeSetBuilder<Decoration>();
const decorations: Range<Decoration>[] = [];

for (const node of nodes.nodes) {
if (!settings.preview_mode) {
if (tr.selection?.ranges?.some(range => node.partially_in_range(range.from, range.to))) {
markContents(decorations, node, 'criticmarkup-editing');
} else if (node.type === NodeType.SUBSTITUTION) {
removeBracket(decorations, node, true);
markContents(decorations, node, 'criticmarkup-editing criticmarkup-inline criticmarkup-deletion criticmarkup-substitution', true)
decorations.push(
Decoration.replace({
attributes: { 'data-contents': 'string' },
}).range((node as SubstitutionNode).middle, (node as SubstitutionNode).middle + 2)
);
markContents(decorations, node, 'criticmarkup-editing criticmarkup-inline criticmarkup-addition criticmarkup-substitution', false)
removeBracket(decorations, node, false);
} else {
removeBracket(decorations, node, true);
markContents(decorations, node, `criticmarkup-editing criticmarkup-inline criticmarkup-${node.repr.toLowerCase()}`);
removeBracket(decorations, node, false);
}
} else if (settings.preview_mode === 1) {
if (node.type === NodeType.ADDITION) {
removeBracket(decorations, node, true);
markContents(decorations, node, 'criticmarkup-accepted')
removeBracket(decorations, node, false);
} else if (node.type === NodeType.DELETION) {
// markContents(decorations, node, 'rejected')
hideNode(decorations, node);
} else if (node.type === NodeType.SUBSTITUTION) {
decorations.push(Decoration.replace({}).range(node.from, node.from + 3));
markContents(decorations, node, 'criticmarkup-accepted', true)
// markContents(decorations, node, 'rejected', false)
decorations.push(Decoration.replace({}).range((node as SubstitutionNode).middle, node.to));
} else {
removeBrackets(decorations, node);
}
} else if (settings.preview_mode === 2) {
if (node.type === NodeType.ADDITION) {
// markContents(decorations, node, 'rejected');
hideNode(decorations, node);
} else if (node.type === NodeType.DELETION) {
removeBracket(decorations, node, true);
markContents(decorations, node, 'criticmarkup-accepted')
removeBracket(decorations, node, false);
} else if (node.type === NodeType.SUBSTITUTION) {
decorations.push(Decoration.replace({}).range(node.from, (node as SubstitutionNode).middle + 2));
// markContents(decorations, node, 'rejected', true);
markContents(decorations, node, 'criticmarkup-accepted', false);
decorations.push(Decoration.replace({}).range(node.to - 3, node.to));
} else {
removeBrackets(decorations, node);
}
}
}
return Decoration.set(decorations);
},

provide(field: StateField<DecorationSet>): Extension {
return EditorView.decorations.from(field);
}
});



export function livePreview (settings: PluginSettings): Extension {
return ViewPlugin.fromClass(
class CriticMarkupViewPlugin implements PluginValue {
Expand Down
17 changes: 10 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { change_suggestions } from './editor/context-menu-commands';
import { treeParser } from './editor/tree-parser';
import { nodesInSelection } from './editor/editor-util';

import { inlineCommentRenderer, livePreview } from './editor/renderers/live-preview';
import { inlineCommentRenderer, livePreview, livePreviewRenderer } from './editor/renderers/live-preview';
import { postProcess, postProcessorUpdate } from './editor/renderers/post-processor';

import { keybindExtensions } from './editor/suggestion-mode/keybinds';
Expand Down Expand Up @@ -56,9 +56,12 @@ export default class CommentatorPlugin extends Plugin {
this.editorExtensions.push(treeParser);
this.editorExtensions.push(inlineCommentRenderer);

if (this.settings.live_preview)
this.editorExtensions.push(livePreview(this.settings));

if (this.settings.live_preview) {
if (this.settings.alternative_live_preview)
this.editorExtensions.push(livePreviewRenderer(this.settings));
else
this.editorExtensions.push(livePreview(this.settings));
}

// Performance: ~3ms in stress-test
if (this.settings.editor_gutter)
Expand All @@ -84,7 +87,7 @@ export default class CommentatorPlugin extends Plugin {
event.clipboardData.setData('text/plain', removed_syntax);
event.preventDefault();
}
}
},
}));

}
Expand Down Expand Up @@ -147,7 +150,7 @@ export default class CommentatorPlugin extends Plugin {
icon: 'comment',
callback: async () => {
this.app.vault.setConfig('vimMode', !this.app.vault.getConfig('vimMode'));
}
},
});

for (const command of commands) {
Expand All @@ -169,7 +172,7 @@ export default class CommentatorPlugin extends Plugin {
if (result && !args[0])
this.loadEditorExtensions();
return result;
}
};
},
}));
});
Expand Down
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ export interface PluginSettings {
* Enable live preview rendering
*/
live_preview: boolean;
/** Enable alternative live preview renderer */
alternative_live_preview: boolean;

/**
* Enable corrected cursor movement near/within nodes
*/
Expand Down
11 changes: 11 additions & 0 deletions src/ui/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ export class CommentatorSettings extends PluginSettingTab {
}
));

new Setting(containerEl)
.setName("Alternative Live preview renderer")
.setDesc("May bring worse performance, but offers a more accurate preview")
.addToggle(toggle => toggle.setValue(this.plugin.settings.alternative_live_preview)
.onChange(async (value) => {
this.plugin.settings.alternative_live_preview = value;
await this.plugin.saveSettings();
}
));


new Setting(containerEl)
.setName("Alternative cursor movement")
.setDesc("Toggle corrected cursor movement of cursor when CriticMarkup tags are present")
Expand Down

0 comments on commit af2e03a

Please sign in to comment.