From 252c19c9203ea211413aa417ad5ea371eef9000b Mon Sep 17 00:00:00 2001 From: "jwren@google.com" Date: Tue, 20 Jan 2015 21:48:00 +0000 Subject: [PATCH] Addition of the new flag "file-read-mode" into the analysis server to fix the offset bug with IntelliJs integration. R=brianwilkerson@google.com, paulberry@google.com Review URL: https://codereview.chromium.org//857283003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@43028 260f80e4-7a28-3924-810f-c04153c831b5 --- pkg/analysis_server/doc/api.html | 16 +++++++ .../lib/src/analysis_server.dart | 1 + .../lib/src/server/driver.dart | 19 +++++++- .../lib/src/socket_server.dart | 13 +++++- pkg/analysis_server/tool/spec/spec_input.html | 16 +++++++ .../lib/file_system/physical_file_system.dart | 12 +++++- pkg/analyzer/lib/src/generated/source_io.dart | 8 +++- .../test/generated/all_the_rest_test.dart | 43 +++++++++++++++++++ 8 files changed, 122 insertions(+), 6 deletions(-) diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html index c3214a9c54a..0e594504353 100644 --- a/pkg/analysis_server/doc/api.html +++ b/pkg/analysis_server/doc/api.html @@ -173,6 +173,22 @@ dt.typeDefinition { errors produced for all files in the actual analysis roots. +
+
--file-read-mode
+
+ An enumeration of the ways files can be read from disk. Some clients + normalize end of line characters which would make the file offset and + range information incorrect. The default option is as-is, but + can also be set to normalize-eol-always. The default option + (as-is) reads files as they are on disk. The + normalize-eol-always option does the following: +
    +
  • '\r\n' is converted to '\n';
  • +
  • '\r' by itself is converted to '\n';
  • +
  • this happens regardless of the OS editor is running on.
  • +
+
+

Domain: server

diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart index bf5453ea150..644435888d3 100644 --- a/pkg/analysis_server/lib/src/analysis_server.dart +++ b/pkg/analysis_server/lib/src/analysis_server.dart @@ -951,6 +951,7 @@ class AnalysisServerOptions { bool enableIncrementalResolutionApi = false; bool enableIncrementalResolutionValidation = false; bool noErrorNotification = false; + String fileReadMode = 'as-is'; } /** diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart index dc275175add..62d875847a7 100644 --- a/pkg/analysis_server/lib/src/server/driver.dart +++ b/pkg/analysis_server/lib/src/server/driver.dart @@ -122,10 +122,15 @@ class Driver implements ServerStarter { static const String SDK_OPTION = "sdk"; /** - * The name of the option used to disable error notifications. + * The name of the flag used to disable error notifications. */ static const String NO_ERROR_NOTIFICATION = "no-error-notification"; + /** + * The name of the option used to set the file read mode. + */ + static const String FILE_READ_MODE = "file-read-mode"; + /** * The instrumentation server that is to be used by the analysis server. */ @@ -201,6 +206,7 @@ class Driver implements ServerStarter { analysisServerOptions.enableIncrementalResolutionValidation = results[INCREMENTAL_RESOLUTION_VALIDATION]; analysisServerOptions.noErrorNotification = results[NO_ERROR_NOTIFICATION]; + analysisServerOptions.fileReadMode = results[FILE_READ_MODE]; _initIncrementalLogger(results[INCREMENTAL_RESOLUTION_LOG]); @@ -328,6 +334,17 @@ class Driver implements ServerStarter { help: "disable sending all analysis error notifications to the server", defaultsTo: false, negatable: false); + parser.addOption( + FILE_READ_MODE, + help: "an option of the ways files can be read from disk, " + + "some clients normalize end of line characters which would make " + + "the file offset and range information incorrect.", + allowed: ["as-is", "normalize-eol-always"], + allowedHelp: { + "as-is": "file contents are read as-is, no file changes occur", + "normalize-eol-always": + "file contents normalize the end of line characters to the single character new line `\n`" + }, defaultsTo: "as-is"); return parser; } diff --git a/pkg/analysis_server/lib/src/socket_server.dart b/pkg/analysis_server/lib/src/socket_server.dart index f2038ecf283..cdc873aad80 100644 --- a/pkg/analysis_server/lib/src/socket_server.dart +++ b/pkg/analysis_server/lib/src/socket_server.dart @@ -62,8 +62,17 @@ class SocketServer { }); return; } - PhysicalResourceProvider resourceProvider = - PhysicalResourceProvider.INSTANCE; + PhysicalResourceProvider resourceProvider; + if (analysisServerOptions.fileReadMode == 'as-is') { + resourceProvider = PhysicalResourceProvider.INSTANCE; + } else if (analysisServerOptions.fileReadMode == 'normalize-eol-always') { + resourceProvider = + new PhysicalResourceProvider(PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS); + } else { + throw new Exception( + 'File read mode was set to the unknown mode: $analysisServerOptions.fileReadMode'); + } + analysisServer = new AnalysisServer( serverChannel, resourceProvider, diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html index aa86cb65d86..ec4e6085703 100644 --- a/pkg/analysis_server/tool/spec/spec_input.html +++ b/pkg/analysis_server/tool/spec/spec_input.html @@ -135,6 +135,22 @@ errors produced for all files in the actual analysis roots. +

+
--file-read-mode
+
+ An enumeration of the ways files can be read from disk. Some clients + normalize end of line characters which would make the file offset and + range information incorrect. The default option is as-is, but + can also be set to normalize-eol-always. The default option + (as-is) reads files as they are on disk. The + normalize-eol-always option does the following: +
    +
  • '\r\n' is converted to '\n';
  • +
  • '\r' by itself is converted to '\n';
  • +
  • this happens regardless of the OS editor is running on.
  • +
+
+

diff --git a/pkg/analyzer/lib/file_system/physical_file_system.dart b/pkg/analyzer/lib/file_system/physical_file_system.dart index 431e92ca25a..3f3e452a4af 100644 --- a/pkg/analyzer/lib/file_system/physical_file_system.dart +++ b/pkg/analyzer/lib/file_system/physical_file_system.dart @@ -19,8 +19,12 @@ import 'file_system.dart'; * A `dart:io` based implementation of [ResourceProvider]. */ class PhysicalResourceProvider implements ResourceProvider { + + static final NORMALIZE_EOL_ALWAYS = + (String string) => string.replaceAll(new RegExp('\r\n?'), '\n'); + static final PhysicalResourceProvider INSTANCE = - new PhysicalResourceProvider._(); + new PhysicalResourceProvider(null); /** * The name of the directory containing plugin specific subfolders used to @@ -28,7 +32,11 @@ class PhysicalResourceProvider implements ResourceProvider { */ static final String SERVER_DIR = ".dartServer"; - PhysicalResourceProvider._(); + PhysicalResourceProvider(String fileReadMode(String s)) { + if (fileReadMode != null) { + FileBasedSource.fileReadMode = fileReadMode; + } + } @override Context get pathContext => io.Platform.isWindows ? windows : posix; diff --git a/pkg/analyzer/lib/src/generated/source_io.dart b/pkg/analyzer/lib/src/generated/source_io.dart index 58aee2498df..ba147e3a611 100644 --- a/pkg/analyzer/lib/src/generated/source_io.dart +++ b/pkg/analyzer/lib/src/generated/source_io.dart @@ -87,6 +87,12 @@ class DirectoryBasedSourceContainer implements SourceContainer { * Instances of the class `FileBasedSource` implement a source that represents a file. */ class FileBasedSource implements Source { + + /** + * A function that changes the way that files are read off of disk. + */ + static Function fileReadMode = (String s) => s; + /** * The URI from which this source was originally derived. */ @@ -146,7 +152,7 @@ class FileBasedSource implements Source { TimestampedData get contentsFromFile { return new TimestampedData( file.lastModified(), - file.readAsStringSync()); + fileReadMode(file.readAsStringSync())); } @override diff --git a/pkg/analyzer/test/generated/all_the_rest_test.dart b/pkg/analyzer/test/generated/all_the_rest_test.dart index 5852a91ffe6..3c1f95b3b68 100644 --- a/pkg/analyzer/test/generated/all_the_rest_test.dart +++ b/pkg/analyzer/test/generated/all_the_rest_test.dart @@ -9,6 +9,7 @@ library engine.all_the_rest_test; import 'dart:collection'; +import 'package:analyzer/file_system/physical_file_system.dart'; import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; import 'package:analyzer/src/generated/constant.dart'; import 'package:analyzer/src/generated/element.dart'; @@ -7384,6 +7385,48 @@ class FileBasedSourceTest { expect(source.shortName, "exist.dart"); } + void test_fileReadMode() { + expect(FileBasedSource.fileReadMode('a'), 'a'); + expect(FileBasedSource.fileReadMode('a\n'), 'a\n'); + expect(FileBasedSource.fileReadMode('ab'), 'ab'); + expect(FileBasedSource.fileReadMode('abc'), 'abc'); + expect(FileBasedSource.fileReadMode('a\nb'), 'a\nb'); + expect(FileBasedSource.fileReadMode('a\rb'), 'a\rb'); + expect(FileBasedSource.fileReadMode('a\r\nb'), 'a\r\nb'); + } + + void test_fileReadMode_changed() { + FileBasedSource.fileReadMode = (String s) => s + 'xyz'; + expect(FileBasedSource.fileReadMode('a'), 'axyz'); + expect(FileBasedSource.fileReadMode('a\n'), 'a\nxyz'); + expect(FileBasedSource.fileReadMode('ab'), 'abxyz'); + expect(FileBasedSource.fileReadMode('abc'), 'abcxyz'); + FileBasedSource.fileReadMode = (String s) => s; + } + + void test_fileReadMode_normalize_eol_always() { + FileBasedSource.fileReadMode = + PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS; + expect(FileBasedSource.fileReadMode('a'), 'a'); + + // '\n' -> '\n' as first, last and only character + expect(FileBasedSource.fileReadMode('\n'), '\n'); + expect(FileBasedSource.fileReadMode('a\n'), 'a\n'); + expect(FileBasedSource.fileReadMode('\na'), '\na'); + + // '\r\n' -> '\n' as first, last and only character + expect(FileBasedSource.fileReadMode('\r\n'), '\n'); + expect(FileBasedSource.fileReadMode('a\r\n'), 'a\n'); + expect(FileBasedSource.fileReadMode('\r\na'), '\na'); + + // '\r' -> '\n' as first, last and only character + expect(FileBasedSource.fileReadMode('\r'), '\n'); + expect(FileBasedSource.fileReadMode('a\r'), 'a\n'); + expect(FileBasedSource.fileReadMode('\ra'), '\na'); + + FileBasedSource.fileReadMode = (String s) => s; + } + void test_hashCode() { JavaFile file1 = FileUtilities2.createFile("/does/not/exist.dart"); JavaFile file2 = FileUtilities2.createFile("/does/not/exist.dart");