iamcco / coc-flutter

flutter support for (Neo)vim
472 stars 38 forks source link

Parenthesis and literal elipsis are auto-completed in addition to a method name. #170

Open your-diary opened 1 year ago

your-diary commented 1 year ago

Describe the bug

When autocompletion, parenthesis and ellipsis are inserted in addition to a method name.

output

I've been a user of coc-flutter for months but this behavior suddenly appeared recently for some reason.

Expected: runApp Actual: runApp(...)

To Reproduce

  1. Create a project.

    $ flutter create abc
    $ cd abc
    $ vi lib/main.dart
  2. Type runA and press Ctrl+N to select the first candidate.

Expected behavior

runApp is inserted instead of runApp(...). Similarly, toString instead of toString(). (In other words, only method names should be printed. As noted earlier, until recently, this expected behavior had been observed.)

Desktop (please complete the following information):

Output channel:

  1. Set "flutter.trace.server": "verbose"
  2. Restart coc using :CocRestart
  3. Reproduce the issue
  4. :CocCommand workspace.showOutput open output list and select flutter

Paste Log from above:

[dev-command]: register dev command
[sdk]: No local fvm sdk
[sdk]: which flutter command => /opt/homebrew/bin/flutter
[sdk]: /opt/homebrew/Caskroom/flutter/3.7.1/flutter/version => 3.7.1
[sdk]: searchPaths: ~/snap/flutter/common/flutter
[sdk]: flutter command path => /opt/homebrew/Caskroom/flutter/3.7.1/flutter/bin/flutter
[sdk]: dart sdk home => /opt/homebrew/Caskroom/flutter/3.7.1/flutter/bin/cache/dart-sdk
[sdk]: analyzer path => /opt/homebrew/Caskroom/flutter/3.7.1/flutter/bin/cache/dart-sdk/bin/snapshots/analysis_server.dart.snapshot
[sdk]: dart command path => /opt/homebrew/Caskroom/flutter/3.7.1/flutter/bin/cache/dart-sdk/bin/dart
[fs]: executing command /opt/homebrew/Caskroom/flutter/3.7.1/flutter/bin/cache/dart-sdk/bin/dart --version
[sdk]: dart version: v2.19.1
[lsp-server]: rightVersion true
[lsp-server]: analysis server ready!
[lsp-closing-labels]: register closing labels
[daemon]: got message: {
  "event": "daemon.connected",
  "params": {
    "version": "0.6.1",
    "pid": 89841
  }
}
[daemon]: got message: {
  "event": "daemon.logMessage",
  "params": {
    "level": "status",
    "message": "Starting device daemon..."
  }
}
[daemon]: got message: {
  "id": 1
}
[daemon]: Message without event
[daemon]: got message: {
  "event": "device.added",
  "params": {
    "id": "macos",
    "name": "macOS",
    "platform": "darwin",
    "emulator": false,
    "category": "desktop",
    "platformType": "macos",
    "ephemeral": false,
    "emulatorId": null,
    "sdk": "macOS 13.2.1 22D68 darwin-arm64",
    "capabilities": {
      "hotReload": true,
      "hotRestart": true,
      "screenshot": false,
      "fastStart": false,
      "flutterExit": true,
      "hardwareRendering": true,
      "startPaused": true
    }
  }
}
[daemon]: New device: macOS
[daemon]: got message: {
  "event": "device.added",
  "params": {
    "id": "chrome",
    "name": "Chrome",
    "platform": "web-javascript",
    "emulator": false,
    "category": "web",
    "platformType": "web",
    "ephemeral": false,
    "emulatorId": null,
    "sdk": "Google Chrome 110.0.5481.177",
    "capabilities": {
      "hotReload": true,
      "hotRestart": true,
      "screenshot": false,
      "fastStart": false,
      "flutterExit": false,
      "hardwareRendering": true,
      "startPaused": true
    }
  }
}
[daemon]: New device: Chrome

Config

Here's the output of :CocConfig:

{
    "diagnostic.enable": true,
    "diagnostic.enableSign": false,
    "diagnostic.level": "error",
    "signature.target": "echo",
    "suggest.noselect": true,
    "notification.disabledProgressSources": [
        "*"
    ],
    "hover.target": "float",
    "coc.preferences.jumpCommand": "tab drop",
    "inlayHint.enable": false
}
your-diary commented 1 year ago

Let me also paste the log of flutter-lsp.

I think this is the related log entry:

[Trace - 13:12:15.886] Sending request 'completionItem/resolve - (6)'.
Params: {
    "data": {
        "file": "/Users/user/abc/lib/main.dart",
        "importUris": [
            "package:flutter/material.dart"
        ],
        "ref": "package:flutter/src/widgets/binding.dart;package:flutter/src/widgets/binding.dart;runApp"
    },
    "detail": "(Widget app) → void",
    "insertTextFormat": 2,
    "kind": 3,
    "label": "runApp(…)",
    "sortText": "9999546",
    "textEditText": "runApp(${0:app})"
}

