Add observer to watch change of 'new_line_endings'?

I am able to get and set the value of Current File Preferences using require('ko/views').current().koDoc.new_line_endings but now I want to add a prefObserverService.addObserver which requires a string value of the property, so new_line_endings I thought, but it doesn’t work. I use the shell to see if I could see it and found 'endOfLine' but that seems to be for the global preference, and koDoc.prefs.getPrefType('new_line_endings') cam back blank. What am I missing (apart from a brain cell or two :slight_smile: )?

An example of what I want to do, I believe that 'pleaseObserveMe' is being set on the Current File Preferences.

const views = require('ko/views');

const listeners = {
  observe: function (subject, topic) {
    require('ko/dialogs').alert(topic);
  }
};
  
views.current().koDoc.prefs.setBoolean('pleaseObserveMe', false);

views.current().koDoc.prefs.prefObserverService.addObserver(
  listeners.observe,
  'pleaseObserveMe',
  false
);

views.current().koDoc.prefs.setBoolean('pleaseObserveMe', true);

views.current().koDoc.prefs.prefObserverService.removeObserver(
  listeners.observe,
  'pleaseObserveMe',
  false
);

Why do I want to do this? Well I wanted a more general event for Current File Preferences changed, but I could’t find one or a wya in the wiki https://github.com/Komodo/KomodoEdit/wiki/138-events-and-notifications So this was the idea that I can up with from browsing the source code on GitHub and looking through the Javascript Shell for an appropriate method.

The observe method is implied (consider it part of the interface, which JS doesn’t really have so you have to just try and get it right, yay JS).

So basically your code looks right, but you need to use “listeners” as the observer, eg.

views.current().koDoc.prefs.prefObserverService.addObserver(
  listeners,
  'pleaseObserveMe',
  false
);

The rest stays the same.

Ok, both syntaxes appear to work for me in the example that I gave above, weird. That aside, assuming that your syntax is correct (thank you), this still doesn’t help me watch for new_line_endings changing. i.e.

const views = require('ko/views');

const listeners = {
  observe: function (subject, topic) {
    require('ko/dialogs').alert(topic);
  }
};

views.current().koDoc.new_line_endings = 1;

views.current().koDoc.prefs.prefObserverService.addObserver(
  listeners,
  'new_line_endings',
  false
);

views.current().koDoc.new_line_endings = 0;

views.current().koDoc.prefs.prefObserverService.removeObserver(
  listeners,
  'new_line_endings',
  false
);

