External Tools : manipulate current buffer (use as STDIN, insert STDOUT into it)
Answered
I would like to run some external command, using the current buffer (or current selection) as STDIN, and insert command's output back into the editor.
Example 1 :
- select some lines in my current buffer
- run the equivalent of "cat selection | sort" on it
- in my currnet buffer, replace current selection with the output of this command
Example 2 :
- in my current (unsaved) buffer, use buffer's content as STDIN, and $LineNumber$ as a parameter to an external tool
- generate a function header comment for the function at line $LineNumber$ in current buffer
- insert that comment at caret position
I may have missed something, but I couldn't find those options in "External Tools".
Is there a way to do this ?
Please sign in to leave a comment.
No, it's not possible via External Tools, related requests:
https://youtrack.jetbrains.com/issue/IDEA-76371
https://youtrack.jetbrains.com/issue/IDEABKL-6913
Ok, too bad.
Thank you for your answer !
I know this is an old issue, but just in case someone found this while searching for the same problem....
I have a crude work-around for sending STDIN to an external command. It uses the bash shell and xclip, so this may not work for Mac or Windows users.
In the Edit Tool window, set:
Replace some-command with a command that reads STDIN and writes STDOUT.
Please note that the single quotes around $SelectedText$ may not do a perfect job of escaping the selected text. Selecting this text and running your external command will definitely ruin your day:
Also, xclip can be fairly magical in its behavior. You may need to set "-selection" to XA_CLIPBOARD.
You should now be able to highlight some text, then select Tools -> External Tools -> your-command. The output to the command will be in the clipboard buffer. Use middle click (or CTRL-V if you're lucky) to paste.
I will follow up on what Pdimarco said (again with the acknowledgment that this is an old thread, but may help others searching).
I realized (thanks to the solution from @Pdimarco) that intellij gives you all the variables you need to accomplish this task. Since you can use: "$FilePath$" $SelectionStartLine$ $SelectionStartColumn$ $SelectionEndLine$ $SelectionEndColumn$ to indicate exactly where in a file you need to replace a piece of text, you can call your helper script with those arguments and it can replace that chunk of the file.
I generalized it a bit and wrote a helper script I called scalpel that expects to be called with the name of another script to execute, followed by the special args mentioned above, and it will run that script, passing it the section as stdin, then replace it with whatever the script spits out on stdout. At the end of the day, it's effectively what I wanted out of intellij. Here's what I've got for that script. No guarantees that it works for 100% of cases:
#!/usr/bin/env python3
import sys
import subprocess
def extract_text(file_path, start_line, start_col, end_line, end_col):
"""Extract the text from the specified section of the file."""
with open(file_path, 'r') as file:
lines = file.readlines()
selected_text = []
if start_line == end_line:
selected_text.append(lines[start_line][start_col:end_col])
else:
selected_text.append(lines[start_line][start_col:])
for i in range(start_line + 1, end_line):
selected_text.append(lines[i])
selected_text.append(lines[end_line][:end_col])
return ''.join(selected_text)
def replace_text_in_file(file_path, start_line, start_col, end_line, end_col, new_text):
file_contents = ""
with open(file_path, 'r') as file:
file_contents = file.readlines()
file_part1 = ""
line_iter = 0
while line_iter < start_line:
file_part1 += file_contents[line_iter]
line_iter += 1
col_iter = 0
while col_iter < start_col:
file_part1 += file_contents[line_iter][col_iter]
col_iter += 1
while line_iter < end_line:
line_iter += 1
file_part2 = ""
col_iter = end_col
file_part2 += file_contents[line_iter][col_iter:]
line_iter += 1
while line_iter < len(file_contents):
file_part2 += file_contents[line_iter]
line_iter += 1
with open(file_path, 'w') as file:
file.write(file_part1 + new_text + file_part2)
def run_script(script_to_run, input_text):
"""Run the given script with the extracted text as input."""
result = subprocess.run([script_to_run], input=input_text, text=True, capture_output=True, shell=True)
return result.stdout
if __name__ == "__main__":
# Get the arguments: script_to_run, file_path, start_line, start_col, end_line, end_col
script_to_run = sys.argv[1]
file_path = sys.argv[2]
start_line = int(sys.argv[3]) - 1
start_col = int(sys.argv[4]) - 1
end_line = int(sys.argv[5]) - 1
end_col = int(sys.argv[6]) - 1
print("Got Arguments: ", sys.argv)
# Step 1: Extract the selected text from the file
selected_text = extract_text(file_path, start_line, start_col, end_line, end_col)
# Step 2: Run the given script with the selected text passed as stdin
processed_text = run_script(script_to_run, selected_text)
# Step 3: Replace the selected text in the file with the processed output
replace_text_in_file(file_path, start_line, start_col, end_line, end_col, processed_text)
I went ahead and packaged up the above script as: https://pypi.org/project/splice-replace