Skip to content

Commit 39e9b34

Browse files
committed
Fix: add an is_toc parameters to a LoadFile request.
1 parent 523ca4e commit 39e9b34

File tree

10 files changed

+79
-46
lines changed

10 files changed

+79
-46
lines changed

client/src/CodeChatEditorFramework.mts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
CodeChatForWeb,
3838
EditorMessage,
3939
EditorMessageContents,
40+
KeysOfRustEnum,
4041
MessageResult,
4142
UpdateMessageContents,
4243
} from "./shared_types.mjs";
@@ -134,7 +135,7 @@ class WebSocketComm {
134135
assert(message !== undefined);
135136
const keys = Object.keys(message);
136137
assert(keys.length === 1);
137-
const key = keys[0];
138+
const key = keys[0] as KeysOfRustEnum<EditorMessageContents>;
138139
const value = Object.values(message)[0];
139140

140141
// Process this message.

client/src/shared_types.mts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,18 @@ import { ResultErrTypes } from "./rust-types/ResultErrTypes.js";
4141
// Manually define this, since `ts-rs` can't export `webserver.MessageResult`.
4242
type MessageResult = { Ok: ResultOkTypes } | { Err: ResultErrTypes };
4343

44+
// Modified from [SO](https://stackoverflow.com/a/79050131). If the value is a string, use it; otherwise, assume it's a dict and use its key.
45+
type KeysOfRustEnum<T> = T extends T ? (T extends string ? T : keyof T) : never;
46+
4447
export type {
45-
EditorMessageContents,
4648
CodeMirror,
49+
CodeMirrorDiffable,
4750
CodeMirrorDocBlockTuple,
4851
CodeChatForWeb,
49-
StringDiff,
50-
CodeMirrorDiffable,
51-
UpdateMessageContents,
5252
EditorMessage,
53+
EditorMessageContents,
54+
KeysOfRustEnum,
5355
MessageResult,
56+
StringDiff,
57+
UpdateMessageContents,
5458
};

extensions/VSCode/src/extension.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import { CodeChatEditorServer, initServer } from "./index.js";
4141
import {
4242
autosave_timeout_ms,
4343
EditorMessage,
44+
EditorMessageContents,
45+
KeysOfRustEnum,
4446
MessageResult,
4547
rand,
4648
UpdateMessageContents,
@@ -314,7 +316,8 @@ export const activate = (context: vscode.ExtensionContext) => {
314316
}
315317
const keys = Object.keys(message);
316318
assert(keys.length === 1);
317-
const key = keys[0];
319+
const key =
320+
keys[0] as KeysOfRustEnum<EditorMessageContents>;
318321
const value = Object.values(message)[0];
319322

320323
// Process this message.
@@ -526,14 +529,21 @@ export const activate = (context: vscode.ExtensionContext) => {
526529
}
527530

528531
case "LoadFile": {
529-
const load_file = value as string;
532+
const [load_file, is_toc] = value as [
533+
string,
534+
boolean,
535+
];
530536
// Look through all open documents to see if we have
531537
// the requested file.
532538
const doc = get_document(load_file);
539+
// If the request is for the TOC as a TOC (not as an editable file), don't create a new version; the value we send won't be used and doesn't matter.
540+
if (!is_toc) {
541+
version = rand();
542+
}
533543
const load_file_result: null | [string, number] =
534544
doc === undefined
535545
? null
536-
: [doc.getText(), (version = rand())];
546+
: [doc.getText(), version];
537547
console_log(
538548
`CodeChat Editor extension: Result(LoadFile(${format_struct(load_file_result)}))`,
539549
);

server/src/ide/filewatcher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ async fn processing_task(
634634
break;
635635
}
636636

637-
EditorMessageContents::LoadFile(_) => {
637+
EditorMessageContents::LoadFile(..) => {
638638
// We never have the requested file loaded in this
639639
// "IDE". Intead, it's always on disk.
640640
send_response(&from_ide_tx, m.id, Ok(ResultOkTypes::LoadFile(None))).await;

server/src/ide/vscode/tests.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -353,10 +353,11 @@ async fn test_vscode_ide_websocket3() {
353353
//
354354
// Message ids: IDE - 0, Server - 1->2, Client - 0.
355355
let em = read_message(&mut ws_ide).await;
356-
let msg = cast!(em.message, EditorMessageContents::LoadFile);
356+
let (msg, is_toc) = cast!(em.message, EditorMessageContents::LoadFile, a, b);
357357
// Compare these as strings -- we want to ensure the path separator is
358358
// correct for the current platform.
359359
assert_eq!(file_path.to_string_lossy(), msg.to_string_lossy());
360+
assert_eq!(is_toc, false);
360361
assert_eq!(em.id, INITIAL_MESSAGE_ID + MESSAGE_ID_INCREMENT);
361362

362363
// Reply to the `LoadFile` message -- the file isn't present.
@@ -415,7 +416,7 @@ async fn test_vscode_ide_websocket3a() {
415416
//
416417
// Message ids: IDE - 0, Server - 0->2, Client - 0.
417418
let em = read_message(&mut ws_ide).await;
418-
cast!(em.message, EditorMessageContents::LoadFile);
419+
cast!(em.message, EditorMessageContents::LoadFile, a, b);
419420
// Skip comparing the file names, due to the backslash encoding.
420421
assert_eq!(em.id, INITIAL_MESSAGE_ID + MESSAGE_ID_INCREMENT);
421422

@@ -510,11 +511,12 @@ async fn test_vscode_ide_websocket8() {
510511
//
511512
// Message ids: IDE - 1, Server - 1->2, Client - 0.
512513
let em = read_message(&mut ws_ide).await;
513-
let msg = cast!(em.message, EditorMessageContents::LoadFile);
514+
let (msg, is_toc) = cast!(em.message, EditorMessageContents::LoadFile, a, b);
514515
assert_eq!(
515516
path::absolute(Path::new(&msg)).unwrap(),
516517
path::absolute(&file_path).unwrap()
517518
);
519+
assert_eq!(is_toc, false);
518520
assert_eq!(em.id, INITIAL_MESSAGE_ID + MESSAGE_ID_INCREMENT);
519521

520522
// Reply to the `LoadFile` message with the file's contents.
@@ -943,8 +945,9 @@ async fn test_vscode_ide_websocket4() {
943945
//
944946
// Message ids: IDE - 0, Server - 1->2, Client - 1.
945947
let em = read_message(&mut ws_ide).await;
946-
let msg = cast!(em.message, EditorMessageContents::LoadFile);
948+
let (msg, is_toc) = cast!(em.message, EditorMessageContents::LoadFile, a, b);
947949
assert_eq!(fs::canonicalize(&msg).unwrap(), file_path_temp);
950+
assert_eq!(is_toc, false);
948951
assert_eq!(em.id, INITIAL_MESSAGE_ID + MESSAGE_ID_INCREMENT);
949952

950953
// Reply to the `LoadFile` message: the IDE doesn't have the file.
@@ -1030,11 +1033,12 @@ async fn test_vscode_ide_websocket4() {
10301033
//
10311034
// Message ids: IDE - 0, Server - 3->4, Client - 0.
10321035
let em = read_message(&mut ws_ide).await;
1033-
let msg = cast!(em.message, EditorMessageContents::LoadFile);
1036+
let (msg, is_toc) = cast!(em.message, EditorMessageContents::LoadFile, a, b);
10341037
assert_eq!(
10351038
fs::canonicalize(&msg).unwrap(),
10361039
fs::canonicalize(test_dir.join("toc.md")).unwrap()
10371040
);
1041+
assert_eq!(is_toc, false);
10381042
assert_eq!(em.id, INITIAL_MESSAGE_ID + 3.0 * MESSAGE_ID_INCREMENT);
10391043

10401044
// Reply to the `LoadFile` message: the IDE doesn't have the file.
@@ -1210,8 +1214,9 @@ async fn test_vscode_ide_websocket4a() {
12101214
//
12111215
// Message ids: IDE - 0, Server - 1->2, Client - 1.
12121216
let em = read_message(&mut ws_ide).await;
1213-
let msg = cast!(em.message, EditorMessageContents::LoadFile);
1217+
let (msg, is_toc) = cast!(em.message, EditorMessageContents::LoadFile, a, b);
12141218
assert_eq!(fs::canonicalize(&msg).unwrap(), file_path_temp);
1219+
assert_eq!(is_toc, false);
12151220
assert_eq!(em.id, INITIAL_MESSAGE_ID + MESSAGE_ID_INCREMENT);
12161221

12171222
// Reply to the `LoadFile` message: the IDE doesn't have the file.
@@ -1324,8 +1329,9 @@ async fn test_vscode_ide_websocket4b() {
13241329
//
13251330
// Message ids: IDE - 0, Server - 1->2, Client - 1.
13261331
let em = read_message(&mut ws_ide).await;
1327-
let msg = cast!(em.message, EditorMessageContents::LoadFile);
1332+
let (msg, is_toc) = cast!(em.message, EditorMessageContents::LoadFile, a, b);
13281333
assert_eq!(fs::canonicalize(&msg).unwrap(), file_path_temp);
1334+
assert_eq!(is_toc, false);
13291335
assert_eq!(em.id, INITIAL_MESSAGE_ID + MESSAGE_ID_INCREMENT);
13301336

13311337
// Reply to the `LoadFile` message: the IDE doesn't have the file.

server/src/translation.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,10 @@ use crate::{
229229
queue_send, queue_send_func,
230230
webserver::{
231231
EditorMessage, EditorMessageContents, INITIAL_MESSAGE_ID, MESSAGE_ID_INCREMENT,
232-
ProcessingTaskHttpRequest, ResultErrTypes, ResultOkTypes, SimpleHttpResponse,
233-
SimpleHttpResponseError, UpdateMessageContents, WebAppState, WebsocketQueues,
234-
file_to_response, path_to_url, send_response, try_canonicalize, try_read_as_text,
235-
url_to_path,
232+
ProcessingTaskHttpRequest, ProcessingTaskHttpRequestFlags, ResultErrTypes, ResultOkTypes,
233+
SimpleHttpResponse, SimpleHttpResponseError, UpdateMessageContents, WebAppState,
234+
WebsocketQueues, file_to_response, path_to_url, send_response, try_canonicalize,
235+
try_read_as_text, url_to_path,
236236
},
237237
};
238238

@@ -494,7 +494,7 @@ pub async fn translation_task(
494494
// Handle messages that the IDE must not send.
495495
EditorMessageContents::Opened(_) |
496496
EditorMessageContents::OpenUrl(_) |
497-
EditorMessageContents::LoadFile(_) |
497+
EditorMessageContents::LoadFile(..) |
498498
EditorMessageContents::ClientHtml(_) => {
499499
let err = ResultErrTypes::IdeIllegalMessage;
500500
error!("{err:?}");
@@ -543,7 +543,10 @@ pub async fn translation_task(
543543
// Convert the request into a `LoadFile` message.
544544
queue_send!(tt.to_ide_tx.send(EditorMessage {
545545
id: tt.id,
546-
message: EditorMessageContents::LoadFile(http_request.file_path.clone())
546+
message: EditorMessageContents::LoadFile
547+
(http_request.file_path.clone(),
548+
http_request.flags == ProcessingTaskHttpRequestFlags::Toc
549+
)
547550
}));
548551
// Store the ID and request, which are needed to send a
549552
// response when the `LoadFile` result is received.
@@ -557,7 +560,7 @@ pub async fn translation_task(
557560
match client_message.message {
558561
// Handle messages that the client must not send.
559562
EditorMessageContents::Opened(_) |
560-
EditorMessageContents::LoadFile(_) |
563+
EditorMessageContents::LoadFile(..) |
561564
EditorMessageContents::RequestClose |
562565
EditorMessageContents::ClientHtml(_) => {
563566
let err = ResultErrTypes::ClientIllegalMessage;
@@ -575,7 +578,7 @@ pub async fn translation_task(
575578
debug!("Forwarding it to the IDE.");
576579
// If the Client can't read our diff, send the full
577580
// text next time.
578-
if matches!(result, Err(ResultErrTypes::OutOfSync)) {
581+
if matches!(result, Err(ResultErrTypes::OutOfSync(..))) {
579582
tt.sent_full = false;
580583
}
581584
queue_send!(tt.to_ide_tx.send(client_message))
@@ -706,7 +709,7 @@ impl TranslationTask {
706709
if !is_loadfile {
707710
debug!("Forwarding it to the Client.");
708711
// If the Server can't read our diff, send the full text next time.
709-
if matches!(result, Err(ResultErrTypes::OutOfSync)) {
712+
if matches!(result, Err(ResultErrTypes::OutOfSync(..))) {
710713
self.sent_full = false;
711714
}
712715
queue_send_func!(self.to_client_tx.send(ide_message));
@@ -745,7 +748,10 @@ impl TranslationTask {
745748
let use_pdf_js = http_request.file_path.extension() == Some(OsStr::new("pdf"));
746749
let ((simple_http_response, option_update), file_contents) = match file_contents_option {
747750
Some((file_contents, new_version)) => {
748-
self.version = new_version;
751+
// Only pay attention to the version if this is an editable Client file.
752+
if http_request.flags == ProcessingTaskHttpRequestFlags::None {
753+
self.version = new_version;
754+
}
749755
// The IDE just sent the full contents; we're sending full
750756
// contents to the Client.
751757
self.sent_full = true;

server/src/webserver.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,15 @@ pub struct ProcessingTaskHttpRequest {
116116
/// The path of the file requested.
117117
pub file_path: PathBuf,
118118
/// Flags for this file: none, TOC, raw.
119-
flags: ProcessingTaskHttpRequestFlags,
119+
pub flags: ProcessingTaskHttpRequestFlags,
120120
/// True if test mode is enabled.
121121
is_test_mode: bool,
122122
/// A queue to send the response back to the HTTP task.
123123
pub response_queue: oneshot::Sender<SimpleHttpResponse>,
124124
}
125125

126126
#[derive(Debug, PartialEq)]
127-
enum ProcessingTaskHttpRequestFlags {
127+
pub enum ProcessingTaskHttpRequestFlags {
128128
// No flags provided.
129129
None,
130130
// This file is a TOC.
@@ -216,9 +216,11 @@ pub enum EditorMessageContents {
216216
// #### These messages may only be sent by the Server.
217217
/// Ask the IDE if the provided file is loaded. If so, the IDE should
218218
/// respond by sending a `LoadFile` with the requested file. If not, the
219-
/// returned `Result` should indicate the error "not loaded". Valid
220-
/// destinations: IDE.
221-
LoadFile(PathBuf),
219+
/// returned `Result` should indicate the error "not loaded". The boolean
220+
/// indicates if the request is for the non-editable sidebar TOC file, or
221+
/// for an editable Client file (which still may be the TOC, if that's being
222+
/// edited). Valid destinations: IDE.
223+
LoadFile(PathBuf, bool),
222224
/// This may only be used to respond to an `Opened` message; it contains the
223225
/// HTML for the CodeChat Editor Client to display in its built-in browser.
224226
/// Valid destinations: IDE.
@@ -249,6 +251,8 @@ pub enum ResultOkTypes {
249251
Void,
250252
/// The `LoadFile` message provides file contents and a revision number, if
251253
/// available. This message may only be sent from the IDE to the Server.
254+
/// If this is sent in response to a `LoadFile` where the toc boolean is false,
255+
/// this value will be ignored.
252256
LoadFile(Option<(String, f64)>),
253257
}
254258

@@ -1218,7 +1222,7 @@ pub fn client_websocket(
12181222
// can send.
12191223
match &joint_message.message {
12201224
// Check for an invalid message.
1221-
EditorMessageContents::LoadFile(_) |
1225+
EditorMessageContents::LoadFile(..) |
12221226
EditorMessageContents::ClientHtml(_) |
12231227
EditorMessageContents::Closed => {
12241228
let err = ResultErrTypes::ClientIllegalMessage;

server/tests/overall_1.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ async fn test_server_core(
446446
expected_messages.insert(
447447
EditorMessage {
448448
id: server_id,
449-
message: EditorMessageContents::LoadFile(txt_path.clone()),
449+
message: EditorMessageContents::LoadFile(txt_path.clone(), false),
450450
},
451451
false,
452452
);
@@ -465,11 +465,13 @@ async fn test_server_core(
465465
// request for the TOC. The ordering isn't fixed; accommodate this.
466466
let msg = codechat_server.get_message_timeout(TIMEOUT).await.unwrap();
467467
assert_eq!(msg.id, server_id);
468-
let msg_contents = cast!(msg.message, EditorMessageContents::LoadFile);
469-
let next_path = if msg_contents == toc_path.clone() {
470-
txt_path.clone()
468+
let (msg_contents, is_toc) = cast!(msg.message, EditorMessageContents::LoadFile, a, b);
469+
let (next_path, next_is_toc) = if msg_contents == toc_path.clone() {
470+
assert_eq!(is_toc, true);
471+
(txt_path.clone(), false)
471472
} else if msg_contents == txt_path.clone() {
472-
toc_path.clone()
473+
assert_eq!(is_toc, false);
474+
(toc_path.clone(), true)
473475
} else {
474476
panic!("Unexpected path {msg_contents:?}.");
475477
};
@@ -482,7 +484,7 @@ async fn test_server_core(
482484
codechat_server.get_message_timeout(TIMEOUT).await.unwrap(),
483485
EditorMessage {
484486
id: server_id,
485-
message: EditorMessageContents::LoadFile(next_path),
487+
message: EditorMessageContents::LoadFile(next_path, next_is_toc),
486488
}
487489
);
488490
codechat_server
@@ -535,7 +537,7 @@ async fn test_server_core(
535537
codechat_server.get_message_timeout(TIMEOUT).await.unwrap(),
536538
EditorMessage {
537539
id: server_id,
538-
message: EditorMessageContents::LoadFile(pdf_path.clone())
540+
message: EditorMessageContents::LoadFile(pdf_path.clone(), false)
539541
}
540542
);
541543
codechat_server
@@ -547,7 +549,7 @@ async fn test_server_core(
547549
codechat_server.get_message_timeout(TIMEOUT).await.unwrap(),
548550
EditorMessage {
549551
id: server_id,
550-
message: EditorMessageContents::LoadFile(toc_path)
552+
message: EditorMessageContents::LoadFile(toc_path, true)
551553
}
552554
);
553555
codechat_server
@@ -560,7 +562,7 @@ async fn test_server_core(
560562
codechat_server.get_message_timeout(TIMEOUT).await.unwrap(),
561563
EditorMessage {
562564
id: server_id,
563-
message: EditorMessageContents::LoadFile(pdf_path)
565+
message: EditorMessageContents::LoadFile(pdf_path, false)
564566
}
565567
);
566568
codechat_server
@@ -640,7 +642,7 @@ async fn test_client_core(
640642
codechat_server.get_message_timeout(TIMEOUT).await.unwrap(),
641643
EditorMessage {
642644
id: server_id,
643-
message: EditorMessageContents::LoadFile(path.clone())
645+
message: EditorMessageContents::LoadFile(path.clone(), false)
644646
}
645647
);
646648
codechat_server
@@ -653,7 +655,7 @@ async fn test_client_core(
653655
codechat_server.get_message_timeout(TIMEOUT).await.unwrap(),
654656
EditorMessage {
655657
id: server_id,
656-
message: EditorMessageContents::LoadFile(toc_path)
658+
message: EditorMessageContents::LoadFile(toc_path, true)
657659
}
658660
);
659661
codechat_server

server/tests/overall_2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ async fn test_5_core(
300300
doc: vec![StringDiff {
301301
from: 0,
302302
to: Some(8),
303-
insert: "# foobarTest.\n".to_string(),
303+
insert: "# Tesbart.\n".to_string(),
304304
},],
305305
doc_blocks: vec![],
306306
version,

0 commit comments

Comments
 (0)