def update_phantoms(self, view): # Display phantoms for embeds self.phantom_set = sublime.PhantomSet(view) phantoms = [] for region in view.find_by_selector( 'meta.brackets markup.underline.link.embed'): link = view.substr(region) note_name = note_name_from_link(link) note_path = path_to_note(view, note_name) extension = file_extension(note_name) if extension == None: lines = open(note_path, 'r').readlines() note_content = ''.join( ['<p>' + line + '</p>' for line in lines]) phantom_content = NOTE_EMBED_TEMPLATE.format(note_content) phantom = sublime.Phantom(region, phantom_content, sublime.LAYOUT_BLOCK) phantoms.append(phantom) elif is_image_extension(extension): log('embed jpg') note_content = IMAGE_EMBED_TEMPLATE.format(note_path) phantom = sublime.Phantom(region, note_content, sublime.LAYOUT_BLOCK) phantoms.append(phantom) self.phantom_set.update(phantoms)
def on_selection_modified_async(self): self.phantoms.update([]) # https://github.com/SublimeTextIssues/Core/issues/1497 s = self.view.sel()[0] if not (s.empty() and 'meta.tag.todo' in self.view.scope_name(s.a)): return rgn = self.view.extract_scope(s.a) text = self.view.substr(rgn) match = re.match(r'@due\(([^@\n]*)\)[\s$]*', text) # print(s, rgn, text) if not match: return # print(match.group(1)) preview_offset = self.view.settings().get('due_preview_offset', 0) remain_format = self.view.settings().get('due_remain_format', '{time} remaining') overdue_format = self.view.settings().get('due_overdue_format', '{time} overdue') date_format = self.view.settings().get('date_format', '(%y-%m-%d %H:%M)') start = rgn.a + 5 # within parenthesis now = datetime.now().replace(second=0, microsecond=0) date, error, region = expand_short_date(self.view, start, start, now, date_format) upd = [] if not error: if now >= date: delta = '-' + format_delta(self.view, now - date) else: delta = format_delta(self.view, date - now) content = (overdue_format if '-' in delta else remain_format).format(time=delta.lstrip('-') or 'a little bit') if content: if self.view.settings().get('show_remain_due', False): # replace existing remain/overdue phantom phantoms = self.view.settings().get('plain_tasks_remain_time_phantoms', []) for index, (point, _) in enumerate(phantoms): if point == region.a - 4: phantoms[index] = [point, str(delta)] self.view.settings().set('plain_tasks_remain_time_phantoms', phantoms) break else: upd.append(sublime.Phantom( sublime.Region(region.a - 4), content, sublime.LAYOUT_BELOW)) date = date.strftime(date_format).strip('()') if date == match.group(1).strip(): self.phantoms.update(upd) return upd.append(sublime.Phantom( sublime.Region(region.b - preview_offset), date or ( '{0}:<br> days:\t{1}<br> hours:\t{2}<br> minutes:\t{3}<br>'.format(*error) if len(error) == 4 else '{0}:<br> year:\t{1}<br> month:\t{2}<br> day:\t{3}<br> HH:\t{4}<br> MM:\t{5}<br>'.format(*error)), sublime.LAYOUT_INLINE)) self.phantoms.update(upd)
def rebuild_phantom(self): with self.rebuild_lock: html = '''<body id="tree"> <style> body { font-size: 12px; line-height: 16px; } .file a, .dir a { display: block; padding-left: 4px; } .dir a { padding-top: 1px; padding-bottom: 2px; } .dir a { text-decoration: none; } .file.active { background-color: color(var(--background) blend(var(--foreground) 80%)); border-radius: 3px; } .file span { font-size: 7px; } .file a { text-decoration: none; color: var(--foreground); } </style>''' + ''.join(self.render_subtree(self.tree, [])) + '</body>' self.phantom = sublime.Phantom(sublime.Region(0), html, sublime.LAYOUT_BLOCK, on_navigate=self.on_click) self.phantom_set.update([self.phantom])
def to_phantom(self, view): point = view.text_point(self.line - 1, 0) region = view.line(point) pos = region.end() html = template.substitute(**self.args()) return sublime.Phantom(sublime.Region(pos, pos), html, sublime.LAYOUT_INLINE)
def __init__(self, symbol, current_position, matches, commonprefix): self.name = "Toks: " + symbol sublime.active_window().destroy_output_panel(self.name) self.op = sublime.active_window().create_output_panel(self.name) sublime.active_window().run_command("show_panel", {"panel": "output." + self.name}) contents = "<b><a href=$hide style=\"text-decoration: none\">" + chr( 0x00D7) + "</a> " + symbol + "</b>" if isinstance(current_position, str): contents += " (<a href=" + os.path.join( commonprefix, current_position) + ">" + current_position + "</a>)" else: contents += " (<a href=$back>" + "unsaved view" + "</a>)" self.pre_lookup_view = current_position contents += "<ul>" for match in matches: contents += "<li><a href=" + os.path.join( commonprefix, match[0]) + ">" + match[0] + "</a> " + html.escape( match[1], quote=False) + " " + match[2] + "</li>" contents += "</ul>" self.phantom = sublime.Phantom(self.op.sel()[0], '<body>' + contents + '</body>', sublime.LAYOUT_INLINE, on_navigate=self.on_phantom_navigate) self.phantomset = sublime.PhantomSet(self.op, "toks") self.phantomset.update([self.phantom])
def show_phantoms(self, view): """Show phantoms for compilation errors. Args: view (sublime.View): current view """ view.erase_phantoms(PopupErrorVis._TAG) if view.buffer_id() not in self.phantom_sets: phantom_set = sublime.PhantomSet(view, PopupErrorVis._TAG) self.phantom_sets[view.buffer_id()] = phantom_set else: phantom_set = self.phantom_sets[view.buffer_id()] phantoms = [] current_error_dict = self.err_regions[view.buffer_id()] for err in current_error_dict: errors_dict = current_error_dict[err] errors_html = PhantomErrorVis._as_html(errors_dict) pt = view.text_point(err - 1, 1) phantoms.append( sublime.Phantom(sublime.Region(pt, view.line(pt).b), errors_html, sublime.LAYOUT_BELOW, on_navigate=self._on_phantom_navigate)) phantom_set.update(phantoms)
def handle_response(self, response) -> None: phantoms = [] for val in response: color = val['color'] red = color['red'] * 255 green = color['green'] * 255 blue = color['blue'] * 255 alpha = color['alpha'] content = """ <div style='padding: 0.4em; margin-top: 0.1em; border: 1px solid color(var(--foreground) alpha(0.25)); background-color: rgba({}, {}, {}, {})'> </div>""".format(red, green, blue, alpha) range = Range.from_lsp(val['range']) region = range_to_region(range, self.view) phantoms.append(sublime.Phantom(region, content, sublime.LAYOUT_INLINE)) if phantoms: if not self.color_phantom_set: self.color_phantom_set = sublime.PhantomSet(self.view, "lsp_color") self.color_phantom_set.update(phantoms) else: self.color_phantom_set = None
def insert_description(self, view, edit, description, location=0): html = markdown.markdown(description) lines = utils.split_lines_to_length(html) formated_description = "<body style=\"background-color: #101010\">{}</body>".format(lines) self.phantom_set.update([sublime.Phantom(sublime.Region(0, 0), formated_description, sublime.LAYOUT_INLINE)])
def update_regions(view, phantom_set): # Only for python syntax if view.settings().get('syntax').find('ython') == -1: return # Find block indicators blocks = view.find_all(r'^[[:blank:]]*#[[:blank:]]*%%') phantoms = [] for block in blocks: # Get previous line block = view.line(block) block = sublime.Region(block.begin() - 1, block.begin() - 1) block = view.line(block) # Create phantom phantoms.append( sublime.Phantom( block, ('<body id="py_blocks">' + stylesheet + '<div class="horz">' + ' ' * 79 + '</div></body>'), sublime.LAYOUT_BLOCK)) v_id = view.buffer_id() phantom_set[v_id] = sublime.PhantomSet(view, 'py_blocks') phantom_set[v_id].update(phantoms)
def show_syntax_errors(self, header, stderr, file_regex, include_other_files): """Display an output panel containing the syntax errors, and set gutter marks for each error.""" file_name = os.path.basename(self.view.file_name()) phantoms = [] for error in stderr.splitlines(): if file_name not in error: continue match = re.match(file_regex, error) print(match.groups()) if not match or not match.group(2): Logger.log("skipping unrecognizable error:\n" + error + "\nmatch:" + str(match)) continue row = int(match.group(2)) - 1 column = 0 if len(match.groups()) == 4: column = int(match.group(3)[:-1]) - 1 error = '<div class="warning">^ {}</div>'.format(match.groups()[-1].strip()) pt = self.view.text_point(row, column) phantoms.append(sublime.Phantom(sublime.Region(pt), error, sublime.LAYOUT_BELOW)) return phantoms
def calc_imports(self, imports): # TODO: cache modules! phantoms = [] lines, modules = imports cnt = 0 final_data = [] final_modules = [] for module in modules: if module and self.find_root_path(module): final_modules.append(module) final_data.append({"region": lines[cnt], "module": module}) cnt = cnt + 1 if len(final_modules) is 0: return None args = [] try: args = json.dumps(final_modules) except OSError: print('Error trying to stringify json!') return None data = self.node_bridge(PLUGIN_NODE_PATH, [ self.base_path, args ]) json_data = json.loads(data) cnt = 0 for module in final_data: size_data = json_data[cnt] if size_data['size']: line = self.view.line(module["region"].a) # TODO: change to settings kb = size_data['size'] / 1000 color = '#666' if kb > self.get_setting('min_size_warning', 40.0): color = 'var(--yellowish)' if kb > self.get_setting('min_size_error', 80.0): color = 'var(--redish)' gziptxt = '' if (self.get_setting('show_gzip', False)): gzipkb = size_data['gzip'] / 1000 gziptxt = '<span style="color: color(%s blend(var(--background) 50%%));">- %.2fkB gzip</span>' % (color, gzipkb) phantoms.append(sublime.Phantom( sublime.Region(line.b), ''' <style>html, body {margin: 0; padding:0; background-color: transparent;}</style> <span style="background-color: transparent; color: %s; padding: 0 15px; font-size: .9rem; line-height: 1.3rem;"><b>%.2fkB</b> %s</span> ''' % (color, kb, gziptxt), sublime.LAYOUT_INLINE )) cnt = cnt + 1 self.phantoms.update(phantoms)
def update_phantoms(self): phantoms = [] # Don't do any calculations on 1MB or larger files if self.view.size() < 2**20: # candidates = self.view.find_all('\$.*\$') candidates = self.view.find_all('12340982382') for r in candidates: self.view.fold(r) line_region = self.view.line(r.a) line = self.view.substr(r)[1:-1] leftTag = "<small style = \"color:#696969; background-color:#161C23;\"> " rightTag = "</small>" utf = leftTag + latex2utf(line) + rightTag idx = r.a - line_region.a if idx != -1: op_pt = line_region.a + idx phantoms.append( sublime.Phantom(sublime.Region(r.b), str(utf), sublime.LAYOUT_INLINE)) self.phantom_set.update(phantoms)
def show(view, marker, as_phantom=False): "Displays Emmet abbreviation as a preview for given view" content = None buffer_id = view.buffer_id() try: content = format_snippet(marker.preview()) except Exception as e: content = '<div class="error">%s</div>' % format_snippet(str(e)) if content: if as_phantom: if buffer_id not in phantom_sets_by_buffer: phantom_set = sublime.PhantomSet(view, 'emmet') phantom_sets_by_buffer[buffer_id] = phantom_set else: phantom_set = phantom_sets_by_buffer[buffer_id] r = sublime.Region(marker.region.end(), marker.region.end()) phantoms = [ sublime.Phantom(r, phantom_content(content), sublime.LAYOUT_INLINE) ] phantom_set.update(phantoms) elif not view.is_popup_visible() or previews_by_buffer.get( buffer_id, None) != marker.abbreviation: previews_by_buffer[buffer_id] = marker.abbreviation view.show_popup(popup_content(content), sublime.COOPERATE_WITH_AUTO_COMPLETE, marker.region.begin(), 400, 300)
def show_html(md_view, preview): global windows_phantom_set html = markdown2html(get_view_content(md_view), os.path.dirname(md_view.file_name()), md_view.settings().get('color_scheme')) phantom_set = windows_phantom_set.setdefault( preview.window().id(), sublime.PhantomSet(preview, 'markdown_live_preview')) phantom_set.update([ sublime.Phantom( sublime.Region(0), html, sublime.LAYOUT_BLOCK, lambda href: sublime.run_command('open_url', {'url': href})) ]) # lambda href: sublime.run_command('open_url', {'url': href}) # get the "ratio" of the markdown view's position. # 0 < y < 1 y = md_view.text_to_layout( md_view.sel()[0].begin())[1] / md_view.layout_extent()[1] # set the vector (position) for the preview vector = [0, y * preview.layout_extent()[1]] # remove half of the viewport_extent.y to center it on the screen (verticaly) vector[1] -= preview.viewport_extent()[1] / 2 # make sure the minimum is 0 vector[1] = 0 if vector[1] < 0 else vector[1] # the hide the first line vector[1] += preview.line_height() preview.set_viewport_position(vector, animate=False)
def run(self, edit): if not view_is_suitable(self.view): return phantoms = [] self.view.erase_phantoms('git-blame') # Before adding the phantom, see if the current phantom that is displayed is at the same spot at the selection if self.phantom_set.phantoms: phantom_exists = self.view.line(self.view.sel()[0]) == self.view.line(self.phantom_set.phantoms[0].region) if phantom_exists: self.phantom_set.update(phantoms) return for region in self.view.sel(): line = self.view.line(region) (row, col) = self.view.rowcol(region.begin()) full_path = self.view.file_name() try: blame_output = self.get_blame(int(row) + 1, full_path) except Exception as e: communicate_error(e) return sha, user, date, time = self.parse_blame(blame_output) body = template_one.format(sha=sha, user=user, date=date, time=time, stylesheet=stylesheet_one) phantom = sublime.Phantom(line, body, sublime.LAYOUT_BLOCK, self.on_phantom_close) phantoms.append(phantom) self.phantom_set.update(phantoms)
def display_previews(view, previews, pt=-1, current_index=0): global phantom_sets_by_buffer buffer_id = view.buffer_id() if ( buffer_id in phantom_sets_by_buffer and pt != phantom_sets_by_buffer[buffer_id][1] ): return preview_html, preview_regions = generate_previews(previews, current_index) on_navigate = get_on_navigate(view, previews, current_index, pt) view.add_regions( "cfml_method_preview", merge_regions(preview_regions), "source", flags=sublime.DRAW_NO_FILL, ) phantom_set = sublime.PhantomSet(view, "cfml_method_preview") phantom_sets_by_buffer[buffer_id] = (phantom_set, pt) phantom = sublime.Phantom( view.line(pt), preview_html, sublime.LAYOUT_BLOCK, on_navigate ) phantom_set.update([phantom])
def on_modified(self, view): if 'text.git-commit-message' not in view.scope_name(0): return first_line = view.line(0) text = view.substr(first_line) html = text.replace(' ', ' ') with open(os.path.join(__file__, '..', 'emojis.json')) as fp: for alias, base64 in json.load(fp).items(): html = html.replace( ':{}:'.format(alias), '<img src="data:image/png;base64,{0}" height="{1}" width="{1}"/>' .format(base64, view.line_height() - 2)) length = len(EMOJI_ALIASES.sub(' ', text)) color = 'var(--' + ('greenish' if length <= 50 else 'redish') html += ' <span style="color: {}"> → {}</span>'.format(color, length) view_id = view.id() ps = phantom_sets.get(view_id) or sublime.PhantomSet( view, 'commit-msg-preview') phantom_sets[view_id] = ps ps.update([sublime.Phantom(first_line, html, sublime.LAYOUT_BLOCK)])
def _render(self): settings = self.view.settings() enabled = settings.get(OPT_ENABLED, True) if not enabled: self.phantoms.update([]) return phantoms = [] self.phantoms.update([]) current_line = self.view.rowcol(self.view.sel()[0].begin())[0] visible_lines = self.view.lines(self.view.visible_region()) total_lines = len(visible_lines) lines = self.view.lines( sublime.Region( self.view.text_point(current_line - total_lines, 0), self.view.text_point(current_line + total_lines, 0))) for line in lines: line_number = self.view.rowcol(line.a)[0] phantoms.append(sublime.Phantom( line, self._tpl(*self._value(line_number, current_line)), sublime.LAYOUT_INLINE)) self.phantoms.update(phantoms)
def on_selection_modified_async(self): self.phantoms.update([]) # https://github.com/SublimeTextIssues/Core/issues/1497 s = self.view.sel()[0] if not (s.empty() and 'meta.tag.todo' in self.view.scope_name(s.a)): return rgn = self.view.extract_scope(s.a) text = self.view.substr(rgn) match = re.match(r'@due(\([^@\n]*\))[\s$]*', text) # print(s, rgn, text) if not match: return # print(match.group(1)) date_format = self.view.settings().get('date_format', '(%y-%m-%d %H:%M)') start = rgn.a + 5 # within parenthesis date, error, region = expand_short_date(self.view, start, start, datetime.now(), date_format) if date == match.group(1).strip(): return self.phantoms.update([sublime.Phantom( sublime.Region(region.b - 1), date.strftime(date_format).strip('()') if date else '{0}:<br> days:\t{1}<br> hours:\t{2}<br> minutes:\t{3}<br>'.format(*error) if len(error) == 4 else '{0}:<br> year:\t{1}<br> month:\t{2}<br> day:\t{3}<br> HH:\t{4}<br> MM:\t{5}<br>'.format(*error), sublime.LAYOUT_INLINE)])
def update_phantoms(self, view, phantoms): if not self.loaded: return thmname = get_settings(view, 'anaconda_linter_phantoms_theme', 'phantom') tplname = get_settings(view, 'anaconda_linter_phantoms_template', 'default') thm = self.themes.get(thmname, self.themes['phantom']) tpl = self.templates.get(tplname, self.templates['default']) vid = view.id() if vid not in self.phantomsets: self.phantomsets[vid] = sublime.PhantomSet(view, 'Anaconda') sublime_phantoms = [] for item in phantoms: region = view.full_line(view.text_point(item['line'], 0)) context = {'css': thm} context.update(item) content = tpl.safe_substitute(context) sublime_phantoms.append( sublime.Phantom(region, content, sublime.LAYOUT_BLOCK)) self.phantomsets[vid].update(sublime_phantoms)
def _render(self): settings = self.view.settings() enabled = settings.get(OPT_ENABLED, True) self._clear() if not enabled: return phantoms = [] current_line = self.view.rowcol(self.view.sel()[0].begin())[0] current_line_char = settings.get(OPT_CURRENT_CHAR, "0") lines = self.view.lines(self.view.visible_region()) for line in lines: line_number = self.view.rowcol(line.a)[0] value = self._value( line_number, current_line, current_line_char) phantoms.append( sublime.Phantom( line, self._tpl(value, line_number == current_line), sublime.LAYOUT_INLINE)) self._phantoms.update(phantoms) self.view.set_viewport_position( (0, self.view.viewport_position()[1]), False) sublime.set_timeout(self._clear, settings.get(OPT_CLEAR_TIMEOUT, 1000))
def add_labels(self, edit: sublime.Edit, regions: List[sublime.Region], labels: str) -> None: """Replaces the given regions with labels""" phantoms = [] # List[sublime.Phantom] for idx, region in enumerate(regions): label = labels[last_index + idx - len(regions)] if self.hinting_mode == HINTING_MODE_REPLACE_CHAR: # if the target char is Chinese, # use full-width label to prevent from content position shifting if CHINESE_REGEX_OBJ.match(self.view.substr(region)): label = char_width_converter.h2f(label) self.view.replace(edit, region, label) elif self.hinting_mode == HINTING_MODE_INLINE_PHANTOM: phantoms.append( sublime.Phantom( region, PHANTOM_TEMPLATE.format(css=self.phantom_css, label=label), sublime.LAYOUT_INLINE, )) ps = get_view_phantom_set(self.view) ps.update(phantoms)
def run(self, edit): phantoms = [] self.view.erase_phantoms('git-blame') for region in self.view.sel(): line = self.view.line(region) (row, col) = self.view.rowcol(region.begin()) full_path = self.view.file_name() result = self.get_blame(int(row) + 1, full_path) if not result: # Unable to get blame return sha, user, date, time = self.parse_blame(result) settings = sublime.load_settings('Preferences.sublime-settings') scheme_color = settings.get('gitblame.scheme') or 'dark' body = template.format(sha=sha, user=user, date=date, time=time, scheme=template_scheme.get( scheme_color, '')) phantom = sublime.Phantom(line, body, sublime.LAYOUT_BLOCK, self.on_phantom_close) phantoms.append(phantom) self.phantom_set.update(phantoms)
def build_phantoms(self): """Add links to side-by-side base file for editing this setting in the user file.""" if self.view.is_loading(): sublime.set_timeout(self.build_phantoms, 20) return l.debug("Building phantom set for view %r", self.view.file_name()) key_regions = self.view.find_by_selector(KEY_SCOPE) phantoms = [] for region in key_regions: key_name = self.view.substr(region) phantom_region = sublime.Region(region.end() + 1) # before colon content = "<a href=\"edit:{0}\">✏</a>".format( html.escape(key_name)) phantoms.append( sublime.Phantom( region=phantom_region, content=PHANTOM_TEMPLATE.format(content), layout=sublime.LAYOUT_INLINE, # use weak reference for callback # to allow for phantoms to be cleaned up in __del__ on_navigate=WeakMethodProxy(self.on_navigate), )) l.debug("Made %d phantoms", len(phantoms)) self.phantom_set.update(phantoms)
def handle_response(self, response: Optional[List[dict]]) -> None: color_infos = response if response else [] phantoms = [] for color_info in color_infos: color = color_info['color'] red = color['red'] * 255 green = color['green'] * 255 blue = color['blue'] * 255 alpha = color['alpha'] content = """ <style>html {{padding: 0}}</style> <div style='padding: 0.4em; margin-top: 0.2em; border: 1px solid color(var(--foreground) alpha(0.25)); background-color: rgba({}, {}, {}, {})'> </div>""".format(red, green, blue, alpha) range = Range.from_lsp(color_info['range']) region = range_to_region(range, self.view) phantoms.append( sublime.Phantom(region, content, sublime.LAYOUT_INLINE)) self.phantom_set.update(phantoms)
def build_phantoms(view, errs, formatter): phantoms = [] show_focus_links = len(errs) > 1 for tbck in errs: line = tbck['line'] text = tbck['text'] testcase = tbck.get('testcase') if text == '': continue pt = view.text_point(line - 1, 0) indentation = get_indentation_at(view, pt) text = formatter.format_text(text, indentation) if show_focus_links and testcase: focus_link = ( ' <a href="focus:{}">focus test</a>'.format(testcase)) lines = text.split('<br />') text = '<br />'.join([lines[0] + focus_link] + lines[1:]) phantoms.append( sublime.Phantom(sublime.Region(pt, view.line(pt).b), ('<body id=inline-error>' + STYLESHEET + '<div class="error">' + '<span class="message">' + text + '</span>' + '</div>' + '</body>'), sublime.LAYOUT_BELOW, _on_navigate)) return phantoms
def run(self, edit): phantoms = [] self.view.erase_phantoms('git-blame') #Before adding the phantom, see if the current phantom that is displayed is at the same spot at the selection if self.phantom_set.phantoms and self.view.line( self.view.sel()[0]) == self.view.line( self.phantom_set.phantoms[0].region): self.phantom_set.update(phantoms) return for region in self.view.sel(): line = self.view.line(region) (row, col) = self.view.rowcol(region.begin()) full_path = self.view.file_name() result = self.get_blame(int(row) + 1, full_path) if not result: # Unable to get blame return sha, user, date, time = self.parse_blame(result) body = template_one.format(sha=sha, user=user, date=date, time=time, stylesheet=stylesheet_one) phantom = sublime.Phantom(line, body, sublime.LAYOUT_BLOCK, self.on_phantom_close) phantoms.append(phantom) self.phantom_set.update(phantoms)
def run(self, edit, output): phantoms = [ sublime.Phantom(self.get_region(x['line']), x['html'], sublime.LAYOUT_INLINE) for x in output ] self.view.erase_phantoms('import_cost') self.phantom_set.update(phantoms)
def update_phantoms(self): stylesheet = """ <style> div.lt-error { padding: 0.4rem 0 0.4rem 0.7rem; margin: 0.2rem 0; border-radius: 2px; } div.lt-error span.message { padding-right: 0.7rem; } div.lt-error a { text-decoration: inherit; padding: 0.35rem 0.7rem 0.45rem 0.8rem; position: relative; bottom: 0.05rem; border-radius: 0 2px 2px 0; font-weight: bold; } html.dark div.lt-error a { background-color: #00000018; } html.light div.lt-error a { background-color: #ffffff18; } </style> """ for file, errs in self.errs_by_file.items(): view = self.window.find_open_file(file) if view: buffer_id = view.buffer_id() if buffer_id not in self.phantom_sets_by_buffer: phantom_set = sublime.PhantomSet(view, "lt_exec") self.phantom_sets_by_buffer[buffer_id] = phantom_set else: phantom_set = self.phantom_sets_by_buffer[buffer_id] phantoms = [] for line, column, text, error_class in errs: pt = view.text_point(line - 1, column - 1) html_text = html.escape(text, quote=False) phantom_content = """ <body id="inline-error"> {stylesheet} <div class="lt-error {error_class}"> <span class="message">{html_text}</span> <a href="hide">{cancel_char}</a> </div> </body> """.format(cancel_char=chr(0x00D7), **locals()) phantoms.append(sublime.Phantom( sublime.Region(pt, view.line(pt).b), phantom_content, sublime.LAYOUT_BELOW, on_navigate=self.on_phantom_navigate)) phantom_set.update(phantoms)
def create_phantom(view: sublime.View, diagnostic: Diagnostic) -> sublime.Phantom: region = range_to_region(diagnostic.range, view) # TODO: hook up hide phantom (if keeping them) content = create_phantom_html(diagnostic.message) return sublime.Phantom( region, '<p>' + content + '</p>', sublime.LAYOUT_BELOW, lambda href: on_phantom_navigate(view, href, region.begin()))