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

Commit 48a58fe

Browse files
saurabh95swmitra
authored andcommitted
Saurabh95/encoding support (#13412)
* Now encoding is passed as parameter on file read and it is used by writefile in order to preserve encoding * UI wiring * Code Cleanup * Fixed failing tests * Added warning Dialog while changing encoding * Added some Linux specific changes * Fixed some linting errors * Reverted last commit * Minor changes * Now selected encoding is stored in state * Fixed lint error * Added some more encodings * Added some more encodings * Fixed lint errors * Removed duplicate encodings * Fixed failing tests * Used externalized strings * Addressed review comments * Added supported encodings file * Addressed review comments * Addressed review comments
1 parent be28282 commit 48a58fe

File tree

12 files changed

+297
-11
lines changed

12 files changed

+297
-11
lines changed

src/document/Document.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,25 @@ define(function (require, exports, module) {
758758
return this.file instanceof InMemoryFile;
759759
};
760760

761+
/**
762+
* Reloads the document from FileSystem
763+
* @return {promise} - to check if reload was successful or not
764+
*/
765+
Document.prototype.reload = function () {
766+
var $deferred = $.Deferred();
767+
var self = this;
768+
FileUtils.readAsText(this.file)
769+
.done(function (text, readTimestamp) {
770+
self.refreshText(text, readTimestamp);
771+
$deferred.resolve();
772+
})
773+
.fail(function (error) {
774+
console.log("Error reloading contents of " + self.file.fullPath, error);
775+
$deferred.reject();
776+
});
777+
return $deferred.promise();
778+
};
779+
761780
// We dispatch events from the module level, and the instance level. Instance events are wired up
762781
// in the Document constructor.
763782
EventDispatcher.makeEventDispatcher(exports);

src/document/DocumentCommandHandlers.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,22 @@ define(function (require, exports, module) {
322322
});
323323

324324
var file = FileSystem.getFileForPath(fullPath);
325+
if (options && options.encoding) {
326+
file._encoding = options.encoding;
327+
} else {
328+
var projectRoot = ProjectManager.getProjectRoot(),
329+
context = {
330+
location : {
331+
scope: "user",
332+
layer: "project",
333+
layerID: projectRoot.fullPath
334+
}
335+
};
336+
var encoding = PreferencesManager.getViewState("encoding", context);
337+
if (encoding[fullPath]) {
338+
file._encoding = encoding[fullPath];
339+
}
340+
}
325341
MainViewManager._open(paneId, file, options)
326342
.done(function () {
327343
result.resolve(file);
@@ -902,7 +918,21 @@ define(function (require, exports, module) {
902918
doc.isSaving = true; // mark that we're saving the document
903919

904920
// First, write document's current text to new file
921+
if (doc.file._encoding && doc.file._encoding !== "UTF-8") {
922+
var projectRoot = ProjectManager.getProjectRoot(),
923+
context = {
924+
location : {
925+
scope: "user",
926+
layer: "project",
927+
layerID: projectRoot.fullPath
928+
}
929+
};
930+
var encoding = PreferencesManager.getViewState("encoding", context);
931+
encoding[path] = doc.file._encoding;
932+
PreferencesManager.setViewState("encoding", encoding, context);
933+
}
905934
newFile = FileSystem.getFileForPath(path);
935+
newFile._encoding = doc.file._encoding;
906936

907937
// Save as warns you when you're about to overwrite a file, so we
908938
// explicitly allow "blind" writes to the filesystem in this case,

src/document/DocumentManager.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ define(function (require, exports, module) {
9393
Commands = require("command/Commands"),
9494
PerfUtils = require("utils/PerfUtils"),
9595
LanguageManager = require("language/LanguageManager"),
96+
ProjectManager = require("project/ProjectManager"),
9697
Strings = require("strings");
9798

9899

@@ -419,7 +420,7 @@ define(function (require, exports, module) {
419420
if (doc) {
420421
result.resolve(doc.getText(), doc.diskTimestamp, checkLineEndings ? doc._lineEndings : null);
421422
} else {
422-
file.read(function (err, contents, stat) {
423+
file.read(function (err, contents, encoding, stat) {
423424
if (err) {
424425
result.reject(err);
425426
} else {
@@ -498,6 +499,18 @@ define(function (require, exports, module) {
498499
// via notifyFileDeleted
499500
FileSyncManager.syncOpenDocuments(Strings.FILE_DELETED_TITLE);
500501

502+
var projectRoot = ProjectManager.getProjectRoot(),
503+
context = {
504+
location : {
505+
scope: "user",
506+
layer: "project",
507+
layerID: projectRoot.fullPath
508+
}
509+
};
510+
var encoding = PreferencesManager.getViewState("encoding", context);
511+
delete encoding[fullPath];
512+
PreferencesManager.setViewState("encoding", encoding, context);
513+
501514
if (!getOpenDocumentForPath(fullPath) &&
502515
!MainViewManager.findInAllWorkingSets(fullPath).length) {
503516
// For images not open in the workingset,

src/editor/EditorStatusBar.js

Lines changed: 161 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,21 @@ define(function (require, exports, module) {
4040
PreferencesManager = require("preferences/PreferencesManager"),
4141
StatusBar = require("widgets/StatusBar"),
4242
Strings = require("strings"),
43+
FileUtils = require("file/FileUtils"),
44+
InMemoryFile = require("document/InMemoryFile"),
45+
Dialogs = require("widgets/Dialogs"),
46+
DefaultDialogs = require("widgets/DefaultDialogs"),
47+
ProjectManager = require("project/ProjectManager"),
48+
Async = require("utils/Async"),
49+
FileSystem = require("filesystem/FileSystem"),
4350
StringUtils = require("utils/StringUtils");
51+
52+
var SupportedEncodingsText = require("text!supported-encodings.json"),
53+
SupportedEncodings = JSON.parse(SupportedEncodingsText);
4454

4555
/* StatusBar indicators */
4656
var languageSelect, // this is a DropdownButton instance
57+
encodingSelect, // this is a DropdownButton instance
4758
$cursorInfo,
4859
$fileInfo,
4960
$indentType,
@@ -74,12 +85,24 @@ define(function (require, exports, module) {
7485
var doc = editor.document,
7586
lang = doc.getLanguage();
7687

77-
// Ensure width isn't left locked by a previous click of the dropdown (which may not have resulted in a "change" event at the time)
78-
languageSelect.$button.css("width", "auto");
7988
// Show the current language as button title
8089
languageSelect.$button.text(lang.getName());
8190
}
8291

92+
/**
93+
* Update encoding
94+
* @param {Editor} editor Current editor
95+
*/
96+
function _updateEncodingInfo(editor) {
97+
var doc = editor.document;
98+
99+
// Show the current encoding as button title
100+
if (!doc.file._encoding) {
101+
doc.file._encoding = "UTF-8";
102+
}
103+
encodingSelect.$button.text(doc.file._encoding);
104+
}
105+
83106
/**
84107
* Update file information
85108
* @param {Editor} editor Current editor
@@ -277,6 +300,7 @@ define(function (require, exports, module) {
277300

278301
_updateCursorInfo(null, current);
279302
_updateLanguageInfo(current);
303+
_updateEncodingInfo(current);
280304
_updateFileInfo(current);
281305
_initOverwriteMode(current);
282306
_updateIndentType(fullPath);
@@ -305,6 +329,40 @@ define(function (require, exports, module) {
305329
languageSelect.items.unshift(LANGUAGE_SET_AS_DEFAULT);
306330
}
307331

332+
/**
333+
* Change the encoding and reload the current document.
334+
* If passed then save the preferred encoding in state.
335+
*/
336+
function _changeEncodingAndReloadDoc(document) {
337+
var promise = document.reload();
338+
promise.done(function (text, readTimestamp) {
339+
encodingSelect.$button.text(document.file._encoding);
340+
// Store the preferred encoding in the state
341+
var projectRoot = ProjectManager.getProjectRoot(),
342+
context = {
343+
location : {
344+
scope: "user",
345+
layer: "project",
346+
layerID: projectRoot.fullPath
347+
}
348+
};
349+
var encoding = PreferencesManager.getViewState("encoding", context);
350+
encoding[document.file.fullPath] = document.file._encoding;
351+
PreferencesManager.setViewState("encoding", encoding, context);
352+
});
353+
promise.fail(function (error) {
354+
console.log("Error reloading contents of " + document.file.fullPath, error);
355+
});
356+
}
357+
358+
359+
/**
360+
* Populate the encodingSelect DropdownButton's menu with all registered encodings
361+
*/
362+
function _populateEncodingDropdown() {
363+
encodingSelect.items = SupportedEncodings;
364+
}
365+
308366
/**
309367
* Initialize
310368
*/
@@ -343,6 +401,27 @@ define(function (require, exports, module) {
343401
$("#status-language").append(languageSelect.$button);
344402
languageSelect.$button.attr("title", Strings.STATUSBAR_LANG_TOOLTIP);
345403

404+
405+
encodingSelect = new DropdownButton("", [], function (item, index) {
406+
var document = EditorManager.getActiveEditor().document;
407+
var html = _.escape(item);
408+
409+
// Show indicators for currently selected & default languages for the current file
410+
if (item === "UTF-8") {
411+
html += " <span class='default-language'>" + Strings.STATUSBAR_DEFAULT_LANG + "</span>";
412+
}
413+
if (item === document.file._encoding) {
414+
html = "<span class='checked-language'></span>" + html;
415+
}
416+
return html;
417+
});
418+
419+
encodingSelect.dropdownExtraClasses = "dropdown-status-bar";
420+
encodingSelect.$button.addClass("btn-status-bar");
421+
$("#status-encoding").append(encodingSelect.$button);
422+
encodingSelect.$button.attr("title", Strings.STATUSBAR_ENCODING_TOOLTIP);
423+
424+
346425
// indentation event handlers
347426
$indentType.on("click", _toggleIndentType);
348427
$indentWidthLabel
@@ -389,16 +468,96 @@ define(function (require, exports, module) {
389468
}
390469
});
391470

471+
// Encoding select change handler
472+
encodingSelect.on("select", function (e, encoding) {
473+
var document = EditorManager.getActiveEditor().document,
474+
fullPath = document.file.fullPath;
475+
476+
document.file._encoding = encoding;
477+
478+
479+
if (!(document.file instanceof InMemoryFile) && document.isDirty) {
480+
var dialogId = DefaultDialogs.DIALOG_ID_EXT_CHANGED,
481+
message = StringUtils.format(
482+
Strings.DIRTY_FILE_ENCODING_CHANGE_WARN,
483+
StringUtils.breakableUrl(
484+
ProjectManager.makeProjectRelativeIfPossible(document.file.fullPath)
485+
)
486+
),
487+
buttons = [
488+
{
489+
className: Dialogs.DIALOG_BTN_CLASS_LEFT,
490+
id: Dialogs.DIALOG_BTN_DONTSAVE,
491+
text: Strings.IGNORE_RELOAD_FROM_DISK
492+
},
493+
{
494+
className: Dialogs.DIALOG_BTN_CLASS_PRIMARY,
495+
id: Dialogs.DIALOG_BTN_CANCEL,
496+
text: Strings.CANCEL
497+
}
498+
];
499+
500+
Dialogs.showModalDialog(dialogId, Strings.SAVE_FILE_ENCODING_CHANGE_WARN, message, buttons)
501+
.done(function (id) {
502+
if (id === Dialogs.DIALOG_BTN_DONTSAVE) {
503+
_changeEncodingAndReloadDoc(document);
504+
}
505+
});
506+
} else if (document.file instanceof InMemoryFile) {
507+
encodingSelect.$button.text(encoding);
508+
} else if (!document.isDirty) {
509+
_changeEncodingAndReloadDoc(document);
510+
}
511+
});
512+
392513
$statusOverwrite.on("click", _updateEditorOverwriteMode);
393514
}
394515

395516
// Initialize: status bar focused listener
396517
EditorManager.on("activeEditorChange", _onActiveEditorChange);
397518

519+
function _checkFileExistance(filePath, index, encoding) {
520+
var deferred = new $.Deferred(),
521+
fileEntry = FileSystem.getFileForPath(filePath);
522+
523+
fileEntry.exists(function (err, exists) {
524+
if (!err && exists) {
525+
deferred.resolve();
526+
} else {
527+
delete encoding[filePath];
528+
deferred.reject();
529+
}
530+
});
531+
532+
return deferred.promise();
533+
}
534+
535+
ProjectManager.on("projectOpen", function () {
536+
var projectRoot = ProjectManager.getProjectRoot(),
537+
context = {
538+
location : {
539+
scope: "user",
540+
layer: "project",
541+
layerID: projectRoot.fullPath
542+
}
543+
};
544+
var encoding = PreferencesManager.getViewState("encoding", context);
545+
if (!encoding) {
546+
PreferencesManager.setViewState("encoding", {}, context);
547+
}
548+
Async.doSequentially(Object.keys(encoding), function (filePath, index) {
549+
return _checkFileExistance(filePath, index, encoding);
550+
}, false)
551+
.always(function () {
552+
PreferencesManager.setViewState("encoding", encoding, context);
553+
});
554+
});
555+
398556
AppInit.htmlReady(_init);
399557
AppInit.appReady(function () {
400558
// Populate language switcher with all languages after startup; update it later if this set changes
401559
_populateLanguageDropdown();
560+
_populateEncodingDropdown();
402561
LanguageManager.on("languageAdded languageModified", _populateLanguageDropdown);
403562
_onActiveEditorChange(null, EditorManager.getActiveEditor(), null);
404563
StatusBar.show();

src/file/FileUtils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ define(function (require, exports, module) {
7777
});
7878

7979
// Read file
80-
file.read(function (err, data, stat) {
80+
file.read(function (err, data, encoding, stat) {
8181
if (!err) {
8282
result.resolve(data, stat.mtime);
8383
} else {

0 commit comments

Comments
 (0)