| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- import type { RefObject } from 'react'
- import BackwardIterator from './BackwardIterator'
- import type { DatabaseFunction } from '@/data/database-functions/database-functions-query'
- import type { Schema } from '@/data/database/schemas-query'
- import type { TableColumn } from '@/data/database/table-columns-query'
- // [Joshen] Needs to be fixed
- export default function getPgsqlCompletionProvider(monaco: any, pgInfoRef: RefObject<any>) {
- return {
- triggerCharacters: [' ', '.', '"'],
- provideCompletionItems: function (model: any, position: any, context: any) {
- try {
- // position.column should minus 2 as it returns 2 for first char
- // position.lineNumber should minus 1
- let iterator = new BackwardIterator(model, position.column - 2, position.lineNumber - 1)
- if (context.triggerCharacter === '"') {
- return startingQuoteScenarioSuggestions(monaco, pgInfoRef, iterator)
- } else if (context.triggerCharacter === '.') {
- return dotScenarioSuggestions(monaco, pgInfoRef, iterator)
- } else {
- return defaultScenarioSuggestions(monaco, pgInfoRef)
- }
- } catch (_) {
- // any error, returns empty suggestion
- return { suggestions: [] }
- }
- },
- }
- }
- function startingQuoteScenarioSuggestions(monaco: any, pgInfoRef: RefObject<any>, iterator: any) {
- const items: any[] = []
- let startingQuotedIdent = iterator.isFowardDQuote()
- if (!startingQuotedIdent) return { suggestions: items }
- iterator.next() // get passed the starting quote
- if (iterator.isNextPeriod()) {
- // probably a field - get the ident
- let ident = iterator.readIdent()
- let isQuotedIdent = false
- if (ident.match(/^\".*?\"$/)) {
- isQuotedIdent = true
- ident = fixQuotedIdent(ident)
- }
- let table = pgInfoRef.current.tableColumns.find((tbl: TableColumn) => {
- return (
- (isQuotedIdent && tbl.tablename === ident) ||
- (!isQuotedIdent && tbl.tablename.toLocaleLowerCase() == ident.toLocaleLowerCase())
- )
- })
- if (!table) return { suggestions: items }
- table.columns.forEach((field: any) => {
- items.push({
- label: field.attname,
- kind: monaco.languages.CompletionItemKind.Property,
- detail: field.data_type,
- insertText: field.attname,
- })
- })
- } else {
- // probably a table - list the tables
- pgInfoRef.current.tableColumns.forEach((table: TableColumn) => {
- items.push({
- label: table.tablename,
- kind: monaco.languages.CompletionItemKind.Class,
- insertText: table.tablename,
- })
- })
- }
- return { suggestions: items }
- }
- function dotScenarioSuggestions(monaco: any, pgInfoRef: RefObject<any>, iterator: any) {
- const items: any[] = []
- let idents = readIdents(iterator, 3)
- let pos = 0
- let schema = pgInfoRef.current.schemas.find((sch: Schema) => {
- const _ident = idents && idents.length > pos ? idents[pos] : {}
- return (
- (_ident.isQuoted && sch.name === _ident.name) ||
- (!_ident.isQuoted && sch.name?.toLocaleLowerCase() == _ident.name?.toLocaleLowerCase())
- )
- })
- if (!schema) {
- schema = pgInfoRef.current.schemas.find((sch: Schema) => {
- return sch.name == 'public'
- })
- } else {
- pos++
- }
- if (idents.length == pos) {
- pgInfoRef.current.tableColumns.forEach((tbl: TableColumn) => {
- if (tbl.schemaname != schema.name) {
- return
- }
- items.push({
- label: tbl.tablename,
- kind: monaco.languages.CompletionItemKind.Class,
- detail: tbl.schemaname !== 'public' ? tbl.schemaname : null,
- insertText: formatInsertText(tbl.tablename),
- })
- })
- return { suggestions: items }
- }
- let table = pgInfoRef.current.tableColumns.find((tbl: TableColumn) => {
- const _ident = idents && idents.length > pos ? idents[pos] : {}
- return (
- (tbl.schemaname == schema.name && _ident.isQuoted && tbl.tablename === _ident.name) ||
- (!_ident.isQuoted && tbl.tablename?.toLocaleLowerCase() == _ident.name?.toLocaleLowerCase())
- )
- })
- if (table) {
- table.columns.forEach((field: any) => {
- items.push({
- label: field.attname,
- kind: monaco.languages.CompletionItemKind.Property,
- detail: field.data_type,
- insertText: formatInsertText(field.attname),
- })
- })
- }
- return { suggestions: items }
- }
- function defaultScenarioSuggestions(monaco: any, pgInfoRef: RefObject<any>) {
- const items: any = []
- if (pgInfoRef.current.keywords?.length > 0) {
- pgInfoRef.current.keywords.forEach((x: string) => {
- items.push({
- label: x,
- kind: monaco.languages.CompletionItemKind.Keyword,
- insertText: x,
- })
- })
- }
- if (pgInfoRef.current.schemas?.length > 0) {
- pgInfoRef.current.schemas.forEach((x: Schema) => {
- items.push({
- label: x.name,
- kind: monaco.languages.CompletionItemKind.Keyword,
- insertText: x.name,
- })
- })
- }
- if (pgInfoRef.current.tableColumns?.length > 0) {
- pgInfoRef.current.tableColumns.forEach((x: TableColumn) => {
- const insertText = x.schemaname == 'public' ? x.tablename : x.schemaname + '.' + x.tablename
- items.push({
- label: x.tablename,
- detail: x.schemaname !== 'public' ? x.schemaname : null,
- kind: x.is_table
- ? monaco.languages.CompletionItemKind.Class
- : monaco.languages.CompletionItemKind.Interface,
- insertText: formatInsertText(insertText),
- })
- x.columns.forEach((field: any) => {
- if (!field) return
- let foundItem = items.find(
- (i: any) =>
- i.label === field?.attname &&
- i.kind === monaco.languages.CompletionItemKind.Field &&
- i.detail === field?.data_type
- )
- if (foundItem) {
- foundItem.tables.push(x.tablename)
- foundItem.tables.sort()
- foundItem.documentation = foundItem.tables.join(', ')
- } else {
- items.push({
- label: field.attname,
- kind: monaco.languages.CompletionItemKind.Field,
- detail: field.data_type,
- documentation: x.tablename,
- tables: [x.tablename],
- insertText: formatInsertText(field.attname),
- })
- }
- })
- })
- }
- if (pgInfoRef.current.functions?.length > 0) {
- pgInfoRef.current.functions.forEach((x: DatabaseFunction) => {
- items.push({
- label: x.name,
- kind: monaco.languages.CompletionItemKind.Function,
- detail: x.return_type,
- insertText: x.name,
- })
- })
- }
- return { suggestions: items }
- }
- function fixQuotedIdent(str: string) {
- return str.replace(/^\"/, '').replace(/\"$/, '').replace(/\"\"/, '"')
- }
- function readIdents(iterator: any, maxlvl: number) {
- return iterator.readIdents(maxlvl).map((name: string) => {
- let isQuoted = false
- if (name.match(/^\".*?\"$/)) {
- isQuoted = true
- name = fixQuotedIdent(name)
- }
- return { isQuoted: isQuoted, name: name }
- })
- }
- function formatInsertText(value: string) {
- const hasUpperCase = !(value == value.toLowerCase())
- return hasUpperCase ? `"${value}"` : value
- }
|