Skip to content
Merged

Dev #68

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
#
# Copyright 2022-2024, axodotdev
# SPDX-License-Identifier: MIT or Apache-2.0
Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.1-prerelease.2/cargo-dist-installer.sh | sh"
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.2/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@v4
with:
Expand Down
14 changes: 7 additions & 7 deletions client/src/CodeChatEditor.mts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ declare global {
interface Window {
CodeChatEditor: {
// Called by the Client Framework.
open_lp: (all_source: CodeChatForWeb) => Promise<void>;
open_lp: (code_chat_for_web: CodeChatForWeb) => Promise<void>;
on_save: (_only_if_dirty: boolean) => Promise<void>;
allow_navigation: boolean;
};
Expand Down Expand Up @@ -211,8 +211,8 @@ const is_doc_only = () => {
};

// Wait for the DOM to load before opening the file.
const open_lp = async (all_source: CodeChatForWeb) =>
on_dom_content_loaded(() => _open_lp(all_source));
const open_lp = async (code_chat_for_web: CodeChatForWeb) =>
on_dom_content_loaded(() => _open_lp(code_chat_for_web));

// Store the HTML sent for CodeChat Editor documents. We can't simply use TinyMCE's [getContent](https://www.tiny.cloud/docs/tinymce/latest/apis/tinymce.editor/#getContent), since this modifies the content based on cleanup rules before returning it -- which causes applying diffs to this unexpectedly modified content to produce incorrect results. This text is the unmodified content sent from the IDE.
let doc_content = "";
Expand All @@ -224,7 +224,7 @@ let doc_content = "";
const _open_lp = async (
// A data structure provided by the server, containing the source and
// associated metadata. See[`AllSource`](#AllSource).
all_source: CodeChatForWeb,
code_chat_for_web: CodeChatForWeb,
) => {
// Use[URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
// to parse out the search parameters of this window's URL.
Expand All @@ -239,9 +239,9 @@ const _open_lp = async (
const editorMode = EditorMode[urlParams.get("mode") ?? "edit"];

// Get the<code><a href="#current_metadata">current_metadata</a></code> from
// the provided`all_source` struct and store it as a global variable.
current_metadata = all_source["metadata"];
const source = all_source["source"];
// the provided `code_chat_for_web` struct and store it as a global variable.
current_metadata = code_chat_for_web["metadata"];
const source = code_chat_for_web["source"];
const codechat_body = document.getElementById(
"CodeChat-body",
) as HTMLDivElement;
Expand Down
3 changes: 1 addition & 2 deletions client/src/CodeMirror-integration.mts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ import {
StateEffect,
EditorSelection,
Transaction,
TransactionSpec,
Annotation,
} from "@codemirror/state";
import { cpp } from "@codemirror/lang-cpp";
Expand Down Expand Up @@ -1027,7 +1026,7 @@ export const CodeMirror_load = async (
}
};

// Appply a `StringDiff` to the before string to produce the after string.
// Apply a `StringDiff` to the before string to produce the after string.
export const apply_diff_str = (before: string, diffs: StringDiff[]) => {
// Walk from the last diff to the first. JavaScript doesn't have reverse
// iteration AFAIK.
Expand Down
2 changes: 1 addition & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Changelog
=========

* [Github master](https://github.com/bjones1/CodeChat_Editor):
* No changes.
* Better support for opening a page in a web browser.
* v0.1.21, 2025-Jul-18:
* Allow specifying the host address the server binds to.
* Send server logs to the console by default.
Expand Down
53 changes: 32 additions & 21 deletions extensions/VSCode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import process from "node:process";

// ### Third-party packages
import escape from "escape-html";
import vscode, { commands, ViewColumn } from "vscode";
import vscode, { commands, Position, Range } from "vscode";
import { WebSocket } from "ws";

// ### Local packages
Expand All @@ -40,9 +40,7 @@ import {
MessageResult,
UpdateMessageContents,
} from "../../../client/src/shared_types.mjs";
//
// None.
//

// Globals
// -------
enum CodeChatEditorClientLocation {
Expand Down Expand Up @@ -123,12 +121,6 @@ export const activate = (context: vscode.ExtensionContext) => {
// <https://code.visualstudio.com/docs/extensionAPI/vscode-api#Event>`\_.
context.subscriptions.push(
vscode.workspace.onDidChangeTextDocument((event) => {
// If this change was produced by applying an
// `Update` from the Client, ignore it. Do this first, in case the update causes no changes to the content, since we still need to set `ignore_text_document_change` to `false`.
if (ignore_text_document_change) {
ignore_text_document_change = false;
return;
}
// VSCode sends empty change events -- ignore these.
if (event.contentChanges.length === 0) {
return;
Expand Down Expand Up @@ -373,15 +365,14 @@ export const activate = (context: vscode.ExtensionContext) => {
const source =
current_update.contents.source;
// Is this plain text, or a diff?
// This will produce a change event, which
// we'll ignore.
ignore_text_document_change = true;
// Use a workspace edit, since calls to
// `TextEditor.edit` must be made to the
// active editor only.
const wse = new vscode.WorkspaceEdit();
if ("Plain" in source) {
const new_contents = source.Plain.doc;
// This will produce a change event, which
// we'll ignore.
ignore_text_document_change = true;
// Use a workspace edit, since calls to
// `TextEditor.edit` must be made to the
// active editor only.
const wse = new vscode.WorkspaceEdit();
wse.replace(
doc.uri,
doc.validateRange(
Expand All @@ -392,12 +383,32 @@ export const activate = (context: vscode.ExtensionContext) => {
0,
),
),
new_contents,
source.Plain.doc,
);
vscode.workspace.applyEdit(wse);
} else {
assert("Diff" in source);
const diffs = source.Diff.doc;
for (const diff of diffs) {
// Convert from character offsets from the beginning of the document to a `Position`
// (line, then offset on that line) needed by VSCode.
const from = doc.positionAt(diff.from);
if (diff.to === undefined) {
// This is an insert.
wse.insert(doc.uri, from, diff.insert);
} else {
// This is a replace or delete.
const to = doc.positionAt(diff.to);
wse.replace(
doc.uri,
new Range(from, to),
diff.insert,
);
}
}
}
vscode.workspace.applyEdit(wse).then(() => ignore_text_document_change = false);
} else {
// TODO handle diffs.
// TODO: handle cursor/scroll position updates.
assert(false);
}
send_result(id);
Expand Down
4 changes: 3 additions & 1 deletion server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ lexer_explain = []
# ------------
[dependencies]
actix-files = "0.6"
actix-http = "3.9.0"
actix-rt = "2.9.0"
actix-web = "4"
actix-web-httpauth = "0.8.2"
actix-ws = "0.3.0"
bytes = { version = "1", features = ["serde"] }
chrono = "0.4"
Expand All @@ -68,7 +70,6 @@ mime_guess = "2.0.5"
minreq = "2.12.0"
normalize-line-endings = "0.3.0"
notify-debouncer-full = "0.5"
open = "5.3.0"
path-slash = "0.2.1"
pest = "2.7.14"
pest_derive = "2.7.14"
Expand All @@ -86,6 +87,7 @@ tokio-postgres = { version = "0.7", features = ["with-chrono-0_4"] }
ts-rs = { version = "11.0.1", features = ["serde-compat", "import-esm"] }
url = "2.5.2"
urlencoding = "2"
webbrowser = "1.0.5"

# [Windows-only
# dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies).
Expand Down
2 changes: 1 addition & 1 deletion server/dist-workspace.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ members = ["cargo:."]
# Config for 'dist'
[dist]
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.28.1-prerelease.2"
cargo-dist-version = "0.28.2"
# Extra static files to include in each App (path relative to this Cargo.toml's dir)
include = ["log4rs.yml", "hashLocations.json", "../client/static"]
# The installers to generate for each app
Expand Down
27 changes: 23 additions & 4 deletions server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use clap::{Parser, Subcommand};
use log::LevelFilter;

// ### Local
use code_chat_editor::webserver::{self, GetServerUrlError, path_to_url};
use code_chat_editor::webserver::{self, Credentials, GetServerUrlError, path_to_url};

// Data structures
// ---------------
Expand Down Expand Up @@ -78,6 +78,10 @@ enum Commands {
/// Control logging verbosity.
#[arg(short, long)]
log: Option<LevelFilter>,

/// Define the username:password used to limit access to the server. By default, access is unlimited.
#[arg(short, long, value_parser = parse_credentials)]
credentials: Option<Credentials>,
},
/// Start the webserver in a child process then exit.
Start {
Expand All @@ -96,15 +100,15 @@ enum Commands {
impl Cli {
fn run(self, addr: &SocketAddr) -> Result<(), Box<dyn std::error::Error>> {
match &self.command {
Commands::Serve { log } => {
Commands::Serve { log, credentials } => {
#[cfg(debug_assertions)]
if let Some(TestMode::Sleep) = self.test_mode {
// For testing, don't start the server at all.
std::thread::sleep(std::time::Duration::from_secs(10));
return Ok(());
}
webserver::configure_logger(log.unwrap_or(LevelFilter::Info))?;
webserver::main(addr).unwrap();
webserver::main(addr, credentials.clone()).unwrap();
}
Commands::Start { open } => {
// Poll the server to ensure it starts.
Expand Down Expand Up @@ -134,7 +138,7 @@ impl Cli {
let open_path = fs::canonicalize(open_path)?;
let open_path =
path_to_url(&format!("{address}/fw/fsb"), None, &open_path);
open::that_detached(&open_path)?;
webbrowser::open(&open_path)?;
}

return Ok(());
Expand Down Expand Up @@ -309,6 +313,21 @@ fn port_in_range(s: &str) -> Result<u16, String> {
}
}

fn parse_credentials(s: &str) -> Result<Credentials, String> {
let split_: Vec<_> = s.split(":").collect();
if split_.len() != 2 {
Err(format!(
"Unable to parse credentials as username:password; found {} colon-separated string(s)",
split_.len()
))
} else {
Ok(Credentials {
username: split_[0].to_string(),
password: split_[1].to_string(),
})
}
}

fn fix_addr(addr: &SocketAddr) -> SocketAddr {
if addr.ip() == IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)) {
let mut addr = *addr;
Expand Down
Loading
Loading