Completion provides auto-complete candidates for TiDB statements using the omni TiDB completion engine, replacing the previous mysql ANTLR completer.
(ctx context.Context, cCtx base.CompletionContext, statement string, caretLine int, caretOffset int)
| 50 | // Completion provides auto-complete candidates for TiDB statements using the |
| 51 | // omni TiDB completion engine, replacing the previous mysql ANTLR completer. |
| 52 | func Completion(ctx context.Context, cCtx base.CompletionContext, statement string, caretLine int, caretOffset int) ([]base.Candidate, error) { |
| 53 | // Limit completion to the statement containing the caret so table refs from |
| 54 | // earlier statements in the buffer don't leak into the candidate set. |
| 55 | stmt, pos := currentStatement(statement, lineOffsetToBytePos(statement, caretLine, caretOffset)) |
| 56 | |
| 57 | cat := buildCatalog(ctx, cCtx, stmt) |
| 58 | caretInBacktick := caretInsideBacktickIdentifier(stmt, pos) |
| 59 | candidateMap := make(map[string]base.Candidate) |
| 60 | for _, c := range omnicompletion.Complete(stmt, pos, cat) { |
| 61 | t := omniCandidateTypeToBase(c.Type) |
| 62 | text := c.Text |
| 63 | switch { |
| 64 | case isObjectIdentifierCandidate(t): |
| 65 | text = quoteIdentifierIfNeeded(c.Text, caretInBacktick) |
| 66 | case t == base.CandidateTypeFunction: |
| 67 | text = c.Text + "()" |
| 68 | default: |
| 69 | // Keywords and value-like candidates pass through unchanged. |
| 70 | } |
| 71 | candidateMap[string(t)+":"+text] = base.Candidate{ |
| 72 | Type: t, |
| 73 | Text: text, |
| 74 | Definition: c.Definition, |
| 75 | Comment: c.Comment, |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | // In the read-only query scene, drop keywords that *initiate* writes |
| 80 | // (DML/DDL/transaction/admin statements) so the editor doesn't offer to start |
| 81 | // a write statement. This only applies at a statement-start position: the same |
| 82 | // keywords are valid sub-keywords inside read statements (e.g. CREATE in SHOW |
| 83 | // CREATE TABLE, UPDATE in SELECT ... FOR UPDATE) and must be preserved there. |
| 84 | if cCtx.Scene == base.SceneTypeQuery && caretAtStatementStart(stmt, pos) { |
| 85 | for key, c := range candidateMap { |
| 86 | if c.Type == base.CandidateTypeKeyword && writeStatementKeywords[strings.ToUpper(c.Text)] { |
| 87 | delete(candidateMap, key) |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | result := make([]base.Candidate, 0, len(candidateMap)) |
| 93 | for _, c := range candidateMap { |
| 94 | result = append(result, c) |
| 95 | } |
| 96 | slices.SortFunc(result, func(a, b base.Candidate) int { |
| 97 | if a.Type != b.Type { |
| 98 | if a.Type < b.Type { |
| 99 | return -1 |
| 100 | } |
| 101 | return 1 |
| 102 | } |
| 103 | if a.Text < b.Text { |
| 104 | return -1 |
| 105 | } |
| 106 | if a.Text > b.Text { |
| 107 | return 1 |
| 108 | } |
| 109 | return 0 |