# ============================================================================== # Development.nm # Author: Tony Balinski # # Macros to help in writing new C ++ code. # # Example Macro definitions:- # # Name: Development>New C++ file # s = string_dialog("Make new C++ file:", "OK", "Cancel") # if (s != "" && $string_dialog_button == 1) # Development_makeNewFile_Cpp(s) # # Name: Development>New C++ Class files # s = string_dialog("Make header and source C++ file for class:", \ # "OK", "Cancel") # if (s != "" && $string_dialog_button == 1) # Development_makeClassFiles_Cpp(s) # # Name: Development>Open C++ Class files # s = string_dialog("Directory: " $file_path "\n" \ # "Open header and source C++ file for class:", \ # "OK", "Cancel") # if (s != "" && $string_dialog_button == 1) # Development_openClassFiles_Cpp(s) # # Name: Development>Make C++ Method in class.cpp file@C++ # Development_makeCppMethod() # # If you use the NEDIT_LOADED.nm macros, precede each macro text with # NEDIT_require_macro_file("Development.nm") # # Much of this was written to my personal taste (many comment bars, # Doxygen-style /// comment lines before class and method names, two-space # "Whitesmith" indenting, strict 80-column adherence. Modify to taste. # ============================================================================== NEDIT_require_macro_file("extensions.nm") # ------------------------------------------------------------------------------ # Globals: # User preference values # # $Development_Authors[] # An array of author name strings, indexed from 0 # # $Development_Copyright[] # Should contain the copyright string, indexed by the string "copyright" # # $Development_CVS[] # An array of values for constructing RCS/CVS/SCCS "what" strings - # indexed elements are: # ["Keywords"] version control system substitution strings to add # to the "what" string, eg "$Header$" or "%W%" # ["WhatPrefix"] first component of all "what" strings, eg "@(#)" # ["FileIdPrefix"] prefix for "what" string's identifier # For C/C++, a "what" string will be generated as: # static const char [] = # " ";\n\n # # $Development_Layout[] # Contains source generation layout information: # ["MaxCols"] maximum line length, default 80; # if zero, line breaking is not applied # # $Development_Header[$language_mode][] # Should contain details about building a header comment block: # ["box"] non-zero means pad the block to align right hand # line ends # ["bar"] character to repeat to build a bar # ["open"] first line of comment block (before the "\n") # ["close"] last line of comment block (before the "\n") # ["left"] first characters of each block line # ["right"] last characters of each block line # ["order"] elements to print in a space separated string: # / - a blank line # bar - a comment bar of repeated bar characters # file - the current file name # cdate - the current date (creation date) # author - the author list # copy - the copyright notice # # C++ preference values - these are set up during macro loading: # # $Development_CppFileType[] # C++ file extensions indexed by file type name, eg ["C++ header"] = ".h" # # $Development_CppFileExtn[] # C++ file extensions indexed by the file extension, eg [".cpp"] = ".cpp" # # $Development_CppFileExtnToType[] # Map of alternative extensions to basic extensions in # $Development_CppFileExtn[] ("inverse" of $Development_CppFileExtnAlt[], # built by Development_SetupCppFileExtnToType() # # $Development_CppPrefFileExtn[] # Actual extensions to use; if empty, it will be built by # Development_SetupCppPreferredFileExtn() using file names found in the # current directory, matching extensions which are indices to # $Development_CppFileExtnToType[]; this results in new C++ source files # ending with ".C" rather than ".cpp" if ".C" files were found, say. # # $Development_CppFileExtnAlt[] # List of alternative extensions (as a string) for extensions in # $Development_CppFileExtn[]; eg [".h"] = ".h .H .h++ .hh" # # $Development_CppClassToFileRE[] # A set of replace_in_string() values: each key is used as a RE to match # a class name. If a match is found, the value corresponding to that key # is used as the replacement pattern to generate a file name. # # You can set up your author list and copyright information by creating another # macro file, setting up these values. This file should then be loaded. The # following assumes a file called DevelopmentData.nm and the use of the # NEDIT_LOADED.nm macros to ensure loading. # # The file DevelopmentData.nm should contain something like: # # custom DevelopmentData.nm file # $Development_Authors[0] = "Mark Edel" # $Development_Authors[1] = "Tony Balinski" # # # $Development_Copyright["copyright"] = "NEdit.org" # ------------------------------------------------------------------------------ NEDIT_require_macro_file("DevelopmentData.nm") # The following assignments force arrays $Development_Authors and # $Development_Copyright to exist. $Development_Authors[""] = "" $Development_Copyright[""] = "" $Development_CVS[""] = "" $Development_Layout[""] = "" $Development_Header[""] = "" if (!("C++" in $Development_Header)) { $Development_Header["C++"] = $empty_array $Development_Header["C++"]["box"] = 0 $Development_Header["C++"]["bar"] = "=" $Development_Header["C++"]["open"] = "" $Development_Header["C++"]["close"] = "" $Development_Header["C++"]["left"] = "// " $Development_Header["C++"]["right"] = "" $Development_Header["C++"]["order"] = "bar / file / cdate author / copy / bar" } if (!("C" in $Development_Header)) { $Development_Header["C"] = $empty_array $Development_Header["C"]["box"] = 1 $Development_Header["C"]["bar"] = "*" $Development_Header["C"]["open"] = "" $Development_Header["C"]["close"] = "" $Development_Header["C"]["left"] = "/* " $Development_Header["C"]["right"] = " */" $Development_Header["C"]["order"] = "bar / file / cdate author / copy / bar" } if (!("Java" in $Development_Header)) { $Development_Header["Java"] = $empty_array $Development_Header["Java"]["box"] = 0 $Development_Header["Java"]["bar"] = "-" $Development_Header["Java"]["open"] = "/**" $Development_Header["Java"]["close"] = " */" $Development_Header["Java"]["left"] = " * " $Development_Header["Java"]["right"] = "" $Development_Header["Java"]["order"] = "/ file / cdate author / copy" } # defaults for header block are like C's if (!("Plain" in $Development_Header)) $Development_Header["Plain"] = $Development_Header["C"] # general hash comments if (!("#" in $Development_Header)) { $Development_Header["#"] = $empty_array $Development_Header["#"]["box"] = 1 $Development_Header["#"]["bar"] = "=" $Development_Header["#"]["open"] = "" $Development_Header["#"]["close"] = "" $Development_Header["#"]["left"] = "#* " $Development_Header["#"]["right"] = " * " $Development_Header["#"]["order"] = "bar / file / cdate author / copy / bar" } if (!("Perl" in $Development_Header)) $Development_Header["Perl"] = $Development_Header["#"] if (!("NEdit Macro" in $Development_Header)) $Development_Header["NEdit Macro"] = $Development_Header["#"] if (!("MaxCols" in $Development_Layout)) { $Development_Layout["MaxCols"] = 80 } if (!("Keywords" in $Development_CVS)) { $Development_CVS["Keywords"] = "$Id$" } if (!("WhatPrefix" in $Development_CVS)) { $Development_CVS["WhatPrefix"] = "@(#)" } if (!("FileIdPrefix" in $Development_CVS)) { $Development_CVS["FileIdPrefix"] = "what_" } $Development_CppFileType["C++ header"] = ".h" $Development_CppFileType["C++ source"] = ".cpp" $Development_CppFileType["C++ inline"] = ".inl" $Development_CppFileExtn[".h"] = ".h" $Development_CppFileExtn[".cpp"] = ".cpp" $Development_CppFileExtn[".inl"] = ".inl" $Development_CppFileExtnToType = $empty_array $Development_CppPrefFileExtn = $empty_array $Development_CppClassToFileRE[""] = 0 delete $Development_CppClassToFileRE[""] # list of alternative extensions for our primary extension types $Development_CppFileExtnAlt[""] = "" list = $Development_CppFileExtnAlt if (!(".h" in list)) list[".h"] = ".h .H .h++ .hh" if (!(".cpp" in list)) list[".cpp"] = ".cpp .C .c++ .cc" if (!(".inl" in list)) list[".inl"] = ".inl .inc .i" $Development_CppFileExtnAlt = list delete $Development_CppFileExtnAlt[""] # ------------------------------------------------------------------------------ # Development_SetupCppFileExtnToType(): creates a $Development_CppFileExtnToType # array to map various file extension strings to one of the acceptable # file types, as defined by $Development_CppFileExtnAlt. It returns the # $Development_CppFileExtnToType array. # ------------------------------------------------------------------------------ define Development_SetupCppFileExtnToType { if ($Development_CppFileExtnToType[] > 0) return $Development_CppFileExtnToType extnToType = $empty_array for (x in $Development_CppFileExtnAlt) { alts = split($Development_CppFileExtnAlt[x], "\\s+", "regex") for (xx in alts) if (alts[xx] != "") extnToType[alts[xx]] = x } # feedback = "" # for (x in extnToType) # { # feedback = feedback "Extension " x " => " extnToType[x] "\n" # } # # if (feedback != "") # { # dialog("Altered file extensions:\n\n" feedback) # } $Development_CppFileExtnToType = extnToType return $Development_CppFileExtnToType } # ------------------------------------------------------------------------------ # Development_SetupCppPreferredFileExtn([redo]): if an argument (redo) is # present or the array $Development_CppPrefFileExtn is empty, this # function looks at the files in the current directory, trying to find # valid extensions. It counts these then sets up the result array # $Development_CppPrefFileExtn to contain the most numerous acceptable # extensions of each. Otherwise the array is assumed to be valid and is # returned directly. # ------------------------------------------------------------------------------ define Development_SetupCppPreferredFileExtn { if ($Development_CppPrefFileExtn[] > 0 && $n_args == 0) return $Development_CppPrefFileExtn x2t = Development_SetupCppFileExtnToType() ls = shell_command("ls", "") ls = replace_in_string(ls, "^.*(\\.\\w+)$", "\\1", "regex") ls = replace_in_string(ls, "\n\n+", "\n", "regex", "copy") ls = replace_in_string(ls, "(?n\n$)", "", "regex", "copy") extns = split(ls, "\n") # count occurrences of each extension prefExtn = $empty_array # initialise the count array for (extn in x2t) prefExtn[extn] = 0 # count the values for (i in extns) { extn = extns[i] if (extn in prefExtn) prefExtn[extn]++ } # find results resExtn = $Development_CppFileExtn for (extn in prefExtn) { if (prefExtn[extn] > 0) { if (x2t[extn] in resExtn) { # resExtn already contains a result: compare with this one if (prefExtn[extn] > prefExtn[resExtn[x2t[extn]]]) resExtn[x2t[extn]] = extn } else resExtn[x2t[extn]] = extn } } # now resExtn contains the preferred extensions for those files seen # add any missing ones if (!(".cpp" in resExtn)) resExtn[".cpp"] = ".cpp" if (!(".h" in resExtn)) resExtn[".h"] = ".h" if (!(".inl" in resExtn)) resExtn[".inl"] = ".inl" feedback = "" for (t in $Development_CppFileType) { x = $Development_CppFileType[t] feedback = feedback "Extension for " t " (" x ") => " resExtn[x] \ " (" prefExtn[resExtn[x]] " files)\n" } while (feedback != "") { if (dialog("Altered file extensions:\n\n" feedback, "OK", "Change") == 2) { feedback = "" for (t in $Development_CppFileType) { x = $Development_CppFileType[t] s = "" err = "" while (s == "") { s = string_dialog(err "Altered file extension for " t " (" x ")" \ "\n(default " resExtn[x] ")", "OK", "Cancel") err = "" if (s == "") s = resExtn[x] if ($string_dialog_button == 2) break if (search_string(s, "^\\.[\\w+]+$", 0, "regex") != 0) { err = "Bad extension: " s "\n(must be of form .xxx)\n" s = "" } if (s != "") resExtn[x] = s } } for (t in $Development_CppFileType) { x = $Development_CppFileType[t] feedback = feedback "Extension for " t " (" x ") => " resExtn[x] \ " (" prefExtn[resExtn[x]] " files)\n" } } else feedback = "" } $Development_CppPrefFileExtn = resExtn return $Development_CppPrefFileExtn } # ------------------------------------------------------------------------------ # Development_SetupClassToFileName(): prompts the user to set up the # $Development_CppClassToFileRE[] array. This will have (at least) the # entry [""] in it at the end of this function. # ------------------------------------------------------------------------------ define Development_SetupClassToFileName { if ("" in $Development_CppClassToFileRE) delete $Development_CppClassToFileRE[""] prompt = "Class name to file name conversion\n" for (;;) { num = $Development_CppClassToFileRE[] tot = "Definitions so far: " num "\n" if (num > 0) { list = "" for (re in $Development_CppClassToFileRE) { rpl = $Development_CppClassToFileRE[re] list = list "'" re "' -> '" rpl "'\n" } line = list_dialog(prompt tot "Choose an action", list, \ "Add new", "Delete", "Done all") btn = $list_dialog_button if (btn == 3 || btn == 0) { $Development_CppClassToFileRE[""] = 0 return } if (btn == 2 && line != "") { for (re in $Development_CppClassToFileRE) { list = "'" re "' -> '" rpl "'" if (list == line) { delete $Development_CppClassToFileRE[re] break } } continue } else if (btn != 1) continue } re = string_dialog(prompt tot "Enter RE matching a class name", \ "OK", "Done all") btn = $string_dialog_button if (btn == 2) { $Development_CppClassToFileRE[""] = 0 return } if (re == "") continue rpl = "" for (;;) { rpl = string_dialog(prompt "Enter replacement string for RE '" re "'", \ "OK", "Skip", "Done all") if (btn == 2) break else if (btn == 3) { $Development_CppClassToFileRE[""] = 0 return } if (rpl == "") if (dialog(prompt "Replacement string for RE '" re "' is empty", \ "OK", "Bad") != 1) continue # try again break } if (btn != 2) $Development_CppClassToFileRE[re] = rpl } } # ------------------------------------------------------------------------------ # Development_ClassToFileName(classname): returns the result of transforming the # class name to a file name root using the replacement matches of the # $Development_CppClassToFileRE[] array. Only one replacement is done. # ------------------------------------------------------------------------------ define Development_ClassToFileName { name = $1 if (!("" in $Development_CppClassToFileRE)) Development_SetupClassToFileName() for (re in $Development_CppClassToFileRE) { if (re == "") continue if (search_string(name, re, 0, "regex") >= 0) { rpl = $Development_CppClassToFileRE[re] return replace_in_string(name, re, rpl, "regex") } } return name } # ------------------------------------------------------------------------------ # Development_fileNameToIdentifier(): returns the current file name with any # non-word characters swapped for underscores. # ------------------------------------------------------------------------------ define Development_fileNameToIdentifier { return replace_in_string($file_name, "\\W", "_", "regex", "copy") } # ------------------------------------------------------------------------------ # Development_getCopyright([silent]): returns the copyright string for headings # in new files; if non-existent or if silent is present and non-zero, # a dialog prompts for it, storing its value for later. # ------------------------------------------------------------------------------ define Development_getCopyright { silent = 0 if ($n_args >= 1) silent = ($1 != "" && $1 != "0") cright = "" if ("copyright" in $Development_Copyright) cright = $Development_Copyright["copyright"] if (!silent || cright == "") { s = string_dialog("Enter copyright owner\nCurrently: "cright, \ "OK","Clear All","Done") if ($string_dialog_button == 2) cright = "" else if (s != "" && $string_dialog_button == 1) cright = s # else leave alone } $Development_Copyright["copyright"] = cright return cright } # ------------------------------------------------------------------------------ # Development_getAuthors([silent]): returns the array of author names for # headings in new files; if none assigned or if silent is present and # non-zero, successive dialog boxes prompt for the author list. # ------------------------------------------------------------------------------ define Development_getAuthors { silent = 0 if ($n_args >= 1) silent = ($1 != "" && $1 != "0") authlist = "" i = 0 for (i = 0; i in $Development_Authors; i++) authlist = authlist "\n" $Development_Authors[i] if (!silent || i == 0) { while (1) { s = string_dialog("Enter another author\nCurrently: "authlist, \ "OK","Clear All","Done") if ($string_dialog_button == 2) authlist = "" else if (s == "" || $string_dialog_button != 1) break else authlist = authlist "\n" s } } authlist = replace_in_string(authlist, "^\n", "", "regex") $Development_Authors = split(authlist, "\n") return authlist } # ------------------------------------------------------------------------------ # Development_makeWhatString_C_Cpp(): inserts a C/C++ static const what-string # definition for the current file, containing the what lead-in and RCS/CVS # substitution marker keywords. # ------------------------------------------------------------------------------ define Development_makeWhatString_C_Cpp { f = Development_fileNameToIdentifier() x = "" x = x "static const char " $Development_CVS["FileIdPrefix"] f "[] =" x = x " \"" $Development_CVS["WhatPrefix"] " " \ $Development_CVS["Keywords"] "\";\n\n" beginning_of_line() insert_string(x) } # ------------------------------------------------------------------------------ # Development_makeWhatString_Hash(): inserts a line, starting with hash ('#'), # containing the what lead-in and RCS/CVS substitution marker keywords. # This is a suitable script comment line. # ------------------------------------------------------------------------------ define Development_makeWhatString_Hash { f = Development_fileNameToIdentifier() x = "" x = x "# \"" $Development_CVS["WhatPrefix"] " " \ $Development_CVS["Keywords"] "\"\n" beginning_of_line() insert_string(x) } # ------------------------------------------------------------------------------ # Development_makeWhatString_HTML_SGML_XML(): inserts a line, formatted as a # HTML comment, containing the what lead-in and the RCS/CVS $Name$ and # $Id$ substitution marker keywords. # ------------------------------------------------------------------------------ define Development_makeWhatString_HTML_SGML_XML { f = Development_fileNameToIdentifier() x = "" x = x "\n" beginning_of_line() insert_string(x) } # ------------------------------------------------------------------------------ # Development_makeFileHeaderComment_block([silent]): builds a file header # comment box with copyright notice, creation date (today), author list # and file name, returning it as a string. If silent is present and # non-zero or non-empty prompting will not occur for assigned values. # ------------------------------------------------------------------------------ define Development_makeFileHeaderComment_block { silent = 0 if ($n_args >= 1) silent = ($1 != "" && $1 != "0") # defaults for header block if (!("Plain" in $Development_Header)) dialog("No default header settings for \"Plain\" language type") else a = $Development_Header["Plain"] # make sure all fields are set up if ($language_mode in $Development_Header) { for (k in a) { if (!(k in $Development_Header[$language_mode])) $Development_Header[$language_mode][k] = a[k] } } a = $Development_Header[$language_mode] cright = Development_getCopyright(silent) cols = $Development_Layout["MaxCols"] - length(a["left"]) - length(a["right"]) order = split(a["order"], " +", "regex") bar = "" if (a["bar"] != "") bar = repeat(a["bar"], cols) pad = "" if (a["box"]) pad = repeat(" ", cols) date = split(shell_command("date +'%b %e, %Y'", ""), "\n")[0] year = split(shell_command("date +'%Y'", ""), "\n")[0] # values value["/"] = "" value["bar"] = bar value["copy"] = "Copyright (c) " year " " cright ". All rights reserved." value["cdate"] = "Creation date: " date value["file"] = $file_name authnames = "Authors: " authspace = replace_in_string(authnames, ".", " ", "regex", "copy") authlist = Development_getAuthors(silent) auths = split(authlist, "\n") pref = authnames x = "" n = "" for (i = 0; (i in auths) && auths[i] != ""; i++) { s = pref auths[i] x = x n s pref = authspace n = "\n" } value["author"] = x x = "" n = "" for (k in order) { i = order[k] v = "[" i "]" if (i in value) v = value[i] x = x n v n = "\n" } x = replace_in_string(x, " *$", pad, "regex", "copy") if (a["box"]) x = replace_in_string(x, "(.{"cols"}) +$", "\\1", "regex", "copy") if (n == "\n") { x = a["left"] \ replace_in_string(x, n, a["right"] n a["left"], "copy") \ a["right"] n } if (a["open"] != "") x = a["open"] "\n" x if (a["close"] != "") x = x a["close"] "\n" x = replace_in_string(x, " +$", "", "regex", "copy") return x } # ------------------------------------------------------------------------------ # Development_makeFileHeaderComment_C_Cpp(): uses # Development_makeFileHeaderComment_block() to build a comment box for C # or C++ programs. # ------------------------------------------------------------------------------ define Development_makeFileHeaderComment_C_Cpp { silent = 0 if ($n_args >= 1) silent = ($1 != "" && $1 != "0") set_cursor_pos(0) deselect_all() x = Development_makeFileHeaderComment_block(silent) insert_string(x "\n") } # ------------------------------------------------------------------------------ # Development_makeFileHeaderComment_Hash(): uses # Development_makeFileHeaderComment_base() to build a comment box for # files using hash comments. # ------------------------------------------------------------------------------ define Development_makeFileHeaderComment_Hash { silent = 0 if ($n_args >= 1) silent = ($1 != "" && $1 != "0") if (!($language_mode in $Development_Header)) $Development_Header[$language_mode] = $Development_Header["#"] set_cursor_pos(0) deselect_all() x = Development_makeFileHeaderComment_block(silent) insert_string(x "\n") } # ------------------------------------------------------------------------------ # Development_addHeaderFileGuards_C_Cpp(): adds requisite include guards for # C/C++ header files. # ------------------------------------------------------------------------------ define Development_addHeaderFileGuards_C_Cpp { f = Development_fileNameToIdentifier() deselect_all() beginning_of_file() insert_string("#ifndef " f "_INCLUDED\n#define " f "_INCLUDED\n\n") if ($language_mode == "C") s = "\n#endif /* #ifndef " f "_INCLUDED */" else s = "\n#endif // #ifndef " f"_INCLUDED" end_of_file() insert_string(s) } # ============================================================================== # ------------------------------------------------------------------------------ # Development_makeNewFile_Cpp(name, [text [, silent]]): create a new C++ file, # add comment-box header, what-string and guards (if necessary), and any # required initial text. If name (the name of the new file to create) does # not end in .cpp, .h or .inl, this function fails. It will not overwrite # an existing, non-empty file. It returns 1 if it was able to add # everything, zero otherwise. # ------------------------------------------------------------------------------ define Development_makeNewFile_Cpp { name = $1 if (name == "") return 0 text = "" if ($n_args >= 2) text = $2 silent = 0 if ($n_args >= 3) silent = ($3 != "" && $3 != "0") x2t = Development_SetupCppFileExtnToType() extns = "" for (x in x2t) extns = extns x " " # absolute pathname? if not, add the current file's path if (substring(name, 0, 1) != "/") { path = $file_path if (path == "") path = $NEDIT_START_DIR name = path "/" name name = NEDIT_remove_dotdot(name) name = NEDIT_reduce_filename(name) } # do we have a valid extension? extn = replace_in_string(name, ".*(\\.[\\w+]+)$", "\\1", "regex") if (!(extn in x2t)) { dialog("The file name\n " name \ "\ndoes not end with one of the extensions\n" extns "\n" \ "Not creating") return 0 } content = read_file(name) if (!$read_status) { if (dialog("Creating " name, "OK", "Cancel") != 1) return 0 if (!write_file("", name)) { dialog("Cannot create file " name ": abandoning") return 0 } } open(name) if (name != focus_window(name)) { dialog("Could not open " name) return 0 } if ($read_only) { dialog("File " name " is read-only") return 0 } if ($text_length != 0) { if (!silent) dialog("File " name " is not new\n" \ "(text length: " $text_length ")\n" \ "No automatic modifications will be made") return 0 } # OK now start adding stuff set_language_mode("C++") Development_makeWhatString_C_Cpp() if (text != "") insert_string(text) # OK, is it an include file? Use the extension to find out if (search_string(x2t[extn], "^(\\.h|\\.inl)$", 0, "regex") == 0) { Development_addHeaderFileGuards_C_Cpp() } Development_makeFileHeaderComment_C_Cpp(silent) return 1 } # ------------------------------------------------------------------------------ # Development_makeClassFiles_Cpp(name): attempts to make a pair of files using # name as this base file name, in the directory of the current document, # and adding .cpp (for C++ source) and .h (for a header). The .h file will # hold a class definition skeleton; the .cpp will include the .h. # ------------------------------------------------------------------------------ define Development_makeClassFiles_Cpp { name = $1 if (search_string(name, "^(?n\\w+$)", 0, "regex") != 0) { dialog("Invalid C++ class name '" name "' for class file creation") return } px = Development_SetupCppPreferredFileExtn() fname = Development_ClassToFileName(name) s = "class " name "\n" \ " {\n" \ " public:\n" \ " "name"();\n" \ " "name"(const "name" &other);\n" \ " virtual ~"name"();\n" \ " const "name" &operator = (const "name" &other);\n\n" \ " protected:\n" \ " private:\n" \ " };\n" cmt = "===========" cmt = "// " cmt cmt cmt cmt cmt cmt cmt "\n" class = cmt "\n///\n\n" s "\n" cmt Development_makeNewFile_Cpp(fname px[".h"], class, "silent") pos = search("class " name, 0) if (pos >= 0) set_cursor_pos(pos) incl = replace_in_string(cmt, "=", "-") \ "\n#include \"" fname px[".h"] "\"\n\n" cmt "\n" Development_makeNewFile_Cpp(fname px[".cpp"], incl, "silent") set_cursor_pos($text_length) } # ------------------------------------------------------------------------------ # Development_openClassFiles_Cpp(name): given a name or file path and basename # in the parameter name, this function attempts to find files with # extensions .cpp and .h, then to open them. # ------------------------------------------------------------------------------ define Development_openClassFiles_Cpp { name = $1 if (search_string(name, "/", 0) == -1) { # no slashes: treat it as a class name if (search_string(name, "^(?n\\w+$)", 0, "regex") != 0) { dialog("Invalid C++ class name '" name) return } name = $file_path Development_ClassToFileName(name) } else { # treat it as a file: orthogonalise the path if (substring(name, 0, 1) != "/") name = $file_path name name = NEDIT_remove_dotdot(name) name = NEDIT_reduce_filename(name) } px = Development_SetupCppPreferredFileExtn() error = "" read_file(name px[".h"]) if ($read_status) open(name px[".h"]) else error = error "\n" name px[".h"] read_file(name px[".cpp"]) if ($read_status) open(name px[".cpp"]) else error = error "\n" name px[".cpp"] if (error != "") dialog("Could not find:" error) } # ------------------------------------------------------------------------------ # Development_getCppMatchingCloseBrace(obracepos): searches forward for a close # brace matching the open brace at obracepos. Returns its position or -1. # ------------------------------------------------------------------------------ define Development_getCppMatchingCloseBrace { obracepos = $1 # sub RE to read over comments and strings comment = "/\\*(?:(?!\\*/).)*\\*/|" # C comment \ "//[^\n]*\n" # C++ comment cmtStr = comment # comments \ "|\"(?:\\\\.|[^\"\\\\])*\"" # string docEnd = "(?!\n)$" # match end-of-doc # don't actually match the brace at the end ocbraceRE = "(?n(?:" cmtStr "|[^{}])*(?=[{}]|" docEnd "))" # make sure we're on an open brace if (search("{", obracepos) != obracepos) return -1 pos = obracepos + 1 depth = 1 while (depth > 0) { b = search(ocbraceRE, pos, "regex") e = $search_end if (b < pos || e == $text_length) break brace = get_range(e, e + 1) if (brace == "}") --depth else ++depth pos = e + 1 } if (depth) return -1 return pos - 1 } # ------------------------------------------------------------------------------ # Development_getCppClass(pos): tries to find the enclosing class(es) for a # method declaration. Returns an array of positional and other # information: # ["class"] class name # ["classPos"] start position of class name # ["classEndPos"] start position of class name # ------------------------------------------------------------------------------ define Development_getCppClass { pos = $1 RES = $empty_array # sub RE to read over comments and strings comment = "/\\*(?:(?!\\*/).)*\\*/|" # C comment \ "//[^\n]*\n" # C++ comment cmtStr = comment # comments \ "|\"(?:\\\\.|[^\"\\\\])*\"" # string # allowing a successful match on end-of-doc avoids slow backtracking # (but we need to verify each find for end-of-doc if we have no other test) docEnd = "(?!\n)$" # match end-of-doc # the following strings find open/close-braces outside of a C++ comment # (the brace comes inside the end of the found section) obraceRE = "(?n(?:" cmtStr "|[^{])*(?:\\{|" docEnd "))" cbraceRE = "(?n(?:" cmtStr "|[^}])*(?:\\}|" docEnd "))" ocbraceRE = "(?n(?:" cmtStr "|[^{}])*(?:[{}]|" docEnd "))" # similarly, find "struct" or "class" (finishing at the end of the keyword) classLikeRE = "(?:struct|class|namespace)" classRE = "(?n(?:" # match across lines \ cmtStr # comments and strings \ "|(?:)" # allow friend decls \ "|(?!<" classLikeRE ">)." # but not struct or class \ ")*?" # as much as you like \ "(?:<" classLikeRE ">|" docEnd "))" # finish with keyword or eof # and this stops at the end of the next word outside of comments wordRE = "(?n(?:" comment "|\\s)*(?:<\\w+>|" docEnd "))" # find class names from the top className = "" classPathSep = "" endC = 0 # classRE stops at the end of class|struct: need a classname before pos for (begC = search(classRE, endC, "regex"); \ 0 <= begC && $search_end < pos; \ begC = search(classRE, endC, "regex")) { endC = $search_end # find the next word (non-commented) wordPos = search(wordRE, endC, "regex") if (wordPos != endC) continue wordEnd = $search_end if (wordEnd > pos) # class name should be before pos return $empty_array wordPos = search("<\\w", wordEnd, "regex", "backward") word = get_range(wordPos, wordEnd) # now find first openbrace following the class specification ob = search(obraceRE, wordEnd, "regex") if (ob >= wordEnd && $search_end < pos) # brace should be before pos ob = $search_end - 1 else return $empty_array cb = Development_getCppMatchingCloseBrace(ob) if (cb < ob) return $empty_array if (cb < pos) continue # now we have braces spanning pos className = className classPathSep word classPathSep = "::" # keep results up to date RES["class"] = className RES["classPos"] = wordPos RES["classEndPos"] = wordEnd } return RES } # ------------------------------------------------------------------------------ # Development_extractPrototypeCPP(pos): search from pos in the current file for # a method declaration. pos is expected to be in the method name (or in # the word "operator"). Returns an array of positional and other # information: # ["class"] class name # ["classPos"] start position of class name # ["classEndPos"] start position of class name # ["method"] method name # ["methodPos"] start position of method name # ["methodEndPos"] end position of method name # ["prototype"] prototype for method definition # ["prototypeCheck"] prototype for method detection # ["protoStart"] start position of prototype # ["protoEnd"] end position of prototype # ------------------------------------------------------------------------------ define Development_extractPrototypeCPP { pos = $1 RES = $empty_array # find start of current word while (search("\\w+", pos - 1, "regex") == pos - 1) pos-- # destructor? if (search("~", pos - 1) == pos - 1) pos-- # check if (search("~?\\w+", pos, "regex") != pos) return RES endpos = $search_end # operator? if (search("= 0 && $search_end > beg) beg = $search_end # and with C++ comments: // front = search("//.*[\\s\\n]*", pos, "regex","backward") if (front >= 0 && $search_end > beg) beg = $search_end # and now for standard C++ punctuation front = search("(;|\\}|\\{|:)[\\s\\n]*", pos, "regex", "backward") if (front >= 0 && $search_end > beg) beg = $search_end # find previous class name if (!("class" in RES)) { extraRES = Development_getCppClass(front) RES = RES + extraRES } # try again! (DOES NOT ACCOUNT FOR NESTED CLASSES OR COMMENTS) if (!("class" in RES)) { classpos = search("(?", front, \ "regex", "backward") if (classpos < 0) return $empty_array classpos = search("\\w+", $search_end, "regex") class = get_range(classpos, $search_end) RES["class"] = class RES["classPos"] = classpos RES["classEndPos"] = $search_end } class = RES["class"] comment = "" if (beg > pos) { comment = "COMMENT: " beg = pos } method = class "::" name # get the next semi-colon (DOES NOT ACCOUNT FOR NESTED CLASSES OR COMMENTS) # open brace (start of function) or colon (start of constructor) or "try" # (start of function try block) semi = search("([;{]|(?)", endpos, "regex") # build the prototype pt = get_range(beg, pos) method get_range(endpos, semi) RES["protoStart"] = beg RES["protoEnd"] = semi # collapse spaces, strip comments pt = replace_in_string(pt, "//.*\\n", " ", "regex", "copy") pt = replace_in_string(pt, "[\\n]+", " ", "regex", "copy") pt = replace_in_string(pt, "/\\*((?!\\*/).)*\\*/", " ", "regex", "copy") pt = replace_in_string(pt, "\\s+", " ", "regex", "copy") pt = replace_in_string(pt, " *\\( *", "\\(", "regex", "copy") pt = replace_in_string(pt, " *\\)", "\\)", "regex", "copy") pt = replace_in_string(pt, "^ ?((?:.(?! $))+.) ?", "\\1", "regex", "copy") pt = replace_in_string(pt, " ,", ",", "copy") pt = replace_in_string(pt, "(operator)\\s*([^\\w\\s()]+|\\(\\))\\s*(\\()", \ "\\1 \\2 \\3", "regex", "copy") # remove C++ class definition specific stuff: = ???, = 0, virtual, static pt = replace_in_string(pt, "(? ", "", "regex", "copy") pt = replace_in_string(pt, " ", "", "regex", "copy") RES["prototype"] = pt pt = replace_in_string(pt, "\\s*/\\*((?!\\*/).)*\\*/", "", "regex", "copy") pt = replace_in_string(pt, " , ?", ", ", "regex", "copy") RES["prototypeCheck"] = comment pt return RES } # ------------------------------------------------------------------------------ # Colors for lexical elements defining a method, as used in # Development_makeCppMethod(). # ------------------------------------------------------------------------------ $Development_makeCppMethod_color = "" $Development_makeCppClass_color = "" # ------------------------------------------------------------------------------ # Development_makeCppMethod_makeColor(): sets up the colors of the rangesets to # be used to color the lexical elements defining a method. # ------------------------------------------------------------------------------ define Development_makeCppMethod_makeColor { if ($Development_makeCppMethod_color == "") { isDark = -1 style = get_style_at_pos(0) if ("back_rgb" in style) { rgb = style["back_rgb"] r = hex_to_int(substring(rgb, 1, 3)) g = hex_to_int(substring(rgb, 3, 5)) b = hex_to_int(substring(rgb, 5, 7)) isDark = (r < 192 && g < 192 && b < 220 && (r + g + b) < (3 * 128)) } if (isDark == -1) isDark = (2 == dialog("Assume what kind of text background?", \ "Bright", "Dark")) if (isDark) { $Development_makeCppMethod_color = "darkgreen" $Development_makeCppClass_color = "steelblue4" } else { $Development_makeCppMethod_color = "lightgreen" $Development_makeCppClass_color = "lightblue" } } } # ------------------------------------------------------------------------------ # Development_makeCppMethod(): if positioned in a method name of a class in a # header (.h) file, this function looks for a corresponding implementation # in a source (.cpp) file, named for the method's class. If no such file # exists, a dialog prompts whether to create one, or to look in a file of # the same name as the current one, with another extension (.cpp, .inl or # .h). Given the appropriate file, it checks whether an implementation # already exists. If methods of the same name exist with different # prototypes, a dialog prompts whether to create an implementation of the # chosen method; if the prototype matches an implemented method, a dialog # warning is issued and the implementation selected. Otherwise a new # method implementation skeleton is added at the end of the appropriate # file. # ------------------------------------------------------------------------------ define Development_makeCppMethod { Development_makeCppMethod_makeColor() px = Development_SetupCppPreferredFileExtn() rsa = rangeset_get_by_name("C++ method") if (rsa[] == 0) { rsp = rangeset_create() rangeset_set_color(rsp, $Development_makeCppMethod_color) rangeset_set_name(rsp, "C++ method") } else { rsp = rsa[0] rangeset_subtract(rsp, 0, $text_length) } rsa = rangeset_get_by_name("C++ class") if (rsa[] == 0) { rs = rangeset_create() rangeset_set_color(rs, $Development_makeCppClass_color) rangeset_set_name(rs, "C++ class") } else { rs = rsa[0] rangeset_subtract(rs, 0, $text_length) } pos = $cursor res = Development_extractPrototypeCPP(pos) if (!("method" in res) || !("class" in res) || !("prototype" in res)) { dialog("You need to start on the method name in the prototype") return } # color pieces rangeset_add(rs, res["methodPos"], res["methodEndPos"]) rangeset_add(rs, res["classPos"], res["classEndPos"]) rangeset_add(rsp, res["protoStart"], res["protoEnd"]) check = res["prototypeCheck"] prototype = res["prototype"] class = res["class"] method = class "::" res["method"] # go to class implementation file classfile = $file_path Development_ClassToFileName(class) px[".cpp"] opened = 0 while (!opened) { if (focus_window(classfile) != classfile) { classfilecontent = read_file(classfile) if ($read_status) open(classfile) if (focus_window(classfile) != classfile) { repl = "(?:" \ quote_literal_as_regex(px[".cpp"]) "|" \ quote_literal_as_regex(px[".h"]) "|" \ quote_literal_as_regex(px[".inl"]) ")$" stripped = $file_path $file_name stripped = replace_in_string(stripped, repl, "", "regex", "copy") res = dialog("could not open class file " classfile \ "\nCreate it or" \ "\nUse " stripped ".??", \ "Create", px[".cpp"], px[".inl"], px[".h"], "Cancel") if (res == 1) { cmt = "===========" cmt = "// " cmt cmt cmt cmt cmt cmt cmt "\n" incl = replace_in_string(cmt, "=", "-") \ "\n#include \"" $file_name "\"\n\n" cmt "\n" if (!Development_makeNewFile_Cpp(classfile, incl, "silent")) { dialog("could not create class file " classfile, "Cancel") return } } else if (res == 2) classfile = stripped px[".cpp"] else if (res == 3) classfile = stripped px[".inl"] else if (res == 4) classfile = stripped px[".h"] else return } } else opened = 1 } # raise_window(classfile) open(classfile) # have we already done it? endpos = 0 overloads = "" poslen = length($text_length) possp = substring(" ", 0, poslen) for (pos = search(method, 0, "case", "forward"); \ pos >= 0; \ pos = search(method, endpos, "case", "forward")) { endpos = $search_end pos2 = search("::\\s*\\w+", pos, "regex", "forward") if (pos2 >= pos) endpos = $search_end res = Development_extractPrototypeCPP(endpos - 1) # dialog("in method: "method"\n" \ # " found method: "res["class"] "::" res["method"] "\n" \ # "< " prototype "\n" \ # "> " res["prototypeCheck"]) if (("prototypeCheck" in res) && check == res["prototypeCheck"]) { set_cursor_pos(pos) select(pos, pos + length(method)) dialog($file_path $file_name "\nline " $line "\n" \ "Method\n " check "\nalready implemented") return } else if (!("class" in res)) { set_cursor_pos(pos) select(pos, pos + length(method)) dialog($file_path $file_name "\nline " $line "\n" \ "Method\n " check "\n missing class name") return } else if (method == (res["class"] "::" res["method"])) { spos = substring(pos ": ", 0, poslen + 2) overloads = overloads spos res["prototypeCheck"] "\n" } } if (overloads != "") { s = list_dialog("The method "method" prototype:\n "check \ "\nhas overloads:", overloads, \ "Create", "Go there", "Cancel") if ($list_dialog_button == 1) { # do nothing } else if ($list_dialog_button == 2 && s != "") { pos = replace_in_string(s, ":.*", "", "regex") set_cursor_pos(pos) select(pos, pos + length(method)) return } else return } pt = prototype # do we need to break it? maxCols = $Development_Layout["MaxCols"] if (maxCols && length(pt) > maxCols) { pos = search_string(pt, method, 0, "case") + length(method) # insert newlines for every argument e = search_string(pt, "\\(.*\\)", pos, "regex") if (e < pos) dialog("No parentheses found: not a method!") e++ pt = substring(pt, 0, e) "\n " substring(pt, e) pos = e + 5 for (e = search_string(pt, ",\\s*", pos, "regex"); \ e > pos; \ e = search_string(pt, ",\\s*", pos, "regex")) { e++ f = $search_end pt = substring(pt, 0, e) "\n " substring(pt, f) pos = e + 5 } e = search_string(pt, ")", pos, "case") if (e < pos) dialog("No closing parenthesis found: not a method!") pt = substring(pt, 0, e) "\n " substring(pt, e) } cmt = "-----------" cmt = "// " cmt cmt cmt cmt cmt cmt cmt "\n" pos = $text_length set_cursor_pos($text_length) # insert a new method body todo = "// TODO: complete method body\n" insert_string("\n\n" cmt "\n/// \n\n" pt "\n {\n " todo " }\n") # tidy up any sequence of empty lines: we only want one blank line at most find(".", "backward", "regex") replace("\n\n\n+", "\n\n", "forward", "regex", "nobell") set_cursor_pos($text_length) set_cursor_pos($cursor - 5) }