@@ -25,18 +25,26 @@ function isChunkStartLine(text: string, isRDoc: boolean) {
2525
2626function isChunkEndLine ( text : string , isRDoc : boolean ) {
2727 if ( isRDoc ) {
28- return ( isRChunkLine ( text ) ) ;
28+ const isSectionHeader = text . match ( / ^ # + \s * .* [ - # + = * ] { 4 , } / g) ;
29+ return ( isRChunkLine ( text ) || isSectionHeader ) ;
2930 } else {
3031 return ( ! ! text . match ( / ^ \s * ` ` ` + \s * $ / g) ) ;
3132 }
3233}
3334
34- function getChunkLanguage ( text : string ) {
35+ function getChunkLanguage ( text : string , isRDoc : boolean = false ) {
36+ if ( isRDoc ) {
37+ return 'r' ;
38+ }
3539 return text . replace ( / ^ \s * ` ` ` + \s * \{ ( \w + ) \s * .* \} \s * $ / g, '$1' ) . toLowerCase ( ) ;
3640}
3741
38- function getChunkOptions ( text : string ) {
39- return text . replace ( / ^ \s * ` ` ` + \s * \{ \w + \s * , ? \s * ( .* ) \s * \} \s * $ / g, '$1' ) ;
42+ function getChunkOptions ( text : string , isRDoc : boolean = false ) {
43+ if ( isRDoc ) {
44+ return text . replace ( / ^ # + \s * % % / g, '' ) ;
45+ } else {
46+ return text . replace ( / ^ \s * ` ` ` + \s * \{ \w + \s * , ? \s * ( .* ) \s * \} \s * $ / g, '$1' ) ;
47+ }
4048}
4149
4250function getChunkEval ( chunkOptions : string ) {
@@ -184,6 +192,7 @@ export function getChunks(document: vscode.TextDocument): RMarkdownChunk[] {
184192 let chunkId = 0 ; // One-based index
185193 let chunkStartLine : number | undefined = undefined ;
186194 let chunkEndLine : number | undefined = undefined ;
195+ let codeEndLine : number | undefined = undefined ;
187196 let chunkLanguage : string | undefined = undefined ;
188197 let chunkOptions : string | undefined = undefined ;
189198 let chunkEval : boolean | undefined = undefined ;
@@ -195,20 +204,30 @@ export function getChunks(document: vscode.TextDocument): RMarkdownChunk[] {
195204 chunkId ++ ;
196205 chunkStartLine = line ;
197206 chunkLanguage = getChunkLanguage ( lines [ line ] ) ;
198- chunkOptions = getChunkOptions ( lines [ line ] ) ;
207+ chunkOptions = getChunkOptions ( lines [ line ] , isRDoc ) ;
199208 chunkEval = getChunkEval ( chunkOptions ) ;
200209 }
201210 } else {
202- if ( isChunkEndLine ( lines [ line ] , isRDoc ) ) {
211+ // Second condition is for the last chunk in an .R file
212+ const isRDocAndFinalLine = ( isRDoc && line === lines . length - 1 ) ;
213+ if ( isChunkEndLine ( lines [ line ] , isRDoc ) || isRDocAndFinalLine ) {
203214 chunkEndLine = line ;
204-
215+ codeEndLine = line - 1 ;
216+
217+ // isChunkEndLine looks for `# %%` in `.R` files, so if found, then need to go back one line to mark end of code chunk.
218+ if ( isRDoc && ! isRDocAndFinalLine ) {
219+ chunkEndLine = chunkEndLine - 1 ;
220+ codeEndLine = chunkEndLine ;
221+ line = line - 1 ;
222+ }
223+
205224 const chunkRange = new vscode . Range (
206225 new vscode . Position ( chunkStartLine , 0 ) ,
207226 new vscode . Position ( line , lines [ line ] . length )
208227 ) ;
209228 const codeRange = new vscode . Range (
210229 new vscode . Position ( chunkStartLine + 1 , 0 ) ,
211- new vscode . Position ( line - 1 , lines [ line - 1 ] . length )
230+ new vscode . Position ( codeEndLine , lines [ codeEndLine ] . length )
212231 ) ;
213232
214233 chunks . push ( {
@@ -237,35 +256,27 @@ function getCurrentChunk(chunks: RMarkdownChunk[], line: number): RMarkdownChunk
237256 return ;
238257 }
239258
240- const lines = textEditor . document . getText ( ) . split ( / \r ? \n / ) ;
241-
242- let chunkStartLineAtOrAbove = line ;
243- // `- 1` to cover edge case when cursor is at 'chunk end line'
244- let chunkEndLineAbove = line - 1 ;
245-
246- const isRDoc = isRDocument ( textEditor . document ) ;
247-
248- while ( chunkStartLineAtOrAbove >= 0 && ! isChunkStartLine ( lines [ chunkStartLineAtOrAbove ] , isRDoc ) ) {
249- chunkStartLineAtOrAbove -- ;
250- }
251-
252- while ( chunkEndLineAbove >= 0 && ! isChunkEndLine ( lines [ chunkEndLineAbove ] , isRDoc ) ) {
253- chunkEndLineAbove -- ;
259+ // Case: If `chunks` is empty, return undefined
260+ if ( chunks . length === 0 ) {
261+ return undefined ;
254262 }
255-
256- // Case: Cursor is within chunk
257- if ( chunkEndLineAbove < chunkStartLineAtOrAbove ) {
258- line = chunkStartLineAtOrAbove ;
259- } else {
260- // Cases: Cursor is above the first chunk, at the first chunk or outside of chunk. Find the 'chunk start line' of the next chunk below the cursor.
261- let chunkStartLineBelow = line + 1 ;
262- while ( ! isChunkStartLine ( lines [ chunkStartLineBelow ] , isRDoc ) ) {
263- chunkStartLineBelow ++ ;
263+
264+ // Case: Cursor is above first chunk, use first chunk
265+ if ( line < chunks [ 0 ] . startLine ) {
266+ return chunks [ 0 ] ;
267+ }
268+ // Case: Cursor is below last chunk, return last chunk
269+ if ( line > chunks [ chunks . length - 1 ] . endLine ) {
270+ return chunks [ chunks . length - 1 ] ;
271+ }
272+ // chunks.filter(i => line >= i.startLine)[0];
273+ for ( const chunk of chunks ) {
274+ // Case: Cursor is within chunk, use current chunk
275+ // Case: Cursor is between, use next chunk below cursor
276+ if ( chunk . endLine >= line ) {
277+ return chunk ;
264278 }
265- line = chunkStartLineBelow ;
266279 }
267- const currentChunk = chunks . find ( i => i . startLine <= line && i . endLine >= line ) ;
268- return currentChunk ;
269280}
270281
271282// Alternative `getCurrentChunk` for cases:
@@ -335,7 +346,11 @@ export async function runPreviousChunk(chunks: RMarkdownChunk[] = _getChunks(),
335346 const currentChunk = getCurrentChunk ( chunks , line ) ;
336347 const previousChunk = getPreviousChunk ( chunks , line ) ;
337348
338- if ( previousChunk && previousChunk !== currentChunk ) {
349+ // Case: cursor is below the last chunk, run last chunk
350+ if ( currentChunk && line > currentChunk . endLine ) {
351+ await ( runChunksInTerm ( [ currentChunk . codeRange ] ) ) ;
352+ // Case: currentChunk is not the first chunk, so run previousChunk
353+ } else if ( previousChunk && previousChunk !== currentChunk ) {
339354 await runChunksInTerm ( [ previousChunk . codeRange ] ) ;
340355 }
341356
@@ -346,6 +361,7 @@ export async function runNextChunk(chunks: RMarkdownChunk[] = _getChunks(),
346361 const currentChunk = getCurrentChunk ( chunks , line ) ;
347362 const nextChunk = getNextChunk ( chunks , line ) ;
348363
364+ // Case: currentChunk is not the last chunk, so run nextChunk
349365 if ( nextChunk && nextChunk !== currentChunk ) {
350366 await runChunksInTerm ( [ nextChunk . codeRange ] ) ;
351367 }
@@ -363,7 +379,8 @@ export async function runAboveChunks(chunks: RMarkdownChunk[] = _getChunks(),
363379
364380 const codeRanges : vscode . Range [ ] = [ ] ;
365381
366- if ( previousChunk !== currentChunk ) {
382+ // Only do something if current chunk is not the first chunk
383+ if ( currentChunk . id > 1 ) {
367384 for ( let i = firstChunkId ; i <= previousChunkId ; i ++ ) {
368385 const chunk = chunks . find ( e => e . id === i ) ;
369386 if ( chunk ?. eval ) {
@@ -386,7 +403,9 @@ export async function runBelowChunks(chunks: RMarkdownChunk[] = _getChunks(),
386403 const lastChunkId = chunks . length ;
387404
388405 const codeRanges : vscode . Range [ ] = [ ] ;
389- if ( nextChunk !== currentChunk ) {
406+
407+ // Only do something if current chunk is not the last chunk
408+ if ( currentChunk . id < lastChunkId ) {
390409 for ( let i = nextChunkId ; i <= lastChunkId ; i ++ ) {
391410 const chunk = chunks . find ( e => e . id === i ) ;
392411 if ( chunk ?. eval ) {
0 commit comments