Skip to content

Commit

Permalink
Move language to top-level attr on session (#159)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjreinhart authored Jul 26, 2024
1 parent 9cbac61 commit 24e537c
Show file tree
Hide file tree
Showing 16 changed files with 80 additions and 87 deletions.
8 changes: 4 additions & 4 deletions packages/api/ai/generate.mts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const makeGenerateCellUserPrompt = (session: SessionType, insertIdx: number, que
text: '==== INTRODUCE CELL HERE ====',
});

const inlineSrcbookWithPlaceholder = encode(cellsWithPlaceholder, session.metadata, {
const inlineSrcbookWithPlaceholder = encode(cellsWithPlaceholder, session.language, {
inline: true,
});

Expand All @@ -57,7 +57,7 @@ const makeGenerateCellEditUserPrompt = (
session: SessionType,
cell: CodeCellType,
) => {
const inlineSrcbook = encode(session.cells, session.metadata, { inline: true });
const inlineSrcbook = encode(session.cells, session.language, { inline: true });

const prompt = `==== BEGIN SRCBOOK ====
${inlineSrcbook}
Expand Down Expand Up @@ -129,7 +129,7 @@ export async function generateCells(
): Promise<GenerateCellsResult> {
const model = await getOpenAIModel();

const systemPrompt = makeGenerateCellSystemPrompt(session.metadata.language);
const systemPrompt = makeGenerateCellSystemPrompt(session.language);
const userPrompt = makeGenerateCellUserPrompt(session, insertIdx, query);
const result = await generateText({
model: model,
Expand Down Expand Up @@ -157,7 +157,7 @@ export async function generateCells(
export async function generateCellEdit(query: string, session: SessionType, cell: CodeCellType) {
const model = await getOpenAIModel();

const systemPrompt = makeGenerateCellEditSystemPrompt(session.metadata.language);
const systemPrompt = makeGenerateCellEditSystemPrompt(session.language);
const userPrompt = makeGenerateCellEditUserPrompt(query, session, cell);
const result = await generateText({
model: model,
Expand Down
2 changes: 1 addition & 1 deletion packages/api/server/http.mts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ router.post('/srcbooks', cors(), async (req, res) => {
}

try {
const srcbookDir = await createSrcbook(name, { language });
const srcbookDir = await createSrcbook(name, language);
return res.json({ error: false, result: { name, path: srcbookDir } });
} catch (e) {
const error = e as unknown as Error;
Expand Down
20 changes: 6 additions & 14 deletions packages/api/server/ws.mts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ async function depsInstall(payload: DepsInstallPayloadType) {

wss.broadcast(`session:${updatedSession.id}`, 'cell:updated', { cell: updatedCell });

if (updatedSession.metadata.language === 'typescript') {
if (updatedSession.language === 'typescript') {
const mustCreateTsServer = !tsservers.has(updatedSession.id);

// Make sure to handle the following case here:
Expand Down Expand Up @@ -304,11 +304,7 @@ async function cellCreate(payload: CellCreatePayloadType) {
// TODO: handle potential errors
await addCell(session, cell, index);

if (
session.metadata.language === 'typescript' &&
cell.type === 'code' &&
tsservers.has(session.id)
) {
if (session.language === 'typescript' && cell.type === 'code' && tsservers.has(session.id)) {
const tsserver = tsservers.get(session.id);

tsserver.open({
Expand Down Expand Up @@ -385,11 +381,7 @@ async function cellUpdate(payload: CellUpdatePayloadType) {

const cell = result.cell as CodeCellType;

if (
session.metadata.language === 'typescript' &&
cell.type === 'code' &&
tsservers.has(session.id)
) {
if (session.language === 'typescript' && cell.type === 'code' && tsservers.has(session.id)) {
const tsserver = tsservers.get(session.id);

// This isn't intended for renaming, so the filenames
Expand Down Expand Up @@ -432,7 +424,7 @@ async function cellRename(payload: CellRenamePayloadType) {
}

if (
session.metadata.language === 'typescript' &&
session.language === 'typescript' &&
cellBeforeUpdate.type === 'code' &&
tsservers.has(session.id)
) {
Expand Down Expand Up @@ -495,7 +487,7 @@ async function cellDelete(payload: CellDeletePayloadType) {
if (cell.type === 'code') {
removeCodeCellFromDisk(updatedSession.dir, cell.filename);

if (updatedSession.metadata.language === 'typescript' && tsservers.has(updatedSession.id)) {
if (updatedSession.language === 'typescript' && tsservers.has(updatedSession.id)) {
const file = pathToCodeFile(updatedSession.dir, cell.filename);
const tsserver = tsservers.get(updatedSession.id);
tsserver.close({ file });
Expand Down Expand Up @@ -562,7 +554,7 @@ async function tsserverStart(payload: TsServerStartPayloadType) {
throw new Error(`No session exists for session '${payload.sessionId}'`);
}

if (session.metadata.language !== 'typescript') {
if (session.language !== 'typescript') {
throw new Error(`tsserver can only be used with TypeScript Srcbooks.`);
}

Expand Down
54 changes: 24 additions & 30 deletions packages/api/session.mts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ export async function createSession(srcbookDir: string) {
id: Path.basename(srcbookDir),
dir: srcbookDir,
cells: result.cells,
metadata: result.metadata,
language: result.language,
openedAt: Date.now(),
};

// TODO: Read from disk once we support editing tsconfig.json.
if (session.metadata.language === 'typescript') {
if (session.language === 'typescript') {
session['tsconfig.json'] = buildTsconfigJson();
}

Expand Down Expand Up @@ -93,9 +93,9 @@ export async function addCell(

switch (cell.type) {
case 'markdown':
return writeReadmeToDisk(session.dir, session.metadata, session.cells);
return writeReadmeToDisk(session.dir, session.language, session.cells);
case 'code':
return writeCellToDisk(session.dir, session.metadata, session.cells, cell);
return writeCellToDisk(session.dir, session.language, session.cells, cell);
}
}

Expand All @@ -108,7 +108,7 @@ export async function updateSession(
const updatedSession = { ...session, ...updates };
sessions[id] = updatedSession;
if (flush) {
await writeToDisk(updatedSession.dir, session.metadata, updatedSession.cells);
await writeToDisk(updatedSession.dir, session.language, updatedSession.cells);
}
return updatedSession;
}
Expand All @@ -118,7 +118,7 @@ export async function exportSrcmdFile(session: SessionType, destinationPath: str
throw new Error(`Cannot export .src.md file: ${destinationPath} already exists`);
}

return fs.writeFile(destinationPath, encode(session.cells, session.metadata, { inline: true }));
return fs.writeFile(destinationPath, encode(session.cells, session.language, { inline: true }));
}

export async function findSession(id: string): Promise<SessionType> {
Expand Down Expand Up @@ -155,7 +155,7 @@ function updateTitleCell(session: SessionType, cell: TitleCellType, updates: any
const attrs = TitleCellUpdateAttrsSchema.parse(updates);
return updateCellWithRollback(session, cell, attrs, async (session) => {
try {
await writeReadmeToDisk(session.dir, session.metadata, session.cells);
await writeReadmeToDisk(session.dir, session.language, session.cells);
} catch (e) {
console.error(e);
return [{ message: 'An error occurred persisting files to disk' }];
Expand All @@ -167,7 +167,7 @@ function updateMarkdownCell(session: SessionType, cell: MarkdownCellType, update
const attrs = MarkdownCellUpdateAttrsSchema.parse(updates);
return updateCellWithRollback(session, cell, attrs, async (session) => {
try {
await writeReadmeToDisk(session.dir, session.metadata, session.cells);
await writeReadmeToDisk(session.dir, session.language, session.cells);
} catch (e) {
console.error(e);
return [{ message: 'An error occurred persisting files to disk' }];
Expand All @@ -181,7 +181,7 @@ function updatePackageJsonCell(session: SessionType, cell: PackageJsonCellType,
try {
await writeCellToDisk(
session.dir,
session.metadata,
session.language,
session.cells,
updatedCell as PackageJsonCellType,
);
Expand All @@ -202,7 +202,7 @@ async function updateCodeCell(
try {
await writeCellToDisk(
session.dir,
session.metadata,
session.language,
session.cells,
updatedCell as CodeCellType,
);
Expand Down Expand Up @@ -235,14 +235,12 @@ export async function updateCodeCellFilename(
};
}

const language = session.metadata.language;

if (language !== languageFromFilename(filename)) {
if (session.language !== languageFromFilename(filename)) {
return {
success: false,
errors: [
{
message: `File must have one of the following extensions: ${extensionsForLanguage(language)}`,
message: `File must have one of the following extensions: ${extensionsForLanguage(session.language)}`,
attribute: 'filename',
},
],
Expand All @@ -260,7 +258,7 @@ export async function updateCodeCellFilename(
try {
await moveCodeCellOnDisk(
session.dir,
session.metadata,
session.language,
session.cells,
updatedCell as CodeCellType,
cell.filename,
Expand All @@ -286,22 +284,18 @@ export function updateCell(session: SessionType, cell: CellType, updates: CellUp
}

export function sessionToResponse(session: SessionType) {
if (session.metadata.language === 'typescript') {
return {
id: session.id,
cells: session.cells,
metadata: session.metadata,
'tsconfig.json': session['tsconfig.json'],
openedAt: session.openedAt,
};
} else {
return {
id: session.id,
cells: session.cells,
metadata: session.metadata,
openedAt: session.openedAt,
};
const result: Pick<SessionType, 'id' | 'cells' | 'language' | 'tsconfig.json' | 'openedAt'> = {
id: session.id,
cells: session.cells,
language: session.language,
openedAt: session.openedAt,
};

if (session.language === 'typescript') {
result['tsconfig.json'] = session['tsconfig.json'];
}

return result;
}

export async function readPackageJsonContentsFromDisk(session: SessionType) {
Expand Down
29 changes: 14 additions & 15 deletions packages/api/srcbook/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type {
CodeCellType,
CodeLanguageType,
PackageJsonCellType,
SrcbookMetadataType,
} from '@srcbook/shared';
import { randomid } from '@srcbook/shared';
import { encode, decode } from '../srcmd.mjs';
Expand All @@ -25,8 +24,8 @@ function writeCellOnlyToDisk(srcbookDir: string, cell: PackageJsonCellType | Cod
return fs.writeFile(path, cell.source, { encoding: 'utf8' });
}

export function writeToDisk(srcbookDir: string, metadata: SrcbookMetadataType, cells: CellType[]) {
const writes = [writeReadmeToDisk(srcbookDir, metadata, cells)];
export function writeToDisk(srcbookDir: string, language: CodeLanguageType, cells: CellType[]) {
const writes = [writeReadmeToDisk(srcbookDir, language, cells)];

for (const cell of cells) {
if (cell.type === 'package.json' || cell.type === 'code') {
Expand All @@ -39,37 +38,37 @@ export function writeToDisk(srcbookDir: string, metadata: SrcbookMetadataType, c

export function writeCellToDisk(
srcbookDir: string,
metadata: SrcbookMetadataType,
language: CodeLanguageType,
cells: CellType[],
cell: PackageJsonCellType | CodeCellType,
) {
// Readme must also be updated
return Promise.all([
writeReadmeToDisk(srcbookDir, metadata, cells),
writeReadmeToDisk(srcbookDir, language, cells),
writeCellOnlyToDisk(srcbookDir, cell),
]);
}

export function moveCodeCellOnDisk(
srcbookDir: string,
metadata: SrcbookMetadataType,
language: CodeLanguageType,
cells: CellType[],
cell: CodeCellType,
oldFilename: string,
) {
return Promise.all([
writeReadmeToDisk(srcbookDir, metadata, cells),
writeReadmeToDisk(srcbookDir, language, cells),
fs.unlink(pathToCodeFile(srcbookDir, oldFilename)),
fs.writeFile(pathToCodeFile(srcbookDir, cell.filename), cell.source, { encoding: 'utf8' }),
]);
}

export function writeReadmeToDisk(
srcbookDir: string,
metadata: SrcbookMetadataType,
language: CodeLanguageType,
cells: CellType[],
) {
return fs.writeFile(pathToReadme(srcbookDir), encode(cells, metadata, { inline: false }), {
return fs.writeFile(pathToReadme(srcbookDir), encode(cells, language, { inline: false }), {
encoding: 'utf8',
});
}
Expand Down Expand Up @@ -107,9 +106,9 @@ export async function importSrcbookFromSrcmdText(text: string, directoryBasename
throw new Error(`Cannot decode invalid srcmd`);
}

const dirname = await createSrcbookDir(result.metadata.language, directoryBasename);
const dirname = await createSrcbookDir(result.language, directoryBasename);

await writeToDisk(dirname, result.metadata, result.cells);
await writeToDisk(dirname, result.language, result.cells);

return dirname;
}
Expand All @@ -120,8 +119,8 @@ export async function importSrcbookFromSrcmdText(text: string, directoryBasename
* This private directory has a randomid() private identifier.
* Users are not supposed to be aware or modify private directories.
*/
export async function createSrcbook(title: string, metadata: SrcbookMetadataType) {
const dirname = await createSrcbookDir(metadata.language);
export async function createSrcbook(title: string, language: CodeLanguageType) {
const dirname = await createSrcbookDir(language);

const cells: CellType[] = [
{
Expand All @@ -132,13 +131,13 @@ export async function createSrcbook(title: string, metadata: SrcbookMetadataType
{
id: randomid(),
type: 'package.json',
source: buildPackageJson(metadata.language),
source: buildPackageJson(language),
filename: 'package.json',
status: 'idle',
},
];

await writeToDisk(dirname, metadata, cells);
await writeToDisk(dirname, language, cells);

return dirname;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/api/srcmd.mts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function decodeDir(dir: string): Promise<DecodeResult> {
// Wait for all file reads to complete
await Promise.all(pendingFileReads);

return { error: false, metadata: readmeResult.metadata, cells };
return { error: false, language: readmeResult.language, cells };
} catch (e) {
const error = e as unknown as Error;
return { error: true, errors: [error.message] };
Expand Down
4 changes: 2 additions & 2 deletions packages/api/srcmd/decoding.mts
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ export function decode(contents: string): DecodeResult {
// Finally, return either the set of errors or the tokens converted to cells if no errors were found.
return errors.length > 0
? { error: true, errors: errors }
: { error: false, metadata, cells: convertToCells(groups) };
: { error: false, language: metadata.language, cells: convertToCells(groups) };
}

/**
* This is used to decode a subset of a .src.md file.
*
* For example, we generate a subset of a Srcbook (1 or more cells) using AI.
* When that happens, we do not have the entire .src.md contents, so we need
* to ignore some aspects of it, like parsing the metadata.
* to ignore some aspects of it, like parsing the srcbook metadata comment.
*/
export function decodeCells(contents: string): DecodeCellsResult {
const tokens = marked.lexer(contents);
Expand Down
Loading

0 comments on commit 24e537c

Please sign in to comment.