Skip to content

Commit 74d7c84

Browse files
committed
Fix: translate EOLs when loading a file from disk.
1 parent 9bec903 commit 74d7c84

File tree

6 files changed

+184
-39
lines changed

6 files changed

+184
-39
lines changed

docs/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Changelog
2323
* Fix indexing in diffs for characters that use more than one UTF-16 code
2424
unit, such as 😄,👉🏿,👨‍👦, and 🇺🇳.
2525
* Fix data corruption with adjacent doc blocks.
26+
* Translate line endings when loading a file from disk.
2627
* v0.1.23, 2025-Jul-24
2728
* Correct diff errors in IDE with CRLF line endings.
2829
* Upgrade to newest release of MathJax, TinyMCE.

server/src/webserver.rs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,8 @@ async fn make_simple_http_response(
701701
// If this file is currently being edited, this is the body of an `Update`
702702
// message to send.
703703
Option<UpdateMessageContents>,
704+
// The resulting file contents, if this is a CodeChat Editor file
705+
Option<String>,
704706
) {
705707
// Convert the provided URL back into a file name.
706708
let file_path = &http_request.file_path;
@@ -710,19 +712,14 @@ async fn make_simple_http_response(
710712
Err(err) => (
711713
SimpleHttpResponse::Err(SimpleHttpResponseError::Io(err)),
712714
None,
715+
None,
713716
),
714717
Ok(mut fc) => {
715718
let file_contents = try_read_as_text(&mut fc).await;
716719
// <a id="binary-file-sniffer"></a>If this is a binary file (meaning
717720
// we can't read the contents as UTF-8), send the contents as none
718721
// to signal this isn't a text file.
719-
file_to_response(
720-
http_request,
721-
current_filepath,
722-
file_contents.as_deref(),
723-
use_pdf_js,
724-
)
725-
.await
722+
file_to_response(http_request, current_filepath, file_contents, use_pdf_js).await
726723
}
727724
}
728725
}
@@ -755,7 +752,7 @@ async fn file_to_response(
755752
// `try_canonicalize`.
756753
current_filepath: &Path,
757754
// Contents of this file, if it's text; None if it was binary data.
758-
file_contents: Option<&str>,
755+
file_contents: Option<String>,
759756
// True to use the PDF.js viewer for this file.
760757
use_pdf_js: bool,
761758
) -> (
@@ -765,6 +762,8 @@ async fn file_to_response(
765762
// populate the Client with the parsed `file_contents`. In all other cases,
766763
// return None.
767764
Option<UpdateMessageContents>,
765+
// The `file_contents` if this is a
766+
Option<String>,
768767
) {
769768
// Use a lossy conversion, since this is UI display, not filesystem access.
770769
let file_path = &http_request.file_path;
@@ -774,6 +773,7 @@ async fn file_to_response(
774773
file_path.to_path_buf(),
775774
)),
776775
None,
776+
None,
777777
);
778778
};
779779
let name = escape_html(&file_name.to_string_lossy());
@@ -791,6 +791,7 @@ async fn file_to_response(
791791
codechat_editor_js_name,
792792
)),
793793
None,
794+
None,
794795
);
795796
};
796797
let codechat_editor_css_name = format!("CodeChatEditor{js_test_suffix}.css");
@@ -800,27 +801,28 @@ async fn file_to_response(
800801
codechat_editor_css_name,
801802
)),
802803
None,
804+
file_contents,
803805
);
804806
};
805807

806808
// Compare these files, since both have been canonicalized by
807809
// `try_canonical`.
808810
let is_current_file = file_path == current_filepath;
809811
let is_toc = http_request.flags == ProcessingTaskHttpRequestFlags::Toc;
810-
let (translation_results_string, path_to_toc) = if let Some(file_contents_text) = file_contents
811-
{
812-
if is_current_file || is_toc {
813-
source_to_codechat_for_web_string(file_contents_text, file_path, is_toc)
812+
let (translation_results_string, path_to_toc) =
813+
if let Some(ref file_contents_text) = file_contents {
814+
if is_current_file || is_toc {
815+
source_to_codechat_for_web_string(file_contents_text, file_path, is_toc)
816+
} else {
817+
// If this isn't the current file, then don't parse it.
818+
(TranslationResultsString::Unknown, None)
819+
}
814820
} else {
815-
// If this isn't the current file, then don't parse it.
816-
(TranslationResultsString::Unknown, None)
817-
}
818-
} else {
819-
(
820-
TranslationResultsString::Binary,
821-
find_path_to_toc(file_path),
822-
)
823-
};
821+
(
822+
TranslationResultsString::Binary,
823+
find_path_to_toc(file_path),
824+
)
825+
};
824826
let is_project = path_to_toc.is_some();
825827
// For project files, add in the sidebar. Convert this from a Windows path
826828
// to a Posix path if necessary.
@@ -852,6 +854,7 @@ async fn file_to_response(
852854
file_name,
853855
))),
854856
None,
857+
None,
855858
);
856859
};
857860
return (
@@ -871,13 +874,14 @@ async fn file_to_response(
871874
},
872875
),
873876
None,
877+
None,
874878
);
875879
}
876880

