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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions