diff --git a/src/api/json/catalog.json b/src/api/json/catalog.json index 82f6c9782cb..341a7a9f771 100644 --- a/src/api/json/catalog.json +++ b/src/api/json/catalog.json @@ -3679,6 +3679,12 @@ "fileMatch": ["notebook.mod.json"], "url": "https://raw.githubusercontent.com/BookkeepersMC/notebook-schemas/master/notebook.mod.json/schemas/main.json" }, + { + "name": "Nova Editor", + "description": "Nova Editor Extension Manifest", + "fileMatch": ["**/*.novaextension/extension.json", "extension.json"], + "url": "https://json.schemastore.org/extension.json" + }, { "name": "NOX Framework (Service)", "description": "NOX service definition", diff --git a/src/negative_test/extension/incorrect-debug-adaptor.json b/src/negative_test/extension/incorrect-debug-adaptor.json new file mode 100644 index 00000000000..7ef753fe068 --- /dev/null +++ b/src/negative_test/extension/incorrect-debug-adaptor.json @@ -0,0 +1,13 @@ +{ + "bugs": "https://example.com/debug-issues", + "categories": ["issues"], + "debugAdapters": { + "identifier": "debug.helper.adapter", + "name": "Debug Helper Adapter" + }, + "description": "An extension with debug support.", + "identifier": "com.example.debug-helper", + "name": "Debug Helper", + "organization": "Debug Tools Ltd.", + "version": "1.1.0" +} diff --git a/src/negative_test/extension/invalid-activation-events.json b/src/negative_test/extension/invalid-activation-events.json new file mode 100644 index 00000000000..05f276d7089 --- /dev/null +++ b/src/negative_test/extension/invalid-activation-events.json @@ -0,0 +1,10 @@ +{ + "activationEvents": "onStart", + "bugs": "https://example.com/issues", + "categories": ["Utilities"], + "description": "An example with invalid activation events.", + "identifier": "com.example.invalid-activation", + "name": "Invalid Activation Events", + "organization": "Example Org", + "version": "1.0.0" +} diff --git a/src/negative_test/extension/invalid-url.json b/src/negative_test/extension/invalid-url.json new file mode 100644 index 00000000000..202efa1b6df --- /dev/null +++ b/src/negative_test/extension/invalid-url.json @@ -0,0 +1,9 @@ +{ + "bugs": "not-a-valid-url", + "categories": ["Utilities"], + "description": "An example with invalid URLs.", + "identifier": "com.example.invalid-url", + "name": "Invalid URL", + "organization": "Example Org", + "version": "1.0.0" +} diff --git a/src/negative_test/extension/invalid-version.json b/src/negative_test/extension/invalid-version.json new file mode 100644 index 00000000000..202efa1b6df --- /dev/null +++ b/src/negative_test/extension/invalid-version.json @@ -0,0 +1,9 @@ +{ + "bugs": "not-a-valid-url", + "categories": ["Utilities"], + "description": "An example with invalid URLs.", + "identifier": "com.example.invalid-url", + "name": "Invalid URL", + "organization": "Example Org", + "version": "1.0.0" +} diff --git a/src/negative_test/extension/missing-required-fields.json b/src/negative_test/extension/missing-required-fields.json new file mode 100644 index 00000000000..a16cd0cbe02 --- /dev/null +++ b/src/negative_test/extension/missing-required-fields.json @@ -0,0 +1,3 @@ +{ + "name": "Missing Required Fields" +} diff --git a/src/negative_test/extension/unrecognised-category.json b/src/negative_test/extension/unrecognised-category.json new file mode 100644 index 00000000000..c41823aa7a2 --- /dev/null +++ b/src/negative_test/extension/unrecognised-category.json @@ -0,0 +1,9 @@ +{ + "bugs": "https://example.com/issues", + "categories": ["Utilities"], + "description": "A basic valid extension.", + "identifier": "com.example.basic-extension", + "name": "Basic Extension", + "organization": "Example Corp", + "version": "1.0.0" +} diff --git a/src/negative_test/extension/wrong-type.json b/src/negative_test/extension/wrong-type.json new file mode 100644 index 00000000000..99e8741d802 --- /dev/null +++ b/src/negative_test/extension/wrong-type.json @@ -0,0 +1,15 @@ +{ + "bugs": "https://example.com/debug-issues", + "categories": ["issues"], + "debugAdapters": [ + { + "identifier": "debug.helper.adapter", + "name": "Debug Helper Adapter" + } + ], + "description": "An extension with debug support.", + "identifier": "com.example.debug-helper", + "name": "Debug Helper", + "organization": "Debug Tools Ltd.", + "version": "1.1.0" +} diff --git a/src/schemas/json/extension.json b/src/schemas/json/extension.json new file mode 100644 index 00000000000..cbe9faa4910 --- /dev/null +++ b/src/schemas/json/extension.json @@ -0,0 +1,436 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/extension.json", + "title": "Nova Extension Manifest", + "description": "A schema for defining Nova extension manifests.", + "type": "object", + "additionalProperties": false, + "required": [ + "bugs", + "categories", + "description", + "identifier", + "name", + "organization", + "version" + ], + "properties": { + "activationEvents": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Set of Activation Events." + }, + "breakpoints": { + "type": "array", + "description": "Set of Breakpoint templates provided (Nova 9).", + "items": { + "type": "object", + "properties": { + "syntax": { + "type": "string", + "description": "The name of language syntax. Source breakpoints will be enabled within lines of code using this syntax." + } + }, + "additionalProperties": false + } + }, + "bugs": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "description": "A string representing the URL to the extension’s issue tracker." + }, + { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "A string representing the URL to the extension’s issue tracker." + }, + "email": { + "type": "string", + "format": "email", + "description": "An email address that will be the target of a mailto: link." + } + }, + "additionalProperties": false + } + ] + }, + "categories": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "clips", + "commands", + "completions", + "formatters", + "issues", + "key-bindings", + "languages", + "sidebars", + "tasks", + "themes" + ] + }, + "description": "An array of Category identifiers (required)." + }, + "commands": { + "type": "object", + "properties": { + "editor": { + "type": "array", + "description": "Groups commands that appear within the Editor menu for text documents", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "command": { + "type": "string" + }, + "shortcut": { + "type": "string" + }, + "when": { + "type": "string" + }, + "filters": { + "type": "object", + "properties": { + "syntaxes": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "extensions": { + "type": "array", + "description": "Groups commands that appear within the Extensions menu for the workspace", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "command": { + "type": "string" + }, + "shortcut": { + "type": "string" + }, + "when": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "command-palette": { + "type": "array", + "description": "Groups commands that should only appear within the Command Palette", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "command": { + "type": "string" + }, + "shortcut": { + "type": "string" + }, + "when": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "text": { + "type": "array", + "description": "Groups commands that should be available within an editor for direct handling of text input, such as emulation of alternative key binding schemes", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "command": { + "type": "string" + }, + "shortcut": { + "type": "string" + }, + "when": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false, + "description": "User-invokable commands can be added to the menus and command palette, optionally with keyboard key-equivalents. These commands invoke a callback registered in the JavaScript execution environment of the extension." + }, + "config": { + "type": "array", + "description": "Configuration items for the extension." + }, + "configWorkspace": { + "type": "array", + "description": "Configuration items for the extension." + }, + "description": { + "type": "string", + "description": "The user-readable descriptive text (required)." + }, + "debugAdapters": { + "type": "object", + "description": "Set of Debug Adapters provided (Nova 9).", + "additionalProperties": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The user-readable name for the adapter." + }, + "image": { + "type": "string", + "description": "The name of an image contributed by your extension." + } + }, + "additionalProperties": false + } + }, + "entitlements": { + "type": "object", + "description": "Description of an extension’s Entitlements.", + "properties": { + "clipboard": { + "type": "boolean", + "description": "Provides access to the Clipboard API." + }, + "filesystem": { + "type": "string", + "enum": ["readonly", "readwrite"], + "description": "Provides access to the FileSystem and related APIs to read and/or write to files on disk." + }, + "requests": { + "type": "boolean", + "description": "Provides access to the Fetch API to perform asynchronous HTTP network requests." + }, + "process": { + "type": "boolean", + "description": "Provides access to the Process API to launch and communicate with external tools." + } + }, + "additionalProperties": false + }, + "funding": { + "type": "string", + "format": "uri", + "description": "A string representing the URL to the extension’s funding page, if any." + }, + "homepage": { + "type": "string", + "format": "uri", + "description": "A string representing the URL to the extension’s website homepage." + }, + "identifier": { + "type": "string", + "description": "The unique extension identifier (required)." + }, + "issueMatchers": { + "type": "object", + "description": "Set of Issue Matchers provided to the IssueParser API.", + "additionalProperties": { + "type": "object", + "properties": { + "pattern": { + "type": "object" + }, + "linesStartAt1": { + "type": "boolean", + "default": true, + "description": "Whether line numbers captured by the matcher are 1-indexed." + }, + "columnsStartAt1": { + "type": "boolean", + "default": true, + "description": "Whether column numbers captured by the matcher are 1-indexed." + } + }, + "additionalProperties": false + } + }, + "license": { + "type": "string", + "description": "The name of the extension’s license, if any." + }, + "max_runtime": { + "type": "string", + "description": "The maximum version of the runtime supported by the extension. The extension will not be able to be installed in application versions newer than this." + }, + "min_runtime": { + "type": "string", + "description": "The minimum version of the runtime supported for the extension. The extension will not be able to be installed in application versions older than this." + }, + "name": { + "type": "string", + "description": "The user-readable name (required)." + }, + "organization": { + "type": "string", + "description": "The user-readable organization name (required)." + }, + "repository": { + "type": "string", + "format": "uri", + "description": "A string representing the URL to the extension’s code repository, if any." + }, + "version": { + "type": "string", + "pattern": "^\\d+\\.\\d+\\.\\d+$", + "description": "The extension version (required). Must follow semantic versioning." + }, + "main": { + "type": "string", + "description": "Relative path to the extension’s Main Script." + }, + "sidebars": { + "type": "array", + "description": "Set of Sidebars provided to the TreeView API.", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier of the sidebar. Must be unique within the scope of the extension." + }, + "name": { + "type": "string", + "description": "The user-readable name of the sidebar. This value is automatically localized." + }, + "smallImage": { + "type": "string", + "description": "The name of an image to use when the sidebar dock is in Small mode." + }, + "smallSelectedImage": { + "type": "string", + "description": "The name of an image to use when the sidebar dock is in Small mode and the sidebar is selected." + }, + "largeImage": { + "type": "string", + "description": "The name of an image to use when the sidebar dock is in Large mode." + }, + "largeSelectedImage": { + "type": "string", + "description": "The name of an image to use when the sidebar dock is in Large mode and the sidebar is selected." + }, + "sections": { + "type": "array", + "description": "An array of section definitions defining the content of the sidebar.", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier of the section. Must be unique within the sidebar." + }, + "name": { + "type": "string", + "description": "The user-readable name of the section, shown in its header." + }, + "allowMultiple": { + "type": "boolean", + "description": "Whether the tree shown within the section allows multiple selection." + }, + "placeholderImage": { + "type": "string", + "description": "The name of an image to display if the section has no content." + }, + "placeholderText": { + "type": "string", + "description": "A label to display if the section has no content." + }, + "headerCommands": { + "type": "array", + "description": "An array of Command definitions that will be shown as buttons in the header of the section.", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the command button." + }, + "image": { + "type": "string", + "description": "The name of the image for the command button." + }, + "tooltip": { + "type": "string", + "description": "The tooltip text for the command button." + }, + "command": { + "type": "string", + "description": "The command to be executed when the button is clicked." + }, + "when": { + "type": "string", + "description": "A condition that determines when the command button is enabled." + } + }, + "additionalProperties": false + } + }, + "contextCommands": { + "type": "array", + "description": "An array of Command definitions that will be shown in the contextual menu of the sidebar.", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the contextual command." + }, + "command": { + "type": "string", + "description": "The command to be executed when the contextual command is selected." + }, + "when": { + "type": "string", + "description": "A condition that determines when the contextual command is enabled." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "taskTemplates": { + "type": "object", + "description": "Set of Task Templates." + } + } +} diff --git a/src/test/extension/basic.json b/src/test/extension/basic.json new file mode 100644 index 00000000000..97354e74230 --- /dev/null +++ b/src/test/extension/basic.json @@ -0,0 +1,9 @@ +{ + "bugs": "https://example.com/issues", + "categories": ["commands"], + "description": "A basic valid extension.", + "identifier": "com.example.basic-extension", + "name": "Basic Extension", + "organization": "Example Corp", + "version": "1.0.0" +} diff --git a/src/test/extension/debug.json b/src/test/extension/debug.json new file mode 100644 index 00000000000..0e3d96f3eab --- /dev/null +++ b/src/test/extension/debug.json @@ -0,0 +1,15 @@ +{ + "bugs": "https://example.com/debug-issues", + "categories": ["issues"], + "debugAdapters": { + "debugpy": { + "image": "debugpy", + "name": "Python (debugpy)" + } + }, + "description": "An extension with debug support.", + "identifier": "com.example.debug-helper", + "name": "Debug Helper", + "organization": "Debug Tools Ltd.", + "version": "1.1.0" +} diff --git a/src/test/extension/full.json b/src/test/extension/full.json new file mode 100644 index 00000000000..dc5580af299 --- /dev/null +++ b/src/test/extension/full.json @@ -0,0 +1,233 @@ +{ + "activationEvents": ["onStart", "onOpen"], + "bugs": "https://example.com/bugs", + "categories": ["commands", "issues"], + "commands": { + "editor": [ + { + "command": "wrapSelectionInTag", + "filters": { + "syntaxes": ["html"] + }, + "shortcut": "cmd-<", + "title": "Wrap Selection In Tag", + "when": "editorHasFocus" + } + ] + }, + "config": [ + { + "default": false, + "description": "Whether to always ignore selected text.", + "key": "myextension.ignore-selection", + "title": "Ignore Selection", + "type": "boolean" + }, + { + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce diam urna, sagittis et imperdiet a, molestie hendrerit lectus. Duis accumsan viverra lectus et vulputate. Nullam at mi ac ipsum luctus dapibus quis eget ante.", + "key": "myextension.description", + "title": "Description", + "type": "text" + }, + { + "command": "myextension.autoconfigure", + "title": "Autoconfigure", + "type": "command" + }, + { + "children": [ + { + "key": "myextension.temp-filename-format", + "placeholder": "foobar.ext.tmp", + "title": "Temporary Filename Format", + "type": "string" + }, + { + "description": "Donec interdum ligula non ipsum consectetur blandit. Name eget pharetra est.", + "key": "myextension.count", + "max": 100, + "min": 0, + "title": "Count", + "type": "number" + }, + { + "default": "text", + "key": "myextension.file-type", + "title": "File Type", + "type": "enum", + "values": ["text", "photo", "video"] + } + ], + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu fermentum nibh.", + "link": "https://library.panic.com/options", + "title": "File Options", + "type": "section" + } + ], + "configWorkspace": [ + { + "default": false, + "description": "Whether to always ignore selected text.", + "key": "myextension.ignore-selection", + "title": "Ignore Selection", + "type": "boolean" + }, + { + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce diam urna, sagittis et imperdiet a, molestie hendrerit lectus. Duis accumsan viverra lectus et vulputate. Nullam at mi ac ipsum luctus dapibus quis eget ante.", + "key": "myextension.description", + "title": "Description", + "type": "text" + }, + { + "command": "myextension.autoconfigure", + "title": "Autoconfigure", + "type": "command" + }, + { + "children": [ + { + "key": "myextension.temp-filename-format", + "placeholder": "foobar.ext.tmp", + "title": "Temporary Filename Format", + "type": "string" + }, + { + "description": "Donec interdum ligula non ipsum consectetur blandit. Name eget pharetra est.", + "key": "myextension.count", + "max": 100, + "min": 0, + "title": "Count", + "type": "number" + }, + { + "default": "text", + "key": "myextension.file-type", + "title": "File Type", + "type": "enum", + "values": ["text", "photo", "video"] + } + ], + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu fermentum nibh.", + "link": "https://library.panic.com/options", + "title": "File Options", + "type": "section" + } + ], + "debugAdapters": { + "debugpy": { + "image": "debugpy", + "name": "Python (debugpy)" + } + }, + "description": "An extension with all supported fields.", + "funding": "https://patreon.com/example", + "homepage": "https://example.com", + "identifier": "com.example.full-feature", + "issueMatchers": { + "my-issue-matcher": { + "pattern": { + "code": 2, + "file": 3, + "line": 4, + "message": 1, + "regexp": "^(?:PHP\\s+)?Parse error:\\s+(.+?)\\s+\\(([a-zA-Z0-9-_]+)\\) in ([^:]+?) on line (\\d+)$", + "severity": "error" + } + } + }, + "license": "MIT", + "max_runtime": "3.0", + "min_runtime": "1.0", + "name": "Full Feature Extension", + "organization": "Full Dev Inc.", + "repository": "https://github.com/example/repo", + "sidebars": [ + { + "id": "mysidebar", + "largeImage": "sidebar-large", + "name": "My Sidebar", + "sections": [ + { + "allowMultiple": false, + "contextCommands": [ + { + "command": "mysidebar.remove", + "title": "Remove", + "when": "viewItem != null" + } + ], + "headerCommands": [ + { + "command": "mysidebar.add", + "image": "__builtin.add", + "title": "Add", + "tooltip": "Add a new color" + }, + { + "command": "mysidebar.remove", + "image": "__builtin.remove", + "title": "Remove", + "tooltip": "Remove the selected color", + "when": "viewItem != null" + } + ], + "id": "mysidebar-colors", + "name": "Colors" + } + ], + "smallImage": "sidebar-small" + } + ], + "taskTemplates": { + "webserver": { + "config": [ + { + "default": "localhost", + "key": "php.host", + "placeholder": "localhost", + "title": "Host", + "type": "string" + }, + { + "default": 8000, + "key": "php.port", + "max": 65535, + "min": 1, + "placeholder": "8000", + "title": "Port", + "type": "number" + }, + { + "default": ".", + "key": "php.document-root", + "title": "Document Root", + "type": "path" + }, + { + "description": "Custom arguments to pass to the PHP webserver command.", + "key": "php.custom-args", + "title": "Additional Arguments", + "type": "string" + } + ], + "description": "Runs the PHP development server.", + "name": "PHP Webserver", + "persistent": true, + "tasks": { + "run": { + "args": [ + "-S", + "$(Config:php.host):$(Config:php.port)", + "-t", + "$(Config:php.document-root)", + "$(Config:php.custom-args)" + ], + "command": "php", + "env": {}, + "shell": true + } + } + } + }, + "version": "2.0.0" +} diff --git a/src/test/extension/minimal.json b/src/test/extension/minimal.json new file mode 100644 index 00000000000..f4432d788c7 --- /dev/null +++ b/src/test/extension/minimal.json @@ -0,0 +1,9 @@ +{ + "bugs": "https://example.com/issues", + "categories": ["issues"], + "description": "A minimal valid extension.", + "identifier": "com.example.minimal", + "name": "Minimal Extension", + "organization": "Minimal Inc.", + "version": "0.1.0" +}