Can Functions be Edited in their own Window ?

When we upgraded from Servoy 3 to 6.1, all my Global “Methods” are now “Functions” in one huge run-on text file

This makes editing problematic, e.g. Find / Replace sees all the functions, not just one

Is there any way to edit a Function in it’s own window ?

and, is there any advantage to this one huge file idea, e.g. can variable declarations span all functions?

I do see that a formatting error in ONE Global Function can wipe them ALL out ! Yikes !

greg

Hi Greg,

There is a way to make the editor ‘focus’ on a single function.
It’s all about this button in the button-bar. You should see it enabled when you click in the Outline view.
[attachment=1]Toggle-Button-2.png[/attachment]

If you don’t see this button then you need to do the following to make it show:

  • Make sure you have a Script editor open and selected.
  • Go to the menubar > Customize Perspective.
    You will see the following window, expand the Editor Presentation node.

[attachment=0]customize-perspective.png[/attachment]

Here you enable the ‘Show Selected Element Only’ option.
Close the window and you should have the button in the button-bar.
To be able to toggle this button you need to click in the Outline view, after you have it enabled you can ‘focus’ on each function by selecting the function in the outline view.

Only downside of this is that you don’t see the JSDoc headers anymore so if you need to edit those you need to unselect the function in the outline view or toggle the button.

Hope this helps.

Very strange. The separate “Methods” in Servoy 3 made more sense

Now, when I do a file “search”, and select an item found, I end up in the middle of this “globals” file, with no idea which function I am in

and an even bigger “downside” is that a format error in one global function wipes them all out. Try it. This seems a bug

greg

Only downside of this is that you don’t see the JSDoc headers anymore so if you need to edit those you need to unselect the function in the outline view or toggle the button.

gdurniak:
Very strange. The separate “Methods” in Servoy 3 made more sense

Now, when I do a file “search”, and select an item found, I end up in the middle of this “globals” file, with no idea which function I am in

and an even bigger “downside” is that a format error in one global function wipes them all out. Try it. This seems a bug

Best practice is to organize your code in scopes now and use globals as little as possible. A whole bunch of unrelated code in one file = bad. Related code in one scope file = good. Example scope called “FRACTIONS” from https://www.servoyforge.net/projects/fractions:

/**
 * Wrapped an open source fractional library for Servoy use.
 * Note: this is an early version of ratio.js. (https://github.com/LarryBattle/Ratio.js)
 * @author Data Mosaic <http://www.data-mosaic.com/>
 * 
 * @author Larry Battle <http://bateru.com/news/contact-me>
 * @license MIT
 * @version 1.0
 * @date May 08, 2012
 * @purpose To provide a function that converts a decimal to a simplified fraction.
 * @info <http://bateru.com/news/2012/05/code-of-the-day-javascript-convert-decimal-to-a-simplified-fraction/>
 */

/**
 * Highest denominator to attempt to match on
 * @type {Number}
 * @private 
 *
 * @properties={typeid:35,uuid:"D2D263FE-7078-410E-A130-5DC6427D39D2",variableType:4}
 */
var _fractionPrecision = 64;

/**
 * Highest number of decimals
 * @type {Number}
 * @private 
 *
 * @properties={typeid:35,uuid:"B3D8AC4E-18C4-42EF-A643-26B531D7F733",variableType:4}
 */
var _decimalPrecision = 6;

/**
 * Only calculate the fraction array once
 * @type {Object}
 * @private
 *
 * @properties={typeid:35,uuid:"DFCD9BB3-AFAF-4299-80F1-30A89B6EF19C",variableType:-4}
 */
var _fractionObject = getFractionObject();

/**
 * Get object with all possible fractions
 * @private  
 *
 * @properties={typeid:24,uuid:"74B39990-D0A6-44A6-A209-5E2823AF2380"}
 */
function getFractionObject() {
	function getKeys(obj) {
	 	var props = [];
		for (var prop in obj) {
			if (obj.hasOwnProperty(prop)) {
				props.push(prop);
			}
		}
		return props;
	};
		
	var obj = {
		0 : '0',
		1 : '1'
	},
	num = _fractionPrecision,
	den = _fractionPrecision + 1,
	value;
	while (--num) {
		while (--den > 1) {
			value = (num / den).toFixed(_decimalPrecision);
			if (value < 1) {
				obj[value] = num + "/" + den;
			}
		}
		den = _fractionPrecision + 1;
	}
	obj.keys = getKeys(obj);
	return obj;
};

