Skip to content

Commit 22d153f

Browse files
kevinzluCopybara Bot
andauthored
Bring to head of internal repo (#69)
* Project import generated by Copybara. GitOrigin-RevId: d654d56312896b6b8b1204c6b6c9d029e09fb9ea * accidental delete * move --------- Co-authored-by: Copybara Bot <copybara@exafunction.com>
1 parent 662b746 commit 22d153f

File tree

11 files changed

+196
-126
lines changed

11 files changed

+196
-126
lines changed

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
[![Twitter Follow](https://img.shields.io/badge/style--blue?style=social&logo=twitter&label=Follow%20%40codeiumdev)](https://twitter.com/intent/follow?screen_name=codeiumdev)
99
![License](https://img.shields.io/github/license/Exafunction/codeium-chrome)
1010
[![Docs](https://img.shields.io/badge/Codeium%20Docs-09B6A2)](https://docs.codeium.com)
11-
[![Canny Board](https://img.shields.io/badge/Feature%20Requests-6b69ff)](https://codeium.canny.io/feature-requests/)
12-
[![built with Codeium](https://codeium.com/badges/main)](https://codeium.com?repo_name=exafunction%2Fcodeium.vim)
1311

1412
[![Visual Studio](https://img.shields.io/visual-studio-marketplace/i/Codeium.codeium?label=Visual%20Studio&logo=visualstudio)](https://marketplace.visualstudio.com/items?itemName=Codeium.codeium)
1513
[![JetBrains](https://img.shields.io/jetbrains/plugin/d/20540?label=JetBrains)](https://plugins.jetbrains.com/plugin/20540-codeium/)

buf.gen.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
version: v2
22
plugins:
3-
- local: node_modules/@bufbuild/protoc-gen-es/bin/protoc-gen-es
3+
- local: [node, node_modules/@bufbuild/protoc-gen-es/bin/protoc-gen-es]
44
out: proto
55
opt:
66
- target=ts
77
- import_extension=none
8-
- local: node_modules/@connectrpc/protoc-gen-connect-es/bin/protoc-gen-connect-es
8+
- local: [node, node_modules/@connectrpc/protoc-gen-connect-es/bin/protoc-gen-connect-es]
99
out: proto
1010
opt:
1111
- target=ts

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codeium-chrome",
3-
"version": "1.20.4",
3+
"version": "1.26.3",
44
"description": "",
55
"license": "MIT",
66
"scripts": {

src/codemirror.ts

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IDisposable } from '@lumino/disposable';
22
import type CodeMirror from 'codemirror';
33

44
import { editorLanguage, language } from './codemirrorLanguages';
5-
import { CODEIUM_DEBUG, IdeInfo, LanguageServerClient } from './common';
5+
import { CODEIUM_DEBUG, IdeInfo, KeyCombination, LanguageServerClient } from './common';
66
import { TextAndOffsets, computeTextAndOffsets } from './notebook';
77
import { numUtf8BytesToNumCodeUnits } from './utf';
88
import { EditorOptions } from '../proto/exa/codeium_common_pb/codeium_common_pb';
@@ -308,48 +308,61 @@ export class CodeMirrorManager {
308308
doc: CodeMirror.Doc,
309309
event: KeyboardEvent,
310310
alsoHandle: { tab: boolean; escape: boolean },
311-
tabKey: string = 'Tab'
311+
keyCombination?: KeyCombination
312312
): { consumeEvent: boolean | undefined; forceTriggerCompletion: boolean } {
313313
let forceTriggerCompletion = false;
314-
if (event.ctrlKey) {
315-
if (event.key === ' ') {
316-
forceTriggerCompletion = true;
317-
} else {
318-
return { consumeEvent: false, forceTriggerCompletion };
319-
}
314+
if (event.ctrlKey && event.key === ' ') {
315+
forceTriggerCompletion = true;
320316
}
317+
321318
// Classic notebook may autocomplete these.
322319
if ('"\')}]'.includes(event.key)) {
323320
forceTriggerCompletion = true;
324321
}
322+
325323
if (event.isComposing) {
326324
this.clearCompletion('composing');
327325
return { consumeEvent: false, forceTriggerCompletion };
328326
}
329-
// Shift-tab in jupyter notebooks shows documentation.
330-
if (event.key === 'Tab' && event.shiftKey) {
331-
return { consumeEvent: false, forceTriggerCompletion };
327+
328+
// Accept completion logic
329+
if (keyCombination) {
330+
const matchesKeyCombination =
331+
event.key.toLowerCase() === keyCombination.key.toLowerCase() &&
332+
!!event.ctrlKey === !!keyCombination.ctrl &&
333+
!!event.altKey === !!keyCombination.alt &&
334+
!!event.shiftKey === !!keyCombination.shift &&
335+
!!event.metaKey === !!keyCombination.meta;
336+
337+
if (matchesKeyCombination && this.acceptCompletion()) {
338+
return { consumeEvent: true, forceTriggerCompletion };
339+
}
332340
}
333341

334342
// TODO(kevin): clean up autoHandle logic
335343
// Currently we have:
336344
// Jupyter Notebook: tab = true, escape = false
337345
// Code Mirror Websites: tab = true, escape = true
338346
// Jupyter Lab: tab = false, escape = false
339-
if (!event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey) {
340-
if (alsoHandle.tab && event.key === tabKey && this.acceptCompletion()) {
341-
return { consumeEvent: true, forceTriggerCompletion };
342-
}
343-
if (alsoHandle.escape && event.key === 'Escape' && this.clearCompletion('user dismissed')) {
344-
return { consumeEvent: true, forceTriggerCompletion };
345-
}
346-
// Special case if we are in jupyter notebooks and the tab key has been rebinded.
347-
// We do not want to consume the default keybinding, because it triggers the default
348-
// jupyter completion.
349-
if (alsoHandle.tab && !alsoHandle.escape && tabKey !== 'Tab' && event.key === 'Tab') {
350-
return { consumeEvent: false, forceTriggerCompletion };
351-
}
347+
348+
// Code Mirror Websites only
349+
if (
350+
!event.metaKey &&
351+
!event.ctrlKey &&
352+
!event.altKey &&
353+
!event.shiftKey &&
354+
alsoHandle.escape &&
355+
event.key === 'Escape' &&
356+
this.clearCompletion('user dismissed')
357+
) {
358+
return { consumeEvent: true, forceTriggerCompletion };
352359
}
360+
361+
// Shift-tab in jupyter notebooks shows documentation.
362+
if (event.key === 'Tab' && event.shiftKey) {
363+
return { consumeEvent: false, forceTriggerCompletion };
364+
}
365+
353366
switch (event.key) {
354367
case 'Delete':
355368
case 'ArrowDown':

src/codemirrorInject.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ export class CodeMirrorState {
5454
const { consumeEvent, forceTriggerCompletion } = this.codeMirrorManager.beforeMainKeyHandler(
5555
editor.getDoc(),
5656
event,
57-
{ tab: true, escape: true }
57+
{ tab: true, escape: true },
58+
{ key: 'Tab', ctrl: false, alt: false, shift: false, meta: false }
5859
);
5960
if (consumeEvent !== undefined) {
6061
if (consumeEvent) {

src/common.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '../proto/exa/language_server_pb/language_server_pb';
1414

1515
const EXTENSION_NAME = 'chrome';
16-
const EXTENSION_VERSION = '1.20.4';
16+
const EXTENSION_VERSION = '1.26.3';
1717

1818
export const CODEIUM_DEBUG = false;
1919
export const DEFAULT_PATH = 'unknown_url';
@@ -23,13 +23,21 @@ export interface ClientSettings {
2323
defaultModel?: string;
2424
}
2525

26+
export interface KeyCombination {
27+
key: string;
28+
ctrl?: boolean;
29+
alt?: boolean;
30+
shift?: boolean;
31+
meta?: boolean;
32+
}
33+
2634
export interface JupyterLabKeyBindings {
27-
accept: string;
28-
dismiss: string;
35+
accept: KeyCombination;
36+
dismiss: KeyCombination;
2937
}
3038

3139
export interface JupyterNotebookKeyBindings {
32-
accept: string;
40+
accept: KeyCombination;
3341
}
3442

3543
async function getClientSettings(): Promise<ClientSettings> {

src/component/Options.tsx

Lines changed: 102 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,52 @@ const Options = () => {
148148
const [portalUrlText, setPortalUrlText] = useState('');
149149
const modelRef = createRef<HTMLInputElement>();
150150
const [modelText, setModelText] = useState('');
151-
const jupyterlabKeybindingAcceptRef = createRef<HTMLInputElement>();
152151
const [jupyterlabKeybindingAcceptText, setJupyterlabKeybindingAcceptText] = useState('');
153-
const jupyterlabKeybindingDismissRef = createRef<HTMLInputElement>();
154152
const [jupyterlabKeybindingDismissText, setJupyterlabKeybindingDismissText] = useState('');
155-
const jupyterNotebookKeybindingAcceptRef = createRef<HTMLInputElement>();
156153
const [jupyterNotebookKeybindingAcceptText, setJupyterNotebookKeybindingAcceptText] =
157154
useState('');
158155
const [jupyterDebounceMs, setJupyterDebounceMs] = useState(0);
159156
const jupyterDebounceMsRef = createRef<HTMLInputElement>();
157+
const [currentKey, setCurrentKey] = useState({
158+
key: '',
159+
ctrl: false,
160+
alt: false,
161+
shift: false,
162+
meta: false,
163+
});
164+
const [jupyterlabAcceptInput, setJupyterlabAcceptInput] = useState(false);
165+
const [jupyterlabDismissInput, setJupyterlabDismissInput] = useState(false);
166+
const [notebookAcceptInput, setNotebookAcceptInput] = useState(false);
167+
168+
const formatKeyCombination = (key: any) => {
169+
const modifiers = [];
170+
if (key.ctrl) modifiers.push('Ctrl');
171+
if (key.alt) modifiers.push('Alt');
172+
if (key.shift) modifiers.push('Shift');
173+
if (key.meta) modifiers.push('Meta');
174+
return [...modifiers, key.key.toUpperCase()].join('+');
175+
};
176+
177+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
178+
e.preventDefault();
179+
const key = e.key;
180+
if (key !== 'Control' && key !== 'Alt' && key !== 'Shift' && key !== 'Meta') {
181+
const ctrl = e.ctrlKey;
182+
const alt = e.altKey;
183+
const shift = e.shiftKey;
184+
const meta = e.metaKey;
185+
setCurrentKey({ key, ctrl, alt, shift, meta });
186+
187+
// Force blur using setTimeout to ensure it happens after state update
188+
setTimeout(() => {
189+
if (e.currentTarget) {
190+
e.currentTarget.blur();
191+
// Also try to remove focus from the document
192+
(document.activeElement as HTMLElement)?.blur();
193+
}
194+
}, 0);
195+
}
196+
};
160197

161198
useEffect(() => {
162199
(async () => {
@@ -203,6 +240,7 @@ const Options = () => {
203240
}
204241
return PUBLIC_WEBSITE;
205242
}, []);
243+
206244
return (
207245
<Box sx={{ width: '100%', maxWidth: 400, bgcolor: 'background.paper' }}>
208246
{!CODEIUM_ENTERPRISE && (
@@ -328,95 +366,84 @@ const Options = () => {
328366
}}
329367
/>
330368
<Box sx={{ my: 2, mx: 2 }}>
331-
<Typography variant="h6"> Jupyterlab settings </Typography>
369+
<Typography variant="h6"> Jupyter Settings </Typography>
332370
<Typography variant="body2">
333-
A single keystroke is supported. The syntax is described{' '}
334-
<Link
335-
href="https://github.com/jupyterlab/lumino/blob/f85aad4903504c942fc202c57270e707f1ab87c1/packages/commands/src/index.ts#L1061-L1083"
336-
target="_blank"
337-
>
338-
here
339-
<OpenInNewIcon
340-
fontSize="small"
341-
sx={{
342-
verticalAlign: 'bottom',
343-
}}
344-
/>
345-
</Link>
346-
.
371+
Press the desired key combination in the input field. For example, press "Ctrl+Tab" for a
372+
Ctrl+Tab shortcut.
373+
</Typography>
374+
375+
<Typography variant="subtitle1" sx={{ mt: 2, mb: 1 }}>
376+
JupyterLab
347377
</Typography>
348378
<TextField
349379
id="jupyterlabKeybindingAccept"
350-
label="Accept key binding"
380+
label="Accept Shortcut"
351381
variant="standard"
352382
fullWidth
353-
inputRef={jupyterlabKeybindingAcceptRef}
354-
value={jupyterlabKeybindingAcceptText}
355-
onChange={(e) => setJupyterlabKeybindingAcceptText(e.target.value)}
383+
value={jupyterlabAcceptInput ? 'Press keys...' : jupyterlabKeybindingAcceptText || 'Tab'}
384+
onFocus={() => setJupyterlabAcceptInput(true)}
385+
onBlur={async () => {
386+
setJupyterlabAcceptInput(false);
387+
if (currentKey.key) {
388+
const formatted = formatKeyCombination(currentKey);
389+
setJupyterlabKeybindingAcceptText(formatted);
390+
await setStorageItem('jupyterlabKeybindingAccept', formatted);
391+
setCurrentKey({ key: '', ctrl: false, alt: false, shift: false, meta: false });
392+
}
393+
}}
394+
onKeyDown={handleKeyDown}
356395
/>
357-
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
358-
<Button
359-
variant="text"
360-
onClick={async () => {
361-
const keybinding = jupyterlabKeybindingAcceptRef.current?.value;
362-
await setStorageItem('jupyterlabKeybindingAccept', keybinding);
363-
}}
364-
sx={{ textTransform: 'none' }}
365-
>
366-
Enter Keybinding <LoginIcon />
367-
</Button>
368-
</Box>
369396
<TextField
370397
id="jupyterlabKeybindingDismiss"
371-
label="Dismiss key binding"
398+
label="Dismiss Shortcut"
372399
variant="standard"
373400
fullWidth
374-
inputRef={jupyterlabKeybindingDismissRef}
375-
value={jupyterlabKeybindingDismissText}
376-
onChange={(e) => setJupyterlabKeybindingDismissText(e.target.value)}
401+
value={
402+
jupyterlabDismissInput ? 'Press keys...' : jupyterlabKeybindingDismissText || 'Escape'
403+
}
404+
onFocus={() => setJupyterlabDismissInput(true)}
405+
onBlur={async () => {
406+
setJupyterlabDismissInput(false);
407+
if (currentKey.key) {
408+
const formatted = formatKeyCombination(currentKey);
409+
setJupyterlabKeybindingDismissText(formatted);
410+
await setStorageItem('jupyterlabKeybindingDismiss', formatted);
411+
setCurrentKey({ key: '', ctrl: false, alt: false, shift: false, meta: false });
412+
}
413+
}}
414+
onKeyDown={handleKeyDown}
377415
/>
378-
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
379-
<Button
380-
variant="text"
381-
onClick={async () => {
382-
const keybinding = jupyterlabKeybindingDismissRef.current?.value;
383-
await setStorageItem('jupyterlabKeybindingDismiss', keybinding);
384-
}}
385-
sx={{ textTransform: 'none' }}
386-
>
387-
Enter Keybinding <LoginIcon />
388-
</Button>
389-
</Box>
390-
</Box>
391-
<Box sx={{ my: 2, mx: 2 }}>
392-
<Typography variant="h6"> Jupyter Notebook settings </Typography>
416+
417+
<Typography variant="subtitle1" sx={{ mt: 2, mb: 1 }}>
418+
Jupyter Notebook
419+
</Typography>
393420
<TextField
394421
id="jupyterNotebookKeybindingAccept"
395-
label="Accept key binding"
422+
label="Accept Shortcut"
396423
variant="standard"
397424
fullWidth
398-
inputRef={jupyterNotebookKeybindingAcceptRef}
399-
value={jupyterNotebookKeybindingAcceptText}
400-
onChange={(e) => setJupyterNotebookKeybindingAcceptText(e.target.value)}
425+
value={
426+
notebookAcceptInput ? 'Press keys...' : jupyterNotebookKeybindingAcceptText || 'Tab'
427+
}
428+
onFocus={() => setNotebookAcceptInput(true)}
429+
onBlur={async () => {
430+
setNotebookAcceptInput(false);
431+
if (currentKey.key) {
432+
const formatted = formatKeyCombination(currentKey);
433+
setJupyterNotebookKeybindingAcceptText(formatted);
434+
await setStorageItem('jupyterNotebookKeybindingAccept', formatted);
435+
setCurrentKey({ key: '', ctrl: false, alt: false, shift: false, meta: false });
436+
}
437+
}}
438+
onKeyDown={handleKeyDown}
401439
/>
402-
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
403-
<Button
404-
variant="text"
405-
onClick={async () => {
406-
const keybinding = jupyterNotebookKeybindingAcceptRef.current?.value;
407-
await setStorageItem('jupyterNotebookKeybindingAccept', keybinding);
408-
}}
409-
sx={{ textTransform: 'none' }}
410-
>
411-
Enter Keybinding <LoginIcon />
412-
</Button>
413-
</Box>
414-
</Box>
415-
<Box sx={{ my: 2, mx: 2 }}>
416-
<Typography variant="h6"> Jupyter debounce time </Typography>
440+
441+
<Typography variant="subtitle1" sx={{ mt: 2, mb: 1 }}>
442+
Performance
443+
</Typography>
417444
<TextField
418445
id="jupyterDebounceMs"
419-
label="Debounce time (ms)"
446+
label="Debounce (ms)"
420447
variant="standard"
421448
fullWidth
422449
type="number"
@@ -428,8 +455,8 @@ const Options = () => {
428455
<Button
429456
variant="text"
430457
onClick={async () => {
431-
const debounceTime = Number(jupyterDebounceMsRef.current?.value);
432-
await setStorageItem('jupyterDebounceMs', debounceTime);
458+
const debounceMs = parseInt(jupyterDebounceMsRef.current?.value ?? '0');
459+
await setStorageItem('jupyterDebounceMs', debounceMs);
433460
}}
434461
sx={{ textTransform: 'none' }}
435462
>

0 commit comments

Comments
 (0)