sql writer
This commit is contained in:
274
vscode-extension/relspec-template-editor/README.md
Normal file
274
vscode-extension/relspec-template-editor/README.md
Normal file
@@ -0,0 +1,274 @@
|
||||
# RelSpec Template Editor for VS Code
|
||||
|
||||
Visual editor and tooling for RelSpec PostgreSQL migration templates.
|
||||
|
||||
## Features
|
||||
|
||||
### 1. Template Preview
|
||||
- **Command**: `RelSpec: Preview Template`
|
||||
- **Shortcut**: Click the preview icon in the editor title bar
|
||||
- Preview your templates with sample data
|
||||
- Side-by-side view of template, data, and rendered SQL
|
||||
|
||||
### 2. Syntax Validation
|
||||
- **Command**: `RelSpec: Validate Template`
|
||||
- Automatic validation on save (configurable)
|
||||
- Highlights syntax errors inline
|
||||
- Checks for unclosed template tags
|
||||
|
||||
### 3. IntelliSense
|
||||
- Auto-completion for template functions
|
||||
- Function signatures and documentation on hover
|
||||
- Keyword completions (`if`, `range`, `template`, etc.)
|
||||
|
||||
### 4. Template Scaffolding
|
||||
- **Command**: `RelSpec: New Template`
|
||||
- Quick scaffolding for common template types:
|
||||
- DDL operations
|
||||
- Constraints
|
||||
- Indexes
|
||||
- Audit trails
|
||||
- Reusable fragments
|
||||
|
||||
### 5. Function Library
|
||||
- **Command**: `RelSpec: List Available Functions`
|
||||
- Browse all available template functions
|
||||
- See examples and documentation
|
||||
- Quick reference for template development
|
||||
|
||||
## Installation
|
||||
|
||||
### From Source
|
||||
|
||||
1. Clone the RelSpec repository
|
||||
2. Navigate to the extension directory:
|
||||
```bash
|
||||
cd vscode-extension
|
||||
```
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
4. Compile the extension:
|
||||
```bash
|
||||
npm run compile
|
||||
```
|
||||
5. Open in VS Code:
|
||||
```bash
|
||||
code .
|
||||
```
|
||||
6. Press `F5` to launch the extension in a new window
|
||||
|
||||
### From VSIX (when published)
|
||||
|
||||
```bash
|
||||
code --install-extension relspec-template-editor-0.1.0.vsix
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Opening Templates
|
||||
|
||||
1. Open your RelSpec project in VS Code
|
||||
2. Navigate to `pkg/writers/pgsql/templates/`
|
||||
3. Open any `.tmpl` file
|
||||
4. The extension will automatically activate
|
||||
|
||||
### Previewing Templates
|
||||
|
||||
1. Open a template file
|
||||
2. Click the preview icon in the editor title bar OR
|
||||
3. Run command: `RelSpec: Preview Template` (Ctrl+Shift+P)
|
||||
4. The preview pane will show:
|
||||
- Your template source
|
||||
- Sample data (configurable)
|
||||
- Rendered SQL output
|
||||
|
||||
### Configuring Sample Data
|
||||
|
||||
Set custom sample data for preview in settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"relspec.previewSampleData": {
|
||||
"SchemaName": "public",
|
||||
"TableName": "users",
|
||||
"ColumnName": "email",
|
||||
"Columns": [
|
||||
{"Name": "id", "Type": "integer", "NotNull": true},
|
||||
{"Name": "email", "Type": "text"}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Creating New Templates
|
||||
|
||||
1. Run command: `RelSpec: New Template`
|
||||
2. Select template type
|
||||
3. Enter template name
|
||||
4. The extension creates a scaffolded template
|
||||
5. Edit and customize
|
||||
|
||||
### Using IntelliSense
|
||||
|
||||
Type `{{` to trigger auto-completion:
|
||||
|
||||
```gotmpl
|
||||
{{upper // Shows function signature and documentation
|
||||
{{. // Shows available fields from data structure
|
||||
```
|
||||
|
||||
Hover over function names to see:
|
||||
- Function signature
|
||||
- Description
|
||||
- Usage examples
|
||||
|
||||
## Configuration
|
||||
|
||||
Available settings:
|
||||
|
||||
| Setting | Type | Default | Description |
|
||||
|---------|------|---------|-------------|
|
||||
| `relspec.templatePath` | string | `pkg/writers/pgsql/templates` | Path to template directory |
|
||||
| `relspec.autoValidate` | boolean | `true` | Validate templates on save |
|
||||
| `relspec.showDataStructures` | boolean | `true` | Show data structure hints in preview |
|
||||
| `relspec.previewSampleData` | object | `{}` | Sample JSON data for preview |
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
| Command | Shortcut | Description |
|
||||
|---------|----------|-------------|
|
||||
| Preview Template | N/A | Click preview icon in title bar |
|
||||
| Validate Template | N/A | Use command palette |
|
||||
| New Template | N/A | Use command palette |
|
||||
|
||||
## Template Functions Reference
|
||||
|
||||
The extension provides IntelliSense for all RelSpec template functions:
|
||||
|
||||
### String Manipulation
|
||||
- `upper` - Convert to uppercase
|
||||
- `lower` - Convert to lowercase
|
||||
- `title` - Title case
|
||||
- `snake_case` - Convert to snake_case
|
||||
- `camelCase` - Convert to camelCase
|
||||
|
||||
### SQL Formatting
|
||||
- `indent` - Indent text
|
||||
- `quote` - Quote for SQL
|
||||
- `escape` - Escape special characters
|
||||
- `safe_identifier` - Make safe SQL identifier
|
||||
|
||||
### Type Conversion
|
||||
- `goTypeToSQL` - Go type → SQL type
|
||||
- `sqlTypeToGo` - SQL type → Go type
|
||||
- `isNumeric` - Check if numeric type
|
||||
- `isText` - Check if text type
|
||||
|
||||
### Collection Helpers
|
||||
- `first` - First element
|
||||
- `last` - Last element
|
||||
- `filter` - Filter elements
|
||||
- `mapFunc` - Map function
|
||||
- `join_with` - Join with separator
|
||||
- `join` - Join strings
|
||||
|
||||
See full documentation: [Template Functions](../pkg/writers/pgsql/TEMPLATE_FUNCTIONS.md)
|
||||
|
||||
## Code Snippets
|
||||
|
||||
The extension includes snippets for common patterns:
|
||||
|
||||
| Prefix | Description |
|
||||
|--------|-------------|
|
||||
| `tmpl-define` | Define a new template |
|
||||
| `tmpl-template` | Use a template |
|
||||
| `tmpl-block` | Define a block |
|
||||
| `tmpl-if` | If statement |
|
||||
| `tmpl-range` | Range loop |
|
||||
| `tmpl-with` | With statement |
|
||||
|
||||
## Development
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
npm run compile
|
||||
```
|
||||
|
||||
### Watching
|
||||
|
||||
```bash
|
||||
npm run watch
|
||||
```
|
||||
|
||||
### Linting
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Extension Not Activating
|
||||
|
||||
**Problem**: Extension doesn't activate when opening .tmpl files
|
||||
|
||||
**Solution**:
|
||||
1. Check that file has `.tmpl` extension
|
||||
2. Reload VS Code window (Ctrl+Shift+P → "Reload Window")
|
||||
3. Check extension is enabled in Extensions panel
|
||||
|
||||
### Preview Not Working
|
||||
|
||||
**Problem**: Preview shows error or doesn't update
|
||||
|
||||
**Solution**:
|
||||
1. Ensure RelSpec binary is in PATH
|
||||
2. Check template syntax is valid
|
||||
3. Verify sample data is valid JSON
|
||||
|
||||
### IntelliSense Not Working
|
||||
|
||||
**Problem**: Auto-completion doesn't trigger
|
||||
|
||||
**Solution**:
|
||||
1. Type `{{` to trigger
|
||||
2. Check language mode is set to "Go Template"
|
||||
3. Reload window
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions welcome! Please:
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Add tests
|
||||
5. Submit a pull request
|
||||
|
||||
## License
|
||||
|
||||
Same as RelSpec project license.
|
||||
|
||||
## Links
|
||||
|
||||
- [RelSpec Documentation](../README.md)
|
||||
- [Template Documentation](../pkg/writers/pgsql/TEMPLATES.md)
|
||||
- [Template Inheritance](../pkg/writers/pgsql/TEMPLATE_INHERITANCE.md)
|
||||
- [Issue Tracker](https://github.com/yourorg/relspec/issues)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 0.1.0 (Initial Release)
|
||||
- Template preview with sample data
|
||||
- Syntax validation
|
||||
- IntelliSense for functions
|
||||
- Template scaffolding
|
||||
- Function library browser
|
||||
119
vscode-extension/relspec-template-editor/package.json
Normal file
119
vscode-extension/relspec-template-editor/package.json
Normal file
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"name": "relspec-template-editor",
|
||||
"displayName": "RelSpec Template Editor",
|
||||
"description": "Visual editor for RelSpec PostgreSQL migration templates",
|
||||
"version": "0.1.0",
|
||||
"engines": {
|
||||
"vscode": "^1.80.0"
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages",
|
||||
"Formatters",
|
||||
"Snippets"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:gotmpl",
|
||||
"onCommand:relspec.previewTemplate",
|
||||
"onCommand:relspec.validateTemplate",
|
||||
"onCommand:relspec.newTemplate"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "gotmpl",
|
||||
"aliases": ["Go Template", "gotmpl"],
|
||||
"extensions": [".tmpl"],
|
||||
"configuration": "./language-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "gotmpl",
|
||||
"scopeName": "source.gotmpl",
|
||||
"path": "./syntaxes/gotmpl.tmLanguage.json"
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "relspec.previewTemplate",
|
||||
"title": "RelSpec: Preview Template",
|
||||
"icon": "$(preview)"
|
||||
},
|
||||
{
|
||||
"command": "relspec.validateTemplate",
|
||||
"title": "RelSpec: Validate Template"
|
||||
},
|
||||
{
|
||||
"command": "relspec.newTemplate",
|
||||
"title": "RelSpec: New Template"
|
||||
},
|
||||
{
|
||||
"command": "relspec.listFunctions",
|
||||
"title": "RelSpec: List Available Functions"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"editor/title": [
|
||||
{
|
||||
"command": "relspec.previewTemplate",
|
||||
"when": "resourceLangId == gotmpl",
|
||||
"group": "navigation"
|
||||
}
|
||||
],
|
||||
"editor/context": [
|
||||
{
|
||||
"command": "relspec.validateTemplate",
|
||||
"when": "resourceLangId == gotmpl",
|
||||
"group": "relspec"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "RelSpec Template Editor",
|
||||
"properties": {
|
||||
"relspec.templatePath": {
|
||||
"type": "string",
|
||||
"default": "pkg/writers/pgsql/templates",
|
||||
"description": "Path to template directory"
|
||||
},
|
||||
"relspec.autoValidate": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Automatically validate templates on save"
|
||||
},
|
||||
"relspec.showDataStructures": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Show data structure hints in preview"
|
||||
},
|
||||
"relspec.previewSampleData": {
|
||||
"type": "string",
|
||||
"default": "{}",
|
||||
"description": "Sample JSON data for template preview"
|
||||
}
|
||||
}
|
||||
},
|
||||
"snippets": [
|
||||
{
|
||||
"language": "gotmpl",
|
||||
"path": "./snippets/relspec.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"lint": "eslint src --ext ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "^1.80.0",
|
||||
"@types/node": "16.x",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
||||
"@typescript-eslint/parser": "^5.59.0",
|
||||
"eslint": "^8.41.0",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
438
vscode-extension/relspec-template-editor/src/extension.ts
Normal file
438
vscode-extension/relspec-template-editor/src/extension.ts
Normal file
@@ -0,0 +1,438 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
// Extension activation
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log('RelSpec Template Editor activated');
|
||||
|
||||
// Register commands
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('relspec.previewTemplate', previewTemplate)
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('relspec.validateTemplate', validateTemplate)
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('relspec.newTemplate', newTemplate)
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('relspec.listFunctions', listFunctions)
|
||||
);
|
||||
|
||||
// Register completion provider
|
||||
const completionProvider = vscode.languages.registerCompletionItemProvider(
|
||||
'gotmpl',
|
||||
new TemplateCompletionProvider(),
|
||||
'{{', '.'
|
||||
);
|
||||
context.subscriptions.push(completionProvider);
|
||||
|
||||
// Register hover provider
|
||||
const hoverProvider = vscode.languages.registerHoverProvider(
|
||||
'gotmpl',
|
||||
new TemplateHoverProvider()
|
||||
);
|
||||
context.subscriptions.push(hoverProvider);
|
||||
|
||||
// Auto-validate on save
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidSaveTextDocument((document) => {
|
||||
const config = vscode.workspace.getConfiguration('relspec');
|
||||
if (config.get('autoValidate') && document.languageId === 'gotmpl') {
|
||||
validateTemplate();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Preview template with sample data
|
||||
async function previewTemplate() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || editor.document.languageId !== 'gotmpl') {
|
||||
vscode.window.showErrorMessage('Please open a .tmpl file');
|
||||
return;
|
||||
}
|
||||
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
'templatePreview',
|
||||
'Template Preview',
|
||||
vscode.ViewColumn.Beside,
|
||||
{
|
||||
enableScripts: true
|
||||
}
|
||||
);
|
||||
|
||||
const templateContent = editor.document.getText();
|
||||
const config = vscode.workspace.getConfiguration('relspec');
|
||||
const sampleDataString = config.get<string>('previewSampleData', '{}');
|
||||
|
||||
try {
|
||||
const sampleData = JSON.parse(sampleDataString);
|
||||
const preview = await renderTemplate(templateContent, sampleData);
|
||||
|
||||
panel.webview.html = getWebviewContent(templateContent, preview, sampleData);
|
||||
} catch (error) {
|
||||
panel.webview.html = getErrorWebviewContent(String(error));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate template syntax
|
||||
async function validateTemplate() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || editor.document.languageId !== 'gotmpl') {
|
||||
return;
|
||||
}
|
||||
|
||||
const templateContent = editor.document.getText();
|
||||
const diagnostics: vscode.Diagnostic[] = [];
|
||||
|
||||
// Check for common template errors
|
||||
const errors = checkTemplateSyntax(templateContent);
|
||||
for (const error of errors) {
|
||||
const range = new vscode.Range(
|
||||
error.line,
|
||||
error.column,
|
||||
error.line,
|
||||
error.column + error.length
|
||||
);
|
||||
const diagnostic = new vscode.Diagnostic(
|
||||
range,
|
||||
error.message,
|
||||
vscode.DiagnosticSeverity.Error
|
||||
);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
// Update diagnostics
|
||||
const collection = vscode.languages.createDiagnosticCollection('relspec');
|
||||
collection.set(editor.document.uri, diagnostics);
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
vscode.window.showInformationMessage('Template is valid');
|
||||
}
|
||||
}
|
||||
|
||||
// Create new template from scaffold
|
||||
async function newTemplate() {
|
||||
const templateType = await vscode.window.showQuickPick(
|
||||
[
|
||||
{ label: 'DDL Template', value: 'ddl' },
|
||||
{ label: 'Constraint Template', value: 'constraint' },
|
||||
{ label: 'Index Template', value: 'index' },
|
||||
{ label: 'Audit Template', value: 'audit' },
|
||||
{ label: 'Custom Fragment', value: 'fragment' }
|
||||
],
|
||||
{ placeHolder: 'Select template type' }
|
||||
);
|
||||
|
||||
if (!templateType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const templateName = await vscode.window.showInputBox({
|
||||
prompt: 'Enter template name',
|
||||
placeHolder: 'my_template'
|
||||
});
|
||||
|
||||
if (!templateName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scaffold = getTemplateScaffold(templateType.value, templateName);
|
||||
const config = vscode.workspace.getConfiguration('relspec');
|
||||
const templatePath = config.get<string>('templatePath', 'pkg/writers/pgsql/templates');
|
||||
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolders) {
|
||||
vscode.window.showErrorMessage('No workspace folder open');
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = path.join(workspaceFolders[0].uri.fsPath, templatePath, `${templateName}.tmpl`);
|
||||
fs.writeFileSync(filePath, scaffold);
|
||||
|
||||
const document = await vscode.workspace.openTextDocument(filePath);
|
||||
await vscode.window.showTextDocument(document);
|
||||
}
|
||||
|
||||
// List available template functions
|
||||
async function listFunctions() {
|
||||
const functions = getTemplateFunctions();
|
||||
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
'functionsList',
|
||||
'RelSpec Template Functions',
|
||||
vscode.ViewColumn.Beside,
|
||||
{}
|
||||
);
|
||||
|
||||
panel.webview.html = getFunctionsWebviewContent(functions);
|
||||
}
|
||||
|
||||
// Completion provider for template functions and keywords
|
||||
class TemplateCompletionProvider implements vscode.CompletionItemProvider {
|
||||
provideCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
): vscode.CompletionItem[] {
|
||||
const linePrefix = document.lineAt(position).text.substr(0, position.character);
|
||||
|
||||
if (!linePrefix.endsWith('{{')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const functions = getTemplateFunctions();
|
||||
const completionItems: vscode.CompletionItem[] = [];
|
||||
|
||||
// Add function completions
|
||||
for (const func of functions) {
|
||||
const item = new vscode.CompletionItem(func.name, vscode.CompletionItemKind.Function);
|
||||
item.detail = func.signature;
|
||||
item.documentation = new vscode.MarkdownString(func.description);
|
||||
item.insertText = new vscode.SnippetString(`${func.name} \${1:arg}}}`)
|
||||
;
|
||||
completionItems.push(item);
|
||||
}
|
||||
|
||||
// Add keyword completions
|
||||
const keywords = ['if', 'else', 'end', 'range', 'with', 'define', 'template', 'block'];
|
||||
for (const keyword of keywords) {
|
||||
const item = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
|
||||
completionItems.push(item);
|
||||
}
|
||||
|
||||
return completionItems;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover provider for template functions
|
||||
class TemplateHoverProvider implements vscode.HoverProvider {
|
||||
provideHover(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
): vscode.Hover | undefined {
|
||||
const range = document.getWordRangeAtPosition(position);
|
||||
if (!range) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const word = document.getText(range);
|
||||
const functions = getTemplateFunctions();
|
||||
const func = functions.find(f => f.name === word);
|
||||
|
||||
if (func) {
|
||||
const markdown = new vscode.MarkdownString();
|
||||
markdown.appendCodeblock(func.signature, 'go');
|
||||
markdown.appendMarkdown('\n\n' + func.description);
|
||||
markdown.appendMarkdown('\n\n**Example:**\n');
|
||||
markdown.appendCodeblock(func.example, 'gotmpl');
|
||||
return new vscode.Hover(markdown);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
function renderTemplate(template: string, data: any): Promise<string> {
|
||||
// In a real implementation, this would call the Go binary
|
||||
// For now, return a placeholder
|
||||
return Promise.resolve(`-- Rendered SQL would appear here\n-- Template: ${template.substring(0, 50)}...`);
|
||||
}
|
||||
|
||||
function checkTemplateSyntax(template: string): Array<{ line: number; column: number; length: number; message: string }> {
|
||||
const errors: Array<{ line: number; column: number; length: number; message: string }> = [];
|
||||
|
||||
// Basic syntax checking
|
||||
const lines = template.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
// Check for unclosed {{
|
||||
const openCount = (line.match(/\{\{/g) || []).length;
|
||||
const closeCount = (line.match(/\}\}/g) || []).length;
|
||||
if (openCount > closeCount) {
|
||||
errors.push({
|
||||
line: i,
|
||||
column: line.indexOf('{{'),
|
||||
length: 2,
|
||||
message: 'Unclosed template tag'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
function getTemplateScaffold(type: string, name: string): string {
|
||||
const scaffolds: Record<string, string> = {
|
||||
ddl: `{{/* ${name} - DDL operation template */}}
|
||||
{{- define "${name}" -}}
|
||||
ALTER TABLE {{.SchemaName}}.{{.TableName}}
|
||||
-- Add your DDL operation here
|
||||
{{- end -}}`,
|
||||
constraint: `{{/* ${name} - Constraint template */}}
|
||||
{{- define "${name}" -}}
|
||||
ALTER TABLE {{.SchemaName}}.{{.TableName}}
|
||||
ADD CONSTRAINT {{.ConstraintName}}
|
||||
-- Add constraint definition here
|
||||
{{- end -}}`,
|
||||
index: `{{/* ${name} - Index template */}}
|
||||
{{- define "${name}" -}}
|
||||
CREATE {{if .Unique}}UNIQUE {{end}}INDEX IF NOT EXISTS {{.IndexName}}
|
||||
ON {{.SchemaName}}.{{.TableName}}
|
||||
USING {{.IndexType}} ({{join .Columns ", "}});
|
||||
{{- end -}}`,
|
||||
audit: `{{/* ${name} - Audit template */}}
|
||||
{{- define "${name}" -}}
|
||||
-- Audit configuration for {{.TableName}}
|
||||
{{- end -}}`,
|
||||
fragment: `{{/* ${name} - Reusable fragment */}}
|
||||
{{- define "${name}" -}}
|
||||
-- Add your reusable SQL fragment here
|
||||
{{- end -}}`
|
||||
};
|
||||
|
||||
return scaffolds[type] || scaffolds.fragment;
|
||||
}
|
||||
|
||||
function getTemplateFunctions() {
|
||||
return [
|
||||
{
|
||||
name: 'upper',
|
||||
signature: 'upper(s string) string',
|
||||
description: 'Convert string to uppercase',
|
||||
example: '{{upper "hello"}} // HELLO'
|
||||
},
|
||||
{
|
||||
name: 'lower',
|
||||
signature: 'lower(s string) string',
|
||||
description: 'Convert string to lowercase',
|
||||
example: '{{lower "HELLO"}} // hello'
|
||||
},
|
||||
{
|
||||
name: 'snake_case',
|
||||
signature: 'snake_case(s string) string',
|
||||
description: 'Convert string to snake_case',
|
||||
example: '{{snake_case "UserId"}} // user_id'
|
||||
},
|
||||
{
|
||||
name: 'camelCase',
|
||||
signature: 'camelCase(s string) string',
|
||||
description: 'Convert string to camelCase',
|
||||
example: '{{camelCase "user_id"}} // userId'
|
||||
},
|
||||
{
|
||||
name: 'quote',
|
||||
signature: 'quote(s string) string',
|
||||
description: 'Quote string for SQL (escapes single quotes)',
|
||||
example: '{{quote "O\'Brien"}} // \'O\'\'Brien\''
|
||||
},
|
||||
{
|
||||
name: 'safe_identifier',
|
||||
signature: 'safe_identifier(s string) string',
|
||||
description: 'Make string safe for SQL identifier',
|
||||
example: '{{safe_identifier "User-Id"}} // user_id'
|
||||
},
|
||||
{
|
||||
name: 'join',
|
||||
signature: 'join(slice []string, sep string) string',
|
||||
description: 'Join string slice with separator',
|
||||
example: '{{join .Columns ", "}}'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
function getWebviewContent(template: string, preview: string, data: any): string {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Template Preview</title>
|
||||
<style>
|
||||
body { font-family: var(--vscode-font-family); padding: 20px; }
|
||||
.section { margin-bottom: 30px; }
|
||||
h2 { color: var(--vscode-foreground); border-bottom: 1px solid var(--vscode-panel-border); }
|
||||
pre { background: var(--vscode-editor-background); padding: 15px; border-radius: 4px; overflow-x: auto; }
|
||||
.data { color: var(--vscode-editor-foreground); }
|
||||
.sql { color: var(--vscode-textPreformat-foreground); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="section">
|
||||
<h2>Template</h2>
|
||||
<pre class="data">${escapeHtml(template)}</pre>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>Sample Data</h2>
|
||||
<pre class="data">${escapeHtml(JSON.stringify(data, null, 2))}</pre>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>Rendered SQL</h2>
|
||||
<pre class="sql">${escapeHtml(preview)}</pre>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function getErrorWebviewContent(error: string): string {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Error</title>
|
||||
<style>
|
||||
body { font-family: var(--vscode-font-family); padding: 20px; }
|
||||
.error { color: var(--vscode-errorForeground); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2 class="error">Template Error</h2>
|
||||
<pre>${escapeHtml(error)}</pre>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function getFunctionsWebviewContent(functions: any[]): string {
|
||||
const functionsHtml = functions.map(f => `
|
||||
<div class="function">
|
||||
<h3>${f.name}</h3>
|
||||
<code>${f.signature}</code>
|
||||
<p>${f.description}</p>
|
||||
<pre>${f.example}</pre>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Template Functions</title>
|
||||
<style>
|
||||
body { font-family: var(--vscode-font-family); padding: 20px; }
|
||||
.function { margin-bottom: 30px; border-bottom: 1px solid var(--vscode-panel-border); padding-bottom: 20px; }
|
||||
h3 { color: var(--vscode-foreground); margin-bottom: 10px; }
|
||||
code { background: var(--vscode-textCodeBlock-background); padding: 4px 8px; border-radius: 3px; }
|
||||
pre { background: var(--vscode-editor-background); padding: 15px; border-radius: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Available Template Functions</h1>
|
||||
${functionsHtml}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function escapeHtml(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
19
vscode-extension/relspec-template-editor/tsconfig.json
Normal file
19
vscode-extension/relspec-template-editor/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "ES2020",
|
||||
"lib": ["ES2020"],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "out",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "out"]
|
||||
}
|
||||
Reference in New Issue
Block a user