def init_ghcmod_completions(self): # Init LANGUAGE completions self.language_completions = call_ghcmod_and_wait(['lang']).split('\n') log("Reading LANGUAGE completions from ghc-mod") # Init import module completion self.module_completions = call_ghcmod_and_wait(['list']).split('\n')
def run(self): while True: name, fn, args, kwargs = self.jobs.get() try: fn(*args, **kwargs) except Exception as e: log('worker: job {0} fails with {1}'.format(name, e), log_debug)
def _refresh_all_module_info(self, cabal_dir): "Rebuild module information for all files under the specified directory." begin_time = time.clock() log('reinspecting project ({0})'.format(cabal_dir)) # Process all files within the Cabal project: # TODO: Only process files within the .cabal file's "src" directory. files_in_dir = list_files_in_dir_recursively(cabal_dir) haskell_source_files = [x for x in files_in_dir if x.endswith('.hs')] for filename in haskell_source_files: self._refresh_module_info(filename) end_time = time.clock() log('total inspection time: {0} seconds'.format(end_time - begin_time))
def mark_errors_in_views(errors): "Mark the regions in open views where errors were found." begin_time = time.clock() # Mark each diagnostic in each open view in all windows: for w in sublime.windows(): for v in w.views(): view_filename = v.file_name() # Unsaved files have no file name if view_filename is None: continue errors_in_view = filter(lambda x: are_paths_equal(view_filename, x.filename), errors) mark_errors_in_this_view(errors_in_view, v) end_time = time.clock() log("total time to mark {0} diagnostics: {1} seconds".format(len(errors), end_time - begin_time))
def wait_ghcmod_and_parse(view, filename, msg, cmds_with_args, alter_messages_cb): sublime.set_timeout(lambda: hide_output(view), 0) parsed_messages = [] file_dir = os.path.dirname(filename) all_cmds_successful = True all_cmds_outputs = [] for (cmd, args) in cmds_with_args: stdout = call_ghcmod_and_wait(args, filename) # stdout contains NULL as line endings within one message # error_output_regex using indents to determine one message scope # Replace NULLs to indents out1 = stdout.replace('\0', '\n ') # Hlint outputs Error instead Warning for tips # so lets replace them out = out1.replace('Error', 'Warning') success = len(out.strip()) == 0 if not success: all_cmds_outputs.append(out) log(u"ghc-mod %s didn't exit with success on '%s'" % (u' '.join(cmd), filename)) all_cmds_successful &= success parsed = parse_output_messages(file_dir, out) for p in parsed: parsed_messages.append((cmd, p)) if alter_messages_cb: alter_messages_cb(parsed_messages) concated_messages = [m[1] for m in parsed_messages] # Set global error list set_global_error_messages(concated_messages) sublime.set_timeout(lambda: mark_messages_in_views(concated_messages), 0) output_text = (format_output_messages(concated_messages) if parsed_messages else '\n'.join(all_cmds_outputs)) exit_code = 0 if all_cmds_successful else 1 show_output_result_text(view, msg, output_text, exit_code, file_dir)
def init_ghcmod_completions(self): if not get_setting('enable_ghc_mod'): return sublime.status_message('SublimeHaskell: Updating ghc_mod completions...') # Init LANGUAGE completions autocompletion.language_completions = call_ghcmod_and_wait(['lang']).splitlines() log("Reading LANGUAGE completions from ghc-mod") # Init import module completion autocompletion.module_completions = call_ghcmod_and_wait(['list']).splitlines() sublime.status_message('SublimeHaskell: Updating ghc_mod completions ' + u" \u2714")
def run_build(view, project_name, project_dir, command, use_cabal_dev=None): global projects_being_built # Don't build if a build is already running for this project # We compare the project_name for simplicity (projects with same # names are of course possible, but unlikely, so we let them wait) if project_name in projects_being_built: print "SublimeHaskell: Not building '%s' because it is already being built" % project_name sublime.status_message('SublimeHaskell: Already building %s' % project_name) return # Set project as building projects_being_built.add(project_name) # Run cabal or cabal-dev if use_cabal_dev is None: use_cabal_dev = get_setting_async('use_cabal_dev') tool = cabal_tool[use_cabal_dev] config = cabal_command[command] # Title of tool: Cabal, Cabal-Dev tool_title = tool['message'] # Title of action: Cleaning, Building, etc. action_title = config['message'] # Extra arguments lambda extra_args = tool['extra'] # Tool name: cabal, cabal-dev tool_name = tool['command'] # Tool arguments (commands): build, clean, etc. tool_steps = config['steps'] # Assemble command lines to run (possibly multiple steps) commands = [extra_args([tool_name] + step) for step in tool_steps] log('running build commands: {0}'.format(commands)) def done_callback(): # Set project as done being built so that it can be built again projects_being_built.remove(project_name) # Run them run_chain_build_thread( view, project_dir, tool_title + ': ' + action_title + ' ' + project_name, commands, on_done=done_callback)
def mark_messages_in_views(errors): "Mark the regions in open views where errors were found." begin_time = time.clock() # Mark each diagnostic in each open view in all windows: for w in sublime.windows(): for v in w.views(): view_filename = v.file_name() # Unsaved files have no file name if view_filename is None: continue errors_in_view = list( filter(lambda x: are_paths_equal(view_filename, x.filename), errors)) mark_messages_in_view(errors_in_view, v) end_time = time.clock() log('total time to mark {0} diagnostics: {1} seconds'.format( len(errors), end_time - begin_time))
def _refresh_all_module_info(self, cabal_dir): "Rebuild module information for all files under the specified directory." begin_time = time.clock() log('reinspecting project ({0})'.format(cabal_dir)) # Process all files within the Cabal project: # TODO: Only process files within the .cabal file's "src" directory. (project_name, cabal_file) = get_cabal_in_dir(cabal_dir) # set project and read cabal if cabal_file and project_name: self._refresh_project_info(cabal_dir, project_name, cabal_file) files_in_dir = list_files_in_dir_recursively(cabal_dir) haskell_source_files = [x for x in files_in_dir if x.endswith('.hs') and ('dist/build/autogen' not in x)] for filename in haskell_source_files: self._refresh_module_info(filename) end_time = time.clock() log('total inspection time: {0} seconds'.format(end_time - begin_time))
def wait_ghcmod_and_parse(view, filename, msg, cmds_with_args, alter_messages_cb): sublime.set_timeout(lambda: hide_output(view), 0) parsed_messages = [] file_dir = os.path.dirname(filename) all_cmds_successful = True all_cmds_outputs = [] for (cmd, args) in cmds_with_args: stdout = call_ghcmod_and_wait(args, filename) # stdout contains NULL as line endings within one message # error_output_regex using indents to determine one message scope # Replace NULLs to indents out = stdout.replace('\0', '\n ') success = len(out.strip()) == 0 if not success: all_cmds_outputs.append(out) log(u"ghc-mod %s didn't exit with success on '%s'" % (u' '.join(cmd), filename)) all_cmds_successful &= success parsed = parse_output_messages(file_dir, out) for p in parsed: parsed_messages.append((cmd, p)) if alter_messages_cb: alter_messages_cb(parsed_messages) concated_messages = [m[1] for m in parsed_messages] # Set global error list set_global_error_messages(concated_messages) sublime.set_timeout(lambda: mark_messages_in_views(concated_messages), 0) output_text = (format_output_messages(concated_messages) if parsed_messages else '\n'.join(all_cmds_outputs)) exit_code = 0 if all_cmds_successful else 1 show_output_result_text(view, msg, output_text, exit_code, file_dir)
def run(self, edit): log("SublimeHaskellNextError") v = self.view fn = v.file_name().encode("utf-8") line, column = v.rowcol(v.sel()[0].a) gotoline = -1 if fn in ERRORS: for errLine in ERRORS[fn]: if errLine > line: gotoline = errLine break # No next line: Wrap around if possible if gotoline == -1 and len(ERRORS[fn]) > 0: gotoline = ERRORS[fn].keys()[0] if gotoline != -1: v.window().open_file("%s:%d" % (fn, gotoline), sublime.ENCODED_POSITION) else: sublime.status_message("No more errors or warnings!")
def run(self, edit): log("SublimeHaskellNextError") v = self.view fn = v.file_name().encode("utf-8") line, column = v.rowcol(v.sel()[0].a) line += 1 gotoline = -1 if fn in ERRORS: for errLine in sorted(ERRORS[fn].keys()): if errLine > line: gotoline = errLine break # No next line: Wrap around if possible if gotoline == -1 and len(ERRORS[fn]) > 0: gotoline = sorted(ERRORS[fn].keys())[0] if gotoline != -1: v.window().open_file("%s:%d" % (fn, gotoline), sublime.ENCODED_POSITION) else: sublime.status_message("No more errors or warnings!")
def on_query_completions(self, view, prefix, locations): begin_time = time.clock() # Only suggest symbols if the current file is part of a Cabal project. # TODO: Only suggest symbols from within this project. cabal_dir = get_cabal_project_dir_of_view(view) if cabal_dir is not None: completions = self.get_special_completions(view, prefix, locations) if not completions: completions = self.inspector.get_completions(view.file_name()) end_time = time.clock() log('time to get completions: {0} seconds'.format(end_time - begin_time)) # Don't put completions with special characters (?, !, ==, etc.) # into completion because that wipes all default Sublime completions: # See http://www.sublimetext.com/forum/viewtopic.php?t=8659 # TODO: work around this return [ c for c in completions if NO_SPECIAL_CHARS_RE.match(c[0]) ] return []
def on_query_completions(self, view, prefix, locations): if not is_haskell_source(view): return [] begin_time = time.clock() # Only suggest symbols if the current file is part of a Cabal project. # TODO: Only suggest symbols from within this project. completions = autocompletion.get_import_completions(view, prefix, locations) if not completions: completions = autocompletion.get_completions(view, prefix, locations) end_time = time.clock() log('time to get completions: {0} seconds'.format(end_time - begin_time)) # Don't put completions with special characters (?, !, ==, etc.) # into completion because that wipes all default Sublime completions: # See http://www.sublimetext.com/forum/viewtopic.php?t=8659 # TODO: work around this comp = [c for c in completions if NO_SPECIAL_CHARS_RE.match(c[0])] if get_setting('inhibit_completions'): return (comp, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) return comp