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  */