Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c07136b36 | |||
| 580f9b6e02 | |||
| 68bc7ba620 | |||
| dd48db0ed3 |
+2
-2
@@ -69,7 +69,7 @@ We haven't created prerequisite steps for building on Linux yet, but you can fol
|
||||
|
||||
### Build instructions
|
||||
|
||||
Before building Void, please follow the prerequisite steps above for your operating system. Also make sure you've already built the Void extension (or just run `cd ./extensions/void && npm install && npm run build && npm run compile && cd ../..`).
|
||||
Before building Void, please follow the prerequisite steps above for your operating system. Also make sure you've already built and compiled the Void extension (or just run `cd ./extensions/void && npm install && npm run build && npm run compile && cd ../..`).
|
||||
|
||||
To build Void, first open `void/` in VSCode. Then:
|
||||
|
||||
@@ -146,7 +146,7 @@ We're always glad to talk about new ideas, help you get set up, and make sure yo
|
||||
|
||||
## Submitting a Pull Request
|
||||
|
||||
Please submit a pull request once you've made a change. You don't need to submit an issue.
|
||||
Please submit a pull request once you've made a change. You don't need to submit an issue.
|
||||
|
||||
Please don't use AI to write your PR 🙂.
|
||||
|
||||
|
||||
@@ -32,6 +32,13 @@ type Diff = {
|
||||
lenses: vscode.CodeLens[],
|
||||
} & BaseDiff
|
||||
|
||||
type Inset = {
|
||||
text: string,
|
||||
height: number,
|
||||
line: number,
|
||||
insetObj: vscode.WebviewEditorInset,
|
||||
}
|
||||
|
||||
// editor -> sidebar
|
||||
type MessageToSidebar = (
|
||||
| { type: 'ctrl+l', selection: CodeSelection } // user presses ctrl+l in the editor. selection and path are frozen snapshots
|
||||
@@ -88,6 +95,7 @@ type ChatMessage =
|
||||
export {
|
||||
BaseDiff, Diff,
|
||||
DiffArea,
|
||||
Inset,
|
||||
CodeSelection,
|
||||
File,
|
||||
MessageFromSidebar,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { findDiffs } from './findDiffs';
|
||||
import { throttle } from 'lodash';
|
||||
import { DiffArea, BaseDiff, Diff } from '../common/shared_types';
|
||||
import { DiffArea, BaseDiff, Diff, Inset } from '../common/shared_types';
|
||||
import { readFileContentOfUri } from './extensionLib/readFileContentOfUri';
|
||||
import { updateWebviewHTML } from './extensionLib/updateWebviewHTML';
|
||||
|
||||
@@ -10,15 +10,15 @@ const THROTTLE_TIME = 100
|
||||
|
||||
// TODO in theory this should be disposed
|
||||
const greenDecoration = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: 'rgba(0 255 51 / 0.2)',
|
||||
isWholeLine: false, // after: { contentText: ' [original]', color: 'rgba(0 255 60 / 0.5)' } // hoverMessage: originalText // this applies to hovering over after:...
|
||||
backgroundColor: 'rgba(0, 255, 51, 0.2)',
|
||||
isWholeLine: true, // after: { contentText: ' [original]', color: 'rgba(0 255 60 / 0.5)' } // hoverMessage: originalText // this applies to hovering over after:...
|
||||
})
|
||||
const lightGrayDecoration = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: 'rgba(218 218 218 / .2)',
|
||||
backgroundColor: 'rgba(218, 218, 218, 0.2)',
|
||||
isWholeLine: true,
|
||||
})
|
||||
const darkGrayDecoration = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: 'rgb(148 148 148 / .2)',
|
||||
backgroundColor: 'rgb(148, 148, 148, 0.2)',
|
||||
isWholeLine: true,
|
||||
})
|
||||
|
||||
@@ -28,6 +28,8 @@ export class DiffProvider implements vscode.CodeLensProvider {
|
||||
private _originalFileOfDocument: { [docUriStr: string]: string } = {}
|
||||
private _diffAreasOfDocument: { [docUriStr: string]: DiffArea[] } = {}
|
||||
private _diffsOfDocument: { [docUriStr: string]: Diff[] } = {}
|
||||
private _insetsOfDocument: { [docUriStr: string]: Inset[] } = {}
|
||||
|
||||
|
||||
private _diffareaidPool = 0
|
||||
private _diffidPool = 0
|
||||
@@ -186,23 +188,15 @@ export class DiffProvider implements vscode.CodeLensProvider {
|
||||
// compute the diffs
|
||||
const diffs = findDiffs(originalCode, currentCode)
|
||||
|
||||
// console.log('____________')
|
||||
// diffs.forEach(diff => {
|
||||
// console.log('__DELETED: ', diff.originalCode)
|
||||
// console.log('__INSERTED: ', diff.code)
|
||||
// })
|
||||
|
||||
// add the diffs to `this._diffsOfDocument[docUriStr]`
|
||||
this.createDiffs(editor.document.uri, diffs, diffArea)
|
||||
|
||||
|
||||
// // print diffs
|
||||
// console.log('!ORIGINAL FILE:', JSON.stringify(originalFile))
|
||||
// console.log('!NEW FILE :', JSON.stringify(editor.document.getText().replace(/\r\n/g, '\n')))
|
||||
// console.log('!AREA originalCode:', JSON.stringify(originalCode))
|
||||
// console.log('!AREA currentCode :', JSON.stringify(currentCode))
|
||||
// for (const diff of this._diffsOfDocument[docUriStr]) {
|
||||
// console.log('------------')
|
||||
// console.log('originalCode:', JSON.stringify(diff.originalCode))
|
||||
// console.log('currentCode:', JSON.stringify(diff.code))
|
||||
// console.log('originalRange:', diff.originalRange.start.line, diff.originalRange.end.line,)
|
||||
// console.log('currentRange:', diff.range.start.line, diff.range.end.line,)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
// update green highlighting
|
||||
@@ -215,32 +209,88 @@ export class DiffProvider implements vscode.CodeLensProvider {
|
||||
);
|
||||
|
||||
// update red highlighting
|
||||
// this._diffsOfDocument[docUriStr]
|
||||
// .filter(diff => diff.originalCode !== '')
|
||||
// .forEach(diff => {
|
||||
// const text = originalFile.split('\n').slice(diff.originalRange.start.line, diff.originalRange.start.line + 1).join('\n')
|
||||
// const height = text.split('\n').length
|
||||
// const line = diff.range.start.line - 1
|
||||
/* NAIVE APPROACH */
|
||||
if (!this._insetsOfDocument[docUriStr])
|
||||
this._insetsOfDocument[docUriStr] = []
|
||||
for (const inset of this._insetsOfDocument[docUriStr]) {
|
||||
inset.insetObj.dispose()
|
||||
}
|
||||
this._insetsOfDocument[docUriStr] = this._diffsOfDocument[docUriStr]
|
||||
.filter(diff => diff.originalCode !== '')
|
||||
.map(diff => {
|
||||
const text = diff.originalCode
|
||||
const height = diff.originalCode.split('\n').length
|
||||
const line = diff.range.start.line - 1
|
||||
const insetObj = vscode.window.createWebviewTextEditorInset(editor, line, height);
|
||||
updateWebviewHTML(insetObj.webview, this._extensionUri, { jsOutLocation: 'dist/webviews/diffline/index.js', cssOutLocation: 'dist/webviews/styles.css', isCode: true },
|
||||
{ text }
|
||||
)
|
||||
return { text, line, height, insetObj }
|
||||
})
|
||||
/* CACHING APPROACH */
|
||||
// function getCompareString<T extends { [key: string]: any; }>(obj: T, compareKeys: string[]): string {
|
||||
// return compareKeys.map(key => `${key}:${JSON.stringify(obj?.[key])}`).join('|');
|
||||
// }
|
||||
|
||||
// const inset = vscode.window.createWebviewTextEditorInset(editor, line, height);
|
||||
// updateWebviewHTML(inset.webview, this._extensionUri, { jsOutLocation: 'dist/webviews/diffline/index.js', cssOutLocation: 'dist/webviews/styles.css' },
|
||||
// { text }
|
||||
// )
|
||||
// function compareArrays<T extends { [key: string]: any }>(arr1: T[], arr2: T[], compareKeys: string[]) {
|
||||
|
||||
// const getKey = (obj: T) => getCompareString(obj, compareKeys);
|
||||
|
||||
// const set1 = new Set(arr1.map(getKey));
|
||||
// const set2 = new Set(arr2.map(getKey));
|
||||
|
||||
// const common = arr1.filter(obj => set2.has(getKey(obj)))
|
||||
// const uniqueToFirst = arr1.filter(obj => !set2.has(getKey(obj)))
|
||||
// const uniqueToSecond = arr2.filter(obj => !set1.has(getKey(obj)))
|
||||
// const uniqueToFirstKeys = new Set(uniqueToFirst.map(getKey))
|
||||
// const uniqueToSecondKeys = new Set(uniqueToSecond.map(getKey))
|
||||
|
||||
|
||||
// })
|
||||
// return {
|
||||
// common,
|
||||
// uniqueToFirst,
|
||||
// uniqueToSecond,
|
||||
// uniqueToFirstKeys,
|
||||
// uniqueToSecondKeys,
|
||||
// getKey,
|
||||
// };
|
||||
// }
|
||||
// compare the current and old insets and update the ones that have changed
|
||||
// if (!this._insetsOfDocument[docUriStr])
|
||||
// this._insetsOfDocument[docUriStr] = []
|
||||
// const oldInsets: Inset[] = this._insetsOfDocument[docUriStr]
|
||||
// const currInsets: Inset[] = this._diffsOfDocument[docUriStr]
|
||||
// .map(diff => ({ text: diff.originalCode, height: diff.originalCode.split('\n').length, line: diff.range.start.line - 1, insetObj: undefined! }))
|
||||
// const {
|
||||
// common: commonInsets,
|
||||
// uniqueToFirst: deletedInsets,
|
||||
// uniqueToSecond: addedInsets,
|
||||
// } = compareArrays(oldInsets, currInsets, ['text', 'height', 'line'])
|
||||
// console.log('DELETED INSETS', deletedInsets)
|
||||
// console.log('ADDED INSETS', addedInsets)
|
||||
// for (const deletedInset of deletedInsets) { // delete insets in the deleted set
|
||||
// deletedInset.insetObj.dispose()
|
||||
// }
|
||||
// for (const addedInset of addedInsets) { // add insets in the added set
|
||||
// const insetObj = vscode.window.createWebviewTextEditorInset(editor, addedInset.line, addedInset.height);
|
||||
// updateWebviewHTML(insetObj.webview, this._extensionUri, { jsOutLocation: 'dist/webviews/diffline/index.js', cssOutLocation: 'dist/webviews/styles.css', isCode: true },
|
||||
// { text: addedInset.text }
|
||||
// )
|
||||
// addedInset.insetObj = insetObj
|
||||
// }
|
||||
// this._insetsOfDocument[docUriStr] = commonInsets.concat(addedInsets)
|
||||
|
||||
// for each diffArea, highlight its sweepIndex in dark gray
|
||||
editor.setDecorations(
|
||||
darkGrayDecoration,
|
||||
(this._diffAreasOfDocument[docUriStr]
|
||||
.filter(diffArea => diffArea.sweepIndex !== null)
|
||||
.map(diffArea => {
|
||||
let s = diffArea.sweepIndex!
|
||||
return new vscode.Range(s, 0, s, 0)
|
||||
})
|
||||
)
|
||||
)
|
||||
// // for each diffArea, highlight its sweepIndex in dark gray
|
||||
// editor.setDecorations(
|
||||
// darkGrayDecoration,
|
||||
// (this._diffAreasOfDocument[docUriStr]
|
||||
// .filter(diffArea => diffArea.sweepIndex !== null)
|
||||
// .map(diffArea => {
|
||||
// let s = diffArea.sweepIndex!
|
||||
// return new vscode.Range(s, 0, s, 0)
|
||||
// })
|
||||
// )
|
||||
// )
|
||||
|
||||
// for each diffArea, highlight sweepIndex+1...end in light gray
|
||||
editor.setDecorations(
|
||||
@@ -396,7 +446,8 @@ export class DiffProvider implements vscode.CodeLensProvider {
|
||||
|
||||
|
||||
// used by us only
|
||||
public updateStream = throttle(async (docUriStr: string, diffArea: DiffArea, newDiffAreaCode: string) => {
|
||||
public updateStream = throttle(async (docUriStr: string, diffArea: DiffArea, newDiffAreaCode: string, options?: { endStream?: boolean }) => {
|
||||
const { endStream, } = options || {}
|
||||
|
||||
const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
|
||||
if (!editor) {
|
||||
@@ -404,12 +455,12 @@ export class DiffProvider implements vscode.CodeLensProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
// original code all diffs are based on in the code
|
||||
// original code all diffs are based on
|
||||
const originalDiffAreaCode = (this._originalFileOfDocument[docUriStr] || '').split('\n').slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
|
||||
// figure out where to highlight based on where the AI is in the stream right now, use the last diff in findDiffs to figure that out
|
||||
const diffs = findDiffs(originalDiffAreaCode, newDiffAreaCode)
|
||||
const lastDiff = diffs?.[diffs.length - 1] ?? null
|
||||
const lastDiff = diffs?.[diffs.length - 1]
|
||||
|
||||
// these are two different coordinate systems - new and old line number
|
||||
let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted
|
||||
@@ -443,14 +494,14 @@ export class DiffProvider implements vscode.CodeLensProvider {
|
||||
const oldFileBottom = originalDiffAreaCode.split('\n').slice(oldFileStartLine + 1, Infinity).join('\n')
|
||||
|
||||
let newCode = `${newFileTop}\n${oldFileBottom}`
|
||||
diffArea.sweepIndex = newFileEndLine
|
||||
// replace oldDACode with newDACode with a vscode edit
|
||||
diffArea.sweepIndex = endStream ? null : newFileEndLine
|
||||
|
||||
// replace oldCode with newCode using a vscode edit
|
||||
const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
|
||||
const diffareaRange = new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)
|
||||
workspaceEdit.replace(editor.document.uri, diffareaRange, newCode)
|
||||
await vscode.workspace.applyEdit(workspaceEdit)
|
||||
|
||||
}, THROTTLE_TIME)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import * as vscode from 'vscode'
|
||||
|
||||
|
||||
export const readFileContentOfUri = async (uri: vscode.Uri): Promise<string> => {
|
||||
const document = await vscode.workspace.openTextDocument(uri);
|
||||
return document.getText().replace(/\r\n/g, '\n') ?? '' // Normalize line endings
|
||||
// export const readFileContentOfUri = async (uri: vscode.Uri): Promise<string> => {
|
||||
// const document = await vscode.workspace.openTextDocument(uri.fsPath);
|
||||
// return document.getText().replace(/\r\n/g, '\n') ?? '' // Normalize line endings
|
||||
// };
|
||||
|
||||
};
|
||||
|
||||
// this is the old version, which only reads the most recently saved version
|
||||
// export const readFileContentOfUri = async (uri: vscode.Uri) => {
|
||||
// return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8')
|
||||
// .replace(/\r\n/g, '\n') // replace windows \r\n with \n
|
||||
// }
|
||||
// TODO this only accesses the most recently saved version; make it instead access the most recent version in the vscode editor
|
||||
export const readFileContentOfUri = async (uri: vscode.Uri) => {
|
||||
return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8')
|
||||
.replace(/\r\n/g, '\n') // replace windows \r\n with \n
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ function generateNonce() {
|
||||
|
||||
|
||||
// call this when you have access to the webview to set its html
|
||||
export const updateWebviewHTML = (webview: vscode.Webview, extensionUri: vscode.Uri, { jsOutLocation, cssOutLocation }: { jsOutLocation: string, cssOutLocation: string }, props?: object) => {
|
||||
export const updateWebviewHTML = (webview: vscode.Webview, extensionUri: vscode.Uri, { jsOutLocation, cssOutLocation, isCode }: { jsOutLocation: string, cssOutLocation: string, isCode?: boolean }, props?: object) => {
|
||||
|
||||
// 'dist/sidebar/index.js'
|
||||
// 'dist/sidebar/styles.css'
|
||||
@@ -21,6 +21,8 @@ export const updateWebviewHTML = (webview: vscode.Webview, extensionUri: vscode.
|
||||
const rootUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri));
|
||||
const nonce = generateNonce();
|
||||
|
||||
const codeStyle = `style="padding-left:0; padding-right:0; font-family: Consolas, "Courier New", monospace; font-weight: normal; font-size: 14px; font-feature-settings: "liga" 0, "calt" 0; font-variation-settings: normal; line-height: 19px; letter-spacing: 0px;"`
|
||||
|
||||
const webviewHTML = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -31,8 +33,8 @@ export const updateWebviewHTML = (webview: vscode.Webview, extensionUri: vscode.
|
||||
<base href="${rootUri}/">
|
||||
<link href="${stylesUri}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"${props ? ` data-void-props="${encodeURIComponent(JSON.stringify(props))}"` : ''}></div>
|
||||
<body ${isCode ? codeStyle : ''}>
|
||||
<div id="root" ${props ? `data-void-props="${encodeURIComponent(JSON.stringify(props))}"` : ''}></div>
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
@@ -127,6 +127,6 @@ export function findDiffs(oldStr: string, newStr: string) {
|
||||
}
|
||||
} // end for
|
||||
|
||||
console.debug('Replacements', replacements)
|
||||
// console.debug('Replacements', replacements)
|
||||
return replacements
|
||||
}
|
||||
|
||||
@@ -3,22 +3,6 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { updateWebviewHTML as _updateWebviewHTML, updateWebviewHTML } from '../extensionLib/updateWebviewHTML';
|
||||
|
||||
// this comes from vscode.proposed.editorInsets.d.ts
|
||||
declare module 'vscode' {
|
||||
export interface WebviewEditorInset {
|
||||
readonly editor: vscode.TextEditor;
|
||||
readonly line: number;
|
||||
readonly height: number;
|
||||
readonly webview: vscode.Webview;
|
||||
readonly onDidDispose: Event<void>;
|
||||
dispose(): void;
|
||||
}
|
||||
export namespace window {
|
||||
export function createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options?: vscode.WebviewOptions): WebviewEditorInset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class CtrlKWebviewProvider {
|
||||
|
||||
|
||||
@@ -2,21 +2,17 @@ import React, { ReactNode, createContext, useCallback, useContext, useEffect, us
|
||||
|
||||
const PropsContext = createContext<any>(undefined as unknown as any)
|
||||
|
||||
export const getPropsObj = (rootElement: HTMLElement) => {
|
||||
let props = rootElement.getAttribute("data-void-props")
|
||||
let propsObj: object | null = null
|
||||
if (props !== null) {
|
||||
propsObj = JSON.parse(decodeURIComponent(props))
|
||||
}
|
||||
return propsObj
|
||||
}
|
||||
|
||||
// provider for whatever came in data-void-props
|
||||
export function PropsProvider({ children, rootElement }: { children: ReactNode, rootElement: HTMLElement }) {
|
||||
|
||||
const [props, setProps] = useState<object | null>(null)
|
||||
|
||||
// update props when rootElement changes
|
||||
useEffect(() => {
|
||||
let props = rootElement.getAttribute("data-void-props")
|
||||
let propsObj: object | null = null
|
||||
if (props !== null) {
|
||||
propsObj = JSON.parse(decodeURIComponent(props))
|
||||
}
|
||||
setProps(propsObj)
|
||||
}, [rootElement])
|
||||
|
||||
export function PropsProvider({ children, props }: { children: ReactNode, props: object | null }) {
|
||||
return (
|
||||
<PropsContext.Provider value={props}>
|
||||
{children}
|
||||
@@ -24,13 +20,20 @@ export function PropsProvider({ children, rootElement }: { children: ReactNode,
|
||||
)
|
||||
}
|
||||
|
||||
export function useVoidProps<T extends {}>(): T | null {
|
||||
export function useVoidProps<T extends {}>(): T {
|
||||
// context is the "value" from above
|
||||
const context: T | null | undefined = useContext<T>(PropsContext)
|
||||
// only undefined if has no provider
|
||||
if (context === undefined) {
|
||||
throw new Error("useVoidProps missing Provider")
|
||||
}
|
||||
if (context === null) {
|
||||
throw new Error("useVoidProps had null props")
|
||||
}
|
||||
if (!(context instanceof Object)) {
|
||||
throw new Error("useVoidProps props was not an object")
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getVSCodeAPI, awaitVSCodeResponse, onMessageFromVSCode } from "./getVsc
|
||||
import { initPosthog, identifyUser } from "./posthog";
|
||||
import { ThreadsProvider } from "./contextForThreads";
|
||||
import { ConfigProvider } from "./contextForConfig";
|
||||
import { PropsProvider } from "./contextForProps";
|
||||
import { getPropsObj, PropsProvider } from "./contextForProps";
|
||||
|
||||
const ListenersAndTracking = () => {
|
||||
// initialize posthog
|
||||
@@ -51,7 +51,7 @@ export const mount = (children: React.ReactNode) => {
|
||||
const content = (<>
|
||||
<ListenersAndTracking />
|
||||
|
||||
<PropsProvider rootElement={rootElement}>
|
||||
<PropsProvider props={getPropsObj(rootElement)}>
|
||||
<ThreadsProvider>
|
||||
<ConfigProvider>
|
||||
{children}
|
||||
|
||||
@@ -11,18 +11,17 @@ export const DiffLine = () => {
|
||||
|
||||
const props = useVoidProps<props>()
|
||||
|
||||
console.log('props!', props)
|
||||
|
||||
if (!props) {
|
||||
return null
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const text = props.text
|
||||
let text = props?.text ?? ''
|
||||
|
||||
return <>
|
||||
<div>
|
||||
{text}
|
||||
<div className="view-line relative pointer-events-none">
|
||||
<div className={`absolute w-[100%] h-[100%] bg-[rgba(255,0,51,0.2)] z-10`}></div>
|
||||
<span>
|
||||
<span className='whitespace-pre text-[14px]'>
|
||||
{text}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
};
|
||||
|
||||
@@ -43,4 +43,4 @@ html {
|
||||
|
||||
.dropdown {
|
||||
@apply bg-vscode-dropdown-bg text-vscode-dropdown-foreground border-vscode-dropdown-border focus:outline-vscode-focus-border;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user