Skip to content

Commit

Permalink
Merge pull request #1 from justinmilner1/Add_Ctrl+backspace
Browse files Browse the repository at this point in the history
  • Loading branch information
Iamshankhadeep committed Mar 31, 2024
2 parents d9a6691 + f78638b commit 0af2e27
Show file tree
Hide file tree
Showing 82 changed files with 1,945 additions and 586 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ nvm use
1. The new VS Code window with the extension is referred to as the _Host VS Code_
2. The window you started debugging from is referred to as the _Main VS Code_

4. To package the extension, run `npm run package` in the `extensions/vscode` directory. This will generate `extensions/vscode/build/continue-patch.vsix`, which you can install by right-clicking and selecting "Install Extension VSIX".

> Note: Breakpoints can be used in both the `core` and `extensions/vscode` folders while debugging, but are not currently supported inside of `gui` code. Hot-reloading is enabled with Vite, so if you make any changes to the `gui`, they should be automatically reflected without rebuilding. Similarly, any changes to `core` or `extensions/vscode` will be automatically included by just reloading the _Host VS Code_ window with cmd/ctrl+shift+p "Reload Window".
#### JetBrains
Expand Down
43 changes: 23 additions & 20 deletions core/autocomplete/completionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { AutocompleteLanguageInfo } from "./languages";
import {
avoidPathLine,
noTopLevelKeywordsMidline,
stopAtLines,
stopAtRepeatingLines,
stopAtSimilarLine,
Expand Down Expand Up @@ -53,7 +54,7 @@ const autocompleteCache = AutocompleteLruCache.get();
function formatExternalSnippet(
filepath: string,
snippet: string,
language: AutocompleteLanguageInfo
language: AutocompleteLanguageInfo,
) {
const comment = language.comment;
const lines = [
Expand All @@ -78,8 +79,8 @@ export async function getTabCompletion(
filepath: string,
contents: string,
cursorIndex: number,
ide: IDE
) => Promise<AutocompleteSnippet[]>
ide: IDE,
) => Promise<AutocompleteSnippet[]>,
): Promise<AutocompleteOutcome | undefined> {
const startTime = Date.now();

Expand Down Expand Up @@ -129,7 +130,7 @@ export async function getTabCompletion(
filepath,
fullPrefix + fullSuffix,
fullPrefix.length,
ide
ide,
),
new Promise((resolve) => {
setTimeout(() => resolve([]), 100);
Expand All @@ -155,7 +156,7 @@ export async function getTabCompletion(
recentlyEditedRanges,
recentlyEditedFiles,
llm.model,
extrasSnippets
extrasSnippets,
);

// Template prompt
Expand All @@ -172,7 +173,7 @@ export async function getTabCompletion(
// Format snippets as comments and prepend to prefix
const formattedSnippets = snippets
.map((snippet) =>
formatExternalSnippet(snippet.filepath, snippet.contents, lang)
formatExternalSnippet(snippet.filepath, snippet.contents, lang),
)
.join("\n");
if (formattedSnippets.length > 0) {
Expand Down Expand Up @@ -207,7 +208,10 @@ export async function getTabCompletion(
let stop = [
...(completionOptions?.stop || []),
"\n\n",
// The following are commonly appended to completions by starcoder and other models
"/src/",
".t.",
"#- coding: utf-8",
"```",
...lang.stopWords,
];
Expand All @@ -224,7 +228,7 @@ export async function getTabCompletion(
raw: true,
stop,
}),
multiline
multiline,
);

// LLM
Expand All @@ -241,14 +245,13 @@ export async function getTabCompletion(
let chars = generatorWithCancellation();
const gen2 = onlyWhitespaceAfterEndOfLine(
noFirstCharNewline(chars),
lang.endOfLine
);
const lineGenerator = streamWithNewLines(
avoidPathLine(
stopAtRepeatingLines(stopAtLines(streamLines(gen2))),
lang.comment
)
lang.endOfLine,
);
let lineGenerator = stopAtRepeatingLines(stopAtLines(streamLines(gen2)));
lineGenerator = avoidPathLine(lineGenerator, lang.comment);
lineGenerator = noTopLevelKeywordsMidline(lineGenerator, lang.stopWords);
lineGenerator = streamWithNewLines(lineGenerator);

const finalGenerator = stopAtSimilarLine(lineGenerator, lineBelowCursor);
for await (const update of finalGenerator) {
completion += update;
Expand Down Expand Up @@ -293,11 +296,11 @@ export class CompletionProvider {
filepath: string,
contents: string,
cursorIndex: number,
ide: IDE
) => Promise<AutocompleteSnippet[]>
ide: IDE,
) => Promise<AutocompleteSnippet[]>,
) {
this.generatorReuseManager = new GeneratorReuseManager(
this.onError.bind(this)
this.onError.bind(this),
);
}

Expand Down Expand Up @@ -347,7 +350,7 @@ export class CompletionProvider {

public async provideInlineCompletionItems(
input: AutocompleteInput,
token: AbortSignal | undefined
token: AbortSignal | undefined,
): Promise<AutocompleteOutcome | undefined> {
// Create abort signal if not given
if (!token) {
Expand All @@ -372,7 +375,7 @@ export class CompletionProvider {
const lastUUID = await new Promise((resolve) =>
setTimeout(() => {
resolve(CompletionProvider.lastUUID);
}, options.debounceDelay)
}, options.debounceDelay),
);
if (uuid !== lastUUID) {
return undefined;
Expand Down Expand Up @@ -402,7 +405,7 @@ export class CompletionProvider {
this.ide,
this.generatorReuseManager,
input,
this.getDefinitionsFromLsp
this.getDefinitionsFromLsp,
);
const completion = outcome?.completion;

Expand Down
26 changes: 23 additions & 3 deletions core/autocomplete/lineStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ import { distance } from "fastest-levenshtein";
import { DiffLine } from "..";
import { LineStream } from "../diff/util";

export async function* noTopLevelKeywordsMidline(
lines: LineStream,
topLevelKeywords: string[],
): LineStream {
for await (const line of lines) {
for (const keyword of topLevelKeywords) {
const indexOf = line.indexOf(keyword + " ");
if (indexOf >= 0 && line.slice(indexOf - 1, indexOf).trim() !== "") {
yield line.slice(0, indexOf);
break;
}
}
yield line;
}
}

export async function* avoidPathLine(
stream: LineStream,
comment: string,
Expand Down Expand Up @@ -54,11 +70,11 @@ export async function* stopAtSimilarLine(
}
}

const LINES_TO_STOP_AT = ["# End of file."];
const LINES_TO_STOP_AT = ["# End of file.", "<STOP EDITING HERE"];

export async function* stopAtLines(stream: LineStream): LineStream {
for await (const line of stream) {
if (LINES_TO_STOP_AT.includes(line)) {
if (LINES_TO_STOP_AT.some((stopAt) => line.trim().includes(stopAt))) {
break;
}
yield line;
Expand All @@ -69,7 +85,8 @@ function shouldRemoveLineBeforeStart(line: string): boolean {
return (
line.trimStart().startsWith("```") ||
line.trim() === "[CODE]" ||
line.trim() === ""
line.trim() === "" ||
line.trim() === "<START EDITING HERE>"
);
}

Expand Down Expand Up @@ -142,6 +159,9 @@ export async function* filterEnglishLinesAtStart(lines: LineStream) {
let i = 0;
let wasEnglishFirstLine = false;
for await (let line of lines) {
if (i === 0 && line.trim() === "") {
continue;
}
if (i === 0) {
if (isEnglishFirstLine(line)) {
wasEnglishFirstLine = true;
Expand Down
2 changes: 1 addition & 1 deletion core/autocomplete/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function getTemplateForModel(model: string): AutocompleteTemplate {
return deepseekFimTemplate;
}

if (lowerCaseModel.includes("gpt")) {
if (lowerCaseModel.includes("gpt") || lowerCaseModel.includes("davinci-002")) {
return gptAutocompleteTemplate;
}

Expand Down
4 changes: 2 additions & 2 deletions core/commands/slash/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from "../../autocomplete/lineStream";
import { streamLines } from "../../diff/util";
import { stripImages } from "../../llm/countTokens";
import { dedentAndGetCommonWhitespace, renderPromptTemplate } from "../../util";
import { dedentAndGetCommonWhitespace } from "../../util";
import {
RangeInFileWithContents,
contextItemToRangeInFileWithContents,
Expand Down Expand Up @@ -467,7 +467,7 @@ const EditSlashCommand: SlashCommand = {
const template = llm.promptTemplates?.edit;
let generator: AsyncGenerator<string>;
if (template) {
let rendered = renderPromptTemplate(
let rendered = llm.renderPromptTemplate(
template,
// typeof template === 'string' ? template : template.prompt,
messages.slice(0, messages.length - 1),
Expand Down
28 changes: 28 additions & 0 deletions core/continueServer/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Chunk } from "..";

export interface EmbeddingsCacheChunk {
vector: number[];
startLine: number;
endLine: number;
contents: string;
}

interface ArtifactReturnTypes {
chunks: Chunk[];
embeddings: EmbeddingsCacheChunk[];
}

export type ArtifactType = keyof ArtifactReturnTypes;

export interface EmbeddingsCacheResponse<T extends ArtifactType> {
files: { [cacheKey: string]: ArtifactReturnTypes[T] };
}

export interface IContinueServerClient {
getConfig(): Promise<{ configJson: string; configJs: string }>;
getFromIndexCache<T extends ArtifactType>(
keys: string[],
artifactId: T,
repoName: string | undefined,
): Promise<EmbeddingsCacheResponse<T>>;
}
24 changes: 24 additions & 0 deletions core/continueServer/stubs/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
ArtifactType,
EmbeddingsCacheResponse,
IContinueServerClient,
} from "../interface";

export class ContinueServerClient implements IContinueServerClient {
constructor(
private readonly serverUrl: string,
private readonly userToken: Promise<string | undefined>,
) {}

public async getConfig(): Promise<{ configJson: string; configJs: string }> {
throw new Error("Not Implemented");
}

public async getFromIndexCache<T extends ArtifactType>(
keys: string[],
artifactId: T,
repoName: string | undefined,
): Promise<EmbeddingsCacheResponse<T>> {
return { files: {} };
}
}
2 changes: 2 additions & 0 deletions core/diff/streamDiff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export async function* streamDiff(
newLines: LineStream,
): AsyncGenerator<DiffLine> {
const mutatedOldLines = [...oldLines]; // be careful

// If one indentation mistake is made, others are likely. So we are more permissive about matching
let seenIndentationMistake = false;

let newLineResult = await newLines.next();
Expand Down
21 changes: 17 additions & 4 deletions core/diff/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@ function linesMatchPerfectly(lineA: string, lineB: string): boolean {
return lineA === lineB && lineA !== "";
}

function linesMatch(lineA: string, lineB: string): boolean {
const END_BRACKETS = ["}", "});", "})"];

function linesMatch(
lineA: string,
lineB: string,
linesBetween: number = 0,
): boolean {
// Require a perfect (without padding) match for these lines
// Otherwise they are edit distance 1 from empty lines and other single char lines (e.g. each other)
if (["}", "*"].includes(lineA.trim())) {
if (["}", "*", "});", "})"].includes(lineA.trim())) {
return lineA.trim() === lineB.trim();
}

const d = distance(lineA, lineB);
return (
(d / Math.max(lineA.length, lineB.length) < 0.5 ||
// Should be more unlikely for lines to fuzzy match if they are further away
(d / Math.max(lineA.length, lineB.length) < 0.5 - linesBetween * 0.05 ||
lineA.trim() === lineB.trim()) &&
lineA.trim() !== ""
);
Expand All @@ -37,10 +44,16 @@ export function matchLine(
return [0, true, newLine.trim()];
}

const isEndBracket = END_BRACKETS.includes(newLine.trim());
for (let i = 0; i < oldLines.length; i++) {
// Don't match end bracket lines if too far away
if (i > 4 && isEndBracket) {
return [-1, false, newLine];
}

if (linesMatchPerfectly(newLine, oldLines[i])) {
return [i, true, newLine];
} else if (linesMatch(newLine, oldLines[i])) {
} else if (linesMatch(newLine, oldLines[i], i)) {
// This is a way to fix indentation, but only for sufficiently long lines to avoid matching whitespace or short lines
if (
newLine.trimStart() === oldLines[i].trimStart() &&
Expand Down
22 changes: 21 additions & 1 deletion core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ export interface LLMReturnValue {
prompt: string;
completion: string;
}

export type PromptTemplate =
| string
| ((
history: ChatMessage[],
otherData: Record<string, string>,
) => string | ChatMessage[]);

export interface ILLM extends LLMOptions {
get providerName(): ModelProvider;

Expand Down Expand Up @@ -90,7 +98,18 @@ export interface ILLM extends LLMOptions {

supportsImages(): boolean;

supportsCompletions(): boolean;

supportsPrefill(): boolean;

listModels(): Promise<string[]>;

renderPromptTemplate(
template: PromptTemplate,
history: ChatMessage[],
otherData: Record<string, string>,
canPutWordsInModelsMouth?: boolean,
): string | ChatMessage[];
}

export type ContextProviderType = "normal" | "query" | "submenu";
Expand Down Expand Up @@ -243,7 +262,6 @@ export type ChatHistory = ChatHistoryItem[];
// LLM

export interface LLMFullCompletionOptions extends BaseCompletionOptions {
raw?: boolean;
log?: boolean;

model?: string;
Expand Down Expand Up @@ -382,6 +400,7 @@ export interface IDE {
getBranch(dir: string): Promise<string>;
getStats(directory: string): Promise<{ [path: string]: number }>;
getTags(artifactId: string): Promise<IndexTag[]>;
getRepoName(dir: string): Promise<string | undefined>;
}

// Slash Commands
Expand Down Expand Up @@ -575,6 +594,7 @@ interface BaseCompletionOptions {
maxTokens?: number;
numThreads?: number;
keepAlive?: number;
raw?: boolean;
}

export interface ModelDescription {
Expand Down

0 comments on commit 0af2e27

Please sign in to comment.