def test_SyntaxCache_syntaxdef(): m = Mocker() syn = SyntaxCache() syn.cache.append("something") sd = m.mock(SyntaxDefinition) with m: assert syn.syntaxdef is not sd syn.syntaxdef = sd eq_(syn.cache, []) eq_(syn.syntaxdef, sd) syn.cache.append("something") eq_(syn.cache, ["something"]) eq_(syn.syntaxdef, sd)
def test_SyntaxCache_syntaxdef(): m = Mocker() syn = SyntaxCache() syn.cache.append("something") sd = m.mock(SyntaxDefinition) with m: assert syn.syntaxdef is not sd syn.syntaxdef = sd eq_(syn.cache, []) eq_(syn.syntaxdef, sd) syn.cache.append("something") eq_(syn.cache, ["something"]) eq_(syn.syntaxdef, sd)
def init(self): super(TextDocument, self).init() self.setUndoManager_(UndoManager.alloc().init()) self.id = next(doc_id_gen) self.icon_cache = (None, None) self.document_attrs = { ak.NSDocumentTypeDocumentAttribute: ak.NSPlainTextDocumentType, ak.NSCharacterEncodingDocumentAttribute: fn.NSUTF8StringEncoding, } self.text_storage = ak.NSTextStorage.alloc( ).initWithString_attributes_("", {}) self.syntaxer = SyntaxCache() self._filestat = None self.props = KVOProxy(self) self.indent_mode = app.config["indent.mode"] self.indent_size = app.config[ "indent.size"] # should come from syntax definition self.newline_mode = app.config["newline_mode"] self.highlight_selected_text = app.config[ "highlight_selected_text.enabled"] self.reset_text_attributes(self.indent_size) #self.save_hooks = [] return self
def init(self): super(TextDocument, self).init() self.setUndoManager_(UndoManager.alloc().init()) self.id = doc_id_gen.next() self.icon_cache = (None, None) self.document_attrs = { NSDocumentTypeDocumentAttribute: NSPlainTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding, } self.text_storage = NSTextStorage.alloc().initWithString_attributes_(u"", {}) self.syntaxer = SyntaxCache() self._filestat = None self.props = KVOProxy(self) self.indent_mode = const.INDENT_MODE_SPACE self.indent_size = 4 # should come from syntax definition self.newline_mode = const.NEWLINE_MODE_UNIX self.reset_text_attributes(self.indent_size) # self.save_hooks = [] return self
def init(self): super(TextDocument, self).init() self.setUndoManager_(UndoManager.alloc().init()) self.id = next(doc_id_gen) self.icon_cache = (None, None) self.document_attrs = { ak.NSDocumentTypeDocumentAttribute: ak.NSPlainTextDocumentType, ak.NSCharacterEncodingDocumentAttribute: fn.NSUTF8StringEncoding, } self.text_storage = ak.NSTextStorage.alloc().initWithString_attributes_("", {}) self.syntaxer = SyntaxCache() self._filestat = None self.props = KVOProxy(self) self.indent_mode = app.config["indent.mode"] self.indent_size = app.config["indent.size"] # should come from syntax definition self.newline_mode = app.config["newline_mode"] self.highlight_selected_text = app.config["highlight_selected_text.enabled"] self.reset_text_attributes(self.indent_size) #self.save_hooks = [] return self
class TextDocument(ak.NSDocument): @classmethod def get_with_path(cls, path): """Get a document with the given path Documents returned by this method have been added to the document controllers list of documents. """ url = fn.NSURL.fileURLWithPath_(path) dc = ak.NSDocumentController.sharedDocumentController() doc = dc.documentForURL_(url) if doc is None: if os.path.exists(path): doctype, err = dc.typeForContentsOfURL_error_(url, None) doc, err = dc.makeDocumentWithContentsOfURL_ofType_error_( url, doctype, None) if err is not None: raise Error(err.localizedFailureReason()) if doc is None: raise Error("could not open document: %s" % path) dc.addDocument_(doc) else: doc, err = dc.makeUntitledDocumentOfType_error_( const.TEXT_DOCUMENT, None) doc.setFileURL_(url) doc.update_syntaxer() return doc def init(self): super(TextDocument, self).init() self.setUndoManager_(UndoManager.alloc().init()) self.id = next(doc_id_gen) self.icon_cache = (None, None) self.document_attrs = { ak.NSDocumentTypeDocumentAttribute: ak.NSPlainTextDocumentType, ak.NSCharacterEncodingDocumentAttribute: fn.NSUTF8StringEncoding, } self.text_storage = ak.NSTextStorage.alloc( ).initWithString_attributes_("", {}) self.syntaxer = SyntaxCache() self._filestat = None self.props = KVOProxy(self) self.indent_mode = app.config["indent.mode"] self.indent_size = app.config[ "indent.size"] # should come from syntax definition self.newline_mode = app.config["newline_mode"] self.highlight_selected_text = app.config[ "highlight_selected_text.enabled"] self.reset_text_attributes(self.indent_size) #self.save_hooks = [] return self def properties(self): return self.props def setProperties_(self, value): pass @property def text(self): return self.text_storage.mutableString() @text.setter def text(self, value): self.text_storage.mutableString().setString_(value) self.reset_text_attributes(self.indent_size) @property def newline_mode(self): return self._newline_mode @newline_mode.setter def newline_mode(self, value): self._newline_mode = value self.eol = const.EOLS[value] @property def character_encoding(self): return self.document_attrs.get(ak.NSCharacterEncodingDocumentAttribute) @character_encoding.setter def character_encoding(self, value): # TODO when value is None encoding should be removed from document_attrs if value is not None: self.document_attrs[ ak.NSCharacterEncodingDocumentAttribute] = value else: self.document_attrs.pop(ak.NSCharacterEncodingDocumentAttribute, None) def reset_text_attributes(self, indent_size): font = ak.NSFont.fontWithName_size_("Monaco", 10.0) spcw = font.screenFontWithRenderingMode_(ak.NSFontDefaultRenderingMode) \ .advancementForGlyph_(ord(" ")).width ps = ak.NSParagraphStyle.defaultParagraphStyle().mutableCopy() ps.setTabStops_([]) ps.setDefaultTabInterval_(spcw * indent_size) ps = ps.copy() self._text_attributes = attrs = { ak.NSFontAttributeName: font, ak.NSParagraphStyleAttributeName: ps, } range = fn.NSMakeRange(0, self.text_storage.length()) self.text_storage.addAttributes_range_(attrs, range) for view in app.iter_views_of_document(self): if view.text_view is not None: view.text_view.setTypingAttributes_(attrs) view.text_view.setDefaultParagraphStyle_(ps) def default_text_attributes(self): return self._text_attributes def makeWindowControllers(self): editor = app.current_editor() if editor is None: editor = app.create_editor() view = TextDocumentView.create_with_document(self) editor.add_document_view(view) self.addWindowController_(editor.wc) editor.current_view = view def readFromData_ofType_error_(self, data, doctype, error): success, err = self.read_data_into_textstorage(data, self.text_storage) if success: self.analyze_content() return (success, err) def read_data_into_textstorage(self, data, text_storage): options = { ak.NSDefaultAttributesDocumentOption: self.default_text_attributes() } options.update(self.document_attrs) while True: success, attrs, err = text_storage \ .readFromData_options_documentAttributes_error_( data, options, None, None) if success or ak.NSCharacterEncodingDocumentAttribute not in options: if success: self.document_attrs = attrs break if err: log.error(err) options.pop(ak.NSCharacterEncodingDocumentAttribute, None) return success, err def dataOfType_error_(self, doctype, error): range = fn.NSMakeRange(0, self.text_storage.length()) attrs = self.document_attrs data, err = self.text_storage \ .dataFromRange_documentAttributes_error_(range, attrs, None) if err is None: try: self.update_syntaxer() app.save_editor_states() except Exception: log.error("unexpected error", exc_info=True) # if self.project is not None: # self.project.save() # self.updateSyntaxer() # if self.text_view is not None: # # make the undo manager recognize edits after save # self.text_view.breakUndoCoalescing() return (data, err) def setFileModificationDate_(self, date): super(TextDocument, self).setFileModificationDate_(date) self._filestat = None def analyze_content(self): text = self.text_storage.string() start, end, cend = text.getLineStart_end_contentsEnd_forRange_( None, None, None, (0, 0)) if end != cend: eol = EOLREF.get(text[cend:end], const.NEWLINE_MODE_UNIX) self.newline_mode = eol mode, size = calculate_indent_mode_and_size(text) if size is not None: self.indent_size = size if mode is not None: self.indent_mode = mode def is_externally_modified(self): """check if this document has been modified by another program""" url = self.fileURL() if url is not None and os.path.exists(url.path()): ok, mdate, err = url.getResourceValue_forKey_error_( None, fn.NSURLContentModificationDateKey, None) if ok: return self.fileModificationDate() != mdate return None def check_for_external_changes(self, window): if not self.is_externally_modified(): return if self.isDocumentEdited(): if window is None: return # ignore change (no gui for alert) stat = filestat(self.fileURL().path()) if self._filestat == stat: return self._filestat = stat def callback(code): if code == ak.NSAlertFirstButtonReturn: self.reload_document() alert = Alert.alloc().init() alert.setMessageText_("ā%sā source document changed" % self.displayName()) alert.setInformativeText_("Discard changes and reload?") alert.addButtonWithTitle_("Reload") alert.addButtonWithTitle_("Cancel") alert.beginSheetModalForWindow_withCallback_(window, callback) else: self.reload_document() @refactor( "improve undo after reload - use difflib to replace changed text only") def reload_document(self): """Reload document with the given URL This implementation allows the user to undo beyond the reload. The down-side is that it may use a lot of memory if the document is very large. """ url = self.fileURL() if url is None or not os.path.exists(url.path()): return undo = self.undoManager() undo.should_remove = False textstore = self.text_storage self.text_storage = ak.NSTextStorage.alloc().init() try: ok, err = self.revertToContentsOfURL_ofType_error_( url, self.fileType(), None) finally: tempstore = self.text_storage self.text_storage = textstore undo.should_remove = True if not ok: log.error("could not reload document: %s", err) return # TODO report err textview = None for view in app.iter_views_of_document(self): textview = view.text_view if textview is not None: break text = tempstore.string() range = fn.NSRange(0, textstore.length()) if textview is None: textstore.replaceCharactersInRange_withString_(range, text) undo.removeAllActions() elif textview.shouldChangeTextInRange_replacementString_(range, text): #state = self.documentState textstore.replaceCharactersInRange_withString_(range, text) #self.documentState = state textview.didChangeText() textview.breakUndoCoalescing() # HACK use timed invocation to allow didChangeText notification # to update change count before _clearUndo is invoked self.performSelector_withObject_afterDelay_( "_clearChanges", self, 0) textview.setSelectedRange_(fn.NSRange(0, 0)) self.update_syntaxer() @untested def prepareSavePanel_(self, panel): try: panel.setCanSelectHiddenExtension_(True) panel.setExtensionHidden_(False) panel.setAllowsOtherFileTypes_(True) name = panel.nameFieldStringValue() # 10.6 API url = self.fileURL() if url is not None: filename = url.lastPathComponent() directory = url.URLByDeletingLastPathComponent() panel.setDirectoryURL_(directory) # 10.6 API else: filename = name name += ".txt" if name != filename or (name.endswith(".txt") and "." in name[:-4]): panel.setNameFieldStringValue_(filename) exts = ["txt"] if "." in filename: ext = filename.rsplit(".", 1)[1] if ext not in exts: exts.insert(0, ext) panel.setAllowedFileTypes_(exts) except Exception: log.error("cannot prepare save panel...", exc_info=True) return True def _clearChanges(self): self.updateChangeCount_(ak.NSChangeCleared) def icon(self): url = self.fileURL() key = "" if url is None else url.path() old_key, data = self.icon_cache if old_key is None or old_key != key: data = fetch_icon(key) self.icon_cache = (key, data) return data @property def comment_token(self): return self.syntaxer.syntaxdef.comment_token def _get_syntaxdef(self): return self.syntaxer.syntaxdef def _set_syntaxdef(self, value): self.syntaxer.syntaxdef = value self.syntaxer.color_text(self.text_storage) syntaxdef = property(_get_syntaxdef, _set_syntaxdef) def update_syntaxer(self): if self.text_storage.delegate() is not self: self.text_storage.setDelegate_(self) filename = self.lastComponentOfFileName() if filename != self.syntaxer.filename: self.syntaxer.filename = filename syntaxdef = app.syntax_factory.get_definition(filename) if self.syntaxdef is not syntaxdef: self.props.syntaxdef = syntaxdef self.syntaxer.color_text(self.text_storage) def textStorageDidProcessEditing_(self, notification): range = self.text_storage.editedRange() self.syntaxer.color_text(self.text_storage, range) def updateChangeCount_(self, ctype): super(TextDocument, self).updateChangeCount_(ctype) app.item_changed(self, ctype) # def set_primary_window_controller(self, wc): # if wc.document() is self: # if self.scroll_view not in wc.mainView.subviews(): # wc.setDocument_(self) # else: # self.addWindowController_(wc) # if wc.controller.find_project_with_document(self) is None: # wc.add_document(self) def __repr__(self): return "<%s 0x%x %s>" % (type(self).__name__, id(self), self.displayName()) def close(self): # remove window controllers here so NSDocument does not close the windows for wc in list(self.windowControllers()): self.removeWindowController_(wc) ts = self.text_storage if ts is not None and ts.delegate() is self: ts.setDelegate_(None) self.text_storage = None super(TextDocument, self).close()
def test_SyntaxCache_syntaxdef_default(): syn = SyntaxCache() eq_(syn.syntaxdef, PLAIN_TEXT) # check default eq_(syn.filename, None) # check default
class TextDocument(ak.NSDocument): @classmethod def get_with_path(cls, path): """Get a document with the given path Documents returned by this method have been added to the document controllers list of documents. """ url = fn.NSURL.fileURLWithPath_(path) dc = ak.NSDocumentController.sharedDocumentController() doc = dc.documentForURL_(url) if doc is None: if os.path.exists(path): doctype, err = dc.typeForContentsOfURL_error_(url, None) doc, err = dc.makeDocumentWithContentsOfURL_ofType_error_( url, doctype, None) if err is not None: raise Error(err.localizedFailureReason()) if doc is None: raise Error("could not open document: %s" % path) dc.addDocument_(doc) else: doc, err = dc.makeUntitledDocumentOfType_error_( const.TEXT_DOCUMENT, None) doc.setFileURL_(url) doc.update_syntaxer() return doc def init(self): super(TextDocument, self).init() self.setUndoManager_(UndoManager.alloc().init()) self.id = next(doc_id_gen) self.icon_cache = (None, None) self.document_attrs = { ak.NSDocumentTypeDocumentAttribute: ak.NSPlainTextDocumentType, ak.NSCharacterEncodingDocumentAttribute: fn.NSUTF8StringEncoding, } self.text_storage = ak.NSTextStorage.alloc().initWithString_attributes_("", {}) self.syntaxer = SyntaxCache() self._filestat = None self.props = KVOProxy(self) self.indent_mode = app.config["indent.mode"] self.indent_size = app.config["indent.size"] # should come from syntax definition self.newline_mode = app.config["newline_mode"] self.highlight_selected_text = app.config["highlight_selected_text.enabled"] self.reset_text_attributes(self.indent_size) #self.save_hooks = [] return self def properties(self): return self.props def setProperties_(self, value): pass @property def text(self): return self.text_storage.mutableString() @text.setter def text(self, value): self.text_storage.mutableString().setString_(value) self.reset_text_attributes(self.indent_size) @property def newline_mode(self): return self._newline_mode @newline_mode.setter def newline_mode(self, value): self._newline_mode = value self.eol = const.EOLS[value] @property def character_encoding(self): return self.document_attrs.get(ak.NSCharacterEncodingDocumentAttribute) @character_encoding.setter def character_encoding(self, value): # TODO when value is None encoding should be removed from document_attrs if value is not None: self.document_attrs[ak.NSCharacterEncodingDocumentAttribute] = value else: self.document_attrs.pop(ak.NSCharacterEncodingDocumentAttribute, None) def reset_text_attributes(self, indent_size): font = ak.NSFont.fontWithName_size_("Monaco", 10.0) spcw = font.screenFontWithRenderingMode_(ak.NSFontDefaultRenderingMode) \ .advancementForGlyph_(ord(" ")).width ps = ak.NSParagraphStyle.defaultParagraphStyle().mutableCopy() ps.setTabStops_([]) ps.setDefaultTabInterval_(spcw * indent_size) ps = ps.copy() self._text_attributes = attrs = { ak.NSFontAttributeName: font, ak.NSParagraphStyleAttributeName: ps, } range = fn.NSMakeRange(0, self.text_storage.length()) self.text_storage.addAttributes_range_(attrs, range) for view in app.iter_views_of_document(self): if view.text_view is not None: view.text_view.setTypingAttributes_(attrs) view.text_view.setDefaultParagraphStyle_(ps) def default_text_attributes(self): return self._text_attributes def makeWindowControllers(self): editor = app.current_editor() if editor is None: editor = app.create_editor() view = TextDocumentView.create_with_document(self) editor.add_document_view(view) self.addWindowController_(editor.wc) editor.current_view = view def readFromData_ofType_error_(self, data, doctype, error): success, err = self.read_data_into_textstorage(data, self.text_storage) if success: self.analyze_content() return (success, err) def read_data_into_textstorage(self, data, text_storage): options = {ak.NSDefaultAttributesDocumentOption: self.default_text_attributes()} options.update(self.document_attrs) while True: success, attrs, err = text_storage \ .readFromData_options_documentAttributes_error_( data, options, None, None) if success or ak.NSCharacterEncodingDocumentAttribute not in options: if success: self.document_attrs = attrs break if err: log.error(err) options.pop(ak.NSCharacterEncodingDocumentAttribute, None) return success, err def dataOfType_error_(self, doctype, error): range = fn.NSMakeRange(0, self.text_storage.length()) attrs = self.document_attrs data, err = self.text_storage \ .dataFromRange_documentAttributes_error_(range, attrs, None) if err is None: try: self.update_syntaxer() app.save_editor_states() except Exception: log.error("unexpected error", exc_info=True) # if self.project is not None: # self.project.save() # self.updateSyntaxer() # if self.text_view is not None: # # make the undo manager recognize edits after save # self.text_view.breakUndoCoalescing() return (data, err) def setFileModificationDate_(self, date): super(TextDocument, self).setFileModificationDate_(date) self._filestat = None def analyze_content(self): text = self.text_storage.string() start, end, cend = text.getLineStart_end_contentsEnd_forRange_( None, None, None, (0, 0)) if end != cend: eol = EOLREF.get(text[cend:end], const.NEWLINE_MODE_UNIX) self.newline_mode = eol mode, size = calculate_indent_mode_and_size(text) if size is not None: self.indent_size = size if mode is not None: self.indent_mode = mode def is_externally_modified(self): """check if this document has been modified by another program""" url = self.fileURL() if url is not None and os.path.exists(url.path()): ok, mdate, err = url.getResourceValue_forKey_error_( None, fn.NSURLContentModificationDateKey, None) if ok: return self.fileModificationDate() != mdate return None def check_for_external_changes(self, window): if not self.is_externally_modified(): return if self.isDocumentEdited(): if window is None: return # ignore change (no gui for alert) stat = filestat(self.fileURL().path()) if self._filestat == stat: return self._filestat = stat def callback(code): if code == ak.NSAlertFirstButtonReturn: self.reload_document() alert = Alert.alloc().init() alert.setMessageText_("ā%sā source document changed" % self.displayName()) alert.setInformativeText_("Discard changes and reload?") alert.addButtonWithTitle_("Reload") alert.addButtonWithTitle_("Cancel") alert.beginSheetModalForWindow_withCallback_(window, callback) else: self.reload_document() @refactor("improve undo after reload - use difflib to replace changed text only") def reload_document(self): """Reload document with the given URL This implementation allows the user to undo beyond the reload. The down-side is that it may use a lot of memory if the document is very large. """ url = self.fileURL() if url is None or not os.path.exists(url.path()): return undo = self.undoManager() undo.should_remove = False textstore = self.text_storage self.text_storage = ak.NSTextStorage.alloc().init() try: ok, err = self.revertToContentsOfURL_ofType_error_( url, self.fileType(), None) finally: tempstore = self.text_storage self.text_storage = textstore undo.should_remove = True if not ok: log.error("could not reload document: %s", err) return # TODO report err textview = None for view in app.iter_views_of_document(self): textview = view.text_view if textview is not None: break text = tempstore.string() range = fn.NSRange(0, textstore.length()) if textview is None: textstore.replaceCharactersInRange_withString_(range, text) undo.removeAllActions() elif textview.shouldChangeTextInRange_replacementString_(range, text): #state = self.documentState textstore.replaceCharactersInRange_withString_(range, text) #self.documentState = state textview.didChangeText() textview.breakUndoCoalescing() # HACK use timed invocation to allow didChangeText notification # to update change count before _clearUndo is invoked self.performSelector_withObject_afterDelay_("_clearChanges", self, 0) textview.setSelectedRange_(fn.NSRange(0, 0)) self.update_syntaxer() @untested def prepareSavePanel_(self, panel): try: panel.setCanSelectHiddenExtension_(True) panel.setExtensionHidden_(False) panel.setAllowsOtherFileTypes_(True) name = panel.nameFieldStringValue() # 10.6 API url = self.fileURL() if url is not None: filename = url.lastPathComponent() directory = url.URLByDeletingLastPathComponent() panel.setDirectoryURL_(directory) # 10.6 API else: filename = name name += ".txt" if name != filename or (name.endswith(".txt") and "." in name[:-4]): panel.setNameFieldStringValue_(filename) exts = ["txt"] if "." in filename: ext = filename.rsplit(".", 1)[1] if ext not in exts: exts.insert(0, ext) panel.setAllowedFileTypes_(exts) except Exception: log.error("cannot prepare save panel...", exc_info=True) return True def _clearChanges(self): self.updateChangeCount_(ak.NSChangeCleared) def icon(self): url = self.fileURL() key = "" if url is None else url.path() old_key, data = self.icon_cache if old_key is None or old_key != key: data = fetch_icon(key) self.icon_cache = (key, data) return data @property def comment_token(self): return self.syntaxer.syntaxdef.comment_token def _get_syntaxdef(self): return self.syntaxer.syntaxdef def _set_syntaxdef(self, value): self.syntaxer.syntaxdef = value self.syntaxer.color_text(self.text_storage) syntaxdef = property(_get_syntaxdef, _set_syntaxdef) def update_syntaxer(self): if self.text_storage.delegate() is not self: self.text_storage.setDelegate_(self) filename = self.lastComponentOfFileName() if filename != self.syntaxer.filename: self.syntaxer.filename = filename syntaxdef = app.syntax_factory.get_definition(filename) if self.syntaxdef is not syntaxdef: self.props.syntaxdef = syntaxdef self.syntaxer.color_text(self.text_storage) def textStorageDidProcessEditing_(self, notification): range = self.text_storage.editedRange() self.syntaxer.color_text(self.text_storage, range) def updateChangeCount_(self, ctype): super(TextDocument, self).updateChangeCount_(ctype) app.item_changed(self, ctype) # def set_primary_window_controller(self, wc): # if wc.document() is self: # if self.scroll_view not in wc.mainView.subviews(): # wc.setDocument_(self) # else: # self.addWindowController_(wc) # if wc.controller.find_project_with_document(self) is None: # wc.add_document(self) def __repr__(self): return "<%s 0x%x %s>" % (type(self).__name__, id(self), self.displayName()) def close(self): # remove window controllers here so NSDocument does not close the windows for wc in list(self.windowControllers()): self.removeWindowController_(wc) ts = self.text_storage if ts is not None and ts.delegate() is self: ts.setDelegate_(None) self.text_storage = None super(TextDocument, self).close()