From 4a0c45ef552c0a2897644cf715ba3e48be9ff173 Mon Sep 17 00:00:00 2001 From: Chris Ainsley Date: Tue, 2 Jun 2015 20:45:02 +0100 Subject: [PATCH] Support non-linked template values Currently, "autocomplete" allows templates to be used for autocompletes. The problem is that only one cursor tabstop can be used ( to signify the cursor resting place after completion), and any values specified as varnames are linked in the editor such that specifying a value for one "varname" will alter the values for all other varnames that share the same varname. Sometimes we want to be able to alter values independently but we want the same initial value. This update adds the following syntaxes Example 1: ${:replacement}${:replacement}${:replacement} In the above example, there will be three tabstops showing "replacment" (without the colon). If the colon was not specified then typing in the highlighted area would alter all three "replacement" fields, with the colon, now there are three replacements to modify across the three tabstops. Typing in one will not affect the others. Alteration 2: Allow the '}' character in varnames. Example: ${:\}} This will create a tabstop showing an initial value of "}" (without quotes). The value of this change is that sometimes the tabestop may require values that include braced brackets but currently, rbrace is not supported as a template value. With the escaped rbrace, it allows a full range of template values. --- .../ui/autocomplete/TemplateCompletion.java | 58 +++++++++++++++---- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/fife/ui/autocomplete/TemplateCompletion.java b/src/main/java/org/fife/ui/autocomplete/TemplateCompletion.java index e640e38..e3473de 100644 --- a/src/main/java/org/fife/ui/autocomplete/TemplateCompletion.java +++ b/src/main/java/org/fife/ui/autocomplete/TemplateCompletion.java @@ -291,24 +291,57 @@ private void parse(String template) { char next = template.charAt(offs+1); switch (next) { case '$': // "$$" => escaped single dollar sign - addTemplatePiece(new TemplatePiece.Text( - template.substring(lastOffs, offs+1))); + addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs+1))); lastOffs = offs + 2; break; case '{': // "${...}" => variable - int closingCurly = template.indexOf('}', offs+2); - if (closingCurly>-1) { - addTemplatePiece(new TemplatePiece.Text( - template.substring(lastOffs, offs))); - String varName = template.substring(offs+2, closingCurly); - if (!"cursor".equals(varName) && isParamDefined(varName)) { - addTemplatePiece(new TemplatePiece.ParamCopy(varName)); + int closingCurly = -2; + + // Allow an escaped closing brace character in the 'varname' part of the template + // Here we check that a closing brace is not prefixed by a backslash + // NOTE :: It doesn't deal with \\ as a prefix to a non escaped '}' currently. + + int scanStart = offs+2; + while (closingCurly == -2) { + closingCurly = template.indexOf('}', scanStart); + if (closingCurly != -1) { + if (template.charAt(closingCurly-1) == '\\') { + scanStart = closingCurly+1; + closingCurly = -2; + } + } + } + + if (closingCurly > -1) { + final String textPriorToTemplateBlock = template.substring(lastOffs, offs); + final TemplatePiece.Text piece = new TemplatePiece.Text(textPriorToTemplateBlock); + addTemplatePiece(piece); + + // Replace escaped rbrace (backslash rbrace) with just rbrace (in varname) + String varName = template.substring(offs+2, closingCurly).replace("\\}", "}"); + + // Use a colon as a signifier that the varname is a non-unified 'varname', so we + // can use the same initial text in autocomplete without unifying the text input + // based on the same initial key. Any varname which starts with colon is essentially + // marked as 'not a variable', and it means that we can substitute freeform text + // without the autocompleter linking user input based on the values matching (which + // is the current behaviour). + + boolean isTabStopNotVarname = varName.startsWith(":"); + varName = isTabStopNotVarname ? varName.substring(1) : varName; + + if ( (!"cursor".equals(varName)) && isParamDefined(varName) && (!isTabStopNotVarname)) { + final TemplatePiece.ParamCopy piece2 = new TemplatePiece.ParamCopy(varName); + addTemplatePiece(piece2); } else { - addTemplatePiece(new TemplatePiece.Param(varName)); + final TemplatePiece.Param piece2 = new TemplatePiece.Param(varName); + addTemplatePiece(piece2); } lastOffs = closingCurly + 1; } + + break; } @@ -316,7 +349,8 @@ private void parse(String template) { if (lastOffs