schemaCompletionSource Example

Hi,
I have a CodeMirror setup to edit SQL and would like schema auto completion, however I’d like to load the schema in parts after the editor is ready.

Reading the docs it looks like I need to implement schemaCompletionSource, but I can’t figure out where this is added to the setup; can I have a pointer please.

I have:

        this.editor = new EditorView({
            doc: this.value || '',
            extensions: [
                basicSetup,
                sql({
                    dialect: MySQL,
                    upperCaseKeywords: true
                }),
            ],
            parent: this.hostElement.nativeElement,
        });

I’ve tried adding this:

let mysqlLanguage = MySQL;
mysqlLanguage.language.data.of({ schemaCompletionSource: this.schemaSource })
//schemaSource returns { schema: { 'abc.table': [ 'id', 'name' ] } }

and setting the dialect to mysqlLanguage; but (I guess obviously) it does nothing

The easiest way to do this is to add schema: mySchema to the options to sql(). If you want to add this separately, you need to add an extension like

mysqlLanguage.language.data.of({
  autocomplete: schemaCompletionSource({schema: mySchema}))
})

I agree the easiest way is adding to the options of sql(), but I think I can only do this at initialisation, is that correct?

I’ve tried adding after init using your code, but it doesn’t seem to work - I’ve added to the try page; what’s wrong with this? Thanks!

https://codemirror.net/try/?c=aW1wb3J0IHtiYXNpY1NldHVwLCBFZGl0b3JWaWV3fSBmcm9tICJjb2RlbWlycm9yIgppbXBvcnQge3NxbCwgTXlTUUwsIHNjaGVtYUNvbXBsZXRpb25Tb3VyY2V9IGZyb20gIkBjb2RlbWlycm9yL2xhbmctc3FsIgoKbGV0IG15c3FsTGFuZ3VhZ2UgPSBNeVNRTDsKCm5ldyBFZGl0b3JWaWV3KHsKICBkb2M6ICJTRUxFQ1QgKiBGUk9NICIsCiAgZXh0ZW5zaW9uczogWwogICAgYmFzaWNTZXR1cCwgCiAgICBzcWwoewogICAgICBkaWFsZWN0OiBteXNxbExhbmd1YWdlCiAgICB9KQogIF0sCiAgcGFyZW50OiBkb2N1bWVudC5ib2R5Cn0pCgpsZXQgbXlTY2hlbWEgPSB7ICdhYmMudGFibGUnOiBbICdpZCcsICduYW1lJyBdIH07Cm15c3FsTGFuZ3VhZ2UubGFuZ3VhZ2UuZGF0YS5vZih7CiAgYXV0b2NvbXBsZXRlOiBzY2hlbWFDb21wbGV0aW9uU291cmNlKHtzY2hlbWE6IG15U2NoZW1hfSkKfSk=

You’re just creating the extension at the end of the program and then doing nothing with it. To make it affect the editor, you have to add it, via something like .dispatch({effects: StateEffect.appendConfig(myExtension)}).

Still not there, sorry if I’m being slow!!

I’ve added this to the bottom of the script, but it doesn’t seem to have done anything:

editor.dispatch({
  effects: StateEffect.appendConfig.of([
    sql({
      dialect: mysqlLanguage
    })
  ])
});

https://codemirror.net/try/?c=aW1wb3J0IHtiYXNpY1NldHVwLCBFZGl0b3JWaWV3fSBmcm9tICJjb2RlbWlycm9yIgppbXBvcnQge1N0YXRlRWZmZWN0fSBmcm9tICdAY29kZW1pcnJvci9zdGF0ZSc7CmltcG9ydCB7c3FsLCBNeVNRTCwgc2NoZW1hQ29tcGxldGlvblNvdXJjZX0gZnJvbSAiQGNvZGVtaXJyb3IvbGFuZy1zcWwiCgpsZXQgbXlzcWxMYW5ndWFnZSA9IE15U1FMOwoKbGV0IGVkaXRvciA9IG5ldyBFZGl0b3JWaWV3KHsKICBkb2M6ICJTRUxFQ1QgKiBGUk9NICIsCiAgZXh0ZW5zaW9uczogWwogICAgYmFzaWNTZXR1cCwgCiAgICBzcWwoewogICAgICBkaWFsZWN0OiBteXNxbExhbmd1YWdlCiAgICB9KQogIF0sCiAgcGFyZW50OiBkb2N1bWVudC5ib2R5Cn0pCgpsZXQgbXlTY2hlbWEgPSB7ICdhYmMudGFibGUnOiBbICdpZCcsICduYW1lJyBdIH07Cm15c3FsTGFuZ3VhZ2UubGFuZ3VhZ2UuZGF0YS5vZih7CiAgYXV0b2NvbXBsZXRlOiBzY2hlbWFDb21wbGV0aW9uU291cmNlKHtzY2hlbWE6IG15U2NoZW1hfSkKfSkKCmVkaXRvci5kaXNwYXRjaCh7CiAgZWZmZWN0czogU3RhdGVFZmZlY3QuYXBwZW5kQ29uZmlnLm9mKFsKICAgIHNxbCh7CiAgICAgIGRpYWxlY3Q6IG15c3FsTGFuZ3VhZ2UKICAgIH0pCiAgXSkKfSk7Cg==

Look mysqlLanguage.language.data.of does nothing except create an extension. If you don’t do anything with the resulting value, it has zero effect anywhere. So put that extension into appendConfig.

Got it! Thank you!

Update the schema using:

let mySchema = { 'abc.person': [ 'id', 'name' ] };
editor.dispatch({
  effects: StateEffect.appendConfig.of(
    MySQL.language.data.of({
      autocomplete: schemaCompletionSource({schema: mySchema})
    })
  )
});

Thanks for this example. If I wanted to update the schema – replacing the previous schema entirely – how would I do that? I tried putting it in a timeout here but the original schema remains. I think I could use StateEffect.reconfigure but I would have to load my original state tree. Is there a better way of doing that where I’m just targeting this one extension?

See this about compartments.

Thanks that’s really helpful. Here’s what I put together in case it helps someone else. There is likely an improvement that can be made where I’m not having to re-instantiate the entire sql extention and I just adjust the schema. Any suggestions welcome!

import {basicSetup, EditorView} from "codemirror"
import {StateEffect, Compartment} from '@codemirror/state';
import {sql, MySQL, schemaCompletionSource} from "@codemirror/lang-sql"

let mysqlLanguage = MySQL;

let comp = new Compartment

let mySchema = { 'my_person': [ 'my_id', 'my_name' ] };
let editor = new EditorView({
  doc: "SELECT * FROM my_",
  extensions: [
    basicSetup, 
    comp.of(sql({
      dialect: mysqlLanguage,
      schema: mySchema
    }))
  ],
  parent: document.body
})


setTimeout(() => {
  alert('resetting')
  let otherSchema = { 'other_person': [ 'other_id', 'other_name' ] };
  editor.dispatch({
    effects: comp.reconfigure(sql({
      dialect: mysqlLanguage,
      schema: otherSchema
    }))
  })
}, 5000)