def feed(self, rng, replacement):
        """
        Directly called by a TextView delegate to replace existing chars
        in given range with the given new chars.

        :param (int, int) | None | str rng: the range of selected chars
        :param str replacement: new chars
        :return:
        """

        if rng is None or rng == self.RANGE_MODIFIABLE_CHARS:
            rng_adjusted = (self.x_modifiable, len(self.chars))
        elif rng == self.RANGE_BUFFER_END:
            rng_adjusted = (len(self.chars), len(self.chars))
        elif rng == self.RANGE_CURSOR_TO_END:
            rng_adjusted = self._adjust_range((self.main_screen.cursor_xs, self.main_screen.text_length))
        else:
            # Convert and adjust the range relative to the input buffer
            rng_adjusted = self._adjust_range(rng)

        # Lock the main_screen for modification
        with self.main_screen.acquire_lock():
            self._ensure_main_screen_consistency()

            # Delete contents of selected range first
            if rng_adjusted[0] != rng_adjusted[1]:
                if self.debug:
                    self.logger.debug('DELETING {!r} (chars: {!r})'.format(rng_adjusted, self.chars))
                self.chars = self.chars[:rng_adjusted[0]] + self.chars[rng_adjusted[1]:]
                replace_rng = (rng_adjusted[0] - self.x_modifiable, rng_adjusted[1] - self.x_modifiable)
                self.main_screen.replace_in_range(replace_rng, '', relative_to_x_modifiable=True)
        # Lock is now released

        if replacement == '':  # pure deletion
            self.stash.renderer.render(no_wait=True)

        elif replacement == '\t':  # TODO: Separate tab manager

            # When no foreground script is running, default tab handler is to auto-complete commands
            tab_handler = (
                self.stash.completer.complete if not self.stash.runtime.child_thread else self.stash.external_tab_handler
            )

            if callable(tab_handler):
                incomplete = self.chars[self.x_modifiable:rng_adjusted[0]]
                try:
                    completed, possibilities = tab_handler(incomplete)

                    if completed != incomplete:
                        with self.main_screen.acquire_lock():
                            self.modifiable_string = completed + self.chars[rng_adjusted[0]:]
                            self.main_screen.modifiable_string = self.modifiable_string
                            self.main_screen.cursor_x = self.main_screen.x_modifiable + len(completed)

                    elif len(possibilities) > 0:  # TODO: handle max possibilities checking
                        # Run through stream feed to allow attributed texts to be processed
                        self.stash.stream.feed(
                            u'\n%s\n%s' % ('  '.join(possibilities),
                                           self.stash.runtime.get_prompt()),
                            render_it=False  # do not render to avoid dead lock on UI thread
                        )
                        with self.main_screen.acquire_lock():
                            self.main_screen.modifiable_string = self.modifiable_string
                            self.main_screen.cursor_x = self.main_screen.x_modifiable + len(incomplete)

                    else:  # no completion can be achieved
                        with self.main_screen.acquire_lock():
                            self.main_screen.modifiable_string = self.modifiable_string
                            self.main_screen.cursor_x = self.main_screen.x_modifiable + len(incomplete)

                except Exception as e:  # TODO: better error handling
                    self.stash.stream.feed(
                        u'\nauto-completion error: %s\n%s' % (repr(e),
                                                              self.stash.runtime.get_prompt()),
                        render_it=False
                    )
                    with self.main_screen.acquire_lock():
                        self.main_screen.modifiable_string = self.modifiable_string
                        self.main_screen.cursor_x = self.main_screen.x_modifiable + len(incomplete)

                self.stash.renderer.render(no_wait=True)
            else:
                # TODO: simply add the tab character or show a warning?
                pass  # do nothing for now

        else:  # process line by line
            # TODO: Ideally the input should be processed by character. But it is slow.
            x = rng_adjusted[0]  # The location where character to be inserted
            for rpln in replacement.splitlines(True):

                # Lock the main_screen for modification
                with self.main_screen.acquire_lock():
                    self._ensure_main_screen_consistency()

                    # Update the mini buffer and the main_screen buffer
                    if rpln.endswith('\n'):  # LF is always added to the end of the line
                        if len(rpln) > 1:  # not a pure return char
                            self.main_screen.replace_in_range(
                                (x - self.x_modifiable,
                                 x - self.x_modifiable),
                                rpln[:-1],
                                relative_to_x_modifiable=True
                            )
                        self.main_screen.replace_in_range(None, u'\n', relative_to_x_modifiable=False)
                        self.chars = self.chars[:x] + rpln[:-1] + self.chars[x:] + '\n'
                    else:
                        # Do not send NULL char to main screen, it crashes the app
                        if rpln != '\0':
                            self.main_screen.replace_in_range(
                                (x - self.x_modifiable,
                                 x - self.x_modifiable),
                                rpln,
                                relative_to_x_modifiable=True
                            )
                        self.chars = self.chars[:x] + rpln + self.chars[x:]
                # Lock is now released

                # After the first line, the range should now always be at the end
                x = len(self.chars)

                # Render after every line
                self.stash.renderer.render(no_wait=True)

            # If complete lines or EOF are available, push them to IO buffer and notify
            # runtime for script running if no script is currently running.
            idx_lf = max(self.chars.rfind('\n'), self.chars.rfind('\0'))
            if idx_lf != -1:
                self.stash.io.push(self.chars[:idx_lf + 1])
                self.chars = self.chars[idx_lf + 1:]  # keep size of chars under control
                if self.runtime_callback is not None:
                    # When a script is running, all input are considered directed
                    # to the running script.
                    callback, self.runtime_callback = self.runtime_callback, None
                    callback()

        Directly called by a TextView delegate to replace existing chars
        in given range with the given new chars.

        :param (int, int) | None | str rng: the range of selected chars
        :param str replacement: new chars
        :return:
        
system/shstreams.py