From 84ef17bce4ff3e3eb0a40ce5c463c0159a5aeb87 Mon Sep 17 00:00:00 2001 From: Tom Owers Date: Mon, 12 Feb 2024 15:46:33 +0000 Subject: [PATCH] feat(autocomplete): Updates and fixes to autocomplete (#20271) * Updates and fixes to autocomplete * Added better support for types --- frontend/src/lib/components/CodeEditor.scss | 8 ++++ frontend/src/lib/components/CodeEditors.tsx | 2 + .../nodes/HogQLQuery/HogQLQueryEditor.tsx | 8 +++- frontend/src/queries/schema.json | 4 ++ frontend/src/queries/schema.ts | 5 ++ posthog/hogql/autocomplete.py | 48 ++++++++++++++++--- posthog/schema.py | 4 ++ posthog/warehouse/api/saved_query.py | 1 + 8 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 frontend/src/lib/components/CodeEditor.scss diff --git a/frontend/src/lib/components/CodeEditor.scss b/frontend/src/lib/components/CodeEditor.scss new file mode 100644 index 0000000000000..8a41a62c5d9ca --- /dev/null +++ b/frontend/src/lib/components/CodeEditor.scss @@ -0,0 +1,8 @@ +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents { + display: block !important; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label { + width: 100%; + text-align: right; +} diff --git a/frontend/src/lib/components/CodeEditors.tsx b/frontend/src/lib/components/CodeEditors.tsx index 102b2da3d3c67..094d8d84340ef 100644 --- a/frontend/src/lib/components/CodeEditors.tsx +++ b/frontend/src/lib/components/CodeEditors.tsx @@ -1,3 +1,5 @@ +import './CodeEditor.scss' + import MonacoEditor, { type EditorProps } from '@monaco-editor/react' import { useValues } from 'kea' import { Spinner } from 'lib/lemon-ui/Spinner' diff --git a/frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx b/frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx index 9bc09e1089cb7..443e357cac026 100644 --- a/frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx +++ b/frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx @@ -239,7 +239,10 @@ export function HogQLQueryEditor(props: HogQLQueryEditorProps): JSX.Element { const sortText = kindToSortText(item.kind, item.label) return { - label: item.label, + label: { + label: item.label, + detail: item.detail, + }, documentation: item.documentation, insertText: item.insertText, range: { @@ -338,6 +341,9 @@ export function HogQLQueryEditor(props: HogQLQueryEditorProps): JSX.Element { scrollBeyondLastLine: false, automaticLayout: true, fixedOverflowWidgets: true, + suggest: { + showInlineDetails: true, + }, }} /> diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index 4c14f1fd1c36d..953dd48ad72e6 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -314,6 +314,10 @@ "AutocompleteCompletionItem": { "additionalProperties": false, "properties": { + "detail": { + "description": "A human-readable string with additional information about this item, like type or symbol information.", + "type": "string" + }, "documentation": { "description": "A human-readable string that represents a doc-comment.", "type": "string" diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index 57f0e90f1a9db..997a93328e2ae 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -249,6 +249,11 @@ export interface AutocompleteCompletionItem { * A human-readable string that represents a doc-comment. */ documentation?: string + /** + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + detail?: string /** * A string or snippet that should be inserted in a document when selecting * this completion. diff --git a/posthog/hogql/autocomplete.py b/posthog/hogql/autocomplete.py index 8aba7802b256e..5cb7c17fa5e52 100644 --- a/posthog/hogql/autocomplete.py +++ b/posthog/hogql/autocomplete.py @@ -82,6 +82,27 @@ def constant_type_to_database_field(constant_type: ConstantType, name: str) -> D return DatabaseField(name=name) +def convert_field_or_table_to_type_string(field_or_table: FieldOrTable) -> str: + if isinstance(field_or_table, ast.BooleanDatabaseField): + return "Boolean" + if isinstance(field_or_table, ast.IntegerDatabaseField): + return "Integer" + if isinstance(field_or_table, ast.FloatDatabaseField): + return "Float" + if isinstance(field_or_table, ast.StringDatabaseField): + return "String" + if isinstance(field_or_table, ast.DateTimeDatabaseField): + return "DateTime" + if isinstance(field_or_table, ast.DateDatabaseField): + return "Date" + if isinstance(field_or_table, ast.StringJSONDatabaseField): + return "Object" + if isinstance(field_or_table, (ast.Table, ast.LazyJoin)): + return "Table" + + return "" + + def get_table(context: HogQLContext, join_expr: ast.JoinExpr, ctes: Optional[Dict[str, CTE]]) -> None | Table: assert context.database is not None @@ -165,6 +186,7 @@ def extend_responses( suggestions: List[AutocompleteCompletionItem], kind: Kind = Kind.Variable, insert_text: Optional[Callable[[str], str]] = None, + details: Optional[List[str | None]] = None, ) -> None: suggestions.extend( [ @@ -172,8 +194,9 @@ def extend_responses( insertText=insert_text(key) if insert_text is not None else key, label=key, kind=kind, + detail=details[index] if details is not None else None, ) - for key in keys + for index, key in enumerate(keys) ] ) @@ -208,7 +231,10 @@ def get_hogql_autocomplete(query: HogQLAutocomplete, team: Team) -> HogQLAutocom select_ast = parse_select(query.select) if query.filters: - select_ast = cast(ast.SelectQuery, replace_filters(select_ast, query.filters, team)) + try: + select_ast = cast(ast.SelectQuery, replace_filters(select_ast, query.filters, team)) + except Exception: + pass if isinstance(select_ast, ast.SelectQuery): ctes = select_ast.ctes @@ -256,8 +282,14 @@ def get_hogql_autocomplete(query: HogQLAutocomplete, team: Team) -> HogQLAutocom if is_last_part: if last_table.fields.get(str(chain_part)) is None: - fields = list(table.fields.keys()) - extend_responses(fields, response.suggestions) + keys: List[str] = [] + details: List[str | None] = [] + table_fields = list(table.fields.items()) + for field_name, field_or_table in table_fields: + keys.append(field_name) + details.append(convert_field_or_table_to_type_string(field_or_table)) + + extend_responses(keys=keys, suggestions=response.suggestions, details=details) available_functions = ALL_EXPOSED_FUNCTION_NAMES extend_responses( @@ -289,9 +321,13 @@ def get_hogql_autocomplete(query: HogQLAutocomplete, team: Team) -> HogQLAutocom name__contains=match_term, team_id=team.pk, type=property_type, - )[:PROPERTY_DEFINITION_LIMIT].values("name") + )[:PROPERTY_DEFINITION_LIMIT].values("name", "property_type") - extend_responses([prop["name"] for prop in properties], response.suggestions) + extend_responses( + keys=[prop["name"] for prop in properties], + suggestions=response.suggestions, + details=[prop["property_type"] for prop in properties], + ) elif isinstance(field, VirtualTable) or isinstance(field, LazyTable): fields = list(last_table.fields.keys()) extend_responses(fields, response.suggestions) diff --git a/posthog/schema.py b/posthog/schema.py index 328f61346b2b3..544a77f4737cd 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -64,6 +64,10 @@ class AutocompleteCompletionItem(BaseModel): model_config = ConfigDict( extra="forbid", ) + detail: Optional[str] = Field( + default=None, + description="A human-readable string with additional information about this item, like type or symbol information.", + ) documentation: Optional[str] = Field( default=None, description="A human-readable string that represents a doc-comment." ) diff --git a/posthog/warehouse/api/saved_query.py b/posthog/warehouse/api/saved_query.py index 02182f9b3a6b7..b87499e056295 100644 --- a/posthog/warehouse/api/saved_query.py +++ b/posthog/warehouse/api/saved_query.py @@ -71,6 +71,7 @@ def validate_query(self, query): _is_valid_view = is_valid_view(select_ast) if not _is_valid_view: raise exceptions.ValidationError(detail="Ensure all fields are aliased") + try: print_ast( node=select_ast,