Need help to make Maxscript functions recognized by Function List
-
Hello,
I am currently using a User Defined Language setup I found a long time ago for Maxscript, the internal scripting language for 3dsMax. Since the definition is old and the package not so easy to found I shared it on my website here: http://werwackfx.com/index.php/graphictools/maxscript-main/maxscript-articles/73-maxscriptinnotepadpp
This language definition is nice and really helps to work with Notepad++ when developping with Maxscript. Unfortunately it is not complete and the functions are not recognized in the Function Lister.
Can someone help me to write the appropriate regular expressions to make them visible?
Beside it would be very nice if this language could be intergrated by default into NPP package.Here are how the functions look like in the code (I do appologize but I don’t see the balise “Code” to highlight it nor how to attach a file).
Thank you very much for your help.
/*
Block comment
*/– Line comment
– Files extension can be *.ms or *.mcr
global myGlobalVariable
macroScript MyMacroscript category:“MyMacroscript” internalCategory:“MyMacroscript” tooltip: “MyMacroscript” buttonText:“MyMacroscript” Icon:#(“MyMacroscript”,1)
(
– blabla
local lScriptRootDir = getdir #userScripts + “\”local lLauncherFile = lScriptRootDir + "_ScriptLauncher.ms" if doesFileExist lLauncherFile then filein lLauncherFile else if doesFileExist (lLauncherFile + "e") then filein (lLauncherFile + "e") else messagebox ("File \"" + filenameFromPath lLauncherFile + "\" not found in script directory\n\nIf the script has been uninstalled, shortcuts must be removed manually") title:"Warning"
)
macroScript MyMacroscriptWithLessParameters buttonText:“MyMacroscriptWithLessParameters” Icon:#(“MyMacroscript”,1)
(
– blabla
messagebox “toto”
)– functions can be identified by the term “fn” or by “function”, this with the same behavior
– return value is not mandatory
– indentations are not madatory
fn myFunction01 =
(
local lMyVar = “toto” – the use of “local” is not mandatory
return lMyVar
)function myFunction02 myParam =
(
messagebox ("myParam: " + myParam as string)
)function myFunction03 myOptionalParam: myOptionalParam02:“my default value” = – optional parameters ends with : and may be followed by an initisalization value
(
messagebox ("myOptionalParam02: " + myOptionalParam02 as string)
)struct tWkSubstanceObjectBoundaries
(
myVariable, – initialization of the variables is not mandatory
myOtherVariable = undefined,mfDisplayInfos = ( format "\myVariable: %\n" (if undefined != myVariable then myVariable.name else "undefined") ), fn mfInit rScriptRootDir: = ( if unsupplied != rScriptRootDir do mSCRIPTROOTDIR = rScriptRootDir ) -- note that there is no coma at the end of the last variable or function of the structure
)
-
@WerwacK I’ll will give it a try.
-
Hi MAPJe71,
Thanks a lot, that’s very kind of you :)
I think this would also be appreciated by Lukas, a user how also wanted to use NPP with Maxscript: https://notepad-plus-plus.org/community/topic/13259/user-defined-language-problem-with-code-folding
By the way I noticed in the code I submitted above that the single-line comments have been converted to one single character. Line comments are really dash dash: - - (with no space inbetween).
Werwack
-
@WerwacK could you try the following parser:
<parser displayName="MAXScript - Scripting language for AutoDesk 3ds Max" id ="maxscript_syntax" commentExpr="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) (?s:\x2F\x2A.*?\x2A\x2F) # Multi Line Comment | (?m-s:-{2}.*$) # Single Line Comment | (?: # String Literal - Double Quoted @?\x22 # - start-of-double-quoted-(meta-)string indicator (?: [^\x22\x5C] # - any character except double-quote and backslash | \x5C # - unless: backslash escape sequence for... (?: [\x22\x25\x2A\x3F\x5Cnrt] # ...a double-quote, %, *, ?, \, newline, carriage-return, tab... | x[A-Fa-f\d]+ # ...or character code sequence ) )* # - can be empty \x22 # - end-of-double-quoted-(meta-)string indicator ) " > <function mainExpr="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) (?i: (?:mapped\s+)?(?:fn|function) | macroScript )\s+ \K (?'VALID_ID' # valid identifier, use as subroutine \b(?!(?-i: # keywords (case sensitive), not to be used as identifier a(?:bout|n(?:d|imate)|[st]) | by | c(?:a(?:se|tch)|o(?:llect|ntinue|ordsys)) | do | e(?:lse|xit) | f(?:n|or|rom|unction) | global | i[fn] | local | ma(?:croscript|pped|x) | not | o(?:ff?|[nr]) | p(?:arameters|ersistent|lugin) | r(?:cmenu|eturn|ollout) | s(?:et|truct) | t(?:hen|hrow|o(?:ol)?|ry) | u(?:ndo|tility) | w(?:h(?:e(?:n|re)|ile)|ith) )\b) [A-Za-z_]\w* # valid character combination for identifiers ) " /> </parser>
with the following associations:
<association id= "maxscript_syntax" userDefinedLangName="MaxScript" /> <association id= "maxscript_syntax" ext=".ms" /> <association id= "maxscript_syntax" ext=".mcr" />
NOTE: make sure the
userDefinedLangName
value exactly equals (i.e. case sensitive) the name used in your User Defined Language setup. -
Hi MAPJe71,
Thanks a lot!! It works pretty well.
There is maybe something that could be improved. I saw on the screenshot provided on the Function List page (https://notepad-plus-plus.org/features/function-list.html) that there is a folding level between the name of the file and the function list. I assume this is for classes.
Would it be possible to make the macroscripts and structure appear as if they were classes? That would allow to distinguish functions that are part of a structure from those that are at the root level. Beside it would also separate functions from one structure from those of an other one.I put some code sample and a screenshot of the result here: https://we.tl/mrbLNJhywO
Best regards,
Werwack
-
Nice to hear it works and thanks for the samples!
Looking at the grammar I was already thinking about visualizing thestruct
as a branch in the tree. I will let you know how it works out. -
To have both macroscripts and structs appear as branches in the tree I could only think of implementing separate parsers for
.ms
and.mcr
files.
This however requires to associate each parser with a different User Defined Language (UDL). You could define two UDL’s that only differ by name e.g.MaxScript
andMaxScript Macro
and associate them as follows:<association id= "maxscript_syntax" userDefinedLangName="MaxScript" /> <association id= "maxscript_syntax" ext=".ms" /> <association id= "maxscript_macrosyntax" userDefinedLangName="MaxScript Macro" /> <association id= "maxscript_macrosyntax" ext=".mcr" />
The two parser would then look like this:
<!-- =================================================== [ MAXScript ] --> <parser displayName="MAXScript - Scripting language for AutoDesk 3ds Max" id ="maxscript_syntax" commentExpr="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) (?s:\x2F\x2A.*?\x2A\x2F) # Multi Line Comment | (?m-s:-{2}.*$) # Single Line Comment | (?: # String Literal - Double Quoted @?\x22 # - start-of-double-quoted-(meta-)string indicator (?: [^\x22\x5C] # - any character except double-quote and backslash | \x5C # - unless: backslash escape sequence for... (?: [\x22\x25\x2A\x3F\x5Cnrt] # ...a double-quote, %, *, ?, \, newline, carriage-return, tab... | x[A-Fa-f\d]+ # ...or character code sequence ) )* # - can be empty \x22 # - end-of-double-quoted-(meta-)string indicator ) " > <classRange mainExpr ="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) ^\h* (?i:struct)\s+ (?'VALID_ID' # valid identifier, use as subroutine \b(?!(?-i: # keywords (case sensitive), not to be used as identifier a(?:bout|n(?:d|imate)|[st]) | by | c(?:a(?:se|tch)|o(?:llect|ntinue|ordsys)) | do | e(?:lse|xit) | f(?:n|or|rom|unction) | global | i[fn] | local | ma(?:croscript|pped|x) | not | o(?:ff?|[nr]) | p(?:arameters|ersistent|lugin) | r(?:cmenu|eturn|ollout) | s(?:et|truct) | t(?:hen|hrow|o(?:ol)?|ry) | u(?:ndo|tility) | w(?:h(?:e(?:n|re)|ile)|ith) )\b) [A-Za-z_]\w* # valid character combination for identifiers ) \s*[^()]* \( # start-of-body indicator (?s:.*) # body contents \) # end-of-body indicator " openSymbole ="\(" closeSymbole="\)" > <className> <nameExpr expr="(?i:struct)\s+\K[A-Za-z_]\w*" /> </className> <function mainExpr="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) ^\h* (?i:fn|function)\s+ \K (?'VALID_ID' # valid identifier, use as subroutine \b(?!(?-i: # keywords (case sensitive), not to be used as identifier a(?:bout|n(?:d|imate)|[st]) | by | c(?:a(?:se|tch)|o(?:llect|ntinue|ordsys)) | do | e(?:lse|xit) | f(?:n|or|rom|unction) | global | i[fn] | local | ma(?:croscript|pped|x) | not | o(?:ff?|[nr]) | p(?:arameters|ersistent|lugin) | r(?:cmenu|eturn|ollout) | s(?:et|truct) | t(?:hen|hrow|o(?:ol)?|ry) | u(?:ndo|tility) | w(?:h(?:e(?:n|re)|ile)|ith) )\b) [A-Za-z_]\w* # valid character combination for identifiers ) " > <functionName> <funcNameExpr expr="[A-Za-z_]\w*" /> </functionName> </function> </classRange> <function mainExpr="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) ^\h* (?i:fn|function|macroscript)\s+ \K (?'VALID_ID' # valid identifier, use as subroutine \b(?!(?-i: # keywords (case sensitive), not to be used as identifier a(?:bout|n(?:d|imate)|[st]) | by | c(?:a(?:se|tch)|o(?:llect|ntinue|ordsys)) | do | e(?:lse|xit) | f(?:n|or|rom|unction) | global | i[fn] | local | ma(?:croscript|pped|x) | not | o(?:ff?|[nr]) | p(?:arameters|ersistent|lugin) | r(?:cmenu|eturn|ollout) | s(?:et|truct) | t(?:hen|hrow|o(?:ol)?|ry) | u(?:ndo|tility) | w(?:h(?:e(?:n|re)|ile)|ith) )\b) [A-Za-z_]\w* # valid character combination for identifiers ) " /> </parser> <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <parser displayName="MAXScript Macro - Scripting language for AutoDesk 3ds Max" id ="maxscript_macrosyntax" commentExpr="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) (?s:\x2F\x2A.*?\x2A\x2F) # Multi Line Comment | (?m-s:-{2}.*$) # Single Line Comment | (?: # String Literal - Double Quoted @?\x22 # - start-of-double-quoted-(meta-)string indicator (?: [^\x22\x5C] # - any character except double-quote and backslash | \x5C # - unless: backslash escape sequence for... (?: [\x22\x25\x2A\x3F\x5Cnrt] # ...a double-quote, %, *, ?, \, newline, carriage-return, tab... | x[A-Fa-f\d]+ # ...or character code sequence ) )* # - can be empty \x22 # - end-of-double-quoted-(meta-)string indicator ) " > <classRange mainExpr ="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) ^\h* (?i:macroscript)\s+ (?'VALID_ID' # valid identifier, use as subroutine \b(?!(?-i: # keywords (case sensitive), not to be used as identifier a(?:bout|n(?:d|imate)|[st]) | by | c(?:a(?:se|tch)|o(?:llect|ntinue|ordsys)) | do | e(?:lse|xit) | f(?:n|or|rom|unction) | global | i[fn] | local | ma(?:croscript|pped|x) | not | o(?:ff?|[nr]) | p(?:arameters|ersistent|lugin) | r(?:cmenu|eturn|ollout) | s(?:et|truct) | t(?:hen|hrow|o(?:ol)?|ry) | u(?:ndo|tility) | w(?:h(?:e(?:n|re)|ile)|ith) )\b) [A-Za-z_]\w* # valid character combination for identifiers ) \s*[^()]* \( # start-of-body indicator (?s:.*) # body contents \) # end-of-body indicator " openSymbole ="\(" closeSymbole="\)" > <className> <nameExpr expr="(?i:macroscript)\s+\K[A-Za-z_]\w*" /> </className> <function mainExpr="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) ^\h* (?i:fn|function)\s+ \K (?'VALID_ID' # valid identifier, use as subroutine \b(?!(?-i: # keywords (case sensitive), not to be used as identifier a(?:bout|n(?:d|imate)|[st]) | by | c(?:a(?:se|tch)|o(?:llect|ntinue|ordsys)) | do | e(?:lse|xit) | f(?:n|or|rom|unction) | global | i[fn] | local | ma(?:croscript|pped|x) | not | o(?:ff?|[nr]) | p(?:arameters|ersistent|lugin) | r(?:cmenu|eturn|ollout) | s(?:et|truct) | t(?:hen|hrow|o(?:ol)?|ry) | u(?:ndo|tility) | w(?:h(?:e(?:n|re)|ile)|ith) )\b) [A-Za-z_]\w* # valid character combination for identifiers ) " > <functionName> <funcNameExpr expr="[A-Za-z_]\w*" /> </functionName> </function> </classRange> <function mainExpr="(?x) # Utilize inline comments (see `RegEx - Pattern Modifiers`) ^\h* (?i:macroscript)\s+ \K (?'VALID_ID' # valid identifier, use as subroutine \b(?!(?-i: # keywords (case sensitive), not to be used as identifier a(?:bout|n(?:d|imate)|[st]) | by | c(?:a(?:se|tch)|o(?:llect|ntinue|ordsys)) | do | e(?:lse|xit) | f(?:n|or|rom|unction) | global | i[fn] | local | ma(?:croscript|pped|x) | not | o(?:ff?|[nr]) | p(?:arameters|ersistent|lugin) | r(?:cmenu|eturn|ollout) | s(?:et|truct) | t(?:hen|hrow|o(?:ol)?|ry) | u(?:ndo|tility) | w(?:h(?:e(?:n|re)|ile)|ith) )\b) [A-Za-z_]\w* # valid character combination for identifiers ) " /> </parser>
-
Hi MAPJe71,
Thank you for this new code.
I think having 2 separated parser definitions, one for the scripts (.ms) and one for the macros (.mcr), is fine in most of the cases, and your suggestion is nice :)I tested the code above and I have the following issues:
-
for .ms files, the structure is correctly identified with the “class” icon and as a new folder level. If there are 2 or more structures in the file though other structures are not recognized at all.
-
for .mcr files the new folder level for macros is not recognized. It would be nice if it could also support several macros in the .mcr file.
I updated the sample files to introduce several structures and macros to test this, you can find them in this package: https://we.tl/kSLs8d5zhg
Thanks a lot for your help,
Werwack
-