def _save_done(self, modified_buffer: GtkSource.Buffer):
        """Gtk callback after the saving has been done."""
        if self._saving_dialog is not None:
            self._saving_dialog.hide()
            self._saving_dialog = None

        modified_buffer.set_modified(False)
        self._waiting_for_reload = True

        # Resync the breakpoints at the Breakpoint Manager.
        # Collect all line marks and check which is the first temporary opcode text mark in it, this is
        # the opcode to break on.
        breakpoints_to_resync = {}
        for line in range(0, modified_buffer.get_line_count()):
            marks = EditorTextMarkUtil.get_line_marks_for(
                modified_buffer, line, 'breakpoint')
            if len(marks) > 0:
                for ssb_filename, opcode_offset in EditorTextMarkUtil.get_tmp_opcodes_in_line(
                        modified_buffer, line):
                    if ssb_filename not in breakpoints_to_resync:
                        breakpoints_to_resync[ssb_filename] = []
                    breakpoints_to_resync[ssb_filename].append(opcode_offset)

        for ssb_filename, b_points in breakpoints_to_resync.items():
            self.file_context.breakpoint_manager.resync(ssb_filename, b_points)
 def add_line_mark_for_op(cls, b: GtkSource.Buffer, ssb_filename: str,
                          opcode_addr: int, name: str, category: str,
                          is_for_macro_call: bool):
     m = cls._get_opcode_mark(b, ssb_filename, opcode_addr,
                              is_for_macro_call)
     if m is not None:
         b.create_source_mark(name, category, b.get_iter_at_mark(m))
 def scroll_to_op(cls, b: GtkSource.Buffer, view: GtkSource.View,
                  ssb_filename: str, opcode_addr: int,
                  is_for_macro_call: bool):
     m = cls._get_opcode_mark(b, ssb_filename, opcode_addr,
                              is_for_macro_call)
     if m is not None:
         view.scroll_to_mark(m, 0.1, False, 0.1, 0.1)
         b.place_cursor(b.get_iter_at_mark(m))
 def _get_opcode_mark(cls, b: GtkSource.Buffer, ssb_filename: str,
                      opcode_addr: int,
                      is_for_macro_call: bool) -> Optional[Gtk.TextMark]:
     if is_for_macro_call:
         return b.get_mark(
             f'opcode_<<<{ssb_filename}>>>_{opcode_addr}_call')
     else:
         return b.get_mark(f'opcode_<<<{ssb_filename}>>>_{opcode_addr}')
 def create_opcode_mark(cls, b: GtkSource.Buffer, ssb_filename: str,
                        offset: int, line: int, col: int, is_tmp: bool,
                        is_for_macro_call: bool):
     textiter = b.get_iter_at_line_offset(line, col)
     tmp_prefix = 'TMP_' if is_tmp else ''
     macro_call_suffix = '_call' if is_for_macro_call else ''
     b.create_mark(
         f'{tmp_prefix}opcode_<<<{ssb_filename}>>>_{offset}{macro_call_suffix}',
         textiter)
 def remove_breakpoint_line_mark(cls, b: GtkSource.Buffer,
                                 ssb_filename: str, opcode_offset: int,
                                 category: str):
     # XXX: This is a bit ugly, but due to the fact, that there can be one call to a macro
     # in the same file, there can be exactly 0-2 line markers:
     for i in [0, 1]:
         m: Gtk.TextMark = b.get_mark(
             f'for:opcode_<<<{ssb_filename}>>>_{opcode_offset}_{i}')
         if m is None:
             return
         b.remove_source_marks(b.get_iter_at_mark(m), b.get_iter_at_mark(m),
                               category)
 def on_buffer_notify_cursor_position(self, buffer: GtkSource.Buffer,
                                      *args):
     textiter = buffer.get_iter_at_offset(buffer.props.cursor_position)
     if 'string' in buffer.get_context_classes_at_iter(textiter):
         # iter_backward_to_context_class_toggle and iter_forward_to_context_class_toggle
         # seem to be broken (because of course they are), so we do it manually.
         start = self._get_string_start(textiter)
         end = self._get_string_end(textiter)
         if start is None or end is None:
             return True
         string = buffer.get_text(start, end, False)
         self.context.on_selected_string_changed(string)
     return True
 def _build_calltip_data(self, textiter: Gtk.TextIter,
                         buffer: GtkSource.Buffer):
     cursor = textiter.copy()
     count_commas = 0
     count_commas_since_last_lang_string_begin_mark = 0
     while cursor.backward_char():
         if cursor.get_char() == ')':
             # We are not in a function, for sure!
             return None
         if cursor.get_char() == '{' or cursor.get_char() == '<':
             # Handle middle of language string or a pos marker
             count_commas -= count_commas_since_last_lang_string_begin_mark
             count_commas_since_last_lang_string_begin_mark = 0
         if cursor.get_char() == '}' or cursor.get_char() == '>':
             # Handle end of language string or a pos marker
             count_commas_since_last_lang_string_begin_mark = 0
         if cursor.get_char() == '(':
             # Handle the opcode/function name
             start_of_word = cursor.copy()
             backward_until_space(start_of_word)
             opcode_name = buffer.get_text(start_of_word, cursor, False)
             for op in self.opcodes:
                 if op.name == opcode_name:
                     return op, count_commas
             return None
         if cursor.get_char() == ',':
             # Collect commas for the arg index
             count_commas += 1
             count_commas_since_last_lang_string_begin_mark += 1
     return None
