diff --git a/dendron-main.code-workspace b/dendron-main.code-workspace index 69d11e0123..b29024718f 100644 --- a/dendron-main.code-workspace +++ b/dendron-main.code-workspace @@ -100,7 +100,9 @@ "pasteImage.prefix": "/", "editor.suggest.snippetsPreventQuickSuggestions": false, "editor.suggest.showSnippets": true, - "editor.tabCompletion": "on" + "editor.tabCompletion": "on", + "markdown-preview-enhanced.enableWikiLinkSyntax": true, + "markdown-preview-enhanced.wikiLinkFileExtension": ".md" }, "extensions": { "recommendations": ["esbenp.prettier-vscode"] diff --git a/packages/common-all/src/constants/views.ts b/packages/common-all/src/constants/views.ts index 690adc3d78..4dbfb0cd9b 100644 --- a/packages/common-all/src/constants/views.ts +++ b/packages/common-all/src/constants/views.ts @@ -20,6 +20,7 @@ export enum DendronEditorViewKey { SCHEMA_GRAPH = "dendron.graph-schema", NOTE_PREVIEW = "dendron.note-preview", SEED_BROWSER = "dendron.seed-browser", + SCHEMA_VALIDATOR = "dendron.schema-validator", } export enum DendronTreeViewKey { @@ -64,6 +65,12 @@ export const EDITOR_VIEWS: Record = { bundleName: "SeedBrowser", type: "webview", }, + [DendronEditorViewKey.SCHEMA_VALIDATOR]: { + desc: "Schema Validator", + label: "Schema Validator", + bundleName: "DendronSchemaValidator", + type: "webview", + }, }; /** diff --git a/packages/common-all/src/dnode.ts b/packages/common-all/src/dnode.ts index 85751b621f..9d6410eb52 100644 --- a/packages/common-all/src/dnode.ts +++ b/packages/common-all/src/dnode.ts @@ -399,7 +399,7 @@ export class NoteUtils { notePath: noteOpts.fname, schemaModDict: engine.schemas, }); - if (maybeMatch) { + if (maybeMatch && !maybeMatch.partial) { const { schema, schemaModule } = maybeMatch; NoteUtils.addSchema({ note, schemaModule, schema }); } @@ -1273,6 +1273,10 @@ type SchemaMatchResult = { schema: SchemaProps; namespace: boolean; notePath: string; + /** + * True if only a partial match. + */ + partial: boolean; }; export class SchemaUtils { @@ -1668,14 +1672,26 @@ export class SchemaUtils { notePath, namespace: domainSchema.data.namespace || false, schemaModule: match, + partial: false, }; } - return SchemaUtils.matchPathWithSchema({ + const result = SchemaUtils.matchPathWithSchema({ notePath, matched: "", schemaCandidates: [domainSchema], schemaModule: match, }); + // If cannot go deeper, return what we have partially + if (result === undefined) { + return { + schema: domainSchema, + notePath, + namespace: domainSchema.data.namespace || false, + schemaModule: match, + partial: true, + }; + } + return result; } } @@ -1723,6 +1739,7 @@ export class SchemaUtils { schema, namespace, notePath, + partial: false, }; } @@ -1733,13 +1750,27 @@ export class SchemaUtils { const nextSchemaCandidates = matchNextNamespace ? schema.children.map((id) => schemaModule.schemas[id]) : [schema]; - return SchemaUtils.matchPathWithSchema({ + + // recursive step + const next = SchemaUtils.matchPathWithSchema({ notePath, matched: nextNotePath, schemaCandidates: nextSchemaCandidates, schemaModule, matchNamespace: matchNextNamespace, }); + // If cannot go deeper, return what we have partially + if (next === undefined) { + // possible bug: next itself is partial + return { + schemaModule, + schema, + namespace, + notePath, + partial: true, + }; + } + return next; } return; } @@ -1772,6 +1803,7 @@ export class SchemaUtils { namespace, notePath, schemaModule, + partial: false, }; } return; diff --git a/packages/dendron-plugin-views/src/components/DendronSchemaValidator.tsx b/packages/dendron-plugin-views/src/components/DendronSchemaValidator.tsx new file mode 100644 index 0000000000..7baa7c8a73 --- /dev/null +++ b/packages/dendron-plugin-views/src/components/DendronSchemaValidator.tsx @@ -0,0 +1,176 @@ +import { DendronProps } from "../types"; +import { Select, AutoComplete } from "antd"; +import { useEffect, useState } from "react"; +import { + SchemaModuleProps, + SchemaProps, + SchemaUtils, +} from "@dendronhq/common-all"; + +function generatePath(module: SchemaModuleProps, schema: SchemaProps): string { + const partString = schema.data.pattern + ? `${schema.data.pattern}` + : schema.data.namespace + ? `${schema.id}.*` + : schema.id; + if (schema.parent && schema.parent !== "root") { + return ( + generatePath(module, module.schemas[schema.parent]) + "." + partString + ); + } + return partString; +} + +function getCompletions( + schemaModule: SchemaModuleProps, + schema: SchemaProps +): { label: string; value: string }[] { + const path = generatePath(schemaModule, schema); + return [ + { label: path, value: path }, + ...schema.children + .map((child) => getCompletions(schemaModule, schemaModule.schemas[child])) + .flat(), + ]; +} + +function SchemaBox({ + schema, + schemaModule, + currentMatch, + partialMatch, +}: { + schema: SchemaProps; + schemaModule: SchemaModuleProps; + currentMatch: string | null; + partialMatch: boolean; +}) { + const match = currentMatch === schema.id; + return ( +
+

+ {schema.title}{" "} + + {match && (partialMatch ? "(partial match)" : "(match)")} + +

+

+ ({generatePath(schemaModule, schema)}) +

+
+ {schema.children.map((childID) => ( + + ))} +
+
+ ); +} + +export default function DendronSchemaValidator({ engine }: DendronProps) { + const [selectedDomain, setSelectedDomain] = useState(null); + const [inputtedHierarchy, setInputtedHierarchy] = useState(""); + useEffect(() => { + window.postMessage({ + type: "onDidChangeActiveTextEditor", + data: { note: { id: "" }, sync: true }, + source: "vscode", + }); + }, []); + useEffect(() => { + if (selectedDomain !== null) { + setInputtedHierarchy(selectedDomain); + } + }, [selectedDomain]); + useEffect(() => { + const schemas = Object.keys(engine.schemas); + if (selectedDomain === null && schemas.length > 0) { + setSelectedDomain(schemas[0]); + setInputtedHierarchy(schemas[0]); + } + }, [engine.schemas, selectedDomain]); + const schemaModule = + selectedDomain !== null ? engine.schemas[selectedDomain] : null; + const match = + schemaModule !== null + ? SchemaUtils.matchPathWithSchema({ + notePath: inputtedHierarchy, + matched: "", + schemaCandidates: [schemaModule.schemas[schemaModule.root.id]], + schemaModule, + }) + : undefined; + const schemaValues = Object.entries(engine.schemas).map(([id, schema]) => ({ + value: id, + label: schema.root.fname, + })); + return ( +
+

Schema Validator

+
select a schema file
+
+