Пример #1
0
    def run(self):
        # Compile the CabalInspector:
        # TODO: Where to compile it?
        sublime.set_timeout(lambda: sublime.status_message('Compiling Haskell CabalInspector...'), 0)

        exit_code, out, err = call_and_wait(['ghc',
            '--make', CABAL_INSPECTOR_SOURCE_PATH,
            '-o', CABAL_INSPECTOR_EXE_PATH,
            '-outputdir', CABAL_INSPECTOR_OBJ_DIR])

        if exit_code != 0:
            error_msg = u"SublimeHaskell: Failed to compile CabalInspector\n{0}".format(err)
            wait_for_window(lambda w: self.show_errors(w, error_msg))
        else:
            sublime.set_timeout(lambda: sublime.status_message('Compiling Haskell CabalInspector' + u" \u2714"), 0)
        # Continue anyway

        # Compile the ModuleInspector:
        sublime.set_timeout(lambda: sublime.status_message('Compiling Haskell ModuleInspector...'), 0)

        exit_code, out, err = call_and_wait(['ghc',
            '--make', MODULE_INSPECTOR_SOURCE_PATH,
            '-o', MODULE_INSPECTOR_EXE_PATH,
            '-outputdir', MODULE_INSPECTOR_OBJ_DIR])

        if exit_code != 0:
            error_msg = u"SublimeHaskell: Failed to compile ModuleInspector\n{0}".format(err)
            wait_for_window(lambda w: self.show_errors(w, error_msg))
            return

        sublime.set_timeout(lambda: sublime.status_message('Compiling Haskell ModuleInspector' + u" \u2714"), 0)

        # For first time, inspect all open folders and files
        wait_for_window(lambda w: self.mark_all_files(w))

        # TODO: If compilation failed, we can't proceed; handle this.
        # Periodically wake up and see if there is anything to inspect.
        while True:
            files_to_reinspect = []
            with self.dirty_files_lock:
                files_to_reinspect = self.dirty_files
                self.dirty_files = []
            # Find the cabal project corresponding to each "dirty" file:
            cabal_dirs = []
            standalone_files = []
            for filename in files_to_reinspect:
                d = get_cabal_project_dir_of_file(filename)
                if d is not None:
                    cabal_dirs.append(d)
                else:
                    standalone_files.append(filename)
            # Eliminate duplicate project directories:
            cabal_dirs = list(set(cabal_dirs))
            standalone_files = list(set(standalone_files))
            for d in cabal_dirs:
                self._refresh_all_module_info(d)
            for f in standalone_files:
                self._refresh_module_info(f)
            self.reinspect_event.wait(AGENT_SLEEP_TIMEOUT)
            self.reinspect_event.clear()
Пример #2
0
 def _refresh_module_info(self, filename):
     "Rebuild module information for the specified file."
     # TODO: Only do this within Haskell files in Cabal projects.
     # TODO: Skip this file if it hasn't changed since it was last inspected.
     # TODO: Currently the ModuleInspector only delivers top-level functions
     #       with hand-written type signatures. This code should make that clear.
     # If the file hasn't changed since it was last inspected, do nothing:
     modification_time = os.stat(filename).st_mtime
     if CHECK_MTIME:
         inspection_time = self._get_inspection_time_of_file(filename)
         if modification_time <= inspection_time:
             return
     exit_code, stdout, stderr = call_and_wait(
         [MODULE_INSPECTOR_EXE_PATH, filename])
     if exit_code == 0:
         new_info = json.loads(stdout)
     else:
         # There was a problem parsing the file; create an error entry.
         new_info = {'error': 'ModuleInspector failed'}
     # Remember when this info was collected.
     new_info['inspectedAt'] = modification_time
     # Dump the currently-known module info to disk:
     formatted_json = json.dumps(self.info, indent=2)
     with open(OUTPUT_PATH, 'w') as f:
         f.write(formatted_json)
     with self.info_lock:
         self.info[filename] = new_info
