예제 #1
0
    def run(self, edit, results_panel_name, find_results, symbol_occurences):
        logger.debug("The helper command '{}' has been triggered.".format(
            self.__class__.__name__))

        # Fill the view with the results
        self.view.insert(edit, 0, find_results)

        # Assure the results navigation starts from the beggining
        self.view.sel().clear()

        # Convert symbol occurences to regions
        regions_to_highlight = [
            sublime.Region(self.view.text_point(row, col_start),
                           self.view.text_point(row, col_end))
            for row, col_start, col_end in symbol_occurences
        ]

        # Highlight all symbol occurences
        flags = (sublime.DRAW_STIPPLED_UNDERLINE | sublime.DRAW_NO_FILL
                 | sublime.DRAW_NO_OUTLINE)

        self.view.add_regions("symbol",
                              regions_to_highlight,
                              scope="storage.type",
                              flags=flags)

        sublime.active_window().run_command(
            "show_panel", {"panel": "output." + results_panel_name})
        sublime.active_window().focus_view(self.view)
예제 #2
0
    def _create_results_panel(self, find_results, symbol_occurences):
        """ Create a Find Results panel, fill it with `find_results`, and
        highlight all `symbol_occurences`.
        """
        logger.debug("Preparing results panel.")

        syntax = "Packages/RTags/Find References Results.hidden-tmLanguage"
        file_regex = '^([^ \t].*):$'
        line_regex = '^ +([0-9]+):'
        working_dir = ''
        results_panel_name = "RTags: References"

        results_view = self.view.window().create_output_panel(
            results_panel_name)
        results_view.settings().set("result_file_regex", file_regex)
        results_view.settings().set("result_line_regex", line_regex)
        results_view.settings().set("result_base_dir", working_dir)
        results_view.settings().set("word_wrap", False)
        results_view.settings().set("line_numbers", False)
        results_view.settings().set("gutter", True)
        results_view.settings().set("scroll_past_end", False)
        results_view.settings().set("rulers", [])
        results_view.settings().set("translate_tabs_to_spaces", False)
        results_view.settings().set("highlight_line", True)
        results_view.assign_syntax(syntax)

        results_view.run_command(
            "publish_results_to_panel", {
                "results_panel_name": results_panel_name,
                "find_results": find_results,
                "symbol_occurences": symbol_occurences
            })
예제 #3
0
파일: rc_call.py 프로젝트: artisdom/RTags-1
    def _log_rc_success(output):
        """ Log about a successful execution of an rc command.

        Params:
            output - UTF-8 decoded output of that command
        """
        success_msg_format = "Successfully executed rc command:\n \"{}\""
        success_msg = success_msg_format.format(output)
        logger.debug(success_msg)
예제 #4
0
    def _set_cursor_location_different_view(source_view, dest_file_name,
                                            dest_row, dest_col):
        """ Navigate to a different view directly to the desired position.
        """
        logger.debug("Navigating to location within a different view.")

        target_location = ':'.join(
            [dest_file_name, str(dest_row),
             str(dest_col)])

        # Last cursor location is automatically saved for future use of
        # 'jump_back' command.
        source_view.window().open_file(target_location,
                                       sublime.ENCODED_POSITION)
예제 #5
0
    def _parse_output(self, output):
        """ Parse `output` received from "rc" to a useful form.

        Assuming output format is "{target_filename}:{row}:{col}:".
        """
        logger.debug("Parsing output returned from rc call.")

        # Extract target location from output (remove trailing colon)
        target_location = output[:-1]  # "{target_location}:"

        # Split target location to target file name, row and col
        delimiter = ':'  # "{target_filename}:{row}:{col}"
        target_filename, row, col = target_location.split(delimiter)

        return target_filename, (int)(row), (int)(col)
