From 5368c8151df632ba0ebd863cbc7ff105dc4027bf Mon Sep 17 00:00:00 2001 From: Christoph Schlosser Date: Thu, 1 Mar 2018 23:17:23 +0100 Subject: Change config structure --- src/CodeParserController.ts | 4 +- src/Config.ts | 124 +++++++++++++++++++--------- src/Lang/Cpp/CppDocGen.ts | 73 ++++++++-------- src/Lang/Cpp/CppParser.ts | 2 +- src/test/CppTests/Con-AndDestructor.test.ts | 2 +- src/test/CppTests/Config.test.ts | 34 ++++---- src/test/CppTests/FileDescription.test.ts | 2 +- src/test/CppTests/SmartText.test.ts | 8 +- 8 files changed, 146 insertions(+), 103 deletions(-) (limited to 'src') diff --git a/src/CodeParserController.ts b/src/CodeParserController.ts index 1b42f3d..1084c46 100644 --- a/src/CodeParserController.ts +++ b/src/CodeParserController.ts @@ -72,7 +72,7 @@ export default class CodeParserController { const cont: string = activeLine.text.trim(); - return this.cfg.triggerSequence === cont; + return this.cfg.C.triggerSequence === cont; } private onEvent(activeEditor: TextEditor, event: TextDocumentContentChangeEvent) { @@ -97,7 +97,7 @@ export default class CodeParserController { const currentPos: Position = window.activeTextEditor.selection.active; const startReplace: Position = new Position( currentPos.line, - currentPos.character - this.cfg.triggerSequence.length, + currentPos.character - this.cfg.C.triggerSequence.length, ); const nextLineText: string = window.activeTextEditor.document.lineAt(startReplace.line + 1).text; diff --git a/src/Config.ts b/src/Config.ts index e3740b0..ed88386 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -1,62 +1,104 @@ import { workspace } from "vscode"; +// tslint:disable:max-classes-per-file +// tslint:disable:max-line-length -export class Config { - public static ImportFromSettings(): Config { - const values: Config = new Config(); - - const cfg = workspace.getConfiguration("doxdocgen.generic"); - - values.triggerSequence = cfg.get("triggerSequence", values.triggerSequence); - values.firstLine = cfg.get("firstLine", values.firstLine); - values.commentPrefix = cfg.get("commentPrefix", values.commentPrefix); - values.lastLine = cfg.get("lastLine", values.lastLine); - values.newLineAfterBrief = cfg.get("newLineAfterBrief", values.newLineAfterBrief); - values.newLineAfterParams = cfg.get("newLineAfterParams", values.newLineAfterParams); - values.newLineAfterTParams = cfg.get("newLineAfterTParams", values.newLineAfterTParams); - values.includeTypeAtReturn = cfg.get("includeTypeAtReturn", values.includeTypeAtReturn); - values.boolReturnsTrueFalse = cfg.get("boolReturnsTrueFalse", values.boolReturnsTrueFalse); - values.briefTemplate = cfg.get("briefTemplate", values.briefTemplate); - values.paramTemplate = cfg.get("paramTemplate", values.paramTemplate); - values.tparamTemplate = cfg.get("tparamTemplate", values.tparamTemplate); - values.returnTemplate = cfg.get("returnTemplate", values.returnTemplate); - values.linesToGet = cfg.get("linesToGet", values.linesToGet); - values.authorTag = cfg.get("authorTag", values.authorTag); - values.fileTemplate = cfg.get("fileTemplate", values.fileTemplate); - values.dateTemplate = cfg.get("dateTemplate", values.dateTemplate); - values.dateFormat = cfg.get("dateFormat", values.dateFormat); - values.fileOrder = cfg.get("fileOrder", values.fileOrder); - values.ctorText = cfg.get("ctorText", values.ctorText); - values.dtorText = cfg.get("dtorText", values.dtorText); - values.generateSmartText = cfg.get("generateSmartText", values.generateSmartText); - - return values; +class C { + public static getConfiguration() { + return workspace.getConfiguration("doxdocgen.c"); } - public readonly paramTemplateReplace: string = "{param}"; - public readonly typeTemplateReplace: string = "{type}"; - public readonly nameTemplateReplace: string = "{name}"; - public readonly dateTemplateReplace: string = "{date}"; - public triggerSequence: string = "/**"; public firstLine: string = "/**"; public commentPrefix: string = " * "; public lastLine: string = " */"; +} + +class Cpp { + public static getConfiguration() { + return workspace.getConfiguration("doxdocgen.cpp"); + } + + public newLineAfterTParams: boolean = false; + public tparamTemplate: string = "@tparam {param} "; + public ctorText: string = "Construct a new {name} object"; + public dtorText: string = "Destroy the {name} object"; +} + +class File { + public static getConfiguration() { + return workspace.getConfiguration("doxdocgen.file"); + } + + public fileTemplate: string = "@file {name}"; + public fileOrder: string[] = ["brief", "file", "author", "date"]; +} + +class Generic { + public static getConfiguration() { + return workspace.getConfiguration("doxdocgen.generic"); + } + public newLineAfterBrief: boolean = true; public newLineAfterParams: boolean = false; - public newLineAfterTParams: boolean = false; public includeTypeAtReturn: boolean = true; public boolReturnsTrueFalse: boolean = true; public briefTemplate: string = "@brief "; public paramTemplate: string = "@param {param} "; - public tparamTemplate: string = "@tparam {param} "; public returnTemplate: string = "@return {type} "; public linesToGet: number = 20; public authorTag: string = "@author your name"; - public fileTemplate: string = "@file {name}"; public dateTemplate: string = "@date {date}"; public dateFormat: string = "YYYY-MM-DD"; - public fileOrder: string[] = ["brief", "file", "author", "date"]; - public ctorText: string = "Construct a new {name} object"; - public dtorText: string = "Destroy the {name} object"; public generateSmartText: boolean = true; } + +export class Config { + public static ImportFromSettings(): Config { + const values: Config = new Config(); + + values.C.triggerSequence = C.getConfiguration().get("triggerSequence", values.C.triggerSequence); + values.C.firstLine = C.getConfiguration().get("firstLine", values.C.firstLine); + values.C.commentPrefix = C.getConfiguration().get("commentPrefix", values.C.commentPrefix); + values.C.lastLine = C.getConfiguration().get("lastLine", values.C.lastLine); + + values.Cpp.newLineAfterTParams = Cpp.getConfiguration().get("newLineAfterTParams", values.Cpp.newLineAfterTParams); + values.Cpp.tparamTemplate = Cpp.getConfiguration().get("tparamTemplate", values.Cpp.tparamTemplate); + values.Cpp.ctorText = Cpp.getConfiguration().get("ctorText", values.Cpp.ctorText); + values.Cpp.dtorText = Cpp.getConfiguration().get("dtorText", values.Cpp.dtorText); + + values.File.fileTemplate = File.getConfiguration().get("fileTemplate", values.File.fileTemplate); + values.File.fileOrder = File.getConfiguration().get("fileOrder", values.File.fileOrder); + + values.Generic.newLineAfterBrief = Generic.getConfiguration().get("newLineAfterBrief", values.Generic.newLineAfterBrief); + values.Generic.newLineAfterParams = Generic.getConfiguration().get("newLineAfterParams", values.Generic.newLineAfterParams); + values.Generic.includeTypeAtReturn = Generic.getConfiguration().get("includeTypeAtReturn", values.Generic.includeTypeAtReturn); + values.Generic.boolReturnsTrueFalse = Generic.getConfiguration().get("boolReturnsTrueFalse", values.Generic.boolReturnsTrueFalse); + values.Generic.briefTemplate = Generic.getConfiguration().get("briefTemplate", values.Generic.briefTemplate); + values.Generic.paramTemplate = Generic.getConfiguration().get("paramTemplate", values.Generic.paramTemplate); + values.Generic.returnTemplate = Generic.getConfiguration().get("returnTemplate", values.Generic.returnTemplate); + values.Generic.linesToGet = Generic.getConfiguration().get("linesToGet", values.Generic.linesToGet); + values.Generic.authorTag = Generic.getConfiguration().get("authorTag", values.Generic.authorTag); + values.Generic.dateTemplate = Generic.getConfiguration().get("dateTemplate", values.Generic.dateTemplate); + values.Generic.dateFormat = Generic.getConfiguration().get("dateFormat", values.Generic.dateFormat); + values.Generic.generateSmartText = Generic.getConfiguration().get("generateSmartText", values.Generic.generateSmartText); + + return values; + } + + public readonly paramTemplateReplace: string = "{param}"; + public readonly typeTemplateReplace: string = "{type}"; + public readonly nameTemplateReplace: string = "{name}"; + public readonly dateTemplateReplace: string = "{date}"; + + public C: C; + public Cpp: Cpp; + public File: File; + public Generic: Generic; + + constructor() { + this.C = new C(); + this.Cpp = new Cpp(); + this.File = new File(); + this.Generic = new Generic(); + } +} diff --git a/src/Lang/Cpp/CppDocGen.ts b/src/Lang/Cpp/CppDocGen.ts index b825570..eb60c9a 100644 --- a/src/Lang/Cpp/CppDocGen.ts +++ b/src/Lang/Cpp/CppDocGen.ts @@ -90,7 +90,7 @@ export class CppDocGen implements IDocGen { } protected getSmartText(): string { - if (!this.cfg.generateSmartText) { + if (!this.cfg.Generic.generateSmartText) { return ""; } switch (this.specialCase) { @@ -99,7 +99,7 @@ export class CppDocGen implements IDocGen { return ""; } else { const str = this.getTemplatedString(this.cfg.nameTemplateReplace, - this.cfg.ctorText, + this.cfg.Cpp.ctorText, this.func.name.trim()); this.smartTextLength = str.length; return str; @@ -110,7 +110,7 @@ export class CppDocGen implements IDocGen { return ""; } else { const str = this.getTemplatedString(this.cfg.nameTemplateReplace, - this.cfg.dtorText, + this.cfg.Cpp.dtorText, this.func.name.replace("~", "").trim()); this.smartTextLength = str.length; return str; @@ -125,7 +125,7 @@ export class CppDocGen implements IDocGen { } protected generateBrief(lines: string[]) { - lines.push(this.cfg.commentPrefix + this.cfg.briefTemplate + this.getSmartText()); + lines.push(this.cfg.C.commentPrefix + this.cfg.Generic.briefTemplate + this.getSmartText()); } protected generateFromTemplate(lines: string[], replace: string, template: string, templateWith: string[]) { @@ -134,7 +134,7 @@ export class CppDocGen implements IDocGen { templateWith.forEach((element: string) => { // Ignore null values if (element !== null) { - line = this.cfg.commentPrefix; + line = this.cfg.C.commentPrefix; line += this.getTemplatedString(replace, template, element); lines.push(line); } @@ -142,7 +142,7 @@ export class CppDocGen implements IDocGen { } protected generateReturnParams(): string[] { - if (this.cfg.includeTypeAtReturn === false) { + if (this.cfg.Generic.includeTypeAtReturn === false) { return [""]; } @@ -160,65 +160,65 @@ export class CppDocGen implements IDocGen { const boolReturnIndex: number = this.func.type.nodes .findIndex((n) => n instanceof CppToken && n.type === CppTokenType.Symbol && n.value === "bool"); - if (boolReturnIndex !== -1 && this.cfg.boolReturnsTrueFalse === true) { + if (boolReturnIndex !== -1 && this.cfg.Generic.boolReturnsTrueFalse === true) { params.push("true"); params.push("false"); } else if (voidReturnIndex !== -1 && ptrReturnIndex !== -1) { - params.push(this.cfg.includeTypeAtReturn === true ? this.func.type.Yield() : ""); + params.push(this.cfg.Generic.includeTypeAtReturn === true ? this.func.type.Yield() : ""); } else if (voidReturnIndex === -1 && this.func.type.nodes.length > 0) { - params.push(this.cfg.includeTypeAtReturn === true ? this.func.type.Yield() : ""); + params.push(this.cfg.Generic.includeTypeAtReturn === true ? this.func.type.Yield() : ""); } return params; } protected generateAuthorTag(lines: string[]) { - if (this.cfg.authorTag.trim().length !== 0) { - lines.push(this.cfg.commentPrefix + this.cfg.authorTag); + if (this.cfg.Generic.authorTag.trim().length !== 0) { + lines.push(this.cfg.C.commentPrefix + this.cfg.Generic.authorTag); } } protected generateFilenameFromTemplate(lines: string[]) { - if (this.cfg.fileTemplate.trim().length !== 0) { + if (this.cfg.File.fileTemplate.trim().length !== 0) { this.generateFromTemplate( lines, this.cfg.nameTemplateReplace, - this.cfg.fileTemplate, + this.cfg.File.fileTemplate, [this.activeEditor.document.fileName.replace(/^.*[\\\/]/, "")], ); } } protected generateDateFromTemplate(lines: string[]) { - if (this.cfg.dateTemplate.trim().length !== 0 && - this.cfg.dateFormat.trim().length !== 0) { + if (this.cfg.Generic.dateTemplate.trim().length !== 0 && + this.cfg.Generic.dateFormat.trim().length !== 0) { this.generateFromTemplate( lines, this.cfg.dateTemplateReplace, - this.cfg.dateTemplate, - [moment().format(this.cfg.dateFormat)], + this.cfg.Generic.dateTemplate, + [moment().format(this.cfg.Generic.dateFormat)], ); } } protected insertFirstLine(lines: string[]) { - if (this.cfg.firstLine.trim().length !== 0) { - lines.push(this.cfg.firstLine); + if (this.cfg.C.firstLine.trim().length !== 0) { + lines.push(this.cfg.C.firstLine); } } protected insertBrief(lines: string[]) { - if (this.cfg.briefTemplate.trim().length !== 0) { + if (this.cfg.Generic.briefTemplate.trim().length !== 0) { this.generateBrief(lines); - if (this.cfg.newLineAfterBrief === true) { - lines.push(this.cfg.commentPrefix); + if (this.cfg.Generic.newLineAfterBrief === true) { + lines.push(this.cfg.C.commentPrefix); } } } protected insertLastLine(lines: string[]) { - if (this.cfg.lastLine.trim().length !== 0) { - lines.push(this.cfg.lastLine); + if (this.cfg.C.lastLine.trim().length !== 0) { + lines.push(this.cfg.C.lastLine); } } @@ -227,7 +227,7 @@ export class CppDocGen implements IDocGen { this.insertFirstLine(lines); - this.cfg.fileOrder.forEach((element) => { + this.cfg.File.fileOrder.forEach((element) => { switch (element) { case "brief": { this.insertBrief(lines); @@ -263,29 +263,30 @@ export class CppDocGen implements IDocGen { this.insertBrief(lines); - if (this.cfg.tparamTemplate.trim().length !== 0 && this.templateParams.length > 0) { + if (this.cfg.Cpp.tparamTemplate.trim().length !== 0 && this.templateParams.length > 0) { this.generateFromTemplate( lines, this.cfg.paramTemplateReplace, - this.cfg.tparamTemplate, + this.cfg.Cpp.tparamTemplate, this.templateParams, ); - if (this.cfg.newLineAfterTParams === true) { - lines.push(this.cfg.commentPrefix); + if (this.cfg.Cpp.newLineAfterTParams === true) { + lines.push(this.cfg.C.commentPrefix); } } - if (this.cfg.paramTemplate.trim().length !== 0 && this.params.length > 0) { + if (this.cfg.Generic.paramTemplate.trim().length !== 0 && this.params.length > 0) { const paramNames: string[] = this.params.map((p) => p.name); - this.generateFromTemplate(lines, this.cfg.paramTemplateReplace, this.cfg.paramTemplate, paramNames); - if (this.cfg.newLineAfterParams === true) { - lines.push(this.cfg.commentPrefix); + this.generateFromTemplate(lines, this.cfg.paramTemplateReplace, this.cfg.Generic.paramTemplate, paramNames); + if (this.cfg.Generic.newLineAfterParams === true) { + lines.push(this.cfg.C.commentPrefix); } } - if (this.cfg.returnTemplate.trim().length !== 0 && this.func.type !== null) { + if (this.cfg.Generic.returnTemplate.trim().length !== 0 && this.func.type !== null) { const returnParams = this.generateReturnParams(); - this.generateFromTemplate(lines, this.cfg.typeTemplateReplace, this.cfg.returnTemplate, returnParams); + // tslint:disable-next-line:max-line-length + this.generateFromTemplate(lines, this.cfg.typeTemplateReplace, this.cfg.Generic.returnTemplate, returnParams); } this.insertLastLine(lines); @@ -300,7 +301,7 @@ export class CppDocGen implements IDocGen { let character: number = comment.indexOf("\n"); // If a first line is included find the 2nd line with a newline. - if (this.cfg.firstLine.trim().length !== 0) { + if (this.cfg.C.firstLine.trim().length !== 0) { line++; const oldCharacter: number = character; character = comment.indexOf("\n", oldCharacter + 1) - oldCharacter; diff --git a/src/Lang/Cpp/CppParser.ts b/src/Lang/Cpp/CppParser.ts index 8dfa005..00d14c3 100644 --- a/src/Lang/Cpp/CppParser.ts +++ b/src/Lang/Cpp/CppParser.ts @@ -286,7 +286,7 @@ export default class CppParser implements ICodeParser { logicalLine = nextLineTxt; // Get method end line - let linesToGet: number = this.cfg.linesToGet; + let linesToGet: number = this.cfg.Generic.linesToGet; while (linesToGet-- > 0) { // Check for end of expression. nextLine = new Position(nextLine.line + 1, nextLine.character); nextLineTxt = this.activeEditor.document.lineAt(nextLine.line).text.trim(); diff --git a/src/test/CppTests/Con-AndDestructor.test.ts b/src/test/CppTests/Con-AndDestructor.test.ts index 7858c30..36e4fd6 100644 --- a/src/test/CppTests/Con-AndDestructor.test.ts +++ b/src/test/CppTests/Con-AndDestructor.test.ts @@ -15,7 +15,7 @@ import TestSetup from "./TestSetup"; suite("C++ - Con- and Destructor Tests", () => { const testSetup: TestSetup = new TestSetup("void foo();"); - testSetup.cfg.generateSmartText = false; + testSetup.cfg.Generic.generateSmartText = false; // Tests test("Normal Constructor", () => { diff --git a/src/test/CppTests/Config.test.ts b/src/test/CppTests/Config.test.ts index f210cf4..d37f93d 100644 --- a/src/test/CppTests/Config.test.ts +++ b/src/test/CppTests/Config.test.ts @@ -28,13 +28,13 @@ suite("C++ - Configuration Tests", () => { test("Modified template", () => { testSetup.cfg = new Config(); - testSetup.cfg.firstLine = ""; - testSetup.cfg.lastLine = ""; - testSetup.cfg.commentPrefix = "/// "; - testSetup.cfg.briefTemplate = "\\brief "; - testSetup.cfg.paramTemplate = "\\param {param} "; - testSetup.cfg.tparamTemplate = "\\tparam {param} "; - testSetup.cfg.returnTemplate = "\\return {type} "; + testSetup.cfg.C.firstLine = ""; + testSetup.cfg.C.lastLine = ""; + testSetup.cfg.C.commentPrefix = "/// "; + testSetup.cfg.Generic.briefTemplate = "\\brief "; + testSetup.cfg.Generic.paramTemplate = "\\param {param} "; + testSetup.cfg.Cpp.tparamTemplate = "\\tparam {param} "; + testSetup.cfg.Generic.returnTemplate = "\\return {type} "; const result = testSetup.SetLine("template bool foo(T a);").GetResult(); assert.equal("/// \\brief \n/// \n/// \\tparam T \n/// \\param a \n" @@ -44,7 +44,7 @@ suite("C++ - Configuration Tests", () => { test("Disable true false on bool", () => { testSetup.cfg = new Config(); - testSetup.cfg.boolReturnsTrueFalse = false; + testSetup.cfg.Generic.boolReturnsTrueFalse = false; const result = testSetup.SetLine("template bool foo(T a);").GetResult(); assert.equal("/**\n * @brief \n * \n * @tparam T \n * @param a \n" + " * @return bool \n */", result); @@ -52,7 +52,7 @@ suite("C++ - Configuration Tests", () => { test("Disable including return type.", () => { testSetup.cfg = new Config(); - testSetup.cfg.includeTypeAtReturn = false; + testSetup.cfg.Generic.includeTypeAtReturn = false; const result = testSetup.SetLine("template bool foo(T a);").GetResult(); assert.equal("/**\n * @brief \n * \n * @tparam T \n * @param a \n" @@ -61,9 +61,9 @@ suite("C++ - Configuration Tests", () => { test("Newlines after params and tparams but not after brief", () => { testSetup.cfg = new Config(); - testSetup.cfg.newLineAfterBrief = false; - testSetup.cfg.newLineAfterParams = true; - testSetup.cfg.newLineAfterTParams = true; + testSetup.cfg.Generic.newLineAfterBrief = false; + testSetup.cfg.Generic.newLineAfterParams = true; + testSetup.cfg.Cpp.newLineAfterTParams = true; const result = testSetup.SetLine("template bool foo(T a);").GetResult(); assert.equal("/**\n * @brief \n * @tparam T \n * \n * @param a \n * \n" @@ -84,11 +84,11 @@ suite("C++ - Configuration Tests", () => { test("Lines to get test", () => { testSetup.cfg = new Config(); - testSetup.cfg.linesToGet = 2; + testSetup.cfg.Generic.linesToGet = 2; const positiveResult = testSetup.SetLines(["template bool \n", "foo(T a);"]).GetResult(); assert.equal("/**\n * @brief \n * \n * @tparam T \n * @param a \n * " + "@return true \n * @return false \n */", positiveResult); - testSetup.cfg.linesToGet = 0; + testSetup.cfg.Generic.linesToGet = 0; testSetup.firstLine = 1; const negativeResult = testSetup.SetLines(["template bool \n", "foo(T a);"]).GetResult(); assert.equal("/**\n * @brief \n * \n */", negativeResult); @@ -97,20 +97,20 @@ suite("C++ - Configuration Tests", () => { test("File description order test", () => { testSetup.cfg = new Config(); testSetup.firstLine = 0; - testSetup.cfg.fileOrder = ["brief", "author", "date", "file"]; + testSetup.cfg.File.fileOrder = ["brief", "author", "date", "file"]; const result = testSetup.SetLine("").GetResult(); assert.equal("/**\n * @brief \n * \n * @author your name\n" + " * @date " + moment().format("YYYY-MM-DD") + "\n * @file MockDocument.h\n */", result); }); test("Custom smart text Ctor", () => { - testSetup.cfg.ctorText = "Test {name}"; + testSetup.cfg.Cpp.ctorText = "Test {name}"; const result = testSetup.SetLine("Foo();").GetResult(); assert.equal("/**\n * @brief Test Foo\n * \n */", result); }); test("Custom smart text Dtor", () => { - testSetup.cfg.dtorText = "Test {name}"; + testSetup.cfg.Cpp.dtorText = "Test {name}"; const result = testSetup.SetLine("~Foo();").GetResult(); assert.equal("/**\n * @brief Test Foo\n * \n */", result); }); diff --git a/src/test/CppTests/FileDescription.test.ts b/src/test/CppTests/FileDescription.test.ts index c4ab4af..8c8d4d4 100644 --- a/src/test/CppTests/FileDescription.test.ts +++ b/src/test/CppTests/FileDescription.test.ts @@ -31,7 +31,7 @@ suite("File Description Tests", () => { }); test("Don't generate non existing commands", () => { - testSetup.cfg.fileOrder = ["dates"]; + testSetup.cfg.File.fileOrder = ["dates"]; const result = testSetup.SetLine("").GetResult(); assert.equal("/**\n */", result); }); diff --git a/src/test/CppTests/SmartText.test.ts b/src/test/CppTests/SmartText.test.ts index bca00da..23ecaba 100644 --- a/src/test/CppTests/SmartText.test.ts +++ b/src/test/CppTests/SmartText.test.ts @@ -17,17 +17,17 @@ suite("Smart Text Tests", () => { // Tests test("Disable smart text Ctor", () => { - testSetup.cfg.generateSmartText = false; + testSetup.cfg.Generic.generateSmartText = false; const result = testSetup.SetLine("Foo();").GetResult(); assert.equal("/**\n * @brief \n * \n */", result); - testSetup.cfg.generateSmartText = true; // for later tests + testSetup.cfg.Generic.generateSmartText = true; // for later tests }); test("Disable smart text Dtor", () => { - testSetup.cfg.generateSmartText = false; + testSetup.cfg.Generic.generateSmartText = false; const result = testSetup.SetLine("~Foo();").GetResult(); assert.equal("/**\n * @brief \n * \n */", result); - testSetup.cfg.generateSmartText = true; // for later tests + testSetup.cfg.Generic.generateSmartText = true; // for later tests }); test("Standard smart text Ctor", () => { -- cgit v1.2.3