zedx 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/dist/generator.d.ts +1 -1
- package/dist/generator.js +52 -22
- package/dist/index.js +37 -29
- package/dist/prompts.d.ts +3 -2
- package/dist/prompts.js +80 -47
- package/dist/templates/base/extension.toml.ejs +14 -1
- package/dist/templates/language/brackets.scm +23 -0
- package/dist/templates/language/config.toml.ejs +29 -0
- package/dist/templates/language/highlights.scm +32 -0
- package/dist/templates/language/indents.scm +17 -0
- package/dist/templates/language/injections.scm +24 -0
- package/dist/templates/language/outline.scm +17 -0
- package/dist/templates/language/overrides.scm +20 -0
- package/dist/templates/language/redactions.scm +16 -0
- package/dist/templates/language/runnables.scm +25 -0
- package/dist/templates/language/textobjects.scm +26 -0
- package/dist/types/index.d.ts +13 -1
- package/package.json +51 -51
package/README.md
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
# zedx
|
|
2
2
|
|
|
3
|
-
Boilerplate generator for Zed extensions.
|
|
3
|
+
Boilerplate generator for [Zed Editor](https://zed.dev/) extensions.
|
|
4
|
+
|
|
5
|
+

|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm install -g zedx
|
|
11
|
+
|
|
12
|
+
# or
|
|
13
|
+
brew install tahayvr/tap/zedx
|
|
9
14
|
```
|
|
10
15
|
|
|
11
16
|
### Usage
|
|
@@ -16,13 +21,14 @@ zedx
|
|
|
16
21
|
|
|
17
22
|
# Bump extension version
|
|
18
23
|
# Run inside extension dir
|
|
19
|
-
zedx
|
|
20
|
-
zedx
|
|
21
|
-
zedx
|
|
24
|
+
zedx version patch # 1.2.3 → 1.2.4
|
|
25
|
+
zedx version minor # 1.2.3 → 1.3.0
|
|
26
|
+
zedx version major # 1.2.3 → 2.0.0
|
|
22
27
|
```
|
|
23
28
|
|
|
24
29
|
### Supported extension types:
|
|
25
30
|
|
|
26
|
-
1. Themes
|
|
31
|
+
1. **Themes** - Color schemes for the editor
|
|
32
|
+
2. **Languages** - Syntax highlighting, indentation, and optional LSP support
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
You can choose to include theme, language, or both when creating an extension.
|
package/dist/generator.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ExtensionOptions } from
|
|
1
|
+
import type { ExtensionOptions } from './types/index.js';
|
|
2
2
|
export declare function generateExtension(options: ExtensionOptions, targetDir: string): Promise<void>;
|
package/dist/generator.js
CHANGED
|
@@ -1,37 +1,67 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import { fileURLToPath } from
|
|
4
|
-
import ejs from
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import ejs from 'ejs';
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
|
-
const isDev = __dirname.includes(
|
|
8
|
-
const PROJECT_ROOT = isDev ? path.join(__dirname,
|
|
9
|
-
const TEMPLATE_DIR = path.join(PROJECT_ROOT,
|
|
7
|
+
const isDev = __dirname.includes('/src/');
|
|
8
|
+
const PROJECT_ROOT = isDev ? path.join(__dirname, '..') : __dirname;
|
|
9
|
+
const TEMPLATE_DIR = path.join(PROJECT_ROOT, 'templates');
|
|
10
10
|
async function renderTemplate(templatePath, data) {
|
|
11
|
-
const template = await fs.readFile(templatePath,
|
|
11
|
+
const template = await fs.readFile(templatePath, 'utf-8');
|
|
12
12
|
return ejs.render(template, data);
|
|
13
13
|
}
|
|
14
14
|
export async function generateExtension(options, targetDir) {
|
|
15
15
|
await fs.ensureDir(targetDir);
|
|
16
|
-
const extToml = await renderTemplate(path.join(TEMPLATE_DIR,
|
|
17
|
-
await fs.writeFile(path.join(targetDir,
|
|
18
|
-
const licensePath = path.join(TEMPLATE_DIR,
|
|
19
|
-
let licenseContent = await fs.readFile(licensePath,
|
|
20
|
-
licenseContent = licenseContent.replaceAll(
|
|
21
|
-
licenseContent = licenseContent.replaceAll(
|
|
22
|
-
await fs.writeFile(path.join(targetDir,
|
|
23
|
-
|
|
16
|
+
const extToml = await renderTemplate(path.join(TEMPLATE_DIR, 'base/extension.toml.ejs'), options);
|
|
17
|
+
await fs.writeFile(path.join(targetDir, 'extension.toml'), extToml);
|
|
18
|
+
const licensePath = path.join(TEMPLATE_DIR, 'base/licenses', options.license);
|
|
19
|
+
let licenseContent = await fs.readFile(licensePath, 'utf-8');
|
|
20
|
+
licenseContent = licenseContent.replaceAll('{{YEAR}}', new Date().getFullYear().toString());
|
|
21
|
+
licenseContent = licenseContent.replaceAll('{{AUTHOR}}', options.author);
|
|
22
|
+
await fs.writeFile(path.join(targetDir, 'LICENSE'), licenseContent);
|
|
23
|
+
if (options.types.includes('theme')) {
|
|
24
|
+
await generateTheme(options, targetDir);
|
|
25
|
+
}
|
|
26
|
+
if (options.types.includes('language')) {
|
|
27
|
+
await generateLanguage(options, targetDir);
|
|
28
|
+
}
|
|
24
29
|
}
|
|
25
30
|
async function generateTheme(options, targetDir) {
|
|
26
|
-
const themeDir = path.join(targetDir,
|
|
31
|
+
const themeDir = path.join(targetDir, 'themes');
|
|
27
32
|
await fs.ensureDir(themeDir);
|
|
28
|
-
const appearance = options.appearance ||
|
|
29
|
-
const appearances = appearance ===
|
|
33
|
+
const appearance = options.appearance || 'dark';
|
|
34
|
+
const appearances = appearance === 'both' ? ['dark', 'light'] : [appearance];
|
|
30
35
|
const themeData = {
|
|
31
36
|
...options,
|
|
32
|
-
themeName: options.themeName ||
|
|
33
|
-
appearances
|
|
37
|
+
themeName: options.themeName || 'My Theme',
|
|
38
|
+
appearances
|
|
34
39
|
};
|
|
35
|
-
const themeJson = await renderTemplate(path.join(TEMPLATE_DIR,
|
|
40
|
+
const themeJson = await renderTemplate(path.join(TEMPLATE_DIR, 'theme/theme.json.ejs'), themeData);
|
|
36
41
|
await fs.writeFile(path.join(themeDir, `${options.id}.json`), themeJson);
|
|
37
42
|
}
|
|
43
|
+
async function generateLanguage(options, targetDir) {
|
|
44
|
+
const languageDir = path.join(targetDir, 'languages', options.languageId);
|
|
45
|
+
await fs.ensureDir(languageDir);
|
|
46
|
+
const configToml = await renderTemplate(path.join(TEMPLATE_DIR, 'language/config.toml.ejs'), options);
|
|
47
|
+
await fs.writeFile(path.join(languageDir, 'config.toml'), configToml);
|
|
48
|
+
const queryFiles = [
|
|
49
|
+
'highlights.scm',
|
|
50
|
+
'brackets.scm',
|
|
51
|
+
'outline.scm',
|
|
52
|
+
'indents.scm',
|
|
53
|
+
'injections.scm',
|
|
54
|
+
'overrides.scm',
|
|
55
|
+
'textobjects.scm',
|
|
56
|
+
'redactions.scm',
|
|
57
|
+
'runnables.scm'
|
|
58
|
+
];
|
|
59
|
+
for (const file of queryFiles) {
|
|
60
|
+
const templatePath = path.join(TEMPLATE_DIR, 'language', file);
|
|
61
|
+
if (await fs.pathExists(templatePath)) {
|
|
62
|
+
let content = await fs.readFile(templatePath, 'utf-8');
|
|
63
|
+
content = ejs.render(content, options);
|
|
64
|
+
await fs.writeFile(path.join(languageDir, file), content);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import path from
|
|
3
|
-
import * as p from
|
|
4
|
-
import color from
|
|
5
|
-
import { Command } from
|
|
6
|
-
import fs from
|
|
7
|
-
import { promptUser, promptThemeDetails } from
|
|
8
|
-
import { generateExtension } from
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import * as p from '@clack/prompts';
|
|
4
|
+
import color from 'picocolors';
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import { promptUser, promptThemeDetails, promptLanguageDetails } from './prompts.js';
|
|
8
|
+
import { generateExtension } from './generator.js';
|
|
9
9
|
function bumpVersion(version, type) {
|
|
10
|
-
const [major, minor, patch] = version.split(
|
|
10
|
+
const [major, minor, patch] = version.split('.').map(Number);
|
|
11
11
|
switch (type) {
|
|
12
|
-
case
|
|
12
|
+
case 'major':
|
|
13
13
|
return `${major + 1}.0.0`;
|
|
14
|
-
case
|
|
14
|
+
case 'minor':
|
|
15
15
|
return `${major}.${minor + 1}.0`;
|
|
16
|
-
case
|
|
16
|
+
case 'patch':
|
|
17
17
|
return `${major}.${minor}.${patch + 1}`;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -22,15 +22,15 @@ function getCallerDir() {
|
|
|
22
22
|
}
|
|
23
23
|
async function bumpExtensionVersion(type) {
|
|
24
24
|
const callerDir = getCallerDir();
|
|
25
|
-
const tomlPath = path.join(callerDir,
|
|
25
|
+
const tomlPath = path.join(callerDir, 'extension.toml');
|
|
26
26
|
if (!(await fs.pathExists(tomlPath))) {
|
|
27
|
-
p.log.error(color.red(
|
|
27
|
+
p.log.error(color.red('No extension.toml found in current directory.'));
|
|
28
28
|
process.exit(1);
|
|
29
29
|
}
|
|
30
|
-
const content = await fs.readFile(tomlPath,
|
|
30
|
+
const content = await fs.readFile(tomlPath, 'utf-8');
|
|
31
31
|
const versionMatch = content.match(/version\s*=\s*"(\d+\.\d+\.\d+)"/);
|
|
32
32
|
if (!versionMatch) {
|
|
33
|
-
p.log.error(color.red(
|
|
33
|
+
p.log.error(color.red('Could not find version in extension.toml'));
|
|
34
34
|
process.exit(1);
|
|
35
35
|
}
|
|
36
36
|
const currentVersion = versionMatch[1];
|
|
@@ -41,30 +41,38 @@ async function bumpExtensionVersion(type) {
|
|
|
41
41
|
}
|
|
42
42
|
async function main() {
|
|
43
43
|
const program = new Command();
|
|
44
|
-
program.name(
|
|
44
|
+
program.name('zedx').description('Boilerplate generator for Zed Editor extensions.');
|
|
45
45
|
program
|
|
46
|
-
.command(
|
|
47
|
-
.description(
|
|
48
|
-
.argument(
|
|
46
|
+
.command('version')
|
|
47
|
+
.description('Bump the version of the extension')
|
|
48
|
+
.argument('<type>', 'Version bump type: major, minor, or patch')
|
|
49
49
|
.action(async (type) => {
|
|
50
|
-
if (![
|
|
51
|
-
p.log.error(color.red(
|
|
50
|
+
if (!['major', 'minor', 'patch'].includes(type)) {
|
|
51
|
+
p.log.error(color.red('Invalid bump type. Use: major, minor, or patch'));
|
|
52
52
|
process.exit(1);
|
|
53
53
|
}
|
|
54
54
|
await bumpExtensionVersion(type);
|
|
55
55
|
});
|
|
56
56
|
if (process.argv.length <= 2) {
|
|
57
|
-
// No command provided, run interactive mode
|
|
58
57
|
const options = await promptUser();
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
if (options.types.includes('theme')) {
|
|
59
|
+
const themeDetails = await promptThemeDetails();
|
|
60
|
+
Object.assign(options, themeDetails);
|
|
61
|
+
}
|
|
62
|
+
if (options.types.includes('language')) {
|
|
63
|
+
const languageDetails = await promptLanguageDetails();
|
|
64
|
+
Object.assign(options, languageDetails);
|
|
65
|
+
}
|
|
61
66
|
const targetDir = path.join(getCallerDir(), options.id);
|
|
62
67
|
await generateExtension(options, targetDir);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
p.
|
|
67
|
-
|
|
68
|
+
p.outro(`${color.green('✓')} ${color.bold('Extension created successfully!')}\n` +
|
|
69
|
+
`${color.gray('─'.repeat(40))}\n` +
|
|
70
|
+
`${color.dim('Location:')} ${color.cyan(targetDir)}`);
|
|
71
|
+
p.outro(`${color.yellow('⚡')} ${color.bold('Next steps')}\n\n` +
|
|
72
|
+
` ${color.gray('1.')} Open Zed\n` +
|
|
73
|
+
` ${color.gray('2.')} ${color.white('Extensions > Install Dev Extension')}\n` +
|
|
74
|
+
` ${color.gray('3.')} Select ${color.cyan(options.id)} folder\n\n` +
|
|
75
|
+
`${color.dim('Learn more:')} ${color.underline(color.blue('https://zed.dev/docs/extensions/developing-extensions'))}`);
|
|
68
76
|
return;
|
|
69
77
|
}
|
|
70
78
|
program.parse(process.argv);
|
package/dist/prompts.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { ExtensionOptions } from
|
|
1
|
+
import type { ExtensionOptions, LanguageOptions } from './types/index.js';
|
|
2
2
|
export declare function promptUser(): Promise<ExtensionOptions>;
|
|
3
3
|
export declare function promptThemeDetails(): Promise<{
|
|
4
4
|
themeName: string;
|
|
5
|
-
appearance:
|
|
5
|
+
appearance: 'light' | 'dark' | 'both';
|
|
6
6
|
}>;
|
|
7
|
+
export declare function promptLanguageDetails(): Promise<Partial<LanguageOptions>>;
|
package/dist/prompts.js
CHANGED
|
@@ -1,79 +1,92 @@
|
|
|
1
|
-
import * as p from
|
|
2
|
-
import color from
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import color from 'picocolors';
|
|
3
3
|
export async function promptUser() {
|
|
4
|
-
p.intro(color.bgBlue(
|
|
5
|
-
const nameDefault =
|
|
4
|
+
p.intro(`${color.bgBlue(color.bold(' zedx '))} ${color.blue('Boilerplate generator for Zed Editor extensions.')}`);
|
|
5
|
+
const nameDefault = 'my-zed-extension';
|
|
6
6
|
const name = await p.text({
|
|
7
|
-
message:
|
|
8
|
-
placeholder: nameDefault
|
|
7
|
+
message: 'Project name:',
|
|
8
|
+
placeholder: nameDefault
|
|
9
9
|
});
|
|
10
10
|
if (p.isCancel(name)) {
|
|
11
|
-
p.cancel(
|
|
11
|
+
p.cancel('Cancelled.');
|
|
12
12
|
process.exit(0);
|
|
13
13
|
}
|
|
14
14
|
const nameValue = name || nameDefault;
|
|
15
|
-
const idDefault = nameValue.toLowerCase().replace(/\s+/g,
|
|
15
|
+
const idDefault = nameValue.toLowerCase().replace(/\s+/g, '-');
|
|
16
16
|
const id = await p.text({
|
|
17
|
-
message:
|
|
17
|
+
message: 'Extension ID:',
|
|
18
18
|
placeholder: idDefault,
|
|
19
19
|
validate: (value) => {
|
|
20
|
-
if (value && value.includes(
|
|
21
|
-
return
|
|
20
|
+
if (value && value.includes(' '))
|
|
21
|
+
return 'ID cannot contain spaces';
|
|
22
22
|
if (value && value !== value.toLowerCase())
|
|
23
|
-
return
|
|
24
|
-
}
|
|
23
|
+
return 'ID must be lowercase';
|
|
24
|
+
}
|
|
25
25
|
});
|
|
26
26
|
if (p.isCancel(id)) {
|
|
27
|
-
p.cancel(
|
|
27
|
+
p.cancel('Cancelled.');
|
|
28
28
|
process.exit(0);
|
|
29
29
|
}
|
|
30
30
|
const idValue = id || idDefault;
|
|
31
|
-
const descriptionDefault =
|
|
31
|
+
const descriptionDefault = 'A Zed extension';
|
|
32
32
|
const description = await p.text({
|
|
33
|
-
message:
|
|
34
|
-
placeholder: descriptionDefault
|
|
33
|
+
message: 'Description:',
|
|
34
|
+
placeholder: descriptionDefault
|
|
35
35
|
});
|
|
36
36
|
if (p.isCancel(description)) {
|
|
37
|
-
p.cancel(
|
|
37
|
+
p.cancel('Cancelled.');
|
|
38
38
|
process.exit(0);
|
|
39
39
|
}
|
|
40
40
|
const descriptionValue = description || descriptionDefault;
|
|
41
41
|
const author = await p.text({
|
|
42
|
-
message:
|
|
42
|
+
message: 'Author name:',
|
|
43
|
+
placeholder: 'name <username@example.com>',
|
|
43
44
|
validate: (value) => {
|
|
44
45
|
if (!value || value.length === 0)
|
|
45
|
-
return
|
|
46
|
-
}
|
|
46
|
+
return 'Author is required';
|
|
47
|
+
}
|
|
47
48
|
});
|
|
48
49
|
if (p.isCancel(author)) {
|
|
49
|
-
p.cancel(
|
|
50
|
+
p.cancel('Cancelled.');
|
|
50
51
|
process.exit(0);
|
|
51
52
|
}
|
|
52
|
-
const repositoryDefault = `https://github.com/${
|
|
53
|
+
const repositoryDefault = `https://github.com/username/${idValue}.git`;
|
|
53
54
|
const repository = await p.text({
|
|
54
|
-
message:
|
|
55
|
-
initialValue: repositoryDefault
|
|
55
|
+
message: 'GitHub repository URL:',
|
|
56
|
+
initialValue: repositoryDefault
|
|
56
57
|
});
|
|
57
58
|
if (p.isCancel(repository)) {
|
|
58
|
-
p.cancel(
|
|
59
|
+
p.cancel('Cancelled.');
|
|
59
60
|
process.exit(0);
|
|
60
61
|
}
|
|
61
62
|
const repositoryValue = repository || repositoryDefault;
|
|
62
63
|
const license = await p.select({
|
|
63
|
-
message:
|
|
64
|
+
message: 'License:',
|
|
64
65
|
options: [
|
|
65
|
-
{ value:
|
|
66
|
-
{ value:
|
|
67
|
-
{ value:
|
|
68
|
-
{ value:
|
|
69
|
-
{ value:
|
|
70
|
-
{ value:
|
|
71
|
-
{ value:
|
|
66
|
+
{ value: 'Apache-2.0', label: 'Apache 2.0' },
|
|
67
|
+
{ value: 'BSD-2-Clause', label: 'BSD 2-Clause' },
|
|
68
|
+
{ value: 'BSD-3-Clause', label: 'BSD 3-Clause' },
|
|
69
|
+
{ value: 'GPL-3.0', label: 'GNU GPLv3' },
|
|
70
|
+
{ value: 'LGPL-3.0', label: 'GNU LGPLv3' },
|
|
71
|
+
{ value: 'MIT', label: 'MIT' },
|
|
72
|
+
{ value: 'Zlib', label: 'zlib' }
|
|
72
73
|
],
|
|
73
|
-
initialValue:
|
|
74
|
+
initialValue: 'MIT'
|
|
74
75
|
});
|
|
75
76
|
if (p.isCancel(license)) {
|
|
76
|
-
p.cancel(
|
|
77
|
+
p.cancel('Cancelled.');
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
const extensionTypes = await p.multiselect({
|
|
81
|
+
message: 'What do you want to include in your extension?',
|
|
82
|
+
options: [
|
|
83
|
+
{ value: 'theme', label: 'Theme', hint: 'Color scheme for the editor' },
|
|
84
|
+
{ value: 'language', label: 'Language', hint: 'Syntax highlighting, indentation, etc.' }
|
|
85
|
+
],
|
|
86
|
+
required: true
|
|
87
|
+
});
|
|
88
|
+
if (p.isCancel(extensionTypes)) {
|
|
89
|
+
p.cancel('Cancelled.');
|
|
77
90
|
process.exit(0);
|
|
78
91
|
}
|
|
79
92
|
const options = {
|
|
@@ -83,34 +96,54 @@ export async function promptUser() {
|
|
|
83
96
|
author: String(author),
|
|
84
97
|
repository: repositoryValue,
|
|
85
98
|
license: license,
|
|
86
|
-
types:
|
|
99
|
+
types: extensionTypes
|
|
87
100
|
};
|
|
88
101
|
return options;
|
|
89
102
|
}
|
|
90
103
|
export async function promptThemeDetails() {
|
|
91
104
|
const themeName = await p.text({
|
|
92
|
-
message:
|
|
93
|
-
placeholder:
|
|
105
|
+
message: 'Theme name:',
|
|
106
|
+
placeholder: 'My Theme'
|
|
94
107
|
});
|
|
95
108
|
if (p.isCancel(themeName)) {
|
|
96
|
-
p.cancel(
|
|
109
|
+
p.cancel('Cancelled.');
|
|
97
110
|
process.exit(0);
|
|
98
111
|
}
|
|
99
112
|
const appearance = await p.select({
|
|
100
|
-
message:
|
|
113
|
+
message: 'Appearance:',
|
|
101
114
|
options: [
|
|
102
|
-
{ value:
|
|
103
|
-
{ value:
|
|
104
|
-
{ value:
|
|
115
|
+
{ value: 'dark', label: 'Dark' },
|
|
116
|
+
{ value: 'light', label: 'Light' },
|
|
117
|
+
{ value: 'both', label: 'Both (Dark & Light)' }
|
|
105
118
|
],
|
|
106
|
-
initialValue:
|
|
119
|
+
initialValue: 'dark'
|
|
107
120
|
});
|
|
108
121
|
if (p.isCancel(appearance)) {
|
|
109
|
-
p.cancel(
|
|
122
|
+
p.cancel('Cancelled.');
|
|
110
123
|
process.exit(0);
|
|
111
124
|
}
|
|
112
125
|
return {
|
|
113
126
|
themeName: String(themeName),
|
|
114
|
-
appearance: appearance
|
|
127
|
+
appearance: appearance
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export async function promptLanguageDetails() {
|
|
131
|
+
const languageName = await p.text({
|
|
132
|
+
message: 'Language name:',
|
|
133
|
+
placeholder: 'My Language'
|
|
134
|
+
});
|
|
135
|
+
if (p.isCancel(languageName)) {
|
|
136
|
+
p.cancel('Cancelled.');
|
|
137
|
+
process.exit(0);
|
|
138
|
+
}
|
|
139
|
+
const result = {
|
|
140
|
+
languageName: String(languageName),
|
|
141
|
+
languageId: String(languageName).toLowerCase().replace(/\s+/g, '-'),
|
|
142
|
+
pathSuffixes: [],
|
|
143
|
+
lineComments: [],
|
|
144
|
+
grammarRepo: '',
|
|
145
|
+
grammarRev: '',
|
|
146
|
+
hasLsp: false
|
|
115
147
|
};
|
|
148
|
+
return result;
|
|
116
149
|
}
|
|
@@ -2,6 +2,19 @@ id = "<%= id %>"
|
|
|
2
2
|
name = "<%= name %>"
|
|
3
3
|
version = "0.0.1"
|
|
4
4
|
schema_version = 1
|
|
5
|
-
authors = ["
|
|
5
|
+
authors = ["<%- author %>"]
|
|
6
6
|
description = "<%= description %>"
|
|
7
7
|
repository = "<%= repository %>"
|
|
8
|
+
|
|
9
|
+
<% if (types.includes('language') && grammarRepo) { %>
|
|
10
|
+
[grammars.<%= languageId %>]
|
|
11
|
+
repository = "<%= grammarRepo %>"
|
|
12
|
+
rev = "<%= grammarRev %>"
|
|
13
|
+
<% } %>
|
|
14
|
+
<% if (hasLsp && lspServerName) { %>
|
|
15
|
+
[language_servers.<%= lspServerName %>]
|
|
16
|
+
name = "<%= lspServerName %>"
|
|
17
|
+
languages = ["<%= languageName %>"]
|
|
18
|
+
|
|
19
|
+
[languages."<%= languageName %>"]
|
|
20
|
+
<% } %>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Bracket Matching Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines matching brackets for rainbow bracket highlighting.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#bracket-matching
|
|
6
|
+
;
|
|
7
|
+
; Captures:
|
|
8
|
+
; @open - Opening bracket
|
|
9
|
+
; @close - Closing bracket
|
|
10
|
+
;
|
|
11
|
+
; To exclude from rainbow brackets, add: (#set! rainbow.exclude)
|
|
12
|
+
;
|
|
13
|
+
; Example:
|
|
14
|
+
; ("(" @open ")" @close)
|
|
15
|
+
; ("[" @open "]" @close)
|
|
16
|
+
; ("{" @open "}" @close)
|
|
17
|
+
;
|
|
18
|
+
; Note: Quotes need escaping in .scm files:
|
|
19
|
+
; ("\"" @open "\"" @close)
|
|
20
|
+
|
|
21
|
+
("(" @open ")" @close)
|
|
22
|
+
("[" @open "]" @close)
|
|
23
|
+
("{" @open "}" @close)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<%= languageName %>
|
|
2
|
+
<%= "=".repeat(languageName.length) %>
|
|
3
|
+
|
|
4
|
+
; name (required): The human readable name shown in the Select Language dropdown
|
|
5
|
+
name = "<%= languageName %>"
|
|
6
|
+
|
|
7
|
+
; grammar (required): The name of a Tree-sitter grammar (must match grammar registration in extension.toml)
|
|
8
|
+
grammar = "<%= languageId %>"
|
|
9
|
+
|
|
10
|
+
; path_suffixes: Array of file suffixes associated with this language (e.g., ["myl", "my"])
|
|
11
|
+
; Unlike file_types in settings, this does not support glob patterns
|
|
12
|
+
path_suffixes = <%- pathSuffixes.length > 0 ? JSON.stringify(pathSuffixes) : '[""]' %>
|
|
13
|
+
|
|
14
|
+
; line_comments: Array of strings used to identify line comments
|
|
15
|
+
; Used for editor::ToggleComments keybind (cmd-/ or ctrl-/)
|
|
16
|
+
line_comments = <%- lineComments.length > 0 ? JSON.stringify(lineComments) : '[""]' %>
|
|
17
|
+
|
|
18
|
+
; tab_size: The indentation/tab size used for this language (default is 4)
|
|
19
|
+
; tab_size = 4
|
|
20
|
+
|
|
21
|
+
; hard_tabs: Whether to indent with tabs (true) or spaces (false, the default)
|
|
22
|
+
; hard_tabs = false
|
|
23
|
+
|
|
24
|
+
; first_line_pattern: A regular expression to match files based on their first line
|
|
25
|
+
; Useful for identifying files by shebang (e.g., "^#!.*\\bbash\\b" for shell scripts)
|
|
26
|
+
; first_line_pattern = ""
|
|
27
|
+
|
|
28
|
+
; debuggers: Array of debugger identifiers for ordering in the New Process Modal
|
|
29
|
+
; debuggers = []
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Syntax Highlighting Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines syntax highlighting rules using Tree-sitter queries.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#syntax-highlighting
|
|
6
|
+
;
|
|
7
|
+
; Capture names (prefix with @):
|
|
8
|
+
; @string - String literals
|
|
9
|
+
; @number - Numeric values
|
|
10
|
+
; @comment - Comments
|
|
11
|
+
; @keyword - Keywords
|
|
12
|
+
; @function - Functions
|
|
13
|
+
; @type - Types
|
|
14
|
+
; @variable - Variables
|
|
15
|
+
; @property - Properties
|
|
16
|
+
; @operator - Operators
|
|
17
|
+
; @punctuation - Punctuation
|
|
18
|
+
; @constant - Constants
|
|
19
|
+
; @attribute - Attributes
|
|
20
|
+
; @tag - Tags
|
|
21
|
+
;
|
|
22
|
+
; Example:
|
|
23
|
+
; (string) @string
|
|
24
|
+
; (number) @number
|
|
25
|
+
; (comment) @comment
|
|
26
|
+
;
|
|
27
|
+
; Learn more about Tree-sitter queries:
|
|
28
|
+
; https://tree-sitter.github.io/tree-sitter/using-parsers/queries
|
|
29
|
+
|
|
30
|
+
(string) @string
|
|
31
|
+
(number) @number
|
|
32
|
+
(comment) @comment
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Auto-Indentation Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines indentation rules for automatic formatting.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#auto-indentation
|
|
6
|
+
;
|
|
7
|
+
; Captures:
|
|
8
|
+
; @end - Closing bracket/brace that ends an indented block
|
|
9
|
+
; @indent - Entire block that should increase indentation
|
|
10
|
+
;
|
|
11
|
+
; Example for block-based languages:
|
|
12
|
+
; (block "}" @end) @indent
|
|
13
|
+
; (function "}" @end) @indent
|
|
14
|
+
;
|
|
15
|
+
; Example for indentation-based languages:
|
|
16
|
+
; (list_item) @indent
|
|
17
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Code Injection Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines rules for embedding one language within another.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#code-injections
|
|
6
|
+
;
|
|
7
|
+
; Common use cases:
|
|
8
|
+
; - SQL queries in Python strings
|
|
9
|
+
; - Code blocks in Markdown
|
|
10
|
+
; - CSS in HTML style tags
|
|
11
|
+
; - JavaScript in JSX/TSX
|
|
12
|
+
;
|
|
13
|
+
; Captures:
|
|
14
|
+
; @injection.language - The language identifier (e.g., "python", "javascript")
|
|
15
|
+
; @injection.content - The content to treat as a different language
|
|
16
|
+
;
|
|
17
|
+
; Example for Markdown code blocks:
|
|
18
|
+
; (fenced_code_block
|
|
19
|
+
; (info_string (language) @injection.language)
|
|
20
|
+
; (code_fence_content) @injection.content)
|
|
21
|
+
;
|
|
22
|
+
; Example for inline markdown:
|
|
23
|
+
; ((inline) @content (#set! injection.language "markdown-inline"))
|
|
24
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Code Outline Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines the structure shown in the code outline panel.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#code-outlinestructure
|
|
6
|
+
;
|
|
7
|
+
; Captures:
|
|
8
|
+
; @name - Display name for the outline item
|
|
9
|
+
; @item - The entire element to show in outline
|
|
10
|
+
; @context - Provides context for the item
|
|
11
|
+
; @context.extra - Additional contextual info
|
|
12
|
+
; @annotation - Annotations (doc comments, decorators)
|
|
13
|
+
;
|
|
14
|
+
; Example:
|
|
15
|
+
; (function_definition name: (identifier) @name) @item
|
|
16
|
+
; (class_declaration name: (type_identifier) @name) @item
|
|
17
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Syntax Override Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines scoped overrides for editor settings.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#syntax-overrides
|
|
6
|
+
;
|
|
7
|
+
; Available overrides:
|
|
8
|
+
; word_characters - Characters considered part of a word
|
|
9
|
+
; completion_query_characters - Characters that trigger autocomplete
|
|
10
|
+
;
|
|
11
|
+
; Use .inclusive suffix to make range inclusive (default is exclusive):
|
|
12
|
+
; (comment) @comment.inclusive
|
|
13
|
+
;
|
|
14
|
+
; Example - JavaScript strings with hyphen completion:
|
|
15
|
+
; (string) @string
|
|
16
|
+
; ; Then set in config.toml:
|
|
17
|
+
; ; [overrides.string]
|
|
18
|
+
; ; completion_query_characters = ["-"]
|
|
19
|
+
|
|
20
|
+
(string) @string
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Text Redaction Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines content to redact when sharing screen/collaborating.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#text-redactions
|
|
6
|
+
;
|
|
7
|
+
; Redacted content is rendered in a neutral style to protect sensitive data.
|
|
8
|
+
;
|
|
9
|
+
; Captures:
|
|
10
|
+
; @redact - Content to be redacted
|
|
11
|
+
;
|
|
12
|
+
; Example:
|
|
13
|
+
; (string) @redact
|
|
14
|
+
; (number) @redact
|
|
15
|
+
; (environment_variable (string) @redact)
|
|
16
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Runnable Code Detection Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines rules for detecting runnable code blocks.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#runnable-code-detection
|
|
6
|
+
;
|
|
7
|
+
; Adds run buttons to the editor gutter for executable code/scripts.
|
|
8
|
+
;
|
|
9
|
+
; Captures:
|
|
10
|
+
; @run - Where to place the run button
|
|
11
|
+
; @script - Also captures the script name
|
|
12
|
+
;
|
|
13
|
+
; Other captures (except underscore-prefixed) are exposed as:
|
|
14
|
+
; ZED_CUSTOM_<CAPTURE_NAME> environment variables
|
|
15
|
+
;
|
|
16
|
+
; Tags (using #set! tag):
|
|
17
|
+
; (#set! tag <tag-name>) - Additional metadata for the runnable
|
|
18
|
+
;
|
|
19
|
+
; Example for package.json scripts:
|
|
20
|
+
; (pair
|
|
21
|
+
; key: (string (string_content) @_name (#eq? @_name "scripts"))
|
|
22
|
+
; value: (object
|
|
23
|
+
; (pair
|
|
24
|
+
; key: (string (string_content) @run @script))))
|
|
25
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
; =============================================================================
|
|
2
|
+
; Text Object Queries
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; This file defines text objects for Vim mode navigation.
|
|
5
|
+
; See: https://zed.dev/docs/extensions/languages#text-objects
|
|
6
|
+
;
|
|
7
|
+
; Vim motions using these:
|
|
8
|
+
; [m / ]m - Previous/next function (or @function.around)
|
|
9
|
+
; [M / ]M - Previous/next class (or @class.around)
|
|
10
|
+
; af / if - Around/inside function
|
|
11
|
+
; ac / ic - Around/inside class
|
|
12
|
+
; gc - Around comment
|
|
13
|
+
;
|
|
14
|
+
; Captures:
|
|
15
|
+
; @function.around - Entire function definition
|
|
16
|
+
; @function.inside - Function body (inside braces)
|
|
17
|
+
; @class.around - Entire class definition
|
|
18
|
+
; @class.inside - Class contents
|
|
19
|
+
; @comment.around - Entire comment block
|
|
20
|
+
; @comment.inside - Comment contents
|
|
21
|
+
;
|
|
22
|
+
; Example:
|
|
23
|
+
; (function_definition) @function.around
|
|
24
|
+
; (function_definition body: (_) @function.inside)
|
|
25
|
+
; (class_declaration) @class.around
|
|
26
|
+
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ExtensionType = 'theme';
|
|
1
|
+
export type ExtensionType = 'theme' | 'language';
|
|
2
2
|
export type License = 'Apache-2.0' | 'BSD-2-Clause' | 'BSD-3-Clause' | 'GPL-3.0' | 'LGPL-3.0' | 'MIT' | 'Zlib';
|
|
3
3
|
export interface ExtensionOptions {
|
|
4
4
|
name: string;
|
|
@@ -14,3 +14,15 @@ export interface ThemeOptions extends ExtensionOptions {
|
|
|
14
14
|
themeName: string;
|
|
15
15
|
appearance: 'light' | 'dark' | 'both';
|
|
16
16
|
}
|
|
17
|
+
export interface LanguageOptions extends ExtensionOptions {
|
|
18
|
+
languageName: string;
|
|
19
|
+
languageId: string;
|
|
20
|
+
pathSuffixes: string[];
|
|
21
|
+
lineComments: string[];
|
|
22
|
+
grammarRepo: string;
|
|
23
|
+
grammarRev: string;
|
|
24
|
+
hasLsp: boolean;
|
|
25
|
+
lspServerName?: string;
|
|
26
|
+
lspCommand?: string;
|
|
27
|
+
lspArgs?: string[];
|
|
28
|
+
}
|
package/package.json
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
2
|
+
"name": "zedx",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Boilerplate generator for Zed Edittor extensions.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"zedx": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && cp -r src/templates dist/",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"dev": "tsx src/index.ts"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"zed",
|
|
20
|
+
"zed-editor",
|
|
21
|
+
"extension",
|
|
22
|
+
"boilerplate",
|
|
23
|
+
"scaffold"
|
|
24
|
+
],
|
|
25
|
+
"author": "Taha Nejad <taha@noiserandom.com>",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/tahayvr/zedx.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/tahayvr/zedx#readme",
|
|
31
|
+
"license": "Apache-2.0",
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/ejs": "^3.1.5",
|
|
40
|
+
"@types/fs-extra": "^11.0.4",
|
|
41
|
+
"@types/node": "^25.2.3",
|
|
42
|
+
"tsx": "^4.21.0",
|
|
43
|
+
"typescript": "^5.9.3"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@clack/prompts": "^0.10.1",
|
|
47
|
+
"commander": "^14.0.3",
|
|
48
|
+
"ejs": "^4.0.1",
|
|
49
|
+
"fs-extra": "^11.3.3",
|
|
50
|
+
"picocolors": "^1.1.1"
|
|
51
|
+
},
|
|
52
|
+
"packageManager": "pnpm@10.30.0+sha512.2b5753de015d480eeb88f5b5b61e0051f05b4301808a82ec8b840c9d2adf7748eb352c83f5c1593ca703ff1017295bc3fdd3119abb9686efc96b9fcb18200937"
|
|
53
53
|
}
|