From cf75b090d3f0ff633977df7af5426eb1c66549af Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Mon, 29 May 2023 15:45:13 +0200 Subject: [PATCH 01/53] feat: organize themes by folders with LICENSE --- styles/src/themes/andromeda/LICENSE | 21 +++++++++++++++++++ .../src/themes/{ => andromeda}/andromeda.ts | 4 ++-- .../{ => atelier-cave}/atelier-cave-dark.ts | 6 +++--- .../{ => atelier-cave}/atelier-cave-light.ts | 6 +++--- .../{ => atelier-dune}/atelier-dune-dark.ts | 6 +++--- .../{ => atelier-dune}/atelier-dune-light.ts | 6 +++--- .../atelier-estuary-dark.ts | 6 +++--- .../atelier-estuary-light.ts | 6 +++--- .../atelier-forest-dark.ts | 6 +++--- .../atelier-forest-light.ts | 6 +++--- .../{ => atelier-heath}/atelier-heath-dark.ts | 6 +++--- .../atelier-heath-light.ts | 6 +++--- .../atelier-lakeside-dark.ts | 6 +++--- .../atelier-lakeside-light.ts | 6 +++--- .../atelier-plateau-dark.ts | 6 +++--- .../atelier-plateau-light.ts | 6 +++--- .../atelier-savanna-dark.ts | 6 +++--- .../atelier-savanna-light.ts | 6 +++--- .../atelier-seaside-dark.ts | 6 +++--- .../atelier-seaside-light.ts | 6 +++--- .../atelier-sulphurpool-dark.ts | 6 +++--- .../atelier-sulphurpool-light.ts | 6 +++--- styles/src/themes/{ => ayu}/ayu-dark.ts | 4 ++-- styles/src/themes/{ => ayu}/ayu-light.ts | 4 ++-- styles/src/themes/{ => ayu}/ayu-mirage.ts | 4 ++-- styles/src/themes/gruvbox/LICENSE | 21 +++++++++++++++++++ .../themes/{ => gruvbox}/gruvbox-common.ts | 4 ++-- .../themes/{ => gruvbox}/gruvbox-dark-hard.ts | 0 .../themes/{ => gruvbox}/gruvbox-dark-soft.ts | 0 .../src/themes/{ => gruvbox}/gruvbox-dark.ts | 0 .../{ => gruvbox}/gruvbox-light-hard.ts | 0 .../{ => gruvbox}/gruvbox-light-soft.ts | 0 .../src/themes/{ => gruvbox}/gruvbox-light.ts | 0 styles/src/themes/one/LICENSE | 21 +++++++++++++++++++ styles/src/themes/{ => one}/one-dark.ts | 6 +++--- styles/src/themes/{ => one}/one-light.ts | 6 +++--- styles/src/themes/rose-pine/LICENSE | 21 +++++++++++++++++++ .../themes/{ => rose-pine}/rose-pine-dawn.ts | 4 ++-- .../themes/{ => rose-pine}/rose-pine-moon.ts | 4 ++-- .../src/themes/{ => rose-pine}/rose-pine.ts | 4 ++-- styles/src/themes/sandcastle/LICENSE | 21 +++++++++++++++++++ .../src/themes/{ => sandcastle}/sandcastle.ts | 4 ++-- styles/src/themes/solarized/LICENSE | 21 +++++++++++++++++++ .../src/themes/{ => solarized}/solarized.ts | 4 ++-- styles/src/themes/summercamp/LICENSE | 21 +++++++++++++++++++ .../src/themes/{ => summercamp}/summercamp.ts | 4 ++-- 46 files changed, 235 insertions(+), 88 deletions(-) create mode 100644 styles/src/themes/andromeda/LICENSE rename styles/src/themes/{ => andromeda}/andromeda.ts (91%) rename styles/src/themes/{ => atelier-cave}/atelier-cave-dark.ts (89%) rename styles/src/themes/{ => atelier-cave}/atelier-cave-light.ts (90%) rename styles/src/themes/{ => atelier-dune}/atelier-dune-dark.ts (89%) rename styles/src/themes/{ => atelier-dune}/atelier-dune-light.ts (90%) rename styles/src/themes/{ => atelier-estuary}/atelier-estuary-dark.ts (89%) rename styles/src/themes/{ => atelier-estuary}/atelier-estuary-light.ts (90%) rename styles/src/themes/{ => atelier-forest}/atelier-forest-dark.ts (89%) rename styles/src/themes/{ => atelier-forest}/atelier-forest-light.ts (90%) rename styles/src/themes/{ => atelier-heath}/atelier-heath-dark.ts (89%) rename styles/src/themes/{ => atelier-heath}/atelier-heath-light.ts (90%) rename styles/src/themes/{ => atelier-lakeside}/atelier-lakeside-dark.ts (90%) rename styles/src/themes/{ => atelier-lakeside}/atelier-lakeside-light.ts (90%) rename styles/src/themes/{ => atelier-plateau}/atelier-plateau-dark.ts (89%) rename styles/src/themes/{ => atelier-plateau}/atelier-plateau-light.ts (90%) rename styles/src/themes/{ => atelier-savanna}/atelier-savanna-dark.ts (89%) rename styles/src/themes/{ => atelier-savanna}/atelier-savanna-light.ts (90%) rename styles/src/themes/{ => atelier-seaside}/atelier-seaside-dark.ts (89%) rename styles/src/themes/{ => atelier-seaside}/atelier-seaside-light.ts (90%) rename styles/src/themes/{ => atelier-sulphurpool}/atelier-sulphurpool-dark.ts (90%) rename styles/src/themes/{ => atelier-sulphurpool}/atelier-sulphurpool-light.ts (90%) rename styles/src/themes/{ => ayu}/ayu-dark.ts (66%) rename styles/src/themes/{ => ayu}/ayu-light.ts (66%) rename styles/src/themes/{ => ayu}/ayu-mirage.ts (66%) create mode 100644 styles/src/themes/gruvbox/LICENSE rename styles/src/themes/{ => gruvbox}/gruvbox-common.ts (98%) rename styles/src/themes/{ => gruvbox}/gruvbox-dark-hard.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-dark-soft.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-dark.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-light-hard.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-light-soft.ts (100%) rename styles/src/themes/{ => gruvbox}/gruvbox-light.ts (100%) create mode 100644 styles/src/themes/one/LICENSE rename styles/src/themes/{ => one}/one-dark.ts (93%) rename styles/src/themes/{ => one}/one-light.ts (93%) create mode 100644 styles/src/themes/rose-pine/LICENSE rename styles/src/themes/{ => rose-pine}/rose-pine-dawn.ts (91%) rename styles/src/themes/{ => rose-pine}/rose-pine-moon.ts (91%) rename styles/src/themes/{ => rose-pine}/rose-pine.ts (91%) create mode 100644 styles/src/themes/sandcastle/LICENSE rename styles/src/themes/{ => sandcastle}/sandcastle.ts (91%) create mode 100644 styles/src/themes/solarized/LICENSE rename styles/src/themes/{ => solarized}/solarized.ts (91%) create mode 100644 styles/src/themes/summercamp/LICENSE rename styles/src/themes/{ => summercamp}/summercamp.ts (91%) diff --git a/styles/src/themes/andromeda/LICENSE b/styles/src/themes/andromeda/LICENSE new file mode 100644 index 0000000000..bdd549491f --- /dev/null +++ b/styles/src/themes/andromeda/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/andromeda.ts b/styles/src/themes/andromeda/andromeda.ts similarity index 91% rename from styles/src/themes/andromeda.ts rename to styles/src/themes/andromeda/andromeda.ts index 7eba7b1481..adfea37d06 100644 --- a/styles/src/themes/andromeda.ts +++ b/styles/src/themes/andromeda/andromeda.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Andromeda" diff --git a/styles/src/themes/atelier-cave-dark.ts b/styles/src/themes/atelier-cave/atelier-cave-dark.ts similarity index 89% rename from styles/src/themes/atelier-cave-dark.ts rename to styles/src/themes/atelier-cave/atelier-cave-dark.ts index a56e22cd92..a61db0e590 100644 --- a/styles/src/themes/atelier-cave-dark.ts +++ b/styles/src/themes/atelier-cave/atelier-cave-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-cave-light.ts b/styles/src/themes/atelier-cave/atelier-cave-light.ts similarity index 90% rename from styles/src/themes/atelier-cave-light.ts rename to styles/src/themes/atelier-cave/atelier-cave-light.ts index 3b180752cf..739fe1320b 100644 --- a/styles/src/themes/atelier-cave-light.ts +++ b/styles/src/themes/atelier-cave/atelier-cave-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-dune-dark.ts b/styles/src/themes/atelier-dune/atelier-dune-dark.ts similarity index 89% rename from styles/src/themes/atelier-dune-dark.ts rename to styles/src/themes/atelier-dune/atelier-dune-dark.ts index 0ab402a99d..7a3c3fbd07 100644 --- a/styles/src/themes/atelier-dune-dark.ts +++ b/styles/src/themes/atelier-dune/atelier-dune-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-dune-light.ts b/styles/src/themes/atelier-dune/atelier-dune-light.ts similarity index 90% rename from styles/src/themes/atelier-dune-light.ts rename to styles/src/themes/atelier-dune/atelier-dune-light.ts index e6a09985d5..3ee60164b6 100644 --- a/styles/src/themes/atelier-dune-light.ts +++ b/styles/src/themes/atelier-dune/atelier-dune-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-estuary-dark.ts b/styles/src/themes/atelier-estuary/atelier-estuary-dark.ts similarity index 89% rename from styles/src/themes/atelier-estuary-dark.ts rename to styles/src/themes/atelier-estuary/atelier-estuary-dark.ts index c4ec50c1e0..5ec6df8d35 100644 --- a/styles/src/themes/atelier-estuary-dark.ts +++ b/styles/src/themes/atelier-estuary/atelier-estuary-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-estuary-light.ts b/styles/src/themes/atelier-estuary/atelier-estuary-light.ts similarity index 90% rename from styles/src/themes/atelier-estuary-light.ts rename to styles/src/themes/atelier-estuary/atelier-estuary-light.ts index 6fce0e4483..bc583d41ac 100644 --- a/styles/src/themes/atelier-estuary-light.ts +++ b/styles/src/themes/atelier-estuary/atelier-estuary-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-forest-dark.ts b/styles/src/themes/atelier-forest/atelier-forest-dark.ts similarity index 89% rename from styles/src/themes/atelier-forest-dark.ts rename to styles/src/themes/atelier-forest/atelier-forest-dark.ts index 7c47c55a83..72f958efdf 100644 --- a/styles/src/themes/atelier-forest-dark.ts +++ b/styles/src/themes/atelier-forest/atelier-forest-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-forest-light.ts b/styles/src/themes/atelier-forest/atelier-forest-light.ts similarity index 90% rename from styles/src/themes/atelier-forest-light.ts rename to styles/src/themes/atelier-forest/atelier-forest-light.ts index 8ce0616476..463350f822 100644 --- a/styles/src/themes/atelier-forest-light.ts +++ b/styles/src/themes/atelier-forest/atelier-forest-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-heath-dark.ts b/styles/src/themes/atelier-heath/atelier-heath-dark.ts similarity index 89% rename from styles/src/themes/atelier-heath-dark.ts rename to styles/src/themes/atelier-heath/atelier-heath-dark.ts index 87458ab8f5..010169b022 100644 --- a/styles/src/themes/atelier-heath-dark.ts +++ b/styles/src/themes/atelier-heath/atelier-heath-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-heath-light.ts b/styles/src/themes/atelier-heath/atelier-heath-light.ts similarity index 90% rename from styles/src/themes/atelier-heath-light.ts rename to styles/src/themes/atelier-heath/atelier-heath-light.ts index 3db3437041..09d39b6d66 100644 --- a/styles/src/themes/atelier-heath-light.ts +++ b/styles/src/themes/atelier-heath/atelier-heath-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-lakeside-dark.ts b/styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts similarity index 90% rename from styles/src/themes/atelier-lakeside-dark.ts rename to styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts index a8297ef9fd..59728f30f3 100644 --- a/styles/src/themes/atelier-lakeside-dark.ts +++ b/styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-lakeside-light.ts b/styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts similarity index 90% rename from styles/src/themes/atelier-lakeside-light.ts rename to styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts index 408fabcaf3..d26c4fe679 100644 --- a/styles/src/themes/atelier-lakeside-light.ts +++ b/styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-plateau-dark.ts b/styles/src/themes/atelier-plateau/atelier-plateau-dark.ts similarity index 89% rename from styles/src/themes/atelier-plateau-dark.ts rename to styles/src/themes/atelier-plateau/atelier-plateau-dark.ts index 731cb824e4..8299116c0e 100644 --- a/styles/src/themes/atelier-plateau-dark.ts +++ b/styles/src/themes/atelier-plateau/atelier-plateau-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-plateau-light.ts b/styles/src/themes/atelier-plateau/atelier-plateau-light.ts similarity index 90% rename from styles/src/themes/atelier-plateau-light.ts rename to styles/src/themes/atelier-plateau/atelier-plateau-light.ts index 96f295a695..7fcc4220ac 100644 --- a/styles/src/themes/atelier-plateau-light.ts +++ b/styles/src/themes/atelier-plateau/atelier-plateau-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-savanna-dark.ts b/styles/src/themes/atelier-savanna/atelier-savanna-dark.ts similarity index 89% rename from styles/src/themes/atelier-savanna-dark.ts rename to styles/src/themes/atelier-savanna/atelier-savanna-dark.ts index dfcb4f27cb..5b6f034563 100644 --- a/styles/src/themes/atelier-savanna-dark.ts +++ b/styles/src/themes/atelier-savanna/atelier-savanna-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-savanna-light.ts b/styles/src/themes/atelier-savanna/atelier-savanna-light.ts similarity index 90% rename from styles/src/themes/atelier-savanna-light.ts rename to styles/src/themes/atelier-savanna/atelier-savanna-light.ts index 4bc1389fc9..4989594876 100644 --- a/styles/src/themes/atelier-savanna-light.ts +++ b/styles/src/themes/atelier-savanna/atelier-savanna-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-seaside-dark.ts b/styles/src/themes/atelier-seaside/atelier-seaside-dark.ts similarity index 89% rename from styles/src/themes/atelier-seaside-dark.ts rename to styles/src/themes/atelier-seaside/atelier-seaside-dark.ts index 1326a27786..00631eff51 100644 --- a/styles/src/themes/atelier-seaside-dark.ts +++ b/styles/src/themes/atelier-seaside/atelier-seaside-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-seaside-light.ts b/styles/src/themes/atelier-seaside/atelier-seaside-light.ts similarity index 90% rename from styles/src/themes/atelier-seaside-light.ts rename to styles/src/themes/atelier-seaside/atelier-seaside-light.ts index 6f6823718a..af6af2f027 100644 --- a/styles/src/themes/atelier-seaside-light.ts +++ b/styles/src/themes/atelier-seaside/atelier-seaside-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-sulphurpool-dark.ts b/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts similarity index 90% rename from styles/src/themes/atelier-sulphurpool-dark.ts rename to styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts index dcfc0d932a..140d02c2e6 100644 --- a/styles/src/themes/atelier-sulphurpool-dark.ts +++ b/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-sulphurpool-light.ts b/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts similarity index 90% rename from styles/src/themes/atelier-sulphurpool-light.ts rename to styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts index b2b5f7c328..b6dba4c384 100644 --- a/styles/src/themes/atelier-sulphurpool-light.ts +++ b/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" const variant: Variant = { meta: { diff --git a/styles/src/themes/ayu-dark.ts b/styles/src/themes/ayu/ayu-dark.ts similarity index 66% rename from styles/src/themes/ayu-dark.ts rename to styles/src/themes/ayu/ayu-dark.ts index c7e86994fe..eb837231fe 100644 --- a/styles/src/themes/ayu-dark.ts +++ b/styles/src/themes/ayu/ayu-dark.ts @@ -1,5 +1,5 @@ -import { createColorScheme } from "./common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "./common/ayu-common" +import { createColorScheme } from "../common/ramps" +import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/ayu-light.ts b/styles/src/themes/ayu/ayu-light.ts similarity index 66% rename from styles/src/themes/ayu-light.ts rename to styles/src/themes/ayu/ayu-light.ts index 9acabf6a39..9c29449db5 100644 --- a/styles/src/themes/ayu-light.ts +++ b/styles/src/themes/ayu/ayu-light.ts @@ -1,5 +1,5 @@ -import { createColorScheme } from "./common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "./common/ayu-common" +import { createColorScheme } from "../common/ramps" +import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/ayu-mirage.ts b/styles/src/themes/ayu/ayu-mirage.ts similarity index 66% rename from styles/src/themes/ayu-mirage.ts rename to styles/src/themes/ayu/ayu-mirage.ts index 2a01512673..52bf549392 100644 --- a/styles/src/themes/ayu-mirage.ts +++ b/styles/src/themes/ayu/ayu-mirage.ts @@ -1,5 +1,5 @@ -import { createColorScheme } from "./common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "./common/ayu-common" +import { createColorScheme } from "../common/ramps" +import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/gruvbox/LICENSE b/styles/src/themes/gruvbox/LICENSE new file mode 100644 index 0000000000..2a92306143 --- /dev/null +++ b/styles/src/themes/gruvbox/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/gruvbox-common.ts b/styles/src/themes/gruvbox/gruvbox-common.ts similarity index 98% rename from styles/src/themes/gruvbox-common.ts rename to styles/src/themes/gruvbox/gruvbox-common.ts index c42362c11c..5958fe62ac 100644 --- a/styles/src/themes/gruvbox-common.ts +++ b/styles/src/themes/gruvbox/gruvbox-common.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta, ThemeSyntax } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta, ThemeSyntax } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Gruvbox" diff --git a/styles/src/themes/gruvbox-dark-hard.ts b/styles/src/themes/gruvbox/gruvbox-dark-hard.ts similarity index 100% rename from styles/src/themes/gruvbox-dark-hard.ts rename to styles/src/themes/gruvbox/gruvbox-dark-hard.ts diff --git a/styles/src/themes/gruvbox-dark-soft.ts b/styles/src/themes/gruvbox/gruvbox-dark-soft.ts similarity index 100% rename from styles/src/themes/gruvbox-dark-soft.ts rename to styles/src/themes/gruvbox/gruvbox-dark-soft.ts diff --git a/styles/src/themes/gruvbox-dark.ts b/styles/src/themes/gruvbox/gruvbox-dark.ts similarity index 100% rename from styles/src/themes/gruvbox-dark.ts rename to styles/src/themes/gruvbox/gruvbox-dark.ts diff --git a/styles/src/themes/gruvbox-light-hard.ts b/styles/src/themes/gruvbox/gruvbox-light-hard.ts similarity index 100% rename from styles/src/themes/gruvbox-light-hard.ts rename to styles/src/themes/gruvbox/gruvbox-light-hard.ts diff --git a/styles/src/themes/gruvbox-light-soft.ts b/styles/src/themes/gruvbox/gruvbox-light-soft.ts similarity index 100% rename from styles/src/themes/gruvbox-light-soft.ts rename to styles/src/themes/gruvbox/gruvbox-light-soft.ts diff --git a/styles/src/themes/gruvbox-light.ts b/styles/src/themes/gruvbox/gruvbox-light.ts similarity index 100% rename from styles/src/themes/gruvbox-light.ts rename to styles/src/themes/gruvbox/gruvbox-light.ts diff --git a/styles/src/themes/one/LICENSE b/styles/src/themes/one/LICENSE new file mode 100644 index 0000000000..dc07dc10ad --- /dev/null +++ b/styles/src/themes/one/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/one-dark.ts b/styles/src/themes/one/one-dark.ts similarity index 93% rename from styles/src/themes/one-dark.ts rename to styles/src/themes/one/one-dark.ts index 85417a0e68..71062940a5 100644 --- a/styles/src/themes/one-dark.ts +++ b/styles/src/themes/one/one-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { fontWeights } from "../common" -import { Meta, ThemeSyntax } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { fontWeights } from "../../common" +import { Meta, ThemeSyntax } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "One Dark" diff --git a/styles/src/themes/one-light.ts b/styles/src/themes/one/one-light.ts similarity index 93% rename from styles/src/themes/one-light.ts rename to styles/src/themes/one/one-light.ts index 7bf21aee17..8ae42e8b1d 100644 --- a/styles/src/themes/one-light.ts +++ b/styles/src/themes/one/one-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" -import { fontWeights } from "../common" -import { Meta, ThemeSyntax } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { fontWeights } from "../../common" +import { Meta, ThemeSyntax } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "One Light" diff --git a/styles/src/themes/rose-pine/LICENSE b/styles/src/themes/rose-pine/LICENSE new file mode 100644 index 0000000000..dfd60136f9 --- /dev/null +++ b/styles/src/themes/rose-pine/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Emilia Dunfelt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/rose-pine-dawn.ts b/styles/src/themes/rose-pine/rose-pine-dawn.ts similarity index 91% rename from styles/src/themes/rose-pine-dawn.ts rename to styles/src/themes/rose-pine/rose-pine-dawn.ts index 427b05f72b..48da855304 100644 --- a/styles/src/themes/rose-pine-dawn.ts +++ b/styles/src/themes/rose-pine/rose-pine-dawn.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Rosé Pine Dawn" diff --git a/styles/src/themes/rose-pine-moon.ts b/styles/src/themes/rose-pine/rose-pine-moon.ts similarity index 91% rename from styles/src/themes/rose-pine-moon.ts rename to styles/src/themes/rose-pine/rose-pine-moon.ts index be2f5a8daf..2e13ca4b70 100644 --- a/styles/src/themes/rose-pine-moon.ts +++ b/styles/src/themes/rose-pine/rose-pine-moon.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Rosé Pine Moon" diff --git a/styles/src/themes/rose-pine.ts b/styles/src/themes/rose-pine/rose-pine.ts similarity index 91% rename from styles/src/themes/rose-pine.ts rename to styles/src/themes/rose-pine/rose-pine.ts index 944550f125..33d7d533e6 100644 --- a/styles/src/themes/rose-pine.ts +++ b/styles/src/themes/rose-pine/rose-pine.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Rosé Pine" diff --git a/styles/src/themes/sandcastle/LICENSE b/styles/src/themes/sandcastle/LICENSE new file mode 100644 index 0000000000..c66a06c51b --- /dev/null +++ b/styles/src/themes/sandcastle/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 George Essig + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/sandcastle.ts b/styles/src/themes/sandcastle/sandcastle.ts similarity index 91% rename from styles/src/themes/sandcastle.ts rename to styles/src/themes/sandcastle/sandcastle.ts index 483f01b27a..0b26521256 100644 --- a/styles/src/themes/sandcastle.ts +++ b/styles/src/themes/sandcastle/sandcastle.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Sandcastle" diff --git a/styles/src/themes/solarized/LICENSE b/styles/src/themes/solarized/LICENSE new file mode 100644 index 0000000000..221eee6f15 --- /dev/null +++ b/styles/src/themes/solarized/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011 Ethan Schoonover + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/solarized.ts b/styles/src/themes/solarized/solarized.ts similarity index 91% rename from styles/src/themes/solarized.ts rename to styles/src/themes/solarized/solarized.ts index 1210c43806..13e3e34460 100644 --- a/styles/src/themes/solarized.ts +++ b/styles/src/themes/solarized/solarized.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta as Metadata } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta as Metadata } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Solarized" diff --git a/styles/src/themes/summercamp/LICENSE b/styles/src/themes/summercamp/LICENSE new file mode 100644 index 0000000000..d7525414ad --- /dev/null +++ b/styles/src/themes/summercamp/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Zoe FiriH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/summercamp.ts b/styles/src/themes/summercamp/summercamp.ts similarity index 91% rename from styles/src/themes/summercamp.ts rename to styles/src/themes/summercamp/summercamp.ts index 7df125e866..41e101af02 100644 --- a/styles/src/themes/summercamp.ts +++ b/styles/src/themes/summercamp/summercamp.ts @@ -1,6 +1,6 @@ import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" +import { Meta } from "../common/colorScheme" +import { colorRamp, createColorScheme } from "../common/ramps" const name = "Summercamp" From 89446c7fd41f3733b657e4f0a57438d323770331 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 29 May 2023 13:48:27 -0700 Subject: [PATCH 02/53] Start work on respecting project-specific settings --- crates/copilot/src/copilot.rs | 5 + crates/copilot_button/src/copilot_button.rs | 2 +- crates/editor/src/display_map.rs | 2 +- crates/editor/src/items.rs | 4 + crates/editor/src/multi_buffer.rs | 20 ++- crates/language/src/buffer.rs | 17 ++- crates/language/src/language_settings.rs | 13 +- crates/project/src/lsp_command.rs | 3 +- crates/project/src/project.rs | 130 +++++++++++++++----- crates/project/src/project_tests.rs | 56 +++++++++ crates/project/src/worktree.rs | 45 +++++-- crates/settings/src/settings_file.rs | 13 +- crates/settings/src/settings_store.rs | 91 +++++++++----- crates/terminal_view/src/terminal_view.rs | 5 +- crates/util/src/paths.rs | 1 + crates/zed/src/languages/yaml.rs | 2 +- 16 files changed, 326 insertions(+), 83 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index de9104a684..88b0aebd17 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -787,6 +787,7 @@ impl Copilot { let position = position.to_point_utf16(buffer); let settings = language_settings( buffer.language_at(position).map(|l| l.name()).as_deref(), + buffer.file().map(|f| f.as_ref()), cx, ); let tab_size = settings.tab_size; @@ -1175,6 +1176,10 @@ mod tests { fn to_proto(&self) -> rpc::proto::File { unimplemented!() } + + fn worktree_id(&self) -> usize { + 0 + } } impl language::LocalFile for File { diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 17d27ca41f..686b3d2dfb 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -198,7 +198,7 @@ impl CopilotButton { if let Some(language) = self.language.clone() { let fs = fs.clone(); let language_enabled = - language_settings::language_settings(Some(language.as_ref()), cx) + language_settings::language_settings(Some(language.as_ref()), None, cx) .show_copilot_suggestions; menu_options.push(ContextMenuItem::handler( format!( diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 366e47ddc6..f8d6f0bc40 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -277,7 +277,7 @@ impl DisplayMap { .as_singleton() .and_then(|buffer| buffer.read(cx).language()) .map(|language| language.name()); - language_settings(language_name.as_deref(), cx).tab_size + language_settings(language_name.as_deref(), None, cx).tab_size } #[cfg(test)] diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 40e7c89cb2..8da746075e 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1231,6 +1231,10 @@ mod tests { unimplemented!() } + fn worktree_id(&self) -> usize { + 0 + } + fn is_deleted(&self) -> bool { unimplemented!() } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 4650dff38f..597feb832b 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1377,8 +1377,14 @@ impl MultiBuffer { point: T, cx: &'a AppContext, ) -> &'a LanguageSettings { - let language = self.language_at(point, cx); - language_settings(language.map(|l| l.name()).as_deref(), cx) + let mut language = None; + let mut file = None; + if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) { + let buffer = buffer.read(cx); + language = buffer.language_at(offset).map(|l| l.name()); + file = buffer.file().map(|f| f.as_ref()); + } + language_settings(language.as_deref(), file, cx) } pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle)) { @@ -2785,9 +2791,13 @@ impl MultiBufferSnapshot { point: T, cx: &'a AppContext, ) -> &'a LanguageSettings { - self.point_to_buffer_offset(point) - .map(|(buffer, offset)| buffer.settings_at(offset, cx)) - .unwrap_or_else(|| language_settings(None, cx)) + let mut language = None; + let mut file = None; + if let Some((buffer, offset)) = self.point_to_buffer_offset(point) { + language = buffer.language_at(offset).map(|l| l.name()); + file = buffer.file().map(|f| f.as_ref()); + } + language_settings(language.as_deref(), file, cx) } pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 93b50cf597..ddec085e2a 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -216,6 +216,11 @@ pub trait File: Send + Sync { /// of its worktree, then this method will return the name of the worktree itself. fn file_name<'a>(&'a self, cx: &'a AppContext) -> &'a OsStr; + /// Returns the id of the worktree to which this file belongs. + /// + /// This is needed for looking up project-specific settings. + fn worktree_id(&self) -> usize; + fn is_deleted(&self) -> bool; fn as_any(&self) -> &dyn Any; @@ -1803,7 +1808,11 @@ impl BufferSnapshot { pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { let language_name = self.language_at(position).map(|language| language.name()); - let settings = language_settings(language_name.as_deref(), cx); + let settings = language_settings( + language_name.as_deref(), + self.file().map(|f| f.as_ref()), + cx, + ); if settings.hard_tabs { IndentSize::tab() } else { @@ -2128,7 +2137,11 @@ impl BufferSnapshot { cx: &'a AppContext, ) -> &'a LanguageSettings { let language = self.language_at(position); - language_settings(language.map(|l| l.name()).as_deref(), cx) + language_settings( + language.map(|l| l.name()).as_deref(), + self.file.as_ref().map(AsRef::as_ref), + cx, + ) } pub fn language_scope_at(&self, position: D) -> Option { diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index c98297c036..70aaca03c4 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -1,3 +1,4 @@ +use crate::File; use anyhow::Result; use collections::HashMap; use globset::GlobMatcher; @@ -13,8 +14,16 @@ pub fn init(cx: &mut AppContext) { settings::register::(cx); } -pub fn language_settings<'a>(language: Option<&str>, cx: &'a AppContext) -> &'a LanguageSettings { - settings::get::(cx).language(language) +pub fn language_settings<'a>( + language: Option<&str>, + file: Option<&dyn File>, + cx: &'a AppContext, +) -> &'a LanguageSettings { + settings::get_local::( + file.map(|f| (f.worktree_id(), f.path().as_ref())), + cx, + ) + .language(language) } pub fn all_language_settings<'a>(cx: &'a AppContext) -> &'a AllLanguageSettings { diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 5ee6443896..0a8b5f8a2e 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1717,7 +1717,8 @@ impl LspCommand for OnTypeFormatting { let tab_size = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), cx).tab_size + let file = buffer.file().map(|f| f.as_ref()); + language_settings(language_name.as_deref(), file, cx).tab_size }); Ok(Self { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 92210a75a8..c76587ff56 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -71,7 +71,10 @@ use std::{ time::{Duration, Instant, SystemTime}, }; use terminals::Terminals; -use util::{debug_panic, defer, merge_json_value_into, post_inc, ResultExt, TryFutureExt as _}; +use util::{ + debug_panic, defer, merge_json_value_into, paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, + ResultExt, TryFutureExt as _, +}; pub use fs::*; pub use worktree::*; @@ -697,12 +700,7 @@ impl Project { .language(Some(&language.name())) .enable_language_server { - let worktree = file.worktree.read(cx); - language_servers_to_start.push(( - worktree.id(), - worktree.as_local().unwrap().abs_path().clone(), - language.clone(), - )); + language_servers_to_start.push((file.worktree.clone(), language.clone())); } } } @@ -732,8 +730,9 @@ impl Project { } // Start all the newly-enabled language servers. - for (worktree_id, worktree_path, language) in language_servers_to_start { - self.start_language_servers(worktree_id, worktree_path, language, cx); + for (worktree, language) in language_servers_to_start { + let worktree_path = worktree.read(cx).abs_path(); + self.start_language_servers(&worktree, worktree_path, language, cx); } if !self.copilot_enabled && Copilot::global(cx).is_some() { @@ -2320,25 +2319,34 @@ impl Project { }); if let Some(file) = File::from_dyn(buffer.read(cx).file()) { - if let Some(worktree) = file.worktree.read(cx).as_local() { - let worktree_id = worktree.id(); - let worktree_abs_path = worktree.abs_path().clone(); - self.start_language_servers(worktree_id, worktree_abs_path, new_language, cx); + let worktree = file.worktree.clone(); + if let Some(tree) = worktree.read(cx).as_local() { + self.start_language_servers(&worktree, tree.abs_path().clone(), new_language, cx); } } } fn start_language_servers( &mut self, - worktree_id: WorktreeId, + worktree: &ModelHandle, worktree_path: Arc, language: Arc, cx: &mut ModelContext, ) { - if !language_settings(Some(&language.name()), cx).enable_language_server { + if !language_settings( + Some(&language.name()), + worktree + .update(cx, |tree, cx| tree.root_file(cx)) + .as_ref() + .map(|f| f as _), + cx, + ) + .enable_language_server + { return; } + let worktree_id = worktree.read(cx).id(); for adapter in language.lsp_adapters() { let key = (worktree_id, adapter.name.clone()); if self.language_server_ids.contains_key(&key) { @@ -2747,23 +2755,22 @@ impl Project { buffers: impl IntoIterator>, cx: &mut ModelContext, ) -> Option<()> { - let language_server_lookup_info: HashSet<(WorktreeId, Arc, Arc)> = buffers + let language_server_lookup_info: HashSet<(ModelHandle, Arc)> = buffers .into_iter() .filter_map(|buffer| { let buffer = buffer.read(cx); let file = File::from_dyn(buffer.file())?; - let worktree = file.worktree.read(cx).as_local()?; let full_path = file.full_path(cx); let language = self .languages .language_for_file(&full_path, Some(buffer.as_rope())) .now_or_never()? .ok()?; - Some((worktree.id(), worktree.abs_path().clone(), language)) + Some((file.worktree.clone(), language)) }) .collect(); - for (worktree_id, worktree_abs_path, language) in language_server_lookup_info { - self.restart_language_servers(worktree_id, worktree_abs_path, language, cx); + for (worktree, language) in language_server_lookup_info { + self.restart_language_servers(worktree, language, cx); } None @@ -2772,11 +2779,13 @@ impl Project { // TODO This will break in the case where the adapter's root paths and worktrees are not equal fn restart_language_servers( &mut self, - worktree_id: WorktreeId, - fallback_path: Arc, + worktree: ModelHandle, language: Arc, cx: &mut ModelContext, ) { + let worktree_id = worktree.read(cx).id(); + let fallback_path = worktree.read(cx).abs_path(); + let mut stops = Vec::new(); for adapter in language.lsp_adapters() { stops.push(self.stop_language_server(worktree_id, adapter.name.clone(), cx)); @@ -2806,7 +2815,7 @@ impl Project { .map(|path_buf| Arc::from(path_buf.as_path())) .unwrap_or(fallback_path); - this.start_language_servers(worktree_id, root_path, language.clone(), cx); + this.start_language_servers(&worktree, root_path, language.clone(), cx); // Lookup new server ids and set them for each of the orphaned worktrees for adapter in language.lsp_adapters() { @@ -3430,7 +3439,12 @@ impl Project { for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers { let settings = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), cx).clone() + language_settings( + language_name.as_deref(), + buffer.file().map(|f| f.as_ref()), + cx, + ) + .clone() }); let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save; @@ -4439,11 +4453,15 @@ impl Project { push_to_history: bool, cx: &mut ModelContext, ) -> Task>> { - let tab_size = buffer.read_with(cx, |buffer, cx| { - let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), cx).tab_size + let (position, tab_size) = buffer.read_with(cx, |buffer, cx| { + let position = position.to_point_utf16(buffer); + let language_name = buffer.language_at(position).map(|l| l.name()); + let file = buffer.file().map(|f| f.as_ref()); + ( + position, + language_settings(language_name.as_deref(), file, cx).tab_size, + ) }); - let position = position.to_point_utf16(buffer.read(cx)); self.request_lsp( buffer.clone(), OnTypeFormatting { @@ -4849,6 +4867,7 @@ impl Project { worktree::Event::UpdatedEntries(changes) => { this.update_local_worktree_buffers(&worktree, changes, cx); this.update_local_worktree_language_servers(&worktree, changes, cx); + this.update_local_worktree_settings(&worktree, changes, cx); } worktree::Event::UpdatedGitRepositories(updated_repos) => { this.update_local_worktree_buffers_git_repos(worktree, updated_repos, cx) @@ -5155,6 +5174,61 @@ impl Project { .detach(); } + pub fn update_local_worktree_settings( + &mut self, + worktree: &ModelHandle, + changes: &UpdatedEntriesSet, + cx: &mut ModelContext, + ) { + let worktree_id = worktree.id(); + let worktree = worktree.read(cx).as_local().unwrap(); + + let mut settings_contents = Vec::new(); + for (path, _, change) in changes.iter() { + if path.ends_with(&*LOCAL_SETTINGS_RELATIVE_PATH) { + let settings_dir = Arc::from( + path.ancestors() + .nth(LOCAL_SETTINGS_RELATIVE_PATH.components().count()) + .unwrap(), + ); + let fs = self.fs.clone(); + let removed = *change == PathChange::Removed; + let abs_path = worktree.absolutize(path); + settings_contents.push(async move { + anyhow::Ok(( + settings_dir, + (!removed).then_some(fs.load(&abs_path).await?), + )) + }); + } + } + + if settings_contents.is_empty() { + return; + } + + cx.spawn_weak(move |_, mut cx| async move { + let settings_contents = futures::future::join_all(settings_contents).await; + cx.update(|cx| { + cx.update_global::(|store, cx| { + for entry in settings_contents { + if let Some((directory, file_content)) = entry.log_err() { + store + .set_local_settings( + worktree_id, + directory, + file_content.as_ref().map(String::as_str), + cx, + ) + .log_err(); + } + } + }); + }); + }) + .detach(); + } + pub fn set_active_path(&mut self, entry: Option, cx: &mut ModelContext) { let new_active_entry = entry.and_then(|project_path| { let worktree = self.worktree_for_id(project_path.worktree_id, cx)?; diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 34b63fd5bd..577b22d730 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -63,6 +63,62 @@ async fn test_symlinks(cx: &mut gpui::TestAppContext) { }); } +#[gpui::test] +async fn test_managing_project_specific_settings( + deterministic: Arc, + cx: &mut gpui::TestAppContext, +) { + init_test(cx); + + let fs = FakeFs::new(cx.background()); + fs.insert_tree( + "/the-root", + json!({ + ".zed": { + "settings.json": r#"{ "tab_size": 8 }"# + }, + "a": { + "a.rs": "fn a() {\n A\n}" + }, + "b": { + ".zed": { + "settings.json": r#"{ "tab_size": 2 }"# + }, + "b.rs": "fn b() {\n B\n}" + } + }), + ) + .await; + + let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; + let worktree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap()); + + deterministic.run_until_parked(); + cx.read(|cx| { + let tree = worktree.read(cx); + + let settings_a = language_settings( + None, + Some(&File::for_entry( + tree.entry_for_path("a/a.rs").unwrap().clone(), + worktree.clone(), + )), + cx, + ); + let settings_b = language_settings( + None, + Some(&File::for_entry( + tree.entry_for_path("b/b.rs").unwrap().clone(), + worktree.clone(), + )), + cx, + ); + + assert_eq!(settings_a.tab_size.get(), 8); + assert_eq!(settings_b.tab_size.get(), 2); + }); +} + #[gpui::test] async fn test_managing_language_servers( deterministic: Arc, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index dc3c172775..7432eb8d40 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -677,6 +677,18 @@ impl Worktree { Worktree::Remote(worktree) => worktree.abs_path.clone(), } } + + pub fn root_file(&self, cx: &mut ModelContext) -> Option { + let entry = self.entry_for_path("")?; + Some(File { + worktree: cx.handle(), + path: entry.path.clone(), + mtime: entry.mtime, + entry_id: entry.id, + is_local: self.is_local(), + is_deleted: false, + }) + } } impl LocalWorktree { @@ -684,14 +696,6 @@ impl LocalWorktree { path.starts_with(&self.abs_path) } - fn absolutize(&self, path: &Path) -> PathBuf { - if path.file_name().is_some() { - self.abs_path.join(path) - } else { - self.abs_path.to_path_buf() - } - } - pub(crate) fn load_buffer( &mut self, id: u64, @@ -1544,6 +1548,14 @@ impl Snapshot { &self.abs_path } + pub fn absolutize(&self, path: &Path) -> PathBuf { + if path.file_name().is_some() { + self.abs_path.join(path) + } else { + self.abs_path.to_path_buf() + } + } + pub fn contains_entry(&self, entry_id: ProjectEntryId) -> bool { self.entries_by_id.get(&entry_id, &()).is_some() } @@ -2383,6 +2395,10 @@ impl language::File for File { .unwrap_or_else(|| OsStr::new(&self.worktree.read(cx).root_name)) } + fn worktree_id(&self) -> usize { + self.worktree.id() + } + fn is_deleted(&self) -> bool { self.is_deleted } @@ -2447,6 +2463,17 @@ impl language::LocalFile for File { } impl File { + pub fn for_entry(entry: Entry, worktree: ModelHandle) -> Self { + Self { + worktree, + path: entry.path.clone(), + mtime: entry.mtime, + entry_id: entry.id, + is_local: true, + is_deleted: false, + } + } + pub fn from_proto( proto: rpc::proto::File, worktree: ModelHandle, @@ -2507,7 +2534,7 @@ pub enum EntryKind { File(CharBag), } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PathChange { /// A filesystem entry was was created. Added, diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index cca2909da2..4c98dca51a 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -4,7 +4,14 @@ use assets::Assets; use fs::Fs; use futures::{channel::mpsc, StreamExt}; use gpui::{executor::Background, AppContext, AssetSource}; -use std::{borrow::Cow, io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration}; +use std::{ + borrow::Cow, + io::ErrorKind, + path::{Path, PathBuf}, + str, + sync::Arc, + time::Duration, +}; use util::{paths, ResultExt}; pub fn register(cx: &mut AppContext) { @@ -17,6 +24,10 @@ pub fn get<'a, T: Setting>(cx: &'a AppContext) -> &'a T { cx.global::().get(None) } +pub fn get_local<'a, T: Setting>(location: Option<(usize, &Path)>, cx: &'a AppContext) -> &'a T { + cx.global::().get(location) +} + pub fn default_settings() -> Cow<'static, str> { match Assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap() { Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 71b3cc635f..2560d0b752 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -89,14 +89,14 @@ pub struct SettingsStore { setting_values: HashMap>, default_deserialized_settings: Option, user_deserialized_settings: Option, - local_deserialized_settings: BTreeMap, serde_json::Value>, + local_deserialized_settings: BTreeMap<(usize, Arc), serde_json::Value>, tab_size_callback: Option<(TypeId, Box Option>)>, } #[derive(Debug)] struct SettingValue { global_value: Option, - local_values: Vec<(Arc, T)>, + local_values: Vec<(usize, Arc, T)>, } trait AnySettingValue { @@ -109,9 +109,9 @@ trait AnySettingValue { custom: &[DeserializedSetting], cx: &AppContext, ) -> Result>; - fn value_for_path(&self, path: Option<&Path>) -> &dyn Any; + fn value_for_path(&self, path: Option<(usize, &Path)>) -> &dyn Any; fn set_global_value(&mut self, value: Box); - fn set_local_value(&mut self, path: Arc, value: Box); + fn set_local_value(&mut self, root_id: usize, path: Arc, value: Box); fn json_schema( &self, generator: &mut SchemaGenerator, @@ -165,7 +165,7 @@ impl SettingsStore { /// /// Panics if the given setting type has not been registered, or if there is no /// value for this setting. - pub fn get(&self, path: Option<&Path>) -> &T { + pub fn get(&self, path: Option<(usize, &Path)>) -> &T { self.setting_values .get(&TypeId::of::()) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) @@ -343,17 +343,19 @@ impl SettingsStore { /// Add or remove a set of local settings via a JSON string. pub fn set_local_settings( &mut self, + root_id: usize, path: Arc, settings_content: Option<&str>, cx: &AppContext, ) -> Result<()> { if let Some(content) = settings_content { self.local_deserialized_settings - .insert(path.clone(), parse_json_with_comments(content)?); + .insert((root_id, path.clone()), parse_json_with_comments(content)?); } else { - self.local_deserialized_settings.remove(&path); + self.local_deserialized_settings + .remove(&(root_id, path.clone())); } - self.recompute_values(Some(&path), cx)?; + self.recompute_values(Some((root_id, &path)), cx)?; Ok(()) } @@ -436,12 +438,12 @@ impl SettingsStore { fn recompute_values( &mut self, - changed_local_path: Option<&Path>, + changed_local_path: Option<(usize, &Path)>, cx: &AppContext, ) -> Result<()> { // Reload the global and local values for every setting. let mut user_settings_stack = Vec::::new(); - let mut paths_stack = Vec::>::new(); + let mut paths_stack = Vec::>::new(); for setting_value in self.setting_values.values_mut() { if let Some(default_settings) = &self.default_deserialized_settings { let default_settings = setting_value.deserialize_setting(default_settings)?; @@ -469,11 +471,11 @@ impl SettingsStore { } // Reload the local values for the setting. - for (path, local_settings) in &self.local_deserialized_settings { + for ((root_id, path), local_settings) in &self.local_deserialized_settings { // Build a stack of all of the local values for that setting. - while let Some(prev_path) = paths_stack.last() { - if let Some(prev_path) = prev_path { - if !path.starts_with(prev_path) { + while let Some(prev_entry) = paths_stack.last() { + if let Some((prev_root_id, prev_path)) = prev_entry { + if root_id != prev_root_id || !path.starts_with(prev_path) { paths_stack.pop(); user_settings_stack.pop(); continue; @@ -485,14 +487,17 @@ impl SettingsStore { if let Some(local_settings) = setting_value.deserialize_setting(&local_settings).log_err() { - paths_stack.push(Some(path.as_ref())); + paths_stack.push(Some((*root_id, path.as_ref()))); user_settings_stack.push(local_settings); // If a local settings file changed, then avoid recomputing local // settings for any path outside of that directory. - if changed_local_path.map_or(false, |changed_local_path| { - !path.starts_with(changed_local_path) - }) { + if changed_local_path.map_or( + false, + |(changed_root_id, changed_local_path)| { + *root_id != changed_root_id || !path.starts_with(changed_local_path) + }, + ) { continue; } @@ -500,7 +505,7 @@ impl SettingsStore { .load_setting(&default_settings, &user_settings_stack, cx) .log_err() { - setting_value.set_local_value(path.clone(), value); + setting_value.set_local_value(*root_id, path.clone(), value); } } } @@ -510,6 +515,24 @@ impl SettingsStore { } } +impl Debug for SettingsStore { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SettingsStore") + .field( + "types", + &self + .setting_values + .values() + .map(|value| value.setting_type_name()) + .collect::>(), + ) + .field("default_settings", &self.default_deserialized_settings) + .field("user_settings", &self.user_deserialized_settings) + .field("local_settings", &self.local_deserialized_settings) + .finish_non_exhaustive() + } +} + impl AnySettingValue for SettingValue { fn key(&self) -> Option<&'static str> { T::KEY @@ -546,10 +569,10 @@ impl AnySettingValue for SettingValue { Ok(DeserializedSetting(Box::new(value))) } - fn value_for_path(&self, path: Option<&Path>) -> &dyn Any { - if let Some(path) = path { - for (settings_path, value) in self.local_values.iter().rev() { - if path.starts_with(&settings_path) { + fn value_for_path(&self, path: Option<(usize, &Path)>) -> &dyn Any { + if let Some((root_id, path)) = path { + for (settings_root_id, settings_path, value) in self.local_values.iter().rev() { + if root_id == *settings_root_id && path.starts_with(&settings_path) { return value; } } @@ -563,11 +586,14 @@ impl AnySettingValue for SettingValue { self.global_value = Some(*value.downcast().unwrap()); } - fn set_local_value(&mut self, path: Arc, value: Box) { + fn set_local_value(&mut self, root_id: usize, path: Arc, value: Box) { let value = *value.downcast().unwrap(); - match self.local_values.binary_search_by_key(&&path, |e| &e.0) { - Ok(ix) => self.local_values[ix].1 = value, - Err(ix) => self.local_values.insert(ix, (path, value)), + match self + .local_values + .binary_search_by_key(&(root_id, &path), |e| (e.0, &e.1)) + { + Ok(ix) => self.local_values[ix].2 = value, + Err(ix) => self.local_values.insert(ix, (root_id, path, value)), } } @@ -884,6 +910,7 @@ mod tests { store .set_local_settings( + 1, Path::new("/root1").into(), Some(r#"{ "user": { "staff": true } }"#), cx, @@ -891,6 +918,7 @@ mod tests { .unwrap(); store .set_local_settings( + 1, Path::new("/root1/subdir").into(), Some(r#"{ "user": { "name": "Jane Doe" } }"#), cx, @@ -899,6 +927,7 @@ mod tests { store .set_local_settings( + 1, Path::new("/root2").into(), Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#), cx, @@ -906,7 +935,7 @@ mod tests { .unwrap(); assert_eq!( - store.get::(Some(Path::new("/root1/something"))), + store.get::(Some((1, Path::new("/root1/something")))), &UserSettings { name: "John Doe".to_string(), age: 31, @@ -914,7 +943,7 @@ mod tests { } ); assert_eq!( - store.get::(Some(Path::new("/root1/subdir/something"))), + store.get::(Some((1, Path::new("/root1/subdir/something")))), &UserSettings { name: "Jane Doe".to_string(), age: 31, @@ -922,7 +951,7 @@ mod tests { } ); assert_eq!( - store.get::(Some(Path::new("/root2/something"))), + store.get::(Some((1, Path::new("/root2/something")))), &UserSettings { name: "John Doe".to_string(), age: 42, @@ -930,7 +959,7 @@ mod tests { } ); assert_eq!( - store.get::(Some(Path::new("/root2/something"))), + store.get::(Some((1, Path::new("/root2/something")))), &MultiKeySettings { key1: "a".to_string(), key2: "b".to_string(), diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 767e3bf4db..2842dfa8a4 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -905,7 +905,10 @@ mod tests { cx: &mut TestAppContext, ) -> (ModelHandle, ViewHandle) { let params = cx.update(AppState::test); - cx.update(|cx| theme::init((), cx)); + cx.update(|cx| { + theme::init((), cx); + language::init(cx); + }); let project = Project::test(params.fs.clone(), [], cx).await; let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index f998fc319f..e3397a1557 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -15,6 +15,7 @@ lazy_static::lazy_static! { pub static ref LAST_USERNAME: PathBuf = CONFIG_DIR.join("last-username.txt"); pub static ref LOG: PathBuf = LOGS_DIR.join("Zed.log"); pub static ref OLD_LOG: PathBuf = LOGS_DIR.join("Zed.log.old"); + pub static ref LOCAL_SETTINGS_RELATIVE_PATH: &'static Path = Path::new(".zed/settings.json"); } pub mod legacy { diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index bd5f2b4c02..d66602ee04 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -107,7 +107,7 @@ impl LspAdapter for YamlLspAdapter { "keyOrdering": false }, "[yaml]": { - "editor.tabSize": language_settings(Some("YAML"), cx).tab_size, + "editor.tabSize": language_settings(Some("YAML"), None, cx).tab_size, } })) .boxed(), From ed0fa2404c3cc53fb4e59a21692fcc18bfe1f4b4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 29 May 2023 14:31:54 -0700 Subject: [PATCH 03/53] Use settings JSON schema when editing local settings files --- crates/zed/src/languages/json.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 406d54cc03..3947c62a6d 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -135,7 +135,10 @@ impl LspAdapter for JsonLspAdapter { }, "schemas": [ { - "fileMatch": [schema_file_match(&paths::SETTINGS)], + "fileMatch": [ + schema_file_match(&paths::SETTINGS), + &*paths::LOCAL_SETTINGS_RELATIVE_PATH, + ], "schema": settings_schema, }, { From 2269c19169741fc9a86e9cf9cbec50b4132e7782 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 14:06:19 +0200 Subject: [PATCH 04/53] chore: add LICENSE files --- styles/src/themes/atelier-cave/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-dune/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-estuary/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-forest/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-heath/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-lakeside/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-plateau/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-savanna/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-seaside/LICENSE | 21 +++++++++++++++++++ styles/src/themes/atelier-sulphurpool/LICENSE | 21 +++++++++++++++++++ styles/src/themes/ayu/LICENSE | 21 +++++++++++++++++++ 11 files changed, 231 insertions(+) create mode 100644 styles/src/themes/atelier-cave/LICENSE create mode 100644 styles/src/themes/atelier-dune/LICENSE create mode 100644 styles/src/themes/atelier-estuary/LICENSE create mode 100644 styles/src/themes/atelier-forest/LICENSE create mode 100644 styles/src/themes/atelier-heath/LICENSE create mode 100644 styles/src/themes/atelier-lakeside/LICENSE create mode 100644 styles/src/themes/atelier-plateau/LICENSE create mode 100644 styles/src/themes/atelier-savanna/LICENSE create mode 100644 styles/src/themes/atelier-seaside/LICENSE create mode 100644 styles/src/themes/atelier-sulphurpool/LICENSE create mode 100644 styles/src/themes/ayu/LICENSE diff --git a/styles/src/themes/atelier-cave/LICENSE b/styles/src/themes/atelier-cave/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-cave/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-dune/LICENSE b/styles/src/themes/atelier-dune/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-dune/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-estuary/LICENSE b/styles/src/themes/atelier-estuary/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-estuary/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-forest/LICENSE b/styles/src/themes/atelier-forest/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-forest/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-heath/LICENSE b/styles/src/themes/atelier-heath/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-heath/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-lakeside/LICENSE b/styles/src/themes/atelier-lakeside/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-lakeside/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-plateau/LICENSE b/styles/src/themes/atelier-plateau/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-plateau/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-savanna/LICENSE b/styles/src/themes/atelier-savanna/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-savanna/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-seaside/LICENSE b/styles/src/themes/atelier-seaside/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-seaside/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-sulphurpool/LICENSE b/styles/src/themes/atelier-sulphurpool/LICENSE new file mode 100644 index 0000000000..9f92967a04 --- /dev/null +++ b/styles/src/themes/atelier-sulphurpool/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/ayu/LICENSE b/styles/src/themes/ayu/LICENSE new file mode 100644 index 0000000000..6b83ef0582 --- /dev/null +++ b/styles/src/themes/ayu/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Ike Ku + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From 0d62e76cd629d721997d0798ca50832dd28a9073 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 15:52:32 +0200 Subject: [PATCH 05/53] refactor: colorSchemes --- styles/src/buildThemes.ts | 2 +- styles/src/colorSchemes.ts | 76 +++++++++++++++++++++----------------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/styles/src/buildThemes.ts b/styles/src/buildThemes.ts index 2a63a407cc..4353765573 100644 --- a/styles/src/buildThemes.ts +++ b/styles/src/buildThemes.ts @@ -1,7 +1,7 @@ import * as fs from "fs" import { tmpdir } from "os" import * as path from "path" -import colorSchemes, { staffColorSchemes } from "./colorSchemes" +import { colorSchemes, staffColorSchemes } from "./colorSchemes" import app from "./styleTree/app" import { ColorScheme } from "./themes/common/colorScheme" import snakeCase from "./utils/snakeCase" diff --git a/styles/src/colorSchemes.ts b/styles/src/colorSchemes.ts index 4d2d7f6e02..e8d0687044 100644 --- a/styles/src/colorSchemes.ts +++ b/styles/src/colorSchemes.ts @@ -2,53 +2,63 @@ import fs from "fs" import path from "path" import { ColorScheme, Meta } from "./themes/common/colorScheme" -const colorSchemes: ColorScheme[] = [] -export default colorSchemes +const THEMES_DIRECTORY = path.resolve(`${__dirname}/themes`) +const STAFF_DIRECTORY = path.resolve(`${__dirname}/themes/staff`) +const IGNORE_ITEMS = ["staff", "common", "template.ts"] +const ACCEPT_EXTENSION = ".ts" -const schemeMeta: Meta[] = [] -export { schemeMeta } +function getAllTsFiles(directoryPath: string) { + const files = fs.readdirSync(directoryPath) + const fileList: string[] = [] -const staffColorSchemes: ColorScheme[] = [] -export { staffColorSchemes } + for (const file of files) { + if (!IGNORE_ITEMS.includes(file)) { + const filePath = path.join(directoryPath, file) -const experimentalColorSchemes: ColorScheme[] = [] -export { experimentalColorSchemes } - -const themes_directory = path.resolve(`${__dirname}/themes`) - -function for_all_color_schemes_in( - themesPath: string, - callback: (module: any, path: string) => void -) { - for (const fileName of fs.readdirSync(themesPath)) { - if (fileName == "template.ts") continue - const filePath = path.join(themesPath, fileName) - - if (fs.statSync(filePath).isFile()) { - const colorScheme = require(filePath) - callback(colorScheme, path.basename(filePath)) + if (fs.statSync(filePath).isDirectory()) { + fileList.push(...getAllTsFiles(filePath)) + } else if (path.extname(file) === ACCEPT_EXTENSION) { + fileList.push(filePath) + } } } + + return fileList } -function fillColorSchemes(themesPath: string, colorSchemes: ColorScheme[]) { - for_all_color_schemes_in(themesPath, (colorScheme, _path) => { +function getAllColorSchemes(directoryPath: string) { + const files = getAllTsFiles(directoryPath) + return files.map((filePath) => ({ + colorScheme: require(filePath), + filePath: path.basename(filePath), + })) +} + +function getColorSchemes(directoryPath: string) { + const colorSchemes: ColorScheme[] = [] + + for (const { colorScheme } of getAllColorSchemes(directoryPath)) { if (colorScheme.dark) colorSchemes.push(colorScheme.dark) - if (colorScheme.light) colorSchemes.push(colorScheme.light) - }) + else if (colorScheme.light) colorSchemes.push(colorScheme.light) + } + + return colorSchemes } -fillColorSchemes(themes_directory, colorSchemes) -fillColorSchemes(path.resolve(`${themes_directory}/staff`), staffColorSchemes) +function getMeta(directoryPath: string) { + const meta: Meta[] = [] -function fillMeta(themesPath: string, meta: Meta[]) { - for_all_color_schemes_in(themesPath, (colorScheme, path) => { + for (const { colorScheme, filePath } of getAllColorSchemes(directoryPath)) { if (colorScheme.meta) { meta.push(colorScheme.meta) } else { - throw Error(`Public theme ${path} must have a meta field`) + throw Error(`Public theme ${filePath} must have a meta field`) } - }) + } + + return meta } -fillMeta(themes_directory, schemeMeta) +export const colorSchemes = getColorSchemes(THEMES_DIRECTORY) +export const staffColorSchemes = getColorSchemes(STAFF_DIRECTORY) +export const schemeMeta = getMeta(THEMES_DIRECTORY) From ac63043c273f724777a1ba9254ee6d8e6a882977 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 16:52:28 +0200 Subject: [PATCH 06/53] chore: move similar themes to one folder --- styles/src/themes/atelier-dune/LICENSE | 21 ------------------- styles/src/themes/atelier-estuary/LICENSE | 21 ------------------- styles/src/themes/atelier-forest/LICENSE | 21 ------------------- styles/src/themes/atelier-heath/LICENSE | 21 ------------------- styles/src/themes/atelier-lakeside/LICENSE | 21 ------------------- styles/src/themes/atelier-plateau/LICENSE | 21 ------------------- styles/src/themes/atelier-savanna/LICENSE | 21 ------------------- styles/src/themes/atelier-seaside/LICENSE | 21 ------------------- styles/src/themes/atelier-sulphurpool/LICENSE | 21 ------------------- .../themes/{atelier-cave => atelier}/LICENSE | 0 .../atelier-cave-dark.ts | 2 +- .../atelier-cave-light.ts | 2 +- .../atelier-dune-dark.ts | 2 +- .../atelier-dune-light.ts | 2 +- .../atelier-estuary-dark.ts | 2 +- .../atelier-estuary-light.ts | 2 +- .../atelier-forest-dark.ts | 2 +- .../atelier-forest-light.ts | 2 +- .../atelier-heath-dark.ts | 2 +- .../atelier-heath-light.ts | 2 +- .../atelier-lakeside-dark.ts | 2 +- .../atelier-lakeside-light.ts | 2 +- .../atelier-plateau-dark.ts | 2 +- .../atelier-plateau-light.ts | 2 +- .../atelier-savanna-dark.ts | 2 +- .../atelier-savanna-light.ts | 2 +- .../atelier-seaside-dark.ts | 2 +- .../atelier-seaside-light.ts | 2 +- .../atelier-sulphurpool-dark.ts | 2 +- .../atelier-sulphurpool-light.ts | 2 +- 30 files changed, 20 insertions(+), 209 deletions(-) delete mode 100644 styles/src/themes/atelier-dune/LICENSE delete mode 100644 styles/src/themes/atelier-estuary/LICENSE delete mode 100644 styles/src/themes/atelier-forest/LICENSE delete mode 100644 styles/src/themes/atelier-heath/LICENSE delete mode 100644 styles/src/themes/atelier-lakeside/LICENSE delete mode 100644 styles/src/themes/atelier-plateau/LICENSE delete mode 100644 styles/src/themes/atelier-savanna/LICENSE delete mode 100644 styles/src/themes/atelier-seaside/LICENSE delete mode 100644 styles/src/themes/atelier-sulphurpool/LICENSE rename styles/src/themes/{atelier-cave => atelier}/LICENSE (100%) rename styles/src/themes/{atelier-cave => atelier}/atelier-cave-dark.ts (95%) rename styles/src/themes/{atelier-cave => atelier}/atelier-cave-light.ts (95%) rename styles/src/themes/{atelier-dune => atelier}/atelier-dune-dark.ts (95%) rename styles/src/themes/{atelier-dune => atelier}/atelier-dune-light.ts (95%) rename styles/src/themes/{atelier-estuary => atelier}/atelier-estuary-dark.ts (95%) rename styles/src/themes/{atelier-estuary => atelier}/atelier-estuary-light.ts (95%) rename styles/src/themes/{atelier-forest => atelier}/atelier-forest-dark.ts (95%) rename styles/src/themes/{atelier-forest => atelier}/atelier-forest-light.ts (95%) rename styles/src/themes/{atelier-heath => atelier}/atelier-heath-dark.ts (95%) rename styles/src/themes/{atelier-heath => atelier}/atelier-heath-light.ts (95%) rename styles/src/themes/{atelier-lakeside => atelier}/atelier-lakeside-dark.ts (95%) rename styles/src/themes/{atelier-lakeside => atelier}/atelier-lakeside-light.ts (95%) rename styles/src/themes/{atelier-plateau => atelier}/atelier-plateau-dark.ts (95%) rename styles/src/themes/{atelier-plateau => atelier}/atelier-plateau-light.ts (95%) rename styles/src/themes/{atelier-savanna => atelier}/atelier-savanna-dark.ts (95%) rename styles/src/themes/{atelier-savanna => atelier}/atelier-savanna-light.ts (95%) rename styles/src/themes/{atelier-seaside => atelier}/atelier-seaside-dark.ts (95%) rename styles/src/themes/{atelier-seaside => atelier}/atelier-seaside-light.ts (95%) rename styles/src/themes/{atelier-sulphurpool => atelier}/atelier-sulphurpool-dark.ts (95%) rename styles/src/themes/{atelier-sulphurpool => atelier}/atelier-sulphurpool-light.ts (95%) diff --git a/styles/src/themes/atelier-dune/LICENSE b/styles/src/themes/atelier-dune/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-dune/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-estuary/LICENSE b/styles/src/themes/atelier-estuary/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-estuary/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-forest/LICENSE b/styles/src/themes/atelier-forest/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-forest/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-heath/LICENSE b/styles/src/themes/atelier-heath/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-heath/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-lakeside/LICENSE b/styles/src/themes/atelier-lakeside/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-lakeside/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-plateau/LICENSE b/styles/src/themes/atelier-plateau/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-plateau/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-savanna/LICENSE b/styles/src/themes/atelier-savanna/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-savanna/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-seaside/LICENSE b/styles/src/themes/atelier-seaside/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-seaside/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-sulphurpool/LICENSE b/styles/src/themes/atelier-sulphurpool/LICENSE deleted file mode 100644 index 9f92967a04..0000000000 --- a/styles/src/themes/atelier-sulphurpool/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2023 Bram de Haan, http://atelierbramdehaan.nl - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/styles/src/themes/atelier-cave/LICENSE b/styles/src/themes/atelier/LICENSE similarity index 100% rename from styles/src/themes/atelier-cave/LICENSE rename to styles/src/themes/atelier/LICENSE diff --git a/styles/src/themes/atelier-cave/atelier-cave-dark.ts b/styles/src/themes/atelier/atelier-cave-dark.ts similarity index 95% rename from styles/src/themes/atelier-cave/atelier-cave-dark.ts rename to styles/src/themes/atelier/atelier-cave-dark.ts index a61db0e590..41ee6a745c 100644 --- a/styles/src/themes/atelier-cave/atelier-cave-dark.ts +++ b/styles/src/themes/atelier/atelier-cave-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-cave/atelier-cave-light.ts b/styles/src/themes/atelier/atelier-cave-light.ts similarity index 95% rename from styles/src/themes/atelier-cave/atelier-cave-light.ts rename to styles/src/themes/atelier/atelier-cave-light.ts index 739fe1320b..9e0473bbfa 100644 --- a/styles/src/themes/atelier-cave/atelier-cave-light.ts +++ b/styles/src/themes/atelier/atelier-cave-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-dune/atelier-dune-dark.ts b/styles/src/themes/atelier/atelier-dune-dark.ts similarity index 95% rename from styles/src/themes/atelier-dune/atelier-dune-dark.ts rename to styles/src/themes/atelier/atelier-dune-dark.ts index 7a3c3fbd07..c21ee61a60 100644 --- a/styles/src/themes/atelier-dune/atelier-dune-dark.ts +++ b/styles/src/themes/atelier/atelier-dune-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-dune/atelier-dune-light.ts b/styles/src/themes/atelier/atelier-dune-light.ts similarity index 95% rename from styles/src/themes/atelier-dune/atelier-dune-light.ts rename to styles/src/themes/atelier/atelier-dune-light.ts index 3ee60164b6..956ae72c60 100644 --- a/styles/src/themes/atelier-dune/atelier-dune-light.ts +++ b/styles/src/themes/atelier/atelier-dune-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-estuary/atelier-estuary-dark.ts b/styles/src/themes/atelier/atelier-estuary-dark.ts similarity index 95% rename from styles/src/themes/atelier-estuary/atelier-estuary-dark.ts rename to styles/src/themes/atelier/atelier-estuary-dark.ts index 5ec6df8d35..b9f1880d68 100644 --- a/styles/src/themes/atelier-estuary/atelier-estuary-dark.ts +++ b/styles/src/themes/atelier/atelier-estuary-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-estuary/atelier-estuary-light.ts b/styles/src/themes/atelier/atelier-estuary-light.ts similarity index 95% rename from styles/src/themes/atelier-estuary/atelier-estuary-light.ts rename to styles/src/themes/atelier/atelier-estuary-light.ts index bc583d41ac..a9eeb612ed 100644 --- a/styles/src/themes/atelier-estuary/atelier-estuary-light.ts +++ b/styles/src/themes/atelier/atelier-estuary-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-forest/atelier-forest-dark.ts b/styles/src/themes/atelier/atelier-forest-dark.ts similarity index 95% rename from styles/src/themes/atelier-forest/atelier-forest-dark.ts rename to styles/src/themes/atelier/atelier-forest-dark.ts index 72f958efdf..352f1ea43e 100644 --- a/styles/src/themes/atelier-forest/atelier-forest-dark.ts +++ b/styles/src/themes/atelier/atelier-forest-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-forest/atelier-forest-light.ts b/styles/src/themes/atelier/atelier-forest-light.ts similarity index 95% rename from styles/src/themes/atelier-forest/atelier-forest-light.ts rename to styles/src/themes/atelier/atelier-forest-light.ts index 463350f822..1378c9b061 100644 --- a/styles/src/themes/atelier-forest/atelier-forest-light.ts +++ b/styles/src/themes/atelier/atelier-forest-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-heath/atelier-heath-dark.ts b/styles/src/themes/atelier/atelier-heath-dark.ts similarity index 95% rename from styles/src/themes/atelier-heath/atelier-heath-dark.ts rename to styles/src/themes/atelier/atelier-heath-dark.ts index 010169b022..4c35194422 100644 --- a/styles/src/themes/atelier-heath/atelier-heath-dark.ts +++ b/styles/src/themes/atelier/atelier-heath-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-heath/atelier-heath-light.ts b/styles/src/themes/atelier/atelier-heath-light.ts similarity index 95% rename from styles/src/themes/atelier-heath/atelier-heath-light.ts rename to styles/src/themes/atelier/atelier-heath-light.ts index 09d39b6d66..51b9ef9398 100644 --- a/styles/src/themes/atelier-heath/atelier-heath-light.ts +++ b/styles/src/themes/atelier/atelier-heath-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts b/styles/src/themes/atelier/atelier-lakeside-dark.ts similarity index 95% rename from styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts rename to styles/src/themes/atelier/atelier-lakeside-dark.ts index 59728f30f3..ece9179860 100644 --- a/styles/src/themes/atelier-lakeside/atelier-lakeside-dark.ts +++ b/styles/src/themes/atelier/atelier-lakeside-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts b/styles/src/themes/atelier/atelier-lakeside-light.ts similarity index 95% rename from styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts rename to styles/src/themes/atelier/atelier-lakeside-light.ts index d26c4fe679..fd265d10c8 100644 --- a/styles/src/themes/atelier-lakeside/atelier-lakeside-light.ts +++ b/styles/src/themes/atelier/atelier-lakeside-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-plateau/atelier-plateau-dark.ts b/styles/src/themes/atelier/atelier-plateau-dark.ts similarity index 95% rename from styles/src/themes/atelier-plateau/atelier-plateau-dark.ts rename to styles/src/themes/atelier/atelier-plateau-dark.ts index 8299116c0e..ef64782bda 100644 --- a/styles/src/themes/atelier-plateau/atelier-plateau-dark.ts +++ b/styles/src/themes/atelier/atelier-plateau-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-plateau/atelier-plateau-light.ts b/styles/src/themes/atelier/atelier-plateau-light.ts similarity index 95% rename from styles/src/themes/atelier-plateau/atelier-plateau-light.ts rename to styles/src/themes/atelier/atelier-plateau-light.ts index 7fcc4220ac..9fe51f5b79 100644 --- a/styles/src/themes/atelier-plateau/atelier-plateau-light.ts +++ b/styles/src/themes/atelier/atelier-plateau-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-savanna/atelier-savanna-dark.ts b/styles/src/themes/atelier/atelier-savanna-dark.ts similarity index 95% rename from styles/src/themes/atelier-savanna/atelier-savanna-dark.ts rename to styles/src/themes/atelier/atelier-savanna-dark.ts index 5b6f034563..36de9b817f 100644 --- a/styles/src/themes/atelier-savanna/atelier-savanna-dark.ts +++ b/styles/src/themes/atelier/atelier-savanna-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-savanna/atelier-savanna-light.ts b/styles/src/themes/atelier/atelier-savanna-light.ts similarity index 95% rename from styles/src/themes/atelier-savanna/atelier-savanna-light.ts rename to styles/src/themes/atelier/atelier-savanna-light.ts index 4989594876..d5d9cb369d 100644 --- a/styles/src/themes/atelier-savanna/atelier-savanna-light.ts +++ b/styles/src/themes/atelier/atelier-savanna-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-seaside/atelier-seaside-dark.ts b/styles/src/themes/atelier/atelier-seaside-dark.ts similarity index 95% rename from styles/src/themes/atelier-seaside/atelier-seaside-dark.ts rename to styles/src/themes/atelier/atelier-seaside-dark.ts index 00631eff51..f7c49ad71e 100644 --- a/styles/src/themes/atelier-seaside/atelier-seaside-dark.ts +++ b/styles/src/themes/atelier/atelier-seaside-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-seaside/atelier-seaside-light.ts b/styles/src/themes/atelier/atelier-seaside-light.ts similarity index 95% rename from styles/src/themes/atelier-seaside/atelier-seaside-light.ts rename to styles/src/themes/atelier/atelier-seaside-light.ts index af6af2f027..1cf6461446 100644 --- a/styles/src/themes/atelier-seaside/atelier-seaside-light.ts +++ b/styles/src/themes/atelier/atelier-seaside-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts b/styles/src/themes/atelier/atelier-sulphurpool-dark.ts similarity index 95% rename from styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts rename to styles/src/themes/atelier/atelier-sulphurpool-dark.ts index 140d02c2e6..b4a4e2a651 100644 --- a/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-dark.ts +++ b/styles/src/themes/atelier/atelier-sulphurpool-dark.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { diff --git a/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts b/styles/src/themes/atelier/atelier-sulphurpool-light.ts similarity index 95% rename from styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts rename to styles/src/themes/atelier/atelier-sulphurpool-light.ts index b6dba4c384..046adbdf43 100644 --- a/styles/src/themes/atelier-sulphurpool/atelier-sulphurpool-light.ts +++ b/styles/src/themes/atelier/atelier-sulphurpool-light.ts @@ -1,7 +1,7 @@ import chroma from "chroma-js" import { Meta } from "../common/colorScheme" import { colorRamp, createColorScheme } from "../common/ramps" -import { metaCommon, name, buildSyntax, Variant } from "../common/atelier-common" +import { metaCommon, name, buildSyntax, Variant } from "./common" const variant: Variant = { meta: { From 05d1dd6b11386002680d8d0df67e975c56517ce2 Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 16:53:12 +0200 Subject: [PATCH 07/53] refactor: move common to theme folder --- styles/src/colorSchemes.ts | 2 +- .../themes/{common/atelier-common.ts => atelier/common.ts} | 2 +- styles/src/themes/ayu/ayu-dark.ts | 2 +- styles/src/themes/ayu/ayu-light.ts | 2 +- styles/src/themes/ayu/ayu-mirage.ts | 2 +- styles/src/themes/{common/ayu-common.ts => ayu/common.ts} | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) rename styles/src/themes/{common/atelier-common.ts => atelier/common.ts} (96%) rename styles/src/themes/{common/ayu-common.ts => ayu/common.ts} (95%) diff --git a/styles/src/colorSchemes.ts b/styles/src/colorSchemes.ts index e8d0687044..07eb26a591 100644 --- a/styles/src/colorSchemes.ts +++ b/styles/src/colorSchemes.ts @@ -4,7 +4,7 @@ import { ColorScheme, Meta } from "./themes/common/colorScheme" const THEMES_DIRECTORY = path.resolve(`${__dirname}/themes`) const STAFF_DIRECTORY = path.resolve(`${__dirname}/themes/staff`) -const IGNORE_ITEMS = ["staff", "common", "template.ts"] +const IGNORE_ITEMS = ["staff", "common", "common.ts"] const ACCEPT_EXTENSION = ".ts" function getAllTsFiles(directoryPath: string) { diff --git a/styles/src/themes/common/atelier-common.ts b/styles/src/themes/atelier/common.ts similarity index 96% rename from styles/src/themes/common/atelier-common.ts rename to styles/src/themes/atelier/common.ts index 08a915d019..7fb07f7a6e 100644 --- a/styles/src/themes/common/atelier-common.ts +++ b/styles/src/themes/atelier/common.ts @@ -1,4 +1,4 @@ -import { License, Meta, ThemeSyntax } from "./colorScheme" +import { License, Meta, ThemeSyntax } from "../common/colorScheme" export interface Variant { meta: Meta diff --git a/styles/src/themes/ayu/ayu-dark.ts b/styles/src/themes/ayu/ayu-dark.ts index eb837231fe..1dc663f161 100644 --- a/styles/src/themes/ayu/ayu-dark.ts +++ b/styles/src/themes/ayu/ayu-dark.ts @@ -1,5 +1,5 @@ import { createColorScheme } from "../common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" +import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/ayu/ayu-light.ts b/styles/src/themes/ayu/ayu-light.ts index 9c29449db5..2543521944 100644 --- a/styles/src/themes/ayu/ayu-light.ts +++ b/styles/src/themes/ayu/ayu-light.ts @@ -1,5 +1,5 @@ import { createColorScheme } from "../common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" +import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/ayu/ayu-mirage.ts b/styles/src/themes/ayu/ayu-mirage.ts index 52bf549392..2ada3678ee 100644 --- a/styles/src/themes/ayu/ayu-mirage.ts +++ b/styles/src/themes/ayu/ayu-mirage.ts @@ -1,5 +1,5 @@ import { createColorScheme } from "../common/ramps" -import { ayu, meta as themeMeta, buildTheme } from "../common/ayu-common" +import { ayu, meta as themeMeta, buildTheme } from "./common" export const meta = { ...themeMeta, diff --git a/styles/src/themes/common/ayu-common.ts b/styles/src/themes/ayu/common.ts similarity index 95% rename from styles/src/themes/common/ayu-common.ts rename to styles/src/themes/ayu/common.ts index f08817ef49..2d3687801f 100644 --- a/styles/src/themes/common/ayu-common.ts +++ b/styles/src/themes/ayu/common.ts @@ -1,8 +1,8 @@ import { dark, light, mirage } from "ayu" -import { ThemeSyntax } from "./syntax" +import { ThemeSyntax } from "../common/syntax" import chroma from "chroma-js" -import { colorRamp } from "./ramps" -import { Meta } from "./colorScheme" +import { colorRamp } from "../common/ramps" +import { Meta } from "../common/colorScheme" export const ayu = { dark, From 14efc18ecac7a601ad8481cde656a5dc6123ef2b Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 18:11:54 +0200 Subject: [PATCH 08/53] refactor: add license file and check it exists --- styles/src/colorSchemes.ts | 33 ++++++++++++++++++------- styles/src/themes/common/colorScheme.ts | 5 ++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/styles/src/colorSchemes.ts b/styles/src/colorSchemes.ts index 07eb26a591..74f1a2a03c 100644 --- a/styles/src/colorSchemes.ts +++ b/styles/src/colorSchemes.ts @@ -1,11 +1,12 @@ import fs from "fs" import path from "path" -import { ColorScheme, Meta } from "./themes/common/colorScheme" +import { ColorScheme, MetaAndLicense } from "./themes/common/colorScheme" const THEMES_DIRECTORY = path.resolve(`${__dirname}/themes`) const STAFF_DIRECTORY = path.resolve(`${__dirname}/themes/staff`) const IGNORE_ITEMS = ["staff", "common", "common.ts"] const ACCEPT_EXTENSION = ".ts" +const LICENSE_FILE_NAME = "LICENSE" function getAllTsFiles(directoryPath: string) { const files = fs.readdirSync(directoryPath) @@ -30,7 +31,9 @@ function getAllColorSchemes(directoryPath: string) { const files = getAllTsFiles(directoryPath) return files.map((filePath) => ({ colorScheme: require(filePath), - filePath: path.basename(filePath), + filePath, + fileName: path.basename(filePath), + licenseFile: `${path.dirname(filePath)}/${LICENSE_FILE_NAME}`, })) } @@ -45,15 +48,27 @@ function getColorSchemes(directoryPath: string) { return colorSchemes } -function getMeta(directoryPath: string) { - const meta: Meta[] = [] +function getMetaAndLicense(directoryPath: string) { + const meta: MetaAndLicense[] = [] - for (const { colorScheme, filePath } of getAllColorSchemes(directoryPath)) { - if (colorScheme.meta) { - meta.push(colorScheme.meta) - } else { + for (const { colorScheme, filePath, licenseFile } of getAllColorSchemes( + directoryPath + )) { + const licenseExists = fs.existsSync(licenseFile) + if (!licenseExists) { + throw Error( + `Public theme should have a LICENSE file ${licenseFile}` + ) + } + + if (!colorScheme.meta) { throw Error(`Public theme ${filePath} must have a meta field`) } + + meta.push({ + meta: colorScheme.meta, + licenseFile, + }) } return meta @@ -61,4 +76,4 @@ function getMeta(directoryPath: string) { export const colorSchemes = getColorSchemes(THEMES_DIRECTORY) export const staffColorSchemes = getColorSchemes(STAFF_DIRECTORY) -export const schemeMeta = getMeta(THEMES_DIRECTORY) +export const schemeMeta = getMetaAndLicense(THEMES_DIRECTORY) diff --git a/styles/src/themes/common/colorScheme.ts b/styles/src/themes/common/colorScheme.ts index 3b5b8f6932..657250f5f2 100644 --- a/styles/src/themes/common/colorScheme.ts +++ b/styles/src/themes/common/colorScheme.ts @@ -19,6 +19,11 @@ export interface ColorScheme { syntax?: Partial } +export interface MetaAndLicense { + meta: Meta + licenseFile: string +} + export interface Meta { name: string author: string From 4568f80f2e7cbc7e822762bcf7d54e782998d02f Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 18:12:31 +0200 Subject: [PATCH 09/53] feat: read from LICENSE file --- styles/src/buildLicenses.ts | 78 ++++++++++--------------------------- 1 file changed, 20 insertions(+), 58 deletions(-) diff --git a/styles/src/buildLicenses.ts b/styles/src/buildLicenses.ts index 3367e50ff0..9a053e9c0c 100644 --- a/styles/src/buildLicenses.ts +++ b/styles/src/buildLicenses.ts @@ -1,11 +1,9 @@ import * as fs from "fs" import toml from "toml" import { schemeMeta } from "./colorSchemes" -import { Meta, Verification } from "./themes/common/colorScheme" -import https from "https" -import crypto from "crypto" +import { MetaAndLicense } from "./themes/common/colorScheme" -const accepted_licenses_file = `${__dirname}/../../script/licenses/zed-licenses.toml` +const ACCEPTED_LICENSES_FILE = `${__dirname}/../../script/licenses/zed-licenses.toml` // Use the cargo-about configuration file as the source of truth for supported licenses. function parseAcceptedToml(file: string): string[] { @@ -20,8 +18,11 @@ function parseAcceptedToml(file: string): string[] { return obj.accepted } -function checkLicenses(schemeMeta: Meta[], licenses: string[]) { - for (let meta of schemeMeta) { +function checkLicenses( + schemeMetaWithLicense: MetaAndLicense[], + licenses: string[] +) { + for (const { meta } of schemeMetaWithLicense) { // FIXME: Add support for conjuctions and conditions if (licenses.indexOf(meta.license.SPDX) < 0) { throw Error( @@ -31,62 +32,23 @@ function checkLicenses(schemeMeta: Meta[], licenses: string[]) { } } -function getLicenseText( - schemeMeta: Meta[], - callback: (meta: Meta, license_text: string) => void -) { - for (let meta of schemeMeta) { - if (typeof meta.license.license_text == "string") { - callback(meta, meta.license.license_text) - } else { - let license_text_obj: Verification = meta.license.license_text - // The following copied from the example code on nodejs.org: - // https://nodejs.org/api/http.html#httpgetoptions-callback - https - .get(license_text_obj.https_url, (res) => { - const { statusCode } = res - - if (statusCode < 200 || statusCode >= 300) { - throw new Error( - `Failed to fetch license for: ${meta.name}, Status Code: ${statusCode}` - ) - } - - res.setEncoding("utf8") - let rawData = "" - res.on("data", (chunk) => { - rawData += chunk - }) - res.on("end", () => { - const hash = crypto - .createHash("sha256") - .update(rawData) - .digest("hex") - if (license_text_obj.license_checksum == hash) { - callback(meta, rawData) - } else { - throw Error( - `Checksum for ${meta.name} did not match file downloaded from ${license_text_obj.https_url}` - ) - } - }) - }) - .on("error", (e) => { - throw e - }) - } +function generateLicenseFile(schemeMetaWithLicense: MetaAndLicense[]) { + for (const { meta, licenseFile } of schemeMetaWithLicense) { + const licenseText = fs.readFileSync(licenseFile).toString() + writeLicense(meta.name, meta.url, licenseText) } } -function writeLicense(schemeMeta: Meta, text: String) { +function writeLicense( + themeName: string, + themeUrl: string, + licenseText: String +) { process.stdout.write( - `## [${schemeMeta.name}](${schemeMeta.url})\n\n${text}\n********************************************************************************\n\n` + `## [${themeName}](${themeUrl})\n\n${licenseText}\n********************************************************************************\n\n` ) } -const accepted_licenses = parseAcceptedToml(accepted_licenses_file) -checkLicenses(schemeMeta, accepted_licenses) - -getLicenseText(schemeMeta, (meta, text) => { - writeLicense(meta, text) -}) +const acceptedLicenses = parseAcceptedToml(ACCEPTED_LICENSES_FILE) +checkLicenses(schemeMeta, acceptedLicenses) +generateLicenseFile(schemeMeta) From 51c7078f47827e3701c275e501bc2a040799d32b Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Tue, 30 May 2023 18:16:50 +0200 Subject: [PATCH 10/53] chore: remove license_text --- styles/src/themes/andromeda/andromeda.ts | 6 ------ styles/src/themes/atelier/common.ts | 5 ----- styles/src/themes/ayu/common.ts | 6 ------ styles/src/themes/common/colorScheme.ts | 7 ------- styles/src/themes/gruvbox/gruvbox-common.ts | 2 -- styles/src/themes/one/one-dark.ts | 6 ------ styles/src/themes/one/one-light.ts | 6 ------ styles/src/themes/rose-pine/rose-pine-dawn.ts | 6 ------ styles/src/themes/rose-pine/rose-pine-moon.ts | 6 ------ styles/src/themes/rose-pine/rose-pine.ts | 6 ------ styles/src/themes/sandcastle/sandcastle.ts | 6 ------ styles/src/themes/solarized/solarized.ts | 6 ------ styles/src/themes/summercamp/summercamp.ts | 6 ------ 13 files changed, 74 deletions(-) diff --git a/styles/src/themes/andromeda/andromeda.ts b/styles/src/themes/andromeda/andromeda.ts index adfea37d06..d7f7f53b90 100644 --- a/styles/src/themes/andromeda/andromeda.ts +++ b/styles/src/themes/andromeda/andromeda.ts @@ -34,12 +34,6 @@ export const meta: Meta = { author: "EliverLara", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/EliverLara/Andromeda/master/LICENSE.md", - license_checksum: - "2f7886f1a05cefc2c26f5e49de1a39fa4466413c1ccb06fc80960e73f5ed4b89", - }, }, url: "https://github.com/EliverLara/Andromeda", } diff --git a/styles/src/themes/atelier/common.ts b/styles/src/themes/atelier/common.ts index 7fb07f7a6e..746834c88b 100644 --- a/styles/src/themes/atelier/common.ts +++ b/styles/src/themes/atelier/common.ts @@ -29,11 +29,6 @@ export const metaCommon: { author: "Bram de Haan (http://atelierbramdehaan.nl)", license: { SPDX: "MIT", - license_text: { - https_url: "https://atelierbram.mit-license.org/license.txt", - license_checksum: - "f95ce526ef4e7eecf7a832bba0e3451cc1000f9ce63eb01ed6f64f8109f5d0a5", - }, }, } diff --git a/styles/src/themes/ayu/common.ts b/styles/src/themes/ayu/common.ts index 2d3687801f..e586e2d22d 100644 --- a/styles/src/themes/ayu/common.ts +++ b/styles/src/themes/ayu/common.ts @@ -79,12 +79,6 @@ export const meta: Meta = { author: "dempfi", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/dempfi/ayu/master/LICENSE", - license_checksum: - "e0af0e0d1754c18ca075649d42f5c6d9a60f8bdc03c20dfd97105f2253a94173", - }, }, url: "https://github.com/dempfi/ayu", } diff --git a/styles/src/themes/common/colorScheme.ts b/styles/src/themes/common/colorScheme.ts index 657250f5f2..5cf125ae8f 100644 --- a/styles/src/themes/common/colorScheme.ts +++ b/styles/src/themes/common/colorScheme.ts @@ -33,13 +33,6 @@ export interface Meta { export interface License { SPDX: SPDXExpression - /// A url where we can download the license's text - license_text: Verification | string -} - -export interface Verification { - https_url: string - license_checksum: string } // License name -> License text diff --git a/styles/src/themes/gruvbox/gruvbox-common.ts b/styles/src/themes/gruvbox/gruvbox-common.ts index 5958fe62ac..b113ce68c6 100644 --- a/styles/src/themes/gruvbox/gruvbox-common.ts +++ b/styles/src/themes/gruvbox/gruvbox-common.ts @@ -248,8 +248,6 @@ export const meta: Meta = { name, license: { SPDX: "MIT", // "MIT/X11" - license_text: - "Copyright \n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/ or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", }, author: "morhetz ", url: "https://github.com/morhetz/gruvbox", diff --git a/styles/src/themes/one/one-dark.ts b/styles/src/themes/one/one-dark.ts index 71062940a5..1a88ddf7dc 100644 --- a/styles/src/themes/one/one-dark.ts +++ b/styles/src/themes/one/one-dark.ts @@ -74,12 +74,6 @@ export const meta: Meta = { author: "simurai", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/atom/atom/master/packages/one-light-ui/LICENSE.md", - license_checksum: - "d5af8fc171f6f600c0ab4e7597dca398dda80dbe6821ce01cef78e859e7a00f8", - }, }, url: "https://github.com/atom/atom/tree/master/packages/one-dark-ui", } diff --git a/styles/src/themes/one/one-light.ts b/styles/src/themes/one/one-light.ts index 8ae42e8b1d..231ee3abb3 100644 --- a/styles/src/themes/one/one-light.ts +++ b/styles/src/themes/one/one-light.ts @@ -73,12 +73,6 @@ export const meta: Meta = { author: "simurai", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/atom/atom/master/packages/one-light-ui/LICENSE.md", - license_checksum: - "d5af8fc171f6f600c0ab4e7597dca398dda80dbe6821ce01cef78e859e7a00f8", - }, }, url: "https://github.com/atom/atom/tree/master/packages/one-light-ui", } diff --git a/styles/src/themes/rose-pine/rose-pine-dawn.ts b/styles/src/themes/rose-pine/rose-pine-dawn.ts index 48da855304..cf953872e1 100644 --- a/styles/src/themes/rose-pine/rose-pine-dawn.ts +++ b/styles/src/themes/rose-pine/rose-pine-dawn.ts @@ -34,12 +34,6 @@ export const meta: Meta = { author: "edunfelt", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", - license_checksum: - "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/rose-pine/rose-pine-moon.ts b/styles/src/themes/rose-pine/rose-pine-moon.ts index 2e13ca4b70..85ac5fd73e 100644 --- a/styles/src/themes/rose-pine/rose-pine-moon.ts +++ b/styles/src/themes/rose-pine/rose-pine-moon.ts @@ -34,12 +34,6 @@ export const meta: Meta = { author: "edunfelt", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", - license_checksum: - "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/rose-pine/rose-pine.ts b/styles/src/themes/rose-pine/rose-pine.ts index 33d7d533e6..fe2817be13 100644 --- a/styles/src/themes/rose-pine/rose-pine.ts +++ b/styles/src/themes/rose-pine/rose-pine.ts @@ -32,12 +32,6 @@ export const meta: Meta = { author: "edunfelt", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", - license_checksum: - "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/sandcastle/sandcastle.ts b/styles/src/themes/sandcastle/sandcastle.ts index 0b26521256..2544b6399a 100644 --- a/styles/src/themes/sandcastle/sandcastle.ts +++ b/styles/src/themes/sandcastle/sandcastle.ts @@ -32,12 +32,6 @@ export const meta: Meta = { author: "gessig", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/gessig/base16-sandcastle-scheme/master/LICENSE", - license_checksum: - "8399d44b4d935b60be9fee0a76d7cc9a817b4f3f11574c9d6d1e8fd57e72ffdc", - }, }, url: "https://github.com/gessig/base16-sandcastle-scheme", } diff --git a/styles/src/themes/solarized/solarized.ts b/styles/src/themes/solarized/solarized.ts index 13e3e34460..6c826fbee7 100644 --- a/styles/src/themes/solarized/solarized.ts +++ b/styles/src/themes/solarized/solarized.ts @@ -35,12 +35,6 @@ export const meta: Metadata = { author: "Ethan Schoonover", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/altercation/solarized/master/LICENSE", - license_checksum: - "494aefdabf86acce06bd63001ad8aedad4ee38da23509d3f917d95aa3368b9a6", - }, }, url: "https://github.com/altercation/solarized", } diff --git a/styles/src/themes/summercamp/summercamp.ts b/styles/src/themes/summercamp/summercamp.ts index 41e101af02..94c0072e4f 100644 --- a/styles/src/themes/summercamp/summercamp.ts +++ b/styles/src/themes/summercamp/summercamp.ts @@ -34,11 +34,5 @@ export const meta: Meta = { url: "https://github.com/zoefiri/base16-sc", license: { SPDX: "MIT", - license_text: { - https_url: - "https://raw.githubusercontent.com/zoefiri/base16-sc/master/LICENSE", - license_checksum: - "fadcc834b7eaf2943800956600e8aeea4b495ecf6490f4c4b6c91556a90accaf", - }, }, } From 31516b7863b04d336253b62c08135b59dfb0056d Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 30 May 2023 14:46:27 -0400 Subject: [PATCH 11/53] `TextStyle::default()` ask system for known existing font family Rather than assuming a specific family exists, try a set of specific names and if they fail, just grab any old font that the system reports as existing Co-Authored-By: Nathan Sobo --- crates/gpui/src/font_cache.rs | 52 +++++++++++++++++++++------ crates/gpui/src/fonts.rs | 11 +++--- crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/mac/fonts.rs | 8 +++++ 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/crates/gpui/src/font_cache.rs b/crates/gpui/src/font_cache.rs index 4388ad4bcb..57dad48e34 100644 --- a/crates/gpui/src/font_cache.rs +++ b/crates/gpui/src/font_cache.rs @@ -25,8 +25,9 @@ struct Family { pub struct FontCache(RwLock); pub struct FontCacheState { - fonts: Arc, + font_system: Arc, families: Vec, + default_family: Option, font_selections: HashMap>, metrics: HashMap, wrapper_pool: HashMap<(FontId, OrderedFloat), Vec>, @@ -42,8 +43,9 @@ unsafe impl Send for FontCache {} impl FontCache { pub fn new(fonts: Arc) -> Self { Self(RwLock::new(FontCacheState { - fonts, + font_system: fonts, families: Default::default(), + default_family: None, font_selections: Default::default(), metrics: Default::default(), wrapper_pool: Default::default(), @@ -73,14 +75,14 @@ impl FontCache { let mut state = RwLockUpgradableReadGuard::upgrade(state); - if let Ok(font_ids) = state.fonts.load_family(name, features) { + if let Ok(font_ids) = state.font_system.load_family(name, features) { if font_ids.is_empty() { continue; } let family_id = FamilyId(state.families.len()); for font_id in &font_ids { - if state.fonts.glyph_for_char(*font_id, 'm').is_none() { + if state.font_system.glyph_for_char(*font_id, 'm').is_none() { return Err(anyhow!("font must contain a glyph for the 'm' character")); } } @@ -99,6 +101,31 @@ impl FontCache { )) } + /// Returns an arbitrary font family that is available on the system. + pub fn known_existing_family(&self) -> FamilyId { + if let Some(family_id) = self.0.read().default_family { + return family_id; + } + + let default_family = self + .load_family( + &["Courier", "Helvetica", "Arial", "Verdana"], + &Default::default(), + ) + .unwrap_or_else(|_| { + let all_family_names = self.0.read().font_system.all_families(); + let all_family_names: Vec<_> = all_family_names + .iter() + .map(|string| string.as_str()) + .collect(); + self.load_family(&all_family_names, &Default::default()) + .expect("could not load any default font family") + }); + + self.0.write().default_family = Some(default_family); + default_family + } + pub fn default_font(&self, family_id: FamilyId) -> FontId { self.select_font(family_id, &Properties::default()).unwrap() } @@ -115,7 +142,7 @@ impl FontCache { let mut inner = RwLockUpgradableReadGuard::upgrade(inner); let family = &inner.families[family_id.0]; let font_id = inner - .fonts + .font_system .select_font(&family.font_ids, properties) .unwrap_or(family.font_ids[0]); @@ -137,7 +164,7 @@ impl FontCache { if let Some(metrics) = state.metrics.get(&font_id) { f(metrics) } else { - let metrics = state.fonts.font_metrics(font_id); + let metrics = state.font_system.font_metrics(font_id); let metric = f(&metrics); let mut state = RwLockUpgradableReadGuard::upgrade(state); state.metrics.insert(font_id, metrics); @@ -157,8 +184,11 @@ impl FontCache { let bounds; { let state = self.0.read(); - glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap(); - bounds = state.fonts.typographic_bounds(font_id, glyph_id).unwrap(); + glyph_id = state.font_system.glyph_for_char(font_id, 'm').unwrap(); + bounds = state + .font_system + .typographic_bounds(font_id, glyph_id) + .unwrap(); } bounds.width() * self.em_scale(font_id, font_size) } @@ -168,8 +198,8 @@ impl FontCache { let advance; { let state = self.0.read(); - glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap(); - advance = state.fonts.advance(font_id, glyph_id).unwrap(); + glyph_id = state.font_system.glyph_for_char(font_id, 'm').unwrap(); + advance = state.font_system.advance(font_id, glyph_id).unwrap(); } advance.x() * self.em_scale(font_id, font_size) } @@ -214,7 +244,7 @@ impl FontCache { .or_default(); let wrapper = wrappers .pop() - .unwrap_or_else(|| LineWrapper::new(font_id, font_size, state.fonts.clone())); + .unwrap_or_else(|| LineWrapper::new(font_id, font_size, state.font_system.clone())); LineWrapperHandle { wrapper: Some(wrapper), font_cache: self.clone(), diff --git a/crates/gpui/src/fonts.rs b/crates/gpui/src/fonts.rs index e0f037acb8..5e77593d05 100644 --- a/crates/gpui/src/fonts.rs +++ b/crates/gpui/src/fonts.rs @@ -295,13 +295,14 @@ impl Default for TextStyle { .as_ref() .expect("TextStyle::default can only be called within a call to with_font_cache"); - let font_family_name = Arc::from("Courier"); - let font_family_id = font_cache - .load_family(&[&font_family_name], &Default::default()) - .unwrap(); + let font_family_id = font_cache.known_existing_family(); let font_id = font_cache .select_font(font_family_id, &Default::default()) - .unwrap(); + .expect("did not have any font in system-provided family"); + let font_family_name = font_cache + .family_name(font_family_id) + .expect("we loaded this family from the font cache, so this should work"); + Self { color: Default::default(), font_family_name, diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 9b4fd7ca51..7fc02b0548 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -343,6 +343,7 @@ pub enum RasterizationOptions { pub trait FontSystem: Send + Sync { fn add_fonts(&self, fonts: &[Arc>]) -> anyhow::Result<()>; + fn all_families(&self) -> Vec; fn load_family(&self, name: &str, features: &FontFeatures) -> anyhow::Result>; fn select_font( &self, diff --git a/crates/gpui/src/platform/mac/fonts.rs b/crates/gpui/src/platform/mac/fonts.rs index 96916698fc..0414cb8a6c 100644 --- a/crates/gpui/src/platform/mac/fonts.rs +++ b/crates/gpui/src/platform/mac/fonts.rs @@ -66,6 +66,14 @@ impl platform::FontSystem for FontSystem { self.0.write().add_fonts(fonts) } + fn all_families(&self) -> Vec { + self.0 + .read() + .system_source + .all_families() + .expect("core text should never return an error") + } + fn load_family(&self, name: &str, features: &Features) -> anyhow::Result> { self.0.write().load_family(name, features) } From 84f98f13c47933eb584ebbfcd66dbb16d11aaf63 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 11:52:14 -0700 Subject: [PATCH 12/53] Seperate open and zoom bits conceptually for new panels co-authored-by: max --- crates/workspace/src/dock.rs | 22 +++-- crates/workspace/src/workspace.rs | 140 +++++++++++++++++++++++++----- 2 files changed, 131 insertions(+), 31 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 73d4f79399..fbfcbad817 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -180,7 +180,7 @@ impl Dock { } pub fn has_focus(&self, cx: &WindowContext) -> bool { - self.active_panel() + self.visible_panel() .map_or(false, |panel| panel.has_focus(cx)) } @@ -259,7 +259,7 @@ impl Dock { cx.focus(&panel); } } else if T::should_close_on_event(event) - && this.active_panel().map_or(false, |p| p.id() == panel.id()) + && this.visible_panel().map_or(false, |p| p.id() == panel.id()) { this.set_open(false, cx); } @@ -315,12 +315,16 @@ impl Dock { } } - pub fn active_panel(&self) -> Option<&Rc> { - let entry = self.active_entry()?; + pub fn visible_panel(&self) -> Option<&Rc> { + let entry = self.visible_entry()?; Some(&entry.panel) } - fn active_entry(&self) -> Option<&PanelEntry> { + pub fn active_panel(&self) -> Option<&Rc> { + Some(&self.panel_entries.get(self.active_panel_index)?.panel) + } + + fn visible_entry(&self) -> Option<&PanelEntry> { if self.is_open { self.panel_entries.get(self.active_panel_index) } else { @@ -329,7 +333,7 @@ impl Dock { } pub fn zoomed_panel(&self, cx: &WindowContext) -> Option> { - let entry = self.active_entry()?; + let entry = self.visible_entry()?; if entry.panel.is_zoomed(cx) { Some(entry.panel.clone()) } else { @@ -362,7 +366,7 @@ impl Dock { } pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement { - if let Some(active_entry) = self.active_entry() { + if let Some(active_entry) = self.visible_entry() { Empty::new() .into_any() .contained() @@ -399,7 +403,7 @@ impl View for Dock { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - if let Some(active_entry) = self.active_entry() { + if let Some(active_entry) = self.visible_entry() { let style = self.style(cx); ChildView::new(active_entry.panel.as_any(), cx) .contained() @@ -417,7 +421,7 @@ impl View for Dock { fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { if cx.is_self_focused() { - if let Some(active_entry) = self.active_entry() { + if let Some(active_entry) = self.visible_entry() { cx.focus(active_entry.panel.as_any()); } else { cx.focus_parent(); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 8afad46ea1..6309b1d01f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -901,7 +901,7 @@ impl Workspace { was_visible = dock.is_open() && dock - .active_panel() + .visible_panel() .map_or(false, |active_panel| active_panel.id() == panel.id()); dock.remove_panel(&panel, cx); }); @@ -1636,7 +1636,7 @@ impl Workspace { } else { dock.set_open(true, cx); dock.activate_panel(panel_index, cx); - dock.active_panel().cloned() + dock.visible_panel().cloned() } }); @@ -1658,17 +1658,27 @@ impl Workspace { pub fn toggle_panel_focus(&mut self, cx: &mut ViewContext) { for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { if let Some(panel_index) = dock.read(cx).panel_index_for_type::() { - let active_item = dock.update(cx, |dock, cx| { - dock.set_open(true, cx); + let focus_center = dock.update(cx, |dock, cx| { dock.activate_panel(panel_index, cx); - dock.active_panel().cloned() - }); - if let Some(active_item) = active_item { - if active_item.has_focus(cx) { - cx.focus_self(); + + if let Some(panel) = dock.active_panel().cloned() { + if panel.has_focus(cx) { + if panel.is_zoomed(cx) { + dock.set_open(false, cx); + } + true + } else { + dock.set_open(true, cx); + cx.focus(panel.as_any()); + false + } } else { - cx.focus(active_item.as_any()); + false } + }); + + if focus_center { + cx.focus_self(); } self.serialize_workspace(cx); @@ -2835,7 +2845,7 @@ impl Workspace { fn build_serialized_docks(this: &Workspace, cx: &AppContext) -> DockStructure { let left_dock = this.left_dock.read(cx); let left_visible = left_dock.is_open(); - let left_active_panel = left_dock.active_panel().and_then(|panel| { + let left_active_panel = left_dock.visible_panel().and_then(|panel| { Some( cx.view_ui_name(panel.as_any().window_id(), panel.id())? .to_string(), @@ -2844,7 +2854,7 @@ impl Workspace { let right_dock = this.right_dock.read(cx); let right_visible = right_dock.is_open(); - let right_active_panel = right_dock.active_panel().and_then(|panel| { + let right_active_panel = right_dock.visible_panel().and_then(|panel| { Some( cx.view_ui_name(panel.as_any().window_id(), panel.id())? .to_string(), @@ -2853,7 +2863,7 @@ impl Workspace { let bottom_dock = this.bottom_dock.read(cx); let bottom_visible = bottom_dock.is_open(); - let bottom_active_panel = bottom_dock.active_panel().and_then(|panel| { + let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| { Some( cx.view_ui_name(panel.as_any().window_id(), panel.id())? .to_string(), @@ -3035,7 +3045,7 @@ impl Workspace { DockPosition::Right => &self.right_dock, DockPosition::Bottom => &self.bottom_dock, }; - let active_panel = dock.read(cx).active_panel()?; + let active_panel = dock.read(cx).visible_panel()?; let element = if Some(active_panel.id()) == self.zoomed.as_ref().map(|zoomed| zoomed.id()) { dock.read(cx).render_placeholder(cx) } else { @@ -4171,6 +4181,82 @@ mod tests { }); } + #[gpui::test] + async fn test_toggle_panel_focus(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); + + let project = Project::test(fs, [], cx).await; + let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + + let panel = workspace.update(cx, |workspace, cx| { + let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right)); + workspace.add_panel(panel.clone(), cx); + + workspace + .right_dock() + .update(cx, |right_dock, cx| right_dock.set_open(true, cx)); + + panel + }); + + // Transfer focus from center to panel + workspace.update(cx, |workspace, cx| { + workspace.toggle_panel_focus::(cx); + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(workspace.right_dock().read(cx).is_open()); + assert!(!panel.is_zoomed(cx)); + assert!(panel.has_focus(cx)); + }); + + // Transfer focus from panel to center + workspace.update(cx, |workspace, cx| { + workspace.toggle_panel_focus::(cx); + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(workspace.right_dock().read(cx).is_open()); + assert!(!panel.is_zoomed(cx)); + assert!(!panel.has_focus(cx)); + }); + + // Focus and zoom panel + panel.update(cx, |panel, cx| { + cx.focus_self(); + panel.set_zoomed(true, cx) + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(workspace.right_dock().read(cx).is_open()); + assert!(panel.is_zoomed(cx)); + assert!(panel.has_focus(cx)); + }); + + // Transfer focus to the center closes the dock + workspace.update(cx, |workspace, cx| { + workspace.toggle_panel_focus::(cx); + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(!workspace.right_dock().read(cx).is_open()); + assert!(panel.is_zoomed(cx)); + assert!(!panel.has_focus(cx)); + }); + + // Transfering focus back to the panel keeps it zoomed + workspace.update(cx, |workspace, cx| { + workspace.toggle_panel_focus::(cx); + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(workspace.right_dock().read(cx).is_open()); + assert!(panel.is_zoomed(cx)); + assert!(panel.has_focus(cx)); + }); + } + #[gpui::test] async fn test_panels(cx: &mut gpui::TestAppContext) { init_test(cx); @@ -4194,7 +4280,7 @@ mod tests { let left_dock = workspace.left_dock(); assert_eq!( - left_dock.read(cx).active_panel().unwrap().id(), + left_dock.read(cx).visible_panel().unwrap().id(), panel_1.id() ); assert_eq!( @@ -4204,7 +4290,12 @@ mod tests { left_dock.update(cx, |left_dock, cx| left_dock.resize_active_panel(1337., cx)); assert_eq!( - workspace.right_dock().read(cx).active_panel().unwrap().id(), + workspace + .right_dock() + .read(cx) + .visible_panel() + .unwrap() + .id(), panel_2.id() ); @@ -4220,10 +4311,10 @@ mod tests { // Since panel_1 was visible on the left, it should now be visible now that it's been moved to the right. // Since it was the only panel on the left, the left dock should now be closed. assert!(!workspace.left_dock().read(cx).is_open()); - assert!(workspace.left_dock().read(cx).active_panel().is_none()); + assert!(workspace.left_dock().read(cx).visible_panel().is_none()); let right_dock = workspace.right_dock(); assert_eq!( - right_dock.read(cx).active_panel().unwrap().id(), + right_dock.read(cx).visible_panel().unwrap().id(), panel_1.id() ); assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.); @@ -4238,7 +4329,12 @@ mod tests { // And the right dock is unaffected in it's displaying of panel_1 assert!(workspace.right_dock().read(cx).is_open()); assert_eq!( - workspace.right_dock().read(cx).active_panel().unwrap().id(), + workspace + .right_dock() + .read(cx) + .visible_panel() + .unwrap() + .id(), panel_1.id() ); }); @@ -4253,7 +4349,7 @@ mod tests { let left_dock = workspace.left_dock(); assert!(left_dock.read(cx).is_open()); assert_eq!( - left_dock.read(cx).active_panel().unwrap().id(), + left_dock.read(cx).visible_panel().unwrap().id(), panel_1.id() ); assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.); @@ -4287,7 +4383,7 @@ mod tests { let left_dock = workspace.left_dock(); assert!(left_dock.read(cx).is_open()); assert_eq!( - left_dock.read(cx).active_panel().unwrap().id(), + left_dock.read(cx).visible_panel().unwrap().id(), panel_1.id() ); assert!(panel_1.is_focused(cx)); @@ -4301,7 +4397,7 @@ mod tests { let left_dock = workspace.left_dock(); assert!(left_dock.read(cx).is_open()); assert_eq!( - left_dock.read(cx).active_panel().unwrap().id(), + left_dock.read(cx).visible_panel().unwrap().id(), panel_1.id() ); }); From 1a23fe91b47e78705afe26cf0e47826dc6ffec6f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 12:00:15 -0700 Subject: [PATCH 13/53] WIP: Remove focus side effects from toggle dock commands co-authored-by: max --- assets/keymaps/default.json | 27 ++-------------- crates/welcome/src/welcome.rs | 2 +- crates/workspace/src/workspace.rs | 51 ++++++------------------------- crates/zed/src/menus.rs | 6 ++-- crates/zed/src/zed.rs | 2 +- 5 files changed, 18 insertions(+), 70 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 35182dfaa6..7e1a8429bf 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -373,30 +373,9 @@ "workspace::ActivatePane", 8 ], - "cmd-b": [ - "workspace::ToggleLeftDock", - { "focus": true } - ], - "cmd-shift-b": [ - "workspace::ToggleLeftDock", - { "focus": false } - ], - "cmd-r": [ - "workspace::ToggleRightDock", - { "focus": true } - ], - "cmd-shift-r": [ - "workspace::ToggleRightDock", - { "focus": false } - ], - "cmd-j": [ - "workspace::ToggleBottomDock", - { "focus": true } - ], - "cmd-shift-j": [ - "workspace::ToggleBottomDock", - { "focus": false } - ], + "cmd-b": "workspace::ToggleLeftDock", + "cmd-r": "workspace::ToggleRightDock", + "cmd-j": "workspace::ToggleBottomDock", "cmd-shift-f": "workspace::NewSearch", "cmd-k cmd-t": "theme_selector::Toggle", "cmd-k cmd-s": "zed::OpenKeymap", diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index cef6f53a6e..b7460c4c46 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -32,7 +32,7 @@ pub fn init(cx: &mut AppContext) { pub fn show_welcome_experience(app_state: &Arc, cx: &mut AppContext) { open_new(&app_state, cx, |workspace, cx| { - workspace.toggle_dock(DockPosition::Left, false, cx); + workspace.toggle_dock(DockPosition::Left, cx); let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); cx.focus(&welcome_page); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 6309b1d01f..1a14f39fb9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -103,24 +103,6 @@ pub trait Modal: View { #[derive(Clone, PartialEq)] pub struct RemoveWorktreeFromProject(pub WorktreeId); -#[derive(Copy, Clone, Default, Deserialize, PartialEq)] -pub struct ToggleLeftDock { - #[serde(default = "default_true")] - pub focus: bool, -} - -#[derive(Copy, Clone, Default, Deserialize, PartialEq)] -pub struct ToggleBottomDock { - #[serde(default = "default_true")] - pub focus: bool, -} - -#[derive(Copy, Clone, Default, Deserialize, PartialEq)] -pub struct ToggleRightDock { - #[serde(default = "default_true")] - pub focus: bool, -} - actions!( workspace, [ @@ -143,16 +125,14 @@ actions!( Restart, Welcome, ToggleZoom, + ToggleLeftDock, + ToggleRightDock, + ToggleBottomDock, ] ); actions!(zed, [OpenSettings]); -impl_actions!( - workspace, - [ToggleLeftDock, ToggleBottomDock, ToggleRightDock] -); - #[derive(Clone, PartialEq)] pub struct OpenPaths { pub paths: Vec, @@ -268,14 +248,14 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| { workspace.activate_next_pane(cx) }); - cx.add_action(|workspace: &mut Workspace, action: &ToggleLeftDock, cx| { - workspace.toggle_dock(DockPosition::Left, action.focus, cx); + cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| { + workspace.toggle_dock(DockPosition::Left, cx); }); - cx.add_action(|workspace: &mut Workspace, action: &ToggleRightDock, cx| { - workspace.toggle_dock(DockPosition::Right, action.focus, cx); + cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| { + workspace.toggle_dock(DockPosition::Right, cx); }); - cx.add_action(|workspace: &mut Workspace, action: &ToggleBottomDock, cx| { - workspace.toggle_dock(DockPosition::Bottom, action.focus, cx); + cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| { + workspace.toggle_dock(DockPosition::Bottom, cx); }); cx.add_action(Workspace::activate_pane_at_index); cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| { @@ -1596,7 +1576,6 @@ impl Workspace { pub fn toggle_dock( &mut self, dock_side: DockPosition, - focus: bool, cx: &mut ViewContext, ) { let dock = match dock_side { @@ -1608,12 +1587,6 @@ impl Workspace { let open = !dock.is_open(); dock.set_open(open, cx); }); - - if dock.read(cx).is_open() && focus { - cx.focus(dock); - } else { - cx.focus_self(); - } cx.notify(); self.serialize_workspace(cx); } @@ -3599,10 +3572,6 @@ fn parse_pixel_position_env_var(value: &str) -> Option { Some(vec2f(width as f32, height as f32)) } -fn default_true() -> bool { - true -} - #[cfg(test)] mod tests { use super::*; @@ -4187,7 +4156,7 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let panel = workspace.update(cx, |workspace, cx| { let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right)); diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index adc1f81589..ecfd567e78 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -91,15 +91,15 @@ pub fn menus() -> Vec> { MenuItem::separator(), MenuItem::action( "Toggle Left Dock", - workspace::ToggleLeftDock { focus: false }, + workspace::ToggleLeftDock, ), MenuItem::action( "Toggle Right Dock", - workspace::ToggleRightDock { focus: false }, + workspace::ToggleRightDock, ), MenuItem::action( "Toggle Bottom Dock", - workspace::ToggleBottomDock { focus: false }, + workspace::ToggleBottomDock, ), MenuItem::submenu(Menu { name: "Editor Layout", diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c22967686a..6dbddae2ad 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -354,7 +354,7 @@ pub fn initialize_workspace( .map_or(false, |entry| entry.is_dir()) }) { - workspace.toggle_dock(project_panel_position, false, cx); + workspace.toggle_dock(project_panel_position, cx); } workspace.add_panel(terminal_panel, cx) From 501f9ab2c6dca2bc90c1466a3f95d10c86d13fb4 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Tue, 30 May 2023 15:06:35 -0400 Subject: [PATCH 14/53] Scrape email from feedback editor --- Cargo.lock | 1 + crates/feedback/Cargo.toml | 1 + crates/feedback/src/feedback_editor.rs | 12 ++++++++++++ crates/feedback/src/feedback_info_text.rs | 4 ++-- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3537056373..3751b34d31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,6 +2236,7 @@ dependencies = [ "log", "postage", "project", + "regex", "search", "serde", "serde_derive", diff --git a/crates/feedback/Cargo.toml b/crates/feedback/Cargo.toml index cd35afbda8..07b6ad790c 100644 --- a/crates/feedback/Cargo.toml +++ b/crates/feedback/Cargo.toml @@ -16,6 +16,7 @@ editor = { path = "../editor" } language = { path = "../language" } gpui = { path = "../gpui" } project = { path = "../project" } +regex.workspace = true search = { path = "../search" } settings = { path = "../settings" } theme = { path = "../theme" } diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index d5d20b069a..5a4f912e3a 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -14,6 +14,7 @@ use isahc::Request; use language::Buffer; use postage::prelude::Stream; use project::Project; +use regex::Regex; use serde::Serialize; use smallvec::SmallVec; use std::{ @@ -46,6 +47,7 @@ pub fn init(cx: &mut AppContext) { #[derive(Serialize)] struct FeedbackRequestBody<'a> { feedback_text: &'a str, + email: Option, metrics_id: Option>, installation_id: Option>, system_specs: SystemSpecs, @@ -157,8 +159,18 @@ impl FeedbackEditor { let is_staff = telemetry.is_staff(); let http_client = zed_client.http_client(); + let re = Regex::new(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b").unwrap(); + + let emails: Vec<&str> = re + .captures_iter(feedback_text) + .map(|capture| capture.get(0).unwrap().as_str()) + .collect(); + + let email = emails.first().map(|e| e.to_string()); + let request = FeedbackRequestBody { feedback_text: &feedback_text, + email, metrics_id, installation_id, system_specs, diff --git a/crates/feedback/src/feedback_info_text.rs b/crates/feedback/src/feedback_info_text.rs index 5852cd9a61..6c55b7a713 100644 --- a/crates/feedback/src/feedback_info_text.rs +++ b/crates/feedback/src/feedback_info_text.rs @@ -34,7 +34,7 @@ impl View for FeedbackInfoText { Flex::row() .with_child( Text::new( - "We read whatever you submit here. For issues and discussions, visit the ", + "Share your feedback. Include your email for replies. For issues and discussions, visit the ", theme.feedback.info_text_default.text.clone(), ) .with_soft_wrap(false) @@ -60,7 +60,7 @@ impl View for FeedbackInfoText { }), ) .with_child( - Text::new(" on GitHub.", theme.feedback.info_text_default.text.clone()) + Text::new(".", theme.feedback.info_text_default.text.clone()) .with_soft_wrap(false) .aligned(), ) From efcf78cbe9512f04444e5546544952af93f21c7a Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Tue, 30 May 2023 15:15:20 -0400 Subject: [PATCH 15/53] Update pull_request_template.md --- .github/pull_request_template.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 67a7ed31c9..2cd2050013 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,12 +2,11 @@ Release Notes: -Use `N/A` in this section if this item should be skipped in the release notes. +- N/A -Add release note lines here: +or -* (Added|Fixed|Improved) ... ([#](https://github.com/zed-industries/community/issues/)). -* ... +- (Added|Fixed|Improved) ... ([#](https://github.com/zed-industries/community/issues/)). If the release notes are only intended for a specific release channel only, add `(-only)` to the end of the release note line. These will be removed by the person making the release. From 9c9af5ed94291ad1e019c671552abdc6b2c193fb Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 12:32:00 -0700 Subject: [PATCH 16/53] Remove focus side effects from toggle dock commands co-authored-by: max --- crates/workspace/src/dock.rs | 7 +-- crates/workspace/src/workspace.rs | 77 +++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index fbfcbad817..13bebc5673 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -201,7 +201,7 @@ impl Dock { self.active_panel_index } - pub fn set_open(&mut self, open: bool, cx: &mut ViewContext) { + pub(crate) fn set_open(&mut self, open: bool, cx: &mut ViewContext) { if open != self.is_open { self.is_open = open; if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) { @@ -212,11 +212,6 @@ impl Dock { } } - pub fn toggle_open(&mut self, cx: &mut ViewContext) { - self.set_open(!self.is_open, cx); - cx.notify(); - } - pub fn set_panel_zoomed( &mut self, panel: &AnyViewHandle, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 1a14f39fb9..657285388a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1583,10 +1583,27 @@ impl Workspace { DockPosition::Bottom => &self.bottom_dock, DockPosition::Right => &self.right_dock, }; - dock.update(cx, |dock, cx| { - let open = !dock.is_open(); - dock.set_open(open, cx); + let focus_center = dock.update(cx, |dock, cx| { + let was_open = dock.is_open(); + dock.set_open(!was_open, cx); + + if let Some(active_panel) = dock.active_panel() { + if was_open { + if active_panel.has_focus(cx) { + return true; + } + } else if active_panel.is_zoomed(cx) { + cx.focus(active_panel.as_any()); + } + } + + false }); + + if focus_center { + cx.focus_self(); + } + cx.notify(); self.serialize_workspace(cx); } @@ -1639,15 +1656,13 @@ impl Workspace { if panel.is_zoomed(cx) { dock.set_open(false, cx); } - true + return true } else { dock.set_open(true, cx); cx.focus(panel.as_any()); - false } - } else { - false } + false }); if focus_center { @@ -4151,7 +4166,7 @@ mod tests { } #[gpui::test] - async fn test_toggle_panel_focus(cx: &mut gpui::TestAppContext) { + async fn test_toggle_docks_and_panels(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.background()); @@ -4191,6 +4206,28 @@ mod tests { assert!(!panel.has_focus(cx)); }); + // Close the dock + workspace.update(cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Right, cx); + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(!workspace.right_dock().read(cx).is_open()); + assert!(!panel.is_zoomed(cx)); + assert!(!panel.has_focus(cx)); + }); + + // Open the dock + workspace.update(cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Right, cx); + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(workspace.right_dock().read(cx).is_open()); + assert!(!panel.is_zoomed(cx)); + assert!(!panel.has_focus(cx)); + }); + // Focus and zoom panel panel.update(cx, |panel, cx| { cx.focus_self(); @@ -4224,6 +4261,30 @@ mod tests { assert!(panel.is_zoomed(cx)); assert!(panel.has_focus(cx)); }); + + // Close the dock while it is zoomed + workspace.update(cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Right, cx) + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(!workspace.right_dock().read(cx).is_open()); + assert!(panel.is_zoomed(cx)); + assert!(workspace.zoomed.is_none()); + assert!(!panel.has_focus(cx)); + }); + + // Opening the dock, when it's zoomed, retains focus + workspace.update(cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Right, cx) + }); + + workspace.read_with(cx, |workspace, cx| { + assert!(workspace.right_dock().read(cx).is_open()); + assert!(panel.is_zoomed(cx)); + assert!(workspace.zoomed.is_some()); + assert!(panel.has_focus(cx)); + }); } #[gpui::test] From 506580438803a2ef050c40351a0841596f6b4952 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 14:55:49 -0700 Subject: [PATCH 17/53] WIP: iron out bugs in interaction between toggle and toggle focus key bindings co-authored-by: max --- crates/workspace/src/workspace.rs | 143 +++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 21 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 657285388a..53240a9a73 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -478,6 +478,7 @@ pub struct Workspace { remote_entity_subscription: Option, modal: Option, zoomed: Option, + zoomed_position: Option, center: PaneGroup, left_dock: ViewHandle, bottom_dock: ViewHandle, @@ -683,6 +684,7 @@ impl Workspace { weak_self: weak_handle.clone(), modal: None, zoomed: None, + zoomed_position: None, center: PaneGroup::new(center_pane.clone()), panes: vec![center_pane.clone()], panes_by_item: Default::default(), @@ -885,6 +887,11 @@ impl Workspace { .map_or(false, |active_panel| active_panel.id() == panel.id()); dock.remove_panel(&panel, cx); }); + + if panel.is_zoomed(cx) { + this.zoomed_position = Some(new_position); + } + dock = match panel.read(cx).position(cx) { DockPosition::Left => &this.left_dock, DockPosition::Bottom => &this.bottom_dock, @@ -903,14 +910,17 @@ impl Workspace { dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx)); if panel.has_focus(cx) { this.zoomed = Some(panel.downgrade().into_any()); + this.zoomed_position = Some(panel.read(cx).position(cx)); } } else if T::should_zoom_out_on_event(event) { this.zoom_out(cx); } else if T::is_focus_event(event) { if panel.is_zoomed(cx) { this.zoomed = Some(panel.downgrade().into_any()); + this.zoomed_position = Some(panel.read(cx).position(cx)); } else { this.zoomed = None; + this.zoomed_position = None; } cx.notify(); } @@ -1573,33 +1583,36 @@ impl Workspace { } } - pub fn toggle_dock( - &mut self, - dock_side: DockPosition, - cx: &mut ViewContext, - ) { + pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext) { let dock = match dock_side { DockPosition::Left => &self.left_dock, DockPosition::Bottom => &self.bottom_dock, DockPosition::Right => &self.right_dock, }; - let focus_center = dock.update(cx, |dock, cx| { - let was_open = dock.is_open(); - dock.set_open(!was_open, cx); + let mut focus_center = false; + let mut zoom_out = false; + dock.update(cx, |dock, cx| { + let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side); + let was_visible = dock.is_open() && !other_is_zoomed; + dock.set_open(!was_visible, cx); if let Some(active_panel) = dock.active_panel() { - if was_open { + if was_visible { if active_panel.has_focus(cx) { - return true; + focus_center = true; } - } else if active_panel.is_zoomed(cx) { - cx.focus(active_panel.as_any()); + } else { + if active_panel.is_zoomed(cx) { + cx.focus(active_panel.as_any()); + } + zoom_out = true; } } - - false }); + if zoom_out { + self.zoom_out_everything_except(dock_side, cx); + } if focus_center { cx.focus_self(); } @@ -1646,9 +1659,24 @@ impl Workspace { } pub fn toggle_panel_focus(&mut self, cx: &mut ViewContext) { - for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { + for (dock, position) in [ + self.left_dock.clone(), + self.bottom_dock.clone(), + self.right_dock.clone(), + ] + .into_iter() + .zip( + [ + DockPosition::Left, + DockPosition::Bottom, + DockPosition::Right, + ] + .into_iter(), + ) { if let Some(panel_index) = dock.read(cx).panel_index_for_type::() { - let focus_center = dock.update(cx, |dock, cx| { + let mut focus_center = false; + let mut zoom_out = false; + dock.update(cx, |dock, cx| { dock.activate_panel(panel_index, cx); if let Some(panel) = dock.active_panel().cloned() { @@ -1656,15 +1684,18 @@ impl Workspace { if panel.is_zoomed(cx) { dock.set_open(false, cx); } - return true + focus_center = true; } else { dock.set_open(true, cx); cx.focus(panel.as_any()); + zoom_out = true; } } - false }); + if zoom_out { + self.zoom_out_everything_except(position, cx); + } if focus_center { cx.focus_self(); } @@ -1685,6 +1716,36 @@ impl Workspace { self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx)); self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx)); self.zoomed = None; + self.zoomed_position = None; + + cx.notify(); + } + + fn zoom_out_everything_except( + &mut self, + except_position: DockPosition, + cx: &mut ViewContext, + ) { + for pane in &self.panes { + pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + } + + if except_position != DockPosition::Left { + self.left_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + } + + if except_position != DockPosition::Bottom { + self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + } + + if except_position != DockPosition::Right { + self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + } + + if self.zoomed_position != Some(except_position) { + self.zoomed = None; + self.zoomed_position = None; + } cx.notify(); } @@ -1901,6 +1962,7 @@ impl Workspace { } else { self.zoomed = None; } + self.zoomed_position = None; self.update_followers( proto::update_followers::Variant::UpdateActiveView(proto::UpdateActiveView { @@ -1960,6 +2022,7 @@ impl Workspace { pane.update(cx, |pane, cx| pane.set_zoomed(true, cx)); if pane.read(cx).has_focus() { self.zoomed = Some(pane.downgrade().into_any()); + self.zoomed_position = None; } cx.notify(); } @@ -3234,13 +3297,38 @@ impl View for Workspace { .with_children(self.zoomed.as_ref().and_then(|zoomed| { enum ZoomBackground {} let zoomed = zoomed.upgrade(cx)?; + + let mut background_style = + theme.workspace.zoomed_background; + match self.zoomed_position { + Some(DockPosition::Left) => { + background_style.padding.left = 0.; + background_style.padding.top = 0.; + background_style.padding.bottom = 0.; + background_style.padding.right *= 1.; + } + Some(DockPosition::Right) => { + background_style.padding.right = 0.; + background_style.padding.top = 0.; + background_style.padding.bottom = 0.; + background_style.padding.left *= 1.; + } + Some(DockPosition::Bottom) => { + background_style.padding.left = 0.; + background_style.padding.right = 0.; + background_style.padding.bottom = 0.; + background_style.padding.top *= 1.; + } + None => {} + } + Some( ChildView::new(&zoomed, cx) .contained() .with_style(theme.workspace.zoomed_foreground) .aligned() .contained() - .with_style(theme.workspace.zoomed_background) + .with_style(background_style) .mouse::(0) .capture_all() .on_down( @@ -4436,6 +4524,14 @@ mod tests { panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomIn)); workspace.read_with(cx, |workspace, _| { assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); + assert_eq!(workspace.zoomed_position, Some(DockPosition::Left)); + }); + + // Move panel to another dock while it is zoomed + panel_1.update(cx, |panel, cx| panel.set_position(DockPosition::Right, cx)); + workspace.read_with(cx, |workspace, _| { + assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); + assert_eq!(workspace.zoomed_position, Some(DockPosition::Right)); }); // If focus is transferred to another view that's not a panel or another pane, we still show @@ -4444,12 +4540,14 @@ mod tests { focus_receiver.update(cx, |_, cx| cx.focus_self()); workspace.read_with(cx, |workspace, _| { assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); + assert_eq!(workspace.zoomed_position, Some(DockPosition::Right)); }); // If focus is transferred elsewhere in the workspace, the panel is no longer zoomed. workspace.update(cx, |_, cx| cx.focus_self()); workspace.read_with(cx, |workspace, _| { assert_eq!(workspace.zoomed, None); + assert_eq!(workspace.zoomed_position, None); }); // If focus is transferred again to another view that's not a panel or a pane, we won't @@ -4457,18 +4555,21 @@ mod tests { focus_receiver.update(cx, |_, cx| cx.focus_self()); workspace.read_with(cx, |workspace, _| { assert_eq!(workspace.zoomed, None); + assert_eq!(workspace.zoomed_position, None); }); // When focus is transferred back to the panel, it is zoomed again. panel_1.update(cx, |_, cx| cx.focus_self()); workspace.read_with(cx, |workspace, _| { assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); + assert_eq!(workspace.zoomed_position, Some(DockPosition::Right)); }); // Emitting a ZoomOut event unzooms the panel. panel_1.update(cx, |_, cx| cx.emit(TestPanelEvent::ZoomOut)); workspace.read_with(cx, |workspace, _| { assert_eq!(workspace.zoomed, None); + assert_eq!(workspace.zoomed_position, None); }); // Emit closed event on panel 1, which is active @@ -4476,8 +4577,8 @@ mod tests { // Now the left dock is closed, because panel_1 was the active panel workspace.read_with(cx, |workspace, cx| { - let left_dock = workspace.left_dock(); - assert!(!left_dock.read(cx).is_open()); + let right_dock = workspace.right_dock(); + assert!(!right_dock.read(cx).is_open()); }); } From 5e4a9abd0907eff8412c8a65df0d6f89bd16eb59 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 15:17:42 -0700 Subject: [PATCH 18/53] Fix bug in panel button dispatch --- crates/workspace/src/dock.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 13bebc5673..2f198f62f4 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -503,13 +503,21 @@ impl View for PanelButtons { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, { + let tooltip_action = + tooltip_action.as_ref().map(|action| action.boxed_clone()); move |_, this, cx| { - if let Some(workspace) = this.workspace.upgrade(cx) { - cx.window_context().defer(move |cx| { - workspace.update(cx, |workspace, cx| { - workspace.toggle_panel(dock_position, panel_ix, cx) - }); - }); + if let Some(tooltip_action) = &tooltip_action { + let window_id = cx.window_id(); + let view_id = this.workspace.id(); + let tooltip_action = tooltip_action.boxed_clone(); + cx.spawn(|_, mut cx| async move { + cx.dispatch_action( + window_id, + view_id, + &*tooltip_action, + ) + .ok(); + }).detach(); } } }) From e80ab5f096bae73e6522d120370089d8cb0104be Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 15:40:30 -0700 Subject: [PATCH 19/53] Fix focus bug with new docks co-authored-by: max --- crates/workspace/src/pane.rs | 15 ++++----------- crates/workspace/src/workspace.rs | 6 +----- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 2919bb7cd4..e722b3be6a 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -306,15 +306,6 @@ impl Pane { &self.workspace } - pub fn is_active(&self) -> bool { - self.is_active - } - - pub fn set_active(&mut self, is_active: bool, cx: &mut ViewContext) { - self.is_active = is_active; - cx.notify(); - } - pub fn has_focus(&self) -> bool { self.has_focus } @@ -1129,7 +1120,7 @@ impl Pane { None }; - let pane_active = self.is_active; + let pane_active = self.has_focus; enum Tabs {} let mut row = Flex::row().scrollable::(1, autoscroll, cx); @@ -1508,7 +1499,7 @@ impl View for Pane { let mut tab_row = Flex::row() .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs")); - if self.is_active { + if self.has_focus { let render_tab_bar_buttons = self.render_tab_bar_buttons.clone(); tab_row.add_child( (render_tab_bar_buttons)(self, cx) @@ -1599,6 +1590,7 @@ impl View for Pane { if !self.has_focus { self.has_focus = true; cx.emit(Event::Focus); + cx.notify(); } self.toolbar.update(cx, |toolbar, cx| { @@ -1633,6 +1625,7 @@ impl View for Pane { self.toolbar.update(cx, |toolbar, cx| { toolbar.pane_focus_update(false, cx); }); + cx.notify(); } fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 53240a9a73..e51a0bf812 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1945,11 +1945,7 @@ impl Workspace { fn handle_pane_focused(&mut self, pane: ViewHandle, cx: &mut ViewContext) { if self.active_pane != pane { - self.active_pane - .update(cx, |pane, cx| pane.set_active(false, cx)); self.active_pane = pane.clone(); - self.active_pane - .update(cx, |pane, cx| pane.set_active(true, cx)); self.status_bar.update(cx, |status_bar, cx| { status_bar.set_active_pane(&self.active_pane, cx); }); @@ -2868,7 +2864,7 @@ impl Workspace { }) }) .collect::>(), - pane.is_active(), + pane.has_focus(), ) }; From 9c707eff27c61136ff995d1462d3cbdadca9822b Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 16:10:07 -0700 Subject: [PATCH 20/53] Rework new terminal actions --- crates/terminal_view/src/terminal_panel.rs | 19 +++++-- crates/terminal_view/src/terminal_view.rs | 8 +-- crates/workspace/src/pane.rs | 6 +- crates/workspace/src/workspace.rs | 64 +++++++--------------- 4 files changed, 41 insertions(+), 56 deletions(-) diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index d34a261bfe..c2f7eb36e4 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -22,7 +22,7 @@ const TERMINAL_PANEL_KEY: &'static str = "TerminalPanel"; actions!(terminal_panel, [ToggleFocus]); pub fn init(cx: &mut AppContext) { - cx.add_action(TerminalPanel::add_terminal); + cx.add_action(TerminalPanel::new_terminal); } pub enum Event { @@ -80,7 +80,7 @@ impl TerminalPanel { cx.window_context().defer(move |cx| { if let Some(this) = this.upgrade(cx) { this.update(cx, |this, cx| { - this.add_terminal(&Default::default(), cx); + this.add_terminal(cx); }); } }) @@ -220,7 +220,18 @@ impl TerminalPanel { } } - fn add_terminal(&mut self, _: &workspace::NewTerminal, cx: &mut ViewContext) { + + fn new_terminal(workspace: &mut Workspace, _: &workspace::NewTerminal, cx: &mut ViewContext) { + let Some(this) = workspace.focus_panel::(cx) else { + return; + }; + + this.update(cx, |this, cx| { + this.add_terminal(cx) + }) + } + + fn add_terminal(&mut self, cx: &mut ViewContext) { let workspace = self.workspace.clone(); cx.spawn(|this, mut cx| async move { let pane = this.read_with(&cx, |this, _| this.pane.clone())?; @@ -363,7 +374,7 @@ impl Panel for TerminalPanel { fn set_active(&mut self, active: bool, cx: &mut ViewContext) { if active && self.pane.read(cx).items_len() == 0 { - self.add_terminal(&Default::default(), cx) + self.add_terminal(cx) } } diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 767e3bf4db..633b83afb8 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -38,7 +38,7 @@ use workspace::{ notifications::NotifyResultExt, pane, register_deserializable_item, searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle}, - Pane, ToolbarItemLocation, Workspace, WorkspaceId, + Pane, ToolbarItemLocation, Workspace, WorkspaceId, NewCenterTerminal, }; pub use terminal::TerminalSettings; @@ -66,10 +66,10 @@ pub fn init(cx: &mut AppContext) { terminal_panel::init(cx); terminal::init(cx); - cx.add_action(TerminalView::deploy); - register_deserializable_item::(cx); + cx.add_action(TerminalView::deploy); + //Useful terminal views cx.add_action(TerminalView::send_text); cx.add_action(TerminalView::send_keystroke); @@ -101,7 +101,7 @@ impl TerminalView { ///Create a new Terminal in the current working directory or the user's home directory pub fn deploy( workspace: &mut Workspace, - _: &workspace::NewTerminal, + _: &NewCenterTerminal, cx: &mut ViewContext, ) { let strategy = settings::get::(cx); diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index e722b3be6a..23d3d6f378 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -3,7 +3,7 @@ mod dragged_item_receiver; use super::{ItemHandle, SplitDirection}; use crate::{ item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewFile, NewSearch, NewTerminal, - ToggleZoom, Workspace, WorkspaceSettings, + ToggleZoom, Workspace, WorkspaceSettings, NewCenterTerminal, }; use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; @@ -131,7 +131,6 @@ pub enum Event { pub struct Pane { items: Vec>, activation_history: Vec, - is_active: bool, zoomed: bool, active_item_index: usize, last_focused_view_by_item: HashMap, @@ -238,7 +237,6 @@ impl Pane { Self { items: Vec::new(), activation_history: Vec::new(), - is_active: true, zoomed: false, active_item_index: 0, last_focused_view_by_item: Default::default(), @@ -996,7 +994,7 @@ impl Pane { AnchorCorner::TopRight, vec![ ContextMenuItem::action("New File", NewFile), - ContextMenuItem::action("New Terminal", NewTerminal), + ContextMenuItem::action("New Terminal", NewCenterTerminal), ContextMenuItem::action("New Search", NewSearch), ], cx, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e51a0bf812..9285e911ef 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -55,7 +55,7 @@ use std::{ path::{Path, PathBuf}, str, sync::{atomic::AtomicUsize, Arc}, - time::Duration, + time::Duration, rc::Rc, }; use crate::{ @@ -119,6 +119,7 @@ actions!( ActivateNextPane, FollowNextCollaborator, NewTerminal, + NewCenterTerminal, ToggleTerminalFocus, NewSearch, Feedback, @@ -1621,44 +1622,15 @@ impl Workspace { self.serialize_workspace(cx); } - pub fn toggle_panel( - &mut self, - position: DockPosition, - panel_index: usize, - cx: &mut ViewContext, - ) { - let dock = match position { - DockPosition::Left => &mut self.left_dock, - DockPosition::Bottom => &mut self.bottom_dock, - DockPosition::Right => &mut self.right_dock, - }; - let active_item = dock.update(cx, move |dock, cx| { - if dock.is_open() && dock.active_panel_index() == panel_index { - dock.set_open(false, cx); - None - } else { - dock.set_open(true, cx); - dock.activate_panel(panel_index, cx); - dock.visible_panel().cloned() - } - }); - - if let Some(active_item) = active_item { - if active_item.has_focus(cx) { - cx.focus_self(); - } else { - cx.focus(active_item.as_any()); - } - } else { - cx.focus_self(); - } - - self.serialize_workspace(cx); - - cx.notify(); + pub fn focus_panel(&mut self, cx: &mut ViewContext) -> Option> { + self.show_or_hide_panel::(cx, |_, _| true)?.as_any().clone().downcast() } pub fn toggle_panel_focus(&mut self, cx: &mut ViewContext) { + self.show_or_hide_panel::(cx, |panel, cx| !panel.has_focus(cx)); + } + + fn show_or_hide_panel(&mut self, cx: &mut ViewContext, show: impl Fn(&dyn PanelHandle, &mut ViewContext) -> bool) -> Option> { for (dock, position) in [ self.left_dock.clone(), self.bottom_dock.clone(), @@ -1676,21 +1648,24 @@ impl Workspace { if let Some(panel_index) = dock.read(cx).panel_index_for_type::() { let mut focus_center = false; let mut zoom_out = false; - dock.update(cx, |dock, cx| { + let panel = dock.update(cx, |dock, cx| { dock.activate_panel(panel_index, cx); - if let Some(panel) = dock.active_panel().cloned() { - if panel.has_focus(cx) { + let panel = dock.active_panel().cloned(); + if let Some(panel) = panel.as_ref() { + let should_show = show(&**panel, cx); + if should_show { + dock.set_open(true, cx); + cx.focus(panel.as_any()); + zoom_out = true; + } else { if panel.is_zoomed(cx) { dock.set_open(false, cx); } focus_center = true; - } else { - dock.set_open(true, cx); - cx.focus(panel.as_any()); - zoom_out = true; } } + panel }); if zoom_out { @@ -1702,9 +1677,10 @@ impl Workspace { self.serialize_workspace(cx); cx.notify(); - break; + return panel; } } + None } fn zoom_out(&mut self, cx: &mut ViewContext) { From 685e8d70072f7f3cfb0dd8beb7bf513c6aa205e7 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 16:23:16 -0700 Subject: [PATCH 21/53] Seperate pane and panel styling co-authored-by: max --- crates/theme/src/theme.rs | 3 ++- crates/workspace/src/pane.rs | 2 +- crates/workspace/src/workspace.rs | 39 +++++++++++++++++-------------- styles/src/styleTree/workspace.ts | 10 +++++--- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index b1c9e9c215..21c01150a8 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -89,7 +89,8 @@ pub struct Workspace { pub breadcrumbs: Interactive, pub disconnected_overlay: ContainedText, pub modal: ContainerStyle, - pub zoomed_foreground: ContainerStyle, + pub zoomed_panel_foreground: ContainerStyle, + pub zoomed_pane_foreground: ContainerStyle, pub zoomed_background: ContainerStyle, pub notification: ContainerStyle, pub notifications: Notifications, diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 23d3d6f378..8dca08ec81 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2,7 +2,7 @@ mod dragged_item_receiver; use super::{ItemHandle, SplitDirection}; use crate::{ - item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewFile, NewSearch, NewTerminal, + item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewFile, NewSearch, ToggleZoom, Workspace, WorkspaceSettings, NewCenterTerminal, }; use anyhow::Result; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9285e911ef..0aefe455ac 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3270,37 +3270,42 @@ impl View for Workspace { enum ZoomBackground {} let zoomed = zoomed.upgrade(cx)?; - let mut background_style = - theme.workspace.zoomed_background; + let mut foreground_style; match self.zoomed_position { Some(DockPosition::Left) => { - background_style.padding.left = 0.; - background_style.padding.top = 0.; - background_style.padding.bottom = 0.; - background_style.padding.right *= 1.; + foreground_style = + theme.workspace.zoomed_panel_foreground; + foreground_style.margin.left = 0.; + foreground_style.margin.top = 0.; + foreground_style.margin.bottom = 0.; } Some(DockPosition::Right) => { - background_style.padding.right = 0.; - background_style.padding.top = 0.; - background_style.padding.bottom = 0.; - background_style.padding.left *= 1.; + foreground_style = + theme.workspace.zoomed_panel_foreground; + foreground_style.margin.right = 0.; + foreground_style.margin.top = 0.; + foreground_style.margin.bottom = 0.; } Some(DockPosition::Bottom) => { - background_style.padding.left = 0.; - background_style.padding.right = 0.; - background_style.padding.bottom = 0.; - background_style.padding.top *= 1.; + foreground_style = + theme.workspace.zoomed_panel_foreground; + foreground_style.margin.left = 0.; + foreground_style.margin.right = 0.; + foreground_style.margin.bottom = 0.; + } + None => { + foreground_style = + theme.workspace.zoomed_pane_foreground; } - None => {} } Some( ChildView::new(&zoomed, cx) .contained() - .with_style(theme.workspace.zoomed_foreground) + .with_style(foreground_style) .aligned() .contained() - .with_style(background_style) + .with_style(theme.workspace.zoomed_background) .mouse::(0) .capture_all() .on_down( diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 737d225784..cf5234aa00 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -119,14 +119,18 @@ export default function workspace(colorScheme: ColorScheme) { cursor: "Arrow", }, zoomedBackground: { - padding: 10, cursor: "Arrow", - background: withOpacity(background(colorScheme.lowest), 0.5) + background: withOpacity(background(colorScheme.lowest), 0.85) }, - zoomedForeground: { + zoomedPaneForeground: { + margin: 10, shadow: colorScheme.modalShadow, border: border(colorScheme.highest, { overlay: true }), }, + zoomedPanelForeground: { + margin: 18, + border: border(colorScheme.highest, { overlay: true }), + }, dock: { left: { border: border(layer, { right: true }), From 0122cd61c555154c61f9f94c0c23f671c322bdf5 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 16:33:42 -0700 Subject: [PATCH 22/53] fmt --- crates/terminal_view/src/terminal_panel.rs | 11 ++++++----- crates/terminal_view/src/terminal_view.rs | 2 +- crates/workspace/src/dock.rs | 3 ++- crates/workspace/src/pane.rs | 4 ++-- crates/workspace/src/workspace.rs | 14 +++++++++++--- crates/zed/src/menus.rs | 15 +++------------ 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index c2f7eb36e4..e45459e683 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -220,15 +220,16 @@ impl TerminalPanel { } } - - fn new_terminal(workspace: &mut Workspace, _: &workspace::NewTerminal, cx: &mut ViewContext) { + fn new_terminal( + workspace: &mut Workspace, + _: &workspace::NewTerminal, + cx: &mut ViewContext, + ) { let Some(this) = workspace.focus_panel::(cx) else { return; }; - this.update(cx, |this, cx| { - this.add_terminal(cx) - }) + this.update(cx, |this, cx| this.add_terminal(cx)) } fn add_terminal(&mut self, cx: &mut ViewContext) { diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 633b83afb8..7f43f99ebd 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -38,7 +38,7 @@ use workspace::{ notifications::NotifyResultExt, pane, register_deserializable_item, searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle}, - Pane, ToolbarItemLocation, Workspace, WorkspaceId, NewCenterTerminal, + NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId, }; pub use terminal::TerminalSettings; diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 2f198f62f4..886afe943d 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -517,7 +517,8 @@ impl View for PanelButtons { &*tooltip_action, ) .ok(); - }).detach(); + }) + .detach(); } } }) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 8dca08ec81..921ae5e010 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2,8 +2,8 @@ mod dragged_item_receiver; use super::{ItemHandle, SplitDirection}; use crate::{ - item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewFile, NewSearch, - ToggleZoom, Workspace, WorkspaceSettings, NewCenterTerminal, + item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewCenterTerminal, NewFile, + NewSearch, ToggleZoom, Workspace, WorkspaceSettings, }; use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 0aefe455ac..d051f6d80a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -53,9 +53,10 @@ use std::{ cmp, env, future::Future, path::{Path, PathBuf}, + rc::Rc, str, sync::{atomic::AtomicUsize, Arc}, - time::Duration, rc::Rc, + time::Duration, }; use crate::{ @@ -1623,14 +1624,21 @@ impl Workspace { } pub fn focus_panel(&mut self, cx: &mut ViewContext) -> Option> { - self.show_or_hide_panel::(cx, |_, _| true)?.as_any().clone().downcast() + self.show_or_hide_panel::(cx, |_, _| true)? + .as_any() + .clone() + .downcast() } pub fn toggle_panel_focus(&mut self, cx: &mut ViewContext) { self.show_or_hide_panel::(cx, |panel, cx| !panel.has_focus(cx)); } - fn show_or_hide_panel(&mut self, cx: &mut ViewContext, show: impl Fn(&dyn PanelHandle, &mut ViewContext) -> bool) -> Option> { + fn show_or_hide_panel( + &mut self, + cx: &mut ViewContext, + show: impl Fn(&dyn PanelHandle, &mut ViewContext) -> bool, + ) -> Option> { for (dock, position) in [ self.left_dock.clone(), self.bottom_dock.clone(), diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index ecfd567e78..76e88325f5 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -89,18 +89,9 @@ pub fn menus() -> Vec> { MenuItem::action("Zoom Out", super::DecreaseBufferFontSize), MenuItem::action("Reset Zoom", super::ResetBufferFontSize), MenuItem::separator(), - MenuItem::action( - "Toggle Left Dock", - workspace::ToggleLeftDock, - ), - MenuItem::action( - "Toggle Right Dock", - workspace::ToggleRightDock, - ), - MenuItem::action( - "Toggle Bottom Dock", - workspace::ToggleBottomDock, - ), + MenuItem::action("Toggle Left Dock", workspace::ToggleLeftDock), + MenuItem::action("Toggle Right Dock", workspace::ToggleRightDock), + MenuItem::action("Toggle Bottom Dock", workspace::ToggleBottomDock), MenuItem::submenu(Menu { name: "Editor Layout", items: vec![ From c9820fde61acd5673dda49c3e4144b8e6bfd1b3b Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 30 May 2023 17:48:41 -0700 Subject: [PATCH 23/53] WIP: Add toast when users attempt to use shift-escape for the first time --- crates/util/src/channel.rs | 2 + crates/workspace/src/notifications.rs | 166 ++++++++++++++------------ crates/workspace/src/pane.rs | 10 +- crates/workspace/src/workspace.rs | 60 +++++++++- crates/zed/src/main.rs | 1 + 5 files changed, 157 insertions(+), 82 deletions(-) diff --git a/crates/util/src/channel.rs b/crates/util/src/channel.rs index 274fd576a0..2b45cb6b66 100644 --- a/crates/util/src/channel.rs +++ b/crates/util/src/channel.rs @@ -2,6 +2,8 @@ use std::env; use lazy_static::lazy_static; +pub struct ZedVersion(pub &'static str); + lazy_static! { pub static ref RELEASE_CHANNEL_NAME: String = if cfg!(debug_assertions) { env::var("ZED_RELEASE_CHANNEL") diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 21b3be09d0..4437a960ef 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -1,5 +1,5 @@ use crate::{Toast, Workspace}; -use collections::HashSet; +use collections::HashMap; use gpui::{AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle}; use std::{any::TypeId, ops::DerefMut}; @@ -34,11 +34,11 @@ impl From<&dyn NotificationHandle> for AnyViewHandle { } struct NotificationTracker { - notifications_sent: HashSet, + notifications_sent: HashMap>, } impl std::ops::Deref for NotificationTracker { - type Target = HashSet; + type Target = HashMap>; fn deref(&self) -> &Self::Target { &self.notifications_sent @@ -54,24 +54,35 @@ impl DerefMut for NotificationTracker { impl NotificationTracker { fn new() -> Self { Self { - notifications_sent: HashSet::default(), + notifications_sent: Default::default(), } } } impl Workspace { + pub fn has_shown_notification_once( + &self, + id: usize, + cx: &ViewContext, + ) -> bool { + cx + .global::() + .get(&TypeId::of::()) + .map(|ids| ids.contains(&id)) + .unwrap_or(false) + } + pub fn show_notification_once( &mut self, id: usize, cx: &mut ViewContext, build_notification: impl FnOnce(&mut ViewContext) -> ViewHandle, ) { - if !cx - .global::() - .contains(&TypeId::of::()) + if !self.has_shown_notification_once::(id, cx) { cx.update_global::(|tracker, _| { - tracker.insert(TypeId::of::()) + let entry = tracker.entry(TypeId::of::()).or_default(); + entry.push(id); }); self.show_notification::(id, cx, build_notification) @@ -247,80 +258,81 @@ pub mod simple_message_notification { let on_click = self.on_click.clone(); let has_click_action = on_click.is_some(); - MouseEventHandler::::new(0, cx, |state, cx| { - Flex::column() - .with_child( - Flex::row() - .with_child( - Text::new(message, theme.message.text.clone()) - .contained() - .with_style(theme.message.container) - .aligned() - .top() - .left() - .flex(1., true), - ) - .with_child( - MouseEventHandler::::new(0, cx, |state, _| { - let style = theme.dismiss_button.style_for(state, false); - Svg::new("icons/x_mark_8.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .contained() - .with_style(style.container) - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - }) - .with_padding(Padding::uniform(5.)) - .on_click(MouseButton::Left, move |_, this, cx| { - this.dismiss(&Default::default(), cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .aligned() - .constrained() - .with_height( - cx.font_cache().line_height(theme.message.text.font_size), - ) + Flex::column() + .with_child( + Flex::row() + .with_child( + Text::new(message, theme.message.text.clone()) + .contained() + .with_style(theme.message.container) .aligned() .top() - .flex_float(), - ), - ) - .with_children({ - let style = theme.action_message.style_for(state, false); - if let Some(click_message) = click_message { - Some( - Flex::row().with_child( - Text::new(click_message, style.text.clone()) + .left() + .flex(1., true), + ) + .with_child( + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme.dismiss_button.style_for(state, false); + Svg::new("icons/x_mark_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .contained() + .with_style(style.container) + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + }) + .with_padding(Padding::uniform(5.)) + .on_click(MouseButton::Left, move |_, this, cx| { + this.dismiss(&Default::default(), cx); + }) + .with_cursor_style(CursorStyle::PointingHand) + .aligned() + .constrained() + .with_height(cx.font_cache().line_height(theme.message.text.font_size)) + .aligned() + .top() + .flex_float(), + ), + ) + .with_children({ + click_message + .map(|click_message| { + MouseEventHandler::::new( + 0, + cx, + |state, _| { + let style = theme.action_message.style_for(state, false); + + Flex::row() + .with_child( + Text::new(click_message, style.text.clone()) + .contained() + .with_style(style.container), + ) .contained() - .with_style(style.container), - ), + }, ) - } else { - None - } + .on_click(MouseButton::Left, move |_, this, cx| { + if let Some(on_click) = on_click.as_ref() { + on_click(cx); + this.dismiss(&Default::default(), cx); + } + }) + // Since we're not using a proper overlay, we have to capture these extra events + .on_down(MouseButton::Left, |_, _, _| {}) + .on_up(MouseButton::Left, |_, _, _| {}) + .with_cursor_style(if has_click_action { + CursorStyle::PointingHand + } else { + CursorStyle::Arrow + }) + }) .into_iter() - }) - .contained() - }) - // Since we're not using a proper overlay, we have to capture these extra events - .on_down(MouseButton::Left, |_, _, _| {}) - .on_up(MouseButton::Left, |_, _, _| {}) - .on_click(MouseButton::Left, move |_, this, cx| { - if let Some(on_click) = on_click.as_ref() { - on_click(cx); - this.dismiss(&Default::default(), cx); - } - }) - .with_cursor_style(if has_click_action { - CursorStyle::PointingHand - } else { - CursorStyle::Arrow - }) - .into_any() + }) + .into_any() } } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 921ae5e010..fad8cd8864 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2,8 +2,8 @@ mod dragged_item_receiver; use super::{ItemHandle, SplitDirection}; use crate::{ - item::WeakItemHandle, toolbar::Toolbar, AutosaveSetting, Item, NewCenterTerminal, NewFile, - NewSearch, ToggleZoom, Workspace, WorkspaceSettings, + item::WeakItemHandle, notify_of_new_dock, toolbar::Toolbar, AutosaveSetting, Item, + NewCenterTerminal, NewFile, NewSearch, ToggleZoom, Workspace, WorkspaceSettings, }; use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; @@ -536,6 +536,12 @@ impl Pane { } pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { + // Potentially warn the user of the new keybinding + let workspace_handle = self.workspace().clone(); + cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) }) + .detach(); + + if self.zoomed { cx.emit(Event::ZoomOut); } else if !self.items.is_empty() { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d051f6d80a..292ec28abc 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -19,7 +19,7 @@ use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, - Client, TypedEnvelope, UserStore, + Client, TypedEnvelope, UserStore, ZED_APP_VERSION, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; @@ -83,7 +83,7 @@ use status_bar::StatusBar; pub use status_bar::StatusItemView; use theme::Theme; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; -use util::{async_iife, paths, ResultExt}; +use util::{async_iife, channel::ZedVersion, paths, ResultExt}; pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings}; lazy_static! { @@ -3190,6 +3190,60 @@ async fn open_items( opened_items } +fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { + const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system"; + const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key"; + + if workspace + .read_with(cx, |workspace, cx| { + let version = cx.global::().0; + if !version.contains("0.88") + && !version.contains("0.89") + && !version.contains("0.90") + && !version.contains("0.91") + && !version.contains("0.92") + { + return true; + } + workspace.has_shown_notification_once::(2, cx) + }) + .unwrap_or(false) + { + return; + } + + if db::kvp::KEY_VALUE_STORE + .read_kvp(NEW_DOCK_HINT_KEY) + .ok() + .flatten() + .is_some() + { + return; + } + + cx.spawn(|_| async move { + db::kvp::KEY_VALUE_STORE + .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string()) + .await + .ok(); + }) + .detach(); + + workspace + .update(cx, |workspace, cx| { + workspace.show_notification_once(2, cx, |cx| { + cx.add_view(|_| { + MessageNotification::new( + "Looking for the dock? Try 'ctrl-`'!\n'shift-escape' now zooms your pane", + ) + .with_click_message("Click to read more about the new panel system") + .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) + }) + }) + }) + .ok(); +} + fn notify_if_database_failed(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml"; @@ -3206,7 +3260,7 @@ fn notify_if_database_failed(workspace: &WeakViewHandle, cx: &mut Asy } else { let backup_path = (*db::BACKUP_DB_PATH).read(); if let Some(backup_path) = backup_path.clone() { - workspace.show_notification_once(0, cx, move |cx| { + workspace.show_notification_once(1, cx, move |cx| { cx.add_view(move |_| { MessageNotification::new(format!( "Database file was corrupted. Old database backed up to {}", diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 31f331ef93..558796807b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -119,6 +119,7 @@ fn main() { app.run(move |cx| { cx.set_global(*RELEASE_CHANNEL); + cx.set_global(util::channel::ZedVersion(env!("CARGO_PKG_VERSION"))); #[cfg(debug_assertions)] cx.set_global(StaffMode(true)); From 8f95435548ee3ef34085992588e4f4107bf1b346 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 30 May 2023 11:05:08 -0700 Subject: [PATCH 24/53] Replicate project-specific settings when collaborating --- .../20221109000000_test_schema.sql | 10 ++ ...0529164700_add_worktree_settings_files.sql | 10 ++ crates/collab/src/db.rs | 101 ++++++++++++++ .../collab/src/db/worktree_settings_file.rs | 19 +++ crates/collab/src/rpc.rs | 50 +++++++ crates/collab/src/tests/integration_tests.rs | 129 ++++++++++++++++++ crates/project/src/project.rs | 105 +++++++++++--- crates/rpc/proto/zed.proto | 9 ++ crates/rpc/src/proto.rs | 2 + crates/settings/src/settings_store.rs | 15 ++ 10 files changed, 432 insertions(+), 18 deletions(-) create mode 100644 crates/collab/migrations/20230529164700_add_worktree_settings_files.sql create mode 100644 crates/collab/src/db/worktree_settings_file.rs diff --git a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql index 7c6a49f179..b0b2a684d9 100644 --- a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql +++ b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql @@ -112,6 +112,16 @@ CREATE INDEX "index_worktree_repository_statuses_on_project_id" ON "worktree_rep CREATE INDEX "index_worktree_repository_statuses_on_project_id_and_worktree_id" ON "worktree_repository_statuses" ("project_id", "worktree_id"); CREATE INDEX "index_worktree_repository_statuses_on_project_id_and_worktree_id_and_work_directory_id" ON "worktree_repository_statuses" ("project_id", "worktree_id", "work_directory_id"); +CREATE TABLE "worktree_settings_files" ( + "project_id" INTEGER NOT NULL, + "worktree_id" INTEGER NOT NULL, + "path" VARCHAR NOT NULL, + "content" TEXT, + PRIMARY KEY(project_id, worktree_id, path), + FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE +); +CREATE INDEX "index_worktree_settings_files_on_project_id" ON "worktree_settings_files" ("project_id"); +CREATE INDEX "index_worktree_settings_files_on_project_id_and_worktree_id" ON "worktree_settings_files" ("project_id", "worktree_id"); CREATE TABLE "worktree_diagnostic_summaries" ( "project_id" INTEGER NOT NULL, diff --git a/crates/collab/migrations/20230529164700_add_worktree_settings_files.sql b/crates/collab/migrations/20230529164700_add_worktree_settings_files.sql new file mode 100644 index 0000000000..973a40af0f --- /dev/null +++ b/crates/collab/migrations/20230529164700_add_worktree_settings_files.sql @@ -0,0 +1,10 @@ +CREATE TABLE "worktree_settings_files" ( + "project_id" INTEGER NOT NULL, + "worktree_id" INT8 NOT NULL, + "path" VARCHAR NOT NULL, + "content" TEXT NOT NULL, + PRIMARY KEY(project_id, worktree_id, path), + FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE +); +CREATE INDEX "index_settings_files_on_project_id" ON "worktree_settings_files" ("project_id"); +CREATE INDEX "index_settings_files_on_project_id_and_wt_id" ON "worktree_settings_files" ("project_id", "worktree_id"); diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index fd28fb9101..1deca1baa8 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -16,6 +16,7 @@ mod worktree_diagnostic_summary; mod worktree_entry; mod worktree_repository; mod worktree_repository_statuses; +mod worktree_settings_file; use crate::executor::Executor; use crate::{Error, Result}; @@ -1494,6 +1495,7 @@ impl Database { updated_repositories: Default::default(), removed_repositories: Default::default(), diagnostic_summaries: Default::default(), + settings_files: Default::default(), scan_id: db_worktree.scan_id as u64, completed_scan_id: db_worktree.completed_scan_id as u64, }; @@ -1638,6 +1640,25 @@ impl Database { }) .collect::>(); + { + let mut db_settings_files = worktree_settings_file::Entity::find() + .filter(worktree_settings_file::Column::ProjectId.eq(project_id)) + .stream(&*tx) + .await?; + while let Some(db_settings_file) = db_settings_files.next().await { + let db_settings_file = db_settings_file?; + if let Some(worktree) = worktrees + .iter_mut() + .find(|w| w.id == db_settings_file.worktree_id as u64) + { + worktree.settings_files.push(WorktreeSettingsFile { + path: db_settings_file.path, + content: db_settings_file.content, + }); + } + } + } + let mut collaborators = project .find_related(project_collaborator::Entity) .all(&*tx) @@ -2637,6 +2658,58 @@ impl Database { .await } + pub async fn update_worktree_settings( + &self, + update: &proto::UpdateWorktreeSettings, + connection: ConnectionId, + ) -> Result>> { + let project_id = ProjectId::from_proto(update.project_id); + let room_id = self.room_id_for_project(project_id).await?; + self.room_transaction(room_id, |tx| async move { + // Ensure the update comes from the host. + let project = project::Entity::find_by_id(project_id) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such project"))?; + if project.host_connection()? != connection { + return Err(anyhow!("can't update a project hosted by someone else"))?; + } + + if let Some(content) = &update.content { + worktree_settings_file::Entity::insert(worktree_settings_file::ActiveModel { + project_id: ActiveValue::Set(project_id), + worktree_id: ActiveValue::Set(update.worktree_id as i64), + path: ActiveValue::Set(update.path.clone()), + content: ActiveValue::Set(content.clone()), + }) + .on_conflict( + OnConflict::columns([ + worktree_settings_file::Column::ProjectId, + worktree_settings_file::Column::WorktreeId, + worktree_settings_file::Column::Path, + ]) + .update_column(worktree_settings_file::Column::Content) + .to_owned(), + ) + .exec(&*tx) + .await?; + } else { + worktree_settings_file::Entity::delete(worktree_settings_file::ActiveModel { + project_id: ActiveValue::Set(project_id), + worktree_id: ActiveValue::Set(update.worktree_id as i64), + path: ActiveValue::Set(update.path.clone()), + ..Default::default() + }) + .exec(&*tx) + .await?; + } + + let connection_ids = self.project_guest_connection_ids(project_id, &tx).await?; + Ok(connection_ids) + }) + .await + } + pub async fn join_project( &self, project_id: ProjectId, @@ -2707,6 +2780,7 @@ impl Database { entries: Default::default(), repository_entries: Default::default(), diagnostic_summaries: Default::default(), + settings_files: Default::default(), scan_id: db_worktree.scan_id as u64, completed_scan_id: db_worktree.completed_scan_id as u64, }, @@ -2819,6 +2893,25 @@ impl Database { } } + // Populate worktree settings files + { + let mut db_settings_files = worktree_settings_file::Entity::find() + .filter(worktree_settings_file::Column::ProjectId.eq(project_id)) + .stream(&*tx) + .await?; + while let Some(db_settings_file) = db_settings_files.next().await { + let db_settings_file = db_settings_file?; + if let Some(worktree) = + worktrees.get_mut(&(db_settings_file.worktree_id as u64)) + { + worktree.settings_files.push(WorktreeSettingsFile { + path: db_settings_file.path, + content: db_settings_file.content, + }); + } + } + } + // Populate language servers. let language_servers = project .find_related(language_server::Entity) @@ -3482,6 +3575,7 @@ pub struct RejoinedWorktree { pub updated_repositories: Vec, pub removed_repositories: Vec, pub diagnostic_summaries: Vec, + pub settings_files: Vec, pub scan_id: u64, pub completed_scan_id: u64, } @@ -3537,10 +3631,17 @@ pub struct Worktree { pub entries: Vec, pub repository_entries: BTreeMap, pub diagnostic_summaries: Vec, + pub settings_files: Vec, pub scan_id: u64, pub completed_scan_id: u64, } +#[derive(Debug)] +pub struct WorktreeSettingsFile { + pub path: String, + pub content: String, +} + #[cfg(test)] pub use test::*; diff --git a/crates/collab/src/db/worktree_settings_file.rs b/crates/collab/src/db/worktree_settings_file.rs new file mode 100644 index 0000000000..f8e87f6e59 --- /dev/null +++ b/crates/collab/src/db/worktree_settings_file.rs @@ -0,0 +1,19 @@ +use super::ProjectId; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "worktree_settings_files")] +pub struct Model { + #[sea_orm(primary_key)] + pub project_id: ProjectId, + #[sea_orm(primary_key)] + pub worktree_id: i64, + #[sea_orm(primary_key)] + pub path: String, + pub content: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 4c117b613d..61c6123a82 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -200,6 +200,7 @@ impl Server { .add_message_handler(start_language_server) .add_message_handler(update_language_server) .add_message_handler(update_diagnostic_summary) + .add_message_handler(update_worktree_settings) .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) @@ -1088,6 +1089,18 @@ async fn rejoin_room( }, )?; } + + for settings_file in worktree.settings_files { + session.peer.send( + session.connection_id, + proto::UpdateWorktreeSettings { + project_id: project.id.to_proto(), + worktree_id: worktree.id, + path: settings_file.path, + content: Some(settings_file.content), + }, + )?; + } } for language_server in &project.language_servers { @@ -1410,6 +1423,18 @@ async fn join_project( }, )?; } + + for settings_file in dbg!(worktree.settings_files) { + session.peer.send( + session.connection_id, + proto::UpdateWorktreeSettings { + project_id: project_id.to_proto(), + worktree_id: worktree.id, + path: settings_file.path, + content: Some(settings_file.content), + }, + )?; + } } for language_server in &project.language_servers { @@ -1525,6 +1550,31 @@ async fn update_diagnostic_summary( Ok(()) } +async fn update_worktree_settings( + message: proto::UpdateWorktreeSettings, + session: Session, +) -> Result<()> { + dbg!(&message); + + let guest_connection_ids = session + .db() + .await + .update_worktree_settings(&message, session.connection_id) + .await?; + + broadcast( + Some(session.connection_id), + guest_connection_ids.iter().copied(), + |connection_id| { + session + .peer + .forward_send(session.connection_id, connection_id, message.clone()) + }, + ); + + Ok(()) +} + async fn start_language_server( request: proto::StartLanguageServer, session: Session, diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index d771f969d8..fb0f63b243 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3114,6 +3114,135 @@ async fn test_fs_operations( }); } +#[gpui::test(iterations = 10)] +async fn test_local_settings( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(&deterministic).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + .await; + let active_call_a = cx_a.read(ActiveCall::global); + + // As client A, open a project that contains some local settings files + client_a + .fs + .insert_tree( + "/dir", + json!({ + ".zed": { + "settings.json": r#"{ "tab_size": 2 }"# + }, + "a": { + ".zed": { + "settings.json": r#"{ "tab_size": 8 }"# + }, + "a.txt": "a-contents", + }, + "b": { + "b.txt": "b-contents", + } + }), + ) + .await; + let (project_a, _) = client_a.build_local_project("/dir", cx_a).await; + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) + .await + .unwrap(); + + // As client B, join that project and observe the local settings. + let project_b = client_b.build_remote_project(project_id, cx_b).await; + let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap()); + deterministic.run_until_parked(); + cx_b.read(|cx| { + let store = cx.global::(); + assert_eq!( + store.local_settings(worktree_b.id()).collect::>(), + &[ + (Path::new("").into(), r#"{"tab_size":2}"#.to_string()), + (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()), + ] + ) + }); + + // As client A, update a settings file. As Client B, see the changed settings. + client_a + .fs + .insert_file("/dir/.zed/settings.json", r#"{}"#.into()) + .await; + deterministic.run_until_parked(); + cx_b.read(|cx| { + let store = cx.global::(); + assert_eq!( + store.local_settings(worktree_b.id()).collect::>(), + &[ + (Path::new("").into(), r#"{}"#.to_string()), + (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()), + ] + ) + }); + + // As client A, create and remove some settings files. As client B, see the changed settings. + client_a + .fs + .remove_file("/dir/.zed/settings.json".as_ref(), Default::default()) + .await + .unwrap(); + client_a + .fs + .create_dir("/dir/b/.zed".as_ref()) + .await + .unwrap(); + client_a + .fs + .insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into()) + .await; + deterministic.run_until_parked(); + cx_b.read(|cx| { + let store = cx.global::(); + assert_eq!( + store.local_settings(worktree_b.id()).collect::>(), + &[ + (Path::new("a").into(), r#"{"tab_size":8}"#.to_string()), + (Path::new("b").into(), r#"{"tab_size":4}"#.to_string()), + ] + ) + }); + + // As client B, disconnect. + server.forbid_connections(); + server.disconnect_client(client_b.peer_id().unwrap()); + + // As client A, change and remove settings files while client B is disconnected. + client_a + .fs + .insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into()) + .await; + client_a + .fs + .remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default()) + .await + .unwrap(); + deterministic.run_until_parked(); + + // As client B, reconnect and see the changed settings. + server.allow_connections(); + deterministic.advance_clock(RECEIVE_TIMEOUT); + cx_b.read(|cx| { + let store = cx.global::(); + assert_eq!( + store.local_settings(worktree_b.id()).collect::>(), + &[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),] + ) + }); +} + #[gpui::test(iterations = 10)] async fn test_buffer_conflict_after_save( deterministic: Arc, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c76587ff56..3cc4a181c5 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -462,6 +462,7 @@ impl Project { client.add_model_request_handler(Self::handle_update_buffer); client.add_model_message_handler(Self::handle_update_diagnostic_summary); client.add_model_message_handler(Self::handle_update_worktree); + client.add_model_message_handler(Self::handle_update_worktree_settings); client.add_model_request_handler(Self::handle_create_project_entry); client.add_model_request_handler(Self::handle_rename_project_entry); client.add_model_request_handler(Self::handle_copy_project_entry); @@ -1105,6 +1106,21 @@ impl Project { .log_err(); } + let store = cx.global::(); + for worktree in self.worktrees(cx) { + let worktree_id = worktree.read(cx).id().to_proto(); + for (path, content) in store.local_settings(worktree.id()) { + self.client + .send(proto::UpdateWorktreeSettings { + project_id, + worktree_id, + path: path.to_string_lossy().into(), + content: Some(content), + }) + .log_err(); + } + } + let (updates_tx, mut updates_rx) = mpsc::unbounded(); let client = self.client.clone(); self.client_state = Some(ProjectClientState::Local { @@ -1217,6 +1233,14 @@ impl Project { message_id: u32, cx: &mut ModelContext, ) -> Result<()> { + cx.update_global::(|store, cx| { + for worktree in &self.worktrees { + store + .clear_local_settings(worktree.handle_id(), cx) + .log_err(); + } + }); + self.join_project_response_message_id = message_id; self.set_worktrees_from_proto(message.worktrees, cx)?; self.set_collaborators_from_proto(message.collaborators, cx)?; @@ -4888,8 +4912,12 @@ impl Project { .push(WorktreeHandle::Weak(worktree.downgrade())); } - cx.observe_release(worktree, |this, worktree, cx| { + let handle_id = worktree.id(); + cx.observe_release(worktree, move |this, worktree, cx| { let _ = this.remove_worktree(worktree.id(), cx); + cx.update_global::(|store, cx| { + store.clear_local_settings(handle_id, cx).log_err() + }); }) .detach(); @@ -5174,14 +5202,16 @@ impl Project { .detach(); } - pub fn update_local_worktree_settings( + fn update_local_worktree_settings( &mut self, worktree: &ModelHandle, changes: &UpdatedEntriesSet, cx: &mut ModelContext, ) { + let project_id = self.remote_id(); let worktree_id = worktree.id(); let worktree = worktree.read(cx).as_local().unwrap(); + let remote_worktree_id = worktree.id(); let mut settings_contents = Vec::new(); for (path, _, change) in changes.iter() { @@ -5195,10 +5225,7 @@ impl Project { let removed = *change == PathChange::Removed; let abs_path = worktree.absolutize(path); settings_contents.push(async move { - anyhow::Ok(( - settings_dir, - (!removed).then_some(fs.load(&abs_path).await?), - )) + (settings_dir, (!removed).then_some(fs.load(&abs_path).await)) }); } } @@ -5207,19 +5234,30 @@ impl Project { return; } + let client = self.client.clone(); cx.spawn_weak(move |_, mut cx| async move { - let settings_contents = futures::future::join_all(settings_contents).await; + let settings_contents: Vec<(Arc, _)> = + futures::future::join_all(settings_contents).await; cx.update(|cx| { cx.update_global::(|store, cx| { - for entry in settings_contents { - if let Some((directory, file_content)) = entry.log_err() { - store - .set_local_settings( - worktree_id, - directory, - file_content.as_ref().map(String::as_str), - cx, - ) + for (directory, file_content) in settings_contents { + let file_content = file_content.and_then(|content| content.log_err()); + store + .set_local_settings( + worktree_id, + directory.clone(), + file_content.as_ref().map(String::as_str), + cx, + ) + .log_err(); + if let Some(remote_id) = project_id { + client + .send(proto::UpdateWorktreeSettings { + project_id: remote_id, + worktree_id: remote_worktree_id.to_proto(), + path: directory.to_string_lossy().into_owned(), + content: file_content, + }) .log_err(); } } @@ -5467,6 +5505,30 @@ impl Project { }) } + async fn handle_update_worktree_settings( + this: ModelHandle, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + this.update(&mut cx, |this, cx| { + let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id); + if let Some(worktree) = this.worktree_for_id(worktree_id, cx) { + cx.update_global::(|store, cx| { + store + .set_local_settings( + worktree.id(), + PathBuf::from(&envelope.payload.path).into(), + envelope.payload.content.as_ref().map(String::as_str), + cx, + ) + .log_err(); + }); + } + Ok(()) + }) + } + async fn handle_create_project_entry( this: ModelHandle, envelope: TypedEnvelope, @@ -6557,8 +6619,8 @@ impl Project { } self.metadata_changed(cx); - for (id, _) in old_worktrees_by_id { - cx.emit(Event::WorktreeRemoved(id)); + for id in old_worktrees_by_id.keys() { + cx.emit(Event::WorktreeRemoved(*id)); } Ok(()) @@ -6928,6 +6990,13 @@ impl WorktreeHandle { WorktreeHandle::Weak(handle) => handle.upgrade(cx), } } + + pub fn handle_id(&self) -> usize { + match self { + WorktreeHandle::Strong(handle) => handle.id(), + WorktreeHandle::Weak(handle) => handle.id(), + } + } } impl OpenBuffer { diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 848cc1c2fa..8351cdfd2d 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -132,6 +132,8 @@ message Envelope { OnTypeFormatting on_type_formatting = 111; OnTypeFormattingResponse on_type_formatting_response = 112; + + UpdateWorktreeSettings update_worktree_settings = 113; } } @@ -339,6 +341,13 @@ message UpdateWorktree { string abs_path = 10; } +message UpdateWorktreeSettings { + uint64 project_id = 1; + uint64 worktree_id = 2; + string path = 3; + optional string content = 4; +} + message CreateProjectEntry { uint64 project_id = 1; uint64 worktree_id = 2; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 07925a0486..13794ea64d 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -236,6 +236,7 @@ messages!( (UpdateProject, Foreground), (UpdateProjectCollaborator, Foreground), (UpdateWorktree, Foreground), + (UpdateWorktreeSettings, Foreground), (UpdateDiffBase, Foreground), (GetPrivateUserInfo, Foreground), (GetPrivateUserInfoResponse, Foreground), @@ -345,6 +346,7 @@ entity_messages!( UpdateProject, UpdateProjectCollaborator, UpdateWorktree, + UpdateWorktreeSettings, UpdateDiffBase ); diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 2560d0b752..bb51fd1ed0 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -359,6 +359,21 @@ impl SettingsStore { Ok(()) } + /// Add or remove a set of local settings via a JSON string. + pub fn clear_local_settings(&mut self, root_id: usize, cx: &AppContext) -> Result<()> { + eprintln!("clearing local settings {root_id}"); + self.local_deserialized_settings + .retain(|k, _| k.0 != root_id); + self.recompute_values(Some((root_id, "".as_ref())), cx)?; + Ok(()) + } + + pub fn local_settings(&self, root_id: usize) -> impl '_ + Iterator, String)> { + self.local_deserialized_settings + .range((root_id, Path::new("").into())..(root_id + 1, Path::new("").into())) + .map(|((_, path), content)| (path.clone(), serde_json::to_string(content).unwrap())) + } + pub fn json_schema( &self, schema_params: &SettingsJsonSchemaParams, From eeba72d7755c6f1f2b4b1c8142b26212583deaec Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 09:03:45 -0700 Subject: [PATCH 25/53] Bump protocol version --- crates/rpc/src/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index b929de9596..bef6efa529 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 56; +pub const PROTOCOL_VERSION: u32 = 57; From 76927b6d95bb8d7baeb38cdb39b59d365776e7c3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 10:29:59 -0700 Subject: [PATCH 26/53] Make active panel's button close its dock --- crates/workspace/src/dock.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 886afe943d..ac52c8f179 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -472,11 +472,22 @@ impl View for PanelButtons { Flex::row() .with_children(panels.into_iter().enumerate().map( |(panel_ix, (view, context_menu))| { - let (tooltip, tooltip_action) = view.icon_tooltip(cx); + let is_active = is_open && panel_ix == active_ix; + let (tooltip, tooltip_action) = if is_active { + ( + format!("Close {} dock", dock_position.to_label()), + Some(match dock_position { + DockPosition::Left => crate::ToggleLeftDock.boxed_clone(), + DockPosition::Bottom => crate::ToggleBottomDock.boxed_clone(), + DockPosition::Right => crate::ToggleRightDock.boxed_clone(), + }), + ) + } else { + view.icon_tooltip(cx) + }; Stack::new() .with_child( MouseEventHandler::::new(panel_ix, cx, |state, cx| { - let is_active = is_open && panel_ix == active_ix; let style = button_style.style_for(state, is_active); Flex::row() .with_child( From ac41564a8f3ac0c575bc29acaf57ede2a7e6753d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 12:34:06 -0700 Subject: [PATCH 27/53] Dismiss zoomed panels by closing their dock, not zooming them out --- crates/workspace/src/dock.rs | 4 ++ crates/workspace/src/workspace.rs | 110 ++++++++++++++++-------------- 2 files changed, 64 insertions(+), 50 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index ac52c8f179..a4b71fa5a7 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -175,6 +175,10 @@ impl Dock { } } + pub fn position(&self) -> DockPosition { + self.position + } + pub fn is_open(&self) -> bool { self.is_open } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d051f6d80a..d721997053 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -908,18 +908,24 @@ impl Workspace { } }); } else if T::should_zoom_in_on_event(event) { - this.zoom_out(cx); dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx)); if panel.has_focus(cx) { this.zoomed = Some(panel.downgrade().into_any()); this.zoomed_position = Some(panel.read(cx).position(cx)); } } else if T::should_zoom_out_on_event(event) { - this.zoom_out(cx); + dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx)); + if this.zoomed_position == Some(prev_position) { + this.zoomed = None; + this.zoomed_position = None; + } + cx.notify(); } else if T::is_focus_event(event) { + let position = panel.read(cx).position(cx); + this.dismiss_zoomed_items_to_reveal(Some(position), cx); if panel.is_zoomed(cx) { this.zoomed = Some(panel.downgrade().into_any()); - this.zoomed_position = Some(panel.read(cx).position(cx)); + this.zoomed_position = Some(position); } else { this.zoomed = None; this.zoomed_position = None; @@ -1592,7 +1598,7 @@ impl Workspace { DockPosition::Right => &self.right_dock, }; let mut focus_center = false; - let mut zoom_out = false; + let mut reveal_dock = false; dock.update(cx, |dock, cx| { let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side); let was_visible = dock.is_open() && !other_is_zoomed; @@ -1607,14 +1613,15 @@ impl Workspace { if active_panel.is_zoomed(cx) { cx.focus(active_panel.as_any()); } - zoom_out = true; + reveal_dock = true; } } }); - if zoom_out { - self.zoom_out_everything_except(dock_side, cx); + if reveal_dock { + self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx); } + if focus_center { cx.focus_self(); } @@ -1623,62 +1630,49 @@ impl Workspace { self.serialize_workspace(cx); } + /// Transfer focus to the panel of the given type. pub fn focus_panel(&mut self, cx: &mut ViewContext) -> Option> { - self.show_or_hide_panel::(cx, |_, _| true)? + self.focus_or_unfocus_panel::(cx, |_, _| true)? .as_any() .clone() .downcast() } + /// Focus the panel of the given type if it isn't already focused. If it is + /// already focused, then transfer focus back to the workspace center. pub fn toggle_panel_focus(&mut self, cx: &mut ViewContext) { - self.show_or_hide_panel::(cx, |panel, cx| !panel.has_focus(cx)); + self.focus_or_unfocus_panel::(cx, |panel, cx| !panel.has_focus(cx)); } - fn show_or_hide_panel( + /// Focus or unfocus the given panel type, depending on the given callback. + fn focus_or_unfocus_panel( &mut self, cx: &mut ViewContext, - show: impl Fn(&dyn PanelHandle, &mut ViewContext) -> bool, + should_focus: impl Fn(&dyn PanelHandle, &mut ViewContext) -> bool, ) -> Option> { - for (dock, position) in [ - self.left_dock.clone(), - self.bottom_dock.clone(), - self.right_dock.clone(), - ] - .into_iter() - .zip( - [ - DockPosition::Left, - DockPosition::Bottom, - DockPosition::Right, - ] - .into_iter(), - ) { + for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { if let Some(panel_index) = dock.read(cx).panel_index_for_type::() { let mut focus_center = false; - let mut zoom_out = false; + let mut reveal_dock = false; let panel = dock.update(cx, |dock, cx| { dock.activate_panel(panel_index, cx); let panel = dock.active_panel().cloned(); if let Some(panel) = panel.as_ref() { - let should_show = show(&**panel, cx); - if should_show { + if should_focus(&**panel, cx) { dock.set_open(true, cx); cx.focus(panel.as_any()); - zoom_out = true; + reveal_dock = true; } else { - if panel.is_zoomed(cx) { - dock.set_open(false, cx); - } + // if panel.is_zoomed(cx) { + // dock.set_open(false, cx); + // } focus_center = true; } } panel }); - if zoom_out { - self.zoom_out_everything_except(position, cx); - } if focus_center { cx.focus_self(); } @@ -1705,28 +1699,38 @@ impl Workspace { cx.notify(); } - fn zoom_out_everything_except( + fn dismiss_zoomed_items_to_reveal( &mut self, - except_position: DockPosition, + except_position: Option, cx: &mut ViewContext, ) { - for pane in &self.panes { - pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + // If a center pane is zoomed, unzoom it. + if except_position.is_some() { + for pane in &self.panes { + pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + } } - if except_position != DockPosition::Left { - self.left_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + // If another dock is zoomed, hide it. + let mut focus_center = false; + for dock in [&self.left_dock, &self.right_dock, &self.bottom_dock] { + dock.update(cx, |dock, cx| { + if Some(dock.position()) != except_position { + if let Some(panel) = dock.active_panel() { + if panel.is_zoomed(cx) { + focus_center |= panel.has_focus(cx); + dock.set_open(false, cx); + } + } + } + }); } - if except_position != DockPosition::Bottom { - self.bottom_dock.update(cx, |dock, cx| dock.zoom_out(cx)); + if focus_center { + cx.focus_self(); } - if except_position != DockPosition::Right { - self.right_dock.update(cx, |dock, cx| dock.zoom_out(cx)); - } - - if self.zoomed_position != Some(except_position) { + if self.zoomed_position != except_position { self.zoomed = None; self.zoomed_position = None; } @@ -1937,6 +1941,7 @@ impl Workspace { self.last_active_center_pane = Some(pane.downgrade()); } + self.dismiss_zoomed_items_to_reveal(None, cx); if pane.read(cx).is_zoomed() { self.zoomed = Some(pane.downgrade().into_any()); } else { @@ -1998,7 +2003,6 @@ impl Workspace { } pane::Event::ZoomIn => { if pane == self.active_pane { - self.zoom_out(cx); pane.update(cx, |pane, cx| pane.set_zoomed(true, cx)); if pane.read(cx).has_focus() { self.zoomed = Some(pane.downgrade().into_any()); @@ -2007,7 +2011,13 @@ impl Workspace { cx.notify(); } } - pane::Event::ZoomOut => self.zoom_out(cx), + pane::Event::ZoomOut => { + pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + if self.zoomed_position.is_none() { + self.zoomed = None; + } + cx.notify(); + } } self.serialize_workspace(cx); From a10933c063307fb768bd122a86f1ff359c0e93fe Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 31 May 2023 12:59:04 -0400 Subject: [PATCH 28/53] Update pane/panel zoom styling --- styles/src/styleTree/workspace.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index cf5234aa00..ec992c9c0b 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -13,6 +13,7 @@ import tabBar from "./tabBar" export default function workspace(colorScheme: ColorScheme) { const layer = colorScheme.lowest + const isLight = colorScheme.isLight const itemSpacing = 8 const titlebarButton = { cornerRadius: 6, @@ -120,16 +121,18 @@ export default function workspace(colorScheme: ColorScheme) { }, zoomedBackground: { cursor: "Arrow", - background: withOpacity(background(colorScheme.lowest), 0.85) + background: isLight + ? withOpacity(background(colorScheme.lowest), 0.8) + : withOpacity(background(colorScheme.highest), 0.6) }, zoomedPaneForeground: { - margin: 10, + margin: 16, shadow: colorScheme.modalShadow, - border: border(colorScheme.highest, { overlay: true }), + border: border(colorScheme.lowest, { overlay: true }), }, zoomedPanelForeground: { - margin: 18, - border: border(colorScheme.highest, { overlay: true }), + margin: 16, + border: border(colorScheme.lowest, { overlay: true }), }, dock: { left: { From 1f72f9e18babcd8906d1d550ef4691831b3accaf Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 31 May 2023 12:59:28 -0400 Subject: [PATCH 29/53] Add un-hooked up zoomIcon styles --- styles/src/styleTree/tabBar.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/styles/src/styleTree/tabBar.ts b/styles/src/styleTree/tabBar.ts index 61d30be7e0..ef1425cd15 100644 --- a/styles/src/styleTree/tabBar.ts +++ b/styles/src/styleTree/tabBar.ts @@ -31,6 +31,10 @@ export default function tabBar(colorScheme: ColorScheme) { iconClose: foreground(layer, "variant"), iconCloseActive: foreground(layer, "hovered"), + // Zoom Icons + iconZoom: foreground(layer, "variant"), + iconZoomActive: foreground(layer, "accent"), + // Indicators iconConflict: foreground(layer, "warning"), iconDirty: foreground(layer, "accent"), From 893615236dd721a235bf8afc3fcb446f3dcd5721 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 12:45:48 -0700 Subject: [PATCH 30/53] Only include one border edge for zoomed panels --- crates/workspace/src/workspace.rs | 56 +++++++++++++++++-------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d721997053..1b2fdc80f4 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3288,32 +3288,36 @@ impl View for Workspace { enum ZoomBackground {} let zoomed = zoomed.upgrade(cx)?; - let mut foreground_style; - match self.zoomed_position { - Some(DockPosition::Left) => { - foreground_style = - theme.workspace.zoomed_panel_foreground; - foreground_style.margin.left = 0.; - foreground_style.margin.top = 0.; - foreground_style.margin.bottom = 0.; - } - Some(DockPosition::Right) => { - foreground_style = - theme.workspace.zoomed_panel_foreground; - foreground_style.margin.right = 0.; - foreground_style.margin.top = 0.; - foreground_style.margin.bottom = 0.; - } - Some(DockPosition::Bottom) => { - foreground_style = - theme.workspace.zoomed_panel_foreground; - foreground_style.margin.left = 0.; - foreground_style.margin.right = 0.; - foreground_style.margin.bottom = 0.; - } - None => { - foreground_style = - theme.workspace.zoomed_pane_foreground; + let mut foreground_style = + theme.workspace.zoomed_pane_foreground; + if let Some(zoomed_dock_position) = self.zoomed_position { + foreground_style = + theme.workspace.zoomed_panel_foreground; + let margin = foreground_style.margin.top; + let border = foreground_style.border.top; + + // Only include a margin and border on the opposite side. + foreground_style.margin.top = 0.; + foreground_style.margin.left = 0.; + foreground_style.margin.bottom = 0.; + foreground_style.margin.right = 0.; + foreground_style.border.top = false; + foreground_style.border.left = false; + foreground_style.border.bottom = false; + foreground_style.border.right = false; + match zoomed_dock_position { + DockPosition::Left => { + foreground_style.margin.right = margin; + foreground_style.border.right = border; + } + DockPosition::Right => { + foreground_style.margin.left = margin; + foreground_style.border.left = border; + } + DockPosition::Bottom => { + foreground_style.margin.top = margin; + foreground_style.border.top = border; + } } } From 3f6aa94a5f4f3c49457f7c4fb96ac3a46d34f8e3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 12:51:57 -0700 Subject: [PATCH 31/53] Use active color for zoom button in a zoomed pane --- crates/terminal_view/src/terminal_panel.rs | 2 ++ crates/workspace/src/pane.rs | 6 +++++- styles/src/styleTree/tabBar.ts | 7 +++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index e45459e683..ac3875af9e 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -70,6 +70,7 @@ impl TerminalPanel { .with_child(Pane::render_tab_bar_button( 0, "icons/plus_12.svg", + false, Some(( "New Terminal".into(), Some(Box::new(workspace::NewTerminal)), @@ -94,6 +95,7 @@ impl TerminalPanel { } else { "icons/maximize_8.svg" }, + pane.is_zoomed(), Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))), cx, move |pane, cx| pane.toggle_zoom(&Default::default(), cx), diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 921ae5e010..80a0256389 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -268,6 +268,7 @@ impl Pane { .with_child(Self::render_tab_bar_button( 0, "icons/plus_12.svg", + false, Some(("New...".into(), None)), cx, |pane, cx| pane.deploy_new_menu(cx), @@ -277,6 +278,7 @@ impl Pane { .with_child(Self::render_tab_bar_button( 1, "icons/split_12.svg", + false, Some(("Split Pane".into(), None)), cx, |pane, cx| pane.deploy_split_menu(cx), @@ -290,6 +292,7 @@ impl Pane { } else { "icons/maximize_8.svg" }, + pane.is_zoomed(), Some(("Toggle Zoom".into(), Some(Box::new(ToggleZoom)))), cx, move |pane, cx| pane.toggle_zoom(&Default::default(), cx), @@ -1401,6 +1404,7 @@ impl Pane { pub fn render_tab_bar_button)>( index: usize, icon: &'static str, + active: bool, tooltip: Option<(String, Option>)>, cx: &mut ViewContext, on_click: F, @@ -1410,7 +1414,7 @@ impl Pane { let mut button = MouseEventHandler::::new(index, cx, |mouse_state, cx| { let theme = &settings::get::(cx).theme.workspace.tab_bar; - let style = theme.pane_button.style_for(mouse_state, false); + let style = theme.pane_button.style_for(mouse_state, active); Svg::new(icon) .with_color(style.color) .constrained() diff --git a/styles/src/styleTree/tabBar.ts b/styles/src/styleTree/tabBar.ts index ef1425cd15..39a1ef0407 100644 --- a/styles/src/styleTree/tabBar.ts +++ b/styles/src/styleTree/tabBar.ts @@ -31,10 +31,6 @@ export default function tabBar(colorScheme: ColorScheme) { iconClose: foreground(layer, "variant"), iconCloseActive: foreground(layer, "hovered"), - // Zoom Icons - iconZoom: foreground(layer, "variant"), - iconZoomActive: foreground(layer, "accent"), - // Indicators iconConflict: foreground(layer, "warning"), iconDirty: foreground(layer, "accent"), @@ -98,6 +94,9 @@ export default function tabBar(colorScheme: ColorScheme) { hover: { color: foreground(layer, "hovered"), }, + active: { + color: foreground(layer, "accent"), + } }, paneButtonContainer: { background: tab.background, From c48fed26bdebdcb7573e7a06b1ac4fcd0068dc9e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 13:05:50 -0700 Subject: [PATCH 32/53] When revealing items, zoom-out any inactive panes --- crates/workspace/src/workspace.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 1b2fdc80f4..3d55a78e88 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1705,8 +1705,8 @@ impl Workspace { cx: &mut ViewContext, ) { // If a center pane is zoomed, unzoom it. - if except_position.is_some() { - for pane in &self.panes { + for pane in &self.panes { + if pane != &self.active_pane { pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); } } From 193474a346100b4496bc074b3a1efcf5c7cf611a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 13:07:20 -0700 Subject: [PATCH 33/53] :art: --- crates/workspace/src/workspace.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 3d55a78e88..b00b8f1102 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1701,7 +1701,7 @@ impl Workspace { fn dismiss_zoomed_items_to_reveal( &mut self, - except_position: Option, + dock_to_reveal: Option, cx: &mut ViewContext, ) { // If a center pane is zoomed, unzoom it. @@ -1715,7 +1715,7 @@ impl Workspace { let mut focus_center = false; for dock in [&self.left_dock, &self.right_dock, &self.bottom_dock] { dock.update(cx, |dock, cx| { - if Some(dock.position()) != except_position { + if Some(dock.position()) != dock_to_reveal { if let Some(panel) = dock.active_panel() { if panel.is_zoomed(cx) { focus_center |= panel.has_focus(cx); @@ -1730,7 +1730,7 @@ impl Workspace { cx.focus_self(); } - if self.zoomed_position != except_position { + if self.zoomed_position != dock_to_reveal { self.zoomed = None; self.zoomed_position = None; } From ef80b539d150d67faa0dc821077364975eafea36 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 13:56:41 -0700 Subject: [PATCH 34/53] Fix notification styling, minimize database reads --- crates/workspace/src/notifications.rs | 38 ++++++++++++------ crates/workspace/src/workspace.rs | 57 +++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 4437a960ef..1060227d4a 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -33,7 +33,7 @@ impl From<&dyn NotificationHandle> for AnyViewHandle { } } -struct NotificationTracker { +pub(crate) struct NotificationTracker { notifications_sent: HashMap>, } @@ -65,8 +65,7 @@ impl Workspace { id: usize, cx: &ViewContext, ) -> bool { - cx - .global::() + cx.global::() .get(&TypeId::of::()) .map(|ids| ids.contains(&id)) .unwrap_or(false) @@ -78,8 +77,7 @@ impl Workspace { cx: &mut ViewContext, build_notification: impl FnOnce(&mut ViewContext) -> ViewHandle, ) { - if !self.has_shown_notification_once::(id, cx) - { + if !self.has_shown_notification_once::(id, cx) { cx.update_global::(|tracker, _| { let entry = tracker.entry(TypeId::of::()).or_default(); entry.push(id); @@ -167,7 +165,7 @@ pub mod simple_message_notification { elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text}, impl_actions, platform::{CursorStyle, MouseButton}, - AppContext, Element, Entity, View, ViewContext, + AnyElement, AppContext, Element, Entity, View, ViewContext, fonts::TextStyle, }; use menu::Cancel; use serde::Deserialize; @@ -195,8 +193,13 @@ pub mod simple_message_notification { ) } + enum NotificationMessage { + Text(Cow<'static, str>), + Element(fn(TextStyle, &AppContext) -> AnyElement), + } + pub struct MessageNotification { - message: Cow<'static, str>, + message: NotificationMessage, on_click: Option)>>, click_message: Option>, } @@ -215,7 +218,16 @@ pub mod simple_message_notification { S: Into>, { Self { - message: message.into(), + message: NotificationMessage::Text(message.into()), + on_click: None, + click_message: None, + } + } + + pub fn new_element(message: fn(TextStyle, &AppContext) -> AnyElement) -> MessageNotification + { + Self { + message: NotificationMessage::Element(message), on_click: None, click_message: None, } @@ -254,7 +266,12 @@ pub mod simple_message_notification { enum MessageNotificationTag {} let click_message = self.click_message.clone(); - let message = self.message.clone(); + let message = match &self.message { + NotificationMessage::Text(text) => { + Text::new(text.to_owned(), theme.message.text.clone()).into_any() + } + NotificationMessage::Element(e) => e(theme.message.text.clone(), cx), + }; let on_click = self.on_click.clone(); let has_click_action = on_click.is_some(); @@ -262,8 +279,7 @@ pub mod simple_message_notification { .with_child( Flex::row() .with_child( - Text::new(message, theme.message.text.clone()) - .contained() + message.contained() .with_style(theme.message.container) .aligned() .top() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 292ec28abc..823c9c9294 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -19,7 +19,7 @@ use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, - Client, TypedEnvelope, UserStore, ZED_APP_VERSION, + Client, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; @@ -60,7 +60,7 @@ use std::{ }; use crate::{ - notifications::simple_message_notification::MessageNotification, + notifications::{simple_message_notification::MessageNotification, NotificationTracker}, persistence::model::{ DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace, }, @@ -81,7 +81,7 @@ use serde::Deserialize; use shared_screen::SharedScreen; use status_bar::StatusBar; pub use status_bar::StatusItemView; -use theme::Theme; +use theme::{Theme, ThemeSettings}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; use util::{async_iife, channel::ZedVersion, paths, ResultExt}; pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings}; @@ -3193,6 +3193,7 @@ async fn open_items( fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system"; const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key"; + const MESSAGE_ID: usize = 2; if workspace .read_with(cx, |workspace, cx| { @@ -3205,7 +3206,7 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo { return true; } - workspace.has_shown_notification_once::(2, cx) + workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) .unwrap_or(false) { @@ -3218,6 +3219,24 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo .flatten() .is_some() { + if !workspace + .read_with(cx, |workspace, cx| { + workspace.has_shown_notification_once::(MESSAGE_ID, cx) + }) + .unwrap_or(false) + { + cx.update(|cx| { + cx.update_global::(|tracker, _| { + let entry = tracker + .entry(TypeId::of::()) + .or_default(); + if !entry.contains(&MESSAGE_ID) { + entry.push(MESSAGE_ID); + } + }); + }); + } + return; } @@ -3232,11 +3251,33 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo workspace .update(cx, |workspace, cx| { workspace.show_notification_once(2, cx, |cx| { + + cx.add_view(|_| { - MessageNotification::new( - "Looking for the dock? Try 'ctrl-`'!\n'shift-escape' now zooms your pane", - ) - .with_click_message("Click to read more about the new panel system") + MessageNotification::new_element(|text, _| { + Text::new( + "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.", + text, + ) + .with_custom_runs(vec![26..32, 34..46], |_, bounds, scene, cx| { + let code_span_background_color = settings::get::(cx) + .theme + .editor + .document_highlight_read_background; + + scene.push_quad(gpui::Quad { + bounds, + background: Some(code_span_background_color), + border: Default::default(), + corner_radius: 2.0, + }) + }) + .into_any() + }) + // MessageNotification::new_( + // "Looking for the dock? Try 'ctrl-`'!\n'shift-escape' now zooms your pane", + // ) + .with_click_message("Read more about the new panel system") .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) }) }) From ed0b9acb0a5d16b363b42efbf7f1e2b30a2b95e7 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:06:36 -0700 Subject: [PATCH 35/53] Add panic if version is 0.91 --- crates/workspace/src/notifications.rs | 11 +++++++---- crates/workspace/src/pane.rs | 1 - crates/workspace/src/workspace.rs | 14 ++------------ 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 1060227d4a..1e3c6044a1 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -163,9 +163,10 @@ pub mod simple_message_notification { use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text}, + fonts::TextStyle, impl_actions, platform::{CursorStyle, MouseButton}, - AnyElement, AppContext, Element, Entity, View, ViewContext, fonts::TextStyle, + AnyElement, AppContext, Element, Entity, View, ViewContext, }; use menu::Cancel; use serde::Deserialize; @@ -224,8 +225,9 @@ pub mod simple_message_notification { } } - pub fn new_element(message: fn(TextStyle, &AppContext) -> AnyElement) -> MessageNotification - { + pub fn new_element( + message: fn(TextStyle, &AppContext) -> AnyElement, + ) -> MessageNotification { Self { message: NotificationMessage::Element(message), on_click: None, @@ -279,7 +281,8 @@ pub mod simple_message_notification { .with_child( Flex::row() .with_child( - message.contained() + message + .contained() .with_style(theme.message.container) .aligned() .top() diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index fad8cd8864..63602ffead 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -541,7 +541,6 @@ impl Pane { cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) }) .detach(); - if self.zoomed { cx.emit(Event::ZoomOut); } else if !self.items.is_empty() { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 823c9c9294..3690db60d0 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3198,13 +3198,8 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo if workspace .read_with(cx, |workspace, cx| { let version = cx.global::().0; - if !version.contains("0.88") - && !version.contains("0.89") - && !version.contains("0.90") - && !version.contains("0.91") - && !version.contains("0.92") - { - return true; + if version.contains("0.91") { + panic!("Please remove the dock key binding change notification"); } workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) @@ -3251,8 +3246,6 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo workspace .update(cx, |workspace, cx| { workspace.show_notification_once(2, cx, |cx| { - - cx.add_view(|_| { MessageNotification::new_element(|text, _| { Text::new( @@ -3274,9 +3267,6 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo }) .into_any() }) - // MessageNotification::new_( - // "Looking for the dock? Try 'ctrl-`'!\n'shift-escape' now zooms your pane", - // ) .with_click_message("Read more about the new panel system") .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) }) From b875d4ed40eac05bf6007a6d9f08f64ca65c446f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:10:50 -0700 Subject: [PATCH 36/53] Remove silly panic --- crates/workspace/src/workspace.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 3690db60d0..73cc811e76 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3197,10 +3197,6 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo if workspace .read_with(cx, |workspace, cx| { - let version = cx.global::().0; - if version.contains("0.91") { - panic!("Please remove the dock key binding change notification"); - } workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) .unwrap_or(false) From 705e36827c8e48dd0b2ccfb969806e41148ec935 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:16:37 -0700 Subject: [PATCH 37/53] add version check --- crates/util/src/channel.rs | 2 -- crates/workspace/src/workspace.rs | 12 ++++++++++-- crates/zed/src/main.rs | 1 - 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/util/src/channel.rs b/crates/util/src/channel.rs index 2b45cb6b66..274fd576a0 100644 --- a/crates/util/src/channel.rs +++ b/crates/util/src/channel.rs @@ -2,8 +2,6 @@ use std::env; use lazy_static::lazy_static; -pub struct ZedVersion(pub &'static str); - lazy_static! { pub static ref RELEASE_CHANNEL_NAME: String = if cfg!(debug_assertions) { env::var("ZED_RELEASE_CHANNEL") diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 73cc811e76..7033530f96 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -19,7 +19,7 @@ use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, - Client, TypedEnvelope, UserStore, + Client, TypedEnvelope, UserStore, ZED_APP_VERSION, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; @@ -83,7 +83,7 @@ use status_bar::StatusBar; pub use status_bar::StatusItemView; use theme::{Theme, ThemeSettings}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; -use util::{async_iife, channel::ZedVersion, paths, ResultExt}; +use util::{async_iife, paths, ResultExt}; pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings}; lazy_static! { @@ -3197,6 +3197,14 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo if workspace .read_with(cx, |workspace, cx| { + let version = ZED_APP_VERSION + .or_else(|| cx.platform().app_version().ok()) + .map(|v| v.to_string()).unwrap_or_default(); + + if !version.contains("0.88") || !version.contains("0.89") || !version.contains("0.90") || !version.contains("0.91") { + return true; + } + workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) .unwrap_or(false) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 558796807b..31f331ef93 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -119,7 +119,6 @@ fn main() { app.run(move |cx| { cx.set_global(*RELEASE_CHANNEL); - cx.set_global(util::channel::ZedVersion(env!("CARGO_PKG_VERSION"))); #[cfg(debug_assertions)] cx.set_global(StaffMode(true)); From 45b42c512d50b96f49c9722793572a0b484b2305 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:17:27 -0700 Subject: [PATCH 38/53] fmt --- crates/workspace/src/workspace.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 7033530f96..c45cdf0c29 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3199,9 +3199,14 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo .read_with(cx, |workspace, cx| { let version = ZED_APP_VERSION .or_else(|| cx.platform().app_version().ok()) - .map(|v| v.to_string()).unwrap_or_default(); + .map(|v| v.to_string()) + .unwrap_or_default(); - if !version.contains("0.88") || !version.contains("0.89") || !version.contains("0.90") || !version.contains("0.91") { + if !version.contains("0.88") + || !version.contains("0.89") + || !version.contains("0.90") + || !version.contains("0.91") + { return true; } From 27ef0e2b524755515eab87d37898f6759f15e2d3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:21:38 -0700 Subject: [PATCH 39/53] De-morgans properly --- crates/workspace/src/workspace.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index c45cdf0c29..5d1bc2993d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3203,9 +3203,9 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo .unwrap_or_default(); if !version.contains("0.88") - || !version.contains("0.89") - || !version.contains("0.90") - || !version.contains("0.91") + && !version.contains("0.89") + && !version.contains("0.90") + && !version.contains("0.91") { return true; } From bf2016adf504637b168b3fca76d0e9fb955ea293 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 31 May 2023 14:22:49 -0700 Subject: [PATCH 40/53] Remove version check --- crates/workspace/src/workspace.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 5d1bc2993d..91824f5855 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -19,7 +19,7 @@ use assets::Assets; use call::ActiveCall; use client::{ proto::{self, PeerId}, - Client, TypedEnvelope, UserStore, ZED_APP_VERSION, + Client, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; @@ -3197,19 +3197,6 @@ fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppCo if workspace .read_with(cx, |workspace, cx| { - let version = ZED_APP_VERSION - .or_else(|| cx.platform().app_version().ok()) - .map(|v| v.to_string()) - .unwrap_or_default(); - - if !version.contains("0.88") - && !version.contains("0.89") - && !version.contains("0.90") - && !version.contains("0.91") - { - return true; - } - workspace.has_shown_notification_once::(MESSAGE_ID, cx) }) .unwrap_or(false) From a2ab7c9eb95f4970b0c2222e4494c83e2cb2779b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 14:42:15 -0700 Subject: [PATCH 41/53] Respect project-specific settings for copilot --- crates/copilot/src/copilot.rs | 2 +- crates/copilot_button/src/copilot_button.rs | 32 +++++++++++-------- crates/editor/src/editor.rs | 15 +++++---- crates/language/src/language_settings.rs | 10 ++++-- crates/project/src/project.rs | 35 +++++++++++---------- crates/project/src/worktree.rs | 2 +- 6 files changed, 56 insertions(+), 40 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 88b0aebd17..1084222648 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -318,7 +318,7 @@ impl Copilot { fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext) { let http = self.http.clone(); let node_runtime = self.node_runtime.clone(); - if all_language_settings(cx).copilot_enabled(None, None) { + if all_language_settings(None, cx).copilot_enabled(None, None) { if matches!(self.server, CopilotServer::Disabled) { let start_task = cx .spawn({ diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 686b3d2dfb..4cb7c15bd9 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -9,7 +9,10 @@ use gpui::{ AnyElement, AppContext, AsyncAppContext, Element, Entity, MouseState, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; -use language::language_settings::{self, all_language_settings, AllLanguageSettings}; +use language::{ + language_settings::{self, all_language_settings, AllLanguageSettings}, + File, +}; use settings::{update_settings_file, SettingsStore}; use std::{path::Path, sync::Arc}; use util::{paths, ResultExt}; @@ -27,7 +30,7 @@ pub struct CopilotButton { editor_subscription: Option<(Subscription, usize)>, editor_enabled: Option, language: Option>, - path: Option>, + file: Option>, fs: Arc, } @@ -41,7 +44,7 @@ impl View for CopilotButton { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let all_language_settings = &all_language_settings(cx); + let all_language_settings = all_language_settings(None, cx); if !all_language_settings.copilot.feature_enabled { return Empty::new().into_any(); } @@ -165,7 +168,7 @@ impl CopilotButton { editor_subscription: None, editor_enabled: None, language: None, - path: None, + file: None, fs, } } @@ -212,9 +215,9 @@ impl CopilotButton { let settings = settings::get::(cx); - if let Some(path) = self.path.as_ref() { - let path_enabled = settings.copilot_enabled_for_path(path); - let path = path.clone(); + if let Some(file) = &self.file { + let path = file.path().clone(); + let path_enabled = settings.copilot_enabled_for_path(&path); menu_options.push(ContextMenuItem::handler( format!( "{} Suggestions for This Path", @@ -279,14 +282,16 @@ impl CopilotButton { let language_name = snapshot .language_at(suggestion_anchor) .map(|language| language.name()); - let path = snapshot.file_at(suggestion_anchor).map(|file| file.path()); + let file = snapshot.file_at(suggestion_anchor).cloned(); self.editor_enabled = Some( - all_language_settings(cx) - .copilot_enabled(language_name.as_deref(), path.map(|p| p.as_ref())), + all_language_settings(self.file.as_ref().map(|f| f.as_ref()), cx).copilot_enabled( + language_name.as_deref(), + file.as_ref().map(|file| file.path().as_ref()), + ), ); self.language = language_name; - self.path = path.cloned(); + self.file = file; cx.notify() } @@ -363,14 +368,15 @@ async fn configure_disabled_globs( } fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { - let show_copilot_suggestions = all_language_settings(cx).copilot_enabled(None, None); + let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None); update_settings_file::(fs, cx, move |file| { file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into()) }); } fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { - let show_copilot_suggestions = all_language_settings(cx).copilot_enabled(Some(&language), None); + let show_copilot_suggestions = + all_language_settings(None, cx).copilot_enabled(Some(&language), None); update_settings_file::(fs, cx, move |file| { file.languages .entry(language) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f5d109e15b..4a9237f563 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3207,12 +3207,12 @@ impl Editor { snapshot: &MultiBufferSnapshot, cx: &mut ViewContext, ) -> bool { - let path = snapshot.file_at(location).map(|file| file.path().as_ref()); + let file = snapshot.file_at(location); let language_name = snapshot .language_at(location) .map(|language| language.name()); - let settings = all_language_settings(cx); - settings.copilot_enabled(language_name.as_deref(), path) + let settings = all_language_settings(file.map(|f| f.as_ref() as _), cx); + settings.copilot_enabled(language_name.as_deref(), file.map(|f| f.path().as_ref())) } fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool { @@ -7076,11 +7076,13 @@ impl Editor { }; // If None, we are in a file without an extension - let file_extension = file_extension.or(self + let file = self .buffer .read(cx) .as_singleton() - .and_then(|b| b.read(cx).file()) + .and_then(|b| b.read(cx).file()); + let file_extension = file_extension.or(file + .as_ref() .and_then(|file| Path::new(file.file_name(cx)).extension()) .and_then(|e| e.to_str()) .map(|a| a.to_string())); @@ -7091,7 +7093,8 @@ impl Editor { .get("vim_mode") == Some(&serde_json::Value::Bool(true)); let telemetry_settings = *settings::get::(cx); - let copilot_enabled = all_language_settings(cx).copilot_enabled(None, None); + let copilot_enabled = + all_language_settings(file.map(|f| f.as_ref()), cx).copilot_enabled(None, None); let copilot_enabled_for_language = self .buffer .read(cx) diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 70aaca03c4..e4e283bd7e 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -26,8 +26,14 @@ pub fn language_settings<'a>( .language(language) } -pub fn all_language_settings<'a>(cx: &'a AppContext) -> &'a AllLanguageSettings { - settings::get::(cx) +pub fn all_language_settings<'a>( + file: Option<&dyn File>, + cx: &'a AppContext, +) -> &'a AllLanguageSettings { + settings::get_local::( + file.map(|f| (f.worktree_id(), f.path().as_ref())), + cx, + ) } #[derive(Debug, Clone)] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 3cc4a181c5..c871bf0b96 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -28,7 +28,7 @@ use gpui::{ ModelHandle, Task, WeakModelHandle, }; use language::{ - language_settings::{all_language_settings, language_settings, FormatOnSave, Formatter}, + language_settings::{language_settings, FormatOnSave, Formatter}, point_to_lsp, proto::{ deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version, @@ -689,18 +689,15 @@ impl Project { } fn on_settings_changed(&mut self, cx: &mut ModelContext) { - let settings = all_language_settings(cx); - let mut language_servers_to_start = Vec::new(); for buffer in self.opened_buffers.values() { if let Some(buffer) = buffer.upgrade(cx) { let buffer = buffer.read(cx); if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) { - if settings - .language(Some(&language.name())) - .enable_language_server - { + let settings = + language_settings(Some(language.name().as_ref()), Some(file), cx); + if settings.enable_language_server { language_servers_to_start.push((file.worktree.clone(), language.clone())); } } @@ -708,18 +705,22 @@ impl Project { } let mut language_servers_to_stop = Vec::new(); - for language in self.languages.to_vec() { - for lsp_adapter in language.lsp_adapters() { - if !settings - .language(Some(&language.name())) + let languages = self.languages.to_vec(); + for (worktree_id, started_lsp_name) in self.language_server_ids.keys() { + let language = languages.iter().find(|l| { + l.lsp_adapters() + .iter() + .any(|adapter| &adapter.name == started_lsp_name) + }); + if let Some(language) = language { + let worktree = self.worktree_for_id(*worktree_id, cx); + let file = worktree.and_then(|tree| tree.update(cx, |tree, cx| tree.root_file(cx))); + // let settings = + // language_settings(Some(language.name().as_ref()), Some(file), cx); + if !language_settings(Some(&language.name()), file.as_ref().map(|f| f as _), cx) .enable_language_server { - let lsp_name = &lsp_adapter.name; - for (worktree_id, started_lsp_name) in self.language_server_ids.keys() { - if lsp_name == started_lsp_name { - language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); - } - } + language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); } } } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 7432eb8d40..5d121adc63 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -679,7 +679,7 @@ impl Worktree { } pub fn root_file(&self, cx: &mut ModelContext) -> Option { - let entry = self.entry_for_path("")?; + let entry = self.root_entry()?; Some(File { worktree: cx.handle(), path: entry.path.clone(), From 38078b93cc1361b58ef4529058ab3e62cc2dc3d3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 14:44:48 -0700 Subject: [PATCH 42/53] v0.90.x dev --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3751b34d31..2b340cf46a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8777,7 +8777,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.89.0" +version = "0.90.0" dependencies = [ "activity_indicator", "ai", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index e96dff1221..c45991cf11 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.89.0" +version = "0.90.0" publish = false [lib] From 03a351fb26a454c1dbd8eec93a4a05f50f4768e0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 14:57:04 -0700 Subject: [PATCH 43/53] Make language settings accessors take an arc dyn file --- crates/copilot/src/copilot.rs | 2 +- crates/copilot_button/src/copilot_button.rs | 2 +- crates/editor/src/editor.rs | 5 ++-- crates/editor/src/multi_buffer.rs | 4 +-- crates/language/src/buffer.rs | 8 ++---- crates/language/src/language_settings.rs | 4 +-- crates/project/src/lsp_command.rs | 3 +-- crates/project/src/project.rs | 30 +++++++++------------ crates/project/src/project_tests.rs | 20 ++++++++------ crates/project/src/worktree.rs | 17 ++++-------- 10 files changed, 41 insertions(+), 54 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 1084222648..9343bd33f6 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -787,7 +787,7 @@ impl Copilot { let position = position.to_point_utf16(buffer); let settings = language_settings( buffer.language_at(position).map(|l| l.name()).as_deref(), - buffer.file().map(|f| f.as_ref()), + buffer.file(), cx, ); let tab_size = settings.tab_size; diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 4cb7c15bd9..27fd2dcb7c 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -285,7 +285,7 @@ impl CopilotButton { let file = snapshot.file_at(suggestion_anchor).cloned(); self.editor_enabled = Some( - all_language_settings(self.file.as_ref().map(|f| f.as_ref()), cx).copilot_enabled( + all_language_settings(self.file.as_ref(), cx).copilot_enabled( language_name.as_deref(), file.as_ref().map(|file| file.path().as_ref()), ), diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4a9237f563..1b62192999 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3211,7 +3211,7 @@ impl Editor { let language_name = snapshot .language_at(location) .map(|language| language.name()); - let settings = all_language_settings(file.map(|f| f.as_ref() as _), cx); + let settings = all_language_settings(file, cx); settings.copilot_enabled(language_name.as_deref(), file.map(|f| f.path().as_ref())) } @@ -7093,8 +7093,7 @@ impl Editor { .get("vim_mode") == Some(&serde_json::Value::Bool(true)); let telemetry_settings = *settings::get::(cx); - let copilot_enabled = - all_language_settings(file.map(|f| f.as_ref()), cx).copilot_enabled(None, None); + let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None); let copilot_enabled_for_language = self .buffer .read(cx) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 597feb832b..1cf30a1676 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1382,7 +1382,7 @@ impl MultiBuffer { if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) { let buffer = buffer.read(cx); language = buffer.language_at(offset).map(|l| l.name()); - file = buffer.file().map(|f| f.as_ref()); + file = buffer.file(); } language_settings(language.as_deref(), file, cx) } @@ -2795,7 +2795,7 @@ impl MultiBufferSnapshot { let mut file = None; if let Some((buffer, offset)) = self.point_to_buffer_offset(point) { language = buffer.language_at(offset).map(|l| l.name()); - file = buffer.file().map(|f| f.as_ref()); + file = buffer.file(); } language_settings(language.as_deref(), file, cx) } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index ddec085e2a..90a86d065c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1808,11 +1808,7 @@ impl BufferSnapshot { pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { let language_name = self.language_at(position).map(|language| language.name()); - let settings = language_settings( - language_name.as_deref(), - self.file().map(|f| f.as_ref()), - cx, - ); + let settings = language_settings(language_name.as_deref(), self.file(), cx); if settings.hard_tabs { IndentSize::tab() } else { @@ -2139,7 +2135,7 @@ impl BufferSnapshot { let language = self.language_at(position); language_settings( language.map(|l| l.name()).as_deref(), - self.file.as_ref().map(AsRef::as_ref), + self.file.as_ref(), cx, ) } diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index e4e283bd7e..31d819b449 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -16,7 +16,7 @@ pub fn init(cx: &mut AppContext) { pub fn language_settings<'a>( language: Option<&str>, - file: Option<&dyn File>, + file: Option<&Arc>, cx: &'a AppContext, ) -> &'a LanguageSettings { settings::get_local::( @@ -27,7 +27,7 @@ pub fn language_settings<'a>( } pub fn all_language_settings<'a>( - file: Option<&dyn File>, + file: Option<&Arc>, cx: &'a AppContext, ) -> &'a AllLanguageSettings { settings::get_local::( diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 0a8b5f8a2e..f349566535 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1717,8 +1717,7 @@ impl LspCommand for OnTypeFormatting { let tab_size = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - let file = buffer.file().map(|f| f.as_ref()); - language_settings(language_name.as_deref(), file, cx).tab_size + language_settings(language_name.as_deref(), buffer.file(), cx).tab_size }); Ok(Self { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c871bf0b96..a106632e36 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -693,12 +693,14 @@ impl Project { for buffer in self.opened_buffers.values() { if let Some(buffer) = buffer.upgrade(cx) { let buffer = buffer.read(cx); - if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) - { + if let Some((file, language)) = buffer.file().zip(buffer.language()) { let settings = language_settings(Some(language.name().as_ref()), Some(file), cx); if settings.enable_language_server { - language_servers_to_start.push((file.worktree.clone(), language.clone())); + if let Some(file) = File::from_dyn(Some(file)) { + language_servers_to_start + .push((file.worktree.clone(), language.clone())); + } } } } @@ -714,10 +716,10 @@ impl Project { }); if let Some(language) = language { let worktree = self.worktree_for_id(*worktree_id, cx); - let file = worktree.and_then(|tree| tree.update(cx, |tree, cx| tree.root_file(cx))); - // let settings = - // language_settings(Some(language.name().as_ref()), Some(file), cx); - if !language_settings(Some(&language.name()), file.as_ref().map(|f| f as _), cx) + let file = worktree.and_then(|tree| { + tree.update(cx, |tree, cx| tree.root_file(cx).map(|f| f as _)) + }); + if !language_settings(Some(&language.name()), file.as_ref(), cx) .enable_language_server { language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); @@ -2362,8 +2364,8 @@ impl Project { Some(&language.name()), worktree .update(cx, |tree, cx| tree.root_file(cx)) - .as_ref() - .map(|f| f as _), + .map(|f| f as _) + .as_ref(), cx, ) .enable_language_server @@ -3464,12 +3466,7 @@ impl Project { for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers { let settings = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - language_settings( - language_name.as_deref(), - buffer.file().map(|f| f.as_ref()), - cx, - ) - .clone() + language_settings(language_name.as_deref(), buffer.file(), cx).clone() }); let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save; @@ -4481,10 +4478,9 @@ impl Project { let (position, tab_size) = buffer.read_with(cx, |buffer, cx| { let position = position.to_point_utf16(buffer); let language_name = buffer.language_at(position).map(|l| l.name()); - let file = buffer.file().map(|f| f.as_ref()); ( position, - language_settings(language_name.as_deref(), file, cx).tab_size, + language_settings(language_name.as_deref(), buffer.file(), cx).tab_size, ) }); self.request_lsp( diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 577b22d730..ee6752412d 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -99,18 +99,22 @@ async fn test_managing_project_specific_settings( let settings_a = language_settings( None, - Some(&File::for_entry( - tree.entry_for_path("a/a.rs").unwrap().clone(), - worktree.clone(), - )), + Some( + &(File::for_entry( + tree.entry_for_path("a/a.rs").unwrap().clone(), + worktree.clone(), + ) as _), + ), cx, ); let settings_b = language_settings( None, - Some(&File::for_entry( - tree.entry_for_path("b/b.rs").unwrap().clone(), - worktree.clone(), - )), + Some( + &(File::for_entry( + tree.entry_for_path("b/b.rs").unwrap().clone(), + worktree.clone(), + ) as _), + ), cx, ); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 5d121adc63..a33a0fc050 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -678,16 +678,9 @@ impl Worktree { } } - pub fn root_file(&self, cx: &mut ModelContext) -> Option { + pub fn root_file(&self, cx: &mut ModelContext) -> Option> { let entry = self.root_entry()?; - Some(File { - worktree: cx.handle(), - path: entry.path.clone(), - mtime: entry.mtime, - entry_id: entry.id, - is_local: self.is_local(), - is_deleted: false, - }) + Some(File::for_entry(entry.clone(), cx.handle())) } } @@ -2463,15 +2456,15 @@ impl language::LocalFile for File { } impl File { - pub fn for_entry(entry: Entry, worktree: ModelHandle) -> Self { - Self { + pub fn for_entry(entry: Entry, worktree: ModelHandle) -> Arc { + Arc::new(Self { worktree, path: entry.path.clone(), mtime: entry.mtime, entry_id: entry.id, is_local: true, is_deleted: false, - } + }) } pub fn from_proto( From 0dd7694ff531a5ae049fd754ddef5787a2a76e11 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 15:10:30 -0700 Subject: [PATCH 44/53] Make language_settings take a language, not a language name --- crates/copilot/src/copilot.rs | 6 +---- crates/copilot_button/src/copilot_button.rs | 27 +++++++++------------ crates/editor/src/display_map.rs | 7 +++--- crates/editor/src/editor.rs | 6 ++--- crates/editor/src/multi_buffer.rs | 8 +++--- crates/language/src/buffer.rs | 10 ++------ crates/language/src/language_settings.rs | 11 +++++---- crates/project/src/lsp_command.rs | 3 +-- crates/project/src/project.rs | 16 +++++------- crates/zed/src/languages/yaml.rs | 7 ++++-- 10 files changed, 41 insertions(+), 60 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 9343bd33f6..7834603552 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -785,11 +785,7 @@ impl Copilot { let buffer = buffer.read(cx); let uri = registered_buffer.uri.clone(); let position = position.to_point_utf16(buffer); - let settings = language_settings( - buffer.language_at(position).map(|l| l.name()).as_deref(), - buffer.file(), - cx, - ); + let settings = language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx); let tab_size = settings.tab_size; let hard_tabs = settings.hard_tabs; let relative_path = buffer diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 27fd2dcb7c..e34fddd9b9 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -11,7 +11,7 @@ use gpui::{ }; use language::{ language_settings::{self, all_language_settings, AllLanguageSettings}, - File, + File, Language, }; use settings::{update_settings_file, SettingsStore}; use std::{path::Path, sync::Arc}; @@ -29,7 +29,7 @@ pub struct CopilotButton { popup_menu: ViewHandle, editor_subscription: Option<(Subscription, usize)>, editor_enabled: Option, - language: Option>, + language: Option>, file: Option>, fs: Arc, } @@ -200,14 +200,13 @@ impl CopilotButton { if let Some(language) = self.language.clone() { let fs = fs.clone(); - let language_enabled = - language_settings::language_settings(Some(language.as_ref()), None, cx) - .show_copilot_suggestions; + let language_enabled = language_settings::language_settings(Some(&language), None, cx) + .show_copilot_suggestions; menu_options.push(ContextMenuItem::handler( format!( "{} Suggestions for {}", if language_enabled { "Hide" } else { "Show" }, - language + language.name() ), move |cx| toggle_copilot_for_language(language.clone(), fs.clone(), cx), )); @@ -279,18 +278,14 @@ impl CopilotButton { let editor = editor.read(cx); let snapshot = editor.buffer().read(cx).snapshot(cx); let suggestion_anchor = editor.selections.newest_anchor().start; - let language_name = snapshot - .language_at(suggestion_anchor) - .map(|language| language.name()); + let language = snapshot.language_at(suggestion_anchor); let file = snapshot.file_at(suggestion_anchor).cloned(); self.editor_enabled = Some( - all_language_settings(self.file.as_ref(), cx).copilot_enabled( - language_name.as_deref(), - file.as_ref().map(|file| file.path().as_ref()), - ), + all_language_settings(self.file.as_ref(), cx) + .copilot_enabled(language, file.as_ref().map(|file| file.path().as_ref())), ); - self.language = language_name; + self.language = language.cloned(); self.file = file; cx.notify() @@ -374,12 +369,12 @@ fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { }); } -fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { +fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(Some(&language), None); update_settings_file::(fs, cx, move |file| { file.languages - .entry(language) + .entry(language.name()) .or_default() .show_copilot_suggestions = Some(!show_copilot_suggestions); }); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index f8d6f0bc40..b0483db68d 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -272,12 +272,11 @@ impl DisplayMap { } fn tab_size(buffer: &ModelHandle, cx: &mut ModelContext) -> NonZeroU32 { - let language_name = buffer + let language = buffer .read(cx) .as_singleton() - .and_then(|buffer| buffer.read(cx).language()) - .map(|language| language.name()); - language_settings(language_name.as_deref(), None, cx).tab_size + .and_then(|buffer| buffer.read(cx).language()); + language_settings(language.as_deref(), None, cx).tab_size } #[cfg(test)] diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1b62192999..9a9f36c577 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3208,11 +3208,9 @@ impl Editor { cx: &mut ViewContext, ) -> bool { let file = snapshot.file_at(location); - let language_name = snapshot - .language_at(location) - .map(|language| language.name()); + let language = snapshot.language_at(location); let settings = all_language_settings(file, cx); - settings.copilot_enabled(language_name.as_deref(), file.map(|f| f.path().as_ref())) + settings.copilot_enabled(language, file.map(|f| f.path().as_ref())) } fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool { diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 1cf30a1676..b7b2b89c8c 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1381,10 +1381,10 @@ impl MultiBuffer { let mut file = None; if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) { let buffer = buffer.read(cx); - language = buffer.language_at(offset).map(|l| l.name()); + language = buffer.language_at(offset); file = buffer.file(); } - language_settings(language.as_deref(), file, cx) + language_settings(language.as_ref(), file, cx) } pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle)) { @@ -2794,10 +2794,10 @@ impl MultiBufferSnapshot { let mut language = None; let mut file = None; if let Some((buffer, offset)) = self.point_to_buffer_offset(point) { - language = buffer.language_at(offset).map(|l| l.name()); + language = buffer.language_at(offset); file = buffer.file(); } - language_settings(language.as_deref(), file, cx) + language_settings(language, file, cx) } pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 90a86d065c..7acb36a92f 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1807,8 +1807,7 @@ impl BufferSnapshot { } pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { - let language_name = self.language_at(position).map(|language| language.name()); - let settings = language_settings(language_name.as_deref(), self.file(), cx); + let settings = language_settings(self.language_at(position), self.file(), cx); if settings.hard_tabs { IndentSize::tab() } else { @@ -2132,12 +2131,7 @@ impl BufferSnapshot { position: D, cx: &'a AppContext, ) -> &'a LanguageSettings { - let language = self.language_at(position); - language_settings( - language.map(|l| l.name()).as_deref(), - self.file.as_ref(), - cx, - ) + language_settings(self.language_at(position), self.file.as_ref(), cx) } pub fn language_scope_at(&self, position: D) -> Option { diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 31d819b449..b03dd6a6b9 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -1,4 +1,4 @@ -use crate::File; +use crate::{File, Language}; use anyhow::Result; use collections::HashMap; use globset::GlobMatcher; @@ -15,7 +15,7 @@ pub fn init(cx: &mut AppContext) { } pub fn language_settings<'a>( - language: Option<&str>, + language: Option<&Arc>, file: Option<&Arc>, cx: &'a AppContext, ) -> &'a LanguageSettings { @@ -23,7 +23,7 @@ pub fn language_settings<'a>( file.map(|f| (f.worktree_id(), f.path().as_ref())), cx, ) - .language(language) + .language(language.map(|l| l.name()).as_deref()) } pub fn all_language_settings<'a>( @@ -170,7 +170,7 @@ impl AllLanguageSettings { .any(|glob| glob.is_match(path)) } - pub fn copilot_enabled(&self, language_name: Option<&str>, path: Option<&Path>) -> bool { + pub fn copilot_enabled(&self, language: Option<&Arc>, path: Option<&Path>) -> bool { if !self.copilot.feature_enabled { return false; } @@ -181,7 +181,8 @@ impl AllLanguageSettings { } } - self.language(language_name).show_copilot_suggestions + self.language(language.map(|l| l.name()).as_deref()) + .show_copilot_suggestions } } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index f349566535..7dab8eff30 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1716,8 +1716,7 @@ impl LspCommand for OnTypeFormatting { .await?; let tab_size = buffer.read_with(&cx, |buffer, cx| { - let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), buffer.file(), cx).tab_size + language_settings(buffer.language(), buffer.file(), cx).tab_size }); Ok(Self { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a106632e36..bec4338596 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -694,8 +694,7 @@ impl Project { if let Some(buffer) = buffer.upgrade(cx) { let buffer = buffer.read(cx); if let Some((file, language)) = buffer.file().zip(buffer.language()) { - let settings = - language_settings(Some(language.name().as_ref()), Some(file), cx); + let settings = language_settings(Some(language), Some(file), cx); if settings.enable_language_server { if let Some(file) = File::from_dyn(Some(file)) { language_servers_to_start @@ -719,9 +718,7 @@ impl Project { let file = worktree.and_then(|tree| { tree.update(cx, |tree, cx| tree.root_file(cx).map(|f| f as _)) }); - if !language_settings(Some(&language.name()), file.as_ref(), cx) - .enable_language_server - { + if !language_settings(Some(language), file.as_ref(), cx).enable_language_server { language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); } } @@ -2361,7 +2358,7 @@ impl Project { cx: &mut ModelContext, ) { if !language_settings( - Some(&language.name()), + Some(&language), worktree .update(cx, |tree, cx| tree.root_file(cx)) .map(|f| f as _) @@ -3465,8 +3462,7 @@ impl Project { let mut project_transaction = ProjectTransaction::default(); for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers { let settings = buffer.read_with(&cx, |buffer, cx| { - let language_name = buffer.language().map(|language| language.name()); - language_settings(language_name.as_deref(), buffer.file(), cx).clone() + language_settings(buffer.language(), buffer.file(), cx).clone() }); let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save; @@ -4477,10 +4473,10 @@ impl Project { ) -> Task>> { let (position, tab_size) = buffer.read_with(cx, |buffer, cx| { let position = position.to_point_utf16(buffer); - let language_name = buffer.language_at(position).map(|l| l.name()); ( position, - language_settings(language_name.as_deref(), buffer.file(), cx).tab_size, + language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx) + .tab_size, ) }); self.request_lsp( diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index d66602ee04..7f87a7caed 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use futures::{future::BoxFuture, FutureExt, StreamExt}; use gpui::AppContext; use language::{ - language_settings::language_settings, LanguageServerBinary, LanguageServerName, LspAdapter, + language_settings::all_language_settings, LanguageServerBinary, LanguageServerName, LspAdapter, }; use node_runtime::NodeRuntime; use serde_json::Value; @@ -101,13 +101,16 @@ impl LspAdapter for YamlLspAdapter { } fn workspace_configuration(&self, cx: &mut AppContext) -> Option> { + let tab_size = all_language_settings(None, cx) + .language(Some("YAML")) + .tab_size; Some( future::ready(serde_json::json!({ "yaml": { "keyOrdering": false }, "[yaml]": { - "editor.tabSize": language_settings(Some("YAML"), None, cx).tab_size, + "editor.tabSize": tab_size, } })) .boxed(), From 0d281c1b89531547879e666320728a47336b47a9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 15:13:01 -0700 Subject: [PATCH 45/53] :art: --- crates/language/src/language_settings.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index b03dd6a6b9..332e789b4d 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -19,21 +19,16 @@ pub fn language_settings<'a>( file: Option<&Arc>, cx: &'a AppContext, ) -> &'a LanguageSettings { - settings::get_local::( - file.map(|f| (f.worktree_id(), f.path().as_ref())), - cx, - ) - .language(language.map(|l| l.name()).as_deref()) + let language_name = language.map(|l| l.name()); + all_language_settings(file, cx).language(language_name.as_deref()) } pub fn all_language_settings<'a>( file: Option<&Arc>, cx: &'a AppContext, ) -> &'a AllLanguageSettings { - settings::get_local::( - file.map(|f| (f.worktree_id(), f.path().as_ref())), - cx, - ) + let location = file.map(|f| (f.worktree_id(), f.path().as_ref())); + settings::get_local(location, cx) } #[derive(Debug, Clone)] From ae7606ce21d089ab10b751fc1a44720fe1592841 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 May 2023 15:38:26 -0700 Subject: [PATCH 46/53] collab 0.12.5 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b340cf46a..07a859b058 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1246,7 +1246,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.12.4" +version = "0.12.5" dependencies = [ "anyhow", "async-tungstenite", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index cd06b9a70a..07d2cdc60a 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.12.4" +version = "0.12.5" publish = false [[bin]] From d8ce333cf8e347e25e9af9aca3a918e57da9c8af Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 1 Jun 2023 16:51:29 +0300 Subject: [PATCH 47/53] Remove wrong assertion --- crates/workspace/src/workspace.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 364c3cadaf..21feea2b40 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -974,9 +974,8 @@ impl Workspace { let timestamp = entry.timestamp; match history.entry(project_path) { hash_map::Entry::Occupied(mut entry) => { - let (old_fs_path, old_timestamp) = entry.get(); + let (_, old_timestamp) = entry.get(); if ×tamp > old_timestamp { - assert_eq!(&fs_path, old_fs_path, "Inconsistent nav history"); entry.insert((fs_path, timestamp)); } } From 51c82da8401219cbf8e7487bfd8fd381ca88082c Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 1 Jun 2023 12:04:45 -0400 Subject: [PATCH 48/53] Avoid blocking forever on startup if config files do not exist The files will still get created if the user opens their settings and saves, otherwise everything will transparently work Co-Authored-By: Antonio Scandurra Co-Authored-By: Max Brunsfeld --- crates/settings/src/settings_file.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 4c98dca51a..3505330eda 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -66,15 +66,22 @@ pub fn watch_config_file( .spawn(async move { let events = fs.watch(&path, Duration::from_millis(100)).await; futures::pin_mut!(events); + + let contents = fs.load(&path).await.unwrap_or_default(); + if tx.unbounded_send(contents).is_err() { + return; + } + loop { + if events.next().await.is_none() { + break; + } + if let Ok(contents) = fs.load(&path).await { if !tx.unbounded_send(contents).is_ok() { break; } } - if events.next().await.is_none() { - break; - } } }) .detach(); From 2390815d67a5b30763a7e3a1fa3f53836347e6ef Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 1 Jun 2023 10:09:04 -0700 Subject: [PATCH 49/53] Make settings store handle no user settings co-authored-by: max --- crates/settings/src/settings_store.rs | 193 +++++++++++++------------- 1 file changed, 97 insertions(+), 96 deletions(-) diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index bb51fd1ed0..3fdb81dd45 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use collections::{btree_map, hash_map, BTreeMap, HashMap}; use gpui::AppContext; use lazy_static::lazy_static; @@ -84,15 +84,26 @@ pub struct SettingsJsonSchemaParams<'a> { } /// A set of strongly-typed setting values defined via multiple JSON files. -#[derive(Default)] pub struct SettingsStore { setting_values: HashMap>, - default_deserialized_settings: Option, - user_deserialized_settings: Option, + default_deserialized_settings: serde_json::Value, + user_deserialized_settings: serde_json::Value, local_deserialized_settings: BTreeMap<(usize, Arc), serde_json::Value>, tab_size_callback: Option<(TypeId, Box Option>)>, } +impl Default for SettingsStore { + fn default() -> Self { + SettingsStore { + setting_values: Default::default(), + default_deserialized_settings: serde_json::json!({}), + user_deserialized_settings: serde_json::json!({}), + local_deserialized_settings: Default::default(), + tab_size_callback: Default::default(), + } + } +} + #[derive(Debug)] struct SettingValue { global_value: Option, @@ -136,27 +147,24 @@ impl SettingsStore { local_values: Vec::new(), })); - if let Some(default_settings) = &self.default_deserialized_settings { - if let Some(default_settings) = setting_value - .deserialize_setting(default_settings) + if let Some(default_settings) = setting_value + .deserialize_setting(&self.default_deserialized_settings) + .log_err() + { + let mut user_values_stack = Vec::new(); + + if let Some(user_settings) = setting_value + .deserialize_setting(&self.user_deserialized_settings) .log_err() { - let mut user_values_stack = Vec::new(); + user_values_stack = vec![user_settings]; + } - if let Some(user_settings) = &self.user_deserialized_settings { - if let Some(user_settings) = - setting_value.deserialize_setting(user_settings).log_err() - { - user_values_stack = vec![user_settings]; - } - } - - if let Some(setting) = setting_value - .load_setting(&default_settings, &user_values_stack, cx) - .log_err() - { - setting_value.set_global_value(setting); - } + if let Some(setting) = setting_value + .load_setting(&default_settings, &user_values_stack, cx) + .log_err() + { + setting_value.set_global_value(setting); } } } @@ -189,9 +197,7 @@ impl SettingsStore { /// This is only for debugging and reporting. For user-facing functionality, /// use the typed setting interface. pub fn untyped_user_settings(&self) -> &serde_json::Value { - self.user_deserialized_settings - .as_ref() - .unwrap_or(&serde_json::Value::Null) + &self.user_deserialized_settings } #[cfg(any(test, feature = "test-support"))] @@ -213,11 +219,7 @@ impl SettingsStore { cx: &AppContext, update: impl FnOnce(&mut T::FileContent), ) { - if self.user_deserialized_settings.is_none() { - self.set_user_settings("{}", cx).unwrap(); - } - let old_text = - serde_json::to_string(self.user_deserialized_settings.as_ref().unwrap()).unwrap(); + let old_text = serde_json::to_string(&self.user_deserialized_settings).unwrap(); let new_text = self.new_text_for_update::(old_text, update); self.set_user_settings(&new_text, cx).unwrap(); } @@ -250,11 +252,7 @@ impl SettingsStore { .setting_values .get(&setting_type_id) .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) - .deserialize_setting( - self.user_deserialized_settings - .as_ref() - .expect("no user settings loaded"), - ) + .deserialize_setting(&self.user_deserialized_settings) .unwrap_or_else(|e| { panic!( "could not deserialize setting type {} from user settings: {}", @@ -323,10 +321,14 @@ impl SettingsStore { default_settings_content: &str, cx: &AppContext, ) -> Result<()> { - self.default_deserialized_settings = - Some(parse_json_with_comments(default_settings_content)?); - self.recompute_values(None, cx)?; - Ok(()) + let settings: serde_json::Value = parse_json_with_comments(default_settings_content)?; + if settings.is_object() { + self.default_deserialized_settings = settings; + self.recompute_values(None, cx)?; + Ok(()) + } else { + Err(anyhow!("settings must be an object")) + } } /// Set the user settings via a JSON string. @@ -335,9 +337,14 @@ impl SettingsStore { user_settings_content: &str, cx: &AppContext, ) -> Result<()> { - self.user_deserialized_settings = Some(parse_json_with_comments(user_settings_content)?); - self.recompute_values(None, cx)?; - Ok(()) + let settings: serde_json::Value = parse_json_with_comments(user_settings_content)?; + if settings.is_object() { + self.user_deserialized_settings = settings; + self.recompute_values(None, cx)?; + Ok(()) + } else { + Err(anyhow!("settings must be an object")) + } } /// Add or remove a set of local settings via a JSON string. @@ -361,7 +368,6 @@ impl SettingsStore { /// Add or remove a set of local settings via a JSON string. pub fn clear_local_settings(&mut self, root_id: usize, cx: &AppContext) -> Result<()> { - eprintln!("clearing local settings {root_id}"); self.local_deserialized_settings .retain(|k, _| k.0 != root_id); self.recompute_values(Some((root_id, "".as_ref())), cx)?; @@ -460,68 +466,63 @@ impl SettingsStore { let mut user_settings_stack = Vec::::new(); let mut paths_stack = Vec::>::new(); for setting_value in self.setting_values.values_mut() { - if let Some(default_settings) = &self.default_deserialized_settings { - let default_settings = setting_value.deserialize_setting(default_settings)?; + let default_settings = + setting_value.deserialize_setting(&self.default_deserialized_settings)?; - user_settings_stack.clear(); - paths_stack.clear(); + user_settings_stack.clear(); + paths_stack.clear(); - if let Some(user_settings) = &self.user_deserialized_settings { - if let Some(user_settings) = - setting_value.deserialize_setting(user_settings).log_err() - { - user_settings_stack.push(user_settings); - paths_stack.push(None); + if let Some(user_settings) = setting_value + .deserialize_setting(&self.user_deserialized_settings) + .log_err() + { + user_settings_stack.push(user_settings); + paths_stack.push(None); + } + + // If the global settings file changed, reload the global value for the field. + if changed_local_path.is_none() { + if let Some(value) = setting_value + .load_setting(&default_settings, &user_settings_stack, cx) + .log_err() + { + setting_value.set_global_value(value); + } + } + + // Reload the local values for the setting. + for ((root_id, path), local_settings) in &self.local_deserialized_settings { + // Build a stack of all of the local values for that setting. + while let Some(prev_entry) = paths_stack.last() { + if let Some((prev_root_id, prev_path)) = prev_entry { + if root_id != prev_root_id || !path.starts_with(prev_path) { + paths_stack.pop(); + user_settings_stack.pop(); + continue; + } } + break; } - // If the global settings file changed, reload the global value for the field. - if changed_local_path.is_none() { + if let Some(local_settings) = + setting_value.deserialize_setting(&local_settings).log_err() + { + paths_stack.push(Some((*root_id, path.as_ref()))); + user_settings_stack.push(local_settings); + + // If a local settings file changed, then avoid recomputing local + // settings for any path outside of that directory. + if changed_local_path.map_or(false, |(changed_root_id, changed_local_path)| { + *root_id != changed_root_id || !path.starts_with(changed_local_path) + }) { + continue; + } + if let Some(value) = setting_value .load_setting(&default_settings, &user_settings_stack, cx) .log_err() { - setting_value.set_global_value(value); - } - } - - // Reload the local values for the setting. - for ((root_id, path), local_settings) in &self.local_deserialized_settings { - // Build a stack of all of the local values for that setting. - while let Some(prev_entry) = paths_stack.last() { - if let Some((prev_root_id, prev_path)) = prev_entry { - if root_id != prev_root_id || !path.starts_with(prev_path) { - paths_stack.pop(); - user_settings_stack.pop(); - continue; - } - } - break; - } - - if let Some(local_settings) = - setting_value.deserialize_setting(&local_settings).log_err() - { - paths_stack.push(Some((*root_id, path.as_ref()))); - user_settings_stack.push(local_settings); - - // If a local settings file changed, then avoid recomputing local - // settings for any path outside of that directory. - if changed_local_path.map_or( - false, - |(changed_root_id, changed_local_path)| { - *root_id != changed_root_id || !path.starts_with(changed_local_path) - }, - ) { - continue; - } - - if let Some(value) = setting_value - .load_setting(&default_settings, &user_settings_stack, cx) - .log_err() - { - setting_value.set_local_value(*root_id, path.clone(), value); - } + setting_value.set_local_value(*root_id, path.clone(), value); } } } From 40c6baf7cbd92bbe700c57e18d7bc442fe12eb71 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 1 Jun 2023 10:49:27 -0700 Subject: [PATCH 50/53] Redraw the terminal on every wakeup co-authored-by: max --- crates/terminal_view/src/terminal_view.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 3c9836fb75..9478b3eef1 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -133,8 +133,8 @@ impl TerminalView { Event::Wakeup => { if !cx.is_self_focused() { this.has_new_content = true; - cx.notify(); } + cx.notify(); cx.emit(Event::Wakeup); } Event::Bell => { From 0065f5715ceaa905b5fa6ba07a2ab9ec923498c7 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Thu, 1 Jun 2023 16:40:54 -0400 Subject: [PATCH 51/53] Add panic events --- crates/zed/src/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 31f331ef93..9ec3e3d3f6 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -41,7 +41,7 @@ use std::{ Arc, Weak, }, thread, - time::Duration, + time::{Duration, SystemTime, UNIX_EPOCH}, }; use sum_tree::Bias; use terminal_view::{get_working_directory, TerminalSettings, TerminalView}; @@ -376,6 +376,7 @@ struct Panic { backtrace: Vec, // TODO // stripped_backtrace: String, + time: u128, } #[derive(Serialize)] @@ -413,6 +414,10 @@ fn init_panic_hook(app_version: String) { .map(|line| line.to_string()) .collect(), // modified_backtrace: None, + time: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis(), }; if let Some(panic_data_json) = serde_json::to_string_pretty(&panic_data).log_err() { From e67e6e6f70b76907d5e44628117a7cc48d0616c9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 1 Jun 2023 14:07:59 -0700 Subject: [PATCH 52/53] Fix ambiguous glob export warnings in rust 1.70 --- crates/editor/src/editor.rs | 4 +++- crates/gpui/src/elements/uniform_list.rs | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index baf2f062fe..5c19bb0121 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -31,7 +31,9 @@ use copilot::Copilot; pub use display_map::DisplayPoint; use display_map::*; pub use editor_settings::EditorSettings; -pub use element::*; +pub use element::{ + Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles, +}; use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index 9ccd57b291..8344914da0 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -36,7 +36,7 @@ struct StateInner { scroll_to: Option, } -pub struct LayoutState { +pub struct UniformListLayoutState { scroll_max: f32, item_height: f32, items: Vec>, @@ -152,7 +152,7 @@ impl UniformList { } impl Element for UniformList { - type LayoutState = LayoutState; + type LayoutState = UniformListLayoutState; type PaintState = (); fn layout( @@ -169,7 +169,7 @@ impl Element for UniformList { let no_items = ( constraint.min, - LayoutState { + UniformListLayoutState { item_height: 0., scroll_max: 0., items: Default::default(), @@ -263,7 +263,7 @@ impl Element for UniformList { ( size, - LayoutState { + UniformListLayoutState { item_height, scroll_max, items, From 585d13d3db5e589174c89fa141493b2d2196076b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 1 Jun 2023 15:20:15 -0700 Subject: [PATCH 53/53] Unzoom all panes when opening a dock --- crates/workspace/src/workspace.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 21feea2b40..795747cfcc 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1705,7 +1705,7 @@ impl Workspace { ) { // If a center pane is zoomed, unzoom it. for pane in &self.panes { - if pane != &self.active_pane { + if pane != &self.active_pane || dock_to_reveal.is_some() { pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); } } @@ -4351,6 +4351,12 @@ mod tests { panel }); + let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); + pane.update(cx, |pane, cx| { + let item = cx.add_view(|_| TestItem::new()); + pane.add_item(Box::new(item), true, true, None, cx); + }); + // Transfer focus from center to panel workspace.update(cx, |workspace, cx| { workspace.toggle_panel_focus::(cx); @@ -4452,6 +4458,25 @@ mod tests { assert!(workspace.zoomed.is_some()); assert!(panel.has_focus(cx)); }); + + // Unzoom and close the panel, zoom the active pane. + panel.update(cx, |panel, cx| panel.set_zoomed(false, cx)); + workspace.update(cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Right, cx) + }); + pane.update(cx, |pane, cx| pane.toggle_zoom(&Default::default(), cx)); + + // Opening a dock unzooms the pane. + workspace.update(cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Right, cx) + }); + workspace.read_with(cx, |workspace, cx| { + let pane = pane.read(cx); + assert!(!pane.is_zoomed()); + assert!(pane.has_focus()); + assert!(workspace.right_dock().read(cx).is_open()); + assert!(workspace.zoomed.is_none()); + }); } #[gpui::test]