angelozerr / tern.java

Use tern.js in Java context
http://ternjs.net/
Other
249 stars 52 forks source link

Old lint markers not removed #374

Closed letmaik closed 8 years ago

letmaik commented 8 years ago

I just set up tern on a project and it doesn't delete old markers, no idea why. When I hover over an old marker it throws an exception:

Tern response# with 5ms: 

{"files":[{"name":"covjson-reader/Coverage.js","text":"","type":"full"}]}

Tern response#eslint with 57ms: 
{"messages":[{"message":"\"isTyped\" is defined but never used","severity":"error","from":15504,"to":15505,"file":"covjson-reader/Coverage.js"}]}
-----------------------------------
Tern request#type: 
-----------------------------------
{"query":{"type":"type","file":"covjson-reader/Coverage.js","end":15426,"docs":true,"urls":true,"types":true},"files":[{"name":"covjson-reader/Coverage.js","text":"import ndarray from 'ndarray'\r\nimport {shallowcopy, minMax, assert, PREFIX} from './util.js'\r\nimport {load} from './ajax.js'\r\n\r\n/** \r\n * Wraps a CoverageJSON Coverage object as a Coverage API object.\r\n * \r\n * @see https://github.com/Reading-eScience-Centre/coverage-jsapi\r\n * \r\n */\r\nexport default class Coverage {\r\n  \r\n  /**\r\n   * @param {Object} covjson A CoverageJSON Coverage object.\r\n   * @param {boolean} cacheRanges\r\n   *   If true, then any range that was loaded remotely is cached.\r\n   *   (The domain is always cached.)\r\n   *                           \r\n   */\r\n  constructor(covjson, cacheRanges = false) {\r\n    this._covjson = covjson\r\n    this._exposeLd(covjson)\r\n    \r\n    /** @type {boolean} */\r\n    this.cacheRanges = cacheRanges\r\n    \r\n    this.id = covjson.id\r\n    \r\n    /** @type {Map} */\r\n    this.parameters = new Map()\r\n    for (let key of Object.keys(covjson.parameters)) {\r\n      transformParameter(covjson.parameters, key)\r\n      this.parameters.set(key, covjson.parameters[key])\r\n    }\r\n    \r\n    let profile = this._covjson.profile || 'Coverage'\r\n    if (!profile.startsWith('http')) {\r\n      profile = PREFIX + profile\r\n    }\r\n    \r\n    /** @type {string} */\r\n    this.type = profile\r\n    \r\n    let domainProfile\r\n    if (typeof this._covjson.domain === 'string') {\r\n      domainProfile = this._covjson.domainProfile || 'Domain'\r\n    } else {\r\n      domainProfile = this._covjson.domain.profile || 'Domain'\r\n    }\r\n\r\n    if (!domainProfile.startsWith('http')) {\r\n      domainProfile = PREFIX + domainProfile\r\n    }\r\n    \r\n    /** @type {string} */\r\n    this.domainType = domainProfile\r\n    \r\n    /**\r\n     * A bounding box array with elements [westLon, southLat, eastLon, northLat].\r\n     * \r\n     * @type {Array|undefined}\r\n     */\r\n    this.bbox = this._covjson.bbox\r\n  }\r\n  \r\n  _exposeLd (covjson) {\r\n    if (!covjson['@context']) {\r\n      // no LD love here...\r\n      this.ld = {}\r\n      return\r\n    }\r\n    // make a deep copy since the object gets modified in-place later\r\n    // but first, remove domain and range which may be embedded\r\n    let copy = shallowcopy(covjson)\r\n    delete copy.domain\r\n    delete copy.ranges\r\n    this.ld = JSON.parse(JSON.stringify(copy))\r\n  }\r\n    \r\n  /**\r\n   * @return {Promise}\r\n   */\r\n  loadDomain () {\r\n    let domainOrUrl = this._covjson.domain\r\n    if (this._domainPromise) return this._domainPromise\r\n    let promise\r\n    if (typeof domainOrUrl === 'object') {\r\n      transformDomain(domainOrUrl)\r\n      promise = Promise.resolve(domainOrUrl)\r\n    } else { // URL\r\n      promise = load(domainOrUrl).then(domain => {\r\n        transformDomain(domain)\r\n        this._covjson.domain = domain\r\n        return domain\r\n      })\r\n    }\r\n    /* The promise gets cached so that the domain is not loaded twice remotely.\r\n     * This might otherwise happen when loadDomain and loadRange is used\r\n     * with Promise.all(). Remember that loadRange also invokes loadDomain.\r\n     */ \r\n    this._domainPromise = promise\r\n    return promise\r\n  }\r\n  \r\n  /**\r\n   * Returns the requested range data as a Promise.\r\n   * \r\n   * Note that this method implicitly loads the domain as well. \r\n   * \r\n   * @example\r\n   * cov.loadRange('salinity').then(function (sal) {\r\n   *   // work with Range object\r\n   * }).catch(function (e) {\r\n   *   // there was an error when loading the range\r\n   *   console.log(e.message)\r\n   * }) \r\n   * @param {string} paramKey The key of the Parameter for which to load the range.\r\n   * @return {Promise} A Promise object which loads the requested range data and succeeds with a Range object.\r\n   */\r\n  loadRange (paramKey) {\r\n    // Since the shape of the range array is derived from the domain, it has to be loaded as well.\r\n    return this.loadDomain().then(domain => {\r\n      let rangeOrUrl = this._covjson.ranges[paramKey]\r\n      if (typeof rangeOrUrl === 'object') {\r\n        transformRange(rangeOrUrl, domain)\r\n        return Promise.resolve(rangeOrUrl)\r\n      } else { // URL\r\n        return load(rangeOrUrl).then(range => {\r\n          transformRange(range, domain)\r\n          if (this.cacheRanges) {\r\n            this._covjson.ranges[paramKey] = range\r\n          }\r\n          return range\r\n        })\r\n      }\r\n    })    \r\n  }\r\n  \r\n  /**\r\n   * Returns the requested range data as a Promise.\r\n   * \r\n   * Note that this method implicitly loads the domain as well. \r\n   * \r\n   * @example\r\n   * cov.loadRanges(['salinity','temp']).then(function (ranges) {\r\n   *   // work with Map object\r\n   *   console.log(ranges.get('salinity').values)\r\n   * }).catch(function (e) {\r\n   *   // there was an error when loading the range data\r\n   *   console.log(e)\r\n   * }) \r\n   * @param {iterable} [paramKeys] An iterable of parameter keys for which to load the range data. If not given, loads all range data.\r\n   * @return {Promise} A Promise object which loads the requested range data and succeeds with a Map object.\r\n   */\r\n  loadRanges (paramKeys) {\r\n    if (paramKeys === undefined) paramKeys = this.parameters.keys()\r\n    paramKeys = Array.from(paramKeys)\r\n    return Promise.all(paramKeys.map(k => this.loadRange(k))).then(ranges => {\r\n      let map = new Map()\r\n      for (let i=0; i < paramKeys.length; i++) {\r\n        map.set(paramKeys[i], ranges[i])\r\n      }\r\n      return map\r\n    })\r\n  }\r\n  \r\n  /**\r\n   * Returns a Promise object which provides a copy of this Coverage object\r\n   * with the domain subsetted by the given indices specification.\r\n   * \r\n   * Note that the coverage type and/or domain type of the resulting coverage\r\n   * may be different than in the original coverage.\r\n   * \r\n   * Note that the subsetted ranges are a view over the original ranges, meaning\r\n   * that no copying is done but also no memory is released if the original\r\n   * coverage is garbage collected.\r\n   * \r\n   * @example\r\n   * cov.subsetByIndex({t: 4, z: {start: 10, stop: 20}, x: [0,1,2] }).then(function(subsetCov) {\r\n   *   // work with subsetted coverage\r\n   * })\r\n   * @param {Object} constraints An object which describes the subsetting constraints.\r\n   *   Every property of it refers to an axis name as defined in Domain.names,\r\n   *   and its value must either be an integer, an array of integers,\r\n   *   or an object with start, stop, and optionally step (defaults to 1) properties\r\n   *   whose values are integers. All integers must be non-negative, step must not be zero.\r\n   *   A simple integer constrains the axis to the given index, an array to a list of indices,\r\n   *   and a start/stop/step object to a range of indices:\r\n   *   If step=1, this includes all indices starting at start and ending at stop (exclusive);\r\n   *   if step>1, all indices start, start + step, ..., start + (q + r - 1) step where \r\n   *   q and r are the quotient and remainder obtained by dividing stop - start by step.\r\n   * @returns {Promise} A Promise object with the subsetted coverage object as result.\r\n   */\r\n  subsetByIndex (constraints) {\r\n    // FIXME rework\r\n    \r\n    return this.loadDomain().then(domain => {      \r\n      // check and normalize constraints to simplify code and to allow more optimization\r\n      constraints = shallowcopy(constraints)\r\n      let isConsecutive = arr => {\r\n        let last = arr[0] - 1\r\n        for (let el of arr) {\r\n          if (el !== last + 1) {\r\n            return false\r\n          }\r\n          last = el\r\n        }\r\n        return true\r\n      }\r\n      for (let axisName in constraints) {\r\n        if (!domain.axes.has(axisName)) {\r\n          // TODO clarify this behaviour in the JS API spec\r\n          delete constraints[axisName]\r\n          continue\r\n        }\r\n        if (Array.isArray(constraints[axisName])) {\r\n          let constraint = constraints[axisName]\r\n          // range subsetting can be done with fast ndarray views if single indices or slicing objects are used\r\n          // therefore, we try to transform some common cases into those forms\r\n          if (constraint.length === 1) {\r\n            // transform 1-element arrays into a number\r\n            constraints[axisName] = constraint[0]\r\n          } else if (isConsecutive(constraint)) {\r\n            // transform arrays of consecutive indices into start, stop object\r\n            constraints[axisName] = {start: constraint[0], stop: constraint[constraint.length-1] + 1}\r\n          }\r\n        }\r\n        if (typeof constraints[axisName] === 'number') {\r\n          let constraint = constraints[axisName]\r\n          constraints[axisName] = {start: constraint, stop: constraint + 1}\r\n        }\r\n        if (!Array.isArray(constraints[axisName])) {\r\n          let {start = 0, \r\n               stop = domain.axes.get(axisName).values.length, \r\n               step = 1} = constraints[axisName]\r\n          if (step <= 0) {\r\n            throw new Error(`Invalid constraint for ${axisName}: step=${step} must be > 0`)\r\n          }\r\n          if (start >= stop || start < 0) {\r\n            throw new Error(`Invalid constraint for ${axisName}: stop=${stop} must be > start=${start} and both >= 0`)\r\n          }\r\n          constraints[axisName] = {start, stop, step}\r\n        }\r\n      }\r\n      for (let axisName of domain.axes.keys()) {\r\n        if (!(axisName in constraints)) {\r\n          let len = domain.axes.get(axisName).values.length\r\n          constraints[axisName] = {start: 0, stop: len, step: 1}\r\n        }\r\n      }\r\n      \r\n      // After normalization, all constraints are either arrays or start,stop,step objects.\r\n      // For all start,stop,step objects, it holds that stop > start, step > 0, start >= 0, stop >= 1.\r\n      // For each axis, a constraint exists.\r\n      \r\n      // subset the axis arrays of the domain (immediately + cached)\r\n      let newdomain = shallowcopy(domain)\r\n      newdomain.axes = new Map(newdomain.axes)\r\n      newdomain._rangeShape = domain._rangeShape.slice() // deep copy\r\n\r\n      for (let axisName of Object.keys(constraints)) {\r\n        let coords = domain.get(axisName).values\r\n        let isTypedArray = ArrayBuffer.isView(coords)\r\n        let constraint = constraints[axisName]\r\n        let newcoords\r\n        if (Array.isArray(constraint)) {\r\n          if (isTypedArray) {\r\n            newcoords = new coords.constructor(constraint.length)\r\n            for (let i=0; i < constraint.length; i++) {\r\n              newcoords[i] = coords[constraint[i]]\r\n            }\r\n          } else {\r\n            newcoords = constraint.map(i => coords[i])\r\n          }\r\n        } else {\r\n          let {start, stop, step} = constraint\r\n          if (start === 0 && stop === coords.length && step === 1) {\r\n            newcoords = coords\r\n          } else if (step === 1 && isTypedArray) {\r\n            newcoords = coords.subarray(start, stop)\r\n          } else {\r\n            let q = Math.trunc((stop - start) / step)\r\n            let r = (stop - start) % step\r\n            let len = start + (q + r - 1)\r\n            newcoords = new coords.constructor(len) // array or typed array\r\n            for (let i=start, j=0; i < stop; i += step, j++) {\r\n              newcoords[j] = coords[i]\r\n            }\r\n          }\r\n        }\r\n        let newaxis = shallowcopy(domain.axes.get(axisName))\r\n        newaxis.values = newcoords\r\n        newdomain.axes.set(axisName, newaxis)\r\n        newdomain._rangeShape[domain._rangeAxisOrder.indexOf(axisName)] = newcoords.length\r\n      }\r\n            \r\n      // subset the ndarrays of the ranges (on request)\r\n      let rangeWrapper = range => {\r\n        let ndarr = range._ndarr\r\n        \r\n        let newndarr\r\n        if (Object.keys(constraints).some(ax => Array.isArray(constraints[ax]))) {\r\n          // There is a list of indices for at least one axis.\r\n          // In that case we cannot directly use SciJS's slicing operations.\r\n          \r\n          // TODO implement\r\n          throw new Error('not implemented yet')\r\n          \r\n        } else {\r\n          // fast ndarray view\r\n          let axisNames = domain._rangeAxisOrder\r\n          let los = axisNames.map(name => constraints[name].start)\r\n          let his = axisNames.map(name => constraints[name].stop)\r\n          let steps = axisNames.map(name => constraints[name].steps)\r\n          newndarr = ndarr.hi(...his).lo(...los).step(...steps)\r\n        }\r\n        \r\n        let newrange = shallowcopy(range)\r\n        newrange._ndarr = newndarr\r\n        newrange.get = createRangeGetFunction(newndarr, domain._rangeAxisOrder)\r\n        \r\n        return newrange\r\n      }\r\n      \r\n      let loadRange = key => this.loadRange(key).then(rangeWrapper)\r\n      \r\n      // we wrap loadRanges as well in case it was overridden from the outside\r\n      // (in which case we could not be sure that it invokes loadRange() and uses the wrapper)\r\n      let loadRanges = keys => this.loadRanges(keys).then(ranges => \r\n        new Map([...ranges].map(([key, range]) => [key, rangeWrapper(range)]))\r\n      )\r\n      \r\n      // assemble everything to a new coverage\r\n      let newcov = shallowcopy(this)\r\n      newcov.loadDomain = () => Promise.resolve(newdomain)\r\n      newcov.loadRange = loadRange\r\n      newcov.loadRanges = loadRanges\r\n      return newcov\r\n    })\r\n  }\r\n}\r\n\r\n/**\r\n * Currently unused, but may need in future.\r\n * This determines the best array type for categorical data which\r\n * doesn't have missing values.\r\n */\r\n/*\r\nfunction arrayType (validMin, validMax) {\r\n  let type\r\n  if (validMin !== undefined) {\r\n    if (validMin >= 0) {\r\n      if (validMax < Math.pow(2,8)) {\r\n        type = Uint8Array\r\n      } else if (validMax < Math.pow(2,16)) {\r\n        type = Uint16Array\r\n      } else if (validMax < Math.pow(2,32)) {\r\n        type = Uint32Array\r\n      } else {\r\n        type = Array\r\n      }\r\n    } else {\r\n      let max = Math.max(Math.abs(validMin), validMax)\r\n      if (max < Math.pow(2,8)) {\r\n        type = Int8Array\r\n      } else if (validMax < Math.pow(2,16)) {\r\n        type = Int16Array\r\n      } else if (validMax < Math.pow(2,32)) {\r\n        type = Int32Array\r\n      } else {\r\n        type = Array\r\n      }\r\n    }\r\n  } else {\r\n    type = Array\r\n  }\r\n  return type\r\n}\r\n*/\r\n\r\n/**\r\n * Transforms a CoverageJSON parameter to the Coverage API format, that is,\r\n * language maps become real Maps. Transformation is made in-place.\r\n * \r\n * @param {Object} param The original parameter.\r\n */\r\nfunction transformParameter (params, key) {\r\n  let param = params[key]\r\n  param.key = key\r\n  let maps = [\r\n              [param, 'description'], \r\n              [param.observedProperty, 'label'],\r\n              [param.observedProperty, 'description'],\r\n              [param.unit, 'label']\r\n             ]\r\n  for (let cat of param.observedProperty.categories || []) {\r\n    maps.push([cat, 'label'])\r\n    maps.push([cat, 'description'])\r\n  }\r\n  for (let entry of maps) {\r\n    transformLanguageMap(entry[0], entry[1])\r\n  }\r\n}\r\n\r\nfunction transformLanguageMap (obj, key) {\r\n  if (!obj || !(key in obj) || obj[key] instanceof Map) {\r\n    return    \r\n  }\r\n  var map = new Map()\r\n  for (let tag of Object.keys(obj[key])) {\r\n    map.set(tag, obj[key][tag])\r\n  }\r\n  obj[key] = map\r\n}\r\n\r\n/**\r\n * Transforms a CoverageJSON range to the Coverage API format, that is,\r\n * no special encoding etc. is left. Transformation is made in-place.\r\n * \r\n * @param {Object} range The original range.\r\n * @param {Object} domain The CoverageJSON domain object. \r\n * @return {Object} The transformed range.\r\n */\r\nfunction transformRange (range, domain) {\r\n  if ('__transformDone' in range) return\r\n  \r\n  const values = range.values\r\n  //const targetDataType = range.dataType // 'integer', 'float', 'string'\r\n   \r\n  const isTyped = ArrayBuffer.isView(values)\r\n  const missingIsEncoded = range.missing === 'nonvalid'\r\n  const hasOffsetFactor = 'offset' in range\r\n\r\n  if ('offset' in range) {\r\n    assert('factor' in range)\r\n  }\r\n  const offset = range.offset\r\n  const factor = range.factor\r\n  \r\n  if (missingIsEncoded) {\r\n    assert('validMin' in range)\r\n    assert('validMax' in range)\r\n  }\r\n  const validMin = range.validMin\r\n  const validMax = range.validMax\r\n  \r\n  let vals\r\n  if (!missingIsEncoded && !hasOffsetFactor) {\r\n    // No transformation necessary.\r\n    vals = values\r\n  } else {\r\n    // Transformation is necessary.\r\n    // we use a regular array so that missing values can be represented as null\r\n    vals = new Array(values.length)\r\n    \r\n    // TODO can we use typed arrays here without having to scan for missing values first?\r\n    //  When typed arrays with missing value encoding was used we could keep that and provide\r\n    //  a higher abstraction on the array similar to an ndarray interface. This means that [] syntax\r\n    //  would be impossible and change to .get(index).\r\n    \r\n    if (hasOffsetFactor) {\r\n      for (let i=0; i < values.length; i++) {\r\n        const val = values[i]\r\n        if (missingIsEncoded && (val < validMin || val > validMax)) {\r\n          // This is necessary as the default value is \"undefined\".\r\n          vals[i] = null\r\n        } else if (!missingIsEncoded && val === null) {\r\n          vals[i] = null\r\n        } else {\r\n          vals[i] = val * factor + offset\r\n        }\r\n      }\r\n      \r\n      if (validMin !== undefined) {\r\n        range.validMin = validMin * factor + offset\r\n        range.validMax = validMax * factor + offset\r\n      }\r\n    } else { // missingIsEncoded == true\r\n      for (let i=0; i < values.length; i++) {\r\n        const val = values[i]\r\n        if (val < validMin || val > validMax) {\r\n          vals[i] = null\r\n        } else {\r\n          vals[i] = val\r\n        }\r\n      }\r\n    }\r\n        \r\n    delete range.offset\r\n    delete range.factor\r\n    delete range.missing\r\n  }\r\n  \r\n  if (validMin === undefined) {\r\n    let [min,max] = minMax(vals)\r\n    if (min !== null) {\r\n      range.validMin = min\r\n      range.validMax = max\r\n    }\r\n  }\r\n  \r\n  let size = new Map() // axis name -> axis size (value count)\r\n  for (let axisName of domain.axes.keys()) {\r\n    size.set(axisName, domain.axes.get(axisName).values.length)\r\n  }\r\n  range.size = size\r\n  \r\n  let ndarr = ndarray(vals, domain._rangeShape)\r\n  range._ndarr = ndarr\r\n  range.get = createRangeGetFunction(ndarr, domain._rangeAxisOrder)\r\n  \r\n  range.__transformDone = true  \r\n  return range\r\n}\r\n\r\n/**\r\n * \r\n * @param axisOrder An array of axis names.\r\n * @returns Function\r\n */\r\nfunction createRangeGetFunction (ndarr, axisOrder) {\r\n  const axisCount = axisOrder.length\r\n  return obj => {\r\n    // TODO optimize this via pre-compilation (benchmark!)\r\n    let indices = new Array(axisCount)\r\n    for (let i=0; i < axisCount; i++) {\r\n      indices[i] = axisOrder[i] in obj ? obj[axisOrder[i]] : 0\r\n    }\r\n    return ndarr.get(...indices)\r\n  }\r\n}\r\n\r\n/**\r\n * Transforms a CoverageJSON domain to the Coverage API format.\r\n * Transformation is made in-place.\r\n * \r\n * @param {Object} domain The original domain object.\r\n * @return {Object} The transformed domain object.\r\n */\r\nfunction transformDomain (domain) {\r\n  if ('__transformDone' in domain) return\r\n  \r\n  let profile = domain.profile || 'Domain'\r\n  if (!profile.startsWith('http')) {\r\n    profile = PREFIX + profile\r\n  }\r\n  domain.type = profile\r\n\r\n  let axes = new Map() // axis name -> axis object\r\n  \r\n  for (let axisName of Object.keys(domain.axes)) {\r\n    axes.set(axisName, domain.axes[axisName])\r\n  }\r\n  domain.axes = axes\r\n  \r\n  domain._rangeAxisOrder = domain.rangeAxisOrder || [...axes.keys()]\r\n  domain._rangeShape = domain._rangeAxisOrder.map(k => axes.get(k).values.length)\r\n  \r\n  // replace 1D numeric axis arrays with typed arrays for efficiency\r\n  for (let axis of axes.values()) {\r\n    if (ArrayBuffer.isView(axis.values)) {\r\n      // already a typed array\r\n      continue\r\n    }\r\n    if (Array.isArray(axis.values) && typeof axis.values[0] === 'number') {\r\n      let arr = new Float64Array(axis.values.length)\r\n      for (let i=0; i < axis.values.length; i++) {\r\n        arr[i] = axis.values[i]\r\n      }\r\n      axis.values = arr\r\n    }\r\n  }\r\n  \r\n  domain.__transformDone = true\r\n  \r\n  return domain\r\n}\r\n","type":"full"}]}

