// Name: C# helper macros for SlickEdit // Version: 0.2 // Author: Ivan N. Zlatev // License: MIT/X11 // /* ############################################################################ Usage ############################################################################ This macro defines a set of C# helper commands to generate code from the variable under cursor. cs_generate_property (_str access="public") cs_generate_argument_null_check cs_generate_field_from_local For a given class: internal class Document { public Document (string file) { } } where the cursor is on "file" cs_generate_property() will generate excluding the comments: internal class Document { private string _file; // cs_generate_field_from_local public Document (string file) { if (file == null) // cs_generate_argument_null_check throw new ArgumentNullException ("file"); _file = file; } public string File { // cs_generate_property get { return _file; } set { _file = value; } } } */ #include "slick.sh" #include "refactor.sh" #include "tagsdb.sh" #include "quickrefactor.sh" _command void cs_generate_argument_null_check () { VS_TAG_BROWSE_INFO varInfo; if (tag_get_browse_info ("", varInfo) >= 0) generate_argument_null_check_from_local (varInfo); } _command void cs_generate_property (_str access = "public") { VS_TAG_BROWSE_INFO varInfo; if (tag_get_browse_info ("", varInfo) >= 0) generate_property_from_local (access, varInfo); } _command void cs_generate_field_from_local () { VS_TAG_BROWSE_INFO varInfo; if (tag_get_browse_info ("", varInfo) >= 0) { generate_assignment (generate_field_from_local (varInfo), varInfo.member_name); } } static void generate_argument_null_check_from_local (VS_TAG_BROWSE_INFO varInfo) { typeless p; _save_pos2 (p); _str lineText; get_line (lineText); while (!endsWith (lineText, "{")) { p_line++; get_line (lineText); } _str indent = get_line_leading_whitespace() :+ indent_string (p_SyntaxIndent); end_line (); _insert_text (p_newline :+ indent :+ "if (" :+ varInfo.member_name :+ " == null)" :+ p_newline :+ indent :+ indent_string (p_SyntaxIndent) :+ "throw new ArgumentNullException (\"" :+ varInfo.member_name :+ "\");"); _restore_pos2 (p); } static void generate_assignment (_str toName, _str fromName) { typeless p; _save_pos2 (p); _str lineText; get_line (lineText); while (!endsWith (lineText, "{")) { p_line++; get_line (lineText); } _str indent = get_line_leading_whitespace() :+ indent_string (p_SyntaxIndent); end_line (); _insert_text (p_newline :+ indent :+ toName :+ " = " :+ fromName :+ ";"); _restore_pos2 (p); } static void generate_property_from_local (_str access, VS_TAG_BROWSE_INFO varInfo) { typeless p; _save_pos2 (p); _str fieldName = generate_field_from_local (varInfo); if (fieldName == null) return; generate_assignment (fieldName, varInfo.member_name); // generate null check for reference types // FIXME: check inheritance tree of var to determine if it is a Sruct // if (strcmp (varInfo.return_type, "short") != 0 && strcmp (varInfo.return_type, "double") != 0 && strcmp (varInfo.return_type, "int") != 0 && strcmp (varInfo.return_type, "byte") != 0 && strcmp (varInfo.return_type, "struct") != 0 && strcmp (varInfo.return_type, "IntPtr") != 0) { cs_generate_argument_null_check (); } p_line = get_function_context_end_line (); _str indent = get_line_leading_whitespace (); end_line (); _str name = varInfo.member_name; name = upcase (substr (name,1,1)) :+ substr (name,2); // upper case the first letter _str property = p_newline :+ p_newline :+ indent :+ access :+ " " :+ varInfo.return_type :+ " " :+ name :+ " {" :+ p_newline :+ indent :+ indent_string (p_SyntaxIndent) :+ "get { return " :+ fieldName :+ "; }" :+ p_newline :+ indent :+ indent_string (p_SyntaxIndent) :+ "set { " :+ fieldName :+ " = value; }" :+ p_newline :+ indent :+ "}"; _insert_text (property); _restore_pos2 (p); } // generates a field prefixed with "_" from a local variable just after the class definition // static _str generate_field_from_local (VS_TAG_BROWSE_INFO varInfo) { typeless p; _save_pos2 (p); _str lineText; VS_TAG_BROWSE_INFO classInfo; if (get_class_context (_QROffset (), classInfo) < 0) return null; p_line = classInfo.scope_line_no; get_line (lineText); if (!endsWith (lineText, "{"))// opening { not on the same line of the class definition p_line++; // assuming opening { is on the next line, so move to there _str indent = get_line_leading_whitespace () :+ indent_string (p_SyntaxIndent); end_line (); _str fieldName = "_" :+ varInfo.member_name; _str field = p_newline :+ indent :+ "private " :+ varInfo.return_type :+ " " :+ fieldName :+ ";"; end_line (); _insert_text (field); _restore_pos2 (p); return fieldName; } // ############################################################################ // Helper Functions taken from refactor.e and quickrefactor.e // ############################################################################ // // static int get_class_context(int seek_position, VS_TAG_BROWSE_INFO &class_cm) { int old_seek_position = (int)_QROffset(); _GoToROffset(seek_position); // get browse information for the tag under the symbol _UpdateContext(true, false, VS_UPDATEFLAG_statement | VS_UPDATEFLAG_list_all); tag_browse_info_init(class_cm); int context_id = tag_current_context(); while (context_id > 0) { // get information about this context item, is it the proc? tag_get_context_info(context_id, class_cm); if (class_cm.type_name=='class' || class_cm.type_name=='struct') { break; } // go up one level tag_get_detail2(VS_TAGDETAIL_context_outer, context_id, context_id); } // Couldn't find a class context. if(class_cm.type_name != 'class' && class_cm.type_name != 'struct') { return -1; } _GoToROffset(old_seek_position); return 0; } static int get_function_context_end_line () { int context_id; int end_line; // get browse information for the tag under the symbol _UpdateContext(true, false, VS_UPDATEFLAG_statement | VS_UPDATEFLAG_list_all); context_id = tag_current_context(); context_type = 0; while (context_id > 0) { // get information about this context item, is it the proc? tag_get_detail2(VS_TAGDETAIL_context_end_linenum, context_id, end_line); tag_get_detail2(VS_TAGDETAIL_context_type, context_id, context_type); if (tag_tree_type_is_func(context_type) && context_type!='proto' && context_type!='procproto') { break; } // go up one level tag_get_detail2(VS_TAGDETAIL_context_outer, context_id, context_id); } return end_line } static boolean endsWith(_str name,_str possibleSuffix) { lenPossibleSuffix := length(possibleSuffix); lenName := length(name); if ( lenPossibleSuffix > lenName ) { return false; } if ( lenPossibleSuffix :== lenName ) { return false; } _str suffix=substr(name,(lenName-lenPossibleSuffix)+1); return suffix:==possibleSuffix; } static _str get_line_leading_whitespace(_str text='', boolean including_newlines=false) { if(text == '') { get_line(text); } // Grab leading whitespace _str leading_indention = ""; int p; if(including_newlines) { p = pos("[^ \t\n\r]", text, 1, "U"); } else { p = pos("[^ \t]", text, 1, "U"); } if(p > 1) { leading_indention = substr(text, 1, p-1); } return leading_indention; }