Module: environment-deuce Synopsis: Environment Deuce Author: Hugh Greene 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 /// Auxilliary commands define function make-deuce-command (function :: ) => (command :: ) let command = method (frame) // Prefer the Deuce frame to the DUIM frame... let frame = find-relevant-editor-frame(frame); when (frame) deuce/execute-command-in-frame(frame, function) end end method; command end function make-deuce-command; define macro make-named-deuce-commands { make-named-deuce-commands ?commands end } => { ?commands } commands: { } => { } { ?:name; ... } => { define constant "frame-" ## ?name = make-deuce-command(?name); ... } end macro make-named-deuce-commands; // Other tools, like the Browser and Debugger, can return the Deuce pane // that has the input focus //---*** It sucks that we have to do things this way, but we really //---*** need for the commands to have the sheet as well as the frame define open generic find-relevant-editor-frame (frame :: ) => (editor-frame :: false-or()); define method find-relevant-editor-frame (frame :: ) => (editor-frame :: false-or()) #f end method find-relevant-editor-frame; define method find-relevant-editor-frame (frame :: ) => (editor-frame :: false-or()) frame end method find-relevant-editor-frame; define macro delegate-to-deuce { delegate-to-deuce ?commands end } => { ?commands } commands: { } => { } { ?env-command:name => ?deuce-command:name; ... } => { define sealed method ?env-command (frame :: ) => () deuce/execute-command-in-frame(frame, ?deuce-command) end method ?env-command; ... } { ?env-command:name ( ?ignored-keys:* ) => ?deuce-command:name; ... } => { define sealed method ?env-command (frame :: , #key ?ignored-keys) => () ignore(?ignored-keys); deuce/execute-command-in-frame(frame, ?deuce-command) end method ?env-command; ... } end macro delegate-to-deuce; /// From the "File" menu ... // ... I/O commands define method frame-open-file (frame :: , #rest keys, #key filename, deuce-frame, #all-keys) => () //---*** cpage: 1998.04.01 Note that this function really only takes #all-keys so that // this method can pass on parameters to open-file via next-method. One // example oddity here is that this function must accept deuce-frame: even // though it's really a "private" piece of information being sent from // this method to open-file. // We really should just export open-file, or something like it, because // this is the wrong abstraction level in which to be doing this. let buffer = frame-buffer(frame); let default = buffer & buffer-default-pathname(buffer); let type = buffer & source-file-type(buffer-major-mode(buffer)); // _This_ is a Deuce frame, so supply it as the 'deuce-frame:' keyword arg apply(next-method, frame, deuce-frame: frame, default: default & as(, default), default-type: type, keys) end method frame-open-file; delegate-to-deuce frame-revert-file (filename) => revert-file; frame-close-file (filename) => close-file; frame-save-file (filename) => save-file; frame-save-file-as (filename) => save-file-as; frame-save-all => save-all-files; end; // ... printing commands //---*** ??? // ... exit-frame: see editor.dylan:handle-event(..., ) //---*** Do we want some command that asks whether to kill all of //---*** DW or just the editor? Could also offer to save buffers before closing. /// From the "Edit" menu ... // ... undo commands define method command-available-for-focus? (gadget :: , command == ) => (available? :: ) let buffer = window-buffer(gadget); let history = buffer-undo-history(buffer); if (history) let (n-undo, n-redo) = undo-history-state(history); ~zero?(n-undo) end end method command-available-for-focus?; define method command-available-for-focus? (gadget :: , command == ) => (available? :: ) let buffer = window-buffer(gadget); let history = buffer-undo-history(buffer); if (history) let (n-undo, n-redo) = undo-history-state(history); ~zero?(n-redo) end end method command-available-for-focus?; define method execute-command-for-focus (gadget :: , command :: ) => () deuce/execute-command-in-frame(window-frame(gadget), deuce/undo-command) end method execute-command-for-focus; define method execute-command-for-focus (gadget :: , command :: ) => () deuce/execute-command-in-frame(window-frame(gadget), deuce/redo-command) end method execute-command-for-focus; // ... editing commands // ... clipboard commands define method frame-selection (frame :: ) => (selection :: false-or()) let pane = frame.%window; selected-text(pane) // Do we have to deal with the "copy-line" policy here? end method frame-selection; define method frame-selection-empty? (frame :: ) => (empty? :: ) let window = frame-window(frame); window & ~window-mark(window) end method frame-selection-empty?; delegate-to-deuce editor-frame-yank => yank; end; // ... selection commands /*---*** Hopefully not needed, since the Deuce gadget obeys //---*** the text gadget protocols define method do-execute-command (frame :: , command :: ) => () deuce/execute-command-in-frame(frame, mark-buffer) end method do-execute-command; define method do-execute-command (frame :: , command :: ) => () let window = frame-window(frame); when (window) clear-mark!(window: window, redisplay?: #t) end end method do-execute-command; define method command-available-for-focus? (frame :: , command == ) => (available? :: ) frame-window(frame) & frame-buffer(frame) & #t end method command-available-for-focus?; define method command-available-for-focus? (frame :: , command == ) => (available? :: ) let window = frame-window(frame); (window & frame-buffer(frame) & window-mark(window)) & #t end method command-available-for-focus?; */ // ... searching commands define method find-string (frame :: ) => () frame-edit-search-options(frame) end method find-string; // We can get here via 'incremental-search-forward', so be careful // to pop up a search dialog if there's already a search string define method find-next-string (frame :: ) => () if (frame-can-find?(frame)) frame-find-next(frame) else frame-edit-search-options(frame) end end method find-next-string; // Same deal w.r.t. 'incremental-search-backward' define method find-previous-string (frame :: ) => () if (frame-can-find?(frame)) frame-find-previous(frame) else frame-edit-search-options(frame) end end method find-previous-string; define method replace-string (frame :: ) => () frame-edit-search-options(frame) end method replace-string; define method query-replace-string (frame :: ) => () frame-edit-search-options(frame) end method query-replace-string; delegate-to-deuce editor-frame-goto => goto-line; end; /// ... searching commands for editor gadgets //---*** cpage: 1998.08.29 Note that we assume the frame command functions will // end up searching the given gadget. We probably need to change // this so there are command functions for sheets, with common // implementation functions that take a frame and a sheet. define method find-string (gadget :: ) => () frame-edit-search-options(sheet-frame(gadget)) end method find-string; define method find-next-string (gadget :: ) => () frame-find-next(sheet-frame(gadget)) end method find-next-string; define method find-previous-string (gadget :: ) => () frame-find-previous(sheet-frame(gadget)) end method find-previous-string; define method replace-string (gadget :: ) => () frame-edit-search-options(sheet-frame(gadget)) end method replace-string; define method query-replace-string (gadget :: ) => () frame-edit-search-options(sheet-frame(gadget)) end method query-replace-string; /// From the "View" menu ... // ... refresh and options define method refresh-frame (frame :: ) => () //---*** Will this potentially do too much redisplay? next-method(); deuce/execute-command-in-frame(frame, force-redisplay) end method refresh-frame; define sealed method editor-frame-show-cursor-position (frame :: ) => () deuce/execute-command-in-frame(frame, deuce/show-position) end method editor-frame-show-cursor-position; delegate-to-deuce frame-edit-options => choose-configuration; end; /// From the "Project" and "Application" menus ... /*---*** andrewa: let's try not delegating these at all... define sealed method do-delegate-to-project-browser (frame :: , command :: ) => () block () let current-project = editor-frame-current-project(frame); if (current-project) with-environment-frame (project-browser = frame, , project: current-project) command(project-browser); end; else command-error("The document in this window is not in the active project."); end; exception (not-found :: type-union(, )) command-error(condition-to-string(not-found)) end; end method do-delegate-to-project-browser; define macro delegate-to-project-browser { delegate-to-project-browser ?commands end } => { ?commands } commands: { } => { } { ?command:name; ... } => { define sealed method ?command (frame :: ) => () do-delegate-to-project-browser(frame, ?command) end method ?command; ... } { ?command:name ( ?ignored-keys:* ) ; ... } => { define sealed method ?command (frame :: , #key ?ignored-keys) => () ignore(?ignored-keys); do-delegate-to-project-browser(frame, ?command) end method ?command; ... } end macro delegate-to-project-browser; //---*** These should probably all become delegated to deuce. delegate-to-project-browser // Project Menu frame-advanced-build-dialog; // Advanced Build... frame-edit-project-settings; // Settings... // Go Menu frame-browse-threads; // Threads // Application Menu frame-start-application; // Start frame-debug-application; // Debug frame-interact; // Interact frame-pause-application (thread, startup-option); // Pause frame-resume-application; // Resume frame-stop-application; // Stop frame-restart-application; // Restart frame-start-or-resume-application; // [Run] toolbar button end; */ /*---*** andrewa: Let's not delegate these, since we can do a better job ---*** sharing the environment's code. delegate-to-deuce // Project Menu frame-build-project (process-subprojects?) => deuce/build-project; frame-clean-build-project (process-subprojects?) => deuce/clean-build-project; end; */ /// From the "Object" menu ... delegate-to-deuce frame-describe-primary-object => deuce/describe-object; frame-document-primary-object => deuce/show-documentation; frame-edit-primary-object => deuce/edit-definition; frame-browse-primary-object => deuce/browse-object; frame-browse-primary-object-class => deuce/browse-class; frame-browse-primary-object-generic-function => deuce/browse-function; //---*** Ditch or disable the next one: frame-display-primary-object-properties => deuce/describe-object; end; /// From the "Tools" menu ... // ... tool cloning define method make-clone (frame :: , #rest initargs) => (frame :: ) apply(next-method, frame, // editor: $environment-editor, buffer: frame-buffer(frame), initargs); end method make-clone; /// From the "History" menu ... /// From the "Help" menu ... /// Glue between Deuce and the Source Control Manager define sealed method do-source-control-operation (window :: , operation :: , #key pathname :: false-or(), reason? :: ) => (success? :: , pathname :: false-or(), message :: false-or()) ignore(reason?); let sccs = current-source-control-system(); if (sccs) block () let buffer = window-buffer(window); let class = select (operation) #"claim" => ; #"check-out" => ; #"check-in" => ; #"abandon" => ; #"merge" => ; #"diff" => ; #"report" => ; #"add" => ; #"remove" => ; end; let (logged-in?, login-failure-message) = source-control-maybe-login(sccs, class, owner: window); if (logged-in?) let filename = pathname & as(, pathname); let previous-options = get-property(buffer-properties(buffer), #"source-control-options"); let info = source-control-command-info (sccs, class, pathname: filename, defaults: previous-options); let options = get-source-control-arguments(sccs, info, owner: window, pathname: filename); if (options) put-property!(buffer-properties(buffer), #"source-control-options", options); execute-command(make(class, options: options)) else values(#f, #f, #f) end else values(#f, #f, login-failure-message) end exception (condition :: ) values(#f, #f, condition-to-string(condition)) end else values(#f, #f, "Source code control is not available") end end method do-source-control-operation; define method source-control-maybe-login (sccs :: , class :: subclass(), #key owner :: ) => (logged-in? :: , message :: false-or()) let login-info = source-control-login-info(sccs, class); if (login-info) let options = get-source-control-arguments(sccs, login-info, owner: owner); if (options) source-control-login(sccs, options) else values(#f, "Login cancelled") end else values(#t, #f) end end method source-control-maybe-login; define method get-source-control-arguments (sccs :: , info :: , #key owner :: , pathname :: false-or() = #f) => (command-options :: false-or()) let frame = sheet-frame(top-level-sheet(owner)); let framem = frame-manager(frame); let class = info.command-class; let title = info.command-title; let options = info.command-options; with-frame-manager (framem) let min-width = 250; let contents :: = make(); local method dialog-complete? (dialog :: ) => (complete? :: ) ignore(dialog); let complete? :: = #t; for (gadgets in contents) let gadget :: = gadgets[1]; let option :: = gadget.gadget-id; if (option-required?(option) & empty?(gadget-value(gadget))) complete? := #f end end; complete? end method dialog-complete?; for (option :: in options) add!(contents, vector(make(