From 10ef50b5937895ee95cd22ed24eba0334722576d Mon Sep 17 00:00:00 2001
From: Akos Kitta <a.kitta@arduino.cc>
Date: Tue, 9 May 2023 12:06:56 +0200
Subject: [PATCH] fix: do not enqueue write operations on conflict

Closes #2051

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
---
 .../browser/theia/filesystem/file-resource.ts | 35 +++++++++++++++++--
 1 file changed, 33 insertions(+), 2 deletions(-)

diff --git a/arduino-ide-extension/src/browser/theia/filesystem/file-resource.ts b/arduino-ide-extension/src/browser/theia/filesystem/file-resource.ts
index c4ef43539..9812aa1da 100644
--- a/arduino-ide-extension/src/browser/theia/filesystem/file-resource.ts
+++ b/arduino-ide-extension/src/browser/theia/filesystem/file-resource.ts
@@ -1,3 +1,4 @@
+import { ResourceSaveOptions } from '@theia/core/lib/common/resource';
 import URI from '@theia/core/lib/common/uri';
 import { injectable } from '@theia/core/shared/inversify';
 import {
@@ -7,6 +8,7 @@ import {
 } from '@theia/filesystem/lib/browser/file-resource';
 import { FileService } from '@theia/filesystem/lib/browser/file-service';
 import {
+  ETAG_DISABLED,
   FileOperationError,
   FileOperationResult,
 } from '@theia/filesystem/lib/common/files';
@@ -51,8 +53,16 @@ class WriteQueuedFileResource extends FileResource {
   ) {
     super(uri, fileService, options);
     const originalDoWrite = this['doWrite'];
-    this['doWrite'] = (content, options) =>
-      this.writeQueue.add(() => originalDoWrite.bind(this)(content, options));
+    this['doWrite'] = (content, options) => {
+      if (isETagDisabledResourceSaveOptions(options)) {
+        // When force overriding without auto-save do not enqueue the modification, it's already enqueued and the conflict is just being resolved.
+        // https://github.com/arduino/arduino-ide/issues/2051
+        return originalDoWrite.bind(this)(content, options);
+      }
+      return this.writeQueue.add(() =>
+        originalDoWrite.bind(this)(content, options)
+      );
+    };
     const originalSaveStream = this['saveStream'];
     if (originalSaveStream) {
       this['saveStream'] = (content, options) =>
@@ -83,3 +93,24 @@ class WriteQueuedFileResource extends FileResource {
     return super.isInSync();
   }
 }
+
+// Theia incorrectly sets the disabled ETag on the `stat` instead of the `version` so `FileResourceVersion#is` is unusable.
+// https://github.com/eclipse-theia/theia/blob/f9063625b861b8433341fcd1a29a0d0298778f4c/packages/filesystem/src/browser/file-resource.ts#L210
+// https://github.com/eclipse-theia/theia/blob/f9063625b861b8433341fcd1a29a0d0298778f4c/packages/filesystem/src/browser/file-resource.ts#L34
+// https://github.com/eclipse-theia/theia/discussions/12502
+function isETagDisabledResourceSaveOptions(
+  options?: ResourceSaveOptions
+): boolean {
+  if (typeof options === 'object') {
+    if ('version' in options && typeof options['version'] === 'object') {
+      const version = <Record<string, unknown>>options['version'];
+      if (version && 'stat' in version && typeof version['stat'] === 'object') {
+        const stat = <Record<string, unknown>>version['stat'];
+        if (stat) {
+          return 'etag' in stat && stat['etag'] === ETAG_DISABLED;
+        }
+      }
+    }
+  }
+  return false;
+}