Skip to content

Commit fb1235b

Browse files
authored
Merge pull request #662 from ElianHugh/code-cells-PR
Code cells in .R files
2 parents f3a6bad + 60949e7 commit fb1235b

2 files changed

Lines changed: 65 additions & 53 deletions

File tree

src/extension.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<apiImp
3232
// is used to export an interface to the help panel
3333
// this export is used e.g. by vscode-r-debugger to show the help panel from within debug sessions
3434
const rExtension = new apiImplementation.RExtensionImplementation();
35-
35+
3636
// assign extension context to global variable
3737
extensionContext = context;
3838

@@ -41,7 +41,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<apiImp
4141
// create R terminal
4242
'r.createRTerm': rTerminal.createRTerm,
4343

44-
// run code from editor in terminal
44+
// run code from editor in terminal
4545
'r.nrow': () => rTerminal.runSelectionOrWord(['nrow']),
4646
'r.length': () => rTerminal.runSelectionOrWord(['length']),
4747
'r.head': () => rTerminal.runSelectionOrWord(['head']),
@@ -103,7 +103,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<apiImp
103103

104104
// (help related commands are registered in rHelp.initializeHelp)
105105
};
106-
for(const key in commands){
106+
for (const key in commands) {
107107
context.subscriptions.push(vscode.commands.registerCommand(key, commands[key]));
108108
}
109109

@@ -115,15 +115,15 @@ export async function activate(context: vscode.ExtensionContext): Promise<apiImp
115115
// register on-enter rule for roxygen comments
116116
const wordPattern = /(-?\d*\.\d\w*)|([^`~!@$^&*()=+[{\]}\\|;:'",<>/\s]+)/g;
117117
vscode.languages.setLanguageConfiguration('r', {
118-
onEnterRules: [
119-
{
120-
// Automatically continue roxygen comments: #'
121-
action: { indentAction: vscode.IndentAction.None, appendText: '#\' ' },
122-
beforeText: /^#'.*/,
123-
},
124-
],
125-
wordPattern,
126-
});
118+
onEnterRules: [
119+
{
120+
// Automatically continue roxygen comments: #'
121+
action: { indentAction: vscode.IndentAction.None, appendText: '#\' ' },
122+
beforeText: /^#'.*/,
123+
},
124+
],
125+
wordPattern,
126+
});
127127

128128
// initialize httpgd viewer
129129
globalHttpgdManager = httpgdViewer.initializeHttpgd();
@@ -133,7 +133,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<apiImp
133133

134134

135135
// register codelens and complmetion providers for r markdown
136-
vscode.languages.registerCodeLensProvider('rmd', new rmarkdown.RMarkdownCodeLensProvider());
136+
vscode.languages.registerCodeLensProvider(['r', 'rmd'], new rmarkdown.RMarkdownCodeLensProvider());
137137
vscode.languages.registerCompletionItemProvider('rmd', new rmarkdown.RMarkdownCompletionItemProvider(), ' ', ',');
138138

139139

@@ -148,13 +148,13 @@ export async function activate(context: vscode.ExtensionContext): Promise<apiImp
148148
vscode.tasks.registerTaskProvider(type, {
149149
provideTasks() {
150150
return [
151-
new vscode.Task({type: type}, vscode.TaskScope.Workspace, 'Check', 'R',
151+
new vscode.Task({ type: type }, vscode.TaskScope.Workspace, 'Check', 'R',
152152
new vscode.ShellExecution('Rscript -e "devtools::check()"')),
153-
new vscode.Task({type: type}, vscode.TaskScope.Workspace, 'Document', 'R',
153+
new vscode.Task({ type: type }, vscode.TaskScope.Workspace, 'Document', 'R',
154154
new vscode.ShellExecution('Rscript -e "devtools::document()"')),
155-
new vscode.Task({type: type}, vscode.TaskScope.Workspace, 'Install', 'R',
155+
new vscode.Task({ type: type }, vscode.TaskScope.Workspace, 'Install', 'R',
156156
new vscode.ShellExecution('Rscript -e "devtools::install()"')),
157-
new vscode.Task({type: type}, vscode.TaskScope.Workspace, 'Test', 'R',
157+
new vscode.Task({ type: type }, vscode.TaskScope.Workspace, 'Test', 'R',
158158
new vscode.ShellExecution('Rscript -e "devtools::test()"')),
159159
];
160160
},
@@ -195,10 +195,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<apiImp
195195
void vscode.commands.executeCommand('setContext', 'r.WorkspaceViewer:show', enableSessionWatcher);
196196

197197
// if session watcher is active, register dyamic completion provider
198-
const liveTriggerCharacters = ['', '[', '(', ',', '$', '@', '"', '\'' ];
198+
const liveTriggerCharacters = ['', '[', '(', ',', '$', '@', '"', '\''];
199199
vscode.languages.registerCompletionItemProvider('r', new completions.LiveCompletionItemProvider(), ...liveTriggerCharacters);
200200
}
201201

202202
return rExtension;
203203
}
204-

src/rmarkdown.ts

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,28 @@ import {
66
import { runChunksInTerm } from './rTerminal';
77
import { config } from './util';
88

9-
function isChunkStartLine(text: string) {
10-
if (text.match(/^\s*```+\s*\{\w+\s*.*$/g)) {
11-
return true;
9+
function isRDocument(document: TextDocument) {
10+
return (document.languageId === 'r');
11+
}
12+
13+
function isRChunkLine(text: string) {
14+
return (!!text.match(/^#+\s*%%/g));
15+
}
16+
17+
function isChunkStartLine(text: string, isRDoc: boolean) {
18+
if (isRDoc) {
19+
return (isRChunkLine(text));
20+
} else {
21+
return (!!text.match(/^\s*```+\s*\{\w+\s*.*$/g));
1222
}
13-
return false;
1423
}
1524

16-
function isChunkEndLine(text: string) {
17-
if (text.match(/^\s*```+\s*$/g)) {
18-
return true;
25+
function isChunkEndLine(text: string, isRDoc: boolean) {
26+
if (isRDoc) {
27+
return (isRChunkLine(text));
28+
} else {
29+
return (!!text.match(/^\s*```+\s*$/g));
1930
}
20-
return false;
2131
}
2232

2333
function getChunkLanguage(text: string) {
@@ -55,14 +65,14 @@ export class RMarkdownCodeLensProvider implements CodeLensProvider {
5565
const rmdCodeLensCommands: string[] = config().get('rmarkdown.codeLensCommands');
5666

5767
// Iterate through all code chunks for getting chunk information for both CodeLens and chunk background color (set by `editor.setDecorations`)
58-
for (let i = 1 ; i <= chunks.length ; i++) {
68+
for (let i = 1; i <= chunks.length; i++) {
5969
const chunk = chunks.filter(e => e.id === i)[0];
6070
const chunkRange = chunk.chunkRange;
6171
const line = chunk.startLine;
6272
chunkRanges.push(chunkRange);
6373

6474
// Enable/disable only CodeLens, without affecting chunk background color.
65-
if (config().get<boolean>('rmarkdown.enableCodeLens') && chunk.language === 'r') {
75+
if (config().get<boolean>('rmarkdown.enableCodeLens') && (chunk.language === 'r') || isRDocument(document)) {
6676
if (token.isCancellationRequested) {
6777
break;
6878
}
@@ -141,9 +151,9 @@ export class RMarkdownCodeLensProvider implements CodeLensProvider {
141151
// For user-specified options, both options and sort order are based on options specified in settings UI or settings.json.
142152
return this.codeLenses.
143153
filter(e => rmdCodeLensCommands.includes(e.command.command)).
144-
sort(function(a, b) {
154+
sort(function (a, b) {
145155
const sorted = rmdCodeLensCommands.indexOf(a.command.command) -
146-
rmdCodeLensCommands.indexOf(b.command.command);
156+
rmdCodeLensCommands.indexOf(b.command.command);
147157
return sorted;
148158
});
149159
}
@@ -176,18 +186,19 @@ function getChunks(document: TextDocument): RMarkdownChunk[] {
176186
let chunkLanguage: string = undefined;
177187
let chunkOptions: string = undefined;
178188
let chunkEval: boolean = undefined;
189+
const isRDoc = isRDocument(document);
179190

180191
while (line < lines.length) {
181192
if (chunkStartLine === undefined) {
182-
if (isChunkStartLine(lines[line])) {
193+
if (isChunkStartLine(lines[line], isRDoc)) {
183194
chunkId++;
184195
chunkStartLine = line;
185196
chunkLanguage = getChunkLanguage(lines[line]);
186197
chunkOptions = getChunkOptions(lines[line]);
187198
chunkEval = getChunkEval(chunkOptions);
188199
}
189200
} else {
190-
if (isChunkEndLine(lines[line])) {
201+
if (isChunkEndLine(lines[line], isRDoc)) {
191202
chunkEndLine = line;
192203

193204
const chunkRange = new Range(
@@ -211,10 +222,10 @@ function getChunks(document: TextDocument): RMarkdownChunk[] {
211222
});
212223

213224
chunkStartLine = undefined;
214-
}
215225
}
216-
line++;
217226
}
227+
line++;
228+
}
218229
return chunks;
219230
}
220231

@@ -225,21 +236,23 @@ function getCurrentChunk(chunks: RMarkdownChunk[], line: number): RMarkdownChunk
225236
// `- 1` to cover edge case when cursor is at 'chunk end line'
226237
let chunkEndLineAbove = line - 1;
227238

228-
while (chunkStartLineAtOrAbove >= 0 && !isChunkStartLine(lines[chunkStartLineAtOrAbove])) {
239+
const isRDoc = isRDocument(window.activeTextEditor.document);
240+
241+
while (chunkStartLineAtOrAbove >= 0 && !isChunkStartLine(lines[chunkStartLineAtOrAbove], isRDoc)) {
229242
chunkStartLineAtOrAbove--;
230243
}
231244

232-
while (chunkEndLineAbove >= 0 && !isChunkEndLine(lines[chunkEndLineAbove])) {
245+
while (chunkEndLineAbove >= 0 && !isChunkEndLine(lines[chunkEndLineAbove], isRDoc)) {
233246
chunkEndLineAbove--;
234247
}
235248

236249
// Case: Cursor is within chunk
237250
if (chunkEndLineAbove < chunkStartLineAtOrAbove) {
238251
line = chunkStartLineAtOrAbove;
239252
} else {
240-
// 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.
253+
// 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.
241254
let chunkStartLineBelow = line + 1;
242-
while (!isChunkStartLine(lines[chunkStartLineBelow])) {
255+
while (!isChunkStartLine(lines[chunkStartLineBelow], isRDoc)) {
243256
chunkStartLineBelow++;
244257
}
245258
line = chunkStartLineBelow;
@@ -287,32 +300,32 @@ function _getStartLine() {
287300
}
288301

289302
export async function runCurrentChunk(chunks: RMarkdownChunk[] = _getChunks(),
290-
line: number = _getStartLine()): Promise<void> {
303+
line: number = _getStartLine()): Promise<void> {
291304
const currentChunk = getCurrentChunk(chunks, line);
292305
await runChunksInTerm([currentChunk.codeRange]);
293306
}
294307

295308
export async function runPreviousChunk(chunks: RMarkdownChunk[] = _getChunks(),
296-
line: number = _getStartLine()): Promise<void> {
309+
line: number = _getStartLine()): Promise<void> {
297310
const previousChunk = getPreviousChunk(chunks, line);
298311
await runChunksInTerm([previousChunk.codeRange]);
299312
}
300313

301314
export async function runNextChunk(chunks: RMarkdownChunk[] = _getChunks(),
302-
line: number = _getStartLine()): Promise<void> {
315+
line: number = _getStartLine()): Promise<void> {
303316
const nextChunk = getNextChunk(chunks, line);
304317
await runChunksInTerm([nextChunk.codeRange]);
305318
}
306319

307320
export async function runAboveChunks(chunks: RMarkdownChunk[] = _getChunks(),
308-
line: number = _getStartLine()): Promise<void> {
321+
line: number = _getStartLine()): Promise<void> {
309322
const previousChunk = getPreviousChunk(chunks, line);
310323
const firstChunkId = 1;
311324
const previousChunkId = previousChunk.id;
312325

313326
const codeRanges: Range[] = [];
314327

315-
for (let i = firstChunkId ; i <= previousChunkId ; i++) {
328+
for (let i = firstChunkId; i <= previousChunkId; i++) {
316329
const chunk = chunks.filter(e => e.id === i)[0];
317330
if (chunk.eval) {
318331
codeRanges.push(chunk.codeRange);
@@ -322,15 +335,15 @@ export async function runAboveChunks(chunks: RMarkdownChunk[] = _getChunks(),
322335
}
323336

324337
export async function runBelowChunks(chunks: RMarkdownChunk[] = _getChunks(),
325-
line: number = _getStartLine()): Promise<void> {
338+
line: number = _getStartLine()): Promise<void> {
326339

327340
const nextChunk = getNextChunk(chunks, line);
328341
const nextChunkId = nextChunk.id;
329342
const lastChunkId = chunks.length;
330343

331344
const codeRanges: Range[] = [];
332345

333-
for (let i = nextChunkId ; i <= lastChunkId ; i++) {
346+
for (let i = nextChunkId; i <= lastChunkId; i++) {
334347
const chunk = chunks.filter(e => e.id === i)[0];
335348
if (chunk.eval) {
336349
codeRanges.push(chunk.codeRange);
@@ -340,14 +353,14 @@ export async function runBelowChunks(chunks: RMarkdownChunk[] = _getChunks(),
340353
}
341354

342355
export async function runCurrentAndBelowChunks(chunks: RMarkdownChunk[] = _getChunks(),
343-
line: number = _getStartLine()): Promise<void> {
356+
line: number = _getStartLine()): Promise<void> {
344357
const currentChunk = getCurrentChunk(chunks, line);
345358
const currentChunkId = currentChunk.id;
346359
const lastChunkId = chunks.length;
347360

348361
const codeRanges: Range[] = [];
349362

350-
for (let i = currentChunkId ; i <= lastChunkId ; i++) {
363+
for (let i = currentChunkId; i <= lastChunkId; i++) {
351364
const chunk = chunks.filter(e => e.id === i)[0];
352365
codeRanges.push(chunk.codeRange);
353366
}
@@ -361,7 +374,7 @@ export async function runAllChunks(chunks: RMarkdownChunk[] = _getChunks()): Pro
361374

362375
const codeRanges: Range[] = [];
363376

364-
for (let i = firstChunkId ; i <= lastChunkId ; i++) {
377+
for (let i = firstChunkId; i <= lastChunkId; i++) {
365378
const chunk = chunks.filter(e => e.id === i)[0];
366379
if (chunk.eval) {
367380
codeRanges.push(chunk.codeRange);
@@ -377,19 +390,19 @@ function goToChunk(chunk: RMarkdownChunk) {
377390
}
378391

379392
export function goToPreviousChunk(chunks: RMarkdownChunk[] = _getChunks(),
380-
line: number = _getStartLine()): void {
393+
line: number = _getStartLine()): void {
381394
const previousChunk = getPreviousChunk(chunks, line);
382395
goToChunk(previousChunk);
383396
}
384397

385398
export function goToNextChunk(chunks: RMarkdownChunk[] = _getChunks(),
386-
line: number = _getStartLine()): void {
399+
line: number = _getStartLine()): void {
387400
const nextChunk = getNextChunk(chunks, line);
388401
goToChunk(nextChunk);
389402
}
390403

391404
export function selectCurrentChunk(chunks: RMarkdownChunk[] = _getChunks(),
392-
line: number = _getStartLine()): void {
405+
line: number = _getStartLine()): void {
393406
const editor = window.activeTextEditor;
394407
const currentChunk = getCurrentChunk__CursorWithinChunk(chunks, line);
395408
const lines = editor.document.getText().split(/\r?\n/);
@@ -426,7 +439,7 @@ export class RMarkdownCompletionItemProvider implements CompletionItemProvider {
426439

427440
public provideCompletionItems(document: TextDocument, position: Position): CompletionItem[] {
428441
const line = document.lineAt(position).text;
429-
if (isChunkStartLine(line) && getChunkLanguage(line) === 'r') {
442+
if (isChunkStartLine(line, false) && getChunkLanguage(line) === 'r') {
430443
return this.chunkOptionCompletionItems;
431444
}
432445

0 commit comments

Comments
 (0)