Repository: mfuentesg/SyncSettings

Issue:
No JSON object could be decoded
Hey! Faced a problem.
Maybe it correlates with the new version of sublime text 4070 alpha

![image](https://user-images.githubusercontent.com/5683877/78499493-ad35cb80-7759-11ea-8b4e-a6d301f32009.png)

```
WARNING:Sync Settings.sync_settings.libs.logger:{'message': 'Not Found', 'documentation_url': 'https://developer.github.com/v3/gists/#delete-a-gist'}
ERROR:Sync Settings.sync_settings.libs.logger:No JSON object could be decoded
Traceback (most recent call last):
  File "./python3.3/json/decoder.py", line 367, in raw_decode
StopIteration

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\krupi\AppData\Roaming\Sublime Text 3\Installed Packages\Sync Settings.sublime-package\sync_settings/commands/download.py", line 53, in download
    package_settings = json.loads('{}' if file_content == '' else file_content)
  File "./python3.3/json/__init__.py", line 316, in loads
  File "./python3.3/json/decoder.py", line 351, in decode
  File "./python3.3/json/decoder.py", line 369, in raw_decode
ValueError: No JSON object could be decoded
ERROR:Sync Settings.sync_settings.libs.logger:No JSON object could be decoded
Traceback (most recent call last):
  File "./python3.3/json/decoder.py", line 367, in raw_decode
StopIteration

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\krupi\AppData\Roaming\Sublime Text 3\Installed Packages\Sync Settings.sublime-package\sync_settings/commands/download.py", line 53, in download
    package_settings = json.loads('{}' if file_content == '' else file_content)
  File "./python3.3/json/__init__.py", line 316, in loads
  File "./python3.3/json/decoder.py", line 351, in decode
  File "./python3.3/json/decoder.py", line 369, in raw_decode
ValueError: No JSON object could be decoded
```

My gist 
https://gist.github.com/krupitskas/b272fea836faffa356c8f0cce9a121b3



Comments:

Patch:
diff --git a/.gitignore b/.gitignore
index 8babfb9..2c1bd69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,5 @@ tests/options.json
 .idea
 cover
 .coverage
+
+pyproject.toml
diff --git a/sync_settings/commands/download.py b/sync_settings/commands/download.py
index d0f2985..5a00a8e 100644
--- a/sync_settings/commands/download.py
+++ b/sync_settings/commands/download.py
@@ -1,6 +1,5 @@
 # -*- coding: utf-8 -*-
 
-import json
 import os
 import sublime
 import sublime_plugin
@@ -50,7 +49,7 @@ class SyncSettingsDownloadCommand(sublime_plugin.WindowCommand):
             file_content = manager.get_content(
                 path.join(self.temp_folder, path.encode('Package Control.sublime-settings'))
             )
-            package_settings = json.loads('{}' if file_content == '' else file_content)
+            package_settings = sublime.decode_value('{}' if file_content == '' else file_content)
             # read installed_packages from remote reference and merge it with the local version
             local_settings = sublime.load_settings('Package Control.sublime-settings')
             setting = 'installed_packages'
diff --git a/sync_settings/libs/gist.py b/sync_settings/libs/gist.py
index a6cdc70..ec6a5d2 100644
--- a/sync_settings/libs/gist.py
+++ b/sync_settings/libs/gist.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 
-import json
+import sublime
 import re
 import requests
 from functools import wraps
@@ -63,14 +63,14 @@ class Gist:
     def create(self, data):
         if not isinstance(data, dict) or not len(data):
             raise ValueError('Gist can`t be created without data')
-        return self.__do_request('post', self.make_uri(), data=json.dumps(data)).json()
+        return self.__do_request('post', self.make_uri(), data=sublime.encode_value(data, True)).json()
 
     @auth
     @with_gid
     def update(self, gid, data):
         if not isinstance(data, dict) or not len(data):
             raise ValueError('Gist can`t be updated without data')
-        return self.__do_request('patch', self.make_uri(gid), data=json.dumps(data)).json()
+        return self.__do_request('patch', self.make_uri(gid), data=sublime.encode_value(data, True)).json()
 
     @auth
     @with_gid
diff --git a/sync_settings/sync_version.py b/sync_settings/sync_version.py
index 2f28eda..7e37570 100644
--- a/sync_settings/sync_version.py
+++ b/sync_settings/sync_version.py
@@ -1,7 +1,6 @@
 # -*- coding: utf-8 -*
 
 import sublime
-import json
 import os
 from .libs.gist import Gist
 from .libs import settings, path
@@ -14,7 +13,7 @@ def get_local_version():
         return {}
     try:
         with open(file_path) as f:
-            return json.load(f)
+            return sublime.decode_value(f.read())
     except:  # noqa: E722
         pass
     return {}
@@ -37,7 +36,7 @@ def get_remote_version():
 
 def update_config_file(info):
     with open(file_path, 'w') as f:
-        json.dump(info, f)
+        f.write(sublime.encode_value(info, True))
 
 
 def show_update_dialog(on_yes=None):


Test patch:
diff --git a/tests/mocks/sublime_mock.py b/tests/mocks/sublime_mock.py
index c7d5c25..d22a014 100644
--- a/tests/mocks/sublime_mock.py
+++ b/tests/mocks/sublime_mock.py
@@ -1,3 +1,6 @@
+import json
+import re
+
 DIALOG_YES = 1
 
 
@@ -29,3 +32,12 @@ def load_settings(*args):
         'included_files': [],
         'excluded_files': []
     })
+
+
+def encode_value(data, pretty):
+    return json.dumps(data)
+
+
+def decode_value(content):
+    decoded = re.sub(re.compile(r"/\*.*?\*/", re.DOTALL), "", content)
+    return json.loads(re.sub(re.compile(r"//.*?\n"), "", decoded))
diff --git a/tests/test_sync_version.py b/tests/test_sync_version.py
index 7a4930d..e518ce8 100644
--- a/tests/test_sync_version.py
+++ b/tests/test_sync_version.py
@@ -42,6 +42,17 @@ class TestSyncVersion(unittest.TestCase):
         v = version.get_local_version()
         self.assertDictEqual({'hash': '123123123', 'created_at': '2019-01-11T02:15:15Z'}, v)
 
+    @mock.patch('sync_settings.libs.path.exists', mock.MagicMock(return_value=True))
+    @mock.patch(
+        'sync_settings.sync_version.open',
+        mock.mock_open(
+            read_data='{"created_at": "2019-01-11T02:15:15Z", /* some comment */"hash": "123123123"}'
+        ),
+    )
+    def test_get_local_version_with_commented_content(self):
+        v = version.get_local_version()
+        self.assertDictEqual({"hash": "123123123", "created_at": "2019-01-11T02:15:15Z"}, v)
+
     @mock.patch('sublime.yes_no_cancel_dialog', mock.MagicMock(return_value=1))
     def test_show_update_dialog(self):
         def on_done():