Skip to content

Creating a custom schema

Mikayla Hutchinson edited this page Apr 18, 2024 · 5 revisions

Overview

A custom .buildschema.json schema allows you to provide documentation and type annotations for your MSBuild project, targets and/or props files, allowing the MSBuild Editor to provide a much better experience for their properties, items, targets, and metadata.

Location

The MSBuild Editor will attempt to locate a sidecar schema for the project file and every targets and props file that it reads via direct and transitive import.

The sidecar schema must have the same name as the MSBuild file with the additional extension .buildschema.json. For example, the sidecar schema for Hello.csproj would be Hello.csproj.buildschema.json, and the sidecar schema for MyBuildLogic.targets would be MyBuildLogic.targets.buildschema.json.

Editing

A JSON schema is available to provide IntelliSense and validation when editing .buildschema.json files. It is located at https://github.com/mhutch/MonoDevelop.MSBuildEditor/blob/main/MonoDevelop.MSBuild/Schemas/buildschema.json.

To use this schema in Visual Studio, paste the above URL into the Schema field at the top of the JSON source editor. You will need to do this separately for every .buildschema.json file. This association will be persisted in the .sln file.

For Visual Studio Code, an association can be set up in .vscode/settings.json that affects all .buildschema.json files on your machine or in a particular repository:

{
  "files.associations": {
    "*.buildschema.json": "jsonc"
  },
  "json.schemas": [
    {
      "fileMatch": [
        "/*.buildschema.json"
      ],
      "url": "https://github.com/mhutch/MonoDevelop.MSBuildEditor/blob/main/MonoDevelop.MSBuild/Schemas/buildschema.json"
    }
  ],
}

Structure

Examples of build schemas can be found in main/MonoDevelop.MSBuild/Schemas.

Toplevel

A .buildschema.json file contains a JSON object with the following toplevel keys. All of these toplevel keys are optional. The file is in jsonc format so it supports comments and trailing commas.

{
  "license": "Copyright information", // this is purely informative for viewers of the file, so you could use a comment instead
  "intellisenseImports": [
    // additional schemas to be imported only when directly editing the file for which this is a sidecar
  ],
  "properties": {
    // definitions of properties
  },
  "items": {
    // definitions of items and their metadata
  },
  "targets": {
    // definitions of properties
  },
  "metadata": {
    // definitions of metadata that applies to multiple items
  }
  "types": {
    // definitions of custom types
  },
}

Common Keys

Description

The description key has a string value that contains documentation for display in tooltips. Description strings use a subset of Markdown, supporting inline `code`, *emphasis*, and **strong** formatting.

Where a definition contains only a description, and no other keys, it may be simplified to a short form that contains only the description string instead of a JSON object. For example, the following two property definitions are equivalent:

"MyFirstProperty": "My first description",
"MySecondProperty": {
  "description": "My second property description",
}

Help Url

The helpUrl key in a symbol definition provides a link to web documentation for that symbol. This is displayed in tooltips and will be opened when using F1 help.

Deprecation Message

The deprecationMessage key in a symbol definition marks that symbol as deprecated. When this key is used, its value cannot be null or empty. The deprecation message will be shown as a warning in the editor when using a deprecated symbol. The deprecation message uses the same subset of Markdown as it used by description.

Type Annotations

The type key in a symbol definition provides a type annotation for that symbol. Type annotations are displayed in tooltips, and are used for validation and IntelliSense.

For example, a property could be marked as a folder-with-slash, indicating that it contains a directory path with a trailing slash.

"MyProperty": {
  "type": "folder-with-slash"
}

A type annotation may use an intrinsic type, reference a shared custom type, or define a custom type inline.

Symbols defined in a schema that do not have a type annotation will be treated as having an unknown type. Note that inferred schemas will infer type information from symbol names, for example property names that end with Path will be treated as having type file-or-folder. However, this inference is not performed for symbols defined in build schema files.

Intrinsic Types

A type annotation of the form "type": "folder-with-slash" is an example of using an intrinsic type. An intrinsic type is a type that that is defined within the MSBuild Editor itself. Other examples of intrinsic types include int, string, url, target-name, and so on. The full list of intrinsic types will not be given here, but all intrinsic types are documented in the JSON schema.

Some intrinsic types have special handling beyond validation. For example:

  • Symbols with the culture type have IntelliSense for .NET culture names
  • Symbols with the nuget-id type have package name IntelliSense based on dynamically querying NuGet.org, and a special tooltip displaying the package's description.
  • Symbols with the guid type have a special item in the completion list that allows generating a new GUID.

Shared Type References

A shared type is defined in the top-level types collection. The key for the definition is an ID that is not displayed to the user, and is only used to reference that shared type within the schema.

A type reference takes the form of an object with a single key, "$ref", with a value that is a JSON Path to the shared type definition.

For example, this reference:

"type": { "$ref": "#/types/myCustomTypeId" }

would resolve to this shared type definition:

{
  "types": {
    "myCustomTypeId": [ "A", "B", "C" ]
  }
}

For the form of the shared type definitions within the toplevel type collection, see Type Definitions.

Inline Type Definitions

Inline type definitions provide an easy way to define a custom type for use by a single symbol. Nontrivial type definitions and types that are expected to be used by multiple symbols should instead used a shared custom type.

Inline type definitions use the exact same form as shared type definitions, described in Type Definitions.

For example:

"MyFirstProperty": {
  "type": [ "A", "B" ]
},
"MySecondProperty": {
  "type": {
    "values": {
      "A": "A description",
      "B": "B description"
    }
  }
}

Shared

Properties

The properties collection contains definitions of properties. These definitions may use the short form:

"MyPropertyName": "property documentation"

Clone this wiki locally