This doesn’t work for views.current().koDoc properties (but new_line_endings' is still a Current File Preference) which doesn’t appear to have prefObserverService.addObserver and prefObserverService.removeObserver but it does have methods observerService am I supposed to use that for new_line_endings (i.e. native Current File Preferences) and pref.prefObserverService for my own custom Current File Preferences? I seem to be in the rough vicinity but I’m clearly lost.

It appears that I could use views.current().koDoc.watch('new_line_endings', listeners.observe); but it’s not recommended. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/watch

Ah, found your problem. the pref is called lineEndings, not new_line_endings. What made you use the latter?

I was unable to find the correct string and so went searching the code base and that was what showed up in all examples that I could find relating to what I was doing.

The other related one I needed was/is existing_line_endings. How did you come about lineEndings?

I will give that a test, thanks.

Here’s a quick screen recording, note when I am hovering over the “Line Endings” dropdown I press shift + right mouse button, which brings up DOM inspector. You need to have the following addons installed to do this:

Here’s the video:

https://dl.dropboxusercontent.com/u/41125814/domi-prefs.mp4

Fantastic, I much prefer knowing how to fish rather than being given one, and all that. :wink:

Happy to teach you how to catch all kinds of fish :wink:

Ok, throw me a fish. :stuck_out_tongue: I still can’t find the setting with this string:

require('ko/dialogs').alert(require('ko/views').current().koDoc.new_line_endings);
require('ko/dialogs').alert(require('ko/prefs').hasPref("lineEndings"));
require('ko/dialogs').alert(require('ko/views').current().koDoc.prefs.hasPref("lineEndings"));
require('ko/dialogs').alert(require('ko/views').current().koDoc.hasPref("lineEndings"));

The first line gives me the value from File Preference Settings.
The second gives me false.
The third gives me false.
The last throws hasPref not a function.

Ok, so lineEndings is just the id of the element on the File Preferences dialog, but in the inspector it shows the command that gets called to set the value as setLineEnding(this.value); So, I do a search in the code base for setLineEnding


This leads me to

And the relevant code we have from there is:
var data = {};

data.changedFields = {};

function setField(name, value)
{
    data.changedFields[name] = value;
}

function setLineEnding(value) {
    if (data.originalLineEnding != "EOL_MIXED") {
        var attrValue = (value == data.originalLineEnding).toString();
        data.preserveLineEndings.setAttribute("disabled", attrValue);
    }
    setField('lineEndings', value);
}

function OnPreferencePageClosing(prefset, ok) {
   ...

            case "lineEndings":
                var le;
                if (value == "EOL_LF") {
                    le = parent.view.koDoc.EOL_LF;
                } else if (value == "EOL_CR") {
                    le = parent.view.koDoc.EOL_CR;
                } else if (value == "EOL_CRLF") {
                    le = parent.view.koDoc.EOL_CRLF;
                }
                // If we've changed things, save it as a pref.
                if (parent.view.koDoc.new_line_endings != le) {
                    var eolpref = '';
                    switch (le) {
                        case parent.view.koDoc.EOL_LF: eolpref = 'LF'; break;
                        case parent.view.koDoc.EOL_CR: eolpref = 'CR'; break;
                        case parent.view.koDoc.EOL_CRLF: eolpref = 'CRLF'; break;
                        default:
                            log.error("unknown line ending: " + le);
                    }
                    if (eolpref) {
                        parent.view.prefs.setStringPref('endOfLine', eolpref);
                        if (parent.view.minimap) {
                            parent.view.minimap.prefs.setStringPref('endOfLine', eolpref);
                        }
                    }
                }
                // Make the change even if 'le' is unchanged to allow a
                // change to "Preserve Line Endings" on EOL_MIXED files to
                // correct the file.
                parent.view.koDoc.new_line_endings = le;
                if (! data.preserveLineEndings.checked) {
                    // This changes the current document's line endings.
                    parent.view.koDoc.existing_line_endings = le;
                }

   ...

So the File Prefence is being set at one of the following:
parent.view.prefs.setStringPref('endOfLine', eolpref);
or
parent.view.minimap.prefs.setStringPref('endOfLine', eolpref);
or
parent.view.koDoc.new_line_endings = le;

Now, the first two are strings, and the preference value that I am looking to get is a number, that must mean the relevant line is
parent.view.koDoc.new_line_endings = le;
And we’ve already established that I can’t observe new_line_endings
So yep, I lost my fishing pole.

I believe I’m beginning to understand what is going on, and the problem is not to do with the observing, which is where I have been placing my attention. It is beginning to seem that the issue is more to do with the File Preferences dialog not staying in sync with with the actual preferences that have been set. Whether this is a feature or a bug, I do not know. I’m going to create a macro and and video (tomorrow) of what I am seeing and hopefully we can wrap this up.

So here is the text macro

/*global require, setTimeout */

const listener =   {
  observe: function (subject, topic, data) {
    /*jshint unused:false */
    const current = require('ko/views').current().koDoc.prefs.getString(
      'endOfLine'
    );

    require('ko/dialogs').alert(`Pref changed: ${topic} : ${current}`);
  }
};

require('ko/views').current().koDoc.prefs.prefObserverService.addObserver(
  listener,
  'endOfLine',
  false
);

require('ko/views').current().koDoc.prefs.setString(
  'endOfLine',
  'LF'
);

require('ko/views').current().koDoc.prefs.setString(
  'endOfLine',
  'CRLF'
);

setTimeout(function () {
  require('ko/views').current().koDoc.prefs.prefObserverService.removeObserver(
    listener,
    'endOfLine'
  );
}, 30000);
  1. Open a file.
  2. Check the file setting
  3. Run the macro
  4. Check the file setting
  5. Close the file
  6. Open the file
  7. Check the file setting

And here is the video that shows the problem.


Changing the file setting from the preference also files the observer, but I missed that in the video.

Seems you caught yourself a rare fish.

Most prefs work with observers, meaning that when you change it Komodo catches on and changes things around accordingly. This is not one of those prefs.

So you have to manually do that what the observer is meant to do, in this case you need to set the proper line endings on the document. I’ve revised your userscript to do this:

/*global require, setTimeout */

(function() {
  
  var koDoc = require('ko/views').current().koDoc;
  var prefs = require('ko/views').current().prefs;
  if ( ! prefs) return;
  
  var listener =   {
    observe: function (subject, topic, data) {
      var current = prefs.getString('endOfLine');
      require('ko/dialogs').alert(`Pref changed: ${topic} : ${current}`);
    }
  };
  
  var setEol = function (value, override)
  {
    prefs.setString('endOfLine', value);
    
    le = koDoc.EOL_LF;
    if (value == "CR") 
      le = koDoc.EOL_CR;
    else if (value == "CRLF") 
      le = koDoc.EOL_CRLF;
    koDoc.new_line_endings = le;
    
    if (override) koDoc.existing_line_endings = le;
  }
  
  prefs.prefObserverService.addObserver(
    listener,
    'endOfLine',
    false
  );
  
  setEol('LF');
  setEol('CRLF', true);
  
  prefs.prefObserverService.removeObserver(
    listener,
    'endOfLine'
  );
  
}).apply();

Pay special attention to the override flag on setEol, when this is true it replaces the existing line endings.

By the way your selected icons appear to be invisible, you should try deleting profile/XRE/lessCache and profile/XRE/icons while Komodo is not running to flush your caches.

Thanks. Haven’ got around to changing my code to the suggested yet, been looking at a little something else. (no, not porn! :stuck_out_tongue: )

I wondered about the icons and assumed it was something local to me, just haven’t been that concerned about them. I tried your suggestion of deleting lessCache and icons but they are still the same.

Well that seems to be working well, now that I have a better understanding of this funny fish. Hopefully I can wrap this macro up pretty soon and share it with you (though not sure how useful it is outside of the learning that I have accomplished). Thanks.