Skip to content

Commit 21ecefb

Browse files
authored
Merge pull request #1 from alfasoftware/fix-debounce-timeout
Fix debounce timeout
2 parents 71eb67d + fd3efd5 commit 21ecefb

File tree

9 files changed

+98
-162
lines changed

9 files changed

+98
-162
lines changed

.github/workflows/jetbrains-release.yaml

Lines changed: 0 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,6 @@ jobs:
5353
with:
5454
ref: ${{ github.event.release.tag_name }}
5555

56-
- name: Import Apple certificate
57-
uses: apple-actions/import-codesign-certs@v3
58-
with:
59-
keychain: ${{ github.run_id }}
60-
keychain-password: ${{ github.run_id }}
61-
p12-file-base64: ${{ secrets.APPLE_CERT_DATA }}
62-
p12-password: ${{ secrets.APPLE_CERT_PASSWORD }}
63-
6456
# Validate wrapper
6557
- name: Gradle Wrapper Validation
6658
uses: gradle/actions/wrapper-validation@v3
@@ -156,50 +148,11 @@ jobs:
156148
cd ../../binary
157149
npm run build
158150
159-
# - name: Code sign darwin binaries
160-
# run: |
161-
# echo "Signing executable with keychain: ${{ github.run_id }}"
162-
# codesign --sign - ../../binary/bin/darwin-x64/continue-binary
163-
# codesign --sign - ../../binary/bin/darwin-arm64/continue-binary
164-
165-
# - name: Sign darwin-arm64 binary
166-
# uses: lando/code-sign-action@v2
167-
# with:
168-
# file: ./binary/bin/darwin-arm64/continue-binary
169-
# certificate-data: ${{ secrets.APPLE_CERT_DATA }}
170-
# certificate-password: ${{ secrets.APPLE_CERT_PASSWORD }}
171-
# apple-notary-user: ${{ secrets.APPLE_NOTARY_USER }}
172-
# apple-notary-password: ${{ secrets.APPLE_NOTARY_PASSWORD }}
173-
# apple-notary-tool: altool
174-
# apple-team-id: 43XFLY66ZD
175-
# apple-product-id: dev.continue.continue-binary
176-
# options: --options runtime --entitlements entitlements.xml
177151
178152
# Build plugin
179153
- name: Build plugin
180154
run: ./gradlew buildPlugin
181155

182-
# Publish the plugin to JetBrains Marketplace
183-
- name: Publish EAP Plugin
184-
if: github.event_name == 'release' || github.event.inputs.publish_build == 'true'
185-
env:
186-
PUBLISH_TOKEN: ${{ secrets.JETBRAINS_PUBLISH_TOKEN }}
187-
CERTIFICATE_CHAIN: ${{ secrets.JETBRAINS_CERTIFICATE_CHAIN }}
188-
PRIVATE_KEY: ${{ secrets.JETBRAINS_PRIVATE_KEY }}
189-
PRIVATE_KEY_PASSWORD: ${{ secrets.JETBRAINS_PRIVATE_KEY_PASSWORD }}
190-
RELEASE_CHANNEL: eap
191-
run: ./gradlew publishPlugin
192-
193-
- name: Publish Stable Plugin
194-
if: github.event_name == 'release' || github.event.inputs.publish_build == 'true'
195-
env:
196-
PUBLISH_TOKEN: ${{ secrets.JETBRAINS_PUBLISH_TOKEN }}
197-
CERTIFICATE_CHAIN: ${{ secrets.JETBRAINS_CERTIFICATE_CHAIN }}
198-
PRIVATE_KEY: ${{ secrets.JETBRAINS_PRIVATE_KEY }}
199-
PRIVATE_KEY_PASSWORD: ${{ secrets.JETBRAINS_PRIVATE_KEY_PASSWORD }}
200-
RELEASE_CHANNEL: default
201-
run: ./gradlew publishPlugin
202-
203156
# Prepare plugin archive content for creating artifact
204157
- name: Prepare Plugin Artifact
205158
id: artifact
@@ -474,68 +427,3 @@ jobs:
474427
name: pluginVerifier-result
475428
path: ${{ github.workspace }}/build/reports/pluginVerifier
476429