[Trace - 13:12:15.899] Received response 'completionItem/resolve - (6)' in 13ms.
Result: {
    "additionalTextEdits": [
        {
            "newText": "import 'package:flutter/material.dart';\n\n",
            "range": {
                "end": {
                    "character": 0,
                    "line": 0
                },
                "start": {
                    "character": 0,
                    "line": 0
                }
            }
        }
    ],
    "data": {
        "file": "/Users/user/abc/lib/main.dart",
        "importUris": [
            "package:flutter/material.dart"
        ],
        "ref": "package:flutter/src/widgets/binding.dart;package:flutter/src/widgets/binding.dart;runApp"
    },
    "detail": "Auto import from 'package:flutter/material.dart'\n\n(Widget app) → void",
    "documentation": {
        "kind": "markdown",
        "value": "Inflate the given widget and attach it to the screen.\n\nThe widget is given constraints during layout that force it to fill the\nentire screen. If you wish to align your widget to one side of the screen\n(e.g., the top), consider using the [Align] widget. If you wish to center\nyour widget, you can also use the [Center] widget.\n\nCalling [runApp] again will detach the previous root widget from the screen\nand attach the given widget in its place. The new widget tree is compared\nagainst the previous widget tree and any differences are applied to the\nunderlying render tree, similar to what happens when a [StatefulWidget]\nrebuilds after calling [State.setState].\n\nInitializes the binding using [WidgetsFlutterBinding] if necessary.\n\nSee also:\n\n * [WidgetsBinding.attachRootWidget], which creates the root widget for the\n   widget hierarchy.\n * [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root\n   element for the element hierarchy.\n * [WidgetsBinding.handleBeginFrame], which pumps the widget pipeline to\n   ensure the widget, element, and render trees are all built."
    },
    "insertTextFormat": 2,
    "kind": 3,
    "label": "runApp(…)",
    "sortText": "9999546"
}
show full log of flutter-lsp (about 3000 lines long) [Trace - 13:12:05.883] Sending request 'initialize - (0)'. Params: { "processId": 90859, "rootPath": "/Users/user/abc", "rootUri": "file:///Users/user/abc", "capabilities": { "workspace": { "applyEdit": true, "workspaceEdit": { "documentChanges": true, "resourceOperations": [ "create", "rename", "delete" ], "failureHandling": "undo", "normalizesLineEndings": true, "changeAnnotationSupport": { "groupsOnLabel": false } }, "didChangeConfiguration": { "dynamicRegistration": true }, "didChangeWatchedFiles": { "dynamicRegistration": true, "relativePatternSupport": true }, "codeLens": { "refreshSupport": true }, "executeCommand": { "dynamicRegistration": true }, "configuration": true, "fileOperations": { "dynamicRegistration": true, "didCreate": true, "didRename": true, "didDelete": true, "willCreate": true, "willRename": true, "willDelete": true }, "semanticTokens": { "refreshSupport": true }, "inlayHint": { "refreshSupport": true }, "inlineValue": { "refreshSupport": true }, "diagnostics": { "refreshSupport": true }, "symbol": { "dynamicRegistration": true, "symbolKind": { "valueSet": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 ] }, "tagSupport": { "valueSet": [ 1 ] }, "resolveSupport": { "properties": [ "location.range" ] } }, "workspaceFolders": true }, "textDocument": { "publishDiagnostics": { "relatedInformation": true, "versionSupport": true, "tagSupport": { "valueSet": [ 1, 2 ] }, "codeDescriptionSupport": true, "dataSupport": true }, "synchronization": { "dynamicRegistration": true, "willSave": true, "willSaveWaitUntil": true, "didSave": true }, "completion": { "dynamicRegistration": true, "contextSupport": true, "completionItem": { "snippetSupport": true, "commitCharactersSupport": true, "documentationFormat": [ "markdown", "plaintext" ], "deprecatedSupport": true, "preselectSupport": true, "insertReplaceSupport": true, "tagSupport": { "valueSet": [ 1 ] }, "resolveSupport": { "properties": [ "documentation", "detail", "additionalTextEdits" ] }, "labelDetailsSupport": true, "insertTextModeSupport": { "valueSet": [ 1, 2 ] } }, "completionItemKind": { "valueSet": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 ] }, "insertTextMode": 2, "completionList": { "itemDefaults": [ "commitCharacters", "editRange", "insertTextFormat", "insertTextMode" ] } }, "hover": { "dynamicRegistration": true, "contentFormat": [ "markdown", "plaintext" ] }, "signatureHelp": { "dynamicRegistration": true, "contextSupport": true, "signatureInformation": { "documentationFormat": [ "markdown", "plaintext" ], "activeParameterSupport": true, "parameterInformation": { "labelOffsetSupport": true } } }, "references": { "dynamicRegistration": true }, "definition": { "dynamicRegistration": true, "linkSupport": true }, "documentHighlight": { "dynamicRegistration": true }, "documentSymbol": { "dynamicRegistration": true, "symbolKind": { "valueSet": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 ] }, "hierarchicalDocumentSymbolSupport": true, "tagSupport": { "valueSet": [ 1 ] }, "labelSupport": true }, "codeAction": { "dynamicRegistration": true, "isPreferredSupport": true, "disabledSupport": true, "dataSupport": true, "honorsChangeAnnotations": false, "resolveSupport": { "properties": [ "edit" ] }, "codeActionLiteralSupport": { "codeActionKind": { "valueSet": [ "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" ] } } }, "codeLens": { "dynamicRegistration": true }, "formatting": { "dynamicRegistration": true }, "rangeFormatting": { "dynamicRegistration": true }, "onTypeFormatting": { "dynamicRegistration": true }, "rename": { "dynamicRegistration": true, "prepareSupport": true, "honorsChangeAnnotations": true, "prepareSupportDefaultBehavior": 1 }, "documentLink": { "dynamicRegistration": true, "tooltipSupport": true }, "typeDefinition": { "dynamicRegistration": true, "linkSupport": true }, "implementation": { "dynamicRegistration": true, "linkSupport": true }, "declaration": { "dynamicRegistration": true, "linkSupport": true }, "colorProvider": { "dynamicRegistration": true }, "foldingRange": { "dynamicRegistration": true, "rangeLimit": 5000, "lineFoldingOnly": true, "foldingRangeKind": { "valueSet": [ "comment", "imports", "region" ] }, "foldingRange": { "collapsedText": false } }, "selectionRange": { "dynamicRegistration": true }, "callHierarchy": { "dynamicRegistration": true }, "linkedEditingRange": { "dynamicRegistration": true }, "semanticTokens": { "dynamicRegistration": true, "tokenTypes": [ "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "decorator", "operator" ], "tokenModifiers": [ "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" ], "formats": [ "relative" ], "requests": { "range": true, "full": { "delta": true } }, "multilineTokenSupport": false, "overlappingTokenSupport": false, "serverCancelSupport": true, "augmentsSyntaxTokens": true }, "inlayHint": { "dynamicRegistration": true, "resolveSupport": { "properties": [ "tooltip", "textEdits", "label.tooltip", "label.location", "label.command" ] } }, "inlineValue": { "dynamicRegistration": true }, "diagnostic": { "dynamicRegistration": true, "relatedDocumentSupport": true }, "typeHierarchy": { "dynamicRegistration": true } }, "window": { "showMessage": { "messageActionItem": { "additionalPropertiesSupport": true } }, "showDocument": { "support": true }, "workDoneProgress": true }, "general": { "regularExpressions": { "engine": "ECMAScript", "version": "ES2020" }, "markdown": { "parser": "marked", "version": "4.0.10" }, "positionEncodings": [ "utf-16" ], "staleRequestSupport": { "cancel": true, "retryOnContentModified": [ "textDocument/inlayHint", "textDocument/semanticTokens/full", "textDocument/semanticTokens/range", "textDocument/semanticTokens/full/delta" ] } } }, "initializationOptions": { "onlyAnalyzeProjectsWithOpenFiles": false, "suggestFromUnimportedLibraries": true, "closingLabels": true }, "trace": "verbose", "workspaceFolders": [ { "uri": "file:///Users/user/abc", "name": "abc" } ], "locale": "en_US", "clientInfo": { "name": "coc.nvim", "version": "0.0.82" } } [Trace - 13:12:05.999] Received response 'initialize - (0)' in 116ms. Result: { "capabilities": { "executeCommandProvider": { "commands": [ "edit.sortMembers", "edit.organizeImports", "edit.fixAll", "edit.sendWorkspaceEdit", "refactor.perform", "move_top_level_to_file" ], "workDoneProgress": true }, "workspace": { "workspaceFolders": { "changeNotifications": true, "supported": true } }, "workspaceSymbolProvider": true }, "serverInfo": { "name": "Dart SDK LSP Analysis Server", "version": "2.19.1" } } [Trace - 13:12:05.999] Sending notification 'initialized'. Params: {} [Trace - 13:12:06.007] Received request 'workspace/configuration - (1)'. Params: { "items": [ { "scopeUri": "file:///Users/user/abc", "section": "dart" }, { "section": "dart" } ] } [Trace - 13:12:06.007] Sending response 'workspace/configuration - (1)'. Processing request took 0ms Result: [ { "analysisExcludedFolders": [], "enableSdkFormatter": true, "lineLength": 80, "completeFunctionCalls": true, "showTodos": true }, { "analysisExcludedFolders": [], "enableSdkFormatter": true, "lineLength": 80, "completeFunctionCalls": true, "showTodos": true } ] [Trace - 13:12:06.023] Received request 'client/registerCapability - (2)'. Params: { "registrations": [ { "id": "0", "method": "textDocument/didOpen", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" }, { "language": "yaml", "pattern": "**/pubspec.yaml", "scheme": "file" }, { "language": "yaml", "pattern": "**/analysis_options.yaml", "scheme": "file" }, { "language": "yaml", "pattern": "**/lib/fix_data.yaml", "scheme": "file" } ] } }, { "id": "1", "method": "textDocument/didClose", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" }, { "language": "yaml", "pattern": "**/pubspec.yaml", "scheme": "file" }, { "language": "yaml", "pattern": "**/analysis_options.yaml", "scheme": "file" }, { "language": "yaml", "pattern": "**/lib/fix_data.yaml", "scheme": "file" } ] } }, { "id": "2", "method": "textDocument/didChange", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" }, { "language": "yaml", "pattern": "**/pubspec.yaml", "scheme": "file" }, { "language": "yaml", "pattern": "**/analysis_options.yaml", "scheme": "file" }, { "language": "yaml", "pattern": "**/lib/fix_data.yaml", "scheme": "file" } ], "syncKind": 2 } }, { "id": "3", "method": "textDocument/completion", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ], "resolveProvider": true, "triggerCharacters": [ ".", "=", "(", "$", "\"", "'", "{", "/", ":" ] } }, { "id": "4", "method": "textDocument/completion", "registerOptions": { "documentSelector": [ { "language": "yaml", "pattern": "**/pubspec.yaml", "scheme": "file" }, { "language": "yaml", "pattern": "**/analysis_options.yaml", "scheme": "file" }, { "language": "yaml", "pattern": "**/lib/fix_data.yaml", "scheme": "file" } ], "resolveProvider": true } }, { "id": "5", "method": "textDocument/hover", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "6", "method": "textDocument/signatureHelp", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ], "retriggerCharacters": [ "," ], "triggerCharacters": [ "(" ] } }, { "id": "7", "method": "textDocument/references", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "8", "method": "textDocument/documentHighlight", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "9", "method": "textDocument/documentSymbol", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "10", "method": "textDocument/documentColor", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "11", "method": "textDocument/formatting", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "12", "method": "textDocument/onTypeFormatting", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ], "firstTriggerCharacter": "}", "moreTriggerCharacter": [ ";" ] } }, { "id": "13", "method": "textDocument/rangeFormatting", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "14", "method": "textDocument/definition", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "15", "method": "textDocument/typeDefinition", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "16", "method": "textDocument/implementation", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "17", "method": "textDocument/codeAction", "registerOptions": { "codeActionKinds": [ "source", "source.organizeImports", "source.fixAll", "source.sortMembers", "quickfix", "refactor" ], "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "18", "method": "textDocument/rename", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ], "prepareProvider": true } }, { "id": "19", "method": "textDocument/foldingRange", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "20", "method": "workspace/willRenameFiles", "registerOptions": { "filters": [ { "pattern": { "glob": "**/*.dart", "matches": "file" }, "scheme": "file" }, { "pattern": { "glob": "**/", "matches": "folder" }, "scheme": "file" } ] } }, { "id": "21", "method": "workspace/didChangeConfiguration" }, { "id": "22", "method": "textDocument/selectionRange", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "23", "method": "textDocument/prepareCallHierarchy", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "24", "method": "textDocument/semanticTokens", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ], "full": { "delta": false }, "legend": { "tokenModifiers": [ "documentation", "constructor", "declaration", "importPrefix", "instance", "static", "escape", "annotation", "control", "label", "interpolation", "void" ], "tokenTypes": [ "annotation", "keyword", "class", "comment", "method", "variable", "parameter", "enum", "enumMember", "type", "source", "property", "namespace", "boolean", "number", "string", "function", "typeParameter" ] }, "range": true } }, { "id": "25", "method": "textDocument/prepareTypeHierarchy", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ] } }, { "id": "26", "method": "textDocument/inlayHint", "registerOptions": { "documentSelector": [ { "language": "dart", "scheme": "file" } ], "resolveProvider": false } } ] } [Trace - 13:12:06.025] Sending response 'client/registerCapability - (2)'. Processing request took 2ms No result returned. [Trace - 13:12:06.025] Sending notification 'textDocument/didOpen'. Params: { "textDocument": { "uri": "file:///Users/user/abc/lib/main.dart", "languageId": "dart", "version": 1, "text": "void main() {}\n" } } [Trace - 13:12:06.397] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [], "uri": "file:///Users/user/abc/android/app/src/profile/AndroidManifest.xml" } [Trace - 13:12:06.398] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [], "uri": "file:///Users/user/abc/android/app/src/main/AndroidManifest.xml" } [Trace - 13:12:06.398] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [], "uri": "file:///Users/user/abc/android/app/src/debug/AndroidManifest.xml" } [Trace - 13:12:06.399] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [], "uri": "file:///Users/user/abc/analysis_options.yaml" } [Trace - 13:12:06.546] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [], "uri": "file:///Users/user/abc/pubspec.yaml" } [Trace - 13:12:06.547] Received request 'window/workDoneProgress/create - (3)'. Params: { "token": "ANALYZING" } [Trace - 13:12:06.547] Sending response 'window/workDoneProgress/create - (3)'. Processing request took 0ms No result returned. [Trace - 13:12:06.557] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [], "uri": "file:///Users/user/abc/lib/main.dart" } [Trace - 13:12:06.558] Received notification '$/progress'. Params: { "token": "ANALYZING", "value": { "kind": "begin", "title": "Analyzing…" } } [Trace - 13:12:07.429] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [ { "code": "creation_with_non_type", "codeDescription": { "href": "https://dart.dev/diagnostics/creation_with_non_type" }, "message": "The name 'MyApp' isn't a class.\nTry correcting the name to match an existing class.", "range": { "end": { "character": 39, "line": 15 }, "start": { "character": 34, "line": 15 } }, "severity": 1, "source": "dart" }, { "code": "unused_import", "codeDescription": { "href": "https://dart.dev/diagnostics/unused_import" }, "message": "Unused import: 'package:abc/main.dart'.\nTry removing the import directive.", "range": { "end": { "character": 30, "line": 10 }, "start": { "character": 7, "line": 10 } }, "severity": 3, "source": "dart" } ], "uri": "file:///Users/user/abc/test/widget_test.dart" } [Trace - 13:12:07.431] Received notification '$/progress'. Params: { "token": "ANALYZING", "value": { "kind": "end" } } [Trace - 13:12:13.571] Sending notification 'textDocument/didChange'. Params: { "textDocument": { "uri": "file:///Users/user/abc/lib/main.dart", "version": 2 }, "contentChanges": [ { "range": { "start": { "line": 0, "character": 13 }, "end": { "line": 0, "character": 13 } }, "text": "r", "rangeLength": 0 } ] } [Trace - 13:12:13.573] Sending request 'textDocument/selectionRange - (1)'. Params: { "textDocument": { "uri": "file:///Users/user/abc/lib/main.dart" }, "positions": [ { "line": 0, "character": 14 } ] } [Trace - 13:12:13.578] Sending request 'textDocument/completion - (2)'. Params: { "textDocument": { "uri": "file:///Users/user/abc/lib/main.dart" }, "position": { "line": 0, "character": 14 }, "context": { "triggerKind": 1 } } [Trace - 13:12:13.662] Received request 'window/workDoneProgress/create - (4)'. Params: { "token": "ANALYZING" } [Trace - 13:12:13.662] Sending response 'window/workDoneProgress/create - (4)'. Processing request took 0ms No result returned. [Trace - 13:12:13.662] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [ { "code": "expected_token", "message": "Expected to find ';'.", "range": { "end": { "character": 14, "line": 0 }, "start": { "character": 13, "line": 0 } }, "severity": 1, "source": "dart" }, { "code": "undefined_identifier", "codeDescription": { "href": "https://dart.dev/diagnostics/undefined_identifier" }, "message": "Undefined name 'r'.\nTry correcting the name to one that is defined, or defining the name.", "range": { "end": { "character": 14, "line": 0 }, "start": { "character": 13, "line": 0 } }, "severity": 1, "source": "dart" } ], "uri": "file:///Users/user/abc/lib/main.dart" } [Trace - 13:12:13.662] Received response 'textDocument/selectionRange - (1)' in 89ms. Result: [ { "parent": { "parent": { "parent": { "range": { "end": { "character": 15, "line": 0 }, "start": { "character": 0, "line": 0 } } }, "range": { "end": { "character": 15, "line": 0 }, "start": { "character": 9, "line": 0 } } }, "range": { "end": { "character": 15, "line": 0 }, "start": { "character": 12, "line": 0 } } }, "range": { "end": { "character": 14, "line": 0 }, "start": { "character": 13, "line": 0 } } } ] [Trace - 13:12:13.679] Sending notification 'textDocument/didChange'. Params: { "textDocument": { "uri": "file:///Users/user/abc/lib/main.dart", "version": 3 }, "contentChanges": [ { "range": { "start": { "line": 0, "character": 14 }, "end": { "line": 0, "character": 14 } }, "text": "u", "rangeLength": 0 } ] } [Trace - 13:12:13.726] Received notification 'textDocument/publishDiagnostics'. Params: { "diagnostics": [ { "code": "expected_token", "message": "Expected to find ';'.", "range": { "end": { "character": 14, "line": 0 }, "start": { "character": 13, "line": 0 } }, "severity": 1, "source": "dart" }, { "code": "undefined_identifier", "codeDescription": { "href": "https://dart.dev/diagnostics/undefined_identifier" }, "message": "Undefined name 'r'.\nTry correcting the name to one that is defined, or defining the name.", "range": { "end": { "character": 14, "line": 0 }, "start": { "character": 13, "line": 0 } }, "severity": 1, "source": "dart" } ], "uri": "file:///Users/user/abc/lib/main.dart" } [Trace - 13:12:13.729] Received notification '$/progress'. Params: { "token": "ANALYZING", "value": { "kind": "begin", "title": "Analyzing…" } } [Trace - 13:12:13.800] Received response 'textDocument/completion - (2)' in 222ms. Result: { "isIncomplete": true, "itemDefaults": { "editRange": { "end": { "character": 14, "line": 0 }, "start": { "character": 13, "line": 0 } }, "insertTextMode": 1 }, "items": [ { "kind": 14, "label": "return", "sortText": "9999437" }, { "documentation": { "kind": "markdown", "value": "Error thrown due to an argument value being outside an accepted range." }, "kind": 7, "label": "RangeError", "sortText": "9999474" }, { "detail": "(dynamic message) → RangeError", "documentation": { "kind": "markdown", "value": "Create a new [RangeError] with the given [message]." }, "filterText": "RangeError", "insertTextFormat": 2, "kind": 4, "label": "RangeError(…)", "sortText": "9999499", "textEditText": "RangeError(${0:message})" }, { "detail": "(num value, [String? name, String? message]) → RangeError", "documentation": { "kind": "markdown", "value": "Create a new [RangeError] with a message for the given [value].\n\nAn optional [name] can specify the argument name that has the\ninvalid value, and the [message] can override the default error\ndescription." }, "filterText": "RangeError.value", "insertTextFormat": 2, "kind": 4, "label": "RangeError.value(…)", "sortText": "9999499", "textEditText": "RangeError.value(${0:value})" }, { "detail": "(num invalidValue, int? minValue, int? maxValue, [String? name, String? message]) → RangeError", "documentation": { "kind": "markdown", "value": "Create a new [RangeError] for a value being outside the valid range.\n\nThe allowed range is from [minValue] to [maxValue], inclusive.\nIf `minValue` or `maxValue` are `null`, the range is infinite in\nthat direction.\n\nFor a range from 0 to the length of something, end exclusive, use\n[RangeError.index].\n\nAn optional [name] can specify the argument name that has the\ninvalid value, and the [message] can override the default error\ndescription." }, "filterText": "RangeError.range", "insertTextFormat": 2, "kind": 4, "label": "RangeError.range(…)", "sortText": "9999499", "textEditText": "RangeError.range(${1:invalidValue}, ${2:minValue}, ${3:maxValue})" }, { "detail": "(int index, dynamic indexable, [String? name, String? message, int? length]) → RangeError", "documentation": { "kind": "markdown", "value": "Creates a new [RangeError] stating that [index] is not a valid index\ninto [indexable].\n\nAn optional [name] can specify the argument name that has the\ninvalid value, and the [message] can override the default error\ndescription.\n\nThe [length] is the length of [indexable] at the time of the error.\nIf `length` is omitted, it defaults to `indexable.length`." }, "filterText": "RangeError.index", "insertTextFormat": 2, "kind": 4, "label": "RangeError.index(…)", "sortText": "9999499", "textEditText": "RangeError.index(${1:index}, ${2:indexable})" }, { "documentation": { "kind": "markdown", "value": "The base class for all record types.\n\nThe run-time type of a record object is a record type, and as such, a\nsubtype of [Record]." }, "kind": 7, "label": "Record", "sortText": "9999474" }, { "documentation": { "kind": "markdown", "value": "A regular expression pattern.\n\nRegular expressions (abbreviated as regex or regexp)\nconsist of a sequence of characters that specify\na match-checking algorithm for text _inputs_.\nApplying a regexp to an input text results either in the regexp matching,\nor accepting, the text, or the text being rejected.\nWhen the regexp matches the text, it further provides some information\nabout *how* it matched the text.\n\nDart regular expressions have the same syntax and semantics as\nJavaScript regular expressions.\nTo learn more about JavaScript regular expressions, see\n.\n\nDart provides the basic regexp matching algorithm as [matchAsPrefix],\nwhich checks if the regexp matches a part of the input starting\nat a specific position.\nIf the regexp matches, Dart returns the details of the match as a\n[RegExpMatch].\n\nYou can build all the other methods of [RegExp] from that basic match\ncheck.\n\nThe most common use of a regexp is to *search* for a match in the input.\nThe [firstMatch] method provides this functionality.\nThis method searches a string for the first position where the regexp\nmatches.\nAgain, if a match is found, Dart returns its details as a [RegExpMatch].\n\nThe following example finds the first match of a regular expression in\na string.\n```dart\nRegExp exp = RegExp(r'(\\w+)');\nString str = 'Parse my string';\nRegExpMatch? match = exp.firstMatch(str);\nprint(match![0]); // \"Parse\"\n```\nUse [allMatches] to look for all matches of a regular expression in\na string.\n\nThe following example finds all matches of a regular expression in\na string.\n```dart\nRegExp exp = RegExp(r'(\\w+)');\nString str = 'Parse my string';\nIterable matches = exp.allMatches(str);\nfor (final m in matches) {\n print(m[0]);\n}\n```\nThe output of the example is:\n```\nParse\nmy\nstring\n```\n\nThe preceding examples use a _raw string_,\na specific string type that prefixes the string literal with `r`.\nUse a raw string to treat each character, including `\\` and `$`,\nin a string as a literal character. Each character then gets passed\nto the [RegExp] parser.\nYou should use a raw string as the argument to the\n[RegExp] constructor.\n\n**Performance Notice**: Regular expressions do not resolve issues\nmagically.\nAnyone can write a regexp that performs inefficiently when\napplied to some string inputs.\nOften, such a regxp will perform well enough on small or common\ninputs, but have pathological performance on large and uncommon inputs.\nThis inconsistent behavior makes performance issues harder to detect\nin testing.\n\nA regexp might not find text any faster than using `String` operations\nto inspect a string.\nThe strength of regexp comes from the ability to specify\n*somewhat* complicated patterns in very few characters.\nThese regexps provide reasonable efficiency in most common cases.\nThis conciseness comes at a cost of readability.\nDue to their syntactic complexity, regexes cannot be considered\nself documenting.\n\nDart regexps implement the ECMAScript RegExp specification.\nThis specification provides a both common and well-known regexp behavior.\nWhen compiling Dart for the web, the compiled code can use the browser’s\nregexp implementation.\n\nThe specification defines ECMAScript regexp behavior using *backtracking*.\nWhen a regexp can choose between different ways to match,\nit tries each way in the order given in the pattern.\nFor example: `RegExp(r\"(foo|bar)baz\")` wants to check for `foo` or `bar`,\nso it checks for `foo` first.\nIf continuing along that path doesn't match the input,\nthe regexp implementation *backtracks*.\nThe implementation resets to the original state from before\nchecking for `foo`,\nforgetting all the work it has done after that,\nand then tries the next choice; `bar` in this example.\n\nThe specification defines these choices\nand the order in which they must be attempted.\nIf a regexp could match an input in more than one way,\nthe order of the choices decides which match the regexp returns.\nCommonly used regexps order their matching choices to ensure\na specific result.\nThe ECMAScript regexp specification limits how Dart\ncan implement regular expressions.\nIt must be a backtracking implementation which checks choices in\na specific order.\nDart cannot choose a different regexp implementation,\nbecause then regexp matching would behave differently.\n\nThe backtracking approach works, but at a cost.\nFor some regexps and some inputs,\nfinding a *correct* match can take a *lot* of tries.\nIt can take even more tries to reject an input\nthat the regexp *almost* matches.\n\nA well-known dangerous regexp pattern comes from\nnesting quantifiers like `*`:\n\n```dart\nvar re = RegExp(r\"^(a*|b)*c\");\nprint(re.hasMatch(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"));\n```\n\nThe regexp pattern doesn't match the input string of only `a`s\nas the input doesn’t contain the required `c`.\nThere exists an _exponential_ number of different ways for `(a*|b)*`\nto match all the `a`s.\nThe backtracking regexp implementation tries *all* of them\nbefore deciding that none of those can lead to a complete match.\nEach extra `a` added to the input doubles the time the regexp\ntakes to return `false`.\n(When backtracking has this exponential potential, it is called\n[“catastrophic backtracking”](https://www.google.com/search?q=regexp+catastrophic+backtracking)).\n\nSequential quantifiers provide another dangerous pattern,\nbut they provide “only” polynomial complexity.\n\n```dart\n// Like `\\w*-\\d`, but check for `b` and `c` in that order.\nvar re = RegExp(r\"^\\w*(b)?\\w*(c)?\\w*-\\d\");\nprint(re.hasMatch(\"a\" * 512));\n```\n\nAgain the input doesn’t match, but `RegExp` must try *n*3 ways\nto match the *n* `a`s _before_ deciding that.\nDoubling the input’s length increases the time to return `false`\n_eightfold_.\nThis exponent increases with the number of sequential quantifiers.\n\nBoth of these patterns look trivial when reduced to such simple regexps.\nHowever, these \"trivial\" patterns often arise as parts of\nmore complicated regular expressions,\nwhere your ability to find the problem gets more difficult.\n\nIn general, if a regexp has potential for *super-linear complexity*,\nyou can craft an input that takes an inordinate amount of time to search.\nThese patterns can then be used for [denial of service attacks](https://en.wikipedia.org/wiki/ReDoS)\nif you apply vulnerable regexp patterns to user-provided inputs.\n\nNo guaranteed solution exists for this problem.\nBe careful to not use regexps with super-linear behavior\nwhere the program may match that regexp against inputs\nwith no guaranteed match.\n\nRules of thumb to avoid regexps with super-linear execution time include:\n\n* Whenever the regexp has a choice, try to make sure\n that the choice can be made based on the next character\n (or very limited look-ahead).\n This limits the need to perform a lot of computation along both choices.\n* When using quantifiers, ensure that the same string cannot match\n both one and more-than-one iteration of the quantifier's\n regular expression.\n (For `(a*|b)*`, the string `\"aa\"` can match both\n `(a*|b){1}` and `(a*|b){2}`.)\n* Most uses of Dart regular expressions *search* for a match,\n for example using [firstMatch].\n If you do not *anchor* the pattern\n to the start of a line or input using `^`,\n this search acts as if the regexp began with an implicit `[^]*`.\n Starting your actual regular expression with `.*`\n then results in potential quadratic behavior for the search.\n Use anchors or [matchAsPrefix] where appropriate,\n or avoid starting the regexp with a quantified pattern.\n* *For experts only:* Neither Dart nor ECMAScript have general\n [“atomic grouping”](https://github.com/tc39/proposal-regexp-atomic-operators).\n Other regular expression dialects use this to limit backtracking.\n If an atomic capture group succeeds once,\n the regexp cannot backtrack into the same match later.\n As lookarounds also serve as atomic groups,\n something similar can be achieved using a *lookahead*:\n `var re = RegExp(r\"^(?=((a*|b)*))\\1d\");`\n The preceding example does the same inefficient matching of `(a*|b)*`.\n Once the regexp has matched as far as possible,\n it completes the positive lookahead.\n Then it skips what the lookahead matched using a back-reference.\n After that, it can no longer backtrack\n and try other combinations of `a`s.\n\nTry to reduce how many ways the regexp can match the same string.\nThat reduces the number of possible backtracks performed\nwhen the regexp does not find a match.\nSeveral guides to [improving the performance of regular expressions](https://www.google.com/search?q=performance+of+regular+expressions)\nexist on the internet. Use these as inspirations, too." }, "kind": 7, "label": "RegExp", "sortText": "9999474" }, { "detail": "(String source, {bool multiLine = false, bool caseSensitive = true, bool unicode = false, bool dotAll = false}) → RegExp", "documentation": { "kind": "markdown", "value": "Constructs a regular expression.\n\nThrows a [FormatException] if [source] does not follow valid regular\nexpression syntax.\n\nIf your code enables `multiLine`, then `^` and `$` will match\nthe beginning and end of a _line_, as well as matching beginning and\nend of the input, respectively.\n\nIf your code disables `caseSensitive`,\nthen Dart ignores the case of letters when matching.\nFor example, with `caseSensitive` disable, the regexp pattern `a`\nmatches both `a` and `A`.\n\nIf your code enables `unicode`, then Dart treats the pattern as a\nUnicode pattern per the ECMAScript standard.\n\nIf your code enables `dotAll`, then the `.` pattern will match _all_\ncharacters, including line terminators.\n\nExample:\n\n```dart\nfinal wordPattern = RegExp(r'(\\w+)');\nfinal digitPattern = RegExp(r'(\\d+)');\n```\n\nThese examples use a _raw string_ as the argument.\nYou should prefer to use a raw string as argument to the [RegExp]\nconstructor, because it makes it easy to write\nthe `\\` and `$` characters as regexp reserved characters.\n\nThe same examples written using non-raw strings would be:\n```dart\nfinal wordPattern = RegExp('(\\\\w+)'); // Should be raw string.\nfinal digitPattern = RegExp('(\\\\d+)'); // Should be raw string.\n```\nUse a non-raw string only when you need to use\nstring interpolation. For example:\n```dart\nPattern keyValuePattern(String keyIdentifier) =>\n RegExp('$keyIdentifier=(\\\\w+)');\n```\nWhen including a string verbatim into the regexp pattern like this,\nbe careful that the string does not contain regular expression\nreserved characters.\nIf that risk exists, use the [escape] function to convert those\ncharacters to safe versions of the reserved characters\nand match only the string itself:\n```dart\nPattern keyValuePattern(String anyStringKey) =>\n RegExp('${RegExp.escape(anyStringKey)}=(\\\\w+)');\n```" }, "filterText": "RegExp", "insertTextFormat": 2, "kind": 4, "label": "RegExp(…)", "sortText": "9999499", "textEditText": "RegExp(${0:source})" }, { "documentation": { "kind": "markdown", "value": "A regular expression match.\n\nRegular expression matches are [Match]es. They also include the ability\nto retrieve the names for any named capture groups and to retrieve\nmatches for named capture groups by name instead of by their index.\n\nExample:\n```dart\nconst pattern =\n r'^\\[(?
Kavantix commented 1 year ago

Hey @your-diary, are you using the latest coc-flutter 1.9.9? This looks like the issue I fixed in that version

your-diary commented 1 year ago

@Kavantix I didn't check the version at the time of posting the issue. Now I re-run :CocInstall coc-flutter to install coc-flutter 1.9.9 but the issue still happens.

Edit: I also tried flutter upgrade command to install the latest Flutter SDK (Flutter 3.7.6, Dart 2.19.3) but the issue still happens.

ktakayama commented 1 year ago

I have same issues. I can fix it with following changes.

diff --git a/src/server/lsp/resolveCompleteItem.ts b/src/server/lsp/resolveCompleteItem.ts
index 2f1b200..dc541a4 100644
--- a/src/server/lsp/resolveCompleteItem.ts
+++ b/src/server/lsp/resolveCompleteItem.ts
@@ -52,7 +52,7 @@ export const getResolveCompleteItemFunc = (options: { completeFunctionCalls: boo

   // if dart.completeFunctionCalls: false
   // do not add `()` snippet
-  if (options.completeFunctionCalls && item.insertTextFormat !== InsertTextFormat.Snippet) {
+  if (options.completeFunctionCalls) {
     // improve function()
     m = label.match(funcCallRegex);
     if (m) {

However, I haven't been able to fully test it because I don't know of any cases other than this one where item.insertTextFormat becomes InsertTextFormat.Snippet.

your-diary commented 1 year ago

@ktakayama Thank you for suggesting a workaround, but I think we should keep the if clause when dart.completeFunctionCalls == true as it is since the name of the option indicates parenthesis shall be auto-completed. Actually README.md says

dart.completeFunctionCalls default: true

Completes functions/methods with their required parameters.

Rather, maybe we should fix it as below. (Link to the original file. It worked at least in my environment after setting dart.completeFunctionCalls: false via :CocConfig.)

diff --git a/src/server/lsp/resolveCompleteItem.ts b/src/server/lsp/resolveCompleteItem.ts
index 73dd392..4d831c7 100644
--- a/src/server/lsp/resolveCompleteItem.ts
+++ b/src/server/lsp/resolveCompleteItem.ts
@@ -52,22 +52,37 @@ export const getResolveCompleteItemFunc = (options: {completeFunctionCalls: bool

     // if dart.completeFunctionCalls: false
     // do not add `()` snippet
     if (options.completeFunctionCalls && item.insertTextFormat !== InsertTextFormat.Snippet) {
         // improve function()
         m = label.match(funcCallRegex);
         if (m) {
             item.insertText = `${m[1]}()\${0}`;
             item.insertTextFormat = InsertTextFormat.Snippet;
             return item;
         }
         // improve function(…?)
         m = label.match(funcCallWithArgsRegex);
         if (m) {
             item.insertText = `${m[1]}(\${1})\${0}`;
             item.insertTextFormat = InsertTextFormat.Snippet;
             return item;
         }
     }

+    if (!options.completeFunctionCalls) {
+        m = label.match(funcCallRegex);
+        if (m) {
+            item.insertText = `${m[1]}`;
+            //TODO: I don't know if we should execute `item.insertTextFormat = ...` here.
+            return item;
+        }
+        m = label.match(funcCallWithArgsRegex);
+        if (m) {
+            item.insertText = `${m[1]}`;
+            //TODO: I don't know if we should execute `item.insertTextFormat = ...` here.
+            return item;
+        }
+    }
+
     return item;
 };

By the way, to apply the above fix directly to the local ~/.config/coc/extensions/node_modules/coc-flutter/out/index.js, insert

if (!t.completeFunctionCalls) {
    if (i=r.match(GR),i) {
        e.insertText=`${i[1]}`;
        return e;
    }
    if (i=r.match(UR),i) {
        e.insertText=`${i[1]}`;
        return e;
    }
}

just after

if(t.completeFunctionCalls&&e.insertTextFormat!==Ze.InsertTextFormat.Snippet){if(i=r.match(GR),i)return e.insertText=`${i[1]}()\${0}`,e.insertTextFormat=Ze.InsertTextFormat.Snippet,e;if(i=r.match(UR),i)return e.insertText=`${i[1]}(\${1})\${0}`,e.insertTextFormat=Ze.InsertTextFormat.Snippet,e}
phferreira commented 1 year ago

I have same problem here https://github.com/iamcco/coc-flutter/issues/171#issuecomment-1476615391

Kavantix commented 1 year ago

@your-diary can you check if v1.9.10 fixed this?

phferreira commented 1 year ago

I have same problem here #171 (comment)

I test with coc-flutter v1.9.10 and flutter in 3.9.0-10.0.pre.22 • channel master, all works fine.

Thank you very much

your-diary commented 1 year ago

@Kavantix Firstly the fix didn't seem to work, but once I set "dart.completeFunctionCalls": true, finally it started working. (The default value is true but I had temporarily set it to false for debugging this issue.)

Thank you very much :)

your-diary commented 1 year ago

It seems the issue reappeared today.

How to reproduce

Almost the same as the OP:

  1. Create a project

  2. open lib/main.dart

  3. Type runa and select RuneIterator.at(...)~

Expected Result

RuneIterator.at is inserted. (Only function name, without paren.)

Actual Result

When "dart.completeFunctionCalls": true, parenthesis () and the string string, index are auto-completed in addition to the function name.

Screenshot 2023-05-11 at 18 48 40

When "dart.completeFunctionCalls": false, parenthesis () and the elipsis ... are auto-completed in addition to the function name.

Screenshot 2023-05-11 at 18 49 10

Versions

Kavantix commented 1 year ago

@your-diary is your problem that it inserts the ... when completeFunctionCalls is turned off?

your-diary commented 1 year ago

@Kavantix Partly, yes. Partly, no. I want RuneIterator.at to be auto-completed (as before) but currently RuneIterator.at(string, index) or RuneIterator.at(...) are auto-completed depending on the value of completeFunctionCalls.

Kavantix commented 1 year ago

@your-diary can you turn on some extra logging and see what the dart lsp is returning in that case

your-diary commented 1 year ago

@Kavantix coc-flutter: 1.9.0 flutter: 3.10.0

log of flutter-lsp (probable relevant part):

[Trace - 19:44:56.785] Sending request 'completionItem/resolve - (7)'.
Params: {
    "detail": "(String string, int index) → RuneIterator",
    "documentation": {
        "kind": "markdown",
        "value": "Create an iterator positioned before the [index]th code unit of the string.\n\nWhen created, there is no [current] value.\nA [moveNext] will use the rune starting at [index] the current value,\nand a [movePrevious] will use the rune ending just before [index] as\nthe current value.\n\nThe [index] position must not be in the middle of a surrogate pair."
    },
    "filterText": "RuneIterator.at",
    "kind": 4,
    "label": "RuneIterator.at(…)",
    "sortText": "9999499",
    "textEditText": "RuneIterator.at"
}

[Trace - 19:44:56.787] Received response 'completionItem/resolve - (7)' in 2ms.
Result: {
    "detail": "(String string, int index) → RuneIterator",
    "documentation": {
        "kind": "markdown",
        "value": "Create an iterator positioned before the [index]th code unit of the string.\n\nWhen created, there is no [current] value.\nA [moveNext] will use the rune starting at [index] the current value,\nand a [movePrevious] will use the rune ending just before [index] as\nthe current value.\n\nThe [index] position must not be in the middle of a surrogate pair."
    },
    "filterText": "RuneIterator.at",
    "kind": 4,
    "label": "RuneIterator.at(…)",
    "sortText": "9999499",
    "textEditText": "RuneIterator.at"
}

[Trace - 19:44:59.227] Sending notification 'textDocument/didChange'.
Params: {
    "textDocument": {
        "uri": "file:///Users/user/abc/lib/main.dart",
        "version": 12
    },
    "contentChanges": [
        {
            "range": {
                "start": {
                    "line": 3,
                    "character": 0
                },
                "end": {
                    "line": 3,
                    "character": 9
                }
            },
            "text": "RuneIterator.at(…)",
            "rangeLength": 9
        }
    ]
}

The full log was so long that GitHub doesn't let me post it.

Kavantix commented 1 year ago

Thanks! It looks like that should be fine, I’ll see if I can find some time to dig into this soon