Skip to content

Commit 5f16cc2

Browse files
committed
Add keywordCompletion option
FEATURE: The new `keywordCompletion` option can be used to define what kind of completions are generated for keywords. Issue #17
1 parent ff800e4 commit 5f16cc2

File tree

3 files changed

+38
-18
lines changed

3 files changed

+38
-18
lines changed

src/complete.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,14 @@ export function completeFromSchema(schema: SQLNamespace,
204204
}
205205
}
206206

207-
export function completeKeywords(keywords: {[name: string]: number}, upperCase: boolean) {
208-
let completions = Object.keys(keywords).map(keyword => ({
209-
label: upperCase ? keyword.toUpperCase() : keyword,
210-
type: keywords[keyword] == Type ? "type" : keywords[keyword] == Keyword ? "keyword" : "variable",
211-
boost: -1
212-
}))
207+
function completionType(tokenType: number) {
208+
return tokenType == Type ? "type" : tokenType == Keyword ? "keyword" : "variable"
209+
}
210+
211+
export function completeKeywords(keywords: {[name: string]: number}, upperCase: boolean,
212+
build: (name: string, type: string) => Completion) {
213+
let completions = Object.keys(keywords)
214+
.map(keyword => build(upperCase ? keyword.toUpperCase() : keyword, completionType(keywords[keyword])))
213215
return ifNotIn(["QuotedIdentifier", "SpecialVar", "String", "LineComment", "BlockComment", "."],
214216
completeFromList(completions))
215217
}

src/sql.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,18 +149,17 @@ export interface SQLConfig {
149149
defaultSchema?: string,
150150
/// When set to true, keyword completions will be upper-case.
151151
upperCaseKeywords?: boolean
152+
/// Can be used to customize the completions generated for keywords.
153+
keywordCompletion?: (label: string, type: string) => Completion
152154
}
153155

156+
function defaultKeyword(label: string, type: string) { return {label, type, boost: -1} }
157+
154158
/// Returns a completion source that provides keyword completion for
155159
/// the given SQL dialect.
156-
export function keywordCompletionSource(dialect: SQLDialect, upperCase = false): CompletionSource {
157-
return completeKeywords(dialect.dialect.words, upperCase)
158-
}
159-
160-
function keywordCompletion(dialect: SQLDialect, upperCase = false): Extension {
161-
return dialect.language.data.of({
162-
autocomplete: keywordCompletionSource(dialect, upperCase)
163-
})
160+
export function keywordCompletionSource(dialect: SQLDialect, upperCase = false,
161+
build?: (label: string, type: string) => Completion): CompletionSource {
162+
return completeKeywords(dialect.dialect.words, upperCase, build || defaultKeyword)
164163
}
165164

166165
/// Returns a completion sources that provides schema-based completion
@@ -183,7 +182,12 @@ function schemaCompletion(config: SQLConfig): Extension {
183182
/// extensions.
184183
export function sql(config: SQLConfig = {}) {
185184
let lang = config.dialect || StandardSQL
186-
return new LanguageSupport(lang.language, [schemaCompletion(config), keywordCompletion(lang, !!config.upperCaseKeywords)])
185+
return new LanguageSupport(lang.language, [
186+
schemaCompletion(config),
187+
lang.language.data.of({
188+
autocomplete: keywordCompletionSource(lang, config.upperCaseKeywords, config.keywordCompletion)
189+
})
190+
])
187191
}
188192

189193
/// The standard SQL dialect.

test/test-complete.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import {EditorState} from "@codemirror/state"
22
import {CompletionContext, CompletionResult, CompletionSource} from "@codemirror/autocomplete"
3-
import {schemaCompletionSource, PostgreSQL, MySQL, SQLConfig, SQLDialect} from "@codemirror/lang-sql"
3+
import {schemaCompletionSource, keywordCompletionSource, PostgreSQL, MySQL, SQLConfig, SQLDialect} from "@codemirror/lang-sql"
44
import ist from "ist"
55

6-
function get(doc: string, conf: SQLConfig & {explicit?: boolean} = {}) {
6+
function get(doc: string, conf: SQLConfig & {explicit?: boolean, keywords?: boolean} = {}) {
77
let cur = doc.indexOf("|"), dialect = conf.dialect || PostgreSQL
88
doc = doc.slice(0, cur) + doc.slice(cur + 1)
99
let state = EditorState.create({
1010
doc,
1111
selection: {anchor: cur},
1212
extensions: [dialect, dialect.language.data.of({
13-
autocomplete: schemaCompletionSource(Object.assign({dialect}, conf))
13+
autocomplete: conf.keywords
14+
? keywordCompletionSource(dialect, conf.upperCaseKeywords, conf.keywordCompletion)
15+
: schemaCompletionSource(Object.assign({dialect}, conf))
1416
})]
1517
})
1618
let result = state.languageDataAt<CompletionSource>("autocomplete", cur)[0](new CompletionContext(state, cur, !!conf.explicit))
@@ -213,4 +215,16 @@ describe("SQL completion", () => {
213215
ist(get("select foo.|", s)!.options[0].type, "constant")
214216
ist(get("select foo.|", s)!.options.length, 1)
215217
})
218+
219+
it("can complete keywords", () => {
220+
ist(get("s|", {keywords: true})!.options.some(c => c.label == "select"))
221+
})
222+
223+
it("can complete upper-case keywords", () => {
224+
ist(get("s|", {keywords: true, upperCaseKeywords: true})!.options.some(c => c.label == "SELECT"))
225+
})
226+
227+
it("can transform keyword completions", () => {
228+
ist(get("s|", {keywords: true, keywordCompletion: l => ({label: l, type: "x"})})!.options.every(c => c.type == "x"))
229+
})
216230
})

0 commit comments

Comments
 (0)