477-
upload-release:
478-
if: false
479-
name: Upload Release
480-
needs:
481-
- build
482-
- test-binaries
483-
- test
484-
runs-on: ubuntu-latest
485-
steps:
486-
# # Update Unreleased section with the current release note
487-
# - name: Patch Changelog
488-
# if: ${{ steps.properties.outputs.changelog != '' }}
489-
# env:
490-
# CHANGELOG: ${{ steps.properties.outputs.changelog }}
491-
# run: |
492-
# ./gradlew patchChangelog --release-note="$CHANGELOG"
493-
494-
- name: Download the plugin
495-
uses: actions/download-artifact@v4
496-
with:
497-
name: ${{ steps.artifact.outputs.filename }}
498-
path: ./build/distributions/
499-
500-
# Upload artifact as a release asset
501-
# - name: Upload Release Asset
502-
# env:
503-
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
504-
# run: gh release upload ${{ github.event.release.tag_name }} ./build/distributions/*
505-
506-
# Publish the plugin to JetBrains Marketplace
507-
- name: Publish Plugin
508-
env:
509-
PUBLISH_TOKEN: ${{ secrets.JETBRAINS_PUBLISH_TOKEN }}
510-
CERTIFICATE_CHAIN: ${{ secrets.JETBRAINS_CERTIFICATE_CHAIN }}
511-
PRIVATE_KEY: ${{ secrets.JETBRAINS_PRIVATE_KEY }}
512-
PRIVATE_KEY_PASSWORD: ${{ secrets.JETBRAINS_PRIVATE_KEY_PASSWORD }}
513-
run: ./gradlew publishPlugin
514-
515-
# Create a pull request
516-
- name: Create Pull Request
517-
if: ${{ steps.properties.outputs.changelog != '' }}
518-
env:
519-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
520-
run: |
521-
VERSION="${{ github.event.release.tag_name }}"
522-
BRANCH="changelog-update-$VERSION"
523-
LABEL="release changelog"
524-
525-
git config user.email "[email protected]"
526-
git config user.name "GitHub Action"
527-
528-
git checkout -b $BRANCH
529-
git commit -am "Changelog update - $VERSION"
530-
git push --set-upstream origin $BRANCH
531-
532-
gh label create "$LABEL" \
533-
--description "Pull requests with release changelog update" \
534-
--force \
535-
|| true
536-
537-
gh pr create \
538-
--title "Changelog update - \`$VERSION\`" \
539-
--body "Current pull request contains patched \`CHANGELOG.md\` file for the \`$VERSION\` version." \
540-
--label "$LABEL" \
541-
--head $BRANCH

