@@ -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
2435async 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
5384async 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)
122153let 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
143156function 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