def run(self): self.view = self.window.active_view() if not self.view: Common.show_status_message("No file active", False) else: project_dir, project_name = Common.locate_cabal_project_from_view( self.view) if not project_dir: Common.show_status_message("Not in project", False) ## FIXME: proj_info = BackendManager.active_backend().project(project_name) self.project_name = project_name self.project_dir = project_dir self.names = ['lib:{0}'.format(project_name)] if proj_info: self.names.extend([ 'exe:{0}'.format(executable['name']) for executable in proj_info['description']['executables'] ]) self.names.extend([ 'test:{0}'.format(test['name']) for test in proj_info['description']['tests'] ]) if len(self.names) > 1: self.window.show_quick_panel(self.names, self.on_done) else: self.on_done(0)
def select_project(self, on_selected, filter_project): projs = [(name, info) for (name, info) in self.get_projects().items() if not filter_project or filter_project(name, info)] def run_selected(psel): on_selected(psel[0], psel[1]['path']) if not projs: Common.show_status_message("No active projects found.", is_ok=False, priority=5) elif len(projs) == 1: # There's only one project, build it run_selected(projs[0]) else: _, cabal_project_name = Common.locate_cabal_project_from_view( self.window.active_view()) Logging.log('Current project: {0}'.format(cabal_project_name)) # Sort by name projs.sort(key=lambda p: p[0]) current_project_idx = next( (i for i, p in enumerate(projs) if p[0] == cabal_project_name), -1) def on_done(idx): if idx != -1: run_selected(projs[idx]) self.window.show_quick_panel( list(map(lambda m: [m[0], m[1]['path']], projs)), on_done, 0, current_project_idx)
def auto_build(self): current_project_dir, current_project_name = Common.locate_cabal_project_from_view(self.view) if current_project_name and current_project_dir: build_mode = Settings.get_project_setting(self.view.window().active_view(), 'auto_build_mode', Settings.PLUGIN.auto_build_mode) build_command = self.MODE_BUILD_COMMAND.get(build_mode) if not build_command: Common.output_error(self.view.window(), "SublimeHaskell: invalid auto_build_mode '%s'" % build_mode) return # Duplicate the dictionary corresponding to the build command. We might modify it later. config = dict(self.BUILD_TOOL_CONFIG[build_command]) addl_config = None if build_mode.endswith('-then-tests'): has_tests = False projects = self.get_projects() if current_project_name in projects and 'description' in projects[current_project_name]: has_tests = projects[current_project_name]['description'].get('tests') is not None if has_tests: addl_config = 'test' elif build_mode.endswith('-then-bench'): addl_config = 'bench' if addl_config is not None: for tool, steps in self.BUILD_TOOL_CONFIG[addl_config]['steps'].items(): config['steps'][tool].extend(steps) Logging.log('auto build: final config:\n{0}'.format(pprint.pformat(config))) self.run_build(current_project_name, current_project_dir, config)
def select_project(self, on_selected, filter_project): '''Select a project from a generated project list. Execution flow continues into the :py:function:`on_selected` function with the project's name and the project's base directory. The :py:function:`filter_project` filters projects before they are shown (see :py:method:`get_projects`.) ''' projs = [(name, info) for (name, info) in self.get_projects().items() if not filter_project or filter_project(name, info)] def run_selected(psel): on_selected(psel[0], psel[1]['path']) if not projs: Common.sublime_status_message("No active projects found.") elif len(projs) == 1: # There's only one project, build it run_selected(projs[0]) else: _, cabal_project_name = Common.locate_cabal_project_from_view(self.view) Logging.log('Current project: {0}'.format(cabal_project_name)) # Sort by name projs.sort(key=lambda p: p[0]) current_project_idx = next((i for i, p in enumerate(projs) if p[0] == cabal_project_name), -1) def on_done(idx): if idx != -1: run_selected(projs[idx]) self.view.window().show_quick_panel([[m[0], m[1].get('path', '??')] for m in projs], on_done, 0, current_project_idx)
def run(self, **_args): self.executables = [] projs = [] builder = Builder(self.window.active_view()) projects = builder.get_projects() for proj, info in projects.items(): if 'description' in info: for exes in info['description']['executables']: projs.append((proj + ': ' + exes['name'], {'dir': info['path'], 'name': exes['name']})) print('SublimeHaskellRunCommand: projects {0}'.format(projects)) if not projs: Common.sublime_status_message('No project or nothing to run') elif len(projs) == 1: # One project proj_info = projs[0][1] self.exec_name = proj_info['name'] self.exec_base_dir = proj_info['dir'] self.prompt_prog_args() else: # Multiple choices _, cabal_project_name = Common.locate_cabal_project_from_view(self.window.active_view()) # Show current project first projs.sort(key=lambda s: (not s[0].startswith(cabal_project_name + ': '), s[0])) self.executables = [p[1] for p in projs] self.window.show_quick_panel([p[0] for p in projs], self.on_project_selected)
def activated_worker(self, view, filename): with self.backend_mgr: EventCommon.assoc_to_project(view, self.backend_mgr, filename) _, project_name = Common.locate_cabal_project_from_view(view) if Common.view_is_haskell_source(view): self.autocompleter.generate_completions_cache( project_name, filename)
def run(self, **_args): self.executables = [] projs = [] builder = Builder(self.window.active_view()) projects = builder.get_projects() for proj, info in projects.items(): if 'description' in info: for exes in info['description']['executables']: projs.append((proj + ': ' + exes['name'], {'dir': info['path'], 'name': exes['name']})) print('SublimeHaskellRunCommand: projects {0}'.format(projects)) if not projs: Common.sublime_status_message('No project or nothing to run') elif len(projs) == 1: # One project proj_info = projs[0][1] self.exec_name = proj_info['name'] self.exec_base_dir = proj_info['dir'] self.prompt_prog_args() else: # Multiple choices _, cabal_project_name = Common.locate_cabal_project_from_view(self.window.active_view()) # Show current project first projs.sort(key=lambda s: (not s[0].startswith(cabal_project_name + ': '), s[0])) self.executables = [p[1] for p in projs] self.window.show_quick_panel([p[0] for p in projs], self.on_project_selected)
def on_post_save(self, view): if not Common.view_is_inspected_source(view): return if Settings.COMPONENT_DEBUG.event_viewer: print('{0}.on_post_save invoked.'.format(type(self).__name__)) filename = view.file_name() if not filename: if Settings.COMPONENT_DEBUG.event_viewer: print('{0}.on_post_save: no file name.'.format( type(self).__name__)) return _project_dir, project_name = Common.locate_cabal_project_from_view( view) if Common.view_is_haskell_source(view): self.type_cache.remove(filename) if Settings.PLUGIN.enable_auto_build: view.window().run_command('sublime_haskell_build_auto') else: EventCommon.do_check_lint( view, continue_success=self.post_successful_check) Utils.run_async('rescan source {0}/{1}'.format(project_name, filename), self.rescan_source, project_name, filename, False)
def scan_resp(_resp): status_msg.result_ok() _project_dir, project_name = Common.locate_cabal_project_from_view( self.view) self.autocompleter.drop_completions_async(current_file_name) self.autocompleter.generate_completions_cache( project_name, current_file_name, contents=view_contents)
def context_completion(self, view, key, _operator, _operand, _matchall): # Completion context is the only branch here where a backend is needed. retval = False with self.backend_mgr: project_dir, project_name = Common.locate_cabal_project_from_view( view) region = view.sel()[0] if region.a == region.b: word_region = view.word(region) preline = Common.get_line_contents_before_region( view, word_region) preline += self.COMPLETION_CHARS[key] qsymbol = Common.get_qualified_symbol(preline) if qsymbol.module: mod_completions = self.autocompleter.get_current_module_completions( project_name, project_dir) if qsymbol.is_import_list: retval = qsymbol.module in mod_completions else: retval = [ m for m in mod_completions if m.startswith(qsymbol.module) ] != [] return retval
def run(self): self.executables = [] projs = [] projects = self.get_projects() for proj, info in projects.items(): if 'description' in info: for exes in info['description']['executables']: projs.append((proj + ": " + exes['name'], { 'dir': info['path'], 'dist': project_dist_path(info['path']), 'name': exes['name'] })) # Nothing to run if not projs: _, cabal_project_name = Common.locate_cabal_project_from_view( self.window.active_view()) # Show current project first projs.sort( key=lambda s: (not s[0].startswith(cabal_project_name), s[0])) self.executables = list(map(lambda m: m[1], projs)) self.window.show_quick_panel(list(map(lambda m: m[0], projs)), self.on_done) else: Common.sublime_status_message('Nothing to run')
def auto_build(self): current_project_dir, current_project_name = Common.locate_cabal_project_from_view(self.view) if current_project_name and current_project_dir: build_mode = Settings.get_project_setting(self.view.window().active_view(), 'auto_build_mode', Settings.PLUGIN.auto_build_mode) build_command = self.MODE_BUILD_COMMAND.get(build_mode) if not build_command: Common.output_error(self.view.window(), "SublimeHaskell: invalid auto_build_mode '%s'" % build_mode) return # Duplicate the dictionary corresponding to the build command. We might modify it later. config = dict(self.BUILD_TOOL_CONFIG[build_command]) addl_config = None if build_mode.endswith('-then-tests'): has_tests = False projects = self.get_projects() if current_project_name in projects and 'description' in projects[current_project_name]: has_tests = projects[current_project_name]['description'].get('tests') is not None if has_tests: addl_config = 'test' elif build_mode.endswith('-then-bench'): addl_config = 'bench' if addl_config is not None: for tool, steps in self.BUILD_TOOL_CONFIG[addl_config]['steps'].items(): config['steps'][tool].extend(steps) Logging.log('auto build: final config:\n{0}'.format(pprint.pformat(config))) self.run_build(current_project_name, current_project_dir, config)
def scan_resp(_resp): status_msg.result_ok() _project_dir, project_name = Common.locate_cabal_project_from_view( self.view) EventCommon.update_completions_async(self.autocompleter, project_name, files=[current_file_name])
def do_load(self, view): filename = view.file_name() if not Common.view_is_haskell_source(view) or not filename: return if Settings.COMPONENT_DEBUG.event_viewer: print('{0}.on_load {1}.'.format(type(self).__name__, filename)) view_settings = view.settings() or {} if Settings.PLUGIN.use_improved_syntax and (filename.endswith(".hs") or filename.endswith(".hsc") or \ view_settings.get('syntax', '').endswith('.tmLanguage')): view_settings.set( 'syntax', 'Packages/SublimeHaskell/Syntaxes/Haskell-SublimeHaskell.sublime-syntax' ) EventCommon.assoc_to_project(view, self.backend_mgr, filename) _project_dir, project_name = Common.locate_cabal_project_from_view( view) if Settings.PLUGIN.enable_infer_types: BackendManager.active_backend().infer(files=[filename]) Utils.run_async('rescan source {0}/{1}'.format(project_name, filename), self.rescan_source, project_name, filename, {'drop_all': False})
def complete(self, sym, file, wide=False, **backend_args): completions = [] ## Have to do this the "old fashioned" way by looking at the actual file buffer, scraping for the module name, ## scraping for the imports, then collecting the names, which may or may not be visible. for view in filter(None, [w.find_open_file(file) for w in sublime.windows()]): ## Logging.log('ghc-mod complete: view = {0}'.format(view), Logging.LOG_DEBUG) _, project_name = Common.locate_cabal_project_from_view(view) backend = self.project_backends.get(project_name) ## Accumulate the source's module and imports: mod_dict = {} ## Symbols for the 'module' modname_region = view.find(self.MODNAME_RE, 0) if modname_region: modname = self.MODNAME_MATCH.search( view.substr(modname_region)).group(1) if modname: mod_dict[modname] = (False, '') for imp_region in view.find_all(r'^import\s+(qualified\s+)'): imp_text = Common.get_line_contents(view, imp_region) ##print('imp_text \'{0}\''.format(imp_text)) imp = self.IMPORT_MATCH.search(imp_text) import_name = imp.group('import') if imp and import_name and import_name not in mod_dict: mod_dict[import_name] = (imp.group('isqual') is not None, imp.group('qual')) ## Adapted from the ghc-mod ghc-comp.el source: These modules are automagically 'preloaded' along with ## any other modules imported by the primary source module. Note that if the user already imports one of ## these modules explicitly, it won't get added. for preload in [ 'Prelude', 'Control.Applicative', 'Control.Exception', 'Control.Monad', 'Data.Char', 'Data.List', 'Data.Maybe', 'System.IO' ]: if preload not in mod_dict: mod_dict[preload] = (False, None) completion_modules = [(modname, ) + mod_dict[modname] for modname in mod_dict] ## 'sum' will flatten the list-of-lists created by the comprehension... if Settings.COMPONENT_DEBUG.completions: print('completion_modules:\n{0}\nunique-ified\n{1}'.format( pprint.pformat(completion_modules), pprint.pformat(list(set(completion_modules))))) completions = sum([ self.collect_completions(backend, mod, sym.qualified_name()) for mod in list(set(completion_modules)) ], []) return self.dispatch_callbacks(filter(None, completions), None, **backend_args)
def select_project(self, on_selected, filter_project): '''Select a project from a generated project list. Execution flow continues into the :py:function:`on_selected` function with the project's name and the project's base directory. The :py:function:`filter_project` filters projects before they are shown (see :py:method:`get_projects`.) ''' projs = [(name, info) for (name, info) in self.get_projects().items() if not filter_project or filter_project(name, info)] def run_selected(psel): on_selected(psel[0], psel[1]['path']) if not projs: Common.sublime_status_message("No active projects found.") elif len(projs) == 1: # There's only one project, build it run_selected(projs[0]) else: _, cabal_project_name = Common.locate_cabal_project_from_view(self.view) Logging.log('Current project: {0}'.format(cabal_project_name)) # Sort by name projs.sort(key=lambda p: p[0]) current_project_idx = next((i for i, p in enumerate(projs) if p[0] == cabal_project_name), -1) def on_done(idx): if idx != -1: run_selected(projs[idx]) self.view.window().show_quick_panel([[m[0], m[1].get('path', '??')] for m in projs], on_done, 0, current_project_idx)
def activated_worker(): with self.backend_mgr: self.assoc_to_project(view, filename) project_name = Common.locate_cabal_project_from_view(view)[1] if Common.is_haskell_source(view): self.autocompleter.generate_completions_cache( project_name, filename)
def run(self, edit, **kwargs): current_file_name = kwargs.get('filename', self.view.file_name()) project_name = Common.locate_cabal_project_from_view(self.view)[1] backend = BackendManager.active_backend() imp_module = Utils.head_of(backend.module(project_name, file=current_file_name)) if imp_module: imports = sorted(imp_module.imports, key=lambda i: i.position.line) supported, result = backend.clean_imports(current_file_name) print(result) if supported: if len(imports) == len(result): Logging.log('replacing imports for {0}'.format(current_file_name), Logging.LOG_TRACE) erased = 0 for imp, new_imp in zip(imports, result): point = self.view.text_point(imp.position.line - 1 - erased, 0) if new_imp.endswith('()'): self.view.erase(edit, self.view.full_line(point)) erased = erased + 1 else: self.view.replace(edit, self.view.line(point), new_imp) else: Common.sublime_status_message('different number of imports: {0} and {1}'.format(len(imports), len(result))) else: if len(result) == 1: Common.sublime_status_message(result[0]) else: sublime.message_dialog('\n'.join(result)) else: Common.sublime_status_message('Clean Imports failed: module not scanned')
def on_load(self, view): filename = view.file_name() if filename is None or not Common.is_inspected_source(view): return if Settings.COMPONENT_DEBUG.event_viewer: print('{0} is_inspected_source {1}.'.format( type(self).__name__ + ".on_load", filename)) self.assoc_to_project(view, filename) project_name = Common.locate_cabal_project_from_view(view)[1] self.rescan_source(project_name, filename) if Common.is_haskell_source(view): if Settings.COMPONENT_DEBUG.event_viewer: print('{0} is_haskell_source {1}.'.format( type(self).__name__ + ".on_load", filename)) view.settings().set('translate_tabs_to_spaces', True) if Settings.PLUGIN.use_improved_syntax: name = os.path.basename(filename.lower()) if Settings.PLUGIN.use_improved_syntax and ( name.endswith(".hs") or name.endswith(".hsc")): view.settings().set( 'syntax', 'Packages/SublimeHaskell/Syntaxes/Haskell-SublimeHaskell.tmLanguage' )
def run(self, edit, **kwargs): self.filename = kwargs.get('filename', self.view.file_name()) project_name = Common.locate_cabal_project_from_view(self.view)[1] if not SourceHaskellTypeCache().has(self.filename): get_type_view(self.view, project_name) else: self.on_types(SourceHaskellTypeCache().get(self.filename))
def run(self, edit, **kwargs): self.filename = kwargs.get('filename', self.view.file_name()) project_name = Common.locate_cabal_project_from_view(self.view)[1] if not SourceHaskellTypeCache().has(self.filename): get_type_view(self.view, project_name) else: self.on_types(SourceHaskellTypeCache().get(self.filename))
def get_completions(self, view, locations): "Get all the completions related to the current file." current_file_name = view.file_name() if not current_file_name: return [] Logging.log('AutoCompleter.get_completions.', Logging.LOG_DEBUG) self.current_filename = current_file_name project_name = Common.locate_cabal_project_from_view(view)[1] line_contents = Common.get_line_contents(view, locations[0]) qsymbol = Common.get_qualified_symbol(line_contents) qualified_prefix = qsymbol.qualified_name() Logging.log('qsymbol {0}'.format(qsymbol), Logging.LOG_DEBUG) Logging.log('current_file_name {0} qualified_prefix {1}'.format(current_file_name, qualified_prefix), Logging.LOG_DEBUG) view_settings = view.settings() wide = view_settings.get('subhask_wide_completion') suggestions = [] completions = [] if qsymbol.module: if qsymbol.is_import_list: current_module = Utils.head_of(BackendManager.active_backend().module(project_name, current_file_name)) if current_module and current_module.location.project: # Search for declarations of qsymbol.module within current project q_module = Utils.head_of(BackendManager.active_backend().scope_modules(project_name, current_file_name, lookup=qsymbol.module, search_type='exact')) if q_module is not None: if q_module.by_source(): proj_module = BackendManager.active_backend().resolve(file=q_module.location.filename, exports=True) if proj_module: suggestions = proj_module.declarations.values() elif q_module.by_cabal(): cabal_module = Utils.head_of(BackendManager.active_backend().module(project_name, lookup=q_module.name, search_type='exact', package=q_module.location.package.name)) if cabal_module: suggestions = cabal_module.declarations.values() else: suggestions = BackendManager.active_backend().complete(qualified_prefix, current_file_name, wide=wide) completions = make_completions(suggestions) else: with self.cache as cache_: if wide: completions += cache_.global_completions() else: completions += cache_.files.get(current_file_name, cache_.global_completions()) completions += self.keyword_completions(qsymbol.name) sort_completions(completions) return completions
def __init__(self, view, selection=None): self.view = view project_name = Common.locate_cabal_project_from_view(self.view)[1] self.selection = selection if selection is not None else view.sel()[0] if SourceHaskellTypeCache().has(self.view.file_name()): types = sorted_types(self.view, SourceHaskellTypeCache().get(self.view.file_name()), self.selection.b) else: types = get_type_view(self.view, project_name, selection=self.selection) self.regions = [TypedRegion.from_region_type(t, view) for t in types] if types else None self.expanded_index = None
def __init__(self, view, selection=None): self.view = view project_name = Common.locate_cabal_project_from_view(self.view)[1] self.selection = selection if selection is not None else view.sel()[0] if SourceHaskellTypeCache().has(self.view.file_name()): types = sorted_types(self.view, SourceHaskellTypeCache().get(self.view.file_name()), self.selection.b) else: types = get_type_view(self.view, project_name, selection=self.selection) self.regions = [TypedRegion.from_region_type(t, view) for t in types] if types else None self.expanded_index = None
def run(self, **_kwargs): view = self.window.active_view() if not view: Common.sublime_status_message("No file active") else: project_dir = Common.locate_cabal_project_from_view(view)[0] if not project_dir: self.window.run_command("sublime_haskell_repl_ghci_current_file", {}) else: self.window.run_command("sublime_haskell_repl_cabal", {})
def get_type_view(view, project_name, selection=None): filename = view.file_name() project_name = Common.locate_cabal_project_from_view(view)[1] if selection is None: selection = view.sel()[0] line, column = view.rowcol(selection.b) module_name = Utils.head_of(BackendManager.active_backend().module(project_name, file=filename)) return get_type(view, project_name, filename, module_name, line, column)
def get_type_view(view, project_name, selection=None): filename = view.file_name() project_name = Common.locate_cabal_project_from_view(view)[1] if selection is None: selection = view.sel()[0] line, column = view.rowcol(selection.b) module_name = Utils.head_of(BackendManager.active_backend().module(project_name, file=filename)) return get_type(view, project_name, filename, module_name, line, column)
def do_hover(self): if self.hover_zone == sublime.HOVER_TEXT: qsymbol = Common.get_qualified_symbol_at_point(self.view, self.point) ## print('hover: qualified symbol {0}'.format(qsymbol)) module_word = qsymbol.module ident = qsymbol.name if module_word is not None and ident is None: # TODO: Any ideas for popup about module? pass elif ident is not None: whois_name = qsymbol.qualified_name() full_name = qsymbol.full_name() # Try get type of hovered symbol typed_expr = None if types.SourceHaskellTypeCache().has(self.filename): typed_expr = self.get_type(types.SourceHaskellTypeCache().get(self.filename), whois_name) else: project_name = Common.locate_cabal_project_from_view(self.view)[1] point_rgn = sublime.Region(self.point, self.point) typed_expr = self.get_type(types.get_type_view(self.view, project_name, point_rgn), whois_name) # Try whois suggest_import = False decl = Utils.head_of(BackendManager.active_backend().whois(whois_name, self.filename)) if not decl: suggest_import = True decl = Utils.head_of(BackendManager.active_backend().lookup(full_name, self.filename)) self.create_symbol_popup(typed_expr, decl, suggest_import) elif self.hover_zone == sublime.HOVER_GUTTER: errs = [err for err in ParseOutput.MARKER_MANAGER.marks_for_view(self.view) if err.region.start.line == self.line] if errs: popup_parts = [self.STYLES.gen_style(self.view.settings().get('color_scheme'))] for err in errs: msg = UnicodeOpers.use_unicode_operators(symbols.escape_text(err.message)) # Decorate first word with style decors = { 'Error': 'error', 'Warning': 'warning', 'Hint': 'hint' } for dec, dec_style in decors.items(): msg = msg.replace(dec, u'<span class="{0}">{1}</span>'.format(dec_style, dec)) popup_parts.append(u'<p>{0}</p>'.format(msg)) if err.correction is not None: popup_parts.append(err.correction.popup()) popup_text = u''.join(popup_parts) self.shown = True self.view.show_popup(popup_text, sublime.HIDE_ON_MOUSE_MOVE_AWAY, self.point, 600, 600, self.on_navigate, self.on_hide)
def on_new(self, view): filename = view.file_name() if filename is None or not Common.is_inspected_source(view): return if Settings.COMPONENT_DEBUG.event_viewer: print('{0} invoked.'.format(type(self).__name__ + ".on_new")) self.assoc_to_project(view, filename) project_name = Common.locate_cabal_project_from_view(view)[1] self.rescan_source(project_name, filename) view.settings().set('translate_tabs_to_spaces', True)
def do_query_completions(self, view, prefix, locations): # Defer starting the backend until as late as possible... if Settings.COMPONENT_DEBUG.event_viewer or Settings.COMPONENT_DEBUG.completions: print('{0} invoked (prefix: {1}).'.format(type(self).__name__ + '.on_query_completions', prefix)) completions = None with self.backend_mgr: begin_time = time.clock() # Only suggest symbols if the current file is part of a Cabal project. filename = view.file_name() line_contents = Common.get_line_contents(view, locations[0]) project_name = Common.locate_cabal_project_from_view(view)[1] completion_flags = 0 if not Settings.PLUGIN.add_word_completions: completion_flags = completion_flags | sublime.INHIBIT_WORD_COMPLETIONS if not Settings.PLUGIN.add_default_completions: completion_flags = completion_flags | sublime.INHIBIT_EXPLICIT_COMPLETIONS curselector = view.scope_name(locations[0]) if Regexs.LANGUAGE_RE.search(line_contents): completions = self.autocompleter.get_lang_completions(project_name) elif Regexs.OPTIONS_GHC_RE.search(line_contents): completions = self.autocompleter.get_flag_completions(project_name) elif 'meta.import.haskell' in curselector: # Inside an import: Complete the imported module name: completions = self.autocompleter.get_import_completions(project_name, filename, locations, line_contents) elif 'meta.declaration.exports.haskell' in curselector: # Export list export_module = Autocomplete.EXPORT_MODULE_RE.search(line_contents) if export_module: # qsymbol = Common.get_qualified_symbol_at_region(view, view.sel()[0]) # TODO: Implement pass else: # Add current file's completions: completions = self.autocompleter.get_completions(view, locations) end_time = time.clock() if Settings.COMPONENT_DEBUG.event_viewer or Settings.COMPONENT_DEBUG.completions: print('time to get completions: {0} seconds'.format(end_time - begin_time)) print('completion flag: {0}'.format(completion_flags)) # 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].split('\t')[0])] # if Settings.PLUGIN.inhibit_completions and len(comp) != 0: # return (comp, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) # return comp return (completions, completion_flags) # if completions else None
def do_hover(self): if self.hover_zone == sublime.HOVER_TEXT: qsymbol = Common.get_qualified_symbol_at_point(self.view, self.point) ## print('hover: qualified symbol {0}'.format(qsymbol)) module_word = qsymbol.module ident = qsymbol.name if module_word is not None and ident is None: # TODO: Any ideas for popup about module? pass elif ident is not None: whois_name = qsymbol.qualified_name() full_name = qsymbol.full_name() # Try get type of hovered symbol typed_expr = None if types.SourceHaskellTypeCache().has(self.filename): typed_expr = self.get_type(types.SourceHaskellTypeCache().get(self.filename), whois_name) else: project_name = Common.locate_cabal_project_from_view(self.view)[1] point_rgn = sublime.Region(self.point, self.point) typed_expr = self.get_type(types.get_type_view(self.view, project_name, point_rgn), whois_name) # Try whois suggest_import = False decl = Utils.head_of(BackendManager.active_backend().whois(whois_name, self.filename)) if not decl: suggest_import = True decl = Utils.head_of(BackendManager.active_backend().lookup(full_name, self.filename)) self.create_symbol_popup(typed_expr, decl, suggest_import) elif self.hover_zone == sublime.HOVER_GUTTER: errs = [err for err in ParseOutput.errors_for_view(self.view) if err.region.start.line == self.line] if errs: popup_parts = [self.STYLES.gen_style(self.view.settings().get('color_scheme'))] for err in errs: msg = UnicodeOpers.use_unicode_operators(symbols.escape_text(err.message)) # Decorate first word with style decors = { 'Error': 'error', 'Warning': 'warning', 'Hint': 'hint' } for dec, dec_style in decors.items(): msg = msg.replace(dec, u'<span class="{0}">{1}</span>'.format(dec_style, dec)) popup_parts.append(u'<p>{0}</p>'.format(msg)) if err.correction is not None: popup_parts.append(err.correction.popup()) popup_text = u''.join(popup_parts) self.view.show_popup(popup_text, sublime.HIDE_ON_MOUSE_MOVE_AWAY, self.point, 600, 600, self.on_navigate, self.on_hide)
def run(self, edit, **kwargs): filename = kwargs.get('filename', self.view.file_name()) line = kwargs.get('line') column = kwargs.get('column') if line is None or column is None: line, column = self.view.rowcol(self.view.sel()[0].b) line, column = int(line), int(column) project_name = Common.locate_cabal_project_from_view(self.view)[1] result = self.get_types(project_name, filename, line, column) self.show_types(result)
def do_new(self, view): filename = view.file_name() if not Common.view_is_haskell_source(view) or not filename: return if Settings.COMPONENT_DEBUG.event_viewer: print('{0}.on_new invoked.'.format(type(self).__name__)) EventCommon.assoc_to_project(view, self.backend_mgr, filename) _project_dir, project_name = Common.locate_cabal_project_from_view(view) Utils.run_async('rescan {0}/{1}'.format(project_name, filename), self.rescan_source, project_name, filename, {'drop_all': True}) view.settings().set('translate_tabs_to_spaces', True)
def run(self, edit, **kwargs): filename = kwargs.get('filename', self.view.file_name()) line = kwargs.get('line') column = kwargs.get('column') if line is None or column is None: line, column = self.view.rowcol(self.view.sel()[0].b) line, column = int(line), int(column) project_name = Common.locate_cabal_project_from_view(self.view)[1] result = self.get_types(project_name, filename, line, column) self.show_types(result)
def on_load(self, view): filename = view.file_name() if not Common.view_is_haskell_source(view) or not filename: return if Settings.COMPONENT_DEBUG.event_viewer: print('{0}.on_load {1}.'.format(type(self).__name__, filename)) EventCommon.assoc_to_project(view, self.backend_mgr, filename) _project_dir, project_name = Common.locate_cabal_project_from_view( view) Utils.run_async('rescan source {0}/{1}'.format(project_name, filename), self.rescan_source, project_name, filename, {'drop_all': False})
def run(self, edit, **_kwargs): filename = self.view.file_name() line, column = self.view.rowcol(self.view.sel()[0].b) project_name = Common.locate_cabal_project_from_view(self.view)[1] result = self.get_best_type(self.get_types(project_name, filename, int(line), int(column))) if result: res = result.region(self.view) qsymbol = Common.get_qualified_symbol_at_region(self.view, self.view.word(res.begin())) line_begin = self.view.line(res).begin() prefix = self.view.substr(sublime.Region(line_begin, res.begin())) indent = re.search(r'(?P<indent>\s*)', prefix).group('indent') signature = '{0}{1} :: {2}\n'.format(indent, qsymbol.name, result.typename) self.view.insert(edit, line_begin, signature)
def run(self, edit, **_kwargs): filename = self.view.file_name() line, column = self.view.rowcol(self.view.sel()[0].b) project_name = Common.locate_cabal_project_from_view(self.view)[1] result = self.get_best_type(self.get_types(project_name, filename, int(line), int(column))) if result: res = result.region(self.view) qsymbol = Common.get_qualified_symbol_at_region(self.view, self.view.word(res.begin())) line_begin = self.view.line(res).begin() prefix = self.view.substr(sublime.Region(line_begin, res.begin())) indent = re.search(r'(?P<indent>\s*)', prefix).group('indent') signature = '{0}{1} :: {2}\n'.format(indent, qsymbol.name, result.typename) self.view.insert(edit, line_begin, signature)
def on_query_context(self, view, key, _operator, _operand, _matchall): if Settings.COMPONENT_DEBUG.event_viewer: print('{0} key = {1}.'.format( type(self).__name__ + '.on_query_context', key)) retval = None if key == 'haskell_autofix': retval = view.settings().get('autofix') elif key == 'auto_completion_popup': retval = Settings.PLUGIN.auto_completion_popup elif key == 'haskell_source': retval = Common.is_haskell_source(view) elif key == 'haskell_source_or_repl': retval = Common.is_haskell_source(view) or Common.is_haskell_repl( view) elif key == 'haskell_repl': retval = Common.is_haskell_repl(view) elif key == 'haskell_symbol_info': retval = Common.is_haskell_symbol_info(view) elif key == 'cabal_source': retval = Common.is_cabal_source(view) elif key == 'scanned_source': retval = self.is_scanned_source(view) elif key == 'in_project': retval = self.is_in_project(view) elif key == "is_module_completion" or key == "is_import_completion": # Completion context is the only branch here where a backend is needed. retval = False with self.backend_mgr: project_dir, project_name = Common.locate_cabal_project_from_view( view) region = view.sel()[0] if region.a == region.b: word_region = view.word(region) preline = Common.get_line_contents_before_region( view, word_region) preline += self.COMPLETION_CHARS[key] qsymbol = Common.get_qualified_symbol(preline) if qsymbol.module: mod_completions = self.autocompleter.get_current_module_completions( project_name, project_dir) if qsymbol.is_import_list: retval = qsymbol.module in mod_completions else: retval = [ m for m in mod_completions if m.startswith(qsymbol.module) ] != [] return retval
def run(self, **_kwargs): self.view = self.window.active_view() if self.view: self.targets = [] self.args = {} project_dir, project_name = Common.locate_cabal_project_from_view( self.view) Logging.log( 'repl: project_dir {0} project_name {1}'.format( project_dir, project_name), Logging.LOG_DEBUG) if project_dir: proj_info = BackendManager.active_backend().project( project_name) self.project_name = project_name self.project_dir = project_dir if proj_info: descrip = proj_info.get('description', {}) if descrip.get('library'): target = '{0} library'.format(project_name) self.targets.append(target) self.args[target] = (project_name, 'lib', '') for exe in descrip.get('executables', []): target = '{0} ({1} executable)'.format( exe['name'], project_name) self.targets.append(target) self.args[target] = (project_name, 'exe', exe['name']) for test in descrip.get('tests', []): target = '{0} ({1} test)'.format( test['name'], project_name) self.targets.append(target) self.args[target] = (project_name, 'test', test['name']) len_targets = len(self.targets) if len_targets == 1: self.on_done(0) elif len_targets > 1: self.window.show_quick_panel(self.targets, self.on_done) else: Common.sublime_status_message('No target found for REPL.') else: Common.sublime_status_message("Not in project") else: Common.sublime_status_message("No file active")
def do_load(self, view): filename = view.file_name() if not Common.view_is_haskell_source(view) or not filename: return if Settings.COMPONENT_DEBUG.event_viewer: print('{0}.on_load {1}.'.format(type(self).__name__, filename)) view_settings = view.settings() or {} if Settings.PLUGIN.use_improved_syntax and (filename.endswith(".hs") or filename.endswith(".hsc") or \ view_settings.get('syntax', '').endswith('.tmLanguage')): view_settings.set('syntax', 'Packages/SublimeHaskell/Syntaxes/Haskell-SublimeHaskell.sublime-syntax') EventCommon.assoc_to_project(view, self.backend_mgr, filename) _project_dir, project_name = Common.locate_cabal_project_from_view(view) Utils.run_async('rescan source {0}/{1}'.format(project_name, filename), self.rescan_source, project_name, filename, {'drop_all': False})
def run(self, _edit, **kwargs): module_name = kwargs.get('module_name') filename = kwargs.get('filename') symdb = kwargs.get('db') scope = kwargs.get('scope') self.candidates = [] self.current_file_name = self.view.window().active_view().file_name() the_module = None project_name = Common.locate_cabal_project_from_view(self.view.window().active_view())[1] if filename: the_module = Utils.head_of(BackendManager.active_backend().module(project_name, file=filename)) if not the_module: Common.sublime_status_message('Module {0} not found'.format(filename)) return elif module_name: cand_mods = self.candidate_modules(project_name, module_name, scope, symdb) if not cand_mods: Common.sublime_status_message('Module {0} not found'.format(module_name)) return elif len(cand_mods) == 1: the_module = self.get_module_info(project_name, cand_mods[0], module_name) if the_module: the_module = Utils.head_of(the_module) else: self.candidates.extend([(m, [m.name, m.location.to_string()]) for m in cand_mods]) else: if self.current_file_name: cand_mods = BackendManager.active_backend().scope_modules(project_name, self.current_file_name) else: symbol_db = symbols.PackageDb.from_string(symdb) if symdb else None cand_mods = BackendManager.active_backend().list_modules(symdb=symbol_db) self.candidates.extend([(m, [m.name, m.location.to_string()]) for m in cand_mods]) if the_module: self.candidates = sorted(list(the_module.declarations.values()), key=lambda d: d.brief()) results = [[decl.brief(use_unicode=False), decl.docs.splitlines()[0] if decl.docs else ''] for decl in self.candidates] self.view.window().show_quick_panel(results, self.on_symbol_selected) else: self.candidates.sort(key=lambda c: c[1][0]) self.view.window().show_quick_panel([c[1] for c in self.candidates], self.on_done)
def run(self, _edit, **_kwargs): if Common.view_is_haskell_symbol_info(self.view): pack = self.view.settings().get('package') mod = self.view.settings().get('module') if pack and mod: webbrowser.open('http://hackage.haskell.org/package/{0}/docs/{1}.html'.format(pack, mod.replace('.', '-'))) else: project_name = Common.locate_cabal_project_from_view(self.view)[1] qsymbol = Common.get_qualified_symbol_at_region(self.view, self.view.sel()[0]) modules = [] if qsymbol.is_module(): # module scope = self.view.file_name() if scope: modules = [m for m in BackendManager.active_backend().scope_modules(project_name, scope, lookup=qsymbol.module, search_type='exact') if m.by_cabal()] else: modules = [m for m in BackendManager.active_backend().list_modules(symdb=m.location.db) if m.name == qsymbol.module and m.by_cabal()] else: # symbol scope = self.view.file_name() if scope: decls = BackendManager.active_backend().whois(qsymbol.qualified_name(), file=scope) or \ BackendManager.active_backend().lookup(qsymbol.full_name(), file=scope) or \ BackendManager.active_backend().symbol(lookup=qsymbol.full_name(), search_type='exact') if not decls: Common.sublime_status_message('Module for symbol {0} not found'.format(qsymbol.full_name())) return modules = [decl.defined_module() for decl in decls] if not modules: Common.sublime_status_message('Module {0} not found'.format(qsymbol.module)) elif len(modules) == 1: pkg_id = modules[0].location.package.package_id() pkg_name = modules[0].name.replace('.', '-') webbrowser.open('http://hackage.haskell.org/package/{0}/docs/{1}.html'.format(pkg_id, pkg_name)) else: self.candidates = modules[:] mod_strings = [[m.name, m.location.package.package_id()] for m in self.candidates] self.view.window().show_quick_panel(mod_strings, self.on_done)
def context_completion(self, view, key, _operator, _operand, _matchall): # Completion context is the only branch here where a backend is needed. retval = False with self.backend_mgr: project_dir, project_name = Common.locate_cabal_project_from_view(view) region = view.sel()[0] if region.a == region.b: word_region = view.word(region) preline = Common.get_line_contents_before_region(view, word_region) preline += self.COMPLETION_CHARS[key] qsymbol = Common.get_qualified_symbol(preline) if qsymbol.module: mod_completions = self.autocompleter.get_current_module_completions(project_name, project_dir) if qsymbol.is_import_list: retval = qsymbol.module in mod_completions else: retval = [m for m in mod_completions if m.startswith(qsymbol.module)] != [] return retval
def do_post_save(self, view): if not Common.view_is_inspected_source(view): return if Settings.COMPONENT_DEBUG.event_viewer: print('{0}.on_post_save invoked.'.format(type(self).__name__)) filename = view.file_name() if not filename: if Settings.COMPONENT_DEBUG.event_viewer: print('{0}.on_post_save: no file name.'.format(type(self).__name__)) return _project_dir, project_name = Common.locate_cabal_project_from_view(view) if Common.view_is_haskell_source(view): self.type_cache.remove(filename) if Settings.PLUGIN.enable_auto_build: Builder.Builder(view, continue_success=self.post_successful_check).auto_build() else: EventCommon.do_check_lint(view, continue_success=self.post_successful_check) Utils.run_async('rescan source {0}/{1}'.format(project_name, filename), self.rescan_source, project_name, filename, False)
def run(self, **kwargs): project = kwargs.get('project') or False decls = [] if project: project_name = Common.locate_cabal_project_from_view(self.view)[1] modules = BackendManager.active_backend().module(project_name, file=self.current_filename) current_project = Utils.head_of(modules).location.project if not current_project: Common.sublime_status_message('File {0} is not in project'.format(self.current_filename)) return decls = self.sorted_decls_name(BackendManager.active_backend().symbol(project=current_project)) self.declarations = [[decl.brief(True, use_unicode=False), decl.module.name] for decl in decls] else: decls = self.sorted_decls_pos(BackendManager.active_backend().symbol(file=self.current_filename, local_names=True)) self.declarations = [[(decl.position.column * ' ') + decl.brief(True, use_unicode=False)] for decl in decls] self.decls = decls[:] if decls: self.window.show_quick_panel(self.declarations, self.on_done, 0, self.closest_idx(decls), self.on_highlighted if not project else None)
def get_completions(self, view, locations): "Get all the completions related to the current file." current_file_name = view.file_name() if not current_file_name: return [] if Settings.COMPONENT_DEBUG.completions: print('AutoCompleter.get_completions.') self.current_filename = current_file_name _, project_name = Common.locate_cabal_project_from_view(view) line_contents = Common.get_line_contents(view, locations[0]) qsymbol = Common.get_qualified_symbol(line_contents) qualified_prefix = qsymbol.qualified_name() if Settings.COMPONENT_DEBUG.completions: print('qsymbol {0}'.format(qsymbol)) print('current_file_name {0} qualified_prefix {1}'.format(current_file_name, qualified_prefix)) view_settings = view.settings() wide = view_settings.get('subhask_wide_completion') backend = BackendManager.active_backend() suggestions = [] completions = [] if qsymbol.module: if qsymbol.is_import_list: current_module = Utils.head_of(backend.module(project_name, current_file_name)) if current_module and current_module.location.project: # Search for declarations of qsymbol.module within current project q_module = Utils.head_of(backend.scope_modules(project_name, current_file_name, lookup=qsymbol.module, search_type='exact')) if q_module is not None: if q_module.by_source(): proj_module = backend.resolve(file=q_module.location.filename, exports=True) if proj_module: suggestions = proj_module.declarations.values() elif q_module.by_cabal(): cabal_module = Utils.head_of(backend.module(project_name, lookup=q_module.name, search_type='exact', package=q_module.location.package.name)) if cabal_module: suggestions = cabal_module.declarations.values() else: if Settings.COMPONENT_DEBUG.completions: print('completions: querying module-specific completions') suggestions = backend.complete(qsymbol, current_file_name, wide=wide) completions = make_completions(suggestions) else: with self.cache as cache_: if wide: if Settings.COMPONENT_DEBUG.completions: print('completions: returning global completions') completions += cache_.global_completions() else: if Settings.COMPONENT_DEBUG.completions: print('completions: returning file-specific completions') completions += cache_.files.get(current_file_name, cache_.global_completions()) completions += self.keyword_completions(qsymbol.name) sort_completions(completions) return completions
def activated_worker(self, view, filename): with self.backend_mgr: EventCommon.assoc_to_project(view, self.backend_mgr, filename) _, project_name = Common.locate_cabal_project_from_view(view) if Common.view_is_haskell_source(view): self.autocompleter.generate_completions_cache(project_name, filename)
def scan_resp(_resp): status_msg.result_ok() _project_dir, project_name = Common.locate_cabal_project_from_view(self.view) self.autocompleter.drop_completions_async(current_file_name) self.autocompleter.generate_completions_cache(project_name, current_file_name, contents=view_contents)
def refresh_view_types(view): if not SourceHaskellTypeCache().has(view.file_name()): project_name, _ = Common.locate_cabal_project_from_view(view) get_type_view(view, project_name)