.github/workflows/main.yaml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -230,18 +230,3 @@ jobs:
230230
pattern: "*-vsix"
231231
path: vsix-artifacts
232232
merge-multiple: true
233-
234-
# 2. Publish the extension to VS Code Marketplace
235-
- name: Publish to VS Code Marketplace
236-
run: |
237-
cd extensions/vscode
238-
npx vsce publish --packagePath ../../vsix-artifacts/*.vsix
239-
env:
240-
VSCE_PAT: ${{ secrets.VSCE_TOKEN }}
241-
242-
# 3. Publish the extension to Open VSX Registry
243-
- name: Publish (Open VSX Registry)
244-
continue-on-error: true
245-
run: |
246-
cd extensions/vscode
247-
npx ovsx publish -p ${{ secrets.VSX_REGISTRY_TOKEN }} --packagePath ../../vsix-artifacts/*.vsix

core/autocomplete/filtering/streamTransforms/StreamTransformPipeline.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { streamLines } from "../../../diff/util";
2-
import { PosthogFeatureFlag, Telemetry } from "../../../util/posthog";
32
import { HelperVars } from "../../util/HelperVars";
43

54
import { stopAtStartOf, stopAtStopTokens } from "./charStream";
@@ -73,10 +72,7 @@ export class StreamTransformPipeline {
7372
fullStop,
7473
);
7574

76-
const timeoutValue = await Telemetry.getValueForFeatureFlag(
77-
PosthogFeatureFlag.AutocompleteTimeout,
78-
);
79-
75+
const timeoutValue = helper.options.modelTimeout;
8076
lineGenerator = showWhateverWeHaveAtXMs(lineGenerator, timeoutValue!);
8177

8278
const finalGenerator = streamWithNewLines(lineGenerator);
Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
import { v4 as uuidv4 } from "uuid";
2+
23
export class AutocompleteDebouncer {
34
private debounceTimeout: NodeJS.Timeout | undefined = undefined;
4-
private debouncing = false;
5-
private lastUUID: string | undefined = undefined;
5+
private currentRequestId: string | undefined = undefined;
66

77
async delayAndShouldDebounce(debounceDelay: number): Promise<boolean> {
8-
// Debounce
9-
const uuid = uuidv4();
10-
this.lastUUID = uuid;
11-
12-
// Debounce
13-
if (this.debouncing) {
14-
this.debounceTimeout?.refresh();
15-
const lastUUID = await new Promise((resolve) =>
16-
setTimeout(() => {
17-
resolve(this.lastUUID);
18-
}, debounceDelay),
19-
);
20-
if (uuid !== lastUUID) {
21-
return true;
22-
}
23-
} else {
24-
this.debouncing = true;
25-
this.debounceTimeout = setTimeout(async () => {
26-
this.debouncing = false;
27-
}, debounceDelay);
8+
// Generate a unique ID for this request
9+
const requestId = uuidv4();
10+
this.currentRequestId = requestId;
11+
12+
// Clear any existing timeout
13+
if (this.debounceTimeout) {
14+
clearTimeout(this.debounceTimeout);
2815
}
29-
30-
return false;
16+
17+
// Create a new promise that resolves after the debounce delay
18+
return new Promise<boolean>((resolve) => {
19+
this.debounceTimeout = setTimeout(() => {
20+
// When the timeout completes, check if this is still the most recent request
21+
const shouldDebounce = this.currentRequestId !== requestId;
22+
23+
// If this is the most recent request, it shouldn't be debounced
24+
if (!shouldDebounce) {
25+
this.currentRequestId = undefined;
26+
}
27+
28+
resolve(shouldDebounce);
29+
}, debounceDelay);
30+
});
3131
}
3232
}

core/config/sharedConfig.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export const sharedConfigSchema = z
3131
useAutocompleteCache: z.boolean(),
3232
useAutocompleteMultilineCompletions: z.enum(["always", "never", "auto"]),
3333
disableAutocompleteInFiles: z.array(z.string()),
34+
modelTimeout: z.number(),
35+
debounceDelay: z.number(),
3436
})
3537
.partial();
3638

@@ -103,6 +105,14 @@ export function modifyAnyConfigWithSharedConfig<
103105
configCopy.tabAutocompleteOptions.disableInFiles =
104106
sharedConfig.disableAutocompleteInFiles;
105107
}
108+
if (sharedConfig.modelTimeout !== undefined) {
109+
configCopy.tabAutocompleteOptions.modelTimeout =
110+
sharedConfig.modelTimeout;
111+
}
112+
if (sharedConfig.debounceDelay !== undefined) {
113+
configCopy.tabAutocompleteOptions.debounceDelay =
114+
sharedConfig.debounceDelay;
115+
}
106116

107117
configCopy.ui = {
108118
...configCopy.ui,

core/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,7 @@ export interface TabAutocompleteOptions {
983983
disable: boolean;
984984
maxPromptTokens: number;
985985
debounceDelay: number;
986+
modelTimeout: number;
986987
maxSuffixPercentage: number;
987988
prefixPercentage: number;
988989
transform?: boolean;

core/util/parameters.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const DEFAULT_AUTOCOMPLETE_OPTS: TabAutocompleteOptions = {
66
prefixPercentage: 0.3,
77
maxSuffixPercentage: 0.2,
88
debounceDelay: 350,
9+
modelTimeout: 150,
910
multilineCompletions: "auto",
1011
// @deprecated TO BE REMOVED
1112
slidingWindowPrefixPercentage: 0.75,

gui/src/components/gui/NumberInput.tsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { useState } from "react";
22

33
interface NumberInputProps {
44
value: number;
@@ -13,24 +13,50 @@ const NumberInput: React.FC<NumberInputProps> = ({
1313
max,
1414
min,
1515
}) => {
16+
const [inputValue, setInputValue] = useState(value.toString());
17+
1618
const handleIncrement = () => {
1719
if (value < max) {
18-
onChange(value + 1);
20+
const newValue = value + 1;
21+
onChange(newValue);
22+
setInputValue(newValue.toString());
1923
}
2024
};
2125

2226
const handleDecrement = () => {
2327
if (value > min) {
24-
onChange(value - 1);
28+
const newValue = value - 1;
29+
onChange(newValue);
30+
setInputValue(newValue.toString());
31+
}
32+
};
33+
34+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
35+
const newInputValue = e.target.value;
36+
setInputValue(newInputValue);
37+
38+
// Only update the actual value if it's a valid number
39+
const numValue = parseInt(newInputValue, 10);
40+
if (!isNaN(numValue)) {
41+
// Apply min/max constraints
42+
const constrainedValue = Math.min(Math.max(numValue, min), max);
43+
onChange(constrainedValue);
2544
}
2645
};
2746

47+
const handleBlur = () => {
48+
// When input loses focus, ensure the displayed value matches the actual value
49+
// This handles cases where the user entered an invalid value
50+
setInputValue(value.toString());
51+
};
52+
2853
return (
2954
<div className="border-vsc-input-border bg-vsc-input-background flex flex-row overflow-hidden rounded-md border border-solid">
3055
<input
3156
type="text"
32-
value={value}
33-
readOnly
57+
value={inputValue}
58+
onChange={handleInputChange}
59+
onBlur={handleBlur}
3460
className="text-vsc-foreground max-w-7 border-none bg-inherit pr-1.5 text-right outline-none ring-0 focus:border-none focus:outline-none focus:ring-0"
3561
style={{
3662
appearance: "none",

gui/src/pages/config/index.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ function ConfigPage() {
130130
const codeBlockToolbarPosition = config.ui?.codeBlockToolbarPosition ?? "top";
131131
const useAutocompleteMultilineCompletions =
132132
config.tabAutocompleteOptions?.multilineCompletions ?? "auto";
133+
const modelTimeout =
134+
config.tabAutocompleteOptions?.modelTimeout ?? 150;
135+
const debounceDelay =
136+
config.tabAutocompleteOptions?.debounceDelay ?? 250;
133137
const fontSize = getFontSize();
134138

135139
// Disable autocomplete
@@ -513,7 +517,32 @@ function ConfigPage() {
513517
<option value="never">Never</option>
514518
</Select>
515519
</label>
516-
520+
<label className="flex items-center justify-between gap-3">
521+
<span className="text-left">Model Timeout (ms)</span>
522+
<NumberInput
523+
value={modelTimeout}
524+
onChange={(val) =>
525+
handleUpdate({
526+
modelTimeout: val,
527+
})
528+
}
529+
min={100}
530+
max={5000}
531+
/>
532+
</label>
533+
<label className="flex items-center justify-between gap-3">
534+
<span className="text-left">Model Debounce (ms)</span>
535+
<NumberInput
536+
value={debounceDelay}
537+
onChange={(val) =>
538+
handleUpdate({
539+
debounceDelay: val,
540+
})
541+
}
542+
min={0}
543+
max={2500}
544+
/>
545+
</label>
517546
<label className="flex items-center justify-between gap-3">
518547
<span className="text-left">Font Size</span>
519548
<NumberInput

0 commit comments

Comments
 (0)