Skip to content

Commit

Permalink
comments
Browse files Browse the repository at this point in the history
  • Loading branch information
jczhong84 committed Dec 9, 2024
1 parent 8fb7da6 commit d6b99d4
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,13 @@ export const TableColumnTooltip: React.FunctionComponent<IProps> = ({
<div>{column.name}</div>
</div>
{column.type && (
<div className="mt4">
<div className="tooltip-title">Type</div>
<div className="mt4 flex-row">
<div className="tooltip-title">Type:</div>
<div className="tooltip-content">{column.type}</div>
</div>
)}
{tagsDOM.length > 0 && (
<div className="mt4">
<div className="tooltip-title">Tags</div>
<div className="tooltip-content">
<div className="DataTableTags flex-row">{tagsDOM}</div>
</div>
</div>
<div className="DataTableTags flex-row">{tagsDOM}</div>
)}
{column.comment && (
<div className="mt4">
Expand All @@ -75,7 +70,7 @@ export const TableColumnTooltip: React.FunctionComponent<IProps> = ({
<div className="tooltip-content">{description}</div>
</div>
)}
{!!column?.stats?.length && (
{statsDOM.length && (
<div className="mt4">
<div className="tooltip-title">Stats</div>
<div className="tooltip-content">{statsDOM}</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import {
autocompletion,
Completion,
CompletionContext,
CompletionResult,
startCompletion,
} from '@codemirror/autocomplete';
import { EditorView } from '@uiw/react-codemirror';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { CodeMirrorToken } from 'lib/codemirror/utils';
import { IPosition } from 'lib/sql-helper/sql-lexer';
import { SqlParser } from 'lib/sql-helper/sql-parser';

export type AutoCompleteType = 'none' | 'schema' | 'all';
Expand All @@ -26,8 +29,7 @@ export const useAutoCompleteExtension = ({
const [typing, setTyping] = useState(false);

const getColumnValueCompletions = useCallback(
(cursor, token) => {
console.log('getColumnValueCompletions', cursor, token);
(cursor: IPosition, token: CodeMirrorToken): CompletionResult => {
const [textBeforeEqual, textAfterEqual] = token.text
.split('=')
.map((s) => s.trim());
Expand All @@ -49,40 +51,15 @@ export const useAutoCompleteExtension = ({
[sqlParserRef]
);

const getCompletions = useCallback(
async (context: CompletionContext) => {
if (type === 'none') {
return null;
}

// Get the token before the cursor, token could be in below foramts
// - column: schema.table.column, table.column, column
// - table: schema.table, table
// - keyword: any keyword
// - column value: column = value, (value may be quoted)
const token = context.matchBefore(
/(\w+\.){0,2}\w*|(\w+.)?\s*=\s*'?\w*/
);

// no token before the cursor, don't open completions.
if (!token?.text) return null;

// Get the cursor position in codemirror v5 format
const cursorPos = context.pos;
const line = context.state.doc.lineAt(cursorPos);
const cursor = { line: line.number - 1, ch: cursorPos - line.from };

const getGeneralCompletions = useCallback(
async (
context: string,
cursor: IPosition,
token: CodeMirrorToken
): Promise<CompletionResult> => {
const tokenText = token.text.toLowerCase();
const sqlParserContext =
sqlParserRef.current.getContextAtPos(cursor);

// handle the case where the token is a column and the user is trying to type a value in a where clause
if (sqlParserContext === 'column' && tokenText.includes('=')) {
return getColumnValueCompletions(cursor, token);
}

const options: Completion[] = [];
if (sqlParserContext === 'column') {
if (context === 'column') {
const columns = sqlParserRef.current.getColumnMatches(
cursor,
token.text
Expand All @@ -93,7 +70,7 @@ export const useAutoCompleteExtension = ({
detail: 'column',
}))
);
} else if (sqlParserContext === 'table') {
} else if (context === 'table') {
const tableNames =
await sqlParserRef.current.getTableNameMatches(tokenText);
options.push(
Expand All @@ -118,13 +95,56 @@ export const useAutoCompleteExtension = ({
);

let from = token.from;
if (sqlParserContext === 'column') {
if (context === 'column') {
from += token.text.lastIndexOf('.') + 1;
}

return { from, options };
},
[sqlParserRef, type, getColumnValueCompletions]
[sqlParserRef, type]
);

const getCompletions = useCallback(
async (context: CompletionContext) => {
if (type === 'none') {
return null;
}

// Get the token before the cursor, token could be in below foramts
// - column value: column = value (value may be quoted)
const columnValueRegex = /(\w+\.)?\w+\s*=\s*['"]?\w*/;
// - column: schema.table.column, table.column, column
// - table: schema.table, table
// - keyword: any keyword
const generalTokenRegex = /(\w+\.){0,2}\w*/;

const columnValueToken = context.matchBefore(columnValueRegex);
const generalToken =
!columnValueToken && context.matchBefore(generalTokenRegex);

// no token before the cursor, don't open completions.
if (!columnValueToken?.text && !generalToken.text) return null;

// Get the cursor position in codemirror v5 format
const cursorPos = context.pos;
const line = context.state.doc.lineAt(cursorPos);
const cursor = { line: line.number - 1, ch: cursorPos - line.from };

const sqlParserContext =
sqlParserRef.current.getContextAtPos(cursor);

// handle the case where the token is a column and the user is trying to type a value in a where clause
if (sqlParserContext === 'column' && columnValueToken?.text) {
return getColumnValueCompletions(cursor, columnValueToken);
}

return getGeneralCompletions(
sqlParserContext,
cursor,
generalToken
);
},
[sqlParserRef, type, getColumnValueCompletions, getGeneralCompletions]
);

const triggerCompletionOnType = () => {
Expand Down
36 changes: 15 additions & 21 deletions querybook/webapp/lib/sql-helper/sql-parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { bind } from 'lodash-decorators';

import { IDataColumn } from 'const/metastore';
import { ICodeAnalysis, TableToken } from 'lib/sql-helper/sql-lexer';
import { ICodeAnalysis, IPosition, TableToken } from 'lib/sql-helper/sql-lexer';
import {
getLanguageSetting,
ILanguageSetting,
Expand Down Expand Up @@ -59,7 +59,7 @@ export class SqlParser {
* @param pos position of the cursor or mouse pointer
* @returns string: 'table', 'column' or 'none'
*/
public getContextAtPos(pos: { line: number; ch: number }): string {
public getContextAtPos(pos: IPosition): string {
if (!this.codeAnalysis?.editorLines) {
return 'none';
}
Expand All @@ -77,7 +77,7 @@ export class SqlParser {
* @param pos position of the cursor or mouse pointer
* @returns TableToken if the cursor is on a table, otherwise null
*/
public getTableAtPos(pos: { line: number; ch: number }): TableToken | null {
public getTableAtPos(pos: IPosition): TableToken | null {
const { line, ch } = pos;
if (this.codeAnalysis) {
const tableReferences: TableToken[] = [].concat.apply(
Expand Down Expand Up @@ -111,10 +111,7 @@ export class SqlParser {
* @param text the token text before or at the cursor
* @returns IDataColumn if the cursor is on a column, otherwise null
*/
public getColumnAtPos(
pos: { line: number; ch: number },
text: string
): IDataColumn | null {
public getColumnAtPos(pos: IPosition, text: string): IDataColumn | null {
const columns = this.getColumnMatches(pos, text, true);
if (columns.length === 1) {
return columns[0];
Expand All @@ -129,23 +126,20 @@ export class SqlParser {
* @returns Array of column values if the cursor is on a column, otherwise empty array
*/
public getColumnValues(
cursor: { line: number; ch: number },
cursor: IPosition,
text: string
): Array<number | string> {
const columns = this.getColumnMatches(cursor, text, true);

if (columns.length === 1) {
const colStats = columns[0].stats ?? [];
if (columns.length !== 1) return [];

// find the stat with key="distinct_values"
const distinctValuesStat = colStats.find(
(stat) => stat.key === 'distinct_values'
);
if (distinctValuesStat?.value instanceof Array) {
return distinctValuesStat?.value;
}

return [];
const colStats = columns[0].stats ?? [];
// find the stat with key="distinct_values"
const distinctValuesStat = colStats.find(
(stat) => stat.key === 'distinct_values'
);
if (distinctValuesStat?.value instanceof Array) {
return distinctValuesStat?.value;
}

return [];
Expand All @@ -159,7 +153,7 @@ export class SqlParser {
* @param exactMatch whether to do exact match or prefix match
*/
public getColumnMatches(
cursor: { line: number; ch: number },
cursor: IPosition,
text: string,
exactMatch: boolean = false
): IDataColumn[] {
Expand Down Expand Up @@ -255,7 +249,7 @@ export class SqlParser {
});
}

private getLineAnalysis(cursor: { line: number; ch: number }) {
private getLineAnalysis(cursor: IPosition): ILineAnalysis {
const lineAnalysis: ILineAnalysis = {
context: 'none',
alias: {},
Expand Down

0 comments on commit d6b99d4

Please sign in to comment.