Skip to content

Commit

Permalink
Merge branch 'deco-update'
Browse files Browse the repository at this point in the history
  • Loading branch information
RyotaUshio committed Oct 7, 2023
2 parents 5789f9b + 8fea4ae commit 49a2905
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 59 deletions.
2 changes: 1 addition & 1 deletion src/ui/lp-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { DataviewInlineApi } from "../api/inline-api";
import { renderValue } from "./render";
import { SyntaxNode } from "@lezer/common";

function selectionAndRangeOverlap(selection: EditorSelection, rangeFrom: number, rangeTo: number) {
export function selectionAndRangeOverlap(selection: EditorSelection, rangeFrom: number, rangeTo: number) {
for (const range of selection.ranges) {
if (range.from <= rangeTo && range.to >= rangeFrom) {
return true;
Expand Down
150 changes: 92 additions & 58 deletions src/ui/views/inline-field-live-preview.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { App, Component, editorInfoField, editorLivePreviewField } from "obsidian";
import { App, Component, TFile, editorInfoField, editorLivePreviewField } from "obsidian";
import { EditorState, RangeSet, RangeSetBuilder, RangeValue, StateEffect, StateField } from "@codemirror/state";
import {
Decoration,
Expand All @@ -13,11 +13,16 @@ import { InlineField, extractInlineFields, parseInlineValue } from "data-import/
import { canonicalizeVarName } from "util/normalize";
import { renderCompactMarkdown, renderValue } from "ui/render";
import { DataviewSettings } from "settings";
import { selectionAndRangeOverlap } from "ui/lp-render";

class InlineFieldValue extends RangeValue {
constructor(public field: InlineField) {
super();
}

eq(other: InlineFieldValue): boolean {
return this.field.key == other.field.key && this.field.value == other.field.value;
}
}

function buildInlineFields(state: EditorState): RangeSet<InlineFieldValue> {
Expand Down Expand Up @@ -46,69 +51,38 @@ export const replaceInlineFieldsInLivePreview = (app: App, settings: DataviewSet
ViewPlugin.fromClass(
class implements PluginValue {
decorations: DecorationSet;
overlappingIndices: number[];
component: Component;

constructor(view: EditorView) {
this.decorations = this.buildDecoration(view);
this.overlappingIndices = this.getOverlappingIndices(view.state);
this.component = new Component();
this.component.load();
this.decorations = this.buildDecorations(view);
}

update(update: ViewUpdate): void {
// To reduce the total number of updating the decorations, we only update if
// the state of overlapping (i.e. which inline field is overlapping with the cursor) has changed
// except when the document has changed or the viewport has changed.

const oldIndices = this.overlappingIndices;
const newIndices = this.getOverlappingIndices(update.state);

const overlapChanged =
update.startState.field(inlineFieldsField).size != update.state.field(inlineFieldsField).size ||
JSON.stringify(oldIndices) != JSON.stringify(newIndices);

this.overlappingIndices = newIndices;

const layoutChanged = update.transactions.some(transaction =>
transaction.effects.some(effect => effect.is(workspaceLayoutChangeEffect))
);

if (update.state.field(editorLivePreviewField)) {
if (update.docChanged || update.viewportChanged || layoutChanged || overlapChanged) {
this.decorations = this.buildDecoration(update.view);
}
} else {
this.decorations = Decoration.none;
}
destroy() {
this.component.unload();
}

buildDecoration(view: EditorView): DecorationSet {
buildDecorations(view: EditorView): DecorationSet {
// Disable in the source mode
if (!view.state.field(editorLivePreviewField)) return Decoration.none;

const markdownView = view.state.field(editorInfoField);
if (!(markdownView instanceof Component)) {
// For a canvas card not assosiated with a note in the vault,
// editorInfoField is not MarkdownView, which inherits from the Component class.
// A component object is required to pass to MarkdownRenderer.render.
return Decoration.none;
}

const file = markdownView.file;
const file = view.state.field(editorInfoField).file;
if (!file) return Decoration.none;

const info = view.state.field(inlineFieldsField);
const builder = new RangeSetBuilder<Decoration>();
const selection = view.state.selection.main;
const selection = view.state.selection;

let x = 0;
for (const { from, to } of view.visibleRanges) {
info.between(from, to, (start, end, { field }) => {
// If the inline field is not overlapping with the cursor, we replace it with a widget.
if (start > selection.to || end < selection.from) {
if (selectionAndRangeOverlap(selection, start, end)) {
builder.add(
start,
end,
Decoration.replace({
widget: new InlineFieldWidget(app, field, x++, file.path, markdownView, settings),
widget: new InlineFieldWidget(app, field, file.path, this.component, settings),
})
);
}
Expand All @@ -117,19 +91,78 @@ export const replaceInlineFieldsInLivePreview = (app: App, settings: DataviewSet
return builder.finish();
}

getOverlappingIndices(state: EditorState): number[] {
const selection = state.selection.main;
const cursor = state.field(inlineFieldsField).iter();
const indices: number[] = [];
let i = 0;
while (cursor.value) {
if (cursor.from <= selection.to && cursor.to >= selection.from) {
indices.push(i);
}
cursor.next();
i++;
update(update: ViewUpdate) {
// only activate in LP and not source mode
if (!update.state.field(editorLivePreviewField)) {
this.decorations = Decoration.none;
return;
}

const layoutChanged = update.transactions.some(transaction =>
transaction.effects.some(effect => effect.is(workspaceLayoutChangeEffect))
);

if (update.docChanged) {
this.decorations = this.decorations.map(update.changes);
this.updateDecorations(update.view);
} else if (update.selectionSet) {
this.updateDecorations(update.view);
} else if (update.viewportChanged || layoutChanged) {
this.decorations = this.buildDecorations(update.view);
}
}

updateDecorations(view: EditorView) {
const file = view.state.field(editorInfoField).file;
if (!file) {
this.decorations = Decoration.none;
return;
}

const inlineFields = view.state.field(inlineFieldsField);
const selection = view.state.selection;

for (const { from, to } of view.visibleRanges) {
inlineFields.between(from, to, (start, end, { field }) => {
const overlap = selectionAndRangeOverlap(selection, start, end);
if (overlap) {
this.removeDeco(start, end);
return;
} else {
this.addDeco(start, end, field, file);
}
});
}
}

removeDeco(start: number, end: number) {
this.decorations.between(start, end, (from, to) => {
this.decorations = this.decorations.update({
filterFrom: from,
filterTo: to,
filter: () => false,
});
});
}

addDeco(start: number, end: number, field: InlineField, file: TFile) {
let exists = false;
this.decorations.between(start, end, () => {
exists = true;
});
if (!exists) {
this.decorations = this.decorations.update({
add: [
{
from: start,
to: end,
value: Decoration.replace({
widget: new InlineFieldWidget(app, field, file.path, this.component, settings),
}),
},
],
});
}
return indices;
}
},
{
Expand All @@ -142,14 +175,17 @@ class InlineFieldWidget extends WidgetType {
constructor(
public app: App,
public field: InlineField,
public id: number,
public sourcePath: string,
public parentComponent: Component,
public settings: DataviewSettings
) {
super();
}

eq(other: InlineFieldWidget): boolean {
return this.field.key == other.field.key && this.field.value == other.field.value;
}

toDOM() {
// A large part of this method was taken from replaceInlineFields() in src/ui/views/inline-field.tsx.
// It will be better to extract the common part as a function...
Expand All @@ -172,7 +208,6 @@ class InlineFieldWidget extends WidgetType {

const value = renderContainer.createSpan({
cls: ["dataview", "inline-field-value"],
attr: { id: "dataview-inline-field-" + this.id },
});
renderValue(
parseInlineValue(this.field.value),
Expand All @@ -185,7 +220,6 @@ class InlineFieldWidget extends WidgetType {
} else {
const value = renderContainer.createSpan({
cls: ["dataview", "inline-field-standalone-value"],
attr: { id: "dataview-inline-field-" + this.id },
});
renderValue(
parseInlineValue(this.field.value),
Expand Down

0 comments on commit 49a2905

Please sign in to comment.