Skip to content

Commit

Permalink
Task views have become modern and sleek
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Brenan committed Feb 14, 2022
1 parent c266e4d commit 65c2a04
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 283 deletions.
2 changes: 1 addition & 1 deletion __mocks__/data-import/web-worker/import-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ export class FileImporter {
public async reload<T>(file: TFile): Promise<T> {
let contents = await this.vault.read(file);
let metadata = await this.metadataCache.getFileCache(file);
return runImport(file.path, contents, metadata as CachedMetadata) as any as T;
return runImport(file.path, contents, file.stat, metadata as CachedMetadata) as any as T;
}
}
9 changes: 8 additions & 1 deletion src/api/data-array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class DataArrayImpl<T> implements DataArray<T> {
get: function (target, prop, reciever) {
if (typeof prop === "symbol") return (target as any)[prop];
else if (typeof prop === "number") return target.values[prop];
else if (prop === "constructor") return target.values.constructor;
else if (!isNaN(parseInt(prop))) return target.values[parseInt(prop)];
else if (DataArrayImpl.ARRAY_FUNCTIONS.has(prop.toString())) return target[prop.toString()];

Expand Down Expand Up @@ -396,7 +397,7 @@ class DataArrayImpl<T> implements DataArray<T> {
}

public toString(): string {
return this.values.toString();
return "[" + this.values.join(", ") + "]";
}
}

Expand Down Expand Up @@ -442,3 +443,9 @@ export namespace DataArray {
return obj instanceof DataArrayImpl;
}
}

// A scary looking polyfill, sure, but it fixes up data array/array interop for us.
const oldArrayIsArray = Array.isArray;
Array.isArray = (arg): arg is any[] => {
return oldArrayIsArray(arg) || DataArray.isDataArray(arg);
};
12 changes: 4 additions & 8 deletions src/api/inline-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ import { FullIndex } from "data-index";
import { renderValue, renderErrorPre } from "ui/render";
import { DataviewApi, DataviewIOApi } from "api/plugin-api";
import { DataviewSettings } from "settings";
import { DataObject, Link, Literal, Values } from "data-model/value";
import { DataObject, Grouping, Link, Literal, Values } from "data-model/value";
import { BoundFunctionImpl, DEFAULT_FUNCTIONS, Functions } from "expression/functions";
import { Context } from "expression/context";
import { defaultLinkHandler } from "query/engine";
import { DateTime, Duration } from "luxon";
import * as Luxon from "luxon";
import { DataArray } from "./data-array";
import { SListItem } from "data-model/serialized/markdown";
import { parseFrontmatter } from "data-import/markdown-file";
import { EXPRESSION } from "expression/parse";