Пример #3
0
 def run(self):
     # Compile the ModuleInspector:
     sublime.status_message('Compiling Haskell ModuleInspector...')
     exit_code, out, err = call_and_wait(['ghc',
         '--make', MODULE_INSPECTOR_SOURCE_PATH,
         '-o', MODULE_INSPECTOR_EXE_PATH,
         '-outputdir', MODULE_INSPECTOR_OBJ_DIR])
     # TODO: If compilation failed, we can't proceed; handle this.
     # Periodically wake up and see if there is anything to inspect.
     while True:
         files_to_reinspect = []
         with self.dirty_files_lock:
             files_to_reinspect = self.dirty_files
             self.dirty_files = []
         # Find the cabal project corresponding to each "dirty" file:
         cabal_dirs = []
         for filename in files_to_reinspect:
             d = get_cabal_project_dir_of_file(filename)
             if d is not None:
                 cabal_dirs.append(d)
         # Eliminate duplicate project directories:
         cabal_dirs = list(set(cabal_dirs))
         for d in cabal_dirs:
             self._refresh_all_module_info(d)
         time.sleep(AGENT_SLEEP_DURATION)
Пример #4
0
def wait_for_build_to_complete(view, cabal_project_dir):
    """Start 'cabal build', wait for it to complete, then parse and diplay
    the resulting errors."""

    # First hide error panel to show that something is going on
    sublime.set_timeout(lambda: hide_output(view), 0)

    exit_code, stdout, stderr = call_and_wait(
        ['cabal', 'build'],
        cwd=cabal_project_dir)

    # stderr/stdout can contain unicode characters
    stdout = stderr.decode('utf-8')
    stderr = stderr.decode('utf-8')

    success = exit_code == 0

    # The process has terminated; parse and display the output:
    parsed_messages = parse_error_messages(cabal_project_dir, stderr)
    if parsed_messages:
        error_messages = u'\n'.join(unicode(x) for x in parsed_messages)
    else:
        error_messages = stderr
    success_message = 'SUCCEEDED' if success else 'FAILED'
    output = u'{0}\n\nBuild {1}'.format(error_messages, success_message)
    # Use set_timeout() so that the call occurs on the main Sublime thread:
    mark_errors_cb = lambda: mark_errors_in_views(parsed_messages)
    sublime.set_timeout(mark_errors_cb, 0)

    # TODO make this an option
    if success:
        sublime.status_message(u"Rebuilding Haskell \u2714")
    else:
        write_output_cb = lambda: write_output(view, output, cabal_project_dir)
        sublime.set_timeout(write_output_cb, 0)
Пример #5
0
def do_build(view, cabal_project_dir):
      exit_code, stdout, stderr = call_and_wait(['cabal', 'build'],cwd=cabal_project_dir)

      # stderr/stdout can contain unicode characters
      stdout = stderr.decode('utf-8')
      stderr = stderr.decode('utf-8')

      success = exit_code == 0

      # The process has terminated; parse and display the output:
      parsed_messages = parse_error_messages(cabal_project_dir, stderr)
      if parsed_messages:
          error_messages = u'\n'.join(unicode(x) for x in parsed_messages)
      else:
          error_messages = stderr
      success_message = 'SUCCEEDED' if success else 'FAILED'
      output = u'{0}\n\nBuild {1}'.format(error_messages, success_message)
      # Use set_timeout() so that the call occurs on the main Sublime thread:
      callback = functools.partial(mark_errors_in_views, parsed_messages)
      sublime.set_timeout(callback, 0)

      # TODO make this an option
      if success:
          sublime.status_message("Rebuilding Haskell successful")
      else:
          callback = functools.partial(write_output, view, output, cabal_project_dir)
          sublime.set_timeout(callback, 0)
Пример #6
0
def wait_for_build_to_complete(add_to_path, view, cabal_project_dir):
    """Start 'cabal build', wait for it to complete, then parse and diplay
    the resulting errors."""

    # First hide error panel to show that something is going on
    sublime.set_timeout(lambda: hide_output(view), 0)
    sublime.set_timeout(lambda: sublime.status_message("Rebuilding Haskell with Cabal"), 0)
    exit_code, stdout, stderr = call_and_wait(add_to_path, ["cabal", "build"], cwd=cabal_project_dir)

    # stderr/stdout can contain unicode characters
    stdout = stderr.decode("utf-8")
    stderr = stderr.decode("utf-8")

    success = exit_code == 0

    # The process has terminated; parse and display the output:
    parsed_messages = parse_error_messages(cabal_project_dir, stderr)
    if parsed_messages:
        error_messages = u"\n".join(unicode(x) for x in parsed_messages)
    else:
        error_messages = stderr
    success_message = "SUCCEEDED" if success else "FAILED"
    output = u"{0}\n\nBuild {1}".format(error_messages, success_message)
    # Use set_timeout() so that the call occurs on the main Sublime thread:
    callback = functools.partial(mark_errors_in_views, parsed_messages)
    sublime.set_timeout(callback, 0)

    # TODO make this an option
    if success:
        sublime.set_timeout(lambda: sublime.status_message("Rebuilding Haskell successful"), 0)
    else:
        callback = functools.partial(write_output, view, output, cabal_project_dir)
        sublime.set_timeout(callback, 0)
