├── .merlin ├── src ├── hayots_ide.h ├── hayots_app_delegate.h ├── hayots_app_dispatcher.h ├── service.ml ├── hayots_ml_stubs.c ├── hayots_app_delegate.c ├── hayots_ide.c ├── hayots.ml └── hayots_app_dispatcher.c ├── .gitignore ├── configure ├── README.md ├── Makefile ├── _oasis └── setup.ml /.merlin: -------------------------------------------------------------------------------- 1 | B _build/src 2 | 3 | S src 4 | 5 | PKG lwt.unix cmdliner yojson bos unix lwt.preemptive 6 | PKG lwt.ppx oasis msgpack 7 | FLG -w +a-4-40..42-44-45-48 8 | -------------------------------------------------------------------------------- /src/hayots_ide.h: -------------------------------------------------------------------------------- 1 | // -*- objc -*- 2 | 3 | #pragma once 4 | 5 | #import 6 | 7 | @interface Hayots_IDE : NSView 8 | 9 | -(void)speak_test; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.cmi 3 | *.cmt 4 | *.cmx 5 | *.cmxa 6 | *.a 7 | _build 8 | *.txt 9 | *.data 10 | *.log 11 | *.native 12 | api.docdir 13 | *.markdown 14 | _tests/* 15 | testapi.c 16 | MGSFragaria.framework/ 17 | libmsgpack-objc.dylib 18 | msgpack-objectivec/ -------------------------------------------------------------------------------- /src/hayots_app_delegate.h: -------------------------------------------------------------------------------- 1 | // -*- objc -*- 2 | 3 | #pragma once 4 | 5 | #import 6 | 7 | #import "hayots_ide.h" 8 | #import "hayots_app_dispatcher.h" 9 | 10 | @interface AppDelegate : NSObject 11 | 12 | @property (strong, nonatomic) Dispatcher *ml_dispatch; 13 | @property (strong, nonatomic) NSWindow *main_window; 14 | @property (strong, nonatomic) Hayots_IDE *app_logic; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # OASIS_START 4 | # DO NOT EDIT (digest: dc86c2ad450f91ca10c931b6045d0499) 5 | set -e 6 | 7 | FST=true 8 | for i in "$@"; do 9 | if $FST; then 10 | set -- 11 | FST=false 12 | fi 13 | 14 | case $i in 15 | --*=*) 16 | ARG=${i%%=*} 17 | VAL=${i##*=} 18 | set -- "$@" "$ARG" "$VAL" 19 | ;; 20 | *) 21 | set -- "$@" "$i" 22 | ;; 23 | esac 24 | done 25 | 26 | ocaml setup.ml -configure "$@" 27 | # OASIS_STOP 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hayots 2 | ------ 3 | 4 | native OCaml IDE on OS X. 5 | 6 | Just type `make`, then try it with `./hayots.native` 7 | 8 | # Build procedure 9 | 10 | 1. Need to `git clone https://github.com/mugginsoft/Fragaria` 11 | 2. `xcodebuild -project Fragaria.xcodeproj -alltargets -configuration Release` 12 | 3. Use framework in build 13 | 4. `cp -R MGSFragaria.framework ~/Library/Frameworks` (better with 14 | `install_name_tool`) 15 | 5. Have the msgpack library installed, can install with `opam 16 | depext` for `brew install msgpack`. 17 | -------------------------------------------------------------------------------- /src/hayots_app_dispatcher.h: -------------------------------------------------------------------------------- 1 | // -*- objc -*- 2 | 3 | #pragma once 4 | 5 | #import 6 | 7 | @interface Dispatcher : NSObject 8 | 9 | @property (nonatomic, strong) NSApplication *app; 10 | @property (nonatomic, strong) NSInputStream *reader; 11 | @property (nonatomic, strong) NSOutputStream *writer; 12 | @property int ocaml_child_pid; 13 | 14 | -(instancetype)initWithReader:(NSInputStream*)r writer:(NSOutputStream*)w; 15 | -(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode; 16 | 17 | -(NSDictionary*)read_command; 18 | -(void)send_reply:(NSDictionary*)reply_dict; 19 | 20 | @end 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/service.ml: -------------------------------------------------------------------------------- 1 | open Lwt.Infix 2 | 3 | class merlin = object 4 | val handle = 5 | Lwt_process.open_process 6 | ("opam", [|"opam"; "config";"exec"; "--"; "ocamlmerlin"|]) 7 | end 8 | 9 | class ocamldebug binary_path = object 10 | val handle = 11 | Lwt_process.open_process 12 | ("opam", [|"opam"; "config";"exec"; "--";"ocamldebug"; binary_path|]) 13 | end 14 | 15 | class utop = object 16 | val handle = 17 | Lwt_process.open_process ("opam", [|"opam"; "config";"exec"; "--"; "utop"|]) 18 | 19 | method read_all = 20 | Lwt_io.read_lines handle#stdout 21 | |> Lwt_stream.iter_s (fun s -> Lwt_io.printlf "%s" s) 22 | 23 | end 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # OASIS_START 2 | # DO NOT EDIT (digest: a3c674b4239234cbbe53afe090018954) 3 | 4 | SETUP = ocaml setup.ml 5 | 6 | build: setup.data 7 | $(SETUP) -build $(BUILDFLAGS) 8 | 9 | doc: setup.data build 10 | $(SETUP) -doc $(DOCFLAGS) 11 | 12 | test: setup.data build 13 | $(SETUP) -test $(TESTFLAGS) 14 | 15 | all: 16 | $(SETUP) -all $(ALLFLAGS) 17 | 18 | install: setup.data 19 | $(SETUP) -install $(INSTALLFLAGS) 20 | 21 | uninstall: setup.data 22 | $(SETUP) -uninstall $(UNINSTALLFLAGS) 23 | 24 | reinstall: setup.data 25 | $(SETUP) -reinstall $(REINSTALLFLAGS) 26 | 27 | clean: 28 | $(SETUP) -clean $(CLEANFLAGS) 29 | 30 | distclean: 31 | $(SETUP) -distclean $(DISTCLEANFLAGS) 32 | 33 | setup.data: 34 | $(SETUP) -configure $(CONFIGUREFLAGS) 35 | 36 | configure: 37 | $(SETUP) -configure $(CONFIGUREFLAGS) 38 | 39 | .PHONY: build doc test all install uninstall reinstall clean distclean configure 40 | 41 | # OASIS_STOP 42 | -------------------------------------------------------------------------------- /src/hayots_ml_stubs.c: -------------------------------------------------------------------------------- 1 | // -*- objc -*- 2 | 3 | #define CAML_NAME_SPACE 4 | 5 | #import 6 | #import 7 | 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | #import 17 | #import 18 | 19 | #import "hayots_app_delegate.h" 20 | #import "hayots_app_dispatcher.h" 21 | 22 | extern "C" { 23 | 24 | void 25 | hayots_ml_init_gui(value sock_for_child) 26 | { 27 | CAMLparam1(sock_for_child); 28 | 29 | @autoreleasepool { 30 | 31 | NSApplication *app = [NSApplication sharedApplication]; 32 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 33 | CFSocketNativeHandle socket_handle = 34 | (CFSocketNativeHandle)Int_val(sock_for_child); 35 | CFReadStreamRef r = NULL; 36 | CFWriteStreamRef w = NULL; 37 | CFStreamCreatePairWithSocket(kCFAllocatorDefault, 38 | socket_handle, 39 | &r, 40 | &w); 41 | 42 | Dispatcher *dispatcher = [[Dispatcher alloc] 43 | initWithReader:(__bridge NSInputStream*)r 44 | writer:(__bridge NSOutputStream*)w]; 45 | AppDelegate *app_delegate = [AppDelegate new]; 46 | app.delegate = app_delegate; 47 | 48 | app_delegate.ml_dispatch = dispatcher; 49 | dispatcher.app = app; 50 | [dispatcher.app run]; 51 | CAMLnoreturn; 52 | } 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /_oasis: -------------------------------------------------------------------------------- 1 | # -*- conf -*- 2 | OASISFormat: 0.4 3 | OCamlVersion: >= 4.04.0 4 | Name: hayots 5 | Version: 0.0.1 6 | Maintainers: Edgar Aroutiounian 7 | Homepage: https://github.com/fxfactorial/hayots 8 | Synopsis: OCaml IDE on OS X 9 | Authors: Edgar Aroutiounian 10 | License: BSD-3-clause 11 | Plugins: META (0.4), DevFiles (0.4) 12 | AlphaFeatures: ocamlbuild_more_args 13 | 14 | Description: OCaml IDE on OS X 15 | PostBuildCommand: 16 | mkdir -p ~/Library/Frameworks; 17 | rm -rf ~/Library/Frameworks/MGSFragaria.framework; 18 | cp -R MGSFragaria.framework ~/Library/Frameworks 19 | 20 | Executable hayots 21 | Build$: system(macosx) 22 | Path: src 23 | install:true 24 | BuildTools:ocamlbuild 25 | MainIs: hayots.ml 26 | NativeOpt: 27 | -g -w +a-4-40..42-44-45-48 28 | -cclib -fsanitize=address 29 | -ccopt -fsanitize=address 30 | CompiledObject: native 31 | CCOpt: 32 | -g -Wall -ObjC++ -std=c++14 -stdlib=libc++ 33 | -I$(PWD)/MGSFragaria.framework/Headers 34 | -I$(PWD)/msgpack-objectivec 35 | -fno-omit-frame-pointer 36 | -fsanitize-undefined-trap-on-error 37 | -fno-optimize-sibling-calls 38 | -fsanitize=address 39 | CCLib: 40 | -lc++ 41 | -L$(PWD) 42 | -lmsgpack-objc 43 | -F$(PWD) 44 | -framework Foundation 45 | -framework Appkit 46 | -framework MGSFragaria 47 | CSources: 48 | hayots_ide.h, 49 | hayots_app_delegate.h, 50 | hayots_app_dispatcher.h, 51 | hayots_app_dispatcher.c, 52 | hayots_ide.c, 53 | hayots_ml_stubs.c, 54 | hayots_app_delegate.c 55 | BuildDepends: 56 | lwt.unix, cmdliner, yojson, 57 | lwt.ppx, lwt.preemptive, 58 | threads, ocp-indent.lib, 59 | msgpack 60 | -------------------------------------------------------------------------------- /src/hayots_app_delegate.c: -------------------------------------------------------------------------------- 1 | // -*- objc -*- 2 | 3 | #import 4 | 5 | #import 6 | 7 | #include "hayots_app_delegate.h" 8 | #include "hayots_ide.h" 9 | 10 | @implementation AppDelegate 11 | 12 | - (void)applicationDidFinishLaunching:(NSNotification *)a_notification 13 | { 14 | int flags = 0; 15 | // Deprecated but works 16 | flags = NSTitledWindowMask 17 | | NSResizableWindowMask 18 | | NSUnifiedTitleAndToolbarWindowMask 19 | | NSClosableWindowMask 20 | | NSMiniaturizableWindowMask; 21 | 22 | // For the future 23 | // flags = 24 | // NSWindowStyleMaskTitled 25 | // | NSWindowStyleMaskResizable 26 | // | NSWindowStyleMaskUnifiedTitleAndToolbar 27 | // | NSWindowStyleMaskClosable 28 | // | NSWindowStyleMaskMiniaturizable; 29 | 30 | auto screen_frame = [[NSScreen mainScreen] frame]; 31 | self.main_window = 32 | [[NSWindow alloc] 33 | initWithContentRect:NSMakeRect(100, 100, 34 | screen_frame.size.width - 200, 35 | screen_frame.size.height - 200) 36 | styleMask:flags 37 | backing:NSBackingStoreBuffered 38 | defer:NO]; 39 | 40 | NSToolbar *bar = [[NSToolbar alloc] initWithIdentifier:@"main_window_toolbar"]; 41 | bar.displayMode = NSToolbarDisplayModeIconAndLabel; 42 | bar.sizeMode = NSToolbarSizeModeSmall; 43 | 44 | self.app_logic = 45 | [[Hayots_IDE alloc] 46 | initWithFrame:NSMakeRect(0, 0, 47 | self.main_window.frame.size.width - 3, 48 | self.main_window.frame.size.height - 20)]; 49 | 50 | self.main_window.contentView = self.app_logic; 51 | 52 | [bar setDelegate:self.app_logic]; 53 | self.main_window.toolbar = bar; 54 | 55 | [self.main_window setTitle:@"Hayots -- OCaml IDE"]; 56 | [self.main_window makeKeyAndOrderFront:NSApp]; 57 | // Make sure we're ahead of anything else. 58 | [self.main_window setLevel:NSNormalWindowLevel + 1]; 59 | 60 | } 61 | 62 | - (void)applicationWillTerminate:(NSApplication *)application 63 | { 64 | kill(self.ml_dispatch.ocaml_child_pid, 5); 65 | } 66 | 67 | -(void)applicationWillResignActive:(id)whatever 68 | { 69 | // Be nice and go away 70 | [self.main_window setLevel:NSNormalWindowLevel]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /src/hayots_ide.c: -------------------------------------------------------------------------------- 1 | // -*- objc -*- 2 | 3 | #import 4 | 5 | #import 6 | 7 | #import "hayots_ide.h" 8 | #import "MGSFragaria.h" 9 | 10 | @implementation Hayots_IDE : NSView 11 | 12 | -(instancetype)initWithFrame:(NSRect)f 13 | { 14 | if ((self = [super initWithFrame:f])) { 15 | [self setup_menus]; 16 | [self setup_main_interface]; 17 | } 18 | return self; 19 | } 20 | 21 | - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar 22 | itemForItemIdentifier:(NSString *)itemIdentifier 23 | willBeInsertedIntoToolbar:(BOOL)flag 24 | { 25 | // If You want to add a new toolbar item, make it here and add the 26 | // identifier to the other two methods related to the toolbar 27 | return nil; 28 | } 29 | 30 | - (NSArray*)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar 31 | { 32 | return @[NSToolbarShowColorsItemIdentifier, NSToolbarShowFontsItemIdentifier]; 33 | } 34 | 35 | - (NSArray*)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar 36 | { 37 | return @[NSToolbarShowColorsItemIdentifier, NSToolbarShowFontsItemIdentifier]; 38 | } 39 | 40 | -(void)setup_menus 41 | { 42 | NSMenu *menu_bar = [NSMenu new]; 43 | NSMenu *app_menu = [NSMenu new]; 44 | 45 | NSMenuItem *app_item = [NSMenuItem new]; 46 | 47 | NSMenuItem *quit_item = 48 | [[NSMenuItem alloc] initWithTitle:@"Quit Hayots" 49 | action:@selector(terminate:) 50 | keyEquivalent:@"q"]; 51 | NSArray *add_these = @[quit_item, [NSMenuItem separatorItem]]; 52 | 53 | for (id iter in add_these) [app_menu addItem:iter]; 54 | 55 | [menu_bar addItem:app_item]; 56 | [app_item setSubmenu:app_menu]; 57 | [NSApp setMainMenu:menu_bar]; 58 | } 59 | 60 | -(void)setup_main_interface 61 | { 62 | NSTextField *f = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 50, 50)]; 63 | f.stringValue = @"Hello world"; 64 | [self addSubview:f]; 65 | 66 | MGSFragaria *fragaria = [[MGSFragaria alloc] init]; 67 | [fragaria setObject:self forKey:MGSFODelegate]; 68 | [fragaria setObject:@"Objective Caml" forKey:MGSFOSyntaxDefinitionName]; 69 | [fragaria embedInView:self]; 70 | [fragaria setString:@"(* Hayots OCaml IDE *)\nlet () = print_endline \"Hello World\""]; 71 | } 72 | 73 | -(void)speak_test 74 | { 75 | std::cout << "Called in IDE\n"; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /src/hayots.ml: -------------------------------------------------------------------------------- 1 | open Lwt.Infix 2 | 3 | external init_ide : int -> unit = "hayots_ml_init_gui" 4 | 5 | let rec forever () = 6 | let _ = Lwt.wait () in 7 | forever () 8 | 9 | class hayots sock = object 10 | 11 | val out_channel = Lwt_io.of_fd ~mode:Lwt_io.Output sock 12 | val in_channel = Lwt_io.of_fd ~mode:Lwt_io.Input sock 13 | 14 | initializer 15 | Gc.finalise 16 | (fun out_chan -> 17 | (Lwt_io.close out_chan >|= fun _ -> ()) |> Lwt.ignore_result) 18 | out_channel 19 | 20 | method send_message s = Lwt_io.( 21 | let length = String.length s in 22 | write_int out_channel length >>= fun () -> 23 | write out_channel s >>= fun () -> 24 | flush out_channel 25 | ) 26 | 27 | method receive_reply = Lwt_io.( 28 | Lwt_io.read_char in_channel >>= fun chr -> 29 | let reply_length = Char.code chr in 30 | let buffer = Bytes.create reply_length in 31 | read_into_exactly in_channel buffer 0 reply_length >>= fun () -> 32 | Lwt_io.flush_all () >>= fun () -> 33 | Lwt.return buffer 34 | ) 35 | 36 | end 37 | 38 | let () = 39 | let (parent_sock, gui_sock) = 40 | Lwt_unix.socketpair Lwt_unix.PF_UNIX Lwt_unix.SOCK_STREAM 0 41 | in 42 | ( 43 | Lwt_io.flush_all () >>= fun () -> 44 | let pid = Lwt_unix.fork () in 45 | (* Cocoa can only run in the master process *) 46 | match pid with 47 | (* Parent process *) 48 | | pid when pid > 0 -> 49 | init_ide (Obj.magic (Lwt_unix.unix_file_descr gui_sock)) 50 | |> Lwt.return 51 | | pid when pid = 0 -> 52 | (* Child needs to tell parent its PID to properly close child, 53 | give NSApplication a chance to properly setup *) 54 | Lwt_unix.sleep 1.0 >>= fun () -> 55 | let hayots = new hayots parent_sock in 56 | `Assoc [("child-pid", `Int (Unix.getpid ()))] 57 | |> Yojson.Basic.to_string 58 | |> hayots#send_message >>= fun () -> 59 | hayots#receive_reply >>= fun reply -> 60 | Lwt_io.printlf "Reply: %s" reply >>= fun () -> 61 | Lwt_io.flush_all () >>= fun () -> 62 | 63 | let utop = new Service.utop in 64 | 65 | utop#read_all >>= fun () -> 66 | Lwt_io.printl "After utop" >>= fun () -> 67 | 68 | forever () 69 | | _ -> assert false 70 | ) 71 | 72 | |> Lwt_main.run 73 | -------------------------------------------------------------------------------- /setup.ml: -------------------------------------------------------------------------------- 1 | (* setup.ml generated for the first time by OASIS v0.4.8 *) 2 | 3 | (* OASIS_START *) 4 | (* DO NOT EDIT (digest: a426e2d026defb34183b787d31fbdcff) *) 5 | (******************************************************************************) 6 | (* OASIS: architecture for building OCaml libraries and applications *) 7 | (* *) 8 | (* Copyright (C) 2011-2016, Sylvain Le Gall *) 9 | (* Copyright (C) 2008-2011, OCamlCore SARL *) 10 | (* *) 11 | (* This library is free software; you can redistribute it and/or modify it *) 12 | (* under the terms of the GNU Lesser General Public License as published by *) 13 | (* the Free Software Foundation; either version 2.1 of the License, or (at *) 14 | (* your option) any later version, with the OCaml static compilation *) 15 | (* exception. *) 16 | (* *) 17 | (* This library is distributed in the hope that it will be useful, but *) 18 | (* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *) 19 | (* or FITNESS FOR A PARTICULAR PURPOSE. See the file COPYING for more *) 20 | (* details. *) 21 | (* *) 22 | (* You should have received a copy of the GNU Lesser General Public License *) 23 | (* along with this library; if not, write to the Free Software Foundation, *) 24 | (* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *) 25 | (******************************************************************************) 26 | 27 | let () = 28 | try 29 | Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH") 30 | with Not_found -> () 31 | ;; 32 | #use "topfind";; 33 | #require "oasis.dynrun";; 34 | open OASISDynRun;; 35 | 36 | let setup_t = BaseCompat.Compat_0_4.adapt_setup_t setup_t 37 | open BaseCompat.Compat_0_4 38 | (* OASIS_STOP *) 39 | 40 | let _ = 41 | BaseEnv.var_define 42 | "PWD" 43 | (fun () -> Unix.getcwd ()) 44 | () 45 | 46 | let current = Unix.getcwd () 47 | 48 | (* Get the Code Editor *) 49 | let _ = Sys.( 50 | if not (Sys.file_exists (current ^ "/" ^ "MGSFragaria.framework")) 51 | then begin 52 | ignore ( 53 | command "git clone https://github.com/mugginsoft/Fragaria"; 54 | command "xcodebuild -project Fragaria/Fragaria.xcodeproj \ 55 | -alltargets -configuration Release"; 56 | command ("cp -R Fragaria/build/Release/MGSFragaria.framework " ^ current); 57 | command "rm -rf Fragaria" 58 | ) 59 | end 60 | ) 61 | 62 | (* Get the msgpack objc lib *) 63 | let _ = Sys.( 64 | if not (Sys.file_exists (current ^ "/" ^ "msgpack-objectivec")) 65 | then begin 66 | ignore ( 67 | command "git clone https://github.com/fxfactorial/msgpack-objectivec"; 68 | command "make -C msgpack-objectivec objc_library"; 69 | command "cp msgpack-objectivec/libmsgpack-objc.dylib ."; 70 | command "make -C msgpack-objectivec clean"; 71 | ) 72 | end 73 | ) 74 | 75 | 76 | let () = setup ();; 77 | -------------------------------------------------------------------------------- /src/hayots_app_dispatcher.c: -------------------------------------------------------------------------------- 1 | // -*- objc -*- 2 | 3 | #import 4 | 5 | #import 6 | 7 | #import "MessagePack.h" 8 | 9 | #import "hayots_app_dispatcher.h" 10 | 11 | @implementation Dispatcher 12 | 13 | -(instancetype)initWithReader:(NSInputStream*)r writer:(NSOutputStream*)w 14 | { 15 | if (self = [super init]) { 16 | self.reader = r; 17 | self.writer = w; 18 | self.reader.delegate = self; 19 | self.writer.delegate = self; 20 | [self.reader scheduleInRunLoop:[NSRunLoop mainRunLoop] 21 | forMode:NSDefaultRunLoopMode]; 22 | [self.reader open]; 23 | [self.writer open]; 24 | 25 | auto queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 26 | 27 | dispatch_async(queue, ^{ 28 | auto *pipe = [NSPipe pipe]; 29 | NSTask *t = [[NSTask alloc] init]; 30 | [t setLaunchPath: @"/bin/bash"]; 31 | [t setStandardOutput:pipe]; 32 | [t setArguments:@[@"-c", @"nvim --api-info"]]; 33 | [t launch]; 34 | auto *file = [pipe fileHandleForReading]; 35 | NSData *data = [file readDataToEndOfFile]; 36 | NSDictionary *d = [data messagePackParse]; 37 | 38 | NSError *error = nil; 39 | NSData *pretty = 40 | [NSJSONSerialization 41 | dataWithJSONObject:d 42 | options:NSJSONWritingPrettyPrinted 43 | error:&error]; 44 | 45 | // Do a separate loop for communicating with nvim 46 | NSTask *nvim = [NSTask new]; 47 | NSPipe *nvim_pipe_out = [NSPipe pipe]; 48 | NSPipe *nvim_pipe_in = [NSPipe pipe]; 49 | [nvim setLaunchPath: @"/bin/bash"]; 50 | [nvim setStandardOutput:nvim_pipe_out]; 51 | [nvim setStandardInput:nvim_pipe_in]; 52 | 53 | [nvim setArguments:@[@"-c", @"nvim --embed"]]; 54 | [nvim launch]; 55 | 56 | auto *nvim_read_handle = [nvim_pipe_out fileHandleForReading]; 57 | NSFileHandle *nvim_write_handle = [nvim_pipe_in fileHandleForWriting]; 58 | uint32_t msgid = 0; 59 | NSData *message = [@[@(0), @(msgid), @"nvim_ui_attach", @[@(400), @(600)]] 60 | messagePack]; 61 | // while (true) { 62 | // [nvim_write_handle writeData:message]; 63 | // NSLog(@"Wrote"); 64 | // NSData *reply = [nvim_read_handle readDataToEndOfFile]; 65 | // NSLog(@"Read back?"); 66 | // NSDictionary *reply_dict = [reply messagePackParse]; 67 | // NSLog(@"Reply:%@", reply_dict); 68 | // break; 69 | // } 70 | 71 | }); 72 | 73 | } 74 | return self; 75 | } 76 | 77 | -(NSDictionary*)read_command 78 | { 79 | NSError *error = nil; 80 | uint8_t read_length[4]; 81 | [self.reader read:read_length maxLength:4]; 82 | int size = (int)(*read_length); 83 | uint8_t buffer[size]; 84 | [self.reader read:buffer maxLength:size]; 85 | NSDictionary *json_command = 86 | [NSJSONSerialization 87 | JSONObjectWithData:[NSData dataWithBytes:buffer length:size] 88 | options:0 89 | error:&error]; 90 | return json_command; 91 | } 92 | 93 | -(void)send_reply:(NSDictionary*)reply_dict 94 | { 95 | // Error is limited to 255 characters 96 | NSError *error = nil; 97 | NSData *d = 98 | [NSJSONSerialization 99 | dataWithJSONObject:reply_dict options:0 error:&error]; 100 | 101 | uint8_t buffer[sizeof(int)]; 102 | *buffer = (int)[d length]; 103 | 104 | [self.writer write:buffer maxLength:1]; 105 | [self.writer write:(const uint8_t * _Nonnull)[d bytes] 106 | maxLength:[d length]]; 107 | } 108 | 109 | -(void)handle_commands 110 | { 111 | NSDictionary *dict = [self read_command]; 112 | NSLog(@"Command dict: %@", dict); 113 | NSNumber *n = [dict objectForKey:@"child-pid"]; 114 | if (n) self.ocaml_child_pid = [n intValue]; 115 | } 116 | 117 | -(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode; 118 | { 119 | switch (eventCode) { 120 | case NSStreamEventHasBytesAvailable: { 121 | [self handle_commands]; 122 | // NSLog(@"As a dict: %@", [self read_command]); 123 | [self send_reply:@{@"Foo":@"bar"}]; 124 | break; 125 | } 126 | case NSStreamEventEndEncountered: 127 | NSLog(@"Client disconnected"); 128 | break; 129 | case NSStreamEventNone: 130 | NSLog(@"What is this none event"); 131 | break; 132 | case NSStreamEventOpenCompleted: 133 | break; 134 | case NSStreamEventErrorOccurred: 135 | NSLog(@"Event error"); 136 | break; 137 | case NSStreamEventHasSpaceAvailable: 138 | NSLog(@"Event has space available"); 139 | break; 140 | } 141 | } 142 | 143 | @end 144 | --------------------------------------------------------------------------------