Tern error#type with 24ms: 
tern.TernException: TernError: No type found at the given position.
    at tern.server.nodejs.NodejsTernHelper.makeRequest(NodejsTernHelper.java:86)
    at tern.server.nodejs.NodejsTernServer.makeRequest(NodejsTernServer.java:163)
    at tern.server.nodejs.NodejsTernServer.request(NodejsTernServer.java:127)
    at tern.server.protocol.TernResultsProcessorsFactory.makeRequestAndProcess(TernResultsProcessorsFactory.java:42)
    at tern.eclipse.ide.internal.core.resources.IDETernServerAsyncReqProcessor.processRequest(IDETernServerAsyncReqProcessor.java:49)
    at tern.server.AbstractTernServer.request(AbstractTernServer.java:167)
    at tern.resources.TernProject.request(TernProject.java:633)
    at tern.resources.TernProject.request(TernProject.java:624)
    at tern.eclipse.ide.ui.hover.TernHover.getHoverInfo2(TernHover.java:78)
    at tern.eclipse.ide.ui.hover.TernHover.getHoverInfo(TernHover.java:49)
    at org.eclipse.wst.jsdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo(BestMatchHover.java:99)
    at org.eclipse.wst.jsdt.internal.ui.text.java.hover.JavaEditorTextHoverProxy.getHoverInfo(JavaEditorTextHoverProxy.java:67)
    at org.eclipse.jface.text.TextViewerHoverManager$4.run(TextViewerHoverManager.java:168)