예제 #6
0
    def extract_single_location(view, avoid_word_end=False):
        """ Return a single cursor's absolute location ({file}:{row}:{col})

            Extract the location of the cursor from a "view" object in format
            {file}:{row}:{col}. In case `view` contains more than a single
            location, empty string ("") is returned.

            In case `avoid_word_end`=True, identify the word in which the
            cursor is in, and if the cursor is at the end of that word, return
            the location of the beginning of it. This option is useful in case
            the location is to be used in a call for rc, since it has a bug
            when handling such locations.
        """

        # Assure there is only a single cursor
        if CursorLocationHelper._is_single_cursor(view):
            logger.debug(
                "Can't extract single location from multiple cursors.")
            return ""

        # Assure that cursor (which represented by region) is not a selection
        region = view.sel()[0]
        if CursorLocationHelper._is_selection(region):
            logger.debug(
                "Can't extract single location from non-empty selection.")
            return ""

        # Extract cursor's point from the cursor's region
        point = region.a

        if avoid_word_end:
            if CursorLocationHelper._is_end_of_word(view, point):
                # Reassign point to the beggining of the word
                point = view.find_by_class(
                    pt=point,
                    forward=False,
                    # Support operator overloading (words of only punctuations)
                    classes=sublime.CLASS_WORD_START
                    | sublime.CLASS_PUNCTUATION_START)

        # Convert current cursor location from "point" to "row" and "col"
        zero_based_row, zero_based_col = view.rowcol(point)
        row, col = zero_based_row + 1, zero_based_col + 1

        return CursorLocationHelper._to_absolute_location(
            view.file_name(), row, col)
예제 #7
0
    def _set_cursor_location_same_view(source_view, dest_row, dest_col):
        """ Set `source_view`'s cursor/s to a new location at the same view.
        """
        logger.debug("Navigating to location within the same view.")

        # Save last cursor location for future use of 'jump_back' command
        cursor_locations_history = history_list.get_jump_history(
            source_view.window().id())
        cursor_locations_history.push_selection(source_view)

        # Modify the view's selection (cursors) by clearing and setting new one
        sel = source_view.sel()
        sel.clear()

        zero_based_row, zero_based_col = dest_row - 1, dest_col - 1
        point = source_view.text_point(zero_based_row, zero_based_col)
        sel.add(point)
        source_view.show(point)
예제 #8
0
파일: rc_call.py 프로젝트: artisdom/RTags-1
    def run(self):
        # Clear result of previous rc call
        self.received_output = ""
        self.rc_returned_successfully = False

        command = self._build_command()
        logger.debug("About to execute rc command: \"{}\"".format(command))

        # Try to execute rc command, and handle the result
        try:
            binary_output = subprocess.check_output(
                command.split(), timeout=self._timeout, shell=False)
        except subprocess.CalledProcessError as e:
            self._log_rc_error(command, e)
        else:
            str_output = binary_output.decode('UTF-8').strip()
            self._log_rc_success(str_output)
            self.received_output = str_output
            self.rc_returned_successfully = True
예제 #9
0
    def run(self):
        logger.info("The functional command '{}' has been triggered.".format(
            self.__class__.__name__))

        # Find all "compile_commands.json" files located in open folders
        compile_commands_file_path_results = [
            os.path.join(folder, file) for folder in self.window.folders()
            for file in os.listdir(folder) if file == "compile_commands.json"
        ]

        logger.debug("Found compile_commands.json files at:\n  {}".format(
            compile_commands_file_path_results))

        if not compile_commands_file_path_results:
            sublime.error_message(
                "[RTags error]\n\n" +
                "There is no compile_commands.json in your environment.")
            return

        if len(compile_commands_file_path_results) > 1:
            sublime.error_message(
                "[RTags error]\n\n" +
                "Multiple compile_commands.json files found in environment.\n"
                "I don't know which one to load, so I'll leave it to you.")
            return

        # Execute rc command
        compile_commands_path = compile_commands_file_path_results[0]
        rc_params = "--load-compile-commands {}".format(compile_commands_path)
        rc_thread = RCCall()
        rc_thread.execute_rc(rc_params)
        rc_thread.join()

        if rc_thread.rc_returned_successfully:
            self.window.status_message(
                "RTags: The compilation database was loaded successfully.")
        else:
            sublime.error_message(
                "[RTags error]\n\n" +
                "RC failed to load the compilation database.\n"
                "RTags can't work without it.\n"
                "Please refer to @StavE.")