877881
let codechat_for_web = match translation_results_string {
878882
// The file type is binary. Ask the HTTP server to serve it raw.
879883
TranslationResultsString::Binary => return
880-
(SimpleHttpResponse::Bin(file_path.to_path_buf()), None)
884+
(SimpleHttpResponse::Bin(file_path.to_path_buf()), None, None)
881885
,
882886
// The file type is unknown. Serve it raw.
883887
TranslationResultsString::Unknown => {
@@ -887,11 +891,12 @@ async fn file_to_response(
887891
mime_guess::from_path(file_path).first_or_text_plain(),
888892
),
889893
None,
894+
None
890895
);
891896
}
892897
// Report a lexer error.
893898
TranslationResultsString::Err(err_string) => {
894-
return (SimpleHttpResponse::Err(SimpleHttpResponseError::LexerError(err_string)), None);
899+
return (SimpleHttpResponse::Err(SimpleHttpResponseError::LexerError(err_string)), None, None);
895900
}
896901
// This is a CodeChat file. The following code wraps the CodeChat for
897902
// web results in a CodeChat Editor Client webpage.
@@ -919,6 +924,7 @@ async fn file_to_response(
919924
</html>"#,
920925
)),
921926
None,
927+
None
922928
);
923929
}
924930
};
@@ -940,6 +946,7 @@ async fn file_to_response(
940946
file_path.to_path_buf(),
941947
)),
942948
None,
949+
None,
943950
);
944951
};
945952
let dir = path_display(raw_dir);
@@ -949,6 +956,7 @@ async fn file_to_response(
949956
file_path.to_path_buf(),
950957
)),
951958
None,
959+
None,
952960
);
953961
};
954962
// Build and return the webpage.
@@ -996,6 +1004,7 @@ async fn file_to_response(
9961004
cursor_position: None,
9971005
scroll_position: None,
9981006
}),
1007+
file_contents,
9991008
)
10001009
}
10011010

server/src/webserver/filewatcher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ async fn processing_task(file_path: &Path, app_state: web::Data<AppState>, conne
534534
// file, which will still produce an error.
535535
let empty_path = PathBuf::new();
536536
let cfp = current_filepath.as_ref().unwrap_or(&empty_path);
537-
let (simple_http_response, option_update) = make_simple_http_response(&http_request, cfp, false).await;
537+
let (simple_http_response, option_update, _) = make_simple_http_response(&http_request, cfp, false).await;
538538
if let Some(update) = option_update {
539539
// Send the update to the client.
540540
queue_send!(to_websocket_tx.send(EditorMessage {

server/src/webserver/vscode.rs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ use crate::{
5555
queue_send,
5656
webserver::{
5757
INITIAL_MESSAGE_ID, MESSAGE_ID_INCREMENT, ProcessingTaskHttpRequest, ResultOkTypes,
58-
SyncState, UpdateMessageContents, escape_html, file_to_response, filesystem_endpoint,
59-
get_server_url, html_wrapper, make_simple_http_response, path_to_url, try_canonicalize,
60-
try_read_as_text, url_to_path,
58+
SimpleHttpResponse, SimpleHttpResponseError, SyncState, UpdateMessageContents, escape_html,
59+
file_to_response, filesystem_endpoint, get_server_url, html_wrapper, path_to_url,
60+
try_canonicalize, try_read_as_text, url_to_path,
6161
},
6262
};
6363

@@ -454,21 +454,47 @@ pub async fn vscode_ide_websocket(
454454
// is a PDF file. (TODO: look at the magic
455455
// number also -- "%PDF").
456456
let use_pdf_js = http_request.file_path.extension() == Some(OsStr::new("pdf"));
457-
let (simple_http_response, option_update) = match file_contents_option {
457+
let (simple_http_response, option_update, file_contents) = match file_contents_option {
458458
Some(file_contents) => {
459459
// If there are Windows newlines, replace
460460
// with Unix; this is reversed when the
461461
// file is sent back to the IDE.
462462
eol = find_eol_type(&file_contents);
463-
let file_contents = file_contents.replace("\r\n", "\n");
464-
let ret = file_to_response(&http_request, &current_file, Some(&file_contents), use_pdf_js).await;
465-
source_code = file_contents;
466-
ret
463+
let file_contents = if use_pdf_js { file_contents } else { file_contents.replace("\r\n", "\n") };
464+
file_to_response(&http_request, &current_file, Some(file_contents), use_pdf_js).await
467465
},
468466
None => {
469467
// The file wasn't available in the IDE.
470468
// Look for it in the filesystem.
471-
make_simple_http_response(&http_request, &current_file, use_pdf_js).await
469+
match File::open(&http_request.file_path).await {
470+
Err(err) => (
471+
SimpleHttpResponse::Err(SimpleHttpResponseError::Io(err)),
472+
None,
473+
None
474+
),
475+
Ok(mut fc) => {
476+
let option_file_contents = try_read_as_text(&mut fc).await;
477+
let option_file_contents = if let Some(file_contents) = option_file_contents {
478+
eol = find_eol_type(&file_contents);
479+
let file_contents = if use_pdf_js { file_contents } else { file_contents.replace("\r\n", "\n") };
480+
Some(file_contents)
481+
} else {
482+
None
483+
};
484+
// <a id="binary-file-sniffer"></a>If this
485+
// is a binary file (meaning we can't read
486+
// the contents as UTF-8), send the
487+
// contents as none to signal this isn't a
488+
// text file.
489+
file_to_response(
490+
&http_request,
491+
&current_file,
492+
option_file_contents,
493+
use_pdf_js,
494+
)
495+
.await
496+
}
497+
}
472498
}
473499
};
474500
if let Some(update) = option_update {
@@ -482,6 +508,7 @@ pub async fn vscode_ide_websocket(
482508
};
483509
// We must clone here, since the original is
484510
// placed in the TX queue.
511+
source_code = file_contents.unwrap();
485512
code_mirror_doc = plain.doc.clone();
486513
code_mirror_doc_blocks = Some(plain.doc_blocks.clone());
487514
sync_state = SyncState::Pending(id);

0 commit comments

Comments
 (0)