File indexing completed on 2024-05-19 04:00:08
0001 var katescript = { 0002 "name": "CMake", 0003 "author": "Alex Turbov <i.zaufi@gmail.com>", 0004 "license": "LGPL", 0005 "revision": 2, 0006 "kate-version": "5.1", 0007 "required-syntax-style": "CMake", 0008 "indent-languages": ["CMake"] 0009 }; // kate-script-header, must be at the start of the file without comments, pure json 0010 0011 /* 0012 This file is part of the Kate Project. 0013 0014 SPDX-License-Identifier: LGPL-2.0-or-later 0015 */ 0016 0017 /** 0018 * Some settings it assumes being in effect: 0019 * indent-width 4; 0020 * space-indent true; 0021 * 0022 * \todo Better to check (assert) some of that modelines... 0023 */ 0024 0025 // required katepart js libraries 0026 require ("cmake_indenter_config.js") 0027 require ("range.js"); 0028 require ("string.js"); 0029 require ("utils.js") 0030 require ("underscore.js") 0031 0032 triggerCharacters = "()$<{\""; 0033 0034 //BEGIN Configuration 0035 var debugMode = false; 0036 //END Configuration 0037 0038 /// Global var to store indentation width for current document type 0039 var gIndentWidth = 4; 0040 /// Map of CMake calls where a key is the \e end of a corresponding \e start call 0041 var END_BEGIN_CALL_PAIRS = { 0042 "endfunction": "function" 0043 , "endmacro": "macro" 0044 , "endforeach" : "foreach" 0045 , "endif" : {target: ["if", "else", "elseif"], push: ["endif"], pop: ["if"]} 0046 , "elseif" : {target: ["if", "elseif"], push: ["endif"], pop: ["if"]} 0047 , "else" : {target: ["if", "elseif"], push: ["endif"], pop: ["if"]} 0048 , "endwhile" : "while" 0049 }; 0050 /// List of CMake commands which introduce indentation decrease 0051 var CONTROL_FLOW_CALLS_TO_UNINDENT_AFTER = [ 0052 "break", "return" 0053 ]; 0054 /// List of CMake commands which introduce indentation increase 0055 var CONTROL_FLOW_CALLS_TO_INDENT_AFTER = [ 0056 "function", "macro", "foreach", "if", "else", "elseif", "while" 0057 ]; 0058 /// List of CMake calls w/o (and/or all optional) parameters 0059 var PARAMETERLESS_CALLS = [ 0060 "break", "else", "elseif", "enable_testing", "endforeach", "endif", "endmacro", "endwhile", "return" 0061 ]; 0062 /// List of CMake command options which have parameter(s) 0063 var INDENT_AFTER_OPTIONS = [ 0064 "OUTPUT" // add_custom_command 0065 , "COMMAND" // add_custom_command, add_custom_target, add_test, execute_process, if 0066 , "ARGS" // add_custom_command, try_run 0067 , "MAIN_DEPENDENCY" // add_custom_command 0068 , "DEPENDS" // add_custom_command, add_custom_target 0069 , "IMPLICIT_DEPENDS" // add_custom_command 0070 , "WORKING_DIRECTORY" // add_custom_command, add_custom_target, add_test, execute_process 0071 , "COMMENT" // add_custom_command, add_custom_target 0072 , "TARGET" // add_custom_command, build_command, get_property, if, set_property 0073 , "SOURCES" // add_custom_target, try_compile 0074 , "ALIAS" // add_executable 0075 , "OBJECT" // add_library, add_library 0076 , "NAME" // add_test 0077 , "CONFIGURATIONS" // add_test, install 0078 , "CONFIGURATION" // build_command 0079 , "PROJECT_NAME" // build_command 0080 , "RESULT" // cmake_host_system_information 0081 , "QUERY" // cmake_host_system_information 0082 , "VERSION" // cmake_minimum_required, cmake_policy 0083 , "SET" // cmake_policy 0084 , "GET" // cmake_policy, list 0085 , "NEWLINE_STYLE" // configure_file 0086 , "EXTRA_INCLUDE" // create_test_sourcelist 0087 , "FUNCTION" // create_test_sourcelist 0088 , "PROPERTY" // define_property, get_property, set_property 0089 , "BRIEF_DOCS" // define_property 0090 , "FULL_DOCS" // define_property 0091 , "TIMEOUT" // execute_process, file 0092 , "RESULT_VARIABLE" // execute_process, include 0093 , "OUTPUT_VARIABLE" // execute_process, try_compile, try_run 0094 , "ERROR_VARIABLE" // execute_process 0095 , "INPUT_FILE" // execute_process 0096 , "OUTPUT_FILE" // execute_process 0097 , "ERROR_FILE" // execute_process 0098 , "TARGETS" // export, install 0099 , "NAMESPACE" // export, install 0100 , "FILE" // export, install 0101 , "PACKAGE" // export 0102 , "WRITE" // file 0103 , "APPEND" // file, list 0104 , "READ" // file 0105 , "LIMIT" // file 0106 , "OFFSET" // file 0107 , "STRINGS" // file 0108 , "LIMIT_COUNT" // file 0109 , "LIMIT_INPUT" // file 0110 , "LIMIT_OUTPUT" // file 0111 , "LENGTH_MINIMUM" // file 0112 , "LENGTH_MAXIMUM" // file 0113 , "REGEX" // file, string 0114 , "GLOB" // file 0115 , "RELATIVE" // file 0116 , "GLOB_RECURSE" // file 0117 , "RENAME" // file, install 0118 , "REMOVE" // file 0119 , "REMOVE_RECURSE" // file 0120 , "MAKE_DIRECTORY" // file 0121 , "RELATIVE_PATH" // file 0122 , "TO_CMAKE_PATH" // file 0123 , "TO_NATIVE_PATH" // file 0124 , "DOWNLOAD" // file 0125 , "INACTIVITY_TIMEOUT" // file 0126 , "STATUS" // file 0127 , "LOG" // file 0128 , "EXPECTED_HASH" // file 0129 , "EXPECTED_MD5" // file 0130 , "TLS_VERIFY" // file 0131 , "TLS_CAINFO" // file 0132 , "UPLOAD" // file 0133 , "TIMESTAMP" // file, string 0134 , "GENERATE" // file 0135 , "INPUT" // file 0136 , "CONTENT" // file 0137 , "CONDITION" // file 0138 , "COPY" // file 0139 , "INSTALL" // file 0140 , "DESTINATION" // file, install 0141 , "FILE_PERMISSIONS" // file, install 0142 , "DIRECTORY_PERMISSIONS" // file, install 0143 , "FILES_MATCHING" // file, install 0144 , "PATTERN" // file, install 0145 , "PERMISSIONS" // file, install 0146 , "EXCLUDE" // file, install, load_cache 0147 , "NAMES" // find_file, find_library, find_package, find_path, find_program 0148 , "HINTS" // find_file, find_library, find_package, find_path, find_program 0149 , "ENV" // find_file, find_library, find_path, find_program 0150 , "PATHS" // find_file, find_library, find_package, find_path, find_program 0151 , "PATH_SUFFIXES" // find_file, find_library, find_package, find_path, find_program 0152 , "DOC" // find_file, find_library, find_path, find_program 0153 , "REQUIRED" // find_package 0154 , "COMPONENTES" // find_package 0155 , "OPTIONAL_COMPONENTES" // find_package 0156 , "CONFIGS" // find_package 0157 , "DIRECTORY" // get_directory_property, get_property, install, set_property 0158 , "DEFINITION" // get_directory_property 0159 , "PROGRAM" // get_filename_component 0160 , "PROGRAM_ARGS" // get_filename_component 0161 , "SOURCE" // get_property, set_property 0162 , "TEST" // get_property, set_property 0163 , "CACHE" // get_property, set, set_property 0164 , "NOT" // if 0165 , "AND" // if 0166 , "OR" // if 0167 , "POLICY" // if 0168 , "EXISTS" // if 0169 , "IS_NEWER_THAN" // if 0170 , "IS_DIRECTORY" // if 0171 , "IS_SYMLINK" // if 0172 , "IS_ABSOLUTE" // if 0173 , "MATCHES" // if 0174 , "LESS" // if, string 0175 , "GREATER" // if, string 0176 , "EQUAL" // if, string 0177 , "STRLESS" // if 0178 , "STRGREATER" // if 0179 , "STREQUAL" // if 0180 , "VERSION_LESS" // if 0181 , "VERSION_GREATER" // if 0182 , "VERSION_EQUAL" // if 0183 , "DEFINED" // if 0184 , "TYPE" // include_external_msproject 0185 , "GUID" // include_external_msproject 0186 , "PLATFORM" // include_external_msproject 0187 , "EXPORT" // install 0188 , "ARCHIVE" // install 0189 , "LIBRARY" // install 0190 , "RUNTIME" // install 0191 , "FRAMEWORK" // install 0192 , "BUNDLE" // install 0193 , "PRIVATE_HEADER" // install 0194 , "PUBLIC_HEADER" // install 0195 , "RESOURCE" // install 0196 , "COMPONENT" // install 0197 , "FILES" // install 0198 , "PROGRAMS" // install 0199 , "SCRIPT" // install 0200 , "CODE" // install 0201 , "LENGTH" // list, string 0202 , "FIND" // list, string 0203 , "INSERT" // list 0204 , "REMOVE_ITEM" // list 0205 , "REMOVE_AT" // list 0206 , "REMOVE_DUPLICATES" // list 0207 , "REVERSE" // list 0208 , "SORT" // list 0209 , "READ_WITH_PREFIX" // load_cache 0210 , "INCLUDE_INTERNALS" // load_cache 0211 , "EXPR" // math 0212 , "UNIX_COMMAND" // separate_arguments 0213 , "WINDOWS_COMMAND" // separate_arguments 0214 // set_directory_properties, set_source_files_properties, set_target_properties, set_tests_properties 0215 , "PROPERTIES" 0216 , "REGULAR_EXPRESSION" // source_group 0217 , "FILES" // source_group 0218 , "MATCH" // string 0219 , "MATCHALL" // string 0220 , "REPLACE" // string 0221 , "COMPARE" // string 0222 , "NOTEQUAL" // string 0223 , "ASCII" // string 0224 , "CONFIGURE" // string 0225 , "TOUPPER" // string 0226 , "TOLOWER" // string 0227 , "SUBSTRING" // string 0228 , "STRIP" // string 0229 , "RANDOM" // string 0230 , "ALPHABET" // string 0231 , "RANDOM_SEED" // string 0232 , "MAKE_C_IDENTIFIER" // string 0233 , "INTERFACE" // target_compile_definitions, target_compile_options, target_include_directories 0234 , "PUBLIC" // target_compile_definitions, target_compile_options, target_include_directories 0235 , "PRIVATE" // target_compile_definitions, target_compile_options, target_include_directories 0236 , "LINK_PRIVATE" // target_link_libraries 0237 , "LINK_PUBLIC" // target_link_libraries 0238 , "CMAKE_FLAGS" // try_compile, try_run 0239 , "COMPILE_DEFINITIONS" // try_compile, try_run 0240 , "LINK_LIBRARIES" // try_compile 0241 , "COPY_FILE" // try_compile 0242 , "COPY_FILE_ERROR" // try_compile 0243 , "COMPILE_OUTPUT_VARIABLE" // try_run 0244 , "RUN_OUTPUT_VARIABLE" // try_run 0245 ]; 0246 0247 0248 /** 0249 * \brief Handle \c ENTER between parenthesis 0250 */ 0251 function tryParenthesisSplit_ch(cursor) 0252 { 0253 var result = -1; 0254 0255 if (isStringOrComment(cursor.line - 1, cursor.column)) 0256 return result; // Do nothing for comments and strings 0257 0258 // Is ENTER was pressed right after '('? 0259 if (document.lastChar(cursor.line - 1) == '(') 0260 { 0261 // Yep, lets get align of the previous line 0262 var prev_line_indent = document.firstColumn(cursor.line - 1); 0263 result = prev_line_indent + gIndentWidth; 0264 // Indent a closing ')' if ENTER was pressed between '(|)' 0265 if (document.firstChar(cursor.line) == ')') 0266 { 0267 document.insertText( 0268 cursor.line 0269 , cursor.column 0270 , "\n" + String().fill(' ', prev_line_indent + gIndentWidth / 2) 0271 ); 0272 view.setCursorPosition(cursor.line, cursor.column); 0273 } 0274 } 0275 0276 if (result != -1) 0277 { 0278 dbg("tryParenthesisSplit_ch: result =", result); 0279 } 0280 return result; 0281 } 0282 0283 /** 0284 * \brief Indent after some control flow CMake calls 0285 */ 0286 function tryIndentAfterControlFlowCalls_ch(cursor) 0287 { 0288 var result = -1; 0289 0290 if (isStringOrComment(cursor.line - 1, cursor.column)) 0291 return result; // Do nothing for comments and strings 0292 0293 var prev_line_indent = document.firstColumn(cursor.line - 1); 0294 var first_prev_line_word = document.wordAt(cursor.line - 1, prev_line_indent).toLowerCase(); 0295 dbg("tryControlFlowCalls_ch: first_prev_line_word =", first_prev_line_word); 0296 if (CONTROL_FLOW_CALLS_TO_INDENT_AFTER.indexOf(first_prev_line_word) != -1) 0297 { 0298 result = prev_line_indent + gIndentWidth; 0299 } 0300 else if (CONTROL_FLOW_CALLS_TO_UNINDENT_AFTER.indexOf(first_prev_line_word) != -1) 0301 { 0302 result = prev_line_indent - gIndentWidth; 0303 } 0304 0305 if (result != -1) 0306 { 0307 dbg("tryControlFlowCalls_ch: result =", result); 0308 } 0309 return result; 0310 } 0311 0312 /** 0313 * \brief Unindent after dandling ')' 0314 */ 0315 function tryAfterClosingParensis_ch(cursor) 0316 { 0317 var result = -1; 0318 0319 if (isStringOrComment(cursor.line - 1, cursor.column)) 0320 return result; // Do nothing for comments and strings 0321 0322 // Check if dandling ')' present on a previous line 0323 var prev_line = document.prevNonEmptyLine(cursor.line - 1); 0324 dbg("tryAfterClosingParensis_ch: prev_line =", prev_line); 0325 var first_column = document.firstColumn(prev_line); 0326 if (document.charAt(prev_line, first_column) == ')') 0327 result = first_column - (gIndentWidth / 2); 0328 0329 if (result != -1) 0330 { 0331 dbg("tryAfterClosingParensis_ch: result =", result); 0332 } 0333 return result; 0334 } 0335 0336 /** 0337 * \brief Indent after parameterless command options 0338 * 0339 * It is common way to format options w/ parameters like this: 0340 * \code 0341 * install( 0342 * DIRECTORY ${name} 0343 * DESTINATION ${DATA_INSTALL_DIR}/kate/pate 0344 * FILES_MATCHING 0345 * PATTERN "*.py" 0346 * PATTERN "*.ui" 0347 * PATTERN "*_ui.rc" 0348 * PATTERN "__pycache__*" EXCLUDE 0349 * ) 0350 * \endcode 0351 * 0352 * I.e. do indent for long parameter lists. 0353 * This function recognize parameterless options (and do not indent after them), 0354 * for everything else it adds an extra indentation... 0355 */ 0356 function tryIndentCommandOptions_ch(cursor) 0357 { 0358 var result = -1; 0359 0360 if (isStringOrComment(cursor.line - 1, cursor.column)) 0361 return result; // Do nothing for comments and strings 0362 0363 // Get last word from a previous line 0364 var last_word = document.wordAt(cursor.line - 1, document.lastColumn(cursor.line - 1)); 0365 0366 // ATTENTION Kate will return the last word "TARGET" for text like this: "TARGET)", 0367 // which is not what we've wanted... so before continue lets make sure that we've 0368 // got that we wanted... 0369 if (last_word[last_word.length - 1] != document.lastChar(cursor.line - 1)) 0370 return result; 0371 0372 dbg("tryIndentCommandOptions_ch: last_word =", last_word); 0373 result = document.firstColumn(cursor.line - 1) 0374 + (INDENT_AFTER_OPTIONS.indexOf(last_word) != -1 ? gIndentWidth : 0) 0375 ; 0376 0377 if (result != -1) 0378 { 0379 dbg("tryIndentCommandOptions_ch: result =", result); 0380 } 0381 return result; 0382 } 0383 0384 /** 0385 * \brief Handle \c ENTER key 0386 */ 0387 function caretPressed(cursor) 0388 { 0389 var result = -1; 0390 var line = cursor.line; 0391 0392 // Dunno what to do if previous line isn't available 0393 if (line - 1 < 0) 0394 return result; // Nothing (dunno) to do if no previous line... 0395 0396 // Register all indent functions 0397 var handlers = [ 0398 tryParenthesisSplit_ch // Handle ENTER between parenthesis 0399 , tryIndentAfterControlFlowCalls_ch // Indent after some control flow CMake calls 0400 , tryAfterClosingParensis_ch // Unindent after dandling ')' 0401 , tryIndentCommandOptions_ch // Indent after parameterless command options 0402 ]; 0403 0404 // Apply all functions until result gets changed 0405 for ( 0406 var i = 0 0407 ; i < handlers.length && result == -1 0408 ; result = handlers[i++](cursor) 0409 ); 0410 0411 return result; 0412 } 0413 0414 /** 0415 * \brief Try to unindent current call when <tt>(</tt> has pressed 0416 */ 0417 function tryUnindentCall(cursor) 0418 { 0419 var result = -2; 0420 0421 if (isStringOrComment(cursor.line, cursor.column)) 0422 return result; // Do nothing for comments and strings 0423 0424 // Check the word before current cursor position 0425 var call_name = document.wordAt(cursor.line, cursor.column - 1).toLowerCase(); 0426 dbg("tryUnindentCall: call_name =", call_name); 0427 var end_calls = _.keys(END_BEGIN_CALL_PAIRS); 0428 var stack_level = 0; 0429 if (_.indexOf(end_calls, call_name) != -1) 0430 { 0431 var lookup_call_name = END_BEGIN_CALL_PAIRS[call_name]; 0432 dbg("tryUnindentCall: typeof lookup_call_name =", typeof lookup_call_name); 0433 var matcher; 0434 if (typeof lookup_call_name === "string") 0435 { 0436 matcher = function(word) 0437 { 0438 return word == lookup_call_name; 0439 }; 0440 } 0441 else if (typeof lookup_call_name === "object") 0442 { 0443 matcher = function(word) 0444 { 0445 // dbg("tryUnindentCall: stack_level =", stack_level, ", word =", word); 0446 if (_.indexOf(lookup_call_name.push, word) != -1) 0447 { 0448 stack_level += 1; 0449 return false; 0450 } 0451 if (stack_level != 0) 0452 { 0453 if (_.indexOf(lookup_call_name.pop, word) != -1) 0454 stack_level -= 1; 0455 return false; 0456 } 0457 return _.indexOf(lookup_call_name.target, word) != -1; 0458 }; 0459 } 0460 else 0461 { 0462 /// \todo Is there any kind of \c assert() in JavaScript at all? 0463 dbg("tryUnindentCall: Unknown type of END_BEGIN_CALL_PAIRS value! Code review required!"); 0464 return result; 0465 } 0466 0467 // Ok, lets find the corresponding start call towards document's start... 0468 var found_line = -1; 0469 for ( 0470 var line = document.prevNonEmptyLine(cursor.line - 1) 0471 ; 0 <= line && found_line == -1 0472 ; line = document.prevNonEmptyLine(line - 1) 0473 ) 0474 { 0475 var first_word = document.wordAt(line, document.firstColumn(line)).toLowerCase(); 0476 // dbg("tryUnindentCall: line =", line, ", word =", first_word); 0477 if (matcher(first_word)) 0478 { 0479 dbg("tryUnindentCall: Found ", first_word); 0480 found_line = line; 0481 } 0482 } 0483 0484 if (found_line != -1) 0485 { 0486 result = document.firstColumn(found_line); 0487 // Add closing parenthesis if none yet 0488 // addCharOrJumpOverIt(cursor.line, cursor.column, ')'); 0489 } 0490 else 0491 { 0492 dbg("tryUnindentCall: Not Found!"); 0493 } 0494 } 0495 0496 if (result != -2) 0497 { 0498 dbg("tryUnindentCall: result =", result); 0499 } 0500 return result; 0501 } 0502 0503 /** 0504 * Align closing parenthesis if it is a first character on a line 0505 */ 0506 function tryAlignCloseParenthesis(cursor) 0507 { 0508 var result = -2; 0509 0510 if (isStringOrComment(cursor.line, cursor.column)) 0511 return result; // Do nothing for comments and strings 0512 0513 if (justEnteredCharIsFirstOnLine(cursor.line, cursor.column, ')')) 0514 { 0515 // Try to find corresponding open parenthesis 0516 var open = document.anchor(cursor.line, cursor.column - 1, '('); 0517 dbg("tryAlignCloseParenthesis: Found open brace at", open); 0518 if (open.isValid()) 0519 result = document.firstColumn(open.line) + (gIndentWidth / 2); 0520 } 0521 0522 if (result != -2) 0523 { 0524 dbg("tryAlignCloseParenthesis: result =", result); 0525 } 0526 return result; 0527 } 0528 0529 /** 0530 * \brief Move cursor out of <tt>'()'</tt> if no parameters required for a given call 0531 */ 0532 function tryJumpOutOfParenthesis(cursor) 0533 { 0534 if (isStringOrComment(cursor.line, cursor.column)) 0535 return; // Do nothing for comments and strings 0536 0537 var first_word = document.wordAt(cursor.line, document.firstColumn(cursor.line)).toLowerCase(); 0538 dbg("tryJumpOutOfParenthesis: @"+cursor.line+","+cursor.column+", char="+document.charAt(cursor)); 0539 if (PARAMETERLESS_CALLS.indexOf(first_word) != -1) 0540 addCharOrJumpOverIt(cursor.line, cursor.column, ')') 0541 } 0542 0543 /** 0544 * \brief Append <tt>'{}'</tt> after \c '$' 0545 */ 0546 function insertVariableExpansion(cursor) 0547 { 0548 if (!cmi_cfg_vgShortcut || isComment(cursor.line, cursor.column)) 0549 return; // Do nothing for comments 0550 0551 var next_ch = document.charAt(cursor); 0552 dbg("insertVariableExpansion: next_ch ='"+next_ch+"'"); 0553 if (!next_ch.match(/[A-Za-z_]/)) 0554 { 0555 if (next_ch != '{') 0556 document.insertText(cursor, "{}"); 0557 view.setCursorPosition(cursor.line, cursor.column + 1); 0558 } 0559 } 0560 0561 /** 0562 * \brief Handle <tt>'$'</tt> character 0563 * 0564 * Here is possble few transformations: if \c '<' pressed after \c '$', 0565 * transform <tt>'${<}'</tt> to <tt>'$<>'</tt> to be ready for 0566 * generator expression and vise versa. 0567 */ 0568 function tryVariableOrGeneratorExpression(cursor, ch) 0569 { 0570 if (!cmi_cfg_vgShortcut || isComment(cursor.line, cursor.column)) 0571 return; // Do nothing for comments and strings 0572 0573 var tail = document.text( 0574 cursor.line 0575 , document.firstColumn(cursor.line) 0576 , cursor.line 0577 , cursor.column 0578 ); 0579 var next_ch = document.charAt(cursor); 0580 dbg("tryVariableOrGeneratorExpression: tail ='"+tail+"'"); 0581 var fix_text = function(text) 0582 { 0583 document.removeText(cursor.line, cursor.column - 2, cursor.line, cursor.column + 1); 0584 document.insertText(cursor.line, cursor.column - 2, text); 0585 view.setCursorPosition(cursor.line, cursor.column - 1); 0586 }; 0587 if (tail.endsWith("${<") && next_ch == '}') 0588 { 0589 fix_text("<>"); // Transform '${}' -> '$<>' 0590 } 0591 else if (tail.endsWith("$<{") && next_ch == '>') 0592 { 0593 fix_text("{}"); // Transform '$<>' -> '${}' 0594 } 0595 else if (tail.endsWith("${{") && next_ch == '}') 0596 { 0597 // Transform '${{|}' --> '${|}' 0598 document.removeText(cursor.line, cursor.column - 1, cursor.line, cursor.column); 0599 } 0600 else if (tail.endsWith("$<<") && next_ch == '>') 0601 { 0602 // Transform '$<<|>' --> '$<|>' 0603 document.removeText(cursor.line, cursor.column - 1, cursor.line, cursor.column); 0604 } 0605 } 0606 0607 /** 0608 * \brief Open/close string literal 0609 * 0610 * \attention Autobracket extension w/ add quote char doesn't work 0611 * for CMake files (file a BUG?)... 0612 */ 0613 function tryString(cursor) 0614 { 0615 if (isComment(cursor.line, cursor.column)) 0616 return; // Do nothing for comments 0617 0618 // NOTE If cursor has a string attribute, then it was an open quote 0619 // character just entered... 0620 // ATTENTION If cursor positioned at the end of a line, 0621 // isString() returns 'false' for some unknown reason... 0622 // so check the attribute for the just entered quote symbol... 0623 if (isString(cursor.line, cursor.column - 1)) 0624 { 0625 // Check if next char is not a quote already 0626 // and/or maybe some punctualtion... Particularly 0627 // ')' remains after some function call -- i.e. smth like 0628 // message(STATUS "|) 0629 // or just entered quot char is a first on a line and 0630 // no other chars are here... 0631 var ch = document.charAt(cursor); 0632 if ((ch != '"' && (ch == ')' || ch == ' ')) 0633 || (justEnteredCharIsFirstOnLine(cursor.line, cursor.column, '"') 0634 && document.lineLength(cursor.line) == cursor.column 0635 ) 0636 ) 0637 { 0638 document.insertText(cursor, '"') 0639 view.setCursorPosition(cursor); 0640 } 0641 } // Do nothing for closing quote char 0642 } 0643 0644 /** 0645 * \brief Process one character 0646 * 0647 * NOTE Cursor positioned right after just entered character and has \c +1 in column. 0648 */ 0649 function processChar(line, ch) 0650 { 0651 var result = -2; // By default, do nothing... 0652 var cursor = view.cursorPosition(); 0653 if (!cursor) 0654 return result; 0655 0656 document.editBegin(); 0657 // Check if char under cursor is the same as just entered, 0658 // and if so, remove it... to make it behave like "overwrite" mode 0659 if (ch != ' ' && document.charAt(cursor) == ch) 0660 document.removeText(line, cursor.column, line, cursor.column + 1); 0661 0662 switch (ch) 0663 { 0664 case '\n': 0665 result = caretPressed(cursor); 0666 break; 0667 case '(': 0668 result = tryUnindentCall(cursor); 0669 tryJumpOutOfParenthesis(cursor); 0670 break; 0671 case ')': 0672 result = tryAlignCloseParenthesis(cursor); 0673 break; 0674 case '$': 0675 insertVariableExpansion(cursor); 0676 break; 0677 case '{': 0678 case '<': 0679 tryVariableOrGeneratorExpression(cursor, ch); 0680 break; 0681 case '"': 0682 tryString(cursor); 0683 break; 0684 default: 0685 break; // Nothing to do... 0686 } 0687 0688 document.editEnd(); 0689 return result; 0690 } 0691 0692 /** 0693 * Try to align a given line 0694 * \todo More actions 0695 */ 0696 function indentLine(line) 0697 { 0698 dbg(">> Going to indent line ", line); 0699 var cursor = new Cursor(line, document.firstColumn(line) + 1); 0700 var result = tryUnindentCall(cursor); 0701 if (result == -2) 0702 result = tryAlignCloseParenthesis(cursor); 0703 cursor = new Cursor(line, document.firstColumn(line)); 0704 if (result == -2) 0705 result = caretPressed(cursor); 0706 dbg("indentLine: result =", result); 0707 0708 if (result == -2) // Still dunno what to do? 0709 result = -1; // ... just align according a previous non empty line 0710 return result; 0711 } 0712 0713 /** 0714 * \brief Process a newline or one of \c triggerCharacters character. 0715 * 0716 * This function is called whenever the user hits \c ENTER key. 0717 * 0718 * It gets three arguments: \c line, \c indentwidth in spaces and typed character 0719 * 0720 * Called for each newline (<tt>ch == \n</tt>) and all characters specified in 0721 * the global variable \c triggerCharacters. When calling \e Tools->Align 0722 * the variable \c ch is empty, i.e. <tt>ch == ''</tt>. 0723 */ 0724 function indent(line, indentWidth, ch) 0725 { 0726 // NOTE Update some global variables 0727 gIndentWidth = indentWidth; 0728 0729 dbg("indentWidth: " + indentWidth); 0730 dbg(" gMode: " + document.highlightingModeAt(view.cursorPosition())); 0731 dbg(" gAttr: " + document.attributeName(view.cursorPosition())); 0732 dbg(" line: " + line); 0733 dbg(" ch: '" + ch + "'"); 0734 0735 if (ch != "") 0736 return processChar(line, ch); 0737 0738 return indentLine(line); 0739 } 0740 0741 // kate: space-indent on; indent-width 4; replace-tabs on; 0742 0743 /** 0744 * \todo Move string/comment checks to \c caretPressed() instead of particular function. 0745 */