-----------------------------------

I use the latest 1.1 stable

angelozerr commented 8 years ago

@neothemachine have you activated "Add Validation Builder" for your project with Validation preferences. If you have done that, you should have Validation in your Builders project properties.

If it is this case, if you do Ctrl+S, markers should disappear, no?

The "tern.TernException: TernError: No type found at the given position." is thrown by ternjs when it cannot find type, it's a normal error.

letmaik commented 8 years ago

On 27/11/2015 10:59, Angelo wrote:

@neothemachine https://github.com/neothemachine have you activated "Add Validation Builder" for your project with Validation preferences. If you have done that, you should have Validation in your Builders project properties.

That was the problem! Thanks, it works now. Why isn't this enabled by default when a project is converted to a Tern project?

angelozerr commented 8 years ago

tern doesn't do that, perhaps it's an another plugin which does that, I don't know.

letmaik commented 8 years ago

What do you mean by tern doesn't do that? Is my problem the standard behaviour or did some other eclipse plugin remove the validation builder?

On 27/11/2015 11:07, Angelo wrote:

tern doesn't do that, perhaps it's an another plugin which does that, I don't know.

— Reply to this email directly or view it on GitHub https://github.com/angelozerr/tern.java/issues/374#issuecomment-160103939.

angelozerr commented 8 years ago