예제 #10
0
        def _get_symbol_column_range(symbol_location):
            """Figure out the start and end columns of the referenced symbol.
            """
            rc_params = "--json --symbol-info {}".format(symbol_location)
            rc_thread = RCCall(self.view.file_name())
            rc_thread.execute_rc(rc_params)
            rc_thread.join()

            if not rc_thread.rc_returned_successfully:
                raise RuntimeError(
                    "Failed to get referenced symbol information")

            output_without_whitesapces = ''.join(
                rc_thread.received_output.split())
            output_as_dict = json.loads(output_without_whitesapces)

            if output_as_dict["endLine"] != output_as_dict["startLine"]:
                logger.debug("Symbol at \"{}\" is spread over multiple lines, "
                             "skipping highlight.".format(symbol_location))
                return (0, 0)

            return output_as_dict["startColumn"], output_as_dict["endColumn"]
예제 #11
0
    def set_cursor_location(source_view, dest_file_name, dest_row, dest_col):
        """ Move `source_view`'s cursor/s location to a new destination.

        Params:
            source_view    - sublime.view instance to modify its cursor
            dest_file_name - destination file name (absolute path)
            dest_row       - destination row number (nonzero based)
            dest_col       - destination column number (nonzero based)

        Note:
            - This method affects sublime's cursor locations history.
            - `source_view`'s cursors will be modified only in case the
            destination cursor location is in the same view.
        """
        logger.debug("About to navigate to a new location.")

        # New cursor location is in the source view
        if os.path.realpath(source_view.file_name()) == dest_file_name:
            CursorLocationHelper._set_cursor_location_same_view(
                source_view, dest_row, dest_col)

        # New cursor location is in a different view
        else:
            try:
                # Fix project/folder path prefix within absolute path, in case
                # the directed file is actually a part of it.
                dest_file_name = (
                    CursorLocationHelper._to_project_path(dest_file_name))
                logger.debug("New location is within the project path.")
            except CursorLocationHelper.FileOutsideProjectPathError:
                # No modification should be done in this case
                logger.debug("New location is outside the project path.")
                pass

            CursorLocationHelper._set_cursor_location_different_view(
                source_view, dest_file_name, dest_row, dest_col)
