Skip to content

Commit 720abee

Browse files
committed
Add autocomplete to LogContext editor
1 parent 579859c commit 720abee

File tree

4 files changed

+65
-12
lines changed

4 files changed

+65
-12
lines changed

src/LogContext/LogContextProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,6 @@ export class LogContextProvider {
126126
runContextQuery?: (() => void),
127127
origQuery?: ElasticsearchQuery
128128
): ReactNode {
129-
return ( LogContextUI({row, runContextQuery, origQuery, updateQuery: query=>{this.contextQuery=query}}))
129+
return ( LogContextUI({row, runContextQuery, origQuery, updateQuery: query=>{this.contextQuery=query}, datasource:this.datasource}))
130130
}
131131
}

src/LogContext/components/LogContextQueryEditor.tsx

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,44 @@ import { useQueryBuilderContext } from 'LogContext/QueryBuilder';
55

66
import CodeMirror, { ReactCodeMirrorRef } from '@uiw/react-codemirror';
77
import {linter, Diagnostic, lintGutter} from "@codemirror/lint"
8+
import {autocompletion, CompletionContext, Completion} from "@codemirror/autocomplete"
9+
import { QuickwitDataSource } from "datasource";
10+
import { useDatasource } from "components/QueryEditor/ElasticsearchQueryContext";
811

912

13+
class QueryAutocompleter {
14+
datasource: QuickwitDataSource
15+
constructor(datasource: QuickwitDataSource){
16+
this.datasource = datasource
17+
}
18+
getFields() {
19+
return this.datasource.getTagKeys()
20+
}
21+
async getSuggestions(word: string): Promise<Completion[]>{
22+
let suggestions: Completion[] = [];
23+
24+
25+
const candidateFields = await this.getFields()
26+
suggestions = (candidateFields
27+
.filter(f=>f.text.startsWith(word))
28+
.map(f=>({
29+
type: 'text',
30+
section: "Fields",
31+
label:f.text,
32+
detail:`${f.value}`,
33+
})));
34+
35+
return suggestions;
36+
}
37+
}
38+
1039
export function LogContextQueryEditor(){
1140
const editorRef = useRef<ReactCodeMirrorRef|null>(null)
1241
const builder = useQueryBuilderContext();
1342
const {parsedQuery} = builder;
1443

44+
const autocompleter = new QueryAutocompleter(useDatasource())
45+
1546
const queryLinter = linter( view => {
1647
let diagnostics: Diagnostic[] = [];
1748

@@ -26,13 +57,30 @@ export function LogContextQueryEditor(){
2657
}
2758
return diagnostics
2859
})
60+
61+
const autocomplete = autocompletion({
62+
override: [async (context: CompletionContext)=>{
63+
let word = context.matchBefore(/\S*/);
64+
65+
if (!word){ return null }
66+
const suggestions = await autocompleter.getSuggestions(word?.text);
67+
68+
return {
69+
from: word.from,
70+
options: suggestions
71+
}
72+
}]
73+
})
74+
75+
76+
2977
return (<CodeMirror
3078
ref={editorRef}
3179
className={css`height:100%`} // XXX : need to set height for both wrapper elements
3280
height="100%"
3381
theme={'dark'}
3482
value={builder.query}
3583
onChange={(query)=>builder.setQuery(query || '' )}
36-
extensions={[queryLinter, lintGutter()]}
84+
extensions={[queryLinter, lintGutter(), autocomplete]}
3785
/>);
3886
}

src/LogContext/components/LogContextUI.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { css } from "@emotion/css";
77
import { Button } from "@grafana/ui";
88
import { useQueryBuilder, QueryBuilderContext } from 'LogContext/QueryBuilder';
99
import { LogContextQueryBuilderSidebar } from "./LogContextQueryBuilderSidebar";
10+
import { DatasourceContext } from "components/QueryEditor/ElasticsearchQueryContext";
11+
import { QuickwitDataSource } from "datasource";
1012

1113
const logContextUiStyle = css`
1214
display: flex;
@@ -21,6 +23,7 @@ export interface LogContextProps {
2123
origQuery?: DataQuery
2224
}
2325
export interface LogContextUIProps extends LogContextProps {
26+
datasource: QuickwitDataSource,
2427
updateQuery: (query: string) => void
2528
}
2629

@@ -55,13 +58,15 @@ export function LogContextUI(props: LogContextUIProps ){
5558

5659
return (
5760
<div className={logContextUiStyle}>
58-
<QueryBuilderContext.Provider value={builder}>
59-
<LogContextQueryBuilderSidebar {...props} updateQuery={setParsedQuery}/>
60-
<div className={css`width:100%; display:flex; flex-direction:column; gap:0.5rem; min-width:0;`}>
61-
{ActionBar}
62-
<LogContextQueryEditor />
63-
</div>
64-
</QueryBuilderContext.Provider>
61+
<DatasourceContext.Provider value={props.datasource}>
62+
<QueryBuilderContext.Provider value={builder}>
63+
<LogContextQueryBuilderSidebar {...props} updateQuery={setParsedQuery}/>
64+
<div className={css`width:100%; display:flex; flex-direction:column; gap:0.5rem; min-width:0;`}>
65+
{ActionBar}
66+
<LogContextQueryEditor />
67+
</div>
68+
</QueryBuilderContext.Provider>
69+
</DatasourceContext.Provider>
6570
</div>
6671
);
6772
}

src/components/QueryEditor/ElasticsearchQueryContext.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import { reducer as metricsReducer } from './MetricAggregationsEditor/state/redu
1111
import { aliasPatternReducer, queryReducer, initQuery, initExploreQuery } from './state';
1212
import { getHook } from 'utils/context';
1313

14-
const RangeContext = createContext<TimeRange | undefined>(undefined);
14+
export const RangeContext = createContext<TimeRange | undefined>(undefined);
1515
export const useRange = getHook(RangeContext);
1616

17-
const QueryContext = createContext<ElasticsearchQuery | undefined>(undefined);
17+
export const QueryContext = createContext<ElasticsearchQuery | undefined>(undefined);
1818
export const useQuery = getHook(QueryContext);
1919

20-
const DatasourceContext = createContext<ElasticDatasource | undefined>(undefined);
20+
export const DatasourceContext = createContext<ElasticDatasource | undefined>(undefined);
2121
export const useDatasource = getHook(DatasourceContext);
2222

2323
interface Props {

0 commit comments

Comments
 (0)