What do you mean by tern doesn't do that?

I mean that it doesn't "Add Validation Builder" by default. Tern provides a WTP Validator and it manages validation like other WTP Validator like HTML, XML, etc

After it's the user who decide if he want to validate after a Ctrl+S or not.

If I have understood your problem, you had not "Add Validation Builder" and when you are typing, markers doesn't disappear. So I suppose you have used manual Validation with contextual menu "Validate".

I suggest you that you try your test with HTML editor and you should see that it's the same behaviour than tern. If not, please tell me.

letmaik commented 8 years ago

Ok, I understand that. Seems to be how WTP projects behave. But on the other hand, if you have a Java project, it also automatically adds the relevant builders and you don't have to do anything manually. For me the most confusing thing was that in the top menu at "Project", the "Build automatically" was enabled, so I assumed it would validate while I type. It's just a bit user unfriendly I think. Even though the "JavaScript" project behaves the same, why not do the following: When you right-click Configure -> Convert to tern project, then after conversion it checks if the validation builder is already added and if not it asks you in a popup if you want to "validate automatically while typing".

On 27/11/2015 11:25, Angelo wrote:

What do you mean by tern doesn't do that?

I mean that it doesn't "Add Validation Builder" by default. Tern provides a WTP Validator and it manages validation like other WTP Validator like HTML, XML, etc

