diff --git a/README.md b/README.md index 793339a..d2cebce 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,11 @@ Defaults to "?".

The characters that can be used to quote identifiers. Defaults to "\"".

+
+ identifiercaseinsensitive⁠?: boolean
+ +

Controls whether identifiers are case-insensitive. Identifiers with upper-case letters are quoted then set to false. Defaults +to false.

unquotedBitLiterals⁠?: boolean
diff --git a/src/complete.ts b/src/complete.ts index 34341f1..7ff703d 100644 --- a/src/complete.ts +++ b/src/complete.ts @@ -101,14 +101,14 @@ class CompletionLevel { list: Completion[] = [] children: {[name: string]: CompletionLevel} | undefined = undefined - constructor(readonly idQuote: string) {} + constructor(readonly idQuote: string, readonly idCaseInsensitive?: boolean) {} child(name: string) { let children = this.children || (this.children = Object.create(null)) let found = children[name] if (found) return found - if (name && !this.list.some(c => c.label == name)) this.list.push(nameCompletion(name, "type", this.idQuote)) - return (children[name] = new CompletionLevel(this.idQuote)) + if (name && !this.list.some(c => c.label == name)) this.list.push(nameCompletion(name, "type", this.idQuote, this.idCaseInsensitive)) + return (children[name] = new CompletionLevel(this.idQuote, this.idCaseInsensitive)) } maybeChild(name: string) { @@ -123,7 +123,7 @@ class CompletionLevel { addCompletions(completions: readonly (Completion | string)[]) { for (let option of completions) - this.addCompletion(typeof option == "string" ? nameCompletion(option, "property", this.idQuote) : option) + this.addCompletion(typeof option == "string" ? nameCompletion(option, "property", this.idQuote, this.idCaseInsensitive) : option) } addNamespace(namespace: SQLNamespace) { @@ -154,8 +154,9 @@ class CompletionLevel { } } -function nameCompletion(label: string, type: string, idQuote: string): Completion { - if (/^[a-z_][a-z_\d]*$/.test(label)) return {label, type} +function nameCompletion(label: string, type: string, idQuote: string, idCaseInsensitive: boolean): Completion { + const regex = new RegExp("^[a-z_][a-z_\\d]*$", idCaseInsensitive ? 'i' : undefined); + if (regex.test(label)) return {label, type} return {label, type, apply: idQuote + label + idQuote} } @@ -168,7 +169,7 @@ export function completeFromSchema(schema: SQLNamespace, defaultTableName?: string, defaultSchemaName?: string, dialect?: SQLDialect): CompletionSource { let idQuote = dialect?.spec.identifierQuotes?.[0] || '"' - let top = new CompletionLevel(idQuote) + let top = new CompletionLevel(idQuote, dialect?.spec.identifierCaseInsensitive) let defaultSchema = defaultSchemaName ? top.child(defaultSchemaName) : null top.addNamespace(schema) if (tables) (defaultSchema || top).addCompletions(tables) diff --git a/src/sql.ts b/src/sql.ts index 0e2351c..7ac932c 100644 --- a/src/sql.ts +++ b/src/sql.ts @@ -76,6 +76,10 @@ export type SQLDialectSpec = { /// The characters that can be used to quote identifiers. Defaults /// to `"\""`. identifierQuotes?: string + /// Controls whether identifiers are case-insensitive. Identifiers + /// with upper-case letters are quoted then set to false. Defaults to + /// false. + identifierCaseInsensitive?: boolean, /// Controls whether bit values can be defined as 0b1010. Defaults /// to false. unquotedBitLiterals?: boolean, diff --git a/src/tokens.ts b/src/tokens.ts index 585df14..3c72707 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -168,6 +168,7 @@ export interface Dialect { operatorChars: string, specialVar: string, identifierQuotes: string, + identifierCaseInsensitive: boolean, words: {[name: string]: number} } @@ -188,6 +189,7 @@ const defaults: Dialect = { operatorChars: "*+\-%<>!=&|~^/", specialVar: "?", identifierQuotes: '"', + identifierCaseInsensitive: false, words: keywords(SQLKeywords, SQLTypes) } diff --git a/test/test-complete.ts b/test/test-complete.ts index 3562093..f7386fe 100644 --- a/test/test-complete.ts +++ b/test/test-complete.ts @@ -1,6 +1,6 @@ import {EditorState} from "@codemirror/state" import {CompletionContext, CompletionResult, CompletionSource} from "@codemirror/autocomplete" -import {schemaCompletionSource, PostgreSQL, MySQL, SQLConfig} from "@codemirror/lang-sql" +import {schemaCompletionSource, PostgreSQL, MySQL, SQLConfig, SQLDialect} from "@codemirror/lang-sql" import ist from "ist" function get(doc: string, conf: SQLConfig & {explicit?: boolean} = {}) { @@ -162,6 +162,15 @@ describe("SQL completion", () => { '`b c`, `b-c`, bup') }) + it("adds identifiers for upper case completions", () => { + ist(str(get("foo.c|", {schema: {foo: ["Column", "cell"]}, dialect: PostgreSQL})), + '"Column", cell') + + const customDialect = SQLDialect.define({...PostgreSQL.spec, identifierCaseInsensitive: true}) + ist(str(get("foo.c|", {schema: {foo: ["Column", "cell"]}, dialect: customDialect})), + 'Column, cell') + }) + it("supports nesting more than two deep", () => { let s = {schema: {"one.two.three": ["four"]}} ist(str(get("o|", s)), "one")