/** Asynchronous API calls related to file / system IO. */
export class DataviewInlineIOApi {
Expand Down Expand Up @@ -173,14 +171,12 @@ export class DataviewInlineApi {

/** Parse a raw textual value into a complex Dataview type, if possible. */
public parse(value: string): Literal {
let raw = EXPRESSION.inlineField.parse(value);
if (raw.status) return raw.value;
else return value;
return this.api.parse(value);
}

/** Convert a basic JS type into a Dataview type by parsing dates, links, durations, and so on. */
public literal(value: any): Literal {
return DataArray.convert(parseFrontmatter(value), this.settings);
return this.api.literal(value);
}

/**
Expand Down Expand Up @@ -312,7 +308,7 @@ export class DataviewInlineApi {
}

/** Render a dataview task view with the given tasks. */
public taskList(tasks: SListItem[] | DataArray<SListItem>, groupByFile: boolean = true) {
public taskList(tasks: Grouping<SListItem> | DataArray<SListItem>, groupByFile: boolean = true) {
return this.api.taskList(tasks, groupByFile, this.container, this.component, this.currentFilePath);
}
}
Expand Down
66 changes: 28 additions & 38 deletions src/api/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { App, Component } from "obsidian";
import { FullIndex } from "data-index/index";
import { matchingSourcePaths } from "data-index/resolver";
import { Sources } from "data-index/source";
import { DataObject, Link, Literal, Values } from "data-model/value";
import { DataObject, Grouping, Link, Literal, Values } from "data-model/value";
import { EXPRESSION } from "expression/parse";
import { renderList, renderTable, renderValue } from "ui/render";
import { DataArray } from "./data-array";
Expand All @@ -16,6 +16,9 @@ import * as Luxon from "luxon";
import { compare, satisfies } from "compare-versions";
import { DvAPIInterface, DvIOAPIInterface } from "../typings/api";
import { DataviewSettings } from "settings";
import { parseFrontmatter } from "data-import/markdown-file";
import { SListItem } from "data-model/serialized/markdown";
import { createFixedTaskView } from "ui/views/task-view";

/** Asynchronous API calls related to file / system IO. */
export class DataviewIOApi implements DvIOAPIInterface {
Expand Down Expand Up @@ -162,6 +165,18 @@ export class DataviewApi implements DvAPIInterface {
return this.func.dur(str) as Duration | null;
}

/** Parse a raw textual value into a complex Dataview type, if possible. */
public parse(value: string): Literal {
let raw = EXPRESSION.inlineField.parse(value);
if (raw.status) return raw.value;
else return value;
}

/** Convert a basic JS type into a Dataview type by parsing dates, links, durations, and so on. */
public literal(value: any): Literal {
return DataArray.convert(parseFrontmatter(value), this.settings);
}

/**
* Compare two arbitrary JavaScript values using Dataview's default comparison rules. Returns a negative value if
* a < b, 0 if a = b, and a positive value if a > b.
Expand All @@ -187,7 +202,6 @@ export class DataviewApi implements DvAPIInterface {
filePath: string
) {
if (!values) return;
if (DataArray.isDataArray(values)) values = values.array();

await renderList(container, values as any[], component, filePath, this.settings);
}
Expand All @@ -201,52 +215,28 @@ export class DataviewApi implements DvAPIInterface {
filePath: string
) {
if (!values) values = [];
if (DataArray.isDataArray(values)) values = values.array();

await renderTable(container, headers, values as any[][], component, filePath, this.settings);
}

/** Render a dataview task view with the given tasks. */
public async taskList(
tasks: any[] | DataArray<any>,
tasks: Grouping<SListItem> | DataArray<SListItem>,
groupByFile: boolean = true,
container: HTMLElement,
component: Component,
filePath: string = ""
) {
if (DataArray.isDataArray(tasks)) tasks = tasks.array();

/*
let taskComponent = new Component();
component.addChild(taskComponent);
if (groupByFile) {
let byFile = new Map<string, []>();
for (let task of tasks as Task[]) {
if (!byFile.has(task.path)) byFile.set(task.path, []);
byFile.get(task.path)?.push(task);
}
let groupings = Groupings.grouped(
Array.from(byFile.entries()).map(([path, tasks]) => {
return { key: Link.file(path), value: Groupings.base(tasks) };
})
);
let subcontainer = container.createDiv();
await renderTasks(subcontainer, groupings, filePath, taskComponent, this.app.vault, this.settings);
} else {
let subcontainer = container.createDiv();
await renderTasks(
subcontainer,
Groupings.base(tasks),
filePath,
taskComponent,
this.app.vault,
this.settings
);
}
*/
let groupedTasks = groupByFile ? this.array(tasks).groupBy(t => Link.file(t.path)) : tasks;
component.addChild(
createFixedTaskView(
this.app,
this.settings,
this.index,
container,
groupedTasks as Grouping<SListItem>,
filePath
)
);
}

/** Render an arbitrary value into a container. */
Expand Down
10 changes: 1 addition & 9 deletions src/data-import/inline-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,6 @@ export function parseInlineValue(value: string): Literal {
else return value;
}

/** Extract full-line or inline fields. */
export function extractFields(line: string): InlineField[] {
let fullLine = extractFullLineField(line);
if (fullLine) return [fullLine];

return extractInlineFields(line);
}

/** Extracts inline fields of the form '[key:: value]' from a line of text. This is done in a relatively
* "robust" way to avoid failing due to bad nesting or other interfering Markdown symbols:
*
Expand Down Expand Up @@ -180,7 +172,7 @@ export const DUE_DATE_REGEX = /[\u{1F4C5}\u{1F4C6}\u{1F5D3}]\s*(\d{4}-\d{2}-\d{2
export const DONE_DATE_REGEX = /\u{2705}\s*(\d{4}-\d{2}-\d{2})/u;

/** Parse special completed/due/done task fields which are marked via emoji. */
export function extractSpecialTaskFields(line: string): InlineField[] {
function extractSpecialTaskFields(line: string): InlineField[] {
let results = [];

let createdMatch = CREATED_DATE_REGEX.exec(line);
Expand Down
16 changes: 8 additions & 8 deletions src/data-import/markdown-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ export function parseMarkdown(
for (let ifield of inlineFields) addRawInlineField(ifield, fields);
} else {
let fullLine = extractFullLineField(line);
if (path == "dataview-testing/Test.md") console.log(line, fullLine);
if (fullLine) addRawInlineField(fullLine, fields);
}
}
Expand All @@ -124,7 +123,7 @@ export function parseMarkdown(
}

// TODO: Consider using an actual parser in leiu of a more expensive regex.
export const LIST_ITEM_REGEX = /^\s*(\d+\.|\*|-|\+)\s*(\[.+\])?\s*(.+)$/mu;
export const LIST_ITEM_REGEX = /^\s*(\d+\.|\*|-|\+)\s*(\[.{0,1}\])?\s*(.+)$/mu;

/**
* Parse list items from the page + metadata. This requires some additional parsing above whatever Obsidian provides,
Expand Down Expand Up @@ -154,7 +153,8 @@ export function parseLists(
let textParts = [rawMatch[3]]
.concat(content.slice(rawElement.position.start.line + 1, rawElement.position.end.line + 1))
.map(t => t.trim());
let text = textParts.join(" ");
let textWithNewline = textParts.join("\n");
let textNoNewline = textParts.join(" ");

// Find the list that we are a part of by line.
let containingListId = (metadata.sections || []).findIndex(
Expand All @@ -174,7 +174,7 @@ export function parseLists(
symbol: rawMatch[1],
link: closestLink,
section: sectionLink,
text: text,
text: textWithNewline,
line: rawElement.position.start.line,
lineCount: rawElement.position.end.line - rawElement.position.start.line + 1,
list: containingListId == -1 ? -1 : (metadata.sections || [])[containingListId].position.start.line,
Expand All @@ -195,14 +195,14 @@ export function parseLists(

// Extract inline fields; extract full-line fields only if we are NOT a task.
item.fields = new Map<string, Literal[]>();
for (let line of text) {
for (let element of extractInlineFields(line, true)) addRawInlineField(element, item.fields);
}
for (let element of extractInlineFields(textNoNewline, true)) addRawInlineField(element, item.fields);

if (!rawElement.task && item.fields.size == 0) {
let fullLine = extractFullLineField(text);
let fullLine = extractFullLineField(textNoNewline);
if (fullLine) addRawInlineField(fullLine, item.fields);
}

cache[item.line] = item;
}

// Tree updating passes. Update child lists. Propogate metadata up to parent tasks. Update task `fullyCompleted`.
Expand Down
2 changes: 1 addition & 1 deletion src/data-index/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class FullIndex extends Component {
// Traverse all markdown files & fill in initial data.
let start = new Date().getTime();
for (const file of this.vault.getMarkdownFiles()) {
this.reloadInternal(file, {});
this.reloadInternal(file, { path: file.path });
this.reload(file);
}
console.log("Dataview: Task & metadata parsing queued in %.3fs.", (new Date().getTime() - start) / 1000.0);
Expand Down
13 changes: 9 additions & 4 deletions src/data-model/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ export class ListItem {
section: this.section,
text: this.text,
line: this.line,
lineCount: this.lineCount,
list: this.list,
path: this.link.path,
children: children,
Expand All @@ -240,9 +241,9 @@ export class ListItem {
due = this.due(),
completed = this.completed();

if (created) result.created = created;
if (due) result.due = due;
if (completed) result.completion = completed;
if (created) result.created = Values.deepCopy(created);
if (due) result.due = Values.deepCopy(due);
if (completed) result.completion = Values.deepCopy(completed);
}

return result as SListItem;
Expand All @@ -255,10 +256,14 @@ export class ListItem {

/** De-duplicates list items across section metadata and page metadata. */
export class ListSerializationCache {
public listItems: Record<number, ListItem>;
public cache: Record<number, SListItem>;

public constructor(public listItems: Record<number, ListItem>) {
public constructor(listItems: ListItem[]) {
this.listItems = {};
this.cache = {};

for (let item of listItems) this.listItems[item.line] = item;
}

public get(lineno: number): SListItem {
Expand Down
11 changes: 9 additions & 2 deletions src/data-model/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export type Literal =

/** A grouping on a type which supports recursively-nested groups. */
export type GroupElement<T> = { key: Literal; rows: Grouping<T> };
export type Grouping<T> = (T | GroupElement<T>)[];
export type Grouping<T> = T[] | GroupElement<T>[];

/** Maps the string type to it's external, API-facing representation. */
export type LiteralRepr<T extends LiteralType> = T extends "boolean"
Expand Down Expand Up @@ -359,9 +359,16 @@ export namespace Values {

export namespace Groupings {
/** Determines if the given group entry is a standalone value, or a grouping of sub-entries. */
export function isGroup<T>(entry: T | GroupElement<T>): entry is GroupElement<T> {
export function isElementGroup<T>(entry: T | GroupElement<T>): entry is GroupElement<T> {
return Values.isObject(entry) && Object.keys(entry).length == 2 && "key" in entry && "rows" in entry;
}

/** Determines if the given array is a grouping array. */
export function isGrouping<T>(entry: T[] | GroupElement<T>[]): entry is GroupElement<T>[] {
for (let element of entry) if (!isElementGroup(element)) return false;

return true;
}
}

//////////
Expand Down
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default class DataviewPlugin extends Plugin {
// Not required anymore, though holding onto it for backwards-compatibility.
this.app.metadataCache.trigger("dataview:api-ready", this.api);

console.log(`Dataview: Version ${this.manifest.version} Loaded`);
console.log(`Dataview: Version ${this.manifest.version} (Obsidian >${this.manifest.minAppVersion})`);
}

private debouncedRefresh: () => void = () => null;
Expand Down
Loading

0 comments on commit 65c2a04

Please sign in to comment.