Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Validate editor preferences #7186

Merged
merged 29 commits into from
Mar 31, 2014
Merged

Validate editor preferences #7186

merged 29 commits into from
Mar 31, 2014

Conversation

redmunds
Copy link
Contributor

This is for #7020.

I did a slight refactoring so editor prefs can be validated when ever they change. Currently only validate "tabSize" and "spaceUnits" which are numbers. All other prefs are boolean, so the only thing validation would do is to enforce default if user mucks up the prefs file.

I started a ValidationUtils module that might be useful in other places.

@dangoor dangoor self-assigned this Mar 13, 2014


/**
* @private (but might be useful in a Validation module)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't private any more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@dangoor
Copy link
Contributor

dangoor commented Mar 13, 2014

I think the validation should be done in the PreferencesManager. Something like this:

PreferencesManager.definePreference(SPACE_UNITS, "number", DEFAULT_SPACE_UNITS, {
    validator: function (value) {
        return ValidationUtils.isIntegerInRange(value, 0, null)
    }
});

Then PreferencesSystem.get can validate on the way out and return the default value if the current value is invalid. This eliminates a lot of the change to Editor.js and also makes it so that JSLint.main doesn't need to duplicate the validation logic.

What do you think of that approach?

@redmunds
Copy link
Contributor Author

I like the validator approach -- nice suggestion! I'll implement that.

@redmunds
Copy link
Contributor Author

Changes pushed. Ready for another review.

@@ -37,6 +37,7 @@ define(function (require, exports, module) {
var CodeInspection = brackets.getModule("language/CodeInspection"),
PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
Strings = brackets.getModule("strings"),
ValidationUtils = brackets.getModule("utils/ValidationUtils"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not required anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed.

@redmunds
Copy link
Contributor Author

@TomMalbran Thanks for the review. Changes pushed.

});
PreferencesManager.definePreference(SPACE_UNITS, "number", DEFAULT_SPACE_UNITS, {
validator: function (value) {
return ValidationUtils.isIntegerInRange(value, 0, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In https://github.com/adobe/brackets/blob/randy/issue-7020/src/editor/EditorStatusBar.js#L134 we make the value be between 1 and 10 for both preferences. Should we make 10 the max here too? And should we fix the min there so that it can be 0 for space units? Once we do that, the min and max values should be exported constants so that we can use them in EditorStatusBar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I forgot about that code! I agree that these should be consistent and I'll define a constant.

I thought @RaymondLim said that 0 is valid for space units, so that's why it's allowed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mean changing the 0 value here, but allow the user to set it to 0 when changing it in the Status Bar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that's what I did.

@redmunds
Copy link
Contributor Author

@TomMalbran Pushed more changes.

@@ -88,7 +88,7 @@ define(function (require, exports, module) {

if (!options.indent) {
// default to using the same indentation value that the editor is using
options.indent = PreferencesManager.get("spaceUnits");
options.indent = PreferencesManager.get("spaceUnits", fullPath);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why we don't use editor.getSpaceUnits here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have an Editor instance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I guess is fine then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice change! At first, I figured this wasn't required, but since we have the path in hand, we're better off using it. That makes this function potentially usable for mass linting independent of the current editor.

@TomMalbran
Copy link
Contributor

The code looks good to me now.

@redmunds
Copy link
Contributor Author

I'm seeing a PreferencesManager unit test fail, and should add some unit tests for the new validator functionality.

should find preferences in the project

Error: Expected 4 to be 92.
    at new jasmine.ExpectationResult (file:///C:/Users/redmunds/dev/github/brackets-shell/Release/dev/test/thirdparty/jasmine-core/jasmine.js:102:32)
    at null.toBe (file:///C:/Users/redmunds/dev/github/brackets-shell/Release/dev/test/thirdparty/jasmine-core/jasmine.js:1194:29)
    at null.<anonymous> (file:///C:/Users/redmunds/dev/github/brackets-shell/Release/dev/test/spec/PreferencesManager-test.js:137:62)
    at jasmine.Block.execute (file:///C:/Users/redmunds/dev/github/brackets-shell/Release/dev/test/thirdparty/jasmine-core/jasmine.js:1024:15)
    at jasmine.Queue.next_ (file:///C:/Users/redmunds/dev/github/brackets-shell/Release/dev/test/thirdparty/jasmine-core/jasmine.js:1842:31)
    at onComplete (file:///C:/Users/redmunds/dev/github/brackets-shell/Release/dev/test/thirdparty/jasmine-core/jasmine.js:1838:18)
    at jasmine.WaitsForBlock.execute (file:///C:/Users/redmunds/dev/github/brackets-shell/Release/dev/test/thirdparty/jasmine-core/jasmine.js:2322:5)
    at file:///C:/Users/redmunds/dev/github/brackets-shell/Release/dev/test/thirdparty/jasmine-core/jasmine.js:2336:12

@RaymondLim
Copy link
Contributor

@redmunds You may need to adjust the value in test file .brackets.json which is using 92 as spaceUnits and your validator only allows up to 10. As you adjust the value is test file, then you also need to adjust all the test cases using that specific value.

@redmunds
Copy link
Contributor Author

Changes pushed. Ready for another review.

@zaggino
Copy link
Contributor

zaggino commented Mar 18, 2014

@redmunds I started a bit on UI and will put together a PR once this is merged so I can include validator strings in that

var valInt = parseInt(value, 10);
if (editor.getUseTabChar()) {
if (!editor.setTabSize(valInt)) {
return; // validation failed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false does not necessarily mean that validation failed. It just means that the value wasn't set. Should we differentiate? I think we can still change the PreferencesSystem.set API at this point because nothing in Brackets relies on that return value, and I'd be surprised if extensions used that yet. In fact, your use here is the first use I've seen of the set return value.

With the API as it stands now, a false return value means that the caller will have to call a separate function to find out if the given value is valid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could easily differentiate. How about returning an object like:

{
    valid: {boolean},
    stored: {boolean}
}

Error message would be added at a later time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a good time to change it, since that API is unused and the change is simple. Your suggestion seems good to me.

@dangoor
Copy link
Contributor

dangoor commented Mar 18, 2014

I'm fine with punting on the extra validation features for now. We'll need to address them as soon as we have a preferences UI, but not necessarily before then.

@peterflynn
Copy link
Member

@redmunds The difference is that factories lead to more succinct and scannable code, with less boilerplate to clutter things. Compare:

    PreferencesManager.definePreference(TAB_SIZE, "number", DEFAULT_TAB_SIZE, {
        validator: function (value) {
            return ValidationUtils.isIntegerInRange(value, MIN_TAB_SIZE, MAX_TAB_SIZE);
        }
    });

vs.

PreferencesManager.definePreference(TAB_SIZE, "number", DEFAULT_TAB_SIZE,
    new Validators.IntValidator(MIN_TAB_SIZE, MAX_TAB_SIZE));

(And the difference could potentially get larger once error strings are introduced).

@zaggino
Copy link
Contributor

zaggino commented Mar 18, 2014

I think you shouldn't remove options object completely, so it should be

{ validator: new Validators.IntValidator(MIN_TAB_SIZE, MAX_TAB_SIZE) }

for a factory.

@redmunds
Copy link
Contributor Author

@zaggino

I started a bit on UI and will put together a PR once this is merged so I can include validator strings in that

Cool, Thanks.

@redmunds
Copy link
Contributor Author

@peterflynn Seems like the only difference is where the function gets created. I kind of like the idea of a ValidationUtils module that would just provide some basic functions that could be used for any kind of validation, so I'm leaning towards keeping it like it is.

@peterflynn
Copy link
Member

@redmunds The difference is how many times the function gets created -- the more verbose way adds 3 extra lines of code (or more) per preference. The factory idea only writes out the method body once, within the central utils module.

I think we should still keep the basic functions, but factory wrappers would simplify the code in 90% of cases...

@redmunds
Copy link
Contributor Author

@dangoor Changes pushed.

@dangoor
Copy link
Contributor

dangoor commented Mar 22, 2014

The validation is okay as it is, and we can optimize more as we see how this usage grows.

The functional approach for shortening this would be: validator: _.partialRight(ValidationUtils.isIntegerInRange, MIN_TAB_SIZE, MAX_TAB_SIZE)

@redmunds One last question: should the Editor.prototype.set* functions return .valid or .stored? The comment currently reads like it should return .stored

@redmunds
Copy link
Contributor Author

@dangoor

The functional approach for shortening this would be: validator: _.partialRight(ValidationUtils.isIntegerInRange, MIN_TAB_SIZE, MAX_TAB_SIZE)

Nice. Done.

One last question: should the Editor.prototype.set* functions return .valid or .stored? The comment currently reads like it should return .stored

I'm having seconds thoughts about this API. What if we keep it Editor.get* and Editor.set* but add an optional fullPath parameter, so code that does not have an editor instance (such as JSLint) can use the API instead of calling PreferencesManager directly?

@redmunds
Copy link
Contributor Author

@dangoor I made the API changes that we discussed. Ready for another review.

var current = EditorManager.getActiveEditor(),
fullPath = current && current.document.file.fullPath;

Editor.setUseTabChar(!Editor.getUseTabChar(fullPath), fullPath);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine and explicit, but to some extent it is redundant because PreferencesManager.get("useTabChar") not qualified by a file will default to the currently edited file.

@dangoor
Copy link
Contributor

dangoor commented Mar 31, 2014

Looks good. Merging.

dangoor added a commit that referenced this pull request Mar 31, 2014
@dangoor dangoor merged commit bb96ad2 into master Mar 31, 2014
@dangoor dangoor deleted the randy/issue-7020 branch March 31, 2014 15:43
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants