text: /*---------------------------------------------------------------------------------------------
   *  Copyright (c) Microsoft Corporation. All rights reserved.
   *  Licensed under the MIT License. See License.txt in the project root for license information.
   *--------------------------------------------------------------------------------------------*/
  'use strict';
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
      function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
      return new (P || (P = Promise))(function (resolve, reject) {
          function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
          function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
          function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
          step((generator = generator.apply(thisArg, _arguments || [])).next());
      });
  };
  var __generator = (this && this.__generator) || function (thisArg, body) {
      var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
      return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
      function verb(n) { return function (v) { return step([n, v]); }; }
      function step(op) {
          if (f) throw new TypeError("Generator is already executing.");
          while (_) try {
              if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
              if (y = 0, t) op = [op[0] & 2, t.value];
              switch (op[0]) {
                  case 0: case 1: t = op; break;
                  case 4: _.label++; return { value: op[1], done: false };
                  case 5: _.label++; y = op[1]; op = [0]; continue;
                  case 7: op = _.ops.pop(); _.trys.pop(); continue;
                  default:
                      if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                      if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                      if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                      if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                      if (t[2]) _.ops.pop();
                      _.trys.pop(); continue;
              }
              op = body.call(thisArg, _);
          } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
          if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
      }
  };
  import { DocumentHighlightKind, Location, Range, SymbolKind, TextEdit, FileType } from '../cssLanguageTypes';
  import * as nls from 'vscode-nls';
  import * as nodes from '../parser/cssNodes';
  import { Symbols } from '../parser/cssSymbolScope';
  import { getColorValue, hslFromColor } from '../languageFacts/facts';
  import { startsWith } from '../utils/strings';
  import { dirname, joinPath } from '../utils/resources';
  var localize = nls.loadMessageBundle();
  var CSSNavigation = /** @class */ (function () {
      function CSSNavigation(fileSystemProvider) {
          this.fileSystemProvider = fileSystemProvider;
      }
      CSSNavigation.prototype.findDefinition = function (document, position, stylesheet) {
          var symbols = new Symbols(stylesheet);
          var offset = document.offsetAt(position);
          var node = nodes.getNodeAtOffset(stylesheet, offset);
          if (!node) {
              return null;
          }
          var symbol = symbols.findSymbolFromNode(node);
          if (!symbol) {
              return null;
          }
          return {
              uri: document.uri,
              range: getRange(symbol.node, document)
          };
      };
      CSSNavigation.prototype.findReferences = function (document, position, stylesheet) {
          var highlights = this.findDocumentHighlights(document, position, stylesheet);
          return highlights.map(function (h) {
              return {
                  uri: document.uri,
                  range: h.range
              };
          });
      };
      CSSNavigation.prototype.findDocumentHighlights = function (document, position, stylesheet) {
          var result = [];
          var offset = document.offsetAt(position);
          var node = nodes.getNodeAtOffset(stylesheet, offset);
          if (!node || node.type === nodes.NodeType.Stylesheet || node.type === nodes.NodeType.Declarations) {
              return result;
          }
          if (node.type === nodes.NodeType.Identifier && node.parent && node.parent.type === nodes.NodeType.ClassSelector) {
              node = node.parent;
          }
          var symbols = new Symbols(stylesheet);
          var symbol = symbols.findSymbolFromNode(node);
          var name = node.getText();
          stylesheet.accept(function (candidate) {
              if (symbol) {
                  if (symbols.matchesSymbol(candidate, symbol)) {
                      result.push({
                          kind: getHighlightKind(candidate),
                          range: getRange(candidate, document)
                      });
                      return false;
                  }
              }
              else if (node && node.type === candidate.type && candidate.matches(name)) {
                  // Same node type and data
                  result.push({
                      kind: getHighlightKind(candidate),
                      range: getRange(candidate, document)
                  });
              }
              return true;
          });
          return result;
      };
      CSSNavigation.prototype.isRawStringDocumentLinkNode = function (node) {
          return node.type === nodes.NodeType.Import;
      };
      CSSNavigation.prototype.findDocumentLinks = function (document, stylesheet, documentContext) {
          var links = this.findUnresolvedLinks(document, stylesheet);
          for (var i = 0; i < links.length; i++) {
              var target = links[i].target;
              if (target && !(/^\w+:\/\//g.test(target))) {
                  var resolved = documentContext.resolveReference(target, document.uri);
                  if (resolved) {
                      links[i].target = resolved;
                  }
              }
          }
          return links;
      };
      CSSNavigation.prototype.findDocumentLinks2 = function (document, stylesheet, documentContext) {
          return __awaiter(this, void 0, void 0, function () {
              var links, resolvedLinks, _i, links_1, link, target, resolvedTarget;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          links = this.findUnresolvedLinks(document, stylesheet);
                          resolvedLinks = [];
                          _i = 0, links_1 = links;
                          _a.label = 1;
                      case 1:
                          if (!(_i < links_1.length)) return [3 /*break*/, 5];
                          link = links_1[_i];
                          target = link.target;
                          if (!(target && !(/^\w+:\/\//g.test(target)))) return [3 /*break*/, 3];
                          return [4 /*yield*/, this.resolveRelativeReference(target, document.uri, documentContext)];
                      case 2:
                          resolvedTarget = _a.sent();
                          if (resolvedTarget !== undefined) {
                              link.target = resolvedTarget;
                              resolvedLinks.push(link);
                          }
                          return [3 /*break*/, 4];
                      case 3:
                          resolvedLinks.push(link);
                          _a.label = 4;
                      case 4:
                          _i++;
                          return [3 /*break*/, 1];
                      case 5: return [2 /*return*/, resolvedLinks];
                  }
              });
          });
      };
      CSSNavigation.prototype.findUnresolvedLinks = function (document, stylesheet) {
          var _this = this;
          var result = [];
          var collect = function (uriStringNode) {
              var rawUri = uriStringNode.getText();
              var range = getRange(uriStringNode, document);
              // Make sure the range is not empty
              if (range.start.line === range.end.line && range.start.character === range.end.character) {
                  return;
              }
              if (startsWith(rawUri, "'") || startsWith(rawUri, "\"")) {
                  rawUri = rawUri.slice(1, -1);
              }
              result.push({ target: rawUri, range: range });
          };
          stylesheet.accept(function (candidate) {
              if (candidate.type === nodes.NodeType.URILiteral) {
                  var first = candidate.getChild(0);
                  if (first) {
                      collect(first);
                  }
                  return false;
              }
              /**
               * In @import, it is possible to include links that do not use `url()`
               * For example, `@import 'foo.css';`
               */
              if (candidate.parent && _this.isRawStringDocumentLinkNode(candidate.parent)) {
                  var rawText = candidate.getText();
                  if (startsWith(rawText, "'") || startsWith(rawText, "\"")) {
                      collect(candidate);
                  }
                  return false;
              }
              return true;
          });
          return result;
      };
      CSSNavigation.prototype.findDocumentSymbols = function (document, stylesheet) {
          var result = [];
          stylesheet.accept(function (node) {
              var entry = {
                  name: null,
                  kind: SymbolKind.Class,
                  location: null
              };
              var locationNode = node;
              if (node instanceof nodes.Selector) {
                  entry.name = node.getText();
                  locationNode = node.findAParent(nodes.NodeType.Ruleset, nodes.NodeType.ExtendsReference);
                  if (locationNode) {
                      entry.location = Location.create(document.uri, getRange(locationNode, document));
                      result.push(entry);
                  }
                  return false;
              }
              else if (node instanceof nodes.VariableDeclaration) {
                  entry.name = node.getName();
                  entry.kind = SymbolKind.Variable;
              }
              else if (node instanceof nodes.MixinDeclaration) {
                  entry.name = node.getName();
                  entry.kind = SymbolKind.Method;
              }
              else if (node instanceof nodes.FunctionDeclaration) {
                  entry.name = node.getName();
                  entry.kind = SymbolKind.Function;
              }
              else if (node instanceof nodes.Keyframe) {
                  entry.name = localize('literal.keyframes', "@keyframes {0}", node.getName());
              }
              else if (node instanceof nodes.FontFace) {
                  entry.name = localize('literal.fontface', "@font-face");
              }
              else if (node instanceof nodes.Media) {
                  var mediaList = node.getChild(0);
                  if (mediaList instanceof nodes.Medialist) {
                      entry.name = '@media ' + mediaList.getText();
                      entry.kind = SymbolKind.Module;
                  }
              }
              if (entry.name) {
                  entry.location = Location.create(document.uri, getRange(locationNode, document));
                  result.push(entry);
              }
              return true;
          });
          return result;
      };
      CSSNavigation.prototype.findDocumentColors = function (document, stylesheet) {
          var result = [];
          stylesheet.accept(function (node) {
              var colorInfo = getColorInformation(node, document);
              if (colorInfo) {
                  result.push(colorInfo);
              }
              return true;
          });
          return result;
      };
      CSSNavigation.prototype.getColorPresentations = function (document, stylesheet, color, range) {
          var result = [];
          var red256 = Math.round(color.red * 255), green256 = Math.round(color.green * 255), blue256 = Math.round(color.blue * 255);
          var label;
          if (color.alpha === 1) {
              label = "rgb(" + red256 + ", " + green256 + ", " + blue256 + ")";
          }
          else {
              label = "rgba(" + red256 + ", " + green256 + ", " + blue256 + ", " + color.alpha + ")";
          }
          result.push({ label: label, textEdit: TextEdit.replace(range, label) });
          if (color.alpha === 1) {
              label = "#" + toTwoDigitHex(red256) + toTwoDigitHex(green256) + toTwoDigitHex(blue256);
          }
          else {
              label = "#" + toTwoDigitHex(red256) + toTwoDigitHex(green256) + toTwoDigitHex(blue256) + toTwoDigitHex(Math.round(color.alpha * 255));
          }
          result.push({ label: label, textEdit: TextEdit.replace(range, label) });
          var hsl = hslFromColor(color);
          if (hsl.a === 1) {
              label = "hsl(" + hsl.h + ", " + Math.round(hsl.s * 100) + "%, " + Math.round(hsl.l * 100) + "%)";
          }
          else {
              label = "hsla(" + hsl.h + ", " + Math.round(hsl.s * 100) + "%, " + Math.round(hsl.l * 100) + "%, " + hsl.a + ")";
          }
          result.push({ label: label, textEdit: TextEdit.replace(range, label) });
          return result;
      };
      CSSNavigation.prototype.doRename = function (document, position, newName, stylesheet) {
          var _a;
          var highlights = this.findDocumentHighlights(document, position, stylesheet);
          var edits = highlights.map(function (h) { return TextEdit.replace(h.range, newName); });
          return {
              changes: (_a = {}, _a[document.uri] = edits, _a)
          };
      };
      CSSNavigation.prototype.resolveRelativeReference = function (ref, documentUri, documentContext) {
          return __awaiter(this, void 0, void 0, function () {
              var moduleName, rootFolderUri, documentFolderUri, modulePath, pathWithinModule;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          if (!(ref[0] === '~' && ref[1] !== '/' && this.fileSystemProvider)) return [3 /*break*/, 3];
                          ref = ref.substring(1);
                          if (!startsWith(documentUri, 'file://')) return [3 /*break*/, 2];
                          moduleName = getModuleNameFromPath(ref);
                          rootFolderUri = documentContext.resolveReference('/', documentUri);
                          documentFolderUri = dirname(documentUri);
                          return [4 /*yield*/, this.resolvePathToModule(moduleName, documentFolderUri, rootFolderUri)];
                      case 1:
                          modulePath = _a.sent();
                          if (modulePath) {
                              pathWithinModule = ref.substring(moduleName.length + 1);
                              return [2 /*return*/, joinPath(modulePath, pathWithinModule)];
                          }
                          _a.label = 2;
                      case 2: return [2 /*return*/, documentContext.resolveReference(ref, documentUri)];
                      case 3: return [2 /*return*/, documentContext.resolveReference(ref, documentUri)];
                  }
              });
          });
      };
      CSSNavigation.prototype.resolvePathToModule = function (_moduleName, documentFolderUri, rootFolderUri) {
          return __awaiter(this, void 0, void 0, function () {
              var packPath;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          packPath = joinPath(documentFolderUri, 'node_modules', _moduleName, 'package.json');
                          return [4 /*yield*/, this.fileExists(packPath)];
                      case 1:
                          if (_a.sent()) {
                              return [2 /*return*/, dirname(packPath)];
                          }
                          else if (rootFolderUri && documentFolderUri.startsWith(rootFolderUri) && (documentFolderUri.length !== rootFolderUri.length)) {
                              return [2 /*return*/, this.resolvePathToModule(_moduleName, dirname(documentFolderUri), rootFolderUri)];
                          }
                          return [2 /*return*/, undefined];
                  }
              });
          });
      };
      CSSNavigation.prototype.fileExists = function (uri) {
          return __awaiter(this, void 0, void 0, function () {
              var stat, err_1;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          if (!this.fileSystemProvider) {
                              return [2 /*return*/, false];
                          }
                          _a.label = 1;
                      case 1:
                          _a.trys.push([1, 3, , 4]);
                          return [4 /*yield*/, this.fileSystemProvider.stat(uri)];
                      case 2:
                          stat = _a.sent();
                          if (stat.type === FileType.Unknown && stat.size === -1) {
                              return [2 /*return*/, false];
                          }
                          return [2 /*return*/, true];
                      case 3:
                          err_1 = _a.sent();
                          return [2 /*return*/, false];
                      case 4: return [2 /*return*/];
                  }
              });
          });
      };
      return CSSNavigation;
  }());
  export { CSSNavigation };
  function getColorInformation(node, document) {
      var color = getColorValue(node);
      if (color) {
          var range = getRange(node, document);
          return { color: color, range: range };
      }
      return null;
  }
  function getRange(node, document) {
      return Range.create(document.positionAt(node.offset), document.positionAt(node.end));
  }
  function getHighlightKind(node) {
      if (node.type === nodes.NodeType.Selector) {
          return DocumentHighlightKind.Write;
      }
      if (node instanceof nodes.Identifier) {
          if (node.parent && node.parent instanceof nodes.Property) {
              if (node.isCustomProperty) {
                  return DocumentHighlightKind.Write;
              }
          }
      }
      if (node.parent) {
          switch (node.parent.type) {
              case nodes.NodeType.FunctionDeclaration:
              case nodes.NodeType.MixinDeclaration:
              case nodes.NodeType.Keyframe:
              case nodes.NodeType.VariableDeclaration:
              case nodes.NodeType.FunctionParameter:
                  return DocumentHighlightKind.Write;
          }
      }
      return DocumentHighlightKind.Read;
  }
  function toTwoDigitHex(n) {
      var r = n.toString(16);
      return r.length !== 2 ? '0' + r : r;
  }
  function getModuleNameFromPath(path) {
      // If a scoped module (starts with @) then get up until second instance of '/', otherwise get until first instance of '/'
      if (path[0] === '@') {
          return path.substring(0, path.indexOf('/', path.indexOf('/') + 1));
      }
      return path.substring(0, path.indexOf('/'));
  }
  