-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How can I run a script at Notepad++ startup? #356
Comments
Yes!
your script, called def bar(): pass in user import foo
foo.bar() This is a minimal way to do it. I'll post some more advanced examples later. |
So I looked in BracketHighlighter as an example and I don't see a "controlling" function? I assume that's because it uses callsbacks to do it's work? |
Easiest solution I found:
|
This is more of a brute force way, not a smart way. :-( |
I looked at this sample, and you're right -- it isn't set up in the best way for getting it running automatically at startup. |
Here's another way, using Python classes, to architect a script so that it is callable by another script: In class Foo2(object):
def __init__(self):
print('fubar') and then in the calling script: import foo2
foo2.Foo2() This is merely a "classed" example, very similar to how I showed it without classes EARLIER. These techniques do the job; they nicely allow another script to call them. If we need a technique where a script runs, and parts of it "hang around" (e.g., the bracket-highlighter script), then we have to tweak the technique a bit: In class Foo3(object):
def __init__(self):
print('fubar')
# e.g. set up a Notepad++ callback here
FOO3 = Foo3() and then in the calling script: from foo3 import FOO3 The The new bracket-highlighter that I will post here will use the technique shown here for "foo3". |
Here's a new version of The modernization allows easier execution at Notepad++ startup via a line in user The script listing: # -*- coding: utf-8 -*-
#########################################
#
# BracketHighlighter (BH)
#
#########################################
# references:
# https://github.com/bruderstein/PythonScript/issues/356
# https://community.notepad-plus-plus.org/topic/14501/has-a-plugin-like-sublime-plugin-brackethighlighter
# for newbie info on PythonScripts, see https://community.notepad-plus-plus.org/topic/23039/faq-desk-how-to-install-and-run-a-script-in-pythonscript
#-------------------------------------------------------------------------------
# to execute, use this in (e.g.) user startup.py:
# from BracketHighlighter import BRACKET_HIGHLIGHTER
# another execution option would be to copy and then paste that line into the PythonScript console >>> box
# note: if running via startup.py, need to make sure that "Initialisation" for "Python Script Configuration" is set to "ATSTARTUP" and not "LAZY".
#-------------------------------------------------------------------------------
from Npp import *
#-------------------------------------------------------------------------------
class BH(object):
def __init__(self):
self.settings_dict = dict()
self.settings_dict['indic_for_box_at_caret'] = notepad.allocateIndicator(1)
assert self.settings_dict['indic_for_box_at_caret'] is not None
for editorX in (editor1, editor2):
self.indicatorOptionsSet(self.settings_dict['indic_for_box_at_caret'], INDICATORSTYLE.STRAIGHTBOX, (238,121,159), 0, 255, True, editorX) # white box rimmed in "pale violet red 2"
self.settings_dict['last_modificationType_for_hack'] = None
editor.callbackSync(self.updateui_callback, [SCINTILLANOTIFICATION.UPDATEUI]) # install callback
editor.callbackSync(self.modified_callback, [SCINTILLANOTIFICATION.MODIFIED]) # may not need to be "Sync", but for now we'll make it that way
def updateui_callback(self, args):
# hack, see https://community.notepad-plus-plus.org/topic/12360/vi-simulator-how-to-highlight-a-word/27, look for "16400" in code:
if args['updated'] == UPDATE.CONTENT and self.settings_dict['last_modificationType_for_hack'] == (MODIFICATIONFLAGS.CHANGEINDICATOR | MODIFICATIONFLAGS.USER): return
for (editorX, pos_range_tuple_list) in self.getViewableEditorAndRangeTupleListList(True):
# clear out any existing highlighting in areas the user can currently see
for (start_pos, end_pos) in pos_range_tuple_list:
editorX.setIndicatorCurrent(self.settings_dict['indic_for_box_at_caret'])
editorX.indicatorClearRange(start_pos, end_pos - start_pos)
for (start_pos, end_pos) in pos_range_tuple_list:
if start_pos <= editorX.getCurrentPos() <= end_pos:
(box_start_offset, box_end_offset) = self.containing_box_indices_into_string(
editorX.getTextRange(start_pos, end_pos),
editorX.getCurrentPos() - start_pos
)
if box_start_offset != None:
size_of_box_in_chars = box_end_offset - box_start_offset
if size_of_box_in_chars <= 2:
pass # rather pointless to box in if the opening and closing delims are right next to each other
else:
editorX.setIndicatorCurrent(self.settings_dict['indic_for_box_at_caret'])
editorX.indicatorFillRange(start_pos + box_start_offset, size_of_box_in_chars)
def modified_callback(self, args):
self.settings_dict['last_modificationType_for_hack'] = args['modificationType']
def indicatorOptionsSet(self, indicator_number, indicator_style, rgb_color_tup, alpha, outline_alpha, draw_under_text, which_editor=editor):
which_editor.indicSetStyle(indicator_number, indicator_style) # e.g. INDICATORSTYLE.ROUNDBOX
which_editor.indicSetFore(indicator_number, rgb_color_tup)
which_editor.indicSetAlpha(indicator_number, alpha) # integer
which_editor.indicSetOutlineAlpha(indicator_number, outline_alpha) # integer
which_editor.indicSetUnder(indicator_number, draw_under_text) # boolean
def containing_box_indices_into_string(self, str_containing_caret, caret_index_into_str):
class Stack:
def __init__(self): self.clear()
def isEmpty(self): return self.size() == 0
def push(self, item): self.items.append(item)
def pop(self): return None if self.size() == 0 else self.items.pop()
def peek(self): return None if self.size() == 0 else self.items[self.size() - 1]
def size(self): return len(self.items)
def clear(self): self.items = []
retval = (None, None) # default to no valid box
get_opening_char_via_closing_char_dict = {
')' : '(',
']' : '[',
'}' : '{',
}
get_closing_char_via_opening_char_dict = dict((v, k) for (k, v) in get_opening_char_via_closing_char_dict.items())
closing_chars = get_opening_char_via_closing_char_dict.keys()
opening_chars = get_opening_char_via_closing_char_dict.values()
box_ending_index = -1
box_starting_index = -1
stack = Stack()
for j in range(caret_index_into_str, len(str_containing_caret)):
c = str_containing_caret[j]
if c in closing_chars:
if stack.isEmpty():
box_ending_index = j
break
else:
if stack.peek() == get_opening_char_via_closing_char_dict[c]:
stack.pop()
else:
break # unbalanced
elif c in opening_chars:
stack.push(c)
if box_ending_index != -1:
stack.clear()
box_starting_index = -1
for j in range(caret_index_into_str - 1, -1, -1):
c = str_containing_caret[j]
if c in opening_chars:
if stack.isEmpty():
box_starting_index = j
break
else:
if stack.peek() == get_closing_char_via_opening_char_dict[c]:
stack.pop()
else:
break # unbalanced
elif c in closing_chars:
stack.push(c)
if box_ending_index != -1:
if box_starting_index != -1:
if str_containing_caret[box_ending_index] == get_closing_char_via_opening_char_dict[str_containing_caret[box_starting_index]]:
retval = (box_starting_index, box_ending_index + 1)
return retval
def fileIsCloned(self, file_name_to_test):
retval = False
clone_detect_dict = {}
file_tup_list = notepad.getFiles()
for tup in file_tup_list:
(filename, _, _, _) = tup
if filename not in clone_detect_dict:
clone_detect_dict[filename] = 0
else:
clone_detect_dict[filename] += 1
if filename == file_name_to_test: break
if file_name_to_test in clone_detect_dict:
if clone_detect_dict[file_name_to_test] >= 1: retval = True
return retval
def fileIsClonedAndIsActiveInBothViews(self, file_name_to_test):
retval = False
if editor1 and editor2:
# both views are in use
if self.fileIsCloned(file_name_to_test):
curr_doc_index_main_view = notepad.getCurrentDocIndex(0)
curr_doc_index_2nd_view = notepad.getCurrentDocIndex(1)
main_view_active_doc_bool = False
secondary_view_active_doc_bool = False
file_tup_list = notepad.getFiles()
for tup in file_tup_list:
(filename, _, index_in_view, view_number) = tup
if filename == file_name_to_test:
if view_number == 0:
if index_in_view == curr_doc_index_main_view:
main_view_active_doc_bool = True
elif view_number == 1:
if index_in_view == curr_doc_index_2nd_view:
secondary_view_active_doc_bool = True
if main_view_active_doc_bool and secondary_view_active_doc_bool:
retval = True
break
return retval
def consolidate_range_tuple_list(self, range_tup_list):
sorted_range_tup_list = sorted(range_tup_list) # sort criteria is first element of tuple in list
saved_2element_list = list(sorted_range_tup_list[0])
for (start, end) in sorted_range_tup_list:
if start <= saved_2element_list[1]:
saved_2element_list[1] = max(saved_2element_list[1], end)
else:
yield tuple(saved_2element_list)
saved_2element_list[0] = start
saved_2element_list[1] = end
yield tuple(saved_2element_list)
def getViewableEditorAndRangeTupleListList(self, work_across_both_views):
retval = []
# retval looks like these examples:
# [ ( editor, [ (0, 1000), (2020, 3000) ] ) ]
# [ ( editor1, [ (0, 1000), (2020, 3000) ] ), ( editor2, [ (4000, 5000), (6020, 7000) ] ) ]
both_views_open = True if editor1 and editor2 else False
curr_file_active_in_both_views = self.fileIsClonedAndIsActiveInBothViews(notepad.getCurrentFilename()) if both_views_open else False
if both_views_open:
ed1_range_tup_list = self.get_onscreen_pos_tup_list(editor1)
ed2_range_tup_list = self.get_onscreen_pos_tup_list(editor2)
if curr_file_active_in_both_views:
range_tup_list = list(self.consolidate_range_tuple_list(ed1_range_tup_list + ed2_range_tup_list))
retval.append((editor, range_tup_list))
elif both_views_open and work_across_both_views:
retval.append((editor1, ed1_range_tup_list))
retval.append((editor2, ed2_range_tup_list))
else:
range_tup_list = self.get_onscreen_pos_tup_list(editor)
retval.append((editor, range_tup_list))
return retval
def get_onscreen_pos_tup_list(self, which_editor): # which_editor is editor1 or editor2 (or maybe even just plain editor)
# loosely based upon the N++ source for SmartHighlighter::highlightViewWithWord()
retval_tup_list = list()
temp_tup_list = []
MAXLINEHIGHLIGHT = 400
firstLine = which_editor.getFirstVisibleLine()
currentLine = firstLine
nbLineOnScreen = which_editor.linesOnScreen()
nrLines = min(nbLineOnScreen, MAXLINEHIGHLIGHT) + 1
lastLine = firstLine + nrLines
prevDocLineChecked = -1
break_out = False
while currentLine < lastLine:
docLine = which_editor.docLineFromVisible(currentLine)
if docLine != prevDocLineChecked:
prevDocLineChecked = docLine
startPos = which_editor.positionFromLine(docLine)
endPos = which_editor.positionFromLine(docLine + 1)
if endPos == -1:
endPos = which_editor.getTextLength() - 1
break_out = True
if endPos > startPos: temp_tup_list.append((startPos, endPos))
if break_out: break
currentLine += 1
if len(temp_tup_list) > 0:
retval_tup_list = list(self.consolidate_range_tuple_list(temp_tup_list))
return retval_tup_list
#-------------------------------------------------------------------------------
BRACKET_HIGHLIGHTER = BH() |
Do I need to so something to include the samples directory itself so it can find BracketHighlighter? |
I added
Which helped but then I got an error in the new Brackethighlighter itself:
|
@chcg Maybe Pythonscript could be modified so that this wouldn't happen?
Yes, this is the right idea.
Your PythonScript is too old; doesn't have support for |
@Skrell - Is there anything else to do about this, if not, can you please close this issue? |
Regarding the import of examples: |
from Samples.BracketHighlighter import BRACKET_HIGHLIGHTER
BRACKET_HIGHLIGHTER.indicatorOptionsSet(
BRACKET_HIGHLIGHTER.settings_dict['indic_for_box_at_caret'], # indicator number
16, # INDIC_FULLBOX
(255,255,0), # yellow
60, # alpha
20, # outline alpha
False) # draw under text Test OK in PS3. |
You mean the I tried creating an empty file there: and then trying this in the PS console: resulted in:
@mpheath 's technique can be used to solve the problem, but I was just curious about the Personally, I'm currently using the "modify sys.path" technique for my own scripts, see HERE, but I'm always looking to improve the way I do things, if there's a better way. |
Yes, but |
I don't understand how that answers my question, but no worries, I'll just keep going with my |
The import would be |
I'm assuming I add some "run" command in startup.py after changing initialization to ONSTARTUP instead of LAZY ?
The text was updated successfully, but these errors were encountered: