I'm building a piece of educational software and I have pseudocode on the output where I would like to highlight a specific line of code depending on which piece of code is running.
First round()
.....
--> highlight this line and the next after it moves
Output: First round has just started
The furthest I got was doing some bash highlighting but that required me to print out the line twice. Once in black and second in a different colour. Any suggestions on how to highlight a specific line? Any help is appreciated.
EDIT: I'm using Pyqt as my GUI toolkit so my output will be displayed in a textbox
You have to get the QTextBlock object that correspond to the line (*) you want to highlight or unhighlight and use a QTextCursor to change the format of that line:
def setLineFormat(self, lineNumber, format):
cursor = QTextCursor(self.textEdit.document().findBlockByNumber(lineNumber))
cursor.setBlockFormat(format)
# with
format = QTextBlockFormat()
format.setBackground(Qt.yellow)
# or
format.clearBackground()
If you are using QSyntaxHighlighter, you could also store the state of the line in the QTextBlock with QTextBlock.setUserState() or setUserData, handle that state in the QSyntaxHighlighter.highlightBlock() method as part of the syntax highlighting, and force the previous and the current lines to be repainted with QSyntaxHighlighter.rehighlightBlock().
*: lines==blocks unless you use a custom document layout
Related
I was making a templated list of methods in vim for a python project. I added lines between each method and wanted to add a pass to each method for now-until I implement the method, this will still be interpretable python code. In vim I know how to edit spatially contiguous lines of a file using :10,17s/<search regex>/<substitute>/ but after doing my edits to add empty lines between methods, I needed to insert the a pass every 3rd line. The way I found to do this used pipes and & via:
:10s/<search regex>/<substitute>|13&|16& etc. I had maybe 15 of the ampersands chained together to get this to work. Is there a more succint way to get this behaviour in vim?
To address comment, here is a minimal example, in the file myfile.py I have:
def _fun1(self):
def _fun2(self):
def _fun3(self):
def _fun4(self):
...etc
On the 2nd line, the 5th line, the 8th line, etc. I want to insert pass (w/4 spaces before to keep consistent spacings), /i have this up to _fun15(self): so would like to get the behavior w/o 14 |lineNo&s chained together. Perhaps an incrementing feature w/a variable for the line numbers or some other code that creates the behavior.
Here is one possible way:
:g/def _fun/normal! opass
On each line matching def _fun…
open a new line below…
and insert pass.
If you want to have one single line between each stub:
:g/def _fun/normal! opass^OJ^Ox
On each line matching def _fun…
open a new line below…
insert pass…
leave insert mode for a single command…
join the line below with the current line…
leave insert mode for a single command…
and remove that pesky <Space>.
Record a macro
qajopass<Esc>jq
Now execute it by running #a (next time you can use ##).
As #midor said it can be then used with :g command in form of:
:g/def _fun\d\+/norm #a
To execute this macro on all matching lines.
To put 'pass' with indentation below each function definition I would use:
:g/^def/put =' pass'
^ ........... begining of each line
put ......... puts contents bellow
To squeeze blank lines:
:g/^$/,/./-1j
a global command the gets from each empty line ^$
until next non-empty line minus one, performs a join command
I am trying to adapt a plugin for automated text replacement in a Sublime Text 3 Plugin. What I want it to do is paste in text from the clipboard and make some automatic text substitutions
import sublime
import sublime_plugin
import re
class PasteAndEscapeCommand(sublime_plugin.TextCommand):
def run(self, edit):
# Position of cursor for all selections
before_selections = [sel for sel in self.view.sel()]
# Paste from clipboard
self.view.run_command('paste')
# Postion of cursor for all selections after paste
after_selections = [sel for sel in self.view.sel()]
# Define a new region based on pre and post paste cursor positions
new_selections = list()
delta = 0
for before, after in zip(before_selections, after_selections):
new = sublime.Region(before.begin() + delta, after.end())
delta = after.end() - before.end()
new_selections.append(new)
# Clear any existing selections
self.view.sel().clear()
# Select the saved region
self.view.sel().add_all(new_selections)
# Replace text accordingly
for region in self.view.sel():
# Get the text from the selected region
text = self.view.substr(region)
# Make the required edits on the text
text = text.replace("\\","\\\\")
text = text.replace("_","\\_")
text = text.replace("*","\\*")
# Paste the text back to the saved region
self.view.replace(edit, region, text)
# Clear selections and set cursor position
self.view.sel().clear()
self.view.sel().add_all(after_selections)
This works for the most part except I need to get the new region for the edited text. The cursor will be placed to the location of the end of the pasted text. However since I am making replacements which always make the text larger the final position will be inaccurate.
I know very little about Python for Sublime and like most others this is my first plugin.
How do I set the cursor position to account for the size changes in the text. I know I need to do something with the after_selections list as I am not sure how to create new regions as they were created from selections which are cleared in an earlier step.
I feel that I am getting close with
# Add the updated region to the selection
self.view.sel().subtract(region)
self.view.sel().add(sublime.Region(region.begin()+len(text)))
This, for some yet unknown to me reason, places the cursor at the beginning and end of the replaced text. A guess would be that I am removing the regions one by one but forgetting some "initial" region that also exists.
Note
I am pretty sure the double loop in the code in the question here is redundant. but that is outside the scope of the question.
I think your own answer to your question is a good one and probably the way I would go if I was to do something like this in this manner.
In particular, since the plugin is modifying the text on the fly and making it longer, the first way that immediately presents itself as a solution other than what your own answer is doing would be to track the length change of the text after the replacements so you can adjust the selections accordingly.
Since I can't really provide a better answer to your question than the one you already came up with, here's an alternative solution to this instead:
import sublime
import sublime_plugin
class PasteAndEscapeCommand(sublime_plugin.TextCommand):
def run(self, edit):
org_text = sublime.get_clipboard()
text = org_text.replace("\\","\\\\")
text = text.replace("_","\\_")
text = text.replace("*","\\*")
sublime.set_clipboard(text)
self.view.run_command("paste")
sublime.set_clipboard(org_text)
This modifies the text on the clipboard to be quoted the way you want it to be quoted so that it can just use the built in paste command to perform the paste.
The last part puts the original clipboard text back on the clipboard, which for your purposes may or may not be needed.
So, one approach for this would be to make new regions as the replaced text is created using their respective lengths as starting positions. Then once the loop is complete clear all existing selections and set the new one we created in the replacement loop.
# Replace text accordingly
new_replacedselections = list()
for region in self.view.sel():
# Get the text from the selected region
text = self.view.substr(region)
# Make the required edits on the text
text = text.replace("\\","\\\\") # Double up slashes
text = text.replace("*","\\*") # Escape *
text = text.replace("_","\\_") # Escape _
# Paste the text back to the saved region
self.view.replace(edit, region, text)
# Add the updated region to the collection
new_replacedselections.append(sublime.Region(region.begin()+len(text)))
# Set the selection positions after the new insertions.
self.view.sel().clear()
self.view.sel().add_all(new_replacedselections)
I'm writing a simple plugin for Sublime Text 3 and need to get the contents of the currently selected line.
I can use
selection = sublime.Region(self.view.sel()[0].begin(), self.view.sel()[0].end())
to get the currently highlighted section, but is there a way to select or return the contents of the entire current line without manually selecting all of it?
The 'duplicate' example on this page does what I want, but seems to be broken in ST3. When I run it I get the error TypeError: run() missing 1 required positional argument: 'args'.
I believe what you're after is view.substr(view.line(view.sel()[0]))
This will return the text of the current line.
I am using Vim with neocomplcache plugin, its feature of usage prompt while completion confused me so much.
It behaved like this: I typed <C-X><C-U> when the cursor was at the end of os.path., then not only completion candidates was listed under the line, but also a horizontal split
which contained docstring of the first candidate appeared
at the top. My question is: how to get rid of this feature so that I only get code completion without the usage prompt?
It's all because preview is in completeopt by default, you can see its value by type
command :set completeopt and the result should be completeopt=menu,preview.
What we need is just menu, so cut the preview, add this line in your vimrc:
set completeopt-=preview
vim help reference:
*'completeopt'* *'cot'*
'completeopt' 'cot' string (default: "menu,preview")
global
{not available when compiled without the
|+insert_expand| feature}
{not in Vi}
A comma separated list of options for Insert mode completion
|ins-completion|. The supported values are:
menu Use a popup menu to show the possible completions. The
menu is only shown when there is more than one match and
sufficient colors are available. |ins-completion-menu|
menuone Use the popup menu also when there is only one match.
Useful when there is additional information about the
match, e.g., what file it comes from.
longest Only insert the longest common text of the matches. If
the menu is displayed you can use CTRL-L to add more
characters. Whether case is ignored depends on the kind
of completion. For buffer text the 'ignorecase' option is
used.
preview Show extra information about the currently selected
completion in the preview window. Only works in
combination with "menu" or "menuone".
I am trying to get Vim to syntax highlight any file that ends with extension .Rtex in the following way:
All top level text is highlighted as TeX
Exception: any text enclosed in \begin{python}...\end{python} is highlighted as Python
I am able to achieve each of these criteria individually, but unable to achieve both simultaneously. I think that somehow the TeX highlighting overrides my Python-highlighted regions, or prevents them from taking effect, and I am stuck trying to figure out how.
First step: edit .vimrc to give files with extension .Rtex the filetype rtex:
au BufRead *.Rtex setf rtex
Second step: create ~/.vim/syntax/rtex.vim. It is the contents of this file that will determine how to highlight .Rtex files.
Third step: enable general top-level TeX highlighting, by making rtex.vim look like this:
runtime! syntax/tex.vim
If I now open a .Rtex file, the entire file is highlighted as TeX, including any text within \begin{python}...\end{python}, as expected.
Fourth step: follow the instructions in Vim's :help syn-include to include python highlighting and apply it to all regions delimited by \begin{python} and \end{python}. My rtex.vim file now looks like this:
runtime! syntax/tex.vim
unlet! b:current_syntax
syntax include #Python syntax/python.vim
syntax region pythonCode start="\\begin{python}" end="\\end{python}" contains=#Python
The unlet! b:current_syntax command is meant to force the python.vim syntax file to execute even though an existing syntax (TeX) is already active.
Problem: If I now open a .Rtex file, the entire file is still highlighted only as TeX. The \begin{python}...\end{python} region seems to have no effect.
Experiment: If I remove or comment out the runtime! command, I do get python highlighting, within the \begin{python}...\end{python} regions, exactly as desired, but of course no TeX highlighting in the remainder of the document. I therefore conclude that the TeX highlighting is somehow responsible for preventing the python regions from taking effect.
Can a Master of Vim offer me any suggestions? I am currently stumped. I have looked at several pages and stackoverflow questions that seem relevant, but none of them have so far led to a solution:
http://vim.wikia.com/wiki/Different_syntax_highlighting_within_regions_of_a_file
Embedded syntax highligting in Vim
VIM syntax highlighting of html nested in yaml
After some more study of the manual, and much more trial and error, I have finally answered my own question (a simultaneously embarrassing and sublime accomplishment), which I now preserve here for posterity.
Basically, I think the problem is that the python highlighting wouldn't take effect because the pythonCode region was itself contained in a region or highlight group defined by tex.vim, so it wasn't top-level. The solution is to also include (rather than just runtime) tex.vim, giving it a name like #TeX, and then add containedin=#TeX to my python region definition. So syntax/rtex.vim now looks like this:
let b:current_syntax = ''
unlet b:current_syntax
runtime! syntax/tex.vim
let b:current_syntax = ''
unlet b:current_syntax
syntax include #TeX syntax/tex.vim
let b:current_syntax = ''
unlet b:current_syntax
syntax include #Python syntax/python.vim
syntax region pythonCode matchgroup=Snip start="\\begin{python}" end="\\end{python}" containedin=#TeX contains=#Python
hi link Snip SpecialComment
let b:current_syntax = 'rtex'
And this works! I'm not sure if all of those unlet b:current_syntax commands are strictly necessary. I also gave the python region delimiters a matchgroup (Snip) so that they can be highlighted themselves (with the SpecialComment color), rather than left just plain, which is apparently what happens by default, since they are no longer considered part of the TeX.
Now it is a trivial thing to add more highlight regions for different languages (e.g., \begin{Scode}...\end{Scode}), which is great if you're getting into literate programming -- the original motivation for my question.
Edit: here is a screenshot showing how it works with Python and R code embedded in a TeX document:
I don't know if it helps, but a hack I use with my Rnw files that use both tex and rnoweb features is as follows:
au BufEnter *.Rnw set filetype=tex | set filetype=rnoweb
Would an adapted version work in your case?