Skip to content

Commit

Permalink
Improve autocomplete and enable schema customization (#8)
Browse files Browse the repository at this point in the history
* Support the .markdoc extension everywhere

* Fix bug in preview feature so it can be invoked from command palette

* Accept an optional URI parameter in `Schema.get`

* Improves autocompletion
  • Loading branch information
rpaul-stripe authored Jul 19, 2023
1 parent b730e8d commit b928c12
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 13 deletions.
2 changes: 1 addition & 1 deletion client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }],
Expand Down
5 changes: 4 additions & 1 deletion client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
39 changes: 33 additions & 6 deletions server/plugins/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}));
},
Expand All @@ -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 [];
Expand Down Expand Up @@ -69,10 +75,31 @@ export default class CompletionProvider {

protected resolvers: Record<string, ResolveFn> = {
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);
Expand Down
4 changes: 2 additions & 2 deletions server/plugins/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion server/plugins/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion server/services/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class Scanner<
> {
protected tokenizer: Markdoc.Tokenizer;
protected files = new Map<string, TMeta>();
protected extensions = [".md", ".mdoc"];
protected extensions = [".md", ".mdoc", ".markdoc"];
protected path: string;

readonly routes = new Map<string, string>();
Expand Down
3 changes: 2 additions & 1 deletion server/services/schema.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -7,7 +8,7 @@ export default class Schema<TConfig extends Config = Config> {

constructor(protected config: TConfig) {}

get(): Markdoc.Config | undefined {
get(uri?: LSP.URI): Markdoc.Config | undefined {
return this.schema;
}

Expand Down

0 comments on commit b928c12

Please sign in to comment.