Stephen Weeks avatar Stephen Weeks committed 0d46776 Merge

auto merge

Comments (0)

Files changed (4)

+features
+========
+
+changes
+=======
+
+- Omake.errors-follow-point {t,nil} ---> 
+  Omake.show-buffer-for-next-error-in {'dedicated-code-window, 'selected-window}
+- changed tmp directory 
+  /tmp/omake-server/seanmcl/-home-seanmcl-ocaml/elisp --->
+  /tmp/omake-server/seanmcl/home/seanmcl/ocaml/elisp
+- Added version numbers in mismatch message
+
+bug fixes
+=========
+
+- server catches and logs all exceptions.  This is good in itself, and in
+  addition prevents a bug where text on stderr was eval'ed by emacs.
+- you can now restart projects in different directories without getting
+  the dead-process error.
+
+--------------------------------------------------------------------------------
+
 features
 ========
 - added a configuration variable, [Omake.errors-follow-cursor], that controls whether
 
 Sane default settings for most emacs users
 
-** js-examples.el
-
-Examples of settings that some users find useful, but are not enabled
-by default.  If you have something useful, but not micro-feature
-quality, this is the place to share it.
-
 ** js-lib.el
 
 A grab-bag of functions for customizing emacs.
 
 Common variable settings for tuareg-mode.
 
-** js-omake.el
-
-Support for using OMake with Emacs
-
-** pa_ounit_tuareg.el
-
-Support for the unit testing camlp4 macros (e.g. TEST) in tuareg-mode
-
 * notes
 
 Note that the following libraries are *not* in this repo:
 
    - tuareg    
    - ocamlspot 
+
+If the unix domain socket file is deleted is running, a 
   :group 'omake
   :type 'string)
 