After it's the user who decide if he want to validate after a Ctrl+S or not.

If I have understood your problem, you had not "Add Validation Builder" and when you are typing, markers doesn't disappear. So I suppose you have used manual Validation with contextual menu "Validate".

I suggest you that you try your test with HTML editor and you should see that it's the same behaviour than tern. If not, please tell me.

— Reply to this email directly or view it on GitHub https://github.com/angelozerr/tern.java/issues/374#issuecomment-160106920.

angelozerr commented 8 years ago

so I assumed it would validate while I type.

It should do that without "Add Validation Builder".

WTP provides 2 validators :

In your case, I think you had not "Validation Builder", when you are typing, errors should appear (otherwise it's a bug). After you have perhaps do "Validate" in a file with contextual menu, and in this case, it's a "Tern Problem" marker which marks errors (see Problem View). This Tern problem marker is a persitent marker. So if you restart Eclipse, your editor will highlighted with those markers, even if you change content in your editor.

To remove those persitent marker, you must use Validate or remove at hand in the Problem View.

I mean that you have 2 markers kind :

letmaik commented 8 years ago

On 27/11/2015 11:53, Angelo wrote:

so I assumed it would validate while I type.

It should do that without "Add Validation Builder".

You're right, I was still messing around with settings and I think I manually did a validation. I didn't know that this is different (permanent) compared to the while-you-type markers. So, I think there's no bug after all, just a bit of unfortunate workflow, which doesn't surprise me in Eclipse ;) I'm closing this now.