Module: environment-manager Author: Hugh Greene Synopsis: Functions the Environment provides to external callers. 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 DEFINITIONS -=- define class () keyword format-string: = "No library in the active project contains '%s'"; end class; define class () keyword format-string: = "No open project contains '%s'"; end class; //--- This finds the libraries that match the given pathname //--- within the active project. define function find-libraries-from-pathname (locator :: ) => (libraries :: /* of: vector(, ) */) let libraries = make(); let project = active-project(); when (project) do-project-file-libraries (method (library :: , record :: ) add!(libraries, vector(library, record)) end, project, locator); end; when (empty?(libraries)) signal(make(, format-arguments: vector(locator))); // --- The above 'signal' is allowed to return, though we have no // restarts yet to, say, allow the user to find/open the project. end; libraries end function find-libraries-from-pathname; define function find-projects-from-pathname (locator :: ) => (projects :: /* of: vector(, ) */) let all-projects = open-projects(); let projects = #(); for (proj in all-projects) let record = find-project-source-record(proj, locator); when (record) projects := add!(projects, vector(proj, record)); end; end; when (empty?(projects)) signal(make(, format-arguments: vector(locator))); // --- The above 'signal' is allowed to return, though we have no // restarts yet to, say, allow the user to find/open the project. end; projects end function find-projects-from-pathname; define function location-info->source-location (project :: , locator :: , coords :: ) => (location :: false-or()) let (start-line, start-column, end-line, end-column) = apply(values, coords); // ---*** Should make a full region-location when they become available. ignore(start-column, end-line, end-column); // Return the within the project. // NB the 2nd parameter of MAKE-LINE-LOCATION expects a line that is // relative to the source-record, so we have to deduct the distance // into the file that the source record starts (ie after the headers). let record = find-project-source-record(project, locator); let offset = record & source-record-start-line(record); when (offset) block () make-line-location(record, start-line - offset) exception (type-union(, )) #f end end end function; // In order for the server interface to work, it must be possible to // coerce to all argument types used here, from . I use a // separate GF from "as" so that I can add my own "sideways" methods // without messing things up for other people. define generic coerce-for-command-call (class :: , object :: ) => (object :: ); // where instance?(object, class) define method coerce-for-command-call (class :: , object :: ) => (object :: ) as(class, object) end method; define method coerce-for-command-call (class :: subclass(), string :: ) => (coords :: ) // Strings representing a "source region" are assumed to be of the form // "[[,][;[,]]]" // where "<>" surrounds 'placeholders' and "[]" indicates "optional" // Any of the placeholders may be empty. local method split (string :: , char :: ) => (before :: , after :: ) let split-index :: false-or() = find-key(string, curry(\==, char)); if (split-index) values(copy-sequence(string, end: split-index), copy-sequence(string, start: split-index + 1)) else values(string, "") end if end method, method get-integer (string :: ) => (value :: ) block() string-to-integer(string); exception () 0 end block; end method; // Parse the integers from the string. let (start-str :: , end-str :: ) = split(string, ';'); let (start-line-str :: , start-column-str :: ) = split(start-str, ','); let (end-line-str :: , end-column-str :: ) = split(end-str, ','); // Any numbers we can't find default to 0. map(get-integer, vector(start-line-str, start-column-str, end-line-str, end-column-str)) end method; /// -=- COMMANDS -=- // The argument types for each of these functions must be ones to // which s can be coerced. In particular, none can be calls // to "type-union" or "one-of", or constant types defined as such. // If a command needs more complicated parsing/conversion (say, // depending on other arguments) it can choose to accept a // and parse it explicitly. define macro command-function-definer { define command-function ?:name ?args ?:body end } => { define function ?name ?args ?body end; register-command-function(?#"name", ?name); } args: { ( ?params:* ) } => { ( ?params ) } { ( ?params:* ) => ( ?return:* ) } => { ( ?params ) => ( ?return ) } end macro; define function call-with-module (function :: , project :: false-or(), file :: , #rest args) => (#rest values) // Result type depends on FUNCTION let module = project & file-module(project, file); when (module) apply(function, project, module, args) end end function; define function defaulted-find-project (project-name :: ) => (project :: false-or()) if (empty?(project-name)) active-project() else find-project(project-name) end end function; // -=- PROPERTIES -=- // ---*** This should be ditched, probably. define function do-cmd-properties (project :: , module :: , name :: , #key object :: false-or() = #f) => (#rest values) unless (object) object := find-named-definition(project, module, name) end; when (object) show-properties(project, object) end end function do-cmd-properties; define command-function properties (client-id, project-name :: , file :: , coords :: , name :: ) ignore(client-id, coords); call-with-module(do-cmd-properties, defaulted-find-project(project-name), file, name) end command-function properties; // -=- COMPLETE -=- define function do-cmd-complete (project :: , module :: , name :: ) => (names :: /* of: */) // ignore(name, module, project); // ---*** Should really call some completion function ... #[] end function do-cmd-complete; define command-function complete (client-id, project-name :: , file :: , coords :: , name :: , #key initial-match-only? :: = #f) => (names :: /* of: */) ignore(client-id, file, coords, name, initial-match-only?); call-with-module(do-cmd-complete, defaulted-find-project(project-name), file, name) end command-function complete; // -=- FIND REFERENCES -=- /* --- For use if we end up grouping edit-definitions with find-uses. define constant $reference-kinds = #[#"definitions", #"uses", #"both"]; */ define function do-cmd-edit-definitions (project :: , module :: , name :: , #key object :: false-or() = #f) => (#rest values) unless (object) object := find-named-definition(project, module, name) end; when (object) let edited? = edit-definition(project, object); unless (edited?) error("Couldn't find '%s'.", environment-object-display-name(project, object, module)) end end end function do-cmd-edit-definitions; define command-function edit-definitions (client-id, project-name :: , file :: , coords :: , name :: ) => () // ignore(client-id, coords); call-with-module(do-cmd-edit-definitions, defaulted-find-project(project-name), file, name); end command-function edit-definitions; // -=- COMPILE -=- // Note: // The 'scope' argument is one of the following: // #"project-parse" => parse source, enough to build browser info; // #"project-compile" => compile only changes in project (usual choice); // #"project-clean-compile" => compile all of a project, even if up-to-date; // #"project-link" => link project, to derive target; // #"project-build" => do "compile" then "link". // #"project-clean-build" => do "clean compile" then "link". // // Exceptions: // "compile" may signal a . define function do-cmd-compile (project :: , scope :: , location :: ) compile-project-location(project, location, scope); end function do-cmd-compile; define command-function compile (client-id, project-name :: , file :: , coords :: , name :: , scope :: ) ignore(client-id, name); // Is name needed here? // Maybe return compilation log? let project = defaulted-find-project(project-name); let location = project & location-info->source-location (project, as(, file), coords); when (location) do-cmd-compile(project, scope, location); end; end command-function; // -=- DOCUMENTATION -=- define function do-cmd-documentation (project :: , module :: , name :: , #key object :: false-or() = #f) => (#rest values) unless (object) object := find-named-definition(project, module, name) end; when (object) show-documentation(name, project, module, object) end end; define command-function documentation (client-id, project-name :: , file :: , coords :: , name :: ) => () ignore(client-id, coords); // hughg: Not 100% sure call-with-module is appropriate here. call-with-module(do-cmd-documentation, defaulted-find-project(project-name), file, name); end command-function; // -=- DESCRIBE -=- define function do-cmd-describe (project :: false-or(), module :: false-or(), name :: , #key object :: false-or() = #f) => (#rest values) when (~object & (module & project)) object := find-named-definition(project, module, name) end; when (object) show-definition-summary(name, project, module, object) end end function do-cmd-describe; define command-function describe (client-id, project-name :: , file :: , coords :: , name :: ) => (success? :: ) ignore(client-id, coords); call-with-module(do-cmd-describe, defaulted-find-project(project-name), file, name); end command-function describe; // -=- BROWSE -=- // Browse an object define function do-cmd-browse (project :: , module :: , name :: , #key object :: false-or() = #f) => (#rest values) unless (object) object := find-named-definition(project, module, name) end; when (object) browse-object(project, object) end end; define command-function browse (client-id, project-name :: , file :: , coords :: , name :: ) => (success? :: ) ignore(client-id, coords, name); call-with-module(do-cmd-browse, defaulted-find-project(project-name), file, name); end command-function browse; // Browse the type of an object define function do-cmd-browse-type (project :: , module :: , name :: , #key object :: false-or() = #f) => (#rest values) unless (object) object := find-named-definition(project, module, name) end; when (object) browse-object-type(project, object) end end; define command-function browse-type (client-id, project-name :: , file :: , coords :: , name :: ) => (success? :: ) ignore(client-id, coords); call-with-module(do-cmd-browse-type, defaulted-find-project(project-name), file, name); end command-function browse-type; // Browse the generic function of some method call define function do-cmd-browse-function (project :: , module :: , name :: , #key object :: false-or() = #f) => (#rest values) unless (object) object := find-named-definition(project, module, name) end; when (object) browse-object-generic-function(project, object) end end; define command-function browse-function (client-id, project-name :: , file :: , coords :: , name :: ) => (success? :: ) ignore(client-id, coords); call-with-module(do-cmd-browse-function, defaulted-find-project(project-name), file, name); end command-function browse-function; // -=- OPEN A FILE -=- define function do-cmd-open-file (filename :: ) => (success? :: ) environment-open-file(as(, filename)) end function do-cmd-open-file; define command-function OpenFile (client-id, file :: ) => (success? :: ) ignore(client-id); do-cmd-open-file(file) end command-function OpenFile; // -=- PROVIDE RESULTS ASYNCHRONOUSLY -=- define function do-cmd-provide-results (id :: , results :: ) => () %provide-results(id, results) end; define command-function provide-results (client-id, results-id :: , results :: ) => () ignore(client-id); do-cmd-provide-results(results-id, results) end command-function provide-results;