예제 #12
0
    def _parse_output(self, output):
        """ Parse `output` received from "rc" to a useful form.

        Returns:
         - String containing the results in a format matching the appropriate
           syntax definition (See `_create_results_panel()`)
         - Occurences list of the referenced symbol (row, col_start, col_end)
         - Statistics about the results (number of references, number of files)

        Assuming output is multiple lines of the format:
            "{target_filename}:{row}:{col}:{line_content}"

        The output is converted to match Sublime Text's "Find Results" syntax:
            {target_filename}:
             {row}: {line_content}
        """
        def _get_symbol_column_range(symbol_location):
            """Figure out the start and end columns of the referenced symbol.
            """
            rc_params = "--json --symbol-info {}".format(symbol_location)
            rc_thread = RCCall()
            rc_thread.execute_rc(rc_params)
            rc_thread.join()

            if not rc_thread.rc_returned_successfully:
                raise RuntimeError(
                    "Failed to get referenced symbol information")

            output_without_whitesapces = ''.join(
                rc_thread.received_output.split())
            output_as_dict = json.loads(output_without_whitesapces)

            if output_as_dict["endLine"] != output_as_dict["startLine"]:
                raise Exception(
                    "A symbol is not supposed to spread over multiple lines.")

            return output_as_dict["startColumn"], output_as_dict["endColumn"]

        logger.debug("Parsing output returned from rc call.")

        # Will contain the result of the parse
        find_results = ''

        # Will contain statistics about the find results
        number_of_results = 0
        number_of_files = 0

        # List of (row, col_start, col_end) tuples of the referenced symbol
        # occurences. note: row and col are zero based
        symbol_occurences = []

        # Every file name should be mentioned once, so duplications are removed
        previous_target_filename = ''
        previous_row = 0
        line_number = 0
        for line in output.splitlines():
            # Split output line to target file name, row, col and context
            delimiter = ':'
            target_filename, row, col, context = line.split(delimiter,
                                                            maxsplit=3)

            # Whether current result belongs to a different file (against prev)
            if target_filename != previous_target_filename:
                # Bind future results to current target file name
                find_results += target_filename + ':\n'

                # Append current result line
                find_results += ' ' + row + ':' + context + '\n'

                previous_row = row
                previous_target_filename = target_filename
                number_of_files += 1
                line_number += 1
            elif row != previous_row:
                # No need to print multiple results for the same line number
                # Append current result line
                find_results += ' ' + row + ':' + context + '\n'
                previous_row = row
            else:
                # Cancel line number increase from previous iteration
                line_number -= 1

            # Calculate the column offset for the referenced symbol occurence
            # The offset considers: " {row}:<tabstop>"
            offset = 1 + len(row) + 1

            # Used to calculate the end column of the referenced symbol
            symbol_location = ':'.join((target_filename, row, col))
            col_start, col_end = _get_symbol_column_range(symbol_location)
            col_start += offset
            col_end += offset

            # Add the referenced symbol occurence to the list
            symbol_occurences.append((line_number, col_start, col_end))

            number_of_results += 1
            line_number += 1

        return (find_results, symbol_occurences, number_of_results,
                number_of_files)
예제 #13
0
파일: rc_call.py 프로젝트: artisdom/RTags-1
    def _log_rc_error(cmd, e):
        """ Log about an error in rc execution.

        Params:
            cmd - rc command that caused the error
            e   - CalledProcessError exception

        RC exit codes:
            success - 0
            general_failure - 32
            network_failure - 33
            timeout_failure - 34
            not_indexed - 35
            connection_failure - 36
            protocol_failure - 37
            argument_parse_error - 38
        """
        # Errors that should be supressed from the normal user
        rc_exit_codes_unrelevant_to_user = {
            "general_failure": 32,
            "network_failure": 33,
            "timeout_failure": 34,
            "argument_parse_error": 38
        }

        # Errors that the user must be notified about
        rc_exit_codes_very_relevant_to_user = {
            "connection_failure": {
                "exit_code": 36,
                "user_friendly_message":
                "Failed to connect to rdm server.\n"
                "Are you sure it is running?"
            },
            "protocol_failure": {
                "exit_code": 37,
                "user_friendly_message":
                "Protocol version mismatch.                                 \n"
                "This plugin version does not support your RTags installation "
                "version."
            },
        }

        error_msg_format = (
            "Executing rc resulted in an error (return code {}).\n"
            " Command: \"{}\"\n"
            " Output: \"{}\"")
        error_msg = error_msg_format.format(
            e.returncode, cmd, e.output.decode('UTF-8').strip())

        # Classify the exit code by how relevant it is for the user
        if e.returncode not in rc_exit_codes_unrelevant_to_user.values():
            for very_relevant_error in (
                    rc_exit_codes_very_relevant_to_user.values()):
                if e.returncode == very_relevant_error["exit_code"]:
                    sublime.error_message(
                        "[RTags error]\n" +
                        very_relevant_error["user_friendly_message"])
                    break
            logger.error(error_msg)
        else:
            error_msg = "RC error supressed from user:\n" + error_msg
            logger.debug(error_msg)