Optimize editor initialization and multi-file diff loading

- Share immutable built-in document commands, editor command tables, default keybindings, and syntax color scheme storage across editor instances. Preserve mutations through copy-on-write storage and explicit command-removal tracking.
  - Use ShortcutMap consistently with unordered lookup while retaining deterministic ordering where serialization or conflict resolution depends on it. Cache ambiguous syntax-extension checks and invalidate the cache when language definitions change.
  - Prepare Git commit-detail diffs and sub-line changes on worker threads with cancellation and stale-request protection. Release raw patch memory before UI construction and add regression coverage for prepared and cancelled diffs.
  - Add Diff Changed support and enable collapsible file headers for Diff HEAD, Diff Staged, and Diff Changes.
  - Update translations, compatibility call sites, tests, and the efsw dependency.
This commit is contained in:
Martín Lucas Golini
2026-08-30 17:35:50 -03:00
parent 3afcb3f2d2
commit 8e5d180a4d
45 changed files with 937 additions and 489 deletions
+1
View File
@@ -373,6 +373,7 @@ Zweignamen eingeben:</string>
<string name="git_create_local_branch">Lokalen Zweig erstellen?</string> <string name="git_create_local_branch">Lokalen Zweig erstellen?</string>
<string name="git_delete_branch">Löschen</string> <string name="git_delete_branch">Löschen</string>
<string name="git_diff_head">HEAD-Diff</string> <string name="git_diff_head">HEAD-Diff</string>
<string name="git_diff_changed">Änderungs-Diff</string>
<string name="git_diff_staged">Stage-Diff</string> <string name="git_diff_staged">Stage-Diff</string>
<string name="git_discard">Verwerfen</string> <string name="git_discard">Verwerfen</string>
<string name="git_discard_all">Alles verwerfen</string> <string name="git_discard_all">Alles verwerfen</string>
+1
View File
@@ -357,6 +357,7 @@ Enter the name for the branch:</string>
<string name="git_create_local_branch">Create local branch?</string> <string name="git_create_local_branch">Create local branch?</string>
<string name="git_delete_branch">Delete</string> <string name="git_delete_branch">Delete</string>
<string name="git_diff_head">Diff HEAD</string> <string name="git_diff_head">Diff HEAD</string>
<string name="git_diff_changed">Diff Changed</string>
<string name="git_diff_staged">Diff Staged</string> <string name="git_diff_staged">Diff Staged</string>
<string name="git_discard">Discard</string> <string name="git_discard">Discard</string>
<string name="git_discard_all">Discard All</string> <string name="git_discard_all">Discard All</string>
+1
View File
@@ -356,6 +356,7 @@ Entrez le nom de la branche :</string>
<string name="git_create_local_branch">Créer une branche locale ?</string> <string name="git_create_local_branch">Créer une branche locale ?</string>
<string name="git_delete_branch">Supprimer</string> <string name="git_delete_branch">Supprimer</string>
<string name="git_diff_head">Diff HEAD</string> <string name="git_diff_head">Diff HEAD</string>
<string name="git_diff_changed">Diff des modifications</string>
<string name="git_diff_staged">Diff Staged</string> <string name="git_diff_staged">Diff Staged</string>
<string name="git_discard">Annuler</string> <string name="git_discard">Annuler</string>
<string name="git_discard_all">Tout annuler</string> <string name="git_discard_all">Tout annuler</string>
+1
View File
@@ -277,6 +277,7 @@
Enter the name for the branch:</string> Enter the name for the branch:</string>
<string name="git_create_local_branch">新建本地分支?</string> <string name="git_create_local_branch">新建本地分支?</string>
<string name="git_delete_branch">删除</string> <string name="git_delete_branch">删除</string>
<string name="git_diff_changed">更改差异</string>
<string name="git_discard">Discard</string> <string name="git_discard">Discard</string>
<string name="git_drop_stash">Drop Stash</string> <string name="git_drop_stash">Drop Stash</string>
<string name="git_drop_stash_title">Drop Stash</string> <string name="git_drop_stash_title">Drop Stash</string>
+10 -5
View File
@@ -3,7 +3,8 @@
#include <eepp/config.hpp> #include <eepp/config.hpp>
#if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || ( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) ) #if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || \
( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) )
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#else #else
@@ -13,9 +14,12 @@
namespace EE { namespace EE {
#if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || ( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) ) #if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || \
( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) )
template <typename Key, typename Value> using UnorderedMap = std::unordered_map<Key, Value>; template <typename Key, typename Value, typename Hash = std::hash<Key>,
typename KeyEqual = std::equal_to<Key>>
using UnorderedMap = std::unordered_map<Key, Value, Hash, KeyEqual>;
template <typename Key> using UnorderedSet = std::unordered_set<Key>; template <typename Key> using UnorderedSet = std::unordered_set<Key>;
@@ -28,8 +32,9 @@ using SmallUnorderedSet = std::unordered_set<Key>;
#else #else
template <typename Key, typename Value> template <typename Key, typename Value, typename Hash = ankerl::unordered_dense::hash<Key>,
using UnorderedMap = ankerl::unordered_dense::map<Key, Value>; typename KeyEqual = std::equal_to<Key>>
using UnorderedMap = ankerl::unordered_dense::map<Key, Value, Hash, KeyEqual>;
template <typename Key> using UnorderedSet = ankerl::unordered_dense::set<Key>; template <typename Key> using UnorderedSet = ankerl::unordered_dense::set<Key>;
+10 -3
View File
@@ -2,6 +2,7 @@
#define EE_UI_DOC_SYNTAXCOLORSCHEME_HPP #define EE_UI_DOC_SYNTAXCOLORSCHEME_HPP
#include <eepp/system/color.hpp> #include <eepp/system/color.hpp>
#include <memory>
#include <vector> #include <vector>
namespace EE { namespace System { namespace EE { namespace System {
@@ -317,11 +318,17 @@ class EE_API SyntaxColorScheme {
void setName( const std::string& name ); void setName( const std::string& name );
protected: protected:
std::string mName; struct Storage {
UnorderedMap<SyntaxStyleType, Style> mSyntaxColors; std::string name;
UnorderedMap<SyntaxStyleType, Style> mEditorColors; UnorderedMap<SyntaxStyleType, Style> syntaxColors;
UnorderedMap<SyntaxStyleType, Style> editorColors;
};
std::shared_ptr<Storage> mStorage;
mutable UnorderedMap<SyntaxStyleType, Style> mStyleCache; mutable UnorderedMap<SyntaxStyleType, Style> mStyleCache;
void ensureUniqueStorage();
template <typename SyntaxStyleType> template <typename SyntaxStyleType>
const SyntaxColorScheme::Style& getSyntaxStyleFromCache( const SyntaxStyleType& type ) const; const SyntaxColorScheme::Style& getSyntaxStyleFromCache( const SyntaxStyleType& type ) const;
}; };
@@ -2,12 +2,14 @@
#define EE_UI_DOC_SYNTAXSTYLEMANAGER_HPP #define EE_UI_DOC_SYNTAXSTYLEMANAGER_HPP
#include <eepp/config.hpp> #include <eepp/config.hpp>
#include <eepp/core/containers.hpp>
#include <eepp/system/lock.hpp> #include <eepp/system/lock.hpp>
#include <eepp/system/mutex.hpp> #include <eepp/system/mutex.hpp>
#include <eepp/system/singletondeclarations.hpp> #include <eepp/system/singletondeclarations.hpp>
#include <eepp/ui/doc/hextlanguagetype.hpp> #include <eepp/ui/doc/hextlanguagetype.hpp>
#include <eepp/ui/doc/syntaxdefinition.hpp> #include <eepp/ui/doc/syntaxdefinition.hpp>
#include <optional> #include <optional>
#include <unordered_map>
#include <vector> #include <vector>
namespace EE { namespace System { namespace EE { namespace System {
@@ -127,6 +129,7 @@ class EE_API SyntaxDefinitionManager {
std::vector<std::shared_ptr<SyntaxDefinition>> mDefinitions; std::vector<std::shared_ptr<SyntaxDefinition>> mDefinitions;
std::vector<SyntaxPreDefinition> mPreDefinitions; std::vector<SyntaxPreDefinition> mPreDefinitions;
std::map<std::string, std::string> mPriorities; std::map<std::string, std::string> mPriorities;
mutable UnorderedMap<std::string, bool> mExtensionManyLanguagesCache;
FileAssociations mFileAssociations; FileAssociations mFileAssociations;
mutable Mutex mMutex; mutable Mutex mMutex;
mutable Mutex mFileAssociationsMutex; mutable Mutex mFileAssociationsMutex;
+13
View File
@@ -22,6 +22,7 @@
#include <eepp/ui/doc/textrange.hpp> #include <eepp/ui/doc/textrange.hpp>
#include <eepp/ui/doc/textundostack.hpp> #include <eepp/ui/doc/textundostack.hpp>
#include <functional> #include <functional>
#include <memory>
#include <vector> #include <vector>
using namespace EE::System; using namespace EE::System;
@@ -132,6 +133,7 @@ class EE_API TextDocument {
typedef std::function<void()> DocumentCommand; typedef std::function<void()> DocumentCommand;
typedef std::function<void( Client* )> DocumentRefCommand; typedef std::function<void( Client* )> DocumentRefCommand;
typedef UnorderedMap<std::string, DocumentRefCommand> DocumentRefCommands;
TextDocument( bool verbose = true ); TextDocument( bool verbose = true );
@@ -450,6 +452,9 @@ class EE_API TextDocument {
void setCommand( const std::string& command, const DocumentRefCommand& func ); void setCommand( const std::string& command, const DocumentRefCommand& func );
/** Installs an immutable command table shared by documents with the same client type. */
void setSharedRefCommands( std::shared_ptr<const DocumentRefCommands> commands );
bool hasCommand( const std::string& command ); bool hasCommand( const std::string& command );
bool removeCommand( const std::string& command ); bool removeCommand( const std::string& command );
@@ -818,6 +823,8 @@ class EE_API TextDocument {
Uint32 mPageSize{ 10 }; Uint32 mPageSize{ 10 };
UnorderedMap<std::string, DocumentCommand> mCommands; UnorderedMap<std::string, DocumentCommand> mCommands;
UnorderedMap<std::string, DocumentRefCommand> mRefCommands; UnorderedMap<std::string, DocumentRefCommand> mRefCommands;
std::shared_ptr<const DocumentRefCommands> mSharedRefCommands;
std::unique_ptr<UnorderedSet<std::string>> mRemovedDefaultCommands;
String mNonWordChars; String mNonWordChars;
Client* mActiveClient{ nullptr }; Client* mActiveClient{ nullptr };
mutable Mutex mLoadingMutex; mutable Mutex mLoadingMutex;
@@ -831,6 +838,12 @@ class EE_API TextDocument {
void initializeCommands(); void initializeCommands();
using BuiltinDocumentCommand = void ( * )( TextDocument* );
static const UnorderedMap<std::string, BuiltinDocumentCommand>& getBuiltinCommands();
bool isDefaultCommandRemoved( const std::string& command ) const;
void cleanChangeId(); void cleanChangeId();
void notifyDocumentLoaded(); void notifyDocumentLoaded();
+37 -9
View File
@@ -2,8 +2,10 @@
#define EE_UI_KEYBOARDSHORTCUT_HPP #define EE_UI_KEYBOARDSHORTCUT_HPP
#include <eepp/config.hpp> #include <eepp/config.hpp>
#include <eepp/core/containers.hpp>
#include <eepp/window/keycodes.hpp> #include <eepp/window/keycodes.hpp>
#include <map> #include <map>
#include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@@ -20,8 +22,6 @@ class UIWidget;
class EE_API KeyBindings { class EE_API KeyBindings {
public: public:
typedef std::map<Uint64, std::string> ShortcutMap;
struct Shortcut { struct Shortcut {
Shortcut() {} Shortcut() {}
Shortcut( Keycode key, Uint32 mod ) : key( key ), mod( mod ) {} Shortcut( Keycode key, Uint32 mod ) : key( key ), mod( mod ) {}
@@ -31,11 +31,30 @@ class EE_API KeyBindings {
Uint32 mod{ 0 }; Uint32 mod{ 0 };
Uint64 toUint64() const { return (Uint64)mod << 32 | (Uint64)key; } Uint64 toUint64() const { return (Uint64)mod << 32 | (Uint64)key; }
operator Uint64() const { return toUint64(); } operator Uint64() const { return toUint64(); }
bool operator<( const Shortcut& other ) const { return toUint64() < other.toUint64(); }
bool operator==( const Shortcut& other ) const {
return key == other.key && mod == other.mod;
}
bool operator!=( const Shortcut& other ) const { return !( *this == other ); }
bool empty() const { return 0 == mod && 0 == key; } bool empty() const { return 0 == mod && 0 == key; }
}; };
struct ShortcutHash {
size_t operator()( const Shortcut& shortcut ) const noexcept {
return hashCombine( static_cast<size_t>( shortcut.key ),
static_cast<size_t>( shortcut.mod ) );
}
};
typedef UnorderedMap<Shortcut, std::string, ShortcutHash> ShortcutMap;
static KeyBindings::Shortcut sanitizeShortcut( const KeyBindings::Shortcut& shortcut ); static KeyBindings::Shortcut sanitizeShortcut( const KeyBindings::Shortcut& shortcut );
/** Returns the shortcut keys in ascending packed-value order. This provides deterministic
* iteration at boundaries where the order affects the selected reverse binding or serialized
* output, while keeping ShortcutMap optimized for lookup. */
static std::vector<Shortcut> getOrderedShortcuts( const ShortcutMap& bindings );
static std::string keybindFormat( std::string str ); static std::string keybindFormat( std::string str );
static Shortcut toShortcut( const Window::Input* input, const std::string& keys ); static Shortcut toShortcut( const Window::Input* input, const std::string& keys );
@@ -45,14 +64,15 @@ class EE_API KeyBindings {
KeyBindings( const Window::Input* input ); KeyBindings( const Window::Input* input );
/** Shares @p bindings storage while keeping this instance's input source. */
void setKeybinds( const KeyBindings& bindings );
void addKeybindsString( const std::map<std::string, std::string>& binds ); void addKeybindsString( const std::map<std::string, std::string>& binds );
void addKeybinds( const std::map<Shortcut, std::string>& binds ); void addKeybinds( const ShortcutMap& binds );
void addKeybindsStringUnordered( const std::unordered_map<std::string, std::string>& binds ); void addKeybindsStringUnordered( const std::unordered_map<std::string, std::string>& binds );
void addKeybindsUnordered( const std::unordered_map<Shortcut, std::string>& binds );
void addKeybindString( const std::string& key, const std::string& command ); void addKeybindString( const std::string& key, const std::string& command );
void addKeybind( const Shortcut& key, const std::string& command ); void addKeybind( const Shortcut& key, const std::string& command );
@@ -85,23 +105,31 @@ class EE_API KeyBindings {
const ShortcutMap& getShortcutMap() const; const ShortcutMap& getShortcutMap() const;
const std::map<std::string, Uint64>& getKeybindings() const; const std::map<std::string, Shortcut>& getKeybindings() const;
Shortcut getShortcutFromCommand( const std::string& cmd ) const; Shortcut getShortcutFromCommand( const std::string& cmd ) const;
std::string getShortcutString( Shortcut shortcut, bool format = false ) const; std::string getShortcutString( Shortcut shortcut, bool format = false ) const;
protected: protected:
struct Storage {
ShortcutMap shortcuts;
std::map<std::string, Shortcut> keybindingsInvert;
};
const Window::Input* mInput; const Window::Input* mInput;
ShortcutMap mShortcuts; std::shared_ptr<Storage> mStorage;
std::map<std::string, Uint64> mKeybindingsInvert;
static std::shared_ptr<Storage> getEmptyStorage();
void ensureUniqueStorage();
}; };
}} // namespace EE::UI }} // namespace EE::UI
template <> struct std::hash<EE::UI::KeyBindings::Shortcut> { template <> struct std::hash<EE::UI::KeyBindings::Shortcut> {
std::size_t operator()( EE::UI::KeyBindings::Shortcut const& s ) const noexcept { std::size_t operator()( EE::UI::KeyBindings::Shortcut const& s ) const noexcept {
return s.toUint64(); return EE::UI::KeyBindings::ShortcutHash{}( s );
} }
}; };
@@ -17,9 +17,9 @@ namespace EE { namespace UI { namespace Tools {
class EE_API UICodeEditorSplitter { class EE_API UICodeEditorSplitter {
public: public:
static const std::map<KeyBindings::Shortcut, std::string> getDefaultKeybindings(); static const KeyBindings::ShortcutMap getDefaultKeybindings();
static const std::map<KeyBindings::Shortcut, std::string> getLocalDefaultKeybindings(); static const KeyBindings::ShortcutMap getLocalDefaultKeybindings();
static Uint32 getDefaultSwitchToTabModifier(); static Uint32 getDefaultSwitchToTabModifier();
+30 -1
View File
@@ -1,10 +1,12 @@
#ifndef EE_UI_TOOLS_UIDIFFVIEW_HPP #ifndef EE_UI_TOOLS_UIDIFFVIEW_HPP
#define EE_UI_TOOLS_UIDIFFVIEW_HPP #define EE_UI_TOOLS_UIDIFFVIEW_HPP
#include <atomic>
#include <eepp/ui/uicodeeditor.hpp> #include <eepp/ui/uicodeeditor.hpp>
#include <eepp/ui/uilinearlayout.hpp> #include <eepp/ui/uilinearlayout.hpp>
#include <eepp/ui/uiselectbutton.hpp> #include <eepp/ui/uiselectbutton.hpp>
#include <eepp/ui/widgetcommandexecuter.hpp> #include <eepp/ui/widgetcommandexecuter.hpp>
#include <memory>
namespace EE { namespace EE {
@@ -23,6 +25,8 @@ class UIDiffEditorPlugin;
class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter { class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter {
public: public:
class PreparedMultiFileDiff;
enum class ViewMode { Unified, SideBySide }; enum class ViewMode { Unified, SideBySide };
enum class SubLineDiffAlgorithm { LCS, SES }; enum class SubLineDiffAlgorithm { LCS, SES };
@@ -30,7 +34,21 @@ class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter {
static UIScrollView* NewMultiFileDiffViewer( const std::string& patchText, static UIScrollView* NewMultiFileDiffViewer( const std::string& patchText,
const std::string& repoPath = "", const std::string& repoPath = "",
ViewMode viewMode = ViewMode::Unified ); ViewMode viewMode = ViewMode::Unified,
bool interactiveFileHeaders = false );
static UIScrollView*
NewMultiFileDiffViewer( std::shared_ptr<PreparedMultiFileDiff> preparedDiff,
const std::string& repoPath = "", ViewMode viewMode = ViewMode::Unified,
bool interactiveFileHeaders = false );
/**
* Parses patches and computes sub-line changes without touching UI, documents, or syntax
* definitions. This is safe to run on a worker thread. Returns null when cancelled.
*/
static std::shared_ptr<PreparedMultiFileDiff>
prepareMultiFileDiff( const std::string& patchText,
const std::shared_ptr<std::atomic_bool>& cancelled = {} );
static std::vector<std::string> splitDiff( const std::string& multiFileDiff ); static std::vector<std::string> splitDiff( const std::string& multiFileDiff );
@@ -109,6 +127,7 @@ class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter {
bool isInteractiveFileHeader() const { return mInteractiveFileHeader; } bool isInteractiveFileHeader() const { return mInteractiveFileHeader; }
void setCollapsed( bool collapsed ); void setCollapsed( bool collapsed );
bool isCollapsed() const { return mCollapsed; } bool isCollapsed() const { return mCollapsed; }
bool areHeadersVisible() const { return mHeadersVisible; } bool areHeadersVisible() const { return mHeadersVisible; }
@@ -128,6 +147,8 @@ class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter {
void setAutoDeleteOldTempImage( bool set ) { mAutoDeleteOldTempImage = set; } void setAutoDeleteOldTempImage( bool set ) { mAutoDeleteOldTempImage = set; }
protected: protected:
struct PreparedPatch;
UICodeEditor* mEditor{ nullptr }; UICodeEditor* mEditor{ nullptr };
UICodeEditor* mLeftEditor{ nullptr }; UICodeEditor* mLeftEditor{ nullptr };
UICodeEditor* mRightEditor{ nullptr }; UICodeEditor* mRightEditor{ nullptr };
@@ -179,6 +200,14 @@ class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter {
void computeSubLineDiff( DiffLine& oldLine, DiffLine& newLine ); void computeSubLineDiff( DiffLine& oldLine, DiffLine& newLine );
static PreparedPatch preparePatch( const std::string& patchText,
const std::string& originalFilePath,
SubLineDiffAlgorithm algorithm,
const std::shared_ptr<std::atomic_bool>& cancelled );
void loadPreparedPatch( PreparedPatch&& patch, const std::string& originalFilePath,
const std::string& oldFilePath, const std::string& repoPath );
void updateEditorsText(); void updateEditorsText();
void updateButtonsText(); void updateButtonsText();
+4 -2
View File
@@ -219,7 +219,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
static UICodeEditor* NewOpt( const bool& autoRegisterBaseCommands, static UICodeEditor* NewOpt( const bool& autoRegisterBaseCommands,
const bool& autoRegisterBaseKeybindings ); const bool& autoRegisterBaseKeybindings );
static const std::map<KeyBindings::Shortcut, std::string> getDefaultKeybindings(); static std::shared_ptr<const KeyBindings> getDefaultKeybindings();
static const MouseBindings::ShortcutMap getDefaultMousebindings(); static const MouseBindings::ShortcutMap getDefaultMousebindings();
@@ -407,7 +407,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
void addKeyBindsString( const std::map<std::string, std::string>& binds, void addKeyBindsString( const std::map<std::string, std::string>& binds,
const bool& allowLocked = false ); const bool& allowLocked = false );
void addKeyBinds( const std::map<KeyBindings::Shortcut, std::string>& binds, void addKeyBinds( const KeyBindings::ShortcutMap& binds,
const bool& allowLocked = false ); const bool& allowLocked = false );
const bool& getHighlightCurrentLine() const; const bool& getHighlightCurrentLine() const;
@@ -896,6 +896,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
const Tools::UIDocFindReplace* getFindReplace() const { return mFindReplace; } const Tools::UIDocFindReplace* getFindReplace() const { return mFindReplace; }
protected: protected:
static std::shared_ptr<const TextDocument::DocumentRefCommands> getDefaultEditorCommands();
struct LastXOffset { struct LastXOffset {
TextPosition position{ 0, 0 }; TextPosition position{ 0, 0 };
Float offset{ 0.f }; Float offset{ 0.f };
+1 -1
View File
@@ -668,7 +668,7 @@ class EE_API UISceneNode : public SceneNode {
* *
* @param binds Map of KeyBindings::Shortcut to command strings. * @param binds Map of KeyBindings::Shortcut to command strings.
*/ */
void addKeyBinds( const std::map<KeyBindings::Shortcut, std::string>& binds ); void addKeyBinds( const KeyBindings::ShortcutMap& binds );
typedef std::function<void()> KeyBindingCommand; typedef std::function<void()> KeyBindingCommand;
+2 -1
View File
@@ -191,9 +191,10 @@ class EE_API UIWindow : public UIWidget {
void addKeyBindsString( const std::map<std::string, std::string>& binds ); void addKeyBindsString( const std::map<std::string, std::string>& binds );
void addKeyBinds( const std::map<KeyBindings::Shortcut, std::string>& binds ); void addKeyBinds( const KeyBindings::ShortcutMap& binds );
void setKeyBindingCommand( const std::string& command, KeyBindingCommand func ); void setKeyBindingCommand( const std::string& command, KeyBindingCommand func );
void removeKeyBindingCommand( const std::string& command ); void removeKeyBindingCommand( const std::string& command );
void executeKeyBindingCommand( const std::string& command ); void executeKeyBindingCommand( const std::string& command );
+44 -23
View File
@@ -37,7 +37,7 @@ namespace EE { namespace UI { namespace Doc {
// "minimap_highlight" (Minimap text highlight color) // "minimap_highlight" (Minimap text highlight color)
// "minimap_visible_area" (Minimap visible area marker color) // "minimap_visible_area" (Minimap visible area marker color)
SyntaxColorScheme SyntaxColorScheme::getDefaultDark() { static SyntaxColorScheme createDefaultDarkSyntaxColorScheme() {
return { return {
"eepp", "eepp",
{ {
@@ -86,7 +86,12 @@ SyntaxColorScheme SyntaxColorScheme::getDefaultDark() {
{ "minimap_visible_area"_sst, Color( "#FFFFFF0A" ) } } }; { "minimap_visible_area"_sst, Color( "#FFFFFF0A" ) } } };
} }
SyntaxColorScheme SyntaxColorScheme::getDefaultLight() { SyntaxColorScheme SyntaxColorScheme::getDefaultDark() {
static const SyntaxColorScheme colorScheme = createDefaultDarkSyntaxColorScheme();
return colorScheme;
}
static SyntaxColorScheme createDefaultLightSyntaxColorScheme() {
return { return {
"github", "github",
{ {
@@ -135,6 +140,11 @@ SyntaxColorScheme SyntaxColorScheme::getDefaultLight() {
{ "minimap_visible_area"_sst, Color( "#00000011" ) } } }; { "minimap_visible_area"_sst, Color( "#00000011" ) } } };
} }
SyntaxColorScheme SyntaxColorScheme::getDefaultLight() {
static const SyntaxColorScheme colorScheme = createDefaultLightSyntaxColorScheme();
return colorScheme;
}
SyntaxColorScheme::Style parseStyle( SyntaxColorScheme::Style parseStyle(
const std::string& value, bool* colorWasSet = nullptr, const std::string& value, bool* colorWasSet = nullptr,
const UnorderedMap<SyntaxStyleType, SyntaxColorScheme::Style>* syntaxColors = nullptr ) { const UnorderedMap<SyntaxStyleType, SyntaxColorScheme::Style>* syntaxColors = nullptr ) {
@@ -212,11 +222,11 @@ std::vector<SyntaxColorScheme> SyntaxColorScheme::loadFromStream( IOStream& stre
std::string value( ini.getValue( keyIdx, valueIdx ) ); std::string value( ini.getValue( keyIdx, valueIdx ) );
if ( !value.empty() ) { if ( !value.empty() ) {
SyntaxColorScheme::Style style = parseStyle( value ); SyntaxColorScheme::Style style = parseStyle( value );
if ( refColorScheme.mSyntaxColors.find( toSyntaxStyleType( valueName ) ) != if ( refColorScheme.mStorage->syntaxColors.find( toSyntaxStyleType( valueName ) ) !=
refColorScheme.mSyntaxColors.end() ) { refColorScheme.mStorage->syntaxColors.end() ) {
colorScheme.setSyntaxStyle( toSyntaxStyleType( valueName ), style ); colorScheme.setSyntaxStyle( toSyntaxStyleType( valueName ), style );
} else if ( refColorScheme.mEditorColors.find( toSyntaxStyleType( valueName ) ) != } else if ( refColorScheme.mStorage->editorColors.find( toSyntaxStyleType(
refColorScheme.mEditorColors.end() ) { valueName ) ) != refColorScheme.mStorage->editorColors.end() ) {
colorScheme.setEditorSyntaxStyle( toSyntaxStyleType( valueName ), style ); colorScheme.setEditorSyntaxStyle( toSyntaxStyleType( valueName ), style );
} }
} }
@@ -257,20 +267,26 @@ std::vector<SyntaxColorScheme> SyntaxColorScheme::loadFromPack( Pack* pack,
return {}; return {};
} }
SyntaxColorScheme::SyntaxColorScheme() {} SyntaxColorScheme::SyntaxColorScheme() : mStorage( std::make_shared<Storage>() ) {}
SyntaxColorScheme::SyntaxColorScheme( const std::string& name, SyntaxColorScheme::SyntaxColorScheme( const std::string& name,
const UnorderedMap<SyntaxStyleType, Style>& syntaxColors, const UnorderedMap<SyntaxStyleType, Style>& syntaxColors,
const UnorderedMap<SyntaxStyleType, Style>& editorColors ) : const UnorderedMap<SyntaxStyleType, Style>& editorColors ) :
mName( name ), mSyntaxColors( syntaxColors ), mEditorColors( editorColors ) {} mStorage( std::make_shared<Storage>( Storage{ name, syntaxColors, editorColors } ) ) {}
void SyntaxColorScheme::ensureUniqueStorage() {
if ( !mStorage.unique() )
mStorage = std::make_shared<Storage>( *mStorage );
mStyleCache.clear();
}
static const SyntaxColorScheme::Style StyleEmpty = { Color::Transparent }; static const SyntaxColorScheme::Style StyleEmpty = { Color::Transparent };
static const SyntaxColorScheme StyleDefault = SyntaxColorScheme::getDefaultDark(); static const SyntaxColorScheme StyleDefault = SyntaxColorScheme::getDefaultDark();
const SyntaxColorScheme::Style& const SyntaxColorScheme::Style&
SyntaxColorScheme::getSyntaxStyle( const SyntaxStyleType& type ) const { SyntaxColorScheme::getSyntaxStyle( const SyntaxStyleType& type ) const {
auto it = mSyntaxColors.find( type ); auto it = mStorage->syntaxColors.find( type );
if ( it != mSyntaxColors.end() ) if ( it != mStorage->syntaxColors.end() )
return it->second; return it->second;
else if ( type == "keyword2"_sst ) else if ( type == "keyword2"_sst )
return getSyntaxStyle( "type"_sst ); return getSyntaxStyle( "type"_sst );
@@ -292,22 +308,24 @@ SyntaxColorScheme::getSyntaxStyle( const SyntaxStyleType& type ) const {
} }
bool SyntaxColorScheme::hasSyntaxStyle( const SyntaxStyleType& type ) const { bool SyntaxColorScheme::hasSyntaxStyle( const SyntaxStyleType& type ) const {
return mSyntaxColors.find( type ) != mSyntaxColors.end(); return mStorage->syntaxColors.find( type ) != mStorage->syntaxColors.end();
} }
void SyntaxColorScheme::setSyntaxStyles( const UnorderedMap<SyntaxStyleType, Style>& styles ) { void SyntaxColorScheme::setSyntaxStyles( const UnorderedMap<SyntaxStyleType, Style>& styles ) {
mSyntaxColors.insert( styles.begin(), styles.end() ); ensureUniqueStorage();
mStorage->syntaxColors.insert( styles.begin(), styles.end() );
} }
void SyntaxColorScheme::setSyntaxStyle( const SyntaxStyleType& type, void SyntaxColorScheme::setSyntaxStyle( const SyntaxStyleType& type,
const SyntaxColorScheme::Style& style ) { const SyntaxColorScheme::Style& style ) {
mSyntaxColors[type] = style; ensureUniqueStorage();
mStorage->syntaxColors[type] = style;
} }
const SyntaxColorScheme::Style& const SyntaxColorScheme::Style&
SyntaxColorScheme::getEditorSyntaxStyle( const SyntaxStyleType& type ) const { SyntaxColorScheme::getEditorSyntaxStyle( const SyntaxStyleType& type ) const {
auto it = mEditorColors.find( type ); auto it = mStorage->editorColors.find( type );
if ( it != mEditorColors.end() ) if ( it != mStorage->editorColors.end() )
return it->second; return it->second;
if ( type == "widget_background"_sst ) if ( type == "widget_background"_sst )
return getEditorSyntaxStyle( "gutter_background"_sst ); return getEditorSyntaxStyle( "gutter_background"_sst );
@@ -348,20 +366,23 @@ const Color& SyntaxColorScheme::getEditorColor( const SyntaxStyleType& type ) co
void SyntaxColorScheme::setEditorSyntaxStyles( void SyntaxColorScheme::setEditorSyntaxStyles(
const EE::UnorderedMap<SyntaxStyleType, SyntaxColorScheme::Style>& styles ) { const EE::UnorderedMap<SyntaxStyleType, SyntaxColorScheme::Style>& styles ) {
mEditorColors.insert( styles.begin(), styles.end() ); ensureUniqueStorage();
mStorage->editorColors.insert( styles.begin(), styles.end() );
} }
void SyntaxColorScheme::setEditorSyntaxStyle( const SyntaxStyleType& type, void SyntaxColorScheme::setEditorSyntaxStyle( const SyntaxStyleType& type,
const SyntaxColorScheme::Style& style ) { const SyntaxColorScheme::Style& style ) {
mEditorColors[type] = style; ensureUniqueStorage();
mStorage->editorColors[type] = style;
} }
const std::string& SyntaxColorScheme::getName() const { const std::string& SyntaxColorScheme::getName() const {
return mName; return mStorage->name;
} }
void SyntaxColorScheme::setName( const std::string& name ) { void SyntaxColorScheme::setName( const std::string& name ) {
mName = name; ensureUniqueStorage();
mStorage->name = name;
} }
template <typename SyntaxStyleType> template <typename SyntaxStyleType>
@@ -369,18 +390,18 @@ const SyntaxColorScheme::Style&
SyntaxColorScheme::getSyntaxStyleFromCache( const SyntaxStyleType& type ) const { SyntaxColorScheme::getSyntaxStyleFromCache( const SyntaxStyleType& type ) const {
bool colorWasSet = false; bool colorWasSet = false;
if constexpr ( std::is_same_v<SyntaxStyleType, std::string> ) if constexpr ( std::is_same_v<SyntaxStyleType, std::string> )
mStyleCache[type] = parseStyle( type, &colorWasSet, &mSyntaxColors ); mStyleCache[type] = parseStyle( type, &colorWasSet, &mStorage->syntaxColors );
else { else {
auto cache = SyntaxPattern::SyntaxStyleTypeCache.find( type ); auto cache = SyntaxPattern::SyntaxStyleTypeCache.find( type );
if ( cache != SyntaxPattern::SyntaxStyleTypeCache.end() ) { if ( cache != SyntaxPattern::SyntaxStyleTypeCache.end() ) {
mStyleCache[type] = parseStyle( cache->second, &colorWasSet, &mSyntaxColors ); mStyleCache[type] = parseStyle( cache->second, &colorWasSet, &mStorage->syntaxColors );
} else { } else {
return StyleEmpty; return StyleEmpty;
} }
} }
if ( !colorWasSet ) { if ( !colorWasSet ) {
auto normalStyle = mSyntaxColors.find( "normal"_sst ); auto normalStyle = mStorage->syntaxColors.find( "normal"_sst );
if ( normalStyle != mSyntaxColors.end() ) if ( normalStyle != mStorage->syntaxColors.end() )
mStyleCache[type].color = normalStyle->second.color; mStyleCache[type].color = normalStyle->second.color;
} }
return mStyleCache[type]; return mStyleCache[type];
+45 -42
View File
@@ -567,12 +567,14 @@ SyntaxDefinition& SyntaxDefinitionManager::add( SyntaxDefinition&& syntaxStyle )
syntaxStyle.mLanguageIndex = mDefinitions.size(); syntaxStyle.mLanguageIndex = mDefinitions.size();
syntaxStyle.compile(); syntaxStyle.compile();
mDefinitions.emplace_back( std::make_shared<SyntaxDefinition>( std::move( syntaxStyle ) ) ); mDefinitions.emplace_back( std::make_shared<SyntaxDefinition>( std::move( syntaxStyle ) ) );
mExtensionManyLanguagesCache.clear();
return *mDefinitions.back().get(); return *mDefinitions.back().get();
} }
void SyntaxDefinitionManager::addPreDefinition( SyntaxPreDefinition&& preDefinition ) { void SyntaxDefinitionManager::addPreDefinition( SyntaxPreDefinition&& preDefinition ) {
Lock l( mMutex ); Lock l( mMutex );
mPreDefinitions.emplace_back( std::move( preDefinition ) ); mPreDefinitions.emplace_back( std::move( preDefinition ) );
mExtensionManyLanguagesCache.clear();
} }
const SyntaxDefinition& SyntaxDefinitionManager::getPlainDefinition() const { const SyntaxDefinition& SyntaxDefinitionManager::getPlainDefinition() const {
@@ -1104,6 +1106,7 @@ bool SyntaxDefinitionManager::loadFromStream( IOStream& stream,
Lock l( mMutex ); Lock l( mMutex );
mDefinitions[pos.value()] = mDefinitions[pos.value()] =
std::make_shared<SyntaxDefinition>( std::move( res ) ); std::make_shared<SyntaxDefinition>( std::move( res ) );
mExtensionManyLanguagesCache.clear();
} else { } else {
if ( addedLangs ) if ( addedLangs )
addedLangs->push_back( res.getLanguageName() ); addedLangs->push_back( res.getLanguageName() );
@@ -1111,6 +1114,7 @@ bool SyntaxDefinitionManager::loadFromStream( IOStream& stream,
res.mLanguageIndex = mDefinitions.size(); res.mLanguageIndex = mDefinitions.size();
mDefinitions.emplace_back( mDefinitions.emplace_back(
std::make_shared<SyntaxDefinition>( std::move( res ) ) ); std::make_shared<SyntaxDefinition>( std::move( res ) ) );
mExtensionManyLanguagesCache.clear();
} }
} }
} }
@@ -1125,6 +1129,7 @@ bool SyntaxDefinitionManager::loadFromStream( IOStream& stream,
Lock l( mMutex ); Lock l( mMutex );
mDefinitions[pos.value()] = mDefinitions[pos.value()] =
std::make_shared<SyntaxDefinition>( std::move( res ) ); std::make_shared<SyntaxDefinition>( std::move( res ) );
mExtensionManyLanguagesCache.clear();
} else { } else {
if ( addedLangs ) if ( addedLangs )
addedLangs->push_back( res.getLanguageName() ); addedLangs->push_back( res.getLanguageName() );
@@ -1132,6 +1137,7 @@ bool SyntaxDefinitionManager::loadFromStream( IOStream& stream,
res.mLanguageIndex = mDefinitions.size(); res.mLanguageIndex = mDefinitions.size();
mDefinitions.emplace_back( mDefinitions.emplace_back(
std::make_shared<SyntaxDefinition>( std::move( res ) ) ); std::make_shared<SyntaxDefinition>( std::move( res ) ) );
mExtensionManyLanguagesCache.clear();
} }
} }
} }
@@ -1259,55 +1265,52 @@ bool SyntaxDefinitionManager::extensionCanRepresentManyLanguages( std::string ex
if ( extension[0] != '.' ) if ( extension[0] != '.' )
extension = '.' + extension; extension = '.' + extension;
std::unordered_set<std::string> count; Lock l( mMutex );
{ if ( auto it = mExtensionManyLanguagesCache.find( extension );
Lock l( mMutex ); it != mExtensionManyLanguagesCache.end() )
for ( const auto& definition : mDefinitions ) { return it->second;
for ( const auto& ext : definition->getFiles() ) {
if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || auto supportsExtension = [&extension]( const std::vector<std::string>& files ) {
String::endsWith( ext, "$" ) ) { for ( const auto& ext : files ) {
LuaPattern words( ext ); if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) ||
int start, end; String::endsWith( ext, "$" ) ) {
if ( words.find( extension, start, end ) ) { LuaPattern pattern( ext );
count.insert( definition->getLanguageName() ); int start, end;
if ( count.size() > 1 ) if ( pattern.find( extension, start, end ) )
return true; return true;
break; } else if ( extension == ext ) {
} return true;
} else if ( extension == ext ) {
count.insert( definition->getLanguageName() );
if ( count.size() > 1 )
return true;
break;
}
} }
} }
return false;
};
const std::string* matchedLanguage = nullptr;
auto foundDifferentLanguage = [&matchedLanguage]( const std::string& language ) {
if ( !matchedLanguage ) {
matchedLanguage = &language;
return false;
}
return *matchedLanguage != language;
};
for ( const auto& definition : mDefinitions ) {
if ( supportsExtension( definition->getFiles() ) &&
foundDifferentLanguage( definition->getLanguageName() ) ) {
mExtensionManyLanguagesCache.emplace( extension, true );
return true;
}
} }
{ for ( const auto& preDefinition : mPreDefinitions ) {
Lock l( mMutex ); if ( supportsExtension( preDefinition.getFiles() ) &&
for ( const auto& preDefinition : mPreDefinitions ) { foundDifferentLanguage( preDefinition.getLanguageName() ) ) {
for ( const auto& ext : preDefinition.getFiles() ) { mExtensionManyLanguagesCache.emplace( extension, true );
if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || return true;
String::endsWith( ext, "$" ) ) {
LuaPattern words( ext );
int start, end;
if ( words.find( extension, start, end ) ) {
count.insert( preDefinition.getLanguageName() );
if ( count.size() > 1 )
return true;
break;
}
} else if ( extension == ext ) {
count.insert( preDefinition.getLanguageName() );
if ( count.size() > 1 )
return true;
break;
}
}
} }
} }
mExtensionManyLanguagesCache.emplace( std::move( extension ), false );
return false; return false;
} }
+160 -85
View File
@@ -302,10 +302,12 @@ bool TextDocument::fileMightBeBinary( const std::string& file ) {
} }
bool TextDocument::isTextDocumentCommand( std::string_view cmd ) { bool TextDocument::isTextDocumentCommand( std::string_view cmd ) {
(void)getBuiltinCommands();
return TEXT_DOCUMENT_COMMANDS.contains( String::hash( cmd ) ); return TEXT_DOCUMENT_COMMANDS.contains( String::hash( cmd ) );
} }
bool TextDocument::isTextDocumentCommand( String::HashType cmdHash ) { bool TextDocument::isTextDocumentCommand( String::HashType cmdHash ) {
(void)getBuiltinCommands();
return TEXT_DOCUMENT_COMMANDS.contains( cmdHash ); return TEXT_DOCUMENT_COMMANDS.contains( cmdHash );
} }
@@ -1475,7 +1477,16 @@ std::string TextDocument::toUtf8String() {
std::vector<std::string> TextDocument::getCommandList() const { std::vector<std::string> TextDocument::getCommandList() const {
std::vector<std::string> cmds; std::vector<std::string> cmds;
cmds.reserve( mCommands.size() + mRefCommands.size() ); cmds.reserve( getBuiltinCommands().size() + mCommands.size() + mRefCommands.size() +
( mSharedRefCommands ? mSharedRefCommands->size() : 0 ) );
for ( const auto& cmd : getBuiltinCommands() ) {
if ( !isDefaultCommandRemoved( cmd.first ) )
cmds.push_back( cmd.first );
}
if ( mSharedRefCommands ) {
for ( const auto& cmd : *mSharedRefCommands )
cmds.push_back( cmd.first );
}
for ( const auto& cmd : mRefCommands ) for ( const auto& cmd : mRefCommands )
cmds.push_back( cmd.first ); cmds.push_back( cmd.first );
for ( const auto& cmd : mCommands ) for ( const auto& cmd : mCommands )
@@ -3205,25 +3216,42 @@ bool TextDocument::isDirty() const {
void TextDocument::execute( const std::string& command ) { void TextDocument::execute( const std::string& command ) {
auto cmdIt = mCommands.find( command ); auto cmdIt = mCommands.find( command );
if ( cmdIt != mCommands.end() ) if ( cmdIt != mCommands.end() )
cmdIt->second(); return cmdIt->second();
auto builtinIt = getBuiltinCommands().find( command );
if ( builtinIt != getBuiltinCommands().end() && !isDefaultCommandRemoved( command ) )
builtinIt->second( this );
} }
void TextDocument::execute( const std::string& command, Client* client ) { void TextDocument::execute( const std::string& command, Client* client ) {
auto cmdRefIt = mRefCommands.find( command ); auto cmdRefIt = mRefCommands.find( command );
if ( cmdRefIt != mRefCommands.end() ) if ( cmdRefIt != mRefCommands.end() )
return cmdRefIt->second( client ); return cmdRefIt->second( client );
if ( mSharedRefCommands ) {
auto sharedCmdIt = mSharedRefCommands->find( command );
if ( sharedCmdIt != mSharedRefCommands->end() )
return sharedCmdIt->second( client );
}
auto cmdIt = mCommands.find( command ); auto cmdIt = mCommands.find( command );
if ( cmdIt != mCommands.end() ) if ( cmdIt != mCommands.end() )
return cmdIt->second(); return cmdIt->second();
auto builtinIt = getBuiltinCommands().find( command );
if ( builtinIt != getBuiltinCommands().end() && !isDefaultCommandRemoved( command ) )
builtinIt->second( this );
} }
void TextDocument::setCommands( const UnorderedMap<std::string, DocumentCommand>& cmds ) { void TextDocument::setCommands( const UnorderedMap<std::string, DocumentCommand>& cmds ) {
mCommands.insert( cmds.begin(), cmds.end() ); mCommands.insert( cmds.begin(), cmds.end() );
if ( mRemovedDefaultCommands ) {
for ( const auto& cmd : cmds )
mRemovedDefaultCommands->erase( cmd.first );
}
} }
void TextDocument::setCommand( const std::string& command, void TextDocument::setCommand( const std::string& command,
const TextDocument::DocumentCommand& func ) { const TextDocument::DocumentCommand& func ) {
mCommands[command] = func; mCommands[command] = func;
if ( mRemovedDefaultCommands )
mRemovedDefaultCommands->erase( command );
} }
void TextDocument::setCommand( const std::string& command, void TextDocument::setCommand( const std::string& command,
@@ -3231,13 +3259,43 @@ void TextDocument::setCommand( const std::string& command,
mRefCommands[command] = func; mRefCommands[command] = func;
} }
void TextDocument::setSharedRefCommands(
std::shared_ptr<const TextDocument::DocumentRefCommands> commands ) {
mSharedRefCommands = std::move( commands );
}
bool TextDocument::hasCommand( const std::string& command ) { bool TextDocument::hasCommand( const std::string& command ) {
return mCommands.find( command ) != mCommands.end() || return mCommands.find( command ) != mCommands.end() ||
mRefCommands.find( command ) != mRefCommands.end(); mRefCommands.find( command ) != mRefCommands.end() ||
( mSharedRefCommands &&
mSharedRefCommands->find( command ) != mSharedRefCommands->end() ) ||
( getBuiltinCommands().find( command ) != getBuiltinCommands().end() &&
!isDefaultCommandRemoved( command ) );
} }
bool TextDocument::removeCommand( const std::string& command ) { bool TextDocument::removeCommand( const std::string& command ) {
return mCommands.erase( command ) > 0 || mRefCommands.erase( command ) > 0; if ( mCommands.erase( command ) > 0 ) {
if ( getBuiltinCommands().find( command ) != getBuiltinCommands().end() ) {
if ( !mRemovedDefaultCommands )
mRemovedDefaultCommands = std::make_unique<UnorderedSet<std::string>>();
mRemovedDefaultCommands->insert( command );
}
return true;
}
if ( mRefCommands.erase( command ) > 0 )
return true;
if ( getBuiltinCommands().find( command ) != getBuiltinCommands().end() &&
!isDefaultCommandRemoved( command ) ) {
if ( !mRemovedDefaultCommands )
mRemovedDefaultCommands = std::make_unique<UnorderedSet<std::string>>();
mRemovedDefaultCommands->insert( command );
return true;
}
return false;
}
bool TextDocument::isDefaultCommandRemoved( const std::string& command ) const {
return mRemovedDefaultCommands && mRemovedDefaultCommands->contains( command );
} }
static constexpr auto MAX_CAPTURES = 12; static constexpr auto MAX_CAPTURES = 12;
@@ -4885,88 +4943,105 @@ void TextDocument::clearIndentation() {
} }
} }
void TextDocument::initializeCommands() { const UnorderedMap<std::string, TextDocument::BuiltinDocumentCommand>&
// The built-in document commands and editor-specific commands share this table. Reserve their TextDocument::getBuiltinCommands() {
// known steady-state capacity so every new editor does not repeatedly grow and rehash it. #define EE_TEXT_DOCUMENT_COMMAND( Name, Method ) \
mCommands.reserve( 128 ); { \
mCommands["reset-document"] = [this] { reset(); }; Name, +[]( TextDocument* document ) { document->Method(); } \
mCommands["save-doc"] = [this] { save(); };
mCommands["delete-to-previous-word"] = [this] { deleteToPreviousWord(); };
mCommands["delete-to-previous-char"] = [this] { deleteToPreviousChar(); };
mCommands["delete-to-next-word"] = [this] { deleteToNextWord(); };
mCommands["delete-to-next-char"] = [this] { deleteToNextChar(); };
mCommands["delete-current-line"] = [this] { deleteCurrentLine(); };
mCommands["delete-to-start-of-line"] = [this] { deleteToStartOfLine(); };
mCommands["delete-to-end-of-line"] = [this] { deleteToEndOfLine(); };
mCommands["delete-selection"] = [this] { deleteSelection(); };
mCommands["delete-word"] = [this] { deleteWord(); };
mCommands["delete-paragraph"] = [this] { deleteCurrentParagraph(); };
mCommands["move-to-previous-char"] = [this] { moveToPreviousChar(); };
mCommands["move-to-previous-word"] = [this] { moveToPreviousWord(); };
mCommands["move-to-next-char"] = [this] { moveToNextChar(); };
mCommands["move-to-next-word"] = [this] { moveToNextWord(); };
mCommands["move-to-previous-line"] = [this] { moveToPreviousLine(); };
mCommands["move-to-next-line"] = [this] { moveToNextLine(); };
mCommands["move-to-previous-page"] = [this] { moveToPreviousPage( mPageSize ); };
mCommands["move-to-next-page"] = [this] { moveToNextPage( mPageSize ); };
mCommands["move-to-start-of-doc"] = [this] { moveToStartOfDoc(); };
mCommands["move-to-end-of-doc"] = [this] { moveToEndOfDoc(); };
mCommands["move-to-start-of-line"] = [this] { moveToStartOfLine(); };
mCommands["move-to-end-of-line"] = [this] { moveToEndOfLine(); };
mCommands["move-to-start-of-content"] = [this] { moveToStartOfContent(); };
mCommands["move-to-previous-paragraph"] = [this] { moveToPreviousParagraph(); };
mCommands["move-to-next-paragraph"] = [this] { moveToNextParagraph(); };
mCommands["move-lines-up"] = [this] { moveLinesUp(); };
mCommands["move-lines-down"] = [this] { moveLinesDown(); };
mCommands["select-to-previous-char"] = [this] { selectToPreviousChar(); };
mCommands["select-to-previous-word"] = [this] { selectToPreviousWord(); };
mCommands["select-to-previous-line"] = [this] { selectToPreviousLine(); };
mCommands["select-to-next-char"] = [this] { selectToNextChar(); };
mCommands["select-to-next-word"] = [this] { selectToNextWord(); };
mCommands["select-to-next-line"] = [this] { selectToNextLine(); };
mCommands["select-word"] = [this] { selectWord(); };
mCommands["select-all-words"] = [this] { selectAllWords(); };
mCommands["select-line"] = [this] { selectLine(); };
mCommands["select-single-line"] = [this] { selectSingleLine(); };
mCommands["select-to-start-of-line"] = [this] { selectToStartOfLine(); };
mCommands["select-to-end-of-line"] = [this] { selectToEndOfLine(); };
mCommands["select-to-start-of-doc"] = [this] { selectToStartOfDoc(); };
mCommands["select-to-start-of-content"] = [this] { selectToStartOfContent(); };
mCommands["select-to-end-of-doc"] = [this] { selectToEndOfDoc(); };
mCommands["select-to-previous-page"] = [this] { selectToPreviousPage( mPageSize ); };
mCommands["select-to-next-page"] = [this] { selectToNextPage( mPageSize ); };
mCommands["select-paragraph"] = [this] { selectCurrentParagraph(); };
mCommands["select-all"] = [this] { selectAll(); };
mCommands["new-line"] = [this] { newLine(); };
mCommands["new-line-above"] = [this] { newLineAbove(); };
mCommands["indent"] = [this] { indent(); };
mCommands["unindent"] = [this] { unindent(); };
mCommands["undo"] = [this] { undo(); };
mCommands["redo"] = [this] { redo(); };
mCommands["toggle-line-comments"] = [this] { toggleLineComments(); };
mCommands["toggle-block-comments"] = [this] { toggleBlockComments(); };
mCommands["selection-to-upper"] = [this] { toUpperSelection(); };
mCommands["selection-to-lower"] = [this] { toLowerSelection(); };
mCommands["reset-cursor"] = [this] { resetSelection(); };
mCommands["add-cursor-above"] = [this] { addCursorAbove(); };
mCommands["add-cursor-below"] = [this] { addCursorBelow(); };
mCommands["cursor-undo"] = [this] { cursorUndo(); };
mCommands["select-all-matches"] = [this] { selectAllMatches(); };
mCommands["escape"] = [this] { escape(); };
mCommands["unescape"] = [this] { unescape(); };
mCommands["to-base64"] = [this] { toBase64(); };
mCommands["from-base64"] = [this] { fromBase64(); };
mCommands["trim-trailing-whitespace"] = [this] { trimTrailingWhitespace(); };
mCommands["join-lines"] = [this] { joinLines(); };
mCommands["duplicate-line-or-selection"] = [this] { duplicateLineOrSelection(); };
mCommands["convert-indentation-to-tabs"] = [this] { convertIndentationToTabs(); };
mCommands["convert-indentation-to-spaces"] = [this] { convertIndentationToSpaces(); };
mCommands["clear-indentation"] = [this] { clearIndentation(); };
if ( TEXT_DOCUMENT_COMMANDS.empty() ) {
for ( const auto& [cmd, _] : mCommands )
TEXT_DOCUMENT_COMMANDS.insert( String::hash( cmd ) );
} }
static const UnorderedMap<std::string, BuiltinDocumentCommand> commands{
EE_TEXT_DOCUMENT_COMMAND( "reset-document", reset ),
EE_TEXT_DOCUMENT_COMMAND( "save-doc", save ),
EE_TEXT_DOCUMENT_COMMAND( "delete-to-previous-word", deleteToPreviousWord ),
EE_TEXT_DOCUMENT_COMMAND( "delete-to-previous-char", deleteToPreviousChar ),
EE_TEXT_DOCUMENT_COMMAND( "delete-to-next-word", deleteToNextWord ),
EE_TEXT_DOCUMENT_COMMAND( "delete-to-next-char", deleteToNextChar ),
EE_TEXT_DOCUMENT_COMMAND( "delete-current-line", deleteCurrentLine ),
EE_TEXT_DOCUMENT_COMMAND( "delete-to-start-of-line", deleteToStartOfLine ),
EE_TEXT_DOCUMENT_COMMAND( "delete-to-end-of-line", deleteToEndOfLine ),
EE_TEXT_DOCUMENT_COMMAND( "delete-selection", deleteSelection ),
EE_TEXT_DOCUMENT_COMMAND( "delete-word", deleteWord ),
EE_TEXT_DOCUMENT_COMMAND( "delete-paragraph", deleteCurrentParagraph ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-previous-char", moveToPreviousChar ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-previous-word", moveToPreviousWord ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-next-char", moveToNextChar ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-next-word", moveToNextWord ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-previous-line", moveToPreviousLine ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-next-line", moveToNextLine ),
{ "move-to-previous-page",
+[]( TextDocument* document ) { document->moveToPreviousPage( document->mPageSize ); } },
{ "move-to-next-page",
+[]( TextDocument* document ) { document->moveToNextPage( document->mPageSize ); } },
EE_TEXT_DOCUMENT_COMMAND( "move-to-start-of-doc", moveToStartOfDoc ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-end-of-doc", moveToEndOfDoc ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-start-of-line", moveToStartOfLine ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-end-of-line", moveToEndOfLine ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-start-of-content", moveToStartOfContent ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-previous-paragraph", moveToPreviousParagraph ),
EE_TEXT_DOCUMENT_COMMAND( "move-to-next-paragraph", moveToNextParagraph ),
EE_TEXT_DOCUMENT_COMMAND( "move-lines-up", moveLinesUp ),
EE_TEXT_DOCUMENT_COMMAND( "move-lines-down", moveLinesDown ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-previous-char", selectToPreviousChar ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-previous-word", selectToPreviousWord ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-previous-line", selectToPreviousLine ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-next-char", selectToNextChar ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-next-word", selectToNextWord ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-next-line", selectToNextLine ),
EE_TEXT_DOCUMENT_COMMAND( "select-word", selectWord ),
EE_TEXT_DOCUMENT_COMMAND( "select-all-words", selectAllWords ),
EE_TEXT_DOCUMENT_COMMAND( "select-line", selectLine ),
EE_TEXT_DOCUMENT_COMMAND( "select-single-line", selectSingleLine ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-start-of-line", selectToStartOfLine ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-end-of-line", selectToEndOfLine ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-start-of-doc", selectToStartOfDoc ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-start-of-content", selectToStartOfContent ),
EE_TEXT_DOCUMENT_COMMAND( "select-to-end-of-doc", selectToEndOfDoc ),
{ "select-to-previous-page",
+[]( TextDocument* document ) {
document->selectToPreviousPage( document->mPageSize );
} },
{ "select-to-next-page",
+[]( TextDocument* document ) { document->selectToNextPage( document->mPageSize ); } },
EE_TEXT_DOCUMENT_COMMAND( "select-paragraph", selectCurrentParagraph ),
EE_TEXT_DOCUMENT_COMMAND( "select-all", selectAll ),
EE_TEXT_DOCUMENT_COMMAND( "new-line", newLine ),
EE_TEXT_DOCUMENT_COMMAND( "new-line-above", newLineAbove ),
EE_TEXT_DOCUMENT_COMMAND( "indent", indent ),
EE_TEXT_DOCUMENT_COMMAND( "unindent", unindent ),
EE_TEXT_DOCUMENT_COMMAND( "undo", undo ),
EE_TEXT_DOCUMENT_COMMAND( "redo", redo ),
EE_TEXT_DOCUMENT_COMMAND( "toggle-line-comments", toggleLineComments ),
EE_TEXT_DOCUMENT_COMMAND( "toggle-block-comments", toggleBlockComments ),
EE_TEXT_DOCUMENT_COMMAND( "selection-to-upper", toUpperSelection ),
EE_TEXT_DOCUMENT_COMMAND( "selection-to-lower", toLowerSelection ),
EE_TEXT_DOCUMENT_COMMAND( "reset-cursor", resetSelection ),
EE_TEXT_DOCUMENT_COMMAND( "add-cursor-above", addCursorAbove ),
EE_TEXT_DOCUMENT_COMMAND( "add-cursor-below", addCursorBelow ),
EE_TEXT_DOCUMENT_COMMAND( "cursor-undo", cursorUndo ),
EE_TEXT_DOCUMENT_COMMAND( "select-all-matches", selectAllMatches ),
EE_TEXT_DOCUMENT_COMMAND( "escape", escape ),
EE_TEXT_DOCUMENT_COMMAND( "unescape", unescape ),
EE_TEXT_DOCUMENT_COMMAND( "to-base64", toBase64 ),
EE_TEXT_DOCUMENT_COMMAND( "from-base64", fromBase64 ),
EE_TEXT_DOCUMENT_COMMAND( "trim-trailing-whitespace", trimTrailingWhitespace ),
EE_TEXT_DOCUMENT_COMMAND( "join-lines", joinLines ),
EE_TEXT_DOCUMENT_COMMAND( "duplicate-line-or-selection", duplicateLineOrSelection ),
EE_TEXT_DOCUMENT_COMMAND( "convert-indentation-to-tabs", convertIndentationToTabs ),
EE_TEXT_DOCUMENT_COMMAND( "convert-indentation-to-spaces", convertIndentationToSpaces ),
EE_TEXT_DOCUMENT_COMMAND( "clear-indentation", clearIndentation ),
};
#undef EE_TEXT_DOCUMENT_COMMAND
static const bool commandHashesInitialized = [] {
for ( const auto& [command, _] : commands )
TEXT_DOCUMENT_COMMANDS.insert( String::hash( command ) );
return true;
}();
(void)commandHashesInitialized;
return commands;
}
void TextDocument::initializeCommands() {
(void)getBuiltinCommands();
} }
size_t TextDocument::getTopMostCursorIndex() { size_t TextDocument::getTopMostCursorIndex() {
+62 -39
View File
@@ -1,3 +1,4 @@
#include <algorithm>
#include <eepp/ui/keyboardshortcut.hpp> #include <eepp/ui/keyboardshortcut.hpp>
#include <eepp/window/input.hpp> #include <eepp/window/input.hpp>
@@ -21,7 +22,32 @@ KeyBindings::Shortcut KeyBindings::sanitizeShortcut( const KeyBindings::Shortcut
return sanitized; return sanitized;
} }
KeyBindings::KeyBindings( const Window::Input* input ) : mInput( input ) {} std::vector<KeyBindings::Shortcut>
KeyBindings::getOrderedShortcuts( const KeyBindings::ShortcutMap& bindings ) {
std::vector<Shortcut> shortcuts;
shortcuts.reserve( bindings.size() );
for ( const auto& binding : bindings )
shortcuts.emplace_back( binding.first );
std::sort( shortcuts.begin(), shortcuts.end() );
return shortcuts;
}
std::shared_ptr<KeyBindings::Storage> KeyBindings::getEmptyStorage() {
static auto storage = std::make_shared<Storage>();
return storage;
}
KeyBindings::KeyBindings( const Window::Input* input ) :
mInput( input ), mStorage( getEmptyStorage() ) {}
void KeyBindings::setKeybinds( const KeyBindings& bindings ) {
mStorage = bindings.mStorage;
}
void KeyBindings::ensureUniqueStorage() {
if ( !mStorage.unique() )
mStorage = std::make_shared<Storage>( *mStorage );
}
void KeyBindings::addKeybindsString( const std::map<std::string, std::string>& binds ) { void KeyBindings::addKeybindsString( const std::map<std::string, std::string>& binds ) {
for ( auto& bind : binds ) { for ( auto& bind : binds ) {
@@ -29,9 +55,9 @@ void KeyBindings::addKeybindsString( const std::map<std::string, std::string>& b
} }
} }
void KeyBindings::addKeybinds( const std::map<KeyBindings::Shortcut, std::string>& binds ) { void KeyBindings::addKeybinds( const KeyBindings::ShortcutMap& binds ) {
for ( auto& bind : binds ) { for ( const auto& shortcut : getOrderedShortcuts( binds ) ) {
addKeybind( bind.first, bind.second ); addKeybind( shortcut, binds.find( shortcut )->second );
} }
} }
@@ -42,20 +68,15 @@ void KeyBindings::addKeybindsStringUnordered(
} }
} }
void KeyBindings::addKeybindsUnordered(
const std::unordered_map<KeyBindings::Shortcut, std::string>& binds ) {
for ( auto& bind : binds ) {
addKeybind( bind.first, bind.second );
}
}
void KeyBindings::addKeybindString( const std::string& key, const std::string& command ) { void KeyBindings::addKeybindString( const std::string& key, const std::string& command ) {
addKeybind( getShortcutFromString( key ), command ); addKeybind( getShortcutFromString( key ), command );
} }
void KeyBindings::addKeybind( const KeyBindings::Shortcut& key, const std::string& command ) { void KeyBindings::addKeybind( const KeyBindings::Shortcut& key, const std::string& command ) {
mShortcuts[sanitizeShortcut( key )] = command; ensureUniqueStorage();
mKeybindingsInvert[command] = sanitizeShortcut( key ); auto shortcut = sanitizeShortcut( key );
mStorage->shortcuts[shortcut] = command;
mStorage->keybindingsInvert[command] = shortcut;
} }
void KeyBindings::replaceKeybindString( const std::string& keys, const std::string& command ) { void KeyBindings::replaceKeybindString( const std::string& keys, const std::string& command ) {
@@ -63,18 +84,19 @@ void KeyBindings::replaceKeybindString( const std::string& keys, const std::stri
} }
void KeyBindings::replaceKeybind( const KeyBindings::Shortcut& keys, const std::string& command ) { void KeyBindings::replaceKeybind( const KeyBindings::Shortcut& keys, const std::string& command ) {
ensureUniqueStorage();
bool erased; bool erased;
do { do {
erased = false; erased = false;
auto it = mShortcuts.find( sanitizeShortcut( keys ) ); auto it = mStorage->shortcuts.find( sanitizeShortcut( keys ) );
if ( it != mShortcuts.end() ) { if ( it != mStorage->shortcuts.end() ) {
mShortcuts.erase( it ); mStorage->shortcuts.erase( it );
mKeybindingsInvert.erase( it->second ); mStorage->keybindingsInvert.erase( it->second );
erased = true; erased = true;
} }
} while ( erased ); } while ( erased );
mShortcuts[sanitizeShortcut( keys )] = command; mStorage->shortcuts[sanitizeShortcut( keys )] = command;
mKeybindingsInvert[command] = sanitizeShortcut( keys ); mStorage->keybindingsInvert[command] = sanitizeShortcut( keys );
} }
KeyBindings::Shortcut KeyBindings::toShortcut( const Window::Input* input, KeyBindings::Shortcut KeyBindings::toShortcut( const Window::Input* input,
@@ -102,9 +124,10 @@ KeyBindings::Shortcut KeyBindings::getShortcutFromString( const std::string& key
} }
void KeyBindings::removeKeybind( const KeyBindings::Shortcut& keys ) { void KeyBindings::removeKeybind( const KeyBindings::Shortcut& keys ) {
auto it = mShortcuts.find( keys.toUint64() ); ensureUniqueStorage();
if ( it != mShortcuts.end() ) { auto it = mStorage->shortcuts.find( keys );
mShortcuts.erase( it ); if ( it != mStorage->shortcuts.end() ) {
mStorage->shortcuts.erase( it );
} }
} }
@@ -113,25 +136,26 @@ void KeyBindings::removeKeybind( const std::string& kb ) {
} }
bool KeyBindings::existsKeybind( const KeyBindings::Shortcut& keys ) { bool KeyBindings::existsKeybind( const KeyBindings::Shortcut& keys ) {
return mShortcuts.find( keys.toUint64() ) != mShortcuts.end(); return mStorage->shortcuts.find( keys ) != mStorage->shortcuts.end();
} }
bool KeyBindings::hasCommand( const std::string& command ) { bool KeyBindings::hasCommand( const std::string& command ) {
return mKeybindingsInvert.find( command ) != mKeybindingsInvert.end(); return mStorage->keybindingsInvert.find( command ) != mStorage->keybindingsInvert.end();
} }
KeyBindings::Shortcut KeyBindings::getShortcutFromCommand( const std::string& cmd ) const { KeyBindings::Shortcut KeyBindings::getShortcutFromCommand( const std::string& cmd ) const {
auto it = mKeybindingsInvert.find( cmd ); auto it = mStorage->keybindingsInvert.find( cmd );
if ( it != mKeybindingsInvert.end() ) if ( it != mStorage->keybindingsInvert.end() )
return it->second; return it->second;
return {}; return {};
} }
void KeyBindings::removeCommandKeybind( const std::string& command ) { void KeyBindings::removeCommandKeybind( const std::string& command ) {
auto kbIt = mKeybindingsInvert.find( command ); ensureUniqueStorage();
if ( kbIt != mKeybindingsInvert.end() ) { auto kbIt = mStorage->keybindingsInvert.find( command );
if ( kbIt != mStorage->keybindingsInvert.end() ) {
removeKeybind( kbIt->second ); removeKeybind( kbIt->second );
mKeybindingsInvert.erase( command ); mStorage->keybindingsInvert.erase( command );
} }
} }
@@ -141,8 +165,8 @@ void KeyBindings::removeCommandsKeybind( const std::vector<std::string>& command
} }
std::string KeyBindings::getCommandFromKeyBind( const KeyBindings::Shortcut& keys ) { std::string KeyBindings::getCommandFromKeyBind( const KeyBindings::Shortcut& keys ) {
auto it = mShortcuts.find( sanitizeShortcut( keys ) ); auto it = mStorage->shortcuts.find( sanitizeShortcut( keys ) );
if ( it != mShortcuts.end() ) { if ( it != mStorage->shortcuts.end() ) {
return it->second; return it->second;
} }
return ""; return "";
@@ -166,23 +190,22 @@ std::string KeyBindings::keybindFormat( std::string str ) {
} }
std::string KeyBindings::getCommandKeybindString( const std::string& command ) const { std::string KeyBindings::getCommandKeybindString( const std::string& command ) const {
auto it = mKeybindingsInvert.find( command ); auto it = mStorage->keybindingsInvert.find( command );
if ( it == mKeybindingsInvert.end() ) if ( it == mStorage->keybindingsInvert.end() )
return ""; return "";
return keybindFormat( getShortcutString( Shortcut( it->second ) ) ); return keybindFormat( getShortcutString( it->second ) );
} }
void KeyBindings::reset() { void KeyBindings::reset() {
mShortcuts.clear(); mStorage = getEmptyStorage();
mKeybindingsInvert.clear();
} }
const KeyBindings::ShortcutMap& KeyBindings::getShortcutMap() const { const KeyBindings::ShortcutMap& KeyBindings::getShortcutMap() const {
return mShortcuts; return mStorage->shortcuts;
} }
const std::map<std::string, Uint64>& KeyBindings::getKeybindings() const { const std::map<std::string, KeyBindings::Shortcut>& KeyBindings::getKeybindings() const {
return mKeybindingsInvert; return mStorage->keybindingsInvert;
} }
std::string KeyBindings::fromShortcut( const Window::Input* input, KeyBindings::Shortcut shortcut, std::string KeyBindings::fromShortcut( const Window::Input* input, KeyBindings::Shortcut shortcut,
+4 -3
View File
@@ -11,10 +11,11 @@ using namespace EE::System;
namespace EE { namespace UI { namespace Tools { namespace EE { namespace UI { namespace Tools {
const std::map<KeyBindings::Shortcut, std::string> UICodeEditorSplitter::getDefaultKeybindings() { const KeyBindings::ShortcutMap UICodeEditorSplitter::getDefaultKeybindings() {
auto keybindings = UICodeEditor::getDefaultKeybindings(); auto keybindings = UICodeEditor::getDefaultKeybindings();
auto localKeybindings = getLocalDefaultKeybindings(); auto localKeybindings = getLocalDefaultKeybindings();
localKeybindings.insert( keybindings.begin(), keybindings.end() ); for ( const auto& [shortcut, command] : keybindings->getShortcutMap() )
localKeybindings.emplace( shortcut, command );
return localKeybindings; return localKeybindings;
} }
@@ -28,7 +29,7 @@ Uint32 UICodeEditorSplitter::getDefaultSwitchToTabModifier() {
return DefaultSwitchToTabModifier; return DefaultSwitchToTabModifier;
} }
const std::map<KeyBindings::Shortcut, std::string> const KeyBindings::ShortcutMap
UICodeEditorSplitter::getLocalDefaultKeybindings() { UICodeEditorSplitter::getLocalDefaultKeybindings() {
return { return {
{ { KEY_S, KeyMod::getDefaultModifier() }, "save-doc" }, { { KEY_S, KeyMod::getDefaultModifier() }, "save-doc" },
+154 -57
View File
@@ -25,6 +25,21 @@
namespace EE { namespace UI { namespace Tools { namespace EE { namespace UI { namespace Tools {
struct UIDiffView::PreparedPatch {
std::vector<DiffLine> lines;
std::string filename;
std::string binaryOldPath;
std::string binaryNewPath;
std::string binaryFileName;
bool hasCompleteFile{ false };
bool isBinaryImage{ false };
};
class UIDiffView::PreparedMultiFileDiff {
public:
std::vector<PreparedPatch> patches;
};
static bool imagesHaveSameDimensions( const std::string& oldFilePath, static bool imagesHaveSameDimensions( const std::string& oldFilePath,
const std::string& newFilePath ) { const std::string& newFilePath ) {
int oldWidth = 0; int oldWidth = 0;
@@ -80,8 +95,19 @@ static Sprite* setImageViewerImage( UIImageViewer* viewer, Image* image ) {
} }
UIScrollView* UIDiffView::NewMultiFileDiffViewer( const std::string& patchText, UIScrollView* UIDiffView::NewMultiFileDiffViewer( const std::string& patchText,
const std::string& repoPath, ViewMode viewMode ) { const std::string& repoPath, ViewMode viewMode,
auto diffs = UIDiffView::splitDiff( patchText ); bool interactiveFileHeaders ) {
return NewMultiFileDiffViewer( prepareMultiFileDiff( patchText ), repoPath, viewMode,
interactiveFileHeaders );
}
UIScrollView*
UIDiffView::NewMultiFileDiffViewer( std::shared_ptr<PreparedMultiFileDiff> preparedDiff,
const std::string& repoPath, ViewMode viewMode,
bool interactiveFileHeaders ) {
if ( !preparedDiff )
return nullptr;
auto* uiSceneNode = SceneManager::instance()->getUISceneNode(); auto* uiSceneNode = SceneManager::instance()->getUISceneNode();
const bool wasLoading = uiSceneNode && uiSceneNode->isLoading(); const bool wasLoading = uiSceneNode && uiSceneNode->isLoading();
if ( uiSceneNode ) if ( uiSceneNode )
@@ -92,7 +118,7 @@ UIScrollView* UIDiffView::NewMultiFileDiffViewer( const std::string& patchText,
vbox->setParent( scrollView ); vbox->setParent( scrollView );
vbox->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); vbox->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent );
for ( const auto& diff : diffs ) { for ( auto& patch : preparedDiff->patches ) {
auto* diffView = UIDiffView::New(); auto* diffView = UIDiffView::New();
diffView->setViewMode( viewMode ); diffView->setViewMode( viewMode );
diffView->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); diffView->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent );
@@ -100,7 +126,8 @@ UIScrollView* UIDiffView::NewMultiFileDiffViewer( const std::string& patchText,
diffView->setHeadersVisible( true ); diffView->setHeadersVisible( true );
diffView->setViewModeToggleVisible( false ); diffView->setViewModeToggleVisible( false );
diffView->setCompleteViewToggleVisible( false ); diffView->setCompleteViewToggleVisible( false );
diffView->loadFromPatch( diff, "", "", repoPath ); diffView->setInteractiveFileHeader( interactiveFileHeaders );
diffView->loadPreparedPatch( std::move( patch ), "", "", repoPath );
} }
if ( uiSceneNode ) { if ( uiSceneNode ) {
@@ -506,9 +533,8 @@ UIDiffView::UIDiffView() :
mLeftEditor->setFontSize( mRightEditor->getFontSize() ); mLeftEditor->setFontSize( mRightEditor->getFontSize() );
mLeftPlugin->registerUpdate( mLeftEditor ); mLeftPlugin->registerUpdate( mLeftEditor );
} ); } );
mRightEditor->getVScrollBar()->on( Event::OnSizeChange, [this] ( auto ) { mRightEditor->getVScrollBar()->on( Event::OnSizeChange,
updateModeButton(); [this]( auto ) { updateModeButton(); } );
} );
for ( auto* editor : { mEditor, mLeftEditor, mRightEditor } ) { for ( auto* editor : { mEditor, mLeftEditor, mRightEditor } ) {
editor->on( Event::OnSizeChange, [this]( auto ) { onAutoSize(); } ); editor->on( Event::OnSizeChange, [this]( auto ) { onAutoSize(); } );
@@ -993,12 +1019,13 @@ void UIDiffView::updateImageDiffView() {
} }
} }
void UIDiffView::computeSubLineDiff( DiffLine& oldLine, DiffLine& newLine ) { static void computeSubLineDiffImpl( UIDiffView::DiffLine& oldLine, UIDiffView::DiffLine& newLine,
UIDiffView::SubLineDiffAlgorithm algorithm ) {
dtl::Diff<String::StringBaseType, String::View> diff( oldLine.text.view(), dtl::Diff<String::StringBaseType, String::View> diff( oldLine.text.view(),
newLine.text.view() ); newLine.text.view() );
diff.compose(); diff.compose();
if ( mSubLineDiffAlgorithm == SubLineDiffAlgorithm::SES ) { if ( algorithm == UIDiffView::SubLineDiffAlgorithm::SES ) {
auto ses = diff.getSes().getSequence(); auto ses = diff.getSes().getSequence();
Int64 oldIdx = 0; Int64 oldIdx = 0;
Int64 newIdx = 0; Int64 newIdx = 0;
@@ -1068,11 +1095,18 @@ void UIDiffView::computeSubLineDiff( DiffLine& oldLine, DiffLine& newLine ) {
} }
} }
static void applySubLineDiff( void UIDiffView::computeSubLineDiff( DiffLine& oldLine, DiffLine& newLine ) {
computeSubLineDiffImpl( oldLine, newLine, mSubLineDiffAlgorithm );
}
static bool applySubLineDiff(
std::vector<UIDiffView::DiffLine>& lines, std::vector<UIDiffView::DiffLine>& lines,
std::function<void( UIDiffView::DiffLine&, UIDiffView::DiffLine& )> computeSubLineDiff ) { std::function<void( UIDiffView::DiffLine&, UIDiffView::DiffLine& )> computeSubLineDiff,
const std::shared_ptr<std::atomic_bool>& cancelled = {} ) {
size_t i = 0; size_t i = 0;
while ( i < lines.size() ) { while ( i < lines.size() ) {
if ( cancelled && cancelled->load( std::memory_order_relaxed ) )
return false;
if ( lines[i].type == UIDiffView::DiffLineType::Removed ) { if ( lines[i].type == UIDiffView::DiffLineType::Removed ) {
size_t j = i; size_t j = i;
while ( j < lines.size() && lines[j].type == UIDiffView::DiffLineType::Removed ) while ( j < lines.size() && lines[j].type == UIDiffView::DiffLineType::Removed )
@@ -1085,6 +1119,8 @@ static void applySubLineDiff(
size_t numAdded = k - j; size_t numAdded = k - j;
size_t numToCompare = std::min( numRemoved, numAdded ); size_t numToCompare = std::min( numRemoved, numAdded );
for ( size_t m = 0; m < numToCompare; m++ ) { for ( size_t m = 0; m < numToCompare; m++ ) {
if ( cancelled && cancelled->load( std::memory_order_relaxed ) )
return false;
computeSubLineDiff( lines[i + m], lines[j + m] ); computeSubLineDiff( lines[i + m], lines[j + m] );
} }
i = k; i = k;
@@ -1092,6 +1128,7 @@ static void applySubLineDiff(
i++; i++;
} }
} }
return true;
} }
struct BinaryImagePatch { struct BinaryImagePatch {
@@ -1212,19 +1249,21 @@ static BinaryImagePatch parseBinaryImagePatch( const std::vector<std::string>& l
return patch; return patch;
} }
void UIDiffView::loadFromPatch( const std::string& patchText, const std::string& originalFilePath, UIDiffView::PreparedPatch
const std::string& oldFilePath, const std::string& repoPath ) { UIDiffView::preparePatch( const std::string& patchText, const std::string& originalFilePath,
resetToTextDiffView(); SubLineDiffAlgorithm algorithm,
mLines.clear(); const std::shared_ptr<std::atomic_bool>& cancelled ) {
PreparedPatch prepared;
auto lines = String::split( patchText, '\n', true ); auto lines = String::split( patchText, '\n', true );
if ( cancelled && cancelled->load( std::memory_order_relaxed ) )
return prepared;
std::string fileText; std::string fileText;
bool hasCompleteFile = false;
std::vector<std::string> fileLines; std::vector<std::string> fileLines;
if ( !originalFilePath.empty() && FileSystem::fileExists( originalFilePath ) ) { if ( !originalFilePath.empty() && FileSystem::fileExists( originalFilePath ) ) {
FileSystem::fileGet( originalFilePath, fileText ); FileSystem::fileGet( originalFilePath, fileText );
fileLines = String::split( fileText, '\n', true ); fileLines = String::split( fileText, '\n', true );
hasCompleteFile = true; prepared.hasCompleteFile = true;
} }
Int64 oldLineNum = 0; Int64 oldLineNum = 0;
@@ -1234,27 +1273,16 @@ void UIDiffView::loadFromPatch( const std::string& patchText, const std::string&
std::string filename; std::string filename;
auto imagePatch = parseBinaryImagePatch( lines, originalFilePath ); auto imagePatch = parseBinaryImagePatch( lines, originalFilePath );
if ( imagePatch.isBinary ) { prepared.isBinaryImage = imagePatch.isBinary;
std::string oldImagePath( oldFilePath ); prepared.binaryOldPath = std::move( imagePatch.oldPath );
if ( oldImagePath.empty() ) { prepared.binaryNewPath = std::move( imagePatch.newPath );
oldImagePath = prepared.binaryFileName = std::move( imagePatch.fileName );
resolveImagePatchPath( imagePatch.oldPath, originalFilePath, false, repoPath ); prepared.lines.reserve( lines.size() + fileLines.size() );
}
std::string newImagePath(
resolveImagePatchPath( imagePatch.newPath, originalFilePath, true, repoPath ) );
if ( oldImagePath == newImagePath ) for ( size_t lineIdx = 0; lineIdx < lines.size(); ++lineIdx ) {
oldImagePath.clear(); if ( ( lineIdx & 0xFF ) == 0 && cancelled && cancelled->load( std::memory_order_relaxed ) )
return PreparedPatch{};
if ( loadImageDiffFromPaths( oldImagePath, newImagePath ) ) { const auto& line = lines[lineIdx];
if ( !imagePatch.fileName.empty() )
mFileName = std::move( imagePatch.fileName );
updateFileHeaderInfo();
return;
}
}
for ( const auto& line : lines ) {
if ( String::startsWith( line, "diff " ) || String::startsWith( line, "index " ) || if ( String::startsWith( line, "diff " ) || String::startsWith( line, "index " ) ||
String::startsWith( line, "--- " ) || String::startsWith( line, "+++ " ) ) { String::startsWith( line, "--- " ) || String::startsWith( line, "+++ " ) ) {
if ( String::startsWith( line, "+++ " ) ) { if ( String::startsWith( line, "+++ " ) ) {
@@ -1270,27 +1298,33 @@ void UIDiffView::loadFromPatch( const std::string& patchText, const std::string&
size_t minusPos = line.find( "-" ); size_t minusPos = line.find( "-" );
size_t plusPos = line.find( "+" ); size_t plusPos = line.find( "+" );
if ( minusPos != std::string::npos && plusPos != std::string::npos ) { if ( minusPos != std::string::npos && plusPos != std::string::npos ) {
auto parseLineNumber = [&line]( size_t start, size_t end, Int64& lineNumber ) {
if ( end == std::string::npos || end <= start )
return false;
Int64 parsedLineNumber;
if ( !String::fromString( parsedLineNumber, std::string_view{ line }.substr(
start, end - start ) ) )
return false;
lineNumber = parsedLineNumber - 1;
return true;
};
size_t commaPos = line.find( ",", minusPos ); size_t commaPos = line.find( ",", minusPos );
size_t spacePos = line.find( " ", minusPos ); size_t spacePos = line.find( " ", minusPos );
if ( commaPos != std::string::npos && commaPos < spacePos ) { if ( commaPos != std::string::npos && commaPos < spacePos ) {
oldLineNum = parseLineNumber( minusPos + 1, commaPos, oldLineNum );
std::stoll( line.substr( minusPos + 1, commaPos - minusPos - 1 ) ) - 1;
} else if ( spacePos != std::string::npos ) { } else if ( spacePos != std::string::npos ) {
oldLineNum = parseLineNumber( minusPos + 1, spacePos, oldLineNum );
std::stoll( line.substr( minusPos + 1, spacePos - minusPos - 1 ) ) - 1;
} }
commaPos = line.find( ",", plusPos ); commaPos = line.find( ",", plusPos );
spacePos = line.find( " ", plusPos ); spacePos = line.find( " ", plusPos );
if ( commaPos != std::string::npos && commaPos < spacePos ) { if ( commaPos != std::string::npos && commaPos < spacePos ) {
newLineNum = parseLineNumber( plusPos + 1, commaPos, newLineNum );
std::stoll( line.substr( plusPos + 1, commaPos - plusPos - 1 ) ) - 1;
} else if ( spacePos != std::string::npos ) { } else if ( spacePos != std::string::npos ) {
newLineNum = parseLineNumber( plusPos + 1, spacePos, newLineNum );
std::stoll( line.substr( plusPos + 1, spacePos - plusPos - 1 ) ) - 1;
} }
if ( hasCompleteFile ) { if ( prepared.hasCompleteFile ) {
while ( expectedNewLineNum < newLineNum + 1 && while ( expectedNewLineNum < newLineNum + 1 &&
expectedNewLineNum <= (Int64)fileLines.size() ) { expectedNewLineNum <= (Int64)fileLines.size() ) {
DiffLine dline; DiffLine dline;
@@ -1298,7 +1332,7 @@ void UIDiffView::loadFromPatch( const std::string& patchText, const std::string&
dline.text = fileLines[expectedNewLineNum - 1]; dline.text = fileLines[expectedNewLineNum - 1];
dline.oldLineNum = expectedOldLineNum++; dline.oldLineNum = expectedOldLineNum++;
dline.newLineNum = expectedNewLineNum++; dline.newLineNum = expectedNewLineNum++;
mLines.push_back( dline ); prepared.lines.push_back( std::move( dline ) );
} }
} }
expectedOldLineNum = oldLineNum + 1; expectedOldLineNum = oldLineNum + 1;
@@ -1335,31 +1369,88 @@ void UIDiffView::loadFromPatch( const std::string& patchText, const std::string&
expectedNewLineNum = newLineNum + 1; expectedNewLineNum = newLineNum + 1;
} }
mLines.push_back( dline ); prepared.lines.push_back( std::move( dline ) );
} }
if ( hasCompleteFile ) { if ( prepared.hasCompleteFile ) {
while ( expectedNewLineNum <= (Int64)fileLines.size() ) { while ( expectedNewLineNum <= (Int64)fileLines.size() ) {
if ( ( expectedNewLineNum & 0xFF ) == 0 && cancelled &&
cancelled->load( std::memory_order_relaxed ) )
return PreparedPatch{};
DiffLine dline; DiffLine dline;
dline.type = DiffLineType::Common; dline.type = DiffLineType::Common;
dline.text = fileLines[expectedNewLineNum - 1]; dline.text = fileLines[expectedNewLineNum - 1];
dline.oldLineNum = expectedOldLineNum++; dline.oldLineNum = expectedOldLineNum++;
dline.newLineNum = expectedNewLineNum++; dline.newLineNum = expectedNewLineNum++;
mLines.push_back( dline ); prepared.lines.push_back( std::move( dline ) );
} }
} }
applySubLineDiff( mLines, [this]( DiffLine& oldLine, DiffLine& newLine ) { if ( !applySubLineDiff(
computeSubLineDiff( oldLine, newLine ); prepared.lines,
} ); [algorithm]( DiffLine& oldLine, DiffLine& newLine ) {
computeSubLineDiffImpl( oldLine, newLine, algorithm );
},
cancelled ) )
return PreparedPatch{};
setCompleteViewToggleVisible( hasCompleteFile ); prepared.filename = std::move( filename );
return prepared;
}
if ( !filename.empty() ) { std::shared_ptr<UIDiffView::PreparedMultiFileDiff>
auto def = SyntaxDefinitionManager::instance()->getByExtension( filename ); UIDiffView::prepareMultiFileDiff( const std::string& patchText,
const std::shared_ptr<std::atomic_bool>& cancelled ) {
if ( cancelled && cancelled->load( std::memory_order_relaxed ) )
return {};
auto diffs = splitDiff( patchText );
auto prepared = std::make_shared<PreparedMultiFileDiff>();
prepared->patches.reserve( diffs.size() );
for ( const auto& diff : diffs ) {
if ( cancelled && cancelled->load( std::memory_order_relaxed ) )
return {};
prepared->patches.emplace_back(
preparePatch( diff, "", SubLineDiffAlgorithm::LCS, cancelled ) );
}
if ( cancelled && cancelled->load( std::memory_order_relaxed ) )
return {};
return prepared;
}
void UIDiffView::loadPreparedPatch( PreparedPatch&& patch, const std::string& originalFilePath,
const std::string& oldFilePath, const std::string& repoPath ) {
resetToTextDiffView();
mLines.clear();
if ( patch.isBinaryImage ) {
std::string oldImagePath( oldFilePath );
if ( oldImagePath.empty() ) {
oldImagePath =
resolveImagePatchPath( patch.binaryOldPath, originalFilePath, false, repoPath );
}
std::string newImagePath(
resolveImagePatchPath( patch.binaryNewPath, originalFilePath, true, repoPath ) );
if ( oldImagePath == newImagePath )
oldImagePath.clear();
if ( loadImageDiffFromPaths( oldImagePath, newImagePath ) ) {
if ( !patch.binaryFileName.empty() )
mFileName = String::fromUtf8( patch.binaryFileName );
updateFileHeaderInfo();
return;
}
}
mLines = std::move( patch.lines );
setCompleteViewToggleVisible( patch.hasCompleteFile );
if ( !patch.filename.empty() ) {
auto def = SyntaxDefinitionManager::instance()->getByExtension( patch.filename );
mSyntaxDef = mSyntaxDef =
SyntaxDefinitionManager::instance()->getLanguageDefinition( def.getLanguageIndex() ); SyntaxDefinitionManager::instance()->getLanguageDefinition( def.getLanguageIndex() );
mFileName = std::move( filename ); mFileName = String::fromUtf8( patch.filename );
} }
updateFileHeaderInfo(); updateFileHeaderInfo();
@@ -1368,6 +1459,12 @@ void UIDiffView::loadFromPatch( const std::string& patchText, const std::string&
onSizeChange(); onSizeChange();
} }
void UIDiffView::loadFromPatch( const std::string& patchText, const std::string& originalFilePath,
const std::string& oldFilePath, const std::string& repoPath ) {
loadPreparedPatch( preparePatch( patchText, originalFilePath, mSubLineDiffAlgorithm, {} ),
originalFilePath, oldFilePath, repoPath );
}
void UIDiffView::loadFromStrings( const std::string& oldText, const std::string& newText, void UIDiffView::loadFromStrings( const std::string& oldText, const std::string& newText,
const std::string& originalFilePath ) { const std::string& originalFilePath ) {
resetToTextDiffView(); resetToTextDiffView();
+84 -137
View File
@@ -49,9 +49,7 @@ UICodeEditor* UICodeEditor::NewOpt( const bool& autoRegisterBaseCommands,
return eeNew( UICodeEditor, ( autoRegisterBaseCommands, autoRegisterBaseKeybindings ) ); return eeNew( UICodeEditor, ( autoRegisterBaseCommands, autoRegisterBaseKeybindings ) );
} }
using CodeEditorKeyBindingMap = std::map<KeyBindings::Shortcut, std::string>; static KeyBindings::ShortcutMap createDefaultCodeEditorKeybindings() {
static CodeEditorKeyBindingMap createDefaultCodeEditorKeybindings() {
return { return {
{ { KEY_BACKSPACE, KeyMod::getDefaultModifier() }, "delete-to-previous-word" }, { { KEY_BACKSPACE, KeyMod::getDefaultModifier() }, "delete-to-previous-word" },
{ { KEY_BACKSPACE, KEYMOD_SHIFT }, "delete-to-previous-char" }, { { KEY_BACKSPACE, KEYMOD_SHIFT }, "delete-to-previous-char" },
@@ -132,12 +130,12 @@ static CodeEditorKeyBindingMap createDefaultCodeEditorKeybindings() {
}; };
} }
static std::shared_ptr<const CodeEditorKeyBindingMap> getCachedDefaultCodeEditorKeybindings() { std::shared_ptr<const KeyBindings> UICodeEditor::getDefaultKeybindings() {
struct Cache { struct Cache {
Mutex mutex; Mutex mutex;
Uint32 defaultModifier{ 0 }; Uint32 defaultModifier{ 0 };
Uint32 secondaryModifier{ 0 }; Uint32 secondaryModifier{ 0 };
std::shared_ptr<const CodeEditorKeyBindingMap> bindings; std::shared_ptr<const KeyBindings> bindings;
}; };
static Cache cache; static Cache cache;
@@ -146,18 +144,15 @@ static std::shared_ptr<const CodeEditorKeyBindingMap> getCachedDefaultCodeEditor
Lock lock( cache.mutex ); Lock lock( cache.mutex );
if ( !cache.bindings || cache.defaultModifier != defaultModifier || if ( !cache.bindings || cache.defaultModifier != defaultModifier ||
cache.secondaryModifier != secondaryModifier ) { cache.secondaryModifier != secondaryModifier ) {
cache.bindings = auto bindings = std::make_shared<KeyBindings>( nullptr );
std::make_shared<const CodeEditorKeyBindingMap>( createDefaultCodeEditorKeybindings() ); bindings->addKeybinds( createDefaultCodeEditorKeybindings() );
cache.bindings = std::move( bindings );
cache.defaultModifier = defaultModifier; cache.defaultModifier = defaultModifier;
cache.secondaryModifier = secondaryModifier; cache.secondaryModifier = secondaryModifier;
} }
return cache.bindings; return cache.bindings;
} }
const std::map<KeyBindings::Shortcut, std::string> UICodeEditor::getDefaultKeybindings() {
return *getCachedDefaultCodeEditorKeybindings();
}
const MouseBindings::ShortcutMap UICodeEditor::getDefaultMousebindings() { const MouseBindings::ShortcutMap UICodeEditor::getDefaultMousebindings() {
return { { { MouseAction::Down, EE_BUTTON_LMASK, KeyMod::getDefaultModifier() }, return { { { MouseAction::Down, EE_BUTTON_LMASK, KeyMod::getDefaultModifier() },
"add-cursor-at-mouse-position" }, "add-cursor-at-mouse-position" },
@@ -2914,7 +2909,7 @@ void UICodeEditor::addKeyBindsString( const std::map<std::string, std::string>&
} }
} }
void UICodeEditor::addKeyBinds( const std::map<KeyBindings::Shortcut, std::string>& binds, void UICodeEditor::addKeyBinds( const KeyBindings::ShortcutMap& binds,
const bool& allowLocked ) { const bool& allowLocked ) {
mKeyBindings.addKeybinds( binds ); mKeyBindings.addKeybinds( binds );
for ( const auto& bind : binds ) { for ( const auto& bind : binds ) {
@@ -4853,129 +4848,81 @@ void UICodeEditor::drawLineEndings( const DocumentLineRange& lineRange, const Ve
} }
} }
void UICodeEditor::registerCommands() { std::shared_ptr<const TextDocument::DocumentRefCommands> UICodeEditor::getDefaultEditorCommands() {
mUnlockedCmd.reserve( 8 ); #define EE_CODE_EDITOR_COMMAND( Name, Method ) \
mDoc->setCommand( "move-to-previous-line", []( Client* client ) { { \
static_cast<UICodeEditor*>( client )->moveToPreviousLine(); Name, []( Client* client ) { static_cast<UICodeEditor*>( client )->Method(); } \
} ); }
mDoc->setCommand( "move-to-next-line", []( Client* client ) { static const auto commands = std::make_shared<const TextDocument::DocumentRefCommands>(
static_cast<UICodeEditor*>( client )->moveToNextLine(); TextDocument::DocumentRefCommands{
} ); EE_CODE_EDITOR_COMMAND( "move-to-previous-line", moveToPreviousLine ),
mDoc->setCommand( "move-to-previous-page", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "move-to-next-line", moveToNextLine ),
static_cast<UICodeEditor*>( client )->moveToPreviousPage(); EE_CODE_EDITOR_COMMAND( "move-to-previous-page", moveToPreviousPage ),
} ); EE_CODE_EDITOR_COMMAND( "move-to-next-page", moveToNextPage ),
mDoc->setCommand( "move-to-next-page", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "move-to-start-of-line", moveToStartOfLine ),
static_cast<UICodeEditor*>( client )->moveToNextPage(); EE_CODE_EDITOR_COMMAND( "move-to-end-of-line", moveToEndOfLine ),
} ); EE_CODE_EDITOR_COMMAND( "move-to-start-of-content", moveToStartOfContent ),
mDoc->setCommand( "move-to-start-of-line", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "select-to-previous-line", selectToPreviousLine ),
static_cast<UICodeEditor*>( client )->moveToStartOfLine(); EE_CODE_EDITOR_COMMAND( "select-to-next-line", selectToNextLine ),
} ); EE_CODE_EDITOR_COMMAND( "select-to-start-of-line", selectToStartOfLine ),
mDoc->setCommand( "move-to-end-of-line", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "select-to-end-of-line", selectToEndOfLine ),
static_cast<UICodeEditor*>( client )->moveToEndOfLine(); EE_CODE_EDITOR_COMMAND( "select-to-start-of-content", selectToStartOfContent ),
} ); EE_CODE_EDITOR_COMMAND( "move-scroll-up", moveScrollUp ),
mDoc->setCommand( "move-to-start-of-content", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "move-scroll-down", moveScrollDown ),
static_cast<UICodeEditor*>( client )->moveToStartOfContent(); EE_CODE_EDITOR_COMMAND( "jump-lines-up", jumpLinesUp ),
} ); EE_CODE_EDITOR_COMMAND( "jump-lines-down", jumpLinesDown ),
mDoc->setCommand( "select-to-previous-line", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "indent", indent ),
static_cast<UICodeEditor*>( client )->selectToPreviousLine(); EE_CODE_EDITOR_COMMAND( "unindent", unindent ),
} ); EE_CODE_EDITOR_COMMAND( "copy", copy ),
mDoc->setCommand( "select-to-next-line", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "cut", cut ),
static_cast<UICodeEditor*>( client )->selectToNextLine(); EE_CODE_EDITOR_COMMAND( "paste", paste ),
} ); EE_CODE_EDITOR_COMMAND( "font-size-grow", fontSizeGrow ),
mDoc->setCommand( "select-to-start-of-line", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "font-size-shrink", fontSizeShrink ),
static_cast<UICodeEditor*>( client )->selectToStartOfLine(); EE_CODE_EDITOR_COMMAND( "font-size-reset", fontSizeReset ),
} ); { "lock",
mDoc->setCommand( "select-to-end-of-line", []( Client* client ) { []( Client* client ) { static_cast<UICodeEditor*>( client )->setLocked( true ); } },
static_cast<UICodeEditor*>( client )->selectToEndOfLine(); { "unlock",
} ); []( Client* client ) { static_cast<UICodeEditor*>( client )->setLocked( false ); } },
mDoc->setCommand( "select-to-start-of-content", []( Client* client ) { { "lock-toggle",
static_cast<UICodeEditor*>( client )->selectToStartOfContent(); []( Client* client ) {
} ); auto* editor = static_cast<UICodeEditor*>( client );
mDoc->setCommand( "move-scroll-up", []( Client* client ) { editor->setLocked( !editor->isLocked() );
static_cast<UICodeEditor*>( client )->moveScrollUp(); } },
} ); EE_CODE_EDITOR_COMMAND( "open-containing-folder", openContainingFolder ),
mDoc->setCommand( "move-scroll-down", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "copy-containing-folder-path", copyContainingFolderPath ),
static_cast<UICodeEditor*>( client )->moveScrollDown(); EE_CODE_EDITOR_COMMAND( "copy-file-path", copyFilePath ),
} ); { "copy-file-path-and-position",
mDoc->setCommand( "jump-lines-up", []( Client* client ) { []( Client* client ) {
static_cast<UICodeEditor*>( client )->jumpLinesUp(); static_cast<UICodeEditor*>( client )->copyFilePath( true );
} ); } },
mDoc->setCommand( "jump-lines-down", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "open-context-menu", createContextMenu ),
static_cast<UICodeEditor*>( client )->jumpLinesDown(); EE_CODE_EDITOR_COMMAND( "add-cursor-at-mouse-position", addCursorAtMousePosition ),
} ); EE_CODE_EDITOR_COMMAND( "add-cursors-from-current-to-mouse-position",
mDoc->setCommand( "indent", addCursorsFromCurrentToMousePosition ),
[]( Client* client ) { static_cast<UICodeEditor*>( client )->indent(); } ); EE_CODE_EDITOR_COMMAND( "toggle-fold", toggleFoldUnfold ),
mDoc->setCommand( "unindent", EE_CODE_EDITOR_COMMAND( "fold-all", foldAll ),
[]( Client* client ) { static_cast<UICodeEditor*>( client )->unindent(); } ); EE_CODE_EDITOR_COMMAND( "unfold-all", unfoldAll ),
mDoc->setCommand( "copy", EE_CODE_EDITOR_COMMAND( "fold", fold ),
[]( Client* client ) { static_cast<UICodeEditor*>( client )->copy(); } ); EE_CODE_EDITOR_COMMAND( "unfold", unfold ),
mDoc->setCommand( "cut", { "open-hover-url",
[]( Client* client ) { static_cast<UICodeEditor*>( client )->cut(); } ); []( Client* client ) {
mDoc->setCommand( "paste", auto* editor = static_cast<UICodeEditor*>( client );
[]( Client* client ) { static_cast<UICodeEditor*>( client )->paste(); } ); if ( !editor->mLink.empty() )
mDoc->setCommand( "font-size-grow", []( Client* client ) { Engine::instance()->openURI( editor->mLink );
static_cast<UICodeEditor*>( client )->fontSizeGrow(); } },
} ); EE_CODE_EDITOR_COMMAND( "add-cursor-above", addCursorAbove ),
mDoc->setCommand( "font-size-shrink", []( Client* client ) { EE_CODE_EDITOR_COMMAND( "add-cursor-below", addCursorBelow ),
static_cast<UICodeEditor*>( client )->fontSizeShrink(); } );
} ); #undef EE_CODE_EDITOR_COMMAND
mDoc->setCommand( "font-size-reset", []( Client* client ) { return commands;
static_cast<UICodeEditor*>( client )->fontSizeReset(); }
} );
mDoc->setCommand(
"lock", []( Client* client ) { static_cast<UICodeEditor*>( client )->setLocked( true ); } );
mDoc->setCommand( "unlock", []( Client* client ) {
static_cast<UICodeEditor*>( client )->setLocked( false );
} );
mDoc->setCommand( "lock-toggle", []( Client* client ) {
UICodeEditor* editor = static_cast<UICodeEditor*>( client );
editor->setLocked( !editor->isLocked() );
} );
mDoc->setCommand( "open-containing-folder", []( Client* client ) {
static_cast<UICodeEditor*>( client )->openContainingFolder();
} );
mDoc->setCommand( "copy-containing-folder-path", []( Client* client ) {
static_cast<UICodeEditor*>( client )->copyContainingFolderPath();
} );
mDoc->setCommand( "copy-file-path", []( Client* client ) {
static_cast<UICodeEditor*>( client )->copyFilePath();
} );
mDoc->setCommand( "copy-file-path-and-position", []( Client* client ) {
static_cast<UICodeEditor*>( client )->copyFilePath( true );
} );
mDoc->setCommand( "find-replace", [this] { showFindReplace(); } );
mDoc->setCommand( "open-context-menu", []( Client* client ) {
static_cast<UICodeEditor*>( client )->createContextMenu();
} );
mDoc->setCommand( "add-cursor-at-mouse-position", []( Client* client ) {
static_cast<UICodeEditor*>( client )->addCursorAtMousePosition();
} );
mDoc->setCommand( "add-cursors-from-current-to-mouse-position", []( Client* client ) {
static_cast<UICodeEditor*>( client )->addCursorsFromCurrentToMousePosition();
} );
mDoc->setCommand( "toggle-fold", []( Client* client ) {
static_cast<UICodeEditor*>( client )->toggleFoldUnfold();
} );
mDoc->setCommand( "fold-all",
[]( Client* client ) { static_cast<UICodeEditor*>( client )->foldAll(); } );
mDoc->setCommand( "unfold-all",
[]( Client* client ) { static_cast<UICodeEditor*>( client )->unfoldAll(); } );
mDoc->setCommand( "fold",
[]( Client* client ) { static_cast<UICodeEditor*>( client )->fold(); } );
mDoc->setCommand( "unfold",
[]( Client* client ) { static_cast<UICodeEditor*>( client )->unfold(); } );
mDoc->setCommand( "open-hover-url", []( Client* client ) {
UICodeEditor* editor = static_cast<UICodeEditor*>( client );
if ( !editor->mLink.empty() )
Engine::instance()->openURI( editor->mLink );
} );
mDoc->setCommand( "add-cursor-above", []( Client* client ) {
static_cast<UICodeEditor*>( client )->addCursorAbove();
} );
mDoc->setCommand( "add-cursor-below", []( Client* client ) {
static_cast<UICodeEditor*>( client )->addCursorBelow();
} );
void UICodeEditor::registerCommands() {
mDoc->setSharedRefCommands( getDefaultEditorCommands() );
// This is the only default editor command bound to a specific editor instance.
mDoc->setCommand( "find-replace", [this] { showFindReplace(); } );
mUnlockedCmd.reserve( 8 );
mUnlockedCmd.insert( { "copy", "select-all", "open-containing-folder", mUnlockedCmd.insert( { "copy", "select-all", "open-containing-folder",
"copy-containing-folder-path", "copy-file-path", "copy-containing-folder-path", "copy-file-path",
"copy-file-path-and-position", "open-context-menu", "find-replace" } ); "copy-file-path-and-position", "open-context-menu", "find-replace" } );
@@ -5007,9 +4954,9 @@ Tools::UIDocFindReplace* UICodeEditor::getFindReplace() {
} }
void UICodeEditor::registerKeybindings() { void UICodeEditor::registerKeybindings() {
// Editors keep mutable bindings, but the immutable defaults only need to be constructed once // Bindings use copy-on-write storage, so unmodified editors share both lookup maps and only
// for each configured default-modifier pair. // allocate their own copy if an editor-specific binding is changed.
mKeyBindings.addKeybinds( *getCachedDefaultCodeEditorKeybindings() ); mKeyBindings.setKeybinds( *getDefaultKeybindings() );
mMouseBindings.addMousebinds( getDefaultMousebindings() ); mMouseBindings.addMousebinds( getDefaultMousebindings() );
} }
+1 -1
View File
@@ -2071,7 +2071,7 @@ void UISceneNode::addKeyBindsString( const std::map<std::string, std::string>& b
mKeyBindings.addKeybindsString( binds ); mKeyBindings.addKeybindsString( binds );
} }
void UISceneNode::addKeyBinds( const std::map<KeyBindings::Shortcut, std::string>& binds ) { void UISceneNode::addKeyBinds( const KeyBindings::ShortcutMap& binds ) {
mKeyBindings.addKeybinds( binds ); mKeyBindings.addKeybinds( binds );
} }
+1 -1
View File
@@ -1804,7 +1804,7 @@ void UIWindow::addKeyBindsString( const std::map<std::string, std::string>& bind
mKeyBindings.addKeybindsString( binds ); mKeyBindings.addKeybindsString( binds );
} }
void UIWindow::addKeyBinds( const std::map<KeyBindings::Shortcut, std::string>& binds ) { void UIWindow::addKeyBinds( const KeyBindings::ShortcutMap& binds ) {
mKeyBindings.addKeybinds( binds ); mKeyBindings.addKeybinds( binds );
} }
@@ -67,7 +67,7 @@ class UITerminal : public UIWidget {
void addKeyBindsString( const std::map<std::string, std::string>& binds ); void addKeyBindsString( const std::map<std::string, std::string>& binds );
void addKeyBinds( const std::map<KeyBindings::Shortcut, std::string>& binds ); void addKeyBinds( const KeyBindings::ShortcutMap& binds );
bool execute( const std::string& command ); bool execute( const std::string& command );
@@ -412,7 +412,7 @@ void UITerminal::addKeyBindsString( const std::map<std::string, std::string>& bi
mKeyBindings.addKeybindsString( binds ); mKeyBindings.addKeybindsString( binds );
} }
void UITerminal::addKeyBinds( const std::map<KeyBindings::Shortcut, std::string>& binds ) { void UITerminal::addKeyBinds( const KeyBindings::ShortcutMap& binds ) {
mKeyBindings.addKeybinds( binds ); mKeyBindings.addKeybinds( binds );
} }
+1 -1
View File
@@ -201,7 +201,7 @@ int testTextRendering() {
} }
EE_MAIN_FUNC int main( int, char*[] ) { EE_MAIN_FUNC int main( int, char*[] ) {
return testTextRendering(); // return testTextRendering();
win = Engine::instance()->createWindow( WindowSettings( 1366, 768, "eepp - UI Perf Test" ) ); win = Engine::instance()->createWindow( WindowSettings( 1366, 768, "eepp - UI Perf Test" ) );
@@ -8,6 +8,41 @@ using namespace EE;
using namespace EE::UI; using namespace EE::UI;
using namespace EE::Window; using namespace EE::Window;
UTEST( KeyboardShortcut, BulkBindingsPreserveLegacyOrderedReverseLookup ) {
const KeyBindings::Shortcut first{ KEY_A, KEYMOD_NONE };
const KeyBindings::Shortcut second{ KEY_B, KEYMOD_CTRL };
const KeyBindings::Shortcut third{ KEY_C, KEYMOD_CTRL | KEYMOD_SHIFT };
KeyBindings::ShortcutMap defaults;
defaults.emplace( third, "duplicate-command" );
defaults.emplace( first, "duplicate-command" );
defaults.emplace( second, "duplicate-command" );
auto ordered = KeyBindings::getOrderedShortcuts( defaults );
EXPECT_EQ( first.toUint64(), ordered.front().toUint64() );
EXPECT_EQ( third.toUint64(), ordered.back().toUint64() );
KeyBindings bindings( nullptr );
bindings.addKeybinds( defaults );
EXPECT_EQ( third.toUint64(),
bindings.getShortcutFromCommand( "duplicate-command" ).toUint64() );
}
UTEST( KeyboardShortcut, SharedBindingsDetachOnMutation ) {
KeyBindings defaults( nullptr );
defaults.addKeybind( { KEY_A, KEYMOD_CTRL }, "select-all" );
KeyBindings bindings( nullptr );
bindings.setKeybinds( defaults );
EXPECT_TRUE( &bindings.getShortcutMap() == &defaults.getShortcutMap() );
EXPECT_TRUE( bindings.getCommandFromKeyBind( { KEY_A, KEYMOD_CTRL } ) == "select-all" );
bindings.addKeybind( { KEY_C, KEYMOD_CTRL }, "copy" );
EXPECT_TRUE( &bindings.getShortcutMap() != &defaults.getShortcutMap() );
EXPECT_TRUE( bindings.getCommandFromKeyBind( { KEY_C, KEYMOD_CTRL } ) == "copy" );
EXPECT_TRUE( defaults.getCommandFromKeyBind( { KEY_C, KEYMOD_CTRL } ).empty() );
EXPECT_TRUE( defaults.getCommandFromKeyBind( { KEY_A, KEYMOD_CTRL } ) == "select-all" );
}
UTEST( KeyboardShortcut, AggregateAltUsesPrimaryAltAndKeepsAltGrDistinct ) { UTEST( KeyboardShortcut, AggregateAltUsesPrimaryAltAndKeepsAltGrDistinct ) {
UIApplication app( WindowSettings{ 320, 240, "eepp - keyboard shortcut tests" } ); UIApplication app( WindowSettings{ 320, 240, "eepp - keyboard shortcut tests" } );
auto& bindings = app.getUI()->getKeyBindings(); auto& bindings = app.getUI()->getKeyBindings();
@@ -1,4 +1,5 @@
#include "utest.hpp" #include "utest.hpp"
#include <algorithm>
#include <eepp/system/filesystem.hpp> #include <eepp/system/filesystem.hpp>
#include <eepp/system/sys.hpp> #include <eepp/system/sys.hpp>
#include <eepp/ui/doc/textdocument.hpp> #include <eepp/ui/doc/textdocument.hpp>
@@ -6,6 +7,28 @@
using namespace EE::UI::Doc; using namespace EE::UI::Doc;
using namespace EE::System; using namespace EE::System;
UTEST( TextDocument, BuiltinCommandsRemainAvailableWithoutPerDocumentClosures ) {
TextDocument doc;
doc.textInput( "content" );
EXPECT_TRUE( doc.hasCommand( "select-all" ) );
EXPECT_TRUE( doc.hasCommand( "delete-selection" ) );
doc.execute( "select-all" );
doc.execute( "delete-selection" );
EXPECT_TRUE( doc.isEmpty() );
auto commands = doc.getCommandList();
EXPECT_TRUE( std::find( commands.begin(), commands.end(), "undo" ) != commands.end() );
}
UTEST( TextDocument, RemovingBuiltinCommandIsLocalToDocument ) {
TextDocument first;
TextDocument second;
EXPECT_TRUE( first.removeCommand( "undo" ) );
EXPECT_FALSE( first.hasCommand( "undo" ) );
EXPECT_TRUE( second.hasCommand( "undo" ) );
}
UTEST( TextRanges, keepsCommonSelectionsInline ) { UTEST( TextRanges, keepsCommonSelectionsInline ) {
TextRanges ranges; TextRanges ranges;
EXPECT_TRUE( ranges.is_small() ); EXPECT_TRUE( ranges.is_small() );
+51 -11
View File
@@ -25,6 +25,43 @@ class TestableCodeEditor : public UICodeEditor {
void clearLongestLineWidthDirtyForTest() { mLongestLineWidthDirty = false; } void clearLongestLineWidthDirtyForTest() { mLongestLineWidthDirty = false; }
}; };
UTEST( SyntaxColorScheme, CopiesShareStorageAndDetachOnMutation ) {
auto defaults = SyntaxColorScheme::getDefaultDark();
auto copy = defaults;
EXPECT_TRUE( &defaults.getSyntaxStyle( SyntaxStyleTypes::Keyword ) ==
&copy.getSyntaxStyle( SyntaxStyleTypes::Keyword ) );
const auto defaultKeyword = defaults.getSyntaxStyle( SyntaxStyleTypes::Keyword ).color;
copy.setSyntaxStyle( SyntaxStyleTypes::Keyword, SyntaxColorScheme::Style{ Color::Red } );
EXPECT_TRUE( copy.getSyntaxStyle( SyntaxStyleTypes::Keyword ).color == Color::Red );
EXPECT_TRUE( defaults.getSyntaxStyle( SyntaxStyleTypes::Keyword ).color == defaultKeyword );
EXPECT_TRUE( &defaults.getSyntaxStyle( SyntaxStyleTypes::Keyword ) !=
&copy.getSyntaxStyle( SyntaxStyleTypes::Keyword ) );
}
UTEST( SyntaxDefinitionManager, ManyLanguageExtensionCacheInvalidatesOnAdd ) {
auto* manager = SyntaxDefinitionManager::instance();
const std::string extension( ".eepp-many-languages-cache-test" );
const std::string preDefinitionExtension( ".eepp-many-languages-predefinition-cache-test" );
EXPECT_FALSE( manager->extensionCanRepresentManyLanguages( extension ) );
manager->add( { "EEPP Cache Test A", { extension }, {} } );
EXPECT_FALSE( manager->extensionCanRepresentManyLanguages( extension ) );
manager->add( { "EEPP Cache Test B", { extension }, {} } );
EXPECT_TRUE( manager->extensionCanRepresentManyLanguages( extension ) );
EXPECT_TRUE( manager->extensionCanRepresentManyLanguages( extension ) );
manager->add( { "EEPP Cache Test C", { preDefinitionExtension }, {} } );
EXPECT_FALSE( manager->extensionCanRepresentManyLanguages( preDefinitionExtension ) );
manager->addPreDefinition( { "EEPP Cache Test PreDefinition",
[]() -> SyntaxDefinition& {
return SyntaxDefinitionManager::instance()->add(
{ "EEPP Cache Test PreDefinition", {}, {} } );
},
{ preDefinitionExtension } } );
EXPECT_TRUE( manager->extensionCanRepresentManyLanguages( preDefinitionExtension ) );
}
UTEST( MainThreadLifetime, InvalidatedCallbacksDoNotRun ) { UTEST( MainThreadLifetime, InvalidatedCallbacksDoNotRun ) {
UIApplication app( WindowSettings{ 320, 240, "eepp - main thread lifetime test" } ); UIApplication app( WindowSettings{ 320, 240, "eepp - main thread lifetime test" } );
int owner = 42; int owner = 42;
@@ -101,25 +138,28 @@ UTEST( UICodeEditor, DefaultKeybindingCacheTracksConfiguredModifiers ) {
const Uint32 originalSecondaryModifier = KeyMod::getDefaultSecondaryModifier(); const Uint32 originalSecondaryModifier = KeyMod::getDefaultSecondaryModifier();
auto defaultBindings = UICodeEditor::getDefaultKeybindings(); auto defaultBindings = UICodeEditor::getDefaultKeybindings();
auto copy = defaultBindings.find( { KEY_C, originalDefaultModifier } ); const auto& defaultShortcutMap = defaultBindings->getShortcutMap();
EXPECT_TRUE( copy != defaultBindings.end() ); auto copy = defaultShortcutMap.find( KeyBindings::Shortcut{ KEY_C, originalDefaultModifier } );
if ( copy != defaultBindings.end() ) EXPECT_TRUE( copy != defaultShortcutMap.end() );
if ( copy != defaultShortcutMap.end() )
EXPECT_STREQ( "copy", copy->second.c_str() ); EXPECT_STREQ( "copy", copy->second.c_str() );
KeyMod::setDefaultModifier( KEYMOD_LALT ); KeyMod::setDefaultModifier( KEYMOD_LALT );
KeyMod::setDefaultSecondaryModifier( KEYMOD_META ); KeyMod::setDefaultSecondaryModifier( KEYMOD_META );
auto reconfiguredBindings = UICodeEditor::getDefaultKeybindings(); auto reconfiguredBindings = UICodeEditor::getDefaultKeybindings();
copy = reconfiguredBindings.find( { KEY_C, KEYMOD_LALT } ); const auto& reconfiguredShortcutMap = reconfiguredBindings->getShortcutMap();
EXPECT_TRUE( copy != reconfiguredBindings.end() ); copy = reconfiguredShortcutMap.find( KeyBindings::Shortcut{ KEY_C, KEYMOD_LALT } );
if ( copy != reconfiguredBindings.end() ) EXPECT_TRUE( copy != reconfiguredShortcutMap.end() );
if ( copy != reconfiguredShortcutMap.end() )
EXPECT_STREQ( "copy", copy->second.c_str() ); EXPECT_STREQ( "copy", copy->second.c_str() );
KeyMod::setDefaultModifier( originalDefaultModifier ); KeyMod::setDefaultModifier( originalDefaultModifier );
KeyMod::setDefaultSecondaryModifier( originalSecondaryModifier ); KeyMod::setDefaultSecondaryModifier( originalSecondaryModifier );
auto restoredBindings = UICodeEditor::getDefaultKeybindings(); auto restoredBindings = UICodeEditor::getDefaultKeybindings();
copy = restoredBindings.find( { KEY_C, originalDefaultModifier } ); const auto& restoredShortcutMap = restoredBindings->getShortcutMap();
EXPECT_TRUE( copy != restoredBindings.end() ); copy = restoredShortcutMap.find( KeyBindings::Shortcut{ KEY_C, originalDefaultModifier } );
if ( copy != restoredBindings.end() ) EXPECT_TRUE( copy != restoredShortcutMap.end() );
if ( copy != restoredShortcutMap.end() )
EXPECT_STREQ( "copy", copy->second.c_str() ); EXPECT_STREQ( "copy", copy->second.c_str() );
} }
@@ -236,7 +276,7 @@ UTEST( KeybindingsHelper, PreservesUserShortcutWhenAddingBinding ) {
std::unordered_map<std::string, std::string> keybindings; std::unordered_map<std::string, std::string> keybindings;
std::unordered_map<std::string, std::string> invertedKeybindings; std::unordered_map<std::string, std::string> invertedKeybindings;
const std::map<KeyBindings::Shortcut, std::string> defaultKeybindings{ const KeyBindings::ShortcutMap defaultKeybindings{
{ { KEY_D, KeyMod::getDefaultModifier() }, "select-word" }, { { KEY_D, KeyMod::getDefaultModifier() }, "select-word" },
{ { KEY_X, KeyMod::getDefaultModifier() }, "cut" }, { { KEY_X, KeyMod::getDefaultModifier() }, "cut" },
}; };
@@ -275,7 +315,7 @@ UTEST( KeybindingsHelper, RestoresMissingCommandWhenShortcutIsFree ) {
std::unordered_map<std::string, std::string> keybindings; std::unordered_map<std::string, std::string> keybindings;
std::unordered_map<std::string, std::string> invertedKeybindings; std::unordered_map<std::string, std::string> invertedKeybindings;
const std::map<KeyBindings::Shortcut, std::string> defaultKeybindings{ const KeyBindings::ShortcutMap defaultKeybindings{
{ { KEY_D, KeyMod::getDefaultModifier() }, "select-word" }, { { KEY_D, KeyMod::getDefaultModifier() }, "select-word" },
{ { KEY_X, KeyMod::getDefaultModifier() }, "cut" }, { { KEY_X, KeyMod::getDefaultModifier() }, "cut" },
}; };
+46 -2
View File
@@ -1,4 +1,5 @@
#include "utest.h" #include "utest.h"
#include <atomic>
#include <eepp/graphics/image.hpp> #include <eepp/graphics/image.hpp>
#include <eepp/system/filesystem.hpp> #include <eepp/system/filesystem.hpp>
#include <eepp/ui/doc/textdocument.hpp> #include <eepp/ui/doc/textdocument.hpp>
@@ -8,6 +9,7 @@
#include <eepp/ui/uicodeeditor.hpp> #include <eepp/ui/uicodeeditor.hpp>
#include <eepp/ui/uiscenenode.hpp> #include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uiscrollview.hpp> #include <eepp/ui/uiscrollview.hpp>
#include <thread>
using namespace EE; using namespace EE;
using namespace EE::UI; using namespace EE::UI;
@@ -124,11 +126,15 @@ diff --git a/second.txt b/second.txt
)patch"; )patch";
auto* viewer = auto* viewer =
UIDiffView::NewMultiFileDiffViewer( patchText, "", UIDiffView::ViewMode::SideBySide ); UIDiffView::NewMultiFileDiffViewer( patchText, "", UIDiffView::ViewMode::SideBySide, true );
auto diffViews = viewer->findAllByType<UIDiffView>( UI_TYPE_DIFF_VIEW ); auto diffViews = viewer->findAllByType<UIDiffView>( UI_TYPE_DIFF_VIEW );
ASSERT_EQ( size_t{ 2 }, diffViews.size() ); ASSERT_EQ( size_t{ 2 }, diffViews.size() );
for ( const auto* diffView : diffViews ) for ( const auto* diffView : diffViews ) {
EXPECT_EQ( UIDiffView::ViewMode::SideBySide, diffView->getViewMode() ); EXPECT_EQ( UIDiffView::ViewMode::SideBySide, diffView->getViewMode() );
EXPECT_FALSE( diffView->isViewModeToggleVisible() );
EXPECT_FALSE( diffView->isCompleteViewToggleVisible() );
EXPECT_TRUE( diffView->isInteractiveFileHeader() );
}
UIDiffView::setMultiFileViewMode( viewer, UIDiffView::ViewMode::Unified ); UIDiffView::setMultiFileViewMode( viewer, UIDiffView::ViewMode::Unified );
UIDiffView::setMultiFileCollapsed( viewer, true ); UIDiffView::setMultiFileCollapsed( viewer, true );
@@ -146,6 +152,44 @@ diff --git a/second.txt b/second.txt
eeDelete( viewer ); eeDelete( viewer );
} }
UTEST( UIDiffView, PreparedMultiFileDiffBuildsViewer ) {
std::string patchText = R"patch(diff --git a/first.txt b/first.txt
--- a/first.txt
+++ b/first.txt
@@ -1 +1 @@
-old
+new
diff --git a/second.txt b/second.txt
--- a/second.txt
+++ b/second.txt
@@ -1 +1 @@
-before
+after
)patch";
std::shared_ptr<UIDiffView::PreparedMultiFileDiff> prepared;
std::thread worker(
[&prepared, &patchText] { prepared = UIDiffView::prepareMultiFileDiff( patchText ); } );
worker.join();
ASSERT_TRUE( prepared );
UIApplication app( WindowSettings{ 800, 600, "eepp - unit tests" } );
auto* viewer = UIDiffView::NewMultiFileDiffViewer( std::move( prepared ), "",
UIDiffView::ViewMode::SideBySide );
ASSERT_TRUE( viewer );
auto diffViews = viewer->findAllByType<UIDiffView>( UI_TYPE_DIFF_VIEW );
ASSERT_EQ( size_t{ 2 }, diffViews.size() );
EXPECT_TRUE( diffViews[0]->getFileName().toUtf8() == "first.txt" );
EXPECT_TRUE( diffViews[1]->getFileName().toUtf8() == "second.txt" );
EXPECT_EQ( UIDiffView::ViewMode::SideBySide, diffViews[0]->getViewMode() );
eeDelete( viewer );
}
UTEST( UIDiffView, PreparedMultiFileDiffHonorsCancellation ) {
auto cancelled = std::make_shared<std::atomic_bool>( true );
EXPECT_FALSE( UIDiffView::prepareMultiFileDiff( "diff --git a/a b/a\n", cancelled ) );
}
UTEST( UIDiffView, MultiFileViewerHandlesLargePatches ) { UTEST( UIDiffView, MultiFileViewerHandlesLargePatches ) {
UIApplication app( WindowSettings{ 800, 600, "eepp - unit tests" } ); UIApplication app( WindowSettings{ 800, 600, "eepp - unit tests" } );
app.getUI()->flushDirtyStyleAndLayout(); app.getUI()->flushDirtyStyleAndLayout();
+25 -21
View File
@@ -1231,15 +1231,15 @@ UITabWidget* App::getSidePanel() const {
return mSidePanel; return mSidePanel;
} }
const std::map<KeyBindings::Shortcut, std::string>& App::getRealLocalKeybindings() const { const KeyBindings::ShortcutMap& App::getRealLocalKeybindings() const {
return mRealLocalKeybindings; return mRealLocalKeybindings;
} }
const std::map<KeyBindings::Shortcut, std::string>& App::getRealSplitterKeybindings() const { const KeyBindings::ShortcutMap& App::getRealSplitterKeybindings() const {
return mRealSplitterKeybindings; return mRealSplitterKeybindings;
} }
const std::map<KeyBindings::Shortcut, std::string>& App::getRealTerminalKeybindings() const { const KeyBindings::ShortcutMap& App::getRealTerminalKeybindings() const {
return mRealTerminalKeybindings; return mRealTerminalKeybindings;
} }
@@ -1510,32 +1510,35 @@ void App::loadKeybindings() {
getMigrateKeybindings(), mConfig.iniState ); getMigrateKeybindings(), mConfig.iniState );
auto localKeybindings = getLocalKeybindings(); auto localKeybindings = getLocalKeybindings();
for ( const auto& kb : localKeybindings ) { for ( const auto& shortcut : KeyBindings::getOrderedShortcuts( localKeybindings ) ) {
auto found = mKeybindingsInvert.find( kb.second ); const auto& command = localKeybindings.find( shortcut )->second;
auto found = mKeybindingsInvert.find( command );
if ( found != mKeybindingsInvert.end() ) { if ( found != mKeybindingsInvert.end() ) {
mRealLocalKeybindings[bindings.getShortcutFromString( found->second )] = kb.second; mRealLocalKeybindings[bindings.getShortcutFromString( found->second )] = command;
} else { } else {
mRealLocalKeybindings[kb.first] = kb.second; mRealLocalKeybindings[shortcut] = command;
} }
} }
auto localSplitterKeybindings = UICodeEditorSplitter::getLocalDefaultKeybindings(); auto localSplitterKeybindings = UICodeEditorSplitter::getLocalDefaultKeybindings();
for ( const auto& kb : localSplitterKeybindings ) { for ( const auto& shortcut : KeyBindings::getOrderedShortcuts( localSplitterKeybindings ) ) {
auto found = mKeybindingsInvert.find( kb.second ); const auto& command = localSplitterKeybindings.find( shortcut )->second;
auto found = mKeybindingsInvert.find( command );
if ( found != mKeybindingsInvert.end() ) { if ( found != mKeybindingsInvert.end() ) {
mRealSplitterKeybindings[bindings.getShortcutFromString( found->second )] = kb.second; mRealSplitterKeybindings[bindings.getShortcutFromString( found->second )] = command;
} else { } else {
mRealSplitterKeybindings[kb.first] = kb.second; mRealSplitterKeybindings[shortcut] = command;
} }
} }
auto localTerminalKeybindings = TerminalManager::getTerminalKeybindings(); auto localTerminalKeybindings = TerminalManager::getTerminalKeybindings();
for ( const auto& kb : localTerminalKeybindings ) { for ( const auto& shortcut : KeyBindings::getOrderedShortcuts( localTerminalKeybindings ) ) {
auto found = mKeybindingsInvert.find( kb.second ); const auto& command = localTerminalKeybindings.find( shortcut )->second;
auto found = mKeybindingsInvert.find( command );
if ( found != mKeybindingsInvert.end() ) { if ( found != mKeybindingsInvert.end() ) {
mRealTerminalKeybindings[bindings.getShortcutFromString( found->second )] = kb.second; mRealTerminalKeybindings[bindings.getShortcutFromString( found->second )] = command;
} else { } else {
mRealTerminalKeybindings[kb.first] = kb.second; mRealTerminalKeybindings[shortcut] = command;
} }
} }
} }
@@ -2100,7 +2103,7 @@ const AppConfig& App::getConfig() const {
return mConfig; return mConfig;
} }
const std::map<KeyBindings::Shortcut, std::string>& App::getRealDefaultKeybindings() { const KeyBindings::ShortcutMap& App::getRealDefaultKeybindings() {
if ( mRealDefaultKeybindings.empty() ) { if ( mRealDefaultKeybindings.empty() ) {
mRealDefaultKeybindings.insert( mRealLocalKeybindings.begin(), mRealDefaultKeybindings.insert( mRealLocalKeybindings.begin(),
mRealLocalKeybindings.end() ); mRealLocalKeybindings.end() );
@@ -2112,7 +2115,7 @@ const std::map<KeyBindings::Shortcut, std::string>& App::getRealDefaultKeybindin
return mRealDefaultKeybindings; return mRealDefaultKeybindings;
} }
std::map<KeyBindings::Shortcut, std::string> App::getDefaultKeybindings() { KeyBindings::ShortcutMap App::getDefaultKeybindings() {
auto bindings = UICodeEditorSplitter::getDefaultKeybindings(); auto bindings = UICodeEditorSplitter::getDefaultKeybindings();
auto local = getLocalKeybindings(); auto local = getLocalKeybindings();
auto app = TerminalManager::getTerminalKeybindings(); auto app = TerminalManager::getTerminalKeybindings();
@@ -2127,7 +2130,7 @@ static Uint32 DefaultSwitchToStatusPanelModifier = KeyMod::getDefaultModifier();
static Uint32 DefaultSwitchToStatusPanelModifier = KeyMod::getDefaultSecondaryModifier(); static Uint32 DefaultSwitchToStatusPanelModifier = KeyMod::getDefaultSecondaryModifier();
#endif #endif
std::map<KeyBindings::Shortcut, std::string> App::getLocalKeybindings() { KeyBindings::ShortcutMap App::getLocalKeybindings() {
return { return {
{ { KEY_PRINTSCREEN, KEYMOD_NONE }, "take-screenshot" }, { { KEY_PRINTSCREEN, KEYMOD_NONE }, "take-screenshot" },
{ { KEY_RETURN, KeyMod::getDefaultSecondaryModifier() | KeyMod::getDefaultModifier() }, { { KEY_RETURN, KeyMod::getDefaultSecondaryModifier() | KeyMod::getDefaultModifier() },
@@ -2626,15 +2629,16 @@ void App::loadAudioFromPath( const std::string& path, bool autoPlay ) {
} }
void App::loadDiffFromMemory( const std::string& content, const std::string& originalFilePath, void App::loadDiffFromMemory( const std::string& content, const std::string& originalFilePath,
const std::string& oldFilePath, const std::string& repoPath ) { const std::string& oldFilePath, const std::string& repoPath,
bool interactiveFileHeaders ) {
if ( UIDiffView::isMultiFileDiff( content ) ) { if ( UIDiffView::isMultiFileDiff( content ) ) {
auto diffViewTitle = i18n( "diff_viewer", "Diff Viewer" ) + ": " + originalFilePath; auto diffViewTitle = i18n( "diff_viewer", "Diff Viewer" ) + ": " + originalFilePath;
UIIcon* icon = getUISceneNode()->findIcon( "filetype-diff" ); UIIcon* icon = getUISceneNode()->findIcon( "filetype-diff" );
if ( !icon ) if ( !icon )
icon = getUISceneNode()->findIcon( "file" ); icon = getUISceneNode()->findIcon( "file" );
auto scrollView = auto scrollView = UIDiffView::NewMultiFileDiffViewer(
UIDiffView::NewMultiFileDiffViewer( content, repoPath, mConfig.editor.diffViewMode ); content, repoPath, mConfig.editor.diffViewMode, interactiveFileHeaders );
auto [tab, iv] = getSplitter()->createWidget( scrollView, diffViewTitle ); auto [tab, iv] = getSplitter()->createWidget( scrollView, diffViewTitle );
if ( icon ) if ( icon )
tab->setIcon( icon->createDrawable( getMenuIconSize() ) ); tab->setIcon( icon->createDrawable( getMenuIconSize() ) );
+12 -12
View File
@@ -179,11 +179,11 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
DrawablePtr findIcon( const std::string& name, const size_t iconSize ); DrawablePtr findIcon( const std::string& name, const size_t iconSize );
const std::map<KeyBindings::Shortcut, std::string>& getRealDefaultKeybindings(); const KeyBindings::ShortcutMap& getRealDefaultKeybindings();
std::map<KeyBindings::Shortcut, std::string> getDefaultKeybindings(); KeyBindings::ShortcutMap getDefaultKeybindings();
std::map<KeyBindings::Shortcut, std::string> getLocalKeybindings(); KeyBindings::ShortcutMap getLocalKeybindings();
std::map<std::string, std::string> getMigrateKeybindings(); std::map<std::string, std::string> getMigrateKeybindings();
@@ -581,8 +581,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
void loadDiffFromPaths( const std::string& oldPath, const std::string& newPath ); void loadDiffFromPaths( const std::string& oldPath, const std::string& newPath );
void loadDiffFromMemory( const std::string& content, const std::string& originalFilePath = "", void loadDiffFromMemory( const std::string& content, const std::string& originalFilePath = "",
const std::string& oldFilePath = "", const std::string& oldFilePath = "", const std::string& repoPath = "",
const std::string& repoPath = "" ); bool interactiveFileHeaders = false );
void loadDiffFromStrings( const std::string& str, const std::string& otherStr ); void loadDiffFromStrings( const std::string& str, const std::string& otherStr );
void configureDiffView( UIDiffView* diffView ); void configureDiffView( UIDiffView* diffView );
@@ -619,11 +619,11 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
UITabWidget* getSidePanel() const; UITabWidget* getSidePanel() const;
const std::map<KeyBindings::Shortcut, std::string>& getRealLocalKeybindings() const; const KeyBindings::ShortcutMap& getRealLocalKeybindings() const;
const std::map<KeyBindings::Shortcut, std::string>& getRealSplitterKeybindings() const; const KeyBindings::ShortcutMap& getRealSplitterKeybindings() const;
const std::map<KeyBindings::Shortcut, std::string>& getRealTerminalKeybindings() const; const KeyBindings::ShortcutMap& getRealTerminalKeybindings() const;
const std::string& getFileToOpen() const; const std::string& getFileToOpen() const;
@@ -699,10 +699,10 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
std::unordered_map<std::string, std::string> mGlobalSearchKeybindings; std::unordered_map<std::string, std::string> mGlobalSearchKeybindings;
std::unordered_map<std::string, std::string> mDocumentSearchKeybindings; std::unordered_map<std::string, std::string> mDocumentSearchKeybindings;
std::unordered_map<std::string, std::string> mStatusBarKeybindings; std::unordered_map<std::string, std::string> mStatusBarKeybindings;
std::map<KeyBindings::Shortcut, std::string> mRealLocalKeybindings; KeyBindings::ShortcutMap mRealLocalKeybindings;
std::map<KeyBindings::Shortcut, std::string> mRealSplitterKeybindings; KeyBindings::ShortcutMap mRealSplitterKeybindings;
std::map<KeyBindings::Shortcut, std::string> mRealTerminalKeybindings; KeyBindings::ShortcutMap mRealTerminalKeybindings;
std::map<KeyBindings::Shortcut, std::string> mRealDefaultKeybindings; KeyBindings::ShortcutMap mRealDefaultKeybindings;
std::unordered_map<std::string, std::string> mMousebindings; std::unordered_map<std::string, std::string> mMousebindings;
std::unordered_map<std::string, std::string> mMousebindingsInvert; std::unordered_map<std::string, std::string> mMousebindingsInvert;
std::string mConfigPath; std::string mConfigPath;
+15 -12
View File
@@ -72,17 +72,19 @@ void KeybindingsHelper::updateKeybindings(
IniFile& ini, const std::string& group, Input* input, IniFile& ini, const std::string& group, Input* input,
std::unordered_map<std::string, std::string>& keybindings, std::unordered_map<std::string, std::string>& keybindings,
std::unordered_map<std::string, std::string>& invertedKeybindings, std::unordered_map<std::string, std::string>& invertedKeybindings,
const std::map<KeyBindings::Shortcut, std::string>& defKeybindings, bool forceRebind, const KeyBindings::ShortcutMap& defKeybindings, bool forceRebind,
const std::map<std::string, std::string>& migrateKeyindings, IniFile& iniState ) { const std::map<std::string, std::string>& migrateKeyindings, IniFile& iniState ) {
KeyBindings bindings( input ); KeyBindings bindings( input );
bool added = false; bool added = false;
bool migrated = false; bool migrated = false;
const auto orderedDefaultShortcuts = KeyBindings::getOrderedShortcuts( defKeybindings );
if ( ini.findKey( group ) != IniFile::noID ) { if ( ini.findKey( group ) != IniFile::noID ) {
keybindings = ini.getKeyUnorderedMap( group ); keybindings = ini.getKeyUnorderedMap( group );
} else { } else {
for ( const auto& it : defKeybindings ) for ( const auto& shortcut : orderedDefaultShortcuts )
ini.setValue( group, bindings.getShortcutString( it.first ), it.second ); ini.setValue( group, bindings.getShortcutString( shortcut ),
defKeybindings.find( shortcut )->second );
added = true; added = true;
} }
for ( const auto& key : keybindings ) for ( const auto& key : keybindings )
@@ -93,9 +95,9 @@ void KeybindingsHelper::updateKeybindings(
auto foundCmd = invertedKeybindings.find( migrate.first ); auto foundCmd = invertedKeybindings.find( migrate.first );
if ( foundCmd != invertedKeybindings.end() && foundCmd->second == migrate.second ) { if ( foundCmd != invertedKeybindings.end() && foundCmd->second == migrate.second ) {
KeyBindings::Shortcut shortcut; KeyBindings::Shortcut shortcut;
for ( const auto& defKb : defKeybindings ) { for ( const auto& defaultShortcut : orderedDefaultShortcuts ) {
if ( defKb.second == foundCmd->first ) { if ( defKeybindings.find( defaultShortcut )->second == foundCmd->first ) {
shortcut = defKb.first; shortcut = defaultShortcut;
break; break;
} }
} }
@@ -121,15 +123,16 @@ void KeybindingsHelper::updateKeybindings(
bool keybindingsWereEmpty = keybindings.empty(); bool keybindingsWereEmpty = keybindings.empty();
if ( defKeybindings.size() != keybindings.size() || forceRebind ) { if ( defKeybindings.size() != keybindings.size() || forceRebind ) {
for ( auto& key : defKeybindings ) { for ( const auto& shortcut : orderedDefaultShortcuts ) {
auto foundCmd = invertedKeybindings.find( key.second ); const auto& command = defKeybindings.find( shortcut )->second;
auto shortcutStr = bindings.getShortcutString( key.first ); auto foundCmd = invertedKeybindings.find( command );
auto shortcutStr = bindings.getShortcutString( shortcut );
if ( ( foundCmd == invertedKeybindings.end() || keybindingsWereEmpty ) && if ( ( foundCmd == invertedKeybindings.end() || keybindingsWereEmpty ) &&
keybindings.find( shortcutStr ) == keybindings.end() ) { keybindings.find( shortcutStr ) == keybindings.end() ) {
keybindings[shortcutStr] = key.second; keybindings[shortcutStr] = command;
invertedKeybindings[key.second] = shortcutStr; invertedKeybindings[command] = shortcutStr;
ini.setValue( group, shortcutStr, key.second ); ini.setValue( group, shortcutStr, command );
added = true; added = true;
} }
} }
+1 -1
View File
@@ -23,7 +23,7 @@ class KeybindingsHelper {
IniFile& ini, const std::string& group, Input* input, IniFile& ini, const std::string& group, Input* input,
std::unordered_map<std::string, std::string>& keybindings, std::unordered_map<std::string, std::string>& keybindings,
std::unordered_map<std::string, std::string>& invertedKeybindings, std::unordered_map<std::string, std::string>& invertedKeybindings,
const std::map<KeyBindings::Shortcut, std::string>& defKeybindings, bool forceRebind, const KeyBindings::ShortcutMap& defKeybindings, bool forceRebind,
const std::map<std::string, std::string>& migrateKeyindings, IniFile& iniState ); const std::map<std::string, std::string>& migrateKeyindings, IniFile& iniState );
static void static void
@@ -124,7 +124,7 @@ UIWidget* UIBreakpointsTableView::createCell( UIWidget* rowWidget, const ModelIn
return UITableView::createCell( rowWidget, index ); return UITableView::createCell( rowWidget, index );
} }
const std::map<KeyBindings::Shortcut, std::string> const KeyBindings::ShortcutMap
StatusDebuggerController::getLocalDefaultKeybindings() { StatusDebuggerController::getLocalDefaultKeybindings() {
return { return {
{ { KEY_TAB, UICodeEditorSplitter::getDefaultSwitchToTabModifier() }, "next-tab" }, { { KEY_TAB, UICodeEditorSplitter::getDefaultSwitchToTabModifier() }, "next-tab" },
@@ -42,7 +42,7 @@ class StatusDebuggerController : public StatusBarElement, public UITabWidgetSpli
public: public:
enum class State { NotStarted, Running, Paused }; enum class State { NotStarted, Running, Paused };
static const std::map<KeyBindings::Shortcut, std::string> getLocalDefaultKeybindings(); static const KeyBindings::ShortcutMap getLocalDefaultKeybindings();
StatusDebuggerController( UISplitter* mainSplitter, UISceneNode* uiSceneNode, StatusDebuggerController( UISplitter* mainSplitter, UISceneNode* uiSceneNode,
PluginContextProvider* pluginContext ); PluginContextProvider* pluginContext );
+2
View File
@@ -877,6 +877,8 @@ Git::Result Git::diff( DiffMode mode, const std::string& projectDir ) {
modeTxt = "--staged"; modeTxt = "--staged";
break; break;
} }
case DiffChanged:
break;
} }
return gitSimple( String::format( "diff %s", modeTxt ), projectDir ); return gitSimple( String::format( "diff %s", modeTxt ), projectDir );
} }
+1 -1
View File
@@ -306,7 +306,7 @@ class Git {
bool isEmpty() const { return name.empty(); } bool isEmpty() const { return name.empty(); }
}; };
enum DiffMode { DiffHead, DiffStaged }; enum DiffMode { DiffHead, DiffStaged, DiffChanged };
Git( const std::string& projectDir = "", const std::string& gitPath = "" ); Git( const std::string& projectDir = "", const std::string& gitPath = "" );
+35 -7
View File
@@ -212,6 +212,8 @@ GitPlugin::~GitPlugin() {
mLifetime.invalidate(); mLifetime.invalidate();
waitUntilLoaded(); waitUntilLoaded();
mShuttingDown = true; mShuttingDown = true;
mCommitDetails.cancelDiffPreparation();
mDetachedHistory.details.cancelDiffPreparation();
mConflictViewCloseConnection.disconnect(); mConflictViewCloseConnection.disconnect();
mConflictView = nullptr; mConflictView = nullptr;
mConflictSessions.clear(); mConflictSessions.clear();
@@ -2152,10 +2154,13 @@ void GitPlugin::diff( const Git::DiffMode mode, const std::string& repoPath ) {
case Git::DiffStaged: case Git::DiffStaged:
modeName = "staged"; modeName = "staged";
break; break;
case Git::DiffChanged:
modeName = "changed";
break;
} }
plugin->getPluginContext()->loadDiffFromMemory( plugin->getPluginContext()->loadDiffFromMemory(
res.result, UIDiffView::isMultiFileDiff( res.result ) ? modeName : "", "", res.result, UIDiffView::isMultiFileDiff( res.result ) ? modeName : "", "", repoPath,
repoPath ); true );
} ); } );
} ); } );
} }
@@ -2992,6 +2997,8 @@ void GitPlugin::CommitDetailsState::openCommitDetails( GitPlugin& plugin, const
if ( commit.hash.empty() && !isWorkingTree ) if ( commit.hash.empty() && !isWorkingTree )
return; return;
const std::string selectedRepo = plugin.repoSelected(); const std::string selectedRepo = plugin.repoSelected();
cancelDiffPreparation();
diffPreparationCancelled = std::make_shared<std::atomic_bool>( false );
const Uint64 requestGeneration = ++generation; const Uint64 requestGeneration = ++generation;
this->commit = commit; this->commit = commit;
workingTree = isWorkingTree; workingTree = isWorkingTree;
@@ -3163,14 +3170,31 @@ void GitPlugin::CommitDetailsState::loadCommitFiles( GitPlugin& plugin, bool det
const std::string repo = this->repo; const std::string repo = this->repo;
const Git::Commit commit = this->commit; const Git::Commit commit = this->commit;
const bool workingTree = this->workingTree; const bool workingTree = this->workingTree;
const auto diffPreparationCancelled = this->diffPreparationCancelled;
auto git = plugin.mGit; auto git = plugin.mGit;
const auto lifetime = plugin.mLifetime.weakHandle(); const auto lifetime = plugin.mLifetime.weakHandle();
plugin.runAsyncTask( [git = std::move( git ), lifetime, generation, repo, commit, detached, plugin.runAsyncTask( [git = std::move( git ), lifetime, generation, repo, commit, detached,
workingTree] { workingTree, diffPreparationCancelled] {
if ( diffPreparationCancelled &&
diffPreparationCancelled->load( std::memory_order_relaxed ) )
return;
auto result = auto result =
workingTree ? git->workingTreeFiles( repo ) : git->commitFiles( commit, repo ); workingTree ? git->workingTreeFiles( repo ) : git->commitFiles( commit, repo );
if ( diffPreparationCancelled &&
diffPreparationCancelled->load( std::memory_order_relaxed ) )
return;
std::shared_ptr<UIDiffView::PreparedMultiFileDiff> preparedDiff;
if ( result.success() && !result.patch.empty() ) {
preparedDiff =
UIDiffView::prepareMultiFileDiff( result.patch, diffPreparationCancelled );
if ( diffPreparationCancelled &&
diffPreparationCancelled->load( std::memory_order_relaxed ) )
return;
std::string{}.swap( result.patch );
}
lifetime.run( [generation, repo, commit, detached, workingTree, lifetime.run( [generation, repo, commit, detached, workingTree,
result = std::move( result )]( GitPlugin* plugin ) mutable { result = std::move( result ),
preparedDiff = std::move( preparedDiff )]( GitPlugin* plugin ) mutable {
auto& details = detached ? plugin->mDetachedHistory.details : plugin->mCommitDetails; auto& details = detached ? plugin->mDetachedHistory.details : plugin->mCommitDetails;
if ( plugin->mShuttingDown || generation != details.generation || if ( plugin->mShuttingDown || generation != details.generation ||
repo != details.repo || repo != plugin->repoSelected() || repo != details.repo || repo != plugin->repoSelected() ||
@@ -3230,9 +3254,9 @@ void GitPlugin::CommitDetailsState::loadCommitFiles( GitPlugin& plugin, bool det
details.gitHub->setVisible( !details.url.empty() ); details.gitHub->setVisible( !details.url.empty() );
details.diffContainer->closeAllChildren(); details.diffContainer->closeAllChildren();
details.diff = nullptr; details.diff = nullptr;
if ( !result.patch.empty() ) { if ( preparedDiff ) {
details.diff = details.diff = UIDiffView::NewMultiFileDiffViewer( std::move( preparedDiff ), repo,
UIDiffView::NewMultiFileDiffViewer( result.patch, repo, details.viewMode ); details.viewMode );
details.diff->setLayoutSizePolicy( SizePolicy::MatchParent, details.diff->setLayoutSizePolicy( SizePolicy::MatchParent,
SizePolicy::MatchParent ); SizePolicy::MatchParent );
details.diff->setParent( details.diffContainer ); details.diff->setParent( details.diffContainer );
@@ -3750,6 +3774,8 @@ void GitPlugin::buildSidePanelTab() {
"diff-added" ); "diff-added" );
if ( type == Git::GitStatusType::Changed ) { if ( type == Git::GitStatusType::Changed ) {
menuAdd( menu, "git-diff-changed",
i18n( "git_diff_changed", "Diff Changed" ), "diff-multiple" );
menu->addSeparator(); menu->addSeparator();
menuAdd( menu, "git-discard-all", menuAdd( menu, "git-discard-all",
i18n( "git_discard_all", "Discard All" ) ); i18n( "git_discard_all", "Discard All" ) );
@@ -3775,6 +3801,8 @@ void GitPlugin::buildSidePanelTab() {
(Uint32)Git::GitStatusType::Changed ) ); (Uint32)Git::GitStatusType::Changed ) );
} else if ( id == "git-diff-staged" ) { } else if ( id == "git-diff-staged" ) {
diff( Git::DiffMode::DiffStaged, repoPath ); diff( Git::DiffMode::DiffStaged, repoPath );
} else if ( id == "git-diff-changed" ) {
diff( Git::DiffMode::DiffChanged, repoPath );
} }
} ); } );
@@ -188,6 +188,7 @@ class GitPlugin : public PluginBase {
Git::Commit commit; Git::Commit commit;
std::string repo; std::string repo;
std::atomic<Uint64> generation{ 0 }; std::atomic<Uint64> generation{ 0 };
std::shared_ptr<std::atomic_bool> diffPreparationCancelled;
EventConnection closeConnection; EventConnection closeConnection;
Tools::UIDiffView::ViewMode viewMode{ Tools::UIDiffView::ViewMode::Unified }; Tools::UIDiffView::ViewMode viewMode{ Tools::UIDiffView::ViewMode::Unified };
bool messageExpanded{ false }; bool messageExpanded{ false };
@@ -199,7 +200,14 @@ class GitPlugin : public PluginBase {
void loadCommitFiles( GitPlugin& plugin, bool detached ); void loadCommitFiles( GitPlugin& plugin, bool detached );
void cancelDiffPreparation() {
if ( diffPreparationCancelled )
diffPreparationCancelled->store( true, std::memory_order_relaxed );
diffPreparationCancelled.reset();
}
void reset() { void reset() {
cancelDiffPreparation();
++generation; ++generation;
view = nullptr; view = nullptr;
subject = nullptr; subject = nullptr;
@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <eepp/core/string.hpp> #include <eepp/core/string.hpp>
#include <eepp/graphics/drawable.hpp>
#include <eepp/ui/doc/textrange.hpp> #include <eepp/ui/doc/textrange.hpp>
#include <optional> #include <optional>
#include <string> #include <string>
@@ -170,7 +171,8 @@ class PluginContextProvider {
virtual void loadDiffFromMemory( const std::string& content, virtual void loadDiffFromMemory( const std::string& content,
const std::string& originalFilePath = "", const std::string& originalFilePath = "",
const std::string& oldFilePath = "", const std::string& oldFilePath = "",
const std::string& repoPath = "" ) = 0; const std::string& repoPath = "",
bool interactiveFileHeaders = false ) = 0;
virtual void loadFolder( std::string path, bool forceNewWindow = false ) = 0; virtual void loadFolder( std::string path, bool forceNewWindow = false ) = 0;
+1 -1
View File
@@ -131,7 +131,7 @@ void TerminalManager::loadTerminalColorSchemes() {
mTerminalCurrentColorScheme = mTerminalColorSchemes.begin()->first; mTerminalCurrentColorScheme = mTerminalColorSchemes.begin()->first;
} }
std::map<KeyBindings::Shortcut, std::string> TerminalManager::getTerminalKeybindings() { KeyBindings::ShortcutMap TerminalManager::getTerminalKeybindings() {
return { return {
{ { KEY_T, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "create-new-terminal" }, { { KEY_T, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "create-new-terminal" },
{ { KEY_E, { { KEY_E,
+1 -1
View File
@@ -34,7 +34,7 @@ class TerminalManager {
void loadTerminalColorSchemes(); void loadTerminalColorSchemes();
static std::map<KeyBindings::Shortcut, std::string> getTerminalKeybindings(); static KeyBindings::ShortcutMap getTerminalKeybindings();
const std::string& getTerminalColorSchemesPath() const; const std::string& getTerminalColorSchemesPath() const;
+1 -1
View File
@@ -10,7 +10,7 @@
namespace ecode { namespace ecode {
static const std::map<KeyBindings::Shortcut, std::string> getDefaultKeybindings() { static const KeyBindings::ShortcutMap getDefaultKeybindings() {
return { return {
{ { KEY_C, KeyMod::getDefaultModifier() }, "copy" }, { { KEY_C, KeyMod::getDefaultModifier() }, "copy" },
{ { KEY_X, KeyMod::getDefaultModifier() }, "cut" }, { { KEY_X, KeyMod::getDefaultModifier() }, "cut" },