45 class Feedback { |
45 class Feedback { |
46 |
46 |
47 // Patern for substituted fields within a customized format string |
47 // Patern for substituted fields within a customized format string |
48 private static final Pattern FIELD_PATTERN = Pattern.compile("\\{(.*?)\\}"); |
48 private static final Pattern FIELD_PATTERN = Pattern.compile("\\{(.*?)\\}"); |
49 |
49 |
|
50 // Internal field name for truncation length |
|
51 private static final String TRUNCATION_FIELD = "<truncation>"; |
|
52 |
50 // Current mode |
53 // Current mode |
51 private Mode mode = new Mode("", false); // initial value placeholder during start-up |
54 private Mode mode = new Mode("", false); // initial value placeholder during start-up |
52 |
55 |
53 // Mapping of mode names to mode modes |
56 // Mapping of mode names to mode modes |
54 private final Map<String, Mode> modeMap = new HashMap<>(); |
57 private final Map<String, Mode> modeMap = new HashMap<>(); |
99 return new Setter(messageHandler, at).setFeedback(); |
102 return new Setter(messageHandler, at).setFeedback(); |
100 } |
103 } |
101 |
104 |
102 public boolean setFormat(MessageHandler messageHandler, ArgTokenizer at) { |
105 public boolean setFormat(MessageHandler messageHandler, ArgTokenizer at) { |
103 return new Setter(messageHandler, at).setFormat(); |
106 return new Setter(messageHandler, at).setFormat(); |
|
107 } |
|
108 |
|
109 public boolean setTruncation(MessageHandler messageHandler, ArgTokenizer at) { |
|
110 return new Setter(messageHandler, at).setTruncation(); |
104 } |
111 } |
105 |
112 |
106 public boolean setNewMode(MessageHandler messageHandler, ArgTokenizer at) { |
113 public boolean setNewMode(MessageHandler messageHandler, ArgTokenizer at) { |
107 return new Setter(messageHandler, at).setNewMode(); |
114 return new Setter(messageHandler, at).setNewMode(); |
108 } |
115 } |
249 } |
256 } |
250 m.appendTail(sb); |
257 m.appendTail(sb); |
251 return sb.toString(); |
258 return sb.toString(); |
252 } |
259 } |
253 |
260 |
|
261 // Compute the display output given full context and values |
254 String format(FormatCase fc, FormatAction fa, FormatWhen fw, |
262 String format(FormatCase fc, FormatAction fa, FormatWhen fw, |
255 FormatResolve fr, FormatUnresolved fu, FormatErrors fe, |
263 FormatResolve fr, FormatUnresolved fu, FormatErrors fe, |
256 String name, String type, String value, String unresolved, List<String> errorLines) { |
264 String name, String type, String value, String unresolved, List<String> errorLines) { |
|
265 // Convert the context into a bit representation used as selectors for store field formats |
257 long bits = bits(fc, fa, fw, fr, fu, fe); |
266 long bits = bits(fc, fa, fw, fr, fu, fe); |
258 String fname = name==null? "" : name; |
267 String fname = name==null? "" : name; |
259 String ftype = type==null? "" : type; |
268 String ftype = type==null? "" : type; |
260 String fvalue = value==null? "" : value; |
269 // Compute the representation of value |
|
270 String fvalue; |
|
271 if (value==null) { |
|
272 fvalue = ""; |
|
273 } else { |
|
274 // Retrieve the truncation length |
|
275 String truncField = format(TRUNCATION_FIELD, bits); |
|
276 if (truncField.isEmpty()) { |
|
277 // No truncation set, use whole value |
|
278 fvalue = value; |
|
279 } else { |
|
280 // Convert truncation length to int |
|
281 // this is safe since it has been tested before it is set |
|
282 int trunc = Integer.parseUnsignedInt(truncField); |
|
283 if (value.length() > trunc) { |
|
284 if (trunc <= 5) { |
|
285 // Very short truncations have no room for "..." |
|
286 fvalue = value.substring(0, trunc); |
|
287 } else { |
|
288 // Normal truncation, make total length equal truncation length |
|
289 fvalue = value.substring(0, trunc - 4) + " ..."; |
|
290 } |
|
291 } else { |
|
292 // Within truncation length, use whole value |
|
293 fvalue = value; |
|
294 } |
|
295 } |
|
296 } |
261 String funresolved = unresolved==null? "" : unresolved; |
297 String funresolved = unresolved==null? "" : unresolved; |
262 String errors = errorLines.stream() |
298 String errors = errorLines.stream() |
263 .map(el -> String.format( |
299 .map(el -> String.format( |
264 format("errorline", bits), |
300 format("errorline", bits), |
265 fname, ftype, fvalue, funresolved, "*cannot-use-errors-here*", el)) |
301 fname, ftype, fvalue, funresolved, "*cannot-use-errors-here*", el)) |
617 String field = at.next(); |
653 String field = at.next(); |
618 if (field == null || at.isQuoted()) { |
654 if (field == null || at.isQuoted()) { |
619 errorat("jshell.err.feedback.expected.field"); |
655 errorat("jshell.err.feedback.expected.field"); |
620 valid = false; |
656 valid = false; |
621 } |
657 } |
622 String format = valid? nextFormat() : null; |
658 String format = valid ? nextFormat() : null; |
|
659 return installFormat(m, field, format, "/help /set format"); |
|
660 } |
|
661 |
|
662 // For /set truncation <mode> <length> <selector>... |
|
663 boolean setTruncation() { |
|
664 Mode m = nextMode(); |
|
665 String length = at.next(); |
|
666 if (length == null) { |
|
667 errorat("jshell.err.truncation.expected.length"); |
|
668 valid = false; |
|
669 } else { |
|
670 try { |
|
671 // Assure that integer format is correct |
|
672 Integer.parseUnsignedInt(length); |
|
673 } catch (NumberFormatException ex) { |
|
674 errorat("jshell.err.truncation.length.not.integer", length); |
|
675 valid = false; |
|
676 } |
|
677 } |
|
678 // install length into an internal format field |
|
679 return installFormat(m, TRUNCATION_FIELD, length, "/help /set truncation"); |
|
680 } |
|
681 |
|
682 // install the format of a field under parsed selectors |
|
683 boolean installFormat(Mode m, String field, String format, String help) { |
623 String slRaw; |
684 String slRaw; |
624 List<SelectorList> slList = new ArrayList<>(); |
685 List<SelectorList> slList = new ArrayList<>(); |
625 while (valid && (slRaw = at.next()) != null) { |
686 while (valid && (slRaw = at.next()) != null) { |
626 SelectorList sl = new SelectorList(); |
687 SelectorList sl = new SelectorList(); |
627 sl.parseSelectorList(slRaw); |
688 sl.parseSelectorList(slRaw); |
628 slList.add(sl); |
689 slList.add(sl); |
629 } |
690 } |
630 if (valid) { |
691 if (valid) { |
631 if (slList.isEmpty()) { |
692 if (slList.isEmpty()) { |
|
693 // No selectors specified, then always the format |
632 m.set(field, ALWAYS, format); |
694 m.set(field, ALWAYS, format); |
633 } else { |
695 } else { |
|
696 // Set the format of the field for specified selector |
634 slList.stream() |
697 slList.stream() |
635 .forEach(sl -> m.set(field, |
698 .forEach(sl -> m.set(field, |
636 sl.cases.getSet(), sl.actions.getSet(), sl.whens.getSet(), |
699 sl.cases.getSet(), sl.actions.getSet(), sl.whens.getSet(), |
637 sl.resolves.getSet(), sl.unresolvedCounts.getSet(), sl.errorCounts.getSet(), |
700 sl.resolves.getSet(), sl.unresolvedCounts.getSet(), sl.errorCounts.getSet(), |
638 format)); |
701 format)); |
639 } |
702 } |
640 } else { |
703 } else { |
641 fluffmsg("jshell.msg.see", "/help /set format"); |
704 fluffmsg("jshell.msg.see", help); |
642 } |
705 } |
643 return valid; |
706 return valid; |
644 } |
707 } |
645 |
708 |
646 Mode nextMode() { |
709 Mode nextMode() { |