Skip to content

Commit

Permalink
Merge pull request #29 from williamthome/feat/html_completions
Browse files Browse the repository at this point in the history
feat: html completions
  • Loading branch information
williamthome authored Jun 16, 2022
2 parents 80af2a2 + 01676e0 commit 6abe8ce
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 47 deletions.
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ Also some special completions are provided, like the atoms `true`, `false` and `

![Snippets](images/snippets.gif)

#### Models

A great help is the models snippets. Typing `m.` all models are listed and picking one shows all `m_get` possibilities.

![m_get snippets](images/m_get_snippets.gif)

#### Other languages completions

- HTML: Typing `<` outside a `.tpl` expression popups HTML snippets.

### Go to definition

Navigate to files in the `.tpl` by pressing <kbd>Ctrl</kbd> + <kbd>Click</kbd> over file names.
Expand All @@ -38,22 +44,27 @@ Zotonic documentation is displayed hovering tags, actions, filters, validators,

### Syntax Highlight

Improve semantic highlight.\
See:
- [Writing a TextMate Grammar: Some Lessons Learned](https://www.apeth.com/nonblog/stories/textmatebundle.html)
- [Semantic Highlight Guide](https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide)
- Improve semantic highlight. See:
- [Writing a TextMate Grammar: Some Lessons Learned](https://www.apeth.com/nonblog/stories/textmatebundle.html)
- [Semantic Highlight Guide](https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide)

### Snippets

Review arguments and tabstops.
- Review arguments and tabstops;
- CSS completions;
- Javascript completions.

### Docs

Improve style.
- Review links;
- Improve style.

## Backers

## [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/williamthome)
If you like this tool, please consider buying me a coffee ( or more ) as a token of appreciation.\
I'm thankful for your never-ending support.

[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/williamthome)

## Contributing

Expand Down
69 changes: 66 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
".",
"[",
"{",
"|"
"|",
"<"
]
}
},
Expand Down Expand Up @@ -144,6 +145,7 @@
"preRelease": false
},
"dependencies": {
"axios": "^0.27.2"
"axios": "^0.27.2",
"vscode-html-languageservice": "^5.0.0"
}
}
120 changes: 85 additions & 35 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as vscodeHtmlLanguageService from "vscode-html-languageservice";
import axios from "axios";
import config from "./config";
import { mGetExpressions, Expression, FindFile } from './utils/snippets';
Expand Down Expand Up @@ -107,48 +108,97 @@ export function activate(context: vscode.ExtensionContext) {
_token: vscode.CancellationToken,
_context: vscode.CompletionContext
) {
const modelNameRe = /\bm\.(\w+)?/;
const modelNameRange = document.getWordRangeAtPosition(position, modelNameRe);
if (!!modelNameRange && !modelNameRange.isEmpty) {
const modelsPattern = "{apps,apps_user}/**/src/models/**/m_*.erl";
const models = await vscode.workspace.findFiles(modelsPattern);
return models.reduce((arr, file) => {
const modelRe = /(?<=\bm_).*?(?=.erl)/;
const modelMatch = modelRe.exec(file.fsPath);
if (!modelMatch || !modelMatch.length) {
return arr;
const tplRe = /(?<={(%|{|#|_)?).*/;
const tplRange = document.getWordRangeAtPosition(position, tplRe);
if (tplRange && !tplRange.isEmpty) {
const variableRe = /(?<=\[).*?(?=\])|(?<={).*?(?=})|(?<=:).*?(?=}|,)|(?<==).*?(?=(%}|,|}))/;
const variableRange = document.getWordRangeAtPosition(position, variableRe);
if (!!variableRange) {
// TODO: Variables snippets.
// It will be awesome if variables can pop up as suggestion.

const modelNameRe = /\bm\.(\w+)?/;
const modelNameRange = document.getWordRangeAtPosition(position, modelNameRe);
if (!!modelNameRange && !modelNameRange.isEmpty) {
const modelsPattern = "{apps,apps_user}/**/src/models/**/m_*.erl";
const models = await vscode.workspace.findFiles(modelsPattern);
return models.reduce((arr, file) => {
const modelRe = /(?<=\bm_).*?(?=.erl)/;
const modelMatch = modelRe.exec(file.fsPath);
if (!modelMatch || !modelMatch.length) {
return arr;
}

const model = modelMatch[0];
const modelExpressionsFinder = (m: string) => mGetExpressions(findFile, m);
const snippet = new vscode.CompletionItem(model);
snippet.insertText = new vscode.SnippetString(model);
snippet.command = {
command: "tpl.snippet.pick",
title: "m_get",
arguments: [model, modelExpressionsFinder]
};
arr.push(snippet);
return arr;
}, new Array<vscode.CompletionItem>());
}

const model = modelMatch[0];
const modelExpressionsFinder = (m: string) => mGetExpressions(findFile, m);
const snippet = new vscode.CompletionItem(model);
snippet.insertText = new vscode.SnippetString(model);
snippet.command = {
command: "tpl.snippet.pick",
title: "m_get",
arguments: [model, modelExpressionsFinder]
};
arr.push(snippet);
return arr;
}, new Array<vscode.CompletionItem>());
}
const modelRe = /(?<=(\b(if|in)\b|({|:|=))\s*)(\s|m(\.)?)/;
const modelRange = document.getWordRangeAtPosition(position, modelRe);
if (!!modelRange && !modelRange.isEmpty) {
const snippet = new vscode.CompletionItem("m.");
snippet.insertText = new vscode.SnippetString("m.");
snippet.command = {
command: "editor.action.triggerSuggest",
title: "m"
};

return [snippet];
}
}

const variableRe = /(?<=\[).*?(?=\])|(?<={).*?(?=})|(?<=:).*?(?=}|,)|(?<==).*?(?=(}|,|%}))/;
const variableRange = document.getWordRangeAtPosition(position, variableRe);
if (!!variableRange) {
// TODO: Variables snippets.
// It will be awesome if variables can pop up as suggestion.
return;
return undefined;
}

const mSnippet = new vscode.CompletionItem("m");
mSnippet.insertText = new vscode.SnippetString("m");
const htmlRe = /(?<=<).*/;
const htmlRange = document.getWordRangeAtPosition(position, htmlRe);
if (htmlRange && !htmlRange.isEmpty) {
const htmlLanguageService =
vscodeHtmlLanguageService.getLanguageService();
const htmlTextDocument =
vscodeHtmlLanguageService.TextDocument.create(
document.uri.path,
document.languageId,
document.version,
document.getText()
);
const htmlDocument =
vscodeHtmlLanguageService.getLanguageService().parseHTMLDocument(
htmlTextDocument
);
const htmlCompletionList =
htmlLanguageService.doComplete(
htmlTextDocument,
position,
htmlDocument
);
htmlCompletionList.items = htmlCompletionList.items.map(i => {
if (i.textEdit?.newText) {
i.command = {
command: "tpl.snippet.insert",
arguments: [i.textEdit.newText],
title: i.label
};
i.textEdit.newText = "";
}
return i;
});
return htmlCompletionList as vscode.CompletionList;
}

return [
mSnippet
];
return undefined;
}
}, ".", "[", "{", "|");
}, ".", "[", "{", "|", "<");

context.subscriptions.push(completionProvider);

Expand Down

0 comments on commit 6abe8ce

Please sign in to comment.