()
| 149 | } |
| 150 | |
| 151 | func (c *Completer) completion() ([]base.Candidate, error) { |
| 152 | // Check if the caret is inside or at the start of a backtick-quoted identifier. |
| 153 | // caretTokenIndex points to the first token at or after cursorByteOffset, so |
| 154 | // the containing token (when the cursor is inside one) is at index-1. |
| 155 | checkTokenQuoted := func(idx int) bool { |
| 156 | if idx < 0 || idx >= len(c.tokens) { |
| 157 | return false |
| 158 | } |
| 159 | tok := c.tokens[idx] |
| 160 | return mysqlparser.IsIdentTokenType(tok.Type) && tok.Loc < len(c.sql) && c.sql[tok.Loc] == '`' |
| 161 | } |
| 162 | if checkTokenQuoted(c.caretTokenIndex) { |
| 163 | c.caretTokenIsQuoted = true |
| 164 | } else if c.caretTokenIndex > 0 { |
| 165 | prev := c.tokens[c.caretTokenIndex-1] |
| 166 | if prev.End >= c.cursorByteOffset && checkTokenQuoted(c.caretTokenIndex-1) { |
| 167 | c.caretTokenIsQuoted = true |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | caretIndex := c.caretTokenIndex |
| 172 | if caretIndex > 0 && !isNoSeparatorRequired(c.tokens[caretIndex-1].Type) { |
| 173 | caretIndex-- |
| 174 | } |
| 175 | c.referencesStack = append([][]base.TableReference{{}}, c.referencesStack...) |
| 176 | |
| 177 | candidates := mysqlparser.Collect(c.sql, c.cursorByteOffset) |
| 178 | |
| 179 | if len(candidates.Rules) == 0 { |
| 180 | if prefixTok, ok := c.prefixToken(); ok { |
| 181 | candidates = mysqlparser.Collect(c.sql, prefixTok.Loc) |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | for _, rc := range candidates.Rules { |
| 186 | if rc.Rule == "columnref" { |
| 187 | c.collectLeadingTableReferences(caretIndex) |
| 188 | c.takeReferencesSnapshot() |
| 189 | c.collectRemainingTableReferences() |
| 190 | c.takeReferencesSnapshot() |
| 191 | break |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | return c.convertCandidates(candidates) |
| 196 | } |
| 197 | |
| 198 | func (c *Completer) prefixToken() (mysqlparser.Token, bool) { |
| 199 | idx := c.caretTokenIndex |
no test coverage detected