-
Notifications
You must be signed in to change notification settings - Fork 138
Expand file tree
/
Copy pathextension.ts
More file actions
259 lines (219 loc) · 13.3 KB
/
extension.ts
File metadata and controls
259 lines (219 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
'use strict';
// interfaces, functions, etc. provided by vscode
import * as vscode from 'vscode';
import * as os from 'os';
import path = require('path');
// functions etc. implemented in this extension
import * as preview from './preview';
import * as rGitignore from './rGitignore';
import * as lintrConfig from './lintrConfig';
import * as cppProperties from './cppProperties';
import * as rTerminal from './rTerminal';
import * as session from './session';
import * as util from './util';
import * as rstudioapi from './rstudioapi';
import * as rmarkdown from './rmarkdown';
import * as workspaceViewer from './workspaceViewer';
import * as apiImplementation from './apiImplementation';
import * as rHelp from './helpViewer';
import * as completions from './completions';
import * as rShare from './liveShare';
import * as httpgdViewer from './plotViewer';
import * as languageService from './languageService';
import { RTaskProvider } from './tasks';
// global objects used in other files
export const homeExtDir = (): string => util.getDir(path.join(os.homedir(), '.vscode-R'));
export const tmpDir = (): string => util.getDir(path.join(homeExtDir(), 'tmp'));
export let rWorkspace: workspaceViewer.WorkspaceDataProvider | undefined = undefined;
export let globalRHelp: rHelp.RHelp | undefined = undefined;
export let extensionContext: vscode.ExtensionContext;
export let enableSessionWatcher: boolean | undefined = undefined;
export let globalHttpgdManager: httpgdViewer.HttpgdManager | undefined = undefined;
export let rmdPreviewManager: rmarkdown.RMarkdownPreviewManager | undefined = undefined;
export let rmdKnitManager: rmarkdown.RMarkdownKnitManager | undefined = undefined;
export let sessionStatusBarItem: vscode.StatusBarItem | undefined = undefined;
// Called (once) when the extension is activated
export async function activate(context: vscode.ExtensionContext): Promise<apiImplementation.RExtensionImplementation> {
if (vscode.extensions.getExtension('mikhail-arkhipov.r')) {
void vscode.window.showInformationMessage('The R Tools (Mikhail-Arkhipov.r) extension is enabled and will have conflicts with vscode-R. To use vscode-R, please disable or uninstall the extension.');
void vscode.commands.executeCommand('workbench.extensions.search', '@installed R Tools');
}
// create a new instance of RExtensionImplementation
// is used to export an interface to the help panel
// this export is used e.g. by vscode-r-debugger to show the help panel from within debug sessions
const rExtension = new apiImplementation.RExtensionImplementation();
// assign extension context to global variable
extensionContext = context;
// assign session watcher setting to global variable
enableSessionWatcher = util.config().get<boolean>('sessionWatcher') ?? false;
rmdPreviewManager = new rmarkdown.RMarkdownPreviewManager();
rmdKnitManager = new rmarkdown.RMarkdownKnitManager();
// register commands specified in package.json
const commands = {
// create R terminal
'r.createRTerm': rTerminal.createRTerm,
// run code from editor in terminal
'r.nrow': () => rTerminal.runSelectionOrWord(['nrow']),
'r.length': () => rTerminal.runSelectionOrWord(['length']),
'r.head': () => rTerminal.runSelectionOrWord(['head']),
'r.thead': () => rTerminal.runSelectionOrWord(['t', 'head']),
'r.names': () => rTerminal.runSelectionOrWord(['names']),
'r.view': () => rTerminal.runSelectionOrWord(['View']),
'r.runSource': () => { void rTerminal.runSource(false); },
'r.runSelection': (code?: string) => { code ? void rTerminal.runTextInTerm(code) : void rTerminal.runSelection(); },
'r.runFromLineToEnd': rTerminal.runFromLineToEnd,
'r.runFromBeginningToLine': rTerminal.runFromBeginningToLine,
'r.runSelectionRetainCursor': rTerminal.runSelectionRetainCursor,
'r.runCommandWithSelectionOrWord': rTerminal.runCommandWithSelectionOrWord,
'r.runCommandWithEditorPath': rTerminal.runCommandWithEditorPath,
'r.runCommand': rTerminal.runCommand,
'r.runSourcewithEcho': () => { void rTerminal.runSource(true); },
// chunk related
'r.selectCurrentChunk': rmarkdown.selectCurrentChunk,
'r.runCurrentChunk': rmarkdown.runCurrentChunk,
'r.runCurrentChunkAndMove': rmarkdown.runCurrentChunkAndMove,
'r.runPreviousChunk': rmarkdown.runPreviousChunk,
'r.runNextChunk': rmarkdown.runNextChunk,
'r.runAboveChunks': rmarkdown.runAboveChunks,
'r.runCurrentAndBelowChunks': rmarkdown.runCurrentAndBelowChunks,
'r.runBelowChunks': rmarkdown.runBelowChunks,
'r.runAllChunks': rmarkdown.runAllChunks,
'r.goToPreviousChunk': rmarkdown.goToPreviousChunk,
'r.goToNextChunk': rmarkdown.goToNextChunk,
'r.runChunks': rTerminal.runChunksInTerm,
// rmd related
'r.knitRmd': () => { void rmdKnitManager?.knitRmd(false, undefined); },
'r.knitRmdToPdf': () => { void rmdKnitManager?.knitRmd(false, 'pdf_document'); },
'r.knitRmdToHtml': () => { void rmdKnitManager?.knitRmd(false, 'html_document'); },
'r.knitRmdToAll': () => { void rmdKnitManager?.knitRmd(false, 'all'); },
'r.rmarkdown.newDraft': () => rmarkdown.newDraft(),
'r.rmarkdown.setKnitDirectory': () => rmdKnitManager?.setKnitDir(),
'r.rmarkdown.showPreviewToSide': () => rmdPreviewManager?.previewRmd(vscode.ViewColumn.Beside),
'r.rmarkdown.showPreview': (uri: vscode.Uri) => rmdPreviewManager?.previewRmd(vscode.ViewColumn.Active, uri),
'r.rmarkdown.preview.refresh': () => rmdPreviewManager?.updatePreview(),
'r.rmarkdown.preview.openExternal': () => void rmdPreviewManager?.openExternalBrowser(),
'r.rmarkdown.preview.showSource': () => rmdPreviewManager?.showSource(),
'r.rmarkdown.preview.toggleStyle': () => rmdPreviewManager?.toggleTheme(),
'r.rmarkdown.preview.enableAutoRefresh': () => rmdPreviewManager?.enableAutoRefresh(),
'r.rmarkdown.preview.disableAutoRefresh': () => rmdPreviewManager?.disableAutoRefresh(),
// file creation (under file submenu)
'r.rmarkdown.newFileDraft': () => rmarkdown.newDraft(),
'r.newFileDocument': () => vscode.workspace.openTextDocument({language: 'r'}).then((v) => vscode.window.showTextDocument(v)),
// editor independent commands
'r.createGitignore': rGitignore.createGitignore,
'r.createLintrConfig': lintrConfig.createLintrConfig,
'r.generateCCppProperties': cppProperties.generateCppProperties,
'r.loadAll': () => rTerminal.runTextInTerm('devtools::load_all()'),
// environment independent commands. this is a workaround for using the Tasks API: https://github.com/microsoft/vscode/issues/40758
'r.build': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Build'),
'r.buildBinary': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Build Binary'),
'r.check': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Check'),
'r.document': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Document'),
'r.install': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Install'),
'r.test': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Test'),
// interaction with R sessions
'r.previewDataframe': preview.previewDataframe,
'r.previewEnvironment': preview.previewEnvironment,
'r.attachActive': session.attachActive,
'r.launchAddinPicker': rstudioapi.launchAddinPicker,
// workspace viewer
'r.workspaceViewer.refreshEntry': () => rWorkspace?.refresh(),
'r.workspaceViewer.view': (node: workspaceViewer.GlobalEnvItem) => node?.label && workspaceViewer.viewItem(node.label),
'r.workspaceViewer.remove': (node: workspaceViewer.GlobalEnvItem) => node?.label && workspaceViewer.removeItem(node.label),
'r.workspaceViewer.clear': workspaceViewer.clearWorkspace,
'r.workspaceViewer.load': workspaceViewer.loadWorkspace,
'r.workspaceViewer.save': workspaceViewer.saveWorkspace,
// browser controls
'r.browser.refresh': session.refreshBrowser,
'r.browser.openExternal': session.openExternalBrowser,
// (help related commands are registered in rHelp.initializeHelp)
};
for (const [key, value] of Object.entries(commands)) {
context.subscriptions.push(vscode.commands.registerCommand(key, value));
}
// keep track of terminals
context.subscriptions.push(vscode.window.onDidCloseTerminal(rTerminal.deleteTerminal));
// start language service
if (util.config().get<boolean>('lsp.enabled')) {
const lsp = vscode.extensions.getExtension('reditorsupport.r-lsp');
if (lsp) {
void vscode.window.showInformationMessage('The R language server extension has been integrated into vscode-R. You need to disable or uninstall REditorSupport.r-lsp and reload window to use the new version.');
void vscode.commands.executeCommand('workbench.extensions.search', '@installed r-lsp');
} else {
context.subscriptions.push(new languageService.LanguageService());
}
}
// register on-enter rule for roxygen comments
const wordPattern = /(-?\d*\.\d\w*)|([^`~!@$^&*()=+[{\]}\\|;:'",<>/\s]+)/g;
vscode.languages.setLanguageConfiguration('r', {
onEnterRules: [
{
// Automatically continue roxygen comments: #'
action: { indentAction: vscode.IndentAction.None, appendText: '#\' ' },
beforeText: /^\s*#'\s*[^\s]/, // matches a non-empty roxygen line
},
{
// Automatically continue roxygen comments: #'
action: { indentAction: vscode.IndentAction.None, appendText: '#\' ' },
beforeText: /^\s*#'/, // matches any roxygen comment line, even an empty one
previousLineText: /^\s*([^#\s].*|#[^'\s].*|#'\s*[^\s].*|)$/, // matches everything but an empty roxygen line
},
],
wordPattern,
});
// register terminal-provider
context.subscriptions.push(vscode.window.registerTerminalProfileProvider('r.terminal-profile',
{
async provideTerminalProfile() {
return {
options: await rTerminal.makeTerminalOptions()
};
}
}
));
// initialize httpgd viewer
globalHttpgdManager = httpgdViewer.initializeHttpgd();
// initialize the package/help related functions
globalRHelp = await rHelp.initializeHelp(context, rExtension);
// register codelens and completion providers for r markdown and r files
vscode.languages.registerCodeLensProvider(['r', 'rmd'], new rmarkdown.RMarkdownCodeLensProvider());
vscode.languages.registerCompletionItemProvider('rmd', new rmarkdown.RMarkdownCompletionItemProvider(), ' ', ',');
vscode.languages.registerFoldingRangeProvider('r', new rmarkdown.RChunkFoldingProvider());
// register (session) hover and completion providers
vscode.languages.registerHoverProvider(['r', 'rmd'], new completions.HoverProvider());
vscode.languages.registerHoverProvider(['r', 'rmd'], new completions.HelpLinkHoverProvider());
vscode.languages.registerCompletionItemProvider(['r', 'rmd'], new completions.StaticCompletionItemProvider(), '@');
// deploy liveshare listener
await rShare.initLiveShare(context);
// register task provider
const taskProvider = new RTaskProvider();
vscode.tasks.registerTaskProvider(taskProvider.type, taskProvider);
// deploy session watcher (if configured by user)
if (enableSessionWatcher) {
if (!rShare.isGuestSession) {
console.info('Initialize session watcher');
void session.deploySessionWatcher(context.extensionPath);
// create status bar item that contains info about the session watcher
console.info('Create sessionStatusBarItem');
sessionStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 1000);
sessionStatusBarItem.command = 'r.attachActive';
sessionStatusBarItem.text = 'R: (not attached)';
sessionStatusBarItem.tooltip = 'Click to attach active terminal.';
sessionStatusBarItem.show();
context.subscriptions.push(sessionStatusBarItem);
void session.startRequestWatcher(sessionStatusBarItem);
}
// track active text editor
rstudioapi.trackLastActiveTextEditor(vscode.window.activeTextEditor);
vscode.window.onDidChangeActiveTextEditor(rstudioapi.trackLastActiveTextEditor);
// register the R Workspace tree view
// creates a custom context value for the workspace view
// only shows view when session watcher is enabled
rWorkspace = new workspaceViewer.WorkspaceDataProvider();
// if session watcher is active, register dyamic completion provider
const liveTriggerCharacters = ['', '[', '(', ',', '$', '@', '"', '\''];
vscode.languages.registerCompletionItemProvider(['r', 'rmd'], new completions.LiveCompletionItemProvider(), ...liveTriggerCharacters);
}
void vscode.commands.executeCommand('setContext', 'r.WorkspaceViewer:show', enableSessionWatcher);
return rExtension;
}