Skip to content

Improper completion of YAML string values #164

@shlomoswidler

Description

@shlomoswidler

Summary

When using codemirror-json-schema@0.8.1 with YAML mode, property completions with const, enum, or default string values insert unquoted values that YAML parses as non-strings (numbers, booleans, etc.).

Reproduction

Schema

{
  "type": "object",
  "properties": {
    "version": {
      "type": "string",
      "const": "3.0",
      "description": "Version string"
    }
  }
}

Steps

  1. Start typing v in an empty YAML document
  2. Autocomplete suggests version as a property
  3. Accept the completion (Tab or Enter)

Expected Result

version: "3.0"

Actual Result

version: 3.0

Problem

YAML parses 3.0 as the number 3 (float), not the string "3.0". This causes schema validation to fail with:

Expected value at version to be 3.0, but value given is 3

The library's apply() function for property completions inserts key: defaultValue without considering YAML's type inference rules. Values that look like:

  • Numbers (3.0, 123, .5)
  • Booleans (true, false, yes, no, on, off)
  • Null (null, ~)

...all need to be quoted when the schema expects a string type.

Workaround

Wrap the completion's apply function to detect unquoted string values and re-insert them with quotes:

yamlLanguage.data.of({
    autocomplete: async (context) => {
        const result = await yamlCompletion()(context);
        if (result?.options) {
            result.options = result.options.map(opt => {
                if (opt.type === 'property' && typeof opt.apply === 'function') {
                    const originalApply = opt.apply;
                    opt.apply = (view, completion, from, to) => {
                        originalApply(view, completion, from, to);

                        // Post-process: quote string values that look like non-strings
                        const propSchema = schema?.properties?.[opt.label];
                        if (propSchema?.type === 'string' && (propSchema.const || propSchema.enum)) {
                            const line = view.state.doc.lineAt(view.state.selection.main.head);
                            const match = line.text.match(/^(\s*\w+:\s*)([^"'\s].*)$/);
                            if (match && /^[\d.]/.test(match[2])) {
                                const quotedLine = match[1] + '"' + match[2] + '"';
                                view.dispatch({
                                    changes: { from: line.from, to: line.to, insert: quotedLine },
                                    selection: { anchor: line.from + quotedLine.length }
                                });
                            }
                        }
                    };
                }
                return opt;
            });
        }
        return result;
    }
})

Suggested Fix

The library should be YAML-aware when inserting default values. For string-typed properties with const/enum/default values, it should:

  1. Check if the value would be parsed as a non-string by YAML
  2. If so, wrap the value in quotes (either "value" or 'value')

This could be implemented in the apply function generator for property completions in src/yaml-completion.ts (or equivalent).

Environment

  • codemirror-json-schema: 0.8.1
  • @codemirror/lang-yaml: 6.1.1
  • CodeMirror 6

Related

This issue affects any schema property where:

  • type: "string"
  • Has const, enum, or default value
  • The value looks like a YAML scalar (number, boolean, null)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions