Version 2.15.0-290.0.dev
Merge commit 'b0bca6ae6a0cff71ee763c32ce5924f84a40346e' into 'dev'
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index 2e4f847..4a20a40 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -139,11 +139,10 @@
VariableGet(function.positionalParameters.first),
StringLiteral(_getExtensionMemberName(node))
], types: [
- DynamicType()
+ function.returnType
]))
..fileOffset = node.fileOffset;
- return ReturnStatement(
- AsExpression(getPropertyInvocation, function.returnType));
+ return ReturnStatement(getPropertyInvocation);
}
/// Returns a new function body for the given [node] external setter.
@@ -153,18 +152,18 @@
ReturnStatement _getExternalSetterBody(Procedure node) {
var function = node.function;
assert(function.positionalParameters.length == 2);
+ var value = function.positionalParameters.last;
var setPropertyInvocation = StaticInvocation(
_setPropertyTarget,
Arguments([
VariableGet(function.positionalParameters.first),
StringLiteral(_getExtensionMemberName(node)),
- VariableGet(function.positionalParameters.last)
+ VariableGet(value)
], types: [
- DynamicType()
+ value.type
]))
..fileOffset = node.fileOffset;
- return ReturnStatement(AsExpression(
- _lowerSetProperty(setPropertyInvocation), function.returnType));
+ return ReturnStatement(_lowerSetProperty(setPropertyInvocation));
}
/// Returns a new function body for the given [node] external method.
@@ -183,11 +182,10 @@
.map((argument) => VariableGet(argument))
.toList())
], types: [
- DynamicType()
+ function.returnType
]))
..fileOffset = node.fileOffset;
- return ReturnStatement(AsExpression(
- _lowerCallMethod(callMethodInvocation), function.returnType));
+ return ReturnStatement(_lowerCallMethod(callMethodInvocation));
}
/// Returns the extension member name.
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index 09adf9a..b84e3fd 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -420,6 +420,7 @@
+
<h3>Requests</h3><dl><dt class="request"><a name="request_server.getVersion">server.getVersion</a></dt><dd><div class="box"><pre>request: {
"id": String
"method": "server.getVersion"
@@ -3081,6 +3082,8 @@
+
+
<dl><dt class="typeDefinition"><a name="type_AddContentOverlay">AddContentOverlay: object</a></dt><dd>
<p>
A directive to begin overlaying the contents of a file. The supplied
@@ -5566,7 +5569,7 @@
An enumeration of the services provided by the server domain.
</p>
- <dl><dt class="value">STATUS</dt></dl></dd><dt class="typeDefinition"><a name="type_SourceChange">SourceChange: object</a></dt><dd>
+ <dl><dt class="value">LOG</dt><dt class="value">STATUS</dt></dl></dd><dt class="typeDefinition"><a name="type_SourceChange">SourceChange: object</a></dt><dd>
<p>
A description of a set of edits that implement a single conceptual change.
</p>
@@ -6062,7 +6065,7 @@
TODO: TBD
</p>
<h2 class="domain"><a name="index">Index</a></h2>
-<h3>Domains</h3><h4>server (<a href="#domain_server">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_server.getVersion">getVersion</a></li><li><a href="#request_server.shutdown">shutdown</a></li><li><a href="#request_server.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_server.connected">connected</a></li><li><a href="#notification_server.error">error</a></li><li><a href="#notification_server.status">status</a></li></ul></div></div><h4>analysis (<a href="#domain_analysis">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_analysis.getErrors">getErrors</a></li><li><a href="#request_analysis.getHover">getHover</a></li><li><a href="#request_analysis.getLibraryDependencies">getLibraryDependencies</a></li><li><a href="#request_analysis.getNavigation">getNavigation</a></li><li><a href="#request_analysis.getReachableSources">getReachableSources</a></li><li><a href="#request_analysis.reanalyze">reanalyze</a></li><li><a href="#request_analysis.setAnalysisRoots">setAnalysisRoots</a></li><li><a href="#request_analysis.setGeneralSubscriptions">setGeneralSubscriptions</a></li><li><a href="#request_analysis.setPriorityFiles">setPriorityFiles</a></li><li><a href="#request_analysis.setSubscriptions">setSubscriptions</a></li><li><a href="#request_analysis.updateContent">updateContent</a></li><li><a href="#request_analysis.updateOptions">updateOptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_analysis.analyzedFiles">analyzedFiles</a></li><li><a href="#notification_analysis.closingLabels">closingLabels</a></li><li><a href="#notification_analysis.errors">errors</a></li><li><a href="#notification_analysis.flushResults">flushResults</a></li><li><a href="#notification_analysis.folding">folding</a></li><li><a href="#notification_analysis.highlights">highlights</a></li><li><a href="#notification_analysis.implemented">implemented</a></li><li><a href="#notification_analysis.invalidate">invalidate</a></li><li><a href="#notification_analysis.navigation">navigation</a></li><li><a href="#notification_analysis.occurrences">occurrences</a></li><li><a href="#notification_analysis.outline">outline</a></li><li><a href="#notification_analysis.overrides">overrides</a></li></ul></div></div><h4>completion (<a href="#domain_completion">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_completion.getSuggestions">getSuggestions</a></li><li><a href="#request_completion.setSubscriptions">setSubscriptions</a></li><li><a href="#request_completion.registerLibraryPaths">registerLibraryPaths</a></li><li><a href="#request_completion.getSuggestionDetails">getSuggestionDetails</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</a></li><li><a href="#notification_completion.availableSuggestions">availableSuggestions</a></li><li><a href="#notification_completion.existingImports">existingImports</a></li></ul></div></div><h4>search (<a href="#domain_search">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_search.findElementReferences">findElementReferences</a></li><li><a href="#request_search.findMemberDeclarations">findMemberDeclarations</a></li><li><a href="#request_search.findMemberReferences">findMemberReferences</a></li><li><a href="#request_search.findTopLevelDeclarations">findTopLevelDeclarations</a></li><li><a href="#request_search.getTypeHierarchy">getTypeHierarchy</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_search.results">results</a></li></ul></div></div><h4>edit (<a href="#domain_edit">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_edit.format">format</a></li><li><a href="#request_edit.getAssists">getAssists</a></li><li><a href="#request_edit.getAvailableRefactorings">getAvailableRefactorings</a></li><li><a href="#request_edit.getFixes">getFixes</a></li><li><a href="#request_edit.getPostfixCompletion">getPostfixCompletion</a></li><li><a href="#request_edit.getRefactoring">getRefactoring</a></li><li><a href="#request_edit.sortMembers">sortMembers</a></li><li><a href="#request_edit.organizeDirectives">organizeDirectives</a></li></ul></div><h4>execution (<a href="#domain_execution">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_execution.createContext">createContext</a></li><li><a href="#request_execution.deleteContext">deleteContext</a></li><li><a href="#request_execution.getSuggestions">getSuggestions</a></li><li><a href="#request_execution.mapUri">mapUri</a></li><li><a href="#request_execution.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_execution.launchData">launchData</a></li></ul></div></div><h4>diagnostic (<a href="#domain_diagnostic">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_diagnostic.getDiagnostics">getDiagnostics</a></li><li><a href="#request_diagnostic.getServerPort">getServerPort</a></li></ul></div><h4>flutter (<a href="#domain_flutter">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_flutter.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_flutter.outline">outline</a></li></ul></div></div><h3>Types (<a href="#types">↑</a>)</h3><div class="subindex"><ul><li><a href="#type_AddContentOverlay">AddContentOverlay</a></li><li><a href="#type_AnalysisError">AnalysisError</a></li><li><a href="#type_AnalysisErrorFixes">AnalysisErrorFixes</a></li><li><a href="#type_AnalysisErrorSeverity">AnalysisErrorSeverity</a></li><li><a href="#type_AnalysisErrorType">AnalysisErrorType</a></li><li><a href="#type_AnalysisOptions">AnalysisOptions</a></li><li><a href="#type_AnalysisService">AnalysisService</a></li><li><a href="#type_AnalysisStatus">AnalysisStatus</a></li><li><a href="#type_AvailableSuggestion">AvailableSuggestion</a></li><li><a href="#type_AvailableSuggestionRelevanceTag">AvailableSuggestionRelevanceTag</a></li><li><a href="#type_AvailableSuggestionSet">AvailableSuggestionSet</a></li><li><a href="#type_BulkFix">BulkFix</a></li><li><a href="#type_BulkFixDetail">BulkFixDetail</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionId">CompletionId</a></li><li><a href="#type_CompletionService">CompletionService</a></li><li><a href="#type_CompletionSuggestion">CompletionSuggestion</a></li><li><a href="#type_CompletionSuggestionKind">CompletionSuggestionKind</a></li><li><a href="#type_ContextData">ContextData</a></li><li><a href="#type_DiagnosticMessage">DiagnosticMessage</a></li><li><a href="#type_Element">Element</a></li><li><a href="#type_ElementDeclaration">ElementDeclaration</a></li><li><a href="#type_ElementKind">ElementKind</a></li><li><a href="#type_ExecutableFile">ExecutableFile</a></li><li><a href="#type_ExecutableKind">ExecutableKind</a></li><li><a href="#type_ExecutionContextId">ExecutionContextId</a></li><li><a href="#type_ExecutionService">ExecutionService</a></li><li><a href="#type_ExistingImport">ExistingImport</a></li><li><a href="#type_ExistingImports">ExistingImports</a></li><li><a href="#type_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</a></li><li><a href="#type_FlutterOutline">FlutterOutline</a></li><li><a href="#type_FlutterOutlineAttribute">FlutterOutlineAttribute</a></li><li><a href="#type_FlutterOutlineKind">FlutterOutlineKind</a></li><li><a href="#type_FlutterService">FlutterService</a></li><li><a href="#type_FlutterWidgetProperty">FlutterWidgetProperty</a></li><li><a href="#type_FlutterWidgetPropertyEditor">FlutterWidgetPropertyEditor</a></li><li><a href="#type_FlutterWidgetPropertyEditorKind">FlutterWidgetPropertyEditorKind</a></li><li><a href="#type_FlutterWidgetPropertyValue">FlutterWidgetPropertyValue</a></li><li><a href="#type_FlutterWidgetPropertyValueEnumItem">FlutterWidgetPropertyValueEnumItem</a></li><li><a href="#type_FoldingKind">FoldingKind</a></li><li><a href="#type_FoldingRegion">FoldingRegion</a></li><li><a href="#type_GeneralAnalysisService">GeneralAnalysisService</a></li><li><a href="#type_HighlightRegion">HighlightRegion</a></li><li><a href="#type_HighlightRegionType">HighlightRegionType</a></li><li><a href="#type_HoverInformation">HoverInformation</a></li><li><a href="#type_ImplementedClass">ImplementedClass</a></li><li><a href="#type_ImplementedMember">ImplementedMember</a></li><li><a href="#type_ImportedElementSet">ImportedElementSet</a></li><li><a href="#type_ImportedElements">ImportedElements</a></li><li><a href="#type_IncludedSuggestionRelevanceTag">IncludedSuggestionRelevanceTag</a></li><li><a href="#type_IncludedSuggestionSet">IncludedSuggestionSet</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</a></li><li><a href="#type_LibraryPathSet">LibraryPathSet</a></li><li><a href="#type_LinkedEditGroup">LinkedEditGroup</a></li><li><a href="#type_LinkedEditSuggestion">LinkedEditSuggestion</a></li><li><a href="#type_LinkedEditSuggestionKind">LinkedEditSuggestionKind</a></li><li><a href="#type_Location">Location</a></li><li><a href="#type_NavigationRegion">NavigationRegion</a></li><li><a href="#type_NavigationTarget">NavigationTarget</a></li><li><a href="#type_Occurrences">Occurrences</a></li><li><a href="#type_Outline">Outline</a></li><li><a href="#type_OverriddenMember">OverriddenMember</a></li><li><a href="#type_Override">Override</a></li><li><a href="#type_Position">Position</a></li><li><a href="#type_PostfixTemplateDescriptor">PostfixTemplateDescriptor</a></li><li><a href="#type_PubStatus">PubStatus</a></li><li><a href="#type_RefactoringFeedback">RefactoringFeedback</a></li><li><a href="#type_RefactoringKind">RefactoringKind</a></li><li><a href="#type_RefactoringMethodParameter">RefactoringMethodParameter</a></li><li><a href="#type_RefactoringMethodParameterKind">RefactoringMethodParameterKind</a></li><li><a href="#type_RefactoringOptions">RefactoringOptions</a></li><li><a href="#type_RefactoringProblem">RefactoringProblem</a></li><li><a href="#type_RefactoringProblemSeverity">RefactoringProblemSeverity</a></li><li><a href="#type_RemoveContentOverlay">RemoveContentOverlay</a></li><li><a href="#type_RequestError">RequestError</a></li><li><a href="#type_RequestErrorCode">RequestErrorCode</a></li><li><a href="#type_RuntimeCompletionExpression">RuntimeCompletionExpression</a></li><li><a href="#type_RuntimeCompletionExpressionType">RuntimeCompletionExpressionType</a></li><li><a href="#type_RuntimeCompletionExpressionTypeKind">RuntimeCompletionExpressionTypeKind</a></li><li><a href="#type_RuntimeCompletionVariable">RuntimeCompletionVariable</a></li><li><a href="#type_SearchId">SearchId</a></li><li><a href="#type_SearchResult">SearchResult</a></li><li><a href="#type_SearchResultKind">SearchResultKind</a></li><li><a href="#type_ServerService">ServerService</a></li><li><a href="#type_SourceChange">SourceChange</a></li><li><a href="#type_SourceEdit">SourceEdit</a></li><li><a href="#type_SourceFileEdit">SourceFileEdit</a></li><li><a href="#type_TypeHierarchyItem">TypeHierarchyItem</a></li></ul></div><h3>Refactorings (<a href="#refactorings">↑</a>)</h3><div class="subindex"><ul><li><a href="#refactoring_CONVERT_GETTER_TO_METHOD">CONVERT_GETTER_TO_METHOD</a></li><li><a href="#refactoring_CONVERT_METHOD_TO_GETTER">CONVERT_METHOD_TO_GETTER</a></li><li><a href="#refactoring_EXTRACT_LOCAL_VARIABLE">EXTRACT_LOCAL_VARIABLE</a></li><li><a href="#refactoring_EXTRACT_METHOD">EXTRACT_METHOD</a></li><li><a href="#refactoring_EXTRACT_WIDGET">EXTRACT_WIDGET</a></li><li><a href="#refactoring_INLINE_LOCAL_VARIABLE">INLINE_LOCAL_VARIABLE</a></li><li><a href="#refactoring_INLINE_METHOD">INLINE_METHOD</a></li><li><a href="#refactoring_MOVE_FILE">MOVE_FILE</a></li><li><a href="#refactoring_RENAME">RENAME</a></li></ul></div>
+<h3>Domains</h3><h4>server (<a href="#domain_server">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_server.getVersion">getVersion</a></li><li><a href="#request_server.shutdown">shutdown</a></li><li><a href="#request_server.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_server.connected">connected</a></li><li><a href="#notification_server.error">error</a></li><li><a href="#notification_server.log">log</a></li><li><a href="#notification_server.status">status</a></li></ul></div></div><h4>analysis (<a href="#domain_analysis">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_analysis.getErrors">getErrors</a></li><li><a href="#request_analysis.getHover">getHover</a></li><li><a href="#request_analysis.getLibraryDependencies">getLibraryDependencies</a></li><li><a href="#request_analysis.getNavigation">getNavigation</a></li><li><a href="#request_analysis.getReachableSources">getReachableSources</a></li><li><a href="#request_analysis.reanalyze">reanalyze</a></li><li><a href="#request_analysis.setAnalysisRoots">setAnalysisRoots</a></li><li><a href="#request_analysis.setGeneralSubscriptions">setGeneralSubscriptions</a></li><li><a href="#request_analysis.setPriorityFiles">setPriorityFiles</a></li><li><a href="#request_analysis.setSubscriptions">setSubscriptions</a></li><li><a href="#request_analysis.updateContent">updateContent</a></li><li><a href="#request_analysis.updateOptions">updateOptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_analysis.analyzedFiles">analyzedFiles</a></li><li><a href="#notification_analysis.closingLabels">closingLabels</a></li><li><a href="#notification_analysis.errors">errors</a></li><li><a href="#notification_analysis.flushResults">flushResults</a></li><li><a href="#notification_analysis.folding">folding</a></li><li><a href="#notification_analysis.highlights">highlights</a></li><li><a href="#notification_analysis.implemented">implemented</a></li><li><a href="#notification_analysis.invalidate">invalidate</a></li><li><a href="#notification_analysis.navigation">navigation</a></li><li><a href="#notification_analysis.occurrences">occurrences</a></li><li><a href="#notification_analysis.outline">outline</a></li><li><a href="#notification_analysis.overrides">overrides</a></li></ul></div></div><h4>completion (<a href="#domain_completion">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_completion.getSuggestions">getSuggestions</a></li><li><a href="#request_completion.setSubscriptions">setSubscriptions</a></li><li><a href="#request_completion.registerLibraryPaths">registerLibraryPaths</a></li><li><a href="#request_completion.getSuggestionDetails">getSuggestionDetails</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</a></li><li><a href="#notification_completion.availableSuggestions">availableSuggestions</a></li><li><a href="#notification_completion.existingImports">existingImports</a></li></ul></div></div><h4>search (<a href="#domain_search">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_search.findElementReferences">findElementReferences</a></li><li><a href="#request_search.findMemberDeclarations">findMemberDeclarations</a></li><li><a href="#request_search.findMemberReferences">findMemberReferences</a></li><li><a href="#request_search.findTopLevelDeclarations">findTopLevelDeclarations</a></li><li><a href="#request_search.getTypeHierarchy">getTypeHierarchy</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_search.results">results</a></li></ul></div></div><h4>edit (<a href="#domain_edit">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_edit.format">format</a></li><li><a href="#request_edit.getAssists">getAssists</a></li><li><a href="#request_edit.getAvailableRefactorings">getAvailableRefactorings</a></li><li><a href="#request_edit.getFixes">getFixes</a></li><li><a href="#request_edit.getPostfixCompletion">getPostfixCompletion</a></li><li><a href="#request_edit.getRefactoring">getRefactoring</a></li><li><a href="#request_edit.sortMembers">sortMembers</a></li><li><a href="#request_edit.organizeDirectives">organizeDirectives</a></li></ul></div><h4>execution (<a href="#domain_execution">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_execution.createContext">createContext</a></li><li><a href="#request_execution.deleteContext">deleteContext</a></li><li><a href="#request_execution.getSuggestions">getSuggestions</a></li><li><a href="#request_execution.mapUri">mapUri</a></li><li><a href="#request_execution.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_execution.launchData">launchData</a></li></ul></div></div><h4>diagnostic (<a href="#domain_diagnostic">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_diagnostic.getDiagnostics">getDiagnostics</a></li><li><a href="#request_diagnostic.getServerPort">getServerPort</a></li></ul></div><h4>flutter (<a href="#domain_flutter">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_flutter.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_flutter.outline">outline</a></li></ul></div></div><h3>Types (<a href="#types">↑</a>)</h3><div class="subindex"><ul><li><a href="#type_AddContentOverlay">AddContentOverlay</a></li><li><a href="#type_AnalysisError">AnalysisError</a></li><li><a href="#type_AnalysisErrorFixes">AnalysisErrorFixes</a></li><li><a href="#type_AnalysisErrorSeverity">AnalysisErrorSeverity</a></li><li><a href="#type_AnalysisErrorType">AnalysisErrorType</a></li><li><a href="#type_AnalysisOptions">AnalysisOptions</a></li><li><a href="#type_AnalysisService">AnalysisService</a></li><li><a href="#type_AnalysisStatus">AnalysisStatus</a></li><li><a href="#type_AvailableSuggestion">AvailableSuggestion</a></li><li><a href="#type_AvailableSuggestionRelevanceTag">AvailableSuggestionRelevanceTag</a></li><li><a href="#type_AvailableSuggestionSet">AvailableSuggestionSet</a></li><li><a href="#type_BulkFix">BulkFix</a></li><li><a href="#type_BulkFixDetail">BulkFixDetail</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionId">CompletionId</a></li><li><a href="#type_CompletionService">CompletionService</a></li><li><a href="#type_CompletionSuggestion">CompletionSuggestion</a></li><li><a href="#type_CompletionSuggestionKind">CompletionSuggestionKind</a></li><li><a href="#type_ContextData">ContextData</a></li><li><a href="#type_DiagnosticMessage">DiagnosticMessage</a></li><li><a href="#type_Element">Element</a></li><li><a href="#type_ElementDeclaration">ElementDeclaration</a></li><li><a href="#type_ElementKind">ElementKind</a></li><li><a href="#type_ExecutableFile">ExecutableFile</a></li><li><a href="#type_ExecutableKind">ExecutableKind</a></li><li><a href="#type_ExecutionContextId">ExecutionContextId</a></li><li><a href="#type_ExecutionService">ExecutionService</a></li><li><a href="#type_ExistingImport">ExistingImport</a></li><li><a href="#type_ExistingImports">ExistingImports</a></li><li><a href="#type_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</a></li><li><a href="#type_FlutterOutline">FlutterOutline</a></li><li><a href="#type_FlutterOutlineAttribute">FlutterOutlineAttribute</a></li><li><a href="#type_FlutterOutlineKind">FlutterOutlineKind</a></li><li><a href="#type_FlutterService">FlutterService</a></li><li><a href="#type_FlutterWidgetProperty">FlutterWidgetProperty</a></li><li><a href="#type_FlutterWidgetPropertyEditor">FlutterWidgetPropertyEditor</a></li><li><a href="#type_FlutterWidgetPropertyEditorKind">FlutterWidgetPropertyEditorKind</a></li><li><a href="#type_FlutterWidgetPropertyValue">FlutterWidgetPropertyValue</a></li><li><a href="#type_FlutterWidgetPropertyValueEnumItem">FlutterWidgetPropertyValueEnumItem</a></li><li><a href="#type_FoldingKind">FoldingKind</a></li><li><a href="#type_FoldingRegion">FoldingRegion</a></li><li><a href="#type_GeneralAnalysisService">GeneralAnalysisService</a></li><li><a href="#type_HighlightRegion">HighlightRegion</a></li><li><a href="#type_HighlightRegionType">HighlightRegionType</a></li><li><a href="#type_HoverInformation">HoverInformation</a></li><li><a href="#type_ImplementedClass">ImplementedClass</a></li><li><a href="#type_ImplementedMember">ImplementedMember</a></li><li><a href="#type_ImportedElementSet">ImportedElementSet</a></li><li><a href="#type_ImportedElements">ImportedElements</a></li><li><a href="#type_IncludedSuggestionRelevanceTag">IncludedSuggestionRelevanceTag</a></li><li><a href="#type_IncludedSuggestionSet">IncludedSuggestionSet</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</a></li><li><a href="#type_LibraryPathSet">LibraryPathSet</a></li><li><a href="#type_LinkedEditGroup">LinkedEditGroup</a></li><li><a href="#type_LinkedEditSuggestion">LinkedEditSuggestion</a></li><li><a href="#type_LinkedEditSuggestionKind">LinkedEditSuggestionKind</a></li><li><a href="#type_Location">Location</a></li><li><a href="#type_NavigationRegion">NavigationRegion</a></li><li><a href="#type_NavigationTarget">NavigationTarget</a></li><li><a href="#type_Occurrences">Occurrences</a></li><li><a href="#type_Outline">Outline</a></li><li><a href="#type_OverriddenMember">OverriddenMember</a></li><li><a href="#type_Override">Override</a></li><li><a href="#type_Position">Position</a></li><li><a href="#type_PostfixTemplateDescriptor">PostfixTemplateDescriptor</a></li><li><a href="#type_PubStatus">PubStatus</a></li><li><a href="#type_RefactoringFeedback">RefactoringFeedback</a></li><li><a href="#type_RefactoringKind">RefactoringKind</a></li><li><a href="#type_RefactoringMethodParameter">RefactoringMethodParameter</a></li><li><a href="#type_RefactoringMethodParameterKind">RefactoringMethodParameterKind</a></li><li><a href="#type_RefactoringOptions">RefactoringOptions</a></li><li><a href="#type_RefactoringProblem">RefactoringProblem</a></li><li><a href="#type_RefactoringProblemSeverity">RefactoringProblemSeverity</a></li><li><a href="#type_RemoveContentOverlay">RemoveContentOverlay</a></li><li><a href="#type_RequestError">RequestError</a></li><li><a href="#type_RequestErrorCode">RequestErrorCode</a></li><li><a href="#type_RuntimeCompletionExpression">RuntimeCompletionExpression</a></li><li><a href="#type_RuntimeCompletionExpressionType">RuntimeCompletionExpressionType</a></li><li><a href="#type_RuntimeCompletionExpressionTypeKind">RuntimeCompletionExpressionTypeKind</a></li><li><a href="#type_RuntimeCompletionVariable">RuntimeCompletionVariable</a></li><li><a href="#type_SearchId">SearchId</a></li><li><a href="#type_SearchResult">SearchResult</a></li><li><a href="#type_SearchResultKind">SearchResultKind</a></li><li><a href="#type_ServerService">ServerService</a></li><li><a href="#type_SourceChange">SourceChange</a></li><li><a href="#type_SourceEdit">SourceEdit</a></li><li><a href="#type_SourceFileEdit">SourceFileEdit</a></li><li><a href="#type_TypeHierarchyItem">TypeHierarchyItem</a></li></ul></div><h3>Refactorings (<a href="#refactorings">↑</a>)</h3><div class="subindex"><ul><li><a href="#refactoring_CONVERT_GETTER_TO_METHOD">CONVERT_GETTER_TO_METHOD</a></li><li><a href="#refactoring_CONVERT_METHOD_TO_GETTER">CONVERT_METHOD_TO_GETTER</a></li><li><a href="#refactoring_EXTRACT_LOCAL_VARIABLE">EXTRACT_LOCAL_VARIABLE</a></li><li><a href="#refactoring_EXTRACT_METHOD">EXTRACT_METHOD</a></li><li><a href="#refactoring_EXTRACT_WIDGET">EXTRACT_WIDGET</a></li><li><a href="#refactoring_INLINE_LOCAL_VARIABLE">INLINE_LOCAL_VARIABLE</a></li><li><a href="#refactoring_INLINE_METHOD">INLINE_METHOD</a></li><li><a href="#refactoring_MOVE_FILE">MOVE_FILE</a></li><li><a href="#refactoring_RENAME">RENAME</a></li></ul></div>
</body></html>
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index fd9998b..2689506 100644
--- a/pkg/analysis_server/lib/protocol/protocol_constants.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart
@@ -350,6 +350,8 @@
const String SERVER_NOTIFICATION_ERROR_IS_FATAL = 'isFatal';
const String SERVER_NOTIFICATION_ERROR_MESSAGE = 'message';
const String SERVER_NOTIFICATION_ERROR_STACK_TRACE = 'stackTrace';
+const String SERVER_NOTIFICATION_LOG = 'server.log';
+const String SERVER_NOTIFICATION_LOG_ENTRY = 'entry';
const String SERVER_NOTIFICATION_STATUS = 'server.status';
const String SERVER_NOTIFICATION_STATUS_ANALYSIS = 'analysis';
const String SERVER_NOTIFICATION_STATUS_PUB = 'pub';
diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart
index ac2f847..8cd904d 100644
--- a/pkg/analysis_server/lib/protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart
@@ -15871,18 +15871,249 @@
int get hashCode => version.hashCode;
}
+/// ServerLogEntry
+///
+/// {
+/// "time": int
+/// "kind": ServerLogEntryKind
+/// "data": String
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class ServerLogEntry implements HasToJson {
+ /// The time (milliseconds since epoch) at which the server created this log
+ /// entry.
+ int time;
+
+ /// The kind of the entry, used to determine how to interpret the "data"
+ /// field.
+ ServerLogEntryKind kind;
+
+ /// The payload of the entry, the actual format is determined by the "kind"
+ /// field.
+ String data;
+
+ ServerLogEntry(this.time, this.kind, this.data);
+
+ factory ServerLogEntry.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ int time;
+ if (json.containsKey('time')) {
+ time = jsonDecoder.decodeInt(jsonPath + '.time', json['time']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'time');
+ }
+ ServerLogEntryKind kind;
+ if (json.containsKey('kind')) {
+ kind = ServerLogEntryKind.fromJson(
+ jsonDecoder, jsonPath + '.kind', json['kind']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'kind');
+ }
+ String data;
+ if (json.containsKey('data')) {
+ data = jsonDecoder.decodeString(jsonPath + '.data', json['data']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'data');
+ }
+ return ServerLogEntry(time, kind, data);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'ServerLogEntry', json);
+ }
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['time'] = time;
+ result['kind'] = kind.toJson();
+ result['data'] = data;
+ return result;
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is ServerLogEntry) {
+ return time == other.time && kind == other.kind && data == other.data;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ time,
+ kind,
+ data,
+ );
+}
+
+/// ServerLogEntryKind
+///
+/// enum {
+/// NOTIFICATION
+/// RAW
+/// REQUEST
+/// RESPONSE
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class ServerLogEntryKind implements Enum {
+ /// A notification from the server, such as "analysis.highlights". The "data"
+ /// field contains a JSON object with abbreviated notification.
+ static const ServerLogEntryKind NOTIFICATION =
+ ServerLogEntryKind._('NOTIFICATION');
+
+ /// Arbitrary string, describing some event that happened in the server, e.g.
+ /// starting a file analysis, and details which files were accessed. These
+ /// entries are not structured, but provide context information about
+ /// requests and notification, and can be related by "time" for further
+ /// manual analysis.
+ static const ServerLogEntryKind RAW = ServerLogEntryKind._('RAW');
+
+ /// A request from the client, as the server views it, e.g.
+ /// "edit.getAssists". The "data" field contains a JSON object with
+ /// abbreviated request.
+ static const ServerLogEntryKind REQUEST = ServerLogEntryKind._('REQUEST');
+
+ /// Various counters and measurements related to execution of a request. The
+ /// "data" field contains a JSON object with following fields:
+ ///
+ /// - "id" - the id of the request - copied from the request.
+ /// - "method" - the method of the request, e.g. "edit.getAssists".
+ /// - "clientRequestTime" - the time (milliseconds since epoch) at which the
+ /// client made the request - copied from the request.
+ /// - "serverRequestTime" - the time (milliseconds since epoch) at which the
+ /// server received and decoded the JSON request.
+ /// - "responseTime" - the time (milliseconds since epoch) at which the
+ /// server created the response to be encoded into JSON and sent to the
+ /// client.
+ static const ServerLogEntryKind RESPONSE = ServerLogEntryKind._('RESPONSE');
+
+ /// A list containing all of the enum values that are defined.
+ static const List<ServerLogEntryKind> VALUES = <ServerLogEntryKind>[
+ NOTIFICATION,
+ RAW,
+ REQUEST,
+ RESPONSE
+ ];
+
+ @override
+ final String name;
+
+ const ServerLogEntryKind._(this.name);
+
+ factory ServerLogEntryKind(String name) {
+ switch (name) {
+ case 'NOTIFICATION':
+ return NOTIFICATION;
+ case 'RAW':
+ return RAW;
+ case 'REQUEST':
+ return REQUEST;
+ case 'RESPONSE':
+ return RESPONSE;
+ }
+ throw Exception('Illegal enum value: $name');
+ }
+
+ factory ServerLogEntryKind.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ if (json is String) {
+ try {
+ return ServerLogEntryKind(json);
+ } catch (_) {
+ // Fall through
+ }
+ }
+ throw jsonDecoder.mismatch(jsonPath, 'ServerLogEntryKind', json);
+ }
+
+ @override
+ String toString() => 'ServerLogEntryKind.$name';
+
+ String toJson() => name;
+}
+
+/// server.log params
+///
+/// {
+/// "entry": ServerLogEntry
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class ServerLogParams implements HasToJson {
+ ServerLogEntry entry;
+
+ ServerLogParams(this.entry);
+
+ factory ServerLogParams.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ ServerLogEntry entry;
+ if (json.containsKey('entry')) {
+ entry = ServerLogEntry.fromJson(
+ jsonDecoder, jsonPath + '.entry', json['entry']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'entry');
+ }
+ return ServerLogParams(entry);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'server.log params', json);
+ }
+ }
+
+ factory ServerLogParams.fromNotification(Notification notification) {
+ return ServerLogParams.fromJson(
+ ResponseDecoder(null), 'params', notification.params);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['entry'] = entry.toJson();
+ return result;
+ }
+
+ Notification toNotification() {
+ return Notification('server.log', toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is ServerLogParams) {
+ return entry == other.entry;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => entry.hashCode;
+}
+
/// ServerService
///
/// enum {
+/// LOG
/// STATUS
/// }
///
/// Clients may not extend, implement or mix-in this class.
class ServerService implements Enum {
+ static const ServerService LOG = ServerService._('LOG');
+
static const ServerService STATUS = ServerService._('STATUS');
/// A list containing all of the enum values that are defined.
- static const List<ServerService> VALUES = <ServerService>[STATUS];
+ static const List<ServerService> VALUES = <ServerService>[LOG, STATUS];
@override
final String name;
@@ -15891,6 +16122,8 @@
factory ServerService(String name) {
switch (name) {
+ case 'LOG':
+ return LOG;
case 'STATUS':
return STATUS;
}
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 26000c7..2b98916 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -32,6 +32,7 @@
import 'package:analysis_server/src/plugin/notification_manager.dart';
import 'package:analysis_server/src/protocol_server.dart' as server;
import 'package:analysis_server/src/search/search_domain.dart';
+import 'package:analysis_server/src/server/completion_request_aborting.dart';
import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
import 'package:analysis_server/src/server/detachable_filesystem_manager.dart';
import 'package:analysis_server/src/server/diagnostic_server.dart';
@@ -40,6 +41,7 @@
import 'package:analysis_server/src/server/sdk_configuration.dart';
import 'package:analysis_server/src/services/flutter/widget_descriptions.dart';
import 'package:analysis_server/src/utilities/process.dart';
+import 'package:analysis_server/src/utilities/request_statistics.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/exception/exception.dart';
@@ -114,6 +116,9 @@
final DetachableFileSystemManager? detachableFileSystemManager;
+ final CompletionRequestAborting completionRequestAborting =
+ CompletionRequestAborting();
+
/// Initialize a newly created server to receive requests from and send
/// responses to the given [channel].
///
@@ -130,6 +135,7 @@
InstrumentationService instrumentationService, {
http.Client? httpClient,
ProcessRunner? processRunner,
+ RequestStatisticsHelper? requestStatistics,
DiagnosticServer? diagnosticServer,
this.detachableFileSystemManager,
// Disable to avoid using this in unit tests.
@@ -144,6 +150,7 @@
httpClient,
processRunner,
NotificationManager(channel, baseResourceProvider.pathContext),
+ requestStatistics: requestStatistics,
enableBazelWatcher: enableBazelWatcher,
) {
var contextManagerCallbacks =
@@ -164,7 +171,7 @@
io.pid,
).toNotification(),
);
- channel.listen(handleRequest, onDone: done, onError: error);
+ channel.requests.listen(handleRequest, onDone: done, onError: error);
handlers = <server.RequestHandler>[
ServerDomainHandler(this),
AnalysisDomainHandler(this),
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index 7779957..9e758b6 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -26,6 +26,8 @@
import 'package:analysis_server/src/utilities/file_string_sink.dart';
import 'package:analysis_server/src/utilities/null_string_sink.dart';
import 'package:analysis_server/src/utilities/process.dart';
+import 'package:analysis_server/src/utilities/request_statistics.dart';
+import 'package:analysis_server/src/utilities/tee_string_sink.dart';
import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/session.dart';
@@ -125,6 +127,8 @@
/// Performance information before initial analysis is complete.
final ServerPerformance performanceDuringStartup = ServerPerformance();
+ RequestStatisticsHelper? requestStatistics;
+
PerformanceLog? analysisPerformanceLogger;
/// The set of the files that are currently priority.
@@ -151,6 +155,7 @@
http.Client? httpClient,
ProcessRunner? processRunner,
this.notificationManager, {
+ this.requestStatistics,
bool enableBazelWatcher = false,
}) : resourceProvider = OverlayResourceProvider(baseResourceProvider),
pubApi = PubApi(instrumentationService, httpClient,
@@ -190,6 +195,10 @@
sink = FileStringSink(path);
}
}
+ final requestStatistics = this.requestStatistics;
+ if (requestStatistics != null) {
+ sink = TeeStringSink(sink, requestStatistics.perfLoggerStringSink);
+ }
final analysisPerformanceLogger =
this.analysisPerformanceLogger = PerformanceLog(sink);
diff --git a/pkg/analysis_server/lib/src/channel/byte_stream_channel.dart b/pkg/analysis_server/lib/src/channel/byte_stream_channel.dart
index 869c9b9..955e547 100644
--- a/pkg/analysis_server/lib/src/channel/byte_stream_channel.dart
+++ b/pkg/analysis_server/lib/src/channel/byte_stream_channel.dart
@@ -8,6 +8,7 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/src/channel/channel.dart';
+import 'package:analysis_server/src/utilities/request_statistics.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
/// Instances of the class [ByteStreamClientChannel] implement a
@@ -72,20 +73,40 @@
/// standard input and standard output) to communicate with clients.
class ByteStreamServerChannel implements ServerCommunicationChannel {
final Stream _input;
-
final IOSink _output;
/// The instrumentation service that is to be used by this analysis server.
final InstrumentationService _instrumentationService;
+ /// The helper for recording request / response statistics.
+ final RequestStatisticsHelper? _requestStatistics;
+
/// Completer that will be signalled when the input stream is closed.
final Completer _closed = Completer();
/// True if [close] has been called.
bool _closeRequested = false;
+ @override
+ late final Stream<Request> requests = _input
+ .transform(const Utf8Decoder())
+ .transform(const LineSplitter())
+ .transform(
+ StreamTransformer.fromHandlers(
+ handleData: _readRequest,
+ handleDone: (sink) {
+ close();
+ sink.close();
+ },
+ ),
+ );
+
ByteStreamServerChannel(
- this._input, this._output, this._instrumentationService);
+ this._input, this._output, this._instrumentationService,
+ {RequestStatisticsHelper? requestStatistics})
+ : _requestStatistics = requestStatistics {
+ _requestStatistics?.serverChannel = this;
+ }
/// Future that will be completed when the input stream is closed.
Future get closed {
@@ -102,17 +123,6 @@
}
@override
- void listen(void Function(Request request) onRequest,
- {Function? onError, void Function()? onDone}) {
- _input.transform(const Utf8Decoder()).transform(LineSplitter()).listen(
- (String data) => _readRequest(data, onRequest),
- onError: onError, onDone: () {
- close();
- onDone?.call();
- });
- }
-
- @override
void sendNotification(Notification notification) {
// Don't send any further notifications after the communication channel is
// closed.
@@ -121,7 +131,10 @@
}
var jsonEncoding = json.encode(notification.toJson());
_outputLine(jsonEncoding);
- _instrumentationService.logNotification(jsonEncoding);
+ if (!identical(notification.event, 'server.log')) {
+ _instrumentationService.logNotification(jsonEncoding);
+ _requestStatistics?.logNotification(notification);
+ }
}
@override
@@ -131,6 +144,7 @@
if (_closeRequested) {
return;
}
+ _requestStatistics?.addResponse(response);
var jsonEncoding = json.encode(response.toJson());
_outputLine(jsonEncoding);
_instrumentationService.logResponse(jsonEncoding);
@@ -147,7 +161,7 @@
/// Read a request from the given [data] and use the given function to handle
/// the request.
- void _readRequest(String data, void Function(Request request) onRequest) {
+ void _readRequest(String data, Sink<Request> sink) {
// Ignore any further requests after the communication channel is closed.
if (_closed.isCompleted) {
return;
@@ -160,6 +174,7 @@
sendResponse(Response.invalidRequestFormat());
return;
}
- onRequest(request);
+ _requestStatistics?.addRequest(request);
+ sink.add(request);
}
}
diff --git a/pkg/analysis_server/lib/src/channel/channel.dart b/pkg/analysis_server/lib/src/channel/channel.dart
index 6b5e95f..37a6e86 100644
--- a/pkg/analysis_server/lib/src/channel/channel.dart
+++ b/pkg/analysis_server/lib/src/channel/channel.dart
@@ -98,17 +98,12 @@
/// objects that allow an [AnalysisServer] to receive [Request]s and to return
/// both [Response]s and [Notification]s.
abstract class ServerCommunicationChannel {
+ /// The single-subscription stream of requests.
+ Stream<Request> get requests;
+
/// Close the communication channel.
void close();
- /// Listen to the channel for requests. If a request is received, invoke the
- /// [onRequest] function. If an error is encountered while trying to read from
- /// the socket, invoke the [onError] function. If the socket is closed by the
- /// client, invoke the [onDone] function.
- /// Only one listener is allowed per channel.
- void listen(void Function(Request request) onRequest,
- {Function onError, void Function() onDone});
-
/// Send the given [notification] to the client.
void sendNotification(Notification notification);
diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart
index 1595e38..ec4c4f3 100644
--- a/pkg/analysis_server/lib/src/domain_analysis.dart
+++ b/pkg/analysis_server/lib/src/domain_analysis.dart
@@ -417,6 +417,7 @@
}
}
+ server.completionRequestAborting.abort();
server.updateContent(request.id, params.files);
//
// Forward the request to the plugins.
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index d44110b..925a100 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -297,6 +297,14 @@
return;
}
+ server.completionRequestAborting.abort();
+ if (await server.completionRequestAborting.waitIfAborted(request)) {
+ return server.sendResponse(
+ CompletionGetSuggestions2Result(offset, 0, [], [], true)
+ .toResponse(request.id),
+ );
+ }
+
var performance = OperationPerformanceImpl('<root>');
performance.runAsync(
'request',
@@ -501,6 +509,8 @@
return;
}
+ server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
+
if (offset < 0 || offset > resolvedUnit.content.length) {
server.sendResponse(Response.invalidParameter(
request,
@@ -578,7 +588,7 @@
);
computeIncludedSetList(
declarationsTracker,
- resolvedUnit,
+ completionRequest,
includedSuggestionSets,
includedElementNames,
);
@@ -709,11 +719,8 @@
_RequestToPlugins? _sendRequestToPlugins(
DartCompletionRequest completionRequest,
) {
- var resolvedUnit = completionRequest.result;
- var analysisContext = resolvedUnit.session.analysisContext;
-
var pluginRequestParameters = plugin.CompletionGetSuggestionsParams(
- resolvedUnit.path,
+ completionRequest.path,
completionRequest.offset,
);
@@ -722,7 +729,7 @@
parameters: pluginRequestParameters,
futures: server.pluginManager.broadcastRequest(
pluginRequestParameters,
- contextRoot: analysisContext.contextRoot,
+ contextRoot: completionRequest.analysisContext.contextRoot,
),
);
}
diff --git a/pkg/analysis_server/lib/src/domain_server.dart b/pkg/analysis_server/lib/src/domain_server.dart
index 78a3cee..559f3b8 100644
--- a/pkg/analysis_server/lib/src/domain_server.dart
+++ b/pkg/analysis_server/lib/src/domain_server.dart
@@ -49,6 +49,9 @@
server.serverServices =
ServerSetSubscriptionsParams.fromRequest(request).subscriptions.toSet();
+ server.requestStatistics?.isNotificationSubscribed =
+ server.serverServices.contains(ServerService.LOG);
+
return ServerSetSubscriptionsResult().toResponse(request.id);
}
diff --git a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
index 1d4b4b5..e8838ce 100644
--- a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
+++ b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
@@ -3,29 +3,29 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/protocol_server.dart' as protocol;
+import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/services/available_declarations.dart';
-/// Compute which suggestion sets should be included into completion inside
-/// the given [resolvedUnit] of a file. Depending on the file path, it might
+/// Compute which suggestion sets should be included into completion for
+/// the given completion [request]. Depending on the file path, it might
/// include different sets, e.g. inside the `lib/` directory of a `Pub` package
/// only regular dependencies can be referenced, but `test/` can reference
/// both regular and "dev" dependencies.
void computeIncludedSetList(
DeclarationsTracker tracker,
- ResolvedUnitResult resolvedUnit,
+ DartCompletionRequest request,
List<protocol.IncludedSuggestionSet> includedSetList,
Set<String> includedElementNames,
) {
- var analysisContext = resolvedUnit.session.analysisContext;
- var context = tracker.getContext(analysisContext);
+ var context = tracker.getContext(request.analysisContext);
if (context == null) return;
- var librariesObject = context.getLibraries(resolvedUnit.path);
+ var librariesObject = context.getLibraries(request.path);
- var importedUriSet = resolvedUnit.libraryElement.importedLibraries
+ var importedUriSet = request.libraryElement.importedLibraries
.map((importedLibrary) => importedLibrary.source.uri)
.toSet();
@@ -48,7 +48,7 @@
protocol.IncludedSuggestionSet(
library.id,
relevance,
- displayUri: _getRelativeFileUri(resolvedUnit, library.uri),
+ displayUri: _getRelativeFileUri(request, library.uri),
),
);
@@ -172,11 +172,11 @@
}
/// Computes the best URI to import [what] into the [unit] library.
-String? _getRelativeFileUri(ResolvedUnitResult unit, Uri what) {
+String? _getRelativeFileUri(DartCompletionRequest request, Uri what) {
if (what.scheme == 'file') {
- var pathContext = unit.session.resourceProvider.pathContext;
+ var pathContext = request.analysisSession.resourceProvider.pathContext;
- var libraryPath = unit.libraryElement.source.fullName;
+ var libraryPath = request.libraryElement.source.fullName;
var libraryFolder = pathContext.dirname(libraryPath);
var whatPath = pathContext.fromUri(what);
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index 9f90b8b..b73448c 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -207,6 +207,7 @@
//
var responses =
await waitForResponses(pluginFutures, requestParameters: requestParams);
+ server.requestStatistics?.addItemTimeNow(request, 'pluginResponses');
var converter = ResultConverter();
var pluginChanges = <plugin.PrioritizedSourceChange>[];
for (var response in responses) {
@@ -267,6 +268,7 @@
//
var responses =
await waitForResponses(pluginFutures, requestParameters: requestParams);
+ server.requestStatistics?.addItemTimeNow(request, 'pluginResponses');
var converter = ResultConverter();
for (var response in responses) {
var result = plugin.EditGetFixesResult.fromResponse(response);
@@ -591,6 +593,7 @@
Request request, String file, int offset) async {
var errorFixesList = <AnalysisErrorFixes>[];
var result = await server.getResolvedUnit(file);
+ server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
if (result != null) {
var lineInfo = result.lineInfo;
var requestLine = lineInfo.getLocation(offset).lineNumber;
@@ -641,6 +644,7 @@
}
}
}
+ server.requestStatistics?.addItemTimeNow(request, 'computedFixes');
return errorFixesList;
}
@@ -736,6 +740,7 @@
var changes = <SourceChange>[];
var result = await server.getResolvedUnit(file);
+ server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
if (result != null) {
var context = DartAssistContextImpl(
@@ -765,6 +770,8 @@
'parameters': parametersFile,
});
}
+
+ server.requestStatistics?.addItemTimeNow(request, 'computedAssists');
}
return changes;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index a0bf84c..a4eb456 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -312,7 +312,7 @@
includedSuggestionRelevanceTags != null) {
computeIncludedSetList(
declarationsTracker,
- unit,
+ completionRequest,
includedSuggestionSets,
includedElementNames,
);
diff --git a/pkg/analysis_server/lib/src/server/completion_request_aborting.dart b/pkg/analysis_server/lib/src/server/completion_request_aborting.dart
new file mode 100644
index 0000000..8f03f53
--- /dev/null
+++ b/pkg/analysis_server/lib/src/server/completion_request_aborting.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:meta/meta.dart';
+
+class CompletionRequestAborting {
+ /// IDs of requests to abort on another completion request.
+ final Set<String> _pendingRequests = {};
+
+ /// IDs from [_pendingRequests] that we decided to abort.
+ final Set<String> _abortedRequests = {};
+
+ /// The function to be invoked when requests are aborted.
+ @visibleForTesting
+ void Function(Set<String> id)? onAbort;
+
+ /// Abort requests that were pending.
+ void abort() {
+ onAbort?.call(_pendingRequests);
+ _abortedRequests.addAll(_pendingRequests);
+ }
+
+ /// Return `true` if the [request] should be aborted because there is
+ /// another request in the queue, so [abort] was invoked while pumping the
+ /// event queue.
+ Future<bool> waitIfAborted(Request request) async {
+ // Mark the current request as pending.
+ var id = request.id;
+ _pendingRequests.add(id);
+
+ // Wait for more requests to arrive and abort this one.
+ await _pumpEventQueue(64);
+
+ // We are done waiting.
+ _pendingRequests.remove(id);
+
+ // See it the request was aborted.
+ return _abortedRequests.remove(id);
+ }
+
+ static Future<void> _pumpEventQueue(int times) {
+ if (times == 0) return Future.value();
+ return Future(() => _pumpEventQueue(times - 1));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/server/dev_server.dart b/pkg/analysis_server/lib/src/server/dev_server.dart
index 62886da6..b11c418 100644
--- a/pkg/analysis_server/lib/src/server/dev_server.dart
+++ b/pkg/analysis_server/lib/src/server/dev_server.dart
@@ -165,21 +165,11 @@
Stream<Notification> get onNotification => _notificationController.stream;
@override
- void close() {
- _notificationController.close();
- }
+ Stream<Request> get requests => _requestController.stream;
@override
- void listen(
- void Function(Request request) onRequest, {
- Function? onError,
- void Function()? onDone,
- }) {
- _requestController.stream.listen(
- onRequest,
- onError: onError,
- onDone: onDone,
- );
+ void close() {
+ _notificationController.close();
}
@override
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index a44869e..5016f76 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -24,6 +24,7 @@
import 'package:analysis_server/src/server/sdk_configuration.dart';
import 'package:analysis_server/src/server/stdio_server.dart';
import 'package:analysis_server/src/socket_server.dart';
+import 'package:analysis_server/src/utilities/request_statistics.dart';
import 'package:analysis_server/starter.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/instrumentation/file_instrumentation.dart';
@@ -302,6 +303,7 @@
dartSdkManager,
crashReportingAttachmentsBuilder,
instrumentationService,
+ RequestStatisticsHelper(),
analytics,
diagnosticServerPort,
errorNotifier,
@@ -316,6 +318,7 @@
DartSdkManager dartSdkManager,
CrashReportingAttachmentsBuilder crashReportingAttachmentsBuilder,
InstrumentationService instrumentationService,
+ RequestStatisticsHelper requestStatistics,
telemetry.Analytics analytics,
int? diagnosticServerPort,
ErrorNotifier errorNotifier,
@@ -350,6 +353,7 @@
dartSdkManager,
crashReportingAttachmentsBuilder,
instrumentationService,
+ requestStatistics,
diagnosticServer,
detachableFileSystemManager);
httpServer = HttpAnalysisServer(socketServer);
diff --git a/pkg/analysis_server/lib/src/server/isolate_analysis_server.dart b/pkg/analysis_server/lib/src/server/isolate_analysis_server.dart
index 882f759..2ef30d3 100644
--- a/pkg/analysis_server/lib/src/server/isolate_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/server/isolate_analysis_server.dart
@@ -28,6 +28,7 @@
serverIsolateChannel.stream,
IOSink(serverIsolateChannel.sink),
socketServer.instrumentationService,
+ requestStatistics: socketServer.requestStatistics,
);
socketServer.createAnalysisServer(serverChannel);
await serverChannel.closed;
diff --git a/pkg/analysis_server/lib/src/server/stdio_server.dart b/pkg/analysis_server/lib/src/server/stdio_server.dart
index 78a036b..06c5030 100644
--- a/pkg/analysis_server/lib/src/server/stdio_server.dart
+++ b/pkg/analysis_server/lib/src/server/stdio_server.dart
@@ -27,6 +27,7 @@
stdin,
stdout,
socketServer.instrumentationService,
+ requestStatistics: socketServer.requestStatistics,
);
socketServer.createAnalysisServer(serverChannel);
return serverChannel.closed;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index a5aacbf..0789ea2 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -37,6 +37,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
+import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
@@ -122,7 +123,7 @@
}) async {
request.checkAborted();
var pathContext = request.resourceProvider.pathContext;
- if (!file_paths.isDart(pathContext, request.result.path)) {
+ if (!file_paths.isDart(pathContext, request.path)) {
return const <CompletionSuggestion>[];
}
@@ -276,8 +277,14 @@
/// The information about a requested list of completions within a Dart file.
class DartCompletionRequest {
+ /// The analysis session that produced the elements of the request.
+ final AnalysisSessionImpl analysisSession;
+
final CompletionPreference completionPreference;
+ /// The content of the file in which completion is requested.
+ final String content;
+
/// Return the type imposed on the target's `containingNode` based on its
/// context, or `null` if the context does not impose any type.
final DartType? contextType;
@@ -291,6 +298,9 @@
/// compute relevance scores for suggestions.
final FeatureComputer featureComputer;
+ /// The library element of the file in which completion is requested.
+ final LibraryElement libraryElement;
+
/// Return the offset within the source at which the completion is being
/// requested.
final int offset;
@@ -299,14 +309,13 @@
/// request.
final OpType opType;
+ /// The absolute path of the file where completion is requested.
+ final String path;
+
/// The source range that represents the region of text that should be
/// replaced when a suggestion is selected.
final SourceRange replacementRange;
- /// The analysis result for the file in which the completion is being
- /// requested.
- final ResolvedUnitResult result;
-
/// Return the source in which the completion is being requested.
final Source source;
@@ -343,34 +352,45 @@
}
return DartCompletionRequest._(
+ analysisSession: resolvedUnit.session as AnalysisSessionImpl,
completionPreference: completionPreference,
+ content: resolvedUnit.content,
contextType: contextType,
dartdocDirectiveInfo: dartdocDirectiveInfo ?? DartdocDirectiveInfo(),
documentationCache: documentationCache,
featureComputer: featureComputer,
+ libraryElement: resolvedUnit.libraryElement,
offset: offset,
opType: opType,
+ path: resolvedUnit.path,
replacementRange: target.computeReplacementRange(offset),
- result: resolvedUnit,
source: resolvedUnit.unit.declaredElement!.source,
target: target,
);
}
DartCompletionRequest._({
+ required this.analysisSession,
required this.completionPreference,
+ required this.content,
required this.contextType,
required this.dartdocDirectiveInfo,
required this.documentationCache,
required this.featureComputer,
+ required this.libraryElement,
required this.offset,
required this.opType,
+ required this.path,
required this.replacementRange,
- required this.result,
required this.source,
required this.target,
});
+ DriverBasedAnalysisContext get analysisContext {
+ var analysisContext = analysisSession.analysisContext;
+ return analysisContext as DriverBasedAnalysisContext;
+ }
+
/// Return the feature set that was used to analyze the compilation unit in
/// which suggestions are being made.
FeatureSet get featureSet => libraryElement.featureSet;
@@ -386,10 +406,6 @@
return entity is Expression && entity.inConstantContext;
}
- /// Return the library element which contains the unit in which the completion
- /// is occurring.
- LibraryElement get libraryElement => result.libraryElement;
-
/// Answer the [DartType] for Object in dart:core
DartType get objectType => libraryElement.typeProvider.objectType;
@@ -408,16 +424,11 @@
int get replacementOffset => replacementRange.offset;
/// Return the resource provider associated with this request.
- ResourceProvider get resourceProvider => result.session.resourceProvider;
-
- /// Return the content of the [source] in which the completion is being
- /// requested, or `null` if the content could not be accessed.
- String? get sourceContents => result.content;
+ ResourceProvider get resourceProvider => analysisSession.resourceProvider;
/// Return the [SourceFactory] of the request.
SourceFactory get sourceFactory {
- var context = result.session.analysisContext as DriverBasedAnalysisContext;
- return context.driver.sourceFactory;
+ return analysisContext.driver.sourceFactory;
}
/// Return prefix that already exists in the document for [target] or empty
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
index 734a36e..2441362 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
@@ -163,13 +163,8 @@
return;
}
- var libraryUnits = request.result.unit.declaredElement?.library.units;
- if (libraryUnits == null) {
- return;
- }
-
var visitor = LibraryElementSuggestionBuilder(request, builder);
- for (var unit in libraryUnits) {
+ for (var unit in request.libraryElement.units) {
if (unit.source != request.source) {
unit.accept(visitor);
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
index 1bf599e..52456d4 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
@@ -13,7 +13,6 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
-import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/lint/pub.dart';
import 'package:analyzer/src/workspace/pub.dart';
import 'package:collection/collection.dart';
@@ -37,8 +36,8 @@
@override
Future<void> computeSuggestions() async {
- var session = request.result.session as AnalysisSessionImpl;
- var analysisDriver = session.getDriver(); // ignore: deprecated_member_use
+ var analysisSession = request.analysisSession;
+ var analysisDriver = request.analysisContext.driver;
var fsState = analysisDriver.fsState;
var filter = _buildFilter(fsState);
@@ -65,7 +64,7 @@
continue;
}
- var elementResult = await session.getLibraryByUri(file.uriStr);
+ var elementResult = await analysisSession.getLibraryByUri(file.uriStr);
if (elementResult is! LibraryElementResult) {
continue;
}
@@ -92,7 +91,7 @@
}
_Filter _buildFilter(FileSystemState fsState) {
- var file = fsState.getFileForPath(request.result.path);
+ var file = fsState.getFileForPath(request.path);
var workspacePackage = file.workspacePackage;
if (workspacePackage is PubWorkspacePackage) {
return _PubFilter(workspacePackage, file.path);
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index 1766158..8ffc7d4 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -738,8 +738,8 @@
Future<void> suggestOverride(SimpleIdentifier targetId,
ExecutableElement element, bool invokeSuper) async {
var displayTextBuffer = StringBuffer();
- var builder = ChangeBuilder(session: request.result.session);
- await builder.addDartFileEdit(request.result.path, (builder) {
+ var builder = ChangeBuilder(session: request.analysisSession);
+ await builder.addDartFileEdit(request.path, (builder) {
builder.addReplacement(range.node(targetId), (builder) {
builder.writeOverride(
element,
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/uri_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/uri_contributor.dart
index 703acf8..4a11a87 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/uri_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/uri_contributor.dart
@@ -57,7 +57,7 @@
// Quoted empty string
visitSimpleStringLiteral(uri);
} else {
- var data = request.sourceContents!;
+ var data = request.content;
if (end == data.length) {
var ch = data[end - 1];
if (ch != '"' && ch != "'") {
@@ -69,7 +69,7 @@
}
}
} else if (offset == start && offset == end) {
- var data = request.sourceContents!;
+ var data = request.content;
if (end == data.length) {
var ch = data[end - 1];
if (ch == '"' || ch == "'") {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
index 19d86b0..33d20fe 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/utilities.dart
@@ -197,7 +197,7 @@
}
String getRequestLineIndent(DartCompletionRequest request) {
- var content = request.result.content;
+ var content = request.content;
var lineStartOffset = request.offset;
var notWhitespaceOffset = request.offset;
for (; lineStartOffset > 0; lineStartOffset--) {
diff --git a/pkg/analysis_server/lib/src/socket_server.dart b/pkg/analysis_server/lib/src/socket_server.dart
index 5763cfe..8e938ef 100644
--- a/pkg/analysis_server/lib/src/socket_server.dart
+++ b/pkg/analysis_server/lib/src/socket_server.dart
@@ -10,6 +10,7 @@
import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
import 'package:analysis_server/src/server/detachable_filesystem_manager.dart';
import 'package:analysis_server/src/server/diagnostic_server.dart';
+import 'package:analysis_server/src/utilities/request_statistics.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/sdk.dart';
@@ -35,6 +36,7 @@
final CrashReportingAttachmentsBuilder crashReportingAttachmentsBuilder;
final InstrumentationService instrumentationService;
+ final RequestStatisticsHelper? requestStatistics;
@override
final DiagnosticServer? diagnosticServer;
final DetachableFileSystemManager? detachableFileSystemManager;
@@ -49,6 +51,7 @@
this.sdkManager,
this.crashReportingAttachmentsBuilder,
this.instrumentationService,
+ this.requestStatistics,
this.diagnosticServer,
this.detachableFileSystemManager);
@@ -59,7 +62,7 @@
var error = RequestError(
RequestErrorCode.SERVER_ALREADY_STARTED, 'Server already started');
serverChannel.sendResponse(Response('', error: error));
- serverChannel.listen((Request request) {
+ serverChannel.requests.listen((Request request) {
serverChannel.sendResponse(Response(request.id, error: error));
});
return;
@@ -75,6 +78,7 @@
sdkManager,
crashReportingAttachmentsBuilder,
instrumentationService,
+ requestStatistics: requestStatistics,
diagnosticServer: diagnosticServer,
detachableFileSystemManager: detachableFileSystemManager,
enableBazelWatcher: true,
diff --git a/pkg/analysis_server/lib/src/utilities/mocks.dart b/pkg/analysis_server/lib/src/utilities/mocks.dart
index 9c581cd..bef5102 100644
--- a/pkg/analysis_server/lib/src/utilities/mocks.dart
+++ b/pkg/analysis_server/lib/src/utilities/mocks.dart
@@ -36,6 +36,9 @@
MockServerChannel();
@override
+ Stream<Request> get requests => requestController.stream;
+
+ @override
void close() {
_closed = true;
}
@@ -46,13 +49,6 @@
}
@override
- void listen(void Function(Request request) onRequest,
- {Function? onError, void Function()? onDone}) {
- requestController.stream
- .listen(onRequest, onError: onError, onDone: onDone);
- }
-
- @override
void sendNotification(Notification notification) {
// Don't deliver notifications after the connection is closed.
if (_closed) {
diff --git a/pkg/analysis_server/lib/src/utilities/request_statistics.dart b/pkg/analysis_server/lib/src/utilities/request_statistics.dart
new file mode 100644
index 0000000..7cdf234
--- /dev/null
+++ b/pkg/analysis_server/lib/src/utilities/request_statistics.dart
@@ -0,0 +1,256 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/channel/byte_stream_channel.dart';
+
+/// Helper for tracking request handling statistics.
+///
+/// All [DateTime] are local, not UTC.
+class RequestStatisticsHelper {
+ final String _sdkVersion = Platform.version.split(' ').first;
+
+ final Map<String, _RequestStatistics> _statisticsMap = {};
+
+ /// The [StringSink] to which performance logger should copy its output.
+ late final _ServerLogStringSink _perfLoggerStringSink =
+ _ServerLogStringSink(this);
+
+ /// The channel to send 'server.log' notifications to.
+ ByteStreamServerChannel? _serverChannel;
+
+ /// Is `true` if the client subscribed for "server.log" notification.
+ bool _isNotificationSubscribed = false;
+
+ RequestStatisticsHelper();
+
+ /// Set whether the client subscribed for "server.log" notification.
+ set isNotificationSubscribed(bool value) {
+ _isNotificationSubscribed = value;
+ }
+
+ /// The [StringSink] to which performance logger should copy its output.
+ StringSink get perfLoggerStringSink => _perfLoggerStringSink;
+
+ /// The channel sets itself using this method.
+ set serverChannel(ByteStreamServerChannel serverChannel) {
+ _serverChannel = serverChannel;
+ }
+
+ /// Add a time marker item to the data associated with the [request].
+ void addItemTimeNow(Request request, String name) {
+ var id = request.id;
+ var stat = _statisticsMap[id];
+ if (stat != null) {
+ stat.items.add(
+ _RequestStatisticsItem(
+ name,
+ timeValue: DateTime.now(),
+ ),
+ );
+ }
+ }
+
+ /// The new [request] was received. Record the time when the client sent it,
+ /// and the time when the server received it (now).
+ void addRequest(Request request) {
+ _logRequest(request);
+
+ var id = request.id;
+
+ var clientRequestMilliseconds = request.clientRequestTime;
+ if (clientRequestMilliseconds == null) {
+ return;
+ }
+ var clientRequestTime = DateTime.fromMillisecondsSinceEpoch(
+ clientRequestMilliseconds,
+ );
+
+ var serverRequestTime = DateTime.now();
+
+ _statisticsMap[id] = _RequestStatistics(
+ id,
+ request.method,
+ clientRequestTime,
+ serverRequestTime,
+ );
+ }
+
+ /// The server finished processing a request, and sends the [response].
+ /// Record the time when the response is about to be sent to the client.
+ void addResponse(Response response) {
+ if (!_isNotificationSubscribed) return;
+ if (_serverChannel == null) return;
+
+ var id = response.id;
+ var stat = _statisticsMap.remove(id);
+ if (stat != null) {
+ var responseTime = DateTime.now();
+ _sendLogEntry(ServerLogEntryKind.RESPONSE, stat.toJson(responseTime));
+ }
+ }
+
+ void logNotification(Notification notification) {
+ if (!_isNotificationSubscribed) return;
+ if (_serverChannel == null) return;
+
+ var event = notification.event;
+
+ // Don't log large and often notifications.
+ if (event == 'analysis.errors' ||
+ event == 'completion.availableSuggestions') {
+ return;
+ }
+
+ var params = notification.params;
+ if (params == null) {
+ return;
+ }
+
+ var map = <String, Object>{
+ 'event': event,
+ };
+
+ if (event == 'analysis.highlights' ||
+ event == 'analysis.implemented' ||
+ event == 'analysis.navigation' ||
+ event == 'analysis.outline' ||
+ event == 'analysis.overrides') {
+ map['file'] = params['file'] as String;
+ }
+
+ if (event == 'server.status') {
+ var analysis = params['analysis'];
+ if (analysis is Map<String, Object?>) {
+ map['isAnalyzing'] = analysis['isAnalyzing'] as bool;
+ }
+ }
+
+ _sendLogEntry(ServerLogEntryKind.NOTIFICATION, map);
+ }
+
+ void _logRequest(Request request) {
+ if (!_isNotificationSubscribed) return;
+ if (_serverChannel == null) return;
+
+ var method = request.method;
+ var map = <String, Object>{
+ 'id': request.id,
+ 'method': method,
+ };
+
+ {
+ var clientRequestTime = request.clientRequestTime;
+ if (clientRequestTime != null) {
+ map['clientRequestTime'] = clientRequestTime;
+ }
+ }
+
+ if (method == 'analysis.updateContent') {
+ var filesMap = request.params['files'];
+ if (filesMap is Map<String, Object>) {
+ map['files'] = filesMap.keys.toList();
+ }
+ } else {
+ map = request.toJson();
+ }
+
+ _sendLogEntry(ServerLogEntryKind.REQUEST, map);
+ }
+
+ void _sendLogEntry(ServerLogEntryKind kind, Object data) {
+ if (!_isNotificationSubscribed) return;
+
+ var serverChannel = _serverChannel;
+ if (serverChannel == null) return;
+
+ serverChannel.sendNotification(
+ Notification(
+ 'server.log',
+ <String, Object>{
+ 'time': DateTime.now().millisecondsSinceEpoch,
+ 'kind': kind.toJson(),
+ 'data': data,
+ 'sdkVersion': _sdkVersion,
+ },
+ ),
+ );
+ }
+}
+
+class _RequestStatistics {
+ final String id;
+ final String method;
+ final DateTime clientRequestTime;
+ final DateTime serverRequestTime;
+ final List<_RequestStatisticsItem> items = [];
+
+ _RequestStatistics(
+ this.id,
+ this.method,
+ this.clientRequestTime,
+ this.serverRequestTime,
+ );
+
+ Map<String, Object> toJson(DateTime responseTime) {
+ var map = {
+ 'id': id,
+ 'method': method,
+ 'clientRequestTime': clientRequestTime.millisecondsSinceEpoch,
+ 'serverRequestTime': serverRequestTime.millisecondsSinceEpoch,
+ 'responseTime': responseTime.millisecondsSinceEpoch,
+ };
+ if (items.isNotEmpty) {
+ map['items'] = items.map((item) => item.toJson()).toList();
+ }
+ return map;
+ }
+}
+
+class _RequestStatisticsItem {
+ final String name;
+ final DateTime? timeValue;
+
+ _RequestStatisticsItem(this.name, {this.timeValue});
+
+ Map<String, Object> toJson() {
+ final timeValue = this.timeValue;
+ if (timeValue != null) {
+ return {
+ 'name': name,
+ 'timeValue': timeValue.millisecondsSinceEpoch,
+ };
+ }
+ throw StateError('Unknown value: $name');
+ }
+}
+
+class _ServerLogStringSink implements StringSink {
+ final RequestStatisticsHelper helper;
+
+ _ServerLogStringSink(this.helper);
+
+ @override
+ void write(Object? obj) {
+ throw UnimplementedError();
+ }
+
+ @override
+ void writeAll(Iterable objects, [String separator = '']) {
+ throw UnimplementedError();
+ }
+
+ @override
+ void writeCharCode(int charCode) {
+ throw UnimplementedError();
+ }
+
+ @override
+ void writeln([Object? obj = '']) {
+ helper._sendLogEntry(ServerLogEntryKind.RAW, '$obj');
+ }
+}
diff --git a/pkg/analysis_server/lib/src/utilities/tee_string_sink.dart b/pkg/analysis_server/lib/src/utilities/tee_string_sink.dart
new file mode 100644
index 0000000..2b715c2
--- /dev/null
+++ b/pkg/analysis_server/lib/src/utilities/tee_string_sink.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A [StringSink] that writes into two other [StringSink]s.
+class TeeStringSink implements StringSink {
+ final StringSink sink1;
+ final StringSink sink2;
+
+ TeeStringSink(this.sink1, this.sink2);
+
+ @override
+ void write(Object? obj) {
+ sink1.write(obj);
+ sink2.write(obj);
+ }
+
+ @override
+ void writeAll(Iterable<dynamic> objects, [String separator = '']) {
+ sink1.writeAll(objects, separator);
+ sink2.writeAll(objects, separator);
+ }
+
+ @override
+ void writeCharCode(int charCode) {
+ sink1.writeCharCode(charCode);
+ sink2.writeCharCode(charCode);
+ }
+
+ @override
+ void writeln([Object? obj = '']) {
+ sink1.writeln(obj);
+ sink2.writeln(obj);
+ }
+}
diff --git a/pkg/analysis_server/test/channel/byte_stream_channel_test.dart b/pkg/analysis_server/test/channel/byte_stream_channel_test.dart
index 0724bfe..d89741d 100644
--- a/pkg/analysis_server/test/channel/byte_stream_channel_test.dart
+++ b/pkg/analysis_server/test/channel/byte_stream_channel_test.dart
@@ -135,7 +135,7 @@
errorStream = errorStreamController.stream;
var doneCompleter = Completer();
doneFuture = doneCompleter.future;
- channel.listen((Request request) {
+ channel.requests.listen((Request request) {
requestStreamController.add(request);
}, onError: (error) {
errorStreamController.add(error);
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 30d1d34..226a8b8 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -228,6 +228,73 @@
NotImportedContributor.onFile = null;
}
+ Future<void> test_abort_onAnotherCompletionRequest() async {
+ var abortedIdSet = <String>{};
+ server.completionRequestAborting.onAbort = (idSet) {
+ abortedIdSet.addAll(idSet);
+ };
+
+ newFile(testFilePath, content: '');
+
+ await _configureWithWorkspaceRoot();
+
+ // Send three requests, the first two should be aborted.
+ var response0 = _sendTestCompletionRequest('0', 0);
+ var response1 = _sendTestCompletionRequest('1', 0);
+ var response2 = _sendTestCompletionRequest('2', 0);
+
+ // Wait for all three.
+ var validator0 = await response0.toResult();
+ var validator1 = await response1.toResult();
+ var validator2 = await response2.toResult();
+
+ // The first two should be aborted.
+ expect(abortedIdSet, {'0', '1'});
+
+ validator0
+ ..assertIncomplete()
+ ..suggestions.assertEmpty();
+
+ validator1
+ ..assertIncomplete()
+ ..suggestions.assertEmpty();
+
+ validator2
+ ..assertComplete()
+ ..suggestions.assertCompletionsContainsAll(
+ ['int', 'double', 'Future', 'Directory'],
+ );
+ }
+
+ Future<void> test_abort_onUpdateContent() async {
+ var abortedIdSet = <String>{};
+ server.completionRequestAborting.onAbort = (idSet) {
+ abortedIdSet.addAll(idSet);
+ };
+
+ newFile(testFilePath, content: '');
+
+ await _configureWithWorkspaceRoot();
+
+ // Schedule a completion request.
+ var response = _sendTestCompletionRequest('0', 0);
+
+ // Simulate typing in the IDE.
+ await _handleSuccessfulRequest(
+ AnalysisUpdateContentParams({
+ testFilePathPlatform: AddContentOverlay('void f() {}'),
+ }).toRequest('1'),
+ );
+
+ // The request should be aborted.
+ var validator = await response.toResult();
+ expect(abortedIdSet, {'0'});
+
+ validator
+ ..assertIncomplete()
+ ..suggestions.assertEmpty();
+ }
+
Future<void> test_notImported_abort() async {
await _configureWithWorkspaceRoot();
@@ -1529,6 +1596,16 @@
maxResults: maxResults,
);
}
+
+ RequestWithFutureResponse _sendTestCompletionRequest(String id, int offset) {
+ var request = CompletionGetSuggestions2Params(
+ testFilePath,
+ 0,
+ 1 << 10,
+ ).toRequest(id);
+ var futureResponse = _handleRequest(request);
+ return RequestWithFutureResponse(offset, request, futureResponse);
+ }
}
@reflectiveTest
@@ -2597,6 +2674,21 @@
}
}
+class RequestWithFutureResponse {
+ final int offset;
+ final Request request;
+ final Future<Response> futureResponse;
+
+ RequestWithFutureResponse(this.offset, this.request, this.futureResponse);
+
+ Future<CompletionGetSuggestions2ResponseValidator> toResult() async {
+ var response = await futureResponse;
+ expect(response, isResponseSuccess(request.id));
+ var result = CompletionGetSuggestions2Result.fromResponse(response);
+ return CompletionGetSuggestions2ResponseValidator(offset, result);
+ }
+}
+
class SingleSuggestionValidator {
final CompletionSuggestion suggestion;
final List<String>? libraryUrisToImport;
diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
index b84f147b..9a707cf 100644
--- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
@@ -112,6 +112,16 @@
/// Stream controller for [onServerError].
late StreamController<ServerErrorParams> _onServerError;
+ /// The stream of entries describing events happened in the server.
+ ///
+ /// Parameters
+ ///
+ /// entry: ServerLogEntry
+ late Stream<ServerLogParams> onServerLog;
+
+ /// Stream controller for [onServerLog].
+ late StreamController<ServerLogParams> _onServerLog;
+
/// Reports the current status of the server. Parameters are omitted if there
/// has been no change in the status represented by that parameter.
///
@@ -2576,6 +2586,8 @@
onServerConnected = _onServerConnected.stream.asBroadcastStream();
_onServerError = StreamController<ServerErrorParams>(sync: true);
onServerError = _onServerError.stream.asBroadcastStream();
+ _onServerLog = StreamController<ServerLogParams>(sync: true);
+ onServerLog = _onServerLog.stream.asBroadcastStream();
_onServerStatus = StreamController<ServerStatusParams>(sync: true);
onServerStatus = _onServerStatus.stream.asBroadcastStream();
_onAnalysisAnalyzedFiles =
@@ -2648,6 +2660,10 @@
_onServerError
.add(ServerErrorParams.fromJson(decoder, 'params', params));
break;
+ case 'server.log':
+ outOfTestExpect(params, isServerLogParams);
+ _onServerLog.add(ServerLogParams.fromJson(decoder, 'params', params));
+ break;
case 'server.status':
outOfTestExpect(params, isServerStatusParams);
_onServerStatus
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index ae6e670..5fd8c3c 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -1559,12 +1559,35 @@
'WRITE'
]);
+/// ServerLogEntry
+///
+/// {
+/// "time": int
+/// "kind": ServerLogEntryKind
+/// "data": String
+/// }
+final Matcher isServerLogEntry = LazyMatcher(() => MatchesJsonObject(
+ 'ServerLogEntry',
+ {'time': isInt, 'kind': isServerLogEntryKind, 'data': isString}));
+
+/// ServerLogEntryKind
+///
+/// enum {
+/// NOTIFICATION
+/// RAW
+/// REQUEST
+/// RESPONSE
+/// }
+final Matcher isServerLogEntryKind = MatchesEnum(
+ 'ServerLogEntryKind', ['NOTIFICATION', 'RAW', 'REQUEST', 'RESPONSE']);
+
/// ServerService
///
/// enum {
+/// LOG
/// STATUS
/// }
-final Matcher isServerService = MatchesEnum('ServerService', ['STATUS']);
+final Matcher isServerService = MatchesEnum('ServerService', ['LOG', 'STATUS']);
/// SourceChange
///
@@ -3021,6 +3044,14 @@
final Matcher isServerGetVersionResult = LazyMatcher(
() => MatchesJsonObject('server.getVersion result', {'version': isString}));
+/// server.log params
+///
+/// {
+/// "entry": ServerLogEntry
+/// }
+final Matcher isServerLogParams = LazyMatcher(
+ () => MatchesJsonObject('server.log params', {'entry': isServerLogEntry}));
+
/// server.setSubscriptions params
///
/// {
diff --git a/pkg/analysis_server/test/socket_server_test.dart b/pkg/analysis_server/test/socket_server_test.dart
index e82b6b8..1c7bb82 100644
--- a/pkg/analysis_server/test/socket_server_test.dart
+++ b/pkg/analysis_server/test/socket_server_test.dart
@@ -101,13 +101,13 @@
static SocketServer _createSocketServer(MockServerChannel channel) {
final errorNotifier = ErrorNotifier();
final server = SocketServer(
- AnalysisServerOptions(),
- DartSdkManager(''),
- CrashReportingAttachmentsBuilder.empty,
- errorNotifier,
- null,
- null,
- );
+ AnalysisServerOptions(),
+ DartSdkManager(''),
+ CrashReportingAttachmentsBuilder.empty,
+ errorNotifier,
+ null,
+ null,
+ null);
server.createAnalysisServer(channel);
errorNotifier.server = server.analysisServer;
diff --git a/pkg/analysis_server/test/src/plugin/notification_manager_test.dart b/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
index 0f7483d..5504afb 100644
--- a/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
+++ b/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
@@ -468,23 +468,10 @@
server.Notification? sentNotification;
@override
- void close() {
- fail('Unexpected invocation of close');
- }
-
- @override
- void listen(void Function(server.Request) onRequest,
- {Function? onError, void Function()? onDone}) {
- fail('Unexpected invocation of listen');
- }
+ dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@override
void sendNotification(server.Notification notification) {
sentNotification = notification;
}
-
- @override
- void sendResponse(server.Response response) {
- fail('Unexpected invocation of sendResponse');
- }
}
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index 9b76591..b57a1f8 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -1226,7 +1226,7 @@
listener: listener,
).computeSuggestions(dartRequest, performance);
- computeIncludedSetList(declarationsTracker, dartRequest.result,
+ computeIncludedSetList(declarationsTracker, dartRequest,
includedSuggestionSetList, includedElementNames);
var includedSuggestionSetMap = {
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/ServerLogEntry.java b/pkg/analysis_server/tool/spec/generated/java/types/ServerLogEntry.java
new file mode 100644
index 0000000..c590fe5
--- /dev/null
+++ b/pkg/analysis_server/tool/spec/generated/java/types/ServerLogEntry.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ *
+ * This file has been automatically generated. Please do not edit it manually.
+ * To regenerate the file, use the script "pkg/analysis_server/tool/spec/generate_files".
+ */
+package org.dartlang.analysis.server.protocol;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import com.google.common.collect.Lists;
+import com.google.dart.server.utilities.general.JsonUtilities;
+import com.google.dart.server.utilities.general.ObjectUtilities;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * A log entry from the server.
+ *
+ * @coverage dart.server.generated.types
+ */
+@SuppressWarnings("unused")
+public class ServerLogEntry {
+
+ public static final ServerLogEntry[] EMPTY_ARRAY = new ServerLogEntry[0];
+
+ public static final List<ServerLogEntry> EMPTY_LIST = Lists.newArrayList();
+
+ /**
+ * The time (milliseconds since epoch) at which the server created this log entry.
+ */
+ private final int time;
+
+ /**
+ * The kind of the entry, used to determine how to interpret the "data" field.
+ */
+ private final String kind;
+
+ /**
+ * The payload of the entry, the actual format is determined by the "kind" field.
+ */
+ private final String data;
+
+ /**
+ * Constructor for {@link ServerLogEntry}.
+ */
+ public ServerLogEntry(int time, String kind, String data) {
+ this.time = time;
+ this.kind = kind;
+ this.data = data;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ServerLogEntry) {
+ ServerLogEntry other = (ServerLogEntry) obj;
+ return
+ other.time == time &&
+ ObjectUtilities.equals(other.kind, kind) &&
+ ObjectUtilities.equals(other.data, data);
+ }
+ return false;
+ }
+
+ public static ServerLogEntry fromJson(JsonObject jsonObject) {
+ int time = jsonObject.get("time").getAsInt();
+ String kind = jsonObject.get("kind").getAsString();
+ String data = jsonObject.get("data").getAsString();
+ return new ServerLogEntry(time, kind, data);
+ }
+
+ public static List<ServerLogEntry> fromJsonArray(JsonArray jsonArray) {
+ if (jsonArray == null) {
+ return EMPTY_LIST;
+ }
+ ArrayList<ServerLogEntry> list = new ArrayList<ServerLogEntry>(jsonArray.size());
+ Iterator<JsonElement> iterator = jsonArray.iterator();
+ while (iterator.hasNext()) {
+ list.add(fromJson(iterator.next().getAsJsonObject()));
+ }
+ return list;
+ }
+
+ /**
+ * The payload of the entry, the actual format is determined by the "kind" field.
+ */
+ public String getData() {
+ return data;
+ }
+
+ /**
+ * The kind of the entry, used to determine how to interpret the "data" field.
+ */
+ public String getKind() {
+ return kind;
+ }
+
+ /**
+ * The time (milliseconds since epoch) at which the server created this log entry.
+ */
+ public int getTime() {
+ return time;
+ }
+
+ @Override
+ public int hashCode() {
+ HashCodeBuilder builder = new HashCodeBuilder();
+ builder.append(time);
+ builder.append(kind);
+ builder.append(data);
+ return builder.toHashCode();
+ }
+
+ public JsonObject toJson() {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("time", time);
+ jsonObject.addProperty("kind", kind);
+ jsonObject.addProperty("data", data);
+ return jsonObject;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[");
+ builder.append("time=");
+ builder.append(time + ", ");
+ builder.append("kind=");
+ builder.append(kind + ", ");
+ builder.append("data=");
+ builder.append(data);
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/ServerLogEntryKind.java b/pkg/analysis_server/tool/spec/generated/java/types/ServerLogEntryKind.java
new file mode 100644
index 0000000..9964b6f
--- /dev/null
+++ b/pkg/analysis_server/tool/spec/generated/java/types/ServerLogEntryKind.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ *
+ * This file has been automatically generated. Please do not edit it manually.
+ * To regenerate the file, use the script "pkg/analysis_server/tool/spec/generate_files".
+ */
+package org.dartlang.analysis.server.protocol;
+
+/**
+ * An enumeration of the kinds of server long entries.
+ *
+ * @coverage dart.server.generated.types
+ */
+public class ServerLogEntryKind {
+
+ /**
+ * A notification from the server, such as "analysis.highlights". The "data" field contains a JSON
+ * object with abbreviated notification.
+ */
+ public static final String NOTIFICATION = "NOTIFICATION";
+
+ /**
+ * Arbitrary string, describing some event that happened in the server, e.g. starting a file
+ * analysis, and details which files were accessed. These entries are not structured, but provide
+ * context information about requests and notification, and can be related by "time" for further
+ * manual analysis.
+ */
+ public static final String RAW = "RAW";
+
+ /**
+ * A request from the client, as the server views it, e.g. "edit.getAssists". The "data" field
+ * contains a JSON object with abbreviated request.
+ */
+ public static final String REQUEST = "REQUEST";
+
+ /**
+ * Various counters and measurements related to execution of a request. The "data" field contains a
+ * JSON object with following fields:
+ *
+ * - "id" - the id of the request - copied from the request.
+ * - "method" - the method of the request, e.g. "edit.getAssists".
+ * - "clientRequestTime" - the time (milliseconds since epoch) at which the client made the request
+ * - copied from the request.
+ * - "serverRequestTime" - the time (milliseconds since epoch) at which the server received and
+ * decoded the JSON request.
+ * - "responseTime" - the time (milliseconds since epoch) at which the server created the response
+ * to be encoded into JSON and sent to the client.
+ */
+ public static final String RESPONSE = "RESPONSE";
+
+}
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/ServerService.java b/pkg/analysis_server/tool/spec/generated/java/types/ServerService.java
index fed7a98..99f51b9 100644
--- a/pkg/analysis_server/tool/spec/generated/java/types/ServerService.java
+++ b/pkg/analysis_server/tool/spec/generated/java/types/ServerService.java
@@ -15,6 +15,8 @@
*/
public class ServerService {
+ public static final String LOG = "LOG";
+
public static final String STATUS = "STATUS";
}
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index cbbe829..aa08d74 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -354,6 +354,16 @@
</field>
</params>
</notification>
+ <notification event="log" experimental="true">
+ <p>
+ The stream of entries describing events happened in the server.
+ </p>
+ <params>
+ <field name="entry">
+ <ref>ServerLogEntry</ref>
+ </field>
+ </params>
+ </notification>
<notification event="status">
<p>
Reports the current status of the server. Parameters are
@@ -5314,6 +5324,7 @@
An enumeration of the services provided by the server domain.
</p>
<enum>
+ <value><code>LOG</code></value>
<value><code>STATUS</code></value>
</enum>
</type>
@@ -5388,6 +5399,94 @@
</field>
</object>
</type>
+ <type name="ServerLogEntry" experimental="true">
+ <p>
+ A log entry from the server.
+ </p>
+ <object>
+ <field name="time">
+ <ref>int</ref>
+ <p>
+ The time (milliseconds since epoch) at which the server created
+ this log entry.
+ </p>
+ </field>
+ <field name="kind" >
+ <ref>ServerLogEntryKind</ref>
+ <p>
+ The kind of the entry, used to determine how to interpret the "data"
+ field.
+ </p>
+ </field>
+ <field name="data">
+ <ref>String</ref>
+ <p>
+ The payload of the entry, the actual format is determined by the
+ "kind" field.
+ </p>
+ </field>
+ </object>
+ </type>
+ <type name="ServerLogEntryKind" experimental="true">
+ <p>
+ An enumeration of the kinds of server long entries.
+ </p>
+ <enum>
+ <value>
+ <code>NOTIFICATION</code>
+ <p>
+ A notification from the server, such as "analysis.highlights".
+ The "data" field contains a JSON object with abbreviated notification.
+ </p>
+ </value>
+ <value>
+ <code>RAW</code>
+ <p>
+ Arbitrary string, describing some event that happened in the server,
+ e.g. starting a file analysis, and details which files were accessed.
+ These entries are not structured, but provide context information
+ about requests and notification, and can be related by "time" for
+ further manual analysis.
+ </p>
+ </value>
+ <value>
+ <code>REQUEST</code>
+ <p>
+ A request from the client, as the server views it, e.g.
+ "edit.getAssists". The "data" field contains a JSON object with
+ abbreviated request.
+ </p>
+ </value>
+ <value>
+ <code>RESPONSE</code>
+ <p>
+ Various counters and measurements related to execution of a request.
+ The "data" field contains a JSON object with following fields:
+ </p>
+ <ul>
+ <li>
+ "id" - the id of the request - copied from the request.
+ </li>
+ <li>
+ "method" - the method of the request, e.g. "edit.getAssists".
+ </li>
+ <li>
+ "clientRequestTime" - the time (milliseconds since epoch) at which
+ the client made the request - copied from the request.
+ </li>
+ <li>
+ "serverRequestTime" - the time (milliseconds since epoch) at which
+ the server received and decoded the JSON request.
+ </li>
+ <li>
+ "responseTime" - the time (milliseconds since epoch) at which the
+ server created the response to be encoded into JSON and sent to the
+ client.
+ </li>
+ </ul>
+ </value>
+ </enum>
+ </type>
</types>
<refactorings>
<h2><a name="refactorings">Refactorings</a></h2>
diff --git a/pkg/analysis_server_client/lib/handler/notification_handler.dart b/pkg/analysis_server_client/lib/handler/notification_handler.dart
index f7ec39c..2d8715a 100644
--- a/pkg/analysis_server_client/lib/handler/notification_handler.dart
+++ b/pkg/analysis_server_client/lib/handler/notification_handler.dart
@@ -99,6 +99,9 @@
case SERVER_NOTIFICATION_ERROR:
onServerError(ServerErrorParams.fromJson(decoder, 'params', params));
break;
+ case SERVER_NOTIFICATION_LOG:
+ onServerLog(ServerLogParams.fromJson(decoder, 'params', params));
+ break;
case SERVER_NOTIFICATION_STATUS:
onServerStatus(ServerStatusParams.fromJson(decoder, 'params', params));
break;
@@ -276,6 +279,9 @@
/// notification.
void onServerError(ServerErrorParams params) {}
+ /// The stream of entries describing events happened in the server.
+ void onServerLog(ServerLogParams params) {}
+
/// Reports the current status of the server. Parameters are
/// omitted if there has been no change in the status
/// represented by that parameter.
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
index fd9998b..2689506 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
@@ -350,6 +350,8 @@
const String SERVER_NOTIFICATION_ERROR_IS_FATAL = 'isFatal';
const String SERVER_NOTIFICATION_ERROR_MESSAGE = 'message';
const String SERVER_NOTIFICATION_ERROR_STACK_TRACE = 'stackTrace';
+const String SERVER_NOTIFICATION_LOG = 'server.log';
+const String SERVER_NOTIFICATION_LOG_ENTRY = 'entry';
const String SERVER_NOTIFICATION_STATUS = 'server.status';
const String SERVER_NOTIFICATION_STATUS_ANALYSIS = 'analysis';
const String SERVER_NOTIFICATION_STATUS_PUB = 'pub';
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
index ad1ed76..2dcbd32 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
@@ -15871,18 +15871,249 @@
int get hashCode => version.hashCode;
}
+/// ServerLogEntry
+///
+/// {
+/// "time": int
+/// "kind": ServerLogEntryKind
+/// "data": String
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class ServerLogEntry implements HasToJson {
+ /// The time (milliseconds since epoch) at which the server created this log
+ /// entry.
+ int time;
+
+ /// The kind of the entry, used to determine how to interpret the "data"
+ /// field.
+ ServerLogEntryKind kind;
+
+ /// The payload of the entry, the actual format is determined by the "kind"
+ /// field.
+ String data;
+
+ ServerLogEntry(this.time, this.kind, this.data);
+
+ factory ServerLogEntry.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ int time;
+ if (json.containsKey('time')) {
+ time = jsonDecoder.decodeInt(jsonPath + '.time', json['time']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'time');
+ }
+ ServerLogEntryKind kind;
+ if (json.containsKey('kind')) {
+ kind = ServerLogEntryKind.fromJson(
+ jsonDecoder, jsonPath + '.kind', json['kind']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'kind');
+ }
+ String data;
+ if (json.containsKey('data')) {
+ data = jsonDecoder.decodeString(jsonPath + '.data', json['data']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'data');
+ }
+ return ServerLogEntry(time, kind, data);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'ServerLogEntry', json);
+ }
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['time'] = time;
+ result['kind'] = kind.toJson();
+ result['data'] = data;
+ return result;
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is ServerLogEntry) {
+ return time == other.time && kind == other.kind && data == other.data;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ time,
+ kind,
+ data,
+ );
+}
+
+/// ServerLogEntryKind
+///
+/// enum {
+/// NOTIFICATION
+/// RAW
+/// REQUEST
+/// RESPONSE
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class ServerLogEntryKind implements Enum {
+ /// A notification from the server, such as "analysis.highlights". The "data"
+ /// field contains a JSON object with abbreviated notification.
+ static const ServerLogEntryKind NOTIFICATION =
+ ServerLogEntryKind._('NOTIFICATION');
+
+ /// Arbitrary string, describing some event that happened in the server, e.g.
+ /// starting a file analysis, and details which files were accessed. These
+ /// entries are not structured, but provide context information about
+ /// requests and notification, and can be related by "time" for further
+ /// manual analysis.
+ static const ServerLogEntryKind RAW = ServerLogEntryKind._('RAW');
+
+ /// A request from the client, as the server views it, e.g.
+ /// "edit.getAssists". The "data" field contains a JSON object with
+ /// abbreviated request.
+ static const ServerLogEntryKind REQUEST = ServerLogEntryKind._('REQUEST');
+
+ /// Various counters and measurements related to execution of a request. The
+ /// "data" field contains a JSON object with following fields:
+ ///
+ /// - "id" - the id of the request - copied from the request.
+ /// - "method" - the method of the request, e.g. "edit.getAssists".
+ /// - "clientRequestTime" - the time (milliseconds since epoch) at which the
+ /// client made the request - copied from the request.
+ /// - "serverRequestTime" - the time (milliseconds since epoch) at which the
+ /// server received and decoded the JSON request.
+ /// - "responseTime" - the time (milliseconds since epoch) at which the
+ /// server created the response to be encoded into JSON and sent to the
+ /// client.
+ static const ServerLogEntryKind RESPONSE = ServerLogEntryKind._('RESPONSE');
+
+ /// A list containing all of the enum values that are defined.
+ static const List<ServerLogEntryKind> VALUES = <ServerLogEntryKind>[
+ NOTIFICATION,
+ RAW,
+ REQUEST,
+ RESPONSE
+ ];
+
+ @override
+ final String name;
+
+ const ServerLogEntryKind._(this.name);
+
+ factory ServerLogEntryKind(String name) {
+ switch (name) {
+ case 'NOTIFICATION':
+ return NOTIFICATION;
+ case 'RAW':
+ return RAW;
+ case 'REQUEST':
+ return REQUEST;
+ case 'RESPONSE':
+ return RESPONSE;
+ }
+ throw Exception('Illegal enum value: $name');
+ }
+
+ factory ServerLogEntryKind.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ if (json is String) {
+ try {
+ return ServerLogEntryKind(json);
+ } catch (_) {
+ // Fall through
+ }
+ }
+ throw jsonDecoder.mismatch(jsonPath, 'ServerLogEntryKind', json);
+ }
+
+ @override
+ String toString() => 'ServerLogEntryKind.$name';
+
+ String toJson() => name;
+}
+
+/// server.log params
+///
+/// {
+/// "entry": ServerLogEntry
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class ServerLogParams implements HasToJson {
+ ServerLogEntry entry;
+
+ ServerLogParams(this.entry);
+
+ factory ServerLogParams.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ ServerLogEntry entry;
+ if (json.containsKey('entry')) {
+ entry = ServerLogEntry.fromJson(
+ jsonDecoder, jsonPath + '.entry', json['entry']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'entry');
+ }
+ return ServerLogParams(entry);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'server.log params', json);
+ }
+ }
+
+ factory ServerLogParams.fromNotification(Notification notification) {
+ return ServerLogParams.fromJson(
+ ResponseDecoder(null), 'params', notification.params);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['entry'] = entry.toJson();
+ return result;
+ }
+
+ Notification toNotification() {
+ return Notification('server.log', toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is ServerLogParams) {
+ return entry == other.entry;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => entry.hashCode;
+}
+
/// ServerService
///
/// enum {
+/// LOG
/// STATUS
/// }
///
/// Clients may not extend, implement or mix-in this class.
class ServerService implements Enum {
+ static const ServerService LOG = ServerService._('LOG');
+
static const ServerService STATUS = ServerService._('STATUS');
/// A list containing all of the enum values that are defined.
- static const List<ServerService> VALUES = <ServerService>[STATUS];
+ static const List<ServerService> VALUES = <ServerService>[LOG, STATUS];
@override
final String name;
@@ -15891,6 +16122,8 @@
factory ServerService(String name) {
switch (name) {
+ case 'LOG':
+ return LOG;
case 'STATUS':
return STATUS;
}
diff --git a/tests/standalone/io/directory_rename_test.dart b/tests/standalone/io/directory_rename_test.dart
new file mode 100644
index 0000000..9d31374
--- /dev/null
+++ b/tests/standalone/io/directory_rename_test.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Directory rename test.
+
+import "dart:io";
+
+import "package:expect/expect.dart";
+import 'test_utils.dart' show withTempDir;
+
+testRenameToNewPath() async {
+ await withTempDir('testRenameToNewPath', (Directory tempDir) async {
+ final dir1 = Directory("${tempDir.path}/dir1");
+ dir1.createSync();
+
+ dir1.renameSync("${tempDir.path}/dir2");
+ Expect.isTrue(Directory("${tempDir.path}/dir2").existsSync());
+ });
+}
+
+testRenameDoesNotAdjustPath() async {
+ await withTempDir('testRenameToNewPath', (Directory tempDir) async {
+ final dir1 = Directory("${tempDir.path}/dir1");
+ dir1.createSync();
+ final originalPath = dir1.path;
+
+ dir1.renameSync("${tempDir.path}/dir2");
+ final finalPath = dir1.path;
+ Expect.isTrue(originalPath == finalPath,
+ "$originalPath != $finalPath - path should not be updated");
+ });
+}
+
+testRenameToSamePath() async {
+ await withTempDir('testRenameToSamePath', (Directory tempDir) async {
+ final dir = Directory("${tempDir.path}/dir");
+ dir.createSync();
+ final file = File("${dir.path}/file");
+ file.createSync();
+
+ try {
+ dir.renameSync(dir.path);
+ if (Platform.isWindows) {
+ Expect.fail('Directory.rename to same path should fail on Windows');
+ } else {
+ Expect.isTrue(file.existsSync());
+ }
+ } on FileSystemException catch (e) {
+ if (Platform.isWindows) {
+ // On Windows, the directory will be *deleted*.
+ Expect.isFalse(dir.existsSync());
+ Expect.isTrue(
+ e.osError!.message.contains('cannot find the file specified'));
+ } else {
+ Expect.fail('Directory.rename to same path should not fail on '
+ '${Platform.operatingSystem} (${Platform.operatingSystemVersion}): '
+ '$e');
+ }
+ }
+ });
+}
+
+testRenameToExistingFile() async {
+ await withTempDir('testRenameToExistingFile', (Directory tempDir) async {
+ final dir = Directory("${tempDir.path}/dir");
+ dir.createSync();
+ final file = File("${tempDir.path}/file");
+ file.createSync();
+
+ // Overwriting an exsting file is not allowed.
+ try {
+ dir.renameSync(file.path);
+ Expect.fail('Directory.rename should fail to rename a non-directory');
+ } on FileSystemException catch (e) {
+ if (Platform.isLinux || Platform.isMacOS) {
+ Expect.isTrue(e.osError!.message.contains('Not a directory'));
+ } else if (Platform.isWindows) {
+ Expect.isTrue(e.osError!.message.contains('file already exists'));
+ }
+ }
+ });
+}
+
+testRenameToExistingEmptyDirectory() async {
+ await withTempDir('testRenameToExistingEmptyDirectory',
+ (Directory tempDir) async {
+ final dir1 = Directory("${tempDir.path}/dir1");
+ dir1.createSync();
+ File("${dir1.path}/file").createSync();
+
+ final dir2 = Directory("${tempDir.path}/dir2");
+ dir2.createSync();
+
+ dir1.renameSync(dir2.path);
+ // Verify that the file contained in dir1 have been moved.
+ Expect.isTrue(File("${dir2.path}/file").existsSync());
+ });
+}
+
+testRenameToExistingNonEmptyDirectory() async {
+ await withTempDir('testRenameToExistingNonEmptyDirectory',
+ (Directory tempDir) async {
+ final dir1 = Directory("${tempDir.path}/dir1");
+ dir1.createSync();
+ File("${dir1.path}/file1").createSync();
+
+ final dir2 = Directory("${tempDir.path}/dir2");
+ dir2.createSync();
+ File("${dir2.path}/file2").createSync();
+
+ try {
+ dir1.renameSync(dir2.path);
+ if (Platform.isWindows) {
+ // Verify that the old directory is deleted.
+ Expect.isTrue(File("${dir2.path}/file1").existsSync());
+ Expect.isFalse(File("${dir2.path}/file2").existsSync());
+ } else {
+ Expect.fail(
+ 'Directory.rename should fail to rename a non-empty directory '
+ 'except on Windows');
+ }
+ } on FileSystemException catch (e) {
+ if (Platform.isLinux || Platform.isMacOS) {
+ Expect.isTrue(e.osError!.message.contains('Directory not empty'));
+ }
+ }
+ });
+}
+
+main() async {
+ await testRenameToNewPath();
+ await testRenameDoesNotAdjustPath();
+ await testRenameToSamePath();
+ await testRenameToExistingFile();
+ await testRenameToExistingEmptyDirectory();
+ await testRenameToExistingNonEmptyDirectory();
+}
diff --git a/tests/standalone/io/directory_test.dart b/tests/standalone/io/directory_test.dart
index 5c39405..c6f6e81 100644
--- a/tests/standalone/io/directory_test.dart
+++ b/tests/standalone/io/directory_test.dart
@@ -582,36 +582,6 @@
});
}
-testRename() {
- var temp1 = Directory.systemTemp.createTempSync('directory_test');
- var temp2 = Directory.systemTemp.createTempSync('directory_test');
- var temp3 = temp1.renameSync(temp2.path);
- Expect.isFalse(temp1.existsSync());
- Expect.isTrue(temp2.existsSync());
- Expect.equals(temp3.path, temp2.path);
-
- var temp4 = temp2.renameSync(temp1.path);
- Expect.isFalse(temp3.existsSync());
- Expect.isFalse(temp2.existsSync());
- Expect.isTrue(temp1.existsSync());
- Expect.isTrue(temp4.existsSync());
- Expect.equals(temp1.path, temp4.path);
-
- String foo = '${temp4.path}/foo';
- String bar = '${temp4.path}/bar';
- new File(foo).createSync();
- try {
- new Directory(foo).renameSync(bar);
- Expect.fail('Directory.rename should fail to rename a non-directory');
- } on FileSystemException catch (e) {
- if (Platform.isLinux || Platform.isMacOS) {
- Expect.isTrue(e.osError!.message.contains('Not a directory'));
- }
- }
-
- temp1.deleteSync(recursive: true);
-}
-
main() {
DirectoryTest.testMain();
NestedTempDirectoryTest.testMain();
@@ -621,5 +591,4 @@
testCreateExisting();
testCreateDirExistingFileSync();
testCreateDirExistingFile();
- testRename();
}
diff --git a/tests/standalone_2/io/directory_rename_test.dart b/tests/standalone_2/io/directory_rename_test.dart
new file mode 100644
index 0000000..e943a8a
--- /dev/null
+++ b/tests/standalone_2/io/directory_rename_test.dart
@@ -0,0 +1,140 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Directory rename test.
+
+// @dart = 2.9
+
+import "dart:io";
+
+import "package:expect/expect.dart";
+import 'test_utils.dart' show withTempDir;
+
+testRenameToNewPath() async {
+ await withTempDir('testRenameToNewPath', (Directory tempDir) async {
+ final dir1 = Directory("${tempDir.path}/dir1");
+ dir1.createSync();
+
+ dir1.renameSync("${tempDir.path}/dir2");
+ Expect.isTrue(Directory("${tempDir.path}/dir2").existsSync());
+ });
+}
+
+testRenameDoesNotAdjustPath() async {
+ await withTempDir('testRenameToNewPath', (Directory tempDir) async {
+ final dir1 = Directory("${tempDir.path}/dir1");
+ dir1.createSync();
+ final originalPath = dir1.path;
+
+ dir1.renameSync("${tempDir.path}/dir2");
+ final finalPath = dir1.path;
+ Expect.isTrue(originalPath == finalPath,
+ "$originalPath != $finalPath - path should not be updated");
+ });
+}
+
+testRenameToSamePath() async {
+ await withTempDir('testRenameToSamePath', (Directory tempDir) async {
+ final dir = Directory("${tempDir.path}/dir");
+ dir.createSync();
+ final file = File("${dir.path}/file");
+ file.createSync();
+
+ try {
+ dir.renameSync(dir.path);
+ if (Platform.isWindows) {
+ Expect.fail('Directory.rename to same path should fail on Windows');
+ } else {
+ Expect.isTrue(file.existsSync());
+ }
+ } on FileSystemException catch (e) {
+ if (Platform.isWindows) {
+ // On Windows, the directory will be *deleted*.
+ Expect.isFalse(dir.existsSync());
+ Expect.isTrue(
+ e.osError.message.contains('cannot find the file specified'));
+ } else {
+ Expect.fail('Directory.rename to same path should not fail on '
+ '${Platform.operatingSystem} (${Platform.operatingSystemVersion}): '
+ '$e');
+ }
+ }
+ });
+}
+
+testRenameToExistingFile() async {
+ await withTempDir('testRenameToExistingFile', (Directory tempDir) async {
+ final dir = Directory("${tempDir.path}/dir");
+ dir.createSync();
+ final file = File("${tempDir.path}/file");
+ file.createSync();
+
+ // Overwriting an exsting file is not allowed.
+ try {
+ dir.renameSync(file.path);
+ Expect.fail('Directory.rename should fail to rename a non-directory');
+ } on FileSystemException catch (e) {
+ if (Platform.isLinux || Platform.isMacOS) {
+ Expect.isTrue(e.osError.message.contains('Not a directory'));
+ } else if (Platform.isWindows) {
+ Expect.isTrue(e.osError.message.contains('file already exists'));
+ }
+ }
+ });
+}
+
+testRenameToExistingEmptyDirectory() async {
+ await withTempDir('testRenameToExistingEmptyDirectory',
+ (Directory tempDir) async {
+ final dir1 = Directory("${tempDir.path}/dir1");
+ dir1.createSync();
+ File("${dir1.path}/file").createSync();
+
+ final dir2 = Directory("${tempDir.path}/dir2");
+ dir2.createSync();
+
+ dir1.renameSync(dir2.path);
+ // Verify that the file contained in dir1 has been moved.
+ Expect.isTrue(File("${dir2.path}/file").existsSync());
+ });
+}
+
+testRenameToExistingNonEmptyDirectory() async {
+ await withTempDir('testRenameToExistingNonEmptyDirectory',
+ (Directory tempDir) async {
+ final dir1 = Directory("${tempDir.path}/dir1");
+ dir1.createSync();
+ File("${dir1.path}/file1").createSync();
+
+ final dir2 = Directory("${tempDir.path}/dir2");
+ dir2.createSync();
+ File("${dir2.path}/file2").createSync();
+
+ try {
+ dir1.renameSync(dir2.path);
+ if (Platform.isWindows) {
+ // Verify that the old directory is deleted.
+ Expect.isTrue(File("${dir2.path}/file1").existsSync());
+ Expect.isFalse(File("${dir2.path}/file2").existsSync());
+ } else {
+ Expect.fail(
+ 'Directory.rename should fail to rename a non-empty directory '
+ 'except on Windows');
+ }
+ } on FileSystemException catch (e) {
+ if (Platform.isLinux || Platform.isMacOS) {
+ Expect.isTrue(e.osError.message.contains('Directory not empty'));
+ }
+ }
+ });
+}
+
+main() async {
+ await testRenameToNewPath();
+ await testRenameDoesNotAdjustPath();
+ await testRenameToSamePath();
+ await testRenameToExistingFile();
+ await testRenameToExistingEmptyDirectory();
+ await testRenameToExistingNonEmptyDirectory();
+}
diff --git a/tests/standalone_2/io/directory_test.dart b/tests/standalone_2/io/directory_test.dart
index 91acf75..f0dad53 100644
--- a/tests/standalone_2/io/directory_test.dart
+++ b/tests/standalone_2/io/directory_test.dart
@@ -585,37 +585,6 @@
});
}
-testRename() {
- var temp1 = Directory.systemTemp.createTempSync('directory_test');
- var temp2 = Directory.systemTemp.createTempSync('directory_test');
- var temp3 = temp1.renameSync(temp2.path);
- Expect.isFalse(temp1.existsSync());
- Expect.isTrue(temp2.existsSync());
- Expect.equals(temp3.path, temp2.path);
-
- var temp4 = temp2.renameSync(temp1.path);
- Expect.isFalse(temp3.existsSync());
- Expect.isFalse(temp2.existsSync());
- Expect.isTrue(temp1.existsSync());
- Expect.isTrue(temp4.existsSync());
- Expect.equals(temp1.path, temp4.path);
-
- String foo = '${temp4.path}/foo';
- String bar = '${temp4.path}/bar';
- new File(foo).createSync();
- try {
- new Directory(foo).renameSync(bar);
- Expect.fail('Directory.rename should fail to rename a non-directory');
- } catch (e) {
- Expect.isTrue(e is FileSystemException);
- if (Platform.isLinux || Platform.isMacOS) {
- Expect.isTrue(e.osError.message.contains('Not a directory'));
- }
- }
-
- temp1.deleteSync(recursive: true);
-}
-
main() {
DirectoryTest.testMain();
NestedTempDirectoryTest.testMain();
@@ -625,5 +594,4 @@
testCreateExisting();
testCreateDirExistingFileSync();
testCreateDirExistingFile();
- testRename();
}
diff --git a/tools/VERSION b/tools/VERSION
index cdd0b9e..41c3212 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 289
+PRERELEASE 290
PRERELEASE_PATCH 0
\ No newline at end of file