@@ -20,15 +20,33 @@ sess_app <- function(port = NULL, token = NULL, use_rstudioapi = TRUE, use_httpg
2020 # Temporary file for static plot serving
2121 .sess_env $ latest_plot_path <- file.path(.sess_env $ tempdir , " sess_plot.png" )
2222
23+ is_manual <- (! is.null(port ) && ! is.na(port ) && nzchar(port )) ||
24+ (! is.null(token ) && ! is.na(token ) && nzchar(token ))
25+
2326 if (is.null(port ) || is.na(port )) {
2427 port <- Sys.getenv(" SESS_PORT" )
2528 }
2629 if (is.null(token ) || is.na(token ) || ! nzchar(token )) {
2730 token <- Sys.getenv(" SESS_TOKEN" )
2831 }
2932
33+ # Derive file path for fallback/reconnection
34+ pid <- Sys.getpid()
35+ home <- path.expand(" ~" )
36+ file_path <- file.path(home , " .vscode-R" , " sessions" , sprintf(" %d.json" , pid ))
37+
38+ if (! nzchar(port ) || ! nzchar(token )) {
39+ if (file.exists(file_path )) {
40+ tryCatch({
41+ config <- jsonlite :: fromJSON(readLines(file_path , warn = FALSE ))
42+ port <- config $ port
43+ token <- config $ token
44+ }, error = function (e ) NULL )
45+ }
46+ }
47+
3048 if (! nzchar(port ) || ! nzchar(token )) {
31- warning(" [sess] SESS_PORT or SESS_TOKEN not set . Cannot connect to VS Code." )
49+ warning(" [sess] Connection info not available . Cannot connect to VS Code." )
3250 return (invisible (NULL ))
3351 }
3452
@@ -37,93 +55,116 @@ sess_app <- function(port = NULL, token = NULL, use_rstudioapi = TRUE, use_httpg
3755 cat(sprintf(" \r %s\n\n %s" , msg , prompt ))
3856 }
3957
40- url <- sprintf(" ws://127.0.0.1:%s/?token=%s" , port , token )
41- ws <- websocket :: WebSocket $ new(url , autoConnect = FALSE )
42-
43- ws $ onOpen(function (event ) {
44- .sess_env $ ws <- ws
45- print_async_msg(" [sess] Connected to VS Code" )
46-
47- # Send the attach handshake immediately upon connection
48- notify_client(" attach" , list (
49- version = sprintf(" %s.%s" , R.version $ major , R.version $ minor ),
50- pid = Sys.getpid(),
51- tempdir = .sess_env $ tempdir ,
52- wd = getwd(),
53- info = list (
54- command = commandArgs()[[1L ]],
55- version = R.version.string ,
56- start_time = format(Sys.time())
57- )
58- ))
59- })
60-
61- ws $ onMessage(function (event ) {
62- # Handle JSON-RPC 2.0 messages COMING FROM the client
63- payload <- tryCatch(jsonlite :: fromJSON(event $ data ), error = function (e ) NULL )
64-
65- if (! is.null(payload ) && ! is.null(payload $ id )) {
66- if (! is.null(payload $ method )) {
67- # It's a Request from the Client (e.g., 'workspace', 'plot_latest')
68- handlers <- list (
69- " workspace" = function (p ) get_workspace_data(),
70- " hover" = function (p ) handle_hover(p $ expr ),
71- " completion" = function (p ) handle_complete(p $ expr , p $ trigger ),
72- " plot_latest" = function (p ) handle_plot_latest(p )
58+ connect <- function () {
59+ url <- sprintf(" ws://127.0.0.1:%s/?token=%s" , port , token )
60+ ws <- websocket :: WebSocket $ new(
61+ url ,
62+ autoConnect = FALSE ,
63+ accessLogChannels = " none" ,
64+ errorLogChannels = " none"
65+ )
66+
67+ ws $ onOpen(function (event ) {
68+ .sess_env $ ws <- ws
69+ print_async_msg(" [sess] Connected to VS Code" )
70+
71+ # Send the attach handshake immediately upon connection
72+ notify_client(" attach" , list (
73+ version = sprintf(" %s.%s" , R.version $ major , R.version $ minor ),
74+ pid = Sys.getpid(),
75+ tempdir = .sess_env $ tempdir ,
76+ wd = getwd(),
77+ info = list (
78+ command = commandArgs()[[1L ]],
79+ version = R.version.string ,
80+ start_time = format(Sys.time())
7381 )
74-
75- if (payload $ method %in% names(handlers )) {
76- res <- tryCatch(
77- {
78- handlers [[payload $ method ]](payload $ params )
79- },
80- error = function (e ) {
81- warning(sprintf(
82- " [sess] Error in handler for '%s': %s" ,
83- payload $ method , e $ message
84- ))
85- NULL
86- }
82+ ))
83+ })
84+
85+ ws $ onMessage(function (event ) {
86+ # Handle JSON-RPC 2.0 messages COMING FROM the client
87+ payload <- tryCatch(jsonlite :: fromJSON(event $ data ), error = function (e ) NULL )
88+
89+ if (! is.null(payload ) && ! is.null(payload $ id )) {
90+ if (! is.null(payload $ method )) {
91+ # It's a Request from the Client (e.g., 'workspace', 'plot_latest')
92+ handlers <- list (
93+ " workspace" = function (p ) get_workspace_data(),
94+ " hover" = function (p ) handle_hover(p $ expr ),
95+ " completion" = function (p ) handle_complete(p $ expr , p $ trigger ),
96+ " plot_latest" = function (p ) handle_plot_latest(p )
8797 )
8898
89- succ_resp <- list (
90- jsonrpc = " 2.0" ,
91- id = payload $ id ,
92- result = res
93- )
94- ws $ send(jsonlite :: toJSON(succ_resp , auto_unbox = TRUE , null = " null" , force = TRUE ))
99+ if (payload $ method %in% names(handlers )) {
100+ res <- tryCatch(
101+ {
102+ handlers [[payload $ method ]](payload $ params )
103+ },
104+ error = function (e ) {
105+ warning(sprintf(
106+ " [sess] Error in handler for '%s': %s" ,
107+ payload $ method , e $ message
108+ ))
109+ NULL
110+ }
111+ )
112+
113+ succ_resp <- list (
114+ jsonrpc = " 2.0" ,
115+ id = payload $ id ,
116+ result = res
117+ )
118+ ws $ send(jsonlite :: toJSON(succ_resp , auto_unbox = TRUE , null = " null" , force = TRUE ))
119+ } else {
120+ err_resp <- list (
121+ jsonrpc = " 2.0" ,
122+ id = payload $ id ,
123+ error = list (code = - 32601 , message = " Method not found" )
124+ )
125+ ws $ send(jsonlite :: toJSON(err_resp , auto_unbox = TRUE , null = " null" , force = TRUE ))
126+ }
95127 } else {
96- err_resp <- list (
97- jsonrpc = " 2.0" ,
98- id = payload $ id ,
99- error = list (code = - 32601 , message = " Method not found" )
100- )
101- ws $ send(jsonlite :: toJSON(err_resp , auto_unbox = TRUE , null = " null" , force = TRUE ))
102- }
103- } else {
104- # It's a Response (to our RStudio API request)
105- if (! is.null(payload $ result )) {
106- .sess_env $ pending_responses [[as.character(payload $ id )]] <-
107- payload $ result
108- } else if (! is.null(payload $ error )) {
109- .sess_env $ pending_responses [[as.character(payload $ id )]] <-
110- structure(payload $ error , class = " json_rpc_error" )
128+ # It's a Response (to our RStudio API request)
129+ if (! is.null(payload $ result )) {
130+ .sess_env $ pending_responses [[as.character(payload $ id )]] <-
131+ payload $ result
132+ } else if (! is.null(payload $ error )) {
133+ .sess_env $ pending_responses [[as.character(payload $ id )]] <-
134+ structure(payload $ error , class = " json_rpc_error" )
135+ }
111136 }
112137 }
113- }
114- })
138+ })
115139
116- ws $ onClose(function (event ) {
117- .sess_env $ ws <- NULL
118- print_async_msg(" [sess] Disconnected from VS Code" )
119- })
140+ ws $ onClose(function (event ) {
141+ .sess_env $ ws <- NULL
142+ if (is_manual ) {
143+ print_async_msg(" [sess] Disconnected from VS Code." )
144+ return ()
145+ }
146+ print_async_msg(" [sess] Disconnected from VS Code. Retrying in 5 seconds..." )
147+ later :: later(function () {
148+ if (file.exists(file_path )) {
149+ tryCatch({
150+ config <- jsonlite :: fromJSON(readLines(file_path , warn = FALSE ))
151+ port <<- config $ port
152+ token <<- config $ token
153+ }, error = function (e ) NULL )
154+ }
155+ connect()
156+ }, 5 )
157+ })
158+
159+ ws $ onError(function (event ) {
160+ print_async_msg(sprintf(" [sess] WebSocket error: %s" , event $ message ))
161+ })
120162
121- ws $ onError(function (event ) {
122- print_async_msg(sprintf(" [sess] WebSocket error: %s" , event $ message ))
123- })
163+ ws $ connect()
164+ }
124165
125166 # Connect to VS Code
126- ws $ connect()
167+ connect()
127168
128169 # Register runtime hooks
129170 if (is.na(use_rstudioapi )) use_rstudioapi <- TRUE
0 commit comments