Skip to content

Commit 7cffbe8

Browse files
authored
Merge pull request #5 from Wzixiao/main
Add the function of splitting the left and right code of the cursor
2 parents 55abb37 + e8d0406 commit 7cffbe8

File tree

4 files changed

+260
-74
lines changed

4 files changed

+260
-74
lines changed

background.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,11 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
1717
});
1818
return true; // Required to use sendResponse asynchronously
1919
}
20+
else if(request.type === "getmodelType"){
21+
chrome.storage.sync.get("modelType", (data) => {
22+
sendResponse({ modelType: data.modelType });
23+
});
24+
return true; // Required to use sendResponse asynchronously
25+
}
2026
});
2127

content.js

Lines changed: 170 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,66 @@ async function getChecked() {
1919
});
2020
});
2121
}
22+
async function getmodelType() {
23+
return new Promise((resolve) => {
24+
chrome.runtime.sendMessage({ type: "getmodelType" }, (response) => {
25+
resolve(response.modelType);
26+
});
27+
});
28+
}
29+
30+
// Use a regular expression to match the content between triple backticks
31+
const codeBlockRegex = /```([\s\S]*)```/;
32+
const codeHalfBlockRegex = /```([\s\S]*)/;
2233

2334
// Function to send request to OpenAI API
2435
async function sendToOpenAI(prompt) {
2536
const apiKey = await getOpenAIKey();
37+
const modelType = await getmodelType();
2638
if (!apiKey) {
2739
// 总是忘记填写。。所以等了半天总是以为网络错误,改成了alert
2840
alert("OpenAI API key not set.");
2941
return;
42+
}else if(!modelType) {
43+
alert("modelType not set.");
44+
return;
3045
}
31-
const response = await fetch("https://api.openai.com/v1/chat/completions", {
46+
const response = await fetch("https://api.openai.com/v1/completions", {
3247
method: "POST",
3348
headers: {
3449
"Content-Type": "application/json",
3550
"Authorization": `Bearer ${apiKey}`,
3651
},
3752
body: JSON.stringify({
38-
model: "gpt-3.5-turbo",
39-
messages: [{ role: "user", content: prompt }],
53+
model: modelType,
54+
prompt,
55+
temperature: 0.1,
56+
max_tokens: 40,
4057
}),
4158
// 添加一个最大请求时间,目前没做相关于超时的处理,但如果超时了,就会在请求的单元格展示error
4259
timeout: 30000
43-
4460
});
61+
4562
const data = await response.json();
46-
const suggestion = data.choices && data.choices[0] && data.choices[0].message.content;
47-
// Use a regular expression to match the content between triple backticks
48-
const codeBlockRegex = /```([\s\S]*?)```/g;
49-
const match = codeBlockRegex.exec(suggestion);
50-
return match && match[1] ? match[1].replace(/\u200B/g, '') : '';
63+
const suggestion = data.choices && data.choices[0] && data.choices[0].text;
64+
65+
66+
// don't know how many "```" exists, It is related to the token and also related to the model
67+
let count = (suggestion.match(/```/g) || []).length;
68+
let code = ""
69+
switch(count){
70+
case 1:
71+
var match = suggestion.match(codeHalfBlockRegex)
72+
code = match && match[1] ? match[1].replace(/^\n\s*/, '').replace(/\n.*$/, '').replace(/\u200B/g, ''):""
73+
break;
74+
case 2:
75+
var match = suggestion.match(codeBlockRegex)
76+
code = match && match[1] ? match[1].replace(/^\n\s*/, '').replace(/\n.*$/, '').replace(/\u200B/g, ''):""
77+
break;
78+
default:code = suggestion;
79+
}
80+
81+
return code
5182
}
5283

5384
async function sendToOtherService(code) {
@@ -121,24 +152,6 @@ let isRequestSuccessful = false;
121152
// Textarea during the request (allows writing code in other cells while the request is in progress)
122153
let activeRequestTextarea = null;
123154

124-
// Adds an event listener for filling in code after the request is completed
125-
const addFillCodeKeyListener = (event) => {
126-
if (event.ctrlKey && !isRequestInProgress && isRequestSuccessful) {
127-
event.preventDefault();
128-
129-
// Get the previously existing animated text element (if any)
130-
// If it doesn't exist, it's assumed that the user doesn't need the code
131-
const animationElementList = document.querySelectorAll(".per-insert-code");
132-
133-
// If the animated text element exists, it's assumed that the user wants to insert the code into the code block
134-
if (animationElementList.length === 1) {
135-
insertSuggestion(codeToFill);
136-
}
137-
138-
// Reset the request successful flag
139-
isRequestSuccessful = false;
140-
}
141-
};
142155

143156
function insertSuggestion(suggestion) {
144157
// Focus the textarea, otherwise, it is not possible to insert the suggestion using the Tab key from another location
@@ -164,61 +177,59 @@ function insertSuggestion(suggestion) {
164177
activeRequestTextarea.dispatchEvent(tabEvent);
165178
}
166179

167-
// Check if the current page is a Jupyter Notebook
168-
if (document.querySelector('body.notebook_app')) {
169-
170-
document.addEventListener('keydown', async (event) => {
171-
// Check if the Ctrl + Space keys were pressed
172-
if (event.ctrlKey && event.code === 'Space') {
173-
// 防止默认事件
174-
event.preventDefault();
175180

176-
if (isRequestInProgress || isRequestSuccessful) {
177-
return
178-
}
179181

180-
// 获取当前输入框的Textarea
181-
const activeTextarea = document.activeElement;
182+
const getActiveCellPointerCode = (activeCell) => {
183+
let leftContext = ""
184+
let rightContext = ""
182185

183-
activeRequestTextarea = activeTextarea
186+
// get cursor element
187+
const cursorElement = activeCell.querySelector('div.CodeMirror-cursor')
184188

185-
// 从当前输入框的Textarea获取当前输入框(单元格)
186-
const activeCell = activeTextarea.parentElement.parentElement
189+
const style = window.getComputedStyle(cursorElement);
187190

188-
if (activeCell) {
189-
// Retrieve the content of the active cell
190-
const code = getCellContentText(activeCell);
191+
// 指针所在位置的偏移量
192+
const cursorOffsetLeft = Math.round(parseFloat(style.getPropertyValue('left')))
191193

192-
// 开始动画
193-
const [animationInterval, animationElement] = startWaitingAnimation(activeCell)
194-
195-
isRequestInProgress = true
196-
const suggestion = await getCodeCompletion(code)
197-
if (suggestion) {
198-
clearInterval(animationInterval)
199-
isRequestSuccessful = true
200-
isRequestInProgress = false
201-
codeToFill = suggestion
202-
203-
// 将文字动画框的内容替换成code
204-
animationElement.innerHTML = suggestion
205-
}
194+
// Which line
195+
const lineIndex = Math.round(parseFloat(style.getPropertyValue('top')) / 17)
196+
// Obtain element for all line
197+
const linesElement = activeCell.getElementsByClassName('CodeMirror-line')
198+
// code dom element length in active line
199+
const codeElementWdth = linesElement[lineIndex].querySelector("span").offsetWidth
206200

201+
// Determine whether the pointer is at the end of a line, Because there is a left marring, so -4, but due to precision issues so -3
202+
if(cursorOffsetLeft - 3 < codeElementWdth){
203+
return [null, null]
204+
}
207205

206+
for (let i = 0; i < linesElement.length; i++) {
207+
if(i <= lineIndex) {
208+
leftContext += linesElement[i].textContent + "\n"
209+
}else {
210+
rightContext += linesElement[i].textContent + "\n"
208211
}
209212
}
210-
});
211-
document.addEventListener('keydown', addFillCodeKeyListener);
213+
214+
return [leftContext, rightContext]
212215
}
213216

214-
function getCellContentText(activeCell) {
215217

218+
219+
function getCellContentText(activeCell) {
216220
const cellElements = Array.from(document.querySelectorAll('.cell'));
217221
const activeCellIndex = cellElements.findIndex(cell => cell.contains(activeCell));
218222
// Check if there are at least 3 cells before the active cell
219223

220224
let combinedContent = "<start_jupyter>";
221225

226+
// LeftContext refers to the left side of the pointer, and vice versa, If both are null, it is determined that the pointer is not at the far right
227+
const [leftContext, rightContext] = getActiveCellPointerCode(activeCell)
228+
229+
if(!leftContext && !rightContext){
230+
return null
231+
}
232+
222233
// Iterate through the last 3 cells before the active cell
223234
const startIndex = activeCellIndex - 3 < 0 ? 0 : activeCellIndex - 3;
224235
for (let i = startIndex; i <= activeCellIndex; i++) {
@@ -238,6 +249,7 @@ function getCellContentText(activeCell) {
238249
const text = extractTextFromCell(cellElement);
239250
combinedContent += `<jupyter_text>${text}`;
240251
}
252+
241253
}
242254

243255
return combinedContent;
@@ -254,23 +266,38 @@ function extractTextFromCell(cell) {
254266
return content.join('\n');
255267
}
256268

269+
270+
257271
// 开始等待动画,有30s等待时间,如果等待时间过了,出现“error”字体,返回两个值如下,接收:"const [animationInterval, animationElement] = startWaitingAnimation(activeCall)"
258272
// 1. animationInterval(interval, 动画计时器),可使用clearInterval(animationInterval)消除动画, 每次请求完毕必须要关掉
259273
// 2. animationElement (dom, 动画字体节点), animationElement.innerHTML = xxx 来赋值
260-
const startWaitingAnimation = (activeCall) => {
261-
const activeCell = activeCall.querySelectorAll('.CodeMirror-scroll .CodeMirror-code');
262-
263-
const lastElement = activeCell[activeCell.length - 1].querySelector('.CodeMirror-line:last-child');
264-
265-
// 设置它等待时的动画字体dom元素
274+
const startWaitingAnimation = (activeCell) => {
275+
276+
// get cursor element
277+
const cursorElement = activeCell.querySelector('div.CodeMirror-cursor')
278+
const style = window.getComputedStyle(cursorElement);
279+
// Which line
280+
const lineIndex = Math.round(parseFloat(style.getPropertyValue('top')) / 17)
281+
// Obtain element for all line
282+
const linesElement = activeCell.getElementsByClassName('CodeMirror-line')
283+
const currectLineSpanList = linesElement[lineIndex].querySelectorAll('span span')
284+
285+
// Set the animated font dom element when it waits
266286
const animationElement = document.createElement('span');
267287

268288
animationElement.classList.add("per-insert-code")
269289
animationElement.style.color = 'grey';
270290

271-
lastElement.appendChild(animationElement);
291+
// If it is a blank line
292+
if(currectLineSpanList.length == 0){
293+
const withAllCodeSpan = linesElement[lineIndex].querySelectorAll('span')
294+
withAllCodeSpan[withAllCodeSpan.length-1].appendChild(animationElement)
295+
}else{
296+
currectLineSpanList[currectLineSpanList.length-1].insertAdjacentElement('afterend', animationElement);
297+
}
298+
272299

273-
// 等待步数,每步0.333s
300+
// Waiting steps, 0.333 seconds per step
274301
let timeLeft = 90;
275302
const animationInterval = setInterval(() => {
276303
let animatedText = ''
@@ -290,7 +317,7 @@ const startWaitingAnimation = (activeCall) => {
290317

291318
animationElement.innerHTML = ' ' + animatedText + " time left: " + Math.floor(timeLeft-- / 3) + "s"
292319

293-
// 请求失败
320+
// request timeout
294321
if (timeLeft <= 0) {
295322
animationElement.innerHTML = "error"
296323
clearInterval(animationInterval)
@@ -301,3 +328,74 @@ const startWaitingAnimation = (activeCall) => {
301328
}
302329

303330

331+
// Adds an event listener for filling in code after the request is completed
332+
const addFillCodeKeyListener = (event) => {
333+
if (event.ctrlKey && !isRequestInProgress && isRequestSuccessful) {
334+
event.preventDefault();
335+
336+
// Get the previously existing animated text element (if any)
337+
// If it doesn't exist, it's assumed that the user doesn't need the code
338+
const animationElementList = document.querySelectorAll(".per-insert-code");
339+
340+
// If the animated text element exists, it's assumed that the user wants to insert the code into the code block
341+
if (animationElementList.length === 1) {
342+
// delete animation element
343+
animationElementList[0].remove()
344+
345+
insertSuggestion(codeToFill);
346+
}
347+
348+
// Reset the request successful flag
349+
isRequestSuccessful = false;
350+
}
351+
};
352+
353+
354+
// Check if the current page is a Jupyter Notebook
355+
if (document.querySelector('body.notebook_app')) {
356+
357+
document.addEventListener('keydown', async (event) => {
358+
// Check if the Ctrl + Space keys were pressed
359+
if (event.ctrlKey && event.code === 'Space') {
360+
// Block default events
361+
event.preventDefault();
362+
363+
if (isRequestInProgress || isRequestSuccessful) {
364+
return
365+
}
366+
367+
//Obtain the Textarea of the current input box
368+
const activeTextarea = document.activeElement;
369+
370+
activeRequestTextarea = activeTextarea
371+
372+
// Obtain the current input box (cell) from the Textarea of the current input box
373+
const activeCell = activeTextarea.parentElement.parentElement
374+
375+
// Retrieve the content of the active cell
376+
const code = getCellContentText(activeCell);
377+
378+
if (!code) return;
379+
380+
if (activeCell) {
381+
// Start Animation
382+
const [animationInterval, animationElement] = startWaitingAnimation(activeCell)
383+
384+
isRequestInProgress = true
385+
const suggestion = await getCodeCompletion(code)
386+
387+
if (suggestion) {
388+
clearInterval(animationInterval)
389+
isRequestSuccessful = true
390+
isRequestInProgress = false
391+
codeToFill = suggestion
392+
393+
// Replace the content of the text animation box with code
394+
animationElement.innerHTML = suggestion
395+
}
396+
397+
}
398+
}
399+
});
400+
document.addEventListener('keydown', addFillCodeKeyListener);
401+
}

0 commit comments

Comments
 (0)