Skip to content

Commit af6dff4

Browse files
authored
feat(compass-editor, compass-query-bar): query history autocomplete formatting COMPASS-8063 (#6060)
1 parent 3868044 commit af6dff4

File tree

8 files changed

+112
-45
lines changed

8 files changed

+112
-45
lines changed

package-lock.json

Lines changed: 20 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-editor/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
"@codemirror/lint": "^6.1.1",
7272
"@codemirror/state": "^6.1.4",
7373
"@codemirror/view": "^6.7.1",
74-
"@lezer/highlight": "^1.1.3",
74+
"@lezer/highlight": "^1.2.0",
7575
"@mongodb-js/compass-components": "^1.28.1",
7676
"@mongodb-js/mongodb-constants": "^0.10.0",
7777
"mongodb-query-parser": "^4.2.0",

packages/compass-editor/src/codemirror/query-autocompleter-with-history.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ describe('query history autocompleter', function () {
6868

6969
it('returns all saved queries as completions on click', async function () {
7070
expect(
71-
await getCompletions('{}', savedQueries, undefined, mockOnApply)
71+
await getCompletions('{}', savedQueries, undefined, mockOnApply, 'light')
7272
).to.have.lengthOf(5);
7373
});
7474

7575
it('returns combined completions when user starts typing', async function () {
7676
expect(
77-
await getCompletions('foo', savedQueries, undefined, mockOnApply)
77+
await getCompletions('foo', savedQueries, undefined, mockOnApply, 'light')
7878
).to.have.lengthOf(50);
7979
});
8080

@@ -88,7 +88,8 @@ describe('query history autocompleter', function () {
8888
'{ bar: 1, buz: 2, foo: "b',
8989
savedQueries,
9090
undefined,
91-
mockOnApply
91+
mockOnApply,
92+
'light'
9293
)
9394
).map((completion) => completion.label)
9495
).to.deep.eq(['bar', '1', 'buz', '2', 'foo', ...prettifiedSavedQueries]);

packages/compass-editor/src/codemirror/query-autocompleter-with-history.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@ import type {
1111
} from '@codemirror/autocomplete';
1212
import type { CompletionOptions } from '../autocompleter';
1313
import { css } from '@mongodb-js/compass-components';
14+
import type { CodemirrorThemeType } from '../editor';
1415

1516
export const createQueryWithHistoryAutocompleter = (
1617
recentQueries: SavedQuery[],
1718
options: Pick<CompletionOptions, 'fields' | 'serverVersion'> = {},
18-
onApply: (query: SavedQuery['queryProperties']) => void
19+
onApply: (query: SavedQuery['queryProperties']) => void,
20+
theme: CodemirrorThemeType
1921
): CompletionSource => {
2022
const queryHistoryAutocompleter = createQueryHistoryAutocompleter(
2123
recentQueries,
22-
onApply
24+
onApply,
25+
theme
2326
);
2427

2528
const originalQueryAutocompleter = createQueryAutocompleter(options);

packages/compass-editor/src/codemirror/query-history-autocompleter.ts

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,28 @@ import type {
22
CompletionContext,
33
CompletionSource,
44
} from '@codemirror/autocomplete';
5-
import { formatDate, spacing } from '@mongodb-js/compass-components';
5+
import {
6+
fontFamilies,
7+
formatDate,
8+
spacing,
9+
css,
10+
} from '@mongodb-js/compass-components';
611
import { toJSString } from 'mongodb-query-parser';
7-
import { css } from '@mongodb-js/compass-components';
12+
import { languages } from '../editor';
13+
import { highlightCode } from '@lezer/highlight';
14+
import { type CodemirrorThemeType, highlightStyles } from '../editor';
815

916
export type SavedQuery = {
1017
lastExecuted: Date;
1118
queryProperties: {
12-
[properyName: string]: any;
19+
[propertyName: string]: any;
1320
};
1421
};
1522

1623
export const createQueryHistoryAutocompleter = (
1724
savedQueries: SavedQuery[],
18-
onApply: (query: SavedQuery['queryProperties']) => void
25+
onApply: (query: SavedQuery['queryProperties']) => void,
26+
theme: CodemirrorThemeType
1927
): CompletionSource => {
2028
return function queryCompletions(context: CompletionContext) {
2129
if (savedQueries.length === 0) {
@@ -30,7 +38,7 @@ export const createQueryHistoryAutocompleter = (
3038
label: createQuery(query),
3139
type: 'text',
3240
detail: formatDate(query.lastExecuted.getTime()),
33-
info: () => createInfo(query).dom,
41+
info: () => createInfo(query, theme).dom,
3442
apply: () => {
3543
onApply(query.queryProperties);
3644
},
@@ -54,15 +62,15 @@ export const createQueryHistoryAutocompleter = (
5462
const queryLabelStyles = css({
5563
textTransform: 'capitalize',
5664
fontWeight: 'bold',
57-
margin: `${spacing[2]}px 0`,
65+
fontFamily: fontFamilies.default,
5866
});
5967

6068
const queryCodeStyles = css({
61-
maxHeight: '30vh',
62-
});
63-
64-
const completionInfoStyles = css({
65-
overflow: 'auto',
69+
fontFamily: fontFamilies.code,
70+
margin: `${spacing[50]}px`,
71+
marginLeft: `${spacing[100]}px`,
72+
padding: 0,
73+
whiteSpace: 'pre-wrap',
6674
});
6775

6876
export function createQuery(query: SavedQuery): string {
@@ -72,16 +80,22 @@ export function createQuery(query: SavedQuery): string {
7280
const noFilterKey = key === 'filter' ? '' : `${key}: `;
7381
res += formattedQuery ? `, ${noFilterKey}${formattedQuery}` : '';
7482
});
75-
const len = res.length;
76-
return len <= 100 ? res.slice(2, res.length) : res.slice(2, 100);
83+
return res.slice(2, res.length);
7784
}
7885

79-
function createInfo(query: SavedQuery): {
86+
const javascriptExpressionLanguageParser =
87+
languages['javascript-expression']().language.parser;
88+
89+
function createInfo(
90+
query: SavedQuery,
91+
theme: CodemirrorThemeType
92+
): {
8093
dom: Node;
8194
destroy?: () => void;
8295
} {
96+
const customHighlighter = highlightStyles[theme];
8397
const container = document.createElement('div');
84-
container.className = completionInfoStyles;
98+
8599
Object.entries(query.queryProperties).forEach(([key, value]) => {
86100
const formattedQuery = toJSString(value);
87101
const codeDiv = document.createElement('div');
@@ -92,7 +106,32 @@ function createInfo(query: SavedQuery): {
92106

93107
const code = document.createElement('pre');
94108
code.className = queryCodeStyles;
95-
if (formattedQuery) code.textContent = formattedQuery;
109+
110+
function emit(text: string, classes: string | null) {
111+
const node = document.createTextNode(text);
112+
if (classes) {
113+
const span = document.createElement('span');
114+
span.appendChild(node);
115+
span.className = classes;
116+
code.appendChild(span);
117+
} else {
118+
code.appendChild(node);
119+
}
120+
}
121+
122+
function emitBreak() {
123+
code.appendChild(document.createTextNode('\n'));
124+
}
125+
126+
if (formattedQuery) {
127+
highlightCode(
128+
formattedQuery,
129+
javascriptExpressionLanguageParser.parse(formattedQuery),
130+
customHighlighter,
131+
emit,
132+
emitBreak
133+
);
134+
}
96135

97136
codeDiv.append(label);
98137
codeDiv.appendChild(code);

packages/compass-editor/src/editor.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ const tabKeymap: KeyBinding[] = [
144144
indentWithTab,
145145
];
146146

147-
type CodemirrorThemeType = 'light' | 'dark';
147+
export type CodemirrorThemeType = 'light' | 'dark';
148148

149149
export const editorPalette = {
150150
light: {
@@ -324,6 +324,10 @@ function getStylesForTheme(theme: CodemirrorThemeType) {
324324
},
325325
'& .cm-tooltip.cm-tooltip-autocomplete > ul': {
326326
fontFamily: fontFamilies.code,
327+
boxShadow: `0 ${spacing[50]}px ${spacing[200]}px rgba(0, 0, 0, 0.25)`,
328+
},
329+
'& .cm-tooltip-autocomplete ul li': {
330+
display: 'flex',
327331
},
328332
'& .cm-tooltip-autocomplete ul li[aria-selected]': {
329333
color: editorPalette[theme].autocompleteColor,
@@ -333,16 +337,31 @@ function getStylesForTheme(theme: CodemirrorThemeType) {
333337
'& .cm-completionIcon': {
334338
display: 'none',
335339
},
340+
'& .cm-completionLabel': {
341+
flex: 1,
342+
overflow: 'hidden',
343+
textOverflow: 'ellipsis',
344+
whiteSpace: 'nowrap',
345+
},
336346
'& .cm-completionDetail': {
337347
color: rgba(editorPalette[theme].autocompleteColor, 0.5),
338348
fontStyle: 'normal',
339349
marginRight: '1em',
350+
marginLeft: '1em',
340351
},
341352
'& .cm-completionMatchedText': {
342353
color: editorPalette[theme].autocompleteMatchColor,
343354
fontWeight: 'bold',
344355
textDecoration: 'none',
345356
},
357+
'.cm-tooltip.cm-completionInfo': {
358+
boxShadow: `0 ${spacing[50]}px ${spacing[200]}px rgba(0, 0, 0, 0.25)`,
359+
overflow: 'auto',
360+
marginTop: 0,
361+
paddingTop: 0,
362+
fontSize: '12px',
363+
maxHeight: '70vh',
364+
},
346365
'& .cm-tooltip .completion-info p': {
347366
margin: 0,
348367
marginRight: `${spacing[2]}px`,
@@ -1521,3 +1540,4 @@ export { MultilineEditor as CodemirrorMultilineEditor };
15211540
export { setCodemirrorEditorValue };
15221541
export { getCodemirrorEditorValue };
15231542
export type { CompletionSource as Completer };
1543+
export { highlightStyles };

packages/compass-query-bar/src/components/option-editor.spec.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ describe('OptionEditor', function () {
204204
userEvent.click(screen.getByRole('textbox'));
205205
await waitFor(() => {
206206
expect(screen.getAllByText('{ a: 1 }')[0]).to.be.visible;
207-
expect(screen.getAllByText('{ a: 1 }')[1]).to.be.visible;
208207
expect(screen.getByText('{ a: 2 }, sort: { a: -1 }')).to.be.visible;
209208
});
210209

packages/compass-query-bar/src/components/option-editor.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
spacing,
99
SignalPopover,
1010
rafraf,
11+
useDarkMode,
1112
} from '@mongodb-js/compass-components';
1213
import type { Command, EditorRef } from '@mongodb-js/compass-editor';
1314
import {
@@ -133,6 +134,8 @@ export const OptionEditor: React.FunctionComponent<OptionEditorProps> = ({
133134
hover: true,
134135
});
135136

137+
const darkMode = useDarkMode();
138+
136139
const onApplyRef = useRef(onApply);
137140
onApplyRef.current = onApply;
138141

@@ -167,7 +170,8 @@ export const OptionEditor: React.FunctionComponent<OptionEditorProps> = ({
167170
fields: schemaFields,
168171
serverVersion,
169172
},
170-
onApplyQuery
173+
onApplyQuery,
174+
darkMode ? 'dark' : 'light'
171175
)
172176
: createQueryAutocompleter({
173177
fields: schemaFields,
@@ -179,6 +183,7 @@ export const OptionEditor: React.FunctionComponent<OptionEditorProps> = ({
179183
serverVersion,
180184
onApplyQuery,
181185
isQueryHistoryAutocompleteEnabled,
186+
darkMode,
182187
]);
183188

184189
const onFocus = () => {

0 commit comments

Comments
 (0)