diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 0845774fb3..9397d78e2b 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -103,6 +103,7 @@ jobs: name: package.${{ matrix.runtime }} path: | build/sourcegit-*.AppImage + build/sourcegit-*.AppImage.zsync build/sourcegit_*.deb build/sourcegit-*.rpm - name: Delete temp artifacts diff --git a/README.md b/README.md index 2bf6c6968b..2ac5ba0723 100644 --- a/README.md +++ b/README.md @@ -72,11 +72,11 @@ You can download the latest stable from [Releases](https://github.com/sourcegit- This software creates a folder, which is platform-dependent, to store user settings, downloaded avatars and crash logs. -| OS | PATH | -|---------|-------------------------------------------| -| Windows | `%APPDATA%\SourceGit` | -| Linux | `~/.sourcegit` | -| macOS | `~/Library/Application Support/SourceGit` | +| OS | PATH | +|---------|--------------------------------------------------------------------------------------------------| +| Windows | `%APPDATA%\SourceGit` | +| Linux | `${XDG_CONFIG_HOME}/SourceGit` (preference.json) `${XDG_CACHE_HOME}/SourceGit` (avatars & logs) | +| macOS | `~/Library/Application Support/SourceGit` | > [!TIP] > * You can open this data storage directory from the main menu `Open Data Storage Directory`. @@ -163,6 +163,14 @@ For **Linux** users: sudo apt install sourcegit ``` +* `AOSC OS`: In addition to AMD64 and ARM64, support for LoongArch and RISC-V has been added. + ```shell + sudo oma install sourcegit + ``` + +> [!NOTE] +> RISC-V support is untested. + * `AppImage` files can be found on [AppImage hub](https://appimage.github.io/SourceGit/), `xdg-open` (`xdg-utils`) must be installed to support open native file manager. * Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) or [git-credential-libsecret](https://pkgs.org/search/?q=git-credential-libsecret) is installed on your Linux. * Maybe you need to set environment variable `AVALONIA_SCREEN_SCALE_FACTORS`. See https://github.com/AvaloniaUI/Avalonia/wiki/Configuring-X11-per-monitor-DPI. @@ -186,6 +194,31 @@ For `OpenAI`: * `Server` must be `https://api.openai.com/v1` +For `OrcaRouter`: + +* `Server` should be `https://api.orcarouter.ai/v1` +* `API Key` should be an OrcaRouter API key (prefixed with `sk-orca-`) +* A good starting `Model` is `orcarouter/auto`, which routes each request to the best available model + +For `Cheaper Inference`: + +* `Server` should be `https://api.cheaperinference.com/v1` +* `API Key` should be a Cheaper Inference API key (prefixed with `ci_live_`) +* A good starting `Model` is `gpt-5.4-mini` + +For `Opper`: + +* `Server` should be `https://api.opper.ai/v3/compat` +* `API Key` should be an Opper API key, created at https://platform.opper.ai +* A good starting `Model` is `gpt-5.4-mini` + +For `API Route`: + +* `Server` should be `https://global.api-route.com/v1` +* `API Key` should be your API Route API key +* `Model` can be `gpt-6.1-sol` if it is available to your key; otherwise select an available chat model from the fetched catalog +* See the [API documentation](https://github.com/DennyHo0917/api-route/blob/main/API.md) for authentication and model discovery + For other AI service: * The `Server` should fill in a URL equivalent to OpenAI's `https://api.openai.com/v1`. For example, when using `Ollama`, it should be `http://localhost:11434/v1` instead of `http://localhost:11434/api/generate` @@ -207,7 +240,7 @@ This app supports open repository in external tools listed in the table below. > [!NOTE] > This app will try to find those tools based on some pre-defined or expected locations automatically. If you are using one portable version of these tools, it will not be detected by this app. -> To solve this problem you can add a file named `external_editors.json` in app data storage directory and provide the path directly. +> To solve this problem you can add a file named `external_editors.json` in app data storage directory (config dir for Linux) and provide the path directly. > User can also exclude some editors by using `external_editors.json`. The format of `external_editors.json`: diff --git a/THIRD-PARTY-LICENSES.md b/THIRD-PARTY-LICENSES.md index e2600fb4f4..4be26e4a12 100644 --- a/THIRD-PARTY-LICENSES.md +++ b/THIRD-PARTY-LICENSES.md @@ -112,3 +112,38 @@ The project uses the following third-party libraries or assets - **Commit**: 68d98dc57f8486c2946ae28dc86bf8e91d45da4d - **License**: MIT License - **License Link**: https://github.com/vuejs/language-tools/blob/68d98dc57f8486c2946ae28dc86bf8e91d45da4d/LICENSE + +### OCaml + +- **Source**: https://github.com/shentao/vue-multiselect/blob/master/docs/shiki/languages/ocaml.tmLanguage.json +- **Commit**: 8092917041f0ae02cbe4c6982deddc931c6adecd +- **License**: MIT License +- **License Link**: https://github.com/shentao/vue-multiselect/blob/master/LICENSE + +### OCamllex + +- **Source**: https://github.com/textmate/ocaml.tmbundle/blob/master/Syntaxes/OCamllex.tmLanguage +- **Commit**: 1eff26848b39368414cb4214183a2cba22f12d0e +- **License**: Permission to copy, use, modify, sell and distribute this software is granted. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose. +- **License Link**: https://github.com/textmate/ocaml.tmbundle/tree/master#license + +### OCamlyacc + +- **Source**: https://github.com/textmate/ocaml.tmbundle/blob/master/Syntaxes/OCamlyacc.tmLanguage +- **Commit**: 08a892aef6a4b285b1902d9ed1427653fce53e31 +- **License**: Permission to copy, use, modify, sell and distribute this software is granted. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose. +- **License Link**: https://github.com/textmate/ocaml.tmbundle/tree/master#license + +### Erlang + +- **Source**: https://github.com/shentao/vue-multiselect/blob/master/docs/shiki/languages/erlang.tmLanguage.json +- **Commit**: 8092917041f0ae02cbe4c6982deddc931c6adecd +- **License**: MIT License +- **License Link**: https://github.com/shentao/vue-multiselect/blob/master/LICENSE + +### Swift + +- **Source**: https://github.com/jtbandes/swift-tmlanguage/blob/main/Swift.tmLanguage.json +- **Commit**: abda279b567a0a7f35f81642c1b830f437f718d9 +- **License**: MIT License +- **License Link**: https://github.com/jtbandes/swift-tmlanguage/blob/main/LICENSE.md \ No newline at end of file diff --git a/TRANSLATION.md b/TRANSLATION.md index 47960c5473..0e33d6f6a4 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,144 +6,160 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-89.52%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-93.94%25-yellow)
Missing keys in de_DE.axaml -- Text.AIAssistant.Use -- Text.App.HideOthers -- Text.Apply.3Way -- Text.Apply.Source -- Text.Apply.Source.File -- Text.Apply.Source.Clipboard -- Text.Bisect.WaitingForCheckoutAnother -- Text.Bisect.WaitingForFirstBad -- Text.BranchCM.CompareWithSpecial -- Text.CheckoutBranchFromStash -- Text.CheckoutBranchFromStash.Branch -- Text.CheckoutBranchFromStash.Stash -- Text.CheckoutDetached -- Text.CheckoutDetached.Target -- Text.CheckoutDetached.Warning -- Text.Clone.Bookmark -- Text.Clone.Group -- Text.CommandPalette.Branches -- Text.CommandPalette.BranchesAndTags -- Text.CommandPalette.RepositoryActions -- Text.CommandPalette.RevisionFiles -- Text.CommitCM.CopyAuthorTime -- Text.CommitCM.CopyCommitterTime -- Text.CommitDetail.CollapseToBottom -- Text.CommitMessageTextBox.Column -- Text.Compare.Changes -- Text.Compare.Commits -- Text.Compare.Commits.LeftOnly -- Text.Compare.Commits.RightOnly -- Text.Compare.Commits.Tips -- Text.Configure.Git.EnableRecursiveWhenAutoUpdatingSubmodules -- Text.ConfigureCustomActionControls.StringFormatter -- Text.ConfigureCustomActionControls.StringFormatter.Tip -- Text.ConfigureCustomActionControls.UseFriendlyName -- Text.ConfirmEmptyCommit.StageSelectedThenCommit -- Text.CopyAsPatch -- Text.DeleteBranch.AskForRemote -- Text.DeleteBranch.Force -- Text.Diff.EmptyFile -- Text.Diff.Submodule.UncommittedChanges -- Text.Discard.IncludeModified -- Text.FileModeChange -- Text.FileModeChange.Deleted -- Text.FileModeChange.Directory -- Text.FileModeChange.Executable -- Text.FileModeChange.New -- Text.FileModeChange.Normal -- Text.FileModeChange.Submodule -- Text.FileModeChange.Symlink -- Text.FileModeChange.Unknown -- Text.GitFlow.Finish -- Text.GitFlow.FinishWithRebase -- Text.GitFlow.StartAt -- Text.GitFlow.StartName -- Text.GotoRevisionSelector -- Text.Histories.HighlightsInGraph -- Text.Histories.HighlightsInGraph.All -- Text.Histories.HighlightsInGraph.CurrentBranchOnly -- Text.Histories.HighlightsInGraph.CurrentBranchAndSelectedCommits -- Text.Histories.HighlightsInGraph.SelectedCommitsOnly -- Text.HistoriesDetailsStandalone -- Text.HistoriesDetailsStandalone.CommitDetail -- Text.HistoriesDetailsStandalone.RevisionCompare -- Text.Hotkeys.Global.OpenLocalRepository -- Text.Hotkeys.Repo.CreateBranch -- Text.Hotkeys.Repo.GoToChild -- Text.Hotkeys.Repo.ToggleHistoriesDetailPanel -- Text.Init.CommandTip -- Text.Init.ErrorMessageTip -- Text.InteractiveRebase.NoVerify -- Text.Launcher.NewVersion -- Text.Merge.Test -- Text.Merge.Test.NoConflicts -- Text.Merge.Test.UnknownError -- Text.Merge.Test.WillCauseConflicts -- Text.OpenLocalRepository -- Text.OpenLocalRepository.Bookmark -- Text.OpenLocalRepository.Group -- Text.OpenLocalRepository.Path -- Text.Preferences.AI.AdditionalPrompt -- Text.Preferences.AI.Model -- Text.Preferences.AI.Model.AutoFetchAvailableModels -- Text.Preferences.General.ShowRelativeTimeInGraph -- Text.Preferences.General.Use24Hours -- Text.Preferences.General.UseCompactBranchNames -- Text.Preferences.Git.UseStashAndReapplyByDefault -- Text.Rebase.NoVerify -- Text.Rebase.Test -- Text.Rebase.Test.OK -- Text.Rebase.Test.UnknownError -- Text.Rebase.Test.WillCauseConflicts -- Text.RemoteCM.EnableAutoFetch -- Text.Repository.FilterCommits.Collapse -- Text.Repository.FilterCommits.Expand -- Text.Repository.FilterCommits.Summary -- Text.StashCM.ApplyFileChanges -- Text.StashCM.Branch -- Text.SubmoduleRevisionCompare -- Text.SubmoduleRevisionCompare.OpenDetails -- Text.TagCM.Checkout -- Text.TagCM.Merge -- Text.UpdateSubmodules.Recursive -- Text.WorkingCopy.AddToGitIgnore.UntrackedInSameFolder -- Text.WorkingCopy.FilterChanges -- Text.Worktree.Branch -- Text.Worktree.Head -- Text.Worktree.Path +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree +- Text.BranchTree.PushURL +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder +- Text.CommitDetail.Files.Tips +- Text.Dashboard +- Text.Diff.Binary.ViewContentOfNew +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress +- Text.Histories.AdvancedOptions +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config +- Text.Preferences.AI.ExtraHeaders +- Text.Preferences.AI.ReasoningEffortLevel +- Text.Remote.ResetPushURL +- Text.RemoteCM.SetPushURL +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped +- Text.WorkingCopy.CollapseSidebar +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar
-### ![el__GR](https://img.shields.io/badge/el__GR-99.51%25-yellow) +### ![el__GR](https://img.shields.io/badge/el__GR-93.48%25-yellow)
Missing keys in el_GR.axaml +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree +- Text.BranchTree.PushURL +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder +- Text.CommitDetail.Files.Tips +- Text.Dashboard +- Text.Diff.Binary.ViewContentOfNew +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress +- Text.Histories.AdvancedOptions +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard - Text.Launcher.NewVersion +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config +- Text.Preferences.AI.ExtraHeaders +- Text.Preferences.AI.ReasoningEffortLevel +- Text.Remote.ResetPushURL +- Text.RemoteCM.SetPushURL - Text.Repository.FilterCommits.Collapse - Text.Repository.FilterCommits.Expand - Text.Repository.FilterCommits.Summary +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped +- Text.WorkingCopy.CollapseSidebar +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges
-### ![es__ES](https://img.shields.io/badge/es__ES-99.90%25-yellow) - -
-Missing keys in es_ES.axaml - -- Text.WorkingCopy.FilterChanges - -
+### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) -### ![fr__FR](https://img.shields.io/badge/fr__FR-95.49%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-89.75%25-yellow)
Missing keys in fr_FR.axaml @@ -153,16 +169,32 @@ This document shows the translation status of each locale file in the repository - Text.Apply.Source.Clipboard - Text.Bisect.WaitingForCheckoutAnother - Text.Bisect.WaitingForFirstBad +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree +- Text.BranchTree.PushURL +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder - Text.CheckoutDetached - Text.CheckoutDetached.Target - Text.CheckoutDetached.Warning - Text.CommitCM.CopyAuthorTime - Text.CommitCM.CopyCommitterTime +- Text.CommitDetail.Files.Tips - Text.Configure.Git.EnableRecursiveWhenAutoUpdatingSubmodules - Text.CopyAsPatch -- Text.DeleteBranch.AskForRemote +- Text.Dashboard - Text.DeleteBranch.Force +- Text.Diff.Binary.ViewContentOfNew - Text.Diff.EmptyFile +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress - Text.FileModeChange - Text.FileModeChange.Deleted - Text.FileModeChange.Directory @@ -176,28 +208,76 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithRebase - Text.GitFlow.StartAt - Text.GitFlow.StartName +- Text.Histories.AdvancedOptions +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard - Text.Launcher.NewVersion - Text.Merge.Test - Text.Merge.Test.NoConflicts - Text.Merge.Test.UnknownError - Text.Merge.Test.WillCauseConflicts +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config +- Text.Preferences.AI.ExtraHeaders +- Text.Preferences.AI.ReasoningEffortLevel - Text.Preferences.General.UseCompactBranchNames - Text.Rebase.Test - Text.Rebase.Test.OK - Text.Rebase.Test.UnknownError - Text.Rebase.Test.WillCauseConflicts +- Text.Remote.ResetPushURL +- Text.RemoteCM.SetPushURL - Text.Repository.FilterCommits.Collapse - Text.Repository.FilterCommits.Expand - Text.Repository.FilterCommits.Summary +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip - Text.TagCM.Checkout - Text.TagCM.Merge +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped - Text.UpdateSubmodules.Recursive - Text.WorkingCopy.AddToGitIgnore.UntrackedInSameFolder +- Text.WorkingCopy.CollapseSidebar +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges
-### ![he__IL](https://img.shields.io/badge/he__IL-95.49%25-yellow) +### ![he__IL](https://img.shields.io/badge/he__IL-89.75%25-yellow)
Missing keys in he_IL.axaml @@ -207,16 +287,32 @@ This document shows the translation status of each locale file in the repository - Text.Apply.Source.Clipboard - Text.Bisect.WaitingForCheckoutAnother - Text.Bisect.WaitingForFirstBad +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree +- Text.BranchTree.PushURL +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder - Text.CheckoutDetached - Text.CheckoutDetached.Target - Text.CheckoutDetached.Warning - Text.CommitCM.CopyAuthorTime - Text.CommitCM.CopyCommitterTime +- Text.CommitDetail.Files.Tips - Text.Configure.Git.EnableRecursiveWhenAutoUpdatingSubmodules - Text.CopyAsPatch -- Text.DeleteBranch.AskForRemote +- Text.Dashboard - Text.DeleteBranch.Force +- Text.Diff.Binary.ViewContentOfNew - Text.Diff.EmptyFile +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress - Text.FileModeChange - Text.FileModeChange.Deleted - Text.FileModeChange.Directory @@ -230,37 +326,149 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithRebase - Text.GitFlow.StartAt - Text.GitFlow.StartName +- Text.Histories.AdvancedOptions +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard - Text.Launcher.NewVersion - Text.Merge.Test - Text.Merge.Test.NoConflicts - Text.Merge.Test.UnknownError - Text.Merge.Test.WillCauseConflicts +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config +- Text.Preferences.AI.ExtraHeaders +- Text.Preferences.AI.ReasoningEffortLevel - Text.Preferences.General.UseCompactBranchNames - Text.Rebase.Test - Text.Rebase.Test.OK - Text.Rebase.Test.UnknownError - Text.Rebase.Test.WillCauseConflicts +- Text.Remote.ResetPushURL +- Text.RemoteCM.SetPushURL - Text.Repository.FilterCommits.Collapse - Text.Repository.FilterCommits.Expand - Text.Repository.FilterCommits.Summary +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip - Text.TagCM.Checkout - Text.TagCM.Merge +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped - Text.UpdateSubmodules.Recursive - Text.WorkingCopy.AddToGitIgnore.UntrackedInSameFolder +- Text.WorkingCopy.CollapseSidebar +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges
-### ![id__ID](https://img.shields.io/badge/id__ID-99.90%25-yellow) +### ![id__ID](https://img.shields.io/badge/id__ID-93.94%25-yellow)
Missing keys in id_ID.axaml +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree +- Text.BranchTree.PushURL +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder +- Text.CommitDetail.Files.Tips +- Text.Dashboard +- Text.Diff.Binary.ViewContentOfNew +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress +- Text.Histories.AdvancedOptions +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config +- Text.Preferences.AI.ReasoningEffortLevel +- Text.Remote.ResetPushURL +- Text.RemoteCM.SetPushURL +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped +- Text.WorkingCopy.CollapseSidebar +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges
-### ![it__IT](https://img.shields.io/badge/it__IT-89.03%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-83.78%25-yellow)
Missing keys in it_IT.axaml @@ -273,8 +481,19 @@ This document shows the translation status of each locale file in the repository - Text.Apply.Source.Clipboard - Text.Bisect.WaitingForCheckoutAnother - Text.Bisect.WaitingForFirstBad +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree - Text.BranchCM.CompareWithSpecial +- Text.BranchTree.PushURL - Text.ChangeCM.ResetFileTo +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder - Text.CheckoutBranchFromStash - Text.CheckoutBranchFromStash.Branch - Text.CheckoutBranchFromStash.Stash @@ -290,7 +509,7 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.CopyAuthorTime - Text.CommitCM.CopyCommitterTime - Text.CommitDetail.CollapseToBottom -- Text.CommitMessageTextBox.Column +- Text.CommitDetail.Files.Tips - Text.Compare.Changes - Text.Compare.Commits - Text.Compare.Commits.LeftOnly @@ -302,11 +521,15 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.UseFriendlyName - Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.CopyAsPatch -- Text.DeleteBranch.AskForRemote +- Text.Dashboard - Text.DeleteBranch.Force +- Text.Diff.Binary.ViewContentOfNew - Text.Diff.EmptyFile - Text.Diff.Submodule.UncommittedChanges - Text.Discard.IncludeModified +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress - Text.FileModeChange - Text.FileModeChange.Deleted - Text.FileModeChange.Directory @@ -321,11 +544,13 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.StartAt - Text.GitFlow.StartName - Text.GotoRevisionSelector +- Text.Histories.AdvancedOptions - Text.Histories.HighlightsInGraph - Text.Histories.HighlightsInGraph.All - Text.Histories.HighlightsInGraph.CurrentBranchOnly - Text.Histories.HighlightsInGraph.CurrentBranchAndSelectedCommits - Text.Histories.HighlightsInGraph.SelectedCommitsOnly +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent - Text.Histories.ShowColumns - Text.HistoriesDetailsStandalone - Text.HistoriesDetailsStandalone.CommitDetail @@ -334,7 +559,9 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.CreateBranch - Text.Hotkeys.Repo.GoToChild - Text.Hotkeys.Repo.GoToParent -- Text.Hotkeys.Repo.ToggleHistoriesDetailPanel +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard - Text.Init.CommandTip - Text.Init.ErrorMessageTip - Text.InteractiveRebase.NoVerify @@ -343,13 +570,17 @@ This document shows the translation status of each locale file in the repository - Text.Merge.Test.NoConflicts - Text.Merge.Test.UnknownError - Text.Merge.Test.WillCauseConflicts +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config - Text.OpenLocalRepository - Text.OpenLocalRepository.Bookmark - Text.OpenLocalRepository.Group - Text.OpenLocalRepository.Path - Text.Preferences.AI.AdditionalPrompt +- Text.Preferences.AI.ExtraHeaders - Text.Preferences.AI.Model - Text.Preferences.AI.Model.AutoFetchAvailableModels +- Text.Preferences.AI.ReasoningEffortLevel - Text.Preferences.General.ShowRelativeTimeInGraph - Text.Preferences.General.Use24Hours - Text.Preferences.General.UseCompactBranchNames @@ -359,20 +590,59 @@ This document shows the translation status of each locale file in the repository - Text.Rebase.Test.OK - Text.Rebase.Test.UnknownError - Text.Rebase.Test.WillCauseConflicts +- Text.Remote.ResetPushURL - Text.RemoteCM.EnableAutoFetch +- Text.RemoteCM.SetPushURL - Text.Repository.FilterCommits.Collapse - Text.Repository.FilterCommits.Expand - Text.Repository.FilterCommits.Summary +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip - Text.SelfUpdate.CurrentVersion - Text.SelfUpdate.ReleaseDate +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title - Text.StashCM.ApplyFileChanges - Text.StashCM.Branch +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip - Text.SubmoduleRevisionCompare - Text.SubmoduleRevisionCompare.OpenDetails - Text.TagCM.Checkout - Text.TagCM.Merge +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped - Text.UpdateSubmodules.Recursive - Text.WorkingCopy.AddToGitIgnore.UntrackedInSameFolder +- Text.WorkingCopy.CollapseSidebar +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges - Text.Worktree.Branch - Text.Worktree.Head @@ -380,33 +650,66 @@ This document shows the translation status of each locale file in the repository
-### ![ja__JP](https://img.shields.io/badge/ja__JP-99.51%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-97.95%25-yellow)
Missing keys in ja_JP.axaml -- Text.Launcher.NewVersion -- Text.Repository.FilterCommits.Collapse -- Text.Repository.FilterCommits.Expand -- Text.Repository.FilterCommits.Summary -- Text.WorkingCopy.FilterChanges +- Text.BranchCM.CheckoutAsWorktree +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder +- Text.CommitDetail.Files.Tips +- Text.Statistics.Commits +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped +- Text.WorkingCopy.CollapseSidebar +- Text.WorkingCopy.ExpandSidebar
-### ![ko__KR](https://img.shields.io/badge/ko__KR-96.96%25-yellow) +### ![ko__KR](https://img.shields.io/badge/ko__KR-91.15%25-yellow)
Missing keys in ko_KR.axaml - Text.Bisect.WaitingForCheckoutAnother - Text.Bisect.WaitingForFirstBad +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree +- Text.BranchTree.PushURL +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder - Text.CheckoutDetached - Text.CheckoutDetached.Target - Text.CheckoutDetached.Warning +- Text.CommitDetail.Files.Tips - Text.Configure.Git.EnableRecursiveWhenAutoUpdatingSubmodules -- Text.DeleteBranch.AskForRemote +- Text.Dashboard - Text.DeleteBranch.Force +- Text.Diff.Binary.ViewContentOfNew - Text.Diff.EmptyFile +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress - Text.FileModeChange - Text.FileModeChange.Deleted - Text.FileModeChange.Directory @@ -420,19 +723,67 @@ This document shows the translation status of each locale file in the repository - Text.GitFlow.FinishWithRebase - Text.GitFlow.StartAt - Text.GitFlow.StartName +- Text.Histories.AdvancedOptions +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard - Text.Launcher.NewVersion +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config +- Text.Preferences.AI.ExtraHeaders +- Text.Preferences.AI.ReasoningEffortLevel +- Text.Remote.ResetPushURL +- Text.RemoteCM.SetPushURL - Text.Repository.FilterCommits.Collapse - Text.Repository.FilterCommits.Expand - Text.Repository.FilterCommits.Summary +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip - Text.TagCM.Checkout - Text.TagCM.Merge +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped - Text.UpdateSubmodules.Recursive - Text.WorkingCopy.AddToGitIgnore.UntrackedInSameFolder +- Text.WorkingCopy.CollapseSidebar +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges
-### ![pt__BR](https://img.shields.io/badge/pt__BR-62.78%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-58.99%25-red)
Missing keys in pt_BR.axaml @@ -446,10 +797,13 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.WaitingForCheckoutAnother - Text.Bisect.WaitingForFirstBad - Text.Blame.BlameOnPreviousRevision +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree - Text.BranchCM.CompareWithSpecial - Text.BranchCM.InteractiveRebase.Manually - Text.BranchTree.AheadBehind - Text.BranchTree.Behind +- Text.BranchTree.PushURL - Text.BranchTree.Tracking - Text.BranchTree.URL - Text.BranchTree.Worktree @@ -463,6 +817,14 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WarnUpdatingSubmodules - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder - Text.CheckoutBranchFromStash - Text.CheckoutBranchFromStash.Branch - Text.CheckoutBranchFromStash.Stash @@ -496,12 +858,12 @@ This document shows the translation status of each locale file in the repository - Text.CommitDetail.Changes.Count - Text.CommitDetail.CollapseToBottom - Text.CommitDetail.Files.Search +- Text.CommitDetail.Files.Tips - Text.CommitDetail.Info.CopyEmail - Text.CommitDetail.Info.CopyName - Text.CommitDetail.Info.CopyNameAndEmail - Text.CommitDetail.Info.Key - Text.CommitDetail.Info.Signer -- Text.CommitMessageTextBox.Column - Text.CommitMessageTextBox.Placeholder - Text.CommitMessageTextBox.SubjectCount - Text.Compare.Changes @@ -550,10 +912,10 @@ This document shows the translation status of each locale file in the repository - Text.CopyAsPatch - Text.CopyFullPath - Text.CreateBranch.OverwriteExisting +- Text.Dashboard - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path -- Text.DeleteBranch.AskForRemote - Text.DeleteBranch.Force - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes @@ -561,6 +923,7 @@ This document shows the translation status of each locale file in the repository - Text.DeleteRepositoryNode.Path - Text.DeleteRepositoryNode.TipForGroup - Text.DeleteRepositoryNode.TipForRepository +- Text.Diff.Binary.ViewContentOfNew - Text.Diff.EmptyFile - Text.Diff.First - Text.Diff.Image.Blend @@ -583,6 +946,9 @@ This document shows the translation status of each locale file in the repository - Text.ExecuteCustomAction.Target - Text.ExecuteCustomAction.Repository - Text.Fetch.Force +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress - Text.FileCM.CustomAction - Text.FileCM.ResolveUsing - Text.FileModeChange @@ -602,11 +968,13 @@ This document shows the translation status of each locale file in the repository - Text.GitLFS.Locks.UnlockAllMyLocks - Text.GitLFS.Locks.UnlockAllMyLocks.Confirm - Text.GotoRevisionSelector +- Text.Histories.AdvancedOptions - Text.Histories.HighlightsInGraph - Text.Histories.HighlightsInGraph.All - Text.Histories.HighlightsInGraph.CurrentBranchOnly - Text.Histories.HighlightsInGraph.CurrentBranchAndSelectedCommits - Text.Histories.HighlightsInGraph.SelectedCommitsOnly +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent - Text.Histories.ShowColumns - Text.HistoriesDetailsStandalone - Text.HistoriesDetailsStandalone.CommitDetail @@ -620,7 +988,9 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.GoToChild - Text.Hotkeys.Repo.GoToParent - Text.Hotkeys.Repo.OpenCommandPalette -- Text.Hotkeys.Repo.ToggleHistoriesDetailPanel +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard - Text.Init.CommandTip - Text.Init.ErrorMessageTip - Text.InProgress.CherryPick.Head @@ -666,6 +1036,8 @@ This document shows the translation status of each locale file in the repository - Text.No - Text.Open - Text.Open.SystemDefaultEditor +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config - Text.OpenFile - Text.OpenLocalRepository - Text.OpenLocalRepository.Bookmark @@ -674,9 +1046,11 @@ This document shows the translation status of each locale file in the repository - Text.PageTabBar.Tab.MoveToWorkspace - Text.PageTabBar.Tab.Refresh - Text.Preferences.AI.AdditionalPrompt +- Text.Preferences.AI.ExtraHeaders - Text.Preferences.AI.Model - Text.Preferences.AI.Model.AutoFetchAvailableModels - Text.Preferences.AI.ReadApiKeyFromEnv +- Text.Preferences.AI.ReasoningEffortLevel - Text.Preferences.Appearance.EditorTabWidth - Text.Preferences.Appearance.UseAutoHideScrollBars - Text.Preferences.DiffMerge.DiffArgs @@ -708,13 +1082,14 @@ This document shows the translation status of each locale file in the repository - Text.Rebase.Test.OK - Text.Rebase.Test.UnknownError - Text.Rebase.Test.WillCauseConflicts +- Text.Remote.ResetPushURL - Text.RemoteCM.CustomAction - Text.RemoteCM.EnableAutoFetch +- Text.RemoteCM.SetPushURL - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName - Text.Repository.ClearStashes -- Text.Repository.Dashboard - Text.Repository.FilterCommits - Text.Repository.FilterCommits.Collapse - Text.Repository.FilterCommits.Expand @@ -723,12 +1098,13 @@ This document shows the translation status of each locale file in the repository - Text.Repository.HistoriesLayout.Horizontal - Text.Repository.HistoriesLayout.Vertical - Text.Repository.HistoriesOrder -- Text.Repository.MoreOptions - Text.Repository.Notifications.Clear - Text.Repository.OpenAsFolder - Text.Repository.Resolve - Text.Repository.Search.ByContent - Text.Repository.Search.ByPath +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip - Text.Repository.ShowDecoratedCommitsOnly - Text.Repository.ShowFirstParentOnly - Text.Repository.ShowFlags @@ -746,6 +1122,10 @@ This document shows the translation status of each locale file in the repository - Text.ScanRepositories.UseCustomDir - Text.SelfUpdate.CurrentVersion - Text.SelfUpdate.ReleaseDate +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL - Text.SetSubmoduleBranch - Text.SetSubmoduleBranch.Submodule - Text.SetSubmoduleBranch.Current @@ -756,11 +1136,33 @@ This document shows the translation status of each locale file in the repository - Text.SetUpstream.Unset - Text.SetUpstream.Upstream - Text.SHALinkCM.NavigateTo +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title - Text.Stash.Mode - Text.StashCM.ApplyFileChanges - Text.StashCM.Branch - Text.StashCM.CopyMessage - Text.StashCM.SaveAsPatch +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip - Text.Submodule.Branch - Text.Submodule.CopyBranch - Text.Submodule.Deinit @@ -790,6 +1192,12 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.CustomAction - Text.TagCM.DeleteMultiple - Text.TagCM.Merge +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped - Text.UpdateSubmodules.Recursive - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch - Text.ViewLogs @@ -800,6 +1208,7 @@ This document shows the translation status of each locale file in the repository - Text.WorkingCopy.AddToGitIgnore.UntrackedInSameFolder - Text.WorkingCopy.ClearCommitHistories - Text.WorkingCopy.ClearCommitHistories.Confirm +- Text.WorkingCopy.CollapseSidebar - Text.WorkingCopy.CommitToEdit - Text.WorkingCopy.ConfirmCommitWithDetachedHead - Text.WorkingCopy.ConfirmCommitWithFilter @@ -808,6 +1217,8 @@ This document shows the translation status of each locale file in the repository - Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts - Text.WorkingCopy.Conflicts.UseMine - Text.WorkingCopy.Conflicts.UseTheirs +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges - Text.WorkingCopy.NoVerify - Text.WorkingCopy.ResetAuthor @@ -822,7 +1233,7 @@ This document shows the translation status of each locale file in the repository ### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) -### ![ta__IN](https://img.shields.io/badge/ta__IN-64.45%25-red) +### ![ta__IN](https://img.shields.io/badge/ta__IN-60.58%25-red)
Missing keys in ta_IN.axaml @@ -853,6 +1264,8 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.WaitingForMark - Text.Blame.BlameOnPreviousRevision - Text.Blame.IgnoreWhitespace +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree - Text.BranchCM.CompareTwo - Text.BranchCM.CompareWith - Text.BranchCM.CompareWithHead @@ -867,6 +1280,7 @@ This document shows the translation status of each locale file in the repository - Text.BranchTree.AheadBehind - Text.BranchTree.Behind - Text.BranchTree.InvalidUpstream +- Text.BranchTree.PushURL - Text.BranchTree.Remote - Text.BranchTree.Status - Text.BranchTree.Tracking @@ -882,6 +1296,14 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WarnUpdatingSubmodules - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder - Text.CheckoutBranchFromStash - Text.CheckoutBranchFromStash.Branch - Text.CheckoutBranchFromStash.Stash @@ -912,12 +1334,12 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.Reset - Text.CommitDetail.Changes.Count - Text.CommitDetail.CollapseToBottom +- Text.CommitDetail.Files.Tips - Text.CommitDetail.Info.CopyEmail - Text.CommitDetail.Info.CopyName - Text.CommitDetail.Info.CopyNameAndEmail - Text.CommitDetail.Info.Key - Text.CommitDetail.Info.Signer -- Text.CommitMessageTextBox.Column - Text.CommitMessageTextBox.Placeholder - Text.CommitMessageTextBox.SubjectCount - Text.Compare.Changes @@ -961,15 +1383,16 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmRestart.Message - Text.CopyAsPatch - Text.CreateBranch.OverwriteExisting +- Text.Dashboard - Text.DealWithLocalChanges.DoNothing - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path -- Text.DeleteBranch.AskForRemote - Text.DeleteBranch.Force - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip +- Text.Diff.Binary.ViewContentOfNew - Text.Diff.EmptyFile - Text.Diff.Image.Blend - Text.Diff.Image.Difference @@ -989,6 +1412,9 @@ This document shows the translation status of each locale file in the repository - Text.EditBranchDescription.Target - Text.ExecuteCustomAction.Target - Text.ExecuteCustomAction.Repository +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress - Text.FileCM.CustomAction - Text.FileModeChange - Text.FileModeChange.Deleted @@ -1007,11 +1433,13 @@ This document shows the translation status of each locale file in the repository - Text.GitLFS.Locks.UnlockAllMyLocks - Text.GitLFS.Locks.UnlockAllMyLocks.Confirm - Text.GotoRevisionSelector +- Text.Histories.AdvancedOptions - Text.Histories.HighlightsInGraph - Text.Histories.HighlightsInGraph.All - Text.Histories.HighlightsInGraph.CurrentBranchOnly - Text.Histories.HighlightsInGraph.CurrentBranchAndSelectedCommits - Text.Histories.HighlightsInGraph.SelectedCommitsOnly +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent - Text.Histories.ShowColumns - Text.HistoriesDetailsStandalone - Text.HistoriesDetailsStandalone.CommitDetail @@ -1024,7 +1452,9 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.GoToChild - Text.Hotkeys.Repo.GoToParent - Text.Hotkeys.Repo.OpenCommandPalette -- Text.Hotkeys.Repo.ToggleHistoriesDetailPanel +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard - Text.Init.CommandTip - Text.Init.ErrorMessageTip - Text.InteractiveRebase.NoVerify @@ -1061,6 +1491,8 @@ This document shows the translation status of each locale file in the repository - Text.No - Text.Open - Text.Open.SystemDefaultEditor +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config - Text.OpenFile - Text.OpenLocalRepository - Text.OpenLocalRepository.Bookmark @@ -1069,9 +1501,11 @@ This document shows the translation status of each locale file in the repository - Text.PageTabBar.Tab.MoveToWorkspace - Text.PageTabBar.Tab.Refresh - Text.Preferences.AI.AdditionalPrompt +- Text.Preferences.AI.ExtraHeaders - Text.Preferences.AI.Model - Text.Preferences.AI.Model.AutoFetchAvailableModels - Text.Preferences.AI.ReadApiKeyFromEnv +- Text.Preferences.AI.ReasoningEffortLevel - Text.Preferences.Appearance.UseAutoHideScrollBars - Text.Preferences.DiffMerge.DiffArgs - Text.Preferences.DiffMerge.DiffArgs.Tip @@ -1099,21 +1533,23 @@ This document shows the translation status of each locale file in the repository - Text.Rebase.Test.OK - Text.Rebase.Test.UnknownError - Text.Rebase.Test.WillCauseConflicts +- Text.Remote.ResetPushURL - Text.RemoteCM.CustomAction - Text.RemoteCM.EnableAutoFetch +- Text.RemoteCM.SetPushURL - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName - Text.Repository.ClearStashes -- Text.Repository.Dashboard - Text.Repository.FilterCommits.Collapse - Text.Repository.FilterCommits.Expand - Text.Repository.FilterCommits.Summary -- Text.Repository.MoreOptions - Text.Repository.OpenAsFolder - Text.Repository.Resolve - Text.Repository.Search.ByContent - Text.Repository.Search.ByPath +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip - Text.Repository.ShowDecoratedCommitsOnly - Text.Repository.ShowFirstParentOnly - Text.Repository.ShowFlags @@ -1127,15 +1563,41 @@ This document shows the translation status of each locale file in the repository - Text.ScanRepositories.UseCustomDir - Text.SelfUpdate.CurrentVersion - Text.SelfUpdate.ReleaseDate +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL - Text.SetSubmoduleBranch - Text.SetSubmoduleBranch.Submodule - Text.SetSubmoduleBranch.Current - Text.SetSubmoduleBranch.New - Text.SetSubmoduleBranch.New.Tip +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title - Text.Stash.Mode - Text.StashCM.ApplyFileChanges - Text.StashCM.Branch - Text.StashCM.CopyMessage +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip - Text.Submodule.Branch - Text.Submodule.CopyBranch - Text.Submodule.Deinit @@ -1165,6 +1627,12 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.CustomAction - Text.TagCM.DeleteMultiple - Text.TagCM.Merge +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped - Text.UpdateSubmodules.Target - Text.UpdateSubmodules.Recursive - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch @@ -1176,12 +1644,15 @@ This document shows the translation status of each locale file in the repository - Text.WorkingCopy.AddToGitIgnore.UntrackedInSameFolder - Text.WorkingCopy.ClearCommitHistories - Text.WorkingCopy.ClearCommitHistories.Confirm +- Text.WorkingCopy.CollapseSidebar - Text.WorkingCopy.ConfirmCommitWithDetachedHead - Text.WorkingCopy.Conflicts.Merge - Text.WorkingCopy.Conflicts.MergeExternal - Text.WorkingCopy.Conflicts.OpenExternalMergeToolAllConflicts - Text.WorkingCopy.Conflicts.UseMine - Text.WorkingCopy.Conflicts.UseTheirs +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges - Text.WorkingCopy.NoVerify - Text.WorkingCopy.ResetAuthor @@ -1193,7 +1664,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-65.23%25-red) +### ![uk__UA](https://img.shields.io/badge/uk__UA-61.32%25-red)
Missing keys in uk_UA.axaml @@ -1224,6 +1695,8 @@ This document shows the translation status of each locale file in the repository - Text.Bisect.WaitingForMark - Text.Blame.BlameOnPreviousRevision - Text.Blame.IgnoreWhitespace +- Text.Blame.Tips +- Text.BranchCM.CheckoutAsWorktree - Text.BranchCM.CompareTwo - Text.BranchCM.CompareWith - Text.BranchCM.CompareWithHead @@ -1238,6 +1711,7 @@ This document shows the translation status of each locale file in the repository - Text.BranchTree.AheadBehind - Text.BranchTree.Behind - Text.BranchTree.InvalidUpstream +- Text.BranchTree.PushURL - Text.BranchTree.Remote - Text.BranchTree.Status - Text.BranchTree.Tracking @@ -1253,6 +1727,14 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WarnUpdatingSubmodules - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream +- Text.CheckoutAsWorktree +- Text.CheckoutAsWorktree.LocalBranch +- Text.CheckoutAsWorktree.NewBranchName +- Text.CheckoutAsWorktree.NewBranchName.Placeholder +- Text.CheckoutAsWorktree.RemoteBranch +- Text.CheckoutAsWorktree.Tracking +- Text.CheckoutAsWorktree.WorktreePath +- Text.CheckoutAsWorktree.WorktreePath.Placeholder - Text.CheckoutBranchFromStash - Text.CheckoutBranchFromStash.Branch - Text.CheckoutBranchFromStash.Stash @@ -1283,12 +1765,12 @@ This document shows the translation status of each locale file in the repository - Text.CommitCM.Reset - Text.CommitDetail.Changes.Count - Text.CommitDetail.CollapseToBottom +- Text.CommitDetail.Files.Tips - Text.CommitDetail.Info.CopyEmail - Text.CommitDetail.Info.CopyName - Text.CommitDetail.Info.CopyNameAndEmail - Text.CommitDetail.Info.Key - Text.CommitDetail.Info.Signer -- Text.CommitMessageTextBox.Column - Text.CommitMessageTextBox.Placeholder - Text.CommitMessageTextBox.SubjectCount - Text.Compare.Changes @@ -1328,15 +1810,16 @@ This document shows the translation status of each locale file in the repository - Text.ConfirmRestart.Message - Text.CopyAsPatch - Text.CreateBranch.OverwriteExisting +- Text.Dashboard - Text.DealWithLocalChanges.DoNothing - Text.DeinitSubmodule - Text.DeinitSubmodule.Force - Text.DeinitSubmodule.Path -- Text.DeleteBranch.AskForRemote - Text.DeleteBranch.Force - Text.DeleteMultiTags - Text.DeleteMultiTags.DeleteFromRemotes - Text.DeleteMultiTags.Tip +- Text.Diff.Binary.ViewContentOfNew - Text.Diff.EmptyFile - Text.Diff.Image.Blend - Text.Diff.Image.Difference @@ -1356,6 +1839,9 @@ This document shows the translation status of each locale file in the repository - Text.EditBranchDescription.Target - Text.ExecuteCustomAction.Target - Text.ExecuteCustomAction.Repository +- Text.File.Binary +- Text.File.HexViewer +- Text.File.HexViewer.GotoAddress - Text.FileCM.CustomAction - Text.FileModeChange - Text.FileModeChange.Deleted @@ -1374,11 +1860,13 @@ This document shows the translation status of each locale file in the repository - Text.GitLFS.Locks.UnlockAllMyLocks - Text.GitLFS.Locks.UnlockAllMyLocks.Confirm - Text.GotoRevisionSelector +- Text.Histories.AdvancedOptions - Text.Histories.HighlightsInGraph - Text.Histories.HighlightsInGraph.All - Text.Histories.HighlightsInGraph.CurrentBranchOnly - Text.Histories.HighlightsInGraph.CurrentBranchAndSelectedCommits - Text.Histories.HighlightsInGraph.SelectedCommitsOnly +- Text.Histories.HighlightsInGraph.SelectedCommitsOnlyFirstParent - Text.Histories.ShowColumns - Text.HistoriesDetailsStandalone - Text.HistoriesDetailsStandalone.CommitDetail @@ -1391,7 +1879,9 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Repo.GoToChild - Text.Hotkeys.Repo.GoToParent - Text.Hotkeys.Repo.OpenCommandPalette -- Text.Hotkeys.Repo.ToggleHistoriesDetailPanel +- Text.Hotkeys.Repo.ToggleCommitDetailPanel +- Text.Hotkeys.Repo.ToggleSearchCommits +- Text.Hotkeys.Repo.ViewDashboard - Text.Init.CommandTip - Text.Init.ErrorMessageTip - Text.InteractiveRebase.NoVerify @@ -1428,6 +1918,8 @@ This document shows the translation status of each locale file in the repository - Text.No - Text.Open - Text.Open.SystemDefaultEditor +- Text.OpenAppDataDir.Cache +- Text.OpenAppDataDir.Config - Text.OpenFile - Text.OpenLocalRepository - Text.OpenLocalRepository.Bookmark @@ -1436,9 +1928,11 @@ This document shows the translation status of each locale file in the repository - Text.PageTabBar.Tab.MoveToWorkspace - Text.PageTabBar.Tab.Refresh - Text.Preferences.AI.AdditionalPrompt +- Text.Preferences.AI.ExtraHeaders - Text.Preferences.AI.Model - Text.Preferences.AI.Model.AutoFetchAvailableModels - Text.Preferences.AI.ReadApiKeyFromEnv +- Text.Preferences.AI.ReasoningEffortLevel - Text.Preferences.Appearance.UseAutoHideScrollBars - Text.Preferences.DiffMerge.DiffArgs - Text.Preferences.DiffMerge.DiffArgs.Tip @@ -1466,21 +1960,23 @@ This document shows the translation status of each locale file in the repository - Text.Rebase.Test.OK - Text.Rebase.Test.UnknownError - Text.Rebase.Test.WillCauseConflicts +- Text.Remote.ResetPushURL - Text.RemoteCM.CustomAction - Text.RemoteCM.EnableAutoFetch +- Text.RemoteCM.SetPushURL - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate - Text.Repository.BranchSort.ByName - Text.Repository.ClearStashes -- Text.Repository.Dashboard - Text.Repository.FilterCommits.Collapse - Text.Repository.FilterCommits.Expand - Text.Repository.FilterCommits.Summary -- Text.Repository.MoreOptions - Text.Repository.OpenAsFolder - Text.Repository.Resolve - Text.Repository.Search.ByContent - Text.Repository.Search.ByPath +- Text.Repository.Search.Method +- Text.Repository.Search.ToolTip - Text.Repository.ShowDecoratedCommitsOnly - Text.Repository.ShowFirstParentOnly - Text.Repository.ShowFlags @@ -1494,15 +1990,41 @@ This document shows the translation status of each locale file in the repository - Text.ScanRepositories.UseCustomDir - Text.SelfUpdate.CurrentVersion - Text.SelfUpdate.ReleaseDate +- Text.SetPushUrl +- Text.SetPushUrl.PushURL +- Text.SetPushUrl.Remote +- Text.SetPushUrl.URL - Text.SetSubmoduleBranch - Text.SetSubmoduleBranch.Submodule - Text.SetSubmoduleBranch.Current - Text.SetSubmoduleBranch.New - Text.SetSubmoduleBranch.New.Tip +- Text.SSHKeyHelper +- Text.SSHKeyHelper.ConfirmDeletion +- Text.SSHKeyHelper.Fingerprint +- Text.SSHKeyHelper.Generator +- Text.SSHKeyHelper.Generator.ConfirmPassphrase +- Text.SSHKeyHelper.Generator.Email +- Text.SSHKeyHelper.Generator.Email.Placeholder +- Text.SSHKeyHelper.Generator.Name +- Text.SSHKeyHelper.Generator.Name.Placeholder +- Text.SSHKeyHelper.Generator.Passphrase +- Text.SSHKeyHelper.Generator.Start +- Text.SSHKeyHelper.Generator.Type +- Text.SSHKeyHelper.Generator.UsePassphrase +- Text.SSHKeyHelper.Path +- Text.SSHKeyHelper.PublicKey +- Text.SSHKeyHelper.Title - Text.Stash.Mode - Text.StashCM.ApplyFileChanges - Text.StashCM.Branch - Text.StashCM.CopyMessage +- Text.Statistics.Commits +- Text.Statistics.Tips +- Text.Statistics.TopAuthors +- Text.Statistics.TopAuthors.Count +- Text.Statistics.TopAuthors.Percentage +- Text.Statistics.TopAuthors.Tip - Text.Submodule.Branch - Text.Submodule.CopyBranch - Text.Submodule.Deinit @@ -1532,6 +2054,12 @@ This document shows the translation status of each locale file in the repository - Text.TagCM.CustomAction - Text.TagCM.DeleteMultiple - Text.TagCM.Merge +- Text.Terminate +- Text.TrustRepository +- Text.TrustRepository.CommandTip +- Text.TrustRepository.Description +- Text.TrustRepository.Path +- Text.TrustRepository.ScanSkipped - Text.UpdateSubmodules.Recursive - Text.UpdateSubmodules.UpdateToRemoteTrackingBranch - Text.ViewLogs @@ -1542,9 +2070,12 @@ This document shows the translation status of each locale file in the repository - Text.WorkingCopy.AddToGitIgnore.UntrackedInSameFolder - Text.WorkingCopy.ClearCommitHistories - Text.WorkingCopy.ClearCommitHistories.Confirm +- Text.WorkingCopy.CollapseSidebar - Text.WorkingCopy.ConfirmCommitWithDetachedHead - Text.WorkingCopy.Conflicts.Merge - Text.WorkingCopy.Conflicts.MergeExternal +- Text.WorkingCopy.DiscardAll +- Text.WorkingCopy.ExpandSidebar - Text.WorkingCopy.FilterChanges - Text.WorkingCopy.NoVerify - Text.WorkingCopy.ResetAuthor diff --git a/VERSION b/VERSION index a5780a001d..bb71f1638b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2026.16 \ No newline at end of file +2026.22 \ No newline at end of file diff --git a/build/resources/flatpak/sourcegit.desktop b/build/resources/flatpak/sourcegit.desktop index f1213e6f6d..9cf9665be9 100644 --- a/build/resources/flatpak/sourcegit.desktop +++ b/build/resources/flatpak/sourcegit.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Name=SourceGit Comment=Open-source & Free Git GUI Client -Exec=sourcegit.sh +Exec=sourcegit Icon=io.github.sourcegit_scm.sourcegit Terminal=false Type=Application diff --git a/build/scripts/package.linux.sh b/build/scripts/package.linux.sh index 262f141ae8..e4645b7213 100755 --- a/build/scripts/package.linux.sh +++ b/build/scripts/package.linux.sh @@ -51,7 +51,9 @@ ln -rsf SourceGit.AppDir/opt/sourcegit/sourcegit SourceGit.AppDir/AppRun ln -rsf SourceGit.AppDir/usr/share/applications/com.sourcegit_scm.SourceGit.desktop SourceGit.AppDir cp resources/appimage/sourcegit.appdata.xml SourceGit.AppDir/usr/share/metainfo/com.sourcegit_scm.SourceGit.appdata.xml -ARCH="$appimage_arch" ./appimagetool -v SourceGit.AppDir "sourcegit-$VERSION.linux.$arch.AppImage" +ARCH="$appimage_arch" ./appimagetool -v \ + -u "gh-releases-zsync|sourcegit-scm|sourcegit|latest|sourcegit-*.linux.$arch.AppImage.zsync" \ + SourceGit.AppDir "sourcegit-$VERSION.linux.$arch.AppImage" mkdir -p resources/deb/opt/sourcegit/ mkdir -p resources/deb/usr/bin diff --git a/build/scripts/package.osx-app.sh b/build/scripts/package.osx-app.sh index 8f3ddc77c8..78c31114f4 100755 --- a/build/scripts/package.osx-app.sh +++ b/build/scripts/package.osx-app.sh @@ -14,4 +14,14 @@ sed "s/SOURCE_GIT_VERSION/$VERSION/g" resources/app/App.plist > SourceGit.app/Co rm -rf SourceGit.app/Contents/MacOS/SourceGit.dsym rm -f SourceGit.app/Contents/MacOS/*.pdb +case "$RUNTIME" in + osx-arm64) ARCH=arm64 ;; + osx-x64) ARCH=x86_64 ;; + *) echo "Unsupported RUNTIME: $RUNTIME" >&2; exit 1 ;; +esac + +clang -arch "$ARCH" ../tools/setsid-macos/setsid.c -o SourceGit.app/Contents/MacOS/setsid -mmacosx-version-min=13.0 +codesign --force --deep --sign - SourceGit.app +codesign --verify --deep --strict --verbose=2 SourceGit.app + zip "sourcegit_$VERSION.$RUNTIME.zip" -r SourceGit.app diff --git a/depends/AvaloniaEdit b/depends/AvaloniaEdit index 11bf2b6a82..ddf00a91dc 160000 --- a/depends/AvaloniaEdit +++ b/depends/AvaloniaEdit @@ -1 +1 @@ -Subproject commit 11bf2b6a8265329974606604ecc96ece8bfbc0b9 +Subproject commit ddf00a91dc0da29b85c79b85c9583860213a99ff diff --git a/screenshots/theme_dark.png b/screenshots/theme_dark.png index 6ecde949b7..bd8fcf339e 100644 Binary files a/screenshots/theme_dark.png and b/screenshots/theme_dark.png differ diff --git a/screenshots/theme_light.png b/screenshots/theme_light.png index 205e38d6b8..baa54ef722 100644 Binary files a/screenshots/theme_light.png and b/screenshots/theme_light.png differ diff --git a/src/AI/Agent.cs b/src/AI/Agent.cs index a018390634..178683ec68 100644 --- a/src/AI/Agent.cs +++ b/src/AI/Agent.cs @@ -21,6 +21,11 @@ public async Task GenerateCommitMessageAsync(string repo, string currentBranch, throw new Exception("Failed to fetch available models from this service. Please check your configuration and try again."); var options = new ChatCompletionOptions() { Tools = { ChatTools.GetDetailChangesInFile } }; +#pragma warning disable OPENAI001 + if (!_service.ReasoningEffortLevel.Equals(Options.IgnoredReasoningEffortLevel, StringComparison.OrdinalIgnoreCase)) + options.ReasoningEffortLevel = new ChatReasoningEffortLevel(_service.ReasoningEffortLevel); +#pragma warning restore OPENAI001 + var userMessageBuilder = new StringBuilder(); userMessageBuilder .AppendLine("Generate a commit message (follow the rule of conventional commit message) for given git repository.") diff --git a/src/AI/ExtraHeadersPolicy.cs b/src/AI/ExtraHeadersPolicy.cs new file mode 100644 index 0000000000..a14924ba64 --- /dev/null +++ b/src/AI/ExtraHeadersPolicy.cs @@ -0,0 +1,48 @@ +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SourceGit.AI +{ + public class ExtraHeadersPolicy : PipelinePolicy + { + public ExtraHeadersPolicy(string headers) + { + _headers = headers; + } + + public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) + { + Apply(message); + ProcessNext(message, pipeline, currentIndex); + } + + public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) + { + Apply(message); + await ProcessNextAsync(message, pipeline, currentIndex); + } + + private void Apply(PipelineMessage message) + { + var lines = _headers.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var line in lines) + { + var idx = line.IndexOf(':'); + if (idx < 0) + continue; + + var key = line.Substring(0, idx).Trim(); + var value = line.Substring(idx + 1).Trim(); + if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) + continue; + + message.Request.Headers.Add(key, value); + } + } + + private readonly string _headers; + } +} diff --git a/src/AI/Options.cs b/src/AI/Options.cs new file mode 100644 index 0000000000..e83b72caad --- /dev/null +++ b/src/AI/Options.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace SourceGit.AI +{ + public static class Options + { + public static readonly string IgnoredReasoningEffortLevel = "unspecified"; + public static readonly List ReasoningEffortLevels = [ + IgnoredReasoningEffortLevel, + "none", + "minimal", + "low", + "medium", + "high", + ]; + } +} diff --git a/src/AI/Service.cs b/src/AI/Service.cs index 7d311c6c04..abc1c63a12 100644 --- a/src/AI/Service.cs +++ b/src/AI/Service.cs @@ -1,7 +1,9 @@ using System; using System.ClientModel; +using System.ClientModel.Primitives; using System.Collections.Generic; using System.Text.Json.Serialization; + using Azure.AI.OpenAI; using CommunityToolkit.Mvvm.ComponentModel; using OpenAI; @@ -54,12 +56,24 @@ public bool AutoFetchAvailableModels set => SetProperty(ref _autoFetchAvailableModels, value); } + public string ReasoningEffortLevel + { + get => _reasoningEffortLevel; + set => SetProperty(ref _reasoningEffortLevel, value); + } + public string AdditionalPrompt { get; set; } = string.Empty; + public string ExtraHeaders + { + get; + set; + } = string.Empty; + public void FetchAvailableModels() { if (!_autoFetchAvailableModels) @@ -86,13 +100,26 @@ public ChatClient GetChatClient() private OpenAIClient GetOpenAIClient() { var credential = new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey); - return Server.Contains("openai.azure.com/", StringComparison.Ordinal) - ? new AzureOpenAIClient(new Uri(Server), credential) - : new OpenAIClient(credential, new() { Endpoint = new Uri(Server) }); + + if (Server.Contains("openai.azure.com/", StringComparison.Ordinal)) + { + if (string.IsNullOrEmpty(ExtraHeaders)) + return new AzureOpenAIClient(new Uri(Server), credential); + + var azureOptions = new AzureOpenAIClientOptions(); + azureOptions.AddPolicy(new ExtraHeadersPolicy(ExtraHeaders), PipelinePosition.PerCall); + return new AzureOpenAIClient(new Uri(Server), credential, azureOptions); + } + + var options = new OpenAIClientOptions() { Endpoint = new Uri(Server) }; + if (!string.IsNullOrEmpty(ExtraHeaders)) + options.AddPolicy(new ExtraHeadersPolicy(ExtraHeaders), PipelinePosition.PerCall); + return new OpenAIClient(credential, options); } private string _name = string.Empty; private string _model = string.Empty; + private string _reasoningEffortLevel = Options.IgnoredReasoningEffortLevel; private bool _autoFetchAvailableModels = true; } } diff --git a/src/App.Commands.cs b/src/App.Commands.cs index 06c0afbf12..ad92a5755f 100644 --- a/src/App.Commands.cs +++ b/src/App.Commands.cs @@ -55,9 +55,14 @@ public static bool IsCheckForUpdateCommandVisible } }); + public static readonly Command OpenAppConfigDirCommand = new Command(_ => + { + Native.OS.OpenInFileManager(Native.OS.BasicDirectories.ConfigDir); + }); + public static readonly Command OpenAppDataDirCommand = new Command(_ => { - Native.OS.OpenInFileManager(Native.OS.DataDir); + Native.OS.OpenInFileManager(Native.OS.BasicDirectories.CacheDir); }); public static readonly Command OpenAboutCommand = new Command(async _ => @@ -96,5 +101,15 @@ public static bool IsCheckForUpdateCommandVisible if (OperatingSystem.IsMacOS()) Native.MacOSUtilities.ShowAllApplications(); }); + + public static readonly Command OpenSSHKeyHelperCommand = new Command(async _ => + { + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) + { + var vm = new ViewModels.SSHKeyHelper(); + var dialog = new Views.SSHKeyHelper() { DataContext = vm }; + await dialog.ShowDialog(owner); + } + }); } } diff --git a/src/App.axaml b/src/App.axaml index bfc05cfb4b..2663e44ad4 100644 --- a/src/App.axaml +++ b/src/App.axaml @@ -8,6 +8,7 @@ + @@ -44,6 +45,7 @@ + diff --git a/src/App.axaml.cs b/src/App.axaml.cs index a22f764d6f..66882b4869 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -23,7 +23,7 @@ public partial class App : Application [STAThread] public static void Main(string[] args) { - Native.OS.SetupDataDir(); + Native.OS.SetupBasicDirectories(); AppDomain.CurrentDomain.UnhandledException += (_, e) => { @@ -350,7 +350,7 @@ private bool TryLaunchAsFileHistoryViewer(IClassicDesktopStyleApplicationLifetim var test = new Commands.QueryRepositoryRootPath(dir).GetResult(); if (!test.IsSuccess || string.IsNullOrEmpty(test.StdOut)) { - Console.Out.WriteLine($"'{args[1]}' is not in a valid git repository"); + Console.Out.WriteLine(string.IsNullOrWhiteSpace(test.StdErr) ? $"'{args[1]}' is not in a valid git repository" : test.StdErr.Trim()); desktop.Shutdown(-1); return true; } @@ -395,7 +395,7 @@ private bool TryLaunchAsBlameViewer(IClassicDesktopStyleApplicationLifetime desk var test = new Commands.QueryRepositoryRootPath(dir).GetResult(); if (!test.IsSuccess || string.IsNullOrEmpty(test.StdOut)) { - Console.Out.WriteLine($"'{args[1]}' is not in a valid git repository"); + Console.Out.WriteLine(string.IsNullOrWhiteSpace(test.StdErr) ? $"'{args[1]}' is not in a valid git repository" : test.StdErr.Trim()); desktop.Shutdown(-1); return true; } diff --git a/src/Commands/AddSafeDirectory.cs b/src/Commands/AddSafeDirectory.cs new file mode 100644 index 0000000000..52e190db69 --- /dev/null +++ b/src/Commands/AddSafeDirectory.cs @@ -0,0 +1,11 @@ +namespace SourceGit.Commands +{ + public class AddSafeDirectory : Command + { + public AddSafeDirectory(string ctx, string value) + { + Context = ctx; + Args = $"config --global --add safe.directory {value.Quoted()}"; + } + } +} diff --git a/src/Commands/Blame.cs b/src/Commands/Blame.cs index 0f247d736f..a0bc26ee4c 100644 --- a/src/Commands/Blame.cs +++ b/src/Commands/Blame.cs @@ -37,7 +37,7 @@ public Blame(string repo, string file, string revision, bool ignoreWhitespace) ParseLine(line); if (_result.IsBinary) - break; + return _result; } if (_needUnifyCommitSHA) @@ -49,6 +49,9 @@ public Blame(string repo, string file, string revision, bool ignoreWhitespace) } } + if (_content.Length > 0) + _content.Length--; // Remove the last newline character + _result.Content = _content.ToString(); return _result; } @@ -66,7 +69,8 @@ private void ParseLine(string line) if (!match.Success) return; - _content.AppendLine(match.Groups[5].Value); + // Force using `\n` as line-ending + _content.Append(match.Groups[5].Value).Append('\n'); var commit = match.Groups[1].Value; var file = match.Groups[2].Value.Trim(); diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index 977c81012d..14430805c2 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -62,7 +62,7 @@ public async Task ExecAsync() lock (capturedLock) { if (captured is { Process: { HasExited: false } }) - captured.Process.Kill(true); + Native.OS.TerminateProcess(captured.Process); } }); } @@ -158,8 +158,36 @@ protected async Task ReadToEndAsync() protected ProcessStartInfo CreateGitStartInfo(bool redirect) { + var useSetSid = CancellationToken.CanBeCanceled && Native.OS.SupportSetSid(); + var selfExecFile = Environment.ProcessPath; + var builder = new StringBuilder(2048); + + if (useSetSid) + builder.Append(Native.OS.GitExecutable.Quoted()).Append(' '); + + builder + .Append("--no-pager -c core.quotepath=off -c credential.helper=") + .Append(Native.OS.CredentialHelper) + .Append(' '); + + switch (Editor) + { + case EditorType.CoreEditor: + builder.Append($"""-c core.editor="\"{selfExecFile}\" --core-editor" """); + break; + case EditorType.RebaseEditor: + builder.Append($"""-c core.editor="\"{selfExecFile}\" --rebase-message-editor" -c sequence.editor="\"{selfExecFile}\" --rebase-todo-editor" -c rebase.abbreviateCommands=true """); + break; + default: + builder.Append("-c core.editor=true "); + break; + } + + builder.Append(Args); + var start = new ProcessStartInfo(); - start.FileName = Native.OS.GitExecutable; + start.FileName = useSetSid ? Native.OS.GetSetSidExecutable() : Native.OS.GitExecutable; + start.Arguments = builder.ToString(); start.UseShellExecute = false; start.CreateNoWindow = true; @@ -172,7 +200,6 @@ protected ProcessStartInfo CreateGitStartInfo(bool redirect) } // Force using this app as SSH askpass program - var selfExecFile = Environment.ProcessPath; start.Environment.Add("SSH_ASKPASS", selfExecFile); // Can not use parameter here, because it invoked by SSH with `exec` start.Environment.Add("SSH_ASKPASS_REQUIRE", "prefer"); start.Environment.Add("SOURCEGIT_LAUNCH_AS_ASKPASS", "TRUE"); @@ -181,37 +208,15 @@ protected ProcessStartInfo CreateGitStartInfo(bool redirect) // If an SSH private key was provided, sets the environment. if (!start.Environment.ContainsKey("GIT_SSH_COMMAND") && !string.IsNullOrEmpty(SSHKey)) - start.Environment.Add("GIT_SSH_COMMAND", $"ssh -i '{SSHKey}' -F '/dev/null'"); + start.Environment.Add("GIT_SSH_COMMAND", $"ssh -i '{SSHKey}' -o AddKeysToAgent=yes"); - // Force using en_US.UTF-8 locale - if (OperatingSystem.IsLinux()) + // Force the C locale on Unix, so that git's output (including fatal errors) can always be parsed. + if (!OperatingSystem.IsWindows()) { start.Environment.Add("LANG", "C"); start.Environment.Add("LC_ALL", "C"); } - var builder = new StringBuilder(2048); - builder - .Append("--no-pager -c core.quotepath=off -c credential.helper=") - .Append(Native.OS.CredentialHelper) - .Append(' '); - - switch (Editor) - { - case EditorType.CoreEditor: - builder.Append($"""-c core.editor="\"{selfExecFile}\" --core-editor" """); - break; - case EditorType.RebaseEditor: - builder.Append($"""-c core.editor="\"{selfExecFile}\" --rebase-message-editor" -c sequence.editor="\"{selfExecFile}\" --rebase-todo-editor" -c rebase.abbreviateCommands=true """); - break; - default: - builder.Append("-c core.editor=true "); - break; - } - - builder.Append(Args); - start.Arguments = builder.ToString(); - // Working directory if (!string.IsNullOrEmpty(WorkingDirectory)) start.WorkingDirectory = WorkingDirectory; diff --git a/src/Commands/Config.cs b/src/Commands/Config.cs index 75f4d738a2..78baee3cdf 100644 --- a/src/Commands/Config.cs +++ b/src/Commands/Config.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Threading.Tasks; namespace SourceGit.Commands @@ -46,7 +45,7 @@ public async Task> ReadAllAsync() Args = "config -l"; var output = await ReadToEndAsync().ConfigureAwait(false); - var rs = new Dictionary(); + var rs = new Dictionary(StringComparer.OrdinalIgnoreCase); if (output.IsSuccess) { var lines = output.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); @@ -55,7 +54,7 @@ public async Task> ReadAllAsync() var parts = line.Split('=', 2); if (parts.Length == 2) { - var key = parts[0].ToLower(CultureInfo.CurrentCulture); // Always use lower case for key + var key = parts[0]; var value = parts[1]; rs[key] = value; } diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index 89248ddc1e..fb008aeee9 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -35,6 +35,9 @@ public partial class Diff : Command private const string SPECIAL_NO_NEWLINE = " No newline at end of file"; private const string SPECIAL_SUBMODULE = "Subproject commit "; + private const int MAX_INLINE_CONTENT_LENGTH = 1024; + private const int MAX_INLINE_CHUNKS_PER_LINE = 16; + public Diff(string repo, Models.DiffOption opt, int numContextLines, bool ignoreWhitespace, bool ignoreCRAtEOL) { _result.TextDiff = new Models.TextDiff(); @@ -330,11 +333,11 @@ private void ProcessInlineHighlights() var left = _deleted[i]; var right = _added[i]; - if (left.Content.Length > 1024 || right.Content.Length > 1024) + if (left.Content.Length > MAX_INLINE_CONTENT_LENGTH || right.Content.Length > MAX_INLINE_CONTENT_LENGTH) continue; var chunks = Models.TextInlineChange.Compare(left.Content, right.Content); - if (chunks.Count > 4) + if (chunks.Count > MAX_INLINE_CHUNKS_PER_LINE) continue; foreach (var chunk in chunks) diff --git a/src/Commands/DoesBranchExistOnRemote.cs b/src/Commands/DoesBranchExistOnRemote.cs new file mode 100644 index 0000000000..3a971c3e8f --- /dev/null +++ b/src/Commands/DoesBranchExistOnRemote.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; + +namespace SourceGit.Commands +{ + public class DoesBranchExistOnRemote : Command + { + public DoesBranchExistOnRemote(string repo, Models.Remote remote, Models.Branch branch) + { + WorkingDirectory = repo; + SSHKey = remote.PrivateSSHKey; + RaiseError = false; + Args = $"ls-remote {remote.Name} {branch.Name}"; + } + + public async Task GetResultAsync() + { + var rs = await ReadToEndAsync(); + return rs.IsSuccess && rs.StdOut.Trim().Length > 0; + } + } +} diff --git a/src/Commands/Fetch.cs b/src/Commands/Fetch.cs index 9143612576..679ffc9ac6 100644 --- a/src/Commands/Fetch.cs +++ b/src/Commands/Fetch.cs @@ -1,53 +1,40 @@ using System.Text; -using System.Threading.Tasks; namespace SourceGit.Commands { public class Fetch : Command { - public Fetch(string repo, string remote, bool noTags, bool force) + public Fetch(string repo, Models.Remote remote, bool noTags, bool force) { - _remote = remote; - WorkingDirectory = repo; Context = repo; + SSHKey = remote.PrivateSSHKey; var builder = new StringBuilder(512); builder.Append("fetch --progress --verbose "); builder.Append(noTags ? "--no-tags " : "--tags "); if (force) builder.Append("--force "); - builder.Append(remote); + builder.Append(remote.Name); Args = builder.ToString(); } - public Fetch(string repo, string remote) + public Fetch(string repo, Models.Remote remote) { - _remote = remote; - WorkingDirectory = repo; Context = repo; + SSHKey = remote.PrivateSSHKey; RaiseError = false; - - Args = $"fetch --progress --verbose {remote}"; + Args = $"fetch --progress --verbose {remote.Name}"; } - public Fetch(string repo, Models.Branch local, Models.Branch remote) + public Fetch(string repo, Models.Remote remote, Models.Branch remoteBranch, Models.Branch local) { - _remote = remote.Remote; - WorkingDirectory = repo; Context = repo; - Args = $"fetch --progress --verbose {remote.Remote} {remote.Name}:{local.Name}"; + SSHKey = remote.PrivateSSHKey; + Args = $"fetch --progress --verbose {remote.Name} {remoteBranch.Name}:{local.Name}"; } - - public async Task RunAsync() - { - SSHKey = await new Config(WorkingDirectory).GetAsync($"remote.{_remote}.sshkey").ConfigureAwait(false); - return await ExecAsync().ConfigureAwait(false); - } - - private readonly string _remote; } } diff --git a/src/Commands/Pull.cs b/src/Commands/Pull.cs index e121445bac..7e8f4f731c 100644 --- a/src/Commands/Pull.cs +++ b/src/Commands/Pull.cs @@ -1,32 +1,26 @@ using System.Text; -using System.Threading.Tasks; namespace SourceGit.Commands { public class Pull : Command { - public Pull(string repo, string remote, string branch, bool useRebase) + public Pull(string repo, Models.Remote remote, Models.Branch remoteBranch, bool useRebase) { - _remote = remote; - WorkingDirectory = repo; Context = repo; + SSHKey = remote.PrivateSSHKey; var builder = new StringBuilder(512); - builder.Append("pull --verbose --progress "); - if (useRebase) - builder.Append("--rebase=true "); - builder.Append(remote).Append(' ').Append(branch); + builder + .Append("pull --verbose --progress --rebase=") + .Append(useRebase ? "true" : "false") + .Append(' ') + .Append(remote.Name); - Args = builder.ToString(); - } + if (remoteBranch != null) + builder.Append(' ').Append(remoteBranch.Name); - public async Task RunAsync() - { - SSHKey = await new Config(WorkingDirectory).GetAsync($"remote.{_remote}.sshkey").ConfigureAwait(false); - return await ExecAsync().ConfigureAwait(false); + Args = builder.ToString(); } - - private readonly string _remote; } } diff --git a/src/Commands/Push.cs b/src/Commands/Push.cs index 44394dc412..e04e81ef94 100644 --- a/src/Commands/Push.cs +++ b/src/Commands/Push.cs @@ -1,16 +1,14 @@ using System.Text; -using System.Threading.Tasks; namespace SourceGit.Commands { public class Push : Command { - public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force) + public Push(string repo, Models.Branch local, Models.Remote remote, Models.Branch remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force, bool noVerify) { - _remote = remote; - WorkingDirectory = repo; Context = repo; + SSHKey = remote.PrivateSSHKey; var builder = new StringBuilder(1024); builder.Append("push --progress --verbose "); @@ -22,33 +20,41 @@ public Push(string repo, string local, string remote, string remoteBranch, bool builder.Append("-u "); if (force) builder.Append("--force-with-lease "); + if (noVerify) + builder.Append("--no-verify "); - builder.Append(remote).Append(' ').Append(local).Append(':').Append(remoteBranch); + builder.Append(remote.Name).Append(' ').Append(local.Name).Append(':').Append(remoteBranch.Name); Args = builder.ToString(); } - public Push(string repo, string remote, string refname, bool isDelete) + public Push(string repo, Models.Commit revision, Models.Remote remote, Models.Branch remoteBranch, bool force) { - _remote = remote; + WorkingDirectory = repo; + Context = repo; + SSHKey = remote.PrivateSSHKey; + + var builder = new StringBuilder(1024); + builder.Append("push --progress --verbose "); + if (force) + builder.Append("--force-with-lease "); + builder.Append(remote.Name).Append(' ').Append(revision.SHA).Append(':').Append(remoteBranch.Name); + Args = builder.ToString(); + } + + public Push(string repo, Models.Remote remote, string refname, bool isDelete) + { WorkingDirectory = repo; Context = repo; + SSHKey = remote.PrivateSSHKey; var builder = new StringBuilder(512); builder.Append("push "); if (isDelete) builder.Append("--delete "); - builder.Append(remote).Append(' ').Append(refname); + builder.Append(remote.Name).Append(' ').Append(refname); Args = builder.ToString(); } - - public async Task RunAsync() - { - SSHKey = await new Config(WorkingDirectory).GetAsync($"remote.{_remote}.sshkey").ConfigureAwait(false); - return await ExecAsync().ConfigureAwait(false); - } - - private readonly string _remote; } } diff --git a/src/Commands/QueryConflictFileState.cs b/src/Commands/QueryConflictFileState.cs index 02578b2183..f9a52ee121 100644 --- a/src/Commands/QueryConflictFileState.cs +++ b/src/Commands/QueryConflictFileState.cs @@ -12,7 +12,7 @@ public QueryConflictFileState(string repo, Models.Change change) WorkingDirectory = repo; Context = repo; - Args = $"diff --no-color --no-ext-diff --full-index --patch {opt}"; + Args = $"diff --no-color --no-ext-diff --no-textconv --full-index --patch {opt}"; } public async Task GetResultAsync() diff --git a/src/Commands/QueryFileSize.cs b/src/Commands/QueryFileSize.cs index c1ad51dfe5..c2afe26504 100644 --- a/src/Commands/QueryFileSize.cs +++ b/src/Commands/QueryFileSize.cs @@ -1,29 +1,21 @@ -using System.Text.RegularExpressions; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace SourceGit.Commands { - public partial class QueryFileSize : Command + public class QueryFileSize : Command { - [GeneratedRegex(@"^\d+\s+\w+\s+[0-9a-f]+\s+(\d+)\s+.*$")] - private static partial Regex REG_FORMAT(); - public QueryFileSize(string repo, string file, string revision) { WorkingDirectory = repo; Context = repo; - Args = $"ls-tree {revision} -l -- {file.Quoted()}"; + Args = $"cat-file -s {revision}:{file.Quoted()}"; } public async Task GetResultAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); - if (rs.IsSuccess) - { - var match = REG_FORMAT().Match(rs.StdOut); - if (match.Success) - return long.Parse(match.Groups[1].Value); - } + if (rs.IsSuccess && long.TryParse(rs.StdOut.Trim(), out var size)) + return size; return 0; } diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs index d1bd30a2c8..7c2e4156ab 100644 --- a/src/Commands/QueryLocalChanges.cs +++ b/src/Commands/QueryLocalChanges.cs @@ -34,9 +34,24 @@ public QueryLocalChanges(string repo, bool includeUntracked = true, bool noOptio try { using var proc = new Process(); + var captured = new CapturedProcess() { Process = proc }; + var capturedLock = new object(); + proc.StartInfo = CreateGitStartInfo(true); proc.Start(); + if (CancellationToken.CanBeCanceled) + { + CancellationToken.Register(() => + { + lock (capturedLock) + { + if (captured is { Process: { HasExited: false } proc }) + Native.OS.TerminateProcess(proc); + } + }); + } + while (await proc.StandardOutput.ReadLineAsync().ConfigureAwait(false) is { } line) { var match = REG_FORMAT().Match(line); @@ -165,6 +180,11 @@ public QueryLocalChanges(string repo, bool includeUntracked = true, bool noOptio if (change.Index != Models.ChangeState.None || change.WorkTree != Models.ChangeState.None) outs.Add(change); } + + lock (capturedLock) + { + captured.Process = null; + } } catch { @@ -173,5 +193,10 @@ public QueryLocalChanges(string repo, bool includeUntracked = true, bool noOptio return outs; } + + private class CapturedProcess + { + public Process Process { get; set; } = null; + } } } diff --git a/src/Commands/QueryRemotes.cs b/src/Commands/QueryRemotes.cs index f8a23b7aab..6adff9b36d 100644 --- a/src/Commands/QueryRemotes.cs +++ b/src/Commands/QueryRemotes.cs @@ -1,73 +1,78 @@ using System; using System.Collections.Generic; -using System.Text.RegularExpressions; using System.Threading.Tasks; namespace SourceGit.Commands { - public partial class QueryRemotes : Command + public class QueryRemotes { - [GeneratedRegex(@"^([\w\.\-]+)\s*(\S+).*$")] - private static partial Regex REG_REMOTE(); - public QueryRemotes(string repo) { - WorkingDirectory = repo; - Context = repo; - Args = "remote -v"; + _repo = repo; } public async Task> GetResultAsync() { - var outs = new List(); - var rs = await ReadToEndAsync().ConfigureAwait(false); - if (!rs.IsSuccess) - return outs; - - var config = await new Config(WorkingDirectory).ReadAllAsync().ConfigureAwait(false); + var names = new HashSet(); + var urls = new Dictionary(); + var pushUrls = new Dictionary(); + var privateSSHKeys = new Dictionary(); var disableAutoFetchRemotes = new HashSet(); + + var config = await new Config(_repo).ReadAllAsync().ConfigureAwait(false); foreach (var (k, v) in config) { - if (k.StartsWith("remote.", StringComparison.Ordinal) && - k.EndsWith(".disableautofetch", StringComparison.Ordinal) && - v.Equals("true", StringComparison.OrdinalIgnoreCase)) + if (!k.StartsWith("remote.", StringComparison.Ordinal)) + continue; + + if (k.EndsWith(".url", StringComparison.Ordinal)) + { + var name = k.Substring(7, k.Length - 11).Trim('"'); + names.Add(name); + urls[name] = v; + } + else if (k.EndsWith(".pushurl", StringComparison.OrdinalIgnoreCase)) + { + var name = k.Substring(7, k.Length - 15).Trim('"'); + names.Add(name); + pushUrls[name] = v; + } + else if (k.EndsWith(".sshkey", StringComparison.OrdinalIgnoreCase)) { - var remoteName = k.Substring(7, k.Length - 24).Trim('"'); - disableAutoFetchRemotes.Add(remoteName); + var name = k.Substring(7, k.Length - 14).Trim('"'); + names.Add(name); + privateSSHKeys[name] = v; + } + else if (k.EndsWith(".disableautofetch", StringComparison.OrdinalIgnoreCase) && + v.Equals("true", StringComparison.OrdinalIgnoreCase)) + { + var name = k.Substring(7, k.Length - 24).Trim('"'); + disableAutoFetchRemotes.Add(name); } } - var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); - foreach (var line in lines) + var remotes = new List(); + foreach (var name in names) { - var match = REG_REMOTE().Match(line); - if (!match.Success) + if (!urls.TryGetValue(name, out var url)) continue; - var remote = new Models.Remote() + var r = new Models.Remote() { - Name = match.Groups[1].Value, - URL = match.Groups[2].Value, - DisableAutoFetch = disableAutoFetchRemotes.Contains(match.Groups[1].Value) + Name = name, + URL = url, + PushURL = pushUrls.TryGetValue(name, out var pushURL) ? pushURL : null, + PrivateSSHKey = privateSSHKeys.TryGetValue(name, out var privateSSHKey) ? privateSSHKey : null, + DisableAutoFetch = disableAutoFetchRemotes.Contains(name) }; - if (outs.Find(x => x.Name == remote.Name) != null) - continue; - - if (remote.URL.StartsWith("git@", StringComparison.Ordinal)) - { - var hostEnd = remote.URL.IndexOf(':', 4); - if (hostEnd > 4) - { - var host = remote.URL.Substring(4, hostEnd - 4); - Models.HTTPSValidator.Add(host); - } - } - - outs.Add(remote); + remotes.Add(r); } - return outs; + remotes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal)); + return remotes; } + + private readonly string _repo; } } diff --git a/src/Commands/QueryRepositoryStatus.cs b/src/Commands/QueryRepositoryStatus.cs index 3d6adac8e1..8244afb215 100644 --- a/src/Commands/QueryRepositoryStatus.cs +++ b/src/Commands/QueryRepositoryStatus.cs @@ -17,7 +17,7 @@ public QueryRepositoryStatus(string repo) public async Task GetResultAsync() { - Args = "status --porcelain=v2 -b"; + Args = "status --porcelain=v2 -b -uall --ignore-submodules=dirty"; var rs = await ReadToEndAsync().ConfigureAwait(false); if (!rs.IsSuccess) return null; @@ -36,12 +36,17 @@ public QueryRepositoryStatus(string repo) else status.CurrentBranch = head; - if (count == 4 && lines[3].StartsWith("# branch.ab ", StringComparison.Ordinal)) - ParseTrackStatus(status, lines[3].Substring(12).Trim()); + if (count >= 4) + { + var changesCount = count - 3; + if (lines[3].StartsWith("# branch.ab ", StringComparison.Ordinal)) + { + ParseTrackStatus(status, lines[3].Substring(12).Trim()); + changesCount--; + } - status.LocalChanges = await new CountLocalChanges(WorkingDirectory, true) { RaiseError = false } - .GetResultAsync() - .ConfigureAwait(false); + status.LocalChanges = changesCount; + } return status; } diff --git a/src/Commands/QuerySubmoduleRevision.cs b/src/Commands/QuerySubmoduleRevision.cs index a70f1efb68..a61f2d880a 100644 --- a/src/Commands/QuerySubmoduleRevision.cs +++ b/src/Commands/QuerySubmoduleRevision.cs @@ -28,12 +28,12 @@ public QuerySubmoduleRevision(string repo, string revision) var rs = await ReadToEndAsync().ConfigureAwait(false); if (!rs.IsSuccess || string.IsNullOrEmpty(rs.StdOut)) - return null; + return new Models.RevisionSubmodule(_revision); var commit = new Models.Commit(); var lines = rs.StdOut.Split('\0'); if (lines.Length < 9) - return null; + return new Models.RevisionSubmodule(_revision); commit.SHA = lines[0]; if (!string.IsNullOrEmpty(lines[1])) @@ -46,19 +46,14 @@ public QuerySubmoduleRevision(string repo, string revision) commit.CommitterTime = ulong.Parse(lines[6]); commit.Subject = lines[7]; - var message = new Models.CommitFullMessage() { Message = lines[8].TrimEnd() }; + var message = lines[8].TrimEnd(); var uncommittedChangesCount = 0; if (_hasUncommittedChange) uncommittedChangesCount = await new CountLocalChanges(WorkingDirectory, true) .GetResultAsync() .ConfigureAwait(false); - return new Models.RevisionSubmodule() - { - Commit = commit, - FullMessage = message, - UncommittedChanges = uncommittedChangesCount - }; + return new Models.RevisionSubmodule(commit, message, uncommittedChangesCount); } private bool _hasUncommittedChange = false; diff --git a/src/Commands/Remote.cs b/src/Commands/Remote.cs index 6a15037740..f7296b1cef 100644 --- a/src/Commands/Remote.cs +++ b/src/Commands/Remote.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Text; +using System.Threading.Tasks; namespace SourceGit.Commands { @@ -28,33 +29,25 @@ public async Task RenameAsync(string name, string to) return await ExecAsync(); } - public async Task PruneAsync(string name) + public async Task PruneAsync(string name, string sshKey) { + SSHKey = sshKey; Args = $"remote prune {name}"; return await ExecAsync(); } - public async Task GetURLAsync(string name, bool isPush) + public async Task SetURLAsync(string name, string url, bool isDelete, bool isPush) { - Args = "remote get-url" + (isPush ? " --push " : " ") + name; + var builder = new StringBuilder(); + builder.Append("remote set-url "); + if (isDelete) + builder.Append("--delete "); + if (isPush) + builder.Append("--push "); + builder.Append(name).Append(' ').Append(url); - var rs = await ReadToEndAsync(); - return rs.IsSuccess ? rs.StdOut.Trim() : string.Empty; - } - - public async Task SetURLAsync(string name, string url, bool isPush) - { - Args = "remote set-url" + (isPush ? " --push " : " ") + $"{name} {url}"; + Args = builder.ToString(); return await ExecAsync(); } - - public async Task HasBranchAsync(string remote, string branch) - { - SSHKey = await new Config(WorkingDirectory).GetAsync($"remote.{remote}.sshkey"); - Args = $"ls-remote {remote} {branch}"; - - var rs = await ReadToEndAsync(); - return rs.IsSuccess && rs.StdOut.Trim().Length > 0; - } } } diff --git a/src/Commands/SaveRevisionFile.cs b/src/Commands/SaveRevisionFile.cs index 410d15b07c..6c810289d1 100644 --- a/src/Commands/SaveRevisionFile.cs +++ b/src/Commands/SaveRevisionFile.cs @@ -16,7 +16,7 @@ public static async Task RunAsync(string repo, string revision, string file, str var isLFSFiltered = await new IsLFSFiltered(repo, revision, file).GetResultAsync().ConfigureAwait(false); if (isLFSFiltered) { - var pointerStream = await QueryFileContent.RunAsync(repo, revision, file).ConfigureAwait(false); + await using var pointerStream = await QueryFileContent.RunAsync(repo, revision, file).ConfigureAwait(false); await ExecCmdAsync(repo, "lfs smudge", saveTo, pointerStream).ConfigureAwait(false); } else diff --git a/src/Commands/Worktree.cs b/src/Commands/Worktree.cs index 7b70e2ab49..137bb3f7f1 100644 --- a/src/Commands/Worktree.cs +++ b/src/Commands/Worktree.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; using System.Threading.Tasks; @@ -61,20 +62,33 @@ public Worktree(string repo) return worktrees; } - public async Task AddAsync(string fullpath, string name, bool createNew, string tracking) + public async Task AddAsync(string fullpath, string name) + { + if (string.IsNullOrEmpty(name)) + Args = $"worktree add {fullpath.Quoted()}"; + else + Args = $"worktree add -b {name} {fullpath.Quoted()}"; + + return await ExecAsync().ConfigureAwait(false); + } + + public async Task AddWithLocalBranchAsync(string fullpath, string localBranch) + { + Args = $"worktree add -B {localBranch} {fullpath.Quoted()} {localBranch}"; + return await ExecAsync().ConfigureAwait(false); + } + + public async Task AddWithRemoteBranchAsync(string fullpath, string name, string remoteBranch, bool tracking) { var builder = new StringBuilder(1024); builder.Append("worktree add "); - if (!string.IsNullOrEmpty(tracking)) - builder.Append("--track "); - if (!string.IsNullOrEmpty(name)) - builder.Append(createNew ? "-b " : "-B ").Append(name).Append(' '); - builder.Append(fullpath.Quoted()).Append(' '); - - if (!string.IsNullOrEmpty(tracking)) - builder.Append(tracking); - else if (!string.IsNullOrEmpty(name) && !createNew) - builder.Append(name); + + if (tracking) + builder.Append("--track -b ").Append(string.IsNullOrEmpty(name) ? Path.GetFileName(fullpath) : name).Append(' '); + else if (!string.IsNullOrEmpty(name)) + builder.Append("-b ").Append(name).Append(' '); + + builder.Append(fullpath.Quoted()).Append(' ').Append(remoteBranch); Args = builder.ToString(); return await ExecAsync().ConfigureAwait(false); diff --git a/src/Converters/IntConverters.cs b/src/Converters/IntConverters.cs index f44fa96d5a..4d6c723a8d 100644 --- a/src/Converters/IntConverters.cs +++ b/src/Converters/IntConverters.cs @@ -14,13 +14,16 @@ public static class IntConverters public static readonly FuncValueConverter IsZero = new(v => v == 0); - public static readonly FuncValueConverter IsNotOne = - new(v => v != 1); - public static readonly FuncValueConverter ToTreeMargin = new(v => new Thickness(v * 16, 0, 0, 0)); public static readonly FuncValueConverter ToUnsolvedDesc = new(v => v == 0 ? App.Text("MergeConflictEditor.AllResolved") : App.Text("MergeConflictEditor.ConflictsRemaining", v)); + + public static readonly FuncValueConverter ToBadgeWithLimit = + new(v => v > 999 ? "999+" : v.ToString()); + + public static readonly FuncValueConverter IsMaxCountOfStatistics = + new(v => v >= 200000); } } diff --git a/src/Converters/LongConverters.cs b/src/Converters/LongConverters.cs index 05a55e95bd..fb72aadb79 100644 --- a/src/Converters/LongConverters.cs +++ b/src/Converters/LongConverters.cs @@ -18,6 +18,11 @@ public static class LongConverters return $"{(bytes / GB):G3} GB ({bytes:N0})"; }); + public static readonly FuncValueConverter IsGreaterThanZero = new(bytes => + { + return bytes > 0; + }); + private const double KB = 1024; private const double MB = 1024 * 1024; private const double GB = 1024 * 1024 * 1024; diff --git a/src/Converters/ObjectConverters.cs b/src/Converters/ObjectConverters.cs deleted file mode 100644 index f7c57764dd..0000000000 --- a/src/Converters/ObjectConverters.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Globalization; -using Avalonia.Data.Converters; - -namespace SourceGit.Converters -{ - public static class ObjectConverters - { - public class IsTypeOfConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value == null || parameter == null) - return false; - - return value.GetType().IsAssignableTo((Type)parameter); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return new NotImplementedException(); - } - } - - public static readonly IsTypeOfConverter IsTypeOf = new IsTypeOfConverter(); - } -} diff --git a/src/Models/AvatarManager.cs b/src/Models/AvatarManager.cs index 9616ac64d2..7e4593fd86 100644 --- a/src/Models/AvatarManager.cs +++ b/src/Models/AvatarManager.cs @@ -38,13 +38,13 @@ public static AvatarManager Instance private readonly Lock _synclock = new(); private string _storePath; private List _avatars = new List(); - private Dictionary _resources = new Dictionary(); + private Cache _cache = new Cache(256); private HashSet _requesting = new HashSet(); private HashSet _defaultAvatars = new HashSet(); public void Start() { - _storePath = Path.Combine(Native.OS.DataDir, "avatars"); + _storePath = Path.Combine(Native.OS.BasicDirectories.CacheDir, "avatars"); if (!Directory.Exists(_storePath)) Directory.CreateDirectory(_storePath); @@ -120,7 +120,7 @@ public void Start() Dispatcher.UIThread.Post(() => { - _resources[email] = img; + _cache.AddOrUpdate(email, img); NotifyResourceChanged(email, img); }); } @@ -146,7 +146,7 @@ public Bitmap Request(string email, bool forceRefetch) if (_defaultAvatars.Contains(email)) return null; - _resources.Remove(email); + _cache.Remove(email); var localFile = Path.Combine(_storePath, GetEmailHash(email)); if (File.Exists(localFile)) @@ -156,7 +156,7 @@ public Bitmap Request(string email, bool forceRefetch) } else { - if (_resources.TryGetValue(email, out var value)) + if (_cache.TryGet(email, out var value)) return value; var localFile = Path.Combine(_storePath, GetEmailHash(email)); @@ -167,7 +167,7 @@ public Bitmap Request(string email, bool forceRefetch) using (var stream = File.OpenRead(localFile)) { var img = Bitmap.DecodeToWidth(stream, 128); - _resources.Add(email, img); + _cache.AddOrUpdate(email, img); return img; } } @@ -197,7 +197,7 @@ public void SetFromLocal(string email, string file) image = Bitmap.DecodeToWidth(stream, 128); } - _resources[email] = image; + _cache.AddOrUpdate(email, image); lock (_synclock) { @@ -217,7 +217,7 @@ public void SetFromLocal(string email, string file) private void LoadDefaultAvatar(string key, string img) { var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/{img}", UriKind.RelativeOrAbsolute)); - _resources.Add(key, new Bitmap(icon)); + _cache.AddOrUpdate(key, new Bitmap(icon)); _defaultAvatars.Add(key); } @@ -233,5 +233,76 @@ private void NotifyResourceChanged(string email, Bitmap image) foreach (var avatar in _avatars) avatar.OnAvatarResourceChanged(email, image); } + + private class Cache + { + private readonly int _capacity; + private readonly LinkedList _imgList; + private readonly Dictionary> _key2img; + + private class Item + { + public string Key { get; } + public Bitmap Image { get; set; } + + public Item(string key, Bitmap image) + { + Key = key; + Image = image; + } + } + + public Cache(int capacity) + { + _capacity = capacity; + _key2img = new Dictionary>(); + _imgList = new LinkedList(); + } + + public bool TryGet(string key, out Bitmap bitmap) + { + if (_key2img.TryGetValue(key, out var node)) + { + _imgList.Remove(node); + _imgList.AddFirst(node); + bitmap = node.Value.Image; + return true; + } + + bitmap = null; + return false; + } + + public void AddOrUpdate(string key, Bitmap bitmap) + { + if (_key2img.TryGetValue(key, out var node)) + { + _imgList.Remove(node); + _imgList.AddFirst(node); + node.Value.Image = bitmap; + return; + } + + if (_key2img.Count >= _capacity) + { + var lastNode = _imgList.Last; + _imgList.RemoveLast(); + _key2img.Remove(lastNode.Value.Key); + } + + var newNode = new LinkedListNode(new Item(key, bitmap)); + _imgList.AddFirst(newNode); + _key2img[key] = newNode; + } + + public void Remove(string key) + { + if (_key2img.TryGetValue(key, out var node)) + { + _imgList.Remove(node); + _key2img.Remove(key); + } + } + } } } diff --git a/src/Models/BinaryFile.cs b/src/Models/BinaryFile.cs new file mode 100644 index 0000000000..d875ac2f5b --- /dev/null +++ b/src/Models/BinaryFile.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; + +namespace SourceGit.Models +{ + public class BinaryFile : IDisposable + { + public long Size + { + get => _size; + } + + public BinaryFile(string file, bool needDeleteFile = false) + { + _filePath = file; + _needDeleteFile = needDeleteFile; + + if (File.Exists(_filePath)) + { + _size = new FileInfo(_filePath).Length; + _reader = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read, BUFFER_SIZE, FileOptions.RandomAccess); + _readedStart = 0; + _readedEnd = Math.Min(_size, BUFFER_SIZE); + + if (_size > 0) + { + _reader.Seek(_readedStart, SeekOrigin.Begin); + _reader.ReadExactly(_buffer, 0, (int)_readedEnd); + } + } + } + + public void Dispose() + { + _reader?.Dispose(); + _reader = null; + + if (_needDeleteFile && File.Exists(_filePath)) + File.Delete(_filePath); + } + + public ArraySegment Read(long offset, long length) + { + if (_reader == null || _size == 0 || offset >= _size) + return Array.Empty(); + + if (length > 8192) + length = 8192; + + if (offset + length > _size) + length = _size - offset; + + if (_readedStart <= offset && _readedEnd >= offset + length) + return new ArraySegment(_buffer, (int)(offset - _readedStart), (int)length); + + _readedStart = (Math.Max(0, offset - 2048) / 1024) * 1024; + _readedEnd = Math.Min(_readedStart + BUFFER_SIZE, _size); + + _reader.Seek(_readedStart, SeekOrigin.Begin); + _reader.ReadExactly(_buffer, 0, (int)(_readedEnd - _readedStart)); + + return new ArraySegment(_buffer, (int)(offset - _readedStart), (int)length); + } + + private const int BUFFER_SIZE = 16384; + + private string _filePath = string.Empty; + private bool _needDeleteFile = false; + private FileStream _reader = null; + private long _size = 0; + private long _readedStart = 0; + private long _readedEnd = 0; + private byte[] _buffer = new byte[BUFFER_SIZE]; + } +} diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index 8557f271da..916a79b1bb 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -14,6 +14,7 @@ public enum CommitGraphHighlighting CurrentBranchOnly, SelectedCommitsOnly, CurrentBranchAndSelectedCommits, + SelectedCommitsOnlyFirstParent, } public class CommitGraph @@ -70,7 +71,7 @@ public class Dot public List Links { get; } = []; public List Dots { get; } = []; - public static CommitGraph Generate(List commits, bool recalculateMergeState, bool firstParentOnlyEnabled, CommitGraphHighlighting highlighting, HashSet highlightExtraCommits) + public static CommitGraph Generate(List commits, bool firstParentOnlyEnabled, CommitGraphHighlighting highlighting, HashSet highlightExtraCommits) { const double unitWidth = 12; const double halfWidth = 6; @@ -82,36 +83,19 @@ public static CommitGraph Generate(List commits, bool recalculateMergeSt var ended = new List(); var offsetY = -halfHeight; var colorPicker = new ColorPicker(); - var merged = new HashSet(); + var defHighlighting = highlighting == CommitGraphHighlighting.All; foreach (var commit in commits) { PathHelper major = null; - // Update merge state of this commit. - if (recalculateMergeState) - { - if (commit.IsMerged) - { - merged.Remove(commit.SHA); - foreach (var p in commit.Parents) - merged.Add(p); - } - else if (merged.Remove(commit.SHA)) - { - commit.IsMerged = true; - foreach (var p in commit.Parents) - merged.Add(p); - } - } - // Update current y offset offsetY += unitHeight; // Find first curves that links to this commit and marks others that links to this commit ended. var offsetX = 4 - halfWidth; var maxOffsetOld = unsolved.Count > 0 ? unsolved[^1].LastX : offsetX + unitWidth; - var isHighlighted = false; + var isHighlighted = defHighlighting; foreach (var l in unsolved) { if (l.Next.Equals(commit.SHA, StringComparison.Ordinal)) @@ -160,35 +144,34 @@ public static CommitGraph Generate(List commits, bool recalculateMergeSt // Calculate highlighted state if (!isHighlighted) { - if (highlighting == CommitGraphHighlighting.All) - { - isHighlighted = true; - } - else if (highlighting == CommitGraphHighlighting.CurrentBranchOnly) - { - isHighlighted = commit.IsMerged; - } - else if (highlighting == CommitGraphHighlighting.SelectedCommitsOnly) - { - isHighlighted = highlightExtraCommits.Remove(commit.SHA); - if (isHighlighted) - { - foreach (var p in commit.Parents) - highlightExtraCommits.Add(p); - } - } - else + switch (highlighting) { - if (commit.IsMerged) - { - isHighlighted = true; - } - else if (highlightExtraCommits.Remove(commit.SHA)) - { - isHighlighted = true; - foreach (var p in commit.Parents) - highlightExtraCommits.Add(p); - } + case CommitGraphHighlighting.CurrentBranchOnly: + isHighlighted = commit.IsMerged; + break; + case CommitGraphHighlighting.SelectedCommitsOnly: + case CommitGraphHighlighting.SelectedCommitsOnlyFirstParent: + if (highlightExtraCommits.Remove(commit.SHA)) + { + isHighlighted = true; + // Highlight first parent, other parents are dealt with later + if (commit.Parents.Count > 0) + highlightExtraCommits.Add(commit.Parents[0]); + } + break; + default: // CommitGraphHighlighting.CurrentBranchAndSelectedCommits + if (commit.IsMerged) + { + isHighlighted = true; + } + else if (highlightExtraCommits.Remove(commit.SHA)) + { + isHighlighted = true; + // Highlight first parent, other parents are dealt with later + if (commit.Parents.Count > 0) + highlightExtraCommits.Add(commit.Parents[0]); + } + break; } } commit.IsHighlightedInGraph = isHighlighted; @@ -227,6 +210,9 @@ public static CommitGraph Generate(List commits, bool recalculateMergeSt // Deal with other parents (the first parent has been processed) if (!firstParentOnlyEnabled) { + if (highlighting == CommitGraphHighlighting.SelectedCommitsOnlyFirstParent) + isHighlighted = false; + for (int j = 1; j < commit.Parents.Count; j++) { var parentHash = commit.Parents[j]; diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs index ddb3d7e07a..35b175c530 100644 --- a/src/Models/DiffResult.cs +++ b/src/Models/DiffResult.cs @@ -63,6 +63,10 @@ public class LFSDiff public class BinaryDiff { + public string Repository { get; set; } = string.Empty; + public string FilePath { get; set; } = string.Empty; + public string NewRevision { get; set; } = string.Empty; + public long OldSize { get; set; } = 0; public long NewSize { get; set; } = 0; } diff --git a/src/Models/ExternalTool.cs b/src/Models/ExternalTool.cs index 7f93cf8bdd..04dc2acd4b 100644 --- a/src/Models/ExternalTool.cs +++ b/src/Models/ExternalTool.cs @@ -63,6 +63,7 @@ public void Launch(string args) FileName = ExecFile, Arguments = args, UseShellExecute = false, + CreateNoWindow = true, }); } } @@ -120,7 +121,7 @@ public List Tools public ExternalToolsFinder() { - var customPathsConfig = Path.Combine(Native.OS.DataDir, "external_editors.json"); + var customPathsConfig = Path.Combine(Native.OS.BasicDirectories.ConfigDir, "external_editors.json"); try { if (File.Exists(customPathsConfig)) @@ -186,7 +187,7 @@ public void Cursor(Func platformFinder) public void FindJetBrainsFromToolbox(Func platformFinder) { - var supported = new List { "CL", "DB", "DL", "DS", "GO", "JB", "PC", "PS", "PY", "QA", "QD", "RD", "RM", "RR", "WRS", "WS" }; + var supported = new List { "AI", "CL", "DB", "DL", "DS", "GO", "IC", "IU", "JB", "PC", "PS", "PY", "QA", "QD", "RD", "RM", "RR", "WRS", "WS" }; var state = Path.Combine(platformFinder(), "state.json"); if (File.Exists(state)) { diff --git a/src/Models/HistoryFilter.cs b/src/Models/HistoryFilter.cs index b09f074cb2..369b237d72 100644 --- a/src/Models/HistoryFilter.cs +++ b/src/Models/HistoryFilter.cs @@ -1,4 +1,5 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using System.Text.Json.Serialization; +using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.Models { @@ -38,9 +39,11 @@ public FilterMode Mode set => SetProperty(ref _mode, value); } - public bool IsBranch + [JsonIgnore] + public bool IsValid { - get => Type != FilterType.Tag; + get => _isValid; + set => SetProperty(ref _isValid, value); } public HistoryFilter() @@ -56,5 +59,6 @@ public HistoryFilter(string pattern, FilterType type, FilterMode mode) private string _pattern = string.Empty; private FilterMode _mode = FilterMode.None; + private bool _isValid = true; } } diff --git a/src/Models/IpcChannel.cs b/src/Models/IpcChannel.cs index 4e9fc0b162..1a169d2acf 100644 --- a/src/Models/IpcChannel.cs +++ b/src/Models/IpcChannel.cs @@ -10,16 +10,54 @@ namespace SourceGit.Models { public class IpcChannel : IDisposable { - public bool IsFirstInstance { get; } + public bool IsFirstInstance { get; private set; } public event Action MessageReceived; public IpcChannel() { - try + IsFirstInstance = false; + + var lockFile = GetLockFilePath(); + _lockFilePath = lockFile.Path; + + if (OperatingSystem.IsLinux() && lockFile.NeedChangePermissions) + { + // On Linux, if the lock file is created in the XDG_RUNTIME_DIR, we need to set the permissions to 700 (rwx------) to ensure that only the current user can access it. + try + { + _singletonLock = new FileStream(_lockFilePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); + File.SetUnixFileMode(_lockFilePath, File.GetUnixFileMode(_lockFilePath) | UnixFileMode.StickyBit); + IsFirstInstance = true; + } + catch + { + try + { + _singletonLock = new FileStream(_lockFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); + IsFirstInstance = true; + } + catch + { + // Just ignore the exception and assume that another instance is running. + } + } + } + else + { + try + { + _singletonLock = File.Open(_lockFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); + IsFirstInstance = true; + } + catch + { + // Just ignore the exception and assume that another instance is running. + } + } + + if (IsFirstInstance) { - _singletonLock = File.Open(Path.Combine(Native.OS.DataDir, "process.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); - IsFirstInstance = true; _server = new NamedPipeServerStream( GetPipeName(), PipeDirection.In, @@ -29,10 +67,6 @@ public IpcChannel() _cancellationTokenSource = new CancellationTokenSource(); Task.Run(StartServer); } - catch - { - IsFirstInstance = false; - } } public void SendToFirstInstance(string cmd) @@ -67,14 +101,45 @@ public void Dispose() { _cancellationTokenSource?.Cancel(); _singletonLock?.Dispose(); + + if (IsFirstInstance && File.Exists(_lockFilePath)) + File.Delete(_lockFilePath); } - private static string GetPipeName() + private LockFile GetLockFilePath() { - var dataDir = Native.OS.DataDir.Replace('\\', '/').TrimEnd('/'); - var hash = SHA256.HashData(Encoding.UTF8.GetBytes(dataDir)); - var hashStr = Convert.ToHexString(hash)[..16]; - return $"SourceGitIPCChannel{Environment.UserName}_{hashStr}"; + // On Windows and macOS, we can use the cache directory for the lock file. + if (!OperatingSystem.IsLinux()) + return new LockFile(Path.Combine(Native.OS.BasicDirectories.CacheDir, "process.lock"), false); + + // On Linux, we should first check if the app is running in portable mode. + var appImage = Environment.GetEnvironmentVariable("APPIMAGE"); + if (!string.IsNullOrEmpty(appImage) && File.Exists(appImage)) + { + var portableDir = Path.Combine(Path.GetDirectoryName(appImage)!, "data"); + if (Directory.Exists(portableDir)) + return new LockFile(Path.Combine(portableDir, "process.lock"), false); + } + + // If not in portable mode, we should use the XDG_RUNTIME_DIR environment variable for the lock file. + var runtimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR"); + if (string.IsNullOrEmpty(runtimeDir) || !Directory.Exists(runtimeDir)) + return new LockFile(Path.Combine(Native.OS.BasicDirectories.CacheDir, "process.lock"), false); + + return new LockFile(Path.Combine(runtimeDir, "sourcegit.instance.lock"), true); + } + + private string GetPipeName() + { + // SourceGit does not support multiple instances on macOS, so we can use a fixed pipe name for macOS. + if (OperatingSystem.IsMacOS()) + return "SourceGit"; + + // Windows and Linux can have multiple instances of SourceGit running (portable-mode), so we need to generate a unique pipe name based on the data directory. + var dataDir = Native.OS.BasicDirectories.CacheDir.Replace('\\', '/').TrimEnd('/'); + var hashStr = $"{Environment.UserName}_{dataDir}"; + var hash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(hashStr))).Substring(0, 10); + return $"SG_{hash}"; } private async void StartServer() @@ -103,6 +168,9 @@ private async void StartServer() } } + private record LockFile(string Path, bool NeedChangePermissions); + + private string _lockFilePath = null; private FileStream _singletonLock = null; private NamedPipeServerStream _server = null; private CancellationTokenSource _cancellationTokenSource = null; diff --git a/src/Models/Null.cs b/src/Models/Null.cs index 1820d4d050..9e25977d07 100644 --- a/src/Models/Null.cs +++ b/src/Models/Null.cs @@ -1,4 +1,7 @@ namespace SourceGit.Models { - public class Null; + public class Null + { + public static readonly Null Instance = new Null(); + } } diff --git a/src/Models/RefName.cs b/src/Models/RefName.cs new file mode 100644 index 0000000000..42ad6d785d --- /dev/null +++ b/src/Models/RefName.cs @@ -0,0 +1,84 @@ +using System; + +namespace SourceGit.Models +{ + /// + /// Validates branch and tag names using the same rules that git itself enforces, + /// mirroring `git check-ref-format --allow-onelevel`. Rules are taken from the git-check-ref-format documentation. + /// Rule 2 (a refname must contain at least one slash) is intentionally waived, matching `--allow-onelevel`. + /// + public static class RefName + { + public static bool IsValidBranchName(string name) + { + if (string.Equals(name, "HEAD", StringComparison.Ordinal)) + return false; + + return IsValidRefName(name); + } + + public static bool IsValidTagName(string name) => IsValidRefName(name); + + private static bool IsValidRefName(string name) + { + if (string.IsNullOrEmpty(name)) + return false; + + // Anything starting with '-' is treated as a CLI option. + if (name.StartsWith('-')) + return false; + + // Rule 9: cannot be the single character '@'. + if (name.Equals("@", StringComparison.Ordinal)) + return false; + + // Rule 6: cannot begin or end with '/', or contain consecutive slashes. + if (name[0] == '/' || name[^1] == '/' || name.Contains("//", StringComparison.Ordinal)) + return false; + + // Rule 7: cannot end with a dot. + if (name[^1] == '.') + return false; + + // Rule 3: cannot contain two consecutive dots. + if (name.Contains("..", StringComparison.Ordinal)) + return false; + + // Rule 8: cannot contain the sequence '@{'. + if (name.Contains("@{", StringComparison.Ordinal)) + return false; + + // Rules 4, 5 & 10: no control chars, DEL, space, or ~ ^ : ? * [ \. + foreach (var ch in name) + { + if (ch is < ' ' or '\x7f') + return false; + + switch (ch) + { + case ' ': + case '~': + case '^': + case ':': + case '?': + case '*': + case '[': + case '\\': + return false; + } + } + + // Rule 1: no slash-separated component may begin with a dot or end with ".lock". + foreach (var component in name.Split('/')) + { + if (component[0] == '.') + return false; + + if (component.EndsWith(".lock", StringComparison.Ordinal)) + return false; + } + + return true; + } + } +} diff --git a/src/Models/Remote.cs b/src/Models/Remote.cs index 12681b4b4d..ab16874d26 100644 --- a/src/Models/Remote.cs +++ b/src/Models/Remote.cs @@ -31,6 +31,8 @@ public partial class Remote public string Name { get; set; } public string URL { get; set; } + public string PushURL { get; set; } + public string PrivateSSHKey { get; set; } public bool DisableAutoFetch { get; set; } public static bool IsSSH(string url) diff --git a/src/Models/RepositoryUIStates.cs b/src/Models/RepositoryUIStates.cs index 723fe900b1..0b35f8d618 100644 --- a/src/Models/RepositoryUIStates.cs +++ b/src/Models/RepositoryUIStates.cs @@ -376,7 +376,7 @@ public void RenameBranchFilter(string oldName, string newName) if (filter.Type == FilterType.LocalBranch && filter.Pattern.Equals(oldName, StringComparison.Ordinal)) { - filter.Pattern = $"refs/heads/{newName}"; + filter.Pattern = newName; break; } } diff --git a/src/Models/RevisionFile.cs b/src/Models/RevisionFile.cs index 9a9fe7d209..86e9a1aedd 100644 --- a/src/Models/RevisionFile.cs +++ b/src/Models/RevisionFile.cs @@ -6,7 +6,18 @@ namespace SourceGit.Models { public class RevisionBinaryFile { + public string Repository { get; set; } = string.Empty; + public string File { get; set; } = string.Empty; + public string Revision { get; set; } = string.Empty; public long Size { get; set; } = 0; + + public RevisionBinaryFile(string repo, string file, string revision, long size) + { + Repository = repo; + File = file; + Revision = revision; + Size = size; + } } public class RevisionImageFile @@ -40,5 +51,18 @@ public class RevisionSubmodule public Commit Commit { get; set; } = null; public CommitFullMessage FullMessage { get; set; } = null; public int UncommittedChanges { get; set; } = 0; + + public RevisionSubmodule(string hash) + { + Commit = new Commit() { SHA = hash }; + FullMessage = new CommitFullMessage() { Message = string.Empty }; + } + + public RevisionSubmodule(Commit commit, string message, int uncommittedChanges) + { + Commit = commit; + FullMessage = new CommitFullMessage() { Message = message }; + UncommittedChanges = uncommittedChanges; + } } } diff --git a/src/Models/SSHKeyPair.cs b/src/Models/SSHKeyPair.cs new file mode 100644 index 0000000000..1094b03137 --- /dev/null +++ b/src/Models/SSHKeyPair.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; + +namespace SourceGit.Models +{ + public record SSHKeyType(string Name, string Description, string Cmdline) + { + public static readonly List Supported = [ + new SSHKeyType("ED25519", "Recommended", "-t ed25519"), + new SSHKeyType("RSA", "4096 Bits", "-t rsa -b 4096"), + ]; + } + + public class SSHKeyPair + { + public string PrivateKeyPath { get; set; } + public string PublicKeyPath { get; set; } + public string RawPublicKey { get; set; } + public string Fingerprint { get; set; } = "--- (invalid)"; + public string Name => Path.GetFileName(PrivateKeyPath); + + public SSHKeyPair(string privateKey, string publicKey) + { + PrivateKeyPath = privateKey; + PublicKeyPath = publicKey; + RawPublicKey = File.ReadAllText(publicKey); + + var parts = RawPublicKey.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length != 3) + return; + + try + { + var blob = Convert.FromBase64String(parts[1]); + Fingerprint = "SHA256:" + Convert.ToBase64String(SHA256.HashData(blob)).TrimEnd('='); + } + catch + { + // Ignore errors and keep the fingerprint as invalid + } + } + } +} diff --git a/src/Models/SafeDirectories.cs b/src/Models/SafeDirectories.cs new file mode 100644 index 0000000000..5fcf24a3af --- /dev/null +++ b/src/Models/SafeDirectories.cs @@ -0,0 +1,103 @@ +using System; +using System.Text; +using System.Text.RegularExpressions; + +namespace SourceGit.Models +{ + /// + /// Helpers for the `safe.directory` protection introduced by git 2.35.2 (CVE-2022-24765). + /// Git refuses to use a repository whose top-level directory is owned by another user (which is + /// always the case for network shares / UNC paths) unless the path is listed in the `safe.directory` config. + /// + public static partial class SafeDirectories + { + public static bool IsUntrustedRepository(string output) + { + if (string.IsNullOrWhiteSpace(output)) + return false; + + return output.Contains("detected dubious ownership", StringComparison.OrdinalIgnoreCase) || + output.Contains("unsafe repository", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Tries to get the value that should be written into the `safe.directory` config. + /// Prefers the value suggested by git itself (which knows the correct form for the current platform), + /// and falls back to building a value from the given path. + /// + public static bool TryGetSafeDirectoryValue(string path, string output, out string value) + { + value = ParseSuggestedValue(output); + if (!string.IsNullOrEmpty(value)) + return true; + + var normalized = Normalize(path); + if (string.IsNullOrEmpty(normalized)) + return false; + + // Git for Windows uses the `%(prefix)/` prefix for UNC paths. + value = OperatingSystem.IsWindows() && normalized.StartsWith("//", StringComparison.Ordinal) ? $"%(prefix)/{normalized}" : normalized; + return true; + } + + private static string Normalize(string path) + { + return path?.Replace('\\', '/').TrimEnd('/') ?? string.Empty; + } + + private static string ParseSuggestedValue(string output) + { + if (string.IsNullOrEmpty(output)) + return string.Empty; + + var match = REG_SAFE_DIRECTORY_HINT().Match(output); + if (!match.Success) + return string.Empty; + + return Dequote(match.Groups["value"].Value.Trim()); + } + + /// + /// Git quotes the suggested value with `sq_quote_buf()`, which escapes `'` and `!` as `'\''` and + /// `'\!'`. Decodes it back to the plain path (see `quote.c` of git). + /// + private static string Dequote(string value) + { + if (value.Length > 1 && value[0] == '\'') + { + var builder = new StringBuilder(value.Length); + for (var i = 1; i < value.Length; i++) + { + var c = value[i]; + if (c != '\'') + { + builder.Append(c); + continue; + } + + if (i == value.Length - 1) + return builder.ToString(); + + if (i + 3 < value.Length && value[i + 1] == '\\' && (value[i + 2] == '\'' || value[i + 2] == '!') && value[i + 3] == '\'') + { + builder.Append(value[i + 2]); + i += 3; + continue; + } + + return string.Empty; + } + + return string.Empty; + } + + if (value.Length > 1 && value[0] == '"' && value[^1] == '"') + return value[1..^1]; + + return value; + } + + [GeneratedRegex(@"--add\s+safe\.directory\s+(?[^\r\n]+)", RegexOptions.Multiline)] + private static partial Regex REG_SAFE_DIRECTORY_HINT(); + } +} diff --git a/src/Models/TextMateHelper.cs b/src/Models/TextMateHelper.cs index 5bb50da691..bdddeb1b54 100644 --- a/src/Models/TextMateHelper.cs +++ b/src/Models/TextMateHelper.cs @@ -27,6 +27,11 @@ public static class GrammarUtility new ExtraGrammar("source.hxml", [".hxml"], "hxml.json"), new ExtraGrammar("text.html.jsp", [".jsp", ".jspf", ".tag"], "jsp.json"), new ExtraGrammar("source.vue", [".vue"], "vue.json"), + new ExtraGrammar("source.erlang", [".erl", ".escript", ".hrl"], "erlang.json"), + new ExtraGrammar("source.ocaml", [".ml", ".mli"], "ocaml.json"), + new ExtraGrammar("source.ocamllex", [".mll"], "ocamllex.json"), + new ExtraGrammar("source.ocamlyacc", [".mly"], "ocamlyacc.json"), + new ExtraGrammar("source.swift", [".swift"], "swift.json"), ]; private static readonly Dictionary s_cachedRawGrammars = new(); @@ -36,7 +41,7 @@ public static string GetScope(string file, RegistryOptions reg) var extension = Path.GetExtension(file); if (extension == ".h") extension = ".cpp"; - else if (extension is ".resx" or ".plist" or ".manifest") + else if (extension is ".resx" or ".plist" or ".manifest" or ".sln" or ".slnx") extension = ".xml"; else if (extension == ".command") extension = ".sh"; diff --git a/src/Models/Watcher.cs b/src/Models/Watcher.cs index ce18fe4be2..66e246bc07 100644 --- a/src/Models/Watcher.cs +++ b/src/Models/Watcher.cs @@ -43,6 +43,7 @@ public Watcher(IRepository repo, string fullpath, string gitDir) combined.Renamed += OnRepositoryChanged; combined.Changed += OnRepositoryChanged; combined.Deleted += OnRepositoryChanged; + combined.Error += OnWatcherError; combined.EnableRaisingEvents = false; _watchers.Add(combined); @@ -58,6 +59,7 @@ public Watcher(IRepository repo, string fullpath, string gitDir) wc.Renamed += OnWorkingCopyChanged; wc.Changed += OnWorkingCopyChanged; wc.Deleted += OnWorkingCopyChanged; + wc.Error += OnWatcherError; wc.EnableRaisingEvents = false; var git = new FileSystemWatcher(); @@ -69,6 +71,7 @@ public Watcher(IRepository repo, string fullpath, string gitDir) git.Renamed += OnGitDirChanged; git.Changed += OnGitDirChanged; git.Deleted += OnGitDirChanged; + git.Error += OnWatcherError; git.EnableRaisingEvents = false; _watchers.Add(wc); @@ -240,6 +243,21 @@ private void OnRepositoryChanged(object o, FileSystemEventArgs e) HandleWorkingCopyFileChanged(name, e.FullPath); } + /// + /// The internal buffer overflowed (a large fetch or checkout writes thousands of files), so + /// some events were dropped and we cannot know which. Reload everything once things settle. + /// + private void OnWatcherError(object o, ErrorEventArgs e) + { + var desired = DateTime.Now.AddSeconds(1).ToFileTime(); + Interlocked.Exchange(ref _updateBranch, desired); + Interlocked.Exchange(ref _updateTags, desired); + Interlocked.Exchange(ref _updateStashes, desired); + Interlocked.Exchange(ref _updateWC, desired); + if (_repo.MayHaveSubmodules()) + Interlocked.Exchange(ref _updateSubmodules, desired); + } + private void OnGitDirChanged(object o, FileSystemEventArgs e) { if (string.IsNullOrEmpty(e.Name)) @@ -290,7 +308,7 @@ private void HandleGitDirFileChanged(string name) { Interlocked.Exchange(ref _updateTags, DateTime.Now.AddSeconds(.5).ToFileTime()); } - else if (name.StartsWith("refs/stash", StringComparison.Ordinal)) + else if (name.StartsWith("refs/stash", StringComparison.Ordinal) || name.Equals("logs/refs/stash", StringComparison.Ordinal)) { Interlocked.Exchange(ref _updateStashes, DateTime.Now.AddSeconds(.5).ToFileTime()); } @@ -302,6 +320,12 @@ private void HandleGitDirFileChanged(string name) { Interlocked.Exchange(ref _updateBranch, DateTime.Now.AddSeconds(.5).ToFileTime()); } + else if (name.Equals("packed-refs", StringComparison.Ordinal)) + { + var desired = DateTime.Now.AddSeconds(.5).ToFileTime(); + Interlocked.Exchange(ref _updateBranch, desired); + Interlocked.Exchange(ref _updateTags, desired); + } else if (name.StartsWith("reftable/", StringComparison.Ordinal)) { var desired = DateTime.Now.AddSeconds(.5).ToFileTime(); diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index a28d88ebe1..3d09f82663 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using Avalonia; @@ -13,6 +14,9 @@ namespace SourceGit.Native [SupportedOSPlatform("linux")] internal class Linux : OS.IBackend { + [DllImport("libc", SetLastError = true)] + private static extern int kill(int pid, int sig); + public void SetupApp(AppBuilder builder) { builder.With(new X11PlatformOptions() { EnableIme = true }); @@ -35,20 +39,55 @@ public void SetupWindow(Window window) } } - public string GetDataDir() + public OS.Directories GetOrCreateDirectories() { + var dirs = new OS.Directories(); + var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + // AppImage supports portable mode var appImage = Environment.GetEnvironmentVariable("APPIMAGE"); if (!string.IsNullOrEmpty(appImage) && File.Exists(appImage)) { var portableDir = Path.Combine(Path.GetDirectoryName(appImage)!, "data"); if (Directory.Exists(portableDir)) - return portableDir; + { + dirs.ConfigDir = portableDir; + dirs.CacheDir = portableDir; + return dirs; + } } - // Runtime data dir: ~/.sourcegit - var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - return Path.Combine(home, ".sourcegit"); + // XDG Base Directory Specification: https://specifications.freedesktop.org/basedir/latest/ + dirs.ConfigDir = GetXdgDirectory("XDG_CONFIG_HOME", Path.Combine(home, ".config"), "SourceGit"); + dirs.CacheDir = GetXdgDirectory("XDG_CACHE_HOME", Path.Combine(home, ".cache"), "SourceGit"); + + // If the app basic dirs already exist, we can skip the migration step + if (Directory.Exists(dirs.ConfigDir) && Directory.Exists(dirs.CacheDir)) + return dirs; + + // Create the config and cache directories if they don't exist + if (!Directory.Exists(dirs.ConfigDir)) + Directory.CreateDirectory(dirs.ConfigDir); + if (!Directory.Exists(dirs.CacheDir)) + Directory.CreateDirectory(dirs.CacheDir); + + // Migrate legacy data dir: ~/.sourcegit to XDG standard directories + var legacyDir = Path.Combine(home, ".sourcegit"); + if (Directory.Exists(legacyDir)) + { + try + { + File.Copy(Path.Combine(legacyDir, "preference.json"), Path.Combine(dirs.ConfigDir, "preference.json"), true); + Directory.Move(Path.Combine(legacyDir, "avatars"), Path.Combine(dirs.CacheDir, "avatars")); + Directory.Delete(legacyDir, true); + } + catch + { + // Ignore any errors during migration + } + } + + return dirs; } public string FindGitExecutable() @@ -138,6 +177,39 @@ public void OpenWithDefaultEditor(string file) } } + public bool SupportSetSid() + { + return true; + } + + public string GetSetSidExecutable() + { + return "setsid"; + } + + public void TerminateProcess(Process proc) + { + if (kill(-proc.Id, 15) != 0) + { + // If the process already exited, we can just ignore the error. + if (Marshal.GetLastPInvokeError() == 3 /* ESRCH */) + return; + + // Actually, this will not be called since the process is + // spawned by us (EPERM will not happen), and SIGTERM (15) + // is a valid signal (EINVAL will not happen). + // See https://www.man7.org/linux/man-pages/man2/kill.2.html + try + { + proc.Kill(true); + } + catch + { + // Ignore any errors when trying to kill the process + } + } + } + private string FindExecutable(string filename) { var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; @@ -152,5 +224,14 @@ private string FindExecutable(string filename) var local = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "bin", filename); return File.Exists(local) ? local : string.Empty; } + + private string GetXdgDirectory(string envVar, string fallback, string subDirName) + { + var dir = Environment.GetEnvironmentVariable(envVar); + if (!string.IsNullOrEmpty(dir) && Directory.Exists(dir)) + return Path.Combine(dir, subDirName); + + return Path.Combine(fallback, subDirName); + } } } diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index 54a2a8a723..06d09e8fa3 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -15,6 +15,14 @@ namespace SourceGit.Native [SupportedOSPlatform("macOS")] internal class MacOS : OS.IBackend { + [DllImport("/usr/lib/libSystem.B.dylib", SetLastError = true)] + private static extern int kill(int pid, int sig); + + public MacOS() + { + _setsidExecutable = Path.Combine(Path.GetDirectoryName(Environment.ProcessPath), "setsid"); + } + public void SetupApp(AppBuilder builder) { builder.With(new MacOSPlatformOptions() @@ -29,7 +37,7 @@ public void SetupApp(AppBuilder builder) else if (!path.Contains("/opt/homebrew/", StringComparison.Ordinal)) path = "/opt/homebrew/bin:/opt/homebrew/sbin:" + path; - var customPathFile = Path.Combine(OS.DataDir, "PATH"); + var customPathFile = Path.Combine(OS.BasicDirectories.ConfigDir, "PATH"); if (File.Exists(customPathFile)) { var env = File.ReadAllText(customPathFile).Trim(); @@ -47,11 +55,19 @@ public void SetupWindow(Window window) window.BorderThickness = new Thickness(0); } - public string GetDataDir() + public OS.Directories GetOrCreateDirectories() { - return Path.Combine( + var dirs = new OS.Directories(); + var appDataDir = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SourceGit"); + + if (!Directory.Exists(appDataDir)) + Directory.CreateDirectory(appDataDir); + + dirs.ConfigDir = appDataDir; + dirs.CacheDir = appDataDir; + return dirs; } public string FindGitExecutable() @@ -113,6 +129,47 @@ public void OpenWithDefaultEditor(string file) { Process.Start("open", file.Quoted()); } + + public bool SupportSetSid() + { + return File.Exists(_setsidExecutable); + } + + public string GetSetSidExecutable() + { + return _setsidExecutable; + } + + public void TerminateProcess(Process proc) + { + if (!SupportSetSid()) + { + proc.Kill(true); + return; + } + + if (kill(-proc.Id, 15) != 0) + { + // If the process already exited, we can just ignore the error. + if (Marshal.GetLastPInvokeError() == 3 /* ESRCH */) + return; + + // Actually, this will not be called since the process is + // spawned by us (EPERM will not happen), and SIGTERM (15) + // is a valid signal (EINVAL will not happen). + // See https://www.man7.org/linux/man-pages/man2/kill.2.html + try + { + proc.Kill(true); + } + catch + { + // Ignore any errors when trying to kill the process + } + } + } + + private string _setsidExecutable = null; } [SupportedOSPlatform("macOS")] @@ -247,9 +304,10 @@ public static void AdjustTrafficLightsForThickTitleBar(Window window) return; } - objc_msgSend_Void_Point(nsCloseBtn, s_selSetFrameOrigin, new(14, 2)); - objc_msgSend_Void_Point(nsMinBtn, s_selSetFrameOrigin, new(14 + 20, 2)); - objc_msgSend_Void_Point(nsZoomBtn, s_selSetFrameOrigin, new(14 + 40, 2)); + var yOffset = OperatingSystem.IsMacOSVersionAtLeast(26) ? 6 : 2; + objc_msgSend_Void_Point(nsCloseBtn, s_selSetFrameOrigin, new(14, yOffset)); + objc_msgSend_Void_Point(nsMinBtn, s_selSetFrameOrigin, new(14 + 20, yOffset)); + objc_msgSend_Void_Point(nsZoomBtn, s_selSetFrameOrigin, new(14 + 40, yOffset)); } public static void HideSelf() diff --git a/src/Native/OS.cs b/src/Native/OS.cs index ceaa69c84a..741fcf8440 100644 --- a/src/Native/OS.cs +++ b/src/Native/OS.cs @@ -14,12 +14,18 @@ namespace SourceGit.Native { public static partial class OS { + public class Directories + { + public string ConfigDir { get; set; } = string.Empty; + public string CacheDir { get; set; } = string.Empty; + } + public interface IBackend { void SetupApp(AppBuilder builder); void SetupWindow(Window window); - string GetDataDir(); + Directories GetOrCreateDirectories(); string FindGitExecutable(); string FindTerminal(Models.ShellOrTerminal shell); List FindExternalTools(); @@ -28,13 +34,17 @@ public interface IBackend void OpenInFileManager(string path); void OpenBrowser(string url); void OpenWithDefaultEditor(string file); + + bool SupportSetSid(); + string GetSetSidExecutable(); + void TerminateProcess(Process proc); } - public static string DataDir + public static Directories BasicDirectories { get; private set; - } = string.Empty; + } = new(); public static string GitExecutable { @@ -133,11 +143,9 @@ static OS() throw new PlatformNotSupportedException(); } - public static void SetupDataDir() + public static void SetupBasicDirectories() { - DataDir = _backend.GetDataDir(); - if (!Directory.Exists(DataDir)) - Directory.CreateDirectory(DataDir); + BasicDirectories = _backend.GetOrCreateDirectories(); } public static void SetupApp(AppBuilder builder) @@ -160,7 +168,7 @@ public static void LogException(Exception ex) if (ex == null) return; - var crashDir = Path.Combine(DataDir, "crashes"); + var crashDir = Path.Combine(BasicDirectories.CacheDir, "crashes"); if (!Directory.Exists(crashDir)) Directory.CreateDirectory(crashDir); @@ -297,6 +305,21 @@ public static string GetRelativePathToHome(string path) return path; } + public static bool SupportSetSid() + { + return _backend.SupportSetSid(); + } + + public static string GetSetSidExecutable() + { + return _backend.GetSetSidExecutable(); + } + + public static void TerminateProcess(Process proc) + { + _backend.TerminateProcess(proc); + } + private static void UpdateGitVersion() { if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable)) diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index 2ec98ca40c..fde6cb25a7 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -29,6 +29,18 @@ internal class Windows : OS.IBackend [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)] private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags); + [DllImport("kernel32.dll")] + private static extern bool SetConsoleCtrlHandler(IntPtr handler, bool add); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool AttachConsole(int dwProcessId); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, int dwProcessGroupId); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool FreeConsole(); + public void SetupApp(AppBuilder builder) { // Do nothing for now. @@ -42,16 +54,30 @@ public void SetupWindow(Window window) window.Padding = new Thickness(0); } - public string GetDataDir() + public OS.Directories GetOrCreateDirectories() { + var dirs = new OS.Directories(); var execFile = Environment.ProcessPath; var portableDir = Path.Combine(Path.GetDirectoryName(execFile)!, "data"); if (Directory.Exists(portableDir)) - return portableDir; + { + dirs.ConfigDir = portableDir; + dirs.CacheDir = portableDir; + } + else + { + var appDataDir = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "SourceGit"); + + if (!Directory.Exists(appDataDir)) + Directory.CreateDirectory(appDataDir); - return Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "SourceGit"); + dirs.ConfigDir = appDataDir; + dirs.CacheDir = appDataDir; + } + + return dirs; } public string FindGitExecutable() @@ -196,6 +222,41 @@ public void OpenWithDefaultEditor(string file) Process.Start(start); } + public bool SupportSetSid() + { + return false; + } + + public string GetSetSidExecutable() + { + return null; + } + + public void TerminateProcess(Process proc) + { + var pid = proc.Id; + if (AttachConsole(pid)) + { + SetConsoleCtrlHandler(IntPtr.Zero, true); + try + { + if (!GenerateConsoleCtrlEvent(0, 0)) + proc.Kill(true); + + proc.WaitForExit(2000); + } + finally + { + FreeConsole(); + SetConsoleCtrlHandler(IntPtr.Zero, false); + } + } + else + { + proc.Kill(true); + } + } + #region HELPER_METHODS private List GenerateVSProjectLaunchOptions(string path) { diff --git a/src/Resources/Fonts.axaml b/src/Resources/Fonts.axaml new file mode 100644 index 0000000000..832026bead --- /dev/null +++ b/src/Resources/Fonts.axaml @@ -0,0 +1,5 @@ + + fonts:Inter#Inter + fonts:SourceGit#JetBrains Mono NL + diff --git a/src/Resources/Grammars/erlang.json b/src/Resources/Grammars/erlang.json new file mode 100644 index 0000000000..216086ce5f --- /dev/null +++ b/src/Resources/Grammars/erlang.json @@ -0,0 +1,1753 @@ +{ + "comment": "The recognition of function definitions and compiler directives (such as module, record and macro definitions) requires that each of the aforementioned constructs must be the first string inside a line (except for whitespace). Also, the function/module/record/macro names must be given unquoted. -- desp", + "fileTypes": ["erl", "escript", "hrl", "xrl", "yrl"], + "keyEquivalent": "^~E", + "name": "erlang", + "patterns": [ + { + "include": "#module-directive" + }, + { + "include": "#import-export-directive" + }, + { + "include": "#behaviour-directive" + }, + { + "include": "#record-directive" + }, + { + "include": "#define-directive" + }, + { + "include": "#macro-directive" + }, + { + "include": "#directive" + }, + { + "include": "#function" + }, + { + "include": "#everything-else" + } + ], + "repository": { + "atom": { + "patterns": [ + { + "begin": "(')", + "beginCaptures": { + "1": { + "name": "punctuation.definition.symbol.begin.erlang" + } + }, + "end": "(')", + "endCaptures": { + "1": { + "name": "punctuation.definition.symbol.end.erlang" + } + }, + "name": "constant.other.symbol.quoted.single.erlang", + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.definition.escape.erlang" + }, + "3": { + "name": "punctuation.definition.escape.erlang" + } + }, + "match": "(\\\\)([bdefnrstv\\\\'\"]|(\\^)[@-_a-z]|[0-7]{1,3}|x[\\da-fA-F]{2})", + "name": "constant.other.symbol.escape.erlang" + }, + { + "match": "\\\\\\^?.?", + "name": "invalid.illegal.atom.erlang" + } + ] + }, + { + "match": "[a-z][a-zA-Z\\d@_]*+", + "name": "constant.other.symbol.unquoted.erlang" + } + ] + }, + "behaviour-directive": { + "captures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.behaviour.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.type.class.behaviour.definition.erlang" + }, + "5": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "6": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "match": "^\\s*+(-)\\s*+(behaviour)\\s*+(\\()\\s*+([a-z][a-zA-Z\\d@_]*+)\\s*+(\\))\\s*+(\\.)", + "name": "meta.directive.behaviour.erlang" + }, + "binary": { + "begin": "(<<)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.binary.begin.erlang" + } + }, + "end": "(>>)", + "endCaptures": { + "1": { + "name": "punctuation.definition.binary.end.erlang" + } + }, + "name": "meta.structure.binary.erlang", + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.separator.binary.erlang" + }, + "2": { + "name": "punctuation.separator.value-size.erlang" + } + }, + "match": "(,)|(:)" + }, + { + "include": "#internal-type-specifiers" + }, + { + "include": "#everything-else" + } + ] + }, + "character": { + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.definition.character.erlang" + }, + "2": { + "name": "constant.character.escape.erlang" + }, + "3": { + "name": "punctuation.definition.escape.erlang" + }, + "5": { + "name": "punctuation.definition.escape.erlang" + } + }, + "match": "(\\$)((\\\\)([bdefnrstv\\\\'\"]|(\\^)[@-_a-z]|[0-7]{1,3}|x[\\da-fA-F]{2}))", + "name": "constant.character.erlang" + }, + { + "match": "\\$\\\\\\^?.?", + "name": "invalid.illegal.character.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.character.erlang" + } + }, + "match": "(\\$)[ \\S]", + "name": "constant.character.erlang" + }, + { + "match": "\\$.?", + "name": "invalid.illegal.character.erlang" + } + ] + }, + "comment": { + "begin": "(^[ \\t]+)?(?=%)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.erlang" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "%", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.erlang" + } + }, + "end": "\\n", + "name": "comment.line.percentage.erlang" + } + ] + }, + "define-directive": { + "patterns": [ + { + "begin": "^\\s*+(-)\\s*+(define)\\s*+(\\()\\s*+([a-zA-Z\\d@_]++)\\s*+", + "beginCaptures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.define.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.function.macro.definition.erlang" + } + }, + "end": "(\\))\\s*+(\\.)", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "2": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "name": "meta.directive.define.erlang", + "patterns": [ + { + "include": "#everything-else" + } + ] + }, + { + "begin": "(?=^\\s*+-\\s*+define\\s*+\\(\\s*+[a-zA-Z\\d@_]++\\s*+\\()", + "end": "(\\))\\s*+(\\.)", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "2": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "name": "meta.directive.define.erlang", + "patterns": [ + { + "begin": "^\\s*+(-)\\s*+(define)\\s*+(\\()\\s*+([a-zA-Z\\d@_]++)\\s*+(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.define.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.function.macro.definition.erlang" + }, + "5": { + "name": "punctuation.definition.parameters.begin.erlang" + } + }, + "end": "(\\))\\s*(,)", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "2": { + "name": "punctuation.separator.parameters.erlang" + } + }, + "patterns": [ + { + "match": ",", + "name": "punctuation.separator.parameters.erlang" + }, + { + "include": "#everything-else" + } + ] + }, + { + "match": "\\|\\||\\||:|;|,|\\.|->", + "name": "punctuation.separator.define.erlang" + }, + { + "include": "#everything-else" + } + ] + } + ] + }, + "directive": { + "patterns": [ + { + "begin": "^\\s*+(-)\\s*+([a-z][a-zA-Z\\d@_]*+)\\s*+(\\(?)", + "beginCaptures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + } + }, + "end": "(\\)?)\\s*+(\\.)", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "2": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "name": "meta.directive.erlang", + "patterns": [ + { + "include": "#everything-else" + } + ] + }, + { + "captures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.erlang" + }, + "3": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "match": "^\\s*+(-)\\s*+([a-z][a-zA-Z\\d@_]*+)\\s*+(\\.)", + "name": "meta.directive.erlang" + } + ] + }, + "everything-else": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#record-usage" + }, + { + "include": "#macro-usage" + }, + { + "include": "#expression" + }, + { + "include": "#keyword" + }, + { + "include": "#textual-operator" + }, + { + "include": "#language-constant" + }, + { + "include": "#function-call" + }, + { + "include": "#tuple" + }, + { + "include": "#list" + }, + { + "include": "#binary" + }, + { + "include": "#parenthesized-expression" + }, + { + "include": "#character" + }, + { + "include": "#number" + }, + { + "include": "#atom" + }, + { + "include": "#string" + }, + { + "include": "#symbolic-operator" + }, + { + "include": "#variable" + } + ] + }, + "expression": { + "patterns": [ + { + "begin": "\\b(if)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.if.erlang" + } + }, + "end": "\\b(end)\\b", + "endCaptures": { + "1": { + "name": "keyword.control.end.erlang" + } + }, + "name": "meta.expression.if.erlang", + "patterns": [ + { + "include": "#internal-expression-punctuation" + }, + { + "include": "#everything-else" + } + ] + }, + { + "begin": "\\b(case)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.case.erlang" + } + }, + "end": "\\b(end)\\b", + "endCaptures": { + "1": { + "name": "keyword.control.end.erlang" + } + }, + "name": "meta.expression.case.erlang", + "patterns": [ + { + "include": "#internal-expression-punctuation" + }, + { + "include": "#everything-else" + } + ] + }, + { + "begin": "\\b(receive)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.receive.erlang" + } + }, + "end": "\\b(end)\\b", + "endCaptures": { + "1": { + "name": "keyword.control.end.erlang" + } + }, + "name": "meta.expression.receive.erlang", + "patterns": [ + { + "include": "#internal-expression-punctuation" + }, + { + "include": "#everything-else" + } + ] + }, + { + "captures": { + "1": { + "name": "keyword.control.fun.erlang" + }, + "4": { + "name": "entity.name.type.class.module.erlang" + }, + "5": { + "name": "variable.other.erlang" + }, + "6": { + "name": "punctuation.separator.module-function.erlang" + }, + "8": { + "name": "entity.name.function.erlang" + }, + "9": { + "name": "variable.other.erlang" + }, + "10": { + "name": "punctuation.separator.function-arity.erlang" + } + }, + "comment": "Implicit function expression with optional module qualifier when both module and function can be atom or variable", + "match": "\\b(fun)\\s+((([a-z][a-zA-Z\\d@_]*+)|(_[a-zA-Z\\d@_]++|[A-Z][a-zA-Z\\d@_]*+))\\s*+(:)\\s*+)?(([a-z][a-zA-Z\\d@_]*+|'[^']*+')|(_[a-zA-Z\\d@_]++|[A-Z][a-zA-Z\\d@_]*+))\\s*(/)", + "name": "meta.expression.fun.implicit.erlang" + }, + { + "begin": "\\b(fun)\\s+(([a-z][a-zA-Z\\d@_]*+)|(_[a-zA-Z\\d@_]++|[A-Z][a-zA-Z\\d@_]*+))\\s*+(:)", + "beginCaptures": { + "1": { + "name": "keyword.control.fun.erlang" + }, + "3": { + "name": "entity.name.type.class.module.erlang" + }, + "4": { + "name": "variable.other.erlang" + }, + "5": { + "name": "punctuation.separator.module-function.erlang" + } + }, + "comment": "Implicit function expression with module qualifier when module can be atom or variable and function can by anything", + "end": "(/)", + "endCaptures": { + "1": { + "name": "punctuation.separator.function-arity.erlang" + } + }, + "name": "meta.expression.fun.implicit.erlang", + "patterns": [ + { + "include": "#everything-else" + } + ] + }, + { + "begin": "\\b(fun)\\s+(?!\\()", + "beginCaptures": { + "1": { + "name": "keyword.control.fun.erlang" + } + }, + "comment": "Implicit function expression when both module and function can by anything", + "end": "(/)", + "endCaptures": { + "1": { + "name": "punctuation.separator.function-arity.erlang" + } + }, + "name": "meta.expression.fun.implicit.erlang", + "patterns": [ + { + "include": "#everything-else" + } + ] + }, + { + "begin": "\\b(fun)\\s*+(\\()(?=(\\s*+\\())", + "beginCaptures": { + "1": { + "name": "entity.name.function.erlang" + }, + "2": { + "name": "punctuation.definition.parameters.begin.erlang" + } + }, + "comment": "Function type in type specification", + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + } + }, + "patterns": [ + { + "include": "#everything-else" + } + ] + }, + { + "begin": "\\b(fun)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.fun.erlang" + } + }, + "comment": "Explicit function expression", + "end": "\\b(end)\\b", + "endCaptures": { + "1": { + "name": "keyword.control.end.erlang" + } + }, + "name": "meta.expression.fun.erlang", + "patterns": [ + { + "begin": "(?=\\()", + "end": "(;)|(?=\\bend\\b)", + "endCaptures": { + "1": { + "name": "punctuation.separator.clauses.erlang" + } + }, + "patterns": [ + { + "include": "#internal-function-parts" + } + ] + }, + { + "include": "#everything-else" + } + ] + }, + { + "begin": "\\b(try)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.try.erlang" + } + }, + "end": "\\b(end)\\b", + "endCaptures": { + "1": { + "name": "keyword.control.end.erlang" + } + }, + "name": "meta.expression.try.erlang", + "patterns": [ + { + "include": "#internal-expression-punctuation" + }, + { + "include": "#everything-else" + } + ] + }, + { + "begin": "\\b(begin)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.begin.erlang" + } + }, + "end": "\\b(end)\\b", + "endCaptures": { + "1": { + "name": "keyword.control.end.erlang" + } + }, + "name": "meta.expression.begin.erlang", + "patterns": [ + { + "include": "#internal-expression-punctuation" + }, + { + "include": "#everything-else" + } + ] + }, + { + "begin": "\\b(maybe)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.maybe.erlang" + } + }, + "end": "\\b(end)\\b", + "endCaptures": { + "1": { + "name": "keyword.control.end.erlang" + } + }, + "name": "meta.expression.maybe.erlang", + "patterns": [ + { + "include": "#internal-expression-punctuation" + }, + { + "include": "#everything-else" + } + ] + } + ] + }, + "function": { + "begin": "^\\s*+([a-z][a-zA-Z\\d@_]*+|'[^']*+')\\s*+(?=\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.definition.erlang" + } + }, + "end": "(\\.)", + "endCaptures": { + "1": { + "name": "punctuation.terminator.function.erlang" + } + }, + "name": "meta.function.erlang", + "patterns": [ + { + "captures": { + "1": { + "name": "entity.name.function.erlang" + } + }, + "match": "^\\s*+([a-z][a-zA-Z\\d@_]*+|'[^']*+')\\s*+(?=\\()" + }, + { + "begin": "(?=\\()", + "end": "(;)|(?=\\.)", + "endCaptures": { + "1": { + "name": "punctuation.separator.clauses.erlang" + } + }, + "patterns": [ + { + "include": "#parenthesized-expression" + }, + { + "include": "#internal-function-parts" + } + ] + }, + { + "include": "#everything-else" + } + ] + }, + "function-call": { + "begin": "(?=([a-z][a-zA-Z\\d@_]*+|'[^']*+'|_[a-zA-Z\\d@_]++|[A-Z][a-zA-Z\\d@_]*+)\\s*+(\\(|:\\s*+([a-z][a-zA-Z\\d@_]*+|'[^']*+'|_[a-zA-Z\\d@_]++|[A-Z][a-zA-Z\\d@_]*+)\\s*+\\())", + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + } + }, + "name": "meta.function-call.erlang", + "patterns": [ + { + "begin": "((erlang)\\s*+(:)\\s*+)?(is_atom|is_binary|is_constant|is_float|is_function|is_integer|is_list|is_number|is_pid|is_port|is_reference|is_tuple|is_record|abs|element|hd|length|node|round|self|size|tl|trunc)\\s*+(\\()", + "beginCaptures": { + "2": { + "name": "entity.name.type.class.module.erlang" + }, + "3": { + "name": "punctuation.separator.module-function.erlang" + }, + "4": { + "name": "entity.name.function.guard.erlang" + }, + "5": { + "name": "punctuation.definition.parameters.begin.erlang" + } + }, + "end": "(?=\\))", + "patterns": [ + { + "match": ",", + "name": "punctuation.separator.parameters.erlang" + }, + { + "include": "#everything-else" + } + ] + }, + { + "begin": "((([a-z][a-zA-Z\\d@_]*+|'[^']*+')|(_[a-zA-Z\\d@_]++|[A-Z][a-zA-Z\\d@_]*+))\\s*+(:)\\s*+)?(([a-z][a-zA-Z\\d@_]*+|'[^']*+')|(_[a-zA-Z\\d@_]++|[A-Z][a-zA-Z\\d@_]*+))\\s*+(\\()", + "beginCaptures": { + "3": { + "name": "entity.name.type.class.module.erlang" + }, + "4": { + "name": "variable.other.erlang" + }, + "5": { + "name": "punctuation.separator.module-function.erlang" + }, + "7": { + "name": "entity.name.function.erlang" + }, + "8": { + "name": "variable.other.erlang" + }, + "9": { + "name": "punctuation.definition.parameters.begin.erlang" + } + }, + "end": "(?=\\))", + "patterns": [ + { + "match": ",", + "name": "punctuation.separator.parameters.erlang" + }, + { + "include": "#everything-else" + } + ] + } + ] + }, + "import-export-directive": { + "patterns": [ + { + "begin": "^\\s*+(-)\\s*+(import)\\s*+(\\()\\s*+([a-z][a-zA-Z\\d@_]*+|'[^']*+')\\s*+(,)", + "beginCaptures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.import.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.type.class.module.erlang" + }, + "5": { + "name": "punctuation.separator.parameters.erlang" + } + }, + "end": "(\\))\\s*+(\\.)", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "2": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "name": "meta.directive.import.erlang", + "patterns": [ + { + "include": "#internal-function-list" + } + ] + }, + { + "begin": "^\\s*+(-)\\s*+(export)\\s*+(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.export.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + } + }, + "end": "(\\))\\s*+(\\.)", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "2": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "name": "meta.directive.export.erlang", + "patterns": [ + { + "include": "#internal-function-list" + } + ] + } + ] + }, + "internal-expression-punctuation": { + "captures": { + "1": { + "name": "punctuation.separator.clause-head-body.erlang" + }, + "2": { + "name": "punctuation.separator.clauses.erlang" + }, + "3": { + "name": "punctuation.separator.expressions.erlang" + } + }, + "match": "(->)|(;)|(,)" + }, + "internal-function-list": { + "begin": "(\\[)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.list.begin.erlang" + } + }, + "end": "(\\])", + "endCaptures": { + "1": { + "name": "punctuation.definition.list.end.erlang" + } + }, + "name": "meta.structure.list.function.erlang", + "patterns": [ + { + "begin": "([a-z][a-zA-Z\\d@_]*+|'[^']*+')\\s*+(/)", + "beginCaptures": { + "1": { + "name": "entity.name.function.erlang" + }, + "2": { + "name": "punctuation.separator.function-arity.erlang" + } + }, + "end": "(,)|(?=\\])", + "endCaptures": { + "1": { + "name": "punctuation.separator.list.erlang" + } + }, + "patterns": [ + { + "include": "#everything-else" + } + ] + }, + { + "include": "#everything-else" + } + ] + }, + "internal-function-parts": { + "patterns": [ + { + "begin": "(?=\\()", + "end": "(->)", + "endCaptures": { + "1": { + "name": "punctuation.separator.clause-head-body.erlang" + } + }, + "patterns": [ + { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.parameters.begin.erlang" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + } + }, + "patterns": [ + { + "match": ",", + "name": "punctuation.separator.parameters.erlang" + }, + { + "include": "#everything-else" + } + ] + }, + { + "match": ",|;", + "name": "punctuation.separator.guards.erlang" + }, + { + "include": "#everything-else" + } + ] + }, + { + "match": ",", + "name": "punctuation.separator.expressions.erlang" + }, + { + "include": "#everything-else" + } + ] + }, + "internal-record-body": { + "begin": "(\\{)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.class.record.begin.erlang" + } + }, + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.definition.class.record.end.erlang" + } + }, + "name": "meta.structure.record.erlang", + "patterns": [ + { + "begin": "(([a-z][a-zA-Z\\d@_]*+|'[^']*+')|(_))", + "beginCaptures": { + "2": { + "name": "variable.other.field.erlang" + }, + "3": { + "name": "variable.language.omitted.field.erlang" + } + }, + "end": "(,)|(?=\\})", + "endCaptures": { + "1": { + "name": "punctuation.separator.class.record.erlang" + } + }, + "patterns": [ + { + "include": "#everything-else" + } + ] + }, + { + "include": "#everything-else" + } + ] + }, + "internal-type-specifiers": { + "begin": "(/)", + "beginCaptures": { + "1": { + "name": "punctuation.separator.value-type.erlang" + } + }, + "end": "(?=,|:|>>)", + "patterns": [ + { + "captures": { + "1": { + "name": "storage.type.erlang" + }, + "2": { + "name": "storage.modifier.signedness.erlang" + }, + "3": { + "name": "storage.modifier.endianness.erlang" + }, + "4": { + "name": "storage.modifier.unit.erlang" + }, + "5": { + "name": "punctuation.separator.unit-specifiers.erlang" + }, + "6": { + "name": "constant.numeric.integer.decimal.erlang" + }, + "7": { + "name": "punctuation.separator.type-specifiers.erlang" + } + }, + "match": "(integer|float|binary|bytes|bitstring|bits|utf8|utf16|utf32)|(signed|unsigned)|(big|little|native)|(unit)(:)(\\d++)|(-)" + } + ] + }, + "keyword": { + "match": "\\b(after|begin|case|catch|cond|end|fun|if|let|of|try|receive|when|maybe|else)\\b", + "name": "keyword.control.erlang" + }, + "language-constant": { + "match": "\\b(false|true|undefined)\\b", + "name": "constant.language" + }, + "list": { + "begin": "(\\[)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.list.begin.erlang" + } + }, + "end": "(\\])", + "endCaptures": { + "1": { + "name": "punctuation.definition.list.end.erlang" + } + }, + "name": "meta.structure.list.erlang", + "patterns": [ + { + "match": "\\||\\|\\||,", + "name": "punctuation.separator.list.erlang" + }, + { + "include": "#everything-else" + } + ] + }, + "macro-directive": { + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.ifdef.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.function.macro.erlang" + }, + "5": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "6": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "match": "^\\s*+(-)\\s*+(ifdef)\\s*+(\\()\\s*+([a-zA-z\\d@_]++)\\s*+(\\))\\s*+(\\.)", + "name": "meta.directive.ifdef.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.ifndef.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.function.macro.erlang" + }, + "5": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "6": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "match": "^\\s*+(-)\\s*+(ifndef)\\s*+(\\()\\s*+([a-zA-z\\d@_]++)\\s*+(\\))\\s*+(\\.)", + "name": "meta.directive.ifndef.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.undef.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.function.macro.erlang" + }, + "5": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "6": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "match": "^\\s*+(-)\\s*+(undef)\\s*+(\\()\\s*+([a-zA-z\\d@_]++)\\s*+(\\))\\s*+(\\.)", + "name": "meta.directive.undef.erlang" + } + ] + }, + "macro-usage": { + "captures": { + "1": { + "name": "keyword.operator.macro.erlang" + }, + "2": { + "name": "entity.name.function.macro.erlang" + } + }, + "match": "(\\?\\??)\\s*+([a-zA-Z\\d@_]++)", + "name": "meta.macro-usage.erlang" + }, + "module-directive": { + "captures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.module.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.type.class.module.definition.erlang" + }, + "5": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "6": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "match": "^\\s*+(-)\\s*+(module)\\s*+(\\()\\s*+([a-z][a-zA-Z\\d@_]*+)\\s*+(\\))\\s*+(\\.)", + "name": "meta.directive.module.erlang" + }, + "number": { + "begin": "(?=\\d)", + "end": "(?!\\d)", + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.separator.integer-float.erlang" + }, + "2": { + "name": "punctuation.separator.float-exponent.erlang" + } + }, + "match": "\\d++(\\.)\\d++([eE][\\+\\-]?\\d++)?", + "name": "constant.numeric.float.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "2(#)([0-1]++_)*[0-1]++", + "name": "constant.numeric.integer.binary.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "3(#)([0-2]++_)*[0-2]++", + "name": "constant.numeric.integer.base-3.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "4(#)([0-3]++_)*[0-3]++", + "name": "constant.numeric.integer.base-4.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "5(#)([0-4]++_)*[0-4]++", + "name": "constant.numeric.integer.base-5.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "6(#)([0-5]++_)*[0-5]++", + "name": "constant.numeric.integer.base-6.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "7(#)([0-6]++_)*[0-6]++", + "name": "constant.numeric.integer.base-7.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "8(#)([0-7]++_)*[0-7]++", + "name": "constant.numeric.integer.octal.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "9(#)([0-8]++_)*[0-8]++", + "name": "constant.numeric.integer.base-9.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "10(#)(\\d++_)*\\d++", + "name": "constant.numeric.integer.decimal.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "11(#)([\\daA]++_)*[\\daA]++", + "name": "constant.numeric.integer.base-11.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "12(#)([\\da-bA-B]++_)*[\\da-bA-B]++", + "name": "constant.numeric.integer.base-12.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "13(#)([\\da-cA-C]++_)*[\\da-cA-C]++", + "name": "constant.numeric.integer.base-13.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "14(#)([\\da-dA-D]++_)*[\\da-dA-D]++", + "name": "constant.numeric.integer.base-14.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "15(#)([\\da-eA-E]++_)*[\\da-eA-E]++", + "name": "constant.numeric.integer.base-15.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "16(#)([\\da-fA-F]++_)*[\\da-fA-F]++", + "name": "constant.numeric.integer.hexadecimal.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "17(#)([\\da-gA-G]++_)*[\\da-gA-G]++", + "name": "constant.numeric.integer.base-17.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "18(#)([\\da-hA-H]++_)*[\\da-hA-H]++", + "name": "constant.numeric.integer.base-18.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "19(#)([\\da-iA-I]++_)*[\\da-iA-I]++", + "name": "constant.numeric.integer.base-19.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "20(#)([\\da-jA-J]++_)*[\\da-jA-J]++", + "name": "constant.numeric.integer.base-20.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "21(#)([\\da-kA-K]++_)*[\\da-kA-K]++", + "name": "constant.numeric.integer.base-21.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "22(#)([\\da-lA-L]++_)*[\\da-lA-L]++", + "name": "constant.numeric.integer.base-22.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "23(#)([\\da-mA-M]++_)*[\\da-mA-M]++", + "name": "constant.numeric.integer.base-23.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "24(#)([\\da-nA-N]++_)*[\\da-nA-N]++", + "name": "constant.numeric.integer.base-24.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "25(#)([\\da-oA-O]++_)*[\\da-oA-O]++", + "name": "constant.numeric.integer.base-25.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "26(#)([\\da-pA-P]++_)*[\\da-pA-P]++", + "name": "constant.numeric.integer.base-26.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "27(#)([\\da-qA-Q]++_)*[\\da-qA-Q]++", + "name": "constant.numeric.integer.base-27.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "28(#)([\\da-rA-R]++_)*[\\da-rA-R]++", + "name": "constant.numeric.integer.base-28.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "29(#)([\\da-sA-S]++_)*[\\da-sA-S]++", + "name": "constant.numeric.integer.base-29.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "30(#)([\\da-tA-T]++_)*[\\da-tA-T]++", + "name": "constant.numeric.integer.base-30.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "31(#)([\\da-uA-U]++_)*[\\da-uA-U]++", + "name": "constant.numeric.integer.base-31.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "32(#)([\\da-vA-V]++_)*[\\da-vA-V]++", + "name": "constant.numeric.integer.base-32.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "33(#)([\\da-wA-W]++_)*[\\da-wA-W]++", + "name": "constant.numeric.integer.base-33.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "34(#)([\\da-xA-X]++_)*[\\da-xA-X]++", + "name": "constant.numeric.integer.base-34.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "35(#)([\\da-yA-Y]++_)*[\\da-yA-Y]++", + "name": "constant.numeric.integer.base-35.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.separator.base-integer.erlang" + } + }, + "match": "36(#)([\\da-zA-Z]++_)*[\\da-zA-Z]++", + "name": "constant.numeric.integer.base-36.erlang" + }, + { + "match": "\\d++#([\\da-zA-Z]++_)*[\\da-zA-Z]++", + "name": "invalid.illegal.integer.erlang" + }, + { + "match": "(\\d++_)*\\d++", + "name": "constant.numeric.integer.decimal.erlang" + } + ] + }, + "parenthesized-expression": { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.expression.begin.erlang" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.expression.end.erlang" + } + }, + "name": "meta.expression.parenthesized", + "patterns": [ + { + "include": "#everything-else" + } + ] + }, + "record-directive": { + "begin": "^\\s*+(-)\\s*+(record)\\s*+(\\()\\s*+([a-z][a-zA-Z\\d@_]*+|'[^']*+')\\s*+(,)", + "beginCaptures": { + "1": { + "name": "punctuation.section.directive.begin.erlang" + }, + "2": { + "name": "keyword.control.directive.import.erlang" + }, + "3": { + "name": "punctuation.definition.parameters.begin.erlang" + }, + "4": { + "name": "entity.name.type.class.record.definition.erlang" + }, + "5": { + "name": "punctuation.separator.parameters.erlang" + } + }, + "end": "(\\))\\s*+(\\.)", + "endCaptures": { + "1": { + "name": "punctuation.definition.parameters.end.erlang" + }, + "2": { + "name": "punctuation.section.directive.end.erlang" + } + }, + "name": "meta.directive.record.erlang", + "patterns": [ + { + "include": "#internal-record-body" + }, + { + "include": "#comment" + } + ] + }, + "record-usage": { + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.operator.record.erlang" + }, + "2": { + "name": "entity.name.type.class.record.erlang" + }, + "3": { + "name": "punctuation.separator.record-field.erlang" + }, + "4": { + "name": "variable.other.field.erlang" + } + }, + "match": "(#)\\s*+([a-z][a-zA-Z\\d@_]*+|'[^']*+')\\s*+(\\.)\\s*+([a-z][a-zA-Z\\d@_]*+|'[^']*+')", + "name": "meta.record-usage.erlang" + }, + { + "begin": "(#)\\s*+([a-z][a-zA-Z\\d@_]*+|'[^']*+')", + "beginCaptures": { + "1": { + "name": "keyword.operator.record.erlang" + }, + "2": { + "name": "entity.name.type.class.record.erlang" + } + }, + "end": "(?<=\\})", + "name": "meta.record-usage.erlang", + "patterns": [ + { + "include": "#internal-record-body" + } + ] + } + ] + }, + "string": { + "begin": "(\")", + "beginCaptures": { + "1": { + "name": "punctuation.definition.string.begin.erlang" + } + }, + "end": "(\")", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.erlang" + } + }, + "name": "string.quoted.double.erlang", + "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.definition.escape.erlang" + }, + "3": { + "name": "punctuation.definition.escape.erlang" + } + }, + "match": "(\\\\)([bdefnrstv\\\\'\"]|(\\^)[@-_a-z]|[0-7]{1,3}|x[\\da-fA-F]{2})", + "name": "constant.character.escape.erlang" + }, + { + "match": "\\\\\\^?.?", + "name": "invalid.illegal.string.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.placeholder.erlang" + }, + "3": { + "name": "punctuation.separator.placeholder-parts.erlang" + }, + "4": { + "name": "punctuation.separator.placeholder-parts.erlang" + }, + "6": { + "name": "punctuation.separator.placeholder-parts.erlang" + }, + "8": { + "name": "punctuation.separator.placeholder-parts.erlang" + }, + "10": { + "name": "punctuation.separator.placeholder-parts.erlang" + }, + "12": { + "name": "punctuation.separator.placeholder-parts.erlang" + } + }, + "match": "(~)((\\-)?\\d++|(\\*))?((\\.)(\\d++|(\\*)))?((\\.)((\\*)|.))?[~cfegswpWPBX#bx\\+ni]", + "name": "constant.other.placeholder.erlang" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.placeholder.erlang" + }, + "2": { + "name": "punctuation.separator.placeholder-parts.erlang" + } + }, + "match": "(~)(\\*)?(\\d++)?[~du\\-#fsacl]", + "name": "constant.other.placeholder.erlang" + }, + { + "match": "~[^\"]?", + "name": "invalid.illegal.string.erlang" + } + ] + }, + "symbolic-operator": { + "match": "\\+\\+|\\+|--|-|\\*|/=|/|=/=|=:=|==|=<|=|<-|<|>=|>|!|::|\\?=", + "name": "keyword.operator.symbolic.erlang" + }, + "textual-operator": { + "match": "\\b(andalso|band|and|bxor|xor|bor|orelse|or|bnot|not|bsl|bsr|div|rem)\\b", + "name": "keyword.operator.textual.erlang" + }, + "tuple": { + "begin": "(\\{)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tuple.begin.erlang" + } + }, + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.definition.tuple.end.erlang" + } + }, + "name": "meta.structure.tuple.erlang", + "patterns": [ + { + "match": ",", + "name": "punctuation.separator.tuple.erlang" + }, + { + "include": "#everything-else" + } + ] + }, + "variable": { + "captures": { + "1": { + "name": "variable.other.erlang" + }, + "2": { + "name": "variable.language.omitted.erlang" + } + }, + "match": "(_[a-zA-Z\\d@_]++|[A-Z][a-zA-Z\\d@_]*+)|(_)" + } + }, + "scopeName": "source.erlang", + "uuid": "58EA597D-5158-4BF7-9FB2-B05135D1E166" +} \ No newline at end of file diff --git a/src/Resources/Grammars/ocaml.json b/src/Resources/Grammars/ocaml.json new file mode 100644 index 0000000000..26674d6eb5 --- /dev/null +++ b/src/Resources/Grammars/ocaml.json @@ -0,0 +1,2961 @@ +{ + "name": "ocaml", + "scopeName": "source.ocaml", + "fileTypes": [".ml", ".mli"], + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "include": "#decl" + } + ], + "repository": { + "attribute": { + "begin": "(\\[)[[:space:]]*((?|~$])@{1,3}(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\]", + "beginCaptures": { + "1": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + }, + "2": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "endCaptures": { + "0": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + } + }, + "patterns": [ + { + "include": "#attributePayload" + } + ] + }, + "attributeIdentifier": { + "match": "((?|~$])%(?![#\\-:!?.@*/&%^+<=>|~$]))((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))", + "captures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "punctuation.definition.tag" + } + } + }, + "attributePayload": { + "patterns": [ + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]%|^%))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "((?|~$])[:\\?](?![#\\-:!?.@*/&%^+<=>|~$]))|(?<=[[:space:]])|(?=\\])", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#pathModuleExtended" + }, + { + "include": "#pathRecord" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?=\\])", + "patterns": [ + { + "include": "#signature" + }, + { + "include": "#type" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]\\?|^\\?))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?=\\])", + "patterns": [ + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]\\?|^\\?))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?=\\])|\\bwhen\\b", + "endCaptures": { + "1": {} + }, + "patterns": [ + { + "include": "#pattern" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]when|^when))(?![[:word:]]))", + "end": "(?=\\])", + "patterns": [ + { + "include": "#term" + } + ] + } + ] + }, + { + "include": "#term" + } + ] + }, + "bindClassTerm": { + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]and|^and|[^[:word:]]class|^class|[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?|~$])(:)|(=)(?![#\\-:!?.@*/&%^+<=>|~$])|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]and|^and|[^[:word:]]class|^class|[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?=(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)[[:space:]]*,|[^[:space:][:lower:]%])|(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)|(?=\\btype\\b)", + "endCaptures": { + "0": { + "name": "entity.name.function strong emphasis" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + } + ] + }, + { + "begin": "\\[", + "end": "\\]", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + }, + { + "include": "#bindTermArgs" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?|~$])=(?![#\\-:!?.@*/&%^+<=>|~$])|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|val)\\b)", + "endCaptures": { + "0": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#literalClassType" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\band\\b|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + } + ] + }, + "bindClassType": { + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]and|^and|[^[:word:]]class|^class|[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?|~$])(:)|(=)(?![#\\-:!?.@*/&%^+<=>|~$])|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]and|^and|[^[:word:]]class|^class|[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?=(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)[[:space:]]*,|[^[:space:][:lower:]%])|(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)|(?=\\btype\\b)", + "endCaptures": { + "0": { + "name": "entity.name.function strong emphasis" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + } + ] + }, + { + "begin": "\\[", + "end": "\\]", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + }, + { + "include": "#bindTermArgs" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?|~$])=(?![#\\-:!?.@*/&%^+<=>|~$])|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|val)\\b)", + "endCaptures": { + "0": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#literalClassType" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\band\\b|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + } + }, + "patterns": [ + { + "include": "#literalClassType" + } + ] + } + ] + }, + "bindConstructor": { + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]exception|^exception))(?![[:word:]]))|(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]\\+=|^\\+=|[^#\\-:!?.@*/&%^+<=>|~$]=|^=|[^#\\-:!?.@*/&%^+<=>|~$]\\||^\\|))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(:)|(\\bof\\b)|((?|~$])\\|(?![#\\-:!?.@*/&%^+<=>|~$]))|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "punctuation.definition.tag" + }, + "3": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + }, + { + "match": "\\.\\.", + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + { + "match": "\\b(?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)\\b(?![[:space:]]*(?:\\.|\\([^\\*]))", + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + }, + { + "include": "#type" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))|(?:(?<=(?:[^[:word:]]of|^of))(?![[:word:]]))", + "end": "(?|~$])\\|(?![#\\-:!?.@*/&%^+<=>|~$])|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "0": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, + "bindSignature": { + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?:(?<=(?:[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?|~$])=(?![#\\-:!?.@*/&%^+<=>|~$])", + "endCaptures": { + "0": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pathModuleExtended" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\band\\b|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + } + }, + "patterns": [ + { + "include": "#signature" + } + ] + } + ] + }, + "bindStructure": { + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?:(?<=(?:[^[:word:]]and|^and))(?![[:word:]]))|(?=[[:upper:]])", + "end": "(?|~$])(:(?!=))|(:?=)(?![#\\-:!?.@*/&%^+<=>|~$])|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|open|type|val)\\b)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "match": "\\bmodule\\b", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + }, + { + "match": "(?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)", + "name": "entity.name.function strong emphasis" + }, + { + "begin": "\\((?!\\))", + "end": "\\)", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?|~$]):(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "(?=\\))", + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + } + }, + "patterns": [ + { + "include": "#signature" + } + ] + }, + { + "include": "#variableModule" + } + ] + }, + { + "include": "#literalUnit" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\b(and)\\b|((?|~$])=(?![#\\-:!?.@*/&%^+<=>|~$]))|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#signature" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:=|^:=|[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\b(?:(and)|(with))\\b|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + }, + "2": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + } + }, + "patterns": [ + { + "include": "#structure" + } + ] + } + ] + }, + "bindTerm": { + "patterns": [ + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]!|^!))(?![#\\-:!?.@*/&%^+<=>|~$]))|(?:(?<=(?:[^[:word:]]and|^and|[^[:word:]]external|^external|[^[:word:]]let|^let|[^[:word:]]method|^method|[^[:word:]]val|^val))(?![[:word:]]))", + "end": "(\\bmodule\\b)|(\\bopen\\b)|(?|~$])(:)|((?|~$])=(?![#\\-:!?.@*/&%^+<=>|~$]))(?![#\\-:!?.@*/&%^+<=>|~$])|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "1": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + }, + "2": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "3": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "4": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]!|^!))(?![#\\-:!?.@*/&%^+<=>|~$]))|(?:(?<=(?:[^[:word:]]and|^and|[^[:word:]]external|^external|[^[:word:]]let|^let|[^[:word:]]method|^method|[^[:word:]]val|^val))(?![[:word:]]))", + "end": "(?=\\b(?:module|open)\\b)|(?=(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)[[:space:]]*,|[^[:space:][:lower:]%])|(\\brec\\b)|((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "2": { + "name": "entity.name.function strong emphasis" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + }, + { + "include": "#comment" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]rec|^rec))(?![[:word:]]))", + "end": "((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))|(?=[^[:space:][:alpha:]])", + "endCaptures": { + "0": { + "name": "entity.name.function strong emphasis" + } + }, + "patterns": [ + { + "include": "#bindTermArgs" + } + ] + }, + { + "include": "#bindTermArgs" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]module|^module))(?![[:word:]]))", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "include": "#declModule" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]open|^open))(?![[:word:]]))", + "end": "(?=\\bin\\b)|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "include": "#pathModuleSimple" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?|~$])=(?![#\\-:!?.@*/&%^+<=>|~$])|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "0": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\btype\\b|(?=[^[:space:]])", + "endCaptures": { + "0": { + "name": "keyword.control" + } + } + }, + { + "begin": "(?:(?<=(?:[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?|~$])\\.(?![#\\-:!?.@*/&%^+<=>|~$])", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#pattern" + } + ] + }, + { + "include": "#type" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\band\\b|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + } + ] + }, + "bindTermArgs": { + "patterns": [ + { + "begin": "~|\\?", + "end": ":|(?=[^[:space:]])", + "applyEndPatternLast": true, + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "endCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]~|^~|[^#\\-:!?.@*/&%^+<=>|~$]\\?|^\\?))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)|(?<=\\))", + "endCaptures": { + "0": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "\\((?!\\*)", + "end": "\\)", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "begin": "(?<=\\()", + "end": ":|=", + "endCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "match": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + } + ] + }, + { + "begin": "(?<=:)", + "end": "=|(?=\\))", + "endCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?=\\))", + "patterns": [ + { + "include": "#term" + } + ] + } + ] + } + ] + } + ] + }, + { + "include": "#pattern" + } + ] + }, + "bindType": { + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]and|^and|[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?|~$])\\+=|=(?![#\\-:!?.@*/&%^+<=>|~$])|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "0": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + }, + { + "include": "#pathType" + }, + { + "match": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "entity.name.function strong" + }, + { + "include": "#type" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]\\+=|^\\+=|[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\band\\b|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + } + }, + "patterns": [ + { + "include": "#bindConstructor" + } + ] + } + ] + }, + "comment": { + "patterns": [ + { + "include": "#attribute" + }, + { + "include": "#extension" + }, + { + "include": "#commentBlock" + }, + { + "include": "#commentDoc" + } + ] + }, + "commentBlock": { + "begin": "\\(\\*(?!\\*[^\\)])", + "end": "\\*\\)", + "name": "comment", + "contentName": "emphasis", + "patterns": [ + { + "include": "#commentBlock" + }, + { + "include": "#commentDoc" + } + ] + }, + "commentDoc": { + "begin": "\\(\\*\\*", + "end": "\\*\\)", + "name": "comment", + "patterns": [ + { + "match": "\\*" + }, + { + "include": "#comment" + } + ] + }, + "decl": { + "patterns": [ + { + "include": "#declClass" + }, + { + "include": "#declException" + }, + { + "include": "#declInclude" + }, + { + "include": "#declModule" + }, + { + "include": "#declOpen" + }, + { + "include": "#declTerm" + }, + { + "include": "#declType" + } + ] + }, + "declClass": { + "begin": "\\bclass\\b", + "end": ";;|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "entity.name.class constant.numeric markup.underline" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "begin": "(?:(?<=(?:[^[:word:]]class|^class))(?![[:word:]]))", + "end": "\\btype\\b|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|val)\\b)", + "beginCaptures": { + "0": { + "name": "entity.name.class constant.numeric markup.underline" + } + }, + "endCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "include": "#bindClassTerm" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "include": "#bindClassType" + } + ] + } + ] + }, + "declException": { + "begin": "\\bexception\\b", + "end": ";;|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "keyword markup.underline" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + }, + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "include": "#bindConstructor" + } + ] + }, + "declInclude": { + "begin": "\\binclude\\b", + "end": ";;|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + }, + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "include": "#signature" + } + ] + }, + "declModule": { + "begin": "(?:(?<=(?:[^[:word:]]module|^module))(?![[:word:]]))|\\bmodule\\b", + "end": ";;|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename markup.underline" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "begin": "(?:(?<=(?:[^[:word:]]module|^module))(?![[:word:]]))", + "end": "(\\btype\\b)|(?=[[:upper:]])", + "endCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + }, + { + "include": "#comment" + }, + { + "match": "\\brec\\b", + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]type|^type))(?![[:word:]]))", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "include": "#bindSignature" + } + ] + }, + { + "begin": "(?=[[:upper:]])", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "include": "#bindStructure" + } + ] + } + ] + }, + "declOpen": { + "begin": "\\bopen\\b", + "end": ";;|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + }, + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "include": "#pathModuleExtended" + } + ] + }, + "declTerm": { + "begin": "\\b(?:(external|val)|(method)|(let))\\b(!?)", + "end": ";;|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "1": { + "name": "support.type markup.underline" + }, + "2": { + "name": "storage.type markup.underline" + }, + "3": { + "name": "keyword.control markup.underline" + }, + "4": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "include": "#bindTerm" + } + ] + }, + "declType": { + "begin": "(?:(?<=(?:[^[:word:]]type|^type))(?![[:word:]]))|\\btype\\b", + "end": ";;|(?=\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "keyword markup.underline" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "include": "#bindType" + } + ] + }, + "extension": { + "begin": "(\\[)((?|~$])%{1,3}(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\]", + "beginCaptures": { + "1": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + }, + "2": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "endCaptures": { + "0": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + } + }, + "patterns": [ + { + "include": "#attributePayload" + } + ] + }, + "literal": { + "patterns": [ + { + "include": "#termConstructor" + }, + { + "include": "#literalArray" + }, + { + "include": "#literalBoolean" + }, + { + "include": "#literalCharacter" + }, + { + "include": "#literalList" + }, + { + "include": "#literalNumber" + }, + { + "include": "#literalObjectTerm" + }, + { + "include": "#literalString" + }, + { + "include": "#literalRecord" + }, + { + "include": "#literalUnit" + } + ] + }, + "literalArray": { + "begin": "\\[\\|", + "end": "\\|\\]", + "captures": { + "0": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + }, + "literalBoolean": { + "match": "\\bfalse|true\\b", + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + }, + "literalCharacter": { + "begin": "(?|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename emphasis" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]with|^with))(?![[:word:]]))", + "end": "(:)|(=)|(;)|(?=\\})", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "support.type strong" + }, + "3": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "match": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename emphasis" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(;)|(=)|(?=\\})", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": ";|(?=\\})", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + } + ] + }, + "literalString": { + "patterns": [ + { + "begin": "\"", + "end": "\"", + "name": "string beginning.punctuation.definition.quote.markdown", + "patterns": [ + { + "include": "#literalStringEscape" + } + ] + }, + { + "begin": "(\\{)([_[:lower:]]*?)(\\|)", + "end": "(\\|)(\\2)(\\})", + "name": "string beginning.punctuation.definition.quote.markdown", + "patterns": [ + { + "include": "#literalStringEscape" + } + ] + } + ] + }, + "literalStringEscape": { + "match": "\\\\(?:[\\\\\"ntbr]|[[:digit:]][[:digit:]][[:digit:]]|x[[:xdigit:]][[:xdigit:]]|o[0-3][0-7][0-7])" + }, + "literalUnit": { + "match": "\\(\\)", + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + }, + "pathModuleExtended": { + "patterns": [ + { + "include": "#pathModulePrefixExtended" + }, + { + "match": "(?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)", + "name": "entity.name.class constant.numeric" + } + ] + }, + "pathModulePrefixExtended": { + "begin": "(?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*\\.|$|\\()", + "end": "(?![[:space:]\\.]|$|\\()", + "beginCaptures": { + "0": { + "name": "entity.name.class constant.numeric" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "\\(", + "end": "\\)", + "captures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "match": "((?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*\\)))", + "name": "string.other.link variable.language variable.parameter emphasis" + }, + { + "include": "#structure" + } + ] + }, + { + "begin": "(?|~$])\\.(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "((?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*\\.|$))|((?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*(?:$|\\()))|((?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*\\)))|(?![[:space:]\\.[:upper:]]|$|\\()", + "beginCaptures": { + "0": { + "name": "keyword strong" + } + }, + "endCaptures": { + "1": { + "name": "entity.name.class constant.numeric" + }, + "2": { + "name": "entity.name.function strong" + }, + "3": { + "name": "string.other.link variable.language variable.parameter emphasis" + } + } + } + ] + }, + "pathModulePrefixExtendedParens": { + "begin": "\\(", + "end": "\\)", + "captures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "match": "((?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*\\)))", + "name": "string.other.link variable.language variable.parameter emphasis" + }, + { + "include": "#structure" + } + ] + }, + "pathModulePrefixSimple": { + "begin": "(?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*\\.)", + "end": "(?![[:space:]\\.])", + "beginCaptures": { + "0": { + "name": "entity.name.class constant.numeric" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?|~$])\\.(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "((?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*\\.))|((?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)(?=[[:space:]]*))|(?![[:space:]\\.[:upper:]])", + "beginCaptures": { + "0": { + "name": "keyword strong" + } + }, + "endCaptures": { + "1": { + "name": "entity.name.class constant.numeric" + }, + "2": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + } + } + } + ] + }, + "pathModuleSimple": { + "patterns": [ + { + "include": "#pathModulePrefixSimple" + }, + { + "match": "(?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)", + "name": "entity.name.class constant.numeric" + } + ] + }, + "pathRecord": { + "patterns": [ + { + "begin": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "end": "(?=[^[:space:]\\.])(?!\\(\\*)", + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]\\.|^\\.))(?![#\\-:!?.@*/&%^+<=>|~$]))|(?|~$])\\.(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "((?|~$])\\.(?![#\\-:!?.@*/&%^+<=>|~$]))|((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|mutable|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))|(?<=\\))|(?<=\\])", + "beginCaptures": { + "0": { + "name": "keyword strong" + } + }, + "endCaptures": { + "1": { + "name": "keyword strong" + }, + "2": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pathModulePrefixSimple" + }, + { + "begin": "\\((?!\\*)", + "end": "\\)", + "captures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + }, + { + "begin": "\\[", + "end": "\\]", + "captures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#pattern" + } + ] + } + ] + } + ] + } + ] + }, + "pattern": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#patternArray" + }, + { + "include": "#patternLazy" + }, + { + "include": "#patternList" + }, + { + "include": "#patternMisc" + }, + { + "include": "#patternModule" + }, + { + "include": "#patternRecord" + }, + { + "include": "#literal" + }, + { + "include": "#patternParens" + }, + { + "include": "#patternType" + }, + { + "include": "#variablePattern" + }, + { + "include": "#termOperator" + } + ] + }, + "patternArray": { + "begin": "\\[\\|", + "end": "\\|\\]", + "captures": { + "0": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + } + }, + "patterns": [ + { + "include": "#pattern" + } + ] + }, + "patternLazy": { + "match": "lazy", + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "patternList": { + "begin": "\\[", + "end": "\\]", + "captures": { + "0": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + } + }, + "patterns": [ + { + "include": "#pattern" + } + ] + }, + "patternMisc": { + "match": "((?|~$]),(?![#\\-:!?.@*/&%^+<=>|~$]))|([#\\-:!?.@*/&%^+<=>|~$]+)|\\b(as)\\b", + "captures": { + "1": { + "name": "string.regexp strong" + }, + "2": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "3": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + } + }, + "patternModule": { + "begin": "\\bmodule\\b", + "end": "(?=\\))", + "beginCaptures": { + "0": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + } + }, + "patterns": [ + { + "include": "#declModule" + } + ] + }, + "patternParens": { + "begin": "\\((?!\\))", + "end": "\\)", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?|~$]):(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "(?=\\))", + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + }, + { + "include": "#pattern" + } + ] + }, + "patternRecord": { + "begin": "\\{", + "end": "\\}", + "captures": { + "0": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong strong" + } + }, + "patterns": [ + { + "begin": "(?<=\\{|;)", + "end": "(:)|(=)|(;)|(with)|(?=\\})", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "support.type strong" + }, + "3": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "4": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pathModulePrefixSimple" + }, + { + "match": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename emphasis" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]with|^with))(?![[:word:]]))", + "end": "(:)|(=)|(;)|(?=\\})", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "support.type strong" + }, + "3": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "match": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename emphasis" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(;)|(=)|(?=\\})", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": ";|(?=\\})", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#pattern" + } + ] + } + ] + }, + "patternType": { + "begin": "\\btype\\b", + "end": "(?=\\))", + "beginCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "include": "#declType" + } + ] + }, + "pragma": { + "begin": "(?|~$])#(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#literalNumber" + }, + { + "include": "#literalString" + } + ] + }, + "signature": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#signatureLiteral" + }, + { + "include": "#signatureFunctor" + }, + { + "include": "#pathModuleExtended" + }, + { + "include": "#signatureParens" + }, + { + "include": "#signatureRecovered" + }, + { + "include": "#signatureConstraints" + } + ] + }, + "signatureConstraints": { + "begin": "\\bwith\\b", + "end": "(?=\\))|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]with|^with))(?![[:word:]]))", + "end": "\\b(?:(module)|(type))\\b", + "endCaptures": { + "1": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + }, + "2": { + "name": "keyword" + } + } + }, + { + "include": "#declModule" + }, + { + "include": "#declType" + } + ] + }, + "signatureFunctor": { + "patterns": [ + { + "begin": "\\bfunctor\\b", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]functor|^functor))(?![[:word:]]))", + "end": "(\\(\\))|(\\((?!\\)))", + "endCaptures": { + "1": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + }, + "2": { + "name": "punctuation.definition.tag" + } + } + }, + { + "begin": "(?<=\\()", + "end": "(:)|(\\))", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#variableModule" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#signature" + } + ] + }, + { + "begin": "(?<=\\))", + "end": "(\\()|((?|~$])->(?![#\\-:!?.@*/&%^+<=>|~$]))", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag" + }, + "2": { + "name": "support.type strong" + } + } + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]->|^->))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "include": "#signature" + } + ] + } + ] + }, + { + "match": "(?|~$])->(?![#\\-:!?.@*/&%^+<=>|~$])", + "name": "support.type strong" + } + ] + }, + "signatureLiteral": { + "begin": "\\bsig\\b", + "end": "\\bend\\b", + "captures": { + "0": { + "name": "punctuation.definition.tag emphasis" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "include": "#decl" + } + ] + }, + "signatureParens": { + "begin": "\\((?!\\))", + "end": "\\)", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?|~$]):(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "(?=\\))", + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + } + }, + "patterns": [ + { + "include": "#signature" + } + ] + }, + { + "include": "#signature" + } + ] + }, + "signatureRecovered": { + "patterns": [ + { + "begin": "\\(|(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:|[^#\\-:!?.@*/&%^+<=>|~$]->|^->))(?![#\\-:!?.@*/&%^+<=>|~$]))|(?:(?<=(?:[^[:word:]]include|^include|[^[:word:]]open|^open))(?![[:word:]]))", + "end": "\\bmodule\\b|(?!$|[[:space:]]|\\bmodule\\b)", + "endCaptures": { + "0": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + } + } + }, + { + "begin": "(?:(?<=(?:[^[:word:]]module|^module))(?![[:word:]]))", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]module|^module))(?![[:word:]]))", + "end": "\\btype\\b", + "endCaptures": { + "0": { + "name": "keyword" + } + } + }, + { + "begin": "(?:(?<=(?:[^[:word:]]type|^type))(?![[:word:]]))", + "end": "\\bof\\b", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + } + }, + { + "begin": "(?:(?<=(?:[^[:word:]]of|^of))(?![[:word:]]))", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "include": "#signature" + } + ] + } + ] + } + ] + }, + "structure": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#structureLiteral" + }, + { + "include": "#structureFunctor" + }, + { + "include": "#pathModuleExtended" + }, + { + "include": "#structureParens" + } + ] + }, + "structureFunctor": { + "patterns": [ + { + "begin": "\\bfunctor\\b", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]functor|^functor))(?![[:word:]]))", + "end": "(\\(\\))|(\\((?!\\)))", + "endCaptures": { + "1": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + }, + "2": { + "name": "punctuation.definition.tag" + } + } + }, + { + "begin": "(?<=\\()", + "end": "(:)|(\\))", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#variableModule" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#signature" + } + ] + }, + { + "begin": "(?<=\\))", + "end": "(\\()|((?|~$])->(?![#\\-:!?.@*/&%^+<=>|~$]))", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag" + }, + "2": { + "name": "support.type strong" + } + } + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]->|^->))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "patterns": [ + { + "include": "#structure" + } + ] + } + ] + }, + { + "match": "(?|~$])->(?![#\\-:!?.@*/&%^+<=>|~$])", + "name": "support.type strong" + } + ] + }, + "structureLiteral": { + "begin": "\\bstruct\\b", + "end": "\\bend\\b", + "captures": { + "0": { + "name": "punctuation.definition.tag emphasis" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pragma" + }, + { + "include": "#decl" + } + ] + }, + "structureParens": { + "begin": "\\(", + "end": "\\)", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#structureUnpack" + }, + { + "include": "#structure" + } + ] + }, + "structureUnpack": { + "begin": "\\bval\\b", + "end": "(?=\\))", + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + } + }, + "term": { + "patterns": [ + { + "include": "#termLet" + }, + { + "include": "#termAtomic" + } + ] + }, + "termAtomic": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#termConditional" + }, + { + "include": "#termConstructor" + }, + { + "include": "#termDelim" + }, + { + "include": "#termFor" + }, + { + "include": "#termFunction" + }, + { + "include": "#literal" + }, + { + "include": "#termMatch" + }, + { + "include": "#termMatchRule" + }, + { + "include": "#termPun" + }, + { + "include": "#termOperator" + }, + { + "include": "#termTry" + }, + { + "include": "#termWhile" + }, + { + "include": "#pathRecord" + } + ] + }, + "termConditional": { + "match": "\\b(?:if|then|else)\\b", + "name": "keyword.control" + }, + "termConstructor": { + "patterns": [ + { + "include": "#pathModulePrefixSimple" + }, + { + "match": "(?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)", + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong" + } + ] + }, + "termDelim": { + "patterns": [ + { + "begin": "\\((?!\\))", + "end": "\\)", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + }, + { + "begin": "\\bbegin\\b", + "end": "\\bend\\b", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "include": "#attributeIdentifier" + }, + { + "include": "#term" + } + ] + } + ] + }, + "termFor": { + "patterns": [ + { + "begin": "\\bfor\\b", + "end": "\\bdone\\b", + "beginCaptures": { + "0": { + "name": "keyword.control" + } + }, + "endCaptures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]for|^for))(?![[:word:]]))", + "end": "(?|~$])=(?![#\\-:!?.@*/&%^+<=>|~$])", + "endCaptures": { + "0": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#pattern" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "\\b(?:downto|to)\\b", + "endCaptures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]to|^to))(?![[:word:]]))", + "end": "\\bdo\\b", + "endCaptures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]do|^do))(?![[:word:]]))", + "end": "(?=\\bdone\\b)", + "patterns": [ + { + "include": "#term" + } + ] + } + ] + } + ] + }, + "termFunction": { + "match": "\\b(?:(fun)|(function))\\b", + "captures": { + "1": { + "name": "storage.type" + }, + "2": { + "name": "storage.type" + } + } + }, + "termLet": { + "patterns": [ + { + "begin": "(?:(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=|[^#\\-:!?.@*/&%^+<=>|~$]->|^->))(?![#\\-:!?.@*/&%^+<=>|~$]))|(?<=;|\\())(?=[[:space:]]|\\blet\\b)|(?:(?<=(?:[^[:word:]]begin|^begin|[^[:word:]]do|^do|[^[:word:]]else|^else|[^[:word:]]in|^in|[^[:word:]]struct|^struct|[^[:word:]]then|^then|[^[:word:]]try|^try))(?![[:word:]]))|(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]@@|^@@))(?![#\\-:!?.@*/&%^+<=>|~$]))[[:space:]]+", + "end": "\\b(?:(and)|(let))\\b|(?=[^[:space:]])(?!\\(\\*)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + }, + "2": { + "name": "storage.type markup.underline" + } + }, + "patterns": [ + { + "include": "#comment" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]and|^and|[^[:word:]]let|^let))(?![[:word:]]))|(let)", + "end": "\\b(?:(and)|(in))\\b|(?=\\}|\\)|\\]|\\b(?:end|class|exception|external|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "1": { + "name": "storage.type markup.underline" + } + }, + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp markup.underline" + }, + "2": { + "name": "storage.type markup.underline" + } + }, + "patterns": [ + { + "include": "#bindTerm" + } + ] + } + ] + }, + "termMatch": { + "begin": "\\bmatch\\b", + "end": "\\bwith\\b", + "captures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + }, + "termMatchRule": { + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]fun|^fun|[^[:word:]]function|^function|[^[:word:]]with|^with))(?![[:word:]]))", + "end": "(?|~$])(\\|)|(->)(?![#\\-:!?.@*/&%^+<=>|~$])", + "endCaptures": { + "1": { + "name": "support.type strong" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#attributeIdentifier" + }, + { + "include": "#pattern" + } + ] + }, + { + "begin": "(?:(?<=(?:[^\\[#\\-:!?.@*/&%^+<=>|~$]\\||^\\|))(?![#\\-:!?.@*/&%^+<=>|~$]))|(?|~$])\\|(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "(?|~$])(\\|)|(->)(?![#\\-:!?.@*/&%^+<=>|~$])", + "beginCaptures": { + "0": { + "name": "support.type strong" + } + }, + "endCaptures": { + "1": { + "name": "support.type strong" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#pattern" + }, + { + "begin": "\\bwhen\\b", + "end": "(?=(?|~$])->(?![#\\-:!?.@*/&%^+<=>|~$]))", + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + } + ] + } + ] + }, + "termOperator": { + "patterns": [ + { + "begin": "(?|~$])#(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "beginCaptures": { + "0": { + "name": "keyword" + } + }, + "endCaptures": { + "0": { + "name": "entity.name.function" + } + } + }, + { + "match": "<-", + "captures": { + "0": { + "name": "keyword.control strong" + } + } + }, + { + "match": "(,|[#\\-:!?.@*/&%^+<=>|~$]+)|(;)", + "captures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "2": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + } + }, + { + "match": "\\b(?:and|assert|asr|land|lazy|lsr|lxor|mod|new|or)\\b", + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + ] + }, + "termPun": { + "begin": "(?|~$])\\?|~(?![#\\-:!?.@*/&%^+<=>|~$])", + "end": ":|(?=[^[:space:]:])", + "applyEndPatternLast": true, + "beginCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "endCaptures": { + "0": { + "name": "keyword" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]\\?|^\\?|[^#\\-:!?.@*/&%^+<=>|~$]~|^~))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "endCaptures": { + "0": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + } + } + } + ] + }, + "termTry": { + "begin": "\\btry\\b", + "end": "\\bwith\\b", + "captures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + }, + "termWhile": { + "patterns": [ + { + "begin": "\\bwhile\\b", + "end": "\\bdone\\b", + "beginCaptures": { + "0": { + "name": "keyword.control" + } + }, + "endCaptures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "begin": "(?:(?<=(?:[^[:word:]]while|^while))(?![[:word:]]))", + "end": "\\bdo\\b", + "endCaptures": { + "0": { + "name": "keyword.control" + } + }, + "patterns": [ + { + "include": "#term" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]do|^do))(?![[:word:]]))", + "end": "(?=\\bdone\\b)", + "patterns": [ + { + "include": "#term" + } + ] + } + ] + } + ] + }, + "type": { + "patterns": [ + { + "include": "#comment" + }, + { + "match": "\\bnonrec\\b", + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + { + "include": "#pathModulePrefixExtended" + }, + { + "include": "#typeLabel" + }, + { + "include": "#typeObject" + }, + { + "include": "#typeOperator" + }, + { + "include": "#typeParens" + }, + { + "include": "#typePolymorphicVariant" + }, + { + "include": "#typeRecord" + }, + { + "include": "#typeConstructor" + } + ] + }, + "typeConstructor": { + "patterns": [ + { + "begin": "(_)|((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))|(')((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))|(?<=[^\\*]\\)|\\])", + "end": "(?=\\((?!\\*)|\\*|:|,|=|\\.|>|-|\\{|\\[|\\+|\\}|\\)|\\]|;|\\|)|((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))[:space:]*(?!\\(\\*|[[:word:]])|(?=;;|\\}|\\)|\\]|\\b(?:end|and|class|exception|external|in|include|inherit|initializer|let|method|module|open|type|val)\\b)", + "beginCaptures": { + "1": { + "name": "comment constant.regexp meta.separator.markdown" + }, + "3": { + "name": "string.other.link variable.language variable.parameter emphasis strong emphasis" + }, + "4": { + "name": "keyword.control emphasis" + } + }, + "endCaptures": { + "1": { + "name": "entity.name.function strong" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pathModulePrefixExtended" + } + ] + } + ] + }, + "typeLabel": { + "patterns": [ + { + "begin": "(\\??)((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))[[:space:]]*((?|~$]):(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(?=(?|~$])->(?![#\\-:!?.@*/&%^+<=>|~$]))", + "captures": { + "1": { + "name": "keyword strong emphasis" + }, + "2": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename emphasis" + }, + "3": { + "name": "keyword" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, + "typeModule": { + "begin": "\\bmodule\\b", + "end": "(?=\\))", + "beginCaptures": { + "0": { + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename" + } + }, + "patterns": [ + { + "include": "#pathModuleExtended" + }, + { + "include": "#signatureConstraints" + } + ] + }, + "typeObject": { + "begin": "<", + "end": ">", + "captures": { + "0": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong strong" + } + }, + "patterns": [ + { + "begin": "(?<=<|;)", + "end": "(:)|(?=>)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "3": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "4": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pathModulePrefixSimple" + }, + { + "match": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename emphasis" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(;)|(?=>)", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, + "typeOperator": { + "patterns": [ + { + "match": ",|;|[#\\-:!?.@*/&%^+<=>|~$]+", + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + } + ] + }, + "typeParens": { + "begin": "\\(", + "end": "\\)", + "captures": { + "0": { + "name": "punctuation.definition.tag" + } + }, + "patterns": [ + { + "match": ",", + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + { + "include": "#typeModule" + }, + { + "include": "#type" + } + ] + }, + "typePolymorphicVariant": { + "begin": "\\[", + "end": "\\]", + "patterns": [] + }, + "typeRecord": { + "begin": "\\{", + "end": "\\}", + "captures": { + "0": { + "name": "constant.language constant.numeric entity.other.attribute-name.id.css strong strong" + } + }, + "patterns": [ + { + "begin": "(?<=\\{|;)", + "end": "(:)|(=)|(;)|(with)|(?=\\})", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "support.type strong" + }, + "3": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "4": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#pathModulePrefixSimple" + }, + { + "match": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename emphasis" + } + ] + }, + { + "begin": "(?:(?<=(?:[^[:word:]]with|^with))(?![[:word:]]))", + "end": "(:)|(=)|(;)|(?=\\})", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp strong" + }, + "2": { + "name": "support.type strong" + }, + "3": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "match": "(?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*)", + "name": "markup.inserted constant.language support.constant.property-value entity.name.filename emphasis" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]:|^:))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": "(;)|(=)|(?=\\})", + "endCaptures": { + "1": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + }, + "2": { + "name": "support.type strong" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + }, + { + "begin": "(?:(?<=(?:[^#\\-:!?.@*/&%^+<=>|~$]=|^=))(?![#\\-:!?.@*/&%^+<=>|~$]))", + "end": ";|(?=\\})", + "endCaptures": { + "0": { + "name": "variable.other.class.js message.error variable.interpolation string.regexp" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, + "variableModule": { + "match": "(?:\\b(?=[[:upper:]])[[:alpha:]_][[:word:]']*)", + "captures": { + "0": { + "name": "string.other.link variable.language variable.parameter emphasis" + } + } + }, + "variablePattern": { + "match": "(\\b_\\b)|((?:(?!\\b(?:and|'|as|asr|assert|\\*|begin|class|:|,|@|constraint|do|done|downto|else|end|=|exception|external|false|for|\\.|fun|function|functor|>|-|if|in|include|inherit|initializer|land|lazy|\\{|\\(|\\[|<|let|lor|lsl|lsr|lxor|match|method|mod|module|mutable|new|nonrec|#|object|of|open|or|%|\\+|private|\\?|\"|rec|\\\\|\\}|\\)|\\]|;|sig|/|struct|then|~|to|true|try|type|val|\\||virtual|when|while|with)\\b(?:[^']|$))\\b(?=[[:lower:]_])[[:alpha:]_][[:word:]']*))", + "captures": { + "1": { + "name": "comment constant.regexp meta.separator.markdown" + }, + "2": { + "name": "string.other.link variable.language variable.parameter emphasis" + } + } + } + } +} diff --git a/src/Resources/Grammars/ocamllex.json b/src/Resources/Grammars/ocamllex.json new file mode 100644 index 0000000000..80795ad9e0 --- /dev/null +++ b/src/Resources/Grammars/ocamllex.json @@ -0,0 +1,302 @@ +{ + "name": "ocamllex", + "scopeName": "source.ocamllex", + "fileTypes": [ "mll" ], + "foldingStartMarker": "{", + "foldingStopMarker": "}", + "keyEquivalent": "^~O", + "patterns": [ + { + "begin": "^\\s*({)", + "beginCaptures": { + "1": { + "name": "punctuation.section.embedded.ocaml.begin.ocamllex" + } + }, + "end": "^\\s*(})", + "endCaptures": { + "1": { + "name": "punctuation.section.embedded.ocaml.end.ocamllex" + } + }, + "name": "meta.embedded.ocaml", + "patterns": [ + { + "include": "source.ocaml" + } + ] + }, + { + "begin": "\\b(let)\\s+([a-z][a-zA-Z0-9'_]*)\\s+=", + "beginCaptures": { + "1": { + "name": "keyword.other.pattern-definition.ocamllex" + }, + "2": { + "name": "entity.name.type.pattern.stupid-goddamn-hack.ocamllex" + } + }, + "end": "^(?:\\s*let)|(?:\\s*(rule|$))", + "name": "meta.pattern-definition.ocaml", + "patterns": [ + { + "include": "#match-patterns" + } + ] + }, + { + "begin": "(rule|and)\\s+([a-z][a-zA-Z0-9_]*)\\s+(=)\\s+(parse)(?=\\s*$)|((?", + "endCaptures": { + "0": { + "name": "punctuation.definition.type-declaration.end.ocamlyacc" + } + }, + "name": "meta.token.type-declaration.ocamlyacc", + "patterns": [ + { + "include": "source.ocaml" + } + ] + } + ] + } + } +} diff --git a/src/Resources/Grammars/swift.json b/src/Resources/Grammars/swift.json new file mode 100644 index 0000000000..c5629f30a7 --- /dev/null +++ b/src/Resources/Grammars/swift.json @@ -0,0 +1,3253 @@ +{ + "comment": "See swift.tmbundle/grammar-test.swift for test cases.", + "fileTypes": ["swift"], + "firstLineMatch": "^#!/.*\\bswift", + "keyEquivalent": "^~S", + "scopeName": "source.swift", + "uuid": "8C743E89-9C8D-4833-8149-6500D82AA463", + "name": "Swift", + "patterns": [{ "include": "#root" }], + + "repository": { + "async-throws": { + "match": "\\b(?:(throws\\s+async|rethrows\\s+async)|(throws|rethrows)|(async))\\b", + "captures": { + "1": { "name": "invalid.illegal.await-must-precede-throws.swift" }, + "2": { "name": "storage.modifier.exception.swift" }, + "3": { "name": "storage.modifier.async.swift" } + } + }, + + "attributes": { + "patterns": [ + { + "name": "meta.attribute.available.swift", + "begin": "((@)available)(\\()", + "end": "\\)", + "beginCaptures": { + "1": { "name": "storage.modifier.attribute.swift" }, + "2": { "name": "punctuation.definition.attribute.swift" }, + "3": { "name": "punctuation.definition.arguments.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.arguments.end.swift" } + }, + "patterns": [ + { + "match": "\\b(swift|(?:iOS|macOS|OSX|watchOS|tvOS|visionOS|UIKitForMac)(?:ApplicationExtension)?)\\b(?:\\s+([0-9]+(?:\\.[0-9]+)*\\b))?", + "captures": { + "1": { "name": "keyword.other.platform.os.swift" }, + "2": { "name": "constant.numeric.swift" } + } + }, + + { + "begin": "\\b(introduced|deprecated|obsoleted)\\s*(:)\\s*", + "end": "(?!\\G)", + "beginCaptures": { + "1": { "name": "keyword.other.swift" }, + "2": { "name": "punctuation.separator.key-value.swift" } + }, + "patterns": [ + { + "name": "constant.numeric.swift", + "match": "\\b[0-9]+(?:\\.[0-9]+)*\\b" + } + ] + }, + + { + "begin": "\\b(message|renamed)\\s*(:)\\s*(?=\")", + "end": "(?!\\G)", + "beginCaptures": { + "1": { "name": "keyword.other.swift" }, + "2": { "name": "punctuation.separator.key-value.swift" } + }, + "patterns": [{ "include": "#literals" }] + }, + + { + "match": "(?:(\\*)|\\b(deprecated|unavailable|noasync)\\b)\\s*(.*?)(?=[,)])", + "captures": { + "1": { "name": "keyword.other.platform.all.swift" }, + "2": { "name": "keyword.other.swift" }, + "3": { "name": "invalid.illegal.character-not-allowed-here.swift" } + } + } + ] + }, + + { + "name": "meta.attribute.objc.swift", + "begin": "((@)objc)(\\()", + "end": "\\)", + "beginCaptures": { + "1": { "name": "storage.modifier.attribute.swift" }, + "2": { "name": "punctuation.definition.attribute.swift" }, + "3": { "name": "punctuation.definition.arguments.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.arguments.end.swift" } + }, + "patterns": [ + { + "name": "entity.name.function.swift", + "match": "\\w*(?::(?:\\w*:)*(\\w*))?", + "captures": { + "1": { "name": "invalid.illegal.missing-colon-after-selector-piece.swift" } + } + } + ] + }, + + { + "comment": "any other attribute", + "name": "meta.attribute.swift", + "begin": "(@)(?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k)", + "end": "(?!\\G\\()", + "beginCaptures": { + "0": { "name": "storage.modifier.attribute.swift" }, + "1": { "name": "punctuation.definition.attribute.swift" }, + "2": { "name": "punctuation.definition.identifier.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { + "name": "meta.arguments.attribute.swift", + "begin": "\\(", + "end": "\\)", + "beginCaptures": { + "0": { "name": "punctuation.definition.arguments.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.arguments.end.swift" } + }, + "patterns": [{ "include": "#expressions" }] + } + ] + } + ] + }, + + "builtin-functions": { + "patterns": [ + { + "comment": "Member functions in the standard library in Swift 3 which may be used with trailing closures and no parentheses", + "name": "support.function.swift", + "match": "(?<=\\.)(?:s(?:ort(?:ed)?|plit)|contains|index|partition|f(?:i(?:lter|rst)|orEach|latMap)|with(?:MutableCharacters|CString|U(?:nsafe(?:Mutable(?:BufferPointer|Pointer(?:s|To(?:Header|Elements)))|BufferPointer)|TF8Buffer))|m(?:in|a(?:p|x)))(?=\\s*[({])\\b" + }, + + { + "comment": "Member functions in the standard library in Swift 3", + "name": "support.function.swift", + "match": "(?<=\\.)(?:s(?:ymmetricDifference|t(?:oreBytes|arts|ride)|ortInPlace|u(?:ccessor|ffix|btract(?:ing|InPlace|WithOverflow)?)|quareRoot|amePosition)|h(?:oldsUnique(?:Reference|OrPinnedReference)|as(?:Suffix|Prefix))|ne(?:gate(?:d)?|xt)|c(?:o(?:untByEnumerating|py(?:Bytes)?)|lamp(?:ed)?|reate)|t(?:o(?:IntMax|Opaque|UIntMax)|ake(?:RetainedValue|UnretainedValue)|r(?:uncatingRemainder|a(?:nscodedLength|ilSurrogate)))|i(?:s(?:MutableAndUniquelyReferenced(?:OrPinned)?|S(?:trictSu(?:perset(?:Of)?|bset(?:Of)?)|u(?:perset(?:Of)?|bset(?:Of)?))|Continuation|T(?:otallyOrdered|railSurrogate)|Disjoint(?:With)?|Unique(?:Reference|lyReferenced(?:OrPinned)?)|Equal|Le(?:ss(?:ThanOrEqualTo)?|adSurrogate))|n(?:sert(?:ContentsOf)?|tersect(?:ion|InPlace)?|itialize(?:Memory|From)?|dex(?:Of|ForKey)))|o(?:verlaps|bjectAt)|d(?:i(?:stance(?:To)?|vide(?:d|WithOverflow)?)|e(?:s(?:cendant|troy)|code(?:CString)?|initialize|alloc(?:ate(?:Capacity)?)?)|rop(?:First|Last))|u(?:n(?:ion(?:InPlace)?|derestimateCount|wrappedOrError)|p(?:date(?:Value)?|percased))|join(?:ed|WithSeparator)|p(?:op(?:First|Last)|ass(?:Retained|Unretained)|re(?:decessor|fix))|e(?:scape(?:d)?|n(?:code|umerate(?:d)?)|lementsEqual|xclusiveOr(?:InPlace)?)|f(?:orm(?:Remainder|S(?:ymmetricDifference|quareRoot)|TruncatingRemainder|In(?:tersection|dex)|Union)|latten|rom(?:CString(?:RepairingIllFormedUTF8)?|Opaque))|w(?:i(?:thMemoryRebound|dth)|rite(?:To)?)|l(?:o(?:wercased|ad)|e(?:adSurrogate|xicographical(?:Compare|lyPrecedes)))|a(?:ss(?:ign(?:BackwardFrom|From)?|umingMemoryBound)|d(?:d(?:ing(?:Product)?|Product|WithOverflow)?|vanced(?:By)?)|utorelease|ppend(?:ContentsOf)?|lloc(?:ate)?|bs)|r(?:ound(?:ed)?|e(?:serveCapacity|tain|duce|place(?:Range|Subrange)?|verse(?:d)?|quest(?:NativeBuffer|UniqueMutableBackingBuffer)|lease|m(?:ove(?:Range|Subrange|Value(?:ForKey)?|First|Last|A(?:tIndex|ll))?|ainder(?:WithOverflow)?)))|ge(?:nerate|t(?:Objects|Element))|m(?:in(?:imum(?:Magnitude)?|Element)|ove(?:Initialize(?:Memory|BackwardFrom|From)?|Assign(?:From)?)?|ultipl(?:y(?:WithOverflow)?|ied)|easure|a(?:ke(?:Iterator|Description)|x(?:imum(?:Magnitude)?|Element)))|bindMemory)(?=\\s*\\()" + }, + + { + "comment": "Member functions in the standard library in Swift 2 only", + "name": "support.function.swift", + "match": "(?<=\\.)(?:s(?:uperclassMirror|amePositionIn|tartsWith)|nextObject|c(?:haracterAtIndex|o(?:untByEnumeratingWithState|pyWithZone)|ustom(?:Mirror|PlaygroundQuickLook))|is(?:EmptyInput|ASCII)|object(?:Enumerator|ForKey|AtIndex)|join|put|keyEnumerator|withUnsafeMutablePointerToValue|length|getMirror|m(?:oveInitializeAssignFrom|ember))(?=\\s*\\()" + } + ] + }, + + "builtin-global-functions": { + "patterns": [ + { + "begin": "\\b(type)(\\()\\s*(of)(:)", + "end": "\\)", + "beginCaptures": { + "1": { "name": "support.function.dynamic-type.swift" }, + "2": { "name": "punctuation.definition.arguments.begin.swift" }, + "3": { "name": "support.variable.parameter.swift" }, + "4": { "name": "punctuation.separator.argument-label.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.arguments.end.swift" } + }, + "patterns": [{ "include": "#expressions" }] + }, + + { + "comment": "Global functions available in Swift 3 which may be used with trailing closures and no parentheses", + "name": "support.function.swift", + "match": "\\b(?:anyGenerator|autoreleasepool)(?=\\s*[({])\\b" + }, + + { + "comment": "Global functions available in Swift 3", + "name": "support.function.swift", + "match": "\\b(?:s(?:tride(?:of(?:Value)?)?|izeof(?:Value)?|equence|wap)|numericCast|transcode|is(?:UniquelyReferenced(?:NonObjC)?|KnownUniquelyReferenced)|zip|d(?:ump|ebugPrint)|unsafe(?:BitCast|Downcast|Unwrap|Address(?:Of)?)|pr(?:int|econdition(?:Failure)?)|fatalError|with(?:Unsafe(?:MutablePointer|Pointer)|ExtendedLifetime|VaList)|a(?:ssert(?:ionFailure)?|lignof(?:Value)?|bs)|re(?:peatElement|adLine)|getVaList|m(?:in|ax))(?=\\s*\\()" + }, + + { + "comment": "Global functions available in Swift 2 only", + "name": "support.function.swift", + "match": "\\b(?:s(?:ort|uffix|pli(?:ce|t))|insert|overlaps|d(?:istance|rop(?:First|Last))|join|prefix|extend|withUnsafe(?:MutablePointers|Pointers)|lazy|advance|re(?:flect|move(?:Range|Last|A(?:tIndex|ll))))(?=\\s*\\()" + } + ] + }, + + "builtin-properties": { + "patterns": [ + { + "comment": "The simpler (?<=\\bProcess\\.|\\bCommandLine\\.) breaks VS Code / Atom, see https://github.com/textmate/swift.tmbundle/issues/29", + "name": "support.variable.swift", + "match": "(?<=^Process\\.|\\WProcess\\.|^CommandLine\\.|\\WCommandLine\\.)(arguments|argc|unsafeArgv)" + }, + + { + "comment": "Properties in the standard library in Swift 3", + "name": "support.variable.swift", + "match": "(?<=\\.)(?:s(?:t(?:artIndex|ri(?:ngValue|de))|i(?:ze|gn(?:BitIndex|ificand(?:Bit(?:Count|Pattern)|Width)?|alingNaN)?)|u(?:perclassMirror|mmary|bscriptBaseAddress))|h(?:eader|as(?:hValue|PointerRepresentation))|n(?:ulTerminatedUTF8|ext(?:Down|Up)|a(?:n|tiveOwner))|c(?:haracters|ount(?:TrailingZeros)?|ustom(?:Mirror|PlaygroundQuickLook)|apacity)|i(?:s(?:S(?:ign(?:Minus|aling(?:NaN)?)|ubnormal)|N(?:ormal|aN)|Canonical|Infinite|Zero|Empty|Finite|ASCII)|n(?:dices|finity)|dentity)|owner|de(?:scription|bugDescription)|u(?:n(?:safelyUnwrapped|icodeScalar(?:s)?|derestimatedCount)|tf(?:16|8(?:Start|C(?:String|odeUnitCount))?)|intValue|ppercaseString|lp(?:OfOne)?)|p(?:i|ointee)|e(?:ndIndex|lements|xponent(?:Bit(?:Count|Pattern))?)|value(?:s)?|keys|quietNaN|f(?:irst(?:ElementAddress(?:IfContiguous)?)?|loatingPointClass)|l(?:ittleEndian|owercaseString|eastNo(?:nzeroMagnitude|rmalMagnitude)|a(?:st|zy))|a(?:l(?:ignment|l(?:ocatedElementCount|Zeros))|rray(?:PropertyIsNativeTypeChecked)?)|ra(?:dix|wValue)|greatestFiniteMagnitude|m(?:in|emory|ax)|b(?:yteS(?:ize|wapped)|i(?:nade|tPattern|gEndian)|uffer|ase(?:Address)?))\\b" + }, + + { + "comment": "Properties in the standard library in Swift 2 only", + "name": "support.variable.swift", + "match": "(?<=\\.)(?:boolValue|disposition|end|objectIdentifier|quickLookObject|start|valueType)\\b" + }, + + { + "comment": "Enum cases in the standard library - note that there is some overlap between these and the properties", + "name": "support.variable.swift", + "match": "(?<=\\.)(?:s(?:calarValue|i(?:ze|gnalingNaN)|o(?:und|me)|uppressed|prite|et)|n(?:one|egative(?:Subnormal|Normal|Infinity|Zero))|c(?:ol(?:or|lection)|ustomized)|t(?:o(?:NearestOr(?:Even|AwayFromZero)|wardZero)|uple|ext)|i(?:nt|mage)|optional|d(?:ictionary|o(?:uble|wn))|u(?:Int|p|rl)|p(?:o(?:sitive(?:Subnormal|Normal|Infinity|Zero)|int)|lus)|e(?:rror|mptyInput)|view|quietNaN|float|a(?:ttributedString|wayFromZero)|r(?:ectangle|ange)|generated|minus|b(?:ool|ezierPath))\\b" + } + ] + }, + + "builtin-types": { + "comment": "Types provided in the standard library", + "patterns": [ + { "include": "#builtin-types-builtin-class-type" }, + { "include": "#builtin-types-builtin-enum-type" }, + { "include": "#builtin-types-builtin-protocol-type" }, + { "include": "#builtin-types-builtin-struct-type" }, + { "include": "#builtin-types-builtin-typealias" }, + + { + "name": "support.type.any.swift", + "match": "\\bAny\\b" + } + ] + }, + + "builtin-types-builtin-class-type": { + "comment": "Builtin class types", + "name": "support.class.swift", + "match": "\\b(Managed(Buffer|ProtoBuffer)|NonObjectiveCBase|AnyGenerator)\\b" + }, + + "builtin-types-builtin-enum-type": { + "patterns": [ + { + "comment": "CommandLine is an enum, but it acts like a constant", + "name": "support.constant.swift", + "match": "\\b(?:CommandLine|Process(?=\\.))\\b" + }, + + { + "comment": "The return type of a function that never returns", + "name": "support.constant.never.swift", + "match": "\\bNever\\b" + }, + + { + "comment": "Enum types in the standard library in Swift 3", + "name": "support.type.swift", + "match": "\\b(?:ImplicitlyUnwrappedOptional|Representation|MemoryLayout|FloatingPointClassification|SetIndexRepresentation|SetIteratorRepresentation|FloatingPointRoundingRule|UnicodeDecodingResult|Optional|DictionaryIndexRepresentation|AncestorRepresentation|DisplayStyle|PlaygroundQuickLook|Never|FloatingPointSign|Bit|DictionaryIteratorRepresentation)\\b" + }, + + { + "comment": "Enum types in the standard library in Swift 2 only", + "name": "support.type.swift", + "match": "\\b(?:MirrorDisposition|QuickLookObject)\\b" + } + ] + }, + + "builtin-types-builtin-protocol-type": { + "patterns": [ + { + "name": "support.type.swift", + "match": "\\bActor\\b" + }, + + { + "comment": "Protocols in the standard library in Swift 3", + "name": "support.type.swift", + "match": "\\b(?:Ra(?:n(?:domAccess(?:Collection|Indexable)|geReplaceable(?:Collection|Indexable))|wRepresentable)|M(?:irrorPath|utable(?:Collection|Indexable))|Bi(?:naryFloatingPoint|twiseOperations|directional(?:Collection|Indexable))|S(?:tr(?:ideable|eamable)|igned(?:Number|Integer)|e(?:tAlgebra|quence))|Hashable|C(?:o(?:llection|mparable)|ustom(?:Reflectable|StringConvertible|DebugStringConvertible|PlaygroundQuickLookable|LeafReflectable)|VarArg)|TextOutputStream|I(?:n(?:teger(?:Arithmetic)?|dexable(?:Base)?)|teratorProtocol)|OptionSet|Un(?:signedInteger|icodeCodec)|E(?:quatable|rror|xpressibleBy(?:BooleanLiteral|String(?:Interpolation|Literal)|NilLiteral|IntegerLiteral|DictionaryLiteral|UnicodeScalarLiteral|ExtendedGraphemeClusterLiteral|FloatLiteral|ArrayLiteral))|FloatingPoint|L(?:osslessStringConvertible|azy(?:SequenceProtocol|CollectionProtocol))|A(?:nyObject|bsoluteValuable))\\b" + }, + + { + "comment": "Protocols in the standard library in Swift 2 only", + "name": "support.type.swift", + "match": "\\b(?:Ran(?:domAccessIndexType|geReplaceableCollectionType)|GeneratorType|M(?:irror(?:Type|PathType)|utable(?:Sliceable|CollectionType))|B(?:i(?:twiseOperationsType|directionalIndexType)|oolean(?:Type|LiteralConvertible))|S(?:tring(?:InterpolationConvertible|LiteralConvertible)|i(?:nkType|gned(?:NumberType|IntegerType))|e(?:tAlgebraType|quenceType)|liceable)|NilLiteralConvertible|C(?:ollectionType|VarArgType)|Inte(?:rvalType|ger(?:Type|LiteralConvertible|ArithmeticType))|O(?:utputStreamType|ptionSetType)|DictionaryLiteralConvertible|Un(?:signedIntegerType|icode(?:ScalarLiteralConvertible|CodecType))|E(?:rrorType|xten(?:sibleCollectionType|dedGraphemeClusterLiteralConvertible))|F(?:orwardIndexType|loat(?:ingPointType|LiteralConvertible))|A(?:nyCollectionType|rrayLiteralConvertible))\\b" + } + ] + }, + + "builtin-types-builtin-struct-type": { + "patterns": [ + { + "comment": "Structs in the standard library in Swift 3", + "name": "support.type.swift", + "match": "\\b(?:R(?:e(?:peat(?:ed)?|versed(?:RandomAccess(?:Collection|Index)|Collection|Index))|an(?:domAccessSlice|ge(?:Replaceable(?:RandomAccessSlice|BidirectionalSlice|Slice)|Generator)?))|Generator(?:Sequence|OfOne)|M(?:irror|utable(?:Ran(?:domAccessSlice|geReplaceable(?:RandomAccessSlice|BidirectionalSlice|Slice))|BidirectionalSlice|Slice)|anagedBufferPointer)|B(?:idirectionalSlice|ool)|S(?:t(?:aticString|ri(?:ng|deT(?:hrough(?:Generator|Iterator)?|o(?:Generator|Iterator)?)))|et(?:I(?:ndex|terator))?|lice)|HalfOpenInterval|C(?:haracter(?:View)?|o(?:ntiguousArray|untable(?:Range|ClosedRange)|llectionOfOne)|OpaquePointer|losed(?:Range(?:I(?:ndex|terator))?|Interval)|VaListPointer)|I(?:n(?:t(?:16|8|32|64)?|d(?:ices|ex(?:ing(?:Generator|Iterator))?))|terator(?:Sequence|OverOne)?)|Zip2(?:Sequence|Iterator)|O(?:paquePointer|bjectIdentifier)|D(?:ictionary(?:I(?:ndex|terator)|Literal)?|ouble|efault(?:RandomAccessIndices|BidirectionalIndices|Indices))|U(?:n(?:safe(?:RawPointer|Mutable(?:RawPointer|BufferPointer|Pointer)|BufferPointer(?:Generator|Iterator)?|Pointer)|icodeScalar(?:View)?|foldSequence|managed)|TF(?:16(?:View)?|8(?:View)?|32)|Int(?:16|8|32|64)?)|Join(?:Generator|ed(?:Sequence|Iterator))|PermutationGenerator|E(?:numerate(?:Generator|Sequence|d(?:Sequence|Iterator))|mpty(?:Generator|Collection|Iterator))|Fl(?:oat(?:80)?|atten(?:Generator|BidirectionalCollection(?:Index)?|Sequence|Collection(?:Index)?|Iterator))|L(?:egacyChildren|azy(?:RandomAccessCollection|Map(?:RandomAccessCollection|Generator|BidirectionalCollection|Sequence|Collection|Iterator)|BidirectionalCollection|Sequence|Collection|Filter(?:Generator|BidirectionalCollection|Sequence|Collection|I(?:ndex|terator))))|A(?:ny(?:RandomAccessCollection|Generator|BidirectionalCollection|Sequence|Hashable|Collection|I(?:ndex|terator))|utoreleasingUnsafeMutablePointer|rray(?:Slice)?))\\b" + }, + + { + "comment": "Structs in the standard library in Swift 2 only", + "name": "support.type.swift", + "match": "\\b(?:R(?:everse(?:RandomAccess(?:Collection|Index)|Collection|Index)|awByte)|Map(?:Generator|Sequence|Collection)|S(?:inkOf|etGenerator)|Zip2Generator|DictionaryGenerator|Filter(?:Generator|Sequence|Collection(?:Index)?)|LazyForwardCollection|Any(?:RandomAccessIndex|BidirectionalIndex|Forward(?:Collection|Index)))\\b" + } + ] + }, + + "builtin-types-builtin-typealias": { + "patterns": [ + { + "comment": "Typealiases in the standard library in Swift 3", + "name": "support.type.swift", + "match": "\\b(?:Raw(?:Significand|Exponent|Value)|B(?:ooleanLiteralType|uffer|ase)|S(?:t(?:orage|r(?:i(?:ngLiteralType|de)|eam(?:1|2)))|ubSequence)|NativeBuffer|C(?:hild(?:ren)?|Bool|S(?:hort|ignedChar)|odeUnit|Char(?:16|32)?|Int|Double|Unsigned(?:Short|Char|Int|Long(?:Long)?)|Float|WideChar|Long(?:Long)?)|I(?:n(?:t(?:Max|egerLiteralType)|d(?:ices|ex(?:Distance)?))|terator)|Distance|U(?:n(?:icodeScalar(?:Type|Index|View|LiteralType)|foldFirstSequence)|TF(?:16(?:Index|View)|8Index)|IntMax)|E(?:lement(?:s)?|x(?:tendedGraphemeCluster(?:Type|LiteralType)|ponent))|V(?:oid|alue)|Key|Float(?:32|LiteralType|64)|AnyClass)\\b" + }, + + { + "comment": "Typealiases in the standard library in Swift 2 only", + "name": "support.type.swift", + "match": "\\b(?:Generator|PlaygroundQuickLook|UWord|Word)\\b" + } + ] + }, + + "code-block": { + "begin": "\\{", + "end": "\\}", + "beginCaptures": { + "0": { "name": "punctuation.section.scope.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.section.scope.end.swift" } + }, + "patterns": [{ "include": "$self" }] + }, + + "comments": { + "patterns": [ + { + "name": "comment.line.number-sign.swift", + "match": "\\A^(#!).*$\\n?", + "captures": { + "1": { "name": "punctuation.definition.comment.swift" } + } + }, + + { + "name": "comment.block.documentation.swift", + "begin": "/\\*\\*(?!/)", + "end": "\\*/", + "beginCaptures": { + "0": { "name": "punctuation.definition.comment.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.comment.end.swift" } + }, + "patterns": [{ "include": "#comments-nested" }] + }, + + { + "name": "comment.block.documentation.playground.swift", + "begin": "/\\*:", + "end": "\\*/", + "beginCaptures": { + "0": { "name": "punctuation.definition.comment.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.comment.end.swift" } + }, + "patterns": [{ "include": "#comments-nested" }] + }, + + { + "name": "comment.block.swift", + "begin": "/\\*", + "end": "\\*/", + "beginCaptures": { + "0": { "name": "punctuation.definition.comment.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.comment.end.swift" } + }, + "patterns": [{ "include": "#comments-nested" }] + }, + + { + "name": "invalid.illegal.unexpected-end-of-block-comment.swift", + "match": "\\*/" + }, + + { + "begin": "(^[ \\t]+)?(?=//)", + "end": "(?!\\G)", + "beginCaptures": { + "1": { "name": "punctuation.whitespace.comment.leading.swift" } + }, + "patterns": [ + { + "name": "comment.line.triple-slash.documentation.swift", + "begin": "///", + "end": "$", + "beginCaptures": { + "0": { "name": "punctuation.definition.comment.swift" } + } + }, + + { + "name": "comment.line.double-slash.documentation.swift", + "begin": "//:", + "end": "$", + "beginCaptures": { + "0": { "name": "punctuation.definition.comment.swift" } + } + }, + + { + "name": "comment.line.double-slash.swift", + "begin": "//", + "end": "$", + "beginCaptures": { + "0": { "name": "punctuation.definition.comment.swift" } + } + } + ] + } + ] + }, + + "comments-nested": { + "begin": "/\\*", + "end": "\\*/", + "patterns": [{ "include": "#comments-nested" }] + }, + + "compiler-control": { + "patterns": [ + { + "contentName": "comment.block.preprocessor.swift", + "begin": "^\\s*(#)(if|elseif)\\s+(false)\\b.*?(?=$|//|/\\*)", + "end": "(?=^\\s*(#(elseif|else|endif)\\b))", + "beginCaptures": { + "0": { "name": "meta.preprocessor.conditional.swift" }, + "1": { "name": "punctuation.definition.preprocessor.swift" }, + "2": { "name": "keyword.control.import.preprocessor.conditional.swift" }, + "3": { "name": "constant.language.boolean.swift" } + } + }, + + { + "name": "meta.preprocessor.conditional.swift", + "captures": { + "1": { "name": "punctuation.definition.preprocessor.swift" }, + "2": { "name": "keyword.control.import.preprocessor.conditional.swift" } + }, + "begin": "^\\s*(#)(if|elseif)\\s+", + "end": "(?=\\s*(?://|/\\*))|$", + "patterns": [ + { + "name": "keyword.operator.logical.swift", + "match": "(&&|\\|\\|)" + }, + + { + "name": "constant.language.boolean.swift", + "match": "\\b(true|false)\\b" + }, + + { + "match": "\\b(arch)\\s*(\\()\\s*(?:(arm|arm64|powerpc64|powerpc64le|i386|x86_64|s390x)|\\w+)\\s*(\\))", + "captures": { + "1": { "name": "keyword.other.condition.swift" }, + "2": { "name": "punctuation.definition.parameters.begin.swift" }, + "3": { "name": "support.constant.platform.architecture.swift" }, + "4": { "name": "punctuation.definition.parameters.end.swift" } + } + }, + + { + "match": "\\b(os)\\s*(\\()\\s*(?:(macOS|OSX|iOS|tvOS|watchOS|visionOS|Android|Linux|FreeBSD|Windows|PS4)|\\w+)\\s*(\\))", + "captures": { + "1": { "name": "keyword.other.condition.swift" }, + "2": { "name": "punctuation.definition.parameters.begin.swift" }, + "3": { "name": "support.constant.platform.os.swift" }, + "4": { "name": "punctuation.definition.parameters.end.swift" } + } + }, + + { + "match": "\\b(canImport)\\s*(\\()([\\p{L}_][\\p{L}_\\p{N}\\p{M}]*)(\\))", + "captures": { + "1": { "name": "keyword.other.condition.swift" }, + "2": { "name": "punctuation.definition.parameters.begin.swift" }, + "3": { "name": "entity.name.type.module.swift" }, + "4": { "name": "punctuation.definition.parameters.end.swift" } + } + }, + + { + "begin": "\\b(targetEnvironment)\\s*(\\()", + "end": "(\\))|$", + "beginCaptures": { + "1": { "name": "keyword.other.condition.swift" }, + "2": { "name": "punctuation.definition.parameters.begin.swift" } + }, + "endCaptures": { + "1": { "name": "punctuation.definition.parameters.end.swift" } + }, + "patterns": [ + { + "name": "support.constant.platform.environment.swift", + "match": "\\b(simulator|UIKitForMac)\\b" + } + ] + }, + + { + "begin": "\\b(swift|compiler)\\s*(\\()", + "end": "(\\))|$", + "beginCaptures": { + "1": { "name": "keyword.other.condition.swift" }, + "2": { "name": "punctuation.definition.parameters.begin.swift" } + }, + "endCaptures": { + "1": { "name": "punctuation.definition.parameters.end.swift" } + }, + "patterns": [ + { + "name": "keyword.operator.comparison.swift", + "match": ">=|<" + }, + + { + "name": "constant.numeric.swift", + "match": "\\b[0-9]+(?:\\.[0-9]+)*\\b" + } + ] + } + ] + }, + + { + "name": "meta.preprocessor.conditional.swift", + "match": "^\\s*(#)(else|endif)(.*?)(?=$|//|/\\*)", + "captures": { + "1": { "name": "punctuation.definition.preprocessor.swift" }, + "2": { "name": "keyword.control.import.preprocessor.conditional.swift" }, + "3": { + "patterns": [ + { + "name": "invalid.illegal.character-not-allowed-here.swift", + "match": "\\S+" + } + ] + } + } + }, + + { + "name": "meta.preprocessor.sourcelocation.swift", + "match": "^\\s*(#)(sourceLocation)((\\()([^)]*)(\\)))(.*?)(?=$|//|/\\*)", + "captures": { + "1": { "name": "punctuation.definition.preprocessor.swift" }, + "2": { "name": "keyword.control.import.preprocessor.sourcelocation.swift" }, + "4": { "name": "punctuation.definition.parameters.begin.swift" }, + "5": { + "patterns": [ + { + "begin": "(file)\\s*(:)\\s*(?=\")", + "end": "(?!\\G)", + "beginCaptures": { + "1": { "name": "support.variable.parameter.swift" }, + "2": { "name": "punctuation.separator.key-value.swift" } + }, + "patterns": [{ "include": "#literals" }] + }, + + { + "match": "(line)\\s*(:)\\s*([0-9]+)", + "captures": { + "1": { "name": "support.variable.parameter.swift" }, + "2": { "name": "punctuation.separator.key-value.swift" }, + "3": { "name": "constant.numeric.integer.swift" } + } + }, + + { + "name": "punctuation.separator.parameters.swift", + "match": "," + }, + + { + "name": "invalid.illegal.character-not-allowed-here.swift", + "match": "\\S+" + } + ] + }, + "6": { "name": "punctuation.definition.parameters.begin.swift" }, + "7": { + "patterns": [ + { + "name": "invalid.illegal.character-not-allowed-here.swift", + "match": "\\S+" + } + ] + } + } + } + ] + }, + + "conditionals": { + "patterns": [ + { + "begin": "(?&|\\^~.])(->)(?![/=\\-+!*%<>&|\\^~.])", + "captures": { + "1": { "name": "keyword.operator.type.function.swift" } + } + }, + + { + "comment": "Swift 3: A & B", + "match": "(?&|\\^~.])(&)(?![/=\\-+!*%<>&|\\^~.])", + "captures": { + "1": { "name": "keyword.operator.type.composition.swift" } + } + }, + + { + "name": "keyword.operator.type.optional.swift", + "match": "[?!]" + }, + + { + "name": "keyword.operator.function.variadic-parameter.swift", + "match": "\\.\\.\\." + }, + + { + "comment": "Swift 2: protocol", + "name": "keyword.other.type.composition.swift", + "match": "\\bprotocol\\b" + }, + + { + "name": "keyword.other.type.metatype.swift", + "match": "(?<=\\.)(?:Protocol|Type)\\b" + }, + + { "include": "#declarations-available-types-tuple-type" }, + { "include": "#declarations-available-types-collection-type" }, + { "include": "#declarations-generic-argument-clause" } + ] + }, + + "declarations-available-types-collection-type": { + "comment": "array and dictionary types [Value] and [Key: Value]", + "begin": "\\[", + "end": "\\]|(?=[>){}])", + "beginCaptures": { + "0": { "name": "punctuation.section.collection-type.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.section.collection-type.end.swift" } + }, + "patterns": [ + { "include": "#declarations-available-types" }, + { "include": "#literals-numeric" }, + + { + "name": "support.variable.inferred.swift", + "match": "\\b_\\b" + }, + + { + "name": "keyword.other.inline-array.swift", + "match": "(?<=\\s)\\bof\\b(?=\\s+[\\p{L}_\\d\\p{N}\\p{M}\\[(])" + }, + + { + "begin": ":", + "end": "(?=\\]|[>){}])", + "beginCaptures": { + "0": { "name": "punctuation.separator.key-value.swift" } + }, + "patterns": [ + { + "name": "invalid.illegal.extra-colon-in-dictionary-type.swift", + "match": ":" + }, + { "include": "#declarations-available-types" } + ] + } + ] + }, + + "declarations-available-types-tuple-type": { + "begin": "\\(", + "end": "\\)|(?=[>\\]{}])", + "beginCaptures": { + "0": { "name": "punctuation.section.tuple-type.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.section.tuple-type.end.swift" } + }, + "patterns": [{ "include": "#declarations-available-types" }] + }, + + "declarations-extension": { + "name": "meta.definition.type.$1.swift", + "begin": "\\b(extension)\\s+", + "end": "(?<=\\})", + "beginCaptures": { + "1": { "name": "storage.type.$1.swift" } + }, + "patterns": [ + { + "name": "entity.name.type.swift", + "begin": "\\G(?!\\s*[:{\\n])", + "end": "(?=\\s*[:{\\n])|(?!\\G)(?=\\s*where\\b)", + "patterns": [{ "include": "#declarations-available-types" }] + }, + { "include": "#comments" }, + { + "comment": "SE-0143: Conditional Conformances", + "include": "#declarations-generic-where-clause" + }, + { "include": "#declarations-inheritance-clause" }, + + { + "name": "meta.definition.type.body.swift", + "begin": "\\{", + "end": "\\}", + "beginCaptures": { + "0": { "name": "punctuation.definition.type.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.type.end.swift" } + }, + "patterns": [{ "include": "$self" }] + } + ] + }, + + "declarations-function": { + "name": "meta.definition.function.swift", + "begin": "(?x)\n\\b\n(func)\n\\s+\n(\n (?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k)\n | (?:\n (\n (? # operator-head\n [/=\\-+!*%<>&|^~?]\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n )\n (\n \\g\n | (? # operator-character\n [\\x{0300}-\\x{036F}]\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n )*\n )\n | ( \\. ( \\g | \\g | \\. )+ ) # Dot operators\n )\n)\n\\s*\n(?=\\(|<)", + "end": "(?<=\\})|$(?# functions in protocol declarations or generated interfaces have no body)", + "beginCaptures": { + "1": { "name": "storage.type.function.swift" }, + "2": { "name": "entity.name.function.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-generic-parameter-clause" }, + { "include": "#declarations-parameter-clause" }, + { "include": "#declarations-function-result" }, + { "include": "#async-throws" }, + { + "comment": "Swift 3: generic constraints after the parameters and return type", + "include": "#declarations-generic-where-clause" + }, + + { + "name": "meta.definition.function.body.swift", + "begin": "(\\{)", + "end": "(\\})", + "beginCaptures": { + "1": { "name": "punctuation.section.function.begin.swift" } + }, + "endCaptures": { + "1": { "name": "punctuation.section.function.end.swift" } + }, + "patterns": [{ "include": "$self" }] + } + ] + }, + + "declarations-function-initializer": { + "name": "meta.definition.function.initializer.swift", + "begin": "(?&|\\^~.])(->)(?![/=\\-+!*%<>&|\\^~.])\\s*", + "end": "(?!\\G)(?=\\{|\\bwhere\\b|;|=)|$", + "beginCaptures": { + "1": { "name": "keyword.operator.function-result.swift" } + }, + "patterns": [ + { + "name": "storage.modifier.swift", + "match": "\\bsending\\b" + }, + { "include": "#declarations-available-types" } + ] + }, + + "declarations-function-subscript": { + "name": "meta.definition.function.subscript.swift", + "begin": "(?|(?=[)\\]{}])", + "beginCaptures": { + "0": { "name": "punctuation.separator.generic-argument-clause.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.separator.generic-argument-clause.end.swift" } + }, + "patterns": [ + { "include": "#literals-numeric" }, + { "include": "#declarations-available-types" } + ] + }, + + "declarations-generic-parameter-clause": { + "name": "meta.generic-parameter-clause.swift", + "begin": "<", + "end": ">|(?=[^\\w\\d:<>\\s,=&`])(?# characters besides these are never valid in a generic param list -- even if it's not really a valid clause, we should stop trying to parse it if we see one of them.)", + "beginCaptures": { + "0": { "name": "punctuation.separator.generic-parameter-clause.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.separator.generic-parameter-clause.end.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { + "comment": "Swift 2: constraints inside the generic param list", + "include": "#declarations-generic-where-clause" + }, + + { + "name": "keyword.other.declaration-specifier.swift", + "match": "\\blet\\b" + }, + + { + "name": "keyword.control.loop.swift", + "match": "\\beach\\b" + }, + + { + "match": "\\b((?!\\d)\\w[\\w\\d]*)\\b", + "captures": { + "1": { "name": "variable.language.generic-parameter.swift" } + } + }, + + { + "name": "punctuation.separator.generic-parameters.swift", + "match": "," + }, + + { + "name": "meta.generic-parameter-constraint.swift", + "begin": "(:)\\s*", + "end": "(?=[,>]|(?!\\G)\\bwhere\\b)", + "beginCaptures": { + "1": { "name": "punctuation.separator.generic-parameter-constraint.swift" } + }, + "patterns": [ + { + "name": "entity.other.inherited-class.swift", + "begin": "\\G", + "end": "(?=[,>]|(?!\\G)\\bwhere\\b)", + "patterns": [ + { "include": "#declarations-type-identifier" }, + { "include": "#declarations-type-operators" } + ] + } + ] + } + ] + }, + + "declarations-generic-where-clause": { + "name": "meta.generic-where-clause.swift", + "begin": "\\b(where)\\b\\s*", + "end": "(?!\\G)$|(?=[>{};\\n]|//|/\\*)", + "beginCaptures": { + "1": { "name": "keyword.other.generic-constraint-introducer.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-generic-where-clause-requirement-list" } + ] + }, + + "declarations-generic-where-clause-requirement-list": { + "begin": "\\G|,\\s*", + "end": "(?=[,>{};\\n]|//|/\\*)", + "patterns": [ + { "include": "#comments" }, + { "include": "#constraint" }, + { "include": "#declarations-available-types" }, + + { + "name": "meta.generic-where-clause.same-type-requirement.swift", + "begin": "(?&|\\^~.])(==)(?![/=\\-+!*%<>&|\\^~.])", + "end": "(?=\\s*[,>{};\\n]|//|/\\*)", + "beginCaptures": { + "1": { "name": "keyword.operator.generic-constraint.same-type.swift" } + }, + "patterns": [{ "include": "#declarations-available-types" }] + }, + + { + "name": "meta.generic-where-clause.conformance-requirement.swift", + "begin": "(?&|\\^~.])(:)(?![/=\\-+!*%<>&|\\^~.])", + "end": "(?=\\s*[,>{};\\n]|//|/\\*)", + "beginCaptures": { + "1": { "name": "keyword.operator.generic-constraint.conforms-to.swift" } + }, + "patterns": [ + { + "contentName": "entity.other.inherited-class.swift", + "begin": "\\G\\s*", + "end": "(?=\\s*[,>{};\\n]|//|/\\*)", + "patterns": [{ "include": "#declarations-available-types" }] + } + ] + } + ] + }, + + "declarations-import": { + "name": "meta.import.swift", + "begin": "(?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k)", + "captures": { + "1": { "name": "punctuation.definition.identifier.swift" }, + "2": { "name": "punctuation.definition.identifier.swift" } + } + }, + + { + "name": "entity.name.type.swift", + "match": "(?x)\n(?<=\\G|\\.)\n\\$[0-9]+" + }, + + { + "name": "entity.name.type.swift", + "match": "(?x)\n(?<=\\G|\\.)\n(?:\n (\n (? # operator-head\n [/=\\-+!*%<>&|^~?]\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n )\n (\n \\g\n | (? # operator-character\n [\\x{0300}-\\x{036F}]\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n )*\n )\n | ( \\. ( \\g | \\g | \\. )+ ) # Dot operators\n)\n(?=\\.|;|$|//|/\\*|\\s)", + "captures": { + "1": { + "patterns": [ + { + "name": "invalid.illegal.dot-not-allowed-here.swift", + "match": "\\." + } + ] + } + } + }, + + { + "name": "punctuation.separator.import.swift", + "match": "\\." + }, + + { + "name": "invalid.illegal.character-not-allowed-here.swift", + "begin": "(?!\\s*(;|$|//|/\\*))", + "end": "(?=\\s*(;|$|//|/\\*))" + } + ] + } + ] + }, + + "declarations-inheritance-clause": { + "name": "meta.inheritance-clause.swift", + "begin": "(:)(?=\\s*\\{)|(:)\\s*", + "end": "(?!\\G)$|(?=[={}]|(?!\\G)\\bwhere\\b)", + "beginCaptures": { + "1": { "name": "invalid.illegal.empty-inheritance-clause.swift" }, + "2": { "name": "punctuation.separator.inheritance-clause.swift" } + }, + "patterns": [ + { + "begin": "\\bclass\\b", + "end": "(?=[={}]|(?!\\G)\\bwhere\\b)", + "beginCaptures": { + "0": { "name": "storage.type.class.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-inheritance-clause-more-types" } + ] + }, + + { + "begin": "\\G", + "end": "(?!\\G)$|(?=[={}]|(?!\\G)\\bwhere\\b)", + "patterns": [ + { "include": "#attributes" }, + { "include": "#comments" }, + { "include": "#declarations-inheritance-clause-inherited-type" }, + { "include": "#declarations-inheritance-clause-more-types" }, + { "include": "#declarations-type-operators" } + ] + } + ] + }, + + "declarations-inheritance-clause-inherited-type": { + "name": "entity.other.inherited-class.swift", + "begin": "(?=[`\\p{L}_])", + "end": "(?!\\G)", + "patterns": [{ "include": "#declarations-type-identifier" }] + }, + + "declarations-inheritance-clause-more-types": { + "name": "meta.inheritance-list.more-types", + "begin": ",\\s*", + "end": "(?!\\G)(?!//|/\\*)|(?=[,={}]|(?!\\G)\\bwhere\\b)", + "patterns": [ + { "include": "#attributes" }, + { "include": "#comments" }, + { "include": "#declarations-inheritance-clause-inherited-type" }, + { "include": "#declarations-inheritance-clause-more-types" }, + { "include": "#declarations-type-operators" } + ] + }, + + "declarations-macro": { + "name": "meta.definition.macro.swift", + "begin": "(?x)\n\\b\n(macro)\n\\s+\n((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\n\\s*\n(?=\\(|<|=)", + "end": "$|(?=;|//|/\\*|\\}|=)", + "beginCaptures": { + "1": { "name": "storage.type.function.swift" }, + "2": { "name": "entity.name.function.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-generic-parameter-clause" }, + { "include": "#declarations-parameter-clause" }, + { "include": "#declarations-function-result" }, + { "include": "#async-throws" }, + { + "comment": "Swift 3: generic constraints after the parameters and return type", + "include": "#declarations-generic-where-clause" + } + ] + }, + + "declarations-operator": { + "name": "meta.definition.operator.swift", + "begin": "(?x)\n(?:\n \\b(prefix|infix|postfix)\n \\s+\n)?\n\\b\n(operator)\n\\s+\n(\n (\n (? # operator-head\n [/=\\-+!*%<>&|^~?]\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n )\n (\n \\g\n | \\. # Invalid dot\n | (? # operator-character\n [\\x{0300}-\\x{036F}]\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n )*+\n )\n | ( \\. ( \\g | \\g | \\. )++ ) # Dot operators\n)\n\\s*", + "end": "(;)|$\\n?|(?=//|/\\*)", + "beginCaptures": { + "1": { "name": "storage.modifier.swift" }, + "2": { "name": "storage.type.function.operator.swift" }, + "3": { "name": "entity.name.function.operator.swift" }, + "4": { + "comment": "workaround for https://github.com/microsoft/vscode-textmate/issues/140#issuecomment-1793610346", + "name": "entity.name.function.operator.swift", + "patterns": [ + { + "name": "invalid.illegal.dot-not-allowed-here.swift", + "match": "\\." + } + ] + } + }, + "endCaptures": { + "1": { "name": "punctuation.terminator.statement.swift" } + }, + "patterns": [ + { "include": "#declarations-operator-swift2" }, + { "include": "#declarations-operator-swift3" }, + + { + "name": "invalid.illegal.character-not-allowed-here.swift", + "match": "((?!$|;|//|/\\*)\\S)+" + } + ] + }, + + "declarations-operator-swift2": { + "begin": "\\G(\\{)", + "end": "(\\})", + "beginCaptures": { + "1": { "name": "punctuation.definition.operator.begin.swift" } + }, + "endCaptures": { + "1": { "name": "punctuation.definition.operator.end.swift" } + }, + "patterns": [ + { "include": "#comments" }, + + { + "match": "\\b(associativity)\\s+(left|right)\\b", + "captures": { + "1": { "name": "storage.modifier.swift" }, + "2": { "name": "keyword.other.operator.associativity.swift" } + } + }, + + { + "match": "\\b(precedence)\\s+([0-9]+)\\b", + "captures": { + "1": { "name": "storage.modifier.swift" }, + "2": { "name": "constant.numeric.integer.swift" } + } + }, + + { + "match": "\\b(assignment)\\b", + "captures": { + "1": { "name": "storage.modifier.swift" } + } + } + ] + }, + + "declarations-operator-swift3": { + "match": "\\G(:)\\s*((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))", + "captures": { + "2": { + "name": "entity.other.inherited-class.swift", + "patterns": [{ "include": "#declarations-types-precedencegroup" }] + }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + } + }, + + "declarations-parameter-clause": { + "name": "meta.parameter-clause.swift", + "begin": "(\\()", + "end": "(\\))(?:\\s*(async)\\b)?", + "beginCaptures": { + "1": { "name": "punctuation.definition.parameters.begin.swift" } + }, + "endCaptures": { + "1": { "name": "punctuation.definition.parameters.end.swift" }, + "2": { "name": "storage.modifier.async.swift" } + }, + "patterns": [{ "include": "#declarations-parameter-list" }] + }, + + "declarations-parameter-list": { + "patterns": [ + { + "comment": "External parameter labels are considered part of the function name", + "match": "((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s+((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))(?=\\s*:)", + "captures": { + "1": { "name": "entity.name.function.swift" }, + "2": { "name": "punctuation.definition.identifier.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "variable.parameter.function.swift" }, + "5": { "name": "punctuation.definition.identifier.swift" }, + "6": { "name": "punctuation.definition.identifier.swift" } + } + }, + + { + "comment": "If no external label is given, the name is both the external label and the internal variable name", + "match": "(((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k)))(?=\\s*:)", + "captures": { + "1": { "name": "variable.parameter.function.swift" }, + "2": { "name": "entity.name.function.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + } + }, + + { + "begin": ":\\s*(?!\\s)", + "end": "(?=[,)])", + "patterns": [ + { + "name": "storage.modifier.swift", + "match": "\\bsending\\b" + }, + { "include": "#declarations-available-types" }, + + { + "name": "invalid.illegal.extra-colon-in-parameter-list.swift", + "match": ":" + }, + + { + "comment": "a parameter's default value", + "begin": "=", + "end": "(?=[,)])", + "beginCaptures": { + "0": { "name": "keyword.operator.assignment.swift" } + }, + "patterns": [{ "include": "#expressions" }] + } + ] + } + ] + }, + + "declarations-precedencegroup": { + "name": "meta.definition.precedencegroup.swift", + "begin": "\\b(precedencegroup)\\s+((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s*(?=\\{)", + "end": "(?!\\G)", + "beginCaptures": { + "1": { "name": "storage.type.precedencegroup.swift" }, + "2": { "name": "entity.name.type.precedencegroup.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { + "begin": "\\{", + "end": "\\}", + "beginCaptures": { + "0": { "name": "punctuation.definition.precedencegroup.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.precedencegroup.end.swift" } + }, + "patterns": [ + { "include": "#comments" }, + + { + "match": "\\b(higherThan|lowerThan)\\s*:\\s*((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))", + "captures": { + "1": { "name": "storage.modifier.swift" }, + "2": { + "name": "entity.other.inherited-class.swift", + "patterns": [{ "include": "#declarations-types-precedencegroup" }] + }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + } + }, + + { + "match": "\\b(associativity)\\b(?:\\s*:\\s*(right|left|none)\\b)?", + "captures": { + "1": { "name": "storage.modifier.swift" }, + "2": { "name": "keyword.other.operator.associativity.swift" } + } + }, + + { + "match": "\\b(assignment)\\b(?:\\s*:\\s*(true|false)\\b)?", + "captures": { + "1": { "name": "storage.modifier.swift" }, + "2": { "name": "constant.language.boolean.swift" } + } + } + ] + } + ] + }, + + "declarations-protocol": { + "name": "meta.definition.type.protocol.swift", + "begin": "\\b(protocol)\\s+((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))", + "end": "(?<=\\})", + "beginCaptures": { + "1": { "name": "storage.type.$1.swift" }, + "2": { "name": "entity.name.type.$1.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-inheritance-clause" }, + { + "comment": "SE-0142: Permit where clauses to constrain associated types", + "include": "#declarations-generic-where-clause" + }, + + { + "name": "meta.definition.type.body.swift", + "begin": "\\{", + "end": "\\}", + "beginCaptures": { + "0": { "name": "punctuation.definition.type.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.type.end.swift" } + }, + "patterns": [ + { "include": "#declarations-protocol-protocol-method" }, + { "include": "#declarations-protocol-protocol-initializer" }, + { "include": "#declarations-protocol-associated-type" }, + { "include": "$self" } + ] + } + ] + }, + + "declarations-protocol-associated-type": { + "name": "meta.definition.associatedtype.swift", + "begin": "\\b(associatedtype)\\s+((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s*", + "end": "(?!\\G)$|(?=[;}]|$)", + "beginCaptures": { + "1": { "name": "keyword.other.declaration-specifier.swift" }, + "2": { "name": "variable.language.associatedtype.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { "include": "#declarations-inheritance-clause" }, + { + "comment": "SE-0142: Permit where clauses to constrain associated types", + "include": "#declarations-generic-where-clause" + }, + { "include": "#declarations-typealias-assignment" } + ] + }, + + "declarations-protocol-protocol-initializer": { + "name": "meta.definition.function.initializer.swift", + "begin": "(?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k)\n | (?:\n (\n (? # operator-head\n [/=\\-+!*%<>&|^~?]\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n )\n (\n \\g\n | (? # operator-character\n [\\x{0300}-\\x{036F}]\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n )*\n )\n | ( \\. ( \\g | \\g | \\. )+ ) # Dot operators\n )\n )\n\\s*\n(?=\\(|<)", + "end": "$|(?=;|//|/\\*|\\})", + "beginCaptures": { + "1": { "name": "storage.type.function.swift" }, + "2": { "name": "entity.name.function.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-generic-parameter-clause" }, + { "include": "#declarations-parameter-clause" }, + { "include": "#declarations-function-result" }, + { "include": "#async-throws" }, + { + "comment": "Swift 3: generic constraints after the parameters and return type", + "include": "#declarations-generic-where-clause" + }, + + { + "name": "invalid.illegal.function-body-not-allowed-in-protocol.swift", + "begin": "\\{", + "end": "\\}", + "beginCaptures": { + "0": { "name": "punctuation.section.function.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.section.function.end.swift" } + }, + "patterns": [{ "include": "$self" }] + } + ] + }, + + "declarations-type": { + "patterns": [ + { + "name": "meta.definition.type.$1.swift", + "begin": "\\b(class(?!\\s+(?:func|var|let)\\b)|struct|actor)\\b\\s*((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))", + "end": "(?<=\\})", + "beginCaptures": { + "1": { "name": "storage.type.$1.swift" }, + "2": { "name": "entity.name.type.$1.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-generic-parameter-clause" }, + { + "comment": "Swift 3: generic constraints after the generic param list", + "include": "#declarations-generic-where-clause" + }, + { "include": "#declarations-inheritance-clause" }, + + { + "name": "meta.definition.type.body.swift", + "begin": "\\{", + "end": "\\}", + "beginCaptures": { + "0": { "name": "punctuation.definition.type.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.type.end.swift" } + }, + "patterns": [{ "include": "$self" }] + } + ] + }, + { "include": "#declarations-type-enum" } + ] + }, + + "declarations-type-enum": { + "name": "meta.definition.type.$1.swift", + "begin": "\\b(enum)\\s+((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))", + "end": "(?<=\\})", + "beginCaptures": { + "1": { "name": "storage.type.$1.swift" }, + "2": { "name": "entity.name.type.$1.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-generic-parameter-clause" }, + { + "comment": "Swift 3: generic constraints after the generic param list", + "include": "#declarations-generic-where-clause" + }, + { "include": "#declarations-inheritance-clause" }, + + { + "name": "meta.definition.type.body.swift", + "begin": "\\{", + "end": "\\}", + "beginCaptures": { + "0": { "name": "punctuation.definition.type.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.type.end.swift" } + }, + "patterns": [ + { "include": "#declarations-type-enum-enum-case-clause" }, + { "include": "$self" } + ] + } + ] + }, + + "declarations-type-enum-associated-values": { + "begin": "\\G\\(", + "end": "\\)", + "beginCaptures": { + "0": { "name": "punctuation.definition.parameters.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.parameters.end.swift" } + }, + "patterns": [ + { "include": "#comments" }, + + { + "begin": "(?x)\n(?:(_)|((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*\\k))\n\\s+\n(((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*\\k))\n\\s*(:)", + "end": "(?=[,)\\]])", + "beginCaptures": { + "1": { "name": "entity.name.function.swift" }, + "2": { "name": "invalid.illegal.distinct-labels-not-allowed.swift" }, + "5": { "name": "variable.parameter.function.swift" }, + "7": { "name": "punctuation.separator.argument-label.swift" } + }, + "patterns": [{ "include": "#declarations-available-types" }] + }, + + { + "begin": "(((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*\\k))\\s*(:)", + "end": "(?=[,)\\]])", + "beginCaptures": { + "1": { "name": "entity.name.function.swift" }, + "2": { "name": "variable.parameter.function.swift" }, + "4": { "name": "punctuation.separator.argument-label.swift" } + }, + "patterns": [{ "include": "#declarations-available-types" }] + }, + + { + "comment": "an element without a label (i.e. anything else)", + "begin": "(?![,)\\]])(?=\\S)", + "end": "(?=[,)\\]])", + "patterns": [ + { "include": "#declarations-available-types" }, + + { + "name": "invalid.illegal.extra-colon-in-parameter-list.swift", + "match": ":" + } + ] + } + ] + }, + + "declarations-type-enum-enum-case": { + "begin": "(?x)((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s*", + "end": "(?<=\\))|(?![=(])", + "beginCaptures": { + "1": { "name": "variable.other.enummember.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-type-enum-associated-values" }, + { "include": "#declarations-type-enum-raw-value-assignment" } + ] + }, + + "declarations-type-enum-enum-case-clause": { + "begin": "\\b(case)\\b\\s*", + "end": "(?=[;}])|(?!\\G)(?!//|/\\*)(?=[^\\s,])", + "beginCaptures": { + "1": { "name": "storage.type.enum.case.swift" } + }, + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-type-enum-enum-case" }, + { "include": "#declarations-type-enum-more-cases" } + ] + }, + + "declarations-type-enum-more-cases": { + "name": "meta.enum-case.more-cases", + "begin": ",\\s*", + "end": "(?!\\G)(?!//|/\\*)(?=[;}]|[^\\s,])", + "patterns": [ + { "include": "#comments" }, + { "include": "#declarations-type-enum-enum-case" }, + { "include": "#declarations-type-enum-more-cases" } + ] + }, + + "declarations-type-enum-raw-value-assignment": { + "begin": "(=)\\s*", + "end": "(?!\\G)", + "beginCaptures": { + "1": { "name": "keyword.operator.assignment.swift" } + }, + "patterns": [{ "include": "#comments" }, { "include": "#literals" }] + }, + + "declarations-type-identifier": { + "begin": "((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s*", + "end": "(?!<)", + "beginCaptures": { + "1": { + "name": "meta.type-name.swift", + "patterns": [{ "include": "#builtin-types" }] + }, + "2": { "name": "punctuation.definition.identifier.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { + "begin": "(?=<)", + "end": "(?!\\G)", + "patterns": [{ "include": "#declarations-generic-argument-clause" }] + } + ] + }, + + "declarations-type-operators": { + "patterns": [ + { + "comment": "Swift 3: A & B", + "match": "(?&|\\^~.])(&)(?![/=\\-+!*%<>&|\\^~.])", + "captures": { + "1": { "name": "keyword.operator.type.composition.swift" } + } + }, + + { + "comment": "SE-0390: Noncopyable structs and enums", + "match": "(?&|\\^~.])(~)(?![/=\\-+!*%<>&|\\^~.])", + "captures": { + "1": { "name": "keyword.operator.type.requirement-suppression.swift" } + } + } + ] + }, + + "declarations-typealias": { + "name": "meta.definition.typealias.swift", + "begin": "\\b(typealias)\\s+((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s*", + "end": "(?!\\G)$|(?=;|//|/\\*|$)", + "beginCaptures": { + "1": { "name": "keyword.other.declaration-specifier.swift" }, + "2": { "name": "entity.name.type.typealias.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.identifier.swift" } + }, + "patterns": [ + { + "begin": "\\G(?=<)", + "end": "(?!\\G)", + "patterns": [{ "include": "#declarations-generic-parameter-clause" }] + }, + { "include": "#declarations-typealias-assignment" } + ] + }, + + "declarations-typealias-assignment": { + "begin": "(=)\\s*", + "end": "(?!\\G)$|(?=;|//|/\\*|$)", + "beginCaptures": { + "1": { "name": "keyword.operator.assignment.swift" } + }, + "patterns": [{ "include": "#declarations-available-types" }] + }, + + "declarations-typed-variable-declaration": { + "begin": "(?x)\n\\b(?:(async)\\s+)?(let|var)\\b\\s+\n(?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k)\\s*\n:", + "end": "(?=$|[={])", + "beginCaptures": { + "1": { "name": "storage.modifier.async.swift" }, + "2": { "name": "keyword.other.declaration-specifier.swift" } + }, + "patterns": [{ "include": "#declarations-available-types" }] + }, + + "declarations-types-precedencegroup": { + "patterns": [ + { + "comment": "Precedence groups in the standard library", + "name": "support.type.swift", + "match": "\\b(?:BitwiseShift|Assignment|RangeFormation|Casting|Addition|NilCoalescing|Comparison|LogicalConjunction|LogicalDisjunction|Default|Ternary|Multiplication|FunctionArrow)Precedence\\b" + } + ] + }, + + "expressions": { + "comment": "trailing closures need to be parsed before other member references", + "patterns": [ + { "include": "#expressions-without-trailing-closures-or-member-references" }, + { "include": "#expressions-trailing-closure" }, + { "include": "#member-reference" } + ] + }, + + "expressions-trailing-closure": { + "patterns": [ + { + "comment": "foo { body } -- a call with a trailing closure and no argument clause", + "name": "meta.function-call.trailing-closure-only.swift", + "match": "(#?(?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))(?=\\s*\\{)", + "captures": { + "1": { "name": "support.function.any-method.swift" }, + "2": { "name": "punctuation.definition.identifier.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" } + } + }, + + { + "comment": "foo: { body } -- labeled-trailing-closure (SE-0279)", + "match": "((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s*(:)(?=\\s*\\{)", + "captures": { + "1": { "name": "support.function.any-method.trailing-closure-label.swift" }, + "2": { "name": "punctuation.definition.identifier.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.separator.argument-label.swift" } + } + } + ] + }, + + "expressions-without-trailing-closures": { + "patterns": [ + { "include": "#expressions-without-trailing-closures-or-member-references" }, + { "include": "#member-references" } + ] + }, + + "expressions-without-trailing-closures-or-member-references": { + "patterns": [ + { "include": "#comments" }, + { "include": "#code-block" }, + { "include": "#attributes" }, + { + "include": "#expressions-without-trailing-closures-or-member-references-closure-parameter" + }, + { "include": "#literals" }, + { "include": "#operators" }, + { "include": "#builtin-types" }, + { "include": "#builtin-functions" }, + { "include": "#builtin-global-functions" }, + { "include": "#builtin-properties" }, + { + "include": "#expressions-without-trailing-closures-or-member-references-compound-name" + }, + { "include": "#conditionals" }, + { "include": "#keywords" }, + { + "include": "#expressions-without-trailing-closures-or-member-references-availability-condition" + }, + { + "include": "#expressions-without-trailing-closures-or-member-references-function-or-macro-call-expression" + }, + { + "include": "#expressions-without-trailing-closures-or-member-references-macro-expansion" + }, + { + "include": "#expressions-without-trailing-closures-or-member-references-subscript-expression" + }, + { + "include": "#expressions-without-trailing-closures-or-member-references-parenthesized-expression" + }, + + { + "name": "support.variable.discard-value.swift", + "match": "\\b_\\b" + } + ] + }, + + "expressions-without-trailing-closures-or-member-references-availability-condition": { + "begin": "\\B(#(?:un)?available)(\\()", + "end": "\\)", + "beginCaptures": { + "1": { "name": "support.function.availability-condition.swift" }, + "2": { "name": "punctuation.definition.arguments.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.arguments.end.swift" } + }, + "patterns": [ + { + "match": "\\s*\\b((?:iOS|macOS|OSX|watchOS|tvOS|visionOS|UIKitForMac)(?:ApplicationExtension)?)\\b(?:\\s+([0-9]+(?:\\.[0-9]+)*\\b))", + "captures": { + "1": { "name": "keyword.other.platform.os.swift" }, + "2": { "name": "constant.numeric.swift" } + } + }, + + { + "match": "(\\*)\\s*(.*?)(?=[,)])", + "captures": { + "1": { "name": "keyword.other.platform.all.swift" }, + "2": { "name": "invalid.illegal.character-not-allowed-here.swift" } + } + }, + + { + "name": "invalid.illegal.character-not-allowed-here.swift", + "match": "[^\\s,)]+" + } + ] + }, + + "expressions-without-trailing-closures-or-member-references-closure-parameter": { + "name": "variable.language.closure-parameter.swift", + "match": "\\$[0-9]+" + }, + + "expressions-without-trailing-closures-or-member-references-compound-name": { + "comment": "a reference to a function with disambiguating argument labels, such as foo(_:), foo(bar:), etc.", + "match": "(?x)\n((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k)) # function name\n\\(\n (\n (\n ((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k)) # argument label\n : # colon\n )+\n )\n\\)", + "captures": { + "1": { "name": "entity.name.function.compound-name.swift" }, + "2": { "name": "punctuation.definition.entity.swift" }, + "3": { "name": "punctuation.definition.entity.swift" }, + "4": { + "patterns": [ + { + "name": "entity.name.function.compound-name.swift", + "match": "(?`?)(?!_:)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k):", + "captures": { + "1": { "name": "punctuation.definition.entity.swift" }, + "2": { "name": "punctuation.definition.entity.swift" } + } + } + ] + } + } + }, + + "expressions-without-trailing-closures-or-member-references-expression-element-list": { + "patterns": [ + { "include": "#comments" }, + + { + "comment": "an element with a label", + "begin": "((?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s*(:)", + "end": "(?=[,)\\]])", + "beginCaptures": { + "1": { "name": "support.function.any-method.swift" }, + "2": { "name": "punctuation.definition.identifier.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.separator.argument-label.swift" } + }, + "patterns": [{ "include": "#expressions" }] + }, + + { + "comment": "an element without a label (i.e. anything else)", + "begin": "(?![,)\\]])(?=\\S)", + "end": "(?=[,)\\]])", + "patterns": [{ "include": "#expressions" }] + } + ] + }, + + "expressions-without-trailing-closures-or-member-references-function-or-macro-call-expression": { + "patterns": [ + { + "comment": "foo(args) -- a call whose callee is a highlightable name", + "name": "meta.function-call.swift", + "begin": "(#?(?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))\\s*(\\()", + "end": "\\)", + "beginCaptures": { + "1": { "name": "support.function.any-method.swift" }, + "2": { "name": "punctuation.definition.identifier.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" }, + "4": { "name": "punctuation.definition.arguments.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.arguments.end.swift" } + }, + "patterns": [ + { + "include": "#expressions-without-trailing-closures-or-member-references-expression-element-list" + } + ] + }, + + { + "comment": "[Int](args) -- a call whose callee is a more complicated expression", + "name": "meta.function-call.swift", + "begin": "(?<=[`\\])}>\\p{L}_\\p{N}\\p{M}])\\s*(\\()", + "end": "\\)", + "beginCaptures": { + "1": { "name": "punctuation.definition.arguments.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.arguments.end.swift" } + }, + "patterns": [ + { + "include": "#expressions-without-trailing-closures-or-member-references-expression-element-list" + } + ] + } + ] + }, + + "expressions-without-trailing-closures-or-member-references-macro-expansion": { + "name": "support.function.any-method.swift", + "match": "(#(?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))" + }, + + "expressions-without-trailing-closures-or-member-references-parenthesized-expression": { + "comment": "correctly matching closure expressions is too hard (depends on trailing \"in\") so we just tack on some basics to the end of parenthesized-expression", + "begin": "\\(", + "end": "(\\))\\s*((?:\\b(?:async|throws|rethrows)\\s)*)", + "beginCaptures": { + "0": { "name": "punctuation.section.tuple.begin.swift" } + }, + "endCaptures": { + "1": { "name": "punctuation.section.tuple.end.swift" }, + "2": { + "patterns": [ + { + "name": "invalid.illegal.rethrows-only-allowed-on-function-declarations.swift", + "match": "\\brethrows\\b" + }, + { "include": "#async-throws" } + ] + } + }, + "patterns": [ + { + "include": "#expressions-without-trailing-closures-or-member-references-expression-element-list" + } + ] + }, + + "expressions-without-trailing-closures-or-member-references-subscript-expression": { + "name": "meta.subscript-expression.swift", + "begin": "(?<=[`\\p{L}_\\p{N}\\p{M}])\\s*(\\[)", + "end": "\\]", + "beginCaptures": { + "1": { "name": "punctuation.definition.arguments.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.arguments.end.swift" } + }, + "patterns": [ + { + "include": "#expressions-without-trailing-closures-or-member-references-expression-element-list" + } + ] + }, + + "keywords": { + "patterns": [ + { + "name": "keyword.control.branch.swift", + "match": "(?\n (?> # no backtracking, avoids issues with negative lookbehind at end\n (?:\n \\\\Q\n (?:(?!\\\\E)(?!/).)*+\n # A quoted sequence may not have a closing E, in which case it extends to the end of the regex\n (?:\\\\E | (?=/))\n | \\\\.\n | \\(\\?\\#[^)]*\\)\n | \\(\\?\n # InterpolatedCallout\n (?>\n {[^{].*?}\n | {{[^{].*?}}\n | {{{[^{].*?}}}\n | {{{{[^{].*?}}}}\n | {{{{{[^{].*?}}}}}\n | {{{{{{.+?}}}}}}\n )\n (?:\\[(?!\\d)\\w+\\])?\n [X<>]?\n \\)\n # Allow nested character classes to a limited depth\n | \\[(?:\n \\\\. |\n [^\\[\\]\\\\] |\n \\[(?:\n \\\\. |\n [^\\[\\]\\\\] |\n \\[(?:\n \\\\. |\n [^\\[\\]\\\\] |\n \\[(?:\n \\\\. |\n [^\\[\\]\\\\]\n )+\\]\n )+\\]\n )+\\]\n )+\\]\n | \\(\\g?+\\)\n | (?:(?!/)[^()\\[\\\\])+ # any character (until end)\n )+\n )\n )?+\n (?\n (?> # no backtracking, avoids issues with negative lookbehind at end\n (?:\n \\\\Q\n (?:(?!\\\\E)(?!/\\2).)*+\n # A quoted sequence may not have a closing E, in which case it extends to the end of the regex\n (?:\\\\E | (?=/\\2))\n | \\\\.\n | \\(\\?\\#[^)]*\\)\n | \\(\\?\n # InterpolatedCallout\n (?>\n {[^{].*?}\n | {{[^{].*?}}\n | {{{[^{].*?}}}\n | {{{{[^{].*?}}}}\n | {{{{{[^{].*?}}}}}\n | {{{{{{.+?}}}}}}\n )\n (?:\\[(?!\\d)\\w+\\])?\n [X<>]?\n \\)\n # Allow nested character classes to a limited depth\n | \\[(?:\n \\\\. |\n [^\\[\\]\\\\] |\n \\[(?:\n \\\\. |\n [^\\[\\]\\\\] |\n \\[(?:\n \\\\. |\n [^\\[\\]\\\\] |\n \\[(?:\n \\\\. |\n [^\\[\\]\\\\]\n )+\\]\n )+\\]\n )+\\]\n )+\\]\n | \\(\\g?+\\)\n | (?:(?!/\\2)[^()\\[\\\\])+ # any character (until end)\n )+\n )\n)?+\n(/\\2) # (4)\n| \\#+/.+(\\n)", + "captures": { + "0": { + "patterns": [{ "include": "#literals-regular-expression-literal-regex-guts" }] + }, + "1": { "name": "punctuation.definition.string.begin.regexp.swift" }, + "4": { "name": "punctuation.definition.string.end.regexp.swift" }, + "5": { "name": "invalid.illegal.returns-not-allowed.regexp" } + } + } + ] + }, + + "literals-regular-expression-literal-backreference-or-subpattern": { + "comment": "These patterns are separated to work around issues like https://github.com/microsoft/vscode-textmate/issues/164", + "patterns": [ + { + "comment": "'\\g{' NamedOrNumberRef '}'", + "match": "(?x)(\\\\g\\{) (?: ((?!\\d)\\w+) (?:([+-])(\\d+))? | ([+-]?\\d+) (?:([+-])(\\d+))? ) (\\})", + "captures": { + "1": { "name": "constant.character.escape.backslash.regexp" }, + "2": { "name": "variable.other.group-name.regexp" }, + "3": { "name": "keyword.operator.recursion-level.regexp" }, + "4": { "name": "constant.numeric.integer.decimal.regexp" }, + "5": { "name": "constant.numeric.integer.decimal.regexp" }, + "6": { "name": "keyword.operator.recursion-level.regexp" }, + "7": { "name": "constant.numeric.integer.decimal.regexp" }, + "8": { "name": "constant.character.escape.backslash.regexp" } + } + }, + + { + "comment": "'\\g' NumberRef", + "match": "(?x)(\\\\g) ([+-]?\\d+) (?:([+-])(\\d+))?", + "captures": { + "1": { "name": "constant.character.escape.backslash.regexp" }, + "2": { "name": "constant.numeric.integer.decimal.regexp" }, + "3": { "name": "keyword.operator.recursion-level.regexp" }, + "4": { "name": "constant.numeric.integer.decimal.regexp" } + } + }, + + { + "comment": "'\\k<' NamedOrNumberRef '>'\n '\\g<' NamedOrNumberRef '>'", + "match": "(?x)(\\\\[gk]<) (?: ((?!\\d)\\w+) (?:([+-])(\\d+))? | ([+-]?\\d+) (?:([+-])(\\d+))? ) (>)", + "captures": { + "1": { "name": "constant.character.escape.backslash.regexp" }, + "2": { "name": "variable.other.group-name.regexp" }, + "3": { "name": "keyword.operator.recursion-level.regexp" }, + "4": { "name": "constant.numeric.integer.decimal.regexp" }, + "5": { "name": "constant.numeric.integer.decimal.regexp" }, + "6": { "name": "keyword.operator.recursion-level.regexp" }, + "7": { "name": "constant.numeric.integer.decimal.regexp" }, + "8": { "name": "constant.character.escape.backslash.regexp" } + } + }, + + { + "comment": "\"\\k'\" NamedOrNumberRef \"'\"\n \"\\g'\" NamedOrNumberRef \"'\"", + "match": "(?x)(\\\\[gk]') (?: ((?!\\d)\\w+) (?:([+-])(\\d+))? | ([+-]?\\d+) (?:([+-])(\\d+))? ) (')", + "captures": { + "1": { "name": "constant.character.escape.backslash.regexp" }, + "2": { "name": "variable.other.group-name.regexp" }, + "3": { "name": "keyword.operator.recursion-level.regexp" }, + "4": { "name": "constant.numeric.integer.decimal.regexp" }, + "5": { "name": "constant.numeric.integer.decimal.regexp" }, + "6": { "name": "keyword.operator.recursion-level.regexp" }, + "7": { "name": "constant.numeric.integer.decimal.regexp" }, + "8": { "name": "constant.character.escape.backslash.regexp" } + } + }, + + { + "comment": "'\\k{' NamedRef '}'", + "match": "(?x)(\\\\k\\{) ((?!\\d)\\w+) (?:([+-])(\\d+))? (\\})", + "captures": { + "1": { "name": "constant.character.escape.backslash.regexp" }, + "2": { "name": "variable.other.group-name.regexp" }, + "3": { "name": "keyword.operator.recursion-level.regexp" }, + "4": { "name": "constant.numeric.integer.decimal.regexp" }, + "5": { "name": "constant.character.escape.backslash.regexp" } + } + }, + + { + "name": "keyword.other.back-reference.regexp", + "match": "\\\\[1-9][0-9]+" + }, + + { + "comment": "'(?P=' NamedRef ')'", + "match": "(?x)(\\(\\?(?:P[=>]|&)) ((?!\\d)\\w+) (?:([+-])(\\d+))? (\\))", + "captures": { + "1": { "name": "keyword.other.back-reference.regexp" }, + "2": { "name": "variable.other.group-name.regexp" }, + "3": { "name": "keyword.operator.recursion-level.regexp" }, + "4": { "name": "constant.numeric.integer.decimal.regexp" }, + "5": { "name": "keyword.other.back-reference.regexp" } + } + }, + + { + "name": "keyword.other.back-reference.regexp", + "match": "\\(\\?R\\)" + }, + + { + "comment": "'(?' NumberRef ')'", + "match": "(?x)(\\(\\?) ([+-]?\\d+) (?:([+-])(\\d+))? (\\))", + "captures": { + "1": { "name": "keyword.other.back-reference.regexp" }, + "2": { "name": "constant.numeric.integer.decimal.regexp" }, + "3": { "name": "keyword.operator.recursion-level.regexp" }, + "4": { "name": "constant.numeric.integer.decimal.regexp" }, + "5": { "name": "keyword.other.back-reference.regexp" } + } + } + ] + }, + + "literals-regular-expression-literal-backtracking-directive-or-global-matching-option": { + "match": "(?x)\n(\\(\\*)\n(?:\n (ACCEPT|FAIL|F|MARK(?=:)|(?=:)|COMMIT|PRUNE|SKIP|THEN)\n (?:(:)([^)]+))?\n | (?:(LIMIT_(?:DEPTH|HEAP|MATCH))(=)(\\d+))\n | (\n CRLF | CR | ANYCRLF | ANY | LF | NUL\n | BSR_ANYCRLF | BSR_UNICODE\n | NOTEMPTY_ATSTART | NOTEMPTY\n | NO_AUTO_POSSESS | NO_DOTSTAR_ANCHOR\n | NO_JIT | NO_START_OPT | UTF | UCP\n )\n)\n(\\))", + "captures": { + "1": { "name": "keyword.control.directive.regexp" }, + "2": { "name": "keyword.control.directive.regexp" }, + "3": { "name": "keyword.control.directive.regexp" }, + "4": { "name": "variable.language.tag.regexp" }, + "5": { "name": "keyword.control.directive.regexp" }, + "6": { "name": "keyword.operator.assignment.regexp" }, + "7": { "name": "constant.numeric.integer.decimal.regexp" }, + "8": { "name": "keyword.control.directive.regexp" }, + "9": { "name": "keyword.control.directive.regexp" } + } + }, + + "literals-regular-expression-literal-callout": { + "name": "meta.callout.regexp", + "match": "(?x)\n# PCRECallout\n(\\()(?\\?C)\n (?:\n (?\\d+)\n | `(?(?:[^`]|``)*)`\n | '(?(?:[^']|'')*)'\n | \"(?(?:[^\"]|\"\")*)\"\n | \\^(?(?:[^\\^]|\\^\\^)*)\\^\n | %(?(?:[^%]|%%)*)%\n | \\#(?(?:[^#]|\\#\\#)*)\\#\n | \\$(?(?:[^$]|\\$\\$)*)\\$\n | \\{(?(?:[^}]|\\}\\})*)\\}\n )?\n(\\))\n# NamedCallout\n| (\\()(?\\*)\n (?(?!\\d)\\w+)\n (?:\\[(?(?!\\d)\\w+)\\])?\n (?:\\{ [^,}]+ (?:,[^,}]+)* \\})?\n (\\))\n# InterpolatedCallout\n| (\\()(?\\?)\n (?>(\\{(?:\\g<-1>|(?!{).*?)\\}))\n (?:\\[(?(?!\\d)\\w+)\\])?\n (?[X<>]?)\n (\\))", + "captures": { + "1": { "name": "punctuation.definition.group.regexp" }, + "2": { "name": "keyword.control.callout.regexp" }, + "3": { "name": "constant.numeric.integer.decimal.regexp" }, + "4": { "name": "entity.name.function.callout.regexp" }, + "5": { "name": "entity.name.function.callout.regexp" }, + "6": { "name": "entity.name.function.callout.regexp" }, + "7": { "name": "entity.name.function.callout.regexp" }, + "8": { "name": "entity.name.function.callout.regexp" }, + "9": { "name": "entity.name.function.callout.regexp" }, + "10": { "name": "entity.name.function.callout.regexp" }, + "11": { "name": "entity.name.function.callout.regexp" }, + "12": { "name": "punctuation.definition.group.regexp" }, + "13": { "name": "punctuation.definition.group.regexp" }, + "14": { "name": "keyword.control.callout.regexp" }, + "15": { "name": "entity.name.function.callout.regexp" }, + "16": { "name": "variable.language.tag-name.regexp" }, + "17": { "name": "punctuation.definition.group.regexp" }, + "18": { "name": "punctuation.definition.group.regexp" }, + "19": { "name": "keyword.control.callout.regexp" }, + "21": { "name": "variable.language.tag-name.regexp" }, + "22": { "name": "keyword.control.callout.regexp" }, + "23": { "name": "punctuation.definition.group.regexp" } + } + }, + + "literals-regular-expression-literal-character-properties": { + "name": "constant.other.character-class.set.regexp", + "match": "(?x)\n\\\\[pP]\\{ ([\\s\\w-]+(?:=[\\s\\w-]+)?) \\}\n| (\\[:) ([\\s\\w-]+(?:=[\\s\\w-]+)?) (:\\])", + "captures": { + "1": { "name": "support.variable.character-property.regexp" }, + "2": { "name": "punctuation.definition.character-class.regexp" }, + "3": { "name": "support.variable.character-property.regexp" }, + "4": { "name": "punctuation.definition.character-class.regexp" } + } + }, + + "literals-regular-expression-literal-custom-char-class": { + "patterns": [ + { + "name": "constant.other.character-class.set.regexp", + "begin": "(\\[)(\\^)?", + "end": "\\]", + "beginCaptures": { + "1": { "name": "punctuation.definition.character-class.regexp" }, + "2": { "name": "keyword.operator.negation.regexp" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.character-class.regexp" } + }, + "patterns": [ + { "include": "#literals-regular-expression-literal-custom-char-class-members" } + ] + } + ] + }, + + "literals-regular-expression-literal-custom-char-class-members": { + "comment": "TODO: should also include atoms?", + "patterns": [ + { + "comment": "\\b inside a character class represents a backspace", + "name": "constant.character.escape.backslash.regexp", + "match": "\\\\b" + }, + { "include": "#literals-regular-expression-literal-custom-char-class" }, + { "include": "#literals-regular-expression-literal-quote" }, + { "include": "#literals-regular-expression-literal-set-operators" }, + { "include": "#literals-regular-expression-literal-unicode-scalars" }, + { "include": "#literals-regular-expression-literal-character-properties" } + ] + }, + + "literals-regular-expression-literal-group-option-toggle": { + "comment": "A matching option sequence may be part of an \"isolated group\" which has an implicit scope that wraps the remaining elements of the current group", + "name": "keyword.other.option-toggle.regexp", + "match": "(?x)\n\\(\\?\n(?:\n \\^(?:[iJmnsUxwDPSW]|xx|y\\{[gw]\\})*\n | (?:[iJmnsUxwDPSW]|xx|y\\{[gw]\\})+\n | (?:[iJmnsUxwDPSW]|xx|y\\{[gw]\\})* - (?:[iJmnsUxwDPSW]|xx|y\\{[gw]\\})*\n)\n\\)" + }, + + "literals-regular-expression-literal-group-or-conditional": { + "patterns": [ + { + "name": "meta.group.absent.regexp", + "begin": "(\\()(\\?~)", + "end": "\\)", + "beginCaptures": { + "1": { "name": "punctuation.definition.group.regexp" }, + "2": { "name": "keyword.control.conditional.absent.regexp" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.group.regexp" } + }, + "patterns": [{ "include": "#literals-regular-expression-literal-regex-guts" }] + }, + + { + "name": "meta.group.conditional.regexp", + "begin": "(?x)\n# KnownConditionalStart\n(\\() (?\\?\\()\n (?:\n (? (?[+-]?\\d+)(?:(?[+-])(?\\d+))? )\n | (?R) \\g?\n | (?R&) (? (?(?!\\d)\\w+) (?:(?[+-])(?\\d+))? )\n | (?<) (?:\\g|\\g) (?>)\n | (?') (?:\\g|\\g) (?')\n | (?DEFINE)\n | (?VERSION)(?>?=)(?\\d+\\.\\d+)\n )\n(?\\))\n| (\\()(?\\?)(?=\\()", + "end": "\\)", + "beginCaptures": { + "1": { "name": "punctuation.definition.group.regexp" }, + "2": { "name": "keyword.control.conditional.regexp" }, + "4": { "name": "constant.numeric.integer.decimal.regexp" }, + "5": { "name": "keyword.operator.recursion-level.regexp" }, + "6": { "name": "constant.numeric.integer.decimal.regexp" }, + "7": { "name": "keyword.control.conditional.regexp" }, + "8": { "name": "keyword.control.conditional.regexp" }, + "10": { "name": "variable.other.group-name.regexp" }, + "11": { "name": "keyword.operator.recursion-level.regexp" }, + "12": { "name": "constant.numeric.integer.decimal.regexp" }, + "13": { "name": "keyword.control.conditional.regexp" }, + "14": { "name": "keyword.control.conditional.regexp" }, + "15": { "name": "keyword.control.conditional.regexp" }, + "16": { "name": "keyword.control.conditional.regexp" }, + "17": { "name": "keyword.control.conditional.regexp" }, + "18": { "name": "keyword.control.conditional.regexp" }, + "19": { "name": "keyword.operator.comparison.regexp" }, + "20": { "name": "constant.numeric.integer.decimal.regexp" }, + "21": { "name": "keyword.control.conditional.regexp" }, + "22": { "name": "punctuation.definition.group.regexp" }, + "23": { "name": "keyword.control.conditional.regexp" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.group.regexp" } + }, + "patterns": [{ "include": "#literals-regular-expression-literal-regex-guts" }] + }, + + { + "name": "meta.group.regexp", + "begin": "(?x)\n(\\()\n(\n # BasicGroupKind\n (\\?)\n (?:\n ([:|>=!*] | <[=!*])\n # named groups\n | P?< (?:((?!\\d)\\w+) (-))? ((?!\\d)\\w+) >\n | ' (?:((?!\\d)\\w+) (-))? ((?!\\d)\\w+) '\n # matching options\n | (?:\n \\^(?:[iJmnsUxwDPSW]|xx|y\\{[gw]\\})*\n | (?:[iJmnsUxwDPSW]|xx|y\\{[gw]\\})+\n | (?:[iJmnsUxwDPSW]|xx|y\\{[gw]\\})* - (?:[iJmnsUxwDPSW]|xx|y\\{[gw]\\})*\n ): # case without : is handled by group-option-toggle\n )\n # PCRE2GroupKind\n | \\*(\n atomic\n |pla|positive_lookahead\n |nla|negative_lookahead\n |plb|positive_lookbehind\n |nlb|negative_lookbehind\n |napla|non_atomic_positive_lookahead\n |naplb|non_atomic_positive_lookbehind\n |sr|script_run\n |asr|atomic_script_run\n ):\n)?+", + "end": "\\)", + "beginCaptures": { + "1": { "name": "punctuation.definition.group.regexp" }, + "2": { "name": "keyword.other.group-options.regexp" }, + "3": { "name": "punctuation.definition.group.regexp" }, + "4": { "name": "punctuation.definition.group.regexp" }, + "5": { "name": "variable.other.group-name.regexp" }, + "6": { "name": "keyword.operator.balancing-group.regexp" }, + "7": { "name": "variable.other.group-name.regexp" }, + "8": { "name": "variable.other.group-name.regexp" }, + "9": { "name": "keyword.operator.balancing-group.regexp" }, + "10": { "name": "variable.other.group-name.regexp" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.group.regexp" } + }, + "patterns": [{ "include": "#literals-regular-expression-literal-regex-guts" }] + } + ] + }, + + "literals-regular-expression-literal-line-comment": { + "name": "comment.line.regexp", + "match": "(\\#).*$", + "captures": { + "1": { "name": "punctuation.definition.comment.regexp" } + } + }, + + "literals-regular-expression-literal-quote": { + "name": "string.quoted.other.regexp.swift", + "begin": "\\\\Q", + "end": "\\\\E|(\\n)", + "beginCaptures": { + "0": { "name": "constant.character.escape.backslash.regexp" } + }, + "endCaptures": { + "0": { "name": "constant.character.escape.backslash.regexp" }, + "1": { "name": "invalid.illegal.returns-not-allowed.regexp" } + } + }, + + "literals-regular-expression-literal-regex-guts": { + "patterns": [ + { "include": "#literals-regular-expression-literal-quote" }, + + { + "name": "comment.block.regexp", + "begin": "\\(\\?\\#", + "end": "\\)", + "beginCaptures": { + "0": { "name": "punctuation.definition.comment.begin.regexp" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.comment.end.regexp" } + } + }, + + { + "name": "meta.embedded.expression.regexp", + "begin": "<\\{", + "end": "\\}>", + "beginCaptures": { + "0": { "name": "punctuation.section.embedded.begin.regexp" } + }, + "endCaptures": { + "0": { "name": "punctuation.section.embedded.end.regexp" } + } + }, + + { "include": "#literals-regular-expression-literal-unicode-scalars" }, + { "include": "#literals-regular-expression-literal-character-properties" }, + + { + "name": "keyword.control.anchor.regexp", + "match": "[$^]|\\\\[AbBGyYzZ]|\\\\K" + }, + + { + "include": "#literals-regular-expression-literal-backtracking-directive-or-global-matching-option" + }, + { "include": "#literals-regular-expression-literal-callout" }, + { + "include": "#literals-regular-expression-literal-backreference-or-subpattern" + }, + + { + "name": "constant.character.character-class.regexp", + "match": "\\.|\\\\[CdDhHNORsSvVwWX]" + }, + + { + "name": "constant.character.entity.control-character.regexp", + "match": "\\\\c." + }, + + { + "name": "constant.character.escape.backslash.regexp", + "match": "\\\\[^c]" + }, + + { + "name": "keyword.operator.or.regexp", + "match": "\\|" + }, + + { + "name": "keyword.operator.quantifier.regexp", + "match": "[*+?]" + }, + + { + "name": "keyword.operator.quantifier.regexp", + "match": "\\{\\s*\\d+\\s*(?:,\\s*\\d*\\s*)?\\}|\\{\\s*,\\s*\\d+\\s*\\}" + }, + + { "include": "#literals-regular-expression-literal-custom-char-class" }, + { "include": "#literals-regular-expression-literal-group-option-toggle" }, + { "include": "#literals-regular-expression-literal-group-or-conditional" } + ] + }, + + "literals-regular-expression-literal-set-operators": { + "patterns": [ + { + "name": "keyword.operator.intersection.regexp.swift", + "match": "&&" + }, + + { + "name": "keyword.operator.subtraction.regexp.swift", + "match": "--" + }, + + { + "name": "keyword.operator.symmetric-difference.regexp.swift", + "match": "\\~\\~" + } + ] + }, + + "literals-regular-expression-literal-unicode-scalars": { + "name": "constant.character.numeric.regexp", + "match": "(?x)\n\\\\u\\{\\s*(?:[0-9a-fA-F]+\\s*)+\\}\n| \\\\u[0-9a-fA-F]{4}\n| \\\\x\\{[0-9a-fA-F]+\\}\n| \\\\x[0-9a-fA-F]{0,2}\n| \\\\U[0-9a-fA-F]{8}\n| \\\\o\\{[0-7]+\\}\n| \\\\0[0-7]{0,3}\n| \\\\N\\{(?:U\\+[0-9a-fA-F]{1,8} | [\\s\\w-]+)\\}" + }, + + "literals-string": { + "patterns": [ + { + "comment": "SE-0168: Multi-Line String Literals", + "name": "string.quoted.double.block.swift", + "begin": "\"\"\"", + "end": "\"\"\"(#*)", + "beginCaptures": { + "0": { "name": "punctuation.definition.string.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.string.end.swift" }, + "1": { "name": "invalid.illegal.extra-closing-delimiter.swift" } + }, + "patterns": [ + { + "name": "invalid.illegal.content-after-opening-delimiter.swift", + "match": "\\G.+(?=\"\"\")|\\G.+" + }, + + { + "name": "constant.character.escape.newline.swift", + "match": "\\\\\\s*\\n" + }, + + { "include": "#literals-string-string-guts" }, + + { + "comment": "Allow \\(\"\"\"...\"\"\") to appear inside a block string", + "name": "invalid.illegal.content-before-closing-delimiter.swift", + "match": "\\S((?!\\\\\\().)*(?=\"\"\")" + } + ] + }, + + { + "name": "string.quoted.double.block.raw.swift", + "begin": "#\"\"\"(?!#)(?=(?:[^\"]|\"(?!#))*$)", + "end": "\"\"\"#(#*)", + "beginCaptures": { + "0": { "name": "punctuation.definition.string.begin.swift" } + }, + "endCaptures": { + "0": { "name": "punctuation.definition.string.end.swift" }, + "1": { "name": "invalid.illegal.extra-closing-delimiter.swift" } + }, + "patterns": [ + { + "name": "invalid.illegal.content-after-opening-delimiter.swift", + "match": "\\G.+(?=\"\"\")|\\G.+" + }, + + { + "name": "constant.character.escape.newline.swift", + "match": "\\\\#\\s*\\n" + }, + + { "include": "#literals-string-raw-string-guts" }, + + { + "comment": "Allow \\(\"\"\"...\"\"\") to appear inside a block string", + "name": "invalid.illegal.content-before-closing-delimiter.swift", + "match": "\\S((?!\\\\#\\().)*(?=\"\"\")" + } + ] + }, + + { + "name": "string.quoted.double.block.raw.swift", + "begin": "(?`?)[\\p{L}_][\\p{L}_\\p{N}\\p{M}]*(\\k))", + "captures": { + "1": { "name": "variable.other.swift" }, + "2": { "name": "punctuation.definition.identifier.swift" }, + "3": { "name": "punctuation.definition.identifier.swift" } + } + } + ] + }, + + "operators": { + "patterns": [ + { + "comment": "Type casting", + "name": "keyword.operator.type-casting.swift", + "match": "\\b(is\\b|as([!?]\\B|\\b))" + }, + + { + "comment": "This rule helps us speed up the matching.", + "begin": "(?x)\n(?=\n (? # operator-head\n [/=\\-+!*%<>&|^~?]\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n )\n | \\.\n (\n \\g # operator-head\n | \\.\n | [\\x{0300}-\\x{036F}] # operator-character\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n)", + "end": "(?!\\G)", + "patterns": [ + { + "comment": "Prefix unary operator", + "match": "(?x)\n\\G # Matching from the beginning ensures\n # that we start with operator-head\n(?<=^|[\\s(\\[{,;:])\n(\n (?!(//|/\\*|\\*/))\n (\n [/=\\-+!*%<>&|^~?] # operator-head\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n \n | [\\x{0300}-\\x{036F}] # operator-character\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n)++\n(?![\\s)\\]},;:]|\\z)", + "captures": { + "0": { + "patterns": [ + { + "name": "keyword.operator.increment-or-decrement.swift", + "match": "\\G(\\+\\+|\\-\\-)$" + }, + + { + "name": "keyword.operator.arithmetic.unary.swift", + "match": "\\G(\\+|\\-)$" + }, + + { + "name": "keyword.operator.logical.not.swift", + "match": "\\G!$" + }, + + { + "name": "keyword.operator.bitwise.not.swift", + "match": "\\G~$" + }, + + { + "name": "keyword.operator.custom.prefix.swift", + "match": ".+" + } + ] + } + } + }, + + { + "comment": "Postfix unary operator", + "match": "(?x)\n\\G # Matching from the beginning ensures\n # that we start with operator-head\n(?&|^~?] # operator-head\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n \n | [\\x{0300}-\\x{036F}] # operator-character\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n)++\n(?=[\\s)\\]},;:]|\\z)", + "captures": { + "0": { + "patterns": [ + { + "name": "keyword.operator.increment-or-decrement.swift", + "match": "\\G(\\+\\+|\\-\\-)$" + }, + + { + "name": "keyword.operator.increment-or-decrement.swift", + "match": "\\G!$" + }, + + { + "name": "keyword.operator.custom.postfix.swift", + "match": ".+" + } + ] + } + } + }, + + { + "comment": "Infix operator", + "match": "(?x)\n\\G # Matching from the beginning ensures\n # that we start with operator-head\n(\n (?!(//|/\\*|\\*/))\n (\n [/=\\-+!*%<>&|^~?] # operator-head\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n \n | [\\x{0300}-\\x{036F}] # operator-character\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n)++", + "captures": { + "0": { + "patterns": [ + { + "name": "keyword.operator.assignment.swift", + "match": "\\G=$" + }, + + { + "name": "keyword.operator.assignment.compound.swift", + "match": "\\G(\\+|\\-|\\*|/|%|<<|>>|&|\\^|\\||&&|\\|\\|)=$" + }, + + { + "name": "keyword.operator.arithmetic.swift", + "match": "\\G(\\+|\\-|\\*|/)$" + }, + + { + "name": "keyword.operator.arithmetic.overflow.swift", + "match": "\\G&(\\+|\\-|\\*)$" + }, + + { + "name": "keyword.operator.arithmetic.remainder.swift", + "match": "\\G%$" + }, + + { + "name": "keyword.operator.comparison.swift", + "match": "\\G(==|!=|>|<|>=|<=|~=)$" + }, + + { + "name": "keyword.operator.coalescing.swift", + "match": "\\G\\?\\?$" + }, + + { + "name": "keyword.operator.logical.swift", + "match": "\\G(&&|\\|\\|)$" + }, + + { + "name": "keyword.operator.bitwise.swift", + "match": "\\G(&|\\||\\^|<<|>>)$" + }, + + { + "name": "keyword.operator.bitwise.swift", + "match": "\\G(===|!==)$" + }, + + { + "name": "keyword.operator.ternary.swift", + "match": "\\G\\?$" + }, + + { + "name": "keyword.operator.custom.infix.swift", + "match": ".+" + } + ] + } + } + }, + + { + "comment": "Dot prefix unary operator", + "match": "(?x)\n\\G # Matching from the beginning ensures\n # that we start with operator-head\n(?<=^|[\\s(\\[{,;:])\n\\. # dot\n(\n (?!(//|/\\*|\\*/))\n (\n \\. # dot\n | [/=\\-+!*%<>&|^~?] # operator-head\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n \n | [\\x{0300}-\\x{036F}] # operator-character\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n)++\n(?![\\s)\\]},;:]|\\z)", + "captures": { + "0": { + "patterns": [ + { + "name": "keyword.operator.custom.prefix.dot.swift", + "match": ".+" + } + ] + } + } + }, + + { + "comment": "Dot postfix unary operator", + "match": "(?x)\n\\G # Matching from the beginning ensures\n # that we start with operator-head\n(?&|^~?] # operator-head\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n \n | [\\x{0300}-\\x{036F}] # operator-character\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n)++\n(?=[\\s)\\]},;:]|\\z)", + "captures": { + "0": { + "patterns": [ + { + "name": "keyword.operator.custom.postfix.dot.swift", + "match": ".+" + } + ] + } + } + }, + + { + "comment": "Dot infix operator", + "match": "(?x)\n\\G # Matching from the beginning ensures\n # that we start with operator-head\n\\. # dot\n(\n (?!(//|/\\*|\\*/))\n (\n \\. # dot\n | [/=\\-+!*%<>&|^~?] # operator-head\n | [\\x{00A1}-\\x{00A7}]\n | [\\x{00A9}\\x{00AB}]\n | [\\x{00AC}\\x{00AE}]\n | [\\x{00B0}-\\x{00B1}\\x{00B6}\\x{00BB}\\x{00BF}\\x{00D7}\\x{00F7}]\n | [\\x{2016}-\\x{2017}\\x{2020}-\\x{2027}]\n | [\\x{2030}-\\x{203E}]\n | [\\x{2041}-\\x{2053}]\n | [\\x{2055}-\\x{205E}]\n | [\\x{2190}-\\x{23FF}]\n | [\\x{2500}-\\x{2775}]\n | [\\x{2794}-\\x{2BFF}]\n | [\\x{2E00}-\\x{2E7F}]\n | [\\x{3001}-\\x{3003}]\n | [\\x{3008}-\\x{3030}]\n \n | [\\x{0300}-\\x{036F}] # operator-character\n | [\\x{1DC0}-\\x{1DFF}]\n | [\\x{20D0}-\\x{20FF}]\n | [\\x{FE00}-\\x{FE0F}]\n | [\\x{FE20}-\\x{FE2F}]\n | [\\x{E0100}-\\x{E01EF}]\n )\n)++", + "captures": { + "0": { + "patterns": [ + { + "name": "keyword.operator.range.swift", + "match": "\\G\\.\\.[.<]$" + }, + + { + "name": "keyword.operator.custom.infix.dot.swift", + "match": ".+" + } + ] + } + } + } + ] + }, + + { + "name": "keyword.operator.ternary.swift", + "match": ":" + } + ] + }, + + "root": { + "patterns": [ + { "include": "#compiler-control" }, + { "include": "#declarations" }, + { "include": "#expressions" } + ] + } + } +} \ No newline at end of file diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index 6852b6a51e..a410aef45f 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -1,5 +1,6 @@ M41 512c0-128 46-241 138-333C271 87 384 41 512 41s241 46 333 138c92 92 138 205 138 333s-46 241-138 333c-92 92-205 138-333 138s-241-46-333-138C87 753 41 640 41 512zm87 0c0 108 36 195 113 271s164 113 271 113c108 0 195-36 271-113s113-164 113-271-36-195-113-271c-77-77-164-113-271-113-108 0-195 36-271 113C164 317 128 404 128 512zm256 148V292l195 113L768 512l-195 113-195 113v-77zm148-113-61 36V440l61 36 61 36-61 36z + M306 761c3-3 8-7 16-11 15-7 35-14 60-19 4-1 14-3 11-18l-3-16c-4-18-13-14-18-13C301 699 256 724 256 763c0 58 112 91 252 91 140 0 252-33 252-91 0-38-45-64-116-78-5-1-16-3-18 13-1 6-2 11-3 15-4 18 8 18 14 19 24 5 44 11 58 18 8 4 13 8 16 11 1 2 2 2 2 1 0 3-17 15-50 24-40 12-95 19-155 19-59 0-114-7-155-19-32-9-49-21-50-24 0 0 0-0 2-2zm73-123c54 79 107 142 129 142 22 0 75-62 129-142 62-91 101-173 101-231C738 280 635 176 508 176c-127 0-230 104-230 232 0 57 39 139 101 231zm150 64a446 446 0 01-21 23c-6-6-13-14-21-23-21-25-46-57-68-91-57-84-92-159-92-204 0-102 82-184 182-184 100 0 182 82 182 184 0 45-36 120-92 204-23 33-47 66-68 91zm-21-200a102 102 0 100-204 102 102 0 000 204zm0-48a54 54 0 110-108 54 54 0 010 108z M304 464a128 128 0 01128-128c71 0 128 57 128 128v224a32 32 0 01-64 0V592h-128v95a32 32 0 01-64 0v-224zm64 1v64h128v-64a64 64 0 00-64-64c-35 0-64 29-64 64zM688 337c18 0 32 14 32 32v319a32 32 0 01-32 32c-18 0-32-14-32-32v-319a32 32 0 0132-32zM84 911l60-143A446 446 0 0164 512C64 265 265 64 512 64s448 201 448 448-201 448-448 448c-54 0-105-9-153-27l-242 22a32 32 0 01-32-44zm133-150-53 126 203-18 13 5c41 15 85 23 131 23 212 0 384-172 384-384S724 128 512 128 128 300 128 512c0 82 26 157 69 220l20 29z M233 163h560c11 0 20-9 20-20V63h-226c-6-36-37-63-74-63s-68 27-74 63h-226v80c0 11 9 20 20 20zM883 63h-10v112c0 33-27 60-60 60h-600c-33 0-60-27-60-60V63h-10c-44 0-80 36-80 80v801c0 44 36 80 80 80h740c44 0 80-36 80-80V143c0-44-36-80-80-80zm-720 469c0-24 20-44 44-44h62v-72c0-24 20-44 44-44s44 20 44 44v72h62c24 0 44 20 44 44s-20 44-44 44h-62v72c0 24-20 44-44 44s-44-20-44-44v-72h-62c-24 0-44-20-44-44zm700 307c0 24-20 44-44 44h-612c-24 0-44-20-44-44s20-44 44-44h612c24 0 44 20 44 44zm0-191c0 24-20 44-44 44h-232c-24 0-44-20-44-44s20-44 44-44h232c24 0 44 20 44 44zm0-191c0 24-20 44-44 44h-162c-24 0-44-20-44-44s20-44 44-44h162c24 0 44 20 44 44z M366 146l293 0 0-73-293 0 0 73zm658 366 0 274q0 38-27 65t-65 27l-841 0q-38 0-65-27t-27-65l0-274 384 0 0 91q0 15 11 26t26 11l183 0q15 0 26-11t11-26l0-91 384 0zm-439 0 0 73-146 0 0-73 146 0zm439-274 0 219-1024 0 0-219q0-38 27-65t65-27l201 0 0-91q0-23 16-39t39-16l329 0q23 0 39 16t16 39l0 91 201 0q38 0 65 27t27 65z @@ -34,6 +35,7 @@ M896 811l-128 0c-23 0-43-19-43-43 0-23 19-43 43-43l107 0c13 0 21-9 21-21L896 107c0-13-9-21-21-21L448 85c-13 0-21 9-21 21l0 21c0 23-19 43-43 43-23 0-43-19-43-43L341 85c0-47 38-85 85-85l469 0c47 0 85 38 85 85l0 640C981 772 943 811 896 811zM683 299l0 640c0 47-38 85-85 85L128 1024c-47 0-85-38-85-85L43 299c0-47 38-85 85-85l469 0C644 213 683 252 683 299zM576 299 149 299c-13 0-21 9-21 21l0 597c0 13 9 21 21 21l427 0c13 0 21-9 21-21L597 320C597 307 589 299 576 299z M339 297c17-23 25-51 25-83 2-42-12-79-43-108S255 62 215 65c-32 0-60 8-84 25s-42 39-54 67-15 56-10 86 19 55 40 76c21 21 47 35 76 41v303c-30 6-55 20-76 41-21 21-35 47-40 76-5 30-2 58 10 86s30 50 54 67 52 25 83 25 59-8 84-25c25-17 45-39 57-67 6-19 10-39 10-61 0-30-8-57-25-83-21-34-52-55-92-64v-299l25-6c28-13 50-32 67-57zm-45 471c8 15 12 30 11 46-1 16-6 31-16 46-10 15-23 25-40 32s-34 8-51 5-32-11-46-24-22-28-24-45c-6-28-1-53 18-75s41-33 68-33c17 0 32 4 46 13 14 8 25 20 33 35zM167 288c-15-11-26-24-33-41-7-17-10-34-6-51 3-17 11-32 24-45s28-21 46-25 36-3 53 5c17 7 30 18 40 32 10 14 15 29 16 46 1 17-3 33-11 48-8 15-20 27-33 35-14 8-29 13-46 13s-33-5-48-16zm615 45c2-19-1-38-10-57-11-28-29-50-54-67s-53-25-83-25h-111l76-76-45-41-127 127v41l127 127 45-41-76-76h111c23 0 44 8 62 25 18 17 27 38 27 64v89h57V332zm0 449H960v-61H782V542h-61v178H543v61h178v178h61V781z M280 145l243 341 0-0 45 63-0 0 79 110a143 143 0 11-36 75l-88-123-92 126c1 4 1 9 1 13l0 5a143 143 0 11-36-95l82-113L221 188l60-43zm473 541a70 70 0 100 140 70 70 0 000-140zm-463 0a70 70 0 100 140 70 70 0 000-140zM772 145l59 43-232 319-45-63L772 145z + M925 386c-23-53-55-101-96-142-41-41-89-73-142-96C631 124 573 112 512 112s-119 12-174 35c-53 23-101 55-142 96-41 41-73 89-96 142C76 441 64 500 64 560c0 133 58 258 160 343l2 1c6 5 13 8 21 8h532c8 0 15-3 21-8l2-1C902 818 960 693 960 560c0-61-12-119-35-174zM761 836H263C185 766 140 666 140 560c0-99 39-193 109-263 70-70 164-109 263-109 99 0 193 39 263 109 70 70 109 164 109 263 0 106-45 206-123 276zM624 422c-3-3-8-3-11 0L528 506c-19-5-39-0-54 15-22 22-22 57 0 79 22 22 57 22 79 0 15-15 20-35 15-54l85-85c3-3 3-8 0-11l-28-28zM490 320h44c4 0 8-4 8-8v-80c0-4-4-8-8-8h-44c-4 0-8 4-8 8v80c0 4 4 8 8 8zM750 538v44c0 4 4 8 8 8h80c4 0 8-4 8-8v-44c0-4-4-8-8-8h-80c-4 0-8 4-8 8zM763 341l-31-31c-3-3-8-3-11 0l-57 57c-3 3-3 8 0 11l31 31c3 3 8 3 11 0l57-57c3-3 3-8 0-11zM304 310c-3-3-8-3-11 0l-31 31c-3 3-3 8 0 11l57 57c3 3 8 3 11 0l31-31c3-3 3-8 0-11l-57-57zM262 530h-80c-4 0-8 4-8 8v44c0 4 4 8 8 8h80c4 0 8-4 8-8v-44c0-4-4-8-8-8z M853 137h-102V102h-68v34H341V102h-68v34h-102C133 137 102 167 102 205v68h819V205c0-38-31-68-68-68zM102 341v512C102 891 133 922 171 922h683C891 922 922 891 922 853v-512H102zM444 819h-68V512H307v-68h137V819zm273-273a102 102 0 01-30 72l-97 97 0 0a34 34 0 00-10 24v12H717V819H512v-80a102 102 0 0130-72l97-97a34 34 0 0010-24h0C649 527 633 512 614 512s-34 15-34 34H512c0-57 46-102 102-102s102 46 102 102h-0 0z M128 183C128 154 154 128 183 128h521c30 0 55 26 55 55v38c0 17-17 34-34 34s-34-17-34-34v-26H196v495h26c17 0 34 17 34 34s-17 34-34 34h-38c-30 0-55-26-55-55V183zM380 896h-34c-26 0-47-21-47-47v-90h68V828h64V896H380c4 0 0 0 0 0zM759 828V896h90c26 0 47-21 47-47v-90h-68V828h-68zM828 435H896V346c0-26-21-47-47-47h-90v68H828v68zM435 299v68H367V439H299V346C299 320 320 299 346 299h90zM367 649H299v-107h68v107zM546 367V299h107v68h-107zM828 546H896v107h-68v-107zM649 828V896h-107v-68h107zM730 508v188c0 17-17 34-34 34h-188c-17 0-34-17-34-34s17-34 34-34h102l-124-124c-13-13-13-34 0-47 13-13 34-13 47 0l124 124V512c0-17 17-34 34-34 21-4 38 9 38 30z M889 0H135c-32 0-59 26-59 59v906c0 32 26 59 59 59h753c32 0 59-26 59-59v-906c1-33-26-59-58-59zm-165 177c31 0 56 25 56 56s-25 56-56 56-56-25-56-56 25-56 56-56zm-212 0c31 0 56 25 56 56S543 288 512 288s-56-25-56-56S481 177 512 177zm-212 0c31 0 56 25 56 56s-25 56-56 56-56-25-56-56 25-56 56-56zm209 606H285c-25 0-44-20-44-44 0-25 20-44 44-44h224c25 0 44 20 44 44 0 24-20 44-44 44zm230-212H285c-25 0-44-20-44-44 0-25 20-44 44-44h453c25 0 44 20 44 44 1 24-20 44-44 44z @@ -55,6 +57,7 @@ M949 727l-217-231a33 33 0 00-48 0 33 33 0 000 48l157 172H389a35 35 0 00-35 35c0 19 16 34 35 34h452l-160 179a34 34 0 005 54c14 10 33 7 45-5l219-237a33 33 0 000-49zM719 196h131c-24-91-95-160-185-185v131c0 27 25 54 54 54zM129 846l1-747s-7-37 36-33h359v52s-7 76 32 133a191 191 0 00146 84h91v126h66v-191H719a126 126 0 01-127-127V0H155c-51 0-91 40-91 91v767c0 51 40 91 91 91h193v-66H155c0-0-26 4-26-36z M888 314l-198-198c-8-8-21-8-29 0s-8 21 0 29l163 163-216 0L609 43c0-11-9-20-20-20l-408 0c-11 0-20 9-20 20l0 939c0 11 9 20 20 20l694 0c11 0 20-9 20-20l0-653C894 323 892 318 888 314zM854 961l-653 0L201 63l367 0 0 265c0 11 9 20 20 20l265 0L854 961z M416 832H128V128h384v192C512 355 541 384 576 384L768 384v32c0 19 13 32 32 32S832 435 832 416v-64c0-6 0-19-6-25l-256-256c-6-6-19-6-25-6H128A64 64 0 0064 128v704C64 867 93 896 129 896h288c19 0 32-13 32-32S435 832 416 832zM576 172 722 320H576V172zM736 512C614 512 512 614 512 736S614 960 736 960s224-102 224-224S858 512 736 512zM576 736C576 646 646 576 736 576c32 0 58 6 83 26l-218 218c-19-26-26-51-26-83zm160 160c-32 0-64-13-96-32l224-224c19 26 32 58 32 96 0 90-70 160-160 160z + M725 768c24 0 43 19 43 43s-19 43-43 43-43-19-43-43 19-43 43-43zm0-128c-116 0-216 71-256 171 40 100 140 171 256 171s216-71 256-171c-40-100-140-171-256-171zm0 277a107 107 0 110-213 107 107 0 010 213zM396 853H256V171h299v213h213v174c30 3 58 11 85 21V341l-256-256H256a85 85 0 00-85 85v683a85 85 0 0085 85h192c-21-25-39-54-52-85z M896 320c0-19-6-32-19-45l-192-192c-13-13-26-19-45-19H192c-38 0-64 26-64 64v768c0 38 26 64 64 64h640c38 0 64-26 64-64V320zm-256 384H384c-19 0-32-13-32-32s13-32 32-32h256c19 0 32 13 32 32s-13 32-32 32zm166-384H640V128l192 192h-26z M599 425 599 657 425 832 425 425 192 192 832 192Z M320 239 213 299l60-107L213 85l107 60L427 85 367 192 427 299 320 239m512 418L939 597l-60 107L939 811l-107-60L725 811l60-107L725 597l107 60M939 85l-60 107L939 299l-107-60L725 299l60-107L725 85l107 60L939 85m-369 460 104-104-90-90-104 104 90 90m44-234 100 100c17 16 17 44 0 60L215 969c-17 17-44 17-60 0l-100-100c-17-16-17-44 0-60L553 311c17-17 44-17 60 0z @@ -72,7 +75,6 @@ M0 512M1024 512M512 0M512 1024M955 323q0 23-16 39l-414 414-78 78q-16 16-39 16t-39-16l-78-78-207-207q-16-16-16-39t16-39l78-78q16-16 39-16t39 16l168 169 375-375q16-16 39-16t39 16l78 78q16 16 16 39z M416 64H768v64h-64v704h64v64H448v-64h64V512H416a224 224 0 1 1 0-448zM576 832h64V128H576v704zM416 128H512v320H416a160 160 0 0 1 0-320z M24 512A488 488 0 01512 24A488 488 0 011000 512A488 488 0 01512 1000A488 488 0 0124 512zm447-325v327L243 619l51 111 300-138V187H471z - M832 64h128v278l-128-146V64zm64 448L512 73 128 512H0L448 0h128l448 512h-128zm0 83V1024H640V704c0-35-29-64-64-64h-128a64 64 0 00-64 64v320H128V595l384-424 384 424z M512 0C229 0 0 229 0 512c0 283 229 512 512 512s512-229 512-512c0-283-229-512-512-512zm0 958C266 958 66 758 66 512S266 66 512 66 958 266 958 512 758 958 512 958zM192 416h96a32 32 0 0032-32v-32a32 32 0 00-32-32H192a32 32 0 00-32 32v32a32 32 0 0032 32zM384 416h96a32 32 0 0032-32v-32a32 32 0 00-32-32h-96a32 32 0 00-32 32v32a32 32 0 0032 32zM576 416h96a32 32 0 0032-32v-32a32 32 0 00-32-32h-96a32 32 0 00-32 32v32a32 32 0 0032 32zM832 320h-64a32 32 0 00-32 32v128h-160a32 32 0 00-32 32v32a32 32 0 0032 32h256a32 32 0 0032-32v-192a32 32 0 00-32-32zM320 544v-32a32 32 0 00-32-32H192a32 32 0 00-32 32v32a32 32 0 0032 32h96a32 32 0 0032-32zM384 576h96a32 32 0 0032-32v-32a32 32 0 00-32-32h-96a32 32 0 00-32 32v32a32 32 0 0032 32zM800 640H256a32 32 0 00-32 32v32a32 32 0 0032 32h544a32 32 0 0032-32v-32a32 32 0 00-32-32z M973 358a51 51 0 0151 51v563a51 51 0 01-51 51H51a51 51 0 01-51-51V410a51 51 0 0151-51h256a51 51 0 110 102H102v461h819V461h-205a51 51 0 110-102h256zM51 102a51 51 0 110-102h256c141 0 256 115 256 256v388l66-66a51 51 0 1172 72l-154 154a51 51 0 01-72 0l-154-154a51 51 0 1172-72L461 644V256c0-85-69-154-154-154H51z M512 0C229 0 0 229 0 512s229 512 512 512 512-229 512-512S795 0 512 0zM512 928c-230 0-416-186-416-416S282 96 512 96s416 186 416 416S742 928 512 928zM538 343c47 0 83-38 83-78 0-32-21-61-62-61-55 0-82 45-82 77C475 320 498 343 538 343zM533 729c-8 0-11-10-3-40l43-166c16-61 11-100-22-100-39 0-131 40-211 108l16 27c25-17 68-35 78-35 8 0 7 10 0 36l-38 158c-23 89 1 110 34 110 33 0 118-30 196-110l-19-25C575 717 543 729 533 729z @@ -125,6 +127,9 @@ M155 143h715v81H155V143zm358 94 358 369H662l1 278H363V605H155l358-369z M702 677 590 565a148 148 0 10-25 27L676 703zm-346-200a115 115 0 11115 115A115 115 0 01355 478z M928 500a21 21 0 00-19-20L858 472a11 11 0 01-9-9c-1-6-2-13-3-19a11 11 0 015-12l46-25a21 21 0 0010-26l-8-22a21 21 0 00-24-13l-51 10a11 11 0 01-12-6c-3-6-6-11-10-17a11 11 0 011-13l34-39a21 21 0 001-28l-15-18a20 20 0 00-27-4l-45 27a11 11 0 01-13-1c-5-4-10-9-15-12a11 11 0 01-3-12l19-49a21 21 0 00-9-26l-20-12a21 21 0 00-27 6L650 193a9 9 0 01-11 3c-1-1-12-5-20-7a11 11 0 01-7-10l1-52a21 21 0 00-17-22l-23-4a21 21 0 00-24 14L532 164a11 11 0 01-11 7h-20a11 11 0 01-11-7l-17-49a21 21 0 00-24-15l-23 4a21 21 0 00-17 22l1 52a11 11 0 01-8 11c-5 2-15 6-19 7c-4 1-8 0-12-4l-33-40A21 21 0 00313 146l-20 12A21 21 0 00285 184l19 49a11 11 0 01-3 12c-5 4-10 8-15 12a11 11 0 01-13 1L228 231a21 21 0 00-27 4L186 253a21 21 0 001 28L221 320a11 11 0 011 13c-3 5-7 11-10 17a11 11 0 01-12 6l-51-10a21 21 0 00-24 13l-8 22a21 21 0 0010 26l46 25a11 11 0 015 12l0 3c-1 6-2 11-3 16a11 11 0 01-9 9l-51 8A21 21 0 0096 500v23A21 21 0 00114 544l51 8a11 11 0 019 9c1 6 2 13 3 19a11 11 0 01-5 12l-46 25a21 21 0 00-10 26l8 22a21 21 0 0024 13l51-10a11 11 0 0112 6c3 6 6 11 10 17a11 11 0 01-1 13l-34 39a21 21 0 00-1 28l15 18a20 20 0 0027 4l45-27a11 11 0 0113 1c5 4 10 9 15 12a11 11 0 013 12l-19 49a21 21 0 009 26l20 12a21 21 0 0027-6L374 832c3-3 7-5 10-4c7 3 12 5 20 7a11 11 0 018 10l-1 52a21 21 0 0017 22l23 4a21 21 0 0024-14l17-50a11 11 0 0111-7h20a11 11 0 0111 7l17 49a21 21 0 0020 15a19 19 0 004 0l23-4a21 21 0 0017-22l-1-52a11 11 0 018-10c8-3 13-5 18-7l1 0c6-2 9 0 11 3l34 41A21 21 0 00710 878l20-12a21 21 0 009-26l-18-49a11 11 0 013-12c5-4 10-8 15-12a11 11 0 0113-1l45 27a21 21 0 0027-4l15-18a21 21 0 00-1-28l-34-39a11 11 0 01-1-13c3-5 7-11 10-17a11 11 0 0112-6l51 10a21 21 0 0024-13l8-22a21 21 0 00-10-26l-46-25a11 11 0 01-5-12l0-3c1-6 2-11 3-16a11 11 0 019-9l51-8a21 21 0 0018-21v-23zm-565 188a32 32 0 01-51 5a270 270 0 011-363a32 32 0 0151 6l91 161a32 32 0 010 31zM512 782a270 270 0 01-57-6a32 32 0 01-20-47l92-160a32 32 0 0127-16h184a32 32 0 0130 41c-35 109-137 188-257 188zm15-328L436 294a32 32 0 0121-47a268 268 0 0155-6c120 0 222 79 257 188a32 32 0 01-30 41h-184a32 32 0 01-28-16z + M360 623c-32 0-62 11-89 32s-46 48-54 79H94c-8 3-15 7-20 14s-9 15-10 26c-1 11 2 19 10 26s17 10 28 10h115c8 32 26 58 54 79 28 21 58 32 91 32s64-11 91-32c28-21 46-48 54-79h421c8-3 15-7 22-14 7-7 10-15 10-24s-4-18-12-26c-8-8-17-12-28-12H507c-8-32-26-58-54-79s-58-32-89-32h-4zm0 75c21 0 39 7 54 22 15 15 22 32 22 52s-7 37-22 52c-15 15-32 22-52 22-20 0-37-7-52-22s-22-32-22-52 7-37 22-52 32-22 54-22h-4zM662 103c-34 0-66 11-93 32-28 21-46 48-54 79H94c-8 3-15 7-20 14s-8 15-8 24 3 18 10 26c7 8 15 12 26 12h413c8 32 26 58 54 79 28 21 58 32 91 32 33 0 64-11 91-32s46-48 54-79h123c8-3 15-7 22-14s10-15 10-26-4-19-12-26-17-10-28-10H805c-8-32-26-58-54-79s-58-32-89-32zm-4 75c21 0 39 7 54 22 15 15 22 32 22 52s-7 37-22 52c-15 15-32 22-52 22-20 0-37-7-52-22s-22-32-22-52 7-37 22-52 32-22 54-22h-4z + M864 277a117 117 0 00-117-117h-469A117 117 0 00160 277v469a117 117 0 00117 117h469a117 117 0 00117-117v-469zm64 469a181 181 0 01-181 181h-469a181 181 0 01-181-181v-469A181 181 0 01277 96h469a181 181 0 01181 181v469zM256 320a64 64 0 11128 0v384a64 64 0 01-128 0v-384z + M858 213a47 47 0 00-47-47H422v691h388a47 47 0 0047-47V213zm77 597a124 124 0 01-124 124H213A124 124 0 0190 811V213c0-68 55-124 124-124h597c68 0 124 55 124 124v597z M622 718v137q0 9-7 16t-16 7H462q-9 0-16-7t-7-16v-137q0-9 7-16t16-7h137q9 0 16 7t7 16zm181-343q0 31-9 58t-20 44-31 34-33 25-35 20q-23 13-39 37t-16 38q0 10-7 19t-16 9H459q-9 0-15-11T439 626v-26q0-47 37-89T558 449q34-15 48-32t14-43q0-24-27-42T532 313q-37 0-62 17-20 14-61 66-7 9-18 9-7 0-14-5L283 329q-7-6-9-14t3-16q91-152 265-152 46 0 92 18t83 47 61 73 23 91z M900 287c40 69 60 144 60 225s-20 156-60 225c-40 69-94 123-163 163-69 40-144 60-225 60s-156-20-225-60c-69-40-123-94-163-163C84 668 64 593 64 512s20-156 60-225 94-123 163-163c69-40 144-60 225-60s156 20 225 60 123 94 163 163zM762 512c0-9-3-16-9-22L578 315l-44-44c-6-6-13-9-22-9s-16 3-22 9l-44 44-176 176c-6 6-9 13-9 22s3 16 9 22l44 44c6 6 13 9 22 9s16-3 22-9l92-92v269c0 9 3 16 9 22 6 6 13 9 22 9h62c8 0 16-3 22-9 6-6 9-13 9-22V486l92 92c6 6 13 9 22 9 8 0 16-3 22-9l44-44c6-6 9-13 9-22z M512 939C465 939 427 900 427 853 427 806 465 768 512 768 559 768 597 806 597 853 597 900 559 939 512 939M555 85 555 555 747 363 807 423 512 719 217 423 277 363 469 555 469 85 555 85Z @@ -139,12 +144,14 @@ M1097 372h-460l-146-299H146a73 73 0 00-73 73v731a73 73 0 0073 73h878a73 73 0 0073-73V372zM146 0h390l146 299h488V878a146 146 0 01-146 146H146a146 146 0 01-146-146V146a146 146 0 01146-146zm439 0h195l146 246h-195l-146-246zm244 0h195a146 146 0 01146 146v100h-195l-146-246z M177 156c-22 5-33 17-36 37c-10 57-33 258-13 278l445 445c23 23 61 23 84 0l246-246c23-23 23-61 0-84l-445-445C437 120 231 145 177 156zM331 344c-26 26-69 26-95 0c-26-26-26-69 0-95s69-26 95 0C357 276 357 318 331 344z M683 537h-144v-142h-142V283H239a44 44 0 00-41 41v171a56 56 0 0014 34l321 321a41 41 0 0058 0l174-174a41 41 0 000-58zm-341-109a41 41 0 110-58a41 41 0 010 58zM649 284V142h-69v142h-142v68h142v142h69v-142h142v-68h-142z - M996 452 572 28A96 96 0 00504 0H96C43 0 0 43 0 96v408a96 96 0 0028 68l424 424c37 37 98 37 136 0l408-408c37-37 37-98 0-136zM224 320c-53 0-96-43-96-96s43-96 96-96 96 43 96 96-43 96-96 96zm1028 268L844 996c-37 37-98 37-136 0l-1-1L1055 647c34-34 53-79 53-127s-19-93-53-127L663 0h97a96 96 0 0168 28l424 424c37 37 37 98 0 136z + M43 152A109 109 0 01152 43h220c48 0 109 25 143 59L950 537a109 109 0 010 154L691 949a109 109 0 01-154 0L102 514C68 481 43 419 43 371v-220zm85 0v220c0 25 16 65 34 83l435 435a24 24 0 0033 0l259-259c9-9 9-24 0-33L454 162C436 144 397 128 371 128h-220C138 128 128 138 128 152zM277 341a64 64 0 110-128 64 64 0 010 128z + M256 256q0-30-21-52t-52-21-52 21-21 52 21 52 52 21 52-21 21-52zM866 585q0 30-21 51l-281 281q-22 21-52 21-30 0-51-21l-409-409q-22-21-37-58t-15-67l0-238q0-30 22-51t51-22l238 0q30 0 67 15t58 37l409 408q21 22 21 52zM1085 585q0 30-21 51l-281 281q-22 21-52 21-21 0-34-8t-30-26l269-269q21-21 21-51 0-30-21-52l-409-408q-22-22-58-37t-67-15l128 0q30 0 67 15t58 37l409 408q21 22 21 52z M765 118 629 239l-16 137-186 160 54 59 183-168 144 4 136-129 47-43-175-12L827 67zM489 404c-66 0-124 55-124 125s54 121 124 121c66 0 120-55 120-121H489l23-121c-8-4-16-4-23-4zM695 525c0 114-93 207-206 207s-206-94-206-207 93-207 206-207c16 0 27 0 43 4l43-207c-27-4-54-8-85-8-229 0-416 188-416 419s187 419 416 419c225 0 408-180 416-403v-12l-210-4z M144 112h736c18 0 32 14 32 32v736c0 18-14 32-32 32H144c-18 0-32-14-32-32V144c0-18 14-32 32-32zm112 211v72a9 9 0 003 7L386 509 259 615a9 9 0 00-3 7v72a9 9 0 0015 7L493 516a9 9 0 000-14l-222-186a9 9 0 00-15 7zM522 624a10 10 0 00-10 10v60a10 10 0 0010 10h237a10 10 0 0010-10v-60a10 10 0 00-10-10H522z + M929 201c-1-1-1-2-2-3-6-6-15-6-21 0L709 395l-78-78L828 120c1-1 2-2 2-3 4-7 2-16-5-20-112-66-259-51-356 45-77 77-102 185-76 283l-335 335c-3 3-3 9 0 13l194 194c4 4 9 4 13 0l335-335c98 26 206 1 283-76 96-97 112-244 45-356zM825 498c-66 66-166 79-245 38l-10 10-0-0-313 313-91-91 263-263s0 0 0 0l60-60c-41-79-28-179 38-245a211 211 0 01165-61L541 291a37 37 0 000 52l142 142a37 37 0 0052 0l152-152c4 59-16 120-61 164z M170 831 513 489 855 831 960 726 512 278 64 726 170 831zM512 278h448v-128h-896v128h448z M897 673v13c0 51-42 93-93 93h-10c-1 0-2 0-2 0H220c-23 0-42 19-42 42v13c0 23 19 42 42 42h552c14 0 26 12 26 26 0 14-12 26-26 26H220c-51 0-93-42-93-93v-13c0-51 42-93 93-93h20c1-0 2-0 2-0h562c23 0 42-19 42-42v-13c0-11-5-22-13-29-8-7-17-11-28-10H660c-14 0-26-12-26-26 0-14 12-26 26-26h144c24-1 47 7 65 24 18 17 29 42 29 67zM479 98c-112 0-203 91-203 203 0 44 14 85 38 118l132 208c15 24 50 24 66 0l133-209c23-33 37-73 37-117 0-112-91-203-203-203zm0 327c-68 0-122-55-122-122s55-122 122-122 122 55 122 122-55 122-122 122z - M912 800a48 48 0 1 1 0 96h-416a48 48 0 1 1 0-96h416z m-704-704A112 112 0 0 1 256 309.184V480h80a48 48 0 0 1 0 96H256v224h81.664a48 48 0 1 1 0 96H256a96 96 0 0 1-96-96V309.248A112 112 0 0 1 208 96z m704 384a48 48 0 1 1 0 96h-416a48 48 0 0 1 0-96h416z m0-320a48 48 0 1 1 0 96h-416a48 48 0 0 1 0-96h416z + M192 320a64 64 0 0 1-64-64V128a64 64 0 0 1 64-64h256a64 64 0 0 1 64 64v128a64 64 0 0 1-64 64H352v160H512V448a64 64 0 0 1 64-64h256a64 64 0 0 1 64 64v128a64 64 0 0 1-64 64H576a64 64 0 0 1-64-64v-32H352v256H512V768a64 64 0 0 1 64-64h256a64 64 0 0 1 64 64v128a64 64 0 0 1-64 64H576a64 64 0 0 1-64-64v-32H352a64 64 0 0 1-64-64V320H192z M30 0 30 30 0 15z M0 0 0 30 30 15z M762 1024C876 818 895 504 448 514V768L64 384l384-384v248c535-14 595 472 314 776z diff --git a/src/Resources/Images/ExternalToolIcons/JetBrains/AI.png b/src/Resources/Images/ExternalToolIcons/JetBrains/AI.png new file mode 100644 index 0000000000..64390ed327 Binary files /dev/null and b/src/Resources/Images/ExternalToolIcons/JetBrains/AI.png differ diff --git a/src/Resources/Images/ExternalToolIcons/JetBrains/IC.png b/src/Resources/Images/ExternalToolIcons/JetBrains/IC.png new file mode 100644 index 0000000000..62e50e936d Binary files /dev/null and b/src/Resources/Images/ExternalToolIcons/JetBrains/IC.png differ diff --git a/src/Resources/Images/ExternalToolIcons/JetBrains/IU.png b/src/Resources/Images/ExternalToolIcons/JetBrains/IU.png new file mode 100644 index 0000000000..62e50e936d Binary files /dev/null and b/src/Resources/Images/ExternalToolIcons/JetBrains/IU.png differ diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 57a344c634..9e2d348bc8 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -16,19 +16,20 @@ Pfad für diesen Worktree. Relative Pfade werden unterstützt. Branch-Name: Optional. Standard ist der Name des Zielordners. - Branch verfolgen: - Remote-Branch verfolgen - Was auschecken: - Neuen Branch erstellen - Existierender Branch AI-Assistent Modell NEU GENERIEREN Verwende AI, um Commit-Nachrichten zu generieren + Übernehmen SourceGit minimieren + Andere ausblenden Alles anzeigen - Wähle die anzuwendende .patch-Datei + 3-Way Merge + Wähle die anzuwendende patch-Datei Ignoriere Leerzeichen-Änderungen + Quelle: + Datei + Zwischenablage Patch anwenden Leerzeichen: Stash anwenden @@ -46,12 +47,13 @@ KEINE ALS UNVERÄNDERT ANGENOMMENEN DATEIEN Bild laden… Aktualisieren - BINÄRE DATEI WIRD NICHT UNTERSTÜTZT!!! Bisect Abbrechen Schlecht Gut Überspringen + Bisecting. Einen anderen Commit auschecken und als gut oder schlecht markieren. + Bisecting. Aktuellen Commit als schlecht markieren oder einen anderen auschecken und als schlecht markieren. Bisecting. Aktuellen Commit als gut oder schlecht markieren und einen anderen auschecken. Bisecting. Ist der aktuelle HEAD gut oder schlecht? Blame @@ -62,6 +64,7 @@ Ausgewählte 2 Branches vergleichen Vergleichen mit… Vergleichen mit HEAD + Vergleichen mit ${0}$ Branch-Namen kopieren PR erstellen… PR für Upstream ${0}$ erstellen… @@ -113,6 +116,12 @@ Die folgenden Submodule müssen aktualisiert werden:{0}Möchtest du sie aktualisieren? Auschecken & Fast Forward Fast Forward zu: + Branch aus Stash auschecken + Neuer Branch: + Stash: + Commit oder Tag auschecken + Ziel: + Achtung: Danach ist HEAD losgelöst (‚detached‘) Cherry Pick Quelle an Commit-Nachricht anhängen Commit(s): @@ -124,6 +133,8 @@ Remote-Repository klonen Extra-Parameter: Zusätzliche Argumente für das Klonen des Repositorys. Optional. + Lesezeichen: + Gruppe: Lokaler Name: Repository-Name. Optional. Übergeordnetes Verzeichnis: @@ -131,14 +142,20 @@ Repository-URL: SCHLIESSEN Editor + Branches + Branches & Tags + Benutzerdefinierte Repository-Aktionen + Dateien der Revision Commit auschecken Diesen Commit cherry-picken Mehrere cherry-picken… Vergleiche mit HEAD Vergleiche mit Worktree Autor + Autor-Zeitpunkt Commit-Nachricht Committer + Committer-Zeitpunkt SHA Betreff Benutzerdefinierte Aktion @@ -158,6 +175,7 @@ ÄNDERUNGEN geänderte Datei(en) Änderungen durchsuchen… + Details einklappen DATEIEN LFS-DATEI Dateien durchsuchen… @@ -181,6 +199,11 @@ Commit-Nachricht eingeben. Leerzeile zum Trennen von Betreff und Beschreibung verwenden! BETREFF Vergleichen + ÄNDERUNGEN + COMMITS + NUR LINKS VORHANDENE COMMITS + NUR RECHTS VORHANDENE COMMITS + Tipp: Wenn die andere Seite HEAD ist, kannst du ausgewählte Commits über das Kontextmenü cherry-picken. Repository-Einstellungen COMMIT-VORLAGE Vordefinierte Parameter: @@ -225,6 +248,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Minute(n) Konventionelle Commit-Typen Standard-Remote + `--recursive` beim Auto-Aktualisieren von Submodulen verwenden Bevorzugter Merge-Modus TICKETSYSTEM Beispiel für Azure-DevOps-Regel hinzufügen @@ -257,8 +281,11 @@ $1, $2, … Werte der Eingabe-Steuerelemente Bezeichnung: Einträge: Nutze ‚|‘, um Einträge zu trennen + String-Formatierung: + Optional. Formatiert die Ausgabe. Wird ignoriert, wenn die Eingabe leer ist. Verwende bitte `${VALUE}` als Platzhalter für die Eingabe. Die vordefinierten Parameter ${REPO}, ${REMOTE}, ${BRANCH}, ${BRANCH_FRIENDLY_NAME}, ${SHA}, ${FILE} und ${TAG} bleiben hier verwendbar Typ: + Kurznamen verwenden: Arbeitsumgebungen Farbe Name @@ -266,6 +293,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente WEITER Leerer Commit erkannt! Möchtest du trotzdem fortfahren (--allow-empty)? ALLES STAGEN & COMMITTEN + AUSGEWÄHLTE STAGEN & COMMITTEN Leerer Commit erkannt! Möchtest du trotzdem fortfahren (--allow-empty) oder alle Änderungen stagen und dann committen? Neustart erforderlich Du musst dieses Programm neustarten, um die Änderungen zu übernehmen. @@ -278,6 +306,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Typ der Änderung: Kopieren Gesamten Text kopieren + Als Patch kopieren Ganzen Pfad kopieren Pfad kopieren Branch erstellen… @@ -310,7 +339,9 @@ $1, $2, … Werte der Eingabe-Steuerelemente Submodul: Branch löschen Branch: + Löschen erzwingen, auch bei nicht gemergten Commits Du bist dabei, einen Remote-Branch zu löschen!!! + Auch Remote-Branch ${0}$ löschen Mehrere Branches löschen Du versuchst, mehrere Branches auf einmal zu löschen. Kontrolliere dies sorgfältig, bevor du fortfährst! Mehrere Tags löschen @@ -330,6 +361,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Tag: Von Remote-Repositorys löschen BINÄRER VERGLEICH + LEERE DATEI Erster Unterschied Ignoriere Leerzeichen-Änderungen ÜBEREINANDER @@ -349,6 +381,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente SUBMODUL GELÖSCHT NEU + + {0} nicht committete Änderungen Seiten tauschen Syntax-Hervorhebung Zeilenumbruch @@ -365,6 +398,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Alle Änderungen in der Arbeitskopie. Änderungen: Ignorierte Dateien einbeziehen + Geänderte/gelöschte Dateien einbeziehen Nicht-verfolgte Dateien einbeziehen {0} Änderungen werden verworfen Du kannst das nicht rückgängig machen!!! @@ -400,14 +434,25 @@ $1, $2, … Werte der Eingabe-Steuerelemente Dateiverlauf ÄNDERUNG INHALT + Dateimodus-Änderung: + Gelöschter Dateimodus: + Verzeichnis + Ausführbar + Neuer Dateimodus: + Normal + Submodul + Symlink + Unbekannt Git Flow Development-Branch: Feature: Feature-Präfix: + Abschließen ${0}$ FLOW – Feature fertigstellen FLOW – Hotfix fertigstellen FLOW – Release fertigstellen Ziel: + Vor dem Mergen rebasen Squash beim Merge Hotfix: Hotfix-Präfix: @@ -416,10 +461,12 @@ $1, $2, … Werte der Eingabe-Steuerelemente Production-Branch: Release: Release-Präfix: + Beginnen bei: Feature beginnen… FLOW – Feature beginnen Hotfix beginnen… FLOW – Hotfix beginnen + Name: Namen eingeben Release beginnen… FLOW – Release beginnen @@ -453,17 +500,25 @@ $1, $2, … Werte der Eingabe-Steuerelemente Remote: Verfolge alle ‚{0}‘ Dateien Verfolge alle *{0} Dateien - VERLAUF + Commit auswählen AUTOR AUTOR-ZEITPUNKT COMMIT-ZEITPUNKT VERLAUF & COMMIT-NACHRICHT SHA + HERVORHEBUNGEN IM GRAPHEN + Alle + Nur aktueller Branch + Aktueller Branch & ausgewählte Commits + Nur ausgewählte Commits {0} COMMITS AUSGEWÄHLT SPALTEN ANZEIGEN Halte ‚Strg‘ oder ‚Umschalt‘, um mehrere Commits auszuwählen. Halte ⌘ oder ⇧, um mehrere Commits auszuwählen TIPPS: + In separatem Fenster öffnen + Commit-Details + Revisionsvergleich Tastaturkürzel-Referenz GLOBAL Klone neues Repository @@ -471,6 +526,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Zur nächsten Registerkarte wechseln Zur vorherigen Registerkarte wechseln Neue Registerkarte erstellen + Lokales Repository öffnen Einstellungen öffnen Auswahlmenü für Arbeitsumgebungen anzeigen Aktive Registerkarte wechseln @@ -479,21 +535,22 @@ $1, $2, … Werte der Eingabe-Steuerelemente Gestagte Änderungen committen Gestagte Änderungen committen und pushen Alle Änderungen stagen und committen + Neuen Branch erstellen Fetch, wird direkt ausgeführt - Dashboard-Modus (Standard) + Springe zum Nachfolger des ausgewählten Commits Springe zum Vorgänger des ausgewählten Commits Befehlspalette öffnen - Commit-Suchmodus Pull, wird direkt ausgeführt Push, wird direkt ausgeführt Erzwinge Neuladen des Repositorys Wechsle zu ‚Änderungen‘ - Wechsle zu ‚Verlauf‘ Wechsle zu ‚Stashes‘ Verwerfen Stagen Unstagen Initialisiere Repository + Möchtest du `git init` in diesem Pfad ausführen? + Öffnen des Repositorys fehlgeschlagen. Grund: Pfad: Cherry-Pick wird durchgeführt. Verarbeite Commit @@ -505,6 +562,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Reverte Commit Interaktiver Rebase Lokale Änderungen stashen & wieder anwenden + pre-rebase Hook überspringen Auf: Drag & Drop zum Anordnen der Commits Ziel-Branch: @@ -513,6 +571,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Befehle FEHLER INFO + Neue Version verfügbar Repositorys öffnen Registerkarten Arbeitsumgebungen @@ -521,6 +580,10 @@ $1, $2, … Werte der Eingabe-Steuerelemente Ziel-Branch: Merge-Option: Quelle: + Merge wird geprüft… + Merge ist ohne Konflikte möglich + Unbekannter Fehler bei der Merge-Prüfung + Merge wird Konflikte verursachen Zuerst meine, dann deren Zuerst deren, dann meine BEIDE VERWENDEN @@ -532,7 +595,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente ERGEBNIS SPEICHERN & STAGEN DEREN - Merge-Konflikt – {0} + Merge-Konflikte Ungespeicherte Änderungen verwerfen? MEINE VERWENDEN DEREN VERWENDEN @@ -554,6 +617,10 @@ $1, $2, … Werte der Eingabe-Steuerelemente Programmdaten-Ordner öffnen Datei öffnen Öffnen in externem Merge-Tool + Lokales Repository öffnen + Lesezeichen: + Gruppe: + Ordner: Optional. Neue Registerkarte erstellen Registerkarte schließen @@ -577,7 +644,10 @@ $1, $2, … Werte der Eingabe-Steuerelemente Gestern Einstellungen AI + Zusätzlicher Prompt (Verwende `-`, um deine Anforderungen aufzulisten) API-Schlüssel + Modell + Verfügbare Modell-IDs automatisch abrufen Name Der eingegebene Wert ist der Name der Umgebungsvariable, aus der der API-Schlüssel gelesen wird Server @@ -609,8 +679,11 @@ $1, $2, … Werte der Eingabe-Steuerelemente Commit-Historie Standardmäßig ‚ÄNDERUNGEN‘-Ansicht anzeigen Standardmäßig Registerkarte ‚ÄNDERUNGEN‘ in Commit-Details anzeigen + Relative Zeitangaben im Commit-Graphen anzeigen Zeige Tags im Commit-Verlauf Längenvorgabe für Commit-Nachrichten + 24-Stunden-Format + Kompakte Branch-Namen im Commit-Graphen verwenden Standard-Avatar im GitHub-Stil generieren GIT Aktiviere Auto-CRLF @@ -625,6 +698,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente git-credential-libsecret statt git-credential-manager verwenden Benutzername Globaler Git-Benutzername + Änderungen beim Auschecken oder Mergen von Branches standardmäßig stashen & wieder anwenden Git-Version GPG-SIGNIERUNG Commit-Signierung @@ -656,6 +730,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Erzwinge Push Lokaler Branch: NEU + pre-push Hook überspringen Remote: Revision: Revision zu Remote-Branch pushen @@ -672,7 +747,12 @@ $1, $2, … Werte der Eingabe-Steuerelemente Schließen Aktuellen Branch rebasen Lokale Änderungen stashen & wieder anwenden + pre-rebase Hook überspringen Auf: + Rebase wird geprüft… + Rebase ist ohne Konflikte möglich + Unbekannter Fehler bei der Rebase-Prüfung + Rebase wird Konflikte verursachen Remote hinzufügen Remote bearbeiten Name: @@ -683,6 +763,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Benutzerdefinierte Aktion Löschen… Bearbeiten… + Auto-Fetch aktivieren Fetch Im Browser öffnen Prune @@ -706,14 +787,16 @@ $1, $2, … Werte der Eingabe-Steuerelemente WEITER Benutzerdefinierte Aktionen Keine benutzerdefinierten Aktionen - Dashboard Alle Änderungen verwerfen Öffne im Datei-Browser Suche Branches / Tags / Submodule Sichtbarkeit im Verlauf + Filter einklappen Aufheben Im Verlauf ausblenden + Filter ausklappen Im Verlauf filtern + Filter aktiv LAYOUT Horizontal Vertikal @@ -721,7 +804,6 @@ $1, $2, … Werte der Eingabe-Steuerelemente Commit-Zeitpunkt Topologisch LOKALE BRANCHES - Mehr Optionen… Zum HEAD wechseln Erstelle Branch BENACHRICHTIGUNGEN LÖSCHEN @@ -811,6 +893,8 @@ $1, $2, … Werte der Eingabe-Steuerelemente Gestagte und ungestagte Änderungen der ausgewählten Datei(en) werden gestasht!!! Lokale Änderungen stashen Anwenden + Änderungen anwenden + Neuen Branch auschecken Kopiere Nachricht Entfernen Als Patch speichern… @@ -847,9 +931,12 @@ $1, $2, … Werte der Eingabe-Steuerelemente nicht gemerged Aktualisieren URL + Details der Submodul-Änderung + DETAILS ÖFFNEN OK TAGGER ZEIT + Tag auschecken… Vergleiche 2 Tags Vergleichen mit… Vergleichen mit HEAD @@ -860,11 +947,13 @@ $1, $2, … Werte der Eingabe-Steuerelemente Benutzerdefinierte Aktion Lösche ${0}$… Lösche ausgewählte {0} Tags… + Merge ${0}$ in ${1}$ hinein… Pushe ${0}$… Submodule aktualisieren Alle Submodule Bei Bedarf initialisieren Submodul: + Verschachtelte Submodule aktualisieren Auf verfolgten Remote-Branch des Submoduls aktualisieren URL: Logs @@ -891,6 +980,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Ignoriere *{0} Dateien im selben Ordner Ignoriere nicht-verfolgte Dateien in diesem Ordner Ignoriere nur diese Datei + Ignoriere alle nicht-verfolgten Dateien im selben Ordner Nachbesserung Du kannst diese Datei jetzt stagen. Verlauf löschen @@ -910,6 +1000,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente DATEI-KONFLIKTE SIND GELÖST MEINE VERSION VERWENDEN DEREN VERSION VERWENDEN + Änderungen filtern… NICHT-VERFOLGTE DATEIEN EINBEZIEHEN KEINE BISHERIGEN COMMIT-NACHRICHTEN KEINE COMMIT-VORLAGEN @@ -927,9 +1018,12 @@ $1, $2, … Werte der Eingabe-Steuerelemente ARBEITSUMGEBUNG: Arbeitsumgebungen konfigurieren… WORKTREE + BRANCH Pfad kopieren + HEAD Sperren Öffnen + PFAD Entfernen Entsperren JA diff --git a/src/Resources/Locales/el_GR.axaml b/src/Resources/Locales/el_GR.axaml index 4de2a90030..8e5e5f4dfe 100644 --- a/src/Resources/Locales/el_GR.axaml +++ b/src/Resources/Locales/el_GR.axaml @@ -16,11 +16,6 @@ Διαδρομή για αυτό το worktree. Υποστηρίζεται σχετική διαδρομή. Όνομα κλάδου: Προαιρετικό. Προεπιλογή είναι το όνομα του φακέλου προορισμού. - Παρακολούθηση κλάδου: - Παρακολούθηση απομακρυσμένου κλάδου - Τι να γίνει checkout: - Δημιουργία νέου κλάδου - Υπάρχων κλάδος Βοηθός AI ΜΟΝΤΕΛΟ ΕΠΑΝΑΔΗΜΙΟΥΡΓΙΑ @@ -30,7 +25,7 @@ Απόκρυψη άλλων Εμφάνιση όλων Τριμερής συγχώνευση (3-Way) - Επιλέξτε αρχείο .patch για εφαρμογή + Επιλέξτε αρχείο patch για εφαρμογή Αγνόηση αλλαγών κενών χαρακτήρων Πηγή: Αρχείο @@ -52,7 +47,6 @@ ΚΑΝΕΝΑ ΑΡΧΕΙΟ ΔΕΝ ΘΕΩΡΕΙΤΑΙ ΑΜΕΤΑΒΛΗΤΟ Φόρτωση εικόνας... Ανανέωση - ΤΟ ΔΥΑΔΙΚΟ ΑΡΧΕΙΟ ΔΕΝ ΥΠΟΣΤΗΡΙΖΕΤΑΙ!!! Bisect Ματαίωση Κακό @@ -202,7 +196,6 @@ SHA Υπογράφων: Άνοιγμα στον περιηγητή - ΣΤΗΛΗ Εισαγάγετε το μήνυμα commit. Χρησιμοποιήστε μια κενή γραμμή για να διαχωρίσετε το θέμα από την περιγραφή! ΘΕΜΑ Σύγκριση @@ -345,10 +338,10 @@ Εξαναγκασμός απο-αρχικοποίησης ακόμη κι αν περιέχει τοπικές αλλαγές. Υπομονάδα: Διαγραφή κλάδου - Θέλετε να διαγράψετε και τον ακόλουθο απομακρυσμένο κλάδο; Κλάδος: Εξαναγκασμένη διαγραφή ακόμη κι αν περιέχει μη συγχωνευμένα commit Πρόκειται να διαγράψετε έναν απομακρυσμένο κλάδο!!! + Διαγραφή και του απομακρυσμένου κλάδου ${0}$ Διαγραφή πολλαπλών κλάδων Προσπαθείτε να διαγράψετε πολλούς κλάδους ταυτόχρονα. Βεβαιωθείτε ότι ελέγξατε ξανά πριν προχωρήσετε! Διαγραφή πολλαπλών ετικετών @@ -508,7 +501,6 @@ Παρακολούθηση αρχείων με όνομα '{0}' Παρακολούθηση όλων των αρχείων *{0} Επιλογή commit - ΙΣΤΟΡΙΚΟ ΣΥΓΓΡΑΦΕΑΣ ΩΡΑ ΣΥΓΓΡΑΦΕΑ ΩΡΑ COMMIT @@ -545,17 +537,13 @@ Προσθήκη όλων των αλλαγών στο ευρετήριο και commit Δημιουργία νέου κλάδου Fetch, ξεκινά άμεσα - Λειτουργία πίνακα ελέγχου (Προεπιλογή) Μετάβαση στον απόγονο του επιλεγμένου commit Μετάβαση στον γονέα του επιλεγμένου commit Άνοιγμα παλέτας εντολών - Λειτουργία αναζήτησης commit Pull, ξεκινά άμεσα Push, ξεκινά άμεσα Εξαναγκασμός επαναφόρτωσης αυτού του αποθετηρίου - Ανάπτυξη/σύμπτυξη πίνακα λεπτομερειών στο `ΙΣΤΟΡΙΚΟ` Μετάβαση στις 'ΤΟΠΙΚΕΣ ΑΛΛΑΓΕΣ' - Μετάβαση στο 'ΙΣΤΟΡΙΚΟ' Μετάβαση στα 'STASHES' Απόρριψη Προσθήκη στο ευρετήριο @@ -741,6 +729,7 @@ Εξαναγκασμένο push Τοπικός κλάδος: ΝΕΟ + Παράκαμψη του pre-push hook Απομακρυσμένο: Αναθεώρηση: Push αναθεώρησης σε απομακρυσμένο @@ -797,7 +786,6 @@ ΣΥΝΕΧΕΙΑ Προσαρμοσμένες ενέργειες Καμία προσαρμοσμένη ενέργεια - Πίνακας ελέγχου Απόρριψη όλων των αλλαγών Άνοιγμα στον περιηγητή αρχείων Αναζήτηση κλάδων/ετικετών/υπομονάδων @@ -812,7 +800,6 @@ Ημερομηνία commit Τοπολογικά ΤΟΠΙΚΟΙ ΚΛΑΔΟΙ - Περισσότερες επιλογές... Μετάβαση στο HEAD Δημιουργία κλάδου ΕΚΚΑΘΑΡΙΣΗ ΕΙΔΟΠΟΙΗΣΕΩΝ diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 676bd57a02..76ac9e4dcb 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -12,11 +12,6 @@ Path for this worktree. Relative path is supported. Branch Name: Optional. Default is the destination folder name. - Track Branch: - Tracking remote branch - What to Checkout: - Create New Branch - Existing Branch AI Assistant MODEL RE-GENERATE @@ -26,7 +21,7 @@ Hide Others Show All 3-Way Merge - Select .patch file to apply + Select patch file to apply Ignore whitespace changes Source: File @@ -48,7 +43,6 @@ NO FILES ASSUMED AS UNCHANGED Load Image... Refresh - BINARY FILE NOT SUPPORTED!!! Bisect Abort Bad @@ -61,8 +55,10 @@ Blame Blame on Previous Revision Ignore whitespace changes + Holding Ctrl (on Windows/Linux) or ⌘ (on macOS) when clicking commit SHA will rebuild blame data based on that revision. BLAME ON THIS FILE IS NOT SUPPORTED!!! Checkout ${0}$... + Checkout ${0}$ as worktree... Compare selected 2 branches Compare with... Compare with HEAD @@ -92,6 +88,7 @@ {0} commit(s) ahead, {1} commit(s) behind {0} commit(s) behind Invalid + PUSH URL REMOTE STATUS TRACKING @@ -118,6 +115,14 @@ The following submodules need to be updated:{0}Do you want to update them? Checkout & Fast-Forward Fast-Forward to: + Checkout Branch as Worktree + Local Branch: + New Branch Name: + Optional. Default is the destination folder name. + Remote Branch: + Tracking this remote branch. + Worktree Path: + Path for this worktree. Relative path is supported. Checkout Branch From Stash New Branch: Stash: @@ -182,6 +187,7 @@ LFS File Search Files... Submodule + SELECT FILE TO VIEW CONTENT INFORMATION AUTHOR COMMITTER @@ -198,7 +204,6 @@ SHA Signer: Open in Browser - COL Enter commit message. Please use an empty-line to separate subject and description! SUBJECT Compare @@ -334,6 +339,7 @@ lightweight Hold Ctrl to start directly Cut + Dashboard Discard Do Nothing Stash & Reapply @@ -341,10 +347,10 @@ Force de-init even if it contains local changes. Submodule: Delete Branch - Do you want to also delete the following remote branch? Branch: Force delete even if it contains unmerged commit(s) You are about to delete a remote branch!!! + Also delete remote branch ${0}$ Delete Multiple Branches You are trying to delete multiple branches at one time. Be sure to double-check before taking action! Delete Multiple Tags @@ -364,6 +370,7 @@ Tag: Delete from remote repositories BINARY DIFF + VIEW NEW CONTENT EMPTY FILE First Difference Ignore Whitespace Changes @@ -420,6 +427,9 @@ Fetch without tags Remote: Fetch Remote Changes + BINARY FILE + HEX VIEWER + Go to address... Assume unchanged Custom Action Discard... @@ -504,7 +514,7 @@ Track files named '{0}' Track all *{0} files Select Commit - HISTORY + Advanced Options AUTHOR AUTHOR TIME COMMIT TIME @@ -515,6 +525,7 @@ Current Branch Only Current Branch & Selected Commits Selected Commits Only + Selected Commits (First-parent Only) SELECTED {0} COMMITS SHOW COLUMNS Hold 'Ctrl' or 'Shift' to select multiple commits. @@ -541,18 +552,17 @@ Stage all changes and commit Create new branch Fetch, starts directly - Dashboard mode (Default) Goto child of selected commit Goto parent of selected commit Open command palette - Commit search mode Pull, starts directly Push, starts directly Force to reload this repository - Expand/Collapse details panel in `HISTORY` - Switch to 'LOCAL CHANGES' - Switch to 'HISTORY' - Switch to 'STASHES' + Expand/Collapse commit details panel + Toggle commit search panel + Switch to 'Local Changes' + Switch to 'Dashboard' + Switch to 'Stashes' Discard Stage Unstage @@ -623,6 +633,8 @@ Open Default Editor (System) Open Data Storage Directory + Cache + Config Open File Open in External Merge Tool Open Local Repository @@ -654,10 +666,12 @@ AI Additional Prompt (Use `-` to list your requirements) API Key + Extra Headers (one `Name: Value` per line) Model Fetch available model-ids automatically Name Entered value is the name to load API key from ENV + Reasoning Effort Level Server APPEARANCE Default Font @@ -738,6 +752,7 @@ Force push Local Branch: NEW + Bypass the pre-push hook Remote: Revision: Push Revision To Remote @@ -764,6 +779,7 @@ Edit Remote Name: Remote name + Reset push URL to this repository URL Repository URL: Remote git repository URL Copy URL @@ -774,6 +790,7 @@ Fetch Open In Browser Prune + Set Push URL... Confirm to Remove Worktree Enable `--force` Option Target: @@ -794,10 +811,9 @@ CONTINUE Custom Actions No Custom Actions - Dashboard Discard all changes Open in File Browser - Search Branches/Tags/Submodules + Filter Branches/Tags/Submodules Visibility in Graph Collapse Filters Unset @@ -812,7 +828,6 @@ Commit Date Topologically LOCAL BRANCHES - More options... Navigate to HEAD Create Branch CLEAR NOTIFICATIONS @@ -822,13 +837,15 @@ REMOTES Add Remote RESOLVE - Search Commit + SEARCH COMMITS Author Content Message Path SHA Current Branch + SEARCH BY + Search Commits Decorated commits only First-parent only SHOW FLAGS @@ -879,6 +896,10 @@ New Version Release Date: Software Update There are currently no updates available. + Set Push URL for Remote + Push URL: + Remote: + URL: Set Submodule's Branch Submodule: Current: @@ -892,6 +913,22 @@ Go to SSH Private Key: Private SSH key store path + Open SSH Key Helper + Do you really want to delete the following keys? + Fingerprint + Generate SSH Key + Confirm Passphrase: + Email: + Required. + Key Name: + Required. For example: id_github + Passphrase: + GENERATE + Type: + Use passphrase for private key + Path + Public Key + SSH Key Helper START Stash Include untracked files @@ -909,13 +946,19 @@ Save as Patch... Drop Stash Drop: - STASHES + Stashes CHANGES STASHES Statistics + COMMITS OVERVIEW MONTH WEEK + SourceGit will only get a maximum of 200,000 commits according to the target branch for analysis. + TOP AUTHORS + COMMITS + PERCENTAGE + Authors with less than 1% commits will be grouped under Others. AUTHORS: COMMITS: SUBMODULES @@ -958,6 +1001,12 @@ Delete selected {0} tags... Merge ${0}$ into ${1}$... Push ${0}$... + TERMINATE + Untrusted Repository + Do you want to run `git config --global --add safe.directory {0}` command to trust this path? + Git refused to access this repository: + Path: + {0} repositories were skipped because git does not trust them. Please open them manually and confirm whether to trust them. Update Submodules All submodules Initialize as needed @@ -983,7 +1032,7 @@ Open Terminal Rescan Repositories in Default Clone Dir Search Repositories... - LOCAL CHANGES + Local Changes Git Ignore Ignore all *{0} files Ignore *{0} files in the same folder @@ -994,6 +1043,7 @@ You can stage this file now. Clear History Are you sure you want to clear all commit message history? This action cannot be undone. + Hide Sidebar COMMIT COMMIT & PUSH Template/History @@ -1009,6 +1059,8 @@ FILE CONFLICTS ARE RESOLVED USE MINE USE THEIRS + DISCARD ALL + Show Sidebar Filter Changes... INCLUDE UNTRACKED FILES NO RECENT INPUT MESSAGES diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index ac2e7d76fe..e5a8c77e75 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -11,16 +11,11 @@ Agregar Archivo(s) Para Ignorar Patrón: Almacenar Archivo: - Agregar Worktree + Agregar Árbol de trabajo Ubicación: - Ruta para este worktree. Se admite ruta relativa. + Ruta para este árbol de trabajo. Se admite ruta relativa. Nombre de la Rama: Opcional. Por defecto es el nombre de la carpeta de destino. - Rama de Seguimiento: - Seguimiento de rama remota - Qué Checkout: - Crear Nueva Rama - Rama Existente Asistente OpenAI Modelo RE-GENERAR @@ -30,7 +25,7 @@ Ocultar Otros Mostrar Todo Merge a 3 vías (3-Way) - Seleccionar archivo .patch para aplicar + Seleccionar archivo parche para aplicar Ignorar cambios de espacios en blanco Fuente: Archivo @@ -52,7 +47,6 @@ NO HAY ARCHIVOS ASUMIDOS COMO SIN CAMBIOS Cargar Imagen... Refrescar - ¡ARCHIVO BINARIO NO SOPORTADO! Bisect Abortar Malo @@ -65,8 +59,10 @@ Blame Blame sobre la Revisión Previa Ignorar cambios de espacios en blanco + Mantener presionado Ctrl (en Windows/Linux) o ⌘ (en macOS) cuando se hace click en el SHA del commit reconstruirá lo datos del blame basados en esa revisión. ¡BLAME EN ESTE ARCHIVO NO SOPORTADO! - Checkout ${0}$... + Checkout a ${0}$... + Checkout a ${0}$ como árbol de trabajo... Comparar las 2 ramas seleccionadas Comparar con... Comparar con HEAD @@ -90,17 +86,18 @@ Rebase ${0}$ en ${1}$... Renombrar ${0}$... Resetear ${0}$ a ${1}$... - Cambiar a ${0}$ (worktree) + Cambiar a ${0}$ (árbol de trabajo) Establecer Rama de Seguimiento... {0} commit(s) adelante {0} commit(s) adelante, {1} commit(s) detrás {0} commit(s) detrás Inválido + PUSH URL REMOTO ESTADO SEGUIMIENTO URL - WORKTREE + ÁRBOL DE TRABAJO CANCELAR Resetear a Revisión Padre Resetear a Esta Revisión @@ -122,11 +119,19 @@ Los siguientes submódulos necesitan ser actualizados:{0} ¿Quieres actualizarlos? Checkout y Fast-Forward Fast-Forward a: + Checkout a Rama como Árbol de trabajo + Rama Local: + Nombre de la Nueva Rama: + Opcional. Por defecto es el nombre de la carpeta de destino. + Rama Remota: + Rastrear esta rama remota. + Ruta del Árbol de trabajo: + Ruta para este árbol de trabajo. Se puede usar una ruta relativa. Checkout a la Rama desde el Stash Nueva Rama: Stash: Hacer checkout a Commit o Etiqueta - Objetivo: + Destino: Advertencia: Después de hacer esto, se deprenderá/separará de HEAD Cherry Pick Añadir fuente al mensaje de commit @@ -156,7 +161,7 @@ Cherry-Pick Este Commit Cherry-Pick ... Comparar con HEAD - Comparar con Worktree + Comparar con Árbol de trabajo Autor Fecha/Hora del Autor Mensaje @@ -186,6 +191,7 @@ Archivo LFS Buscar Archivos... Submódulo + SELECCIONAR ARCHIVO PARA VER SU CONTENIDO INFORMACIÓN AUTOR COMMITTER @@ -202,7 +208,6 @@ SHA Firmante: Abrir en Navegador - COL Ingresa el mensaje de commit. ¡Por favor usa una línea en blanco para separar el título y la descripción! ASUNTO Comparar @@ -338,6 +343,7 @@ ligera Mantenga Ctrl para iniciar directamente Cortar + Tablero Descartar No Hacer Nada Stash y Volver a aplicar @@ -345,10 +351,10 @@ Forzar desinicialización incluso si contiene cambios locales. Submódulo: Eliminar Rama - ¿Quieres que también se elimine la siguiente rama remota? Rama: Forzar la eliminación incluso si contiene commit(s) sin fusionar ¡Estás a punto de eliminar una rama remota! + También eliminar la rama remota ${0}$ Eliminar Múltiples Ramas Estás intentando eliminar múltiples ramas a la vez. ¡Asegúrate de comprobar dos veces antes de realizar esta acción! Eliminar Múltiples Etiquetas @@ -368,6 +374,7 @@ Etiqueta: Eliminar de los repositorios remotos DIFERENCIA BINARIA + VER CONTENIDO NUEVO ARCHIVO VACÍO Primera Diferencia Ignorar Cambio de Espacios en Blanco @@ -424,6 +431,9 @@ Fetch sin etiquetas Remoto: Fetch Cambios Remotos + ARCHIVO BINARIO + VISOR HEXADECIMAL + Ir a la dirección... Asumir como sin cambios Acción Personalizada Descartar... @@ -508,7 +518,7 @@ Seguir archivos llamados '{0}' Seguir todos los archivos *{0} Seleccionar Commit - Historias + Opciones Avanzadas AUTOR HORA DEL AUTOR FECHA DE COMMIT @@ -519,6 +529,7 @@ Solo la Rama Actual Rama Actual y los Commits Seleccionados Solo los Commits Seleccionados + Commits seleccionados (Solamente el primer padre) {0} COMMITS SELECCIONADOS MOSTRAR COLUMNAS Mantén 'Ctrl' o 'Shift' para seleccionar múltiples commits. @@ -545,17 +556,16 @@ Stage todos los cambios y commit Crear nueva rama Fetch, empieza directamente - Modo Dashboard (Por Defecto) Ir al hijo del commit seleccionado Ir al padre del commit seleccionado Abrir paleta de comandos - Modo de búsqueda de commits Pull, empieza directamente Push, empieza directamente Forzar a recargar este repositorio - Expandir/Colapsar el panel de detalles en el `HISTORIAL`/`HISTORY` + Expandir/Colapsar panel detallado del commit + Alternar panel de búsqueda del commit Cambiar a 'Cambios' - Cambiar a 'Historias' + Cambiar a 'Tablero' Cambiar a 'Stashes' Descartar Stage @@ -577,7 +587,7 @@ Saltar el hook de pre-rebase En: Arrastrar y soltar para reordenar commits - Rama Objetivo: + Rama Destino: Copiar Enlace Abrir en el Navegador Comandos @@ -627,6 +637,8 @@ Abrir Editor por defecto (Sistema) Abrir Directorio de Datos de la App + Cache + Configuración Abrir Archivo Abrir en Herramienta de Merge Abrir Repositorio Local @@ -658,10 +670,12 @@ OPEN AI Prompt adicional (Usa `-` para listar tus requerimientos) Clave API + Encabezados adicionales (un `Nombre: Valor` por línea) Modelo Traer automáticamente los model-ids disponibles Nombre El valor ingresado es el nombre de la clave API a cargar desde ENV + Nivel de Esfuerzo de Razonamiento Servidor APARIENCIA Fuente por defecto @@ -728,8 +742,8 @@ Shell/Terminal Podar Remoto Destino: - Podar Worktrees - Podar información de worktree en `$GIT_COMMON_DIR/worktrees` + Podar Árboles de trabajo + Podar información del árbol de trabajo en `$GIT_COMMON_DIR/worktrees` Pull Rama Remota: En: @@ -742,6 +756,7 @@ Forzar push Rama Local: NUEVO + Saltar el hook de pre-push Remoto: Revisión: Push Revisión al Remoto @@ -768,6 +783,7 @@ Editar Remoto Nombre: Nombre remoto + Resetear la "push URL" a la URL de este repositorio URL del Repositorio: URL del repositorio git remoto Copiar URL @@ -778,7 +794,8 @@ Fetch Abrir En Navegador Podar (Prune) - Confirmar para Eliminar Worktree + Establecer Push URL... + Confirmar para Eliminar Árbol de trabajo Utilizar Opción `--force` Destino: Renombrar Rama @@ -798,7 +815,6 @@ CONTINUAR Acciones Personalizadas No hay ninguna Acción Personalizada - Dashboard Descartar todos los cambios Abrir en el Explorador Buscar Ramas/Etiquetas/Submódulos @@ -816,7 +832,6 @@ Fecha de Commit Topológicamente RAMAS LOCALES - Más opciones... Navegar a HEAD Crear Rama LIMPIAR NOTIFICACIONES @@ -833,6 +848,8 @@ Ruta SHA Rama Actual + BUSCAR POR + Buscar Commits Sólo los commits decorados Mostrar sólo el primer padre MOSTRAR FLAGS @@ -852,8 +869,8 @@ Abrir en Terminal Ver Logs Visitar '{0}' en el Navegador - WORKTREES - AÑADIR WORKTREE + ÁRBOLES DE TRABAJO + AÑADIR ÁRBOL DE TRABAJO PRUNE URL del Repositorio Git Resetear Rama Actual a Revisión @@ -883,6 +900,10 @@ Fecha de Release de la Nueva Versión: Actualización de Software Actualmente no hay actualizaciones disponibles. + Establecer Push URL para el Remoto + Push URL: + Remoto: + URL: Establecer Rama del Submódulo Submódulo: Actual: @@ -896,6 +917,22 @@ Ir a Clave Privada SSH: Ruta de almacenamiento de la clave privada SSH + Abrir el Asistente para Claves SSH + ¿De verdad deseas borrar las siguientes claves? + Fingerprint + Generar Clave SSH + Confirmar Contraseña: + Email: + Requerido. + Nombre de la Clave: + Requerido. Por ejemplo: id_github + Contraseña: + GENERAR + Tipo: + Usar contraseña para la clave privada + Ruta + Clave Pública + Asistente para Claves SSH INICIAR Stash Incluir archivos sin rastrear @@ -917,9 +954,15 @@ CAMBIOS STASHES Estadísticas + COMMITS GENERAL MES SEMANA + SourceGit solamente obtendrá un máximo de 200,000 commits de acuerdo a la rama destino para su análisis. + AUTORES DESTACADOS + COMMITS + PORCENTAJE + Autores con menos del 1% de commits serán agrupados bajo Otros. AUTORES: COMMITS: SUBMÓDULOS @@ -962,6 +1005,12 @@ Eliminar {0} etiquetas seleccionadas... Hacer merge de ${0}$ a ${1}$... Hacer push a ${0}$... + FINALIZAR + Repositorio No confiable + ¿Quieres correr el comando `git config --global --add safe.directory {0}` para confiar en está ruta? + Git rechazó el acceso a este repositorio: + Ruta: + {0} repositorios fueron omitidos porque git no confía en ellos. Ábrelos manualmente por favor y confirma si confías en ellos. Actualizar Submódulos Todos los submódulos Inicializar según sea necesario @@ -998,6 +1047,7 @@ Puedes hacer stage a este archivo ahora. Limpiar Historial ¿Estás seguro de querer limpiar todo el historial de los mensajes de commit? Esta acción no se puede deshacer. + Ocultar Barra lateral COMMIT COMMIT y PUSH Plantilla/Historias @@ -1013,6 +1063,9 @@ LOS CONFLICTOS DE ARCHIVOS ESTÁN RESUELTOS USAR MÍOS USAR SUYOS + DESCARTAR TODO + Mostrar Barra lateral + Cambios de Filtro... INCLUIR ARCHIVOS SIN RASTREAR NO HAY MENSAJES DE ENTRADA RECIENTES NO HAY PLANTILLAS DE COMMIT @@ -1029,7 +1082,7 @@ Plantilla: ${0}$ ESPACIO DE TRABAJO: Configura Espacios de Trabajo... - WORKTREE + ÁRBOL DE TRABAJO RAMA Copiar Ruta HEAD diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index a094fb2ae6..e6b46a1e27 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -16,11 +16,6 @@ Chemin vers ce worktree. Relatif supporté. Nom de branche: Optionnel. Nom du dossier de destination par défaut. - Suivre la branche : - Suivi de la branche distante - Que récupérer : - Créer une nouvelle branche - Branche existante Assistant IA Modèle RE-GÉNÉRER @@ -30,7 +25,7 @@ Masquer les autres Tout Afficher Fusion à 3 voies - Selectionner le fichier .patch à appliquer + Selectionner le fichier patch à appliquer Ignorer les changements d'espaces blancs Appliquer le patch Espaces blancs : @@ -49,7 +44,6 @@ PAS DE FICHIERS PRÉSUMÉS INCHANGÉS Charger l'Image... Rafraîchir - FICHIER BINAIRE NON SUPPORTÉ !!! Bisect Annuler Mauvais @@ -192,7 +186,6 @@ SHA Signataire : Ouvrir dans le navigateur - COL Saisissez le message de commit. Utilisez une ligne vide pour séparer le sujet et la description ! SUJET Comparer @@ -335,6 +328,7 @@ Supprimer la branche Branche : Vous êtes sur le point de supprimer une branche distante !!! + Supprimer également la branche distante ${0}$ Supprimer plusieurs branches Vous essayez de supprimer plusieurs branches à la fois. Assurez-vous de revérifier avant de procéder ! Supprimer plusieurs tags @@ -480,7 +474,6 @@ Suivre les fichiers appelés '{0}' Suivre tous les fichiers *{0} Sélectionner un commit - Historique AUTEUR HEURE DE L'AUTEUR HEURE DE COMMIT @@ -517,17 +510,13 @@ Ajouter tous les changements et commit Créer une nouvelle branche Fetch, démarre directement - Mode tableau de bord (Défaut) Aller au commit enfant Aller au commit parent Ouvrir la palette de commandes - Recherche de commit Pull, démarre directement Push, démarre directement Forcer le rechargement du dépôt - Afficher/Masquer le panneau de détails du panneau`HISTORIQUE` Basculer vers 'Changements' - Basculer vers 'Historique' Basculer vers 'Stashes' Rejeter Indexer @@ -708,6 +697,7 @@ Poussage forcé Branche locale : NOUVEAU + Contourner le hook de pre-push Dépôt distant : Révision : Pousser la Révision vers le Dépôt Distant @@ -760,7 +750,6 @@ CONTINUER Actions personnalisées Pas d'actions personnalisées - Tableau de bord Rejeter tous les changements Ouvrir dans l'explorateur de fichiers Rechercher Branches/Tags/Submodules @@ -775,7 +764,6 @@ Date du commit Topologiquement BRANCHES LOCALES - Plus d'options... Naviguer vers le HEAD Créer une branche EFFACER LES NOTIFICATIONS diff --git a/src/Resources/Locales/he_IL.axaml b/src/Resources/Locales/he_IL.axaml index 64346545d6..98426b3023 100644 --- a/src/Resources/Locales/he_IL.axaml +++ b/src/Resources/Locales/he_IL.axaml @@ -16,11 +16,6 @@ נתיב ל־worktree זה. נתיב יחסי נתמך. שם Branch: לא חובה. ברירת המחדל היא שם תיקיית היעד. - Branch למעקב: - מעקב אחר branch מרוחק - מה לבצע לו checkout: - צור Branch חדש - Branch קיים עוזר AI מודל צור מחדש @@ -30,7 +25,7 @@ הסתר אחרים הצג הכול 3-Way Merge - בחר קובץ .patch להחלה + בחר קובץ patch להחלה התעלמות משינויי whitespace החל Patch Whitespace: @@ -49,7 +44,6 @@ אין קבצים בהנחת assume-unchanged טען תמונה... רענן - קובץ בינארי אינו נתמך!!! Bisect ביטול פגום (Bad) @@ -192,7 +186,6 @@ SHA חותם: פתח בדפדפן - עמודה הזן הודעת commit. יש להפריד בין subject לתיאור באמצעות שורה ריקה! SUBJECT השווה @@ -335,6 +328,7 @@ מחק Branch Branch: פעולה זו תמחק branch מרוחק!!! + למחוק גם את ה־branch המרוחק ${0}$ מחק מספר Branches פעולה זו תמחק מספר branches בו זמנית. בדוק היטב לפני הביצוע! מחק מספר Tags @@ -480,7 +474,6 @@ מעקב אחר קבצים בשם '{0}' מעקב אחר כל הקבצים *{0} בחר Commit - היסטוריה AUTHOR זמן AUTHOR זמן COMMIT @@ -517,17 +510,13 @@ Stage לכל השינויים ו־commit צור branch חדש Fetch, מתחיל מיידית - מצב Dashboard (ברירת מחדל) עבור לצאצא של ה־commit שנבחר עבור להורה של ה־commit שנבחר פתח command palette - מצב חיפוש commits Pull, מתחיל מיידית Push, מתחיל מיידית טען את המאגר מחדש בכוח - הרחבה/כיווץ של חלונית הפרטים ב־`HISTORY` עבור ל־'LOCAL CHANGES' - עבור ל־'HISTORY' עבור ל־'STASHES' השלך Stage @@ -708,6 +697,7 @@ Force push Branch מקומי: חדש + דלג על pre-push hook Remote: Revision: Push של Revision ל־Remote @@ -760,7 +750,6 @@ המשך פעולות מותאמות אישית אין פעולות מותאמות אישית - Dashboard השלך כל השינויים פתח בסייר הקבצים חיפוש Branches/Tags/Submodules @@ -775,7 +764,6 @@ תאריך Commit טופולוגי BRANCHES מקומיים - אפשרויות נוספות... ניווט אל HEAD צור Branch נקה התראות diff --git a/src/Resources/Locales/id_ID.axaml b/src/Resources/Locales/id_ID.axaml index 383ae256d9..a50be71a66 100644 --- a/src/Resources/Locales/id_ID.axaml +++ b/src/Resources/Locales/id_ID.axaml @@ -16,11 +16,6 @@ Jalur untuk worktree ini. Jalur relatif didukung. Nama Branch: Opsional. Standar adalah nama folder tujuan. - Track Branch: - Track remote branch - Yang Akan Di-Checkout: - Buat Branch Baru - Branch Yang Ada Asisten AI Model BUAT ULANG @@ -30,7 +25,7 @@ Sembunyikan Lainnya Tampilkan Semua Merge 3 Arah - Pilih berkas .patch untuk diterapkan + Pilih berkas patch untuk diterapkan Abaikan perubahan whitespace Sumber: Berkas @@ -52,7 +47,6 @@ TIDAK ADA BERKAS YANG DIASUMSIKAN TIDAK BERUBAH Muat Gambar... Segarkan - BERKAS BINARY TIDAK DIDUKUNG!!! Bisect Batalkan Buruk @@ -202,7 +196,6 @@ SHA Penanda Tangan: Buka di Browser - KOL Masukkan pesan commit. Gunakan baris kosong untuk memisahkan subjek dan deskripsi! SUBJEK Perbandingan @@ -345,10 +338,10 @@ Paksa de-init meski mengandung perubahan lokal. Submodule: Hapus Branch - Apakah Anda juga ingin menghapus remote branch berikut? Branch: Paksa hapus meskipun berisi commit yang belum di-merge Anda akan menghapus remote branch!!! + Juga hapus remote branch ${0}$ Hapus Beberapa Branch Anda akan menghapus beberapa branch sekaligus. Pastikan untuk memeriksa ulang sebelum bertindak! Hapus Beberapa Tag @@ -508,7 +501,6 @@ Track berkas bernama '{0}' Track semua berkas *{0} Pilih Commit - RIWAYAT AUTHOR WAKTU AUTHOR WAKTU COMMIT @@ -545,17 +537,13 @@ Stage semua perubahan dan commit Buat branch baru Fetch, langsung dimulai - Mode dashboard (Default) Ke turunan commit yang dipilih Ke induk commit yang dipilih Buka palet perintah - Mode pencarian commit Pull, langsung dimulai Push, langsung dimulai Paksa muat ulang repositori ini - Bentangkan/Ciutkan panel detail di `RIWAYAT` Pindah ke 'Changes' - Pindah ke 'History' Pindah ke 'Stashes' Buang Stage @@ -658,6 +646,7 @@ AI Prompt Tambahan (Gunakan `-` untuk mencantumkan kebutuhan Anda) API Key + Header Tambahan (satu `Nama: Nilai` per baris) Model Ambil model-id yang tersedia secara otomatis Nama @@ -742,6 +731,7 @@ Paksa push Branch Lokal: BARU + Lewati hook pre-push Remote: Revisi: Push Revisi ke Remote @@ -798,7 +788,6 @@ LANJUTKAN Aksi Kustom Tidak Ada Aksi Kustom - Dashboard Buang semua perubahan Buka di File Browser Cari Branch/Tag/Submodule @@ -816,7 +805,6 @@ Tanggal Commit Topologis BRANCH LOKAL - Opsi lainnya... Navigasi ke HEAD Buat Branch BERSIHKAN NOTIFIKASI diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index 46b2fa9a51..3ce0bae2c9 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -16,18 +16,13 @@ Percorso per questo worktree. Supportato il percorso relativo. Nome Branch: Facoltativo. Predefinito è il nome della cartella di destinazione. - Traccia Branch: - Traccia branch remoto - Di cosa fare il checkout: - Crea nuovo branch - Branch esistente Assistente AI Modello RIGENERA Usa AI per generare il messaggio di commit Nascondi SourceGit Mostra Tutto - Seleziona file .patch da applicare + Seleziona file patch da applicare Ignora modifiche agli spazi Applica Patch Spazi: @@ -46,7 +41,6 @@ NESSUN FILE ASSUNTO COME INVARIATO Carico l'Immagine... Aggiorna - FILE BINARIO NON SUPPORTATO!!! Biseca Annulla Cattiva @@ -310,6 +304,7 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Elimina Branch Branch: Stai per eliminare un branch remoto!!! + Elimina anche il branch remoto ${0}$ Elimina Branch Multipli Stai per eliminare più branch contemporaneamente. Controlla attentamente prima di procedere! Elimina Tag Multipli @@ -452,7 +447,6 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Remoto: Traccia file con nome '{0}' Traccia tutti i file *{0} - STORICO AUTORE ORA AUTORE ORA COMMIT @@ -478,14 +472,11 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Committa e invia le modifiche in stage Fai lo stage di tutte le modifiche e committa Recupera, avvia direttamente - Modalità Dashboard (Predefinita) Apri tavolozza comandi - Modalità ricerca commit Scarica, avvia direttamente Invia, avvia direttamente Forza l'aggiornamento di questo repository Passa a 'Modifiche' - Passa a 'Storico' Passa a 'Stashes' Scarta Aggiungi in stage @@ -653,6 +644,7 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Forza l'invio Branch Locale: NUOVO + Ignora l'hook di pre-push Remoto: Revisione: Invia Revisione Al Remoto @@ -703,7 +695,6 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle CONTINUA Azioni Personalizzate Nessuna Azione Personalizzata - Dashboard Scarta tutte le modifiche Apri nell'Esplora File Cerca Branch/Tag/Sottomodulo @@ -718,7 +709,6 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Per data del commit Topologicamente BRANCH LOCALI - Altre opzioni... Vai a HEAD Crea Branch CANCELLA LE NOTIFICHE diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index 844e708740..ac9637eea9 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -16,11 +16,6 @@ この作業ツリーへのパス (相対パスも使用できます) ブランチの名前: 省略可能 - 既定では宛先のフォルダーと同じ名前が使用されます - 追跡するブランチ: - 追跡するリモートブランチを設定 - チェックアウトする内容: - 新しいブランチを作成 - 既存のブランチ AI アシスタント モデル 再生成 @@ -30,7 +25,7 @@ 他を隠す すべて表示 3 分割マージ - 適用する .patch ファイルを選択 + 適用するパッチファイルを選択 空白文字の変更を無視 適用元: ファイル @@ -52,7 +47,6 @@ 未変更と見なしたファイルはありません 画像を選択... 再読み込み - バイナリファイルはサポートされていません!!! 問題の発生源を特定 中止 問題あり @@ -65,6 +59,7 @@ 著者の履歴 前のリビジョンの著者の履歴 空白文字の変更を無視 + Ctrl (Windows/Linux) または ⌘ (macOS) キーを押しながらコミットの SHA をクリックすると、そのリビジョンを基点に著者の履歴データを再構築します。 このファイルに著者の履歴は表示できません!!! ${0}$ をチェックアウト... 選択した 2 つのブランチを比較 @@ -96,6 +91,7 @@ ローカルに {0} コミット済み、リモートに {1} コミットあり リモートに {0} コミットあり 無効 + プッシュ先の URL リモート 状態 追跡対象 @@ -202,7 +198,6 @@ SHA 署名者: ブラウザーで開く - 列 コミットメッセージを入力してください。タイトルと説明は、空行で分けて記述してください! タイトル 比較 @@ -338,6 +333,7 @@ 軽量 Ctrl キーを押しながらで直接実行できます 切り取り + ダッシュボード 破棄 何もしない スタッシュして再適用 @@ -345,10 +341,10 @@ ローカルの変更の有無に関わらず、強制的に解除 サブモジュール: ブランチを削除 - 次のリモートブランチも削除しますか? ブランチ: 未マージのコミットがあっても強制的に削除 リモートブランチを削除しようとしています!!! + リモートブランチの ${0}$ も削除 複数のブランチを削除 複数のブランチをまとめて削除しようとしています。操作を行う前によく確認してください! 複数のタグを削除 @@ -368,6 +364,7 @@ タグ: リモートリポジトリからも削除 バイナリの差分 + 新しい内容を表示 空のファイル 最初の差分 空白文字の変更を無視 @@ -424,6 +421,9 @@ タグなしでフェッチ リモート: リモートの変更をフェッチ + バイナリファイル + バイナリビューアー + アドレスに移動... 未変更と見なす カスタムアクション 破棄... @@ -508,7 +508,7 @@ '{0}' という名前のファイルを追跡 *{0} ファイルをすべて追跡 コミットを選択 - 履歴 + 詳細オプション 著者 著者の日時 コミット日時 @@ -519,6 +519,7 @@ 現在のブランチのみ 現在のブランチ & 選択したコミット 選択したコミットのみ + 選択したコミットかつ最初の親コミットのみ {0} コミットを選択しました 表示する列 'Ctrl' または 'Shift' キーを押しながらで、複数のコミットを選択できます。 @@ -529,7 +530,7 @@ リビジョンを比較 キーボードショートカットを確認 総合 - 新しいリポジトリをクローン + リモートリポジトリをクローン 現在のタブを閉じる 次のタブに移動 前のタブに移動 @@ -545,17 +546,16 @@ すべての変更をステージに上げてコミット 新しいブランチを作成 フェッチ (直接実行) - ダッシュボードモード (既定) 選択したコミットの子コミットに移動 選択したコミットの親コミットに移動 コマンドパレットを開く - コミット検索モード プル (直接実行) プッシュ (直接実行) 現在のリポジトリを強制的に再読み込み - `履歴` の詳細パネルを広げる/折りたたむ + コミットの詳細パネルを広げる/折りたたむ + コミットの検索パネルを切り替え 'ローカルの変更' に切り替え - '履歴' に切り替え + 'ダッシュボード' に切り替え 'スタッシュ' に切り替え 破棄 ステージに上げる @@ -583,6 +583,7 @@ コマンド エラー 通知 + 新しいバージョンが利用できます リポジトリを開く タブ ワークスペース @@ -626,6 +627,8 @@ 開く 既定のエディター (システム) データの保管ディレクトリを開く + キャッシュ + 設定 ファイルを開く 外部のマージツールで開く ローカルリポジトリを開く @@ -657,10 +660,12 @@ AI 追加のプロンプト (`-` で必要事項を羅列してください) API キー + 追加のヘッダー (`名前: 値` を一行ごとに指定してください) モデル 利用できるモデル ID を自動的に取得 名前 この値を環境変数の名前とし、そこから API キーを読み込む + 思考努力レベル サーバー 外観 既定のフォント @@ -741,6 +746,7 @@ 強制的にプッシュ ローカルブランチ: 新規 + プッシュ前のフックを実行しない リモート: リビジョン: リビジョンをリモートにプッシュ @@ -767,6 +773,7 @@ リモートを編集 名前: リモート名 + プッシュ先の URL を、このリポジトリの URL にリセット リポジトリの URL: リモートの Git リポジトリの URL URL をコピー @@ -777,6 +784,7 @@ フェッチ ブラウザーで開く 掃除 + プッシュ先の URL を設定... 作業ツリーの削除を確認 `--force` オプションを有効化 対象: @@ -797,14 +805,16 @@ 続行 カスタムアクション カスタムアクションがありません - ダッシュボード すべての変更を破棄 ファイルブラウザーで開く - ブランチ/タグ/サブモジュールを検索 + ブランチ/タグ/サブモジュールを絞り込み グラフでの可視性 + 絞り込み条件を折りたたむ 解除 コミットグラフから隠す + 絞り込み条件を広げる コミットグラフで絞り込む + 個の条件で絞り込んでいます レイアウト 水平 垂直 @@ -812,7 +822,6 @@ コミット日時 トポロジカル ローカルブランチ - その他のオプション... HEAD に移動 ブランチを作成 通知を消去 @@ -829,6 +838,8 @@ パス SHA 現在のブランチ + 検索対象 + コミットを検索 参照付きのコミットのみ 最初の親コミットのみ 表示フラグ @@ -879,6 +890,10 @@ 新しいバージョンのリリース日: ソフトウェアの更新 利用できる更新はありません。 + リモートにプッシュ先の URL を設定 + プッシュ先の URL: + リモート: + URL: サブモジュールのブランチを設定 サブモジュール: 現在: @@ -892,6 +907,22 @@ このコミットに移動 SSH の秘密鍵: SSH の秘密鍵が保管されているパス + SSH の鍵ツールを開く + これらの鍵を削除しますか? + フィンガープリント + SSH 鍵を生成 + パスフレーズを確認: + メールアドレス: + 必須 + 鍵の名前: + 必須 - 例: id_github + パスフレーズ: + 生成 + 種類: + 秘密鍵にパスフレーズを使用 + パス + 公開鍵 + SSH の鍵ツール 開始 スタッシュ 未追跡のファイルを含める @@ -916,6 +947,7 @@ 概要 月間 週間 + 対象のブランチの 200,000 コミットまでのみを分析して表示しています。 著者: コミット: サブモジュール @@ -958,6 +990,7 @@ 選択した {0} 個のタグを削除... ${0}$ を ${1}$ にマージ... ${0}$ をプッシュ... + 中止 サブモジュールを更新 すべてのサブモジュール 必要に応じて初期化 @@ -1009,6 +1042,8 @@ ファイルの衝突が解決されました 自分の変更を使用 相手の変更を使用 + すべて破棄 + 変更を絞り込み... 未追跡のファイルを含める 最近の入力メッセージはありません コミットテンプレートがありません diff --git a/src/Resources/Locales/ko_KR.axaml b/src/Resources/Locales/ko_KR.axaml index 5a1d7b053f..e0695553fb 100644 --- a/src/Resources/Locales/ko_KR.axaml +++ b/src/Resources/Locales/ko_KR.axaml @@ -16,11 +16,6 @@ 이 워크트리의 경로입니다. 상대 경로를 지원합니다. 브랜치 이름: 선택 사항. 기본값은 대상 폴더 이름입니다. - 추적할 브랜치: - 원격 브랜치 추적 - 체크아웃할 대상: - 새 브랜치 생성 - 기존 브랜치 AI 어시스턴트 모델 재생성 @@ -30,7 +25,7 @@ 다른 항목 숨기기 모두 보기 3방향 병합 - 적용할 .patch 파일을 선택하세요 + 적용할 패치 파일을 선택하세요 공백 변경 사항 무시 원본: 파일 @@ -52,7 +47,6 @@ 변경되지 않음으로 간주된 파일 없음 이미지 불러오기... 새로 고침 - 바이너리 파일은 지원되지 않습니다!!! 이진 탐색 중단 나쁨 @@ -197,7 +191,6 @@ SHA 서명자: 브라우저에서 열기 - 열 커밋 메시지를 입력하세요. 제목과 설명은 빈 줄로 구분하세요! 제목 비교 @@ -334,6 +327,7 @@ 브랜치 삭제 브랜치: 원격 브랜치를 삭제하려고 합니다!!! + 원격 브랜치 ${0}$도 함께 삭제 여러 브랜치 삭제 한 번에 여러 브랜치를 삭제하려고 합니다. 실행하기 전에 다시 한번 확인하세요! 여러 태그 삭제 @@ -479,7 +473,6 @@ '{0}' 이름의 파일 추적 모든 *{0} 파일 추적 커밋 선택 - 히스토리 작성자 작성 시간 커밋 시간 @@ -516,17 +509,13 @@ 모든 변경 사항 스테이징 후 커밋 브랜치 생성 Fetch (바로 시작) - 대시보드 모드 (기본) 선택한 커밋의 자식으로 이동 선택한 커밋의 부모로 이동 명령 팔레트 열기 - 커밋 검색 모드 열기 Pull (바로 시작) 푸시 (바로 시작) 이 저장소 강제 새로고침 - 히스토리에서 세부 정보 패널 펼치기/접기 '변경 사항'으로 전환 - '히스토리'로 전환 '스태시'로 전환 폐기 스테이지 @@ -712,6 +701,7 @@ 강제 푸시 로컬 브랜치: 신규 + pre-push 훅 건너뛰기 원격: 리비전: 리비전을 원격에 푸시 @@ -768,7 +758,6 @@ 계속 사용자 지정 작업 사용자 지정 작업 없음 - 대시보드 모든 변경 사항 폐기 파일 탐색기에서 열기 브랜치/태그/서브모듈 검색 @@ -783,7 +772,6 @@ 커밋 날짜 위상 정렬 로컬 브랜치 - 추가 옵션... HEAD로 이동 브랜치 생성 알림 지우기 diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 8155dc647b..5084f0180b 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -16,18 +16,13 @@ Caminho para este worktree. Caminho relativo é suportado. Nome do Branch: Opcional. O padrão é o nome da pasta de destino. - Rastrear Branch: - Rastreando branch remoto - O que Checar: - Criar Novo Branch - Branch Existente Assietente IA Modelo Gerar novamente Utilizar IA para gerar mensagem de commit Esconder SourceGit Mostrar Todos - Selecione o arquivo .patch para aplicar + Selecione o arquivo patch para aplicar Ignorar mudanças de espaço em branco Aplicar Patch Espaço em Branco: @@ -46,7 +41,6 @@ NENHUM ARQUIVO CONSIDERADO SEM ALTERAÇÕES Carregar imagem... Atualizar - ARQUIVO BINÁRIO NÃO SUPORTADO!!! Bisect Abortar Inválido @@ -215,6 +209,7 @@ Excluir Branch Branch: Você está prestes a excluir uma branch remota!!! + Também excluir branch remoto ${0}$ Excluir Múltiplos Branches Você está tentando excluir vários branches de uma vez. Certifique-se de verificar antes de agir! Excluir Remoto @@ -327,7 +322,6 @@ Remoto: Rastrear arquivos nomeados '{0}' Rastrear todos os arquivos *{0} - Históricos AUTOR DATA DO AUTOR HORA DO COMMIT @@ -349,13 +343,10 @@ Commitar e enviar mudanças preparadas Preparar todas as mudanças e commitar Buscar, imediatamente - Modo de Dashboard (Padrão) - Modo de busca de commits Puxar, imediatamente Enviar, imediatamente Forçar recarregamento deste repositório Alternar para 'Mudanças' - Alternar para 'Históricos' Alternar para 'Stashes' Descartar Preparar @@ -464,6 +455,7 @@ Certifica de que submodules foram enviadas Forçar push Branch Local: + Ignorar o hook de pre-push Remoto: Empurrar Alterações para o Remoto Branch Remoto: diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 525796412a..f9260cc4f9 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -16,11 +16,6 @@ Путь к рабочему каталогу (поддерживается относительный путь) Имя ветки: Имя целевого каталога по умолчанию (необязательно) - Отслеживание ветки: - Отслеживание внешней ветки - Переключиться на: - Создать новую ветку - Ветку из списка Помощник OpenAI Модель ПЕРЕСОЗДАТЬ @@ -30,7 +25,7 @@ Скрыть остальные Показать все Трехстороннее слияние - Выберите файл .patch для применения + Выберите файл заплатку для применения Игнорировать изменения пробелов Источник: Файл @@ -52,7 +47,6 @@ СПИСОК ПУСТ Загрузить картинку... Обновить - ДВОИЧНЫЙ ФАЙЛ НЕ ПОДДЕРЖИВАЕТСЯ!!! Раздвоить О Плохая @@ -65,8 +59,10 @@ Расследование Расследование на предыдущей редакции Игнорировать изменения пробелов + Если при нажатии на ревизию SHA удерживать нажатой Ctrl (на Windows/Linux) или ⌘ (на macOS), данные расследования будут перестроены на основе этой ревизии. РАССЛЕДОВАНИЕ В ЭТОМ ФАЙЛЕ НЕ ПОДДЕРЖИВАЕТСЯ!!! Переключиться на ${0}$... + Сделать ${0}$ рабочим деревом... Сравнить две выбранные ветки Сравнить с ... Сравнить с ГОЛОВОЙ @@ -96,6 +92,7 @@ {0} ревизий вперёд, {1} ревизий назад {0} ревизий назад Неверно + АДРЕС ВЫГРУЗКИ УДАЛЁННЫЙ СОСТОЯНИЕ ОТСЛЕЖИВАНИЕ @@ -122,6 +119,14 @@ Подмодулям требуется обновление:{0}Обновить их? Переключиться и перемотать Перемотать к: + Сделать ветку рабочим деревом + Локальная ветка: + Имя новой ветки: + По желанию. Используемое имя целевого каталога по умолчанию. + Удалённая ветка: + Отслеживать эту удалённую ветку. + Путь рабочего дерева: + Путь для данного рабочего дерева. Поддерживается относительный путь. Переключить ветку из отложенного Новая ветка: Отложенный: @@ -186,6 +191,7 @@ Файл LFS Поиск файлов... Подмодуль + ВЫБЕРИТЕ ФАЙЛ ДЛЯ ПРОСМОТРА СОДЕРЖИМОГО ИНФОРМАЦИЯ АВТОР РЕВИЗОР (ИСПОЛНИТЕЛЬ) @@ -202,7 +208,6 @@ SHA Подписант: Открыть в браузере - СТБ Введите сообщение ревизии. Пожалуйста, используйте пустые строки для разделения субъекта и описания! СУБЪЕКТ Сравнить @@ -221,14 +226,14 @@ ${files:N} Пути изменённых файлов, не более N ${pure_files} То же, что и ${files}, но только имена файлов ${pure_files:N} То же, что и ${files:N}, но только имена файлов - Cодержание: + Содержание: Название: ПОЛЬЗОВАТЕЛЬСКОЕ ДЕЙСТВИЕ Аргументы: Встроенные параметры: ${REPO} Путь репозитория - ${REMOTE} Выбранная удаённая ветка + ${REMOTE} Выбранная удалённая ветка ${BRANCH} Выбранная ветка, без ${REMOTE} удалённых веток ${BRANCH_FRIENDLY_NAME} Понятное имя выбранной ветки, содержащую ${REMOTE} удалённые ветки ${SHA} Хеш выбранной ревизии @@ -246,7 +251,7 @@ Удалённый Репозиторий Метка - Ждать для выполения выхода + Ждать для выполнения выхода Адрес электронной почты Адрес электронной почты GIT @@ -338,17 +343,18 @@ Простой Удерживайте Ctrl, чтобы сразу начать Вырезать + Обзор Отклонить Ничего не делать - Отложить и примненить повторно + Отложить и применить повторно Удалить подмодуль Принудительно удалить даже если содержит локальные изменения. Подмодуль: Удалить ветку - Вы хотите также удалить следующую удалённую ветку? Ветка: Принудительно удалить, даже если содержит неслитые ревизии Вы собираетесь удалить внешнюю ветку!!! + Также удалите внешнюю ветку ${0}$ Удаление нескольких веток Вы пытаетесь удалить несколько веток одновременно. Обязательно перепроверьте, прежде чем предпринимать какие-либо действия! Удалить несколько меток @@ -368,6 +374,7 @@ Метка: Удалить из внешнего репозитория СРАВНЕНИЕ БИНАРНИКОВ + ПРОСМОТР НОВОГО СОДЕРЖИМОГО ФАЙЛ ПУСТ Первое сравнение Игнорировать изменения пробелов @@ -388,7 +395,7 @@ ПОДМОДУЛЬ УДАЛЁН НОВЫЙ - + {0} Незафиксированных изменний + + {0} Незафиксированных изменений Обмен Подсветка синтаксиса Перенос слов в строке @@ -424,6 +431,9 @@ Извлечь без меток Внешний репозиторий: Извлечь внешние изменения + ДВОИЧНЫЙ ФАЙЛ + ПРОСМОТР В ШЕСТНАДЦАТЕРИЧНОМ ФОРМАТЕ + Перейти по адресу... Не отслеживать Пользовательское действие Отклонить... @@ -448,7 +458,7 @@ Новый режим файла: Обычный Подмодуль - Символическая сскла + Символическая ссылка Неизвестно Git-процесс Ветка разработчика: @@ -508,7 +518,7 @@ Отслеживать файлы с именем «{0}» Отслеживать все файлы (*{0}) Выбрать ревизию - Истории + Расширенные опции АВТОР ВРЕМЯ АВТОРА ВРЕМЯ РЕВИЗИИ @@ -519,6 +529,7 @@ Только текущая ветка Текущая ветка и выбранные ревизии Только выбранные ревизии + Выбранные ревизии (Только первый родитель) ВЫБРАНО РЕВИЗИЙ: {0} ПОКАЗЫВАТЬ КОЛОНКИ Удерживайте Ctrl или Shift, чтобы выбрать несколько ревизий. @@ -536,7 +547,7 @@ Создать новую вкладку Открыть локальный репозиторий Открыть диалоговое окно настроек - Показать рабочее пространство в выпадющем меню + Показать рабочее пространство в выпадающем меню Переключиться на вкладку Масштабирование РЕПОЗИТОРИЙ @@ -545,29 +556,28 @@ Сформировать все изменения и зафиксировать Создать новую ветку Извлечь (fetch), запускается сразу - Режим доски (по умолчанию) К дочернему выбранной ревизии К родительскому выбранной ревизии Открыть палитру команд - Режим поиска ревизий Загрузить (pull), запускается сразу Выложить (push), запускается сразу Принудительно перечитать репозиторий - Развернуть/Свернуть панель подробностей в «ИСТОРИИ» + Развернуть/Свернуть панель подробностей ревизии + Переключить панель поиска ревизий Переключить на «Изменения» - Переключить на «Историю» + Переключить на «Обзор» Переключить на «Отложенные» Отклонить Сформировать Расформировать Создать репозиторий - Вы действительно хотите запуститб команду «git init» по этому пути? + Вы действительно хотите запустить команду «git init» по этому пути? Не удалось открыть репозиторий. Причина: Путь: Выполняется частичный перенос ревизий (cherry-pick). - Обрабтка ревизии. + Обработка ревизии. Выполняется слияние. - Выполяется. + Выполняется. Выполняется перенос. Остановлен на Выполняется отмена ревизии. @@ -627,6 +637,8 @@ Открыть Редактор по умолчанию (Системный) Открыть каталог данных программы + Кеш + Конфигурация Открыть файл Открыть в инструменте слияния Открыть локальный репозиторий @@ -655,13 +667,15 @@ {0} лет назад Вчера Параметры - ОТКРЫТЬ ИИ + ИИ Дополнительная подсказка (Для перечисления ваших требований используйте `-`) Ключ API + Дополнительные заголовки (по одному «Имя: Значение» на строку) Модель Автоматическое получение доступных идентификаторов моделей Имя: Введённое значение — это имя для загрузки API-ключа из ENV + Уровень обоснованности усилий Сервер ВИД Шрифт по умолчанию @@ -669,7 +683,7 @@ Размер шрифта По умолчанию Редактор - Моноширный шрифт + Моноширинный шрифт Тема Переопределение темы Автоматически скрывать прокрутку @@ -700,8 +714,8 @@ GIT Включить автозавершение CRLF Каталог клонирования по умолчанию - Электроная почта пользователя - Общая электроная почта пользователя git + Электронная почта пользователя + Общая электронная почта пользователя git Разрешить (--prune) при скачивании Разрешить (--ignore-cr-at-eol) в сравнении Для работы программы требуется версия Git (>= 2.25.1) @@ -742,9 +756,10 @@ Принудительно выложить Локальная ветка: НОВЫЙ + Обходной зацеп предварительного выкладывания Внешний репозиторий: Ревизия: - Выложить ревизию на удалёную + Выложить ревизию на удалённую Выложить изменения на внешний репозиторий Ветка внешнего репозитория: Отслеживать ветку @@ -754,7 +769,7 @@ Внешний репозиторий: Метка: Отправить к НОВОЙ ветке - Введитте имя для новой удалённой ветки: + Введите имя для новой удалённой ветки: Выйти Перемещение текущей ветки Отложить и применить повторно локальные изменения @@ -768,6 +783,7 @@ Редактировать внешний репозиторий Имя: Имя внешнего репозитория + Установить адрес выгрузки равным адресу этого репозитория Адрес: Адрес внешнего репозитория git Копировать адрес @@ -778,6 +794,7 @@ Извлечь Открыть в браузере Удалить + Установить адрес выгрузки... Подтвердить удаление рабочего каталога Включить опцию (--force) Цель: @@ -798,7 +815,6 @@ ПРОДОЛЖИТЬ Изменить действия Не изменять действия - Панель Отклонить все изменения. Открыть в файловом менеджере Поиск веток, меток и подмодулей @@ -816,7 +832,6 @@ Дата ревизии Топологически ЛОКАЛЬНЫЕ ВЕТКИ - Больше опций... Навигация по ГОЛОВЕ (HEAD) Создать ветку ОЧИСТКА УВЕДОМЛЕНИЙ @@ -833,14 +848,16 @@ Путь SHA Текущая ветка + ИСКАТЬ В + Поиск ревизий Только оформленные ревизии Показывать только первый родительский ПОКАЗЫВАТЬ ФЛАГИ Показывать потерянные ревизии Показывать подмодули как дерево - Показывать метки как катлог + Показывать метки как каталог ПРОПУСТИТЬ - Статистикa + Статистика ПОДМОДУЛИ ДОБАВИТЬ ПОДМОДУЛЬ ОБНОВИТЬ ПОДМОДУЛЬ @@ -883,6 +900,10 @@ Дата выпуска новой версии: Обновление ПО Сейчас нет обновлений. + Установить адрес выгрузки на удалённый + Адрес выгрузки: + Удалённый: + Адрес: Установить ветку подмодуля Подмодуль: Текущий: @@ -895,7 +916,23 @@ Копировать SHA Перейти Приватный ключ SSH: - Путь хранения приватного ключа SSH + Путь хранения частного SSH-ключа + Открыть помощника SSH-ключей + Вы действительно хотите удалить следующие ключи? + Отпечаток + Создать SSH-ключ + Подтвердить парольную фразу: + Почта: + Требуется. + Имя ключа: + Требуется. К примеру: id_github + Парольная фраза: + СОЗДАТЬ + Тип: + Использовать парольную фразу для частного ключа + Путь + Открытый ключ + Помощник SSH-ключа ЗАПУСК Отложить Включить неотслеживаемые файлы @@ -917,9 +954,15 @@ ИЗМЕНЕНИЯ ОТЛОЖЕННЫЕ Статистика + РЕВИЗИИ ОБЗОР МЕСЯЦ НЕДЕЛЯ + SourceGit проанализирует не более 200 000 ревизий в целевой ветке. + ТОП АВТОРОВ + РЕВИЗИИ + ПРОЦЕНТ + Авторы с долей менее 1% ревизий объединены в группу «Другие». АВТОРЫ: РЕВИЗИИ: ПОДМОДУЛИ @@ -962,6 +1005,12 @@ Удалить выбранные метки ({0})... Влить ${0}$ в ${1}$... Выложить ${0}$... + ПРЕКРАТИТЬ + Недоверенный репозиторий + Вы хотите выполнить команду (git config --global --add safe.directory {0}), чтобы разрешить доступ к этому пути? + Отказ гита в доступе к этому репозиторию: + Путь: + Пропущено репозиториев из-за недоверия к ним гитом: {0}. Пожалуйста, откройте их вручную и проверьте, можно ли им доверять. Обновление подмодулей Все подмодули Создавать по необходимости @@ -998,6 +1047,7 @@ Теперь вы можете сформировать этот файл. Очистить историю Вы действительно хотите очистить всю историю сообщений ревизии? Данное действие нельзя отменить. + Скрыть боковую панель ЗАФИКСИРОВАТЬ ЗАФИКСИРОВАТЬ и ОТПРАВИТЬ Шаблон/Истории @@ -1013,6 +1063,8 @@ КОНФЛИКТЫ ФАЙЛОВ РАЗРЕШЕНЫ ИСПОЛЬЗОВАТЬ МОИ ИСПОЛЬЗОВАТЬ ИХ + ОТКЛОНИТЬ ВСЕ + Показать боковую панель Фильтр изменений... ВКЛЮЧИТЬ НЕОТСЛЕЖИВАЕМЫЕ ФАЙЛЫ НЕТ ПОСЛЕДНИХ ВХОДНЫХ СООБЩЕНИЙ diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index 5cbea045bb..3473ff1cab 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -11,11 +11,6 @@ இந்த பணிமரத்திற்கான பாதை. தொடர்புடைய பாதை ஆதரிக்கப்படுகிறது. கிளை பெயர்: விருப்பத்தேர்வு. இயல்புநிலை இலக்கு கோப்புறை பெயர். - கிளை கண்காணி: - தொலை கிளையைக் கண்காணித்தல் - என்ன சரிபார்க்க வேண்டும்: - புதிய கிளையை உருவாக்கு - ஏற்கனவே உள்ள கிளை செநு உதவியாளர் மாதிரி மறு-உருவாக்கு @@ -37,7 +32,6 @@ கோப்புகள் மாற்றப்படவில்லை எனக் கருதப்படுகிறது எந்த கோப்புகளும் மாற்றப்படவில்லை எனக் கருதப்படுகிறது புதுப்பி - இருமம் கோப்பு ஆதரிக்கப்படவில்லை!!! குற்றச்சாட்டு இந்த கோப்பில் குற்றம் சாட்ட ஆதரிக்கப்படவில்லை!!! ${0}$ சரிபார்... @@ -193,6 +187,7 @@ கிளையை நீக்கு கிளை: நீங்கள் ஒரு தொலை கிளையை நீக்கப் போகிறீர்கள்!!! + தொலை ${0}$ கிளையையும் நீக்கு பல கிளைகளை நீக்கு நீங்கள் ஒரே நேரத்தில் பல கிளைகளை நீக்க முயற்சிக்கிறீர்கள் நடவடிக்கை எடுப்பதற்கு முன் மீண்டும் சரிபார்! தொலையை நீக்கு @@ -312,7 +307,6 @@ தொலை: '{0}' என பெயரிடப்பட்ட கோப்புகளைக் கண்காணி அனைத்து *{0} கோப்புகளையும் கண்காணி - வரலாறு ஆசிரியர் ஆசிரியர் நேரம் உறுதிமொழி நேரம் @@ -335,13 +329,10 @@ நிலைபடுத்திய மாற்றங்களை உறுதிமொழி மற்றும் தள்ளு அனைத்து மாற்றங்களையும் நிலைபடுத்தி உறுதிமொழி எடு, நேரடியாகத் தொடங்குகிறது - முகப்பலகை பயன்முறை (இயல்புநிலை) - உறுதிமொழி தேடல் பயன்முறை இழு, நேரடியாகத் தொடங்குகிறது தள்ளு, நேரடியாகத் தொடங்குகிறது இந்த களஞ்சியத்தை மீண்டும் ஏற்ற கட்டாயப்படுத்து 'மாற்றங்கள்' என்பதற்கு மாறு - 'வரலாறுகள்' என்பதற்கு மாறு 'பதுகிவைத்தவை' என்பதற்கு மாறு நிராகரி நிலைபடுத்து @@ -463,6 +454,7 @@ துணைத் தொகுதிகள் தள்ளப்பட்டது என்பதை உறுதிசெய் கட்டாயமாக தள்ளு உள்ளக கிளை: + pre-push hook-ஐத் தவிர் தொலை: மாற்றங்களை தொலைக்கு தள்ளு தொலை கிளை: diff --git a/src/Resources/Locales/uk_UA.axaml b/src/Resources/Locales/uk_UA.axaml index 9cad040c7a..5f44193e39 100644 --- a/src/Resources/Locales/uk_UA.axaml +++ b/src/Resources/Locales/uk_UA.axaml @@ -11,11 +11,6 @@ Шлях для цього робочого дерева. Відносний шлях підтримується. Назва гілки: Необов'язково. За замовчуванням — назва кінцевої папки. - Відстежувати гілку: - Відстежувати віддалену гілку - Що перемкнути: - Створити нову гілку - Наявна гілка AI Асистент Модель ПЕРЕГЕНЕРУВАТИ @@ -37,7 +32,6 @@ ФАЙЛИ, ЩО ВВАЖАЮТЬСЯ НЕЗМІНЕНИМИ НЕМАЄ ФАЙЛІВ, ЩО ВВАЖАЮТЬСЯ НЕЗМІНЕНИМИ Оновити - БІНАРНИЙ ФАЙЛ НЕ ПІДТРИМУЄТЬСЯ!!! Автор рядка ПОШУК АВТОРА РЯДКА ДЛЯ ЦЬОГО ФАЙЛУ НЕ ПІДТРИМУЄТЬСЯ!!! Перейти на ${0}$... @@ -197,6 +191,7 @@ Видалити гілку Гілка: Ви збираєтеся видалити віддалену гілку!!! + Також видалити віддалену гілку ${0}$ Видалити кілька гілок Ви намагаєтеся видалити кілька гілок одночасно. Перевірте ще раз перед виконанням! Видалити віддалене сховище @@ -316,7 +311,6 @@ Віддалений: Відстежувати файли, названі '{0}' Відстежувати всі *{0} файли - ІСТОРІЯ АВТОР ЧАС АВТОРА ЧАС КОМІТУ @@ -339,13 +333,10 @@ Закомітити та надіслати проіндексовані зміни Індексувати всі зміни та закомітити Fetch, запускається без діалогу - Режим панелі керування (за замовчуванням) - Режим пошуку комітів Pull, запускається без діалогу Push, запускається без діалогу Примусово перезавантажити це сховище Перейти до 'Зміни' - Перейти до 'Історія' Перейти до 'Схованки' Скасувати Індексувати @@ -467,6 +458,7 @@ Переконатися, що підмодулі надіслано Примусовий push Локальна гілка: + Обхід pre-push hook Віддалене сховище: Надіслати зміни на віддалене сховище Віддалена гілка: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index d77cd54181..d24f14b1e8 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -16,11 +16,6 @@ 填写该工作树的路径。支持相对路径。 分支名 : 选填。默认使用目标文件夹名称。 - 跟踪分支 - 设置上游跟踪分支 - 检出分支方式 : - 创建新分支 - 已有分支 AI助手 模型 重新生成 @@ -52,7 +47,6 @@ 没有不跟踪更改的文件 加载本地图片 重新加载 - 二进制文件不支持该操作!!! 二分定位(bisect) 终止 标记错误 @@ -65,8 +59,10 @@ 逐行追溯(blame) 对当前版本的前一版本执行逐行追溯操作 忽略空白符变化 + 按住 Ctrl (Windows及Linux平台) 或 ⌘ (macOS平台) 键并点击提交哈希将基于所选的提交重新计算逐行追溯 选中文件不支持该操作!!! 检出(checkout) ${0}$... + 检出 ${0}$ 为新工作树... 比较选中的 2 个分支 与其他分支或标签比较... 与当前 HEAD 比较 @@ -96,6 +92,7 @@ 领先 {0} 个提交,落后 {1} 个提交 落后 {0} 个提交 不存在 + 推送地址 远程 状态 上游分支 @@ -122,6 +119,14 @@ 以下子模块需要更新:{0}是否立即更新? 检出分支并快进 上游分支 : + 检出分支为新的工作树 + 本地分支 : + 新分支名称 : + 选填。默认使用目标文件夹名称。 + 远程分支 : + 新分支跟踪此远程分支 + 工作树路径 : + 必填。可填写相对路径。 从所选贮藏检出分支 新分支 : 所选贮藏 : @@ -186,6 +191,7 @@ LFS文件 查找文件... 子模块 + 请选择查看的文件 基本信息 修改者 提交者 @@ -202,7 +208,6 @@ 提交指纹 签名者 : 浏览器中查看 - 列 请输入提交的信息。注意:主题与具体描述中间需要空白行分隔! 主题 比较 @@ -338,6 +343,7 @@ 轻量标签 按住Ctrl键点击将以默认参数运行 剪切 + 仪表板 丢弃更改 不做处理 贮藏并自动恢复 @@ -345,10 +351,10 @@ 强制取消,即使包含本地变更 子模块 : 删除分支确认 - 是否同时删除以下远程分支? 分支名 : 强制删除,即使包含未合并的提交 您正在删除远程上的分支,请务必小心!!! + 同时删除远程分支 ${0}$ 删除多个分支 您正在尝试一次性删除多个分支,请务必仔细检查后再执行操作! 删除多个标签 @@ -368,6 +374,7 @@ 标签名 : 同时删除远程仓库中的此标签 二进制文件 + 查看新内容 空文件 首个差异 忽略空白符号变化 @@ -424,6 +431,9 @@ 不拉取远程标签 远程仓库 : 拉取远程仓库内容 + 二进制文件 + 查看内容 + 定位地址... 不跟踪此文件的更改 自定义操作 放弃更改... @@ -508,7 +518,7 @@ 跟踪名为'{0}'的文件 跟踪所有 *{0} 文件 选择前往的提交 - 历史记录 + 高级设置 作者 修改时间 提交时间 @@ -519,6 +529,7 @@ 仅当前分支 当前分支和选中的提交 仅选中的提交 + 仅选中的提交 (First-parent 模式) 已选中 {0} 项提交 显示列 可以按住 Ctrl 或 Shift 键选择多个提交 @@ -545,17 +556,16 @@ 自动暂存全部变更并提交 新建分支 拉取 (fetch) 远程变更 - 切换左边栏为分支/标签等显示模式(默认) 前往选中提交的子提交 前往选中提交的父提交 打开快捷命令面板 - 切换左边栏为提交搜索模式 拉回 (pull) 远程变更 推送本地变更到远程 重新加载仓库状态 - 展开/折叠历史提交中的详情面板 + 展开/折叠提交的详情面板 + 打开/关闭搜索提交面板 显示本地更改 - 显示历史记录 + 显示仪表盘 显示贮藏列表 丢弃 暂存 @@ -627,6 +637,8 @@ 打开 系统默认编辑器 浏览应用数据目录 + 缓存数据 + 偏好设置数据 打开文件 使用外部对比工具查看 打开本地仓库 @@ -658,10 +670,12 @@ AI 附加提示词 (请使用 `-` 列出您的要求) API密钥 + 附加 HTTP 请求头部 (格式:每行一条 Name: Value) 模型 自动拉取可用模型列表 配置名称 从环境变量(填写环境变量名)中读取API密钥 + 推理努力等级 服务地址 外观配置 缺省字体 @@ -742,6 +756,7 @@ 启用强制推送 本地分支 : 新建 + 绕过 pre-push 钩子 远程仓库 : 修订 : 推送指定修订到远程仓库 @@ -768,6 +783,7 @@ 编辑远程仓库 远程名 : 唯一远程名 + 重置推送地址为相同地址 仓库地址 : 远程仓库的地址 复制远程地址 @@ -778,6 +794,7 @@ 拉取(fetch)更新 在浏览器中打开 清理远程已删除分支 + 设置独立推送地址... 移除工作树操作确认 启用`--force`选项 目标工作树 : @@ -798,7 +815,6 @@ 下一步 自定义操作 自定义操作未设置 - 主页 放弃所有更改 在文件浏览器中打开 快速查找分支/标签/子模块 @@ -816,7 +832,6 @@ 按提交时间 按拓扑排序 本地分支 - 更多选项... 定位HEAD 新建分支 清空通知列表 @@ -833,6 +848,8 @@ 路径 提交指纹 仅在当前分支中查找 + 搜索方式 + 查找提交 仅显示分支、标签所引用的提交 仅显示合并提交中的第一个提交 显示内容 @@ -883,6 +900,10 @@ 新版发布时间 : 软件更新 当前已是最新版本。 + 设置远程推送地址 + 推送地址 : + 远程 : + 拉取地址 : 修改子模块追踪分支 子模块 : 当前追踪分支 : @@ -896,6 +917,22 @@ 跳转到提交 SSH密钥 : SSH密钥文件 + 打开SSH密钥工具 + 您确定要删除以下SSH密钥吗? + SHA-256 指纹 + 生成SSH密钥 + 再次确认口令: + 邮箱: + 必填项 + 密钥文件名: + 必填项,如:id_github + 输入口令: + 生 成 + 密钥类型: + 为私钥文件设置口令 + 文件路径 + 公钥内容 + SSH密钥工具 开 始 贮藏(stash) 包含未跟踪的文件 @@ -917,9 +954,15 @@ 查看变更 贮藏列表 提交统计 + 提交统计图 总览 本月 本周 + SourceGit 仅会按所选分支最多取出 200000 条提交记录进行分析 + 主要贡献者 + 提交数量 + 百分比 + 提交占比小于1%的作者会被统计入Others分组 贡献者人数: 提交次数: 子模块 @@ -962,6 +1005,12 @@ 删除选中 {0} 个标签... 合并 ${0}$ 到 ${1}$... 推送 ${0}$... + 终止运行 + 不受信任的仓库 + 是否执行 `git config --global --add safe.directory {0}` 命令以信任该仓库? + Git 拒绝访问该仓库: + 路径 : + 有 {0} 个仓库因 git 的信任检查被跳过,请手动打开它们并确认是否信任。 更新子模块 更新所有子模块 如未初始化子模块,先初始化 @@ -998,6 +1047,7 @@ 现在您已可将其加入暂存区中 清空历史提交信息 您确定要清空所有的历史提交信息记录吗(执行操作后无法撤回)? + 隐藏左侧栏 提交 提交并推送 历史输入/模板 @@ -1013,6 +1063,8 @@ 文件冲突已解决 使用 MINE 使用 THEIRS + 放弃所有更改 + 显示左侧栏 查找变更... 显示未跟踪文件 没有提交信息记录 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index b07c829967..97680c6103 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -16,11 +16,6 @@ 填寫該工作樹的路徑。支援相對路徑。 分支名稱: 選填。預設使用目標資料夾名稱。 - 追蹤分支 - 設定遠端追蹤分支 - 簽出分支方式: - 建立新分支 - 已有分支 AI 助理 模型 重新產生 @@ -52,7 +47,6 @@ 沒有不追蹤變更的檔案 載入本機圖片... 重新載入 - 二進位檔案不支援該操作! 二分搜尋 (bisect) 中止 標記為錯誤 @@ -65,8 +59,10 @@ 逐行溯源 (blame) 對上一個版本執行逐行溯源 忽略空白符號變化 + 按住 Ctrl 鍵 (Windows/Linux) 或 ⌘ 鍵 (macOS) 並點擊提交編號,將依據該次提交重新執行逐行溯源 所選擇的檔案不支援該操作! 簽出 (checkout) ${0}$... + 簽出 (checkout) ${0}$ 為工作樹... 比較所選的 2 個分支 與其他分支或標籤比較... 與目前 HEAD 比較 @@ -96,6 +92,7 @@ 領先 {0} 次提交,落後 {1} 次提交 落後 {0} 次提交 無效 + 推送網址 遠端 狀態 上游分支 @@ -122,6 +119,14 @@ 以下子模組需要更新: {0},您要立即更新嗎? 簽出分支並快轉 上游分支: + 簽出分支為工作樹 + 本機分支: + 新分支名稱: + 選填。預設使用目標資料夾名稱。 + 遠端分支: + 追蹤此遠端分支 + 工作樹路徑: + 填寫該工作樹的路徑。支援相對路徑。 從所選擱置簽出分支 新分支: 所選擱置: @@ -180,12 +185,13 @@ 另存為修補檔 (patch)... 變更對比 個檔案已變更 - 搜尋變更... + 篩選變更... 收合詳細面板 檔案列表 LFS 檔案 搜尋檔案... 子模組 + 請選擇要檢視內容的檔案 基本資訊 作者 提交者 @@ -202,7 +208,6 @@ 提交編號 簽署人: 在瀏覽器中檢視 - 欄 請輸入提交訊息,標題與詳細描述之間請使用單行空白區隔。 標題 比較 @@ -338,6 +343,7 @@ 輕量標籤 按住 Ctrl 鍵將直接以預設參數執行 剪下 + 儀表板 捨棄變更 不做處理 擱置變更並自動復原 @@ -345,10 +351,10 @@ 強制取消,即使包含本機變更 子模組: 刪除分支確認 - 您是否也想刪除以下遠端分支? 分支名稱: 強制刪除,即使包含未合併的提交 您正在刪除遠端上的分支,請務必小心! + 同時刪除遠端分支 ${0}$ 刪除多個分支 您正在嘗試一次性刪除多個分支,請務必仔細檢查後再刪除! 刪除多個標籤 @@ -368,6 +374,7 @@ 標籤名稱: 同時刪除遠端存放庫中的此標籤 二進位檔案 + 檢視新內容 空檔案 第一個差異 忽略空白符號變化 @@ -424,6 +431,9 @@ 不提取遠端標籤 遠端存放庫: 提取遠端存放庫內容 + 二進位檔案 + 檢視內容 + 前往地址... 不追蹤此檔案的變更 自訂動作 捨棄變更... @@ -508,7 +518,7 @@ 追蹤名稱為「{0}」的檔案 追蹤所有 *{0} 檔案 選取要前往的提交 - 歷史記錄 + 進階選項 作者 修改時間 提交時間 @@ -519,6 +529,7 @@ 僅目前分支 目前分支和選取的提交 僅選取的提交 + 僅選取的提交 (First-parent 模式) 已選取 {0} 項提交 顯示欄位 可以按住 Ctrl 或 Shift 鍵選擇多個提交 @@ -545,18 +556,17 @@ 自動暫存全部變更並提交 新增分支 提取 (fetch) 遠端的變更 - 切換左邊欄為分支/標籤等顯示模式 (預設) 前往所選提交的子提交 前往所選提交的父提交 開啟命令面板 - 切換左邊欄為歷史搜尋模式 拉取 (pull) 遠端的變更 推送 (push) 本機變更到遠端存放庫 強制重新載入存放庫 - 在歷史頁面中展開/收合詳細面板 + 展開/收合提交詳細面板 + 開啟/關閉搜尋提交面板 顯示本機變更 - 顯示歷史記錄 - 顯示擱置變更列表 + 顯示儀表板 + 顯示擱置變更 捨棄 暫存 取消暫存 @@ -627,6 +637,8 @@ 開啟 系統預設編輯器 瀏覽程式資料目錄 + 快取資料 + 偏好設定資料 開啟檔案 使用外部比對工具檢視 開啟本機存放庫 @@ -658,10 +670,12 @@ AI 附加提示詞 (請使用 '-' 列出您的要求) API 金鑰 + 額外標頭 (每行一組 `名稱: 值`) 模型 自動取得可用的模型列表 名稱 從環境變數中 (輸入環境變數名稱) 讀取 API 金鑰 + 推理努力等級 伺服器 外觀設定 預設字型 @@ -742,6 +756,7 @@ 啟用強制推送 本機分支: 新增 + 繞過 pre-push hook 遠端存放庫: 修訂: 推送修訂到遠端存放庫 @@ -768,6 +783,7 @@ 編輯遠端存放庫 遠端名稱: 唯一遠端名稱 + 重設推送網址為存放庫網址 存放庫網址: 遠端存放庫的網址 複製遠端網址 @@ -778,6 +794,7 @@ 提取 (fetch) 更新 在瀏覽器中開啟網址 清理遠端已刪除分支 + 設定推送網址... 刪除工作樹操作確認 啟用 [--force] 選項 目標工作樹: @@ -798,10 +815,9 @@ 下一步 自訂動作 沒有自訂的動作 - 首頁 捨棄所有變更 在檔案瀏覽器中開啟 - 快速搜尋分支/標籤/子模組 + 篩選分支/標籤/子模組 篩選以顯示或隱藏 摺疊篩選器 取消指定 @@ -816,7 +832,6 @@ 依時間排序 依拓撲排序 本機分支 - 更多選項... 回到 HEAD 新增分支 清除所有通知 @@ -833,6 +848,8 @@ 路徑 提交編號 僅搜尋目前分支 + 搜尋方式 + 搜尋提交 只顯示分支和標籤引用的提交 只顯示合併提交的第一父提交 顯示內容 @@ -883,6 +900,10 @@ 新版本發行日期: 軟體更新 目前已是最新版本。 + 設定遠端存放庫的推送網址 + 推送網址: + 遠端: + 網址: 設定子模組的追蹤分支 子模組: 目前追蹤分支: @@ -896,6 +917,22 @@ 前往此提交 SSH 金鑰: SSH 金鑰檔案 + 開啟 SSH 金鑰工具 + 您確定要刪除以下的金鑰嗎? + 指紋 + 產生 SSH 金鑰 + 再次輸入您的密碼: + 電子郵件: + 必填。 + 金鑰名稱: + 必填,例如: id_github + 輸入您的密碼: + 產 生 + 類型: + 為私密金鑰檔案設定密碼 + 路徑 + 公開金鑰 + SSH 金鑰工具 開 始 擱置變更 (stash) 包含未追蹤的檔案 @@ -917,9 +954,15 @@ 檢視變更 擱置變更列表 提交統計 + 提交統計圖 總覽 本月 本週 + SourceGit 僅會依所選分支取出最多 200,000 筆提交記錄進行分析 + 主要貢獻者 + 提交次數 + 百分比 + 提交佔比低於 1% 的貢獻者會被歸入 Others 分組 貢獻者人數: 提交次數: 子模組 @@ -962,6 +1005,12 @@ 刪除所選的 {0} 個標籤... 合併 ${0}$ 到 ${1}$... 推送 ${0}$... + 終止執行 + 不受信任的存放庫 + 您是否要執行 `git config --global --add safe.directory {0}` 命令以信任該存放庫? + Git 拒絕存取該存放庫: + 路徑: + 有 {0} 個存放庫因 git 的信任檢查而被略過,請手動開啟並確認是否信任。 更新子模組 更新所有子模組 如果子模組尚未初始化,則將其初始化 @@ -986,7 +1035,7 @@ 開啟本機存放庫 開啟終端機 重新掃描預設複製 (clone) 目錄下的存放庫 - 快速搜尋存放庫... + 篩選存放庫... 本機變更 加入至 .gitignore 忽略清單 忽略所有 *{0} 檔案 @@ -996,11 +1045,12 @@ 忽略同路徑下所有新增檔案 修補 現在您已可將其加入暫存區中 - 清除提交訊息歷史 + 清除提交訊息記錄 您確定要清除所有提交訊息記錄嗎 (執行後無法復原)? + 收合側邊欄 提 交 提交並推送 - 歷史輸入/範本 + 提交訊息記錄/範本 觸發點擊事件 提交 (修改原始提交) 自動暫存全部變更並提交 @@ -1013,7 +1063,9 @@ 檔案衝突已解決 使用我方版本 (ours) 使用對方版本 (theirs) - 搜尋變更... + 捨棄所有變更 + 展開側邊欄 + 篩選變更... 顯示未追蹤檔案 沒有提交訊息記錄 沒有可套用的提交訊息範本 diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 27878b5cc5..d1ba76c821 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -21,6 +21,8 @@ + + @@ -174,9 +176,9 @@ - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/CommitSearchView.axaml.cs b/src/Views/CommitSearchView.axaml.cs new file mode 100644 index 0000000000..0df4ca04a5 --- /dev/null +++ b/src/Views/CommitSearchView.axaml.cs @@ -0,0 +1,102 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Threading; + +namespace SourceGit.Views +{ + public partial class CommitSearchView : UserControl + { + public CommitSearchView() + { + InitializeComponent(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == IsVisibleProperty && IsVisible) + Dispatcher.UIThread.Post(() => TxtSearchCommitsBox.Focus(NavigationMethod.Directional), DispatcherPriority.Background); + } + + private void OnSearchBoxKeyDown(object _, KeyEventArgs e) + { + if (DataContext is not ViewModels.SearchCommitContext ctx) + return; + + if (e.Key == Key.Enter) + { + ctx.StartSearch(); + e.Handled = true; + } + else if (e.Key == Key.Down) + { + if (ctx.Suggestions is { Count: > 0 }) + { + SearchSuggestionBox.Focus(NavigationMethod.Tab); + SearchSuggestionBox.SelectedIndex = 0; + } + + e.Handled = true; + } + else if (e.Key == Key.Escape) + { + ctx.ClearSuggestions(); + e.Handled = true; + } + } + + private void OnSearchSuggestionBoxKeyDown(object _, KeyEventArgs e) + { + if (DataContext is not ViewModels.SearchCommitContext ctx) + return; + + if (e.Key == Key.Escape) + { + ctx.ClearSuggestions(); + e.Handled = true; + } + else if (e.Key == Key.Enter) + { + var selected = SearchSuggestionBox.SelectedItem; + if (selected is string content) + { + ctx.Filter = content; + TxtSearchCommitsBox.CaretIndex = content.Length; + } + else if (selected is Models.User user) + { + var apply = user.ToString().EscapeForBRE(); + ctx.Filter = apply; + TxtSearchCommitsBox.CaretIndex = apply.Length; + } + + ctx.StartSearch(); + e.Handled = true; + } + } + + private void OnSearchSuggestionTapped(object sender, TappedEventArgs e) + { + if (DataContext is not ViewModels.SearchCommitContext ctx) + return; + + var selected = (sender as Control)?.DataContext; + if (selected is string content) + { + ctx.Filter = content; + TxtSearchCommitsBox.CaretIndex = content.Length; + } + else if (selected is Models.User user) + { + var apply = user.ToString().EscapeForBRE(); + ctx.Filter = apply; + TxtSearchCommitsBox.CaretIndex = apply.Length; + } + + ctx.StartSearch(); + e.Handled = true; + } + } +} diff --git a/src/Views/Compare.axaml b/src/Views/Compare.axaml index 3f340d4954..72f4a0a799 100644 --- a/src/Views/Compare.axaml +++ b/src/Views/Compare.axaml @@ -172,17 +172,19 @@ + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=ChangeViewMode, Mode=TwoWay}"/> - - + + + + @@ -192,7 +194,7 @@ IsVisible="{Binding IsLoadingChanges}"/> - + @@ -209,7 +211,7 @@ Focusable="False"/> - + 0 } selected } vm && - sender is ChangeCollectionView view) + if (DataContext is ViewModels.Compare { ChangeSelection: { Count: > 0 } selection } vm) { - var menu = new ContextMenu(); - var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); patch.Icon = this.CreateMenuIcon("Icons.Save"); @@ -43,7 +40,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e if (storageFile != null) { var saveTo = storageFile.Path.LocalPath; - var succ = await vm.SaveChangesAsPatchAsync(selected, saveTo); + var succ = await vm.SaveChangesAsPatchAsync(selection.Changes, saveTo); if (succ) await new Alert().ShowAsync(this, "Save patch successfully.", false); } @@ -56,36 +53,47 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e e.Handled = true; }; - if (selected.Count == 1) + var selectedSingleFolder = selection.IsSingleFolder; + var fullPathOfFolder = selectedSingleFolder ? vm.GetAbsPath(selection.SingleFolderPath) : null; + var relativePathOfFolder = selectedSingleFolder ? selection.SingleFolderPath : null; + + var menu = new ContextMenu(); + if (selection.Count == 1) { - var change = selected[0]; - var openWithMerger = new MenuItem(); - openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); - openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; - openWithMerger.Click += (_, ev) => + var change = selection.Changes[0]; + var changeFullPath = vm.GetAbsPath(change.Path); + + if (!selection.HasFolder) { - vm.OpenInExternalDiffTool(change); - ev.Handled = true; - }; - menu.Items.Add(openWithMerger); + var openWithMerger = new MenuItem(); + openWithMerger.Header = App.Text("OpenInExternalMergeTool"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; + openWithMerger.Click += (_, ev) => + { + vm.OpenInExternalDiffTool(change); + ev.Handled = true; + }; + menu.Items.Add(openWithMerger); + } - if (change.Index != Models.ChangeState.Deleted) + if (!selection.HasFolder || selectedSingleFolder) { - var full = vm.GetAbsPath(change.Path); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); explore.Icon = this.CreateMenuIcon("Icons.Explore"); - explore.IsEnabled = File.Exists(full); + explore.IsEnabled = selectedSingleFolder ? Directory.Exists(fullPathOfFolder) : File.Exists(changeFullPath); explore.Click += (_, ev) => { - Native.OS.OpenInFileManager(full); + Native.OS.OpenInFileManager(selectedSingleFolder ? fullPathOfFolder : changeFullPath); ev.Handled = true; }; menu.Items.Add(explore); } - menu.Items.Add(new MenuItem() { Header = "-" }); + if (menu.Items.Count > 0) + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(patch); if (vm.CanResetFiles) @@ -119,7 +127,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await this.CopyTextAsync(change.Path); + await this.CopyTextAsync(selectedSingleFolder ? relativePathOfFolder : change.Path); ev.Handled = true; }; @@ -129,7 +137,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - await this.CopyTextAsync(vm.GetAbsPath(change.Path)); + await this.CopyTextAsync(selectedSingleFolder ? fullPathOfFolder : changeFullPath); ev.Handled = true; }; @@ -139,6 +147,22 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e } else { + if (selectedSingleFolder) + { + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); + explore.IsEnabled = Directory.Exists(fullPathOfFolder); + explore.Click += (_, ev) => + { + Native.OS.OpenInFileManager(fullPathOfFolder); + ev.Handled = true; + }; + + menu.Items.Add(explore); + menu.Items.Add(new MenuItem() { Header = "-" }); + } + menu.Items.Add(patch); if (vm.CanResetFiles) @@ -148,7 +172,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToLeft.Click += async (_, ev) => { - await vm.ResetMultipleToLeftAsync(selected); + await vm.ResetMultipleToLeftAsync(selection.Changes); ev.Handled = true; }; @@ -157,7 +181,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e resetToRight.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToRight.Click += async (_, ev) => { - await vm.ResetMultipleToRightAsync(selected); + await vm.ResetMultipleToRightAsync(selection.Changes); ev.Handled = true; }; @@ -172,11 +196,19 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - var builder = new StringBuilder(); - foreach (var c in selected) - builder.AppendLine(c.Path); + if (selectedSingleFolder) + { + await this.CopyTextAsync(relativePathOfFolder); + } + else + { + var builder = new StringBuilder(); + foreach (var c in selection.Changes) + builder.AppendLine(c.Path); + + await this.CopyTextAsync(builder.ToString()); + } - await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; @@ -186,11 +218,19 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - var builder = new StringBuilder(); - foreach (var c in selected) - builder.AppendLine(vm.GetAbsPath(c.Path)); + if (selectedSingleFolder) + { + await this.CopyTextAsync(fullPathOfFolder); + } + else + { + var builder = new StringBuilder(); + foreach (var c in selection.Changes) + builder.AppendLine(vm.GetAbsPath(c.Path)); + + await this.CopyTextAsync(builder.ToString()); + } - await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; @@ -199,7 +239,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e menu.Items.Add(copyFullPath); } - menu.Open(view); + menu.Open(sender as Control); } e.Handled = true; @@ -281,10 +321,7 @@ private void OnPressedSHA(object sender, PointerPressedEventArgs e) private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) { - if (DataContext is not ViewModels.Compare vm) - return; - - if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges }) + if (DataContext is not ViewModels.Compare { ChangeSelection: { Count: > 0 } selection } vm) return; var cmdKey = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; @@ -292,13 +329,17 @@ private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) { var builder = new StringBuilder(); var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift); - if (selectedChanges.Count == 1) + if (selection.IsSingleFolder) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(selection.SingleFolderPath) : selection.SingleFolderPath); + } + else if (selection.Changes.Count == 1) { - builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path); + builder.Append(copyAbsPath ? vm.GetAbsPath(selection.Changes[0].Path) : selection.Changes[0].Path); } else { - foreach (var c in selectedChanges) + foreach (var c in selection.Changes) builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); } diff --git a/src/Views/Conflict.axaml b/src/Views/Conflict.axaml index 95d475a014..5b2625d649 100644 --- a/src/Views/Conflict.axaml +++ b/src/Views/Conflict.axaml @@ -9,7 +9,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.Conflict" x:DataType="vm:Conflict"> - + diff --git a/src/Views/CopyButton.cs b/src/Views/CopyButton.cs new file mode 100644 index 0000000000..0ce7908491 --- /dev/null +++ b/src/Views/CopyButton.cs @@ -0,0 +1,125 @@ +using System; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Interactivity; +using Avalonia.Media; +using Avalonia.Threading; + +namespace SourceGit.Views +{ + public class CopyButton : Button + { + protected override Type StyleKeyOverride => typeof(Button); + + public static readonly DirectProperty CopyTextProperty = + AvaloniaProperty.RegisterDirect( + nameof(CopyText), + static o => o.CopyText, + static (o, v) => o.CopyText = v); + + public string CopyText + { + get => _copyText; + set => SetAndRaise(CopyTextProperty, ref _copyText, value); + } + + public static readonly DirectProperty IsCopiedProperty = + AvaloniaProperty.RegisterDirect( + nameof(IsCopied), + static o => o.IsCopied); + + public bool IsCopied + { + get => _isCopied; + private set => SetAndRaise(IsCopiedProperty, ref _isCopied, value); + } + + public CopyButton() + { + Classes.Add("icon_button"); + + _copyIcon = new Path() { Width = 12, Height = 12 }; + _checkIcon = new Path() + { + Width = 14, + Height = 14, + Margin = new Thickness(0, 2, 0, 0), + Fill = Brushes.Green, + IsVisible = false, + }; + + var grid = new Grid(); + grid.Children.Add(_copyIcon); + grid.Children.Add(_checkIcon); + Content = grid; + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + if (this.FindResource("Icons.Copy") is Geometry copyGeo) + _copyIcon.Data = copyGeo; + if (this.FindResource("Icons.Check") is Geometry checkGeo) + _checkIcon.Data = checkGeo; + + _resetTimer = new DispatcherTimer(); + _resetTimer.Interval = TimeSpan.FromSeconds(1); + _resetTimer.Tag = this; + _resetTimer.Tick += static (o, _) => + { + if (o is DispatcherTimer { Tag: CopyButton btn } timer) + { + btn.IsCopied = false; + timer.IsEnabled = false; + } + }; + _resetTimer.IsEnabled = false; + } + + protected override void OnUnloaded(RoutedEventArgs e) + { + _resetTimer.Tag = null; + _resetTimer.IsEnabled = false; + base.OnUnloaded(e); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == IsCopiedProperty) + { + _copyIcon.IsVisible = !_isCopied; + _checkIcon.IsVisible = _isCopied; + } + else if (change.Property == CopyTextProperty) + { + // Reset the copied state when CopyText changes (e.g. switching to a different commit) + IsCopied = false; + _resetTimer?.Stop(); + } + } + + protected override async void OnClick() + { + base.OnClick(); + + var text = CopyText; + if (string.IsNullOrEmpty(text)) + return; + + await this.CopyTextAsync(text); + IsCopied = true; + _resetTimer?.Start(); + } + + private readonly Path _copyIcon; + private readonly Path _checkIcon; + private string _copyText = string.Empty; + private bool _isCopied = false; + private DispatcherTimer _resetTimer = null; + } +} diff --git a/src/Views/DeleteBranch.axaml b/src/Views/DeleteBranch.axaml index 3cd83e73d7..d27b11ea78 100644 --- a/src/Views/DeleteBranch.axaml +++ b/src/Views/DeleteBranch.axaml @@ -18,7 +18,7 @@ Text="{DynamicResource Text.DeleteBranch}"/> - + @@ -37,24 +37,36 @@ - + + - - - - - + + + + + + + + + + diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index baf10f9b27..ba889d09b7 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -8,8 +8,11 @@ xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.DiffView" - x:DataType="vm:DiffContext"> - + x:DataType="vm:DiffContext" + CornerRadius="4" + BorderThickness="1" + BorderBrush="{DynamicResource Brush.Border2}"> + @@ -246,6 +249,13 @@ + + - - + + + + + + + + + + - - + + + diff --git a/src/Views/Histories.axaml b/src/Views/Histories.axaml index 9555d7e21f..a3d24529fd 100644 --- a/src/Views/Histories.axaml +++ b/src/Views/Histories.axaml @@ -80,6 +80,10 @@ + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index e3cc1eb897..00aa09a228 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -42,15 +42,28 @@ - + + + + + + + + + + + - + - + + + + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 99ebf66d9c..0ac14e6bb3 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -267,18 +267,14 @@ protected override async void OnKeyDown(KeyEventArgs e) repo.SelectedViewIndex = 2; e.Handled = true; return; - case Key.F when e.KeyModifiers.HasFlag(KeyModifiers.Shift): - repo.IsSearchingCommits = true; - e.Handled = true; - return; - case Key.H when e.KeyModifiers.HasFlag(KeyModifiers.Shift): - repo.IsSearchingCommits = false; - e.Handled = true; - return; case Key.P when e.KeyModifiers.HasFlag(KeyModifiers.Shift): vm.CommandPalette = new ViewModels.RepositoryCommandPalette(repo); e.Handled = true; return; + case Key.F when e.KeyModifiers.HasFlag(KeyModifiers.Shift): + repo.Histories.IsSearchingCommits = !repo.Histories.IsSearchingCommits; + e.Handled = true; + return; case Key.B when e.KeyModifiers.HasFlag(KeyModifiers.Shift): if (repo.CanCreatePopup() && repo.GetSelectedCommitInHistory() is { } bc) repo.ShowPopup(new ViewModels.CreateBranch(repo, bc)); @@ -299,6 +295,7 @@ protected override async void OnKeyDown(KeyEventArgs e) else if (e.Key == Key.Escape) { vm.ActivePage.CancelPopup(); + vm.ActivePage.Notifications.Clear(); e.Handled = true; return; } @@ -321,6 +318,19 @@ protected override async void OnKeyDown(KeyEventArgs e) base.OnKeyDown(e); } + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + if (!e.Handled) + { + if (e.Properties.PointerUpdateKind == PointerUpdateKind.XButton1Pressed) + (DataContext as ViewModels.Launcher)?.GotoPrevTab(); + else if (e.Properties.PointerUpdateKind == PointerUpdateKind.XButton2Pressed) + (DataContext as ViewModels.Launcher)?.GotoNextTab(); + } + } + protected override void OnClosing(WindowClosingEventArgs e) { base.OnClosing(e); diff --git a/src/Views/LauncherPage.axaml b/src/Views/LauncherPage.axaml index bcbecbeca8..08107bfa90 100644 --- a/src/Views/LauncherPage.axaml +++ b/src/Views/LauncherPage.axaml @@ -9,12 +9,10 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.LauncherPage" x:DataType="vm:LauncherPage"> - + @@ -34,11 +32,35 @@ - + - + + + + + + + + + + + + + + + + + + + + + @@ -59,69 +81,88 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - + + + + - + + + + diff --git a/src/Views/LauncherPage.axaml.cs b/src/Views/LauncherPage.axaml.cs index 7a95064124..8981a86c29 100644 --- a/src/Views/LauncherPage.axaml.cs +++ b/src/Views/LauncherPage.axaml.cs @@ -59,17 +59,17 @@ private void OnPopupCancel(object _, RoutedEventArgs e) e.Handled = true; } - private void OnMaskClicked(object sender, PointerPressedEventArgs e) + private void OnPopupTerminate(object _, RoutedEventArgs e) { - OnPopupCancel(sender, e); + if (DataContext is ViewModels.LauncherPage page) + page.TerminatePopup(); + + e.Handled = true; } - private async void OnCopyNotification(object sender, RoutedEventArgs e) + private void OnMaskClicked(object sender, PointerPressedEventArgs e) { - if (sender is Button { DataContext: Models.Notification notice }) - await this.CopyTextAsync(notice.Message); - - e.Handled = true; + OnPopupCancel(sender, e); } private void OnDismissNotification(object sender, RoutedEventArgs e) diff --git a/src/Views/LauncherPagesCommandPalette.axaml b/src/Views/LauncherPagesCommandPalette.axaml index 70408dce82..4acb43a2a8 100644 --- a/src/Views/LauncherPagesCommandPalette.axaml +++ b/src/Views/LauncherPagesCommandPalette.axaml @@ -84,7 +84,7 @@ - + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/LauncherTabBar.axaml.cs b/src/Views/LauncherTabBar.axaml.cs index 83826afcec..19bb5967ac 100644 --- a/src/Views/LauncherTabBar.axaml.cs +++ b/src/Views/LauncherTabBar.axaml.cs @@ -302,6 +302,17 @@ private void OnTabContextRequested(object sender, ContextRequestedEventArgs e) if (vm.ActivePage.Data is ViewModels.Repository repo) { + var explore = new MenuItem(); + explore.Header = App.Text("Repository.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); + explore.Tag = OperatingSystem.IsMacOS() ? "⌘+E" : "Ctrl+E"; + explore.Click += (_, e) => + { + Native.OS.OpenInFileManager(repo.FullPath); + e.Handled = true; + }; + menu.Items.Add(explore); + var refresh = new MenuItem(); refresh.Header = App.Text("PageTabBar.Tab.Refresh"); refresh.Icon = this.CreateMenuIcon("Icons.Loading"); diff --git a/src/Views/OpenLocalRepository.axaml b/src/Views/OpenLocalRepository.axaml index 6886caa1e5..ee802ab575 100644 --- a/src/Views/OpenLocalRepository.axaml +++ b/src/Views/OpenLocalRepository.axaml @@ -44,10 +44,17 @@ Height="28" Padding="8,0" VerticalAlignment="Center" HorizontalAlignment="Stretch" ItemsSource="{Binding Groups}" - SelectedItem="{Binding Group, Mode=TwoWay}"> + SelectedIndex="{Binding SelectedGroupIndex, Mode=TwoWay}"> - - + + + + + diff --git a/src/Views/PieChart.cs b/src/Views/PieChart.cs new file mode 100644 index 0000000000..0da216d9d9 --- /dev/null +++ b/src/Views/PieChart.cs @@ -0,0 +1,376 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.VisualTree; + +namespace SourceGit.Views +{ + public record PieChartToolTip(string Title, int Count, double Percentage, IBrush Brush); + + public class PieChart : Control + { + public static readonly DirectProperty ReportProperty = + AvaloniaProperty.RegisterDirect( + nameof(Report), + o => o.Report, + (o, v) => o.Report = v); + + public Models.StatisticsReport Report + { + get => _report; + set => SetAndRaise(ReportProperty, ref _report, value); + } + + public static readonly DirectProperty HoveringTipProperty = + AvaloniaProperty.RegisterDirect( + nameof(HoveringTip), + o => o.HoveringTip); + + public PieChartToolTip HoveringTip + { + get => _hoveringTip; + private set => SetAndRaise(HoveringTipProperty, ref _hoveringTip, value); + } + + public static readonly StyledProperty FontFamilyProperty = + AvaloniaProperty.Register(nameof(FontFamily)); + + public FontFamily FontFamily + { + get => GetValue(FontFamilyProperty); + set => SetValue(FontFamilyProperty, value); + } + + public static readonly StyledProperty SecondaryFontFamilyProperty = + AvaloniaProperty.Register(nameof(SecondaryFontFamily)); + + public FontFamily SecondaryFontFamily + { + get => GetValue(SecondaryFontFamilyProperty); + set => SetValue(SecondaryFontFamilyProperty, value); + } + + public static readonly StyledProperty ForegroundProperty = + AvaloniaProperty.Register(nameof(Foreground), Brushes.White); + + public IBrush Foreground + { + get => GetValue(ForegroundProperty); + set => SetValue(ForegroundProperty, value); + } + + public static readonly StyledProperty SecondaryForegroundProperty = + AvaloniaProperty.Register(nameof(SecondaryForeground), Brushes.Gray); + + public IBrush SecondaryForeground + { + get => GetValue(SecondaryForegroundProperty); + set => SetValue(SecondaryForegroundProperty, value); + } + + public override void Render(DrawingContext context) + { + base.Render(context); + + _arcs.Clear(); + _lastHovered = null; + + var w = Bounds.Width; + var h = Bounds.Height; + var foreground = Foreground; + var secondaryForeground = SecondaryForeground; + var typeface = new Typeface(FontFamily); + var secondaryTypeface = new Typeface(SecondaryFontFamily); + + _center = new Point(w / 2, h / 2); + _radius = Math.Min(w, h) / 2 - 48; + context.FillRectangle(Brushes.Transparent, new Rect(0, 0, w, h)); + + if (_report == null || _report.Authors.Count == 0) + { + var label = new FormattedText("0", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, 14, foreground); + context.DrawEllipse(null, new Pen(s_brushes[0]), _center, _radius, _radius); + context.DrawText(label, new Point(_center.X - label.Width / 2, _center.Y - label.Height / 2)); + return; + } + else if (_report.Authors.Count == 1) + { + var single = _report.Authors[0]; + var label = new FormattedText( + single.User.Name, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + 12, + Brushes.White); + context.DrawEllipse(s_brushes[0], null, _center, _radius, _radius); + context.DrawText(label, new Point(_center.X - label.Width / 2, _center.Y - label.Height / 2)); + + _arcs.Add(new(0, 2 * Math.PI, single.User.Name, single.Count, 1.0, s_brushes[0], single)); + return; + } + + var total = _report.Total; + var renderedCount = 0; + var startAngle = -Math.PI / 2; + var remaining = 2 * Math.PI; + var leftArcs = new List(); + var rightArcs = new List(); + for (var i = 0; i < _report.Authors.Count; i++) + { + var author = _report.Authors[i]; + var percent = (double)author.Count / total; + if (percent > 0.01) + { + var brushIdx = i % s_brushes.Length; + var brush = (i == _report.Authors.Count - 1 && brushIdx == 0) ? s_brushes[1] : s_brushes[brushIdx]; + var sweepAngle = percent * 2 * Math.PI; + DrawArc(context, startAngle, sweepAngle, brush); + + var arc = new Arc(startAngle, sweepAngle, author.User.Name, author.Count, percent, brush, author); + _arcs.Add(arc); + if (arc.IsLeftSide) + leftArcs.Add(arc); + else + rightArcs.Add(arc); + + remaining -= sweepAngle; + startAngle += sweepAngle; + renderedCount += author.Count; + } + else + { + var sweepAngle = remaining; + var count = total - renderedCount; + DrawArc(context, startAngle, sweepAngle, Brushes.Gray); + + var arc = new Arc(startAngle, sweepAngle, "Others", count, (double)count / total, Brushes.Gray, null); + _arcs.Add(arc); + if (arc.IsLeftSide) + leftArcs.Add(arc); + else + rightArcs.Add(arc); + break; + } + } + + if (leftArcs.Count > 0) + { + for (var i = leftArcs.Count - 1; i >= 0; i--) + { + var arc = leftArcs[i]; + var pen = new Pen(arc.Tip.Brush, 1); + var lineStartX = _center.X - _radius - 32; + + var edgePoint = new Point( + _center.X + _radius * Math.Cos(arc.MidAngle), + _center.Y + _radius * Math.Sin(arc.MidAngle)); + + var edgeLineStartPoint = new Point( + _center.X + (_radius + 32) * Math.Cos(arc.MidAngle), + _center.Y + (_radius + 32) * Math.Sin(arc.MidAngle)); + + context.DrawLine(pen, new Point(lineStartX, edgeLineStartPoint.Y), edgeLineStartPoint); + context.DrawLine(pen, edgeLineStartPoint, edgePoint); + + var title = new FormattedText( + arc.Tip.Title, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + 12, + foreground); + var percentage = new FormattedText( + $"{arc.Tip.Percentage:P1}", + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + secondaryTypeface, + 12, + secondaryForeground); + + var labelStartX = lineStartX - 8 - percentage.WidthIncludingTrailingWhitespace - 4 - title.WidthIncludingTrailingWhitespace; + var labelMidY = edgeLineStartPoint.Y; + context.DrawText(title, new Point(labelStartX, labelMidY - title.Height * 0.5)); + context.DrawText(percentage, new Point(labelStartX + title.WidthIncludingTrailingWhitespace + 4, labelMidY - percentage.Height * 0.5)); + } + } + + if (rightArcs.Count > 0) + { + for (var i = 0; i < rightArcs.Count; i++) + { + var arc = rightArcs[i]; + var pen = new Pen(arc.Tip.Brush, 1); + var lineStartX = _center.X + _radius + 32; + + var edgePoint = new Point( + _center.X + _radius * Math.Cos(arc.MidAngle), + _center.Y + _radius * Math.Sin(arc.MidAngle)); + + var edgeLineStartPoint = new Point( + _center.X + (_radius + 32) * Math.Cos(arc.MidAngle), + _center.Y + (_radius + 32) * Math.Sin(arc.MidAngle)); + + context.DrawLine(pen, new Point(lineStartX, edgeLineStartPoint.Y), edgeLineStartPoint); + context.DrawLine(pen, edgeLineStartPoint, edgePoint); + + var title = new FormattedText( + arc.Tip.Title, + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + 12, + foreground); + var percentage = new FormattedText( + $"{arc.Tip.Percentage:P1}", + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + secondaryTypeface, + 12, + secondaryForeground); + + var labelStartX = lineStartX + 8; + var labelMidY = edgeLineStartPoint.Y; + context.DrawText(title, new Point(labelStartX, labelMidY - title.Height * 0.5)); + context.DrawText(percentage, new Point(labelStartX + title.WidthIncludingTrailingWhitespace + 4, labelMidY - percentage.Height * 0.5)); + } + } + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ReportProperty) + { + _lastHovered = null; + HoveringTip = null; + } + + if (change.Property == ReportProperty || + change.Property == FontFamilyProperty || + change.Property == ForegroundProperty || + change.Property == SecondaryForegroundProperty) + InvalidateVisual(); + } + + protected override void OnPointerMoved(PointerEventArgs e) + { + base.OnPointerMoved(e); + + if (_report == null || _report.Authors.Count == 0) + { + _lastHovered = null; + HoveringTip = null; + return; + } + + var pos = e.GetPosition(this); + var distance = Math.Sqrt(Math.Pow(pos.X - _center.X, 2) + Math.Pow(pos.Y - _center.Y, 2)); + if (distance > _radius) + { + _lastHovered = null; + HoveringTip = null; + return; + } + + var angle = Math.Atan2(pos.Y - _center.Y, pos.X - _center.X); + if (angle < -Math.PI / 2) + angle += 2 * Math.PI; + + foreach (var arc in _arcs) + { + if (angle >= arc.StartAngle && angle < arc.EndAngle) + { + if (_lastHovered == arc) + return; + + _lastHovered = arc; + HoveringTip = arc.Tip; + return; + } + } + + _lastHovered = null; + HoveringTip = null; + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + if (_lastHovered != null) + { + var statisticsView = this.FindAncestorOfType(); + if (statisticsView is { DataContext: ViewModels.Statistics vm }) + { + if (_lastHovered.Author == null || _lastHovered.Author == vm.SelectedAuthor) + vm.ChangeAuthor(null); + else + vm.ChangeAuthor(_lastHovered.Author); + } + } + } + + private void DrawArc(DrawingContext context, double startAngle, double sweepAngle, IBrush brush) + { + var endAngle = startAngle + sweepAngle; + var startPoint = new Point(_center.X + _radius * Math.Cos(startAngle), _center.Y + _radius * Math.Sin(startAngle)); + var endPoint = new Point(_center.X + _radius * Math.Cos(endAngle), _center.Y + _radius * Math.Sin(endAngle)); + var geometry = new StreamGeometry(); + using (var ctx = geometry.Open()) + { + ctx.BeginFigure(_center, true); + ctx.LineTo(startPoint); + ctx.ArcTo(endPoint, new Size(_radius, _radius), 0, sweepAngle > Math.PI, SweepDirection.Clockwise); + ctx.LineTo(_center); + } + context.DrawGeometry(brush, null, geometry); + } + + private class Arc + { + public double StartAngle { get; set; } + public double EndAngle { get; set; } + public PieChartToolTip Tip { get; set; } + public Models.StatisticsAuthor Author { get; set; } + + public double MidAngle => (StartAngle + EndAngle) / 2; + public bool IsLeftSide => MidAngle > Math.PI * 0.5; + + public Arc(double startAngle, double sweepAngle, string title, int count, double percentage, IBrush brush, Models.StatisticsAuthor author) + { + StartAngle = startAngle; + EndAngle = startAngle + sweepAngle; + Tip = new PieChartToolTip(title, count, percentage, brush); + Author = author; + } + } + + private static readonly IBrush[] s_brushes = new IBrush[] + { + Brushes.Orange, + Brushes.ForestGreen, + Brushes.Turquoise, + Brushes.Olive, + Brushes.Magenta, + Brushes.Red, + Brushes.Khaki, + Brushes.Lime, + Brushes.RoyalBlue, + Brushes.Teal, + }; + + private Models.StatisticsReport _report = null; + private Point _center = new Point(0, 0); + private double _radius = 0; + private List _arcs = new List(); + private Arc _lastHovered = null; + private PieChartToolTip _hoveringTip = null; + } +} diff --git a/src/Views/PopupDataTemplates.cs b/src/Views/PopupDataTemplates.cs index bfb2673aa0..25151b211a 100644 --- a/src/Views/PopupDataTemplates.cs +++ b/src/Views/PopupDataTemplates.cs @@ -35,6 +35,9 @@ public Control Build(object param) return; } } + + // Although this control may not be focusable, we only want to change the window's previous focus after the call. + ctl.Focus(NavigationMethod.Directional); }; return control; diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index b0a4753bb8..55c2b17276 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -917,6 +917,13 @@ Content="{DynamicResource Text.Preferences.AI.Model.AutoFetchAvailableModels}" IsChecked="{Binding AutoFetchAvailableModels, Mode=TwoWay}"/> + + + + + + diff --git a/src/Views/Preferences.axaml.cs b/src/Views/Preferences.axaml.cs index 99ec73a5f2..773a0f3e7b 100644 --- a/src/Views/Preferences.axaml.cs +++ b/src/Views/Preferences.axaml.cs @@ -341,7 +341,11 @@ private async void SelectShellOrTerminal(object _, RoutedEventArgs e) { options = new FilePickerOpenOptions() { - FileTypeFilter = [new FilePickerFileType(shell.Name) { Patterns = [shell.Exec] }], + FileTypeFilter = [new FilePickerFileType(shell.Name) + { + Patterns = shell.Exec.Split('|', + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + }], AllowMultiple = false, }; } diff --git a/src/Views/Push.axaml b/src/Views/Push.axaml index 4f013bf838..e306d6b7db 100644 --- a/src/Views/Push.axaml +++ b/src/Views/Push.axaml @@ -19,7 +19,7 @@ Text="{DynamicResource Text.Push.Title}"/> - + + + diff --git a/src/Views/Push.axaml.cs b/src/Views/Push.axaml.cs index fd7f6747fa..29742b562a 100644 --- a/src/Views/Push.axaml.cs +++ b/src/Views/Push.axaml.cs @@ -20,8 +20,10 @@ private async void OnPushToNewBranch(object sender, RoutedEventArgs e) if (launcher == null) return; - var dialog = new PushToNewBranch(); - dialog.SetRemote(push.SelectedRemote.Name); + var dialog = new PushToNewBranch() + { + DataContext = new ViewModels.PushToNewBranch(push.SelectedRemote.Name) + }; var name = await dialog.ShowDialog(launcher); if (!string.IsNullOrEmpty(name)) diff --git a/src/Views/PushToNewBranch.axaml b/src/Views/PushToNewBranch.axaml index 0c4eef922e..7b145e8123 100644 --- a/src/Views/PushToNewBranch.axaml +++ b/src/Views/PushToNewBranch.axaml @@ -3,12 +3,14 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:v="using:SourceGit.Views" + xmlns:vm="using:SourceGit.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.PushToNewBranch" x:Name="ThisControl" + x:DataType="vm:PushToNewBranch" Icon="/App.ico" Title="{DynamicResource Text.PushToNewBranch}" - SizeToContent="WidthAndHeight" + Width="400" SizeToContent="Height" CanResize="False" WindowStartupLocation="CenterOwner"> @@ -39,17 +41,18 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + IsVisible="{Binding IsTagGroupExpanded, Mode=OneWay}" + SelectionChanged="OnTagsSelectionChanged" + RowsChanged="OnLeftSidebarRowsChanged" + SearchRequested="OnToggleSidebarFilter"/> + + + + + + + + - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index f5bac41d8d..93d77396ec 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -10,8 +10,75 @@ namespace SourceGit.Views { public partial class Repository : UserControl { + public static readonly DirectProperty IsCommitSearchPanelVisibleProperty = + AvaloniaProperty.RegisterDirect( + nameof(IsCommitSearchPanelVisible), + static o => o.IsCommitSearchPanelVisible, + static (o, v) => o.IsCommitSearchPanelVisible = v); + + public bool IsCommitSearchPanelVisible + { + get => _isCommitSearchPanelVisible; + set + { + if (SetAndRaise(IsCommitSearchPanelVisibleProperty, ref _isCommitSearchPanelVisible, value)) + CalculateSidebarWidth(); + } + } + + public static readonly DirectProperty IsNormalSidebarVisibleProperty = + AvaloniaProperty.RegisterDirect( + nameof(IsNormalSidebarVisible), + static o => o.IsNormalSidebarVisible, + static (o, v) => o.IsNormalSidebarVisible = v); + + public bool IsNormalSidebarVisible + { + get => _isNormalSidebarVisible; + set + { + if (SetAndRaise(IsNormalSidebarVisibleProperty, ref _isNormalSidebarVisible, value)) + CalculateSidebarWidth(); + } + } + + public static readonly DirectProperty SidebarMinWidthProperty = + AvaloniaProperty.RegisterDirect( + nameof(SidebarMinWidth), + static o => o.SidebarMinWidth, + static (o, v) => o.SidebarMinWidth = v); + + public double SidebarMinWidth + { + get => _sidebarMinWidth; + set => SetAndRaise(SidebarMinWidthProperty, ref _sidebarMinWidth, value); + } + + public static readonly DirectProperty SidebarWidthProperty = + AvaloniaProperty.RegisterDirect( + nameof(SidebarWidth), + static o => o.SidebarWidth, + static (o, v) => o.SidebarWidth = v); + + public GridLength SidebarWidth + { + get => _sidebarWidth; + set + { + if (SetAndRaise(SidebarWidthProperty, ref _sidebarWidth, value)) + { + var layout = ViewModels.Preferences.Instance.Layout; + if (_isCommitSearchPanelVisible) + layout.RepositorySearchCommitWidth = value.Value; + else if (_isNormalSidebarVisible) + layout.RepositorySidebarWidth = value.Value; + } + } + } + public Repository() { + _sidebarWidth = new GridLength(ViewModels.Preferences.Instance.Layout.RepositorySidebarWidth); InitializeComponent(); } @@ -21,51 +88,42 @@ protected override void OnLoaded(RoutedEventArgs e) UpdateLeftSidebarLayout(); } - private void OnToggleFilter(object _, RoutedEventArgs e) + private async void OnOpenConfigure(object sender, RoutedEventArgs e) { - FilterBox.Focus(); - e.Handled = true; + if (DataContext is ViewModels.Repository repo) + { + await this.ShowDialogAsync(new ViewModels.RepositoryConfigure(repo)); + e.Handled = true; + } } - private void OnSearchCommitPanelPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + private async void OnSkipInProgress(object sender, RoutedEventArgs e) { - if (e.Property == IsVisibleProperty && sender is Grid { IsVisible: true }) - TxtSearchCommitsBox.Focus(); + if (DataContext is ViewModels.Repository repo) + await repo.SkipMergeAsync(); + + e.Handled = true; } - private void OnSearchKeyDown(object _, KeyEventArgs e) + private void OnResolveInProgress(object sender, RoutedEventArgs e) { - if (DataContext is not ViewModels.Repository repo) - return; - - if (e.Key == Key.Enter) - { - repo.SearchCommitContext.StartSearch(); - e.Handled = true; - } - else if (e.Key == Key.Down) - { - if (repo.SearchCommitContext.Suggestions is { Count: > 0 }) - { - SearchSuggestionBox.Focus(NavigationMethod.Tab); - SearchSuggestionBox.SelectedIndex = 0; - } + if (DataContext is ViewModels.Repository repo) + repo.SelectedViewIndex = 1; - e.Handled = true; - } - else if (e.Key == Key.Escape) - { - repo.SearchCommitContext.ClearSuggestions(); - e.Handled = true; - } + e.Handled = true; } - private void OnClearSearchCommitFilter(object _, RoutedEventArgs e) + private async void OnAbortInProgress(object sender, RoutedEventArgs e) { - if (DataContext is not ViewModels.Repository repo) - return; + if (DataContext is ViewModels.Repository repo) + await repo.AbortMergeAsync(); - repo.SearchCommitContext.ClearFilter(); + e.Handled = true; + } + + private void OnToggleSidebarFilter(object _, RoutedEventArgs e) + { + FilterBox.Focus(); e.Handled = true; } @@ -186,11 +244,10 @@ private void OnLeftSidebarSizeChanged(object _, SizeChangedEventArgs e) private void UpdateLeftSidebarLayout() { - var vm = DataContext as ViewModels.Repository; - if (vm?.Settings == null) + if (!IsLoaded || _isCommitSearchPanelVisible) return; - if (!IsLoaded) + if (DataContext is not ViewModels.Repository { UIStates: { } } vm) return; var leftHeight = LeftSidebarGroups.Bounds.Height - 28.0 * 5 - 4; @@ -307,222 +364,6 @@ private void UpdateLeftSidebarLayout() } } - private void OnSearchSuggestionBoxKeyDown(object _, KeyEventArgs e) - { - if (DataContext is not ViewModels.Repository repo) - return; - - if (e.Key == Key.Escape) - { - repo.SearchCommitContext.ClearSuggestions(); - e.Handled = true; - } - else if (e.Key == Key.Enter) - { - var selected = SearchSuggestionBox.SelectedItem; - if (selected is string content) - { - repo.SearchCommitContext.Filter = content; - TxtSearchCommitsBox.CaretIndex = content.Length; - } - else if (selected is Models.User user) - { - var apply = user.ToString().EscapeForBRE(); - repo.SearchCommitContext.Filter = apply; - TxtSearchCommitsBox.CaretIndex = apply.Length; - } - - repo.SearchCommitContext.StartSearch(); - e.Handled = true; - } - } - - private void OnSearchSuggestionTapped(object sender, TappedEventArgs e) - { - if (DataContext is not ViewModels.Repository repo) - return; - - var ctx = (sender as Control)?.DataContext; - if (ctx is string content) - { - repo.SearchCommitContext.Filter = content; - TxtSearchCommitsBox.CaretIndex = content.Length; - } - else if (ctx is Models.User user) - { - var apply = user.ToString().EscapeForBRE(); - repo.SearchCommitContext.Filter = apply; - TxtSearchCommitsBox.CaretIndex = apply.Length; - } - - repo.SearchCommitContext.StartSearch(); - e.Handled = true; - } - - private void OnOpenAdvancedHistoriesOption(object sender, RoutedEventArgs e) - { - if (sender is Button button && DataContext is ViewModels.Repository { Histories: { } histories } repo) - { - var pref = ViewModels.Preferences.Instance; - - var layout = new MenuItem(); - layout.Header = App.Text("Repository.HistoriesLayout"); - layout.IsEnabled = false; - - var isHorizontal = pref.UseTwoColumnsLayoutInHistories; - var horizontal = new MenuItem(); - horizontal.Header = App.Text("Repository.HistoriesLayout.Horizontal"); - if (isHorizontal) - horizontal.Icon = this.CreateMenuIcon("Icons.Check"); - horizontal.Click += (_, ev) => - { - pref.UseTwoColumnsLayoutInHistories = true; - ev.Handled = true; - }; - - var vertical = new MenuItem(); - vertical.Header = App.Text("Repository.HistoriesLayout.Vertical"); - if (!isHorizontal) - vertical.Icon = this.CreateMenuIcon("Icons.Check"); - vertical.Click += (_, ev) => - { - pref.UseTwoColumnsLayoutInHistories = false; - ev.Handled = true; - }; - - var showFlags = new MenuItem(); - showFlags.Header = App.Text("Repository.ShowFlags"); - showFlags.IsEnabled = false; - - var reflog = new MenuItem(); - reflog.Header = App.Text("Repository.ShowLostCommits"); - reflog.Tag = "--reflog"; - if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.Reflog)) - reflog.Icon = this.CreateMenuIcon("Icons.Check"); - reflog.Click += (_, ev) => - { - repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.Reflog); - ev.Handled = true; - }; - - var firstParentOnly = new MenuItem(); - firstParentOnly.Header = App.Text("Repository.ShowFirstParentOnly"); - firstParentOnly.Tag = "--first-parent"; - if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly)) - firstParentOnly.Icon = this.CreateMenuIcon("Icons.Check"); - firstParentOnly.Click += (_, ev) => - { - repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.FirstParentOnly); - ev.Handled = true; - }; - - var simplifyByDecoration = new MenuItem(); - simplifyByDecoration.Header = App.Text("Repository.ShowDecoratedCommitsOnly"); - simplifyByDecoration.Tag = "--simplify-by-decoration"; - if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.SimplifyByDecoration)) - simplifyByDecoration.Icon = this.CreateMenuIcon("Icons.Check"); - simplifyByDecoration.Click += (_, ev) => - { - repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.SimplifyByDecoration); - ev.Handled = true; - }; - - var order = new MenuItem(); - order.Header = App.Text("Repository.HistoriesOrder"); - order.IsEnabled = false; - - var dateOrder = new MenuItem(); - dateOrder.Header = App.Text("Repository.HistoriesOrder.ByDate"); - dateOrder.Tag = "--date-order"; - if (!repo.EnableTopoOrderInHistory) - dateOrder.Icon = this.CreateMenuIcon("Icons.Check"); - dateOrder.Click += (_, ev) => - { - repo.EnableTopoOrderInHistory = false; - ev.Handled = true; - }; - - var topoOrder = new MenuItem(); - topoOrder.Header = App.Text("Repository.HistoriesOrder.Topo"); - topoOrder.Tag = "--topo-order"; - if (repo.EnableTopoOrderInHistory) - topoOrder.Icon = this.CreateMenuIcon("Icons.Check"); - topoOrder.Click += (_, ev) => - { - repo.EnableTopoOrderInHistory = true; - ev.Handled = true; - }; - - var highlights = new MenuItem(); - highlights.Header = App.Text("Histories.HighlightsInGraph"); - highlights.IsEnabled = false; - - var all = new MenuItem(); - all.Header = App.Text("Histories.HighlightsInGraph.All"); - if (histories.GraphHighlighting == Models.CommitGraphHighlighting.All) - all.Icon = this.CreateMenuIcon("Icons.Check"); - all.Click += (_, ev) => - { - histories.GraphHighlighting = Models.CommitGraphHighlighting.All; - ev.Handled = true; - }; - - var currentBranchOnly = new MenuItem(); - currentBranchOnly.Header = App.Text("Histories.HighlightsInGraph.CurrentBranchOnly"); - if (histories.GraphHighlighting == Models.CommitGraphHighlighting.CurrentBranchOnly) - currentBranchOnly.Icon = this.CreateMenuIcon("Icons.Check"); - currentBranchOnly.Click += (_, ev) => - { - histories.GraphHighlighting = Models.CommitGraphHighlighting.CurrentBranchOnly; - ev.Handled = true; - }; - - var selectedCommitsOnly = new MenuItem(); - selectedCommitsOnly.Header = App.Text("Histories.HighlightsInGraph.SelectedCommitsOnly"); - if (histories.GraphHighlighting == Models.CommitGraphHighlighting.SelectedCommitsOnly) - selectedCommitsOnly.Icon = this.CreateMenuIcon("Icons.Check"); - selectedCommitsOnly.Click += (_, ev) => - { - histories.GraphHighlighting = Models.CommitGraphHighlighting.SelectedCommitsOnly; - ev.Handled = true; - }; - - var currentBranchAndSelectedCommits = new MenuItem(); - currentBranchAndSelectedCommits.Header = App.Text("Histories.HighlightsInGraph.CurrentBranchAndSelectedCommits"); - if (histories.GraphHighlighting == Models.CommitGraphHighlighting.CurrentBranchAndSelectedCommits) - currentBranchAndSelectedCommits.Icon = this.CreateMenuIcon("Icons.Check"); - currentBranchAndSelectedCommits.Click += (_, ev) => - { - histories.GraphHighlighting = Models.CommitGraphHighlighting.CurrentBranchAndSelectedCommits; - ev.Handled = true; - }; - - var menu = new ContextMenu(); - menu.Placement = PlacementMode.BottomEdgeAlignedLeft; - menu.Items.Add(layout); - menu.Items.Add(horizontal); - menu.Items.Add(vertical); - menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(showFlags); - menu.Items.Add(reflog); - menu.Items.Add(firstParentOnly); - menu.Items.Add(simplifyByDecoration); - menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(order); - menu.Items.Add(dateOrder); - menu.Items.Add(topoOrder); - menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(highlights); - menu.Items.Add(all); - menu.Items.Add(currentBranchOnly); - menu.Items.Add(selectedCommitsOnly); - menu.Items.Add(currentBranchAndSelectedCommits); - menu.Open(button); - } - - e.Handled = true; - } - private void OnOpenSortLocalBranchMenu(object sender, RoutedEventArgs e) { if (sender is Button button && DataContext is ViewModels.Repository repo) @@ -642,30 +483,6 @@ private async void OnPruneWorktrees(object sender, RoutedEventArgs e) e.Handled = true; } - private async void OnSkipInProgress(object sender, RoutedEventArgs e) - { - if (DataContext is ViewModels.Repository repo) - await repo.SkipMergeAsync(); - - e.Handled = true; - } - - private void OnResolveInProgress(object sender, RoutedEventArgs e) - { - if (DataContext is ViewModels.Repository repo) - repo.SelectedViewIndex = 1; - - e.Handled = true; - } - - private async void OnAbortInProgress(object sender, RoutedEventArgs e) - { - if (DataContext is ViewModels.Repository repo) - await repo.AbortMergeAsync(); - - e.Handled = true; - } - private void OnRemoveSelectedHistoryFilter(object sender, RoutedEventArgs e) { if (DataContext is ViewModels.Repository repo && sender is Button { DataContext: Models.HistoryFilter filter }) @@ -686,11 +503,36 @@ private async void OnBisectCommand(object sender, RoutedEventArgs e) private void OnRightPagePropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) { - if (e.Property == Border.IsVisibleProperty && sender is Border page) + if (e.Property == Control.IsVisibleProperty && sender is Control page) { var diffViewer = page.FindDescendantOfType(); diffViewer?.ToggleHotkeyBindings(page.IsVisible); } } + + private void CalculateSidebarWidth() + { + var layout = ViewModels.Preferences.Instance.Layout; + if (_isCommitSearchPanelVisible) + { + SidebarMinWidth = 200; + SidebarWidth = new GridLength(layout.RepositorySearchCommitWidth, GridUnitType.Pixel); + } + else if (_isNormalSidebarVisible) + { + SidebarMinWidth = 200; + SidebarWidth = new GridLength(layout.RepositorySidebarWidth, GridUnitType.Pixel); + } + else + { + SidebarMinWidth = 0; + SidebarWidth = new GridLength(0, GridUnitType.Pixel); + } + } + + private bool _isNormalSidebarVisible = true; + private bool _isCommitSearchPanelVisible = false; + private double _sidebarMinWidth = 200; + private GridLength _sidebarWidth; } } diff --git a/src/Views/RepositoryToolbar.axaml b/src/Views/RepositoryToolbar.axaml index 87450c0245..08ed8e83c5 100644 --- a/src/Views/RepositoryToolbar.axaml +++ b/src/Views/RepositoryToolbar.axaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" + xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.RepositoryToolbar" x:DataType="vm:Repository"> @@ -18,11 +19,7 @@ - - @@ -45,8 +42,14 @@ - - + + + + + + + @@ -107,30 +127,29 @@ - + diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs index 0d03acb89b..c5ba572cad 100644 --- a/src/Views/RepositoryToolbar.axaml.cs +++ b/src/Views/RepositoryToolbar.axaml.cs @@ -162,15 +162,6 @@ private async void OpenStatistics(object _, RoutedEventArgs e) } } - private async void OpenConfigure(object sender, RoutedEventArgs e) - { - if (DataContext is ViewModels.Repository repo) - { - await this.ShowDialogAsync(new ViewModels.RepositoryConfigure(repo)); - e.Handled = true; - } - } - private async void Fetch(object sender, TappedEventArgs e) { if (DataContext is ViewModels.Repository repo) @@ -477,15 +468,6 @@ private async void StartBisect(object sender, RoutedEventArgs e) e.Handled = true; } - private async void Cleanup(object sender, RoutedEventArgs e) - { - if (DataContext is ViewModels.Repository repo) - { - await repo.CleanupAsync(); - e.Handled = true; - } - } - private void OpenCustomActionMenu(object sender, RoutedEventArgs ev) { if (DataContext is ViewModels.Repository repo && sender is Control control) @@ -531,14 +513,23 @@ private async void OpenGitLogs(object sender, RoutedEventArgs e) } } + private async void Cleanup(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.Repository repo) + { + await repo.CleanupAsync(); + e.Handled = true; + } + } + private void NavigateToHead(object sender, RoutedEventArgs e) { - if (DataContext is ViewModels.Repository { CurrentBranch: not null } repo) + if (DataContext is ViewModels.Repository { CurrentBranch: { } head } repo) { var repoView = TopLevel.GetTopLevel(this)?.FindDescendantOfType(); - repoView?.LocalBranchTree?.Select(repo.CurrentBranch); + repoView?.LocalBranchTree?.Select(head); - repo.NavigateToCommit(repo.CurrentBranch.Head); + repo.NavigateToCommit(head.Head); e.Handled = true; } } diff --git a/src/Views/RevisionCompare.axaml b/src/Views/RevisionCompare.axaml index 5452a4e5e2..e76bb87962 100644 --- a/src/Views/RevisionCompare.axaml +++ b/src/Views/RevisionCompare.axaml @@ -102,17 +102,19 @@ + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=ChangeViewMode, Mode=TwoWay}"/> - - + + + + @@ -122,7 +124,7 @@ IsVisible="{Binding IsLoading}"/> - + @@ -139,7 +141,7 @@ Focusable="False"/> - + 0 } selected } vm && - sender is ChangeCollectionView view) + if (DataContext is ViewModels.RevisionCompare { ChangeSelection: { Count: > 0 } selection } vm) { var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); @@ -41,7 +40,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e if (storageFile != null) { var saveTo = storageFile.Path.LocalPath; - await vm.SaveChangesAsPatchAsync(selected, saveTo); + await vm.SaveChangesAsPatchAsync(selection.Changes, saveTo); } } catch (Exception exception) @@ -52,37 +51,47 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e e.Handled = true; }; + var selectedSingleFolder = selection.IsSingleFolder; + var fullPathOfFolder = selectedSingleFolder ? vm.GetAbsPath(selection.SingleFolderPath) : null; + var relativePathOfFolder = selectedSingleFolder ? selection.SingleFolderPath : null; + var menu = new ContextMenu(); - if (selected.Count == 1) + if (selection.Count == 1) { - var change = selected[0]; + var change = selection.Changes[0]; var changeFullPath = vm.GetAbsPath(change.Path); - var openWithMerger = new MenuItem(); - openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); - openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; - openWithMerger.Click += (_, ev) => + if (!selection.HasFolder) { - vm.OpenChangeWithExternalDiffTool(change); - ev.Handled = true; - }; - menu.Items.Add(openWithMerger); + var openWithMerger = new MenuItem(); + openWithMerger.Header = App.Text("OpenInExternalMergeTool"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; + openWithMerger.Click += (_, ev) => + { + vm.OpenChangeWithExternalDiffTool(change); + ev.Handled = true; + }; + menu.Items.Add(openWithMerger); + } - if (change.Index != Models.ChangeState.Deleted) + if (!selection.HasFolder || selectedSingleFolder) { var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); explore.Icon = this.CreateMenuIcon("Icons.Explore"); - explore.IsEnabled = File.Exists(changeFullPath); + explore.IsEnabled = selectedSingleFolder ? Directory.Exists(fullPathOfFolder) : File.Exists(changeFullPath); explore.Click += (_, ev) => { - Native.OS.OpenInFileManager(changeFullPath); + Native.OS.OpenInFileManager(selectedSingleFolder ? fullPathOfFolder : changeFullPath); ev.Handled = true; }; menu.Items.Add(explore); } + if (menu.Items.Count > 0) + menu.Items.Add(new MenuItem() { Header = "-" }); + var resetToLeft = new MenuItem(); resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.LeftSideDesc); resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout"); @@ -109,7 +118,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await this.CopyTextAsync(change.Path); + await this.CopyTextAsync(selectedSingleFolder ? relativePathOfFolder : change.Path); ev.Handled = true; }; @@ -119,11 +128,10 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - await this.CopyTextAsync(changeFullPath); + await this.CopyTextAsync(selectedSingleFolder ? fullPathOfFolder : changeFullPath); ev.Handled = true; }; - menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(patch); menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(resetToLeft); @@ -134,13 +142,29 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e } else { + if (selectedSingleFolder) + { + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); + explore.IsEnabled = Directory.Exists(fullPathOfFolder); + explore.Click += (_, ev) => + { + Native.OS.OpenInFileManager(fullPathOfFolder); + ev.Handled = true; + }; + + menu.Items.Add(explore); + menu.Items.Add(new MenuItem() { Header = "-" }); + } + var resetToLeft = new MenuItem(); resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.LeftSideDesc); resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToLeft.IsEnabled = vm.CanResetToLeft; resetToLeft.Click += async (_, ev) => { - await vm.ResetMultipleToLeftAsync(selected); + await vm.ResetMultipleToLeftAsync(selection.Changes); ev.Handled = true; }; @@ -150,7 +174,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e resetToRight.IsEnabled = vm.CanResetToRight; resetToRight.Click += async (_, ev) => { - await vm.ResetMultipleToRightAsync(selected); + await vm.ResetMultipleToRightAsync(selection.Changes); ev.Handled = true; }; @@ -160,11 +184,19 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - var builder = new StringBuilder(); - foreach (var c in selected) - builder.AppendLine(c.Path); + if (selectedSingleFolder) + { + await this.CopyTextAsync(relativePathOfFolder); + } + else + { + var builder = new StringBuilder(); + foreach (var c in selection.Changes) + builder.AppendLine(c.Path); + + await this.CopyTextAsync(builder.ToString()); + } - await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; @@ -174,11 +206,19 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - var builder = new StringBuilder(); - foreach (var c in selected) - builder.AppendLine(vm.GetAbsPath(c.Path)); + if (selectedSingleFolder) + { + await this.CopyTextAsync(fullPathOfFolder); + } + else + { + var builder = new StringBuilder(); + foreach (var c in selection.Changes) + builder.AppendLine(vm.GetAbsPath(c.Path)); + + await this.CopyTextAsync(builder.ToString()); + } - await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; @@ -191,7 +231,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e menu.Items.Add(copyFullPath); } - menu.Open(view); + menu.Open(sender as Control); } e.Handled = true; @@ -235,10 +275,7 @@ private async void OnSaveAsPatch(object sender, RoutedEventArgs e) private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) { - if (DataContext is not ViewModels.RevisionCompare vm) - return; - - if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges }) + if (DataContext is not ViewModels.RevisionCompare { ChangeSelection: { Count: > 0 } selection } vm) return; var cmdKey = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; @@ -246,13 +283,17 @@ private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) { var builder = new StringBuilder(); var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift); - if (selectedChanges.Count == 1) + if (selection.IsSingleFolder) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(selection.SingleFolderPath) : selection.SingleFolderPath); + } + else if (selection.Changes.Count == 1) { - builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path); + builder.Append(copyAbsPath ? vm.GetAbsPath(selection.Changes[0].Path) : selection.Changes[0].Path); } else { - foreach (var c in selectedChanges) + foreach (var c in selection.Changes) builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); } diff --git a/src/Views/RevisionFileContentViewer.axaml b/src/Views/RevisionFileContentViewer.axaml index 3422578fe2..f62620f9d1 100644 --- a/src/Views/RevisionFileContentViewer.axaml +++ b/src/Views/RevisionFileContentViewer.axaml @@ -11,11 +11,16 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -96,7 +103,8 @@ + Foreground="{DynamicResource Brush.FG2}" + FontFeatures="+tnum"/> + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=ChangeViewMode, Mode=TwoWay}"/> - @@ -147,17 +155,15 @@ Focusable="False"/> - - - - - - - + + + + + diff --git a/src/Views/StashesPage.axaml.cs b/src/Views/StashesPage.axaml.cs index 2c9af6e9da..678337c457 100644 --- a/src/Views/StashesPage.axaml.cs +++ b/src/Views/StashesPage.axaml.cs @@ -22,10 +22,10 @@ private void OnMainLayoutSizeChanged(object sender, SizeChangedEventArgs e) var layout = ViewModels.Preferences.Instance.Layout; var width = grid.Bounds.Width; - var maxLeft = width - 304; + var leftWidth = Math.Max(width - 304, 300); - if (layout.StashesLeftWidth.Value - maxLeft > 1.0) - layout.StashesLeftWidth = new GridLength(maxLeft, GridUnitType.Pixel); + if (layout.StashesLeftWidth.Value - leftWidth > 1.0) + layout.StashesLeftWidth = new GridLength(leftWidth, GridUnitType.Pixel); } private async void OnStashListKeyDown(object sender, KeyEventArgs e) @@ -141,39 +141,48 @@ private void OnStashDoubleTapped(object sender, TappedEventArgs e) private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e) { - if (DataContext is ViewModels.StashesPage { SelectedChanges: { Count: > 0 } selected } vm && - sender is ChangeCollectionView view) + if (DataContext is ViewModels.StashesPage { ChangeSelection: { Count: > 0 } selection } vm) { - var menu = new ContextMenu(); + var selectedSingleFolder = selection.IsSingleFolder; + var fullPathOfFolder = selectedSingleFolder ? vm.GetAbsPath(selection.SingleFolderPath) : null; + var relativePathOfFolder = selectedSingleFolder ? selection.SingleFolderPath : null; - if (selected.Count == 1) + var menu = new ContextMenu(); + if (selection.Count == 1) { - var change = selected[0]; - var fullPath = vm.GetAbsPath(change.Path); - - var openWithMerger = new MenuItem(); - openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); - openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; - openWithMerger.Click += (_, ev) => + var change = selection.Changes[0]; + var changeFullPath = vm.GetAbsPath(change.Path); + + if (!selection.HasFolder) { - vm.OpenChangeWithExternalDiffTool(change); - ev.Handled = true; - }; - - var explore = new MenuItem(); - explore.Header = App.Text("RevealFile"); - explore.Icon = this.CreateMenuIcon("Icons.Explore"); - explore.IsEnabled = File.Exists(fullPath); - explore.Click += (_, ev) => + var openWithMerger = new MenuItem(); + openWithMerger.Header = App.Text("OpenInExternalMergeTool"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; + openWithMerger.Click += (_, ev) => + { + vm.OpenChangeWithExternalDiffTool(change); + ev.Handled = true; + }; + menu.Items.Add(openWithMerger); + } + + if (!selection.HasFolder || selectedSingleFolder) { - Native.OS.OpenInFileManager(fullPath); - ev.Handled = true; - }; + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); + explore.IsEnabled = selectedSingleFolder ? Directory.Exists(fullPathOfFolder) : File.Exists(changeFullPath); + explore.Click += (_, ev) => + { + Native.OS.OpenInFileManager(selectedSingleFolder ? fullPathOfFolder : changeFullPath); + ev.Handled = true; + }; + menu.Items.Add(explore); + } - menu.Items.Add(openWithMerger); - menu.Items.Add(explore); - menu.Items.Add(new MenuItem { Header = "-" }); + if (menu.Items.Count > 0) + menu.Items.Add(new MenuItem { Header = "-" }); } var applyChanges = new MenuItem(); @@ -181,7 +190,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e applyChanges.Icon = this.CreateMenuIcon("Icons.Diff"); applyChanges.Click += async (_, ev) => { - await vm.ApplySelectedChanges(selected); + await vm.ApplySelectedChanges(selection.Changes); ev.Handled = true; }; @@ -190,7 +199,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e checkoutFiles.Icon = this.CreateMenuIcon("Icons.File.Checkout"); checkoutFiles.Click += async (_, ev) => { - await vm.CheckoutFilesAsync(selected); + await vm.CheckoutFilesAsync(selection.Changes); ev.Handled = true; }; @@ -200,14 +209,18 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - if (selected.Count == 1) + if (selectedSingleFolder) + { + await this.CopyTextAsync(relativePathOfFolder); + } + else if (selection.Changes.Count == 1) { - await this.CopyTextAsync(selected[0].Path); + await this.CopyTextAsync(selection.Changes[0].Path); } else { var builder = new StringBuilder(); - foreach (var c in selected) + foreach (var c in selection.Changes) builder.AppendLine(c.Path); await this.CopyTextAsync(builder.ToString()); @@ -222,14 +235,18 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - if (selected.Count == 1) + if (selectedSingleFolder) + { + await this.CopyTextAsync(fullPathOfFolder); + } + else if (selection.Changes.Count == 1) { - await this.CopyTextAsync(vm.GetAbsPath(selected[0].Path)); + await this.CopyTextAsync(vm.GetAbsPath(selection.Changes[0].Path)); } else { var builder = new StringBuilder(); - foreach (var c in selected) + foreach (var c in selection.Changes) builder.AppendLine(vm.GetAbsPath(c.Path)); await this.CopyTextAsync(builder.ToString()); @@ -243,7 +260,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e menu.Items.Add(new MenuItem { Header = "-" }); menu.Items.Add(copyPath); menu.Items.Add(copyFullPath); - menu.Open(view); + menu.Open(sender as Control); } e.Handled = true; @@ -251,23 +268,24 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) { - if (DataContext is not ViewModels.StashesPage vm) - return; - - if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges }) + if (DataContext is not ViewModels.StashesPage { ChangeSelection: { Count: > 0 } selection } vm) return; if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control) && e.Key == Key.C) { var builder = new StringBuilder(); var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift); - if (selectedChanges.Count == 1) + if (selection.IsSingleFolder) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(selection.SingleFolderPath) : selection.SingleFolderPath); + } + else if (selection.Changes.Count == 1) { - builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path); + builder.Append(copyAbsPath ? vm.GetAbsPath(selection.Changes[0].Path) : selection.Changes[0].Path); } else { - foreach (var c in selectedChanges) + foreach (var c in selection.Changes) builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); } diff --git a/src/Views/Statistics.axaml b/src/Views/Statistics.axaml index 8a8ebd6489..48a26774cf 100644 --- a/src/Views/Statistics.axaml +++ b/src/Views/Statistics.axaml @@ -5,19 +5,22 @@ xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" xmlns:v="using:SourceGit.Views" + xmlns:c="using:SourceGit.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.Statistics" x:DataType="vm:Statistics" x:Name="ThisControl" Title="{DynamicResource Text.Statistics}" - Width="800" Height="500" + Width="1080" Height="720" + MinWidth="1080" MinHeight="720" WindowStartupLocation="CenterOwner" - CanResize="False"> + CanResize="True"> @@ -133,7 +136,7 @@ + + + @@ -198,49 +210,129 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + - - - - - + + + + + diff --git a/src/Views/SubmoduleRevisionCompare.axaml b/src/Views/SubmoduleRevisionCompare.axaml index c0c8bba0b7..e94888b750 100644 --- a/src/Views/SubmoduleRevisionCompare.axaml +++ b/src/Views/SubmoduleRevisionCompare.axaml @@ -134,15 +134,15 @@ + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=ChangeViewMode, Mode=TwoWay}"/> - @@ -171,7 +171,7 @@ Focusable="False"/> - + 0 } selected } vm && - sender is ChangeCollectionView view) + if (DataContext is ViewModels.SubmoduleRevisionCompare { ChangeSelection: { Count: > 0 } selection } vm) { - var menu = new ContextMenu(); - var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); patch.Icon = this.CreateMenuIcon("Icons.Save"); @@ -42,7 +39,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e if (storageFile != null) { var saveTo = storageFile.Path.LocalPath; - var succ = await vm.SaveChangesAsPatchAsync(selected, saveTo); + var succ = await vm.SaveChangesAsPatchAsync(selection.Changes, saveTo); if (succ) await new Alert().ShowAsync(this, "Save patch successfully.", false); } @@ -55,37 +52,46 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e e.Handled = true; }; - if (selected.Count == 1) + var selectedSingleFolder = selection.IsSingleFolder; + var fullPathOfFolder = selectedSingleFolder ? vm.GetAbsPath(selection.SingleFolderPath) : null; + var relativePathOfFolder = selectedSingleFolder ? selection.SingleFolderPath : null; + + var menu = new ContextMenu(); + if (selection.Count == 1) { - var change = selected[0]; - var openWithMerger = new MenuItem(); - openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); - openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; - openWithMerger.Click += (_, ev) => + var change = selection.Changes[0]; + var changeFullPath = vm.GetAbsPath(change.Path); + + if (!selection.HasFolder) { - vm.OpenInExternalDiffTool(change); - ev.Handled = true; - }; - menu.Items.Add(openWithMerger); + var openWithMerger = new MenuItem(); + openWithMerger.Header = App.Text("OpenInExternalMergeTool"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; + openWithMerger.Click += (_, ev) => + { + vm.OpenInExternalDiffTool(change); + ev.Handled = true; + }; + menu.Items.Add(openWithMerger); + } - if (change.Index != Models.ChangeState.Deleted) + if (!selection.HasFolder || selectedSingleFolder) { - var full = vm.GetAbsPath(change.Path); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); explore.Icon = this.CreateMenuIcon("Icons.Explore"); - explore.IsEnabled = File.Exists(full); + explore.IsEnabled = selectedSingleFolder ? Directory.Exists(fullPathOfFolder) : File.Exists(changeFullPath); explore.Click += (_, ev) => { - Native.OS.OpenInFileManager(full); + Native.OS.OpenInFileManager(selectedSingleFolder ? fullPathOfFolder : changeFullPath); ev.Handled = true; }; menu.Items.Add(explore); } - menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(patch); + if (menu.Items.Count > 0) + menu.Items.Add(new MenuItem() { Header = "-" }); var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); @@ -93,7 +99,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await this.CopyTextAsync(change.Path); + await this.CopyTextAsync(selectedSingleFolder ? relativePathOfFolder : change.Path); ev.Handled = true; }; @@ -103,16 +109,33 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - await this.CopyTextAsync(vm.GetAbsPath(change.Path)); + await this.CopyTextAsync(selectedSingleFolder ? fullPathOfFolder : changeFullPath); ev.Handled = true; }; + menu.Items.Add(patch); menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(copyPath); menu.Items.Add(copyFullPath); } else { + if (selectedSingleFolder) + { + var explore = new MenuItem(); + explore.Header = App.Text("RevealFile"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); + explore.IsEnabled = Directory.Exists(fullPathOfFolder); + explore.Click += (_, ev) => + { + Native.OS.OpenInFileManager(fullPathOfFolder); + ev.Handled = true; + }; + + menu.Items.Add(explore); + menu.Items.Add(new MenuItem() { Header = "-" }); + } + menu.Items.Add(patch); var copyPath = new MenuItem(); @@ -121,11 +144,19 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - var builder = new StringBuilder(); - foreach (var c in selected) - builder.AppendLine(c.Path); + if (selectedSingleFolder) + { + await this.CopyTextAsync(relativePathOfFolder); + } + else + { + var builder = new StringBuilder(); + foreach (var c in selection.Changes) + builder.AppendLine(c.Path); + + await this.CopyTextAsync(builder.ToString()); + } - await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; @@ -135,11 +166,19 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - var builder = new StringBuilder(); - foreach (var c in selected) - builder.AppendLine(vm.GetAbsPath(c.Path)); + if (selectedSingleFolder) + { + await this.CopyTextAsync(fullPathOfFolder); + } + else + { + var builder = new StringBuilder(); + foreach (var c in selection.Changes) + builder.AppendLine(vm.GetAbsPath(c.Path)); + + await this.CopyTextAsync(builder.ToString()); + } - await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; @@ -148,7 +187,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e menu.Items.Add(copyFullPath); } - menu.Open(view); + menu.Open(sender as Control); } e.Handled = true; @@ -156,10 +195,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) { - if (DataContext is not ViewModels.SubmoduleRevisionCompare vm) - return; - - if (sender is not ChangeCollectionView { SelectedChanges: { Count: > 0 } selectedChanges }) + if (DataContext is not ViewModels.SubmoduleRevisionCompare { ChangeSelection: { Count: > 0 } selection } vm) return; var cmdKey = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control; @@ -167,13 +203,17 @@ private async void OnChangeCollectionViewKeyDown(object sender, KeyEventArgs e) { var builder = new StringBuilder(); var copyAbsPath = e.KeyModifiers.HasFlag(KeyModifiers.Shift); - if (selectedChanges.Count == 1) + if (selection.IsSingleFolder) + { + builder.Append(copyAbsPath ? vm.GetAbsPath(selection.SingleFolderPath) : selection.SingleFolderPath); + } + else if (selection.Count == 1) { - builder.Append(copyAbsPath ? vm.GetAbsPath(selectedChanges[0].Path) : selectedChanges[0].Path); + builder.Append(copyAbsPath ? vm.GetAbsPath(selection.Changes[0].Path) : selection.Changes[0].Path); } else { - foreach (var c in selectedChanges) + foreach (var c in selection.Changes) builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); } diff --git a/src/Views/SubmodulesView.axaml b/src/Views/SubmodulesView.axaml index cf7a6f95a3..b339b0abc3 100644 --- a/src/Views/SubmodulesView.axaml +++ b/src/Views/SubmodulesView.axaml @@ -106,7 +106,7 @@ ContextRequested="OnItemContextRequested" ToolTip.Tip="{Binding Module}" ToolTip.Placement="Right"> - - + @@ -140,7 +139,7 @@ - +