Пример #7
0
def run_binary(name, bin_file, base_dir):
    exit_code, out, err = call_and_wait(bin_file, cwd=base_dir)
    window = sublime.active_window()
    if not window:
        return
    if exit_code == 0:
        sublime.set_timeout(lambda: sublime.status_message('SublimeHaskell: Running ' + name + u" \u2714"), 0)
        sublime.set_timeout(lambda: write_output(window, out, base_dir), 0)
    else:
        sublime.set_timeout(lambda: sublime.status_message('SublimeHaskell: Running ' + name + u" \u2717"), 0)
        sublime.set_timeout(lambda: write_output(window, err, base_dir), 0)
Пример #8
0
    def _refresh_project_info(self, cabal_dir, project_name, cabal_file):
        exit_code, out, err = call_and_wait(
            [CABAL_INSPECTOR_EXE_PATH, cabal_file])

        if exit_code == 0:
            new_info = json.loads(out)

            if 'error' not in new_info:
                if 'executables' in new_info:
                    with autocompletion.projects_lock:
                        autocompletion.projects[project_name] = {
                            'dir': cabal_dir,
                            'cabal': os.path.basename(cabal_file),
                            'executables': new_info['executables'] }
Пример #9
0
def wait_for_chain_to_complete(view, cabal_project_dir, msg, cmds):
    """Chains several commands, wait for them to complete, then parse and display
    the resulting errors."""

    # First hide error panel to show that something is going on
    sublime.set_timeout(lambda: hide_output(view), 0)

    # run and wait commands, fail on first fail
    for cmd in cmds:
        exit_code, stdout, stderr = call_and_wait(
            cmd,
            cwd=cabal_project_dir)
        if exit_code != 0:
            break

    parse_output_messages_and_show(view, msg, cabal_project_dir, exit_code, stderr)
Пример #10
0
    def _refresh_module_info(self, filename):
        "Rebuild module information for the specified file."
        # TODO: Only do this within Haskell files in Cabal projects.
        # TODO: Skip this file if it hasn't changed since it was last inspected.
        # TODO: Currently the ModuleInspector only delivers top-level functions
        #       with hand-written type signatures. This code should make that clear.
        # If the file hasn't changed since it was last inspected, do nothing:
        if not filename.endswith('.hs'):
            return

        modification_time = os.stat(filename).st_mtime
        if CHECK_MTIME:
            inspection_time = self._get_inspection_time_of_file(filename)
            if modification_time <= inspection_time:
                return
        exit_code, stdout, stderr = call_and_wait(
            [MODULE_INSPECTOR_EXE_PATH, filename])
        # Update only when module is ok
        if exit_code == 0:
            new_info = json.loads(stdout)

            if 'error' not in new_info:
                # Load standard modules
                if 'imports' in new_info:
                    # Get all module names (filter away imports without module name)
                    module_names = filter(id, [i.get('importName') for i in new_info['imports']])

                    # PRELUDE: Add Prelude to the imports if it is not imported manually
                    if u'Prelude' not in module_names:
                        module_names.insert(0, u'Prelude')

                    for import_name in module_names:
                        self._load_standard_module(import_name)

                # Remember when this info was collected.
                new_info['inspectedAt'] = modification_time
                # Dump the currently-known module info to disk:
                formatted_json = json.dumps(autocompletion.info, indent=2)
                with open(OUTPUT_PATH, 'w') as f:
                    f.write(formatted_json)
                with autocompletion.info_lock:
                    autocompletion.info[filename] = new_info
                autocompletion.module_completions.add(new_info['moduleName'])
Пример #11
0
def wait_for_chain_to_complete(view, cabal_project_dir, msg, cmds, on_done):
    """Chains several commands, wait for them to complete, then parse and display
    the resulting errors."""

    # First hide error panel to show that something is going on
    sublime.set_timeout(lambda: hide_output(view), 0)

    # run and wait commands, fail on first fail
    for cmd in cmds:
        exit_code, stdout, stderr = call_and_wait(cmd, cwd=cabal_project_dir)
        if exit_code != 0:
            break

    errmsg = stderr if stderr else stdout

    # Notify UI thread that commands are done
    sublime.set_timeout(on_done, 0)

    parse_output_messages_and_show(view, msg, cabal_project_dir, exit_code,
                                   errmsg)