-(defcustom Omake.errors-follow-point nil
-  "When t, Omake.next-error will open the ml file in the current window"
+(defcustom Omake.show-buffer-for-next-error-in 'dedicated-code-window
+  "Either 'dedicated-code-window or 'selected-window"
   :group 'omake
-  :type 'boolean)
-;; (setq Omake.errors-follow-point t)
+  :type 'symbol)
+;; (setq Omake.show-buffer-for-next-error-in 'selected-window)
 
 ;;============================================================================;;
 ;; Faces                                                                      ;;
 
 ;; Detect version changes
 
-(defconst Omake.pre-version 7
+(defconst Omake.pre-version 8
   "We use a version number to synchronize the elisp code the omake server
 To roll a new version of elisp that is incompatible with ocaml or vice
 versa, you must bump the version number.  This prevents old elisp code
 (defun Omake.Window.get (kind)
   "Get an omake window.  Return the current window if none is set."
   (assert (Omake.window-kind-p kind))
-  (if (and Omake.errors-follow-point (equal kind Omake.Window.Code))
+  (if (and (equal kind Omake.Window.Code)
+           (equal Omake.show-buffer-for-next-error-in 'selected-window))
       (selected-window)
     (let* ((w (gethash kind Omake.Window.window-table))
            (ok (and w (window-live-p w)))
 
 (setq auto-revert-verbose nil)
 
-(defun Omake.File.unslash (s)
-  "Replace '/' with '-' in a string"
-  (apply 'concat (List.intersperse "-" (split-string s "/"))))
-;; (Omake.Util.unslash "/a/b/c")
-;; (Omake.Util.unslash (Omake.id-to-string id))
-
 (defun Omake.File.auto-revert (file buffer-name)
   "Open a file in auto-revert mode, and set the buffer name"
   (when (not (get-buffer buffer-name))
 (defun Omake.File.project-file (name id)
   (assert (Omake.id-p id))
   (let ((id (Omake.id-to-string id)))
-    (format "%s/%s/%s/%s"
-            Omake.File.root Omake.user
-            (Omake.File.unslash id)
-            name)))
+    (format "%s/%s%s/%s" Omake.File.root Omake.user id name)))
 
 (defun Omake.File.omake (id) (Omake.File.project-file "omake" id))
 (defun Omake.File.elisp (id) (Omake.File.project-file "elisp" id))
 ;; (Omake.Server.version)
 
 (defun Omake.Server.detect-mismatch ()
-  (unless (equal (Omake.Server.version) Omake.version)
-    (let ((res (y-or-n-p "The omake server has a different version than your elisp library.  Reload? ")))
-      (if (not res)
-          (error "Not starting server")
-        (load-library "omake")
-        (Omake.Server.start)))))
+  (let ((vs (Omake.Server.version))
+        (vo Omake.version))
+    (unless (equal vs vo)
+      (let ((res (y-or-n-p "Server version (%d) differes from elisp version (%d).  Reload? ")))
+        (if (not res)
+            (error "Not starting server")
+          (load-library "omake")
+          (if (equal vs Omake.version)
+              (Omake.Server.start)
+            (message "The versions still differ.  Aborting.")))))))
 
 (defun Omake.Server.start ()
   (when (Omake.Server.in-use)
   (assert (not (equal server-received server-version)))
   (let ((msg
          (if (< server-received server-version)
-             (format "The server version %d is newer than the elisp version %d.  Run M-x load-library omake" server-version server-received)
-           (format "The server version %d is older than the elisp version %d.  Run M-x Omake.shutdown then M-x Omake.compile" server-version server-received)
+             (format "Server version (%d) is newer than elisp version (%d).  Run M-x load-library omake" server-version server-received)
+           (format "Server version (%d) is older than elisp version (%d).  Run M-x Omake.shutdown then M-x Omake.compile" server-version server-received)
            )))
     (message msg)))
 ;; (Omake.Ping.version-mismatch :server-received 3 :server-version 2)
 (* -------------------------------------------------------------------------- *)
 
 (* Use a version number to synchronize with the elisp code. *)
-let omake_server_version = 7
+let omake_server_version = 8
 
 (* -------------------------------------------------------------------------- *)
 (*  Util                                                                      *)
 type 'a deferred = 'a Deferred.t
 type ('a, 'b) result = ('a, 'b) Result.t
 let paren s = sprintf "(%s)" s
-let unslash s = String.tr ~target:'/' ~replacement:'-' s
 let rm file = Asys.file_exists file >>= function
   | `Yes -> Aunix.unlink file
   | `No | `Unknown -> Deferred.unit
 let time_prefix () = sprintf "[%s]" (Time.to_string (Time.now ()))
 let log_msg s = sprintf "%s %s\n" (time_prefix()) s
 let ( ** ) f g x = f (g x)
+let dprintf fmt = Printf.printf (fmt ^^ "\n%!")
+let exn_to_string exn = Exn.to_string (Monitor.extract_exn exn)
+module Exn = struct end 
 
 (* -------------------------------------------------------------------------- *)
 (*  Ids                                                                       *)
   let _ = Shell.mkdir ~p:() root
 
   let project_dir id =
-    let dir = sprintf "%s/%s" root (unslash (Id.to_string id)) in
+    let dir = sprintf "%s%s" root (Id.to_string id) in
     Shell.mkdir ~p:() dir;
     dir
 
 module Server : sig
   val logf : ('a, unit, string, unit) format4 -> 'a
   val wait_for_log : unit -> unit deferred
+  val log_exn : ?msg:string -> exn -> unit
 end = struct
   let log_writer = lazy (Writer.open_file File.server_log)
   let wait_for_log () = Lazy.force log_writer >>| fun _ -> ()
   let logf fmt =
     match Deferred.peek (Lazy.force log_writer) with
     | None ->
-      printf "WARNING: no log writer.  Writing to stdout\n";
+      printf "WARNING: no log writer.  Writing to stdout.\n";
       ksprintf (fun s -> printf "%s" (log_msg s)) fmt
     | Some writer ->
       ksprintf (fun s -> Writer.writef writer "%s" (log_msg s)) fmt
+  let log_exn ?(msg = "Caught exception") e = 
+    logf "%s: %s" msg (exn_to_string e)
 end
 
 (* -------------------------------------------------------------------------- *)
           Server.logf
             "Can't read env file for %s (%s). Writing new file from bash env."
             (Id.to_string id)
+            (* (exn_to_string error); *)
             (Error.to_string_hum error);
-          (* (Error.to_string_hum error); *)
           create id
       end
     | false -> create id
     log_writer : Writer.t;
     (* Pid of the shell managing the omake process *)
     pid : Pid.t;
-    (* Exit status of the omake parent process (debug) *)
+    (* Exit status of the omake parent process. *)
     omake_process_status : Unix.Exit_or_signal.t deferred;
+    (* Whether to alert emacs when the omake process is dead. *)
+    mutable alert_when_omake_dies : bool;
   }
   val create : Create.t -> t deferred
   val start : ?file:path -> t -> unit deferred
     log_writer : Writer.t;
     pid : Pid.t;
     omake_process_status : Unix.Exit_or_signal.t deferred;
+    mutable alert_when_omake_dies : bool;
   }
 
   let to_string_hum t =
-    sprintf "{ id = %s; omakeroot = %s; compilation = %s; pid = %s; determined = %b; }"
+    sprintf "{ id = %s; omakeroot = %s; compilation = %s; pid = %s; determined = %b; alert = %b; }"
       (Id.to_string t.id)
       t.omakeroot_dir
       t.compilation_dir
       (Pid.to_string t.pid)
       (Deferred.is_determined t.omake_process_status)
+      t.alert_when_omake_dies
 
   let logf t fmt =
     ksprintf (fun s -> Writer.write t.log_writer (log_msg s)) fmt
 
+  let handle_dead_omake_process t = 
+    let ids = Id.to_string t.id in
+    t.omake_process_status >>> (fun res ->
+      if t.alert_when_omake_dies then begin 
+        Server.logf "Process died for model: %s" ids;
+        To_emacs.send_async
+          "(Omake.Ocaml.update-model-dead :id \"%s\" :msg \"The omake process is dead: %s.\")"
+          ids
+          (Unix.Exit_or_signal.to_string_hum res)
+      end)
+
   let create t =
     try_with (fun () ->
       let id = t.Create.id in
             pid := Some pid';
             Writer.writef log_writer "Pid: %s\n" (Pid.to_string pid'))
         in
-        (omake_process_status >>> (fun res ->
-          Server.logf "Process died for model: %s" (Id.to_string id);
-          To_emacs.send_async "(message \"omake is dead for %s\")" (Id.to_string id);
-          To_emacs.send_async
-            "(Omake.Ocaml.update-model-dead :id \"%s\" :msg \"The omake process is dead: %s.\")"
-            (Id.to_string id)
-            (Unix.Exit_or_signal.to_string_hum res)));
         let pid = match !pid with
           | None -> failwith "Impossible: f has returned"
           | Some pid -> pid
         ; log_writer
         ; pid
         ; omake_process_status
+        ; alert_when_omake_dies = true
         })
         in
         match t with
-        | `Ok t -> return t
+        | `Ok t -> 
+          handle_dead_omake_process t;
+          return t
         | `Already_closed -> failwithf "Already closed: %s" (Id.to_string id) ()
         | `Error exn -> raise exn)) >>= function
     | Ok t -> return t
     | Error exn ->
-      Server.logf "ERROR: %s" (Exn.to_string (Monitor.extract_exn exn));
+      Server.logf "ERROR: %s" (exn_to_string exn);
       raise exn
 
   let start ?file t =
     let elisp_pipe = Omake.parse_omake_output ~omakeroot_dir reader in
     Pipe.iter elisp_pipe ~f:(function
     | Error exn ->
-      logf t "ERROR: %s" (Exn.to_string exn);
+      logf t "ERROR: %s" (exn_to_string exn);
       Deferred.unit
     | Ok elisp ->
       logf t "New output";
     in
     kids >>= fun kids ->
     try_with (fun () ->
+      t.alert_when_omake_dies <- false;
       Deferred.List.iter kids ~f:(fun k -> Ashell.run "kill" [k]) >>= fun () ->
       Ashell.run "kill" [pid]) >>| function
     | Ok () -> ()
-    | Error exn ->
-      Server.logf "Error killing processes: %s" (Exn.to_string exn)
+    | Error exn -> 
+      Server.log_exn ~msg:"Error killing processes" exn
 end
 
 (* -------------------------------------------------------------------------- *)
         Writer.close writer) >>= function
     | Ok () -> Deferred.unit
     | Error exn ->
-      Server.logf "Caught exception: %s" (Exn.to_string (Monitor.extract_exn exn));
+      Server.log_exn (Monitor.extract_exn exn);
       Deferred.unit)
 
 let connect msg =
   (* Make sure the message ends with a space so the sexp parser knows
      it's finished *)
   let msg = msg ^ " " in
-  Tcp.connect_unix ~file:File.socket () >>= fun (reader, writer) ->
-  Writer.write writer msg;
-  Pipe.iter (Reader.pipe reader) ~f:(fun s ->
-    printf "%s\n" s;
-    Deferred.unit)
+  let file = File.socket in
+  Asys.file_exists file >>= function
+  | `No | `Unknown ->
+    failwithf "Missing socket file: %s" file ()
+  | `Yes -> 
+    Tcp.connect_unix ~file () >>= fun (reader, writer) ->
+    Writer.write writer msg;
+    Pipe.iter (Reader.pipe reader) ~f:(fun s ->
+      printf "%s\n" s;
+      Deferred.unit)
 
 (* -------------------------------------------------------------------------- *)
 (*  Command                                                                   *)
   )
   (fun pid -> schedule
     ~f:(fun () ->
-      To_emacs.set_pid pid;
-      serve ())
+      try_with (fun () -> 
+        To_emacs.set_pid pid;
+        serve ()) >>| function
+      | Ok () -> ()
+      | Error exn -> Server.log_exn exn)
     ())
 
 let send_cmd =
     )
     (fun msg ->
       schedule
-      ~f:(fun () -> connect msg)
+      ~f:(fun () -> 
+        try_with (fun () -> connect msg) >>| function
+        | Error exn -> 
+          Server.log_exn (Monitor.extract_exn exn);
+          To_emacs.send_async "(message \"Omake server error.  Do M-x Omake.show-server-log\")"
+        | Ok () -> ())
       ~quit:()
       ())
 
 let in_use_cmd =
   Command.basic
     ~summary:"return 0 if a server is listening on the socket, 1 otherwise"
-    Command.Spec.(
-      const ()
-    )
+    Command.Spec.( const () )
     (fun () ->
       schedule
         ~f:(fun () -> try_with (fun () -> connect "(Ping 0)") >>| function
-        | Ok _ -> shutdown 0
-        | Error _ -> shutdown 1)
-        ~quit:()
+        | Ok () -> shutdown 0
+        | Error _ -> 
+          shutdown 1)
         ())
 
 let show_model_cmd =
       let msg = sprintf "(Show_model %s)" id in
       schedule
         ~f:(fun () -> try_with (fun () -> connect msg) >>| function
-        | Ok _ -> shutdown 0
-        | Error _ -> shutdown 1)
+        | Ok () -> shutdown 0
+        | Error exn -> 
+          printf "Omake server error: %s\n" (exn_to_string exn);
+          shutdown 1)
         ~quit:()
         ())
 
         let elisp = Omake.parse_omake_output ~omakeroot_dir:"/a/b/c" lines in
         Pipe.iter elisp ~f:(function
         | Error exn ->
-          printf "%s\n" (Exn.to_string exn);
+          printf "%s\n" (exn_to_string exn);
           shutdown 1;
           Deferred.unit
         | Ok e -> printf "%s\n" e;
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.