Ctrl K
ComunidadGuías…

packages/sql/completion-templates.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type { SqlCatalog } from './catalog'
import type { CompletionContext } from './completion-context'
import type { CompletionItem } from './completion-items'
import { orderedItems, quoteIfNeeded } from './completion-items'
import { scopeTable } from './resolve'
import type { StatementScope, TableRef } from './scope'

const singular = (name: string) => {
  if (name.endsWith('ies')) {
    return `${name.slice(0, -3)}y`
  }
  return name.endsWith('s') ? name.slice(0, -1) : name
}

const joinConditionItems = (
  scope: StatementScope,
  catalog: SqlCatalog
): CompletionItem[] => {
  const joined = scope.visible.at(-1)
  if (!joined || scope.visible.length < 2) {
    return []
  }
  const pairs: string[] = []
  const sides = (ref: TableRef) => {
    const table = scopeTable(scope, catalog, ref)
    return {
      columns: table?.columns?.map((column) => column.name.toLowerCase()) ?? [],
      prefix: ref.alias ?? ref.name,
      table,
    }
  }
  const right = sides(joined)
  for (const ref of scope.visible.slice(0, -1)) {
    const left = sides(ref)
    if (!left.table || !right.table) {
      continue
    }
    for (const [from, to] of [
      [left, right],
      [right, left],
    ] as const) {
      const key = `${singular(to.table?.name ?? '').toLowerCase()}_id`
      if (from.columns.includes(key) && to.columns.includes('id')) {
        pairs.push(`${from.prefix}.${key} = ${to.prefix}.id`)
      }
    }
  }
  return orderedItems(
    pairs.map((label) => ({ label })),
    'column'
  )
}

const insertTemplate = (
  scope: StatementScope,
  catalog: SqlCatalog
): CompletionItem[] => {
  const table = scope.target && scopeTable(scope, catalog, scope.target)
  const columns = table?.columns?.filter((column) => column.name !== 'id')
  if (!columns || columns.length === 0) {
    return []
  }
  const names = columns.map((column) => column.name)
  const inserted = names.map((name) => quoteIfNeeded(catalog, name))
  return orderedItems(
    [
      {
        detail: names.join(', '),
        insertText: `(${inserted.join(', ')})\nVALUES (${names.map((name, index) => `\${${index + 1}:${name}}`).join(', ')})`,
        label: '(columns…) VALUES (…)',
        snippet: true,
      },
    ],
    'keyword'
  )
}

export const templateItems = (
  context: CompletionContext,
  catalog: SqlCatalog
): CompletionItem[] => {
  const { scope, selectedColumns, template } = context
  if (template === 'select') {
    const [only] = scope.visible
    const table =
      only && scope.visible.length === 1
        ? scopeTable(scope, catalog, only)
        : undefined
    const all =
      table?.columns?.map((column) => quoteIfNeeded(catalog, column.name)) ?? []
    return orderedItems(
      [
        { label: '*' },
        ...(all.length > 0
          ? [
              {
                detail: 'all columns',
                insertText: all.join(', '),
                label: all.join(', '),
              },
            ]
          : []),
      ],
      'column'
    )
  }
  if (template === 'group-by' && selectedColumns.length > 0) {
    return orderedItems(
      [
        ...(selectedColumns.length > 1
          ? [
              {
                detail: 'every selected column',
                label: selectedColumns.join(', '),
              },
            ]
          : []),
        ...selectedColumns.map((label) => ({ label })),
      ],
      'column'
    )
  }
  if (template === 'join-on') {
    return joinConditionItems(scope, catalog)
  }
  if (template === 'into') {
    return insertTemplate(scope, catalog)
  }
  return []
}