1
1
//
2
2
// r_variables.rs
3
3
//
4
- // Copyright (C) 2023-2024 by Posit Software, PBC
4
+ // Copyright (C) 2023-2025 by Posit Software, PBC
5
5
//
6
6
//
7
7
@@ -26,6 +26,7 @@ use harp::environment::Environment;
26
26
use harp:: environment:: EnvironmentFilter ;
27
27
use harp:: exec:: RFunction ;
28
28
use harp:: exec:: RFunctionExt ;
29
+ use harp:: get_option;
29
30
use harp:: object:: RObject ;
30
31
use harp:: utils:: r_assert_type;
31
32
use harp:: vector:: CharacterVector ;
@@ -42,6 +43,17 @@ use crate::r_task;
42
43
use crate :: thread:: RThreadSafe ;
43
44
use crate :: variables:: variable:: PositronVariable ;
44
45
46
+ /// Enumeration of treatments for the .Last.value variable
47
+ pub enum LastValue {
48
+ /// Always show the .Last.value variable in the Variables pane. This is used
49
+ /// by tests to show the value without changing the global option.
50
+ Always ,
51
+
52
+ /// Use the value of the global option `positron.show_last_value` to
53
+ /// determine whether to show the .Last.value variable
54
+ UseOption ,
55
+ }
56
+
45
57
/**
46
58
* The R Variables handler provides the server side of Positron's Variables panel, and is
47
59
* responsible for creating and updating the list of variables.
@@ -66,6 +78,14 @@ pub struct RVariables {
66
78
/// thread. Tracked in https://github.com/posit-dev/positron/issues/1812
67
79
current_bindings : RThreadSafe < Vec < Binding > > ,
68
80
version : u64 ,
81
+
82
+ /// Whether to always show the .Last.value in the Variables pane, regardless
83
+ /// of the value of positron.show_last_value
84
+ show_last_value : LastValue ,
85
+
86
+ /// Whether we are currently showing the .Last.value variable in the Variables
87
+ /// pane.
88
+ showing_last_value : bool ,
69
89
}
70
90
71
91
impl RVariables {
@@ -76,6 +96,23 @@ impl RVariables {
76
96
* - `comm`: A channel used to send messages to the frontend
77
97
*/
78
98
pub fn start ( env : RObject , comm : CommSocket , comm_manager_tx : Sender < CommManagerEvent > ) {
99
+ // Start with default settings
100
+ Self :: start_with_config ( env, comm, comm_manager_tx, LastValue :: UseOption ) ;
101
+ }
102
+
103
+ /**
104
+ * Creates a new RVariables instance with specific configuration.
105
+ *
106
+ * - `env`: An R environment to scan for variables, typically R_GlobalEnv
107
+ * - `comm`: A channel used to send messages to the frontend
108
+ * - `show_last_value`: Whether to include .Last.value in the variables list
109
+ */
110
+ pub fn start_with_config (
111
+ env : RObject ,
112
+ comm : CommSocket ,
113
+ comm_manager_tx : Sender < CommManagerEvent > ,
114
+ show_last_value : LastValue ,
115
+ ) {
79
116
// Validate that the RObject we were passed is actually an environment
80
117
if let Err ( err) = r_assert_type ( env. sexp , & [ ENVSXP ] ) {
81
118
log:: warn!(
@@ -99,6 +136,8 @@ impl RVariables {
99
136
env,
100
137
current_bindings,
101
138
version: 0 ,
139
+ show_last_value,
140
+ showing_last_value: false ,
102
141
} ;
103
142
environment. execution_thread( ) ;
104
143
} ) ;
@@ -196,6 +235,14 @@ impl RVariables {
196
235
r_task ( || {
197
236
self . update_bindings ( self . bindings ( ) ) ;
198
237
238
+ // If the special .Last.value variable is enabled, add it to the
239
+ // list. This is a special R value that doesn't have its own
240
+ // binding.
241
+ if let Some ( last_value) = self . last_value ( ) {
242
+ self . showing_last_value = true ;
243
+ variables. push ( last_value. var ( ) ) ;
244
+ }
245
+
199
246
for binding in self . current_bindings . get ( ) {
200
247
variables. push ( PositronVariable :: new ( binding) . var ( ) ) ;
201
248
}
@@ -357,6 +404,45 @@ impl RVariables {
357
404
}
358
405
}
359
406
407
+ /// Gets the value of the special variable '.Last.value' (the value of the
408
+ /// last expression evaluated at the top level), if enabled.
409
+ ///
410
+ /// Returns None in all other cases.
411
+ fn last_value ( & self ) -> Option < PositronVariable > {
412
+ // Check the cached value first
413
+ let show_last_value = match self . show_last_value {
414
+ LastValue :: Always => true ,
415
+ LastValue :: UseOption => {
416
+ // If we aren't always showing the last value, update from the
417
+ // global option
418
+ let use_last_value = get_option ( "positron.show_last_value" ) ;
419
+ match use_last_value. get_bool ( 0 ) {
420
+ Ok ( Some ( true ) ) => true ,
421
+ _ => false ,
422
+ }
423
+ } ,
424
+ } ;
425
+
426
+ if show_last_value {
427
+ match harp:: environment:: last_value ( ) {
428
+ Ok ( last_robj) => Some ( PositronVariable :: from (
429
+ String :: from ( ".Last.value" ) ,
430
+ String :: from ( ".Last.value" ) ,
431
+ last_robj. sexp ,
432
+ ) ) ,
433
+ Err ( err) => {
434
+ // This isn't a critical error but would also be very
435
+ // unexpected.
436
+ log:: error!( "Environment: Could not evaluate .Last.value ({err:?})" ) ;
437
+ None
438
+ } ,
439
+ }
440
+ } else {
441
+ // Last value display is disabled
442
+ None
443
+ }
444
+ }
445
+
360
446
#[ tracing:: instrument( level = "trace" , skip_all) ]
361
447
fn update ( & mut self , request_id : Option < String > ) {
362
448
let mut assigned: Vec < Variable > = vec ! [ ] ;
@@ -371,6 +457,18 @@ impl RVariables {
371
457
let mut new_iter = new_bindings. get ( ) . iter ( ) ;
372
458
let mut new_next = new_iter. next ( ) ;
373
459
460
+ // Track the last value if the user has requested it. Treat this
461
+ // value as assigned every time we update the Variables list.
462
+ if let Some ( last_value) = self . last_value ( ) {
463
+ self . showing_last_value = true ;
464
+ assigned. push ( last_value. var ( ) ) ;
465
+ } else if self . showing_last_value {
466
+ // If we are no longer showing the last value, remove it from
467
+ // the list of assigned variables
468
+ self . showing_last_value = false ;
469
+ removed. push ( ".Last.value" . to_string ( ) ) ;
470
+ }
471
+
374
472
loop {
375
473
match ( old_next, new_next) {
376
474
// nothing more to do
0 commit comments