/**
 * Get the closest number
 * 
 * @param {Number[]} arr
 * @param {Number} val
 * @return {Number|Boolean}
 * @private 
 *
 * @properties={typeid:24,uuid:"F76DF876-15E8-4AD3-949B-22E3C16602E4"}
 */
function getClosestNum(arr, val) {
	if (typeof arr !== "object" || !(arr.hasOwnProperty('length')) || isNaN(val)) {
		return false;
	}
	var i = arr.length,
	j = i - 1,
	minDiff = Math.abs(+val - arr[j]),
	diff;
	while (i--) {
		diff = Math.abs(+val - arr[i]);
		if (diff < minDiff) {
			minDiff = diff;
			j = i;
		}
	}
	return arr[j];
};

/**
 * Get a fraction representation from a decimal
 * 
 * @param {Number} dec
 * @return {String|Boolean}
 *
 * @properties={typeid:24,uuid:"9654BA30-4AA4-4C93-BCEF-108F4730E4E0"}
 */
function getFractionFromDecimal(dec) {
	if (isNaN(dec) || !isFinite(dec)) {
		return false;
	}
	if (!/\./.test(dec)) {
		return dec;
	}
	var fracs = _fractionObject,
	matches = dec.toString().match(/(\d+)(\.\d+)/),
	fraction = fracs[getClosestNum(fracs.keys, Math.abs(+matches[2]))],
	sign = ( 0 < dec || (fraction == "0" && Math.abs(dec) < 1) ) ? '' : '-';
	if (1 < Math.abs(dec)) {
		if (isNaN(fraction)) {
			fraction = +matches[1] + " " + fraction;
		} else {
			fraction = +matches[1] + (+fraction);
		}
	}
	return sign + fraction;
};

/**
 * Get a decimal representation from a fraction
 * 
 * @param {String} fraction
 * @return {Number|Boolean}
 *
 * @properties={typeid:24,uuid:"41703256-520A-42B4-84EC-D8813CE6DA77"}
 */
function getDecimalFromFraction(fraction) {
	var parts = [],
	j,
	arr = [];
	
	/**
	 * @param {Array} parts
	 * @param {Array} arr
	 * @return {Number}
	 */
	function frac(parts,arr) {
		function getNumeratorWithSign(top, bottom) {
			var sign = (+top * (+bottom || 1)) < 0 ? -1 : 1;
			return Math.abs(+top) * sign;
		}
		
		parts = parts.split(/\//);
		arr[0] = getNumeratorWithSign(parts[0], parts[1]);
		arr[1] = Math.abs(+parts[1]);
		
		return arr
	}
	
	//nothing
	if (!fraction) {
		return 0
	}
	//whole number
	else if (fraction == parseInt(fraction)) {
		return parseInt(fraction)
	}
	else if (/\d\s*\//.test(fraction)) {
		//mixed number
		if (/\d\s+[+\-]?\d/.test(fraction)) {
			parts = fraction.match(/(\S+)\s+(\S.*)/);
			arr = frac(parts[2],[]);
			j = 0 < (parseFloat(parts[1]) * arr[0]) ? 1 : -1;
			arr[0] = j * (Math.abs(arr[0]) + Math.abs(parts[1] * arr[1]));
		}
		//fraction
		else {
			arr = frac(fraction,arr)
		}
	
		return arr[0] / arr[1]
	}
}
	
/**
 * Called for performing a conversion between a displayed value and a database value.
 *
 * @param {String|Number} displayedValue The displayed value.
 *
 * @return {Number} the database value.
 *
 * @properties={typeid:24,uuid:"861CC360-A31D-48FC-991E-416A5A7C3941"}
 */
function convertFraction2DB(displayedValue) {
	if (typeof displayedValue == 'string') {
		return getDecimalFromFraction(displayedValue)
	}	
}

/**
 * Called for performing a conversion between a database value and a displayed value.
 *
 * @param {Number} databaseValue The database value.
 *
 * @return {String} the displayed value.
 *
 * @properties={typeid:24,uuid:"6F37C385-32FF-4B62-A54B-B4E82BF9A5FB"}
 */
function convertDB2Fraction(databaseValue) {
	//don't show .0 for whole numbers
	if (typeof databaseValue == 'number' && parseInt(databaseValue) == databaseValue) {
		return utils.numberFormat(databaseValue,'#')
	}
	//format the fraction
	else if (typeof databaseValue == 'number') {
		return getFractionFromDecimal(databaseValue)
	}
	//shouldn't need this return; needed to break out of loop when called with display instead of database value as input
	else {
		return databaseValue
	}
}

Way better than Servoy 3 once you reorganize things.

Glad I asked. This would help greatly

if I copy / paste each Function and ID into it’s own “Scope”, will everything still work ? No broken links ?

greg

PS
It would have been nice if the 3 to 6 conversion process offered this as a choice, rather than lumping everything together

Best practice is to organize your code in scopes now and use globals as little as possible. A whole bunch of unrelated code in one file = bad.

gdurniak:
if I copy / paste each Function and ID into it’s own “Scope”, will everything still work ? No broken links ?

Not quite as simple as that. The copy/paste works for moving code around but referencing your functions is different:

globals.someMethod() now becomes scopes.someScope.someMethod()

Now is your chance to test out the awesome refactoring capabilities of Serclipse that you could only dream about in Servoy 3 :)

