diff --git a/client/client.ts b/client/client.ts index 07bfbff..765dfe4 100644 --- a/client/client.ts +++ b/client/client.ts @@ -104,7 +104,7 @@ export default class MarkdocClient implements VSC.Disposable { debug: { ...run, options: { execArgv: ["--nolazy", "--inspect=6009"] } }, }; - const pattern = pathutil.join(fsPath, config.path, "**/*.{md,mdoc}"); + const pattern = pathutil.join(fsPath, config.path, "**/*.{md,mdoc,markdoc}"); const client: LSP.LanguageClientOptions = { initializationOptions: { config }, documentSelector: [{ language: "markdoc", scheme, pattern }], diff --git a/client/extension.ts b/client/extension.ts index 19ece4e..21a95b1 100644 --- a/client/extension.ts +++ b/client/extension.ts @@ -173,7 +173,10 @@ export default class Extension { VSC.window.showTextDocument(doc); } - async onPreview(uri: VSC.Uri) { + async onPreview(previewUri: VSC.Uri) { + const uri = previewUri ?? VSC.window.activeTextEditor?.document.uri; + if (!uri) return; + const client = this.findClient(uri); if (!client?.canPreview()) return; diff --git a/server/plugins/completion.ts b/server/plugins/completion.ts index 96c4e79..5cc5d1b 100644 --- a/server/plugins/completion.ts +++ b/server/plugins/completion.ts @@ -16,14 +16,19 @@ type Completion = { export default class CompletionProvider { protected completions: Completion[] = [ { - match: /\{%[ ]*[^ ]+$/, + match: /\{%[ ]*(\/)?[^ ]+$/, complete: (params, matches, text) => { - const schema = this.services.Schema.get(); + const uri = params.textDocument.uri; + const schema = this.services.Schema.get(uri); if (!schema?.tags) return []; - const block = text.trim() === matches[0]; return Object.keys(schema.tags).map((label) => ({ - data: { resolve: "tag", block }, + data: { + resolve: "tag", uri, + block: text.trim() === matches[0], + closing: matches[1], + pos: params.position + }, label, })); }, @@ -33,7 +38,8 @@ export default class CompletionProvider { match: /.*\{%[ ]*([a-zA-Z-_]+)[^\}]* ([a-zA-Z-_]+)="?[^ ]+$/, complete: (params, matches) => { const [tagName, attrName] = matches.slice(1); - const schema = this.services.Schema.get(); + const uri = params.textDocument.uri; + const schema = this.services.Schema.get(uri); const attr = schema?.tags?.[tagName]?.attributes?.[attrName]; if (!attr?.matches) return []; @@ -69,10 +75,31 @@ export default class CompletionProvider { protected resolvers: Record = { tag: (item) => { - const schema = this.services.Schema.get(); + const schema = this.services.Schema.get(item.data.uri); const config = schema?.tags?.[item.label]; if (!config) return item; + + if (item.data.block) { + const ast = this.services.Documents.ast(item.data.uri); + if (ast) { + for (const node of ast.walk()) + if (node.tag && node.lines.includes(item.data.pos.line)) + return {label: item.label}; + } + } else { + const doc = this.services.Documents.get(item.data.uri); + if (doc) { + const pos: LSP.Position = item.data.pos; + const range = LSP.Range.create(pos.line, pos.character, pos.line + 1, 0); + const text = doc.getText(range); + if (text.match(/^(?:(?!{%).)+%}/)) + return {label: item.label}; + } + } + + if (item.data.closing) + return {...item, insertText: `${item.label} %}`}; const attrs = Object.entries(config.attributes ?? {}); const required = attrs.filter(([_, { required }]) => required); diff --git a/server/plugins/validation.ts b/server/plugins/validation.ts index 673e773..20260f7 100644 --- a/server/plugins/validation.ts +++ b/server/plugins/validation.ts @@ -59,7 +59,7 @@ export default class ValidationProvider { validate(uri: string) { const doc = this.services.Documents.ast(uri); - const schema = this.services.Schema.get(); + const schema = this.services.Schema.get(uri); if (!schema || !doc) return; @@ -78,7 +78,7 @@ export default class ValidationProvider { if (Scanner.has(part.attributes.file)) partials[part.attributes.file] = true; - return { ...Schema?.get(), partials }; + return { ...Schema?.get(uri), partials }; } onDidSave({ document: { uri } }: TextChangeEvent) { diff --git a/server/plugins/watch.ts b/server/plugins/watch.ts index 1a26e0a..9c0b96c 100644 --- a/server/plugins/watch.ts +++ b/server/plugins/watch.ts @@ -9,7 +9,7 @@ export default class Watch { protected connection: LSP.Connection, protected services: ServiceInstances ) { - const content = pathutil.join(config.path, "**/*.{md,mdoc}"); + const content = pathutil.join(config.path, "**/*.{md,mdoc,markdoc}"); services.Watcher.add(content, this.onContentChange.bind(this)); if (config.schema?.watch && config.schema.path) diff --git a/server/services/scanner.ts b/server/services/scanner.ts index 9251163..3a7b409 100644 --- a/server/services/scanner.ts +++ b/server/services/scanner.ts @@ -13,7 +13,7 @@ export default class Scanner< > { protected tokenizer: Markdoc.Tokenizer; protected files = new Map(); - protected extensions = [".md", ".mdoc"]; + protected extensions = [".md", ".mdoc", ".markdoc"]; protected path: string; readonly routes = new Map(); diff --git a/server/services/schema.ts b/server/services/schema.ts index c1d2f4b..7765774 100644 --- a/server/services/schema.ts +++ b/server/services/schema.ts @@ -1,3 +1,4 @@ +import * as LSP from "vscode-languageserver/node"; import * as pathutil from "path"; import type * as Markdoc from "@markdoc/markdoc"; import type { Config } from "../types"; @@ -7,7 +8,7 @@ export default class Schema { constructor(protected config: TConfig) {} - get(): Markdoc.Config | undefined { + get(uri?: LSP.URI): Markdoc.Config | undefined { return this.schema; }