33=================================
44
55File History:
6- Created: 2018-09-29 [0.0.1] GuiDocEditor
7- Created: 2019-04-22 [0.0.1] BackgroundWordCounter
8- Created: 2019-09-29 [0.2.1] GuiDocEditSearch
9- Created: 2020-04-25 [0.4.5] GuiDocEditHeader
10- Rewritten: 2020-06-15 [0.9] GuiDocEditSearch
11- Created: 2020-06-27 [0.10] GuiDocEditFooter
12- Rewritten: 2020-10-07 [1.0b3] BackgroundWordCounter
13- Created: 2023-11-06 [2.2b1] MetaCompleter
14- Created: 2023-11-07 [2.2b1] GuiDocToolBar
6+ Created: 2018-09-29 [0.0.1] GuiDocEditor
7+ Created: 2019-04-22 [0.0.1] BackgroundWordCounter
8+ Created: 2019-09-29 [0.2.1] GuiDocEditSearch
9+ Created: 2020-04-25 [0.4.5] GuiDocEditHeader
10+ Rewritten: 2020-06-15 [0.9] GuiDocEditSearch
11+ Created: 2020-06-27 [0.10] GuiDocEditFooter
12+ Rewritten: 2020-10-07 [1.0b3] BackgroundWordCounter
13+ Created: 2023-11-06 [2.2b1] MetaCompleter
14+ Created: 2023-11-07 [2.2b1] GuiDocToolBar
15+ Extended: 2025-05-18 [2.7rc1] CommandCompleter
1516
1617This file is a part of novelWriter
1718Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
@@ -149,7 +150,7 @@ def __init__(self, parent: QWidget) -> None:
149150 self ._autoReplace = TextAutoReplace ()
150151
151152 # Completer
152- self ._completer = MetaCompleter (self )
153+ self ._completer = CommandCompleter (self )
153154 self ._completer .complete .connect (self ._insertCompletion )
154155
155156 # Create Custom Document
@@ -1079,13 +1080,16 @@ def _docChange(self, pos: int, removed: int, added: int) -> None:
10791080
10801081 if (block := self ._qDocument .findBlock (pos )).isValid ():
10811082 text = block .text ()
1082- if text . startswith ( "@" ) and added + removed == 1 :
1083+ if text and text [ 0 ] in "@%" and added + removed == 1 :
10831084 # Only run on single character changes, or it will trigger
10841085 # at unwanted times when other changes are made to the document
10851086 cursor = self .textCursor ()
10861087 bPos = cursor .positionInBlock ()
10871088 if bPos > 0 and (viewport := self .viewport ()):
1088- show = self ._completer .updateText (text , bPos )
1089+ if text [0 ] == "@" :
1090+ show = self ._completer .updateMetaText (text , bPos )
1091+ else :
1092+ show = self ._completer .updateCommentText (text , bPos )
10891093 point = self .cursorRect ().bottomRight ()
10901094 self ._completer .move (viewport .mapToGlobal (point ))
10911095 self ._completer .setVisible (show )
@@ -2073,13 +2077,13 @@ def _allowAutoReplace(self, state: bool) -> None:
20732077 return
20742078
20752079
2076- class MetaCompleter (QMenu ):
2077- """GuiWidget: Meta Completer Menu
2080+ class CommandCompleter (QMenu ):
2081+ """GuiWidget: Command Completer Menu
20782082
20792083 This is a context menu with options populated from the user's
2080- defined tags. It also helps to type the meta data keyword on a new
2081- line starting with an @ . The updateText function should be called on
2082- every keystroke on a line starting with @.
2084+ defined tags and keys . It also helps to type the meta data keyword
2085+ on a new line starting with @ or % . The update functions should be
2086+ called on every keystroke on a line starting with @ or % .
20832087 """
20842088
20852089 complete = pyqtSignal (int , int , str )
@@ -2088,15 +2092,15 @@ def __init__(self, parent: QWidget) -> None:
20882092 super ().__init__ (parent = parent )
20892093 return
20902094
2091- def updateText (self , text : str , pos : int ) -> bool :
2095+ def updateMetaText (self , text : str , pos : int ) -> bool :
20922096 """Update the menu options based on the line of text."""
20932097 self .clear ()
20942098 kw , sep , _ = text .partition (":" )
20952099 if pos <= len (kw ):
20962100 offset = 0
20972101 length = len (kw .rstrip ())
20982102 suffix = "" if sep else ":"
2099- options = list (filter (
2103+ options = sorted (filter (
21002104 lambda x : x .startswith (kw .rstrip ()), nwKeyWords .VALID_KEYS
21012105 ))
21022106 else :
@@ -2108,7 +2112,7 @@ def updateText(self, text: str, pos: int) -> bool:
21082112 offset = tPos [index ] if lookup else pos
21092113 length = len (lookup )
21102114 suffix = ""
2111- options = list (filter (
2115+ options = sorted (filter (
21122116 lambda x : lookup in x .lower (), SHARED .project .index .getClassTags (
21132117 nwKeyWords .KEY_CLASS .get (kw .strip ())
21142118 )
@@ -2117,13 +2121,57 @@ def updateText(self, text: str, pos: int) -> bool:
21172121 if not options :
21182122 return False
21192123
2120- for value in sorted ( options ) :
2124+ for value in options :
21212125 rep = value + suffix
21222126 action = qtAddAction (self , value )
21232127 action .triggered .connect (qtLambda (self ._emitComplete , offset , length , rep ))
21242128
21252129 return True
21262130
2131+ def updateCommentText (self , text : str , pos : int ) -> bool :
2132+ """Update the menu options based on the line of text."""
2133+ self .clear ()
2134+ cmd , sep , _ = text .partition (":" )
2135+ if pos <= len (cmd ):
2136+ clean = text [1 :].lstrip ()[:6 ].lower ()
2137+ if clean [:6 ] == "story." :
2138+ pre , _ , key = cmd .partition ("." )
2139+ offset = len (pre ) + 1
2140+ length = len (key )
2141+ suffix = "" if sep else ": "
2142+ options = sorted (filter (
2143+ lambda x : x .startswith (key .rstrip ()),
2144+ SHARED .project .index .getStoryKeys (),
2145+ ))
2146+ elif clean [:5 ] == "note." :
2147+ pre , _ , key = cmd .partition ("." )
2148+ offset = len (pre ) + 1
2149+ length = len (key )
2150+ suffix = "" if sep else ": "
2151+ options = sorted (filter (
2152+ lambda x : x .startswith (key .rstrip ()),
2153+ SHARED .project .index .getNoteKeys (),
2154+ ))
2155+ elif pos < 12 :
2156+ offset = 0
2157+ length = len (cmd .rstrip ())
2158+ suffix = ""
2159+ options = list (filter (
2160+ lambda x : x .startswith (cmd .rstrip ()),
2161+ ["%Synopsis: " , "%Short: " , "%Story" , "%Note" ],
2162+ ))
2163+ else :
2164+ return False
2165+
2166+ if options :
2167+ for value in options :
2168+ rep = value + suffix
2169+ action = qtAddAction (self , rep .rstrip (":. " ))
2170+ action .triggered .connect (qtLambda (self ._emitComplete , offset , length , rep ))
2171+ return True
2172+
2173+ return False
2174+
21272175 ##
21282176 # Events
21292177 ##
0 commit comments