Example #9
0
    def __init__(self, preferences, action_name=None):
        super(SourceView, self).__init__()
        if action_name:
            self.action_name = action_name

        self.set_hexpand(True)
        self.set_vexpand(True)
        self.text_buffer = Buffer.new_with_language(
            LANGS['.%s' % preferences.parser])
        self.text_buffer.connect("changed", self.inc_changes)
        self.source_view = View.new_with_buffer(self.text_buffer)

        self.spellchecker = Checker()
        self.spellchecker.connect("language-changed", self.language_changed)

        self.source_view.override_font(
            FontDescription.from_string('Monospace'))
        # self.source_view.set_monospace(True) since 3.16
        self.add(self.source_view)

        editor_pref = preferences.editor
        self.set_period_save(editor_pref.period_save)
        self.set_check_spelling(editor_pref.check_spelling,
                                editor_pref.spell_lang)
        self.set_spaces_instead_of_tabs(editor_pref.spaces_instead_of_tabs)
        self.source_view.set_tab_width(editor_pref.tab_width)
        self.source_view.set_auto_indent(editor_pref.auto_indent)
        self.source_view.set_show_line_numbers(editor_pref.line_numbers)
        self.source_view.set_show_right_margin(editor_pref.right_margin)
        self.source_view.set_highlight_current_line(editor_pref.current_line)
        self.set_text_wrapping(editor_pref.text_wrapping)
        self.set_white_chars(editor_pref.white_chars)
 def add_breakpoint_line_mark(cls, b: GtkSource.Buffer, ssb_filename: str,
                              opcode_offset: int, category: str):
     ms = []
     m: Gtk.TextMark = cls._get_opcode_mark(b, ssb_filename, opcode_offset,
                                            True)
     if m is not None:
         ms.append(m)
     m = cls._get_opcode_mark(b, ssb_filename, opcode_offset, False)
     if m is not None:
         ms.append(m)
     for i, m in enumerate(ms):
         line_iter = b.get_iter_at_line(b.get_iter_at_mark(m).get_line())
         lm: Gtk.TextMark = b.get_mark(
             f'for:opcode_<<<{ssb_filename}>>>_{opcode_offset}_{i}')
         if lm is not None:
             return
         b.create_source_mark(
             f'for:opcode_<<<{ssb_filename}>>>_{opcode_offset}_{i}',
             category, line_iter)
 def on_sourcebuffer_delete_range(self, buffer: GtkSource.Buffer,
                                  start: Gtk.TextIter, end: Gtk.TextIter):
     if start.get_line() != end.get_line() or start.get_chars_in_line(
     ) == 0:
         i = start.copy()
         ms = []
         while i.get_offset() <= end.get_offset():
             ms += buffer.get_source_marks_at_iter(i, 'breakpoint')
             if not i.forward_char():
                 break
         for m in ms:
             self.remove_breakpoint(m)
     return True
Example #12
0
    def __init__(self, win, preferences, action_name=None):
        super(SourceView, self).__init__()
        if action_name:
            self.action_name = action_name

        self.set_hexpand(True)
        self.set_vexpand(True)
        self.text_buffer = Buffer.new_with_language(LANGS['.%s' %
                                                          preferences.parser])
        self.text_buffer.connect("changed", self.inc_changes)
        # TODO: will work when FileSaver and FileLoader will be used
        # self.text_buffer.set_implicit_trailing_newline(False)
        self.source_view = View.new_with_buffer(self.text_buffer)

        adj = self.get_vadjustment()
        adj.connect("value-changed", self.on_scroll_changed)

        self.spellchecker = Checker()
        self.spellchecker.connect("language-changed", self.on_language_changed)

        self.source_view.override_font(
            FontDescription.from_string('Monospace'))
        # self.source_view.set_monospace(True) since 3.16
        self.add(self.source_view)

        editor_pref = preferences.editor
        self.set_period_save(editor_pref.period_save)
        self.set_check_spelling(editor_pref.check_spelling,
                                editor_pref.spell_lang)
        self.set_spaces_instead_of_tabs(editor_pref.spaces_instead_of_tabs)
        self.source_view.set_tab_width(editor_pref.tab_width)
        self.source_view.set_auto_indent(editor_pref.auto_indent)
        self.source_view.set_show_line_numbers(editor_pref.line_numbers)
        self.source_view.set_show_right_margin(editor_pref.right_margin)
        self.source_view.set_highlight_current_line(editor_pref.current_line)
        self.set_text_wrapping(editor_pref.text_wrapping)
        self.set_white_chars(editor_pref.white_chars)

        self.search_settings = SearchSettings(wrap_around=True)
        self.search_context = SearchContext.new(self.text_buffer,
                                                self.search_settings)
        self.search_mark = None

        self.__win = win
        timeout_add(200, self.check_in_thread)
 def get_line_marks_for(cls, b: GtkSource.Buffer, line: int,
                        category: str) -> List[GtkSource.Mark]:
     return b.get_source_marks_at_line(line, category)
 def remove_all_line_marks(cls, b: GtkSource.Buffer, category: str):
     b.remove_source_marks(b.get_start_iter(), b.get_end_iter(), category)
 def switch_to_new_op_marks(cls, b: GtkSource.Buffer, ssb_filename: str):
     textiter: Gtk.TextIter = b.get_start_iter().copy()
     # TODO: This is probably pretty slow
     while textiter.forward_char():
         old_marks_at_pos = [
             m for m in textiter.get_marks() if m.get_name()
             and m.get_name().startswith(f'opcode_<<<{ssb_filename}>>>_')
         ]
         new_marks_at_pos = [
             m for m in textiter.get_marks() if m.get_name() and
             m.get_name().startswith(f'TMP_opcode_<<<{ssb_filename}>>>_')
         ]
         for m in old_marks_at_pos:
             b.delete_mark(m)
         for m in new_marks_at_pos:
             name = m.get_name()
             # Maybe by chance an old mark with this name still exists elsewhere, remove it.
             om = b.get_mark(name[4:])
             if om is not None:
                 b.delete_mark(om)
             # Move by deleting and re-creating.
             match = MARK_PATTERN_TMP.match(m.get_name())
             if match.group(3):
                 b.create_mark(
                     f'opcode_<<<{str(match.group(1))}>>>_{int(match.group(2))}_{match.group(3)}',
                     textiter)
             else:
                 b.create_mark(
                     f'opcode_<<<{str(match.group(1))}>>>_{int(match.group(2))}',
                     textiter)
             b.delete_mark(m)
    def on_buffer_notify_cursor_position(self, buffer: GtkSource.Buffer,
                                         *args):
        textiter = buffer.get_iter_at_offset(buffer.props.cursor_position)
        tip = self._build_calltip_data(textiter, buffer)
        if not tip:
            if self.position_mark_calltip is not None:
                self.position_mark_calltip.reset(self._active_widget)
            if self._active_widget:
                self._active_widget.destroy()
                self._active_widget = None
                self._active_op = None
                self._active_arg = None
            return True

        op: Pmd2ScriptOpCode
        op, arg_index = tip
        if not self._active_widget:
            self._active_widget = GtkSource.CompletionInfo.new()
            self._active_widget.set_attached_to(self.view)

        self._active_widget.move_to_iter(self.view, textiter)

        op_was_same = self._active_op == op
        if not op_was_same:
            self._active_op = op
            for c in self._active_widget.get_children():
                self._active_widget.remove(c)

            outer_box: Gtk.Box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4)
            btn_box: Gtk.Box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 4)
            outer_box.pack_start(btn_box, True, False, 0)
            self._active_widget.add(outer_box)

        if not op_was_same or self._active_arg != arg_index:
            self._active_arg = arg_index
            btn_box = self._active_widget.get_children()[0].get_children()[0]
            for c in btn_box.get_children():
                btn_box.remove(c)
            for i, arg in enumerate(op.arguments):
                lbl: Gtk.Label = Gtk.Label.new('')
                if arg_index == i:
                    markup = f'<b>{arg.name}: <i>{arg.type}</i></b>, '
                else:
                    markup = f'<span weight="light">{arg.name}:  <i>{arg.type}</i></span>, '
                if i == len(
                        op.arguments) - 1 and not op.repeating_argument_group:
                    markup = markup.rstrip(', ')
                lbl.set_markup(markup)
                btn_box.pack_start(lbl, True, False, 0)
            if op.repeating_argument_group:
                lbl = Gtk.Label.new('[')
                btn_box.pack_start(lbl, True, False, 0)
                for i, arg in enumerate(op.repeating_argument_group.arguments):
                    lbl = Gtk.Label.new('')
                    # TODO: Support highlighting individual repeating args. (not really used though)
                    if arg_index >= len(op.arguments):
                        markup = f'<b>{arg.name}: <i>{arg.type}</i></b>, '
                    else:
                        markup = f'<span weight="light">{arg.name}:  <i>{arg.type}</i></span>, '
                    if i == len(op.repeating_argument_group.arguments) - 1:
                        markup = markup.rstrip(', ')
                    lbl.set_markup(markup)
                    btn_box.pack_start(lbl, True, False, 0)
                lbl = Gtk.Label.new('... ]')
                btn_box.pack_start(lbl, True, False, 0)

        if self.position_mark_calltip is not None:
            self.position_mark_calltip.add_button_if_pos_mark(
                self._active_widget.get_children()[0], buffer)

        self._active_widget.show_all()

        return True