mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-02 11:10:31 +03:00
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:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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" },
|
||||||
|
|||||||
@@ -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
@@ -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() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() );
|
||||||
|
|||||||
@@ -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 ) ==
|
||||||
|
©.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 ) !=
|
||||||
|
©.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" },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Vendored
+1
-1
Submodule src/thirdparty/efsw updated: 5df6a032e2...41ddf6822f
+25
-21
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
|||||||
@@ -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 );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = "" );
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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" },
|
||||||
|
|||||||
Reference in New Issue
Block a user