Skip to content

Standardize codeblock formatting #6281

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions core/context/providers/CurrentFileContextProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ContextProviderDescription,
ContextProviderExtras,
} from "../../";
import { formatCodeblock } from "../../util/formatCodeblock";
import { getUriDescription } from "../../util/uri";

class CurrentFileContextProvider extends BaseContextProvider {
Expand All @@ -24,10 +25,9 @@ class CurrentFileContextProvider extends BaseContextProvider {
return [];
}

const { relativePathOrBasename, last2Parts, baseName } = getUriDescription(
currentFile.path,
await extras.ide.getWorkspaceDirs(),
);
const workspaceDirs = await extras.ide.getWorkspaceDirs();
const { relativePathOrBasename, last2Parts, baseName, extension } =
getUriDescription(currentFile.path, workspaceDirs);

let prefix = "This is the currently open file:";
let name = baseName;
Expand All @@ -39,10 +39,15 @@ class CurrentFileContextProvider extends BaseContextProvider {
name = "Active file: " + baseName;
}

const codeblock = formatCodeblock(
relativePathOrBasename,
currentFile.contents,
extension,
);
return [
{
description: last2Parts,
content: `${prefix}\n\n\`\`\`${relativePathOrBasename}\n${currentFile.contents}\n\`\`\``,
content: `${prefix}\n\n${codeblock}`,
name,
uri: {
type: "file",
Expand Down
13 changes: 9 additions & 4 deletions core/context/providers/FileContextProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LoadSubmenuItemsArgs,
} from "../../";
import { walkDirs } from "../../indexing/walkDir";
import { formatCodeblock } from "../../util/formatCodeblock";
import {
getShortestUniqueRelativeUriPaths,
getUriDescription,
Expand All @@ -30,17 +31,21 @@ class FileContextProvider extends BaseContextProvider {
// Assume the query is a filepath
const fileUri = query.trim();
const content = await extras.ide.readFile(fileUri);
const workspaceDirs = await extras.ide.getWorkspaceDirs();

const { relativePathOrBasename, last2Parts, baseName } = getUriDescription(
fileUri,
await extras.ide.getWorkspaceDirs(),
const { relativePathOrBasename, last2Parts, baseName, extension } =
getUriDescription(fileUri, workspaceDirs);
const codeblock = formatCodeblock(
relativePathOrBasename,
content,
extension,
);

return [
{
name: baseName,
description: last2Parts,
content: `\`\`\`${relativePathOrBasename}\n${content}\n\`\`\``,
content: codeblock,
uri: {
type: "file",
value: fileUri,
Expand Down
11 changes: 9 additions & 2 deletions core/context/providers/OpenFilesContextProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ContextProviderDescription,
ContextProviderExtras,
} from "../../index.js";
import { formatCodeblock } from "../../util/formatCodeblock.js";
import { getUriDescription } from "../../util/uri.js";
import { BaseContextProvider } from "../index.js";

Expand All @@ -28,12 +29,18 @@ class OpenFilesContextProvider extends BaseContextProvider {
return await Promise.all(
openFiles.map(async (filepath: string) => {
const content = await ide.readFile(filepath);
const { relativePathOrBasename, last2Parts, baseName } =
const { relativePathOrBasename, last2Parts, baseName, extension } =
getUriDescription(filepath, workspaceDirs);

const codeblock = formatCodeblock(
relativePathOrBasename,
content,
extension,
);

return {
description: last2Parts,
content: `\`\`\`${relativePathOrBasename}\n${content}\n\`\`\``,
content: codeblock,
name: baseName,
uri: {
type: "file",
Expand Down
37 changes: 29 additions & 8 deletions core/context/providers/ProblemsContextProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ContextProviderDescription,
ContextProviderExtras,
} from "../../index.js";
import { formatCodeblock } from "../../util/formatCodeblock.js";
import { getUriDescription } from "../../util/uri.js";
import { BaseContextProvider } from "../index.js";

Expand All @@ -22,24 +23,44 @@ class ProblemsContextProvider extends BaseContextProvider {
const problems = await ide.getProblems();
const workspaceDirs = await ide.getWorkspaceDirs();

type FileCache = {
lines: string[];
desc: ReturnType<typeof getUriDescription>;
};
const files: Map<string, FileCache> = new Map();

const items = await Promise.all(
problems.map(async (problem) => {
const { relativePathOrBasename, baseName } = getUriDescription(
problem.filepath,
workspaceDirs,
);
const content = await ide.readFile(problem.filepath);
const lines = content.split("\n");
const rangeContent = lines
let cachedFile: FileCache;
if (files.has(problem.filepath)) {
cachedFile = files.get(problem.filepath)!;
} else {
const content = await ide.readFile(problem.filepath);
const desc = getUriDescription(problem.filepath, workspaceDirs);
const lines = content.split("\n");
cachedFile = { lines, desc };
files.set(problem.filepath, cachedFile);
}

const { relativePathOrBasename, baseName, extension } = cachedFile.desc;

const rangeContent = cachedFile.lines
.slice(
Math.max(0, problem.range.start.line - 2),
problem.range.end.line + 2,
)
.join("\n");

const codeblock = formatCodeblock(
relativePathOrBasename,
rangeContent,
extension,
problem.range,
);

return {
description: "Problems in current file",
content: `\`\`\`${relativePathOrBasename}\n${rangeContent}\n\`\`\`\n${problem.message}\n\n`,
content: `${codeblock}\n${problem.message}\n\n`,
name: `Warning in ${baseName}`,
};
}),
Expand Down
14 changes: 11 additions & 3 deletions core/context/retrieval/retrieval.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BranchAndDir, ContextItem, ContextProviderExtras } from "../../";
import { formatCodeblock } from "../../util/formatCodeblock";
import { getUriDescription } from "../../util/uri";
import { INSTRUCTIONS_BASE_ITEM } from "../providers/utils";

Expand Down Expand Up @@ -100,17 +101,24 @@ export async function retrieveContextItemsFromEmbeddings(
...results
.sort((a, b) => a.filepath.localeCompare(b.filepath))
.map((r) => {
const { relativePathOrBasename, last2Parts, baseName } =
const { relativePathOrBasename, last2Parts, baseName, extension } =
getUriDescription(r.filepath, workspaceDirs);

if (baseName === "package.json") {
console.warn("Retrieval pipeline: package.json detected");
}

const rangeString = `(${r.startLine + 1}-${r.endLine + 1})`;
const codeblock = formatCodeblock(
relativePathOrBasename,
r.content,
extension,
rangeString,
);
return {
name: `${baseName} (${r.startLine + 1}-${r.endLine + 1})`,
name: `${baseName} ${rangeString}`,
description: last2Parts,
content: `\`\`\`${relativePathOrBasename}\n${r.content}\n\`\`\``,
content: codeblock,
uri: {
type: "file" as const,
value: r.filepath,
Expand Down
14 changes: 7 additions & 7 deletions core/edit/lazy/applyCodeBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ import { deterministicApplyLazyEdit } from "./deterministic";
import { streamLazyApply } from "./streamLazyApply";
import { applyUnifiedDiff, isUnifiedDiffFormat } from "./unifiedDiffApply";

function canUseInstantApply(filename: string) {
const fileExtension = getUriFileExtension(filename);
return supportedLanguages[fileExtension] !== undefined;
function canUseInstantApply(fileUri: string) {
const fileExt = getUriFileExtension(fileUri);
return supportedLanguages[fileExt] !== undefined;
}

export async function applyCodeBlock(
oldFile: string,
newLazyFile: string,
filename: string,
fileUri: string,
llm: ILLM,
abortController: AbortController,
): Promise<{
isInstantApply: boolean;
diffLinesGenerator: AsyncGenerator<DiffLine>;
}> {
if (canUseInstantApply(filename)) {
if (canUseInstantApply(fileUri)) {
const diffLines = await deterministicApplyLazyEdit({
oldFile,
newLazyFile,
filename,
fileUri,
onlyFullFileRewrite: true,
});

Expand Down Expand Up @@ -54,7 +54,7 @@ export async function applyCodeBlock(
isInstantApply: false,
diffLinesGenerator: streamLazyApply(
oldFile,
filename,
fileUri,
newLazyFile,
llm,
abortController,
Expand Down
12 changes: 9 additions & 3 deletions core/edit/lazy/deterministic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { diff as myersDiff } from "myers-diff";
import { DiffLine } from "../..";
import { myersDiff as continueMyersDiff } from "../../diff/myers";
import { dedent } from "../../util";
import { localPathToUri } from "../../util/pathToUri";
import { deterministicApplyLazyEdit } from "./deterministic";

const UNIFIED_DIFF_SYMBOLS = {
Expand All @@ -17,14 +18,14 @@ const UNIFIED_DIFF_SYMBOLS = {
async function collectDiffs(
oldFile: string,
newFile: string,
filename: string,
fileUri: string,
): Promise<{ ourDiffs: DiffLine[]; myersDiffs: any }> {
const ourDiffs: DiffLine[] = [];

for (const diffLine of (await deterministicApplyLazyEdit({
oldFile,
newLazyFile: newFile,
filename,
fileUri,
})) ?? []) {
ourDiffs.push(diffLine);
}
Expand Down Expand Up @@ -61,7 +62,12 @@ async function expectDiff(file: string) {
const [oldFile, newFile, expectedDiff] = testFileContents
.split("\n---\n")
.map((s) => s.replace(/^\n+/, "").trimEnd());
const { ourDiffs: streamDiffs } = await collectDiffs(oldFile, newFile, file);
const fileUri = localPathToUri(testFilePath);
const { ourDiffs: streamDiffs } = await collectDiffs(
oldFile,
newFile,
fileUri,
);
const displayedDiff = displayDiff(streamDiffs);

if (!expectedDiff || expectedDiff.trim() === "") {
Expand Down
18 changes: 8 additions & 10 deletions core/edit/lazy/deterministic.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import path from "path";

import { distance } from "fastest-levenshtein";
import Parser from "web-tree-sitter";

Expand All @@ -8,6 +6,7 @@ import { LANGUAGES } from "../../autocomplete/constants/AutocompleteLanguageInfo
import { myersDiff } from "../../diff/myers";
import { getParserForFile } from "../../util/treeSitter";

import { getUriFileExtension } from "../../util/uri";
import { findInAst } from "./findInAst";

type AstReplacements = Array<{
Expand Down Expand Up @@ -109,12 +108,10 @@ function shouldRejectDiff(diff: DiffLine[]): boolean {

function nodeSurroundedInLazyBlocks(
parser: Parser,

file: string,
filename: string,
fileExt: string,
): { newTree: Parser.Tree; newFile: string } | undefined {
const ext = path.extname(filename).slice(1);
const language = LANGUAGES[ext];
const language = LANGUAGES[fileExt];
if (language) {
const newFile = `${language.singleLineComment} ... existing code ...\n\n${file}\n\n${language.singleLineComment} ... existing code...`;
const newTree = parser.parse(newFile);
Expand All @@ -129,7 +126,7 @@ function nodeSurroundedInLazyBlocks(
export async function deterministicApplyLazyEdit({
oldFile,
newLazyFile,
filename,
fileUri,
/**
* Using this as a flag to slowly reintroduce lazy applies.
* With this set, we will only attempt to deterministically apply
Expand All @@ -140,10 +137,11 @@ export async function deterministicApplyLazyEdit({
}: {
oldFile: string;
newLazyFile: string;
filename: string;
fileUri: string;
onlyFullFileRewrite?: boolean;
}): Promise<DiffLine[] | undefined> {
const parser = await getParserForFile(filename);
const fileExt = getUriFileExtension(fileUri);
const parser = await getParserForFile(fileUri);
if (!parser) {
return undefined;
}
Expand Down Expand Up @@ -175,7 +173,7 @@ export async function deterministicApplyLazyEdit({
);
if (firstSimilarNode?.parent?.equals(oldTree.rootNode)) {
// If so, we tack lazy blocks to start and end, and run the usual algorithm
const result = nodeSurroundedInLazyBlocks(parser, newLazyFile, filename);
const result = nodeSurroundedInLazyBlocks(parser, newLazyFile, fileExt);
if (result) {
newLazyFile = result.newFile;
newTree = result.newTree;
Expand Down
24 changes: 14 additions & 10 deletions core/edit/lazy/prompts.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { ChatMessage } from "../..";
import { dedent } from "../../util";
import { getUriFileExtension } from "../../util/uri";

export const UNCHANGED_CODE = "UNCHANGED CODE";

type LazyApplyPrompt = (
oldCode: string,
filename: string,
fileUri: string,
newCode: string,
) => ChatMessage[];

Expand All @@ -21,18 +22,21 @@ const RULES = [
"The code should always be syntactically valid, even with the comments.",
];

function claude35SonnetLazyApplyPrompt(
...args: Parameters<LazyApplyPrompt>
): ReturnType<LazyApplyPrompt> {
const claude35SonnetLazyApplyPrompt: LazyApplyPrompt = (
oldCode,
fileUri,
newCode,
) => {
const fileExt = getUriFileExtension(fileUri);
const userContent = dedent`
ORIGINAL CODE:
\`\`\`${args[1]}
${args[0]}
\`\`\`${fileExt}
${oldCode}
\`\`\`

NEW CODE:
\`\`\`
${args[2]}
\`\`\`${fileExt}
${newCode}
\`\`\`

Above is a code block containing the original version of a file (ORIGINAL CODE) and below it is a code snippet (NEW CODE) that was suggested as modification to the original file. Your task is to apply the NEW CODE to the ORIGINAL CODE and show what the entire file would look like after it is applied.
Expand All @@ -41,14 +45,14 @@ function claude35SonnetLazyApplyPrompt(

const assistantContent = dedent`
Sure! Here's the modified version of the file after applying the new code:
\`\`\`${args[1]}
\`\`\`${fileExt}
`;

return [
{ role: "user", content: userContent },
{ role: "assistant", content: assistantContent },
];
}
};

export function lazyApplyPromptForModel(
model: string,
Expand Down
Loading
Loading