def do_inspection(self): self.busy = True try: projects = [] files = [] files_contents = {} if not self.cabal_scanned: self.cabal_scanned = True self.inspect_user_db() with self.dirty_files as dirty_files: if Settings.COMPONENT_DEBUG.inspection: print('do_inspection: dirty_files: {0}'.format(dirty_files)) for finspect in dirty_files.keys() or []: projdir = Common.get_cabal_project_dir_of_file(finspect) if projdir is not None: projects.append(projdir) files.append(finspect) projects = list(set(projects)) files = list(set(files)) files_contents = dict((file, content) for file, content in dirty_files.items() if content) dirty_files.clear() cand_cabals = [] with self.cabal_to_load as cabals_to_load: cand_cabals = cabals_to_load del cabals_to_load[:] for cabal in cand_cabals: projdir = Common.get_cabal_project_dir_of_file(cabal) if projdir is not None: projects.append(projdir) files = list(set(files)) projects = list(set(projects)) self.inspect( paths=[], files=list(set(files)), projects=list(set(projects)), contents=files_contents, ) finally: self.busy = False
def on_autofix(self, corrections): output_messages = [ ParseOutput.OutputMessage( m['source']['file'], HsResultParse.parse_region(m['region']).to_zero_based(), m['level'].capitalize() + ': ' + m['note']['message'].replace('\n', '\n '), m['level']) for m in self.messages ] self.corrections = corrections for corr in self.corrections: corr.message_region.to_zero_based() self.corrections_dict = dict( ((os.path.normpath(c.file), c.message_region.start.line, c.message_region.start.column), c) for c in self.corrections) for omsg in output_messages: okey = (os.path.normpath(omsg.filename), omsg.region.start.line, omsg.region.start.column) if okey in self.corrections_dict: omsg.correction = self.corrections_dict[okey] ParseOutput.set_global_error_messages(output_messages) output_text = ParseOutput.format_output_messages(output_messages) if Settings.PLUGIN.show_error_window: sublime.set_timeout(lambda: ParseOutput.write_output(self.view, output_text, Common.get_cabal_project_dir_of_file(self.filename) or \ os.path.dirname(self.filename), panel_display=not self.fly_mode and len(output_messages)), 0) sublime.set_timeout( lambda: ParseOutput.mark_messages_in_views(output_messages), 0)
def run(self): if not Settings.PLUGIN.enable_hsdev: return Settings.PLUGIN.add_change_callback('enable_hsdev', self.on_hsdev_enabled) Settings.PLUGIN.add_change_callback('inspect_modules', self.on_inspect_modules_changed) self.start_hsdev() while True: if Settings.PLUGIN.enable_hsdev and not self.client.ping(): Logging.log('hsdev ping: no pong', Logging.LOG_WARNING) scan_paths = [] with self.dirty_paths as dirty_paths: scan_paths = dirty_paths[:] dirty_paths[:] = [] files_to_reinspect = [] with self.dirty_files as dirty_files: files_to_reinspect = dirty_files[:] dirty_files[:] = [] projects = [] files = [] if len(files_to_reinspect) > 0: projects = [] files = [] for finspect in files_to_reinspect: projdir = Common.get_cabal_project_dir_of_file(finspect) if projdir is not None: projects.append(projdir) else: files.append(finspect) projects = list(set(projects)) files = list(set(files)) self.inspect(paths=scan_paths, projects=projects, files=files) load_cabal = [] with self.cabal_to_load as cabal_to_load: load_cabal = cabal_to_load[:] cabal_to_load[:] = [] for cabal in load_cabal: Worker.run_async('inspect cabal {0}'.format(cabal), self.inspect_cabal, cabal) if files_to_reinspect and Settings.PLUGIN.enable_hdocs: self.client_back.docs(files=files_to_reinspect) self.reinspect_event.wait(HsDevLocalAgent.sleep_timeout) self.reinspect_event.clear()
def mark_response(self, view, msgs, corrections, fly_mode): '''Generate a list of :py:class:`OutputMessage` objects from a backend response. ''' self.clear_error_marks() def to_output_message(msg): filename = msg.get('source', {}).get('file', '<no file/command line/OPTIONS_GHC>') file_view = view.window().find_open_file(filename) if msg.get('region'): region = HsResultParse.parse_region( msg.get('region')).to_zero_based() else: region = Symbols.Region(0) level = msg.get('level', 'uncategorized') caption = level.capitalize() + ': ' + msg.get('note', {}).get( 'message', '') caption.replace('\n', '\n ') return OutputMessage(file_view, filename, region, caption, level) self.messages = [to_output_message(msg) for msg in msgs] self.limit_messages(view) # Hack alert: Region and Position.to_zero_based() return the original object (self) after updating it. # 'and' returns the right hand side, which is never None or a false value. # # Bascially, a Pythonic way to make side effects happen and still get a list of things we want. corrections_dict = dict( ((os.path.normpath(c.file), c.message_region.start.line, c.message_region.start.column), c) for c in [ corr.message_region.to_zero_based() and corr for corr in corrections or [] ]) for omsg in self.messages: okey = (os.path.normpath(omsg.filename), omsg.region.start.line, omsg.region.start.column) if okey in corrections_dict: omsg.correction = corrections_dict[okey] self.error_marks.extend(self.messages) if Settings.PLUGIN.show_error_window: filename = view.file_name() cabal_proj_dir = Common.get_cabal_project_dir_of_file( filename) or os.path.dirname(filename) show_panel = not fly_mode and self.messages output_text = self.format_output_messages() sublime.set_timeout( lambda: self.make_message_panel(view, output_text, cabal_proj_dir, show_panel), 0) sublime.set_timeout(self.update_markers_across_views, 0)
def do_inspection(self): self.busy = True try: scan_paths = [] files_to_reinspect = [] projects = [] files = [] with self.dirty_paths as dirty_paths: if Settings.COMPONENT_DEBUG.inspection: print( 'do_inspection: dirty_paths: {0}'.format(dirty_paths)) scan_paths = dirty_paths[:] del dirty_paths[:] with self.dirty_files as dirty_files: if Settings.COMPONENT_DEBUG.inspection: print( 'do_inspection: dirty_files: {0}'.format(dirty_files)) projects = [] files = [] for finspect in dirty_files.keys() or []: projdir = Common.get_cabal_project_dir_of_file(finspect) if projdir is not None: projects.append(projdir) files.append(finspect) projects = list(set(projects)) files = list(set(files)) file_contents = dict([(file, content) for file, content in dirty_files.items() if content]) dirty_files.clear() self.inspect(scan_paths, projects, files, file_contents) cand_cabals = [] with self.cabal_to_load as cabals_to_load: cand_cabals = cabals_to_load del cabals_to_load[:] for cabal in cand_cabals: Utils.run_async('inspect cabal {0}'.format(cabal), self.inspect_cabal, cabal) if files_to_reinspect and Settings.PLUGIN.enable_hdocs: self.backend.docs(files=files_to_reinspect) finally: self.busy = False
def do_inspection(self): self.busy = True try: scan_paths = [] files_to_reinspect = [] projects = [] files = [] with self.dirty_paths as dirty_paths: if Settings.COMPONENT_DEBUG.inspection: print('do_inspection: dirty_paths: {0}'.format(dirty_paths)) scan_paths = dirty_paths[:] del dirty_paths[:] with self.dirty_files as dirty_files: if Settings.COMPONENT_DEBUG.inspection: print('do_inspection: dirty_files: {0}'.format(dirty_files)) projects = [] files = [] for finspect in dirty_files.keys() or []: projdir = Common.get_cabal_project_dir_of_file(finspect) if projdir is not None: projects.append(projdir) files.append(finspect) projects = list(set(projects)) files = list(set(files)) file_contents = dict([(file, content) for file, content in dirty_files.items() if content]) dirty_files.clear() self.inspect(scan_paths, projects, files, file_contents) cand_cabals = [] with self.cabal_to_load as cabals_to_load: cand_cabals = cabals_to_load del cabals_to_load[:] for cabal in cand_cabals: Utils.run_async('inspect cabal {0}'.format(cabal), self.inspect_cabal, cabal) if files_to_reinspect and Settings.PLUGIN.enable_hdocs: self.backend.docs(files=files_to_reinspect) finally: self.busy = False
def do_inspection(self): self.busy = True try: scan_paths = [] with self.dirty_paths as dirty_paths: scan_paths = dirty_paths[:] dirty_paths[:] = [] files_to_reinspect = [] with self.dirty_files as dirty_files: files_to_reinspect = dirty_files[:] dirty_files[:] = [] projects = [] files = [] if len(files_to_reinspect) > 0: projects = [] files = [] for finspect in files_to_reinspect: projdir = Common.get_cabal_project_dir_of_file(finspect) if projdir is not None: projects.append(projdir) else: files.append(finspect) projects = list(set(projects)) files = list(set(files)) self.inspect(paths=scan_paths, projects=projects, files=files) load_cabal = [] with self.cabal_to_load as cabal_to_load: load_cabal = cabal_to_load[:] cabal_to_load[:] = [] for cabal in load_cabal: Utils.run_async('inspect cabal {0}'.format(cabal), self.inspect_cabal, cabal) if files_to_reinspect and Settings.PLUGIN.enable_hdocs: self.backend.docs(files=files_to_reinspect) finally: self.busy = False
def mark_response(self, view, msgs, corrections, fly_mode): '''Generate a list of :py:class:`OutputMessage` objects from a backend response. ''' self.clear_error_marks() def to_output_message(msg): filename = msg.get('source', {}).get('file', '<no file/command line/OPTIONS_GHC>') file_view = view.window().find_open_file(filename) if msg.get('region'): region = HsResultParse.parse_region(msg.get('region')).to_zero_based() else: region = Symbols.Region(0) level = msg.get('level', 'uncategorized') caption = level.capitalize() + ': ' + msg.get('note', {}).get('message', '') caption.replace('\n', '\n ') return OutputMessage(file_view, filename, region, caption, level) self.messages = [to_output_message(msg) for msg in msgs] self.limit_messages(view) # Hack alert: Region and Position.to_zero_based() return the original object (self) after updating it. # 'and' returns the right hand side, which is never None or a false value. # # Bascially, a Pythonic way to make side effects happen and still get a list of things we want. corrections_dict = dict(((os.path.normpath(c.file), c.message_region.start.line, c.message_region.start.column), c) for c in [corr.message_region.to_zero_based() and corr for corr in corrections or []]) for omsg in self.messages: okey = (os.path.normpath(omsg.filename), omsg.region.start.line, omsg.region.start.column) if okey in corrections_dict: omsg.correction = corrections_dict[okey] self.error_marks.extend(self.messages) if Settings.PLUGIN.show_error_window: filename = view.file_name() cabal_proj_dir = Common.get_cabal_project_dir_of_file(filename) or os.path.dirname(filename) show_panel = not fly_mode and self.messages output_text = self.format_output_messages() sublime.set_timeout(functools.partial(self.make_message_panel, view, output_text, cabal_proj_dir, show_panel), 0) sublime.set_timeout(self.update_markers_across_views, 0)
def call_ghcmod_and_wait(arg_list, filename=None, cabal=None): """ Calls ghc-mod with the given arguments. Shows a sublime error message if ghc-mod is not available. """ ghc_opts_args = get_ghc_opts_args(filename, add_package_db=False, cabal=cabal) try: command = ['ghc-mod'] + ghc_opts_args + arg_list # Logging.log('running ghc-mod: {0}'.format(command)) # Set cwd to user directory # Otherwise ghc-mod will fail with 'cannot satisfy package...' # Seems, that user directory works well # Current source directory is set with -i argument in get_ghc_opts_args # # When cabal project is available current directory is set to the project root # to avoid troubles with possible template haskell openFile calls ghc_mod_current_dir = ProcHelper.get_source_dir(filename) if filename: cabal_project_dir = Common.get_cabal_project_dir_of_file(filename) if cabal_project_dir: ghc_mod_current_dir = cabal_project_dir exit_code, out, err = ProcHelper.ProcHelper.run_process(command, cwd=ghc_mod_current_dir) if exit_code != 0: raise Exception("%s exited with status %d and stderr: %s" % (' '.join(command), exit_code, err)) # return crlf2lf(out) return out except OSError as os_exc: if os_exc.errno == errno.ENOENT: Common.output_error_async(sublime.active_window(), "SublimeHaskell: ghc-mod was not found!\n" "It is used for LANGUAGE and import autocompletions and type inference.\n" "Try adjusting the 'add_to_PATH' setting.\n" "You can also turn this off using the 'enable_ghc_mod' setting.") # Re-raise so that calling code doesn't try to work on the `None` return value raise os_exc
def on_autofix(self, corrections): # Oh, this looks pretty ugly. But, list comprehensions are supposedly faster than loops. And since this is # is supporting Haskell, why not use the functional approach? :-) output_messages = [ParseOutput.OutputMessage(msg.get('source', {}).get('file', '<no file/command line/OPTIONS_GHC>'), HsResultParse.parse_region(msg.get('region')).to_zero_based() \ if msg.get('region') is not None else Symbols.Region(0), msg.get('level', 'uncategorized').capitalize() + ': ' + \ msg.get('note', {}).get('message', '').replace('\n', '\n '), msg.get('level', 'uncategorized')) for msg in self.msgs] # Hack alert: Region and Position.to_zero_based() return the original object (self) after updating it. 'and' returns the # right hand side, which is never None or a false value. self.corrections_dict = dict( ((os.path.normpath(c.file), c.message_region.start.line, c.message_region.start.column), c) for c in [ corr.message_region.to_zero_based() and corr for corr in corrections or [] ]) for omsg in output_messages: okey = (os.path.normpath(omsg.filename), omsg.region.start.line, omsg.region.start.column) if okey in self.corrections_dict: omsg.correction = self.corrections_dict[okey] ParseOutput.set_global_error_messages(output_messages) output_text = ParseOutput.format_output_messages(output_messages) if Settings.PLUGIN.show_error_window: cabal_proj_dir = Common.get_cabal_project_dir_of_file( self.filename) or os.path.dirname(self.filename) panel_display = not self.fly_mode and output_messages sublime.set_timeout( lambda: ParseOutput.write_output( self.view, output_text, cabal_proj_dir, panel_display), 0) sublime.set_timeout( lambda: ParseOutput.mark_messages_in_views(output_messages), 0)