Completion provides auto-complete candidates for PartiQL statements. It uses the omni completion engine for keyword and table suggestions, and supplements with column suggestions from bytebase metadata.
(ctx context.Context, cCtx base.CompletionContext, statement string, caretLine int, caretOffset int)
| 23 | // It uses the omni completion engine for keyword and table suggestions, |
| 24 | // and supplements with column suggestions from bytebase metadata. |
| 25 | func Completion(ctx context.Context, cCtx base.CompletionContext, statement string, caretLine int, caretOffset int) ([]base.Candidate, error) { |
| 26 | // Build omni catalog from bytebase metadata. |
| 27 | cat := catalog.New() |
| 28 | var databaseMetadata *model.DatabaseMetadata |
| 29 | if cCtx.Metadata != nil && cCtx.DefaultDatabase != "" { |
| 30 | _, dbMeta, err := cCtx.Metadata(ctx, cCtx.InstanceID, cCtx.DefaultDatabase) |
| 31 | if err == nil && dbMeta != nil { |
| 32 | databaseMetadata = dbMeta |
| 33 | schema := dbMeta.GetSchemaMetadata("") |
| 34 | if schema != nil { |
| 35 | for _, table := range schema.ListTableNames() { |
| 36 | cat.AddTable(table) |
| 37 | } |
| 38 | } |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | // Convert (line, offset) to byte position in the statement. |
| 43 | pos := lineOffsetToBytePos(statement, caretLine, caretOffset) |
| 44 | |
| 45 | // Get omni completion candidates. |
| 46 | omniCandidates := completion.Complete(statement, pos, cat) |
| 47 | |
| 48 | // Convert to base.Candidate format. |
| 49 | candidateMap := make(map[string]base.Candidate) |
| 50 | for _, c := range omniCandidates { |
| 51 | var candidateType base.CandidateType |
| 52 | switch c.Kind { |
| 53 | case "keyword": |
| 54 | candidateType = base.CandidateTypeKeyword |
| 55 | case "table": |
| 56 | candidateType = base.CandidateTypeTable |
| 57 | default: |
| 58 | candidateType = base.CandidateTypeNone |
| 59 | } |
| 60 | key := candidateKey(c.Text, candidateType) |
| 61 | candidateMap[key] = base.Candidate{ |
| 62 | Type: candidateType, |
| 63 | Text: c.Text, |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | // Add column completions if we detect a SELECT-item context. |
| 68 | // The omni completion engine handles keyword/table contexts; for columns, |
| 69 | // we check whether the cursor is in a position where columns are relevant |
| 70 | // (after SELECT but before FROM, or in WHERE/HAVING clauses). |
| 71 | if databaseMetadata != nil && isColumnContext(statement, pos) { |
| 72 | addColumnCandidates(candidateMap, statement, pos, databaseMetadata) |
| 73 | } |
| 74 | |
| 75 | // Filter candidates by scene. In query-only mode, exclude DML/DDL |
| 76 | // keywords that are not valid in read-only editor contexts. |
| 77 | if cCtx.Scene == base.SceneTypeQuery { |
| 78 | for key, c := range candidateMap { |
| 79 | if c.Type == base.CandidateTypeKeyword && isDMLKeyword(c.Text) { |
| 80 | delete(candidateMap, key) |
| 81 | } |
| 82 | } |