gdurniak:
Very strange. The separate “Methods” in Servoy 3 made more sense

Now, when I do a file “search”, and select an item found, I end up in the middle of this “globals” file, with no idea which function I am in

and an even bigger “downside” is that a format error in one global function wipes them all out. Try it. This seems a bug

Hi Greg,

just a heads up: I felt exactly the same way moving from 3.5 to 4.0 a few years ago.
But I will tell you the same as David already did: this is way better than 3.5 and YES you can screw up a whole js file in such a way that Servoy won’t display it.
Remember all js files are ‘safely’ stored in your workspace and you can open these with a simple text editor in case things really go wrong.
But in any way Servoy is far more forgiving writing faulty js code than any browser will be (been there, done that)

About the fact not ‘knowing’ where you are in a (globals) js file: open the ‘outline’ tab which will be in sync with your active editor window.
Allow yourself some time to get used to all the great stuff that Eclipse is offering you to work with Servoy and of course all the nice improvements R&D made over the last view years.

+1

Can Refactoring help to move a function from my “globals” scope, to a new scope ?

I can certainly Rename a function, but “Move” does not seem to allow me to select a new Scope

and this previous post suggests a cut and paste, to generate a new UUID

viewtopic.php?f=38&t=19084

and if I cut and paste a Function WITH it’s UUID into a new scope, it seems to work, but I still must do a Find / Replace to fix all the javascript references

greg

Hi Greg,

If you really want to MOVE the functions with the UUID’s intact you need to use the following procedure:

  1. CUT the functions with JSDoc’s you want to move
  2. SAVE the file you cut the code out (else Servoy will still think these UUID’s are given out)
  3. PASTE the copied code into the new location
  4. and of course save your new code again

So step 2 is a VERY important step, if you don’t do that you will end up with new UUID’s for your pasted code the moment you save them.

So now this takes care of any methods that were referenced by their UUID (events, buttons, etc.) but in your code you could have called these as well.
As David already pointed out if you place these methods in their own scope then you need to address them as scopes.. .
So a a quick search (CTRL-H, file search) will do the trick to find any references.

Hope this helps.

ok, thanks, I have tried that, and it does work

since mine is a “converted” solution, I have a ton of “global” functions ( methods ) that must be moved to separate scopes

but it’s still a 5 step process, so Refactoring is not quite so “awesome” after all :-)

greg

gdurniak:
since mine is a “converted” solution, I have a ton of “global” functions ( methods ) that must be moved to separate scopes

but it’s still a 5 step process, so Refactoring is not quite so “awesome” after all :-)

I don’t think you’ll get any sympathy for this. Reorganizing code is what it is. Being able to reconnect everything once you reorganize is where refactoring comes in. That job without refactoring can be quite an epic chore.

In the Solution Explorer, it seems that “Scopes” should be called “Global Scopes”, since my original Servoy 3 “Globals” are now just one of many “Global Scopes”

an interesting arrangement

greg

no we have

“Scopes” → “global” which is your original global scope (globals.js) file that is your servoy 3 globals thing.

besides that under “Scopes” you can now have any named scopes (except ofcourse “globals” which is the default one)