Module: deuce-internals Synopsis: The Deuce editor Author: Scott McKay Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// Composite buffers define open abstract class () end class ; define method composite-buffer? (buffer :: ) => (composite? :: singleton(#f)) #f end method composite-buffer?; define sealed inline method composite-buffer? (buffer :: ) => (composite? :: singleton(#t)) #t end method composite-buffer?; define method buffer-section-separator-style (buffer :: ) => (style :: ) #"always" end method buffer-section-separator-style; // When we add a section node to a composite buffer, establish a two-way // associated between the composite buffer and any other buffers that // use the section define sealed method add-node! (buffer :: , node :: , #key after :: type-union(, one-of(#f, #"start", #"end")) = #"end") => () ignore(after); next-method(); let section = node-section(node); do-associated-buffers (other :: = *editor-frame*) when (buffer ~== other & buffer-contains-section?(other, section)) unless (member?(buffer, buffer-associated-buffers(other))) push!(buffer-associated-buffers(other), buffer) end; unless (member?(other, buffer-associated-buffers(buffer))) push!(buffer-associated-buffers(buffer), other) end end end end method add-node!; define sealed method remove-node! (buffer :: , node :: ) => () let section = node-section(node); do-associated-buffers (other :: = *editor-frame*) when (buffer ~== other & buffer-contains-section?(other, section)) buffer-associated-buffers(other) := remove!(buffer-associated-buffers(other), buffer) end end; next-method() end method remove-node!; define method buffer-default-pathname (buffer :: ) => (pathname :: ) "definitions" end method buffer-default-pathname; // Composite buffers save like file buffers, but the 'save-buffer' method // write out all of the associated file buffers. Cool, huh? define method saves-like-file-buffer? (buffer :: ) => (saves? :: ) #t end method saves-like-file-buffer?; // Saving a composite buffer causes all of the associated buffers to be saved. // Note the "Save As" just saves the text of the buffer to the specified location, // as it would for any other kind of buffer. define method save-buffer (buffer :: , #key frame = *editor-frame*, editor) => (pathname :: false-or(), condition) let editor = editor | frame-editor(frame); let window = frame-window(frame); let buffers :: = make(); // Do this the hard way, so that we only offer to save buffers // that have been modified by way of this composite buffer for (node = buffer-start-node(buffer) then node-next(node), until: ~node) let section :: = node-section(node); when (section-modification-tick(section) > section-sectionization-tick(section)) let home-buffer = section-home-buffer(section, editor: editor); when (home-buffer & file-buffer?(home-buffer)) add-new!(buffers, home-buffer) end end end; do-save-all-files(frame, buffers, curry(display-message, window)); //--- A kludgy way of indicating the save succeeded... values("all home files", #f) end method save-buffer; define sealed method kill-buffer (buffer :: , #key frame = *editor-frame*, editor, no-exit-frame) => () ignore(editor, no-exit-frame); for (node = buffer-start-node(buffer) then node-next(node), until: ~node) let section = node-section(node); do-associated-buffers (other :: = frame) when (buffer ~== other & buffer-contains-section?(other, section)) buffer-associated-buffers(other) := remove!(buffer-associated-buffers(other), buffer) end end end; next-method(); end method kill-buffer; // A command that will get us from a section to its home buffer... define command edit-home-definition (frame, #key section) "Edit this definition in its home buffer." let window :: = frame-window(frame); let buffer :: = frame-buffer(frame); when (composite-buffer?(buffer)) let section = section | line-section(bp-line(point())); let home = section-home-buffer(section); when (home & home ~== buffer) select-buffer-in-appropriate-window(window, home, line: section-start-line(section)) end end; frame-last-command-type(frame) := #"motion" end command edit-home-definition; define sealed class (, , ) end class ; define sealed domain make (singleton()); define sealed domain initialize (); /// Special-purpose buffers for browsing define open abstract class (, ) // The definition we'll browse // Back-end clients get to define exactly what a definition is... sealed constant slot browsing-buffer-definition, required-init-keyword: definition:; // The name-key applied to the definition gives a string sealed constant slot browsing-buffer-name-key :: , required-init-keyword: name-key:; // A format string for generating the name of the buffer, or #f // (in which case the subclass may supply a 'buffer-name' method) sealed constant slot %format-string :: false-or() = #f, init-keyword: format-string:; sealed slot %browsing-buffer-name :: false-or() = #f; // The generator applied to the definition produces a sequence of sections sealed constant slot browsing-buffer-generator :: , required-init-keyword: generator:; sealed constant slot %node-class :: subclass() = , init-keyword: node-class:; end class ; define method buffer-name (buffer :: ) => (name :: ) buffer.%browsing-buffer-name | begin let definition = browsing-buffer-definition(buffer); let name-key = browsing-buffer-name-key(buffer); let name = format-to-string(buffer.%format-string | "%s", name-key(definition)); buffer.%browsing-buffer-name := name; name end end method buffer-name; define method revert-buffer (buffer :: , #key buffer-filler :: false-or() = fill-definition-browsing-buffer, major-mode) => (reverted? :: ) ignore(major-mode); // Reset the timestamps on the buffer // Since the undo history is per-section, we don't need to reset it let tick = tick(); buffer-modification-tick(buffer) := tick; buffer-save-tick(buffer) := tick; // Now go read the contents of the buffer when (buffer-filler) buffer-filler(buffer) end; #t end method revert-buffer; define method fill-definition-browsing-buffer (buffer :: ) => () let definition = browsing-buffer-definition(buffer); let generator = browsing-buffer-generator(buffer); let sections :: false-or() = generator(definition); buffer-start-node(buffer) := #f; buffer-end-node(buffer) := #f; if (sections & ~empty?(sections)) for (section :: in sections) let node = make-section-node(buffer, section, node-class: buffer.%node-class); add-node!(buffer, node) end else // No definitions, so make an empty section node for the buffer let node = make-empty-section-node(buffer); add-node!(buffer, node) end end method fill-definition-browsing-buffer; /// Class and function browsing buffer classes define sealed class () keyword format-string: = "Subclasses of %s"; end class ; define sealed class () keyword format-string: = "Superclasses of %s"; end class ; define sealed class () keyword format-string: = "Methods of class %s"; end class ; define sealed class () keyword format-string: = "Methods of generic function of %s"; end class ; define sealed class () keyword format-string: = "Callers of %s"; end class ; define sealed class () keyword format-string: = "Callees of %s"; end class ;