Merge branch 'main' into auto-update
5
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
||||||
/zed.xcworkspace
|
/zed.xcworkspace
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/script/node_modules
|
/script/node_modules
|
||||||
/crates/server/.env.toml
|
/styles/node_modules
|
||||||
/crates/server/static/styles.css
|
/crates/collab/.env.toml
|
||||||
|
/crates/collab/static/styles.css
|
||||||
/vendor/bin
|
/vendor/bin
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
collaborators = ["nathansobo", "as-cii", "maxbrunsfeld", "iamnbutler", "Kethku"]
|
collaborators = ["nathansobo", "as-cii", "maxbrunsfeld", "iamnbutler", "gibusu", "Kethku"]
|
||||||
|
|
634
Cargo.lock
generated
14
Dockerfile
|
@ -14,20 +14,20 @@ RUN --mount=type=cache,target=./script/node_modules \
|
||||||
RUN --mount=type=cache,target=./script/node_modules \
|
RUN --mount=type=cache,target=./script/node_modules \
|
||||||
script/build-css --release
|
script/build-css --release
|
||||||
|
|
||||||
# Compile server
|
# Compile collab server
|
||||||
RUN --mount=type=cache,target=./script/node_modules \
|
RUN --mount=type=cache,target=./script/node_modules \
|
||||||
--mount=type=cache,target=/usr/local/cargo/registry \
|
--mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
--mount=type=cache,target=./target \
|
--mount=type=cache,target=./target \
|
||||||
cargo build --release --package zed-server --bin zed-server
|
cargo build --release --package collab --bin collab
|
||||||
|
|
||||||
# Copy server binary out of cached directory
|
# Copy collab server binary out of cached directory
|
||||||
RUN --mount=type=cache,target=./target \
|
RUN --mount=type=cache,target=./target \
|
||||||
cp /app/target/release/zed-server /app/zed-server
|
cp /app/target/release/collab /app/collab
|
||||||
|
|
||||||
# Copy server binary to the runtime image
|
# Copy collab server binary to the runtime image
|
||||||
FROM debian:bullseye-slim as runtime
|
FROM debian:bullseye-slim as runtime
|
||||||
RUN apt-get update; \
|
RUN apt-get update; \
|
||||||
apt-get install -y --no-install-recommends libcurl4-openssl-dev ca-certificates
|
apt-get install -y --no-install-recommends libcurl4-openssl-dev ca-certificates
|
||||||
WORKDIR app
|
WORKDIR app
|
||||||
COPY --from=builder /app/zed-server /app
|
COPY --from=builder /app/collab /app
|
||||||
ENTRYPOINT ["/app/zed-server"]
|
ENTRYPOINT ["/app/collab"]
|
||||||
|
|
|
@ -11,5 +11,5 @@ RUN apt-get update; \
|
||||||
apt-get install -y --no-install-recommends libssl1.1
|
apt-get install -y --no-install-recommends libssl1.1
|
||||||
WORKDIR app
|
WORKDIR app
|
||||||
COPY --from=builder /app/bin/sqlx /app
|
COPY --from=builder /app/bin/sqlx /app
|
||||||
COPY ./server/migrations /app/migrations
|
COPY ./collab/migrations /app/migrations
|
||||||
ENTRYPOINT ["/app/sqlx", "migrate", "run"]
|
ENTRYPOINT ["/app/sqlx", "migrate", "run"]
|
||||||
|
|
2
Procfile
|
@ -1,2 +1,2 @@
|
||||||
web: cd ../zed.dev && PORT=3000 npx next dev
|
web: cd ../zed.dev && PORT=3000 npx next dev
|
||||||
collab: cd crates/server && cargo run
|
collab: cd crates/collab && cargo run
|
||||||
|
|
|
@ -23,7 +23,7 @@ script/sqlx migrate run
|
||||||
script/seed-db
|
script/seed-db
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `zed.dev` and the collaboration server.
|
Run the web frontend and the collaboration server.
|
||||||
|
|
||||||
```
|
```
|
||||||
brew install foreman
|
brew install foreman
|
||||||
|
|
Before Width: | Height: | Size: 879 B After Width: | Height: | Size: 879 B |
Before Width: | Height: | Size: 979 B After Width: | Height: | Size: 979 B |
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 464 B |
Before Width: | Height: | Size: 499 B After Width: | Height: | Size: 499 B |
Before Width: | Height: | Size: 317 B After Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 284 B |
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 512 B |
Before Width: | Height: | Size: 516 B After Width: | Height: | Size: 516 B |
Before Width: | Height: | Size: 445 B After Width: | Height: | Size: 445 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 776 B After Width: | Height: | Size: 776 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 733 B After Width: | Height: | Size: 733 B |
Before Width: | Height: | Size: 676 B After Width: | Height: | Size: 676 B |
Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 322 B |
264
assets/keymaps/default.json
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
{
|
||||||
|
"*": {
|
||||||
|
"ctrl-alt-cmd-f": "workspace::FollowNextCollaborator",
|
||||||
|
"cmd-s": "workspace::Save",
|
||||||
|
"cmd-alt-i": "zed::DebugElements",
|
||||||
|
"cmd-k cmd-left": "workspace::ActivatePreviousPane",
|
||||||
|
"cmd-k cmd-right": "workspace::ActivateNextPane",
|
||||||
|
"cmd-=": "zed::IncreaseBufferFontSize",
|
||||||
|
"cmd--": "zed::DecreaseBufferFontSize",
|
||||||
|
"cmd-,": "zed::OpenSettings"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"up": "menu::SelectPrev",
|
||||||
|
"ctrl-p": "menu::SelectPrev",
|
||||||
|
"down": "menu::SelectNext",
|
||||||
|
"ctrl-n": "menu::SelectNext",
|
||||||
|
"cmd-up": "menu::SelectFirst",
|
||||||
|
"cmd-down": "menu::SelectLast",
|
||||||
|
"enter": "menu::Confirm",
|
||||||
|
"escape": "menu::Cancel"
|
||||||
|
},
|
||||||
|
"Pane": {
|
||||||
|
"shift-cmd-{": "pane::ActivatePrevItem",
|
||||||
|
"shift-cmd-}": "pane::ActivateNextItem",
|
||||||
|
"cmd-w": "pane::CloseActiveItem",
|
||||||
|
"alt-cmd-w": "pane::CloseInactiveItems",
|
||||||
|
"ctrl--": "pane::GoBack",
|
||||||
|
"shift-ctrl-_": "pane::GoForward",
|
||||||
|
"cmd-k up": [
|
||||||
|
"pane::Split",
|
||||||
|
"Up"
|
||||||
|
],
|
||||||
|
"cmd-k down": [
|
||||||
|
"pane::Split",
|
||||||
|
"Down"
|
||||||
|
],
|
||||||
|
"cmd-k left": [
|
||||||
|
"pane::Split",
|
||||||
|
"Left"
|
||||||
|
],
|
||||||
|
"cmd-k right": [
|
||||||
|
"pane::Split",
|
||||||
|
"Right"
|
||||||
|
],
|
||||||
|
"cmd-shift-F": "project_search::ToggleFocus",
|
||||||
|
"cmd-f": "project_search::ToggleFocus",
|
||||||
|
"cmd-g": "search::SelectNextMatch",
|
||||||
|
"cmd-shift-G": "search::SelectPrevMatch"
|
||||||
|
},
|
||||||
|
"Workspace": {
|
||||||
|
"cmd-shift-F": "project_search::Deploy",
|
||||||
|
"cmd-k cmd-t": "theme_selector::Toggle",
|
||||||
|
"cmd-k t": "theme_selector::Reload",
|
||||||
|
"cmd-t": "project_symbols::Toggle",
|
||||||
|
"cmd-p": "file_finder::Toggle",
|
||||||
|
"cmd-shift-P": "command_palette::Toggle",
|
||||||
|
"alt-shift-D": "diagnostics::Deploy",
|
||||||
|
"ctrl-alt-cmd-j": "journal::NewJournalEntry",
|
||||||
|
"cmd-1": [
|
||||||
|
"workspace::ToggleSidebarItemFocus",
|
||||||
|
{
|
||||||
|
"side": "Left",
|
||||||
|
"item_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cmd-shift-!": [
|
||||||
|
"workspace::ToggleSidebarItem",
|
||||||
|
{
|
||||||
|
"side": "Left",
|
||||||
|
"item_index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ProjectSearchBar": {
|
||||||
|
"enter": "project_search::Search",
|
||||||
|
"cmd-enter": "project_search::SearchInNew"
|
||||||
|
},
|
||||||
|
"BufferSearchBar": {
|
||||||
|
"escape": "buffer_search::Dismiss",
|
||||||
|
"cmd-f": "buffer_search::FocusEditor",
|
||||||
|
"enter": "search::SelectNextMatch",
|
||||||
|
"shift-enter": "search::SelectPrevMatch"
|
||||||
|
},
|
||||||
|
"Editor": {
|
||||||
|
"escape": "editor::Cancel",
|
||||||
|
"backspace": "editor::Backspace",
|
||||||
|
"ctrl-h": "editor::Backspace",
|
||||||
|
"delete": "editor::Delete",
|
||||||
|
"ctrl-d": "editor::Delete",
|
||||||
|
"tab": "editor::Tab",
|
||||||
|
"shift-tab": "editor::TabPrev",
|
||||||
|
"cmd-[": "editor::Outdent",
|
||||||
|
"cmd-]": "editor::Indent",
|
||||||
|
"ctrl-shift-K": "editor::DeleteLine",
|
||||||
|
"alt-backspace": "editor::DeleteToPreviousWordStart",
|
||||||
|
"alt-h": "editor::DeleteToPreviousWordStart",
|
||||||
|
"ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart",
|
||||||
|
"ctrl-alt-h": "editor::DeleteToPreviousSubwordStart",
|
||||||
|
"alt-delete": "editor::DeleteToNextWordEnd",
|
||||||
|
"alt-d": "editor::DeleteToNextWordEnd",
|
||||||
|
"ctrl-alt-delete": "editor::DeleteToNextSubwordEnd",
|
||||||
|
"ctrl-alt-d": "editor::DeleteToNextSubwordEnd",
|
||||||
|
"cmd-backspace": "editor::DeleteToBeginningOfLine",
|
||||||
|
"cmd-delete": "editor::DeleteToEndOfLine",
|
||||||
|
"ctrl-k": "editor::CutToEndOfLine",
|
||||||
|
"cmd-shift-D": "editor::DuplicateLine",
|
||||||
|
"ctrl-cmd-up": "editor::MoveLineUp",
|
||||||
|
"ctrl-cmd-down": "editor::MoveLineDown",
|
||||||
|
"cmd-x": "editor::Cut",
|
||||||
|
"cmd-c": "editor::Copy",
|
||||||
|
"cmd-v": "editor::Paste",
|
||||||
|
"cmd-z": "editor::Undo",
|
||||||
|
"cmd-shift-Z": "editor::Redo",
|
||||||
|
"up": "editor::MoveUp",
|
||||||
|
"down": "editor::MoveDown",
|
||||||
|
"left": "editor::MoveLeft",
|
||||||
|
"right": "editor::MoveRight",
|
||||||
|
"ctrl-p": "editor::MoveUp",
|
||||||
|
"ctrl-n": "editor::MoveDown",
|
||||||
|
"ctrl-b": "editor::MoveLeft",
|
||||||
|
"ctrl-f": "editor::MoveRight",
|
||||||
|
"alt-left": "editor::MoveToPreviousWordStart",
|
||||||
|
"alt-b": "editor::MoveToPreviousWordStart",
|
||||||
|
"ctrl-alt-left": "editor::MoveToPreviousSubwordStart",
|
||||||
|
"ctrl-alt-b": "editor::MoveToPreviousSubwordStart",
|
||||||
|
"alt-right": "editor::MoveToNextWordEnd",
|
||||||
|
"alt-f": "editor::MoveToNextWordEnd",
|
||||||
|
"ctrl-alt-right": "editor::MoveToNextSubwordEnd",
|
||||||
|
"ctrl-alt-f": "editor::MoveToNextSubwordEnd",
|
||||||
|
"cmd-left": "editor::MoveToBeginningOfLine",
|
||||||
|
"ctrl-a": "editor::MoveToBeginningOfLine",
|
||||||
|
"cmd-right": "editor::MoveToEndOfLine",
|
||||||
|
"ctrl-e": "editor::MoveToEndOfLine",
|
||||||
|
"cmd-up": "editor::MoveToBeginning",
|
||||||
|
"cmd-down": "editor::MoveToEnd",
|
||||||
|
"shift-up": "editor::SelectUp",
|
||||||
|
"ctrl-shift-P": "editor::SelectUp",
|
||||||
|
"shift-down": "editor::SelectDown",
|
||||||
|
"ctrl-shift-N": "editor::SelectDown",
|
||||||
|
"shift-left": "editor::SelectLeft",
|
||||||
|
"ctrl-shift-B": "editor::SelectLeft",
|
||||||
|
"shift-right": "editor::SelectRight",
|
||||||
|
"ctrl-shift-F": "editor::SelectRight",
|
||||||
|
"alt-shift-left": "editor::SelectToPreviousWordStart",
|
||||||
|
"alt-shift-B": "editor::SelectToPreviousWordStart",
|
||||||
|
"ctrl-alt-shift-left": "editor::SelectToPreviousSubwordStart",
|
||||||
|
"ctrl-alt-shift-B": "editor::SelectToPreviousSubwordStart",
|
||||||
|
"alt-shift-right": "editor::SelectToNextWordEnd",
|
||||||
|
"alt-shift-F": "editor::SelectToNextWordEnd",
|
||||||
|
"ctrl-alt-shift-right": "editor::SelectToNextSubwordEnd",
|
||||||
|
"cmd-shift-up": "editor::SelectToBeginning",
|
||||||
|
"cmd-shift-down": "editor::SelectToEnd",
|
||||||
|
"cmd-a": "editor::SelectAll",
|
||||||
|
"cmd-l": "editor::SelectLine",
|
||||||
|
"cmd-shift-L": "editor::SplitSelectionIntoLines",
|
||||||
|
"cmd-alt-up": "editor::AddSelectionAbove",
|
||||||
|
"cmd-ctrl-p": "editor::AddSelectionAbove",
|
||||||
|
"cmd-alt-down": "editor::AddSelectionBelow",
|
||||||
|
"cmd-ctrl-n": "editor::AddSelectionBelow",
|
||||||
|
"ctrl-alt-shift-F": "editor::SelectToNextSubwordEnd",
|
||||||
|
"cmd-shift-left": [
|
||||||
|
"editor::SelectToBeginningOfLine",
|
||||||
|
{
|
||||||
|
"stop_at_soft_wraps": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ctrl-shift-A": [
|
||||||
|
"editor::SelectToBeginningOfLine",
|
||||||
|
{
|
||||||
|
"stop_at_soft_wraps": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cmd-shift-right": [
|
||||||
|
"editor::SelectToEndOfLine",
|
||||||
|
{
|
||||||
|
"stop_at_soft_wraps": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ctrl-shift-E": [
|
||||||
|
"editor::SelectToEndOfLine",
|
||||||
|
{
|
||||||
|
"stop_at_soft_wraps": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cmd-d": [
|
||||||
|
"editor::SelectNext",
|
||||||
|
{
|
||||||
|
"replace_newest": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cmd-k cmd-d": [
|
||||||
|
"editor::SelectNext",
|
||||||
|
{
|
||||||
|
"replace_newest": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cmd-/": "editor::ToggleComments",
|
||||||
|
"alt-up": "editor::SelectLargerSyntaxNode",
|
||||||
|
"ctrl-w": "editor::SelectLargerSyntaxNode",
|
||||||
|
"alt-down": "editor::SelectSmallerSyntaxNode",
|
||||||
|
"ctrl-shift-W": "editor::SelectSmallerSyntaxNode",
|
||||||
|
"cmd-u": "editor::UndoSelection",
|
||||||
|
"cmd-shift-U": "editor::RedoSelection",
|
||||||
|
"f8": "editor::GoToNextDiagnostic",
|
||||||
|
"shift-f8": "editor::GoToPrevDiagnostic",
|
||||||
|
"f2": "editor::Rename",
|
||||||
|
"f12": "editor::GoToDefinition",
|
||||||
|
"alt-shift-f12": "editor::FindAllReferences",
|
||||||
|
"ctrl-m": "editor::MoveToEnclosingBracket",
|
||||||
|
"pageup": "editor::PageUp",
|
||||||
|
"pagedown": "editor::PageDown",
|
||||||
|
"alt-cmd-[": "editor::Fold",
|
||||||
|
"alt-cmd-]": "editor::UnfoldLines",
|
||||||
|
"alt-cmd-f": "editor::FoldSelectedRanges",
|
||||||
|
"ctrl-space": "editor::ShowCompletions",
|
||||||
|
"cmd-.": "editor::ToggleCodeActions",
|
||||||
|
"alt-enter": "editor::OpenExcerpts",
|
||||||
|
"cmd-f10": "editor::RestartLanguageServer"
|
||||||
|
},
|
||||||
|
"Editor && renaming": {
|
||||||
|
"enter": "editor::ConfirmRename"
|
||||||
|
},
|
||||||
|
"Editor && showing_completions": {
|
||||||
|
"enter": "editor::ConfirmCompletion",
|
||||||
|
"tab": "editor::ConfirmCompletion"
|
||||||
|
},
|
||||||
|
"Editor && showing_code_actions": {
|
||||||
|
"enter": "editor::ConfirmCodeAction"
|
||||||
|
},
|
||||||
|
"Editor && mode == full": {
|
||||||
|
"enter": "editor::Newline",
|
||||||
|
"cmd-f": [
|
||||||
|
"buffer_search::Deploy",
|
||||||
|
{
|
||||||
|
"focus": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cmd-e": [
|
||||||
|
"buffer_search::Deploy",
|
||||||
|
{
|
||||||
|
"focus": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cmd-shift-O": "outline::Toggle",
|
||||||
|
"ctrl-g": "go_to_line::Toggle"
|
||||||
|
},
|
||||||
|
"Editor && mode == auto_height": {
|
||||||
|
"alt-enter": [
|
||||||
|
"editor::Input",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"GoToLine": {
|
||||||
|
"escape": "go_to_line::Toggle",
|
||||||
|
"enter": "go_to_line::Confirm"
|
||||||
|
},
|
||||||
|
"ChatPanel": {
|
||||||
|
"enter": "chat_panel::Send"
|
||||||
|
},
|
||||||
|
"ProjectPanel": {
|
||||||
|
"left": "project_panel::CollapseSelectedEntry",
|
||||||
|
"right": "project_panel::ExpandSelectedEntry"
|
||||||
|
}
|
||||||
|
}
|
93
assets/keymaps/vim.json
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
{
|
||||||
|
"Editor && VimControl": {
|
||||||
|
"i": [
|
||||||
|
"vim::SwitchMode",
|
||||||
|
"Insert"
|
||||||
|
],
|
||||||
|
"g": [
|
||||||
|
"vim::PushOperator",
|
||||||
|
{
|
||||||
|
"Namespace": "G"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"h": "vim::Left",
|
||||||
|
"j": "vim::Down",
|
||||||
|
"k": "vim::Up",
|
||||||
|
"l": "vim::Right",
|
||||||
|
"0": "vim::StartOfLine",
|
||||||
|
"shift-$": "vim::EndOfLine",
|
||||||
|
"shift-G": "vim::EndOfDocument",
|
||||||
|
"w": "vim::NextWordStart",
|
||||||
|
"shift-W": [
|
||||||
|
"vim::NextWordStart",
|
||||||
|
{
|
||||||
|
"ignorePunctuation": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"e": "vim::NextWordEnd",
|
||||||
|
"shift-E": [
|
||||||
|
"vim::NextWordEnd",
|
||||||
|
{
|
||||||
|
"ignorePunctuation": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"b": "vim::PreviousWordStart",
|
||||||
|
"shift-B": [
|
||||||
|
"vim::PreviousWordStart",
|
||||||
|
{
|
||||||
|
"ignorePunctuation": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"escape": [
|
||||||
|
"vim::SwitchMode",
|
||||||
|
"Normal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Editor && vim_operator == g": {
|
||||||
|
"g": "vim::StartOfDocument"
|
||||||
|
},
|
||||||
|
"Editor && vim_mode == insert": {
|
||||||
|
"escape": "vim::NormalBefore",
|
||||||
|
"ctrl-c": "vim::NormalBefore"
|
||||||
|
},
|
||||||
|
"Editor && vim_mode == normal": {
|
||||||
|
"c": [
|
||||||
|
"vim::PushOperator",
|
||||||
|
"Change"
|
||||||
|
],
|
||||||
|
"d": [
|
||||||
|
"vim::PushOperator",
|
||||||
|
"Delete"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Editor && vim_operator == c": {
|
||||||
|
"w": [
|
||||||
|
"vim::NextWordEnd",
|
||||||
|
{
|
||||||
|
"ignorePunctuation": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shift-W": [
|
||||||
|
"vim::NextWordEnd",
|
||||||
|
{
|
||||||
|
"ignorePunctuation": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Editor && vim_operator == d": {
|
||||||
|
"w": [
|
||||||
|
"vim::NextWordStart",
|
||||||
|
{
|
||||||
|
"ignorePunctuation": false,
|
||||||
|
"stopAtNewline": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shift-W": [
|
||||||
|
"vim::NextWordStart",
|
||||||
|
{
|
||||||
|
"ignorePunctuation": true,
|
||||||
|
"stopAtNewline": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
1338
assets/themes/dark.json
Normal file
1338
assets/themes/light.json
Normal file
14
crates/assets/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "assets"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/assets.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gpui = { path = "../gpui" }
|
||||||
|
anyhow = "1.0.38"
|
||||||
|
rust-embed = { version = "6.3", features = ["include-exclude"] }
|
||||||
|
|
|
@ -3,7 +3,7 @@ use gpui::AssetSource;
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
#[folder = "assets"]
|
#[folder = "../../assets"]
|
||||||
#[exclude = "*.DS_Store"]
|
#[exclude = "*.DS_Store"]
|
||||||
pub struct Assets;
|
pub struct Assets;
|
||||||
|
|
|
@ -8,9 +8,10 @@ path = "src/auto_update.rs"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpui = { path = "../gpui" }
|
|
||||||
theme = { path = "../theme" }
|
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
|
gpui = { path = "../gpui" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use client::http::{self, HttpClient};
|
use client::http::{self, HttpClient};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
action,
|
actions,
|
||||||
elements::{Empty, MouseEventHandler, Text},
|
elements::{Empty, MouseEventHandler, Text},
|
||||||
platform::AppVersion,
|
platform::AppVersion,
|
||||||
AsyncAppContext, Element, Entity, ModelContext, ModelHandle, MutableAppContext, Task, View,
|
AsyncAppContext, Element, Entity, ModelContext, ModelHandle, MutableAppContext, Task, View,
|
||||||
|
@ -9,10 +9,11 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use settings::Settings;
|
||||||
use smol::{fs::File, io::AsyncReadExt, process::Command};
|
use smol::{fs::File, io::AsyncReadExt, process::Command};
|
||||||
use std::{env, ffi::OsString, path::PathBuf, sync::Arc, time::Duration};
|
use std::{env, ffi::OsString, path::PathBuf, sync::Arc, time::Duration};
|
||||||
use surf::Request;
|
use surf::Request;
|
||||||
use workspace::{ItemHandle, Settings, StatusItemView};
|
use workspace::{ItemHandle, StatusItemView};
|
||||||
|
|
||||||
const POLL_INTERVAL: Duration = Duration::from_secs(60 * 60);
|
const POLL_INTERVAL: Duration = Duration::from_secs(60 * 60);
|
||||||
const ACCESS_TOKEN: &'static str = "618033988749894";
|
const ACCESS_TOKEN: &'static str = "618033988749894";
|
||||||
|
@ -24,6 +25,8 @@ lazy_static! {
|
||||||
pub static ref ZED_APP_PATH: Option<PathBuf> = env::var("ZED_APP_PATH").ok().map(PathBuf::from);
|
pub static ref ZED_APP_PATH: Option<PathBuf> = env::var("ZED_APP_PATH").ok().map(PathBuf::from);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actions!(auto_update, [Check, DismissErrorMessage]);
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub enum AutoUpdateStatus {
|
pub enum AutoUpdateStatus {
|
||||||
Idle,
|
Idle,
|
||||||
|
@ -46,8 +49,6 @@ pub struct AutoUpdateIndicator {
|
||||||
updater: Option<ModelHandle<AutoUpdater>>,
|
updater: Option<ModelHandle<AutoUpdater>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
action!(DismissErrorMessage);
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct JsonRelease {
|
struct JsonRelease {
|
||||||
version: String,
|
version: String,
|
||||||
|
@ -66,16 +67,15 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut Mutab
|
||||||
updater
|
updater
|
||||||
});
|
});
|
||||||
cx.set_global(Some(auto_updater));
|
cx.set_global(Some(auto_updater));
|
||||||
|
cx.add_global_action(|_: &Check, cx| {
|
||||||
|
if let Some(updater) = AutoUpdater::get(cx) {
|
||||||
|
updater.update(cx, |updater, cx| updater.poll(cx));
|
||||||
|
}
|
||||||
|
});
|
||||||
cx.add_action(AutoUpdateIndicator::dismiss_error_message);
|
cx.add_action(AutoUpdateIndicator::dismiss_error_message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(cx: &mut MutableAppContext) {
|
|
||||||
if let Some(updater) = AutoUpdater::get(cx) {
|
|
||||||
updater.update(cx, |updater, cx| updater.poll(cx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AutoUpdater {
|
impl AutoUpdater {
|
||||||
fn get(cx: &mut MutableAppContext) -> Option<ModelHandle<Self>> {
|
fn get(cx: &mut MutableAppContext) -> Option<ModelHandle<Self>> {
|
||||||
cx.default_global::<Option<ModelHandle<Self>>>().clone()
|
cx.default_global::<Option<ModelHandle<Self>>>().clone()
|
||||||
|
|
|
@ -14,6 +14,7 @@ gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@ use gpui::{
|
||||||
use language::{Buffer, OutlineItem};
|
use language::{Buffer, OutlineItem};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use search::ProjectSearchView;
|
use search::ProjectSearchView;
|
||||||
|
use settings::Settings;
|
||||||
use theme::SyntaxTheme;
|
use theme::SyntaxTheme;
|
||||||
use workspace::{ItemHandle, Settings, ToolbarItemLocation, ToolbarItemView};
|
use workspace::{ItemHandle, ToolbarItemLocation, ToolbarItemView};
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
UpdateLocation,
|
UpdateLocation,
|
||||||
|
|
|
@ -11,6 +11,7 @@ doctest = false
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -4,19 +4,18 @@ use client::{
|
||||||
};
|
};
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
action,
|
actions,
|
||||||
elements::*,
|
elements::*,
|
||||||
keymap::Binding,
|
|
||||||
platform::CursorStyle,
|
platform::CursorStyle,
|
||||||
views::{ItemType, Select, SelectStyle},
|
views::{ItemType, Select, SelectStyle},
|
||||||
AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View,
|
AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View,
|
||||||
ViewContext, ViewHandle,
|
ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use postage::prelude::Stream;
|
use postage::prelude::Stream;
|
||||||
|
use settings::{Settings, SoftWrap};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::{OffsetDateTime, UtcOffset};
|
use time::{OffsetDateTime, UtcOffset};
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
use workspace::{settings::SoftWrap, Settings};
|
|
||||||
|
|
||||||
const MESSAGE_LOADING_THRESHOLD: usize = 50;
|
const MESSAGE_LOADING_THRESHOLD: usize = 50;
|
||||||
|
|
||||||
|
@ -33,14 +32,11 @@ pub struct ChatPanel {
|
||||||
|
|
||||||
pub enum Event {}
|
pub enum Event {}
|
||||||
|
|
||||||
action!(Send);
|
actions!(chat_panel, [Send, LoadMoreMessages]);
|
||||||
action!(LoadMoreMessages);
|
|
||||||
|
|
||||||
pub fn init(cx: &mut MutableAppContext) {
|
pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(ChatPanel::send);
|
cx.add_action(ChatPanel::send);
|
||||||
cx.add_action(ChatPanel::load_more_messages);
|
cx.add_action(ChatPanel::load_more_messages);
|
||||||
|
|
||||||
cx.add_bindings(vec![Binding::new("enter", Send, Some("ChatPanel"))]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChatPanel {
|
impl ChatPanel {
|
||||||
|
|
24
crates/cli/Cargo.toml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
[package]
|
||||||
|
name = "cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/cli.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "cli"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
clap = { version = "3.1", features = ["derive"] }
|
||||||
|
dirs = "3.0"
|
||||||
|
ipc-channel = "0.16"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
core-foundation = "0.9"
|
||||||
|
core-services = "0.2"
|
||||||
|
plist = "1.3"
|
22
crates/cli/src/cli.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
pub use ipc_channel::ipc;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct IpcHandshake {
|
||||||
|
pub requests: ipc::IpcSender<CliRequest>,
|
||||||
|
pub responses: ipc::IpcReceiver<CliResponse>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum CliRequest {
|
||||||
|
Open { paths: Vec<PathBuf>, wait: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum CliResponse {
|
||||||
|
Ping,
|
||||||
|
Stdout { message: String },
|
||||||
|
Stderr { message: String },
|
||||||
|
Exit { status: i32 },
|
||||||
|
}
|
124
crates/cli/src/main.rs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use clap::Parser;
|
||||||
|
use cli::{CliRequest, CliResponse, IpcHandshake};
|
||||||
|
use core_foundation::{
|
||||||
|
array::{CFArray, CFIndex},
|
||||||
|
string::kCFStringEncodingUTF8,
|
||||||
|
url::{CFURLCreateWithBytes, CFURL},
|
||||||
|
};
|
||||||
|
use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType};
|
||||||
|
use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{ffi::OsStr, fs, path::PathBuf, ptr};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[clap(name = "zed", global_setting(clap::AppSettings::NoAutoVersion))]
|
||||||
|
struct Args {
|
||||||
|
/// Wait for all of the given paths to be closed before exiting.
|
||||||
|
#[clap(short, long)]
|
||||||
|
wait: bool,
|
||||||
|
/// A sequence of space-separated paths that you want to open.
|
||||||
|
#[clap()]
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
|
/// Print Zed's version and the app path.
|
||||||
|
#[clap(short, long)]
|
||||||
|
version: bool,
|
||||||
|
/// Custom Zed.app path
|
||||||
|
#[clap(short, long)]
|
||||||
|
bundle_path: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct InfoPlist {
|
||||||
|
#[serde(rename = "CFBundleShortVersionString")]
|
||||||
|
bundle_short_version_string: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
let bundle_path = if let Some(bundle_path) = args.bundle_path {
|
||||||
|
bundle_path.canonicalize()?
|
||||||
|
} else {
|
||||||
|
locate_bundle()?
|
||||||
|
};
|
||||||
|
|
||||||
|
if args.version {
|
||||||
|
let plist_path = bundle_path.join("Contents/Info.plist");
|
||||||
|
let plist = plist::from_file::<_, InfoPlist>(plist_path)?;
|
||||||
|
println!(
|
||||||
|
"Zed {} – {}",
|
||||||
|
plist.bundle_short_version_string,
|
||||||
|
bundle_path.to_string_lossy()
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (tx, rx) = launch_app(bundle_path)?;
|
||||||
|
|
||||||
|
tx.send(CliRequest::Open {
|
||||||
|
paths: args
|
||||||
|
.paths
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| fs::canonicalize(path).map_err(|error| anyhow!(error)))
|
||||||
|
.collect::<Result<Vec<PathBuf>>>()?,
|
||||||
|
wait: args.wait,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
while let Ok(response) = rx.recv() {
|
||||||
|
match response {
|
||||||
|
CliResponse::Ping => {}
|
||||||
|
CliResponse::Stdout { message } => println!("{message}"),
|
||||||
|
CliResponse::Stderr { message } => eprintln!("{message}"),
|
||||||
|
CliResponse::Exit { status } => std::process::exit(status),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn locate_bundle() -> Result<PathBuf> {
|
||||||
|
let cli_path = std::env::current_exe()?.canonicalize()?;
|
||||||
|
let mut app_path = cli_path.clone();
|
||||||
|
while app_path.extension() != Some(OsStr::new("app")) {
|
||||||
|
if !app_path.pop() {
|
||||||
|
return Err(anyhow!("cannot find app bundle containing {:?}", cli_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(app_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn launch_app(app_path: PathBuf) -> Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> {
|
||||||
|
let (server, server_name) = IpcOneShotServer::<IpcHandshake>::new()?;
|
||||||
|
let url = format!("zed-cli://{server_name}");
|
||||||
|
|
||||||
|
let status = unsafe {
|
||||||
|
let app_url =
|
||||||
|
CFURL::from_path(&app_path, true).ok_or_else(|| anyhow!("invalid app path"))?;
|
||||||
|
let url_to_open = CFURL::wrap_under_create_rule(CFURLCreateWithBytes(
|
||||||
|
ptr::null(),
|
||||||
|
url.as_ptr(),
|
||||||
|
url.len() as CFIndex,
|
||||||
|
kCFStringEncodingUTF8,
|
||||||
|
ptr::null(),
|
||||||
|
));
|
||||||
|
let urls_to_open = CFArray::from_copyable(&[url_to_open.as_concrete_TypeRef()]);
|
||||||
|
LSOpenFromURLSpec(
|
||||||
|
&LSLaunchURLSpec {
|
||||||
|
appURL: app_url.as_concrete_TypeRef(),
|
||||||
|
itemURLs: urls_to_open.as_concrete_TypeRef(),
|
||||||
|
passThruParams: ptr::null(),
|
||||||
|
launchFlags: kLSLaunchDefaults,
|
||||||
|
asyncRefCon: ptr::null_mut(),
|
||||||
|
},
|
||||||
|
ptr::null_mut(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
let (_, handshake) = server.accept()?;
|
||||||
|
Ok((handshake.requests, handshake.responses))
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("cannot start {:?}", app_path))
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ async-tungstenite = { version = "0.16", features = ["async-tls"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
image = "0.23"
|
image = "0.23"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4"
|
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
|
|
|
@ -13,7 +13,7 @@ use async_tungstenite::tungstenite::{
|
||||||
};
|
};
|
||||||
use futures::{future::LocalBoxFuture, FutureExt, StreamExt};
|
use futures::{future::LocalBoxFuture, FutureExt, StreamExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, AsyncAppContext,
|
actions, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, AsyncAppContext,
|
||||||
Entity, ModelContext, ModelHandle, MutableAppContext, Task, View, ViewContext, ViewHandle,
|
Entity, ModelContext, ModelHandle, MutableAppContext, Task, View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use http::HttpClient;
|
use http::HttpClient;
|
||||||
|
@ -50,7 +50,7 @@ lazy_static! {
|
||||||
.and_then(|s| if s.is_empty() { None } else { Some(s) });
|
.and_then(|s| if s.is_empty() { None } else { Some(s) });
|
||||||
}
|
}
|
||||||
|
|
||||||
action!(Authenticate);
|
actions!(client, [Authenticate]);
|
||||||
|
|
||||||
pub fn init(rpc: Arc<Client>, cx: &mut MutableAppContext) {
|
pub fn init(rpc: Arc<Client>, cx: &mut MutableAppContext) {
|
||||||
cx.add_global_action(move |_: &Authenticate, cx| {
|
cx.add_global_action(move |_: &Authenticate, cx| {
|
||||||
|
|
|
@ -6,7 +6,6 @@ use anyhow::{anyhow, Result};
|
||||||
use futures::{future::BoxFuture, stream::BoxStream, Future, StreamExt};
|
use futures::{future::BoxFuture, stream::BoxStream, Future, StreamExt};
|
||||||
use gpui::{executor, ModelHandle, TestAppContext};
|
use gpui::{executor, ModelHandle, TestAppContext};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use postage::barrier;
|
|
||||||
use rpc::{proto, ConnectionId, Peer, Receipt, TypedEnvelope};
|
use rpc::{proto, ConnectionId, Peer, Receipt, TypedEnvelope};
|
||||||
use std::{fmt, rc::Rc, sync::Arc};
|
use std::{fmt, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
|
@ -23,7 +22,6 @@ struct FakeServerState {
|
||||||
connection_id: Option<ConnectionId>,
|
connection_id: Option<ConnectionId>,
|
||||||
forbid_connections: bool,
|
forbid_connections: bool,
|
||||||
auth_count: usize,
|
auth_count: usize,
|
||||||
connection_killer: Option<barrier::Sender>,
|
|
||||||
access_token: usize,
|
access_token: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,15 +74,13 @@ impl FakeServer {
|
||||||
Err(EstablishConnectionError::Unauthorized)?
|
Err(EstablishConnectionError::Unauthorized)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let (client_conn, server_conn, kill) =
|
let (client_conn, server_conn, _) = Connection::in_memory(cx.background());
|
||||||
Connection::in_memory(cx.background());
|
|
||||||
let (connection_id, io, incoming) =
|
let (connection_id, io, incoming) =
|
||||||
peer.add_test_connection(server_conn, cx.background()).await;
|
peer.add_test_connection(server_conn, cx.background()).await;
|
||||||
cx.background().spawn(io).detach();
|
cx.background().spawn(io).detach();
|
||||||
let mut state = state.lock();
|
let mut state = state.lock();
|
||||||
state.connection_id = Some(connection_id);
|
state.connection_id = Some(connection_id);
|
||||||
state.incoming = Some(incoming);
|
state.incoming = Some(incoming);
|
||||||
state.connection_killer = Some(kill);
|
|
||||||
Ok(client_conn)
|
Ok(client_conn)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["Nathan Sobo <nathan@warp.dev>"]
|
authors = ["Nathan Sobo <nathan@warp.dev>"]
|
||||||
default-run = "zed-server"
|
default-run = "collab"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "zed-server"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "zed-server"
|
name = "collab"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "seed"
|
name = "seed"
|
||||||
|
@ -15,25 +15,27 @@ required-features = ["seed-support"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
|
util = { path = "../util" }
|
||||||
anyhow = "1.0.40"
|
anyhow = "1.0.40"
|
||||||
async-io = "1.3"
|
async-io = "1.3"
|
||||||
async-std = { version = "1.8.0", features = ["attributes"] }
|
async-std = { version = "1.8.0", features = ["attributes"] }
|
||||||
async-trait = "0.1.50"
|
async-trait = "0.1.50"
|
||||||
async-tungstenite = "0.16"
|
async-tungstenite = "0.16"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
clap = "=3.0.0-beta.2"
|
clap = "3.1"
|
||||||
comrak = "0.10"
|
comrak = "0.10"
|
||||||
either = "1.6"
|
either = "1.6"
|
||||||
envy = "0.4.2"
|
envy = "0.4.2"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
handlebars = "3.5"
|
handlebars = "3.5"
|
||||||
http-auth-basic = "0.1.3"
|
http-auth-basic = "0.1.3"
|
||||||
|
json_env_logger = "0.1"
|
||||||
jwt-simple = "0.10.0"
|
jwt-simple = "0.10.0"
|
||||||
lipsum = { version = "0.8", optional = true }
|
lipsum = { version = "0.8", optional = true }
|
||||||
|
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||||
oauth2 = { version = "4.0.0", default_features = false }
|
oauth2 = { version = "4.0.0", default_features = false }
|
||||||
oauth2-surf = "0.1.1"
|
oauth2-surf = "0.1.1"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rust-embed = { version = "6.3", features = ["include-exclude"] }
|
rust-embed = { version = "6.3", features = ["include-exclude"] }
|
||||||
scrypt = "0.7"
|
scrypt = "0.7"
|
||||||
|
@ -64,6 +66,8 @@ editor = { path = "../editor", features = ["test-support"] }
|
||||||
language = { path = "../language", features = ["test-support"] }
|
language = { path = "../language", features = ["test-support"] }
|
||||||
lsp = { path = "../lsp", features = ["test-support"] }
|
lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
project = { path = "../project", features = ["test-support"] }
|
project = { path = "../project", features = ["test-support"] }
|
||||||
|
settings = { path = "../settings", features = ["test-support"] }
|
||||||
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace", features = ["test-support"] }
|
workspace = { path = "../workspace", features = ["test-support"] }
|
||||||
ctor = "0.1"
|
ctor = "0.1"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
|
@ -1,2 +1,2 @@
|
||||||
web: ./target/release/zed-server
|
collab: ./target/release/collab
|
||||||
release: ./target/release/sqlx migrate run
|
release: ./target/release/sqlx migrate run
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
@ -8,14 +8,14 @@ kind: Service
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
namespace: ${ZED_KUBE_NAMESPACE}
|
namespace: ${ZED_KUBE_NAMESPACE}
|
||||||
name: zed
|
name: collab
|
||||||
annotations:
|
annotations:
|
||||||
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443"
|
service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443"
|
||||||
service.beta.kubernetes.io/do-loadbalancer-certificate-id: "2634d353-1ab4-437f-add2-4ffd8f315233"
|
service.beta.kubernetes.io/do-loadbalancer-certificate-id: "40879815-9a6b-4bbb-8207-8f2c7c0218f9"
|
||||||
spec:
|
spec:
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
selector:
|
selector:
|
||||||
app: zed
|
app: collab
|
||||||
ports:
|
ports:
|
||||||
- name: web
|
- name: web
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
@ -26,19 +26,19 @@ apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
namespace: ${ZED_KUBE_NAMESPACE}
|
namespace: ${ZED_KUBE_NAMESPACE}
|
||||||
name: zed
|
name: collab
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: zed
|
app: collab
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: zed
|
app: collab
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: zed
|
- name: collab
|
||||||
image: "${ZED_IMAGE_ID}"
|
image: "${ZED_IMAGE_ID}"
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
|
@ -81,6 +81,10 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: api
|
name: api
|
||||||
key: token
|
key: token
|
||||||
|
- name: LOG_JSON
|
||||||
|
value: "1"
|
||||||
|
- name: RUST_LOG
|
||||||
|
value: "trace"
|
||||||
securityContext:
|
securityContext:
|
||||||
capabilities:
|
capabilities:
|
||||||
# FIXME - Switch to the more restrictive `PERFMON` capability.
|
# FIXME - Switch to the more restrictive `PERFMON` capability.
|
|
@ -111,7 +111,6 @@ async fn create_access_token(request: Request) -> tide::Result {
|
||||||
.get_user_by_github_login(request.param("github_login")?)
|
.get_user_by_github_login(request.param("github_login")?)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| surf::Error::from_str(StatusCode::NotFound, "user not found"))?;
|
.ok_or_else(|| surf::Error::from_str(StatusCode::NotFound, "user not found"))?;
|
||||||
let access_token = auth::create_access_token(request.db().as_ref(), user.id).await?;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct QueryParams {
|
struct QueryParams {
|
||||||
|
@ -123,9 +122,6 @@ async fn create_access_token(request: Request) -> tide::Result {
|
||||||
surf::Error::from_str(StatusCode::UnprocessableEntity, "invalid query params")
|
surf::Error::from_str(StatusCode::UnprocessableEntity, "invalid query params")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let encrypted_access_token =
|
|
||||||
auth::encrypt_access_token(&access_token, query_params.public_key.clone())?;
|
|
||||||
|
|
||||||
let mut user_id = user.id;
|
let mut user_id = user.id;
|
||||||
if let Some(impersonate) = query_params.impersonate {
|
if let Some(impersonate) = query_params.impersonate {
|
||||||
if user.admin {
|
if user.admin {
|
||||||
|
@ -151,6 +147,10 @@ async fn create_access_token(request: Request) -> tide::Result {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let access_token = auth::create_access_token(request.db().as_ref(), user_id).await?;
|
||||||
|
let encrypted_access_token =
|
||||||
|
auth::encrypt_access_token(&access_token, query_params.public_key.clone())?;
|
||||||
|
|
||||||
Ok(tide::Response::builder(StatusCode::Ok)
|
Ok(tide::Response::builder(StatusCode::Ok)
|
||||||
.body(json!({"user_id": user_id, "encrypted_access_token": encrypted_access_token}))
|
.body(json!({"user_id": user_id, "encrypted_access_token": encrypted_access_token}))
|
||||||
.build())
|
.build())
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::{AppState, Request, RequestExt as _};
|
use crate::{AppState, Request, RequestExt as _};
|
||||||
use serde::Deserialize;
|
use log::as_serde;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tide::{http::mime, log, Server};
|
use tide::{http::mime, Server};
|
||||||
|
|
||||||
pub fn add_routes(app: &mut Server<Arc<AppState>>) {
|
pub fn add_routes(app: &mut Server<Arc<AppState>>) {
|
||||||
app.at("/").get(get_home);
|
app.at("/").get(get_home);
|
||||||
|
@ -18,7 +19,7 @@ async fn get_home(mut request: Request) -> tide::Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_signup(mut request: Request) -> tide::Result {
|
async fn post_signup(mut request: Request) -> tide::Result {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
struct Form {
|
struct Form {
|
||||||
github_login: String,
|
github_login: String,
|
||||||
email_address: String,
|
email_address: String,
|
||||||
|
@ -38,7 +39,7 @@ async fn post_signup(mut request: Request) -> tide::Result {
|
||||||
.map(str::to_string)
|
.map(str::to_string)
|
||||||
.unwrap_or(form.github_login);
|
.unwrap_or(form.github_login);
|
||||||
|
|
||||||
log::info!("Signup submitted: {:?}", form);
|
log::info!(form = as_serde!(form); "signup submitted");
|
||||||
|
|
||||||
// Save signup in the database
|
// Save signup in the database
|
||||||
request
|
request
|
|
@ -27,7 +27,7 @@ use rust_embed::RustEmbed;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use surf::http::cookies::SameSite;
|
use surf::http::cookies::SameSite;
|
||||||
use tide::{log, sessions::SessionMiddleware};
|
use tide::sessions::SessionMiddleware;
|
||||||
use tide_compress::CompressMiddleware;
|
use tide_compress::CompressMiddleware;
|
||||||
|
|
||||||
type Request = tide::Request<Arc<AppState>>;
|
type Request = tide::Request<Arc<AppState>>;
|
||||||
|
@ -138,7 +138,11 @@ struct LayoutData {
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() -> tide::Result<()> {
|
async fn main() -> tide::Result<()> {
|
||||||
log::start();
|
if std::env::var("LOG_JSON").is_ok() {
|
||||||
|
json_env_logger::init();
|
||||||
|
} else {
|
||||||
|
tide::log::start();
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(error) = env::load_dotenv() {
|
if let Err(error) = env::load_dotenv() {
|
||||||
log::error!(
|
log::error!(
|
|
@ -66,6 +66,10 @@ pub struct JoinedProject<'a> {
|
||||||
pub project: &'a Project,
|
pub project: &'a Project,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SharedProject {
|
||||||
|
pub authorized_user_ids: Vec<UserId>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct UnsharedProject {
|
pub struct UnsharedProject {
|
||||||
pub connection_ids: Vec<ConnectionId>,
|
pub connection_ids: Vec<ConnectionId>,
|
||||||
pub authorized_user_ids: Vec<UserId>,
|
pub authorized_user_ids: Vec<UserId>,
|
||||||
|
@ -130,9 +134,6 @@ impl Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
self.check_invariants();
|
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +245,9 @@ impl Store {
|
||||||
language_servers: Default::default(),
|
language_servers: Default::default(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if let Some(connection) = self.connections.get_mut(&host_connection_id) {
|
||||||
|
connection.projects.insert(project_id);
|
||||||
|
}
|
||||||
self.next_project_id += 1;
|
self.next_project_id += 1;
|
||||||
project_id
|
project_id
|
||||||
}
|
}
|
||||||
|
@ -266,16 +270,12 @@ impl Store {
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(project_id);
|
.insert(project_id);
|
||||||
}
|
}
|
||||||
if let Some(connection) = self.connections.get_mut(&project.host_connection_id) {
|
|
||||||
connection.projects.insert(project_id);
|
|
||||||
}
|
|
||||||
project.worktrees.insert(worktree_id, worktree);
|
project.worktrees.insert(worktree_id, worktree);
|
||||||
if let Ok(share) = project.share_mut() {
|
if let Ok(share) = project.share_mut() {
|
||||||
share.worktrees.insert(worktree_id, Default::default());
|
share.worktrees.insert(worktree_id, Default::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
self.check_invariants();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("no such project"))?
|
Err(anyhow!("no such project"))?
|
||||||
|
@ -312,8 +312,6 @@ impl Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
self.check_invariants();
|
|
||||||
Ok(project)
|
Ok(project)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("no such project"))?
|
Err(anyhow!("no such project"))?
|
||||||
|
@ -358,13 +356,14 @@ impl Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
self.check_invariants();
|
|
||||||
|
|
||||||
Ok((worktree, guest_connection_ids))
|
Ok((worktree, guest_connection_ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn share_project(&mut self, project_id: u64, connection_id: ConnectionId) -> bool {
|
pub fn share_project(
|
||||||
|
&mut self,
|
||||||
|
project_id: u64,
|
||||||
|
connection_id: ConnectionId,
|
||||||
|
) -> tide::Result<SharedProject> {
|
||||||
if let Some(project) = self.projects.get_mut(&project_id) {
|
if let Some(project) = self.projects.get_mut(&project_id) {
|
||||||
if project.host_connection_id == connection_id {
|
if project.host_connection_id == connection_id {
|
||||||
let mut share = ProjectShare::default();
|
let mut share = ProjectShare::default();
|
||||||
|
@ -372,10 +371,12 @@ impl Store {
|
||||||
share.worktrees.insert(*worktree_id, Default::default());
|
share.worktrees.insert(*worktree_id, Default::default());
|
||||||
}
|
}
|
||||||
project.share = Some(share);
|
project.share = Some(share);
|
||||||
return true;
|
return Ok(SharedProject {
|
||||||
|
authorized_user_ids: project.authorized_user_ids(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
Err(anyhow!("no such project"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unshare_project(
|
pub fn unshare_project(
|
||||||
|
@ -402,9 +403,6 @@ impl Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
self.check_invariants();
|
|
||||||
|
|
||||||
Ok(UnsharedProject {
|
Ok(UnsharedProject {
|
||||||
connection_ids,
|
connection_ids,
|
||||||
authorized_user_ids,
|
authorized_user_ids,
|
||||||
|
@ -490,9 +488,6 @@ impl Store {
|
||||||
share.active_replica_ids.insert(replica_id);
|
share.active_replica_ids.insert(replica_id);
|
||||||
share.guests.insert(connection_id, (replica_id, user_id));
|
share.guests.insert(connection_id, (replica_id, user_id));
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
self.check_invariants();
|
|
||||||
|
|
||||||
Ok(JoinedProject {
|
Ok(JoinedProject {
|
||||||
replica_id,
|
replica_id,
|
||||||
project: &self.projects[&project_id],
|
project: &self.projects[&project_id],
|
||||||
|
@ -525,9 +520,6 @@ impl Store {
|
||||||
let connection_ids = project.connection_ids();
|
let connection_ids = project.connection_ids();
|
||||||
let authorized_user_ids = project.authorized_user_ids();
|
let authorized_user_ids = project.authorized_user_ids();
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
self.check_invariants();
|
|
||||||
|
|
||||||
Ok(LeftProject {
|
Ok(LeftProject {
|
||||||
connection_ids,
|
connection_ids,
|
||||||
authorized_user_ids,
|
authorized_user_ids,
|
||||||
|
@ -555,10 +547,6 @@ impl Store {
|
||||||
worktree.entries.insert(entry.id, entry.clone());
|
worktree.entries.insert(entry.id, entry.clone());
|
||||||
}
|
}
|
||||||
let connection_ids = project.connection_ids();
|
let connection_ids = project.connection_ids();
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
self.check_invariants();
|
|
||||||
|
|
||||||
Ok(connection_ids)
|
Ok(connection_ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,7 +620,7 @@ impl Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn check_invariants(&self) {
|
pub fn check_invariants(&self) {
|
||||||
for (connection_id, connection) in &self.connections {
|
for (connection_id, connection) in &self.connections {
|
||||||
for project_id in &connection.projects {
|
for project_id in &connection.projects {
|
||||||
let project = &self.projects.get(&project_id).unwrap();
|
let project = &self.projects.get(&project_id).unwrap();
|