Skip to content
Merged
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
190 changes: 91 additions & 99 deletions content.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ async function sendToOtherService(code) {
}
})
})
console.log("[prompt]", JSON.stringify(prompt));
const data = await response.json();
if (/^\n/.test(data)) {
data = data.replace(/^\n/, '');
Expand All @@ -79,14 +78,6 @@ async function sendToOtherService(code) {
return data[0].generated_text;
}

// 请求完毕后准备填入的代码
let readyToFillCode = ""
// 是否正在请求(防止多次请求等问题)
let isRequtest = false
// 是否请求成功(防止用户在本次未请求完就按下Tab)
let isRequtestSuccess = false
// 请求时的输入框 (提供一边请求一边去别的单元格写代码的功能)
let requestingTextarea = null


async function getCodeCompletion(code) {
Expand Down Expand Up @@ -121,6 +112,58 @@ async function getOtherServiceUrlWrapper(code) {
return await sendToOtherService(code)
}

// Code to be filled in after request completion
let codeToFill = "";
// Flag indicating whether a request is in progress (prevents multiple requests, etc.)
let isRequestInProgress = false;
// Flag indicating whether the request was successful (prevents filling in code before the request is complete)
let isRequestSuccessful = false;
// Textarea during the request (allows writing code in other cells while the request is in progress)
let activeRequestTextarea = null;

// Adds an event listener for filling in code after the request is completed
const addFillCodeKeyListener = (event) => {
if (event.ctrlKey && !isRequestInProgress && isRequestSuccessful) {
event.preventDefault();

// Get the previously existing animated text element (if any)
// If it doesn't exist, it's assumed that the user doesn't need the code
const animationElementList = document.querySelectorAll(".per-insert-code");

// If the animated text element exists, it's assumed that the user wants to insert the code into the code block
if (animationElementList.length === 1) {
insertSuggestion(codeToFill);
}

// Reset the request successful flag
isRequestSuccessful = false;
}
};

function insertSuggestion(suggestion) {
// Focus the textarea, otherwise, it is not possible to insert the suggestion using the Tab key from another location
activeRequestTextarea.focus();

// Get the current cursor position
const cursorPosition = activeRequestTextarea.selectionStart;

// Insert the suggestion at the cursor position
const newValue = activeRequestTextarea.value.slice(0, cursorPosition) + suggestion + activeRequestTextarea.value.slice(cursorPosition);
activeRequestTextarea.value = newValue;

// Update the cursor position after inserting the suggestion
const newCursorPosition = cursorPosition + suggestion.length;
activeRequestTextarea.selectionStart = activeRequestTextarea.selectionEnd = cursorPosition + suggestion.length;

// Trigger an input event on the textarea to update the CodeMirror instance
const event = new Event('input', { bubbles: true, cancelable: true });
activeRequestTextarea.dispatchEvent(event);

// Trigger a keydown event with Tab key to perform auto-indentation
const tabEvent = new KeyboardEvent('keydown', { key: 'Tab' });
activeRequestTextarea.dispatchEvent(tabEvent);
}

// Check if the current page is a Jupyter Notebook
if (document.querySelector('body.notebook_app')) {

Expand All @@ -130,32 +173,32 @@ if (document.querySelector('body.notebook_app')) {
// 防止默认事件
event.preventDefault();

if (isRequtest || isRequtestSuccess) {
if (isRequestInProgress || isRequestSuccessful) {
return
}

// 获取当前输入框的Textarea
const activeTextarea = document.activeElement;

requestingTextarea = activeTextarea
activeRequestTextarea = activeTextarea

// 从当前输入框的Textarea获取当前输入框(单元格)
const activeCell = activeTextarea.parentElement.parentElement

if (activeCell) {
// Retrieve the content of the active cell
const code = getCellContent(activeCell);
const code = getCellContentText(activeCell);

// 开始动画
const [animationInterval, animationElement] = startWaitingAnimation(activeCell)

isRequtest = true
isRequestInProgress = true
const suggestion = await getCodeCompletion(code)
if (suggestion) {
clearInterval(animationInterval)
isRequtestSuccess = true
isRequtest = false
readyToFillCode = suggestion
isRequestSuccessful = true
isRequestInProgress = false
codeToFill = suggestion

// 将文字动画框的内容替换成code
animationElement.innerHTML = suggestion
Expand All @@ -165,102 +208,51 @@ if (document.querySelector('body.notebook_app')) {
}
}
});

function getCellContent(cell) {
const allCells = document.querySelectorAll('.cell .input_area .CodeMirror');
const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre');
const contextContent = getPreviousCellsContent(cell, allCells);

const content = [];

codeMirrorLines.forEach((line) => {
content.push(line.textContent);
});

const cellContent = content.join('\n');
console.log("[context]", JSON.stringify(contextContent))
console.log("[cell]", JSON.stringify(cellContent))
if (contextContent) {
return contextContent + "<jupyter_code>" + cellContent;
}
return cellContent;
// return contextContent + '\n' + cellContent;
}
}


// 添加tab监听器,用户请求完毕后按下tab键填入代码
const addTabEvent = (event) => {
if (event.ctrlKey && !isRequtest && isRequtestSuccess) {
event.preventDefault();

// 获取之前的动画文字Dom(有且只能存在一个或者不存在),如果之前的动画文字框不存在,则在逻辑中认为用户不需要这段代码
const animationElementList = document.querySelectorAll(".per-insert-code")

// 动画文字Dom存在,则在逻辑中认为用户想把这段代码插入到代码块中
if(animationElementList.length == 1){
insertSuggestion(readyToFillCode)
}

//关闭请求成功的状态
isRequtestSuccess = false
}
document.addEventListener('keydown', addFillCodeKeyListener);
}

function getCellContentText(activeCell) {

document.addEventListener('keydown', addTabEvent)
const cellElements = Array.from(document.querySelectorAll('.cell'));
const activeCellIndex = cellElements.findIndex(cell => cell.contains(activeCell));
// Check if there are at least 3 cells before the active cell

function insertSuggestion(suggestion) {
// 获取焦点,否则无法从其他位置按下Tab插入
requestingTextarea.focus();

// Get the current cursor position
const cursorPosition = requestingTextarea.selectionStart;

// Insert the suggestion at the cursor position
const newValue = requestingTextarea.value.slice(0, cursorPosition) + suggestion + requestingTextarea.value.slice(cursorPosition);
requestingTextarea.value = newValue;
let combinedContent = "<start_jupyter>";

// Update the cursor position after inserting the suggestion
const newCursorPosition = cursorPosition + suggestion.length;
requestingTextarea.selectionStart = requestingTextarea.selectionEnd = cursorPosition + suggestion.length;
// Iterate through the last 3 cells before the active cell
const startIndex = activeCellIndex - 3 < 0 ? 0 : activeCellIndex - 3;
for (let i = startIndex; i <= activeCellIndex; i++) {
const cellElement = cellElements[i];

// Trigger an input event on the textarea to update the CodeMirror instance
const event = new Event('input', { bubbles: true, cancelable: true });
requestingTextarea.dispatchEvent(event);

// Trigger a keydown event with Tab key to perform auto-indentation
const tabEvent = new KeyboardEvent('keydown', { key: 'Tab' });
requestingTextarea.dispatchEvent(tabEvent);
}



function getPreviousCellsContent(activeCell, allCells) {
const previousCellsContent = [];
let codeCellCount = 0;

for (const cell of allCells) {
// Stop when the active cell is reached or when three code cells have been processed
if (cell === activeCell) break;

// Check if the cell is a code cell
const isCodeCell = cell.closest('.cell').classList.contains('code_cell');
if (isCodeCell) {
const cellContent = getCellContent(cell);
previousCellsContent.push(cellContent); // Add the content to the end of the array
codeCellCount++;
if (cellElement.classList.contains('code_cell')) {
const code = extractTextFromCell(cellElement);
combinedContent += `<jupyter_code>${code}`;
const outputElement = cellElement.querySelector('.output_subarea');
if (outputElement) {
if (i !== activeCellIndex) {
combinedContent += `<jupyter_output>`;
combinedContent += outputElement.textContent;
}
}
} else if (cellElement.classList.contains('text_cell')) {
const text = extractTextFromCell(cellElement);
combinedContent += `<jupyter_text>${text}`;
}
}

// Reverse the array to have the content in the correct order
const lastThreeCellsContent = previousCellsContent.slice(-3);

return lastThreeCellsContent.join('<jupyter_code>');
return combinedContent;
}

function extractTextFromCell(cell) {
const codeMirrorLines = cell.querySelectorAll('.CodeMirror-code pre');
const content = [];

codeMirrorLines.forEach((line) => {
content.push(line.textContent);
});

return content.join('\n');
}

// 开始等待动画,有30s等待时间,如果等待时间过了,出现“error”字体,返回两个值如下,接收:"const [animationInterval, animationElement] = startWaitingAnimation(activeCall)"
// 1. animationInterval(interval, 动画计时器),可使用clearInterval(animationInterval)消除动画, 每次请求完毕必须要关掉
Expand Down
Loading