mirror of
https://github.com/textmate/textmate.git
synced 2026-01-23 13:47:54 -05:00
Presently this can only be done by right-clicking the tab and selecting the “Sticky” option — if a tab is sticky then it will not be closed when executing any of the batch close actions (Close All Tabs, Close Other Tabs, Close Tabs to the Right, and holding option down while opening a file via file browser or file chooser). Closes #1038.
378 lines
15 KiB
C++
378 lines
15 KiB
C++
#ifndef DOCUMENT_H_MIJOONQT
|
|
#define DOCUMENT_H_MIJOONQT
|
|
|
|
#include <buffer/buffer.h>
|
|
#include <undo/undo.h>
|
|
#include <plist/uuid.h>
|
|
#include <plist/date.h>
|
|
#include <settings/settings.h>
|
|
#include <text/types.h>
|
|
#include <authorization/authorization.h>
|
|
#include <file/bytes.h>
|
|
#include <file/open.h>
|
|
#include <file/save.h>
|
|
#include <file/encoding.h>
|
|
#include <scope/scope.h>
|
|
#include <regexp/glob.h>
|
|
#include <oak/debug.h>
|
|
|
|
namespace document
|
|
{
|
|
struct watch_t;
|
|
struct document_t;
|
|
typedef std::shared_ptr<watch_t> watch_ptr;
|
|
typedef std::shared_ptr<document_t> document_ptr;
|
|
typedef std::shared_ptr<document_t const> document_const_ptr;
|
|
typedef std::weak_ptr<document_t> document_weak_ptr;
|
|
|
|
struct PUBLIC open_callback_t : file::open_callback_t
|
|
{
|
|
virtual ~open_callback_t () { }
|
|
virtual void show_content (std::string const& path, io::bytes_ptr content, std::map<std::string, std::string> const& attributes, std::string const& fileType, encoding::type const& encoding, std::vector<oak::uuid_t> const& binaryImportFilters, std::vector<oak::uuid_t> const& textImportFilters) { }
|
|
virtual void show_document (std::string const& path, document_ptr document) = 0;
|
|
virtual void show_error (std::string const& path, document_ptr document, std::string const& message, oak::uuid_t const& filter) = 0;
|
|
virtual void show_error (std::string const& path, std::string const& message, oak::uuid_t const& filter) { }
|
|
};
|
|
|
|
typedef std::shared_ptr<open_callback_t> open_callback_ptr;
|
|
|
|
struct PUBLIC save_callback_t : file::save_callback_t
|
|
{
|
|
virtual ~save_callback_t () { }
|
|
virtual void did_save_document (document_ptr document, std::string const& path, bool success, std::string const& message, oak::uuid_t const& filter) = 0;
|
|
virtual void did_save (std::string const& path, io::bytes_ptr content, encoding::type const& encoding, bool success, std::string const& message, oak::uuid_t const& filter) { }
|
|
};
|
|
|
|
typedef std::shared_ptr<save_callback_t> save_callback_ptr;
|
|
|
|
struct PUBLIC document_t : std::enable_shared_from_this<document_t>
|
|
{
|
|
WATCH_LEAKS(document_t);
|
|
|
|
document_t () : _did_load_marks(false), _selection(NULL_STR), _folded(NULL_STR), _visible_rect(NULL_STR), _disable_callbacks(false), _revision(0), _disk_revision(0), _modified(false), _path(NULL_STR), _open_count(0), _has_lru(false), _is_on_disk(false), _recent_tracking(true), _backup_path(NULL_STR), _backup_revision(0), _virtual_path(NULL_STR), _custom_name(NULL_STR), _untitled_count(0), _file_type(NULL_STR), /*_folder(NULL_STR),*/ _disk_encoding(NULL_STR), _disk_newlines(NULL_STR), _disk_bom(false) { }
|
|
~document_t ();
|
|
|
|
bool operator== (document_t const& rhs) const { return _identifier == rhs._identifier; }
|
|
bool operator!= (document_t const& rhs) const { return _identifier != rhs._identifier; }
|
|
|
|
// ============================================================
|
|
// = Doing one-pass reading of file (find in arbitrary files) =
|
|
// ============================================================
|
|
|
|
struct reader_t { virtual io::bytes_ptr next () = 0; virtual ~reader_t () { } };
|
|
typedef std::shared_ptr<reader_t> reader_ptr;
|
|
reader_ptr create_reader () const;
|
|
|
|
// ======================================================
|
|
// = Performing replacements (from outside a text view) =
|
|
// ======================================================
|
|
|
|
void replace (std::multimap<text::range_t, std::string> const& replacements);
|
|
|
|
// ===================================================================
|
|
// = Controlling marks (bookmarks, warnings, errors, search matches) =
|
|
// ===================================================================
|
|
|
|
struct mark_t
|
|
{
|
|
WATCH_LEAKS(document_t::mark_t);
|
|
|
|
mark_t (char const* type = "bookmark") : type(type), info("") { }
|
|
mark_t (std::string const& type, std::string const& info = "") : type(type), info(info) { }
|
|
bool operator== (mark_t const& rhs) const { return type == rhs.type && info == rhs.info; }
|
|
bool operator!= (mark_t const& rhs) const { return type != rhs.type || info != rhs.info; }
|
|
std::string type, info;
|
|
};
|
|
|
|
void add_mark (text::range_t const& range, mark_t const& mark = mark_t());
|
|
void remove_all_marks (std::string const& typeToClear = NULL_STR);
|
|
std::multimap<text::range_t, mark_t> marks () const;
|
|
|
|
private:
|
|
void load_marks (std::string const& src) const;
|
|
void setup_marks (std::string const& src, ng::buffer_t& buf) const;
|
|
std::string marks_as_string () const;
|
|
mutable std::multimap<text::range_t, mark_t> _marks;
|
|
mutable bool _did_load_marks;
|
|
|
|
std::string _selection;
|
|
std::string _folded;
|
|
std::string _visible_rect;
|
|
io::bytes_ptr _content;
|
|
|
|
// ===============
|
|
// = Symbol list =
|
|
// ===============
|
|
public:
|
|
std::map<text::pos_t, std::string> symbols ();
|
|
|
|
// ===================
|
|
// = Callback system =
|
|
// ===================
|
|
|
|
struct callback_t
|
|
{
|
|
WATCH_LEAKS(document_t::callback_t);
|
|
|
|
enum event_t
|
|
{
|
|
did_save,
|
|
|
|
did_change_open_status,
|
|
did_change_modified_status,
|
|
did_change_on_disk_status,
|
|
did_change_path,
|
|
did_change_file_type,
|
|
did_change_indent_settings,
|
|
// did_change_display_name,
|
|
did_change_marks,
|
|
// did_change_symbols,
|
|
};
|
|
|
|
virtual ~callback_t () { }
|
|
virtual void handle_document_event (document_ptr document, event_t event) = 0;
|
|
};
|
|
|
|
void add_callback (callback_t* callback) { _callbacks.add(callback); }
|
|
void remove_callback (callback_t* callback) { _callbacks.remove(callback); }
|
|
|
|
private:
|
|
void check_modified (ssize_t diskRev, ssize_t rev);
|
|
|
|
void broadcast (callback_t::event_t event, bool cascade = true)
|
|
{
|
|
if(_disable_callbacks)
|
|
return;
|
|
|
|
_disable_callbacks = !cascade;
|
|
_callbacks(&callback_t::handle_document_event, shared_from_this(), event);
|
|
_disable_callbacks = false;
|
|
}
|
|
|
|
oak::callbacks_t<callback_t> _callbacks;
|
|
bool _disable_callbacks;
|
|
|
|
// ===================
|
|
// = For OakTextView =
|
|
// ===================
|
|
|
|
void post_load (std::string const& path, io::bytes_ptr content, std::map<std::string, std::string> const& attributes, std::string const& fileType, encoding::type const& encoding);
|
|
|
|
struct open_callback_wrapper_t : file::open_callback_t
|
|
{
|
|
open_callback_wrapper_t (document::document_ptr doc, document::open_callback_ptr callback) : _document(doc), _callbacks(1, callback) { }
|
|
|
|
void select_charset (std::string const& path, io::bytes_ptr content, file::open_context_ptr context) { _callbacks[0]->select_charset(path, content, context); }
|
|
void select_line_feeds (std::string const& path, io::bytes_ptr content, file::open_context_ptr context) { _callbacks[0]->select_line_feeds(path, content, context); }
|
|
void select_file_type (std::string const& path, io::bytes_ptr content, file::open_context_ptr context) { if(_document->file_type() == NULL_STR) _callbacks[0]->select_file_type(path, content, context); else context->set_file_type(_document->file_type()); }
|
|
void add_callback (document::open_callback_ptr callback) { _callbacks.push_back(callback); }
|
|
|
|
void show_content (std::string const& path, io::bytes_ptr content, std::map<std::string, std::string> const& attributes, std::string const& fileType, encoding::type const& encoding, std::vector<oak::uuid_t> const& binaryImportFilters, std::vector<oak::uuid_t> const& textImportFilters)
|
|
{
|
|
// we are deleted in post_load() so make a copy of relevant data
|
|
std::vector<document::open_callback_ptr> callbacks(_callbacks);
|
|
document::document_ptr doc = _document;
|
|
|
|
_document->post_load(path, content, attributes, fileType, encoding);
|
|
iterate(cb, callbacks)
|
|
(*cb)->show_document(path, doc);
|
|
}
|
|
|
|
void show_error (std::string const& path, std::string const& message, oak::uuid_t const& filter)
|
|
{
|
|
// we are deleted in post_load() so make a copy of relevant data
|
|
std::vector<document::open_callback_ptr> callbacks(_callbacks);
|
|
document::document_ptr doc = _document;
|
|
|
|
_document->post_load(path, io::bytes_ptr(), std::map<std::string, std::string>(), NULL_STR, encoding::type());
|
|
iterate(cb, callbacks)
|
|
(*cb)->show_error(path, doc, message, filter);
|
|
}
|
|
|
|
private:
|
|
document::document_ptr _document;
|
|
std::vector<document::open_callback_ptr> _callbacks;
|
|
};
|
|
|
|
typedef std::shared_ptr<open_callback_wrapper_t> open_callback_wrapper_ptr;
|
|
open_callback_wrapper_ptr _open_callback;
|
|
|
|
void post_save (std::string const& path, io::bytes_ptr content, encoding::type const& encoding, bool succes);
|
|
|
|
public:
|
|
bool try_open (document::open_callback_ptr callback);
|
|
void open ();
|
|
void close ();
|
|
|
|
void show ();
|
|
void hide ();
|
|
oak::date_t const& lru () const;
|
|
|
|
void try_save (document::save_callback_ptr callback);
|
|
bool save ();
|
|
bool backup ();
|
|
void detach_backup () { _backup_path = NULL_STR; }
|
|
|
|
void set_path (std::string const& newPath);
|
|
void set_virtual_path (std::string const& virtualPath) { _virtual_path = virtualPath; }
|
|
void set_custom_name (std::string const& newCustomName) { _custom_name = newCustomName; }
|
|
void set_file_type (std::string const& newFileType);
|
|
|
|
std::string path () const { return _path; }
|
|
std::string virtual_path () const { return _virtual_path == NULL_STR ? _path : _virtual_path; }
|
|
std::string custom_name () const { return _custom_name; }
|
|
std::string backup_path () const;
|
|
std::string display_name () const;
|
|
|
|
void set_disk_encoding (encoding::type const& encoding) { _disk_newlines = encoding.newlines(); _disk_encoding = encoding.charset(); _disk_bom = encoding.byte_order_mark(); }
|
|
encoding::type disk_encoding () const { return encoding::type(_disk_newlines, _disk_encoding, _disk_bom); }
|
|
|
|
encoding::type encoding_for_save_as_path (std::string const& path);
|
|
|
|
bool recent_tracking () const { return _recent_tracking && _path != NULL_STR; }
|
|
void set_recent_tracking (bool flag) { _recent_tracking = flag; }
|
|
|
|
bool sticky () const { return _sticky; }
|
|
void set_sticky (bool flag) { _sticky = flag; }
|
|
|
|
ng::buffer_t& buffer () { ASSERT(_buffer); return *_buffer; }
|
|
ng::buffer_t const& buffer () const { ASSERT(_buffer); return *_buffer; }
|
|
|
|
ng::undo_manager_t& undo_manager () { ASSERT(_undo_manager); return *_undo_manager; }
|
|
ng::undo_manager_t const& undo_manager () const { ASSERT(_undo_manager); return *_undo_manager; }
|
|
|
|
std::string content () const;
|
|
void set_content (std::string const& str);
|
|
|
|
// =============
|
|
// = Accessors =
|
|
// =============
|
|
|
|
oak::uuid_t identifier () const { return _identifier; }
|
|
ssize_t revision () const { return _revision; }
|
|
void set_revision (ssize_t rev) { check_modified(_disk_revision, rev); }
|
|
bool is_open () const { return _open_count != 0 && !_open_callback; }
|
|
|
|
std::string file_type () const;
|
|
|
|
std::map<std::string, std::string> document_variables () const;
|
|
|
|
bool is_modified () const;
|
|
bool is_on_disk () const { return is_open() ? _is_on_disk : path::exists(path()); }
|
|
void set_disk_revision (ssize_t rev) { check_modified(rev, _revision); }
|
|
std::string const& selection () const { return _selection; }
|
|
std::string const& folded () const { return _folded; }
|
|
std::string visible_rect () const { return _visible_rect; }
|
|
|
|
void set_selection (std::string const& sel) { _selection = sel; _visible_rect = NULL_STR; }
|
|
void set_folded (std::string const& folded) { _folded = folded; }
|
|
void set_visible_rect (std::string const& rect) { _visible_rect = rect; }
|
|
|
|
void set_authorization (osx::authorization_t const& auth) { _authorization = auth; }
|
|
|
|
private:
|
|
void setup_buffer ();
|
|
void grammar_did_change ();
|
|
|
|
void set_modified (bool flag);
|
|
|
|
// ==============
|
|
// = Properties =
|
|
// ==============
|
|
|
|
friend document_ptr create (std::string const& path);
|
|
friend document_ptr from_content (std::string const& content, std::string const& fileType);
|
|
friend document_ptr find (oak::uuid_t const& uuid, bool searchBackups);
|
|
|
|
oak::uuid_t _identifier; // to identify this document when there is no path
|
|
path::identifier_t _key;
|
|
ssize_t _revision;
|
|
ssize_t _disk_revision;
|
|
bool _modified;
|
|
|
|
std::string _path; // does not imply there actually is a file
|
|
size_t _open_count; // document open in some window/tab
|
|
mutable oak::date_t _lru; // last time document was shown
|
|
mutable bool _has_lru;
|
|
bool _is_on_disk;
|
|
bool _recent_tracking;
|
|
bool _sticky = false;
|
|
|
|
mutable std::string _backup_path; // if there is a backup, this is set — we can have a backup even when there is no path
|
|
mutable ssize_t _backup_revision;
|
|
|
|
std::string _virtual_path;
|
|
std::string _custom_name;
|
|
mutable size_t _untitled_count; // this is ≠ 0 if the document is untitled
|
|
|
|
mutable std::string _file_type; // this may also be in the settings
|
|
// oak::uuid_t _grammar_uuid;
|
|
|
|
std::shared_ptr<ng::buffer_t> _buffer;
|
|
std::string _pristine_buffer = NULL_STR;
|
|
std::shared_ptr<ng::undo_manager_t> _undo_manager;
|
|
void mark_pristine ();
|
|
|
|
// std::string _folder; // when there is no path, this value is where the document will likely end up, i.e, used for retrieving settings and default save location
|
|
osx::authorization_t _authorization; // when opened via sudo
|
|
|
|
friend struct document_tracker_t;
|
|
size_t untitled_count () const { return _untitled_count; }
|
|
|
|
std::string _disk_encoding;
|
|
std::string _disk_newlines;
|
|
bool _disk_bom;
|
|
|
|
protected: // so that we can trigger the callback in unit tests
|
|
watch_ptr _file_watcher;
|
|
friend struct watch_t;
|
|
void watch_callback (int flags, std::string const& newPath, bool async = true);
|
|
};
|
|
|
|
PUBLIC document_ptr create (std::string const& path = NULL_STR);
|
|
PUBLIC document_ptr find (oak::uuid_t const& uuid, bool searchBackups = false);
|
|
PUBLIC document_ptr from_content (std::string const& content, std::string const& fileType = NULL_STR);
|
|
|
|
// ====================
|
|
// = Document scanner =
|
|
// ====================
|
|
|
|
struct PUBLIC scanner_t
|
|
{
|
|
WATCH_LEAKS(scanner_t);
|
|
|
|
scanner_t (std::string const& path, path::glob_list_t const& glob, bool follow_links = false, bool depth_first = false, bool includeUntitled = true);
|
|
~scanner_t ();
|
|
|
|
bool is_running () const { return is_running_flag; }
|
|
void stop () { should_stop_flag = true; }
|
|
void wait () const { pthread_join(thread, NULL); }
|
|
|
|
static std::vector<document_ptr> open_documents ();
|
|
|
|
std::vector<document_ptr> accept_documents ();
|
|
std::string get_current_path () const;
|
|
|
|
private:
|
|
std::string path;
|
|
path::glob_list_t glob;
|
|
bool follow_links, depth_first;
|
|
|
|
pthread_t thread;
|
|
mutable pthread_mutex_t mutex;
|
|
volatile bool is_running_flag, should_stop_flag;
|
|
|
|
void thread_main ();
|
|
void scan_dir (std::string const& dir);
|
|
|
|
std::string current_path;
|
|
std::vector<document_ptr> documents;
|
|
std::set< std::pair<dev_t, ino_t> > seen_paths;
|
|
};
|
|
|
|
typedef std::shared_ptr<scanner_t> scanner_ptr;
|
|
|
|
} /* document */
|
|
|
|
#endif /* end of include guard: DOCUMENT_H_MIJOONQT */
|