Skip to content

Commit

Permalink
v0feat: simplified auto-quoting/escape in autocomplete
Browse files Browse the repository at this point in the history
- Note that values that have had their quotes escaped but have no spaces are no longer quoted.
  • Loading branch information
AlansCodeLog committed Jun 9, 2024
1 parent baaab41 commit 252726e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 28 deletions.
28 changes: 4 additions & 24 deletions src/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as handle from "./ast/handlers.js"
import { applyBoolean } from "./internal/applyBoolean.js"
import { applyPrefix } from "./internal/applyPrefix.js"
import { checkParserOpts } from "./internal/checkParserOpts.js"
import { escapeVariableOrPrefix } from "./internal/escapeVariableOrPrefix.js"
import { extractPosition } from "./internal/extractPosition.js"
import { getUnclosedRightParenCount } from "./internal/getUnclosedRightParenCount.js"
import { parseParserOptions } from "./internal/parseParserOptions.js"
Expand All @@ -34,6 +35,7 @@ const OPPOSITE = {
[TOKEN_TYPE.AND]: TOKEN_TYPE.OR,
[TOKEN_TYPE.OR]: TOKEN_TYPE.AND,
}

function isEqualSet(setA: Set<any>, setB: Set<any>): boolean {
if (setA.size !== setB.size) return false
for (const key of setA) {
Expand Down Expand Up @@ -1048,31 +1050,9 @@ export class Parser<T extends {} = {}> {
: type === SUGGESTION_TYPE.VALUE
? values
: unreachable()
return arr.map(variable => {
// we don't need to alter options since we can just check there are no quotes (also tells us no prefixes are used) and no operators are defined
const res = self.parse(variable)
if (isNode(res) && res.type === AST_TYPE.CONDITION &&
res.operator === undefined &&
isNode(res.value) && res.value.type === AST_TYPE.VARIABLE &&
res.value.quote === undefined) {
return { suggestion, value: res.value.value.value! }
} else {
return { suggestion, value: quote + variable.replace(new RegExp(quote, "g"), `\\${quote}`) + quote }
}
})
return arr.map(variable => ({ suggestion, value: escapeVariableOrPrefix(variable, quote) }))
}
case SUGGESTION_TYPE.PREFIX: return prefixes.map(prefix => {
const res = self.parse(prefix)
if (isNode(res) &&
res.type === AST_TYPE.CONDITION &&
res.operator === undefined &&
isNode(res.value) && res.value.type === AST_TYPE.VARIABLE &&
res.value.quote === undefined) {
return { suggestion, value: res.value.value.value! }
} else {
return { suggestion, value: quote + prefix.replace(new RegExp(quote, "g"), `\\${quote}`) + quote }
}
})
case SUGGESTION_TYPE.PREFIX: return prefixes.map(prefix => ({ suggestion, value: escapeVariableOrPrefix(prefix, quote) }))
}
}).flat()
}
Expand Down
36 changes: 36 additions & 0 deletions src/internal/escapeVariableOrPrefix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { multisplice } from "@alanscodelog/utils/multisplice.js"

export function escapeVariableOrPrefix(variable: string, preferredQuote: string): string {
let doQuote = false
for (const quoteType of ["\"", "'", "`"]) {
if (!variable.includes(quoteType) && !variable.includes(" ")) continue
if (variable.startsWith(quoteType)
&& variable.endsWith(quoteType)) {
break
}
const indexes: number[] = []
for (let i = 0; i < variable.length; i++) {
const char = variable[i]
if (char === undefined) break
if (char === "\\") {
i += 2
continue
}
if (char === " ") {
doQuote = true
}
if (char === quoteType) indexes.push(i)
}

if (indexes.length === 0) break

const newVal = multisplice(variable.split(""), indexes, 0, "\\").array.join("")
variable = newVal

break
}
if (doQuote) {
variable = `${preferredQuote}${variable}${preferredQuote}`
}
return variable
}
8 changes: 4 additions & 4 deletions tests/cursorInfo-autosuggest-autocomplete-autoreplace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ const simplify = (suggestions: Suggestion[]): Omit<Suggestion, "cursorInfo">[] =
})
// todo unify genExpected* functions
const rawPrefixes = ["prefix", `quoted prefix`, `prefix"requires"escape`]
const prefixes = ["prefix", `"quoted prefix"`, `"prefix\\"requires\\"escape"`]
const prefixes = ["prefix", `"quoted prefix"`, `prefix\\"requires\\"escape`]
const genExpectedPrefixes = (suggestion: Suggestion) => prefixes.map(value => ({ suggestion, value }))

const rawVariables = ["variable", `quoted variable`, `variable"requires"escape`]
const variables = ["variable", `"quoted variable"`, `"variable\\"requires\\"escape"`]
const variables = ["variable", `"quoted variable"`, `variable\\"requires\\"escape`]
const genExpectedVariables = (suggestion: Suggestion) => variables.map(value => ({ suggestion, value }))

const wordOperators = ["and", "AND", "or", "OR"]
Expand All @@ -85,10 +85,10 @@ const genExpectedCustomOps = (suggestion: Suggestion) => customPropertyOperators
const properties = ["prop"]
const genExpectedProps = (suggestion: Suggestion) => properties.map(value => ({ suggestion, value }))
const rawArrayValues = ["value", `quoted value`, `value"requires"escape`]
const arrayValues = ["value", `"quoted value"`, `"value\\"requires\\"escape"`]
const arrayValues = ["value", `"quoted value"`, `value\\"requires\\"escape`]
const genExpectedArrayValues = (suggestion: Suggestion) => arrayValues.map(value => ({ suggestion, value }))
const rawValues = ["value", `quoted value`, `value"requires"escape`]
const values = ["value", `"quoted value"`, `"value\\"requires\\"escape"`]
const values = ["value", `"quoted value"`, `value\\"requires\\"escape`]
const genExpectedValues = (suggestion: Suggestion) => values.map(value => ({ suggestion, value }))

const completionOpts: Parameters<Parser["autocomplete"]>[2] = {
Expand Down

0 comments on commit 252726e

Please sign in to comment.