Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the inline fields rendering in live preview #2088

Merged
merged 1 commit into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { App, Component, debounce, MarkdownPostProcessorContext, Plugin, PluginSettingTab, Setting } from "obsidian";
import { App, Component, debounce, MarkdownPostProcessorContext, MarkdownView, Plugin, PluginSettingTab, Setting } from "obsidian";
import { renderErrorPre } from "ui/render";
import { FullIndex } from "data-index/index";
import { parseField } from "expression/parse";
Expand All @@ -11,7 +11,7 @@ import { currentLocale } from "util/locale";
import { DateTime } from "luxon";
import { DataviewInlineApi } from "api/inline-api";
import { replaceInlineFields } from "ui/views/inline-field";
import { inlineFieldsField, replaceInlineFieldsInLivePreview } from "./ui/views/inline-field-live-preview";
import { inlineFieldsField, replaceInlineFieldsInLivePreview, workspaceLayoutChangeEffect } from "./ui/views/inline-field-live-preview";
import { DataviewInit } from "ui/markdown";
import { inlinePlugin } from "./ui/lp-render";
import { Extension } from "@codemirror/state";
Expand Down Expand Up @@ -121,8 +121,20 @@ export default class DataviewPlugin extends Plugin {
this.app.metadataCache.trigger("dataview:api-ready", this.api);
console.log(`Dataview: version ${this.manifest.version} (requires obsidian ${this.manifest.minAppVersion})`);

// Mainly intended to detect when the user switches between live preview and source mode.
this.registerEvent(
this.app.workspace.on("layout-change", () => {
this.app.workspace.iterateAllLeaves((leaf) => {
if (leaf.view instanceof MarkdownView && leaf.view.editor.cm) {
leaf.view.editor.cm.dispatch({
effects: workspaceLayoutChangeEffect.of(null)
})
}
})
})
)
this.registerEditorExtension(inlineFieldsField);
this.registerEditorExtension(replaceInlineFieldsInLivePreview(this.app));
this.registerEditorExtension(replaceInlineFieldsInLivePreview(this.app, this.settings));
}

private debouncedRefresh: () => void = () => null;
Expand Down
8 changes: 8 additions & 0 deletions src/typings/obsidian-ex.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { DataviewApi } from "api/plugin-api";
import "obsidian";
import { EditorView } from "@codemirror/view";

declare module "obsidian" {
interface MetadataCache {
Expand All @@ -23,6 +24,13 @@ declare module "obsidian" {
/** Sent to rendered dataview components to tell them to possibly refresh */
on(name: "dataview:refresh-views", callback: () => void, ctx?: any): EventRef;
}

interface Editor {
/**
* CodeMirror editor instance
*/
cm?: EditorView;
}
}

declare global {
Expand Down
58 changes: 47 additions & 11 deletions src/ui/views/inline-field-live-preview.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { App, Component, MarkdownRenderer, editorInfoField } from "obsidian";
import { EditorState, RangeSet, RangeSetBuilder, RangeValue, StateField } from "@codemirror/state";
import { App, Component, MarkdownRenderer, editorInfoField, editorLivePreviewField } from "obsidian";
import { EditorState, RangeSet, RangeSetBuilder, RangeValue, StateEffect, StateField } from "@codemirror/state";
import {
Decoration,
DecorationSet,
Expand All @@ -9,8 +9,10 @@ import {
ViewUpdate,
WidgetType,
} from "@codemirror/view";
import { InlineField, extractInlineFields } from "data-import/inline-field";
import { InlineField, extractInlineFields, parseInlineValue } from "data-import/inline-field";
import { canonicalizeVarName } from "util/normalize";
import { renderValue } from "ui/render";
import { DataviewSettings } from "settings";

class InlineFieldValue extends RangeValue {
constructor(public field: InlineField) {
Expand Down Expand Up @@ -40,7 +42,7 @@ export const inlineFieldsField = StateField.define<RangeSet<InlineFieldValue>>({
});

/** Create a view plugin that renders inline fields in live preview just as in the reading view. */
export const replaceInlineFieldsInLivePreview = (app: App) =>
export const replaceInlineFieldsInLivePreview = (app: App, settings: DataviewSettings) =>
ViewPlugin.fromClass(
class implements PluginValue {
decorations: DecorationSet;
Expand All @@ -59,18 +61,31 @@ export const replaceInlineFieldsInLivePreview = (app: App) =>
const oldIndices = this.overlappingIndices;
const newIndices = this.getOverlappingIndices(update.state);

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

this.overlappingIndices = newIndices;

if (update.docChanged || update.viewportChanged || overlapChanged) {
this.decorations = this.buildDecoration(update.view);
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;
}
}

buildDecoration(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,
Expand All @@ -95,7 +110,7 @@ export const replaceInlineFieldsInLivePreview = (app: App) =>
start,
end,
Decoration.replace({
widget: new InlineFieldWidget(app, field, x++, file.path, markdownView),
widget: new InlineFieldWidget(app, field, x++, file.path, markdownView, settings),
})
);
}
Expand Down Expand Up @@ -131,7 +146,8 @@ class InlineFieldWidget extends WidgetType {
public field: InlineField,
public id: number,
public sourcePath: string,
public parentComponent: Component
public parentComponent: Component,
public settings: DataviewSettings
) {
super();
}
Expand Down Expand Up @@ -161,13 +177,27 @@ class InlineFieldWidget extends WidgetType {
cls: ["dataview", "inline-field-value"],
attr: { id: "dataview-inline-field-" + this.id },
});
this.renderMarkdown(value, this.field.value);
renderValue(
parseInlineValue(this.field.value),
value,
this.sourcePath,
this.parentComponent,
this.settings,
false
);
} else {
const value = renderContainer.createSpan({
cls: ["dataview", "inline-field-standalone-value"],
attr: { id: "dataview-inline-field-" + this.id },
});
this.renderMarkdown(value, this.field.value);
renderValue(
parseInlineValue(this.field.value),
value,
this.sourcePath,
this.parentComponent,
this.settings,
false
);
}

return renderContainer;
Expand Down Expand Up @@ -195,3 +225,9 @@ export async function renderMarkdown(
}
return null;
}

/**
* A state effect that represents the workspace's layout change.
* Mainly intended to detect when the user switches between live preview and source mode.
*/
export const workspaceLayoutChangeEffect = StateEffect.define<null>();
Loading